diff options
-rw-r--r-- | Master/texmf-dist/doc/generic/markdown/markdown.pdf | bin | 0 -> 314655 bytes | |||
-rw-r--r-- | Master/texmf-dist/source/generic/markdown/docstrip.cfg | 105 | ||||
-rw-r--r-- | Master/texmf-dist/source/generic/markdown/markdown.dtx | 3590 | ||||
-rw-r--r-- | Master/texmf-dist/source/generic/markdown/markdown.ins | 8 | ||||
-rw-r--r-- | Master/texmf-dist/tex/context/third/markdown/t-markdown.tex | 140 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/markdown/markdown.tex | 392 | ||||
-rw-r--r-- | Master/texmf-dist/tex/latex/markdown/markdown.sty | 331 | ||||
-rw-r--r-- | Master/texmf-dist/tex/luatex/markdown/markdown.lua | 1057 | ||||
-rwxr-xr-x | Master/tlpkg/bin/tlpkg-ctan-check | 2 | ||||
-rw-r--r-- | Master/tlpkg/tlpsrc/collection-latexextra.tlpsrc | 1 | ||||
-rw-r--r-- | Master/tlpkg/tlpsrc/markdown.tlpsrc | 0 |
11 files changed, 5625 insertions, 1 deletions
diff --git a/Master/texmf-dist/doc/generic/markdown/markdown.pdf b/Master/texmf-dist/doc/generic/markdown/markdown.pdf Binary files differnew file mode 100644 index 00000000000..afa021947e9 --- /dev/null +++ b/Master/texmf-dist/doc/generic/markdown/markdown.pdf diff --git a/Master/texmf-dist/source/generic/markdown/docstrip.cfg b/Master/texmf-dist/source/generic/markdown/docstrip.cfg new file mode 100644 index 00000000000..64fdc439c76 --- /dev/null +++ b/Master/texmf-dist/source/generic/markdown/docstrip.cfg @@ -0,0 +1,105 @@ +\askforoverwritefalse +\def\luapreamble{% +-- ^^J% +-- Copyright (C) 2009-2016 John MacFarlane, Hans Hagen^^J% +-- ^^J% +-- Permission is hereby granted, free of charge, to any person obtaining^^J% +-- a copy of this software and associated documentation files (the^^J% +-- "Software"), to deal in the Software without restriction, including^^J% +-- without limitation the rights to use, copy, modify, merge, publish,^^J% +-- distribute, sublicense, and/or sell copies of the Software, and to^^J% +-- permit persons to whom the Software is furnished to do so, subject to^^J% +-- the following conditions:^^J% +-- ^^J% +-- The above copyright notice and this permission notice shall be included^^J% +-- in all copies or substantial portions of the Software.^^J% +-- ^^J% +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,^^J% +-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF^^J% +-- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.^^J% +-- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY^^J% +-- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,^^J% +-- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE^^J% +-- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.^^J% +-- ^^J% +-- Copyright (C) 2016 Vít Novotný^^J% +-- ^^J% +-- This work may be distributed and/or modified under the^^J% +-- conditions of the LaTeX Project Public License, either version 1.3^^J% +-- of this license or (at your option) any later version.^^J% +-- The latest version of this license is in^^J% +-- ^^J% +-- \space\space\space\space http://www.latex-project.org/lppl.txt^^J% +-- ^^J% +-- and version 1.3 or later is part of all distributions of LaTeX^^J% +-- version 2005/12/01 or later.^^J% +-- ^^J% +-- This work has the LPPL maintenance status `maintained'.^^J% +-- The Current Maintainer of this work is Vít Novotný.^^J% +-- ^^J% +-- Send bug reports, requests for additions and questions^^J% +-- either to the GitHub issue tracker at^^J% +-- ^^J% +-- \space\space\space\space https://github.com/witiko/markdown/issues^^J% +-- ^^J% +-- or to the e-mail address <witiko@mail.muni.cz>.^^J% +-- ^^J% +-- MODIFICATION ADVICE:^^J% +-- ^^J% +-- If you want to customize this file, it is best to make a copy of^^J% +-- the source file(s) from which it was produced. Use a different^^J% +-- name for your copy(ies) and modify the copy(ies); this will ensure^^J% +-- that your modifications do not get overwritten when you install a^^J% +-- new release of the standard system. You should also ensure that^^J% +-- your modified source file does not generate any modified file with^^J% +-- the same name as a standard file.^^J% +-- ^^J% +-- You will also need to produce your own, suitably named, .ins file to^^J% +-- control the generation of files from your source file; this file^^J% +-- should contain your own preambles for the files it generates, not^^J% +-- those in the standard .ins files.^^J% +-- } +\let\luapostamble\empty +\declarepreamble\texpreamble + +Copyright (C) 2016 Vít Novotný + +This work may be distributed and/or modified under the +conditions of the LaTeX Project Public License, either version 1.3 +of this license or (at your option) any later version. +The latest version of this license is in + + http://www.latex-project.org/lppl.txt + +and version 1.3 or later is part of all distributions of LaTeX +version 2005/12/01 or later. + +This work has the LPPL maintenance status `maintained'. +The Current Maintainer of this work is Vít Novotný. + +Send bug reports, requests for additions and questions +either to the GitHub issue tracker at + + https://github.com/Witiko/markdown/issues + +or to the e-mail address <witiko@mail.muni.cz>. + +MODIFICATION ADVICE: + +If you want to customize this file, it is best to make a copy of +the source file(s) from which it was produced. Use a different +name for your copy(ies) and modify the copy(ies); this will ensure +that your modifications do not get overwritten when you install a +new release of the standard system. You should also ensure that +your modified source file does not generate any modified file with +the same name as a standard file. + +You will also need to produce your own, suitably named, .ins file to +control the generation of files from your source file; this file +should contain your own preambles for the files it generates, not +those in the standard .ins files. + +The names of the source files used are shown above. + +\endpreamble +\let\texpostamble\defaultpostamble diff --git a/Master/texmf-dist/source/generic/markdown/markdown.dtx b/Master/texmf-dist/source/generic/markdown/markdown.dtx new file mode 100644 index 00000000000..49995231e70 --- /dev/null +++ b/Master/texmf-dist/source/generic/markdown/markdown.dtx @@ -0,0 +1,3590 @@ +% \iffalse +%<*driver> +\documentclass{ltxdockit} +\usepackage{btxdockit} +\usepackage[utf8]{inputenc} +\usepackage[T1]{fontenc} +\usepackage[american]{babel} +\usepackage{microtype} +\usepackage{amsmath} +\usepackage{fancyvrb} +\usepackage{hologo} +\usepackage{xcolor} +\usepackage{doc} + +% Set up the style. +\emergencystretch=1em +\RecustomVerbatimEnvironment + {Verbatim}{Verbatim} + {gobble=2,frame=single} +\setcounter{secnumdepth}{4} +\addtokomafont{title}{\sffamily} +\addtokomafont{paragraph}{\spotcolor} +\addtokomafont{section}{\spotcolor} +\addtokomafont{subsection}{\spotcolor} +\addtokomafont{subsubsection}{\spotcolor} +\addtokomafont{descriptionlabel}{\spotcolor} +\setkomafont{caption}{\bfseries\sffamily\spotcolor} +\setkomafont{captionlabel}{\bfseries\sffamily\spotcolor} +\hypersetup{citecolor=spot} +\let\oldCodelineNo\theCodelineNo +\def\theCodelineNo{\textcolor{gray}{\oldCodelineNo}} + +% Define some markup. +\let\pkg\relax % A package name +\newcommand\mdef[1]{% A TeX macro definition + \phantomsection\label{macro:#1}\textcolor{spot}{\cs{#1}}} +\newcommand\m[1]{% A TeX macro reference + \hyperref[macro:#1]{\textcolor{spot}{\cs{#1}}}} +\newcommand\envmdef[1]{% A TeX macro definition + \phantomsection\label{environment:#1}\textcolor{spot}{\t`#1`}} +\newcommand\envm[1]{% A LaTeX environment reference + \hyperref[environment:#1]{\textcolor{spot}{\t`#1`}}} +\newcommand\luamdef[1]{% A Lua object / method definition + \phantomsection\label{lua:#1}\textcolor{spot}{\t`#1`}} +\newcommand\luam[1]{% A Lua object / method reference + \hyperref[lua:#1]{\t`#1`}} +\def\t`#1`{% Inline code + \textcolor{spot}{\text{\texttt{#1}}}} +\newcommand\Optitem[2][]{% An option item definition + \phantomsection\label{opt:#2}\optitem[#1]{#2}} +\newcommand\Valitem[2][]{% A value item definition + \phantomsection\label{opt:#2}\valitem[#1]{#2}} +\newcommand\Opt[1]{% An option / value item reference + \hyperref[opt:#1]{\t`#1`}} +\newcommand\acro[1]{% An acronym + \textsc{#1}} + +% Set up the catcodes. +\catcode`\_=12 % We won't be typesetting math and Lua contains lots of `_`. + +% Set up the title page. +\titlepage{% + title={A Markdown Interpreter for \TeX{}}, + subtitle={}, + url={https://github.com/witiko/markdown}, + author={Vít Novotný (based on the work of John MacFarlane and Hans Hagen)}, + email={witiko@mail.muni.cz}, + revision={\input VERSION}, + date={\today}} +\CodelineIndex + +% Set up the bibliography. +\usepackage{filecontents} +\begin{filecontents}{markdown.bib} + @book{luatex16, + author = {{Lua\TeX{} development team}}, + title = {Lua\TeX{} reference manual (0.95.0)}, + url = {http://www.luatex.org/svn/trunk/manual/luatex.pdf}, + urldate = {2016-05-12}} + @book{latex16, + author = {Braams, Johannes and Carlisle, David and Jeffrey, Alan and + Lamport, Leslie and Mittelbach, Frank and Rowley, Chris and + Schöpf, Rainer}, + title = {The \Hologo{LaTeX2e} Sources}, + date = {2016-03-31}, + url = {http://mirrors.ctan.org/macros/latex/base/source2e.pdf}, + urldate = {2016-06-02}} + @book{ierusalimschy13, + author = {Ierusalimschy, Roberto}, + year = {2013}, + title = {Programming in Lua}, + edition = {3}, + isbn = {978-85-903798-5-0}, + pagetotal = {xviii, 347}, + location = {Rio de Janeiro}, + publisher = {PUC-Rio}} + @book{knuth86, + author = {Knuth, Donald Ervin}, + year = {1986}, + title = {The \TeX{}book}, + edition = {3}, + isbn = {0-201-13447-0}, + pagetotal = {ix, 479}, + publisher = {Addison-Westley}} +\end{filecontents} +\usepackage[ + backend=biber, + style=iso-numeric, + sorting=none, + autolang=other, + sortlocale=auto]{biblatex} +\addbibresource{markdown.bib} + +\begin{document} + \printtitlepage + \tableofcontents + \DocInput{markdown.dtx} + \printbibliography +\end{document} +%</driver> +% \fi +% +% \section{Introduction} +% This document is a reference manual for the \pkg{Markdown} package. It is +% split into three sections. This section explains the purpose and the +% background of the package and outlines its prerequisites. Section +% \ref{sec:interfaces} describes the interfaces exposed by the package along +% with usage notes and examples. It is aimed at the user of the package. +% Section \ref{sec:implementation} describes the implementation of the package. +% It is aimed at the developer of the package and the curious user. +% +% \subsection{About \pkg{Markdown}} +% The \pkg{Markdown} package provides facilities for the conversion of markdown +% markup to plain \TeX{}. These are provided both in the form of a Lua module +% and in the form of plain \TeX{}, \LaTeX{}, and \Hologo{ConTeXt} macro +% packages that enable the direct inclusion of markdown documents inside \TeX{} +% documents. +% +% Architecturally, the package consists of the \pkg{Lunamark} Lua module by +% John MacFarlane, which was slimmed down and rewritten for the needs of the +% package. On top of \pkg{Lunamark} sits code for the plain \TeX{}, \LaTeX{}, +% and \Hologo{ConTeXt} formats by Vít Novotný. +% +% \iffalse +%<*lua> +% \fi +% \begin{macrocode} +if not modules then modules = { } end modules ['markdown'] = { + version = "1.0.1", + comment = "A module for the conversion from markdown to plain TeX", + author = "John MacFarlane, Hans Hagen, Vít Novotný", + copyright = "2009-2016 John MacFarlane, Hans Hagen; 2016 Vít Novotný", + license = "LPPL 1.3" +} +% \end{macrocode} +% +% \subsection{Feedback} +% Please use the \pkg{markdown} project page on +% GitHub\footnote{\url{https://github.com/witiko/markdown/issues}} to report +% bugs and submit feature requests. Before making a feature request, please +% ensure that you have thoroughly studied this manual. If you do not want to +% report a bug or request a feature but are simply in need of assistance, you +% might want to consider posting your question on the \TeX-\LaTeX{} Stack +% Exchange\footnote{\url{https://tex.stackexchange.com}}. +% +% \subsection{Acknowledgements} +% I would like to thank the Faculty of Informatics at the Masaryk University in +% Brno for providing me with the opportunity to work on this package alongside +% my studies. I would also like to thank the creator of the Lunamark Lua +% module, John Macfarlane, for releasing Lunamark under a permissive license +% that enabled its inclusion into the package. +% +% The \TeX{} part of the package draws inspiration from several sources +% including the source code of \Hologo{LaTeX2e}, the \pkg{minted} package by +% Geoffrey M. Poore -- which likewise tackles the issue of interfacing with an +% external interpreter from \TeX, the \pkg{filecontents} package by Scott +% Pakin, and others. +% +% \subsection{Prerequisites} +% This section gives an overview of all resources required by the package. +% +% \subsubsection{Lua Prerequisites}\label{sec:luaprerequisites} +% The Lua part of the package requires the following Lua modules: +% \begin{description} +% \item[\pkg{LPeg}] A pattern-matching library for the writing of recursive +% descent parsers via the Parsing Expression Grammars (\acro{peg}s). It is +% used by the \pkg{Lunamark} library to parse the markdown input. +% \begin{macrocode} +local lpeg = require("lpeg") +% \end{macrocode} +% \item[\pkg{Selene Unicode}] A library that provides support for the +% processing of wide strings. It is used by the \pkg{Lunamark} library to +% cast image, link, and footnote tags to the lower case. +% \begin{macrocode} +local unicode = require("unicode") +% \end{macrocode} +% \item[\pkg{MD5}] A library that provides \acro{md5} crypto functions. It is +% used by the \pkg{Lunamark} library to compute the digest of the input for +% caching purposes. +% \begin{macrocode} +local md5 = require("md5") +% \end{macrocode} +% \end{description} +% All the abovelisted modules are statically linked into the Lua\TeX{} engine +% (see \cite[Section~3.3]{luatex16}). +% +% \iffalse +%</lua> +%<*tex> +% \fi +% \subsubsection{Plain \TeX{} Prerequisites}\label{sec:texprerequisites} +% The plain \TeX{} part of the package requires the following Lua module: +% \begin{description} +% \item[\pkg{Lua File System}] A library that provides access to the +% filesystem via \acro{os}-specific syscalls. It is used by the plain +% \TeX{} code to create the cache directory specified by the +% \m{markdownOptionCacheDir} macro before interfacing with the +% \pkg{Lunamark} library. +% +% The plain \TeX{} code makes use of the \luam{isDir} method that was added +% to the module by the Lua\TeX{} engine developers (see +% \cite[Section~3.2]{luatex16}). This method is not present in the base +% library. +% \end{description} +% The \pkg{Lua File System} module is statically linked into the Lua\TeX{} +% engine (see \cite[Section~3.3]{luatex16}). +% +% The plain \TeX{} part of the package also requires that the plain \TeX{} +% format (or its superset) is loaded and that either the Lua\TeX{} +% \m{directlua} primitive or the shell access file stream 18 is available. +% +% \iffalse +%</tex> +%<*latex> +% \fi +% \subsubsection{\LaTeX{} Prerequisites}\label{sec:latexprerequisites} +% The \LaTeX{} part of the package requires that the \Hologo{LaTeX2e} format is +% loaded and also, since it uses the plain \TeX{} implementation, all the plain +% \TeX{} prerequisites (see Section \ref{sec:texprerequisites}). +% \begin{macrocode} +\NeedsTeXFormat{LaTeX2e}% +% \end{macrocode} +% +% The following \Hologo{LaTeX2e} packages are also required: +% \begin{description} +% \item[\pkg{keyval}] A package that enables the creation of parameter sets. +% This package is used to provide the \m{markdownSetup} macro, the package +% options processing, as well as the parameters of the \envm{markdown*} +% \LaTeX{} environment. +% \item[\pkg{url}] A package that provides the \m{url} macro for the +% typesetting of \acro{url}s. It is used to provide the default token renderer +% prototype (see Section \ref{sec:texrendererprototypes}) for links. +% \item[\pkg{graphicx}] A package that provides the \m{includegraphics} macro for +% the typesetting of images. It is used to provide the corresponding +% default token renderer prototype (see Section +% \ref{sec:texrendererprototypes}). +% \item[\pkg{paralist}] A package that provides the \envm{compactitem}, +% \envm{compactenum}, and \envm{compactdesc} macros for the +% typesetting of tight bulleted lists, ordered lists, and definition lists. +% It is used to provide the corresponding default token renderer prototypes +% (see Section \ref{sec:texrendererprototypes}). +% \item[\pkg{ifthen}] A package that provides a concise syntax for the +% inspection of macro values. It is used to determine whether or not the +% \pkg{paralist} package should be loaded based on the user options. +% \item[\pkg{fancyvrb}] A package that provides the \m{VerbatimInput} macros +% for the verbatim inclusion of files containing code. It is used to +% provide the corresponding default token renderer prototype (see Section +% \ref{sec:texrendererprototypes}). +% \end{description} +% +% \iffalse +%</latex> +%<*context> +% \fi +% \subsubsection{\Hologo{ConTeXt} prerequisites} +% The \Hologo{ConTeXt} part of the package requires that either the Mark II or +% the Mark IV format is loaded and also, since it uses the plain \TeX{} +% implementation, all the plain \TeX{} prerequisites (see Section +% \ref{sec:texprerequisites}). +% +% \section{User Guide}\label{sec:interfaces} +% This part of the manual describes the interfaces exposed by the package +% along with usage notes and examples. It is aimed at the user of the package. +% +% Since neither \TeX{} nor Lua provide interfaces as a language construct, the +% separation to interfaces and implementations is purely abstract. It serves as +% a means of structuring this manual and as a promise to the user that if they +% only access the package through the interfaces, the future versions of the +% package should remain backwards compatible. +% +% \iffalse +%</context> +%<*lua> +% \fi +% \subsection{Lua Interface}\label{sec:luainterface} +% The Lua interface provides the conversion from markdown to plain \TeX{}. +% This interface is used by the plain \TeX{} implementation (see Section +% \ref{sec:teximplementation}) and will be of interest to the developers of +% other packages and Lua modules. +% +% The Lua interface is implemented by the \t`markdown` Lua module. +% +% \begin{macrocode} +local M = {} +% \end{macrocode} +% +% \subsubsection{Conversion from Markdown to Plain \TeX{}} +% \label{sec:luaconversion} +% The Lua interface exposes the \luamdef{new}\t`(options)` method. This +% method creates converter functions that perform the conversion from markdown +% to plain \TeX{} according to the table \t`options` that contains options +% recognized by the Lua interface. (see Section \ref{sec:luaoptions}). The +% \t`options` parameter is optional; when unspecified, the behaviour will be +% the same as if \t`options` were an empty table. +% \begin{macrocode} +M.new = {} +% \end{macrocode} +% +% The following example Lua code converts the markdown string \t`_Hello +% world!_` to a \TeX{} output using the default options and prints the \TeX{} +% output: +% \begin{Verbatim} +% local md = require("markdown") +% local convert = md.new() +% print(convert("_Hello world!_")) +% \end{Verbatim} +% +% \subsubsection{Options}\label{sec:luaoptions} +% The Lua interface recognizes the following options. When unspecified, the +% value of a key is taken from the \luamdef{defaultOptions} table. +% \begin{macrocode} +local defaultOptions = {} +% \end{macrocode} +% \begin{optionlist} +% \Optitem[false]{blankBeforeBlockquote}{\opt{true}, \opt{false}} +% \begin{valuelist} +% \item[true] Require a blank line between a paragraph and the following +% blockquote. +% \item[false] Do not require a blank line between a paragraph and the +% following blockquote. +% \end{valuelist} +% \begin{macrocode} +defaultOptions.blankBeforeBlockquote = false +% \end{macrocode} +% +% \Optitem[false]{blankBeforeHeading}{\opt{true}, \opt{false}} +% \begin{valuelist} +% \item[true] Require a blank line between a paragraph and the following +% header. +% \item[false] Do not require a blank line between a paragraph and the +% following header. +% \end{valuelist} +% \begin{macrocode} +defaultOptions.blankBeforeHeading = false +% \end{macrocode} +% +% \Valitem[.]{cacheDir}{directory} +% The path to the directory containing auxiliary cache files. +% +% When iteratively writing and typesetting a markdown document, the cache +% files are going to accumulate over time. You are advised to clean the +% cache directory every now and then, or to set it to a temporary filesystem +% (such as \t`/tmp` on \acro{un*x} systems), which gets periodically +% emptied. +% \begin{macrocode} +defaultOptions.cacheDir = "." +% \end{macrocode} +% +% \Optitem[false]{definitionLists}{\opt{true}, \opt{false}} +% \begin{valuelist} +% \item[true] Enable the pandoc definition list syntax extension: +% \begin{Verbatim} +% Term 1 +% +% : Definition 1 +% +% Term 2 with *inline markup* +% +% : Definition 2 +% +% { some code, part of Definition 2 } +% +% Third paragraph of definition 2. +% \end{Verbatim} +% \item[false] Disable the pandoc definition list syntax extension. +% \end{valuelist} +% \begin{macrocode} +defaultOptions.definitionLists = false +% \end{macrocode} +% +% \Optitem[false]{hashEnumerators}{\opt{true}, \opt{false}} +% \begin{valuelist} +% \item[true] Enable the use of hash symbols (\t`\#`) as ordered item +% list markers. +% \item[false] Disable the use of hash symbols (\t`\#`) as ordered item +% list markers. +% \end{valuelist} +% \begin{macrocode} +defaultOptions.hashEnumerators = false +% \end{macrocode} +% +% \Optitem[false]{hybrid}{\opt{true}, \opt{false}} +% \begin{valuelist} +% \item[true] Disable the escaping of special plain \TeX{} characters, +% which makes it possible to intersperse your markdown markup with +% \TeX{} code. The intended usage is in documents prepared manually by +% a human author. In such documents, it can often be desirable to mix +% \TeX{} and markdown markup freely. +% +% \item[false] Enable the escaping of special plain \TeX{} characters +% outside verbatim environments, so that they are not interpretted by +% \TeX{}. This is encouraged when typesetting automatically generated +% content or markdown documents that were not prepared with this +% package in mind. +% \end{valuelist} +% \begin{macrocode} +defaultOptions.hybrid = false +% \end{macrocode} +% +% \Optitem[false]{footnotes}{\opt{true}, \opt{false}} +% \begin{valuelist} +% \item[true] Enable the pandoc footnote syntax extension: +% \begin{Verbatim} +% Here is a footnote reference,[^1] and another.[^longnote] +% +% [^1]: Here is the footnote. +% +% [^longnote]: Here's one with multiple blocks. +% +% Subsequent paragraphs are indented to show that they +% belong to the previous footnote. +% +% { some.code } +% +% The whole paragraph can be indented, or just the +% first line. In this way, multi-paragraph footnotes +% work like multi-paragraph list items. +% +% This paragraph won't be part of the note, because it +% isn't indented. +% \end{Verbatim} +% \item[false] Disable the pandoc footnote syntax extension. +% \end{valuelist} +% \begin{macrocode} +defaultOptions.footnotes = false +% \end{macrocode} +% +% \Optitem[false]{preserveTabs}{\opt{true}, \opt{false}} +% \begin{valuelist} +% \item[true] Preserve all tabs in the input. +% \item[false] Convert any tabs in the input to spaces. +% \end{valuelist} +% \begin{macrocode} +defaultOptions.preserveTabs = false +% \end{macrocode} +% +% \Optitem[false]{smartEllipses}{\opt{true}, \opt{false}} +% \begin{valuelist} +% \item[true] Convert any ellipses in the input to the +% \m{markdownRendererEllipsis} \TeX{} macro. +% \item[false] Preserve all ellipses in the input. +% \end{valuelist} +% \begin{macrocode} +defaultOptions.smartEllipses = false +% \end{macrocode} +% +% \Optitem[true]{startNumber}{\opt{true}, \opt{false}} +% \begin{valuelist} +% \item[true] Make the number in the first item in ordered lists +% significant. The item numbers will be passed to the +% \m{markdownRendererOlItemWithNumber} \TeX{} macro. +% \item[false] Ignore the number in the items of ordered lists. Each +% item will only produce a \m{markdownRendererOlItem} \TeX{} macro. +% \end{valuelist} +% \begin{macrocode} +defaultOptions.startNumber = true +% \end{macrocode} +% +% \Optitem[true]{tightLists}{\opt{true}, \opt{false}} +% \begin{valuelist} +% \item[true] Lists whose bullets do not consist of multiple paragraphs +% will be detected and passed to the +% \m{markdownRendererOlBeginTight}, \m{markdownRendererOlEndTight}, +% \m{markdownRendererUlBeginTight}, \m{markdownRendererUlEndTight}, +% \m{markdownRendererDlBeginTight}, and \m{markdownRendererDlEndTight} +% macros. +% \item[false] Lists whose bullets do not consist of multiple paragraphs +% will be treated the same way as lists that do. +% \end{valuelist} +% \begin{macrocode} +defaultOptions.tightLists = true +% \end{macrocode} +% \end{optionlist} +% +% \iffalse +%</lua> +%<*tex> +% \fi\subsection{Plain \TeX{} Interface}\label{sec:texinterface} +% The plain \TeX{} interface provides macros for the typesetting of markdown input +% from within plain \TeX{}, for setting the Lua interface options (see Section +% \ref{sec:luaoptions}) used during the conversion from markdown to plain +% \TeX{}, and for changing the way markdown the tokens are rendered. +% \begin{macrocode} +\def\markdownVersion{2016/06/03}% +% \end{macrocode} +% +% The plain \TeX{} interface is implemented by the \t`markdown.tex` file that +% can be loaded as follows: +% \begin{Verbatim} +% \input markdown +% \end{Verbatim} +% It is expected that the special plain \TeX{} characters have the expected +% category codes, when \m{input}ting the file. +% +% \subsubsection{Typesetting Markdown}\label{sec:textypesetting} +% The interface exposes the \mdef{markdownBegin}, \mdef{markdownEnd}, and +% \mdef{markdownInput} macros. +% +% The \m{markdownBegin} macro marks the beginning of a markdown document +% fragment and the \m{markdownEnd} macro marks its end. +% \begin{macrocode} +\let\markdownBegin\relax +\let\markdownEnd\relax +% \end{macrocode} +% You may prepend your own code to the \m{markdownBegin} macro and redefine the +% \m{markdownEnd} macro to produce special effects before and after the +% markdown block. +% +% There are several limitations to the macros you need to be aware of. +% The first limitation concerns the \m{markdownEnd} macro, which must be +% visible directly from the input line buffer (it may not be produced as a +% result of input expansion). Otherwise, it will not be recognized as the end +% of the markdown string otherwise. As a corrolary, the \m{markdownEnd} string +% may not appear anywhere inside the markdown input. +% +% Another limitation concerns spaces at the right end of an input line. In +% markdown, these are used to produce a forced line break. However, any such +% spaces are removed before the lines enter the input buffer of \TeX{} (see +% \cite[p.~46]{knuth86}). As a corrolary, the \m{markdownBegin} macro also +% ignores them. +% +% The \m{markdownBegin} and \m{markdownEnd} macros will also consume the rest +% of the lines at which they appear. In the following example plain \TeX{} +% code, the characters \t`c`, \t`e`, and \t`f` will not appear in the output. +% \begin{Verbatim} +% \input markdown +% a +% b \markdownBegin c +% d +% e \markdownEnd f +% g +% \bye +% \end{Verbatim} +% +% Note that you may also not nest the \m{markdownBegin} and \m{markdownEnd} +% macros. +% +% The following example plain \TeX{} code showcases the usage of the +% \m{markdownBegin} and \m{markdownEnd} macros: +% \begin{Verbatim} +% \input markdown +% \markdownBegin +% _Hello_ **world** ... +% \markdownEnd +% \bye +% \end{Verbatim} +% +% The \m{markdownInput} macro accepts a single parameter containing the +% filename of a markdown document and expands to the result of the conversion +% of the input markdown document to plain \TeX{}. +% \begin{macrocode} +\let\markdownInput\relax +% \end{macrocode} +% This macro is not subject to the abovelisted limitations of the +% \m{markdownBegin} and \m{markdownEnd} macros. +% +% The following example plain \TeX{} code showcases the usage of the +% \m{markdownInput} macro: +% \begin{Verbatim} +% \input markdown +% \markdownInput{hello.md} +% \bye +% \end{Verbatim} +% +% \subsubsection{Options}\label{sec:texoptions} +% The plain \TeX{} options are represented by \TeX{} macros. Some of them map +% directly to the options recognized by the Lua interface (see Section +% \ref{sec:luaoptions}), while some of them are specific to the plain \TeX{} +% interface. +% +% \paragraph{File and directory names} +% The \mdef{markdownOptionHelperScriptFileName} macro sets the filename of the +% helper Lua script file that is created during the conversion from markdown to +% plain \TeX{} in \TeX{} engines without the \m{directlua} primitive. It +% defaults to \m{jobname}\t`.markdown.lua`, where \m{jobname} is the base name +% of the document being typeset. +% +% The expansion of this macro must not contain quotation marks (\t`"`) or +% backslash symbols (\t`\textbackslash`). Mind that \TeX{} engines tend to +% put quotation marks around \m{jobname}, when it contains spaces. +% \begin{macrocode} +\def\markdownOptionHelperScriptFileName{\jobname.markdown.lua}% +% \end{macrocode} +% +% The \mdef{markdownOptionInputTempFileName} macro sets the filename of the +% temporary input file that is created during the conversion from markdown to +% plain \TeX{} in \TeX{} engines without the \m{directlua} primitive. It +% defaults to \m{jobname}\t`.markdown.out`. The same limitations as in +% the case of the \m{markdownOptionHelperScriptFileName} macro apply here. +% \begin{macrocode} +\def\markdownOptionInputTempFileName{\jobname.markdown.in}% +% \end{macrocode} +% +% The \mdef{markdownOptionOutputTempFileName} macro sets the filename of the +% temporary output file that is created during the conversion from markdown to +% plain \TeX{} in \TeX{} engines without the \m{directlua} primitive. It +% defaults to \m{jobname}\t`.markdown.out`. The same limitations apply here as +% in the case of the \m{markdownOptionHelperScriptFileName} macro. +% \begin{macrocode} +\def\markdownOptionOutputTempFileName{\jobname.markdown.out}% +% \end{macrocode} +% +% The \mdef{markdownOptionCacheDir} macro corresponds to the Lua interface +% \Opt{cacheDir} option that sets the name of the directory that will contain +% the produced cache files. The option defaults to \t`_markdown-`\m{jobname}, +% which is a similar naming scheme to the one used by the \pkg{minted} \LaTeX{} +% package. The same limitations apply here as in the case of the +% \m{markdownOptionHelperScriptFileName} macro. +% \begin{macrocode} +\def\markdownOptionCacheDir{_markdown-\jobname}% +% \end{macrocode} +% +% \paragraph{Lua Interface Options} +% The following macros map directly to the options recognized by the Lua +% interface (see Section \ref{sec:luaoptions}) and are not processed by the +% plain \TeX{} implementation, only passed along to Lua. They are undefined, which +% makes them fall back to the default values provided by the Lua interface. +% \begin{macrocode} +\let\markdownOptionBlankBeforeBlockquote\undefined +\let\markdownOptionBlankBeforeHeading\undefined +\let\markdownOptionDefinitionLists\undefined +\let\markdownOptionHashEnumerator\undefined +\let\markdownOptionHybrid\undefined +\let\markdownOptionFootnotes\undefined +\let\markdownOptionPreserveTabs\undefined +\let\markdownOptionSmartEllipses\undefined +\let\markdownOptionStartNumber\undefined +\let\markdownOptionTightLists\undefined +% \end{macrocode} +% +% \subsubsection{Token Renderers}\label{sec:texrenderersuser} +% The following \TeX{} macros may occur inside the output of the +% converter functions exposed by the Lua interface (see Section +% \ref{sec:luaconversion}) and represent the parsed markdown tokens. These +% macros are intended to be redefined by the user who is typesetting a +% document. By default, they point to the corresponding prototypes (see Section +% \ref{sec:texrendererprototypes}). +% +% \paragraph{Line Break Renderer} +% The \mdef{markdownRendererLineBreak} macro represents a forced line break. +% The macro receives no arguments. +% \begin{macrocode} +\def\markdownRendererLineBreak{% + \markdownRendererLineBreakPrototype}% +% \end{macrocode} +% +% \paragraph{Ellipsis Renderer} +% The \mdef{markdownRendererEllipsis} macro replaces any occurance of ASCII +% ellipses in the input text. This macro will only be produced, when the +% \Opt{smartEllipses} option is \t`true`. The macro receives no arguments. +% \begin{macrocode} +\def\markdownRendererEllipsis{% + \markdownRendererEllipsisPrototype}% +% \end{macrocode} +% +% \paragraph{Code Span Renderer} +% The \mdef{markdownRendererCodeSpan} macro represents inlined code span in the +% input text. It receives a single argument that corresponds to the inlined +% code span. +% \begin{macrocode} +\def\markdownRendererCodeSpan{% + \markdownRendererCodeSpanPrototype}% +% \end{macrocode} +% +% \paragraph{Link Renderer} +% The \mdef{markdownRendererLink} macro represents a hyperlink. It receives +% three arguments: the label, the \acro{uri}, and the title of the link. +% \begin{macrocode} +\def\markdownRendererLink{% + \markdownRendererLinkPrototype}% +% \end{macrocode} +% +% \paragraph{Image Renderer} +% The \mdef{markdownRendererImage} macro represents an image. It receives three +% arguments: the label, the \acro{url}, and the title of the image. +% \begin{macrocode} +\def\markdownRendererImage{% + \markdownRendererImagePrototype}% +% \end{macrocode} +% +% \paragraph{Bullet List Renderers} +% The \mdef{markdownRendererUlBegin} macro represents the beginning of a +% bulleted list that contains an item with several paragraphs of text (the +% list is not tight). The macro receives no arguments. +% \begin{macrocode} +\def\markdownRendererUlBegin{% + \markdownRendererUlBeginPrototype}% +% \end{macrocode} +% +% The \mdef{markdownRendererUlBeginTight} macro represents the beginning of a +% bulleted list that contains no item with several paragraphs of text (the list +% is tight). This macro will only be produced, when the \Opt{tightLists} option +% is \t`false`. The macro receives no arguments. +% \begin{macrocode} +\def\markdownRendererUlBeginTight{% + \markdownRendererUlBeginTightPrototype}% +% \end{macrocode} +% +% The \mdef{markdownRendererUlItem} macro represents an item in a bulleted +% list. +% The macro receives no arguments. +% \begin{macrocode} +\def\markdownRendererUlItem{% + \markdownRendererUlItemPrototype}% +% \end{macrocode} +% +% The \mdef{markdownRendererUlEnd} macro represents the end of a bulleted list +% that contains an item with several paragraphs of text (the list is not +% tight). The macro receives no arguments. +% \begin{macrocode} +\def\markdownRendererUlEnd{% + \markdownRendererUlEndPrototype}% +% \end{macrocode} +% +% The \mdef{markdownRendererUlEndTight} macro represents the end of a bulleted +% list that contains no item with several paragraphs of text (the list is +% tight). This macro will only be produced, when the \Opt{tightLists} option is +% \t`false`. The macro receives no arguments. +% \begin{macrocode} +\def\markdownRendererUlEndTight{% + \markdownRendererUlEndTightPrototype}% +% \end{macrocode} +% +% \paragraph{Ordered List Renderers} +% The \mdef{markdownRendererOlBegin} macro represents the beginning of an +% ordered list that contains an item with several paragraphs of text (the +% list is not tight). The macro receives no arguments. +% \begin{macrocode} +\def\markdownRendererOlBegin{% + \markdownRendererOlBeginPrototype}% +% \end{macrocode} +% +% The \mdef{markdownRendererOlBeginTight} macro represents the beginning of an +% ordered list that contains no item with several paragraphs of text (the +% list is tight). This macro will only be produced, when the \Opt{tightLists} +% option is \t`false`. The macro receives no arguments. +% \begin{macrocode} +\def\markdownRendererOlBeginTight{% + \markdownRendererOlBeginTightPrototype}% +% \end{macrocode} +% +% The \mdef{markdownRendererOlItem} macro represents an item in an ordered list. +% This macro will only be produced, when the \Opt{startNumber} option is +% \t`false`. The macro receives no arguments. +% \begin{macrocode} +\def\markdownRendererOlItem{% + \markdownRendererOlItemPrototype}% +% \end{macrocode} +% +% The \mdef{markdownRendererOlItemWithNumber} macro represents an item in an +% ordered list. This macro will only be produced, when the \Opt{startNumber} +% option is \t`true`. The macro receives no arguments. +% \begin{macrocode} +\def\markdownRendererOlItemWithNumber{% + \markdownRendererOlItemWithNumberPrototype}% +% \end{macrocode} +% +% The \mdef{markdownRendererOlEnd} macro represents the end of an ordered list +% that contains an item with several paragraphs of text (the list is not +% tight). The macro receives no arguments. +% \begin{macrocode} +\def\markdownRendererOlEnd{% + \markdownRendererOlEndPrototype}% +% \end{macrocode} +% +% The \mdef{markdownRendererOlEndTight} macro represents the end of an ordered +% list that contains no item with several paragraphs of text (the list is +% tight). This macro will only be produced, when the \Opt{tightLists} option is +% \t`false`. The macro receives no arguments. +% \begin{macrocode} +\def\markdownRendererOlEndTight{% + \markdownRendererOlEndTightPrototype}% +% \end{macrocode} +% +% \paragraph{Definition List Renderers} +% The following macros are only produces, when the \Opt{definitionLists} option +% is \t`true`. +% +% The \mdef{markdownRendererDlBegin} macro represents the beginning of a +% definition list that contains an item with several paragraphs of text (the +% list is not tight). The macro receives no arguments. +% \begin{macrocode} +\def\markdownRendererDlBegin{% + \markdownRendererDlBeginPrototype}% +% \end{macrocode} +% +% The \mdef{markdownRendererDlBeginTight} macro represents the beginning of a +% definition list that contains an item with several paragraphs of text (the +% list is not tight). This macro will only be produced, when the +% \Opt{tightLists} option is \t`false`. The macro receives no arguments. +% \begin{macrocode} +\def\markdownRendererDlBeginTight{% + \markdownRendererDlBeginTightPrototype}% +% \end{macrocode} +% +% The \mdef{markdownRendererDlItem} macro represents an item in a definition +% list. The macro receives a single argument that corresponds to the term being +% defined. +% \begin{macrocode} +\def\markdownRendererDlItem{% + \markdownRendererDlItemPrototype}% +% \end{macrocode} +% +% The \mdef{markdownRendererDlEnd} macro represents the end of a definition +% list that contains an item with several paragraphs of text (the list is not +% tight). The macro receives no arguments. +% \begin{macrocode} +\def\markdownRendererDlEnd{% + \markdownRendererDlEndPrototype}% +% \end{macrocode} +% +% The \mdef{markdownRendererDlEndTight} macro represents the end of a +% definition list that contains no item with several paragraphs of text (the +% list is tight). This macro will only be produced, when the \Opt{tightLists} +% option is \t`false`. The macro receives no arguments. +% \begin{macrocode} +\def\markdownRendererDlEndTight{% + \markdownRendererDlEndTightPrototype}% +% \end{macrocode} +% +% \paragraph{Emphasis Renderers} +% The \mdef{markdownRendererEmphasis} macro represents an emphasized span of +% text. The macro receives a single argument that corresponds to the emphasized +% span of text. +% \begin{macrocode} +\def\markdownRendererEmphasis{% + \markdownRendererEmphasisPrototype}% +% \end{macrocode} +% +% The \mdef{markdownRendererStrongEmphasis} macro represents a strongly +% emphasized span of text. The macro receives a single argument that +% corresponds to the emphasized span of text. +% \begin{macrocode} +\def\markdownRendererStrongEmphasis{% + \markdownRendererStrongEmphasisPrototype}% +% \end{macrocode} +% +% \paragraph{Block Quote Renderers} +% The \mdef{markdownRendererBlockQuoteBegin} macro represents the beginning of +% a block quote. The macro receives no arguments. +% \begin{macrocode} +\def\markdownRendererBlockQuoteBegin{% + \markdownRendererBlockQuoteBeginPrototype}% +% \end{macrocode} +% +% The \mdef{markdownRendererBlockQuoteEnd} macro represents the end of a block +% quote. The macro receives no arguments. +% \begin{macrocode} +\def\markdownRendererBlockQuoteEnd{% + \markdownRendererBlockQuoteEndPrototype}% +% \end{macrocode} +% +% \paragraph{Code Block Renderer} +% The \mdef{markdownRendererInputVerbatim} macro represents a code +% block. The macro receives a single argument that corresponds to the +% filename of a file contaning the code block to input. +% \begin{macrocode} +\def\markdownRendererInputVerbatim{% + \markdownRendererInputVerbatimPrototype}% +% \end{macrocode} +% +% \paragraph{Heading Renderers} +% The \mdef{markdownRendererHeadingOne} macro represents a first level heading. +% The macro receives a single argument that corresponds to the heading text. +% \begin{macrocode} +\def\markdownRendererHeadingOne{% + \markdownRendererHeadingOnePrototype}% +% \end{macrocode} +% +% The \mdef{markdownRendererHeadingTwo} macro represents a second level +% heading. The macro receives a single argument that corresponds to the heading +% text. +% \begin{macrocode} +\def\markdownRendererHeadingTwo{% + \markdownRendererHeadingTwoPrototype}% +% \end{macrocode} +% +% The \mdef{markdownRendererHeadingThree} macro represents a third level +% heading. The macro receives a single argument that corresponds to the heading +% text. +% \begin{macrocode} +\def\markdownRendererHeadingThree{% + \markdownRendererHeadingThreePrototype}% +% \end{macrocode} +% +% The \mdef{markdownRendererHeadingFour} macro represents a fourth level +% heading. The macro receives a single argument that corresponds to the heading +% text. +% \begin{macrocode} +\def\markdownRendererHeadingFour{% + \markdownRendererHeadingFourPrototype}% +% \end{macrocode} +% +% The \mdef{markdownRendererHeadingFive} macro represents a fifth level +% heading. The macro receives a single argument that corresponds to the heading +% text. +% \begin{macrocode} +\def\markdownRendererHeadingFive{% + \markdownRendererHeadingFivePrototype}% +% \end{macrocode} +% +% The \mdef{markdownRendererHeadingSix} macro represents a sixth level +% heading. The macro receives a single argument that corresponds to the heading +% text. +% \begin{macrocode} +\def\markdownRendererHeadingSix{% + \markdownRendererHeadingSixPrototype}% +% \end{macrocode} +% +% \paragraph{Horizontal Rule Renderer} +% The \mdef{markdownRendererHorizontalRule} macro represents a horizontal rule. +% The macro receives no arguments. +% \begin{macrocode} +\def\markdownRendererHorizontalRule{% + \markdownRendererHorizontalRulePrototype}% +% \end{macrocode} +% +% \paragraph{Footnote Renderer} +% The \mdef{markdownRendererFootnote} macro represents a footnote. This macro +% will only be produced, when the \Opt{footnotes} option is \t`true`. The +% macro receives a single argument that corresponds to the footnote text. +% \begin{macrocode} +\def\markdownRendererFootnote{% + \markdownRendererFootnotePrototype}% +% \end{macrocode} +% +% \subsubsection{Token Renderer Prototypes}\label{sec:texrendererprototypes} +% The following \TeX{} macros provide definitions for the token renderers (see +% Section \ref{sec:texrenderersuser}) that have not been redefined by the +% user. These macros are intended to be redefined by macro package authors +% who wish to provide sensible default token renderers. They are also redefined +% by the \LaTeX{} and \Hologo{ConTeXt} implementations (see sections +% \ref{sec:lateximplementation} and \ref{sec:contextimplementation}). +% \begin{macrocode} +\def\markdownRendererLineBreakPrototype{}% +\def\markdownRendererEllipsisPrototype{}% +\long\def\markdownRendererCodeSpanPrototype#1{}% +\long\def\markdownRendererLinkPrototype#1#2#3{}% +\long\def\markdownRendererImagePrototype#1#2#3{}% +\def\markdownRendererUlBeginPrototype{}% +\def\markdownRendererUlBeginTightPrototype{}% +\def\markdownRendererUlItemPrototype{}% +\def\markdownRendererUlEndPrototype{}% +\def\markdownRendererUlEndTightPrototype{}% +\def\markdownRendererOlBeginPrototype{}% +\def\markdownRendererOlBeginTightPrototype{}% +\def\markdownRendererOlItemPrototype{}% +\long\def\markdownRendererOlItemWithNumberPrototype#1{}% +\def\markdownRendererOlEndPrototype{}% +\def\markdownRendererOlEndTightPrototype{}% +\def\markdownRendererDlBeginPrototype{}% +\def\markdownRendererDlBeginTightPrototype{}% +\long\def\markdownRendererDlItemPrototype#1{}% +\def\markdownRendererDlEndPrototype{}% +\def\markdownRendererDlEndTightPrototype{}% +\long\def\markdownRendererEmphasisPrototype#1{}% +\long\def\markdownRendererStrongEmphasisPrototype#1{}% +\def\markdownRendererBlockQuoteBeginPrototype{}% +\def\markdownRendererBlockQuoteEndPrototype{}% +\long\def\markdownRendererInputVerbatimPrototype#1{}% +\long\def\markdownRendererHeadingOnePrototype#1{}% +\long\def\markdownRendererHeadingTwoPrototype#1{}% +\long\def\markdownRendererHeadingThreePrototype#1{}% +\long\def\markdownRendererHeadingFourPrototype#1{}% +\long\def\markdownRendererHeadingFivePrototype#1{}% +\long\def\markdownRendererHeadingSixPrototype#1{}% +\def\markdownRendererHorizontalRulePrototype{}% +\long\def\markdownRendererFootnotePrototype#1{}% +% \end{macrocode} +% +% \subsubsection{Logging Facilities} +% The \mdef{markdownInfo}, \mdef{markdownWarning}, and +% \mdef{markdownError} macros provide access to logging to the rest of +% the macros. Their first argument specifies the text of the info, warning, or +% error message. +% \begin{macrocode} +\def\markdownInfo#1{}% +\def\markdownWarning#1{}% +% \end{macrocode} +% The \m{markdownError} macro receives a second argument that provides a help +% text suggesting a remedy to the error. +% \begin{macrocode} +\def\markdownError#1{}% +% \end{macrocode} +% You may redefine these macros to redirect and process the info, warning, and +% error messages. +% +% \subsubsection{Miscellanea} +% The \mdef{markdownLuaRegisterIBCallback} and +% \mdef{markdownLuaUnregisterIBCallback} macros specify the Lua code for +% registering and unregistering a callback for changing the contents of the +% line input buffer before a \TeX{} engine that supports direct Lua access via +% the \m{directlua} macro starts looking at it. The first argument of the +% \m{markdownLuaRegisterIBCallback} macro corresponds to the callback function +% being registered. +% +% Local members defined within \m{markdownLuaRegisterIBCallback} are guaranteed +% to be visible from \m{markdownLuaUnregisterIBCallback} and the execution of +% the two macros alternates, so it is not necessary to consider the case, when +% one of the macros is called twice in a row. +% \begin{macrocode} +\def\markdownLuaRegisterIBCallback#1{% + local old_callback = callback.find("process_input_buffer") + callback.register("process_input_buffer", #1)}% +\def\markdownLuaUnregisterIBCallback{% + callback.register("process_input_buffer", old_callback)}% +% \end{macrocode} +% +% The \mdef{markdownMakeOther} macro is used by the package, when a \TeX{} +% engine that does not support direct Lua access is starting to buffer a text. +% The plain \TeX{} implementation changes the category code of plain \TeX{} +% special characters to other, but there may be other active characters that +% may break the output. This macro should temporarily change the category of +% these to \emph{other}. +% \begin{macrocode} +\let\markdownMakeOther\relax +% \end{macrocode} +% +% The \mdef{markdownReadAndConvert} macro implements the \m{markdownBegin} +% macro. The first argument specifies the token sequence that will terminate +% the markdown input (\m{markdownEnd} in the instance of the \m{markdownBegin} +% macro) when the plain \TeX{} special characters have had their category +% changed to \emph{other}. The second argument specifies the token sequence +% that will actually be inserted into the document, when the ending token +% sequence has been found. +% \begin{macrocode} +\let\markdownReadAndConvert\relax +\begingroup +% \end{macrocode} +% Locally swap the category code of the backslash symbol (\t`\textbackslash`) +% with the pipe symbol (\t`|`). This is required in order that all the special +% symbols in the first argument of the \t`markdownReadAndConvert` macro have +% the category code \emph{other}. +% \begin{macrocode} + \catcode`\|=0\catcode`\\=12% + |gdef|markdownBegin{% + |markdownReadAndConvert{\markdownEnd}% + {|markdownEnd}}% +|endgroup +% \end{macrocode} +% The macro is exposed in the interface, so that the user can create their own +% markdown environments. Due to the way the arguments are passed to Lua (see +% Section \ref{sec:directlua}), the first argument may not contain the +% string \t`]]` (regardless of the category code of the bracket symbol (\t`]`)). +% +% \iffalse +%</tex> +%<*latex> +% \fi\subsection{\LaTeX{} Interface}\label{sec:latexinterface} +% The \LaTeX{} interface provides \LaTeX{} environments for the typesetting of +% markdown input from within \LaTeX{}, facilities for setting Lua interface +% options (see Section \ref{sec:luaoptions}) used during the conversion from +% markdown to plain \TeX{}, and facilities for changing the way markdown tokens +% are rendered. The rest of the interface is inherited from the plain \TeX{} +% interface (see Section \ref{sec:texinterface}). +% +% The \LaTeX{} interface is implemented by the \t`markdown.sty` file, which +% can be loaded from the \LaTeX{} document preamble as follows: +% \begin{Verbatim}[commandchars=\\\{\}] +% \textbackslash{}usepackage[\textrm{\meta{options}}]\{markdown\} +% \end{Verbatim} +% where \meta{options} are the \LaTeX{} interface options (see Section +% \ref{sec:latexoptions}). +% +% \subsubsection{Typesetting Markdown} +% The interface exposes the \envmdef{markdown} and \envmdef{markdown*} +% \LaTeX{} environments, and redefines the \m{markdownInput} command. +% +% The \envm{markdown} and \envm{markdown*} \LaTeX{} environments are used to +% typeset markdown document fragments. The starred version of the +% \envm{markdown} environment accepts \LaTeX{} interface options (see +% Section \ref{sec:latexoptions}) as its only argument. These options will +% only influnce this markdown document fragment. +% \begin{macrocode} +\newenvironment{markdown}\relax\relax +\newenvironment{markdown*}[1]\relax\relax +% \end{macrocode} +% You may prepend your own code to the \m{markdown} macro and append your own +% code to the \m{endmarkdown} macro to produce special effects before and after +% the \envm{markdown} \LaTeX{} environment (and likewise for the starred +% version). +% +% Note that the \envm{markdown} and \envm{markdown*} \LaTeX{} environments are +% subject to the same limitations as the \m{markdownBegin} and \m{markdownEnd} +% macros exposed by the plain \TeX{} interface. +% +% The following example \LaTeX{} code showcases the usage of the +% \envm{markdown} and \envm{markdown*} environments: +% \begin{Verbatim} +% \documentclass{article} \documentclass{article} +% \usepackage{markdown} \usepackage{markdown} +% \begin{document} \begin{document} +% % ... % ... +% \begin{markdown} \begin{markdown*}{smartEllipses} +% _Hello_ **world** ... _Hello_ **world** ... +% \end{markdown} \end{markdown*} +% % ... % ... +% \end{document} \end{document} +% \end{Verbatim} +% +% The \m{markdownInput} macro accepts a single mandatory parameter containing +% the filename of a markdown document and expands to the result of the +% conversion of the input markdown document to plain \TeX{}. Unlike the +% \m{markdownInput} macro provided by the plain \TeX{} interface, this macro +% also accepts \LaTeX{} interface options (see Section \ref{sec:latexoptions}) +% as its optional argument. These options will only influnce this markdown +% document. +% +% The following example \LaTeX{} code showcases the usage of the +% \m{markdownInput} macro: +% \begin{Verbatim} +% \documentclass{article} +% \usepackage{markdown} +% \begin{document} +% % ... +% \markdownInput[smartEllipses]{hello.md} +% % ... +% \end{document} +% \end{Verbatim} +% +% \subsubsection{Options}\label{sec:latexoptions} +% The \LaTeX{} options are represented by a comma-delimited list of +% \meta{\meta{key}=\meta{value}} pairs. For boolean options, the +% \meta{=\meta{value}} part is optional, and \meta{\meta{key}} will be +% interpreted as \meta{\meta{key}=true}. +% +% The \LaTeX{} options map directly to the options recognized by the plain +% \TeX{} interface (see Section \ref{sec:texoptions}) and to the markdown token +% renderers and their prototypes recognized by the plain \TeX{} interface (see +% Sections \ref{sec:texrenderersuser} and \ref{sec:texrendererprototypes}). +% +% The \LaTeX{} options may be specified when loading the \LaTeX{} package (see +% Section \ref{sec:latexinterface}), when using the \envm{markdown*} \LaTeX{} +% environment, or via the \mdef{markdownSetup} macro. The \m{markdownSetup} +% macro receives the options to set up as its only argument. +% \begin{macrocode} +\newcommand\markdownSetup[1]{% + \setkeys{markdownOptions}{#1}}% +% \end{macrocode} +% +% \paragraph{Plain \TeX{} Interface Options} +% The following options map directly to the option macros exposed by the plain +% \TeX{} interface (see Section \ref{sec:texoptions}). +% \begin{macrocode} +\RequirePackage{keyval} +\define@key{markdownOptions}{helperScriptFileName}{% + \def\markdownOptionHelperScriptFileName{#1}}% +\define@key{markdownOptions}{inputTempFileName}{% + \def\markdownOptionInputTempFileName{#1}}% +\define@key{markdownOptions}{outputTempFileName}{% + \def\markdownOptionOutputTempFileName{#1}}% +\define@key{markdownOptions}{blankBeforeBlockquote}[true]{% + \def\markdownOptionBlankBeforeBlockquote{#1}}% +\define@key{markdownOptions}{blankBeforeHeading}[true]{% + \def\markdownOptionBlankBeforeHeading{#1}}% +\define@key{markdownOptions}{cacheDir}{% + \def\markdownOptionCacheDir{#1}}% +\define@key{markdownOptions}{definitionLists}[true]{% + \def\markdownOptionDefinitionLists{#1}}% +\define@key{markdownOptions}{hashEnumerators}[true]{% + \def\markdownOptionHashEnumerators{#1}}% +\define@key{markdownOptions}{hybrid}[true]{% + \def\markdownOptionHybrid{#1}}% +\define@key{markdownOptions}{footnotes}[true]{% + \def\markdownOptionFootnotes{#1}}% +\define@key{markdownOptions}{preserveTabs}[true]{% + \def\markdownOptionPreserveTabs{#1}}% +\define@key{markdownOptions}{smartEllipses}[true]{% + \def\markdownOptionSmartEllipses{#1}}% +\define@key{markdownOptions}{startNumber}[true]{% + \def\markdownOptionStartNumber{#1}}% +% \end{macrocode} +% If the \t`tightLists=false` option is specified, when loading the package, +% then the \pkg{paralist} package for typesetting tight lists will not be +% automatically loaded. This precaution is meant to minimize the footprint of +% this package, since some documentclasses (\pkg{beamer}) do not play nice +% with the \pkg{paralist} package. +% \begin{macrocode} +\define@key{markdownOptions}{tightLists}[true]{% + \def\markdownOptionTightLists{#1}}% +% \end{macrocode} +% +% The following example \LaTeX{} code showcases a possible configuration of +% plain \TeX{} interface options \m{markdownOptionHybrid}, +% \m{markdownOptionSmartEllipses}, and \m{markdownOptionCacheDir}. +% \begin{Verbatim} +% \markdownSetup{ +% hybrid, +% smartEllipses, +% cacheDir = /tmp, +% } +% \end{Verbatim} +% +% \paragraph{Plain \TeX{} Markdown Token Renderers} +% The \LaTeX{} interface recognizes an option with the \t`renderers` key, +% whose value must be a list of options that map directly to the markdown token +% renderer macros exposed by the plain \TeX{} interface (see Section +% \ref{sec:texrenderersuser}). +% \begin{macrocode} +\define@key{markdownOptions}{renderers}{% + \setkeys{markdownRenderers}{#1}}% +\define@key{markdownRenderers}{lineBreak}{% + \renewcommand\markdownRendererLineBreak{#1}}% +\define@key{markdownRenderers}{ellipsis}{% + \renewcommand\markdownRendererEllipsis{#1}}% +\define@key{markdownRenderers}{codeSpan}{% + \renewcommand\markdownRendererCodeSpan[1]{#1}}% +\define@key{markdownRenderers}{link}{% + \renewcommand\markdownRendererLink[3]{#1}}% +\define@key{markdownRenderers}{image}{% + \renewcommand\markdownRendererImage[3]{#1}}% +\define@key{markdownRenderers}{ulBegin}{% + \renewcommand\markdownRendererUlBegin{#1}}% +\define@key{markdownRenderers}{ulBeginTight}{% + \renewcommand\markdownRendererUlBeginTight{#1}}% +\define@key{markdownRenderers}{ulItem}{% + \renewcommand\markdownRendererUlItem{#1}}% +\define@key{markdownRenderers}{ulEnd}{% + \renewcommand\markdownRendererUlEnd{#1}}% +\define@key{markdownRenderers}{ulEndTight}{% + \renewcommand\markdownRendererUlEndTight{#1}}% +\define@key{markdownRenderers}{olBegin}{% + \renewcommand\markdownRendererOlBegin{#1}}% +\define@key{markdownRenderers}{olBeginTight}{% + \renewcommand\markdownRendererOlBeginTight{#1}}% +\define@key{markdownRenderers}{olItem}{% + \renewcommand\markdownRendererOlItem{#1}}% +\define@key{markdownRenderers}{olItemWithNumber}{% + \renewcommand\markdownRendererOlItemWithNumber[1]{#1}}% +\define@key{markdownRenderers}{olEnd}{% + \renewcommand\markdownRendererOlEnd{#1}}% +\define@key{markdownRenderers}{olEndTight}{% + \renewcommand\markdownRendererOlEndTight{#1}}% +\define@key{markdownRenderers}{dlBegin}{% + \renewcommand\markdownRendererDlBegin{#1}}% +\define@key{markdownRenderers}{dlBeginTight}{% + \renewcommand\markdownRendererDlBeginTight{#1}}% +\define@key{markdownRenderers}{dlItem}{% + \renewcommand\markdownRendererDlItem[1]{#1}}% +\define@key{markdownRenderers}{dlEnd}{% + \renewcommand\markdownRendererDlEnd{#1}}% +\define@key{markdownRenderers}{dlEndTight}{% + \renewcommand\markdownRendererDlEndTight{#1}}% +\define@key{markdownRenderers}{emphasis}{% + \renewcommand\markdownRendererEmphasis[1]{#1}}% +\define@key{markdownRenderers}{strongEmphasis}{% + \renewcommand\markdownRendererStrongEmphasis[1]{#1}}% +\define@key{markdownRenderers}{blockQuoteBegin}{% + \renewcommand\markdownRendererBlockQuoteBegin{#1}}% +\define@key{markdownRenderers}{blockQuoteEnd}{% + \renewcommand\markdownRendererBlockQuoteEnd{#1}}% +\define@key{markdownRenderers}{inputVerbatim}{% + \renewcommand\markdownRendererInputVerbatim[1]{#1}}% +\define@key{markdownRenderers}{headingOne}{% + \renewcommand\markdownRendererHeadingOne[1]{#1}}% +\define@key{markdownRenderers}{headingTwo}{% + \renewcommand\markdownRendererHeadingTwo[1]{#1}}% +\define@key{markdownRenderers}{headingThree}{% + \renewcommand\markdownRendererHeadingThree[1]{#1}}% +\define@key{markdownRenderers}{headingFour}{% + \renewcommand\markdownRendererHeadingFour[1]{#1}}% +\define@key{markdownRenderers}{headingFive}{% + \renewcommand\markdownRendererHeadingFive[1]{#1}}% +\define@key{markdownRenderers}{headingSix}{% + \renewcommand\markdownRendererHeadingSix[1]{#1}}% +\define@key{markdownRenderers}{horizontalRule}{% + \renewcommand\markdownRendererHorizontalRule{#1}}% +\define@key{markdownRenderers}{footnote}{% + \renewcommand\markdownRendererFootnote[1]{#1}}% +% \end{macrocode} +% +% The following example \LaTeX{} code showcases a possible configuration of the +% \m{markdownRendererLink} and \m{markdownRendererEmphasis} markdown token +% renderers. +% \begin{Verbatim} +% \markdownSetup{ +% renderers = { +% link = {#3}, % Render links as the link title. +% emphasis = {\emph{#1}}, % Render emphasized text via `\emph`. +% } +% } +% \end{Verbatim} +% +% \paragraph{Plain \TeX{} Markdown Token Renderer Prototypes} +% The \LaTeX{} interface recognizes an option with the \t`rendererPrototypes` +% key, whose value must be a list of options that map directly to the markdown +% token renderer prototype macros exposed by the plain \TeX{} interface (see +% Section \ref{sec:texrendererprototypes}). +% \begin{macrocode} +\define@key{markdownOptions}{rendererPrototypes}{% + \setkeys{markdownRendererPrototypes}{#1}}% +\define@key{markdownRendererPrototypes}{lineBreak}{% + \renewcommand\markdownRendererLineBreakPrototype{#1}}% +\define@key{markdownRendererPrototypes}{ellipsis}{% + \renewcommand\markdownRendererEllipsisPrototype{#1}}% +\define@key{markdownRendererPrototypes}{codeSpan}{% + \renewcommand\markdownRendererCodeSpanPrototype[1]{#1}}% +\define@key{markdownRendererPrototypes}{link}{% + \renewcommand\markdownRendererLink[3]{#1}}% +\define@key{markdownRendererPrototypes}{image}{% + \renewcommand\markdownRendererImage[3]{#1}}% +\define@key{markdownRendererPrototypes}{ulBegin}{% + \renewcommand\markdownRendererUlBeginPrototype{#1}}% +\define@key{markdownRendererPrototypes}{ulBeginTight}{% + \renewcommand\markdownRendererUlBeginTightPrototype{#1}}% +\define@key{markdownRendererPrototypes}{ulItem}{% + \renewcommand\markdownRendererUlItemPrototype{#1}}% +\define@key{markdownRendererPrototypes}{ulEnd}{% + \renewcommand\markdownRendererUlEndPrototype{#1}}% +\define@key{markdownRendererPrototypes}{ulEndTight}{% + \renewcommand\markdownRendererUlEndTightPrototype{#1}}% +\define@key{markdownRendererPrototypes}{olBegin}{% + \renewcommand\markdownRendererOlBeginPrototype{#1}}% +\define@key{markdownRendererPrototypes}{olBeginTight}{% + \renewcommand\markdownRendererOlBeginTightPrototype{#1}}% +\define@key{markdownRendererPrototypes}{olItem}{% + \renewcommand\markdownRendererOlItemPrototype{#1}}% +\define@key{markdownRendererPrototypes}{olItemWithNumber}{% + \renewcommand\markdownRendererOlItemWithNumberPrototype[1]{#1}}% +\define@key{markdownRendererPrototypes}{olEnd}{% + \renewcommand\markdownRendererOlEndPrototype{#1}}% +\define@key{markdownRendererPrototypes}{olEndTight}{% + \renewcommand\markdownRendererOlEndTightPrototype{#1}}% +\define@key{markdownRendererPrototypes}{dlBegin}{% + \renewcommand\markdownRendererDlBeginPrototype{#1}}% +\define@key{markdownRendererPrototypes}{dlBeginTight}{% + \renewcommand\markdownRendererDlBeginTightPrototype{#1}}% +\define@key{markdownRendererPrototypes}{dlItem}{% + \renewcommand\markdownRendererDlItemPrototype[1]{#1}}% +\define@key{markdownRendererPrototypes}{dlEnd}{% + \renewcommand\markdownRendererDlEndPrototype{#1}}% +\define@key{markdownRendererPrototypes}{dlEndTight}{% + \renewcommand\markdownRendererDlEndTightPrototype{#1}}% +\define@key{markdownRendererPrototypes}{emphasis}{% + \renewcommand\markdownRendererEmphasisPrototype[1]{#1}}% +\define@key{markdownRendererPrototypes}{strongEmphasis}{% + \renewcommand\markdownRendererStrongEmphasisPrototype[1]{#1}}% +\define@key{markdownRendererPrototypes}{blockQuoteBegin}{% + \renewcommand\markdownRendererBlockQuoteBeginPrototype{#1}}% +\define@key{markdownRendererPrototypes}{blockQuoteEnd}{% + \renewcommand\markdownRendererBlockQuoteEndPrototype{#1}}% +\define@key{markdownRendererPrototypes}{inputVerbatim}{% + \renewcommand\markdownRendererInputVerbatimPrototype[1]{#1}}% +\define@key{markdownRendererPrototypes}{headingOne}{% + \renewcommand\markdownRendererHeadingOnePrototype[1]{#1}}% +\define@key{markdownRendererPrototypes}{headingTwo}{% + \renewcommand\markdownRendererHeadingTwoPrototype[1]{#1}}% +\define@key{markdownRendererPrototypes}{headingThree}{% + \renewcommand\markdownRendererHeadingThreePrototype[1]{#1}}% +\define@key{markdownRendererPrototypes}{headingFour}{% + \renewcommand\markdownRendererHeadingFourPrototype[1]{#1}}% +\define@key{markdownRendererPrototypes}{headingFive}{% + \renewcommand\markdownRendererHeadingFivePrototype[1]{#1}}% +\define@key{markdownRendererPrototypes}{headingSix}{% + \renewcommand\markdownRendererHeadingSixPrototype[1]{#1}}% +\define@key{markdownRendererPrototypes}{horizontalRule}{% + \renewcommand\markdownRendererHorizontalRulePrototype{#1}}% +\define@key{markdownRendererPrototypes}{footnote}{% + \renewcommand\markdownRendererFootnotePrototype[1]{#1}}% +% \end{macrocode} +% +% The following example \LaTeX{} code showcases a possible configuration of the +% \m{markdownRendererImagePrototype} and \m{markdownRendererCodeSpanPrototype} +% markdown token renderer prototypes. +% \begin{Verbatim} +% \markdownSetup{ +% renderers = { +% image = {\includegraphics{#2}}, +% codeSpan = {\texttt{#1}}, % Render inline code via `\texttt`. +% } +% } +% \end{Verbatim} +% +% \iffalse +%</latex> +%<*context> +% \fi\subsection{\Hologo{ConTeXt} Interface}\label{sec:contextinterface} +% The \Hologo{ConTeXt} interface provides a start-stop macro pair for the +% typesetting of markdown input from within \Hologo{ConTeXt}. The rest of the +% interface is inherited from the plain \TeX{} interface (see Section +% \ref{sec:texinterface}). +% \begin{macrocode} +\writestatus{loading}{ConTeXt User Module / markdown}% +\unprotect +% \end{macrocode} +% +% The \Hologo{ConTeXt} interface is implemented by the +% \t`t-markdown.tex` \Hologo{ConTeXt} module file that can be loaded as follows: +% \begin{Verbatim} +% \usemodule[t][markdown] +% \end{Verbatim} +% It is expected that the special plain \TeX{} characters have the expected +% category codes, when \m{input}ting the file. +% +% \subsubsection{Typesetting Markdown} +% The interface exposes the \mdef{startmarkdown} and \mdef{stopmarkdown} macro +% pair for the typesetting of a markdown document fragment. +% \begin{macrocode} +\let\startmarkdown\relax +\let\stopmarkdown\relax +% \end{macrocode} +% You may prepend your own code to the \m{startmarkdown} macro and redefine the +% \m{stopmarkdown} macro to produce special effects before and after the +% markdown block. +% +% Note that the \m{startmarkdown} and \m{stopmarkdown} macros +% are subject to the same limitations as the \m{markdownBegin} and +% \m{markdownEnd} macros exposed by the plain \TeX{} interface. +% +% The following example \Hologo{ConTeXt} code showcases the usage of the +% \m{startmarkdown} and \m{stopmarkdown} macros: +% \begin{Verbatim} +% \usemodule[t][markdown] +% \starttext +% \startmarkdown +% _Hello_ **world** ... +% \stopmarkdown +% \stoptext +% \end{Verbatim} +% +% \section{Technical Documentation}\label{sec:implementation} +% This part of the manual describes the implementation of the interfaces +% exposed by the package (see Section \ref{sec:interfaces}) and is aimed at the +% developers of the package, as well as the curious users. +% +% \iffalse +%</context> +%<*lua> +% \fi\subsection{Lua Implementation}\label{sec:luaimplementation} +% The Lua implementation implements \luamdef{writer} and \luamdef{reader} +% objects that provide the conversion from markdown to plain \TeX{}. +% +% The Lunamark Lua module implements writers for the conversion to various +% other formats, such as DocBook, Groff, or \acro{HTML}. These were stripped +% from the module and the remaining markdown reader and plain \TeX{} writer +% were hidden behind the converter functions exposed by the Lua interface (see +% Section \ref{sec:luainterface}). +% +% \begin{macrocode} +local upper, gsub, format, length = + string.upper, string.gsub, string.format, string.len +local concat = table.concat +local P, R, S, V, C, Cg, Cb, Cmt, Cc, Ct, B, Cs, any = + lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.C, lpeg.Cg, lpeg.Cb, + lpeg.Cmt, lpeg.Cc, lpeg.Ct, lpeg.B, lpeg.Cs, lpeg.P(1) +% \end{macrocode} +% +% \subsubsection{Utility Functions} +% This section documents the utility functions used by the Lua code. These +% functions are encapsulated in the \t`util` object. The functions were +% originally located in the \t`lunamark/util.lua` file in the Lunamark Lua +% module. +% \begin{macrocode} +local util = {} +% \end{macrocode} +% +% The \luamdef{util.err} method prints an error message \t`msg` and exits. +% If \t`exit_code` is provided, it specifies the exit code. Otherwise, the +% exit code will be 1. +% \begin{macrocode} +function util.err(msg, exit_code) + io.stderr:write("markdown.lua: " .. msg .. "\n") + os.exit(exit_code or 1) +end +% \end{macrocode} +% +% The \luamdef{util.cache} method computes the digest of \t`string` and +% \t`salt`, adds the \t`suffix` and looks into the directory \t`dir`, whether a +% file with such a name exists. If it does not, it gets created with +% \t`transform(string)` as its content. The filename is then returned. +% \begin{macrocode} +function util.cache(dir, string, salt, transform, suffix) + local digest = md5.sumhexa(string .. (salt or "")) + local name = util.pathname(dir, digest .. suffix) + local file = io.open(name, "r") + if file == nil then -- If no cache entry exists, then create a new one. + local file = assert(io.open(name, "w")) + local result = string + if transform ~= nil then + result = transform(result) + end + assert(file:write(result)) + assert(file:close()) + end + return name +end +% \end{macrocode} +% +% The \luamdef{util.table_copy} method creates a shallow copy of a table \t`t` +% and its metatable. +% \begin{macrocode} +function util.table_copy(t) + local u = { } + for k, v in pairs(t) do u[k] = v end + return setmetatable(u, getmetatable(t)) +end +% \end{macrocode} +% +% The \luamdef{util.expand_tabs_in_line} expands tabs in string \t`s`. If +% \t`tabstop` is specified, it is used as the tab stop width. Otherwise, +% the tab stop width of 4 characters is used. The method is a copy of the tab +% expansion algorithm from \cite[Chapter~21]{ierusalimschy13}. +% \begin{macrocode} +function util.expand_tabs_in_line(s, tabstop) + local tab = tabstop or 4 + local corr = 0 + return (s:gsub("()\t", function(p) + local sp = tab - (p - 1 + corr) % tab + corr = corr - 1 + sp + return string.rep(" ", sp) + end)) +end +% \end{macrocode} +% +% The \luamdef{util.walk} method walks a rope \t`t`, applying a function \t`f` +% to each leaf element in order. A rope is an array whose elements may be +% ropes, strings, numbers, or functions. If a leaf element is a function, call +% it and get the return value before proceeding. +% \begin{macrocode} +function util.walk(t, f) + local typ = type(t) + if typ == "string" then + f(t) + elseif typ == "table" then + local i = 1 + local n + n = t[i] + while n do + util.walk(n, f) + i = i + 1 + n = t[i] + end + elseif typ == "function" then + local ok, val = pcall(t) + if ok then + util.walk(val,f) + end + else + f(tostring(t)) + end +end +% \end{macrocode} +% +% The \luamdef{util.flatten} method flattens an array \t`ary` that does not +% contain cycles and returns the result. +% \begin{macrocode} +function util.flatten(ary) + local new = {} + for _,v in ipairs(ary) do + if type(v) == "table" then + for _,w in ipairs(util.flatten(v)) do + new[#new + 1] = w + end + else + new[#new + 1] = v + end + end + return new +end +% \end{macrocode} +% +% The \luamdef{util.rope_to_string} method converts a rope \t`rope` to a +% string and returns it. For the definition of a rope, see the definition of +% the \luam{util.walk} method. +% \begin{macrocode} +function util.rope_to_string(rope) + local buffer = {} + util.walk(rope, function(x) buffer[#buffer + 1] = x end) + return table.concat(buffer) +end +% \end{macrocode} +% +% The \luamdef{util.rope_last} method retrieves the last item in a rope. For +% the definition of a rope, see the definition of the \luam{util.walk} method. +% \begin{macrocode} +function util.rope_last(rope) + if #rope == 0 then + return nil + else + local l = rope[#rope] + if type(l) == "table" then + return util.rope_last(l) + else + return l + end + end +end +% \end{macrocode} +% +% Given an array \t`ary` and a string \t`x`, the \luamdef{util.intersperse} +% method returns an array \t`new`, such that \t`ary[i] == new[2*(i-1)+1]` and +% \t`new[2*i] == x` for all $1\leq\t`i`\leq\t`\#ary`$. +% \begin{macrocode} +function util.intersperse(ary, x) + local new = {} + local l = #ary + for i,v in ipairs(ary) do + local n = #new + new[n + 1] = v + if i ~= l then + new[n + 2] = x + end + end + return new +end +% \end{macrocode} +% +% Given an array \t`ary` and a function \t`f`, the \luamdef{util.map} method +% returns an array \t`new`, such that \t`new[i] == f(ary[i])` for all +% $1\leq\t`i`\leq\t`\#ary`$. +% \begin{macrocode} +function util.map(ary, f) + local new = {} + for i,v in ipairs(ary) do + new[i] = f(v) + end + return new +end +% \end{macrocode} +% +% Given a table \t`char_escapes` mapping escapable characters to escaped +% strings and optionally a table \t`string_escapes` mapping escapable strings +% to escaped strings, the \luamdef{util.escaper} method returns an escaper +% function that escapes all occurances of escapable strings and characters (in +% this order). +% +% The method uses \pkg{LPeg}, which is faster than the Lua \t`string.gsub` +% built-in method. +% \begin{macrocode} +function util.escaper(char_escapes, string_escapes) +% \end{macrocode} +% Build a string of escapable characters. +% \begin{macrocode} + local char_escapes_list = "" + for i,_ in pairs(char_escapes) do + char_escapes_list = char_escapes_list .. i + end +% \end{macrocode} +% Create an \pkg{LPeg} capture \t`escapable` that produces the escaped string +% corresponding to the matched escapable character. +% \begin{macrocode} + local escapable = S(char_escapes_list) / char_escapes +% \end{macrocode} +% If \t`string_escapes` is provided, turn \t`escapable` into the +% {\catcode`\_=8\[ +% \sum_{(\t`k`,\t`v`)\in\t`string\_escapes`}\t`P(k) / v` + \t`escapable` +% \]}^^A +% capture that replaces any occurance of the string \t`k` with the string +% \t`v` for each $(\t`k`, \t`v`)\in\t`string\_escapes`$. Note that the pattern +% summation is not commutative and the its operands are inspected in the +% summation order during the matching. As a corrolary, the strings always +% take precedence over the characters. +% \begin{macrocode} + if string_escapes then + for k,v in pairs(string_escapes) do + escapable = P(k) / v + escapable + end + end +% \end{macrocode} +% Create an \pkg{LPeg} capture \t`escape_string` that captures anything +% \t`escapable` does and matches any other unmatched characters. +% \begin{macrocode} + local escape_string = Cs((escapable + any)^0) +% \end{macrocode} +% Return a function that matches the input string \t`s` against the +% \t`escape_string` capture. +% \begin{macrocode} + return function(s) + return lpeg.match(escape_string, s) + end +end +% \end{macrocode} +% +% The \luamdef{util.pathname} method produces a pathname out of a directory +% name \t`dir` and a filename \t`file` and returns it. +% \begin{macrocode} +function util.pathname(dir, file) + if #dir == 0 then + return file + else + return dir .. "/" .. file + end +end +% \end{macrocode} +% \subsubsection{Plain \TeX{} Writer}\label{sec:texwriter} +% This section documents the \luam{writer} object, which implements the +% routines for producing the \TeX{} output. The object is an amalgamate of the +% generic, \TeX{}, \LaTeX{} writer objects that were located in the +% \t`lunamark/writer/generic.lua`, \t`lunamark/writer/tex.lua`, and +% \t`lunamark/writer/latex.lua` files in the Lunamark Lua module. +% +% Although not specified in the Lua interface (see Section +% \ref{sec:luainterface}), the \luam{writer} object is exported, so that the +% curious user could easily tinker with the methods of the objects produced by +% the \luam{writer.new} method described below. The user should be aware, +% however, that the implementation may change in a future revision. +% \begin{macrocode} +M.writer = {} +% \end{macrocode} +% +% The \luamdef{writer.new} method creates and returns a new \TeX{} writer +% object associated with the Lua interface options (see Section +% \ref{sec:luaoptions}) \t`options`. When \t`options` are unspecified, it is +% assumed that an empty table was passed to the method. +% +% The objects produced by the \luam{writer.new} method expose instance methods +% and variables of their own. As a convention, I will refer to these +% \meta{member}s as \t`writer->`\meta{member}. +% \begin{macrocode} +function M.writer.new(options) + local self = {} + options = options or {} +% \end{macrocode} +% +% Make the \t`options` table inherit from the \luam{defaultOptions} table. +% \begin{macrocode} + setmetatable(options, { __index = function (_, key) + return defaultOptions[key] end }) +% \end{macrocode} +% +% Define \luamdef{writer->suffix} as the suffix of the produced cache files. +% \begin{macrocode} + self.suffix = ".tex" +% \end{macrocode} +% +% Define \luamdef{writer->space} as the output format of a space character. +% \begin{macrocode} + self.space = " " +% \end{macrocode} +% +% Define \luamdef{writer->plain} as a function that will transform an input +% plain text block \t`s` to the output format. +% \begin{macrocode} + function self.plain(s) + return s + end +% \end{macrocode} +% +% Define \luamdef{writer->paragraph} as a function that will transform an +% input paragraph \t`s` to the output format. +% \begin{macrocode} + function self.paragraph(s) + return s + end +% \end{macrocode} +% +% Define \luamdef{writer->pack} as a function that will take the filename +% \t`name` of the output file prepared by the reader and transform it to the +% output format. +% \begin{macrocode} + function self.pack(name) + return [[\input"]] .. name .. [["\relax]] + end +% \end{macrocode} +% +% Define \luamdef{writer->interblocksep} as the output format of a block +% element separator. +% \begin{macrocode} + self.interblocksep = "\n\n" +% \end{macrocode} +% +% Define \luamdef{writer->containersep} as the output format of a container +% separator. +% \begin{macrocode} + self.containersep = "\n\n" +% \end{macrocode} +% +% Define \luamdef{writer->eof} as the end of file marker in the output format. +% \begin{macrocode} + self.eof = [[\relax]] +% \end{macrocode} +% +% Define \luamdef{writer->linebreak} as the output format of a forced line break. +% \begin{macrocode} + self.linebreak = "\\markdownRendererLineBreak " +% \end{macrocode} +% +% Define \luamdef{writer->ellipsis} as the output format of an ellipsis. +% \begin{macrocode} + self.ellipsis = "\\markdownRendererEllipsis{}" +% \end{macrocode} +% +% Define \luamdef{writer->hrule} as the output format of a horizontal rule. +% \begin{macrocode} + self.hrule = "\\markdownRendererHorizontalRule " +% \end{macrocode} +% +% Define a table \luamdef{escaped} containing the mapping from special plain +% \TeX{} characters to their escaped variants. +% \begin{macrocode} + local escaped = { + ["{"] = "\\{", + ["}"] = "\\}", + ["$"] = "\\$", + ["%"] = "\\%", + ["&"] = "\\&", + ["_"] = "\\_", + ["#"] = "\\#", + ["^"] = "\\^{}", + ["\\"] = "\\char92{}", + ["~"] = "\\char126{}", + ["|"] = "\\char124{}", } +% \end{macrocode} +% Use the \luam{escaped} table to create an escaper function \luamdef{escape}. +% \begin{macrocode} + local escape = util.escaper(escaped) +% \end{macrocode} +% +% Define \luamdef{writer->string} as a function that will transform an input +% plain text span \t`s` to the output format. If the \Opt{hybrid} option is +% \t`true`, use an identity function. Otherwise, use the \luam{escape} +% function. +% \begin{macrocode} + if options.hybrid then + self.string = function(s) return s end + else + self.string = escape + end +% \end{macrocode} +% +% Define \luamdef{writer->code} as a function that will transform an input +% inlined code span \t`s` to the output format. +% \begin{macrocode} + function self.code(s) + return {"\\markdownRendererCodeSpan{",escape(s),"}"} + end +% \end{macrocode} +% +% Define \luamdef{writer->link} as a function that will transform an input +% hyperlink to the output format, where \t`lab` corresponds to thelabel, +% \t`src` to \acro{uri}, and \t`tit` to the title of the link. +% \begin{macrocode} + function self.link(lab,src,tit) + return {"\\markdownRendererLink{",lab,"}", + "{",self.string(src),"}", + "{",self.string(tit),"}"} + end +% \end{macrocode} +% +% Define \luamdef{writer->image} as a function that will transform an input +% image to the output format, where \t`lab` corresponds to the label, \t`src` +% to the \acro{url}, and \t`tit` to the title of the image. +% \begin{macrocode} + function self.image(lab,src,tit) + return {"\\markdownRendererImage{",lab,"}", + "{",self.string(src),"}", + "{",self.string(tit),"}"} + end +% \end{macrocode} +% +% Define \luamdef{writer->bulletlist} as a function that will transform an input +% bulleted list to the output format, where \t`items` is an array of the list +% items and \t`tight` specifies, whether the list is tight or not. +% \begin{macrocode} + local function ulitem(s) + return {"\\markdownRendererUlItem ",s} + end + + function self.bulletlist(items,tight) + local buffer = {} + for _,item in ipairs(items) do + buffer[#buffer + 1] = ulitem(item) + end + local contents = util.intersperse(buffer,"\n") + if tight and options.tightLists then + return {"\\markdownRendererUlBeginTight\n",contents, + "\n\\markdownRendererUlEndTight "} + else + return {"\\markdownRendererUlBegin\n",contents, + "\n\\markdownRendererUlEnd "} + end + end +% \end{macrocode} +% +% Define \luamdef{writer->ollist} as a function that will transform an input +% ordered list to the output format, where \t`items` is an array of the list +% items and \t`tight` specifies, whether the list is tight or not. If the +% optional parameter \t`startnum` is present, it should be used as the number +% of the first list item. +% \begin{macrocode} + local function olitem(s,num) + if num ~= nil then + return {"\\markdownRendererOlItemWithNumber{",num,"}",s} + else + return {"\\markdownRendererOlItem ",s} + end + end + + function self.orderedlist(items,tight,startnum) + local buffer = {} + local num = startnum + for _,item in ipairs(items) do + buffer[#buffer + 1] = olitem(item,num) + if num ~= nil then + num = num + 1 + end + end + local contents = util.intersperse(buffer,"\n") + if tight and options.tightLists then + return {"\\markdownRendererOlBeginTight\n",contents, + "\n\\markdownRendererOlEndTight "} + else + return {"\\markdownRendererOlBegin\n",contents, + "\n\\markdownRendererOlEnd "} + end + end +% \end{macrocode} +% +% Define \luamdef{writer->definitionlist} as a function that will transform an +% input definition list to the output format, where \t`items` is an array of +% tables, each of the form \t`\{ term = t, definitions = defs \}`, where \t`t` +% is a term and \t`defs` is an array of definitions. \t`tight` specifies, +% whether the list is tight or not. +% \begin{macrocode} + local function dlitem(term,defs) + return {"\\markdownRendererDlItem{",term,"}\n",defs} + end + + function self.definitionlist(items,tight) + local buffer = {} + for _,item in ipairs(items) do + buffer[#buffer + 1] = dlitem(item.term, + util.intersperse(item.definitions, self.interblocksep)) + end + local contents = util.intersperse(buffer, self.containersep) + if tight and options.tightLists then + return {"\\markdownRendererDlBeginTight\n\n", contents, + "\n\n\\markdownRendererDlEndTight\n"} + else + return {"\\markdownRendererDlBegin\n\n", contents, + "\n\n\\markdownRendererDlEnd\n"} + end + end +% \end{macrocode} +% +% Define \luamdef{writer->emphasis} as a function that will transform an +% emphasized span \t`s` of input text to the output format. +% \begin{macrocode} + function self.emphasis(s) + return {"\\markdownRendererEmphasis{",s,"}"} + end +% \end{macrocode} +% +% Define \luamdef{writer->strong} as a function that will transform a strongly +% emphasized span \t`s` of input text to the output format. +% \begin{macrocode} + function self.strong(s) + return {"\\markdownRendererStrongEmphasis{",s,"}"} + end +% \end{macrocode} +% +% Define \luamdef{writer->blockquote} as a function that will transform an +% input block quote \t`s` to the output format. +% \begin{macrocode} + function self.blockquote(s) + return {"\\markdownRendererBlockQuoteBegin\n",s, + "\n\\markdownRendererBlockQuoteEnd "} + end +% \end{macrocode} +% +% Define \luamdef{writer->verbatim} as a function that will transform an +% input code block \t`s` to the output format. +% \begin{macrocode} + function self.verbatim(s) + local name = util.cache(options.cacheDir, s, nil, nil, ".verbatim") + return {"\\markdownRendererInputVerbatim{",name,"}"} + end +% \end{macrocode} +% +% Define \luamdef{writer->heading} as a function that will transform an +% input heading \t`s` at level \t`level` to the output format. +% \begin{macrocode} + function self.heading(s,level) + local cmd + if level == 1 then + cmd = "\\markdownRendererHeadingOne" + elseif level == 2 then + cmd = "\\markdownRendererHeadingTwo" + elseif level == 3 then + cmd = "\\markdownRendererHeadingThree" + elseif level == 4 then + cmd = "\\markdownRendererHeadingFour" + elseif level == 5 then + cmd = "\\markdownRendererHeadingFive" + elseif level == 6 then + cmd = "\\markdownRendererHeadingSix" + else + cmd = "" + end + return {cmd,"{",s,"}"} + end +% \end{macrocode} +% +% Define \luamdef{writer->footnote} as a function that will transform an +% input footnote \t`s` to the output format. +% \begin{macrocode} + function self.note(s) + return {"\\markdownRendererFootnote{",s,"}"} + end + + return self +end +% \end{macrocode} +% +% \subsubsection{Markdown Reader}\label{sec:markdownreader} +% This section documents the \luam{reader} object, which implements the +% routines for parsing the markdown input. The object corresponds to the +% markdown reader object that was located in the +% \t`lunamark/reader/markdown.lua` file in the Lunamark Lua module. +% +% Although not specified in the Lua interface (see Section +% \ref{sec:luainterface}), the \luam{reader} object is exported, so that the +% curious user could easily tinker with the methods of the objects produced by +% the \luam{reader.new} method described below. The user should be aware, +% however, that the implementation may change in a future revision. +% +% The \luamdef{reader.new} method creates and returns a new \TeX{} reader +% object associated with the Lua interface options (see Section +% \ref{sec:luaoptions}) \t`options` and with a writer object \t`writer`. When +% \t`options` are unspecified, it is assumed that an empty table was passed to +% the method. +% +% The objects produced by the \luam{reader.new} method expose instance methods +% and variables of their own. As a convention, I will refer to these +% \meta{member}s as \t`reader->`\meta{member}. +% \begin{macrocode} +M.reader = {} +function M.reader.new(writer, options) + local self = {} + options = options or {} +% \end{macrocode} +% +% Make the \t`options` table inherit from the \luam{defaultOptions} table. +% \begin{macrocode} + setmetatable(options, { __index = function (_, key) + return defaultOptions[key] end }) +% \end{macrocode} +% +% \paragraph{Top Level Helper Functions} +% Define \luamdef{normalize_tag} as a function that normalizes a markdown +% reference tag by lowercasing it, and by collapsing any adjacent whitespace +% characters. +% \begin{macrocode} + local function normalize_tag(tag) + return unicode.utf8.lower( + gsub(util.rope_to_string(tag), "[ \n\r\t]+", " ")) + end +% \end{macrocode} +% +% Define \luamdef{expandtabs} either as an identity function, when the +% \Opt{preserveTabs} Lua inrerface option is \t`true`, or to a function that +% expands tabs into spaces otherwise. +% \begin{macrocode} + local expandtabs + if options.preserveTabs then + expandtabs = function(s) return s end + else + expandtabs = function(s) + if s:find("\t") then + return s:gsub("[^\n]*", util.expand_tabs_in_line) + else + return s + end + end + end +% \end{macrocode} +% \paragraph{Top Level Parsing Functions} +% \begin{macrocode} + local syntax + local blocks + local inlines + + local parse_blocks = + function(str) + local res = lpeg.match(blocks, str) + if res == nil then + error(format("parse_blocks failed on:\n%s", str:sub(1,20))) + else + return res + end + end + + local parse_inlines = + function(str) + local res = lpeg.match(inlines, str) + if res == nil then + error(format("parse_inlines failed on:\n%s", + str:sub(1,20))) + else + return res + end + end + + local parse_inlines_no_link = + function(str) + local res = lpeg.match(inlines_no_link, str) + if res == nil then + error(format("parse_inlines_no_link failed on:\n%s", + str:sub(1,20))) + else + return res + end + end +% \end{macrocode} +% \paragraph{Generic \acro{peg} Patterns} +% \begin{macrocode} + local percent = P("%") + local asterisk = P("*") + local dash = P("-") + local plus = P("+") + local underscore = P("_") + local period = P(".") + local hash = P("#") + local ampersand = P("&") + local backtick = P("`") + local less = P("<") + local more = P(">") + local space = P(" ") + local squote = P("'") + local dquote = P('"') + local lparent = P("(") + local rparent = P(")") + local lbracket = P("[") + local rbracket = P("]") + local circumflex = P("^") + local slash = P("/") + local equal = P("=") + local colon = P(":") + local semicolon = P(";") + local exclamation = P("!") + + local digit = R("09") + local hexdigit = R("09","af","AF") + local letter = R("AZ","az") + local alphanumeric = R("AZ","az","09") + local keyword = letter * alphanumeric^0 + + local doubleasterisks = P("**") + local doubleunderscores = P("__") + local fourspaces = P(" ") + + local any = P(1) + local fail = any - 1 + local always = P("") + + local escapable = S("\\`*_{}[]()+_.!#-~:^") + local anyescaped = P("\\") / "" * escapable + + any + + local tab = P("\t") + local spacechar = S("\t ") + local spacing = S(" \n\r\t") + local newline = P("\n") + local nonspacechar = any - spacing + local tightblocksep = P("\001") + + local specialchar + if options.smartEllipses then + specialchar = S("*_`&[]!\\.") + else + specialchar = S("*_`&[]!\\") + end + + local normalchar = any - + (specialchar + spacing + tightblocksep) + local optionalspace = spacechar^0 + local spaces = spacechar^1 + local eof = - any + local nonindentspace = space^-3 * - spacechar + local indent = space^-3 * tab + + fourspaces / "" + local linechar = P(1 - newline) + + local blankline = optionalspace * newline / "\n" + local blanklines = blankline^0 + local skipblanklines = (optionalspace * newline)^0 + local indentedline = indent /"" * C(linechar^1 * newline^-1) + local optionallyindentedline = indent^-1 /"" * C(linechar^1 * newline^-1) + local sp = spacing^0 + local spnl = optionalspace * (newline * optionalspace)^-1 + local line = linechar^0 * newline + + linechar^1 * eof + local nonemptyline = line - blankline + + local chunk = line * (optionallyindentedline - blankline)^0 + + -- block followed by 0 or more optionally + -- indented blocks with first line indented. + local function indented_blocks(bl) + return Cs( bl + * (blankline^1 * indent * -blankline * bl)^0 + * blankline^1 ) + end +% \end{macrocode} +% \paragraph{List \acro{peg} Patterns} +% \begin{macrocode} + local bulletchar = C(plus + asterisk + dash) + + local bullet = ( bulletchar * #spacing * (tab + space^-3) + + space * bulletchar * #spacing * (tab + space^-2) + + space * space * bulletchar * #spacing * (tab + space^-1) + + space * space * space * bulletchar * #spacing + ) * -bulletchar + + if options.hashEnumerators then + dig = digit + hash + else + dig = digit + end + + local enumerator = C(dig^3 * period) * #spacing + + C(dig^2 * period) * #spacing * (tab + space^1) + + C(dig * period) * #spacing * (tab + space^-2) + + space * C(dig^2 * period) * #spacing + + space * C(dig * period) * #spacing * (tab + space^-1) + + space * space * C(dig^1 * period) * #spacing +% \end{macrocode} +% \paragraph{Code Span \acro{peg} Patterns} +% \begin{macrocode} + local openticks = Cg(backtick^1, "ticks") + + local function captures_equal_length(s,i,a,b) + return #a == #b and i + end + + local closeticks = space^-1 * + Cmt(C(backtick^1) * Cb("ticks"), captures_equal_length) + + local intickschar = (any - S(" \n\r`")) + + (newline * -blankline) + + (space - closeticks) + + (backtick^1 - closeticks) + + local inticks = openticks * space^-1 * C(intickschar^1) * closeticks +% \end{macrocode} +% \paragraph{Tag \acro{peg} Patterns} +% \begin{macrocode} + local leader = space^-3 + + -- in balanced brackets, parentheses, quotes: + local bracketed = P{ lbracket + * ((anyescaped - (lbracket + rbracket + + blankline^2)) + V(1))^0 + * rbracket } + + local inparens = P{ lparent + * ((anyescaped - (lparent + rparent + + blankline^2)) + V(1))^0 + * rparent } + + local squoted = P{ squote * alphanumeric + * ((anyescaped - (squote + blankline^2)) + + V(1))^0 + * squote } + + local dquoted = P{ dquote * alphanumeric + * ((anyescaped - (dquote + blankline^2)) + + V(1))^0 + * dquote } + + -- bracketed 'tag' for markdown links, allowing nested brackets: + local tag = lbracket + * Cs((alphanumeric^1 + + bracketed + + inticks + + (anyescaped - (rbracket + blankline^2)))^0) + * rbracket + + -- url for markdown links, allowing balanced parentheses: + local url = less * Cs((anyescaped-more)^0) * more + + Cs((inparens + (anyescaped-spacing-rparent))^1) + + -- quoted text possibly with nested quotes: + local title_s = squote * Cs(((anyescaped-squote) + squoted)^0) * + squote + + local title_d = dquote * Cs(((anyescaped-dquote) + dquoted)^0) * + dquote + + local title_p = lparent + * Cs((inparens + (anyescaped-rparent))^0) + * rparent + + local title = title_d + title_s + title_p + + local optionaltitle = spnl * title * spacechar^0 + + Cc("") +% \end{macrocode} +% \paragraph{Footnote \acro{peg} Patterns} +% \begin{macrocode} + local rawnotes = {} + + local function strip_first_char(s) + return s:sub(2) + end + + -- like indirect_link + local function lookup_note(ref) + return function() + local found = rawnotes[normalize_tag(ref)] + if found then + return writer.note(parse_blocks(found)) + else + return {"[^", ref, "]"} + end + end + end + + local function register_note(ref,rawnote) + rawnotes[normalize_tag(ref)] = rawnote + return "" + end + + local RawNoteRef = #(lbracket * circumflex) * tag / strip_first_char + + local NoteRef = RawNoteRef / lookup_note + + local NoteBlock + + if options.footnotes then + NoteBlock = leader * RawNoteRef * colon * spnl * + indented_blocks(chunk) / register_note + else + NoteBlock = fail + end +% \end{macrocode} +% \paragraph{Link and Image \acro{peg} Patterns} +% \begin{macrocode} + -- List of references defined in the document + local references + + -- add a reference to the list + local function register_link(tag,url,title) + references[normalize_tag(tag)] = { url = url, title = title } + return "" + end + + -- parse a reference definition: [foo]: /bar "title" + local define_reference_parser = + leader * tag * colon * spacechar^0 * url * optionaltitle * blankline^1 + + -- lookup link reference and return either + -- the link or nil and fallback text. + local function lookup_reference(label,sps,tag) + local tagpart + if not tag then + tag = label + tagpart = "" + elseif tag == "" then + tag = label + tagpart = "[]" + else + tagpart = {"[", parse_inlines(tag), "]"} + end + if sps then + tagpart = {sps, tagpart} + end + local r = references[normalize_tag(tag)] + if r then + return r + else + return nil, {"[", parse_inlines(label), "]", tagpart} + end + end + + -- lookup link reference and return a link, if the reference is found, + -- or a bracketed label otherwise. + local function indirect_link(label,sps,tag) + return function() + local r,fallback = lookup_reference(label,sps,tag) + if r then + return writer.link(parse_inlines_no_link(label), r.url, r.title) + else + return fallback + end + end + end + + -- lookup image reference and return an image, if the reference is found, + -- or a bracketed label otherwise. + local function indirect_image(label,sps,tag) + return function() + local r,fallback = lookup_reference(label,sps,tag) + if r then + return writer.image(writer.string(label), r.url, r.title) + else + return {"!", fallback} + end + end + end +% \end{macrocode} +% \paragraph{Inline Element \acro{peg} Patterns} +% \begin{macrocode} + local Inline = V("Inline") + + local Str = normalchar^1 / writer.string + + local Ellipsis = P("...") / writer.ellipsis + + local Smart = Ellipsis + + local Symbol = (specialchar - tightblocksep) / writer.string + + local Code = inticks / writer.code + + local bqstart = more + local headerstart = hash + + (line * (equal^1 + dash^1) * optionalspace * newline) + + if options.blankBeforeBlockquote then + bqstart = fail + end + + if options.blankBeforeHeading then + headerstart = fail + end + + local Endline = newline * -( -- newline, but not before... + blankline -- paragraph break + + tightblocksep -- nested list + + eof -- end of document + + bqstart + + headerstart + ) * spacechar^0 / writer.space + +% \end{macrocode} +% Make two and more trailing spaces before a newline produce a forced line +% break, throw away one or more trailing spaces and an optional newline at the +% end of a file, and reduce one or more spaces and an optional newline into a +% single space. +% \begin{macrocode} + local Space = spacechar^2 * Endline / writer.linebreak + + spacechar^1 * Endline^-1 * eof / "" + + spacechar^1 * Endline^-1 * optionalspace / writer.space + + -- parse many p between starter and ender + local function between(p, starter, ender) + local ender2 = B(nonspacechar) * ender + return (starter * #nonspacechar * Ct(p * (p - ender2)^0) * ender2) + end + + local Strong = ( between(Inline, doubleasterisks, doubleasterisks) + + between(Inline, doubleunderscores, doubleunderscores) + ) / writer.strong + + local Emph = ( between(Inline, asterisk, asterisk) + + between(Inline, underscore, underscore) + ) / writer.emphasis + + local urlchar = anyescaped - newline - more + + local AutoLinkUrl = less + * C(alphanumeric^1 * P("://") * urlchar^1) + * more + / function(url) + return writer.link(writer.string(url), url) + end + + local AutoLinkEmail = less + * C((alphanumeric + S("-._+"))^1 * P("@") * urlchar^1) + * more + / function(email) + return writer.link(writer.string(email), + "mailto:"..email) + end + + local DirectLink = (tag / parse_inlines_no_link) -- no links inside links + * spnl + * lparent + * (url + Cc("")) -- link can be empty [foo]() + * optionaltitle + * rparent + / writer.link + + local IndirectLink = tag * (C(spnl) * tag)^-1 / indirect_link + + -- parse a link or image (direct or indirect) + local Link = DirectLink + IndirectLink + + local DirectImage = exclamation + * (tag / parse_inlines) + * spnl + * lparent + * (url + Cc("")) -- link can be empty [foo]() + * optionaltitle + * rparent + / writer.image + + local IndirectImage = exclamation * tag * (C(spnl) * tag)^-1 / + indirect_image + + local Image = DirectImage + IndirectImage + + -- avoid parsing long strings of * or _ as emph/strong + local UlOrStarLine = asterisk^4 + underscore^4 / writer.string + + local EscapedChar = S("\\") * C(escapable) / writer.string +% \end{macrocode} +% \paragraph{Block Element \acro{peg} Patterns} +% \begin{macrocode} + local Block = V("Block") + + local Verbatim = Cs( (blanklines + * ((indentedline - blankline))^1)^1 + ) / expandtabs / writer.verbatim + + -- strip off leading > and indents, and run through blocks + local Blockquote = Cs(( + ((leader * more * space^-1)/"" * linechar^0 * newline)^1 + * (-blankline * linechar^1 * newline)^0 + * blankline^0 + )^1) / parse_blocks / writer.blockquote + + local function lineof(c) + return (leader * (P(c) * optionalspace)^3 * newline * blankline^1) + end + + local HorizontalRule = ( lineof(asterisk) + + lineof(dash) + + lineof(underscore) + ) / writer.hrule + + local Reference = define_reference_parser / register_link + + local Paragraph = nonindentspace * Ct(Inline^1) * newline + * ( blankline^1 + + #hash + + #(leader * more * space^-1) + ) + / writer.paragraph + + local Plain = nonindentspace * Ct(Inline^1) / writer.plain +% \end{macrocode} +% \paragraph{List \acro{peg} Patterns} +% \begin{macrocode} + local starter = bullet + enumerator + + -- we use \001 as a separator between a tight list item and a + -- nested list under it. + local NestedList = Cs((optionallyindentedline - starter)^1) + / function(a) return "\001"..a end + + local ListBlockLine = optionallyindentedline + - blankline - (indent^-1 * starter) + + local ListBlock = line * ListBlockLine^0 + + local ListContinuationBlock = blanklines * (indent / "") * ListBlock + + local function TightListItem(starter) + return -HorizontalRule + * (Cs(starter / "" * ListBlock * NestedList^-1) / + parse_blocks) + * -(blanklines * indent) + end + + local function LooseListItem(starter) + return -HorizontalRule + * Cs( starter / "" * ListBlock * Cc("\n") + * (NestedList + ListContinuationBlock^0) + * (blanklines / "\n\n") + ) / parse_blocks + end + + local BulletList = ( Ct(TightListItem(bullet)^1) + * Cc(true) * skipblanklines * -bullet + + Ct(LooseListItem(bullet)^1) + * Cc(false) * skipblanklines ) / + writer.bulletlist + + local function orderedlist(items,tight,startNumber) + if options.startNumber then + startNumber = tonumber(startNumber) or 1 -- fallback for '#' + else + startNumber = nil + end + return writer.orderedlist(items,tight,startNumber) + end + + local OrderedList = Cg(enumerator, "listtype") * + ( Ct(TightListItem(Cb("listtype")) * + TightListItem(enumerator)^0) + * Cc(true) * skipblanklines * -enumerator + + Ct(LooseListItem(Cb("listtype")) * + LooseListItem(enumerator)^0) + * Cc(false) * skipblanklines + ) * Cb("listtype") / orderedlist + + local defstartchar = S("~:") + local defstart = ( defstartchar * #spacing * (tab + space^-3) + + space * defstartchar * #spacing * (tab + space^-2) + + space * space * defstartchar * #spacing * + (tab + space^-1) + + space * space * space * defstartchar * #spacing + ) + + local dlchunk = Cs(line * (indentedline - blankline)^0) + + local function definition_list_item(term, defs, tight) + return { term = parse_inlines(term), definitions = defs } + end + + local DefinitionListItemLoose = C(line) * skipblanklines + * Ct((defstart * + indented_blocks(dlchunk) / + parse_blocks)^1) + * Cc(false) + / definition_list_item + + local DefinitionListItemTight = C(line) + * Ct((defstart * dlchunk / + parse_blocks)^1) + * Cc(true) + / definition_list_item + + local DefinitionList = ( Ct(DefinitionListItemLoose^1) * Cc(false) + + Ct(DefinitionListItemTight^1) + * (skipblanklines * + -DefinitionListItemLoose * Cc(true)) + ) / writer.definitionlist +% \end{macrocode} +% \paragraph{Blank \acro{peg} Patterns} +% \begin{macrocode} + local Blank = blankline / "" + + NoteBlock + + Reference + + (tightblocksep / "\n") +% \end{macrocode} +% \paragraph{Heading \acro{peg} Patterns} +% \begin{macrocode} + -- parse Atx heading start and return level + local HeadingStart = #hash * C(hash^-6) * -hash / length + + -- parse setext header ending and return level + local HeadingLevel = equal^1 * Cc(1) + dash^1 * Cc(2) + + local function strip_atx_end(s) + return s:gsub("[#%s]*\n$","") + end + + -- parse atx header + local AtxHeading = Cg(HeadingStart,"level") + * optionalspace + * (C(line) / strip_atx_end / parse_inlines) + * Cb("level") + / writer.heading + + -- parse setext header + local SetextHeading = #(line * S("=-")) + * Ct(line / parse_inlines) + * HeadingLevel + * optionalspace * newline + / writer.heading +% \end{macrocode} +% \paragraph{Top Level \acro{peg} Specification} +% \begin{macrocode} + syntax = + { "Blocks", + + Blocks = Blank^0 * + Block^-1 * + (Blank^0 / function() + return writer.interblocksep + end * Block)^0 * + Blank^0 * + eof, + + Blank = Blank, + + Block = V("Blockquote") + + V("Verbatim") + + V("HorizontalRule") + + V("BulletList") + + V("OrderedList") + + V("Heading") + + V("DefinitionList") + + V("Paragraph") + + V("Plain"), + + Blockquote = Blockquote, + Verbatim = Verbatim, + HorizontalRule = HorizontalRule, + BulletList = BulletList, + OrderedList = OrderedList, + Heading = AtxHeading + SetextHeading, + DefinitionList = DefinitionList, + DisplayHtml = DisplayHtml, + Paragraph = Paragraph, + Plain = Plain, + + Inline = V("Str") + + V("Space") + + V("Endline") + + V("UlOrStarLine") + + V("Strong") + + V("Emph") + + V("NoteRef") + + V("Link") + + V("Image") + + V("Code") + + V("AutoLinkUrl") + + V("AutoLinkEmail") + + V("EscapedChar") + + V("Smart") + + V("Symbol"), + + Str = Str, + Space = Space, + Endline = Endline, + UlOrStarLine = UlOrStarLine, + Strong = Strong, + Emph = Emph, + NoteRef = NoteRef, + Link = Link, + Image = Image, + Code = Code, + AutoLinkUrl = AutoLinkUrl, + AutoLinkEmail = AutoLinkEmail, + InlineHtml = InlineHtml, + HtmlEntity = HtmlEntity, + EscapedChar = EscapedChar, + Smart = Smart, + Symbol = Symbol, + } + + if not options.definitionLists then + syntax.DefinitionList = fail + end + + if not options.footnotes then + syntax.NoteRef = fail + end + + if not options.smartEllipses then + syntax.Smart = fail + end + + blocks = Ct(syntax) + + local inlines_t = util.table_copy(syntax) + inlines_t[1] = "Inlines" + inlines_t.Inlines = Inline^0 * (spacing^0 * eof / "") + inlines = Ct(inlines_t) + + inlines_no_link_t = util.table_copy(inlines_t) + inlines_no_link_t.Link = fail + inlines_no_link = Ct(inlines_no_link_t) +% \end{macrocode} +% +% \paragraph{Exported Conversion Function} +% Define \luamdef{reader->convert} as a function that converts markdown string +% \t`input` into a plain \TeX{} output and returns it. Note that the converter +% assumes that the input has \acro{unix} line endings. +% \begin{macrocode} + function self.convert(input) + references = {} +% \end{macrocode} +% When determining the name of the cache file, create salt for the hashing +% function out of the passed options recognized by the Lua interface (see +% Section \ref{sec:luaoptions}). The \Opt{cacheDir} option is disregarded. +% \begin{macrocode} + local opt_string = {} + for k,_ in pairs(defaultOptions) do + local v = options[k] + if k ~= "cacheDir" then + opt_string[#opt_string+1] = k .. "=" .. tostring(v) + end + end + table.sort(opt_string) + local salt = table.concat(opt_string, ",") +% \end{macrocode} +% Produce the cache file, transform its filename via the \luam{writer->pack} +% method, and return the result. +% \begin{macrocode} + local name = util.cache(options.cacheDir, input, salt, function(input) + return util.rope_to_string(parse_blocks(input)) .. writer.eof + end, ".md" .. writer.suffix) + return writer.pack(name) + end + return self +end +% \end{macrocode} +% \subsubsection{Conversion from Markdown to Plain \TeX{}} +% The \luam{new} method returns the \luam{reader->convert} function of a reader +% object associated with the Lua interface options (see Section +% \ref{sec:luaoptions}) \t`options` and with a writer object associated with +% \t`options`. +% \begin{macrocode} +function M.new(options) + local writer = M.writer.new(options) + local reader = M.reader.new(writer, options) + return reader.convert +end + +return M +% \end{macrocode} +% +% \iffalse +%</lua> +%<*tex> +% \fi\subsection{Plain \TeX{} Implementation}\label{sec:teximplementation} +% The plain \TeX{} implementation provides macros for the interfacing between +% \TeX{} and Lua and for the buffering of input text. These macros are then +% used to implement the macros for the conversion from markdown to plain \TeX{} +% exposed by the plain \TeX{} interface (see Section \ref{sec:texinterface}). +% +% \subsubsection{Logging Facilities}\label{sec:texinterfacelogging} +% \begin{macrocode} +\def\markdownInfo#1{% + \message{(l.\the\inputlineno) markdown.tex info: #1.}}% +\def\markdownWarning#1{% + \message{(l.\the\inputlineno) markdown.tex warning: #1}}% +\def\markdownError#1#2{% + \errhelp{#2.}% + \errmessage{(l.\the\inputlineno) markdown.tex error: #1}}% +% \end{macrocode} +% +% \subsubsection{Options} +% The following definitions should be considered placeholder. +% \begin{macrocode} +\def\markdownRendererLineBreakPrototype{\hfil\break}% +\let\markdownRendererEllipsisPrototype\dots +\long\def\markdownRendererCodeSpanPrototype#1{{\tt#1}}% +\long\def\markdownRendererLinkPrototype#1#2#3{#1}% +\long\def\markdownRendererImagePrototype#1#2#3{#1}% +\def\markdownRendererUlBeginPrototype{}% +\def\markdownRendererUlBeginTightPrototype{}% +\def\markdownRendererUlItemPrototype{}% +\def\markdownRendererUlEndPrototype{}% +\def\markdownRendererUlEndTightPrototype{}% +\def\markdownRendererOlBeginPrototype{}% +\def\markdownRendererOlBeginTightPrototype{}% +\def\markdownRendererOlItemPrototype{}% +\long\def\markdownRendererOlItemWithNumberPrototype#1{}% +\def\markdownRendererOlEndPrototype{}% +\def\markdownRendererOlEndTightPrototype{}% +\def\markdownRendererDlBeginPrototype{}% +\def\markdownRendererDlBeginTightPrototype{}% +\long\def\markdownRendererDlItemPrototype#1{#1}% +\def\markdownRendererDlEndPrototype{}% +\def\markdownRendererDlEndTightPrototype{}% +\long\def\markdownRendererEmphasisPrototype#1{{\it#1}}% +\long\def\markdownRendererStrongEmphasisPrototype#1{{\it#1}}% +\def\markdownRendererBlockQuoteBeginPrototype{\par\begingroup\it}% +\def\markdownRendererBlockQuoteEndPrototype{\endgroup\par}% +\long\def\markdownRendererInputVerbatimPrototype#1{% + \par{\tt\input"#1"\relax}\par}% +\long\def\markdownRendererHeadingOnePrototype#1{#1}% +\long\def\markdownRendererHeadingTwoPrototype#1{#1}% +\long\def\markdownRendererHeadingThreePrototype#1{#1}% +\long\def\markdownRendererHeadingFourPrototype#1{#1}% +\long\def\markdownRendererHeadingFivePrototype#1{#1}% +\long\def\markdownRendererHeadingSixPrototype#1{#1}% +\def\markdownRendererHorizontalRulePrototype{}% +\long\def\markdownRendererFootnotePrototype#1{#1}% +% \end{macrocode} +% +% \subsubsection{Lua Snippets} +% The \mdef{markdownLuaOptions} macro expands to a Lua table that +% contains the plain \TeX{} options (see Section \ref{sec:texoptions}) in a +% format recognized by Lua (see Section \ref{sec:luaoptions}). Note that the +% boolean options are not sanitized and expect the plain \TeX{} option macros +% to expand to either \t`true` or \t`false`. +% \begin{macrocode} +\def\markdownLuaOptions{{% + \ifx\markdownOptionBlankBeforeBlockquote\undefined\else + blankBeforeBlockquote = \markdownOptionBlankBeforeBlockquote, + \fi + \ifx\markdownOptionBlankBeforeHeading\undefined\else + blankBeforeHeading = \markdownOptionBlankBeforeHeading, + \fi + \ifx\markdownOptionCacheDir\undefined\else + cacheDir = "\markdownOptionCacheDir", + \fi + \ifx\markdownOptionDefinitionLists\undefined\else + definitionLists = \markdownOptionDefinitionLists, + \fi + \ifx\markdownOptionHashEnumerators\undefined\else + hashEnumerators = \markdownOptionHashEnumerators, + \fi + \ifx\markdownOptionHybrid\undefined\else + hybrid = \markdownOptionHybrid, + \fi + \ifx\markdownOptionFootnotes\undefined\else + footnotes = \markdownOptionFootnotes, + \fi + \ifx\markdownOptionPreserveTabs\undefined\else + preserveTabs = \markdownOptionPreserveTabs, + \fi + \ifx\markdownOptionSmartEllipses\undefined\else + smartEllipses = \markdownOptionSmartEllipses, + \fi + \ifx\markdownOptionStartNumber\undefined\else + startNumber = \markdownOptionStartNumber, + \fi + \ifx\markdownOptionTightLists\undefined\else + tightLists = \markdownOptionTightLists, + \fi} +}% +% \end{macrocode} +% +% The \mdef{markdownPrepare} macro contains the Lua code that is executed prior +% to any conversion from markdown to plain \TeX{}. It exposes the +% \luam{convert} function for the use by any further Lua code. +% \begin{macrocode} +\def\markdownPrepare{% +% \end{macrocode} +% First, ensure that the \m{markdownOptionCacheDir} directory exists. +% \begin{macrocode} + local lfs = require("lfs") + local cacheDir = "\markdownOptionCacheDir" + if lfs.isdir(cacheDir) == true then else + assert(lfs.mkdir(cacheDir)) + end +% \end{macrocode} +% Next, load the \t`markdown` module and create a converter function using +% the plain \TeX{} options, which were serialized to a Lua table via the +% \m{markdownLuaOptions} macro. +% \begin{macrocode} + local md = require("markdown") + local convert = md.new(\markdownLuaOptions) +}% +% \end{macrocode} +% +% \subsubsection{Lua \t`\textbackslash write18` Bridge}\label{sec:luabridge} +% The following \TeX{} code is intended for \TeX{} engines that do not provide +% direct access to Lua, but expose the shell of the operating system through +% the output file stream 18 (\Hologo{XeTeX}, \hologo{pdfLaTeX}). +% The \m{markdownLuaExecute} and \m{markdownReadAndConvert} macros defined here +% and in Section \ref{sec:directlua} are meant to be transparent to the +% remaining code. +% +% The package assumes that although the user is not using the Lua\TeX{} engine, +% their TeX distribution contains it, and uses shell access to produce and +% execute Lua scripts using the \TeX{}Lua interpreter (see +% \cite[Section~3.1.1]{luatex16}). +% \begin{macrocode} +\ifx\directlua\undefined +% \end{macrocode} +% +% The macro \mdef{markdownLuaExecuteFileStream} contains the number of the output +% file stream that will be used to store the helper Lua script in the file named +% \m{markdownOptionHelperScriptFileName} during the expansion of the macro +% \m{markdownLuaExecute}, and to store the markdown input in the file +% named \m{markdownOptionInputTempFileName} during the expansion of the macro +% \m{markdownReadAndConvert}. +% \begin{macrocode} + \csname newwrite\endcsname\markdownLuaExecuteFileStream +% \end{macrocode} +% +% The \mdef{markdownLuaExecuteShellEscape} macro contains the numeric value of +% either the \m{pdfshellescape} (Lua\TeX{}, \Hologo{pdfTeX}) or the +% \m{shellescape} (\Hologo{XeTeX}) commands. This value indicates, whether the +% shell access is enabled (\t`1`), disabled (\t`0`), or restricted (\t`2`). If +% neither of these commands is defined, act as if the shell access were enabled. +% \begin{macrocode} + \csname newcount\endcsname\markdownLuaExecuteShellEscape + \ifx\pdfshellescape\undefined + \ifx\shellescape\undefined + \markdownLuaExecuteShellEscape=1% + \else + \markdownLuaExecuteShellEscape=\shellescape + \fi + \else + \markdownLuaExecuteShellEscape=\pdfshellescape + \fi +% \end{macrocode} +% +% The \mdef{markdownLuaExecute} macro executes the Lua code it has received as +% its first argument. The Lua code may not directly interact with the \TeX{} +% engine, but it can use the \luam{print} function in the same manner it +% would use the \luam{tex.print} method. +% \begin{macrocode} + \def\markdownLuaExecute#1{% +% \end{macrocode} +% If the shell is accessible, create the file +% \m{markdownOptionHelperScriptFileName} and fill it with the input Lua code +% prepended with \pkg{kpathsea} initialization, so that Lua modules from the +% \TeX{} distribution are available. +% \begin{macrocode} + \ifnum\markdownLuaExecuteShellEscape=1% + \immediate\openout\markdownLuaExecuteFileStream=% + \markdownOptionHelperScriptFileName + \markdownInfo{Writing a helper Lua script to the file + "\markdownOptionHelperScriptFileName"}% + \immediate\write\markdownLuaExecuteFileStream{% + local kpse = require('kpse') + kpse.set_program_name('luatex') #1}% + \immediate\closeout\markdownLuaExecuteFileStream +% \end{macrocode} +% Execute the generated \m{markdownOptionHelperScriptFileName} Lua script using +% the \TeX{}Lua binary and store the output in the +% \m{markdownOptionOutputTempFileName} file. +% \begin{macrocode} + \markdownInfo{Executing a helper Lua script from the file + "\markdownOptionHelperScriptFileName" and storing the result in the + file "\markdownOptionOutputTempFileName"}% + \immediate\write18{texlua "\markdownOptionHelperScriptFileName" > + "\markdownOptionOutputTempFileName"}% +% \end{macrocode} +% \m{input} the generated \m{markdownOptionOutputTempFileName} file. +% \begin{macrocode} + \input\markdownOptionOutputTempFileName\relax + \else +% \end{macrocode} +% If the shell is inaccessible, let the user know and suggest a remedy. +% \begin{macrocode} + \markdownError{I can not access the shell}{Either run the TeX + compiler with the --shell-escape or the --enable-write18 flag, + or set shell_escape=t in the texmf.cnf file}% + \fi}% +% \end{macrocode} +% +% The \mdef{markdownReadAndConvertTab} macro contains the tab character literal. +% \begin{macrocode} + \begingroup + \catcode`\^^I=12% + \gdef\markdownReadAndConvertTab{^^I}% + \endgroup +% \end{macrocode} +% +% The \m{markdownReadAndConvert} macro is largely a rewrite of the +% \Hologo{LaTeX2e} \m{filecontents} macro to plain \TeX{}. +% \begin{macrocode} + \begingroup +% \end{macrocode} +% Make the newline and tab characters active and swap the character codes of the +% backslash symbol (\t`\textbackslash`) and the pipe symbol (\t`|`), so that +% we can use the backslash as an ordinary character inside the macro definition. +% \begin{macrocode} + \catcode`\^^M=13% + \catcode`\^^I=13% + \catcode`|=0% + \catcode`\\=12% + |gdef|markdownReadAndConvert#1#2{% + |begingroup% +% \end{macrocode} +% Open the \m{markdownOptionInputTempFileName} file for writing. +% \begin{macrocode} + |immediate|openout|markdownLuaExecuteFileStream% + |markdownOptionInputTempFileName% + |markdownInfo{Buffering markdown input into the temporary % + input file "|markdownOptionInputTempFileName" and scanning % + for the closing token sequence "#1"}% +% \end{macrocode} +% Locally change the category of the special plain \TeX{} characters to +% \emph{other} in order to prevent unwanted interpretation of the input. +% Change also the category of the space and tab characters, so that we +% can retrieve them unaltered. +% \begin{macrocode} + |def|do##1{|catcode`##1=12}|dospecials% + |catcode`| =12% + |markdownMakeOther% +% \end{macrocode} +% The \mdef{markdownReadAndConvertProcessLine} macro will process the individual +% lines of output. Note the use of the comments to ensure that the entire macro +% is at a single line and therefore no (active) newline symbols are produced. +% \begin{macrocode} + |def|markdownReadAndConvertProcessLine##1#1##2#1##3|relax{% +% \end{macrocode} +% When the ending token sequence does not appear in the line, store the line in +% the \m{markdownOptionInputTempFileName} file. +% \begin{macrocode} + |ifx|relax##3|relax% + |immediate|write|markdownLuaExecuteFileStream{##1}% + |else% +% \end{macrocode} +% When the ending token sequence appears in the line, make the next newline +% character close the \m{markdownOptionInputTempFileName} file, return the +% character categories back to the former state, convert the +% \m{markdownOptionInputTempFileName} file from markdown to plain \TeX{}, +% \m{input} the result of the conversion, and expand the ending control +% sequence. +% \begin{macrocode} + |def^^M{% + |markdownInfo{The ending token sequence was found}% + |immediate|write|markdownLuaExecuteFileStream{}% + |immediate|closeout|markdownLuaExecuteFileStream% + |endgroup% + |markdownInput|markdownOptionInputTempFileName% + #2}% + |fi% +% \end{macrocode} +% Repeat with the next line. +% \begin{macrocode} + ^^M}% +% \end{macrocode} +% Make the tab character active at expansion time and make it expand to a +% literal tab character. +% \begin{macrocode} + |catcode`|^^I=13% + |def^^I{|markdownReadAndConvertTab}% +% \end{macrocode} +% Make the newline character active at expansion time and make it consume the +% rest of the line on expansion. Throw away the rest of the first line and +% pass the second line to the \m{markdownReadAndConvertProcessLine} macro. +% \begin{macrocode} + |catcode`|^^M=13% + |def^^M##1^^M{% + |def^^M####1^^M{% + |markdownReadAndConvertProcessLine####1#1#1|relax}% + ^^M}% + ^^M}% +% \end{macrocode} +% Reset the character categories back to the former state. +% \begin{macrocode} + |endgroup +% \end{macrocode} +% +% \subsubsection{Direct Lua Access}\label{sec:directlua} +% The following \TeX{} code is intended for \TeX{} engines that provide +% direct access to Lua (Lua\TeX{}). The \m{markdownLuaExecute} and +% \m{markdownReadAndConvert} defined here and in Section \ref{sec:luabridge} +% are meant to be transparent to the remaining code. +% \begin{macrocode} +\else +% \end{macrocode} +% The direct Lua access version of the \m{markdownLuaExecute} macro is defined +% in terms of the \m{directlua} primitive. The \luam{print} function is set as +% an alias to the \m{tex.print} method in order to mimic the behaviour of the +% \m{markdownLuaExecute} definition from Section \ref{sec:luabridge}, +% \begin{macrocode} + \def\markdownLuaExecute#1{\directlua{local print = tex.print #1}}% +% \end{macrocode} +% +% In the definition of the direct Lua access version of the +% \m{markdownReadAndConvert} macro, we will be using the hash symbol +% (\t`\#`), the underscore symbol (\t`_`), the caret symbol (\t`\^`), the +% dollar sign (\t`\$`), the backslash symbol (\t`\textbackslash`), the percent +% sign (\t`\%`), and the braces (\t`\{\}`) as a part of the Lua syntax. +% \begin{macrocode} + \begingroup +% \end{macrocode} +% +% To this end, we will make the underscore symbol, the dollar sign, and caret +% symbols ordinary characters, +% \begin{macrocode} + \catcode`\_=12% + \catcode`\$=12% + \catcode`\^=12% +% \end{macrocode} +% swap the category code of the hash symbol with the slash symbol (\t`/`). +% \begin{macrocode} + \catcode`\/=6% + \catcode`\#=12% +% \end{macrocode} +% swap the category code of the percent sign with the at symbol (\t`@`). +% \begin{macrocode} + \catcode`\@=14% + \catcode`\%=12% +% \end{macrocode} +% swap the category code of the backslash symbol with the pipe symbol (\t`|`), +% \begin{macrocode} + \catcode`|=0@ + \catcode`\\=12@ +% \end{macrocode} +% Braces are a part of the plain \TeX{} syntax, but they are not removed during +% expansion, so we do not need to bother with changing their category codes. +% \begin{macrocode} + |gdef|markdownReadAndConvert/1/2{@ +% \end{macrocode} +% Make the \mdef{markdownReadAndConvertAfter} macro store the token sequence +% that will be inserted into the document after the ending token sequence has +% been found. +% \begin{macrocode} + |def|markdownReadAndConvertAfter{/2}@ + |markdownInfo{Buffering markdown input and scanning for the + closing token sequence "/1"}@ + |directlua{@ +% \end{macrocode} +% Set up an empty Lua table that will serve as our buffer. +% \begin{macrocode} + |markdownPrepare + local buffer = {} +% \end{macrocode} +% Create a regex that will match the ending input sequence. Escape any special +% regex characters (like a star inside \t`\textbackslash end\{markdown*\}`) +% inside the input. +% \begin{macrocode} + local ending_sequence = "^.-" .. ([[/1]]):gsub( + "([%(%)%.%%%+%-%*%?%[%]%^%$])", "%%%1") +% \end{macrocode} +% Register a callback that will notify you about new lines of input. +% \begin{macrocode} + |markdownLuaRegisterIBCallback{function(line) +% \end{macrocode} +% When the ending token sequence appears on a line, unregister the callback, +% convert the contents of our buffer from markdown to plain \TeX{}, and insert +% the result into the input line buffer of \TeX{}. +% \begin{macrocode} + if line:match(ending_sequence) then + |markdownLuaUnregisterIBCallback + local input = table.concat(buffer, "\n") .. "\n\n" + local output = convert(input) + return [[\markdownInfo{The ending token sequence was found}]] .. + output .. [[\markdownReadAndConvertAfter]] +% \end{macrocode} +% When the ending token sequence does not appear on a line, store the line in +% our buffer, and insert either \m{fi}, if this is the first line of input, +% or an empty token list to the input line buffer of \TeX{}. +% \begin{macrocode} + else + buffer[#buffer+1] = line + return [[\]] .. (#buffer == 1 and "fi" or "relax") + end + end}}@ +% \end{macrocode} +% Insert \m{iffalse} after the \m{markdownReadAndConvert} macro in order to +% consume the rest of the first line of input. +% \begin{macrocode} + |iffalse}@ +% \end{macrocode} +% Reset the character categories back to the former state. +% \begin{macrocode} + |endgroup +\fi +% \end{macrocode} +% +% \subsubsection{Typesetting Markdown} +% The \m{markdownInput} macro uses an implementation of the +% \m{markdownLuaExecute} macro to convert the contents of the file whose +% filename it has received as its single argument from markdown to plain +% \TeX{}. +% \begin{macrocode} +\begingroup +% \end{macrocode} +% Swap the category code of the backslash symbol and the pipe symbol, so that +% we may use the backslash symbol freely inside the Lua code. +% \begin{macrocode} + \catcode`|=0% + \catcode`\\=12% + |gdef|markdownInput#1{% + |markdownInfo{Including markdown document "#1"}% + |markdownLuaExecute{% + |markdownPrepare + local input = assert(io.open("#1","r")):read("*a") .. "\n\n" +% \end{macrocode} +% Since the Lua converter expects \acro{unix} line endings, normalize the +% input. +% \begin{macrocode} + print(convert(input:gsub("\r\n?", "\n")))}}% +|endgroup +% \end{macrocode} +% +% \iffalse +%</tex> +%<*latex> +% \fi\subsection{\LaTeX{} Implementation}\label{sec:lateximplementation} +% The \LaTeX{} implemenation makes use of the fact that, apart from some subtle +% differences, \LaTeX{} implements the majority of the plain \TeX{} format +% (see \cite[Section~9]{latex16}). As a consequence, we can directly reuse the +% existing plain \TeX{} implementation. +% \begin{macrocode} +\input markdown +\ProvidesPackage{markdown}[\markdownVersion]% +% \end{macrocode} +% +% \subsubsection{Logging Facilities} +% The \LaTeX{} implementation redefines the plain \TeX{} logging macros (see +% Section \ref{sec:texinterfacelogging}) to use the \LaTeX{} \m{PackageInfo}, +% \m{PackageWarning}, and \m{PackageError} macros. +% \begin{macrocode} +\renewcommand\markdownInfo[1]{\PackageInfo{markdown}{#1}}% +\renewcommand\markdownWarning[1]{\PackageWarning{markdown}{#1}}% +\renewcommand\markdownError[2]{\PackageError{markdown}{#1}{#2.}}% +% \end{macrocode} +% +% \subsubsection{Typesetting Markdown} +% The \mdef{markdownInputPlainTeX} macro is used to store the original plain +% \TeX{} implementation of the \m{markdownInput} macro. The \m{markdownInput} +% is then redefined to accept an optional argument with options recognized by +% the \LaTeX{} interface (see Section \ref{sec:latexoptions}). +% \begin{macrocode} +\let\markdownInputPlainTeX\markdownInput +\renewcommand\markdownInput[2][]{% + \begingroup + \markdownSetup{#1}% + \markdownInputPlainTeX{#2}% + \endgroup}% +% \end{macrocode} +% +% The \env{markdown}, and \env{markdown*} \LaTeX{} environments are implemented +% using the \m{markdownReadAndConvert} macro. +% \begin{macrocode} +\renewenvironment{markdown}{% + \markdownReadAndConvert@markdown{}}\relax +\renewenvironment{markdown*}[1]{% + \markdownSetup{#1}% + \markdownReadAndConvert@markdown*}\relax +\begingroup +% \end{macrocode} +% Locally swap the category code of the backslash symbol with the pipe symbol, +% and of the left (\t`\{`) and right brace (\t`\}`) with the less-than (\t`<`) +% and greater-than (\t`>`) signs. This is required in order that all the +% special symbols that appear in the first argument of the +% \t`markdownReadAndConvert` macro have the category code \emph{other}. +% \begin{macrocode} + \catcode`\|=0\catcode`\<=1\catcode`\>=2% + \catcode`\\=12|catcode`|{=12|catcode`|}=12% + |gdef|markdownReadAndConvert@markdown#1<% + |markdownReadAndConvert<\end{markdown#1}>% + <|end<markdown#1>>>% +|endgroup +% \end{macrocode} +% +% \subsubsection{Options} +% The supplied package options are processed using the \m{markdownSetup} macro. +% \begin{macrocode} +\DeclareOption*{% + \expandafter\markdownSetup\expandafter{\CurrentOption}}% +\ProcessOptions\relax +% \end{macrocode} +% +% The following configuration should be considered placeholder. +% \begin{macrocode} +\RequirePackage{url} +\RequirePackage{graphicx} +% \end{macrocode} +% If the \m{markdownOptionTightLists} macro expands to \t`false`, do not load +% the \pkg{paralist} package. +% \begin{macrocode} +\RequirePackage{ifthen} +\ifx\markdownOptionTightLists\undefined + \RequirePackage{paralist} +\else + \ifthenelse{\equal{\markdownOptionTightLists}{false}}{}{ + \RequirePackage{paralist}} +\fi +\RequirePackage{fancyvrb} +\markdownSetup{rendererPrototypes={ + lineBreak = {\\}, + codeSpan = {\texttt{#1}}, + link = {#1\footnote{\ifx\empty#3\empty\else#3: + \fi\texttt<\url{#2}\texttt>}}, + image = {\begin{figure} + \begin{center}% + \includegraphics{#2}% + \end{center}% + \ifx\empty#3\empty\else + \caption{#3}% + \fi + \label{fig:#1}% + \end{figure}}, + ulBegin = {\begin{itemize}}, + ulBeginTight = {\begin{compactitem}}, + ulItem = {\item}, + ulEnd = {\end{itemize}}, + ulEndTight = {\end{compactitem}}, + olBegin = {\begin{enumerate}}, + olBeginTight = {\begin{compactenum}}, + olItem = {\item}, + olItemWithNumber = {\item[#1.]}, + olEnd = {\end{enumerate}}, + olEndTight = {\end{compactenum}}, + dlBegin = {\begin{description}}, + dlBeginTight = {\begin{compactdesc}}, + dlItem = {\item[#1]}, + dlEnd = {\end{description}}, + dlEndTight = {\end{compactdesc}}, + emphasis = {\emph{#1}}, + strongEmphasis = {% + \ifx\alert\undefined + \textbf{\emph{#1}}% + \else % Beamer support + \alert{\emph{#1}} + \fi}, + blockQuoteBegin = {\begin{quotation}}, + blockQuoteEnd = {\end{quotation}}, + inputVerbatim = {\VerbatimInput{#1}}, + horizontalRule = {\noindent\rule[0.5ex]{\linewidth}{1pt}}, + footnote = {\footnote{#1}}}}% + +\ifx\chapter\undefined + \markdownSetup{rendererPrototypes={ + headingOne = {\section{#1}}, + headingTwo = {\subsection{#1}}, + headingThree = {\subsubsection{#1}}, + headingFour = {\paragraph{#1}}, + headingFive = {\subparagraph{#1}}}}% +\else + \markdownSetup{rendererPrototypes={ + headingOne = {\chapter{#1}}, + headingTwo = {\section{#1}}, + headingThree = {\subsection{#1}}, + headingFour = {\subsubsection{#1}}, + headingFive = {\paragraph{#1}}, + headingSix = {\subparagraph{#1}}}}% +\fi +% \end{macrocode} +% +% \subsubsection{Miscellanea} +% Unlike base Lua\TeX{}, which only allows for a single registered function per +% a callback (see \cite[Section~8.1]{luatex16}), the \Hologo{LaTeX2e} format +% disables the \luam{callback.register} method and exposes the +% \luam{luatexbase.add_to_callback} and \luam{luatexbase.remove_from_callback} +% methods that enable the user code to hook several functions on a single +% callback (see \cite[Section~73.4]{latex16}). +% +% To make our code function with the \Hologo{LaTeX2e} format, we need to +% redefine the \m{markdownLuaRegisterIBCallback} and +% \m{markdownLuaUnregisterIBCallback} macros accordingly. +% \begin{macrocode} +\renewcommand\markdownLuaRegisterIBCallback[1]{% + luatexbase.add_to_callback("process_input_buffer", #1, % + "The markdown input processor")} +\renewcommand\markdownLuaUnregisterIBCallback{% + luatexbase.remove_from_callback("process_input_buffer",% + "The markdown input processor")} +% \end{macrocode} +% +% When buffering user input, we should disable the bytes with the high bit set, +% since these are made active by the \pkg{inputenc} package. We will do this by +% redefining the \m{markdownMakeOther} macro accordingly. The code is courtesy +% of Scott Pakin, the creator of the \pkg{filecontents} package. +% \begin{macrocode} +\newcommand\markdownMakeOther{% + \count0=128\relax + \loop + \catcode\count0=11\relax + \advance\count0 by 1\relax + \ifnum\count0<256\repeat}% +% \end{macrocode} +% +% \iffalse +%</latex> +%<*context> +% \fi\subsection{\Hologo{ConTeXt} Implementation} +% \label{sec:contextimplementation} +% The \Hologo{ConTeXt} implementation makes use of the fact that, apart from +% some subtle differences, the Mark II and Mark IV \Hologo{ConTeXt} formats +% \emph{seem} to implement (the documentation is scarce) the majority of the +% plain \TeX{} format required by the plain \TeX{} implementation. As a +% consequence, we can directly reuse the existing plain \TeX{} implementation +% after supplying the missing plain \TeX{} macros. +% \begin{macrocode} +\def\dospecials{\do\ \do\\\do\{\do\}\do\$\do\&% + \do\#\do\^\do\_\do\%\do\~}% +% \end{macrocode} +% When there is no Lua support, then just load the plain \TeX{} implementation. +% \begin{macrocode} +\ifx\directlua\undefined + \input markdown +\else +% \end{macrocode} +% When there is Lua support, check if we can set the \t`process_input_buffer` +% Lua\TeX{} callback. +% \begin{macrocode} + \directlua{% + local function unescape(str) + return (str:gsub("|", string.char(92))) end + local old_callback = callback.find("process_input_buffer") + callback.register("process_input_buffer", function() end) + local new_callback = callback.find("process_input_buffer") +% \end{macrocode} +% +% If we can not, we are probably using ConTeXt Mark IV. In ConTeXt Mark IV, the +% \t`process_input_buffer` callback is currently frozen (inaccessible from the +% user code) and, due to the lack of available documentation, it is unclear to +% me how to emulate it. Therefore, we will just force the plain \TeX{} +% implementation to use the \m{write18} bridge (see Section +% \ref{sec:luabridge}) by locally undefining the \m{directlua} primitive. +% \begin{macrocode} + if new_callback == false then + tex.print(unescape([[|let|markdownDirectLua|directlua + |let|directlua|undefined + |input markdown + |let|directlua|markdownDirectLua + |let|markdownDirectLua|undefined]])) +% \end{macrocode} +% +% If we can, then just load the plain \TeX{} implementation. +% \begin{macrocode} + else + callback.register("process_input_buffer", old_callback) + tex.print(unescape("|input markdown")) + end}% +\fi +% \end{macrocode} +% +% \subsubsection{Logging Facilities} +% The \Hologo{ConTeXt} implementation redefines the plain \TeX{} logging macros (see +% Section \ref{sec:texinterfacelogging}) to use the \Hologo{ConTeXt} +% \m{writestatus} macro. +% \begin{macrocode} +\def\markdownInfo#1{\writestatus{markdown}{#1.}}% +\def\markdownWarning#1{\writestatus{markdown\space warn}{#1.}}% +% \end{macrocode} +% +% \subsubsection{Typesetting Markdown} +% The \m{startmarkdown} and \m{stopmarkdown} macros are implemented using the +% \m{markdownReadAndConvert} macro. +% \begin{macrocode} +\begingroup +% \end{macrocode} +% Locally swap the category code of the backslash symbol with the pipe symbol. +% This is required in order that all the special symbols that appear in the +% first argument of the \t`markdownReadAndConvert` macro have the category code +% \emph{other}. +% \begin{macrocode} + \catcode`\|=0% + \catcode`\\=12% + |gdef|startmarkdown{% + |markdownReadAndConvert{\stopmarkdown}% + {|stopmarkdown}}% +|endgroup +% \end{macrocode} +% +% \subsubsection{Options} +% The following configuration should be considered placeholder. +% \begin{macrocode} +\def\markdownRendererLineBreakPrototype{\blank}% +\long\def\markdownRendererLinkPrototype#1#2#3{% + \useURL[#1][#2][][#3]#1\footnote[#1]{\ifx\empty#3\empty\else#3: + \fi\tt<\hyphenatedurl{#2}>}}% +\long\def\markdownRendererImagePrototype#1#2#3{% + \placefigure[][fig:#1]{#3}{\externalfigure[#2]}}% +\def\markdownRendererUlBeginPrototype{\startitemize}% +\def\markdownRendererUlBeginTightPrototype{\startitemize[packed]}% +\def\markdownRendererUlItemPrototype{\item}% +\def\markdownRendererUlEndPrototype{\stopitemize}% +\def\markdownRendererUlEndTightPrototype{\stopitemize}% +\def\markdownRendererOlBeginPrototype{\startitemize[n]}% +\def\markdownRendererOlBeginTightPrototype{\startitemize[packed,n]}% +\def\markdownRendererOlItemPrototype{\item}% +\long\def\markdownRendererOlItemWithNumberPrototype#1{\sym{#1.}}% +\def\markdownRendererOlEndPrototype{\stopitemize}% +\def\markdownRendererOlEndTightPrototype{\stopitemize}% +\definedescription + [markdownConTeXtDlItemPrototype] + [location=hanging, + margin=standard, + headstyle=bold]% +\definestartstop + [MarkdownConTeXtDlPrototype] + [before=\blank, + after=\blank]% +\definestartstop + [MarkdownConTeXtDlTightPrototype] + [before=\blank\startpacked, + after=\stoppacked\blank]% +\def\markdownRendererDlBeginPrototype{% + \startMarkdownConTeXtDlPrototype}% +\def\markdownRendererDlBeginTightPrototype{% + \startMarkdownConTeXtDlTightPrototype}% +\long\long\def\markdownRendererDlItemPrototype#1{% + \markdownConTeXtDlItemPrototype{#1}}% +\def\markdownRendererDlEndPrototype{% + \stopMarkdownConTeXtDlPrototype}% +\def\markdownRendererDlEndTightPrototype{% + \stopMarkdownConTeXtDlTightPrototype}% +\long\def\markdownRendererEmphasisPrototype#1{{\em#1}}% +\long\def\markdownRendererStrongEmphasisPrototype#1{{\bf\em#1}}% +\def\markdownRendererBlockQuoteBeginPrototype{\startquotation}% +\def\markdownRendererBlockQuoteEndPrototype{\stopquotation}% +\long\def\markdownRendererInputVerbatimPrototype#1{\typefile{#1}}% +\long\def\markdownRendererHeadingOnePrototype#1{\chapter{#1}}% +\long\def\markdownRendererHeadingTwoPrototype#1{\section{#1}}% +\long\def\markdownRendererHeadingThreePrototype#1{\subsection{#1}}% +\long\def\markdownRendererHeadingFourPrototype#1{\subsubsection{#1}}% +\long\def\markdownRendererHeadingFivePrototype#1{\subsubsubsection{#1}}% +\long\def\markdownRendererHeadingSixPrototype#1{\subsubsubsubsection{#1}}% +\def\markdownRendererHorizontalRulePrototype{% + \blackrule[height=1pt, width=\hsize]}% +\long\def\markdownRendererFootnotePrototype#1{\footnote{#1}}% +\stopmodule\protect +% \end{macrocode} +% +% \iffalse +%</context> +% \fi diff --git a/Master/texmf-dist/source/generic/markdown/markdown.ins b/Master/texmf-dist/source/generic/markdown/markdown.ins new file mode 100644 index 00000000000..76ed44df1ef --- /dev/null +++ b/Master/texmf-dist/source/generic/markdown/markdown.ins @@ -0,0 +1,8 @@ +\input docstrip.tex +\generate{\usepreamble\luapreamble\usepostamble\luapostamble + \file{markdown.lua}{\from{markdown.dtx}{lua}} + \usepreamble\texpreamble\usepostamble\texpostamble + \file{markdown.tex}{\from{markdown.dtx}{tex}} + \file{markdown.sty}{\from{markdown.dtx}{latex}} + \file{t-markdown.tex}{\from{markdown.dtx}{context}}} +\endbatchfile diff --git a/Master/texmf-dist/tex/context/third/markdown/t-markdown.tex b/Master/texmf-dist/tex/context/third/markdown/t-markdown.tex new file mode 100644 index 00000000000..115836433e6 --- /dev/null +++ b/Master/texmf-dist/tex/context/third/markdown/t-markdown.tex @@ -0,0 +1,140 @@ +%% +%% This is file `t-markdown.tex', +%% generated with the docstrip utility. +%% +%% The original source files were: +%% +%% markdown.dtx (with options: `context') +%% +%% Copyright (C) 2016 Vít Novotný +%% +%% This work may be distributed and/or modified under the +%% conditions of the LaTeX Project Public License, either version 1.3 +%% of this license or (at your option) any later version. +%% The latest version of this license is in +%% +%% http://www.latex-project.org/lppl.txt +%% +%% and version 1.3 or later is part of all distributions of LaTeX +%% version 2005/12/01 or later. +%% +%% This work has the LPPL maintenance status `maintained'. +%% The Current Maintainer of this work is Vít Novotný. +%% +%% Send bug reports, requests for additions and questions +%% either to the GitHub issue tracker at +%% +%% https://github.com/Witiko/markdown/issues +%% +%% or to the e-mail address <witiko@mail.muni.cz>. +%% +%% MODIFICATION ADVICE: +%% +%% If you want to customize this file, it is best to make a copy of +%% the source file(s) from which it was produced. Use a different +%% name for your copy(ies) and modify the copy(ies); this will ensure +%% that your modifications do not get overwritten when you install a +%% new release of the standard system. You should also ensure that +%% your modified source file does not generate any modified file with +%% the same name as a standard file. +%% +%% You will also need to produce your own, suitably named, .ins file to +%% control the generation of files from your source file; this file +%% should contain your own preambles for the files it generates, not +%% those in the standard .ins files. +%% +%% The names of the source files used are shown above. +%% +\writestatus{loading}{ConTeXt User Module / markdown}% +\unprotect +\let\startmarkdown\relax +\let\stopmarkdown\relax +\def\dospecials{\do\ \do\\\do\{\do\}\do\$\do\&% + \do\#\do\^\do\_\do\%\do\~}% +\ifx\directlua\undefined + \input markdown +\else + \directlua{% + local function unescape(str) + return (str:gsub("|", string.char(92))) end + local old_callback = callback.find("process_input_buffer") + callback.register("process_input_buffer", function() end) + local new_callback = callback.find("process_input_buffer") + if new_callback == false then + tex.print(unescape([[|let|markdownDirectLua|directlua + |let|directlua|undefined + |input markdown + |let|directlua|markdownDirectLua + |let|markdownDirectLua|undefined]])) + else + callback.register("process_input_buffer", old_callback) + tex.print(unescape("|input markdown")) + end}% +\fi +\def\markdownInfo#1{\writestatus{markdown}{#1.}}% +\def\markdownWarning#1{\writestatus{markdown\space warn}{#1.}}% +\begingroup + \catcode`\|=0% + \catcode`\\=12% + |gdef|startmarkdown{% + |markdownReadAndConvert{\stopmarkdown}% + {|stopmarkdown}}% +|endgroup +\def\markdownRendererLineBreakPrototype{\blank}% +\long\def\markdownRendererLinkPrototype#1#2#3{% + \useURL[#1][#2][][#3]#1\footnote[#1]{\ifx\empty#3\empty\else#3: + \fi\tt<\hyphenatedurl{#2}>}}% +\long\def\markdownRendererImagePrototype#1#2#3{% + \placefigure[][fig:#1]{#3}{\externalfigure[#2]}}% +\def\markdownRendererUlBeginPrototype{\startitemize}% +\def\markdownRendererUlBeginTightPrototype{\startitemize[packed]}% +\def\markdownRendererUlItemPrototype{\item}% +\def\markdownRendererUlEndPrototype{\stopitemize}% +\def\markdownRendererUlEndTightPrototype{\stopitemize}% +\def\markdownRendererOlBeginPrototype{\startitemize[n]}% +\def\markdownRendererOlBeginTightPrototype{\startitemize[packed,n]}% +\def\markdownRendererOlItemPrototype{\item}% +\long\def\markdownRendererOlItemWithNumberPrototype#1{\sym{#1.}}% +\def\markdownRendererOlEndPrototype{\stopitemize}% +\def\markdownRendererOlEndTightPrototype{\stopitemize}% +\definedescription + [markdownConTeXtDlItemPrototype] + [location=hanging, + margin=standard, + headstyle=bold]% +\definestartstop + [MarkdownConTeXtDlPrototype] + [before=\blank, + after=\blank]% +\definestartstop + [MarkdownConTeXtDlTightPrototype] + [before=\blank\startpacked, + after=\stoppacked\blank]% +\def\markdownRendererDlBeginPrototype{% + \startMarkdownConTeXtDlPrototype}% +\def\markdownRendererDlBeginTightPrototype{% + \startMarkdownConTeXtDlTightPrototype}% +\long\long\def\markdownRendererDlItemPrototype#1{% + \markdownConTeXtDlItemPrototype{#1}}% +\def\markdownRendererDlEndPrototype{% + \stopMarkdownConTeXtDlPrototype}% +\def\markdownRendererDlEndTightPrototype{% + \stopMarkdownConTeXtDlTightPrototype}% +\long\def\markdownRendererEmphasisPrototype#1{{\em#1}}% +\long\def\markdownRendererStrongEmphasisPrototype#1{{\bf\em#1}}% +\def\markdownRendererBlockQuoteBeginPrototype{\startquotation}% +\def\markdownRendererBlockQuoteEndPrototype{\stopquotation}% +\long\def\markdownRendererInputVerbatimPrototype#1{\typefile{#1}}% +\long\def\markdownRendererHeadingOnePrototype#1{\chapter{#1}}% +\long\def\markdownRendererHeadingTwoPrototype#1{\section{#1}}% +\long\def\markdownRendererHeadingThreePrototype#1{\subsection{#1}}% +\long\def\markdownRendererHeadingFourPrototype#1{\subsubsection{#1}}% +\long\def\markdownRendererHeadingFivePrototype#1{\subsubsubsection{#1}}% +\long\def\markdownRendererHeadingSixPrototype#1{\subsubsubsubsection{#1}}% +\def\markdownRendererHorizontalRulePrototype{% + \blackrule[height=1pt, width=\hsize]}% +\long\def\markdownRendererFootnotePrototype#1{\footnote{#1}}% +\stopmodule\protect +\endinput +%% +%% End of file `t-markdown.tex'. diff --git a/Master/texmf-dist/tex/generic/markdown/markdown.tex b/Master/texmf-dist/tex/generic/markdown/markdown.tex new file mode 100644 index 00000000000..1a90792aacf --- /dev/null +++ b/Master/texmf-dist/tex/generic/markdown/markdown.tex @@ -0,0 +1,392 @@ +%% +%% This is file `markdown.tex', +%% generated with the docstrip utility. +%% +%% The original source files were: +%% +%% markdown.dtx (with options: `tex') +%% +%% Copyright (C) 2016 Vít Novotný +%% +%% This work may be distributed and/or modified under the +%% conditions of the LaTeX Project Public License, either version 1.3 +%% of this license or (at your option) any later version. +%% The latest version of this license is in +%% +%% http://www.latex-project.org/lppl.txt +%% +%% and version 1.3 or later is part of all distributions of LaTeX +%% version 2005/12/01 or later. +%% +%% This work has the LPPL maintenance status `maintained'. +%% The Current Maintainer of this work is Vít Novotný. +%% +%% Send bug reports, requests for additions and questions +%% either to the GitHub issue tracker at +%% +%% https://github.com/Witiko/markdown/issues +%% +%% or to the e-mail address <witiko@mail.muni.cz>. +%% +%% MODIFICATION ADVICE: +%% +%% If you want to customize this file, it is best to make a copy of +%% the source file(s) from which it was produced. Use a different +%% name for your copy(ies) and modify the copy(ies); this will ensure +%% that your modifications do not get overwritten when you install a +%% new release of the standard system. You should also ensure that +%% your modified source file does not generate any modified file with +%% the same name as a standard file. +%% +%% You will also need to produce your own, suitably named, .ins file to +%% control the generation of files from your source file; this file +%% should contain your own preambles for the files it generates, not +%% those in the standard .ins files. +%% +%% The names of the source files used are shown above. +%% +\def\markdownVersion{2016/06/03}% +\let\markdownBegin\relax +\let\markdownEnd\relax +\let\markdownInput\relax +\def\markdownOptionHelperScriptFileName{\jobname.markdown.lua}% +\def\markdownOptionInputTempFileName{\jobname.markdown.in}% +\def\markdownOptionOutputTempFileName{\jobname.markdown.out}% +\def\markdownOptionCacheDir{_markdown-\jobname}% +\let\markdownOptionBlankBeforeBlockquote\undefined +\let\markdownOptionBlankBeforeHeading\undefined +\let\markdownOptionDefinitionLists\undefined +\let\markdownOptionHashEnumerator\undefined +\let\markdownOptionHybrid\undefined +\let\markdownOptionFootnotes\undefined +\let\markdownOptionPreserveTabs\undefined +\let\markdownOptionSmartEllipses\undefined +\let\markdownOptionStartNumber\undefined +\let\markdownOptionTightLists\undefined +\def\markdownRendererLineBreak{% + \markdownRendererLineBreakPrototype}% +\def\markdownRendererEllipsis{% + \markdownRendererEllipsisPrototype}% +\def\markdownRendererCodeSpan{% + \markdownRendererCodeSpanPrototype}% +\def\markdownRendererLink{% + \markdownRendererLinkPrototype}% +\def\markdownRendererImage{% + \markdownRendererImagePrototype}% +\def\markdownRendererUlBegin{% + \markdownRendererUlBeginPrototype}% +\def\markdownRendererUlBeginTight{% + \markdownRendererUlBeginTightPrototype}% +\def\markdownRendererUlItem{% + \markdownRendererUlItemPrototype}% +\def\markdownRendererUlEnd{% + \markdownRendererUlEndPrototype}% +\def\markdownRendererUlEndTight{% + \markdownRendererUlEndTightPrototype}% +\def\markdownRendererOlBegin{% + \markdownRendererOlBeginPrototype}% +\def\markdownRendererOlBeginTight{% + \markdownRendererOlBeginTightPrototype}% +\def\markdownRendererOlItem{% + \markdownRendererOlItemPrototype}% +\def\markdownRendererOlItemWithNumber{% + \markdownRendererOlItemWithNumberPrototype}% +\def\markdownRendererOlEnd{% + \markdownRendererOlEndPrototype}% +\def\markdownRendererOlEndTight{% + \markdownRendererOlEndTightPrototype}% +\def\markdownRendererDlBegin{% + \markdownRendererDlBeginPrototype}% +\def\markdownRendererDlBeginTight{% + \markdownRendererDlBeginTightPrototype}% +\def\markdownRendererDlItem{% + \markdownRendererDlItemPrototype}% +\def\markdownRendererDlEnd{% + \markdownRendererDlEndPrototype}% +\def\markdownRendererDlEndTight{% + \markdownRendererDlEndTightPrototype}% +\def\markdownRendererEmphasis{% + \markdownRendererEmphasisPrototype}% +\def\markdownRendererStrongEmphasis{% + \markdownRendererStrongEmphasisPrototype}% +\def\markdownRendererBlockQuoteBegin{% + \markdownRendererBlockQuoteBeginPrototype}% +\def\markdownRendererBlockQuoteEnd{% + \markdownRendererBlockQuoteEndPrototype}% +\def\markdownRendererInputVerbatim{% + \markdownRendererInputVerbatimPrototype}% +\def\markdownRendererHeadingOne{% + \markdownRendererHeadingOnePrototype}% +\def\markdownRendererHeadingTwo{% + \markdownRendererHeadingTwoPrototype}% +\def\markdownRendererHeadingThree{% + \markdownRendererHeadingThreePrototype}% +\def\markdownRendererHeadingFour{% + \markdownRendererHeadingFourPrototype}% +\def\markdownRendererHeadingFive{% + \markdownRendererHeadingFivePrototype}% +\def\markdownRendererHeadingSix{% + \markdownRendererHeadingSixPrototype}% +\def\markdownRendererHorizontalRule{% + \markdownRendererHorizontalRulePrototype}% +\def\markdownRendererFootnote{% + \markdownRendererFootnotePrototype}% +\def\markdownRendererLineBreakPrototype{}% +\def\markdownRendererEllipsisPrototype{}% +\long\def\markdownRendererCodeSpanPrototype#1{}% +\long\def\markdownRendererLinkPrototype#1#2#3{}% +\long\def\markdownRendererImagePrototype#1#2#3{}% +\def\markdownRendererUlBeginPrototype{}% +\def\markdownRendererUlBeginTightPrototype{}% +\def\markdownRendererUlItemPrototype{}% +\def\markdownRendererUlEndPrototype{}% +\def\markdownRendererUlEndTightPrototype{}% +\def\markdownRendererOlBeginPrototype{}% +\def\markdownRendererOlBeginTightPrototype{}% +\def\markdownRendererOlItemPrototype{}% +\long\def\markdownRendererOlItemWithNumberPrototype#1{}% +\def\markdownRendererOlEndPrototype{}% +\def\markdownRendererOlEndTightPrototype{}% +\def\markdownRendererDlBeginPrototype{}% +\def\markdownRendererDlBeginTightPrototype{}% +\long\def\markdownRendererDlItemPrototype#1{}% +\def\markdownRendererDlEndPrototype{}% +\def\markdownRendererDlEndTightPrototype{}% +\long\def\markdownRendererEmphasisPrototype#1{}% +\long\def\markdownRendererStrongEmphasisPrototype#1{}% +\def\markdownRendererBlockQuoteBeginPrototype{}% +\def\markdownRendererBlockQuoteEndPrototype{}% +\long\def\markdownRendererInputVerbatimPrototype#1{}% +\long\def\markdownRendererHeadingOnePrototype#1{}% +\long\def\markdownRendererHeadingTwoPrototype#1{}% +\long\def\markdownRendererHeadingThreePrototype#1{}% +\long\def\markdownRendererHeadingFourPrototype#1{}% +\long\def\markdownRendererHeadingFivePrototype#1{}% +\long\def\markdownRendererHeadingSixPrototype#1{}% +\def\markdownRendererHorizontalRulePrototype{}% +\long\def\markdownRendererFootnotePrototype#1{}% +\def\markdownInfo#1{}% +\def\markdownWarning#1{}% +\def\markdownError#1{}% +\def\markdownLuaRegisterIBCallback#1{% + local old_callback = callback.find("process_input_buffer") + callback.register("process_input_buffer", #1)}% +\def\markdownLuaUnregisterIBCallback{% + callback.register("process_input_buffer", old_callback)}% +\let\markdownMakeOther\relax +\let\markdownReadAndConvert\relax +\begingroup + \catcode`\|=0\catcode`\\=12% + |gdef|markdownBegin{% + |markdownReadAndConvert{\markdownEnd}% + {|markdownEnd}}% +|endgroup +\def\markdownInfo#1{% + \message{(l.\the\inputlineno) markdown.tex info: #1.}}% +\def\markdownWarning#1{% + \message{(l.\the\inputlineno) markdown.tex warning: #1}}% +\def\markdownError#1#2{% + \errhelp{#2.}% + \errmessage{(l.\the\inputlineno) markdown.tex error: #1}}% +\def\markdownRendererLineBreakPrototype{\hfil\break}% +\let\markdownRendererEllipsisPrototype\dots +\long\def\markdownRendererCodeSpanPrototype#1{{\tt#1}}% +\long\def\markdownRendererLinkPrototype#1#2#3{#1}% +\long\def\markdownRendererImagePrototype#1#2#3{#1}% +\def\markdownRendererUlBeginPrototype{}% +\def\markdownRendererUlBeginTightPrototype{}% +\def\markdownRendererUlItemPrototype{}% +\def\markdownRendererUlEndPrototype{}% +\def\markdownRendererUlEndTightPrototype{}% +\def\markdownRendererOlBeginPrototype{}% +\def\markdownRendererOlBeginTightPrototype{}% +\def\markdownRendererOlItemPrototype{}% +\long\def\markdownRendererOlItemWithNumberPrototype#1{}% +\def\markdownRendererOlEndPrototype{}% +\def\markdownRendererOlEndTightPrototype{}% +\def\markdownRendererDlBeginPrototype{}% +\def\markdownRendererDlBeginTightPrototype{}% +\long\def\markdownRendererDlItemPrototype#1{#1}% +\def\markdownRendererDlEndPrototype{}% +\def\markdownRendererDlEndTightPrototype{}% +\long\def\markdownRendererEmphasisPrototype#1{{\it#1}}% +\long\def\markdownRendererStrongEmphasisPrototype#1{{\it#1}}% +\def\markdownRendererBlockQuoteBeginPrototype{\par\begingroup\it}% +\def\markdownRendererBlockQuoteEndPrototype{\endgroup\par}% +\long\def\markdownRendererInputVerbatimPrototype#1{% + \par{\tt\input"#1"\relax}\par}% +\long\def\markdownRendererHeadingOnePrototype#1{#1}% +\long\def\markdownRendererHeadingTwoPrototype#1{#1}% +\long\def\markdownRendererHeadingThreePrototype#1{#1}% +\long\def\markdownRendererHeadingFourPrototype#1{#1}% +\long\def\markdownRendererHeadingFivePrototype#1{#1}% +\long\def\markdownRendererHeadingSixPrototype#1{#1}% +\def\markdownRendererHorizontalRulePrototype{}% +\long\def\markdownRendererFootnotePrototype#1{#1}% +\def\markdownLuaOptions{{% + \ifx\markdownOptionBlankBeforeBlockquote\undefined\else + blankBeforeBlockquote = \markdownOptionBlankBeforeBlockquote, + \fi + \ifx\markdownOptionBlankBeforeHeading\undefined\else + blankBeforeHeading = \markdownOptionBlankBeforeHeading, + \fi + \ifx\markdownOptionCacheDir\undefined\else + cacheDir = "\markdownOptionCacheDir", + \fi + \ifx\markdownOptionDefinitionLists\undefined\else + definitionLists = \markdownOptionDefinitionLists, + \fi + \ifx\markdownOptionHashEnumerators\undefined\else + hashEnumerators = \markdownOptionHashEnumerators, + \fi + \ifx\markdownOptionHybrid\undefined\else + hybrid = \markdownOptionHybrid, + \fi + \ifx\markdownOptionFootnotes\undefined\else + footnotes = \markdownOptionFootnotes, + \fi + \ifx\markdownOptionPreserveTabs\undefined\else + preserveTabs = \markdownOptionPreserveTabs, + \fi + \ifx\markdownOptionSmartEllipses\undefined\else + smartEllipses = \markdownOptionSmartEllipses, + \fi + \ifx\markdownOptionStartNumber\undefined\else + startNumber = \markdownOptionStartNumber, + \fi + \ifx\markdownOptionTightLists\undefined\else + tightLists = \markdownOptionTightLists, + \fi} +}% +\def\markdownPrepare{% + local lfs = require("lfs") + local cacheDir = "\markdownOptionCacheDir" + if lfs.isdir(cacheDir) == true then else + assert(lfs.mkdir(cacheDir)) + end + local md = require("markdown") + local convert = md.new(\markdownLuaOptions) +}% +\ifx\directlua\undefined + \csname newwrite\endcsname\markdownLuaExecuteFileStream + \csname newcount\endcsname\markdownLuaExecuteShellEscape + \ifx\pdfshellescape\undefined + \ifx\shellescape\undefined + \markdownLuaExecuteShellEscape=1% + \else + \markdownLuaExecuteShellEscape=\shellescape + \fi + \else + \markdownLuaExecuteShellEscape=\pdfshellescape + \fi + \def\markdownLuaExecute#1{% + \ifnum\markdownLuaExecuteShellEscape=1% + \immediate\openout\markdownLuaExecuteFileStream=% + \markdownOptionHelperScriptFileName + \markdownInfo{Writing a helper Lua script to the file + "\markdownOptionHelperScriptFileName"}% + \immediate\write\markdownLuaExecuteFileStream{% + local kpse = require('kpse') + kpse.set_program_name('luatex') #1}% + \immediate\closeout\markdownLuaExecuteFileStream + \markdownInfo{Executing a helper Lua script from the file + "\markdownOptionHelperScriptFileName" and storing the result in the + file "\markdownOptionOutputTempFileName"}% + \immediate\write18{texlua "\markdownOptionHelperScriptFileName" > + "\markdownOptionOutputTempFileName"}% + \input\markdownOptionOutputTempFileName\relax + \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}% + \begingroup + \catcode`\^^I=12% + \gdef\markdownReadAndConvertTab{^^I}% + \endgroup + \begingroup + \catcode`\^^M=13% + \catcode`\^^I=13% + \catcode`|=0% + \catcode`\\=12% + |gdef|markdownReadAndConvert#1#2{% + |begingroup% + |immediate|openout|markdownLuaExecuteFileStream% + |markdownOptionInputTempFileName% + |markdownInfo{Buffering markdown input into the temporary % + input file "|markdownOptionInputTempFileName" and scanning % + for the closing token sequence "#1"}% + |def|do##1{|catcode`##1=12}|dospecials% + |catcode`| =12% + |markdownMakeOther% + |def|markdownReadAndConvertProcessLine##1#1##2#1##3|relax{% + |ifx|relax##3|relax% + |immediate|write|markdownLuaExecuteFileStream{##1}% + |else% + |def^^M{% + |markdownInfo{The ending token sequence was found}% + |immediate|write|markdownLuaExecuteFileStream{}% + |immediate|closeout|markdownLuaExecuteFileStream% + |endgroup% + |markdownInput|markdownOptionInputTempFileName% + #2}% + |fi% + ^^M}% + |catcode`|^^I=13% + |def^^I{|markdownReadAndConvertTab}% + |catcode`|^^M=13% + |def^^M##1^^M{% + |def^^M####1^^M{% + |markdownReadAndConvertProcessLine####1#1#1|relax}% + ^^M}% + ^^M}% + |endgroup +\else + \def\markdownLuaExecute#1{\directlua{local print = tex.print #1}}% + \begingroup + \catcode`\_=12% + \catcode`\$=12% + \catcode`\^=12% + \catcode`\/=6% + \catcode`\#=12% + \catcode`\@=14% + \catcode`\%=12% + \catcode`|=0@ + \catcode`\\=12@ + |gdef|markdownReadAndConvert/1/2{@ + |def|markdownReadAndConvertAfter{/2}@ + |markdownInfo{Buffering markdown input and scanning for the + closing token sequence "/1"}@ + |directlua{@ + |markdownPrepare + local buffer = {} + local ending_sequence = "^.-" .. ([[/1]]):gsub( + "([%(%)%.%%%+%-%*%?%[%]%^%$])", "%%%1") + |markdownLuaRegisterIBCallback{function(line) + if line:match(ending_sequence) then + |markdownLuaUnregisterIBCallback + local input = table.concat(buffer, "\n") .. "\n\n" + local output = convert(input) + return [[\markdownInfo{The ending token sequence was found}]] .. + output .. [[\markdownReadAndConvertAfter]] + else + buffer[#buffer+1] = line + return [[\]] .. (#buffer == 1 and "fi" or "relax") + end + end}}@ + |iffalse}@ + |endgroup +\fi +\begingroup + \catcode`|=0% + \catcode`\\=12% + |gdef|markdownInput#1{% + |markdownInfo{Including markdown document "#1"}% + |markdownLuaExecute{% + |markdownPrepare + local input = assert(io.open("#1","r")):read("*a") .. "\n\n" + print(convert(input:gsub("\r\n?", "\n")))}}% +|endgroup +\endinput +%% +%% End of file `markdown.tex'. diff --git a/Master/texmf-dist/tex/latex/markdown/markdown.sty b/Master/texmf-dist/tex/latex/markdown/markdown.sty new file mode 100644 index 00000000000..c568c8212b5 --- /dev/null +++ b/Master/texmf-dist/tex/latex/markdown/markdown.sty @@ -0,0 +1,331 @@ +%% +%% This is file `markdown.sty', +%% generated with the docstrip utility. +%% +%% The original source files were: +%% +%% markdown.dtx (with options: `latex') +%% +%% Copyright (C) 2016 Vít Novotný +%% +%% This work may be distributed and/or modified under the +%% conditions of the LaTeX Project Public License, either version 1.3 +%% of this license or (at your option) any later version. +%% The latest version of this license is in +%% +%% http://www.latex-project.org/lppl.txt +%% +%% and version 1.3 or later is part of all distributions of LaTeX +%% version 2005/12/01 or later. +%% +%% This work has the LPPL maintenance status `maintained'. +%% The Current Maintainer of this work is Vít Novotný. +%% +%% Send bug reports, requests for additions and questions +%% either to the GitHub issue tracker at +%% +%% https://github.com/Witiko/markdown/issues +%% +%% or to the e-mail address <witiko@mail.muni.cz>. +%% +%% MODIFICATION ADVICE: +%% +%% If you want to customize this file, it is best to make a copy of +%% the source file(s) from which it was produced. Use a different +%% name for your copy(ies) and modify the copy(ies); this will ensure +%% that your modifications do not get overwritten when you install a +%% new release of the standard system. You should also ensure that +%% your modified source file does not generate any modified file with +%% the same name as a standard file. +%% +%% You will also need to produce your own, suitably named, .ins file to +%% control the generation of files from your source file; this file +%% should contain your own preambles for the files it generates, not +%% those in the standard .ins files. +%% +%% The names of the source files used are shown above. +%% +\NeedsTeXFormat{LaTeX2e}% +\newenvironment{markdown}\relax\relax +\newenvironment{markdown*}[1]\relax\relax +\newcommand\markdownSetup[1]{% + \setkeys{markdownOptions}{#1}}% +\RequirePackage{keyval} +\define@key{markdownOptions}{helperScriptFileName}{% + \def\markdownOptionHelperScriptFileName{#1}}% +\define@key{markdownOptions}{inputTempFileName}{% + \def\markdownOptionInputTempFileName{#1}}% +\define@key{markdownOptions}{outputTempFileName}{% + \def\markdownOptionOutputTempFileName{#1}}% +\define@key{markdownOptions}{blankBeforeBlockquote}[true]{% + \def\markdownOptionBlankBeforeBlockquote{#1}}% +\define@key{markdownOptions}{blankBeforeHeading}[true]{% + \def\markdownOptionBlankBeforeHeading{#1}}% +\define@key{markdownOptions}{cacheDir}{% + \def\markdownOptionCacheDir{#1}}% +\define@key{markdownOptions}{definitionLists}[true]{% + \def\markdownOptionDefinitionLists{#1}}% +\define@key{markdownOptions}{hashEnumerators}[true]{% + \def\markdownOptionHashEnumerators{#1}}% +\define@key{markdownOptions}{hybrid}[true]{% + \def\markdownOptionHybrid{#1}}% +\define@key{markdownOptions}{footnotes}[true]{% + \def\markdownOptionFootnotes{#1}}% +\define@key{markdownOptions}{preserveTabs}[true]{% + \def\markdownOptionPreserveTabs{#1}}% +\define@key{markdownOptions}{smartEllipses}[true]{% + \def\markdownOptionSmartEllipses{#1}}% +\define@key{markdownOptions}{startNumber}[true]{% + \def\markdownOptionStartNumber{#1}}% +\define@key{markdownOptions}{tightLists}[true]{% + \def\markdownOptionTightLists{#1}}% +\define@key{markdownOptions}{renderers}{% + \setkeys{markdownRenderers}{#1}}% +\define@key{markdownRenderers}{lineBreak}{% + \renewcommand\markdownRendererLineBreak{#1}}% +\define@key{markdownRenderers}{ellipsis}{% + \renewcommand\markdownRendererEllipsis{#1}}% +\define@key{markdownRenderers}{codeSpan}{% + \renewcommand\markdownRendererCodeSpan[1]{#1}}% +\define@key{markdownRenderers}{link}{% + \renewcommand\markdownRendererLink[3]{#1}}% +\define@key{markdownRenderers}{image}{% + \renewcommand\markdownRendererImage[3]{#1}}% +\define@key{markdownRenderers}{ulBegin}{% + \renewcommand\markdownRendererUlBegin{#1}}% +\define@key{markdownRenderers}{ulBeginTight}{% + \renewcommand\markdownRendererUlBeginTight{#1}}% +\define@key{markdownRenderers}{ulItem}{% + \renewcommand\markdownRendererUlItem{#1}}% +\define@key{markdownRenderers}{ulEnd}{% + \renewcommand\markdownRendererUlEnd{#1}}% +\define@key{markdownRenderers}{ulEndTight}{% + \renewcommand\markdownRendererUlEndTight{#1}}% +\define@key{markdownRenderers}{olBegin}{% + \renewcommand\markdownRendererOlBegin{#1}}% +\define@key{markdownRenderers}{olBeginTight}{% + \renewcommand\markdownRendererOlBeginTight{#1}}% +\define@key{markdownRenderers}{olItem}{% + \renewcommand\markdownRendererOlItem{#1}}% +\define@key{markdownRenderers}{olItemWithNumber}{% + \renewcommand\markdownRendererOlItemWithNumber[1]{#1}}% +\define@key{markdownRenderers}{olEnd}{% + \renewcommand\markdownRendererOlEnd{#1}}% +\define@key{markdownRenderers}{olEndTight}{% + \renewcommand\markdownRendererOlEndTight{#1}}% +\define@key{markdownRenderers}{dlBegin}{% + \renewcommand\markdownRendererDlBegin{#1}}% +\define@key{markdownRenderers}{dlBeginTight}{% + \renewcommand\markdownRendererDlBeginTight{#1}}% +\define@key{markdownRenderers}{dlItem}{% + \renewcommand\markdownRendererDlItem[1]{#1}}% +\define@key{markdownRenderers}{dlEnd}{% + \renewcommand\markdownRendererDlEnd{#1}}% +\define@key{markdownRenderers}{dlEndTight}{% + \renewcommand\markdownRendererDlEndTight{#1}}% +\define@key{markdownRenderers}{emphasis}{% + \renewcommand\markdownRendererEmphasis[1]{#1}}% +\define@key{markdownRenderers}{strongEmphasis}{% + \renewcommand\markdownRendererStrongEmphasis[1]{#1}}% +\define@key{markdownRenderers}{blockQuoteBegin}{% + \renewcommand\markdownRendererBlockQuoteBegin{#1}}% +\define@key{markdownRenderers}{blockQuoteEnd}{% + \renewcommand\markdownRendererBlockQuoteEnd{#1}}% +\define@key{markdownRenderers}{inputVerbatim}{% + \renewcommand\markdownRendererInputVerbatim[1]{#1}}% +\define@key{markdownRenderers}{headingOne}{% + \renewcommand\markdownRendererHeadingOne[1]{#1}}% +\define@key{markdownRenderers}{headingTwo}{% + \renewcommand\markdownRendererHeadingTwo[1]{#1}}% +\define@key{markdownRenderers}{headingThree}{% + \renewcommand\markdownRendererHeadingThree[1]{#1}}% +\define@key{markdownRenderers}{headingFour}{% + \renewcommand\markdownRendererHeadingFour[1]{#1}}% +\define@key{markdownRenderers}{headingFive}{% + \renewcommand\markdownRendererHeadingFive[1]{#1}}% +\define@key{markdownRenderers}{headingSix}{% + \renewcommand\markdownRendererHeadingSix[1]{#1}}% +\define@key{markdownRenderers}{horizontalRule}{% + \renewcommand\markdownRendererHorizontalRule{#1}}% +\define@key{markdownRenderers}{footnote}{% + \renewcommand\markdownRendererFootnote[1]{#1}}% +\define@key{markdownOptions}{rendererPrototypes}{% + \setkeys{markdownRendererPrototypes}{#1}}% +\define@key{markdownRendererPrototypes}{lineBreak}{% + \renewcommand\markdownRendererLineBreakPrototype{#1}}% +\define@key{markdownRendererPrototypes}{ellipsis}{% + \renewcommand\markdownRendererEllipsisPrototype{#1}}% +\define@key{markdownRendererPrototypes}{codeSpan}{% + \renewcommand\markdownRendererCodeSpanPrototype[1]{#1}}% +\define@key{markdownRendererPrototypes}{link}{% + \renewcommand\markdownRendererLink[3]{#1}}% +\define@key{markdownRendererPrototypes}{image}{% + \renewcommand\markdownRendererImage[3]{#1}}% +\define@key{markdownRendererPrototypes}{ulBegin}{% + \renewcommand\markdownRendererUlBeginPrototype{#1}}% +\define@key{markdownRendererPrototypes}{ulBeginTight}{% + \renewcommand\markdownRendererUlBeginTightPrototype{#1}}% +\define@key{markdownRendererPrototypes}{ulItem}{% + \renewcommand\markdownRendererUlItemPrototype{#1}}% +\define@key{markdownRendererPrototypes}{ulEnd}{% + \renewcommand\markdownRendererUlEndPrototype{#1}}% +\define@key{markdownRendererPrototypes}{ulEndTight}{% + \renewcommand\markdownRendererUlEndTightPrototype{#1}}% +\define@key{markdownRendererPrototypes}{olBegin}{% + \renewcommand\markdownRendererOlBeginPrototype{#1}}% +\define@key{markdownRendererPrototypes}{olBeginTight}{% + \renewcommand\markdownRendererOlBeginTightPrototype{#1}}% +\define@key{markdownRendererPrototypes}{olItem}{% + \renewcommand\markdownRendererOlItemPrototype{#1}}% +\define@key{markdownRendererPrototypes}{olItemWithNumber}{% + \renewcommand\markdownRendererOlItemWithNumberPrototype[1]{#1}}% +\define@key{markdownRendererPrototypes}{olEnd}{% + \renewcommand\markdownRendererOlEndPrototype{#1}}% +\define@key{markdownRendererPrototypes}{olEndTight}{% + \renewcommand\markdownRendererOlEndTightPrototype{#1}}% +\define@key{markdownRendererPrototypes}{dlBegin}{% + \renewcommand\markdownRendererDlBeginPrototype{#1}}% +\define@key{markdownRendererPrototypes}{dlBeginTight}{% + \renewcommand\markdownRendererDlBeginTightPrototype{#1}}% +\define@key{markdownRendererPrototypes}{dlItem}{% + \renewcommand\markdownRendererDlItemPrototype[1]{#1}}% +\define@key{markdownRendererPrototypes}{dlEnd}{% + \renewcommand\markdownRendererDlEndPrototype{#1}}% +\define@key{markdownRendererPrototypes}{dlEndTight}{% + \renewcommand\markdownRendererDlEndTightPrototype{#1}}% +\define@key{markdownRendererPrototypes}{emphasis}{% + \renewcommand\markdownRendererEmphasisPrototype[1]{#1}}% +\define@key{markdownRendererPrototypes}{strongEmphasis}{% + \renewcommand\markdownRendererStrongEmphasisPrototype[1]{#1}}% +\define@key{markdownRendererPrototypes}{blockQuoteBegin}{% + \renewcommand\markdownRendererBlockQuoteBeginPrototype{#1}}% +\define@key{markdownRendererPrototypes}{blockQuoteEnd}{% + \renewcommand\markdownRendererBlockQuoteEndPrototype{#1}}% +\define@key{markdownRendererPrototypes}{inputVerbatim}{% + \renewcommand\markdownRendererInputVerbatimPrototype[1]{#1}}% +\define@key{markdownRendererPrototypes}{headingOne}{% + \renewcommand\markdownRendererHeadingOnePrototype[1]{#1}}% +\define@key{markdownRendererPrototypes}{headingTwo}{% + \renewcommand\markdownRendererHeadingTwoPrototype[1]{#1}}% +\define@key{markdownRendererPrototypes}{headingThree}{% + \renewcommand\markdownRendererHeadingThreePrototype[1]{#1}}% +\define@key{markdownRendererPrototypes}{headingFour}{% + \renewcommand\markdownRendererHeadingFourPrototype[1]{#1}}% +\define@key{markdownRendererPrototypes}{headingFive}{% + \renewcommand\markdownRendererHeadingFivePrototype[1]{#1}}% +\define@key{markdownRendererPrototypes}{headingSix}{% + \renewcommand\markdownRendererHeadingSixPrototype[1]{#1}}% +\define@key{markdownRendererPrototypes}{horizontalRule}{% + \renewcommand\markdownRendererHorizontalRulePrototype{#1}}% +\define@key{markdownRendererPrototypes}{footnote}{% + \renewcommand\markdownRendererFootnotePrototype[1]{#1}}% +\input markdown +\ProvidesPackage{markdown}[\markdownVersion]% +\renewcommand\markdownInfo[1]{\PackageInfo{markdown}{#1}}% +\renewcommand\markdownWarning[1]{\PackageWarning{markdown}{#1}}% +\renewcommand\markdownError[2]{\PackageError{markdown}{#1}{#2.}}% +\let\markdownInputPlainTeX\markdownInput +\renewcommand\markdownInput[2][]{% + \begingroup + \markdownSetup{#1}% + \markdownInputPlainTeX{#2}% + \endgroup}% +\renewenvironment{markdown}{% + \markdownReadAndConvert@markdown{}}\relax +\renewenvironment{markdown*}[1]{% + \markdownSetup{#1}% + \markdownReadAndConvert@markdown*}\relax +\begingroup + \catcode`\|=0\catcode`\<=1\catcode`\>=2% + \catcode`\\=12|catcode`|{=12|catcode`|}=12% + |gdef|markdownReadAndConvert@markdown#1<% + |markdownReadAndConvert<\end{markdown#1}>% + <|end<markdown#1>>>% +|endgroup +\DeclareOption*{% + \expandafter\markdownSetup\expandafter{\CurrentOption}}% +\ProcessOptions\relax +\RequirePackage{url} +\RequirePackage{graphicx} +\RequirePackage{ifthen} +\ifx\markdownOptionTightLists\undefined + \RequirePackage{paralist} +\else + \ifthenelse{\equal{\markdownOptionTightLists}{false}}{}{ + \RequirePackage{paralist}} +\fi +\RequirePackage{fancyvrb} +\markdownSetup{rendererPrototypes={ + lineBreak = {\\}, + codeSpan = {\texttt{#1}}, + link = {#1\footnote{\ifx\empty#3\empty\else#3: + \fi\texttt<\url{#2}\texttt>}}, + image = {\begin{figure} + \begin{center}% + \includegraphics{#2}% + \end{center}% + \ifx\empty#3\empty\else + \caption{#3}% + \fi + \label{fig:#1}% + \end{figure}}, + ulBegin = {\begin{itemize}}, + ulBeginTight = {\begin{compactitem}}, + ulItem = {\item}, + ulEnd = {\end{itemize}}, + ulEndTight = {\end{compactitem}}, + olBegin = {\begin{enumerate}}, + olBeginTight = {\begin{compactenum}}, + olItem = {\item}, + olItemWithNumber = {\item[#1.]}, + olEnd = {\end{enumerate}}, + olEndTight = {\end{compactenum}}, + dlBegin = {\begin{description}}, + dlBeginTight = {\begin{compactdesc}}, + dlItem = {\item[#1]}, + dlEnd = {\end{description}}, + dlEndTight = {\end{compactdesc}}, + emphasis = {\emph{#1}}, + strongEmphasis = {% + \ifx\alert\undefined + \textbf{\emph{#1}}% + \else % Beamer support + \alert{\emph{#1}} + \fi}, + blockQuoteBegin = {\begin{quotation}}, + blockQuoteEnd = {\end{quotation}}, + inputVerbatim = {\VerbatimInput{#1}}, + horizontalRule = {\noindent\rule[0.5ex]{\linewidth}{1pt}}, + footnote = {\footnote{#1}}}}% + +\ifx\chapter\undefined + \markdownSetup{rendererPrototypes={ + headingOne = {\section{#1}}, + headingTwo = {\subsection{#1}}, + headingThree = {\subsubsection{#1}}, + headingFour = {\paragraph{#1}}, + headingFive = {\subparagraph{#1}}}}% +\else + \markdownSetup{rendererPrototypes={ + headingOne = {\chapter{#1}}, + headingTwo = {\section{#1}}, + headingThree = {\subsection{#1}}, + headingFour = {\subsubsection{#1}}, + headingFive = {\paragraph{#1}}, + headingSix = {\subparagraph{#1}}}}% +\fi +\renewcommand\markdownLuaRegisterIBCallback[1]{% + luatexbase.add_to_callback("process_input_buffer", #1, % + "The markdown input processor")} +\renewcommand\markdownLuaUnregisterIBCallback{% + luatexbase.remove_from_callback("process_input_buffer",% + "The markdown input processor")} +\newcommand\markdownMakeOther{% + \count0=128\relax + \loop + \catcode\count0=11\relax + \advance\count0 by 1\relax + \ifnum\count0<256\repeat}% +\endinput +%% +%% End of file `markdown.sty'. diff --git a/Master/texmf-dist/tex/luatex/markdown/markdown.lua b/Master/texmf-dist/tex/luatex/markdown/markdown.lua new file mode 100644 index 00000000000..e740d18c92e --- /dev/null +++ b/Master/texmf-dist/tex/luatex/markdown/markdown.lua @@ -0,0 +1,1057 @@ +-- +-- Copyright (C) 2009-2016 John MacFarlane, Hans Hagen +-- +-- Permission is hereby granted, free of charge, to any person obtaining +-- a copy of this software and associated documentation files (the +-- "Software"), to deal in the Software without restriction, including +-- without limitation the rights to use, copy, modify, merge, publish, +-- distribute, sublicense, and/or sell copies of the Software, and to +-- permit persons to whom the Software is furnished to do so, subject to +-- the following conditions: +-- +-- The above copyright notice and this permission notice shall be included +-- in all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +-- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +-- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +-- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +-- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +-- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-- +-- Copyright (C) 2016 Vít Novotný +-- +-- This work may be distributed and/or modified under the +-- conditions of the LaTeX Project Public License, either version 1.3 +-- of this license or (at your option) any later version. +-- The latest version of this license is in +-- +-- http://www.latex-project.org/lppl.txt +-- +-- and version 1.3 or later is part of all distributions of LaTeX +-- version 2005/12/01 or later. +-- +-- This work has the LPPL maintenance status `maintained'. +-- The Current Maintainer of this work is Vít Novotný. +-- +-- Send bug reports, requests for additions and questions +-- either to the GitHub issue tracker at +-- +-- https://github.com/witiko/markdown/issues +-- +-- or to the e-mail address <witiko@mail.muni.cz>. +-- +-- MODIFICATION ADVICE: +-- +-- If you want to customize this file, it is best to make a copy of +-- the source file(s) from which it was produced. Use a different +-- name for your copy(ies) and modify the copy(ies); this will ensure +-- that your modifications do not get overwritten when you install a +-- new release of the standard system. You should also ensure that +-- your modified source file does not generate any modified file with +-- the same name as a standard file. +-- +-- You will also need to produce your own, suitably named, .ins file to +-- control the generation of files from your source file; this file +-- should contain your own preambles for the files it generates, not +-- those in the standard .ins files. +-- +if not modules then modules = { } end modules ['markdown'] = { + version = "1.0.1", + comment = "A module for the conversion from markdown to plain TeX", + author = "John MacFarlane, Hans Hagen, Vít Novotný", + copyright = "2009-2016 John MacFarlane, Hans Hagen; 2016 Vít Novotný", + license = "LPPL 1.3" +} +local lpeg = require("lpeg") +local unicode = require("unicode") +local md5 = require("md5") +local M = {} +M.new = {} +local defaultOptions = {} +defaultOptions.blankBeforeBlockquote = false +defaultOptions.blankBeforeHeading = false +defaultOptions.cacheDir = "." +defaultOptions.definitionLists = false +defaultOptions.hashEnumerators = false +defaultOptions.hybrid = false +defaultOptions.footnotes = false +defaultOptions.preserveTabs = false +defaultOptions.smartEllipses = false +defaultOptions.startNumber = true +defaultOptions.tightLists = true +local upper, gsub, format, length = + string.upper, string.gsub, string.format, string.len +local concat = table.concat +local P, R, S, V, C, Cg, Cb, Cmt, Cc, Ct, B, Cs, any = + lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.C, lpeg.Cg, lpeg.Cb, + lpeg.Cmt, lpeg.Cc, lpeg.Ct, lpeg.B, lpeg.Cs, lpeg.P(1) +local util = {} +function util.err(msg, exit_code) + io.stderr:write("markdown.lua: " .. msg .. "\n") + os.exit(exit_code or 1) +end +function util.cache(dir, string, salt, transform, suffix) + local digest = md5.sumhexa(string .. (salt or "")) + local name = util.pathname(dir, digest .. suffix) + local file = io.open(name, "r") + if file == nil then -- If no cache entry exists, then create a new one. + local file = assert(io.open(name, "w")) + local result = string + if transform ~= nil then + result = transform(result) + end + assert(file:write(result)) + assert(file:close()) + end + return name +end +function util.table_copy(t) + local u = { } + for k, v in pairs(t) do u[k] = v end + return setmetatable(u, getmetatable(t)) +end +function util.expand_tabs_in_line(s, tabstop) + local tab = tabstop or 4 + local corr = 0 + return (s:gsub("()\t", function(p) + local sp = tab - (p - 1 + corr) % tab + corr = corr - 1 + sp + return string.rep(" ", sp) + end)) +end +function util.walk(t, f) + local typ = type(t) + if typ == "string" then + f(t) + elseif typ == "table" then + local i = 1 + local n + n = t[i] + while n do + util.walk(n, f) + i = i + 1 + n = t[i] + end + elseif typ == "function" then + local ok, val = pcall(t) + if ok then + util.walk(val,f) + end + else + f(tostring(t)) + end +end +function util.flatten(ary) + local new = {} + for _,v in ipairs(ary) do + if type(v) == "table" then + for _,w in ipairs(util.flatten(v)) do + new[#new + 1] = w + end + else + new[#new + 1] = v + end + end + return new +end +function util.rope_to_string(rope) + local buffer = {} + util.walk(rope, function(x) buffer[#buffer + 1] = x end) + return table.concat(buffer) +end +function util.rope_last(rope) + if #rope == 0 then + return nil + else + local l = rope[#rope] + if type(l) == "table" then + return util.rope_last(l) + else + return l + end + end +end +function util.intersperse(ary, x) + local new = {} + local l = #ary + for i,v in ipairs(ary) do + local n = #new + new[n + 1] = v + if i ~= l then + new[n + 2] = x + end + end + return new +end +function util.map(ary, f) + local new = {} + for i,v in ipairs(ary) do + new[i] = f(v) + end + return new +end +function util.escaper(char_escapes, string_escapes) + local char_escapes_list = "" + for i,_ in pairs(char_escapes) do + char_escapes_list = char_escapes_list .. i + end + local escapable = S(char_escapes_list) / char_escapes + if string_escapes then + for k,v in pairs(string_escapes) do + escapable = P(k) / v + escapable + end + end + local escape_string = Cs((escapable + any)^0) + return function(s) + return lpeg.match(escape_string, s) + end +end +function util.pathname(dir, file) + if #dir == 0 then + return file + else + return dir .. "/" .. file + end +end +M.writer = {} +function M.writer.new(options) + local self = {} + options = options or {} + setmetatable(options, { __index = function (_, key) + return defaultOptions[key] end }) + self.suffix = ".tex" + self.space = " " + function self.plain(s) + return s + end + function self.paragraph(s) + return s + end + function self.pack(name) + return [[\input"]] .. name .. [["\relax]] + end + self.interblocksep = "\n\n" + self.containersep = "\n\n" + self.eof = [[\relax]] + self.linebreak = "\\markdownRendererLineBreak " + self.ellipsis = "\\markdownRendererEllipsis{}" + self.hrule = "\\markdownRendererHorizontalRule " + local escaped = { + ["{"] = "\\{", + ["}"] = "\\}", + ["$"] = "\\$", + ["%"] = "\\%", + ["&"] = "\\&", + ["_"] = "\\_", + ["#"] = "\\#", + ["^"] = "\\^{}", + ["\\"] = "\\char92{}", + ["~"] = "\\char126{}", + ["|"] = "\\char124{}", } + local escape = util.escaper(escaped) + if options.hybrid then + self.string = function(s) return s end + else + self.string = escape + end + function self.code(s) + return {"\\markdownRendererCodeSpan{",escape(s),"}"} + end + function self.link(lab,src,tit) + return {"\\markdownRendererLink{",lab,"}", + "{",self.string(src),"}", + "{",self.string(tit),"}"} + end + function self.image(lab,src,tit) + return {"\\markdownRendererImage{",lab,"}", + "{",self.string(src),"}", + "{",self.string(tit),"}"} + end + local function ulitem(s) + return {"\\markdownRendererUlItem ",s} + end + + function self.bulletlist(items,tight) + local buffer = {} + for _,item in ipairs(items) do + buffer[#buffer + 1] = ulitem(item) + end + local contents = util.intersperse(buffer,"\n") + if tight and options.tightLists then + return {"\\markdownRendererUlBeginTight\n",contents, + "\n\\markdownRendererUlEndTight "} + else + return {"\\markdownRendererUlBegin\n",contents, + "\n\\markdownRendererUlEnd "} + end + end + local function olitem(s,num) + if num ~= nil then + return {"\\markdownRendererOlItemWithNumber{",num,"}",s} + else + return {"\\markdownRendererOlItem ",s} + end + end + + function self.orderedlist(items,tight,startnum) + local buffer = {} + local num = startnum + for _,item in ipairs(items) do + buffer[#buffer + 1] = olitem(item,num) + if num ~= nil then + num = num + 1 + end + end + local contents = util.intersperse(buffer,"\n") + if tight and options.tightLists then + return {"\\markdownRendererOlBeginTight\n",contents, + "\n\\markdownRendererOlEndTight "} + else + return {"\\markdownRendererOlBegin\n",contents, + "\n\\markdownRendererOlEnd "} + end + end + local function dlitem(term,defs) + return {"\\markdownRendererDlItem{",term,"}\n",defs} + end + + function self.definitionlist(items,tight) + local buffer = {} + for _,item in ipairs(items) do + buffer[#buffer + 1] = dlitem(item.term, + util.intersperse(item.definitions, self.interblocksep)) + end + local contents = util.intersperse(buffer, self.containersep) + if tight and options.tightLists then + return {"\\markdownRendererDlBeginTight\n\n", contents, + "\n\n\\markdownRendererDlEndTight\n"} + else + return {"\\markdownRendererDlBegin\n\n", contents, + "\n\n\\markdownRendererDlEnd\n"} + end + end + function self.emphasis(s) + return {"\\markdownRendererEmphasis{",s,"}"} + end + function self.strong(s) + return {"\\markdownRendererStrongEmphasis{",s,"}"} + end + function self.blockquote(s) + return {"\\markdownRendererBlockQuoteBegin\n",s, + "\n\\markdownRendererBlockQuoteEnd "} + end + function self.verbatim(s) + local name = util.cache(options.cacheDir, s, nil, nil, ".verbatim") + return {"\\markdownRendererInputVerbatim{",name,"}"} + end + function self.heading(s,level) + local cmd + if level == 1 then + cmd = "\\markdownRendererHeadingOne" + elseif level == 2 then + cmd = "\\markdownRendererHeadingTwo" + elseif level == 3 then + cmd = "\\markdownRendererHeadingThree" + elseif level == 4 then + cmd = "\\markdownRendererHeadingFour" + elseif level == 5 then + cmd = "\\markdownRendererHeadingFive" + elseif level == 6 then + cmd = "\\markdownRendererHeadingSix" + else + cmd = "" + end + return {cmd,"{",s,"}"} + end + function self.note(s) + return {"\\markdownRendererFootnote{",s,"}"} + end + + return self +end +M.reader = {} +function M.reader.new(writer, options) + local self = {} + options = options or {} + setmetatable(options, { __index = function (_, key) + return defaultOptions[key] end }) + local function normalize_tag(tag) + return unicode.utf8.lower( + gsub(util.rope_to_string(tag), "[ \n\r\t]+", " ")) + end + local expandtabs + if options.preserveTabs then + expandtabs = function(s) return s end + else + expandtabs = function(s) + if s:find("\t") then + return s:gsub("[^\n]*", util.expand_tabs_in_line) + else + return s + end + end + end + local syntax + local blocks + local inlines + + local parse_blocks = + function(str) + local res = lpeg.match(blocks, str) + if res == nil then + error(format("parse_blocks failed on:\n%s", str:sub(1,20))) + else + return res + end + end + + local parse_inlines = + function(str) + local res = lpeg.match(inlines, str) + if res == nil then + error(format("parse_inlines failed on:\n%s", + str:sub(1,20))) + else + return res + end + end + + local parse_inlines_no_link = + function(str) + local res = lpeg.match(inlines_no_link, str) + if res == nil then + error(format("parse_inlines_no_link failed on:\n%s", + str:sub(1,20))) + else + return res + end + end + local percent = P("%") + local asterisk = P("*") + local dash = P("-") + local plus = P("+") + local underscore = P("_") + local period = P(".") + local hash = P("#") + local ampersand = P("&") + local backtick = P("`") + local less = P("<") + local more = P(">") + local space = P(" ") + local squote = P("'") + local dquote = P('"') + local lparent = P("(") + local rparent = P(")") + local lbracket = P("[") + local rbracket = P("]") + local circumflex = P("^") + local slash = P("/") + local equal = P("=") + local colon = P(":") + local semicolon = P(";") + local exclamation = P("!") + + local digit = R("09") + local hexdigit = R("09","af","AF") + local letter = R("AZ","az") + local alphanumeric = R("AZ","az","09") + local keyword = letter * alphanumeric^0 + + local doubleasterisks = P("**") + local doubleunderscores = P("__") + local fourspaces = P(" ") + + local any = P(1) + local fail = any - 1 + local always = P("") + + local escapable = S("\\`*_{}[]()+_.!#-~:^") + local anyescaped = P("\\") / "" * escapable + + any + + local tab = P("\t") + local spacechar = S("\t ") + local spacing = S(" \n\r\t") + local newline = P("\n") + local nonspacechar = any - spacing + local tightblocksep = P("\001") + + local specialchar + if options.smartEllipses then + specialchar = S("*_`&[]!\\.") + else + specialchar = S("*_`&[]!\\") + end + + local normalchar = any - + (specialchar + spacing + tightblocksep) + local optionalspace = spacechar^0 + local spaces = spacechar^1 + local eof = - any + local nonindentspace = space^-3 * - spacechar + local indent = space^-3 * tab + + fourspaces / "" + local linechar = P(1 - newline) + + local blankline = optionalspace * newline / "\n" + local blanklines = blankline^0 + local skipblanklines = (optionalspace * newline)^0 + local indentedline = indent /"" * C(linechar^1 * newline^-1) + local optionallyindentedline = indent^-1 /"" * C(linechar^1 * newline^-1) + local sp = spacing^0 + local spnl = optionalspace * (newline * optionalspace)^-1 + local line = linechar^0 * newline + + linechar^1 * eof + local nonemptyline = line - blankline + + local chunk = line * (optionallyindentedline - blankline)^0 + + -- block followed by 0 or more optionally + -- indented blocks with first line indented. + local function indented_blocks(bl) + return Cs( bl + * (blankline^1 * indent * -blankline * bl)^0 + * blankline^1 ) + end + local bulletchar = C(plus + asterisk + dash) + + local bullet = ( bulletchar * #spacing * (tab + space^-3) + + space * bulletchar * #spacing * (tab + space^-2) + + space * space * bulletchar * #spacing * (tab + space^-1) + + space * space * space * bulletchar * #spacing + ) * -bulletchar + + if options.hashEnumerators then + dig = digit + hash + else + dig = digit + end + + local enumerator = C(dig^3 * period) * #spacing + + C(dig^2 * period) * #spacing * (tab + space^1) + + C(dig * period) * #spacing * (tab + space^-2) + + space * C(dig^2 * period) * #spacing + + space * C(dig * period) * #spacing * (tab + space^-1) + + space * space * C(dig^1 * period) * #spacing + local openticks = Cg(backtick^1, "ticks") + + local function captures_equal_length(s,i,a,b) + return #a == #b and i + end + + local closeticks = space^-1 * + Cmt(C(backtick^1) * Cb("ticks"), captures_equal_length) + + local intickschar = (any - S(" \n\r`")) + + (newline * -blankline) + + (space - closeticks) + + (backtick^1 - closeticks) + + local inticks = openticks * space^-1 * C(intickschar^1) * closeticks + local leader = space^-3 + + -- in balanced brackets, parentheses, quotes: + local bracketed = P{ lbracket + * ((anyescaped - (lbracket + rbracket + + blankline^2)) + V(1))^0 + * rbracket } + + local inparens = P{ lparent + * ((anyescaped - (lparent + rparent + + blankline^2)) + V(1))^0 + * rparent } + + local squoted = P{ squote * alphanumeric + * ((anyescaped - (squote + blankline^2)) + + V(1))^0 + * squote } + + local dquoted = P{ dquote * alphanumeric + * ((anyescaped - (dquote + blankline^2)) + + V(1))^0 + * dquote } + + -- bracketed 'tag' for markdown links, allowing nested brackets: + local tag = lbracket + * Cs((alphanumeric^1 + + bracketed + + inticks + + (anyescaped - (rbracket + blankline^2)))^0) + * rbracket + + -- url for markdown links, allowing balanced parentheses: + local url = less * Cs((anyescaped-more)^0) * more + + Cs((inparens + (anyescaped-spacing-rparent))^1) + + -- quoted text possibly with nested quotes: + local title_s = squote * Cs(((anyescaped-squote) + squoted)^0) * + squote + + local title_d = dquote * Cs(((anyescaped-dquote) + dquoted)^0) * + dquote + + local title_p = lparent + * Cs((inparens + (anyescaped-rparent))^0) + * rparent + + local title = title_d + title_s + title_p + + local optionaltitle = spnl * title * spacechar^0 + + Cc("") + local rawnotes = {} + + local function strip_first_char(s) + return s:sub(2) + end + + -- like indirect_link + local function lookup_note(ref) + return function() + local found = rawnotes[normalize_tag(ref)] + if found then + return writer.note(parse_blocks(found)) + else + return {"[^", ref, "]"} + end + end + end + + local function register_note(ref,rawnote) + rawnotes[normalize_tag(ref)] = rawnote + return "" + end + + local RawNoteRef = #(lbracket * circumflex) * tag / strip_first_char + + local NoteRef = RawNoteRef / lookup_note + + local NoteBlock + + if options.footnotes then + NoteBlock = leader * RawNoteRef * colon * spnl * + indented_blocks(chunk) / register_note + else + NoteBlock = fail + end + -- List of references defined in the document + local references + + -- add a reference to the list + local function register_link(tag,url,title) + references[normalize_tag(tag)] = { url = url, title = title } + return "" + end + + -- parse a reference definition: [foo]: /bar "title" + local define_reference_parser = + leader * tag * colon * spacechar^0 * url * optionaltitle * blankline^1 + + -- lookup link reference and return either + -- the link or nil and fallback text. + local function lookup_reference(label,sps,tag) + local tagpart + if not tag then + tag = label + tagpart = "" + elseif tag == "" then + tag = label + tagpart = "[]" + else + tagpart = {"[", parse_inlines(tag), "]"} + end + if sps then + tagpart = {sps, tagpart} + end + local r = references[normalize_tag(tag)] + if r then + return r + else + return nil, {"[", parse_inlines(label), "]", tagpart} + end + end + + -- lookup link reference and return a link, if the reference is found, + -- or a bracketed label otherwise. + local function indirect_link(label,sps,tag) + return function() + local r,fallback = lookup_reference(label,sps,tag) + if r then + return writer.link(parse_inlines_no_link(label), r.url, r.title) + else + return fallback + end + end + end + + -- lookup image reference and return an image, if the reference is found, + -- or a bracketed label otherwise. + local function indirect_image(label,sps,tag) + return function() + local r,fallback = lookup_reference(label,sps,tag) + if r then + return writer.image(writer.string(label), r.url, r.title) + else + return {"!", fallback} + end + end + end + local Inline = V("Inline") + + local Str = normalchar^1 / writer.string + + local Ellipsis = P("...") / writer.ellipsis + + local Smart = Ellipsis + + local Symbol = (specialchar - tightblocksep) / writer.string + + local Code = inticks / writer.code + + local bqstart = more + local headerstart = hash + + (line * (equal^1 + dash^1) * optionalspace * newline) + + if options.blankBeforeBlockquote then + bqstart = fail + end + + if options.blankBeforeHeading then + headerstart = fail + end + + local Endline = newline * -( -- newline, but not before... + blankline -- paragraph break + + tightblocksep -- nested list + + eof -- end of document + + bqstart + + headerstart + ) * spacechar^0 / writer.space + + local Space = spacechar^2 * Endline / writer.linebreak + + spacechar^1 * Endline^-1 * eof / "" + + spacechar^1 * Endline^-1 * optionalspace / writer.space + + -- parse many p between starter and ender + local function between(p, starter, ender) + local ender2 = B(nonspacechar) * ender + return (starter * #nonspacechar * Ct(p * (p - ender2)^0) * ender2) + end + + local Strong = ( between(Inline, doubleasterisks, doubleasterisks) + + between(Inline, doubleunderscores, doubleunderscores) + ) / writer.strong + + local Emph = ( between(Inline, asterisk, asterisk) + + between(Inline, underscore, underscore) + ) / writer.emphasis + + local urlchar = anyescaped - newline - more + + local AutoLinkUrl = less + * C(alphanumeric^1 * P("://") * urlchar^1) + * more + / function(url) + return writer.link(writer.string(url), url) + end + + local AutoLinkEmail = less + * C((alphanumeric + S("-._+"))^1 * P("@") * urlchar^1) + * more + / function(email) + return writer.link(writer.string(email), + "mailto:"..email) + end + + local DirectLink = (tag / parse_inlines_no_link) -- no links inside links + * spnl + * lparent + * (url + Cc("")) -- link can be empty [foo]() + * optionaltitle + * rparent + / writer.link + + local IndirectLink = tag * (C(spnl) * tag)^-1 / indirect_link + + -- parse a link or image (direct or indirect) + local Link = DirectLink + IndirectLink + + local DirectImage = exclamation + * (tag / parse_inlines) + * spnl + * lparent + * (url + Cc("")) -- link can be empty [foo]() + * optionaltitle + * rparent + / writer.image + + local IndirectImage = exclamation * tag * (C(spnl) * tag)^-1 / + indirect_image + + local Image = DirectImage + IndirectImage + + -- avoid parsing long strings of * or _ as emph/strong + local UlOrStarLine = asterisk^4 + underscore^4 / writer.string + + local EscapedChar = S("\\") * C(escapable) / writer.string + local Block = V("Block") + + local Verbatim = Cs( (blanklines + * ((indentedline - blankline))^1)^1 + ) / expandtabs / writer.verbatim + + -- strip off leading > and indents, and run through blocks + local Blockquote = Cs(( + ((leader * more * space^-1)/"" * linechar^0 * newline)^1 + * (-blankline * linechar^1 * newline)^0 + * blankline^0 + )^1) / parse_blocks / writer.blockquote + + local function lineof(c) + return (leader * (P(c) * optionalspace)^3 * newline * blankline^1) + end + + local HorizontalRule = ( lineof(asterisk) + + lineof(dash) + + lineof(underscore) + ) / writer.hrule + + local Reference = define_reference_parser / register_link + + local Paragraph = nonindentspace * Ct(Inline^1) * newline + * ( blankline^1 + + #hash + + #(leader * more * space^-1) + ) + / writer.paragraph + + local Plain = nonindentspace * Ct(Inline^1) / writer.plain + local starter = bullet + enumerator + + -- we use \001 as a separator between a tight list item and a + -- nested list under it. + local NestedList = Cs((optionallyindentedline - starter)^1) + / function(a) return "\001"..a end + + local ListBlockLine = optionallyindentedline + - blankline - (indent^-1 * starter) + + local ListBlock = line * ListBlockLine^0 + + local ListContinuationBlock = blanklines * (indent / "") * ListBlock + + local function TightListItem(starter) + return -HorizontalRule + * (Cs(starter / "" * ListBlock * NestedList^-1) / + parse_blocks) + * -(blanklines * indent) + end + + local function LooseListItem(starter) + return -HorizontalRule + * Cs( starter / "" * ListBlock * Cc("\n") + * (NestedList + ListContinuationBlock^0) + * (blanklines / "\n\n") + ) / parse_blocks + end + + local BulletList = ( Ct(TightListItem(bullet)^1) + * Cc(true) * skipblanklines * -bullet + + Ct(LooseListItem(bullet)^1) + * Cc(false) * skipblanklines ) / + writer.bulletlist + + local function orderedlist(items,tight,startNumber) + if options.startNumber then + startNumber = tonumber(startNumber) or 1 -- fallback for '#' + else + startNumber = nil + end + return writer.orderedlist(items,tight,startNumber) + end + + local OrderedList = Cg(enumerator, "listtype") * + ( Ct(TightListItem(Cb("listtype")) * + TightListItem(enumerator)^0) + * Cc(true) * skipblanklines * -enumerator + + Ct(LooseListItem(Cb("listtype")) * + LooseListItem(enumerator)^0) + * Cc(false) * skipblanklines + ) * Cb("listtype") / orderedlist + + local defstartchar = S("~:") + local defstart = ( defstartchar * #spacing * (tab + space^-3) + + space * defstartchar * #spacing * (tab + space^-2) + + space * space * defstartchar * #spacing * + (tab + space^-1) + + space * space * space * defstartchar * #spacing + ) + + local dlchunk = Cs(line * (indentedline - blankline)^0) + + local function definition_list_item(term, defs, tight) + return { term = parse_inlines(term), definitions = defs } + end + + local DefinitionListItemLoose = C(line) * skipblanklines + * Ct((defstart * + indented_blocks(dlchunk) / + parse_blocks)^1) + * Cc(false) + / definition_list_item + + local DefinitionListItemTight = C(line) + * Ct((defstart * dlchunk / + parse_blocks)^1) + * Cc(true) + / definition_list_item + + local DefinitionList = ( Ct(DefinitionListItemLoose^1) * Cc(false) + + Ct(DefinitionListItemTight^1) + * (skipblanklines * + -DefinitionListItemLoose * Cc(true)) + ) / writer.definitionlist + local Blank = blankline / "" + + NoteBlock + + Reference + + (tightblocksep / "\n") + -- parse Atx heading start and return level + local HeadingStart = #hash * C(hash^-6) * -hash / length + + -- parse setext header ending and return level + local HeadingLevel = equal^1 * Cc(1) + dash^1 * Cc(2) + + local function strip_atx_end(s) + return s:gsub("[#%s]*\n$","") + end + + -- parse atx header + local AtxHeading = Cg(HeadingStart,"level") + * optionalspace + * (C(line) / strip_atx_end / parse_inlines) + * Cb("level") + / writer.heading + + -- parse setext header + local SetextHeading = #(line * S("=-")) + * Ct(line / parse_inlines) + * HeadingLevel + * optionalspace * newline + / writer.heading + syntax = + { "Blocks", + + Blocks = Blank^0 * + Block^-1 * + (Blank^0 / function() + return writer.interblocksep + end * Block)^0 * + Blank^0 * + eof, + + Blank = Blank, + + Block = V("Blockquote") + + V("Verbatim") + + V("HorizontalRule") + + V("BulletList") + + V("OrderedList") + + V("Heading") + + V("DefinitionList") + + V("Paragraph") + + V("Plain"), + + Blockquote = Blockquote, + Verbatim = Verbatim, + HorizontalRule = HorizontalRule, + BulletList = BulletList, + OrderedList = OrderedList, + Heading = AtxHeading + SetextHeading, + DefinitionList = DefinitionList, + DisplayHtml = DisplayHtml, + Paragraph = Paragraph, + Plain = Plain, + + Inline = V("Str") + + V("Space") + + V("Endline") + + V("UlOrStarLine") + + V("Strong") + + V("Emph") + + V("NoteRef") + + V("Link") + + V("Image") + + V("Code") + + V("AutoLinkUrl") + + V("AutoLinkEmail") + + V("EscapedChar") + + V("Smart") + + V("Symbol"), + + Str = Str, + Space = Space, + Endline = Endline, + UlOrStarLine = UlOrStarLine, + Strong = Strong, + Emph = Emph, + NoteRef = NoteRef, + Link = Link, + Image = Image, + Code = Code, + AutoLinkUrl = AutoLinkUrl, + AutoLinkEmail = AutoLinkEmail, + InlineHtml = InlineHtml, + HtmlEntity = HtmlEntity, + EscapedChar = EscapedChar, + Smart = Smart, + Symbol = Symbol, + } + + if not options.definitionLists then + syntax.DefinitionList = fail + end + + if not options.footnotes then + syntax.NoteRef = fail + end + + if not options.smartEllipses then + syntax.Smart = fail + end + + blocks = Ct(syntax) + + local inlines_t = util.table_copy(syntax) + inlines_t[1] = "Inlines" + inlines_t.Inlines = Inline^0 * (spacing^0 * eof / "") + inlines = Ct(inlines_t) + + inlines_no_link_t = util.table_copy(inlines_t) + inlines_no_link_t.Link = fail + inlines_no_link = Ct(inlines_no_link_t) + function self.convert(input) + references = {} + local opt_string = {} + for k,_ in pairs(defaultOptions) do + local v = options[k] + if k ~= "cacheDir" then + opt_string[#opt_string+1] = k .. "=" .. tostring(v) + end + end + table.sort(opt_string) + local salt = table.concat(opt_string, ",") + local name = util.cache(options.cacheDir, input, salt, function(input) + return util.rope_to_string(parse_blocks(input)) .. writer.eof + end, ".md" .. writer.suffix) + return writer.pack(name) + end + return self +end +function M.new(options) + local writer = M.writer.new(options) + local reader = M.reader.new(writer, options) + return reader.convert +end + +return M diff --git a/Master/tlpkg/bin/tlpkg-ctan-check b/Master/tlpkg/bin/tlpkg-ctan-check index e85aedc0cb3..8220c906295 100755 --- a/Master/tlpkg/bin/tlpkg-ctan-check +++ b/Master/tlpkg/bin/tlpkg-ctan-check @@ -374,7 +374,7 @@ my @TLP_working = qw( make4ht makebarcode makebase makebox makecell makecirc makecmds makedtx makeglos makeplot makeshape mandi manfnt manfnt-font manuscript margbib - marginfix marginnote marvosym + marginfix marginnote markdown marvosym matc3 matc3mem match_parens math-e mathabx mathabx-type1 mathalfa mathastext mathcomp mathdesign mathdots mathexam mathpartir diff --git a/Master/tlpkg/tlpsrc/collection-latexextra.tlpsrc b/Master/tlpkg/tlpsrc/collection-latexextra.tlpsrc index cd62a12af62..b98c1867600 100644 --- a/Master/tlpkg/tlpsrc/collection-latexextra.tlpsrc +++ b/Master/tlpkg/tlpsrc/collection-latexextra.tlpsrc @@ -607,6 +607,7 @@ depend manfnt depend manuscript depend marginfix depend marginnote +depend markdown depend mathalfa depend mathastext depend mathexam diff --git a/Master/tlpkg/tlpsrc/markdown.tlpsrc b/Master/tlpkg/tlpsrc/markdown.tlpsrc new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/Master/tlpkg/tlpsrc/markdown.tlpsrc |