summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/generic/markdown/markdown.dtx
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2021-08-07 20:19:27 +0000
committerKarl Berry <karl@freefriends.org>2021-08-07 20:19:27 +0000
commitd0d20a6ba7cff5159a2f78b414326262f45b0542 (patch)
treeb14872bbfe4e416eed32b7707844a2078189d27f /Master/texmf-dist/source/generic/markdown/markdown.dtx
parent84dbd3f0f8c0982f6944cc314d7ac0cbefec0769 (diff)
markdown (7aug21)
git-svn-id: svn://tug.org/texlive/trunk@60182 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/source/generic/markdown/markdown.dtx')
-rw-r--r--Master/texmf-dist/source/generic/markdown/markdown.dtx1780
1 files changed, 1521 insertions, 259 deletions
diff --git a/Master/texmf-dist/source/generic/markdown/markdown.dtx b/Master/texmf-dist/source/generic/markdown/markdown.dtx
index a3ebaecd88d..f035985ca80 100644
--- a/Master/texmf-dist/source/generic/markdown/markdown.dtx
+++ b/Master/texmf-dist/source/generic/markdown/markdown.dtx
@@ -2,7 +2,7 @@
%<*driver>
\documentclass{ltxdockit}
\usepackage[american]{babel}
-\usepackage{amsmath,btxdockit,doc,fancyvrb,hologo,microtype,minted}
+\usepackage{amsmath,btxdockit,doc,fancyvrb,graphicx,hologo,microtype,minted}
\usepackage{fontspec}
\defaultfontfeatures[\rmfamily,\sffamily,\ttfamily]{}
@@ -21,6 +21,12 @@
\hypersetup{citecolor=spot}
\let\oldCodelineNo\theCodelineNo
\def\theCodelineNo{\textcolor[gray]{0.5}{\oldCodelineNo}}
+\makeatletter\renewcommand\fps@figure{htbp}\makeatother
+\setkeys{Gin}{
+ width = \columnwidth,
+ height = 0.65\paperheight,
+ keepaspectratio,
+}
% Define some markup.
\let\pkg\relax % A package name
@@ -58,6 +64,8 @@
html,
stripPercentSigns,
underscores=false,
+ theme=witiko/dot,
+ theme=witiko/graphicx/http,
]{markdown}
\markdownSetup{
renderers = {
@@ -93,6 +101,12 @@
% Set up the bibliography.
\begin{filecontents}[overwrite,nosearch,noheader]{markdown.bib}
+@book{tantau21,
+ author = {Till Tantau and Joseph Wright and Vedran Miletić},
+ title = {The Beamer class},
+ date = {2021-02-10},
+ url = {http://mirrors.ctan.org/macros/latex/contrib/beamer/doc/beameruserguide.pdf},
+ urldate = {2021-02-11}}
@online{sotkov17,
author = {Sotkov, Anton},
title = {File transclusion syntax for Markdown},
@@ -153,6 +167,12 @@
isbn = {0-201-13447-0},
pagetotal = {ix, 479},
publisher = {Addison-Wesley}}
+@online{novotny21,
+ author = {Novotný, Vít},
+ title = {\Hologo{LaTeX2e} no longer keys packages by pathnames},
+ date = {2021-02-20},
+ url = {https://github.com/latex3/latex2e/issues/510},
+ urldate = {2021-02-21}}
\end{filecontents}
\usepackage[
backend=biber,
@@ -577,11 +597,11 @@ documentation][techdoc].
% \fi
% \begin{macrocode}
local metadata = {
- version = "2.9.0",
+ version = "2.10.0",
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-2020 Vít Novotný"},
+ "2016-2021 Vít Novotný"},
license = "LPPL 1.3"
}
@@ -660,7 +680,10 @@ This should produce the following files:
* `markdown.lua`, the Lua module,
* `markdown-cli.lua`, the Lua command-line interface,
* `markdown.tex`, the plain \TeX{} macro package,
- * `markdown.sty`, the \LaTeX{} package, and
+ * `markdown.sty`, the \LaTeX{} package,
+ * `markdownthemewitiko_dot.sty`, the `witiko/dot` \LaTeX{} theme,
+ * `markdownthemewitiko_graphicx_http.sty`, the `witiko/graphicx/http` \LaTeX{} theme,
+ * `markdownthemewitiko_tilde.sty`, the `witiko/tilde` \LaTeX{} theme, and
* `t-markdown.tex`, the \Hologo{ConTeXt} module.
### Local Installation
@@ -673,6 +696,9 @@ placed:
* `<TEXMF>/scripts/markdown/markdown-cli.lua`
* `<TEXMF>/tex/generic/markdown/markdown.tex`
* `<TEXMF>/tex/latex/markdown/markdown.sty`
+ * `<TEXMF>/tex/latex/markdown/markdownthemewitiko_dot.sty`
+ * `<TEXMF>/tex/latex/markdown/markdownthemewitiko_graphicx_http.sty`
+ * `<TEXMF>/tex/latex/markdown/markdownthemewitiko_tilde.sty`
* `<TEXMF>/tex/context/third/markdown/t-markdown.tex`
where `<TEXMF>` corresponds to a root of your \TeX{} distribution, such as
@@ -686,6 +712,11 @@ Alternatively, you can also store the above files in the same folder as your
\TeX{} document and distribute them together. This way your document can be
portably typeset on legacy \TeX{} distributions.
+Since Markdown version 2.10.0, the file `markdown.tex` must be placed in
+a directory named `markdown` due to [issue #74][issue-74].
+
+ [issue-74]: https://github.com/witiko/markdown/issues/74 (Does not compile if user names own document markdown.tex)
+
%</manual>
%<*lua>
% \fi
@@ -726,7 +757,18 @@ local lpeg = require("lpeg")
%
% \end{markdown}
% \begin{macrocode}
-local unicode = require("unicode")
+local ran_ok, unicode = pcall(require, "unicode")
+% \end{macrocode}
+% \begin{markdown}
+%
+% If the Selene Unicode library is unavailable and we are using
+% Lua${}\geq{}$5.3, we will use the built-in support for Unicode.
+%
+% \end{markdown}
+% \begin{macrocode}
+if not ran_ok then
+ unicode = {["utf8"]={char=utf8.char}}
+end
% \end{macrocode}
% \par
% \begin{markdown}
@@ -801,12 +843,19 @@ local md5 = require("md5")
% The \LaTeX{} part of the package requires that the \Hologo{LaTeX2e} format is
% loaded,
% \end{markdown}
+% \iffalse
+%<*latex-themes-witiko-dot,latex-themes-witiko-graphicx-http,latex-themes-witiko-tilde>
+% \fi
% \begin{macrocode}
\NeedsTeXFormat{LaTeX2e}%
% \end{macrocode}
+% \iffalse
+%</latex-themes-witiko-dot,latex-themes-witiko-graphicx-http,latex-themes-witiko-tilde>
+% \fi
% \begin{markdown}
-% all the plain \TeX{} prerequisites (see Section \ref{sec:texprerequisites}),
-% and the following \Hologo{LaTeX2e} packages:
+% a \TeX{} engine that extends \Hologo{eTeX}, all the plain \TeX{}
+% prerequisites (see Section \ref{sec:texprerequisites}), and the following
+% \Hologo{LaTeX2e} packages:
%
% \pkg{keyval}
%
@@ -822,37 +871,38 @@ local md5 = require("md5")
% \par
% \begin{markdown}
%
-% \pkg{url}
+% \pkg{xstring}
%
-%: 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.
+%: A package that provides useful macros for manipulating strings of tokens.
%
% \end{markdown}
% \begin{macrocode}
-\RequirePackage{url}
+\RequirePackage{xstring}
% \end{macrocode}
% \par
% \begin{markdown}
%
+% The following packages are soft prerequisites and will not be loaded
+% if the `plain` package option has been enabled (see Section
+% \ref{sec:latexplain}):
+%
+% \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.
+%
% \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}).
%
-% \end{markdown}
-% \begin{macrocode}
-\RequirePackage{graphicx}
-% \end{macrocode}
-% \par
-% \begin{markdown}
-%
% \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
+% ordered lists, and definition lists. It is used to provide the
% corresponding default token renderer prototypes (see Section
% \ref{sec:texrendererprototypes}).
%
@@ -860,14 +910,9 @@ local md5 = require("md5")
%
%: 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.
-%
-% \end{markdown}
-% \begin{macrocode}
-\RequirePackage{ifthen}
-% \end{macrocode}
-% \par
-% \begin{markdown}
+% should be loaded based on the user options, in the `witiko/dot` \LaTeX{}
+% theme (see Section \ref{sec:latexthemes}), and to provide default token
+% renderer prototypes (see Section \ref{sec:texrendererprototypes}).
%
% \pkg{fancyvrb}
%
@@ -876,34 +921,19 @@ local md5 = require("md5")
% corresponding default token renderer prototype (see Section
% \ref{sec:texrendererprototypes}).
%
-% \end{markdown}
-% \begin{macrocode}
-\RequirePackage{fancyvrb}
-% \end{macrocode}
-% \par
-% \begin{markdown}
-%
% \pkg{csvsimple}
%
%: A package that provides the default token renderer prototype for
% iA\,Writer content blocks with the \acro{csv} filename extension (see
% Section \ref{sec:texrendererprototypes}).
%
-% \end{markdown}
-% \begin{macrocode}
-\RequirePackage{csvsimple}
-% \end{macrocode}
-% \par
-% \begin{markdown}
-%
% \pkg{gobble}
%
-%: A package that provides the \m{\@gobblethree} \TeX{} command.
+%: A package that provides the \m{\@gobblethree} \TeX{} command that
+% is used in the default renderer prototype for citations (see
+% Section \ref{sec:texrendererprototypes}).
%
% \end{markdown}
-% \begin{macrocode}
-\RequirePackage{gobble}
-% \end{macrocode}
% \iffalse
%</latex>
%<*context>
@@ -963,8 +993,8 @@ Using a text editor, create a text document named `hello.lua` with the
following content:
``` lua
#!/usr/bin/env texlua
-local kpse = require("kpse")
-kpse.set_program_name("luatex")
+local ran_ok, kpse = pcall(require, "kpse")
+if ran_ok then kpse.set_program_name("luatex") end
local markdown = require("markdown")
local convert = markdown.new()
print(convert("Hello *world*!"))
@@ -1220,8 +1250,8 @@ Using a text editor, create a text document named `example.lua` with the
following content:
``` lua
#!/usr/bin/env texlua
-local kpse = require("kpse")
-kpse.set_program_name("luatex")
+local ran_ok, kpse = pcall(require, "kpse")
+if ran_ok then kpse.set_program_name("luatex") end
local markdown = require("markdown")
local input, convert_safe, convert_unsafe, paragraph
@@ -1308,7 +1338,10 @@ following content:
\input lmfonts
\input safe
+\begingroup
+\catcode`\%=12
\input unsafe
+\endgroup
\bye
```````
Using a text editor, create a text document named `example.md` with the
@@ -2677,13 +2710,13 @@ defaultOptions.breakableBlockquotes = false
%
: true
- : Replace regular spaces with non-breakable spaces inside the prenotes
+ : Replace regular spaces with non-breaking spaces inside the prenotes
and postnotes of citations produced via the pandoc citation syntax
extension.
false
- : Do not replace regular spaces with non-breakable spaces inside the
+ : Do not replace regular spaces with non-breaking spaces inside the
prenotes and postnotes of citations produced via the pandoc citation
syntax extension.
@@ -2729,7 +2762,7 @@ lualatex document.tex
lualatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
-following text, where the middot (`·`) denotes a non-breakable space:
+following text, where the middot (`·`) denotes a non-breaking space:
> The TeXbook [1, p.·123·and·130] is good.
>
@@ -4506,7 +4539,10 @@ following content:
\input lmfonts
\input optionfalse
+\begingroup
+\catcode`\%=12
\input optiontrue
+\endgroup
\bye
```````
Using a text editor, create a text document named `content.md` with the
@@ -4666,7 +4702,7 @@ Using a text editor, create a text document named `document.tex` with the
following content:
``` tex
\documentclass{article}
-\usepackage[footnotes, inlineFootnotes]{markdown}
+\usepackage[inlineFootnotes]{markdown}
\begin{document}
\begin{markdown}
Here is an inline note.^[Inlines notes are easier to
@@ -4692,7 +4728,6 @@ Using a text editor, create a text document named `document.tex` with the
following content:
``` tex
\usemodule[t][markdown]
-\def\markdownOptionFootnotes{true}
\def\markdownOptionInlineFootnotes{true}
\starttext
\startmarkdown
@@ -4920,10 +4955,9 @@ following content:
``` tex
\documentclass{article}
\usepackage{markdown}
-\usepackage{filecontents}
-\begin{filecontents*}{example.md}
+\begin{filecontents}[overwrite,nosearch,noheader]{example.md}
## A section
-\end{filecontents*}
+\end{filecontents}
\begin{document}
\markdownInput[shiftHeadings=-1]{example.md}
\markdownInput{example.md}
@@ -5092,8 +5126,7 @@ following content:
``` tex
\documentclass{article}
\usepackage[headerAttributes]{markdown}
-\usepackage{filecontents}
-\begin{filecontents*}{hamlet.md}
+\begin{filecontents}[overwrite,nosearch,noheader]{hamlet.md}
# The Tragedy of Hamlet
Shakespeare's longest play.
@@ -5105,7 +5138,7 @@ Hamlet dies.
## Act I {#act-1}
Hamlet talks to ghost.
-\end{filecontents*}
+\end{filecontents}
\begin{document}
\markdownInput[slice=^ ^act-3]{hamlet.md}
\markdownInput[slice=act-1]{hamlet.md}
@@ -5328,7 +5361,9 @@ following content:
\usepackage{markdown}
\markdownSetup{
renderers = {
- ellipsis = {. . .} }}
+ ellipsis = {. . .}
+ }
+}
\begin{document}
\begin{markdown}
@@ -5520,6 +5555,109 @@ defaultOptions.startNumber = true
%</lua,lua-cli>
%<*manual-options>
+#### Option `stripIndent`
+
+`stripIndent` (default value: `false`)
+
+% \fi
+% \markdownBegin
+%
+% \Optitem[false]{stripIndent}{\opt{true}, \opt{false}}
+%
+: true
+
+ : Strip the minimal indentation of non-blank lines from all
+ lines in a markdown document. Requires that the
+ \Opt{preserveTabs} Lua option is \opt{false}:
+
+ ``` tex
+ \documentclass{article}
+ \usepackage[stripIndent]{markdown}
+ \begin{document}
+ \begin{markdown}
+ Hello *world*!
+ \end{markdown}
+ \end{document}
+ ```````
+
+: false
+
+ : Do not strip any indentation from the lines in a markdown
+ document.
+
+% \markdownEnd
+% \iffalse
+
+##### Plain \TeX{} Example {.unnumbered}
+
+Using a text editor, create a text document named `document.tex` with the
+following content:
+``` tex
+\input markdown
+\def\markdownOptionStripIndent{true}
+\markdownBegin
+ Hello *world*!
+\markdownEnd
+\bye
+```````
+Next, invoke LuaTeX from the terminal:
+``` sh
+luatex document.tex
+``````
+A PDF document named `document.pdf` should be produced and contain the text
+“Hello *world*!”
+
+##### \LaTeX{} Example {.unnumbered}
+
+Using a text editor, create a text document named `document.tex` with the
+following content:
+``` tex
+\documentclass{article}
+\usepackage[stripIndent]{markdown}
+\begin{document}
+ \begin{markdown}
+ Hello *world*!
+ \end{markdown}
+\end{document}
+```````
+Next, invoke LuaTeX from the terminal:
+``` sh
+lualatex document.tex
+``````
+A PDF document named `document.pdf` should be produced and contain the
+text “Hello *world*!”
+
+##### \Hologo{ConTeXt} Example {.unnumbered}
+
+Using a text editor, create a text document named `document.tex` with the
+following content:
+``` tex
+\usemodule[t][markdown]
+\def\markdownOptionStripIndent{true}
+\starttext
+ \startmarkdown
+ Hello *world*!
+ \stopmarkdown
+\stoptext
+````````
+Next, invoke LuaTeX from the terminal:
+``` sh
+context document.tex
+`````
+A PDF document named `document.pdf` should be produced and contain the
+text “Hello *world*!”
+
+%</manual-options>
+%<*lua,lua-cli>
+% \fi
+% \begin{macrocode}
+defaultOptions.stripIndent = false
+% \end{macrocode}
+% \par
+% \iffalse
+%</lua,lua-cli>
+%<*manual-options>
+
#### Option `tableCaptions`
`tableCaptions` (default value: `false`)
@@ -5634,6 +5772,110 @@ defaultOptions.tableCaptions = false
%</lua,lua-cli>
%<*manual-options>
+#### Option `texComments`
+
+`texComments` (default value: `false`)
+
+% \fi
+% \markdownBegin
+%
+% \Optitem[false]{texComments}{\opt{true}, \opt{false}}
+%
+: true
+
+ : Strip \TeX{}-style comments.
+
+ ``` tex
+ \documentclass{article}
+ \usepackage[texComments]{markdown}
+ \begin{document}
+ \begin{markdown}
+ Hel% this is a comment
+ lo *world*!
+ \end{markdown}
+ \end{document}
+ ```````
+
+: false
+
+ : Do not strip \TeX{}-style comments.
+
+% \markdownEnd
+% \iffalse
+
+##### Plain \TeX{} Example {.unnumbered}
+
+Using a text editor, create a text document named `document.tex` with the
+following content:
+``` tex
+\input markdown
+\def\markdownOptionTeXComments{true}
+\markdownBegin
+Hel% this is a comment
+ lo *world*!
+\markdownEnd
+\bye
+```````
+Next, invoke LuaTeX from the terminal:
+``` sh
+luatex document.tex
+``````
+A PDF document named `document.pdf` should be produced and contain the text
+“Hello *world*!”
+
+##### \LaTeX{} Example {.unnumbered}
+
+Using a text editor, create a text document named `document.tex` with the
+following content:
+``` tex
+\documentclass{article}
+\usepackage[texComments]{markdown}
+\begin{document}
+\begin{markdown}
+Hel% this is a comment
+ lo *world*!
+\end{markdown}
+\end{document}
+```````
+Next, invoke LuaTeX from the terminal:
+``` sh
+lualatex document.tex
+``````
+A PDF document named `document.pdf` should be produced and contain the
+text “Hello *world*!”
+
+##### \Hologo{ConTeXt} Example {.unnumbered}
+
+Using a text editor, create a text document named `document.tex` with the
+following content:
+``` tex
+\usemodule[t][markdown]
+\def\markdownOptionTeXComments{true}
+\starttext
+\startmarkdown
+Hel% this is a comment
+ lo *world*!
+\stopmarkdown
+\stoptext
+````````
+Next, invoke LuaTeX from the terminal:
+``` sh
+context document.tex
+`````
+A PDF document named `document.pdf` should be produced and contain the
+text “Hello *world*!”
+
+%</manual-options>
+%<*lua,lua-cli>
+% \fi
+% \begin{macrocode}
+defaultOptions.texComments = false
+% \end{macrocode}
+% \par
+% \iffalse
+%</lua,lua-cli>
+%<*manual-options>
+
#### Option `tightLists`
@@ -6096,8 +6338,8 @@ pdftex --shell-escape document.tex
%
% \end{markdown}
% \begin{macrocode}
-\def\markdownLastModified{2020/09/14}%
-\def\markdownVersion{2.9.0}%
+\def\markdownLastModified{2021/08/06}%
+\def\markdownVersion{2.10.0}%
% \end{macrocode}
% \par
% \begin{markdown}
@@ -6483,7 +6725,9 @@ bug](https://github.com/witiko/markdown/issues).
\let\markdownOptionSlice\undefined
\let\markdownOptionSmartEllipses\undefined
\let\markdownOptionStartNumber\undefined
+\let\markdownOptionStripIndent\undefined
\let\markdownOptionTableCaptions\undefined
+\let\markdownOptionTeXComments\undefined
\let\markdownOptionTightLists\undefined
% \end{macrocode}
% \par
@@ -7062,7 +7306,7 @@ Next, invoke LuaTeX from the terminal:
luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
-following text, where the middot (`·`) denotes a non-breakable space:
+following text, where the middot (`·`) denotes a non-breaking space:
> Bartel·Leendert van·der·Waerden
@@ -7090,7 +7334,7 @@ Next, invoke LuaTeX from the terminal:
lualatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
-following text, where the middot (`·`) denotes a non-breakable space:
+following text, where the middot (`·`) denotes a non-breaking space:
> Bartel·Leendert van·der·Waerden
@@ -7113,7 +7357,7 @@ Next, invoke LuaTeX from the terminal:
context document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
-following text, where the middot (`·`) denotes a non-breakable space:
+following text, where the middot (`·`) denotes a non-breaking space:
> Bartel·Leendert van·der·Waerden
@@ -7619,12 +7863,11 @@ Using a text editor, create a text document named `document.tex` with the
following content:
``` tex
\documentclass{article}
-\usepackage{filecontents}
-\begin{filecontents*}{markdown-languages.json}
+\begin{filecontents}[overwrite,nosearch,noheader]{markdown-languages.json}
{
"json": "JavaScript Object Notation",
}
-\end{filecontents*}
+\end{filecontents}
\usepackage[contentBlocks]{markdown}
\markdownSetup{
renderers = {
@@ -8171,7 +8414,8 @@ an ordered list. The macro receives no arguments.
The \mdef{markdownRendererOlItemWithNumber} macro represents an item in an
ordered list. This macro will only be produced, when the \Opt{startNumber}
-option is enabled. The macro receives no arguments.
+option is enabled. The macro receives a single numeric argument that
+corresponds to the item number.
% \end{markdown}
%
@@ -9997,7 +10241,7 @@ A PDF document named `document.pdf` should be produced and contain the
following text:
> This is a parenthesized citation *abrahams90* with a prefix see
-> and a postfix *pp. > 12*, and a citation *eijkhout91* with a
+> and a postfix *pp. 12*, and a citation *eijkhout91* with a
> postfix *pp. 34*.
%</manual-tokens>
@@ -10180,8 +10424,6 @@ following content:
: Demonstration of pipe table syntax
\end{markdown}
\end{document}
-\end{markdown}
-\end{document}
```````
Next, invoke LuaTeX from the terminal:
``` sh
@@ -10220,6 +10462,69 @@ following text:
%
% \begin{markdown}
+#### Inline HTML Comment Renderer
+The \mdef{markdownRendererInlineHtmlComment} macro represents the contents of an
+inline \acro{HTML} comment. This macro will only be produced, when the
+\Opt{html} option is enabled. The macro receives a single argument that
+corresponds to the contents of the \acro{HTML} comment.
+
+% \end{markdown}
+%
+% \iffalse
+
+##### \LaTeX{} Example {.unnumbered}
+
+Using a text editor, create a text document named `document.tex` with the
+following content:
+``` tex
+\documentclass{article}
+\usepackage[html]{markdown}
+\usepackage{marginnote}
+\markdownSetup{
+ renderers = {
+ inlineHtmlComment = {\marginnote{#1}},
+ },
+}
+\begin{document}
+\begin{markdown}
+A useful use of HTML comments are side notes.
+<!-- Side notes are displayed in the horizontal margins next to the relevant
+passages, which makes them easier for the reader to find than footnotes. -->
+\end{markdown}
+\end{document}
+```````
+Next, invoke LuaTeX from the terminal:
+``` sh
+lualatex document.tex
+lualatex document.tex
+``````
+A PDF document named `document.pdf` should be produced and contain the
+following body text:
+
+> A useful use of HTML comments are side notes.
+
+The horizontal margins should contain the following text:
+
+> Side notes are displayed in the horizontal margins next to the relevant
+> passages, which makes them easier for the reader to find than footnotes.
+
+%</manual-tokens>
+%<*tex>
+% \fi
+%
+% \begin{macrocode}
+\def\markdownRendererInlineHtmlComment{%
+ \markdownRendererInlineHtmlCommentPrototype}%
+% \end{macrocode}
+% \par
+%
+% \iffalse
+%</tex>
+%<*manual-tokens>
+% \fi
+%
+% \begin{markdown}
+
### Token Renderer Prototypes
% \label{sec:texrendererprototypes}
@@ -10410,6 +10715,7 @@ following text:
\def\markdownRendererCitePrototype#1{}%
\def\markdownRendererTextCitePrototype#1{}%
\def\markdownRendererTablePrototype#1#2#3{}%
+\def\markdownRendererInlineHtmlCommentPrototype#1{}%
% \end{macrocode}
% \par
% \begin{markdown}
@@ -10418,27 +10724,9 @@ following text:
% The \mdef{markdownInfo}, \mdef{markdownWarning}, and \mdef{markdownError}
% macros perform logging for the Markdown package. Their first argument
% specifies the text of the info, warning, or error message.
-%
-% \end{markdown}
-% \begin{macrocode}
-\def\markdownInfo#1{}%
-\def\markdownWarning#1{}%
-% \end{macrocode}
-% \par
-% \begin{markdown}
-%
% The \m{markdownError} macro receives a second argument that provides a help
-% text.
-%
-% \end{markdown}
-% \begin{macrocode}
-\def\markdownError#1#2{}%
-% \end{macrocode}
-% \par
-% \begin{markdown}
-%
-% You may redefine these macros to redirect and process the info, warning, and
-% error messages.
+% text. You may redefine these macros to redirect and process the info,
+% warning, and error messages.
%
%### Miscellanea
% The \mdef{markdownMakeOther} macro is used by the package, when a \TeX{}
@@ -10677,31 +10965,566 @@ pdflatex --shell-escape document.tex
%
% The \LaTeX{} options are represented by a comma-delimited list of
% \meta{key}`=`\meta{value} pairs. For boolean options, the `=`\meta{value}
-% part is optional, and \meta{key} will be interpreted as \meta{key}`=true`.
+% part is optional, and \meta{key} will be interpreted as \meta{key}`=true`
+% if the `=`\meta{value} part has been omitted.
%
-% The \LaTeX{} options map directly to the options recognized by the plain
+% Except for the `plain` option described in Section \ref{sec:latexplain}, and
+% the \LaTeX{} themes described in Section \ref{sec:latexthemes}, and the
+% \LaTeX{} setup snippets described in Section \ref{sec:latexsetupsnippets},
+% \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.
+% The \LaTeX{} options may be specified when loading the \LaTeX{} package, when
+% using the \envm{markdown*} \LaTeX{} environment or the \m{markdownInput}
+% macro (see Section \ref{sec:latexinterface}), or via the \mdef{markdownSetup}
+% macro. The \m{markdownSetup} macro receives the options to set up as its only
+% argument:
%
% \markdownEnd
% \begin{macrocode}
\newcommand\markdownSetup[1]{%
\setkeys{markdownOptions}{#1}}%
% \end{macrocode}
+% \begin{markdown}
+%
+% We may also store \LaTeX{} options as *setup snippets* and invoke them later
+% using the \mdef{markdownSetupSnippet} macro. The \m{markdownSetupSnippet}
+% macro receives two arguments: the name of the setup snippet and the options
+% to store:
+%
+% \end{markdown}
+% \begin{macrocode}
+\newcommand\markdownSetupSnippet[2]{%
+ \@ifundefined
+ {markdownLaTeXSetupSnippet\markdownLaTeXThemeName#1}{%
+ \newtoks\next
+ \next={#2}%
+ \expandafter\let\csname markdownLaTeXSetupSnippet%
+ \markdownLaTeXThemeName#1\endcsname=\next
+ }{%
+ \markdownWarning
+ {Redefined setup snippet \markdownLaTeXThemeName#1}%
+ \csname markdownLaTeXSetupSnippet%
+ \markdownLaTeXThemeName#1\endcsname={#2}%
+ }}%
+% \end{macrocode}
+% \begin{markdown}
+%
+% See Section \ref{sec:latexthemes} for information on interactions between
+% setup snippets and \LaTeX{} themes. See Section \ref{sec:latexsetupsnippets}
+% for information about invoking the stored setup snippets.
+%
+% \end{markdown}
+% \iffalse
+%</latex>
+%<*manual-options>
+
+### \LaTeX{}
+
+\LaTeX{} options allow us to disable the redefinition of the default renderer
+prototypes from plain \TeX{}, load user-contributed themes, and invoke
+user-defined setup snippets.
+
+% \fi
% \par
% \begin{markdown}
+
+#### No default token renderer prototypes
+% \label{sec:latexplain}
+Default token renderer prototypes require \LaTeX{} packages that may clash with
+other packages used in a document. Additionally, if we redefine token
+renderers and renderer prototypes ourselves, the default definitions will bring
+no benefit to us. Using the `plain` package option, we can keep the default
+definitions from the plain \TeX{} implementation (see Section
+\ref{sec:textokenrendererprototypes}) and prevent the soft \LaTeX{}
+prerequisites in Section \ref{sec:latexprerequisites} from being loaded:
+
+``` tex
+\usepackage[plain]{markdown}
+```````
+
+% \end{markdown}
+% \iffalse
+%</manual-options>
+%<*latex>
+% \fi
+% \begin{macrocode}
+\newif\ifmarkdownLaTeXPlain
+ \markdownLaTeXPlainfalse
+\define@key{markdownOptions}{plain}[true]{%
+ \ifmarkdownLaTeXLoaded
+ \markdownWarning
+ {The plain option must be specified when loading the package}%
+ \else
+ \markdownLaTeXPlaintrue
+ \fi}
+% \end{macrocode}
+% \iffalse
+%</latex>
+%<*manual-options>
+% \fi
+% \begin{markdown}
+
+#### \LaTeX{} themes
+% \label{sec:latexthemes}
+
+User-contributed \LaTeX{} themes for the Markdown package provide a
+domain-specific interpretation of some Markdown tokens. Similarly to \LaTeX{}
+packages, themes allow the authors to achieve a specific look and other
+high-level goals without low-level programming.
+
+% The \LaTeX{} option with key `theme` loads a \LaTeX{} package (further
+% referred to as *a theme*) named `markdowntheme`\meta{munged theme
+% name}`.sty`, where the *munged theme name* is the *theme name* after a
+% substitution of all forward slashes (\texttt{/}) for an underscore
+% (\texttt{_}), the theme name is a value that is *qualified* and contains no
+% underscores, and a value is qualified if and only if it contains at least one
+% forward slash. Themes are inspired by the Beamer \LaTeX{} package, which
+% provides similar functionality with its \m{usetheme} macro [@tantau21,
+% Section 15.1].
+%
+% Theme names must be qualified to minimize naming conflicts between different
+% themes intended for a single \LaTeX{} document class or for a single \LaTeX{}
+% package. The preferred format of a theme name is \meta{theme author}`/`<!--
+% -->\meta{target \LaTeX{} document class or package}`/`\meta{private naming
+% scheme}, where the *private naming scheme* may contain additional forward
+% slashes. For example, a theme by a user `witiko` for the MU theme of the
+% Beamer document class may have the name `witiko/beamer/MU`.
+%
+% Theme names are munged, because \LaTeX{} packages are identified only by
+% their filenames, not by their pathnames. [@novotny21] Therefore, we can't
+% store the qualified theme names directly using directories, but we must
+% encode the individual segments of the qualified theme in the filename. For
+% example, loading a theme named `witiko/beamer/MU` would load a \LaTeX{}
+% package named `markdownthemewitiko_beamer_MU.sty`.
+%
+% If the \LaTeX{} option with key `theme` is (repeatedly) specified in the
+% \m{usepackage} macro, the loading of the theme(s) will be postponed in
+% first-in-first-out order until after the Markdown \LaTeX{} package has been
+% loaded. Otherwise, the theme(s) will be loaded immediately. For example,
+% there is a theme named `witiko/dot`, which typesets fenced code blocks with
+% the `dot` infostring as images of directed graphs rendered by the Graphviz
+% tools. The following code would first load the Markdown package, then the
+% `markdownthemewitiko_beamer_MU.sty` \LaTeX{} package, and finally the
+% `markdownthemewitiko_dot.sty` \LaTeX{} package:
+% \end{markdown}
+% \iffalse
+
+For example, to load themes named `witiko/beamer/MU` and `witiko/dot`, you
+would use the following code in the preamble of your document:
+
+% \fi
+% \par
+% \begin{markdown}
+
+``` tex
+\usepackage[
+ theme = witiko/beamer/MU,
+ theme = witiko/dot,
+]{markdown}
+```````
+
+% \end{markdown}
+% \iffalse
+%</manual-options>
+%<*latex>
+% \fi
+% \begin{macrocode}
+\newif\ifmarkdownLaTeXLoaded
+ \markdownLaTeXLoadedfalse
+\AtEndOfPackage{\markdownLaTeXLoadedtrue}%
+\define@key{markdownOptions}{theme}{%
+ \IfSubStr{#1}{/}{}{%
+ \markdownError
+ {Won't load theme with unqualified name #1}%
+ {Theme names must contain at least one forward slash}}%
+ \StrSubstitute{#1}{/}{_}[\markdownLaTeXThemePackageName]%
+ \edef\markdownLaTeXThemePackageName{%
+ markdowntheme\markdownLaTeXThemePackageName}%
+ \expandafter\markdownLaTeXThemeLoad\expandafter{%
+ \markdownLaTeXThemePackageName}{#1/}}%
+\newcommand\markdownLaTeXThemeName{}%
+\newcommand\markdownLaTeXThemeLoad[2]{%
+ \ifmarkdownLaTeXLoaded
+ \def\markdownLaTeXThemeName{#2}%
+ \RequirePackage{#1}%
+ \def\markdownLaTeXThemeName{}%
+ \else
+ \AtEndOfPackage{%
+ \def\markdownLaTeXThemeName{#2}%
+ \RequirePackage{#1}%
+ \def\markdownLaTeXThemeName{}}%
+ \fi}%
+% \end{macrocode}
+% \begin{markdown}
%
+% The \LaTeX{} themes have a useful synergy with the setup snippets (see
+% Section \ref{sec:latexoptions}): To make it less likely that different
+% themes will define setup snippets with the same name, we will prepend
+% \meta{theme name}`/` before the snippet name and use the result as the
+% snippet name. For example, if the `witiko/dot` theme defines the `product`
+% setup snippet, the setup snippet will be available under the name
+% `witiko/dot/product`.
+%
+% \end{markdown}
+% \iffalse
+%</latex>
+%<*manual-options>
+% \fi
+% \begin{markdown}
+
+Due to limitations of \LaTeX{}, themes may not be loaded after the
+beginning of a \LaTeX{} document.
+
+% \end{markdown}
+% \iffalse
+%</manual-options>
+%<*latex>
+% \fi
+% \begin{macrocode}
+\@onlypreamble\KV@markdownOptions@theme
+% \end{macrocode}
+% \iffalse
+%</latex>
+%<*manual-options>
+% \fi
+% \par
+% \markdownBegin
+
+Example themes provided with the Markdown package include:
+
+\pkg{witiko/dot}
+
+: A theme that typesets fenced code blocks with the `dot …` infostring
+ as images of directed graphs rendered by the Graphviz tools. The
+ right tail of the infostring is used as the image title.
+% ```` tex
+% \documentclass{article}
+% \usepackage[theme=witiko/dot]{markdown}
+% \setkeys{Gin}{
+% width = \columnwidth,
+% height = 0.65\paperheight,
+% keepaspectratio}
+% \begin{document}
+% \begin{markdown}
+% ``` dot Various formats of mathemathical formulae
+% digraph tree {
+% margin = 0;
+% rankdir = "LR";
+%
+% latex -> pmml;
+% latex -> cmml;
+% pmml -> slt;
+% cmml -> opt;
+% cmml -> prefix;
+% cmml -> infix;
+% pmml -> mterms [style=dashed];
+% cmml -> mterms;
+%
+% latex [label = "LaTeX"];
+% pmml [label = "Presentation MathML"];
+% cmml [label = "Content MathML"];
+% slt [label = "Symbol Layout Tree"];
+% opt [label = "Operator Tree"];
+% prefix [label = "Prefix"];
+% infix [label = "Infix"];
+% mterms [label = "M-Terms"];
+% }
+% ```
+% \end{markdown}
+% \end{document}
+% ````````
+% Typesetting the above document produces the output shown in
+% Figure~\ref{fig:witiko/dot}.
+% ``` dot Various formats of mathemathical formulae \label{fig:witiko/dot}
+% digraph tree {
+% margin = 0;
+% rankdir = "LR";
+%
+% latex -> pmml;
+% latex -> cmml;
+% pmml -> slt;
+% cmml -> opt;
+% cmml -> prefix;
+% cmml -> infix;
+% pmml -> mterms [style=dashed];
+% cmml -> mterms;
+%
+% latex [label = "LaTeX"];
+% pmml [label = "Presentation MathML"];
+% cmml [label = "Content MathML"];
+% slt [label = "Symbol Layout Tree"];
+% opt [label = "Operator Tree"];
+% prefix [label = "Prefix"];
+% infix [label = "Infix"];
+% mterms [label = "M-Terms"];
+% }
+% ```
+ The theme requires a Unix-like operating system with GNU Diffutils and
+ Graphviz installed. The theme also requires shell access unless the
+ \m{markdownOptionFrozenCache} plain \TeX{} option is enabled.
+
+% \markdownEnd
+% \iffalse
+
+##### \LaTeX{} Example {.unnumbered}
+
+Using a text editor, create a text document named `document.tex` with the
+following content:
+```` tex
+\documentclass{article}
+\usepackage[theme=witiko/dot]{markdown}
+\setkeys{Gin}{
+ width=\columnwidth,
+ height=0.65\paperheight,
+ keepaspectratio}
+\begin{document}
+\begin{markdown}
+``` dot Various formats of mathemathical formulae
+digraph tree {
+ margin = 0;
+ rankdir = "LR";
+
+ latex -> pmml;
+ latex -> cmml;
+ pmml -> slt;
+ cmml -> opt;
+ cmml -> prefix;
+ cmml -> infix;
+ pmml -> mterms [style=dashed];
+ cmml -> mterms;
+
+ latex [label = "LaTeX"];
+ pmml [label = "Presentation MathML"];
+ cmml [label = "Content MathML"];
+ slt [label = "Symbol Layout Tree"];
+ opt [label = "Operator Tree"];
+ prefix [label = "Prefix"];
+ infix [label = "Infix"];
+ mterms [label = "M-Terms"];
+}
+```
+\end{markdown}
+\end{document}
+````````
+Next, invoke LuaTeX from the terminal:
+``` sh
+lualatex document.tex
+``````
+A PDF document named `document.pdf` should be produced and contain
+a drawing of a directed graph similar to Figure 1 from the following
+conference article:
+
+> NOVOTNÝ, Vít, Petr SOJKA, Michal ŠTEFÁNIK and Dávid LUPTÁK. Three is Better
+> than One: Ensembling Math Information Retrieval Systems. *CEUR Workshop
+> Proceedings*. Thessaloniki, Greece: M. Jeusfeld c/o Redaktion Sun SITE,
+> Informatik V, RWTH Aachen., 2020, vol. 2020, No 2696, p. 1-30. ISSN 1613-0073.
+> <http://ceur-ws.org/Vol-2696/paper_235.pdf>
+
+%</manual-options>
+%<*latex-themes-witiko-dot>
+% \fi
+% \begin{macrocode}
+\ProvidesPackage{markdownthemewitiko_dot}[2021/03/09]%
+% \end{macrocode}
+% \iffalse
+%</latex-themes-witiko-dot>
+%<*manual-options>
+% \fi
+% \par
+% \markdownBegin
+
+\pkg{witiko/graphicx/http}
+
+: A theme that adds support for downloading images whose URL has the
+ http or https protocol.
+% ``` tex
+% \documentclass{article}
+% \usepackage[theme=witiko/graphicx/http]{markdown}
+% \begin{document}
+% \begin{markdown}
+% ![img](https://github.com/witiko/markdown/raw/master/banner.png
+% "The banner of the Markdown package")
+% \end{markdown}
+% \end{document}
+% ```````
+% Typesetting the above document produces the output shown in
+% Figure~\ref{fig:witiko/graphicx/http}.
+% ![img](https://github.com/witiko/markdown/raw/master/banner.png
+% "The banner of the Markdown package \label{fig:witiko/graphicx/http}")
+ The theme requires the \pkg{catchfile} \LaTeX{} package and a Unix-like
+ operating system with GNU Coreutils `md5sum` and either GNU Wget or cURL
+ installed. The theme also requires shell access unless the
+ \m{markdownOptionFrozenCache} plain \TeX{} option is enabled.
+
+% \markdownEnd
+% \iffalse
+
+##### \LaTeX{} Example {.unnumbered}
+
+Using a text editor, create a text document named `document.tex` with the
+following content:
+``` tex
+\documentclass{article}
+\usepackage[theme=witiko/graphicx/http]{markdown}
+\begin{document}
+\begin{markdown}
+![img](https://github.com/witiko/markdown/raw/master/banner.png
+ "The banner of the Markdown package")
+\end{markdown}
+\end{document}
+```````
+Next, invoke LuaTeX from the terminal:
+``` sh
+lualatex document.tex
+``````
+A PDF document named `document.pdf` should be produced and contain the
+following image:
+
+> ![img](https://github.com/witiko/markdown/raw/master/banner.png
+> "The banner of the Markdown package")
+
+%</manual-options>
+%<*latex-themes-witiko-graphicx-http>
+% \fi
+% \begin{macrocode}
+\ProvidesPackage{markdownthemewitiko_graphicx_http}[2021/03/22]%
+\RequirePackage{catchfile}
+% \end{macrocode}
+% \iffalse
+%</latex-themes-witiko-graphicx-http>
+%<*manual-options>
+% \fi
+% \par
+% \markdownBegin
+
+\pkg{witiko/tilde}
+
+: A theme that makes tilde (`~`) always typeset the non-breaking space even
+ when the \Opt{hybrid} Lua option is `false`.
+% ``` tex
+% \documentclass{article}
+% \usepackage[theme=witiko/tilde]{markdown}
+% \begin{document}
+% \begin{markdown}
+% Bartel~Leendert van~der~Waerden
+% \end{markdown}
+% \end{document}
+% ```````
+% Typesetting the above document produces the following text:
+% “Bartel~Leendert van~der~Waerden”.
+%
+% \markdownEnd
+% \iffalse
+
+##### \LaTeX{} Example {.unnumbered}
+
+Using a text editor, create a text document named `document.tex` with the
+following content:
+``` tex
+\documentclass{article}
+\usepackage[theme=witiko/tilde]{markdown}
+\begin{document}
+\begin{markdown}
+Bartel~Leendert van~der~Waerden
+\end{markdown}
+\end{document}
+```````
+Next, invoke LuaTeX from the terminal:
+``` sh
+lualatex document.tex
+``````
+A PDF document named `document.pdf` should be produced and contain the
+following text, where the middot (`·`) denotes a non-breaking space:
+
+> Bartel·Leendert van·der·Waerden
+
+%</manual-options>
+%<*latex-themes-witiko-tilde>
+% \fi
+% \begin{macrocode}
+\ProvidesPackage{markdownthemewitiko_tilde}[2021/03/22]%
+% \end{macrocode}
+% \iffalse
+%</latex-themes-witiko-tilde>
+%<*manual-options>
+% \fi
+% \par
+% \begin{markdown}
+%
+% Please, see Section~\ref{sec:latexthemesimplementation} for implementation
+% details of the example themes.
+
+#### \LaTeX{} setup snippets
+% \label{sec:latexsetupsnippets}
+% \end{markdown}
+% \iffalse
+
+User-contributed \LaTeX{} themes provide global control over high-level goals.
+However, it is often desirable to change only some local aspects of a document.
+\LaTeX{} setup snippets provide syntactic sugar for defining and invoking
+various options locally.
+
+% \fi
+% \begin{markdown}
+% The \LaTeX{} option with key `snippet` invokes a snippet named \meta{value}:
+% \end{markdown}
+% \iffalse
+%</manual-options>
+%<*latex>
+% \fi
+% \begin{macrocode}
+\define@key{markdownOptions}{snippet}{%
+ \expandafter\markdownSetup\expandafter{%
+ \the\csname markdownLaTeXSetupSnippet#1\endcsname}}%
+% \end{macrocode}
+% \iffalse
+%</latex>
+%<*manual-options>
+% \fi
+% \par
+% \markdownBegin
+
+Here is how we can use setup snippets to store options and invoke them later:
+
+``` tex
+\markdownSetupSnippet{romanNumerals}{
+ renderers = {
+ olItemWithNumber = {%
+ \item[\romannumeral#1\relax.]%
+ },
+ },
+}
+\begin{markdown}
+
+The following ordered list will be preceded by arabic numerals:
+
+1. wahid
+2. aithnayn
+
+\end{markdown}
+\begin{markdown*}{snippet=romanNumerals}
+
+The following ordered list will be preceded by roman numerals:
+
+3. tres
+4. quattuor
+
+\end{markdown*}
+
+```````
+
%#### Plain \TeX{} Interface Options
% The following options map directly to the option macros exposed by the plain
% \TeX{} interface (see Section \ref{sec:texoptions}).
%
-% \end{markdown}
+% \markdownEnd
+% \iffalse
+%</manual-options>
+%<*latex>
+% \fi
% \begin{macrocode}
\define@key{markdownOptions}{helperScriptFileName}{%
\def\markdownOptionHelperScriptFileName{#1}}%
@@ -10761,8 +11584,12 @@ pdflatex --shell-escape document.tex
\def\markdownOptionSlice{#1}}%
\define@key{markdownOptions}{startNumber}[true]{%
\def\markdownOptionStartNumber{#1}}%
+\define@key{markdownOptions}{stripIndent}[true]{%
+ \def\markdownOptionStripIndent{#1}}%
\define@key{markdownOptions}{tableCaptions}[true]{%
\def\markdownOptionTableCaptions{#1}}%
+\define@key{markdownOptions}{texComments}[true]{%
+ \def\markdownOptionTeXComments{#1}}%
\define@key{markdownOptions}{tightLists}[true]{%
\def\markdownOptionTightLists{#1}}%
\define@key{markdownOptions}{underscores}[true]{%
@@ -10949,6 +11776,8 @@ pdflatex --shell-escape document.tex
\renewcommand\markdownRendererTextCite[1]{#1}}%
\define@key{markdownRenderers}{table}{%
\renewcommand\markdownRendererTable[3]{#1}}%
+\define@key{markdownRenderers}{inlineHtmlComment}{%
+ \renewcommand\markdownRendererInlineHtmlComment[1]{#1}}%
% \end{macrocode}
% \par
% \begin{markdown}
@@ -11092,6 +11921,8 @@ pdflatex --shell-escape document.tex
\renewcommand\markdownRendererTextCitePrototype[1]{#1}}%
\define@key{markdownRendererPrototypes}{table}{%
\renewcommand\markdownRendererTablePrototype[3]{#1}}%
+\define@key{markdownRendererPrototypes}{inlineHtmlComment}{%
+ \renewcommand\markdownRendererInlineHtmlCommentPrototype[1]{#1}}%
% \end{macrocode}
% \par
% \begin{markdown}
@@ -11175,6 +12006,7 @@ texexec --passon=--shell-escape document.tex
% \end{markdown}
% \begin{macrocode}
\writestatus{loading}{ConTeXt User Module / markdown}%
+\startmodule[markdown]
\unprotect
% \end{macrocode}
% \par
@@ -11294,7 +12126,8 @@ function util.cache(dir, string, salt, transform, suffix)
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 file = assert(io.open(name, "w"),
+ [[could not open file "]] .. name .. [[" for writing]])
local result = string
if transform ~= nil then
result = transform(result)
@@ -13671,7 +14504,8 @@ M.writer = {}
%
% 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 `writer->`\meta{member}.
+% \meta{member}s as `writer->`\meta{member}. All member variables are
+% immutable unless explicitly stated otherwise.
%
% \end{markdown}
% \begin{macrocode}
@@ -13693,7 +14527,8 @@ function M.writer.new(options)
% \begin{markdown}
%
% Parse the \Opt{slice} option and define \luamdef{writer->slice\_begin}
-% \luamdef{writer->slice\_end}, and \luamdef{writer->is\_writing}.
+% \luamdef{writer->slice\_end}, and \luamdef{writer->is\_writing}. The
+% \luam{writer->is\_writing} member variable is mutable.
%
% \end{markdown}
% \begin{macrocode}
@@ -13847,9 +14682,10 @@ function M.writer.new(options)
% Define a table \luamdef{escaped_chars} containing the mapping from special
% plain \TeX{} characters (including the active pipe character (`|`) 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.
+% \luamdef{escaped_uri_chars}, \luamdef{escaped_citation_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.
%
% \end{markdown}
% \begin{macrocode}
@@ -13886,10 +14722,10 @@ function M.writer.new(options)
% \par
% \begin{markdown}
%
-% 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}.
+% Use the \luam{escaped_chars}, \luam{escaped_uri_chars},
+% \luam{escaped_citation_chars}, and \luam{escaped_minimal_strings} tables
+% to create the \luamdef{escape}, \luamdef{escape_citation}, and
+% \luamdef{escape_uri} escaper functions.
%
% \end{markdown}
% \begin{macrocode}
@@ -13902,10 +14738,12 @@ function M.writer.new(options)
% \begin{markdown}
%
% Define \luamdef{writer->string} as a function that will transform an input
-% plain text span `s` to the output format and \luamdef{writer->uri} as a
-% function that will transform an input \acro{uri} `u` to the output format.
-% If the \Opt{hybrid} option is enabled, use identity functions. Otherwise,
-% use the \luam{escape} and \luam{escape_minimal} functions.
+% plain text span `s` to the output format, \luamdef{writer->citation} as a
+% function that will transform an input citation name `c` to the output format,
+% and \luamdef{writer->uri} as a function that will transform an input
+% \acro{uri} `u` to the output format. If the \Opt{hybrid} option is enabled,
+% use identity functions. Otherwise, use the \luam{escape},
+% \luam{escape_citation}, and \luam{escape_uri} functions.
%
% \end{markdown}
% \begin{macrocode}
@@ -13922,13 +14760,25 @@ function M.writer.new(options)
% \par
% \begin{markdown}
%
+% Define \luamdef{writer->escape} as a function that will transform an input
+% plain text span to the output format. Unlike the \luam{writer->string}
+% function, \luam{writer->escape} always uses the \luam{escape} function,
+% even when the \Opt{hybrid} option is enabled.
+%
+% \end{markdown}
+% \begin{macrocode}
+ self.escape = escape
+% \end{macrocode}
+% \par
+% \begin{markdown}
+%
% Define \luamdef{writer->code} as a function that will transform an input
% inlined code span `s` to the output format.
%
% \end{markdown}
% \begin{macrocode}
function self.code(s)
- return {"\\markdownRendererCodeSpan{",escape(s),"}"}
+ return {"\\markdownRendererCodeSpan{",self.escape(s),"}"}
end
% \end{macrocode}
% \par
@@ -13942,7 +14792,7 @@ function M.writer.new(options)
% \begin{macrocode}
function self.link(lab,src,tit)
return {"\\markdownRendererLink{",lab,"}",
- "{",self.string(src),"}",
+ "{",self.escape(src),"}",
"{",self.uri(src),"}",
"{",self.string(tit or ""),"}"}
end
@@ -13957,6 +14807,7 @@ function M.writer.new(options)
% \end{markdown}
% \begin{macrocode}
function self.table(rows, caption)
+ if not self.is_writing then return "" end
local buffer = {"\\markdownRendererTable{",
caption or "", "}{", #rows - 1, "}{", #rows[1], "}"}
local temp = rows[2] -- put alignments on the first row
@@ -13970,10 +14821,10 @@ function M.writer.new(options)
end
table.insert(buffer, column)
if i > 1 then
- table.insert(buffer, "}%\n")
+ table.insert(buffer, "}")
end
end
- table.insert(buffer, "}%\n")
+ table.insert(buffer, "}")
end
return buffer
end
@@ -13999,20 +14850,34 @@ function M.writer.new(options)
%
% The \luamdef{languages_json} table maps programming language filename
% extensions to fence infostrings. All \luam{options.contentBlocksLanguageMap}
-% files located by \pkg{kpathsea} are loaded into a chain of tables.
+% files located by the KPathSea library are loaded into a chain of tables.
% \luam{languages_json} corresponds to the first table and is chained with
% the rest via Lua metatables.
%
% \end{markdown}
% \begin{macrocode}
local languages_json = (function()
- local kpse = require("kpse")
- kpse.set_program_name("luatex")
+ local ran_ok, kpse = pcall(require, "kpse")
+ if ran_ok then
+ kpse.set_program_name("luatex")
+% \end{macrocode}
+% \begin{markdown}
+%
+% If the KPathSea library is unavailable, perhaps because we are using
+% LuaMeta\TeX, we will only locate the \luam{options.contentBlocksLanguageMap}
+% in the current working directory:
+%
+% \end{markdown}
+% \begin{macrocode}
+ else
+ kpse = {lookup=function(filename, options) return filename end}
+ end
local base, prev, curr
- for _, file in ipairs{kpse.lookup(options.contentBlocksLanguageMap,
- { all=true })} do
- json = io.open(file, "r"):read("*all")
- :gsub('("[^\n]-"):','[%1]=')
+ for _, filename in ipairs{kpse.lookup(options.contentBlocksLanguageMap,
+ { all=true })} do
+ local file = io.open(filename, "r")
+ if not file then goto continue end
+ json = file:read("*all"):gsub('("[^\n]-"):','[%1]=')
curr = (function()
local _ENV={ json=json, load=load } -- run in sandbox
return load("return "..json)()
@@ -14025,6 +14890,7 @@ function M.writer.new(options)
end
prev = curr
end
+ ::continue::
end
return base or {}
end)()
@@ -14137,15 +15003,15 @@ function M.writer.new(options)
% \par
% \begin{markdown}
%
-% Define \luamdef{writer->inline_html} and \luamdef{writer->display_html}
-% as functions that will transform an inline or block \acro{html} element
-% respectively to the output format, where `html` is the \acro{html}
-% input.
+% Define \luamdef{writer->inline_html_comment} as a function that will
+% transform the contents of an inline \acro{HTML} comment, to the output
+% format, where `contents` are the contents of the \acro{HTML} comment.
%
% \end{markdown}
% \begin{macrocode}
- function self.inline_html(html) return "" end
- function self.display_html(html) return "" end
+ function self.inline_html_comment(contents)
+ return {"\\markdownRendererInlineHtmlComment{",contents,"}"}
+ end
% \end{macrocode}
% \par
% \begin{markdown}
@@ -14255,8 +15121,9 @@ function M.writer.new(options)
% \par
% \begin{markdown}
%
-% Define \luamdef{writer->active_headings} as a stack of identifiers
-% of the headings that are currently active.
+% Define \luamdef{writer->active\_headings} as a stack of identifiers
+% of the headings that are currently active. The
+% \luam{writer->active\_headings} member variable is mutable.
%
% \end{markdown}
% \begin{macrocode}
@@ -14376,6 +15243,57 @@ function M.writer.new(options)
end
return buffer
end
+% \end{macrocode}
+% \par
+% \begin{markdown}
+%
+% Define \luamdef{writer->get_state} as a function that returns the current
+% state of the writer, where the state of a writer are its mutable member
+% variables.
+%
+% \end{markdown}
+% \begin{macrocode}
+ function self.get_state()
+ return {
+ is_writing=self.is_writing,
+ active_headings={table.unpack(self.active_headings)},
+ }
+ end
+% \end{macrocode}
+% \par
+% \begin{markdown}
+%
+% Define \luamdef{writer->set_state} as a function that restores the input
+% state `s` and returns the previous state of the writer.
+%
+% \end{markdown}
+% \begin{macrocode}
+ function self.set_state(s)
+ previous_state = self.get_state()
+ for key, value in pairs(state) do
+ self[key] = value
+ end
+ return previous_state
+ end
+% \end{macrocode}
+% \par
+% \begin{markdown}
+%
+% Define \luamdef{writer->defer_call} as a function that will encapsulate the
+% input function `f`, so that `f` is called with the state of the writer at the
+% time of calling \luam{writer->defer_call}.
+%
+% \end{markdown}
+% \begin{macrocode}
+ function self.defer_call(f)
+ state = self.get_state()
+ return function(...)
+ state = self.set_state(state)
+ local return_value = f(...)
+ self.set_state(state)
+ return return_value
+ end
+ end
return self
end
@@ -14428,6 +15346,7 @@ parsers.semicolon = P(";")
parsers.exclamation = P("!")
parsers.pipe = P("|")
parsers.tilde = P("~")
+parsers.backslash = P("\\")
parsers.tab = P("\t")
parsers.newline = P("\n")
parsers.tightblocksep = P("\001")
@@ -14450,7 +15369,7 @@ parsers.any = P(1)
parsers.fail = parsers.any - 1
parsers.escapable = S("\\`*_{}[]()+_.!<>#-~:^@;")
-parsers.anyescaped = P("\\") / "" * parsers.escapable
+parsers.anyescaped = parsers.backslash / "" * parsers.escapable
+ parsers.any
parsers.spacechar = S("\t ")
@@ -14483,6 +15402,30 @@ parsers.spnl = parsers.optionalspace
parsers.line = parsers.linechar^0 * parsers.newline
parsers.nonemptyline = parsers.line - parsers.blankline
+parsers.commented_line = Cs(((parsers.linechar -- initial
+ - parsers.backslash
+ - parsers.percent)^1
+ + (parsers.backslash -- even backslash
+ * parsers.backslash)^1
+ + (parsers.backslash -- odd backslash
+ * parsers.backslash)^0
+ * (parsers.backslash / ""
+ * parsers.percent
+ + parsers.backslash
+ * (parsers.linechar -- initial
+ + parsers.newline
+ - parsers.backslash
+ - parsers.percent))
+ )^0)
+ * ((parsers.percent -- comment
+ * parsers.line
+ * #parsers.blankline) -- blank line
+ / "\n"
+ + parsers.percent -- comment
+ * parsers.line
+ * parsers.optionalspace -- leading tabs and spaces
+ + C(parsers.newline))
+
parsers.chunk = parsers.line * (parsers.optionallyindentedline
- parsers.blankline)^0
@@ -14959,9 +15902,13 @@ parsers.htmlattribute = parsers.spacing^1
* parsers.sp * parsers.equal * parsers.sp
* parsers.htmlattributevalue
-parsers.htmlcomment = P("<!--") * (parsers.any - P("-->"))^0 * P("-->")
+parsers.htmlcomment = P("<!--")
+ * parsers.optionalspace
+ * Cs((parsers.any - parsers.optionalspace * P("-->"))^0)
+ * parsers.optionalspace
+ * P("-->")
-parsers.htmlinstruction = P("<?") * (parsers.any - P("?>" ))^0 * P("?>" )
+parsers.htmlinstruction = P("<?") * (parsers.any - P("?>"))^0 * P("?>")
parsers.openelt_any = parsers.less * parsers.keyword * parsers.htmlattribute^0
* parsers.sp * parsers.more
@@ -15008,17 +15955,11 @@ end
parsers.in_matched_block_tags = parsers.less
* Cmt(#parsers.openelt_block, parse_matched_tags)
-parsers.displayhtml = parsers.htmlcomment
+parsers.displayhtml = parsers.htmlcomment / ""
+ parsers.emptyelt_block
+ parsers.openelt_exact("hr")
+ parsers.in_matched_block_tags
+ parsers.htmlinstruction
-
-parsers.inlinehtml = parsers.emptyelt_any
- + parsers.htmlcomment
- + parsers.htmlinstruction
- + parsers.openelt_any
- + parsers.closeelt_any
% \end{macrocode}
% \par
% \begin{markdown}
@@ -15204,15 +16145,29 @@ function M.reader.new(writer, options)
% \end{markdown}
% \begin{macrocode}
local function normalize_tag(tag)
- return unicode.utf8.lower(
+ return string.lower(
gsub(util.rope_to_string(tag), "[ \n\r\t]+", " "))
end
% \end{macrocode}
% \par
% \begin{markdown}
%
+% Define \luamdef{iterlines} as a function that iterates over the lines of
+% the input string `s`, transforms them using an input function `f`, and
+% reassembles them into a new string, which it returns.
+%
+% \end{markdown}
+% \begin{macrocode}
+ local function iterlines(s, f)
+ rope = lpeg.match(Ct((parsers.line / f)^1), s)
+ return util.rope_to_string(rope)
+ end
+% \end{macrocode}
+% \par
+% \begin{markdown}
+%
% Define \luamdef{expandtabs} either as an identity function, when the
-% \Opt{preserveTabs} Lua inrerface option is enabled, or to a function that
+% \Opt{preserveTabs} Lua interface option is enabled, or to a function that
% expands tabs into spaces otherwise.
%
% \end{markdown}
@@ -15223,7 +16178,7 @@ function M.reader.new(writer, options)
else
expandtabs = function(s)
if s:find("\t") then
- return s:gsub("[^\n]*", util.expand_tabs_in_line)
+ return iterlines(s, util.expand_tabs_in_line)
else
return s
end
@@ -15248,8 +16203,52 @@ function M.reader.new(writer, options)
%
% \end{markdown}
% \begin{macrocode}
- local function create_parser(name, grammar)
+ local function create_parser(name, grammar, toplevel)
return function(str)
+% \end{macrocode}
+% \par
+% \begin{markdown}
+%
+% If the parser is top-level and the \Opt{stripIndent} Lua option is enabled,
+% we will first expand tabs in the input string `str` into spaces and then we
+% will count the minimum indent across all lines, skipping blank lines. Next,
+% we will remove the minimum indent from all lines.
+%
+% \end{markdown}
+% \begin{macrocode}
+ if toplevel and options.stripIndent then
+ local min_prefix_length, min_prefix = nil, ''
+ str = iterlines(str, function(line)
+ if lpeg.match(parsers.nonemptyline, line) == nil then
+ return line
+ end
+ line = util.expand_tabs_in_line(line)
+ prefix = lpeg.match(C(parsers.optionalspace), line)
+ local prefix_length = #prefix
+ local is_shorter = min_prefix_length == nil
+ is_shorter = is_shorter or prefix_length < min_prefix_length
+ if is_shorter then
+ min_prefix_length, min_prefix = prefix_length, prefix
+ end
+ return line
+ end)
+ str = str:gsub('^' .. min_prefix, '')
+ end
+% \end{macrocode}
+% \par
+% \begin{markdown}
+%
+% If the parser is top-level and the \Opt{texComments} Lua option is
+% enabled, we will strip all plain \TeX{} comments from the input string `str`
+% together with the trailing newline characters.
+%
+% \end{markdown}
+% \begin{macrocode}
+ if toplevel and options.texComments then
+ str = lpeg.match(Ct(parsers.commented_line^1), str)
+ str = util.rope_to_string(str)
+ print(str)
+ end
local res = lpeg.match(grammar(), str)
if res == nil then
error(format("%s failed on:\n%s", name, str:sub(1,20)))
@@ -15263,37 +16262,43 @@ function M.reader.new(writer, options)
= create_parser("parse_blocks",
function()
return larsers.blocks
- end)
+ end, false)
local parse_blocks_toplevel
= create_parser("parse_blocks_toplevel",
function()
return larsers.blocks_toplevel
- end)
+ end, true)
local parse_inlines
= create_parser("parse_inlines",
function()
return larsers.inlines
- end)
+ end, false)
local parse_inlines_no_link
= create_parser("parse_inlines_no_link",
function()
return larsers.inlines_no_link
- end)
+ end, false)
local parse_inlines_no_inline_note
= create_parser("parse_inlines_no_inline_note",
function()
return larsers.inlines_no_inline_note
- end)
+ end, false)
+
+ local parse_inlines_no_html
+ = create_parser("parse_inlines_no_html",
+ function()
+ return larsers.inlines_no_html
+ end, false)
local parse_inlines_nbsp
= create_parser("parse_inlines_nbsp",
function()
return larsers.inlines_nbsp
- end)
+ end, false)
% \end{macrocode}
% \par
% \begin{markdown}
@@ -15381,14 +16386,14 @@ function M.reader.new(writer, options)
-- like indirect_link
local function lookup_note(ref)
- return function()
+ return writer.defer_call(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)
end
local function register_note(ref,rawnote)
@@ -15480,27 +16485,27 @@ larsers.PipeTable = Ct(larsers.table_row * parsers.newline
-- 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()
+ return writer.defer_call(function()
local r,fallback = lookup_reference(label,sps,tag)
if r then
return writer.link(parse_inlines_no_link(label), r.url, r.title)
else
return fallback
end
- 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()
+ return writer.defer_call(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
% \end{macrocode}
% \par
@@ -15602,7 +16607,7 @@ larsers.PipeTable = Ct(larsers.table_row * parsers.newline
* C(parsers.alphanumeric^1 * P("://") * parsers.urlchar^1)
* parsers.more
/ function(url)
- return writer.link(writer.string(url), url)
+ return writer.link(writer.escape(url), url)
end
larsers.AutoLinkEmail = parsers.less
@@ -15610,7 +16615,7 @@ larsers.PipeTable = Ct(larsers.table_row * parsers.newline
* P("@") * parsers.urlchar^1)
* parsers.more
/ function(email)
- return writer.link(writer.string(email),
+ return writer.link(writer.escape(email),
"mailto:"..email)
end
@@ -15642,20 +16647,22 @@ larsers.PipeTable = Ct(larsers.table_row * parsers.newline
larsers.Image = larsers.DirectImage + larsers.IndirectImage
- larsers.TextCitations = Ct(Cc("")
+ larsers.TextCitations = Ct((parsers.spnl
+ * Cc("")
* parsers.citation_name
* ((parsers.spnl
* parsers.lbracket
* parsers.citation_headless_body
- * parsers.rbracket) + Cc("")))
+ * parsers.rbracket) + Cc("")))^1)
/ function(raw_cites)
return larsers.citations(true, raw_cites)
end
larsers.ParenthesizedCitations
- = Ct(parsers.lbracket
+ = Ct((parsers.spnl
+ * parsers.lbracket
* parsers.citation_body
- * parsers.rbracket)
+ * parsers.rbracket)^1)
/ function(raw_cites)
return larsers.citations(false, raw_cites)
end
@@ -15666,9 +16673,14 @@ larsers.PipeTable = Ct(larsers.table_row * parsers.newline
larsers.UlOrStarLine = parsers.asterisk^4 + parsers.underscore^4
/ writer.string
- larsers.EscapedChar = S("\\") * C(parsers.escapable) / writer.string
+ larsers.EscapedChar = parsers.backslash * C(parsers.escapable) / writer.string
- larsers.InlineHtml = C(parsers.inlinehtml) / writer.inline_html
+ larsers.InlineHtml = parsers.emptyelt_any
+ + (parsers.htmlcomment / parse_inlines_no_html)
+ / writer.inline_html_comment
+ + parsers.htmlinstruction
+ + parsers.openelt_any
+ + parsers.closeelt_any
larsers.HtmlEntity = parsers.hexentity / entities.hex_entity / writer.string
+ parsers.decentity / entities.dec_entity / writer.string
@@ -15686,8 +16698,7 @@ larsers.PipeTable = Ct(larsers.table_row * parsers.newline
* parsers.contentblock_tail
/ writer.contentblock
- larsers.DisplayHtml = C(parsers.displayhtml)
- / expandtabs / writer.display_html
+ larsers.DisplayHtml = parsers.displayhtml
larsers.Verbatim = Cs( (parsers.blanklines
* ((parsers.indentedline - parsers.blankline))^1)^1
@@ -16044,6 +17055,10 @@ larsers.PipeTable = Ct(larsers.table_row * parsers.newline
syntax.Smart = parsers.fail
end
+ if options.preserveTabs then
+ options.stripIndent = false
+ end
+
if not options.pipeTables then
syntax.PipeTable = parsers.fail
end
@@ -16067,6 +17082,12 @@ larsers.PipeTable = Ct(larsers.table_row * parsers.newline
inlines_no_inline_note_t.InlineNote = parsers.fail
larsers.inlines_no_inline_note = Ct(inlines_no_inline_note_t)
+ local inlines_no_html_t = util.table_copy(inlines_t)
+ inlines_no_html_t.DisplayHtml = parsers.fail
+ inlines_no_html_t.InlineHtml = parsers.fail
+ inlines_no_html_t.HtmlEntity = parsers.fail
+ larsers.inlines_no_html = Ct(inlines_no_html_t)
+
local inlines_nbsp_t = util.table_copy(inlines_t)
inlines_nbsp_t.Endline = larsers.NonbreakingEndline
inlines_nbsp_t.Space = larsers.NonbreakingSpace
@@ -16125,10 +17146,12 @@ larsers.PipeTable = Ct(larsers.table_row * parsers.newline
else
mode = "w"
end
- file = assert(io.open(options.frozenCacheFileName, mode))
- assert(file:write([[\expandafter\def\csname markdownFrozenCache]] ..
- options.frozenCacheCounter .. [[\endcsname{]] .. output .. [[}]] ..
- "\n"))
+ file = assert(io.open(options.frozenCacheFileName, mode),
+ [[could not open file "]] .. options.frozenCacheFileName
+ .. [[" for writing]])
+ assert(file:write([[\expandafter\global\expandafter\def\csname ]]
+ .. [[markdownFrozenCache]] .. options.frozenCacheCounter
+ .. [[\endcsname{]] .. output .. [[}]] .. "\n"))
assert(file:close())
end
return output
@@ -16172,7 +17195,8 @@ return M
local input
if input_filename then
- local input_file = assert(io.open(input_filename, "r"))
+ local input_file = assert(io.open(input_filename, "r"),
+ [[could not open file "]] .. input_filename .. [[" for reading]])
input = assert(input_file:read("*a"))
assert(input_file:close())
else
@@ -16189,8 +17213,8 @@ if options.cacheDir and not lfs.isdir(options.cacheDir) then
assert(lfs.mkdir(options["cacheDir"]))
end
-local kpse = require("kpse")
-kpse.set_program_name("luatex")
+local ran_ok, kpse = pcall(require, "kpse")
+if ran_ok then kpse.set_program_name("luatex") end
local md = require("markdown")
% \end{macrocode}
% \begin{markdown}
@@ -16214,7 +17238,8 @@ local convert = md.new(options)
local output = convert(input:gsub("\r\n?", "\n") .. "\n")
if output_filename then
- local output_file = assert(io.open(output_filename, "w"))
+ local output_file = assert(io.open(output_filename, "w"),
+ [[could not open file "]] .. output_filename .. [[" for writing]])
assert(output_file:write(output))
assert(output_file:close())
else
@@ -16241,13 +17266,19 @@ end
%
% \end{markdown}
% \begin{macrocode}
-\def\markdownInfo#1{%
- \immediate\write-1{(l.\the\inputlineno) markdown.tex info: #1.}}%
-\def\markdownWarning#1{%
- \immediate\write16{(l.\the\inputlineno) markdown.tex warning: #1}}%
-\def\markdownError#1#2{%
- \errhelp{#2.}%
- \errmessage{(l.\the\inputlineno) markdown.tex error: #1}}%
+\ifx\markdownInfo\undefined
+ \def\markdownInfo#1{%
+ \immediate\write-1{(l.\the\inputlineno) markdown.tex info: #1.}}%
+\fi
+\ifx\markdownWarning\undefined
+ \def\markdownWarning#1{%
+ \immediate\write16{(l.\the\inputlineno) markdown.tex warning: #1}}%
+\fi
+\ifx\markdownError\undefined
+ \def\markdownError#1#2{%
+ \errhelp{#2.}%
+ \errmessage{(l.\the\inputlineno) markdown.tex error: #1}}%
+\fi
% \end{macrocode}
% \par
% \begin{markdown}
@@ -16270,6 +17301,7 @@ end
% \begin{markdown}
%
%### Token Renderer Prototypes
+% \label{sec:textokenrendererprototypes}
% The following definitions should be considered placeholder.
%
% \end{markdown}
@@ -16425,9 +17457,15 @@ end
\ifx\markdownOptionStartNumber\undefined\else
startNumber = \markdownOptionStartNumber,
\fi
+\ifx\markdownOptionStripIndent\undefined\else
+ stripIndent = \markdownOptionStripIndent,
+\fi
\ifx\markdownOptionTableCaptions\undefined\else
tableCaptions = \markdownOptionTableCaptions,
\fi
+\ifx\markdownOptionTeXComments\undefined\else
+ texComments = \markdownOptionTeXComments,
+\fi
\ifx\markdownOptionTightLists\undefined\else
tightLists = \markdownOptionTightLists,
\fi
@@ -16793,8 +17831,8 @@ end
"|markdownOptionHelperScriptFileName"}%
|immediate|write|markdownOutputFileStream{%
local ran_ok, error = pcall(function()
- local kpse = require("kpse")
- kpse.set_program_name("luatex")
+ local ran_ok, kpse = pcall(require, "kpse")
+ if ran_ok then kpse.set_program_name("luatex") end
#1
end)
% \end{macrocode}
@@ -16891,6 +17929,15 @@ end
|gdef|markdownInput#1{%
% \end{macrocode}
% \begin{markdown}
+% Change the category code of the percent sign (`%`) to other, so that a user
+% of the \Opt{hybrid} Lua option or a malevolent actor can't produce TeX
+% comments in the plain TeX output of the Markdown package.
+% \end{markdown}
+% \begin{macrocode}
+ |begingroup
+ |catcode`|%=12
+% \end{macrocode}
+% \begin{markdown}
% If we are reading from the frozen cache, input it, expand the corresponding
% `\markdownFrozenCache`\meta{number} macro, and increment
% \m{markdownFrozenCacheCounter}.
@@ -16905,7 +17952,7 @@ end
|markdownInfo{Including markdown document number
"|the|markdownFrozenCacheCounter" from frozen cache}%
|csname markdownFrozenCache|the|markdownFrozenCacheCounter|endcsname
- |advance|markdownFrozenCacheCounter by 1|relax
+ |global|advance|markdownFrozenCacheCounter by 1|relax
}{%
|markdownInfo{Including markdown document "#1"}%
% \end{macrocode}
@@ -16919,7 +17966,8 @@ end
|closein|markdownInputFileStream
|markdownLuaExecute{%
|markdownPrepare
- local file = assert(io.open("#1", "r"))
+ local file = assert(io.open("#1", "r"),
+ [[could not open file "#1" for reading]])
local input = assert(file:read("*a"))
assert(file:close())
% \end{macrocode}
@@ -16937,9 +17985,10 @@ end
% \end{markdown}
% \begin{macrocode}
|markdownIfOption{FinalizeCache}{%
- |advance|markdownFrozenCacheCounter by 1|relax
+ |global|advance|markdownFrozenCacheCounter by 1|relax
}%
}%
+ |endgroup
}%
|endgroup
% \end{macrocode}
@@ -16958,9 +18007,16 @@ end
% format~[@latex17, Section 9]. As a consequence, we can directly reuse the
% existing plain \TeX{} implementation.
%
+% The \LaTeX{} implementation redefines the plain \TeX{} logging macros (see
+% Section \ref{sec:texinterfacelogging}) to use the \LaTeX{} \m{PackageInfo},
+% \m{PackageWarning}, and \m{PackageError} macros.
+%
% \end{markdown}
% \begin{macrocode}
-\input markdown
+\newcommand\markdownInfo[1]{\PackageInfo{markdown}{#1}}%
+\newcommand\markdownWarning[1]{\PackageWarning{markdown}{#1}}%
+\newcommand\markdownError[2]{\PackageError{markdown}{#1}{#2.}}%
+\input markdown/markdown
\def\markdownVersionSpace{ }%
\ProvidesPackage{markdown}[\markdownLastModified\markdownVersionSpace v%
\markdownVersion\markdownVersionSpace markdown renderer]%
@@ -16975,9 +18031,6 @@ end
%
% \end{markdown}
% \begin{macrocode}
-\renewcommand\markdownInfo[1]{\PackageInfo{markdown}{#1}}%
-\renewcommand\markdownWarning[1]{\PackageWarning{markdown}{#1}}%
-\renewcommand\markdownError[2]{\PackageError{markdown}{#1}{#2.}}%
% \end{macrocode}
% \par
% \begin{markdown}
@@ -17041,7 +18094,6 @@ end
\expandafter\markdownSetup\expandafter{\CurrentOption}}%
\ProcessOptions\relax
% \end{macrocode}
-% \par
% \begin{markdown}
%
% After processing the options, activate the `renderers` and
@@ -17059,8 +18111,229 @@ end
% \par
% \begin{markdown}
%
+%#### \LaTeX{} Themes
+% \label{sec:latexthemesimplementation}
+% This section implements example themes provided with the Markdown package.
+%
+% The `witiko/dot` theme enables the \Opt{fencedCode} Lua option:
+%
+% \end{markdown}
+% \iffalse
+%</latex>
+%<*latex-themes-witiko-dot>
+% \fi
+% \begin{macrocode}
+\markdownSetup{fencedCode}%
+% \end{macrocode}
+% \begin{markdown}
+%
+% We store the previous definition of the fenced code token renderer prototype:
+%
+% \end{markdown}
+% \begin{macrocode}
+\let\markdown@witiko@dot@oldRendererInputFencedCodePrototype
+ \markdownRendererInputFencedCodePrototype
+% \end{macrocode}
+% \begin{markdown}
+%
+% If the infostring starts with `dot …`, we redefine the fenced code block
+% token renderer prototype, so that it typesets the code block via Graphviz
+% tools if and only if the \m{markdownOptionFrozenCache} plain \TeX{} option is
+% disabled and the code block has not been previously typeset:
+%
+% \end{markdown}
+% \begin{macrocode}
+\RequirePackage{ifthen}
+\renewcommand\markdownRendererInputFencedCode[2]{%
+ \def\next##1 ##2\relax{%
+ \ifthenelse{\equal{##1}{dot}}{%
+ \markdownIfOption{FrozenCache}{}{%
+ \immediate\write18{%
+ if ! test -e #1.pdf.source || ! diff #1 #1.pdf.source;
+ then
+ dot -Tpdf -o #1.pdf #1;
+ cp #1 #1.pdf.source;
+ fi}}%
+% \end{macrocode}
+% \begin{markdown}
+%
+% We include the typeset image using the image token renderer:
+%
+% \end{markdown}
+% \begin{macrocode}
+ \markdownRendererImage{Graphviz image}{#1.pdf}{#1.pdf}{##2}%
+% \end{macrocode}
+% \begin{markdown}
+%
+% If the infostring does not start with `dot …`, we use the previous definition
+% of the fenced code token renderer prototype:
+%
+% \end{markdown}
+% \begin{macrocode}
+ }{%
+ \markdown@witiko@dot@oldRendererInputFencedCodePrototype{#1}{#2}%
+ }%
+ }%
+ \next#2 \relax}%
+% \end{macrocode}
+% \iffalse
+%</latex-themes-witiko-dot>
+%<*latex-themes-witiko-graphicx-http>
+% \fi
+% \par
+% \begin{markdown}
+%
+% The `witiko/graphicx/http` theme stores the previous definition of the image
+% token renderer prototype:
+%
+% \end{markdown}
+% \begin{macrocode}
+\let\markdown@witiko@graphicx@http@oldRendererImagePrototype
+ \markdownRendererImagePrototype
+% \end{macrocode}
+% \begin{markdown}
+%
+% We define the \m{markdown@witiko@graphicx@http@counter} counter to enumerate
+% the images for caching and the \m{markdown@witiko@graphicx@http@filename}
+% command, which will store the pathname of the file containing the pathname
+% of the downloaded image file.
+%
+% \end{markdown}
+% \begin{macrocode}
+\newcount\markdown@witiko@graphicx@http@counter
+\markdown@witiko@graphicx@http@counter=0
+\newcommand\markdown@witiko@graphicx@http@filename{%
+ \markdownOptionCacheDir/witiko_graphicx_http%
+ .\the\markdown@witiko@graphicx@http@counter}%
+% \end{macrocode}
+% \begin{markdown}
+%
+% We define the \m{markdown@witiko@graphicx@http@download} command, which will
+% receive two arguments that correspond to the URL of the online image and to
+% the pathname, where the online image should be downloaded. The command will
+% produce a shell command that tries to downloads the online image to the
+% pathname.
+%
+% \end{markdown}
+% \begin{macrocode}
+\newcommand\markdown@witiko@graphicx@http@download[2]{%
+ wget -O #2 #1 || curl --location -o #2 #1 || rm -f #2}
+% \end{macrocode}
+% \begin{markdown}
+%
+% We locally swap the category code of the percentage sign with the line feed
+% control character, so that we can use percentage signs in the shell code:
+%
+% \end{markdown}
+% \begin{macrocode}
+\begingroup
+\catcode`\%=12
+\catcode`\^^A=14
+% \end{macrocode}
+% \begin{markdown}
+%
+% We redefine the image token renderer prototype, so that it tries to download
+% an online image.
+%
+% \end{markdown}
+% \begin{macrocode}
+\global\def\markdownRendererImagePrototype#1#2#3#4{^^A
+ \begingroup
+ \edef\filename{\markdown@witiko@graphicx@http@filename}^^A
+% \end{macrocode}
+% \begin{markdown}
+%
+% The image will be downloaded only if the image URL has the http or https
+% protocols and the \m{markdownOptionFrozenCache} plain \TeX{} option is disabled:
+%
+% \end{markdown}
+% \begin{macrocode}
+ \markdownIfOption{FrozenCache}{}{^^A
+ \immediate\write18{^^A
+ if printf '%s' "#3" | grep -q -E '^https?:';
+ then
+% \end{macrocode}
+% \begin{markdown}
+%
+% The image will be downloaded to the pathname \m{markdownOptionCacheDir}<!--
+% -->`/`\meta{the MD5 digest of the image URL}`.`\meta{the suffix of the
+% image URL}:
+%
+% \end{markdown}
+% \begin{macrocode}
+ OUTPUT_PREFIX="\markdownOptionCacheDir";
+ OUTPUT_BODY="$(printf '%s' '#3' | md5sum | cut -d' ' -f1)";
+ OUTPUT_SUFFIX="$(printf '%s' '#3' | sed 's/.*[.]//')";
+ OUTPUT="$OUTPUT_PREFIX/$OUTPUT_BODY.$OUTPUT_SUFFIX";
+% \end{macrocode}
+% \begin{markdown}
+%
+% The image will be downloaded only if it has not already been downloaded:
+%
+% \end{markdown}
+% \begin{macrocode}
+ if ! [ -e "$OUTPUT" ];
+ then
+ \markdown@witiko@graphicx@http@download{'#3'}{"$OUTPUT"};
+ printf '%s' "$OUTPUT" > "\filename";
+ fi;
+% \end{macrocode}
+% \begin{markdown}
+%
+% If the image does not have the http or https protocols or the image has
+% already been downloaded, the URL will be stored as-is:
+%
+% \end{markdown}
+% \begin{macrocode}
+ else
+ printf '%s' '#3' > "\filename";
+ fi}}^^A
+% \end{macrocode}
+% \begin{markdown}
+%
+% We load the pathname of the downloaded image and we typeset the image using
+% the previous definition of the image renderer prototype:
+%
+% \end{markdown}
+% \begin{macrocode}
+ \CatchFileDef{\filename}{\filename}{}^^A
+ \markdown@witiko@graphicx@http@oldRendererImagePrototype^^A
+ {#1}{#2}{\filename}{#4}^^A
+ \endgroup
+ \global\advance\markdown@witiko@graphicx@http@counter by 1\relax}^^A
+\endgroup
+% \end{macrocode}
+% \iffalse
+%</latex-themes-witiko-graphicx-http>
+%<*latex-themes-witiko-tilde>
+% \fi
+% \par
+% \begin{markdown}
+%
+% The `witiko/tilde` theme redefines the tilde token renderer prototype,
+% so that it expands to a non-breaking space:
+%
+% \end{markdown}
+% \begin{macrocode}
+\renewcommand\markdownRendererTildePrototype{~}%
+% \end{macrocode}
+% \iffalse
+%</latex-themes-witiko-tilde>
+%<*latex>
+% \fi
+% \par
+% \begin{markdown}
+%
%### Token Renderer Prototypes
-% The following configuration should be considered placeholder.
+% The following configuration should be considered placeholder. If the `plain`
+% package option has been enabled (see Section \ref{sec:latexplain}), none of
+% it will take effect.
+% \end{markdown}
+% \begin{macrocode}
+\ifmarkdownLaTeXPlain\else
+% \end{macrocode}
+% \par
+% \begin{markdown}
%
% If the \m{markdownOptionTightLists} macro expands to `false`, do not load
% the \pkg{paralist} package. This is necessary for \Hologo{LaTeX2e} document
@@ -17070,13 +18343,14 @@ end
%
% \end{markdown}
% \begin{macrocode}
-\ifx\markdownOptionTightLists\undefined
- \@ifclassloaded{beamer}{}{
- \RequirePackage{paralist}}
-\else
- \ifthenelse{\equal{\markdownOptionTightLists}{false}}{}{
- \RequirePackage{paralist}}
-\fi
+\RequirePackage{ifthen}
+\@ifundefined{markdownOptionTightLists}{%
+ \@ifclassloaded{beamer}{}{%
+ \RequirePackage{paralist}}%
+}{%
+ \ifthenelse{\equal{\markdownOptionTightLists}{false}}{}{%
+ \RequirePackage{paralist}}%
+}%
% \end{macrocode}
% \par
% \begin{markdown}
@@ -17104,6 +18378,9 @@ end
olEndTight = {\markdownRendererOlEnd},
dlBeginTight = {\markdownRendererDlBegin},
dlEndTight = {\markdownRendererDlEnd}}}}
+\RequirePackage{csvsimple}
+\RequirePackage{fancyvrb}
+\RequirePackage{graphicx}
\markdownSetup{rendererPrototypes={
lineBreak = {\\},
leftBrace = {\textbraceleft},
@@ -17137,10 +18414,10 @@ end
\label{fig:#1}%
\end{figure}},
ulBegin = {\begin{itemize}},
- ulItem = {\item},
+ ulItem = {\item{}},
ulEnd = {\end{itemize}},
olBegin = {\begin{enumerate}},
- olItem = {\item},
+ olItem = {\item{}},
olItemWithNumber = {\item[#1.]},
olEnd = {\end{enumerate}},
dlBegin = {\begin{description}},
@@ -17154,8 +18431,8 @@ end
\ifx\relax#2\relax
\VerbatimInput{#1}%
\else
- \ifx\minted@code\undefined
- \ifx\lst@version\undefined
+ \@ifundefined{minted@code}{%
+ \@ifundefined{lst@version}{%
\markdownRendererInputFencedCode{#1}{}%
% \end{macrocode}
% \par
@@ -17165,9 +18442,9 @@ end
%
% \end{markdown}
% \begin{macrocode}
- \else
+ }{%
\lstinputlisting[language=#2]{#1}%
- \fi
+ }%
% \end{macrocode}
% \par
% \begin{markdown}
@@ -17177,9 +18454,9 @@ end
%
% \end{markdown}
% \begin{macrocode}
- \else
+ }{%
\inputminted{#2}{#1}%
- \fi
+ }%
\fi},
horizontalRule = {\noindent\rule[0.5ex]{\linewidth}{1pt}},
footnote = {\footnote{#1}}}}
@@ -17191,19 +18468,10 @@ end
%
% \end{markdown}
% \begin{macrocode}
-\newif\ifmarkdownLATEXStrongEmphasisNested
-\markdownLATEXStrongEmphasisNestedfalse
-\markdownSetup{rendererPrototypes={
- strongEmphasis = {%
- \ifmarkdownLATEXStrongEmphasisNested
- \markdownLATEXStrongEmphasisNestedfalse
- \textmd{#1}%
- \markdownLATEXStrongEmphasisNestedtrue
- \else
- \markdownLATEXStrongEmphasisNestedtrue
- \textbf{#1}%
- \markdownLATEXStrongEmphasisNestedfalse
- \fi}}}
+\def\markdownLATEXStrongEmphasis#1{%
+ \IfSubStr\f@series{b}{\textnormal{#1}}{\textbf{#1}}}
+\markdownSetup{rendererPrototypes={strongEmphasis={%
+ \protect\markdownLATEXStrongEmphasis{#1}}}}
% \end{macrocode}
% \par
% \begin{markdown}
@@ -17212,14 +18480,14 @@ end
%
% \end{markdown}
% \begin{macrocode}
-\ifx\chapter\undefined
+\@ifundefined{chapter}{%
\markdownSetup{rendererPrototypes = {
headingOne = {\section{#1}},
headingTwo = {\subsection{#1}},
headingThree = {\subsubsection{#1}},
headingFour = {\paragraph{#1}\leavevmode},
headingFive = {\subparagraph{#1}\leavevmode}}}
-\else
+}{%
\markdownSetup{rendererPrototypes = {
headingOne = {\chapter{#1}},
headingTwo = {\section{#1}},
@@ -17227,7 +18495,7 @@ end
headingFour = {\subsubsection{#1}},
headingFive = {\paragraph{#1}\leavevmode},
headingSix = {\subparagraph{#1}\leavevmode}}}
-\fi
+}%
% \end{macrocode}
% \par
% \begin{markdown}
@@ -17243,6 +18511,7 @@ end
\newcount\markdownLaTeXCitationsCounter
% Basic implementation
+\RequirePackage{gobble}
\def\markdownLaTeXBasicCitations#1#2#3#4#5#6{%
\advance\markdownLaTeXCitationsCounter by 1\relax
\ifx\relax#4\relax
@@ -17417,47 +18686,47 @@ end
cite = {%
\markdownLaTeXCitationsCounter=1%
\def\markdownLaTeXCitationsTotal{#1}%
- \ifx\autocites\undefined
- \ifx\citep\undefined
+ \@ifundefined{autocites}{%
+ \@ifundefined{citep}{%
\expandafter\expandafter\expandafter
\markdownLaTeXBasicCitations
\expandafter\expandafter\expandafter{%
\expandafter\expandafter\expandafter}%
\expandafter\expandafter\expandafter{%
\expandafter\expandafter\expandafter}%
- \else
+ }{%
\expandafter\expandafter\expandafter
\markdownLaTeXNatbibCitations
\expandafter\expandafter\expandafter{%
\expandafter\expandafter\expandafter}%
- \fi
- \else
+ }%
+ }{%
\expandafter\expandafter\expandafter
\markdownLaTeXBibLaTeXCitations
\expandafter{\expandafter}%
- \fi},
+ }},
textCite = {%
\markdownLaTeXCitationsCounter=1%
\def\markdownLaTeXCitationsTotal{#1}%
- \ifx\autocites\undefined
- \ifx\citep\undefined
+ \@ifundefined{autocites}{%
+ \@ifundefined{citep}{%
\expandafter\expandafter\expandafter
\markdownLaTeXBasicTextCitations
\expandafter\expandafter\expandafter{%
\expandafter\expandafter\expandafter}%
\expandafter\expandafter\expandafter{%
\expandafter\expandafter\expandafter}%
- \else
+ }{%
\expandafter\expandafter\expandafter
\markdownLaTeXNatbibTextCitations
\expandafter\expandafter\expandafter{%
\expandafter\expandafter\expandafter}%
- \fi
- \else
+ }%
+ }{%
\expandafter\expandafter\expandafter
\markdownLaTeXBibLaTeXTextCitations
\expandafter{\expandafter}%
- \fi}}}
+ }}}}
% \end{macrocode}
% \par
% \begin{markdown}
@@ -17469,6 +18738,7 @@ end
%
% \end{markdown}
% \begin{macrocode}
+\RequirePackage{url}
\def\markdownRendererLinkPrototype{%
\begingroup
\catcode`\#=12
@@ -17540,8 +18810,6 @@ end
\fi
\advance\markdownLaTeXRowCounter by 1\relax
\ifnum\markdownLaTeXRowCounter>\markdownLaTeXRowTotal\relax
- \markdownInfo{\the\markdownLaTeXTable}
- \markdownInfo{\the\markdownLaTeXTableEnd}
\the\markdownLaTeXTable
\the\markdownLaTeXTableEnd
\expandafter\@gobble
@@ -17564,6 +18832,7 @@ end
\addto@hook\markdownLaTeXTable{#1\\}%
\expandafter\@gobble
\fi\markdownLaTeXRenderTableCell}
+\fi
% \end{macrocode}
% \par
% \begin{markdown}
@@ -17600,11 +18869,17 @@ end
% consequence, we can directly reuse the existing plain \TeX{} implementation
% after supplying the missing plain \TeX{} macros.
%
+% The \Hologo{ConTeXt} implementation redefines the plain \TeX{} logging macros
+% (see Section \ref{sec:texinterfacelogging}) to use the \Hologo{ConTeXt}
+% \m{writestatus} macro.
+%
% \end{markdown}
% \begin{macrocode}
+\def\markdownInfo#1{\writestatus{markdown}{#1.}}%
+\def\markdownWarning#1{\writestatus{markdown\space warn}{#1.}}%
\def\dospecials{\do\ \do\\\do\{\do\}\do\$\do\&%
\do\#\do\^\do\_\do\%\do\~}%
-\input markdown
+\input markdown/markdown
% \end{macrocode}
% \par
% \begin{markdown}
@@ -17637,19 +18912,6 @@ end
% \par
% \begin{markdown}
%
-%### Logging Facilities
-% The \Hologo{ConTeXt} implementation redefines the plain \TeX{} logging macros (see
-% Section \ref{sec:texinterfacelogging}) to use the \Hologo{ConTeXt}
-% \m{writestatus} macro.
-%
-% \end{markdown}
-% \begin{macrocode}
-\def\markdownInfo#1{\writestatus{markdown}{#1.}}%
-\def\markdownWarning#1{\writestatus{markdown\space warn}{#1.}}%
-% \end{macrocode}
-% \par
-% \begin{markdown}
-%
%### Typesetting Markdown
% The \m{startmarkdown} and \m{stopmarkdown} macros are implemented using the
% \m{markdownReadAndConvert} macro.
@@ -17704,12 +18966,12 @@ end
\def\markdownConTeXtCSV{csv}
\def\markdownRendererContentBlockPrototype#1#2#3#4{%
\def\markdownConTeXtCSV@arg{#1}%
- \ifx\markdownConTeXtCSV@arg\markdownConTeXtCSV
+ \ifx\markdownConTeXtCSV@arg\markdownConTeXtCSV
\placetable[][tab:#1]{#4}{%
\processseparatedfile[MarkdownConTeXtCSV][#3]}%
- \else
- \markdownInput{#3}%
- \fi}%
+ \else
+ \markdownInput{#3}%
+ \fi}%
\def\markdownRendererImagePrototype#1#2#3#4{%
\placefigure[][fig:#1]{#4}{\externalfigure[#3]}}%
\def\markdownRendererUlBeginPrototype{\startitemize}%