diff options
Diffstat (limited to 'Master')
-rw-r--r-- | Master/texmf-dist/doc/generic/markdown/CHANGES.md | 45 | ||||
-rw-r--r-- | Master/texmf-dist/doc/generic/markdown/README.md | 14 | ||||
-rw-r--r-- | Master/texmf-dist/doc/generic/markdown/VERSION | 2 | ||||
-rw-r--r-- | Master/texmf-dist/doc/generic/markdown/markdown.html | 58 | ||||
-rw-r--r-- | Master/texmf-dist/doc/generic/markdown/markdown.pdf | bin | 1089312 -> 1099548 bytes | |||
-rw-r--r-- | Master/texmf-dist/scripts/markdown/markdown-cli.lua | 2 | ||||
-rw-r--r-- | Master/texmf-dist/source/generic/markdown/markdown.dtx | 977 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/markdown/markdown.tex | 4 | ||||
-rw-r--r-- | Master/texmf-dist/tex/luatex/markdown/markdown.lua | 407 |
9 files changed, 936 insertions, 573 deletions
diff --git a/Master/texmf-dist/doc/generic/markdown/CHANGES.md b/Master/texmf-dist/doc/generic/markdown/CHANGES.md index 48d2d7066d0..614cef6912c 100644 --- a/Master/texmf-dist/doc/generic/markdown/CHANGES.md +++ b/Master/texmf-dist/doc/generic/markdown/CHANGES.md @@ -2,6 +2,41 @@ ## 3.5.0 +## 3.4.3 + +Fixes: + +- Remove trailing paragraph/interblock separators in right-open slice + intervals. (#408, #419) +- Do not misinterpret bracketed e-mails as citations. (#424, #426, + sponsored by @istqborg) +- Comply with CommonMark 0.31.2. (#416, 40b516ee, de8d137d, #432, + contributed by @lostenderman) +- Do not end a paragraph before a `:::` in fenced divs. + (#407, lostenderman/markdown#157, #427, #428, lostenderman/markdown#158, + #431, contributed by @lostenderman) + +Documentation: + +- Add slides from the defense of projects MUNI/33/1654/2022 and + MUNI/33/1658/2022 to `README.md`. (49f01ccf) +- Remove `<mroot>` from MathML in the user manual. (#420, #422, + contributed by @quark67) + +Contributed Software: + +- Make the documentation of contributions more detailed. (3f928162) + +Docker: + +- Add support for TeX Live 2024. + (#411, bafbb164, #413, 04957eee, 16000aa4, #425, 9549a5d8, 8f8e1315, + 844beafc, cf592003) + +Continuous Integration: + +- Style-check tabs and trailing spaces in `markdown.dtx`. (a0c941ca) + ## 3.4.2 (2024-03-09) Fixes: @@ -119,7 +154,7 @@ Fixes: - Remove the `options.cacheDir` directory if it is empty after conversion. (5cfcea6) - Allow tables inside lists. (#368, #371, contributed by @lostenderman, - sponsored by ISTQB) + sponsored by @istqborg) - Check that shell access is unrestricted before attempting shell escape. (#365, witiko/lt3luabridge#22, latex3/latex3#1339, #372) @@ -152,7 +187,7 @@ Development: Fixes: - Make the `import` LaTeX option correctly handle recursive imports. - (68c7a2f5, danopolan/istqb_latex#87) + (68c7a2f5, istqborg/istqb_shared_documents#87) - Support attributes on multi-line setext headings. (#315, #355, #356, contributed by @lostenderman) - Correctly process the combination of Lua options `fancyLists` and @@ -160,8 +195,8 @@ Fixes: - Properly parse emphasis at line endings in headings. (#358, #360, contributed by @lostenderman) - Fix fancy lists that use roman numerals as markers. - (danopolan/istqb_latex#87, #359, #364, - contributed by @lostenderman, sponsored by ISTQB) + (istqborg/istqb_shared_documents#87, #359, #364, + contributed by @lostenderman, sponsored by @istqborg) Documentation: @@ -232,7 +267,7 @@ Documentation: Fixes: - Correctly parse paragraphs with trailing spaces. - (danopolan/istqb_latex#77, #345, #347) + (istqborg/istqb_shared_documents#77, #345, #347) ## 3.0.0 (2023-08-25) diff --git a/Master/texmf-dist/doc/generic/markdown/README.md b/Master/texmf-dist/doc/generic/markdown/README.md index 5a87521f0fc..76ad39d740b 100644 --- a/Master/texmf-dist/doc/generic/markdown/README.md +++ b/Master/texmf-dist/doc/generic/markdown/README.md @@ -204,9 +204,10 @@ For further information, consult one of the following: 6. Talks: - [Five Years of Markdown in LaTeX: What, Why, How, and Whereto][pv212-fall2020] (in Czech), - [Markdown 2.10.0: LaTeX Themes & Snippets, Two Flavors of Comments, and LuaMetaTeX][tb131-video] ([slides][tb131-slides]), - - [A Self-Publisher's Take on Markdown and TeX][tb134-01-video] ([slides][tb134-01-slides]), and - - [A Gentle Introduction to Markdown for Writers][tb134-02-video] ([slides][tb134-02-slides], [example][tb134-02-example]), and - - [Markdown 3: What's New, What's Next?][tb137-video] ([slides][tb137-slides]). + - [A Self-Publisher's Take on Markdown and TeX][tb134-01-video] ([slides][tb134-01-slides]), + - [A Gentle Introduction to Markdown for Writers][tb134-02-video] ([slides][tb134-02-slides], [example][tb134-02-example]), + - [Markdown 3: What's New, What's Next?][tb137-video] ([slides][tb137-slides]), and + - An Implementation of the CommonMark Standard and new Syntax Extensions to the Markdown Package for TeX ([slides][gencur-defense-slides]). 7. Theses: - [Generic TeX Writer for the Pandoc Document Converter][thesis-umhg5] - [An implementation of the CommonMark standard into the Markdown package for TeX][thesis-r7z7l] @@ -248,6 +249,8 @@ For further information, consult one of the following: [pv212-fall2020]: https://is.muni.cz/elearning/io/?qurl=%2Fel%2Ffi%2Fpodzim2020%2FPV212%2Findex.qwarp;prejit=5595952 + [gencur-defense-slides]: https://docs.google.com/presentation/d/e/2PACX-1vRbgJZ-UJlj5WMOjgWnq0BeNWKdA9ZhFqCKajhCjXtv3OarSKmojl5-X8tDp1ivnKtujuyEDmD2z_Z0/pub "An Implementation of the CommonMark Standard and new Syntax Extensions to the Markdown Package for TeX" + [install]: https://mirrors.ctan.org/macros/generic/markdown/markdown.html#installation "Markdown Package User Manual" [liantze]: http://liantze.penguinattack.org/ "Rants from the Lab" [overleaf]: https://www.overleaf.com/ "Overleaf: Real-time Collaborative Writing and Publishing Tools with Integrated PDF Preview" @@ -271,16 +274,17 @@ Acknowledgements | [<img width="150" src="https://www.fi.muni.cz/images/fi-logo.png">][fimu] | I gratefully acknowledge the funding from the [Faculty of Informatics][fimu] at the [Masaryk University][mu] in Brno, Czech Republic, for the development of the Markdown package in projects [MUNI/33/12/2015][], [MUNI/33/1784/2020][], [MUNI/33/0776/2021][], [MUNI/33/1654/2022][], and [MUNI/33/1658/2022][]. | | [<img width="150" src="https://cdn.overleaf.com/img/ol-brand/overleaf_og_logo.png">][overleaf] | Extensive user documentation for the Markdown package was kindly written by [Lian Tze Lim][liantze] and published by [Overleaf][]. | | [<img width="150" src="https://pbs.twimg.com/profile_images/1004769879319334912/6Bh1UthD.jpg">][omedym] | Support for content slicing (Lua options [`shiftHeadings`][option-shift-headings] and [`slice`][option-slice]) and pipe tables (Lua options [`pipeTables`][option-pipe-tables] and [`tableCaptions`][option-table-captions]) was graciously sponsored by [David Vins][dvins] and [Omedym][]. | -| [<img width="150" src="https://www.istqb.org/static/istqb-logo-1b043e800a580724ad223567f9ea57c0.png">][istqb] | Fixes for issues [#359][issue-359] and [#368][issue-368] were graciously sponsored by the [International Software Testing Qualifications Board (ISTQB)][istqb]. | +| [<img width="150" src="https://www.istqb.org/static/istqb-logo-1b043e800a580724ad223567f9ea57c0.png">][istqb] | Fixes for issues [#359][issue-359], [#368][issue-368], and [#424][issue-424] were graciously sponsored by the [International Software Testing Qualifications Board (ISTQB)][istqb]. | [dvins]: https://github.com/dvins "David Vins" [fimu]: https://www.fi.muni.cz/index.html.en "Faculty of Informatics, Masaryk University" - [ISTQB]: https://www.istqb.org/ "International Software Testing Qualifications Board" + [ISTQB]: https://github.com/istqborg "ISTQB.ORG: Official ISTQB® GitHub account" [mu]: https://www.muni.cz/en "Masaryk University" [Omedym]: https://www.omedym.com/ "Omedym" [issue-359]: https://github.com/witiko/markdown/issues/359 "First item of a fancy list forms a separate list" [issue-368]: https://github.com/witiko/markdown/issues/368 "Tables nested in list items have empty lines" + [issue-424]: https://github.com/witiko/markdown/issues/424 "E-mail addresses are incorrectly interpreted as bracketed citations" [option-pipe-tables]: https://mirrors.ctan.org/macros/generic/markdown/markdown.html#pipe-tables "Markdown Package User Manual" [option-shift-headings]: https://mirrors.ctan.org/macros/generic/markdown/markdown.html#option-shiftheadings "Markdown Package User Manual" diff --git a/Master/texmf-dist/doc/generic/markdown/VERSION b/Master/texmf-dist/doc/generic/markdown/VERSION index 7fba28f70e4..d13fdfa34fd 100644 --- a/Master/texmf-dist/doc/generic/markdown/VERSION +++ b/Master/texmf-dist/doc/generic/markdown/VERSION @@ -1 +1 @@ -3.4.2-0-ga45cf0ed (2024-03-09) +3.4.3-0-ge2c6be1a (2024-04-04) diff --git a/Master/texmf-dist/doc/generic/markdown/markdown.html b/Master/texmf-dist/doc/generic/markdown/markdown.html index a5616317fcd..277f98bdebd 100644 --- a/Master/texmf-dist/doc/generic/markdown/markdown.html +++ b/Master/texmf-dist/doc/generic/markdown/markdown.html @@ -93,7 +93,7 @@ <header id="title-block-header"> <h1 class="title">Markdown Package User Manual</h1> <p class="author">Vít Starý Novotný</p> -<p class="date">3.4.2-0-ga45cf0ed 2024-03-09</p> +<p class="date">3.4.3-0-ge2c6be1a 2024-04-04</p> </header> <nav id="TOC" role="doc-toc"> <ul> @@ -604,8 +604,8 @@ class="sourceCode sh"><code class="sourceCode bash"><span id="cb31-1"><a href="# contain the following text:</p> <blockquote> <p>$\sqrt{-1}$ <em>equals</em> $i$.</p> -<p><math><mroot><msqrt><mo>−</mo><mn>1</mn></msqrt></mroot></math> -<em>equals</em> <math><mi>i</mi></math>.</p> +<p><math><msqrt><mo>−</mo><mn>1</mn></msqrt></math> <em>equals</em> +<math><mi>i</mi></math>.</p> </blockquote> <p>Invoking pdfTeX should have the same effect:</p> <div class="sourceCode" id="cb32"><pre @@ -641,8 +641,8 @@ class="sourceCode sh"><code class="sourceCode bash"><span id="cb34-1"><a href="# contain the following text:</p> <blockquote> <p>$\sqrt{-1}$ <em>equals</em> $i$.</p> -<p><math><mroot><msqrt><mo>−</mo><mn>1</mn></msqrt></mroot></math> -<em>equals</em> <math><mi>i</mi></math>.</p> +<p><math><msqrt><mo>−</mo><mn>1</mn></msqrt></math> <em>equals</em> +<math><mi>i</mi></math>.</p> </blockquote> <p>In this case, we cannot use pdfTeX, because pdfTeX does not define the <code>\directlua</code> <span class="tex">T<sub>e</sub>X</span> @@ -693,8 +693,8 @@ title="Kpathsea - TeX Users Group">Kpathsea</a>.</p> contain the following text:</p> <blockquote> <p>$\sqrt{-1}$ <em>equals</em> $i$.</p> -<p><math><mroot><msqrt><mo>−</mo><mn>1</mn></msqrt></mroot></math> -<em>equals</em> <math><mi>i</mi></math>.</p> +<p><math><msqrt><mo>−</mo><mn>1</mn></msqrt></math> <em>equals</em> +<math><mi>i</mi></math>.</p> </blockquote> <p>Invoking pdfTeX should have the same effect:</p> <div class="sourceCode" id="cb38"><pre @@ -737,8 +737,8 @@ class="sourceCode sh"><code class="sourceCode bash"><span id="cb40-1"><a href="# contain the following text:</p> <blockquote> <p>$\sqrt{-1}$ <em>equals</em> $i$.</p> -<p><math><mroot><msqrt><mo>−</mo><mn>1</mn></msqrt></mroot></math> -<em>equals</em> <math><mi>i</mi></math>.</p> +<p><math><msqrt><mo>−</mo><mn>1</mn></msqrt></math> <em>equals</em> +<math><mi>i</mi></math>.</p> </blockquote> <p>Invoking pdfTeX should have the same effect:</p> <div class="sourceCode" id="cb41"><pre @@ -785,8 +785,8 @@ class="sourceCode sh"><code class="sourceCode bash"><span id="cb43-1"><a href="# contain the following text:</p> <blockquote> <p>$\sqrt{-1}$ <em>equals</em> $i$.</p> -<p><math><mroot><msqrt><mo>−</mo><mn>1</mn></msqrt></mroot></math> -<em>equals</em> <math><mi>i</mi></math>.</p> +<p><math><msqrt><mo>−</mo><mn>1</mn></msqrt></math> <em>equals</em> +<math><mi>i</mi></math>.</p> </blockquote> <p>Invoking pdfTeX should have the same effect:</p> <div class="sourceCode" id="cb44"><pre @@ -828,8 +828,8 @@ class="sourceCode sh"><code class="sourceCode bash"><span id="cb46-1"><a href="# contain the following text:</p> <blockquote> <p>$\sqrt{-1}$ <em>equals</em> $i$.</p> -<p><math><mroot><msqrt><mo>−</mo><mn>1</mn></msqrt></mroot></math> -<em>equals</em> <math><mi>i</mi></math>.</p> +<p><math><msqrt><mo>−</mo><mn>1</mn></msqrt></math> <em>equals</em> +<math><mi>i</mi></math>.</p> </blockquote> <h2 data-number="2.2" id="options"><span class="header-section-number">2.2</span> Options</h2> @@ -4679,8 +4679,8 @@ class="sourceCode sh"><code class="sourceCode bash"><span id="cb279-1"><a href=" contain the following text:</p> <blockquote> <p>$\sqrt {-1}$ <em>equals</em> $i$.</p> -<p><math><mroot><msqrt><mo>−</mo><mn>1</mn></msqrt></mroot></math> -<em>equals</em> <math><mi>i</mi></math>.</p> +<p><math><msqrt><mo>−</mo><mn>1</mn></msqrt></math> <em>equals</em> +<math><mi>i</mi></math>.</p> </blockquote> <h5 class="unnumbered" id="lua-cli-example-10">Lua CLI Example</h5> <p>Using a text editor, create a text document named @@ -4718,8 +4718,8 @@ title="Kpathsea - TeX Users Group">Kpathsea</a>.</p> contain the following text:</p> <blockquote> <p>$\sqrt {-1}$ <em>equals</em> $i$.</p> -<p><math><mroot><msqrt><mo>−</mo><mn>1</mn></msqrt></mroot></math> -<em>equals</em> <math><mi>i</mi></math>.</p> +<p><math><msqrt><mo>−</mo><mn>1</mn></msqrt></math> <em>equals</em> +<math><mi>i</mi></math>.</p> </blockquote> <h5 class="unnumbered" id="plain-tex-example-11">Plain <span class="tex">T<sub>e</sub>X</span> Example</h5> @@ -4746,8 +4746,8 @@ class="sourceCode sh"><code class="sourceCode bash"><span id="cb284-1"><a href=" contain the following text:</p> <blockquote> <p>$\sqrt {-1}$ <em>equals</em> $i$.</p> -<p><math><mroot><msqrt><mo>−</mo><mn>1</mn></msqrt></mroot></math> -<em>equals</em> <math><mi>i</mi></math>.</p> +<p><math><msqrt><mo>−</mo><mn>1</mn></msqrt></math> <em>equals</em> +<math><mi>i</mi></math>.</p> </blockquote> <h5 class="unnumbered" id="latex-example-26"><span class="latex">L<sup>a</sup>T<sub>e</sub>X</span> Example</h5> @@ -4774,8 +4774,8 @@ class="sourceCode sh"><code class="sourceCode bash"><span id="cb286-1"><a href=" contain the following text:</p> <blockquote> <p>$\sqrt {-1}$ <em>equals</em> $i$.</p> -<p><math><mroot><msqrt><mo>−</mo><mn>1</mn></msqrt></mroot></math> -<em>equals</em> <math><mi>i</mi></math>.</p> +<p><math><msqrt><mo>−</mo><mn>1</mn></msqrt></math> <em>equals</em> +<math><mi>i</mi></math>.</p> </blockquote> <h5 class="unnumbered" id="context-example-17">Con<span class="tex">T<sub>e</sub>X</span>t Example</h5> @@ -4802,8 +4802,8 @@ class="sourceCode sh"><code class="sourceCode bash"><span id="cb288-1"><a href=" contain the following text:</p> <blockquote> <p>$\sqrt {-1}$ <em>equals</em> $i$.</p> -<p><math><mroot><msqrt><mo>−</mo><mn>1</mn></msqrt></mroot></math> -<em>equals</em> <math><mi>i</mi></math>.</p> +<p><math><msqrt><mo>−</mo><mn>1</mn></msqrt></math> <em>equals</em> +<math><mi>i</mi></math>.</p> </blockquote> <h4 data-number="2.2.1.35" id="option-inlinecodeattributes"><span class="header-section-number">2.2.1.35</span> Option @@ -9109,8 +9109,8 @@ contain the following text:</p> <blockquote> <p>$\sqrt{-1}$ *equals* $i$.</p> <p>$\sqrt{-1}$ <em>equals</em> $i$.</p> -<p><math><mroot><msqrt><mo>−</mo><mn>1</mn></msqrt></mroot></math> -<em>equals</em> <math><mi>i</mi></math>.</p> +<p><math><msqrt><mo>−</mo><mn>1</mn></msqrt></math> <em>equals</em> +<math><mi>i</mi></math>.</p> </blockquote> <h5 class="unnumbered" id="latex-example-63"><span class="latex">L<sup>a</sup>T<sub>e</sub>X</span> Example</h5> @@ -9145,8 +9145,8 @@ contain the following text:</p> <blockquote> <p>$\sqrt{-1}$ *equals* $i$.</p> <p>$\sqrt{-1}$ <em>equals</em> $i$.</p> -<p><math><mroot><msqrt><mo>−</mo><mn>1</mn></msqrt></mroot></math> -<em>equals</em> <math><mi>i</mi></math>.</p> +<p><math><msqrt><mo>−</mo><mn>1</mn></msqrt></math> <em>equals</em> +<math><mi>i</mi></math>.</p> </blockquote> <h5 class="unnumbered" id="context-example-40">Con<span class="tex">T<sub>e</sub>X</span>t Example</h5> @@ -9177,8 +9177,8 @@ contain the following text:</p> <blockquote> <p>$\sqrt{-1}$ *equals* $i$.</p> <p>$\sqrt{-1}$ <em>equals</em> $i$.</p> -<p><math><mroot><msqrt><mo>−</mo><mn>1</mn></msqrt></mroot></math> -<em>equals</em> <math><mi>i</mi></math>.</p> +<p><math><msqrt><mo>−</mo><mn>1</mn></msqrt></math> <em>equals</em> +<math><mi>i</mi></math>.</p> </blockquote> <h4 data-number="2.3.1.8" id="code-span-attribute-context-renderers"><span diff --git a/Master/texmf-dist/doc/generic/markdown/markdown.pdf b/Master/texmf-dist/doc/generic/markdown/markdown.pdf Binary files differindex 34822a723fa..8e42e32d0b1 100644 --- a/Master/texmf-dist/doc/generic/markdown/markdown.pdf +++ b/Master/texmf-dist/doc/generic/markdown/markdown.pdf diff --git a/Master/texmf-dist/scripts/markdown/markdown-cli.lua b/Master/texmf-dist/scripts/markdown/markdown-cli.lua index 8e1b5d667af..610c982cbff 100644 --- a/Master/texmf-dist/scripts/markdown/markdown-cli.lua +++ b/Master/texmf-dist/scripts/markdown/markdown-cli.lua @@ -58,7 +58,7 @@ -- those in the standard .ins files. -- local metadata = { - version = "3.4.2-0-ga45cf0ed", + version = "3.4.3-0-ge2c6be1a", comment = "A module for the conversion from markdown to plain TeX", author = "John MacFarlane, Hans Hagen, Vít Starý Novotný", copyright = {"2009-2016 John MacFarlane, Hans Hagen", diff --git a/Master/texmf-dist/source/generic/markdown/markdown.dtx b/Master/texmf-dist/source/generic/markdown/markdown.dtx index 5ce5a6496e1..dd4c7367fb8 100644 --- a/Master/texmf-dist/source/generic/markdown/markdown.dtx +++ b/Master/texmf-dist/source/generic/markdown/markdown.dtx @@ -104,7 +104,13 @@ \newunicodechar{☒}{\markdownRendererTickedBox} \newunicodechar{⌛}{\markdownRendererHalfTickedBox} \newunicodechar{☐}{\markdownRendererUntickedBox} -\newunicodechar{😉}{;-)} +\usepackage{emoji} +\makeatletter +\@ifpackagelater{emoji}{2020/03/16}{ + \newunicodechar{😉}{\emoji{winking-face}} +}{ + \newunicodechar{😉}{;-)} +} \makeatother % Set up the catcodes. @@ -1881,7 +1887,7 @@ following text: > \$\\sqrt{-1}\$ *equals* \$i\$. > -> <math><mroot><msqrt><mo>−</mo><mn>1</mn></msqrt></mroot></math> +> <math><msqrt><mo>−</mo><mn>1</mn></msqrt></math> > *equals* > <math><mi>i</mi></math>. @@ -1924,7 +1930,7 @@ following text: > \$\\sqrt{-1}\$ *equals* \$i\$. > -> <math><mroot><msqrt><mo>−</mo><mn>1</mn></msqrt></mroot></math> +> <math><msqrt><mo>−</mo><mn>1</mn></msqrt></math> > *equals* > <math><mi>i</mi></math>. @@ -1977,7 +1983,7 @@ following text: > \$\\sqrt{-1}\$ *equals* \$i\$. > -> <math><mroot><msqrt><mo>−</mo><mn>1</mn></msqrt></mroot></math> +> <math><msqrt><mo>−</mo><mn>1</mn></msqrt></math> > *equals* > <math><mi>i</mi></math>. @@ -5563,15 +5569,15 @@ A PDF document named `document.pdf` should be produced and contain the following text: > Term 1 -> +> > : Definition 1 -> +> > Term 2 with *inline markup* -> +> > : Definition 2 -> +> > { some code, part of Definition 2 } -> +> > Third paragraph of definition 2. ##### \Hologo{ConTeXt} Example {.unnumbered} @@ -5605,15 +5611,15 @@ A PDF document named `document.pdf` should be produced and contain the following text: > Term 1 -> +> > : Definition 1 -> +> > Term 2 with *inline markup* -> +> > : Definition 2 -> +> > { some code, part of Definition 2 } -> +> > Third paragraph of definition 2. %</manual-options> @@ -7157,7 +7163,7 @@ following text: > \$\\sqrt {-1}\$ *equals* \$i\$. > -> <math><mroot><msqrt><mo>−</mo><mn>1</mn></msqrt></mroot></math> +> <math><msqrt><mo>−</mo><mn>1</mn></msqrt></math> > *equals* > <math><mi>i</mi></math>. @@ -7199,7 +7205,7 @@ following text: > \$\\sqrt {-1}\$ *equals* \$i\$. > -> <math><mroot><msqrt><mo>−</mo><mn>1</mn></msqrt></mroot></math> +> <math><msqrt><mo>−</mo><mn>1</mn></msqrt></math> > *equals* > <math><mi>i</mi></math>. @@ -7231,7 +7237,7 @@ following text: > \$\\sqrt {-1}\$ *equals* \$i\$. > -> <math><mroot><msqrt><mo>−</mo><mn>1</mn></msqrt></mroot></math> +> <math><msqrt><mo>−</mo><mn>1</mn></msqrt></math> > *equals* > <math><mi>i</mi></math>. @@ -7263,7 +7269,7 @@ following text: > \$\\sqrt {-1}\$ *equals* \$i\$. > -> <math><mroot><msqrt><mo>−</mo><mn>1</mn></msqrt></mroot></math> +> <math><msqrt><mo>−</mo><mn>1</mn></msqrt></math> > *equals* > <math><mi>i</mi></math>. @@ -7295,7 +7301,7 @@ following text: > \$\\sqrt {-1}\$ *equals* \$i\$. > -> <math><mroot><msqrt><mo>−</mo><mn>1</mn></msqrt></mroot></math> +> <math><msqrt><mo>−</mo><mn>1</mn></msqrt></math> > *equals* > <math><mi>i</mi></math>. @@ -8066,20 +8072,20 @@ A PDF document named `document.pdf` should be produced and contain the following text: > Here is a note reference,[^1] and another.[^longnote] -> +> > [^1]: Here is the note. -> +> > [^longnote]: Here's one with multiple blocks. -> +> > Subsequent paragraphs are indented to show that they > belong to the previous note. -> +> > { some.code } -> +> > The whole paragraph can be indented, or just the > first line. In this way, multi-paragraph notes > work like multi-paragraph list items. -> +> > This paragraph won't be part of the note, because it > isn't indented. @@ -8120,20 +8126,20 @@ A PDF document named `document.pdf` should be produced and contain the following text: > Here is a note reference,[^1] and another.[^longnote] -> +> > [^1]: Here is the note. -> +> > [^longnote]: Here's one with multiple blocks. -> +> > Subsequent paragraphs are indented to show that they > belong to the previous note. -> +> > { some.code } -> +> > The whole paragraph can be indented, or just the > first line. In this way, multi-paragraph notes > work like multi-paragraph list items. -> +> > This paragraph won't be part of the note, because it > isn't indented. @@ -9123,13 +9129,13 @@ A PDF document named `document.pdf` should be produced and contain the following text: > The following list respects the numbers specified in the markup: -> +> > 3. third item > 4. fourth item > 5. fifth item > > The following list does not respect the numbers specified in the markup: -> +> > 1. third item > 2. fourth item > 3. fifth item @@ -9168,13 +9174,13 @@ A PDF document named `document.pdf` should be produced and contain the following text: > The following list respects the numbers specified in the markup: -> +> > 3. third item > 4. fourth item > 5. fifth item > > The following list does not respect the numbers specified in the markup: -> +> > 1. third item > 2. fourth item > 3. fifth item @@ -9812,7 +9818,7 @@ following text: > | 12 | 12 | 12 | 12 | > | 123 | 123 | 123 | 123 | > | 1 | 1 | 1 | 1 | -> +> > : Demonstration of pipe table syntax. ##### \Hologo{ConTeXt} Example {.unnumbered} @@ -9850,7 +9856,7 @@ following text: > | 12 | 12 | 12 | 12 | > | 123 | 123 | 123 | 123 | > | 1 | 1 | 1 | 1 | -> +> > : Demonstration of pipe table syntax. %</manual-options> @@ -10234,7 +10240,7 @@ A PDF document named `document.pdf` should be produced and contain the following text: > $E=mc^2$ -> +> > $$\hat{f} \left ( \xi \right )= \int_{-\infty}^{\infty} f\left ( x \right ) e^{-i2\pi \xi x} dx$$ ##### \LaTeX{} Example {.unnumbered} @@ -10262,7 +10268,7 @@ A PDF document named `document.pdf` should be produced and contain the following text: > $E=mc^2$ -> +> > $$\hat{f} \left ( \xi \right )= \int_{-\infty}^{\infty} f\left ( x \right ) e^{-i2\pi \xi x} dx$$ ##### \Hologo{ConTeXt} Example {.unnumbered} @@ -10290,7 +10296,7 @@ A PDF document named `document.pdf` should be produced and contain the following text: > $E=mc^2$ -> +> > $$\hat{f} \left ( \xi \right )= \int_{-\infty}^{\infty} f\left ( x \right ) e^{-i2\pi \xi x} dx$$ %</manual-options> @@ -10445,7 +10451,7 @@ A PDF document named `document.pdf` should be produced and contain the following text: > \\(E=mc^2\\) -> +> > \\[\hat{f} \left ( \xi \right )= \int_{-\infty}^{\infty} f\left ( x \right ) e^{-i2\pi \xi x} dx\\] ##### \LaTeX{} Example {.unnumbered} @@ -10473,7 +10479,7 @@ A PDF document named `document.pdf` should be produced and contain the following text: > \\(E=mc^2\\) -> +> > \\[\hat{f} \left ( \xi \right )= \int_{-\infty}^{\infty} f\left ( x \right ) e^{-i2\pi \xi x} dx\\] ##### \Hologo{ConTeXt} Example {.unnumbered} @@ -10501,7 +10507,7 @@ A PDF document named `document.pdf` should be produced and contain the following text: > \\(E=mc^2\\) -> +> > \\[\hat{f} \left ( \xi \right )= \int_{-\infty}^{\infty} f\left ( x \right ) e^{-i2\pi \xi x} dx\\] %</manual-options> @@ -10656,7 +10662,7 @@ A PDF document named `document.pdf` should be produced and contain the following text: > \(E=mc^2\) -> +> > \[\hat{f} \left ( \xi \right )= \int_{-\infty}^{\infty} f\left ( x \right ) e^{-i2\pi \xi x} dx\] ##### \LaTeX{} Example {.unnumbered} @@ -10684,7 +10690,7 @@ A PDF document named `document.pdf` should be produced and contain the following text: > \(E=mc^2\) -> +> > \[\hat{f} \left ( \xi \right )= \int_{-\infty}^{\infty} f\left ( x \right ) e^{-i2\pi \xi x} dx\] ##### \Hologo{ConTeXt} Example {.unnumbered} @@ -10712,7 +10718,7 @@ A PDF document named `document.pdf` should be produced and contain the following text: > \(E=mc^2\) -> +> > \[\hat{f} \left ( \xi \right )= \int_{-\infty}^{\infty} f\left ( x \right ) e^{-i2\pi \xi x} dx\] %</manual-options> @@ -10815,21 +10821,21 @@ A PDF document named `document.pdf` should be produced and contain the following text: > The following list is tight: -> +> > - first item > - second item > - third item -> +> > The following list is loose: -> +> > - first item > - second item that spans -> +> > multiple paragraphs > - third item -> +> > The following list is now also loose: -> +> > - first item > > - second item @@ -11298,7 +11304,7 @@ following text: > \$\\sqrt{-1}\$ *equals* \$i\$. > -> <math><mroot><msqrt><mo>−</mo><mn>1</mn></msqrt></mroot></math> +> <math><msqrt><mo>−</mo><mn>1</mn></msqrt></math> > *equals* > <math><mi>i</mi></math>. @@ -11373,10 +11379,10 @@ pdftex --shell-escape document.tex % ``` tex % \input markdown % a -% b \markdownBegin c -% d +% b \markdownBegin c +% d % e \markdownEnd f -% g +% g % \bye % ``````` % @@ -12696,7 +12702,7 @@ The following ordered list will be preceded by roman numerals: \end{markdown} ``````` -Furthermore, we can also specify the name of the snippet in the current +Furthermore, we can also specify the name of the snippet in the current namespace, which can be different from the name of the snippet in the `jdoe/lists` theme. For example, we can make the snippet `jdoe/lists/romanNumerals` available under the name `roman`. @@ -13532,11 +13538,11 @@ following text: > This is a tight list (the first item, the second item, and the third item). > > This is a loose list: -> +> > - This is the first item. -> +> > - This is the second item. -> +> > - This is the third item. ##### \LaTeX{} Example {.unnumbered} @@ -13560,7 +13566,7 @@ following content: }, ulItemEnd = {}, ulEndTight = {).}, - }, + }, ] This is a tight list @@ -13579,7 +13585,7 @@ This is a tight list ulItem = {\item}, ulItemEnd = {.}, ulEnd = {\end{itemize}}, - }, + }, ] This is a loose list @@ -13602,11 +13608,11 @@ following text: > This is a tight list (the first item, the second item, and the third item). > > This is a loose list: -> +> > - This is the first item. -> +> > - This is the second item. -> +> > - This is the third item. ##### \Hologo{ConTeXt} Example {.unnumbered} @@ -13668,11 +13674,11 @@ following text: > This is a tight list (the first item, the second item, and the third item). > > This is a loose list: -> +> > - This is the first item. -> +> > - This is the second item. -> +> > - This is the third item. %</manual-tokens> @@ -13800,7 +13806,7 @@ following text: The \mdef{markdownRendererTextCite} macro represents a string of one or more text citations. This macro will only be produced, when the -\Opt{citations} option is enabled. The macro receives parameters in the same +\Opt{citations} option is enabled. The macro receives parameters in the same format as the \mref{markdownRendererCite} macro. % \end{markdown} @@ -13926,8 +13932,8 @@ filename of a file contaning the code block contents. The \mdef{markdownRendererInputFencedCode} macro represents a fenced code block. This macro will only be produced, when the \Opt{fencedCode} option is enabled. The macro receives three arguments that correspond to the filename of -a file contaning the code block contents, the fully escaped code fence infostring -that can be directly typeset, and the raw code fence infostring that can be used +a file contaning the code block contents, the fully escaped code fence infostring +that can be directly typeset, and the raw code fence infostring that can be used outside typesetting. % \end{markdown} @@ -14083,7 +14089,7 @@ following text: > > \$\\sqrt{-1}\$ *equals* \$i\$. > -> <math><mroot><msqrt><mo>−</mo><mn>1</mn></msqrt></mroot></math> +> <math><msqrt><mo>−</mo><mn>1</mn></msqrt></math> > *equals* > <math><mi>i</mi></math>. @@ -14124,7 +14130,7 @@ following text: > > \$\\sqrt{-1}\$ *equals* \$i\$. > -> <math><mroot><msqrt><mo>−</mo><mn>1</mn></msqrt></mroot></math> +> <math><msqrt><mo>−</mo><mn>1</mn></msqrt></math> > *equals* > <math><mi>i</mi></math>. @@ -14161,7 +14167,7 @@ following text: > > \$\\sqrt{-1}\$ *equals* \$i\$. > -> <math><mroot><msqrt><mo>−</mo><mn>1</mn></msqrt></mroot></math> +> <math><msqrt><mo>−</mo><mn>1</mn></msqrt></math> > *equals* > <math><mi>i</mi></math>. @@ -15223,7 +15229,7 @@ following text: > Hello *world*! > > *(The end of a block)* -> +> > _Foo_ bar! %</manual-tokens> @@ -16395,7 +16401,7 @@ following text: > - Hello *world*! > > *(The end of a block)* -> +> > _Foo_ bar! ##### \LaTeX{} Example {.unnumbered} @@ -16432,7 +16438,7 @@ following text: > - Hello *world*! > > *(The end of a block)* -> +> > _Foo_ bar! ##### \Hologo{ConTeXt} Example {.unnumbered} @@ -16464,7 +16470,7 @@ following text: > - Hello *world*! > > *(The end of a block)* -> +> > _Foo_ bar! %</manual-tokens> @@ -18080,11 +18086,11 @@ following text: > This is a tight list (the first item, the second item, and the third item). > > This is a loose list: -> +> > 1. This is the first item. -> +> > 2. This is the second item. -> +> > 3. This is the third item. ##### \LaTeX{} Example {.unnumbered} @@ -18113,7 +18119,7 @@ following content: }, olItemEnd = {}, olEndTight = {).}, - }, + }, ] This is a tight list @@ -18143,7 +18149,7 @@ This is a tight list }, olItemEnd = {.}, olEnd = {\end{enumerate}}, - }, + }, ] This is a loose list @@ -18166,11 +18172,11 @@ following text: > This is a tight list (the first item, the second item, and the third item). > > This is a loose list: -> +> > 1. This is the first item. -> +> > 2. This is the second item. -> +> > 3. This is the third item. ##### \Hologo{ConTeXt} Example {.unnumbered} @@ -18253,11 +18259,11 @@ following text: > This is a tight list (the first item, the second item, and the third item). > > This is a loose list: -> +> > 1. This is the first item. -> +> > 2. This is the second item. -> +> > 3. This is the third item. %</manual-tokens> @@ -19323,7 +19329,7 @@ A PDF document named `document.pdf` should be produced and contain the following text: > $E=mc^2\dots$ -> +> > $$\hat{f} \left ( \xi \right )= \int_{-\infty}^{\infty} f\left ( x \right ) e^{-i2\pi \xi x} dx\eqno(1)$$ ##### \LaTeX{} Example {.unnumbered} @@ -19352,7 +19358,7 @@ A PDF document named `document.pdf` should be produced and contain the following text: > $E=mc^2\dots$ -> +> > $$\hat{f} \left ( \xi \right )= \int_{-\infty}^{\infty} f\left ( x \right ) e^{-i2\pi \xi x} dx\quad(1)$$ ##### \Hologo{ConTeXt} Example {.unnumbered} @@ -19380,7 +19386,7 @@ A PDF document named `document.pdf` should be produced and contain the following text: > $E=mc^2\dots$ -> +> > $$\hat{f} \left ( \xi \right )= \int_{-\infty}^{\infty} f\left ( x \right ) e^{-i2\pi \xi x} dx\quad(1)$$ %</manual-tokens> @@ -21053,7 +21059,7 @@ following text: > \$\\sqrt{-1}\$ *equals* \$i\$. > -> <math><mroot><msqrt><mo>−</mo><mn>1</mn></msqrt></mroot></math> +> <math><msqrt><mo>−</mo><mn>1</mn></msqrt></math> > *equals* > <math><mi>i</mi></math>. @@ -21674,7 +21680,7 @@ following text: > \$\\sqrt{-1}\$ *equals* \$i\$. > -> <math><mroot><msqrt><mo>−</mo><mn>1</mn></msqrt></mroot></math> +> <math><msqrt><mo>−</mo><mn>1</mn></msqrt></math> > *equals* > <math><mi>i</mi></math>. @@ -22057,19 +22063,6 @@ end % \par % \begin{markdown} % -% The \luamdef{util.lookup_files} method looks up files with filename `f` -% and returns their paths. Further options for the \pkg{Kpathsea} library -% can be specified in table `options`. [@luatex21, Section 10.7.4] -% -% \end{markdown} -% \begin{macrocode} -function util.lookup_files(f, options) - return kpse.lookup(f, options) -end -% \end{macrocode} -% \par -% \begin{markdown} -% % The \luamdef{util.expand_tabs_in_line} expands tabs in string `s`. If % `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 @@ -24509,7 +24502,7 @@ function entities.char_entity(s) end local char_table = {} for _, code_point in ipairs(code_points) do - table.insert(char_table, unicode.utf8.char(code_point)) + table.insert(char_table, unicode.utf8.char(code_point)) end return table.concat(char_table) end @@ -24689,9 +24682,10 @@ function M.writer.new(options) % % \end{markdown} % \begin{macrocode} + self.interblocksep_text = "\\markdownRendererInterblockSeparator\n{}" function self.interblocksep() if not self.is_writing then return "" end - return "\\markdownRendererInterblockSeparator\n{}" + return self.interblocksep_text end % \end{macrocode} % \par @@ -24704,9 +24698,24 @@ function M.writer.new(options) % % \end{markdown} % \begin{macrocode} + self.paragraphsep_text = "\\markdownRendererParagraphSeparator\n{}" function self.paragraphsep() if not self.is_writing then return "" end - return "\\markdownRendererParagraphSeparator\n{}" + return self.paragraphsep_text + end +% \end{macrocode} +% \par +% \begin{markdown} +% +% Define \luamdef{writer->undosep} as a function that will remove the output +% produced by an immediately preceding block element / paragraph separator. +% +% \end{markdown} +% \begin{macrocode} + self.undosep_text = "\\markdownRendererUndoSeparator\n{}" + function self.undosep() + if not self.is_writing then return "" end + return self.undosep_text end % \end{macrocode} % \par @@ -25378,6 +25387,7 @@ function M.writer.new(options) if attributes["#" .. self.slice_end_identifier] ~= nil and self.slice_end_type == "^" then if self.is_writing then + table.insert(buf, self.undosep()) table.insert(buf, tear_down_attributes()) end self.is_writing = false @@ -25429,6 +25439,7 @@ function M.writer.new(options) if attributes["#" .. self.slice_end_identifier] ~= nil and self.slice_end_type == "$" then if self.is_writing then + table.insert(buf, self.undosep()) table.insert(buf, tear_down_attributes()) end self.is_writing = false @@ -25448,55 +25459,55 @@ function M.writer.new(options) % % \end{markdown} % \begin{macrocode} -local function create_auto_identifier(s) - local buffer = {} - local prev_space = false - local letter_found = false + local function create_auto_identifier(s) + local buffer = {} + local prev_space = false + local letter_found = false - for _, code in utf8.codes(uni_algos.normalize.NFC(s)) do - local char = utf8.char(code) + for _, code in utf8.codes(uni_algos.normalize.NFC(s)) do + local char = utf8.char(code) - -- Remove everything up to the first letter. - if not letter_found then - local is_letter = unicode.utf8.match(char, "%a") - if is_letter then - letter_found = true - else - goto continue + -- Remove everything up to the first letter. + if not letter_found then + local is_letter = unicode.utf8.match(char, "%a") + if is_letter then + letter_found = true + else + goto continue + end end - end - -- Remove all non-alphanumeric characters, except underscores, hyphens, and periods. - if not unicode.utf8.match(char, "[%w_%-%.%s]") then - goto continue - end - - -- Replace all spaces and newlines with hyphens. - if unicode.utf8.match(char, "[%s\n]") then - char = "-" - if prev_space then + -- Remove all non-alphanumeric characters, except underscores, hyphens, and periods. + if not unicode.utf8.match(char, "[%w_%-%.%s]") then goto continue + end + + -- Replace all spaces and newlines with hyphens. + if unicode.utf8.match(char, "[%s\n]") then + char = "-" + if prev_space then + goto continue + else + prev_space = true + end else - prev_space = true + -- Convert all alphabetic characters to lowercase. + char = unicode.utf8.lower(char) + prev_space = false end - else - -- Convert all alphabetic characters to lowercase. - char = unicode.utf8.lower(char) - prev_space = false - end - table.insert(buffer, char) + table.insert(buffer, char) - ::continue:: - end + ::continue:: + end - if prev_space then - table.remove(buffer) - end + if prev_space then + table.remove(buffer) + end - local identifier = #buffer == 0 and "section" or table.concat(buffer, "") - return identifier -end + local identifier = #buffer == 0 and "section" or table.concat(buffer, "") + return identifier + end % \end{macrocode} % \begin{markdown} % @@ -25504,56 +25515,56 @@ end % % \end{markdown} % \begin{macrocode} -local function create_gfm_auto_identifier(s) - local buffer = {} - local prev_space = false - local letter_found = false + local function create_gfm_auto_identifier(s) + local buffer = {} + local prev_space = false + local letter_found = false - for _, code in utf8.codes(uni_algos.normalize.NFC(s)) do - local char = utf8.char(code) + for _, code in utf8.codes(uni_algos.normalize.NFC(s)) do + local char = utf8.char(code) - -- Remove everything up to the first non-space. - if not letter_found then - local is_letter = unicode.utf8.match(char, "%S") - if is_letter then - letter_found = true - else - goto continue + -- Remove everything up to the first non-space. + if not letter_found then + local is_letter = unicode.utf8.match(char, "%S") + if is_letter then + letter_found = true + else + goto continue + end end - end - -- Remove all non-alphanumeric characters, except underscores and hyphens. - if not unicode.utf8.match(char, "[%w_%-%s]") then - prev_space = false - goto continue - end - - -- Replace all spaces and newlines with hyphens. - if unicode.utf8.match(char, "[%s\n]") then - char = "-" - if prev_space then + -- Remove all non-alphanumeric characters, except underscores and hyphens. + if not unicode.utf8.match(char, "[%w_%-%s]") then + prev_space = false goto continue + end + + -- Replace all spaces and newlines with hyphens. + if unicode.utf8.match(char, "[%s\n]") then + char = "-" + if prev_space then + goto continue + else + prev_space = true + end else - prev_space = true + -- Convert all alphabetic characters to lowercase. + char = unicode.utf8.lower(char) + prev_space = false end - else - -- Convert all alphabetic characters to lowercase. - char = unicode.utf8.lower(char) - prev_space = false - end - table.insert(buffer, char) + table.insert(buffer, char) - ::continue:: - end + ::continue:: + end - if prev_space then - table.remove(buffer) - end + if prev_space then + table.remove(buffer) + end - local identifier = #buffer == 0 and "section" or table.concat(buffer, "") - return identifier -end + local identifier = #buffer == 0 and "section" or table.concat(buffer, "") + return identifier + end % \end{macrocode} % \par % \begin{markdown} @@ -25564,6 +25575,8 @@ end % % \end{markdown} % \begin{macrocode} + self.secbegin_text = "\\markdownRendererSectionBegin\n" + self.secend_text = "\n\\markdownRendererSectionEnd " function self.heading(s, level, attributes) local buf = {} local flat_text, inlines = table.unpack(s) @@ -25573,8 +25586,8 @@ end table.insert(buf, self.push_attributes("heading", nil, - "\\markdownRendererSectionBegin\n", - "\n\\markdownRendererSectionEnd ")) + self.secbegin_text, + self.secend_text)) end -- pop attributes for sections that have ended @@ -25595,8 +25608,8 @@ end -- push attributes for the new section local start_output = {} local end_output = {} - table.insert(start_output, "\\markdownRendererSectionBegin\n") - table.insert(end_output, "\n\\markdownRendererSectionEnd ") + table.insert(start_output, self.secbegin_text) + table.insert(end_output, self.secend_text) table.insert(buf, self.push_attributes("heading", normalized_attributes, @@ -25752,7 +25765,6 @@ parsers.letter = R("AZ","az") parsers.alphanumeric = R("AZ","az","09") parsers.keyword = parsers.letter * (parsers.alphanumeric + parsers.dash)^0 -parsers.internal_punctuation = S(":;,.?") parsers.doubleasterisks = P("**") parsers.doubleunderscores = P("__") @@ -25763,7 +25775,50 @@ parsers.any = P(1) parsers.succeed = P(true) parsers.fail = P(false) -parsers.escapable = S("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~") +parsers.internal_punctuation = S(":;,.?") +parsers.ascii_punctuation = S("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~") +% \end{macrocode} +% \par +% \begin{markdown} +% +%### Unicode punctuation +% This section documents [the Unicode punctuation][unicode-punctuation] +% recognized by the markdown reader. The punctuation is organized in the +% \luamdef{parsers.punctuation} table according to the number of bytes occupied +% after conversion to \acro{utf}8. +% +% [unicode-punctuation]: https://spec.commonmark.org/0.31.2/#unicode-punctuation-character +% (CommonMark Spec, Version 0.31.2 (2024-01-28)) +% +% \end{markdown} +% \begin{macrocode} +parsers.punctuation = {} +(function() + local pathname = kpse.lookup("UnicodeData.txt") + local file = assert(io.open(pathname, "r"), + [[Could not open file "UnicodeData.txt"]]) + for line in file:lines() do + local codepoint, major_category = line:match("^(%x+);[^;]*;(%a)") + if major_category == "P" or major_category == "S" then + local code = unicode.utf8.char(tonumber(codepoint, 16)) + if parsers.punctuation[#code] == nil then + parsers.punctuation[#code] = parsers.fail + end + local code_parser = parsers.succeed + for i = 1, #code do + local byte = code:sub(i, i) + local byte_parser = S(byte) + code_parser = code_parser + * byte_parser + end + parsers.punctuation[#code] = parsers.punctuation[#code] + + code_parser + end + end + assert(file:close()) +end)() + +parsers.escapable = parsers.ascii_punctuation parsers.anyescaped = parsers.backslash / "" * parsers.escapable + parsers.any @@ -25812,8 +25867,8 @@ parsers.leader = parsers.space^-3 % \end{markdown} % \begin{macrocode} local function has_trail(indent_table) - return indent_table ~= nil and - indent_table.trail ~= nil and + return indent_table ~= nil and + indent_table.trail ~= nil and next(indent_table.trail) ~= nil end @@ -25825,8 +25880,8 @@ end % \end{markdown} % \begin{macrocode} local function has_indents(indent_table) - return indent_table ~= nil and - indent_table.indents ~= nil and + return indent_table ~= nil and + indent_table.indents ~= nil and next(indent_table.indents) ~= nil end @@ -25902,8 +25957,8 @@ end % % Process the spacing of a string of spaces and tabs `spacing` with preceding indent width from % the start of the line `indent` and strip up to `left_strip_length` spaces. Return the remainder -% `remainder` and whether there is enough spaces to produce a code `is_code`. Return how many -% spaces were stripped, as well as if the minimum was met `is_minimum` and what remainder it +% `remainder` and whether there is enough spaces to produce a code `is_code`. Return how many +% spaces were stripped, as well as if the minimum was met `is_minimum` and what remainder it % left `minimum_remainder`. % % \end{markdown} @@ -25947,7 +26002,7 @@ local function process_starter_spacing(indent, spacing, minimum, left_strip_leng minimum_found = true minimum_remainder = minimum_remainder .. string.rep(" ", count - minimum) end - + if (code_started) then code_start = code_start .. character elseif (count >= minimum + 4) then @@ -26119,13 +26174,13 @@ end % % Apply the patterns decoded from the indents of the indent table `indent_table` % iteratively starting at position `index` of the string `s`. If the `is_optional` -% mode is selected, match as many patterns as possible, else match all or fail. -% With the option `is_blank`, the parsing behaves as optional after the position +% mode is selected, match as many patterns as possible, else match all or fail. +% With the option `is_blank`, the parsing behaves as optional after the position % of a blank-only indent has been surpassed. % % \end{markdown} % \begin{macrocode} -local function traverse_indent(s, i, indent_table, is_optional, is_blank) +local function traverse_indent(s, i, indent_table, is_optional, is_blank, current_line_indents) local new_index = i local preceding_indentation = 0 @@ -26133,6 +26188,10 @@ local function traverse_indent(s, i, indent_table, is_optional, is_blank) local blank_starter = left_blank_starter(indent_table) + if current_line_indents == nil then + current_line_indents = {} + end + for index = 1,#indent_table.indents do local value = indent_table.indents[index] local pattern = decode_pattern(value.name) @@ -26142,10 +26201,10 @@ local function traverse_indent(s, i, indent_table, is_optional, is_blank) if new_indent_info == nil then local blankline_end = lpeg.match(Ct(parsers.blankline * Cg(Cp(), "pos")), s, new_index) if is_optional or not indent_table.ignore_blockquote_blank or not blankline_end then - return is_optional, new_index, current_trail + return is_optional, new_index, current_trail, current_line_indents end - return traverse_indent(s, tonumber(blankline_end.pos), indent_table, is_optional, is_blank) + return traverse_indent(s, tonumber(blankline_end.pos), indent_table, is_optional, is_blank, current_line_indents) end local raw_last_trail = new_indent_info[1] @@ -26158,13 +26217,13 @@ local function traverse_indent(s, i, indent_table, is_optional, is_blank) -- check previous trail if not space_only and next(current_trail) == nil then local sp = process_starter_spacing(0, raw_last_trail, 0, 0) - current_trail = {is_code=sp.is_code, remainder=sp.remainder, total_length=sp.total_length, + current_trail = {is_code=sp.is_code, remainder=sp.remainder, total_length=sp.total_length, full_remainder=sp.full_remainder} end if next(current_trail) ~= nil then if not space_only and current_trail.is_code then - return is_optional, new_index, current_trail + return is_optional, new_index, current_trail, current_line_indents end if current_trail.internal_remainder ~= nil then raw_last_trail = current_trail.internal_remainder @@ -26196,7 +26255,7 @@ local function traverse_indent(s, i, indent_table, is_optional, is_blank) local sp = process_starter_spacing(total_indent_level, spacing_to_process, minimum, left_strip_length) if space_only and not sp.is_minimum then - return is_optional or (is_blank and blank_starter <= index), new_index, current_trail + return is_optional or (is_blank and blank_starter <= index), new_index, current_trail, current_line_indents end local indent_length = raw_last_trail_length + delimiter_length + sp.left_total_stripped @@ -26210,10 +26269,12 @@ local function traverse_indent(s, i, indent_table, is_optional, is_blank) current_trail = {is_code=sp.is_code, remainder=sp.remainder, internal_remainder=sp.minimum_remainder, total_length=sp.total_length, full_remainder=sp.full_remainder} + + current_line_indents[#current_line_indents + 1] = new_indent_info new_index = next_index end - return true, new_index, current_trail + return true, new_index, current_trail, current_line_indents end % \end{macrocode} @@ -26230,8 +26291,8 @@ end % \end{macrocode} % \begin{markdown} % -% Check if the current trail of the `indent_table` would produce code if it is expected `expect_code` -% or it would not if it is not. If there is no trail, process and check the current spacing `spacing`. +% Check if the current trail of the `indent_table` would produce code if it is expected `expect_code` +% or it would not if it is not. If there is no trail, process and check the current spacing `spacing`. % % \end{markdown} % \begin{macrocode} @@ -26242,7 +26303,7 @@ local function check_trail_joined(s, i, indent_table, spacing, expect_code, omit if has_trail(indent_table) then local trail = indent_table.trail is_code = trail.is_code - if is_code then + if is_code then remainder = trail.remainder else remainder = trail.full_remainder @@ -26250,7 +26311,7 @@ local function check_trail_joined(s, i, indent_table, spacing, expect_code, omit else local sp = process_starter_spacing(0, spacing, 0, 0) is_code = sp.is_code - if is_code then + if is_code then remainder = sp.remainder else remainder = sp.full_remainder @@ -26291,7 +26352,7 @@ end % \end{macrocode} % \begin{markdown} % -% Check the indentation of the continuation line, optionally with +% Check the indentation of the continuation line, optionally with % the mode `is_optional` selected. Check blank line exclusively with `is_blank`. % % \end{markdown} @@ -26301,9 +26362,11 @@ local function check_continuation_indentation(s, i, indent_table, is_optional, i return true end - local passes, new_index, current_trail = traverse_indent(s, i, indent_table, is_optional, is_blank) + local passes, new_index, current_trail, current_line_indents = + traverse_indent(s, i, indent_table, is_optional, is_blank) if passes then + indent_table.current_line_indents = current_line_indents indent_table = add_trail(indent_table, current_trail) return new_index, indent_table end @@ -26341,7 +26404,7 @@ end % \end{macrocode} % \begin{markdown} % -% Take the trail `trail` or create a new one from `spacing` and comapre it +% Take the trail `trail` or create a new one from `spacing` and compare it % with the expected `trail_type`. On success return the index `i` and the % remainder of the trail. % @@ -26394,25 +26457,25 @@ end % \end{macrocode} % \begin{markdown} % -% Check the indentation of the continuation line, optionally with -% the mode `is_optional` selected. Check blank line specifically with `is_blank`. +% Check the indentation of the continuation line, optionally with +% the mode `is_optional` selected. Check blank line specifically with `is_blank`. % Additionally, also directly check the new trail with a type `trail_type`. % % \end{markdown} % \begin{macrocode} local function check_continuation_indentation_and_trail(s, i, indent_table, is_optional, is_blank, trail_type, - reset_rem, omit_remainder) + reset_rem, omit_remainder) if not has_indents(indent_table) then local spacing, new_index = lpeg.match(C(parsers.spacechar^0) * Cp(), s, i) local result, remainder = check_trail_type(s, i, indent_table.trail, spacing, trail_type) if remainder == nil then - if result then + if result then return new_index end return false end - if result then - return new_index, remainder + if result then + return new_index, remainder end return false end @@ -26431,17 +26494,17 @@ local function check_continuation_indentation_and_trail(s, i, indent_table, is_o end local result, remainder = check_trail_type(s, new_index, current_trail, spacing, trail_type) if remainder == nil or omit_remainder then - if result then - return new_index + if result then + return new_index end return false end - + if is_blank and reset_rem then remainder = remove_remainder_if_blank(indent_table, remainder) end - if result then - return new_index, remainder + if result then + return new_index, remainder end return false end @@ -26516,11 +26579,11 @@ parsers.check_minimal_blank_indent_and_full_code_trail = Cmt( Cb("indent_info") check_continuation_indentation_and_trail) parsers.check_minimal_indent_and_any_trail = Cmt( Cb("indent_info") - * Cc(false) * Cc(false) * Cc("full-any") * Cc(true) * Cc(false), + * Cc(false) * Cc(false) * Cc("full-any") * Cc(true) * Cc(false), check_continuation_indentation_and_trail) parsers.check_minimal_blank_indent_and_any_trail = Cmt( Cb("indent_info") - * Cc(false) * Cc(true) * Cc("full-any") * Cc(true) * Cc(false), + * Cc(false) * Cc(true) * Cc("full-any") * Cc(true) * Cc(false), check_continuation_indentation_and_trail) parsers.check_minimal_blank_indent_and_any_trail_no_rem = Cmt( Cb("indent_info") @@ -26528,11 +26591,11 @@ parsers.check_minimal_blank_indent_and_any_trail_no_rem = Cmt( Cb("indent_info") check_continuation_indentation_and_trail) parsers.check_optional_indent_and_any_trail = Cmt( Cb("indent_info") - * Cc(true) * Cc(false) * Cc("full-any") * Cc(true) * Cc(false), + * Cc(true) * Cc(false) * Cc("full-any") * Cc(true) * Cc(false), check_continuation_indentation_and_trail) parsers.check_optional_blank_indent_and_any_trail = Cmt( Cb("indent_info") - * Cc(true) * Cc(true) * Cc("full-any") * Cc(true) * Cc(false), + * Cc(true) * Cc(true) * Cc("full-any") * Cc(true) * Cc(false), check_continuation_indentation_and_trail) % \end{macrocode} @@ -26865,10 +26928,10 @@ parsers.block_keyword = -- end conditions parsers.html_blankline_end_condition = parsers.linechar^0 - * ( parsers.newline - * (parsers.check_minimal_blank_indent_and_any_trail + * ( parsers.newline + * (parsers.check_minimal_blank_indent_and_any_trail * #parsers.blankline - + parsers.check_minimal_indent_and_any_trail) + + parsers.check_minimal_indent_and_any_trail) * parsers.linechar^1)^0 * (parsers.newline^-1 / "") @@ -26877,10 +26940,10 @@ local function remove_trailing_blank_lines(s) end parsers.html_until_end = function(end_marker) - return Cs(Cs((parsers.newline - * (parsers.check_minimal_blank_indent_and_any_trail + return Cs(Cs((parsers.newline + * (parsers.check_minimal_blank_indent_and_any_trail * #parsers.blankline - + parsers.check_minimal_indent_and_any_trail) + + parsers.check_minimal_indent_and_any_trail) + parsers.linechar - end_marker)^0 * parsers.linechar^0 * parsers.newline^-1) / remove_trailing_blank_lines) @@ -26896,27 +26959,27 @@ parsers.html_attribute_name = (parsers.letter + parsers.colon + parsers.undersco * (parsers.alphanumeric + parsers.colon + parsers.underscore + parsers.period + parsers.dash)^0 -parsers.html_attribute_value = parsers.squote - * (parsers.linechar - parsers.squote)^0 +parsers.html_attribute_value = parsers.squote + * (parsers.linechar - parsers.squote)^0 * parsers.squote - + parsers.dquote - * (parsers.linechar - parsers.dquote)^0 + + parsers.dquote + * (parsers.linechar - parsers.dquote)^0 * parsers.dquote + ( parsers.any - parsers.spacechar - parsers.newline - parsers.dquote - parsers.squote - parsers.backtick - parsers.equal - parsers.less - parsers.more)^1 -parsers.html_inline_attribute_value = parsers.squote +parsers.html_inline_attribute_value = parsers.squote * (V("NoSoftLineBreakEndline") + parsers.any - parsers.blankline^2 - - parsers.squote)^0 + - parsers.squote)^0 * parsers.squote - + parsers.dquote + + parsers.dquote * (V("NoSoftLineBreakEndline") + parsers.any - parsers.blankline^2 - - parsers.dquote)^0 + - parsers.dquote)^0 * parsers.dquote + (parsers.any - parsers.spacechar - parsers.newline - parsers.dquote - parsers.squote - parsers.backtick @@ -26944,7 +27007,7 @@ parsers.html_non_newline_attribute = parsers.spacechar^1 * parsers.html_attribute_value_specification^-1 parsers.nested_breaking_blank = parsers.newline - * parsers.check_minimal_blank_indent + * parsers.check_minimal_blank_indent * parsers.blankline parsers.html_comment_start = P("<!--") @@ -26953,10 +27016,10 @@ parsers.html_comment_end = P("-->") parsers.html_comment = Cs( parsers.html_comment_start * parsers.html_until_end(parsers.html_comment_end)) - + parsers.html_inline_comment = (parsers.html_comment_start / "") * -P(">") * -P("->") - * Cs((V("NoSoftLineBreakEndline") + parsers.any - P("--") + * Cs((V("NoSoftLineBreakEndline") + parsers.any - parsers.nested_breaking_blank - parsers.html_comment_end)^0) * (parsers.html_comment_end / "") @@ -26980,7 +27043,7 @@ parsers.html_declaration = Cs( parsers.html_declaration_start * parsers.html_until_end(parsers.html_declaration_end)) parsers.html_inline_declaration = parsers.html_declaration_start - * Cs(V("NoSoftLineBreakEndline") + parsers.any + * Cs(V("NoSoftLineBreakEndline") + parsers.any - parsers.nested_breaking_blank - parsers.html_declaration_end)^0 * parsers.html_declaration_end @@ -26992,7 +27055,7 @@ parsers.html_instruction = Cs( parsers.html_instruction_start * parsers.html_until_end(parsers.html_instruction_end)) parsers.html_inline_instruction = parsers.html_instruction_start - * Cs(V("NoSoftLineBreakEndline") + parsers.any + * Cs(V("NoSoftLineBreakEndline") + parsers.any - parsers.nested_breaking_blank - parsers.html_instruction_end)^0 * parsers.html_instruction_end @@ -27135,7 +27198,7 @@ parsers.html_any_block = parsers.html_any_block_opening parsers.html_inline_comment_full = parsers.html_comment_start * -P(">") * -P("->") - * Cs((V("NoSoftLineBreakEndline") + parsers.any - P("--") + * Cs((V("NoSoftLineBreakEndline") + parsers.any - P("--") - parsers.nested_breaking_blank - parsers.html_comment_end)^0) * parsers.html_comment_end @@ -27367,7 +27430,7 @@ parsers.heading_start = #parsers.hash * C(parsers.hash^-6) * -parsers.hash / length -- parse setext header ending and return level -parsers.heading_level = parsers.nonindentspace * parsers.equal^1 * parsers.optionalspace * #parsers.newline * Cc(1) +parsers.heading_level = parsers.nonindentspace * parsers.equal^1 * parsers.optionalspace * #parsers.newline * Cc(1) + parsers.nonindentspace * parsers.dash^1 * parsers.optionalspace * #parsers.newline * Cc(2) local function strip_atx_end(s) @@ -27377,10 +27440,10 @@ end parsers.atx_heading = parsers.check_trail_no_rem * Cg(parsers.heading_start, "level") * (C( parsers.optionalspace - * parsers.hash^0 + * parsers.hash^0 * parsers.optionalspace * parsers.newline) - + parsers.spacechar^1 + + parsers.spacechar^1 * C(parsers.line)) % \end{macrocode} % \par @@ -27627,7 +27690,7 @@ function M.reader.new(writer, options) parsers.minimally_indented_par_or_plain = parsers.minimally_indented_paragraph + parsers.minimally_indented_plain - parsers.minimally_indented_par_or_plain_no_blank = parsers.minimally_indented_par_or_plain + parsers.minimally_indented_par_or_plain_no_blank = parsers.minimally_indented_par_or_plain - parsers.minimally_indented_blankline parsers.minimally_indented_ref = parsers.check_minimal_indent * V("Reference") @@ -27653,8 +27716,8 @@ function M.reader.new(writer, options) % \begin{macrocode} parsers.separator_loop = function(separated_block, paragraph, block_separator, paragraph_separator) - return separated_block - + block_separator + return separated_block + + block_separator * paragraph * separated_block + paragraph_separator @@ -27681,20 +27744,20 @@ function M.reader.new(writer, options) end parsers.sep_group_no_output = function(blank) - return blank^0 * parsers.eof + return blank^0 * parsers.eof + blank^0 end parsers.content_blank = parsers.minimally_indented_blankline - parsers.ref_or_block_separated = parsers.sep_group_no_output(parsers.content_blank) - * ( parsers.minimally_indented_ref + parsers.ref_or_block_separated = parsers.sep_group_no_output(parsers.content_blank) + * ( parsers.minimally_indented_ref - parsers.content_blank) + parsers.block_sep_group(parsers.content_blank) - * ( parsers.minimally_indented_block + * ( parsers.minimally_indented_block - parsers.content_blank) - parsers.loop_body_pair = + parsers.loop_body_pair = parsers.create_loop_body_pair(parsers.ref_or_block_separated, parsers.minimally_indented_par_or_plain_no_blank, parsers.block_sep_group(parsers.content_blank), @@ -27712,7 +27775,7 @@ function M.reader.new(writer, options) parsers.indented_content = function() return Ct( (V("Reference") + (parsers.blankline / "")) * parsers.content_blank^0 - * parsers.check_minimal_indent + * parsers.check_minimal_indent * parsers.content_loop + (V("Reference") + (parsers.blankline / "")) * parsers.content_blank^0 @@ -27758,7 +27821,7 @@ function M.reader.new(writer, options) * allowed_end end - parsers.starter = parsers.bullet(parsers.dash) + parsers.starter = parsers.bullet(parsers.dash) + parsers.bullet(parsers.asterisk) + parsers.bullet(parsers.plus) + parsers.enumerator(parsers.period) @@ -27842,7 +27905,7 @@ function M.reader.new(writer, options) % \end{macrocode} % \begin{markdown} % -% Render content between the `opening_index` and `closing_index` in the delimiter table `t` +% Render content between the `opening_index` and `closing_index` in the delimiter table `t` % as emphasis. % % \end{markdown} @@ -27856,7 +27919,7 @@ function M.reader.new(writer, options) % \end{macrocode} % \begin{markdown} % -% Render content between the `opening_index` and `closing_index` in the delimiter table `t` +% Render content between the `opening_index` and `closing_index` in the delimiter table `t` % as strong emphasis. % % \end{markdown} @@ -27876,8 +27939,8 @@ function M.reader.new(writer, options) % \end{markdown} % \begin{macrocode} local function breaks_three_rule(opening_delimiter, closing_delimiter) - return (opening_delimiter.is_closing or closing_delimiter.is_opening) and - ((opening_delimiter.original_count + closing_delimiter.original_count) % 3 == 0) and + return (opening_delimiter.is_closing or closing_delimiter.is_opening) and + ((opening_delimiter.original_count + closing_delimiter.original_count) % 3 == 0) and (opening_delimiter.original_count % 3 ~= 0 or closing_delimiter.original_count % 3 ~= 0) end @@ -27940,11 +28003,11 @@ function M.reader.new(writer, options) while current_position <= max_position do local value = t[current_position] - - if value.type ~= "delimiter" or - value.element ~= "emphasis" or - not value.is_active or - not value.is_closing or + + if value.type ~= "delimiter" or + value.element ~= "emphasis" or + not value.is_active or + not value.is_closing or (value.current_count <= 0) then current_position = current_position + 1 goto continue @@ -27978,7 +28041,7 @@ function M.reader.new(writer, options) t[current_position].current_count = current_closing_count - 1 fill_emph(t, opener_position, current_position) end - + ::continue:: end end @@ -28006,7 +28069,6 @@ function M.reader.new(writer, options) return lpeg.R("\240\244") * cont * cont * cont end end - % \end{macrocode} % \begin{markdown} % @@ -28024,15 +28086,22 @@ function M.reader.new(writer, options) else char_length = pos + 1 end - c = lpeg.match({ C(utf8_by_byte_count(char_length)) },s,i+pos) - if (c ~= nil) and (unicode.utf8.match(c, chartype)) then - return i + + if (chartype == "punctuation") then + if lpeg.match(parsers.punctuation[char_length], s, i+pos) then + return i + end + else + c = lpeg.match({ C(utf8_by_byte_count(char_length)) },s,i+pos) + if (c ~= nil) and (unicode.utf8.match(c, chartype)) then + return i + end end end end local function check_preceding_unicode_punctuation(s, i) - return check_unicode_type(s, i, -4, -1, "%p") + return check_unicode_type(s, i, -4, -1, "punctuation") end local function check_preceding_unicode_whitespace(s, i) @@ -28040,19 +28109,19 @@ function M.reader.new(writer, options) end local function check_following_unicode_punctuation(s, i) - return check_unicode_type(s, i, 0, 3, "%p") + return check_unicode_type(s, i, 0, 3, "punctuation") end local function check_following_unicode_whitespace(s, i) return check_unicode_type(s, i, 0, 3, "%s") end - parsers.unicode_preceding_punctuation = B(parsers.escapable) + parsers.unicode_preceding_punctuation = B(parsers.escapable) + Cmt(parsers.succeed, check_preceding_unicode_punctuation) parsers.unicode_preceding_whitespace = Cmt(parsers.succeed, check_preceding_unicode_whitespace) - parsers.unicode_following_punctuation = #parsers.escapable + parsers.unicode_following_punctuation = #parsers.escapable + Cmt(parsers.succeed, check_following_unicode_punctuation) parsers.unicode_following_whitespace = Cmt(parsers.succeed, check_following_unicode_whitespace) @@ -28064,8 +28133,8 @@ function M.reader.new(writer, options) end parsers.left_flanking_delimiter_run = function(character) - return (B( parsers.any) - * (parsers.unicode_preceding_punctuation + parsers.unicode_preceding_whitespace) + return (B( parsers.any) + * (parsers.unicode_preceding_punctuation + parsers.unicode_preceding_whitespace) + -B(parsers.any)) * parsers.delimiter_run(character) * parsers.unicode_following_punctuation @@ -28079,7 +28148,7 @@ function M.reader.new(writer, options) * parsers.delimiter_run(character) * (parsers.unicode_following_punctuation + parsers.unicode_following_whitespace + parsers.eof) - + (B(parsers.any) + + (B(parsers.any) * -(parsers.unicode_preceding_punctuation + parsers.unicode_preceding_whitespace)) * parsers.delimiter_run(character) end @@ -28087,13 +28156,13 @@ function M.reader.new(writer, options) if options.underscores then parsers.emph_start = parsers.left_flanking_delimiter_run(parsers.asterisk) + (-#parsers.right_flanking_delimiter_run(parsers.underscore) - + (parsers.unicode_preceding_punctuation + + (parsers.unicode_preceding_punctuation * #parsers.right_flanking_delimiter_run(parsers.underscore))) * parsers.left_flanking_delimiter_run(parsers.underscore) parsers.emph_end = parsers.right_flanking_delimiter_run(parsers.asterisk) + (-#parsers.left_flanking_delimiter_run(parsers.underscore) - + #(parsers.left_flanking_delimiter_run(parsers.underscore) + + #(parsers.left_flanking_delimiter_run(parsers.underscore) * parsers.unicode_following_punctuation)) * parsers.right_flanking_delimiter_run(parsers.underscore) else @@ -28115,7 +28184,7 @@ function M.reader.new(writer, options) * Cg(Cc(true), "is_opening") * Cg(Cc(false), "is_closing")) - parsers.emph_capturing_close = Ct( Cg(Cc("delimiter"), "type") + parsers.emph_capturing_close = Ct( Cg(Cc("delimiter"), "type") * Cg(Cc("emphasis"), "element") * Cg(C(parsers.emph_end), "content") * Cg(Cc(false), "is_opening") @@ -28281,20 +28350,20 @@ function M.reader.new(writer, options) end parsers.link_and_emph_endline = parsers.newline - * ((parsers.check_minimal_indent - * -V("EndlineExceptions") - + parsers.check_optional_indent - * -V("EndlineExceptions") + * ((parsers.check_minimal_indent + * -V("EndlineExceptions") + + parsers.check_optional_indent + * -V("EndlineExceptions") * -parsers.starter) / "") * parsers.spacechar^0 / "\n" parsers.link_and_emph_content = Ct( Cg(Cc("content"), "type") - * Cg(Cs(( parsers.link_emph_precedence + * Cg(Cs(( parsers.link_emph_precedence + parsers.backslash * parsers.any + parsers.link_and_emph_endline + (parsers.linechar - parsers.blankline^2 - - parsers.link_image_open_or_close + - parsers.link_image_open_or_close - parsers.emph_open_or_close))^0), "content")) parsers.link_and_emph_table = (parsers.link_image_opening + parsers.emph_open) @@ -28330,7 +28399,7 @@ function M.reader.new(writer, options) local value = t[i] if value.type == "delimiter" and value.is_opening and - (value.element == "link" or value.element == "image") + (value.element == "link" or value.element == "image") and not value.removed then if value.is_active then return i @@ -28455,8 +28524,8 @@ function M.reader.new(writer, options) % \end{macrocode} % \begin{markdown} % -% Parse content between two delimiters in the delimiter table `t`. Produce the respective link and image -% macros. +% Parse content between two delimiters in the delimiter table `t`. Produce the respective link and image +% macros. % % \end{markdown} % \begin{macrocode} @@ -28538,7 +28607,7 @@ function M.reader.new(writer, options) % \end{macrocode} % \begin{markdown} % -% Resolve an inline link [a](b "c") from the delimiters at `opening_index` and `closing_index` +% Resolve an inline link [a](b "c") from the delimiters at `opening_index` and `closing_index` % within a delimiter table `t`. Here, compared to other types of links, no reference definition is needed. % % \end{markdown} @@ -28571,7 +28640,7 @@ function M.reader.new(writer, options) % \begin{markdown} % % Resolve a full link [a][b] from the delimiters at `opening_index` and `closing_index` within a delimiter table `t`. -% Continue if a tag `b` is not found in the references. +% Continue if a tag `b` is not found in the references. % % \end{markdown} % \begin{macrocode} @@ -28581,7 +28650,7 @@ function M.reader.new(writer, options) local r = self.lookup_reference(next_link_content) if r then - local inline_content = resolve_inline_following_content(t, next_link_closing_index, false, + local inline_content = resolve_inline_following_content(t, next_link_closing_index, false, t.match_link_attributes) r.attributes = join_attributes(r.attributes, inline_content.attributes) render_link_or_image(t, opening_index, next_link_closing_index, closing_index, r) @@ -28591,9 +28660,9 @@ function M.reader.new(writer, options) % \end{macrocode} % \begin{markdown} % -% Resolve a collapsed link [a][] from the delimiters at `opening_index` and `closing_index` +% Resolve a collapsed link [a][] from the delimiters at `opening_index` and `closing_index` % within a delimiter table `t`. -% Continue if a tag `a` is not found in the references. +% Continue if a tag `a` is not found in the references. % % \end{markdown} % \begin{macrocode} @@ -28613,7 +28682,7 @@ function M.reader.new(writer, options) % \begin{markdown} % % Parse a table of link and emphasis delimiters `t`. -% First, iterate over the link delimiters and produce either link or image macros. +% First, iterate over the link delimiters and produce either link or image macros. % Then run `process_emphasis` over the entire delimiter table, resolving emphasis and strong % emphasis and parsing any content outside of closed delimiters. % @@ -28625,8 +28694,8 @@ function M.reader.new(writer, options) end for i,value in ipairs(t) do - if not value.is_closing or - value.type ~= "delimiter" or + if not value.is_closing or + value.type ~= "delimiter" or not (value.element == "link" or value.element == "image") then goto continue end @@ -28640,7 +28709,7 @@ function M.reader.new(writer, options) opening_delimiter.removed = true local link_type = opening_delimiter.link_type - + if (link_type == "inline") then resolve_inline_link(t, opener_position, i) end @@ -28657,7 +28726,7 @@ function M.reader.new(writer, options) ::continue:: end - t[#t].content = t[#t].content:gsub("%s*$","") + t[#t].content = t[#t].content:gsub("%s*$","") process_emphasis(t, 1, #t) local final_result = collect_emphasis_content(t, 1, #t) @@ -28706,7 +28775,7 @@ function M.reader.new(writer, options) parsers.interrupting_bullets = parsers.fail parsers.interrupting_enumerators = parsers.fail else - parsers.interrupting_bullets = parsers.bullet(parsers.dash, true) + parsers.interrupting_bullets = parsers.bullet(parsers.dash, true) + parsers.bullet(parsers.asterisk, true) + parsers.bullet(parsers.plus, true) @@ -28756,17 +28825,17 @@ function M.reader.new(writer, options) parsers.NoSoftLineBreakEndline = parsers.newline - * (parsers.check_minimal_indent - * -V("NoSoftLineBreakEndlineExceptions") - + parsers.check_optional_indent - * -V("NoSoftLineBreakEndlineExceptions") + * (parsers.check_minimal_indent + * -V("NoSoftLineBreakEndlineExceptions") + + parsers.check_optional_indent + * -V("NoSoftLineBreakEndlineExceptions") * -parsers.starter) * parsers.spacechar^0 / writer.space parsers.EndlineBreak = parsers.backslash * parsers.Endline / writer.hard_line_break - + parsers.OptionalIndent = parsers.spacechar^1 / writer.space @@ -28790,7 +28859,7 @@ function M.reader.new(writer, options) / writer.soft_line_break parsers.NonbreakingSpace - = parsers.spacechar^2 * parsers.Endline + = parsers.spacechar^2 * parsers.Endline / writer.hard_line_break + parsers.spacechar^1 * parsers.Endline^-1 * parsers.eof / "" + parsers.spacechar^1 * parsers.Endline @@ -28864,23 +28933,23 @@ end % % \end{markdown} % \begin{macrocode} - parsers.DisplayHtml = Cs(parsers.check_trail - * ( parsers.html_comment - + parsers.html_special_block - + parsers.html_block - + parsers.html_any_block - + parsers.html_instruction - + parsers.html_cdatasection + parsers.DisplayHtml = Cs(parsers.check_trail + * ( parsers.html_comment + + parsers.html_special_block + + parsers.html_block + + parsers.html_any_block + + parsers.html_instruction + + parsers.html_cdatasection + parsers.html_declaration)) / writer.block_html_element parsers.indented_non_blank_line = parsers.indentedline - parsers.blankline - parsers.Verbatim = Cs( - parsers.check_code_trail + parsers.Verbatim = Cs( + parsers.check_code_trail * (parsers.line - parsers.blankline) * ((parsers.check_minimal_blank_indent_and_full_code_trail * parsers.blankline)^0 - * ((parsers.check_minimal_indent / "") * parsers.check_code_trail + * ((parsers.check_minimal_indent / "") * parsers.check_code_trail * (parsers.line - parsers.blankline))^1)^0 ) / self.expandtabs / writer.verbatim @@ -28921,23 +28990,23 @@ end parsers.list_blank = parsers.conditionally_indented_blankline - parsers.ref_or_block_list_separated = parsers.sep_group_no_output(parsers.list_blank) + parsers.ref_or_block_list_separated = parsers.sep_group_no_output(parsers.list_blank) * parsers.minimally_indented_ref + parsers.block_sep_group(parsers.list_blank) * parsers.minimally_indented_block parsers.ref_or_block_non_separated = parsers.minimally_indented_ref - + (parsers.succeed / writer.interblocksep) + + (parsers.succeed / writer.interblocksep) * parsers.minimally_indented_block - parsers.minimally_indented_blankline - parsers.tight_list_loop_body_pair = + parsers.tight_list_loop_body_pair = parsers.create_loop_body_pair(parsers.ref_or_block_non_separated, parsers.minimally_indented_par_or_plain_no_blank, (parsers.succeed / writer.interblocksep), (parsers.succeed / writer.paragraphsep)) - parsers.loose_list_loop_body_pair = + parsers.loose_list_loop_body_pair = parsers.create_loop_body_pair(parsers.ref_or_block_list_separated, parsers.minimally_indented_par_or_plain, parsers.block_sep_group(parsers.list_blank), @@ -28969,7 +29038,7 @@ end * #parsers.list_blank * remove_indent("li") + ( (V("Reference") + (parsers.blankline / "")) - * parsers.check_minimal_indent + * parsers.check_minimal_indent * parsers.tight_list_content_loop + (V("Reference") + (parsers.blankline / "")) + (parsers.tickbox^-1 / writer.escape) @@ -28977,11 +29046,11 @@ end ) * parsers.list_item_tightness_condition ) - + parsers.indented_content_loose = Ct( (parsers.blankline / "") * #parsers.list_blank + ( (V("Reference") + (parsers.blankline / "")) - * parsers.check_minimal_indent + * parsers.check_minimal_indent * parsers.loose_list_content_loop + (V("Reference") + (parsers.blankline / "")) + (parsers.tickbox^-1 / writer.escape) @@ -29004,7 +29073,7 @@ end parsers.BulletListOfType = function(bullet_type) local bullet = parsers.bullet(bullet_type) - return ( Ct( parsers.TightListItem(bullet) + return ( Ct( parsers.TightListItem(bullet) * ( (parsers.check_minimal_indent / "") * parsers.TightListItem(bullet) )^0 @@ -29024,7 +29093,7 @@ end ) / writer.bulletlist end - parsers.BulletList = parsers.BulletListOfType(parsers.dash) + parsers.BulletList = parsers.BulletListOfType(parsers.dash) + parsers.BulletListOfType(parsers.asterisk) + parsers.BulletListOfType(parsers.plus) @@ -29046,11 +29115,11 @@ end return Cg(enumerator, "listtype") * (Ct( parsers.TightListItem(Cb("listtype")) * ((parsers.check_minimal_indent / "") * parsers.TightListItem(enumerator))^0) - * Cc(true) - * -#((parsers.list_blank^0 / "") + * Cc(true) + * -#((parsers.list_blank^0 / "") * parsers.check_minimal_indent * enumerator) + Ct( parsers.LooseListItem(Cb("listtype")) - * ((parsers.list_blank^0 / "") + * ((parsers.list_blank^0 / "") * (parsers.check_minimal_indent / "") * parsers.LooseListItem(enumerator))^0) * Cc(false) ) * Ct(Cb("listtype")) / ordered_list @@ -29090,10 +29159,10 @@ end parsers.AtxHeading = parsers.check_trail_no_rem * Cg(parsers.heading_start, "level") * ((C( parsers.optionalspace - * parsers.hash^0 + * parsers.hash^0 * parsers.optionalspace * parsers.newline) - + parsers.spacechar^1 + + parsers.spacechar^1 * C(parsers.line)) / strip_atx_end / parsers.parse_heading_text) @@ -29108,7 +29177,7 @@ end * parsers.newline^-1 parsers.SetextHeading = parsers.freeze_trail * parsers.check_trail_no_rem - * #(parsers.heading_text + * #(parsers.heading_text * parsers.check_minimal_indent * parsers.check_trail * parsers.heading_level) * Cs(parsers.heading_text) / parsers.parse_heading_text @@ -29637,16 +29706,59 @@ end table.sort(opt_string) local salt = table.concat(opt_string, ",") .. "," .. metadata.version local output + local function convert(input) + local document = self.parser_functions.parse_blocks(input) + local output = util.rope_to_string(writer.document(document)) +% \end{macrocode} +% \begin{markdown} +% Remove block element / paragraph separators immediately followed by the +% output of \luamref{writer->undosep}, possibly interleaved by section ends. +% Then, remove any leftover output of \luamref{writer->undosep}. +% \end{markdown} +% \begin{macrocode} + local undosep_start, undosep_end + local potential_secend_start, secend_start + local potential_sep_start, sep_start + while true do + -- find a `writer->undosep` + undosep_start, undosep_end = output:find(writer.undosep_text, 1, true) + if undosep_start == nil then break end + -- skip any preceding section ends + secend_start = undosep_start + while true do + potential_secend_start = secend_start - #writer.secend_text + if potential_secend_start < 1 + or output:sub(potential_secend_start, secend_start - 1) ~= writer.secend_text then + break + end + secend_start = potential_secend_start + end + -- find an immediately preceding block element / paragraph separator + sep_start = secend_start + potential_sep_start = sep_start - #writer.interblocksep_text + if potential_sep_start >= 1 + and output:sub(potential_sep_start, sep_start - 1) == writer.interblocksep_text then + sep_start = potential_sep_start + else + potential_sep_start = sep_start - #writer.paragraphsep_text + if potential_sep_start >= 1 + and output:sub(potential_sep_start, sep_start - 1) == writer.paragraphsep_text then + sep_start = potential_sep_start + end + end + -- remove `writer->undosep` and immediately preceding block element / paragraph separator + output = output:sub(1, sep_start - 1) + .. output:sub(secend_start, undosep_start - 1) + .. output:sub(undosep_end + 1) + end + return output + end % \end{macrocode} % \begin{markdown} % If we cache markdown documents, produce the cache file and transform its % filename to plain \TeX{} output via the \luamref{writer->pack} method. % \end{markdown} % \begin{macrocode} - local function convert(input) - local document = self.parser_functions.parse_blocks(input) - return util.rope_to_string(writer.document(document)) - end if options.eagerCache or options.finalizeCache then local name = util.cache(options.cacheDir, input, salt, convert, ".md" .. writer.suffix) @@ -29840,11 +29952,11 @@ M.extensions.citations = function(citation_nbsps) + parsers.bracketed + parsers.inticks + parsers.autolink - + V("InlineHtml") + + V("InlineHtml") + V("Space") + V("Endline") + (parsers.anyescaped - (parsers.newline + parsers.rbracket + parsers.blankline^2)) - - (parsers.spnl * parsers.dash^-1 * parsers.at))^0) + - (parsers.spnl * parsers.dash^-1 * parsers.at))^1) local citation_body_postnote = Cs((parsers.alphanumeric^1 @@ -29856,18 +29968,28 @@ M.extensions.citations = function(citation_nbsps) + (parsers.anyescaped - (parsers.newline + parsers.rbracket + parsers.semicolon + parsers.blankline^2)) - - (parsers.spnl * parsers.rbracket))^0) + - (parsers.spnl * parsers.rbracket))^1) local citation_body_chunk - = citation_body_prenote - * parsers.spnlc * citation_name + = ( citation_body_prenote + * parsers.spnlc_sep + + Cc("") + * parsers.spnlc + ) + * citation_name * (parsers.internal_punctuation - parsers.semicolon)^-1 - * parsers.spnlc * citation_body_postnote + * ( parsers.spnlc + * citation_body_postnote + + Cc("") + * parsers.spnlc + ) local citation_body = citation_body_chunk - * (parsers.semicolon * parsers.spnlc - * citation_body_chunk)^0 + * ( parsers.semicolon + * parsers.spnlc + * citation_body_chunk + )^0 local citation_headless_body_postnote = Cs((parsers.alphanumeric^1 @@ -29883,8 +30005,10 @@ M.extensions.citations = function(citation_nbsps) local citation_headless_body = citation_headless_body_postnote - * (parsers.sp * parsers.semicolon * parsers.spnlc - * citation_body_chunk)^0 + * ( parsers.semicolon + * parsers.spnlc + * citation_body_chunk + )^0 local citations = function(text_cites, raw_cites) @@ -29969,7 +30093,7 @@ M.extensions.content_blocks = function(language_map) % \begin{macrocode} local languages_json = (function() local base, prev, curr - for _, pathname in ipairs{util.lookup_files(language_map, { all=true })} do + for _, pathname in ipairs{kpse.lookup(language_map, { all=true })} do local file = io.open(pathname, "r") if not file then goto continue end local input = assert(file:read("*a")) @@ -30180,7 +30304,7 @@ M.extensions.definition_lists = function(tight_lists) local indented_blocks = function(bl) return Cs( bl - * (blank^1 * (parsers.check_minimal_indent / "") + * (blank^1 * (parsers.check_minimal_indent / "") * parsers.check_code_trail * -parsers.blankline * bl)^0 * (blank^1 + parsers.eof)) end @@ -30348,7 +30472,7 @@ M.extensions.fancy_lists = function() local digit_marker = parsers.dig * parsers.dig^-8 - local markers = { + local markers = { {lowercase_opening_roman_marker, lowercase_roman_marker}, {uppercase_opening_roman_marker, uppercase_roman_marker}, lowercase_letter_marker, @@ -30358,9 +30482,9 @@ M.extensions.fancy_lists = function() digit_marker } - local delims = { - parsers.period, - parsers.rparent + local delims = { + parsers.period, + parsers.rparent } local markers_table = combine_markers_and_delims(markers, delims) @@ -30466,11 +30590,11 @@ M.extensions.fancy_lists = function() return Cg(enumerator_start, "listtype") * (Ct( TightListItem(Cb("listtype")) * ((parsers.check_minimal_indent / "") * TightListItem(enumerator_cont))^0) - * Cc(true) - * -#((parsers.conditionally_indented_blankline^0 / "") + * Cc(true) + * -#((parsers.conditionally_indented_blankline^0 / "") * parsers.check_minimal_indent * enumerator_cont) + Ct( LooseListItem(Cb("listtype")) - * ((parsers.conditionally_indented_blankline^0 / "") + * ((parsers.conditionally_indented_blankline^0 / "") * (parsers.check_minimal_indent / "") * LooseListItem(enumerator_cont))^0) * Cc(false) ) * Ct(Cb("listtype")) / fancylist @@ -30479,10 +30603,10 @@ M.extensions.fancy_lists = function() local FancyList = join_table_with_func(FancyListOfType, markers_table) local Endline = parsers.newline - * (parsers.check_minimal_indent - * -parsers.EndlineExceptions - + parsers.check_optional_indent - * -parsers.EndlineExceptions + * (parsers.check_minimal_indent + * -parsers.EndlineExceptions + + parsers.check_optional_indent + * -parsers.EndlineExceptions * -starter) * parsers.spacechar^0 / writer.soft_line_break @@ -30574,12 +30698,12 @@ M.extensions.fenced_code = function(blank_before_code_fence, return str:gsub("^%s*(.-)%s*$", "%1") end - local tilde_infostring = Cs(Cs((V("HtmlEntity") - + parsers.anyescaped + local tilde_infostring = Cs(Cs((V("HtmlEntity") + + parsers.anyescaped - parsers.newline)^0) / strip_enclosing_whitespaces) - local backtick_infostring = Cs(Cs((V("HtmlEntity") + local backtick_infostring = Cs(Cs((V("HtmlEntity") + (-#(parsers.backslash * parsers.backtick) * parsers.anyescaped) - parsers.newline - parsers.backtick)^0) @@ -30588,14 +30712,14 @@ M.extensions.fenced_code = function(blank_before_code_fence, local fenceindent local function has_trail(indent_table) - return indent_table ~= nil and - indent_table.trail ~= nil and + return indent_table ~= nil and + indent_table.trail ~= nil and next(indent_table.trail) ~= nil end local function has_indents(indent_table) - return indent_table ~= nil and - indent_table.indents ~= nil and + return indent_table ~= nil and + indent_table.indents ~= nil and next(indent_table.indents) ~= nil end @@ -30808,24 +30932,57 @@ M.extensions.fenced_divs = function(blank_before_div_fence) % \par % \begin{markdown} % -% Initialize a named group named `div_level` for tracking how deep we are -% nested in divs. +% Initialize a named group named `fenced_div_level` for tracking how deep +% we are nested in divs and the named group `fenced_div_num_opening_indents` +% for tracking the indent of the starting div fence. The former named group +% is immutable and should roll back properly when we fail to match a fenced +% div. The latter is mutable and may contain items from unsuccessful matches +% on top. However, we always know how many items at the head of the latter we +% can trust by consulting the former. % % \end{markdown} % \begin{macrocode} - self.initialize_named_group("div_level", "0") + self.initialize_named_group("fenced_div_level", "0") + self.initialize_named_group("fenced_div_num_opening_indents") - local function increment_div_level(increment) - local function update_div_level(s, i, current_level) -- luacheck: ignore s i - current_level = tonumber(current_level) - local next_level = tostring(current_level + increment) - return true, next_level + local function increment_div_level() + local function push_indent_table(s, i, indent_table, -- luacheck: ignore s i + fenced_div_num_opening_indents, fenced_div_level) + fenced_div_level = tonumber(fenced_div_level) + 1 + local num_opening_indents = 0 + if indent_table.indents ~= nil then + num_opening_indents = #indent_table.indents + end + fenced_div_num_opening_indents[fenced_div_level] = num_opening_indents + return true, fenced_div_num_opening_indents end - return Cg( Cmt(Cb("div_level"), update_div_level) - , "div_level") + local function increment_level(s, i, fenced_div_level) -- luacheck: ignore s i + fenced_div_level = tonumber(fenced_div_level) + 1 + return true, tostring(fenced_div_level) + end + + return Cg( Cmt( Cb("indent_info") + * Cb("fenced_div_num_opening_indents") + * Cb("fenced_div_level"), push_indent_table) + , "fenced_div_num_opening_indents") + * Cg( Cmt( Cb("fenced_div_level"), increment_level) + , "fenced_div_level") end - + + local function decrement_div_level() + local function pop_indent_table(s, i, fenced_div_indent_table, fenced_div_level) -- luacheck: ignore s i + fenced_div_level = tonumber(fenced_div_level) + fenced_div_indent_table[fenced_div_level] = nil + return true, tostring(fenced_div_level - 1) + end + + return Cg( Cmt( Cb("fenced_div_num_opening_indents") + * Cb("fenced_div_level"), pop_indent_table) + , "fenced_div_level") + end + + local non_fenced_div_block = parsers.check_minimal_indent * V("Block") - parsers.check_minimal_indent_and_trail * fenced_div_end @@ -30860,11 +31017,12 @@ M.extensions.fenced_divs = function(blank_before_div_fence) return attr end / writer.div_begin - * increment_div_level(1) + * increment_div_level() * parsers.skipblanklines * Ct(content_loop) * parsers.minimally_indented_blank^0 - * parsers.check_minimal_indent_and_trail * fenced_div_end * increment_div_level(-1) + * parsers.check_minimal_indent_and_trail * fenced_div_end + * decrement_div_level() * (Cc("") / writer.div_end) self.insert_pattern("Block after Verbatim", @@ -30878,17 +31036,38 @@ M.extensions.fenced_divs = function(blank_before_div_fence) % % If the `blank_before_div_fence` parameter is `false`, we will have the % closing div at the beginning of a line break the current paragraph if -% we are currently nested in a div. +% we are currently nested in a div and the indentation matches the opening +% div fence. % % \end{markdown} % \begin{macrocode} - local function check_div_level(s, i, current_level) -- luacheck: ignore s i - current_level = tonumber(current_level) - return current_level > 0 + local function is_inside_div() + local function check_div_level(s, i, fenced_div_level) -- luacheck: ignore s i + fenced_div_level = tonumber(fenced_div_level) + return fenced_div_level > 0 + end + + return Cmt(Cb("fenced_div_level"), check_div_level) + end + + local function check_indent() + local function compare_indent(s, i, indent_table, -- luacheck: ignore s i + fenced_div_num_opening_indents, fenced_div_level) + fenced_div_level = tonumber(fenced_div_level) + local num_current_indents = (indent_table.current_line_indents ~= nil and + #indent_table.current_line_indents) or 0 + local num_opening_indents = fenced_div_num_opening_indents[fenced_div_level] + return num_current_indents == num_opening_indents + end + + return Cmt( Cb("indent_info") + * Cb("fenced_div_num_opening_indents") + * Cb("fenced_div_level"), compare_indent) end - local is_inside_div = Cmt(Cb("div_level"), check_div_level) - local fencestart = is_inside_div * fenced_div_end + local fencestart = is_inside_div() + * fenced_div_end + * check_indent() if not blank_before_div_fence then self.update_rule("EndlineExceptions", function(previous_pattern) @@ -30945,10 +31124,10 @@ M.extensions.header_attributes = function() return s:gsub("%s*$","") end - local heading_line = (parsers.linechar + local heading_line = (parsers.linechar - (parsers.attributes * parsers.optionalspace - * parsers.newline))^1 + * parsers.newline))^1 - parsers.thematic_break_lines local heading_text = heading_line @@ -30956,7 +31135,7 @@ M.extensions.header_attributes = function() * parsers.newline^-1 local SetextHeading = parsers.freeze_trail * parsers.check_trail_no_rem - * #(heading_text + * #(heading_text * (parsers.attributes * parsers.optionalspace * parsers.newline)^-1 @@ -31131,7 +31310,7 @@ M.extensions.link_attributes = function() % \begin{macrocode} local define_reference_parser = (parsers.check_trail / "") * parsers.link_label * parsers.colon * parsers.spnlc * parsers.url - * ( parsers.spnlc_sep * parsers.title * (parsers.spnlc * Ct(parsers.attributes)) + * ( parsers.spnlc_sep * parsers.title * (parsers.spnlc * Ct(parsers.attributes)) * parsers.only_blank + parsers.spnlc_sep * parsers.title * parsers.only_blank + Cc("") * (parsers.spnlc * Ct(parsers.attributes)) * parsers.only_blank @@ -31284,7 +31463,7 @@ M.extensions.notes = function(notes, inline_notes) local indented_blocks = function(bl) return Cs( bl - * (blank^1 * (parsers.check_optional_indent / "") + * (blank^1 * (parsers.check_optional_indent / "") * parsers.check_code_trail * -parsers.blankline * bl)^0) end @@ -31447,7 +31626,7 @@ M.extensions.pipe_tables = function(table_captions, table_attributes) , table_hline_separator , table_hline_column) - local table_caption_beginning = (parsers.check_minimal_blank_indent_and_any_trail_no_rem + local table_caption_beginning = (parsers.check_minimal_blank_indent_and_any_trail_no_rem * parsers.optionalspace * parsers.newline)^0 * parsers.check_minimal_indent_and_trail * (P("Table")^-1 * parsers.colon) @@ -32246,7 +32425,7 @@ function M.new(options) % % \end{markdown} % \begin{macrocode} - local pathname = util.lookup_files(filename) + local pathname = kpse.lookup(filename) local input_file = assert(io.open(pathname, "r"), [[Could not open user-defined syntax extension "]] .. pathname .. [[" for reading]]) diff --git a/Master/texmf-dist/tex/generic/markdown/markdown.tex b/Master/texmf-dist/tex/generic/markdown/markdown.tex index 39d77c941de..22eb36e4fba 100644 --- a/Master/texmf-dist/tex/generic/markdown/markdown.tex +++ b/Master/texmf-dist/tex/generic/markdown/markdown.tex @@ -572,8 +572,8 @@ { boolean } { true } \ExplSyntaxOff -\def\markdownLastModified{2024-03-09}% -\def\markdownVersion{3.4.2-0-ga45cf0ed}% +\def\markdownLastModified{2024-04-04}% +\def\markdownVersion{3.4.3-0-ge2c6be1a}% \let\markdownBegin\relax \let\markdownEnd\relax \let\markdownInput\relax diff --git a/Master/texmf-dist/tex/luatex/markdown/markdown.lua b/Master/texmf-dist/tex/luatex/markdown/markdown.lua index 37448b4503a..1e520a30bf5 100644 --- a/Master/texmf-dist/tex/luatex/markdown/markdown.lua +++ b/Master/texmf-dist/tex/luatex/markdown/markdown.lua @@ -58,7 +58,7 @@ -- those in the standard .ins files. -- local metadata = { - version = "3.4.2-0-ga45cf0ed", + version = "3.4.3-0-ge2c6be1a", comment = "A module for the conversion from markdown to plain TeX", author = "John MacFarlane, Hans Hagen, Vít Starý Novotný", copyright = {"2009-2016 John MacFarlane, Hans Hagen", @@ -222,9 +222,6 @@ function util.encode_json_string(s) s = s:gsub([["]], [[\"]]) return [["]] .. s .. [["]] end -function util.lookup_files(f, options) - return kpse.lookup(f, options) -end function util.expand_tabs_in_line(s, tabstop) local tab = tabstop or 4 local corr = 0 @@ -2488,7 +2485,7 @@ function entities.char_entity(s) end local char_table = {} for _, code_point in ipairs(code_points) do - table.insert(char_table, unicode.utf8.char(code_point)) + table.insert(char_table, unicode.utf8.char(code_point)) end return table.concat(char_table) end @@ -2540,13 +2537,20 @@ function M.writer.new(options) function self.pack(name) return [[\input{]] .. name .. [[}\relax]] end + self.interblocksep_text = "\\markdownRendererInterblockSeparator\n{}" function self.interblocksep() if not self.is_writing then return "" end - return "\\markdownRendererInterblockSeparator\n{}" + return self.interblocksep_text end + self.paragraphsep_text = "\\markdownRendererParagraphSeparator\n{}" function self.paragraphsep() if not self.is_writing then return "" end - return "\\markdownRendererParagraphSeparator\n{}" + return self.paragraphsep_text + end + self.undosep_text = "\\markdownRendererUndoSeparator\n{}" + function self.undosep() + if not self.is_writing then return "" end + return self.undosep_text end self.soft_line_break = function() if self.flatten_inlines then return "\n" end @@ -2911,6 +2915,7 @@ function M.writer.new(options) if attributes["#" .. self.slice_end_identifier] ~= nil and self.slice_end_type == "^" then if self.is_writing then + table.insert(buf, self.undosep()) table.insert(buf, tear_down_attributes()) end self.is_writing = false @@ -2949,6 +2954,7 @@ function M.writer.new(options) if attributes["#" .. self.slice_end_identifier] ~= nil and self.slice_end_type == "$" then if self.is_writing then + table.insert(buf, self.undosep()) table.insert(buf, tear_down_attributes()) end self.is_writing = false @@ -2961,105 +2967,107 @@ function M.writer.new(options) end return buf end -local function create_auto_identifier(s) - local buffer = {} - local prev_space = false - local letter_found = false + local function create_auto_identifier(s) + local buffer = {} + local prev_space = false + local letter_found = false - for _, code in utf8.codes(uni_algos.normalize.NFC(s)) do - local char = utf8.char(code) + for _, code in utf8.codes(uni_algos.normalize.NFC(s)) do + local char = utf8.char(code) - -- Remove everything up to the first letter. - if not letter_found then - local is_letter = unicode.utf8.match(char, "%a") - if is_letter then - letter_found = true - else - goto continue + -- Remove everything up to the first letter. + if not letter_found then + local is_letter = unicode.utf8.match(char, "%a") + if is_letter then + letter_found = true + else + goto continue + end end - end - - -- Remove all non-alphanumeric characters, except underscores, hyphens, and periods. - if not unicode.utf8.match(char, "[%w_%-%.%s]") then - goto continue - end - -- Replace all spaces and newlines with hyphens. - if unicode.utf8.match(char, "[%s\n]") then - char = "-" - if prev_space then + -- Remove all non-alphanumeric characters, except underscores, hyphens, and periods. + if not unicode.utf8.match(char, "[%w_%-%.%s]") then goto continue + end + + -- Replace all spaces and newlines with hyphens. + if unicode.utf8.match(char, "[%s\n]") then + char = "-" + if prev_space then + goto continue + else + prev_space = true + end else - prev_space = true + -- Convert all alphabetic characters to lowercase. + char = unicode.utf8.lower(char) + prev_space = false end - else - -- Convert all alphabetic characters to lowercase. - char = unicode.utf8.lower(char) - prev_space = false - end - table.insert(buffer, char) + table.insert(buffer, char) - ::continue:: - end + ::continue:: + end + + if prev_space then + table.remove(buffer) + end - if prev_space then - table.remove(buffer) + local identifier = #buffer == 0 and "section" or table.concat(buffer, "") + return identifier end + local function create_gfm_auto_identifier(s) + local buffer = {} + local prev_space = false + local letter_found = false - local identifier = #buffer == 0 and "section" or table.concat(buffer, "") - return identifier -end -local function create_gfm_auto_identifier(s) - local buffer = {} - local prev_space = false - local letter_found = false + for _, code in utf8.codes(uni_algos.normalize.NFC(s)) do + local char = utf8.char(code) - for _, code in utf8.codes(uni_algos.normalize.NFC(s)) do - local char = utf8.char(code) + -- Remove everything up to the first non-space. + if not letter_found then + local is_letter = unicode.utf8.match(char, "%S") + if is_letter then + letter_found = true + else + goto continue + end + end - -- Remove everything up to the first non-space. - if not letter_found then - local is_letter = unicode.utf8.match(char, "%S") - if is_letter then - letter_found = true - else + -- Remove all non-alphanumeric characters, except underscores and hyphens. + if not unicode.utf8.match(char, "[%w_%-%s]") then + prev_space = false goto continue end - end - - -- Remove all non-alphanumeric characters, except underscores and hyphens. - if not unicode.utf8.match(char, "[%w_%-%s]") then - prev_space = false - goto continue - end - -- Replace all spaces and newlines with hyphens. - if unicode.utf8.match(char, "[%s\n]") then - char = "-" - if prev_space then - goto continue + -- Replace all spaces and newlines with hyphens. + if unicode.utf8.match(char, "[%s\n]") then + char = "-" + if prev_space then + goto continue + else + prev_space = true + end else - prev_space = true + -- Convert all alphabetic characters to lowercase. + char = unicode.utf8.lower(char) + prev_space = false end - else - -- Convert all alphabetic characters to lowercase. - char = unicode.utf8.lower(char) - prev_space = false - end - table.insert(buffer, char) + table.insert(buffer, char) - ::continue:: - end + ::continue:: + end - if prev_space then - table.remove(buffer) - end + if prev_space then + table.remove(buffer) + end - local identifier = #buffer == 0 and "section" or table.concat(buffer, "") - return identifier -end + local identifier = #buffer == 0 and "section" or table.concat(buffer, "") + return identifier + end + self.secbegin_text = "\\markdownRendererSectionBegin\n" + self.secend_text = "\n\\markdownRendererSectionEnd " function self.heading(s, level, attributes) local buf = {} local flat_text, inlines = table.unpack(s) @@ -3069,8 +3077,8 @@ end table.insert(buf, self.push_attributes("heading", nil, - "\\markdownRendererSectionBegin\n", - "\n\\markdownRendererSectionEnd ")) + self.secbegin_text, + self.secend_text)) end -- pop attributes for sections that have ended @@ -3091,8 +3099,8 @@ end -- push attributes for the new section local start_output = {} local end_output = {} - table.insert(start_output, "\\markdownRendererSectionBegin\n") - table.insert(end_output, "\n\\markdownRendererSectionEnd ") + table.insert(start_output, self.secbegin_text) + table.insert(end_output, self.secend_text) table.insert(buf, self.push_attributes("heading", normalized_attributes, @@ -3201,7 +3209,6 @@ parsers.letter = R("AZ","az") parsers.alphanumeric = R("AZ","az","09") parsers.keyword = parsers.letter * (parsers.alphanumeric + parsers.dash)^0 -parsers.internal_punctuation = S(":;,.?") parsers.doubleasterisks = P("**") parsers.doubleunderscores = P("__") @@ -3212,7 +3219,35 @@ parsers.any = P(1) parsers.succeed = P(true) parsers.fail = P(false) -parsers.escapable = S("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~") +parsers.internal_punctuation = S(":;,.?") +parsers.ascii_punctuation = S("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~") +parsers.punctuation = {} +(function() + local pathname = kpse.lookup("UnicodeData.txt") + local file = assert(io.open(pathname, "r"), + [[Could not open file "UnicodeData.txt"]]) + for line in file:lines() do + local codepoint, major_category = line:match("^(%x+);[^;]*;(%a)") + if major_category == "P" or major_category == "S" then + local code = unicode.utf8.char(tonumber(codepoint, 16)) + if parsers.punctuation[#code] == nil then + parsers.punctuation[#code] = parsers.fail + end + local code_parser = parsers.succeed + for i = 1, #code do + local byte = code:sub(i, i) + local byte_parser = S(byte) + code_parser = code_parser + * byte_parser + end + parsers.punctuation[#code] = parsers.punctuation[#code] + + code_parser + end + end + assert(file:close()) +end)() + +parsers.escapable = parsers.ascii_punctuation parsers.anyescaped = parsers.backslash / "" * parsers.escapable + parsers.any @@ -3463,7 +3498,7 @@ local function left_blank_starter(indent_table) return blank_starter_index end -local function traverse_indent(s, i, indent_table, is_optional, is_blank) +local function traverse_indent(s, i, indent_table, is_optional, is_blank, current_line_indents) local new_index = i local preceding_indentation = 0 @@ -3471,6 +3506,10 @@ local function traverse_indent(s, i, indent_table, is_optional, is_blank) local blank_starter = left_blank_starter(indent_table) + if current_line_indents == nil then + current_line_indents = {} + end + for index = 1,#indent_table.indents do local value = indent_table.indents[index] local pattern = decode_pattern(value.name) @@ -3480,10 +3519,10 @@ local function traverse_indent(s, i, indent_table, is_optional, is_blank) if new_indent_info == nil then local blankline_end = lpeg.match(Ct(parsers.blankline * Cg(Cp(), "pos")), s, new_index) if is_optional or not indent_table.ignore_blockquote_blank or not blankline_end then - return is_optional, new_index, current_trail + return is_optional, new_index, current_trail, current_line_indents end - return traverse_indent(s, tonumber(blankline_end.pos), indent_table, is_optional, is_blank) + return traverse_indent(s, tonumber(blankline_end.pos), indent_table, is_optional, is_blank, current_line_indents) end local raw_last_trail = new_indent_info[1] @@ -3502,7 +3541,7 @@ local function traverse_indent(s, i, indent_table, is_optional, is_blank) if next(current_trail) ~= nil then if not space_only and current_trail.is_code then - return is_optional, new_index, current_trail + return is_optional, new_index, current_trail, current_line_indents end if current_trail.internal_remainder ~= nil then raw_last_trail = current_trail.internal_remainder @@ -3534,7 +3573,7 @@ local function traverse_indent(s, i, indent_table, is_optional, is_blank) local sp = process_starter_spacing(total_indent_level, spacing_to_process, minimum, left_strip_length) if space_only and not sp.is_minimum then - return is_optional or (is_blank and blank_starter <= index), new_index, current_trail + return is_optional or (is_blank and blank_starter <= index), new_index, current_trail, current_line_indents end local indent_length = raw_last_trail_length + delimiter_length + sp.left_total_stripped @@ -3548,10 +3587,12 @@ local function traverse_indent(s, i, indent_table, is_optional, is_blank) current_trail = {is_code=sp.is_code, remainder=sp.remainder, internal_remainder=sp.minimum_remainder, total_length=sp.total_length, full_remainder=sp.full_remainder} + + current_line_indents[#current_line_indents + 1] = new_indent_info new_index = next_index end - return true, new_index, current_trail + return true, new_index, current_trail, current_line_indents end local function check_trail(expect_code, is_code) @@ -3609,9 +3650,11 @@ local function check_continuation_indentation(s, i, indent_table, is_optional, i return true end - local passes, new_index, current_trail = traverse_indent(s, i, indent_table, is_optional, is_blank) + local passes, new_index, current_trail, current_line_indents = + traverse_indent(s, i, indent_table, is_optional, is_blank) if passes then + indent_table.current_line_indents = current_line_indents indent_table = add_trail(indent_table, current_trail) return new_index, indent_table end @@ -4113,7 +4156,7 @@ parsers.html_comment = Cs( parsers.html_comment_start parsers.html_inline_comment = (parsers.html_comment_start / "") * -P(">") * -P("->") - * Cs((V("NoSoftLineBreakEndline") + parsers.any - P("--") + * Cs((V("NoSoftLineBreakEndline") + parsers.any - parsers.nested_breaking_blank - parsers.html_comment_end)^0) * (parsers.html_comment_end / "") @@ -4907,7 +4950,6 @@ function M.reader.new(writer, options) return lpeg.R("\240\244") * cont * cont * cont end end - local function check_unicode_type(s, i, start_pos, end_pos, chartype) local c local char_length @@ -4917,15 +4959,22 @@ function M.reader.new(writer, options) else char_length = pos + 1 end - c = lpeg.match({ C(utf8_by_byte_count(char_length)) },s,i+pos) - if (c ~= nil) and (unicode.utf8.match(c, chartype)) then - return i + + if (chartype == "punctuation") then + if lpeg.match(parsers.punctuation[char_length], s, i+pos) then + return i + end + else + c = lpeg.match({ C(utf8_by_byte_count(char_length)) },s,i+pos) + if (c ~= nil) and (unicode.utf8.match(c, chartype)) then + return i + end end end end local function check_preceding_unicode_punctuation(s, i) - return check_unicode_type(s, i, -4, -1, "%p") + return check_unicode_type(s, i, -4, -1, "punctuation") end local function check_preceding_unicode_whitespace(s, i) @@ -4933,7 +4982,7 @@ function M.reader.new(writer, options) end local function check_following_unicode_punctuation(s, i) - return check_unicode_type(s, i, 0, 3, "%p") + return check_unicode_type(s, i, 0, 3, "punctuation") end local function check_following_unicode_whitespace(s, i) @@ -6136,7 +6185,43 @@ end local output local function convert(input) local document = self.parser_functions.parse_blocks(input) - return util.rope_to_string(writer.document(document)) + local output = util.rope_to_string(writer.document(document)) + local undosep_start, undosep_end + local potential_secend_start, secend_start + local potential_sep_start, sep_start + while true do + -- find a `writer->undosep` + undosep_start, undosep_end = output:find(writer.undosep_text, 1, true) + if undosep_start == nil then break end + -- skip any preceding section ends + secend_start = undosep_start + while true do + potential_secend_start = secend_start - #writer.secend_text + if potential_secend_start < 1 + or output:sub(potential_secend_start, secend_start - 1) ~= writer.secend_text then + break + end + secend_start = potential_secend_start + end + -- find an immediately preceding block element / paragraph separator + sep_start = secend_start + potential_sep_start = sep_start - #writer.interblocksep_text + if potential_sep_start >= 1 + and output:sub(potential_sep_start, sep_start - 1) == writer.interblocksep_text then + sep_start = potential_sep_start + else + potential_sep_start = sep_start - #writer.paragraphsep_text + if potential_sep_start >= 1 + and output:sub(potential_sep_start, sep_start - 1) == writer.paragraphsep_text then + sep_start = potential_sep_start + end + end + -- remove `writer->undosep` and immediately preceding block element / paragraph separator + output = output:sub(1, sep_start - 1) + .. output:sub(secend_start, undosep_start - 1) + .. output:sub(undosep_end + 1) + end + return output end if options.eagerCache or options.finalizeCache then local name = util.cache(options.cacheDir, input, salt, convert, @@ -6254,11 +6339,11 @@ M.extensions.citations = function(citation_nbsps) + parsers.bracketed + parsers.inticks + parsers.autolink - + V("InlineHtml") + + V("InlineHtml") + V("Space") + V("Endline") + (parsers.anyescaped - (parsers.newline + parsers.rbracket + parsers.blankline^2)) - - (parsers.spnl * parsers.dash^-1 * parsers.at))^0) + - (parsers.spnl * parsers.dash^-1 * parsers.at))^1) local citation_body_postnote = Cs((parsers.alphanumeric^1 @@ -6270,18 +6355,28 @@ M.extensions.citations = function(citation_nbsps) + (parsers.anyescaped - (parsers.newline + parsers.rbracket + parsers.semicolon + parsers.blankline^2)) - - (parsers.spnl * parsers.rbracket))^0) + - (parsers.spnl * parsers.rbracket))^1) local citation_body_chunk - = citation_body_prenote - * parsers.spnlc * citation_name + = ( citation_body_prenote + * parsers.spnlc_sep + + Cc("") + * parsers.spnlc + ) + * citation_name * (parsers.internal_punctuation - parsers.semicolon)^-1 - * parsers.spnlc * citation_body_postnote + * ( parsers.spnlc + * citation_body_postnote + + Cc("") + * parsers.spnlc + ) local citation_body = citation_body_chunk - * (parsers.semicolon * parsers.spnlc - * citation_body_chunk)^0 + * ( parsers.semicolon + * parsers.spnlc + * citation_body_chunk + )^0 local citation_headless_body_postnote = Cs((parsers.alphanumeric^1 @@ -6297,8 +6392,10 @@ M.extensions.citations = function(citation_nbsps) local citation_headless_body = citation_headless_body_postnote - * (parsers.sp * parsers.semicolon * parsers.spnlc - * citation_body_chunk)^0 + * ( parsers.semicolon + * parsers.spnlc + * citation_body_chunk + )^0 local citations = function(text_cites, raw_cites) @@ -6359,7 +6456,7 @@ end M.extensions.content_blocks = function(language_map) local languages_json = (function() local base, prev, curr - for _, pathname in ipairs{util.lookup_files(language_map, { all=true })} do + for _, pathname in ipairs{kpse.lookup(language_map, { all=true })} do local file = io.open(pathname, "r") if not file then goto continue end local input = assert(file:read("*a")) @@ -7040,17 +7137,44 @@ M.extensions.fenced_divs = function(blank_before_div_fence) * parsers.colon^3 * parsers.optionalspace * (parsers.newline + parsers.eof) - self.initialize_named_group("div_level", "0") + self.initialize_named_group("fenced_div_level", "0") + self.initialize_named_group("fenced_div_num_opening_indents") + + local function increment_div_level() + local function push_indent_table(s, i, indent_table, -- luacheck: ignore s i + fenced_div_num_opening_indents, fenced_div_level) + fenced_div_level = tonumber(fenced_div_level) + 1 + local num_opening_indents = 0 + if indent_table.indents ~= nil then + num_opening_indents = #indent_table.indents + end + fenced_div_num_opening_indents[fenced_div_level] = num_opening_indents + return true, fenced_div_num_opening_indents + end - local function increment_div_level(increment) - local function update_div_level(s, i, current_level) -- luacheck: ignore s i - current_level = tonumber(current_level) - local next_level = tostring(current_level + increment) - return true, next_level + local function increment_level(s, i, fenced_div_level) -- luacheck: ignore s i + fenced_div_level = tonumber(fenced_div_level) + 1 + return true, tostring(fenced_div_level) end - return Cg( Cmt(Cb("div_level"), update_div_level) - , "div_level") + return Cg( Cmt( Cb("indent_info") + * Cb("fenced_div_num_opening_indents") + * Cb("fenced_div_level"), push_indent_table) + , "fenced_div_num_opening_indents") + * Cg( Cmt( Cb("fenced_div_level"), increment_level) + , "fenced_div_level") + end + + local function decrement_div_level() + local function pop_indent_table(s, i, fenced_div_indent_table, fenced_div_level) -- luacheck: ignore s i + fenced_div_level = tonumber(fenced_div_level) + fenced_div_indent_table[fenced_div_level] = nil + return true, tostring(fenced_div_level - 1) + end + + return Cg( Cmt( Cb("fenced_div_num_opening_indents") + * Cb("fenced_div_level"), pop_indent_table) + , "fenced_div_level") end local non_fenced_div_block = parsers.check_minimal_indent * V("Block") @@ -7087,11 +7211,12 @@ M.extensions.fenced_divs = function(blank_before_div_fence) return attr end / writer.div_begin - * increment_div_level(1) + * increment_div_level() * parsers.skipblanklines * Ct(content_loop) * parsers.minimally_indented_blank^0 - * parsers.check_minimal_indent_and_trail * fenced_div_end * increment_div_level(-1) + * parsers.check_minimal_indent_and_trail * fenced_div_end + * decrement_div_level() * (Cc("") / writer.div_end) self.insert_pattern("Block after Verbatim", @@ -7099,13 +7224,33 @@ M.extensions.fenced_divs = function(blank_before_div_fence) self.add_special_character(":") - local function check_div_level(s, i, current_level) -- luacheck: ignore s i - current_level = tonumber(current_level) - return current_level > 0 + local function is_inside_div() + local function check_div_level(s, i, fenced_div_level) -- luacheck: ignore s i + fenced_div_level = tonumber(fenced_div_level) + return fenced_div_level > 0 + end + + return Cmt(Cb("fenced_div_level"), check_div_level) + end + + local function check_indent() + local function compare_indent(s, i, indent_table, -- luacheck: ignore s i + fenced_div_num_opening_indents, fenced_div_level) + fenced_div_level = tonumber(fenced_div_level) + local num_current_indents = (indent_table.current_line_indents ~= nil and + #indent_table.current_line_indents) or 0 + local num_opening_indents = fenced_div_num_opening_indents[fenced_div_level] + return num_current_indents == num_opening_indents + end + + return Cmt( Cb("indent_info") + * Cb("fenced_div_num_opening_indents") + * Cb("fenced_div_level"), compare_indent) end - local is_inside_div = Cmt(Cb("div_level"), check_div_level) - local fencestart = is_inside_div * fenced_div_end + local fencestart = is_inside_div() + * fenced_div_end + * check_indent() if not blank_before_div_fence then self.update_rule("EndlineExceptions", function(previous_pattern) @@ -8116,7 +8261,7 @@ function M.new(options) end for _, user_extension_filename in ipairs(options.extensions) do local user_extension = (function(filename) - local pathname = util.lookup_files(filename) + local pathname = kpse.lookup(filename) local input_file = assert(io.open(pathname, "r"), [[Could not open user-defined syntax extension "]] .. pathname .. [[" for reading]]) |