% \iffalse %<*driver> \documentclass{ltxdockit} \usepackage{btxdockit} \usepackage[utf8]{inputenc} \usepackage[T1]{fontenc} \usepackage[american]{babel} \usepackage{microtype} \usepackage{amsmath} \usepackage{fancyvrb} \usepackage{hologo} \usepackage{xcolor} \usepackage{doc} % Set up the style. \emergencystretch=1em \RecustomVerbatimEnvironment {Verbatim}{Verbatim} {gobble=2,frame=single} \setcounter{secnumdepth}{4} \addtokomafont{title}{\sffamily} \addtokomafont{paragraph}{\spotcolor} \addtokomafont{section}{\spotcolor} \addtokomafont{subsection}{\spotcolor} \addtokomafont{subsubsection}{\spotcolor} \addtokomafont{descriptionlabel}{\spotcolor} \setkomafont{caption}{\bfseries\sffamily\spotcolor} \setkomafont{captionlabel}{\bfseries\sffamily\spotcolor} \hypersetup{citecolor=spot} \let\oldCodelineNo\theCodelineNo \def\theCodelineNo{\textcolor{gray}{\oldCodelineNo}} % Define some markup. \let\pkg\relax % A package name \newcommand\mdef[1]{% A TeX macro definition \phantomsection\label{macro:#1}\textcolor{spot}{\cs{#1}}} \newcommand\m[1]{% A TeX macro reference \hyperref[macro:#1]{\textcolor{spot}{\cs{#1}}}} \newcommand\envmdef[1]{% A TeX macro definition \phantomsection\label{environment:#1}\textcolor{spot}{\t`#1`}} \newcommand\envm[1]{% A LaTeX environment reference \hyperref[environment:#1]{\textcolor{spot}{\t`#1`}}} \newcommand\luamdef[1]{% A Lua object / method definition \phantomsection\label{lua:#1}\textcolor{spot}{\t`#1`}} \newcommand\luam[1]{% A Lua object / method reference \hyperref[lua:#1]{\t`#1`}} \def\t`#1`{% Inline code \textcolor{spot}{\text{\texttt{#1}}}} \newcommand\Optitem[2][]{% An option item definition \phantomsection\label{opt:#2}\optitem[#1]{#2}} \newcommand\Valitem[2][]{% A value item definition \phantomsection\label{opt:#2}\valitem[#1]{#2}} \newcommand\Opt[1]{% An option / value item reference \hyperref[opt:#1]{\t`#1`}} \newcommand\acro[1]{% An acronym \textsc{#1}} % Set up the catcodes. \catcode`\_=12 % We won't be typesetting math and Lua contains lots of `_`. % Set up the title page. \titlepage{% title={A Markdown Interpreter for \TeX{}}, subtitle={}, url={https://github.com/witiko/markdown}, author={Vít Novotný (based on the work of John MacFarlane and Hans Hagen)}, email={witiko@mail.muni.cz}, revision={\input VERSION}, date={\today}} \CodelineIndex % Set up the bibliography. \usepackage{filecontents} \begin{filecontents}{markdown.bib} @book{luatex16, author = {{Lua\TeX{} development team}}, title = {Lua\TeX{} reference manual}, date = {2016-09-27}, url = {http://www.luatex.org/svn/trunk/manual/luatex.pdf}, urldate = {2016-11-27}} @book{latex16, author = {Braams, Johannes and Carlisle, David and Jeffrey, Alan and Lamport, Leslie and Mittelbach, Frank and Rowley, Chris and Schöpf, Rainer}, title = {The \Hologo{LaTeX2e} Sources}, date = {2016-03-31}, url = {http://mirrors.ctan.org/macros/latex/base/source2e.pdf}, urldate = {2016-09-27}} @book{ierusalimschy13, author = {Ierusalimschy, Roberto}, year = {2013}, title = {Programming in Lua}, edition = {3}, isbn = {978-85-903798-5-0}, pagetotal = {xviii, 347}, location = {Rio de Janeiro}, publisher = {PUC-Rio}} @book{knuth86, author = {Knuth, Donald Ervin}, year = {1986}, title = {The \TeX{}book}, edition = {3}, isbn = {0-201-13447-0}, pagetotal = {ix, 479}, publisher = {Addison-Westley}} \end{filecontents} \usepackage[ backend=biber, style=iso-numeric, sorting=none, autolang=other, sortlocale=auto]{biblatex} \addbibresource{markdown.bib} \begin{document} \printtitlepage \tableofcontents \DocInput{markdown.dtx} \printbibliography \end{document} % % \fi % % \section{Introduction} % This document is a reference manual for the \pkg{Markdown} package. It is % split into three sections. This section explains the purpose and the % background of the package and outlines its prerequisites. Section % \ref{sec:interfaces} describes the interfaces exposed by the package along % with usage notes and examples. It is aimed at the user of the package. % Section \ref{sec:implementation} describes the implementation of the package. % It is aimed at the developer of the package and the curious user. % % \subsection{About \pkg{Markdown}} % The \pkg{Markdown} package provides facilities for the conversion of markdown % markup to plain \TeX{}. These are provided both in the form of a Lua module % and in the form of plain \TeX{}, \LaTeX{}, and \Hologo{ConTeXt} macro % packages that enable the direct inclusion of markdown documents inside \TeX{} % documents. % % Architecturally, the package consists of the \pkg{Lunamark} v0.5.0 Lua module % by John MacFarlane, which was slimmed down and rewritten for the needs of the % package. On top of \pkg{Lunamark} sits code for the plain \TeX{}, \LaTeX{}, % and \Hologo{ConTeXt} formats by Vít Novotný. % % \iffalse %<*lua> % \fi % \begin{macrocode} local metadata = { version = "2.2.2", comment = "A module for the conversion from markdown to plain TeX", author = "John MacFarlane, Hans Hagen, Vít Novotný", copyright = "2009-2016 John MacFarlane, Hans Hagen; 2016 Vít Novotný", license = "LPPL 1.3" } if not modules then modules = { } end modules['markdown'] = metadata % \end{macrocode} % % \subsection{Feedback} % Please use the \pkg{markdown} project page on % GitHub\footnote{\url{https://github.com/witiko/markdown/issues}} to report % bugs and submit feature requests. Before making a feature request, please % ensure that you have thoroughly studied this manual. If you do not want to % report a bug or request a feature but are simply in need of assistance, you % might want to consider posting your question on the \TeX-\LaTeX{} Stack % Exchange\footnote{\url{https://tex.stackexchange.com}}. % % \subsection{Acknowledgements} % I would like to thank the Faculty of Informatics at the Masaryk University in % Brno for providing me with the opportunity to work on this package alongside % my studies. I would also like to thank the creator of the Lunamark Lua % module, John Macfarlane, for releasing Lunamark under a permissive license % that enabled its inclusion into the package. % % The \TeX{} part of the package draws inspiration from several sources % including the source code of \Hologo{LaTeX2e}, the \pkg{minted} package by % Geoffrey M. Poore -- which likewise tackles the issue of interfacing with an % external interpreter from \TeX, the \pkg{filecontents} package by Scott % Pakin, and others. % % \subsection{Prerequisites} % This section gives an overview of all resources required by the package. % % \subsubsection{Lua Prerequisites}\label{sec:luaprerequisites} % The Lua part of the package requires that the following Lua modules are % available from within the Lua\TeX{} engine: % \begin{description} % \item[\pkg{LPeg${}\geq{}$0.10}] A pattern-matching library for the writing % of recursive descent parsers via the Parsing Expression Grammars % (\acro{peg}s). It is used by the \pkg{Lunamark} library to parse the % markdown input. \pkg{LPeg${}\geq{}$0.10} is included in % Lua\TeX${}\geq{}$0.72.0 (\TeX Live${}\geq{}2013$). % \begin{macrocode} local lpeg = require("lpeg") % \end{macrocode} % \item[\pkg{Selene Unicode}] A library that provides support for the % processing of wide strings. It is used by the \pkg{Lunamark} library to % cast image, link, and footnote tags to the lower case. \pkg{Selene % Unicode} is included in all releases of Lua\TeX{} (\TeX % Live${}\geq{}2008$). % \begin{macrocode} local unicode = require("unicode") % \end{macrocode} % \item[\pkg{MD5}] A library that provides \acro{md5} crypto functions. It is % used by the \pkg{Lunamark} library to compute the digest of the input for % caching purposes. \pkg{MD5} is included in all releases of Lua\TeX{} % (\TeX Live${}\geq{}2008$). % \begin{macrocode} local md5 = require("md5") % \end{macrocode} % \end{description} % All the abovelisted modules are statically linked into the current version of % the Lua\TeX{} engine (see \cite[Section~3.3]{luatex16}). % % \iffalse % %<*tex> % \fi % \subsubsection{Plain \TeX{} Prerequisites}\label{sec:texprerequisites} % The plain \TeX{} part of the package requires that the plain \TeX{} % format (or its superset) is loaded, all the Lua prerequisites (see % Section \ref{sec:luaprerequisites}) and the following Lua module: % \begin{description} % \item[\pkg{Lua File System}] A library that provides access to the % filesystem via \acro{os}-specific syscalls. It is used by the plain % \TeX{} code to create the cache directory specified by the % \m{markdownOptionCacheDir} macro before interfacing with the % \pkg{Lunamark} library. \pkg{Lua File System} is included in all releases % of Lua\TeX{} (\TeX Live${}\geq{}2008$). % % The plain \TeX{} code makes use of the \luam{isdir} method that was added % to the \pkg{Lua File System} library by the Lua\TeX{} engine developers % (see \cite[Section~3.2]{luatex16}). % \end{description} % The \pkg{Lua File System} module is statically linked into the Lua\TeX{} % engine (see \cite[Section~3.3]{luatex16}). % % The plain \TeX{} part of the package also requires that either the Lua\TeX{} % \m{directlua} primitive or the shell access file stream 18 is available. % % \iffalse % %<*latex> % \fi % \subsubsection{\LaTeX{} Prerequisites}\label{sec:latexprerequisites} % The \LaTeX{} part of the package requires that the \Hologo{LaTeX2e} format is % loaded, % \begin{macrocode} \NeedsTeXFormat{LaTeX2e}% % \end{macrocode} % all the plain \TeX{} prerequisites (see Section \ref{sec:texprerequisites}), % and the following \Hologo{LaTeX2e} packages: % \begin{description} % \item[\pkg{keyval}] A package that enables the creation of parameter sets. % This package is used to provide the \m{markdownSetup} macro, the package % options processing, as well as the parameters of the \envm{markdown*} % \LaTeX{} environment. % \item[\pkg{url}] A package that provides the \m{url} macro for the % typesetting of \acro{url}s. It is used to provide the default token renderer % prototype (see Section \ref{sec:texrendererprototypes}) for links. % \item[\pkg{graphicx}] A package that provides the \m{includegraphics} macro for % the typesetting of images. It is used to provide the corresponding % default token renderer prototype (see Section % \ref{sec:texrendererprototypes}). % \item[\pkg{paralist}] A package that provides the \envm{compactitem}, % \envm{compactenum}, and \envm{compactdesc} macros for the % typesetting of tight bulleted lists, ordered lists, and definition lists. % It is used to provide the corresponding default token renderer prototypes % (see Section \ref{sec:texrendererprototypes}). % \item[\pkg{ifthen}] A package that provides a concise syntax for the % inspection of macro values. It is used to determine whether or not the % \pkg{paralist} package should be loaded based on the user options. % \item[\pkg{fancyvrb}] A package that provides the \m{VerbatimInput} macros % for the verbatim inclusion of files containing code. It is used to % provide the corresponding default token renderer prototype (see Section % \ref{sec:texrendererprototypes}). % \end{description} % % \iffalse % %<*context> % \fi % \subsubsection{\Hologo{ConTeXt} prerequisites} % The \Hologo{ConTeXt} part of the package requires that either the Mark II or % the Mark IV format is loaded and all the plain \TeX{} prerequisites (see % Section \ref{sec:texprerequisites}). % % \section{User Guide}\label{sec:interfaces} % This part of the manual describes the interfaces exposed by the package % along with usage notes and examples. It is aimed at the user of the package. % % Since neither \TeX{} nor Lua provide interfaces as a language construct, the % separation to interfaces and implementations is purely abstract. It serves as % a means of structuring this manual and as a promise to the user that if they % only access the package through the interfaces, the future versions of the % package should remain backwards compatible. % % \iffalse % %<*lua> % \fi % \subsection{Lua Interface}\label{sec:luainterface} % The Lua interface provides the conversion from \acro{utf}-\oldstylenums8 % encoded markdown to plain \TeX{}. This interface is used by the plain \TeX{} % implementation (see Section \ref{sec:teximplementation}) and will be of % interest to the developers of other packages and Lua modules. % % The Lua interface is implemented by the \t`markdown` Lua module. % % \begin{macrocode} local M = {} % \end{macrocode} % % \subsubsection{Conversion from Markdown to Plain \TeX{}} % \label{sec:luaconversion} % The Lua interface exposes the \luamdef{new}\t`(options)` method. This % method creates converter functions that perform the conversion from markdown % to plain \TeX{} according to the table \t`options` that contains options % recognized by the Lua interface. (see Section \ref{sec:luaoptions}). The % \t`options` parameter is optional; when unspecified, the behaviour will be % the same as if \t`options` were an empty table. % % The following example Lua code converts the markdown string \t`_Hello % world!_` to a \TeX{} output using the default options and prints the \TeX{} % output: % \begin{Verbatim} % local md = require("markdown") % local convert = md.new() % print(convert("_Hello world!_")) % \end{Verbatim} % % \subsubsection{Options}\label{sec:luaoptions} % The Lua interface recognizes the following options. When unspecified, the % value of a key is taken from the \luamdef{defaultOptions} table. % \begin{macrocode} local defaultOptions = {} % \end{macrocode} % \begin{optionlist} % \Optitem[false]{blankBeforeBlockquote}{\opt{true}, \opt{false}} % \begin{valuelist} % \item[true] Require a blank line between a paragraph and the following % blockquote. % \item[false] Do not require a blank line between a paragraph and the % following blockquote. % \end{valuelist} % \begin{macrocode} defaultOptions.blankBeforeBlockquote = false % \end{macrocode} % % \Optitem[false]{blankBeforeCodeFence}{\opt{true}, \opt{false}} % \begin{valuelist} % \item[true] Require a blank line between a paragraph and the following % fenced code block. % \item[false] Do not require a blank line between a paragraph and the % following fenced code block. % \end{valuelist} % \begin{macrocode} defaultOptions.blankBeforeCodeFence = false % \end{macrocode} % % \Optitem[false]{blankBeforeHeading}{\opt{true}, \opt{false}} % \begin{valuelist} % \item[true] Require a blank line between a paragraph and the following % header. % \item[false] Do not require a blank line between a paragraph and the % following header. % \end{valuelist} % \begin{macrocode} defaultOptions.blankBeforeHeading = false % \end{macrocode} % % \Valitem[.]{cacheDir}{directory} % The path to the directory containing auxiliary cache files. % % When iteratively writing and typesetting a markdown document, the cache % files are going to accumulate over time. You are advised to clean the % cache directory every now and then, or to set it to a temporary filesystem % (such as \t`/tmp` on \acro{un*x} systems), which gets periodically % emptied. % \begin{macrocode} defaultOptions.cacheDir = "." % \end{macrocode} % % \Optitem[false]{citationNbsps}{\opt{true}, \opt{false}} % \begin{valuelist} % \item[true] Replace regular spaces with non-breakable spaces inside the % prenotes and postnotes of citations produced via the pandoc citation % syntax extension. % \item[false] Do not replace regular spaces with non-breakable spaces % inside the prenotes and postnotes of citations produced via the pandoc % citation syntax extension. % \end{valuelist} % \begin{macrocode} defaultOptions.citationNbsps = true % \end{macrocode} % % \Optitem[false]{citations}{\opt{true}, \opt{false}} % \begin{valuelist} % \item[true] Enable the pandoc citation syntax extension: % \begin{Verbatim} % Here is a simple parenthetical citation [@doe99] and here % is a string of several [see @doe99, pp. 33-35; also % @smith04, chap. 1]. % % A parenthetical citation can have a [prenote @doe99] and % a [@smith04 postnote]. The name of the author can be % suppressed by inserting a dash before the name of an % author as follows [-@smith04]. % % Here is a simple text citation @doe99 and here is % a string of several @doe99 [pp. 33-35; also @smith04, % chap. 1]. Here is one with the name of the author % suppressed -@doe99. % \end{Verbatim} % \item[false] Disable the pandoc citation syntax extension. % \end{valuelist} % \begin{macrocode} defaultOptions.citations = false % \end{macrocode} % % \Optitem[false]{definitionLists}{\opt{true}, \opt{false}} % \begin{valuelist} % \item[true] Enable the pandoc definition list syntax extension: % \begin{Verbatim} % Term 1 % % : Definition 1 % % Term 2 with *inline markup* % % : Definition 2 % % { some code, part of Definition 2 } % % Third paragraph of definition 2. % \end{Verbatim} % \item[false] Disable the pandoc definition list syntax extension. % \end{valuelist} % \begin{macrocode} defaultOptions.definitionLists = false % \end{macrocode} % % \Optitem[false]{hashEnumerators}{\opt{true}, \opt{false}} % \begin{valuelist} % \item[true] Enable the use of hash symbols (\t`\#`) as ordered item % list markers: % \begin{Verbatim} % #. Bird % #. McHale % #. Parish % \end{Verbatim} % \item[false] Disable the use of hash symbols (\t`\#`) as ordered item % list markers. % \end{valuelist} % \begin{macrocode} defaultOptions.hashEnumerators = false % \end{macrocode} % % \Optitem[false]{hybrid}{\opt{true}, \opt{false}} % \begin{valuelist} % \item[true] Disable the escaping of special plain \TeX{} characters, % which makes it possible to intersperse your markdown markup with % \TeX{} code. The intended usage is in documents prepared manually by % a human author. In such documents, it can often be desirable to mix % \TeX{} and markdown markup freely. % % \item[false] Enable the escaping of special plain \TeX{} characters % outside verbatim environments, so that they are not interpretted by % \TeX{}. This is encouraged when typesetting automatically generated % content or markdown documents that were not prepared with this % package in mind. % \end{valuelist} % \begin{macrocode} defaultOptions.hybrid = false % \end{macrocode} % % \Optitem[false]{fencedCode}{\opt{true}, \opt{false}} % \begin{valuelist} % \item[true] Enable the commonmark fenced code block extension: % \begin{Verbatim} % ~~~ js % if (a > 3) { % moveShip(5 * gravity, DOWN); % } % ~~~~~~ % % ``` html %
%
% // Some comments
% line 1 of code
% line 2 of code
% line 3 of code
%
%
% ```
% \end{Verbatim}
% \item[true] Disable the commonmark fenced code block extension.
% \end{valuelist}
% \begin{macrocode}
defaultOptions.fencedCode = false
% \end{macrocode}
%
% \Optitem[false]{footnotes}{\opt{true}, \opt{false}}
% \begin{valuelist}
% \item[true] Enable the pandoc footnote syntax extension:
% \begin{Verbatim}
% Here is a footnote reference,[^1] and another.[^longnote]
%
% [^1]: Here is the footnote.
%
% [^longnote]: Here's one with multiple blocks.
%
% Subsequent paragraphs are indented to show that they
% belong to the previous footnote.
%
% { some.code }
%
% The whole paragraph can be indented, or just the
% first line. In this way, multi-paragraph footnotes
% work like multi-paragraph list items.
%
% This paragraph won't be part of the note, because it
% isn't indented.
% \end{Verbatim}
% \item[false] Disable the pandoc footnote syntax extension.
% \end{valuelist}
% \begin{macrocode}
defaultOptions.footnotes = false
% \end{macrocode}
%
% \Optitem[false]{inlineFootnotes}{\opt{true}, \opt{false}}
% \begin{valuelist}
% \item[true] Enable the pandoc inline footnote syntax extension:
% \begin{Verbatim}
% Here is an inline note.^[Inlines notes are easier to
% write, since you don't have to pick an identifier and
% move down to type the note.]
% \end{Verbatim}
% \item[false] Disable the pandoc inline footnote syntax extension.
% \end{valuelist}
% \begin{macrocode}
defaultOptions.inlineFootnotes = false
% \end{macrocode}
%
% \Optitem[false]{preserveTabs}{\opt{true}, \opt{false}}
% \begin{valuelist}
% \item[true] Preserve all tabs in the input.
% \item[false] Convert any tabs in the input to spaces.
% \end{valuelist}
% \begin{macrocode}
defaultOptions.preserveTabs = false
% \end{macrocode}
%
% \Optitem[false]{smartEllipses}{\opt{true}, \opt{false}}
% \begin{valuelist}
% \item[true] Convert any ellipses in the input to the
% \m{markdownRendererEllipsis} \TeX{} macro.
% \item[false] Preserve all ellipses in the input.
% \end{valuelist}
% \begin{macrocode}
defaultOptions.smartEllipses = false
% \end{macrocode}
%
% \Optitem[true]{startNumber}{\opt{true}, \opt{false}}
% \begin{valuelist}
% \item[true] Make the number in the first item in ordered lists
% significant. The item numbers will be passed to the
% \m{markdownRendererOlItemWithNumber} \TeX{} macro.
% \item[false] Ignore the number in the items of ordered lists. Each
% item will only produce a \m{markdownRendererOlItem} \TeX{} macro.
% \end{valuelist}
% \begin{macrocode}
defaultOptions.startNumber = true
% \end{macrocode}
%
% \Optitem[true]{tightLists}{\opt{true}, \opt{false}}
% \begin{valuelist}
% \item[true] Lists whose bullets do not consist of multiple paragraphs
% will be detected and passed to the
% \m{markdownRendererOlBeginTight}, \m{markdownRendererOlEndTight},
% \m{markdownRendererUlBeginTight}, \m{markdownRendererUlEndTight},
% \m{markdownRendererDlBeginTight}, and \m{markdownRendererDlEndTight}
% macros.
% \item[false] Lists whose bullets do not consist of multiple paragraphs
% will be treated the same way as lists that do.
% \end{valuelist}
% \begin{macrocode}
defaultOptions.tightLists = true
% \end{macrocode}
% \end{optionlist}
%
% \iffalse
%
%<*tex>
% \fi\subsection{Plain \TeX{} Interface}\label{sec:texinterface}
% The plain \TeX{} interface provides macros for the typesetting of markdown input
% from within plain \TeX{}, for setting the Lua interface options (see Section
% \ref{sec:luaoptions}) used during the conversion from markdown to plain
% \TeX{}, and for changing the way markdown the tokens are rendered.
% \begin{macrocode}
\def\markdownLastModified{2016/12/09}%
\def\markdownVersion{2.2.2}%
% \end{macrocode}
%
% The plain \TeX{} interface is implemented by the \t`markdown.tex` file that
% can be loaded as follows:
% \begin{Verbatim}
% \input markdown
% \end{Verbatim}
% It is expected that the special plain \TeX{} characters have the expected
% category codes, when \m{input}ting the file.
%
% \subsubsection{Typesetting Markdown}\label{sec:textypesetting}
% The interface exposes the \mdef{markdownBegin}, \mdef{markdownEnd}, and
% \mdef{markdownInput} macros.
%
% The \m{markdownBegin} macro marks the beginning of a markdown document
% fragment and the \m{markdownEnd} macro marks its end.
% \begin{macrocode}
\let\markdownBegin\relax
\let\markdownEnd\relax
% \end{macrocode}
% You may prepend your own code to the \m{markdownBegin} macro and redefine the
% \m{markdownEnd} macro to produce special effects before and after the
% markdown block.
%
% There are several limitations to the macros you need to be aware of.
% The first limitation concerns the \m{markdownEnd} macro, which must be
% visible directly from the input line buffer (it may not be produced as a
% result of input expansion). Otherwise, it will not be recognized as the end
% of the markdown string otherwise. As a corrolary, the \m{markdownEnd} string
% may not appear anywhere inside the markdown input.
%
% Another limitation concerns spaces at the right end of an input line. In
% markdown, these are used to produce a forced line break. However, any such
% spaces are removed before the lines enter the input buffer of \TeX{} (see
% \cite[p.~46]{knuth86}). As a corrolary, the \m{markdownBegin} macro also
% ignores them.
%
% The \m{markdownBegin} and \m{markdownEnd} macros will also consume the rest
% of the lines at which they appear. In the following example plain \TeX{}
% code, the characters \t`c`, \t`e`, and \t`f` will not appear in the output.
% \begin{Verbatim}
% \input markdown
% a
% b \markdownBegin c
% d
% e \markdownEnd f
% g
% \bye
% \end{Verbatim}
%
% Note that you may also not nest the \m{markdownBegin} and \m{markdownEnd}
% macros.
%
% The following example plain \TeX{} code showcases the usage of the
% \m{markdownBegin} and \m{markdownEnd} macros:
% \begin{Verbatim}
% \input markdown
% \markdownBegin
% _Hello_ **world** ...
% \markdownEnd
% \bye
% \end{Verbatim}
%
% The \m{markdownInput} macro accepts a single parameter containing the
% filename of a markdown document and expands to the result of the conversion
% of the input markdown document to plain \TeX{}.
% \begin{macrocode}
\let\markdownInput\relax
% \end{macrocode}
% This macro is not subject to the abovelisted limitations of the
% \m{markdownBegin} and \m{markdownEnd} macros.
%
% The following example plain \TeX{} code showcases the usage of the
% \m{markdownInput} macro:
% \begin{Verbatim}
% \input markdown
% \markdownInput{hello.md}
% \bye
% \end{Verbatim}
%
% \subsubsection{Options}\label{sec:texoptions}
% The plain \TeX{} options are represented by \TeX{} macros. Some of them map
% directly to the options recognized by the Lua interface (see Section
% \ref{sec:luaoptions}), while some of them are specific to the plain \TeX{}
% interface.
%
% \paragraph{File and directory names}
% The \mdef{markdownOptionHelperScriptFileName} macro sets the filename of the
% helper Lua script file that is created during the conversion from markdown to
% plain \TeX{} in \TeX{} engines without the \m{directlua} primitive. It
% defaults to \m{jobname}\t`.markdown.lua`, where \m{jobname} is the base name
% of the document being typeset.
%
% The expansion of this macro must not contain quotation marks (\t`"`) or
% backslash symbols (\t`\textbackslash`). Mind that \TeX{} engines tend to
% put quotation marks around \m{jobname}, when it contains spaces.
% \begin{macrocode}
\def\markdownOptionHelperScriptFileName{\jobname.markdown.lua}%
% \end{macrocode}
%
% The \mdef{markdownOptionInputTempFileName} macro sets the filename of the
% temporary input file that is created during the conversion from markdown to
% plain \TeX{} in \TeX{} engines without the \m{directlua} primitive. It
% defaults to \m{jobname}\t`.markdown.out`. The same limitations as in
% the case of the \m{markdownOptionHelperScriptFileName} macro apply here.
% \begin{macrocode}
\def\markdownOptionInputTempFileName{\jobname.markdown.in}%
% \end{macrocode}
%
% The \mdef{markdownOptionOutputTempFileName} macro sets the filename of the
% temporary output file that is created during the conversion from markdown to
% plain \TeX{} in \TeX{} engines without the \m{directlua} primitive. It
% defaults to \m{jobname}\t`.markdown.out`. The same limitations apply here as
% in the case of the \m{markdownOptionHelperScriptFileName} macro.
% \begin{macrocode}
\def\markdownOptionOutputTempFileName{\jobname.markdown.out}%
% \end{macrocode}
%
% The \mdef{markdownOptionCacheDir} macro corresponds to the Lua interface
% \Opt{cacheDir} option that sets the name of the directory that will contain
% the produced cache files. The option defaults to \t`_markdown_`\m{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
% \m{markdownOptionHelperScriptFileName} macro.
% \begin{macrocode}
\def\markdownOptionCacheDir{./_markdown_\jobname}%
% \end{macrocode}
%
% \paragraph{Lua Interface Options}
% The following macros map directly to the options recognized by the Lua
% interface (see Section \ref{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.
% \begin{macrocode}
\let\markdownOptionBlankBeforeBlockquote\undefined
\let\markdownOptionBlankBeforeCodeFence\undefined
\let\markdownOptionBlankBeforeHeading\undefined
\let\markdownOptionCitations\undefined
\let\markdownOptionCitationNbsps\undefined
\let\markdownOptionDefinitionLists\undefined
\let\markdownOptionFootnotes\undefined
\let\markdownOptionFencedCode\undefined
\let\markdownOptionHashEnumerators\undefined
\let\markdownOptionHybrid\undefined
\let\markdownOptionInlineFootnotes\undefined
\let\markdownOptionPreserveTabs\undefined
\let\markdownOptionSmartEllipses\undefined
\let\markdownOptionStartNumber\undefined
\let\markdownOptionTightLists\undefined
% \end{macrocode}
%
% \subsubsection{Token Renderers}\label{sec:texrenderersuser}
% The following \TeX{} macros may occur inside the output of the
% converter functions exposed by the Lua interface (see Section
% \ref{sec:luaconversion}) and represent the parsed markdown tokens. These
% macros are intended to be redefined by the user who is typesetting a
% document. By default, they point to the corresponding prototypes (see Section
% \ref{sec:texrendererprototypes}).
%
% \paragraph{Interblock Separator Renderer}
% The \mdef{markdownRendererInterblockSeparator} macro represents a separator
% between two markdown block elements. The macro receives no arguments.
% \begin{macrocode}
\def\markdownRendererInterblockSeparator{%
\markdownRendererInterblockSeparatorPrototype}%
% \end{macrocode}
%
% \paragraph{Line Break Renderer}
% The \mdef{markdownRendererLineBreak} macro represents a forced line break.
% The macro receives no arguments.
% \begin{macrocode}
\def\markdownRendererLineBreak{%
\markdownRendererLineBreakPrototype}%
% \end{macrocode}
%
% \paragraph{Ellipsis Renderer}
% The \mdef{markdownRendererEllipsis} macro replaces any occurance of ASCII
% ellipses in the input text. This macro will only be produced, when the
% \Opt{smartEllipses} option is \t`true`. The macro receives no arguments.
% \begin{macrocode}
\def\markdownRendererEllipsis{%
\markdownRendererEllipsisPrototype}%
% \end{macrocode}
%
% \paragraph{Non-breaking Space Renderer}
% The \mdef{markdownRendererNbsp} macro represents a non-breaking space.
% \begin{macrocode}
\def\markdownRendererNbsp{%
\markdownRendererNbspPrototype}%
% \end{macrocode}
%
% \paragraph{Special Character Renderers}
% The following macros replace any special plain \TeX{} characters (including
% the active pipe character (\t`|`) of \Hologo{ConTeXt}) in the input text.
% These macros will only be produced, when the \Opt{hybrid} option is
% \t`false`.
% \begin{macrocode}
\def\markdownRendererLeftBrace{%
\markdownRendererLeftBracePrototype}%
\def\markdownRendererRightBrace{%
\markdownRendererRightBracePrototype}%
\def\markdownRendererDollarSign{%
\markdownRendererDollarSignPrototype}%
\def\markdownRendererPercentSign{%
\markdownRendererPercentSignPrototype}%
\def\markdownRendererAmpersand{%
\markdownRendererAmpersandPrototype}%
\def\markdownRendererUnderscore{%
\markdownRendererUnderscorePrototype}%
\def\markdownRendererHash{%
\markdownRendererHashPrototype}%
\def\markdownRendererCircumflex{%
\markdownRendererCircumflexPrototype}%
\def\markdownRendererBackslash{%
\markdownRendererBackslashPrototype}%
\def\markdownRendererTilde{%
\markdownRendererTildePrototype}%
\def\markdownRendererPipe{%
\markdownRendererPipePrototype}%
% \end{macrocode}
%
% \paragraph{Code Span Renderer}
% The \mdef{markdownRendererCodeSpan} macro represents inlined code span in the
% input text. It receives a single argument that corresponds to the inlined
% code span.
% \begin{macrocode}
\def\markdownRendererCodeSpan{%
\markdownRendererCodeSpanPrototype}%
% \end{macrocode}
%
% \paragraph{Link Renderer}
% The \mdef{markdownRendererLink} macro represents a hyperlink. It receives
% four arguments: the label, the fully escaped \acro{uri} that can be directly
% typeset, the raw \acro{uri} that can be used outside typesetting, and the
% title of the link.
% \begin{macrocode}
\def\markdownRendererLink{%
\markdownRendererLinkPrototype}%
% \end{macrocode}
%
% \paragraph{Image Renderer}
% The \mdef{markdownRendererImage} macro represents an image. It receives four
% four arguments: the label, the fully escaped \acro{uri} that can be directly
% typeset, the raw \acro{uri} that can be used outside typesetting, and the
% title of the link.
% \begin{macrocode}
\def\markdownRendererImage{%
\markdownRendererImagePrototype}%
% \end{macrocode}
%
% \paragraph{Bullet List Renderers}
% The \mdef{markdownRendererUlBegin} macro represents the beginning of a
% bulleted list that contains an item with several paragraphs of text (the
% list is not tight). The macro receives no arguments.
% \begin{macrocode}
\def\markdownRendererUlBegin{%
\markdownRendererUlBeginPrototype}%
% \end{macrocode}
%
% The \mdef{markdownRendererUlBeginTight} macro represents the beginning of a
% bulleted list that contains no item with several paragraphs of text (the list
% is tight). This macro will only be produced, when the \Opt{tightLists} option
% is \t`false`. The macro receives no arguments.
% \begin{macrocode}
\def\markdownRendererUlBeginTight{%
\markdownRendererUlBeginTightPrototype}%
% \end{macrocode}
%
% The \mdef{markdownRendererUlItem} macro represents an item in a bulleted
% list. The macro receives no arguments.
% \begin{macrocode}
\def\markdownRendererUlItem{%
\markdownRendererUlItemPrototype}%
% \end{macrocode}
%
% The \mdef{markdownRendererUlItemEnd} macro represents the end of an item in a
% bulleted list. The macro receives no arguments.
% \begin{macrocode}
\def\markdownRendererUlItemEnd{%
\markdownRendererUlItemEndPrototype}%
% \end{macrocode}
%
% The \mdef{markdownRendererUlEnd} macro represents the end of a bulleted list
% that contains an item with several paragraphs of text (the list is not
% tight). The macro receives no arguments.
% \begin{macrocode}
\def\markdownRendererUlEnd{%
\markdownRendererUlEndPrototype}%
% \end{macrocode}
%
% The \mdef{markdownRendererUlEndTight} macro represents the end of a bulleted
% list that contains no item with several paragraphs of text (the list is
% tight). This macro will only be produced, when the \Opt{tightLists} option is
% \t`false`. The macro receives no arguments.
% \begin{macrocode}
\def\markdownRendererUlEndTight{%
\markdownRendererUlEndTightPrototype}%
% \end{macrocode}
%
% \paragraph{Ordered List Renderers}
% The \mdef{markdownRendererOlBegin} macro represents the beginning of an
% ordered list that contains an item with several paragraphs of text (the
% list is not tight). The macro receives no arguments.
% \begin{macrocode}
\def\markdownRendererOlBegin{%
\markdownRendererOlBeginPrototype}%
% \end{macrocode}
%
% The \mdef{markdownRendererOlBeginTight} macro represents the beginning of an
% ordered list that contains no item with several paragraphs of text (the
% list is tight). This macro will only be produced, when the \Opt{tightLists}
% option is \t`false`. The macro receives no arguments.
% \begin{macrocode}
\def\markdownRendererOlBeginTight{%
\markdownRendererOlBeginTightPrototype}%
% \end{macrocode}
%
% The \mdef{markdownRendererOlItem} macro represents an item in an ordered list.
% This macro will only be produced, when the \Opt{startNumber} option is
% \t`false`. The macro receives no arguments.
% \begin{macrocode}
\def\markdownRendererOlItem{%
\markdownRendererOlItemPrototype}%
% \end{macrocode}
%
% The \mdef{markdownRendererOlItemEnd} macro represents the end of an item in
% an ordered list. The macro receives no arguments.
% \begin{macrocode}
\def\markdownRendererOlItemEnd{%
\markdownRendererOlItemEndPrototype}%
% \end{macrocode}
%
% The \mdef{markdownRendererOlItemWithNumber} macro represents an item in an
% ordered list. This macro will only be produced, when the \Opt{startNumber}
% option is \t`true`. The macro receives no arguments.
% \begin{macrocode}
\def\markdownRendererOlItemWithNumber{%
\markdownRendererOlItemWithNumberPrototype}%
% \end{macrocode}
%
% The \mdef{markdownRendererOlEnd} macro represents the end of an ordered list
% that contains an item with several paragraphs of text (the list is not
% tight). The macro receives no arguments.
% \begin{macrocode}
\def\markdownRendererOlEnd{%
\markdownRendererOlEndPrototype}%
% \end{macrocode}
%
% The \mdef{markdownRendererOlEndTight} macro represents the end of an ordered
% list that contains no item with several paragraphs of text (the list is
% tight). This macro will only be produced, when the \Opt{tightLists} option is
% \t`false`. The macro receives no arguments.
% \begin{macrocode}
\def\markdownRendererOlEndTight{%
\markdownRendererOlEndTightPrototype}%
% \end{macrocode}
%
% \paragraph{Definition List Renderers}
% The following macros are only produces, when the \Opt{definitionLists} option
% is \t`true`.
%
% The \mdef{markdownRendererDlBegin} macro represents the beginning of a
% definition list that contains an item with several paragraphs of text (the
% list is not tight). The macro receives no arguments.
% \begin{macrocode}
\def\markdownRendererDlBegin{%
\markdownRendererDlBeginPrototype}%
% \end{macrocode}
%
% The \mdef{markdownRendererDlBeginTight} macro represents the beginning of a
% definition list that contains an item with several paragraphs of text (the
% list is not tight). This macro will only be produced, when the
% \Opt{tightLists} option is \t`false`. The macro receives no arguments.
% \begin{macrocode}
\def\markdownRendererDlBeginTight{%
\markdownRendererDlBeginTightPrototype}%
% \end{macrocode}
%
% The \mdef{markdownRendererDlItem} macro represents a term in a definition
% list. The macro receives a single argument that corresponds to the term
% being defined.
% \begin{macrocode}
\def\markdownRendererDlItem{%
\markdownRendererDlItemPrototype}%
% \end{macrocode}
%
% The \mdef{markdownRendererDlItemEnd} macro represents the end of a list of
% definitions for a single term.
% \begin{macrocode}
\def\markdownRendererDlItemEnd{%
\markdownRendererDlItemEndPrototype}%
% \end{macrocode}
%
% The \mdef{markdownRendererDlDefinitionBegin} macro represents the beginning
% of a definition in a definition list. There can be several definitions for
% a single term.
% \begin{macrocode}
\def\markdownRendererDlDefinitionBegin{%
\markdownRendererDlDefinitionBeginPrototype}%
% \end{macrocode}
%
% The \mdef{markdownRendererDlDefinitionEnd} macro represents the end of a
% definition in a definition list. There can be several definitions for a
% single term.
% \begin{macrocode}
\def\markdownRendererDlDefinitionEnd{%
\markdownRendererDlDefinitionEndPrototype}%
% \end{macrocode}
%
% The \mdef{markdownRendererDlEnd} macro represents the end of a definition
% list that contains an item with several paragraphs of text (the list is not
% tight). The macro receives no arguments.
% \begin{macrocode}
\def\markdownRendererDlEnd{%
\markdownRendererDlEndPrototype}%
% \end{macrocode}
%
% The \mdef{markdownRendererDlEndTight} macro represents the end of a
% definition list that contains no item with several paragraphs of text (the
% list is tight). This macro will only be produced, when the \Opt{tightLists}
% option is \t`false`. The macro receives no arguments.
% \begin{macrocode}
\def\markdownRendererDlEndTight{%
\markdownRendererDlEndTightPrototype}%
% \end{macrocode}
%
% \paragraph{Emphasis Renderers}
% The \mdef{markdownRendererEmphasis} macro represents an emphasized span of
% text. The macro receives a single argument that corresponds to the emphasized
% span of text.
% \begin{macrocode}
\def\markdownRendererEmphasis{%
\markdownRendererEmphasisPrototype}%
% \end{macrocode}
%
% The \mdef{markdownRendererStrongEmphasis} macro represents a strongly
% emphasized span of text. The macro receives a single argument that
% corresponds to the emphasized span of text.
% \begin{macrocode}
\def\markdownRendererStrongEmphasis{%
\markdownRendererStrongEmphasisPrototype}%
% \end{macrocode}
%
% \paragraph{Block Quote Renderers}
% The \mdef{markdownRendererBlockQuoteBegin} macro represents the beginning of
% a block quote. The macro receives no arguments.
% \begin{macrocode}
\def\markdownRendererBlockQuoteBegin{%
\markdownRendererBlockQuoteBeginPrototype}%
% \end{macrocode}
%
% The \mdef{markdownRendererBlockQuoteEnd} macro represents the end of a block
% quote. The macro receives no arguments.
% \begin{macrocode}
\def\markdownRendererBlockQuoteEnd{%
\markdownRendererBlockQuoteEndPrototype}%
% \end{macrocode}
%
% \paragraph{Code Block Renderers}
% The \mdef{markdownRendererInputVerbatim} macro represents a code
% block. The macro receives a single argument that corresponds to the
% filename of a file contaning the code block contents.
% \begin{macrocode}
\def\markdownRendererInputVerbatim{%
\markdownRendererInputVerbatimPrototype}%
% \end{macrocode}
%
% The \mdef{markdownRendererInputFencedCode} macro represents a fenced code
% block. This macro will only be produced, when the \Opt{fencedCode} option is
% \t`true`. The macro receives two arguments that correspond to the filename of
% a file contaning the code block contents and to the code fence infostring.
% \begin{macrocode}
\def\markdownRendererInputFencedCode{%
\markdownRendererInputFencedCodePrototype}%
% \end{macrocode}
%
% \paragraph{Heading Renderers}
% The \mdef{markdownRendererHeadingOne} macro represents a first level heading.
% The macro receives a single argument that corresponds to the heading text.
% \begin{macrocode}
\def\markdownRendererHeadingOne{%
\markdownRendererHeadingOnePrototype}%
% \end{macrocode}
%
% The \mdef{markdownRendererHeadingTwo} macro represents a second level
% heading. The macro receives a single argument that corresponds to the heading
% text.
% \begin{macrocode}
\def\markdownRendererHeadingTwo{%
\markdownRendererHeadingTwoPrototype}%
% \end{macrocode}
%
% The \mdef{markdownRendererHeadingThree} macro represents a third level
% heading. The macro receives a single argument that corresponds to the heading
% text.
% \begin{macrocode}
\def\markdownRendererHeadingThree{%
\markdownRendererHeadingThreePrototype}%
% \end{macrocode}
%
% The \mdef{markdownRendererHeadingFour} macro represents a fourth level
% heading. The macro receives a single argument that corresponds to the heading
% text.
% \begin{macrocode}
\def\markdownRendererHeadingFour{%
\markdownRendererHeadingFourPrototype}%
% \end{macrocode}
%
% The \mdef{markdownRendererHeadingFive} macro represents a fifth level
% heading. The macro receives a single argument that corresponds to the heading
% text.
% \begin{macrocode}
\def\markdownRendererHeadingFive{%
\markdownRendererHeadingFivePrototype}%
% \end{macrocode}
%
% The \mdef{markdownRendererHeadingSix} macro represents a sixth level
% heading. The macro receives a single argument that corresponds to the heading
% text.
% \begin{macrocode}
\def\markdownRendererHeadingSix{%
\markdownRendererHeadingSixPrototype}%
% \end{macrocode}
%
% \paragraph{Horizontal Rule Renderer}
% The \mdef{markdownRendererHorizontalRule} macro represents a horizontal rule.
% The macro receives no arguments.
% \begin{macrocode}
\def\markdownRendererHorizontalRule{%
\markdownRendererHorizontalRulePrototype}%
% \end{macrocode}
%
% \paragraph{Footnote Renderer}
% The \mdef{markdownRendererFootnote} macro represents a footnote. This macro
% will only be produced, when the \Opt{footnotes} option is \t`true`. The
% macro receives a single argument that corresponds to the footnote text.
% \begin{macrocode}
\def\markdownRendererFootnote{%
\markdownRendererFootnotePrototype}%
% \end{macrocode}
%
% \paragraph{Parenthesized Citations Renderer}
% The \mdef{markdownRendererCite} macro represents a string of one or more
% parenthetical citations. This macro will only be produced, when the
% \Opt{citations} option is \t`true`. The macro receives the parameter
% \t`\textbraceleft`\meta{number of citations}\t`\textbraceright` followed by
% \meta{suppress author}\t`\textbraceleft`\meta{prenote}\t`\textbraceright^^A
% \textbraceleft`\meta{postnote}\t`\textbraceright\textbraceleft`\meta{name}^^A
% \t`\textbraceright` repeated \meta{number of citations} times. The
% \meta{suppress author} parameter is either the token \t`-`, when the author's
% name is to be suppressed, or \t`+` otherwise.
% \begin{macrocode}
\def\markdownRendererCite{%
\markdownRendererCitePrototype}%
% \end{macrocode}
%
% \paragraph{Text Citations Renderer}
% The \mdef{markdownRendererTextCite} macro represents a string of one or more
% text citations. This macro will only be produced, when the
% \Opt{citations} option is \t`true`. The macro receives parameters in the same
% format as the \m{markdownRendererCite} macro.
% \begin{macrocode}
\def\markdownRendererTextCite{%
\markdownRendererTextCitePrototype}%
% \end{macrocode}
%
% \subsubsection{Token Renderer Prototypes}\label{sec:texrendererprototypes}
% The following \TeX{} macros provide definitions for the token renderers (see
% Section \ref{sec:texrenderersuser}) that have not been redefined by the
% user. These macros are intended to be redefined by macro package authors
% who wish to provide sensible default token renderers. They are also redefined
% by the \LaTeX{} and \Hologo{ConTeXt} implementations (see sections
% \ref{sec:lateximplementation} and \ref{sec:contextimplementation}).
% \begin{macrocode}
\def\markdownRendererInterblockSeparatorPrototype{}%
\def\markdownRendererLineBreakPrototype{}%
\def\markdownRendererEllipsisPrototype{}%
\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\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\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{}%
% \end{macrocode}
%
% \subsubsection{Logging Facilities}
% The \mdef{markdownInfo}, \mdef{markdownWarning}, and
% \mdef{markdownError} macros provide access to logging to the rest of
% the macros. Their first argument specifies the text of the info, warning, or
% error message.
% \begin{macrocode}
\def\markdownInfo#1{}%
\def\markdownWarning#1{}%
% \end{macrocode}
% The \m{markdownError} macro receives a second argument that provides a help
% text suggesting a remedy to the error.
% \begin{macrocode}
\def\markdownError#1{}%
% \end{macrocode}
% You may redefine these macros to redirect and process the info, warning, and
% error messages.
%
% \subsubsection{Miscellanea}
% The \mdef{markdownLuaRegisterIBCallback} and
% \mdef{markdownLuaUnregisterIBCallback} macros specify the Lua code for
% registering and unregistering a callback for changing the contents of the
% line input buffer before a \TeX{} engine that supports direct Lua access via
% the \m{directlua} macro starts looking at it. The first argument of the
% \m{markdownLuaRegisterIBCallback} macro corresponds to the callback function
% being registered.
%
% Local members defined within \m{markdownLuaRegisterIBCallback} are guaranteed
% to be visible from \m{markdownLuaUnregisterIBCallback} and the execution of
% the two macros alternates, so it is not necessary to consider the case, when
% one of the macros is called twice in a row.
% \begin{macrocode}
\def\markdownLuaRegisterIBCallback#1{%
local old_callback = callback.find("process_input_buffer")
callback.register("process_input_buffer", #1)}%
\def\markdownLuaUnregisterIBCallback{%
callback.register("process_input_buffer", old_callback)}%
% \end{macrocode}
%
% The \mdef{markdownMakeOther} macro is used by the package, when a \TeX{}
% engine that does not support direct Lua access is starting to buffer a text.
% The plain \TeX{} implementation changes the category code of plain \TeX{}
% special characters to other, but there may be other active characters that
% may break the output. This macro should temporarily change the category of
% these to \emph{other}.
% \begin{macrocode}
\let\markdownMakeOther\relax
% \end{macrocode}
%
% The \mdef{markdownReadAndConvert} macro implements the \m{markdownBegin}
% macro. The first argument specifies the token sequence that will terminate
% the markdown input (\m{markdownEnd} in the instance of the \m{markdownBegin}
% macro) when the plain \TeX{} special characters have had their category
% changed to \emph{other}. The second argument specifies the token sequence
% that will actually be inserted into the document, when the ending token
% sequence has been found.
% \begin{macrocode}
\let\markdownReadAndConvert\relax
\begingroup
% \end{macrocode}
% Locally swap the category code of the backslash symbol (\t`\textbackslash`)
% with the pipe symbol (\t`|`). This is required in order that all the special
% symbols in the first argument of the \t`markdownReadAndConvert` macro have
% the category code \emph{other}.
% \begin{macrocode}
\catcode`\|=0\catcode`\\=12%
|gdef|markdownBegin{%
|markdownReadAndConvert{\markdownEnd}%
{|markdownEnd}}%
|endgroup
% \end{macrocode}
% The macro is exposed in the interface, so that the user can create their own
% markdown environments. Due to the way the arguments are passed to Lua (see
% Section \ref{sec:directlua}), the first argument may not contain the
% string \t`]]` (regardless of the category code of the bracket symbol (\t`]`)).
%
% The \mdef{markdownMode} macro specifies how the plain \TeX{} implementation
% interfaces with the Lua interface. The valid values and their meaning are
% as follows:
% \begin{itemize}
% \item\t`0` -- Shell escape via the 18 output file stream
% \item\t`1` -- Shell escape via the Lua \luam{os.execute} method
% \item\t`2` -- Direct Lua access
% \end{itemize}
% 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{}
% implementation, the correct value will be automatically detected. The outcome
% of changing the value of \m{markdownMode} after the implementation has been
% loaded is undefined.
% \begin{macrocode}
\ifx\markdownMode\undefined
\ifx\directlua\undefined
\def\markdownMode{0}%
\else
\def\markdownMode{2}%
\fi
\fi
% \end{macrocode}
%
%
% \iffalse
%
%<*latex>
% \fi\subsection{\LaTeX{} Interface}\label{sec:latexinterface}
% The \LaTeX{} interface provides \LaTeX{} environments for the typesetting of
% markdown input from within \LaTeX{}, facilities for setting Lua interface
% options (see Section \ref{sec:luaoptions}) used during the conversion from
% markdown to plain \TeX{}, and facilities for changing the way markdown tokens
% are rendered. The rest of the interface is inherited from the plain \TeX{}
% interface (see Section \ref{sec:texinterface}).
%
% The \LaTeX{} interface is implemented by the \t`markdown.sty` file, which
% can be loaded from the \LaTeX{} document preamble as follows:
% \begin{Verbatim}[commandchars=\\\{\}]
% \textbackslash{}usepackage[\textrm{\meta{options}}]\{markdown\}
% \end{Verbatim}
% where \meta{options} are the \LaTeX{} interface options (see Section
% \ref{sec:latexoptions}). Note that \meta{options} inside the \m{usepackage}
% macro may not set the \t`markdownRenderers` (see Section
% \ref{sec:latexrenderers}) and \t`markdownRendererPrototypes` (see Section
% \ref{sec:latexrendererprototypes}) keys. This limitation is due to the way
% \Hologo{LaTeX2e} parses package options.
%
% \subsubsection{Typesetting Markdown}
% The interface exposes the \envmdef{markdown} and \envmdef{markdown*}
% \LaTeX{} environments, and redefines the \m{markdownInput} command.
%
% The \envm{markdown} and \envm{markdown*} \LaTeX{} environments are used to
% typeset markdown document fragments. The starred version of the
% \envm{markdown} environment accepts \LaTeX{} interface options (see
% Section \ref{sec:latexoptions}) as its only argument. These options will
% only influnce this markdown document fragment.
% \begin{macrocode}
\newenvironment{markdown}\relax\relax
\newenvironment{markdown*}[1]\relax\relax
% \end{macrocode}
% You may prepend your own code to the \m{markdown} macro and append your own
% code to the \m{endmarkdown} macro to produce special effects before and after
% the \envm{markdown} \LaTeX{} environment (and likewise for the starred
% version).
%
% Note that the \envm{markdown} and \envm{markdown*} \LaTeX{} environments are
% subject to the same limitations as the \m{markdownBegin} and \m{markdownEnd}
% macros exposed by the plain \TeX{} interface.
%
% The following example \LaTeX{} code showcases the usage of the
% \envm{markdown} and \envm{markdown*} environments:
% \begin{Verbatim}
% \documentclass{article} \documentclass{article}
% \usepackage{markdown} \usepackage{markdown}
% \begin{document} \begin{document}
% % ... % ...
% \begin{markdown} \begin{markdown*}{smartEllipses}
% _Hello_ **world** ... _Hello_ **world** ...
% \end{markdown} \end{markdown*}
% % ... % ...
% \end{document} \end{document}
% \end{Verbatim}
%
% The \m{markdownInput} macro accepts a single mandatory parameter containing
% the filename of a markdown document and expands to the result of the
% conversion of the input markdown document to plain \TeX{}. Unlike the
% \m{markdownInput} macro provided by the plain \TeX{} interface, this macro
% also accepts \LaTeX{} interface options (see Section \ref{sec:latexoptions})
% as its optional argument. These options will only influnce this markdown
% document.
%
% The following example \LaTeX{} code showcases the usage of the
% \m{markdownInput} macro:
% \begin{Verbatim}
% \documentclass{article}
% \usepackage{markdown}
% \begin{document}
% % ...
% \markdownInput[smartEllipses]{hello.md}
% % ...
% \end{document}
% \end{Verbatim}
%
% \subsubsection{Options}\label{sec:latexoptions}
% The \LaTeX{} options are represented by a comma-delimited list of
% \meta{\meta{key}=\meta{value}} pairs. For boolean options, the
% \meta{=\meta{value}} part is optional, and \meta{\meta{key}} will be
% interpreted as \meta{\meta{key}=true}.
%
% The \LaTeX{} options map directly to the options recognized by the plain
% \TeX{} interface (see Section \ref{sec:texoptions}) and to the markdown token
% renderers and their prototypes recognized by the plain \TeX{} interface (see
% Sections \ref{sec:texrenderersuser} and \ref{sec:texrendererprototypes}).
%
% The \LaTeX{} options may be specified when loading the \LaTeX{} package (see
% Section \ref{sec:latexinterface}), when using the \envm{markdown*} \LaTeX{}
% environment, or via the \mdef{markdownSetup} macro. The \m{markdownSetup}
% macro receives the options to set up as its only argument.
% \begin{macrocode}
\newcommand\markdownSetup[1]{%
\setkeys{markdownOptions}{#1}}%
% \end{macrocode}
%
% \paragraph{Plain \TeX{} Interface Options}
% The following options map directly to the option macros exposed by the plain
% \TeX{} interface (see Section \ref{sec:texoptions}).
% \begin{macrocode}
\RequirePackage{keyval}
\define@key{markdownOptions}{helperScriptFileName}{%
\def\markdownOptionHelperScriptFileName{#1}}%
\define@key{markdownOptions}{inputTempFileName}{%
\def\markdownOptionInputTempFileName{#1}}%
\define@key{markdownOptions}{outputTempFileName}{%
\def\markdownOptionOutputTempFileName{#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}{citations}[true]{%
\def\markdownOptionCitations{#1}}%
\define@key{markdownOptions}{citationNbsps}[true]{%
\def\markdownOptionCitationNbsps{#1}}%
\define@key{markdownOptions}{cacheDir}{%
\def\markdownOptionCacheDir{#1}}%
\define@key{markdownOptions}{definitionLists}[true]{%
\def\markdownOptionDefinitionLists{#1}}%
\define@key{markdownOptions}{footnotes}[true]{%
\def\markdownOptionFootnotes{#1}}%
\define@key{markdownOptions}{fencedCode}[true]{%
\def\markdownOptionFencedCode{#1}}%
\define@key{markdownOptions}{hashEnumerators}[true]{%
\def\markdownOptionHashEnumerators{#1}}%
\define@key{markdownOptions}{hybrid}[true]{%
\def\markdownOptionHybrid{#1}}%
\define@key{markdownOptions}{inlineFootnotes}[true]{%
\def\markdownOptionInlineFootnotes{#1}}%
\define@key{markdownOptions}{preserveTabs}[true]{%
\def\markdownOptionPreserveTabs{#1}}%
\define@key{markdownOptions}{smartEllipses}[true]{%
\def\markdownOptionSmartEllipses{#1}}%
\define@key{markdownOptions}{startNumber}[true]{%
\def\markdownOptionStartNumber{#1}}%
\define@key{markdownOptions}{tightLists}[true]{%
\def\markdownOptionTightLists{#1}}%
% \end{macrocode}
%
% The following example \LaTeX{} code showcases a possible configuration of
% plain \TeX{} interface options \m{markdownOptionHybrid},
% \m{markdownOptionSmartEllipses}, and \m{markdownOptionCacheDir}.
% \begin{Verbatim}
% \markdownSetup{
% hybrid,
% smartEllipses,
% cacheDir = /tmp,
% }
% \end{Verbatim}
%
% \paragraph{Plain \TeX{} Markdown Token Renderers}\label{sec:latexrenderers}
% The \LaTeX{} interface recognizes an option with the \t`renderers` key,
% whose value must be a list of options that map directly to the markdown token
% renderer macros exposed by the plain \TeX{} interface (see Section
% \ref{sec:texrenderersuser}).
% \begin{macrocode}
\define@key{markdownRenderers}{interblockSeparator}{%
\renewcommand\markdownRendererInterblockSeparator{#1}}%
\define@key{markdownRenderers}{lineBreak}{%
\renewcommand\markdownRendererLineBreak{#1}}%
\define@key{markdownRenderers}{ellipsis}{%
\renewcommand\markdownRendererEllipsis{#1}}%
\define@key{markdownRenderers}{nbsp}{%
\renewcommand\markdownRendererNbsp{#1}}%
\define@key{markdownRenderers}{leftBrace}{%
\renewcommand\markdownRendererLeftBrace{#1}}%
\define@key{markdownRenderers}{rightBrace}{%
\renewcommand\markdownRendererRightBrace{#1}}%
\define@key{markdownRenderers}{dollarSign}{%
\renewcommand\markdownRendererDollarSign{#1}}%
\define@key{markdownRenderers}{percentSign}{%
\renewcommand\markdownRendererPercentSign{#1}}%
\define@key{markdownRenderers}{ampersand}{%
\renewcommand\markdownRendererAmpersand{#1}}%
\define@key{markdownRenderers}{underscore}{%
\renewcommand\markdownRendererUnderscore{#1}}%
\define@key{markdownRenderers}{hash}{%
\renewcommand\markdownRendererHash{#1}}%
\define@key{markdownRenderers}{circumflex}{%
\renewcommand\markdownRendererCircumflex{#1}}%
\define@key{markdownRenderers}{backslash}{%
\renewcommand\markdownRendererBackslash{#1}}%
\define@key{markdownRenderers}{tilde}{%
\renewcommand\markdownRendererTilde{#1}}%
\define@key{markdownRenderers}{pipe}{%
\renewcommand\markdownRendererPipe{#1}}%
\define@key{markdownRenderers}{codeSpan}{%
\renewcommand\markdownRendererCodeSpan[1]{#1}}%
\define@key{markdownRenderers}{link}{%
\renewcommand\markdownRendererLink[4]{#1}}%
\define@key{markdownRenderers}{image}{%
\renewcommand\markdownRendererImage[4]{#1}}%
\define@key{markdownRenderers}{ulBegin}{%
\renewcommand\markdownRendererUlBegin{#1}}%
\define@key{markdownRenderers}{ulBeginTight}{%
\renewcommand\markdownRendererUlBeginTight{#1}}%
\define@key{markdownRenderers}{ulItem}{%
\renewcommand\markdownRendererUlItem{#1}}%
\define@key{markdownRenderers}{ulItemEnd}{%
\renewcommand\markdownRendererUlItemEnd{#1}}%
\define@key{markdownRenderers}{ulEnd}{%
\renewcommand\markdownRendererUlEnd{#1}}%
\define@key{markdownRenderers}{ulEndTight}{%
\renewcommand\markdownRendererUlEndTight{#1}}%
\define@key{markdownRenderers}{olBegin}{%
\renewcommand\markdownRendererOlBegin{#1}}%
\define@key{markdownRenderers}{olBeginTight}{%
\renewcommand\markdownRendererOlBeginTight{#1}}%
\define@key{markdownRenderers}{olItem}{%
\renewcommand\markdownRendererOlItem{#1}}%
\define@key{markdownRenderers}{olItemWithNumber}{%
\renewcommand\markdownRendererOlItemWithNumber[1]{#1}}%
\define@key{markdownRenderers}{olItemEnd}{%
\renewcommand\markdownRendererOlItemEnd{#1}}%
\define@key{markdownRenderers}{olEnd}{%
\renewcommand\markdownRendererOlEnd{#1}}%
\define@key{markdownRenderers}{olEndTight}{%
\renewcommand\markdownRendererOlEndTight{#1}}%
\define@key{markdownRenderers}{dlBegin}{%
\renewcommand\markdownRendererDlBegin{#1}}%
\define@key{markdownRenderers}{dlBeginTight}{%
\renewcommand\markdownRendererDlBeginTight{#1}}%
\define@key{markdownRenderers}{dlItem}{%
\renewcommand\markdownRendererDlItem[1]{#1}}%
\define@key{markdownRenderers}{dlItemEnd}{%
\renewcommand\markdownRendererDlItemEnd{#1}}%
\define@key{markdownRenderers}{dlDefinitionBegin}{%
\renewcommand\markdownRendererDlDefinitionBegin{#1}}%
\define@key{markdownRenderers}{dlDefinitionEnd}{%
\renewcommand\markdownRendererDlDefinitionEnd{#1}}%
\define@key{markdownRenderers}{dlEnd}{%
\renewcommand\markdownRendererDlEnd{#1}}%
\define@key{markdownRenderers}{dlEndTight}{%
\renewcommand\markdownRendererDlEndTight{#1}}%
\define@key{markdownRenderers}{emphasis}{%
\renewcommand\markdownRendererEmphasis[1]{#1}}%
\define@key{markdownRenderers}{strongEmphasis}{%
\renewcommand\markdownRendererStrongEmphasis[1]{#1}}%
\define@key{markdownRenderers}{blockQuoteBegin}{%
\renewcommand\markdownRendererBlockQuoteBegin{#1}}%
\define@key{markdownRenderers}{blockQuoteEnd}{%
\renewcommand\markdownRendererBlockQuoteEnd{#1}}%
\define@key{markdownRenderers}{inputVerbatim}{%
\renewcommand\markdownRendererInputVerbatim[1]{#1}}%
\define@key{markdownRenderers}{inputFencedCode}{%
\renewcommand\markdownRendererInputFencedCode[2]{#1}}%
\define@key{markdownRenderers}{headingOne}{%
\renewcommand\markdownRendererHeadingOne[1]{#1}}%
\define@key{markdownRenderers}{headingTwo}{%
\renewcommand\markdownRendererHeadingTwo[1]{#1}}%
\define@key{markdownRenderers}{headingThree}{%
\renewcommand\markdownRendererHeadingThree[1]{#1}}%
\define@key{markdownRenderers}{headingFour}{%
\renewcommand\markdownRendererHeadingFour[1]{#1}}%
\define@key{markdownRenderers}{headingFive}{%
\renewcommand\markdownRendererHeadingFive[1]{#1}}%
\define@key{markdownRenderers}{headingSix}{%
\renewcommand\markdownRendererHeadingSix[1]{#1}}%
\define@key{markdownRenderers}{horizontalRule}{%
\renewcommand\markdownRendererHorizontalRule{#1}}%
\define@key{markdownRenderers}{footnote}{%
\renewcommand\markdownRendererFootnote[1]{#1}}%
\define@key{markdownRenderers}{cite}{%
\renewcommand\markdownRendererCite[1]{#1}}%
\define@key{markdownRenderers}{textCite}{%
\renewcommand\markdownRendererTextCite[1]{#1}}%
% \end{macrocode}
%
% The following example \LaTeX{} code showcases a possible configuration of the
% \m{markdownRendererLink} and \m{markdownRendererEmphasis} markdown token
% renderers.
% \begin{Verbatim}
% \markdownSetup{
% renderers = {
% link = {#4}, % Render links as the link title.
% emphasis = {\emph{#1}}, % Render emphasized text via `\emph`.
% }
% }
% \end{Verbatim}
%
% \paragraph{Plain \TeX{} Markdown Token Renderer Prototypes}
% \label{sec:latexrendererprototypes}
% The \LaTeX{} interface recognizes an option with the \t`rendererPrototypes`
% key, whose value must be a list of options that map directly to the markdown
% token renderer prototype macros exposed by the plain \TeX{} interface (see
% Section \ref{sec:texrendererprototypes}).
% \begin{macrocode}
\define@key{markdownRendererPrototypes}{interblockSeparator}{%
\renewcommand\markdownRendererInterblockSeparatorPrototype{#1}}%
\define@key{markdownRendererPrototypes}{lineBreak}{%
\renewcommand\markdownRendererLineBreakPrototype{#1}}%
\define@key{markdownRendererPrototypes}{ellipsis}{%
\renewcommand\markdownRendererEllipsisPrototype{#1}}%
\define@key{markdownRendererPrototypes}{nbsp}{%
\renewcommand\markdownRendererNbspPrototype{#1}}%
\define@key{markdownRendererPrototypes}{leftBrace}{%
\renewcommand\markdownRendererLeftBracePrototype{#1}}%
\define@key{markdownRendererPrototypes}{rightBrace}{%
\renewcommand\markdownRendererRightBracePrototype{#1}}%
\define@key{markdownRendererPrototypes}{dollarSign}{%
\renewcommand\markdownRendererDollarSignPrototype{#1}}%
\define@key{markdownRendererPrototypes}{percentSign}{%
\renewcommand\markdownRendererPercentSignPrototype{#1}}%
\define@key{markdownRendererPrototypes}{ampersand}{%
\renewcommand\markdownRendererAmpersandPrototype{#1}}%
\define@key{markdownRendererPrototypes}{underscore}{%
\renewcommand\markdownRendererUnderscorePrototype{#1}}%
\define@key{markdownRendererPrototypes}{hash}{%
\renewcommand\markdownRendererHashPrototype{#1}}%
\define@key{markdownRendererPrototypes}{circumflex}{%
\renewcommand\markdownRendererCircumflexPrototype{#1}}%
\define@key{markdownRendererPrototypes}{backslash}{%
\renewcommand\markdownRendererBackslashPrototype{#1}}%
\define@key{markdownRendererPrototypes}{tilde}{%
\renewcommand\markdownRendererTildePrototype{#1}}%
\define@key{markdownRendererPrototypes}{pipe}{%
\renewcommand\markdownRendererPipePrototype{#1}}%
\define@key{markdownRendererPrototypes}{codeSpan}{%
\renewcommand\markdownRendererCodeSpanPrototype[1]{#1}}%
\define@key{markdownRendererPrototypes}{link}{%
\renewcommand\markdownRendererLinkPrototype[4]{#1}}%
\define@key{markdownRendererPrototypes}{image}{%
\renewcommand\markdownRendererImagePrototype[4]{#1}}%
\define@key{markdownRendererPrototypes}{ulBegin}{%
\renewcommand\markdownRendererUlBeginPrototype{#1}}%
\define@key{markdownRendererPrototypes}{ulBeginTight}{%
\renewcommand\markdownRendererUlBeginTightPrototype{#1}}%
\define@key{markdownRendererPrototypes}{ulItem}{%
\renewcommand\markdownRendererUlItemPrototype{#1}}%
\define@key{markdownRendererPrototypes}{ulItemEnd}{%
\renewcommand\markdownRendererUlItemEndPrototype{#1}}%
\define@key{markdownRendererPrototypes}{ulEnd}{%
\renewcommand\markdownRendererUlEndPrototype{#1}}%
\define@key{markdownRendererPrototypes}{ulEndTight}{%
\renewcommand\markdownRendererUlEndTightPrototype{#1}}%
\define@key{markdownRendererPrototypes}{olBegin}{%
\renewcommand\markdownRendererOlBeginPrototype{#1}}%
\define@key{markdownRendererPrototypes}{olBeginTight}{%
\renewcommand\markdownRendererOlBeginTightPrototype{#1}}%
\define@key{markdownRendererPrototypes}{olItem}{%
\renewcommand\markdownRendererOlItemPrototype{#1}}%
\define@key{markdownRendererPrototypes}{olItemWithNumber}{%
\renewcommand\markdownRendererOlItemWithNumberPrototype[1]{#1}}%
\define@key{markdownRendererPrototypes}{olItemEnd}{%
\renewcommand\markdownRendererOlItemEndPrototype{#1}}%
\define@key{markdownRendererPrototypes}{olEnd}{%
\renewcommand\markdownRendererOlEndPrototype{#1}}%
\define@key{markdownRendererPrototypes}{olEndTight}{%
\renewcommand\markdownRendererOlEndTightPrototype{#1}}%
\define@key{markdownRendererPrototypes}{dlBegin}{%
\renewcommand\markdownRendererDlBeginPrototype{#1}}%
\define@key{markdownRendererPrototypes}{dlBeginTight}{%
\renewcommand\markdownRendererDlBeginTightPrototype{#1}}%
\define@key{markdownRendererPrototypes}{dlItem}{%
\renewcommand\markdownRendererDlItemPrototype[1]{#1}}%
\define@key{markdownRendererPrototypes}{dlItemEnd}{%
\renewcommand\markdownRendererDlItemEndPrototype{#1}}%
\define@key{markdownRendererPrototypes}{dlDefinitionBegin}{%
\renewcommand\markdownRendererDlDefinitionBeginPrototype{#1}}%
\define@key{markdownRendererPrototypes}{dlDefinitionEnd}{%
\renewcommand\markdownRendererDlDefinitionEndPrototype{#1}}%
\define@key{markdownRendererPrototypes}{dlEnd}{%
\renewcommand\markdownRendererDlEndPrototype{#1}}%
\define@key{markdownRendererPrototypes}{dlEndTight}{%
\renewcommand\markdownRendererDlEndTightPrototype{#1}}%
\define@key{markdownRendererPrototypes}{emphasis}{%
\renewcommand\markdownRendererEmphasisPrototype[1]{#1}}%
\define@key{markdownRendererPrototypes}{strongEmphasis}{%
\renewcommand\markdownRendererStrongEmphasisPrototype[1]{#1}}%
\define@key{markdownRendererPrototypes}{blockQuoteBegin}{%
\renewcommand\markdownRendererBlockQuoteBeginPrototype{#1}}%
\define@key{markdownRendererPrototypes}{blockQuoteEnd}{%
\renewcommand\markdownRendererBlockQuoteEndPrototype{#1}}%
\define@key{markdownRendererPrototypes}{inputVerbatim}{%
\renewcommand\markdownRendererInputVerbatimPrototype[1]{#1}}%
\define@key{markdownRendererPrototypes}{inputFencedCode}{%
\renewcommand\markdownRendererInputFencedCodePrototype[2]{#1}}%
\define@key{markdownRendererPrototypes}{headingOne}{%
\renewcommand\markdownRendererHeadingOnePrototype[1]{#1}}%
\define@key{markdownRendererPrototypes}{headingTwo}{%
\renewcommand\markdownRendererHeadingTwoPrototype[1]{#1}}%
\define@key{markdownRendererPrototypes}{headingThree}{%
\renewcommand\markdownRendererHeadingThreePrototype[1]{#1}}%
\define@key{markdownRendererPrototypes}{headingFour}{%
\renewcommand\markdownRendererHeadingFourPrototype[1]{#1}}%
\define@key{markdownRendererPrototypes}{headingFive}{%
\renewcommand\markdownRendererHeadingFivePrototype[1]{#1}}%
\define@key{markdownRendererPrototypes}{headingSix}{%
\renewcommand\markdownRendererHeadingSixPrototype[1]{#1}}%
\define@key{markdownRendererPrototypes}{horizontalRule}{%
\renewcommand\markdownRendererHorizontalRulePrototype{#1}}%
\define@key{markdownRendererPrototypes}{footnote}{%
\renewcommand\markdownRendererFootnotePrototype[1]{#1}}%
\define@key{markdownRendererPrototypes}{cite}{%
\renewcommand\markdownRendererCitePrototype[1]{#1}}%
\define@key{markdownRendererPrototypes}{textCite}{%
\renewcommand\markdownRendererTextCitePrototype[1]{#1}}%
% \end{macrocode}
%
% The following example \LaTeX{} code showcases a possible configuration of the
% \m{markdownRendererImagePrototype} and \m{markdownRendererCodeSpanPrototype}
% markdown token renderer prototypes.
% \begin{Verbatim}
% \markdownSetup{
% rendererPrototypes = {
% image = {\includegraphics{#2}},
% codeSpan = {\texttt{#1}}, % Render inline code via `\texttt`.
% }
% }
% \end{Verbatim}
%
% \iffalse
%
%<*context>
% \fi\subsection{\Hologo{ConTeXt} Interface}\label{sec:contextinterface}
% The \Hologo{ConTeXt} interface provides a start-stop macro pair for the
% typesetting of markdown input from within \Hologo{ConTeXt}. The rest of the
% interface is inherited from the plain \TeX{} interface (see Section
% \ref{sec:texinterface}).
% \begin{macrocode}
\writestatus{loading}{ConTeXt User Module / markdown}%
\unprotect
% \end{macrocode}
%
% The \Hologo{ConTeXt} interface is implemented by the
% \t`t-markdown.tex` \Hologo{ConTeXt} module file that can be loaded as follows:
% \begin{Verbatim}
% \usemodule[t][markdown]
% \end{Verbatim}
% It is expected that the special plain \TeX{} characters have the expected
% category codes, when \m{input}ting the file.
%
% \subsubsection{Typesetting Markdown}
% The interface exposes the \mdef{startmarkdown} and \mdef{stopmarkdown} macro
% pair for the typesetting of a markdown document fragment.
% \begin{macrocode}
\let\startmarkdown\relax
\let\stopmarkdown\relax
% \end{macrocode}
% You may prepend your own code to the \m{startmarkdown} macro and redefine the
% \m{stopmarkdown} macro to produce special effects before and after the
% markdown block.
%
% Note that the \m{startmarkdown} and \m{stopmarkdown} macros
% are subject to the same limitations as the \m{markdownBegin} and
% \m{markdownEnd} macros exposed by the plain \TeX{} interface.
%
% The following example \Hologo{ConTeXt} code showcases the usage of the
% \m{startmarkdown} and \m{stopmarkdown} macros:
% \begin{Verbatim}
% \usemodule[t][markdown]
% \starttext
% \startmarkdown
% _Hello_ **world** ...
% \stopmarkdown
% \stoptext
% \end{Verbatim}
%
% \section{Technical Documentation}\label{sec:implementation}
% This part of the manual describes the implementation of the interfaces
% exposed by the package (see Section \ref{sec:interfaces}) and is aimed at the
% developers of the package, as well as the curious users.
%
% \iffalse
%
%<*lua>
% \fi\subsection{Lua Implementation}\label{sec:luaimplementation}
% The Lua implementation implements \luamdef{writer} and \luamdef{reader}
% objects that provide the conversion from markdown to plain \TeX{}.
%
% The Lunamark Lua module implements writers for the conversion to various
% other formats, such as DocBook, Groff, or \acro{HTML}. These were stripped
% from the module and the remaining markdown reader and plain \TeX{} writer
% were hidden behind the converter functions exposed by the Lua interface (see
% Section \ref{sec:luainterface}).
%
% \begin{macrocode}
local upper, gsub, format, length =
string.upper, string.gsub, string.format, string.len
local concat = table.concat
local P, R, S, V, C, Cg, Cb, Cmt, Cc, Ct, B, Cs, any =
lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.C, lpeg.Cg, lpeg.Cb,
lpeg.Cmt, lpeg.Cc, lpeg.Ct, lpeg.B, lpeg.Cs, lpeg.P(1)
% \end{macrocode}
%
% \subsubsection{Utility Functions}
% This section documents the utility functions used by the Lua code. These
% functions are encapsulated in the \t`util` object. The functions were
% originally located in the \t`lunamark/util.lua` file in the Lunamark Lua
% module.
% \begin{macrocode}
local util = {}
% \end{macrocode}
%
% The \luamdef{util.err} method prints an error message \t`msg` and exits.
% If \t`exit_code` is provided, it specifies the exit code. Otherwise, the
% exit code will be 1.
% \begin{macrocode}
function util.err(msg, exit_code)
io.stderr:write("markdown.lua: " .. msg .. "\n")
os.exit(exit_code or 1)
end
% \end{macrocode}
%
% The \luamdef{util.cache} method computes the digest of \t`string` and
% \t`salt`, adds the \t`suffix` and looks into the directory \t`dir`, whether a
% file with such a name exists. If it does not, it gets created with
% \t`transform(string)` as its content. The filename is then returned.
% \begin{macrocode}
function util.cache(dir, string, salt, transform, suffix)
local digest = md5.sumhexa(string .. (salt or ""))
local name = util.pathname(dir, digest .. suffix)
local file = io.open(name, "r")
if file == nil then -- If no cache entry exists, then create a new one.
local file = assert(io.open(name, "w"))
local result = string
if transform ~= nil then
result = transform(result)
end
assert(file:write(result))
assert(file:close())
end
return name
end
% \end{macrocode}
%
% The \luamdef{util.table_copy} method creates a shallow copy of a table \t`t`
% and its metatable.
% \begin{macrocode}
function util.table_copy(t)
local u = { }
for k, v in pairs(t) do u[k] = v end
return setmetatable(u, getmetatable(t))
end
% \end{macrocode}
%
% The \luamdef{util.expand_tabs_in_line} expands tabs in string \t`s`. If
% \t`tabstop` is specified, it is used as the tab stop width. Otherwise,
% the tab stop width of 4 characters is used. The method is a copy of the tab
% expansion algorithm from \cite[Chapter~21]{ierusalimschy13}.
% \begin{macrocode}
function util.expand_tabs_in_line(s, tabstop)
local tab = tabstop or 4
local corr = 0
return (s:gsub("()\t", function(p)
local sp = tab - (p - 1 + corr) % tab
corr = corr - 1 + sp
return string.rep(" ", sp)
end))
end
% \end{macrocode}
%
% The \luamdef{util.walk} method walks a rope \t`t`, applying a function \t`f`
% to each leaf element in order. A rope is an array whose elements may be
% ropes, strings, numbers, or functions. If a leaf element is a function, call
% it and get the return value before proceeding.
% \begin{macrocode}
function util.walk(t, f)
local typ = type(t)
if typ == "string" then
f(t)
elseif typ == "table" then
local i = 1
local n
n = t[i]
while n do
util.walk(n, f)
i = i + 1
n = t[i]
end
elseif typ == "function" then
local ok, val = pcall(t)
if ok then
util.walk(val,f)
end
else
f(tostring(t))
end
end
% \end{macrocode}
%
% The \luamdef{util.flatten} method flattens an array \t`ary` that does not
% contain cycles and returns the result.
% \begin{macrocode}
function util.flatten(ary)
local new = {}
for _,v in ipairs(ary) do
if type(v) == "table" then
for _,w in ipairs(util.flatten(v)) do
new[#new + 1] = w
end
else
new[#new + 1] = v
end
end
return new
end
% \end{macrocode}
%
% The \luamdef{util.rope_to_string} method converts a rope \t`rope` to a
% string and returns it. For the definition of a rope, see the definition of
% the \luam{util.walk} method.
% \begin{macrocode}
function util.rope_to_string(rope)
local buffer = {}
util.walk(rope, function(x) buffer[#buffer + 1] = x end)
return table.concat(buffer)
end
% \end{macrocode}
%
% The \luamdef{util.rope_last} method retrieves the last item in a rope. For
% the definition of a rope, see the definition of the \luam{util.walk} method.
% \begin{macrocode}
function util.rope_last(rope)
if #rope == 0 then
return nil
else
local l = rope[#rope]
if type(l) == "table" then
return util.rope_last(l)
else
return l
end
end
end
% \end{macrocode}
%
% Given an array \t`ary` and a string \t`x`, the \luamdef{util.intersperse}
% method returns an array \t`new`, such that \t`ary[i] == new[2*(i-1)+1]` and
% \t`new[2*i] == x` for all $1\leq\t`i`\leq\t`\#ary`$.
% \begin{macrocode}
function util.intersperse(ary, x)
local new = {}
local l = #ary
for i,v in ipairs(ary) do
local n = #new
new[n + 1] = v
if i ~= l then
new[n + 2] = x
end
end
return new
end
% \end{macrocode}
%
% Given an array \t`ary` and a function \t`f`, the \luamdef{util.map} method
% returns an array \t`new`, such that \t`new[i] == f(ary[i])` for all
% $1\leq\t`i`\leq\t`\#ary`$.
% \begin{macrocode}
function util.map(ary, f)
local new = {}
for i,v in ipairs(ary) do
new[i] = f(v)
end
return new
end
% \end{macrocode}
%
% Given a table \t`char_escapes` mapping escapable characters to escaped
% strings and optionally a table \t`string_escapes` mapping escapable strings
% to escaped strings, the \luamdef{util.escaper} method returns an escaper
% function that escapes all occurances of escapable strings and characters (in
% this order).
%
% The method uses \pkg{LPeg}, which is faster than the Lua \t`string.gsub`
% built-in method.
% \begin{macrocode}
function util.escaper(char_escapes, string_escapes)
% \end{macrocode}
% Build a string of escapable characters.
% \begin{macrocode}
local char_escapes_list = ""
for i,_ in pairs(char_escapes) do
char_escapes_list = char_escapes_list .. i
end
% \end{macrocode}
% Create an \pkg{LPeg} capture \t`escapable` that produces the escaped string
% corresponding to the matched escapable character.
% \begin{macrocode}
local escapable = S(char_escapes_list) / char_escapes
% \end{macrocode}
% If \t`string_escapes` is provided, turn \t`escapable` into the
% {\catcode`\_=8\[
% \sum_{(\t`k`,\t`v`)\in\t`string\_escapes`}\t`P(k) / v` + \t`escapable`
% \]}^^A
% capture that replaces any occurance of the string \t`k` with the string
% \t`v` for each $(\t`k`, \t`v`)\in\t`string\_escapes`$. Note that the pattern
% summation is not commutative and its operands are inspected in the
% summation order during the matching. As a corrolary, the strings always
% take precedence over the characters.
% \begin{macrocode}
if string_escapes then
for k,v in pairs(string_escapes) do
escapable = P(k) / v + escapable
end
end
% \end{macrocode}
% Create an \pkg{LPeg} capture \t`escape_string` that captures anything
% \t`escapable` does and matches any other unmatched characters.
% \begin{macrocode}
local escape_string = Cs((escapable + any)^0)
% \end{macrocode}
% Return a function that matches the input string \t`s` against the
% \t`escape_string` capture.
% \begin{macrocode}
return function(s)
return lpeg.match(escape_string, s)
end
end
% \end{macrocode}
%
% The \luamdef{util.pathname} method produces a pathname out of a directory
% name \t`dir` and a filename \t`file` and returns it.
% \begin{macrocode}
function util.pathname(dir, file)
if #dir == 0 then
return file
else
return dir .. "/" .. file
end
end
% \end{macrocode}
% \subsubsection{Plain \TeX{} Writer}\label{sec:texwriter}
% This section documents the \luam{writer} object, which implements the
% routines for producing the \TeX{} output. The object is an amalgamate of the
% generic, \TeX{}, \LaTeX{} writer objects that were located in the
% \t`lunamark/writer/generic.lua`, \t`lunamark/writer/tex.lua`, and
% \t`lunamark/writer/latex.lua` files in the Lunamark Lua module.
%
% Although not specified in the Lua interface (see Section
% \ref{sec:luainterface}), the \luam{writer} object is exported, so that the
% curious user could easily tinker with the methods of the objects produced by
% the \luam{writer.new} method described below. The user should be aware,
% however, that the implementation may change in a future revision.
% \begin{macrocode}
M.writer = {}
% \end{macrocode}
%
% The \luamdef{writer.new} method creates and returns a new \TeX{} writer
% object associated with the Lua interface options (see Section
% \ref{sec:luaoptions}) \t`options`. When \t`options` are unspecified, it is
% assumed that an empty table was passed to the method.
%
% The objects produced by the \luam{writer.new} method expose instance methods
% and variables of their own. As a convention, I will refer to these
% \meta{member}s as \t`writer->`\meta{member}.
% \begin{macrocode}
function M.writer.new(options)
local self = {}
options = options or {}
% \end{macrocode}
%
% Make the \t`options` table inherit from the \luam{defaultOptions} table.
% \begin{macrocode}
setmetatable(options, { __index = function (_, key)
return defaultOptions[key] end })
% \end{macrocode}
%
% Define \luamdef{writer->suffix} as the suffix of the produced cache files.
% \begin{macrocode}
self.suffix = ".tex"
% \end{macrocode}
%
% Define \luamdef{writer->space} as the output format of a space character.
% \begin{macrocode}
self.space = " "
% \end{macrocode}
%
% Define \luamdef{writer->nbsp} as the output format of a non-breaking space
% character.
% \begin{macrocode}
self.nbsp = "\\markdownRendererNbsp{}"
% \end{macrocode}
%
% Define \luamdef{writer->plain} as a function that will transform an input
% plain text block \t`s` to the output format.
% \begin{macrocode}
function self.plain(s)
return s
end
% \end{macrocode}
%
% Define \luamdef{writer->paragraph} as a function that will transform an
% input paragraph \t`s` to the output format.
% \begin{macrocode}
function self.paragraph(s)
return s
end
% \end{macrocode}
%
% Define \luamdef{writer->pack} as a function that will take the filename
% \t`name` of the output file prepared by the reader and transform it to the
% output format.
% \begin{macrocode}
function self.pack(name)
return [[\input"]] .. name .. [["\relax]]
end
% \end{macrocode}
%
% Define \luamdef{writer->interblocksep} as the output format of a block
% element separator.
% \begin{macrocode}
self.interblocksep = "\\markdownRendererInterblockSeparator\n{}"
% \end{macrocode}
%
% Define \luamdef{writer->eof} as the end of file marker in the output format.
% \begin{macrocode}
self.eof = [[\relax]]
% \end{macrocode}
%
% Define \luamdef{writer->linebreak} as the output format of a forced line break.
% \begin{macrocode}
self.linebreak = "\\markdownRendererLineBreak\n{}"
% \end{macrocode}
%
% Define \luamdef{writer->ellipsis} as the output format of an ellipsis.
% \begin{macrocode}
self.ellipsis = "\\markdownRendererEllipsis{}"
% \end{macrocode}
%
% Define \luamdef{writer->hrule} as the output format of a horizontal rule.
% \begin{macrocode}
self.hrule = "\\markdownRendererHorizontalRule{}"
% \end{macrocode}
%
% Define a table \luamdef{escaped_chars} containing the mapping from special
% plain \TeX{} characters (including the active pipe character (\t`|`) of
% \Hologo{ConTeXt}) to their escaped variants. Define tables
% \luamdef{escaped_minimal_chars} and \luamdef{escaped_minimal_strings}
% containing the mapping from special plain characters and character strings
% that need to be escaped even in content that will not be typeset.
% \begin{macrocode}
local escaped_chars = {
["{"] = "\\markdownRendererLeftBrace{}",
["}"] = "\\markdownRendererRightBrace{}",
["$"] = "\\markdownRendererDollarSign{}",
["%"] = "\\markdownRendererPercentSign{}",
["&"] = "\\markdownRendererAmpersand{}",
["_"] = "\\markdownRendererUnderscore{}",
["#"] = "\\markdownRendererHash{}",
["^"] = "\\markdownRendererCircumflex{}",
["\\"] = "\\markdownRendererBackslash{}",
["~"] = "\\markdownRendererTilde{}",
["|"] = "\\markdownRendererPipe{}", }
local escaped_minimal_chars = {
["{"] = "\\markdownRendererLeftBrace{}",
["}"] = "\\markdownRendererRightBrace{}",
["%"] = "\\markdownRendererPercentSign{}",
["\\"] = "\\markdownRendererBackslash{}", }
local escaped_minimal_strings = {
["^^"] = "\\markdownRendererCircumflex\\markdownRendererCircumflex ", }
% \end{macrocode}
% Use the \luam{escaped_chars} table to create an escaper function
% \luamdef{escape} and the \luam{escaped_minimal_chars} and
% \luam{escaped_minimal_strings} tables to create an escaper function
% \luamdef{escape_minimal}.
% \begin{macrocode}
local escape = util.escaper(escaped_chars)
local escape_minimal = util.escaper(escaped_minimal_chars,
escaped_minimal_strings)
% \end{macrocode}
%
% Define \luamdef{writer->string} as a function that will transform an input
% plain text span \t`s` to the output format and \luamdef{writer->uri} as a
% function that will transform an input \acro{uri} \t`u` to the output format.
% If the \Opt{hybrid} option is \t`true`, use identity functions. Otherwise,
% use the \luam{escape} and \luam{escape_minimal} functions.
% \begin{macrocode}
if options.hybrid then
self.string = function(s) return s end
self.uri = function(u) return u end
else
self.string = escape
self.uri = escape_minimal
end
% \end{macrocode}
%
% Define \luamdef{writer->code} as a function that will transform an input
% inlined code span \t`s` to the output format.
% \begin{macrocode}
function self.code(s)
return {"\\markdownRendererCodeSpan{",escape(s),"}"}
end
% \end{macrocode}
%
% Define \luamdef{writer->link} as a function that will transform an input
% hyperlink to the output format, where \t`lab` corresponds to the label,
% \t`src` to \acro{uri}, and \t`tit` to the title of the link.
% \begin{macrocode}
function self.link(lab,src,tit)
return {"\\markdownRendererLink{",lab,"}",
"{",self.string(src),"}",
"{",self.uri(src),"}",
"{",self.string(tit or ""),"}"}
end
% \end{macrocode}
%
% Define \luamdef{writer->image} as a function that will transform an input
% image to the output format, where \t`lab` corresponds to the label, \t`src`
% to the \acro{url}, and \t`tit` to the title of the image.
% \begin{macrocode}
function self.image(lab,src,tit)
return {"\\markdownRendererImage{",lab,"}",
"{",self.string(src),"}",
"{",self.uri(src),"}",
"{",self.string(tit or ""),"}"}
end
% \end{macrocode}
%
% Define \luamdef{writer->bulletlist} as a function that will transform an input
% bulleted list to the output format, where \t`items` is an array of the list
% items and \t`tight` specifies, whether the list is tight or not.
% \begin{macrocode}
local function ulitem(s)
return {"\\markdownRendererUlItem ",s,
"\\markdownRendererUlItemEnd "}
end
function self.bulletlist(items,tight)
local buffer = {}
for _,item in ipairs(items) do
buffer[#buffer + 1] = ulitem(item)
end
local contents = util.intersperse(buffer,"\n")
if tight and options.tightLists then
return {"\\markdownRendererUlBeginTight\n",contents,
"\n\\markdownRendererUlEndTight "}
else
return {"\\markdownRendererUlBegin\n",contents,
"\n\\markdownRendererUlEnd "}
end
end
% \end{macrocode}
%
% Define \luamdef{writer->ollist} as a function that will transform an input
% ordered list to the output format, where \t`items` is an array of the list
% items and \t`tight` specifies, whether the list is tight or not. If the
% optional parameter \t`startnum` is present, it should be used as the number
% of the first list item.
% \begin{macrocode}
local function olitem(s,num)
if num ~= nil then
return {"\\markdownRendererOlItemWithNumber{",num,"}",s,
"\\markdownRendererOlItemEnd "}
else
return {"\\markdownRendererOlItem ",s,
"\\markdownRendererOlItemEnd "}
end
end
function self.orderedlist(items,tight,startnum)
local buffer = {}
local num = startnum
for _,item in ipairs(items) do
buffer[#buffer + 1] = olitem(item,num)
if num ~= nil then
num = num + 1
end
end
local contents = util.intersperse(buffer,"\n")
if tight and options.tightLists then
return {"\\markdownRendererOlBeginTight\n",contents,
"\n\\markdownRendererOlEndTight "}
else
return {"\\markdownRendererOlBegin\n",contents,
"\n\\markdownRendererOlEnd "}
end
end
% \end{macrocode}
%
% Define \luamdef{writer->definitionlist} as a function that will transform an
% input definition list to the output format, where \t`items` is an array of
% tables, each of the form \t`\{ term = t, definitions = defs \}`, where \t`t`
% is a term and \t`defs` is an array of definitions. \t`tight` specifies,
% whether the list is tight or not.
% \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)
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}
%
% Define \luamdef{writer->emphasis} as a function that will transform an
% emphasized span \t`s` of input text to the output format.
% \begin{macrocode}
function self.emphasis(s)
return {"\\markdownRendererEmphasis{",s,"}"}
end
% \end{macrocode}
%
% Define \luamdef{writer->strong} as a function that will transform a strongly
% emphasized span \t`s` of input text to the output format.
% \begin{macrocode}
function self.strong(s)
return {"\\markdownRendererStrongEmphasis{",s,"}"}
end
% \end{macrocode}
%
% Define \luamdef{writer->blockquote} as a function that will transform an
% input block quote \t`s` to the output format.
% \begin{macrocode}
function self.blockquote(s)
return {"\\markdownRendererBlockQuoteBegin\n",s,
"\n\\markdownRendererBlockQuoteEnd "}
end
% \end{macrocode}
%
% Define \luamdef{writer->verbatim} as a function that will transform an
% input code block \t`s` to the output format.
% \begin{macrocode}
function self.verbatim(s)
local name = util.cache(options.cacheDir, s, nil, nil, ".verbatim")
return {"\\markdownRendererInputVerbatim{",name,"}"}
end
% \end{macrocode}
%
% Define \luamdef{writer->codeFence} as a function that will transform an
% input fenced code block \t`s` with the infostring \t`i` to the output
% format.
% \begin{macrocode}
function self.fencedCode(i, s)
local name = util.cache(options.cacheDir, s, nil, nil, ".verbatim")
return {"\\markdownRendererInputFencedCode{",name,"}{",i,"}"}
end
% \end{macrocode}
%
% Define \luamdef{writer->heading} as a function that will transform an
% input heading \t`s` at level \t`level` to the output format.
% \begin{macrocode}
function self.heading(s,level)
local cmd
if level == 1 then
cmd = "\\markdownRendererHeadingOne"
elseif level == 2 then
cmd = "\\markdownRendererHeadingTwo"
elseif level == 3 then
cmd = "\\markdownRendererHeadingThree"
elseif level == 4 then
cmd = "\\markdownRendererHeadingFour"
elseif level == 5 then
cmd = "\\markdownRendererHeadingFive"
elseif level == 6 then
cmd = "\\markdownRendererHeadingSix"
else
cmd = ""
end
return {cmd,"{",s,"}"}
end
% \end{macrocode}
%
% Define \luamdef{writer->note} as a function that will transform an
% input footnote \t`s` to the output format.
% \begin{macrocode}
function self.note(s)
return {"\\markdownRendererFootnote{",s,"}"}
end
% \end{macrocode}
%
% Define \luamdef{writer->citations} as a function that will transform an
% input array of citations \t`cites` to the output format. If \t`text_cites`
% is \t`true`, the citations should be rendered in-text, when applicable.
% The \t`cites` array contains tables with the following keys and values:
% \begin{itemize}
% \item\t`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\t`prenote` -- The value of the key is either \t`nil` or a rope
% that should be inserted before the citation.
% \item\t`postnote` -- The value of the key is either \t`nil` or a rope
% that should be inserted after the citation.
% \item\t`name` -- The value of this key is the citation name.
% \end{itemize}
% \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
return self
end
% \end{macrocode}
% \subsubsection{Generic \acro{peg} Patterns}
% These \acro{peg} patterns have been temporarily moved outside the
% \luam{reader.new} method, which is currently hitting the limit of 200 local
% variables. To resolve this issue, all the \acro{peg} patterns local to
% \luam{reader.new} will be moved to a static hash table at some point in the
% future.
% \begin{macrocode}
local percent = P("%")
local at = P("@")
local comma = P(",")
local asterisk = P("*")
local dash = P("-")
local plus = P("+")
local underscore = P("_")
local period = P(".")
local hash = P("#")
local ampersand = P("&")
local backtick = P("`")
local less = P("<")
local more = P(">")
local space = P(" ")
local squote = P("'")
local dquote = P('"')
local lparent = P("(")
local rparent = P(")")
local lbracket = P("[")
local rbracket = P("]")
local circumflex = P("^")
local slash = P("/")
local equal = P("=")
local colon = P(":")
local semicolon = P(";")
local exclamation = P("!")
local tilde = P("~")
local digit = R("09")
local hexdigit = R("09","af","AF")
local letter = R("AZ","az")
local alphanumeric = R("AZ","az","09")
local keyword = letter * alphanumeric^0
local internal_punctuation = S(":;,.#$%&-+?<>~/")
local doubleasterisks = P("**")
local doubleunderscores = P("__")
local fourspaces = P(" ")
local any = P(1)
local fail = any - 1
local always = P("")
local escapable = S("\\`*_{}[]()+_.!<>#-~:^@;")
local anyescaped = P("\\") / "" * escapable
+ any
local tab = P("\t")
local spacechar = S("\t ")
local spacing = S(" \n\r\t")
local newline = P("\n")
local nonspacechar = any - spacing
local tightblocksep = P("\001")
local specialchar = S("*_`&[]`\meta{member}.
% \begin{macrocode}
M.reader = {}
function M.reader.new(writer, options)
local self = {}
options = options or {}
% \end{macrocode}
%
% Make the \t`options` table inherit from the \luam{defaultOptions} table.
% \begin{macrocode}
setmetatable(options, { __index = function (_, key)
return defaultOptions[key] end })
% \end{macrocode}
%
% \paragraph{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.
% \begin{macrocode}
local function normalize_tag(tag)
return unicode.utf8.lower(
gsub(util.rope_to_string(tag), "[ \n\r\t]+", " "))
end
% \end{macrocode}
%
% Define \luamdef{expandtabs} either as an identity function, when the
% \Opt{preserveTabs} Lua inrerface option is \t`true`, or to a function that
% expands tabs into spaces otherwise.
% \begin{macrocode}
local expandtabs
if options.preserveTabs then
expandtabs = function(s) return s end
else
expandtabs = function(s)
if s:find("\t") then
return s:gsub("[^\n]*", util.expand_tabs_in_line)
else
return s
end
end
end
% \end{macrocode}
% \paragraph{Top Level Parsing Functions}
% \begin{macrocode}
local syntax
local blocks_toplevel
local blocks
local inlines
local inlines_no_link
local inlines_no_inline_note
local inlines_nbsp
local function create_parser(name, grammar)
return function(str)
local res = lpeg.match(grammar(), str)
if res == nil then
error(format("%s failed on:\n%s", name, str:sub(1,20)))
else
return res
end
end
end
local parse_blocks = create_parser("parse_blocks",
function() return blocks end)
local parse_blocks_toplevel = create_parser("parse_blocks_toplevel",
function() return blocks_toplevel end)
local parse_inlines = create_parser("parse_inlines",
function() return inlines end)
local parse_inlines_no_link = create_parser("parse_inlines_no_link",
function() return inlines_no_link end)
local parse_inlines_no_inline_note = create_parser(
"parse_inlines_no_inline_note",
function() return inlines_no_inline_note end)
local parse_inlines_nbsp = create_parser("parse_inlines_nbsp",
function() return inlines_nbsp end)
% \end{macrocode}
% \paragraph{List \acro{peg} Patterns}
% \begin{macrocode}
local bulletchar = C(plus + asterisk + dash)
local bullet = ( Cg(bulletchar, "bulletchar") * #spacing * (tab + space^-3)
+ space * Cg(bulletchar, "bulletchar") * #spacing * (tab + space^-2)
+ space * space * Cg(bulletchar, "bulletchar") * #spacing * (tab + space^-1)
+ space * space * space * Cg(bulletchar, "bulletchar") * #spacing
)
if options.hashEnumerators then
dig = digit + hash
else
dig = digit
end
local enumerator = C(dig^3 * period) * #spacing
+ C(dig^2 * period) * #spacing * (tab + space^1)
+ C(dig * period) * #spacing * (tab + space^-2)
+ space * C(dig^2 * period) * #spacing
+ space * C(dig * period) * #spacing * (tab + space^-1)
+ space * space * C(dig^1 * period) * #spacing
% \end{macrocode}
% \paragraph{Code Span \acro{peg} Patterns}
% \begin{macrocode}
local openticks = Cg(backtick^1, "ticks")
local function captures_equal_length(s,i,a,b)
return #a == #b and i
end
local closeticks = space^-1 *
Cmt(C(backtick^1) * Cb("ticks"), captures_equal_length)
local intickschar = (any - S(" \n\r`"))
+ (newline * -blankline)
+ (space - closeticks)
+ (backtick^1 - closeticks)
local inticks = openticks * space^-1 * C(intickschar^0) * closeticks
% \paragraph{Fenced Code \acro{peg} Patterns}
% \begin{macrocode}
local function captures_geq_length(s,i,a,b)
return #a >= #b and i
end
local infostring = (linechar - (backtick + space^1 * (newline + eof)))^0
local fenceindent
local function fencehead(char)
return C(nonindentspace) / function(s) fenceindent = #s end
* Cg(char^3, "fencelength")
* optionalspace * C(infostring) * optionalspace
* (newline + eof)
end
local function fencetail(char)
return nonindentspace
* Cmt(C(char^3) * Cb("fencelength"),
captures_geq_length)
* optionalspace * (newline + eof)
+ eof
end
local function fencedline(char)
return C(line - fencetail(char))
/ function(s)
return s:gsub("^" .. string.rep(" ?",
fenceindent), "")
end
end
% \end{macrocode}
% \paragraph{Tag \acro{peg} Patterns}
% \begin{macrocode}
local leader = space^-3
-- in balanced brackets, parentheses, quotes:
local bracketed = P{ lbracket
* ((anyescaped - (lbracket + rbracket
+ blankline^2)) + V(1))^0
* rbracket }
local inparens = P{ lparent
* ((anyescaped - (lparent + rparent
+ blankline^2)) + V(1))^0
* rparent }
local squoted = P{ squote * alphanumeric
* ((anyescaped - (squote + blankline^2))
+ V(1))^0
* squote }
local dquoted = P{ dquote * alphanumeric
* ((anyescaped - (dquote + blankline^2))
+ V(1))^0
* dquote }
-- bracketed 'tag' for markdown links, allowing nested brackets:
local tag = lbracket
* Cs((alphanumeric^1
+ bracketed
+ inticks
+ (anyescaped - (rbracket + blankline^2)))^0)
* rbracket
-- url for markdown links, allowing balanced parentheses:
local url = less * Cs((anyescaped-more)^0) * more
+ Cs((inparens + (anyescaped-spacing-rparent))^1)
-- quoted text possibly with nested quotes:
local title_s = squote * Cs(((anyescaped-squote) + squoted)^0) *
squote
local title_d = dquote * Cs(((anyescaped-dquote) + dquoted)^0) *
dquote
local title_p = lparent
* Cs((inparens + (anyescaped-rparent))^0)
* rparent
local title = title_d + title_s + title_p
local optionaltitle = spnl * title * spacechar^0
+ Cc("")
% \end{macrocode}
% \paragraph{Citation \acro{peg} Patterns}
% \begin{macrocode}
local citation_name = Cs(dash^-1) * at
* Cs(alphanumeric
* (alphanumeric + internal_punctuation
- comma - semicolon)^0)
local citation_body_prenote
= Cs((alphanumeric^1
+ bracketed
+ inticks
+ (anyescaped
- (rbracket + blankline^2))
- (spnl * dash^-1 * at))^0)
local citation_body_postnote
= Cs((alphanumeric^1
+ bracketed
+ inticks
+ (anyescaped
- (rbracket + semicolon + blankline^2))
- (spnl * rbracket))^0)
local citation_body_chunk
= citation_body_prenote
* spnl * citation_name
* (comma * spnl)^-1
* citation_body_postnote
local citation_body = citation_body_chunk
* (semicolon * spnl * citation_body_chunk)^0
local citation_headless_body_postnote
= Cs((alphanumeric^1
+ bracketed
+ inticks
+ (anyescaped
- (rbracket + at + semicolon + blankline^2))
- (spnl * rbracket))^0)
local citation_headless_body
= citation_headless_body_postnote
* (sp * semicolon * spnl * citation_body_chunk)^0
% \end{macrocode}
% \paragraph{Footnote \acro{peg} Patterns}
% \begin{macrocode}
local rawnotes = {}
local function strip_first_char(s)
return s:sub(2)
end
-- like indirect_link
local function lookup_note(ref)
return function()
local found = rawnotes[normalize_tag(ref)]
if found then
return writer.note(parse_blocks_toplevel(found))
else
return {"[", parse_inlines("^" .. ref), "]"}
end
end
end
local function register_note(ref,rawnote)
rawnotes[normalize_tag(ref)] = rawnote
return ""
end
local RawNoteRef = #(lbracket * circumflex) * tag / strip_first_char
local NoteRef = RawNoteRef / lookup_note
local NoteBlock = leader * RawNoteRef * colon * spnl
* indented_blocks(chunk) / register_note
local InlineNote = circumflex -- no notes inside notes
* (tag / parse_inlines_no_inline_note)
/ writer.note
% \end{macrocode}
% \paragraph{Link and Image \acro{peg} Patterns}
% \begin{macrocode}
-- List of references defined in the document
local references
-- add a reference to the list
local function register_link(tag,url,title)
references[normalize_tag(tag)] = { url = url, title = title }
return ""
end
-- parse a reference definition: [foo]: /bar "title"
local define_reference_parser =
leader * tag * colon * spacechar^0 * url * optionaltitle * blankline^1
-- lookup link reference and return either
-- the link or nil and fallback text.
local function lookup_reference(label,sps,tag)
local tagpart
if not tag then
tag = label
tagpart = ""
elseif tag == "" then
tag = label
tagpart = "[]"
else
tagpart = {"[", parse_inlines(tag), "]"}
end
if sps then
tagpart = {sps, tagpart}
end
local r = references[normalize_tag(tag)]
if r then
return r
else
return nil, {"[", parse_inlines(label), "]", tagpart}
end
end
-- lookup link reference and return a link, if the reference is found,
-- or a bracketed label otherwise.
local function indirect_link(label,sps,tag)
return function()
local r,fallback = lookup_reference(label,sps,tag)
if r then
return writer.link(parse_inlines_no_link(label), r.url, r.title)
else
return fallback
end
end
end
-- lookup image reference and return an image, if the reference is found,
-- or a bracketed label otherwise.
local function indirect_image(label,sps,tag)
return function()
local r,fallback = lookup_reference(label,sps,tag)
if r then
return writer.image(writer.string(label), r.url, r.title)
else
return {"!", fallback}
end
end
end
% \end{macrocode}
% \paragraph{Spacing \acro{peg} Patterns}
% \begin{macrocode}
local bqstart = more
local headerstart = hash
+ (line * (equal^1 + dash^1) * optionalspace * newline)
local fencestart = fencehead(backtick) + fencehead(tilde)
if options.blankBeforeBlockquote then
bqstart = fail
end
if options.blankBeforeHeading then
headerstart = fail
end
if not options.fencedCode or options.blankBeforeCodeFence then
fencestart = fail
end
% \end{macrocode}
% \paragraph{String \acro{peg} Rules}
% \begin{macrocode}
local Inline = V("Inline")
local Str = normalchar^1 / writer.string
local Symbol = (specialchar - tightblocksep) / writer.string
% \end{macrocode}
% \paragraph{Ellipsis \acro{peg} Rules}
% \begin{macrocode}
local Ellipsis = P("...") / writer.ellipsis
local Smart = Ellipsis
% \end{macrocode}
% \paragraph{Inline Code Block \acro{peg} Rules}
% \begin{macrocode}
local Code = inticks / writer.code
% \end{macrocode}
% \paragraph{Spacing \acro{peg} Rules}
% \begin{macrocode}
local Endline = newline * -( -- newline, but not before...
blankline -- paragraph break
+ tightblocksep -- nested list
+ eof -- end of document
+ bqstart
+ headerstart
+ fencestart
) * spacechar^0 / writer.space
local Space = spacechar^2 * Endline / writer.linebreak
+ spacechar^1 * Endline^-1 * eof / ""
+ spacechar^1 * Endline^-1 * optionalspace / writer.space
local NonbreakingEndline
= newline * -( -- newline, but not before...
blankline -- paragraph break
+ tightblocksep -- nested list
+ eof -- end of document
+ bqstart
+ headerstart
+ fencestart
) * spacechar^0 / writer.nbsp
local NonbreakingSpace
= spacechar^2 * Endline / writer.linebreak
+ spacechar^1 * Endline^-1 * eof / ""
+ spacechar^1 * Endline^-1 * optionalspace / writer.nbsp
-- parse many p between starter and ender
local function between(p, starter, ender)
local ender2 = B(nonspacechar) * ender
return (starter * #nonspacechar * Ct(p * (p - ender2)^0) * ender2)
end
% \end{macrocode}
% \paragraph{Emphasis \acro{peg} Rules}
% \begin{macrocode}
local Strong = ( between(Inline, doubleasterisks, doubleasterisks)
+ between(Inline, doubleunderscores, doubleunderscores)
) / writer.strong
local Emph = ( between(Inline, asterisk, asterisk)
+ between(Inline, underscore, underscore)
) / writer.emphasis
% \end{macrocode}
% \paragraph{Link \acro{peg} Rules}
% \begin{macrocode}
local urlchar = anyescaped - newline - more
local AutoLinkUrl = less
* C(alphanumeric^1 * P("://") * urlchar^1)
* more
/ function(url)
return writer.link(writer.string(url), url)
end
local AutoLinkEmail = less
* C((alphanumeric + S("-._+"))^1 * P("@") * urlchar^1)
* more
/ function(email)
return writer.link(writer.string(email),
"mailto:"..email)
end
local DirectLink = (tag / parse_inlines_no_link) -- no links inside links
* spnl
* lparent
* (url + Cc("")) -- link can be empty [foo]()
* optionaltitle
* rparent
/ writer.link
local IndirectLink = tag * (C(spnl) * tag)^-1 / indirect_link
-- parse a link or image (direct or indirect)
local Link = DirectLink + IndirectLink
% \end{macrocode}
% \paragraph{Image \acro{peg} Rules}
% \begin{macrocode}
local DirectImage = exclamation
* (tag / parse_inlines)
* spnl
* lparent
* (url + Cc("")) -- link can be empty [foo]()
* optionaltitle
* rparent
/ writer.image
local IndirectImage = exclamation * tag * (C(spnl) * tag)^-1 /
indirect_image
local Image = DirectImage + IndirectImage
% \end{macrocode}
% \paragraph{Miscellaneous Inline \acro{peg} Rules}
% \begin{macrocode}
-- avoid parsing long strings of * or _ as emph/strong
local UlOrStarLine = asterisk^4 + underscore^4 / writer.string
local EscapedChar = S("\\") * C(escapable) / writer.string
% \end{macrocode}
% \paragraph{Citations \acro{peg} Rules}
% \begin{macrocode}
local function citations(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.string(raw_cites[i+2]),
postnote = normalize(raw_cites[i+3]),
}
end
return writer.citations(text_cites, cites)
end
local TextCitations = Ct(Cc("")
* citation_name
* ((spnl
* lbracket
* citation_headless_body
* rbracket) + Cc(""))) /
function(raw_cites)
return citations(true, raw_cites)
end
local ParenthesizedCitations
= Ct(lbracket
* citation_body
* rbracket) /
function(raw_cites)
return citations(false, raw_cites)
end
local Citations = TextCitations + ParenthesizedCitations
% \end{macrocode}
% \paragraph{Code Block \acro{peg} Rules}
% \begin{macrocode}
local Block = V("Block")
local Verbatim = Cs( (blanklines
* ((indentedline - blankline))^1)^1
) / expandtabs / writer.verbatim
local TildeFencedCode
= fencehead(tilde)
* Cs(fencedline(tilde)^0)
* fencetail(tilde)
local BacktickFencedCode
= fencehead(backtick)
* Cs(fencedline(backtick)^0)
* fencetail(backtick)
local FencedCode = (TildeFencedCode + BacktickFencedCode)
/ function(infostring, code)
return writer.fencedCode(
writer.string(infostring),
expandtabs(code))
end
% \end{macrocode}
% \paragraph{Blockquote \acro{peg} Patterns}
% \begin{macrocode}
-- strip off leading > and indents, and run through blocks
local Blockquote = Cs((
((leader * more * space^-1)/"" * linechar^0 * newline)^1
* (-blankline * linechar^1 * newline)^0
* (blankline^0 / "")
)^1) / parse_blocks_toplevel / writer.blockquote
local function lineof(c)
return (leader * (P(c) * optionalspace)^3 * (newline * blankline^1
+ newline^-1 * eof))
end
% \end{macrocode}
% \paragraph{Horizontal Rule \acro{peg} Rules}
% \begin{macrocode}
local HorizontalRule = ( lineof(asterisk)
+ lineof(dash)
+ lineof(underscore)
) / writer.hrule
% \end{macrocode}
% \paragraph{List \acro{peg} Rules}
% \begin{macrocode}
local starter = bullet + enumerator
-- we use \001 as a separator between a tight list item and a
-- nested list under it.
local NestedList = Cs((optionallyindentedline - starter)^1)
/ function(a) return "\001"..a end
local ListBlockLine = optionallyindentedline
- blankline - (indent^-1 * starter)
local ListBlock = line * ListBlockLine^0
local ListContinuationBlock = blanklines * (indent / "") * ListBlock
local function TightListItem(starter)
return -HorizontalRule
* (Cs(starter / "" * ListBlock * NestedList^-1) /
parse_blocks)
* -(blanklines * indent)
end
local function LooseListItem(starter)
return -HorizontalRule
* Cs( starter / "" * ListBlock * Cc("\n")
* (NestedList + ListContinuationBlock^0)
* (blanklines / "\n\n")
) / parse_blocks
end
local BulletList = ( Ct(TightListItem(bullet)^1)
* Cc(true) * skipblanklines * -bullet
+ Ct(LooseListItem(bullet)^1)
* Cc(false) * skipblanklines ) /
writer.bulletlist
local function orderedlist(items,tight,startNumber)
if options.startNumber then
startNumber = tonumber(startNumber) or 1 -- fallback for '#'
else
startNumber = nil
end
return writer.orderedlist(items,tight,startNumber)
end
local OrderedList = Cg(enumerator, "listtype") *
( Ct(TightListItem(Cb("listtype")) *
TightListItem(enumerator)^0)
* Cc(true) * skipblanklines * -enumerator
+ Ct(LooseListItem(Cb("listtype")) *
LooseListItem(enumerator)^0)
* Cc(false) * skipblanklines
) * Cb("listtype") / orderedlist
local defstartchar = S("~:")
local defstart = ( defstartchar * #spacing * (tab + space^-3)
+ space * defstartchar * #spacing * (tab + space^-2)
+ space * space * defstartchar * #spacing *
(tab + space^-1)
+ space * space * space * defstartchar * #spacing
)
local dlchunk = Cs(line * (indentedline - blankline)^0)
local function definition_list_item(term, defs, tight)
return { term = parse_inlines(term), definitions = defs }
end
local DefinitionListItemLoose = C(line) * skipblanklines
* Ct((defstart *
indented_blocks(dlchunk) /
parse_blocks_toplevel)^1)
* Cc(false)
/ definition_list_item
local DefinitionListItemTight = C(line)
* Ct((defstart * dlchunk /
parse_blocks)^1)
* Cc(true)
/ definition_list_item
local DefinitionList = ( Ct(DefinitionListItemLoose^1) * Cc(false)
+ Ct(DefinitionListItemTight^1)
* (skipblanklines *
-DefinitionListItemLoose * Cc(true))
) / writer.definitionlist
% \end{macrocode}
% \paragraph{Blank Line \acro{peg} Rules}
% \begin{macrocode}
local Reference = define_reference_parser / register_link
local Blank = blankline / ""
+ NoteBlock
+ Reference
+ (tightblocksep / "\n")
% \end{macrocode}
% \paragraph{Paragraph \acro{peg} Rules}
% \begin{macrocode}
local Paragraph = nonindentspace * Ct(Inline^1) * newline
* ( blankline^1
+ #hash
+ #(leader * more * space^-1)
)
/ writer.paragraph
local ToplevelParagraph
= nonindentspace * Ct(Inline^1) * (newline
* ( blankline^1
+ #hash
+ #(leader * more * space^-1)
+ eof
)
+ eof )
/ writer.paragraph
local Plain = nonindentspace * Ct(Inline^1) / writer.plain
% \end{macrocode}
% \paragraph{Heading \acro{peg} Rules}
% \begin{macrocode}
-- parse Atx heading start and return level
local HeadingStart = #hash * C(hash^-6) * -hash / length
-- parse setext header ending and return level
local HeadingLevel = equal^1 * Cc(1) + dash^1 * Cc(2)
local function strip_atx_end(s)
return s:gsub("[#%s]*\n$","")
end
-- parse atx header
local AtxHeading = Cg(HeadingStart,"level")
* optionalspace
* (C(line) / strip_atx_end / parse_inlines)
* Cb("level")
/ writer.heading
-- parse setext header
local SetextHeading = #(line * S("=-"))
* Ct(line / parse_inlines)
* HeadingLevel
* optionalspace * newline
/ writer.heading
local Heading = AtxHeading + SetextHeading
% \end{macrocode}
% \paragraph{Top Level \acro{peg} Specification}
% \begin{macrocode}
syntax =
{ "Blocks",
Blocks = Blank^0 *
Block^-1 *
(Blank^0 / function()
return writer.interblocksep
end * Block)^0 *
Blank^0 *
eof,
Blank = Blank,
Block = V("Blockquote")
+ V("Verbatim")
+ V("FencedCode")
+ V("HorizontalRule")
+ V("BulletList")
+ V("OrderedList")
+ V("Heading")
+ V("DefinitionList")
+ V("Paragraph")
+ V("Plain"),
Blockquote = Blockquote,
Verbatim = Verbatim,
FencedCode = FencedCode,
HorizontalRule = HorizontalRule,
BulletList = BulletList,
OrderedList = OrderedList,
Heading = Heading,
DefinitionList = DefinitionList,
DisplayHtml = DisplayHtml,
Paragraph = Paragraph,
Plain = Plain,
Inline = V("Str")
+ V("Space")
+ V("Endline")
+ V("UlOrStarLine")
+ V("Strong")
+ V("Emph")
+ V("InlineNote")
+ V("NoteRef")
+ V("Citations")
+ V("Link")
+ V("Image")
+ V("Code")
+ V("AutoLinkUrl")
+ V("AutoLinkEmail")
+ V("EscapedChar")
+ V("Smart")
+ V("Symbol"),
Str = Str,
Space = Space,
Endline = Endline,
UlOrStarLine = UlOrStarLine,
Strong = Strong,
Emph = Emph,
InlineNote = InlineNote,
NoteRef = NoteRef,
Citations = Citations,
Link = Link,
Image = Image,
Code = Code,
AutoLinkUrl = AutoLinkUrl,
AutoLinkEmail = AutoLinkEmail,
InlineHtml = InlineHtml,
HtmlEntity = HtmlEntity,
EscapedChar = EscapedChar,
Smart = Smart,
Symbol = Symbol,
}
if not options.definitionLists then
syntax.DefinitionList = fail
end
if not options.fencedCode then
syntax.FencedCode = fail
end
if not options.citations then
syntax.Citations = fail
end
if not options.footnotes then
syntax.NoteRef = fail
end
if not options.inlineFootnotes then
syntax.InlineNote = fail
end
if not options.smartEllipses then
syntax.Smart = fail
end
local blocks_toplevel_t = util.table_copy(syntax)
blocks_toplevel_t.Paragraph = ToplevelParagraph
blocks_toplevel = Ct(blocks_toplevel_t)
blocks = Ct(syntax)
local inlines_t = util.table_copy(syntax)
inlines_t[1] = "Inlines"
inlines_t.Inlines = Inline^0 * (spacing^0 * eof / "")
inlines = Ct(inlines_t)
local inlines_no_link_t = util.table_copy(inlines_t)
inlines_no_link_t.Link = fail
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 = fail
inlines_no_inline_note = Ct(inlines_no_inline_note_t)
local inlines_nbsp_t = util.table_copy(inlines_t)
inlines_nbsp_t.Endline = NonbreakingEndline
inlines_nbsp_t.Space = NonbreakingSpace
inlines_nbsp = Ct(inlines_nbsp_t)
% \end{macrocode}
%
% \paragraph{Exported Conversion Function}
% Define \luamdef{reader->convert} as a function that converts markdown string
% \t`input` into a plain \TeX{} output and returns it. Note that the converter
% assumes that the input has \acro{unix} line endings.
% \begin{macrocode}
function self.convert(input)
references = {}
% \end{macrocode}
% When determining the name of the cache file, create salt for the hashing
% function out of the package version and the passed options recognized by the
% Lua interface (see Section \ref{sec:luaoptions}). The \Opt{cacheDir} option
% is disregarded.
% \begin{macrocode}
local opt_string = {}
for k,_ in pairs(defaultOptions) do
local v = options[k]
if k ~= "cacheDir" then
opt_string[#opt_string+1] = k .. "=" .. tostring(v)
end
end
table.sort(opt_string)
local salt = table.concat(opt_string, ",") .. "," .. metadata.version
% \end{macrocode}
% Produce the cache file, transform its filename via the \luam{writer->pack}
% method, and return the result.
% \begin{macrocode}
local name = util.cache(options.cacheDir, input, salt, function(input)
return util.rope_to_string(parse_blocks_toplevel(input)) .. writer.eof
end, ".md" .. writer.suffix)
return writer.pack(name)
end
return self
end
% \end{macrocode}
% \subsubsection{Conversion from Markdown to Plain \TeX{}}
% The \luam{new} method returns the \luam{reader->convert} function of a reader
% object associated with the Lua interface options (see Section
% \ref{sec:luaoptions}) \t`options` and with a writer object associated with
% \t`options`.
% \begin{macrocode}
function M.new(options)
local writer = M.writer.new(options)
local reader = M.reader.new(writer, options)
return reader.convert
end
return M
% \end{macrocode}
%
% \iffalse
%
%<*tex>
% \fi\subsection{Plain \TeX{} Implementation}\label{sec:teximplementation}
% The plain \TeX{} implementation provides macros for the interfacing between
% \TeX{} and Lua and for the buffering of input text. These macros are then
% used to implement the macros for the conversion from markdown to plain \TeX{}
% exposed by the plain \TeX{} interface (see Section \ref{sec:texinterface}).
%
% \subsubsection{Logging Facilities}\label{sec:texinterfacelogging}
% \begin{macrocode}
\def\markdownInfo#1{%
\message{(l.\the\inputlineno) markdown.tex info: #1.}}%
\def\markdownWarning#1{%
\message{(l.\the\inputlineno) markdown.tex warning: #1}}%
\def\markdownError#1#2{%
\errhelp{#2.}%
\errmessage{(l.\the\inputlineno) markdown.tex error: #1}}%
% \end{macrocode}
%
% \subsubsection{Token Renderer Prototypes}
% The following definitions should be considered placeholder.
% \begin{macrocode}
\def\markdownRendererInterblockSeparatorPrototype{\par}%
\def\markdownRendererLineBreakPrototype{\hfil\break}%
\let\markdownRendererEllipsisPrototype\dots
\def\markdownRendererNbspPrototype{~}%
\def\markdownRendererLeftBracePrototype{\char`{}%
\def\markdownRendererRightBracePrototype{\char`}}%
\def\markdownRendererDollarSignPrototype{\char`$}%
\def\markdownRendererPercentSignPrototype{\char`\%}%
\def\markdownRendererAmpersandPrototype{\char`&}%
\def\markdownRendererUnderscorePrototype{\char`_}%
\def\markdownRendererHashPrototype{\char`\#}%
\def\markdownRendererCircumflexPrototype{\char`^}%
\def\markdownRendererBackslashPrototype{\char`\\}%
\def\markdownRendererTildePrototype{\char`~}%
\def\markdownRendererPipePrototype{|}%
\def\markdownRendererCodeSpanPrototype#1{{\tt#1}}%
\def\markdownRendererLinkPrototype#1#2#3#4{#2}%
\def\markdownRendererImagePrototype#1#2#3#4{#2}%
\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{#1}%
\def\markdownRendererDlItemEndPrototype{}%
\def\markdownRendererDlDefinitionBeginPrototype{}%
\def\markdownRendererDlDefinitionEndPrototype{\par}%
\def\markdownRendererDlEndPrototype{}%
\def\markdownRendererDlEndTightPrototype{}%
\def\markdownRendererEmphasisPrototype#1{{\it#1}}%
\def\markdownRendererStrongEmphasisPrototype#1{{\it#1}}%
\def\markdownRendererBlockQuoteBeginPrototype{\par\begingroup\it}%
\def\markdownRendererBlockQuoteEndPrototype{\endgroup\par}%
\def\markdownRendererInputVerbatimPrototype#1{%
\par{\tt\input"#1"\relax}\par}%
\def\markdownRendererInputFencedCodePrototype#1#2{%
\markdownRendererInputVerbatimPrototype{#1}}%
\def\markdownRendererHeadingOnePrototype#1{#1}%
\def\markdownRendererHeadingTwoPrototype#1{#1}%
\def\markdownRendererHeadingThreePrototype#1{#1}%
\def\markdownRendererHeadingFourPrototype#1{#1}%
\def\markdownRendererHeadingFivePrototype#1{#1}%
\def\markdownRendererHeadingSixPrototype#1{#1}%
\def\markdownRendererHorizontalRulePrototype{}%
\def\markdownRendererFootnotePrototype#1{#1}%
\def\markdownRendererCitePrototype#1{}%
\def\markdownRendererTextCitePrototype#1{}%
% \end{macrocode}
%
% \subsubsection{Lua Snippets}
% The \mdef{markdownLuaOptions} macro expands to a Lua table that
% contains the plain \TeX{} options (see Section \ref{sec:texoptions}) in a
% format recognized by Lua (see Section \ref{sec:luaoptions}). Note that the
% boolean options are not sanitized and expect the plain \TeX{} option macros
% to expand to either \t`true` or \t`false`.
% \begin{macrocode}
\def\markdownLuaOptions{{%
\ifx\markdownOptionBlankBeforeBlockquote\undefined\else
blankBeforeBlockquote = \markdownOptionBlankBeforeBlockquote,
\fi
\ifx\markdownOptionBlankBeforeCodeFence\undefined\else
blankBeforeCodeFence = \markdownOptionBlankBeforeCodeFence,
\fi
\ifx\markdownOptionBlankBeforeHeading\undefined\else
blankBeforeHeading = \markdownOptionBlankBeforeHeading,
\fi
\ifx\markdownOptionCacheDir\undefined\else
cacheDir = "\markdownOptionCacheDir",
\fi
\ifx\markdownOptionCitations\undefined\else
citations = \markdownOptionCitations,
\fi
\ifx\markdownOptionCitationNbsps\undefined\else
citationNbsps = \markdownOptionCitationNbsps,
\fi
\ifx\markdownOptionDefinitionLists\undefined\else
definitionLists = \markdownOptionDefinitionLists,
\fi
\ifx\markdownOptionFootnotes\undefined\else
footnotes = \markdownOptionFootnotes,
\fi
\ifx\markdownOptionFencedCode\undefined\else
fencedCode = \markdownOptionFencedCode,
\fi
\ifx\markdownOptionHashEnumerators\undefined\else
hashEnumerators = \markdownOptionHashEnumerators,
\fi
\ifx\markdownOptionHybrid\undefined\else
hybrid = \markdownOptionHybrid,
\fi
\ifx\markdownOptionInlineFootnotes\undefined\else
inlineFootnotes = \markdownOptionInlineFootnotes,
\fi
\ifx\markdownOptionPreserveTabs\undefined\else
preserveTabs = \markdownOptionPreserveTabs,
\fi
\ifx\markdownOptionSmartEllipses\undefined\else
smartEllipses = \markdownOptionSmartEllipses,
\fi
\ifx\markdownOptionStartNumber\undefined\else
startNumber = \markdownOptionStartNumber,
\fi
\ifx\markdownOptionTightLists\undefined\else
tightLists = \markdownOptionTightLists,
\fi}
}%
% \end{macrocode}
%
% The \mdef{markdownPrepare} macro contains the Lua code that is executed prior
% to any conversion from markdown to plain \TeX{}. It exposes the
% \luam{convert} function for the use by any further Lua code.
% \begin{macrocode}
\def\markdownPrepare{%
% \end{macrocode}
% First, ensure that the \m{markdownOptionCacheDir} directory exists.
% \begin{macrocode}
local lfs = require("lfs")
local cacheDir = "\markdownOptionCacheDir"
if lfs.isdir(cacheDir) == true then else
assert(lfs.mkdir(cacheDir))
end
% \end{macrocode}
% Next, load the \t`markdown` module and create a converter function using
% the plain \TeX{} options, which were serialized to a Lua table via the
% \m{markdownLuaOptions} macro.
% \begin{macrocode}
local md = require("markdown")
local convert = md.new(\markdownLuaOptions)
}%
% \end{macrocode}
%
% \subsubsection{Lua Shell Escape Bridge}\label{sec:luabridge}
% The following \TeX{} code is intended for \TeX{} engines that do not provide
% direct access to Lua, but expose the shell of the operating system. This
% corresponds to the \m{markdownMode} values of \t`0` and \t`1`.
%
% The \m{markdownLuaExecute} and \m{markdownReadAndConvert} macros defined here
% and in Section \ref{sec:directlua} are meant to be transparent to the
% remaining code.
%
% The package assumes that although the user is not using the Lua\TeX{} engine,
% their TeX distribution contains it, and uses shell access to produce and
% execute Lua scripts using the \TeX{}Lua interpreter (see
% \cite[Section~3.1.1]{luatex16}).
% \begin{macrocode}
\ifnum\markdownMode<2\relax
\ifnum\markdownMode=0\relax
\markdownInfo{Using mode 0: Shell escape via write18}%
\else
\markdownInfo{Using mode 1: Shell escape via os.execute}%
\fi
% \end{macrocode}
%
% The macro \mdef{markdownLuaExecuteFileStream} contains the number of the output
% file stream that will be used to store the helper Lua script in the file named
% \m{markdownOptionHelperScriptFileName} during the expansion of the macro
% \m{markdownLuaExecute}, and to store the markdown input in the file
% named \m{markdownOptionInputTempFileName} during the expansion of the macro
% \m{markdownReadAndConvert}.
% \begin{macrocode}
\csname newwrite\endcsname\markdownLuaExecuteFileStream
% \end{macrocode}
%
% The \mdef{markdownExecuteShellEscape} macro contains the numeric value indicating
% whether the shell access is enabled (\t`1`), disabled (\t`0`), or restricted
% (\t`2`).
%
% Inherit the value of the the \m{pdfshellescape} (Lua\TeX{}, \Hologo{pdfTeX})
% or the \m{shellescape} (\Hologo{XeTeX}) commands. If neither of these
% commands is defined and Lua is available, attempt to access the
% \luam{status.shell_escape} configuration item.
%
% If you cannot detect, whether the shell access is enabled, act as if it were.
% \begin{macrocode}
\ifx\pdfshellescape\undefined
\ifx\shellescape\undefined
\ifnum\markdownMode=0\relax
\def\markdownExecuteShellEscape{1}%
\else
\def\markdownExecuteShellEscape{%
\directlua{tex.sprint(status.shell_escape or "1")}}%
\fi
\else
\let\markdownExecuteShellEscape\shellescape
\fi
\else
\let\markdownExecuteShellEscape\pdfshellescape
\fi
% \end{macrocode}
%
% The \mdef{markdownExecuteDirect} macro executes the code it has received as
% its first argument by writing it to the output file stream 18, if Lua is
% unavailable, or by using the Lua \luam{markdown.execute} method otherwise.
% \begin{macrocode}
\ifnum\markdownMode=0\relax
\def\markdownExecuteDirect#1{\immediate\write18{#1}}%
\else
\def\markdownExecuteDirect#1{%
\directlua{os.execute("\luaescapestring{#1}")}}%
\fi
% \end{macrocode}
%
% The \mdef{markdownExecute} macro is a wrapper on top of
% \m{markdownExecuteDirect} that checks the value of
% \m{markdownExecuteShellEscape} and prints an error message if the shell is
% inaccessible.
% \begin{macrocode}
\def\markdownExecute#1{%
\ifnum\markdownExecuteShellEscape=1\relax
\markdownExecuteDirect{#1}%
\else
\markdownError{I can not access the shell}{Either run the TeX
compiler with the --shell-escape or the --enable-write18 flag,
or set shell_escape=t in the texmf.cnf file}%
\fi}%
% \end{macrocode}
%
% The \mdef{markdownLuaExecute} macro executes the Lua code it has received as
% its first argument. The Lua code may not directly interact with the \TeX{}
% engine, but it can use the \luam{print} function in the same manner it
% would use the \luam{tex.print} method.
% \begin{macrocode}
\def\markdownLuaExecute#1{%
% \end{macrocode}
% Create the file \m{markdownOptionHelperScriptFileName} and fill it with the
% input Lua code prepended with \pkg{kpathsea} initialization, so that Lua
% modules from the \TeX{} distribution are available.
% \begin{macrocode}
\immediate\openout\markdownLuaExecuteFileStream=%
\markdownOptionHelperScriptFileName
\markdownInfo{Writing a helper Lua script to the file
"\markdownOptionHelperScriptFileName"}%
\immediate\write\markdownLuaExecuteFileStream{%
local kpse = require('kpse')
kpse.set_program_name('luatex') #1}%
\immediate\closeout\markdownLuaExecuteFileStream
% \end{macrocode}
% Execute the generated \m{markdownOptionHelperScriptFileName} Lua script using
% the \TeX{}Lua binary and store the output in the
% \m{markdownOptionOutputTempFileName} file.
% \begin{macrocode}
\markdownInfo{Executing a helper Lua script from the file
"\markdownOptionHelperScriptFileName" and storing the result in the
file "\markdownOptionOutputTempFileName"}%
\markdownExecute{texlua "\markdownOptionHelperScriptFileName" >
"\markdownOptionOutputTempFileName"}%
% \end{macrocode}
% \m{input} the generated \m{markdownOptionOutputTempFileName} file.
% \begin{macrocode}
\input\markdownOptionOutputTempFileName\relax}%
% \end{macrocode}
%
% The \mdef{markdownReadAndConvertTab} macro contains the tab character literal.
% \begin{macrocode}
\begingroup
\catcode`\^^I=12%
\gdef\markdownReadAndConvertTab{^^I}%
\endgroup
% \end{macrocode}
%
% The \m{markdownReadAndConvert} macro is largely a rewrite of the
% \Hologo{LaTeX2e} \m{filecontents} macro to plain \TeX{}.
% \begin{macrocode}
\begingroup
% \end{macrocode}
% Make the newline and tab characters active and swap the character codes of the
% backslash symbol (\t`\textbackslash`) and the pipe symbol (\t`|`), so that
% we can use the backslash as an ordinary character inside the macro definition.
% \begin{macrocode}
\catcode`\^^M=13%
\catcode`\^^I=13%
\catcode`|=0%
\catcode`\\=12%
|gdef|markdownReadAndConvert#1#2{%
|begingroup%
% \end{macrocode}
% Open the \m{markdownOptionInputTempFileName} file for writing.
% \begin{macrocode}
|immediate|openout|markdownLuaExecuteFileStream%
|markdownOptionInputTempFileName%
|markdownInfo{Buffering markdown input into the temporary %
input file "|markdownOptionInputTempFileName" and scanning %
for the closing token sequence "#1"}%
% \end{macrocode}
% Locally change the category of the special plain \TeX{} characters to
% \emph{other} in order to prevent unwanted interpretation of the input.
% Change also the category of the space character, so that we can retrieve it
% unaltered.
% \begin{macrocode}
|def|do##1{|catcode`##1=12}|dospecials%
|catcode`| =12%
|markdownMakeOther%
% \end{macrocode}
% The \mdef{markdownReadAndConvertProcessLine} macro will process the individual
% lines of output. Note the use of the comments to ensure that the entire macro
% is at a single line and therefore no (active) newline symbols are produced.
% \begin{macrocode}
|def|markdownReadAndConvertProcessLine##1#1##2#1##3|relax{%
% \end{macrocode}
% When the ending token sequence does not appear in the line, store the line in
% the \m{markdownOptionInputTempFileName} file.
% \begin{macrocode}
|ifx|relax##3|relax%
|immediate|write|markdownLuaExecuteFileStream{##1}%
|else%
% \end{macrocode}
% When the ending token sequence appears in the line, make the next newline
% character close the \m{markdownOptionInputTempFileName} file, return the
% character categories back to the former state, convert the
% \m{markdownOptionInputTempFileName} file from markdown to plain \TeX{},
% \m{input} the result of the conversion, and expand the ending control
% sequence.
% \begin{macrocode}
|def^^M{%
|markdownInfo{The ending token sequence was found}%
|immediate|closeout|markdownLuaExecuteFileStream%
|endgroup%
|markdownInput|markdownOptionInputTempFileName%
#2}%
|fi%
% \end{macrocode}
% Repeat with the next line.
% \begin{macrocode}
^^M}%
% \end{macrocode}
% Make the tab character active at expansion time and make it expand to a
% literal tab character.
% \begin{macrocode}
|catcode`|^^I=13%
|def^^I{|markdownReadAndConvertTab}%
% \end{macrocode}
% Make the newline character active at expansion time and make it consume the
% rest of the line on expansion. Throw away the rest of the first line and
% pass the second line to the \m{markdownReadAndConvertProcessLine} macro.
% \begin{macrocode}
|catcode`|^^M=13%
|def^^M##1^^M{%
|def^^M####1^^M{%
|markdownReadAndConvertProcessLine####1#1#1|relax}%
^^M}%
^^M}%
% \end{macrocode}
% Reset the character categories back to the former state.
% \begin{macrocode}
|endgroup
% \end{macrocode}
%
% \subsubsection{Direct Lua Access}\label{sec:directlua}
% The following \TeX{} code is intended for \TeX{} engines that provide
% direct access to Lua (Lua\TeX{}). The \m{markdownLuaExecute} and
% \m{markdownReadAndConvert} defined here and in Section \ref{sec:luabridge}
% are meant to be transparent to the remaining code. This corresponds to the
% \m{markdownMode} value of \t`2`.
% \begin{macrocode}
\else
\markdownInfo{Using mode 2: Direct Lua access}%
% \end{macrocode}
% The direct Lua access version of the \m{markdownLuaExecute} macro is defined
% in terms of the \m{directlua} primitive. The \luam{print} function is set as
% an alias to the \m{tex.print} method in order to mimic the behaviour of the
% \m{markdownLuaExecute} definition from Section \ref{sec:luabridge},
% \begin{macrocode}
\def\markdownLuaExecute#1{\directlua{local print = tex.print #1}}%
% \end{macrocode}
%
% In the definition of the direct Lua access version of the
% \m{markdownReadAndConvert} macro, we will be using the hash symbol
% (\t`\#`), the underscore symbol (\t`_`), the circumflex symbol (\t`\^`), the
% dollar sign (\t`\$`), the backslash symbol (\t`\textbackslash`), the percent
% sign (\t`\%`), and the braces (\t`\{\}`) as a part of the Lua syntax.
% \begin{macrocode}
\begingroup
% \end{macrocode}
%
% To this end, we will make the underscore symbol, the dollar sign, and
% circumflex symbols ordinary characters,
% \begin{macrocode}
\catcode`\_=12%
\catcode`\$=12%
\catcode`\^=12%
% \end{macrocode}
% swap the category code of the hash symbol with the slash symbol (\t`/`).
% \begin{macrocode}
\catcode`\/=6%
\catcode`\#=12%
% \end{macrocode}
% swap the category code of the percent sign with the at symbol (\t`@`).
% \begin{macrocode}
\catcode`\@=14%
\catcode`\%=12%
% \end{macrocode}
% swap the category code of the backslash symbol with the pipe symbol (\t`|`),
% \begin{macrocode}
\catcode`|=0@
\catcode`\\=12@
% \end{macrocode}
% Braces are a part of the plain \TeX{} syntax, but they are not removed during
% expansion, so we do not need to bother with changing their category codes.
% \begin{macrocode}
|gdef|markdownReadAndConvert/1/2{@
% \end{macrocode}
% Make the \mdef{markdownReadAndConvertAfter} macro store the token sequence
% that will be inserted into the document after the ending token sequence has
% been found.
% \begin{macrocode}
|def|markdownReadAndConvertAfter{/2}@
|markdownInfo{Buffering markdown input and scanning for the
closing token sequence "/1"}@
|directlua{@
% \end{macrocode}
% Set up an empty Lua table that will serve as our buffer.
% \begin{macrocode}
|markdownPrepare
local buffer = {}
% \end{macrocode}
% Create a regex that will match the ending input sequence. Escape any special
% regex characters (like a star inside \t`\textbackslash end\{markdown*\}`)
% inside the input.
% \begin{macrocode}
local ending_sequence = "^.-" .. ([[/1]]):gsub(
"([%(%)%.%%%+%-%*%?%[%]%^%$])", "%%%1")
% \end{macrocode}
% Register a callback that will notify you about new lines of input.
% \begin{macrocode}
|markdownLuaRegisterIBCallback{function(line)
% \end{macrocode}
% When the ending token sequence appears on a line, unregister the callback,
% convert the contents of our buffer from markdown to plain \TeX{}, and insert
% the result into the input line buffer of \TeX{}.
% \begin{macrocode}
if line:match(ending_sequence) then
|markdownLuaUnregisterIBCallback;
local input = table.concat(buffer, "\n") .. "\n"
local output = convert(input)
return [[\markdownInfo{The ending token sequence was found}]] ..
output .. [[\markdownReadAndConvertAfter]]
% \end{macrocode}
% When the ending token sequence does not appear on a line, store the line in
% our buffer, and insert either \m{fi}, if this is the first line of input,
% or an empty token list to the input line buffer of \TeX{}.
% \begin{macrocode}
else
buffer[#buffer+1] = line
return [[\]] .. (#buffer == 1 and "fi" or "relax")
end
end}}@
% \end{macrocode}
% Insert \m{iffalse} after the \m{markdownReadAndConvert} macro in order to
% consume the rest of the first line of input.
% \begin{macrocode}
|iffalse}@
% \end{macrocode}
% Reset the character categories back to the former state.
% \begin{macrocode}
|endgroup
\fi
% \end{macrocode}
%
% \subsubsection{Typesetting Markdown}
% The \m{markdownInput} macro uses an implementation of the
% \m{markdownLuaExecute} macro to convert the contents of the file whose
% filename it has received as its single argument from markdown to plain
% \TeX{}.
% \begin{macrocode}
\begingroup
% \end{macrocode}
% Swap the category code of the backslash symbol and the pipe symbol, so that
% we may use the backslash symbol freely inside the Lua code.
% \begin{macrocode}
\catcode`|=0%
\catcode`\\=12%
|gdef|markdownInput#1{%
|markdownInfo{Including markdown document "#1"}%
|markdownLuaExecute{%
|markdownPrepare
local input = assert(io.open("#1","r")):read("*a")
% \end{macrocode}
% Since the Lua converter expects \acro{unix} line endings, normalize the
% input.
% \begin{macrocode}
print(convert(input:gsub("\r\n?", "\n")))}}%
|endgroup
% \end{macrocode}
%
% \iffalse
%
%<*latex>
% \fi\subsection{\LaTeX{} Implementation}\label{sec:lateximplementation}
% The \LaTeX{} implemenation makes use of the fact that, apart from some subtle
% differences, \LaTeX{} implements the majority of the plain \TeX{} format
% (see \cite[Section~9]{latex16}). As a consequence, we can directly reuse the
% existing plain \TeX{} implementation.
% \begin{macrocode}
\input markdown
\def\markdownVersionSpace{ }%
\ProvidesPackage{markdown}[\markdownLastModified\markdownVersionSpace v%
\markdownVersion\markdownVersionSpace markdown renderer]%
% \end{macrocode}
%
% \subsubsection{Logging Facilities}
% The \LaTeX{} implementation redefines the plain \TeX{} logging macros (see
% Section \ref{sec:texinterfacelogging}) to use the \LaTeX{} \m{PackageInfo},
% \m{PackageWarning}, and \m{PackageError} macros.
% \begin{macrocode}
\renewcommand\markdownInfo[1]{\PackageInfo{markdown}{#1}}%
\renewcommand\markdownWarning[1]{\PackageWarning{markdown}{#1}}%
\renewcommand\markdownError[2]{\PackageError{markdown}{#1}{#2.}}%
% \end{macrocode}
%
% \subsubsection{Typesetting Markdown}
% The \mdef{markdownInputPlainTeX} macro is used to store the original plain
% \TeX{} implementation of the \m{markdownInput} macro. The \m{markdownInput}
% is then redefined to accept an optional argument with options recognized by
% the \LaTeX{} interface (see Section \ref{sec:latexoptions}).
% \begin{macrocode}
\let\markdownInputPlainTeX\markdownInput
\renewcommand\markdownInput[2][]{%
\begingroup
\markdownSetup{#1}%
\markdownInputPlainTeX{#2}%
\endgroup}%
% \end{macrocode}
%
% The \env{markdown}, and \env{markdown*} \LaTeX{} environments are implemented
% using the \m{markdownReadAndConvert} macro.
% \begin{macrocode}
\renewenvironment{markdown}{%
\markdownReadAndConvert@markdown{}}\relax
\renewenvironment{markdown*}[1]{%
\markdownSetup{#1}%
\markdownReadAndConvert@markdown*}\relax
\begingroup
% \end{macrocode}
% Locally swap the category code of the backslash symbol with the pipe symbol,
% and of the left (\t`\{`) and right brace (\t`\}`) with the less-than (\t`<`)
% and greater-than (\t`>`) signs. This is required in order that all the
% special symbols that appear in the first argument of the
% \t`markdownReadAndConvert` macro have the category code \emph{other}.
% \begin{macrocode}
\catcode`\|=0\catcode`\<=1\catcode`\>=2%
\catcode`\\=12|catcode`|{=12|catcode`|}=12%
|gdef|markdownReadAndConvert@markdown#1<%
|markdownReadAndConvert<\end{markdown#1}>%
<|end