summaryrefslogtreecommitdiff
path: root/macros/generic/markdown/markdown.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'macros/generic/markdown/markdown.dtx')
-rw-r--r--macros/generic/markdown/markdown.dtx13188
1 files changed, 8763 insertions, 4425 deletions
diff --git a/macros/generic/markdown/markdown.dtx b/macros/generic/markdown/markdown.dtx
index 3edb022e15..26b5db62f1 100644
--- a/macros/generic/markdown/markdown.dtx
+++ b/macros/generic/markdown/markdown.dtx
@@ -104,13 +104,7 @@
\newunicodechar{☒}{\markdownRendererTickedBox}
\newunicodechar{⌛}{\markdownRendererHalfTickedBox}
\newunicodechar{☐}{\markdownRendererUntickedBox}
-\usepackage{emoji}
-\makeatletter
-\@ifpackagelater{emoji}{2020/03/16}{
- \newunicodechar{😉}{\emoji{winking-face}}
-}{
- \newunicodechar{😉}{;-)}
-}
+\newunicodechar{😉}{;-)}
\makeatother
% Set up the catcodes.
@@ -122,7 +116,7 @@
---
title: A Markdown Interpreter for \TeX
url: https://github.com/witiko/markdown
-authors: [Vít Novotný]
+authors: [Vít Starý Novotný]
email: witiko@mail.muni.cz
revision: \markdownVersion
date: \markdownLastModified
@@ -763,23 +757,17 @@ abbr {
author = {Knuth, Donald Ervin},
year = {1986},
title = {\TeX: The Program},
- isbn = {0-201-13437-7},
+ isbn = {978-0-201-13437-7},
pagetotal = {xvi, 594},
series = {Computers \& Typesetting},
volume = {B},
location = {Reading, MA},
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}}
@book{eijkhout92,
author = {Victor Eijkhout},
title = {\TeX{} by Topic},
subtitle = {A \TeX nician's Reference},
- isbn = {0-201-56882-0},
+ isbn = {978-0-201-56882-0},
pagetotal = {307},
date = {1992-02-01},
location = {Wokingham, England},
@@ -792,50 +780,23 @@ abbr {
pages = {196-205},
doi = {10.1109/ICPC.2010.41}}
%</techdoc-bibliography>
-%<*latex-themes-witiko-markdown-techdoc>
-\ProvidesPackage{markdownthemewitiko_markdown_techdoc}[2022/02/23]
+%<@@=markdown>
+%<*themes-witiko-markdown-techdoc>
+\ProvidesPackage{markdownthemewitiko_markdown_techdoc}[2022/12/13]
\RequirePackage{etoolbox}
\markdownSetup{
renderers = {
- head*Four = {%
- \paragraph{#1}\leavevmode
- \def\markdownRendererInterblockSeparator{%
- \let\markdownRendererInterblockSeparator\par
- }%
- \noindent
- },
+ head*Four = {\paragraph{#1}\leavevmode},
},
rendererPrototypes = {
codeSpan = {\inline{#1}},
- jek*llDataEnd = {%
+ jekyllData(End) = {%
\AfterEndPreamble{%
\printtitlepage
\tableofcontents
{\def\addcontentsline##1##2##3{}\listoffigures}%
}%
},
- headerAttributeContextBegin = {
- \markdownSetup{
- rendererPrototypes = {
- attributeIdentifier = {%
- \begingroup
- \def\next####1{%
- \def####1########1{%
- \endgroup
- ####1{########1}%
- \label{sec:##1}%
- }%
- }%
- \next\markdownRendererHeadingOne
- \next\markdownRendererHeadingTwo
- \next\markdownRendererHeadingThree
- \next\markdownRendererHeadingFour
- \next\markdownRendererHeadingFive
- \next\markdownRendererHeadingSix
- },
- },
- }%
- },
},
jekyllDataRenderers = {
/authors/* = {%
@@ -853,6 +814,17 @@ abbr {
url = {\gdef\ltd@title@url{#1}},
}
}
+\ExplSyntaxOn
+\markdownSetup{
+ rendererPrototypes = {
+ headerAttributeContextEnd = {
+ \seq_map_inline:Nn
+ \l_@@_header_identifiers_seq
+ { \label { sec:##1 } }
+ },
+ },
+}
+\ExplSyntaxOff
\renewcommand\markdownLaTeXRendererDirectOrIndirectLink[4]{%
#1\footnote{See \url{#3}.}}
\RequirePackage{varioref}
@@ -884,12 +856,12 @@ abbr {
dlEnd = {\end{optionlist}},
}
}
-%</latex-themes-witiko-markdown-techdoc>
+%</themes-witiko-markdown-techdoc>
%<*manual>
---
title: Markdown Package User Manual
-author: Vít Novotný
+author: Vít Starý Novotný
date: \markdownVersion{} \markdownLastModified{}
---
@@ -901,7 +873,7 @@ date: \markdownVersion{} \markdownLastModified{}
Introduction
============
-The [Markdown package][pkg] converts [markdown][] markup to \TeX{} commands. The
+The [Markdown package][pkg] converts [CommonMark][] markup to \TeX{} commands. The
functionality is provided both as a Lua module and as plain \TeX{}, \LaTeX{}, and
\Hologo{ConTeXt} macro packages that can be used to directly typeset \TeX{} documents
containing markdown markup. Unlike other convertors, the Markdown package
@@ -909,10 +881,10 @@ does not require any external programs, and makes it easy to redefine how each
and every markdown element is rendered. Creative abuse of the markdown
syntax is encouraged. 😉
- [markdown]: https://daringfireball.net/projects/markdown/basics
- (Daring Fireball: Markdown Basics)
- [pkg]: https://ctan.org/pkg/markdown
- (CTAN: Package markdown)
+ [commonmark]: https://commonmark.org/
+ (CommonMark: A strongly defined, highly compatible specification of Markdown)
+ [pkg]: https://ctan.org/pkg/markdown
+ (CTAN: Package markdown)
% This document is a technical documentation for the \pkg{Markdown} package. It
% consists of three sections. This section introduces the package and outlines
@@ -942,9 +914,9 @@ documentation][techdoc].
local metadata = {
version = "(((VERSION)))",
comment = "A module for the conversion from markdown to plain TeX",
- author = "John MacFarlane, Hans Hagen, Vít Novotný",
+ author = "John MacFarlane, Hans Hagen, Vít Starý Novotný",
copyright = {"2009-2016 John MacFarlane, Hans Hagen",
- "2016-2023 Vít Novotný"},
+ "2016-2023 Vít Starý Novotný"},
license = "LPPL 1.3c"
}
@@ -994,15 +966,19 @@ make base
``````
This should produce the following files:
-* `markdown.lua`, the Lua module,
-* `libraries/markdown-tinyyaml.lua`, an external library for reading \acro{yaml},
-* `markdown-cli.lua`, the Lua command-line interface,
-* `markdown.tex`, the plain \TeX{} macro package,
-* `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.
+* `markdown.lua`: The Lua module
+* `libraries/markdown-tinyyaml.lua`: An external library for reading \acro{yaml}
+* `markdown-cli.lua`: The Lua command-line interface
+* `markdown.tex`: The plain \TeX{} macro package
+* `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.tex`: The `witiko/tilde` theme
+* `markdownthemewitiko_markdown_defaults.tex`,
+ `markdownthemewitiko_markdown_defaults.sty`, and
+ `t-markdownthemewitiko_markdown_defaults.tex`: The `witiko/markdown/defaults`
+ theme
+* `t-markdown.tex`: The \Hologo{ConTeXt} module
### Local Installation
@@ -1014,11 +990,14 @@ placed:
* `⟨TEXMF⟩/tex/luatex/markdown/markdown-tinyyaml.lua`
* `⟨TEXMF⟩/scripts/markdown/markdown-cli.lua`
* `⟨TEXMF⟩/tex/generic/markdown/markdown.tex`
+* `⟨TEXMF⟩/tex/generic/markdown/markdownthemewitiko_tilde.tex`
+* `⟨TEXMF⟩/tex/generic/markdown/markdownthemewitiko_markdown_defaults.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/latex/markdown/markdownthemewitiko_markdown_defaults.sty`
* `⟨TEXMF⟩/tex/context/third/markdown/t-markdown.tex`
+* `⟨TEXMF⟩/tex/context/third/markdown/t-markdownthemewitiko_markdown_defaults.tex`
where `⟨TEXMF⟩` corresponds to a root of your \TeX{} distribution, such as
`/usr/share/texmf` and `~/texmf` on UN\*X systems or
@@ -1031,10 +1010,20 @@ 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)
+This is where the individual files should be placed:
+
+* `./markdown.lua`
+* `./markdown-tinyyaml.lua`
+* `./markdown-cli.lua`
+* `./markdown/markdown.tex`
+* `./markdown.sty`
+* `./t-markdown.tex`
+* `./markdownthemewitiko_dot.sty`
+* `./markdownthemewitiko_graphicx_http.sty`
+* `./markdownthemewitiko_tilde.tex`
+* `./markdownthemewitiko_markdown_defaults.tex`
+* `./markdownthemewitiko_markdown_defaults.sty`
+* `./t-markdownthemewitiko_markdown_defaults.tex`
%</manual>
%<*lua>
@@ -1077,23 +1066,7 @@ local lpeg = require("lpeg")
%
% \end{markdown}
% \begin{macrocode}
-local unicode
-(function()
- local ran_ok
- ran_ok, unicode = pcall(require, "unicode")
-% \end{macrocode}
-% \begin{markdown}
-%
-% If the Selene Unicode library is unavailable (could be because we are using
-% LuaMeta\TeX) 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)()
+local unicode = require("unicode")
% \end{macrocode}
% \par
% \begin{markdown}
@@ -1133,22 +1106,10 @@ local md5 = require("md5");
% \begin{macrocode}
local should_initialize = package.loaded.kpse == nil
or tex.initialize ~= nil
- local ran_ok
- ran_ok, kpse = pcall(require, "kpse")
- if ran_ok and should_initialize then
+ kpse = require("kpse")
+ if should_initialize then
kpse.set_program_name("luatex")
end
-% \end{macrocode}
-% \begin{markdown}
-%
-% If the \pkg{Kpathsea} library is unavailable, we will look up files only
-% in the current working directory.
-%
-% \end{markdown}
-% \begin{macrocode}
- if not ran_ok then
- kpse = {lookup = function(f, _) return f end}
- end
end)()
% \end{macrocode}
% \par
@@ -1164,35 +1125,7 @@ end)()
%
% \end{markdown}
% \begin{macrocode}
-local uni_case
-(function()
- local ran_ok
- ran_ok, uni_case = pcall(require, "lua-uni-case")
-% \end{macrocode}
-% \begin{markdown}
-%
-% If the lua-uni-algos library is unavailable but the Selene Unicode library
-% is available, we will use its Unicode lower-casing support instead of
-% the more proper case-folding.
-%
-% \end{markdown}
-% \begin{macrocode}
- if not ran_ok then
- if unicode.utf8.lower then
- uni_case = {casefold = unicode.utf8.lower}
- else
-% \end{macrocode}
-% \begin{markdown}
-%
-% If the Selene Unicode library is also unavailable, we will defer to using
-% ASCII lower-casing.
-%
-% \end{markdown}
-% \begin{macrocode}
- uni_case = {casefold = string.lower}
- end
- end
-end)()
+local uni_algos = require("lua-uni-algos")
% \end{macrocode}
% \par
% \begin{markdown}
@@ -1229,10 +1162,16 @@ end)()
%
% \end{markdown}
% \begin{macrocode}
-%<@@=markdown>
+%</tex>
+%<*context>
+\unprotect
+%</context>
+%<*context,tex>
\ifx\ExplSyntaxOn\undefined
- \input expl3-generic\relax
+ \input expl3-generic
\fi
+%</context,tex>
+%<*tex>
% \end{macrocode}
% \begin{markdown}
%
@@ -1264,11 +1203,10 @@ end)()
% \TeX{} part of the package will require that either the Lua\TeX{}
% `\directlua` primitive or the shell access file stream 18 is available in
% your \TeX{} engine. If only the shell access file stream is available in your
-% \TeX{} engine (as is the case with \hologo{pdfTeX} and \Hologo{XeTeX}) or if
-% you enforce the use of shell using the \mref{markdownMode} macro, then unless
-% your \TeX{} engine is globally configured to enable shell access, you will
-% need to provide the `-shell-escape` parameter to your engine when typesetting
-% a document.
+% \TeX{} engine (as is the case with \hologo{pdfTeX} and \Hologo{XeTeX}), then
+% unless your \TeX{} engine is globally configured to enable shell access, you
+% will need to provide the `-shell-escape` parameter to your engine when
+% typesetting a document.
%
% \end{markdown}
% \iffalse
@@ -1284,13 +1222,13 @@ end)()
% loaded,
% \end{markdown}
% \iffalse
-%<*latex-themes-witiko-dot,latex-themes-witiko-graphicx-http,latex-themes-witiko-tilde>
+%<*themes-witiko-dot,latex-themes-witiko-graphicx-http>
% \fi
% \begin{macrocode}
\NeedsTeXFormat{LaTeX2e}%
% \end{macrocode}
% \iffalse
-%</latex-themes-witiko-dot,latex-themes-witiko-graphicx-http,latex-themes-witiko-tilde>
+%</themes-witiko-dot,latex-themes-witiko-graphicx-http>
% \fi
% \begin{markdown}
% a \TeX{} engine that extends \Hologo{eTeX}, and all the plain \TeX{}
@@ -1299,9 +1237,9 @@ end)()
% The following packages are soft prerequisites. They are only used to provide
% default token renderer prototypes (see sections
% <#sec:texrendererprototypes> and
-% <#sec:latex-default-renderer-prototypes>) or \LaTeX{} themes (see Section
-% <#sec:latexthemes>) and will not be loaded if the `plain` package option
-% has been enabled (see Section <#sec:latexplain>):
+% <#sec:latex-token-renderer-prototypes>) or \LaTeX{} themes (see Section
+% <#sec:latexthemes>) and will not be loaded if the option `plain` has been
+% enabled (see Section <#sec:plain>):
%
% \pkg{url}
%
@@ -1333,7 +1271,7 @@ end)()
% \pkg{csvsimple}
%
%: A package that provides the `\csvautotabular` macro for typesetting
-% \acro{csv} files in the default renderer prototypes for iA\,Writer
+% \acro{csv} files in the default renderer prototypes for iA\\,Writer
% content blocks.
%
% \pkg{gobble}
@@ -1378,7 +1316,7 @@ end)()
% \pkg{soulutf8}
%
%: A package that is used in the default renderer prototype for
-% strike-throughs.
+% strike-throughs and marked text.
%
% \pkg{ltxcmds}
%
@@ -1411,7 +1349,7 @@ end)()
% \pkg{m-database}
%
%: A module that provides the default token renderer prototype for
-% iA\,Writer content blocks with the \acro{csv} filename extension (see
+% iA\\,Writer content blocks with the \acro{csv} filename extension (see
% Section <#sec:texrendererprototypes>).
%
% Feedback
@@ -1498,7 +1436,11 @@ Using a text editor, create a text document named `document.tex` with the
following content:
``` tex
\input markdown
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
\input hello
+\endgroup
\bye
```````
@@ -1508,8 +1450,8 @@ Using a text editor, create a text document named `hello.lua` with the
following content:
``` lua
#!/usr/bin/env texlua
-local ran_ok, kpse = pcall(require, "kpse")
-if ran_ok then kpse.set_program_name("luatex") end
+local kpse = require("kpse")
+kpse.set_program_name("luatex")
local markdown = require("markdown")
local convert = markdown.new()
print(convert("Hello *world*!"))
@@ -1569,10 +1511,35 @@ Next, invoke LuaTeX from the terminal:
luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the text
-“Hello *world*!” Invoking pdfTeX should have the same effect:
+“Hello *world*!”
+
+Instead of LuaTeX, you may also use pdfTeX:
``` sh
pdftex --shell-escape document.tex
```````
+This should also produce a PDF document named `document.pdf` with the same content.
+
+***
+
+Instead of writing your markdown document between `\markdownBegin` and
+`\markdownEnd`, you can also include markdown documents using the
+`\markdownInput` macro, similarly to how you might use the `\input` TeX
+primitive to include \TeX{} documents.
+
+Using a text editor, create a text document named `hello.md` with the
+following content:
+``` md
+Hello *world*!
+``````
+Create also a text document named `document.tex` with the following content:
+``` tex
+\input markdown
+\markdownInput{hello.md}
+\bye
+```````
+Next, invoke LuaTeX or pdfTeX from the terminal like in the previous example.
+A PDF document named `document.pdf` should be produced and contain the text
+“Hello *world*!”
### Using \LaTeX{}
@@ -1592,10 +1559,39 @@ Next, invoke LuaTeX from the terminal:
lualatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the text “Hello
-*world*!” Invoking pdfTeX should have the same effect:
+*world*!”
+
+Instead of LuaTeX, you may also use pdfTeX:
``` sh
pdflatex --shell-escape document.tex
``````
+This should also produce a PDF document named `document.pdf` with the same content.
+
+***
+
+Instead of writing your markdown document between `\begin{markdown}` and
+`\end{markdown}`, you can also include markdown documents using the
+`\markdownInput` macro, similarly to how you might use the `\input` TeX
+primitive to include \LaTeX{} documents.
+
+Using a text editor, create a text document named `hello.md` with the
+following content:
+``` md
+Hello *world*!
+``````
+Create also a text document named `document.tex` with the following content:
+``` tex
+\documentclass{article}
+\usepackage{markdown}
+\begin{document}
+\begin{markdown}
+\markdownInput{hello.md}
+\end{markdown}
+\end{document}
+```````
+Next, invoke LuaTeX or pdfTeX from the terminal like in the previous example.
+A PDF document named `document.pdf` should be produced and contain the text
+“Hello *world*!”
***
@@ -1629,25 +1625,48 @@ Hello *world*!
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the text “Hello
*world*!”
***
+Instead of writing your markdown document between `\startmarkdown` and
+`\stopmarkdown`, you can also include markdown documents using the
+`\inputmarkdown` macro, similarly to how you might use the `\input` TeX
+primitive to include \Hologo{ConTeXt} documents.
+
+Using a text editor, create a text document named `hello.md` with the
+following content:
+``` md
+Hello *world*!
+``````
+Create also a text document named `document.tex` with the following content:
+``` tex
+\usemodule[t][markdown]
+\starttext
+\inputmarkdown{hello.md}
+\stoptext
+```````
+Next, invoke LuaTeX from the terminal like in the previous example.
+A PDF document named `document.pdf` should be produced and contain the text
+“Hello *world*!”
+
+***
+
As the next step, try typesetting the example documents distributed along with
the Markdown package:
``` sh
git clone https://github.com/witiko/markdown
cd markdown/examples
-context context.tex
+context --luatex context.tex
``````
A PDF document named `context.pdf` should be produced. Open the text documents
`context.tex` and `example.md` in a text editor to see how the example documents
are structured. Try changing the documents and typesetting them as follows:
``` sh
-context context.tex
+context --luatex context.tex
``````
to see the effect of your changes.
@@ -1674,6 +1693,7 @@ Each part will be shown by example, leaving the implementation details to the
% Lua Interface {#luainterface}
%---------------
%
+% \end{markdown}
% \iffalse
%</lua>
%<*manual-interfaces>
@@ -1700,6 +1720,7 @@ module and a command-line interface (CLI).
%</manual-interfaces>
%<*lua>
% \fi
+% \begin{markdown}
%
% The Lua interface provides the conversion from \acro{utf}-\oldstylenums8
% encoded markdown to plain \TeX{}. This interface is used by the plain \TeX{}
@@ -1755,8 +1776,11 @@ local walkable_syntax = {
"ThematicBreak",
"BulletList",
"OrderedList",
- "Heading",
"DisplayHtml",
+ "Heading",
+ },
+ BlockOrParagraph = {
+ "Block",
"Paragraph",
"Plain",
},
@@ -1764,11 +1788,8 @@ local walkable_syntax = {
"Str",
"Space",
"Endline",
- "UlOrStarLine",
- "Strong",
- "Emph",
- "Link",
- "Image",
+ "EndlineBreak",
+ "LinkAndEmph",
"Code",
"AutoLinkUrl",
"AutoLinkEmail",
@@ -1794,9 +1815,9 @@ local walkable_syntax = {
% not be interpreted by the Markdown package; you can treat it as a comment.
%
% For example. if we'd like to insert `pattern` into the grammar between the
-% `Inline -> Emph` and `Inline -> Link` rules, we would call
-% \luamref{reader->insert_pattern} with `"Inline after Emph"` (or `"Inline
-% before Link"`) and `pattern` as the arguments.
+% `Inline -> LinkAndEmph` and `Inline -> Code` rules, we would call
+% \luamref{reader->insert_pattern} with `"Inline after LinkAndEmph"` (or `"Inline
+% before Code"`) and `pattern` as the arguments.
%
% The \luamref{reader->add_special_character} method adds a new character with
% special meaning to the grammar of markdown. The method receives the character
@@ -1824,26 +1845,30 @@ following content:
``` tex
\input markdown
\input lmfonts
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
\input example
+\endgroup
\bye
```````
Using a text editor, create a text document named `example.lua` with the
following content:
``` lua
#!/usr/bin/env texlua
-local ran_ok, kpse = pcall(require, "kpse")
-if ran_ok then kpse.set_program_name("luatex") end
+local kpse = require("kpse")
+kpse.set_program_name("luatex")
local markdown = require("markdown")
-local input, convert_safe, convert_unsafe, paragraph
+local input, convert_nomath, convert_math, paragraph
input = [[$\sqrt{-1}$ *equals* $i$.]]
-convert_safe = markdown.new()
-convert_unsafe = markdown.new({hybrid = true})
+convert_nomath = markdown.new()
+convert_math = markdown.new({texMathDollars = true})
paragraph = [[\par]]
print(
- convert_safe(input) .. paragraph ..
- convert_unsafe(input)
+ convert_nomath(input) .. paragraph ..
+ convert_math(input)
)
```````
Next, invoke LuaTeX from the terminal:
@@ -1876,16 +1901,16 @@ named `document.tex` with the following content:
\input lmfonts
\directlua{
local markdown = require("markdown")
- local input, convert_safe, convert_unsafe, paragraph
+ local input, convert_nomath, convert_math, paragraph
input = [[$\string\sqrt{-1}$ *equals* $i$.]]
- convert_safe = markdown.new()
- convert_unsafe = markdown.new({hybrid = true})
+ convert_nomath = markdown.new()
+ convert_math = markdown.new({texMathDollars = true})
paragraph = [[\par]]
tex.sprint(
- convert_safe(input) .. paragraph ..
- convert_unsafe(input)
+ convert_nomath(input) .. paragraph ..
+ convert_math(input)
)
}
\bye
@@ -1921,11 +1946,12 @@ following content:
``` tex
\input markdown
\input lmfonts
-\input safe
-
\begingroup
\catcode`\%=12
-\input unsafe
+\catcode`\#=12
+\input nomath
+\par
+\input math
\endgroup
\bye
```````
@@ -1936,8 +1962,8 @@ $\sqrt{-1}$ *equals* $i$.
``````
Next, invoke LuaTeX from the terminal:
``` sh
-texlua ⟨CLI pathname⟩ -- example.md safe.tex
-texlua ⟨CLI pathname⟩ hybrid=true -- example.md unsafe.tex
+texlua ⟨CLI pathname⟩ -- example.md nomath.tex
+texlua ⟨CLI pathname⟩ tex_math_dollars=true -- example.md math.tex
luatex document.tex
``````
where \meta{CLI pathname} corresponds to the location of the Lua CLI script file,
@@ -1957,8 +1983,8 @@ following text:
Invoking pdfTeX should have the same effect:
``` sh
-texlua ⟨CLI pathname⟩ -- example.md safe.tex
-texlua ⟨CLI pathname⟩ hybrid=true -- example.md unsafe.tex
+texlua ⟨CLI pathname⟩ -- example.md nomath.tex
+texlua ⟨CLI pathname⟩ tex_math_dollars=true -- example.md math.tex
pdftex document.tex
``````
@@ -2331,6 +2357,212 @@ interfaces and all the way up to the \LaTeX{} and \Hologo{ConTeXt} interfaces.
% \fi
% \begin{markdown}
%
+%### General Behavior
+%
+% \end{markdown}
+% \par
+% \iffalse
+
+#### Option `eagerCache`
+
+`eagerCache` (default value: `false`)
+
+% \fi
+% \begin{markdown}
+%
+% \Optitem[false]{eagerCache}{\opt{true}, \opt{false}}
+%
+: true
+
+ : Converted markdown documents will be cached in \Opt{cacheDir}. This can be
+ useful for post-processing the converted documents and for recovering
+ historical versions of the documents from the cache. However, it also
+ produces a large number of auxiliary files on the disk and obscures the
+ output of the Lua command-line interface when it is used for plumbing.
+
+ This behavior will always be used if the \Opt{finalizeCache} option is
+ enabled.
+
+: false
+
+ : Converted markdown documents will not be cached. This decreases the number
+ of auxiliary files that we produce and makes it easier to use the Lua
+ command-line interface for plumbing.
+
+ This behavior will only be used when the \Opt{finalizeCache} option is
+ disabled.
+
+% \end{markdown}
+% \iffalse
+
+##### Lua CLI Example {.unnumbered}
+
+Using a text editor, create a text document named `hello.md` with the
+following content:
+``` md
+Hello *world*!
+``````
+Next, invoke LuaTeX from the terminal with the \Opt{eagerCache} option
+disabled:
+``` sh
+texlua ⟨CLI pathname⟩ eagerCache=false -- hello.md hello.tex
+```````
+where \meta{CLI pathname} corresponds to the location of the Lua CLI script file,
+such as `~/texmf/scripts/markdown/markdown-cli.lua` on UN\*X systems or
+`C:\Users\`\meta{Your username}`\texmf\scripts\markdown\markdown-cli.lua` on Windows
+systems. Use the command `kpsewhich -a markdown-cli.lua` to locate the Lua CLI
+script file using [Kpathsea][].
+
+A \TeX{} document named `hello.tex` should be produced and contain the
+following code:
+``` tex
+Hello \markdownRendererEmphasis{world}!\relax
+```
+
+***
+
+Invoke LuaTeX from the terminal again, this time with the \Opt{eagerCache}
+option enabled:
+
+``` tex
+texlua ⟨CLI pathname⟩ eagerCache=true -- hello.md hello.tex
+```
+
+A \TeX{} document named `hello.tex` should be produced and contain the
+following code:
+
+``` tex
+\input ./⟨hash⟩.md.tex\relax
+```
+
+Additionally, a \TeX{} document named `⟨hash⟩.md.tex` should be produced and
+contain the following code:
+
+``` tex
+Hello \markdownRendererEmphasis{world}!\relax
+```
+
+%</manual-options>
+%<*tex>
+% \fi
+% \begin{macrocode}
+\@@_add_lua_option:nnn
+ { eagerCache }
+ { boolean }
+ { false }
+% \end{macrocode}
+% \iffalse
+%</tex>
+%<*lua,lua-cli>
+% \fi
+% \begin{macrocode}
+defaultOptions.eagerCache = false
+% \end{macrocode}
+% \par
+% \iffalse
+%</lua,lua-cli>
+%<*manual-options>
+
+#### Option `singletonCache`
+
+`singletonCache` (default value: `true`)
+
+% \fi
+% \begin{markdown}
+%
+% \Optitem[true]{singletonCache}{\opt{true}, \opt{false}}
+%
+: true
+
+ : Conversion functions produced by the function \luamref{new}`(options)`
+ will be cached in an LRU cache of size 1 keyed by `options`. This is
+ more time- and space-efficient than always producing a new conversion
+ function but may expose bugs related to the idempotence of conversion
+ functions.
+
+ This has been the default behavior since version 3.0.0 of the Markdown
+ package.
+
+: false
+
+ : Every call to the function \luamref{new}`(options)` will produce a new
+ conversion function that will not be cached. This is slower than
+ caching conversion functions and may expose bugs related to memory
+ leaks in the creation of conversion functions, see also issue [#226][1].
+
+ This was the default behavior until version 3.0.0 of the Markdown package.
+
+ [1]: https://github.com/witiko/markdown/pull/226#issuecomment-1599641634
+
+% \end{markdown}
+% \iffalse
+
+##### Lua Module Example {.unnumbered}
+
+Using a text editor, create a text document named `document.tex` with the
+following content:
+``` tex
+\input markdown
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
+\directlua{
+ local markdown = require("markdown")
+ local convert1 = markdown.new({})
+ local convert2 = markdown.new({singletonCache=false})
+ local convert3 = markdown.new({singletonCache=true})
+ local newline = [[^^J^^J]]
+ tex.print(tostring(convert1) .. ", ")
+ tex.print(tostring(convert2) .. ", ")
+ tex.print(tostring(convert3))
+}
+\endgroup
+\bye
+```````
+Then, invoke LuaTeX from the terminal:
+``` sh
+luatex document.tex
+```````
+A PDF document named `document.pdf` should be produced and contain the following text:
+
+> function: 0x1a4a038, function: 0x1a52b18, function: 0x1a4a038
+
+As you can see, the caching causes `convert1` and `convert3` to be the same
+conversion function.
+
+%</manual-options>
+%<*tex>
+% \fi
+% \begin{macrocode}
+\@@_add_lua_option:nnn
+ { singletonCache }
+ { boolean }
+ { true }
+% \end{macrocode}
+% \iffalse
+%</tex>
+%<*lua,lua-cli>
+% \fi
+% \begin{macrocode}
+defaultOptions.singletonCache = true
+% \end{macrocode}
+% \iffalse
+%</lua,lua-cli>
+%<*lua>
+% \fi
+% \begin{macrocode}
+local singletonCache = {
+ convert = nil,
+ options = nil,
+}
+% \end{macrocode}
+% \par
+% \iffalse
+%</lua>
+%<*manual-options>
+% \fi
+% \begin{markdown}
+%
%### File and Directory Names
%
% \end{markdown}
@@ -2365,11 +2597,15 @@ Using a text editor, create a text document named `document.tex` with the
following content:
``` tex
\input markdown
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
\directlua{
local markdown = require("markdown")
local convert = markdown.new({cacheDir = "cache"})
local input = "Hello *world*!"
tex.sprint(convert(input)) }
+\endgroup
\bye
```````
Create an empty directory named `cache` next to our text document. Then, invoke
@@ -2387,7 +2623,11 @@ Using a text editor, create a text document named `document.tex` with the
following content:
``` tex
\input markdown
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
\input hello
+\endgroup
\bye
```````
Using a text editor, create a text document named `hello.md` with the
@@ -2469,7 +2709,7 @@ Hello *world*!
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the text “Hello
*world*!” A directory named `cache` containing several cache files of the
@@ -2507,7 +2747,7 @@ defaultOptions.cacheDir = "."
% \Valitem[markdown-languages.json]{contentBlocksLanguageMap}{filename}
%
: The filename of the \acro{JSON} file that maps filename extensions to
- programming language names in the iA\,Writer content blocks when the
+ programming language names in the iA\\,Writer content blocks when the
\Opt{contentBlocks} option is enabled.
% See Section <#sec:texcontentblockrenderers> for more information.
@@ -2621,7 +2861,7 @@ Create also a text document named `document.tex` with the following content:
````````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
`````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -2739,11 +2979,15 @@ Using a text editor, create a text document named `document.tex` with the
following content:
``` tex
\input markdown
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
\directlua{
local markdown = require("markdown")
local convert = markdown.new({finalizeCache = true, frozenCacheFileName = "cache.tex"})
local input = "Hello *world*!"
tex.sprint(convert(input)) }
+\endgroup
\bye
```````
Then, invoke LuaTeX from the terminal:
@@ -2761,7 +3005,11 @@ Using a text editor, create a text document named `document.tex` with the
following content:
``` tex
\input markdown
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
\input hello
+\endgroup
\bye
```````
Using a text editor, create a text document named `hello.md` with the
@@ -2891,7 +3139,7 @@ Hello *world*!
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the text
“Hello *world*!” A frozen cache will also be produced in the `cache.tex`
@@ -2913,9 +3161,9 @@ Hi *world*!
\stopmarkdown
\stoptext
```````
-Last, invoke pdfTeX without shell access from the terminal:
+Last, invoke LuaTeX from the terminal:
``` sh
-context frozen-document.tex
+context --luatex frozen-document.tex
``````
A PDF document named `frozen-document.pdf` should be produced and contain the
text “Hello *world*!” Since we used the contents of the frozen cache using
@@ -2952,6 +3200,55 @@ defaultOptions.frozenCacheFileName = "frozenCache.tex"
% \par
% \iffalse
+#### Option `autoIdentifiers`
+
+`autoIdentifiers` (default value: `false`)
+
+% \fi
+% \begin{markdown}
+% \Optitem[false]{autoIdentifiers}{\opt{true}, \opt{false}}
+%
+: true
+
+ : Enable the Pandoc [auto identifiers syntax extension][pandoc-auto-identifiers]:
+
+ ``` md
+ The following heading received the identifier `sesame-street`:
+
+ # 123 Sesame Street
+ ``````
+
+ false
+
+ : Disable the Pandoc auto identifiers syntax extension.
+
+See also the option \Opt{gfmAutoIdentifiers}.
+
+ [pandoc-auto-identifiers]: https://pandoc.org/MANUAL.html#extension-auto_identifiers
+
+% \end{markdown}
+% \iffalse
+%</manual-options>
+%<*tex>
+% \fi
+% \begin{macrocode}
+\@@_add_lua_option:nnn
+ { autoIdentifiers }
+ { boolean }
+ { false }
+% \end{macrocode}
+% \iffalse
+%</tex>
+%<*lua,lua-cli>
+% \fi
+% \begin{macrocode}
+defaultOptions.autoIdentifiers = false
+% \end{macrocode}
+% \par
+% \iffalse
+%</lua,lua-cli>
+%<*manual-options>
+
#### Option `blankBeforeBlockquote`
`blankBeforeBlockquote` (default value: `false`)
@@ -2979,6 +3276,9 @@ following content:
``` tex
\input markdown
\input lmfonts
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
\directlua{
local markdown = require("markdown")
local newline = [[^^J^^J]]
@@ -2993,6 +3293,7 @@ following content:
input = "A paragraph." .. newline ..
"> Not a quote." .. newline
tex.sprint(convert(input)) }
+\endgroup
\bye
```````
Then, invoke LuaTeX from the terminal:
@@ -3015,8 +3316,12 @@ following content:
``` tex
\input markdown
\input lmfonts
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
\input optionfalse
\input optiontrue
+\endgroup
\bye
```````
Using a text editor, create a text document named `content.md` with the
@@ -3093,10 +3398,10 @@ A paragraph.
> A quote.
\end{markdown}
-\begin{markdown*}{blankBeforeBlockquote}
+\begin{markdown}[blankBeforeBlockquote]
A paragraph.
> Not a quote.
-\end{markdown*}
+\end{markdown}
\end{document}
```````
@@ -3136,7 +3441,7 @@ A paragraph.
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -3197,6 +3502,9 @@ following content:
``` tex
\input markdown
\input lmfonts
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
\directlua{
local markdown = require("markdown")
local newline = [[^^J^^J]]
@@ -3216,6 +3524,7 @@ following content:
"Not a fenced code." .. newline ..
"```" .. newline
tex.sprint(convert(input)) }
+\endgroup
\bye
```````
Then, invoke LuaTeX from the terminal:
@@ -3240,8 +3549,12 @@ following content:
``` tex
\input markdown
\input lmfonts
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
\input optionfalse
\input optiontrue
+\endgroup
\bye
```````
Using a text editor, create a text document named `content.md` with the
@@ -3331,12 +3644,12 @@ A fenced code.
```
\end{markdown}
-\begin{markdown*}{blankBeforeCodeFence}
+\begin{markdown}[blankBeforeCodeFence]
A paragraph.
```
Not a fenced code.
```
-\end{markdown*}
+\end{markdown}
\end{document}
````````
@@ -3383,7 +3696,7 @@ Not a fenced code.
````````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -3454,13 +3767,13 @@ A fenced div.
:::
\end{markdown}
-\begin{markdown*}{blankBeforeDivFence}
+\begin{markdown}[blankBeforeDivFence]
A paragraph.
::: {.identifier}
Not a fenced div.
:::
-\end{markdown*}
+\end{markdown}
\end{document}
````````
@@ -3529,6 +3842,9 @@ following content:
\input markdown
\input lmfonts
\def\markdownRendererHeadingOne#1{{\bf #1}\par}
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
\directlua{
local markdown = require("markdown")
local newline = [[^^J^^J]]
@@ -3545,6 +3861,7 @@ following content:
"Not a heading." .. newline ..
"==============" .. newline
tex.sprint(convert(input)) }
+\endgroup
\bye
```````
Then, invoke LuaTeX from the terminal:
@@ -3568,8 +3885,12 @@ following content:
``` tex
\input markdown
\input lmfonts
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
\input optionfalse
\input optiontrue
+\endgroup
\bye
```````
Using a text editor, create a text document named `content.md` with the
@@ -3652,11 +3973,11 @@ A heading.
==========
\end{markdown}
-\begin{markdown*}{blankBeforeHeading}
+\begin{markdown}[blankBeforeHeading]
A paragraph.
Not a heading.
==============
-\end{markdown*}
+\end{markdown}
\end{document}
```````
@@ -3699,7 +4020,7 @@ Not a heading.
````````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
`````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -3732,6 +4053,232 @@ defaultOptions.blankBeforeHeading = false
%</lua,lua-cli>
%<*manual-options>
+#### Option `blankBeforeList`
+
+`blankBeforeList` (default value: `false`)
+
+% \fi
+% \begin{markdown}
+%
+% \Optitem[false]{blankBeforeList}{\opt{true}, \opt{false}}
+%
+: true
+
+ : Require a blank line between a paragraph and the following list.
+
+ false
+
+ : Do not require a blank line between a paragraph and the following
+ list.
+
+% \end{markdown}
+% \iffalse
+
+##### Lua Module Example {.unnumbered}
+
+Using a text editor, create a text document named `document.tex` with the
+following content:
+``` tex
+\input markdown
+\input lmfonts
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
+\directlua{
+ local markdown = require("markdown")
+ local newline = [[^^J^^J]]
+ local convert, input
+
+ convert = markdown.new()
+ input = "A paragraph." .. newline ..
+ "- a list" .. newline
+ tex.sprint(convert(input))
+
+ convert = markdown.new({
+ blankBeforeList = true})
+ input = "A paragraph." .. newline ..
+ "- not a list" .. newline
+ tex.sprint(convert(input)) }
+\endgroup
+\bye
+```````
+Then, invoke LuaTeX from the terminal:
+``` sh
+luatex document.tex
+```````
+A PDF document named `document.pdf` should be produced and contain the
+following text:
+
+> A paragraph.
+>
+> - a list
+>
+> A paragraph. - not a list
+
+##### Lua CLI Example {.unnumbered}
+
+Using a text editor, create a text document named `document.tex` with the
+following content:
+``` tex
+\input markdown
+\input lmfonts
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
+\input optionfalse
+\input optiontrue
+\endgroup
+\bye
+```````
+Using a text editor, create a text document named `content.md` with the
+following content:
+```` md
+A paragraph.
+- a list?
+```````
+Next, invoke LuaTeX from the terminal:
+``` sh
+texlua ⟨CLI pathname⟩ -- content.md optionfalse.tex
+texlua ⟨CLI pathname⟩ blankBeforeList=true -- content.md optiontrue.tex
+luatex document.tex
+```````
+where \meta{CLI pathname} corresponds to the location of the Lua CLI script file,
+such as `~/texmf/scripts/markdown/markdown-cli.lua` on UN\*X systems or
+`C:\Users\`\meta{Your username}`\texmf\scripts\markdown\markdown-cli.lua` on Windows
+systems. Use the command `kpsewhich -a markdown-cli.lua` to locate the Lua CLI
+script file using [Kpathsea][].
+
+A PDF document named `document.pdf` should be produced and contain the
+following text:
+
+> A paragraph.
+>
+> - a list?
+>
+> A paragraph. - a list?
+
+##### Plain \TeX{} Example {.unnumbered}
+
+Using a text editor, create a text document named `document.tex` with the
+following content:
+```` tex
+\input markdown
+
+\markdownBegin
+A paragraph.
+- a list
+\markdownEnd
+
+\def\markdownOptionBlankBeforeList{true}
+\markdownBegin
+A paragraph.
+- not a list
+\markdownEnd
+
+\bye
+````````
+Next, invoke LuaTeX from the terminal:
+``` sh
+luatex document.tex
+``````
+A PDF document named `document.pdf` should be produced and contain the
+following text:
+
+> A paragraph.
+>
+> - a list
+>
+> A paragraph. - not a list
+
+##### \LaTeX{} Example {.unnumbered}
+
+Using a text editor, create a text document named `document.tex` with the
+following content:
+```` tex
+\documentclass{article}
+\usepackage{markdown}
+\begin{document}
+
+\begin{markdown}
+A paragraph.
+- a list
+\end{markdown}
+
+\begin{markdown}[blankBeforeList]
+A paragraph.
+- not a list
+\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:
+
+> A paragraph.
+>
+> - a list
+>
+> A paragraph. - not a list
+
+##### \Hologo{ConTeXt} Example {.unnumbered}
+
+Using a text editor, create a text document named `document.tex` with the
+following content:
+```` tex
+\usemodule[t][markdown]
+\starttext
+
+\startmarkdown
+A paragraph.
+- a list
+\stopmarkdown
+
+\setupmarkdown[blankBeforeList = yes]
+\startmarkdown
+A paragraph.
+- not a list
+\stopmarkdown
+
+\stoptext
+````````
+Next, invoke LuaTeX from the terminal:
+``` sh
+context document.tex
+``````
+A PDF document named `document.pdf` should be produced and contain the
+following text:
+
+> A paragraph.
+>
+> - a list
+>
+> A paragraph. - not a list
+
+%</manual-options>
+%<*tex>
+% \fi
+% \begin{macrocode}
+\@@_add_lua_option:nnn
+ { blankBeforeList }
+ { boolean }
+ { false }
+% \end{macrocode}
+% \iffalse
+%</tex>
+%<*lua,lua-cli>
+% \fi
+% \begin{macrocode}
+defaultOptions.blankBeforeList = false
+% \end{macrocode}
+% \par
+% \iffalse
+%</lua,lua-cli>
+%<*manual-options>
+
#### Option `bracketedSpans`
`bracketedSpans` (default value: `false`)
@@ -3830,12 +4377,12 @@ defaultOptions.bracketedSpans = false
#### Option `breakableBlockquotes`
-`breakableBlockquotes` (default value: `false`)
+`breakableBlockquotes` (default value: `true`)
% \fi
% \begin{markdown}
%
-% \Optitem[false]{breakableBlockquotes}{\opt{true}, \opt{false}}
+% \Optitem[true]{breakableBlockquotes}{\opt{true}, \opt{false}}
%
: true
@@ -3856,20 +4403,24 @@ following content:
\input markdown
\input lmfonts
\def\markdownRendererHeadingOne#1{{\bf #1}\par}
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
\directlua{
local markdown = require("markdown")
local newline = [[^^J^^J]]
local convert, input
- convert = markdown.new()
+ convert = markdown.new({breakableBlockquotes = false})
input = "> A single" .. newline .. newline ..
"> block quote." .. newline
tex.sprint(convert(input))
- convert = markdown.new({breakableBlockquotes = true})
+ convert = markdown.new()
input = "> A block quote." .. newline .. newline ..
"> Another block quote." .. newline
tex.sprint(convert(input)) }
+\endgroup
\bye
```````
Then, invoke LuaTeX from the terminal:
@@ -3892,8 +4443,12 @@ following content:
``` tex
\input markdown
\input lmfonts
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
\input optionfalse
\input optiontrue
+\endgroup
\bye
```````
Using a text editor, create a text document named `content.md` with the
@@ -3905,8 +4460,8 @@ following content:
``````
Next, invoke LuaTeX from the terminal:
``` sh
-texlua ⟨CLI pathname⟩ -- content.md optionfalse.tex
-texlua ⟨CLI pathname⟩ breakableBlockquotes=true -- content.md optiontrue.tex
+texlua ⟨CLI pathname⟩ breakableBlockquotes=false -- content.md optionfalse.tex
+texlua ⟨CLI pathname⟩ -- content.md optiontrue.tex
luatex document.tex
```````
where \meta{CLI pathname} corresponds to the location of the Lua CLI script file,
@@ -3931,6 +4486,7 @@ following content:
``` tex
\input markdown
+\def\markdownOptionBreakableBlockquotes{false}
\markdownBegin
> A single
@@ -3968,17 +4524,17 @@ following content:
\usepackage{markdown}
\begin{document}
-\begin{markdown}
+\begin{markdown}[breakableBlockquotes = false]
> A single
> block quote.
\end{markdown}
-\begin{markdown*}{breakableBlockquotes}
+\begin{markdown}
> A block quote.
> Another block quote.
-\end{markdown*}
+\end{markdown}
\end{document}
```````
@@ -4003,6 +4559,7 @@ following content:
\usemodule[t][markdown]
\starttext
+\setupmarkdown[breakableBlockquotes = no]
\startmarkdown
> A single
@@ -4020,7 +4577,7 @@ following content:
````````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
`````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -4038,14 +4595,14 @@ following text:
\@@_add_lua_option:nnn
{ breakableBlockquotes }
{ boolean }
- { false }
+ { true }
% \end{macrocode}
% \iffalse
%</tex>
%<*lua,lua-cli>
% \fi
% \begin{macrocode}
-defaultOptions.breakableBlockquotes = false
+defaultOptions.breakableBlockquotes = true
% \end{macrocode}
% \par
% \iffalse
@@ -4099,9 +4656,9 @@ following content:
The TeXbook [@knuth:tex, p. 123 and 130] is good.
\end{markdown}
-\begin{markdown*}{citationNbsps = false}
+\begin{markdown}[citationNbsps = false]
The TeXbook [@knuth:tex, p. 123 and 130] is good.
-\end{markdown*}
+\end{markdown}
\bibliographystyle{plain}
\bibliography{document.bib}
@@ -4287,6 +4844,9 @@ following content:
``` tex
\input markdown
\input lmfonts
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
\directlua{
local markdown = require("markdown")
local convert = markdown.new()
@@ -4302,6 +4862,7 @@ following content:
"``This is a quote.'' " ..
"``This is another quote.''"
tex.sprint(convert(input)) }
+\endgroup
\bye
```````
Then, invoke LuaTeX from the terminal:
@@ -4322,9 +4883,13 @@ following content:
``` tex
\input markdown
\input lmfonts
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
\input optionfalse
\par
\input optiontrue
+\endgroup
\bye
```````
Using a text editor, create a text document named `content.md` with the
@@ -4396,10 +4961,10 @@ following content:
``This is no longer a code span.''
\end{markdown}
-\begin{markdown*}{codeSpans=false}
+\begin{markdown}[codeSpans=false]
``This is a quote.''
``This is another quote.''
-\end{markdown*}
+\end{markdown}
\end{document}
```````
@@ -4437,7 +5002,7 @@ following content:
````````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
`````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -4479,9 +5044,9 @@ defaultOptions.codeSpans = true
: true
: Enable the
-% iA\,Writer content blocks syntax extension~[@sotkov17]:
+% iA\\,Writer content blocks syntax extension~[@sotkov17]:
% \iffalse
- iA\,Writer content blocks syntax extension:
+ iA\\,Writer content blocks syntax extension:
% \fi
``` md
@@ -4496,7 +5061,7 @@ defaultOptions.codeSpans = true
: false
: Disable the
- iA\,Writer content blocks syntax extension.
+ iA\\,Writer content blocks syntax extension.
% \end{markdown}
% \iffalse
@@ -4601,7 +5166,7 @@ Create also a text document named `document.tex` with the following content:
````````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
`````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -4641,6 +5206,143 @@ defaultOptions.contentBlocks = false
%</lua,lua-cli>
%<*manual-options>
+#### Option `contentLevel`
+
+`contentLevel` (default value: `block`)
+
+% \fi
+% \begin{markdown}
+%
+% \Optitem[block]{contentLevel}{\opt{block}, \opt{inline}}
+%
+: block
+
+ : Treat content as a sequence of blocks.
+ ``` md
+ - this is a list
+ - it contains two items
+ ``````
+
+: inline
+
+ : Treat all content as inline content.
+ ``` md
+ - this is a text
+ - not a list
+ ``````
+
+% \end{markdown}
+% \iffalse
+
+##### Plain \TeX{} Example {.unnumbered}
+
+Using a text editor, create a text document named `document.tex` with the
+following content:
+``` tex
+\input markdown
+\def\markdownOptionContentLevel{inline}
+\markdownBegin
+- this is
+- a text
+\markdownEnd
+\bye
+```````
+Next, invoke LuaTeX from the terminal:
+``` sh
+luatex document.tex
+``````
+A PDF document named `document.pdf` should be produced and contain the
+following text:
+
+> \- this is - a text
+
+##### \LaTeX{} Example {.unnumbered}
+
+Using a text editor, create a text document named `document.tex` with the
+following content:
+``` tex
+\documentclass{article}
+\usepackage{markdown}
+\begin{document}
+
+\begin{markdown}
+- this is
+- a list
+\end{markdown}
+
+\begin{markdown}[contentLevel=inline]
+- this is
+- a text
+\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:
+
+> - this is
+> - a list
+>
+> \- this is - a text
+
+##### \Hologo{ConTeXt} Example {.unnumbered}
+
+Using a text editor, create a text document named `document.tex` with the
+following content:
+``` tex
+\usemodule[t][markdown]
+\starttext
+
+\startmarkdown
+- this is
+- a list
+\stopmarkdown
+
+\setupmarkdown[contentLevel = inline]
+\startmarkdown
+- this is
+- a text
+\stopmarkdown
+
+\stoptext
+````````
+Next, invoke LuaTeX from the terminal:
+``` sh
+context --luatex document.tex
+`````
+A PDF document named `document.pdf` should be produced and contain the
+following text:
+
+> - this is
+> - a list
+>
+> \- this is - a text
+
+%</manual-options>
+%<*tex>
+% \fi
+% \begin{macrocode}
+\@@_add_lua_option:nnn
+ { contentLevel }
+ { string }
+ { block }
+% \end{macrocode}
+% \iffalse
+%</tex>
+%<*lua,lua-cli>
+% \fi
+% \begin{macrocode}
+defaultOptions.contentLevel = "block"
+% \end{macrocode}
+% \par
+% \iffalse
+%</lua,lua-cli>
+%<*manual-options>
+
#### Option `debugExtensions`
`debugExtensions` (default value: `false`)
@@ -4685,7 +5387,7 @@ following content:
``` lua
local strike_through = {
api_version = 2,
- grammar_version = 2,
+ grammar_version = 4,
finalize_grammar = function(reader)
local nonspacechar = lpeg.P(1) - lpeg.S("\t ")
local doubleslashes = lpeg.P("//")
@@ -4699,7 +5401,7 @@ local strike_through = {
lpeg.V("Inline"), doubleslashes, doubleslashes
) / function(s) return {"\\st{", s, "}"} end
- reader.insert_pattern("Inline after Emph", read_strike_through,
+ reader.insert_pattern("Inline after LinkAndEmph", read_strike_through,
"StrikeThrough")
reader.add_special_character("/")
end
@@ -4737,24 +5439,28 @@ be produced and contain the following text:
"Block": [
"Blockquote",
"Verbatim",
+ "FencedCode (built-in fenced_code syntax extension)",
"ThematicBreak",
"BulletList",
"OrderedList",
- "Heading",
"DisplayHtml",
+ "Heading"
+ ],
+ "BlockOrParagraph": [
+ "Block",
"Paragraph",
"Plain"
],
+ "EndlineExceptions": [
+ "EndlineExceptions (built-in fenced_code syntax extension)"
+ ],
"Inline": [
"Str",
"Space",
"Endline",
- "UlOrStarLine",
- "Strong",
- "Emph",
+ "EndlineBreak",
+ "LinkAndEmph",
"StrikeThrough (user-defined \"./strike-through.lua\" syntax extension)",
- "Link",
- "Image",
"Code",
"AutoLinkUrl",
"AutoLinkEmail",
@@ -4893,7 +5599,7 @@ Term 2 with *inline markup*
````````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
`````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -4931,107 +5637,6 @@ defaultOptions.definitionLists = false
%</lua,lua-cli>
%<*manual-options>
-#### Option `eagerCache`
-
-`eagerCache` (default value: `true`)
-
-% \fi
-% \begin{markdown}
-%
-% \Optitem[true]{eagerCache}{\opt{true}, \opt{false}}
-%
-: true
-
- : Converted markdown documents will be cached in \Opt{cacheDir}. This can be
- useful for post-processing the converted documents and for recovering
- historical versions of the documents from the cache. However, it also
- produces a large number of auxiliary files on the disk and obscures the
- output of the Lua command-line interface when it is used for plumbing.
-
- This behavior will always be used if the \Opt{finalizeCache} option is
- enabled.
-
-: false
-
- : Converted markdown documents will not be cached. This decreases the number
- of auxiliary files that we produce and makes it easier to use the Lua
- command-line interface for plumbing.
-
- This behavior will only be used when the \Opt{finalizeCache} option is
- disabled. Recursive nesting of markdown document fragments is undefined
- behavior when \Opt{eagerCache} is disabled.
-
-% \end{markdown}
-% \iffalse
-
-##### Lua Module Example {.unnumbered}
-
-Using a text editor, create a text document named `hello.md` with the
-following content:
-``` md
-Hello *world*!
-``````
-Next, invoke LuaTeX from the terminal with the \Opt{eagerCache} option
-disabled:
-``` sh
-texlua ⟨CLI pathname⟩ eagerCache=false -- hello.md hello.tex
-```````
-where \meta{CLI pathname} corresponds to the location of the Lua CLI script file,
-such as `~/texmf/scripts/markdown/markdown-cli.lua` on UN\*X systems or
-`C:\Users\`\meta{Your username}`\texmf\scripts\markdown\markdown-cli.lua` on Windows
-systems. Use the command `kpsewhich -a markdown-cli.lua` to locate the Lua CLI
-script file using [Kpathsea][].
-
-A \TeX{} document named `hello.tex` should be produced and contain the
-following code:
-``` tex
-Hello \markdownRendererEmphasis{world}!\relax
-```
-
-***
-
-Invoke LuaTeX from the terminal again, this time with the \Opt{eagerCache}
-option enabled:
-
-``` tex
-texlua ⟨CLI pathname⟩ eagerCache=true -- hello.md hello.tex
-```
-
-A \TeX{} document named `hello.tex` should be produced and contain the
-following code:
-
-``` tex
-\input ./⟨hash⟩.md.tex\relax
-```
-
-Additionally, a \TeX{} document named `⟨hash⟩.md.tex` should be produced and
-contain the following code:
-
-``` tex
-Hello \markdownRendererEmphasis{world}!\relax
-```
-
-%</manual-options>
-%<*tex>
-% \fi
-% \begin{macrocode}
-\@@_add_lua_option:nnn
- { eagerCache }
- { boolean }
- { true }
-% \end{macrocode}
-% \iffalse
-%</tex>
-%<*lua,lua-cli>
-% \fi
-% \begin{macrocode}
-defaultOptions.eagerCache = true
-% \end{macrocode}
-% \par
-% \iffalse
-%</lua,lua-cli>
-%<*manual-options>
-
#### Option `expectJekyllData`
`expectJekyllData` (default value: `false`)
@@ -5175,7 +5780,7 @@ defaultOptions.expectJekyllData = false
% ``` lua
% local strike_through = {
% api_version = 2,
-% grammar_version = 2,
+% grammar_version = 4,
% finalize_grammar = function(reader)
% local nonspacechar = lpeg.P(1) - lpeg.S("\t ")
% local doubleslashes = lpeg.P("//")
@@ -5189,7 +5794,7 @@ defaultOptions.expectJekyllData = false
% lpeg.V("Inline"), doubleslashes, doubleslashes
% ) / function(s) return {"\\st{", s, "}"} end
%
-% reader.insert_pattern("Inline after Emph", read_strike_through,
+% reader.insert_pattern("Inline after LinkAndEmph", read_strike_through,
% "StrikeThrough")
% reader.add_special_character("/")
% end
@@ -5210,7 +5815,7 @@ defaultOptions.expectJekyllData = false
% \fi
% \begin{macrocode}
metadata.user_extension_api_version = 2
-metadata.grammar_version = 2
+metadata.grammar_version = 4
% \end{macrocode}
% \iffalse
%</lua>
@@ -5218,17 +5823,17 @@ metadata.grammar_version = 2
% \fi
% \begin{markdown}
%
-% Any changes to the syntax extension API or grammar will cause the
-% corresponding current version to be incremented. After Markdown 3.0.0,
-% any changes to the API and the grammar will be either backwards-compatible
-% or constitute a breaking change that will cause the major version of the
-% Markdown package to increment (to 4.0.0).
+% Any changes to the syntax extension API or grammar will cause the
+% corresponding current version to be incremented. After Markdown 3.0.0,
+% any changes to the API and the grammar will be either backwards-compatible
+% or constitute a breaking change that will cause the major version of the
+% Markdown package to increment (to 4.0.0).
%
-% The `finalize_grammar` field is a function that finalizes the grammar of
-% markdown using the interface of a Lua \luamref{reader} object, such as
-% the \luamref{reader->insert_pattern} and
-% \luamref{reader->add_special_character} methods,
-% see Section <#lua-user-extensions>.
+% The `finalize_grammar` field is a function that finalizes the grammar of
+% markdown using the interface of a Lua \luamref{reader} object, such as
+% the \luamref{reader->insert_pattern} and
+% \luamref{reader->add_special_character} methods,
+% see Section <#sec:lua-user-extensions>.
%
% \end{markdown}
% \iffalse
@@ -5240,7 +5845,7 @@ following content:
``` lua
local strike_through = {
api_version = 2,
- grammar_version = 2,
+ grammar_version = 4,
finalize_grammar = function(reader)
local nonspacechar = lpeg.P(1) - lpeg.S("\t ")
local doubleslashes = lpeg.P("//")
@@ -5254,7 +5859,7 @@ local strike_through = {
lpeg.V("Inline"), doubleslashes, doubleslashes
) / function(s) return {"\\st{", s, "}"} end
- reader.insert_pattern("Inline after Emph", read_strike_through,
+ reader.insert_pattern("Inline after LinkAndEmph", read_strike_through,
"StrikeThrough")
reader.add_special_character("/")
end
@@ -5379,7 +5984,7 @@ c) third item
````````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
`````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -5411,12 +6016,12 @@ defaultOptions.fancyLists = false
#### Option `fencedCode`
-`fencedCode` (default value: `false`)
+`fencedCode` (default value: `true`)
% \fi
% \begin{markdown}
%
-% \Optitem[false]{fencedCode}{\opt{true}, \opt{false}}
+% \Optitem[true]{fencedCode}{\opt{true}, \opt{false}}
%
: true
@@ -5455,7 +6060,7 @@ following content:
```` tex
\documentclass{article}
\usepackage{minted}
-\usepackage[fencedCode]{markdown}
+\usepackage{markdown}
\begin{document}
\begin{markdown}
~~~ js
@@ -5489,7 +6094,7 @@ following text:
> moveShip(5 * gravity, DOWN);
> }
> ```
->
+>
> ``` html
> <pre>
> <code>
@@ -5507,7 +6112,6 @@ Using a text editor, create a text document named `document.tex` with the
following content:
```` tex
\usemodule[t][markdown]
-\setupmarkdown[fencedCode = yes]
\definetyping [js]
\definetyping [html]
\setuptyping [html] [option=XML]
@@ -5534,7 +6138,7 @@ if (a > 3) {
````````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
`````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -5544,7 +6148,7 @@ following text:
> moveShip(5 * gravity, DOWN);
> }
> ```
->
+>
> ``` html
> <pre>
> <code>
@@ -5563,14 +6167,14 @@ following text:
\@@_add_lua_option:nnn
{ fencedCode }
{ boolean }
- { false }
+ { true }
% \end{macrocode}
% \iffalse
%</tex>
%<*lua,lua-cli>
% \fi
% \begin{macrocode}
-defaultOptions.fencedCode = false
+defaultOptions.fencedCode = true
% \end{macrocode}
% \par
% \iffalse
@@ -5873,7 +6477,7 @@ Hello *world*!
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the text
“Hello *world*!” A frozen cache will also be produced in the `cache.tex`
@@ -5889,9 +6493,9 @@ Hi *world*!
\stopmarkdown
\stoptext
```````
-Last, invoke pdfTeX without shell access from the terminal:
+Last, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A new PDF document named `document.pdf` should be produced and contain the
same text “Hello *world*!” Since we used the contents of the frozen cache using
@@ -5947,6 +6551,9 @@ Using a text editor, create a text document named `document.tex` with the
following content:
``` tex
\input markdown
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
\directlua{
local markdown = require("markdown")
local firstConvert = markdown.new({finalizeCache = true, frozenCacheCounter = 0})
@@ -5954,6 +6561,7 @@ following content:
local secondConvert = markdown.new({finalizeCache = true, frozenCacheCounter = 1})
local secondInput = "*world*!"
tex.sprint(firstConvert(firstInput) .. [[ ]] .. secondConvert(secondInput)) }
+\endgroup
\bye
```````
Then, invoke LuaTeX from the terminal:
@@ -5985,91 +6593,49 @@ defaultOptions.frozenCacheCounter = 0
%</lua,lua-cli>
%<*manual-options>
-#### Option `hardLineBreaks`
+#### Option `gfmAutoIdentifiers`
-`hardLineBreaks` (default value: `false`)
+`gfmAutoIdentifiers` (default value: `false`)
% \fi
% \begin{markdown}
-%
-% \Optitem[false]{hardLineBreaks}{\opt{true}, \opt{false}}
+% \Optitem[false]{gfmAutoIdentifiers}{\opt{true}, \opt{false}}
%
: true
- : Interpret all newlines within a paragraph as hard line breaks instead
- of spaces.
+ : Enable the Pandoc [GitHub-flavored auto identifiers syntax extension][pandoc-gfm-auto-identifiers]:
-: false
-
- : Interpret all newlines within a paragraph as spaces.
-
-% \end{markdown}
-% \iffalse
-
-##### \LaTeX{} Example {.unnumbered}
+ ``` md
+ The following heading received the identifier `123-sesame-street`:
-Using a text editor, create a text document named `document.tex` with the
-following content:
-``` tex
-\documentclass{article}
-\usepackage{markdown}
-\begin{document}
+ # 123 Sesame Street
+ ``````
-\begin{markdown}
-'Twas brillig, and the slithy toves
-Did gyre and gimble in the wabe;
-All mimsy were the borogoves,
-And the mome raths outgrabe.
-\end{markdown}
+ false
-\begin{markdown*}{hardLineBreaks}
-'Twas brillig, and the slithy toves
-Did gyre and gimble in the wabe;
-All mimsy were the borogoves,
-And the mome raths outgrabe.
-\end{markdown*}
+ : Disable the Pandoc GitHub-flavored auto identifiers syntax extension.
-\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:
+See also the option \Opt{autoIdentifiers}.
-> 'Twas brillig, and the slithy toves
-> Did gyre and gimble in the wabe;
-> All mimsy were the borogoves,
-> And the mome raths outgrabe.
->
-> 'Twas brillig, and the slithy toves
-> Did gyre and gimble in the wabe;
-> All mimsy were the borogoves,
-> And the mome raths outgrabe.
+ [pandoc-gfm-auto-identifiers]: https://pandoc.org/MANUAL.html#extension-gfm_auto_identifiers
+% \end{markdown}
+% \iffalse
%</manual-options>
%<*tex>
% \fi
% \begin{macrocode}
\@@_add_lua_option:nnn
- { hardLineBreaks }
+ { gfmAutoIdentifiers }
{ boolean }
{ false }
% \end{macrocode}
-% \begin{markdown}
-%
-% The hardLineBreaks option has been deprecated and will be removed in
-% Markdown 3.0.0. From then on, all line breaks within a paragraph will
-% be interpreted as soft line breaks.
-%
-% \end{markdown}
% \iffalse
%</tex>
%<*lua,lua-cli>
% \fi
% \begin{macrocode}
-defaultOptions.hardLineBreaks = false
+defaultOptions.gfmAutoIdentifiers = false
% \end{macrocode}
% \par
% \iffalse
@@ -6119,11 +6685,11 @@ following content:
#. Parish
\end{markdown}
-\begin{markdown*}{hashEnumerators}
+\begin{markdown}[hashEnumerators]
#. Bird
#. McHale
#. Parish
-\end{markdown*}
+\end{markdown}
\end{document}
```````
@@ -6170,7 +6736,7 @@ following content:
````````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
`````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -6258,12 +6824,12 @@ defaultOptions.headerAttributes = false
#### Option `html`
-`html` (default value: `false`)
+`html` (default value: `true`)
% \fi
% \begin{markdown}
%
-% \Optitem[false]{html}{\opt{true}, \opt{false}}
+% \Optitem[true]{html}{\opt{true}, \opt{false}}
%
: true
@@ -6288,9 +6854,12 @@ following content:
``` tex
\input markdown
\input lmfonts
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
\directlua{
local markdown = require("markdown")
- local convert = markdown.new()
+ local convert = markdown.new({html = false})
local newline = [[^^J^^J]]
local input =
"<div>*There is no block tag support.*</div>" .. newline ..
@@ -6301,13 +6870,14 @@ following content:
\par
\directlua{
local markdown = require("markdown")
- local convert = markdown.new({html = true})
+ local convert = markdown.new()
local input =
"<div>*There is block tag support.*</div>" .. newline ..
"*There is <inline tag="tag"></inline> support.*" .. newline ..
"_There is <!-- comment --> support._" .. newline ..
"_There is <? HTML instruction ?> support._"
tex.sprint(convert(input)) }
+\endgroup
\bye
```````
Then, invoke LuaTeX from the terminal:
@@ -6331,9 +6901,13 @@ following content:
``` tex
\input markdown
\input lmfonts
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
\input optionfalse
\par
\input optiontrue
+\endgroup
\bye
```````
Using a text editor, create a text document named `content.md` with the
@@ -6348,8 +6922,8 @@ _Is there <? HTML instruction ?> support?_
````````
Next, invoke LuaTeX from the terminal:
``` sh
-texlua ⟨CLI pathname⟩ -- content.md optionfalse.tex
-texlua ⟨CLI pathname⟩ html=true -- content.md optiontrue.tex
+texlua ⟨CLI pathname⟩ html=false -- content.md optionfalse.tex
+texlua ⟨CLI pathname⟩ -- content.md optiontrue.tex
luatex document.tex
```````
where \meta{CLI pathname} corresponds to the location of the Lua CLI script file,
@@ -6376,6 +6950,7 @@ following content:
\input markdown
\input lmfonts
+\def\markdownOptionHtml{false}
\markdownBegin
<div>
*There is no block tag support.*
@@ -6420,7 +6995,7 @@ following content:
\usepackage{markdown}
\begin{document}
-\begin{markdown}
+\begin{markdown}[html = false]
<div>
*There is no block tag support.*
</div>
@@ -6429,14 +7004,14 @@ _There is no <!-- comment --> support._
_There is no <? HTML instruction ?> support._
\end{markdown}
-\begin{markdown*}{html}
+\begin{markdown}
<div>
*There is block tag support.*
</div>
*There is <inline tag="tag"></inline> support.*
_There is <!-- comment --> support._
_There is <? HTML instruction ?> support._
-\end{markdown*}
+\end{markdown}
\end{document}
```````
@@ -6462,6 +7037,7 @@ following content:
\usemodule[t][markdown]
\starttext
+\setupmarkdown[html = no]
\startmarkdown
<div>
*There is no block tag support.*
@@ -6485,7 +7061,7 @@ _There is <? HTML instruction ?> support._
````````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
`````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -6504,14 +7080,14 @@ following text:
\@@_add_lua_option:nnn
{ html }
{ boolean }
- { false }
+ { true }
% \end{macrocode}
% \iffalse
%</tex>
%<*lua,lua-cli>
% \fi
% \begin{macrocode}
-defaultOptions.html = false
+defaultOptions.html = true
% \end{macrocode}
% \par
% \iffalse
@@ -6552,6 +7128,9 @@ following content:
``` tex
\input markdown
\input lmfonts
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
\directlua{
local markdown = require("markdown")
local input, convert_safe, convert_unsafe, paragraph
@@ -6566,6 +7145,7 @@ following content:
convert_unsafe(input)
)
}
+\endgroup
\bye
```````
Then, invoke LuaTeX from the terminal:
@@ -6588,10 +7168,11 @@ following content:
``` tex
\input markdown
\input lmfonts
-\input optionfalse
-
\begingroup
\catcode`\%=12
+\catcode`\#=12
+\input optionfalse
+\par
\input optiontrue
\endgroup
\bye
@@ -6667,9 +7248,9 @@ following content:
$\sqrt{-1}$ *equals* $i$.
\end{markdown}
-\begin{markdown*}{hybrid}
+\begin{markdown}[hybrid]
$\sqrt{-1}$ *equals* $i$.
-\end{markdown*}
+\end{markdown}
\end{document}
```````
@@ -6707,7 +7288,7 @@ $\sqrt{-1}$ *equals* $i$.
````````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
`````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -6906,7 +7487,7 @@ move down to type the note.]
````````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
`````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -6918,18 +7499,8 @@ following text:
%</manual-options>
%<*tex>
% \fi
-% \begin{markdown}
-%
-% The inlineFootnotes option has been deprecated and will be removed in
-% Markdown 3.0.0.
-%
-% \end{markdown}
% \begin{macrocode}
\@@_add_lua_option:nnn
- { inlineFootnotes }
- { boolean }
- { false }
-\@@_add_lua_option:nnn
{ inlineNotes }
{ boolean }
{ false }
@@ -6939,7 +7510,6 @@ following text:
%<*lua,lua-cli>
% \fi
% \begin{macrocode}
-defaultOptions.inlineFootnotes = false
defaultOptions.inlineNotes = false
% \end{macrocode}
% \par
@@ -7086,7 +7656,7 @@ age: 99
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -7315,7 +7885,7 @@ following content:
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -7346,6 +7916,77 @@ defaultOptions.lineBlocks = false
%</lua,lua-cli>
%<*manual-options>
+#### Option `mark`
+
+`mark` (default value: `false`)
+
+% \fi
+% \begin{markdown}
+%
+% \Optitem[false]{mark}{\opt{true}, \opt{false}}
+%
+: true
+
+ : Enable the Pandoc [mark syntax extension][pandoc-mark]:
+
+ ``` md
+ This ==is highlighted text.==
+ ``````
+
+: false
+
+ : Disable the Pandoc mark syntax extension.
+
+ [pandoc-mark]: https://pandoc.org/MANUAL.html#extension-mark
+
+% \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[mark]{markdown}
+\begin{document}
+\begin{markdown}
+
+This ==is highlighted text.==
+
+\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:
+
+> This <mark>is highlighted text.</mark>
+
+%</manual-options>
+%<*tex>
+% \fi
+% \begin{macrocode}
+\@@_add_lua_option:nnn
+ { mark }
+ { boolean }
+ { false }
+% \end{macrocode}
+% \iffalse
+%</tex>
+%<*lua,lua-cli>
+% \fi
+% \begin{macrocode}
+defaultOptions.mark = false
+% \end{macrocode}
+% \par
+% \iffalse
+%</lua,lua-cli>
+%<*manual-options>
+
#### Option `notes`
`notes` (default value: `false`)
@@ -7473,7 +8114,7 @@ isn't indented.
````````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
`````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -7499,18 +8140,8 @@ following text:
%</manual-options>
%<*tex>
% \fi
-% \begin{markdown}
-%
-% The footnotes option has been deprecated and will be removed in
-% Markdown 3.0.0.
-%
-% \end{markdown}
% \begin{macrocode}
\@@_add_lua_option:nnn
- { footnotes }
- { boolean }
- { false }
-\@@_add_lua_option:nnn
{ notes }
{ boolean }
{ false }
@@ -7520,7 +8151,6 @@ following text:
%<*lua,lua-cli>
% \fi
% \begin{macrocode}
-defaultOptions.footnotes = false
defaultOptions.notes = false
% \end{macrocode}
% \par
@@ -7605,7 +8235,7 @@ following content:
````````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
`````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -7639,12 +8269,12 @@ defaultOptions.pipeTables = false
#### Option `preserveTabs`
-`preserveTabs` (default value: `false`)
+`preserveTabs` (default value: `true`)
% \fi
% \begin{markdown}
%
-% \Optitem[false]{preserveTabs}{\opt{true}, \opt{false}}
+% \Optitem[true]{preserveTabs}{\opt{true}, \opt{false}}
%
: true
@@ -7663,14 +8293,14 @@ defaultOptions.pipeTables = false
\@@_add_lua_option:nnn
{ preserveTabs }
{ boolean }
- { false }
+ { true }
% \end{macrocode}
% \iffalse
%</tex>
%<*lua,lua-cli>
% \fi
% \begin{macrocode}
-defaultOptions.preserveTabs = false
+defaultOptions.preserveTabs = true
% \end{macrocode}
% \par
% \iffalse
@@ -7765,7 +8395,7 @@ following text:
%<*lua,lua-cli>
% \fi
% \begin{macrocode}
-defaultOptions.rawAttribute = true
+defaultOptions.rawAttribute = false
% \end{macrocode}
% \par
% \iffalse
@@ -7971,7 +8601,7 @@ following content:
````````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
`````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -8181,7 +8811,7 @@ following content:
````````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
`````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -8246,8 +8876,11 @@ Using a text editor, create a text document named `document.tex` with the
following content:
``` tex
\input markdown
-\def\markdownRendererEllipsis{. . .}
\input lmfonts
+\def\markdownRendererEllipsis{. . .}
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
\directlua{
local markdown = require("markdown")
local convert = markdown.new()
@@ -8259,6 +8892,7 @@ following content:
local convert = markdown.new({smartEllipses = true})
local input = "... and this is a victorian ellipsis."
tex.sprint(convert(input)) }
+\endgroup
\bye
```````
Then, invoke LuaTeX from the terminal:
@@ -8278,11 +8912,15 @@ Using a text editor, create a text document named `document.tex` with the
following content:
``` tex
\input markdown
-\def\markdownRendererEllipsis{. . .}
\input lmfonts
+\def\markdownRendererEllipsis{. . .}
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
\input optionfalse
\par
\input optiontrue
+\endgroup
\bye
```````
Using a text editor, create a text document named `content.md` with the
@@ -8357,9 +8995,9 @@ following content:
These are just three regular dots ...
\end{markdown}
-\begin{markdown*}{smartEllipses}
+\begin{markdown}[smartEllipses]
... and this is a victorian ellipsis.
-\end{markdown*}
+\end{markdown}
\end{document}
```````
@@ -8396,7 +9034,7 @@ These are just three regular dots ...
````````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
`````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -8466,14 +9104,14 @@ The following list respects the numbers specified in the markup:
5. fifth item
\end{markdown}
-\begin{markdown*}{startNumber=false}
+\begin{markdown}[startNumber=false]
The following list does not respect the numbers specified in the
markup:
3. third item
4. fourth item
5. fifth item
-\end{markdown*}
+\end{markdown}
\end{document}
```````
@@ -8524,7 +9162,7 @@ The following list respects the numbers specified in the markup:
````````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
`````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -8656,7 +9294,7 @@ This is ~~a lunar roving vehicle~~ strike-through text.
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -8771,7 +9409,7 @@ following content:
````````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
`````
A PDF document named `document.pdf` should be produced and contain the
text “Hello *world*!”
@@ -8861,7 +9499,7 @@ H~2~O is a liquid.
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -8953,7 +9591,7 @@ following content:
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -8981,7 +9619,132 @@ defaultOptions.superscripts = false
%</lua,lua-cli>
%<*manual-options>
-#### Option `tableCaptions`
+#### Option `tableAttributes`
+
+`tableAttributes` (default value: `false`)
+
+% \fi
+% \begin{markdown}
+%
+% \Optitem[false]{tableAttributes}{\opt{true}, \opt{false}}
+%
+: true
+
+ : Enable the assignment of HTML attributes to
+% table captions (see the \Opt{tableCaptions} option).
+% \iffalse
+ [table captions](#table-captions).
+% \fi
+
+ ``` md
+ | Right | Left | Default | Center |
+ |------:|:-----|---------|:------:|
+ | 12 | 12 | 12 | 12 |
+ | 123 | 123 | 123 | 123 |
+ | 1 | 1 | 1 | 1 |
+
+ : Demonstration of pipe table syntax. {#example-table}
+ ```
+
+: false
+
+ : Disable the assignment of HTML attributes to table captions.
+
+% \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[
+ pipeTables,
+ tableCaptions,
+ tableAttributes,
+ relativeReferences,
+]{markdown}
+\usepackage{expl3}
+\ExplSyntaxOn
+\markdownSetup{
+ renderers = {
+ tableAttributeContextBegin = {
+ \group_begin:
+ \markdownSetup{
+ renderers = {
+ attributeIdentifier = {
+ \markdownSetup{
+ renderers = {
+ tableAttributeContextEnd = {
+ \label{##1}
+ \group_end:
+ },
+ },
+ }
+ },
+ },
+ }
+ },
+ tableAttributeContextEnd = {
+ \group_end:
+ },
+ },
+}
+\ExplSyntaxOff
+\begin{document}
+\begin{markdown}
+See Table <#example-table>.
+
+| Right | Left | Default | Center |
+|------:|:-----|---------|:------:|
+| 12 | 12 | 12 | 12 |
+| 123 | 123 | 123 | 123 |
+| 1 | 1 | 1 | 1 |
+
+ : Demonstration of pipe table syntax. {#example-table}
+\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:
+
+> See Table 1.
+>
+> | Right | Left | Default | Center |
+> |------:|:-----|---------|:------:|
+> | 12 | 12 | 12 | 12 |
+> | 123 | 123 | 123 | 123 |
+> | 1 | 1 | 1 | 1 |
+>
+> : Table 1. Demonstration of pipe table syntax.
+
+%</manual-options>
+%<*tex>
+% \fi
+% \begin{macrocode}
+\@@_add_lua_option:nnn
+ { tableAttributes }
+ { boolean }
+ { false }
+% \end{macrocode}
+% \iffalse
+%</tex>
+%<*lua,lua-cli>
+% \fi
+% \begin{macrocode}
+defaultOptions.tableAttributes = false
+% \end{macrocode}
+% \par
+% \iffalse
+%</lua,lua-cli>
+%<*manual-options>
+
+#### Option `tableCaptions` {#table-captions}
`tableCaptions` (default value: `false`)
@@ -9077,7 +9840,7 @@ following content:
````````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
`````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -9194,7 +9957,7 @@ following content:
````````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
`````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -9314,7 +10077,7 @@ Hel% this is a comment
````````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
`````
A PDF document named `document.pdf` should be produced and contain the
text “Hello *world*!”
@@ -9376,6 +10139,9 @@ following content:
``` tex
\input markdown
\input lmfonts
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
\directlua{
local markdown = require("markdown")
local newline = [[^^J^^J]]
@@ -9384,6 +10150,7 @@ following content:
[[$E=mc^2$]] .. newline .. newline ..
[[$$\hat{f} \left ( \xi \right )= \int_{-\infty}^{\infty} f\left ( x \right ) e^{-i2\pi \xi x} dx$$]]
tex.sprint(convert(input)) }
+\endgroup
\bye
```````
Then, invoke LuaTeX from the terminal:
@@ -9404,9 +10171,13 @@ following content:
``` tex
\input markdown
\input lmfonts
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
\input optionfalse
\par
\input optiontrue
+\endgroup
\bye
```````
Using a text editor, create a text document named `content.md` with the
@@ -9432,11 +10203,11 @@ A PDF document named `document.pdf` should be produced and contain the
following text:
> \$E=mc^2\$
->
+>
> \$\$\hat{f} \left ( \xi \right )= \int_{-\infty}^{\infty} f\left ( x \right ) e^{-i2\pi \xi x} dx\$\$
>
> $E=mc^2$
->
+>
> $$\hat{f} \left ( \xi \right )= \int_{-\infty}^{\infty} f\left ( x \right ) e^{-i2\pi \xi x} dx$$
##### Plain \TeX{} Example {.unnumbered}
@@ -9513,7 +10284,7 @@ $$\hat{f} \left ( \xi \right )= \int_{-\infty}^{\infty} f\left ( x \right ) e^
````````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
`````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -9579,6 +10350,9 @@ following content:
``` tex
\input markdown
\input lmfonts
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
\directlua{
local markdown = require("markdown")
local newline = [[^^J^^J]]
@@ -9587,6 +10361,7 @@ following content:
[[\\(E=mc^2\\)]] .. newline .. newline ..
[[\\[\hat{f} \left ( \xi \right )= \int_{-\infty}^{\infty} f\left ( x \right ) e^{-i2\pi \xi x} dx\\]]]
tex.sprint(convert(input)) }
+\endgroup
\bye
```````
Then, invoke LuaTeX from the terminal:
@@ -9607,9 +10382,13 @@ following content:
``` tex
\input markdown
\input lmfonts
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
\input optionfalse
\par
\input optiontrue
+\endgroup
\bye
```````
Using a text editor, create a text document named `content.md` with the
@@ -9635,11 +10414,11 @@ A PDF document named `document.pdf` should be produced and contain the
following text:
> \\\(E=mc^2\\)
->
+>
> \\\[\hat{f} \left ( \xi \right )= \int_{-\infty}^{\infty} f\left ( x \right ) e^{-i2\pi \xi x} dx\\]
>
> \\(E=mc^2\\)
->
+>
> \\[\hat{f} \left ( \xi \right )= \int_{-\infty}^{\infty} f\left ( x \right ) e^{-i2\pi \xi x} dx\\]
##### Plain \TeX{} Example {.unnumbered}
@@ -9716,7 +10495,7 @@ following content:
````````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
`````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -9782,6 +10561,9 @@ following content:
``` tex
\input markdown
\input lmfonts
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
\directlua{
local markdown = require("markdown")
local newline = [[^^J^^J]]
@@ -9790,6 +10572,7 @@ following content:
[[\(E=mc^2\)]] .. newline .. newline ..
[[\[\hat{f} \left ( \xi \right )= \int_{-\infty}^{\infty} f\left ( x \right ) e^{-i2\pi \xi x} dx\]]]
tex.sprint(convert(input)) }
+\endgroup
\bye
```````
Then, invoke LuaTeX from the terminal:
@@ -9810,9 +10593,13 @@ following content:
``` tex
\input markdown
\input lmfonts
+\begingroup
+\catcode`\%=12
+\catcode`\#=12
\input optionfalse
\par
\input optiontrue
+\endgroup
\bye
```````
Using a text editor, create a text document named `content.md` with the
@@ -9838,11 +10625,11 @@ A PDF document named `document.pdf` should be produced and contain the
following text:
> (E=mc^2)
->
+>
> [\hat{f} \left ( \xi \right )= \int_{-\infty}^{\infty} f\left ( x \right ) e^{-i2\pi \xi x} dx]
>
> \(E=mc^2\)
->
+>
> \[\hat{f} \left ( \xi \right )= \int_{-\infty}^{\infty} f\left ( x \right ) e^{-i2\pi \xi x} dx\]
##### Plain \TeX{} Example {.unnumbered}
@@ -9919,7 +10706,7 @@ following content:
````````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
`````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -10010,13 +10797,13 @@ The following list is loose:
- third item
\end{markdown}
-\begin{markdown*}{tightLists=false}
+\begin{markdown}[tightLists=false]
The following list is now also loose:
- first item
- second item
- third item
-\end{markdown*}
+\end{markdown}
\end{document}
```````
@@ -10143,9 +10930,9 @@ following content:
This is _emphasized text_ and this is a math subscript: $m\_n$.
\end{markdown}
-\begin{markdown*}{underscores=false}
+\begin{markdown}[underscores=false]
This is *emphasized text* and this is a math subscript: $m_n$.
-\end{markdown*}
+\end{markdown}
\end{document}
```````
@@ -10182,7 +10969,7 @@ This is *emphasized text* and this is a math subscript: $m_n$.
````````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
`````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -10468,6 +11255,7 @@ end
% Plain \TeX{} Interface {#texinterface}
%------------------------
%
+% \end{markdown}
% \iffalse
%</tex>
%<*manual-interfaces>
@@ -10494,7 +11282,7 @@ following content:
$\sqrt{-1}$ *equals* $i$.
\markdownEnd
-\def\markdownOptionHybrid{true}
+\def\markdownOptionTexMathDollars{true}
\markdownBegin
$\sqrt{-1}$ *equals* $i$.
\markdownEnd
@@ -10522,6 +11310,7 @@ pdftex --shell-escape document.tex
%</manual-interfaces>
%<*tex>
% \fi
+% \begin{markdown}
%
% The plain \TeX{} interface provides macros for the typesetting of markdown
% input from within plain \TeX{}, for setting the Lua interface options (see
@@ -10605,9 +11394,11 @@ pdftex --shell-escape document.tex
% \bye
% ```````
%
-% The \mref{markdownInput} macro accepts a single parameter with the filename
-% of a markdown document and expands to the result of the conversion of the
-% input markdown document to plain \TeX{}.
+% You can use the \mref{markdownInput} macro to include markdown documents,
+% similarly to how you might use the \mref{input} \TeX{} primitive to include
+% \TeX{} documents. The \mref{markdownInput} macro accepts a single parameter
+% with the filename of a markdown document and expands to the result of the
+% conversion of the input markdown document to plain \TeX{}.
%
% \end{markdown}
% \begin{macrocode}
@@ -10648,12 +11439,33 @@ pdftex --shell-escape document.tex
% Section <#sec:lua-options>), while some of them are specific to the plain
% \TeX{} interface.
%
+% To determine whether plain \TeX{} is the top layer or if there are other
+% layers above plain \TeX{}, we take a look on whether the
+% \mdef{c_@@_top_layer_tl} token list has already been defined. If not,
+% we will assume that plain \TeX{} is the top layer.
+%
+% \end{markdown}
+% \begin{macrocode}
+\ExplSyntaxOn
+\tl_const:Nn \c_@@_option_layer_plain_tex_tl { plain_tex }
+\cs_generate_variant:Nn
+ \tl_const:Nn
+ { NV }
+\tl_if_exist:NF
+ \c_@@_top_layer_tl
+ {
+ \tl_const:NV
+ \c_@@_top_layer_tl
+ \c_@@_option_layer_plain_tex_tl
+ }
+% \end{macrocode}
+% \begin{markdown}
+%
% To enable the enumeration of plain \TeX{} options, we will maintain the
% \mdef{g_\@\@_plain_tex_options_seq} sequence.
%
% \end{markdown}
% \begin{macrocode}
-\ExplSyntaxOn
\seq_new:N \g_@@_plain_tex_options_seq
% \end{macrocode}
% \begin{markdown}
@@ -10666,7 +11478,6 @@ pdftex --shell-escape document.tex
% \begin{macrocode}
\prop_new:N \g_@@_plain_tex_option_types_prop
\prop_new:N \g_@@_default_plain_tex_options_prop
-\tl_const:Nn \c_@@_option_layer_plain_tex_tl { plain_tex }
\seq_gput_right:NV \g_@@_option_layers_seq \c_@@_option_layer_plain_tex_tl
\cs_new:Nn
\@@_add_plain_tex_option:nnn
@@ -10705,6 +11516,90 @@ following code in your plain \TeX{} document:
% \par
% \begin{markdown}
%
+% The plain \TeX{} options may be also be specified via the \mdef{markdownSetup}
+% macro. Here, the plain \TeX{} 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` if the `=`\meta{value} part has been omitted.
+% The \mref{markdownSetup} macro receives the options to set up as its only
+% argument.
+%
+% \end{markdown}
+% \begin{macrocode}
+\cs_new:Nn
+ \@@_setup:n
+ {
+ \keys_set:nn
+ { markdown/options }
+ { #1 }
+ }
+\cs_gset_eq:NN
+ \markdownSetup
+ \@@_setup:n
+% \end{macrocode}
+% \iffalse
+%</tex>
+%<*manual-options>
+
+Alternatively, you can also set plain \TeX{} options using the `\markdownSetup`
+\TeX{} macro. For example, to set the \Opt{taskLists} Lua option to `true`,
+you would include the following code in your plain \TeX{} document:
+
+``` tex
+\markdownSetup{taskLists = true}
+```
+
+%</manual-options>
+%<*tex>
+% \fi
+% \par
+% \begin{markdown}
+%
+% The
+% \mdef{markdownIfOption}`{`\meta{name}`}{`\meta{iftrue}`}{`\meta{iffalse}`}`
+% macro is provided for testing, whether the value of
+% `\markdownOption`\meta{name} is `true`. If the value is `true`, then
+% \meta{iftrue} is expanded, otherwise \meta{iffalse} is expanded.
+%
+% \end{markdown}
+% \begin{macrocode}
+\prg_new_conditional:Nnn
+ \@@_if_option:n
+ { TF, T, F }
+ {
+ \@@_get_option_type:nN
+ { #1 }
+ \l_tmpa_tl
+ \str_if_eq:NNF
+ \l_tmpa_tl
+ \c_@@_option_type_boolean_tl
+ {
+ \msg_error:nnxx
+ { markdown }
+ { expected-boolean-option }
+ { #1 }
+ { \l_tmpa_tl }
+ }
+ \@@_get_option_value:nN
+ { #1 }
+ \l_tmpa_tl
+ \str_if_eq:NNTF
+ \l_tmpa_tl
+ \c_@@_option_value_true_tl
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+\msg_new:nnn
+ { markdown }
+ { expected-boolean-option }
+ {
+ Option~#1~has~type~#2,~
+ but~a~boolean~was~expected.
+ }
+\let\markdownIfOption=\@@_if_option:nTF
+% \end{macrocode}
+% \begin{markdown}
+%
%#### Finalizing and Freezing the Cache
% The \mdef{markdownOptionFinalizeCache} option corresponds to the Lua
% interface \Opt{finalizeCache} option, which creates an output file
@@ -10777,11 +11672,9 @@ For more information, see the examples for the \Opt{finalizeCache} option.
% \fi
% \begin{markdown}
%
-% 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 `\directlua` primitive. It
-% defaults to `\jobname.markdown.lua`, where `\jobname` is the base name
-% of the document being typeset.
+% The \mdef{markdownOptionInputTempFileName} macro sets the filename of the
+% temporary input file that is created during the buffering of markdown text
+% from a \TeX{} source. It defaults to `\jobname.markdown.in`.
%
% The expansion of this macro must not contain quotation marks (`"`) or
% backslash symbols (`\`). Mind that \TeX{} engines tend to
@@ -10790,140 +11683,311 @@ For more information, see the examples for the \Opt{finalizeCache} option.
% \end{markdown}
% \begin{macrocode}
\@@_add_plain_tex_option:nnn
- { helperScriptFileName }
+ { inputTempFileName }
{ path }
- { \jobname.markdown.lua }
+ { \jobname.markdown.in }
% \end{macrocode}
% \par
% \begin{markdown}
%
-% The \Opt{helperScriptFileName} macro has been deprecated and
-% will be removed in Markdown 3.0.0. To control the filename of the helper Lua
-% script file, use the `\g_luabridge_helper_script_filename_str` macro from the
-% \pkg{lt3luabridge} package.
+% The \mdef{markdownOptionOutputDir} macro sets the path to the directory that
+% will contain the auxiliary cache files produced by the Lua implementation and
+% also the auxiliary files produced by the plain \TeX{} implementation. The
+% option defaults to `.` or, since \TeX{} Live 2024, to the value of the
+% `-output-directory` option of your \TeX{} engine.
%
-% \end{markdown}
-% \begin{macrocode}
-\str_new:N
- \g_luabridge_helper_script_filename_str
-\tl_gset:Nn
- \g_luabridge_helper_script_filename_str
- { \markdownOptionHelperScriptFileName }
-% \end{macrocode}
-% \begin{markdown}
+% The path must be set to the same value as the `-output-directory` option of
+% your \TeX{} engine for the package to function correctly. We need this macro
+% to make the Lua implementation aware where it should store the helper files.
+% The same limitations apply here as in the case of the
+% \Opt{inputTempFileName} macro.
%
-% The \mdef{markdownOptionInputTempFileName} macro sets the filename of the
-% temporary input file that is created during the buffering of markdown text
-% from a \TeX{} source. It defaults to `\jobname.markdown.in`. The same
-% limitations as in the case of the \Opt{helperScriptFileName}
-% macro apply here.
+% The \mref{markdownOptionOutputDir} macro has been deprecated and will be
+% removed in the next major version of the Markdown package. <!-- After the
+% macro has been removed, we should remove support for TeX Live 2023 and
+% earlier, where the automatic detection does not work. -->
%
% \end{markdown}
% \begin{macrocode}
-\@@_add_plain_tex_option:nnn
- { inputTempFileName }
- { path }
- { \jobname.markdown.in }
+\cs_generate_variant:Nn
+ \@@_add_plain_tex_option:nnn
+ { nnV }
% \end{macrocode}
-% \par
% \begin{markdown}
%
-% The \mdef{markdownOptionOutputTempFileName} macro sets the filename of the
-% temporary output file that is created during the conversion from markdown to
-% plain \TeX{} in \mref{markdownMode} other than `2` It defaults to
-% `\jobname.markdown.out`. The same limitations apply here as in the case
-% of the \Opt{helperScriptFileName} macro.
+% Use the \pkg{lt3luabridge} library to determine the default value of the
+% \mref{markdownOptionOutputDir} macro by using the environmental variable
+% `TEXMF_OUTPUT_DIRECTORY` that is available since TeX~Live 2024.
%
% \end{markdown}
% \begin{macrocode}
-\@@_add_plain_tex_option:nnn
- { outputTempFileName }
+\ExplSyntaxOff
+\input lt3luabridge.tex
+\ExplSyntaxOn
+\bool_if:nTF
+ {
+ \cs_if_exist_p:N
+ \luabridge_tl_set:Nn &&
+ (
+ \int_compare_p:nNn
+ { \g_luabridge_method_int }
+ =
+ { \c_luabridge_method_directlua_int } ||
+ \sys_if_shell_unrestricted_p:
+ )
+ }
+ {
+ \luabridge_tl_set:Nn
+ \l_tmpa_tl
+ { print(os.getenv("TEXMF_OUTPUT_DIRECTORY") or ".") }
+ }
+ {
+ \tl_set:Nn
+ \l_tmpa_tl
+ { . }
+ }
+\@@_add_plain_tex_option:nnV
+ { outputDir }
{ path }
- { \jobname.markdown.out }
+ \l_tmpa_tl
% \end{macrocode}
-% \par
+% \iffalse
+%</tex>
+%<*manual-options>
+
+The plain \TeX{} interface provides the following commands that you can use to
+specify the location of temporary files produced during the conversion from
+Markdown to \TeX{}:
+
+- `\markdownOptionInputTempFileName`,
+- `\markdownOptionOutputDir`,
+- `\markdownOptionCacheDir`, and
+- `\markdownOptionFrozenCacheFileName`.
+
+The `\markdownOptionCacheDir` and `\markdownOptionFrozenCacheFileName` commands
+correspond to the `cacheDir` and `frozenCacheFileName` Lua options.
+
+Using a text editor, create a folder named `output-directory` and a text
+document named `document.tex` with the following content:
+``` tex
+\input lmfonts
+\input markdown
+\def\markdownOptionInputTempFileName{temporary-input.md}
+\def\markdownOptionOutputDir{output-directory}
+\def\markdownOptionCacheDir{output-directory/cache-directory}
+\def\markdownOptionEagerCache{true}
+\def\markdownOptionFinalizeCache{true}
+\def\markdownOptionFrozenCacheFileName{output-directory/cache-directory/frozen-cache.tex}
+\markdownBegin
+Hello *world*!
+\markdownEnd
+\bye
+```````
+Next, invoke LuaTeX from the terminal:
+``` sh
+luatex --output-directory output-directory document.tex
+``````
+A text document named `temporary-input.md` should be produced in the folder
+named `output-directory` and contain the following text:
+
+> Hello \*world\*!
+
+A folder named `output-directory/cache-directory` should also be produced and
+contain fragments of the converted markdown document. LuaTeX does not need
+other temporary files to perform the conversion from markdown to \TeX{}. To
+produce the remaining temporary files, invoke pdfTeX from the terminal:
+``` sh
+pdftex --output-directory output-directory --shell-escape document.tex
+``````
+Text document named `temporary-output.md` should be produced in the folder
+named `output-directory`. The document will contain the input markdown
+document converted to \TeX{}.
+
+% \fi
% \begin{markdown}
-%
-% The \Opt{outputTempFileName} macro has been deprecated and
-% will be removed in Markdown 3.0.0.
-%
+
+#### No default token renderer prototypes {#plain}
+
+The Markdown package provides default definitions for token renderer prototypes
+using the `witiko/markdown/defaults`
+% theme (see Section~<sec:#themes>).
+% \iffalse
+theme.
+% \fi
+Although these default definitions provide a useful starting point for authors,
+they use extra resources, especially with higher-level \TeX{} formats such as
+\LaTeX{} and \Hologo{ConTeXt}. Furthermore, the default definitions may change
+at any time, which may pose a problem for maintainers of Markdown themes and
+templates who may require a stable output.
+
+The \mdef{markdownOptionPlain} macro specifies whether higher-level \TeX{}
+formats should only use the plain \TeX{} default definitions or whether they
+should also use the format-specific default definitions. Whereas plain \TeX{}
+default definitions only provide definitions for simple elements such as
+emphasis, strong emphasis, and paragraph separators, format-specific default
+definitions add support for more complex elements such as lists, tables, and
+citations. On the flip side, plain \TeX{} default definitions load no extra
+resources and are rather stable, whereas format-specific default definitions
+load extra resources and are subject to a more rapid change.
+
+Here is how you would enable the macro in a \LaTeX{} document:
+
+``` tex
+\usepackage[plain]{markdown}
+```````
+
+Here is how you would enable the macro in a \Hologo{ConTeXt} document:
+
+``` tex
+\def\markdownOptionPlain{true}
+\usemodule[t][markdown]
+```````
+
+The macro must be set before or during the loading of the package. Setting the
+macro after loading the package has no effect.
+
% \end{markdown}
+% \iffalse
+%</manual-options>
+%<*tex>
+% \fi
% \begin{macrocode}
-\str_new:N
- \g_luabridge_standard_output_filename_str
-\tl_gset:Nn
- \g_luabridge_standard_output_filename_str
- { \markdownOptionOutputTempFileName }
+\@@_add_plain_tex_option:nnn
+ { plain }
+ { boolean }
+ { false }
% \end{macrocode}
+% \iffalse
+%</tex>
+%<*manual-options>
+% \fi
% \begin{markdown}
-%
-% The \mdef{markdownOptionErrorTempFileName} macro sets the filename of the
-% temporary output file that is created when a Lua error is encountered during
-% the conversion from markdown to plain \TeX{} in \mref{markdownMode} other than
-% `2`. It defaults to `\jobname.markdown.err`. The same limitations
-% apply here as in the case of the \Opt{helperScriptFileName}
-% macro.
-%
+
+The \mdef{markdownOptionNoDefaults} macro specifies whether we should prevent
+the loading of default definitions or not. This is useful in contexts, where
+we want to have total control over how all elements are rendered.
+
+Here is how you would enable the macro in a \LaTeX{} document:
+
+``` tex
+\usepackage[noDefaults]{markdown}
+```````
+
+Here is how you would enable the macro in a \Hologo{ConTeXt} document:
+
+``` tex
+\def\markdownOptionNoDefaults{true}
+\usemodule[t][markdown]
+```````
+
+The macro must be set before or during the loading of the package. Setting the
+macro after loading the package has no effect.
+
% \end{markdown}
+% \iffalse
+%</manual-options>
+%<*tex>
+% \fi
% \begin{macrocode}
\@@_add_plain_tex_option:nnn
- { errorTempFileName }
- { path }
- { \jobname.markdown.err }
+ { noDefaults }
+ { boolean }
+ { false }
% \end{macrocode}
-% \par
% \begin{markdown}
%
-% The \Opt{errorTempFileName} macro has been deprecated and
-% will be removed in Markdown 3.0.0. To control the filename of the temporary
-% file for Lua errors, use the `\g_luabridge_error_output_filename_str` macro
-% from the \pkg{lt3luabridge} package.
+%#### Miscellaneous Options
+%
+% The \mdef{markdownOptionStripPercentSigns} macro controls whether a percent
+% sign (`\%`) at the beginning of a line will be discarded when buffering
+% Markdown input (see Section <#sec:buffering>) or not. Notably, this
+% enables the use of markdown when writing \TeX{} package documentation using
+% the \pkg{Doc} \LaTeX{}~package~[@mittelbach17] or similar. The recognized
+% values of the macro are `true` (discard) and `false` (retain). It defaults
+% to `false`.
%
% \end{markdown}
% \begin{macrocode}
-\str_new:N
- \g_luabridge_error_output_filename_str
-\tl_gset:Nn
- \g_luabridge_error_output_filename_str
- { \markdownOptionErrorTempFileName }
+\seq_gput_right:Nn
+ \g_@@_plain_tex_options_seq
+ { stripPercentSigns }
+\prop_gput:Nnn
+ \g_@@_plain_tex_option_types_prop
+ { stripPercentSigns }
+ { boolean }
+\prop_gput:Nnx
+ \g_@@_default_plain_tex_options_prop
+ { stripPercentSigns }
+ { false }
% \end{macrocode}
+% \iffalse
+%</tex>
+%<*manual-options>
+
+#### Package Documentation
+
+The \mdef{markdownOptionStripPercentSigns} macro controls whether a percent
+sign (`\%`) at the beginning of a line will be discarded when reading Markdown
+input from a \TeX{} document. This enables the use of markdown when writing
+\TeX{} package documentation using the [Doc \LaTeX{} package][doc] by Frank
+Mittelbach. The recognized values of the macro are `true` (discard) and `false`
+(retain). It defaults to `false`.
+
+ [doc]: https://ctan.org/pkg/doc (doc – Format LaTeX documentation)
+
+Using a text editor, create a text document named `document.dtx` with the
+following content:
+
+ ``` tex
+ % \iffalse
+ \documentclass{ltxdoc}
+ \usepackage[stripPercentSigns]{markdown}
+ \begin{document}
+ \DocInput{document.dtx}
+ \end{document}
+ % \fi
+ %
+ % \begin{markdown}
+ % Hello *world*!
+ % \end{markdown}
+ ```````
+
+Next, invoke LuaTeX from the terminal:
+``` sh
+lualatex document.dtx
+``````
+A PDF document named `document.pdf` should be produced and contain the text
+“Hello *world*!”
+
+%</manual-options>
+%<*tex>
+% \fi
% \begin{markdown}
%
-% The \mdef{markdownOptionOutputDir} macro sets the path to the directory that
-% will contain the auxiliary cache files produced by the Lua implementation and
-% also the auxiliary files produced by the plain \TeX{} implementation. The
-% option defaults to `.`.
-%
-% The path must be set to the same value as the `-output-directory` option of
-% your \TeX{} engine for the package to function correctly. We need this macro
-% to make the Lua implementation aware where it should store the helper files.
-% The same limitations apply here as in the case of the
-% \Opt{helperScriptFileName} macro.
+%#### Generating Plain \TeX{} Option Macros and Key-Values
%
-% \end{markdown}
-% \begin{macrocode}
-\@@_add_plain_tex_option:nnn
- { outputDir }
- { path }
- { . }
-% \end{macrocode}
-% \begin{markdown}
+% We define the command \mdef{@@_define_option_commands_and_keyvals:} that
+% defines plain \TeX{} macros and the key-value interface
+% of the \mref{markdownSetup} macro for the above plain \TeX{} options.
%
-% Here, we automatically define plain \TeX{} macros for the above plain \TeX{}
-% options.
+% The command also defines macros and key-values that map
+% directly to the options recognized by the Lua interface, such as
+% \mdef{markdownOptionHybrid} for the \Opt{hybrid} Lua option (see Section
+% <#sec:lua-options>), which are not processed by the plain \TeX{}
+% implementation, only passed along to Lua.
%
-% Furthemore, we also define macros that map directly to the options recognized
-% by the Lua interface, such as \mdef{markdownOptionHybrid} for the
-% \Opt{hybrid} Lua option (see Section <#sec:lua-options>), which are not
-% processed by the plain \TeX{} implementation, only passed along to Lua.
+% Furthermore, the command also defines options and key-values
+% for subsequently loaded layers that correspond to higher-level \TeX{} formats
+% such as \LaTeX{} and \Hologo{ConTeXt}.
%
% For the macros that correspond to the non-boolean options recognized by the
% Lua interface, the same limitations apply here in the case of the
-% \Opt{helperScriptFileName} macro.
+% \Opt{inputTempFileName} macro.
%
% \end{markdown}
% \begin{macrocode}
-\cs_new:Nn \@@_plain_tex_define_option_commands:
+\cs_new:Nn
+ \@@_define_option_commands_and_keyvals:
{
\seq_map_inline:Nn
\g_@@_option_layers_seq
@@ -10931,19 +11995,53 @@ For more information, see the examples for the \Opt{finalizeCache} option.
\seq_map_inline:cn
{ g_@@_ ##1 _options_seq }
{
- \@@_plain_tex_define_option_command:n
+ \@@_define_option_command:n
{ ####1 }
+% \end{macrocode}
+% \begin{markdown}
+%
+% To make it easier to copy-and-paste options from Pandoc [@macfarlane22] such
+% as `fancy_lists`, `header_attributes`, and `pipe_tables`, we accept
+% snake\\\_case in addition to camelCase variants of options. As a bonus,
+% studies [@sharif10] also show that snake\\\_case is faster to read than
+% camelCase.
+%
+% \end{markdown}
+% \begin{macrocode}
+ \@@_with_various_cases:nn
+ { ####1 }
+ {
+ \@@_define_option_keyval:nnn
+ { ##1 }
+ { ####1 }
+ { ########1 }
+ }
}
}
}
-\cs_new:Nn \@@_plain_tex_define_option_command:n
+\cs_new:Nn
+ \@@_define_option_command:n
{
- \@@_get_default_option_value:nN
- { #1 }
- \l_tmpa_tl
- \@@_set_option_value:nV
+% \end{macrocode}
+% \begin{markdown}
+%
+% Do not override options defined before loading the package.
+%
+% \end{markdown}
+% \begin{macrocode}
+ \@@_option_tl_to_csname:nN
{ #1 }
\l_tmpa_tl
+ \cs_if_exist:cF
+ { \l_tmpa_tl }
+ {
+ \@@_get_default_option_value:nN
+ { #1 }
+ \l_tmpa_tl
+ \@@_set_option_value:nV
+ { #1 }
+ \l_tmpa_tl
+ }
}
\cs_new:Nn
\@@_set_option_value:nn
@@ -11000,140 +12098,750 @@ For more information, see the examples for the \Opt{finalizeCache} option.
}
}
}
-\@@_plain_tex_define_option_commands:
+\cs_new:Nn
+ \@@_define_option_keyval:nnn
+ {
+ \prop_get:cnN
+ { g_@@_ #1 _option_types_prop }
+ { #2 }
+ \l_tmpa_tl
+ \str_if_eq:VVTF
+ \l_tmpa_tl
+ \c_@@_option_type_boolean_tl
+ {
+ \keys_define:nn
+ { markdown/options }
+ {
+% \end{macrocode}
+% \begin{markdown}
+%
+% For boolean options, we also accept `yes` as an alias for
+% `true` and `no` as an alias for `false`.
+%
+% \end{markdown}
+% \begin{macrocode}
+ #3 .code:n = {
+ \tl_set:Nx
+ \l_tmpa_tl
+ {
+ \str_case:nnF
+ { ##1 }
+ {
+ { yes } { true }
+ { no } { false }
+ }
+ { ##1 }
+ }
+ \@@_set_option_value:nV
+ { #2 }
+ \l_tmpa_tl
+ },
+ #3 .default:n = { true },
+ }
+ }
+ {
+ \keys_define:nn
+ { markdown/options }
+ {
+ #3 .code:n = {
+ \@@_set_option_value:nn
+ { #2 }
+ { ##1 }
+ },
+ }
+ }
+% \end{macrocode}
+% \begin{markdown}
+%
+% For options of type `clist`, we assume that \meta{key} is a regular English
+% noun in plural (such as `extensions`) and we also define the
+% \meta{singular key}`=`\meta{value} interface, where \meta{singular key} is
+% \meta{key} after stripping the trailing -s (such as `extension`). Rather
+% than setting the option to \meta{value}, this interface appends \meta{value}
+% to the current value as the rightmost item in the list.
+%
+% \end{markdown}
+% \begin{macrocode}
+ \str_if_eq:VVT
+ \l_tmpa_tl
+ \c_@@_option_type_clist_tl
+ {
+ \tl_set:Nn
+ \l_tmpa_tl
+ { #3 }
+ \tl_reverse:N
+ \l_tmpa_tl
+ \str_if_eq:enF
+ {
+ \tl_head:V
+ \l_tmpa_tl
+ }
+ { s }
+ {
+ \msg_error:nnn
+ { markdown }
+ { malformed-name-for-clist-option }
+ { #3 }
+ }
+ \tl_set:Nx
+ \l_tmpa_tl
+ {
+ \tl_tail:V
+ \l_tmpa_tl
+ }
+ \tl_reverse:N
+ \l_tmpa_tl
+ \tl_put_right:Nn
+ \l_tmpa_tl
+ {
+ .code:n = {
+ \@@_get_option_value:nN
+ { #2 }
+ \l_tmpa_tl
+ \clist_set:NV
+ \l_tmpa_clist
+ { \l_tmpa_tl, { ##1 } }
+ \@@_set_option_value:nV
+ { #2 }
+ \l_tmpa_clist
+ }
+ }
+ \keys_define:nV
+ { markdown/options }
+ \l_tmpa_tl
+ }
+ }
+\cs_generate_variant:Nn
+ \clist_set:Nn
+ { NV }
+\cs_generate_variant:Nn
+ \keys_define:nn
+ { nV }
+\cs_generate_variant:Nn
+ \@@_set_option_value:nn
+ { nV }
+\prg_generate_conditional_variant:Nnn
+ \str_if_eq:nn
+ { en }
+ { F }
+\msg_new:nnn
+ { markdown }
+ { malformed-name-for-clist-option }
+ {
+ Clist~option~name~#1~does~not~end~with~-s.
+ }
% \end{macrocode}
+% \begin{markdown}
+%
+% If plain \TeX{} is the top layer, we use the
+% \mref{@@_define_option_commands_and_keyvals:} macro to define plain \TeX{}
+% option macros and key-values immediately. Otherwise, we
+% postpone the definition until the upper layers have been loaded.
%
+% \end{markdown}
+% \begin{macrocode}
+\str_if_eq:VVT
+ \c_@@_top_layer_tl
+ \c_@@_option_layer_plain_tex_tl
+ {
+ \@@_define_option_commands_and_keyvals:
+ }
+\ExplSyntaxOff
+% \end{macrocode}
% \iffalse
%</tex>
%<*manual-options>
+% \fi
+% \begin{markdown}
-The plain \TeX{} interface provides the following commands that you can use to
-specify the location of temporary files produced during the conversion from
-Markdown to \TeX{}:
+### Themes {#themes}
-- `\markdownOptionHelperScriptFileName`,
-- `\markdownOptionInputTempFileName`,
-- `\markdownOptionOutputTempFileName`,
-- `\markdownOptionErrorTempFileName`,
-- `\markdownOptionOutputDir`,
-- `\markdownOptionCacheDir`, and
-- `\markdownOptionFrozenCacheFileName`.
+User-defined themes for the Markdown package provide a domain-specific
+interpretation of Markdown tokens. Themes allow the authors to achieve
+a specific look and other high-level goals without low-level programming.
-The `\markdownOptionCacheDir` and `\markdownOptionFrozenCacheFileName` commands
-correspond to the `cacheDir` and `frozenCacheFileName` Lua options.
+% The key-values `theme`=\meta{theme name} and `import`=\meta{theme name} load
+% a \TeX{} document (further referred to as *a theme*) named
+% `markdowntheme`\meta{munged theme name}`.tex`, where the *munged theme name*
+% is the *theme name* after the substitution of all forward slashes (`/`) for
+% an underscore (`_`). The theme name is *qualified* and contains no
+% underscores. A theme name 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 `\usetheme` macro [@tantau21, Section
+% 15.1].
+%
+% Theme names must be qualified to minimize naming conflicts between different
+% themes with a similar purpose. The preferred format of a theme name is
+% \meta{theme author}`/`\meta{theme purpose}`/`\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 to allow structure inside theme names without
+% dictating where the themes should be located inside the \TeX{} directory
+% structure. For example, loading a theme named `witiko/beamer/MU` would
+% load a \TeX{} document package named `markdownthemewitiko_beamer_MU.tex`.
+%
+% \end{markdown}
+% \iffalse
+%</manual-options>
+%<*tex>
+% \fi
+% \begin{macrocode}
+\ExplSyntaxOn
+\keys_define:nn
+ { markdown/options }
+ {
+ theme .code:n = {
+ \@@_set_theme:n
+ { #1 }
+ },
+ import .code:n = {
+ \tl_set:Nn
+ \l_tmpa_tl
+ { #1 }
+% \end{macrocode}
+% \begin{markdown}
+%
+% To ensure that keys containing forward slashes get passed correctly, we
+% replace all forward slashes in the input with backslash tokens with category
+% code letter and then undo the replacement. This means that if any unbraced
+% backslash tokens with category code letter exist in the input, they will be
+% replaced with forward slashes. However, this should be extremely rare.
+%
+% \end{markdown}
+% \begin{macrocode}
+ \tl_replace_all:NnV
+ \l_tmpa_tl
+ { / }
+ \c_backslash_str
+ \keys_set:nV
+ { markdown/options/import }
+ \l_tmpa_tl
+ },
+ }
+% \end{macrocode}
+% \begin{markdown}
+%
+% To keep track of the current theme when themes are nested, we will
+% maintain the \mdef{g_\@\@_themes_seq} stack of theme names.
+% For convenience, the name of the current theme is also available in the
+% \mdef{g_@@_current_theme_tl} macro.
+%
+% \end{markdown}
+% \begin{macrocode}
+\seq_new:N
+ \g_@@_themes_seq
+\tl_new:N
+ \g_@@_current_theme_tl
+\tl_gset:Nn
+ \g_@@_current_theme_tl
+ { }
+\seq_gput_right:NV
+ \g_@@_themes_seq
+ \g_@@_current_theme_tl
+\cs_new:Nn
+ \@@_set_theme:n
+ {
+% \end{macrocode}
+% \begin{markdown}
+%
+% First, we validate the theme name.
+%
+% \end{markdown}
+% \begin{macrocode}
+ \str_if_in:nnF
+ { #1 }
+ { / }
+ {
+ \msg_error:nnn
+ { markdown }
+ { unqualified-theme-name }
+ { #1 }
+ }
+ \str_if_in:nnT
+ { #1 }
+ { _ }
+ {
+ \msg_error:nnn
+ { markdown }
+ { underscores-in-theme-name }
+ { #1 }
+ }
+% \end{macrocode}
+% \begin{markdown}
+%
+% Next, we munge the theme name.
+%
+% \end{markdown}
+% \begin{macrocode}
+ \str_set:Nn
+ \l_tmpa_str
+ { #1 }
+ \str_replace_all:Nnn
+ \l_tmpa_str
+ { / }
+ { _ }
+% \end{macrocode}
+% \begin{markdown}
+%
+% Finally, we load the theme.
+%
+% \end{markdown}
+% \begin{macrocode}
+ \tl_gset:Nn
+ \g_@@_current_theme_tl
+ { #1 / }
+ \seq_gput_right:NV
+ \g_@@_themes_seq
+ \g_@@_current_theme_tl
+ \@@_load_theme:nV
+ { #1 }
+ \l_tmpa_str
+ \seq_gpop_right:NN
+ \g_@@_themes_seq
+ \l_tmpa_tl
+ \seq_get_right:NN
+ \g_@@_themes_seq
+ \l_tmpa_tl
+ \tl_gset:NV
+ \g_@@_current_theme_tl
+ \l_tmpa_tl
+ }
+\msg_new:nnnn
+ { markdown }
+ { unqualified-theme-name }
+ { Won't~load~theme~with~unqualified~name~#1 }
+ { Theme~names~must~contain~at~least~one~forward~slash }
+\msg_new:nnnn
+ { markdown }
+ { underscores-in-theme-name }
+ { Won't~load~theme~with~an~underscore~in~its~name~#1 }
+ { Theme~names~must~not~contain~underscores~in~their~names }
+\cs_generate_variant:Nn
+ \tl_replace_all:Nnn
+ { NnV }
+\ExplSyntaxOff
+% \end{macrocode}
+% \iffalse
+%</tex>
+%<*manual-options>
+% \fi
+% \par
+% \begin{markdown}
-Using a text editor, create a folder named `output-directory` and a text
-document named `document.tex` with the following content:
+Built-in plain \TeX{} themes provided with the Markdown package include:
+
+\pkg{witiko/tilde}
+
+: A theme that makes tilde (`~`) always typeset the non-breaking space even
+ when the \Opt{hybrid} Lua option is disabled.
+% ``` tex
+% \input markdown
+% \markdownSetup{import=witiko/tilde}
+% \markdownBegin
+% Bartel~Leendert van~der~Waerden
+% \markdownEnd
+% \bye
+% ```````
+% Typesetting the above document produces the following text:
+% “Bartel~Leendert van~der~Waerden”.
+%
+% \end{markdown}
+% \iffalse
+
+##### Plain \TeX{} Example {.unnumbered}
+
+Using a text editor, create a text document named `document.tex` with the
+following content:
``` tex
-\input lmfonts
\input markdown
-\def\markdownOptionHelperScriptFileName{helper-script.lua}
-\def\markdownOptionInputTempFileName{temporary-input.md}
-\def\markdownOptionOutputTempFileName{temporary-output.tex}
-\def\markdownOptionErrorTempFileName{error-output.txt}
-\def\markdownOptionOutputDir{output-directory}
-\def\markdownOptionCacheDir{output-directory/cache-directory}
-\markdownOptionFinalizeCachetrue
-\def\markdownOptionFrozenCacheFileName{output-directory/cache-directory/frozen-cache.tex}
+\markdownSetup{import=witiko/tilde}
\markdownBegin
-Hello *world*!
+Bartel~Leendert van~der~Waerden
\markdownEnd
\bye
```````
Next, invoke LuaTeX from the terminal:
``` sh
-luatex --output-directory output-directory document.tex
+luatex document.tex
``````
-A text document named `temporary-input.md` should be produced in the folder
-named `output-directory` and contain the following text:
-
-> Hello \*world\*!
+A PDF document named `document.pdf` should be produced and contain the
+following text, where the middot (`·`) denotes a non-breaking space:
-A folder named `output-directory/cache-directory` should also be produced and
-contain fragments of the converted markdown document. LuaTeX does not need
-other temporary files to perform the conversion from markdown to \TeX{}. To
-produce the remaining temporary files, invoke pdfTeX from the terminal:
-``` sh
-pdftex --output-directory output-directory --shell-escape document.tex
-``````
-Text documents named `helper-script.lua`, and `temporary-output.md` should be
-produced in the folder named `output-directory`. The document named
-`helper-script.lua` will contain Lua code that was executed to convert markdown
-to plain \TeX{}. The document named `temporary-output.tex` will contain the
-input markdown document converted to \TeX{}.
+> Bartel·Leendert van·der·Waerden
-No document named `error-output.txt` should be produced in the folder named
-`output-directory`. This document would only be produced if an error had occured
-while executing the Lua code. If this happens, please [file a
-bug](https://github.com/witiko/markdown/issues).
+% \fi
+% \par
+% \begin{markdown}
-#### Package Documentation
+\pkg{witiko/markdown/defaults}
-The \mdef{markdownOptionStripPercentSigns} macro controls whether a percent
-sign (`\%`) at the beginning of a line will be discarded when reading Markdown
-input from a \TeX{} document. This enables the use of markdown when writing
-\TeX{} package documentation using the [Doc \LaTeX{} package][doc] by Frank
-Mittelbach. The recognized values of the macro are `true` (discard) and `false`
-(retain). It defaults to `false`.
+: A plain \TeX{} theme with the default definitions of token renderer
+ prototypes for plain \TeX{}. This theme is loaded automatically together
+ with the package and explicitly loading it has no effect.
- [doc]: https://ctan.org/pkg/doc (doc – Format LaTeX documentation)
+% Please, see Section <#sec:themes-implementation> for implementation
+% details of the built-in plain \TeX{} themes.
-Using a text editor, create a text document named `document.dtx` with the
-following content:
+### Snippets {#snippets}
- ``` tex
- % \iffalse
- \documentclass{ltxdoc}
- \usepackage[stripPercentSigns]{markdown}
- \begin{document}
- \DocInput{document.dtx}
- \end{document}
- % \fi
- %
- % \begin{markdown}
- % Hello *world*!
- % \end{markdown}
- ```````
+% \end{markdown}
+% \iffalse
-Next, invoke LuaTeX from the terminal:
-``` sh
-lualatex document.dtx
-``````
-A PDF document named `document.pdf` should be produced and contain the text
-“Hello *world*!”
+User-defined themes provide global control over high-level goals.
+However, it is often desirable to change only some local aspects of a document.
+Snippets provide syntactic sugar for defining and invoking various
+options locally.
%</manual-options>
%<*tex>
% \fi
+% \par
% \begin{markdown}
%
-%#### Miscellaneous Options
+% We may set up options as *snippets* using the
+% \mdef{markdownSetupSnippet} macro and invoke them later. The
+% \mref{markdownSetupSnippet} macro receives two arguments: the name
+% of the snippet and the options to store.
%
-% The \mdef{markdownOptionStripPercentSigns} macro controls whether a percent
-% sign (`\%`) at the beginning of a line will be discarded when buffering
-% Markdown input (see Section <#sec:buffering>) or not. Notably, this
-% enables the use of markdown when writing \TeX{} package documentation using
-% the \pkg{Doc} \LaTeX{}~package~[@mittelbach17] or similar. The recognized
-% values of the macro are `true` (discard) and `false` (retain). It defaults
-% to `false`.
+% \end{markdown}
+% \begin{macrocode}
+\ExplSyntaxOn
+\prop_new:N
+ \g_@@_snippets_prop
+\cs_new:Nn
+ \@@_setup_snippet:nn
+ {
+ \tl_if_empty:nT
+ { #1 }
+ {
+ \msg_error:nnn
+ { markdown }
+ { empty-snippet-name }
+ { #1 }
+ }
+ \tl_set:NV
+ \l_tmpa_tl
+ \g_@@_current_theme_tl
+ \tl_put_right:Nn
+ \l_tmpa_tl
+ { #1 }
+ \@@_if_snippet_exists:nT
+ { #1 }
+ {
+ \msg_warning:nnV
+ { markdown }
+ { redefined-snippet }
+ \l_tmpa_tl
+ }
+ \prop_gput:NVn
+ \g_@@_snippets_prop
+ \l_tmpa_tl
+ { #2 }
+ }
+\cs_gset_eq:NN
+ \markdownSetupSnippet
+ \@@_setup_snippet:nn
+\msg_new:nnnn
+ { markdown }
+ { empty-snippet-name }
+ { Empty~snippet~name~#1 }
+ { Pick~a~non-empty~name~for~your~snippet }
+\msg_new:nnn
+ { markdown }
+ { redefined-snippet }
+ { Redefined~snippet~#1 }
+% \end{macrocode}
+% \begin{markdown}
+%
+% To decide whether a snippet exists, we can use the
+% \mdef{markdownIfSnippetExists} macro.
%
% \end{markdown}
% \begin{macrocode}
-\seq_gput_right:Nn
- \g_@@_plain_tex_options_seq
- { stripPercentSigns }
-\prop_gput:Nnn
- \g_@@_plain_tex_option_types_prop
- { stripPercentSigns }
- { boolean }
-\prop_gput:Nnx
- \g_@@_default_plain_tex_options_prop
- { stripPercentSigns }
- { false }
+\prg_new_conditional:Nnn
+ \@@_if_snippet_exists:n
+ { TF, T, F }
+ {
+ \tl_set:NV
+ \l_tmpa_tl
+ \g_@@_current_theme_tl
+ \tl_put_right:Nn
+ \l_tmpa_tl
+ { #1 }
+ \prop_get:NVNTF
+ \g_@@_snippets_prop
+ \l_tmpa_tl
+ \l_tmpb_tl
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+\cs_gset_eq:NN
+ \markdownIfSnippetExists
+ \@@_if_snippet_exists:nTF
+% \end{macrocode}
+% \begin{markdown}
+%
+% The option with key `snippet` invokes a snippet named \meta{value}.
+%
+% \end{markdown}
+% \begin{macrocode}
+\keys_define:nn
+ { markdown/options }
+ {
+ snippet .code:n = {
+ \tl_set:NV
+ \l_tmpa_tl
+ \g_@@_current_theme_tl
+ \tl_put_right:Nn
+ \l_tmpa_tl
+ { #1 }
+ \@@_if_snippet_exists:nTF
+ { #1 }
+ {
+ \prop_get:NVN
+ \g_@@_snippets_prop
+ \l_tmpa_tl
+ \l_tmpb_tl
+ \@@_setup:V
+ \l_tmpb_tl
+ }
+ {
+ \msg_error:nnV
+ { markdown }
+ { undefined-snippet }
+ \l_tmpa_tl
+ }
+ }
+ }
+\msg_new:nnn
+ { markdown }
+ { undefined-snippet }
+ { Can't~invoke~undefined~snippet~#1 }
+\cs_generate_variant:Nn
+ \@@_setup:n
+ { V }
\ExplSyntaxOff
% \end{macrocode}
% \iffalse
%</tex>
+%<*manual-options>
+% \fi
+% \par
+% \markdownBegin
+
+Here is how we can use snippets to store options and invoke them later
+in \LaTeX{}:
+
+``` 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}
+```````
+
+If the `romanNumerals` snippet were defined in the `jdoe/lists` theme,
+we could import the `jdoe/lists` theme and use the qualified name
+`jdoe/lists/romanNumerals` to invoke the snippet:
+
+``` tex
+\markdownSetup{import=jdoe/lists}
+\begin{markdown}[snippet=jdoe/lists/romanNumerals]
+
+The following ordered list will be preceded by roman numerals:
+
+3. tres
+4. quattuor
+
+\end{markdown}
+```````
+
+Alternatively, we can use the extended variant of the `import` \LaTeX{}
+option that allows us to import the `romanNumerals` snippet to the
+current namespace for easier access:
+
+``` tex
+\markdownSetup{
+ import = {
+ jdoe/lists = romanNumerals,
+ },
+}
+\begin{markdown}[snippet=romanNumerals]
+
+The following ordered list will be preceded by roman numerals:
+
+3. tres
+4. quattuor
+
+\end{markdown}
+```````
+
+Furthermore, we can also specify the name of the snippet in the current
+namespace, which can be different from the name of the snippet in the
+`jdoe/lists` theme. For example, we can make the snippet
+`jdoe/lists/romanNumerals` available under the name `roman`.
+
+``` tex
+\markdownSetup{
+ import = {
+ jdoe/lists = romanNumerals as roman,
+ },
+}
+\begin{markdown}[snippet=roman]
+
+The following ordered list will be preceded by roman numerals:
+
+3. tres
+4. quattuor
+
+\end{markdown}
+```````
+
+Several themes and/or snippets can be loaded at once using the extended
+variant of the `import` \LaTeX{} option:
+
+``` tex
+\markdownSetup{
+ import = {
+ jdoe/longpackagename/lists = {
+ arabic as arabic1,
+ roman,
+ alphabetic,
+ },
+ jdoe/anotherlongpackagename/lists = {
+ arabic as arabic2,
+ },
+ jdoe/yetanotherlongpackagename,
+ },
+}
+```````
+
+% \markdownEnd
+% \iffalse
+%</manual-options>
+%<*tex>
+% \fi
+% \begin{macrocode}
+\ExplSyntaxOn
+\tl_new:N
+ \l_@@_import_current_theme_tl
+\keys_define:nn
+ { markdown/options/import }
+ {
+% \end{macrocode}
+% \begin{markdown}
+%
+% If a theme name is given without a list of snippets to import,
+% we assume that an empty list was given.
+%
+% \end{markdown}
+% \begin{macrocode}
+ unknown .default:n = {},
+ unknown .code:n = {
+% \end{macrocode}
+% \begin{markdown}
+%
+% To ensure that keys containing forward slashes get passed correctly, we
+% replace all forward slashes in the input with backslash tokens with category
+% code letter and then undo the replacement. This means that if any unbraced
+% backslash tokens with category code letter exist in the input, they will be
+% replaced with forward slashes. However, this should be extremely rare.
+%
+% \end{markdown}
+% \begin{macrocode}
+ \tl_set_eq:NN
+ \l_@@_import_current_theme_tl
+ \l_keys_key_str
+ \tl_replace_all:NVn
+ \l_@@_import_current_theme_tl
+ \c_backslash_str
+ { / }
+% \end{macrocode}
+% \begin{markdown}
+%
+% Here, we import the snippets.
+%
+% \end{markdown}
+% \begin{macrocode}
+ \clist_map_inline:nn
+ { #1 }
+ {
+ \regex_extract_once:nnNTF
+ { ^(.*?)\s+as\s+(.*?)$ }
+ { ##1 }
+ \l_tmpa_seq
+ {
+ \seq_pop:NN
+ \l_tmpa_seq
+ \l_tmpa_tl
+ \seq_pop:NN
+ \l_tmpa_seq
+ \l_tmpa_tl
+ \seq_pop:NN
+ \l_tmpa_seq
+ \l_tmpb_tl
+ }
+ {
+ \tl_set:Nn
+ \l_tmpa_tl
+ { ##1 }
+ \tl_set:Nn
+ \l_tmpb_tl
+ { ##1 }
+ }
+ \tl_put_left:Nn
+ \l_tmpa_tl
+ { / }
+ \tl_put_left:NV
+ \l_tmpa_tl
+ \l_@@_import_current_theme_tl
+ \@@_setup_snippet:Vx
+ \l_tmpb_tl
+ { snippet = { \l_tmpa_tl } }
+ }
+% \end{macrocode}
+% \begin{markdown}
+%
+% Here, we load the theme.
+%
+% \end{markdown}
+% \begin{macrocode}
+ \@@_set_theme:V
+ \l_@@_import_current_theme_tl
+ },
+ }
+\cs_generate_variant:Nn
+ \tl_replace_all:Nnn
+ { NVn }
+\cs_generate_variant:Nn
+ \@@_set_theme:n
+ { V }
+\cs_generate_variant:Nn
+ \@@_setup_snippet:nn
+ { Vx }
+% \end{macrocode}
+% \iffalse
+%</tex>
%<*manual-tokens>
## Markdown Tokens
@@ -11200,19 +12908,25 @@ In this section, I will describe the individual token renderers.
% \begin{markdown}
#### Attribute Renderers
-The following macros are only produced, when the \Opt{headerAttributes} option
-is enabled.
+The following macros are only produced, when at least one of the following
+options for markdown attributes on different elements is enabled:
+
+- \Opt{autoIdentifiers}
+- \Opt{fencedCodeAttributes}
+- \Opt{gfmAutoIdentifiers}
+- \Opt{headerAttributes}
+- \Opt{inlineCodeAttributes}
+- \Opt{linkAttributes}
\mdef{markdownRendererAttributeIdentifier} represents the \meta{identifier} of
a markdown element (`id="`\meta{identifier}`"` in HTML and `#`\meta{identifier}
-in Markdown's \Opt{headerAttributes} syntax extension). The macro receives a
-single attribute that corresponds to the \meta{identifier}.
+in markdown attributes). The macro receives a single attribute that corresponds
+to the \meta{identifier}.
\mdef{markdownRendererAttributeClassName} represents the \meta{class name} of a
markdown element (`class="`\meta{class name} ...`"` in HTML and
-`.`\meta{class name} in Markdown's \Opt{headerAttributes} syntax extension).
-The macro receives a single attribute that corresponds to the \meta{class
-name}.
+`.`\meta{class name} in markdown attributes). The macro receives a single
+attribute that corresponds to the \meta{class name}.
\mdef{markdownRendererAttributeKeyValue} represents a HTML attribute in the form
\meta{key}`=`\meta{value} that is neither an identifier nor a class name.
@@ -11468,7 +13182,7 @@ A quote from William Shakespeare's King Lear:
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -11512,8 +13226,8 @@ enabled.
The \mdef{markdownRendererBracketedSpanAttributeContextBegin} and
\mdef{markdownRendererBracketedSpanAttributeContextEnd} macros represent the
-beginning and the end of an inline bracketed span in which the attributes of
-the span apply. The macros receive no arguments.
+beginning and the end of a context in which the attributes of
+an inline bracketed span apply. The macros receive no arguments.
% \end{markdown}
%
@@ -11834,7 +13548,7 @@ following content:
\usepackage[tightLists]{markdown}
\begin{document}
-\begin{markdown*}{
+\begin{markdown}[
renderers = {
interblockSeparator = {},
ulBeginTight = { (},
@@ -11847,15 +13561,15 @@ following content:
ulItemEnd = {},
ulEndTight = {).},
},
-}
+]
This is a tight list
- the first item
- the second item
- the third item
-\end{markdown*}
+\end{markdown}
-\begin{markdown*}{
+\begin{markdown}[
renderers = {
interblockSeparator = {%
:\par
@@ -11866,7 +13580,7 @@ This is a tight list
ulItemEnd = {.},
ulEnd = {\end{itemize}},
},
-}
+]
This is a loose list
- This is the first item
@@ -11874,7 +13588,7 @@ This is a loose list
- This is the second item
- This is the third item
-\end{markdown*}
+\end{markdown}
\end{document}
```````
@@ -11946,7 +13660,7 @@ This is a loose list
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -11987,6 +13701,194 @@ following text:
%
% \begin{markdown}
+#### Citation Renderers
+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 enabled. The macro receives the parameter
+`{`\meta{number of citations}`}` followed by \meta{suppress author}
+`{`\meta{prenote}`}{`\meta{postnote}`}{`\meta{name}`}` repeated
+\meta{number of citations} times. The \meta{suppress author} parameter is
+either the token `-`, when the author's name is to be suppressed, or `+`
+otherwise.
+
+% \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[citations]{markdown}
+\newcount\citationsCounter
+\newcount\citationsTotal
+\makeatletter
+\def\citations#1#2#3#4{%
+ a parenthesized citation \emph{#4}
+ \advance\citationsCounter by 1\relax
+ \ifx\relax#2\relax
+ \ifx\relax#3\relax\else
+ with a postfix \emph{#3}%
+ \fi
+ \else
+ with a prefix \emph{#2}%
+ \ifx\relax#3\relax\else
+ \ and a postfix \emph{#3}%
+ \fi
+ \fi
+ \ifnum\citationsCounter>\citationsTotal\relax
+ .%
+ \expandafter\@gobble
+ \else
+ , and
+ \fi\citations}
+\makeatother
+\markdownSetup{
+ renderers = {
+ cite = {%
+ \citationsCounter=1%
+ \citationsTotal=#1%
+ This is
+ \expandafter\citations
+ },
+ },
+}
+\begin{document}
+\begin{markdown}
+[see @abrahams90, pp. 12; @eijkhout91, pp. 34]
+\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:
+
+> This is a parenthesized citation *abrahams90* with a prefix see
+> and a postfix *pp. 12*, and a citation *eijkhout91* with a
+> postfix *pp. 34*.
+
+%</manual-tokens>
+%<*tex>
+% \fi
+%
+% \begin{macrocode}
+\def\markdownRendererCite{%
+ \markdownRendererCitePrototype}%
+\ExplSyntaxOn
+\seq_gput_right:Nn
+ \g_@@_renderers_seq
+ { cite }
+\prop_gput:Nnn
+ \g_@@_renderer_arities_prop
+ { cite }
+ { 1 }
+\ExplSyntaxOff
+% \end{macrocode}
+% \par
+%
+% \iffalse
+%</tex>
+%<*manual-tokens>
+% \fi
+%
+% \begin{markdown}
+
+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 enabled. The macro receives parameters in the same
+format as the \mref{markdownRendererCite} macro.
+
+% \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[citations]{markdown}
+\newcount\citationsCounter
+\newcount\citationsTotal
+\makeatletter
+\def\citations#1#2#3#4{%
+ a text citation \emph{#4}
+ \advance\citationsCounter by 1\relax
+ \ifx\relax#2\relax
+ \ifx\relax#3\relax\else
+ with a postfix \emph{#3}%
+ \fi
+ \else
+ with a prefix \emph{#2}%
+ \ifx\relax#3\relax\else
+ \ and a postfix \emph{#3}%
+ \fi
+ \fi
+ \ifnum\citationsCounter>\citationsTotal\relax
+ .%
+ \expandafter\@gobble
+ \else
+ , and
+ \fi\citations}
+\makeatother
+\markdownSetup{
+ renderers = {
+ textCite = {%
+ \citationsCounter=1%
+ \citationsTotal=#1%
+ This is
+ \expandafter\citations
+ },
+ },
+}
+\begin{document}
+\begin{markdown}
+@abrahams90 [pp. 12; also @eijkhout91]
+\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:
+
+> This is a text citation *abrahams90* with a postfix *pp. 12*,
+> and a citation *eijkhout91* with a prefix *also*.
+
+%</manual-tokens>
+%<*tex>
+% \fi
+%
+% \begin{macrocode}
+\def\markdownRendererTextCite{%
+ \markdownRendererTextCitePrototype}%
+\ExplSyntaxOn
+\seq_gput_right:Nn
+ \g_@@_renderers_seq
+ { textCite }
+\prop_gput:Nnn
+ \g_@@_renderer_arities_prop
+ { textCite }
+ { 1 }
+\ExplSyntaxOff
+% \end{macrocode}
+% \par
+%
+% \iffalse
+%</tex>
+%<*manual-tokens>
+% \fi
+%
+% \begin{markdown}
+
#### Code Block Renderers
The \mdef{markdownRendererInputVerbatim} macro represents a code
block. The macro receives a single argument that corresponds to the
@@ -12023,8 +13925,10 @@ filename of a file contaning the code block contents.
The \mdef{markdownRendererInputFencedCode} macro represents a fenced code
block. This macro will only be produced, when the \Opt{fencedCode} option is
-enabled. The macro receives two arguments that correspond to the filename of
-a file contaning the code block contents and to the code fence infostring.
+enabled. The macro receives three arguments that correspond to the filename of
+a file contaning the code block contents, the fully escaped code fence infostring
+that can be directly typeset, and the raw code fence infostring that can be used
+outside typesetting.
% \end{markdown}
%
@@ -12125,7 +14029,7 @@ following text except for the filename, which may differ:
\prop_gput:Nnn
\g_@@_renderer_arities_prop
{ inputFencedCode }
- { 2 }
+ { 3 }
\ExplSyntaxOff
% \end{macrocode}
% \par
@@ -12203,9 +14107,9 @@ following content:
$\sqrt{-1}$ *equals* $i$
\end{markdown}
-\begin{markdown*}{hybrid}
+\begin{markdown}[hybrid]
$\sqrt{-1}$ *equals* $i$
-\end{markdown*}
+\end{markdown}
\end{document}
```````
@@ -12248,7 +14152,7 @@ $\sqrt{-1}$ *equals* $i$
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -12293,8 +14197,8 @@ option is enabled.
The \mdef{markdownRendererCodeSpanAttributeContextBegin} and
\mdef{markdownRendererCodeSpanAttributeContextEnd} macros represent the beginning
-and the end of an inline code span in which the attributes of the inline code
-span apply. The macros receive no arguments.
+and the end of a context in which the attributes of an inline code span apply.
+The macros receive no arguments.
% \end{markdown}
%
@@ -12370,7 +14274,7 @@ following text:
#### Content Block Renderers {#texcontentblockrenderers}
-The \mdef{markdownRendererContentBlock} macro represents an iA\,Writer content
+The \mdef{markdownRendererContentBlock} macro represents an iA\\,Writer content
block. It receives four arguments: the local file or online image filename
extension cast to the lower case, the fully escaped \acro{uri} that can be
directly typeset, the raw \acro{uri} that can be used outside typesetting,
@@ -12406,7 +14310,7 @@ and the title of the content block.
% \begin{markdown}
The \mdef{markdownRendererContentBlockOnlineImage} macro represents an
-iA\,Writer online image content block. The macro receives the same arguments
+iA\\,Writer online image content block. The macro receives the same arguments
as \mref{markdownRendererContentBlock}.
% \end{markdown}
@@ -12438,7 +14342,7 @@ as \mref{markdownRendererContentBlock}.
%
% \begin{markdown}
-The \mdef{markdownRendererContentBlockCode} macro represents an iA\,Writer
+The \mdef{markdownRendererContentBlockCode} macro represents an iA\\,Writer
content block that was recognized as a file in a known programming language
by its filename extension $s$. If any `markdown-languages.json` file found
by \pkg{kpathsea}^[Filenames other than `markdown-languages.json` may
@@ -12593,7 +14497,7 @@ content:
``````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -12996,7 +14900,7 @@ following content:
\usepackage[definitionLists, tightLists]{markdown}
\begin{document}
-\begin{markdown*}{
+\begin{markdown}[
renderers = {
interblockSeparator = {%
:%
@@ -13018,7 +14922,7 @@ following content:
dlDefinitionBegin = \item,
dlEndTight = {\end{description}},
},
-}
+]
This is a tight definition list
Coffee
@@ -13030,9 +14934,9 @@ Milk
: white cold drink
: nutrient-rich
: produced on an industrial scale
-\end{markdown*}
+\end{markdown}
-\begin{markdown*}{
+\begin{markdown}[
renderers = {
interblockSeparator = {%
\def\markdownRendererInterblockSeparator{\par}%
@@ -13051,7 +14955,7 @@ Milk
dlDefinitionEnd = {},
dlEnd = {.},
},
-}
+]
This is a loose definition list
Coffee
@@ -13069,7 +14973,7 @@ Milk
: nutrient-rich
: produced on an industrial scale
-\end{markdown*}
+\end{markdown}
\end{document}
```````
@@ -13187,7 +15091,7 @@ Milk
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -13311,7 +15215,7 @@ The secret word is ...
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -13356,6 +15260,39 @@ span of text.
% \end{markdown}
%
% \iffalse
+%</manual-tokens>
+%<*tex>
+% \fi
+%
+% \begin{macrocode}
+\def\markdownRendererEmphasis{%
+ \markdownRendererEmphasisPrototype}%
+\ExplSyntaxOn
+\seq_gput_right:Nn
+ \g_@@_renderers_seq
+ { emphasis }
+\prop_gput:Nnn
+ \g_@@_renderer_arities_prop
+ { emphasis }
+ { 1 }
+\ExplSyntaxOff
+% \end{macrocode}
+% \par
+%
+% \iffalse
+%</tex>
+%<*manual-tokens>
+% \fi
+%
+% \begin{markdown}
+
+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.
+
+% \end{markdown}
+%
+% \iffalse
##### Plain \TeX{} Example {.unnumbered}
@@ -13433,7 +15370,7 @@ This is **strong emphasis**.
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -13447,39 +15384,6 @@ following text:
% \fi
%
% \begin{macrocode}
-\def\markdownRendererEmphasis{%
- \markdownRendererEmphasisPrototype}%
-\ExplSyntaxOn
-\seq_gput_right:Nn
- \g_@@_renderers_seq
- { emphasis }
-\prop_gput:Nnn
- \g_@@_renderer_arities_prop
- { emphasis }
- { 1 }
-\ExplSyntaxOff
-% \end{macrocode}
-% \par
-%
-% \iffalse
-%</tex>
-%<*manual-tokens>
-% \fi
-%
-% \begin{markdown}
-
-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.
-
-% \end{markdown}
-%
-% \iffalse
-%</manual-tokens>
-%<*tex>
-% \fi
-%
-% \begin{macrocode}
\def\markdownRendererStrongEmphasis{%
\markdownRendererStrongEmphasisPrototype}%
\ExplSyntaxOn
@@ -13606,7 +15510,7 @@ enabled.
The \mdef{markdownRendererFencedDivAttributeContextBegin} and
\mdef{markdownRendererFencedDivAttributeContextEnd} macros represent the beginning
-and the end of a div in which the attributes of the div apply. The macros
+and the end of a context in which the attributes of a div apply. The macros
receive no arguments.
% \end{markdown}
@@ -13721,12 +15625,12 @@ following text:
% \begin{markdown}
#### Header Attribute Context Renderers
-The following macros are only produced, when the \Opt{headerAttributes} option
-is enabled.
+The following macros are only produced, when the \Opt{autoIdentifiers},
+\Opt{gfmAutoIdentifiers}, or \Opt{headerAttributes} options are enabled.
The \mdef{markdownRendererHeaderAttributeContextBegin} and
\mdef{markdownRendererHeaderAttributeContextEnd} macros represent the beginning
-and the end of a section in which the attributes of a heading apply. The macros
+and the end of a context in which the attributes of a heading apply. The macros
receive no arguments.
% \end{markdown}
@@ -13790,13 +15694,6 @@ following text:
%</manual-tokens>
%<*tex>
% \fi
-% \begin{markdown}
-%
-% These semantics have been deprecated and will be changed in Markdown 3.0.0.
-% From then on, header attribute contexts will only span headings, not the
-% surrounding sections.
-%
-% \end{markdown}
% \begin{macrocode}
\def\markdownRendererHeaderAttributeContextBegin{%
\markdownRendererHeaderAttributeContextBeginPrototype}%
@@ -14096,7 +15993,7 @@ following content:
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -14129,17 +16026,13 @@ following text:
%
% \begin{markdown}
-#### HTML Comment Renderers
+#### 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.
-The \mdef{markdownRendererBlockHtmlCommentBegin} and
-\mdef{markdownRendererBlockHtmlCommentEnd} macros represent the beginning
-and the end of a block \acro{HTML} comment. The macros receive no arguments.
-
% \end{markdown}
%
% \iffalse
@@ -14155,8 +16048,6 @@ following content:
\markdownSetup{
renderers = {
inlineHtmlComment = {\marginnote{#1}},
- blockHtmlCommentBegin = {\begin{quote}},
- blockHtmlCommentEnd = {\end{quote}},
},
}
\begin{document}
@@ -14164,14 +16055,6 @@ following content:
A useful use of inline 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 notes. -->
-
-We can render block HTML comments as blockquotes:
-
-<!--
-Here is a block HTML comment with a code example that a programmer might understand:
-
- foo = bar + baz - 42
--->
\end{markdown}
\end{document}
```````
@@ -14184,13 +16067,6 @@ A PDF document named `document.pdf` should be produced and contain the
following body text:
> A useful use of HTML comments are side notes.
->
-> We can render block HTML comments as blockquotes:
->
-> > Here is a block HTML comment with a code example that a programmer might
-> > understand:
-> >
-> > foo = bar + baz - 42
The horizontal margins should contain the following text:
@@ -14213,28 +16089,6 @@ The horizontal margins should contain the following text:
{ inlineHtmlComment }
{ 1 }
\ExplSyntaxOff
-\def\markdownRendererBlockHtmlCommentBegin{%
- \markdownRendererBlockHtmlCommentBeginPrototype}%
-\ExplSyntaxOn
-\seq_gput_right:Nn
- \g_@@_renderers_seq
- { blockHtmlCommentBegin }
-\prop_gput:Nnn
- \g_@@_renderer_arities_prop
- { blockHtmlCommentBegin }
- { 0 }
-\ExplSyntaxOff
-\def\markdownRendererBlockHtmlCommentEnd{%
- \markdownRendererBlockHtmlCommentEndPrototype}%
-\ExplSyntaxOn
-\seq_gput_right:Nn
- \g_@@_renderers_seq
- { blockHtmlCommentEnd }
-\prop_gput:Nnn
- \g_@@_renderer_arities_prop
- { blockHtmlCommentEnd }
- { 0 }
-\ExplSyntaxOff
% \end{macrocode}
% \par
%
@@ -14374,7 +16228,7 @@ following content:
}^^A
{^^A
\everyeof={\noexpand}^^A
- \edef\filename{\@@input"\jobname.fetched" }^^A
+ \edef\filename{\@@@@input"\jobname.fetched" }^^A
\includegraphics[width=\textwidth]{\filename}^^A
}^^A
}
@@ -14429,7 +16283,7 @@ is enabled.
The \mdef{markdownRendererImageAttributeContextBegin} and
\mdef{markdownRendererImageAttributeContextEnd} macros represent the beginning
-and the end of an image in which the attributes of the image apply. The macros
+and the end of a context in which the attributes of an image apply. The macros
receive no arguments.
% \end{markdown}
@@ -14504,9 +16358,10 @@ following text:
%
% \begin{markdown}
-#### Interblock Separator Renderer
-The \mdef{markdownRendererInterblockSeparator} macro represents a separator
-between two markdown block elements. The macro receives no arguments.
+#### Interblock Separator Renderers
+The \mdef{markdownRendererInterblockSeparator} macro represents an interblock
+separator between two markdown block elements. The macro receives no
+arguments.
% \end{markdown}
%
@@ -14524,7 +16379,7 @@ following content:
\par
}
\markdownBegin
-Hello *world*!
+- Hello *world*!
_Foo_ bar!
\markdownEnd
@@ -14537,7 +16392,7 @@ luatex document.tex
A PDF document named `document.pdf` should be produced and contain the
following text:
-> Hello *world*!
+> - Hello *world*!
>
> *(The end of a block)*
>
@@ -14561,7 +16416,7 @@ following content:
}
\begin{document}
\begin{markdown}
-Hello *world*!
+- Hello *world*!
_Foo_ bar!
\end{markdown}
@@ -14574,7 +16429,7 @@ lualatex document.tex
A PDF document named `document.pdf` should be produced and contain the
following text:
-> Hello *world*!
+> - Hello *world*!
>
> *(The end of a block)*
>
@@ -14593,7 +16448,7 @@ following content:
}
\starttext
\startmarkdown
-Hello *world*!
+- Hello *world*!
_Foo_ bar!
\stopmarkdown
@@ -14601,12 +16456,12 @@ _Foo_ bar!
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
following text:
-> Hello *world*!
+> - Hello *world*!
>
> *(The end of a block)*
>
@@ -14638,7 +16493,82 @@ following text:
%
% \begin{markdown}
-#### Line Block Renderer
+Users can use more than one blank line to delimit two block to indicate the
+end of a series of blocks that make up a logical paragraph. This produces a
+paragraph separator instead of an interblock separator. Between some blocks,
+such as markdown paragraphs, a paragraph separator is always produced.
+
+The \mdef{markdownRendererParagraphSeparator} macro represents a paragraph
+separator. The macro receives no arguments.
+
+% \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{markdown}
+\markdownSetup{
+ renderers = {
+ paragraphSeparator = {%
+ \par
+ \emph{(The end of a paragraph)}%
+ \par
+ },
+ },
+}
+\begin{document}
+\begin{markdown}
+Hello *world*!
+
+_Foo_ bar!
+\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:
+
+> Hello *world*!
+>
+> *(The end of a paragraph)*
+>
+> _Foo_ bar!
+
+%</manual-tokens>
+%<*tex>
+% \fi
+%
+% \begin{macrocode}
+\def\markdownRendererParagraphSeparator{%
+ \markdownRendererParagraphSeparatorPrototype}%
+\ExplSyntaxOn
+\seq_gput_right:Nn
+ \g_@@_renderers_seq
+ { paragraphSeparator }
+\prop_gput:Nnn
+ \g_@@_renderer_arities_prop
+ { paragraphSeparator }
+ { 0 }
+\ExplSyntaxOff
+% \end{macrocode}
+% \par
+%
+% \iffalse
+%</tex>
+%<*manual-tokens>
+% \fi
+%
+% \begin{markdown}
+
+#### Line Block Renderers
The following macros are only produced, when the \Opt{lineBlocks} option
is enabled.
@@ -14722,7 +16652,7 @@ following content:
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -14769,7 +16699,114 @@ following text:
%
% \begin{markdown}
-#### Line Break Renderer
+#### Line Break Renderers
+
+The \mdef{markdownRendererSoftLineBreak} macro represents a soft line break.
+The macro receives no arguments.
+
+% \end{markdown}
+%
+% \iffalse
+
+##### Plain \TeX{} Example {.unnumbered}
+
+Using a text editor, create a text document named `document.tex` with the
+following content:
+``` tex
+\input markdown
+\def\markdownRendererSoftLineBreak{%
+ \par
+ {\it(A soft line break)}%
+ \par
+}
+\markdownInput{example.md}
+\bye
+```````
+Using a text editor, create a text document named `example.md` with the
+following content:
+
+``` md
+Hello world!
+_Foo_ bar!
+``````
+Next, invoke LuaTeX from the terminal:
+``` sh
+luatex document.tex
+``````
+A PDF document named `document.pdf` should be produced and contain the
+following text:
+
+> Hello *world*!
+>
+> *(A soft line break)*
+>
+> _Foo_ bar!
+
+##### \LaTeX{} Example {.unnumbered}
+
+Using a text editor, create a text document named `document.tex` with the
+following content:
+``` tex
+\documentclass{article}
+\usepackage{markdown}
+\markdownSetup{
+ renderers = {
+ softLineBreak = {%
+ \par
+ \emph{(A soft line break)}%
+ \par
+ },
+ },
+}
+\begin{document}
+\markdownInput{example.md}
+\end{document}
+```````
+Using a text editor, create a text document named `example.md` with the
+following content:
+
+``` md
+Hello world!
+_Foo_ bar!
+``````
+Next, invoke LuaTeX from the terminal:
+``` sh
+lualatex document.tex
+``````
+A PDF document named `document.pdf` should be produced and contain the
+following text:
+
+> Hello *world*!
+>
+> *(A soft line break)*
+>
+> _Foo_ bar!
+
+%</manual-tokens>
+%<*tex>
+% \fi
+% \begin{macrocode}
+\def\markdownRendererSoftLineBreak{%
+ \markdownRendererSoftLineBreakPrototype}%
+\ExplSyntaxOn
+\seq_gput_right:Nn
+ \g_@@_renderers_seq
+ { softLineBreak }
+\prop_gput:Nnn
+ \g_@@_renderer_arities_prop
+ { softLineBreak }
+ { 0 }
+\ExplSyntaxOff
+% \end{macrocode}
+% \par
+%
+% \iffalse
+%</tex>
+%<*manual-tokens>
+% \fi
+%
+% \begin{markdown}
+
The \mdef{markdownRendererHardLineBreak} macro represents a hard line break.
The macro receives no arguments.
@@ -14856,51 +16893,10 @@ following text:
%</manual-tokens>
%<*tex>
% \fi
-% \begin{markdown}
-%
-% The \mdef{markdownRendererLineBreak} and
-% \mdef{markdownRendererLineBreakPrototype} macros have been deprecated
-% and will be removed in Markdown 3.0.0.
-%
-% \end{markdown}
% \begin{macrocode}
+\def\markdownRendererHardLineBreak{%
+ \markdownRendererHardLineBreakPrototype}%
\ExplSyntaxOn
-\cs_new:Npn
- \markdownRendererHardLineBreak
- {
- \cs_if_exist:NTF
- \markdownRendererLineBreak
- {
- \markdownWarning
- {
- Line~break~renderer~has~been~deprecated,~
- to~be~removed~in~Markdown~3.0.0
- }
- \markdownRendererLineBreak
- }
- {
- \cs_if_exist:NTF
- \markdownRendererLineBreakPrototype
- {
- \markdownWarning
- {
- Line~break~renderer~prototype~has~been~deprecated,~
- to~be~removed~in~Markdown~3.0.0
- }
- \markdownRendererLineBreakPrototype
- }
- {
- \markdownRendererHardLineBreakPrototype
- }
- }
- }
-\seq_gput_right:Nn
- \g_@@_renderers_seq
- { lineBreak }
-\prop_gput:Nnn
- \g_@@_renderer_arities_prop
- { lineBreak }
- { 0 }
\seq_gput_right:Nn
\g_@@_renderers_seq
{ hardLineBreak }
@@ -15009,7 +17005,7 @@ Please visit [the link][ctan].
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -15049,7 +17045,7 @@ is enabled.
The \mdef{markdownRendererLinkAttributeContextBegin} and
\mdef{markdownRendererLinkAttributeContextEnd} macros represent the beginning
-and the end of a hyperlink in which the attributes of the hyperlink apply.
+and the end of a context in which the attributes of a hyperlink apply.
The macros receive no arguments.
% \end{markdown}
@@ -15124,6 +17120,73 @@ following text:
%
% \begin{markdown}
+#### Marked Text Renderer
+The following macro is only produced, when the \Opt{mark} option is enabled.
+
+The \mdef{markdownRendererMark} macro represents a span of marked or
+highlighted text. The macro receives a single argument that corresponds to
+the marked text.
+
+% \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[mark]{markdown}
+\usepackage{soul}
+\markdownSetup{
+ renderers = {
+ mark = {\hl{#1}},
+ },
+}
+\begin{document}
+\begin{markdown}
+
+This ==is highlighted text.==
+
+\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:
+
+> This <mark>is highlighted text.</mark>
+
+%</manual-tokens>
+%<*tex>
+% \fi
+%
+% \begin{macrocode}
+\def\markdownRendererMark{%
+ \markdownRendererMarkPrototype}%
+\ExplSyntaxOn
+\seq_gput_right:Nn
+ \g_@@_renderers_seq
+ { mark }
+\prop_gput:Nnn
+ \g_@@_renderer_arities_prop
+ { mark }
+ { 1 }
+\ExplSyntaxOff
+% \end{macrocode}
+% \par
+%
+% \iffalse
+%</tex>
+%<*manual-tokens>
+% \fi
+%
+% \begin{markdown}
+
#### Markdown Document Renderers
The \mdef{markdownRendererDocumentBegin} and \mdef{markdownRendererDocumentEnd}
macros represent the beginning and the end of a *markdown* document. The macros
@@ -15450,7 +17513,7 @@ This is some text[^1] and this is some other text[^2].
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -15461,51 +17524,10 @@ following text:
%</manual-tokens>
%<*tex>
% \fi
-% \begin{markdown}
-%
-% The \mdef{markdownRendererFootnote} and
-% \mdef{markdownRendererFootnotePrototype} macros have been deprecated
-% and will be removed in Markdown 3.0.0.
-%
-% \end{markdown}
% \begin{macrocode}
+\def\markdownRendererNote{%
+ \markdownRendererNotePrototype}%
\ExplSyntaxOn
-\cs_new:Npn
- \markdownRendererNote
- {
- \cs_if_exist:NTF
- \markdownRendererFootnote
- {
- \markdownWarning
- {
- Footnote~renderer~has~been~deprecated,~
- to~be~removed~in~Markdown~3.0.0
- }
- \markdownRendererFootnote
- }
- {
- \cs_if_exist:NTF
- \markdownRendererFootnotePrototype
- {
- \markdownWarning
- {
- Footnote~renderer~prototype~has~been~deprecated,~
- to~be~removed~in~Markdown~3.0.0
- }
- \markdownRendererFootnotePrototype
- }
- {
- \markdownRendererNotePrototype
- }
- }
- }
-\seq_gput_right:Nn
- \g_@@_renderers_seq
- { footnote }
-\prop_gput:Nnn
- \g_@@_renderer_arities_prop
- { footnote }
- { 1 }
\seq_gput_right:Nn
\g_@@_renderers_seq
{ note }
@@ -16074,7 +18096,7 @@ following content:
\usepackage[tightLists, startNumber]{markdown}
\begin{document}
-\begin{markdown*}{
+\begin{markdown}[
renderers = {
interblockSeparator = {},
olBeginTight = { (},
@@ -16092,15 +18114,15 @@ following content:
olItemEnd = {},
olEndTight = {).},
},
-}
+]
This is a tight list
1. item
2. item
3. item
-\end{markdown*}
+\end{markdown}
-\begin{markdown*}{
+\begin{markdown}[
renderers = {
interblockSeparator = {%
:\par
@@ -16122,7 +18144,7 @@ This is a tight list
olItemEnd = {.},
olEnd = {\end{enumerate}},
},
-}
+]
This is a loose list
1. item
@@ -16130,7 +18152,7 @@ This is a loose list
2. item
3. item
-\end{markdown*}
+\end{markdown}
\end{document}
```````
@@ -16223,7 +18245,7 @@ This is a loose list
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -16264,103 +18286,6 @@ following text:
%
% \begin{markdown}
-#### 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 enabled. The macro receives the parameter
-`{`\meta{number of citations}`}` followed by \meta{suppress author}
-`{`\meta{prenote}`}{`\meta{postnote}`}{`\meta{name}`}` repeated
-\meta{number of citations} times. The \meta{suppress author} parameter is
-either the token `-`, when the author's name is to be suppressed, or `+`
-otherwise.
-
-% \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[citations]{markdown}
-\newcount\citationsCounter
-\newcount\citationsTotal
-\makeatletter
-\def\citations#1#2#3#4{%
- a parenthesized citation \emph{#4}
- \advance\citationsCounter by 1\relax
- \ifx\relax#2\relax
- \ifx\relax#3\relax\else
- with a postfix \emph{#3}%
- \fi
- \else
- with a prefix \emph{#2}%
- \ifx\relax#3\relax\else
- \ and a postfix \emph{#3}%
- \fi
- \fi
- \ifnum\citationsCounter>\citationsTotal\relax
- .%
- \expandafter\@gobble
- \else
- , and
- \fi\citations}
-\makeatother
-\markdownSetup{
- renderers = {
- cite = {%
- \citationsCounter=1%
- \citationsTotal=#1%
- This is
- \expandafter\citations
- },
- },
-}
-\begin{document}
-\begin{markdown}
-[see @abrahams90, pp. 12; @eijkhout91, pp. 34]
-\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:
-
-> This is a parenthesized citation *abrahams90* with a prefix see
-> and a postfix *pp. 12*, and a citation *eijkhout91* with a
-> postfix *pp. 34*.
-
-%</manual-tokens>
-%<*tex>
-% \fi
-%
-% \begin{macrocode}
-\def\markdownRendererCite{%
- \markdownRendererCitePrototype}%
-\ExplSyntaxOn
-\seq_gput_right:Nn
- \g_@@_renderers_seq
- { cite }
-\prop_gput:Nnn
- \g_@@_renderer_arities_prop
- { cite }
- { 1 }
-\ExplSyntaxOff
-% \end{macrocode}
-% \par
-%
-% \iffalse
-%</tex>
-%<*manual-tokens>
-% \fi
-%
-% \begin{markdown}
-
#### Raw Content Renderers
The \mdef{markdownRendererInputRawInline} macro represents an inline raw span.
The macro receives two arguments: the filename of a file contaning the inline
@@ -16648,7 +18573,7 @@ Bartel~Leendert van~der~Waerden
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
following text, where the middot (`·`) denotes a non-breaking space:
@@ -16868,7 +18793,7 @@ This is ~~a lunar roving vehicle~~ strike-through text.
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -16976,7 +18901,7 @@ H~2~O is a liquid.
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -17083,7 +19008,7 @@ following content:
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -17116,6 +19041,126 @@ following text:
%
% \begin{markdown}
+#### Table Attribute Context Renderers
+The following macros are only produced, when the \Opt{tableCaptions} and
+\Opt{tableAttributes} options are enabled.
+
+The \mdef{markdownRendererTableAttributeContextBegin} and
+\mdef{markdownRendererTableAttributeContextEnd} macros represent the
+beginning and the end of a context in which the attributes of a table
+apply. The macros receive no arguments.
+
+% \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[
+ pipeTables,
+ tableCaptions,
+ tableAttributes,
+ relativeReferences,
+]{markdown}
+\usepackage{expl3}
+\ExplSyntaxOn
+\markdownSetup{
+ renderers = {
+ tableAttributeContextBegin = {
+ \group_begin:
+ \markdownSetup{
+ renderers = {
+ attributeIdentifier = {
+ \markdownSetup{
+ renderers = {
+ tableAttributeContextEnd = {
+ \label{##1}
+ \group_end:
+ },
+ },
+ }
+ },
+ },
+ }
+ },
+ tableAttributeContextEnd = {
+ \group_end:
+ },
+ },
+}
+\ExplSyntaxOff
+\begin{document}
+\begin{markdown}
+See Table <#example-table>.
+
+| Right | Left | Default | Center |
+|------:|:-----|---------|:------:|
+| 12 | 12 | 12 | 12 |
+| 123 | 123 | 123 | 123 |
+| 1 | 1 | 1 | 1 |
+
+ : Demonstration of pipe table syntax. {#example-table}
+\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:
+
+> See Table 1.
+>
+> | Right | Left | Default | Center |
+> |------:|:-----|---------|:------:|
+> | 12 | 12 | 12 | 12 |
+> | 123 | 123 | 123 | 123 |
+> | 1 | 1 | 1 | 1 |
+>
+> : Table 1. Demonstration of pipe table syntax.
+
+%</manual-tokens>
+%<*tex>
+% \fi
+%
+% \begin{macrocode}
+\def\markdownRendererTableAttributeContextBegin{%
+ \markdownRendererTableAttributeContextBeginPrototype}%
+\ExplSyntaxOn
+\seq_gput_right:Nn
+ \g_@@_renderers_seq
+ { tableAttributeContextBegin }
+\prop_gput:Nnn
+ \g_@@_renderer_arities_prop
+ { tableAttributeContextBegin }
+ { 0 }
+\ExplSyntaxOff
+\def\markdownRendererTableAttributeContextEnd{%
+ \markdownRendererTableAttributeContextEndPrototype}%
+\ExplSyntaxOn
+\seq_gput_right:Nn
+ \g_@@_renderers_seq
+ { tableAttributeContextEnd }
+\prop_gput:Nnn
+ \g_@@_renderer_arities_prop
+ { tableAttributeContextEnd }
+ { 0 }
+\ExplSyntaxOff
+% \end{macrocode}
+% \par
+%
+% \iffalse
+%</tex>
+%<*manual-tokens>
+% \fi
+%
+% \begin{markdown}
+
#### Table Renderer
The \mdef{markdownRendererTable} macro represents a table. This macro will only
be produced, when the \Opt{pipeTables} option is enabled. The macro receives the
@@ -17243,10 +19288,10 @@ following text:
%
% \begin{markdown}
-#### Tex Math Renderers
+#### \TeX{} Math Renderers
The \mdef{markdownRendererInlineMath} and \mdef{markdownRendererDisplayMath} macros
represent inline and display \TeX{} math.
-Both macros receive a single argument that corresponds to the tex math content.
+Both macros receive a single argument that corresponds to the \TeX{} math content.
These macros will only be produced, when the \Opt{texMathDollars},
\Opt{texMathSingleBackslash}, or \Opt{texMathDoubleBackslash} option are enabled.
@@ -17329,7 +19374,7 @@ $$\hat{f} \left ( \xi \right )= \int_{-\infty}^{\infty} f\left ( x \right ) e^
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -17375,98 +19420,6 @@ following text:
%
% \begin{markdown}
-#### 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 enabled. The macro receives parameters in the same
-format as the \mref{markdownRendererCite} macro.
-
-% \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[citations]{markdown}
-\newcount\citationsCounter
-\newcount\citationsTotal
-\makeatletter
-\def\citations#1#2#3#4{%
- a text citation \emph{#4}
- \advance\citationsCounter by 1\relax
- \ifx\relax#2\relax
- \ifx\relax#3\relax\else
- with a postfix \emph{#3}%
- \fi
- \else
- with a prefix \emph{#2}%
- \ifx\relax#3\relax\else
- \ and a postfix \emph{#3}%
- \fi
- \fi
- \ifnum\citationsCounter>\citationsTotal\relax
- .%
- \expandafter\@gobble
- \else
- , and
- \fi\citations}
-\makeatother
-\markdownSetup{
- renderers = {
- textCite = {%
- \citationsCounter=1%
- \citationsTotal=#1%
- This is
- \expandafter\citations
- },
- },
-}
-\begin{document}
-\begin{markdown}
-@abrahams90 [pp. 12; also @eijkhout91]
-\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:
-
-> This is a text citation *abrahams90* with a postfix *pp. 12*,
-> and a citation *eijkhout91* with a prefix *also*.
-
-%</manual-tokens>
-%<*tex>
-% \fi
-%
-% \begin{macrocode}
-\def\markdownRendererTextCite{%
- \markdownRendererTextCitePrototype}%
-\ExplSyntaxOn
-\seq_gput_right:Nn
- \g_@@_renderers_seq
- { textCite }
-\prop_gput:Nnn
- \g_@@_renderer_arities_prop
- { textCite }
- { 1 }
-\ExplSyntaxOff
-% \end{macrocode}
-% \par
-%
-% \iffalse
-%</tex>
-%<*manual-tokens>
-% \fi
-%
-% \begin{markdown}
-
#### Thematic Break Renderer
The \mdef{markdownRendererThematicBreak} macro represents a thematic break.
The macro receives no arguments.
@@ -17558,7 +19511,7 @@ This is the second page.
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -17572,51 +19525,10 @@ following text:
%</manual-tokens>
%<*tex>
% \fi
-% \begin{markdown}
-%
-% The \mdef{markdownRendererHorizontalRule} and
-% \mdef{markdownRendererHorizontalRulePrototype} macros have been deprecated
-% and will be removed in Markdown 3.0.0.
-%
-% \end{markdown}
% \begin{macrocode}
+\def\markdownRendererThematicBreak{%
+ \markdownRendererThematicBreakPrototype}%
\ExplSyntaxOn
-\cs_new:Npn
- \markdownRendererThematicBreak
- {
- \cs_if_exist:NTF
- \markdownRendererHorizontalRule
- {
- \markdownWarning
- {
- Horizontal~rule~renderer~has~been~deprecated,~
- to~be~removed~in~Markdown~3.0.0
- }
- \markdownRendererHorizontalRule
- }
- {
- \cs_if_exist:NTF
- \markdownRendererHorizontalRulePrototype
- {
- \markdownWarning
- {
- Horizontal~rule~renderer~prototype~has~been~deprecated,~
- to~be~removed~in~Markdown~3.0.0
- }
- \markdownRendererHorizontalRulePrototype
- }
- {
- \markdownRendererThematicBreakPrototype
- }
- }
- }
-\seq_gput_right:Nn
- \g_@@_renderers_seq
- { horizontalRule }
-\prop_gput:Nnn
- \g_@@_renderer_arities_prop
- { horizontalRule }
- { 0 }
\seq_gput_right:Nn
\g_@@_renderers_seq
{ thematicBreak }
@@ -17695,7 +19607,7 @@ following content:
````````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
`````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -18182,15 +20094,300 @@ age: 99
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
following text:
> Jane Doe is 99 years old.
+%</manual-tokens>
+%<*tex>
% \fi
+% \begin{markdown}
+%
+%#### Generating Plain \TeX{} Token Renderer Macros and Key-Values {#plain-tex-renderers}
+%
+% We define the command \mdef{@@_define_renderers:} that defines plain \TeX{}
+% macros for token renderers. Futhermore, the `\markdownSetup` macro also accepts
+% the `renderers` key, whose value must be a list of key-values, where the keys
+% correspond to the markdown token renderer macros and the values are new
+% definitions of these token renderers.
+%
+% \end{markdown}
+% \begin{macrocode}
+\ExplSyntaxOn
+\cs_new:Nn \@@_define_renderers:
+ {
+ \seq_map_function:NN
+ \g_@@_renderers_seq
+ \@@_define_renderer:n
+ }
+\cs_new:Nn \@@_define_renderer:n
+ {
+ \@@_renderer_tl_to_csname:nN
+ { #1 }
+ \l_tmpa_tl
+ \prop_get:NnN
+ \g_@@_renderer_arities_prop
+ { #1 }
+ \l_tmpb_tl
+ \@@_define_renderer:ncV
+ { #1 }
+ { \l_tmpa_tl }
+ \l_tmpb_tl
+ }
+\cs_new:Nn \@@_renderer_tl_to_csname:nN
+ {
+ \tl_set:Nn
+ \l_tmpa_tl
+ { \str_uppercase:n { #1 } }
+ \tl_set:Nx
+ #2
+ {
+ markdownRenderer
+ \tl_head:f { \l_tmpa_tl }
+ \tl_tail:n { #1 }
+ }
+ }
+\tl_new:N
+ \l_@@_renderer_definition_tl
+\cs_new:Nn \@@_define_renderer:nNn
+ {
+ \keys_define:nn
+ { markdown/options/renderers }
+ {
+ #1 .code:n = {
+ \tl_set:Nn
+ \l_@@_renderer_definition_tl
+ { ##1 }
+ \regex_replace_all:nnN
+ { \cP\#0 }
+ { #1 }
+ \l_@@_renderer_definition_tl
+ \cs_generate_from_arg_count:NNnV
+ #2
+ \cs_set:Npn
+ { #3 }
+ \l_@@_renderer_definition_tl
+ },
+ }
+ }
+\cs_generate_variant:Nn
+ \@@_define_renderer:nNn
+ { ncV }
+\cs_generate_variant:Nn
+ \cs_generate_from_arg_count:NNnn
+ { NNnV }
+\keys_define:nn
+ { markdown/options }
+ {
+ renderers .code:n = {
+ \keys_set:nn
+ { markdown/options/renderers }
+ { #1 }
+ },
+ }
+\ExplSyntaxOff
+% \end{macrocode}
+% \par
+% \begin{markdown}
+%
+% The following example code showcases a possible configuration of the
+% \mref{markdownRendererLink} and \mref{markdownRendererEmphasis} token
+% renderer macros.
+% ``` tex
+% \markdownSetup{
+% renderers = {
+% link = {#4}, \% Render links as the link title.
+% emphasis = {{\it #1}}, \% Render emphasized text using italics.
+% }
+% }
+% ```````
+%
+% In addition to exact token renderer names, we also support wildcards
+% and enumerations that match multiple token renderer names:
+% ``` tex
+% \markdownSetup{
+% renderers = {
+% heading* = {{\bf #1}}, \% Render headings using the bold face.
+% jekyllData(String|Number) = { \% Render YAML string and numbers
+% {\it #2}\% \% using italics.
+% },
+% }
+% }
+% ```````
+%
+% Wildcards and enumerations can be combined:
+% ``` tex
+% \markdownSetup{
+% renderers = {
+% *lItem(|End) = {"}, \% Quote ordered/bullet list items.
+% }
+% }
+% ```````
+%
+% To determine the current token renderer, you can use the parameter `#0`:
+% ``` tex
+% \markdownSetup{
+% renderers = {
+% heading* = {#0: #1}, \% Render headings as the renderer name
+% } \% followed by the heading text.
+% }
+% ```````
+%
+% \end{markdown}
+% \begin{macrocode}
+\ExplSyntaxOn
+\prop_new:N
+ \g_@@_glob_cache_prop
+\tl_new:N
+ \l_@@_current_glob_tl
+\cs_new:Nn
+ \@@_glob_seq:nnN
+ {
+ \tl_set:Nn
+ \l_@@_current_glob_tl
+ { ^ #1 $ }
+ \prop_get:NeNTF
+ \g_@@_glob_cache_prop
+ { #2 / \l_@@_current_glob_tl }
+ \l_tmpa_clist
+ {
+ \seq_set_from_clist:NN
+ #3
+ \l_tmpa_clist
+ }
+ {
+ \seq_clear:N
+ #3
+ \regex_replace_all:nnN
+ { \* }
+ { .* }
+ \l_@@_current_glob_tl
+ \regex_set:NV
+ \l_tmpa_regex
+ \l_@@_current_glob_tl
+ \seq_map_inline:cn
+ { #2 }
+ {
+ \regex_match:NnT
+ \l_tmpa_regex
+ { ##1 }
+ {
+ \seq_put_right:Nn
+ #3
+ { ##1 }
+ }
+ }
+ \clist_set_from_seq:NN
+ \l_tmpa_clist
+ #3
+ \prop_gput:NeV
+ \g_@@_glob_cache_prop
+ { #2 / \l_@@_current_glob_tl }
+ \l_tmpa_clist
+ }
+ }
+% TODO: Remove in TeX Live 2023.
+\prg_generate_conditional_variant:Nnn
+ \prop_get:NnN
+ { NeN }
+ { TF }
+\cs_generate_variant:Nn
+ \regex_set:Nn
+ { NV }
+\cs_generate_variant:Nn
+ \prop_gput:Nnn
+ { NeV }
+\seq_new:N
+ \l_@@_renderer_glob_results_seq
+\keys_define:nn
+ { markdown/options/renderers }
+ {
+ unknown .code:n = {
+ \@@_glob_seq:VnN
+ \l_keys_key_str
+ { g_@@_renderers_seq }
+ \l_@@_renderer_glob_results_seq
+ \seq_if_empty:NTF
+ \l_@@_renderer_glob_results_seq
+ {
+ \msg_error:nnV
+ { markdown }
+ { undefined-renderer }
+ \l_keys_key_str
+ }
+ {
+ \tl_set:Nn
+ \l_@@_renderer_definition_tl
+ { #1 }
+ \seq_map_inline:Nn
+ \l_@@_renderer_glob_results_seq
+ {
+ \@@_renderer_tl_to_csname:nN
+ { ##1 }
+ \l_tmpa_tl
+ \prop_get:NnN
+ \g_@@_renderer_arities_prop
+ { ##1 }
+ \l_tmpb_tl
+ \int_set:Nn
+ \l_tmpa_int
+ \l_tmpb_tl
+ \tl_set:NV
+ \l_tmpb_tl
+ \l_@@_renderer_definition_tl
+ \regex_replace_all:nnN
+ { \cP\#0 }
+ { ##1 }
+ \l_tmpb_tl
+ \cs_generate_from_arg_count:cNVV
+ { \l_tmpa_tl }
+ \cs_set:Npn
+ \l_tmpa_int
+ \l_tmpb_tl
+ }
+ }
+ },
+ }
+\msg_new:nnn
+ { markdown }
+ { undefined-renderer }
+ {
+ Renderer~#1~is~undefined.
+ }
+\cs_generate_variant:Nn
+ \@@_glob_seq:nnN
+ { VnN }
+\cs_generate_variant:Nn
+ \cs_generate_from_arg_count:NNnn
+ { cNVV }
+\cs_generate_variant:Nn
+ \msg_error:nnn
+ { nnV }
+% \end{macrocode}
+% \begin{markdown}
+%
+% If plain \TeX{} is the top layer, we use the \mref{@@_define_renderers:}
+% macro to define plain \TeX{} token renderer macros and key-values
+% immediately. Otherwise, we postpone the definition until the upper layers
+% have been loaded.
%
+% \end{markdown}
+% \begin{macrocode}
+\str_if_eq:VVT
+ \c_@@_top_layer_tl
+ \c_@@_option_layer_plain_tex_tl
+ {
+ \@@_define_renderers:
+ }
+\ExplSyntaxOff
+% \end{macrocode}
+% \iffalse
+%</tex>
+%<*manual-tokens>
+% \fi
% \begin{markdown}
### Token Renderer Prototypes {#texrendererprototypes}
@@ -18250,13 +20447,13 @@ following content:
~
\end{markdown}
-\begin{markdown*}{
+\begin{markdown}[
renderers = {
tilde = {User-defined token renderers take precedence.},
},
-}
+]
~
-\end{markdown*}
+\end{markdown}
\end{document}
```````
Next, invoke LuaTeX from the terminal:
@@ -18294,7 +20491,7 @@ following content:
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -18416,7 +20613,7 @@ age: 99
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -18435,49 +20632,89 @@ following text:
% \end{macrocode}
% \begin{markdown}
%
-% The following \TeX{} macros provide definitions for the token renderers (see
-% Section <#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
-% <#sec:lateximplementation> and <#sec:contextimplementation>).
+% The `jekyllDataRenderers` key can be used as a syntactic sugar for setting
+% the `markdown/jekyllData` key--values without using the expl3 language.
%
% \end{markdown}
% \begin{macrocode}
\ExplSyntaxOn
-\cs_new:Nn \@@_plaintex_define_renderer_prototypes:
+\@@_with_various_cases:nn
+ { jekyllDataRenderers }
{
- \seq_map_function:NN
- \g_@@_renderers_seq
- \@@_plaintex_define_renderer_prototype:n
- \let\markdownRendererBlockHtmlCommentBeginPrototype=\iffalse
- \let\markdownRendererBlockHtmlCommentBegin=\iffalse
- \let\markdownRendererBlockHtmlCommentEndPrototype=\fi
- \let\markdownRendererBlockHtmlCommentEnd=\fi
+ \keys_define:nn
+ { markdown/options }
+ {
+ #1 .code:n = {
+ \tl_set:Nn
+ \l_tmpa_tl
+ { ##1 }
% \end{macrocode}
% \begin{markdown}
%
-% The \mref{markdownRendererFootnote} and
-% \mref{markdownRendererFootnotePrototype} macros have been deprecated
-% and will be removed in Markdown 3.0.0.
+% To ensure that keys containing forward slashes get passed correctly, we
+% replace all forward slashes in the input with backslash tokens with category
+% code letter and then undo the replacement. This means that if any unbraced
+% backslash tokens with category code letter exist in the input, they will be
+% replaced with forward slashes. However, this should be extremely rare.
%
% \end{markdown}
% \begin{macrocode}
- \cs_undefine:N \markdownRendererFootnote
- \cs_undefine:N \markdownRendererFootnotePrototype
+ \tl_replace_all:NnV
+ \l_tmpa_tl
+ { / }
+ \c_backslash_str
+ \keys_set:nV
+ { markdown/options/jekyll-data-renderers }
+ \l_tmpa_tl
+ },
+ }
+ }
+\keys_define:nn
+ { markdown/options/jekyll-data-renderers }
+ {
+ unknown .code:n = {
+ \tl_set_eq:NN
+ \l_tmpa_tl
+ \l_keys_key_str
+ \tl_replace_all:NVn
+ \l_tmpa_tl
+ \c_backslash_str
+ { / }
+ \tl_put_right:Nn
+ \l_tmpa_tl
+ {
+ .code:n = { #1 }
+ }
+ \keys_define:nV
+ { markdown/jekyllData }
+ \l_tmpa_tl
+ }
+ }
+\cs_generate_variant:Nn
+ \keys_define:nn
+ { nV }
+\ExplSyntaxOff
% \end{macrocode}
% \begin{markdown}
%
-% The \mref{markdownRendererHorizontalRule} and
-% \mref{markdownRendererHorizontalRulePrototype} macros have been deprecated
-% and will be removed in Markdown 3.0.0.
+%#### Generating Plain \TeX{} Token Renderer Prototype Macros and Key-Values {#plain-tex-renderer-prototypes}
+%
+% We define the command \mdef{@@_define_renderer_prototypes:} that defines plain \TeX{}
+% macros for token renderer prototypes. Futhermore, the `\markdownSetup` macro also accepts
+% the `rendererPrototype` key, whose value must be a list of key-values, where the keys
+% correspond to the markdown token renderer prototype macros and the values are new
+% definitions of these token renderer prototypes.
%
% \end{markdown}
% \begin{macrocode}
- \cs_undefine:N \markdownRendererHorizontalRule
- \cs_undefine:N \markdownRendererHorizontalRulePrototype
+\ExplSyntaxOn
+\cs_new:Nn \@@_define_renderer_prototypes:
+ {
+ \seq_map_function:NN
+ \g_@@_renderers_seq
+ \@@_define_renderer_prototype:n
}
-\cs_new:Nn \@@_plaintex_define_renderer_prototype:n
+\cs_new:Nn \@@_define_renderer_prototype:n
{
\@@_renderer_prototype_tl_to_csname:nN
{ #1 }
@@ -18486,7 +20723,8 @@ following text:
\g_@@_renderer_arities_prop
{ #1 }
\l_tmpb_tl
- \@@_plaintex_define_renderer_prototype:cV
+ \@@_define_renderer_prototype:ncV
+ { #1 }
{ \l_tmpa_tl }
\l_tmpb_tl
}
@@ -18504,23 +20742,191 @@ following text:
Prototype
}
}
-\cs_new:Nn \@@_plaintex_define_renderer_prototype:Nn
+\tl_new:N
+ \l_@@_renderer_prototype_definition_tl
+\cs_new:Nn \@@_define_renderer_prototype:nNn
{
- \cs_generate_from_arg_count:NNnn
- #1
- \cs_set:Npn
- { #2 }
- { }
+ \keys_define:nn
+ { markdown/options/renderer-prototypes }
+ {
+ #1 .code:n = {
+ \tl_set:Nn
+ \l_@@_renderer_prototype_definition_tl
+ { ##1 }
+ \regex_replace_all:nnN
+ { \cP\#0 }
+ { #1 }
+ \l_@@_renderer_prototype_definition_tl
+ \cs_generate_from_arg_count:NNnV
+ #2
+ \cs_set:Npn
+ { #3 }
+ \l_@@_renderer_prototype_definition_tl
+ },
+ }
+% \end{macrocode}
+% \begin{markdown}
+%
+% Unless the token renderer prototype macro has already been defined,
+% we provide an empty definition.
+%
+% \end{markdown}
+% \begin{macrocode}
+ \cs_if_free:NT
+ #2
+ {
+ \cs_generate_from_arg_count:NNnn
+ #2
+ \cs_set:Npn
+ { #3 }
+ { }
+ }
}
\cs_generate_variant:Nn
- \@@_plaintex_define_renderer_prototype:Nn
- { cV }
-\@@_plaintex_define_renderer_prototypes:
+ \@@_define_renderer_prototype:nNn
+ { ncV }
\ExplSyntaxOff
% \end{macrocode}
% \par
% \begin{markdown}
%
+% The following example code showcases a possible configuration of the
+% `\markdownRendererImagePrototype` and `\markdownRendererCodeSpanPrototype`
+% token renderer prototype macros.
+% ``` tex
+% \markdownSetup{
+% rendererPrototypes = {
+% image = {\pdfximage{#2}}, \% Embed PDF images in the document.
+% codeSpan = {{\tt #1}}, \% Render inline code using monospace.
+% }
+% }
+% ```````
+%
+% In addition to exact token renderer names, we also support wildcards
+% and enumerations that match multiple token renderer prototype names:
+% ``` tex
+% \markdownSetup{
+% rendererPrototypes = {
+% heading* = {{\bf #1}}, \% Render headings using the bold face.
+% jekyllData(String|Number) = { \% Render YAML string and numbers
+% {\it #2}\% \% using italics.
+% },
+% }
+% }
+% ```````
+%
+% Wildcards and enumerations can be combined:
+% ``` tex
+% \markdownSetup{
+% rendererPrototypes = {
+% *lItem(|End) = {"}, \% Quote ordered/bullet list items.
+% }
+% }
+% ```````
+%
+% To determine the current token renderer prototype, you can use the
+% parameter `#0`:
+% ``` tex
+% \markdownSetup{
+% rendererPrototypes = {
+% heading* = {#0: #1}, \% Render headings as the renderer prototype
+% } \% name followed by the heading text.
+% }
+% ```````
+%
+% \end{markdown}
+% \begin{macrocode}
+\ExplSyntaxOn
+\seq_new:N
+ \l_@@_renderer_prototype_glob_results_seq
+\keys_define:nn
+ { markdown/options/renderer-prototypes }
+ {
+ unknown .code:n = {
+ \@@_glob_seq:VnN
+ \l_keys_key_str
+ { g_@@_renderers_seq }
+ \l_@@_renderer_prototype_glob_results_seq
+ \seq_if_empty:NTF
+ \l_@@_renderer_prototype_glob_results_seq
+ {
+ \msg_error:nnV
+ { markdown }
+ { undefined-renderer-prototype }
+ \l_keys_key_str
+ }
+ {
+ \tl_set:Nn
+ \l_@@_renderer_prototype_definition_tl
+ { #1 }
+ \seq_map_inline:Nn
+ \l_@@_renderer_prototype_glob_results_seq
+ {
+ \@@_renderer_prototype_tl_to_csname:nN
+ { ##1 }
+ \l_tmpa_tl
+ \prop_get:NnN
+ \g_@@_renderer_arities_prop
+ { ##1 }
+ \l_tmpb_tl
+ \int_set:Nn
+ \l_tmpa_int
+ \l_tmpb_tl
+ \tl_set:NV
+ \l_tmpb_tl
+ \l_@@_renderer_prototype_definition_tl
+ \regex_replace_all:nnN
+ { \cP\#0 }
+ { ##1 }
+ \l_tmpb_tl
+ \cs_generate_from_arg_count:cNVV
+ { \l_tmpa_tl }
+ \cs_set:Npn
+ \l_tmpa_int
+ \l_tmpb_tl
+ }
+ }
+ },
+ }
+\msg_new:nnn
+ { markdown }
+ { undefined-renderer-prototype }
+ {
+ Renderer~prototype~#1~is~undefined.
+ }
+\@@_with_various_cases:nn
+ { rendererPrototypes }
+ {
+ \keys_define:nn
+ { markdown/options }
+ {
+ #1 .code:n = {
+ \keys_set:nn
+ { markdown/options/renderer-prototypes }
+ { ##1 }
+ },
+ }
+ }
+% \end{macrocode}
+% \begin{markdown}
+%
+% If plain \TeX{} is the top layer, we use the \mref{@@_define_renderer_prototypes:}
+% macro to define plain \TeX{} token renderer prototype macros and key-values
+% immediately. Otherwise, we postpone the definition until the upper layers
+% have been loaded.
+%
+% \end{markdown}
+% \begin{macrocode}
+\str_if_eq:VVT
+ \c_@@_top_layer_tl
+ \c_@@_option_layer_plain_tex_tl
+ {
+ \@@_define_renderer_prototypes:
+ }
+\ExplSyntaxOff
+% \end{macrocode}
+% \begin{markdown}
+%
%### Logging Facilities
% The \mdef{markdownInfo}, \mdef{markdownWarning}, and \mdef{markdownError}
% macros perform logging for the Markdown package. Their first argument
@@ -18529,6 +20935,10 @@ following text:
% text. You may redefine these macros to redirect and process the info,
% warning, and error messages.
%
+% The \mref{markdownInfo}, \mref{markdownWarning}, and \mref{markdownError}
+% macros have been deprecated and will be removed in the next major version of
+% the Markdown package.
+%
%### Miscellanea
% 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.
@@ -18572,72 +20982,26 @@ following text:
|endgroup
% \end{macrocode}
% \begin{markdown}
-% 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 <#sec:direct-lua>), the first argument may not contain the
-% string `]]` (regardless of the category code of the bracket symbol (`]`)).
-%
-% The \mdef{markdownMode} macro specifies how the plain \TeX{} implementation
-% interfaces with the Lua interface. The valid values and their meaning are
-% as follows:
-%
-% - `0` – Shell escape via the 18 output file stream
-% - `1` – Shell escape via the Lua `os.execute` method
-% - `2` – Direct Lua access
-% - `3` – The \pkg{lt3luabridge} Lua package
-%
-% By defining the macro, the user can coerce the package to use a specific mode.
-% If the user does not define the macro prior to loading the plain \TeX{}
-% implementation, the correct value will be automatically detected. The outcome
-% of changing the value of \mref{markdownMode} after the implementation has been
-% loaded is undefined.
-%
-% The \mref{markdownMode} macro has been deprecated and will be removed in
-% Markdown 3.0.0. The code that corresponds to \mref{markdownMode} value of `3`
-% will be the only implementation.
+% The macro is exposed in the interface, so that users can create their own
+% markdown environments. Due to the way the arguments are passed to Lua, the
+% first argument may not contain the string `]]` (regardless of the category
+% code of the bracket symbol).
%
+% The `code` key, which can be used to immediately expand and execute code.
% \end{markdown}
% \begin{macrocode}
\ExplSyntaxOn
-\cs_if_exist:NF
- \markdownMode
+\keys_define:nn
+ { markdown/options }
{
- \file_if_exist:nTF
- { lt3luabridge.tex }
- {
- \cs_new:Npn
- \markdownMode
- { 3 }
- }
- {
- \cs_if_exist:NTF
- \directlua
- {
- \cs_new:Npn
- \markdownMode
- { 2 }
- }
- {
- \cs_new:Npn
- \markdownMode
- { 0 }
- }
- }
+ code .code:n = { #1 },
}
\ExplSyntaxOff
% \end{macrocode}
-% \par
% \begin{markdown}
-%
-% The \mdef{markdownLuaRegisterIBCallback} and
-% \mdef{markdownLuaUnregisterIBCallback} macros have been deprecated and will
-% be removed in Markdown 3.0.0:
+% This can be especially useful in snippets.
%
% \end{markdown}
-% \begin{macrocode}
-\def\markdownLuaRegisterIBCallback#1{\relax}%
-\def\markdownLuaUnregisterIBCallback#1{\relax}%
-% \end{macrocode}
% \iffalse
%</tex>
% \fi
@@ -18674,9 +21038,9 @@ following content:
$\sqrt{-1}$ *equals* $i$
\end{markdown}
-\begin{markdown*}{hybrid}
+\begin{markdown}[texMathDollars]
$\sqrt{-1}$ *equals* $i$
-\end{markdown*}
+\end{markdown}
\end{document}
```````
@@ -18710,15 +21074,26 @@ pdflatex --shell-escape document.tex
% rest of the interface is inherited from the plain \TeX{} interface (see
% Section <#sec:texinterface>).
%
-% The \LaTeX{} implementation redefines the plain \TeX{} logging macros (see
-% Section <#sec:tex-interface-logging>) to use the \LaTeX{} `\PackageInfo`,
-% `\PackageWarning`, and `\PackageError` macros.
+% To determine whether \LaTeX{} is the top layer or if there are other
+% layers above \LaTeX{}, we take a look on whether the
+% \mref{c_@@_top_layer_tl} token list has already been defined. If not,
+% we will assume that \LaTeX{} is the top layer.
%
% \end{markdown}
% \begin{macrocode}
-\newcommand\markdownInfo[1]{\PackageInfo{markdown}{#1}}%
-\newcommand\markdownWarning[1]{\PackageWarning{markdown}{#1}}%
-\newcommand\markdownError[2]{\PackageError{markdown}{#1}{#2.}}%
+\ExplSyntaxOn
+\tl_const:Nn \c_@@_option_layer_latex_tl { latex }
+\cs_generate_variant:Nn
+ \tl_const:Nn
+ { NV }
+\tl_if_exist:NF
+ \c_@@_top_layer_tl
+ {
+ \tl_const:NV
+ \c_@@_top_layer_tl
+ \c_@@_option_layer_latex_tl
+ }
+\ExplSyntaxOff
\input markdown/markdown
% \end{macrocode}
% \begin{markdown}
@@ -18733,12 +21108,12 @@ pdflatex --shell-escape document.tex
% where \meta{options} are the \LaTeX{} interface options (see Section
% <#sec:latex-options>). Note that \meta{options} inside the `\usepackage`
% macro may not set the `markdownRenderers` (see Section
-% <#sec:latex-renderers>) and `markdownRendererPrototypes` (see Section
-% <#sec:latex-renderer-prototypes>) keys. Furthermore, although the
+% <#sec:plain-tex-renderers>) and `markdownRendererPrototypes` (see Section
+% <#sec:plain-tex-renderer-prototypes>) keys. Furthermore, although the
% base variant of the `import` key that loads a single \LaTeX{} theme
% (see Section <#sec:latexthemes>) can be used, the extended variant
% that can load multiple themes and import snippets from them (see
-% Section <#sec:latexsnippets>). This limitation is due to the way
+% Section <#sec:snippets>) cannot. This limitation is due to the way
% \Hologo{LaTeX2e} parses package options.
%
% \end{markdown}
@@ -18749,11 +21124,14 @@ pdflatex --shell-escape document.tex
% The interface exposes the \envmdef{markdown} and \envmdef{markdown*}
% \LaTeX{} environments, and redefines the \mref{markdownInput} command.
%
-% The \envmref{markdown} and \envmref{markdown*} \LaTeX{} environments are used to
-% typeset markdown document fragments. The starred version of the
-% \envmref{markdown} environment accepts \LaTeX{} interface options (see
-% Section <#sec:latex-options>) as its only argument. These options will
-% only influence this markdown document fragment.
+% The \envmref{markdown} and \envmref{markdown*} \LaTeX{} environments are used
+% to typeset markdown document fragments. Both \LaTeX{} environments accept
+% \LaTeX{} interface options (see ection <#sec:latex-options>) as the only
+% argument. This argument is optional for the \envmref{markdown} environment
+% and mandatory for the \envmref{markdown*} environment.
+%
+% The \envmref{markdown*} environment has been deprecated and will be removed
+% in the next major version of the Markdown package.
%
% \end{markdown}
% \begin{macrocode}
@@ -18763,7 +21141,7 @@ pdflatex --shell-escape document.tex
% \markdownBegin
%
% You may prepend your own code to the \mdef{markdown} macro and append your own
-% code to the \mdef{endmarkdown} macro to produce special effects before and after
+% code to the \mref{markdownEnd} macro to produce special effects before and after
% the \envmref{markdown} \LaTeX{} environment (and likewise for the starred
% version).
%
@@ -18778,7 +21156,7 @@ pdflatex --shell-escape document.tex
% \usepackage{markdown} \usepackage{markdown}
% \begin{document} \begin{document}
% \% ... \% ...
-% \begin{markdown} \begin{markdown*}{smartEllipses}
+% \begin{markdown}[smartEllipses] \begin{markdown*}{smartEllipses}
% _Hello_ **world** ... _Hello_ **world** ...
% \end{markdown} \end{markdown*}
% \% ... \% ...
@@ -18812,9 +21190,6 @@ pdflatex --shell-escape document.tex
% part is optional, and \meta{key} will be interpreted as \meta{key}`=true`
% if the `=`\meta{value} part has been omitted.
%
-% Except for the `plain` option described in Section <#sec:latexplain>, and
-% the \LaTeX{} themes described in Section <#sec:latexthemes>, and the
-% \LaTeX{} snippets described in Section <#sec:latex-setup-snippets>,
% \LaTeX{} options map directly to the options recognized by the plain
% \TeX{} interface (see Section <#sec:tex-options>) and to the markdown token
% renderers and their prototypes recognized by the plain \TeX{} interface (see
@@ -18822,75 +21197,36 @@ pdflatex --shell-escape document.tex
%
% The \LaTeX{} options may be specified when loading the \LaTeX{} package, when
% using the \envmref{markdown*} \LaTeX{} environment or the \mref{markdownInput}
-% macro (see Section <#sec:latexinterface>), or via the \mdef{markdownSetup}
-% macro. The \mref{markdownSetup} macro receives the options to set up as its only
-% argument:
-%
-% \markdownEnd
-% \begin{macrocode}
-\ExplSyntaxOn
-\cs_new:Nn
- \@@_setup:n
- {
- \keys_set:nn
- { markdown/latex-options }
- { #1 }
- }
-\let\markdownSetup=\@@_setup:n
-\ExplSyntaxOff
-% \end{macrocode}
-% \begin{markdown}
+% macro (see Section <#sec:latexinterface>), or via the \mref{markdownSetup}
+% macro.
%
-%#### \LaTeX{} snippets {#latex-setup-snippets}
+%#### Finalizing and Freezing the Cache
%
-% We may also set up \LaTeX{} options as *snippets* using the
-% \mdef{markdownSetupSnippet} macro and invoke them later. The
-% \mref{markdownSetupSnippet} macro receives two arguments: the name
-% of the snippet and the options to store:
+% To ensure compatibility with the `minted` package [@poore17, Section
+% 5.1], which supports the `finalizecache` and `frozencache` package options
+% with similar semantics to the \Opt{finalizeCache} and \Opt{frozenCache} plain
+% \TeX{} options, the Markdown package also recognizes these as aliases
+% and accepts them as document class options. By passing `finalizecache` and
+% `frozencache` as document class options, you may conveniently control the
+% behavior of both packages at once:
%
-% \end{markdown}
-% \begin{macrocode}
-\ExplSyntaxOn
-\cs_new:Nn
- \@@_latex_setup_snippet:nn
- {
- \markdownIfSnippetExists
- { #1 }
- {
- \markdownWarning
- {Redefined~snippet~\markdownLaTeXThemeName#1}
- \csname markdownLaTeXSetupSnippet%
- \markdownLaTeXThemeName#1\endcsname={#2}
- }
- {
- \newtoks\next
- \next={#2}
- \expandafter\let\csname markdownLaTeXSetupSnippet%
- \markdownLaTeXThemeName#1\endcsname=\next
- }
- }
-\let\markdownSetupSnippet=\@@_latex_setup_snippet:nn
-\ExplSyntaxOff
-% \end{macrocode}
-% \begin{markdown}
+% ``` tex
+% \documentclass[frozencache]{article}
+% \usepackage{markdown,minted}
+% \begin{document}
+% % ...
+% \end{document}
+% ```````
%
-% To decide whether a snippet exists, we can use the
-% \mdef{markdownIfSnippetExists} macro:
+% We hope that other packages will support the `finalizecache` and
+% `frozencache` package options in the future, so that they can become a
+% standard interface for preparing \LaTeX{} document sources for distribution.
%
-% \end{markdown}
+% \markdownEnd
% \begin{macrocode}
-\newcommand\markdownIfSnippetExists[3]{%
- \@ifundefined
- {markdownLaTeXSetupSnippet\markdownLaTeXThemeName#1}%
- {#3}{#2}}%
+\DeclareOption{finalizecache}{\markdownSetup{finalizeCache}}
+\DeclareOption{frozencache}{\markdownSetup{frozenCache}}
% \end{macrocode}
-% \begin{markdown}
-%
-% See Section <#sec:latexthemes> for information on interactions between
-% snippets and \LaTeX{} themes. See Section <#sec:latexsnippets>
-% for information about invoking the set-up snippets.
-%
-% \end{markdown}
% \iffalse
%</latex>
%<*manual-options>
@@ -18914,133 +21250,95 @@ following code in our \LaTeX{} document:
```
We can also set all plain \TeX{} options directly from \LaTeX{}. For example,
-to set the `\markdownOptionHelperScriptFileName` plain \TeX{} option to
+to set the `\markdownOptionInputTempFileName` plain \TeX{} option to
`helper-script.lua`, we would inclde the following code in our \LaTeX{}
document:
``` tex
\markdownSetup{
- helperScriptFileName = helper-script.lua,
+ inputTempFileName = temporary-input.md,
}
```
%</manual-options>
%<*latex>
% \fi
-% \par
% \begin{markdown}
%
-% To enable the enumeration of \LaTeX{} options, we will maintain the
-% \mdef{g_\@\@_latex_options_seq} sequence.
+%#### Generating Plain \TeX{} Option, Token Renderer, and Token Renderer Prototype Macros and Key-Values
+%
+% If \LaTeX{} is the top layer, we use the
+% \mref{@@_define_option_commands_and_keyvals:}, \mref{@@_define_renderers:},
+% and \mref{@@_define_renderer_prototypes:} macro to define plain \TeX{}
+% option, token renderer, and token renderer prototype macros and key-values
+% immediately. Otherwise, we postpone the definition until the upper layers
+% have been loaded.
%
% \end{markdown}
% \begin{macrocode}
\ExplSyntaxOn
-\seq_new:N \g_@@_latex_options_seq
+\str_if_eq:VVT
+ \c_@@_top_layer_tl
+ \c_@@_option_layer_latex_tl
+ {
+ \@@_define_option_commands_and_keyvals:
+ \@@_define_renderers:
+ \@@_define_renderer_prototypes:
+ }
+\ExplSyntaxOff
% \end{macrocode}
+% \par
% \begin{markdown}
%
-% To enable the reflection of default \LaTeX{} options and their types, we
-% will maintain the \mdef{g_\@\@_default_latex_options_prop} and
-% \mdef{g_\@\@_latex_option_types_prop} property lists, respectively.
+% The following example \LaTeX{} code showcases a possible configuration of
+% plain \TeX{} interface options \Opt{hybrid}, \Opt{smartEllipses}, and
+% \Opt{cacheDir}.
+% ``` tex
+% \markdownSetup{
+% hybrid,
+% smartEllipses,
+% cacheDir = /tmp,
+% }
+% ```````
%
% \end{markdown}
-% \begin{macrocode}
-\prop_new:N \g_@@_latex_option_types_prop
-\prop_new:N \g_@@_default_latex_options_prop
-\tl_const:Nn \c_@@_option_layer_latex_tl { latex }
-\seq_gput_right:NV \g_@@_option_layers_seq \c_@@_option_layer_latex_tl
-\cs_new:Nn
- \@@_add_latex_option:nnn
- {
- \@@_add_option:Vnnn
- \c_@@_option_layer_latex_tl
- { #1 }
- { #2 }
- { #3 }
- }
-% \end{macrocode}
% \iffalse
%</latex>
%<*manual-options>
% \fi
% \begin{markdown}
-#### No default token renderer prototypes {#latexplain}
-
-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 <#sec:tex-token-renderer-prototypes>)
-and prevent the soft \LaTeX{} prerequisites
-% in Section <#sec:latex-prerequisites>
-from being loaded: The plain option must be set before or when loading the
-package. Setting the option after loading the package will have no effect.
-
-``` tex
-\usepackage[plain]{markdown}
-```````
+### Themes {#latexthemes}
-% \end{markdown}
-% \iffalse
-%</manual-options>
-%<*latex>
+% In Section~\ref{sec:themes}, we described the concept of themes.
+In \LaTeX{}, we expand on the concept of
+% themes\iffalse
+[themes](#themes)
% \fi
-% \begin{macrocode}
-\@@_add_latex_option:nnn
- { plain }
- { boolean }
- { false }
-\ExplSyntaxOff
-% \end{macrocode}
-% \iffalse
-%</latex>
-%<*manual-options>
-% \fi
-% \begin{markdown}
+by allowing a theme to be a full-blown \LaTeX{} package. Specifically, the
+key-values `theme`=\meta{theme name} and `import`=\meta{theme name} load a
+\LaTeX{} package named `markdowntheme`\meta{munged theme name}`.sty` if it
+exists and a \TeX{} document named `markdowntheme`\meta{munged theme
+name}`.tex` otherwise.
-#### \LaTeX{} themes {#latexthemes}
+Having the Markdown package automatically load either the generic `.tex`
+*theme file* or the \LaTeX{}-specific `.sty` theme file allows developers
+to have a single *theme file*, when the theme is small or the difference
+between \TeX{} formats is unimportant, and scale up to separate theme files
+native to different \TeX{} formats for large multi-format themes, where
+different code is needed for different \TeX{} formats. To enable code reuse,
+developers can load the `.tex` theme file from the `.sty` theme file using the
+\mdef{markdownLoadPlainTeXTheme} macro.
-User-defined \LaTeX{} themes for the Markdown package provide a domain-specific
-interpretation of 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 `import`=\meta{theme name} 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 the
-% substitution of all forward slashes (`/`) for an underscore (`_`), the theme
-% *name* 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 `\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
-% `\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:
+% If the \LaTeX{} option with keys `theme` or `import` is (repeatedly)
+% specified in the `\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
@@ -19066,121 +21364,22 @@ would use the following code in the preamble of your document:
% \begin{macrocode}
\newif\ifmarkdownLaTeXLoaded
\markdownLaTeXLoadedfalse
-\AtEndOfPackage{\markdownLaTeXLoadedtrue}
-\ExplSyntaxOn
-\tl_new:N \markdownLaTeXThemePackageName
-\cs_new:Nn
- \@@_set_latex_theme:n
- {
- \str_if_in:nnF
- { #1 }
- { / }
- {
- \markdownError
- { Won't~load~theme~with~unqualified~name~#1 }
- { Theme~names~must~contain~at~least~one~forward~slash }
- }
- \str_if_in:nnT
- { #1 }
- { _ }
- {
- \markdownError
- { Won't~load~theme~with~an~underscore~in~its~name~#1 }
- { Theme~names~must~not~contain~underscores~in~their~names }
- }
- \tl_set:Nn \markdownLaTeXThemePackageName { #1 }
- \str_replace_all:Nnn
- \markdownLaTeXThemePackageName
- { / }
- { _ }
- \edef\markdownLaTeXThemePackageName{
- markdowntheme\markdownLaTeXThemePackageName}
- \expandafter\markdownLaTeXThemeLoad\expandafter{
- \markdownLaTeXThemePackageName}{#1/}
- }
-\keys_define:nn
- { markdown/latex-options }
- {
- import .code:n = {
- \tl_set:Nn
- \l_tmpa_tl
- { #1 }
% \end{macrocode}
-% \begin{markdown}
-%
-% To ensure that keys containing forward slashes get passed correctly, we
-% replace all forward slashes in the input with backslash tokens with category
-% code letter and then undo the replacement. This means that if any unbraced
-% backslash tokens with category code letter exist in the input, they will be
-% replaced with forward slashes. However, this should be extremely rare.
-%
-% \end{markdown}
-% \begin{macrocode}
- \tl_replace_all:NnV
- \l_tmpa_tl
- { / }
- \c_backslash_str
- \keys_set:nV
- { markdown/latex-options/import }
- \l_tmpa_tl
- },
- }
-\cs_generate_variant:Nn
- \tl_replace_all:Nnn
- { NnV }
-% \end{macrocode}
-% \begin{markdown}
-%
-% The \LaTeX{} option `theme` has been deprecated and will be removed in
-% Markdown 3.0.0.
-%
-% \end{markdown}
-% \begin{macrocode}
-\keys_define:nn
- { markdown/latex-options }
- {
- theme .code:n = { \@@_set_latex_theme:n { #1 } },
- }
-\ExplSyntaxOff
-% \end{macrocode}
-% \begin{markdown}
-%
-% The \LaTeX{} themes have a useful synergy with snippets (see Section
-% <#sec:latex-setup-snippets>): To make it less likely that different themes
-% will set up 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 sets up the `product` snippet, the
-% snippet will be available under the name `witiko/dot/product`.
-%
-% \end{markdown}
% \iffalse
%</latex>
%<*manual-options>
% \fi
+% \par
% \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}
-\ExplSyntaxOn
-\@onlypreamble
- \@@_set_latex_theme:n
-\ExplSyntaxOff
-% \end{macrocode}
-% \iffalse
-%</latex>
-%<*manual-options>
-% \fi
% \par
% \markdownBegin
-Example themes provided with the Markdown package include:
+Built-in \LaTeX{} themes provided with the Markdown package include:
\pkg{witiko/dot}
@@ -19311,13 +21510,13 @@ conference article:
> <http://ceur-ws.org/Vol-2696/paper_235.pdf>
%</manual-options>
-%<*latex-themes-witiko-dot>
+%<*themes-witiko-dot>
% \fi
% \begin{macrocode}
\ProvidesPackage{markdownthemewitiko_dot}[2021/03/09]%
% \end{macrocode}
% \iffalse
-%</latex-themes-witiko-dot>
+%</themes-witiko-dot>
%<*manual-options>
% \fi
% \par
@@ -19370,909 +21569,71 @@ lualatex document.tex
A PDF document named `document.pdf` should be produced and contain the
following image:
-> ![img](https://github.com/witiko/markdown/raw/main/markdown.png
-> "The banner of the Markdown package")
+> ![img](https://github.com/witiko/markdown/raw/main/markdown.png "The banner of the Markdown package")
%</manual-options>
-%<*latex-themes-witiko-graphicx-http>
+%<*themes-witiko-graphicx-http>
% \fi
% \begin{macrocode}
\ProvidesPackage{markdownthemewitiko_graphicx_http}[2021/03/22]%
% \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 disabled.
-% ``` tex
-% \documentclass{article}
-% \usepackage[import=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[import=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>
+%</themes-witiko-graphicx-http>
%<*manual-options>
% \fi
% \par
% \begin{markdown}
-%
-% Please, see Section <#sec:latex-themes-implementation> for implementation
-% details of the example themes.
-#### \LaTeX{} snippets {#latexsnippets}
+\pkg{witiko/markdown/defaults}
-% \end{markdown}
-% \iffalse
+: A \LaTeX{} theme with the default definitions of token renderer prototypes
+ for plain \TeX{}. This theme is loaded automatically together with the
+ package and explicitly loading it has no effect.
-User-defined \LaTeX{} themes provide global control over high-level goals.
-However, it is often desirable to change only some local aspects of a document.
-\LaTeX{} 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}
-\ExplSyntaxOn
-\keys_define:nn
- { markdown/latex-options }
- {
- snippet .code:n = {
- \markdownIfSnippetExists{#1}
- {
- \expandafter\markdownSetup\expandafter{
- \the\csname markdownLaTeXSetupSnippet
- \markdownLaTeXThemeName#1\endcsname}
- }{
- \markdownError
- {Can't~invoke~setup~snippet~#1}
- {The~setup~snippet~is~undefined}
- }
- }
- }
-\ExplSyntaxOff
-% \end{macrocode}
-% \iffalse
-%</latex>
-%<*manual-options>
-% \fi
-% \par
-% \markdownBegin
-
-Here is how we can use 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*}
-```````
-
-If the `romanNumerals` snippet were defined in the `jdoe/lists` theme,
-we could import the `jdoe/lists` theme and use the qualified name
-`jdoe/lists/romanNumerals` to invoke the snippet:
-
-``` tex
-\markdownSetup{import=jdoe/lists}
-\begin{markdown*}{snippet=jdoe/lists/romanNumerals}
-
-The following ordered list will be preceded by roman numerals:
-
-3. tres
-4. quattuor
-
-\end{markdown*}
-```````
-
-Alternatively, we can use the extended variant of the `import` \LaTeX{}
-option that allows us to import the `romanNumerals` snippet to the
-current namespace for easier access:
-
-``` tex
-\markdownSetup{
- import = {
- jdoe/lists = romanNumerals,
- },
-}
-\begin{markdown*}{snippet=romanNumerals}
-
-The following ordered list will be preceded by roman numerals:
-
-3. tres
-4. quattuor
-
-\end{markdown*}
-```````
-
-Furthermore, we can also specify the name of the snippet in the current
-namespace, which can be different from the name of the snippet in the
-`jdoe/lists` theme. For example, we can make the snippet
-`jdoe/lists/romanNumerals` available under the name `roman`.
-
-``` tex
-\markdownSetup{
- import = {
- jdoe/lists = romanNumerals as roman,
- },
-}
-\begin{markdown*}{snippet=roman}
-
-The following ordered list will be preceded by roman numerals:
-
-3. tres
-4. quattuor
-
-\end{markdown*}
-```````
-
-Several themes and/or snippets can be loaded at once using the extended
-variant of the `import` \LaTeX{} option:
-
-``` tex
-\markdownSetup{
- import = {
- jdoe/longpackagename/lists = {
- arabic as arabic1,
- roman,
- alphabetic,
- },
- jdoe/anotherlongpackagename/lists = {
- arabic as arabic2,
- },
- jdoe/yetanotherlongpackagename,
- },
-}
-```````
-
-% \markdownEnd
-% \iffalse
-%</manual-options>
-%<*latex>
-% \fi
-% \begin{macrocode}
-\ExplSyntaxOn
-\tl_new:N
- \l_@@_latex_import_current_theme_tl
-\keys_define:nn
- { markdown/latex-options/import }
- {
-% \end{macrocode}
-% \begin{markdown}
-%
-% If a theme name is given without a list of snippets to import,
-% we assume that an empty list was given.
-%
-% \end{markdown}
-% \begin{macrocode}
- unknown .default:n = {},
- unknown .code:n = {
-% \end{macrocode}
-% \begin{markdown}
-%
-% To ensure that keys containing forward slashes get passed correctly, we
-% replace all forward slashes in the input with backslash tokens with category
-% code letter and then undo the replacement. This means that if any unbraced
-% backslash tokens with category code letter exist in the input, they will be
-% replaced with forward slashes. However, this should be extremely rare.
-%
-% \end{markdown}
-% \begin{macrocode}
- \tl_set_eq:NN
- \l_@@_latex_import_current_theme_tl
- \l_keys_key_str
- \tl_replace_all:NVn
- \l_@@_latex_import_current_theme_tl
- \c_backslash_str
- { / }
-% \end{macrocode}
-% \begin{markdown}
-%
-% Here, we load the \LaTeX{} theme.
-%
-% \end{markdown}
-% \begin{macrocode}
- \@@_set_latex_theme:V
- \l_@@_latex_import_current_theme_tl
+\AtEndOfPackage{
+ \markdownLaTeXLoadedtrue
% \end{macrocode}
% \begin{markdown}
%
-% Here, we import the \LaTeX{} snippets.
+% At the end of the \LaTeX{} module, we load the
+% `witiko/markdown/defaults` \LaTeX{} theme (see Section <#sec:themes>) with
+% the default definitions for token renderer prototypes unless the option
+% `noDefaults` has been enabled (see Section <#sec:plain>).
%
% \end{markdown}
% \begin{macrocode}
- \clist_map_inline:nn
- { #1 }
- {
- \regex_extract_once:nnNTF
- { ^(.*?)\s+as\s+(.*?)$ }
- { ##1 }
- \l_tmpa_seq
- {
- \seq_pop:NN
- \l_tmpa_seq
- \l_tmpa_tl
- \seq_pop:NN
- \l_tmpa_seq
- \l_tmpa_tl
- \seq_pop:NN
- \l_tmpa_seq
- \l_tmpb_tl
- }
- {
- \tl_set:Nn
- \l_tmpa_tl
- { ##1 }
- \tl_set:Nn
- \l_tmpb_tl
- { ##1 }
- }
- \tl_put_left:Nn
- \l_tmpa_tl
- { / }
- \tl_put_left:NV
- \l_tmpa_tl
- \l_@@_latex_import_current_theme_tl
- \@@_latex_setup_snippet:Vx
- \l_tmpb_tl
- { snippet = { \l_tmpa_tl } }
- }
- },
- }
-\cs_generate_variant:Nn
- \tl_replace_all:Nnn
- { NVn }
-\cs_generate_variant:Nn
- \@@_set_latex_theme:n
- { V }
-\cs_generate_variant:Nn
- \@@_latex_setup_snippet:nn
- { Vx }
-\ExplSyntaxOff
-% \end{macrocode}
-% \begin{markdown}
-%
-%#### Plain \TeX{} Interface Options
-% Here, we automatically define plain \TeX{} macros and the
-% \meta{key}`=`\meta{value} interface for the above \LaTeX{} options.
-%
-% \end{markdown}
-% \begin{macrocode}
-\ExplSyntaxOn
-\cs_new:Nn \@@_latex_define_option_commands_and_keyvals:
- {
- \seq_map_inline:Nn
- \g_@@_latex_options_seq
- {
- \@@_plain_tex_define_option_command:n
- { ##1 }
- }
-% \end{macrocode}
-% \begin{markdown}
-%
-% Furthermore, we also define the \meta{key}`=`\meta{value} interface
-% for all option macros recognized by the Lua and plain \TeX{} interfaces.
-%
-% \end{markdown}
-% \begin{macrocode}
- \seq_map_inline:Nn
- \g_@@_option_layers_seq
- {
- \seq_map_inline:cn
- { g_@@_ ##1 _options_seq }
- {
-% \end{macrocode}
-% \begin{markdown}
-%
-% To make it easier to copy-and-paste options from Pandoc [@macfarlane22] such
-% as `fancy_lists`, `header_attributes`, and `pipe_tables`, we accept
-% snake\\\_case in addition to camelCase variants of options. As a bonus,
-% studies [@sharif10] also show that snake\\\_case is faster to read than
-% camelCase.
-%
-% \end{markdown}
-% \begin{macrocode}
- \@@_with_various_cases:nn
- { ####1 }
- {
- \@@_latex_define_option_keyval:nnn
- { ##1 }
- { ####1 }
- { ########1 }
- }
- }
- }
+ \markdownIfOption{noDefaults}{}{
+ \markdownSetup{theme=witiko/markdown/defaults}
}
-\cs_new:Nn \@@_latex_define_option_keyval:nnn
- {
- \prop_get:cnN
- { g_@@_ #1 _option_types_prop }
- { #2 }
- \l_tmpa_tl
- \keys_define:nn
- { markdown/latex-options }
- {
- #3 .code:n = {
- \@@_set_option_value:nn
- { #2 }
- { ##1 }
- },
- }
- \str_if_eq:VVT
- \l_tmpa_tl
- \c_@@_option_type_boolean_tl
- {
- \keys_define:nn
- { markdown/latex-options }
- {
- #3 .default:n = { true },
- }
- }
-% \end{macrocode}
-% \begin{markdown}
-%
-% For options of type `clist`, we assume that \meta{key} is a regular English
-% noun in plural (such as `extensions`) and we also define the
-% \meta{singular key}`=`\meta{value} interface, where \meta{singular key} is
-% \meta{key} after stripping the trailing -s (such as `extension`). Rather
-% than setting the option to \meta{value}, this interface appends \meta{value}
-% to the current value as the rightmost item in the list.
-%
-% \end{markdown}
-% \begin{macrocode}
- \str_if_eq:VVT
- \l_tmpa_tl
- \c_@@_option_type_clist_tl
- {
- \tl_set:Nn
- \l_tmpa_tl
- { #3 }
- \tl_reverse:N
- \l_tmpa_tl
- \str_if_eq:enF
- {
- \tl_head:V
- \l_tmpa_tl
- }
- { s }
- {
- \msg_error:nnn
- { markdown }
- { malformed-name-for-clist-option }
- { #3 }
- }
- \tl_set:Nx
- \l_tmpa_tl
- {
- \tl_tail:V
- \l_tmpa_tl
- }
- \tl_reverse:N
- \l_tmpa_tl
- \tl_put_right:Nn
- \l_tmpa_tl
- {
- .code:n = {
- \@@_get_option_value:nN
- { #2 }
- \l_tmpa_tl
- \clist_set:NV
- \l_tmpa_clist
- { \l_tmpa_tl, { ##1 } }
- \@@_set_option_value:nV
- { #2 }
- \l_tmpa_clist
- }
- }
- \keys_define:nV
- { markdown/latex-options }
- \l_tmpa_tl
- }
- }
-\cs_generate_variant:Nn
- \clist_set:Nn
- { NV }
-\cs_generate_variant:Nn
- \keys_define:nn
- { nV }
-\cs_generate_variant:Nn
- \@@_set_option_value:nn
- { nV }
-\prg_generate_conditional_variant:Nnn
- \str_if_eq:nn
- { en }
- { F }
-\msg_new:nnn
- { markdown }
- { malformed-name-for-clist-option }
- {
- Clist~option~name~#1~does~not~end~with~-s.
- }
-\@@_latex_define_option_commands_and_keyvals:
-\ExplSyntaxOff
-% \end{macrocode}
-% \par
-% \begin{markdown}
-%
-% The \Opt{finalizeCache} and \Opt{frozenCache} plain
-% \TeX{} options are exposed through \LaTeX{} options with keys `finalizeCache`
-% and `frozenCache`.
-%
-% To ensure compatibility with the `minted` package [@poore17, Section
-% 5.1], which supports the `finalizecache` and `frozencache` package options
-% with similar semantics, the Markdown package also recognizes these as aliases
-% and recognizes them as document class options. By passing `finalizecache` and
-% `frozencache` as document class options, you may conveniently control the
-% behavior of both packages at once:
-%
-% ``` tex
-% \documentclass[frozencache]{article}
-% \usepackage{markdown,minted}
-% \begin{document}
-% % ...
-% \end{document}
-% ```````
-%
-% We hope that other packages will support the `finalizecache` and
-% `frozencache` package options in the future, so that they can become a
-% standard interface for preparing \LaTeX{} document sources for distribution.
-%
-% \end{markdown}
-% \begin{macrocode}
-\DeclareOption{finalizecache}{\markdownSetup{finalizeCache}}
-\DeclareOption{frozencache}{\markdownSetup{frozenCache}}
-% \end{macrocode}
-% \par
-% \begin{markdown}
-%
-% The following example \LaTeX{} code showcases a possible configuration of
-% plain \TeX{} interface options \Opt{hybrid}, \Opt{smartEllipses}, and
-% \Opt{cacheDir}.
-% ``` tex
-% \markdownSetup{
-% hybrid,
-% smartEllipses,
-% cacheDir = /tmp,
-% }
-% ```````
-%
-%#### Plain \TeX{} Markdown Token Renderers {#latex-renderers}
-%
-% The \LaTeX{} interface recognizes an option with the `renderers` key,
-% whose value must be a list of key-values, where the keys correspond
-% to the markdown token renderer macros exposed by the plain \TeX{}
-% interface (see Section <#sec:texrenderersuser>) and the values are
-% new definitions of these token renderers.
-%
-% \end{markdown}
-% \begin{macrocode}
-\ExplSyntaxOn
-\cs_new:Nn \@@_latex_define_renderers:
- {
- \seq_map_function:NN
- \g_@@_renderers_seq
- \@@_latex_define_renderer:n
- }
-\cs_new:Nn \@@_latex_define_renderer:n
- {
- \@@_renderer_tl_to_csname:nN
- { #1 }
- \l_tmpa_tl
- \prop_get:NnN
- \g_@@_renderer_arities_prop
- { #1 }
- \l_tmpb_tl
- \@@_latex_define_renderer:ncV
- { #1 }
- { \l_tmpa_tl }
- \l_tmpb_tl
- }
-\cs_new:Nn \@@_renderer_tl_to_csname:nN
- {
- \tl_set:Nn
- \l_tmpa_tl
- { \str_uppercase:n { #1 } }
- \tl_set:Nx
- #2
- {
- markdownRenderer
- \tl_head:f { \l_tmpa_tl }
- \tl_tail:n { #1 }
- }
- }
-\cs_new:Nn \@@_latex_define_renderer:nNn
- {
- \@@_with_various_cases:nn
- { #1 }
- {
- \keys_define:nn
- { markdown/latex-options/renderers }
- {
- ##1 .code:n = {
- \cs_generate_from_arg_count:NNnn
- #2
- \cs_set:Npn
- { #3 }
- { ####1 }
- },
- }
- }
- }
-\cs_generate_variant:Nn
- \@@_latex_define_renderer:nNn
- { ncV }
-\ExplSyntaxOff
-% \end{macrocode}
-% \par
-% \begin{markdown}
-%
-% The following example \LaTeX{} code showcases a possible configuration of the
-% \mref{markdownRendererLink} and \mref{markdownRendererEmphasis} markdown token
-% renderers.
-% ``` tex
-% \markdownSetup{
-% renderers = {
-% link = {#4}, \% Render links as the link title.
-% emphasis = {\emph{#1}}, \% Render emphasized text via `\emph`.
-% }
-% }
-% ```````
-%
-% In addition to exact token renderer names, we also support wildcards
-% that match multiple token renderer names.
-%
-% \end{markdown}
-% \begin{macrocode}
-\ExplSyntaxOn
-\tl_new:N
- \l_@@_renderer_definition_tl
-\keys_define:nn
- { markdown/latex-options/renderers }
- {
- unknown .code:n = {
- \regex_match:nVTF
- { \* }
- \l_keys_key_str
- {
- \tl_set:Nn
- \l_@@_renderer_definition_tl
- { #1 }
- \tl_set:NV
- \l_tmpa_tl
- \l_keys_key_str
- \regex_replace_all:nnN
- { \* }
- { .* }
- \l_tmpa_tl
- \regex_set:NV
- \l_tmpa_regex
- \l_tmpa_tl
- \int_zero:N
- \l_tmpa_int
- \seq_map_inline:Nn
- \g_@@_renderers_seq
- {
- \@@_with_various_cases:nn
- { ##1 }
- {
- \regex_match:NnT
- \l_tmpa_regex
- { ####1 }
- {
- \@@_renderer_tl_to_csname:nN
- { ##1 }
- \l_tmpa_tl
- \prop_get:NnN
- \g_@@_renderer_arities_prop
- { ##1 }
- \l_tmpb_tl
- \cs_generate_from_arg_count:cNVV
- { \l_tmpa_tl }
- \cs_set:Npn
- \l_tmpb_tl
- \l_@@_renderer_definition_tl
- \int_incr:N
- \l_tmpa_int
- \@@_with_various_cases_break:
- }
- }
- }
- \int_compare:nNnT
- { \l_tmpa_int } = { 0 }
- {
- \msg_error:nnV
- { markdown }
- { nonmatched-renderer-wildcard }
- \l_keys_key_str
- }
- }
- {
- \msg_error:nnV
- { markdown }
- { undefined-renderer }
- \l_keys_key_str
- }
- },
- }
-\msg_new:nnn
- { markdown }
- { undefined-renderer }
- {
- Renderer~#1~is~undefined.
- }
-\msg_new:nnn
- { markdown }
- { nonmatched-renderer-wildcard }
- {
- Wildcard~#1~matches~no~renderers.
- }
-\cs_generate_variant:Nn
- \regex_set:Nn
- { NV }
-\cs_generate_variant:Nn
- \cs_generate_from_arg_count:NNnn
- { cNVV }
-\cs_generate_variant:Nn
- \msg_error:nnn
- { nnV }
-\prg_generate_conditional_variant:Nnn
- \regex_match:nn
- { nV }
- { TF }
-\ExplSyntaxOff
-% \end{macrocode}
-% \begin{markdown}
-%
-%#### Plain \TeX{} Markdown Token Renderer Prototypes {#latex-renderer-prototypes}
-%
-% The \LaTeX{} interface recognizes an option with the
-% `rendererPrototypes` key, whose value must be a list of key-values,
-% where the keys correspond to the markdown token renderer prototype
-% macros exposed by the plain \TeX{} interface (see Section
-% <#sec:texrendererprototypes>) and the values are new definitions of
-% these token renderer prototypes.
-%
-% \end{markdown}
-% \begin{macrocode}
-\ExplSyntaxOn
-\cs_new:Nn \@@_latex_define_renderer_prototypes:
- {
- \seq_map_function:NN
- \g_@@_renderers_seq
- \@@_latex_define_renderer_prototype:n
- }
-\cs_new:Nn \@@_latex_define_renderer_prototype:n
- {
- \@@_renderer_prototype_tl_to_csname:nN
- { #1 }
- \l_tmpa_tl
- \prop_get:NnN
- \g_@@_renderer_arities_prop
- { #1 }
- \l_tmpb_tl
- \@@_latex_define_renderer_prototype:ncV
- { #1 }
- { \l_tmpa_tl }
- \l_tmpb_tl
- }
-\cs_new:Nn \@@_latex_define_renderer_prototype:nNn
- {
- \@@_with_various_cases:nn
- { #1 }
- {
- \keys_define:nn
- { markdown/latex-options/renderer-prototypes }
- {
- ##1 .code:n = {
- \cs_generate_from_arg_count:NNnn
- #2
- \cs_set:Npn
- { #3 }
- { ####1 }
- },
- }
- }
- }
-\cs_generate_variant:Nn
- \@@_latex_define_renderer_prototype:nNn
- { ncV }
-\ExplSyntaxOff
+}
% \end{macrocode}
-% \par
-% \begin{markdown}
-%
-% The following example \LaTeX{} code showcases a possible configuration of the
-% `\markdownRendererImagePrototype` and `\markdownRendererCodeSpanPrototype`
-% markdown token renderer prototypes.
-% ``` tex
-% \markdownSetup{
-% rendererPrototypes = {
-% image = {\includegraphics{#2}},
-% codeSpan = {\texttt{#1}}, \% Render inline code via `\texttt`.
-% }
-% }
-% ```````
-%
-% In addition to exact token renderer prototype names, we also support
-% wildcards that match multiple token renderer prototype names.
-%
-% \end{markdown}
+% \iffalse
+%</latex>
+%<*themes-witiko-markdown-defaults-latex>
+% \fi
% \begin{macrocode}
-\ExplSyntaxOn
-\tl_new:N
- \l_@@_renderer_prototype_definition_tl
-\keys_define:nn
- { markdown/latex-options/renderer-prototypes }
- {
- unknown .code:n = {
- \regex_match:nVTF
- { \* }
- \l_keys_key_str
- {
- \tl_set:Nn
- \l_@@_renderer_prototype_definition_tl
- { #1 }
- \tl_set:NV
- \l_tmpa_tl
- \l_keys_key_str
- \regex_replace_all:nnN
- { \* }
- { .* }
- \l_tmpa_tl
- \regex_set:NV
- \l_tmpa_regex
- \l_tmpa_tl
- \int_zero:N
- \l_tmpa_int
- \seq_map_inline:Nn
- \g_@@_renderers_seq
- {
- \@@_with_various_cases:nn
- { ##1 }
- {
- \regex_match:NnT
- \l_tmpa_regex
- { ####1 }
- {
- \@@_renderer_prototype_tl_to_csname:nN
- { ##1 }
- \l_tmpa_tl
- \prop_get:NnN
- \g_@@_renderer_arities_prop
- { ##1 }
- \l_tmpb_tl
- \cs_generate_from_arg_count:cNVV
- { \l_tmpa_tl }
- \cs_set:Npn
- \l_tmpb_tl
- \l_@@_renderer_prototype_definition_tl
- \int_incr:N
- \l_tmpa_int
- \@@_with_various_cases_break:
- }
- }
- }
- \int_compare:nNnT
- { \l_tmpa_int } = { 0 }
- {
- \msg_error:nnV
- { markdown }
- { nonmatched-renderer-prototype-wildcard }
- \l_keys_key_str
- }
- }
- {
- \msg_error:nnV
- { markdown }
- { undefined-renderer-prototype }
- \l_keys_key_str
- }
- },
- }
-\msg_new:nnn
- { markdown }
- { undefined-renderer-prototype }
- {
- Renderer~prototype~#1~is~undefined.
- }
-\msg_new:nnn
- { markdown }
- { nonmatched-renderer-prototype-wildcard }
- {
- Wildcard~#1~matches~no~renderer~prototypes.
- }
-\cs_generate_variant:Nn
- \regex_set:Nn
- { NV }
-\cs_generate_variant:Nn
- \cs_generate_from_arg_count:NNnn
- { cNVV }
-\cs_generate_variant:Nn
- \msg_error:nnn
- { nnV }
-\prg_generate_conditional_variant:Nnn
- \regex_match:nn
- { nV }
- { TF }
-\ExplSyntaxOff
+\ProvidesPackage{markdownthemewitiko_markdown_defaults}[2024/01/03]%
% \end{macrocode}
-% \begin{markdown}
-%
% \iffalse
-%</latex>
+%</themes-witiko-markdown-defaults-latex>
%<*context>
% \fi
+% \par
+% \begin{markdown}
+%
+% Please, see Section <#sec:latex-themes-implementation> for implementation
+% details of the built-in \LaTeX{} themes.
%
% \Hologo{ConTeXt} Interface {#contextinterface}
%----------------------------
%
+% \end{markdown}
% \iffalse
%</context>
%<*manual-interfaces>
@@ -20297,7 +21658,7 @@ following content:
$\sqrt{-1}$ *equals* $i$.
\stopmarkdown
-\setupmarkdown[hybrid = yes]
+\setupmarkdown[texmathdollars = yes]
\startmarkdown
$\sqrt{-1}$ *equals* $i$.
\stopmarkdown
@@ -20306,7 +21667,7 @@ $\sqrt{-1}$ *equals* $i$.
```````
Next, invoke LuaTeX from the terminal:
``` sh
-context document.tex
+context --luatex document.tex
``````
A PDF document named `document.pdf` should be produced and contain the
following text:
@@ -20320,6 +21681,30 @@ following text:
%</manual-interfaces>
%<*context>
% \fi
+% \begin{markdown}
+%
+% To determine whether \Hologo{ConTeXt} is the top layer or if there are other
+% layers above \Hologo{ConTeXt}, we take a look on whether the
+% \mref{c_@@_top_layer_tl} token list has already been defined. If not,
+% we will assume that \Hologo{ConTeXt} is the top layer.
+%
+% \end{markdown}
+% \begin{macrocode}
+\ExplSyntaxOn
+\tl_const:Nn \c_@@_option_layer_context_tl { context }
+\cs_generate_variant:Nn
+ \tl_const:Nn
+ { NV }
+\tl_if_exist:NF
+ \c_@@_top_layer_tl
+ {
+ \tl_const:NV
+ \c_@@_top_layer_tl
+ \c_@@_option_layer_context_tl
+ }
+\ExplSyntaxOff
+% \end{macrocode}
+% \begin{markdown}
%
% The \Hologo{ConTeXt} interface provides a start-stop macro pair for the
% typesetting of markdown input from within \Hologo{ConTeXt} and facilities for
@@ -20331,19 +21716,6 @@ following text:
% \begin{macrocode}
\writestatus{loading}{ConTeXt User Module / markdown}%
\startmodule[markdown]
-\unprotect
-% \end{macrocode}
-% \par
-% \begin{markdown}
-%
-% The \Hologo{ConTeXt} implementation redefines the plain \TeX{} logging macros
-% (see Section <#sec:tex-interface-logging>) to use the \Hologo{ConTeXt}
-% `\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/markdown
@@ -20362,7 +21734,7 @@ following text:
%### Typesetting Markdown
% The interface exposes the \mdef{startmarkdown} and \mdef{stopmarkdown} macro
% pair for the typesetting of a markdown document fragment, and defines the
-% \mdef{inputmarkdown} command.
+% \mdef{inputmarkdown} macro.
%
% \end{markdown}
% \begin{macrocode}
@@ -20423,21 +21795,16 @@ following text:
% \TeX{} interface (see Section <#sec:tex-options>).
%
% The \Hologo{ConTeXt} options may be specified when using the
-% \mref{inputmarkdown} macro (see Section <#sec:contextinterface>), or via the
-% \mdef{setupmarkdown} macro. The \mref{setupmarkdown} macro receives the
-% options to set up as its only argument:
+% \mref{inputmarkdown} macro (see Section <#sec:contextinterface>),
+% via the \mref{markdownSetup} macro, or via the \mdef{setupmarkdown}`[#1]`
+% macro, which is an alias for \mref{markdownSetup}`{#1}`.
%
% \end{markdown}
% \begin{macrocode}
\ExplSyntaxOn
-\cs_new:Nn
- \@@_setup:n
- {
- \keys_set:nn
- { markdown/context-options }
- { #1 }
- }
-\long\def\setupmarkdown[#1]
+\cs_new:Npn
+ \setupmarkdown
+ [ #1 ]
{
\@@_setup:n
{ #1 }
@@ -20446,51 +21813,14 @@ following text:
% \end{macrocode}
% \begin{markdown}
%
-%#### \Hologo{ConTeXt} Interface Options
-% We define the \meta{key}`=`\meta{value} interface for all option macros
-% recognized by the Lua and plain \TeX{} interfaces.
-%
-% \end{markdown}
-% \begin{macrocode}
-\ExplSyntaxOn
-\cs_new:Nn \@@_context_define_option_commands_and_keyvals:
- {
- \seq_map_inline:Nn
- \g_@@_option_layers_seq
- {
- \seq_map_inline:cn
- { g_@@_ ##1 _options_seq }
- {
-% \end{macrocode}
-% \begin{markdown}
-%
-% To make it easier to copy-and-paste options from Pandoc [@macfarlane22] such
-% as `fancy_lists`, `header_attributes`, and `pipe_tables`, we accept
-% snake\\\_case in addition to camelCase variants of options. As a bonus,
-% studies [@sharif10] also show that snake\\\_case is faster to read than
-% camelCase.
-%
-% \end{markdown}
-% \begin{macrocode}
- \@@_with_various_cases:nn
- { ####1 }
- {
- \@@_context_define_option_keyval:nnn
- { ##1 }
- { ####1 }
- { ########1 }
- }
- }
- }
- }
-% \end{macrocode}
-% \begin{markdown}
+%#### Generating Plain \TeX{} Option Macros and Key-Values
%
-% Furthermore, we also accept caseless variants of options in line with the
-% style of \Hologo{ConTeXt}.
+% Unlike plain \TeX{}, we also accept caseless variants of options
+% in line with the style of \Hologo{ConTeXt}.
%
% \end{markdown}
% \begin{macrocode}
+\ExplSyntaxOn
\cs_new:Nn \@@_caseless:N
{
\regex_replace_all:nnN
@@ -20502,52 +21832,89 @@ following text:
{ #1 }
}
\seq_gput_right:Nn \g_@@_cases_seq { @@_caseless:N }
-\cs_new:Nn \@@_context_define_option_keyval:nnn
+% \end{macrocode}
+% \begin{markdown}
+%
+% If \Hologo{ConTeXt} is the top layer, we use the
+% \mref{@@_define_option_commands_and_keyvals:}, \mref{@@_define_renderers:},
+% and \mref{@@_define_renderer_prototypes:} macro to define plain \TeX{}
+% option, token renderer, and token renderer prototype macros and key-values
+% immediately. Otherwise, we postpone the definition until the upper layers
+% have been loaded.
+%
+% \end{markdown}
+% \begin{macrocode}
+\str_if_eq:VVT
+ \c_@@_top_layer_tl
+ \c_@@_option_layer_context_tl
{
- \prop_get:cnN
- { g_@@_ #1 _option_types_prop }
- { #2 }
- \l_tmpa_tl
- \keys_define:nn
- { markdown/context-options }
- {
- #3 .code:n = {
- \tl_set:Nx
- \l_tmpa_tl
- {
- \str_case:nnF
- { ##1 }
- {
- { yes } { true }
- { no } { false }
- }
- { ##1 }
- }
- \@@_set_option_value:nV
- { #2 }
- \l_tmpa_tl
- },
- }
- \str_if_eq:VVT
- \l_tmpa_tl
- \c_@@_option_type_boolean_tl
- {
- \keys_define:nn
- { markdown/context-options }
- {
- #3 .default:n = { true },
- }
- }
+ \@@_define_option_commands_and_keyvals:
+ \@@_define_renderers:
+ \@@_define_renderer_prototypes:
}
-\cs_generate_variant:Nn
- \@@_set_option_value:nn
- { nV }
-\@@_context_define_option_commands_and_keyvals:
\ExplSyntaxOff
% \end{macrocode}
-% \par
+% \iffalse
+%</context>
+%<*manual-options>
+% \fi
+% \begin{markdown}
+
+### Themes
+
+% In Section~\ref{sec:themes}, we described the concept of themes.
+In \Hologo{ConTeXt}, we expand on the concept of
+% themes\iffalse
+[themes](#themes)
+% \fi
+by allowing a theme to be a full-blown \Hologo{ConTeXt} module. Specifically,
+the key-values `theme`=\meta{theme name} and `import`=\meta{theme name} load a
+\Hologo{ConTeXt} module named `t-markdowntheme`\meta{munged theme name}`.tex`
+if it exists and a \TeX{} document named `markdowntheme`\meta{munged theme
+name}`.tex` otherwise.
+
+Having the Markdown package automatically load either the generic `.tex`
+*theme file* or the \Hologo{ConTeXt}-specific `t-*.tex` theme file allows
+developers to have a single *theme file*, when the theme is small or the
+difference between \TeX{} formats is unimportant, and scale up to separate
+theme files native to different \TeX{} formats for large multi-format themes,
+where different code is needed for different \TeX{} formats. To enable code
+reuse, developers can load the `.tex` theme file from the `t-*.tex` theme file
+using the \mref{markdownLoadPlainTeXTheme} macro.
+
+For example, to load a theme named `witiko/tilde` in your document:
+
+``` tex
+\usemodule[t][markdown]
+\setupmarkdown[import=witiko/tilde]
+```````
+
+Built-in \Hologo{ConTeXt} themes provided with the Markdown package include:
+
+\pkg{witiko/markdown/defaults}
+
+: A \Hologo{ConTeXt} theme with the default definitions of token renderer
+ prototypes for plain \TeX{}. This theme is loaded automatically together
+ with the package and explicitly loading it has no effect.
+
+% \end{markdown}
+% \iffalse
+%</manual-options>
+%<*themes-witiko-markdown-defaults-context>
+% \fi
+% \begin{macrocode}
+\startmodule[markdownthemewitiko_markdown_defaults]
+\unprotect
+% \end{macrocode}
+% \iffalse
+%</themes-witiko-markdown-defaults-context>
+%<*manual-options>
+% \fi
% \begin{markdown}
%
+% Please, see Section <#sec:context-themes-implementation> for implementation
+% details of the built-in \Hologo{ConTeXt} themes.
+%
% Implementation {#implementation}
%================
%
@@ -20564,7 +21931,7 @@ following text:
%
% \end{markdown}
% \iffalse
-%</context>
+%</manual-options>
%<*lua>
% \fi
% \begin{markdown}
@@ -20587,9 +21954,9 @@ following text:
% \begin{macrocode}
local upper, format, length =
string.upper, string.format, string.len
-local P, R, S, V, C, Cg, Cb, Cmt, Cc, Ct, B, Cs, any =
+local P, R, S, V, C, Cg, Cb, Cmt, Cc, Ct, B, Cs, Cp, 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)
+ lpeg.Cmt, lpeg.Cc, lpeg.Ct, lpeg.B, lpeg.Cs, lpeg.Cp, lpeg.P(1)
% \end{macrocode}
% \par
% \begin{markdown}
@@ -20949,13 +22316,13 @@ local character_entities = {
["Tab"] = 9,
["NewLine"] = 10,
["excl"] = 33,
- ["quot"] = 34,
["QUOT"] = 34,
+ ["quot"] = 34,
["num"] = 35,
["dollar"] = 36,
["percnt"] = 37,
- ["amp"] = 38,
["AMP"] = 38,
+ ["amp"] = 38,
["apos"] = 39,
["lpar"] = 40,
["rpar"] = 41,
@@ -20967,31 +22334,36 @@ local character_entities = {
["sol"] = 47,
["colon"] = 58,
["semi"] = 59,
- ["lt"] = 60,
["LT"] = 60,
+ ["lt"] = 60,
+ ["nvlt"] = {60, 8402},
+ ["bne"] = {61, 8421},
["equals"] = 61,
- ["gt"] = 62,
["GT"] = 62,
+ ["gt"] = 62,
+ ["nvgt"] = {62, 8402},
["quest"] = 63,
["commat"] = 64,
- ["lsqb"] = 91,
["lbrack"] = 91,
+ ["lsqb"] = 91,
["bsol"] = 92,
- ["rsqb"] = 93,
["rbrack"] = 93,
+ ["rsqb"] = 93,
["Hat"] = 94,
+ ["UnderBar"] = 95,
["lowbar"] = 95,
- ["grave"] = 96,
["DiacriticalGrave"] = 96,
- ["lcub"] = 123,
+ ["grave"] = 96,
+ ["fjlig"] = {102, 106},
["lbrace"] = 123,
+ ["lcub"] = 123,
+ ["VerticalLine"] = 124,
["verbar"] = 124,
["vert"] = 124,
- ["VerticalLine"] = 124,
- ["rcub"] = 125,
["rbrace"] = 125,
- ["nbsp"] = 160,
+ ["rcub"] = 125,
["NonBreakingSpace"] = 160,
+ ["nbsp"] = 160,
["iexcl"] = 161,
["cent"] = 162,
["pound"] = 163,
@@ -21000,36 +22372,35 @@ local character_entities = {
["brvbar"] = 166,
["sect"] = 167,
["Dot"] = 168,
- ["die"] = 168,
["DoubleDot"] = 168,
+ ["die"] = 168,
["uml"] = 168,
- ["copy"] = 169,
["COPY"] = 169,
+ ["copy"] = 169,
["ordf"] = 170,
["laquo"] = 171,
["not"] = 172,
["shy"] = 173,
- ["reg"] = 174,
- ["circledR"] = 174,
["REG"] = 174,
+ ["circledR"] = 174,
+ ["reg"] = 174,
["macr"] = 175,
- ["OverBar"] = 175,
["strns"] = 175,
["deg"] = 176,
+ ["PlusMinus"] = 177,
["plusmn"] = 177,
["pm"] = 177,
- ["PlusMinus"] = 177,
["sup2"] = 178,
["sup3"] = 179,
- ["acute"] = 180,
["DiacriticalAcute"] = 180,
+ ["acute"] = 180,
["micro"] = 181,
["para"] = 182,
- ["middot"] = 183,
- ["centerdot"] = 183,
["CenterDot"] = 183,
- ["cedil"] = 184,
+ ["centerdot"] = 183,
+ ["middot"] = 183,
["Cedilla"] = 184,
+ ["cedil"] = 184,
["sup1"] = 185,
["ordm"] = 186,
["raquo"] = 187,
@@ -21044,6 +22415,7 @@ local character_entities = {
["Atilde"] = 195,
["Auml"] = 196,
["Aring"] = 197,
+ ["angst"] = 197,
["AElig"] = 198,
["Ccedil"] = 199,
["Egrave"] = 200,
@@ -21093,8 +22465,8 @@ local character_entities = {
["ocirc"] = 244,
["otilde"] = 245,
["ouml"] = 246,
- ["divide"] = 247,
["div"] = 247,
+ ["divide"] = 247,
["oslash"] = 248,
["ugrave"] = 249,
["uacute"] = 250,
@@ -21229,20 +22601,19 @@ local character_entities = {
["gacute"] = 501,
["jmath"] = 567,
["circ"] = 710,
- ["caron"] = 711,
["Hacek"] = 711,
- ["breve"] = 728,
+ ["caron"] = 711,
["Breve"] = 728,
- ["dot"] = 729,
+ ["breve"] = 728,
["DiacriticalDot"] = 729,
+ ["dot"] = 729,
["ring"] = 730,
["ogon"] = 731,
- ["tilde"] = 732,
["DiacriticalTilde"] = 732,
- ["dblac"] = 733,
+ ["tilde"] = 732,
["DiacriticalDoubleAcute"] = 733,
+ ["dblac"] = 733,
["DownBreve"] = 785,
- ["UnderBar"] = 818,
["Alpha"] = 913,
["Beta"] = 914,
["Gamma"] = 915,
@@ -21267,12 +22638,12 @@ local character_entities = {
["Chi"] = 935,
["Psi"] = 936,
["Omega"] = 937,
+ ["ohm"] = 937,
["alpha"] = 945,
["beta"] = 946,
["gamma"] = 947,
["delta"] = 948,
- ["epsiv"] = 949,
- ["varepsilon"] = 949,
+ ["epsi"] = 949,
["epsilon"] = 949,
["zeta"] = 950,
["eta"] = 951,
@@ -21286,38 +22657,39 @@ local character_entities = {
["omicron"] = 959,
["pi"] = 960,
["rho"] = 961,
+ ["sigmaf"] = 962,
["sigmav"] = 962,
["varsigma"] = 962,
- ["sigmaf"] = 962,
["sigma"] = 963,
["tau"] = 964,
["upsi"] = 965,
["upsilon"] = 965,
["phi"] = 966,
- ["phiv"] = 966,
- ["varphi"] = 966,
["chi"] = 967,
["psi"] = 968,
["omega"] = 969,
+ ["thetasym"] = 977,
["thetav"] = 977,
["vartheta"] = 977,
- ["thetasym"] = 977,
["Upsi"] = 978,
["upsih"] = 978,
+ ["phiv"] = 981,
["straightphi"] = 981,
+ ["varphi"] = 981,
["piv"] = 982,
["varpi"] = 982,
["Gammad"] = 988,
- ["gammad"] = 989,
["digamma"] = 989,
+ ["gammad"] = 989,
["kappav"] = 1008,
["varkappa"] = 1008,
["rhov"] = 1009,
["varrho"] = 1009,
- ["epsi"] = 1013,
+ ["epsiv"] = 1013,
["straightepsilon"] = 1013,
- ["bepsi"] = 1014,
+ ["varepsilon"] = 1013,
["backepsilon"] = 1014,
+ ["bepsi"] = 1014,
["IOcy"] = 1025,
["DJcy"] = 1026,
["GJcy"] = 1027,
@@ -21416,40 +22788,40 @@ local character_entities = {
["emsp14"] = 8197,
["numsp"] = 8199,
["puncsp"] = 8200,
- ["thinsp"] = 8201,
["ThinSpace"] = 8201,
- ["hairsp"] = 8202,
+ ["thinsp"] = 8201,
["VeryThinSpace"] = 8202,
- ["ZeroWidthSpace"] = 8203,
- ["NegativeVeryThinSpace"] = 8203,
- ["NegativeThinSpace"] = 8203,
+ ["hairsp"] = 8202,
["NegativeMediumSpace"] = 8203,
["NegativeThickSpace"] = 8203,
+ ["NegativeThinSpace"] = 8203,
+ ["NegativeVeryThinSpace"] = 8203,
+ ["ZeroWidthSpace"] = 8203,
["zwnj"] = 8204,
["zwj"] = 8205,
["lrm"] = 8206,
["rlm"] = 8207,
- ["hyphen"] = 8208,
["dash"] = 8208,
+ ["hyphen"] = 8208,
["ndash"] = 8211,
["mdash"] = 8212,
["horbar"] = 8213,
["Verbar"] = 8214,
["Vert"] = 8214,
- ["lsquo"] = 8216,
["OpenCurlyQuote"] = 8216,
+ ["lsquo"] = 8216,
+ ["CloseCurlyQuote"] = 8217,
["rsquo"] = 8217,
["rsquor"] = 8217,
- ["CloseCurlyQuote"] = 8217,
["lsquor"] = 8218,
["sbquo"] = 8218,
- ["ldquo"] = 8220,
["OpenCurlyDoubleQuote"] = 8220,
+ ["ldquo"] = 8220,
+ ["CloseCurlyDoubleQuote"] = 8221,
["rdquo"] = 8221,
["rdquor"] = 8221,
- ["CloseCurlyDoubleQuote"] = 8221,
- ["ldquor"] = 8222,
["bdquo"] = 8222,
+ ["ldquor"] = 8222,
["dagger"] = 8224,
["Dagger"] = 8225,
["ddagger"] = 8225,
@@ -21463,10 +22835,11 @@ local character_entities = {
["prime"] = 8242,
["Prime"] = 8243,
["tprime"] = 8244,
- ["bprime"] = 8245,
["backprime"] = 8245,
+ ["bprime"] = 8245,
["lsaquo"] = 8249,
["rsaquo"] = 8250,
+ ["OverBar"] = 8254,
["oline"] = 8254,
["caret"] = 8257,
["hybull"] = 8259,
@@ -21474,6 +22847,7 @@ local character_entities = {
["bsemi"] = 8271,
["qprime"] = 8279,
["MediumSpace"] = 8287,
+ ["ThickSpace"] = {8287, 8202},
["NoBreak"] = 8288,
["ApplyFunction"] = 8289,
["af"] = 8289,
@@ -21482,34 +22856,34 @@ local character_entities = {
["InvisibleComma"] = 8291,
["ic"] = 8291,
["euro"] = 8364,
- ["tdot"] = 8411,
["TripleDot"] = 8411,
+ ["tdot"] = 8411,
["DotDot"] = 8412,
["Copf"] = 8450,
["complexes"] = 8450,
["incare"] = 8453,
["gscr"] = 8458,
- ["hamilt"] = 8459,
["HilbertSpace"] = 8459,
["Hscr"] = 8459,
+ ["hamilt"] = 8459,
["Hfr"] = 8460,
["Poincareplane"] = 8460,
- ["quaternions"] = 8461,
["Hopf"] = 8461,
+ ["quaternions"] = 8461,
["planckh"] = 8462,
- ["planck"] = 8463,
["hbar"] = 8463,
- ["plankv"] = 8463,
["hslash"] = 8463,
+ ["planck"] = 8463,
+ ["plankv"] = 8463,
["Iscr"] = 8464,
["imagline"] = 8464,
- ["image"] = 8465,
+ ["Ifr"] = 8465,
["Im"] = 8465,
+ ["image"] = 8465,
["imagpart"] = 8465,
- ["Ifr"] = 8465,
+ ["Laplacetrf"] = 8466,
["Lscr"] = 8466,
["lagran"] = 8466,
- ["Laplacetrf"] = 8466,
["ell"] = 8467,
["Nopf"] = 8469,
["naturals"] = 8469,
@@ -21519,40 +22893,38 @@ local character_entities = {
["wp"] = 8472,
["Popf"] = 8473,
["primes"] = 8473,
- ["rationals"] = 8474,
["Qopf"] = 8474,
+ ["rationals"] = 8474,
["Rscr"] = 8475,
["realine"] = 8475,
- ["real"] = 8476,
["Re"] = 8476,
- ["realpart"] = 8476,
["Rfr"] = 8476,
- ["reals"] = 8477,
+ ["real"] = 8476,
+ ["realpart"] = 8476,
["Ropf"] = 8477,
+ ["reals"] = 8477,
["rx"] = 8478,
- ["trade"] = 8482,
["TRADE"] = 8482,
- ["integers"] = 8484,
+ ["trade"] = 8482,
["Zopf"] = 8484,
- ["ohm"] = 8486,
+ ["integers"] = 8484,
["mho"] = 8487,
["Zfr"] = 8488,
["zeetrf"] = 8488,
["iiota"] = 8489,
- ["angst"] = 8491,
- ["bernou"] = 8492,
["Bernoullis"] = 8492,
["Bscr"] = 8492,
- ["Cfr"] = 8493,
+ ["bernou"] = 8492,
["Cayleys"] = 8493,
+ ["Cfr"] = 8493,
["escr"] = 8495,
["Escr"] = 8496,
["expectation"] = 8496,
- ["Fscr"] = 8497,
["Fouriertrf"] = 8497,
- ["phmmat"] = 8499,
+ ["Fscr"] = 8497,
["Mellintrf"] = 8499,
["Mscr"] = 8499,
+ ["phmmat"] = 8499,
["order"] = 8500,
["orderof"] = 8500,
["oscr"] = 8500,
@@ -21566,8 +22938,8 @@ local character_entities = {
["DifferentialD"] = 8518,
["dd"] = 8518,
["ExponentialE"] = 8519,
- ["exponentiale"] = 8519,
["ee"] = 8519,
+ ["exponentiale"] = 8519,
["ImaginaryI"] = 8520,
["ii"] = 8520,
["frac13"] = 8531,
@@ -21582,46 +22954,47 @@ local character_entities = {
["frac38"] = 8540,
["frac58"] = 8541,
["frac78"] = 8542,
+ ["LeftArrow"] = 8592,
+ ["ShortLeftArrow"] = 8592,
["larr"] = 8592,
["leftarrow"] = 8592,
- ["LeftArrow"] = 8592,
["slarr"] = 8592,
- ["ShortLeftArrow"] = 8592,
+ ["ShortUpArrow"] = 8593,
+ ["UpArrow"] = 8593,
["uarr"] = 8593,
["uparrow"] = 8593,
- ["UpArrow"] = 8593,
- ["ShortUpArrow"] = 8593,
+ ["RightArrow"] = 8594,
+ ["ShortRightArrow"] = 8594,
["rarr"] = 8594,
["rightarrow"] = 8594,
- ["RightArrow"] = 8594,
["srarr"] = 8594,
- ["ShortRightArrow"] = 8594,
- ["darr"] = 8595,
- ["downarrow"] = 8595,
["DownArrow"] = 8595,
["ShortDownArrow"] = 8595,
+ ["darr"] = 8595,
+ ["downarrow"] = 8595,
+ ["LeftRightArrow"] = 8596,
["harr"] = 8596,
["leftrightarrow"] = 8596,
- ["LeftRightArrow"] = 8596,
- ["varr"] = 8597,
- ["updownarrow"] = 8597,
["UpDownArrow"] = 8597,
- ["nwarr"] = 8598,
+ ["updownarrow"] = 8597,
+ ["varr"] = 8597,
["UpperLeftArrow"] = 8598,
+ ["nwarr"] = 8598,
["nwarrow"] = 8598,
- ["nearr"] = 8599,
["UpperRightArrow"] = 8599,
+ ["nearr"] = 8599,
["nearrow"] = 8599,
+ ["LowerRightArrow"] = 8600,
["searr"] = 8600,
["searrow"] = 8600,
- ["LowerRightArrow"] = 8600,
+ ["LowerLeftArrow"] = 8601,
["swarr"] = 8601,
["swarrow"] = 8601,
- ["LowerLeftArrow"] = 8601,
["nlarr"] = 8602,
["nleftarrow"] = 8602,
["nrarr"] = 8603,
["nrightarrow"] = 8603,
+ ["nrarrw"] = {8605, 824},
["rarrw"] = 8605,
["rightsquigarrow"] = 8605,
["Larr"] = 8606,
@@ -21638,27 +23011,27 @@ local character_entities = {
["mapstoleft"] = 8612,
["UpTeeArrow"] = 8613,
["mapstoup"] = 8613,
- ["map"] = 8614,
["RightTeeArrow"] = 8614,
+ ["map"] = 8614,
["mapsto"] = 8614,
["DownTeeArrow"] = 8615,
["mapstodown"] = 8615,
- ["larrhk"] = 8617,
["hookleftarrow"] = 8617,
- ["rarrhk"] = 8618,
+ ["larrhk"] = 8617,
["hookrightarrow"] = 8618,
+ ["rarrhk"] = 8618,
["larrlp"] = 8619,
["looparrowleft"] = 8619,
- ["rarrlp"] = 8620,
["looparrowright"] = 8620,
+ ["rarrlp"] = 8620,
["harrw"] = 8621,
["leftrightsquigarrow"] = 8621,
["nharr"] = 8622,
["nleftrightarrow"] = 8622,
- ["lsh"] = 8624,
["Lsh"] = 8624,
- ["rsh"] = 8625,
+ ["lsh"] = 8624,
["Rsh"] = 8625,
+ ["rsh"] = 8625,
["ldsh"] = 8626,
["rdsh"] = 8627,
["crarr"] = 8629,
@@ -21666,456 +23039,505 @@ local character_entities = {
["curvearrowleft"] = 8630,
["curarr"] = 8631,
["curvearrowright"] = 8631,
- ["olarr"] = 8634,
["circlearrowleft"] = 8634,
- ["orarr"] = 8635,
+ ["olarr"] = 8634,
["circlearrowright"] = 8635,
- ["lharu"] = 8636,
+ ["orarr"] = 8635,
["LeftVector"] = 8636,
["leftharpoonup"] = 8636,
- ["lhard"] = 8637,
- ["leftharpoondown"] = 8637,
+ ["lharu"] = 8636,
["DownLeftVector"] = 8637,
+ ["leftharpoondown"] = 8637,
+ ["lhard"] = 8637,
+ ["RightUpVector"] = 8638,
["uharr"] = 8638,
["upharpoonright"] = 8638,
- ["RightUpVector"] = 8638,
+ ["LeftUpVector"] = 8639,
["uharl"] = 8639,
["upharpoonleft"] = 8639,
- ["LeftUpVector"] = 8639,
- ["rharu"] = 8640,
["RightVector"] = 8640,
+ ["rharu"] = 8640,
["rightharpoonup"] = 8640,
+ ["DownRightVector"] = 8641,
["rhard"] = 8641,
["rightharpoondown"] = 8641,
- ["DownRightVector"] = 8641,
- ["dharr"] = 8642,
["RightDownVector"] = 8642,
+ ["dharr"] = 8642,
["downharpoonright"] = 8642,
- ["dharl"] = 8643,
["LeftDownVector"] = 8643,
+ ["dharl"] = 8643,
["downharpoonleft"] = 8643,
- ["rlarr"] = 8644,
- ["rightleftarrows"] = 8644,
["RightArrowLeftArrow"] = 8644,
- ["udarr"] = 8645,
+ ["rightleftarrows"] = 8644,
+ ["rlarr"] = 8644,
["UpArrowDownArrow"] = 8645,
- ["lrarr"] = 8646,
- ["leftrightarrows"] = 8646,
+ ["udarr"] = 8645,
["LeftArrowRightArrow"] = 8646,
- ["llarr"] = 8647,
+ ["leftrightarrows"] = 8646,
+ ["lrarr"] = 8646,
["leftleftarrows"] = 8647,
- ["uuarr"] = 8648,
+ ["llarr"] = 8647,
["upuparrows"] = 8648,
- ["rrarr"] = 8649,
+ ["uuarr"] = 8648,
["rightrightarrows"] = 8649,
+ ["rrarr"] = 8649,
["ddarr"] = 8650,
["downdownarrows"] = 8650,
- ["lrhar"] = 8651,
["ReverseEquilibrium"] = 8651,
["leftrightharpoons"] = 8651,
- ["rlhar"] = 8652,
- ["rightleftharpoons"] = 8652,
+ ["lrhar"] = 8651,
["Equilibrium"] = 8652,
- ["nlArr"] = 8653,
+ ["rightleftharpoons"] = 8652,
+ ["rlhar"] = 8652,
["nLeftarrow"] = 8653,
- ["nhArr"] = 8654,
+ ["nlArr"] = 8653,
["nLeftrightarrow"] = 8654,
- ["nrArr"] = 8655,
+ ["nhArr"] = 8654,
["nRightarrow"] = 8655,
- ["lArr"] = 8656,
- ["Leftarrow"] = 8656,
+ ["nrArr"] = 8655,
["DoubleLeftArrow"] = 8656,
- ["uArr"] = 8657,
- ["Uparrow"] = 8657,
+ ["Leftarrow"] = 8656,
+ ["lArr"] = 8656,
["DoubleUpArrow"] = 8657,
- ["rArr"] = 8658,
- ["Rightarrow"] = 8658,
- ["Implies"] = 8658,
+ ["Uparrow"] = 8657,
+ ["uArr"] = 8657,
["DoubleRightArrow"] = 8658,
- ["dArr"] = 8659,
- ["Downarrow"] = 8659,
+ ["Implies"] = 8658,
+ ["Rightarrow"] = 8658,
+ ["rArr"] = 8658,
["DoubleDownArrow"] = 8659,
- ["hArr"] = 8660,
- ["Leftrightarrow"] = 8660,
+ ["Downarrow"] = 8659,
+ ["dArr"] = 8659,
["DoubleLeftRightArrow"] = 8660,
+ ["Leftrightarrow"] = 8660,
+ ["hArr"] = 8660,
["iff"] = 8660,
- ["vArr"] = 8661,
- ["Updownarrow"] = 8661,
["DoubleUpDownArrow"] = 8661,
+ ["Updownarrow"] = 8661,
+ ["vArr"] = 8661,
["nwArr"] = 8662,
["neArr"] = 8663,
["seArr"] = 8664,
["swArr"] = 8665,
- ["lAarr"] = 8666,
["Lleftarrow"] = 8666,
- ["rAarr"] = 8667,
+ ["lAarr"] = 8666,
["Rrightarrow"] = 8667,
+ ["rAarr"] = 8667,
["zigrarr"] = 8669,
- ["larrb"] = 8676,
["LeftArrowBar"] = 8676,
- ["rarrb"] = 8677,
+ ["larrb"] = 8676,
["RightArrowBar"] = 8677,
- ["duarr"] = 8693,
+ ["rarrb"] = 8677,
["DownArrowUpArrow"] = 8693,
+ ["duarr"] = 8693,
["loarr"] = 8701,
["roarr"] = 8702,
["hoarr"] = 8703,
- ["forall"] = 8704,
["ForAll"] = 8704,
+ ["forall"] = 8704,
["comp"] = 8705,
["complement"] = 8705,
- ["part"] = 8706,
["PartialD"] = 8706,
- ["exist"] = 8707,
+ ["npart"] = {8706, 824},
+ ["part"] = 8706,
["Exists"] = 8707,
- ["nexist"] = 8708,
+ ["exist"] = 8707,
["NotExists"] = 8708,
+ ["nexist"] = 8708,
["nexists"] = 8708,
["empty"] = 8709,
["emptyset"] = 8709,
["emptyv"] = 8709,
["varnothing"] = 8709,
- ["nabla"] = 8711,
["Del"] = 8711,
- ["isin"] = 8712,
- ["isinv"] = 8712,
+ ["nabla"] = 8711,
["Element"] = 8712,
["in"] = 8712,
- ["notin"] = 8713,
+ ["isin"] = 8712,
+ ["isinv"] = 8712,
["NotElement"] = 8713,
+ ["notin"] = 8713,
["notinva"] = 8713,
- ["niv"] = 8715,
["ReverseElement"] = 8715,
- ["ni"] = 8715,
["SuchThat"] = 8715,
+ ["ni"] = 8715,
+ ["niv"] = 8715,
+ ["NotReverseElement"] = 8716,
["notni"] = 8716,
["notniva"] = 8716,
- ["NotReverseElement"] = 8716,
- ["prod"] = 8719,
["Product"] = 8719,
- ["coprod"] = 8720,
+ ["prod"] = 8719,
["Coproduct"] = 8720,
- ["sum"] = 8721,
+ ["coprod"] = 8720,
["Sum"] = 8721,
+ ["sum"] = 8721,
["minus"] = 8722,
+ ["MinusPlus"] = 8723,
["mnplus"] = 8723,
["mp"] = 8723,
- ["MinusPlus"] = 8723,
- ["plusdo"] = 8724,
["dotplus"] = 8724,
- ["setmn"] = 8726,
- ["setminus"] = 8726,
+ ["plusdo"] = 8724,
["Backslash"] = 8726,
- ["ssetmn"] = 8726,
+ ["setminus"] = 8726,
+ ["setmn"] = 8726,
["smallsetminus"] = 8726,
+ ["ssetmn"] = 8726,
["lowast"] = 8727,
- ["compfn"] = 8728,
["SmallCircle"] = 8728,
- ["radic"] = 8730,
+ ["compfn"] = 8728,
["Sqrt"] = 8730,
+ ["radic"] = 8730,
+ ["Proportional"] = 8733,
["prop"] = 8733,
["propto"] = 8733,
- ["Proportional"] = 8733,
- ["vprop"] = 8733,
["varpropto"] = 8733,
+ ["vprop"] = 8733,
["infin"] = 8734,
["angrt"] = 8735,
["ang"] = 8736,
["angle"] = 8736,
+ ["nang"] = {8736, 8402},
["angmsd"] = 8737,
["measuredangle"] = 8737,
["angsph"] = 8738,
- ["mid"] = 8739,
["VerticalBar"] = 8739,
- ["smid"] = 8739,
+ ["mid"] = 8739,
["shortmid"] = 8739,
- ["nmid"] = 8740,
+ ["smid"] = 8739,
["NotVerticalBar"] = 8740,
- ["nsmid"] = 8740,
+ ["nmid"] = 8740,
["nshortmid"] = 8740,
+ ["nsmid"] = 8740,
+ ["DoubleVerticalBar"] = 8741,
["par"] = 8741,
["parallel"] = 8741,
- ["DoubleVerticalBar"] = 8741,
- ["spar"] = 8741,
["shortparallel"] = 8741,
+ ["spar"] = 8741,
+ ["NotDoubleVerticalBar"] = 8742,
["npar"] = 8742,
["nparallel"] = 8742,
- ["NotDoubleVerticalBar"] = 8742,
- ["nspar"] = 8742,
["nshortparallel"] = 8742,
+ ["nspar"] = 8742,
["and"] = 8743,
["wedge"] = 8743,
["or"] = 8744,
["vee"] = 8744,
["cap"] = 8745,
+ ["caps"] = {8745, 65024},
["cup"] = 8746,
- ["int"] = 8747,
+ ["cups"] = {8746, 65024},
["Integral"] = 8747,
+ ["int"] = 8747,
["Int"] = 8748,
- ["tint"] = 8749,
["iiint"] = 8749,
+ ["tint"] = 8749,
+ ["ContourIntegral"] = 8750,
["conint"] = 8750,
["oint"] = 8750,
- ["ContourIntegral"] = 8750,
["Conint"] = 8751,
["DoubleContourIntegral"] = 8751,
["Cconint"] = 8752,
["cwint"] = 8753,
- ["cwconint"] = 8754,
["ClockwiseContourIntegral"] = 8754,
- ["awconint"] = 8755,
+ ["cwconint"] = 8754,
["CounterClockwiseContourIntegral"] = 8755,
+ ["awconint"] = 8755,
+ ["Therefore"] = 8756,
["there4"] = 8756,
["therefore"] = 8756,
- ["Therefore"] = 8756,
+ ["Because"] = 8757,
["becaus"] = 8757,
["because"] = 8757,
- ["Because"] = 8757,
["ratio"] = 8758,
["Colon"] = 8759,
["Proportion"] = 8759,
- ["minusd"] = 8760,
["dotminus"] = 8760,
+ ["minusd"] = 8760,
["mDDot"] = 8762,
["homtht"] = 8763,
- ["sim"] = 8764,
["Tilde"] = 8764,
- ["thksim"] = 8764,
+ ["nvsim"] = {8764, 8402},
+ ["sim"] = 8764,
["thicksim"] = 8764,
- ["bsim"] = 8765,
+ ["thksim"] = 8764,
["backsim"] = 8765,
+ ["bsim"] = 8765,
+ ["race"] = {8765, 817},
["ac"] = 8766,
+ ["acE"] = {8766, 819},
["mstpos"] = 8766,
["acd"] = 8767,
- ["wreath"] = 8768,
["VerticalTilde"] = 8768,
["wr"] = 8768,
- ["nsim"] = 8769,
+ ["wreath"] = 8768,
["NotTilde"] = 8769,
- ["esim"] = 8770,
+ ["nsim"] = 8769,
["EqualTilde"] = 8770,
+ ["NotEqualTilde"] = {8770, 824},
["eqsim"] = 8770,
- ["sime"] = 8771,
+ ["esim"] = 8770,
+ ["nesim"] = {8770, 824},
["TildeEqual"] = 8771,
+ ["sime"] = 8771,
["simeq"] = 8771,
+ ["NotTildeEqual"] = 8772,
["nsime"] = 8772,
["nsimeq"] = 8772,
- ["NotTildeEqual"] = 8772,
- ["cong"] = 8773,
["TildeFullEqual"] = 8773,
+ ["cong"] = 8773,
["simne"] = 8774,
- ["ncong"] = 8775,
["NotTildeFullEqual"] = 8775,
- ["asymp"] = 8776,
- ["ap"] = 8776,
+ ["ncong"] = 8775,
["TildeTilde"] = 8776,
+ ["ap"] = 8776,
["approx"] = 8776,
- ["thkap"] = 8776,
+ ["asymp"] = 8776,
["thickapprox"] = 8776,
- ["nap"] = 8777,
+ ["thkap"] = 8776,
["NotTildeTilde"] = 8777,
+ ["nap"] = 8777,
["napprox"] = 8777,
["ape"] = 8778,
["approxeq"] = 8778,
["apid"] = 8779,
- ["bcong"] = 8780,
+ ["napid"] = {8779, 824},
["backcong"] = 8780,
- ["asympeq"] = 8781,
+ ["bcong"] = 8780,
["CupCap"] = 8781,
- ["bump"] = 8782,
- ["HumpDownHump"] = 8782,
+ ["asympeq"] = 8781,
+ ["nvap"] = {8781, 8402},
["Bumpeq"] = 8782,
- ["bumpe"] = 8783,
+ ["HumpDownHump"] = 8782,
+ ["NotHumpDownHump"] = {8782, 824},
+ ["bump"] = 8782,
+ ["nbump"] = {8782, 824},
["HumpEqual"] = 8783,
+ ["NotHumpEqual"] = {8783, 824},
+ ["bumpe"] = 8783,
["bumpeq"] = 8783,
- ["esdot"] = 8784,
+ ["nbumpe"] = {8783, 824},
["DotEqual"] = 8784,
["doteq"] = 8784,
- ["eDot"] = 8785,
+ ["esdot"] = 8784,
+ ["nedot"] = {8784, 824},
["doteqdot"] = 8785,
+ ["eDot"] = 8785,
["efDot"] = 8786,
["fallingdotseq"] = 8786,
["erDot"] = 8787,
["risingdotseq"] = 8787,
+ ["Assign"] = 8788,
["colone"] = 8788,
["coloneq"] = 8788,
- ["Assign"] = 8788,
["ecolon"] = 8789,
["eqcolon"] = 8789,
["ecir"] = 8790,
["eqcirc"] = 8790,
- ["cire"] = 8791,
["circeq"] = 8791,
+ ["cire"] = 8791,
["wedgeq"] = 8793,
["veeeq"] = 8794,
- ["trie"] = 8796,
["triangleq"] = 8796,
+ ["trie"] = 8796,
["equest"] = 8799,
["questeq"] = 8799,
- ["ne"] = 8800,
["NotEqual"] = 8800,
- ["equiv"] = 8801,
+ ["ne"] = 8800,
["Congruent"] = 8801,
- ["nequiv"] = 8802,
+ ["bnequiv"] = {8801, 8421},
+ ["equiv"] = 8801,
["NotCongruent"] = 8802,
+ ["nequiv"] = 8802,
["le"] = 8804,
["leq"] = 8804,
- ["ge"] = 8805,
+ ["nvle"] = {8804, 8402},
["GreaterEqual"] = 8805,
+ ["ge"] = 8805,
["geq"] = 8805,
- ["lE"] = 8806,
+ ["nvge"] = {8805, 8402},
["LessFullEqual"] = 8806,
+ ["lE"] = 8806,
["leqq"] = 8806,
- ["gE"] = 8807,
+ ["nlE"] = {8806, 824},
+ ["nleqq"] = {8806, 824},
["GreaterFullEqual"] = 8807,
+ ["NotGreaterFullEqual"] = {8807, 824},
+ ["gE"] = 8807,
["geqq"] = 8807,
+ ["ngE"] = {8807, 824},
+ ["ngeqq"] = {8807, 824},
["lnE"] = 8808,
["lneqq"] = 8808,
+ ["lvertneqq"] = {8808, 65024},
+ ["lvnE"] = {8808, 65024},
["gnE"] = 8809,
["gneqq"] = 8809,
+ ["gvertneqq"] = {8809, 65024},
+ ["gvnE"] = {8809, 65024},
["Lt"] = 8810,
["NestedLessLess"] = 8810,
+ ["NotLessLess"] = {8810, 824},
["ll"] = 8810,
+ ["nLt"] = {8810, 8402},
+ ["nLtv"] = {8810, 824},
["Gt"] = 8811,
["NestedGreaterGreater"] = 8811,
+ ["NotGreaterGreater"] = {8811, 824},
["gg"] = 8811,
- ["twixt"] = 8812,
+ ["nGt"] = {8811, 8402},
+ ["nGtv"] = {8811, 824},
["between"] = 8812,
+ ["twixt"] = 8812,
["NotCupCap"] = 8813,
- ["nlt"] = 8814,
["NotLess"] = 8814,
["nless"] = 8814,
- ["ngt"] = 8815,
+ ["nlt"] = 8814,
["NotGreater"] = 8815,
+ ["ngt"] = 8815,
["ngtr"] = 8815,
- ["nle"] = 8816,
["NotLessEqual"] = 8816,
+ ["nle"] = 8816,
["nleq"] = 8816,
- ["nge"] = 8817,
["NotGreaterEqual"] = 8817,
+ ["nge"] = 8817,
["ngeq"] = 8817,
- ["lsim"] = 8818,
["LessTilde"] = 8818,
["lesssim"] = 8818,
+ ["lsim"] = 8818,
+ ["GreaterTilde"] = 8819,
["gsim"] = 8819,
["gtrsim"] = 8819,
- ["GreaterTilde"] = 8819,
- ["nlsim"] = 8820,
["NotLessTilde"] = 8820,
- ["ngsim"] = 8821,
+ ["nlsim"] = 8820,
["NotGreaterTilde"] = 8821,
- ["lg"] = 8822,
- ["lessgtr"] = 8822,
+ ["ngsim"] = 8821,
["LessGreater"] = 8822,
+ ["lessgtr"] = 8822,
+ ["lg"] = 8822,
+ ["GreaterLess"] = 8823,
["gl"] = 8823,
["gtrless"] = 8823,
- ["GreaterLess"] = 8823,
- ["ntlg"] = 8824,
["NotLessGreater"] = 8824,
- ["ntgl"] = 8825,
+ ["ntlg"] = 8824,
["NotGreaterLess"] = 8825,
- ["pr"] = 8826,
+ ["ntgl"] = 8825,
["Precedes"] = 8826,
+ ["pr"] = 8826,
["prec"] = 8826,
- ["sc"] = 8827,
["Succeeds"] = 8827,
+ ["sc"] = 8827,
["succ"] = 8827,
- ["prcue"] = 8828,
["PrecedesSlantEqual"] = 8828,
+ ["prcue"] = 8828,
["preccurlyeq"] = 8828,
- ["sccue"] = 8829,
["SucceedsSlantEqual"] = 8829,
+ ["sccue"] = 8829,
["succcurlyeq"] = 8829,
- ["prsim"] = 8830,
- ["precsim"] = 8830,
["PrecedesTilde"] = 8830,
+ ["precsim"] = 8830,
+ ["prsim"] = 8830,
+ ["NotSucceedsTilde"] = {8831, 824},
+ ["SucceedsTilde"] = 8831,
["scsim"] = 8831,
["succsim"] = 8831,
- ["SucceedsTilde"] = 8831,
+ ["NotPrecedes"] = 8832,
["npr"] = 8832,
["nprec"] = 8832,
- ["NotPrecedes"] = 8832,
+ ["NotSucceeds"] = 8833,
["nsc"] = 8833,
["nsucc"] = 8833,
- ["NotSucceeds"] = 8833,
+ ["NotSubset"] = {8834, 8402},
+ ["nsubset"] = {8834, 8402},
["sub"] = 8834,
["subset"] = 8834,
+ ["vnsub"] = {8834, 8402},
+ ["NotSuperset"] = {8835, 8402},
+ ["Superset"] = 8835,
+ ["nsupset"] = {8835, 8402},
["sup"] = 8835,
["supset"] = 8835,
- ["Superset"] = 8835,
+ ["vnsup"] = {8835, 8402},
["nsub"] = 8836,
["nsup"] = 8837,
- ["sube"] = 8838,
["SubsetEqual"] = 8838,
+ ["sube"] = 8838,
["subseteq"] = 8838,
+ ["SupersetEqual"] = 8839,
["supe"] = 8839,
["supseteq"] = 8839,
- ["SupersetEqual"] = 8839,
+ ["NotSubsetEqual"] = 8840,
["nsube"] = 8840,
["nsubseteq"] = 8840,
- ["NotSubsetEqual"] = 8840,
+ ["NotSupersetEqual"] = 8841,
["nsupe"] = 8841,
["nsupseteq"] = 8841,
- ["NotSupersetEqual"] = 8841,
["subne"] = 8842,
["subsetneq"] = 8842,
+ ["varsubsetneq"] = {8842, 65024},
+ ["vsubne"] = {8842, 65024},
["supne"] = 8843,
["supsetneq"] = 8843,
+ ["varsupsetneq"] = {8843, 65024},
+ ["vsupne"] = {8843, 65024},
["cupdot"] = 8845,
- ["uplus"] = 8846,
["UnionPlus"] = 8846,
- ["sqsub"] = 8847,
+ ["uplus"] = 8846,
+ ["NotSquareSubset"] = {8847, 824},
["SquareSubset"] = 8847,
+ ["sqsub"] = 8847,
["sqsubset"] = 8847,
- ["sqsup"] = 8848,
+ ["NotSquareSuperset"] = {8848, 824},
["SquareSuperset"] = 8848,
+ ["sqsup"] = 8848,
["sqsupset"] = 8848,
- ["sqsube"] = 8849,
["SquareSubsetEqual"] = 8849,
+ ["sqsube"] = 8849,
["sqsubseteq"] = 8849,
- ["sqsupe"] = 8850,
["SquareSupersetEqual"] = 8850,
+ ["sqsupe"] = 8850,
["sqsupseteq"] = 8850,
- ["sqcap"] = 8851,
["SquareIntersection"] = 8851,
- ["sqcup"] = 8852,
+ ["sqcap"] = 8851,
+ ["sqcaps"] = {8851, 65024},
["SquareUnion"] = 8852,
- ["oplus"] = 8853,
+ ["sqcup"] = 8852,
+ ["sqcups"] = {8852, 65024},
["CirclePlus"] = 8853,
- ["ominus"] = 8854,
+ ["oplus"] = 8853,
["CircleMinus"] = 8854,
- ["otimes"] = 8855,
+ ["ominus"] = 8854,
["CircleTimes"] = 8855,
+ ["otimes"] = 8855,
["osol"] = 8856,
- ["odot"] = 8857,
["CircleDot"] = 8857,
- ["ocir"] = 8858,
+ ["odot"] = 8857,
["circledcirc"] = 8858,
- ["oast"] = 8859,
+ ["ocir"] = 8858,
["circledast"] = 8859,
- ["odash"] = 8861,
+ ["oast"] = 8859,
["circleddash"] = 8861,
- ["plusb"] = 8862,
+ ["odash"] = 8861,
["boxplus"] = 8862,
- ["minusb"] = 8863,
+ ["plusb"] = 8862,
["boxminus"] = 8863,
- ["timesb"] = 8864,
+ ["minusb"] = 8863,
["boxtimes"] = 8864,
- ["sdotb"] = 8865,
+ ["timesb"] = 8864,
["dotsquare"] = 8865,
- ["vdash"] = 8866,
+ ["sdotb"] = 8865,
["RightTee"] = 8866,
- ["dashv"] = 8867,
+ ["vdash"] = 8866,
["LeftTee"] = 8867,
- ["top"] = 8868,
+ ["dashv"] = 8867,
["DownTee"] = 8868,
- ["bottom"] = 8869,
+ ["top"] = 8868,
+ ["UpTee"] = 8869,
["bot"] = 8869,
+ ["bottom"] = 8869,
["perp"] = 8869,
- ["UpTee"] = 8869,
["models"] = 8871,
- ["vDash"] = 8872,
["DoubleRightTee"] = 8872,
+ ["vDash"] = 8872,
["Vdash"] = 8873,
["Vvdash"] = 8874,
["VDash"] = 8875,
@@ -22124,22 +23546,24 @@ local character_entities = {
["nVdash"] = 8878,
["nVDash"] = 8879,
["prurel"] = 8880,
- ["vltri"] = 8882,
- ["vartriangleleft"] = 8882,
["LeftTriangle"] = 8882,
- ["vrtri"] = 8883,
- ["vartriangleright"] = 8883,
+ ["vartriangleleft"] = 8882,
+ ["vltri"] = 8882,
["RightTriangle"] = 8883,
+ ["vartriangleright"] = 8883,
+ ["vrtri"] = 8883,
+ ["LeftTriangleEqual"] = 8884,
["ltrie"] = 8884,
+ ["nvltrie"] = {8884, 8402},
["trianglelefteq"] = 8884,
- ["LeftTriangleEqual"] = 8884,
+ ["RightTriangleEqual"] = 8885,
+ ["nvrtrie"] = {8885, 8402},
["rtrie"] = 8885,
["trianglerighteq"] = 8885,
- ["RightTriangleEqual"] = 8885,
["origof"] = 8886,
["imof"] = 8887,
- ["mumap"] = 8888,
["multimap"] = 8888,
+ ["mumap"] = 8888,
["hercon"] = 8889,
["intcal"] = 8890,
["intercal"] = 8890,
@@ -22147,39 +23571,39 @@ local character_entities = {
["barvee"] = 8893,
["angrtvb"] = 8894,
["lrtri"] = 8895,
- ["xwedge"] = 8896,
["Wedge"] = 8896,
["bigwedge"] = 8896,
- ["xvee"] = 8897,
+ ["xwedge"] = 8896,
["Vee"] = 8897,
["bigvee"] = 8897,
- ["xcap"] = 8898,
+ ["xvee"] = 8897,
["Intersection"] = 8898,
["bigcap"] = 8898,
- ["xcup"] = 8899,
+ ["xcap"] = 8898,
["Union"] = 8899,
["bigcup"] = 8899,
+ ["xcup"] = 8899,
+ ["Diamond"] = 8900,
["diam"] = 8900,
["diamond"] = 8900,
- ["Diamond"] = 8900,
["sdot"] = 8901,
- ["sstarf"] = 8902,
["Star"] = 8902,
- ["divonx"] = 8903,
+ ["sstarf"] = 8902,
["divideontimes"] = 8903,
+ ["divonx"] = 8903,
["bowtie"] = 8904,
["ltimes"] = 8905,
["rtimes"] = 8906,
- ["lthree"] = 8907,
["leftthreetimes"] = 8907,
- ["rthree"] = 8908,
+ ["lthree"] = 8907,
["rightthreetimes"] = 8908,
- ["bsime"] = 8909,
+ ["rthree"] = 8908,
["backsimeq"] = 8909,
- ["cuvee"] = 8910,
+ ["bsime"] = 8909,
["curlyvee"] = 8910,
- ["cuwed"] = 8911,
+ ["cuvee"] = 8910,
["curlywedge"] = 8911,
+ ["cuwed"] = 8911,
["Sub"] = 8912,
["Subset"] = 8912,
["Sup"] = 8913,
@@ -22189,49 +23613,53 @@ local character_entities = {
["fork"] = 8916,
["pitchfork"] = 8916,
["epar"] = 8917,
- ["ltdot"] = 8918,
["lessdot"] = 8918,
+ ["ltdot"] = 8918,
["gtdot"] = 8919,
["gtrdot"] = 8919,
["Ll"] = 8920,
+ ["nLl"] = {8920, 824},
["Gg"] = 8921,
["ggg"] = 8921,
- ["leg"] = 8922,
+ ["nGg"] = {8921, 824},
["LessEqualGreater"] = 8922,
+ ["leg"] = 8922,
+ ["lesg"] = {8922, 65024},
["lesseqgtr"] = 8922,
+ ["GreaterEqualLess"] = 8923,
["gel"] = 8923,
+ ["gesl"] = {8923, 65024},
["gtreqless"] = 8923,
- ["GreaterEqualLess"] = 8923,
["cuepr"] = 8926,
["curlyeqprec"] = 8926,
["cuesc"] = 8927,
["curlyeqsucc"] = 8927,
- ["nprcue"] = 8928,
["NotPrecedesSlantEqual"] = 8928,
- ["nsccue"] = 8929,
+ ["nprcue"] = 8928,
["NotSucceedsSlantEqual"] = 8929,
- ["nsqsube"] = 8930,
+ ["nsccue"] = 8929,
["NotSquareSubsetEqual"] = 8930,
- ["nsqsupe"] = 8931,
+ ["nsqsube"] = 8930,
["NotSquareSupersetEqual"] = 8931,
+ ["nsqsupe"] = 8931,
["lnsim"] = 8934,
["gnsim"] = 8935,
- ["prnsim"] = 8936,
["precnsim"] = 8936,
+ ["prnsim"] = 8936,
["scnsim"] = 8937,
["succnsim"] = 8937,
+ ["NotLeftTriangle"] = 8938,
["nltri"] = 8938,
["ntriangleleft"] = 8938,
- ["NotLeftTriangle"] = 8938,
+ ["NotRightTriangle"] = 8939,
["nrtri"] = 8939,
["ntriangleright"] = 8939,
- ["NotRightTriangle"] = 8939,
+ ["NotLeftTriangleEqual"] = 8940,
["nltrie"] = 8940,
["ntrianglelefteq"] = 8940,
- ["NotLeftTriangleEqual"] = 8940,
+ ["NotRightTriangleEqual"] = 8941,
["nrtrie"] = 8941,
["ntrianglerighteq"] = 8941,
- ["NotRightTriangleEqual"] = 8941,
["vellip"] = 8942,
["ctdot"] = 8943,
["utdot"] = 8944,
@@ -22240,9 +23668,11 @@ local character_entities = {
["isinsv"] = 8947,
["isins"] = 8948,
["isindot"] = 8949,
+ ["notindot"] = {8949, 824},
["notinvc"] = 8950,
["notinvb"] = 8951,
["isinE"] = 8953,
+ ["notinE"] = {8953, 824},
["nisd"] = 8954,
["xnis"] = 8955,
["nis"] = 8956,
@@ -22252,14 +23682,14 @@ local character_entities = {
["barwedge"] = 8965,
["Barwed"] = 8966,
["doublebarwedge"] = 8966,
- ["lceil"] = 8968,
["LeftCeiling"] = 8968,
- ["rceil"] = 8969,
+ ["lceil"] = 8968,
["RightCeiling"] = 8969,
- ["lfloor"] = 8970,
+ ["rceil"] = 8969,
["LeftFloor"] = 8970,
- ["rfloor"] = 8971,
+ ["lfloor"] = 8970,
["RightFloor"] = 8971,
+ ["rfloor"] = 8971,
["drcrop"] = 8972,
["dlcrop"] = 8973,
["urcrop"] = 8974,
@@ -22291,10 +23721,10 @@ local character_entities = {
["lmoustache"] = 9136,
["rmoust"] = 9137,
["rmoustache"] = 9137,
- ["tbrk"] = 9140,
["OverBracket"] = 9140,
- ["bbrk"] = 9141,
+ ["tbrk"] = 9140,
["UnderBracket"] = 9141,
+ ["bbrk"] = 9141,
["bbrktbrk"] = 9142,
["OverParenthesis"] = 9180,
["UnderParenthesis"] = 9181,
@@ -22303,10 +23733,10 @@ local character_entities = {
["trpezium"] = 9186,
["elinters"] = 9191,
["blank"] = 9251,
- ["oS"] = 9416,
["circledS"] = 9416,
- ["boxh"] = 9472,
+ ["oS"] = 9416,
["HorizontalLine"] = 9472,
+ ["boxh"] = 9472,
["boxv"] = 9474,
["boxdr"] = 9484,
["boxdl"] = 9488,
@@ -22352,50 +23782,50 @@ local character_entities = {
["blk14"] = 9617,
["blk12"] = 9618,
["blk34"] = 9619,
+ ["Square"] = 9633,
["squ"] = 9633,
["square"] = 9633,
- ["Square"] = 9633,
- ["squf"] = 9642,
- ["squarf"] = 9642,
- ["blacksquare"] = 9642,
["FilledVerySmallSquare"] = 9642,
+ ["blacksquare"] = 9642,
+ ["squarf"] = 9642,
+ ["squf"] = 9642,
["EmptyVerySmallSquare"] = 9643,
["rect"] = 9645,
["marker"] = 9646,
["fltns"] = 9649,
- ["xutri"] = 9651,
["bigtriangleup"] = 9651,
- ["utrif"] = 9652,
+ ["xutri"] = 9651,
["blacktriangle"] = 9652,
- ["utri"] = 9653,
+ ["utrif"] = 9652,
["triangle"] = 9653,
- ["rtrif"] = 9656,
+ ["utri"] = 9653,
["blacktriangleright"] = 9656,
+ ["rtrif"] = 9656,
["rtri"] = 9657,
["triangleright"] = 9657,
- ["xdtri"] = 9661,
["bigtriangledown"] = 9661,
- ["dtrif"] = 9662,
+ ["xdtri"] = 9661,
["blacktriangledown"] = 9662,
+ ["dtrif"] = 9662,
["dtri"] = 9663,
["triangledown"] = 9663,
- ["ltrif"] = 9666,
["blacktriangleleft"] = 9666,
+ ["ltrif"] = 9666,
["ltri"] = 9667,
["triangleleft"] = 9667,
["loz"] = 9674,
["lozenge"] = 9674,
["cir"] = 9675,
["tridot"] = 9708,
- ["xcirc"] = 9711,
["bigcirc"] = 9711,
+ ["xcirc"] = 9711,
["ultri"] = 9720,
["urtri"] = 9721,
["lltri"] = 9722,
["EmptySmallSquare"] = 9723,
["FilledSmallSquare"] = 9724,
- ["starf"] = 9733,
["bigstar"] = 9733,
+ ["starf"] = 9733,
["star"] = 9734,
["phone"] = 9742,
["female"] = 9792,
@@ -22406,8 +23836,8 @@ local character_entities = {
["clubsuit"] = 9827,
["hearts"] = 9829,
["heartsuit"] = 9829,
- ["diams"] = 9830,
["diamondsuit"] = 9830,
+ ["diams"] = 9830,
["sung"] = 9834,
["flat"] = 9837,
["natur"] = 9838,
@@ -22422,51 +23852,53 @@ local character_entities = {
["VerticalSeparator"] = 10072,
["lbbrk"] = 10098,
["rbbrk"] = 10099,
- ["lobrk"] = 10214,
+ ["bsolhsub"] = 10184,
+ ["suphsol"] = 10185,
["LeftDoubleBracket"] = 10214,
- ["robrk"] = 10215,
+ ["lobrk"] = 10214,
["RightDoubleBracket"] = 10215,
- ["lang"] = 10216,
+ ["robrk"] = 10215,
["LeftAngleBracket"] = 10216,
+ ["lang"] = 10216,
["langle"] = 10216,
- ["rang"] = 10217,
["RightAngleBracket"] = 10217,
+ ["rang"] = 10217,
["rangle"] = 10217,
["Lang"] = 10218,
["Rang"] = 10219,
["loang"] = 10220,
["roang"] = 10221,
- ["xlarr"] = 10229,
- ["longleftarrow"] = 10229,
["LongLeftArrow"] = 10229,
- ["xrarr"] = 10230,
- ["longrightarrow"] = 10230,
+ ["longleftarrow"] = 10229,
+ ["xlarr"] = 10229,
["LongRightArrow"] = 10230,
- ["xharr"] = 10231,
- ["longleftrightarrow"] = 10231,
+ ["longrightarrow"] = 10230,
+ ["xrarr"] = 10230,
["LongLeftRightArrow"] = 10231,
- ["xlArr"] = 10232,
- ["Longleftarrow"] = 10232,
+ ["longleftrightarrow"] = 10231,
+ ["xharr"] = 10231,
["DoubleLongLeftArrow"] = 10232,
- ["xrArr"] = 10233,
- ["Longrightarrow"] = 10233,
+ ["Longleftarrow"] = 10232,
+ ["xlArr"] = 10232,
["DoubleLongRightArrow"] = 10233,
- ["xhArr"] = 10234,
- ["Longleftrightarrow"] = 10234,
+ ["Longrightarrow"] = 10233,
+ ["xrArr"] = 10233,
["DoubleLongLeftRightArrow"] = 10234,
- ["xmap"] = 10236,
+ ["Longleftrightarrow"] = 10234,
+ ["xhArr"] = 10234,
["longmapsto"] = 10236,
+ ["xmap"] = 10236,
["dzigrarr"] = 10239,
["nvlArr"] = 10498,
["nvrArr"] = 10499,
["nvHarr"] = 10500,
["Map"] = 10501,
["lbarr"] = 10508,
- ["rbarr"] = 10509,
["bkarow"] = 10509,
+ ["rbarr"] = 10509,
["lBarr"] = 10510,
- ["rBarr"] = 10511,
["dbkarow"] = 10511,
+ ["rBarr"] = 10511,
["RBarr"] = 10512,
["drbkarow"] = 10512,
["DDotrahd"] = 10513,
@@ -22483,16 +23915,17 @@ local character_entities = {
["rarrbfs"] = 10528,
["nwarhk"] = 10531,
["nearhk"] = 10532,
- ["searhk"] = 10533,
["hksearow"] = 10533,
- ["swarhk"] = 10534,
+ ["searhk"] = 10533,
["hkswarow"] = 10534,
+ ["swarhk"] = 10534,
["nwnear"] = 10535,
["nesear"] = 10536,
["toea"] = 10536,
["seswar"] = 10537,
["tosa"] = 10537,
["swnwar"] = 10538,
+ ["nrarrc"] = {10547, 824},
["rarrc"] = 10547,
["cudarrr"] = 10549,
["ldca"] = 10550,
@@ -22538,10 +23971,10 @@ local character_entities = {
["llhard"] = 10603,
["rharul"] = 10604,
["lrhard"] = 10605,
- ["udhar"] = 10606,
["UpEquilibrium"] = 10606,
- ["duhar"] = 10607,
+ ["udhar"] = 10606,
["ReverseUpEquilibrium"] = 10607,
+ ["duhar"] = 10607,
["RoundImplies"] = 10608,
["erarr"] = 10609,
["simrarr"] = 10610,
@@ -22608,30 +24041,31 @@ local character_entities = {
["trisb"] = 10701,
["rtriltri"] = 10702,
["LeftTriangleBar"] = 10703,
+ ["NotLeftTriangleBar"] = {10703, 824},
+ ["NotRightTriangleBar"] = {10704, 824},
["RightTriangleBar"] = 10704,
- ["race"] = 10714,
["iinfin"] = 10716,
["infintie"] = 10717,
["nvinfin"] = 10718,
["eparsl"] = 10723,
["smeparsl"] = 10724,
["eqvparsl"] = 10725,
- ["lozf"] = 10731,
["blacklozenge"] = 10731,
+ ["lozf"] = 10731,
["RuleDelayed"] = 10740,
["dsol"] = 10742,
- ["xodot"] = 10752,
["bigodot"] = 10752,
- ["xoplus"] = 10753,
+ ["xodot"] = 10752,
["bigoplus"] = 10753,
- ["xotime"] = 10754,
+ ["xoplus"] = 10753,
["bigotimes"] = 10754,
- ["xuplus"] = 10756,
+ ["xotime"] = 10754,
["biguplus"] = 10756,
- ["xsqcup"] = 10758,
+ ["xuplus"] = 10756,
["bigsqcup"] = 10758,
- ["qint"] = 10764,
+ ["xsqcup"] = 10758,
["iiiint"] = 10764,
+ ["qint"] = 10764,
["fpartint"] = 10765,
["cirfnint"] = 10768,
["awint"] = 10769,
@@ -22663,8 +24097,8 @@ local character_entities = {
["triplus"] = 10809,
["triminus"] = 10810,
["tritime"] = 10811,
- ["iprod"] = 10812,
["intprod"] = 10812,
+ ["iprod"] = 10812,
["amalg"] = 10815,
["capdot"] = 10816,
["ncup"] = 10818,
@@ -22694,27 +24128,35 @@ local character_entities = {
["sdote"] = 10854,
["simdot"] = 10858,
["congdot"] = 10861,
+ ["ncongdot"] = {10861, 824},
["easter"] = 10862,
["apacir"] = 10863,
["apE"] = 10864,
+ ["napE"] = {10864, 824},
["eplus"] = 10865,
["pluse"] = 10866,
["Esim"] = 10867,
["Colone"] = 10868,
["Equal"] = 10869,
- ["eDDot"] = 10871,
["ddotseq"] = 10871,
+ ["eDDot"] = 10871,
["equivDD"] = 10872,
["ltcir"] = 10873,
["gtcir"] = 10874,
["ltquest"] = 10875,
["gtquest"] = 10876,
- ["les"] = 10877,
["LessSlantEqual"] = 10877,
+ ["NotLessSlantEqual"] = {10877, 824},
["leqslant"] = 10877,
- ["ges"] = 10878,
+ ["les"] = 10877,
+ ["nleqslant"] = {10877, 824},
+ ["nles"] = {10877, 824},
["GreaterSlantEqual"] = 10878,
+ ["NotGreaterSlantEqual"] = {10878, 824},
["geqslant"] = 10878,
+ ["ges"] = 10878,
+ ["ngeqslant"] = {10878, 824},
+ ["nges"] = {10878, 824},
["lesdot"] = 10879,
["gesdot"] = 10880,
["lesdoto"] = 10881,
@@ -22758,7 +24200,9 @@ local character_entities = {
["simlE"] = 10911,
["simgE"] = 10912,
["LessLess"] = 10913,
+ ["NotNestedLessLess"] = {10913, 824},
["GreaterGreater"] = 10914,
+ ["NotNestedGreaterGreater"] = {10914, 824},
["glj"] = 10916,
["gla"] = 10917,
["ltcc"] = 10918,
@@ -22768,26 +24212,34 @@ local character_entities = {
["smt"] = 10922,
["lat"] = 10923,
["smte"] = 10924,
+ ["smtes"] = {10924, 65024},
["late"] = 10925,
+ ["lates"] = {10925, 65024},
["bumpE"] = 10926,
+ ["NotPrecedesEqual"] = {10927, 824},
+ ["PrecedesEqual"] = 10927,
+ ["npre"] = {10927, 824},
+ ["npreceq"] = {10927, 824},
["pre"] = 10927,
["preceq"] = 10927,
- ["PrecedesEqual"] = 10927,
+ ["NotSucceedsEqual"] = {10928, 824},
+ ["SucceedsEqual"] = 10928,
+ ["nsce"] = {10928, 824},
+ ["nsucceq"] = {10928, 824},
["sce"] = 10928,
["succeq"] = 10928,
- ["SucceedsEqual"] = 10928,
["prE"] = 10931,
["scE"] = 10932,
- ["prnE"] = 10933,
["precneqq"] = 10933,
+ ["prnE"] = 10933,
["scnE"] = 10934,
["succneqq"] = 10934,
["prap"] = 10935,
["precapprox"] = 10935,
["scap"] = 10936,
["succapprox"] = 10936,
- ["prnap"] = 10937,
["precnapprox"] = 10937,
+ ["prnap"] = 10937,
["scnap"] = 10938,
["succnapprox"] = 10938,
["Pr"] = 10939,
@@ -22800,16 +24252,24 @@ local character_entities = {
["supmult"] = 10946,
["subedot"] = 10947,
["supedot"] = 10948,
+ ["nsubE"] = {10949, 824},
+ ["nsubseteqq"] = {10949, 824},
["subE"] = 10949,
["subseteqq"] = 10949,
+ ["nsupE"] = {10950, 824},
+ ["nsupseteqq"] = {10950, 824},
["supE"] = 10950,
["supseteqq"] = 10950,
["subsim"] = 10951,
["supsim"] = 10952,
["subnE"] = 10955,
["subsetneqq"] = 10955,
+ ["varsubsetneqq"] = {10955, 65024},
+ ["vsubnE"] = {10955, 65024},
["supnE"] = 10956,
["supsetneqq"] = 10956,
+ ["varsupsetneqq"] = {10956, 65024},
+ ["vsupnE"] = {10956, 65024},
["csub"] = 10959,
["csup"] = 10960,
["csube"] = 10961,
@@ -22838,6 +24298,7 @@ local character_entities = {
["topcir"] = 10993,
["nhpar"] = 10994,
["parsim"] = 10995,
+ ["nparsl"] = {11005, 8421},
["parsl"] = 11005,
["fflig"] = 64256,
["filig"] = 64257,
@@ -22988,7 +24449,11 @@ local character_entities = {
% \end{markdown}
% \begin{macrocode}
function entities.dec_entity(s)
- return unicode.utf8.char(tonumber(s))
+ local n = tonumber(s)
+ if n == nil then
+ return "&#" .. s .. ";" -- fallback for unknown entities
+ end
+ return unicode.utf8.char(n)
end
% \end{macrocode}
% \par
@@ -23001,7 +24466,28 @@ end
% \end{markdown}
% \begin{macrocode}
function entities.hex_entity(s)
- return unicode.utf8.char(tonumber("0x"..s))
+ local n = tonumber("0x"..s)
+ if n == nil then
+ return "&#x" .. s .. ";" -- fallback for unknown entities
+ end
+ return unicode.utf8.char(n)
+end
+% \end{macrocode}
+% \par
+% \begin{markdown}
+%
+% Given a captured character `x` and a string `s` of hexadecimal digits, the
+% \luamdef{entities.hex_entity_with_x_char} returns the corresponding
+% \acro{utf}8-encoded Unicode codepoint or fallback with the `x` character.
+%
+% \end{markdown}
+% \begin{macrocode}
+function entities.hex_entity_with_x_char(x, s)
+ local n = tonumber("0x"..s)
+ if n == nil then
+ return "&#" .. x .. s .. ";" -- fallback for unknown entities
+ end
+ return unicode.utf8.char(n)
end
% \end{macrocode}
% \par
@@ -23014,11 +24500,18 @@ end
% \end{markdown}
% \begin{macrocode}
function entities.char_entity(s)
- local n = character_entities[s]
- if n == nil then
+ local code_points = character_entities[s]
+ if code_points == nil then
return "&" .. s .. ";"
end
- return unicode.utf8.char(n)
+ if type(code_points) ~= 'table' then
+ code_points = {code_points}
+ end
+ local char_table = {}
+ for _, code_point in ipairs(code_points) do
+ table.insert(char_table, unicode.utf8.char(code_point))
+ end
+ return table.concat(char_table)
end
% \end{macrocode}
% \par
@@ -23073,6 +24566,18 @@ function M.writer.new(options)
% \par
% \begin{markdown}
%
+% Define \luamdef{writer->flatten\_inlines}, which indicates whether or not the
+% writer should produce raw text rather than text in the output format for
+% inline elements. The \luamref{writer->flatten\_inlines} member variable is
+% mutable.
+%
+% \end{markdown}
+% \begin{macrocode}
+ self.flatten_inlines = false
+% \end{macrocode}
+% \par
+% \begin{markdown}
+%
% Parse the \Opt{slice} option and define \luamdef{writer->slice\_begin},
% \luamdef{writer->slice\_end}, and \luamdef{writer->is\_writing}. The
% \luamref{writer->is\_writing} member variable is mutable.
@@ -23192,12 +24697,43 @@ function M.writer.new(options)
% \par
% \begin{markdown}
%
-% Define \luamdef{writer->hard_line_break} as the output format of a forced
+% Define \luamdef{writer->paragraphsep} as the output format of a paragraph
+% separator. Users can use more than one blank line to delimit two blocks to
+% indicate the end of a series of blocks that make up a paragraph. This
+% produces a paragraph separator instead of an interblock separator.
+%
+% \end{markdown}
+% \begin{macrocode}
+ function self.paragraphsep()
+ if not self.is_writing then return "" end
+ return "\\markdownRendererParagraphSeparator\n{}"
+ end
+% \end{macrocode}
+% \par
+% \begin{markdown}
+%
+% Define \luamdef{writer->soft_line_break} as the output format of a soft
% line break.
%
% \end{markdown}
% \begin{macrocode}
- self.hard_line_break = "\\markdownRendererHardLineBreak\n{}"
+ self.soft_line_break = function()
+ if self.flatten_inlines then return "\n" end
+ return "\\markdownRendererSoftLineBreak\n{}"
+ end
+% \end{macrocode}
+% \par
+% \begin{markdown}
+%
+% Define \luamdef{writer->hard_line_break} as the output format of a hard
+% line break.
+%
+% \end{markdown}
+% \begin{macrocode}
+ self.hard_line_break = function()
+ if self.flatten_inlines then return "\n" end
+ return "\\markdownRendererHardLineBreak\n{}"
+ end
% \end{macrocode}
% \par
% \begin{markdown}
@@ -23290,11 +24826,18 @@ function M.writer.new(options)
%
% \end{markdown}
% \begin{macrocode}
- local escape_typographic_text = util.escaper(
+ local function create_escaper(char_escapes, string_escapes)
+ local escape = util.escaper(char_escapes, string_escapes)
+ return function(s)
+ if self.flatten_inlines then return s end
+ return escape(s)
+ end
+ end
+ local escape_typographic_text = create_escaper(
self.escaped_chars, self.escaped_strings)
- local escape_programmatic_text = util.escaper(
+ local escape_programmatic_text = create_escaper(
self.escaped_uri_chars, self.escaped_minimal_strings)
- local escape_minimal = util.escaper(
+ local escape_minimal = create_escaper(
{}, self.escaped_minimal_strings)
% \end{macrocode}
% \par
@@ -23310,6 +24853,7 @@ function M.writer.new(options)
% - \luamdef{writer->math} transforms a math span.
% - \luamdef{writer->identifier} transforms an input programmatic identifier.
% - \luamdef{writer->uri} transforms an input \acro{uri}.
+% - \luamdef{writer->infostring} transforms a fence code infostring.
%
% \end{markdown}
% \begin{macrocode}
@@ -23319,10 +24863,12 @@ function M.writer.new(options)
self.identifier = escape_minimal
self.string = escape_minimal
self.uri = escape_minimal
+ self.infostring = escape_minimal
else
self.identifier = escape_programmatic_text
self.string = escape_typographic_text
self.uri = escape_programmatic_text
+ self.infostring = escape_programmatic_text
end
% \end{macrocode}
% \par
@@ -23335,6 +24881,7 @@ function M.writer.new(options)
% \end{markdown}
% \begin{macrocode}
function self.code(s, attributes)
+ if self.flatten_inlines then return s end
local buf = {}
if attributes ~= nil then
table.insert(buf,
@@ -23361,6 +24908,7 @@ function M.writer.new(options)
% \end{markdown}
% \begin{macrocode}
function self.link(lab, src, tit, attributes)
+ if self.flatten_inlines then return lab end
local buf = {}
if attributes ~= nil then
table.insert(buf,
@@ -23389,6 +24937,7 @@ function M.writer.new(options)
% \end{markdown}
% \begin{macrocode}
function self.image(lab, src, tit, attributes)
+ if self.flatten_inlines then return lab end
local buf = {}
if attributes ~= nil then
table.insert(buf,
@@ -23419,7 +24968,9 @@ function M.writer.new(options)
if not self.is_writing then return "" end
local buffer = {}
for _,item in ipairs(items) do
- buffer[#buffer + 1] = self.bulletitem(item)
+ if item ~= "" then
+ buffer[#buffer + 1] = self.bulletitem(item)
+ end
end
local contents = util.intersperse(buffer,"\n")
if tight and options.tightLists then
@@ -23460,8 +25011,10 @@ function M.writer.new(options)
local buffer = {}
local num = startnum
for _,item in ipairs(items) do
- buffer[#buffer + 1] = self.ordereditem(item,num)
- if num ~= nil then
+ if item ~= "" then
+ buffer[#buffer + 1] = self.ordereditem(item,num)
+ end
+ if num ~= nil and item ~= "" then
num = num + 1
end
end
@@ -23504,27 +25057,13 @@ function M.writer.new(options)
% \end{markdown}
% \begin{macrocode}
function self.inline_html_comment(contents)
+ if self.flatten_inlines then return contents end
return {"\\markdownRendererInlineHtmlComment{",contents,"}"}
end
% \end{macrocode}
% \par
% \begin{markdown}
%
-% Define \luamdef{writer->block_html_comment} as a function that will
-% transform the contents of a block \acro{HTML} comment, to the output
-% format, where `contents` are the contents of the \acro{HTML} comment.
-%
-% \end{markdown}
-% \begin{macrocode}
- function self.block_html_comment(contents)
- if not self.is_writing then return "" end
- return {"\\markdownRendererBlockHtmlCommentBegin\n",contents,
- "\n\\markdownRendererBlockHtmlCommentEnd "}
- end
-% \end{macrocode}
-% \par
-% \begin{markdown}
-%
% Define \luamdef{writer->inline_html_tag} as a function that will
% transform the contents of an opening, closing, or empty inline \acro{HTML}
% tag to the output format, where `contents` are the contents of the
@@ -23533,6 +25072,7 @@ function M.writer.new(options)
% \end{markdown}
% \begin{macrocode}
function self.inline_html_tag(contents)
+ if self.flatten_inlines then return contents end
return {"\\markdownRendererInlineHtmlTag{",self.string(contents),"}"}
end
% \end{macrocode}
@@ -23560,6 +25100,7 @@ function M.writer.new(options)
% \end{markdown}
% \begin{macrocode}
function self.emphasis(s)
+ if self.flatten_inlines then return s end
return {"\\markdownRendererEmphasis{",s,"}"}
end
% \end{macrocode}
@@ -23590,6 +25131,7 @@ function M.writer.new(options)
% \end{markdown}
% \begin{macrocode}
function self.strong(s)
+ if self.flatten_inlines then return s end
return {"\\markdownRendererStrongEmphasis{",s,"}"}
end
% \end{macrocode}
@@ -23602,7 +25144,7 @@ function M.writer.new(options)
% \end{markdown}
% \begin{macrocode}
function self.blockquote(s)
- if #util.rope_to_string(s) == 0 then return "" end
+ if not self.is_writing then return "" end
return {"\\markdownRendererBlockQuoteBegin\n",s,
"\n\\markdownRendererBlockQuoteEnd "}
end
@@ -23649,17 +25191,24 @@ function M.writer.new(options)
%
% \end{markdown}
% \begin{macrocode}
- function self.attributes(attributes)
- local expanded_attributes = {}
- local key_value_regex = "([^= ]+)%s*=%s*(.*)"
+ local seen_identifiers = {}
+ local key_value_regex = "([^= ]+)%s*=%s*(.*)"
+ local function normalize_attributes(attributes, auto_identifiers)
+ -- normalize attributes
+ local normalized_attributes = {}
+ local has_explicit_identifiers = false
local key, value
- for _, attribute in ipairs(attributes) do
- if attribute:sub(1, 1) == "#" or attribute:sub(1, 1) == "." then
- table.insert(expanded_attributes, attribute)
+ for _, attribute in ipairs(attributes or {}) do
+ if attribute:sub(1, 1) == "#" then
+ table.insert(normalized_attributes, attribute)
+ has_explicit_identifiers = true
+ seen_identifiers[attribute:sub(2)] = true
+ elseif attribute:sub(1, 1) == "." then
+ table.insert(normalized_attributes, attribute)
else
key, value = attribute:match(key_value_regex)
if key:lower() == "id" then
- table.insert(expanded_attributes, "#" .. value)
+ table.insert(normalized_attributes, "#" .. value)
elseif key:lower() == "class" then
local classes = {}
for class in value:gmatch("%S+") do
@@ -23667,23 +25216,67 @@ function M.writer.new(options)
end
table.sort(classes)
for _, class in ipairs(classes) do
- table.insert(expanded_attributes, "." .. class)
+ table.insert(normalized_attributes, "." .. class)
end
else
- table.insert(expanded_attributes, attribute)
+ table.insert(normalized_attributes, attribute)
end
end
end
- table.sort(expanded_attributes)
- local buf = {}
- local seen = {}
- for _, attribute in ipairs(expanded_attributes) do
- if seen[attribute] ~= nil then
- goto continue -- prevent duplicate attributes
- else
- seen[attribute] = true
+ -- if no explicit identifiers exist, add auto identifiers
+ if not has_explicit_identifiers and auto_identifiers ~= nil then
+ local seen_auto_identifiers = {}
+ for _, auto_identifier in ipairs(auto_identifiers) do
+ if seen_auto_identifiers[auto_identifier] == nil then
+ seen_auto_identifiers[auto_identifier] = true
+ if seen_identifiers[auto_identifier] == nil then
+ seen_identifiers[auto_identifier] = true
+ table.insert(normalized_attributes,
+ "#" .. auto_identifier)
+ else
+ local auto_identifier_number = 1
+ while true do
+ local numbered_auto_identifier = auto_identifier .. "-"
+ .. auto_identifier_number
+ if seen_identifiers[numbered_auto_identifier] == nil then
+ seen_identifiers[numbered_auto_identifier] = true
+ table.insert(normalized_attributes,
+ "#" .. numbered_auto_identifier)
+ break
+ end
+ auto_identifier_number = auto_identifier_number + 1
+ end
+ end
+ end
+ end
+ end
+
+ -- sort and deduplicate normalized attributes
+ table.sort(normalized_attributes)
+ local seen_normalized_attributes = {}
+ local deduplicated_normalized_attributes = {}
+ for _, attribute in ipairs(normalized_attributes) do
+ if seen_normalized_attributes[attribute] == nil then
+ seen_normalized_attributes[attribute] = true
+ table.insert(deduplicated_normalized_attributes, attribute)
end
+ end
+
+ return deduplicated_normalized_attributes
+ end
+
+ function self.attributes(attributes, should_normalize_attributes)
+ local normalized_attributes
+ if should_normalize_attributes == false then
+ normalized_attributes = attributes
+ else
+ normalized_attributes = normalize_attributes(attributes)
+ end
+
+ local buf = {}
+ local key, value
+ for _, attribute in ipairs(normalized_attributes) do
if attribute:sub(1, 1) == "#" then
table.insert(buf, {"\\markdownRendererAttributeIdentifier{",
attribute:sub(2), "}"})
@@ -23695,7 +25288,6 @@ function M.writer.new(options)
table.insert(buf, {"\\markdownRendererAttributeKeyValue{",
key, "}{", value, "}"})
end
- ::continue::
end
return buf
@@ -23715,6 +25307,19 @@ function M.writer.new(options)
% \par
% \begin{markdown}
%
+% Define \luamdef{writer->attribute\_type\_levels} as a hash table that
+% maps attribute types to the number of attributes of said type in
+% \luamref{writer->active\_attributes}.
+%
+% \end{markdown}
+% \begin{macrocode}
+ self.attribute_type_levels = {}
+ setmetatable(self.attribute_type_levels,
+ { __index = function() return 0 end })
+% \end{macrocode}
+% \par
+% \begin{markdown}
+%
% Define \luamdef{writer->push\_attributes} and
% \luamdef{writer->pop\_attributes} as functions that will add a new set
% of active block-level attributes or remove the most current attributes
@@ -23759,6 +25364,9 @@ function M.writer.new(options)
% \begin{macrocode}
function self.push_attributes(attribute_type, attributes,
start_output, end_output)
+ local attribute_type_level = self.attribute_type_levels[attribute_type]
+ self.attribute_type_levels[attribute_type] = attribute_type_level + 1
+
-- index attributes in a hash table for easy lookup
attributes = attributes or {}
for i = 1, #attributes do
@@ -23776,7 +25384,6 @@ function M.writer.new(options)
end
if attributes["#" .. self.slice_begin_identifier] ~= nil and
self.slice_begin_type == "^" then
- self.is_writing = true
table.insert(buf, apply_attributes())
self.is_writing = true
end
@@ -23812,6 +25419,8 @@ function M.writer.new(options)
local attributes, _, end_output
current_attribute_type, attributes, _, end_output = table.unpack(
self.active_attributes[#self.active_attributes])
+ local attribute_type_level = self.attribute_type_levels[current_attribute_type]
+ self.attribute_type_levels[current_attribute_type] = attribute_type_level - 1
if self.is_writing and end_output ~= nil then
table.insert(buf, end_output)
end
@@ -23833,6 +25442,119 @@ function M.writer.new(options)
return buf
end
% \end{macrocode}
+% \begin{markdown}
+%
+% Create an auto identifier string by stripping and converting characters from string `s`.
+%
+% \end{markdown}
+% \begin{macrocode}
+local function create_auto_identifier(s)
+ local buffer = {}
+ local prev_space = false
+ local letter_found = false
+
+ for _, code in utf8.codes(uni_algos.normalize.NFC(s)) do
+ local char = utf8.char(code)
+
+ -- Remove everything up to the first letter.
+ if not letter_found then
+ local is_letter = unicode.utf8.match(char, "%a")
+ if is_letter then
+ letter_found = true
+ else
+ goto continue
+ end
+ end
+
+ -- Remove all non-alphanumeric characters, except underscores, hyphens, and periods.
+ if not unicode.utf8.match(char, "[%w_%-%.%s]") then
+ goto continue
+ end
+
+ -- Replace all spaces and newlines with hyphens.
+ if unicode.utf8.match(char, "[%s\n]") then
+ char = "-"
+ if prev_space then
+ goto continue
+ else
+ prev_space = true
+ end
+ else
+ -- Convert all alphabetic characters to lowercase.
+ char = unicode.utf8.lower(char)
+ prev_space = false
+ end
+
+ table.insert(buffer, char)
+
+ ::continue::
+ end
+
+ if prev_space then
+ table.remove(buffer)
+ end
+
+ local identifier = #buffer == 0 and "section" or table.concat(buffer, "")
+ return identifier
+end
+% \end{macrocode}
+% \begin{markdown}
+%
+% Create an GitHub-flavored auto identifier string by stripping and converting characters from string `s`.
+%
+% \end{markdown}
+% \begin{macrocode}
+local function create_gfm_auto_identifier(s)
+ local buffer = {}
+ local prev_space = false
+ local letter_found = false
+
+ for _, code in utf8.codes(uni_algos.normalize.NFC(s)) do
+ local char = utf8.char(code)
+
+ -- Remove everything up to the first non-space.
+ if not letter_found then
+ local is_letter = unicode.utf8.match(char, "%S")
+ if is_letter then
+ letter_found = true
+ else
+ goto continue
+ end
+ end
+
+ -- Remove all non-alphanumeric characters, except underscores and hyphens.
+ if not unicode.utf8.match(char, "[%w_%-%s]") then
+ prev_space = false
+ goto continue
+ end
+
+ -- Replace all spaces and newlines with hyphens.
+ if unicode.utf8.match(char, "[%s\n]") then
+ char = "-"
+ if prev_space then
+ goto continue
+ else
+ prev_space = true
+ end
+ else
+ -- Convert all alphabetic characters to lowercase.
+ char = unicode.utf8.lower(char)
+ prev_space = false
+ end
+
+ table.insert(buffer, char)
+
+ ::continue::
+ end
+
+ if prev_space then
+ table.remove(buffer)
+ end
+
+ local identifier = #buffer == 0 and "section" or table.concat(buffer, "")
+ return identifier
+end
+% \end{macrocode}
% \par
% \begin{markdown}
%
@@ -23842,47 +25564,52 @@ function M.writer.new(options)
%
% \end{markdown}
% \begin{macrocode}
- local current_heading_level = 0
function self.heading(s, level, attributes)
local buf = {}
+ local flat_text, inlines = table.unpack(s)
-- push empty attributes for implied sections
- while current_heading_level < level - 1 do
+ while self.attribute_type_levels["heading"] < level - 1 do
table.insert(buf,
self.push_attributes("heading",
nil,
"\\markdownRendererSectionBegin\n",
"\n\\markdownRendererSectionEnd "))
- current_heading_level = current_heading_level + 1
end
-- pop attributes for sections that have ended
- while current_heading_level >= level do
+ while self.attribute_type_levels["heading"] >= level do
table.insert(buf, self.pop_attributes("heading"))
- current_heading_level = current_heading_level - 1
end
+ -- construct attributes for the new section
+ local auto_identifiers = {}
+ if self.options.autoIdentifiers then
+ table.insert(auto_identifiers, create_auto_identifier(flat_text))
+ end
+ if self.options.gfmAutoIdentifiers then
+ table.insert(auto_identifiers, create_gfm_auto_identifier(flat_text))
+ end
+ local normalized_attributes = normalize_attributes(attributes, auto_identifiers)
+
-- push attributes for the new section
local start_output = {}
local end_output = {}
table.insert(start_output, "\\markdownRendererSectionBegin\n")
- if options.headerAttributes and attributes ~= nil and #attributes > 0 then
- table.insert(start_output,
- "\\markdownRendererHeaderAttributeContextBegin\n")
- table.insert(start_output, self.attributes(attributes))
- table.insert(end_output,
- "\n\\markdownRendererHeaderAttributeContextEnd ")
- end
table.insert(end_output, "\n\\markdownRendererSectionEnd ")
table.insert(buf, self.push_attributes("heading",
- attributes,
+ normalized_attributes,
start_output,
end_output))
- current_heading_level = current_heading_level + 1
- assert(current_heading_level == level)
+ assert(self.attribute_type_levels["heading"] == level)
+
+ -- render the heading and its attributes
+ if self.is_writing and #normalized_attributes > 0 then
+ table.insert(buf, "\\markdownRendererHeaderAttributeContextBegin\n")
+ table.insert(buf, self.attributes(normalized_attributes, false))
+ end
- -- produce the renderer
local cmd
level = level + options.shiftHeadings
if level <= 1 then
@@ -23901,7 +25628,11 @@ function M.writer.new(options)
cmd = ""
end
if self.is_writing then
- table.insert(buf, {cmd, "{", s, "}"})
+ table.insert(buf, {cmd, "{", inlines, "}"})
+ end
+
+ if self.is_writing and #normalized_attributes > 0 then
+ table.insert(buf, "\\markdownRendererHeaderAttributeContextEnd ")
end
return buf
@@ -23919,6 +25650,7 @@ function M.writer.new(options)
function self.get_state()
return {
is_writing=self.is_writing,
+ flatten_inlines=self.flatten_inlines,
active_attributes={table.unpack(self.active_attributes)},
}
end
@@ -24013,14 +25745,13 @@ parsers.tilde = P("~")
parsers.backslash = P("\\")
parsers.tab = P("\t")
parsers.newline = P("\n")
-parsers.tightblocksep = P("\001")
parsers.digit = R("09")
parsers.hexdigit = R("09","af","AF")
parsers.letter = R("AZ","az")
parsers.alphanumeric = R("AZ","az","09")
parsers.keyword = parsers.letter
- * parsers.alphanumeric^0
+ * (parsers.alphanumeric + parsers.dash)^0
parsers.internal_punctuation = S(":;,.?")
parsers.doubleasterisks = P("**")
@@ -24042,8 +25773,7 @@ parsers.nonspacechar = parsers.any - parsers.spacing
parsers.optionalspace = parsers.spacechar^0
parsers.normalchar = parsers.any - (V("SpecialChar")
- + parsers.spacing
- + parsers.tightblocksep)
+ + parsers.spacing)
parsers.eof = -parsers.any
parsers.nonindentspace = parsers.space^-3 * - parsers.spacechar
parsers.indent = parsers.space^-3 * parsers.tab
@@ -24064,6 +25794,767 @@ parsers.spnl = parsers.optionalspace
parsers.line = parsers.linechar^0 * parsers.newline
parsers.nonemptyline = parsers.line - parsers.blankline
% \end{macrocode}
+% \par
+% \begin{markdown}
+%
+%#### Parsers Used for Indentation
+%
+% \end{markdown}
+% \begin{macrocode}
+
+parsers.leader = parsers.space^-3
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Check if a trail exists and is non-empty in the indent table `indent_table`.
+%
+% \end{markdown}
+% \begin{macrocode}
+local function has_trail(indent_table)
+ return indent_table ~= nil and
+ indent_table.trail ~= nil and
+ next(indent_table.trail) ~= nil
+end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Check if indent table `indent_table` has any indents.
+%
+% \end{markdown}
+% \begin{macrocode}
+local function has_indents(indent_table)
+ return indent_table ~= nil and
+ indent_table.indents ~= nil and
+ next(indent_table.indents) ~= nil
+end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Add a trail `trail_info` to the indent table `indent_table`.
+%
+% \end{markdown}
+% \begin{macrocode}
+local function add_trail(indent_table, trail_info)
+ indent_table.trail = trail_info
+ return indent_table
+end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Remove a trail `trail_info` from the indent table `indent_table`.
+%
+% \end{markdown}
+% \begin{macrocode}
+local function remove_trail(indent_table)
+ indent_table.trail = nil
+ return indent_table
+end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Update the indent table `indent_table` by adding or removing a new
+% indent `add`.
+%
+% \end{markdown}
+% \begin{macrocode}
+local function update_indent_table(indent_table, new_indent, add)
+ indent_table = remove_trail(indent_table)
+
+ if not has_indents(indent_table) then
+ indent_table.indents = {}
+ end
+
+
+ if add then
+ indent_table.indents[#indent_table.indents + 1] = new_indent
+ else
+ if indent_table.indents[#indent_table.indents].name == new_indent.name then
+ indent_table.indents[#indent_table.indents] = nil
+ end
+ end
+
+ return indent_table
+end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Remove an indent by its name `name`.
+%
+% \end{markdown}
+% \begin{macrocode}
+local function remove_indent(name)
+ local function remove_indent_level(s, i, indent_table) -- luacheck: ignore s i
+ indent_table = update_indent_table(indent_table, {name=name}, false)
+ return true, indent_table
+ end
+
+ return Cg(Cmt(Cb("indent_info"), remove_indent_level), "indent_info")
+end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Process the spacing of a string of spaces and tabs `spacing` with preceding indent width from
+% the start of the line `indent` and strip up to `left_strip_length` spaces. Return the remainder
+% `remainder` and whether there is enough spaces to produce a code `is_code`. Return how many
+% spaces were stripped, as well as if the minimum was met `is_minimum` and what remainder it
+% left `minimum_remainder`.
+%
+% \end{markdown}
+% \begin{macrocode}
+local function process_starter_spacing(indent, spacing, minimum, left_strip_length)
+ left_strip_length = left_strip_length or 0
+
+ local count = 0
+ local tab_value = 4 - (indent) % 4
+
+ local code_started, minimum_found = false, false
+ local code_start, minimum_remainder = "", ""
+
+ local left_total_stripped = 0
+ local full_remainder = ""
+
+ if spacing ~= nil then
+ for i = 1, #spacing do
+ local character = spacing:sub(i, i)
+
+ if character == "\t" then
+ count = count + tab_value
+ tab_value = 4
+ elseif character == " " then
+ count = count + 1
+ tab_value = 4 - (1 - tab_value) % 4
+ end
+
+ if (left_strip_length ~= 0) then
+ local possible_to_strip = math.min(count, left_strip_length)
+ count = count - possible_to_strip
+ left_strip_length = left_strip_length - possible_to_strip
+ left_total_stripped = left_total_stripped + possible_to_strip
+ else
+ full_remainder = full_remainder .. character
+ end
+
+ if (minimum_found) then
+ minimum_remainder = minimum_remainder .. character
+ elseif (count >= minimum) then
+ minimum_found = true
+ minimum_remainder = minimum_remainder .. string.rep(" ", count - minimum)
+ end
+
+ if (code_started) then
+ code_start = code_start .. character
+ elseif (count >= minimum + 4) then
+ code_started = true
+ code_start = code_start .. string.rep(" ", count - (minimum + 4))
+ end
+ end
+ end
+
+ local remainder
+ if (code_started) then
+ remainder = code_start
+ else
+ remainder = string.rep(" ", count - minimum)
+ end
+
+ local is_minimum = count >= minimum
+ return {
+ is_code = code_started,
+ remainder = remainder,
+ left_total_stripped = left_total_stripped,
+ is_minimum = is_minimum,
+ minimum_remainder = minimum_remainder,
+ total_length = count,
+ full_remainder = full_remainder
+ }
+end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Count the total width of all indents in the indent table `indent_table`.
+%
+% \end{markdown}
+% \begin{macrocode}
+local function count_indent_tab_level(indent_table)
+ local count = 0
+ if not has_indents(indent_table) then
+ return count
+ end
+
+ for i=1, #indent_table.indents do
+ count = count + indent_table.indents[i].length
+ end
+ return count
+end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Count the total width of a delimiter `delimiter`.
+%
+% \end{markdown}
+% \begin{macrocode}
+local function total_delimiter_length(delimiter)
+ local count = 0
+ if type(delimiter) == "string" then return #delimiter end
+ for _, value in pairs(delimiter) do
+ count = count + total_delimiter_length(value)
+ end
+ return count
+end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Process the container starter `starter` of a type `indent_type`. Adjust the width of the indent
+% if the delimiter is followed only by whitespaces `is_blank`.
+%
+% \end{markdown}
+% \begin{macrocode}
+local function process_starter_indent(_, _, indent_table, starter, is_blank, indent_type, breakable)
+ local last_trail = starter[1]
+ local delimiter = starter[2]
+ local raw_new_trail = starter[3]
+
+ if indent_type == "bq" and not breakable then
+ indent_table.ignore_blockquote_blank = true
+ end
+
+ if has_trail(indent_table) then
+ local trail = indent_table.trail
+ if trail.is_code then
+ return false
+ end
+ last_trail = trail.remainder
+ else
+ local sp = process_starter_spacing(0, last_trail, 0, 0)
+
+ if sp.is_code then
+ return false
+ end
+ last_trail = sp.remainder
+ end
+
+ local preceding_indentation = count_indent_tab_level(indent_table) % 4
+ local last_trail_length = #last_trail
+ local delimiter_length = total_delimiter_length(delimiter)
+
+ local total_indent_level = preceding_indentation + last_trail_length + delimiter_length
+
+ local sp = {}
+ if not is_blank then
+ sp = process_starter_spacing(total_indent_level, raw_new_trail, 0, 1)
+ end
+
+ local del_trail_length = sp.left_total_stripped
+ if is_blank then
+ del_trail_length = 1
+ elseif not sp.is_code then
+ del_trail_length = del_trail_length + #sp.remainder
+ end
+
+ local indent_length = last_trail_length + delimiter_length + del_trail_length
+ local new_indent_info = {name=indent_type, length=indent_length}
+
+ indent_table = update_indent_table(indent_table, new_indent_info, true)
+ indent_table = add_trail(indent_table, {is_code=sp.is_code, remainder=sp.remainder, total_length=sp.total_length,
+ full_remainder=sp.full_remainder})
+
+ return true, indent_table
+end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Return the pattern corresponding with the indent name `name`.
+%
+% \end{markdown}
+% \begin{macrocode}
+local function decode_pattern(name)
+ local delimeter = parsers.succeed
+ if name == "bq" then
+ delimeter = parsers.more
+ end
+
+ return C(parsers.optionalspace) * C(delimeter) * C(parsers.optionalspace) * Cp()
+end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Find the first blank-only indent of the indent table `indent_table` followed
+% by blank-only indents.
+%
+% \end{markdown}
+% \begin{macrocode}
+local function left_blank_starter(indent_table)
+ local blank_starter_index
+
+ if not has_indents(indent_table) then
+ return
+ end
+
+ for i = #indent_table.indents,1,-1 do
+ local value = indent_table.indents[i]
+ if value.name == "li" then
+ blank_starter_index = i
+ else
+ break
+ end
+ end
+
+ return blank_starter_index
+end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Apply the patterns decoded from the indents of the indent table `indent_table`
+% iteratively starting at position `index` of the string `s`. If the `is_optional`
+% mode is selected, match as many patterns as possible, else match all or fail.
+% With the option `is_blank`, the parsing behaves as optional after the position
+% of a blank-only indent has been surpassed.
+%
+% \end{markdown}
+% \begin{macrocode}
+local function traverse_indent(s, i, indent_table, is_optional, is_blank)
+ local new_index = i
+
+ local preceding_indentation = 0
+ local current_trail = {}
+
+ local blank_starter = left_blank_starter(indent_table)
+
+ for index = 1,#indent_table.indents do
+ local value = indent_table.indents[index]
+ local pattern = decode_pattern(value.name)
+
+ -- match decoded pattern
+ local new_indent_info = lpeg.match(Ct(pattern), s, new_index)
+ if new_indent_info == nil then
+ local blankline_end = lpeg.match(Ct(parsers.blankline * Cg(Cp(), "pos")), s, new_index)
+ if is_optional or not indent_table.ignore_blockquote_blank or not blankline_end then
+ return is_optional, new_index, current_trail
+ end
+
+ return traverse_indent(s, tonumber(blankline_end.pos), indent_table, is_optional, is_blank)
+ end
+
+ local raw_last_trail = new_indent_info[1]
+ local delimiter = new_indent_info[2]
+ local raw_new_trail = new_indent_info[3]
+ local next_index = new_indent_info[4]
+
+ local space_only = delimiter == ""
+
+ -- check previous trail
+ if not space_only and next(current_trail) == nil then
+ local sp = process_starter_spacing(0, raw_last_trail, 0, 0)
+ current_trail = {is_code=sp.is_code, remainder=sp.remainder, total_length=sp.total_length,
+ full_remainder=sp.full_remainder}
+ end
+
+ if next(current_trail) ~= nil then
+ if not space_only and current_trail.is_code then
+ return is_optional, new_index, current_trail
+ end
+ if current_trail.internal_remainder ~= nil then
+ raw_last_trail = current_trail.internal_remainder
+ end
+ end
+
+ local raw_last_trail_length = 0
+ local delimiter_length = 0
+
+ if not space_only then
+ delimiter_length = #delimiter
+ raw_last_trail_length = #raw_last_trail
+ end
+
+ local total_indent_level = preceding_indentation + raw_last_trail_length + delimiter_length
+
+ local spacing_to_process
+ local minimum = 0
+ local left_strip_length = 0
+
+ if not space_only then
+ spacing_to_process = raw_new_trail
+ left_strip_length = 1
+ else
+ spacing_to_process = raw_last_trail
+ minimum = value.length
+ end
+
+ local sp = process_starter_spacing(total_indent_level, spacing_to_process, minimum, left_strip_length)
+
+ if space_only and not sp.is_minimum then
+ return is_optional or (is_blank and blank_starter <= index), new_index, current_trail
+ end
+
+ local indent_length = raw_last_trail_length + delimiter_length + sp.left_total_stripped
+
+ -- update info for the next pattern
+ if not space_only then
+ preceding_indentation = preceding_indentation + indent_length
+ else
+ preceding_indentation = preceding_indentation + value.length
+ end
+
+ current_trail = {is_code=sp.is_code, remainder=sp.remainder, internal_remainder=sp.minimum_remainder,
+ total_length=sp.total_length, full_remainder=sp.full_remainder}
+ new_index = next_index
+ end
+
+ return true, new_index, current_trail
+end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Check if a code trail is expected.
+%
+% \end{markdown}
+% \begin{macrocode}
+local function check_trail(expect_code, is_code)
+ return (expect_code and is_code) or (not expect_code and not is_code)
+end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Check if the current trail of the `indent_table` would produce code if it is expected `expect_code`
+% or it would not if it is not. If there is no trail, process and check the current spacing `spacing`.
+%
+% \end{markdown}
+% \begin{macrocode}
+local function check_trail_joined(s, i, indent_table, spacing, expect_code, omit_remainder) -- luacheck: ignore s i
+ local is_code
+ local remainder
+
+ if has_trail(indent_table) then
+ local trail = indent_table.trail
+ is_code = trail.is_code
+ if is_code then
+ remainder = trail.remainder
+ else
+ remainder = trail.full_remainder
+ end
+ else
+ local sp = process_starter_spacing(0, spacing, 0, 0)
+ is_code = sp.is_code
+ if is_code then
+ remainder = sp.remainder
+ else
+ remainder = sp.full_remainder
+ end
+ end
+
+ local result = check_trail(expect_code, is_code)
+ if omit_remainder then
+ return result
+ end
+ return result, remainder
+end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Check if the current trail of the `indent_table` is of length between `min` and `max`.
+%
+% \end{markdown}
+% \begin{macrocode}
+local function check_trail_length(s, i, indent_table, spacing, min, max) -- luacheck: ignore s i
+ local trail
+
+ if has_trail(indent_table) then
+ trail = indent_table.trail
+ else
+ trail = process_starter_spacing(0, spacing, 0, 0)
+ end
+
+ local total_length = trail.total_length
+ if total_length == nil then
+ return false
+ end
+
+ return min <= total_length and total_length <= max
+end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Check the indentation of the continuation line, optionally with
+% the mode `is_optional` selected. Check blank line exclusively with `is_blank`.
+%
+% \end{markdown}
+% \begin{macrocode}
+local function check_continuation_indentation(s, i, indent_table, is_optional, is_blank)
+ if not has_indents(indent_table) then
+ return true
+ end
+
+ local passes, new_index, current_trail = traverse_indent(s, i, indent_table, is_optional, is_blank)
+
+ if passes then
+ indent_table = add_trail(indent_table, current_trail)
+ return new_index, indent_table
+ end
+ return false
+end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Get name of the last indent from the `indent_table`.
+%
+% \end{markdown}
+% \begin{macrocode}
+local function get_last_indent_name(indent_table)
+ if has_indents(indent_table) then
+ return indent_table.indents[#indent_table.indents].name
+ end
+end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Remove the remainder altogether if the last indent from the `indent_table`
+% is blank-only.
+%
+% \end{markdown}
+% \begin{macrocode}
+local function remove_remainder_if_blank(indent_table, remainder)
+ if get_last_indent_name(indent_table) == "li" then
+ return ""
+ end
+ return remainder
+end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Take the trail `trail` or create a new one from `spacing` and comapre it
+% with the expected `trail_type`. On success return the index `i` and the
+% remainder of the trail.
+%
+% \end{markdown}
+% \begin{macrocode}
+local function check_trail_type(s, i, trail, spacing, trail_type) -- luacheck: ignore s i
+ if trail == nil then
+ trail = process_starter_spacing(0, spacing, 0, 0)
+ end
+
+ if trail_type == "non-code" then
+ return check_trail(false, trail.is_code)
+ end
+ if trail_type == "code" then
+ return check_trail(true, trail.is_code)
+ end
+ if trail_type == "full-code" then
+ if (trail.is_code) then
+ return i, trail.remainder
+ end
+ return i, ""
+ end
+ if trail_type == "full-any" then
+ return i, trail.internal_remainder
+ end
+end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Stores or restores an `is_freezing` trail from indent table `indent_table`.
+%
+% \end{markdown}
+% \begin{macrocode}
+local function trail_freezing(s, i, indent_table, is_freezing) -- luacheck: ignore s i
+ if is_freezing then
+ if indent_table.is_trail_frozen then
+ indent_table.trail = indent_table.frozen_trail
+ else
+ indent_table.frozen_trail = indent_table.trail
+ indent_table.is_trail_frozen = true
+ end
+ else
+ indent_table.frozen_trail = nil
+ indent_table.is_trail_frozen = false
+ end
+ return true, indent_table
+end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Check the indentation of the continuation line, optionally with
+% the mode `is_optional` selected. Check blank line specifically with `is_blank`.
+% Additionally, also directly check the new trail with a type `trail_type`.
+%
+% \end{markdown}
+% \begin{macrocode}
+local function check_continuation_indentation_and_trail(s, i, indent_table, is_optional, is_blank, trail_type,
+ reset_rem, omit_remainder)
+ if not has_indents(indent_table) then
+ local spacing, new_index = lpeg.match(C(parsers.spacechar^0) * Cp(), s, i)
+ local result, remainder = check_trail_type(s, i, indent_table.trail, spacing, trail_type)
+ if remainder == nil then
+ if result then
+ return new_index
+ end
+ return false
+ end
+ if result then
+ return new_index, remainder
+ end
+ return false
+ end
+
+ local passes, new_index, current_trail = traverse_indent(s, i, indent_table, is_optional, is_blank)
+
+ if passes then
+ local spacing
+ if current_trail == nil then
+ local newer_spacing, newer_index = lpeg.match(C(parsers.spacechar^0) * Cp(), s, i)
+ current_trail = process_starter_spacing(0, newer_spacing, 0, 0)
+ new_index = newer_index
+ spacing = newer_spacing
+ else
+ spacing = current_trail.remainder
+ end
+ local result, remainder = check_trail_type(s, new_index, current_trail, spacing, trail_type)
+ if remainder == nil or omit_remainder then
+ if result then
+ return new_index
+ end
+ return false
+ end
+
+ if is_blank and reset_rem then
+ remainder = remove_remainder_if_blank(indent_table, remainder)
+ end
+ if result then
+ return new_index, remainder
+ end
+ return false
+ end
+ return false
+end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% The following patterns check whitespace indentation at the start of a block.
+%
+% \end{markdown}
+% \begin{macrocode}
+parsers.check_trail = Cmt(Cb("indent_info") * C(parsers.spacechar^0) * Cc(false), check_trail_joined)
+
+parsers.check_trail_no_rem = Cmt(Cb("indent_info") * C(parsers.spacechar^0) * Cc(false) * Cc(true), check_trail_joined)
+
+parsers.check_code_trail = Cmt(Cb("indent_info") * C(parsers.spacechar^0) * Cc(true), check_trail_joined)
+
+parsers.check_trail_length_range = function(min, max)
+ return Cmt(Cb("indent_info") * C(parsers.spacechar^0) * Cc(min) * Cc(max), check_trail_length)
+end
+
+parsers.check_trail_length = function(n)
+ return parsers.check_trail_length_range(n, n)
+end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% The following patterns handle trail backup, to prevent a failing pattern to modify it before
+% passing it to the next.
+%
+% \end{markdown}
+% \begin{macrocode}
+parsers.freeze_trail = Cg(Cmt(Cb("indent_info") * Cc(true), trail_freezing), "indent_info")
+
+parsers.unfreeze_trail = Cg(Cmt(Cb("indent_info") * Cc(false), trail_freezing), "indent_info")
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% The following patterns check indentation in continuation lines as defined by the container start.
+%
+% \end{markdown}
+% \begin{macrocode}
+parsers.check_minimal_indent = Cmt(Cb("indent_info") * Cc(false), check_continuation_indentation)
+
+parsers.check_optional_indent = Cmt(Cb("indent_info") * Cc(true), check_continuation_indentation)
+
+parsers.check_minimal_blank_indent = Cmt(Cb("indent_info") * Cc(false) * Cc(true), check_continuation_indentation)
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% The following patterns check indentation in continuation lines as defined by the container start.
+% Additionally the subsequent trail is also directly checked.
+%
+% \end{markdown}
+% \begin{macrocode}
+
+parsers.check_minimal_indent_and_trail = Cmt( Cb("indent_info")
+ * Cc(false) * Cc(false) * Cc("non-code") * Cc(true),
+ check_continuation_indentation_and_trail)
+
+parsers.check_minimal_indent_and_code_trail = Cmt( Cb("indent_info")
+ * Cc(false) * Cc(false) * Cc("code") * Cc(false),
+ check_continuation_indentation_and_trail)
+
+parsers.check_minimal_blank_indent_and_full_code_trail = Cmt( Cb("indent_info")
+ * Cc(false) * Cc(true) * Cc("full-code") * Cc(true),
+ check_continuation_indentation_and_trail)
+
+parsers.check_minimal_indent_and_any_trail = Cmt( Cb("indent_info")
+ * Cc(false) * Cc(false) * Cc("full-any") * Cc(true) * Cc(false),
+ check_continuation_indentation_and_trail)
+
+parsers.check_minimal_blank_indent_and_any_trail = Cmt( Cb("indent_info")
+ * Cc(false) * Cc(true) * Cc("full-any") * Cc(true) * Cc(false),
+ check_continuation_indentation_and_trail)
+
+parsers.check_minimal_blank_indent_and_any_trail_no_rem = Cmt( Cb("indent_info")
+ * Cc(false) * Cc(true) * Cc("full-any") * Cc(true) * Cc(true),
+ check_continuation_indentation_and_trail)
+
+parsers.check_optional_indent_and_any_trail = Cmt( Cb("indent_info")
+ * Cc(true) * Cc(false) * Cc("full-any") * Cc(true) * Cc(false),
+ check_continuation_indentation_and_trail)
+
+parsers.check_optional_blank_indent_and_any_trail = Cmt( Cb("indent_info")
+ * Cc(true) * Cc(true) * Cc("full-any") * Cc(true) * Cc(false),
+ check_continuation_indentation_and_trail)
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% The following patterns specify behaviour around newlines.
+%
+% \end{markdown}
+% \begin{macrocode}
+
+parsers.spnlc_noexc = parsers.optionalspace
+ * (parsers.newline * parsers.check_minimal_indent_and_any_trail)^-1
+
+parsers.spnlc = parsers.optionalspace
+ * (V("EndlineNoSub"))^-1
+
+parsers.spnlc_sep = parsers.optionalspace * V("EndlineNoSub")
+ + parsers.spacechar^1
+
+parsers.only_blank = parsers.spacechar^0 * (parsers.newline + parsers.eof)
+
+% \end{macrocode}
% \begin{figure}
% \hspace*{-0.1\textwidth}
% \begin{minipage}{1.2\textwidth}
@@ -24150,6 +26641,7 @@ parsers.chunk = parsers.line * (parsers.optionallyindentedline
- parsers.blankline)^0
parsers.attribute_key_char = parsers.alphanumeric + S("-_:.")
+parsers.attribute_raw_char = parsers.alphanumeric + S("-_")
parsers.attribute_key = (parsers.attribute_key_char
- parsers.dash - parsers.digit)
* parsers.attribute_key_char^0
@@ -24161,10 +26653,16 @@ parsers.attribute_value = ( (parsers.dquote / "")
* (parsers.squote / ""))
+ ( parsers.anyescaped - parsers.dquote - parsers.rbrace
- parsers.space)^0
+parsers.attribute_identifier = parsers.attribute_key_char^1
+parsers.attribute_classname = parsers.letter
+ * parsers.attribute_key_char^0
+parsers.attribute_raw = parsers.attribute_raw_char^1
parsers.attribute = (parsers.dash * Cc(".unnumbered"))
- + C((parsers.hash + parsers.period)
- * parsers.attribute_key)
+ + C( parsers.hash
+ * parsers.attribute_identifier)
+ + C( parsers.period
+ * parsers.attribute_classname)
+ Cs( parsers.attribute_key
* parsers.optionalspace * parsers.equal * parsers.optionalspace
* parsers.attribute_value)
@@ -24180,7 +26678,7 @@ parsers.attributes = parsers.lbrace
parsers.raw_attribute = parsers.lbrace
* parsers.optionalspace
* parsers.equal
- * C(parsers.attribute_key)
+ * C(parsers.attribute_raw)
* parsers.optionalspace
* parsers.rbrace
@@ -24195,22 +26693,43 @@ end
% \par
% \begin{markdown}
%
-%#### Parsers Used for Markdown Lists
+%#### Parsers Used for HTML Entities
%
% \end{markdown}
% \begin{macrocode}
-parsers.bulletchar = C(parsers.plus + parsers.asterisk + parsers.dash)
+local function repeat_between(pattern, min, max)
+ return -pattern^(max + 1) * pattern^min
+end
-parsers.bullet = ( parsers.bulletchar * #parsers.spacing
- * (parsers.tab + parsers.space^-3)
- + parsers.space * parsers.bulletchar * #parsers.spacing
- * (parsers.tab + parsers.space^-2)
- + parsers.space * parsers.space * parsers.bulletchar
- * #parsers.spacing
- * (parsers.tab + parsers.space^-1)
- + parsers.space * parsers.space * parsers.space
- * parsers.bulletchar * #parsers.spacing
- )
+parsers.hexentity = parsers.ampersand * parsers.hash * C(S("Xx"))
+ * C(repeat_between(parsers.hexdigit, 1, 6)) * parsers.semicolon
+parsers.decentity = parsers.ampersand * parsers.hash
+ * C(repeat_between(parsers.digit, 1, 7)) * parsers.semicolon
+parsers.tagentity = parsers.ampersand * C(parsers.alphanumeric^1)
+ * parsers.semicolon
+
+parsers.html_entities = parsers.hexentity / entities.hex_entity_with_x_char
+ + parsers.decentity / entities.dec_entity
+ + parsers.tagentity / entities.char_entity
+% \end{macrocode}
+% \par
+% \begin{markdown}
+%
+%#### Parsers Used for Markdown Lists
+%
+% \end{markdown}
+% \begin{macrocode}
+parsers.bullet = function(bullet_char, interrupting)
+ local allowed_end
+ if interrupting then
+ allowed_end = C(parsers.spacechar^1) * #parsers.linechar
+ else
+ allowed_end = C(parsers.spacechar^1) + #(parsers.newline + parsers.eof)
+ end
+ return parsers.check_trail
+ * Ct(C(bullet_char) * Cc(""))
+ * allowed_end
+end
local function tickbox(interior)
return parsers.optionalspace * parsers.lbracket
@@ -24235,17 +26754,399 @@ local function captures_equal_length(_,i,a,b)
return #a == #b and i
end
-parsers.closeticks = parsers.space^-1
- * Cmt(C(parsers.backtick^1)
- * Cb("ticks"), captures_equal_length)
+parsers.closeticks = Cmt(C(parsers.backtick^1)
+ * Cb("ticks"), captures_equal_length)
-parsers.intickschar = (parsers.any - S(" \n\r`"))
- + (parsers.newline * -parsers.blankline)
- + (parsers.space - parsers.closeticks)
+parsers.intickschar = (parsers.any - S("\n\r`"))
+ + V("NoSoftLineBreakEndline")
+ (parsers.backtick^1 - parsers.closeticks)
-parsers.inticks = parsers.openticks * parsers.space^-1
- * C(parsers.intickschar^0) * parsers.closeticks
+local function process_inticks(s)
+ s = s:gsub("\n", " ")
+ s = s:gsub("^ (.*) $", "%1")
+ return s
+end
+
+parsers.inticks = parsers.openticks
+ * C(parsers.space^0)
+ * parsers.closeticks
+ + parsers.openticks
+ * Cs(Cs(parsers.intickschar^0) / process_inticks)
+ * parsers.closeticks
+
+% \end{macrocode}
+% \par
+% \begin{markdown}
+%
+%#### Parsers Used for HTML
+%
+% \end{markdown}
+% \begin{macrocode}
+-- case-insensitive match (we assume s is lowercase). must be single byte encoding
+parsers.keyword_exact = function(s)
+ local parser = P(0)
+ for i=1,#s do
+ local c = s:sub(i,i)
+ local m = c .. upper(c)
+ parser = parser * S(m)
+ end
+ return parser
+end
+
+parsers.special_block_keyword =
+ parsers.keyword_exact("pre") +
+ parsers.keyword_exact("script") +
+ parsers.keyword_exact("style") +
+ parsers.keyword_exact("textarea")
+
+parsers.block_keyword =
+ parsers.keyword_exact("address") +
+ parsers.keyword_exact("article") +
+ parsers.keyword_exact("aside") +
+ parsers.keyword_exact("base") +
+ parsers.keyword_exact("basefont") +
+ parsers.keyword_exact("blockquote") +
+ parsers.keyword_exact("body") +
+ parsers.keyword_exact("caption") +
+ parsers.keyword_exact("center") +
+ parsers.keyword_exact("col") +
+ parsers.keyword_exact("colgroup") +
+ parsers.keyword_exact("dd") +
+ parsers.keyword_exact("details") +
+ parsers.keyword_exact("dialog") +
+ parsers.keyword_exact("dir") +
+ parsers.keyword_exact("div") +
+ parsers.keyword_exact("dl") +
+ parsers.keyword_exact("dt") +
+ parsers.keyword_exact("fieldset") +
+ parsers.keyword_exact("figcaption") +
+ parsers.keyword_exact("figure") +
+ parsers.keyword_exact("footer") +
+ parsers.keyword_exact("form") +
+ parsers.keyword_exact("frame") +
+ parsers.keyword_exact("frameset") +
+ parsers.keyword_exact("h1") +
+ parsers.keyword_exact("h2") +
+ parsers.keyword_exact("h3") +
+ parsers.keyword_exact("h4") +
+ parsers.keyword_exact("h5") +
+ parsers.keyword_exact("h6") +
+ parsers.keyword_exact("head") +
+ parsers.keyword_exact("header") +
+ parsers.keyword_exact("hr") +
+ parsers.keyword_exact("html") +
+ parsers.keyword_exact("iframe") +
+ parsers.keyword_exact("legend") +
+ parsers.keyword_exact("li") +
+ parsers.keyword_exact("link") +
+ parsers.keyword_exact("main") +
+ parsers.keyword_exact("menu") +
+ parsers.keyword_exact("menuitem") +
+ parsers.keyword_exact("nav") +
+ parsers.keyword_exact("noframes") +
+ parsers.keyword_exact("ol") +
+ parsers.keyword_exact("optgroup") +
+ parsers.keyword_exact("option") +
+ parsers.keyword_exact("p") +
+ parsers.keyword_exact("param") +
+ parsers.keyword_exact("section") +
+ parsers.keyword_exact("source") +
+ parsers.keyword_exact("summary") +
+ parsers.keyword_exact("table") +
+ parsers.keyword_exact("tbody") +
+ parsers.keyword_exact("td") +
+ parsers.keyword_exact("tfoot") +
+ parsers.keyword_exact("th") +
+ parsers.keyword_exact("thead") +
+ parsers.keyword_exact("title") +
+ parsers.keyword_exact("tr") +
+ parsers.keyword_exact("track") +
+ parsers.keyword_exact("ul")
+
+-- end conditions
+parsers.html_blankline_end_condition = parsers.linechar^0
+ * ( parsers.newline
+ * (parsers.check_minimal_blank_indent_and_any_trail
+ * #parsers.blankline
+ + parsers.check_minimal_indent_and_any_trail)
+ * parsers.linechar^1)^0
+ * (parsers.newline^-1 / "")
+
+local function remove_trailing_blank_lines(s)
+ return s:gsub("[\n\r]+%s*$", "")
+end
+
+parsers.html_until_end = function(end_marker)
+ return Cs(Cs((parsers.newline
+ * (parsers.check_minimal_blank_indent_and_any_trail
+ * #parsers.blankline
+ + parsers.check_minimal_indent_and_any_trail)
+ + parsers.linechar - end_marker)^0
+ * parsers.linechar^0 * parsers.newline^-1)
+ / remove_trailing_blank_lines)
+end
+
+-- attributes
+parsers.html_attribute_spacing = parsers.optionalspace
+ * V("NoSoftLineBreakEndline")
+ * parsers.optionalspace
+ + parsers.spacechar^1
+
+parsers.html_attribute_name = (parsers.letter + parsers.colon + parsers.underscore)
+ * (parsers.alphanumeric + parsers.colon + parsers.underscore
+ + parsers.period + parsers.dash)^0
+
+parsers.html_attribute_value = parsers.squote
+ * (parsers.linechar - parsers.squote)^0
+ * parsers.squote
+ + parsers.dquote
+ * (parsers.linechar - parsers.dquote)^0
+ * parsers.dquote
+ + ( parsers.any - parsers.spacechar - parsers.newline
+ - parsers.dquote - parsers.squote - parsers.backtick
+ - parsers.equal - parsers.less - parsers.more)^1
+
+parsers.html_inline_attribute_value = parsers.squote
+ * (V("NoSoftLineBreakEndline")
+ + parsers.any
+ - parsers.blankline^2
+ - parsers.squote)^0
+ * parsers.squote
+ + parsers.dquote
+ * (V("NoSoftLineBreakEndline")
+ + parsers.any
+ - parsers.blankline^2
+ - parsers.dquote)^0
+ * parsers.dquote
+ + (parsers.any - parsers.spacechar - parsers.newline
+ - parsers.dquote - parsers.squote - parsers.backtick
+ - parsers.equal - parsers.less - parsers.more)^1
+
+parsers.html_attribute_value_specification = parsers.optionalspace
+ * parsers.equal
+ * parsers.optionalspace
+ * parsers.html_attribute_value
+
+parsers.html_spnl = parsers.optionalspace
+ * (V("NoSoftLineBreakEndline") * parsers.optionalspace)^-1
+
+parsers.html_inline_attribute_value_specification = parsers.html_spnl
+ * parsers.equal
+ * parsers.html_spnl
+ * parsers.html_inline_attribute_value
+
+parsers.html_attribute = parsers.html_attribute_spacing
+ * parsers.html_attribute_name
+ * parsers.html_inline_attribute_value_specification^-1
+
+parsers.html_non_newline_attribute = parsers.spacechar^1
+ * parsers.html_attribute_name
+ * parsers.html_attribute_value_specification^-1
+
+parsers.nested_breaking_blank = parsers.newline
+ * parsers.check_minimal_blank_indent
+ * parsers.blankline
+
+parsers.html_comment_start = P("<!--")
+
+parsers.html_comment_end = P("-->")
+
+parsers.html_comment = Cs( parsers.html_comment_start
+ * parsers.html_until_end(parsers.html_comment_end))
+
+parsers.html_inline_comment = (parsers.html_comment_start / "")
+ * -P(">") * -P("->")
+ * Cs((V("NoSoftLineBreakEndline") + parsers.any - P("--")
+ - parsers.nested_breaking_blank - parsers.html_comment_end)^0)
+ * (parsers.html_comment_end / "")
+
+parsers.html_cdatasection_start = P("<![CDATA[")
+
+parsers.html_cdatasection_end = P("]]>")
+
+parsers.html_cdatasection = Cs( parsers.html_cdatasection_start
+ * parsers.html_until_end(parsers.html_cdatasection_end))
+
+parsers.html_inline_cdatasection = parsers.html_cdatasection_start
+ * Cs(V("NoSoftLineBreakEndline") + parsers.any
+ - parsers.nested_breaking_blank - parsers.html_cdatasection_end)^0
+ * parsers.html_cdatasection_end
+
+parsers.html_declaration_start = P("<!") * parsers.letter
+
+parsers.html_declaration_end = P(">")
+
+parsers.html_declaration = Cs( parsers.html_declaration_start
+ * parsers.html_until_end(parsers.html_declaration_end))
+
+parsers.html_inline_declaration = parsers.html_declaration_start
+ * Cs(V("NoSoftLineBreakEndline") + parsers.any
+ - parsers.nested_breaking_blank - parsers.html_declaration_end)^0
+ * parsers.html_declaration_end
+
+parsers.html_instruction_start = P("<?")
+
+parsers.html_instruction_end = P("?>")
+
+parsers.html_instruction = Cs( parsers.html_instruction_start
+ * parsers.html_until_end(parsers.html_instruction_end))
+
+parsers.html_inline_instruction = parsers.html_instruction_start
+ * Cs(V("NoSoftLineBreakEndline") + parsers.any
+ - parsers.nested_breaking_blank - parsers.html_instruction_end)^0
+ * parsers.html_instruction_end
+
+parsers.html_blankline = parsers.newline
+ * parsers.optionalspace
+ * parsers.newline
+
+parsers.html_tag_start = parsers.less
+
+parsers.html_tag_closing_start = parsers.less
+ * parsers.slash
+
+parsers.html_tag_end = parsers.html_spnl
+ * parsers.more
+
+parsers.html_empty_tag_end = parsers.html_spnl
+ * parsers.slash
+ * parsers.more
+
+-- opening tags
+parsers.html_any_open_inline_tag = parsers.html_tag_start
+ * parsers.keyword
+ * parsers.html_attribute^0
+ * parsers.html_tag_end
+
+parsers.html_any_open_tag = parsers.html_tag_start
+ * parsers.keyword
+ * parsers.html_non_newline_attribute^0
+ * parsers.html_tag_end
+
+parsers.html_open_tag = parsers.html_tag_start
+ * parsers.block_keyword
+ * parsers.html_attribute^0
+ * parsers.html_tag_end
+
+parsers.html_open_special_tag = parsers.html_tag_start
+ * parsers.special_block_keyword
+ * parsers.html_attribute^0
+ * parsers.html_tag_end
+
+-- incomplete tags
+parsers.incomplete_tag_following = parsers.spacechar
+ + parsers.more
+ + parsers.slash * parsers.more
+ + #(parsers.newline + parsers.eof)
+
+parsers.incomplete_special_tag_following = parsers.spacechar
+ + parsers.more
+ + #(parsers.newline + parsers.eof)
+
+parsers.html_incomplete_open_tag = parsers.html_tag_start
+ * parsers.block_keyword
+ * parsers.incomplete_tag_following
+
+parsers.html_incomplete_open_special_tag = parsers.html_tag_start
+ * parsers.special_block_keyword
+ * parsers.incomplete_special_tag_following
+
+parsers.html_incomplete_close_tag = parsers.html_tag_closing_start
+ * parsers.block_keyword
+ * parsers.incomplete_tag_following
+
+parsers.html_incomplete_close_special_tag = parsers.html_tag_closing_start
+ * parsers.special_block_keyword
+ * parsers.incomplete_tag_following
+
+-- closing tags
+parsers.html_close_tag = parsers.html_tag_closing_start
+ * parsers.block_keyword
+ * parsers.html_tag_end
+
+parsers.html_any_close_tag = parsers.html_tag_closing_start
+ * parsers.keyword
+ * parsers.html_tag_end
+
+parsers.html_close_special_tag = parsers.html_tag_closing_start
+ * parsers.special_block_keyword
+ * parsers.html_tag_end
+
+-- empty tags
+parsers.html_any_empty_inline_tag = parsers.html_tag_start
+ * parsers.keyword
+ * parsers.html_attribute^0
+ * parsers.html_empty_tag_end
+
+parsers.html_any_empty_tag = parsers.html_tag_start
+ * parsers.keyword
+ * parsers.html_non_newline_attribute^0
+ * parsers.optionalspace
+ * parsers.slash
+ * parsers.more
+
+parsers.html_empty_tag = parsers.html_tag_start
+ * parsers.block_keyword
+ * parsers.html_attribute^0
+ * parsers.html_empty_tag_end
+
+parsers.html_empty_special_tag = parsers.html_tag_start
+ * parsers.special_block_keyword
+ * parsers.html_attribute^0
+ * parsers.html_empty_tag_end
+
+parsers.html_incomplete_blocks = parsers.html_incomplete_open_tag
+ + parsers.html_incomplete_open_special_tag
+ + parsers.html_incomplete_close_tag
+
+-- parse special html blocks
+parsers.html_blankline_ending_special_block_opening = (parsers.html_close_special_tag
+ + parsers.html_empty_special_tag)
+ * #(parsers.optionalspace
+ * (parsers.newline + parsers.eof))
+
+parsers.html_blankline_ending_special_block = parsers.html_blankline_ending_special_block_opening
+ * parsers.html_blankline_end_condition
+
+parsers.html_special_block_opening = parsers.html_incomplete_open_special_tag
+ - parsers.html_empty_special_tag
+
+parsers.html_closing_special_block = parsers.html_special_block_opening
+ * parsers.html_until_end(parsers.html_close_special_tag)
+
+parsers.html_special_block = parsers.html_blankline_ending_special_block
+ + parsers.html_closing_special_block
+
+-- parse html blocks
+parsers.html_block_opening = parsers.html_incomplete_open_tag
+ + parsers.html_incomplete_close_tag
+
+parsers.html_block = parsers.html_block_opening
+ * parsers.html_blankline_end_condition
+
+-- parse any html blocks
+parsers.html_any_block_opening = (parsers.html_any_open_tag
+ + parsers.html_any_close_tag
+ + parsers.html_any_empty_tag)
+ * #(parsers.optionalspace * (parsers.newline + parsers.eof))
+
+parsers.html_any_block = parsers.html_any_block_opening
+ * parsers.html_blankline_end_condition
+
+parsers.html_inline_comment_full = parsers.html_comment_start
+ * -P(">") * -P("->")
+ * Cs((V("NoSoftLineBreakEndline") + parsers.any - P("--")
+ - parsers.nested_breaking_blank - parsers.html_comment_end)^0)
+ * parsers.html_comment_end
+
+parsers.html_inline_tags = parsers.html_inline_comment_full
+ + parsers.html_any_empty_inline_tag
+ + parsers.html_inline_instruction
+ + parsers.html_inline_cdatasection
+ + parsers.html_inline_declaration
+ + parsers.html_any_open_inline_tag
+ + parsers.html_any_close_tag
+
% \end{macrocode}
% \par
% \begin{markdown}
@@ -24254,7 +27155,54 @@ parsers.inticks = parsers.openticks * parsers.space^-1
%
% \end{markdown}
% \begin{macrocode}
-parsers.leader = parsers.space^-3
+parsers.urlchar = parsers.anyescaped
+ - parsers.newline
+ - parsers.more
+
+parsers.auto_link_scheme_part = parsers.alphanumeric
+ + parsers.plus
+ + parsers.period
+ + parsers.dash
+
+parsers.auto_link_scheme = parsers.letter
+ * parsers.auto_link_scheme_part
+ * parsers.auto_link_scheme_part^-30
+
+parsers.absolute_uri = parsers.auto_link_scheme * parsers.colon
+ * (parsers.any - parsers.spacing - parsers.less - parsers.more)^0
+
+parsers.printable_characters = S(".!#$%&'*+/=?^_`{|}~-")
+
+parsers.email_address_local_part_char = parsers.alphanumeric
+ + parsers.printable_characters
+
+parsers.email_address_local_part = parsers.email_address_local_part_char^1
+
+parsers.email_address_dns_label = parsers.alphanumeric
+ * (parsers.alphanumeric + parsers.dash)^-62
+ * B(parsers.alphanumeric)
+
+parsers.email_address_domain = parsers.email_address_dns_label
+ * (parsers.period * parsers.email_address_dns_label)^0
+
+parsers.email_address = parsers.email_address_local_part
+ * parsers.at
+ * parsers.email_address_domain
+
+parsers.auto_link_url = parsers.less
+ * C(parsers.absolute_uri)
+ * parsers.more
+
+parsers.auto_link_email = parsers.less
+ * C(parsers.email_address)
+ * parsers.more
+
+parsers.auto_link_relative_reference = parsers.less
+ * C(parsers.urlchar^1)
+ * parsers.more
+
+parsers.autolink = parsers.auto_link_url
+ + parsers.auto_link_email
-- content in balanced brackets, parentheses, or quotes:
parsers.bracketed = P{ parsers.lbracket
@@ -24284,183 +27232,96 @@ parsers.dquoted = P{ parsers.dquote * parsers.alphanumeric
) + V(1))^0
* parsers.dquote }
--- bracketed tag for markdown links, allowing nested brackets:
-parsers.tag = parsers.lbracket
+parsers.link_text = parsers.lbracket
* Cs((parsers.alphanumeric^1
- + parsers.bracketed
- + parsers.inticks
- + ( parsers.backslash / "" * parsers.rbracket
- + parsers.any
- - (parsers.rbracket + parsers.blankline^2)))^0)
+ + parsers.bracketed
+ + parsers.inticks
+ + parsers.autolink
+ + V("InlineHtml")
+ + ( parsers.backslash * parsers.backslash)
+ + ( parsers.backslash * (parsers.lbracket + parsers.rbracket)
+ + V("NoSoftLineBreakSpace")
+ + V("NoSoftLineBreakEndline")
+ + (parsers.any
+ - (parsers.newline + parsers.lbracket + parsers.rbracket + parsers.blankline^2))))^0)
* parsers.rbracket
+parsers.link_label = parsers.lbracket
+ * -#(parsers.sp * parsers.rbracket)
+ * #((parsers.any - parsers.rbracket)^-999 * parsers.rbracket)
+ * Cs((parsers.alphanumeric^1
+ + parsers.inticks
+ + parsers.autolink
+ + V("InlineHtml")
+ + ( parsers.backslash * parsers.backslash)
+ + ( parsers.backslash * (parsers.lbracket + parsers.rbracket)
+ + V("NoSoftLineBreakSpace")
+ + V("NoSoftLineBreakEndline")
+ + (parsers.any
+ - (parsers.newline + parsers.lbracket + parsers.rbracket + parsers.blankline^2))))^1)
+ * parsers.rbracket
+
+parsers.inparens_url = P{ parsers.lparent
+ * ((parsers.anyescaped - (parsers.lparent
+ + parsers.rparent
+ + parsers.spacing)
+ ) + V(1))^0
+ * parsers.rparent }
+
-- url for markdown links, allowing nested brackets:
parsers.url = parsers.less * Cs((parsers.anyescaped
+ - parsers.newline
+ - parsers.less
- parsers.more)^0)
* parsers.more
- + Cs((parsers.inparens + (parsers.anyescaped
- - parsers.spacing
- - parsers.rparent))^1)
-
--- quoted text, possibly with nested quotes:
-parsers.title_s = parsers.squote * Cs(((parsers.anyescaped-parsers.squote)
- + parsers.squoted)^0)
- * parsers.squote
-
-parsers.title_d = parsers.dquote * Cs(((parsers.anyescaped-parsers.dquote)
- + parsers.dquoted)^0)
- * parsers.dquote
+ + -parsers.less
+ * Cs((parsers.inparens_url + (parsers.anyescaped
+ - parsers.spacing
+ - parsers.lparent
+ - parsers.rparent))^1)
+
+-- quoted text:
+parsers.title_s = parsers.squote
+ * Cs((parsers.html_entities
+ + V("NoSoftLineBreakSpace")
+ + V("NoSoftLineBreakEndline")
+ + (parsers.anyescaped - parsers.newline - parsers.squote - parsers.blankline^2))^0)
+ * parsers.squote
+
+parsers.title_d = parsers.dquote
+ * Cs((parsers.html_entities
+ + V("NoSoftLineBreakSpace")
+ + V("NoSoftLineBreakEndline")
+ + (parsers.anyescaped - parsers.newline - parsers.dquote - parsers.blankline^2))^0)
+ * parsers.dquote
parsers.title_p = parsers.lparent
- * Cs((parsers.inparens + (parsers.anyescaped-parsers.rparent))^0)
+ * Cs((parsers.html_entities
+ + V("NoSoftLineBreakSpace")
+ + V("NoSoftLineBreakEndline")
+ + (parsers.anyescaped - parsers.newline - parsers.lparent - parsers.rparent
+ - parsers.blankline^2))^0)
* parsers.rparent
parsers.title = parsers.title_d + parsers.title_s + parsers.title_p
parsers.optionaltitle
- = parsers.spnl * parsers.title * parsers.spacechar^0
+ = parsers.spnlc * parsers.title * parsers.spacechar^0
+ Cc("")
-parsers.indirect_link
- = parsers.tag
- * ( C(parsers.spnl) * parsers.tag
- + Cc(nil) * Cc(nil) -- always produce exactly two captures
- )
-
-parsers.indirect_image
- = parsers.exclamation * parsers.indirect_link
% \end{macrocode}
% \par
% \begin{markdown}
%
-%#### Parsers Used for HTML
-%
-% \end{markdown}
-% \begin{macrocode}
--- case-insensitive match (we assume s is lowercase). must be single byte encoding
-parsers.keyword_exact = function(s)
- local parser = P(0)
- for i=1,#s do
- local c = s:sub(i,i)
- local m = c .. upper(c)
- parser = parser * S(m)
- end
- return parser
-end
-
-parsers.block_keyword =
- parsers.keyword_exact("address") + parsers.keyword_exact("blockquote") +
- parsers.keyword_exact("center") + parsers.keyword_exact("del") +
- parsers.keyword_exact("dir") + parsers.keyword_exact("div") +
- parsers.keyword_exact("p") + parsers.keyword_exact("pre") +
- parsers.keyword_exact("li") + parsers.keyword_exact("ol") +
- parsers.keyword_exact("ul") + parsers.keyword_exact("dl") +
- parsers.keyword_exact("dd") + parsers.keyword_exact("form") +
- parsers.keyword_exact("fieldset") + parsers.keyword_exact("isindex") +
- parsers.keyword_exact("ins") + parsers.keyword_exact("menu") +
- parsers.keyword_exact("noframes") + parsers.keyword_exact("frameset") +
- parsers.keyword_exact("h1") + parsers.keyword_exact("h2") +
- parsers.keyword_exact("h3") + parsers.keyword_exact("h4") +
- parsers.keyword_exact("h5") + parsers.keyword_exact("h6") +
- parsers.keyword_exact("hr") + parsers.keyword_exact("script") +
- parsers.keyword_exact("noscript") + parsers.keyword_exact("table") +
- parsers.keyword_exact("tbody") + parsers.keyword_exact("tfoot") +
- parsers.keyword_exact("thead") + parsers.keyword_exact("th") +
- parsers.keyword_exact("td") + parsers.keyword_exact("tr")
-
--- There is no reason to support bad html, so we expect quoted attributes
-parsers.htmlattributevalue
- = parsers.squote * (parsers.any - (parsers.blankline
- + parsers.squote))^0
- * parsers.squote
- + parsers.dquote * (parsers.any - (parsers.blankline
- + parsers.dquote))^0
- * parsers.dquote
-
-parsers.htmlattribute = parsers.spacing^1
- * (parsers.alphanumeric + S("_-"))^1
- * parsers.sp * parsers.equal * parsers.sp
- * parsers.htmlattributevalue
-
-parsers.htmlcomment = P("<!--")
- * parsers.optionalspace
- * Cs((parsers.any - parsers.optionalspace * P("-->"))^0)
- * parsers.optionalspace
- * P("-->")
-
-parsers.htmlinstruction = P("<?") * (parsers.any - P("?>"))^0 * P("?>")
-
-parsers.openelt_any = parsers.less * parsers.keyword * parsers.htmlattribute^0
- * parsers.sp * parsers.more
-
-parsers.openelt_exact = function(s)
- return parsers.less * parsers.sp * parsers.keyword_exact(s)
- * parsers.htmlattribute^0 * parsers.sp * parsers.more
-end
-
-parsers.openelt_block = parsers.sp * parsers.block_keyword
- * parsers.htmlattribute^0 * parsers.sp * parsers.more
-
-parsers.closeelt_any = parsers.less * parsers.sp * parsers.slash
- * parsers.keyword * parsers.sp * parsers.more
-
-parsers.closeelt_exact = function(s)
- return parsers.less * parsers.sp * parsers.slash * parsers.keyword_exact(s)
- * parsers.sp * parsers.more
-end
-
-parsers.emptyelt_any = parsers.less * parsers.sp * parsers.keyword
- * parsers.htmlattribute^0 * parsers.sp * parsers.slash
- * parsers.more
-
-parsers.emptyelt_block = parsers.less * parsers.sp * parsers.block_keyword
- * parsers.htmlattribute^0 * parsers.sp * parsers.slash
- * parsers.more
-
-parsers.displaytext = (parsers.any - parsers.less)^1
-
--- return content between two matched HTML tags
-parsers.in_matched = function(s)
- return { parsers.openelt_exact(s)
- * (V(1) + parsers.displaytext
- + (parsers.less - parsers.closeelt_exact(s)))^0
- * parsers.closeelt_exact(s) }
-end
-
-local function parse_matched_tags(s,pos)
- local t = string.lower(lpeg.match(C(parsers.keyword),s,pos))
- return lpeg.match(parsers.in_matched(t),s,pos-1)
-end
-
-parsers.in_matched_block_tags = parsers.less
- * Cmt(#parsers.openelt_block, parse_matched_tags)
-
-% \end{macrocode}
-% \par
-% \begin{markdown}
-%
-%#### Parsers Used for HTML Entities
-%
-% \end{markdown}
-% \begin{macrocode}
-parsers.hexentity = parsers.ampersand * parsers.hash * S("Xx")
- * C(parsers.hexdigit^1) * parsers.semicolon
-parsers.decentity = parsers.ampersand * parsers.hash
- * C(parsers.digit^1) * parsers.semicolon
-parsers.tagentity = parsers.ampersand * C(parsers.alphanumeric^1)
- * parsers.semicolon
-% \end{macrocode}
-% \par
-% \begin{markdown}
-%
-%#### Helpers for Link Reference Definitions
+%#### Helpers for Links and Link Reference Definitions
%
% \end{markdown}
% \begin{macrocode}
-- parse a reference definition: [foo]: /bar "title"
-parsers.define_reference_parser = parsers.leader * parsers.tag * parsers.colon
- * parsers.spacechar^0 * parsers.url
- * parsers.optionaltitle
+parsers.define_reference_parser = (parsers.check_trail / "") * parsers.link_label * parsers.colon
+ * parsers.spnlc * parsers.url
+ * ( parsers.spnlc_sep * parsers.title * parsers.only_blank
+ + Cc("") * parsers.only_blank)
% \end{macrocode}
% \par
% \begin{markdown}
@@ -24470,7 +27331,6 @@ parsers.define_reference_parser = parsers.leader * parsers.tag * parsers.colon
% \end{markdown}
% \begin{macrocode}
parsers.Inline = V("Inline")
-parsers.IndentedInline = V("IndentedInline")
-- parse many p between starter and ender
parsers.between = function(p, starter, ender)
@@ -24478,26 +27338,6 @@ parsers.between = function(p, starter, ender)
return (starter * #parsers.nonspacechar * Ct(p * (p - ender2)^0) * ender2)
end
-parsers.urlchar = parsers.anyescaped
- - parsers.newline
- - parsers.more
-
-parsers.auto_link_url = parsers.less
- * C( parsers.alphanumeric^1 * P("://")
- * parsers.urlchar^1)
- * parsers.more
-
-parsers.auto_link_email
- = parsers.less
- * C((parsers.alphanumeric + S("-._+"))^1
- * P("@") * parsers.urlchar^1)
- * parsers.more
-
-parsers.auto_link_relative_reference
- = parsers.less
- * C(parsers.urlchar^1)
- * parsers.more
-
% \end{macrocode}
% \par
% \begin{markdown}
@@ -24507,10 +27347,13 @@ parsers.auto_link_relative_reference
% \end{markdown}
% \begin{macrocode}
parsers.lineof = function(c)
- return (parsers.leader * (P(c) * parsers.optionalspace)^3
- * (parsers.newline * parsers.blankline^1
- + parsers.newline^-1 * parsers.eof))
+ return (parsers.check_trail_no_rem * (P(c) * parsers.optionalspace)^3
+ * (parsers.newline + parsers.eof))
end
+
+parsers.thematic_break_lines = parsers.lineof(parsers.asterisk)
+ + parsers.lineof(parsers.dash)
+ + parsers.lineof(parsers.underscore)
% \end{macrocode}
% \par
% \begin{markdown}
@@ -24524,11 +27367,21 @@ parsers.heading_start = #parsers.hash * C(parsers.hash^-6)
* -parsers.hash / length
-- parse setext header ending and return level
-parsers.heading_level = parsers.equal^1 * Cc(1) + parsers.dash^1 * Cc(2)
+parsers.heading_level = parsers.nonindentspace * parsers.equal^1 * parsers.optionalspace * #parsers.newline * Cc(1)
+ + parsers.nonindentspace * parsers.dash^1 * parsers.optionalspace * #parsers.newline * Cc(2)
local function strip_atx_end(s)
- return s:gsub("[#%s]*\n$","")
+ return s:gsub("%s+#*%s*\n$","")
end
+
+parsers.atx_heading = parsers.check_trail_no_rem
+ * Cg(parsers.heading_start, "level")
+ * (C( parsers.optionalspace
+ * parsers.hash^0
+ * parsers.optionalspace
+ * parsers.newline)
+ + parsers.spacechar^1
+ * C(parsers.line))
% \end{macrocode}
% \par
% \begin{markdown}
@@ -24610,7 +27463,7 @@ function M.reader.new(writer, options)
tag = util.rope_to_string(tag)
tag = tag:gsub("[ \n\r\t]+", " ")
tag = tag:gsub("^ ", ""):gsub(" $", "")
- tag = uni_case.casefold(tag, true, false)
+ tag = uni_algos.case.casefold(tag, true, false)
return tag
end
% \end{macrocode}
@@ -24733,11 +27586,6 @@ function M.reader.new(writer, options)
return parsers.inlines
end, false)
- self.create_parser("parse_inlines_no_link",
- function()
- return parsers.inlines_no_link
- end, false)
-
self.create_parser("parse_inlines_no_inline_note",
function()
return parsers.inlines_no_inline_note
@@ -24752,6 +27600,134 @@ function M.reader.new(writer, options)
function()
return parsers.inlines_nbsp
end, false)
+ self.create_parser("parse_inlines_no_link_or_emphasis",
+ function()
+ return parsers.inlines_no_link_or_emphasis
+ end, false)
+% \end{macrocode}
+% \par
+% \begin{markdown}
+%
+%#### Parsers Used for Indentation (local)
+%
+% The following patterns represent basic building blocks of indented content.
+%
+% \end{markdown}
+% \begin{macrocode}
+ parsers.minimally_indented_blankline = parsers.check_minimal_indent * (parsers.blankline / "")
+
+ parsers.minimally_indented_block = parsers.check_minimal_indent * V("Block")
+
+ parsers.minimally_indented_block_or_paragraph = parsers.check_minimal_indent * V("BlockOrParagraph")
+
+ parsers.minimally_indented_paragraph = parsers.check_minimal_indent * V("Paragraph")
+
+ parsers.minimally_indented_plain = parsers.check_minimal_indent * V("Plain")
+
+ parsers.minimally_indented_par_or_plain = parsers.minimally_indented_paragraph
+ + parsers.minimally_indented_plain
+
+ parsers.minimally_indented_par_or_plain_no_blank = parsers.minimally_indented_par_or_plain
+ - parsers.minimally_indented_blankline
+
+ parsers.minimally_indented_ref = parsers.check_minimal_indent * V("Reference")
+
+ parsers.minimally_indented_blank = parsers.check_minimal_indent * V("Blank")
+
+ parsers.conditionally_indented_blankline = parsers.check_minimal_blank_indent * (parsers.blankline / "")
+
+ parsers.minimally_indented_ref_or_block = parsers.minimally_indented_ref
+ + parsers.minimally_indented_block
+ - parsers.minimally_indented_blankline
+
+ parsers.minimally_indented_ref_or_block_or_par = parsers.minimally_indented_ref
+ + parsers.minimally_indented_block_or_paragraph
+ - parsers.minimally_indented_blankline
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% The following pattern parses the properly indented content that follows the initial container start.
+%
+% \end{markdown}
+% \begin{macrocode}
+
+ parsers.separator_loop = function(separated_block, paragraph, block_separator, paragraph_separator)
+ return separated_block
+ + block_separator
+ * paragraph
+ * separated_block
+ + paragraph_separator
+ * paragraph
+ end
+
+ parsers.create_loop_body_pair = function(separated_block, paragraph, block_separator, paragraph_separator)
+ return {
+ block = parsers.separator_loop(separated_block, paragraph, block_separator, block_separator),
+ par = parsers.separator_loop(separated_block, paragraph, block_separator, paragraph_separator)
+ }
+ end
+
+ parsers.block_sep_group = function(blank)
+ return blank^0 * parsers.eof
+ + ( blank^2 / writer.paragraphsep
+ + blank^0 / writer.interblocksep
+ )
+ end
+
+ parsers.par_sep_group = function(blank)
+ return blank^0 * parsers.eof
+ + blank^0 / writer.paragraphsep
+ end
+
+ parsers.sep_group_no_output = function(blank)
+ return blank^0 * parsers.eof
+ + blank^0
+ end
+
+ parsers.content_blank = parsers.minimally_indented_blankline
+
+ parsers.ref_or_block_separated = parsers.sep_group_no_output(parsers.content_blank)
+ * ( parsers.minimally_indented_ref
+ - parsers.content_blank)
+ + parsers.block_sep_group(parsers.content_blank)
+ * ( parsers.minimally_indented_block
+ - parsers.content_blank)
+
+ parsers.loop_body_pair =
+ parsers.create_loop_body_pair(parsers.ref_or_block_separated,
+ parsers.minimally_indented_par_or_plain_no_blank,
+ parsers.block_sep_group(parsers.content_blank),
+ parsers.par_sep_group(parsers.content_blank))
+
+ parsers.content_loop = ( V("Block")
+ * parsers.loop_body_pair.block^0
+ + (V("Paragraph") + V("Plain"))
+ * parsers.ref_or_block_separated
+ * parsers.loop_body_pair.block^0
+ + (V("Paragraph") + V("Plain"))
+ * parsers.loop_body_pair.par^0)
+ * parsers.content_blank^0
+
+ parsers.indented_content = function()
+ return Ct( (V("Reference") + (parsers.blankline / ""))
+ * parsers.content_blank^0
+ * parsers.check_minimal_indent
+ * parsers.content_loop
+ + (V("Reference") + (parsers.blankline / ""))
+ * parsers.content_blank^0
+ + parsers.content_loop)
+ end
+
+ parsers.add_indent = function(pattern, name, breakable)
+ return Cg(Cmt( Cb("indent_info")
+ * Ct(pattern)
+ * (#parsers.linechar * Cc(false) + Cc(true)) -- check if starter is blank
+ * Cc(name)
+ * Cc(breakable),
+ process_starter_indent), "indent_info")
+ end
+
% \end{macrocode}
% \par
% \begin{markdown}
@@ -24766,18 +27742,28 @@ function M.reader.new(writer, options)
parsers.dig = parsers.digit
end
- parsers.enumerator = C(parsers.dig^3 * parsers.period) * #parsers.spacing
- + C(parsers.dig^2 * parsers.period) * #parsers.spacing
- * (parsers.tab + parsers.space^1)
- + C(parsers.dig * parsers.period) * #parsers.spacing
- * (parsers.tab + parsers.space^-2)
- + parsers.space * C(parsers.dig^2 * parsers.period)
- * #parsers.spacing
- + parsers.space * C(parsers.dig * parsers.period)
- * #parsers.spacing
- * (parsers.tab + parsers.space^-1)
- + parsers.space * parsers.space * C(parsers.dig^1
- * parsers.period) * #parsers.spacing
+ parsers.enumerator = function(delimiter_type, interrupting)
+ local delimiter_range
+ local allowed_end
+ if interrupting then
+ delimiter_range = P("1")
+ allowed_end = C(parsers.spacechar^1) * #parsers.linechar
+ else
+ delimiter_range = parsers.dig * parsers.dig^-8
+ allowed_end = C(parsers.spacechar^1) + #(parsers.newline + parsers.eof)
+ end
+
+ return parsers.check_trail
+ * Ct(C(delimiter_range) * C(delimiter_type))
+ * allowed_end
+ end
+
+ parsers.starter = parsers.bullet(parsers.dash)
+ + parsers.bullet(parsers.asterisk)
+ + parsers.bullet(parsers.plus)
+ + parsers.enumerator(parsers.period)
+ + parsers.enumerator(parsers.rparent)
+
% \end{macrocode}
% \par
% \begin{markdown}
@@ -24786,20 +27772,369 @@ function M.reader.new(writer, options)
%
% \end{markdown}
% \begin{macrocode}
- -- strip off leading > and indents, and run through blocks
- parsers.blockquote_body = ((parsers.leader * parsers.more * parsers.space^-1)/""
- * parsers.linechar^0 * parsers.newline)^1
- * (-V("BlockquoteExceptions") * parsers.linechar^1
- * parsers.newline)^0
+ parsers.blockquote_start = parsers.check_trail * C(parsers.more) * C(parsers.spacechar^0)
+
+ parsers.blockquote_body = parsers.add_indent(parsers.blockquote_start, "bq", true)
+ * parsers.indented_content()
+ * remove_indent("bq")
if not options.breakableBlockquotes then
- parsers.blockquote_body = parsers.blockquote_body
- * (parsers.blankline^0 / "")
+ parsers.blockquote_body = parsers.add_indent(parsers.blockquote_start, "bq", false)
+ * parsers.indented_content()
+ * remove_indent("bq")
end
% \end{macrocode}
% \par
% \begin{markdown}
%
+%#### Helpers for Emphasis and Strong Emphasis (local)
+%
+% Parse the content of a table `content_part` with links, images and emphasis disabled.
+%
+% \end{markdown}
+% \begin{macrocode}
+ local function parse_content_part(content_part)
+ local rope = util.rope_to_string(content_part)
+ local parsed = self.parser_functions.parse_inlines_no_link_or_emphasis(rope)
+ parsed.indent_info = nil
+ return parsed
+ end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Collect the content between the `opening_index` and `closing_index` in the delimiter table `t`.
+%
+% \end{markdown}
+% \begin{macrocode}
+ local function collect_emphasis_content(t, opening_index, closing_index)
+ local content = {}
+
+ local content_part = {}
+ for i = opening_index, closing_index do
+ local value = t[i]
+
+ if value.rendered ~= nil then
+ content[#content + 1] = parse_content_part(content_part)
+ content_part = {}
+ content[#content + 1] = value.rendered
+ value.rendered = nil
+ else
+ if value.type == "delimiter" and value.element == "emphasis" then
+ if value.is_active then
+ content_part[#content_part + 1] = string.rep(value.character, value.current_count)
+ end
+ else
+ content_part[#content_part + 1] = value.content
+ end
+ value.content = ''
+ value.is_active = false
+ end
+ end
+
+ if next(content_part) ~= nil then
+ content[#content + 1] = parse_content_part(content_part)
+ end
+
+ return content
+ end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Render content between the `opening_index` and `closing_index` in the delimiter table `t`
+% as emphasis.
+%
+% \end{markdown}
+% \begin{macrocode}
+ local function fill_emph(t, opening_index, closing_index)
+ local content = collect_emphasis_content(t, opening_index + 1, closing_index - 1)
+ t[opening_index + 1].is_active = true
+ t[opening_index + 1].rendered = writer.emphasis(content)
+ end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Render content between the `opening_index` and `closing_index` in the delimiter table `t`
+% as strong emphasis.
+%
+% \end{markdown}
+% \begin{macrocode}
+ local function fill_strong(t, opening_index, closing_index)
+ local content = collect_emphasis_content(t, opening_index + 1, closing_index - 1)
+ t[opening_index + 1].is_active = true
+ t[opening_index + 1].rendered = writer.strong(content)
+ end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Check whether the opening delimiter `opening_delimiter` and closing delimiter `closing_delimiter` break
+% rule three together.
+%
+% \end{markdown}
+% \begin{macrocode}
+ local function breaks_three_rule(opening_delimiter, closing_delimiter)
+ return (opening_delimiter.is_closing or closing_delimiter.is_opening) and
+ ((opening_delimiter.original_count + closing_delimiter.original_count) % 3 == 0) and
+ (opening_delimiter.original_count % 3 ~= 0 or closing_delimiter.original_count % 3 ~= 0)
+ end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Look for the first potential emphasis opener in the delimiter table `t` in the range from
+% `bottom_index` to `latest_index` that has the same character `character` as the closing
+% delimiter `closing_delimiter`.
+%
+% \end{markdown}
+% \begin{macrocode}
+ local function find_emphasis_opener(t, bottom_index, latest_index, character, closing_delimiter)
+ for i = latest_index, bottom_index, -1 do
+ local value = t[i]
+ if value.is_active and
+ value.is_opening and
+ value.type == "delimiter" and
+ value.element == "emphasis" and
+ (value.character == character) and
+ (value.current_count > 0) then
+ if not breaks_three_rule(value, closing_delimiter) then
+ return i
+ end
+ end
+ end
+ end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Iterate over the delimiters in the delimiter table `t`, producing emphasis or strong emphasis macros.
+%
+% \end{markdown}
+% \begin{macrocode}
+ local function process_emphasis(t, opening_index, closing_index)
+ for i = opening_index, closing_index do
+ local value = t[i]
+ if value.type == "delimiter" and value.element == "emphasis" then
+ local delimiter_length = string.len(value.content)
+ value.character = string.sub(value.content, 1, 1)
+ value.current_count = delimiter_length
+ value.original_count = delimiter_length
+ end
+ end
+
+ local openers_bottom = {
+ ['*'] = {
+ [true] = {opening_index, opening_index, opening_index},
+ [false] = {opening_index, opening_index, opening_index}
+ },
+ ['_'] = {
+ [true] = {opening_index, opening_index, opening_index},
+ [false] = {opening_index, opening_index, opening_index}
+ }
+ }
+
+ local current_position = opening_index
+ local max_position = closing_index
+
+ while current_position <= max_position do
+ local value = t[current_position]
+
+ if value.type ~= "delimiter" or
+ value.element ~= "emphasis" or
+ not value.is_active or
+ not value.is_closing or
+ (value.current_count <= 0) then
+ current_position = current_position + 1
+ goto continue
+ end
+
+ local character = value.character
+ local is_opening = value.is_opening
+ local closing_length_modulo_three = value.original_count % 3
+
+ local current_openers_bottom = openers_bottom[character][is_opening][closing_length_modulo_three + 1]
+
+ local opener_position = find_emphasis_opener(t, current_openers_bottom, current_position - 1, character, value)
+
+ if (opener_position == nil) then
+ openers_bottom[character][is_opening][closing_length_modulo_three + 1] = current_position
+ current_position = current_position + 1
+ goto continue
+ end
+
+ local opening_delimiter = t[opener_position]
+
+ local current_opening_count = opening_delimiter.current_count
+ local current_closing_count = t[current_position].current_count
+
+ if (current_opening_count >= 2) and (current_closing_count >= 2) then
+ opening_delimiter.current_count = current_opening_count - 2
+ t[current_position].current_count = current_closing_count - 2
+ fill_strong(t, opener_position, current_position)
+ else
+ opening_delimiter.current_count = current_opening_count - 1
+ t[current_position].current_count = current_closing_count - 1
+ fill_emph(t, opener_position, current_position)
+ end
+
+ ::continue::
+ end
+ end
+
+ local cont = lpeg.R("\128\191") -- continuation byte
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Match a UTF-8 character of byte length `n`.
+%
+% \end{markdown}
+% \begin{macrocode}
+ local function utf8_by_byte_count(n)
+ if (n == 1) then
+ return lpeg.R("\0\127")
+ end
+ if (n == 2) then
+ return lpeg.R("\194\223") * cont
+ end
+ if (n == 3) then
+ return lpeg.R("\224\239") * cont * cont
+ end
+ if (n == 4) then
+ return lpeg.R("\240\244") * cont * cont * cont
+ end
+ end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Check if a there is a character of a type `chartype` between the start position `start_pos`
+% and end position `end_pos` in a string `s` relative to current index `i`.
+%
+% \end{markdown}
+% \begin{macrocode}
+ local function check_unicode_type(s, i, start_pos, end_pos, chartype)
+ local c
+ local char_length
+ for pos = start_pos, end_pos, 1 do
+ if (start_pos < 0) then
+ char_length = -pos
+ else
+ char_length = pos + 1
+ end
+ c = lpeg.match({ C(utf8_by_byte_count(char_length)) },s,i+pos)
+ if (c ~= nil) and (unicode.utf8.match(c, chartype)) then
+ return i
+ end
+ end
+ end
+
+ local function check_preceding_unicode_punctuation(s, i)
+ return check_unicode_type(s, i, -4, -1, "%p")
+ end
+
+ local function check_preceding_unicode_whitespace(s, i)
+ return check_unicode_type(s, i, -4, -1, "%s")
+ end
+
+ local function check_following_unicode_punctuation(s, i)
+ return check_unicode_type(s, i, 0, 3, "%p")
+ end
+
+ local function check_following_unicode_whitespace(s, i)
+ return check_unicode_type(s, i, 0, 3, "%s")
+ end
+
+ parsers.unicode_preceding_punctuation = B(parsers.escapable)
+ + Cmt(parsers.succeed, check_preceding_unicode_punctuation)
+
+ parsers.unicode_preceding_whitespace = Cmt(parsers.succeed, check_preceding_unicode_whitespace)
+
+ parsers.unicode_following_punctuation = #parsers.escapable
+ + Cmt(parsers.succeed, check_following_unicode_punctuation)
+
+ parsers.unicode_following_whitespace = Cmt(parsers.succeed, check_following_unicode_whitespace)
+
+ parsers.delimiter_run = function(character)
+ return (B(parsers.backslash * character) + -B(character))
+ * character^1
+ * -#character
+ end
+
+ parsers.left_flanking_delimiter_run = function(character)
+ return (B( parsers.any)
+ * (parsers.unicode_preceding_punctuation + parsers.unicode_preceding_whitespace)
+ + -B(parsers.any))
+ * parsers.delimiter_run(character)
+ * parsers.unicode_following_punctuation
+ + parsers.delimiter_run(character)
+ * -#(parsers.unicode_following_punctuation + parsers.unicode_following_whitespace
+ + parsers.eof)
+ end
+
+ parsers.right_flanking_delimiter_run = function(character)
+ return parsers.unicode_preceding_punctuation
+ * parsers.delimiter_run(character)
+ * (parsers.unicode_following_punctuation + parsers.unicode_following_whitespace
+ + parsers.eof)
+ + (B(parsers.any)
+ * -(parsers.unicode_preceding_punctuation + parsers.unicode_preceding_whitespace))
+ * parsers.delimiter_run(character)
+ end
+
+ if options.underscores then
+ parsers.emph_start = parsers.left_flanking_delimiter_run(parsers.asterisk)
+ + (-#parsers.right_flanking_delimiter_run(parsers.underscore)
+ + (parsers.unicode_preceding_punctuation
+ * #parsers.right_flanking_delimiter_run(parsers.underscore)))
+ * parsers.left_flanking_delimiter_run(parsers.underscore)
+
+ parsers.emph_end = parsers.right_flanking_delimiter_run(parsers.asterisk)
+ + (-#parsers.left_flanking_delimiter_run(parsers.underscore)
+ + #(parsers.left_flanking_delimiter_run(parsers.underscore)
+ * parsers.unicode_following_punctuation))
+ * parsers.right_flanking_delimiter_run(parsers.underscore)
+ else
+ parsers.emph_start = parsers.left_flanking_delimiter_run(parsers.asterisk)
+
+ parsers.emph_end = parsers.right_flanking_delimiter_run(parsers.asterisk)
+ end
+
+ parsers.emph_capturing_open_and_close = #parsers.emph_start * #parsers.emph_end
+ * Ct( Cg(Cc("delimiter"), "type")
+ * Cg(Cc("emphasis"), "element")
+ * Cg(C(parsers.emph_start), "content")
+ * Cg(Cc(true), "is_opening")
+ * Cg(Cc(true), "is_closing"))
+
+ parsers.emph_capturing_open = Ct( Cg(Cc("delimiter"), "type")
+ * Cg(Cc("emphasis"), "element")
+ * Cg(C(parsers.emph_start), "content")
+ * Cg(Cc(true), "is_opening")
+ * Cg(Cc(false), "is_closing"))
+
+ parsers.emph_capturing_close = Ct( Cg(Cc("delimiter"), "type")
+ * Cg(Cc("emphasis"), "element")
+ * Cg(C(parsers.emph_end), "content")
+ * Cg(Cc(false), "is_opening")
+ * Cg(Cc(true), "is_closing"))
+
+ parsers.emph_open_or_close = parsers.emph_capturing_open_and_close
+ + parsers.emph_capturing_open
+ + parsers.emph_capturing_close
+
+ parsers.emph_open = parsers.emph_capturing_open_and_close
+ + parsers.emph_capturing_open
+
+ parsers.emph_close = parsers.emph_capturing_open_and_close
+ + parsers.emph_capturing_close
+
+% \end{macrocode}
+% \par
+% \begin{markdown}
+%
%#### Helpers for Links and Link Reference Definitions (local)
%
% \end{markdown}
@@ -24818,14 +28153,16 @@ function M.reader.new(writer, options)
%
% \end{markdown}
% \begin{macrocode}
- function self.register_link(tag, url, title,
+ function self.register_link(_, tag, url, title,
attributes)
- tag = self.normalize_tag(tag)
- references[tag] = {
- url = url,
- title = title,
- attributes = attributes,
- }
+ local normalized_tag = self.normalize_tag(tag)
+ if references[normalized_tag] == nil then
+ references[normalized_tag] = {
+ url = url,
+ title = title,
+ attributes = attributes
+ }
+ end
return ""
end
@@ -24834,103 +28171,505 @@ function M.reader.new(writer, options)
% \begin{markdown}
%
% The \luamdef{reader->lookup_reference} method looks up a
-% reference with link label `tag`. When the reference exists
-% the method returns a link. The attributes of a link are
-% produced by merging the attributes of the link reference
-% and the optional `attributes`. Otherwise, the method returns a
-% two-tuple of `nil` and fallback text constructed from the
-% link text `label` and the optional spaces `sps` between the
-% link text and the link label.
-%
-% \end{markdown}
-% \begin{macrocode}
- function self.lookup_reference(label, sps, tag,
- attributes)
- local tagpart
- if not tag then
- tag = label
- tagpart = ""
- elseif tag == "" then
- tag = label
- tagpart = "[]"
- else
- tagpart = {
- "[",
- self.parser_functions.parse_inlines(tag),
- "]"
- }
- end
- if sps then
- tagpart = {sps, tagpart}
+% reference with link label `tag`.
+%
+% \end{markdown}
+% \begin{macrocode}
+ function self.lookup_reference(tag)
+ return references[self.normalize_tag(tag)]
+ end
+
+ parsers.title_s_direct_ref = parsers.squote
+ * Cs((parsers.html_entities
+ + (parsers.anyescaped - parsers.squote - parsers.blankline^2))^0)
+ * parsers.squote
+
+ parsers.title_d_direct_ref = parsers.dquote
+ * Cs((parsers.html_entities
+ + (parsers.anyescaped - parsers.dquote - parsers.blankline^2))^0)
+ * parsers.dquote
+
+ parsers.title_p_direct_ref = parsers.lparent
+ * Cs((parsers.html_entities
+ + (parsers.anyescaped - parsers.lparent - parsers.rparent - parsers.blankline^2))^0)
+ * parsers.rparent
+
+ parsers.title_direct_ref = parsers.title_s_direct_ref
+ + parsers.title_d_direct_ref
+ + parsers.title_p_direct_ref
+
+ parsers.inline_direct_ref_inside = parsers.lparent * parsers.spnl
+ * Cg(parsers.url + Cc(""), "url")
+ * parsers.spnl
+ * Cg(parsers.title_direct_ref + Cc(""), "title")
+ * parsers.spnl * parsers.rparent
+
+ parsers.inline_direct_ref = parsers.lparent * parsers.spnlc
+ * Cg(parsers.url + Cc(""), "url")
+ * parsers.spnlc
+ * Cg(parsers.title + Cc(""), "title")
+ * parsers.spnlc * parsers.rparent
+
+ parsers.empty_link = parsers.lbracket
+ * parsers.rbracket
+
+ parsers.inline_link = parsers.link_text
+ * parsers.inline_direct_ref
+
+ parsers.full_link = parsers.link_text
+ * parsers.link_label
+
+ parsers.shortcut_link = parsers.link_label
+ * -(parsers.empty_link + parsers.link_label)
+
+ parsers.collapsed_link = parsers.link_label
+ * parsers.empty_link
+
+ parsers.image_opening = #(parsers.exclamation * parsers.inline_link)
+ * Cg(Cc("inline"), "link_type")
+ + #(parsers.exclamation * parsers.full_link)
+ * Cg(Cc("full"), "link_type")
+ + #(parsers.exclamation * parsers.collapsed_link)
+ * Cg(Cc("collapsed"), "link_type")
+ + #(parsers.exclamation * parsers.shortcut_link)
+ * Cg(Cc("shortcut"), "link_type")
+ + #(parsers.exclamation * parsers.empty_link)
+ * Cg(Cc("empty"), "link_type")
+
+ parsers.link_opening = #parsers.inline_link
+ * Cg(Cc("inline"), "link_type")
+ + #parsers.full_link
+ * Cg(Cc("full"), "link_type")
+ + #parsers.collapsed_link
+ * Cg(Cc("collapsed"), "link_type")
+ + #parsers.shortcut_link
+ * Cg(Cc("shortcut"), "link_type")
+ + #parsers.empty_link
+ * Cg(Cc("empty_link"), "link_type")
+ + #parsers.link_text
+ * Cg(Cc("link_text"), "link_type")
+
+ parsers.link_image_opening = Ct( Cg(Cc("delimiter"), "type")
+ * Cg(Cc(true), "is_opening")
+ * Cg(Cc(false), "is_closing")
+ * ( Cg(Cc("image"), "element")
+ * parsers.image_opening
+ * Cg(parsers.exclamation * parsers.lbracket, "content")
+ + Cg(Cc("link"), "element")
+ * parsers.link_opening
+ * Cg(parsers.lbracket, "content")))
+
+ parsers.link_image_closing = Ct( Cg(Cc("delimiter"), "type")
+ * Cg(Cc("link"), "element")
+ * Cg(Cc(false), "is_opening")
+ * Cg(Cc(true), "is_closing")
+ * ( Cg(Cc(true), "is_direct")
+ * Cg(parsers.rbracket * #parsers.inline_direct_ref, "content")
+ + Cg(Cc(false), "is_direct")
+ * Cg(parsers.rbracket, "content")))
+
+ parsers.link_image_open_or_close = parsers.link_image_opening
+ + parsers.link_image_closing
+
+ if options.html then
+ parsers.link_emph_precedence = parsers.inticks
+ + parsers.autolink
+ + parsers.html_inline_tags
+ else
+ parsers.link_emph_precedence = parsers.inticks
+ + parsers.autolink
+ end
+
+ parsers.link_and_emph_endline = parsers.newline
+ * ((parsers.check_minimal_indent
+ * -V("EndlineExceptions")
+ + parsers.check_optional_indent
+ * -V("EndlineExceptions")
+ * -parsers.starter) / "")
+ * parsers.spacechar^0 / "\n"
+
+ parsers.link_and_emph_content = Ct( Cg(Cc("content"), "type")
+ * Cg(Cs(( parsers.link_emph_precedence
+ + parsers.backslash * parsers.any
+ + parsers.link_and_emph_endline
+ + (parsers.linechar
+ - parsers.blankline^2
+ - parsers.link_image_open_or_close
+ - parsers.emph_open_or_close))^0), "content"))
+
+ parsers.link_and_emph_table = (parsers.link_image_opening + parsers.emph_open)
+ * parsers.link_and_emph_content
+ * ((parsers.link_image_open_or_close + parsers.emph_open_or_close)
+ * parsers.link_and_emph_content)^1
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Collect the content between the `opening_index` and `closing_index` in the delimiter table `t`.
+%
+% \end{markdown}
+% \begin{macrocode}
+ local function collect_link_content(t, opening_index, closing_index)
+ local content = {}
+ for i = opening_index, closing_index do
+ content[#content + 1] = t[i].content
end
- tag = self.normalize_tag(tag)
- local r = references[tag]
- if r then
- local merged_attributes = {}
- for _, attribute in ipairs(r.attributes or {}) do
- table.insert(merged_attributes, attribute)
+ return util.rope_to_string(content)
+ end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Look for the closest potential link opener in the delimiter table `t` in the range from
+% `bottom_index` to `latest_index`.
+%
+% \end{markdown}
+% \begin{macrocode}
+ local function find_link_opener(t, bottom_index, latest_index)
+ for i = latest_index, bottom_index, -1 do
+ local value = t[i]
+ if value.type == "delimiter" and
+ value.is_opening and
+ (value.element == "link" or value.element == "image")
+ and not value.removed then
+ if value.is_active then
+ return i
+ end
+ value.removed = true
+ return nil
end
- for _, attribute in ipairs(attributes or {}) do
- table.insert(merged_attributes, attribute)
+ end
+ end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Find the position of a delimiter that closes a full link after an an index `latest_index`
+% in the delimiter table `t`.
+%
+% \end{markdown}
+% \begin{macrocode}
+ local function find_next_link_closing_index(t, latest_index)
+ for i = latest_index, #t do
+ local value = t[i]
+ if value.is_closing and
+ value.element == "link" and
+ not value.removed then
+ return i
end
- if #merged_attributes == 0 then
- merged_attributes = nil
+ end
+ end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Disable all preceding opening link delimiters by marking them inactive with the `is_active` property
+% to prevent links within links. Images within links are allowed.
+%
+% \end{markdown}
+% \begin{macrocode}
+ local function disable_previous_link_openers(t, opening_index)
+ if t[opening_index].element == "image" then
+ return
+ end
+
+ for i = opening_index, 1, -1 do
+ local value = t[i]
+ if value.is_active and
+ value.type == "delimiter" and
+ value.is_opening and
+ value.element == "link" then
+ value.is_active = false
end
- return {
- url = r.url,
- title = r.title,
- attributes = merged_attributes,
- }
- else
- return nil, {
- "[",
- self.parser_functions.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 writer.defer_call(function()
- local r,fallback = self.lookup_reference(label, sps, tag)
- if r then
- return writer.link(
- self.parser_functions.parse_inlines_no_link(label),
- r.url, r.title)
- else
- return fallback
+% \end{macrocode}
+% \begin{markdown}
+%
+% Disable the delimiters between the `opening_index` and `closing_index` in the delimiter table `t`
+% by marking them inactive with the `is_active` property.
+%
+% \end{markdown}
+% \begin{macrocode}
+ local function disable_range(t, opening_index, closing_index)
+ for i = opening_index, closing_index do
+ local value = t[i]
+ if value.is_active then
+ value.is_active = false
+ if value.type == "delimiter" then
+ value.removed = true
+ end
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 writer.defer_call(function()
- local r,fallback = self.lookup_reference(label, sps, tag)
- if r then
- return writer.image(writer.string(label), r.url, r.title)
- else
- return {"!", fallback}
+% \end{macrocode}
+% \begin{markdown}
+%
+% Clear the parsed content between the `opening_index` and `closing_index` in the delimiter table `t`.
+%
+% \end{markdown}
+% \begin{macrocode}
+ local function delete_parsed_content_in_range(t, opening_index, closing_index)
+ for i = opening_index, closing_index do
+ t[i].rendered = nil
+ end
+ end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Clear the content between the `opening_index` and `closing_index` in the delimiter table `t`.
+%
+% \end{markdown}
+% \begin{macrocode}
+ local function empty_content_in_range(t, opening_index, closing_index)
+ for i = opening_index, closing_index do
+ t[i].content = ''
+ end
+ end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Join the attributes from the link reference definition `reference_attributes` with the link's own
+% attributes `own_attributes`.
+%
+% \end{markdown}
+% \begin{macrocode}
+ local function join_attributes(reference_attributes, own_attributes)
+ local merged_attributes = {}
+ for _, attribute in ipairs(reference_attributes or {}) do
+ table.insert(merged_attributes, attribute)
+ end
+ for _, attribute in ipairs(own_attributes or {}) do
+ table.insert(merged_attributes, attribute)
+ end
+ if next(merged_attributes) == nil then
+ merged_attributes = nil
+ end
+ return merged_attributes
+ end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Parse content between two delimiters in the delimiter table `t`. Produce the respective link and image
+% macros.
+%
+% \end{markdown}
+% \begin{macrocode}
+ local function render_link_or_image(t, opening_index, closing_index, content_end_index, reference)
+ process_emphasis(t, opening_index, content_end_index)
+ local mapped = collect_emphasis_content(t, opening_index + 1, content_end_index - 1)
+
+ local rendered = {}
+ if (t[opening_index].element == "link") then
+ rendered = writer.link(mapped, reference.url, reference.title, reference.attributes)
+ end
+
+ if (t[opening_index].element == "image") then
+ rendered = writer.image(mapped, reference.url, reference.title, reference.attributes)
+ end
+
+ t[opening_index].rendered = rendered
+ delete_parsed_content_in_range(t, opening_index + 1, closing_index)
+ empty_content_in_range(t, opening_index, closing_index)
+ disable_previous_link_openers(t, opening_index)
+ disable_range(t, opening_index, closing_index)
+ end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Match the link destination of an inline link at index `closing_index` in table `t`
+% when `match_reference` is true. Additionally, match attributes when the option
+% \Opt{linkAttributes} is enabled.
+%
+% \end{markdown}
+% \begin{macrocode}
+ local function resolve_inline_following_content(t, closing_index, match_reference, match_link_attributes)
+ local content = ""
+ for i = closing_index + 1, #t do
+ content = content .. t[i].content
+ end
+
+ local matching_content = parsers.succeed
+
+ if match_reference then
+ matching_content = matching_content * parsers.inline_direct_ref_inside
+ end
+
+ if match_link_attributes then
+ matching_content = matching_content * Cg(Ct(parsers.attributes^-1), "attributes")
+ end
+
+ local matched = lpeg.match(Ct(matching_content * Cg(Cp(), "end_position")), content)
+
+ local matched_count = matched.end_position - 1
+ for i = closing_index + 1, #t do
+ local value = t[i]
+
+ local chars_left = matched_count
+ matched_count = matched_count - #value.content
+
+ if matched_count <= 0 then
+ value.content = value.content:sub(chars_left + 1)
+ break
end
- end)
+
+ value.content = ''
+ value.is_active = false
+ end
+
+ local attributes = matched.attributes
+ if attributes == nil or next(attributes) == nil then
+ attributes = nil
+ end
+
+ return {
+ url = matched.url or "",
+ title = matched.title or "",
+ attributes = attributes
+ }
+ end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Resolve an inline link [a](b "c") from the delimiters at `opening_index` and `closing_index`
+% within a delimiter table `t`. Here, compared to other types of links, no reference definition is needed.
+%
+% \end{markdown}
+% \begin{macrocode}
+ local function resolve_inline_link(t, opening_index, closing_index)
+ local inline_content = resolve_inline_following_content(t, closing_index, true, t.match_link_attributes)
+ render_link_or_image(t, opening_index, closing_index, closing_index, inline_content)
+ end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Resolve a shortcut link [a] from the delimiters at `opening_index` and `closing_index` within a delimiter table `t`.
+% Continue if a tag `a` is not found in the references.
+%
+% \end{markdown}
+% \begin{macrocode}
+ local function resolve_shortcut_link(t, opening_index, closing_index)
+ local content = collect_link_content(t, opening_index + 1, closing_index - 1)
+ local r = self.lookup_reference(content)
+
+ if r then
+ local inline_content = resolve_inline_following_content(t, closing_index, false, t.match_link_attributes)
+ r.attributes = join_attributes(r.attributes, inline_content.attributes)
+ render_link_or_image(t, opening_index, closing_index, closing_index, r)
+ end
+ end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Resolve a full link [a][b] from the delimiters at `opening_index` and `closing_index` within a delimiter table `t`.
+% Continue if a tag `b` is not found in the references.
+%
+% \end{markdown}
+% \begin{macrocode}
+ local function resolve_full_link(t, opening_index, closing_index)
+ local next_link_closing_index = find_next_link_closing_index(t, closing_index + 4)
+ local next_link_content = collect_link_content(t, closing_index + 3, next_link_closing_index - 1)
+ local r = self.lookup_reference(next_link_content)
+
+ if r then
+ local inline_content = resolve_inline_following_content(t, next_link_closing_index, false,
+ t.match_link_attributes)
+ r.attributes = join_attributes(r.attributes, inline_content.attributes)
+ render_link_or_image(t, opening_index, next_link_closing_index, closing_index, r)
+ end
+ end
+
+% \end{macrocode}
+% \begin{markdown}
+%
+% Resolve a collapsed link [a][] from the delimiters at `opening_index` and `closing_index`
+% within a delimiter table `t`.
+% Continue if a tag `a` is not found in the references.
+%
+% \end{markdown}
+% \begin{macrocode}
+ local function resolve_collapsed_link(t, opening_index, closing_index)
+ local next_link_closing_index = find_next_link_closing_index(t, closing_index + 4)
+ local content = collect_link_content(t, opening_index + 1, closing_index - 1)
+ local r = self.lookup_reference(content)
+
+ if r then
+ local inline_content = resolve_inline_following_content(t, closing_index, false, t.match_link_attributes)
+ r.attributes = join_attributes(r.attributes, inline_content.attributes)
+ render_link_or_image(t, opening_index, next_link_closing_index, closing_index, r)
+ end
end
- parsers.direct_link_tail = parsers.spnl
- * parsers.lparent
- * (parsers.url + Cc("")) -- link can be empty [foo]()
- * parsers.optionaltitle
- * parsers.rparent
+% \end{macrocode}
+% \begin{markdown}
+%
+% Parse a table of link and emphasis delimiters `t`.
+% First, iterate over the link delimiters and produce either link or image macros.
+% Then run `process_emphasis` over the entire delimiter table, resolving emphasis and strong
+% emphasis and parsing any content outside of closed delimiters.
+%
+% \end{markdown}
+% \begin{macrocode}
+ local function process_links_and_emphasis(t)
+ for _,value in ipairs(t) do
+ value.is_active = true
+ end
+
+ for i,value in ipairs(t) do
+ if not value.is_closing or
+ value.type ~= "delimiter" or
+ not (value.element == "link" or value.element == "image") then
+ goto continue
+ end
+
+ local opener_position = find_link_opener(t, 1, i - 1)
+ if (opener_position == nil) then
+ goto continue
+ end
+
+ local opening_delimiter = t[opener_position]
+ opening_delimiter.removed = true
+
+ local link_type = opening_delimiter.link_type
+
+ if (link_type == "inline") then
+ resolve_inline_link(t, opener_position, i)
+ end
+ if (link_type == "shortcut") then
+ resolve_shortcut_link(t, opener_position, i)
+ end
+ if (link_type == "full") then
+ resolve_full_link(t, opener_position, i)
+ end
+ if (link_type == "collapsed") then
+ resolve_collapsed_link(t, opener_position, i)
+ end
+
+ ::continue::
+ end
- parsers.direct_link = (parsers.tag / self.parser_functions.parse_inlines_no_link)
- * parsers.direct_link_tail
+ t[#t].content = t[#t].content:gsub("%s*$","")
+
+ process_emphasis(t, 1, #t)
+ local final_result = collect_emphasis_content(t, 1, #t)
+ return final_result
+ end
+
+ function self.defer_link_and_emphasis_processing(delimiter_table)
+ return writer.defer_call(function()
+ return process_links_and_emphasis(delimiter_table)
+ end)
+ end
- parsers.direct_image = parsers.exclamation
- * (parsers.tag / self.parser_functions.parse_inlines)
- * parsers.direct_link_tail
% \end{macrocode}
% \par
% \begin{markdown}
@@ -24942,7 +28681,7 @@ function M.reader.new(writer, options)
parsers.Str = (parsers.normalchar * (parsers.normalchar + parsers.at)^0)
/ writer.string
- parsers.Symbol = (V("SpecialChar") - parsers.tightblocksep)
+ parsers.Symbol = (parsers.backtick^1 + V("SpecialChar"))
/ writer.string
parsers.Ellipsis = P("...") / writer.ellipsis
@@ -24954,83 +28693,112 @@ function M.reader.new(writer, options)
if options.blankBeforeBlockquote then
parsers.bqstart = parsers.fail
else
- parsers.bqstart = parsers.more
+ parsers.bqstart = parsers.blockquote_start
end
if options.blankBeforeHeading then
parsers.headerstart = parsers.fail
else
- parsers.headerstart = parsers.hash
- + (parsers.line * (parsers.equal^1 + parsers.dash^1)
- * parsers.optionalspace * parsers.newline)
+ parsers.headerstart = parsers.atx_heading
+ end
+
+ if options.blankBeforeList then
+ parsers.interrupting_bullets = parsers.fail
+ parsers.interrupting_enumerators = parsers.fail
+ else
+ parsers.interrupting_bullets = parsers.bullet(parsers.dash, true)
+ + parsers.bullet(parsers.asterisk, true)
+ + parsers.bullet(parsers.plus, true)
+
+ parsers.interrupting_enumerators = parsers.enumerator(parsers.period, true)
+ + parsers.enumerator(parsers.rparent, true)
+ end
+
+ if options.html then
+ parsers.html_interrupting = parsers.check_trail
+ * ( parsers.html_incomplete_open_tag
+ + parsers.html_incomplete_close_tag
+ + parsers.html_incomplete_open_special_tag
+ + parsers.html_comment_start
+ + parsers.html_cdatasection_start
+ + parsers.html_declaration_start
+ + parsers.html_instruction_start
+ - parsers.html_close_special_tag
+ - parsers.html_empty_special_tag)
+ else
+ parsers.html_interrupting = parsers.fail
end
parsers.EndlineExceptions
= parsers.blankline -- paragraph break
- + parsers.tightblocksep -- nested list
+ parsers.eof -- end of document
+ parsers.bqstart
+ + parsers.thematic_break_lines
+ + parsers.interrupting_bullets
+ + parsers.interrupting_enumerators
+ parsers.headerstart
+ + parsers.html_interrupting
- parsers.Endline = parsers.newline
- * -V("EndlineExceptions")
- * parsers.spacechar^0
- / (options.hardLineBreaks and writer.hard_line_break
- or writer.space)
+ parsers.NoSoftLineBreakEndlineExceptions = parsers.EndlineExceptions
+ parsers.endline = parsers.newline
+ * (parsers.check_minimal_indent
+ * -V("EndlineExceptions")
+ + parsers.check_optional_indent
+ * -V("EndlineExceptions")
+ * -parsers.starter)
+ * parsers.spacechar^0
+
+ parsers.Endline = parsers.endline
+ / writer.soft_line_break
+
+ parsers.EndlineNoSub = parsers.endline
+
+ parsers.NoSoftLineBreakEndline
+ = parsers.newline
+ * (parsers.check_minimal_indent
+ * -V("NoSoftLineBreakEndlineExceptions")
+ + parsers.check_optional_indent
+ * -V("NoSoftLineBreakEndlineExceptions")
+ * -parsers.starter)
+ * parsers.spacechar^0
+ / writer.space
+
+ parsers.EndlineBreak = parsers.backslash * parsers.Endline
+ / writer.hard_line_break
+
parsers.OptionalIndent
= parsers.spacechar^1 / writer.space
- parsers.Space = parsers.spacechar^2 * parsers.Endline / writer.hard_line_break
- + parsers.spacechar^1 * parsers.Endline^-1 * parsers.eof / ""
+ parsers.Space = parsers.spacechar^2 * parsers.Endline
+ / writer.hard_line_break
+ + parsers.spacechar^1 * parsers.Endline^-1 * parsers.eof / self.expandtabs
+ + parsers.spacechar^1 * parsers.Endline
+ / writer.soft_line_break
+ + parsers.spacechar^1 * -parsers.newline / self.expandtabs
+
+ parsers.NoSoftLineBreakSpace
+ = parsers.spacechar^2 * parsers.Endline
+ / writer.hard_line_break
+ + parsers.spacechar^1 * parsers.Endline^-1 * parsers.eof / self.expandtabs
+ parsers.spacechar^1 * parsers.Endline
- * parsers.optionalspace
- / (options.hardLineBreaks
- and writer.hard_line_break
- or writer.space)
- + parsers.spacechar^1 * parsers.optionalspace
- / writer.space
+ / writer.soft_line_break
+ + parsers.spacechar^1 * -parsers.newline / self.expandtabs
parsers.NonbreakingEndline
- = parsers.newline
- * -V("EndlineExceptions")
- * parsers.spacechar^0
- / (options.hardLineBreaks and writer.hard_line_break
- or writer.nbsp)
+ = parsers.endline
+ / writer.soft_line_break
parsers.NonbreakingSpace
- = parsers.spacechar^2 * parsers.Endline / writer.hard_line_break
+ = parsers.spacechar^2 * parsers.Endline
+ / writer.hard_line_break
+ parsers.spacechar^1 * parsers.Endline^-1 * parsers.eof / ""
+ parsers.spacechar^1 * parsers.Endline
* parsers.optionalspace
- / (options.hardLineBreaks
- and writer.hard_line_break
- or writer.nbsp)
+ / writer.soft_line_break
+ parsers.spacechar^1 * parsers.optionalspace
/ writer.nbsp
- if options.underscores then
- parsers.Strong = ( parsers.between(parsers.Inline, parsers.doubleasterisks,
- parsers.doubleasterisks)
- + parsers.between(parsers.Inline, parsers.doubleunderscores,
- parsers.doubleunderscores)
- ) / writer.strong
-
- parsers.Emph = ( parsers.between(parsers.Inline, parsers.asterisk,
- parsers.asterisk)
- + parsers.between(parsers.Inline, parsers.underscore,
- parsers.underscore)
- ) / writer.emphasis
- else
- parsers.Strong = ( parsers.between(parsers.Inline, parsers.doubleasterisks,
- parsers.doubleasterisks)
- ) / writer.strong
-
- parsers.Emph = ( parsers.between(parsers.Inline, parsers.asterisk,
- parsers.asterisk)
- ) / writer.emphasis
- end
-
% \end{macrocode}
% \par
% \begin{markdown}
@@ -25046,7 +28814,6 @@ function self.auto_link_url(url, attributes)
return writer.link(writer.escape(url),
url, nil, attributes)
end
-
% \end{macrocode}
% \par
% \begin{markdown}
@@ -25074,39 +28841,21 @@ end
= parsers.auto_link_relative_reference
/ self.auto_link_url
- parsers.DirectLink = parsers.direct_link
- / writer.link
-
- parsers.IndirectLink = parsers.indirect_link
- / indirect_link
-
- -- parse a link or image (direct or indirect)
- parsers.Link = parsers.DirectLink + parsers.IndirectLink
-
- parsers.DirectImage = parsers.direct_image
- / writer.image
-
- parsers.IndirectImage = parsers.indirect_image
- / indirect_image
-
- parsers.Image = parsers.DirectImage + parsers.IndirectImage
-
- -- avoid parsing long strings of * or _ as emph/strong
- parsers.UlOrStarLine = parsers.asterisk^4 + parsers.underscore^4
- / writer.string
+ parsers.LinkAndEmph = Ct(parsers.link_and_emph_table)
+ / self.defer_link_and_emphasis_processing
parsers.EscapedChar = parsers.backslash * C(parsers.escapable) / writer.string
- parsers.InlineHtml = parsers.emptyelt_any / writer.inline_html_tag
- + (parsers.htmlcomment / self.parser_functions.parse_inlines_no_html)
- / writer.inline_html_comment
- + parsers.htmlinstruction
- + parsers.openelt_any / writer.inline_html_tag
- + parsers.closeelt_any / writer.inline_html_tag
+ parsers.InlineHtml = Cs(parsers.html_inline_comment) / writer.inline_html_comment
+ + Cs(parsers.html_any_empty_inline_tag
+ + parsers.html_inline_instruction
+ + parsers.html_inline_cdatasection
+ + parsers.html_inline_declaration
+ + parsers.html_any_open_inline_tag
+ + parsers.html_any_close_tag)
+ / writer.inline_html_tag
- parsers.HtmlEntity = parsers.hexentity / entities.hex_entity / writer.string
- + parsers.decentity / entities.dec_entity / writer.string
- + parsers.tagentity / entities.char_entity / writer.string
+ parsers.HtmlEntity = parsers.html_entities / writer.string
% \end{macrocode}
% \par
% \begin{markdown}
@@ -25115,40 +28864,40 @@ end
%
% \end{markdown}
% \begin{macrocode}
- parsers.DisplayHtml = (parsers.htmlcomment / self.parser_functions.parse_blocks_nested)
- / writer.block_html_comment
- + parsers.emptyelt_block / writer.block_html_element
- + parsers.openelt_exact("hr") / writer.block_html_element
- + parsers.in_matched_block_tags / writer.block_html_element
- + parsers.htmlinstruction
+ parsers.DisplayHtml = Cs(parsers.check_trail
+ * ( parsers.html_comment
+ + parsers.html_special_block
+ + parsers.html_block
+ + parsers.html_any_block
+ + parsers.html_instruction
+ + parsers.html_cdatasection
+ + parsers.html_declaration))
+ / writer.block_html_element
- parsers.Verbatim = Cs( (parsers.blanklines
- * ((parsers.indentedline - parsers.blankline))^1)^1
- ) / self.expandtabs / writer.verbatim
+ parsers.indented_non_blank_line = parsers.indentedline - parsers.blankline
- parsers.BlockquoteExceptions = parsers.leader * parsers.more
- + parsers.blankline
+ parsers.Verbatim = Cs(
+ parsers.check_code_trail
+ * (parsers.line - parsers.blankline)
+ * ((parsers.check_minimal_blank_indent_and_full_code_trail * parsers.blankline)^0
+ * ((parsers.check_minimal_indent / "") * parsers.check_code_trail
+ * (parsers.line - parsers.blankline))^1)^0
+ ) / self.expandtabs / writer.verbatim
- parsers.Blockquote = Cs(parsers.blockquote_body^1)
- / self.parser_functions.parse_blocks_nested
+ parsers.Blockquote = parsers.blockquote_body
/ writer.blockquote
- parsers.ThematicBreak = ( parsers.lineof(parsers.asterisk)
- + parsers.lineof(parsers.dash)
- + parsers.lineof(parsers.underscore)
- ) / writer.thematic_break
+ parsers.ThematicBreak = parsers.thematic_break_lines
+ / writer.thematic_break
parsers.Reference = parsers.define_reference_parser
- * parsers.blankline^1
/ self.register_link
- parsers.Paragraph = parsers.nonindentspace * Ct(parsers.Inline^1)
- * ( parsers.newline
- * ( parsers.blankline^1
- + #V("EndlineExceptions")
- )
- + parsers.eof)
- / writer.paragraph
+ parsers.Paragraph = parsers.freeze_trail
+ * (Ct((parsers.Inline)^1)
+ * (parsers.newline + parsers.eof)
+ * parsers.unfreeze_trail
+ / writer.paragraph)
parsers.Plain = parsers.nonindentspace * Ct(parsers.Inline^1)
/ writer.plain
@@ -25160,7 +28909,6 @@ end
%
% \end{markdown}
% \begin{macrocode}
- parsers.starter = parsers.bullet + parsers.enumerator
if options.taskLists then
parsers.tickbox = ( parsers.ticked_box
@@ -25171,43 +28919,117 @@ end
parsers.tickbox = parsers.fail
end
- -- we use \001 as a separator between a tight list item and a
- -- nested list under it.
- parsers.NestedList = Cs((parsers.optionallyindentedline
- - parsers.starter)^1)
- / function(a) return "\001"..a end
-
- parsers.ListBlockLine = parsers.optionallyindentedline
- - parsers.blankline - (parsers.indent^-1
- * parsers.starter)
-
- parsers.ListBlock = parsers.line * parsers.ListBlockLine^0
-
- parsers.ListContinuationBlock = parsers.blanklines * (parsers.indent / "")
- * parsers.ListBlock
+ parsers.list_blank = parsers.conditionally_indented_blankline
+
+ parsers.ref_or_block_list_separated = parsers.sep_group_no_output(parsers.list_blank)
+ * parsers.minimally_indented_ref
+ + parsers.block_sep_group(parsers.list_blank)
+ * parsers.minimally_indented_block
+
+ parsers.ref_or_block_non_separated = parsers.minimally_indented_ref
+ + (parsers.succeed / writer.interblocksep)
+ * parsers.minimally_indented_block
+ - parsers.minimally_indented_blankline
+
+ parsers.tight_list_loop_body_pair =
+ parsers.create_loop_body_pair(parsers.ref_or_block_non_separated,
+ parsers.minimally_indented_par_or_plain_no_blank,
+ (parsers.succeed / writer.interblocksep),
+ (parsers.succeed / writer.paragraphsep))
+
+ parsers.loose_list_loop_body_pair =
+ parsers.create_loop_body_pair(parsers.ref_or_block_list_separated,
+ parsers.minimally_indented_par_or_plain,
+ parsers.block_sep_group(parsers.list_blank),
+ parsers.par_sep_group(parsers.list_blank))
+
+ parsers.tight_list_content_loop = V("Block")
+ * parsers.tight_list_loop_body_pair.block^0
+ + (V("Paragraph") + V("Plain"))
+ * parsers.ref_or_block_non_separated
+ * parsers.tight_list_loop_body_pair.block^0
+ + (V("Paragraph") + V("Plain"))
+ * parsers.tight_list_loop_body_pair.par^0
+
+ parsers.loose_list_content_loop = V("Block")
+ * parsers.loose_list_loop_body_pair.block^0
+ + (V("Paragraph") + V("Plain"))
+ * parsers.ref_or_block_list_separated
+ * parsers.loose_list_loop_body_pair.block^0
+ + (V("Paragraph") + V("Plain"))
+ * parsers.loose_list_loop_body_pair.par^0
+
+ parsers.list_item_tightness_condition = -#( parsers.list_blank^0
+ * parsers.minimally_indented_ref_or_block_or_par)
+ * remove_indent("li")
+ + remove_indent("li")
+ * parsers.fail
+
+ parsers.indented_content_tight = Ct( (parsers.blankline / "")
+ * #parsers.list_blank
+ * remove_indent("li")
+ + ( (V("Reference") + (parsers.blankline / ""))
+ * parsers.check_minimal_indent
+ * parsers.tight_list_content_loop
+ + (V("Reference") + (parsers.blankline / ""))
+ + (parsers.tickbox^-1 / writer.escape)
+ * parsers.tight_list_content_loop
+ )
+ * parsers.list_item_tightness_condition
+ )
+
+ parsers.indented_content_loose = Ct( (parsers.blankline / "")
+ * #parsers.list_blank
+ + ( (V("Reference") + (parsers.blankline / ""))
+ * parsers.check_minimal_indent
+ * parsers.loose_list_content_loop
+ + (V("Reference") + (parsers.blankline / ""))
+ + (parsers.tickbox^-1 / writer.escape)
+ * parsers.loose_list_content_loop
+ )
+ )
parsers.TightListItem = function(starter)
- return -parsers.ThematicBreak
- * (Cs(starter / "" * parsers.tickbox^-1 * parsers.ListBlock * parsers.NestedList^-1)
- / self.parser_functions.parse_blocks_nested)
- * -(parsers.blanklines * parsers.indent)
+ return -parsers.ThematicBreak
+ * parsers.add_indent(starter, "li")
+ * parsers.indented_content_tight
end
parsers.LooseListItem = function(starter)
- return -parsers.ThematicBreak
- * Cs( starter / "" * parsers.tickbox^-1 * parsers.ListBlock * Cc("\n")
- * (parsers.NestedList + parsers.ListContinuationBlock^0)
- * (parsers.blanklines / "\n\n")
- ) / self.parser_functions.parse_blocks_nested
+ return -parsers.ThematicBreak
+ * parsers.add_indent(starter, "li")
+ * parsers.indented_content_loose
+ * remove_indent("li")
end
- parsers.BulletList = ( Ct(parsers.TightListItem(parsers.bullet)^1) * Cc(true)
- * parsers.skipblanklines * -parsers.bullet
- + Ct(parsers.LooseListItem(parsers.bullet)^1) * Cc(false)
- * parsers.skipblanklines )
- / writer.bulletlist
+ parsers.BulletListOfType = function(bullet_type)
+ local bullet = parsers.bullet(bullet_type)
+ return ( Ct( parsers.TightListItem(bullet)
+ * ( (parsers.check_minimal_indent / "")
+ * parsers.TightListItem(bullet)
+ )^0
+ )
+ * Cc(true)
+ * -#( (parsers.list_blank^0 / "")
+ * parsers.check_minimal_indent
+ * (bullet - parsers.ThematicBreak)
+ )
+ + Ct( parsers.LooseListItem(bullet)
+ * ( (parsers.list_blank^0 / "")
+ * (parsers.check_minimal_indent / "")
+ * parsers.LooseListItem(bullet)
+ )^0
+ )
+ * Cc(false)
+ ) / writer.bulletlist
+ end
+
+ parsers.BulletList = parsers.BulletListOfType(parsers.dash)
+ + parsers.BulletListOfType(parsers.asterisk)
+ + parsers.BulletListOfType(parsers.plus)
- local function ordered_list(items,tight,startnum)
+ local function ordered_list(items,tight,starter)
+ local startnum = starter[2][1]
if options.startNumber then
startnum = tonumber(startnum) or 1 -- fallback for '#'
if startnum ~= nil then
@@ -25219,14 +29041,23 @@ end
return writer.orderedlist(items,tight,startnum)
end
- parsers.OrderedList = Cg(parsers.enumerator, "listtype") *
- ( Ct(parsers.TightListItem(Cb("listtype"))
- * parsers.TightListItem(parsers.enumerator)^0)
- * Cc(true) * parsers.skipblanklines * -parsers.enumerator
- + Ct(parsers.LooseListItem(Cb("listtype"))
- * parsers.LooseListItem(parsers.enumerator)^0)
- * Cc(false) * parsers.skipblanklines
- ) * Cb("listtype") / ordered_list
+ parsers.OrderedListOfType = function(delimiter_type)
+ local enumerator = parsers.enumerator(delimiter_type)
+ return Cg(enumerator, "listtype")
+ * (Ct( parsers.TightListItem(Cb("listtype"))
+ * ((parsers.check_minimal_indent / "") * parsers.TightListItem(enumerator))^0)
+ * Cc(true)
+ * -#((parsers.list_blank^0 / "")
+ * parsers.check_minimal_indent * enumerator)
+ + Ct( parsers.LooseListItem(Cb("listtype"))
+ * ((parsers.list_blank^0 / "")
+ * (parsers.check_minimal_indent / "") * parsers.LooseListItem(enumerator))^0)
+ * Cc(false)
+ ) * Ct(Cb("listtype")) / ordered_list
+ end
+
+ parsers.OrderedList = parsers.OrderedListOfType(parsers.period)
+ + parsers.OrderedListOfType(parsers.rparent)
% \end{macrocode}
% \par
% \begin{markdown}
@@ -25237,7 +29068,6 @@ end
% \begin{macrocode}
parsers.Blank = parsers.blankline / ""
+ V("Reference")
- + (parsers.tightblocksep / "\n")
% \end{macrocode}
% \par
% \begin{markdown}
@@ -25246,22 +29076,45 @@ end
%
% \end{markdown}
% \begin{macrocode}
+ function parsers.parse_heading_text(s)
+ local inlines = self.parser_functions.parse_inlines(s)
+ local flatten_inlines = self.writer.flatten_inlines
+ self.writer.flatten_inlines = true
+ local flat_text = self.parser_functions.parse_inlines(s)
+ flat_text = util.rope_to_string(flat_text)
+ self.writer.flatten_inlines = flatten_inlines
+ return {flat_text, inlines}
+ end
+
-- parse atx header
- parsers.AtxHeading = Cg(parsers.heading_start, "level")
- * parsers.optionalspace
- * (C(parsers.line)
+ parsers.AtxHeading = parsers.check_trail_no_rem
+ * Cg(parsers.heading_start, "level")
+ * ((C( parsers.optionalspace
+ * parsers.hash^0
+ * parsers.optionalspace
+ * parsers.newline)
+ + parsers.spacechar^1
+ * C(parsers.line))
/ strip_atx_end
- / self.parser_functions.parse_inlines)
+ / parsers.parse_heading_text)
* Cb("level")
/ writer.heading
- parsers.SetextHeading = #(parsers.line * S("=-"))
- * Ct(parsers.linechar^1
- / self.parser_functions.parse_inlines)
- * parsers.newline
- * parsers.heading_level
- * parsers.optionalspace
+ parsers.heading_line = parsers.linechar^1
+ - parsers.thematic_break_lines
+
+ parsers.heading_text = parsers.heading_line
+ * ((V("Endline") / "\n") * (parsers.heading_line - parsers.heading_level))^0
+ * parsers.newline^-1
+
+ parsers.SetextHeading = parsers.freeze_trail * parsers.check_trail_no_rem
+ * #(parsers.heading_text
+ * parsers.check_minimal_indent * parsers.check_trail * parsers.heading_level)
+ * Cs(parsers.heading_text)
+ / parsers.parse_heading_text
+ * parsers.check_minimal_indent_and_trail * parsers.heading_level
* parsers.newline
+ * parsers.unfreeze_trail
/ writer.heading
parsers.Heading = parsers.AtxHeading + parsers.SetextHeading
@@ -25364,12 +29217,42 @@ end
Blocks = V("InitializeState")
* ( V("ExpectedJekyllData")
- * (V("Blank")^0 / writer.interblocksep))^-1
+ * (V("Blank")^0 / writer.interblocksep)
+ )^-1
* V("Blank")^0
- * V("Block")^-1
- * ( V("Blank")^0 / writer.interblocksep
- * V("Block"))^0
- * V("Blank")^0 * parsers.eof,
+% \end{macrocode}
+% \par
+% \begin{markdown}
+%
+% Only create interblock separators between pairs of blocks that are not
+% both paragraphs. Between a pair of paragraphs, any number of blank
+% lines will always produce a paragraph separator.
+%
+% \end{markdown}
+% \begin{macrocode}
+ * ( V("Block")
+ * ( V("Blank")^0 * parsers.eof
+ + ( V("Blank")^2 / writer.paragraphsep
+ + V("Blank")^0 / writer.interblocksep
+ )
+ )
+ + ( V("Paragraph") + V("Plain") )
+ * ( V("Blank")^0 * parsers.eof
+ + ( V("Blank")^2 / writer.paragraphsep
+ + V("Blank")^0 / writer.interblocksep
+ )
+ )
+ * V("Block")
+ * ( V("Blank")^0 * parsers.eof
+ + ( V("Blank")^2 / writer.paragraphsep
+ + V("Blank")^0 / writer.interblocksep
+ )
+ )
+ + ( V("Paragraph") + V("Plain") )
+ * ( V("Blank")^0 * parsers.eof
+ + V("Blank")^0 / writer.paragraphsep
+ )
+ )^0,
ExpectedJekyllData = parsers.fail,
@@ -25381,23 +29264,25 @@ end
ThematicBreak = parsers.ThematicBreak,
BulletList = parsers.BulletList,
OrderedList = parsers.OrderedList,
- Heading = parsers.Heading,
DisplayHtml = parsers.DisplayHtml,
+ Heading = parsers.Heading,
Paragraph = parsers.Paragraph,
Plain = parsers.Plain,
EndlineExceptions = parsers.EndlineExceptions,
- BlockquoteExceptions = parsers.BlockquoteExceptions,
+ NoSoftLineBreakEndlineExceptions
+ = parsers.NoSoftLineBreakEndlineExceptions,
Str = parsers.Str,
Space = parsers.Space,
+ NoSoftLineBreakSpace = parsers.NoSoftLineBreakSpace,
OptionalIndent = parsers.OptionalIndent,
Endline = parsers.Endline,
- UlOrStarLine = parsers.UlOrStarLine,
- Strong = parsers.Strong,
- Emph = parsers.Emph,
- Link = parsers.Link,
- Image = parsers.Image,
+ EndlineNoSub = parsers.EndlineNoSub,
+ NoSoftLineBreakEndline
+ = parsers.NoSoftLineBreakEndline,
+ EndlineBreak = parsers.EndlineBreak,
+ LinkAndEmph = parsers.LinkAndEmph,
Code = parsers.Code,
AutoLinkUrl = parsers.AutoLinkUrl,
AutoLinkEmail = parsers.AutoLinkEmail,
@@ -25500,13 +29385,26 @@ end
% \end{markdown}
% \begin{macrocode}
self.initialize_named_group = function(name, value)
+ local pattern = Ct("")
+ if value ~= nil then
+ pattern = pattern / value
+ end
syntax.InitializeState = syntax.InitializeState
- * Cg(Ct("") / value, name)
+ * Cg(pattern, name)
end
% \end{macrocode}
% \par
% \begin{markdown}
%
+% Add a named group for indentation.
+%
+% \end{markdown}
+% \begin{macrocode}
+ self.initialize_named_group("indent_info")
+% \end{macrocode}
+% \par
+% \begin{markdown}
+%
% Apply syntax extensions.
%
% \end{markdown}
@@ -25578,20 +29476,6 @@ end
% \par
% \begin{markdown}
%
-% Duplicate the `Inline` rule as `IndentedInline` with the right-hand-side
-% terminal symbol `Space` replaced with `OptionalIndent`.
-%
-% \end{markdown}
-% \begin{macrocode}
- walkable_syntax["IndentedInline"] = util.table_copy(
- walkable_syntax["Inline"])
- self.insert_pattern(
- "IndentedInline instead of Space",
- "OptionalIndent")
-% \end{macrocode}
-% \par
-% \begin{markdown}
-%
% Materialize \luamref{walkable_syntax} and merge it into \luamref{syntax} to
% produce the complete \acro{peg} grammar of markdown. Whenever a rule exists
% in both \luamref{walkable_syntax} and \luamref{syntax}, the rule from
@@ -25607,7 +29491,7 @@ end
% \begin{markdown}
%
% Although the interface of the \luamref{reader->insert_pattern} method does
-% document this (see Section <#sec:lua-user-extensions>), we allow the
+% not document this (see Section <#sec:lua-user-extensions>), we allow the
% \luamref{reader->insert_pattern} and \luamref{reader->update_rule}
% methods to insert not just \acro{peg} patterns, but also rule names that
% reference the \acro{peg} grammar of Markdown.
@@ -25667,6 +29551,15 @@ end
syntax.AutoLinkRelativeReference = parsers.fail
end
+ if options.contentLevel == "inline" then
+ syntax[1] = "Inlines"
+ syntax.Inlines = V("InitializeState")
+ * parsers.Inline^0
+ * ( parsers.spacing^0
+ * parsers.eof / "")
+ syntax.Space = parsers.Space + parsers.blankline / writer.space
+ end
+
local blocks_nested_t = util.table_copy(syntax)
blocks_nested_t.ExpectedJekyllData = parsers.fail
parsers.blocks_nested = Ct(blocks_nested_t)
@@ -25681,10 +29574,6 @@ end
* parsers.eof / "")
parsers.inlines = Ct(inlines_t)
- local inlines_no_link_t = util.table_copy(inlines_t)
- inlines_no_link_t.Link = parsers.fail
- parsers.inlines_no_link = Ct(inlines_no_link_t)
-
local inlines_no_inline_note_t = util.table_copy(inlines_t)
inlines_no_inline_note_t.InlineNote = parsers.fail
parsers.inlines_no_inline_note = Ct(inlines_no_inline_note_t)
@@ -25699,6 +29588,11 @@ end
inlines_nbsp_t.Endline = parsers.NonbreakingEndline
inlines_nbsp_t.Space = parsers.NonbreakingSpace
parsers.inlines_nbsp = Ct(inlines_nbsp_t)
+
+ local inlines_no_link_or_emphasis_t = util.table_copy(inlines_t)
+ inlines_no_link_or_emphasis_t.LinkAndEmph = parsers.fail
+ inlines_no_link_or_emphasis_t.EndlineExceptions = parsers.EndlineExceptions - parsers.eof
+ parsers.inlines_no_link_or_emphasis = Ct(inlines_no_link_or_emphasis_t)
% \end{macrocode}
% \par
% \begin{markdown}
@@ -25831,6 +29725,7 @@ M.extensions.bracketed_spans = function()
% \end{markdown}
% \begin{macrocode}
function self.span(s, attr)
+ if self.flatten_inlines then return s end
return {"\\markdownRendererBracketedSpanAttributeContextBegin",
self.attributes(attr),
s,
@@ -25840,13 +29735,25 @@ M.extensions.bracketed_spans = function()
local parsers = self.parsers
local writer = self.writer
- local Span = parsers.between(parsers.Inline,
- parsers.lbracket,
- parsers.rbracket)
+ local span_label = parsers.lbracket
+ * (Cs((parsers.alphanumeric^1
+ + parsers.inticks
+ + parsers.autolink
+ + V("InlineHtml")
+ + ( parsers.backslash * parsers.backslash)
+ + ( parsers.backslash * (parsers.lbracket + parsers.rbracket)
+ + V("Space") + V("Endline")
+ + (parsers.any
+ - (parsers.newline + parsers.lbracket + parsers.rbracket
+ + parsers.blankline^2))))^1)
+ / self.parser_functions.parse_inlines)
+ * parsers.rbracket
+
+ local Span = span_label
* Ct(parsers.attributes)
/ writer.span
- self.insert_pattern("Inline after Emph",
+ self.insert_pattern("Inline before LinkAndEmph",
Span, "Span")
end
}
@@ -25888,11 +29795,24 @@ M.extensions.citations = function(citation_nbsps)
% \end{markdown}
% \begin{macrocode}
function self.citations(text_cites, cites)
- local buffer = {"\\markdownRenderer", text_cites and "TextCite" or "Cite",
- "{", #cites, "}"}
- for _,cite in ipairs(cites) do
- buffer[#buffer+1] = {cite.suppress_author and "-" or "+", "{",
- cite.prenote or "", "}{", cite.postnote or "", "}{", cite.name, "}"}
+ local buffer = {}
+ if self.flatten_inlines then
+ for _,cite in ipairs(cites) do
+ if cite.prenote then
+ table.insert(buffer, {cite.prenote, " "})
+ end
+ table.insert(buffer, cite.name)
+ if cite.postnote then
+ table.insert(buffer, {" ", cite.postnote})
+ end
+ end
+ else
+ table.insert(buffer, {"\\markdownRenderer", text_cites and "TextCite" or "Cite",
+ "{", #cites, "}"})
+ for _,cite in ipairs(cites) do
+ table.insert(buffer, {cite.suppress_author and "-" or "+", "{",
+ cite.prenote or "", "}{", cite.postnote or "", "}{", cite.name, "}"})
+ end
end
return buffer
end
@@ -25919,42 +29839,51 @@ M.extensions.citations = function(citation_nbsps)
= Cs((parsers.alphanumeric^1
+ parsers.bracketed
+ parsers.inticks
+ + parsers.autolink
+ + V("InlineHtml")
+ + V("Space") + V("Endline")
+ (parsers.anyescaped
- - (parsers.rbracket + parsers.blankline^2))
+ - (parsers.newline + parsers.rbracket + parsers.blankline^2))
- (parsers.spnl * parsers.dash^-1 * parsers.at))^0)
local citation_body_postnote
= Cs((parsers.alphanumeric^1
+ parsers.bracketed
+ parsers.inticks
+ + parsers.autolink
+ + V("InlineHtml")
+ + V("Space") + V("Endline")
+ (parsers.anyescaped
- - (parsers.rbracket + parsers.semicolon
+ - (parsers.newline + parsers.rbracket + parsers.semicolon
+ parsers.blankline^2))
- (parsers.spnl * parsers.rbracket))^0)
local citation_body_chunk
= citation_body_prenote
- * parsers.spnl * citation_name
+ * parsers.spnlc * citation_name
* (parsers.internal_punctuation - parsers.semicolon)^-1
- * parsers.spnl * citation_body_postnote
+ * parsers.spnlc * citation_body_postnote
local citation_body
= citation_body_chunk
- * (parsers.semicolon * parsers.spnl
+ * (parsers.semicolon * parsers.spnlc
* citation_body_chunk)^0
local citation_headless_body_postnote
= Cs((parsers.alphanumeric^1
+ parsers.bracketed
+ parsers.inticks
+ + parsers.autolink
+ + V("InlineHtml")
+ + V("Space") + V("Endline")
+ (parsers.anyescaped
- - (parsers.rbracket + parsers.at
+ - (parsers.newline + parsers.rbracket + parsers.at
+ parsers.semicolon + parsers.blankline^2))
- (parsers.spnl * parsers.rbracket))^0)
local citation_headless_body
= citation_headless_body_postnote
- * (parsers.sp * parsers.semicolon * parsers.spnl
+ * (parsers.sp * parsers.semicolon * parsers.spnlc
* citation_body_chunk)^0
local citations
@@ -25983,10 +29912,10 @@ M.extensions.citations = function(citation_nbsps)
end
local TextCitations
- = Ct((parsers.spnl
+ = Ct((parsers.spnlc
* Cc("")
* citation_name
- * ((parsers.spnl
+ * ((parsers.spnlc
* parsers.lbracket
* citation_headless_body
* parsers.rbracket) + Cc("")))^1)
@@ -25995,7 +29924,7 @@ M.extensions.citations = function(citation_nbsps)
end
local ParenthesizedCitations
- = Ct((parsers.spnl
+ = Ct((parsers.spnlc
* parsers.lbracket
* citation_body
* parsers.rbracket)^1)
@@ -26005,7 +29934,7 @@ M.extensions.citations = function(citation_nbsps)
local Citations = TextCitations + ParenthesizedCitations
- self.insert_pattern("Inline after Emph",
+ self.insert_pattern("Inline before LinkAndEmph",
Citations, "Citations")
self.add_special_character("@")
@@ -26018,7 +29947,7 @@ end
%
%#### Content Blocks
%
-% The \luamdef{extensions.content_blocks} function implements the iA\,Writer
+% The \luamdef{extensions.content_blocks} function implements the iA\\,Writer
% content blocks syntax extension. The `language_map` parameter specifies
% the filename of the \acro{JSON} file that maps filename extensions to
% programming language names.
@@ -26068,7 +29997,7 @@ M.extensions.content_blocks = function(language_map)
% \begin{markdown}
%
% Define \luamdef{writer->contentblock} as a function that will transform an
-% input iA\,Writer content block to the output format, where `src`
+% input iA\\,Writer content block to the output format, where `src`
% corresponds to the \acro{uri} prefix, `suf` to the \acro{uri} extension,
% `type` to the type of the content block (`localfile` or `onlineimage`),
% and `tit` to the title of the content block.
@@ -26135,6 +30064,7 @@ M.extensions.content_blocks = function(language_map)
= (parsers.less
* Cs((parsers.anyescaped
- parsers.more
+ - parsers.spacing
- #(parsers.period
* onlineimagesuffix
* parsers.more
@@ -26167,7 +30097,7 @@ M.extensions.content_blocks = function(language_map)
* Cc("localfile")
local ContentBlock
- = parsers.leader
+ = parsers.check_trail_no_rem
* (localfilepath + onlineimageurl)
* contentblock_tail
/ writer.contentblock
@@ -26234,18 +30164,26 @@ M.extensions.definition_lists = function(tight_lists)
local defstartchar = S("~:")
- local defstart = ( defstartchar * #parsers.spacing
+ local defstart = parsers.check_trail_length(0) * defstartchar * #parsers.spacing
* (parsers.tab + parsers.space^-3)
- + parsers.space * defstartchar * #parsers.spacing
+ + parsers.check_trail_length(1) * defstartchar * #parsers.spacing
* (parsers.tab + parsers.space^-2)
- + parsers.space * parsers.space * defstartchar
- * #parsers.spacing
+ + parsers.check_trail_length(2) * defstartchar * #parsers.spacing
* (parsers.tab + parsers.space^-1)
- + parsers.space * parsers.space * parsers.space
- * defstartchar * #parsers.spacing
- )
+ + parsers.check_trail_length(3) * defstartchar * #parsers.spacing
+
+ local indented_line = (parsers.check_minimal_indent / "") * parsers.check_code_trail * parsers.line
- local dlchunk = Cs(parsers.line * (parsers.indentedline - parsers.blankline)^0)
+ local blank = parsers.check_minimal_blank_indent_and_any_trail * parsers.optionalspace * parsers.newline
+
+ local dlchunk = Cs(parsers.line * (indented_line - blank)^0)
+
+ local indented_blocks = function(bl)
+ return Cs( bl
+ * (blank^1 * (parsers.check_minimal_indent / "")
+ * parsers.check_code_trail * -parsers.blankline * bl)^0
+ * (blank^1 + parsers.eof))
+ end
local function definition_list_item(term, defs, _)
return { term = self.parser_functions.parse_inlines(term),
@@ -26253,22 +30191,22 @@ M.extensions.definition_lists = function(tight_lists)
end
local DefinitionListItemLoose
- = C(parsers.line) * parsers.skipblanklines
- * Ct((defstart
- * parsers.indented_blocks(dlchunk)
- / self.parser_functions.parse_blocks_nested)^1)
+ = C(parsers.line) * blank^0
+ * Ct((parsers.check_minimal_indent * (defstart
+ * indented_blocks(dlchunk)
+ / self.parser_functions.parse_blocks_nested))^1)
* Cc(false) / definition_list_item
local DefinitionListItemTight
= C(parsers.line)
- * Ct((defstart * dlchunk
- / self.parser_functions.parse_blocks_nested)^1)
+ * Ct((parsers.check_minimal_indent * (defstart * dlchunk
+ / self.parser_functions.parse_blocks_nested))^1)
* Cc(true) / definition_list_item
local DefinitionList
= ( Ct(DefinitionListItemLoose^1) * Cc(false)
+ Ct(DefinitionListItemTight^1)
- * (parsers.skipblanklines
+ * (blank^0
* -DefinitionListItemLoose * Cc(true))
) / writer.definitionlist
@@ -26322,8 +30260,10 @@ M.extensions.fancy_lists = function()
local buffer = {}
local num = startnum
for _,item in ipairs(items) do
- buffer[#buffer + 1] = self.fancyitem(item,num)
- if num ~= nil then
+ if item ~= "" then
+ buffer[#buffer + 1] = self.fancyitem(item,num)
+ end
+ if num ~= nil and item ~= "" then
num = num + 1
end
end
@@ -26361,52 +30301,101 @@ M.extensions.fancy_lists = function()
local options = self.options
local writer = self.writer
- local label = parsers.dig + parsers.letter
- local numdelim = parsers.period + parsers.rparent
- local enumerator = C(label^3 * numdelim) * #parsers.spacing
- + C(label^2 * numdelim) * #parsers.spacing
- * (parsers.tab + parsers.space^1)
- + C(label * numdelim) * #parsers.spacing
- * (parsers.tab + parsers.space^-2)
- + parsers.space * C(label^2 * numdelim)
- * #parsers.spacing
- + parsers.space * C(label * numdelim)
- * #parsers.spacing
- * (parsers.tab + parsers.space^-1)
- + parsers.space * parsers.space * C(label^1
- * numdelim) * #parsers.spacing
- local starter = parsers.bullet + enumerator
+ local function combine_markers_and_delims(markers, delims)
+ local markers_table = {}
+ for _,marker in ipairs(markers) do
+ local start_marker
+ local continuation_marker
+ if type(marker) == "table" then
+ start_marker = marker[1]
+ continuation_marker = marker[2]
+ else
+ start_marker = marker
+ continuation_marker = marker
+ end
+ for _,delim in ipairs(delims) do
+ table.insert(markers_table, {start_marker, continuation_marker, delim})
+ end
+ end
+ return markers_table
+ end
+
+ local function join_table_with_func(func, markers_table)
+ local pattern = func(table.unpack(markers_table[1]))
+ for i = 2, #markers_table do
+ pattern = pattern + func(table.unpack(markers_table[i]))
+ end
+ return pattern
+ end
- local NestedList = Cs((parsers.optionallyindentedline
- - starter)^1)
- / function(a) return "\001"..a end
+ local lowercase_letter_marker = R("az")
+ local uppercase_letter_marker = R("AZ")
- local ListBlockLine = parsers.optionallyindentedline
- - parsers.blankline - (parsers.indent^-1
- * starter)
+ local roman_marker = function(chars)
+ local m, d, c = P(chars[1]), P(chars[2]), P(chars[3])
+ local l, x, v, i = P(chars[4]), P(chars[5]), P(chars[6]), P(chars[7])
+ return m^-3
+ * (c*m + c*d + d^-1 * c^-3)
+ * (x*c + x*l + l^-1 * x^-3)
+ * (i*x + i*v + v^-1 * i^-3)
+ end
+
+ local lowercase_roman_marker = roman_marker({"m", "d", "c", "l", "x", "v", "i"})
+ local uppercase_roman_marker = roman_marker({"M", "D", "C", "L", "X", "V", "I"})
+
+ local lowercase_opening_roman_marker = P("i")
+ local uppercase_opening_roman_marker = P("I")
+
+ local digit_marker = parsers.dig * parsers.dig^-8
- local ListBlock = parsers.line * ListBlockLine^0
+ local markers = {
+ {lowercase_opening_roman_marker, lowercase_roman_marker},
+ {uppercase_opening_roman_marker, uppercase_roman_marker},
+ lowercase_letter_marker,
+ uppercase_letter_marker,
+ lowercase_roman_marker,
+ uppercase_roman_marker,
+ digit_marker
+ }
+
+ local delims = {
+ parsers.period,
+ parsers.rparent
+ }
- local ListContinuationBlock = parsers.blanklines * (parsers.indent / "")
- * ListBlock
+ local markers_table = combine_markers_and_delims(markers, delims)
+
+ local function enumerator(start_marker, _, delimiter_type, interrupting)
+ local delimiter_range
+ local allowed_end
+ if interrupting then
+ delimiter_range = P("1")
+ allowed_end = C(parsers.spacechar^1) * #parsers.linechar
+ else
+ delimiter_range = start_marker
+ allowed_end = C(parsers.spacechar^1) + #(parsers.newline + parsers.eof)
+ end
+
+ return parsers.check_trail
+ * Ct(C(delimiter_range) * C(delimiter_type))
+ * allowed_end
+ end
+
+ local starter = join_table_with_func(enumerator, markers_table)
local TightListItem = function(starter)
- return -parsers.ThematicBreak
- * (Cs(starter / "" * parsers.tickbox^-1 * ListBlock * NestedList^-1)
- / self.parser_functions.parse_blocks_nested)
- * -(parsers.blanklines * parsers.indent)
+ return parsers.add_indent(starter, "li")
+ * parsers.indented_content_tight
end
local LooseListItem = function(starter)
- return -parsers.ThematicBreak
- * Cs( starter / "" * parsers.tickbox^-1 * ListBlock * Cc("\n")
- * (NestedList + ListContinuationBlock^0)
- * (parsers.blanklines / "\n\n")
- ) / self.parser_functions.parse_blocks_nested
+ return parsers.add_indent(starter, "li")
+ * parsers.indented_content_loose
+ * remove_indent("li")
end
local function roman2number(roman)
- local romans = { ["L"] = 50, ["X"] = 10, ["V"] = 5, ["I"] = 1 }
+ local romans = { ["M"] = 1000, ["D"] = 500, ["C"] = 100, ["L"] = 50, ["X"] = 10, ["V"] = 5, ["I"] = 1 }
local numeral = 0
local i = 1
@@ -26425,8 +30414,7 @@ M.extensions.fancy_lists = function()
return numeral
end
- local function sniffstyle(itemprefix)
- local numstr, delimend = itemprefix:match("^([A-Za-z0-9]*)([.)]*)")
+ local function sniffstyle(numstr, delimend)
local numdelim
if delimend == ")" then
numdelim = "OneParen"
@@ -26435,46 +30423,72 @@ M.extensions.fancy_lists = function()
else
numdelim = "Default"
end
- numstr = numstr or itemprefix
local num
- num = numstr:match("^([IVXL]+)")
+ num = numstr:match("^([I])$")
if num then
return roman2number(num), "UpperRoman", numdelim
end
- num = numstr:match("^([ivxl]+)")
+ num = numstr:match("^([i])$")
if num then
return roman2number(string.upper(num)), "LowerRoman", numdelim
end
- num = numstr:match("^([A-Z])")
+ num = numstr:match("^([A-Z])$")
if num then
return string.byte(num) - string.byte("A") + 1, "UpperAlpha", numdelim
end
- num = numstr:match("^([a-z])")
+ num = numstr:match("^([a-z])$")
if num then
return string.byte(num) - string.byte("a") + 1, "LowerAlpha", numdelim
end
+ num = numstr:match("^([IVXLCDM]+)")
+ if num then
+ return roman2number(num), "UpperRoman", numdelim
+ end
+ num = numstr:match("^([ivxlcdm]+)")
+ if num then
+ return roman2number(string.upper(num)), "LowerRoman", numdelim
+ end
return math.floor(tonumber(numstr) or 1), "Decimal", numdelim
end
local function fancylist(items,tight,start)
- local startnum, numstyle, numdelim = sniffstyle(start)
+ local startnum, numstyle, numdelim = sniffstyle(start[2][1], start[2][2])
return writer.fancylist(items,tight,
- options.startNumber and startnum,
+ options.startNumber and startnum or 1,
numstyle or "Decimal",
numdelim or "Default")
end
- local FancyList = Cg(enumerator, "listtype") *
- ( Ct(TightListItem(Cb("listtype"))
- * TightListItem(enumerator)^0)
- * Cc(true) * parsers.skipblanklines * -enumerator
- + Ct(LooseListItem(Cb("listtype"))
- * LooseListItem(enumerator)^0)
- * Cc(false) * parsers.skipblanklines
- ) * Cb("listtype") / fancylist
+ local FancyListOfType = function(start_marker, continuation_marker, delimiter_type)
+ local enumerator_start = enumerator(start_marker, continuation_marker, delimiter_type)
+ local enumerator_cont = enumerator(continuation_marker, continuation_marker, delimiter_type)
+ return Cg(enumerator_start, "listtype")
+ * (Ct( TightListItem(Cb("listtype"))
+ * ((parsers.check_minimal_indent / "") * TightListItem(enumerator_cont))^0)
+ * Cc(true)
+ * -#((parsers.conditionally_indented_blankline^0 / "")
+ * parsers.check_minimal_indent * enumerator_cont)
+ + Ct( LooseListItem(Cb("listtype"))
+ * ((parsers.conditionally_indented_blankline^0 / "")
+ * (parsers.check_minimal_indent / "") * LooseListItem(enumerator_cont))^0)
+ * Cc(false)
+ ) * Ct(Cb("listtype")) / fancylist
+ end
+
+ local FancyList = join_table_with_func(FancyListOfType, markers_table)
+
+ local Endline = parsers.newline
+ * (parsers.check_minimal_indent
+ * -parsers.EndlineExceptions
+ + parsers.check_optional_indent
+ * -parsers.EndlineExceptions
+ * -starter)
+ * parsers.spacechar^0
+ / writer.soft_line_break
self.update_rule("OrderedList", FancyList)
+ self.update_rule("Endline", Endline)
end
}
end
@@ -26523,7 +30537,7 @@ M.extensions.fenced_code = function(blank_before_code_fence,
end
local name = util.cache_verbatim(options.cacheDir, s)
table.insert(buf, {"\\markdownRendererInputFencedCode{",
- name,"}{",self.string(i),"}"})
+ name,"}{",self.string(i),"}{",self.infostring(i),"}"})
if attr ~= nil then
table.insert(buf, "\\markdownRendererFencedCodeAttributeContextEnd")
end
@@ -26556,18 +30570,52 @@ M.extensions.fenced_code = function(blank_before_code_fence,
return #a >= #b and i
end
- local tilde_infostring
- = C((parsers.linechar
- - (parsers.spacechar^1 * parsers.newline))^0)
+ local function strip_enclosing_whitespaces(str)
+ return str:gsub("^%s*(.-)%s*$", "%1")
+ end
+
+ local tilde_infostring = Cs(Cs((V("HtmlEntity")
+ + parsers.anyescaped
+ - parsers.newline)^0)
+ / strip_enclosing_whitespaces)
- local backtick_infostring
- = C((parsers.linechar
- - (parsers.backtick
- + parsers.spacechar^1 * parsers.newline))^0)
+ local backtick_infostring = Cs(Cs((V("HtmlEntity")
+ + (-#(parsers.backslash * parsers.backtick) * parsers.anyescaped)
+ - parsers.newline
+ - parsers.backtick)^0)
+ / strip_enclosing_whitespaces)
local fenceindent
+
+ local function has_trail(indent_table)
+ return indent_table ~= nil and
+ indent_table.trail ~= nil and
+ next(indent_table.trail) ~= nil
+ end
+
+ local function has_indents(indent_table)
+ return indent_table ~= nil and
+ indent_table.indents ~= nil and
+ next(indent_table.indents) ~= nil
+ end
+
+ local function get_last_indent_name(indent_table)
+ if has_indents(indent_table) then
+ return indent_table.indents[#indent_table.indents].name
+ end
+ end
+
+ local function count_fenced_start_indent(_, _, indent_table, trail)
+ local last_indent_name = get_last_indent_name(indent_table)
+ fenceindent = 0
+ if last_indent_name ~= "li" then
+ fenceindent = #trail
+ end
+ return true
+ end
+
local fencehead = function(char, infostring)
- return C(parsers.nonindentspace) / function(s) fenceindent = #s end
+ return Cmt(Cb("indent_info") * parsers.check_trail, count_fenced_start_indent)
* Cg(char^3, "fencelength")
* parsers.optionalspace
* infostring
@@ -26575,52 +30623,69 @@ M.extensions.fenced_code = function(blank_before_code_fence,
end
local fencetail = function(char)
- return parsers.nonindentspace
+ return parsers.check_trail_no_rem
* Cmt(C(char^3) * Cb("fencelength"), captures_geq_length)
* parsers.optionalspace * (parsers.newline + parsers.eof)
+ parsers.eof
end
- local fencedline = function(char)
- return C(parsers.line - fencetail(char))
- / function(s)
- local i = 1
- local remaining = fenceindent
- while true do
- local c = s:sub(i, i)
- if c == " " and remaining > 0 then
- remaining = remaining - 1
- i = i + 1
- elseif c == "\t" and remaining > 3 then
- remaining = remaining - 4
- i = i + 1
- else
- break
- end
- end
- return s:sub(i)
- end
+ local function process_fenced_line(s, i, indent_table, line_content, is_blank) -- luacheck: ignore s i
+ local remainder = ""
+ if has_trail(indent_table) then
+ remainder = indent_table.trail.internal_remainder
+ end
+
+ if is_blank and get_last_indent_name(indent_table) == "li" then
+ remainder = ""
+ end
+
+ local str = remainder .. line_content
+ local index = 1
+ local remaining = fenceindent
+
+ while true do
+ local c = str:sub(index, index)
+ if c == " " and remaining > 0 then
+ remaining = remaining - 1
+ index = index + 1
+ elseif c == "\t" and remaining > 3 then
+ remaining = remaining - 4
+ index = index + 1
+ else
+ break
+ end
+ end
+
+ return true, str:sub(index)
end
+ local fencedline = function(char)
+ return Cmt(Cb("indent_info") * C(parsers.line - fencetail(char)) * Cc(false), process_fenced_line)
+ end
+
+ local blankfencedline = Cmt(Cb("indent_info") * C(parsers.blankline) * Cc(true), process_fenced_line)
+
local TildeFencedCode
= fencehead(parsers.tilde, tilde_infostring)
- * Cs(fencedline(parsers.tilde)^0)
- * fencetail(parsers.tilde)
+ * Cs(((parsers.check_minimal_blank_indent / "") * blankfencedline
+ + (parsers.check_minimal_indent / "") * fencedline(parsers.tilde))^0)
+ * ((parsers.check_minimal_indent / "") * fencetail(parsers.tilde) + parsers.succeed)
local BacktickFencedCode
= fencehead(parsers.backtick, backtick_infostring)
- * Cs(fencedline(parsers.backtick)^0)
- * fencetail(parsers.backtick)
+ * Cs(((parsers.check_minimal_blank_indent / "") * blankfencedline
+ + (parsers.check_minimal_indent / "") * fencedline(parsers.backtick))^0)
+ * ((parsers.check_minimal_indent / "") * fencetail(parsers.backtick) + parsers.succeed)
- local infostring_with_attributes
- = Ct(C((parsers.linechar
- - ( parsers.optionalspace
- * parsers.attributes))^0)
- * parsers.optionalspace
- * Ct(parsers.attributes))
+ local infostring_with_attributes
+ = Ct(C((parsers.linechar
+ - ( parsers.optionalspace
+ * parsers.attributes))^0)
+ * parsers.optionalspace
+ * Ct(parsers.attributes))
local FencedCode
- = (TildeFencedCode + BacktickFencedCode)
+ = ((TildeFencedCode + BacktickFencedCode)
/ function(infostring, code)
local expanded_code = self.expandtabs(code)
@@ -26641,7 +30706,7 @@ M.extensions.fenced_code = function(blank_before_code_fence,
end
end
return writer.fencedCode(expanded_code, infostring, attr)
- end
+ end)
self.insert_pattern("Block after Verbatim",
FencedCode, "FencedCode")
@@ -26695,7 +30760,7 @@ M.extensions.fenced_divs = function(blank_before_div_fence)
function self.div_begin(attributes)
local start_output = {"\\markdownRendererFencedDivAttributeContextBegin\n",
self.attributes(attributes)}
- local end_output = {"\n\\markdownRendererFencedDivAttributeContextEnd "}
+ local end_output = {"\\markdownRendererFencedDivAttributeContextEnd "}
return self.push_attributes("div", attributes, start_output, end_output)
end
% \end{macrocode}
@@ -26760,6 +30825,31 @@ M.extensions.fenced_divs = function(blank_before_div_fence)
return Cg( Cmt(Cb("div_level"), update_div_level)
, "div_level")
end
+
+ local non_fenced_div_block = parsers.check_minimal_indent * V("Block")
+ - parsers.check_minimal_indent_and_trail * fenced_div_end
+
+ local non_fenced_div_paragraph = parsers.check_minimal_indent * V("Paragraph")
+ - parsers.check_minimal_indent_and_trail * fenced_div_end
+
+ local blank = parsers.minimally_indented_blank
+
+ local block_separated = parsers.block_sep_group(blank)
+ * non_fenced_div_block
+
+ local loop_body_pair = parsers.create_loop_body_pair(block_separated,
+ non_fenced_div_paragraph,
+ parsers.block_sep_group(blank),
+ parsers.par_sep_group(blank))
+
+ local content_loop = ( non_fenced_div_block
+ * loop_body_pair.block^0
+ + non_fenced_div_paragraph
+ * block_separated
+ * loop_body_pair.block^0
+ + non_fenced_div_paragraph
+ * loop_body_pair.par^0)
+ * blank^0
local FencedDiv = fenced_div_begin
/ function (infostring)
@@ -26772,14 +30862,9 @@ M.extensions.fenced_divs = function(blank_before_div_fence)
/ writer.div_begin
* increment_div_level(1)
* parsers.skipblanklines
- * Ct( (V("Block") - fenced_div_end)^-1
- * ( parsers.blanklines
- / function()
- return writer.interblocksep
- end
- * (V("Block") - fenced_div_end))^0)
- * parsers.skipblanklines
- * fenced_div_end * increment_div_level(-1)
+ * Ct(content_loop)
+ * parsers.minimally_indented_blank^0
+ * parsers.check_minimal_indent_and_trail * fenced_div_end * increment_div_level(-1)
* (Cc("") / writer.div_end)
self.insert_pattern("Block after Verbatim",
@@ -26791,8 +30876,9 @@ M.extensions.fenced_divs = function(blank_before_div_fence)
% \par
% \begin{markdown}
%
-% Patch blockquotes, so that they allow the end of a fenced div immediately
-% afterwards.
+% If the `blank_before_div_fence` parameter is `false`, we will have the
+% closing div at the beginning of a line break the current paragraph if
+% we are currently nested in a div.
%
% \end{markdown}
% \begin{macrocode}
@@ -26804,23 +30890,6 @@ M.extensions.fenced_divs = function(blank_before_div_fence)
local is_inside_div = Cmt(Cb("div_level"), check_div_level)
local fencestart = is_inside_div * fenced_div_end
- self.update_rule("BlockquoteExceptions", function(previous_pattern)
- if previous_pattern == nil then
- previous_pattern = parsers.BlockquoteExceptions
- end
- return previous_pattern + fencestart
- end)
-
-% \end{macrocode}
-% \par
-% \begin{markdown}
-%
-% If the `blank_before_div_fence` parameter is `false`, we will have the
-% closing div at the beginning of a line break the current paragraph if
-% we are currently nested in a div.
-%
-% \end{markdown}
-% \begin{macrocode}
if not blank_before_div_fence then
self.update_rule("EndlineExceptions", function(previous_pattern)
if previous_pattern == nil then
@@ -26850,47 +30919,58 @@ M.extensions.header_attributes = function()
local parsers = self.parsers
local writer = self.writer
+ local function strip_atx_end(s)
+ return s:gsub("%s+#*%s*$","")
+ end
+
local AtxHeading = Cg(parsers.heading_start, "level")
* parsers.optionalspace
* (C(((parsers.linechar
- - ((parsers.hash^1
+ - (parsers.attributes
* parsers.optionalspace
- * parsers.attributes^-1
- + parsers.attributes)
- * parsers.optionalspace
- * parsers.newline))
- * (parsers.linechar
- - parsers.hash
- - parsers.lbrace)^0)^1)
- / self.parser_functions.parse_inlines)
+ * parsers.newline))
+ * (parsers.linechar
+ - parsers.lbrace)^0)^1)
+ / strip_atx_end
+ / parsers.parse_heading_text)
* Cg(Ct(parsers.newline
- + (parsers.hash^1
+ + (parsers.attributes
* parsers.optionalspace
- * parsers.attributes^-1
- + parsers.attributes)
- * parsers.optionalspace
- * parsers.newline), "attributes")
+ * parsers.newline)), "attributes")
* Cb("level")
* Cb("attributes")
/ writer.heading
- local SetextHeading = #(parsers.line * S("=-"))
- * (C(((parsers.linechar
- - (parsers.attributes
- * parsers.optionalspace
- * parsers.newline))
- * (parsers.linechar
- - parsers.lbrace)^0)^1)
- / self.parser_functions.parse_inlines)
- * Cg(Ct(parsers.newline
- + (parsers.attributes
- * parsers.optionalspace
- * parsers.newline)), "attributes")
- * parsers.heading_level
- * Cb("attributes")
- * parsers.optionalspace
- * parsers.newline
- / writer.heading
+ local function strip_trailing_spaces(s)
+ return s:gsub("%s*$","")
+ end
+
+ local heading_line = (parsers.linechar
+ - (parsers.attributes
+ * parsers.optionalspace
+ * parsers.newline))^1
+ - parsers.thematic_break_lines
+
+ local heading_text = heading_line
+ * ((V("Endline") / "\n") * (heading_line - parsers.heading_level))^0
+ * parsers.newline^-1
+
+ local SetextHeading = parsers.freeze_trail * parsers.check_trail_no_rem
+ * #(heading_text
+ * (parsers.attributes
+ * parsers.optionalspace
+ * parsers.newline)^-1
+ * parsers.check_minimal_indent * parsers.check_trail * parsers.heading_level)
+ * Cs(heading_text) / strip_trailing_spaces
+ / parsers.parse_heading_text
+ * Cg(Ct((parsers.attributes
+ * parsers.optionalspace
+ * parsers.newline)^-1), "attributes")
+ * parsers.check_minimal_indent_and_trail * parsers.heading_level
+ * Cb("attributes")
+ * parsers.newline
+ * parsers.unfreeze_trail
+ / writer.heading
local Heading = AtxHeading + SetextHeading
self.update_rule("Heading", Heading)
@@ -26985,6 +31065,48 @@ end
% \end{macrocode}
% \begin{markdown}
%
+%#### Marked text
+%
+% The \luamdef{extensions.mark} function implements the Pandoc mark syntax
+% extension.
+%
+% \end{markdown}
+% \begin{macrocode}
+M.extensions.mark = function()
+ return {
+ name = "built-in mark syntax extension",
+ extend_writer = function(self)
+% \end{macrocode}
+% \par
+% \begin{markdown}
+%
+% Define \luamdef{writer->mark} as a function that will transform an input
+% marked text `s` to the output format.
+%
+% \end{markdown}
+% \begin{macrocode}
+ function self.mark(s)
+ if self.flatten_inlines then return s end
+ return {"\\markdownRendererMark{", s, "}"}
+ end
+ end, extend_reader = function(self)
+ local parsers = self.parsers
+ local writer = self.writer
+
+ local doubleequals = P("==")
+
+ local Mark = parsers.between(V("Inline"), doubleequals, doubleequals)
+ / function (inlines) return writer.mark(inlines) end
+
+ self.add_special_character("=")
+ self.insert_pattern("Inline before LinkAndEmph",
+ Mark, "Mark")
+ end
+ }
+end
+% \end{macrocode}
+% \begin{markdown}
+%
%#### Link Attributes
%
% The \luamdef{extensions.link_attributes} function implements the Pandoc
@@ -26998,7 +31120,6 @@ M.extensions.link_attributes = function()
extend_writer = function()
end, extend_reader = function(self)
local parsers = self.parsers
- local writer = self.writer
local options = self.options
% \end{macrocode}
@@ -27008,13 +31129,15 @@ M.extensions.link_attributes = function()
%
% \end{markdown}
% \begin{macrocode}
-
- local define_reference_parser = parsers.define_reference_parser
- * ( parsers.spnl
- * Ct(parsers.attributes))^-1
+ local define_reference_parser = (parsers.check_trail / "") * parsers.link_label * parsers.colon
+ * parsers.spnlc * parsers.url
+ * ( parsers.spnlc_sep * parsers.title * (parsers.spnlc * Ct(parsers.attributes))
+ * parsers.only_blank
+ + parsers.spnlc_sep * parsers.title * parsers.only_blank
+ + Cc("") * (parsers.spnlc * Ct(parsers.attributes)) * parsers.only_blank
+ + Cc("") * parsers.only_blank)
local ReferenceWithAttributes = define_reference_parser
- * parsers.blankline^1
/ self.register_link
self.update_rule("Reference", ReferenceWithAttributes)
@@ -27027,93 +31150,10 @@ M.extensions.link_attributes = function()
% \end{markdown}
% \begin{macrocode}
- local function indirect_link(label, sps, tag,
- attribute_text,
- attributes)
- return writer.defer_call(function()
- local r, fallback = self.lookup_reference(label, sps, tag,
- attributes)
- if r then
- return writer.link(
- self.parser_functions.parse_inlines_no_link(label),
- r.url, r.title, r.attributes)
- else
- local buf = {fallback}
- if attributes then
- table.insert(buf, writer.string(attribute_text))
- end
- return buf
- end
- end)
- end
-
- local DirectLinkWithAttributes = parsers.direct_link
- * (Ct(parsers.attributes))^-1
- / writer.link
-
- local IndirectLinkWithAttributes = parsers.indirect_link
- * (C(Ct(parsers.attributes)))^-1
- / indirect_link
+ local LinkWithAttributesAndEmph = Ct(parsers.link_and_emph_table * Cg(Cc(true), "match_link_attributes"))
+ / self.defer_link_and_emphasis_processing
- local LinkWithAttributes = DirectLinkWithAttributes
- + IndirectLinkWithAttributes
-
-% \end{macrocode}
-% \begin{markdown}
-%
-% Here, we directly update the `Link` grammar rule to keep the
-% method \luamref{reader->parser_functions.parse_inlines_no_link}
-% aware of `LinkWithAttributes` and prevent nested links.
-%
-% If we used \luamref{reader->insert_pattern} instead of
-% \luamref{reader->update_rule}, this correspondence would have
-% been lost and link text would be able to contain nested links.
-%
-% \end{markdown}
-% \begin{macrocode}
- self.update_rule("Link", LinkWithAttributes)
-
-% \end{macrocode}
-% \begin{markdown}
-%
-% The following patterns define direct and indirect images with attributes.
-%
-% \end{markdown}
-% \begin{macrocode}
-
- local function indirect_image(label, sps, tag,
- attribute_text,
- attributes)
- return writer.defer_call(function()
- local r, fallback = self.lookup_reference(label, sps, tag,
- attributes)
- if r then
- return writer.image(writer.string(label),
- r.url, r.title, r.attributes)
- else
- local buf = {"!", fallback}
- if attributes then
- table.insert(buf, writer.string(attribute_text))
- end
- return buf
- end
- end)
- end
-
- local DirectImageWithAttributes = parsers.direct_image
- * Ct(parsers.attributes)
- / writer.image
-
- local IndirectImageWithAttributes = parsers.indirect_image
- * C(Ct(parsers.attributes))
- / indirect_image
-
- local ImageWithAttributes = DirectImageWithAttributes
- + IndirectImageWithAttributes
-
- self.insert_pattern("Inline before Image",
- ImageWithAttributes,
- "ImageWithAttributes")
+ self.update_rule("LinkAndEmph", LinkWithAttributesAndEmph)
% \end{macrocode}
% \begin{markdown}
@@ -27122,7 +31162,6 @@ M.extensions.link_attributes = function()
%
% \end{markdown}
% \begin{macrocode}
-
local AutoLinkUrlWithAttributes
= parsers.auto_link_url
* Ct(parsers.attributes)
@@ -27186,6 +31225,7 @@ M.extensions.notes = function(notes, inline_notes)
% \end{markdown}
% \begin{macrocode}
function self.note(s)
+ if self.flatten_inlines then return "" end
return {"\\markdownRendererNote{",s,"}"}
end
end, extend_reader = function(self)
@@ -27195,10 +31235,10 @@ M.extensions.notes = function(notes, inline_notes)
if inline_notes then
local InlineNote
= parsers.circumflex
- * (parsers.tag / self.parser_functions.parse_inlines_no_inline_note)
+ * (parsers.link_label / self.parser_functions.parse_inlines_no_inline_note)
/ writer.note
- self.insert_pattern("Inline after Emph",
+ self.insert_pattern("Inline after LinkAndEmph",
InlineNote, "InlineNote")
end
if notes then
@@ -27208,7 +31248,7 @@ M.extensions.notes = function(notes, inline_notes)
local RawNoteRef
= #(parsers.lbracket * parsers.circumflex)
- * parsers.tag / strip_first_char
+ * parsers.link_label / strip_first_char
local rawnotes = {}
@@ -27227,21 +31267,36 @@ M.extensions.notes = function(notes, inline_notes)
end
local function register_note(ref,rawnote)
- rawnotes[self.normalize_tag(ref)] = rawnote
+ local normalized_tag = self.normalize_tag(ref)
+ if rawnotes[normalized_tag] == nil then
+ rawnotes[normalized_tag] = rawnote
+ end
return ""
end
local NoteRef = RawNoteRef / lookup_note
+ local optionally_indented_line = parsers.check_optional_indent_and_any_trail * parsers.line
+
+ local blank = parsers.check_optional_blank_indent_and_any_trail * parsers.optionalspace * parsers.newline
+
+ local chunk = Cs(parsers.line * (optionally_indented_line - blank)^0)
+
+ local indented_blocks = function(bl)
+ return Cs( bl
+ * (blank^1 * (parsers.check_optional_indent / "")
+ * parsers.check_code_trail * -parsers.blankline * bl)^0)
+ end
+
local NoteBlock
- = parsers.leader * RawNoteRef * parsers.colon
- * parsers.spnl * parsers.indented_blocks(parsers.chunk)
+ = parsers.check_trail_no_rem * RawNoteRef * parsers.colon
+ * parsers.spnlc * indented_blocks(chunk)
/ register_note
- local Blank = NoteBlock + parsers.Blank
- self.update_rule("Blank", Blank)
+ local Reference = NoteBlock + parsers.Reference
- self.insert_pattern("Inline after Emph",
+ self.update_rule("Reference", Reference)
+ self.insert_pattern("Inline before LinkAndEmph",
NoteRef, "NoteRef")
end
@@ -27257,11 +31312,13 @@ end
% The \luamdef{extensions.pipe_table} function implements the \acro{PHP}
% Markdown table syntax extension (also known as pipe tables in Pandoc). When
% the `table_captions` parameter is `true`, the function also implements the
-% Pandoc table caption syntax extension for table captions.
+% Pandoc table caption syntax extension for table captions. When the
+% `table_attributes` parameter is also `true`, the function also
+% allows attributes to be attached to the (possibly empty) table captions.
%
% \end{markdown}
% \begin{macrocode}
-M.extensions.pipe_tables = function(table_captions)
+M.extensions.pipe_tables = function(table_captions, table_attributes)
local function make_pipe_table_rectangular(rows)
local num_columns = #rows[2]
@@ -27329,10 +31386,18 @@ M.extensions.pipe_tables = function(table_captions)
%
% \end{markdown}
% \begin{macrocode}
- function self.table(rows, caption)
+ function self.table(rows, caption, attributes)
if not self.is_writing then return "" end
- local buffer = {"\\markdownRendererTable{",
- caption or "", "}{", #rows - 1, "}{", #rows[1], "}"}
+ local buffer = {}
+ if attributes ~= nil then
+ table.insert(buffer,
+ "\\markdownRendererTableAttributeContextBegin\n")
+ table.insert(buffer, self.attributes(attributes))
+ end
+ table.insert(buffer,
+ {"\\markdownRendererTable{",
+ caption or "", "}{", #rows - 1, "}{",
+ #rows[1], "}"})
local temp = rows[2] -- put alignments on the first row
rows[2] = rows[1]
rows[1] = temp
@@ -27349,6 +31414,10 @@ M.extensions.pipe_tables = function(table_captions)
end
table.insert(buffer, "}")
end
+ if attributes ~= nil then
+ table.insert(buffer,
+ "\\markdownRendererTableAttributeContextEnd{}")
+ end
return buffer
end
end, extend_reader = function(self)
@@ -27378,31 +31447,65 @@ M.extensions.pipe_tables = function(table_captions)
, table_hline_separator
, table_hline_column)
- local table_caption_beginning = parsers.skipblanklines
- * parsers.nonindentspace
+ local table_caption_beginning = (parsers.check_minimal_blank_indent_and_any_trail_no_rem
+ * parsers.optionalspace * parsers.newline)^0
+ * parsers.check_minimal_indent_and_trail
* (P("Table")^-1 * parsers.colon)
* parsers.optionalspace
+ local function strip_trailing_spaces(s)
+ return s:gsub("%s*$","")
+ end
+
local table_row = pipe_table_row(true
, (C((parsers.linechar - parsers.pipe)^1)
+ / strip_trailing_spaces
/ self.parser_functions.parse_inlines)
, parsers.pipe
, (C((parsers.linechar - parsers.pipe)^0)
+ / strip_trailing_spaces
/ self.parser_functions.parse_inlines))
local table_caption
if table_captions then
table_caption = #table_caption_beginning
* table_caption_beginning
- * Ct(parsers.IndentedInline^1)
- * parsers.newline
+ if table_attributes then
+ table_caption = table_caption
+ * (C(((( parsers.linechar
+ - (parsers.attributes
+ * parsers.optionalspace
+ * parsers.newline
+ * -#( parsers.optionalspace
+ * parsers.linechar)))
+ + ( parsers.newline
+ * #( parsers.optionalspace
+ * parsers.linechar)
+ * C(parsers.optionalspace) / writer.space))
+ * (parsers.linechar
+ - parsers.lbrace)^0)^1)
+ / self.parser_functions.parse_inlines)
+ * (parsers.newline
+ + ( Ct(parsers.attributes)
+ * parsers.optionalspace
+ * parsers.newline))
+ else
+ table_caption = table_caption
+ * C(( parsers.linechar
+ + ( parsers.newline
+ * #( parsers.optionalspace
+ * parsers.linechar)
+ * C(parsers.optionalspace) / writer.space))^1)
+ / self.parser_functions.parse_inlines
+ * parsers.newline
+ end
else
table_caption = parsers.fail
end
- local PipeTable = Ct(table_row * parsers.newline
- * table_hline
- * (parsers.newline * table_row)^0)
+ local PipeTable = Ct(table_row * parsers.newline * (parsers.check_minimal_indent_and_trail / {})
+ * table_hline * parsers.newline
+ * ((parsers.check_minimal_indent / {}) * table_row * parsers.newline)^0)
/ make_pipe_table_rectangular
* table_caption^-1
/ writer.table
@@ -27439,6 +31542,7 @@ M.extensions.raw_inline = function()
% \begin{macrocode}
function self.rawInline(s, attr)
if not self.is_writing then return "" end
+ if self.flatten_inlines then return s end
local name = util.cache_verbatim(options.cacheDir, s)
return {"\\markdownRendererInputRawInline{",
name,"}{", self.string(attr),"}"}
@@ -27479,6 +31583,7 @@ M.extensions.strike_through = function()
% \end{markdown}
% \begin{macrocode}
function self.strike_through(s)
+ if self.flatten_inlines then return s end
return {"\\markdownRendererStrikeThrough{",s,"}"}
end
end, extend_reader = function(self)
@@ -27490,7 +31595,7 @@ M.extensions.strike_through = function()
parsers.doubletildes)
) / writer.strike_through
- self.insert_pattern("Inline after Emph",
+ self.insert_pattern("Inline after LinkAndEmph",
StrikeThrough, "StrikeThrough")
self.add_special_character("~")
@@ -27521,6 +31626,7 @@ M.extensions.subscripts = function()
% \end{markdown}
% \begin{macrocode}
function self.subscript(s)
+ if self.flatten_inlines then return s end
return {"\\markdownRendererSubscript{",s,"}"}
end
end, extend_reader = function(self)
@@ -27531,7 +31637,7 @@ M.extensions.subscripts = function()
parsers.between(parsers.Str, parsers.tilde, parsers.tilde)
) / writer.subscript
- self.insert_pattern("Inline after Emph",
+ self.insert_pattern("Inline after LinkAndEmph",
Subscript, "Subscript")
self.add_special_character("~")
@@ -27562,6 +31668,7 @@ M.extensions.superscripts = function()
% \end{markdown}
% \begin{macrocode}
function self.superscript(s)
+ if self.flatten_inlines then return s end
return {"\\markdownRendererSuperscript{",s,"}"}
end
end, extend_reader = function(self)
@@ -27572,7 +31679,7 @@ M.extensions.superscripts = function()
parsers.between(parsers.Str, parsers.circumflex, parsers.circumflex)
) / writer.superscript
- self.insert_pattern("Inline after Emph",
+ self.insert_pattern("Inline after LinkAndEmph",
Superscript, "Superscript")
self.add_special_character("^")
@@ -27582,7 +31689,7 @@ end
% \end{macrocode}
% \begin{markdown}
%
-%#### Tex Math
+%#### \TeX{} Math
%
% The \luamdef{extensions.tex_math} function implements the Pandoc math
% syntax extensions.
@@ -27605,7 +31712,7 @@ M.extensions.tex_math = function(tex_math_dollars,
% \end{markdown}
% \begin{macrocode}
function self.display_math(s)
- if not self.is_writing then return "" end
+ if self.flatten_inlines then return s end
return {"\\markdownRendererDisplayMath{",self.math(s),"}"}
end
% \end{macrocode}
@@ -27617,7 +31724,7 @@ M.extensions.tex_math = function(tex_math_dollars,
% \end{markdown}
% \begin{macrocode}
function self.inline_math(s)
- if not self.is_writing then return "" end
+ if self.flatten_inlines then return s end
return {"\\markdownRendererInlineMath{",self.math(s),"}"}
end
end, extend_reader = function(self)
@@ -27625,11 +31732,19 @@ M.extensions.tex_math = function(tex_math_dollars,
local writer = self.writer
local function between(p, starter, ender)
- return (starter * C(p * (p - ender)^0) * ender)
+ return (starter * Cs(p * (p - ender)^0) * ender)
+ end
+
+ local function strip_preceding_whitespaces(str)
+ return str:gsub("^%s*(.-)$", "%1")
end
local allowed_before_closing = B( parsers.backslash * parsers.any
- + parsers.any * (parsers.nonspacechar - parsers.backslash))
+ + parsers.any * (parsers.any - parsers.backslash))
+
+ local allowed_before_closing_no_space = B( parsers.backslash * parsers.any
+ + parsers.any * (parsers.nonspacechar - parsers.backslash))
+
% \end{macrocode}
% \begin{markdown}
%
@@ -27637,19 +31752,20 @@ M.extensions.tex_math = function(tex_math_dollars,
%
% \end{markdown}
% \begin{macrocode}
- local dollar_math_content = parsers.backslash^-1
- * parsers.any
+ local dollar_math_content = (parsers.newline * (parsers.check_optional_indent / "")
+ + parsers.backslash^-1
+ * parsers.linechar)
- parsers.blankline^2
- parsers.dollar
local inline_math_opening_dollars = parsers.dollar
* #(parsers.nonspacechar)
- local inline_math_closing_dollars = allowed_before_closing
+ local inline_math_closing_dollars = allowed_before_closing_no_space
* parsers.dollar
* -#(parsers.digit)
- local inline_math_dollars = between(C( dollar_math_content),
+ local inline_math_dollars = between(Cs( dollar_math_content),
inline_math_opening_dollars,
inline_math_closing_dollars)
@@ -27659,7 +31775,7 @@ M.extensions.tex_math = function(tex_math_dollars,
local display_math_closing_dollars = parsers.dollar
* parsers.dollar
- local display_math_dollars = between(C( dollar_math_content),
+ local display_math_dollars = between(Cs( dollar_math_content),
display_math_opening_dollars,
display_math_closing_dollars)
% \end{macrocode}
@@ -27670,7 +31786,8 @@ M.extensions.tex_math = function(tex_math_dollars,
%
% \end{markdown}
% \begin{macrocode}
- local backslash_math_content = parsers.any
+ local backslash_math_content = (parsers.newline * (parsers.check_optional_indent / "")
+ + parsers.linechar)
- parsers.blankline^2
% \end{macrocode}
% \begin{markdown}
@@ -27683,29 +31800,32 @@ M.extensions.tex_math = function(tex_math_dollars,
local inline_math_opening_double = parsers.backslash
* parsers.backslash
* parsers.lparent
- * #(parsers.nonspacechar)
local inline_math_closing_double = allowed_before_closing
+ * parsers.spacechar^0
* parsers.backslash
* parsers.backslash
* parsers.rparent
- local inline_math_double = between(C( backslash_math_content),
+ local inline_math_double = between(Cs( backslash_math_content),
inline_math_opening_double,
inline_math_closing_double)
+ / strip_preceding_whitespaces
local display_math_opening_double = parsers.backslash
* parsers.backslash
* parsers.lbracket
local display_math_closing_double = allowed_before_closing
+ * parsers.spacechar^0
* parsers.backslash
* parsers.backslash
* parsers.rbracket
- local display_math_double = between(C( backslash_math_content),
+ local display_math_double = between(Cs( backslash_math_content),
display_math_opening_double,
display_math_closing_double)
+ / strip_preceding_whitespaces
% \end{macrocode}
% \begin{markdown}
%
@@ -27716,26 +31836,29 @@ M.extensions.tex_math = function(tex_math_dollars,
% \begin{macrocode}
local inline_math_opening_single = parsers.backslash
* parsers.lparent
- * #(parsers.nonspacechar)
local inline_math_closing_single = allowed_before_closing
+ * parsers.spacechar^0
* parsers.backslash
* parsers.rparent
- local inline_math_single = between(C( backslash_math_content),
+ local inline_math_single = between(Cs( backslash_math_content),
inline_math_opening_single,
inline_math_closing_single)
+ / strip_preceding_whitespaces
local display_math_opening_single = parsers.backslash
* parsers.lbracket
local display_math_closing_single = allowed_before_closing
+ * parsers.spacechar^0
* parsers.backslash
* parsers.rbracket
- local display_math_single = between(C( backslash_math_content),
+ local display_math_single = between(Cs( backslash_math_content),
display_math_opening_single,
display_math_closing_single)
+ / strip_preceding_whitespaces
local display_math = parsers.fail
@@ -27759,7 +31882,7 @@ M.extensions.tex_math = function(tex_math_dollars,
local TexMath = display_math / writer.display_math
+ inline_math / writer.inline_math
- self.insert_pattern("Inline after Emph",
+ self.insert_pattern("Inline after LinkAndEmph",
TexMath, "TexMath")
if tex_math_dollars then
@@ -27961,6 +32084,30 @@ function M.new(options)
% \par
% \begin{markdown}
%
+% If the singleton cache contains a conversion function for the same `options`,
+% reuse it.
+%
+% \end{markdown}
+% \begin{macrocode}
+ if options.singletonCache and singletonCache.convert then
+ for k, v in pairs(defaultOptions) do
+ if type(v) == "table" then
+ for i = 1, math.max(#singletonCache.options[k], #options[k]) do
+ if singletonCache.options[k][i] ~= options[k][i] then
+ goto miss
+ end
+ end
+ elseif singletonCache.options[k] ~= options[k] then
+ goto miss
+ end
+ end
+ return singletonCache.convert
+ end
+ ::miss::
+% \end{macrocode}
+% \par
+% \begin{markdown}
+%
% Apply built-in syntax extensions based on `options`.
%
% \end{markdown}
@@ -28026,9 +32173,14 @@ function M.new(options)
table.insert(extensions, line_block_extension)
end
+ if options.mark then
+ local mark_extension = M.extensions.mark()
+ table.insert(extensions, mark_extension)
+ end
+
if options.pipeTables then
local pipe_tables_extension = M.extensions.pipe_tables(
- options.tableCaptions)
+ options.tableCaptions, options.tableAttributes)
table.insert(extensions, pipe_tables_extension)
end
@@ -28062,19 +32214,9 @@ function M.new(options)
table.insert(extensions, tex_math_extension)
end
-% \end{macrocode}
-% \begin{markdown}
-%
-% The footnotes and inlineFootnotes option has been deprecated and will be
-% removed in Markdown 3.0.0.
-%
-% \end{markdown}
-% \begin{macrocode}
- if options.footnotes or options.inlineFootnotes or
- options.notes or options.inlineNotes then
+ if options.notes or options.inlineNotes then
local notes_extension = M.extensions.notes(
- options.footnotes or options.notes,
- options.inlineFootnotes or options.inlineNotes)
+ options.notes, options.inlineNotes)
table.insert(extensions, notes_extension)
end
@@ -28187,14 +32329,51 @@ function M.new(options)
% \par
% \begin{markdown}
%
-% Produce and return a conversion function from markdown to plain \TeX.
+% Produce a conversion function from markdown to plain \TeX.
%
% \end{markdown}
% \begin{macrocode}
local writer = M.writer.new(options)
local reader = M.reader.new(writer, options)
local convert = reader.finalize_grammar(extensions)
-
+% \end{macrocode}
+% \par
+% \begin{markdown}
+%
+% Force garbage collection to reclaim memory for temporary
+% objects created in \luamref{writer.new}, \luamref{reader.new},
+% and \luamref{reader->finalize_grammar}.
+%
+% \end{markdown}
+% \begin{macrocode}
+ collectgarbage("collect")
+% \end{macrocode}
+% \par
+% \begin{markdown}
+%
+% Update the singleton cache.
+%
+% \end{markdown}
+% \begin{macrocode}
+ if options.singletonCache then
+ local singletonCacheOptions = {}
+ for k, v in pairs(options) do
+ singletonCacheOptions[k] = v
+ end
+ setmetatable(singletonCacheOptions,
+ { __index = function (_, key)
+ return defaultOptions[key] end })
+ singletonCache.options = singletonCacheOptions
+ singletonCache.convert = convert
+ end
+% \end{macrocode}
+% \par
+% \begin{markdown}
+%
+% Return the conversion function from markdown to plain \TeX.
+%
+% \end{markdown}
+% \begin{macrocode}
return convert
end
@@ -28246,9 +32425,8 @@ local kpse
(function()
local should_initialize = package.loaded.kpse == nil
or tex.initialize ~= nil
- local ran_ok
- ran_ok, kpse = pcall(require, "kpse")
- if ran_ok and should_initialize then
+ kpse = require("kpse")
+ if should_initialize then
kpse.set_program_name("luatex")
end
end)()
@@ -28276,6 +32454,14 @@ else
assert(io.write(output))
end
% \end{macrocode}
+% \begin{markdown}
+% Remove the `options.cacheDir` directory if it is empty.
+% \end{markdown}
+% \begin{macrocode}
+if options.cacheDir then
+ lfs.rmdir(options["cacheDir"])
+end
+% \end{macrocode}
% \iffalse
%</lua-cli>
%<*tex>
@@ -28295,23 +32481,218 @@ end
%
% \end{markdown}
% \begin{macrocode}
-\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
+\ExplSyntaxOn
+\cs_if_free:NT
+ \markdownInfo
+ {
+ \cs_new:Npn
+ \markdownInfo #1
+ {
+ \msg_info:nne
+ { markdown }
+ { generic-message }
+ { #1 }
+ }
+ }
+\cs_if_free:NT
+ \markdownWarning
+ {
+ \cs_new:Npn
+ \markdownWarning #1
+ {
+ \msg_warning:nne
+ { markdown }
+ { generic-message }
+ { #1 }
+ }
+ }
+\cs_if_free:NT
+ \markdownError
+ {
+ \cs_new:Npn
+ \markdownError #1 #2
+ {
+ \msg_error:nnee
+ { markdown }
+ { generic-message-with-help-text }
+ { #1 }
+ { #2 }
+ }
+ }
+\msg_new:nnn
+ { markdown }
+ { generic-message }
+ { #1 }
+\msg_new:nnnn
+ { markdown }
+ { generic-message-with-help-text }
+ { #1 }
+ { #2 }
+\cs_generate_variant:Nn
+ \msg_info:nnn
+ { nne }
+\cs_generate_variant:Nn
+ \msg_warning:nnn
+ { nne }
+\cs_generate_variant:Nn
+ \msg_error:nnnn
+ { nnee }
+\ExplSyntaxOff
+% \end{macrocode}
+% \par
+% \begin{markdown}
+%
+%### Themes {#themes-implementation}
+%
+% This section implements the theme-loading mechanism and the built-in themes
+% provided with the Markdown package. Futhermore, this section also implements
+% the built-in plain \TeX{} themes provided with the Markdown package.
+%
+% \end{markdown}
+% \begin{macrocode}
+\ExplSyntaxOn
+\prop_new:N \g_@@_plain_tex_loaded_themes_linenos_prop
+\cs_new:Nn
+ \@@_plain_tex_load_theme:nn
+ {
+ \prop_get:NnNTF
+ \g_@@_plain_tex_loaded_themes_linenos_prop
+ { #1 }
+ \l_tmpa_tl
+ {
+ \msg_warning:nnnV
+ { markdown }
+ { repeatedly-loaded-plain-tex-theme }
+ { #1 }
+ \l_tmpa_tl
+ }
+ {
+ \msg_info:nnn
+ { markdown }
+ { loading-plain-tex-theme }
+ { #1 }
+ \prop_gput:Nnx
+ \g_@@_plain_tex_loaded_themes_linenos_prop
+ { #1 }
+ { \tex_the:D \tex_inputlineno:D }
+ \file_input:n
+ { markdown theme #2 }
+ }
+ }
+\msg_new:nnn
+ { markdown }
+ { loading-plain-tex-theme }
+ { Loading~plain~TeX~Markdown~theme~#1 }
+\msg_new:nnn
+ { markdown }
+ { repeatedly-loaded-plain-tex-theme }
+ {
+ Plain~TeX~Markdown~theme~#1~was~previously~
+ loaded~on~line~#2,~not~loading~it~again
+ }
+\cs_generate_variant:Nn
+ \prop_gput:Nnn
+ { Nnx }
+\cs_gset_eq:NN
+ \@@_load_theme:nn
+ \@@_plain_tex_load_theme:nn
+\cs_generate_variant:Nn
+ \@@_load_theme:nn
+ { nV }
+% \end{macrocode}
+% \begin{markdown}
+%
+% Developers can use the \mref{markdownLoadPlainTeXTheme} macro to load a
+% corresponding plain \TeX{} theme from within themes for higher-level \TeX{}
+% formats such as \LaTeX{} and \Hologo{ConTeXt}.
+%
+% \end{markdown}
+% \begin{macrocode}
+\cs_new:Npn
+ \markdownLoadPlainTeXTheme
+ {
+% \end{macrocode}
+% \begin{markdown}
+%
+% First, we extract the name of the current theme from the
+% \mref{g_@@_current_theme_tl} macro.
+%
+% \end{markdown}
+% \begin{macrocode}
+ \tl_set:NV
+ \l_tmpa_tl
+ \g_@@_current_theme_tl
+ \tl_reverse:N
+ \l_tmpa_tl
+ \tl_set:Ne
+ \l_tmpb_tl
+ {
+ \tl_tail:V
+ \l_tmpa_tl
+ }
+ \tl_reverse:N
+ \l_tmpb_tl
+% \end{macrocode}
+% \begin{markdown}
+%
+% Next, we munge the theme name.
+%
+% \end{markdown}
+% \begin{macrocode}
+ \str_set:NV
+ \l_tmpa_str
+ \l_tmpb_tl
+ \str_replace_all:Nnn
+ \l_tmpa_str
+ { / }
+ { _ }
+% \end{macrocode}
+% \begin{markdown}
+%
+% Finally, we load the plain \TeX{} theme.
+%
+% \end{markdown}
+% \begin{macrocode}
+ \@@_plain_tex_load_theme:VV
+ \l_tmpb_tl
+ \l_tmpa_str
+ }
+\cs_generate_variant:Nn
+ \tl_set:Nn
+ { Ne }
+\cs_generate_variant:Nn
+ \@@_plain_tex_load_theme:nn
+ { VV }
+\ExplSyntaxOff
% \end{macrocode}
+% \iffalse
+%</tex>
+%<*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}
+\markdownSetup {
+ rendererPrototypes = {
+ tilde = {~},
+ },
+}
+% \end{macrocode}
+% \iffalse
+%</themes-witiko-tilde>
+%<*themes-witiko-markdown-defaults-tex>
+% \fi
+% \begin{markdown}
+%
+% The `witiko/markdown/defaults` plain \TeX{} theme provides default
+% definitions for token renderer prototypes. See Section
+% <#sec:tex-token-renderer-prototypes> for the actual definitions.
+%
%### Token Renderer Prototypes {#tex-token-renderer-prototypes}
%
% The following definitions should be considered placeholder.
@@ -28319,7 +32700,10 @@ end
% \end{markdown}
% \begin{macrocode}
\def\markdownRendererInterblockSeparatorPrototype{\par}%
+\def\markdownRendererParagraphSeparatorPrototype{%
+ \markdownRendererInterblockSeparator}%
\def\markdownRendererHardLineBreakPrototype{\hfil\break}%
+\def\markdownRendererSoftLineBreakPrototype{ }%
\let\markdownRendererEllipsisPrototype\dots
\def\markdownRendererNbspPrototype{~}%
\def\markdownRendererLeftBracePrototype{\char`\{}%
@@ -28340,7 +32724,7 @@ end
\def\markdownRendererContentBlockOnlineImagePrototype{%
\markdownRendererImage}%
\def\markdownRendererContentBlockCodePrototype#1#2#3#4#5{%
- \markdownRendererInputFencedCode{#3}{#2}}%
+ \markdownRendererInputFencedCode{#3}{#2}{#2}}%
\def\markdownRendererImagePrototype#1#2#3#4{#2}%
\def\markdownRendererUlBeginPrototype{}%
\def\markdownRendererUlBeginTightPrototype{}%
@@ -28378,7 +32762,7 @@ end
\def\markdownRendererLineBlockEndPrototype{\endgroup}%
\def\markdownRendererInputVerbatimPrototype#1{%
\par{\tt\input#1\relax{}}\par}%
-\def\markdownRendererInputFencedCodePrototype#1#2{%
+\def\markdownRendererInputFencedCodePrototype#1#2#3{%
\markdownRendererInputVerbatim{#1}}%
\def\markdownRendererHeadingOnePrototype#1{#1}%
\def\markdownRendererHeadingTwoPrototype#1{#1}%
@@ -28431,12 +32815,7 @@ end
\markdownRendererHeaderAttributeContextEndPrototype
\cs_gset:Npn
\markdownRendererReplacementCharacterPrototype
- {
- % TODO: Replace with `\codepoint_generate:nn` in TeX Live 2023
- \sys_if_engine_pdftex:TF
- { ^^ef^^bf^^bd }
- { ^^^^fffd }
- }
+ { \codepoint_str_generate:n { fffd } }
\ExplSyntaxOff
\def\markdownRendererSectionBeginPrototype{}%
\def\markdownRendererSectionEndPrototype{}%
@@ -28492,7 +32871,7 @@ end
% \par
% \begin{markdown}
%
-%#### YAML Metadata Renderer Prototypes {#expl3-yaml-metadata-implementation}
+%#### YAML Metadata Renderer Prototypes
%
% To keep track of the current type of structure we inhabit when we are
% traversing a \acro{yaml} document, we will maintain the
@@ -28727,6 +33106,35 @@ end
}
\ExplSyntaxOff
% \end{macrocode}
+% \iffalse
+%</themes-witiko-markdown-defaults-tex>
+%<*tex>
+% \fi
+% \begin{markdown}
+%
+% If plain \TeX{} is the top layer, we load the `witiko/markdown/defaults`
+% plain \TeX{} theme with the default definitions for token renderer
+% prototypes unless the option `noDefaults` has been enabled (see Section
+% <#sec:plain>).
+%
+% \end{markdown}
+% \begin{macrocode}
+\ExplSyntaxOn
+\str_if_eq:VVT
+ \c_@@_top_layer_tl
+ \c_@@_option_layer_plain_tex_tl
+ {
+ \ExplSyntaxOff
+ \@@_if_option:nF
+ { noDefaults }
+ {
+ \@@_setup:n
+ {theme = witiko/markdown/defaults}
+ }
+ \ExplSyntaxOn
+ }
+\ExplSyntaxOff
+% \end{macrocode}
% \begin{markdown}
%
%### Lua Snippets
@@ -28849,56 +33257,25 @@ end
% \par
% \begin{markdown}
%
-%### Buffering Markdown Input {#buffering}
-%
-% The
-% \mdef{markdownIfOption}`{`\meta{name}`}{`\meta{iftrue}`}{`\meta{iffalse}`}`
-% macro is provided for testing, whether the value of
-% `\markdownOption`\meta{name} is `true`. If the value is `true`, then
-% \meta{iftrue} is expanded, otherwise \meta{iffalse} is expanded.
+% The \mdef{markdownCleanup} macro contains the Lua code that is executed
+% after any conversion from markdown to plain \TeX{}.
%
% \end{markdown}
% \begin{macrocode}
-\ExplSyntaxOn
-\prg_new_conditional:Nnn
- \@@_if_option:n
- { TF, T, F }
- {
- \@@_get_option_type:nN
- { #1 }
- \l_tmpa_tl
- \str_if_eq:NNF
- \l_tmpa_tl
- \c_@@_option_type_boolean_tl
- {
- \msg_error:nnxx
- { markdown }
- { expected-boolean-option }
- { #1 }
- { \l_tmpa_tl }
- }
- \@@_get_option_value:nN
- { #1 }
- \l_tmpa_tl
- \str_if_eq:NNTF
- \l_tmpa_tl
- \c_@@_option_value_true_tl
- { \prg_return_true: }
- { \prg_return_false: }
- }
-\msg_new:nnn
- { markdown }
- { expected-boolean-option }
- {
- Option~#1~has~type~#2,~
- but~a~boolean~was~expected.
- }
-\let\markdownIfOption=\@@_if_option:nTF
-\ExplSyntaxOff
+\def\markdownCleanup{%
+% \end{macrocode}
+% \begin{markdown}
+% Remove the `options.cacheDir` directory if it is empty.
+% \end{markdown}
+% \begin{macrocode}
+ lfs.rmdir(cacheDir)
+}%
% \end{macrocode}
% \par
% \begin{markdown}
%
+%### Buffering Markdown Input {#buffering}
+%
% The macros \mdef{markdownInputFileStream} and \mdef{markdownOutputFileStream}
% contain the number of the input and output file streams that will be used for
% the IO operations of the package.
@@ -29080,266 +33457,64 @@ end
% \par
% \begin{markdown}
%
-% The following two sections of the implementation have been deprecated and
-% will be removed in Markdown 3.0.0. The code that corresponds to
-% \mref{markdownMode} value of `3` will be the only implementation.
+% Use the \pkg{lt3luabridge} library to define the \mdef{markdownLuaExecute}
+% macro, which takes in a Lua scripts and expands to the standard output
+% produced by its execution.
%
% \end{markdown}
% \begin{macrocode}
\ExplSyntaxOn
-\int_compare:nT
- { \markdownMode = 3 }
+\cs_new:Npn
+ \markdownLuaExecute
+ #1
{
- \markdownInfo{Using~mode~3:~The~lt3luabridge~package}
- \file_input:n { lt3luabridge.tex }
- \cs_new:Npn
- \markdownLuaExecute
- { \luabridgeExecute }
+ \int_compare:nNnT
+ { \g_luabridge_method_int }
+ =
+ { \c_luabridge_method_shell_int }
+ {
+ \sys_if_shell_unrestricted:F
+ {
+ \sys_if_shell:TF
+ {
+ \msg_error:nn
+ { markdown }
+ { restricted-shell-access }
+ }
+ {
+ \msg_error:nn
+ { markdown }
+ { disabled-shell-access }
+ }
+ }
+ }
+ \luabridge_now:e
+ { #1 }
+ }
+\cs_generate_variant:Nn
+ \msg_new:nnnn
+ { nnnV }
+\tl_set:Nn
+ \l_tmpa_tl
+ {
+ You~may~need~to~run~TeX~with~the~--shell-escape~or~the~
+ --enable-write18~flag,~or~write~shell_escape=t~in~the~
+ texmf.cnf~file.
}
+\msg_new:nnnV
+ { markdown }
+ { restricted-shell-access }
+ { Shell~escape~is~restricted }
+ \l_tmpa_tl
+\msg_new:nnnV
+ { markdown }
+ { disabled-shell-access }
+ { Shell~escape~is~disabled }
+ \l_tmpa_tl
\ExplSyntaxOff
% \end{macrocode}
% \begin{markdown}
%
-%### Lua Shell Escape Bridge {#lua-bridge}
-%
-% 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 \mref{markdownMode} values of `0` and `1`.
-%
-% The \mref{markdownLuaExecute} macro defined here and in Section
-% <#sec:direct-lua> are meant to be indistinguishable 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~[@luatex21, Section
-% 4.1.1].
-%
-% \end{markdown}
-% \begin{macrocode}
-\ifnum\markdownMode<2\relax
-\ifnum\markdownMode=0\relax
- \markdownWarning{Using mode 0: Shell escape via write18
- (deprecated, to be removed in Markdown 3.0.0)}%
-\else
- \markdownWarning{Using mode 1: Shell escape via os.execute
- (deprecated, to be removed in Markdown 3.0.0)}%
-\fi
-% \end{macrocode}
-% \par
-% \begin{markdown}
-%
-% The \mdef{markdownExecuteShellEscape} macro contains the numeric value indicating
-% whether the shell access is enabled (`1`), disabled (`0`), or restricted
-% (`2`).
-%
-% Inherit the value of the `\pdfshellescape` (Lua\TeX{}, \Hologo{pdfTeX})
-% or the `\shellescape` (\Hologo{XeTeX}) commands. If neither of these
-% commands is defined and Lua is available, attempt to access the
-% `status.shell_escape` configuration item.
-%
-% If you cannot detect, whether the shell access is enabled, act as if it were.
-%
-% \end{markdown}
-% \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}
-% \par
-% \begin{markdown}
-%
-% 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 `os.execute` method otherwise.
-%
-% \end{markdown}
-% \begin{macrocode}
-\ifnum\markdownMode=0\relax
- \def\markdownExecuteDirect#1{\immediate\write18{#1}}%
-\else
- \def\markdownExecuteDirect#1{%
- \directlua{os.execute("\luaescapestring{#1}")}}%
-\fi
-% \end{macrocode}
-% \par
-% \begin{markdown}
-%
-% The \mdef{markdownExecute} macro is a wrapper on top of
-% \mref{markdownExecuteDirect} that checks the value of
-% \mref{markdownExecuteShellEscape} and prints an error message if the shell is
-% inaccessible.
-%
-% \end{markdown}
-% \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}
-% \par
-% \begin{markdown}
-%
-% 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 `print` function in the same manner it
-% would use the `tex.print` method.
-%
-% \end{markdown}
-% \begin{macrocode}
-\begingroup
-% \end{macrocode}
-% \begin{markdown}
-% 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.
-% \end{markdown}
-% \begin{macrocode}
- \catcode`|=0%
- \catcode`\\=12%
- |gdef|markdownLuaExecute#1{%
-% \end{macrocode}
-% \begin{markdown}
-% Create the file \Opt{helperScriptFileName} and fill it with the
-% input Lua code prepended with \pkg{kpathsea} initialization, so that Lua
-% modules from the \TeX{} distribution are available.
-% \end{markdown}
-% \begin{macrocode}
- |immediate|openout|markdownOutputFileStream=%
- |markdownOptionHelperScriptFileName
- |markdownInfo{Writing a helper Lua script to the file
- "|markdownOptionHelperScriptFileName"}%
- |immediate|write|markdownOutputFileStream{%
- local ran_ok, error = pcall(function()
-% \end{macrocode}
-% \begin{markdown}
-%
-% If \pkg{Kpathsea} has not been loaded before or if Lua\TeX{} has not yet
-% been initialized, configure \pkg{Kpathsea} on top of loading it.
-%
-% \end{markdown}
-% \begin{macrocode}
- local kpse
- (function()
- local should_initialize = package.loaded.kpse == nil
- or tex.initialize
- local ran_ok
- ran_ok, kpse = pcall(require, "kpse")
- if ran_ok and should_initialize then
- kpse.set_program_name("luatex")
- end
- end)()
- #1
- end)
-% \end{macrocode}
-% \begin{markdown}
-% If there was an error, use the file \Opt{errorTempFileName} to
-% store the error message.
-% \end{markdown}
-% \begin{macrocode}
- if not ran_ok then
- local file = io.open("%
- |markdownOptionOutputDir
- /|markdownOptionErrorTempFileName", "w")
- if file then
- file:write(error .. "\n")
- file:close()
- end
- print('\\markdownError{An error was encountered while executing
- Lua code}{For further clues, examine the file
- "|markdownOptionOutputDir
- /|markdownOptionErrorTempFileName"}')
- end}%
- |immediate|closeout|markdownOutputFileStream
-% \end{macrocode}
-% \begin{markdown}
-% Execute the generated \Opt{helperScriptFileName} Lua script using
-% the \TeX{}Lua binary and store the output in the
-% \Opt{outputTempFileName} file.
-% \end{markdown}
-% \begin{macrocode}
- |markdownInfo{Executing a helper Lua script from the file
- "|markdownOptionHelperScriptFileName" and storing the result in the
- file "|markdownOptionOutputTempFileName"}%
- |markdownExecute{texlua "|markdownOptionOutputDir
- /|markdownOptionHelperScriptFileName" > %
- "|markdownOptionOutputDir
- /|markdownOptionOutputTempFileName"}%
-% \end{macrocode}
-% \begin{markdown}
-% `\input` the generated \Opt{outputTempFileName} file.
-% \end{markdown}
-% \begin{macrocode}
- |input|markdownOptionOutputTempFileName|relax}%
-|endgroup
-% \end{macrocode}
-% \par
-% \begin{markdown}
-%
-%### Direct Lua Access {#direct-lua}
-%
-% The following \TeX{} code is intended for \TeX{} engines that provide
-% direct access to Lua (Lua\TeX{}). The macro \mref{markdownLuaExecute} defined
-% here and in Section <#sec:lua-bridge> are meant to be indistinguishable to
-% the remaining code. This corresponds to the \mref{markdownMode} value of `2`.
-%
-% \end{markdown}
-% \begin{macrocode}
-\fi
-\ifnum\markdownMode=2\relax
- \markdownWarning{Using mode 2: Direct Lua access
- (deprecated, to be removed in Markdown 3.0.0)}%
-% \end{macrocode}
-% \par
-% \begin{markdown}
-%
-% The direct Lua access version of the \mref{markdownLuaExecute} macro is defined
-% in terms of the `\directlua` primitive. The `print` function is set as
-% an alias to the `tex.print` method in order to mimic the behaviour of the
-% \mref{markdownLuaExecute} definition from Section <#sec:lua-bridge>,
-%
-% \end{markdown}
-% \begin{macrocode}
-\begingroup
-% \end{macrocode}
-% \begin{markdown}
-% 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.
-% \end{markdown}
-% \begin{macrocode}
- \catcode`|=0%
- \catcode`\\=12%
- |gdef|markdownLuaExecute#1{%
- |directlua{%
- local function print(input)
- local output = {}
- for line in input:gmatch("[^\r\n]+") do
- table.insert(output, line)
- end
- tex.print(output)
- end
- #1
- }%
- }%
-|endgroup
-\fi
-% \end{macrocode}
-% \par
-% \begin{markdown}
-%
%### Typesetting Markdown
%
% The \mref{markdownInput} macro uses an implementation of the
@@ -29413,20 +33588,15 @@ end
[[Could not open file "&1" for reading]])
local input = assert(file:read("*a"))
assert(file:close())
+ print(convert(input))
+ |markdownCleanup}%
% \end{macrocode}
% \begin{markdown}
-% Since the Lua converter expects \acro{unix} line endings, normalize the
-% input. Also add a line ending at the end of the file in case the input file
-% has none.
-% \end{markdown}
-% \begin{macrocode}
- print(convert(input))}%
-% \end{macrocode}
-% \begin{markdown}
-% In case we were finalizing the frozen cache, increment \Opt{frozenCacheCounter}.
+% If we are finalizing the frozen cache, increment \Opt{frozenCacheCounter}.
% \end{markdown}
% \begin{macrocode}
- |global|advance|markdownOptionFrozenCacheCounter by 1|relax
+ |markdownIfOption{finalizeCache}{%
+ |global|advance|markdownOptionFrozenCacheCounter by 1|relax}{}%
}%
|endgroup
}%
@@ -29472,19 +33642,6 @@ end
% \par
% \begin{markdown}
%
-% Use reflection to define the `renderers` and `rendererPrototypes` keys of
-% \mref{markdownSetup} as well as the keys that correspond to Lua options.
-%
-% \end{markdown}
-% \begin{macrocode}
-\ExplSyntaxOn
-\@@_latex_define_renderers:
-\@@_latex_define_renderer_prototypes:
-\ExplSyntaxOff
-% \end{macrocode}
-% \par
-% \begin{markdown}
-%
%### Logging Facilities
% The \LaTeX{} implementation redefines the plain \TeX{} logging macros (see
% Section <#sec:tex-interface-logging>) to use the \LaTeX{} `\PackageInfo`,
@@ -29519,13 +33676,125 @@ end
%
% \end{markdown}
% \begin{macrocode}
-\renewenvironment{markdown}{%
- \markdownReadAndConvert@markdown{}}{%
- \markdownEnd}%
-\renewenvironment{markdown*}[1]{%
- \markdownSetup{#1}%
- \markdownReadAndConvert@markdown*}{%
- \markdownEnd}%
+\ExplSyntaxOn
+\renewenvironment
+ { markdown }
+ {
+% \end{macrocode}
+% \markdownBegin
+%
+% In our implementation of the \envmref{markdown} \LaTeX{} environment, we
+% want to distinguish between the following two cases:
+% ``` tex
+% \begin{markdown} [smartEllipses] \begin{markdown}
+% \% This is an optional argument ^ [smartEllipses]
+% \% ... \% ^ This is link
+% \end{markdown} \end{markdown}
+% ```````
+%
+% Therefore, we cannot use the built-in \LaTeX{} support for environments
+% with optional arguments or packages such as \pkg{xparse}. Instead, we
+% must read the optional argument manually and prevent reading past the
+% end of a line.
+%
+% To prevent reading past the end of a line when looking for the optional
+% argument of the \envmref{markdown} \LaTeX{} environment and accidentally
+% tokenizing markdown text, we change the category code of carriage return
+% (`\r`, ASCII character 13 in decimal) from 5 (end of line).
+%
+% While any category code other than 5 (end of line) would work, we switch to
+% the category 13 (active), which is also used by the
+% \mref{markdownReadAndConvert} macro. This is necessary if we read until the
+% end of a line, because then the carriage return character will be
+% produced by \TeX{} via the `\endlinechar` plain \TeX{} macro and it needs
+% to have the correct category code, so that \mref{markdownReadAndConvert}
+% processes it correctly.
+%
+% \markdownEnd
+% \begin{macrocode}
+ \group_begin:
+ \char_set_catcode_active:n { 13 }
+% \end{macrocode}
+% \begin{markdown}
+%
+% To prevent doubling the hash signs (`#`, ASCII code 35 in decimal), we switch
+% its category from 6 (parameter) to 12 (letter).
+%
+% \end{markdown}
+% \begin{macrocode}
+ \char_set_catcode_letter:n { 35 }
+% \end{macrocode}
+% \begin{markdown}
+%
+% After we have matched the opening `[` that begins the optional argument,
+% we accept carriage returns as well.
+%
+% \end{markdown}
+% \begin{macrocode}
+ \peek_regex_replace_once:nnF
+ { \ *\[([^]]*)\][^\r]* }
+ {
+% \end{macrocode}
+% \begin{markdown}
+%
+% After we have matched the optional argument, we switch back the category
+% code of carriage returns and hash signs and we retokenize the content. This
+% will cause single new lines to produce a space token and multiple new lines
+% to produce `\par` tokens. Furthermore, this will cause hash signs followed
+% by a number to be recognized as parameter numbers, which is necessary when
+% we use the optional argument to redefine token renderers and token renderer
+% prototypes.
+%
+% \end{markdown}
+% \begin{macrocode}
+ \c { group_end: }
+ \c { tl_set_rescan:Nnn } \c { l_tmpa_tl } { } { \1 }
+% \end{macrocode}
+% \begin{markdown}
+%
+% Then, we pass the retokenized content to the \mref{markdownSetup} macro.
+%
+% \end{markdown}
+% \begin{macrocode}
+ \c { @@_setup:V } \c { l_tmpa_tl }
+% \end{macrocode}
+% \begin{markdown}
+%
+% Finally, regardless of whether or not we have matched the optional argument,
+% we let the \mref{markdownReadAndConvert} macro process the rest of the
+% \LaTeX{} environment.
+%
+% \end{markdown}
+% \begin{macrocode}
+ \c { markdownReadAndConvert@markdown } { }
+ }
+ {
+ \group_end:
+ \markdownReadAndConvert@markdown { }
+ }
+ }
+ { \markdownEnd }
+\renewenvironment
+ { markdown* }
+ [ 1 ]
+ {
+ \msg_warning:nnn
+ { markdown }
+ { latex-markdown-star-deprecated }
+ { #1 }
+ \@@_setup:n
+ { #1 }
+ \markdownReadAndConvert@markdown *
+ }
+ { \markdownEnd }
+\msg_new:nnn
+ { markdown }
+ { latex-markdown-star-deprecated }
+ {
+ The~markdown*~LaTeX~environment~has~been~deprecated~and~will~
+ be~removed~in~the~next~major~version~of~the~Markdown~package.
+ }
+\ExplSyntaxOff
\begingroup
% \end{macrocode}
% \begin{markdown}
@@ -29546,51 +33815,147 @@ end
% \par
% \begin{markdown}
%
-%#### \LaTeX{} Themes {#latex-themes-implementation}
+%### Options
+% The supplied package options are processed using the \mref{markdownSetup} macro.
+%
+% \end{markdown}
+% \begin{macrocode}
+\DeclareOption*{%
+ \expandafter\markdownSetup\expandafter{\CurrentOption}}%
+\ProcessOptions\relax
+% \end{macrocode}
+% \begin{markdown}
+%
+%### Themes {#latex-themes-implementation}
%
-% This section implements the theme-loading mechanism and the example themes
-% provided with the Markdown package.
+% This section overrides the plain \TeX{} implementation of the theme-loading
+% mechanism from Section <#sec:themes-implementation>. Futhermore, this section
+% also implements the built-in \LaTeX{} themes provided with the Markdown package.
%
% \end{markdown}
% \begin{macrocode}
\ExplSyntaxOn
+\cs_gset:Nn
+ \@@_load_theme:nn
+ {
% \end{macrocode}
+% \par
% \begin{markdown}
%
-% To keep track of our current place when packages themes have been nested,
-% we will maintain the \mdef{g_\@\@_latex_themes_seq} stack of theme names.
+% If the Markdown package has already been loaded, determine whether
+% a file named `markdowntheme`\meta{munged theme name}`.sty` exists
+% and whether we are still in the preamble.
%
% \end{markdown}
% \begin{macrocode}
-\newcommand\markdownLaTeXThemeName{}
-\seq_new:N \g_@@_latex_themes_seq
-\seq_gput_right:NV
- \g_@@_latex_themes_seq
- \markdownLaTeXThemeName
-\newcommand\markdownLaTeXThemeLoad[2]{
- \def\@tempa{%
- \def\markdownLaTeXThemeName{#2}
- \seq_gput_right:NV
- \g_@@_latex_themes_seq
- \markdownLaTeXThemeName
- \RequirePackage{#1}
- \seq_pop_right:NN
- \g_@@_latex_themes_seq
- \l_tmpa_tl
- \seq_get_right:NN
- \g_@@_latex_themes_seq
- \l_tmpa_tl
- \exp_args:NNV
- \def
- \markdownLaTeXThemeName
- \l_tmpa_tl}
- \ifmarkdownLaTeXLoaded
- \@tempa
- \else
- \exp_args:No
+ \ifmarkdownLaTeXLoaded
+ \ifx\@onlypreamble\@notprerr
+% \end{macrocode}
+% \begin{markdown}
+%
+% If both conditions are true does, end with an error, since we cannot load
+% \LaTeX{} themes after the preamble. Otherwise, try loading a plain \TeX{}
+% theme instead.
+%
+% \end{markdown}
+% \begin{macrocode}
+ \file_if_exist:nTF
+ { markdown theme #2.sty }
+ {
+ \msg_error:nnn
+ { markdown }
+ { latex-theme-after-preamble }
+ { #1 }
+ }
+ {
+ \@@_plain_tex_load_theme:nn
+ { #1 }
+ { #2 }
+ }
+ \else
+% \end{macrocode}
+% \begin{markdown}
+%
+% If the Markdown package has already been loaded but we are still in the
+% preamble, load a \LaTeX{} theme if it exists or load a plain \TeX{} theme
+% otherwise.
+%
+% \end{markdown}
+% \begin{macrocode}
+ \file_if_exist:nTF
+ { markdown theme #2.sty }
+ {
+ \msg_info:nnn
+ { markdown }
+ { loading-latex-theme }
+ { #1 }
+ \RequirePackage
+ { markdown theme #2 }
+ }
+ {
+ \@@_plain_tex_load_theme:nn
+ { #1 }
+ { #2 }
+ }
+ \fi
+ \else
+% \end{macrocode}
+% \begin{markdown}
+%
+% If the Markdown package has not yet been loaded, postpone the loading until
+% the Markdown package has finished loading.
+%
+% \end{markdown}
+% \begin{macrocode}
+ \msg_info:nnn
+ { markdown }
+ { theme-loading-postponed }
+ { #1 }
\AtEndOfPackage
- { \@tempa }
- \fi}
+ {
+ \@@_load_theme:nn
+ { #1 }
+ { #2 }
+ }
+ \fi
+ }
+\msg_new:nnn
+ { markdown }
+ { theme-loading-postponed }
+ {
+ Postponing~loading~Markdown~theme~#1~until~
+ Markdown~package~has~finished~loading
+ }
+\msg_new:nnn
+ { markdown }
+ { loading-latex-theme }
+ { Loading~LaTeX~Markdown~theme~#1 }
+\cs_generate_variant:Nn
+ \msg_new:nnnn
+ { nnVV }
+\tl_set:Nn
+ \l_tmpa_tl
+ { Cannot~load~LaTeX~Markdown~theme~#1~after~ }
+\tl_put_right:NV
+ \l_tmpa_tl
+ \c_backslash_str
+\tl_put_right:Nn
+ \l_tmpa_tl
+ { begin{document} }
+\tl_set:Nn
+ \l_tmpb_tl
+ { Load~Markdown~theme~#1~before~ }
+\tl_put_right:NV
+ \l_tmpb_tl
+ \c_backslash_str
+\tl_put_right:Nn
+ \l_tmpb_tl
+ { begin{document} }
+\msg_new:nnVV
+ { markdown }
+ { latex-theme-after-preamble }
+ \l_tmpa_tl
+ \l_tmpb_tl
\ExplSyntaxOff
% \end{macrocode}
% \begin{markdown}
@@ -29600,7 +33965,7 @@ end
% \end{markdown}
% \iffalse
%</latex>
-%<*latex-themes-witiko-dot>
+%<*themes-witiko-dot>
% \fi
% \begin{macrocode}
\markdownSetup{fencedCode}%
@@ -29632,7 +33997,7 @@ end
%
% \end{markdown}
% \begin{macrocode}
-\renewcommand\markdownRendererInputFencedCodePrototype[2]{%
+\renewcommand\markdownRendererInputFencedCodePrototype[3]{%
\def\next##1 ##2\relax{%
\ifthenelse{\equal{##1}{dot}}{%
\markdownIfOption{frozenCache}{}{%
@@ -29659,14 +34024,14 @@ end
% \end{markdown}
% \begin{macrocode}
}{%
- \markdown@witiko@dot@oldRendererInputFencedCodePrototype{#1}{#2}%
+ \markdown@witiko@dot@oldRendererInputFencedCodePrototype{#1}{#2}{#3}%
}%
}%
\next#2 \relax}%
% \end{macrocode}
% \iffalse
-%</latex-themes-witiko-dot>
-%<*latex-themes-witiko-graphicx-http>
+%</themes-witiko-dot>
+%<*themes-witiko-graphicx-http>
% \fi
% \par
% \begin{markdown}
@@ -29802,152 +34167,32 @@ end
\endgroup
% \end{macrocode}
% \iffalse
-%</latex-themes-witiko-graphicx-http>
-%<*latex-themes-witiko-tilde>
+%</themes-witiko-graphicx-http>
+%<*themes-witiko-markdown-defaults-latex>
% \fi
% \par
% \begin{markdown}
%
-% The `witiko/tilde` theme redefines the tilde token renderer prototype,
-% so that it expands to a non-breaking space:
+% The `witiko/markdown/defaults` \LaTeX{} theme provides default definitions
+% for token renderer prototypes. First, the \LaTeX{} theme loads the plain
+% \TeX{} theme with the default definitions for plain \TeX{}:
%
% \end{markdown}
% \begin{macrocode}
-\renewcommand\markdownRendererTildePrototype{~}%
+\markdownLoadPlainTeXTheme
% \end{macrocode}
-% \iffalse
-%</latex-themes-witiko-tilde>
-%<*latex>
-% \fi
% \par
% \begin{markdown}
%
-%### Options
-% The supplied package options are processed using the \mref{markdownSetup} macro.
-%
-% \end{markdown}
-% \begin{macrocode}
-\DeclareOption*{%
- \expandafter\markdownSetup\expandafter{\CurrentOption}}%
-\ProcessOptions\relax
-% \end{macrocode}
-% \begin{markdown}
-%
-% After processing the options, activate the `jekyllDataRenderes`, `renderers`,
-% `rendererPrototypes`, and `code` keys.
-%
-% \end{markdown}
-% \begin{macrocode}
-\ExplSyntaxOn
-\keys_define:nn
- { markdown/latex-options }
- {
- renderers .code:n = {
- \keys_set:nn
- { markdown/latex-options/renderers }
- { #1 }
- },
- }
-\@@_with_various_cases:nn
- { rendererPrototypes }
- {
- \keys_define:nn
- { markdown/latex-options }
- {
- #1 .code:n = {
- \keys_set:nn
- { markdown/latex-options/renderer-prototypes }
- { ##1 }
- },
- }
- }
-% \end{macrocode}
-% \begin{markdown}
-%
-% The `code` key is used to immediately expand and execute code, which can be
-% especially useful in \LaTeX{} snippets.
-%
-% \end{markdown}
-% \begin{macrocode}
-\keys_define:nn
- { markdown/latex-options }
- {
- code .code:n = { #1 },
- }
-% \end{macrocode}
-% \begin{markdown}
-%
-% The `jekyllDataRenderers` key can be used as a syntactic sugar for setting
-% the `markdown/jekyllData` key--values (see Section <#sec:expl3yamlmetadata>)
-% without using the expl3 language.
-%
-% \end{markdown}
-% \begin{macrocode}
-\@@_with_various_cases:nn
- { jekyllDataRenderers }
- {
- \keys_define:nn
- { markdown/latex-options }
- {
- #1 .code:n = {
- \tl_set:Nn
- \l_tmpa_tl
- { ##1 }
-% \end{macrocode}
-% \begin{markdown}
-%
-% To ensure that keys containing forward slashes get passed correctly, we
-% replace all forward slashes in the input with backslash tokens with category
-% code letter and then undo the replacement. This means that if any unbraced
-% backslash tokens with category code letter exist in the input, they will be
-% replaced with forward slashes. However, this should be extremely rare.
-%
-% \end{markdown}
-% \begin{macrocode}
- \tl_replace_all:NnV
- \l_tmpa_tl
- { / }
- \c_backslash_str
- \keys_set:nV
- { markdown/latex-options/jekyll-data-renderers }
- \l_tmpa_tl
- },
- }
- }
-\keys_define:nn
- { markdown/latex-options/jekyll-data-renderers }
- {
- unknown .code:n = {
- \tl_set_eq:NN
- \l_tmpa_tl
- \l_keys_key_str
- \tl_replace_all:NVn
- \l_tmpa_tl
- \c_backslash_str
- { / }
- \tl_put_right:Nn
- \l_tmpa_tl
- {
- .code:n = { #1 }
- }
- \keys_define:nV
- { markdown/jekyllData }
- \l_tmpa_tl
- }
- }
-\cs_generate_variant:Nn
- \keys_define:nn
- { nV }
-\ExplSyntaxOff
-% \end{macrocode}
-% \par
-% \begin{markdown}
+% Next, the \LaTeX{} theme overrides some of the plain \TeX{} definitions.
+% See Section <#sec:latex-token-renderer-prototypes> for the actual
+% definitions.
%
-%### Token Renderer Prototypes {#latex-default-renderer-prototypes}
+%### Token Renderer Prototypes {#latex-token-renderer-prototypes}
%
-% The following configuration should be considered placeholder. If the `plain`
-% package option has been enabled (see Section <#sec:latexplain>), none of
-% it will take effect.
+% The following configuration should be considered placeholder. If the option
+% `plain` has been enabled (see Section <#sec:plain>), none of the definitions
+% will take effect.
% \end{markdown}
% \begin{macrocode}
\markdownIfOption{plain}{\iffalse}{\iftrue}
@@ -29991,7 +34236,7 @@ end
{ LowerRoman } { \int_to_roman:n { #2 } }
{ UpperRoman } { \int_to_Roman:n { #2 } }
{ LowerAlpha } { \int_to_alph:n { #2 } }
- { UpperAlpha } { \int_to_alph:n { #2 } }
+ { UpperAlpha } { \int_to_Alph:n { #2 } }
}
}
\cs_new:Nn
@@ -30057,16 +34302,26 @@ end
\tl_set:Nn
\l_@@_latex_fancy_list_item_label_delimiter_style_tl
{ #2 }
- \tl_set:Nn
- \l_tmpa_tl
- { \begin{enumerate}[ }
- \tl_put_right:Nx
- \l_tmpa_tl
- { \@@_latex_paralist_style:nn { #1 } { #2 } }
- \tl_put_right:Nn
+ \@@_if_option:nTF
+ { startNumber }
+ {
+ \tl_set:Nn
+ \l_tmpa_tl
+ { \begin{enumerate} }
+ }
+ {
+ \tl_set:Nn
+ \l_tmpa_tl
+ { \begin{enumerate}[ }
+ \tl_put_right:Nx
+ \l_tmpa_tl
+ { \@@_latex_paralist_style:nn { #1 } { #2 } }
+ \tl_put_right:Nn
+ \l_tmpa_tl
+ { ] }
+ }
+ \tl_use:N
\l_tmpa_tl
- { ] }
- \l_tmpa_tl
},
fancyOlEnd = {
\end{enumerate}
@@ -30104,15 +34359,27 @@ end
{
\plpartopsep=\partopsep
\pltopsep=\topsep
- \begin{compactenum}[
}
- \tl_put_right:Nx
- \l_tmpa_tl
- { \@@_latex_paralist_style:nn { #1 } { #2 } }
- \tl_put_right:Nn
+ \@@_if_option:nTF
+ { startNumber }
+ {
+ \tl_put_right:Nn
+ \l_tmpa_tl
+ { \begin{compactenum} }
+ }
+ {
+ \tl_put_right:Nn
+ \l_tmpa_tl
+ { \begin{compactenum}[ }
+ \tl_put_right:Nx
+ \l_tmpa_tl
+ { \@@_latex_paralist_style:nn { #1 } { #2 } }
+ \tl_put_right:Nn
+ \l_tmpa_tl
+ { ] }
+ }
+ \tl_use:N
\l_tmpa_tl
- { ] }
- \l_tmpa_tl
},
fancyOlEndTight = {
\end{compactenum}
@@ -30250,7 +34517,7 @@ end
image = {%
\begin{figure}%
\begin{center}%
- \includegraphics{#3}%
+ \includegraphics[alt={#1}]{#3}%
\end{center}%
\ifx\empty#4\empty\else
\caption{#4}%
@@ -30267,43 +34534,44 @@ end
dlEnd = {\end{description}},
emphasis = {\emph{#1}},
tickedBox = {$\boxtimes$},
- halfTickedBox = {$\boxdot$},
+ halfTickedBox = {$\boxdot$}}}
% \end{macrocode}
% \par
% \begin{markdown}
%
-% If identifier attributes appear at the beginning of a section, we make the
-% next heading produce the `\label` macro.
+% If identifier attributes appear at the beginning of a section, we make them
+% produce the `\label` macro.
%
% \end{markdown}
% \begin{macrocode}
- headerAttributeContextBegin = {%
- \markdownSetup{
- rendererPrototypes = {
- attributeIdentifier = {%
- \begingroup
- \def\next####1{%
- \def####1########1{%
- \endgroup
- ####1{########1}%
- \label{##1}%
- }%
- }%
- \next\markdownRendererHeadingOne
- \next\markdownRendererHeadingTwo
- \next\markdownRendererHeadingThree
- \next\markdownRendererHeadingFour
- \next\markdownRendererHeadingFive
- \next\markdownRendererHeadingSix
- },
- },
- }%
+\ExplSyntaxOn
+\seq_new:N \l_@@_header_identifiers_seq
+\markdownSetup{
+ rendererPrototypes = {
+ headerAttributeContextBegin = {
+ \seq_clear:N \l_@@_header_identifiers_seq
+ \markdownSetup
+ {
+ renderers = {
+ attributeIdentifier = {
+ \seq_put_right:Nn
+ \l_@@_header_identifiers_seq
+ { ##1 }
+ },
+ },
+ }
+ },
+ headerAttributeContextEnd = {
+ \seq_map_inline:Nn
+ \l_@@_header_identifiers_seq
+ { \label { ##1 } }
+ },
},
- headerAttributeContextEnd = {},
+}
+\ExplSyntaxOff
+\markdownSetup{rendererPrototypes={
superscript = {\textsuperscript{#1}},
subscript = {\textsubscript{#1}},
- displayMath = {\begin{displaymath}#1\end{displaymath}},
- inlineMath = {\begin{math}#1\end{math}},
blockQuoteBegin = {\begin{quotation}},
blockQuoteEnd = {\end{quotation}},
inputVerbatim = {\VerbatimInput{#1}},
@@ -30322,7 +34590,7 @@ end
\RequirePackage{ltxcmds}
\ExplSyntaxOn
\cs_gset:Npn
- \markdownRendererInputFencedCodePrototype#1#2
+ \markdownRendererInputFencedCodePrototype#1#2#3
{
\tl_if_empty:nTF
{ #2 }
@@ -30382,7 +34650,7 @@ end
%
% \end{markdown}
% \begin{macrocode}
- { \markdownRendererInputFencedCode{#1}{} }
+ { \markdownRendererInputFencedCode{#1}{}{} }
}
}
}
@@ -30419,16 +34687,16 @@ end
headingOne = {\section{#1}},
headingTwo = {\subsection{#1}},
headingThree = {\subsubsection{#1}},
- headingFour = {\paragraph{#1}\leavevmode},
- headingFive = {\subparagraph{#1}\leavevmode}}}
+ headingFour = {\paragraph{#1}},
+ headingFive = {\subparagraph{#1}}}}
}{%
\markdownSetup{rendererPrototypes = {
headingOne = {\chapter{#1}},
headingTwo = {\section{#1}},
headingThree = {\subsection{#1}},
headingFour = {\subsubsection{#1}},
- headingFive = {\paragraph{#1}\leavevmode},
- headingSix = {\subparagraph{#1}\leavevmode}}}
+ headingFive = {\paragraph{#1}},
+ headingSix = {\subparagraph{#1}}}}
}%
% \end{macrocode}
% \par
@@ -30966,25 +35234,10 @@ end
%
% \end{markdown}
% \begin{macrocode}
-% TODO: Remove the command definition in TeX Live 2021.
-\providecommand\IfFormatAtLeastTF{\@ifl@t@r\fmtversion}
\markdownSetup{
rendererPrototypes = {
jekyllDataEnd = {
-% TODO: Remove the else branch in TeX Live 2021.
- \IfFormatAtLeastTF
- { 2020-10-01 }
- { \AddToHook{begindocument/end}{\maketitle} }
- {
- \ifx\@onlypreamble\@notprerr
- % We are in the document
- \maketitle
- \else
- % We are in the preamble
- \RequirePackage{etoolbox}
- \AfterEndPreamble{\maketitle}
- \fi
- }
+ \AddToHook{begindocument/end}{\maketitle}
},
},
}
@@ -31013,18 +35266,18 @@ end
% \par
% \begin{markdown}
%
-%#### Strike-Through
-% If the \Opt{strikeThrough} option is enabled, we will load the
-% \pkg{soulutf8} package and use it to implement strike-throughs.
+%#### Marked Text
+% If the \Opt{mark} option is enabled, we will load the \pkg{soulutf8} package
+% and use it to implement marked text.
%
% \end{markdown}
% \begin{macrocode}
-\markdownIfOption{strikeThrough}{%
+\markdownIfOption{mark}{%
\RequirePackage{soulutf8}%
\markdownSetup{
rendererPrototypes = {
- strikeThrough = {%
- \st{#1}%
+ mark = {%
+ \hl{#1}%
},
}
}
@@ -31120,8 +35373,12 @@ end
}
}
\ExplSyntaxOff
-\fi % Closes `\markdownIfOption{Plain}{\iffalse}{iftrue}`
+\fi % Closes `\markdownIfOption{plain}{\iffalse}{\iftrue}`
% \end{macrocode}
+% \iffalse
+%</themes-witiko-markdown-defaults-latex>
+%<*latex>
+% \fi
% \par
% \begin{markdown}
%
@@ -31186,7 +35443,7 @@ end
% \begin{markdown}
%
%### Typesetting Markdown
-% The \mref{inputmarkdown} is defined to accept an optional argument with
+% The \mref{inputmarkdown} macro is defined to accept an optional argument with
% options recognized by the \Hologo{ConTeXt} interface (see Section
% <#sec:context-options>).
%
@@ -31198,7 +35455,7 @@ end
\long\def\doinputmarkdown[#1]#2{%
\begingroup
\iffirstargument
- \setupmarkdown{#1}%
+ \setupmarkdown[#1]%
\fi
\markdownInput{#2}%
\endgroup}%
@@ -31220,48 +35477,108 @@ end
%
% \end{markdown}
% \begin{macrocode}
-\ifx\startluacode\undefined % MkII
- \begingroup
- \catcode`\|=0%
- \catcode`\\=12%
- |gdef|startmarkdown{%
- |markdownReadAndConvert{\stopmarkdown}%
- {|stopmarkdown}}%
- |gdef|stopmarkdown{%
- |markdownEnd}%
- |endgroup
-\else % MkIV
- \startluacode
- document.markdown_buffering = false
- local function preserve_trailing_spaces(line)
- if document.markdown_buffering then
- line = line:gsub("[ \t][ \t]$", "\t\t")
- end
- return line
+\startluacode
+ document.markdown_buffering = false
+ local function preserve_trailing_spaces(line)
+ if document.markdown_buffering then
+ line = line:gsub("[ \t][ \t]$", "\t\t")
end
- resolvers.installinputlinehandler(preserve_trailing_spaces)
- \stopluacode
- \begingroup
- \catcode`\|=0%
- \catcode`\\=12%
- |gdef|startmarkdown{%
- |ctxlua{document.markdown_buffering = true}%
- |markdownReadAndConvert{\stopmarkdown}%
- {|stopmarkdown}}%
- |gdef|stopmarkdown{%
- |ctxlua{document.markdown_buffering = false}%
- |markdownEnd}%
- |endgroup
-\fi
+ return line
+ end
+ resolvers.installinputlinehandler(preserve_trailing_spaces)
+\stopluacode
+\begingroup
+ \catcode`\|=0%
+ \catcode`\\=12%
+ |gdef|startmarkdown{%
+ |ctxlua{document.markdown_buffering = true}%
+ |markdownReadAndConvert{\stopmarkdown}%
+ {|stopmarkdown}}%
+ |gdef|stopmarkdown{%
+ |ctxlua{document.markdown_buffering = false}%
+ |markdownEnd}%
+|endgroup
% \end{macrocode}
% \par
% \begin{markdown}
%
-%### Token Renderer Prototypes
-% The following configuration should be considered placeholder.
+%### Themes {#context-themes-implementation}
+%
+% This section overrides the plain \TeX{} implementation of the theme-loading
+% mechanism from Section <#sec:themes-implementation>. Futhermore, this section
+% also implements the built-in \Hologo{ConTeXt} themes provided with the
+% Markdown package.
+%
+% \end{markdown}
+% \begin{macrocode}
+\ExplSyntaxOn
+\cs_gset:Nn
+ \@@_load_theme:nn
+ {
+% \end{macrocode}
+% \par
+% \begin{markdown}
+%
+% Determine whether a file named `t-markdowntheme`\meta{munged theme
+% name}`.tex` exists. If it does, load it. Otherwise, try loading a plain
+% \TeX{} theme instead.
%
% \end{markdown}
% \begin{macrocode}
+ \file_if_exist:nTF
+ { t - markdown theme #2.tex }
+ {
+ \msg_info:nnn
+ { markdown }
+ { loading-context-theme }
+ { #1 }
+ \usemodule
+ [ t ]
+ [ markdown theme #2 ]
+ }
+ {
+ \@@_plain_tex_load_theme:nn
+ { #1 }
+ { #2 }
+ }
+ }
+\msg_new:nnn
+ { markdown }
+ { loading-context-theme }
+ { Loading~ConTeXt~Markdown~theme~#1 }
+\ExplSyntaxOff
+% \end{macrocode}
+% \iffalse
+%</context>
+%<*themes-witiko-markdown-defaults-context>
+% \fi
+% \par
+% \begin{markdown}
+%
+% The `witiko/markdown/defaults` \Hologo{ConTeXt} theme provides default
+% definitions for token renderer prototypes. First, the \Hologo{ConTeXt} theme
+% loads the plain \TeX{} theme with the default definitions for plain \TeX{}:
+%
+% \end{markdown}
+% \begin{macrocode}
+\markdownLoadPlainTeXTheme
+% \end{macrocode}
+% \par
+% \begin{markdown}
+%
+% Next, the \Hologo{ConTeXt} theme overrides some of the plain \TeX{} definitions.
+% See Section <#sec:context-token-renderer-prototypes> for the actual
+% definitions.
+%
+%### Token Renderer Prototypes {#context-token-renderer-prototypes}
+%
+% The following configuration should be considered placeholder. If the option
+% `plain` has been enabled (see Section <#sec:plain>), none of the definitions
+% will take effect.
+%
+% \end{markdown}
+% \begin{macrocode}
+\markdownIfOption{plain}{\iffalse}{\iftrue}
\def\markdownRendererHardLineBreakPrototype{\blank}%
\def\markdownRendererLeftBracePrototype{\textbraceleft}%
\def\markdownRendererRightBracePrototype{\textbraceright}%
@@ -31356,7 +35673,7 @@ end
% \begin{macrocode}
\ExplSyntaxOn
\cs_gset:Npn
- \markdownRendererInputFencedCodePrototype#1#2
+ \markdownRendererInputFencedCodePrototype#1#2#3
{
\tl_if_empty:nTF
{ #2 }
@@ -31416,7 +35733,6 @@ end
\def\markdownRendererSuperscriptPrototype#1{\high{#1}}
\def\markdownRendererSubscriptPrototype#1{\low{#1}}
\def\markdownRendererDisplayMathPrototype#1{\startformula#1\stopformula}%
-\def\markdownRendererInlineMathPrototype#1{$#1$}%
% \end{macrocode}
% \par
% \begin{markdown}
@@ -31544,8 +35860,30 @@ end
\cs_gset_eq:NN
\markdownRendererInputRawBlockPrototype
\markdownRendererInputRawInlinePrototype
+\fi % Closes `\markdownIfOption{plain}{\iffalse}{\iftrue}`
\ExplSyntaxOff
-\stopmodule\protect
+\stopmodule
+\protect
+% \end{macrocode}
+% \iffalse
+%</themes-witiko-markdown-defaults-context>
+%<*context>
+% \fi
+% \par
+% \begin{markdown}
+%
+% At the end of the \Hologo{ConTeXt} module, we load the
+% `witiko/markdown/defaults` \Hologo{ConTeXt} theme with the default
+% definitions for token renderer prototypes unless the option `noDefaults`
+% has been enabled (see Section <#sec:plain>).
+%
+% \end{markdown}
+% \begin{macrocode}
+\markdownIfOption{noDefaults}{}{
+ \setupmarkdown[theme=witiko/markdown/defaults]
+}
+\stopmodule
+\protect
% \end{macrocode}
% \iffalse
%</context>