git.iko.soy/typeable/pandoc
on master help sign in
Merge branch 'master' of https://github.com/jgm/pandoc into textcolor-support
commitf93d7d06f688654137b5e728601441881ff5aebf authorMarc Schreiber <marc.schreiber@fh-aachen.de> dateThu 13 Jul 2017, 11:35 (9 years ago) parent635f2998b502dd
134 files changed+6021−1963
COPYRIGHT+47−4 INSTALL.md+10−2 MANUAL.txt+78−28 Makefile+5−3 README.md+23−18 benchmark/benchmark-pandoc.hs+1−1 data/docx/word/numbering.xml+3−2 data/pandoc.lua+12−12 data/templates/default.commonmark+1−1 data/templates/default.dzslides+1−1 data/templates/default.html4+1−1 data/templates/default.html5+1−1 data/templates/default.latex+2−2 data/templates/default.markdown+1−1 data/templates/default.opendocument+129 data/templates/default.plain+1−1 data/templates/default.revealjs+12−3 data/templates/default.rtf+1−1 data/templates/default.s5+1−1 data/templates/default.slideous+1−1 data/templates/default.slidy+1−1 data/vimwiki.css+82 doc/lua-filters.md+37 linux/control.in+14−13 pandoc.cabal+18−12 src/Text/Pandoc.hs+10−8 src/Text/Pandoc/App.hs+59−23 src/Text/Pandoc/BCP47.hs+139 src/Text/Pandoc/Class.hs+28−6 src/Text/Pandoc/Compat/Time.hs+1−1 src/Text/Pandoc/Error.hs+8 src/Text/Pandoc/Extensions.hs+10−2 src/Text/Pandoc/Logging.hs+22−10 src/Text/Pandoc/Lua.hs+88−145 src/Text/Pandoc/Lua/PandocModule.hs+8−7 src/Text/Pandoc/Options.hs+2−2 src/Text/Pandoc/PDF.hs+34−13 src/Text/Pandoc/Parsing.hs+56−47 src/Text/Pandoc/Pretty.hs+1−1 src/Text/Pandoc/Readers.hs+10−8 src/Text/Pandoc/Readers/DocBook.hs+4−3 src/Text/Pandoc/Readers/Docx.hs+1−1 src/Text/Pandoc/Readers/Docx/Parse.hs+1−1 src/Text/Pandoc/Readers/Docx/StyleMap.hs+1−1 src/Text/Pandoc/Readers/HTML.hs+15−5 src/Text/Pandoc/Readers/Haddock.hs+2−2 src/Text/Pandoc/Readers/LaTeX.hs+1687−1119 src/Text/Pandoc/Readers/LaTeX/Types.hs+48 src/Text/Pandoc/Readers/Markdown.hs+69−39 src/Text/Pandoc/Readers/MediaWiki.hs+5−5 src/Text/Pandoc/Readers/Muse.hs+607 src/Text/Pandoc/Readers/OPML.hs+4−2 src/Text/Pandoc/Readers/Odt/Generic/Fallible.hs−5 src/Text/Pandoc/Readers/Odt/Generic/XMLConverter.hs+2−1 src/Text/Pandoc/Readers/Org.hs+2−1 src/Text/Pandoc/Readers/Org/Inlines.hs+3−2 src/Text/Pandoc/Readers/Org/ParserState.hs+10 src/Text/Pandoc/Readers/RST.hs+44−9 src/Text/Pandoc/Readers/TWiki.hs+5−6 src/Text/Pandoc/Readers/Textile.hs+5−7 src/Text/Pandoc/Readers/Txt2Tags.hs+5−3 src/Text/Pandoc/Readers/Vimwiki.hs+673 src/Text/Pandoc/Shared.hs+9−5 src/Text/Pandoc/Templates.hs+14−10 src/Text/Pandoc/Writers.hs+4−7 src/Text/Pandoc/Writers/AsciiDoc.hs+2−2 src/Text/Pandoc/Writers/CommonMark.hs+4−4 src/Text/Pandoc/Writers/ConTeXt.hs+46−40 src/Text/Pandoc/Writers/Custom.hs+5−2 src/Text/Pandoc/Writers/Docbook.hs+7−5 src/Text/Pandoc/Writers/Docx.hs+15−9 src/Text/Pandoc/Writers/DokuWiki.hs+2−2 src/Text/Pandoc/Writers/EPUB.hs+52−26 src/Text/Pandoc/Writers/FB2.hs+7−6 src/Text/Pandoc/Writers/HTML.hs+41−19 src/Text/Pandoc/Writers/Haddock.hs+2−2 src/Text/Pandoc/Writers/ICML.hs+4−4 src/Text/Pandoc/Writers/JATS.hs+7−5 src/Text/Pandoc/Writers/LaTeX.hs+134−112 src/Text/Pandoc/Writers/Man.hs+2−2 src/Text/Pandoc/Writers/Markdown.hs+21−10 src/Text/Pandoc/Writers/Math.hs+7 src/Text/Pandoc/Writers/MediaWiki.hs+3−4 src/Text/Pandoc/Writers/Ms.hs+4−3 src/Text/Pandoc/Writers/Muse.hs+26−23 src/Text/Pandoc/Writers/ODT.hs+41−7 src/Text/Pandoc/Writers/OPML.hs+3−3 src/Text/Pandoc/Writers/OpenDocument.hs+33−9 src/Text/Pandoc/Writers/Org.hs+2−2 src/Text/Pandoc/Writers/RST.hs+2−2 src/Text/Pandoc/Writers/RTF.hs+9−4 src/Text/Pandoc/Writers/TEI.hs+7−5 src/Text/Pandoc/Writers/Texinfo.hs+2−2 src/Text/Pandoc/Writers/Textile.hs+2−2 src/Text/Pandoc/Writers/ZimWiki.hs+2−2 stack.pkg.yaml+1 stack.yaml+1 test/Tests/Lua.hs+2−2 test/Tests/Old.hs+4 test/Tests/Readers/HTML.hs+6 test/Tests/Readers/LaTeX.hs+2−1 test/Tests/Readers/Muse.hs+318 test/Tests/Writers/Muse.hs+13−2 test/command/1390.md+20 test/command/2118.md+11 test/command/2378.md+27 test/command/262.md+26 test/command/3113.md+1−1 test/command/3236.md+9 test/command/3494.md+2−1 test/command/3537.md+28 test/command/3558.md+6−2 test/command/3577.md+2 test/command/3755.md+23 test/command/3773.md+14 test/command/3779.md+28 test/command/3794.md+7 test/command/934.md+12 test/command/982.md+11 test/command/write18.md+14 test/latex-reader.latex−1 test/latex-reader.native+1−1 test/lua/hello-world-doc.lua+2−2 test/lua/implicit-doc-filter.lua+1−1 test/lua/single-to-double-quoted.lua+4−2 test/markdown-reader-more.native+2−2 test/test-pandoc.hs+2 test/vimwiki-reader.native+309 test/vimwiki-reader.wiki+424 test/writer.jats+1−1 test/writer.muse+3−3 trypandoc/Makefile+1−1 trypandoc/index.html+2 trypandoc/trypandoc.hs+8−5
COPYRIGHT+47−4
@@ -1,7 +1,8 @@
11Pandoc
22Copyright (C) 2006-2017 John MacFarlane <jgm at berkeley dot edu>
33
4−This code is released under the [GPL], version 2 or later:
4+With the exceptions noted below, this code is released under the [GPL],
5+version 2 or later:
56
67 This program is free software; you can redistribute it and/or modify
78 it under the terms of the GNU General Public License as published by
@@ -27,9 +28,51 @@ Pandoc's complete source code is available from the [Pandoc home page].
2728
2829[Pandoc home page]: http://pandoc.org
2930
30−Pandoc includes some code from other authors. The copyright and license
31−statements for these sources are included below. All are GPL-compatible
32−licenses.
31+Pandoc includes some code with different copyrights, or subject to different
32+licenses. The copyright and license statements for these sources are included
33+below. All are GPL-compatible licenses.
34+
35+----------------------------------------------------------------------
36+The modules in the `pandoc-types` repository (Text.Pandoc.Definition,
37+Text.Pandoc.Builder, Text.Pandoc.Generics, Text.Pandoc.JSON,
38+Text.Pandoc.Walk) are licensed under the BSD 3-clause license:
39+
40+Copyright (c) 2006-2017, John MacFarlane
41+
42+All rights reserved.
43+
44+Redistribution and use in source and binary forms, with or without
45+modification, are permitted provided that the following conditions are met:
46+
47+ * Redistributions of source code must retain the above copyright
48+ notice, this list of conditions and the following disclaimer.
49+
50+ * Redistributions in binary form must reproduce the above
51+ copyright notice, this list of conditions and the following
52+ disclaimer in the documentation and/or other materials provided
53+ with the distribution.
54+
55+ * Neither the name of John MacFarlane nor the names of other
56+ contributors may be used to endorse or promote products derived
57+ from this software without specific prior written permission.
58+
59+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
60+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
61+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
62+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
63+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
64+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
65+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
66+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
67+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
68+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
69+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
70+
71+----------------------------------------------------------------------
72+Pandoc's templates (in `data/templates`) are dual-licensed GPL (v2 or
73+higher, same as pandoc) and the BSD 3-clause license.
74+
75+Copyright (c) 2014--2017, John MacFarlane
3376
3477----------------------------------------------------------------------
3578src/Text/Pandoc/Writers/Muse.hs
INSTALL.md+10−2
@@ -272,9 +272,17 @@ To run with cabal, `cabal test`; to run with stack, `stack
272272test`.
273273
274274To run particular tests (pattern-matching on their names), use
275−the `-t` option:
275+the `-p` option:
276276
277− cabal test --test-options='-t markdown'
277+ cabal test --test-options='-p markdown'
278+
279+Or with stack:
280+
281+ stack test --test-arguments='-p markdown'
282+
283+It is often helpful to add `-j4` (run tests in parallel)
284+and `--hide-successes` (don't clutter output with successes)
285+to the test arguments as well.
278286
279287If you add a new feature to pandoc, please add tests as well, following
280288the pattern of the existing tests. The test suite code is in
MANUAL.txt+78−28
@@ -15,18 +15,20 @@ another, and a command-line tool that uses this library. It can read
1515[Markdown], [CommonMark], [PHP Markdown Extra], [GitHub-Flavored
1616Markdown], [MultiMarkdown], and (subsets of) [Textile],
1717[reStructuredText], [HTML], [LaTeX], [MediaWiki markup], [TWiki markup],
18−[Haddock markup], [OPML], [Emacs Org mode], [DocBook], [txt2tags],
19−[EPUB], [ODT] and [Word docx]; and it can write plain text, [Markdown],
20−[CommonMark], [PHP Markdown Extra], [GitHub-Flavored Markdown],
21−[MultiMarkdown], [reStructuredText], [XHTML], [HTML5], [LaTeX]
22−\(including [`beamer`] slide shows\), [ConTeXt], [RTF], [OPML],
23−[DocBook], [OpenDocument], [ODT], [Word docx], [GNU Texinfo], [MediaWiki
24−markup], [DokuWiki markup], [ZimWiki markup], [Haddock markup], [EPUB]
25−\(v2 or v3\), [FictionBook2], [Textile], [groff man], [groff ms], [Emacs
26−Org mode], [AsciiDoc], [InDesign ICML], [TEI Simple], and [Slidy],
27−[Slideous], [DZSlides], [reveal.js] or [S5] HTML slide shows. It can
28−also produce [PDF] output on systems where LaTeX, ConTeXt, `pdfroff`, or
29−`wkhtmltopdf` is installed.
18+[Haddock markup], [OPML], [Emacs Org mode], [DocBook], [Muse], [txt2tags],
19+[Vimwiki], [EPUB], [ODT], and [Word docx]; and it can
20+write plain text, [Markdown], [CommonMark], [PHP Markdown
21+Extra], [GitHub-Flavored Markdown], [MultiMarkdown],
22+[reStructuredText], [XHTML], [HTML5], [LaTeX] \(including
23+[`beamer`] slide shows\), [ConTeXt], [RTF], [OPML], [DocBook],
24+[OpenDocument], [ODT], [Word docx], [GNU Texinfo], [MediaWiki
25+markup], [DokuWiki markup], [ZimWiki markup], [Haddock markup],
26+[EPUB] \(v2 or v3\), [FictionBook2], [Textile], [groff man],
27+[groff ms], [Emacs Org mode], [AsciiDoc], [InDesign ICML], [TEI
28+Simple], [Muse] and [Slidy], [Slideous], [DZSlides], [reveal.js]
29+or [S5] HTML slide shows. It can also produce [PDF] output on
30+systems where LaTeX, ConTeXt, `pdfroff`, or `wkhtmltopdf` is
31+installed.
3032
3133Pandoc's enhanced version of Markdown includes syntax for [footnotes],
3234[tables], flexible [ordered lists], [definition lists], [fenced code
@@ -97,6 +99,8 @@ Markdown can be expected to be lossy.
9799[FictionBook2]: http://www.fictionbook.org/index.php/Eng:XML_Schema_Fictionbook_2.1
98100[InDesign ICML]: https://www.adobe.com/content/dam/Adobe/en/devnet/indesign/cs55-docs/IDML/idml-specification.pdf
99101[TEI Simple]: https://github.com/TEIC/TEI-Simple
102+[Muse]: https://amusewiki.org/library/manual
103+[Vimwiki]: https://vimwiki.github.io
100104
101105Using `pandoc`
102106--------------
@@ -627,10 +631,10 @@ General writer options
627631`--toc`, `--table-of-contents`
628632
629633: Include an automatically generated table of contents (or, in
630− the case of `latex`, `context`, `docx`, `rst`, or `ms`,
631− an instruction to create one) in the output document. This
632− option has no effect on `man`, `docbook4`, `docbook5`, `jats`,
633− `slidy`, `slideous`, `s5`, or `odt` output.
634+ the case of `latex`, `context`, `docx`, `odt`,
635+ `opendocument`, `rst`, or `ms`, an instruction to create
636+ one) in the output document. This option has no effect on
637+ `man`, `docbook4`, `docbook5`, or `jats` output.
634638
635639`--toc-depth=`*NUMBER*
636640
@@ -973,6 +977,12 @@ Options affecting specific writers
973977 documents with few level 1 headers, one might want to use a chapter
974978 level of 2 or 3.
975979
980+`--epub-subdirectory=`*DIRNAME*
981+
982+: Specify the subdirectory in the OCF container that is to hold
983+ the EPUB-specific contents. The default is `EPUB`. To put
984+ the EPUB contents in the top level, use an empty string.
985+
976986`--latex-engine=pdflatex`|`lualatex`|`xelatex`
977987
978988: Use the specified LaTeX engine when producing PDF output.
@@ -1218,7 +1228,8 @@ as the following:
12181228: non-null value if `--toc/--table-of-contents` was specified
12191229
12201230`toc-title`
1221−: title of table of contents (works only with EPUB and docx)
1231+: title of table of contents (works only with EPUB,
1232+ opendocument, odt, docx)
12221233
12231234`include-before`
12241235: contents specified by `-B/--include-before-body` (may have
@@ -1451,6 +1462,9 @@ LaTeX variables are used when [creating a PDF].
14511462`biblatexoptions`
14521463: list of options for biblatex.
14531464
1465+`natbiboptions`
1466+: list of options for natbib.
1467+
14541468[`article`]: https://ctan.org/pkg/article
14551469[`report`]: https://ctan.org/pkg/report
14561470[`book`]: https://ctan.org/pkg/book
@@ -3023,9 +3037,6 @@ For the most part this should give the same output as `raw_html`,
30233037but it makes it easier to write pandoc filters to manipulate groups
30243038of inlines.
30253039
3026−Raw TeX
3027−-------
3028−
30293040#### Extension: `raw_tex` ####
30303041
30313042In addition to raw HTML, pandoc allows raw LaTeX, TeX, and ConTeXt to be
@@ -3050,6 +3061,34 @@ LaTeX, not as Markdown.
30503061Inline LaTeX is ignored in output formats other than Markdown, LaTeX,
30513062Emacs Org mode, and ConTeXt.
30523063
3064+### Generic raw attribute ###
3065+
3066+#### Extension: `raw_attribute` ####
3067+
3068+Inline spans and fenced code blocks with a special
3069+kind of attribute will be parsed as raw content with the
3070+designated format. For example, the following produces a raw
3071+groff `ms` block:
3072+
3073+ ```{=ms}
3074+ .MYMACRO
3075+ blah blah
3076+ ```
3077+And the following produces a raw `html` inline element:
3078+
3079+ This is `<a>html</a>`{=html}
3080+
3081+The format name should match the target format name (see
3082+`-t/--to`, above, for a list, or use `pandoc
3083+--list-output-formats`).
3084+
3085+This extension presupposes that the relevant kind of
3086+inline code or fenced code block is enabled. Thus, for
3087+example, to use a raw attribute with a backtick code block,
3088+`backtick_code_blocks` must be enabled.
3089+
3090+The raw attribute cannot be combined with regular attributes.
3091+
30533092LaTeX macros
30543093------------
30553094
@@ -3191,17 +3230,16 @@ The link text will be used as the image's alt text:
31913230
31923231#### Extension: `implicit_figures` ####
31933232
3194−An image occurring by itself in a paragraph will be rendered as
3195−a figure with a caption.[^5] (In LaTeX, a figure environment will be
3196−used; in HTML, the image will be placed in a `div` with class
3197−`figure`, together with a caption in a `p` with class `caption`.)
3198−The image's alt text will be used as the caption.
3233+An image with nonempty alt text, occurring by itself in a
3234+paragraph, will be rendered as a figure with a caption. The
3235+image's alt text will be used as the caption.
31993236
32003237 ![This is the caption](/url/of/image.png)
32013238
3202−[^5]: This feature is not yet implemented for RTF, OpenDocument, or
3203− ODT. In those formats, you'll just get an image in a paragraph by
3204− itself, with no caption.
3239+How this is rendered depends on the output format. Some output
3240+formats (e.g. RTF) do not yet support figures. In those
3241+formats, you'll just get an image in a paragraph by itself, with
3242+no caption.
32053243
32063244If you just want a regular inline image, just make sure it is not
32073245the only thing in the paragraph. One way to do this is to insert a
@@ -3963,6 +4001,8 @@ block][Extension: `yaml_metadata_block`]. Here is an example:
39634001 text: doi:10.234234.234/33
39644002 publisher: My Press
39654003 rights: © 2007 John Smith, CC BY-NC
4004+ ibooks:
4005+ version: 1.3.4
39664006 ...
39674007
39684008The following fields are recognized:
@@ -4028,6 +4068,16 @@ The following fields are recognized:
40284068 ~ Either `ltr` or `rtl`. Specifies the `page-progression-direction`
40294069 attribute for the [`spine` element].
40304070
4071+`ibooks`
4072+ ~ iBooks-specific metadata, with the following fields:
4073+
4074+ - `version`: (string)
4075+ - `specified-fonts`: `true`|`false` (default `false`)
4076+ - `ipad-orientation-lock`: `portrait-only`|`landscape-only`
4077+ - `iphone-orientation-lock`: `portrait-only`|`landscape-only`
4078+ - `binding`: `true`|`false` (default `true`)
4079+ - `scroll-axis`: `vertical`|`horizontal`|`default`
4080+
40314081[MARC relators]: http://loc.gov/marc/relators/relaterm.html
40324082[`spine` element]: http://idpf.org/epub/301/spec/epub-publications.html#sec-spine-elem
40334083
Makefile+5−3
@@ -7,7 +7,9 @@ quick:
77 stack install --flag 'pandoc:embed_data_files' --fast --test --test-arguments='-j4 --hide-successes $(TESTARGS)'
88
99full:
10− stack install --flag 'pandoc:embed_data_files' --test --test-arguments='-j4' --pedantic
10+ stack install --flag 'pandoc:embed_data_files' --flag 'pandoc:weigh-pandoc' --flag 'pandoc:trypandoc' --bench --no-run-benchmarks --test --test-arguments='-j4 --hide-successes' --ghc-options '-Wall -Werror -fno-warn-unused-do-bind -O0 -j4'
11+
12+haddock:
1113 stack haddock
1214
1315# Note: to accept current results of golden tests,
@@ -16,7 +18,7 @@ test:
1618 stack test --flag 'pandoc:embed_data_files' --fast --test-arguments='-j4 --hide-successes $(TESTARGS)'
1719
1820bench:
19− stack bench
21+ stack bench --benchmark-arguments='$(BENCHARGS)'
2022
2123weigh:
2224 stack build --flag 'pandoc:weigh-pandoc' && stack exec weigh-pandoc
@@ -65,4 +67,4 @@ download_stats:
6567clean:
6668 stack clean
6769
68−.PHONY: deps quick full install clean test bench changes_github macospkg dist prof download_stats reformat lint weigh
70+.PHONY: deps quick full haddock install clean test bench changes_github macospkg dist prof download_stats reformat lint weigh
README.md+23−18
@@ -16,28 +16,32 @@ The universal markup converter
1616
1717Pandoc is a [Haskell] library for converting from one markup format to
1818another, and a command-line tool that uses this library. It can read
19−[Markdown], [CommonMark], [PHP Markdown Extra], [GitHub-Flavored Markdown],
20−[MultiMarkdown], and (subsets of) [Textile], [reStructuredText], [HTML],
21−[LaTeX], [MediaWiki markup], [TWiki markup], [Haddock markup], [OPML], [Emacs
22−Org mode], [DocBook], [txt2tags], [EPUB], [ODT] and [Word docx]; and it can
23−write plain text, [Markdown], [CommonMark], [PHP Markdown Extra],
24−[GitHub-Flavored Markdown], [MultiMarkdown], [reStructuredText], [XHTML],
25−[HTML5], [LaTeX] \(including [`beamer`] slide shows\), [ConTeXt], [RTF], [OPML],
26−[DocBook], [OpenDocument], [ODT], [Word docx], [GNU Texinfo], [MediaWiki
19+[Markdown], [CommonMark], [PHP Markdown Extra], [GitHub-Flavored
20+Markdown], [MultiMarkdown], and (subsets of) [Textile],
21+[reStructuredText], [HTML], [LaTeX], [MediaWiki markup], [TWiki markup],
22+[Haddock markup], [OPML], [Emacs Org mode], [DocBook], [Muse], [txt2tags],
23+[Vimwiki], [EPUB], [ODT], and [Word docx]; and it can
24+write plain text, [Markdown], [CommonMark], [PHP Markdown
25+Extra], [GitHub-Flavored Markdown], [MultiMarkdown],
26+[reStructuredText], [XHTML], [HTML5], [LaTeX] \(including
27+[`beamer`] slide shows\), [ConTeXt], [RTF], [OPML], [DocBook],
28+[OpenDocument], [ODT], [Word docx], [GNU Texinfo], [MediaWiki
2729markup], [DokuWiki markup], [ZimWiki markup], [Haddock markup],
28−[EPUB] \(v2 or v3\), [FictionBook2], [Textile], [groff man] pages,
29−[Emacs Org mode], [AsciiDoc], [InDesign ICML], [TEI Simple], [Muse], and [Slidy],
30−[Slideous], [DZSlides], [reveal.js] or [S5] HTML slide shows. It can also
31−produce [PDF] output on systems where LaTeX, ConTeXt, or `wkhtmltopdf` is
30+[EPUB] \(v2 or v3\), [FictionBook2], [Textile], [groff man],
31+[groff ms], [Emacs Org mode], [AsciiDoc], [InDesign ICML], [TEI
32+Simple], [Muse] and [Slidy], [Slideous], [DZSlides], [reveal.js]
33+or [S5] HTML slide shows. It can also produce [PDF] output on
34+systems where LaTeX, ConTeXt, `pdfroff`, or `wkhtmltopdf` is
3235installed.
3336
3437Pandoc's enhanced version of Markdown includes syntax for [footnotes],
35−[tables], flexible [ordered lists], [definition lists], [fenced code blocks],
36−[superscripts and subscripts], [strikeout], [metadata blocks], automatic tables of
37−contents, embedded LaTeX [math], [citations], and [Markdown inside HTML block
38−elements]. (These enhancements, described
39−further under [Pandoc's Markdown], can be disabled using the
40−`markdown_strict` input or output format.)
38+[tables], flexible [ordered lists], [definition lists], [fenced code
39+blocks], [superscripts and subscripts], [strikeout], [metadata blocks],
40+automatic tables of contents, embedded LaTeX [math], [citations], and
41+[Markdown inside HTML block elements][Extension:
42+`markdown_in_html_blocks`]. (These enhancements, described further under
43+[Pandoc's Markdown], can be disabled using the `markdown_strict` input
44+or output format.)
4145
4246In contrast to most existing tools for converting Markdown to HTML, which
4347use regex substitutions, pandoc has a modular design: it consists of a
@@ -98,6 +102,7 @@ Markdown can be expected to be lossy.
98102[InDesign ICML]: https://www.adobe.com/content/dam/Adobe/en/devnet/indesign/cs55-docs/IDML/idml-specification.pdf
99103[TEI Simple]: https://github.com/TEIC/TEI-Simple
100104[Muse]: https://amusewiki.org/library/manual
105+[Vimwiki]: https://vimwiki.github.io
101106
102107
103108
benchmark/benchmark-pandoc.hs+1−1
@@ -16,7 +16,7 @@ along with this program; if not, write to the Free Software
1616Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1717-}
1818import Text.Pandoc
19−import Text.Pandoc.Class hiding (getCurrentTime)
19+import Text.Pandoc.Class hiding (getCurrentTime, trace)
2020import qualified Text.Pandoc.UTF8 as UTF8
2121import Data.Text (Text)
2222import Data.Time (getCurrentTime)
data/docx/word/numbering.xml+3−2
@@ -1,2 +1,3 @@
1−<?xml version="1.0" encoding="UTF-8"?>
2−<w:numbering xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"><w:abstractNum w:abstractNumId="0"><w:nsid w:val="e17f69ba" /><w:multiLevelType w:val="multilevel" /><w:lvl w:ilvl="0"><w:numFmt w:val="bullet" /><w:lvlText w:val=" " /><w:lvlJc w:val="left" /><w:pPr><w:tabs><w:tab w:val="num" w:pos="0" /></w:tabs><w:ind w:left="480" w:hanging="480" /></w:pPr></w:lvl><w:lvl w:ilvl="1"><w:numFmt w:val="bullet" /><w:lvlText w:val=" " /><w:lvlJc w:val="left" /><w:pPr><w:tabs><w:tab w:val="num" w:pos="720" /></w:tabs><w:ind w:left="1200" w:hanging="480" /></w:pPr></w:lvl><w:lvl w:ilvl="2"><w:numFmt w:val="bullet" /><w:lvlText w:val=" " /><w:lvlJc w:val="left" /><w:pPr><w:tabs><w:tab w:val="num" w:pos="1440" /></w:tabs><w:ind w:left="1920" w:hanging="480" /></w:pPr></w:lvl><w:lvl w:ilvl="3"><w:numFmt w:val="bullet" /><w:lvlText w:val=" " /><w:lvlJc w:val="left" /><w:pPr><w:tabs><w:tab w:val="num" w:pos="2160" /></w:tabs><w:ind w:left="2640" w:hanging="480" /></w:pPr></w:lvl><w:lvl w:ilvl="4"><w:numFmt w:val="bullet" /><w:lvlText w:val=" " /><w:lvlJc w:val="left" /><w:pPr><w:tabs><w:tab w:val="num" w:pos="2880" /></w:tabs><w:ind w:left="3360" w:hanging="480" /></w:pPr></w:lvl><w:lvl w:ilvl="5"><w:numFmt w:val="bullet" /><w:lvlText w:val=" " /><w:lvlJc w:val="left" /><w:pPr><w:tabs><w:tab w:val="num" w:pos="3600" /></w:tabs><w:ind w:left="4080" w:hanging="480" /></w:pPr></w:lvl><w:lvl w:ilvl="6"><w:numFmt w:val="bullet" /><w:lvlText w:val=" " /><w:lvlJc w:val="left" /><w:pPr><w:tabs><w:tab w:val="num" w:pos="4320" /></w:tabs><w:ind w:left="4800" w:hanging="480" /></w:pPr></w:lvl></w:abstractNum><w:num w:numId="1"><w:abstractNumId w:val="0" /></w:num></w:numbering>
1+<?xml version="1.0" encoding="utf-8"?>
2+<w:numbering xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
3+</w:numbering>
data/pandoc.lua+12−12
@@ -23,7 +23,7 @@ THIS SOFTWARE.
2323-- @copyright © 2017 Albert Krewinkel
2424-- @license MIT
2525local M = {
26− _VERSION = "0.2.0"
26+ _VERSION = "0.3.0"
2727}
2828
2929------------------------------------------------------------------------
@@ -137,7 +137,7 @@ end
137137-- @function Doc
138138-- @tparam {Block,...} blocks document content
139139-- @tparam[opt] Meta meta document meta data
140−function M.Doc(blocks, meta)
140+function M.Pandoc(blocks, meta)
141141 meta = meta or {}
142142 return {
143143 ["blocks"] = blocks,
@@ -146,6 +146,8 @@ function M.Doc(blocks, meta)
146146 }
147147end
148148
149+-- DEPRECATED synonym:
150+M.Doc = M.Pandoc
149151
150152------------------------------------------------------------------------
151153-- MetaValue
@@ -360,7 +362,8 @@ M.Table = M.Block:create_constructor(
360362 "Table",
361363 function(caption, aligns, widths, headers, rows)
362364 return {c = {caption, aligns, widths, headers, rows}}
363− end
365+ end,
366+ {"caption", "aligns", "widths", "headers", "rows"}
364367)
365368
366369
@@ -448,8 +451,7 @@ M.Link = M.Inline:create_constructor(
448451 {"attributes", "content", {"target", "title"}}
449452)
450453
451−--- Creates a Math element, either inline or displayed. It is usually simpler to
452−-- use one of the specialized functions @{InlineMath} or @{DisplayMath} instead.
454+--- Creates a Math element, either inline or displayed.
453455-- @function Math
454456-- @tparam "InlineMath"|"DisplayMath" mathtype rendering specifier
455457-- @tparam string text Math content
@@ -461,7 +463,7 @@ M.Math = M.Inline:create_constructor(
461463 end,
462464 {"mathtype", "text"}
463465)
464−--- Creates a DisplayMath element.
466+--- Creates a DisplayMath element (DEPRECATED).
465467-- @function DisplayMath
466468-- @tparam string text Math content
467469-- @treturn Inline Math element
@@ -470,7 +472,7 @@ M.DisplayMath = M.Inline:create_constructor(
470472 function(text) return M.Math("DisplayMath", text) end,
471473 {"mathtype", "text"}
472474)
473−--- Creates an InlineMath inline element.
475+--- Creates an InlineMath inline element (DEPRECATED).
474476-- @function InlineMath
475477-- @tparam string text Math content
476478-- @treturn Inline Math element
@@ -489,9 +491,7 @@ M.Note = M.Inline:create_constructor(
489491 "content"
490492)
491493
492−--- Creates a Quoted inline element given the quote type and quoted content. It
493−-- is usually simpler to use one of the specialized functions @{SingleQuoted} or
494−-- @{DoubleQuoted} instead.
494+--- Creates a Quoted inline element given the quote type and quoted content.
495495-- @function Quoted
496496-- @tparam "DoubleQuote"|"SingleQuote" quotetype type of quotes to be used
497497-- @tparam {Inline,..} content inline content
@@ -501,7 +501,7 @@ M.Quoted = M.Inline:create_constructor(
501501 function(quotetype, content) return {c = {quotetype, content}} end,
502502 {"quotetype", "content"}
503503)
504−--- Creates a single-quoted inline element.
504+--- Creates a single-quoted inline element (DEPRECATED).
505505-- @function SingleQuoted
506506-- @tparam {Inline,..} content inline content
507507-- @treturn Inline quoted element
@@ -511,7 +511,7 @@ M.SingleQuoted = M.Inline:create_constructor(
511511 function(content) return M.Quoted(M.SingleQuote, content) end,
512512 {"quotetype", "content"}
513513)
514−--- Creates a single-quoted inline element.
514+--- Creates a single-quoted inline element (DEPRECATED).
515515-- @function DoubleQuoted
516516-- @tparam {Inline,..} content inline content
517517-- @treturn Inline quoted element
data/templates/default.commonmark+1−1
@@ -11,7 +11,7 @@ $include-before$
1111
1212$endfor$
1313$if(toc)$
14−$toc$
14+$table-of-contents$
1515
1616$endif$
1717$body$
data/templates/default.dzslides+1−1
@@ -186,7 +186,7 @@ $endif$
186186$endif$
187187$if(toc)$
188188<section id="$idprefix$TOC">
189−$toc$
189+$table-of-contents$
190190</section>
191191$endif$
192192$for(include-before)$
data/templates/default.html4+1−1
@@ -57,7 +57,7 @@ $endif$
5757$endif$
5858$if(toc)$
5959<div id="$idprefix$TOC">
60−$toc$
60+$table-of-contents$
6161</div>
6262$endif$
6363$body$
data/templates/default.html5+1−1
@@ -60,7 +60,7 @@ $endif$
6060$endif$
6161$if(toc)$
6262<nav id="$idprefix$TOC">
63−$toc$
63+$table-of-contents$
6464</nav>
6565$endif$
6666$body$
data/templates/default.latex+2−2
@@ -113,7 +113,7 @@ $if(geometry)$
113113\usepackage[$for(geometry)$$geometry$$sep$,$endfor$]{geometry}
114114$endif$
115115$if(natbib)$
116−\usepackage{natbib}
116+\usepackage[$natbiboptions$]{natbib}
117117\bibliographystyle{$if(biblio-style)$$biblio-style$$else$plainnat$endif$}
118118$endif$
119119$if(biblatex)$
@@ -135,7 +135,7 @@ $endif$
135135$if(tables)$
136136\usepackage{longtable,booktabs}
137137% Fix footnotes in tables (requires footnote package)
138−\IfFileExists{footnote.sty}{\usepackage{footnote}\makesavenoteenv{long table}}{}
138+\IfFileExists{footnote.sty}{\usepackage{footnote}\makesavenoteenv{longtable}}{}
139139$endif$
140140$if(graphics)$
141141\usepackage{graphicx,grffile}
data/templates/default.markdown+1−1
@@ -11,7 +11,7 @@ $include-before$
1111
1212$endfor$
1313$if(toc)$
14−$toc$
14+$table-of-contents$
1515
1616$endif$
1717$body$
data/templates/default.opendocument+129
@@ -23,6 +23,135 @@ $endif$
2323$for(include-before)$
2424$include-before$
2525$endfor$
26+$if(toc)$
27+<text:table-of-content>
28+ <text:table-of-content-source text:outline-level="10">
29+ <text:index-title-template text:style-name="Contents_20_Heading">
30+ $toc-title$
31+ </text:index-title-template>
32+ <text:table-of-content-entry-template text:outline-level="1"
33+ text:style-name="Contents_20_1">
34+ <text:index-entry-link-start text:style-name="Internet_20_link" />
35+ <text:index-entry-chapter />
36+ <text:index-entry-text />
37+ <text:index-entry-link-end />
38+ <text:index-entry-tab-stop style:type="right"
39+ style:leader-char="." />
40+ <text:index-entry-link-start text:style-name="Internet_20_link" />
41+ <text:index-entry-page-number />
42+ <text:index-entry-link-end />
43+ </text:table-of-content-entry-template>
44+ <text:table-of-content-entry-template text:outline-level="2"
45+ text:style-name="Contents_20_2">
46+ <text:index-entry-link-start text:style-name="Internet_20_link" />
47+ <text:index-entry-chapter />
48+ <text:index-entry-text />
49+ <text:index-entry-link-end />
50+ <text:index-entry-tab-stop style:type="right"
51+ style:leader-char="." />
52+ <text:index-entry-link-start text:style-name="Internet_20_link" />
53+ <text:index-entry-page-number />
54+ <text:index-entry-link-end />
55+ </text:table-of-content-entry-template>
56+ <text:table-of-content-entry-template text:outline-level="3"
57+ text:style-name="Contents_20_3">
58+ <text:index-entry-link-start text:style-name="Internet_20_link" />
59+ <text:index-entry-chapter />
60+ <text:index-entry-text />
61+ <text:index-entry-link-end />
62+ <text:index-entry-tab-stop style:type="right"
63+ style:leader-char="." />
64+ <text:index-entry-link-start text:style-name="Internet_20_link" />
65+ <text:index-entry-page-number />
66+ <text:index-entry-link-end />
67+ </text:table-of-content-entry-template>
68+ <text:table-of-content-entry-template text:outline-level="4"
69+ text:style-name="Contents_20_4">
70+ <text:index-entry-link-start text:style-name="Internet_20_link" />
71+ <text:index-entry-chapter />
72+ <text:index-entry-text />
73+ <text:index-entry-link-end />
74+ <text:index-entry-tab-stop style:type="right"
75+ style:leader-char="." />
76+ <text:index-entry-link-start text:style-name="Internet_20_link" />
77+ <text:index-entry-page-number />
78+ <text:index-entry-link-end />
79+ </text:table-of-content-entry-template>
80+ <text:table-of-content-entry-template text:outline-level="5"
81+ text:style-name="Contents_20_5">
82+ <text:index-entry-link-start text:style-name="Internet_20_link" />
83+ <text:index-entry-chapter />
84+ <text:index-entry-text />
85+ <text:index-entry-link-end />
86+ <text:index-entry-tab-stop style:type="right"
87+ style:leader-char="." />
88+ <text:index-entry-link-start text:style-name="Internet_20_link" />
89+ <text:index-entry-page-number />
90+ <text:index-entry-link-end />
91+ </text:table-of-content-entry-template>
92+ <text:table-of-content-entry-template text:outline-level="6"
93+ text:style-name="Contents_20_6">
94+ <text:index-entry-link-start text:style-name="Internet_20_link" />
95+ <text:index-entry-chapter />
96+ <text:index-entry-text />
97+ <text:index-entry-link-end />
98+ <text:index-entry-tab-stop style:type="right"
99+ style:leader-char="." />
100+ <text:index-entry-link-start text:style-name="Internet_20_link" />
101+ <text:index-entry-page-number />
102+ <text:index-entry-link-end />
103+ </text:table-of-content-entry-template>
104+ <text:table-of-content-entry-template text:outline-level="7"
105+ text:style-name="Contents_20_7">
106+ <text:index-entry-link-start text:style-name="Internet_20_link" />
107+ <text:index-entry-chapter />
108+ <text:index-entry-text />
109+ <text:index-entry-link-end />
110+ <text:index-entry-tab-stop style:type="right"
111+ style:leader-char="." />
112+ <text:index-entry-link-start text:style-name="Internet_20_link" />
113+ <text:index-entry-page-number />
114+ <text:index-entry-link-end />
115+ </text:table-of-content-entry-template>
116+ <text:table-of-content-entry-template text:outline-level="8"
117+ text:style-name="Contents_20_8">
118+ <text:index-entry-link-start text:style-name="Internet_20_link" />
119+ <text:index-entry-chapter />
120+ <text:index-entry-text />
121+ <text:index-entry-link-end />
122+ <text:index-entry-tab-stop style:type="right"
123+ style:leader-char="." />
124+ <text:index-entry-link-start text:style-name="Internet_20_link" />
125+ <text:index-entry-page-number />
126+ <text:index-entry-link-end />
127+ </text:table-of-content-entry-template>
128+ <text:table-of-content-entry-template text:outline-level="9"
129+ text:style-name="Contents_20_9">
130+ <text:index-entry-link-start text:style-name="Internet_20_link" />
131+ <text:index-entry-chapter />
132+ <text:index-entry-text />
133+ <text:index-entry-link-end />
134+ <text:index-entry-tab-stop style:type="right"
135+ style:leader-char="." />
136+ <text:index-entry-link-start text:style-name="Internet_20_link" />
137+ <text:index-entry-page-number />
138+ <text:index-entry-link-end />
139+ </text:table-of-content-entry-template>
140+ <text:table-of-content-entry-template text:outline-level="10"
141+ text:style-name="Contents_20_10">
142+ <text:index-entry-link-start text:style-name="Internet_20_link" />
143+ <text:index-entry-chapter />
144+ <text:index-entry-text />
145+ <text:index-entry-link-end />
146+ <text:index-entry-tab-stop style:type="right"
147+ style:leader-char="." />
148+ <text:index-entry-link-start text:style-name="Internet_20_link" />
149+ <text:index-entry-page-number />
150+ <text:index-entry-link-end />
151+ </text:table-of-content-entry-template>
152+ </text:table-of-content-source>
153+</text:table-of-content>
154+$endif$
26155$body$
27156$for(include-after)$
28157$include-after$
data/templates/default.plain+1−1
@@ -11,7 +11,7 @@ $include-before$
1111
1212$endfor$
1313$if(toc)$
14−$toc$
14+$table-of-contents$
1515
1616$endif$
1717$body$
data/templates/default.revealjs+12−3
@@ -79,7 +79,7 @@ $endif$
7979$endif$
8080$if(toc)$
8181<section id="$idprefix$TOC">
82−$toc$
82+$table-of-contents$
8383</section>
8484$endif$
8585
@@ -229,15 +229,24 @@ $endif$
229229$if(maxScale)$
230230 maxScale: $maxScale$,
231231$endif$
232+$if(mathjax)$
233+ math: {
234+ mathjax: 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js',
235+ config: 'TeX-AMS_HTML-full',
236+ },
237+$endif$
232238
233239 // Optional reveal.js plugins
234240 dependencies: [
235241 { src: '$revealjs-url$/lib/js/classList.js', condition: function() { return !document.body.classList; } },
236242 { src: '$revealjs-url$/plugin/zoom-js/zoom.js', async: true },
237− $if(notes-server)$
243+$if(notes-server)$
238244 { src: '$revealjs-url$/socket.io/socker.io.js', async: true },
239245 { src: '$revealjs-url$/plugin/notes-server/client.js', async: true },
240− $endif$
246+$endif$
247+$if(mathjax)$
248+ { src: '$revealjs-url$/plugin/math/math.js', async: true },
249+$endif$
241250 { src: '$revealjs-url$/plugin/notes/notes.js', async: true }
242251 ]
243252 });
data/templates/default.rtf+1−1
@@ -18,7 +18,7 @@ $if(spacer)$
1818{\pard \ql \f0 \sa180 \li0 \fi0 \par}
1919$endif$
2020$if(toc)$
21−$toc$
21+$table-of-contents$
2222$endif$
2323$for(include-before)$
2424$include-before$
data/templates/default.s5+1−1
@@ -78,7 +78,7 @@ $endif$
7878$endif$
7979$if(toc)$
8080<div class="slide" id="$idprefix$TOC">
81−$toc$
81+$table-of-contents$
8282</div>
8383$endif$
8484$body$
data/templates/default.slideous+1−1
@@ -83,7 +83,7 @@ $endif$
8383$endif$
8484$if(toc)$
8585<div class="slide" id="$idprefix$TOC">
86−$toc$
86+$table-of-contents$
8787</div>
8888$endif$
8989$body$
data/templates/default.slidy+1−1
@@ -69,7 +69,7 @@ $endif$
6969$endif$
7070$if(toc)$
7171<div class="slide" id="$idprefix$TOC">
72−$toc$
72+$table-of-contents$
7373</div>
7474$endif$
7575$body$
data/vimwiki.cssnew file+82
@@ -0,0 +1,82 @@
1+pre{
2+ font-size: 1.5em
3+}
4+
5+img {vertical-align: middle}
6+
7+body {font-family: Tahoma, Geneva, sans-serif; margin: 1em 2em 1em 2em; font-size: 120%; line-height: 130%;}
8+h1, h2, h3, h4, h5, h6 {font-family: Trebuchet MS, Helvetica, sans-serif; font-weight: bold; line-height:100%; margin-top: 1.5em; margin-bottom: 0.5em;}
9+h1 {font-size: 2.6em; color: #000000;}
10+h2 {font-size: 2.2em; color: #404040;}
11+h3 {font-size: 1.8em; color: #707070;}
12+h4 {font-size: 1.4em; color: #909090;}
13+h5 {font-size: 1.3em; color: #989898;}
14+h6 {font-size: 1.2em; color: #9c9c9c;}
15+p, pre, blockquote, table, ul, ol, dl {margin-top: 1em; margin-bottom: 1em;}
16+ul ul, ul ol, ol ol, ol ul {margin-top: 0.5em; margin-bottom: 0.5em;}
17+li {margin: 0.3em auto;}
18+ul {margin-left: 2em; padding-left: 0.5em;}
19+dt {font-weight: bold;}
20+img {border: none;}
21+pre {border-left: 1px solid #ccc; margin-left: 2em; padding-left: 0.5em;}
22+blockquote {padding: 0.4em; background-color: #f6f5eb;}
23+th, td {border: 1px solid #ccc; padding: 0.3em;}
24+th {background-color: #f0f0f0;}
25+hr {border: none; border-top: 1px solid #ccc; width: 100%;}
26+del {text-decoration: line-through; color: #777777;}
27+.toc li {list-style-type: none;}
28+.todo {font-weight: bold; background-color: #f0ece8; color: #a03020;}
29+.justleft {text-align: left;}
30+.justright {text-align: right;}
31+.justcenter {text-align: center;}
32+.center {margin-left: auto; margin-right: auto;}
33+div.center > table {margin-left: auto; margin-right: auto;}
34+.tag {background-color: #eeeeee; font-family: monospace; padding: 2px;}
35+
36+/* classes for items of todo lists */
37+.done0 {
38+ /* list-style: none; */
39+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAxQAAAMUBHc26qAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAA7SURBVCiR7dMxEgAgCANBI3yVRzF5KxNbW6wsuH7LQ2YKQK1mkswBVERYF5Os3UV3gwd/jF2SkXy66gAZkxS6BniubAAAAABJRU5ErkJggg==);
40+ background-repeat: no-repeat;
41+ background-position: 0 .2em;
42+ padding-left: 1.5em;
43+}
44+.done1 {
45+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAxQAAAMUBHc26qAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAABtSURBVCiR1ZO7DYAwDER9BDmTeZQMFXmUbGYpOjrEryA0wOvO8itOslFrJYAug5BMM4BeSkmjsrv3aVTa8p48Xw1JSkSsWVUFwD05IqS1tmYzk5zzae9jnVVVzGyXb8sALjse+euRkEzu/uirFomVIdDGOLjuAAAAAElFTkSuQmCC);
46+ background-repeat: no-repeat;
47+ background-position: 0 .15em;
48+ padding-left: 1.5em;
49+}
50+.done2 {
51+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAxQAAAMUBHc26qAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAB1SURBVCiRzdO5DcAgDAVQGxjAYgTvxlDIu1FTIRYAp8qlFISkSH7l5kk+ZIwxKiI2mIyqWoeILYRgZ7GINDOLjnmF3VqklKCUMgTee2DmM661Qs55iI3Zm/1u5h9sm4ig9z4ERHTFzLyd4G4+nFlVrYg8+qoF/c0kdpeMsmcAAAAASUVORK5CYII=);
52+ background-repeat: no-repeat;
53+ background-position: 0 .15em;
54+ padding-left: 1.5em;
55+}
56+.done3 {
57+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAxQAAAMUBHc26qAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAABoSURBVCiR7dOxDcAgDATA/0DtUdiKoZC3YhLkHjkVKF3idJHiztKfvrHZWnOSE8Fx95RJzlprimJVnXktvXeY2S0SEZRSAAAbmxnGGKH2I5T+8VfxPhIReQSuuY3XyYWa3T2p6quvOgGrvSFGlewuUAAAAABJRU5ErkJggg==);
58+ background-repeat: no-repeat;
59+ background-position: 0 .15em;
60+ padding-left: 1.5em;
61+}
62+.done4 {
63+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAAQCAYAAAAbBi9cAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAzgAAAM4BlP6ToAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAIISURBVDiNnZQ9SFtRFMd/773kpTaGJoQk1im4VDpWQcTNODhkFBcVTCNCF0NWyeDiIIiCm82QoIMIUkHUxcFBg1SEQoZszSat6cdTn1qNue92CMbEr9Sey+XC/Z/zu+f8h6ukUil3sVg0+M+4cFxk42/jH2wAqqqKSCSiPQdwcHHAnDHH9s/tN1h8V28ETdP+eU8fT9Nt62ancYdIPvJNtsu87bmjrJlrTDVM4RROJs1JrHPrD4Bar7A6cpc54iKOaTdJXCUI2UMVrQZ0Js7YPN18ECKkYNQcJe/OE/4dZsw7VqNXQMvHy3QZXQypQ6ycrtwDjf8aJ+PNEDSCzLpn7+m2pD8ZKHlKarYhy6XjEoCYGcN95qansQeA3fNdki+SaJZGTMQIOoL3W/Z89rxv+tokubNajlvk/vm+LFpF2XnUKZHI0I+QrI7Dw0OZTqdzUkpsM7mZTyfy5OPGyw1tK7AFSvmB/Ks8w8YwbUYbe6/3QEKv0vugfxWPnMLJun+d/kI/WLdizpNjMbAIKrhMF4OuwadBALqqs+RfInwUvuNi+fBd+wjogfogAFVRmffO02q01mZZ0HHdgXIzdz0QQLPezIQygX6llxNKKgOFARYCC49CqhoHIUTlss/Vx2phlYwjw8j1CAlfAiwQiJpiy7o1VHnsG5FISkoJu7Q/2YmmaV+i0ei7v38L2CBguSi5AAAAAElFTkSuQmCC);
64+ background-repeat: no-repeat;
65+ background-position: 0 .15em;
66+ padding-left: 1.5em;
67+}
68+
69+*:not(pre) > code {
70+ font-family: Monaco,"Courier New","DejaVu Sans Mono","Bitstream Vera Sans Mono",monospace;
71+ -webkit-border-radius: 1px;
72+ -moz-border-radius: 1px;
73+ border-radius: 1px;
74+ -moz-background-clip: padding;
75+ -webkit-background-clip: padding-box;
76+ background-clip: padding-box;
77+ padding: 0px 3px;
78+ display: inline-block;
79+ color: #52595d;
80+ border: 1px solid #ccc;
81+ background-color: #f9f9f9;
82+}
doc/lua-filters.md+37
@@ -107,3 +107,40 @@ return {
107107 end,
108108 }
109109```
110+
111+### Extracting information about links
112+
113+This filter prints a table of all the URLs linked to
114+in the document, together with the number of links to
115+that URL.
116+
117+```lua
118+links = {}
119+
120+function Link (el)
121+ if links[el.target] then
122+ links[el.target] = links[el.target] + 1
123+ else
124+ links[el.target] = 1
125+ end
126+ return el
127+end
128+
129+function Doc (blocks, meta)
130+ function strCell(str)
131+ return {pandoc.Plain{pandoc.Str(str)}}
132+ end
133+ local caption = {pandoc.Str "Link", pandoc.Space(), pandoc.Str "count"}
134+ local aligns = {pandoc.AlignDefault, pandoc.AlignLeft}
135+ local widths = {0.8, 0.2}
136+ local headers = {strCell "Target", strCell "Count"}
137+ local rows = {}
138+ for link, count in pairs(links) do
139+ rows[#rows + 1] = {strCell(link), strCell(count)}
140+ end
141+ return pandoc.Doc(
142+ {pandoc.Table(caption, aligns, widths, headers, rows)},
143+ meta
144+ )
145+end
146+```
linux/control.in+14−13
@@ -9,16 +9,17 @@ Suggests: texlive-latex-recommended, texlive-xetex,
99 texlive-fonts-recommended
1010Maintainer: John MacFarlane <jgm@berkeley.edu>
1111Description: general markup converter
12− Pandoc is a Haskell library for converting from one markup
13− format to another, and a command-line tool that uses
14− this library. It can read several dialects of Markdown and
15− (subsets of) HTML, reStructuredText, LaTeX, DocBook, MediaWiki
16− markup, Word docx, ODT, EPUB, TWiki markup, Haddock markup, OPML,
17− Emacs Org-Mode, txt2tags and Textile, and it can write Markdown,
18− reStructuredText, HTML, LaTeX, ConTeXt, DocBook, OPML, GNU TexInfo,
19− OpenDocument, ODT, Word docx, RTF, MediaWiki, ZimWiki,
20− DokuWiki, Textile, groff man pages, groff ms, plain text,
21− Emacs Org-Mode, AsciiDoc, Haddock markup, EPUB (v2 and v3),
22− FictionBook2, InDesign ICML, TEI Simple, JATS XML, Muse,
23− and several kinds of HTML/javascript slide shows (S5, Slidy,
24− Slideous, DZSlides, reveal.js).
12+ Pandoc is a Haskell library for converting from one markup
13+ format to another, and a command-line tool that uses
14+ this library. It can read several dialects of Markdown and
15+ (subsets of) HTML, reStructuredText, LaTeX, DocBook,
16+ MediaWiki markup, TWiki markup, Haddock markup, OPML,
17+ Emacs Org-Mode, txt2tags, Muse, Vimwiki, Word Docx, ODT,
18+ and Textile, and it can write Markdown, reStructuredText,
19+ XHTML, HTML 5, LaTeX, ConTeXt, DocBook, JATS, OPML, TEI,
20+ OpenDocument, ODT, Word docx, RTF, MediaWiki, DokuWiki,
21+ ZimWiki, Textile, groff man, groff ms, plain text,
22+ Emacs Org-Mode, AsciiDoc, Haddock markup, EPUB (v2 and v3),
23+ FictionBook2, InDesign ICML, Muse, and several kinds of
24+ HTML/javascript slide shows (S5, Slidy, Slideous,
25+ DZSlides, reveal.js).
pandoc.cabal+18−12
@@ -18,13 +18,13 @@ Description: Pandoc is a Haskell library for converting from one markup
1818 this library. It can read several dialects of Markdown and
1919 (subsets of) HTML, reStructuredText, LaTeX, DocBook,
2020 MediaWiki markup, TWiki markup, Haddock markup, OPML,
21− Emacs Org-Mode, txt2tags, Word Docx, ODT, and Textile, and
22− it can write Markdown, reStructuredText, XHTML, HTML 5,
23− LaTeX, ConTeXt, DocBook, JATS, OPML, TEI, OpenDocument, ODT,
24− Word docx, RTF, MediaWiki, DokuWiki, ZimWiki, Textile,
25− groff man, groff ms, plain text, Emacs Org-Mode, AsciiDoc,
26− Haddock markup, EPUB (v2 and v3), FictionBook2,
27− InDesign ICML, Muse, and several kinds of
21+ Emacs Org-Mode, txt2tags, Muse, Vimwiki, Word Docx, ODT,
22+ and Textile, and it can write Markdown, reStructuredText,
23+ XHTML, HTML 5, LaTeX, ConTeXt, DocBook, JATS, OPML, TEI,
24+ OpenDocument, ODT, Word docx, RTF, MediaWiki, DokuWiki,
25+ ZimWiki, Textile, groff man, groff ms, plain text,
26+ Emacs Org-Mode, AsciiDoc, Haddock markup, EPUB (v2 and v3),
27+ FictionBook2, InDesign ICML, Muse, and several kinds of
2828 HTML/javascript slide shows (S5, Slidy, Slideous,
2929 DZSlides, reveal.js).
3030 .
@@ -162,6 +162,7 @@ Extra-Source-Files:
162162 test/markdown-citations.txt
163163 test/textile-reader.textile
164164 test/mediawiki-reader.wiki
165+ test/vimwiki-reader.wiki
165166 test/rst-reader.rst
166167 test/s5-basic.html
167168 test/s5-fancy.html
@@ -287,14 +288,14 @@ Library
287288 parsec >= 3.1 && < 3.2,
288289 mtl >= 2.2 && < 2.3,
289290 filepath >= 1.1 && < 1.5,
290− process >= 1.2.3 && < 1.5,
291+ process >= 1.2.3 && < 1.7,
291292 directory >= 1 && < 1.4,
292293 bytestring >= 0.9 && < 0.11,
293294 text >= 0.11 && < 1.3,
294295 safe >= 0.3 && < 0.4,
295296 zip-archive >= 0.2.3.4 && < 0.4,
296297 HTTP >= 4000.0.5 && < 4000.4,
297− texmath >= 0.9.4 && < 0.10,
298+ texmath >= 0.9.4.1 && < 0.10,
298299 xml >= 1.3.12 && < 1.4,
299300 random >= 1 && < 1.2,
300301 pandoc-types >= 1.17 && < 1.18,
@@ -331,7 +332,7 @@ Library
331332 Build-Depends: old-locale >= 1 && < 1.1,
332333 time >= 1.2 && < 1.5
333334 else
334− Build-Depends: time >= 1.5 && < 1.7
335+ Build-Depends: time >= 1.5 && < 1.9
335336 if flag(network-uri)
336337 Build-Depends: network-uri >= 2.6 && < 2.7, network >= 2.6
337338 else
@@ -365,9 +366,11 @@ Library
365366 Text.Pandoc.Readers,
366367 Text.Pandoc.Readers.HTML,
367368 Text.Pandoc.Readers.LaTeX,
369+ Text.Pandoc.Readers.LaTeX.Types,
368370 Text.Pandoc.Readers.Markdown,
369371 Text.Pandoc.Readers.CommonMark,
370372 Text.Pandoc.Readers.MediaWiki,
373+ Text.Pandoc.Readers.Vimwiki,
371374 Text.Pandoc.Readers.RST,
372375 Text.Pandoc.Readers.Org,
373376 Text.Pandoc.Readers.DocBook,
@@ -380,6 +383,7 @@ Library
380383 Text.Pandoc.Readers.Docx,
381384 Text.Pandoc.Readers.Odt,
382385 Text.Pandoc.Readers.EPUB,
386+ Text.Pandoc.Readers.Muse,
383387 Text.Pandoc.Writers,
384388 Text.Pandoc.Writers.Native,
385389 Text.Pandoc.Writers.Docbook,
@@ -460,6 +464,7 @@ Library
460464 Text.Pandoc.Lua.Util,
461465 Text.Pandoc.CSS,
462466 Text.Pandoc.UUID,
467+ Text.Pandoc.BCP47
463468 Text.Pandoc.Slides,
464469 Text.Pandoc.Compat.Time,
465470 Paths_pandoc
@@ -532,7 +537,7 @@ Test-Suite test-pandoc
532537 directory >= 1 && < 1.4,
533538 filepath >= 1.1 && < 1.5,
534539 hslua >= 0.4 && < 0.6,
535− process >= 1.2.3 && < 1.5,
540+ process >= 1.2.3 && < 1.7,
536541 skylighting >= 0.3.3 && < 0.4,
537542 temporary >= 1.1 && < 1.3,
538543 Diff >= 0.2 && < 0.4,
@@ -540,7 +545,7 @@ Test-Suite test-pandoc
540545 tasty-hunit >= 0.9 && < 0.10,
541546 tasty-quickcheck >= 0.8 && < 0.9,
542547 tasty-golden >= 2.3 && < 2.4,
543− QuickCheck >= 2.4 && < 2.10,
548+ QuickCheck >= 2.4 && < 2.11,
544549 containers >= 0.1 && < 0.6,
545550 executable-path >= 0.0 && < 0.1,
546551 zip-archive >= 0.2.3.4 && < 0.4,
@@ -559,6 +564,7 @@ Test-Suite test-pandoc
559564 Tests.Readers.Odt
560565 Tests.Readers.Txt2Tags
561566 Tests.Readers.EPUB
567+ Tests.Readers.Muse
562568 Tests.Writers.Native
563569 Tests.Writers.ConTeXt
564570 Tests.Writers.Docbook
src/Text/Pandoc.hs+10−8
@@ -39,16 +39,18 @@ inline links:
3939
4040> module Main where
4141> import Text.Pandoc
42+> import Data.Text (Text)
43+> import qualified Data.Text.IO as T
4244>
43−> markdownToRST :: String -> Either PandocError String
44−> markdownToRST =
45−> writeRST def {writerReferenceLinks = True} . readMarkdown def
46−>
47−> main = getContents >>= either error return markdownToRST >>= putStrLn
45+> mdToRST :: Text -> IO Text
46+> mdToRST txt = runIOorExplode $
47+> readMarkdown def txt
48+> >>= writeRST def{ writerReferenceLinks = True }
4849
49−Note: all of the readers assume that the input text has @'\n'@
50−line endings. So if you get your input text from a web form,
51−you should remove @'\r'@ characters using @filter (/='\r')@.
50+>
51+> main :: IO ()
52+> main = do
53+> T.getContents >>= mdToRST >>= T.putStrLn
5254
5355-}
5456
src/Text/Pandoc/App.hs+59−23
@@ -76,14 +76,16 @@ import System.IO.Error (isDoesNotExistError)
7676import Text.Pandoc
7777import Text.Pandoc.Builder (setMeta)
7878import Text.Pandoc.Class (PandocIO, extractMedia, fillMediaBag, getLog,
79− setResourcePath, withMediaBag)
79+ setResourcePath, withMediaBag, setTrace)
8080import Text.Pandoc.Highlighting (highlightingStyles)
81−import Text.Pandoc.Lua (runLuaFilter)
81+import Text.Pandoc.Lua (runLuaFilter, LuaException(..))
82+import Text.Pandoc.Writers.Math (defaultMathJaxURL, defaultKaTeXURL)
8283import Text.Pandoc.PDF (makePDF)
8384import Text.Pandoc.Process (pipeProcess)
8485import Text.Pandoc.SelfContained (makeDataURI, makeSelfContained)
8586import Text.Pandoc.Shared (headerShift, isURI, openURL, readDataFile,
86− readDataFileUTF8, safeRead, tabFilter)
87+ readDataFileUTF8, safeRead, tabFilter,
88+ eastAsianLineBreakFilter)
8789import qualified Text.Pandoc.UTF8 as UTF8
8890import Text.Pandoc.XML (toEntities)
8991import Text.Printf
@@ -133,11 +135,11 @@ convertWithOpts opts = do
133135 Nothing -> return Nothing
134136 Just fp -> Just <$> UTF8.readFile fp
135137
136− let csscdn = "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.css"
137138 let mathMethod =
138139 case (optKaTeXJS opts, optKaTeXStylesheet opts) of
139140 (Nothing, _) -> optHTMLMathMethod opts
140− (Just js, ss) -> KaTeX js (fromMaybe csscdn ss)
141+ (Just js, ss) -> KaTeX js (fromMaybe
142+ (defaultKaTeXURL ++ "katex.min.css") ss)
141143
142144
143145 -- --bibliography implies -F pandoc-citeproc for backwards compatibility:
@@ -181,11 +183,12 @@ convertWithOpts opts = do
181183 let msOutput = format == "ms"
182184
183185 -- disabling the custom writer for now
184− writer <- if ".lua" `isSuffixOf` format
186+ (writer, writerExts) <-
187+ if ".lua" `isSuffixOf` format
185188 -- note: use non-lowercased version writerName
186189 then return (TextWriter
187190 (\o d -> liftIO $ writeCustom writerName o d)
188− :: Writer PandocIO)
191+ :: Writer PandocIO, mempty)
189192 else case getWriter writerName of
190193 Left e -> E.throwIO $ PandocAppError $
191194 if format == "pdf"
@@ -195,12 +198,13 @@ convertWithOpts opts = do
195198 "\nand specify an output file with " ++
196199 ".pdf extension (-o filename.pdf)."
197200 else e
198− Right w -> return (w :: Writer PandocIO)
201+ Right (w, es) -> return (w :: Writer PandocIO, es)
199202
200203 -- TODO: we have to get the input and the output into the state for
201204 -- the sake of the text2tags reader.
202− reader <- case getReader readerName of
203− Right r -> return (r :: Reader PandocIO)
205+ (reader, readerExts) <-
206+ case getReader readerName of
207+ Right (r, es) -> return (r :: Reader PandocIO, es)
204208 Left e -> E.throwIO $ PandocAppError e'
205209 where e' = case readerName of
206210 "pdf" -> e ++
@@ -304,11 +308,11 @@ convertWithOpts opts = do
304308 , readerColumns = optColumns opts
305309 , readerTabStop = optTabStop opts
306310 , readerIndentedCodeClasses = optIndentedCodeClasses opts
307− , readerApplyMacros = not laTeXOutput
308311 , readerDefaultImageExtension =
309312 optDefaultImageExtension opts
310313 , readerTrackChanges = optTrackChanges opts
311314 , readerAbbreviations = abbrevs
315+ , readerExtensions = readerExts
312316 }
313317
314318 highlightStyle <- lookupHighlightStyle $ optHighlightStyle opts
@@ -339,6 +343,7 @@ convertWithOpts opts = do
339343 writerNumberSections = optNumberSections opts,
340344 writerNumberOffset = optNumberOffset opts,
341345 writerSectionDivs = optSectionDivs opts,
346+ writerExtensions = writerExts,
342347 writerReferenceLinks = optReferenceLinks opts,
343348 writerReferenceLocation = optReferenceLocation opts,
344349 writerDpi = optDpi opts,
@@ -354,6 +359,7 @@ convertWithOpts opts = do
354359 writerSlideLevel = optSlideLevel opts,
355360 writerHighlightStyle = highlightStyle,
356361 writerSetextHeaders = optSetextHeaders opts,
362+ writerEpubSubdirectory = optEpubSubdirectory opts,
357363 writerEpubMetadata = epubMetadata,
358364 writerEpubFonts = optEpubFonts opts,
359365 writerEpubChapterLevel = optEpubChapterLevel opts,
@@ -375,13 +381,21 @@ convertWithOpts opts = do
375381 "Specify an output file using the -o option."
376382
377383
378− let transforms = case optBaseHeaderLevel opts of
379− x | x > 1 -> [headerShift (x - 1)]
380− | otherwise -> []
384+ let transforms = (case optBaseHeaderLevel opts of
385+ x | x > 1 -> (headerShift (x - 1) :)
386+ | otherwise -> id) $
387+ (if extensionEnabled Ext_east_asian_line_breaks
388+ readerExts &&
389+ not (extensionEnabled Ext_east_asian_line_breaks
390+ writerExts &&
391+ writerWrapText writerOptions == WrapPreserve)
392+ then (eastAsianLineBreakFilter :)
393+ else id)
394+ []
381395
382396 let convertTabs = tabFilter (if optPreserveTabs opts || readerName == "t2t"
383− then 0
384− else optTabStop opts)
397+ then 0
398+ else optTabStop opts)
385399
386400 readSources :: [FilePath] -> PandocIO Text
387401 readSources srcs = convertTabs . T.intercalate (T.pack "\n") <$>
@@ -390,6 +404,7 @@ convertWithOpts opts = do
390404 let runIO' :: PandocIO a -> IO a
391405 runIO' f = do
392406 (res, reports) <- runIOorExplode $ do
407+ setTrace (optTrace opts)
393408 setVerbosity verbosity
394409 x <- f
395410 rs <- getLog
@@ -518,7 +533,8 @@ externalFilter f args' d = liftIO $ do
518533 (exitcode, outbs) <- E.handle filterException $
519534 pipeProcess env' f' args'' $ encode d
520535 case exitcode of
521− ExitSuccess -> return $ either error id $ eitherDecode' outbs
536+ ExitSuccess -> either (E.throwIO . PandocFilterError f)
537+ return $ eitherDecode' outbs
522538 ExitFailure ec -> E.throwIO $ PandocFilterError f
523539 ("Filter returned error status " ++ show ec)
524540 where filterException :: E.SomeException -> IO a
@@ -550,6 +566,7 @@ data Opt = Opt
550566 , optHTMLMathMethod :: HTMLMathMethod -- ^ Method to print HTML math
551567 , optAbbreviations :: Maybe FilePath -- ^ Path to abbrevs file
552568 , optReferenceDoc :: Maybe FilePath -- ^ Path of reference doc
569+ , optEpubSubdirectory :: String -- ^ EPUB subdir in OCF container
553570 , optEpubMetadata :: Maybe FilePath -- ^ EPUB metadata
554571 , optEpubFonts :: [FilePath] -- ^ EPUB fonts to embed
555572 , optEpubChapterLevel :: Int -- ^ Header level at which to split chapters
@@ -558,6 +575,7 @@ data Opt = Opt
558575 , optDumpArgs :: Bool -- ^ Output command-line arguments
559576 , optIgnoreArgs :: Bool -- ^ Ignore command-line arguments
560577 , optVerbosity :: Verbosity -- ^ Verbosity of diagnostic output
578+ , optTrace :: Bool -- ^ Enable tracing
561579 , optLogFile :: Maybe FilePath -- ^ File to write JSON log output
562580 , optFailIfWarnings :: Bool -- ^ Fail on warnings
563581 , optReferenceLinks :: Bool -- ^ Use reference links in writing markdown, rst
@@ -624,6 +642,7 @@ defaultOpts = Opt
624642 , optHTMLMathMethod = PlainMath
625643 , optAbbreviations = Nothing
626644 , optReferenceDoc = Nothing
645+ , optEpubSubdirectory = "EPUB"
627646 , optEpubMetadata = Nothing
628647 , optEpubFonts = []
629648 , optEpubChapterLevel = 1
@@ -632,6 +651,7 @@ defaultOpts = Opt
632651 , optDumpArgs = False
633652 , optIgnoreArgs = False
634653 , optVerbosity = WARNING
654+ , optTrace = False
635655 , optLogFile = Nothing
636656 , optFailIfWarnings = False
637657 , optReferenceLinks = False
@@ -778,10 +798,16 @@ expandFilterPath mbDatadir fp = liftIO $ do
778798 _ -> return fp
779799
780800applyLuaFilters :: MonadIO m
781− => Maybe FilePath -> [FilePath] -> [String] -> Pandoc -> m Pandoc
801+ => Maybe FilePath -> [FilePath] -> [String] -> Pandoc
802+ -> m Pandoc
782803applyLuaFilters mbDatadir filters args d = do
783804 expandedFilters <- mapM (expandFilterPath mbDatadir) filters
784− foldrM ($) d $ map (flip runLuaFilter args) expandedFilters
805+ let go f d' = liftIO $ do
806+ res <- E.try (runLuaFilter mbDatadir f args d')
807+ case res of
808+ Right x -> return x
809+ Left (LuaException s) -> E.throw (PandocFilterError f s)
810+ foldrM ($) d $ map go expandedFilters
785811
786812applyFilters :: MonadIO m
787813 => Maybe FilePath -> [FilePath] -> [String] -> Pandoc -> m Pandoc
@@ -968,7 +994,7 @@ options =
968994 templ <- getDefaultTemplate Nothing arg
969995 case templ of
970996 Right t -> UTF8.hPutStr stdout t
971− Left e -> error $ show e
997+ Left e -> E.throwIO $ PandocAppError (show e)
972998 exitSuccess)
973999 "FORMAT")
9741000 "" -- "Print default template for FORMAT"
@@ -1232,6 +1258,13 @@ options =
12321258 "FILE")
12331259 "" -- "Path of custom reference doc"
12341260
1261+ , Option "" ["epub-subdirectory"]
1262+ (ReqArg
1263+ (\arg opt ->
1264+ return opt { optEpubSubdirectory = arg })
1265+ "DIRNAME")
1266+ "" -- "Name of subdirectory for epub content in OCF container"
1267+
12351268 , Option "" ["epub-cover-image"]
12361269 (ReqArg
12371270 (\arg opt ->
@@ -1355,7 +1388,8 @@ options =
13551388 , Option "" ["mathjax"]
13561389 (OptArg
13571390 (\arg opt -> do
1358− let url' = fromMaybe "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-AMS_CHTML-full" arg
1391+ let url' = fromMaybe (defaultMathJaxURL ++
1392+ "MathJax.js?config=TeX-AMS_CHTML-full") arg
13591393 return opt { optHTMLMathMethod = MathJax url'})
13601394 "URL")
13611395 "" -- "Use MathJax for HTML math"
@@ -1364,7 +1398,7 @@ options =
13641398 (\arg opt ->
13651399 return opt
13661400 { optKaTeXJS =
1367− arg <|> Just "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.js"})
1401+ arg <|> Just (defaultKaTeXURL ++ "katex.min.js")})
13681402 "URL")
13691403 "" -- Use KaTeX for HTML Math
13701404
@@ -1388,7 +1422,7 @@ options =
13881422
13891423 , Option "" ["trace"]
13901424 (NoArg
1391− (\opt -> return opt { optVerbosity = DEBUG }))
1425+ (\opt -> return opt { optTrace = True }))
13921426 "" -- "Turn on diagnostic tracing in readers."
13931427
13941428 , Option "" ["dump-args"]
@@ -1530,6 +1564,8 @@ handleUnrecognizedOption :: String -> [String] -> [String]
15301564handleUnrecognizedOption "--smart" =
15311565 (("--smart/-S has been removed. Use +smart or -smart extension instead.\n" ++
15321566 "For example: pandoc -f markdown+smart -t markdown-smart.") :)
1567+handleUnrecognizedOption "--normalize" =
1568+ ("--normalize has been removed. Normalization is now automatic." :)
15331569handleUnrecognizedOption "-S" = handleUnrecognizedOption "--smart"
15341570handleUnrecognizedOption "--old-dashes" =
15351571 ("--old-dashes has been removed. Use +old_dashes extension instead." :)
src/Text/Pandoc/BCP47.hsnew file+139
@@ -0,0 +1,139 @@
1+{-
2+Copyright (C) 2017 John MacFarlane <jgm@berkeley.edu>
3+
4+This program is free software; you can redistribute it and/or modify
5+it under the terms of the GNU General Public License as published by
6+the Free Software Foundation; either version 2 of the License, or
7+(at your option) any later version.
8+
9+This program is distributed in the hope that it will be useful,
10+but WITHOUT ANY WARRANTY; without even the implied warranty of
11+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+GNU General Public License for more details.
13+
14+You should have received a copy of the GNU General Public License
15+along with this program; if not, write to the Free Software
16+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17+-}
18+
19+{- |
20+ Module : Text.Pandoc.BCP47
21+ Copyright : Copyright (C) 2017 John MacFarlane
22+ License : GNU GPL, version 2 or above
23+
24+ Maintainer : John MacFarlane <jgm@berkeley.edu>
25+ Stability : alpha
26+ Portability : portable
27+
28+Functions for parsing and rendering BCP47 language identifiers.
29+-}
30+module Text.Pandoc.BCP47 (
31+ getLang
32+ , toLang
33+ , parseBCP47
34+ , Lang(..)
35+ , renderLang
36+ )
37+where
38+import Control.Monad (guard)
39+import Data.Char (isAscii, isLetter, isUpper, isLower, toUpper, toLower,
40+ isAlphaNum)
41+import Data.List (intercalate)
42+import Text.Pandoc.Definition
43+import Text.Pandoc.Class (PandocMonad, report)
44+import Text.Pandoc.Logging
45+import Text.Pandoc.Options
46+import qualified Text.Parsec as P
47+
48+-- | Represents BCP 47 language/country code.
49+data Lang = Lang{ langLanguage :: String
50+ , langScript :: String
51+ , langRegion :: String
52+ , langVariants :: [String] }
53+ deriving (Eq, Ord, Show)
54+
55+-- | Render a Lang as BCP 47.
56+renderLang :: Lang -> String
57+renderLang lang = intercalate "-" (langLanguage lang : filter (not . null)
58+ ([langScript lang, langRegion lang] ++ langVariants lang))
59+
60+-- | Get the contents of the `lang` metadata field or variable.
61+getLang :: WriterOptions -> Meta -> Maybe String
62+getLang opts meta =
63+ case lookup "lang" (writerVariables opts) of
64+ Just s -> Just s
65+ _ ->
66+ case lookupMeta "lang" meta of
67+ Just (MetaInlines [Str s]) -> Just s
68+ Just (MetaString s) -> Just s
69+ _ -> Nothing
70+
71+-- | Convert BCP47 string to a Lang, issuing warning
72+-- if there are problems.
73+toLang :: PandocMonad m => Maybe String -> m (Maybe Lang)
74+toLang Nothing = return Nothing
75+toLang (Just s) =
76+ case parseBCP47 s of
77+ Left _ -> do
78+ report $ InvalidLang s
79+ return Nothing
80+ Right l -> return (Just l)
81+
82+-- | Parse a BCP 47 string as a Lang. Currently we parse
83+-- extensions and private-use fields as "variants," even
84+-- though officially they aren't.
85+parseBCP47 :: String -> Either String Lang
86+parseBCP47 lang =
87+ case P.parse bcp47 "lang" lang of
88+ Right r -> Right r
89+ Left e -> Left $ show e
90+ where bcp47 = do
91+ language <- pLanguage
92+ script <- P.option "" pScript
93+ region <- P.option "" pRegion
94+ variants <- P.many (pVariant P.<|> pExtension P.<|> pPrivateUse)
95+ P.eof
96+ return $ Lang{ langLanguage = language
97+ , langScript = script
98+ , langRegion = region
99+ , langVariants = variants }
100+ asciiLetter = P.satisfy (\c -> isAscii c && isLetter c)
101+ pLanguage = do
102+ cs <- P.many1 asciiLetter
103+ let lcs = length cs
104+ guard $ lcs == 2 || lcs == 3
105+ return $ map toLower cs
106+ pScript = P.try $ do
107+ P.char '-'
108+ x <- P.satisfy (\c -> isAscii c && isLetter c && isUpper c)
109+ xs <- P.count 3
110+ (P.satisfy (\c -> isAscii c && isLetter c && isLower c))
111+ return $ map toLower (x:xs)
112+ pRegion = P.try $ do
113+ P.char '-'
114+ cs <- P.many1 asciiLetter
115+ let lcs = length cs
116+ guard $ lcs == 2 || lcs == 3
117+ return $ map toUpper cs
118+ pVariant = P.try $ do
119+ P.char '-'
120+ ds <- P.option "" (P.count 1 P.digit)
121+ cs <- P.many1 asciiLetter
122+ let var = ds ++ cs
123+ guard $ if null ds
124+ then length var >= 5 && length var <= 8
125+ else length var == 4
126+ return $ map toLower var
127+ pExtension = P.try $ do
128+ P.char '-'
129+ cs <- P.many1 $ P.satisfy (\c -> isAscii c && isAlphaNum c)
130+ guard $ length cs >= 2 && length cs <= 8
131+ return $ map toLower cs
132+ pPrivateUse = P.try $ do
133+ P.char '-'
134+ P.char 'x'
135+ P.char '-'
136+ cs <- P.many1 $ P.satisfy (\c -> isAscii c && isAlphaNum c)
137+ guard $ length cs >= 1 && length cs <= 8
138+ let var = "x-" ++ cs
139+ return $ map toLower var
src/Text/Pandoc/Class.hs+28−6
@@ -45,6 +45,7 @@ module Text.Pandoc.Class ( PandocMonad(..)
4545 , getZonedTime
4646 , readFileFromDirs
4747 , report
48+ , setTrace
4849 , getLog
4950 , setVerbosity
5051 , getMediaBag
@@ -78,7 +79,7 @@ import qualified Text.Pandoc.Shared as IO ( readDataFile
7879import qualified Text.Pandoc.UTF8 as UTF8
7980import Text.Pandoc.Compat.Time (UTCTime)
8081import Text.Pandoc.Logging
81−import Text.Parsec (ParsecT)
82+import Text.Parsec (ParsecT, getPosition, sourceLine, sourceName)
8283import qualified Text.Pandoc.Compat.Time as IO (getCurrentTime)
8384import Text.Pandoc.MIME (MimeType, getMimeType, extensionFromMimeType)
8485import Text.Pandoc.Definition
@@ -107,7 +108,7 @@ import qualified System.FilePath.Glob as IO (glob)
107108import qualified System.Directory as IO (getModificationTime)
108109import Control.Monad as M (fail)
109110import Control.Monad.Reader (ReaderT)
110−import Control.Monad.State
111+import Control.Monad.State.Strict
111112import Control.Monad.Except
112113import Control.Monad.Writer (WriterT)
113114import Control.Monad.RWS (RWST)
@@ -117,6 +118,7 @@ import System.IO.Error
117118import System.IO (stderr)
118119import qualified Data.Map as M
119120import Text.Pandoc.Error
121+import qualified Debug.Trace
120122
121123class (Functor m, Applicative m, Monad m, MonadError PandocError m)
122124 => PandocMonad m where
@@ -140,6 +142,11 @@ class (Functor m, Applicative m, Monad m, MonadError PandocError m)
140142 modifyCommonState :: (CommonState -> CommonState) -> m ()
141143 modifyCommonState f = getCommonState >>= putCommonState . f
142144
145+ trace :: String -> m ()
146+ trace msg = do
147+ tracing <- getsCommonState stTrace
148+ when tracing $ Debug.Trace.trace ("[trace] " ++ msg) (return ())
149+
143150 logOutput :: LogMessage -> m ()
144151
145152-- Functions defined for all PandocMonad instances
@@ -155,10 +162,11 @@ report :: PandocMonad m => LogMessage -> m ()
155162report msg = do
156163 verbosity <- getsCommonState stVerbosity
157164 let level = messageVerbosity msg
158− when (level <= verbosity) $
159− logOutput msg
160− unless (level == DEBUG) $
161− modifyCommonState $ \st -> st{ stLog = msg : stLog st }
165+ when (level <= verbosity) $ logOutput msg
166+ modifyCommonState $ \st -> st{ stLog = msg : stLog st }
167+
168+setTrace :: PandocMonad m => Bool -> m ()
169+setTrace useTracing = modifyCommonState $ \st -> st{stTrace = useTracing}
162170
163171setMediaBag :: PandocMonad m => MediaBag -> m ()
164172setMediaBag mb = modifyCommonState $ \st -> st{stMediaBag = mb}
@@ -208,6 +216,7 @@ data CommonState = CommonState { stLog :: [LogMessage]
208216 , stOutputFile :: Maybe FilePath
209217 , stResourcePath :: [FilePath]
210218 , stVerbosity :: Verbosity
219+ , stTrace :: Bool
211220 }
212221
213222instance Default CommonState where
@@ -217,6 +226,7 @@ instance Default CommonState where
217226 , stOutputFile = Nothing
218227 , stResourcePath = ["."]
219228 , stVerbosity = WARNING
229+ , stTrace = False
220230 }
221231
222232runIO :: PandocIO a -> IO (Either PandocError a)
@@ -561,8 +571,20 @@ instance PandocMonad m => PandocMonad (ParsecT s st m) where
561571 getModificationTime = lift . getModificationTime
562572 getCommonState = lift getCommonState
563573 putCommonState = lift . putCommonState
574+ trace msg = do
575+ tracing <- getsCommonState stTrace
576+ when tracing $ do
577+ pos <- getPosition
578+ Debug.Trace.trace
579+ ("[trace] Parsed " ++ msg ++ " at line " ++
580+ show (sourceLine pos) ++
581+ if sourceName pos == "chunk"
582+ then " of chunk"
583+ else "")
584+ (return ())
564585 logOutput = lift . logOutput
565586
587+
566588instance PandocMonad m => PandocMonad (ReaderT r m) where
567589 lookupEnv = lift . lookupEnv
568590 getCurrentTime = lift getCurrentTime
src/Text/Pandoc/Compat/Time.hs+1−1
@@ -27,4 +27,4 @@ where
2727import Data.Time
2828import System.Locale ( defaultTimeLocale )
2929
30−#endif
30+#endif
src/Text/Pandoc/Error.hs+8
@@ -61,7 +61,10 @@ data PandocError = PandocIOError String IOError
6161 | PandocFilterError String String
6262 | PandocCouldNotFindDataFileError String
6363 | PandocResourceNotFound String
64+ | PandocTemplateError String
6465 | PandocAppError String
66+ | PandocEpubSubdirectoryError String
67+ | PandocMacroLoop String
6568 deriving (Show, Typeable, Generic)
6669
6770instance Exception PandocError
@@ -101,7 +104,12 @@ handleError (Left e) =
101104 "Could not find data file " ++ fn
102105 PandocResourceNotFound fn -> err 99 $
103106 "File " ++ fn ++ " not found in resource path"
107+ PandocTemplateError s -> err 5 s
104108 PandocAppError s -> err 1 s
109+ PandocEpubSubdirectoryError s -> err 31 $
110+ "EPUB subdirectory name '" ++ s ++ "' contains illegal characters"
111+ PandocMacroLoop s -> err 91 $
112+ "Loop encountered in expanding macro " ++ s
105113
106114err :: Int -> String -> IO a
107115err exitCode msg = do
src/Text/Pandoc/Extensions.hs+10−2
@@ -45,7 +45,7 @@ module Text.Pandoc.Extensions ( Extension(..)
4545 , githubMarkdownExtensions
4646 , multimarkdownExtensions )
4747where
48−import Data.Bits (clearBit, setBit, testBit)
48+import Data.Bits (clearBit, setBit, testBit, (.|.))
4949import Data.Data (Data)
5050import Data.Typeable (Typeable)
5151import GHC.Generics (Generic)
@@ -55,6 +55,10 @@ import Text.Parsec
5555newtype Extensions = Extensions Integer
5656 deriving (Show, Read, Eq, Ord, Data, Typeable, Generic)
5757
58+instance Monoid Extensions where
59+ mempty = Extensions 0
60+ mappend (Extensions a) (Extensions b) = Extensions (a .|. b)
61+
5862extensionsFromList :: [Extension] -> Extensions
5963extensionsFromList = foldr enableExtension emptyExtensions
6064
@@ -94,6 +98,7 @@ data Extension =
9498 | Ext_fenced_code_attributes -- ^ Allow attributes on fenced code blocks
9599 | Ext_backtick_code_blocks -- ^ GitHub style ``` code blocks
96100 | Ext_inline_code_attributes -- ^ Allow attributes on inline code
101+ | Ext_raw_attribute -- ^ Allow explicit raw blocks/inlines
97102 | Ext_markdown_in_html_blocks -- ^ Interpret as markdown inside HTML blocks
98103 | Ext_native_divs -- ^ Use Div blocks for contents of <div> tags
99104 | Ext_native_spans -- ^ Use Span inlines for contents of <span>
@@ -162,6 +167,7 @@ pandocExtensions = extensionsFromList
162167 , Ext_fenced_code_attributes
163168 , Ext_backtick_code_blocks
164169 , Ext_inline_code_attributes
170+ , Ext_raw_attribute
165171 , Ext_markdown_in_html_blocks
166172 , Ext_native_divs
167173 , Ext_native_spans
@@ -238,7 +244,6 @@ githubMarkdownExtensions = extensionsFromList
238244 , Ext_space_in_atx_header
239245 , Ext_intraword_underscores
240246 , Ext_strikeout
241− , Ext_hard_line_breaks
242247 , Ext_emoji
243248 , Ext_lists_without_preceding_blankline
244249 , Ext_shortcut_reference_links
@@ -275,6 +280,8 @@ multimarkdownExtensions = extensionsFromList
275280 , Ext_subscript
276281 , Ext_backtick_code_blocks
277282 , Ext_spaced_reference_links
283+ -- So far only in dev version of mmd:
284+ , Ext_raw_attribute
278285 ]
279286
280287-- | Language extensions to be used with strict markdown.
@@ -311,6 +318,7 @@ getDefaultExtensions "epub2" = getDefaultExtensions "epub"
311318getDefaultExtensions "epub3" = getDefaultExtensions "epub"
312319getDefaultExtensions "latex" = extensionsFromList
313320 [Ext_smart,
321+ Ext_latex_macros,
314322 Ext_auto_identifiers]
315323getDefaultExtensions "context" = extensionsFromList
316324 [Ext_smart,
src/Text/Pandoc/Logging.hs+22−10
@@ -52,7 +52,7 @@ import Text.Pandoc.Definition
5252import Text.Parsec.Pos
5353
5454-- | Verbosity level.
55−data Verbosity = ERROR | WARNING | INFO | DEBUG
55+data Verbosity = ERROR | WARNING | INFO
5656 deriving (Show, Read, Eq, Data, Enum, Ord, Bounded, Typeable, Generic)
5757
5858instance ToJSON Verbosity where
@@ -63,7 +63,6 @@ instance FromJSON Verbosity where
6363 "ERROR" -> return ERROR
6464 "WARNING" -> return WARNING
6565 "INFO" -> return INFO
66− "DEBUG" -> return DEBUG
6766 _ -> mzero
6867 parseJSON _ = mzero
6968
@@ -78,7 +77,7 @@ data LogMessage =
7877 | CircularReference String SourcePos
7978 | ParsingUnescaped String SourcePos
8079 | CouldNotLoadIncludeFile String SourcePos
81− | ParsingTrace String SourcePos
80+ | MacroAlreadyDefined String SourcePos
8281 | InlineNotRendered Inline
8382 | BlockNotRendered Block
8483 | DocxParserWarning String
@@ -92,7 +91,9 @@ data LogMessage =
9291 | Extracting String
9392 | NoTitleElement String
9493 | NoLangSpecified
94+ | InvalidLang String
9595 | CouldNotHighlight String
96+ | MissingCharacter String
9697 deriving (Show, Eq, Data, Ord, Typeable, Generic)
9798
9899instance ToJSON LogMessage where
@@ -150,11 +151,11 @@ instance ToJSON LogMessage where
150151 "source" .= Text.pack (sourceName pos),
151152 "line" .= toJSON (sourceLine pos),
152153 "column" .= toJSON (sourceColumn pos)]
153− ParsingTrace s pos ->
154− ["contents" .= Text.pack s,
154+ MacroAlreadyDefined name pos ->
155+ ["name" .= Text.pack name,
155156 "source" .= Text.pack (sourceName pos),
156− "line" .= sourceLine pos,
157− "column" .= sourceColumn pos]
157+ "line" .= toJSON (sourceLine pos),
158+ "column" .= toJSON (sourceColumn pos)]
158159 InlineNotRendered il ->
159160 ["contents" .= toJSON il]
160161 BlockNotRendered bl ->
@@ -184,8 +185,12 @@ instance ToJSON LogMessage where
184185 NoTitleElement fallback ->
185186 ["fallback" .= Text.pack fallback]
186187 NoLangSpecified -> []
188+ InvalidLang s ->
189+ ["lang" .= Text.pack s]
187190 CouldNotHighlight msg ->
188191 ["message" .= Text.pack msg]
192+ MissingCharacter msg ->
193+ ["message" .= Text.pack msg]
189194
190195showPos :: SourcePos -> String
191196showPos pos = sn ++ "line " ++
@@ -225,8 +230,8 @@ showLogMessage msg =
225230 "Parsing unescaped '" ++ s ++ "' at " ++ showPos pos
226231 CouldNotLoadIncludeFile fp pos ->
227232 "Could not load include file '" ++ fp ++ "' at " ++ showPos pos
228− ParsingTrace s pos ->
229− "Parsing trace at " ++ showPos pos ++ ": " ++ s
233+ MacroAlreadyDefined name pos ->
234+ "Macro '" ++ name ++ "' already defined, ignoring at " ++ showPos pos
230235 InlineNotRendered il ->
231236 "Not rendering " ++ show il
232237 BlockNotRendered bl ->
@@ -260,8 +265,13 @@ showLogMessage msg =
260265 NoLangSpecified ->
261266 "No value for 'lang' was specified in the metadata.\n" ++
262267 "It is recommended that lang be specified for this format."
268+ InvalidLang s ->
269+ "Invalid 'lang' value '" ++ s ++ "'.\n" ++
270+ "Use an IETF language tag like 'en-US'."
263271 CouldNotHighlight m ->
264272 "Could not highlight code block:\n" ++ m
273+ MissingCharacter m ->
274+ "Missing character: " ++ m
265275
266276messageVerbosity:: LogMessage -> Verbosity
267277messageVerbosity msg =
@@ -275,8 +285,8 @@ messageVerbosity msg =
275285 ReferenceNotFound{} -> WARNING
276286 CircularReference{} -> WARNING
277287 CouldNotLoadIncludeFile{} -> WARNING
288+ MacroAlreadyDefined{} -> WARNING
278289 ParsingUnescaped{} -> INFO
279− ParsingTrace{} -> DEBUG
280290 InlineNotRendered{} -> INFO
281291 BlockNotRendered{} -> INFO
282292 DocxParserWarning{} -> WARNING
@@ -290,4 +300,6 @@ messageVerbosity msg =
290300 Extracting{} -> INFO
291301 NoTitleElement{} -> WARNING
292302 NoLangSpecified -> INFO
303+ InvalidLang{} -> WARNING
293304 CouldNotHighlight{} -> WARNING
305+ MissingCharacter{} -> WARNING
src/Text/Pandoc/Lua.hs+88−145
@@ -1,3 +1,8 @@
1+{-# LANGUAGE FlexibleContexts #-}
2+{-# LANGUAGE FlexibleInstances #-}
3+{-# LANGUAGE MultiParamTypeClasses #-}
4+{-# LANGUAGE DeriveDataTypeable #-}
5+{-# LANGUAGE ScopedTypeVariables #-}
16{-
27Copyright © 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
38
@@ -15,9 +20,6 @@ You should have received a copy of the GNU General Public License
1520along with this program; if not, write to the Free Software
1621Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1722-}
18−{-# LANGUAGE FlexibleContexts #-}
19−{-# LANGUAGE FlexibleInstances #-}
20−{-# LANGUAGE MultiParamTypeClasses #-}
2123{- |
2224 Module : Text.Pandoc.Lua
2325 Copyright : Copyright © 2017 Albert Krewinkel
@@ -28,11 +30,17 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2830
2931Pandoc lua utils.
3032-}
31−module Text.Pandoc.Lua ( runLuaFilter, pushPandocModule ) where
33+module Text.Pandoc.Lua ( LuaException(..),
34+ runLuaFilter,
35+ pushPandocModule ) where
3236
33−import Control.Monad (unless, when, (>=>))
37+import Control.Exception
38+import Control.Monad (unless, when, (>=>), mplus)
3439import Control.Monad.Trans (MonadIO (..))
40+import Data.Data (toConstr, showConstr, dataTypeOf, dataTypeConstrs, Data)
3541import Data.Map (Map)
42+import Data.Maybe (isJust)
43+import Data.Typeable (Typeable)
3644import Scripting.Lua (LuaState, StackValue (..))
3745import Text.Pandoc.Definition
3846import Text.Pandoc.Lua.PandocModule (pushPandocModule)
@@ -42,24 +50,25 @@ import Text.Pandoc.Walk
4250import qualified Data.Map as Map
4351import qualified Scripting.Lua as Lua
4452
53+newtype LuaException = LuaException String
54+ deriving (Show, Typeable)
55+
56+instance Exception LuaException
57+
4558runLuaFilter :: (MonadIO m)
46− => FilePath -> [String] -> Pandoc -> m Pandoc
47−runLuaFilter filterPath args pd = liftIO $ do
59+ => Maybe FilePath -> FilePath -> [String] -> Pandoc -> m Pandoc
60+runLuaFilter datadir filterPath args pd = liftIO $ do
4861 lua <- Lua.newstate
4962 Lua.openlibs lua
50− -- create table in registry to store filter functions
51− Lua.push lua "PANDOC_FILTER_FUNCTIONS"
52− Lua.newtable lua
53− Lua.rawset lua Lua.registryindex
5463 -- store module in global "pandoc"
55− pushPandocModule lua
64+ pushPandocModule datadir lua
5665 Lua.setglobal lua "pandoc"
5766 top <- Lua.gettop lua
5867 status <- Lua.loadfile lua filterPath
59− if (status /= 0)
68+ if status /= 0
6069 then do
6170 Just luaErrMsg <- Lua.peek lua 1
62− error luaErrMsg
71+ throwIO (LuaException luaErrMsg)
6372 else do
6473 Lua.call lua 0 Lua.multret
6574 newtop <- Lua.gettop lua
@@ -80,157 +89,91 @@ pushGlobalFilter lua =
8089 *> Lua.rawseti lua (-2) 1
8190
8291runAll :: [LuaFilter] -> Pandoc -> IO Pandoc
83−runAll [] = return
84−runAll (x:xs) = walkMWithLuaFilter x >=> runAll xs
92+runAll = foldr ((>=>) . walkMWithLuaFilter) return
8593
8694walkMWithLuaFilter :: LuaFilter -> Pandoc -> IO Pandoc
8795walkMWithLuaFilter (LuaFilter lua fnMap) =
88− walkM (execInlineLuaFilter lua fnMap) >=>
89− walkM (execBlockLuaFilter lua fnMap) >=>
90− walkM (execMetaLuaFilter lua fnMap) >=>
91− walkM (execDocLuaFilter lua fnMap)
96+ (if hasOneOf (constructorsFor (dataTypeOf (Str [])))
97+ then walkM (tryFilter lua fnMap :: Inline -> IO Inline)
98+ else return)
99+ >=>
100+ (if hasOneOf (constructorsFor (dataTypeOf (Para [])))
101+ then walkM (tryFilter lua fnMap :: Block -> IO Block)
102+ else return)
103+ >=>
104+ (case Map.lookup "Meta" fnMap of
105+ Just fn -> walkM (\(Pandoc meta blocks) -> do
106+ meta' <- runFilterFunction lua fn meta
107+ return $ Pandoc meta' blocks)
108+ Nothing -> return)
109+ >=>
110+ (case Map.lookup "Pandoc" fnMap `mplus` Map.lookup "Doc" fnMap of
111+ Just fn -> (runFilterFunction lua fn) :: Pandoc -> IO Pandoc
112+ Nothing -> return)
113+ where hasOneOf = any (\k -> isJust (Map.lookup k fnMap))
114+ constructorsFor x = map show (dataTypeConstrs x)
92115
93116type FunctionMap = Map String LuaFilterFunction
94117data LuaFilter = LuaFilter LuaState FunctionMap
95118
96119newtype LuaFilterFunction = LuaFilterFunction { functionIndex :: Int }
97120
98−execDocLuaFilter :: LuaState
99− -> FunctionMap
100− -> Pandoc -> IO Pandoc
101−execDocLuaFilter lua fnMap x = do
102− let docFnName = "Doc"
103− case Map.lookup docFnName fnMap of
121+tryFilter :: (Data a, StackValue a) => LuaState -> FunctionMap -> a -> IO a
122+tryFilter lua fnMap x =
123+ let filterFnName = showConstr (toConstr x) in
124+ case Map.lookup filterFnName fnMap of
104125 Nothing -> return x
105− Just fn -> runLuaFilterFunction lua fn x
106−
107−execMetaLuaFilter :: LuaState
108− -> FunctionMap
109− -> Pandoc -> IO Pandoc
110−execMetaLuaFilter lua fnMap pd@(Pandoc meta blks) = do
111− let metaFnName = "Meta"
112− case Map.lookup metaFnName fnMap of
113− Nothing -> return pd
114− Just fn -> do
115− meta' <- runLuaFilterFunction lua fn meta
116− return $ Pandoc meta' blks
117−
118−execBlockLuaFilter :: LuaState
119− -> FunctionMap
120− -> Block -> IO Block
121−execBlockLuaFilter lua fnMap x = do
122− let tryFilter :: String -> IO Block
123− tryFilter filterFnName =
124− case Map.lookup filterFnName fnMap of
125− Nothing -> return x
126− Just fn -> runLuaFilterFunction lua fn x
127− case x of
128− BlockQuote _ -> tryFilter "BlockQuote"
129− BulletList _ -> tryFilter "BulletList"
130− CodeBlock _ _ -> tryFilter "CodeBlock"
131− DefinitionList _ -> tryFilter "DefinitionList"
132− Div _ _ -> tryFilter "Div"
133− Header _ _ _ -> tryFilter "Header"
134− HorizontalRule -> tryFilter "HorizontalRule"
135− LineBlock _ -> tryFilter "LineBlock"
136− Null -> tryFilter "Null"
137− Para _ -> tryFilter "Para"
138− Plain _ -> tryFilter "Plain"
139− RawBlock _ _ -> tryFilter "RawBlock"
140− OrderedList _ _ -> tryFilter "OrderedList"
141− Table _ _ _ _ _ -> tryFilter "Table"
142−
143−execInlineLuaFilter :: LuaState
144− -> FunctionMap
145− -> Inline -> IO Inline
146−execInlineLuaFilter lua fnMap x = do
147− let tryFilter :: String -> IO Inline
148− tryFilter filterFnName =
149− case Map.lookup filterFnName fnMap of
150− Nothing -> return x
151− Just fn -> runLuaFilterFunction lua fn x
152− let tryFilterAlternatives :: [String] -> IO Inline
153− tryFilterAlternatives [] = return x
154− tryFilterAlternatives (fnName : alternatives) =
155− case Map.lookup fnName fnMap of
156− Nothing -> tryFilterAlternatives alternatives
157− Just fn -> runLuaFilterFunction lua fn x
158− case x of
159− Cite _ _ -> tryFilter "Cite"
160− Code _ _ -> tryFilter "Code"
161− Emph _ -> tryFilter "Emph"
162− Image _ _ _ -> tryFilter "Image"
163− LineBreak -> tryFilter "LineBreak"
164− Link _ _ _ -> tryFilter "Link"
165− Math DisplayMath _ -> tryFilterAlternatives ["DisplayMath", "Math"]
166− Math InlineMath _ -> tryFilterAlternatives ["InlineMath", "Math"]
167− Note _ -> tryFilter "Note"
168− Quoted DoubleQuote _ -> tryFilterAlternatives ["DoubleQuoted", "Quoted"]
169− Quoted SingleQuote _ -> tryFilterAlternatives ["SingleQuoted", "Quoted"]
170− RawInline _ _ -> tryFilter "RawInline"
171− SmallCaps _ -> tryFilter "SmallCaps"
172− SoftBreak -> tryFilter "SoftBreak"
173− Space -> tryFilter "Space"
174− Span _ _ -> tryFilter "Span"
175− Str _ -> tryFilter "Str"
176− Strikeout _ -> tryFilter "Strikeout"
177− Strong _ -> tryFilter "Strong"
178− Subscript _ -> tryFilter "Subscript"
179− Superscript _ -> tryFilter "Superscript"
126+ Just fn -> runFilterFunction lua fn x
180127
181128instance StackValue LuaFilter where
182129 valuetype _ = Lua.TTABLE
183130 push = undefined
184131 peek lua idx = fmap (LuaFilter lua) <$> Lua.peek lua idx
185132
186−-- | Helper class for pushing a single value to the stack via a lua function.
187−-- See @pushViaCall@.
188−class PushViaFilterFunction a where
189− pushViaFilterFunction' :: LuaState -> LuaFilterFunction -> IO () -> Int -> a
190−
191−instance StackValue a => PushViaFilterFunction (IO a) where
192− pushViaFilterFunction' lua lf pushArgs num = do
193− pushFilterFunction lua lf
194− pushArgs
195− Lua.call lua num 1
196− mbres <- Lua.peek lua (-1)
197− case mbres of
198− Nothing -> error $ "Error while trying to get a filter's return "
199− ++ "value from lua stack."
200− Just res -> res <$ Lua.pop lua 1
201−
202−instance (StackValue a, PushViaFilterFunction b) =>
203− PushViaFilterFunction (a -> b) where
204− pushViaFilterFunction' lua lf pushArgs num x =
205− pushViaFilterFunction' lua lf (pushArgs *> push lua x) (num + 1)
206−
207−-- | Push an value to the stack via a lua filter function. The function is
208−-- called with all arguments that are passed to this function and is expected to
209−-- return a single value.
210−runLuaFilterFunction :: PushViaFilterFunction a
211− => LuaState -> LuaFilterFunction -> a
212−runLuaFilterFunction lua lf = pushViaFilterFunction' lua lf (return ()) 0
133+-- | Push a value to the stack via a lua filter function. The filter function is
134+-- called with given element as argument and is expected to return an element.
135+-- Alternatively, the function can return nothing or nil, in which case the
136+-- element is left unchanged.
137+runFilterFunction :: StackValue a => LuaState -> LuaFilterFunction -> a -> IO a
138+runFilterFunction lua lf x = do
139+ pushFilterFunction lua lf
140+ Lua.push lua x
141+ z <- Lua.pcall lua 1 1 0
142+ if (z /= 0)
143+ then do
144+ msg <- Lua.peek lua (-1)
145+ let prefix = "Error while running filter function: "
146+ throwIO . LuaException $
147+ case msg of
148+ Nothing -> prefix ++ "could not read error message"
149+ Just msg' -> prefix ++ msg'
150+ else do
151+ resType <- Lua.ltype lua (-1)
152+ case resType of
153+ Lua.TNIL -> Lua.pop lua 1 *> return x
154+ _ -> do
155+ mbres <- Lua.peek lua (-1)
156+ case mbres of
157+ Nothing -> throwIO $ LuaException
158+ ("Error while trying to get a filter's return "
159+ ++ "value from lua stack.")
160+ Just res -> res <$ Lua.pop lua 1
213161
214162-- | Push the filter function to the top of the stack.
215163pushFilterFunction :: Lua.LuaState -> LuaFilterFunction -> IO ()
216−pushFilterFunction lua lf = do
164+pushFilterFunction lua lf =
217165 -- The function is stored in a lua registry table, retrieve it from there.
218− push lua ("PANDOC_FILTER_FUNCTIONS"::String)
219− Lua.rawget lua Lua.registryindex
220− Lua.rawgeti lua (-1) (functionIndex lf)
221− Lua.remove lua (-2) -- remove registry table from stack
166+ Lua.rawgeti lua Lua.registryindex (functionIndex lf)
167+
168+registerFilterFunction :: LuaState -> Int -> IO LuaFilterFunction
169+registerFilterFunction lua idx = do
170+ isFn <- Lua.isfunction lua idx
171+ unless isFn . throwIO . LuaException $ "Not a function at index " ++ show idx
172+ Lua.pushvalue lua idx
173+ refIdx <- Lua.ref lua Lua.registryindex
174+ return $ LuaFilterFunction refIdx
222175
223176instance StackValue LuaFilterFunction where
224177 valuetype _ = Lua.TFUNCTION
225− push lua v = pushFilterFunction lua v
226− peek lua i = do
227− isFn <- Lua.isfunction lua i
228− unless isFn (error $ "Not a function at index " ++ (show i))
229− Lua.pushvalue lua i
230− push lua ("PANDOC_FILTER_FUNCTIONS"::String)
231− Lua.rawget lua Lua.registryindex
232− len <- Lua.objlen lua (-1)
233− Lua.insert lua (-2)
234− Lua.rawseti lua (-2) (len + 1)
235− Lua.pop lua 1
236− return . Just $ LuaFilterFunction (len + 1)
178+ push = pushFilterFunction
179+ peek = fmap (fmap Just) . registerFilterFunction
src/Text/Pandoc/Lua/PandocModule.hs+8−7
@@ -34,15 +34,16 @@ import Data.Text (pack)
3434import Scripting.Lua (LuaState, call, push, pushhsfunction, rawset)
3535import Text.Pandoc.Class hiding (readDataFile)
3636import Text.Pandoc.Definition (Pandoc)
37+import Text.Pandoc.Options (ReaderOptions(readerExtensions))
3738import Text.Pandoc.Lua.Compat (loadstring)
3839import Text.Pandoc.Lua.StackInstances ()
3940import Text.Pandoc.Readers (Reader (..), getReader)
4041import Text.Pandoc.Shared (readDataFile)
4142
4243-- | Push the "pandoc" on the lua stack.
43−pushPandocModule :: LuaState -> IO ()
44−pushPandocModule lua = do
45− script <- pandocModuleScript
44+pushPandocModule :: Maybe FilePath -> LuaState -> IO ()
45+pushPandocModule datadir lua = do
46+ script <- pandocModuleScript datadir
4647 status <- loadstring lua script "pandoc.lua"
4748 unless (status /= 0) $ call lua 0 1
4849 push lua "__read"
@@ -50,17 +51,17 @@ pushPandocModule lua = do
5051 rawset lua (-3)
5152
5253-- | Get the string representation of the pandoc module
53−pandocModuleScript :: IO String
54−pandocModuleScript = unpack <$> readDataFile Nothing "pandoc.lua"
54+pandocModuleScript :: Maybe FilePath -> IO String
55+pandocModuleScript datadir = unpack <$> readDataFile datadir "pandoc.lua"
5556
5657read_doc :: String -> String -> IO (Either String Pandoc)
5758read_doc formatSpec content = do
5859 case getReader formatSpec of
5960 Left s -> return $ Left s
60− Right reader ->
61+ Right (reader, es) ->
6162 case reader of
6263 TextReader r -> do
63− res <- runIO $ r def (pack content)
64+ res <- runIO $ r def{ readerExtensions = es } (pack content)
6465 case res of
6566 Left s -> return . Left $ show s
6667 Right pd -> return $ Right pd
src/Text/Pandoc/Options.hs+2−2
@@ -61,7 +61,6 @@ data ReaderOptions = ReaderOptions{
6161 , readerStandalone :: Bool -- ^ Standalone document with header
6262 , readerColumns :: Int -- ^ Number of columns in terminal
6363 , readerTabStop :: Int -- ^ Tab stop
64− , readerApplyMacros :: Bool -- ^ Apply macros to TeX math
6564 , readerIndentedCodeClasses :: [String] -- ^ Default classes for
6665 -- indented code blocks
6766 , readerAbbreviations :: Set.Set String -- ^ Strings to treat as abbreviations
@@ -75,7 +74,6 @@ instance Default ReaderOptions
7574 , readerStandalone = False
7675 , readerColumns = 80
7776 , readerTabStop = 4
78− , readerApplyMacros = True
7977 , readerIndentedCodeClasses = []
8078 , readerAbbreviations = defaultAbbrevs
8179 , readerDefaultImageExtension = ""
@@ -213,6 +211,7 @@ data WriterOptions = WriterOptions
213211 , writerHighlightStyle :: Maybe Style -- ^ Style to use for highlighting
214212 -- (Nothing = no highlighting)
215213 , writerSetextHeaders :: Bool -- ^ Use setext headers for levels 1-2 in markdown
214+ , writerEpubSubdirectory :: String -- ^ Subdir for epub in OCF
216215 , writerEpubMetadata :: Maybe String -- ^ Metadata to include in EPUB
217216 , writerEpubFonts :: [FilePath] -- ^ Paths to fonts to embed
218217 , writerEpubChapterLevel :: Int -- ^ Header level for chapters (separate files)
@@ -249,6 +248,7 @@ instance Default WriterOptions where
249248 , writerListings = False
250249 , writerHighlightStyle = Just pygments
251250 , writerSetextHeaders = True
251+ , writerEpubSubdirectory = "EPUB"
252252 , writerEpubMetadata = Nothing
253253 , writerEpubFonts = []
254254 , writerEpubChapterLevel = 1
src/Text/Pandoc/PDF.hs+34−13
@@ -40,7 +40,6 @@ import qualified Data.Text as T
4040import Data.Text (Text)
4141import qualified Data.ByteString as BS
4242import Data.ByteString.Lazy (ByteString)
43−import qualified Data.ByteString.Lazy as B
4443import qualified Data.ByteString.Lazy as BL
4544import qualified Data.ByteString.Lazy.Char8 as BC
4645import Data.Maybe (fromMaybe)
@@ -197,7 +196,22 @@ tex2pdf' verbosity args tmpDir program source = do
197196 _ -> ""
198197 return $ Left $ logmsg <> extramsg
199198 (ExitSuccess, Nothing) -> return $ Left ""
200− (ExitSuccess, Just pdf) -> return $ Right pdf
199+ (ExitSuccess, Just pdf) -> do
200+ missingCharacterWarnings verbosity log'
201+ return $ Right pdf
202+
203+missingCharacterWarnings :: Verbosity -> ByteString -> IO ()
204+missingCharacterWarnings verbosity log' = do
205+ let ls = BC.lines log'
206+ let isMissingCharacterWarning = BC.isPrefixOf "Missing character: "
207+ let warnings = [ UTF8.toStringLazy (BC.drop 19 l)
208+ | l <- ls
209+ , isMissingCharacterWarning l
210+ ]
211+ runIO $ do
212+ setVerbosity verbosity
213+ mapM_ (report . MissingCharacter) warnings
214+ return ()
201215
202216-- parsing output
203217
@@ -255,12 +269,12 @@ runTeXProgram verbosity program args runNumber numRuns tmpDir source = do
255269 mapM_ print env''
256270 putStr "\n"
257271 putStrLn $ "[makePDF] Contents of " ++ file' ++ ":"
258− B.readFile file' >>= B.putStr
272+ BL.readFile file' >>= BL.putStr
259273 putStr "\n"
260274 (exit, out) <- pipeProcess (Just env'') program programArgs BL.empty
261275 when (verbosity >= INFO) $ do
262276 putStrLn $ "[makePDF] Run #" ++ show runNumber
263− B.hPutStr stdout out
277+ BL.hPutStr stdout out
264278 putStr "\n"
265279 if runNumber <= numRuns
266280 then runTeXProgram verbosity program args (runNumber + 1) numRuns tmpDir source
@@ -271,9 +285,16 @@ runTeXProgram verbosity program args runNumber numRuns tmpDir source = do
271285 -- We read PDF as a strict bytestring to make sure that the
272286 -- temp directory is removed on Windows.
273287 -- See https://github.com/jgm/pandoc/issues/1192.
274− then (Just . B.fromChunks . (:[])) `fmap` BS.readFile pdfFile
288+ then (Just . BL.fromChunks . (:[])) `fmap` BS.readFile pdfFile
275289 else return Nothing
276− return (exit, out, pdf)
290+ -- Note that some things like Missing character warnings
291+ -- appear in the log but not on stderr, so we prefer the log:
292+ let logFile = replaceExtension file ".log"
293+ logExists <- doesFileExist logFile
294+ log' <- if logExists
295+ then BL.readFile logFile
296+ else return out
297+ return (exit, log', pdf)
277298
278299ms2pdf :: Verbosity
279300 -> [String]
@@ -294,7 +315,7 @@ ms2pdf verbosity args source = do
294315 (exit, out) <- pipeProcess (Just env') "pdfroff" args
295316 (BL.fromStrict $ UTF8.fromText source)
296317 when (verbosity >= INFO) $ do
297− B.hPutStr stdout out
318+ BL.hPutStr stdout out
298319 putStr "\n"
299320 return $ case exit of
300321 ExitFailure _ -> Left out
@@ -318,12 +339,12 @@ html2pdf verbosity args source = do
318339 mapM_ print env'
319340 putStr "\n"
320341 putStrLn $ "[makePDF] Contents of " ++ file ++ ":"
321− B.readFile file >>= B.putStr
342+ BL.readFile file >>= BL.putStr
322343 putStr "\n"
323344 (exit, out) <- pipeProcess (Just env') "wkhtmltopdf" programArgs BL.empty
324345 removeFile file
325346 when (verbosity >= INFO) $ do
326− B.hPutStr stdout out
347+ BL.hPutStr stdout out
327348 putStr "\n"
328349 pdfExists <- doesFileExist pdfFile
329350 mbPdf <- if pdfExists
@@ -331,7 +352,7 @@ html2pdf verbosity args source = do
331352 -- temp directory is removed on Windows.
332353 -- See https://github.com/jgm/pandoc/issues/1192.
333354 then do
334− res <- (Just . B.fromChunks . (:[])) `fmap` BS.readFile pdfFile
355+ res <- (Just . BL.fromChunks . (:[])) `fmap` BS.readFile pdfFile
335356 removeFile pdfFile
336357 return res
337358 else return Nothing
@@ -365,11 +386,11 @@ context2pdf verbosity tmpDir source = inDirectory tmpDir $ do
365386 mapM_ print env'
366387 putStr "\n"
367388 putStrLn $ "[makePDF] Contents of " ++ file ++ ":"
368− B.readFile file >>= B.putStr
389+ BL.readFile file >>= BL.putStr
369390 putStr "\n"
370391 (exit, out) <- pipeProcess (Just env') "context" programArgs BL.empty
371392 when (verbosity >= INFO) $ do
372− B.hPutStr stdout out
393+ BL.hPutStr stdout out
373394 putStr "\n"
374395 let pdfFile = replaceExtension file ".pdf"
375396 pdfExists <- doesFileExist pdfFile
@@ -377,7 +398,7 @@ context2pdf verbosity tmpDir source = inDirectory tmpDir $ do
377398 -- We read PDF as a strict bytestring to make sure that the
378399 -- temp directory is removed on Windows.
379400 -- See https://github.com/jgm/pandoc/issues/1192.
380− then (Just . B.fromChunks . (:[])) `fmap` BS.readFile pdfFile
401+ then (Just . BL.fromChunks . (:[])) `fmap` BS.readFile pdfFile
381402 else return Nothing
382403 case (exit, mbPdf) of
383404 (ExitFailure _, _) -> do
src/Text/Pandoc/Parsing.hs+56−47
@@ -35,7 +35,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
3535
3636A utility library with parsers used in pandoc readers.
3737-}
38−module Text.Pandoc.Parsing ( anyLine,
38+module Text.Pandoc.Parsing ( takeWhileP,
39+ takeP,
40+ anyLine,
3941 anyLineNewline,
4042 indentWith,
4143 many1Till,
@@ -109,8 +111,6 @@ module Text.Pandoc.Parsing ( anyLine,
109111 dash,
110112 nested,
111113 citeKey,
112− macro,
113− applyMacros',
114114 Parser,
115115 ParserT,
116116 F,
@@ -130,6 +130,7 @@ module Text.Pandoc.Parsing ( anyLine,
130130 runParser,
131131 runParserT,
132132 parse,
133+ tokenPrim,
133134 anyToken,
134135 getInput,
135136 setInput,
@@ -178,24 +179,27 @@ module Text.Pandoc.Parsing ( anyLine,
178179 sourceLine,
179180 setSourceColumn,
180181 setSourceLine,
181− newPos
182+ newPos,
183+ Line,
184+ Column
182185 )
183186where
184187
188+import Data.Text (Text)
185189import Text.Pandoc.Definition
186190import Text.Pandoc.Options
187−import Text.Pandoc.Builder (Blocks, Inlines, rawBlock, HasMeta(..), trimInlines)
191+import Text.Pandoc.Builder (Blocks, Inlines, HasMeta(..), trimInlines)
188192import qualified Text.Pandoc.Builder as B
189193import Text.Pandoc.XML (fromEntities)
190194import qualified Text.Pandoc.UTF8 as UTF8 (putStrLn)
191195import Text.Parsec hiding (token)
192−import Text.Parsec.Pos (newPos)
196+import Text.Parsec.Pos (newPos, initialPos, updatePosString)
193197import Data.Char ( toLower, toUpper, ord, chr, isAscii, isAlphaNum,
194198 isHexDigit, isSpace, isPunctuation )
195199import Data.List ( intercalate, transpose, isSuffixOf )
196200import Text.Pandoc.Shared
197201import qualified Data.Map as M
198−import Text.TeXMath.Readers.TeX.Macros (applyMacros, Macro, pMacroDefinition)
202+import Text.Pandoc.Readers.LaTeX.Types (Macro)
199203import Text.HTML.TagSoup.Entity ( lookupEntity )
200204import Text.Pandoc.Asciify (toAsciiChar)
201205import Data.Monoid ((<>))
@@ -242,6 +246,35 @@ instance Monoid a => Monoid (Future s a) where
242246 mappend = liftM2 mappend
243247 mconcat = liftM mconcat . sequence
244248
249+-- | Parse characters while a predicate is true.
250+takeWhileP :: Stream [Char] m Char
251+ => (Char -> Bool) -> ParserT [Char] st m [Char]
252+takeWhileP f = do
253+ -- faster than 'many (satisfy f)'
254+ inp <- getInput
255+ pos <- getPosition
256+ let (xs, rest) = span f inp
257+ -- needed to persuade parsec that this won't match an empty string:
258+ anyChar
259+ setInput rest
260+ setPosition $ updatePosString pos xs
261+ return xs
262+
263+-- Parse n characters of input (or the rest of the input if
264+-- there aren't n characters).
265+takeP :: Stream [Char] m Char => Int -> ParserT [Char] st m [Char]
266+takeP n = do
267+ guard (n > 0)
268+ -- faster than 'count n anyChar'
269+ inp <- getInput
270+ pos <- getPosition
271+ let (xs, rest) = splitAt n inp
272+ -- needed to persuade parsec that this won't match an empty string:
273+ anyChar
274+ setInput rest
275+ setPosition $ updatePosString pos xs
276+ return xs
277+
245278-- | Parse any line of text
246279anyLine :: Stream [Char] m Char => ParserT [Char] st m [Char]
247280anyLine = do
@@ -366,6 +399,7 @@ parseFromString :: Monad m
366399 -> ParserT String st m a
367400parseFromString parser str = do
368401 oldPos <- getPosition
402+ setPosition $ initialPos "chunk"
369403 oldInput <- getInput
370404 setInput str
371405 result <- parser
@@ -993,7 +1027,7 @@ data ParserState = ParserState
9931027 stateIdentifiers :: Set.Set String, -- ^ Header identifiers used
9941028 stateNextExample :: Int, -- ^ Number of next example
9951029 stateExamples :: M.Map String Int, -- ^ Map from example labels to numbers
996− stateMacros :: [Macro], -- ^ List of macros defined so far
1030+ stateMacros :: M.Map Text Macro, -- ^ Table of macros defined so far
9971031 stateRstDefaultRole :: String, -- ^ Current rST default interpreted text role
9981032 stateRstCustomRoles :: M.Map String (String, Maybe String, Attr), -- ^ Current rST custom text roles
9991033 -- Triple represents: 1) Base role, 2) Optional format (only for :raw:
@@ -1056,8 +1090,8 @@ instance HasIdentifierList ParserState where
10561090 updateIdentifierList f st = st{ stateIdentifiers = f $ stateIdentifiers st }
10571091
10581092class HasMacros st where
1059− extractMacros :: st -> [Macro]
1060− updateMacros :: ([Macro] -> [Macro]) -> st -> st
1093+ extractMacros :: st -> M.Map Text Macro
1094+ updateMacros :: (M.Map Text Macro -> M.Map Text Macro) -> st -> st
10611095
10621096instance HasMacros ParserState where
10631097 extractMacros = stateMacros
@@ -1111,7 +1145,7 @@ defaultParserState =
11111145 stateIdentifiers = Set.empty,
11121146 stateNextExample = 1,
11131147 stateExamples = M.empty,
1114− stateMacros = [],
1148+ stateMacros = M.empty,
11151149 stateRstDefaultRole = "title-reference",
11161150 stateRstCustomRoles = M.empty,
11171151 stateCaption = Nothing,
@@ -1340,33 +1374,6 @@ token :: (Stream s m t)
13401374 -> ParsecT s st m a
13411375token pp pos match = tokenPrim pp (\_ t _ -> pos t) match
13421376
1343−--
1344−-- Macros
1345−--
1346−
1347−-- | Parse a \newcommand or \newenviroment macro definition.
1348−macro :: (Stream [Char] m Char, HasMacros st, HasReaderOptions st)
1349− => ParserT [Char] st m Blocks
1350−macro = do
1351− apply <- getOption readerApplyMacros
1352− (m, def') <- withRaw pMacroDefinition
1353− if apply
1354− then do
1355− updateState $ \st -> updateMacros (m:) st
1356− return mempty
1357− else return $ rawBlock "latex" def'
1358−
1359−-- | Apply current macros to string.
1360−applyMacros' :: (HasReaderOptions st, HasMacros st, Stream [Char] m Char)
1361− => String
1362− -> ParserT [Char] st m String
1363−applyMacros' target = do
1364− apply <- getOption readerApplyMacros
1365− if apply
1366− then do macros <- extractMacros <$> getState
1367− return $ applyMacros macros target
1368− else return target
1369−
13701377infixr 5 <+?>
13711378(<+?>) :: (Monoid a) => ParserT s st m a -> ParserT s st m a -> ParserT s st m a
13721379a <+?> b = a >>= flip fmap (try b <|> return mempty) . (<>)
@@ -1384,10 +1391,11 @@ extractIdClass (ident, cls, kvs) = (ident', cls', kvs')
13841391
13851392insertIncludedFile' :: (PandocMonad m, HasIncludeFiles st,
13861393 Functor mf, Applicative mf, Monad mf)
1387− => ParserT String st m (mf Blocks)
1394+ => ParserT [a] st m (mf Blocks)
1395+ -> (String -> [a])
13881396 -> [FilePath] -> FilePath
1389− -> ParserT String st m (mf Blocks)
1390−insertIncludedFile' blocks dirs f = do
1397+ -> ParserT [a] st m (mf Blocks)
1398+insertIncludedFile' blocks totoks dirs f = do
13911399 oldPos <- getPosition
13921400 oldInput <- getInput
13931401 containers <- getIncludeFiles <$> getState
@@ -1401,7 +1409,7 @@ insertIncludedFile' blocks dirs f = do
14011409 report $ CouldNotLoadIncludeFile f oldPos
14021410 return ""
14031411 setPosition $ newPos f 1 1
1404− setInput contents
1412+ setInput $ totoks contents
14051413 bs <- blocks
14061414 setInput oldInput
14071415 setPosition oldPos
@@ -1411,11 +1419,12 @@ insertIncludedFile' blocks dirs f = do
14111419-- | Parse content of include file as blocks. Circular includes result in an
14121420-- @PandocParseError@.
14131421insertIncludedFile :: (PandocMonad m, HasIncludeFiles st)
1414− => ParserT String st m Blocks
1422+ => ParserT [a] st m Blocks
1423+ -> (String -> [a])
14151424 -> [FilePath] -> FilePath
1416− -> ParserT String st m Blocks
1417−insertIncludedFile blocks dirs f =
1418− runIdentity <$> insertIncludedFile' (Identity <$> blocks) dirs f
1425+ -> ParserT [a] st m Blocks
1426+insertIncludedFile blocks totoks dirs f =
1427+ runIdentity <$> insertIncludedFile' (Identity <$> blocks) totoks dirs f
14191428
14201429-- | Parse content of include file as future blocks. Circular includes result in
14211430-- an @PandocParseError@.
@@ -1423,4 +1432,4 @@ insertIncludedFileF :: (PandocMonad m, HasIncludeFiles st)
14231432 => ParserT String st m (Future st Blocks)
14241433 -> [FilePath] -> FilePath
14251434 -> ParserT String st m (Future st Blocks)
1426−insertIncludedFileF = insertIncludedFile'
1435+insertIncludedFileF p = insertIncludedFile' p id
src/Text/Pandoc/Pretty.hs+1−1
@@ -77,7 +77,7 @@ module Text.Pandoc.Pretty (
7777 )
7878
7979where
80−import Control.Monad.State
80+import Control.Monad.State.Strict
8181import Data.Char (isSpace)
8282import Data.Foldable (toList)
8383import Data.List (intersperse)
src/Text/Pandoc/Readers.hs+10−8
@@ -46,6 +46,7 @@ module Text.Pandoc.Readers
4646 , readMarkdown
4747 , readCommonMark
4848 , readMediaWiki
49+ , readVimwiki
4950 , readRST
5051 , readOrg
5152 , readLaTeX
@@ -59,6 +60,7 @@ module Text.Pandoc.Readers
5960 , readTWiki
6061 , readTxt2Tags
6162 , readEPUB
63+ , readMuse
6264 -- * Miscellaneous
6365 , getReader
6466 , getDefaultExtensions
@@ -81,6 +83,8 @@ import Text.Pandoc.Readers.HTML
8183import Text.Pandoc.Readers.LaTeX
8284import Text.Pandoc.Readers.Markdown
8385import Text.Pandoc.Readers.MediaWiki
86+import Text.Pandoc.Readers.Vimwiki
87+import Text.Pandoc.Readers.Muse
8488import Text.Pandoc.Readers.Native
8589import Text.Pandoc.Readers.Odt
8690import Text.Pandoc.Readers.OPML
@@ -113,6 +117,7 @@ readers = [ ("native" , TextReader readNative)
113117 ,("commonmark" , TextReader readCommonMark)
114118 ,("rst" , TextReader readRST)
115119 ,("mediawiki" , TextReader readMediaWiki)
120+ ,("vimwiki" , TextReader readVimwiki)
116121 ,("docbook" , TextReader readDocBook)
117122 ,("opml" , TextReader readOPML)
118123 ,("org" , TextReader readOrg)
@@ -125,22 +130,19 @@ readers = [ ("native" , TextReader readNative)
125130 ,("odt" , ByteStringReader readOdt)
126131 ,("t2t" , TextReader readTxt2Tags)
127132 ,("epub" , ByteStringReader readEPUB)
133+ ,("muse" , TextReader readMuse)
128134 ]
129135
130−-- | Retrieve reader based on formatSpec (format+extensions).
131−getReader :: PandocMonad m => String -> Either String (Reader m)
136+-- | Retrieve reader, extensions based on formatSpec (format+extensions).
137+getReader :: PandocMonad m => String -> Either String (Reader m, Extensions)
132138getReader s =
133139 case parseFormatSpec s of
134140 Left e -> Left $ intercalate "\n" [m | Message m <- errorMessages e]
135141 Right (readerName, setExts) ->
136142 case lookup readerName readers of
137143 Nothing -> Left $ "Unknown reader: " ++ readerName
138− Just (TextReader r) -> Right $ TextReader $ \o ->
139− r o{ readerExtensions = setExts $
140− getDefaultExtensions readerName }
141− Just (ByteStringReader r) -> Right $ ByteStringReader $ \o ->
142− r o{ readerExtensions = setExts $
143− getDefaultExtensions readerName }
144+ Just r -> Right (r, setExts $
145+ getDefaultExtensions readerName)
144146
145147-- | Read pandoc document from JSON format.
146148readJSON :: ReaderOptions -> Text -> Either PandocError Pandoc
src/Text/Pandoc/Readers/DocBook.hs+4−3
@@ -1,6 +1,6 @@
11module Text.Pandoc.Readers.DocBook ( readDocBook ) where
22import Data.Char (toUpper)
3−import Text.Pandoc.Shared (safeRead)
3+import Text.Pandoc.Shared (safeRead, crFilter)
44import Text.Pandoc.Options
55import Text.Pandoc.Definition
66import Text.Pandoc.Builder
@@ -9,7 +9,7 @@ import Text.HTML.TagSoup.Entity (lookupEntity)
99import Data.Either (rights)
1010import Data.Generics
1111import Data.Char (isSpace)
12−import Control.Monad.State
12+import Control.Monad.State.Strict
1313import Data.List (intersperse)
1414import Data.Maybe (fromMaybe)
1515import Text.TeXMath (readMathML, writeTeX)
@@ -526,7 +526,8 @@ instance Default DBState where
526526
527527readDocBook :: PandocMonad m => ReaderOptions -> Text -> m Pandoc
528528readDocBook _ inp = do
529− let tree = normalizeTree . parseXML . handleInstructions $ T.unpack inp
529+ let tree = normalizeTree . parseXML . handleInstructions
530+ $ T.unpack $ crFilter inp
530531 (bs, st') <- flip runStateT (def{ dbContent = tree }) $ mapM parseBlock $ tree
531532 return $ Pandoc (dbMeta st') (toList . mconcat $ bs)
532533
src/Text/Pandoc/Readers/Docx.hs+1−1
@@ -76,7 +76,7 @@ module Text.Pandoc.Readers.Docx
7676
7777import Codec.Archive.Zip
7878import Control.Monad.Reader
79−import Control.Monad.State
79+import Control.Monad.State.Strict
8080import qualified Data.ByteString.Lazy as B
8181import Data.Default (Default)
8282import Data.List (delete, intersect)
src/Text/Pandoc/Readers/Docx/Parse.hs+1−1
@@ -58,7 +58,7 @@ import Codec.Archive.Zip
5858import Control.Applicative ((<|>))
5959import Control.Monad.Except
6060import Control.Monad.Reader
61−import Control.Monad.State
61+import Control.Monad.State.Strict
6262import Data.Bits ((.|.))
6363import qualified Data.ByteString.Lazy as B
6464import Data.Char (chr, isDigit, ord, readLitChar)
src/Text/Pandoc/Readers/Docx/StyleMap.hs+1−1
@@ -7,7 +7,7 @@ module Text.Pandoc.Readers.Docx.StyleMap ( StyleMaps(..)
77 , hasStyleName
88 ) where
99
10−import Control.Monad.State
10+import Control.Monad.State.Strict
1111import Data.Char (toLower)
1212import qualified Data.Map as M
1313import Text.Pandoc.Readers.Docx.Util
src/Text/Pandoc/Readers/HTML.hs+15−5
@@ -45,7 +45,7 @@ import Text.Pandoc.Definition
4545import qualified Text.Pandoc.Builder as B
4646import Text.Pandoc.Builder (Blocks, Inlines, trimInlines, HasMeta(..))
4747import Text.Pandoc.Shared ( extractSpaces, addMetaField
48− , escapeURI, safeRead )
48+ , escapeURI, safeRead, crFilter )
4949import Text.Pandoc.Options (ReaderOptions(readerExtensions), extensionEnabled,
5050 Extension (Ext_epub_html_exts,
5151 Ext_raw_html, Ext_native_divs, Ext_native_spans))
@@ -53,6 +53,7 @@ import Text.Pandoc.Logging
5353import Text.Pandoc.Parsing hiding ((<|>))
5454import Text.Pandoc.Walk
5555import qualified Data.Map as M
56+import Data.Foldable ( for_ )
5657import Data.Maybe ( fromMaybe, isJust)
5758import Data.List ( intercalate, isPrefixOf )
5859import Data.Char ( isDigit, isLetter, isAlphaNum )
@@ -71,7 +72,7 @@ import Data.Monoid ((<>))
7172import Text.Parsec.Error
7273import qualified Data.Set as Set
7374import Text.Pandoc.Error
74−import Text.Pandoc.Class (PandocMonad, report)
75+import Text.Pandoc.Class (PandocMonad(..))
7576import Control.Monad.Except (throwError)
7677
7778-- | Convert HTML-formatted string to 'Pandoc' document.
@@ -82,7 +83,7 @@ readHtml :: PandocMonad m
8283readHtml opts inp = do
8384 let tags = stripPrefixes . canonicalizeTags $
8485 parseTagsOptions parseOptions{ optTagPosition = True }
85− inp
86+ (crFilter inp)
8687 parseDoc = do
8788 blocks <- (fixPlains False) . mconcat <$> manyTill block eof
8889 meta <- stateMeta . parserState <$> getState
@@ -134,6 +135,13 @@ type HTMLParser m s = ParserT s HTMLState (ReaderT HTMLLocal m)
134135
135136type TagParser m = HTMLParser m [Tag Text]
136137
138+pHtml :: PandocMonad m => TagParser m Blocks
139+pHtml = try $ do
140+ (TagOpen "html" attr) <- lookAhead $ pAnyTag
141+ for_ (lookup "lang" attr) $
142+ updateState . B.setMeta "lang" . B.text . T.unpack
143+ pInTags "html" block
144+
137145pBody :: PandocMonad m => TagParser m Blocks
138146pBody = pInTags "body" block
139147
@@ -162,7 +170,6 @@ pHead = pInTags "head" $ pTitle <|> pMetaTag <|> pBaseTag <|> (mempty <$ pAnyTag
162170
163171block :: PandocMonad m => TagParser m Blocks
164172block = do
165− pos <- getPosition
166173 res <- choice
167174 [ eSection
168175 , eSwitch B.para block
@@ -176,13 +183,14 @@ block = do
176183 , pList
177184 , pHrule
178185 , pTable
186+ , pHtml
179187 , pHead
180188 , pBody
181189 , pDiv
182190 , pPlain
183191 , pRawHtmlBlock
184192 ]
185− report $ ParsingTrace (take 60 $ show $ B.toList res) pos
193+ trace (take 60 $ show $ B.toList res)
186194 return res
187195
188196namespaces :: PandocMonad m => [(String, TagParser m Inlines)]
@@ -797,6 +805,8 @@ pCloses tagtype = try $ do
797805 (TagClose "dl") | tagtype == "dd" -> return ()
798806 (TagClose "table") | tagtype == "td" -> return ()
799807 (TagClose "table") | tagtype == "tr" -> return ()
808+ (TagClose t') | tagtype == "p" && t' `Set.member` blockHtmlTags
809+ -> return () -- see #3794
800810 _ -> mzero
801811
802812pTagText :: PandocMonad m => TagParser m Inlines
src/Text/Pandoc/Readers/Haddock.hs+2−2
@@ -27,7 +27,7 @@ import Text.Pandoc.Class (PandocMonad)
2727import Text.Pandoc.Definition
2828import Text.Pandoc.Error
2929import Text.Pandoc.Options
30−import Text.Pandoc.Shared (splitBy, trim)
30+import Text.Pandoc.Shared (splitBy, trim, crFilter)
3131
3232
3333-- | Parse Haddock markup and return a 'Pandoc' document.
@@ -35,7 +35,7 @@ readHaddock :: PandocMonad m
3535 => ReaderOptions
3636 -> Text
3737 -> m Pandoc
38−readHaddock opts s = case readHaddockEither opts (unpack s) of
38+readHaddock opts s = case readHaddockEither opts (unpack (crFilter s)) of
3939 Right result -> return result
4040 Left e -> throwError e
4141
src/Text/Pandoc/Readers/LaTeX.hs+1687−1119
@@ -1,4 +1,7 @@
11{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE PatternGuards #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE MultiParamTypeClasses #-}
25{-# LANGUAGE ScopedTypeVariables #-}
36{-
47Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu>
@@ -28,20 +31,26 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2831 Portability : portable
2932
3033Conversion of LaTeX to 'Pandoc' document.
34+
3135-}
3236module Text.Pandoc.Readers.LaTeX ( readLaTeX,
37+ applyMacros,
3338 rawLaTeXInline,
3439 rawLaTeXBlock,
35− inlineCommand,
40+ macro,
41+ inlineCommand
3642 ) where
3743
3844import Control.Applicative (many, optional, (<|>))
3945import Control.Monad
4046import Control.Monad.Except (throwError)
41−import Data.Char (chr, isAlphaNum, isLetter, ord)
42−import Data.Text (Text, unpack)
47+import Data.Char (chr, isAlphaNum, isLetter, ord, isDigit)
48+import Data.Default
49+import Data.Text (Text)
50+import qualified Data.Text as T
4351import Data.List (intercalate, isPrefixOf)
4452import qualified Data.Map as M
53+import qualified Data.Set as Set
4554import Data.Maybe (fromMaybe, maybeToList)
4655import Safe (minimumDef)
4756import System.FilePath (addExtension, replaceExtension, takeExtension)
@@ -52,10 +61,19 @@ import Text.Pandoc.Highlighting (fromListingsLanguage, languagesByExtension)
5261import Text.Pandoc.ImageSize (numUnit, showFl)
5362import Text.Pandoc.Logging
5463import Text.Pandoc.Options
55−import Text.Pandoc.Parsing hiding (many, mathDisplay, mathInline, optional,
56− space, (<|>))
64+import Text.Pandoc.Parsing hiding (many, optional, withRaw,
65+ mathInline, mathDisplay,
66+ space, (<|>), spaces, blankline)
5767import Text.Pandoc.Shared
68+import Text.Pandoc.Readers.LaTeX.Types (Macro(..), Tok(..),
69+ TokType(..))
5870import Text.Pandoc.Walk
71+import Text.Pandoc.Error (PandocError(PandocParsecError, PandocMacroLoop))
72+
73+-- for debugging:
74+-- import Text.Pandoc.Extensions (getDefaultExtensions)
75+-- import Text.Pandoc.Class (runIOorExplode, PandocIO)
76+-- import Debug.Trace (traceShowId)
5977
6078-- | Parse LaTeX from string and return 'Pandoc' document.
6179readLaTeX :: PandocMonad m
@@ -63,17 +81,18 @@ readLaTeX :: PandocMonad m
6381 -> Text -- ^ String to parse (assumes @'\n'@ line endings)
6482 -> m Pandoc
6583readLaTeX opts ltx = do
66− parsed <- readWithM parseLaTeX def{ stateOptions = opts } (unpack ltx)
84+ parsed <- runParserT parseLaTeX def{ sOptions = opts } "source"
85+ (tokenize (crFilter ltx))
6786 case parsed of
6887 Right result -> return result
69− Left e -> throwError e
88+ Left e -> throwError $ PandocParsecError (T.unpack ltx) e
7089
7190parseLaTeX :: PandocMonad m => LP m Pandoc
7291parseLaTeX = do
7392 bs <- blocks
7493 eof
7594 st <- getState
76− let meta = stateMeta st
95+ let meta = sMeta st
7796 let doc' = doc bs
7897 let headerLevel (Header n _ _) = [n]
7998 headerLevel _ = []
@@ -87,177 +106,476 @@ parseLaTeX = do
87106 else id) doc'
88107 return $ Pandoc meta bs'
89108
90−type LP m = ParserT String ParserState m
91−
92−anyControlSeq :: PandocMonad m => LP m String
93−anyControlSeq = do
94− char '\\'
95− next <- option '\n' anyChar
96− case next of
97− '\n' -> return ""
98− c | isLetter c -> (c:) <$> (many letter <* optional sp)
99− | otherwise -> return [c]
100−
101−controlSeq :: PandocMonad m => String -> LP m String
102−controlSeq name = try $ do
103− char '\\'
104− case name of
105− "" -> mzero
106− [c] | not (isLetter c) -> string [c]
107− cs -> string cs <* notFollowedBy letter <* optional sp
108− return name
109−
110−dimenarg :: PandocMonad m => LP m String
111−dimenarg = try $ do
112− ch <- option "" $ string "="
113− num <- many1 digit
114− dim <- oneOfStrings ["pt","pc","in","bp","cm","mm","dd","cc","sp"]
115− return $ ch ++ num ++ dim
109+-- testParser :: LP PandocIO a -> Text -> IO a
110+-- testParser p t = do
111+-- res <- runIOorExplode (runParserT p defaultLaTeXState{
112+-- sOptions = def{ readerExtensions =
113+-- enableExtension Ext_raw_tex $
114+-- getDefaultExtensions "latex" }} "source" (tokenize t))
115+-- case res of
116+-- Left e -> error (show e)
117+-- Right r -> return r
118+
119+data LaTeXState = LaTeXState{ sOptions :: ReaderOptions
120+ , sMeta :: Meta
121+ , sQuoteContext :: QuoteContext
122+ , sMacros :: M.Map Text Macro
123+ , sContainers :: [String]
124+ , sHeaders :: M.Map Inlines String
125+ , sLogMessages :: [LogMessage]
126+ , sIdentifiers :: Set.Set String
127+ , sVerbatimMode :: Bool
128+ , sCaption :: Maybe Inlines
129+ , sInListItem :: Bool
130+ , sInTableCell :: Bool
131+ }
132+ deriving Show
133+
134+defaultLaTeXState :: LaTeXState
135+defaultLaTeXState = LaTeXState{ sOptions = def
136+ , sMeta = nullMeta
137+ , sQuoteContext = NoQuote
138+ , sMacros = M.empty
139+ , sContainers = []
140+ , sHeaders = M.empty
141+ , sLogMessages = []
142+ , sIdentifiers = Set.empty
143+ , sVerbatimMode = False
144+ , sCaption = Nothing
145+ , sInListItem = False
146+ , sInTableCell = False
147+ }
148+
149+instance PandocMonad m => HasQuoteContext LaTeXState m where
150+ getQuoteContext = sQuoteContext <$> getState
151+ withQuoteContext context parser = do
152+ oldState <- getState
153+ let oldQuoteContext = sQuoteContext oldState
154+ setState oldState { sQuoteContext = context }
155+ result <- parser
156+ newState <- getState
157+ setState newState { sQuoteContext = oldQuoteContext }
158+ return result
159+
160+instance HasLogMessages LaTeXState where
161+ addLogMessage msg st = st{ sLogMessages = msg : sLogMessages st }
162+ getLogMessages st = reverse $ sLogMessages st
163+
164+instance HasIdentifierList LaTeXState where
165+ extractIdentifierList = sIdentifiers
166+ updateIdentifierList f st = st{ sIdentifiers = f $ sIdentifiers st }
167+
168+instance HasIncludeFiles LaTeXState where
169+ getIncludeFiles = sContainers
170+ addIncludeFile f s = s{ sContainers = f : sContainers s }
171+ dropLatestIncludeFile s = s { sContainers = drop 1 $ sContainers s }
172+
173+instance HasHeaderMap LaTeXState where
174+ extractHeaderMap = sHeaders
175+ updateHeaderMap f st = st{ sHeaders = f $ sHeaders st }
176+
177+instance HasMacros LaTeXState where
178+ extractMacros st = sMacros st
179+ updateMacros f st = st{ sMacros = f (sMacros st) }
180+
181+instance HasReaderOptions LaTeXState where
182+ extractReaderOptions = sOptions
183+
184+instance HasMeta LaTeXState where
185+ setMeta field val st =
186+ st{ sMeta = setMeta field val $ sMeta st }
187+ deleteMeta field st =
188+ st{ sMeta = deleteMeta field $ sMeta st }
189+
190+instance Default LaTeXState where
191+ def = defaultLaTeXState
192+
193+type LP m = ParserT [Tok] LaTeXState m
194+
195+withVerbatimMode :: PandocMonad m => LP m a -> LP m a
196+withVerbatimMode parser = do
197+ updateState $ \st -> st{ sVerbatimMode = True }
198+ result <- parser
199+ updateState $ \st -> st{ sVerbatimMode = False }
200+ return result
201+
202+rawLaTeXBlock :: (PandocMonad m, HasMacros s, HasReaderOptions s)
203+ => ParserT String s m String
204+rawLaTeXBlock = do
205+ lookAhead (try (char '\\' >> letter))
206+ inp <- getInput
207+ let toks = tokenize $ T.pack inp
208+ let rawblock = do
209+ (_, raw) <- try $
210+ withRaw (environment <|> macroDef <|> blockCommand)
211+ return raw
212+ pstate <- getState
213+ let lstate = def{ sOptions = extractReaderOptions pstate }
214+ res <- runParserT rawblock lstate "source" toks
215+ case res of
216+ Left _ -> mzero
217+ Right raw -> takeP (T.length (untokenize raw))
218+
219+macro :: (PandocMonad m, HasMacros s, HasReaderOptions s)
220+ => ParserT String s m Blocks
221+macro = do
222+ guardEnabled Ext_latex_macros
223+ lookAhead (char '\\' *> oneOfStrings ["new", "renew", "provide"] *>
224+ oneOfStrings ["command", "environment"])
225+ inp <- getInput
226+ let toks = tokenize $ T.pack inp
227+ let rawblock = do
228+ (_, raw) <- withRaw $ try macroDef
229+ st <- getState
230+ return (raw, st)
231+ pstate <- getState
232+ let lstate = def{ sOptions = extractReaderOptions pstate
233+ , sMacros = extractMacros pstate }
234+ res <- runParserT rawblock lstate "source" toks
235+ case res of
236+ Left _ -> mzero
237+ Right (raw, st) -> do
238+ updateState (updateMacros (const $ sMacros st))
239+ mempty <$ takeP (T.length (untokenize raw))
240+
241+applyMacros :: (PandocMonad m, HasMacros s, HasReaderOptions s)
242+ => String -> ParserT String s m String
243+applyMacros s = do
244+ (guardEnabled Ext_latex_macros >>
245+ do let retokenize = doMacros 0 *> (toksToString <$> getInput)
246+ pstate <- getState
247+ let lstate = def{ sOptions = extractReaderOptions pstate
248+ , sMacros = extractMacros pstate }
249+ res <- runParserT retokenize lstate "math" (tokenize (T.pack s))
250+ case res of
251+ Left e -> fail (show e)
252+ Right s' -> return s') <|> return s
253+
254+rawLaTeXInline :: (PandocMonad m, HasMacros s, HasReaderOptions s)
255+ => ParserT String s m String
256+rawLaTeXInline = do
257+ lookAhead (try (char '\\' >> letter) <|> char '$')
258+ inp <- getInput
259+ let toks = tokenize $ T.pack inp
260+ let rawinline = do
261+ (_, raw) <- try $ withRaw (inlineEnvironment <|> inlineCommand')
262+ st <- getState
263+ return (raw, st)
264+ pstate <- getState
265+ let lstate = def{ sOptions = extractReaderOptions pstate
266+ , sMacros = extractMacros pstate }
267+ res <- runParserT rawinline lstate "source" toks
268+ case res of
269+ Left _ -> mzero
270+ Right (raw, s) -> do
271+ updateState $ updateMacros (const $ sMacros s)
272+ takeP (T.length (untokenize raw))
273+
274+inlineCommand :: PandocMonad m => ParserT String ParserState m Inlines
275+inlineCommand = do
276+ lookAhead (try (char '\\' >> letter) <|> char '$')
277+ inp <- getInput
278+ let toks = tokenize $ T.pack inp
279+ let rawinline = do
280+ (il, raw) <- try $ withRaw (inlineEnvironment <|> inlineCommand')
281+ st <- getState
282+ return (il, raw, st)
283+ pstate <- getState
284+ let lstate = def{ sOptions = extractReaderOptions pstate
285+ , sMacros = extractMacros pstate }
286+ res <- runParserT rawinline lstate "source" toks
287+ case res of
288+ Left _ -> mzero
289+ Right (il, raw, s) -> do
290+ updateState $ updateMacros (const $ sMacros s)
291+ takeP (T.length (untokenize raw))
292+ return il
293+
294+tokenize :: Text -> [Tok]
295+tokenize = totoks (1, 1)
296+
297+totoks :: (Line, Column) -> Text -> [Tok]
298+totoks (lin,col) t =
299+ case T.uncons t of
300+ Nothing -> []
301+ Just (c, rest)
302+ | c == '\n' ->
303+ Tok (lin, col) Newline "\n"
304+ : totoks (lin + 1,1) rest
305+ | isSpaceOrTab c ->
306+ let (sps, rest') = T.span isSpaceOrTab t
307+ in Tok (lin, col) Spaces sps
308+ : totoks (lin, col + T.length sps) rest'
309+ | isAlphaNum c ->
310+ let (ws, rest') = T.span isAlphaNum t
311+ in Tok (lin, col) Word ws
312+ : totoks (lin, col + T.length ws) rest'
313+ | c == '%' ->
314+ let (cs, rest') = T.break (== '\n') rest
315+ in Tok (lin, col) Comment ("%" <> cs)
316+ : totoks (lin, col + 1 + T.length cs) rest'
317+ | c == '\\' ->
318+ case T.uncons rest of
319+ Nothing -> [Tok (lin, col) Symbol (T.singleton c)]
320+ Just (d, rest')
321+ | isLetter d ->
322+ let (ws, rest'') = T.span isLetter rest
323+ (ss, rest''') = T.span isSpaceOrTab rest''
324+ in Tok (lin, col) (CtrlSeq ws) ("\\" <> ws <> ss)
325+ : totoks (lin,
326+ col + 1 + T.length ws + T.length ss) rest'''
327+ | d == '\t' || d == '\n' ->
328+ Tok (lin, col) Symbol ("\\")
329+ : totoks (lin, col + 1) rest
330+ | otherwise ->
331+ Tok (lin, col) (CtrlSeq (T.singleton d)) (T.pack [c,d])
332+ : totoks (lin, col + 2) rest'
333+ | c == '#' ->
334+ let (t1, t2) = T.span (\d -> d >= '0' && d <= '9') rest
335+ in case safeRead (T.unpack t1) of
336+ Just i ->
337+ Tok (lin, col) (Arg i) ("#" <> t1)
338+ : totoks (lin, col + 1 + T.length t1) t2
339+ Nothing ->
340+ Tok (lin, col) Symbol ("#")
341+ : totoks (lin, col + 1) t2
342+ | c == '^' ->
343+ case T.uncons rest of
344+ Just ('^', rest') ->
345+ case T.uncons rest' of
346+ Just (d, rest'')
347+ | isLowerHex d ->
348+ case T.uncons rest'' of
349+ Just (e, rest''') | isLowerHex e ->
350+ Tok (lin, col) Esc2 (T.pack ['^','^',d,e])
351+ : totoks (lin, col + 4) rest'''
352+ _ ->
353+ Tok (lin, col) Esc1 (T.pack ['^','^',d])
354+ : totoks (lin, col + 3) rest''
355+ | d < '\128' ->
356+ Tok (lin, col) Esc1 (T.pack ['^','^',d])
357+ : totoks (lin, col + 3) rest''
358+ _ -> [Tok (lin, col) Symbol ("^"),
359+ Tok (lin, col + 1) Symbol ("^")]
360+ _ -> Tok (lin, col) Symbol ("^")
361+ : totoks (lin, col + 1) rest
362+ | otherwise ->
363+ Tok (lin, col) Symbol (T.singleton c) : totoks (lin, col + 1) rest
364+
365+ where isSpaceOrTab ' ' = True
366+ isSpaceOrTab '\t' = True
367+ isSpaceOrTab _ = False
368+
369+isLowerHex :: Char -> Bool
370+isLowerHex x = x >= '0' && x <= '9' || x >= 'a' && x <= 'f'
371+
372+untokenize :: [Tok] -> Text
373+untokenize = mconcat . map untoken
374+
375+untoken :: Tok -> Text
376+untoken (Tok _ _ t) = t
377+
378+satisfyTok :: PandocMonad m => (Tok -> Bool) -> LP m Tok
379+satisfyTok f =
380+ try $ do
381+ res <- tokenPrim (T.unpack . untoken) updatePos matcher
382+ doMacros 0 -- apply macros on remaining input stream
383+ return res
384+ where matcher t | f t = Just t
385+ | otherwise = Nothing
386+ updatePos :: SourcePos -> Tok -> [Tok] -> SourcePos
387+ updatePos spos _ (Tok (lin,col) _ _ : _) =
388+ setSourceColumn (setSourceLine spos lin) col
389+ updatePos spos _ [] = spos
390+
391+doMacros :: PandocMonad m => Int -> LP m ()
392+doMacros n = do
393+ verbatimMode <- sVerbatimMode <$> getState
394+ when (not verbatimMode) $ do
395+ inp <- getInput
396+ case inp of
397+ Tok spos (CtrlSeq "begin") _ : Tok _ Symbol "{" :
398+ Tok _ Word name : Tok _ Symbol "}" : ts
399+ -> handleMacros spos name ts
400+ Tok spos (CtrlSeq "end") _ : Tok _ Symbol "{" :
401+ Tok _ Word name : Tok _ Symbol "}" : ts
402+ -> handleMacros spos ("end" <> name) ts
403+ Tok spos (CtrlSeq name) _ : ts
404+ -> handleMacros spos name ts
405+ _ -> return ()
406+ where handleMacros spos name ts = do
407+ macros <- sMacros <$> getState
408+ case M.lookup name macros of
409+ Nothing -> return ()
410+ Just (Macro numargs optarg newtoks) -> do
411+ setInput ts
412+ let getarg = spaces >> braced
413+ args <- case optarg of
414+ Nothing -> count numargs getarg
415+ Just o ->
416+ (:) <$> option o bracketedToks
417+ <*> count (numargs - 1) getarg
418+ let addTok (Tok _ (Arg i) _) acc | i > 0
419+ , i <= numargs =
420+ map (setpos spos) (args !! (i - 1)) ++ acc
421+ addTok t acc = setpos spos t : acc
422+ ts' <- getInput
423+ setInput $ foldr addTok ts' newtoks
424+ if n > 20 -- detect macro expansion loops
425+ then throwError $ PandocMacroLoop (T.unpack name)
426+ else doMacros (n + 1)
427+
428+setpos :: (Line, Column) -> Tok -> Tok
429+setpos spos (Tok _ tt txt) = Tok spos tt txt
430+
431+anyControlSeq :: PandocMonad m => LP m Tok
432+anyControlSeq = satisfyTok isCtrlSeq
433+ where isCtrlSeq (Tok _ (CtrlSeq _) _) = True
434+ isCtrlSeq _ = False
435+
436+anySymbol :: PandocMonad m => LP m Tok
437+anySymbol = satisfyTok isSym
438+ where isSym (Tok _ Symbol _) = True
439+ isSym _ = False
440+
441+spaces :: PandocMonad m => LP m ()
442+spaces = skipMany (satisfyTok (tokTypeIn [Comment, Spaces, Newline]))
443+
444+spaces1 :: PandocMonad m => LP m ()
445+spaces1 = skipMany1 (satisfyTok (tokTypeIn [Comment, Spaces, Newline]))
446+
447+tokTypeIn :: [TokType] -> Tok -> Bool
448+tokTypeIn toktypes (Tok _ tt _) = tt `elem` toktypes
449+
450+controlSeq :: PandocMonad m => Text -> LP m Tok
451+controlSeq name = satisfyTok isNamed
452+ where isNamed (Tok _ (CtrlSeq n) _) = n == name
453+ isNamed _ = False
454+
455+symbol :: PandocMonad m => Char -> LP m Tok
456+symbol c = satisfyTok isc
457+ where isc (Tok _ Symbol d) = case T.uncons d of
458+ Just (c',_) -> c == c'
459+ _ -> False
460+ isc _ = False
461+
462+symbolIn :: PandocMonad m => [Char] -> LP m Tok
463+symbolIn cs = satisfyTok isInCs
464+ where isInCs (Tok _ Symbol d) = case T.uncons d of
465+ Just (c,_) -> c `elem` cs
466+ _ -> False
467+ isInCs _ = False
116468
117469sp :: PandocMonad m => LP m ()
118470sp = whitespace <|> endline
119471
120472whitespace :: PandocMonad m => LP m ()
121−whitespace = skipMany1 $ satisfy (\c -> c == ' ' || c == '\t')
473+whitespace = () <$ satisfyTok isSpaceTok
474+ where isSpaceTok (Tok _ Spaces _) = True
475+ isSpaceTok _ = False
122476
123−endline :: PandocMonad m => LP m ()
124−endline = try (newline >> lookAhead anyChar >> notFollowedBy blankline)
125−
126−isLowerHex :: Char -> Bool
127−isLowerHex x = x >= '0' && x <= '9' || x >= 'a' && x <= 'f'
477+newlineTok :: PandocMonad m => LP m ()
478+newlineTok = () <$ satisfyTok isNewlineTok
128479
129−tildeEscape :: PandocMonad m => LP m Char
130−tildeEscape = try $ do
131− string "^^"
132− c <- satisfy (\x -> x >= '\0' && x <= '\128')
133− d <- if isLowerHex c
134− then option "" $ count 1 (satisfy isLowerHex)
135− else return ""
136− if null d
137− then case ord c of
138− x | x >= 64 && x <= 127 -> return $ chr (x - 64)
139− | otherwise -> return $ chr (x + 64)
140− else return $ chr $ read ('0':'x':c:d)
480+isNewlineTok :: Tok -> Bool
481+isNewlineTok (Tok _ Newline _) = True
482+isNewlineTok _ = False
141483
142484comment :: PandocMonad m => LP m ()
143−comment = do
144− char '%'
145− skipMany (satisfy (/='\n'))
146− optional newline
147− return ()
485+comment = () <$ satisfyTok isCommentTok
486+ where isCommentTok (Tok _ Comment _) = True
487+ isCommentTok _ = False
488+
489+anyTok :: PandocMonad m => LP m Tok
490+anyTok = satisfyTok (const True)
148491
149−bgroup :: PandocMonad m => LP m ()
492+endline :: PandocMonad m => LP m ()
493+endline = try $ do
494+ newlineTok
495+ lookAhead anyTok
496+ notFollowedBy blankline
497+
498+blankline :: PandocMonad m => LP m ()
499+blankline = try $ skipMany whitespace *> newlineTok
500+
501+primEscape :: PandocMonad m => LP m Char
502+primEscape = do
503+ Tok _ toktype t <- satisfyTok (tokTypeIn [Esc1, Esc2])
504+ case toktype of
505+ Esc1 -> case T.uncons (T.drop 2 t) of
506+ Just (c, _)
507+ | c >= '\64' && c <= '\127' -> return (chr (ord c - 64))
508+ | otherwise -> return (chr (ord c + 64))
509+ Nothing -> fail "Empty content of Esc1"
510+ Esc2 -> case safeRead ('0':'x':T.unpack (T.drop 2 t)) of
511+ Just x -> return (chr x)
512+ Nothing -> fail $ "Could not read: " ++ T.unpack t
513+ _ -> fail "Expected an Esc1 or Esc2 token" -- should not happen
514+
515+bgroup :: PandocMonad m => LP m Tok
150516bgroup = try $ do
151− skipMany (spaceChar <|> try (newline <* notFollowedBy blankline))
152− () <$ char '{'
153− <|> () <$ controlSeq "bgroup"
154− <|> () <$ controlSeq "begingroup"
517+ skipMany sp
518+ symbol '{' <|> controlSeq "bgroup" <|> controlSeq "begingroup"
155519
156−egroup :: PandocMonad m => LP m ()
157−egroup = () <$ char '}'
158− <|> () <$ controlSeq "egroup"
159− <|> () <$ controlSeq "endgroup"
520+egroup :: PandocMonad m => LP m Tok
521+egroup = (symbol '}' <|> controlSeq "egroup" <|> controlSeq "endgroup")
160522
161−grouped :: PandocMonad m => Monoid a => LP m a -> LP m a
523+grouped :: (PandocMonad m, Monoid a) => LP m a -> LP m a
162524grouped parser = try $ do
163525 bgroup
164526 -- first we check for an inner 'grouped', because
165527 -- {{a,b}} should be parsed the same as {a,b}
166− try (grouped parser <* egroup)
167− <|> (mconcat <$> manyTill parser egroup)
168−
169−braced :: PandocMonad m => LP m String
170−braced = grouped chunk
171− where chunk =
172− many1 (satisfy (\c -> c /= '\\' && c /= '}' && c /= '{'))
173− <|> try (string "\\}")
174− <|> try (string "\\{")
175− <|> try (string "\\\\")
176− <|> ((\x -> "{" ++ x ++ "}") <$> braced)
177− <|> count 1 anyChar
528+ try (grouped parser <* egroup) <|> (mconcat <$> manyTill parser egroup)
529+
530+braced :: PandocMonad m => LP m [Tok]
531+braced = bgroup *> braced' 1
532+ where braced' (n :: Int) =
533+ handleEgroup n <|> handleBgroup n <|> handleOther n
534+ handleEgroup n = do
535+ t <- egroup
536+ if n == 1
537+ then return []
538+ else (t:) <$> braced' (n - 1)
539+ handleBgroup n = do
540+ t <- bgroup
541+ (t:) <$> braced' (n + 1)
542+ handleOther n = do
543+ t <- anyTok
544+ (t:) <$> braced' n
178545
179546bracketed :: PandocMonad m => Monoid a => LP m a -> LP m a
180−bracketed parser = try $ char '[' *> (mconcat <$> manyTill parser (char ']'))
181−
182−mathDisplay :: PandocMonad m => LP m String -> LP m Inlines
183−mathDisplay p = displayMath <$> (try p >>= applyMacros' . trim)
184−
185−mathInline :: PandocMonad m => LP m String -> LP m Inlines
186−mathInline p = math <$> (try p >>= applyMacros')
187−
188−mathChars :: PandocMonad m => LP m String
189−mathChars =
190− concat <$> many (escapedChar
191− <|> (snd <$> withRaw braced)
192− <|> many1 (satisfy isOrdChar))
193− where escapedChar = try $ do char '\\'
194− c <- anyChar
195− return ['\\',c]
196− isOrdChar '$' = False
197− isOrdChar '{' = False
198− isOrdChar '}' = False
199− isOrdChar '\\' = False
200− isOrdChar _ = True
201−
202−quoted' :: PandocMonad m => (Inlines -> Inlines) -> LP m String -> LP m () -> LP m Inlines
203−quoted' f starter ender = do
204− startchs <- starter
205− smart <- extensionEnabled Ext_smart <$> getOption readerExtensions
206− if smart
207− then do
208− ils <- many (notFollowedBy ender >> inline)
209− (ender >> return (f (mconcat ils))) <|>
210− (<> mconcat ils) <$>
211− lit (case startchs of
212− "``" -> "“"
213− "`" -> "‘"
214− _ -> startchs)
215− else lit startchs
547+bracketed parser = try $ do
548+ symbol '['
549+ mconcat <$> manyTill parser (symbol ']')
216550
217−doubleQuote :: PandocMonad m => LP m Inlines
218−doubleQuote = do
219− quoted' doubleQuoted (try $ string "``") (void $ try $ string "''")
220− <|> quoted' doubleQuoted (string "“") (void $ char '”')
221− -- the following is used by babel for localized quotes:
222− <|> quoted' doubleQuoted (try $ string "\"`") (void $ try $ string "\"'")
223− <|> quoted' doubleQuoted (string "\"") (void $ char '"')
551+dimenarg :: PandocMonad m => LP m Text
552+dimenarg = try $ do
553+ ch <- option False $ True <$ symbol '='
554+ Tok _ _ s <- satisfyTok isWordTok
555+ guard $ (T.take 2 (T.reverse s)) `elem`
556+ ["pt","pc","in","bp","cm","mm","dd","cc","sp"]
557+ let num = T.take (T.length s - 2) s
558+ guard $ T.length num > 0
559+ guard $ T.all isDigit num
560+ return $ T.pack ['=' | ch] <> s
224561
225−singleQuote :: PandocMonad m => LP m Inlines
226−singleQuote = do
227− smart <- extensionEnabled Ext_smart <$> getOption readerExtensions
228− if smart
229− then quoted' singleQuoted (string "`") (try $ char '\'' >> notFollowedBy letter)
230− <|> quoted' singleQuoted (string "‘") (try $ char '’' >> notFollowedBy letter)
231− else str <$> many1 (oneOf "`\'‘’")
562+-- inline elements:
232563
233−inline :: PandocMonad m => LP m Inlines
234−inline = (mempty <$ comment)
235− <|> (space <$ whitespace)
236− <|> (softbreak <$ endline)
237− <|> inlineText
238− <|> inlineCommand
239− <|> inlineEnvironment
240− <|> inlineGroup
241− <|> (char '-' *> option (str "-")
242− (char '-' *> option (str "–") (str "—" <$ char '-')))
243− <|> doubleQuote
244− <|> singleQuote
245− <|> (str "”" <$ try (string "''"))
246− <|> (str "”" <$ char '”')
247− <|> (str "’" <$ char '\'')
248− <|> (str "’" <$ char '’')
249− <|> (str "\160" <$ char '~')
250− <|> mathDisplay (string "$$" *> mathChars <* string "$$")
251− <|> mathInline (char '$' *> mathChars <* char '$')
252− <|> (guardEnabled Ext_literate_haskell *> char '|' *> doLHSverb)
253− <|> (str . (:[]) <$> tildeEscape)
254− <|> (do res <- oneOf "#&~^'`\"[]"
255− pos <- getPosition
256− report $ ParsingUnescaped [res] pos
257− return $ str [res])
564+word :: PandocMonad m => LP m Inlines
565+word = (str . T.unpack . untoken) <$> satisfyTok isWordTok
258566
259−inlines :: PandocMonad m => LP m Inlines
260−inlines = mconcat <$> many (notFollowedBy (char '}') *> inline)
567+regularSymbol :: PandocMonad m => LP m Inlines
568+regularSymbol = (str . T.unpack . untoken) <$> satisfyTok isRegularSymbol
569+ where isRegularSymbol (Tok _ Symbol t) = not $ T.any isSpecial t
570+ isRegularSymbol _ = False
571+ isSpecial c = c `Set.member` specialChars
572+
573+specialChars :: Set.Set Char
574+specialChars = Set.fromList "#$%&~_^\\{}"
575+
576+isWordTok :: Tok -> Bool
577+isWordTok (Tok _ Word _) = True
578+isWordTok _ = False
261579
262580inlineGroup :: PandocMonad m => LP m Inlines
263581inlineGroup = do
@@ -268,248 +586,562 @@ inlineGroup = do
268586 -- we need the span so we can detitlecase bibtex entries;
269587 -- we need to know when something is {C}apitalized
270588
271−block :: PandocMonad m => LP m Blocks
272−block = (mempty <$ comment)
273− <|> (mempty <$ ((spaceChar <|> newline) *> spaces))
274− <|> environment
275− <|> include
276− <|> macro
277− <|> blockCommand
278− <|> paragraph
279− <|> grouped block
589+doLHSverb :: PandocMonad m => LP m Inlines
590+doLHSverb =
591+ (codeWith ("",["haskell"],[]) . T.unpack . untokenize)
592+ <$> manyTill (satisfyTok (not . isNewlineTok)) (symbol '|')
280593
281−blocks :: PandocMonad m => LP m Blocks
282−blocks = mconcat <$> many block
594+mkImage :: PandocMonad m => [(String, String)] -> String -> LP m Inlines
595+mkImage options src = do
596+ let replaceTextwidth (k,v) =
597+ case numUnit v of
598+ Just (num, "\\textwidth") -> (k, showFl (num * 100) ++ "%")
599+ _ -> (k, v)
600+ let kvs = map replaceTextwidth
601+ $ filter (\(k,_) -> k `elem` ["width", "height"]) options
602+ let attr = ("",[], kvs)
603+ let alt = str "image"
604+ case takeExtension src of
605+ "" -> do
606+ defaultExt <- getOption readerDefaultImageExtension
607+ return $ imageWith attr (addExtension src defaultExt) "" alt
608+ _ -> return $ imageWith attr src "" alt
283609
284−getRawCommand :: PandocMonad m => String -> LP m String
285−getRawCommand name' = do
286− rawargs <- withRaw (many (try (optional sp *> opt)) *>
287− option "" (try (optional sp *> dimenarg)) *>
288− many braced)
289− return $ '\\' : name' ++ snd rawargs
610+-- converts e.g. \SI{1}[\$]{} to "$ 1" or \SI{1}{\euro} to "1 €"
611+dosiunitx :: PandocMonad m => LP m Inlines
612+dosiunitx = do
613+ skipopts
614+ value <- tok
615+ valueprefix <- option "" $ bracketed tok
616+ unit <- tok
617+ let emptyOr160 "" = ""
618+ emptyOr160 _ = "\160"
619+ return . mconcat $ [valueprefix,
620+ emptyOr160 valueprefix,
621+ value,
622+ emptyOr160 unit,
623+ unit]
290624
291−lookupListDefault :: (Ord k) => v -> [k] -> M.Map k v -> v
292−lookupListDefault d = (fromMaybe d .) . lookupList
293− where
294− lookupList l m = msum $ map (`M.lookup` m) l
625+lit :: String -> LP m Inlines
626+lit = pure . str
295627
296−blockCommand :: PandocMonad m => LP m Blocks
297−blockCommand = try $ do
298− name <- anyControlSeq
299− guard $ name /= "begin" && name /= "end"
300− star <- option "" (string "*" <* optional sp)
301− let name' = name ++ star
302− let raw = do
303− rawcommand <- getRawCommand name'
304− transformed <- applyMacros' rawcommand
305− guard $ transformed /= rawcommand
306− notFollowedBy $ parseFromString' inlines transformed
307− parseFromString' blocks transformed
308− lookupListDefault raw [name',name] blockCommands
628+removeDoubleQuotes :: Text -> Text
629+removeDoubleQuotes t =
630+ maybe t id $ T.stripPrefix "\"" t >>= T.stripSuffix "\""
309631
310−inBrackets :: Inlines -> Inlines
311−inBrackets x = str "[" <> x <> str "]"
632+doubleQuote :: PandocMonad m => LP m Inlines
633+doubleQuote = do
634+ quoted' doubleQuoted (try $ count 2 $ symbol '`')
635+ (void $ try $ count 2 $ symbol '\'')
636+ <|> quoted' doubleQuoted ((:[]) <$> symbol '“') (void $ symbol '”')
637+ -- the following is used by babel for localized quotes:
638+ <|> quoted' doubleQuoted (try $ sequence [symbol '"', symbol '`'])
639+ (void $ try $ sequence [symbol '"', symbol '\''])
640+ <|> quoted' doubleQuoted ((:[]) <$> symbol '"')
641+ (void $ symbol '"')
312642
313−-- eat an optional argument and one or more arguments in braces
314−ignoreInlines :: PandocMonad m => String -> (String, LP m Inlines)
315−ignoreInlines name = (name, p)
316− where
317− p = do oa <- optargs
318− let rawCommand = '\\':name ++ oa
319− let doraw = guardRaw >> return (rawInline "latex" rawCommand)
320− doraw <|> ignore rawCommand
643+singleQuote :: PandocMonad m => LP m Inlines
644+singleQuote = do
645+ quoted' singleQuoted ((:[]) <$> symbol '`')
646+ (try $ symbol '\'' >>
647+ notFollowedBy (satisfyTok startsWithLetter))
648+ <|> quoted' singleQuoted ((:[]) <$> symbol '‘')
649+ (try $ symbol '’' >>
650+ notFollowedBy (satisfyTok startsWithLetter))
651+ where startsWithLetter (Tok _ Word t) =
652+ case T.uncons t of
653+ Just (c, _) | isLetter c -> True
654+ _ -> False
655+ startsWithLetter _ = False
656+
657+quoted' :: PandocMonad m
658+ => (Inlines -> Inlines)
659+ -> LP m [Tok]
660+ -> LP m ()
661+ -> LP m Inlines
662+quoted' f starter ender = do
663+ startchs <- (T.unpack . untokenize) <$> starter
664+ smart <- extensionEnabled Ext_smart <$> getOption readerExtensions
665+ if smart
666+ then do
667+ ils <- many (notFollowedBy ender >> inline)
668+ (ender >> return (f (mconcat ils))) <|>
669+ (<> mconcat ils) <$>
670+ lit (case startchs of
671+ "``" -> "“"
672+ "`" -> "‘"
673+ cs -> cs)
674+ else lit startchs
321675
322−guardRaw :: PandocMonad m => LP m ()
323−guardRaw = getOption readerExtensions >>= guard . extensionEnabled Ext_raw_tex
676+enquote :: PandocMonad m => LP m Inlines
677+enquote = do
678+ skipopts
679+ quoteContext <- sQuoteContext <$> getState
680+ if quoteContext == InDoubleQuote
681+ then singleQuoted <$> withQuoteContext InSingleQuote tok
682+ else doubleQuoted <$> withQuoteContext InDoubleQuote tok
324683
325−optargs :: PandocMonad m => LP m String
326−optargs = snd <$> withRaw (skipopts *> skipMany (try $ optional sp *> braced))
684+doverb :: PandocMonad m => LP m Inlines
685+doverb = do
686+ Tok _ Symbol t <- anySymbol
687+ marker <- case T.uncons t of
688+ Just (c, ts) | T.null ts -> return c
689+ _ -> mzero
690+ withVerbatimMode $
691+ (code . T.unpack . untokenize) <$>
692+ manyTill (verbTok marker) (symbol marker)
693+
694+verbTok :: PandocMonad m => Char -> LP m Tok
695+verbTok stopchar = do
696+ t@(Tok (lin, col) toktype txt) <- satisfyTok (not . isNewlineTok)
697+ case T.findIndex (== stopchar) txt of
698+ Nothing -> return t
699+ Just i -> do
700+ let (t1, t2) = T.splitAt i txt
701+ inp <- getInput
702+ setInput $ Tok (lin, col + i) Symbol (T.singleton stopchar)
703+ : (totoks (lin, col + i + 1) (T.drop 1 t2)) ++ inp
704+ return $ Tok (lin, col) toktype t1
327705
328−ignore :: (Monoid a, PandocMonad m) => String -> ParserT s u m a
329−ignore raw = do
330− pos <- getPosition
331− report $ SkippedContent raw pos
332− return mempty
706+dolstinline :: PandocMonad m => LP m Inlines
707+dolstinline = do
708+ options <- option [] keyvals
709+ let classes = maybeToList $ lookup "language" options >>= fromListingsLanguage
710+ Tok _ Symbol t <- anySymbol
711+ marker <- case T.uncons t of
712+ Just (c, ts) | T.null ts -> return c
713+ _ -> mzero
714+ let stopchar = if marker == '{' then '}' else marker
715+ withVerbatimMode $
716+ (codeWith ("",classes,[]) . T.unpack . untokenize) <$>
717+ manyTill (verbTok stopchar) (symbol stopchar)
333718
334−ignoreBlocks :: PandocMonad m => String -> (String, LP m Blocks)
335−ignoreBlocks name = (name, p)
336− where
337− p = do oa <- optargs
338− let rawCommand = '\\':name ++ oa
339− let doraw = guardRaw >> return (rawBlock "latex" rawCommand)
340− doraw <|> ignore rawCommand
719+keyval :: PandocMonad m => LP m (String, String)
720+keyval = try $ do
721+ Tok _ Word key <- satisfyTok isWordTok
722+ let isSpecSym (Tok _ Symbol t) = t `elem` [".",":","-","|","\\"]
723+ isSpecSym _ = False
724+ val <- option [] $ do
725+ symbol '='
726+ braced <|> (many1 (satisfyTok isWordTok <|> satisfyTok isSpecSym
727+ <|> anyControlSeq))
728+ optional sp
729+ optional (symbol ',')
730+ optional sp
731+ return (T.unpack key, T.unpack . untokenize $ val)
341732
342−blockCommands :: PandocMonad m => M.Map String (LP m Blocks)
343−blockCommands = M.fromList $
344− [ ("par", mempty <$ skipopts)
345− , ("parbox", braced >> grouped blocks)
346− , ("title", mempty <$ (skipopts *>
347− (grouped inline >>= addMeta "title")
348− <|> (grouped block >>= addMeta "title")))
349− , ("subtitle", mempty <$ (skipopts *> tok >>= addMeta "subtitle"))
350− , ("author", mempty <$ (skipopts *> authors))
351− -- -- in letter class, temp. store address & sig as title, author
352− , ("address", mempty <$ (skipopts *> tok >>= addMeta "address"))
353− , ("signature", mempty <$ (skipopts *> authors))
354− , ("date", mempty <$ (skipopts *> tok >>= addMeta "date"))
355− -- Koma-script metadata commands
356− , ("dedication", mempty <$ (skipopts *> tok >>= addMeta "dedication"))
357− -- sectioning
358− , ("part", section nullAttr (-1))
359− , ("part*", section nullAttr (-1))
360− , ("chapter", section nullAttr 0)
361− , ("chapter*", section ("",["unnumbered"],[]) 0)
362− , ("section", section nullAttr 1)
363− , ("section*", section ("",["unnumbered"],[]) 1)
364− , ("subsection", section nullAttr 2)
365− , ("subsection*", section ("",["unnumbered"],[]) 2)
366− , ("subsubsection", section nullAttr 3)
367− , ("subsubsection*", section ("",["unnumbered"],[]) 3)
368− , ("paragraph", section nullAttr 4)
369− , ("paragraph*", section ("",["unnumbered"],[]) 4)
370− , ("subparagraph", section nullAttr 5)
371− , ("subparagraph*", section ("",["unnumbered"],[]) 5)
372− -- beamer slides
373− , ("frametitle", section nullAttr 3)
374− , ("framesubtitle", section nullAttr 4)
375− -- letters
376− , ("opening", (para . trimInlines) <$> (skipopts *> tok))
377− , ("closing", skipopts *> closing)
378− --
379− , ("hrule", pure horizontalRule)
380− , ("strut", pure mempty)
381− , ("rule", skipopts *> tok *> tok *> pure horizontalRule)
382− , ("item", skipopts *> looseItem)
383− , ("documentclass", skipopts *> braced *> preamble)
384− , ("centerline", (para . trimInlines) <$> (skipopts *> tok))
385− , ("caption", skipopts *> setCaption)
386− , ("bibliography", mempty <$ (skipopts *> braced >>=
387− addMeta "bibliography" . splitBibs))
388− , ("addbibresource", mempty <$ (skipopts *> braced >>=
389− addMeta "bibliography" . splitBibs))
390− -- includes
391− , ("lstinputlisting", inputListing)
392− , ("graphicspath", graphicsPath)
393− -- hyperlink
394− , ("hypertarget", braced >> grouped block)
395− -- LaTeX colors
396− , ("textcolor", coloredBlock "color")
397− , ("colorbox", coloredBlock "background-color")
398− ] ++ map ignoreBlocks
399− -- these commands will be ignored unless --parse-raw is specified,
400− -- in which case they will appear as raw latex blocks
401− [ "newcommand", "renewcommand", "newenvironment", "renewenvironment"
402− -- newcommand, etc. should be parsed by macro, but we need this
403− -- here so these aren't parsed as inline commands to ignore
404− , "special", "pdfannot", "pdfstringdef"
405− , "bibliographystyle"
406− , "maketitle", "makeindex", "makeglossary"
407− , "addcontentsline", "addtocontents", "addtocounter"
408− -- \ignore{} is used conventionally in literate haskell for definitions
409− -- that are to be processed by the compiler but not printed.
410− , "ignore"
411− , "hyperdef"
412− , "markboth", "markright", "markleft"
413− , "hspace", "vspace"
414− , "newpage"
415− , "clearpage"
416− , "pagebreak"
417− ]
733+keyvals :: PandocMonad m => LP m [(String, String)]
734+keyvals = try $ symbol '[' >> manyTill keyval (symbol ']')
418735
419−coloredBlock :: PandocMonad m => String -> LP m Blocks
420−coloredBlock stylename = do
421− skipopts
422− color <- braced
423− let constructor = divWith ("",[],[("style",stylename ++ ": " ++ color)])
424− inlineContents <|> constructor <$> blockContents
425− where inlineContents = do
426− ils <- grouped inline
427− rest <- inlines
428− return (para (ils <> rest))
429− blockContents = grouped block
736+accent :: (Char -> String) -> Inlines -> LP m Inlines
737+accent f ils =
738+ case toList ils of
739+ (Str (x:xs) : ys) -> return $ fromList (Str (f x ++ xs) : ys)
740+ [] -> mzero
741+ _ -> return ils
430742
431−graphicsPath :: PandocMonad m => LP m Blocks
432−graphicsPath = do
433− ps <- bgroup *> (manyTill braced egroup)
434− getResourcePath >>= setResourcePath . (++ ps)
435− return mempty
743+grave :: Char -> String
744+grave 'A' = "À"
745+grave 'E' = "È"
746+grave 'I' = "Ì"
747+grave 'O' = "Ò"
748+grave 'U' = "Ù"
749+grave 'a' = "à"
750+grave 'e' = "è"
751+grave 'i' = "ì"
752+grave 'o' = "ò"
753+grave 'u' = "ù"
754+grave c = [c]
436755
437−addMeta :: PandocMonad m => ToMetaValue a => String -> a -> LP m ()
438−addMeta field val = updateState $ \st ->
439− st{ stateMeta = addMetaField field val $ stateMeta st }
756+acute :: Char -> String
757+acute 'A' = "Á"
758+acute 'E' = "É"
759+acute 'I' = "Í"
760+acute 'O' = "Ó"
761+acute 'U' = "Ú"
762+acute 'Y' = "Ý"
763+acute 'a' = "á"
764+acute 'e' = "é"
765+acute 'i' = "í"
766+acute 'o' = "ó"
767+acute 'u' = "ú"
768+acute 'y' = "ý"
769+acute 'C' = "Ć"
770+acute 'c' = "ć"
771+acute 'L' = "Ĺ"
772+acute 'l' = "ĺ"
773+acute 'N' = "Ń"
774+acute 'n' = "ń"
775+acute 'R' = "Ŕ"
776+acute 'r' = "ŕ"
777+acute 'S' = "Ś"
778+acute 's' = "ś"
779+acute 'Z' = "Ź"
780+acute 'z' = "ź"
781+acute c = [c]
440782
441−splitBibs :: String -> [Inlines]
442−splitBibs = map (str . flip replaceExtension "bib" . trim) . splitBy (==',')
783+circ :: Char -> String
784+circ 'A' = "Â"
785+circ 'E' = "Ê"
786+circ 'I' = "Î"
787+circ 'O' = "Ô"
788+circ 'U' = "Û"
789+circ 'a' = "â"
790+circ 'e' = "ê"
791+circ 'i' = "î"
792+circ 'o' = "ô"
793+circ 'u' = "û"
794+circ 'C' = "Ĉ"
795+circ 'c' = "ĉ"
796+circ 'G' = "Ĝ"
797+circ 'g' = "ĝ"
798+circ 'H' = "Ĥ"
799+circ 'h' = "ĥ"
800+circ 'J' = "Ĵ"
801+circ 'j' = "ĵ"
802+circ 'S' = "Ŝ"
803+circ 's' = "ŝ"
804+circ 'W' = "Ŵ"
805+circ 'w' = "ŵ"
806+circ 'Y' = "Ŷ"
807+circ 'y' = "ŷ"
808+circ c = [c]
443809
444−setCaption :: PandocMonad m => LP m Blocks
445−setCaption = do
446− ils <- tok
447− mblabel <- option Nothing $
448− try $ spaces' >> controlSeq "label" >> (Just <$> tok)
449− let ils' = case mblabel of
450− Just lab -> ils <> spanWith
451− ("",[],[("data-label", stringify lab)]) mempty
452− Nothing -> ils
453− updateState $ \st -> st{ stateCaption = Just ils' }
454− return mempty
810+tilde :: Char -> String
811+tilde 'A' = "Ã"
812+tilde 'a' = "ã"
813+tilde 'O' = "Õ"
814+tilde 'o' = "õ"
815+tilde 'I' = "Ĩ"
816+tilde 'i' = "ĩ"
817+tilde 'U' = "Ũ"
818+tilde 'u' = "ũ"
819+tilde 'N' = "Ñ"
820+tilde 'n' = "ñ"
821+tilde c = [c]
455822
456−resetCaption :: PandocMonad m => LP m ()
457−resetCaption = updateState $ \st -> st{ stateCaption = Nothing }
823+umlaut :: Char -> String
824+umlaut 'A' = "Ä"
825+umlaut 'E' = "Ë"
826+umlaut 'I' = "Ï"
827+umlaut 'O' = "Ö"
828+umlaut 'U' = "Ü"
829+umlaut 'a' = "ä"
830+umlaut 'e' = "ë"
831+umlaut 'i' = "ï"
832+umlaut 'o' = "ö"
833+umlaut 'u' = "ü"
834+umlaut c = [c]
458835
459−authors :: PandocMonad m => LP m ()
460−authors = try $ do
461− bgroup
462− let oneAuthor = mconcat <$>
463− many1 (notFollowedBy' (controlSeq "and") >>
464− (inline <|> mempty <$ blockCommand))
465− -- skip e.g. \vspace{10pt}
466− auths <- sepBy oneAuthor (controlSeq "and")
467− egroup
468− addMeta "author" (map trimInlines auths)
836+hungarumlaut :: Char -> String
837+hungarumlaut 'A' = "A̋"
838+hungarumlaut 'E' = "E̋"
839+hungarumlaut 'I' = "I̋"
840+hungarumlaut 'O' = "Ő"
841+hungarumlaut 'U' = "Ű"
842+hungarumlaut 'Y' = "ӳ"
843+hungarumlaut 'a' = "a̋"
844+hungarumlaut 'e' = "e̋"
845+hungarumlaut 'i' = "i̋"
846+hungarumlaut 'o' = "ő"
847+hungarumlaut 'u' = "ű"
848+hungarumlaut 'y' = "ӳ"
849+hungarumlaut c = [c]
469850
470−section :: PandocMonad m => Attr -> Int -> LP m Blocks
471−section (ident, classes, kvs) lvl = do
472− skipopts
473− contents <- grouped inline
474− lab <- option ident $ try (spaces' >> controlSeq "label" >> spaces' >> braced)
475− attr' <- registerHeader (lab, classes, kvs) contents
476− return $ headerWith attr' lvl contents
851+dot :: Char -> String
852+dot 'C' = "Ċ"
853+dot 'c' = "ċ"
854+dot 'E' = "Ė"
855+dot 'e' = "ė"
856+dot 'G' = "Ġ"
857+dot 'g' = "ġ"
858+dot 'I' = "İ"
859+dot 'Z' = "Ż"
860+dot 'z' = "ż"
861+dot c = [c]
862+
863+macron :: Char -> String
864+macron 'A' = "Ā"
865+macron 'E' = "Ē"
866+macron 'I' = "Ī"
867+macron 'O' = "Ō"
868+macron 'U' = "Ū"
869+macron 'a' = "ā"
870+macron 'e' = "ē"
871+macron 'i' = "ī"
872+macron 'o' = "ō"
873+macron 'u' = "ū"
874+macron c = [c]
875+
876+cedilla :: Char -> String
877+cedilla 'c' = "ç"
878+cedilla 'C' = "Ç"
879+cedilla 's' = "ş"
880+cedilla 'S' = "Ş"
881+cedilla 't' = "ţ"
882+cedilla 'T' = "Ţ"
883+cedilla 'e' = "ȩ"
884+cedilla 'E' = "Ȩ"
885+cedilla 'h' = "ḩ"
886+cedilla 'H' = "Ḩ"
887+cedilla 'o' = "o̧"
888+cedilla 'O' = "O̧"
889+cedilla c = [c]
890+
891+hacek :: Char -> String
892+hacek 'A' = "Ǎ"
893+hacek 'a' = "ǎ"
894+hacek 'C' = "Č"
895+hacek 'c' = "č"
896+hacek 'D' = "Ď"
897+hacek 'd' = "ď"
898+hacek 'E' = "Ě"
899+hacek 'e' = "ě"
900+hacek 'G' = "Ǧ"
901+hacek 'g' = "ǧ"
902+hacek 'H' = "Ȟ"
903+hacek 'h' = "ȟ"
904+hacek 'I' = "Ǐ"
905+hacek 'i' = "ǐ"
906+hacek 'j' = "ǰ"
907+hacek 'K' = "Ǩ"
908+hacek 'k' = "ǩ"
909+hacek 'L' = "Ľ"
910+hacek 'l' = "ľ"
911+hacek 'N' = "Ň"
912+hacek 'n' = "ň"
913+hacek 'O' = "Ǒ"
914+hacek 'o' = "ǒ"
915+hacek 'R' = "Ř"
916+hacek 'r' = "ř"
917+hacek 'S' = "Š"
918+hacek 's' = "š"
919+hacek 'T' = "Ť"
920+hacek 't' = "ť"
921+hacek 'U' = "Ǔ"
922+hacek 'u' = "ǔ"
923+hacek 'Z' = "Ž"
924+hacek 'z' = "ž"
925+hacek c = [c]
926+
927+breve :: Char -> String
928+breve 'A' = "Ă"
929+breve 'a' = "ă"
930+breve 'E' = "Ĕ"
931+breve 'e' = "ĕ"
932+breve 'G' = "Ğ"
933+breve 'g' = "ğ"
934+breve 'I' = "Ĭ"
935+breve 'i' = "ĭ"
936+breve 'O' = "Ŏ"
937+breve 'o' = "ŏ"
938+breve 'U' = "Ŭ"
939+breve 'u' = "ŭ"
940+breve c = [c]
941+
942+toksToString :: [Tok] -> String
943+toksToString = T.unpack . untokenize
944+
945+mathDisplay :: String -> Inlines
946+mathDisplay = displayMath . trim
947+
948+mathInline :: String -> Inlines
949+mathInline = math . trim
950+
951+dollarsMath :: PandocMonad m => LP m Inlines
952+dollarsMath = do
953+ symbol '$'
954+ display <- option False (True <$ symbol '$')
955+ contents <- trim . toksToString <$>
956+ many (notFollowedBy (symbol '$') >> anyTok)
957+ if display
958+ then do
959+ mathDisplay contents <$ try (symbol '$' >> symbol '$')
960+ <|> (guard (null contents) >> return (mathInline ""))
961+ else mathInline contents <$ (symbol '$')
962+
963+-- citations
964+
965+addPrefix :: [Inline] -> [Citation] -> [Citation]
966+addPrefix p (k:ks) = k {citationPrefix = p ++ citationPrefix k} : ks
967+addPrefix _ _ = []
968+
969+addSuffix :: [Inline] -> [Citation] -> [Citation]
970+addSuffix s ks@(_:_) =
971+ let k = last ks
972+ in init ks ++ [k {citationSuffix = citationSuffix k ++ s}]
973+addSuffix _ _ = []
974+
975+simpleCiteArgs :: PandocMonad m => LP m [Citation]
976+simpleCiteArgs = try $ do
977+ first <- optionMaybe $ toList <$> opt
978+ second <- optionMaybe $ toList <$> opt
979+ keys <- try $ bgroup *> (manyTill citationLabel egroup)
980+ let (pre, suf) = case (first , second ) of
981+ (Just s , Nothing) -> (mempty, s )
982+ (Just s , Just t ) -> (s , t )
983+ _ -> (mempty, mempty)
984+ conv k = Citation { citationId = k
985+ , citationPrefix = []
986+ , citationSuffix = []
987+ , citationMode = NormalCitation
988+ , citationHash = 0
989+ , citationNoteNum = 0
990+ }
991+ return $ addPrefix pre $ addSuffix suf $ map conv keys
992+
993+citationLabel :: PandocMonad m => LP m String
994+citationLabel = do
995+ optional sp
996+ toksToString <$>
997+ (many1 (satisfyTok isWordTok <|> symbolIn bibtexKeyChar)
998+ <* optional sp
999+ <* optional (symbol ',')
1000+ <* optional sp)
1001+ where bibtexKeyChar = ".:;?!`'()/*@_+=-[]" :: [Char]
1002+
1003+cites :: PandocMonad m => CitationMode -> Bool -> LP m [Citation]
1004+cites mode multi = try $ do
1005+ cits <- if multi
1006+ then many1 simpleCiteArgs
1007+ else count 1 simpleCiteArgs
1008+ let cs = concat cits
1009+ return $ case mode of
1010+ AuthorInText -> case cs of
1011+ (c:rest) -> c {citationMode = mode} : rest
1012+ [] -> []
1013+ _ -> map (\a -> a {citationMode = mode}) cs
1014+
1015+citation :: PandocMonad m => String -> CitationMode -> Bool -> LP m Inlines
1016+citation name mode multi = do
1017+ (c,raw) <- withRaw $ cites mode multi
1018+ return $ cite c (rawInline "latex" $ "\\" ++ name ++ (toksToString raw))
1019+
1020+handleCitationPart :: Inlines -> [Citation]
1021+handleCitationPart ils =
1022+ let isCite Cite{} = True
1023+ isCite _ = False
1024+ (pref, rest) = break isCite (toList ils)
1025+ in case rest of
1026+ (Cite cs _:suff) -> addPrefix pref $ addSuffix suff cs
1027+ _ -> []
1028+
1029+complexNatbibCitation :: PandocMonad m => CitationMode -> LP m Inlines
1030+complexNatbibCitation mode = try $ do
1031+ (cs, raw) <-
1032+ withRaw $ concat <$> do
1033+ bgroup
1034+ items <- mconcat <$>
1035+ many1 (notFollowedBy (symbol ';') >> inline)
1036+ `sepBy1` (symbol ';')
1037+ egroup
1038+ return $ map handleCitationPart items
1039+ case cs of
1040+ [] -> mzero
1041+ (c:cits) -> return $ cite (c{ citationMode = mode }:cits)
1042+ (rawInline "latex" $ "\\citetext" ++ toksToString raw)
1043+
1044+inNote :: Inlines -> Inlines
1045+inNote ils =
1046+ note $ para $ ils <> str "."
4771047
478−inlineCommand :: PandocMonad m => LP m Inlines
479−inlineCommand = try $ do
480− (name, raw') <- withRaw anyControlSeq
1048+inlineCommand' :: PandocMonad m => LP m Inlines
1049+inlineCommand' = try $ do
1050+ Tok _ (CtrlSeq name) cmd <- anyControlSeq
4811051 guard $ name /= "begin" && name /= "end"
482− star <- option "" (string "*")
483− let name' = name ++ star
1052+ star <- option "" ("*" <$ symbol '*' <* optional sp)
1053+ let name' = name <> star
1054+ let names = ordNub [name', name] -- check non-starred as fallback
4841055 let raw = do
485− guard $ not (isBlockCommand name)
486− rawargs <- withRaw
487− (skipangles *> skipopts *> option "" dimenarg *> many braced)
488− let rawcommand = raw' ++ star ++ snd rawargs
489− transformed <- applyMacros' rawcommand
490− exts <- getOption readerExtensions
491− if transformed /= rawcommand
492− then parseFromString' inlines transformed
493− else if extensionEnabled Ext_raw_tex exts
494− then return $ rawInline "latex" rawcommand
495− else ignore rawcommand
496− (lookupListDefault raw [name',name] inlineCommands <*
497− optional (try (string "{}")))
498−
499−rawInlineOr :: PandocMonad m => String -> LP m Inlines -> LP m Inlines
500−rawInlineOr name' fallback = do
501− parseRaw <- extensionEnabled Ext_raw_tex <$> getOption readerExtensions
502− if parseRaw
503− then rawInline "latex" <$> getRawCommand name'
504− else fallback
1056+ guard $ isInlineCommand name || not (isBlockCommand name)
1057+ rawcommand <- getRawCommand (cmd <> star)
1058+ (guardEnabled Ext_raw_tex >> return (rawInline "latex" rawcommand))
1059+ <|> ignore rawcommand
1060+ lookupListDefault raw names inlineCommands
1061+
1062+tok :: PandocMonad m => LP m Inlines
1063+tok = grouped inline <|> inlineCommand' <|> singleChar
1064+ where singleChar = try $ do
1065+ Tok (lin,col) toktype t <- satisfyTok (tokTypeIn [Word, Symbol])
1066+ guard $ not $ toktype == Symbol &&
1067+ T.any (`Set.member` specialChars) t
1068+ if T.length t > 1
1069+ then do
1070+ let (t1, t2) = (T.take 1 t, T.drop 1 t)
1071+ inp <- getInput
1072+ setInput $ (Tok (lin, col + 1) toktype t2) : inp
1073+ return $ str (T.unpack t1)
1074+ else return $ str (T.unpack t)
1075+
1076+opt :: PandocMonad m => LP m Inlines
1077+opt = bracketed inline
1078+
1079+rawopt :: PandocMonad m => LP m Text
1080+rawopt = do
1081+ symbol '['
1082+ inner <- untokenize <$> manyTill anyTok (symbol ']')
1083+ optional sp
1084+ return $ "[" <> inner <> "]"
1085+
1086+skipopts :: PandocMonad m => LP m ()
1087+skipopts = skipMany rawopt
1088+
1089+-- opts in angle brackets are used in beamer
1090+rawangle :: PandocMonad m => LP m ()
1091+rawangle = try $ do
1092+ symbol '<'
1093+ () <$ manyTill anyTok (symbol '>')
1094+
1095+skipangles :: PandocMonad m => LP m ()
1096+skipangles = skipMany rawangle
1097+
1098+ignore :: (Monoid a, PandocMonad m) => String -> ParserT s u m a
1099+ignore raw = do
1100+ pos <- getPosition
1101+ report $ SkippedContent raw pos
1102+ return mempty
1103+
1104+withRaw :: PandocMonad m => LP m a -> LP m (a, [Tok])
1105+withRaw parser = do
1106+ inp <- getInput
1107+ result <- parser
1108+ nxt <- option (Tok (0,0) Word "") (lookAhead anyTok)
1109+ let raw = takeWhile (/= nxt) inp
1110+ return (result, raw)
1111+
1112+inBrackets :: Inlines -> Inlines
1113+inBrackets x = str "[" <> x <> str "]"
1114+
1115+unescapeURL :: String -> String
1116+unescapeURL ('\\':x:xs) | isEscapable x = x:unescapeURL xs
1117+ where isEscapable c = c `elem` ("#$%&~_^\\{}" :: String)
1118+unescapeURL (x:xs) = x:unescapeURL xs
1119+unescapeURL [] = ""
5051120
506−isBlockCommand :: String -> Bool
507−isBlockCommand s = s `M.member` (blockCommands :: M.Map String (LP PandocPure Blocks))
1121+mathEnvWith :: PandocMonad m
1122+ => (Inlines -> a) -> Maybe Text -> Text -> LP m a
1123+mathEnvWith f innerEnv name = f . mathDisplay . inner <$> mathEnv name
1124+ where inner x = case innerEnv of
1125+ Nothing -> x
1126+ Just y -> "\\begin{" ++ T.unpack y ++ "}\n" ++ x ++
1127+ "\\end{" ++ T.unpack y ++ "}"
1128+
1129+mathEnv :: PandocMonad m => Text -> LP m String
1130+mathEnv name = do
1131+ skipopts
1132+ optional blankline
1133+ res <- manyTill anyTok (end_ name)
1134+ return $ stripTrailingNewlines $ T.unpack $ untokenize res
5081135
1136+inlineEnvironment :: PandocMonad m => LP m Inlines
1137+inlineEnvironment = try $ do
1138+ controlSeq "begin"
1139+ name <- untokenize <$> braced
1140+ M.findWithDefault mzero name inlineEnvironments
5091141
510−inlineEnvironments :: PandocMonad m => M.Map String (LP m Inlines)
511−inlineEnvironments = M.fromList
512− [ ("displaymath", mathEnvWith id Nothing "displaymath")
1142+inlineEnvironments :: PandocMonad m => M.Map Text (LP m Inlines)
1143+inlineEnvironments = M.fromList [
1144+ ("displaymath", mathEnvWith id Nothing "displaymath")
5131145 , ("math", math <$> mathEnv "math")
5141146 , ("equation", mathEnvWith id Nothing "equation")
5151147 , ("equation*", mathEnvWith id Nothing "equation*")
@@ -525,7 +1157,7 @@ inlineEnvironments = M.fromList
5251157 , ("alignat*", mathEnvWith id (Just "aligned") "alignat*")
5261158 ]
5271159
528−inlineCommands :: PandocMonad m => M.Map String (LP m Inlines)
1160+inlineCommands :: PandocMonad m => M.Map Text (LP m Inlines)
5291161inlineCommands = M.fromList $
5301162 [ ("emph", extractSpaces emph <$> tok)
5311163 , ("textit", extractSpaces emph <$> tok)
@@ -554,9 +1186,9 @@ inlineCommands = M.fromList $
5541186 , ("textgreek", tok)
5551187 , ("sep", lit ",")
5561188 , ("cref", rawInlineOr "cref" (inBrackets <$> tok)) -- from cleveref.sty
557− , ("(", mathInline $ manyTill anyChar (try $ string "\\)"))
558− , ("[", mathDisplay $ manyTill anyChar (try $ string "\\]"))
559− , ("ensuremath", mathInline braced)
1189+ , ("(", mathInline . toksToString <$> manyTill anyTok (controlSeq ")"))
1190+ , ("[", mathDisplay . toksToString <$> manyTill anyTok (controlSeq "]"))
1191+ , ("ensuremath", mathInline . toksToString <$> braced)
5601192 , ("texorpdfstring", (\_ x -> x) <$> tok <*> tok)
5611193 , ("P", lit "¶")
5621194 , ("S", lit "§")
@@ -606,7 +1238,10 @@ inlineCommands = M.fromList $
6061238 , ("v", option (str "v") $ try $ tok >>= accent hacek)
6071239 , ("u", option (str "u") $ try $ tok >>= accent breve)
6081240 , ("i", lit "i")
609− , ("\\", linebreak <$ (optional (bracketed inline) *> spaces'))
1241+ , ("\\", linebreak <$ (do inTableCell <- sInTableCell <$> getState
1242+ guard $ not inTableCell
1243+ optional (bracketed inline)
1244+ spaces))
6101245 , (",", lit "\8198")
6111246 , ("@", pure mempty)
6121247 , (" ", lit "\160")
@@ -621,13 +1256,14 @@ inlineCommands = M.fromList $
6211256 , ("verb", doverb)
6221257 , ("lstinline", dolstinline)
6231258 , ("Verb", doverb)
624− , ("url", (unescapeURL <$> braced) >>= \url ->
625− pure (link url "" (str url)))
626− , ("href", (unescapeURL <$> braced <* optional sp) >>= \url ->
627− tok >>= \lab ->
628− pure (link url "" lab))
1259+ , ("url", ((unescapeURL . T.unpack . untokenize) <$> braced) >>= \url ->
1260+ pure (link url "" (str url)))
1261+ , ("href", (unescapeURL . toksToString <$>
1262+ braced <* optional sp) >>= \url ->
1263+ tok >>= \lab -> pure (link url "" lab))
6291264 , ("includegraphics", do options <- option [] keyvals
630− src <- unescapeURL . removeDoubleQuotes <$> braced
1265+ src <- unescapeURL . T.unpack .
1266+ removeDoubleQuotes . untokenize <$> braced
6311267 mkImage options src)
6321268 , ("enquote", enquote)
6331269 , ("cite", citation "cite" NormalCitation False)
@@ -703,368 +1339,469 @@ inlineCommands = M.fromList $
7031339 -- fontawesome
7041340 , ("faCheck", lit "\10003")
7051341 , ("faClose", lit "\10007")
706− ] ++ map ignoreInlines
707− -- these commands will be ignored unless --parse-raw is specified,
708− -- in which case they will appear as raw latex blocks:
709− [ "index"
710− , "hspace"
711− , "vspace"
712− , "newpage"
713− , "clearpage"
714− , "pagebreak"
7151342 ]
7161343
7171344coloredInline :: PandocMonad m => String -> LP m Inlines
7181345coloredInline stylename = do
7191346 skipopts
7201347 color <- braced
721− spanWith ("",[],[("style",stylename ++ ": " ++ color)]) <$> tok
1348+ spanWith ("",[],[("style",stylename ++ ": " ++ toksToString color)]) <$> tok
7221349
7231350ttfamily :: PandocMonad m => LP m Inlines
7241351ttfamily = (code . stringify . toList) <$> tok
7251352
726−mkImage :: PandocMonad m => [(String, String)] -> String -> LP m Inlines
727−mkImage options src = do
728− let replaceTextwidth (k,v) = case numUnit v of
729− Just (num, "\\textwidth") -> (k, showFl (num * 100) ++ "%")
730− _ -> (k, v)
731− let kvs = map replaceTextwidth $ filter (\(k,_) -> k `elem` ["width", "height"]) options
732− let attr = ("",[], kvs)
733− let alt = str "image"
734− case takeExtension src of
735− "" -> do
736− defaultExt <- getOption readerDefaultImageExtension
737− return $ imageWith attr (addExtension src defaultExt) "" alt
738− _ -> return $ imageWith attr src "" alt
1353+rawInlineOr :: PandocMonad m => Text -> LP m Inlines -> LP m Inlines
1354+rawInlineOr name' fallback = do
1355+ parseRaw <- extensionEnabled Ext_raw_tex <$> getOption readerExtensions
1356+ if parseRaw
1357+ then rawInline "latex" <$> getRawCommand name'
1358+ else fallback
7391359
740−inNote :: Inlines -> Inlines
741−inNote ils =
742− note $ para $ ils <> str "."
1360+getRawCommand :: PandocMonad m => Text -> LP m String
1361+getRawCommand txt = do
1362+ (_, rawargs) <- withRaw
1363+ ((if txt == "\\write"
1364+ then () <$ satisfyTok isWordTok -- digits
1365+ else return ()) *>
1366+ skipangles *>
1367+ skipopts *>
1368+ option "" (try (optional sp *> dimenarg)) *>
1369+ many braced)
1370+ return $ T.unpack (txt <> untokenize rawargs)
1371+
1372+isBlockCommand :: Text -> Bool
1373+isBlockCommand s =
1374+ s `M.member` (blockCommands :: M.Map Text (LP PandocPure Blocks))
1375+ || s `Set.member` treatAsBlock
1376+
1377+treatAsBlock :: Set.Set Text
1378+treatAsBlock = Set.fromList
1379+ [ "newcommand", "renewcommand"
1380+ , "newenvironment", "renewenvironment"
1381+ , "providecommand", "provideenvironment"
1382+ -- newcommand, etc. should be parsed by macroDef, but we need this
1383+ -- here so these aren't parsed as inline commands to ignore
1384+ , "special", "pdfannot", "pdfstringdef"
1385+ , "bibliographystyle"
1386+ , "maketitle", "makeindex", "makeglossary"
1387+ , "addcontentsline", "addtocontents", "addtocounter"
1388+ -- \ignore{} is used conventionally in literate haskell for definitions
1389+ -- that are to be processed by the compiler but not printed.
1390+ , "ignore"
1391+ , "hyperdef"
1392+ , "markboth", "markright", "markleft"
1393+ , "hspace", "vspace"
1394+ , "newpage"
1395+ , "clearpage"
1396+ , "pagebreak"
1397+ ]
1398+
1399+isInlineCommand :: Text -> Bool
1400+isInlineCommand s =
1401+ s `M.member` (inlineCommands :: M.Map Text (LP PandocPure Inlines))
1402+ || s `Set.member` treatAsInline
1403+
1404+treatAsInline :: Set.Set Text
1405+treatAsInline = Set.fromList
1406+ [ "index"
1407+ , "hspace"
1408+ , "vspace"
1409+ , "noindent"
1410+ , "newpage"
1411+ , "clearpage"
1412+ , "pagebreak"
1413+ ]
7431414
744−unescapeURL :: String -> String
745−unescapeURL ('\\':x:xs) | isEscapable x = x:unescapeURL xs
746− where isEscapable c = c `elem` ("#$%&~_^\\{}" :: String)
747−unescapeURL (x:xs) = x:unescapeURL xs
748−unescapeURL [] = ""
749−
750−enquote :: PandocMonad m => LP m Inlines
751−enquote = do
752− skipopts
753− context <- stateQuoteContext <$> getState
754− if context == InDoubleQuote
755− then singleQuoted <$> withQuoteContext InSingleQuote tok
756− else doubleQuoted <$> withQuoteContext InDoubleQuote tok
757−
758−doverb :: PandocMonad m => LP m Inlines
759−doverb = do
760− marker <- anyChar
761− code <$> manyTill (satisfy (/='\n')) (char marker)
762−
763−dolstinline :: PandocMonad m => LP m Inlines
764−dolstinline = do
765− options <- option [] keyvals
766− let classes = maybeToList $ lookup "language" options >>= fromListingsLanguage
767− marker <- char '{' <|> anyChar
768− codeWith ("",classes,[]) <$> manyTill (satisfy (/='\n')) (char '}' <|> char marker)
769−
770−doLHSverb :: PandocMonad m => LP m Inlines
771−doLHSverb = codeWith ("",["haskell"],[]) <$> manyTill (satisfy (/='\n')) (char '|')
772−
773−-- converts e.g. \SI{1}[\$]{} to "$ 1" or \SI{1}{\euro} to "1 €"
774−dosiunitx :: PandocMonad m => LP m Inlines
775−dosiunitx = do
776− skipopts
777− value <- tok
778− valueprefix <- option "" $ char '[' >> (mconcat <$> manyTill tok (char ']'))
779− unit <- tok
780− let emptyOr160 "" = ""
781− emptyOr160 _ = "\160"
782− return . mconcat $ [valueprefix,
783− emptyOr160 valueprefix,
784− value,
785− emptyOr160 unit,
786− unit]
787−
788−lit :: String -> LP m Inlines
789−lit = pure . str
1415+lookupListDefault :: (Show k, Ord k) => v -> [k] -> M.Map k v -> v
1416+lookupListDefault d = (fromMaybe d .) . lookupList
1417+ where lookupList l m = msum $ map (`M.lookup` m) l
7901418
791−accent :: (Char -> String) -> Inlines -> LP m Inlines
792−accent f ils =
793− case toList ils of
794− (Str (x:xs) : ys) -> return $ fromList (Str (f x ++ xs) : ys)
795− [] -> mzero
796− _ -> return ils
1419+inline :: PandocMonad m => LP m Inlines
1420+inline = (mempty <$ comment)
1421+ <|> (space <$ whitespace)
1422+ <|> (softbreak <$ endline)
1423+ <|> word
1424+ <|> inlineCommand'
1425+ <|> inlineEnvironment
1426+ <|> inlineGroup
1427+ <|> (symbol '-' *>
1428+ option (str "-") (symbol '-' *>
1429+ option (str "–") (str "—" <$ symbol '-')))
1430+ <|> doubleQuote
1431+ <|> singleQuote
1432+ <|> (str "”" <$ try (symbol '\'' >> symbol '\''))
1433+ <|> (str "”" <$ symbol '”')
1434+ <|> (str "’" <$ symbol '\'')
1435+ <|> (str "’" <$ symbol '’')
1436+ <|> (str "\160" <$ symbol '~')
1437+ <|> dollarsMath
1438+ <|> (guardEnabled Ext_literate_haskell *> symbol '|' *> doLHSverb)
1439+ <|> (str . (:[]) <$> primEscape)
1440+ <|> regularSymbol
1441+ <|> (do res <- symbolIn "#^'`\"[]"
1442+ pos <- getPosition
1443+ let s = T.unpack (untoken res)
1444+ report $ ParsingUnescaped s pos
1445+ return $ str s)
7971446
798−grave :: Char -> String
799−grave 'A' = "À"
800−grave 'E' = "È"
801−grave 'I' = "Ì"
802−grave 'O' = "Ò"
803−grave 'U' = "Ù"
804−grave 'a' = "à"
805−grave 'e' = "è"
806−grave 'i' = "ì"
807−grave 'o' = "ò"
808−grave 'u' = "ù"
809−grave c = [c]
1447+inlines :: PandocMonad m => LP m Inlines
1448+inlines = mconcat <$> many inline
8101449
811−acute :: Char -> String
812−acute 'A' = "Á"
813−acute 'E' = "É"
814−acute 'I' = "Í"
815−acute 'O' = "Ó"
816−acute 'U' = "Ú"
817−acute 'Y' = "Ý"
818−acute 'a' = "á"
819−acute 'e' = "é"
820−acute 'i' = "í"
821−acute 'o' = "ó"
822−acute 'u' = "ú"
823−acute 'y' = "ý"
824−acute 'C' = "Ć"
825−acute 'c' = "ć"
826−acute 'L' = "Ĺ"
827−acute 'l' = "ĺ"
828−acute 'N' = "Ń"
829−acute 'n' = "ń"
830−acute 'R' = "Ŕ"
831−acute 'r' = "ŕ"
832−acute 'S' = "Ś"
833−acute 's' = "ś"
834−acute 'Z' = "Ź"
835−acute 'z' = "ź"
836−acute c = [c]
1450+-- block elements:
8371451
838−circ :: Char -> String
839−circ 'A' = "Â"
840−circ 'E' = "Ê"
841−circ 'I' = "Î"
842−circ 'O' = "Ô"
843−circ 'U' = "Û"
844−circ 'a' = "â"
845−circ 'e' = "ê"
846−circ 'i' = "î"
847−circ 'o' = "ô"
848−circ 'u' = "û"
849−circ 'C' = "Ĉ"
850−circ 'c' = "ĉ"
851−circ 'G' = "Ĝ"
852−circ 'g' = "ĝ"
853−circ 'H' = "Ĥ"
854−circ 'h' = "ĥ"
855−circ 'J' = "Ĵ"
856−circ 'j' = "ĵ"
857−circ 'S' = "Ŝ"
858−circ 's' = "ŝ"
859−circ 'W' = "Ŵ"
860−circ 'w' = "ŵ"
861−circ 'Y' = "Ŷ"
862−circ 'y' = "ŷ"
863−circ c = [c]
864−
865−tilde :: Char -> String
866−tilde 'A' = "Ã"
867−tilde 'a' = "ã"
868−tilde 'O' = "Õ"
869−tilde 'o' = "õ"
870−tilde 'I' = "Ĩ"
871−tilde 'i' = "ĩ"
872−tilde 'U' = "Ũ"
873−tilde 'u' = "ũ"
874−tilde 'N' = "Ñ"
875−tilde 'n' = "ñ"
876−tilde c = [c]
1452+begin_ :: PandocMonad m => Text -> LP m ()
1453+begin_ t = (try $ do
1454+ controlSeq "begin"
1455+ spaces
1456+ symbol '{'
1457+ spaces
1458+ Tok _ Word txt <- satisfyTok isWordTok
1459+ spaces
1460+ symbol '}'
1461+ guard (t == txt)) <?> ("\\begin{" ++ T.unpack t ++ "}")
8771462
878−umlaut :: Char -> String
879−umlaut 'A' = "Ä"
880−umlaut 'E' = "Ë"
881−umlaut 'I' = "Ï"
882−umlaut 'O' = "Ö"
883−umlaut 'U' = "Ü"
884−umlaut 'a' = "ä"
885−umlaut 'e' = "ë"
886−umlaut 'i' = "ï"
887−umlaut 'o' = "ö"
888−umlaut 'u' = "ü"
889−umlaut c = [c]
1463+end_ :: PandocMonad m => Text -> LP m ()
1464+end_ t = (try $ do
1465+ controlSeq "end"
1466+ spaces
1467+ symbol '{'
1468+ spaces
1469+ Tok _ Word txt <- satisfyTok isWordTok
1470+ spaces
1471+ symbol '}'
1472+ guard $ t == txt) <?> ("\\end{" ++ T.unpack t ++ "}")
8901473
891−hungarumlaut :: Char -> String
892−hungarumlaut 'A' = "A̋"
893−hungarumlaut 'E' = "E̋"
894−hungarumlaut 'I' = "I̋"
895−hungarumlaut 'O' = "Ő"
896−hungarumlaut 'U' = "Ű"
897−hungarumlaut 'Y' = "ӳ"
898−hungarumlaut 'a' = "a̋"
899−hungarumlaut 'e' = "e̋"
900−hungarumlaut 'i' = "i̋"
901−hungarumlaut 'o' = "ő"
902−hungarumlaut 'u' = "ű"
903−hungarumlaut 'y' = "ӳ"
904−hungarumlaut c = [c]
1474+preamble :: PandocMonad m => LP m Blocks
1475+preamble = mempty <$ many preambleBlock
1476+ where preambleBlock = spaces1
1477+ <|> void include
1478+ <|> void macroDef
1479+ <|> void blockCommand
1480+ <|> void braced
1481+ <|> (notFollowedBy (begin_ "document") >> void anyTok)
9051482
906−dot :: Char -> String
907−dot 'C' = "Ċ"
908−dot 'c' = "ċ"
909−dot 'E' = "Ė"
910−dot 'e' = "ė"
911−dot 'G' = "Ġ"
912−dot 'g' = "ġ"
913−dot 'I' = "İ"
914−dot 'Z' = "Ż"
915−dot 'z' = "ż"
916−dot c = [c]
1483+paragraph :: PandocMonad m => LP m Blocks
1484+paragraph = do
1485+ x <- trimInlines . mconcat <$> many1 inline
1486+ if x == mempty
1487+ then return mempty
1488+ else return $ para x
9171489
918−macron :: Char -> String
919−macron 'A' = "Ā"
920−macron 'E' = "Ē"
921−macron 'I' = "Ī"
922−macron 'O' = "Ō"
923−macron 'U' = "Ū"
924−macron 'a' = "ā"
925−macron 'e' = "ē"
926−macron 'i' = "ī"
927−macron 'o' = "ō"
928−macron 'u' = "ū"
929−macron c = [c]
1490+include :: PandocMonad m => LP m Blocks
1491+include = do
1492+ (Tok _ (CtrlSeq name) _) <-
1493+ controlSeq "include" <|> controlSeq "input" <|>
1494+ controlSeq "subfile" <|> controlSeq "usepackage"
1495+ skipMany $ bracketed inline -- skip options
1496+ fs <- (map trim . splitBy (==',') . T.unpack . untokenize) <$> braced
1497+ let fs' = if name == "usepackage"
1498+ then map (maybeAddExtension ".sty") fs
1499+ else map (maybeAddExtension ".tex") fs
1500+ dirs <- (splitBy (==':') . fromMaybe ".") <$> lookupEnv "TEXINPUTS"
1501+ mconcat <$> mapM (insertIncludedFile blocks (tokenize . T.pack) dirs) fs'
9301502
931−cedilla :: Char -> String
932−cedilla 'c' = "ç"
933−cedilla 'C' = "Ç"
934−cedilla 's' = "ş"
935−cedilla 'S' = "Ş"
936−cedilla 't' = "ţ"
937−cedilla 'T' = "Ţ"
938−cedilla 'e' = "ȩ"
939−cedilla 'E' = "Ȩ"
940−cedilla 'h' = "ḩ"
941−cedilla 'H' = "Ḩ"
942−cedilla 'o' = "o̧"
943−cedilla 'O' = "O̧"
944−cedilla c = [c]
1503+maybeAddExtension :: String -> FilePath -> FilePath
1504+maybeAddExtension ext fp =
1505+ if null (takeExtension fp)
1506+ then addExtension fp ext
1507+ else fp
9451508
946−hacek :: Char -> String
947−hacek 'A' = "Ǎ"
948−hacek 'a' = "ǎ"
949−hacek 'C' = "Č"
950−hacek 'c' = "č"
951−hacek 'D' = "Ď"
952−hacek 'd' = "ď"
953−hacek 'E' = "Ě"
954−hacek 'e' = "ě"
955−hacek 'G' = "Ǧ"
956−hacek 'g' = "ǧ"
957−hacek 'H' = "Ȟ"
958−hacek 'h' = "ȟ"
959−hacek 'I' = "Ǐ"
960−hacek 'i' = "ǐ"
961−hacek 'j' = "ǰ"
962−hacek 'K' = "Ǩ"
963−hacek 'k' = "ǩ"
964−hacek 'L' = "Ľ"
965−hacek 'l' = "ľ"
966−hacek 'N' = "Ň"
967−hacek 'n' = "ň"
968−hacek 'O' = "Ǒ"
969−hacek 'o' = "ǒ"
970−hacek 'R' = "Ř"
971−hacek 'r' = "ř"
972−hacek 'S' = "Š"
973−hacek 's' = "š"
974−hacek 'T' = "Ť"
975−hacek 't' = "ť"
976−hacek 'U' = "Ǔ"
977−hacek 'u' = "ǔ"
978−hacek 'Z' = "Ž"
979−hacek 'z' = "ž"
980−hacek c = [c]
1509+addMeta :: PandocMonad m => ToMetaValue a => String -> a -> LP m ()
1510+addMeta field val = updateState $ \st ->
1511+ st{ sMeta = addMetaField field val $ sMeta st }
9811512
982−breve :: Char -> String
983−breve 'A' = "Ă"
984−breve 'a' = "ă"
985−breve 'E' = "Ĕ"
986−breve 'e' = "ĕ"
987−breve 'G' = "Ğ"
988−breve 'g' = "ğ"
989−breve 'I' = "Ĭ"
990−breve 'i' = "ĭ"
991−breve 'O' = "Ŏ"
992−breve 'o' = "ŏ"
993−breve 'U' = "Ŭ"
994−breve 'u' = "ŭ"
995−breve c = [c]
1513+authors :: PandocMonad m => LP m ()
1514+authors = try $ do
1515+ bgroup
1516+ let oneAuthor = mconcat <$>
1517+ many1 (notFollowedBy' (controlSeq "and") >>
1518+ (inline <|> mempty <$ blockCommand))
1519+ -- skip e.g. \vspace{10pt}
1520+ auths <- sepBy oneAuthor (controlSeq "and")
1521+ egroup
1522+ addMeta "author" (map trimInlines auths)
9961523
997−tok :: PandocMonad m => LP m Inlines
998−tok = try $ grouped inline <|> inlineCommand <|> str <$> count 1 inlineChar
1524+macroDef :: PandocMonad m => LP m Blocks
1525+macroDef = do
1526+ guardEnabled Ext_latex_macros
1527+ mempty <$ ((commandDef <|> environmentDef) <* doMacros 0)
1528+ where commandDef = do
1529+ (name, macro') <- newcommand
1530+ updateState $ \s -> s{ sMacros = M.insert name macro' (sMacros s) }
1531+ environmentDef = do
1532+ (name, macro1, macro2) <- newenvironment
1533+ updateState $ \s -> s{ sMacros =
1534+ M.insert name macro1 (sMacros s) }
1535+ updateState $ \s -> s{ sMacros =
1536+ M.insert ("end" <> name) macro2 (sMacros s) }
1537+ -- @\newenvironment{envname}[n-args][default]{begin}{end}@
1538+ -- is equivalent to
1539+ -- @\newcommand{\envname}[n-args][default]{begin}@
1540+ -- @\newcommand{\endenvname}@
1541+
1542+newcommand :: PandocMonad m => LP m (Text, Macro)
1543+newcommand = do
1544+ pos <- getPosition
1545+ Tok _ (CtrlSeq mtype) _ <- controlSeq "newcommand" <|>
1546+ controlSeq "renewcommand" <|>
1547+ controlSeq "providecommand"
1548+ optional $ symbol '*'
1549+ Tok _ (CtrlSeq name) txt <- withVerbatimMode $ anyControlSeq <|>
1550+ (symbol '{' *> spaces *> anyControlSeq <* spaces <* symbol '}')
1551+ spaces
1552+ numargs <- option 0 $ try bracketedNum
1553+ spaces
1554+ optarg <- option Nothing $ Just <$> try bracketedToks
1555+ spaces
1556+ contents <- braced
1557+ when (mtype == "newcommand") $ do
1558+ macros <- sMacros <$> getState
1559+ case M.lookup name macros of
1560+ Just _ -> report $ MacroAlreadyDefined (T.unpack txt) pos
1561+ Nothing -> return ()
1562+ return (name, Macro numargs optarg contents)
1563+
1564+newenvironment :: PandocMonad m => LP m (Text, Macro, Macro)
1565+newenvironment = do
1566+ pos <- getPosition
1567+ Tok _ (CtrlSeq mtype) _ <- controlSeq "newenvironment" <|>
1568+ controlSeq "renewenvironment" <|>
1569+ controlSeq "provideenvironment"
1570+ optional $ symbol '*'
1571+ symbol '{'
1572+ spaces
1573+ Tok _ Word name <- satisfyTok isWordTok
1574+ spaces
1575+ symbol '}'
1576+ spaces
1577+ numargs <- option 0 $ try bracketedNum
1578+ spaces
1579+ optarg <- option Nothing $ Just <$> try bracketedToks
1580+ spaces
1581+ startcontents <- braced
1582+ spaces
1583+ endcontents <- braced
1584+ when (mtype == "newenvironment") $ do
1585+ macros <- sMacros <$> getState
1586+ case M.lookup name macros of
1587+ Just _ -> report $ MacroAlreadyDefined (T.unpack name) pos
1588+ Nothing -> return ()
1589+ return (name, Macro numargs optarg startcontents,
1590+ Macro 0 Nothing endcontents)
1591+
1592+bracketedToks :: PandocMonad m => LP m [Tok]
1593+bracketedToks = do
1594+ symbol '['
1595+ manyTill anyTok (symbol ']')
1596+
1597+bracketedNum :: PandocMonad m => LP m Int
1598+bracketedNum = do
1599+ ds <- untokenize <$> bracketedToks
1600+ case safeRead (T.unpack ds) of
1601+ Just i -> return i
1602+ _ -> return 0
9991603
1000−opt :: PandocMonad m => LP m Inlines
1001−opt = bracketed inline
1604+setCaption :: PandocMonad m => LP m Blocks
1605+setCaption = do
1606+ ils <- tok
1607+ mblabel <- option Nothing $
1608+ try $ spaces >> controlSeq "label" >> (Just <$> tok)
1609+ let ils' = case mblabel of
1610+ Just lab -> ils <> spanWith
1611+ ("",[],[("data-label", stringify lab)]) mempty
1612+ Nothing -> ils
1613+ updateState $ \st -> st{ sCaption = Just ils' }
1614+ return mempty
10021615
1003−rawopt :: PandocMonad m => LP m String
1004−rawopt = do
1005− contents <- bracketed (many1 (noneOf "[]") <|> try (string "\\]") <|>
1006− try (string "\\[") <|> rawopt)
1007− optional sp
1008− return $ "[" ++ contents ++ "]"
1616+looseItem :: PandocMonad m => LP m Blocks
1617+looseItem = do
1618+ inListItem <- sInListItem <$> getState
1619+ guard $ not inListItem
1620+ skipopts
1621+ return mempty
10091622
1010−skipopts :: PandocMonad m => LP m ()
1011−skipopts = skipMany rawopt
1623+resetCaption :: PandocMonad m => LP m ()
1624+resetCaption = updateState $ \st -> st{ sCaption = Nothing }
10121625
1013−-- opts in angle brackets are used in beamer
1014−rawangle :: PandocMonad m => LP m ()
1015−rawangle = try $ do
1016− char '<'
1017− skipMany (noneOf ">")
1018− char '>'
1019− return ()
1626+section :: PandocMonad m => Attr -> Int -> LP m Blocks
1627+section (ident, classes, kvs) lvl = do
1628+ skipopts
1629+ contents <- grouped inline
1630+ lab <- option ident $
1631+ try (spaces >> controlSeq "label"
1632+ >> spaces >> toksToString <$> braced)
1633+ attr' <- registerHeader (lab, classes, kvs) contents
1634+ return $ headerWith attr' lvl contents
10201635
1021−skipangles :: PandocMonad m => LP m ()
1022−skipangles = skipMany rawangle
1636+blockCommand :: PandocMonad m => LP m Blocks
1637+blockCommand = try $ do
1638+ Tok _ (CtrlSeq name) txt <- anyControlSeq
1639+ guard $ name /= "begin" && name /= "end"
1640+ star <- option "" ("*" <$ symbol '*' <* optional sp)
1641+ let name' = name <> star
1642+ let names = ordNub [name', name]
1643+ let raw = do
1644+ guard $ isBlockCommand name || not (isInlineCommand name)
1645+ rawBlock "latex" <$> getRawCommand (txt <> star)
1646+ lookupListDefault raw names blockCommands
10231647
1024−inlineText :: PandocMonad m => LP m Inlines
1025−inlineText = str <$> many1 inlineChar
1648+closing :: PandocMonad m => LP m Blocks
1649+closing = do
1650+ contents <- tok
1651+ st <- getState
1652+ let extractInlines (MetaBlocks [Plain ys]) = ys
1653+ extractInlines (MetaBlocks [Para ys ]) = ys
1654+ extractInlines _ = []
1655+ let sigs = case lookupMeta "author" (sMeta st) of
1656+ Just (MetaList xs) ->
1657+ para $ trimInlines $ fromList $
1658+ intercalate [LineBreak] $ map extractInlines xs
1659+ _ -> mempty
1660+ return $ para (trimInlines contents) <> sigs
10261661
1027−inlineChar :: PandocMonad m => LP m Char
1028−inlineChar = noneOf "\\$%&~#{}^'`\"‘’“”-[] \t\n"
1662+blockCommands :: PandocMonad m => M.Map Text (LP m Blocks)
1663+blockCommands = M.fromList $
1664+ [ ("par", mempty <$ skipopts)
1665+ , ("parbox", braced >> grouped blocks)
1666+ , ("title", mempty <$ (skipopts *>
1667+ (grouped inline >>= addMeta "title")
1668+ <|> (grouped block >>= addMeta "title")))
1669+ , ("subtitle", mempty <$ (skipopts *> tok >>= addMeta "subtitle"))
1670+ , ("author", mempty <$ (skipopts *> authors))
1671+ -- -- in letter class, temp. store address & sig as title, author
1672+ , ("address", mempty <$ (skipopts *> tok >>= addMeta "address"))
1673+ , ("signature", mempty <$ (skipopts *> authors))
1674+ , ("date", mempty <$ (skipopts *> tok >>= addMeta "date"))
1675+ -- Koma-script metadata commands
1676+ , ("dedication", mempty <$ (skipopts *> tok >>= addMeta "dedication"))
1677+ -- sectioning
1678+ , ("part", section nullAttr (-1))
1679+ , ("part*", section nullAttr (-1))
1680+ , ("chapter", section nullAttr 0)
1681+ , ("chapter*", section ("",["unnumbered"],[]) 0)
1682+ , ("section", section nullAttr 1)
1683+ , ("section*", section ("",["unnumbered"],[]) 1)
1684+ , ("subsection", section nullAttr 2)
1685+ , ("subsection*", section ("",["unnumbered"],[]) 2)
1686+ , ("subsubsection", section nullAttr 3)
1687+ , ("subsubsection*", section ("",["unnumbered"],[]) 3)
1688+ , ("paragraph", section nullAttr 4)
1689+ , ("paragraph*", section ("",["unnumbered"],[]) 4)
1690+ , ("subparagraph", section nullAttr 5)
1691+ , ("subparagraph*", section ("",["unnumbered"],[]) 5)
1692+ -- beamer slides
1693+ , ("frametitle", section nullAttr 3)
1694+ , ("framesubtitle", section nullAttr 4)
1695+ -- letters
1696+ , ("opening", (para . trimInlines) <$> (skipopts *> tok))
1697+ , ("closing", skipopts *> closing)
1698+ --
1699+ , ("hrule", pure horizontalRule)
1700+ , ("strut", pure mempty)
1701+ , ("rule", skipopts *> tok *> tok *> pure horizontalRule)
1702+ , ("item", looseItem)
1703+ , ("documentclass", skipopts *> braced *> preamble)
1704+ , ("centerline", (para . trimInlines) <$> (skipopts *> tok))
1705+ , ("caption", skipopts *> setCaption)
1706+ , ("bibliography", mempty <$ (skipopts *> braced >>=
1707+ addMeta "bibliography" . splitBibs . toksToString))
1708+ , ("addbibresource", mempty <$ (skipopts *> braced >>=
1709+ addMeta "bibliography" . splitBibs . toksToString))
1710+ -- includes
1711+ , ("lstinputlisting", inputListing)
1712+ , ("graphicspath", graphicsPath)
1713+ -- hyperlink
1714+ , ("hypertarget", try $ braced >> grouped block)
1715+ -- LaTeX colors
1716+ , ("textcolor", coloredBlock "color")
1717+ , ("colorbox", coloredBlock "background-color")
1718+ ]
1719+
1720+
1721+environments :: PandocMonad m => M.Map Text (LP m Blocks)
1722+environments = M.fromList
1723+ [ ("document", env "document" blocks)
1724+ , ("abstract", mempty <$ (env "abstract" blocks >>= addMeta "abstract"))
1725+ , ("letter", env "letter" letterContents)
1726+ , ("minipage", env "minipage" $
1727+ skipopts *> spaces *> optional braced *> spaces *> blocks)
1728+ , ("figure", env "figure" $ skipopts *> figure)
1729+ , ("subfigure", env "subfigure" $ skipopts *> tok *> figure)
1730+ , ("center", env "center" blocks)
1731+ , ("longtable", env "longtable" $
1732+ resetCaption *> simpTable "longtable" False >>= addTableCaption)
1733+ , ("table", env "table" $
1734+ resetCaption *> skipopts *> blocks >>= addTableCaption)
1735+ , ("tabular*", env "tabular" $ simpTable "tabular*" True)
1736+ , ("tabularx", env "tabularx" $ simpTable "tabularx" True)
1737+ , ("tabular", env "tabular" $ simpTable "tabular" False)
1738+ , ("quote", blockQuote <$> env "quote" blocks)
1739+ , ("quotation", blockQuote <$> env "quotation" blocks)
1740+ , ("verse", blockQuote <$> env "verse" blocks)
1741+ , ("itemize", bulletList <$> listenv "itemize" (many item))
1742+ , ("description", definitionList <$> listenv "description" (many descItem))
1743+ , ("enumerate", orderedList')
1744+ , ("alltt", alltt <$> env "alltt" blocks)
1745+ , ("code", guardEnabled Ext_literate_haskell *>
1746+ (codeBlockWith ("",["sourceCode","literate","haskell"],[]) <$>
1747+ verbEnv "code"))
1748+ , ("comment", mempty <$ verbEnv "comment")
1749+ , ("verbatim", codeBlock <$> verbEnv "verbatim")
1750+ , ("Verbatim", fancyverbEnv "Verbatim")
1751+ , ("BVerbatim", fancyverbEnv "BVerbatim")
1752+ , ("lstlisting", do attr <- parseListingsOptions <$> option [] keyvals
1753+ codeBlockWith attr <$> verbEnv "lstlisting")
1754+ , ("minted", minted)
1755+ , ("obeylines", obeylines)
1756+ , ("displaymath", mathEnvWith para Nothing "displaymath")
1757+ , ("equation", mathEnvWith para Nothing "equation")
1758+ , ("equation*", mathEnvWith para Nothing "equation*")
1759+ , ("gather", mathEnvWith para (Just "gathered") "gather")
1760+ , ("gather*", mathEnvWith para (Just "gathered") "gather*")
1761+ , ("multline", mathEnvWith para (Just "gathered") "multline")
1762+ , ("multline*", mathEnvWith para (Just "gathered") "multline*")
1763+ , ("eqnarray", mathEnvWith para (Just "aligned") "eqnarray")
1764+ , ("eqnarray*", mathEnvWith para (Just "aligned") "eqnarray*")
1765+ , ("align", mathEnvWith para (Just "aligned") "align")
1766+ , ("align*", mathEnvWith para (Just "aligned") "align*")
1767+ , ("alignat", mathEnvWith para (Just "aligned") "alignat")
1768+ , ("alignat*", mathEnvWith para (Just "aligned") "alignat*")
1769+ , ("tikzpicture", rawVerbEnv "tikzpicture")
1770+ ]
10291771
10301772environment :: PandocMonad m => LP m Blocks
10311773environment = do
10321774 controlSeq "begin"
1033− name <- braced
1775+ name <- untokenize <$> braced
10341776 M.findWithDefault mzero name environments
10351777 <|> rawEnv name
10361778
1037−inlineEnvironment :: PandocMonad m => LP m Inlines
1038−inlineEnvironment = try $ do
1039− controlSeq "begin"
1040− name <- braced
1041− M.findWithDefault mzero name inlineEnvironments
1779+env :: PandocMonad m => Text -> LP m a -> LP m a
1780+env name p = p <* end_ name
10421781
1043−rawEnv :: PandocMonad m => String -> LP m Blocks
1782+rawEnv :: PandocMonad m => Text -> LP m Blocks
10441783rawEnv name = do
10451784 exts <- getOption readerExtensions
10461785 let parseRaw = extensionEnabled Ext_raw_tex exts
10471786 rawOptions <- mconcat <$> many rawopt
1048− let beginCommand = "\\begin{" ++ name ++ "}" ++ rawOptions
1787+ let beginCommand = "\\begin{" <> name <> "}" <> rawOptions
10491788 pos1 <- getPosition
10501789 (bs, raw) <- withRaw $ env name blocks
1051− raw' <- applyMacros' $ beginCommand ++ raw
1052− if raw' /= beginCommand ++ raw
1053− then parseFromString' blocks raw'
1054− else if parseRaw
1055− then return $ rawBlock "latex" $ beginCommand ++ raw'
1056− else do
1057− unless parseRaw $ do
1058− report $ SkippedContent beginCommand pos1
1059− pos2 <- getPosition
1060− report $ SkippedContent ("\\end{" ++ name ++ "}") pos2
1061− return bs
1062−
1063−rawVerbEnv :: PandocMonad m => String -> LP m Blocks
1790+ if parseRaw
1791+ then return $ rawBlock "latex"
1792+ $ T.unpack $ beginCommand <> untokenize raw
1793+ else do
1794+ unless parseRaw $ do
1795+ report $ SkippedContent (T.unpack beginCommand) pos1
1796+ pos2 <- getPosition
1797+ report $ SkippedContent ("\\end{" ++ T.unpack name ++ "}") pos2
1798+ return bs
1799+
1800+rawVerbEnv :: PandocMonad m => Text -> LP m Blocks
10641801rawVerbEnv name = do
10651802 pos <- getPosition
10661803 (_, raw) <- withRaw $ verbEnv name
1067− let raw' = "\\begin{tikzpicture}" ++ raw
1804+ let raw' = "\\begin{tikzpicture}" ++ toksToString raw
10681805 exts <- getOption readerExtensions
10691806 let parseRaw = extensionEnabled Ext_raw_tex exts
10701807 if parseRaw
@@ -1073,36 +1810,118 @@ rawVerbEnv name = do
10731810 report $ SkippedContent raw' pos
10741811 return mempty
10751812
1076−----
1813+verbEnv :: PandocMonad m => Text -> LP m String
1814+verbEnv name = withVerbatimMode $ do
1815+ skipopts
1816+ optional blankline
1817+ res <- manyTill anyTok (end_ name)
1818+ return $ stripTrailingNewlines $ toksToString res
1819+
1820+fancyverbEnv :: PandocMonad m => Text -> LP m Blocks
1821+fancyverbEnv name = do
1822+ options <- option [] keyvals
1823+ let kvs = [ (if k == "firstnumber"
1824+ then "startFrom"
1825+ else k, v) | (k,v) <- options ]
1826+ let classes = [ "numberLines" |
1827+ lookup "numbers" options == Just "left" ]
1828+ let attr = ("",classes,kvs)
1829+ codeBlockWith attr <$> verbEnv name
1830+
1831+obeylines :: PandocMonad m => LP m Blocks
1832+obeylines = do
1833+ para . fromList . removeLeadingTrailingBreaks .
1834+ walk softBreakToHard . toList <$> env "obeylines" inlines
1835+ where softBreakToHard SoftBreak = LineBreak
1836+ softBreakToHard x = x
1837+ removeLeadingTrailingBreaks = reverse . dropWhile isLineBreak .
1838+ reverse . dropWhile isLineBreak
1839+ isLineBreak LineBreak = True
1840+ isLineBreak _ = False
1841+
1842+minted :: PandocMonad m => LP m Blocks
1843+minted = do
1844+ options <- option [] keyvals
1845+ lang <- toksToString <$> braced
1846+ let kvs = [ (if k == "firstnumber"
1847+ then "startFrom"
1848+ else k, v) | (k,v) <- options ]
1849+ let classes = [ lang | not (null lang) ] ++
1850+ [ "numberLines" |
1851+ lookup "linenos" options == Just "true" ]
1852+ let attr = ("",classes,kvs)
1853+ codeBlockWith attr <$> verbEnv "minted"
1854+
1855+letterContents :: PandocMonad m => LP m Blocks
1856+letterContents = do
1857+ bs <- blocks
1858+ st <- getState
1859+ -- add signature (author) and address (title)
1860+ let addr = case lookupMeta "address" (sMeta st) of
1861+ Just (MetaBlocks [Plain xs]) ->
1862+ para $ trimInlines $ fromList xs
1863+ _ -> mempty
1864+ return $ addr <> bs -- sig added by \closing
1865+
1866+figure :: PandocMonad m => LP m Blocks
1867+figure = try $ do
1868+ resetCaption
1869+ blocks >>= addImageCaption
1870+
1871+addImageCaption :: PandocMonad m => Blocks -> LP m Blocks
1872+addImageCaption = walkM go
1873+ where go (Image attr alt (src,tit))
1874+ | not ("fig:" `isPrefixOf` tit) = do
1875+ mbcapt <- sCaption <$> getState
1876+ return $ case mbcapt of
1877+ Just ils -> Image attr (toList ils) (src, "fig:" ++ tit)
1878+ Nothing -> Image attr alt (src,tit)
1879+ go x = return x
1880+
1881+coloredBlock :: PandocMonad m => String -> LP m Blocks
1882+coloredBlock stylename = do
1883+ skipopts
1884+ color <- braced
1885+ let constructor = divWith ("",[],[("style",stylename ++ ": " ++ toksToString color)])
1886+ inlineContents <|> constructor <$> blockContents
1887+ where inlineContents = do
1888+ ils <- grouped inline
1889+ rest <- inlines
1890+ return (para (ils <> rest))
1891+ blockContents = grouped block
10771892
1078−maybeAddExtension :: String -> FilePath -> FilePath
1079−maybeAddExtension ext fp =
1080− if null (takeExtension fp)
1081− then addExtension fp ext
1082− else fp
1893+graphicsPath :: PandocMonad m => LP m Blocks
1894+graphicsPath = do
1895+ ps <- map toksToString <$> (bgroup *> manyTill braced egroup)
1896+ getResourcePath >>= setResourcePath . (++ ps)
1897+ return mempty
10831898
1084−include :: PandocMonad m => LP m Blocks
1085−include = do
1086− fs' <- try $ do
1087− char '\\'
1088− name <- try (string "include")
1089− <|> try (string "input")
1090− <|> try (string "subfile")
1091− <|> string "usepackage"
1092− -- skip options
1093− skipMany $ try $ char '[' *> manyTill anyChar (char ']')
1094− fs <- (map trim . splitBy (==',')) <$> braced
1095− return $ if name == "usepackage"
1096− then map (maybeAddExtension ".sty") fs
1097− else map (maybeAddExtension ".tex") fs
1098− dirs <- (splitBy (==':') . fromMaybe ".") <$> lookupEnv "TEXINPUTS"
1099− mconcat <$> mapM (insertIncludedFile blocks dirs) fs'
1899+splitBibs :: String -> [Inlines]
1900+splitBibs = map (str . flip replaceExtension "bib" . trim) . splitBy (==',')
1901+
1902+alltt :: Blocks -> Blocks
1903+alltt = walk strToCode
1904+ where strToCode (Str s) = Code nullAttr s
1905+ strToCode Space = RawInline (Format "latex") "\\ "
1906+ strToCode SoftBreak = LineBreak
1907+ strToCode x = x
1908+
1909+parseListingsOptions :: [(String, String)] -> Attr
1910+parseListingsOptions options =
1911+ let kvs = [ (if k == "firstnumber"
1912+ then "startFrom"
1913+ else k, v) | (k,v) <- options ]
1914+ classes = [ "numberLines" |
1915+ lookup "numbers" options == Just "left" ]
1916+ ++ maybeToList (lookup "language" options
1917+ >>= fromListingsLanguage)
1918+ in (fromMaybe "" (lookup "label" options), classes, kvs)
11001919
11011920inputListing :: PandocMonad m => LP m Blocks
11021921inputListing = do
11031922 pos <- getPosition
11041923 options <- option [] keyvals
1105− f <- filter (/='"') <$> braced
1924+ f <- filter (/='"') . toksToString <$> braced
11061925 dirs <- (splitBy (==':') . fromMaybe ".") <$> lookupEnv "TEXINPUTS"
11071926 mbCode <- readFileFromDirs dirs f
11081927 codeLines <- case mbCode of
@@ -1121,169 +1940,10 @@ inputListing = do
11211940 drop (firstline - 1) codeLines
11221941 return $ codeBlockWith (ident,ordNub (classes ++ language),kvs) codeContents
11231942
1124−parseListingsOptions :: [(String, String)] -> Attr
1125−parseListingsOptions options =
1126− let kvs = [ (if k == "firstnumber"
1127− then "startFrom"
1128− else k, v) | (k,v) <- options ]
1129− classes = [ "numberLines" |
1130− lookup "numbers" options == Just "left" ]
1131− ++ maybeToList (lookup "language" options
1132− >>= fromListingsLanguage)
1133− in (fromMaybe "" (lookup "label" options), classes, kvs)
1134−
1135−----
1136−
1137−keyval :: PandocMonad m => LP m (String, String)
1138−keyval = try $ do
1139− key <- many1 alphaNum
1140− val <- option "" $ char '=' >> braced <|> (many1 (alphaNum <|> oneOf ".:-|\\"))
1141− skipMany spaceChar
1142− optional (char ',')
1143− skipMany spaceChar
1144− return (key, val)
1145−
1146−
1147−keyvals :: PandocMonad m => LP m [(String, String)]
1148−keyvals = try $ char '[' *> manyTill keyval (char ']')
1149−
1150−alltt :: PandocMonad m => String -> LP m Blocks
1151−alltt t = walk strToCode <$> parseFromString' blocks
1152− (substitute " " "\\ " $ substitute "%" "\\%" $
1153− intercalate "\\\\\n" $ lines t)
1154− where strToCode (Str s) = Code nullAttr s
1155− strToCode x = x
1156−
1157−rawLaTeXBlock :: PandocMonad m => LP m String
1158−rawLaTeXBlock = snd <$> try (withRaw (environment <|> blockCommand))
1159−
1160−rawLaTeXInline :: PandocMonad m => LP m Inline
1161−rawLaTeXInline = do
1162− raw <- (snd <$> withRaw inlineCommand)
1163− <|> (snd <$> withRaw inlineEnvironment)
1164− <|> (snd <$> withRaw blockCommand)
1165− RawInline "latex" <$> applyMacros' raw
1166−
1167−addImageCaption :: PandocMonad m => Blocks -> LP m Blocks
1168−addImageCaption = walkM go
1169− where go (Image attr alt (src,tit))
1170− | not ("fig:" `isPrefixOf` tit) = do
1171− mbcapt <- stateCaption <$> getState
1172− return $ case mbcapt of
1173− Just ils -> Image attr (toList ils) (src, "fig:" ++ tit)
1174− Nothing -> Image attr alt (src,tit)
1175− go x = return x
1176−
1177−addTableCaption :: PandocMonad m => Blocks -> LP m Blocks
1178−addTableCaption = walkM go
1179− where go (Table c als ws hs rs) = do
1180− mbcapt <- stateCaption <$> getState
1181− return $ case mbcapt of
1182− Just ils -> Table (toList ils) als ws hs rs
1183− Nothing -> Table c als ws hs rs
1184− go x = return x
1185−
1186−environments :: PandocMonad m => M.Map String (LP m Blocks)
1187−environments = M.fromList
1188− [ ("document", env "document" blocks <* skipMany anyChar)
1189− , ("abstract", mempty <$ (env "abstract" blocks >>= addMeta "abstract"))
1190− , ("letter", env "letter" letterContents)
1191− , ("minipage", env "minipage" $
1192− skipopts *> spaces' *> optional braced *> spaces' *> blocks)
1193− , ("figure", env "figure" $ skipopts *> figure)
1194− , ("subfigure", env "subfigure" $ skipopts *> tok *> figure)
1195− , ("center", env "center" blocks)
1196− , ("longtable", env "longtable" $
1197− resetCaption *> simpTable "longtable" False >>= addTableCaption)
1198− , ("table", env "table" $
1199− resetCaption *> skipopts *> blocks >>= addTableCaption)
1200− , ("tabular*", env "tabular" $ simpTable "tabular*" True)
1201− , ("tabularx", env "tabularx" $ simpTable "tabularx" True)
1202− , ("tabular", env "tabular" $ simpTable "tabular" False)
1203− , ("quote", blockQuote <$> env "quote" blocks)
1204− , ("quotation", blockQuote <$> env "quotation" blocks)
1205− , ("verse", blockQuote <$> env "verse" blocks)
1206− , ("itemize", bulletList <$> listenv "itemize" (many item))
1207− , ("description", definitionList <$> listenv "description" (many descItem))
1208− , ("enumerate", orderedList')
1209− , ("alltt", alltt =<< verbEnv "alltt")
1210− , ("code", guardEnabled Ext_literate_haskell *>
1211− (codeBlockWith ("",["sourceCode","literate","haskell"],[]) <$>
1212− verbEnv "code"))
1213− , ("comment", mempty <$ verbEnv "comment")
1214− , ("verbatim", codeBlock <$> verbEnv "verbatim")
1215− , ("Verbatim", fancyverbEnv "Verbatim")
1216− , ("BVerbatim", fancyverbEnv "BVerbatim")
1217− , ("lstlisting", do attr <- parseListingsOptions <$> option [] keyvals
1218− codeBlockWith attr <$> verbEnv "lstlisting")
1219− , ("minted", do options <- option [] keyvals
1220− lang <- grouped (many1 $ satisfy (/='}'))
1221− let kvs = [ (if k == "firstnumber"
1222− then "startFrom"
1223− else k, v) | (k,v) <- options ]
1224− let classes = [ lang | not (null lang) ] ++
1225− [ "numberLines" |
1226− lookup "linenos" options == Just "true" ]
1227− let attr = ("",classes,kvs)
1228− codeBlockWith attr <$> verbEnv "minted")
1229− , ("obeylines", parseFromString
1230− (para . trimInlines . mconcat <$> many inline) =<<
1231− intercalate "\\\\\n" . lines <$> verbEnv "obeylines")
1232− , ("displaymath", mathEnvWith para Nothing "displaymath")
1233− , ("equation", mathEnvWith para Nothing "equation")
1234− , ("equation*", mathEnvWith para Nothing "equation*")
1235− , ("gather", mathEnvWith para (Just "gathered") "gather")
1236− , ("gather*", mathEnvWith para (Just "gathered") "gather*")
1237− , ("multline", mathEnvWith para (Just "gathered") "multline")
1238− , ("multline*", mathEnvWith para (Just "gathered") "multline*")
1239− , ("eqnarray", mathEnvWith para (Just "aligned") "eqnarray")
1240− , ("eqnarray*", mathEnvWith para (Just "aligned") "eqnarray*")
1241− , ("align", mathEnvWith para (Just "aligned") "align")
1242− , ("align*", mathEnvWith para (Just "aligned") "align*")
1243− , ("alignat", mathEnvWith para (Just "aligned") "alignat")
1244− , ("alignat*", mathEnvWith para (Just "aligned") "alignat*")
1245− , ("tikzpicture", rawVerbEnv "tikzpicture")
1246− ]
1247−
1248−figure :: PandocMonad m => LP m Blocks
1249−figure = try $ do
1250− resetCaption
1251− blocks >>= addImageCaption
1252−
1253−letterContents :: PandocMonad m => LP m Blocks
1254−letterContents = do
1255− bs <- blocks
1256− st <- getState
1257− -- add signature (author) and address (title)
1258− let addr = case lookupMeta "address" (stateMeta st) of
1259− Just (MetaBlocks [Plain xs]) ->
1260− para $ trimInlines $ fromList xs
1261− _ -> mempty
1262− return $ addr <> bs -- sig added by \closing
1263−
1264−closing :: PandocMonad m => LP m Blocks
1265−closing = do
1266− contents <- tok
1267− st <- getState
1268− let extractInlines (MetaBlocks [Plain ys]) = ys
1269− extractInlines (MetaBlocks [Para ys ]) = ys
1270− extractInlines _ = []
1271− let sigs = case lookupMeta "author" (stateMeta st) of
1272− Just (MetaList xs) ->
1273− para $ trimInlines $ fromList $
1274− intercalate [LineBreak] $ map extractInlines xs
1275− _ -> mempty
1276− return $ para (trimInlines contents) <> sigs
1943+-- lists
12771944
12781945item :: PandocMonad m => LP m Blocks
1279−item = blocks *> controlSeq "item" *> skipopts *> blocks
1280−
1281−looseItem :: PandocMonad m => LP m Blocks
1282−looseItem = do
1283− ctx <- stateParserContext `fmap` getState
1284− if ctx == ListItemState
1285− then mzero
1286− else return mempty
1946+item = void blocks *> controlSeq "item" *> skipopts *> blocks
12871947
12881948descItem :: PandocMonad m => LP m (Inlines, [Blocks])
12891949descItem = do
@@ -1294,302 +1954,210 @@ descItem = do
12941954 bs <- blocks
12951955 return (ils, [bs])
12961956
1297−env :: PandocMonad m => String -> LP m a -> LP m a
1298−env name p = p <*
1299− (try (controlSeq "end" *> braced >>= guard . (== name))
1300− <?> ("\\end{" ++ name ++ "}"))
1301−
1302−listenv :: PandocMonad m => String -> LP m a -> LP m a
1957+listenv :: PandocMonad m => Text -> LP m a -> LP m a
13031958listenv name p = try $ do
1304− oldCtx <- stateParserContext `fmap` getState
1305− updateState $ \st -> st{ stateParserContext = ListItemState }
1959+ oldInListItem <- sInListItem `fmap` getState
1960+ updateState $ \st -> st{ sInListItem = True }
13061961 res <- env name p
1307− updateState $ \st -> st{ stateParserContext = oldCtx }
1962+ updateState $ \st -> st{ sInListItem = oldInListItem }
13081963 return res
13091964
1310−mathEnvWith :: PandocMonad m
1311− => (Inlines -> a) -> Maybe String -> String -> LP m a
1312−mathEnvWith f innerEnv name = f <$> mathDisplay (inner <$> mathEnv name)
1313− where inner x = case innerEnv of
1314− Nothing -> x
1315− Just y -> "\\begin{" ++ y ++ "}\n" ++ x ++
1316− "\\end{" ++ y ++ "}"
1317−
1318−mathEnv :: PandocMonad m => String -> LP m String
1319−mathEnv name = do
1320− skipopts
1321− optional blankline
1322− let endEnv = try $ controlSeq "end" *> braced >>= guard . (== name)
1323− charMuncher = skipMany comment *>
1324− (many1 (noneOf "\\%") <|> try (string "\\%")
1325− <|> try (string "\\\\") <|> count 1 anyChar)
1326− res <- concat <$> manyTill charMuncher endEnv
1327− return $ stripTrailingNewlines res
1328−
1329−verbEnv :: PandocMonad m => String -> LP m String
1330−verbEnv name = do
1331− skipopts
1332− optional blankline
1333− let endEnv = try $ controlSeq "end" *> braced >>= guard . (== name)
1334− charMuncher = anyChar
1335− res <- manyTill charMuncher endEnv
1336− return $ stripTrailingNewlines res
1337−
1338−fancyverbEnv :: PandocMonad m => String -> LP m Blocks
1339−fancyverbEnv name = do
1340− options <- option [] keyvals
1341− let kvs = [ (if k == "firstnumber"
1342− then "startFrom"
1343− else k, v) | (k,v) <- options ]
1344− let classes = [ "numberLines" |
1345− lookup "numbers" options == Just "left" ]
1346− let attr = ("",classes,kvs)
1347− codeBlockWith attr <$> verbEnv name
1348−
13491965orderedList' :: PandocMonad m => LP m Blocks
13501966orderedList' = try $ do
1351− optional sp
1352− (_, style, delim) <- option (1, DefaultStyle, DefaultDelim) $
1353− try $ char '[' *> anyOrderedListMarker <* char ']'
13541967 spaces
1355− optional $ try $ controlSeq "setlength" *> grouped (controlSeq "itemindent") *> braced
1968+ let markerSpec = do
1969+ symbol '['
1970+ ts <- toksToString <$> manyTill anyTok (symbol ']')
1971+ case runParser anyOrderedListMarker def "option" ts of
1972+ Right r -> return r
1973+ Left _ -> do
1974+ pos <- getPosition
1975+ report $ SkippedContent ("[" ++ ts ++ "]") pos
1976+ return (1, DefaultStyle, DefaultDelim)
1977+ (_, style, delim) <- option (1, DefaultStyle, DefaultDelim) markerSpec
13561978 spaces
1357− start <- option 1 $ try $ do controlSeq "setcounter"
1358− grouped (string "enum" *> many1 (oneOf "iv"))
1979+ optional $ try $ controlSeq "setlength"
1980+ *> grouped (count 1 $ controlSeq "itemindent")
1981+ *> braced
1982+ spaces
1983+ start <- option 1 $ try $ do pos <- getPosition
1984+ controlSeq "setcounter"
1985+ ctr <- toksToString <$> braced
1986+ guard $ "enum" `isPrefixOf` ctr
1987+ guard $ all (`elem` ['i','v']) (drop 4 ctr)
13591988 optional sp
1360− num <- grouped (many1 digit)
1361− spaces
1362− return (read num + 1 :: Int)
1989+ num <- toksToString <$> braced
1990+ case safeRead num of
1991+ Just i -> return (i + 1 :: Int)
1992+ Nothing -> do
1993+ report $ SkippedContent
1994+ ("\\setcounter{" ++ ctr ++
1995+ "}{" ++ num ++ "}") pos
1996+ return 1
13631997 bs <- listenv "enumerate" (many item)
13641998 return $ orderedListWith (start, style, delim) bs
13651999
1366−paragraph :: PandocMonad m => LP m Blocks
1367−paragraph = do
1368− x <- trimInlines . mconcat <$> many1 inline
1369− if x == mempty
1370− then return mempty
1371− else return $ para x
1372−
1373−preamble :: PandocMonad m => LP m Blocks
1374−preamble = mempty <$> manyTill preambleBlock beginDoc
1375− where beginDoc = lookAhead $ try $ controlSeq "begin" *> string "{document}"
1376− preambleBlock = void comment
1377− <|> void sp
1378− <|> void blanklines
1379− <|> void include
1380− <|> void macro
1381− <|> void blockCommand
1382− <|> void anyControlSeq
1383− <|> void braced
1384− <|> void anyChar
1385−
1386−-------
1387−
1388−-- citations
1389−
1390−addPrefix :: [Inline] -> [Citation] -> [Citation]
1391−addPrefix p (k:ks) = k {citationPrefix = p ++ citationPrefix k} : ks
1392−addPrefix _ _ = []
1393−
1394−addSuffix :: [Inline] -> [Citation] -> [Citation]
1395−addSuffix s ks@(_:_) =
1396− let k = last ks
1397− in init ks ++ [k {citationSuffix = citationSuffix k ++ s}]
1398−addSuffix _ _ = []
1399−
1400−simpleCiteArgs :: PandocMonad m => LP m [Citation]
1401−simpleCiteArgs = try $ do
1402− first <- optionMaybe $ toList <$> opt
1403− second <- optionMaybe $ toList <$> opt
1404− keys <- try $ bgroup *> (manyTill citationLabel egroup)
1405− let (pre, suf) = case (first , second ) of
1406− (Just s , Nothing) -> (mempty, s )
1407− (Just s , Just t ) -> (s , t )
1408− _ -> (mempty, mempty)
1409− conv k = Citation { citationId = k
1410− , citationPrefix = []
1411− , citationSuffix = []
1412− , citationMode = NormalCitation
1413− , citationHash = 0
1414− , citationNoteNum = 0
1415− }
1416− return $ addPrefix pre $ addSuffix suf $ map conv keys
1417−
1418−citationLabel :: PandocMonad m => LP m String
1419−citationLabel = optional sp *>
1420− (many1 (satisfy isBibtexKeyChar)
1421− <* optional sp
1422− <* optional (char ',')
1423− <* optional sp)
1424− where isBibtexKeyChar c = isAlphaNum c || c `elem` (".:;?!`'()/*@_+=-[]" :: String)
2000+-- tables
14252001
1426−cites :: PandocMonad m => CitationMode -> Bool -> LP m [Citation]
1427−cites mode multi = try $ do
1428− cits <- if multi
1429− then many1 simpleCiteArgs
1430− else count 1 simpleCiteArgs
1431− let cs = concat cits
1432− return $ case mode of
1433− AuthorInText -> case cs of
1434− (c:rest) -> c {citationMode = mode} : rest
1435− [] -> []
1436− _ -> map (\a -> a {citationMode = mode}) cs
2002+hline :: PandocMonad m => LP m ()
2003+hline = try $ do
2004+ spaces
2005+ controlSeq "hline" <|>
2006+ -- booktabs rules:
2007+ controlSeq "toprule" <|>
2008+ controlSeq "bottomrule" <|>
2009+ controlSeq "midrule" <|>
2010+ controlSeq "endhead" <|>
2011+ controlSeq "endfirsthead"
2012+ spaces
2013+ optional $ bracketed inline
2014+ return ()
14372015
1438−citation :: PandocMonad m => String -> CitationMode -> Bool -> LP m Inlines
1439−citation name mode multi = do
1440− (c,raw) <- withRaw $ cites mode multi
1441− return $ cite c (rawInline "latex" $ "\\" ++ name ++ raw)
2016+lbreak :: PandocMonad m => LP m Tok
2017+lbreak = (controlSeq "\\" <|> controlSeq "tabularnewline") <* spaces
14422018
1443−complexNatbibCitation :: PandocMonad m => CitationMode -> LP m Inlines
1444−complexNatbibCitation mode = try $ do
1445− let ils = (toList . trimInlines . mconcat) <$>
1446− many (notFollowedBy (oneOf "\\};") >> inline)
1447− let parseOne = try $ do
1448− skipSpaces
1449− pref <- ils
1450− cit' <- inline -- expect a citation
1451− let citlist = toList cit'
1452− cits' <- case citlist of
1453− [Cite cs _] -> return cs
1454− _ -> mzero
1455− suff <- ils
1456− skipSpaces
1457− optional $ char ';'
1458− return $ addPrefix pref $ addSuffix suff cits'
1459− (c:cits, raw) <- withRaw $ grouped parseOne
1460− return $ cite (c{ citationMode = mode }:cits)
1461− (rawInline "latex" $ "\\citetext" ++ raw)
2019+amp :: PandocMonad m => LP m Tok
2020+amp = symbol '&'
14622021
1463−-- tables
2022+-- Split a Word into individual Symbols (for parseAligns)
2023+splitWordTok :: PandocMonad m => LP m ()
2024+splitWordTok = do
2025+ inp <- getInput
2026+ case inp of
2027+ (Tok spos Word t : rest) -> do
2028+ setInput $ map (Tok spos Symbol . T.singleton) (T.unpack t) ++ rest
2029+ _ -> return ()
14642030
1465−parseAligns :: PandocMonad m => LP m [(Alignment, Double, (String, String))]
2031+parseAligns :: PandocMonad m => LP m [(Alignment, Double, ([Tok], [Tok]))]
14662032parseAligns = try $ do
1467− bgroup
1468− let maybeBar = skipMany $ sp <|> () <$ char '|' <|> () <$ (char '@' >> braced)
1469− maybeBar
1470− let cAlign = AlignCenter <$ char 'c'
1471− let lAlign = AlignLeft <$ char 'l'
1472− let rAlign = AlignRight <$ char 'r'
1473− let parAlign = AlignLeft <$ char 'p'
1474− -- algins from tabularx
1475− let xAlign = AlignLeft <$ char 'X'
1476− let mAlign = AlignLeft <$ char 'm'
1477− let bAlign = AlignLeft <$ char 'b'
1478− let alignChar = cAlign <|> lAlign <|> rAlign <|> parAlign
1479− <|> xAlign <|> mAlign <|> bAlign
1480− let alignPrefix = char '>' >> braced
1481− let alignSuffix = char '<' >> braced
2033+ let maybeBar = skipMany $
2034+ sp <|> () <$ symbol '|' <|> () <$ (symbol '@' >> braced)
2035+ let cAlign = AlignCenter <$ symbol 'c'
2036+ let lAlign = AlignLeft <$ symbol 'l'
2037+ let rAlign = AlignRight <$ symbol 'r'
2038+ let parAlign = AlignLeft <$ symbol 'p'
2039+ -- aligns from tabularx
2040+ let xAlign = AlignLeft <$ symbol 'X'
2041+ let mAlign = AlignLeft <$ symbol 'm'
2042+ let bAlign = AlignLeft <$ symbol 'b'
2043+ let alignChar = splitWordTok *> ( cAlign <|> lAlign <|> rAlign <|> parAlign
2044+ <|> xAlign <|> mAlign <|> bAlign )
2045+ let alignPrefix = symbol '>' >> braced
2046+ let alignSuffix = symbol '<' >> braced
14822047 let colWidth = try $ do
1483− char '{'
1484− ds <- many1 (oneOf "0123456789.")
2048+ symbol '{'
2049+ ds <- trim . toksToString <$> manyTill anyTok (controlSeq "linewidth")
14852050 spaces
1486− string "\\linewidth"
1487− char '}'
2051+ symbol '}'
14882052 case safeRead ds of
14892053 Just w -> return w
14902054 Nothing -> return 0.0
1491− let alignSpec = do
2055+ let alignSpec = try $ do
14922056 spaces
1493− pref <- option "" alignPrefix
2057+ pref <- option [] alignPrefix
14942058 spaces
14952059 al <- alignChar
1496− width <- colWidth <|> option 0.0 (do s <- braced
2060+ width <- colWidth <|> option 0.0 (do s <- toksToString <$> braced
14972061 pos <- getPosition
14982062 report $ SkippedContent s pos
14992063 return 0.0)
15002064 spaces
1501− suff <- option "" alignSuffix
2065+ suff <- option [] alignSuffix
15022066 return (al, width, (pref, suff))
1503− aligns' <- sepEndBy alignSpec maybeBar
2067+ bgroup
2068+ spaces
2069+ maybeBar
2070+ aligns' <- many (alignSpec <* maybeBar)
15042071 spaces
15052072 egroup
15062073 spaces
1507− return $ aligns'
1508−
1509−hline :: PandocMonad m => LP m ()
1510−hline = try $ do
1511− spaces'
1512− controlSeq "hline" <|>
1513− -- booktabs rules:
1514− controlSeq "toprule" <|>
1515− controlSeq "bottomrule" <|>
1516− controlSeq "midrule" <|>
1517− controlSeq "endhead" <|>
1518− controlSeq "endfirsthead"
1519− spaces'
1520− optional $ bracketed (many1 (satisfy (/=']')))
1521− return ()
1522−
1523−lbreak :: PandocMonad m => LP m ()
1524−lbreak = () <$ try (spaces' *>
1525− (controlSeq "\\" <|> controlSeq "tabularnewline") <*
1526− spaces')
1527−
1528−amp :: PandocMonad m => LP m ()
1529−amp = () <$ try (spaces' *> char '&' <* spaces')
2074+ return aligns'
15302075
15312076parseTableRow :: PandocMonad m
1532− => String -- ^ table environment name
1533− -> [(String, String)] -- ^ pref/suffixes
2077+ => Text -- ^ table environment name
2078+ -> [([Tok], [Tok])] -- ^ pref/suffixes
15342079 -> LP m [Blocks]
1535−parseTableRow envname prefsufs = try $ do
2080+parseTableRow envname prefsufs = do
2081+ notFollowedBy (spaces *> end_ envname)
15362082 let cols = length prefsufs
1537− let tableCellRaw = concat <$> many
1538− (do notFollowedBy amp
1539− notFollowedBy lbreak
1540− notFollowedBy $ () <$ try (string ("\\end{" ++ envname ++ "}"))
1541− many1 (noneOf "&%\n\r\\")
1542− <|> try (string "\\&")
1543− <|> count 1 anyChar)
1544− let plainify bs = case toList bs of
1545− [Para ils] -> plain (fromList ils)
1546− _ -> bs
1547− rawcells <- sepBy1 tableCellRaw amp
1548− guard $ length rawcells == cols
1549− let rawcells' = zipWith (\c (p, s) -> p ++ trim c ++ s) rawcells prefsufs
1550− let tableCell = plainify <$> blocks
1551− cells' <- mapM (parseFromString' tableCell) rawcells'
1552− let numcells = length cells'
2083+ -- add prefixes and suffixes in token stream:
2084+ let celltoks (pref, suff) = do
2085+ prefpos <- getPosition
2086+ contents <- many (notFollowedBy
2087+ (() <$ amp <|> () <$ lbreak <|> end_ envname)
2088+ >> anyTok)
2089+ suffpos <- getPosition
2090+ option [] (count 1 amp)
2091+ return $ map (setpos (sourceLine prefpos, sourceColumn prefpos)) pref
2092+ ++ contents ++
2093+ map (setpos (sourceLine suffpos, sourceColumn suffpos)) suff
2094+ rawcells <- sequence (map celltoks prefsufs)
2095+ oldInput <- getInput
2096+ cells <- sequence $ map (\ts -> setInput ts >> parseTableCell) rawcells
2097+ setInput oldInput
2098+ spaces
2099+ let numcells = length cells
15532100 guard $ numcells <= cols && numcells >= 1
1554− guard $ cells' /= [mempty]
2101+ guard $ cells /= [mempty]
15552102 -- note: a & b in a three-column table leaves an empty 3rd cell:
1556− let cells'' = cells' ++ replicate (cols - numcells) mempty
1557− spaces'
1558− return cells''
2103+ return $ cells ++ replicate (cols - numcells) mempty
15592104
1560−spaces' :: PandocMonad m => LP m ()
1561−spaces' = spaces *> skipMany (comment *> spaces)
2105+parseTableCell :: PandocMonad m => LP m Blocks
2106+parseTableCell = do
2107+ let plainify bs = case toList bs of
2108+ [Para ils] -> plain (fromList ils)
2109+ _ -> bs
2110+ updateState $ \st -> st{ sInTableCell = True }
2111+ cells <- plainify <$> blocks
2112+ updateState $ \st -> st{ sInTableCell = False }
2113+ return cells
15622114
1563−simpTable :: PandocMonad m => String -> Bool -> LP m Blocks
2115+simpTable :: PandocMonad m => Text -> Bool -> LP m Blocks
15642116simpTable envname hasWidthParameter = try $ do
1565− when hasWidthParameter $ () <$ (spaces' >> tok)
2117+ when hasWidthParameter $ () <$ (spaces >> tok)
15662118 skipopts
15672119 colspecs <- parseAligns
15682120 let (aligns, widths, prefsufs) = unzip3 colspecs
15692121 let cols = length colspecs
15702122 optional $ controlSeq "caption" *> skipopts *> setCaption
15712123 optional lbreak
1572− spaces'
2124+ spaces
15732125 skipMany hline
1574− spaces'
2126+ spaces
15752127 header' <- option [] $ try (parseTableRow envname prefsufs <*
15762128 lbreak <* many1 hline)
1577− spaces'
2129+ spaces
15782130 rows <- sepEndBy (parseTableRow envname prefsufs)
15792131 (lbreak <* optional (skipMany hline))
1580− spaces'
2132+ spaces
15812133 optional $ controlSeq "caption" *> skipopts *> setCaption
15822134 optional lbreak
1583− spaces'
2135+ spaces
15842136 let header'' = if null header'
15852137 then replicate cols mempty
15862138 else header'
15872139 lookAhead $ controlSeq "end" -- make sure we're at end
15882140 return $ table mempty (zip aligns widths) header'' rows
15892141
1590−removeDoubleQuotes :: String -> String
1591−removeDoubleQuotes ('"':xs) =
1592− case reverse xs of
1593− '"':ys -> reverse ys
1594− _ -> '"':xs
1595−removeDoubleQuotes xs = xs
2142+addTableCaption :: PandocMonad m => Blocks -> LP m Blocks
2143+addTableCaption = walkM go
2144+ where go (Table c als ws hs rs) = do
2145+ mbcapt <- sCaption <$> getState
2146+ return $ case mbcapt of
2147+ Just ils -> Table (toList ils) als ws hs rs
2148+ Nothing -> Table c als ws hs rs
2149+ go x = return x
2150+
2151+
2152+block :: PandocMonad m => LP m Blocks
2153+block = (mempty <$ spaces1)
2154+ <|> environment
2155+ <|> include
2156+ <|> macroDef
2157+ <|> blockCommand
2158+ <|> paragraph
2159+ <|> grouped block
2160+
2161+blocks :: PandocMonad m => LP m Blocks
2162+blocks = mconcat <$> many block
2163+
src/Text/Pandoc/Readers/LaTeX/Types.hsnew file+48
@@ -0,0 +1,48 @@
1+{-
2+Copyright (C) 2017 John MacFarlane <jgm@berkeley.edu>
3+
4+This program is free software; you can redistribute it and/or modify
5+it under the terms of the GNU General Public License as published by
6+the Free Software Foundation; either version 2 of the License, or
7+(at your option) any later version.
8+
9+This program is distributed in the hope that it will be useful,
10+but WITHOUT ANY WARRANTY; without even the implied warranty of
11+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+GNU General Public License for more details.
13+
14+You should have received a copy of the GNU General Public License
15+along with this program; if not, write to the Free Software
16+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17+-}
18+
19+{- |
20+ Module : Text.Pandoc.Readers.LaTeX.Types
21+ Copyright : Copyright (C) 2017 John MacFarlane
22+ License : GNU GPL, version 2 or above
23+
24+ Maintainer : John MacFarlane <jgm@berkeley.edu>
25+ Stability : alpha
26+ Portability : portable
27+
28+Types for LaTeX tokens and macros.
29+-}
30+module Text.Pandoc.Readers.LaTeX.Types ( Tok(..)
31+ , TokType(..)
32+ , Macro(..)
33+ , Line
34+ , Column )
35+where
36+import Data.Text (Text)
37+import Text.Parsec.Pos (Line, Column)
38+
39+data TokType = CtrlSeq Text | Spaces | Newline | Symbol | Word | Comment |
40+ Esc1 | Esc2 | Arg Int
41+ deriving (Eq, Ord, Show)
42+
43+data Tok = Tok (Line, Column) TokType Text
44+ deriving (Eq, Ord, Show)
45+
46+data Macro = Macro Int (Maybe [Tok]) [Tok]
47+ deriving Show
48+
src/Text/Pandoc/Readers/Markdown.hs+69−39
@@ -52,15 +52,17 @@ import System.FilePath (addExtension, takeExtension)
5252import Text.HTML.TagSoup
5353import Text.Pandoc.Builder (Blocks, Inlines)
5454import qualified Text.Pandoc.Builder as B
55−import Text.Pandoc.Class (PandocMonad, report)
55+import Text.Pandoc.Class (PandocMonad(..), report)
5656import Text.Pandoc.Definition
5757import Text.Pandoc.Emoji (emojis)
58+import Text.Pandoc.Error
5859import Text.Pandoc.Logging
5960import Text.Pandoc.Options
6061import Text.Pandoc.Parsing hiding (tableWith)
6162import Text.Pandoc.Readers.HTML (htmlInBalanced, htmlTag, isBlockTag,
6263 isCommentTag, isInlineTag, isTextTag)
63−import Text.Pandoc.Readers.LaTeX (rawLaTeXBlock, rawLaTeXInline)
64+import Text.Pandoc.Readers.LaTeX (rawLaTeXBlock, rawLaTeXInline, applyMacros,
65+ macro)
6466import Text.Pandoc.Shared
6567import qualified Text.Pandoc.UTF8 as UTF8
6668import Text.Pandoc.XML (fromEntities)
@@ -74,7 +76,7 @@ readMarkdown :: PandocMonad m
7476 -> m Pandoc
7577readMarkdown opts s = do
7678 parsed <- (readWithM parseMarkdown) def{ stateOptions = opts }
77− (T.unpack s ++ "\n\n")
79+ (T.unpack (crFilter s) ++ "\n\n")
7880 case parsed of
7981 Right result -> return result
8082 Left e -> throwError e
@@ -291,18 +293,22 @@ ignorable t = (T.pack "_") `T.isSuffixOf` t
291293
292294toMetaValue :: PandocMonad m
293295 => Text -> MarkdownParser m (F MetaValue)
294−toMetaValue x = toMeta <$> parseFromString' parseBlocks (T.unpack x)
295− where
296− toMeta p = do
297− p' <- p
298− return $
299− case B.toList p' of
300− [Plain xs] -> MetaInlines xs
301− [Para xs]
302− | endsWithNewline x -> MetaBlocks [Para xs]
303− | otherwise -> MetaInlines xs
304− bs -> MetaBlocks bs
305− endsWithNewline t = T.pack "\n" `T.isSuffixOf` t
296+toMetaValue x =
297+ parseFromString' parser' (T.unpack x)
298+ where parser' = (asInlines <$> ((trimInlinesF . mconcat)
299+ <$> (guard (not endsWithNewline)
300+ *> manyTill inline eof)))
301+ <|> (asBlocks <$> parseBlocks)
302+ asBlocks p = do
303+ p' <- p
304+ return $ MetaBlocks (B.toList p')
305+ asInlines p = do
306+ p' <- p
307+ return $ MetaInlines (B.toList p')
308+ endsWithNewline = T.pack "\n" `T.isSuffixOf` x
309+ -- Note: a standard quoted or unquoted YAML value will
310+ -- not end in a newline, but a "block" set off with
311+ -- `|` or `>` will.
306312
307313yamlToMeta :: PandocMonad m
308314 => Yaml.Value -> MarkdownParser m (F MetaValue)
@@ -368,13 +374,14 @@ parseMarkdown = do
368374 -- lookup to get sourcepos
369375 case M.lookup n (stateNotes' st) of
370376 Just (pos, _) -> report (NoteDefinedButNotUsed n pos)
371− Nothing -> error "The impossible happened.") notesDefined
377+ Nothing -> throwError $
378+ PandocShouldNeverHappenError "note not found")
379+ notesDefined
372380 let doc = runF (do Pandoc _ bs <- B.doc <$> blocks
373381 meta <- stateMeta' st
374382 return $ Pandoc meta bs) st
375383 reportLogMessages
376− (do guardEnabled Ext_east_asian_line_breaks
377− return $ eastAsianLineBreakFilter doc) <|> return doc
384+ return doc
378385
379386referenceKey :: PandocMonad m => MarkdownParser m (F Blocks)
380387referenceKey = try $ do
@@ -488,7 +495,6 @@ parseBlocks = mconcat <$> manyTill block eof
488495
489496block :: PandocMonad m => MarkdownParser m (F Blocks)
490497block = do
491− pos <- getPosition
492498 res <- choice [ mempty <$ blanklines
493499 , codeBlockFenced
494500 , yamlMetaBlock
@@ -514,8 +520,7 @@ block = do
514520 , para
515521 , plain
516522 ] <?> "block"
517− report $ ParsingTrace
518− (take 60 $ show $ B.toList $ runF res defaultParserState) pos
523+ trace (take 60 $ show $ B.toList $ runF res defaultParserState)
519524 return res
520525
521526--
@@ -680,19 +685,36 @@ specialAttr = do
680685 char '-'
681686 return $ \(id',cs,kvs) -> (id',cs ++ ["unnumbered"],kvs)
682687
688+rawAttribute :: PandocMonad m => MarkdownParser m String
689+rawAttribute = do
690+ char '{'
691+ skipMany spaceChar
692+ char '='
693+ format <- many1 $ satisfy (\c -> isAlphaNum c || c `elem` "-_")
694+ skipMany spaceChar
695+ char '}'
696+ return format
697+
683698codeBlockFenced :: PandocMonad m => MarkdownParser m (F Blocks)
684699codeBlockFenced = try $ do
685700 c <- try (guardEnabled Ext_fenced_code_blocks >> lookAhead (char '~'))
686701 <|> (guardEnabled Ext_backtick_code_blocks >> lookAhead (char '`'))
687702 size <- blockDelimiter (== c) Nothing
688703 skipMany spaceChar
689− attr <- option ([],[],[]) $
690− try (guardEnabled Ext_fenced_code_attributes >> attributes)
691− <|> ((\x -> ("",[toLanguageId x],[])) <$> many1 nonspaceChar)
704+ rawattr <-
705+ (Left <$> try (guardEnabled Ext_raw_attribute >> rawAttribute))
706+ <|>
707+ (Right <$> option ("",[],[])
708+ (try (guardEnabled Ext_fenced_code_attributes >> attributes)
709+ <|> ((\x -> ("",[toLanguageId x],[])) <$> many1 nonspaceChar)))
692710 blankline
693− contents <- manyTill anyLine (blockDelimiter (== c) (Just size))
711+ contents <- intercalate "\n" <$>
712+ manyTill anyLine (blockDelimiter (== c) (Just size))
694713 blanklines
695− return $ return $ B.codeBlockWith attr $ intercalate "\n" contents
714+ return $ return $
715+ case rawattr of
716+ Left syn -> B.rawBlock syn contents
717+ Right attr -> B.codeBlockWith attr contents
696718
697719-- correctly handle github language identifiers
698720toLanguageId :: String -> String
@@ -1013,7 +1035,8 @@ para = try $ do
10131035 result' <- result
10141036 case B.toList result' of
10151037 [Image attr alt (src,tit)]
1016− | Ext_implicit_figures `extensionEnabled` exts ->
1038+ | not (null alt) &&
1039+ Ext_implicit_figures `extensionEnabled` exts ->
10171040 -- the fig: at beginning of title indicates a figure
10181041 return $ B.para $ B.singleton
10191042 $ Image attr alt (src,'f':'i':'g':':':tit)
@@ -1083,10 +1106,11 @@ latexMacro = try $ do
10831106rawTeXBlock :: PandocMonad m => MarkdownParser m (F Blocks)
10841107rawTeXBlock = do
10851108 guardEnabled Ext_raw_tex
1086− result <- (B.rawBlock "latex" . concat <$>
1087− rawLaTeXBlock `sepEndBy1` blankline)
1088− <|> (B.rawBlock "context" . concat <$>
1109+ result <- (B.rawBlock "context" . concat <$>
10891110 rawConTeXtEnvironment `sepEndBy1` blankline)
1111+ <|> (B.rawBlock "latex" . concat <$>
1112+ rawLaTeXBlock `sepEndBy1` blankline)
1113+
10901114 spaces
10911115 return $ return result
10921116
@@ -1515,17 +1539,24 @@ code :: PandocMonad m => MarkdownParser m (F Inlines)
15151539code = try $ do
15161540 starts <- many1 (char '`')
15171541 skipSpaces
1518− result <- many1Till (many1 (noneOf "`\n") <|> many1 (char '`') <|>
1542+ result <- (trim . concat) <$>
1543+ many1Till (many1 (noneOf "`\n") <|> many1 (char '`') <|>
15191544 (char '\n' >> notFollowedBy' blankline >> return " "))
15201545 (try (skipSpaces >> count (length starts) (char '`') >>
15211546 notFollowedBy (char '`')))
1522− attr <- option ([],[],[]) (try $ guardEnabled Ext_inline_code_attributes
1523− >> attributes)
1524− return $ return $ B.codeWith attr $ trim $ concat result
1547+ rawattr <-
1548+ (Left <$> try (guardEnabled Ext_raw_attribute >> rawAttribute))
1549+ <|>
1550+ (Right <$> option ("",[],[])
1551+ (try (guardEnabled Ext_inline_code_attributes >> attributes)))
1552+ return $ return $
1553+ case rawattr of
1554+ Left syn -> B.rawInline syn result
1555+ Right attr -> B.codeWith attr result
15251556
15261557math :: PandocMonad m => MarkdownParser m (F Inlines)
1527−math = (return . B.displayMath <$> (mathDisplay >>= applyMacros'))
1528− <|> (return . B.math <$> (mathInline >>= applyMacros')) <+?>
1558+math = (return . B.displayMath <$> (mathDisplay >>= applyMacros))
1559+ <|> (return . B.math <$> (mathInline >>= applyMacros)) <+?>
15291560 (guardEnabled Ext_smart *> (return <$> apostrophe)
15301561 <* notFollowedBy (space <|> satisfy isPunctuation))
15311562
@@ -1849,9 +1880,8 @@ rawLaTeXInline' = try $ do
18491880 guardEnabled Ext_raw_tex
18501881 lookAhead (char '\\')
18511882 notFollowedBy' rawConTeXtEnvironment
1852− RawInline _ s <- rawLaTeXInline
1853− return $ return $ B.rawInline "tex" s
1854− -- "tex" because it might be context or latex
1883+ s <- rawLaTeXInline
1884+ return $ return $ B.rawInline "tex" s -- "tex" because it might be context
18551885
18561886rawConTeXtEnvironment :: PandocMonad m => ParserT [Char] st m String
18571887rawConTeXtEnvironment = try $ do
src/Text/Pandoc/Readers/MediaWiki.hs+5−5
@@ -52,13 +52,14 @@ import qualified Data.Set as Set
5252import Text.HTML.TagSoup
5353import Text.Pandoc.Builder (Blocks, Inlines, trimInlines)
5454import qualified Text.Pandoc.Builder as B
55−import Text.Pandoc.Class (PandocMonad, report)
55+import Text.Pandoc.Class (PandocMonad(..))
5656import Text.Pandoc.Definition
5757import Text.Pandoc.Logging
5858import Text.Pandoc.Options
5959import Text.Pandoc.Parsing hiding (nested)
6060import Text.Pandoc.Readers.HTML (htmlTag, isBlockTag, isCommentTag)
61−import Text.Pandoc.Shared (safeRead, stringify, stripTrailingNewlines, trim)
61+import Text.Pandoc.Shared (safeRead, stringify, stripTrailingNewlines, trim,
62+ crFilter)
6263import Text.Pandoc.Walk (walk)
6364import Text.Pandoc.XML (fromEntities)
6465
@@ -77,7 +78,7 @@ readMediaWiki opts s = do
7778 , mwLogMessages = []
7879 , mwInTT = False
7980 }
80− (unpack s ++ "\n")
81+ (unpack (crFilter s) ++ "\n")
8182 case parsed of
8283 Right result -> return result
8384 Left e -> throwError e
@@ -205,7 +206,6 @@ parseMediaWiki = do
205206
206207block :: PandocMonad m => MWParser m Blocks
207208block = do
208− pos <- getPosition
209209 res <- mempty <$ skipMany1 blankline
210210 <|> table
211211 <|> header
@@ -218,7 +218,7 @@ block = do
218218 <|> blockTag
219219 <|> (B.rawBlock "mediawiki" <$> template)
220220 <|> para
221− report $ ParsingTrace (take 60 $ show $ B.toList res) pos
221+ trace (take 60 $ show $ B.toList res)
222222 return res
223223
224224para :: PandocMonad m => MWParser m Blocks
src/Text/Pandoc/Readers/Muse.hsnew file+607
@@ -0,0 +1,607 @@
1+{-
2+ Copyright (C) 2017 Alexander Krotov <ilabdsf@gmail.com>
3+
4+This program is free software; you can redistribute it and/or modify
5+it under the terms of the GNU General Public License as published by
6+the Free Software Foundation; either version 2 of the License, or
7+(at your option) any later version.
8+
9+This program is distributed in the hope that it will be useful,
10+but WITHOUT ANY WARRANTY; without even the implied warranty of
11+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+GNU General Public License for more details.
13+
14+You should have received a copy of the GNU General Public License
15+along with this program; if not, write to the Free Software
16+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17+-}
18+
19+{- |
20+ Module : Text.Pandoc.Readers.Muse
21+ Copyright : Copyright (C) 2017 Alexander Krotov
22+ License : GNU GPL, version 2 or above
23+
24+ Maintainer : Alexander Krotov <ilabdsf@gmail.com>
25+ Stability : alpha
26+ Portability : portable
27+
28+Conversion of Muse text to 'Pandoc' document.
29+-}
30+{-
31+TODO:
32+- {{{ }}} syntax for <example>
33+- Page breaks (five "*")
34+- Headings with anchors (make it round trip with Muse writer)
35+- <verse> and ">"
36+- Definition lists
37+- Org tables
38+- table.el tables
39+- Images with attributes (floating and width)
40+- Anchors
41+- Citations and <biblio>
42+- <play> environment
43+- <verbatim> tag
44+-}
45+module Text.Pandoc.Readers.Muse (readMuse) where
46+
47+import Control.Monad
48+import Control.Monad.Except (throwError)
49+import qualified Data.Map as M
50+import Data.Text (Text, unpack)
51+import Data.List (stripPrefix)
52+import Data.Maybe (fromMaybe)
53+import Text.HTML.TagSoup
54+import Text.Pandoc.Builder (Blocks, Inlines)
55+import qualified Text.Pandoc.Builder as B
56+import Text.Pandoc.Class (PandocMonad(..))
57+import Text.Pandoc.Definition
58+import Text.Pandoc.Logging
59+import Text.Pandoc.Options
60+import Text.Pandoc.Shared (crFilter)
61+import Text.Pandoc.Parsing hiding (nested)
62+import Text.Pandoc.Readers.HTML (htmlTag)
63+import Text.Pandoc.XML (fromEntities)
64+import System.FilePath (takeExtension)
65+
66+-- | Read Muse from an input string and return a Pandoc document.
67+readMuse :: PandocMonad m
68+ => ReaderOptions
69+ -> Text
70+ -> m Pandoc
71+readMuse opts s = do
72+ res <- readWithM parseMuse def{ stateOptions = opts } (unpack (crFilter s))
73+ case res of
74+ Left e -> throwError e
75+ Right d -> return d
76+
77+type MuseParser = ParserT String ParserState
78+
79+--
80+-- main parser
81+--
82+
83+parseMuse :: PandocMonad m => MuseParser m Pandoc
84+parseMuse = do
85+ many directive
86+ blocks <- parseBlocks
87+ st <- getState
88+ let doc = runF (do Pandoc _ bs <- B.doc <$> blocks
89+ meta <- stateMeta' st
90+ return $ Pandoc meta bs) st
91+ reportLogMessages
92+ return doc
93+
94+parseBlocks :: PandocMonad m => MuseParser m (F Blocks)
95+parseBlocks = do
96+ res <- mconcat <$> many block
97+ spaces
98+ eof
99+ return res
100+
101+--
102+-- utility functions
103+--
104+
105+nested :: PandocMonad m => MuseParser m a -> MuseParser m a
106+nested p = do
107+ nestlevel <- stateMaxNestingLevel <$> getState
108+ guard $ nestlevel > 0
109+ updateState $ \st -> st{ stateMaxNestingLevel = stateMaxNestingLevel st - 1 }
110+ res <- p
111+ updateState $ \st -> st{ stateMaxNestingLevel = nestlevel }
112+ return res
113+
114+htmlElement :: PandocMonad m => String -> MuseParser m (Attr, String)
115+htmlElement tag = try $ do
116+ (TagOpen _ attr, _) <- htmlTag (~== TagOpen tag [])
117+ content <- manyTill anyChar (endtag <|> endofinput)
118+ return (htmlAttrToPandoc attr, trim content)
119+ where
120+ endtag = void $ htmlTag (~== TagClose tag)
121+ endofinput = lookAhead $ try $ skipMany blankline >> skipSpaces >> eof
122+ trim = dropWhile (=='\n') . reverse . dropWhile (=='\n') . reverse
123+
124+htmlAttrToPandoc :: [Attribute String] -> Attr
125+htmlAttrToPandoc attrs = (ident, classes, keyvals)
126+ where
127+ ident = fromMaybe "" $ lookup "id" attrs
128+ classes = maybe [] words $ lookup "class" attrs
129+ keyvals = [(k,v) | (k,v) <- attrs, k /= "id" && k /= "class"]
130+
131+parseHtmlContentWithAttrs :: PandocMonad m
132+ => String -> MuseParser m a -> MuseParser m (Attr, [a])
133+parseHtmlContentWithAttrs tag parser = do
134+ (attr, content) <- htmlElement tag
135+ parsedContent <- try $ parseContent content
136+ return (attr, parsedContent)
137+ where
138+ parseContent = parseFromString $ nested $ manyTill parser endOfContent
139+ endOfContent = try $ skipMany blankline >> skipSpaces >> eof
140+
141+parseHtmlContent :: PandocMonad m => String -> MuseParser m a -> MuseParser m [a]
142+parseHtmlContent tag p = liftM snd (parseHtmlContentWithAttrs tag p)
143+
144+--
145+-- directive parsers
146+--
147+
148+parseDirective :: PandocMonad m => MuseParser m (String, F Inlines)
149+parseDirective = do
150+ char '#'
151+ key <- many letter
152+ space
153+ spaces
154+ raw <- many $ noneOf "\n"
155+ newline
156+ value <- parseFromString (trimInlinesF . mconcat <$> many inline) raw
157+ return (key, value)
158+
159+directive :: PandocMonad m => MuseParser m ()
160+directive = do
161+ (key, value) <- parseDirective
162+ updateState $ \st -> st { stateMeta' = B.setMeta key <$> value <*> stateMeta' st }
163+
164+--
165+-- block parsers
166+--
167+
168+block :: PandocMonad m => MuseParser m (F Blocks)
169+block = do
170+ res <- mempty <$ skipMany1 blankline
171+ <|> blockElements
172+ <|> para
173+ skipMany blankline
174+ trace (take 60 $ show $ B.toList $ runF res defaultParserState)
175+ return res
176+
177+blockElements :: PandocMonad m => MuseParser m (F Blocks)
178+blockElements = choice [ comment
179+ , separator
180+ , header
181+ , exampleTag
182+ , literal
183+ , centerTag
184+ , rightTag
185+ , quoteTag
186+ , bulletList
187+ , orderedList
188+ , table
189+ , commentTag
190+ , indentedBlock
191+ , noteBlock
192+ ]
193+
194+comment :: PandocMonad m => MuseParser m (F Blocks)
195+comment = try $ do
196+ char ';'
197+ space
198+ many $ noneOf "\n"
199+ void newline <|> eof
200+ return mempty
201+
202+separator :: PandocMonad m => MuseParser m (F Blocks)
203+separator = try $ do
204+ string "----"
205+ many $ char '-'
206+ many spaceChar
207+ void newline <|> eof
208+ return $ return B.horizontalRule
209+
210+header :: PandocMonad m => MuseParser m (F Blocks)
211+header = try $ do
212+ st <- stateParserContext <$> getState
213+ q <- stateQuoteContext <$> getState
214+ getPosition >>= \pos -> guard (st == NullState && q == NoQuote && sourceColumn pos == 1)
215+ level <- liftM length $ many1 $ char '*'
216+ guard $ level <= 5
217+ skipSpaces
218+ content <- trimInlinesF . mconcat <$> manyTill inline newline
219+ attr <- registerHeader ("", [], []) (runF content defaultParserState)
220+ return $ B.headerWith attr level <$> content
221+
222+exampleTag :: PandocMonad m => MuseParser m (F Blocks)
223+exampleTag = liftM (return . uncurry B.codeBlockWith) $ htmlElement "example"
224+
225+literal :: PandocMonad m => MuseParser m (F Blocks)
226+literal = liftM (return . rawBlock) $ htmlElement "literal"
227+ where
228+ format (_, _, kvs) = fromMaybe "html" $ lookup "format" kvs
229+ rawBlock (attrs, content) = B.rawBlock (format attrs) content
230+
231+blockTag :: PandocMonad m
232+ => (Blocks -> Blocks)
233+ -> String
234+ -> MuseParser m (F Blocks)
235+blockTag f s = do
236+ res <- parseHtmlContent s block
237+ return $ f <$> mconcat res
238+
239+-- <center> tag is ignored
240+centerTag :: PandocMonad m => MuseParser m (F Blocks)
241+centerTag = blockTag id "center"
242+
243+-- <right> tag is ignored
244+rightTag :: PandocMonad m => MuseParser m (F Blocks)
245+rightTag = blockTag id "right"
246+
247+quoteTag :: PandocMonad m => MuseParser m (F Blocks)
248+quoteTag = blockTag B.blockQuote "quote"
249+
250+commentTag :: PandocMonad m => MuseParser m (F Blocks)
251+commentTag = parseHtmlContent "comment" block >> return mempty
252+
253+-- Indented block is either center, right or quote
254+indentedLine :: PandocMonad m => MuseParser m (Int, String)
255+indentedLine = try $ do
256+ indent <- length <$> many1 spaceChar
257+ line <- anyLine
258+ return (indent, line)
259+
260+rawIndentedBlock :: PandocMonad m => MuseParser m (Int, String)
261+rawIndentedBlock = try $ do
262+ lns <- many1 indentedLine
263+ let indent = minimum $ map fst lns
264+ return (indent, unlines $ map snd lns)
265+
266+indentedBlock :: PandocMonad m => MuseParser m (F Blocks)
267+indentedBlock = try $ do
268+ (indent, raw) <- rawIndentedBlock
269+ contents <- withQuoteContext InDoubleQuote $ parseFromString parseBlocks raw
270+ return $ (if indent >= 2 && indent < 6 then B.blockQuote else id) <$> contents
271+
272+para :: PandocMonad m => MuseParser m (F Blocks)
273+para = liftM B.para . trimInlinesF . mconcat <$> many1Till inline endOfParaElement
274+ where
275+ endOfParaElement = lookAhead $ endOfInput <|> endOfPara <|> newBlockElement
276+ endOfInput = try $ skipMany blankline >> skipSpaces >> eof
277+ endOfPara = try $ blankline >> skipMany1 blankline
278+ newBlockElement = try $ blankline >> void blockElements
279+
280+noteMarker :: PandocMonad m => MuseParser m String
281+noteMarker = try $ do
282+ char '['
283+ many1Till digit $ char ']'
284+
285+noteBlock :: PandocMonad m => MuseParser m (F Blocks)
286+noteBlock = try $ do
287+ pos <- getPosition
288+ ref <- noteMarker <* skipSpaces
289+ content <- mconcat <$> blocksTillNote
290+ oldnotes <- stateNotes' <$> getState
291+ case M.lookup ref oldnotes of
292+ Just _ -> logMessage $ DuplicateNoteReference ref pos
293+ Nothing -> return ()
294+ updateState $ \s -> s{ stateNotes' = M.insert ref (pos, content) oldnotes }
295+ return mempty
296+ where
297+ blocksTillNote =
298+ many1Till block (eof <|> () <$ lookAhead noteMarker)
299+
300+--
301+-- lists
302+--
303+
304+listLine :: PandocMonad m => Int -> MuseParser m String
305+listLine markerLength = try $ do
306+ notFollowedBy blankline
307+ indentWith markerLength
308+ anyLineNewline
309+
310+withListContext :: PandocMonad m => MuseParser m a -> MuseParser m a
311+withListContext p = do
312+ state <- getState
313+ let oldContext = stateParserContext state
314+ setState $ state { stateParserContext = ListItemState }
315+ parsed <- p
316+ updateState (\st -> st {stateParserContext = oldContext})
317+ return parsed
318+
319+listContinuation :: PandocMonad m => Int -> MuseParser m String
320+listContinuation markerLength = try $ do
321+ blanks <- many1 blankline
322+ result <- many1 $ listLine markerLength
323+ return $ blanks ++ concat result
324+
325+listStart :: PandocMonad m => MuseParser m Int -> MuseParser m Int
326+listStart marker = try $ do
327+ preWhitespace <- length <$> many spaceChar
328+ st <- stateParserContext <$> getState
329+ getPosition >>= \pos -> guard (st == ListItemState || sourceColumn pos /= 1)
330+ markerLength <- marker
331+ postWhitespace <- length <$> many1 spaceChar
332+ return $ preWhitespace + markerLength + postWhitespace
333+
334+listItem :: PandocMonad m => MuseParser m Int -> MuseParser m (F Blocks)
335+listItem start = try $ do
336+ markerLength <- start
337+ firstLine <- anyLineNewline
338+ blank <- option "" ("\n" <$ blankline)
339+ restLines <- many $ listLine markerLength
340+ let first = firstLine ++ blank ++ concat restLines
341+ rest <- many $ listContinuation markerLength
342+ parseFromString (withListContext parseBlocks) $ concat (first:rest) ++ "\n"
343+
344+bulletListItems :: PandocMonad m => MuseParser m (F [Blocks])
345+bulletListItems = sequence <$> many1 (listItem bulletListStart)
346+
347+bulletListStart :: PandocMonad m => MuseParser m Int
348+bulletListStart = listStart (char '-' >> return 1)
349+
350+bulletList :: PandocMonad m => MuseParser m (F Blocks)
351+bulletList = do
352+ listItems <- bulletListItems
353+ return $ B.bulletList <$> listItems
354+
355+orderedListStart :: PandocMonad m
356+ => ListNumberStyle
357+ -> ListNumberDelim
358+ -> MuseParser m Int
359+orderedListStart style delim = listStart (snd <$> withHorizDisplacement (orderedListMarker style delim))
360+
361+orderedList :: PandocMonad m => MuseParser m (F Blocks)
362+orderedList = try $ do
363+ p@(_, style, delim) <- lookAhead (many spaceChar *> anyOrderedListMarker <* spaceChar)
364+ guard $ style `elem` [Decimal, LowerAlpha, UpperAlpha, LowerRoman, UpperRoman]
365+ guard $ delim == Period
366+ items <- sequence <$> many1 (listItem $ orderedListStart style delim)
367+ return $ B.orderedListWith p <$> items
368+
369+--
370+-- tables
371+--
372+
373+data MuseTable = MuseTable
374+ { museTableCaption :: Inlines
375+ , museTableHeaders :: [[Blocks]]
376+ , museTableRows :: [[Blocks]]
377+ , museTableFooters :: [[Blocks]]
378+ }
379+
380+data MuseTableElement = MuseHeaderRow (F [Blocks])
381+ | MuseBodyRow (F [Blocks])
382+ | MuseFooterRow (F [Blocks])
383+ | MuseCaption (F Inlines)
384+
385+museToPandocTable :: MuseTable -> Blocks
386+museToPandocTable (MuseTable caption headers body footers) =
387+ B.table caption attrs headRow rows
388+ where ncol = maximum (0 : map length (headers ++ body ++ footers))
389+ attrs = replicate ncol (AlignDefault, 0.0)
390+ headRow = if null headers then [] else head headers
391+ rows = (if null headers then [] else tail headers) ++ body ++ footers
392+
393+museAppendElement :: MuseTable
394+ -> MuseTableElement
395+ -> F MuseTable
396+museAppendElement tbl element =
397+ case element of
398+ MuseHeaderRow row -> do
399+ row' <- row
400+ return tbl{ museTableHeaders = museTableHeaders tbl ++ [row'] }
401+ MuseBodyRow row -> do
402+ row' <- row
403+ return tbl{ museTableRows = museTableRows tbl ++ [row'] }
404+ MuseFooterRow row-> do
405+ row' <- row
406+ return tbl{ museTableFooters = museTableFooters tbl ++ [row'] }
407+ MuseCaption inlines -> do
408+ inlines' <- inlines
409+ return tbl{ museTableCaption = inlines' }
410+
411+tableCell :: PandocMonad m => MuseParser m (F Blocks)
412+tableCell = try $ do
413+ content <- trimInlinesF . mconcat <$> manyTill inline (lookAhead cellEnd)
414+ return $ B.plain <$> content
415+ where cellEnd = try $ void (many1 spaceChar >> char '|') <|> void newline <|> eof
416+
417+tableElements :: PandocMonad m => MuseParser m [MuseTableElement]
418+tableElements = tableParseElement `sepEndBy1` (void newline <|> eof)
419+
420+elementsToTable :: [MuseTableElement] -> F MuseTable
421+elementsToTable = foldM museAppendElement emptyTable
422+ where emptyTable = MuseTable mempty mempty mempty mempty
423+
424+table :: PandocMonad m => MuseParser m (F Blocks)
425+table = try $ do
426+ rows <- tableElements
427+ let tbl = elementsToTable rows
428+ let pandocTbl = museToPandocTable <$> tbl :: F Blocks
429+ return pandocTbl
430+
431+tableParseElement :: PandocMonad m => MuseParser m MuseTableElement
432+tableParseElement = tableParseHeader
433+ <|> tableParseBody
434+ <|> tableParseFooter
435+ <|> tableParseCaption
436+
437+tableParseRow :: PandocMonad m => Int -> MuseParser m (F [Blocks])
438+tableParseRow n = try $ do
439+ fields <- tableCell `sepBy2` fieldSep
440+ return $ sequence fields
441+ where p `sepBy2` sep = (:) <$> p <*> many1 (sep >> p)
442+ fieldSep = many1 spaceChar >> count n (char '|') >> (void (many1 spaceChar) <|> void (lookAhead newline))
443+
444+tableParseHeader :: PandocMonad m => MuseParser m MuseTableElement
445+tableParseHeader = MuseHeaderRow <$> tableParseRow 2
446+
447+tableParseBody :: PandocMonad m => MuseParser m MuseTableElement
448+tableParseBody = MuseBodyRow <$> tableParseRow 1
449+
450+tableParseFooter :: PandocMonad m => MuseParser m MuseTableElement
451+tableParseFooter = MuseFooterRow <$> tableParseRow 3
452+
453+tableParseCaption :: PandocMonad m => MuseParser m MuseTableElement
454+tableParseCaption = try $ do
455+ many spaceChar
456+ string "|+"
457+ contents <- trimInlinesF . mconcat <$> many1Till inline (lookAhead $ string "+|")
458+ string "+|"
459+ return $ MuseCaption contents
460+
461+--
462+-- inline parsers
463+--
464+
465+inline :: PandocMonad m => MuseParser m (F Inlines)
466+inline = choice [ br
467+ , footnote
468+ , strong
469+ , strongTag
470+ , emph
471+ , emphTag
472+ , superscriptTag
473+ , subscriptTag
474+ , strikeoutTag
475+ , link
476+ , code
477+ , codeTag
478+ , whitespace
479+ , str
480+ , symbol
481+ ] <?> "inline"
482+
483+footnote :: PandocMonad m => MuseParser m (F Inlines)
484+footnote = try $ do
485+ ref <- noteMarker
486+ return $ do
487+ notes <- asksF stateNotes'
488+ case M.lookup ref notes of
489+ Nothing -> return $ B.str $ "[" ++ ref ++ "]"
490+ Just (_pos, contents) -> do
491+ st <- askF
492+ let contents' = runF contents st { stateNotes' = M.empty }
493+ return $ B.note contents'
494+
495+whitespace :: PandocMonad m => MuseParser m (F Inlines)
496+whitespace = liftM return (lb <|> regsp)
497+ where lb = try $ skipMany spaceChar >> linebreak >> return B.space
498+ regsp = try $ skipMany1 spaceChar >> return B.space
499+
500+br :: PandocMonad m => MuseParser m (F Inlines)
501+br = try $ do
502+ string "<br>"
503+ return $ return B.linebreak
504+
505+linebreak :: PandocMonad m => MuseParser m (F Inlines)
506+linebreak = newline >> notFollowedBy newline >> (lastNewline <|> innerNewline)
507+ where lastNewline = do
508+ eof
509+ return $ return mempty
510+ innerNewline = return $ return B.space
511+
512+emphasisBetween :: (PandocMonad m, Show a) => MuseParser m a -> MuseParser m (F Inlines)
513+emphasisBetween c = try $ enclosedInlines c c
514+
515+enclosedInlines :: (PandocMonad m, Show a, Show b)
516+ => MuseParser m a
517+ -> MuseParser m b
518+ -> MuseParser m (F Inlines)
519+enclosedInlines start end = try $
520+ trimInlinesF . mconcat <$> enclosed start end inline
521+
522+verbatimBetween :: PandocMonad m
523+ => Char
524+ -> MuseParser m String
525+verbatimBetween c = try $ do
526+ char c
527+ many1Till anyChar $ char c
528+
529+inlineTag :: PandocMonad m
530+ => (Inlines -> Inlines)
531+ -> String
532+ -> MuseParser m (F Inlines)
533+inlineTag f s = do
534+ res <- parseHtmlContent s inline
535+ return $ f <$> mconcat res
536+
537+strongTag :: PandocMonad m => MuseParser m (F Inlines)
538+strongTag = inlineTag B.strong "strong"
539+
540+strong :: PandocMonad m => MuseParser m (F Inlines)
541+strong = fmap B.strong <$> emphasisBetween (string "**")
542+
543+emph :: PandocMonad m => MuseParser m (F Inlines)
544+emph = fmap B.emph <$> emphasisBetween (char '*')
545+
546+emphTag :: PandocMonad m => MuseParser m (F Inlines)
547+emphTag = inlineTag B.emph "em"
548+
549+superscriptTag :: PandocMonad m => MuseParser m (F Inlines)
550+superscriptTag = inlineTag B.superscript "sup"
551+
552+subscriptTag :: PandocMonad m => MuseParser m (F Inlines)
553+subscriptTag = inlineTag B.subscript "sub"
554+
555+strikeoutTag :: PandocMonad m => MuseParser m (F Inlines)
556+strikeoutTag = inlineTag B.strikeout "del"
557+
558+code :: PandocMonad m => MuseParser m (F Inlines)
559+code = try $ do
560+ pos <- getPosition
561+ sp <- if sourceColumn pos == 1
562+ then pure mempty
563+ else skipMany1 spaceChar >> pure B.space
564+ cd <- verbatimBetween '='
565+ notFollowedBy nonspaceChar
566+ return $ return (sp B.<> B.code cd)
567+
568+codeTag :: PandocMonad m => MuseParser m (F Inlines)
569+codeTag = do
570+ (attrs, content) <- parseHtmlContentWithAttrs "code" anyChar
571+ return $ return $ B.codeWith attrs $ fromEntities content
572+
573+str :: PandocMonad m => MuseParser m (F Inlines)
574+str = liftM (return . B.str) (many1 alphaNum <|> count 1 characterReference)
575+
576+symbol :: PandocMonad m => MuseParser m (F Inlines)
577+symbol = liftM (return . B.str) $ count 1 nonspaceChar
578+
579+link :: PandocMonad m => MuseParser m (F Inlines)
580+link = try $ do
581+ st <- getState
582+ guard $ stateAllowLinks st
583+ setState $ st{ stateAllowLinks = False }
584+ (url, title, content) <- linkText
585+ setState $ st{ stateAllowLinks = True }
586+ return $ case stripPrefix "URL:" url of
587+ Nothing -> if isImageUrl url
588+ then B.image url title <$> fromMaybe (return mempty) content
589+ else B.link url title <$> fromMaybe (return $ B.str url) content
590+ Just url' -> B.link url' title <$> fromMaybe (return $ B.str url') content
591+ where -- Taken from muse-image-regexp defined in Emacs Muse file lisp/muse-regexps.el
592+ imageExtensions = [".eps", ".gif", ".jpg", ".jpeg", ".pbm", ".png", ".tiff", ".xbm", ".xpm"]
593+ isImageUrl = (`elem` imageExtensions) . takeExtension
594+
595+linkContent :: PandocMonad m => MuseParser m (F Inlines)
596+linkContent = do
597+ char '['
598+ res <- many1Till anyChar $ char ']'
599+ parseFromString (mconcat <$> many1 inline) res
600+
601+linkText :: PandocMonad m => MuseParser m (String, String, Maybe (F Inlines))
602+linkText = do
603+ string "[["
604+ url <- many1Till anyChar $ char ']'
605+ content <- optionMaybe linkContent
606+ char ']'
607+ return (url, "", content)
src/Text/Pandoc/Readers/OPML.hs+4−2
@@ -1,6 +1,6 @@
11{-# LANGUAGE FlexibleContexts #-}
22module Text.Pandoc.Readers.OPML ( readOPML ) where
3−import Control.Monad.State
3+import Control.Monad.State.Strict
44import Data.Char (toUpper)
55import Data.Text (Text, unpack, pack)
66import Data.Default
@@ -9,6 +9,7 @@ import Text.HTML.TagSoup.Entity (lookupEntity)
99import Text.Pandoc.Builder
1010import Text.Pandoc.Class (PandocMonad)
1111import Text.Pandoc.Options
12+import Text.Pandoc.Shared (crFilter)
1213import Text.Pandoc.Readers.HTML (readHtml)
1314import Text.Pandoc.Readers.Markdown (readMarkdown)
1415import Text.XML.Light
@@ -32,7 +33,8 @@ instance Default OPMLState where
3233readOPML :: PandocMonad m => ReaderOptions -> Text -> m Pandoc
3334readOPML _ inp = do
3435 (bs, st') <- flip runStateT def
35− (mapM parseBlock $ normalizeTree $ parseXML (unpack inp))
36+ (mapM parseBlock $ normalizeTree $
37+ parseXML (unpack (crFilter inp)))
3638 return $
3739 setTitle (opmlDocTitle st') $
3840 setAuthors (opmlDocAuthors st') $
src/Text/Pandoc/Readers/Odt/Generic/Fallible.hs−5
@@ -124,8 +124,3 @@ instance ChoiceVector SuccessList where
124124 spreadChoice = Right . SuccessList . (foldr unTagRight []) . collectNonFailing
125125 where unTagRight (Right x) = (x:)
126126 unTagRight _ = id
127−
128−-- | Like 'catMaybes', but for 'Either'.
129−collectRights :: [Either _l r] -> [r]
130−collectRights = collectNonFailing . untag . spreadChoice . SuccessList
131− where untag = fromLeft (error "Unexpected Left")
src/Text/Pandoc/Readers/Odt/Generic/XMLConverter.hs+2−1
@@ -71,6 +71,7 @@ import Control.Applicative hiding ( liftA, liftA2 )
7171import Control.Monad ( MonadPlus )
7272import Control.Arrow
7373
74+import Data.Either ( rights )
7475import qualified Data.Map as M
7576import Data.Default
7677import Data.Maybe
@@ -604,7 +605,7 @@ tryAll :: (NameSpaceID nsID)
604605 -> XMLConverter nsID extraState b [a]
605606tryAll nsID name a = prepareIteration nsID name
606607 >>> iterateS (switchingTheStack a)
607− >>^ collectRights
608+ >>^ rights
608609
609610--------------------------------------------------------------------------------
610611-- Matching children
src/Text/Pandoc/Readers/Org.hs+2−1
@@ -36,6 +36,7 @@ import Text.Pandoc.Definition
3636import Text.Pandoc.Error
3737import Text.Pandoc.Options
3838import Text.Pandoc.Parsing (reportLogMessages)
39+import Text.Pandoc.Shared (crFilter)
3940
4041import Control.Monad.Except (throwError)
4142import Control.Monad.Reader (runReaderT)
@@ -51,7 +52,7 @@ readOrg :: PandocMonad m
5152readOrg opts s = do
5253 parsed <- flip runReaderT def $
5354 readWithM parseOrg (optionsToParserState opts)
54− (T.unpack s ++ "\n\n")
55+ (T.unpack (crFilter s) ++ "\n\n")
5556 case parsed of
5657 Right result -> return result
5758 Left _ -> throwError $ PandocParseError "problem parsing org"
src/Text/Pandoc/Readers/Org/Inlines.hs+3−2
@@ -826,9 +826,10 @@ maybeRight = either (const Nothing) Just
826826inlineLaTeXCommand :: PandocMonad m => OrgParser m String
827827inlineLaTeXCommand = try $ do
828828 rest <- getInput
829− parsed <- (lift . lift) $ runParserT rawLaTeXInline def "source" rest
829+ st <- getState
830+ parsed <- (lift . lift) $ runParserT rawLaTeXInline st "source" rest
830831 case parsed of
831− Right (RawInline _ cs) -> do
832+ Right cs -> do
832833 -- drop any trailing whitespace, those are not be part of the command as
833834 -- far as org mode is concerned.
834835 let cmdNoSpc = dropWhileEnd isSpace cs
src/Text/Pandoc/Readers/Org/ParserState.hs+10
@@ -33,6 +33,7 @@ module Text.Pandoc.Readers.Org.ParserState
3333 , OrgNoteRecord
3434 , HasReaderOptions (..)
3535 , HasQuoteContext (..)
36+ , HasMacros (..)
3637 , TodoMarker (..)
3738 , TodoSequence
3839 , TodoState (..)
@@ -57,14 +58,17 @@ import Control.Monad.Reader (ReaderT, asks, local)
5758import Data.Default (Default (..))
5859import qualified Data.Map as M
5960import qualified Data.Set as Set
61+import Data.Text (Text)
6062
6163import Text.Pandoc.Builder (Blocks, Inlines)
6264import Text.Pandoc.Definition (Meta (..), nullMeta)
6365import Text.Pandoc.Logging
6466import Text.Pandoc.Options (ReaderOptions (..))
67+import Text.Pandoc.Readers.LaTeX.Types (Macro)
6568import Text.Pandoc.Parsing (Future, HasHeaderMap (..), HasIdentifierList (..),
6669 HasIncludeFiles (..), HasLastStrPosition (..),
6770 HasLogMessages (..), HasQuoteContext (..),
71+ HasMacros (..),
6872 HasReaderOptions (..), ParserContext (..),
6973 QuoteContext (..), SourcePos, askF, asksF, returnF,
7074 runF, trimInlinesF)
@@ -118,6 +122,7 @@ data OrgParserState = OrgParserState
118122 , orgStateParserContext :: ParserContext
119123 , orgStateTodoSequences :: [TodoSequence]
120124 , orgLogMessages :: [LogMessage]
125+ , orgMacros :: M.Map Text Macro
121126 }
122127
123128data OrgParserLocal = OrgParserLocal { orgLocalQuoteContext :: QuoteContext }
@@ -148,6 +153,10 @@ instance HasLogMessages OrgParserState where
148153 addLogMessage msg st = st{ orgLogMessages = msg : orgLogMessages st }
149154 getLogMessages st = reverse $ orgLogMessages st
150155
156+instance HasMacros OrgParserState where
157+ extractMacros st = orgMacros st
158+ updateMacros f st = st{ orgMacros = f (orgMacros st) }
159+
151160instance HasIncludeFiles OrgParserState where
152161 getIncludeFiles = orgStateIncludeFiles
153162 addIncludeFile f st = st { orgStateIncludeFiles = f : orgStateIncludeFiles st }
@@ -178,6 +187,7 @@ defaultOrgParserState = OrgParserState
178187 , orgStateParserContext = NullState
179188 , orgStateTodoSequences = []
180189 , orgLogMessages = []
190+ , orgMacros = M.empty
181191 }
182192
183193optionsToParserState :: ReaderOptions -> OrgParserState
src/Text/Pandoc/Readers/RST.hs+44−9
@@ -31,7 +31,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
3131Conversion from reStructuredText to 'Pandoc' document.
3232-}
3333module Text.Pandoc.Readers.RST ( readRST ) where
34−import Control.Monad (guard, liftM, mzero, when)
34+import Control.Monad (guard, liftM, mzero, when, forM_)
3535import Control.Monad.Identity (Identity(..))
3636import Control.Monad.Except (throwError)
3737import Data.Char (isHexDigit, isSpace, toLower, toUpper)
@@ -68,7 +68,7 @@ readRST :: PandocMonad m
6868 -> m Pandoc
6969readRST opts s = do
7070 parsed <- (readWithM parseRST) def{ stateOptions = opts }
71− (T.unpack s ++ "\n\n")
71+ (T.unpack (crFilter s) ++ "\n\n")
7272 case parsed of
7373 Right result -> return result
7474 Left e -> throwError e
@@ -170,7 +170,8 @@ parseRST = do
170170 -- go through once just to get list of reference keys and notes
171171 -- docMinusKeys is the raw document with blanks where the keys were...
172172 docMinusKeys <- concat <$>
173− manyTill (referenceKey <|> noteBlock <|> citationBlock <|>
173+ manyTill (referenceKey <|> anchorDef <|>
174+ noteBlock <|> citationBlock <|>
174175 headerBlock <|> lineClump) eof
175176 setInput docMinusKeys
176177 setPosition startPos
@@ -217,6 +218,7 @@ block = choice [ codeBlock
217218 , fieldList
218219 , include
219220 , directive
221+ , anchor
220222 , comment
221223 , header
222224 , hrule
@@ -1054,16 +1056,49 @@ stripTicks = reverse . stripTick . reverse . stripTick
10541056 where stripTick ('`':xs) = xs
10551057 stripTick xs = xs
10561058
1059+referenceNames :: PandocMonad m => RSTParser m [String]
1060+referenceNames = do
1061+ let rn = try $ do
1062+ string ".. _"
1063+ (_, ref) <- withRaw referenceName
1064+ char ':'
1065+ return ref
1066+ first <- rn
1067+ rest <- many (try (blanklines *> rn))
1068+ return (first:rest)
1069+
10571070regularKey :: PandocMonad m => RSTParser m ()
10581071regularKey = try $ do
1059− string ".. _"
1060− (_,ref) <- withRaw referenceName
1061− char ':'
1072+ -- we allow several references to the same URL, e.g.
1073+ -- .. _hello:
1074+ -- .. _goodbye: url.com
1075+ refs <- referenceNames
10621076 src <- targetURI
1063− let key = toKey $ stripTicks ref
1077+ guard $ not (null src)
10641078 --TODO: parse width, height, class and name attributes
1065− updateState $ \s -> s { stateKeys = M.insert key ((src,""), nullAttr) $
1066− stateKeys s }
1079+ let keys = map (toKey . stripTicks) refs
1080+ forM_ keys $ \key ->
1081+ updateState $ \s -> s { stateKeys = M.insert key ((src,""), nullAttr) $
1082+ stateKeys s }
1083+
1084+anchorDef :: PandocMonad m => RSTParser m [Char]
1085+anchorDef = try $ do
1086+ (refs, raw) <- withRaw (try (referenceNames <* blanklines))
1087+ let keys = map stripTicks refs
1088+ forM_ keys $ \rawkey ->
1089+ updateState $ \s -> s { stateKeys =
1090+ M.insert (toKey rawkey) (('#':rawkey,""), nullAttr) $ stateKeys s }
1091+ -- keep this for 2nd round of parsing, where we'll add the divs (anchor)
1092+ return raw
1093+
1094+anchor :: PandocMonad m => RSTParser m Blocks
1095+anchor = try $ do
1096+ refs <- referenceNames
1097+ blanklines
1098+ b <- block
1099+ -- put identifier on next block:
1100+ let addDiv ref = B.divWith (ref, [], [])
1101+ return $ foldr addDiv b refs
10671102
10681103headerBlock :: PandocMonad m => RSTParser m [Char]
10691104headerBlock = do
src/Text/Pandoc/Readers/TWiki.hs+5−6
@@ -42,13 +42,13 @@ import qualified Data.Foldable as F
4242import Data.Maybe (fromMaybe)
4343import Text.HTML.TagSoup
4444import qualified Text.Pandoc.Builder as B
45−import Text.Pandoc.Class (PandocMonad, report)
45+import Text.Pandoc.Class (PandocMonad(..))
4646import Text.Pandoc.Definition
47−import Text.Pandoc.Logging
4847import Text.Pandoc.Options
49−import Text.Pandoc.Parsing hiding (enclosed, macro, nested)
48+import Text.Pandoc.Parsing hiding (enclosed, nested)
5049import Text.Pandoc.Readers.HTML (htmlTag, isCommentTag)
5150import Text.Pandoc.XML (fromEntities)
51+import Text.Pandoc.Shared (crFilter)
5252import Data.Text (Text)
5353import qualified Data.Text as T
5454
@@ -59,7 +59,7 @@ readTWiki :: PandocMonad m
5959 -> m Pandoc
6060readTWiki opts s = do
6161 res <- readWithM parseTWiki def{ stateOptions = opts }
62− (T.unpack s ++ "\n\n")
62+ (T.unpack (crFilter s) ++ "\n\n")
6363 case res of
6464 Left e -> throwError e
6565 Right d -> return d
@@ -133,12 +133,11 @@ parseTWiki = do
133133
134134block :: PandocMonad m => TWParser m B.Blocks
135135block = do
136− pos <- getPosition
137136 res <- mempty <$ skipMany1 blankline
138137 <|> blockElements
139138 <|> para
140139 skipMany blankline
141− report $ ParsingTrace (take 60 $ show $ B.toList res) pos
140+ trace (take 60 $ show $ B.toList res)
142141 return res
143142
144143blockElements :: PandocMonad m => TWParser m B.Blocks
src/Text/Pandoc/Readers/Textile.hs+5−7
@@ -61,15 +61,14 @@ import Text.HTML.TagSoup (Tag (..), fromAttrib)
6161import Text.HTML.TagSoup.Match
6262import Text.Pandoc.Builder (Blocks, Inlines, trimInlines)
6363import qualified Text.Pandoc.Builder as B
64−import Text.Pandoc.Class (PandocMonad, report)
64+import Text.Pandoc.Class (PandocMonad(..))
6565import Text.Pandoc.CSS
6666import Text.Pandoc.Definition
67−import Text.Pandoc.Logging
6867import Text.Pandoc.Options
6968import Text.Pandoc.Parsing
7069import Text.Pandoc.Readers.HTML (htmlTag, isBlockTag, isInlineTag)
7170import Text.Pandoc.Readers.LaTeX (rawLaTeXBlock, rawLaTeXInline)
72−import Text.Pandoc.Shared (trim)
71+import Text.Pandoc.Shared (trim, crFilter)
7372import Data.Text (Text)
7473import qualified Data.Text as T
7574
@@ -80,7 +79,7 @@ readTextile :: PandocMonad m
8079 -> m Pandoc
8180readTextile opts s = do
8281 parsed <- readWithM parseTextile def{ stateOptions = opts }
83− (T.unpack s ++ "\n\n")
82+ (T.unpack (crFilter s) ++ "\n\n")
8483 case parsed of
8584 Right result -> return result
8685 Left e -> throwError e
@@ -143,8 +142,7 @@ blockParsers = [ codeBlock
143142block :: PandocMonad m => ParserT [Char] ParserState m Blocks
144143block = do
145144 res <- choice blockParsers <?> "block"
146− pos <- getPosition
147− report $ ParsingTrace (take 60 $ show $ B.toList res) pos
145+ trace (take 60 $ show $ B.toList res)
148146 return res
149147
150148commentBlock :: PandocMonad m => ParserT [Char] ParserState m Blocks
@@ -575,7 +573,7 @@ rawHtmlInline = B.rawInline "html" . snd <$> htmlTag isInlineTag
575573rawLaTeXInline' :: PandocMonad m => ParserT [Char] ParserState m Inlines
576574rawLaTeXInline' = try $ do
577575 guardEnabled Ext_raw_tex
578− B.singleton <$> rawLaTeXInline
576+ B.rawInline "latex" <$> rawLaTeXInline
579577
580578-- | Textile standard link syntax is "label":target. But we
581579-- can also have ["label":target].
src/Text/Pandoc/Readers/Txt2Tags.hs+5−3
@@ -40,8 +40,8 @@ import Text.Pandoc.Builder (Blocks, Inlines, trimInlines)
4040import qualified Text.Pandoc.Builder as B
4141import Text.Pandoc.Definition
4242import Text.Pandoc.Options
43−import Text.Pandoc.Parsing hiding (macro, space, spaces, uri)
44−import Text.Pandoc.Shared (compactify, compactifyDL, escapeURI)
43+import Text.Pandoc.Parsing hiding (space, spaces, uri)
44+import Text.Pandoc.Shared (compactify, compactifyDL, escapeURI, crFilter)
4545import Control.Monad (guard, void, when)
4646import Control.Monad.Reader (Reader, asks, runReader)
4747import Data.Default
@@ -95,7 +95,9 @@ readTxt2Tags :: PandocMonad m
9595 -> m Pandoc
9696readTxt2Tags opts s = do
9797 meta <- getT2TMeta
98− let parsed = flip runReader meta $ readWithM parseT2T (def {stateOptions = opts}) (T.unpack s ++ "\n\n")
98+ let parsed = flip runReader meta $
99+ readWithM parseT2T (def {stateOptions = opts}) $
100+ T.unpack (crFilter s) ++ "\n\n"
99101 case parsed of
100102 Right result -> return $ result
101103 Left e -> throwError e
src/Text/Pandoc/Readers/Vimwiki.hsnew file+673
@@ -0,0 +1,673 @@
1+{-
2+ Copyright (C) 2017 Yuchen Pei <me@ypei.me>
3+
4+This program is free software; you can redistribute it and/or modify
5+it under the terms of the GNU General Public License as published by
6+the Free Software Foundation; either version 2 of the License, or
7+(at your option) any later version.
8+
9+This program is distributed in the hope that it will be useful,
10+but WITHOUT ANY WARRANTY; without even the implied warranty of
11+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+GNU General Public License for more details.
13+
14+You should have received a copy of the GNU General Public License
15+along with this program; if not, write to the Free Software
16+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17+-}
18+
19+{- |
20+ Module : Text.Pandoc.Readers.Vimwiki
21+ Copyright : Copyright (C) 2017 Yuchen Pei
22+ License : GNU GPL, version 2 or above
23+
24+ Maintainer : Yuchen Pei <me@ypei.me>
25+ Stability : alpha
26+ Portability : portable
27+
28+Conversion of vimwiki text to 'Pandoc' document.
29+-}
30+{--
31+[X]: implemented
32+[O]: not implemented
33+* block parsers:
34+ * [X] header
35+ * [X] hrule
36+ * [X] comment
37+ * [X] blockquote
38+ * [X] preformatted -- using codeblock
39+ * [X] displaymath
40+ * [X] bulletlist / orderedlist
41+ * [X] todo lists -- using span.
42+ * [X] table
43+ * [X] centered table -- using div
44+ * [O] colspan and rowspan -- see issue #1024
45+ * [X] paragraph
46+ * [X] definition list
47+* inline parsers:
48+ * [X] bareURL
49+ * [X] strong
50+ * [X] emph
51+ * [X] strikeout
52+ * [X] code
53+ * [X] link
54+ * [X] image
55+ * [X] inline math
56+ * [X] tag
57+ * [X] sub- and super-scripts
58+* misc:
59+ * [X] `TODO:` mark
60+ * [X] metadata placeholders: %title and %date
61+ * [O] control placeholders: %template and %nohtml -- ignored
62+--}
63+
64+module Text.Pandoc.Readers.Vimwiki ( readVimwiki
65+ ) where
66+import Control.Monad.Except (throwError)
67+import Control.Monad (guard)
68+import Data.Default
69+import Data.Maybe
70+import Data.Monoid ((<>))
71+import Data.List (isInfixOf, isPrefixOf)
72+import Data.Text (Text, unpack)
73+import Text.Pandoc.Builder (Blocks, Inlines, trimInlines, fromList, toList)
74+import qualified Text.Pandoc.Builder
75+ as B (headerWith, str, space, strong, emph, strikeout, code, link, image,
76+ spanWith, para, horizontalRule, blockQuote, bulletList, plain,
77+ orderedList, simpleTable, softbreak, codeBlockWith, imageWith, divWith,
78+ setMeta, definitionList, superscript, subscript, displayMath,
79+ math)
80+import Text.Pandoc.Class (PandocMonad(..))
81+import Text.Pandoc.Definition (Pandoc(..), Inline(Space),
82+ Block(BulletList, OrderedList), Attr, nullMeta, Meta, ListNumberStyle(..),
83+ ListNumberDelim(..))
84+import Text.Pandoc.Options (ReaderOptions)
85+import Text.Pandoc.Parsing (readWithM, ParserT, stateOptions, ParserState,
86+ stateMeta', blanklines, registerHeader, spaceChar, emailAddress, uri, F, runF,
87+ orderedListMarker, many1Till)
88+import Text.Pandoc.Shared (splitBy, stripFirstAndLast, stringify, crFilter)
89+import Text.Parsec.Char (spaces, char, anyChar, newline, string, noneOf,
90+ alphaNum)
91+import Text.Parsec.Combinator (eof, choice, many1, manyTill, count, skipMany1,
92+ notFollowedBy, option)
93+import Text.Parsec.Prim (many, try, updateState, getState)
94+import Text.Parsec.Char (oneOf, space)
95+import Text.Parsec.Combinator (lookAhead, between)
96+import Text.Parsec.Prim ((<|>))
97+
98+readVimwiki :: PandocMonad m => ReaderOptions -> Text -> m Pandoc
99+readVimwiki opts s = do
100+ res <- readWithM parseVimwiki def{ stateOptions = opts }
101+ (unpack (crFilter s))
102+ case res of
103+ Left e -> throwError e
104+ Right result -> return result
105+
106+type VwParser = ParserT [Char] ParserState
107+
108+
109+-- constants
110+
111+specialChars :: [Char]
112+specialChars = "=*-#[]_~{}`$|:%^,"
113+
114+spaceChars :: [Char]
115+spaceChars = " \t\n"
116+
117+-- main parser
118+
119+parseVimwiki :: PandocMonad m => VwParser m Pandoc
120+parseVimwiki = do
121+ bs <- mconcat <$> many block
122+ spaces
123+ eof
124+ st <- getState
125+ let meta = runF (stateMeta' st) st
126+ return $ Pandoc meta (toList bs)
127+
128+-- block parser
129+
130+block :: PandocMonad m => VwParser m Blocks
131+block = do
132+ res <- choice [ mempty <$ blanklines
133+ , header
134+ , hrule
135+ , mempty <$ comment
136+ , mixedList
137+ , preformatted
138+ , displayMath
139+ , table
140+ , mempty <$ placeholder
141+ , blockQuote
142+ , definitionList
143+ , para
144+ ]
145+ trace (take 60 $ show $ toList res)
146+ return res
147+
148+blockML :: PandocMonad m => VwParser m Blocks
149+blockML = choice [preformatted, displayMath, table]
150+
151+header :: PandocMonad m => VwParser m Blocks
152+header = try $ do
153+ sp <- many spaceChar
154+ eqs <- many1 (char '=')
155+ spaceChar
156+ let lev = length eqs
157+ guard $ lev <= 6
158+ contents <- trimInlines . mconcat <$> manyTill inline (try $ spaceChar
159+ >> (string eqs) >> many spaceChar >> newline)
160+ attr <- registerHeader (makeId contents,
161+ (if sp == "" then [] else ["justcenter"]), []) contents
162+ return $ B.headerWith attr lev contents
163+
164+para :: PandocMonad m => VwParser m Blocks
165+para = try $ do
166+ contents <- trimInlines . mconcat <$> many1 inline
167+ if all (==Space) (toList contents)
168+ then return mempty
169+ else return $ B.para contents
170+
171+hrule :: PandocMonad m => VwParser m Blocks
172+hrule = try $ B.horizontalRule <$ (string "----" >> many (char '-') >> newline)
173+
174+comment :: PandocMonad m => VwParser m ()
175+comment = try $ do
176+ many spaceChar >> string "%%" >> many (noneOf "\n")
177+ return ()
178+
179+blockQuote :: PandocMonad m => VwParser m Blocks
180+blockQuote = try $ do
181+ string " "
182+ contents <- trimInlines . mconcat <$> many1 inlineBQ
183+ if all (==Space) (toList contents)
184+ then return mempty
185+ else return $ B.blockQuote $ B.plain contents
186+
187+definitionList :: PandocMonad m => VwParser m Blocks
188+definitionList = try $
189+ B.definitionList <$> (many1 (dlItemWithDT <|> dlItemWithoutDT))
190+
191+dlItemWithDT :: PandocMonad m => VwParser m (Inlines, [Blocks])
192+dlItemWithDT = do
193+ dt <- definitionTerm
194+ dds <- many definitionDef
195+ return $ (dt, dds)
196+
197+dlItemWithoutDT :: PandocMonad m => VwParser m (Inlines, [Blocks])
198+dlItemWithoutDT = do
199+ dds <- many1 definitionDef
200+ return $ (mempty, dds)
201+
202+definitionDef :: PandocMonad m => VwParser m Blocks
203+definitionDef = try $
204+ (notFollowedBy definitionTerm) >> many spaceChar
205+ >> (definitionDef1 <|> definitionDef2)
206+
207+definitionDef1 :: PandocMonad m => VwParser m Blocks
208+definitionDef1 = try $ mempty <$ defMarkerE
209+
210+definitionDef2 :: PandocMonad m => VwParser m Blocks
211+definitionDef2 = try $ B.plain <$>
212+ (defMarkerM >> (trimInlines . mconcat <$> many inline') <* newline)
213+
214+
215+definitionTerm :: PandocMonad m => VwParser m Inlines
216+definitionTerm = try $ do
217+ x <- definitionTerm1 <|> definitionTerm2
218+ guard $ (stringify x /= "")
219+ return x
220+
221+definitionTerm1 :: PandocMonad m => VwParser m Inlines
222+definitionTerm1 = try $
223+ trimInlines . mconcat <$> manyTill inline' (try $ defMarkerE)
224+
225+definitionTerm2 :: PandocMonad m => VwParser m Inlines
226+definitionTerm2 = try $ trimInlines . mconcat <$> manyTill inline'
227+ (try $ lookAhead $ (defMarkerM >> notFollowedBy hasDefMarkerM))
228+
229+defMarkerM :: PandocMonad m => VwParser m Char
230+defMarkerM = string "::" >> spaceChar
231+
232+defMarkerE :: PandocMonad m => VwParser m Char
233+defMarkerE = string "::" >> newline
234+
235+hasDefMarkerM :: PandocMonad m => VwParser m String
236+hasDefMarkerM = manyTill (noneOf "\n") (try defMarkerM)
237+
238+preformatted :: PandocMonad m => VwParser m Blocks
239+preformatted = try $ do
240+ many spaceChar >> string "{{{"
241+ attrText <- many (noneOf "\n")
242+ lookAhead newline
243+ contents <- manyTill anyChar (try (char '\n' >> many spaceChar >> string "}}}"
244+ >> many spaceChar >> newline))
245+ if (not $ contents == "") && (head contents == '\n')
246+ then return $ B.codeBlockWith (makeAttr attrText) (tail contents)
247+ else return $ B.codeBlockWith (makeAttr attrText) contents
248+
249+makeAttr :: String -> Attr
250+makeAttr s =
251+ let xs = splitBy (`elem` " \t") s in
252+ ("", [], catMaybes $ map nameValue xs)
253+
254+nameValue :: String -> Maybe (String, String)
255+nameValue s =
256+ let t = splitBy (== '=') s in
257+ if length t /= 2
258+ then Nothing
259+ else let (a, b) = (head t, last t) in
260+ if ((length b) < 2) || ((head b, last b) /= ('"', '"'))
261+ then Nothing
262+ else Just (a, stripFirstAndLast b)
263+
264+
265+displayMath :: PandocMonad m => VwParser m Blocks
266+displayMath = try $ do
267+ many spaceChar >> string "{{$"
268+ mathTag <- option "" mathTagParser
269+ many space
270+ contents <- manyTill anyChar (try (char '\n' >> many spaceChar >> string "}}$"
271+ >> many spaceChar >> newline))
272+ let contentsWithTags
273+ | mathTag == "" = contents
274+ | otherwise = "\\begin{" ++ mathTag ++ "}\n" ++ contents
275+ ++ "\n\\end{" ++ mathTag ++ "}"
276+ return $ B.para $ B.displayMath contentsWithTags
277+
278+
279+mathTagLaTeX :: String -> String
280+mathTagLaTeX s = case s of
281+ "equation" -> ""
282+ "equation*" -> ""
283+ "gather" -> "gathered"
284+ "gather*" -> "gathered"
285+ "multline" -> "gathered"
286+ "multline*" -> "gathered"
287+ "eqnarray" -> "aligned"
288+ "eqnarray*" -> "aligned"
289+ "align" -> "aligned"
290+ "align*" -> "aligned"
291+ "alignat" -> "aligned"
292+ "alignat*" -> "aligned"
293+ _ -> s
294+
295+
296+mixedList :: PandocMonad m => VwParser m Blocks
297+mixedList = try $ do
298+ (bl, _) <- mixedList' (-1)
299+ return $ head bl
300+
301+mixedList' :: PandocMonad m => Int -> VwParser m ([Blocks], Int)
302+mixedList' prevInd = do
303+ (curInd, builder) <- option (-1, "na") (lookAhead listStart)
304+ if curInd < prevInd
305+ then return ([], curInd)
306+ else do
307+ listStart
308+ curLine <- listItemContent
309+ let listBuilder =
310+ if builder == "ul" then B.bulletList else B.orderedList
311+ (subList, lowInd) <- (mixedList' curInd)
312+ if lowInd >= curInd
313+ then do
314+ (sameIndList, endInd) <- (mixedList' lowInd)
315+ let curList = (combineList curLine subList) ++ sameIndList
316+ if curInd > prevInd
317+ then return ([listBuilder curList], endInd)
318+ else return (curList, endInd)
319+ else do
320+ let (curList, endInd) = ((combineList curLine subList),
321+ lowInd)
322+ if curInd > prevInd
323+ then return ([listBuilder curList], endInd)
324+ else return (curList, endInd)
325+
326+plainInlineML' :: PandocMonad m => Inlines -> VwParser m Blocks
327+plainInlineML' w = do
328+ xs <- many inlineML
329+ newline
330+ return $ B.plain $ trimInlines $ mconcat $ w:xs
331+
332+plainInlineML :: PandocMonad m => VwParser m Blocks
333+plainInlineML = (notFollowedBy listStart) >> spaceChar >> plainInlineML' mempty
334+
335+
336+listItemContent :: PandocMonad m => VwParser m Blocks
337+listItemContent = try $ do
338+ w <- option mempty listTodoMarker
339+ x <- plainInlineML' w
340+ y <- many blocksThenInline
341+ z <- many blockML
342+ return $ mconcat $ x:y ++ z
343+
344+blocksThenInline :: PandocMonad m => VwParser m Blocks
345+blocksThenInline = try $ do
346+ y <- many1 blockML
347+ x <- plainInlineML
348+ return $ mconcat $ y ++ [x]
349+
350+listTodoMarker :: PandocMonad m => VwParser m Inlines
351+listTodoMarker = try $ do
352+ x <- between (many spaceChar >> char '[') (char ']' >> spaceChar)
353+ (oneOf " .oOX")
354+ return $ makeListMarkerSpan x
355+
356+makeListMarkerSpan :: Char -> Inlines
357+makeListMarkerSpan x =
358+ let cl = case x of
359+ ' ' -> "done0"
360+ '.' -> "done1"
361+ 'o' -> "done2"
362+ 'O' -> "done3"
363+ 'X' -> "done4"
364+ _ -> ""
365+ in
366+ B.spanWith ("", [cl], []) mempty
367+
368+combineList :: Blocks -> [Blocks] -> [Blocks]
369+combineList x [y] = case toList y of
370+ [BulletList z] -> [fromList $ (toList x)
371+ ++ [BulletList z]]
372+ [OrderedList attr z] -> [fromList $ (toList x)
373+ ++ [OrderedList attr z]]
374+ _ -> x:[y]
375+combineList x xs = x:xs
376+
377+listStart :: PandocMonad m => VwParser m (Int, String)
378+listStart = try $ do
379+ s <- many spaceChar
380+ listType <- bulletListMarkers <|> orderedListMarkers
381+ spaceChar
382+ return (length s, listType)
383+
384+bulletListMarkers :: PandocMonad m => VwParser m String
385+bulletListMarkers = "ul" <$ (char '*' <|> char '-')
386+
387+orderedListMarkers :: PandocMonad m => VwParser m String
388+orderedListMarkers =
389+ ("ol" <$ (choice $ (orderedListMarker Decimal Period):(($OneParen)
390+ <$> orderedListMarker
391+ <$> [Decimal, LowerRoman, UpperRoman, LowerAlpha, UpperAlpha])))
392+ <|> ("ol" <$ char '#')
393+
394+--many need trimInlines
395+table :: PandocMonad m => VwParser m Blocks
396+table = try $ do
397+ indent <- lookAhead (many spaceChar)
398+ (th, trs) <- table1 <|> table2
399+ let tab = B.simpleTable th trs
400+ if indent == ""
401+ then return tab
402+ else return $ B.divWith ("", ["center"], []) tab
403+
404+-- table with header
405+table1 :: PandocMonad m => VwParser m ([Blocks], [[Blocks]])
406+table1 = try $ do
407+ th <- tableRow
408+ many1 tableHeaderSeparator
409+ trs <- many tableRow
410+ return (th, trs)
411+
412+-- headerless table
413+table2 :: PandocMonad m => VwParser m ([Blocks], [[Blocks]])
414+table2 = try $ do
415+ trs <- many1 tableRow
416+ return (take (length $ head trs) $ repeat mempty, trs)
417+
418+tableHeaderSeparator :: PandocMonad m => VwParser m ()
419+tableHeaderSeparator = try $ do
420+ many spaceChar >> char '|' >> many1 ((many1 $ char '-') >> char '|')
421+ >> many spaceChar >> newline
422+ return ()
423+
424+tableRow :: PandocMonad m => VwParser m [Blocks]
425+tableRow = try $ do
426+ many spaceChar >> char '|'
427+ s <- lookAhead $ manyTill anyChar (try (char '|' >> many spaceChar
428+ >> newline))
429+ guard $ not $ "||" `isInfixOf` ("|" ++ s ++ "|")
430+ tr <- many tableCell
431+ many spaceChar >> char '\n'
432+ return tr
433+
434+tableCell :: PandocMonad m => VwParser m Blocks
435+tableCell = try $
436+ B.plain <$> trimInlines . mconcat <$> (manyTill inline' (char '|'))
437+
438+placeholder :: PandocMonad m => VwParser m ()
439+placeholder = try $
440+ (choice (ph <$> ["title", "date"])) <|> noHtmlPh <|> templatePh
441+
442+ph :: PandocMonad m => String -> VwParser m ()
443+ph s = try $ do
444+ many spaceChar >> (string $ '%':s) >> spaceChar
445+ contents <- (trimInlines . mconcat <$> (manyTill inline (lookAhead newline)))
446+ --use lookAhead because of placeholder in the whitespace parser
447+ let meta' = return $ B.setMeta s contents nullMeta :: F Meta
448+ updateState $ \st -> st { stateMeta' = stateMeta' st <> meta' }
449+
450+noHtmlPh :: PandocMonad m => VwParser m ()
451+noHtmlPh = try $
452+ () <$ (many spaceChar >> string "%nohtml" >> many spaceChar
453+ >> (lookAhead newline))
454+
455+templatePh :: PandocMonad m => VwParser m ()
456+templatePh = try $
457+ () <$ (many spaceChar >> string "%template" >> (many $ noneOf "\n")
458+ >> (lookAhead newline))
459+
460+-- inline parser
461+
462+inline :: PandocMonad m => VwParser m Inlines
463+inline = choice $ (whitespace endlineP):inlineList
464+
465+inlineList :: PandocMonad m => [VwParser m Inlines]
466+inlineList = [ bareURL
467+ , todoMark
468+ , str
469+ , strong
470+ , emph
471+ , strikeout
472+ , code
473+ , link
474+ , image
475+ , inlineMath
476+ , tag
477+ , superscript
478+ , subscript
479+ , special
480+ ]
481+
482+-- inline parser without softbreaks or comment breaks
483+inline' :: PandocMonad m => VwParser m Inlines
484+inline' = choice $ whitespace':inlineList
485+
486+-- inline parser for blockquotes
487+inlineBQ :: PandocMonad m => VwParser m Inlines
488+inlineBQ = choice $ (whitespace endlineBQ):inlineList
489+
490+-- inline parser for mixedlists
491+inlineML :: PandocMonad m => VwParser m Inlines
492+inlineML = choice $ (whitespace endlineML):inlineList
493+
494+str :: PandocMonad m => VwParser m Inlines
495+str = B.str <$> (many1 $ noneOf $ spaceChars ++ specialChars)
496+
497+whitespace :: PandocMonad m => VwParser m () -> VwParser m Inlines
498+whitespace endline = B.space <$ (skipMany1 spaceChar <|>
499+ (try (newline >> (comment <|> placeholder))))
500+ <|> B.softbreak <$ endline
501+
502+whitespace' :: PandocMonad m => VwParser m Inlines
503+whitespace' = B.space <$ skipMany1 spaceChar
504+
505+special :: PandocMonad m => VwParser m Inlines
506+special = B.str <$> count 1 (oneOf specialChars)
507+
508+bareURL :: PandocMonad m => VwParser m Inlines
509+bareURL = try $ do
510+ (orig, src) <- uri <|> emailAddress
511+ return $ B.link src "" (B.str orig)
512+
513+strong :: PandocMonad m => VwParser m Inlines
514+strong = try $ do
515+ s <- lookAhead $ between (char '*') (char '*') (many1 $ noneOf "*")
516+ guard $ (not $ (head s) `elem` spaceChars)
517+ && (not $ (last s) `elem` spaceChars)
518+ char '*'
519+ contents <- mconcat <$> (manyTill inline' $ char '*'
520+ >> notFollowedBy alphaNum)
521+ return $ (B.spanWith ((makeId contents), [], []) mempty)
522+ <> (B.strong contents)
523+
524+makeId :: Inlines -> String
525+makeId i = concat (stringify <$> (toList i))
526+
527+emph :: PandocMonad m => VwParser m Inlines
528+emph = try $ do
529+ s <- lookAhead $ between (char '_') (char '_') (many1 $ noneOf "_")
530+ guard $ (not $ (head s) `elem` spaceChars)
531+ && (not $ (last s) `elem` spaceChars)
532+ char '_'
533+ contents <- mconcat <$> (manyTill inline' $ char '_'
534+ >> notFollowedBy alphaNum)
535+ return $ B.emph contents
536+
537+strikeout :: PandocMonad m => VwParser m Inlines
538+strikeout = try $ do
539+ string "~~"
540+ contents <- mconcat <$> (many1Till inline' $ string $ "~~")
541+ return $ B.strikeout contents
542+
543+code :: PandocMonad m => VwParser m Inlines
544+code = try $ do
545+ char '`'
546+ contents <- many1Till (noneOf "\n") (char '`')
547+ return $ B.code contents
548+
549+superscript :: PandocMonad m => VwParser m Inlines
550+superscript = try $
551+ B.superscript <$> mconcat <$> (char '^' >> many1Till inline' (char '^'))
552+
553+subscript :: PandocMonad m => VwParser m Inlines
554+subscript = try $
555+ B.subscript <$> mconcat <$> (string ",,"
556+ >> many1Till inline' (try $ string ",,"))
557+
558+link :: PandocMonad m => VwParser m Inlines
559+link = try $ do
560+ string "[["
561+ contents <- lookAhead $ manyTill anyChar (string "]]")
562+ case '|' `elem` contents of
563+ False -> do
564+ manyTill anyChar (string "]]")
565+-- not using try here because [[hell]o]] is not rendered as a link in vimwiki
566+ return $ B.link (procLink contents) "" (B.str contents)
567+ True -> do
568+ url <- manyTill anyChar $ char '|'
569+ lab <- mconcat <$> (manyTill inline $ string "]]")
570+ return $ B.link (procLink url) "" lab
571+
572+image :: PandocMonad m => VwParser m Inlines
573+image = try $ do
574+ string "{{"
575+ contentText <- lookAhead $ manyTill (noneOf "\n") (try $ string "}}")
576+ images $ length $ filter (== '|') contentText
577+
578+images :: PandocMonad m => Int -> VwParser m Inlines
579+images k
580+ | k == 0 = do
581+ imgurl <- manyTill anyChar (try $ string "}}")
582+ return $ B.image (procImgurl imgurl) "" (B.str "")
583+ | k == 1 = do
584+ imgurl <- manyTill anyChar (char '|')
585+ alt <- mconcat <$> (manyTill inline $ (try $ string "}}"))
586+ return $ B.image (procImgurl imgurl) "" alt
587+ | k == 2 = do
588+ imgurl <- manyTill anyChar (char '|')
589+ alt <- mconcat <$> (manyTill inline $ char '|')
590+ attrText <- manyTill anyChar (try $ string "}}")
591+ return $ B.imageWith (makeAttr attrText) (procImgurl imgurl) "" alt
592+ | otherwise = do
593+ imgurl <- manyTill anyChar (char '|')
594+ alt <- mconcat <$> (manyTill inline $ char '|')
595+ attrText <- manyTill anyChar (char '|')
596+ manyTill anyChar (try $ string "}}")
597+ return $ B.imageWith (makeAttr attrText) (procImgurl imgurl) "" alt
598+
599+procLink' :: String -> String
600+procLink' s
601+ | ((take 6 s) == "local:") = "file" ++ (drop 5 s)
602+ | ((take 6 s) == "diary:") = "diary/" ++ (drop 6 s) ++ ".html"
603+ | or ((`isPrefixOf` s) <$> [ "http:", "https:", "ftp:", "file:", "mailto:",
604+ "news:", "telnet:" ])
605+ = s
606+ | s == "" = ""
607+ | (last s) == '/' = s
608+ | otherwise = s ++ ".html"
609+
610+procLink :: String -> String
611+procLink s = procLink' x ++ y
612+ where (x, y) = break (=='#') s
613+
614+procImgurl :: String -> String
615+procImgurl s = if ((take 6 s) == "local:") then "file" ++ (drop 5 s) else s
616+
617+inlineMath :: PandocMonad m => VwParser m Inlines
618+inlineMath = try $ do
619+ char '$'
620+ contents <- many1Till (noneOf "\n") (char '$')
621+ return $ B.math contents
622+
623+tag :: PandocMonad m => VwParser m Inlines
624+tag = try $ do
625+ char ':'
626+ s <- manyTill (noneOf spaceChars) (try (char ':' >> (lookAhead space)))
627+ guard $ not $ "::" `isInfixOf` (":" ++ s ++ ":")
628+ let ss = splitBy (==':') s
629+ return $ mconcat $ (makeTagSpan' $ head ss):(makeTagSpan <$> (tail ss))
630+
631+todoMark :: PandocMonad m => VwParser m Inlines
632+todoMark = try $ do
633+ string "TODO:"
634+ return $ B.spanWith ("", ["todo"], []) (B.str "TODO:")
635+
636+-- helper functions and parsers
637+endlineP :: PandocMonad m => VwParser m ()
638+endlineP = () <$ try (newline <* nFBTTBSB <* notFollowedBy blockQuote)
639+
640+endlineBQ :: PandocMonad m => VwParser m ()
641+endlineBQ = () <$ try (newline <* nFBTTBSB <* string " ")
642+
643+endlineML :: PandocMonad m => VwParser m ()
644+endlineML = () <$ try (newline <* nFBTTBSB <* many1 spaceChar)
645+
646+--- nFBTTBSB is short for notFollowedByThingsThatBreakSoftBreaks
647+nFBTTBSB :: PandocMonad m => VwParser m ()
648+nFBTTBSB =
649+ notFollowedBy newline <*
650+ notFollowedBy hrule <*
651+ notFollowedBy tableRow <*
652+ notFollowedBy header <*
653+ notFollowedBy listStart <*
654+ notFollowedBy preformatted <*
655+ notFollowedBy displayMath <*
656+ notFollowedBy hasDefMarker
657+
658+hasDefMarker :: PandocMonad m => VwParser m ()
659+hasDefMarker = () <$ (manyTill (noneOf "\n") (string "::" >> oneOf spaceChars))
660+
661+makeTagSpan' :: String -> Inlines
662+makeTagSpan' s = B.spanWith ('-' : s, [], []) (B.str "") <>
663+ B.spanWith (s, ["tag"], []) (B.str s)
664+
665+makeTagSpan :: String -> Inlines
666+makeTagSpan s = (B.space) <> (makeTagSpan' s)
667+
668+mathTagParser :: PandocMonad m => VwParser m String
669+mathTagParser = do
670+ s <- try $ lookAhead (char '%' >> (manyTill (noneOf spaceChars)
671+ (try $ char '%' >> many (noneOf $ '%':spaceChars) >> space)))
672+ char '%' >> string s >> char '%'
673+ return $ mathTagLaTeX s
src/Text/Pandoc/Shared.hs+9−5
@@ -49,6 +49,7 @@ module Text.Pandoc.Shared (
4949 toRomanNumeral,
5050 escapeURI,
5151 tabFilter,
52+ crFilter,
5253 -- * Date/time
5354 normalizeDate,
5455 -- * Pandoc block and inline list processing
@@ -117,7 +118,7 @@ import Text.Pandoc.MIME (MimeType)
117118import Text.Pandoc.Error (PandocError(..))
118119import System.FilePath ( (</>) )
119120import Data.Generics (Typeable, Data)
120−import qualified Control.Monad.State as S
121+import qualified Control.Monad.State.Strict as S
121122import qualified Control.Exception as E
122123import Control.Monad (msum, unless, MonadPlus(..))
123124import Text.Pandoc.Pretty (charWidth)
@@ -279,13 +280,12 @@ escapeURI = escapeURIString (not . needsEscaping)
279280 where needsEscaping c = isSpace c || c `elem`
280281 ['<','>','|','"','{','}','[',']','^', '`']
281282
282−-- | Convert tabs to spaces and filter out DOS line endings.
283−-- Tabs will be preserved if tab stop is set to 0.
283+-- | Convert tabs to spaces. Tabs will be preserved if tab stop is set to 0.
284284tabFilter :: Int -- ^ Tab stop
285285 -> T.Text -- ^ Input
286286 -> T.Text
287−tabFilter tabStop = T.filter (/= '\r') . T.unlines .
288− (if tabStop == 0 then id else map go) . T.lines
287+tabFilter 0 = id
288+tabFilter tabStop = T.unlines . map go . T.lines
289289 where go s =
290290 let (s1, s2) = T.break (== '\t') s
291291 in if T.null s2
@@ -294,6 +294,10 @@ tabFilter tabStop = T.filter (/= '\r') . T.unlines .
294294 (tabStop - (T.length s1 `mod` tabStop)) (T.pack " ")
295295 <> go (T.drop 1 s2)
296296
297+-- | Strip out DOS line endings.
298+crFilter :: T.Text -> T.Text
299+crFilter = T.filter (/= '\r')
300+
297301--
298302-- Date/time
299303--
src/Text/Pandoc/Templates.hs+14−10
@@ -33,20 +33,20 @@ A simple templating system with variable substitution and conditionals.
3333
3434-}
3535
36−module Text.Pandoc.Templates ( renderTemplate
36+module Text.Pandoc.Templates ( module Text.DocTemplates
3737 , renderTemplate'
38− , TemplateTarget
39− , varListToJSON
40− , compileTemplate
41− , Template
42− , getDefaultTemplate ) where
38+ , getDefaultTemplate
39+ ) where
4340
4441import qualified Control.Exception as E (IOException, try)
42+import Control.Monad.Except (throwError)
4543import Data.Aeson (ToJSON (..))
4644import qualified Data.Text as T
4745import System.FilePath ((<.>), (</>))
4846import Text.DocTemplates (Template, TemplateTarget, applyTemplate,
4947 compileTemplate, renderTemplate, varListToJSON)
48+import Text.Pandoc.Class (PandocMonad)
49+import Text.Pandoc.Error
5050import Text.Pandoc.Shared (readDataFileUTF8)
5151
5252-- | Get default template for the specified writer.
@@ -72,7 +72,11 @@ getDefaultTemplate user writer = do
7272 _ -> let fname = "templates" </> "default" <.> format
7373 in E.try $ readDataFileUTF8 user fname
7474
75−-- | Like 'applyTemplate', but raising an error if compilation fails.
76−renderTemplate' :: (ToJSON a, TemplateTarget b) => String -> a -> b
77−renderTemplate' template = either error id . applyTemplate (T.pack template)
78−
75+-- | Like 'applyTemplate', but runs in PandocMonad and
76+-- raises an error if compilation fails.
77+renderTemplate' :: (PandocMonad m, ToJSON a, TemplateTarget b)
78+ => String -> a -> m b
79+renderTemplate' template context = do
80+ case applyTemplate (T.pack template) context of
81+ Left e -> throwError (PandocTemplateError e)
82+ Right r -> return r
src/Text/Pandoc/Writers.hs+4−7
@@ -176,19 +176,16 @@ writers = [
176176 ,("muse" , TextWriter writeMuse)
177177 ]
178178
179−getWriter :: PandocMonad m => String -> Either String (Writer m)
179+-- | Retrieve writer, extensions based on formatSpec (format+extensions).
180+getWriter :: PandocMonad m => String -> Either String (Writer m, Extensions)
180181getWriter s
181182 = case parseFormatSpec s of
182183 Left e -> Left $ intercalate "\n" [m | Message m <- errorMessages e]
183184 Right (writerName, setExts) ->
184185 case lookup writerName writers of
185186 Nothing -> Left $ "Unknown writer: " ++ writerName
186− Just (TextWriter r) -> Right $ TextWriter $
187− \o -> r o{ writerExtensions = setExts $
188− getDefaultExtensions writerName }
189− Just (ByteStringWriter r) -> Right $ ByteStringWriter $
190− \o -> r o{ writerExtensions = setExts $
191− getDefaultExtensions writerName }
187+ Just r -> Right (r, setExts $
188+ getDefaultExtensions writerName)
192189
193190writeJSON :: WriterOptions -> Pandoc -> Text
194191writeJSON _ = UTF8.toText . BL.toStrict . encode
src/Text/Pandoc/Writers/AsciiDoc.hs+2−2
@@ -37,7 +37,7 @@ that it has omitted the construct.
3737AsciiDoc: <http://www.methods.co.nz/asciidoc/>
3838-}
3939module Text.Pandoc.Writers.AsciiDoc (writeAsciiDoc) where
40−import Control.Monad.State
40+import Control.Monad.State.Strict
4141import Data.Aeson (Result (..), Value (String), fromJSON, toJSON)
4242import Data.Char (isPunctuation, isSpace)
4343import Data.List (intercalate, intersperse, stripPrefix)
@@ -105,7 +105,7 @@ pandocToAsciiDoc opts (Pandoc meta blocks) = do
105105 $ metadata'
106106 case writerTemplate opts of
107107 Nothing -> return main
108− Just tpl -> return $ renderTemplate' tpl context
108+ Just tpl -> renderTemplate' tpl context
109109
110110-- | Escape special characters for AsciiDoc.
111111escapeString :: String -> String
src/Text/Pandoc/Writers/CommonMark.hs+4−4
@@ -32,7 +32,7 @@ CommonMark: <http://commonmark.org>
3232module Text.Pandoc.Writers.CommonMark (writeCommonMark) where
3333
3434import CMark
35−import Control.Monad.State (State, get, modify, runState)
35+import Control.Monad.State.Strict (State, get, modify, runState)
3636import Data.Foldable (foldrM)
3737import Data.Text (Text)
3838import qualified Data.Text as T
@@ -58,9 +58,9 @@ writeCommonMark opts (Pandoc meta blocks) = do
5858 (inlinesToCommonMark opts)
5959 meta
6060 let context = defField "body" main $ metadata
61− return $ case writerTemplate opts of
62− Nothing -> main
63− Just tpl -> renderTemplate' tpl context
61+ case writerTemplate opts of
62+ Nothing -> return main
63+ Just tpl -> renderTemplate' tpl context
6464
6565processNotes :: Inline -> State [[Block]] Inline
6666processNotes (Note bs) = do
src/Text/Pandoc/Writers/ConTeXt.hs+46−40
@@ -1,4 +1,5 @@
11{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE ScopedTypeVariables #-}
23{-
34Copyright (C) 2007-2017 John MacFarlane <jgm@berkeley.edu>
45
@@ -29,12 +30,13 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2930Conversion of 'Pandoc' format into ConTeXt.
3031-}
3132module Text.Pandoc.Writers.ConTeXt ( writeConTeXt ) where
32−import Control.Monad.State
33+import Control.Monad.State.Strict
3334import Data.Char (ord)
3435import Data.List (intercalate, intersperse)
3536import Data.Maybe (catMaybes)
3637import Data.Text (Text)
3738import Network.URI (unEscapeString)
39+import Text.Pandoc.BCP47
3840import Text.Pandoc.Class (PandocMonad, report)
3941import Text.Pandoc.Logging
4042import Text.Pandoc.Definition
@@ -88,6 +90,7 @@ pandocToConTeXt options (Pandoc meta blocks) = do
8890 ,("top","margin-top")
8991 ,("bottom","margin-bottom")
9092 ]
93+ mblang <- fromBCP47 (getLang options meta)
9194 let context = defField "toc" (writerTableOfContents options)
9295 $ defField "placelist" (intercalate ("," :: String) $
9396 take (writerTOCDepth options +
@@ -100,14 +103,17 @@ pandocToConTeXt options (Pandoc meta blocks) = do
100103 $ defField "body" main
101104 $ defField "layout" layoutFromMargins
102105 $ defField "number-sections" (writerNumberSections options)
106+ $ maybe id (defField "context-lang") mblang
107+ $ (case getField "papersize" metadata of
108+ Just ("a4" :: String) -> resetField "papersize"
109+ ("A4" :: String)
110+ _ -> id)
103111 $ metadata
104− let context' = defField "context-lang" (maybe "" (fromBcp47 . splitBy (=='-')) $
105− getField "lang" context)
106− $ defField "context-dir" (toContextDir $ getField "dir" context)
107− $ context
108− return $ case writerTemplate options of
109− Nothing -> main
110− Just tpl -> renderTemplate' tpl context'
112+ let context' = defField "context-dir" (toContextDir
113+ $ getField "dir" context) context
114+ case writerTemplate options of
115+ Nothing -> return main
116+ Just tpl -> renderTemplate' tpl context'
111117
112118toContextDir :: Maybe String -> String
113119toContextDir (Just "rtl") = "r2l"
@@ -186,6 +192,7 @@ blockToConTeXt b@(RawBlock _ _ ) = do
186192 return empty
187193blockToConTeXt (Div (ident,_,kvs) bs) = do
188194 let align dir txt = "\\startalignment[" <> dir <> "]" $$ txt $$ "\\stopalignment"
195+ mblang <- fromBCP47 (lookup "lang" kvs)
189196 let wrapRef txt = if null ident
190197 then txt
191198 else ("\\reference" <> brackets (text $ toLabel ident) <>
@@ -194,9 +201,9 @@ blockToConTeXt (Div (ident,_,kvs) bs) = do
194201 Just "rtl" -> align "righttoleft"
195202 Just "ltr" -> align "lefttoright"
196203 _ -> id
197− wrapLang txt = case lookup "lang" kvs of
204+ wrapLang txt = case mblang of
198205 Just lng -> "\\start\\language["
199− <> text (fromBcp47' lng) <> "]" $$ txt $$ "\\stop"
206+ <> text lng <> "]" $$ txt $$ "\\stop"
200207 Nothing -> txt
201208 wrapBlank txt = blankline <> txt <> blankline
202209 fmap (wrapBlank . wrapLang . wrapDir . wrapRef) $ blockListToConTeXt bs
@@ -416,12 +423,13 @@ inlineToConTeXt (Note contents) = do
416423 else text "\\startbuffer " <> nest 2 contents' <>
417424 text "\\stopbuffer\\footnote{\\getbuffer}"
418425inlineToConTeXt (Span (_,_,kvs) ils) = do
426+ mblang <- fromBCP47 (lookup "lang" kvs)
419427 let wrapDir txt = case lookup "dir" kvs of
420428 Just "rtl" -> braces $ "\\righttoleft " <> txt
421429 Just "ltr" -> braces $ "\\lefttoright " <> txt
422430 _ -> txt
423− wrapLang txt = case lookup "lang" kvs of
424− Just lng -> "\\start\\language[" <> text (fromBcp47' lng)
431+ wrapLang txt = case mblang of
432+ Just lng -> "\\start\\language[" <> text lng
425433 <> "]" <> txt <> "\\stop "
426434 Nothing -> txt
427435 fmap (wrapLang . wrapDir) $ inlineListToConTeXt ils
@@ -458,36 +466,34 @@ sectionHeader (ident,classes,_) hdrLevel lst = do
458466 <> blankline
459467 _ -> contents <> blankline
460468
461−fromBcp47' :: String -> String
462−fromBcp47' = fromBcp47 . splitBy (=='-')
469+fromBCP47 :: PandocMonad m => Maybe String -> WM m (Maybe String)
470+fromBCP47 mbs = fromBCP47' <$> toLang mbs
463471
464472-- Takes a list of the constituents of a BCP 47 language code
465473-- and irons out ConTeXt's exceptions
466474-- https://tools.ietf.org/html/bcp47#section-2.1
467475-- http://wiki.contextgarden.net/Language_Codes
468−fromBcp47 :: [String] -> String
469−fromBcp47 [] = ""
470−fromBcp47 ("ar":"SY":_) = "ar-sy"
471−fromBcp47 ("ar":"IQ":_) = "ar-iq"
472−fromBcp47 ("ar":"JO":_) = "ar-jo"
473−fromBcp47 ("ar":"LB":_) = "ar-lb"
474−fromBcp47 ("ar":"DZ":_) = "ar-dz"
475−fromBcp47 ("ar":"MA":_) = "ar-ma"
476−fromBcp47 ("de":"1901":_) = "deo"
477−fromBcp47 ("de":"DE":_) = "de-de"
478−fromBcp47 ("de":"AT":_) = "de-at"
479−fromBcp47 ("de":"CH":_) = "de-ch"
480−fromBcp47 ("el":"poly":_) = "agr"
481−fromBcp47 ("en":"US":_) = "en-us"
482−fromBcp47 ("en":"GB":_) = "en-gb"
483−fromBcp47 ("grc":_) = "agr"
484−fromBcp47 x = fromIso $ head x
485− where
486− fromIso "el" = "gr"
487− fromIso "eu" = "ba"
488− fromIso "he" = "il"
489− fromIso "jp" = "ja"
490− fromIso "uk" = "ua"
491− fromIso "vi" = "vn"
492− fromIso "zh" = "cn"
493− fromIso l = l
476+fromBCP47' :: Maybe Lang -> Maybe String
477+fromBCP47' (Just (Lang "ar" _ "SY" _) ) = Just "ar-sy"
478+fromBCP47' (Just (Lang "ar" _ "IQ" _) ) = Just "ar-iq"
479+fromBCP47' (Just (Lang "ar" _ "JO" _) ) = Just "ar-jo"
480+fromBCP47' (Just (Lang "ar" _ "LB" _) ) = Just "ar-lb"
481+fromBCP47' (Just (Lang "ar" _ "DZ" _) ) = Just "ar-dz"
482+fromBCP47' (Just (Lang "ar" _ "MA" _) ) = Just "ar-ma"
483+fromBCP47' (Just (Lang "de" _ _ ["1901"]) ) = Just "deo"
484+fromBCP47' (Just (Lang "de" _ "DE" _) ) = Just "de-de"
485+fromBCP47' (Just (Lang "de" _ "AT" _) ) = Just "de-at"
486+fromBCP47' (Just (Lang "de" _ "CH" _) ) = Just "de-ch"
487+fromBCP47' (Just (Lang "el" _ _ ["poly"]) ) = Just "agr"
488+fromBCP47' (Just (Lang "en" _ "US" _) ) = Just "en-us"
489+fromBCP47' (Just (Lang "en" _ "GB" _) ) = Just "en-gb"
490+fromBCP47' (Just (Lang "grc"_ _ _) ) = Just "agr"
491+fromBCP47' (Just (Lang "el" _ _ _) ) = Just "gr"
492+fromBCP47' (Just (Lang "eu" _ _ _) ) = Just "ba"
493+fromBCP47' (Just (Lang "he" _ _ _) ) = Just "il"
494+fromBCP47' (Just (Lang "jp" _ _ _) ) = Just "ja"
495+fromBCP47' (Just (Lang "uk" _ _ _) ) = Just "ua"
496+fromBCP47' (Just (Lang "vi" _ _ _) ) = Just "vn"
497+fromBCP47' (Just (Lang "zh" _ _ _) ) = Just "cn"
498+fromBCP47' (Just (Lang l _ _ _) ) = Just l
499+fromBCP47' Nothing = Nothing
src/Text/Pandoc/Writers/Custom.hs+5−2
@@ -46,6 +46,7 @@ import Data.Typeable
4646import GHC.IO.Encoding (getForeignEncoding, setForeignEncoding, utf8)
4747import Scripting.Lua (LuaState, StackValue, callfunc)
4848import qualified Scripting.Lua as Lua
49+import Text.Pandoc.Error
4950import Text.Pandoc.Lua.Compat ( loadstring )
5051import Text.Pandoc.Lua.Util ( addValue )
5152import Text.Pandoc.Lua.SharedInstances ()
@@ -141,8 +142,10 @@ writeCustom luaFile opts doc@(Pandoc meta _) = do
141142 let body = rendered
142143 case writerTemplate opts of
143144 Nothing -> return $ pack body
144− Just tpl -> return $ pack $
145− renderTemplate' tpl $ setField "body" body context
145+ Just tpl ->
146+ case applyTemplate (pack tpl) $ setField "body" body context of
147+ Left e -> throw (PandocTemplateError e)
148+ Right r -> return (pack r)
146149
147150docToCustom :: LuaState -> WriterOptions -> Pandoc -> IO String
148151docToCustom lua opts (Pandoc (Meta metamap) blocks) = do
src/Text/Pandoc/Writers/Docbook.hs+7−5
@@ -124,9 +124,9 @@ writeDocbook opts (Pandoc meta blocks) = do
124124 MathML -> True
125125 _ -> False)
126126 $ metadata
127− return $ case writerTemplate opts of
128− Nothing -> main
129− Just tpl -> renderTemplate' tpl context
127+ case writerTemplate opts of
128+ Nothing -> return main
129+ Just tpl -> renderTemplate' tpl context
130130
131131-- | Convert an Element to Docbook.
132132elementToDocbook :: PandocMonad m => WriterOptions -> Int -> Element -> DB m Doc
@@ -217,8 +217,10 @@ blockToDocbook opts (Div (ident,_,_) bs) = do
217217 (if null ident
218218 then mempty
219219 else selfClosingTag "anchor" [("id", ident)]) $$ contents
220−blockToDocbook _ (Header _ _ _) =
221− return empty -- should not occur after hierarchicalize
220+blockToDocbook _ h@(Header _ _ _) = do
221+ -- should not occur after hierarchicalize, except inside lists/blockquotes
222+ report $ BlockNotRendered h
223+ return empty
222224blockToDocbook opts (Plain lst) = inlinesToDocbook opts lst
223225-- title beginning with fig: indicates that the image is a figure
224226blockToDocbook opts (Para [Image attr txt (src,'f':'i':'g':':':_)]) = do
src/Text/Pandoc/Writers/Docx.hs+15−9
@@ -37,7 +37,7 @@ import Codec.Archive.Zip
3737import Control.Applicative ((<|>))
3838import Control.Monad.Except (catchError)
3939import Control.Monad.Reader
40−import Control.Monad.State
40+import Control.Monad.State.Strict
4141import qualified Data.ByteString as B
4242import qualified Data.ByteString.Lazy as BL
4343import qualified Data.ByteString.Lazy.Char8 as BL8
@@ -68,6 +68,7 @@ import qualified Text.Pandoc.UTF8 as UTF8
6868import Text.Pandoc.Walk
6969import Text.Pandoc.Writers.Math
7070import Text.Pandoc.Writers.Shared (fixDisplayMath)
71+import Text.Pandoc.BCP47 (getLang, renderLang, toLang)
7172import Text.Printf (printf)
7273import Text.TeXMath
7374import Text.XML.Light as XML
@@ -257,12 +258,11 @@ writeDocx opts doc@(Pandoc meta _) = do
257258 )
258259
259260 -- styles
260− let lang = case lookupMeta "lang" meta of
261− Just (MetaInlines [Str s]) -> Just s
262− Just (MetaString s) -> Just s
263− _ -> Nothing
261+ mblang <- toLang $ getLang opts meta
264262 let addLang :: Element -> Element
265− addLang e = case lang >>= \l -> (return . XMLC.toTree . go l . XMLC.fromElement) e of
263+ addLang e = case mblang >>= \l ->
264+ (return . XMLC.toTree . go (renderLang l)
265+ . XMLC.fromElement) e of
266266 Just (Elem e') -> e'
267267 _ -> e -- return original
268268 where go :: String -> Cursor -> Cursor
@@ -657,6 +657,9 @@ mkNumbering lists = do
657657 elts <- mapM mkAbstractNum (ordNub lists)
658658 return $ elts ++ zipWith mkNum lists [baseListId..(baseListId + length lists - 1)]
659659
660+maxListLevel :: Int
661+maxListLevel = 8
662+
660663mkNum :: ListMarker -> Int -> Element
661664mkNum marker numid =
662665 mknode "w:num" [("w:numId",show numid)]
@@ -666,7 +669,8 @@ mkNum marker numid =
666669 BulletMarker -> []
667670 NumberMarker _ _ start ->
668671 map (\lvl -> mknode "w:lvlOverride" [("w:ilvl",show (lvl :: Int))]
669− $ mknode "w:startOverride" [("w:val",show start)] ()) [0..6]
672+ $ mknode "w:startOverride" [("w:val",show start)] ())
673+ [0..maxListLevel]
670674
671675mkAbstractNum :: (PandocMonad m) => ListMarker -> m Element
672676mkAbstractNum marker = do
@@ -675,7 +679,8 @@ mkAbstractNum marker = do
675679 return $ mknode "w:abstractNum" [("w:abstractNumId",listMarkerToId marker)]
676680 $ mknode "w:nsid" [("w:val", printf "%8x" nsid)] ()
677681 : mknode "w:multiLevelType" [("w:val","multilevel")] ()
678− : map (mkLvl marker) [0..6]
682+ : map (mkLvl marker)
683+ [0..maxListLevel]
679684
680685mkLvl :: ListMarker -> Int -> Element
681686mkLvl marker lvl =
@@ -706,7 +711,7 @@ mkLvl marker lvl =
706711 bulletFor 3 = "\x2013"
707712 bulletFor 4 = "\x2022"
708713 bulletFor 5 = "\x2013"
709− bulletFor _ = "\x2022"
714+ bulletFor x = bulletFor (x `mod` 6)
710715 styleFor UpperAlpha _ = "upperLetter"
711716 styleFor LowerAlpha _ = "lowerLetter"
712717 styleFor UpperRoman _ = "upperRoman"
@@ -718,6 +723,7 @@ mkLvl marker lvl =
718723 styleFor DefaultStyle 4 = "decimal"
719724 styleFor DefaultStyle 5 = "lowerLetter"
720725 styleFor DefaultStyle 6 = "lowerRoman"
726+ styleFor DefaultStyle x = styleFor DefaultStyle (x `mod` 7)
721727 styleFor _ _ = "decimal"
722728 patternFor OneParen s = s ++ ")"
723729 patternFor TwoParens s = "(" ++ s ++ ")"
src/Text/Pandoc/Writers/DokuWiki.hs+2−2
@@ -41,7 +41,7 @@ DokuWiki: <https://www.dokuwiki.org/dokuwiki>
4141module Text.Pandoc.Writers.DokuWiki ( writeDokuWiki ) where
4242import Control.Monad (zipWithM)
4343import Control.Monad.Reader (ReaderT, ask, local, runReaderT)
44−import Control.Monad.State (StateT, evalStateT, gets, modify)
44+import Control.Monad.State.Strict (StateT, evalStateT, gets, modify)
4545import Data.Default (Default (..))
4646import Data.List (intercalate, intersect, isPrefixOf, transpose)
4747import Data.Text (Text, pack)
@@ -103,7 +103,7 @@ pandocToDokuWiki opts (Pandoc meta blocks) = do
103103 $ metadata
104104 case writerTemplate opts of
105105 Nothing -> return main
106− Just tpl -> return $ renderTemplate' tpl context
106+ Just tpl -> renderTemplate' tpl context
107107
108108-- | Escape special characters for DokuWiki.
109109escapeString :: String -> String
src/Text/Pandoc/Writers/EPUB.hs+52−26
@@ -34,14 +34,14 @@ Conversion of 'Pandoc' documents to EPUB.
3434module Text.Pandoc.Writers.EPUB ( writeEPUB2, writeEPUB3 ) where
3535import Codec.Archive.Zip (Entry, addEntryToArchive, eRelativePath, emptyArchive,
3636 fromArchive, fromEntry, toEntry)
37−import Control.Monad (mplus, when, zipWithM)
37+import Control.Monad (mplus, when, unless, zipWithM)
3838import Control.Monad.Except (catchError, throwError)
39−import Control.Monad.State (State, StateT, evalState, evalStateT, get, gets,
39+import Control.Monad.State.Strict (State, StateT, evalState, evalStateT, get, gets,
4040 lift, modify, put)
4141import qualified Data.ByteString.Lazy as B
4242import qualified Data.ByteString.Lazy.Char8 as B8
4343import qualified Data.Text.Lazy as TL
44−import Data.Char (isAlphaNum, isDigit, toLower)
44+import Data.Char (isAlphaNum, isDigit, toLower, isAscii)
4545import Data.List (intercalate, isInfixOf, isPrefixOf)
4646import qualified Data.Map as M
4747import Data.Maybe (catMaybes, fromMaybe)
@@ -103,6 +103,7 @@ data EPUBMetadata = EPUBMetadata{
103103 , epubCoverImage :: Maybe String
104104 , epubStylesheets :: [FilePath]
105105 , epubPageDirection :: Maybe ProgressionDirection
106+ , epubIbooksFields :: [(String, String)]
106107 } deriving Show
107108
108109data Date = Date{
@@ -312,6 +313,7 @@ metadataFromMeta opts meta = EPUBMetadata{
312313 , epubCoverImage = coverImage
313314 , epubStylesheets = stylesheets
314315 , epubPageDirection = pageDirection
316+ , epubIbooksFields = ibooksFields
315317 }
316318 where identifiers = getIdentifier meta
317319 titles = getTitle meta
@@ -339,6 +341,10 @@ metadataFromMeta opts meta = EPUBMetadata{
339341 Just "ltr" -> Just LTR
340342 Just "rtl" -> Just RTL
341343 _ -> Nothing
344+ ibooksFields = case lookupMeta "ibooks" meta of
345+ Just (MetaMap mp)
346+ -> M.toList $ M.map metaValueToString mp
347+ _ -> []
342348
343349-- | Produce an EPUB2 file from a Pandoc document.
344350writeEPUB2 :: PandocMonad m
@@ -361,8 +367,7 @@ writeEPUB :: PandocMonad m
361367 -> Pandoc -- ^ Document to convert
362368 -> m B.ByteString
363369writeEPUB epubVersion opts doc =
364− let initState = EPUBState { stMediaPaths = []
365− }
370+ let initState = EPUBState { stMediaPaths = [] }
366371 in
367372 evalStateT (pandocToEPUB epubVersion opts doc)
368373 initState
@@ -373,6 +378,10 @@ pandocToEPUB :: PandocMonad m
373378 -> Pandoc
374379 -> E m B.ByteString
375380pandocToEPUB version opts doc@(Pandoc meta _) = do
381+ let epubSubdir = writerEpubSubdirectory opts
382+ -- sanity check on epubSubdir
383+ unless (all (\c -> isAscii c && isAlphaNum c) epubSubdir) $
384+ throwError $ PandocEpubSubdirectoryError epubSubdir
376385 let epub3 = version == EPUB3
377386 let writeHtml o = fmap (UTF8.fromTextLazy . TL.fromStrict) .
378387 writeHtmlStringForEPUB version o
@@ -383,14 +392,15 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do
383392 -- stylesheet
384393 stylesheets <- case epubStylesheets metadata of
385394 [] -> (\x -> [B.fromChunks [x]]) <$>
386− P.readDataFile (writerUserDataDir opts) "epub.css"
395+ P.readDataFile (writerUserDataDir opts)
396+ "epub.css"
387397 fs -> mapM P.readFileLazy fs
388398 let stylesheetEntries = zipWith
389− (\bs n -> mkEntry ("stylesheet" ++ show n ++ ".css") bs)
399+ (\bs n -> mkEntry ("styles/stylesheet" ++ show n ++ ".css") bs)
390400 stylesheets [(1 :: Int)..]
391401
392402 let vars = ("epub3", if epub3 then "true" else "false")
393− : map (\e -> ("css", eRelativePath e)) stylesheetEntries
403+ : map (\e -> ("css", "../" ++ eRelativePath e)) stylesheetEntries
394404 ++ [(x,y) | (x,y) <- writerVariables opts, x /= "css"]
395405 let opts' = opts{ writerEmailObfuscation = NoObfuscation
396406 , writerSectionDivs = True
@@ -418,7 +428,7 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do
418428 tpContent <- lift $ writeHtml opts'{
419429 writerVariables = ("titlepage","true"):vars }
420430 (Pandoc meta [])
421− let tpEntry = mkEntry "title_page.xhtml" tpContent
431+ let tpEntry = mkEntry "text/title_page.xhtml" tpContent
422432
423433 -- handle pictures
424434 -- mediaRef <- P.newIORef []
@@ -431,7 +441,8 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do
431441 when (null xs) $
432442 report $ CouldNotFetchResource f "glob did not match any font files"
433443 return xs
434− let mkFontEntry f = mkEntry (takeFileName f) `fmap` (lift $ P.readFileLazy f)
444+ let mkFontEntry f = mkEntry ("fonts/" ++ takeFileName f) <$>
445+ lift (P.readFileLazy f)
435446 fontFiles <- concat <$> mapM matchingGlob (writerEpubFonts opts')
436447 fontEntries <- mapM mkFontEntry fontFiles
437448
@@ -516,7 +527,7 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do
516527 chapters'
517528
518529 let chapToEntry num (Chapter mbnum bs) =
519− mkEntry (showChapter num) <$>
530+ mkEntry ("text/" ++ showChapter num) <$>
520531 (writeHtml opts'{ writerNumberOffset = fromMaybe [] mbnum }
521532 $ case bs of
522533 (Header _ _ xs : _) ->
@@ -572,7 +583,8 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do
572583 EPUB2 -> "2.0"
573584 EPUB3 -> "3.0")
574585 ,("xmlns","http://www.idpf.org/2007/opf")
575− ,("unique-identifier","epub-id-1")] $
586+ ,("unique-identifier","epub-id-1")
587+ ,("prefix","ibooks: http://vocabulary.itunes.apple.com/rdf/ibooks/vocabulary-extensions-1.0/")] $
576588 [ metadataElement version metadata currentTime
577589 , unode "manifest" $
578590 [ unode "item" ! [("id","ncx"), ("href","toc.ncx")
@@ -648,12 +660,12 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do
648660 navMapFormatter n tit src subs = unode "navPoint" !
649661 [("id", "navPoint-" ++ show n)] $
650662 [ unode "navLabel" $ unode "text" tit
651− , unode "content" ! [("src", src)] $ ()
663+ , unode "content" ! [("src", "text/" ++ src)] $ ()
652664 ] ++ subs
653665
654666 let tpNode = unode "navPoint" ! [("id", "navPoint-0")] $
655667 [ unode "navLabel" $ unode "text" (stringify $ docTitle' meta)
656− , unode "content" ! [("src","title_page.xhtml")] $ () ]
668+ , unode "content" ! [("src","text/title_page.xhtml")] $ () ]
657669
658670 navMap <- lift $ evalStateT (mapM (navPointNode navMapFormatter) secs) 1
659671 let tocData = UTF8.fromStringLazy $ ppTopElement $
@@ -681,7 +693,8 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do
681693 let navXhtmlFormatter :: Int -> String -> String -> [Element] -> Element
682694 navXhtmlFormatter n tit src subs = unode "li" !
683695 [("id", "toc-li-" ++ show n)] $
684− (unode "a" ! [("href",src)]
696+ (unode "a" ! [("href", "text/" ++
697+ src)]
685698 $ tit)
686699 : case subs of
687700 [] -> []
@@ -714,7 +727,11 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do
714727 ]
715728 ]
716729 else []
717− navData <- lift $ writeHtml opts'{ writerVariables = ("navpage","true"):vars }
730+ navData <- lift $ writeHtml opts'{ writerVariables = ("navpage","true"):
731+ -- remove the leading ../ from stylesheet paths:
732+ map (\(k,v) -> if k == "css"
733+ then (k, drop 3 v)
734+ else (k, v)) vars }
718735 (Pandoc (setMeta "title"
719736 (walk removeNote $ fromList $ docTitle' meta) nullMeta)
720737 (navBlocks ++ landmarks))
@@ -728,7 +745,8 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do
728745 unode "container" ! [("version","1.0")
729746 ,("xmlns","urn:oasis:names:tc:opendocument:xmlns:container")] $
730747 unode "rootfiles" $
731− unode "rootfile" ! [("full-path","content.opf")
748+ unode "rootfile" ! [("full-path",
749+ epubSubdir ++ ['/' | not (null epubSubdir)] ++ "content.opf")
732750 ,("media-type","application/oebps-package+xml")] $ ()
733751 let containerEntry = mkEntry "META-INF/container.xml" containerData
734752
@@ -739,10 +757,14 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do
739757 unode "option" ! [("name","specified-fonts")] $ "true"
740758 let appleEntry = mkEntry "META-INF/com.apple.ibooks.display-options.xml" apple
741759
760+ let addEpubSubdir :: Entry -> Entry
761+ addEpubSubdir e = e{ eRelativePath =
762+ epubSubdir ++ ['/' | not (null epubSubdir)] ++ eRelativePath e }
742763 -- construct archive
743− let archive = foldr addEntryToArchive emptyArchive
744− (mimetypeEntry : containerEntry : appleEntry : tpEntry :
745− contentsEntry : tocEntry : navEntry :
764+ let archive = foldr addEntryToArchive emptyArchive $
765+ [mimetypeEntry, containerEntry, appleEntry] ++
766+ map addEpubSubdir
767+ (tpEntry : contentsEntry : tocEntry : navEntry :
746768 (stylesheetEntries ++ picEntries ++ cpicEntry ++
747769 cpgEntry ++ chapterEntries ++ fontEntries))
748770 return $ fromArchive archive
@@ -751,7 +773,8 @@ metadataElement :: EPUBVersion -> EPUBMetadata -> UTCTime -> Element
751773metadataElement version md currentTime =
752774 unode "metadata" ! [("xmlns:dc","http://purl.org/dc/elements/1.1/")
753775 ,("xmlns:opf","http://www.idpf.org/2007/opf")] $ mdNodes
754− where mdNodes = identifierNodes ++ titleNodes ++ dateNodes ++ languageNodes
776+ where mdNodes = identifierNodes ++ titleNodes ++ dateNodes
777+ ++ languageNodes ++ ibooksNodes
755778 ++ creatorNodes ++ contributorNodes ++ subjectNodes
756779 ++ descriptionNodes ++ typeNodes ++ formatNodes
757780 ++ publisherNodes ++ sourceNodes ++ relationNodes
@@ -770,6 +793,8 @@ metadataElement version md currentTime =
770793 [] -> []
771794 (x:_) -> [dcNode "date" ! [("id","epub-date")]
772795 $ dateText x]
796+ ibooksNodes = map ibooksNode (epubIbooksFields md)
797+ ibooksNode (k, v) = unode "meta" ! [("property", "ibooks:" ++ k)] $ v
773798 languageNodes = [dcTag "language" $ epubLanguage md]
774799 creatorNodes = withIds "epub-creator" (toCreatorNode "creator") $
775800 epubCreator md
@@ -883,10 +908,10 @@ modifyMediaRef opts oldsrc = do
883908 Nothing -> catchError
884909 (do (img, mbMime) <- P.fetchItem (writerSourceURL opts) oldsrc
885910 let new = "media/file" ++ show (length media) ++
886− fromMaybe (takeExtension (takeWhile (/='?') oldsrc))
887− (('.':) <$> (mbMime >>= extensionFromMimeType))
911+ fromMaybe (takeExtension (takeWhile (/='?') oldsrc))
912+ (('.':) <$> (mbMime >>= extensionFromMimeType))
888913 epochtime <- floor `fmap` lift P.getPOSIXTime
889− let entry = toEntry new epochtime $ B.fromChunks . (:[]) $ img
914+ let entry = toEntry new epochtime (B.fromChunks . (:[]) $ img)
890915 modify $ \st -> st{ stMediaPaths =
891916 (oldsrc, (new, Just entry)):media}
892917 return new)
@@ -913,12 +938,13 @@ transformInline :: PandocMonad m
913938 -> E m Inline
914939transformInline opts (Image attr lab (src,tit)) = do
915940 newsrc <- modifyMediaRef opts src
916− return $ Image attr lab (newsrc, tit)
941+ return $ Image attr lab ("../" ++ newsrc, tit)
917942transformInline opts (x@(Math t m))
918943 | WebTeX url <- writerHTMLMathMethod opts = do
919944 newsrc <- modifyMediaRef opts (url ++ urlEncode m)
920945 let mathclass = if t == DisplayMath then "display" else "inline"
921− return $ Span ("",["math",mathclass],[]) [Image nullAttr [x] (newsrc, "")]
946+ return $ Span ("",["math",mathclass],[])
947+ [Image nullAttr [x] ("../" ++ newsrc, "")]
922948transformInline opts (RawInline fmt raw)
923949 | fmt == Format "html" = do
924950 let tags = parseTags raw
src/Text/Pandoc/Writers/FB2.hs+7−6
@@ -37,9 +37,9 @@ FictionBook is an XML-based e-book format. For more information see:
3737-}
3838module Text.Pandoc.Writers.FB2 (writeFB2) where
3939
40−import Control.Monad.Except (catchError, throwError)
41−import Control.Monad.State (StateT, evalStateT, get, lift, modify)
42−import Control.Monad.State (liftM)
40+import Control.Monad.Except (catchError)
41+import Control.Monad.State.Strict (StateT, evalStateT, get, lift, modify)
42+import Control.Monad.State.Strict (liftM)
4343import Data.ByteString.Base64 (encode)
4444import qualified Data.ByteString.Char8 as B8
4545import Data.Char (isAscii, isControl, isSpace, toLower)
@@ -54,7 +54,6 @@ import qualified Text.XML.Light.Cursor as XC
5454import Text.Pandoc.Class (PandocMonad, report)
5555import qualified Text.Pandoc.Class as P
5656import Text.Pandoc.Definition
57−import Text.Pandoc.Error
5857import Text.Pandoc.Logging
5958import Text.Pandoc.Options (HTMLMathMethod (..), WriterOptions (..), def)
6059import Text.Pandoc.Shared (capitalize, isHeaderBlock, isURI, linesToPara,
@@ -371,8 +370,10 @@ blockToXml (DefinitionList defs) =
371370 needsBreak (Para _) = False
372371 needsBreak (Plain ins) = LineBreak `notElem` ins
373372 needsBreak _ = True
374−blockToXml (Header _ _ _) = -- should never happen, see renderSections
375− throwError $ PandocShouldNeverHappenError "unexpected header in section text"
373+blockToXml h@(Header _ _ _) = do
374+ -- should not occur after hierarchicalize, except inside lists/blockquotes
375+ report $ BlockNotRendered h
376+ return []
376377blockToXml HorizontalRule = return
377378 [ el "empty-line" ()
378379 , el "p" (txt (replicate 10 '—'))
src/Text/Pandoc/Writers/HTML.hs+41−19
@@ -43,7 +43,7 @@ module Text.Pandoc.Writers.HTML (
4343 writeDZSlides,
4444 writeRevealJs
4545 ) where
46−import Control.Monad.State
46+import Control.Monad.State.Strict
4747import Data.Char (ord, toLower)
4848import Data.Text (Text)
4949import qualified Data.Text.Lazy as TL
@@ -210,7 +210,7 @@ writeHtmlString' st opts d = do
210210 lookup "sourcefile" (writerVariables opts)
211211 report $ NoTitleElement fallback
212212 return $ resetField "pagetitle" fallback context
213− return $ renderTemplate' tpl $
213+ renderTemplate' tpl $
214214 defField "body" (renderHtml' body) context'
215215
216216writeHtml' :: PandocMonad m => WriterState -> WriterOptions -> Pandoc -> m Html
@@ -241,7 +241,7 @@ pandocToHtml opts (Pandoc meta blocks) = do
241241 then blocks
242242 else prepSlides slideLevel blocks
243243 toc <- if writerTableOfContents opts && slideVariant /= S5Slides
244− then tableOfContents opts sects
244+ then fmap renderHtml' <$> tableOfContents opts sects
245245 else return Nothing
246246 blocks' <- liftM (mconcat . intersperse (nl opts)) $
247247 mapM (elementToHtml slideLevel opts) sects
@@ -253,7 +253,9 @@ pandocToHtml opts (Pandoc meta blocks) = do
253253 H.script ! A.src (toValue url)
254254 ! A.type_ "text/javascript"
255255 $ mempty
256− MathJax url ->
256+ MathJax url
257+ | slideVariant /= RevealJsSlides ->
258+ -- mathjax is handled via a special plugin in revealjs
257259 H.script ! A.src (toValue url)
258260 ! A.type_ "text/javascript"
259261 $ case slideVariant of
@@ -285,8 +287,16 @@ pandocToHtml opts (Pandoc meta blocks) = do
285287 (if stMath st
286288 then defField "math" (renderHtml' math)
287289 else id) $
290+ defField "mathjax"
291+ (case writerHTMLMathMethod opts of
292+ MathJax _ -> True
293+ _ -> False) $
288294 defField "quotes" (stQuotes st) $
289− maybe id (defField "toc" . renderHtml') toc $
295+ -- for backwards compatibility we populate toc
296+ -- with the contents of the toc, rather than a
297+ -- boolean:
298+ maybe id (defField "toc") toc $
299+ maybe id (defField "table-of-contents") toc $
290300 defField "author-meta" authsMeta $
291301 maybe id (defField "date-meta") (normalizeDate dateMeta) $
292302 defField "pagetitle" (stringifyHTML (docTitle meta)) $
@@ -597,7 +607,8 @@ blockToHtml opts (Para lst)
597607 contents <- inlineListToHtml opts lst
598608 return $ H.p contents
599609 where
600− isEmptyRaw [RawInline f _] = f /= (Format "html")
610+ isEmptyRaw [RawInline f _] = f `notElem` [Format "html",
611+ Format "html4", Format "html5"]
601612 isEmptyRaw _ = False
602613blockToHtml opts (LineBlock lns) =
603614 if writerWrapText opts == WrapNone
@@ -626,14 +637,17 @@ blockToHtml opts (Div attr@(ident, classes, kvs) bs) = do
626637 NoSlides -> addAttrs opts' attr $ H.div $ contents'
627638 _ -> mempty
628639 else addAttrs opts (ident, classes', kvs) $ divtag $ contents'
629−blockToHtml opts (RawBlock f str)
630− | f == Format "html" = return $ preEscapedString str
631− | (f == Format "latex" || f == Format "tex") &&
632− allowsMathEnvironments (writerHTMLMathMethod opts) &&
633− isMathEnvironment str = blockToHtml opts $ Plain [Math DisplayMath str]
634− | otherwise = do
635− report $ BlockNotRendered (RawBlock f str)
636− return mempty
640+blockToHtml opts (RawBlock f str) = do
641+ ishtml <- isRawHtml f
642+ if ishtml
643+ then return $ preEscapedString str
644+ else if (f == Format "latex" || f == Format "tex") &&
645+ allowsMathEnvironments (writerHTMLMathMethod opts) &&
646+ isMathEnvironment str
647+ then blockToHtml opts $ Plain [Math DisplayMath str]
648+ else do
649+ report $ BlockNotRendered (RawBlock f str)
650+ return mempty
637651blockToHtml _ (HorizontalRule) = do
638652 html5 <- gets stHtml5
639653 return $ if html5 then H5.hr else H.hr
@@ -971,11 +985,13 @@ inlineToHtml opts inline = do
971985 return $ case t of
972986 InlineMath -> m
973987 DisplayMath -> brtag >> m >> brtag
974− (RawInline f str)
975− | f == Format "html" -> return $ preEscapedString str
976− | otherwise -> do
977− report $ InlineNotRendered inline
978− return mempty
988+ (RawInline f str) -> do
989+ ishtml <- isRawHtml f
990+ if ishtml
991+ then return $ preEscapedString str
992+ else do
993+ report $ InlineNotRendered inline
994+ return mempty
979995 (Link attr txt (s,_)) | "mailto:" `isPrefixOf` s -> do
980996 linkText <- inlineListToHtml opts txt
981997 lift $ obfuscateLink opts attr linkText s
@@ -1123,3 +1139,9 @@ allowsMathEnvironments (MathJax _) = True
11231139allowsMathEnvironments (MathML) = True
11241140allowsMathEnvironments (WebTeX _) = True
11251141allowsMathEnvironments _ = False
1142+
1143+isRawHtml :: PandocMonad m => Format -> StateT WriterState m Bool
1144+isRawHtml f = do
1145+ html5 <- gets stHtml5
1146+ return $ f == Format "html" ||
1147+ ((html5 && f == Format "html5") || f == Format "html4")
src/Text/Pandoc/Writers/Haddock.hs+2−2
@@ -33,7 +33,7 @@ Conversion of 'Pandoc' documents to haddock markup.
3333Haddock: <http://www.haskell.org/haddock/doc/html/>
3434-}
3535module Text.Pandoc.Writers.Haddock (writeHaddock) where
36−import Control.Monad.State
36+import Control.Monad.State.Strict
3737import Data.Default
3838import Data.Text (Text)
3939import Data.List (intersperse, transpose)
@@ -80,7 +80,7 @@ pandocToHaddock opts (Pandoc meta blocks) = do
8080 $ metadata
8181 case writerTemplate opts of
8282 Nothing -> return main
83− Just tpl -> return $ renderTemplate' tpl context
83+ Just tpl -> renderTemplate' tpl context
8484
8585-- | Return haddock representation of notes.
8686notesToHaddock :: PandocMonad m
src/Text/Pandoc/Writers/ICML.hs+4−4
@@ -17,7 +17,7 @@ into InDesign with File -> Place.
1717-}
1818module Text.Pandoc.Writers.ICML (writeICML) where
1919import Control.Monad.Except (catchError)
20−import Control.Monad.State
20+import Control.Monad.State.Strict
2121import Data.List (intersperse, isInfixOf, isPrefixOf, stripPrefix)
2222import qualified Data.Set as Set
2323import Data.Text as Text (breakOnAll, pack)
@@ -147,9 +147,9 @@ writeICML opts (Pandoc meta blocks) = do
147147 $ defField "parStyles" (render' $ parStylesToDoc st)
148148 $ defField "hyperlinks" (render' $ hyperlinksToDoc $ links st)
149149 $ metadata
150− return $ case writerTemplate opts of
151− Nothing -> main
152− Just tpl -> renderTemplate' tpl context
150+ case writerTemplate opts of
151+ Nothing -> return main
152+ Just tpl -> renderTemplate' tpl context
153153
154154-- | Auxilary functions for parStylesToDoc and charStylesToDoc.
155155contains :: String -> (String, (String, String)) -> [(String, String)]
src/Text/Pandoc/Writers/JATS.hs+7−5
@@ -128,9 +128,9 @@ docToJATS opts (Pandoc meta blocks) = do
128128 MathML -> True
129129 _ -> False)
130130 $ metadata
131− return $ case writerTemplate opts of
132− Nothing -> main
133− Just tpl -> renderTemplate' tpl context
131+ case writerTemplate opts of
132+ Nothing -> return main
133+ Just tpl -> renderTemplate' tpl context
134134
135135-- | Convert an Element to JATS.
136136elementToJATS :: PandocMonad m => WriterOptions -> Int -> Element -> DB m Doc
@@ -203,8 +203,10 @@ blockToJATS opts (Div (ident,_,kvs) bs) = do
203203 [(k,v) | (k,v) <- kvs, k `elem` ["specific-use",
204204 "content-type", "orientation", "position"]]
205205 return $ inTags True "boxed-text" attr contents
206−blockToJATS _ (Header _ _ _) =
207− return empty -- should not occur after hierarchicalize
206+blockToJATS _ h@(Header _ _ _) = do
207+ -- should not occur after hierarchicalize, except inside lists/blockquotes
208+ report $ BlockNotRendered h
209+ return empty
208210-- No Plain, everything needs to be in a block-level tag
209211blockToJATS opts (Plain lst) = blockToJATS opts (Para lst)
210212-- title beginning with fig: indicates that the image is a figure
src/Text/Pandoc/Writers/LaTeX.hs+134−112
@@ -35,16 +35,17 @@ module Text.Pandoc.Writers.LaTeX (
3535 , writeBeamer
3636 ) where
3737import Control.Applicative ((<|>))
38−import Control.Monad.State
38+import Control.Monad.State.Strict
3939import Data.Aeson (FromJSON, object, (.=))
4040import Data.Char (isAlphaNum, isAscii, isDigit, isLetter, isPunctuation, ord,
4141 toLower)
42−import Data.List (foldl', intercalate, intersperse, isInfixOf, nub, nubBy,
42+import Data.List (foldl', intercalate, intersperse, isInfixOf, nubBy,
4343 stripPrefix, (\\))
4444import Data.Maybe (catMaybes, fromMaybe, isJust)
4545import Data.Text (Text)
4646import qualified Data.Text as T
4747import Network.URI (unEscapeString)
48+import Text.Pandoc.BCP47 (Lang(..), toLang, getLang, renderLang)
4849import Text.Pandoc.Class (PandocMonad, report)
4950import Text.Pandoc.Definition
5051import Text.Pandoc.Highlighting (formatLaTeXBlock, formatLaTeXInline, highlight,
@@ -188,7 +189,8 @@ pandocToLaTeX options (Pandoc meta blocks) = do
188189 st <- get
189190 titleMeta <- stringToLaTeX TextString $ stringify $ docTitle meta
190191 authorsMeta <- mapM (stringToLaTeX TextString . stringify) $ docAuthors meta
191− let docLangs = nub $ query (extract "lang") blocks
192+ docLangs <- catMaybes <$>
193+ mapM (toLang . Just) (ordNub (query (extract "lang") blocks))
192194 let hasStringValue x = isJust (getField x metadata :: Maybe String)
193195 let geometryFromMargins = intercalate [','] $ catMaybes $
194196 map (\(x,y) ->
@@ -198,6 +200,18 @@ pandocToLaTeX options (Pandoc meta blocks) = do
198200 ,("tmargin","margin-top")
199201 ,("bmargin","margin-bottom")
200202 ]
203+ let toPolyObj lang = object [ "name" .= T.pack name
204+ , "options" .= T.pack opts ]
205+ where
206+ (name, opts) = toPolyglossia lang
207+ mblang <- toLang $ case getLang options meta of
208+ Just l -> Just l
209+ Nothing | null docLangs -> Nothing
210+ | otherwise -> Just "en"
211+ -- we need a default here since lang is used in template conditionals
212+
213+ let dirs = query (extract "dir") blocks
214+
201215 let context = defField "toc" (writerTableOfContents options) $
202216 defField "toc-depth" (show (writerTOCDepth options -
203217 if stBook st
@@ -235,26 +249,24 @@ pandocToLaTeX options (Pandoc meta blocks) = do
235249 Biblatex -> defField "biblio-title" biblioTitle .
236250 defField "biblatex" True
237251 _ -> id) $
238− -- set lang to something so polyglossia/babel is included
239− defField "lang" (if null docLangs then ""::String else "en") $
240− defField "otherlangs" docLangs $
241252 defField "colorlinks" (any hasStringValue
242253 ["citecolor", "urlcolor", "linkcolor", "toccolor"]) $
243− defField "dir" (if (null $ query (extract "dir") blocks)
244− then ""::String
245− else "ltr") $
254+ (if null dirs
255+ then id
256+ else defField "dir" ("ltr" :: String)) $
246257 defField "section-titles" True $
247258 defField "geometry" geometryFromMargins $
259+ (case getField "papersize" metadata of
260+ Just ("A4" :: String) -> resetField "papersize"
261+ ("a4" :: String)
262+ _ -> id) $
248263 metadata
249− let toPolyObj lang = object [ "name" .= T.pack name
250− , "options" .= T.pack opts ]
251− where
252− (name, opts) = toPolyglossia lang
253− let lang = maybe [] (splitBy (=='-')) $ getField "lang" context
254− otherlangs = maybe [] (map $ splitBy (=='-')) $ getField "otherlangs" context
255264 let context' =
256− defField "babel-lang" (toBabel lang)
257− $ defField "babel-otherlangs" (map toBabel otherlangs)
265+ -- note: lang is used in some conditionals in the template,
266+ -- so we need to set it if we have any babel/polyglossia:
267+ maybe id (defField "lang" . renderLang) mblang
268+ $ maybe id (defField "babel-lang" . toBabel) mblang
269+ $ defField "babel-otherlangs" (map toBabel docLangs)
258270 $ defField "babel-newcommands" (concatMap (\(poly, babel) ->
259271 -- \textspanish and \textgalician are already used by babel
260272 -- save them as \oritext... and let babel use that
@@ -274,20 +286,16 @@ pandocToLaTeX options (Pandoc meta blocks) = do
274286 -- eliminate duplicates that have same polyglossia name
275287 $ nubBy (\a b -> fst a == fst b)
276288 -- find polyglossia and babel names of languages used in the document
277− $ map (\l ->
278− let lng = splitBy (=='-') l
279− in (fst $ toPolyglossia lng, toBabel lng)
280− )
281− docLangs )
282− $ defField "polyglossia-lang" (toPolyObj lang)
283− $ defField "polyglossia-otherlangs" (map toPolyObj otherlangs)
284− $ defField "latex-dir-rtl" (case (getField "dir" context)::Maybe String of
285− Just "rtl" -> True
286− _ -> False)
289+ $ map (\l -> (fst $ toPolyglossia l, toBabel l)) docLangs
290+ )
291+ $ maybe id (defField "polyglossia-lang" . toPolyObj) mblang
292+ $ defField "polyglossia-otherlangs" (map toPolyObj docLangs)
293+ $ defField "latex-dir-rtl"
294+ (getField "dir" context == Just ("rtl" :: String))
287295 $ context
288− return $ case writerTemplate options of
289− Nothing -> main
290− Just tpl -> renderTemplate' tpl context'
296+ case writerTemplate options of
297+ Nothing -> return main
298+ Just tpl -> renderTemplate' tpl context'
291299
292300-- | Convert Elements to LaTeX
293301elementToLaTeX :: PandocMonad m => WriterOptions -> Element -> LW m Doc
@@ -443,11 +451,12 @@ blockToLaTeX (Div (identifier,classes,kvs) bs) = do
443451 -> "\\leavevmode" <> linkAnchor' <> "%"
444452 _ -> linkAnchor'
445453 let align dir txt = inCmd "begin" dir $$ txt $$ inCmd "end" dir
454+ lang <- toLang $ lookup "lang" kvs
446455 let wrapDir = case lookup "dir" kvs of
447456 Just "rtl" -> align "RTL"
448457 Just "ltr" -> align "LTR"
449458 _ -> id
450− wrapLang txt = case lookup "lang" kvs of
459+ wrapLang txt = case lang of
451460 Just lng -> let (l, o) = toPolyglossiaEnv lng
452461 ops = if null o
453462 then ""
@@ -647,23 +656,25 @@ blockToLaTeX (Header level (id',classes,_) lst) = do
647656 modify $ \s -> s{stInHeading = False}
648657 return hdr
649658blockToLaTeX (Table caption aligns widths heads rows) = do
650− headers <- if all null heads
651− then return empty
652− else do
653− contents <- (tableRowToLaTeX True aligns widths) heads
654− return ("\\toprule" $$ contents $$ "\\midrule")
655− let endhead = if all null heads
656− then empty
657− else text "\\endhead"
658− let endfirsthead = if all null heads
659− then empty
660− else text "\\endfirsthead"
659+ let toHeaders hs = do contents <- (tableRowToLaTeX True aligns widths) hs
660+ return ("\\toprule" $$ contents $$ "\\midrule")
661+ let removeNote (Note _) = Span ("", [], []) []
662+ removeNote x = x
661663 captionText <- inlineListToLaTeX caption
664+ firsthead <- if isEmpty captionText || all null heads
665+ then return empty
666+ else ($$ text "\\endfirsthead") <$> toHeaders heads
667+ head' <- if all null heads
668+ then return empty
669+ -- avoid duplicate notes in head and firsthead:
670+ else ($$ text "\\endhead") <$>
671+ toHeaders (if isEmpty firsthead
672+ then heads
673+ else walk removeNote heads)
662674 let capt = if isEmpty captionText
663675 then empty
664− else text "\\caption" <> braces captionText <> "\\tabularnewline"
665− $$ headers
666− $$ endfirsthead
676+ else text "\\caption" <>
677+ braces captionText <> "\\tabularnewline"
667678 rows' <- mapM (tableRowToLaTeX False aligns widths) rows
668679 let colDescriptors = text $ concat $ map toColDescriptor aligns
669680 modify $ \s -> s{ stTable = True }
@@ -671,9 +682,9 @@ blockToLaTeX (Table caption aligns widths heads rows) = do
671682 braces ("@{}" <> colDescriptors <> "@{}")
672683 -- the @{} removes extra space at beginning and end
673684 $$ capt
685+ $$ firsthead
674686 $$ (if all null heads then "\\toprule" else empty)
675− $$ headers
676− $$ endhead
687+ $$ head'
677688 $$ vcat rows'
678689 $$ "\\bottomrule"
679690 $$ "\\end{longtable}"
@@ -916,13 +927,14 @@ inlineToLaTeX :: PandocMonad m
916927 -> LW m Doc
917928inlineToLaTeX (Span (id',classes,kvs) ils) = do
918929 linkAnchor <- hypertarget False id' empty
930+ lang <- toLang $ lookup "lang" kvs
919931 let cmds = ["textup" | "csl-no-emph" `elem` classes] ++
920932 ["textnormal" | "csl-no-strong" `elem` classes ||
921933 "csl-no-smallcaps" `elem` classes] ++
922934 ["RL" | ("dir", "rtl") `elem` kvs] ++
923935 ["LR" | ("dir", "ltr") `elem` kvs] ++
924− (case lookup "lang" kvs of
925− Just lng -> let (l, o) = toPolyglossia $ splitBy (=='-') lng
936+ (case lang of
937+ Just lng -> let (l, o) = toPolyglossia lng
926938 ops = if null o then "" else ("[" ++ o ++ "]")
927939 in ["text" ++ l ++ ops]
928940 Nothing -> [])
@@ -1237,9 +1249,9 @@ mbBraced x = if not (all isAlphaNum x)
12371249-- Extract a key from divs and spans
12381250extract :: String -> Block -> [String]
12391251extract key (Div attr _) = lookKey key attr
1240−extract key (Plain ils) = concatMap (extractInline key) ils
1241−extract key (Para ils) = concatMap (extractInline key) ils
1242−extract key (Header _ _ ils) = concatMap (extractInline key) ils
1252+extract key (Plain ils) = query (extractInline key) ils
1253+extract key (Para ils) = query (extractInline key) ils
1254+extract key (Header _ _ ils) = query (extractInline key) ils
12431255extract _ _ = []
12441256
12451257-- Extract a key from spans
@@ -1252,85 +1264,95 @@ lookKey :: String -> Attr -> [String]
12521264lookKey key (_,_,kvs) = maybe [] words $ lookup key kvs
12531265
12541266-- In environments \Arabic instead of \arabic is used
1255−toPolyglossiaEnv :: String -> (String, String)
1267+toPolyglossiaEnv :: Lang -> (String, String)
12561268toPolyglossiaEnv l =
1257− case toPolyglossia $ (splitBy (=='-')) l of
1269+ case toPolyglossia l of
12581270 ("arabic", o) -> ("Arabic", o)
12591271 x -> x
12601272
12611273-- Takes a list of the constituents of a BCP 47 language code and
12621274-- converts it to a Polyglossia (language, options) tuple
12631275-- http://mirrors.ctan.org/macros/latex/contrib/polyglossia/polyglossia.pdf
1264−toPolyglossia :: [String] -> (String, String)
1265−toPolyglossia ("ar":"DZ":_) = ("arabic", "locale=algeria")
1266−toPolyglossia ("ar":"IQ":_) = ("arabic", "locale=mashriq")
1267−toPolyglossia ("ar":"JO":_) = ("arabic", "locale=mashriq")
1268−toPolyglossia ("ar":"LB":_) = ("arabic", "locale=mashriq")
1269−toPolyglossia ("ar":"LY":_) = ("arabic", "locale=libya")
1270−toPolyglossia ("ar":"MA":_) = ("arabic", "locale=morocco")
1271−toPolyglossia ("ar":"MR":_) = ("arabic", "locale=mauritania")
1272−toPolyglossia ("ar":"PS":_) = ("arabic", "locale=mashriq")
1273−toPolyglossia ("ar":"SY":_) = ("arabic", "locale=mashriq")
1274−toPolyglossia ("ar":"TN":_) = ("arabic", "locale=tunisia")
1275−toPolyglossia ("de":"1901":_) = ("german", "spelling=old")
1276−toPolyglossia ("de":"AT":"1901":_) = ("german", "variant=austrian, spelling=old")
1277−toPolyglossia ("de":"AT":_) = ("german", "variant=austrian")
1278−toPolyglossia ("de":"CH":"1901":_) = ("german", "variant=swiss, spelling=old")
1279−toPolyglossia ("de":"CH":_) = ("german", "variant=swiss")
1280−toPolyglossia ("de":_) = ("german", "")
1281−toPolyglossia ("dsb":_) = ("lsorbian", "")
1282−toPolyglossia ("el":"polyton":_) = ("greek", "variant=poly")
1283−toPolyglossia ("en":"AU":_) = ("english", "variant=australian")
1284−toPolyglossia ("en":"CA":_) = ("english", "variant=canadian")
1285−toPolyglossia ("en":"GB":_) = ("english", "variant=british")
1286−toPolyglossia ("en":"NZ":_) = ("english", "variant=newzealand")
1287−toPolyglossia ("en":"UK":_) = ("english", "variant=british")
1288−toPolyglossia ("en":"US":_) = ("english", "variant=american")
1289−toPolyglossia ("grc":_) = ("greek", "variant=ancient")
1290−toPolyglossia ("hsb":_) = ("usorbian", "")
1291−toPolyglossia ("la":"x":"classic":_) = ("latin", "variant=classic")
1292−toPolyglossia ("sl":_) = ("slovenian", "")
1293−toPolyglossia x = (commonFromBcp47 x, "")
1276+toPolyglossia :: Lang -> (String, String)
1277+toPolyglossia (Lang "ar" _ "DZ" _) = ("arabic", "locale=algeria")
1278+toPolyglossia (Lang "ar" _ "IQ" _) = ("arabic", "locale=mashriq")
1279+toPolyglossia (Lang "ar" _ "JO" _) = ("arabic", "locale=mashriq")
1280+toPolyglossia (Lang "ar" _ "LB" _) = ("arabic", "locale=mashriq")
1281+toPolyglossia (Lang "ar" _ "LY" _) = ("arabic", "locale=libya")
1282+toPolyglossia (Lang "ar" _ "MA" _) = ("arabic", "locale=morocco")
1283+toPolyglossia (Lang "ar" _ "MR" _) = ("arabic", "locale=mauritania")
1284+toPolyglossia (Lang "ar" _ "PS" _) = ("arabic", "locale=mashriq")
1285+toPolyglossia (Lang "ar" _ "SY" _) = ("arabic", "locale=mashriq")
1286+toPolyglossia (Lang "ar" _ "TN" _) = ("arabic", "locale=tunisia")
1287+toPolyglossia (Lang "de" _ _ vars)
1288+ | "1901" `elem` vars = ("german", "spelling=old")
1289+toPolyglossia (Lang "de" _ "AT" vars)
1290+ | "1901" `elem` vars = ("german", "variant=austrian, spelling=old")
1291+toPolyglossia (Lang "de" _ "AT" _) = ("german", "variant=austrian")
1292+toPolyglossia (Lang "de" _ "CH" vars)
1293+ | "1901" `elem` vars = ("german", "variant=swiss, spelling=old")
1294+toPolyglossia (Lang "de" _ "CH" _) = ("german", "variant=swiss")
1295+toPolyglossia (Lang "de" _ _ _) = ("german", "")
1296+toPolyglossia (Lang "dsb" _ _ _) = ("lsorbian", "")
1297+toPolyglossia (Lang "el" _ "polyton" _) = ("greek", "variant=poly")
1298+toPolyglossia (Lang "en" _ "AU" _) = ("english", "variant=australian")
1299+toPolyglossia (Lang "en" _ "CA" _) = ("english", "variant=canadian")
1300+toPolyglossia (Lang "en" _ "GB" _) = ("english", "variant=british")
1301+toPolyglossia (Lang "en" _ "NZ" _) = ("english", "variant=newzealand")
1302+toPolyglossia (Lang "en" _ "UK" _) = ("english", "variant=british")
1303+toPolyglossia (Lang "en" _ "US" _) = ("english", "variant=american")
1304+toPolyglossia (Lang "grc" _ _ _) = ("greek", "variant=ancient")
1305+toPolyglossia (Lang "hsb" _ _ _) = ("usorbian", "")
1306+toPolyglossia (Lang "la" _ _ vars)
1307+ | "x-classic" `elem` vars = ("latin", "variant=classic")
1308+toPolyglossia (Lang "sl" _ _ _) = ("slovenian", "")
1309+toPolyglossia x = (commonFromBcp47 x, "")
12941310
12951311-- Takes a list of the constituents of a BCP 47 language code and
12961312-- converts it to a Babel language string.
12971313-- http://mirrors.ctan.org/macros/latex/required/babel/base/babel.pdf
12981314-- List of supported languages (slightly outdated):
12991315-- http://tug.ctan.org/language/hyph-utf8/doc/generic/hyph-utf8/hyphenation.pdf
1300−toBabel :: [String] -> String
1301−toBabel ("de":"1901":_) = "german"
1302−toBabel ("de":"AT":"1901":_) = "austrian"
1303−toBabel ("de":"AT":_) = "naustrian"
1304−toBabel ("de":"CH":"1901":_) = "swissgerman"
1305−toBabel ("de":"CH":_) = "nswissgerman"
1306−toBabel ("de":_) = "ngerman"
1307−toBabel ("dsb":_) = "lowersorbian"
1308−toBabel ("el":"polyton":_) = "polutonikogreek"
1309−toBabel ("en":"AU":_) = "australian"
1310−toBabel ("en":"CA":_) = "canadian"
1311−toBabel ("en":"GB":_) = "british"
1312−toBabel ("en":"NZ":_) = "newzealand"
1313−toBabel ("en":"UK":_) = "british"
1314−toBabel ("en":"US":_) = "american"
1315−toBabel ("fr":"CA":_) = "canadien"
1316−toBabel ("fra":"aca":_) = "acadian"
1317−toBabel ("grc":_) = "polutonikogreek"
1318−toBabel ("hsb":_) = "uppersorbian"
1319−toBabel ("la":"x":"classic":_) = "classiclatin"
1320−toBabel ("sl":_) = "slovene"
1321−toBabel x = commonFromBcp47 x
1316+toBabel :: Lang -> String
1317+toBabel (Lang "de" _ "AT" vars)
1318+ | "1901" `elem` vars = "austrian"
1319+ | otherwise = "naustrian"
1320+toBabel (Lang "de" _ "CH" vars)
1321+ | "1901" `elem` vars = "swissgerman"
1322+ | otherwise = "nswissgerman"
1323+toBabel (Lang "de" _ _ vars)
1324+ | "1901" `elem` vars = "german"
1325+ | otherwise = "ngerman"
1326+toBabel (Lang "dsb" _ _ _) = "lowersorbian"
1327+toBabel (Lang "el" _ _ vars)
1328+ | "polyton" `elem` vars = "polutonikogreek"
1329+toBabel (Lang "en" _ "AU" _) = "australian"
1330+toBabel (Lang "en" _ "CA" _) = "canadian"
1331+toBabel (Lang "en" _ "GB" _) = "british"
1332+toBabel (Lang "en" _ "NZ" _) = "newzealand"
1333+toBabel (Lang "en" _ "UK" _) = "british"
1334+toBabel (Lang "en" _ "US" _) = "american"
1335+toBabel (Lang "fr" _ "CA" _) = "canadien"
1336+toBabel (Lang "fra" _ _ vars)
1337+ | "aca" `elem` vars = "acadian"
1338+toBabel (Lang "grc" _ _ _) = "polutonikogreek"
1339+toBabel (Lang "hsb" _ _ _) = "uppersorbian"
1340+toBabel (Lang "la" _ _ vars)
1341+ | "x-classic" `elem` vars = "classiclatin"
1342+toBabel (Lang "sl" _ _ _) = "slovene"
1343+toBabel x = commonFromBcp47 x
13221344
13231345-- Takes a list of the constituents of a BCP 47 language code
13241346-- and converts it to a string shared by Babel and Polyglossia.
13251347-- https://tools.ietf.org/html/bcp47#section-2.1
1326−commonFromBcp47 :: [String] -> String
1327−commonFromBcp47 [] = ""
1328−commonFromBcp47 ("pt":"BR":_) = "brazil"
1348+commonFromBcp47 :: Lang -> String
1349+commonFromBcp47 (Lang "pt" _ "BR" _) = "brazil"
13291350-- Note: documentation says "brazilian" works too, but it doesn't seem to work
13301351-- on some systems. See #2953.
1331−commonFromBcp47 ("sr":"Cyrl":_) = "serbianc"
1332−commonFromBcp47 ("zh":"Latn":"pinyin":_) = "pinyin"
1333−commonFromBcp47 x = fromIso $ head x
1352+commonFromBcp47 (Lang "sr" "Cyrl" _ _) = "serbianc"
1353+commonFromBcp47 (Lang "zh" "Latn" _ vars)
1354+ | "pinyin" `elem` vars = "pinyin"
1355+commonFromBcp47 (Lang l _ _ _) = fromIso l
13341356 where
13351357 fromIso "af" = "afrikaans"
13361358 fromIso "am" = "amharic"
src/Text/Pandoc/Writers/Man.hs+2−2
@@ -31,7 +31,7 @@ Conversion of 'Pandoc' documents to groff man page format.
3131-}
3232module Text.Pandoc.Writers.Man ( writeMan) where
3333import Control.Monad.Except (throwError)
34−import Control.Monad.State
34+import Control.Monad.State.Strict
3535import Data.List (intercalate, intersperse, stripPrefix, sort)
3636import qualified Data.Map as Map
3737import Data.Maybe (fromMaybe)
@@ -110,7 +110,7 @@ pandocToMan opts (Pandoc meta blocks) = do
110110 $ metadata
111111 case writerTemplate opts of
112112 Nothing -> return main
113− Just tpl -> return $ renderTemplate' tpl context
113+ Just tpl -> renderTemplate' tpl context
114114
115115-- | Return man representation of notes.
116116notesToMan :: PandocMonad m => WriterOptions -> [[Block]] -> StateT WriterState m Doc
src/Text/Pandoc/Writers/Markdown.hs+21−10
@@ -35,7 +35,7 @@ Markdown: <http://daringfireball.net/projects/markdown/>
3535-}
3636module Text.Pandoc.Writers.Markdown (writeMarkdown, writePlain) where
3737import Control.Monad.Reader
38−import Control.Monad.State
38+import Control.Monad.State.Strict
3939import Data.Char (chr, isPunctuation, isSpace, ord)
4040import Data.Default
4141import qualified Data.HashMap.Strict as H
@@ -209,8 +209,8 @@ pandocToMarkdown opts (Pandoc meta blocks) = do
209209 Nothing -> empty
210210 let headerBlocks = filter isHeaderBlock blocks
211211 toc <- if writerTableOfContents opts
212− then tableOfContents opts headerBlocks
213− else return empty
212+ then render' <$> tableOfContents opts headerBlocks
213+ else return ""
214214 -- Strip off final 'references' header if markdown citations enabled
215215 let blocks' = if isEnabled Ext_citations opts
216216 then case reverse blocks of
@@ -220,7 +220,11 @@ pandocToMarkdown opts (Pandoc meta blocks) = do
220220 body <- blockListToMarkdown opts blocks'
221221 notesAndRefs' <- notesAndRefs opts
222222 let main = render' $ body <> notesAndRefs'
223− let context = defField "toc" (render' toc)
223+ let context = -- for backwards compatibility we populate toc
224+ -- with the contents of the toc, rather than a
225+ -- boolean:
226+ defField "toc" toc
227+ $ defField "table-of-contents" toc
224228 $ defField "body" main
225229 $ (if isNullMeta meta
226230 then id
@@ -228,7 +232,7 @@ pandocToMarkdown opts (Pandoc meta blocks) = do
228232 $ addVariablesToJSON opts metadata
229233 case writerTemplate opts of
230234 Nothing -> return main
231− Just tpl -> return $ renderTemplate' tpl context
235+ Just tpl -> renderTemplate' tpl context
232236
233237-- | Return markdown representation of reference key table.
234238refsToMarkdown :: PandocMonad m => WriterOptions -> Refs -> MD m Doc
@@ -412,6 +416,9 @@ blockToMarkdown' opts (Plain inlines) = do
412416 '+':s:_ | not isPlain && isSpace s -> "\\" <> contents
413417 '*':s:_ | not isPlain && isSpace s -> "\\" <> contents
414418 '-':s:_ | not isPlain && isSpace s -> "\\" <> contents
419+ '+':[] | not isPlain -> "\\" <> contents
420+ '*':[] | not isPlain -> "\\" <> contents
421+ '-':[] | not isPlain -> "\\" <> contents
415422 '|':_ | (isEnabled Ext_line_blocks opts ||
416423 isEnabled Ext_pipe_tables opts)
417424 && isEnabled Ext_all_symbols_escapable opts
@@ -433,8 +440,10 @@ blockToMarkdown' opts (LineBlock lns) =
433440 return $ (vcat $ map (hang 2 (text "| ")) mdLines) <> blankline
434441 else blockToMarkdown opts $ linesToPara lns
435442blockToMarkdown' opts b@(RawBlock f str)
436− | f == "markdown" = return $ text str <> text "\n"
437− | f == "html" && isEnabled Ext_raw_html opts = do
443+ | f `elem` ["markdown", "markdown_github", "markdown_phpextra",
444+ "markdown_mmd", "markdown_strict"]
445+ = return $ text str <> text "\n"
446+ | f `elem` ["html", "html5", "html4"] && isEnabled Ext_raw_html opts = do
438447 plain <- asks envPlain
439448 return $ if plain
440449 then empty
@@ -1053,10 +1062,12 @@ inlineToMarkdown opts (Math DisplayMath str) =
10531062 (texMathToInlines DisplayMath str >>= inlineListToMarkdown opts)
10541063inlineToMarkdown opts il@(RawInline f str) = do
10551064 plain <- asks envPlain
1056− if not plain &&
1057− ( f == "markdown" ||
1065+ if (plain && f == "plain") || (not plain &&
1066+ ( f `elem` ["markdown", "markdown_github", "markdown_phpextra",
1067+ "markdown_mmd", "markdown_strict"] ||
10581068 (isEnabled Ext_raw_tex opts && (f == "latex" || f == "tex")) ||
1059− (isEnabled Ext_raw_html opts && f == "html") )
1069+ (isEnabled Ext_raw_html opts && f `elem` ["html", "html4", "html5"])
1070+ ))
10601071 then return $ text str
10611072 else do
10621073 report $ InlineNotRendered il
src/Text/Pandoc/Writers/Math.hs+7
@@ -1,6 +1,8 @@
11module Text.Pandoc.Writers.Math
22 ( texMathToInlines
33 , convertMath
4+ , defaultMathJaxURL
5+ , defaultKaTeXURL
46 )
57where
68
@@ -47,3 +49,8 @@ convertMath writer mt str = do
4749 DisplayMath -> DisplayBlock
4850 InlineMath -> DisplayInline
4951
52+defaultMathJaxURL :: String
53+defaultMathJaxURL = "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/"
54+
55+defaultKaTeXURL :: String
56+defaultKaTeXURL = "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/"
src/Text/Pandoc/Writers/MediaWiki.hs+3−4
@@ -31,7 +31,7 @@ MediaWiki: <http://www.mediawiki.org/wiki/MediaWiki>
3131-}
3232module Text.Pandoc.Writers.MediaWiki ( writeMediaWiki ) where
3333import Control.Monad.Reader
34−import Control.Monad.State
34+import Control.Monad.State.Strict
3535import Data.List (intercalate)
3636import qualified Data.Set as Set
3737import Data.Text (Text, pack)
@@ -82,9 +82,8 @@ pandocToMediaWiki (Pandoc meta blocks) = do
8282 let main = body ++ notes
8383 let context = defField "body" main
8484 $ defField "toc" (writerTableOfContents opts) metadata
85− return $ pack
86− $ case writerTemplate opts of
87− Nothing -> main
85+ pack <$> case writerTemplate opts of
86+ Nothing -> return main
8887 Just tpl -> renderTemplate' tpl context
8988
9089-- | Escape special characters for MediaWiki.
src/Text/Pandoc/Writers/Ms.hs+4−3
@@ -52,7 +52,7 @@ import Text.Pandoc.Pretty
5252import Text.Pandoc.Class (PandocMonad, report)
5353import Text.Pandoc.ImageSize
5454import Text.Pandoc.Logging
55−import Control.Monad.State
55+import Control.Monad.State.Strict
5656import Data.Char ( isLower, isUpper, toUpper )
5757import Text.TeXMath (writeEqn)
5858import System.FilePath (takeExtension)
@@ -125,7 +125,7 @@ pandocToMs opts (Pandoc meta blocks) = do
125125 $ metadata
126126 case writerTemplate opts of
127127 Nothing -> return main
128− Just tpl -> return $ renderTemplate' tpl context
128+ Just tpl -> renderTemplate' tpl context
129129
130130-- | Association list of characters to escape.
131131msEscapes :: Map.Map Char String
@@ -411,7 +411,8 @@ definitionListItemToMs opts (label, defs) = do
411411 let (first, rest) = case blocks of
412412 ((Para x):y) -> (Plain x,y)
413413 (x:y) -> (x,y)
414− [] -> error "blocks is null"
414+ [] -> (Plain [], [])
415+ -- should not happen
415416 rest' <- liftM vcat $
416417 mapM (\item -> blockToMs opts item) rest
417418 first' <- blockToMs opts first
src/Text/Pandoc/Writers/Muse.hs+26−23
@@ -42,7 +42,7 @@ However, @\<literal style="html">@ tag is used for HTML raw blocks
4242even though it is supported only in Emacs Muse.
4343-}
4444module Text.Pandoc.Writers.Muse (writeMuse) where
45−import Control.Monad.State
45+import Control.Monad.State.Strict
4646import Data.Text (Text)
4747import Data.List (intersperse, transpose, isInfixOf)
4848import System.FilePath (takeExtension)
@@ -97,11 +97,17 @@ pandocToMuse (Pandoc meta blocks) = do
9797 body <- blockListToMuse blocks
9898 notes <- liftM (reverse . stNotes) get >>= notesToMuse
9999 let main = render colwidth $ body $+$ notes
100− let context = defField "body" main
101− $ metadata
100+ let context = defField "body" main metadata
102101 case writerTemplate opts of
103102 Nothing -> return main
104− Just tpl -> return $ renderTemplate' tpl context
103+ Just tpl -> renderTemplate' tpl context
104+
105+-- | Convert list of Pandoc block elements to Muse
106+-- | without setting stTopLevel.
107+flatBlockListToMuse :: PandocMonad m
108+ => [Block] -- ^ List of block elements
109+ -> StateT WriterState m Doc
110+flatBlockListToMuse blocks = cat <$> mapM blockToMuse blocks
105111
106112-- | Convert list of Pandoc block elements to Muse.
107113blockListToMuse :: PandocMonad m
@@ -112,11 +118,11 @@ blockListToMuse blocks = do
112118 modify $ \s -> s { stTopLevel = not $ stInsideBlock s
113119 , stInsideBlock = True
114120 }
115− contents <- mapM blockToMuse blocks
121+ result <- flatBlockListToMuse blocks
116122 modify $ \s -> s { stTopLevel = stTopLevel oldState
117123 , stInsideBlock = stInsideBlock oldState
118124 }
119− return $ cat contents
125+ return result
120126
121127-- | Convert Pandoc block element to Muse.
122128blockToMuse :: PandocMonad m
@@ -129,23 +135,23 @@ blockToMuse (Para inlines) = do
129135blockToMuse (LineBlock lns) = do
130136 let splitStanza [] = []
131137 splitStanza xs = case break (== mempty) xs of
132− (l, []) -> l : []
138+ (l, []) -> [l]
133139 (l, _:r) -> l : splitStanza r
134140 let joinWithLinefeeds = nowrap . mconcat . intersperse cr
135141 let joinWithBlankLines = mconcat . intersperse blankline
136142 let prettyfyStanza ls = joinWithLinefeeds <$> mapM inlineListToMuse ls
137143 contents <- joinWithBlankLines <$> mapM prettyfyStanza (splitStanza lns)
138144 return $ blankline $$ "<verse>" $$ contents $$ "</verse>" <> blankline
139−blockToMuse (CodeBlock (_,_,_) str) = do
145+blockToMuse (CodeBlock (_,_,_) str) =
140146 return $ "<example>" $$ text str $$ "</example>" $$ blankline
141147blockToMuse (RawBlock (Format format) str) =
142148 return $ blankline $$ "<literal style=\"" <> text format <> "\">" $$
143149 text str $$ "</literal>" $$ blankline
144150blockToMuse (BlockQuote blocks) = do
145− contents <- blockListToMuse blocks
151+ contents <- flatBlockListToMuse blocks
146152 return $ blankline
147153 <> "<quote>"
148− $$ flush contents -- flush to drop blanklines
154+ $$ nest 0 contents -- nest 0 to remove trailing blank lines
149155 $$ "</quote>"
150156 <> blankline
151157blockToMuse (OrderedList (start, style, _) items) = do
@@ -154,11 +160,10 @@ blockToMuse (OrderedList (start, style, _) items) = do
154160 let maxMarkerLength = maximum $ map length markers
155161 let markers' = map (\m -> let s = maxMarkerLength - length m
156162 in m ++ replicate s ' ') markers
157− contents <- mapM (\(item, num) -> orderedListItemToMuse item num) $
158− zip markers' items
163+ contents <- zipWithM orderedListItemToMuse markers' items
159164 -- ensure that sublists have preceding blank line
160165 topLevel <- gets stTopLevel
161− return $ cr $$ ((if topLevel then nest 1 else id) $ vcat contents) $$ blankline
166+ return $ cr $$ (if topLevel then nest 1 else id) (vcat contents) $$ blankline
162167 where orderedListItemToMuse :: PandocMonad m
163168 => String -- ^ marker for list item
164169 -> [Block] -- ^ list item (list of blocks)
@@ -170,7 +175,7 @@ blockToMuse (BulletList items) = do
170175 contents <- mapM bulletListItemToMuse items
171176 -- ensure that sublists have preceding blank line
172177 topLevel <- gets stTopLevel
173− return $ cr $$ ((if topLevel then nest 1 else id) $ vcat contents) $$ blankline
178+ return $ cr $$ (if topLevel then nest 1 else id) (vcat contents) $$ blankline
174179 where bulletListItemToMuse :: PandocMonad m
175180 => [Block]
176181 -> StateT WriterState m Doc
@@ -179,7 +184,7 @@ blockToMuse (BulletList items) = do
179184 return $ hang 2 "- " contents
180185blockToMuse (DefinitionList items) = do
181186 contents <- mapM definitionListItemToMuse items
182− return $ cr $$ (nest 1 $ vcat $ contents) $$ blankline
187+ return $ cr $$ nest 1 (vcat contents) $$ blankline
183188 where definitionListItemToMuse :: PandocMonad m
184189 => ([Inline], [[Block]])
185190 -> StateT WriterState m Doc
@@ -218,8 +223,8 @@ blockToMuse (Table caption _ _ headers rows) = do
218223 -- FIXME: Muse doesn't allow blocks with height more than 1.
219224 let hpipeBlocks sep blocks = hcat $ intersperse sep' blocks
220225 where h = maximum (1 : map height blocks)
221− sep' = lblock (length sep) $ vcat (map text $ replicate h sep)
222− let makeRow sep = (" " <>) . (hpipeBlocks sep . zipWith lblock widthsInChars)
226+ sep' = lblock (length sep) $ vcat (replicate h (text sep))
227+ let makeRow sep = (" " <>) . hpipeBlocks sep . zipWith lblock widthsInChars
223228 let head' = makeRow " || " headers'
224229 let rowSeparator = if noHeaders then " | " else " | "
225230 rows'' <- mapM (\row -> do cols <- mapM blockListToMuse row
@@ -236,9 +241,7 @@ blockToMuse Null = return empty
236241notesToMuse :: PandocMonad m
237242 => Notes
238243 -> StateT WriterState m Doc
239−notesToMuse notes =
240− mapM (\(num, note) -> noteToMuse num note) (zip [1..] notes) >>=
241− return . vsep
244+notesToMuse notes = liftM vsep (zipWithM noteToMuse [1 ..] notes)
242245
243246-- | Return Muse representation of a note.
244247noteToMuse :: PandocMonad m
@@ -268,7 +271,7 @@ conditionalEscapeString s
268271inlineListToMuse :: PandocMonad m
269272 => [Inline]
270273 -> StateT WriterState m Doc
271−inlineListToMuse lst = mapM inlineToMuse lst >>= return . hcat
274+inlineListToMuse lst = liftM hcat (mapM inlineToMuse lst)
272275
273276-- | Convert Pandoc inline element to Muse.
274277inlineToMuse :: PandocMonad m
@@ -316,7 +319,7 @@ inlineToMuse Space = return space
316319inlineToMuse SoftBreak = do
317320 wrapText <- gets $ writerWrapText . stOptions
318321 return $ if wrapText == WrapPreserve then cr else space
319−inlineToMuse (Link _ txt (src, _)) = do
322+inlineToMuse (Link _ txt (src, _)) =
320323 case txt of
321324 [Str x] | escapeURI x == src ->
322325 return $ "[[" <> text (escapeLink x) <> "]]"
@@ -340,7 +343,7 @@ inlineToMuse (Note contents) = do
340343 -- add to notes in state
341344 notes <- gets stNotes
342345 modify $ \st -> st { stNotes = contents:notes }
343− let ref = show $ (length notes) + 1
346+ let ref = show $ length notes + 1
344347 return $ "[" <> text ref <> "]"
345348inlineToMuse (Span (_,name:_,_) inlines) = do
346349 contents <- inlineListToMuse inlines
src/Text/Pandoc/Writers/ODT.hs+41−7
@@ -31,8 +31,9 @@ Conversion of 'Pandoc' documents to ODT.
3131module Text.Pandoc.Writers.ODT ( writeODT ) where
3232import Codec.Archive.Zip
3333import Control.Monad.Except (catchError)
34−import Control.Monad.State
34+import Control.Monad.State.Strict
3535import qualified Data.ByteString.Lazy as B
36+import Data.Generics (everywhere', mkT)
3637import Data.List (isPrefixOf)
3738import Data.Maybe (fromMaybe)
3839import qualified Data.Text.Lazy as TL
@@ -46,13 +47,14 @@ import Text.Pandoc.MIME (extensionFromMimeType, getMimeType)
4647import Text.Pandoc.Options (WrapOption (..), WriterOptions (..))
4748import Text.Pandoc.Pretty
4849import Text.Pandoc.Shared (stringify)
49−import Text.Pandoc.UTF8 (fromStringLazy, fromTextLazy)
50+import Text.Pandoc.UTF8 (fromStringLazy, fromTextLazy, toStringLazy)
5051import Text.Pandoc.Walk
5152import Text.Pandoc.Writers.OpenDocument (writeOpenDocument)
5253import Text.Pandoc.Writers.Shared (fixDisplayMath)
54+import Text.Pandoc.BCP47 (getLang, toLang, Lang(..), renderLang)
5355import Text.Pandoc.XML
5456import Text.TeXMath
55−import Text.XML.Light.Output
57+import Text.XML.Light
5658
5759data ODTState = ODTState { stEntries :: [Entry]
5860 }
@@ -78,6 +80,7 @@ pandocToODT :: PandocMonad m
7880pandocToODT opts doc@(Pandoc meta _) = do
7981 let datadir = writerUserDataDir opts
8082 let title = docTitle meta
83+ lang <- toLang (getLang opts meta)
8184 refArchive <-
8285 case writerReferenceDoc opts of
8386 Just f -> liftM toArchive $ lift $ P.readFileLazy f
@@ -132,18 +135,49 @@ pandocToODT opts doc@(Pandoc meta _) = do
132135 ,("xmlns:ooo","http://openoffice.org/2004/office")
133136 ,("xmlns:grddl","http://www.w3.org/2003/g/data-view#")
134137 ,("office:version","1.2")]
135− $ ( inTagsSimple "office:meta"
136− $ ( inTagsSimple "dc:title" (text $ escapeStringForXML (stringify title))
137− )
138+ $ ( inTagsSimple "office:meta" $
139+ ( inTagsSimple "dc:title"
140+ (text $ escapeStringForXML (stringify title))
141+ $$
142+ case lang of
143+ Just l -> inTagsSimple "dc:language"
144+ (text (escapeStringForXML (renderLang l)))
145+ Nothing -> empty
146+ )
138147 )
139148 )
140149 -- make sure mimetype is first
141150 let mimetypeEntry = toEntry "mimetype" epochtime
142151 $ fromStringLazy "application/vnd.oasis.opendocument.text"
143− let archive'' = addEntryToArchive mimetypeEntry
152+ archive'' <- updateStyleWithLang lang
153+ $ addEntryToArchive mimetypeEntry
144154 $ addEntryToArchive metaEntry archive'
145155 return $ fromArchive archive''
146156
157+updateStyleWithLang :: PandocMonad m => Maybe Lang -> Archive -> O m Archive
158+updateStyleWithLang Nothing arch = return arch
159+updateStyleWithLang (Just lang) arch = do
160+ epochtime <- floor `fmap` (lift P.getPOSIXTime)
161+ return arch{ zEntries = [if eRelativePath e == "styles.xml"
162+ then case parseXMLDoc
163+ (toStringLazy (fromEntry e)) of
164+ Nothing -> e
165+ Just d ->
166+ toEntry "styles.xml" epochtime
167+ ( fromStringLazy
168+ . ppTopElement
169+ . addLang lang $ d )
170+ else e
171+ | e <- zEntries arch] }
172+
173+addLang :: Lang -> Element -> Element
174+addLang lang = everywhere' (mkT updateLangAttr)
175+ where updateLangAttr (Attr n@(QName "language" _ (Just "fo")) _)
176+ = Attr n (langLanguage lang)
177+ updateLangAttr (Attr n@(QName "country" _ (Just "fo")) _)
178+ = Attr n (langRegion lang)
179+ updateLangAttr x = x
180+
147181-- | transform both Image and Math elements
148182transformPicMath :: PandocMonad m => WriterOptions ->Inline -> O m Inline
149183transformPicMath opts (Image attr@(id', cls, _) lab (src,t)) = catchError
src/Text/Pandoc/Writers/OPML.hs+3−3
@@ -60,9 +60,9 @@ writeOPML opts (Pandoc meta blocks) = do
6060 meta'
6161 main <- (render colwidth . vcat) <$> (mapM (elementToOPML opts) elements)
6262 let context = defField "body" main metadata
63− return $ case writerTemplate opts of
64− Nothing -> main
65− Just tpl -> renderTemplate' tpl context
63+ case writerTemplate opts of
64+ Nothing -> return main
65+ Just tpl -> renderTemplate' tpl context
6666
6767
6868writeHtmlInlines :: PandocMonad m => [Inline] -> m Text
src/Text/Pandoc/Writers/OpenDocument.hs+33−9
@@ -33,7 +33,7 @@ Conversion of 'Pandoc' documents to OpenDocument XML.
3333-}
3434module Text.Pandoc.Writers.OpenDocument ( writeOpenDocument ) where
3535import Control.Arrow ((***), (>>>))
36−import Control.Monad.State hiding (when)
36+import Control.Monad.State.Strict hiding (when)
3737import Data.Char (chr)
3838import Data.List (sortBy)
3939import Data.Text (Text)
@@ -50,6 +50,7 @@ import Text.Pandoc.Templates (renderTemplate')
5050import Text.Pandoc.Writers.Math
5151import Text.Pandoc.Writers.Shared
5252import Text.Pandoc.XML
53+import Text.Pandoc.BCP47 (parseBCP47, Lang(..))
5354import Text.Printf (printf)
5455
5556-- | Auxiliary function to convert Plain block to Para.
@@ -168,8 +169,8 @@ inTextStyle d = do
168169 inTags False "style:style"
169170 [("style:name", styleName)
170171 ,("style:family", "text")]
171− $ selfClosingTag "style:text-properties"
172− (concatMap textStyleAttr (Set.toList at)))
172+ $ selfClosingTag "style:text-properties"
173+ (concatMap textStyleAttr (Set.toList at)))
173174 return $ inTags False
174175 "text:span" [("text:style-name",styleName)] d
175176
@@ -219,11 +220,12 @@ writeOpenDocument opts (Pandoc meta blocks) = do
219220 let listStyles = map listStyle (stListStyles s)
220221 let automaticStyles = vcat $ reverse $ styles ++ listStyles
221222 let context = defField "body" body
223+ $ defField "toc" (writerTableOfContents opts)
222224 $ defField "automatic-styles" (render' automaticStyles)
223225 $ metadata
224− return $ case writerTemplate opts of
225− Nothing -> body
226− Just tpl -> renderTemplate' tpl context
226+ case writerTemplate opts of
227+ Nothing -> return body
228+ Just tpl -> renderTemplate' tpl context
227229
228230withParagraphStyle :: PandocMonad m
229231 => WriterOptions -> String -> [Block] -> OD m Doc
@@ -326,7 +328,8 @@ blockToOpenDocument o bs
326328 then return empty
327329 else inParagraphTags =<< inlinesToOpenDocument o b
328330 | LineBlock b <- bs = blockToOpenDocument o $ linesToPara b
329− | Div _ xs <- bs = blocksToOpenDocument o xs
331+ | Div attr xs <- bs = withLangFromAttr attr
332+ (blocksToOpenDocument o xs)
330333 | Header i _ b <- bs = setFirstPara >>
331334 (inHeaderTags i =<< inlinesToOpenDocument o b)
332335 | BlockQuote b <- bs = setFirstPara >> mkBlockQuote b
@@ -444,7 +447,7 @@ inlineToOpenDocument o ils
444447 | writerWrapText o == WrapPreserve
445448 -> return $ preformatted "\n"
446449 | otherwise -> return $ space
447− Span _ xs -> inlinesToOpenDocument o xs
450+ Span attr xs -> withLangFromAttr attr (inlinesToOpenDocument o xs)
448451 LineBreak -> return $ selfClosingTag "text:line-break" []
449452 Str s -> return $ handleSpaces $ escapeStringForXML s
450453 Emph l -> withTextStyle Italic $ inlinesToOpenDocument o l
@@ -606,7 +609,14 @@ paraTableStyles t s (a:xs)
606609 [ ("fo:text-align", x)
607610 , ("style:justify-single-word", "false")]
608611
609−data TextStyle = Italic | Bold | Strike | Sub | Sup | SmallC | Pre
612+data TextStyle = Italic
613+ | Bold
614+ | Strike
615+ | Sub
616+ | Sup
617+ | SmallC
618+ | Pre
619+ | Language Lang
610620 deriving ( Eq,Ord )
611621
612622textStyleAttr :: TextStyle -> [(String,String)]
@@ -624,4 +634,18 @@ textStyleAttr s
624634 | Pre <- s = [("style:font-name" ,"Courier New")
625635 ,("style:font-name-asian" ,"Courier New")
626636 ,("style:font-name-complex" ,"Courier New")]
637+ | Language lang <- s
638+ = [("fo:language" ,langLanguage lang)
639+ ,("fo:country" ,langRegion lang)]
627640 | otherwise = []
641+
642+withLangFromAttr :: PandocMonad m => Attr -> OD m a -> OD m a
643+withLangFromAttr (_,_,kvs) action =
644+ case lookup "lang" kvs of
645+ Nothing -> action
646+ Just l -> do
647+ case parseBCP47 l of
648+ Right lang -> withTextStyle (Language lang) action
649+ Left _ -> do
650+ report $ InvalidLang l
651+ action
src/Text/Pandoc/Writers/Org.hs+2−2
@@ -35,7 +35,7 @@ Conversion of 'Pandoc' documents to Emacs Org-Mode.
3535Org-Mode: <http://orgmode.org>
3636-}
3737module Text.Pandoc.Writers.Org (writeOrg) where
38−import Control.Monad.State
38+import Control.Monad.State.Strict
3939import Data.Char (isAlphaNum, toLower)
4040import Data.Text (Text)
4141import Data.List (intersect, intersperse, isPrefixOf, partition, transpose)
@@ -86,7 +86,7 @@ pandocToOrg (Pandoc meta blocks) = do
8686 $ metadata
8787 case writerTemplate opts of
8888 Nothing -> return main
89− Just tpl -> return $ renderTemplate' tpl context
89+ Just tpl -> renderTemplate' tpl context
9090
9191-- | Return Org representation of notes.
9292notesToOrg :: PandocMonad m => [[Block]] -> Org m Doc
src/Text/Pandoc/Writers/RST.hs+2−2
@@ -31,7 +31,7 @@ Conversion of 'Pandoc' documents to reStructuredText.
3131reStructuredText: <http://docutils.sourceforge.net/rst.html>
3232-}
3333module Text.Pandoc.Writers.RST ( writeRST ) where
34−import Control.Monad.State
34+import Control.Monad.State.Strict
3535import Data.Char (isSpace, toLower)
3636import Data.List (isPrefixOf, stripPrefix)
3737import Data.Maybe (fromMaybe)
@@ -108,7 +108,7 @@ pandocToRST (Pandoc meta blocks) = do
108108 $ metadata
109109 case writerTemplate opts of
110110 Nothing -> return main
111− Just tpl -> return $ renderTemplate' tpl context
111+ Just tpl -> renderTemplate' tpl context
112112 where
113113 normalizeHeadings lev (Header l a i:bs) =
114114 Header lev a i:normalizeHeadings (lev+1) cont ++ normalizeHeadings lev bs'
src/Text/Pandoc/Writers/RTF.hs+9−4
@@ -122,13 +122,18 @@ writeRTF options doc = do
122122 let context = defField "body" body
123123 $ defField "spacer" spacer
124124 $ (if writerTableOfContents options
125− then defField "toc" toc
125+ then defField "table-of-contents" toc
126+ -- for backwards compatibility,
127+ -- we populate toc with the contents
128+ -- of the toc rather than a boolean:
129+ . defField "toc" toc
126130 else id)
127131 $ metadata
128− return $ T.pack
129− $ case writerTemplate options of
132+ T.pack <$>
133+ case writerTemplate options of
130134 Just tpl -> renderTemplate' tpl context
131− Nothing -> case reverse body of
135+ Nothing -> return $
136+ case reverse body of
132137 ('\n':_) -> body
133138 _ -> body ++ "\n"
134139
src/Text/Pandoc/Writers/TEI.hs+7−5
@@ -85,7 +85,7 @@ writeTEI opts (Pandoc meta blocks) = do
8585 $ metadata
8686 case writerTemplate opts of
8787 Nothing -> return main
88− Just tpl -> return $ renderTemplate' tpl context
88+ Just tpl -> renderTemplate' tpl context
8989
9090-- | Convert an Element to TEI.
9191elementToTEI :: PandocMonad m => WriterOptions -> Int -> Element -> m Doc
@@ -159,11 +159,13 @@ blockToTEI opts (Div (ident,_,_) [Para lst]) = do
159159 let attribs = [("id", ident) | not (null ident)]
160160 inTags False "p" attribs <$> inlinesToTEI opts lst
161161blockToTEI opts (Div _ bs) = blocksToTEI opts $ map plainToPara bs
162−blockToTEI _ (Header _ _ _) = return empty
163−-- should not occur after hierarchicalize
162+blockToTEI _ h@(Header _ _ _) = do
163+ -- should not occur after hierarchicalize, except inside lists/blockquotes
164+ report $ BlockNotRendered h
165+ return empty
164166-- For TEI simple, text must be within containing block element, so
165−-- we use plainToPara to ensure that Plain text ends up contained by
166−-- something.
167+-- we use treat as Para to ensure that Plain text ends up contained by
168+-- something:
167169blockToTEI opts (Plain lst) = blockToTEI opts $ Para lst
168170-- title beginning with fig: indicates that the image is a figure
169171--blockToTEI opts (Para [Image attr txt (src,'f':'i':'g':':':_)]) =
src/Text/Pandoc/Writers/Texinfo.hs+2−2
@@ -32,7 +32,7 @@ Conversion of 'Pandoc' format into Texinfo.
3232-}
3333module Text.Pandoc.Writers.Texinfo ( writeTexinfo ) where
3434import Control.Monad.Except (throwError)
35−import Control.Monad.State
35+import Control.Monad.State.Strict
3636import Data.Char (chr, ord)
3737import Data.List (maximumBy, transpose)
3838import Data.Ord (comparing)
@@ -106,7 +106,7 @@ pandocToTexinfo options (Pandoc meta blocks) = do
106106 $ metadata
107107 case writerTemplate options of
108108 Nothing -> return body
109− Just tpl -> return $ renderTemplate' tpl context
109+ Just tpl -> renderTemplate' tpl context
110110
111111-- | Escape things as needed for Texinfo.
112112stringToTexinfo :: String -> String
src/Text/Pandoc/Writers/Textile.hs+2−2
@@ -30,7 +30,7 @@ Conversion of 'Pandoc' documents to Textile markup.
3030Textile: <http://thresholdstate.com/articles/4312/the-textile-reference-manual>
3131-}
3232module Text.Pandoc.Writers.Textile ( writeTextile ) where
33−import Control.Monad.State
33+import Control.Monad.State.Strict
3434import Data.Char (isSpace)
3535import Data.List (intercalate)
3636import Data.Text (Text, pack)
@@ -75,7 +75,7 @@ pandocToTextile opts (Pandoc meta blocks) = do
7575 let context = defField "body" main metadata
7676 case writerTemplate opts of
7777 Nothing -> return main
78− Just tpl -> return $ renderTemplate' tpl context
78+ Just tpl -> renderTemplate' tpl context
7979
8080withUseTags :: PandocMonad m => TW m a -> TW m a
8181withUseTags action = do
src/Text/Pandoc/Writers/ZimWiki.hs+2−2
@@ -33,7 +33,7 @@ http://zim-wiki.org/manual/Help/Wiki_Syntax.html
3333
3434module Text.Pandoc.Writers.ZimWiki ( writeZimWiki ) where
3535import Control.Monad (zipWithM)
36−import Control.Monad.State (StateT, evalStateT, gets, modify)
36+import Control.Monad.State.Strict (StateT, evalStateT, gets, modify)
3737import Data.Default (Default (..))
3838import Data.List (intercalate, isInfixOf, isPrefixOf, transpose)
3939import qualified Data.Map as Map
@@ -78,7 +78,7 @@ pandocToZimWiki opts (Pandoc meta blocks) = do
7878 $ defField "toc" (writerTableOfContents opts)
7979 $ metadata
8080 case writerTemplate opts of
81− Just tpl -> return $ renderTemplate' tpl context
81+ Just tpl -> renderTemplate' tpl context
8282 Nothing -> return main
8383
8484-- | Escape special characters for ZimWiki.
stack.pkg.yaml+1
@@ -19,4 +19,5 @@ packages:
1919extra-deps:
2020- hslua-0.5.0
2121- skylighting-0.3.3
22+- texmath-0.9.4.1
2223resolver: lts-8.16
stack.yaml+1
@@ -9,4 +9,5 @@ packages:
99extra-deps:
1010- hslua-0.5.0
1111- skylighting-0.3.3
12+- texmath-0.9.4.1
1213resolver: lts-8.16
test/Tests/Lua.hs+2−2
@@ -68,7 +68,7 @@ tests = map (localOption (QuickCheckTests 20))
6868
6969assertFilterConversion :: String -> FilePath -> Pandoc -> Pandoc -> Assertion
7070assertFilterConversion msg filterPath docIn docExpected = do
71− docRes <- runLuaFilter ("lua" </> filterPath) [] docIn
71+ docRes <- runLuaFilter Nothing ("lua" </> filterPath) [] docIn
7272 assertEqual msg docExpected docRes
7373
7474roundtripEqual :: (Eq a, Lua.StackValue a) => a -> IO Bool
@@ -78,7 +78,7 @@ roundtripEqual x = (x ==) <$> roundtripped
7878 roundtripped = do
7979 lua <- Lua.newstate
8080 Lua.openlibs lua
81− pushPandocModule lua
81+ pushPandocModule Nothing lua
8282 Lua.setglobal lua "pandoc"
8383 oldSize <- Lua.gettop lua
8484 Lua.push lua x
test/Tests/Old.hs+4
@@ -99,6 +99,10 @@ tests = [ testGroup "markdown"
9999 , test "reader" ["-r", "mediawiki", "-w", "native", "-s"]
100100 "mediawiki-reader.wiki" "mediawiki-reader.native"
101101 ]
102+ , testGroup "vimwiki"
103+ [ test "reader" ["-r", "vimwiki", "-w", "native", "-s"]
104+ "vimwiki-reader.wiki" "vimwiki-reader.native"
105+ ]
102106 , testGroup "dokuwiki"
103107 [ testGroup "writer" $ writerTests "dokuwiki"
104108 , test "inline_formatting" ["-r", "native", "-w", "dokuwiki", "-s"]
test/Tests/Readers/HTML.hs+6
@@ -30,4 +30,10 @@ tests = [ testGroup "base tag"
3030 [ test html "anchor without href" $ "<a name=\"anchor\"/>" =?>
3131 plain (spanWith ("anchor",[],[]) mempty)
3232 ]
33+ , testGroup "lang"
34+ [ test html "lang on <html>" $ "<html lang=\"es\">hola" =?>
35+ setMeta "lang" (text "es") (doc (plain (text "hola")))
36+ , test html "xml:lang on <html>" $ "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"es\"><head></head><body>hola</body></html>" =?>
37+ setMeta "lang" (text "es") (doc (plain (text "hola")))
38+ ]
3339 ]
test/Tests/Readers/LaTeX.hs+2−1
@@ -58,7 +58,8 @@ tests = [ testGroup "basic"
5858 , "blank lines + space + comments" =:
5959 "% my comment\n\n \n % another\n\nhi" =?> para "hi"
6060 , "comment in paragraph" =:
61− "hi % this is a comment\nthere\n" =?> para "hi there"
61+ "hi % this is a comment\nthere\n" =?>
62+ para ("hi" <> softbreak <> "there")
6263 ]
6364
6465 , testGroup "code blocks"
test/Tests/Readers/Muse.hsnew file+318
@@ -0,0 +1,318 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+module Tests.Readers.Muse (tests) where
3+
4+import Data.List (intersperse)
5+import Data.Text (Text)
6+import qualified Data.Text as T
7+import Test.Tasty
8+import Tests.Helpers
9+import Text.Pandoc
10+import Text.Pandoc.Arbitrary ()
11+import Text.Pandoc.Builder
12+import Text.Pandoc.Class
13+
14+muse :: Text -> Pandoc
15+muse = purely $ \s -> do
16+ putCommonState
17+ def { stInputFiles = Just ["in"]
18+ , stOutputFile = Just "out"
19+ }
20+ readMuse def s
21+
22+infix 4 =:
23+(=:) :: ToString c
24+ => String -> (Text, c) -> TestTree
25+(=:) = test muse
26+
27+spcSep :: [Inlines] -> Inlines
28+spcSep = mconcat . intersperse space
29+
30+tests :: [TestTree]
31+tests =
32+ [ testGroup "Inlines"
33+ [ "Plain String" =:
34+ "Hello, World" =?>
35+ para (spcSep [ "Hello,", "World" ])
36+
37+ , "Emphasis" =: "*Foo bar*" =?> para (emph . spcSep $ ["Foo", "bar"])
38+
39+ , "Emphasis tag" =: "<em>Foo bar</em>" =?> para (emph . spcSep $ ["Foo", "bar"])
40+
41+ , "Strong" =:
42+ "**Cider**" =?>
43+ para (strong "Cider")
44+
45+ , "Strong tag" =: "<strong>Strong</strong>" =?> para (strong "Strong")
46+
47+ , "Strong Emphasis" =:
48+ "***strength***" =?>
49+ para (strong . emph $ "strength")
50+
51+ , "Superscript tag" =: "<sup>Superscript</sup>" =?> para (superscript "Superscript")
52+
53+ , "Subscript tag" =: "<sub>Subscript</sub>" =?> para (subscript "Subscript")
54+
55+ , "Strikeout tag" =: "<del>Strikeout</del>" =?> para (strikeout "Strikeout")
56+
57+ , "Linebreak" =: "Line <br> break" =?> para ("Line" <> linebreak <> "break")
58+
59+ , "Code" =: "=foo(bar)=" =?> para (code "foo(bar)")
60+
61+ , "Not code" =: "a=b= =c=d" =?> para (text "a=b= =c=d")
62+
63+ , "Code tag" =: "<code>foo(bar)</code>" =?> para (code "foo(bar)")
64+
65+ , testGroup "Links"
66+ [ "Link without description" =:
67+ "[[https://amusewiki.org/]]" =?>
68+ para (link "https://amusewiki.org/" "" (str "https://amusewiki.org/"))
69+ , "Link with description" =:
70+ "[[https://amusewiki.org/][A Muse Wiki]]" =?>
71+ para (link "https://amusewiki.org/" "" (text "A Muse Wiki"))
72+ , "Image" =:
73+ "[[image.jpg]]" =?>
74+ para (image "image.jpg" "" mempty)
75+ , "Image with description" =:
76+ "[[image.jpg][Image]]" =?>
77+ para (image "image.jpg" "" (text "Image"))
78+ , "Image link" =:
79+ "[[URL:image.jpg]]" =?>
80+ para (link "image.jpg" "" (str "image.jpg"))
81+ , "Image link with description" =:
82+ "[[URL:image.jpg][Image]]" =?>
83+ para (link "image.jpg" "" (text "Image"))
84+ -- Implicit links are supported in Emacs Muse, but not in Amusewiki:
85+ -- https://github.com/melmothx/text-amuse/issues/18
86+ --
87+ -- This test also makes sure '=' without whitespace is not treated as code markup
88+ , "No implicit links" =: "http://example.org/index.php?action=view&id=1"
89+ =?> para "http://example.org/index.php?action=view&id=1"
90+ ]
91+ ]
92+
93+ , testGroup "Blocks"
94+ [ "Block elements end paragraphs" =:
95+ T.unlines [ "First paragraph"
96+ , "----"
97+ , "Second paragraph"
98+ ] =?> para (text "First paragraph") <> horizontalRule <> para (text "Second paragraph")
99+ , testGroup "Horizontal rule"
100+ [ "Less than 4 dashes is not a horizontal rule" =: "---" =?> para (text "---")
101+ , "4 dashes is a horizontal rule" =: "----" =?> horizontalRule
102+ , "5 dashes is a horizontal rule" =: "-----" =?> horizontalRule
103+ , "4 dashes with spaces is a horizontal rule" =: "---- " =?> horizontalRule
104+ ]
105+ , "Quote tag" =: "<quote>Hello, world</quote>" =?> blockQuote (para $ text "Hello, world")
106+ , "Quote" =: " This is a quotation\n" =?> blockQuote (para $ text "This is a quotation")
107+ , "Multiline quote" =: T.unlines [ " This is a quotation"
108+ , " with a continuation"
109+ ]
110+ =?> blockQuote (para $ text "This is a quotation with a continuation")
111+ , "Center" =: "<center>Hello, world</center>" =?> para (text "Hello, world")
112+ , "Right" =: "<right>Hello, world</right>" =?> para (text "Hello, world")
113+ , testGroup "Comments"
114+ [ "Comment tag" =: "<comment>\nThis is a comment\n</comment>" =?> (mempty::Blocks)
115+ , "Line comment" =: "; Comment" =?> (mempty::Blocks)
116+ , "Not a comment (does not start with a semicolon)" =: " ; Not a comment" =?> para (text "; Not a comment")
117+ , "Not a comment (has no space after semicolon)" =: ";Not a comment" =?> para (text ";Not a comment")
118+ ]
119+ , testGroup "Headers"
120+ [ "Part" =:
121+ "* First level\n" =?>
122+ header 1 "First level"
123+ , "Chapter" =:
124+ "** Second level\n" =?>
125+ header 2 "Second level"
126+ , "Section" =:
127+ "*** Third level\n" =?>
128+ header 3 "Third level"
129+ , "Subsection" =:
130+ "**** Fourth level\n" =?>
131+ header 4 "Fourth level"
132+ , "Subsubsection" =:
133+ "***** Fifth level\n" =?>
134+ header 5 "Fifth level"
135+ , "No headers below top level" =:
136+ T.unlines [ "Foo[1]"
137+ , "[1] * Bar"
138+ ] =?>
139+ para (text "Foo" <>
140+ note (para "* Bar"))
141+ ]
142+ , testGroup "Footnotes"
143+ [ "Simple footnote" =:
144+ T.unlines [ "Here is a footnote[1]."
145+ , ""
146+ , "[1] Footnote contents"
147+ ] =?>
148+ para (text "Here is a footnote" <>
149+ note (para "Footnote contents") <>
150+ str ".")
151+ , "Recursive footnote" =:
152+ T.unlines [ "Start recursion here[1]"
153+ , ""
154+ , "[1] Recursion continues here[1]"
155+ ] =?>
156+ para (text "Start recursion here" <>
157+ note (para "Recursion continues here[1]"))
158+ ]
159+ ]
160+ , testGroup "Tables"
161+ [ "Two cell table" =:
162+ "One | Two" =?>
163+ table mempty [(AlignDefault, 0.0), (AlignDefault, 0.0)]
164+ []
165+ [[plain "One", plain "Two"]]
166+ , "Table with multiple words" =:
167+ "One two | three four" =?>
168+ table mempty [(AlignDefault, 0.0), (AlignDefault, 0.0)]
169+ []
170+ [[plain "One two", plain "three four"]]
171+ , "Not a table" =:
172+ "One| Two" =?>
173+ para (text "One| Two")
174+ , "Not a table again" =:
175+ "One |Two" =?>
176+ para (text "One |Two")
177+ , "Two line table" =:
178+ T.unlines
179+ [ "One | Two"
180+ , "Three | Four"
181+ ] =?>
182+ table mempty [(AlignDefault, 0.0), (AlignDefault, 0.0)]
183+ []
184+ [[plain "One", plain "Two"],
185+ [plain "Three", plain "Four"]]
186+ , "Table with one header" =:
187+ T.unlines
188+ [ "First || Second"
189+ , "Third | Fourth"
190+ ] =?>
191+ table mempty [(AlignDefault, 0.0), (AlignDefault, 0.0)]
192+ [plain "First", plain "Second"]
193+ [[plain "Third", plain "Fourth"]]
194+ , "Table with two headers" =:
195+ T.unlines
196+ [ "First || header"
197+ , "Second || header"
198+ , "Foo | bar"
199+ ] =?>
200+ table mempty [(AlignDefault, 0.0), (AlignDefault, 0.0)]
201+ [plain "First", plain "header"]
202+ [[plain "Second", plain "header"],
203+ [plain "Foo", plain "bar"]]
204+ , "Header and footer reordering" =:
205+ T.unlines
206+ [ "Foo ||| bar"
207+ , "Baz || foo"
208+ , "Bar | baz"
209+ ] =?>
210+ table mempty [(AlignDefault, 0.0), (AlignDefault, 0.0)]
211+ [plain "Baz", plain "foo"]
212+ [[plain "Bar", plain "baz"],
213+ [plain "Foo", plain "bar"]]
214+ , "Table with caption" =:
215+ T.unlines
216+ [ "Foo || bar || baz"
217+ , "First | row | here"
218+ , "Second | row | there"
219+ , "|+ Table caption +|"
220+ ] =?>
221+ table (text "Table caption") (replicate 3 (AlignDefault, 0.0))
222+ [plain "Foo", plain "bar", plain "baz"]
223+ [[plain "First", plain "row", plain "here"],
224+ [plain "Second", plain "row", plain "there"]]
225+ , "Caption without table" =:
226+ "|+ Foo bar baz +|" =?>
227+ table (text "Foo bar baz") [] [] []
228+ , "Table indented with space" =:
229+ T.unlines
230+ [ " Foo | bar"
231+ , " Baz | foo"
232+ , " Bar | baz"
233+ ] =?>
234+ table mempty [(AlignDefault, 0.0), (AlignDefault, 0.0)]
235+ []
236+ [[plain "Foo", plain "bar"],
237+ [plain "Baz", plain "foo"],
238+ [plain "Bar", plain "baz"]]
239+ , "Empty cells" =:
240+ T.unlines
241+ [ " | Foo"
242+ , " |"
243+ , " bar |"
244+ , " || baz"
245+ ] =?>
246+ table mempty [(AlignDefault, 0.0), (AlignDefault, 0.0)]
247+ [plain "", plain "baz"]
248+ [[plain "", plain "Foo"],
249+ [plain "", plain ""],
250+ [plain "bar", plain ""]]
251+ ]
252+ , testGroup "Lists"
253+ [ "Bullet list" =:
254+ T.unlines
255+ [ " - Item1"
256+ , ""
257+ , " - Item2"
258+ ] =?>
259+ bulletList [ para "Item1"
260+ , para "Item2"
261+ ]
262+ , "Ordered list" =:
263+ T.unlines
264+ [ " 1. Item1"
265+ , ""
266+ , " 2. Item2"
267+ ] =?>
268+ orderedListWith (1, Decimal, Period) [ para "Item1"
269+ , para "Item2"
270+ ]
271+ , "Nested list" =:
272+ T.unlines
273+ [ " - Item1"
274+ , " - Item2"
275+ , " - Item3"
276+ , " - Item4"
277+ , " 1. Nested"
278+ , " 2. Ordered"
279+ , " 3. List"
280+ ] =?>
281+ bulletList [ mconcat [ para "Item1"
282+ , bulletList [ para "Item2"
283+ , para "Item3"
284+ ]
285+ ]
286+ , mconcat [ para "Item4"
287+ , orderedListWith (1, Decimal, Period) [ para "Nested"
288+ , para "Ordered"
289+ , para "List"
290+ ]
291+ ]
292+ ]
293+ , "List continuation" =:
294+ T.unlines
295+ [ " - a"
296+ , ""
297+ , " b"
298+ , ""
299+ , " c"
300+ ] =?>
301+ bulletList [ mconcat [ para "a"
302+ , para "b"
303+ , para "c"
304+ ]
305+ ]
306+ -- Headers in first column of list continuation are not allowed
307+ , "No headers in list continuation" =:
308+ T.unlines
309+ [ " - Foo"
310+ , ""
311+ , " * Bar"
312+ ] =?>
313+ bulletList [ mconcat [ para "Foo"
314+ , para "* Bar"
315+ ]
316+ ]
317+ ]
318+ ]
test/Tests/Writers/Muse.hs+13−2
@@ -31,14 +31,14 @@ tests = [ testGroup "block elements"
3131 , "Second paragraph."
3232 ]
3333 ]
34− , "line block" =: lineBlock ([text "Foo", text "bar", text "baz"])
34+ , "line block" =: lineBlock [text "Foo", text "bar", text "baz"]
3535 =?> unlines [ "<verse>"
3636 , "Foo"
3737 , "bar"
3838 , "baz"
3939 , "</verse>"
4040 ]
41− , "code block" =: codeBlock ("int main(void) {\n\treturn 0;\n}")
41+ , "code block" =: codeBlock "int main(void) {\n\treturn 0;\n}"
4242 =?> unlines [ "<example>"
4343 , "int main(void) {"
4444 , "\treturn 0;"
@@ -137,6 +137,17 @@ tests = [ testGroup "block elements"
137137 , " second inner definition :: second inner description"
138138 ]
139139 ]
140+ -- Check that list is intended with one space even inside a quote
141+ , "List inside block quote" =: blockQuote (orderedList [ plain $ text "first"
142+ , plain $ text "second"
143+ , plain $ text "third"
144+ ])
145+ =?> unlines [ "<quote>"
146+ , " 1. first"
147+ , " 2. second"
148+ , " 3. third"
149+ , "</quote>"
150+ ]
140151 ]
141152 , testGroup "headings"
142153 [ "normal heading" =:
test/command/1390.mdnew file+20
@@ -0,0 +1,20 @@
1+```
2+% pandoc -f latex -t native
3+\newcommand\foo{+}
4+Testing: $\mu\foo\eta$.
5+^D
6+[Para [Str "Testing:",Space,Math InlineMath "\\mu+\\eta",Str "."]]
7+```
8+
9+<!-- It would be nice to handle this case, but I don't
10+know how:
11+
12+```
13+% pandoc -f latex -t native
14+\newcommand{\vecx}{a + b}
15+$\hat\vecx$
16+^D
17+[Para [Math InlineMath "\\hat{a+b}"]]
18+```
19+-->
20+
test/command/2118.mdnew file+11
@@ -0,0 +1,11 @@
1+```
2+% pandoc -f latex -t native
3+\newcommand{\inclgraph}{\includegraphics[width=0.8\textwidth]}
4+\begin{figure}[ht]
5+ \inclgraph{setminus.png}
6+ \caption{Set subtraction}
7+ \label{fig:setminus}
8+\end{figure}
9+^D
10+[Para [Image ("",[],[("width","80%")]) [Str "Set",Space,Str "subtraction",Span ("",[],[("data-label","fig:setminus")]) []] ("setminus.png","fig:")]]
11+```
test/command/2378.mdnew file+27
@@ -0,0 +1,27 @@
1+Ensure that we don't get duplicated footnotes when
2+a note occurs in a header cell and `\endfirsthead`
3+is used.
4+
5+```
6+% pandoc -t latex
7+| x | y[^fn] |
8+|-|-|
9+|1|2|
10+: a table
11+
12+[^fn]: a footnote
13+^D
14+\begin{longtable}[]{@{}ll@{}}
15+\caption{a table}\tabularnewline
16+\toprule
17+x & y\footnote{a footnote}\tabularnewline
18+\midrule
19+\endfirsthead
20+\toprule
21+x & y{}\tabularnewline
22+\midrule
23+\endhead
24+1 & 2\tabularnewline
25+\bottomrule
26+\end{longtable}
27+```
test/command/262.mdnew file+26
@@ -0,0 +1,26 @@
1+```
2+% pandoc -f rst
3+`hello`_ and `goodbye`_
4+
5+.. _hello:
6+.. _goodbye: example.com
7+^D
8+<p><a href="example.com">hello</a> and <a href="example.com">goodbye</a></p>
9+```
10+
11+```
12+% pandoc -f rst
13+`hello`_ `goodbye`_
14+
15+.. _hello:
16+.. _goodbye:
17+
18+paragraph
19+^D
20+<p><a href="#hello">hello</a> <a href="#goodbye">goodbye</a></p>
21+<div id="hello">
22+<div id="goodbye">
23+<p>paragraph</p>
24+</div>
25+</div>
26+```
test/command/3113.md+1−1
@@ -8,6 +8,6 @@ C&=&D,\\
88E&=&F
99\end{eqnarray}
1010^D
11−[Para [Math DisplayMath "\\begin{aligned}\nA&=&B,\\\\\nC&=&D,\\\\\nE&=&F\\end{aligned}"]]
11+[Para [Math DisplayMath "\\begin{aligned}\nA&=&B,\\\\\nC&=&D,\\\\\n%\\end{eqnarray}\n%\\begin{eqnarray}\nE&=&F\\end{aligned}"]]
1212```
1313
test/command/3236.mdnew file+9
@@ -0,0 +1,9 @@
1+```
2+pandoc -f latex -t native
3+\newcommand{\mycolor}{red}
4+
5+\includegraphics[width=17cm]{\mycolor /header}
6+Magnificent \mycolor{} header.
7+^D
8+[Para [Image ("",[],[("width","17cm")]) [Str "image"] ("red/header",""),SoftBreak,Str "Magnificent",Space,Str "red",Space,Str "header."]]
9+```
test/command/3494.md+2−1
@@ -25,7 +25,8 @@
2525<td style="text-align: left;">thank you</td>
2626</tr>
2727<tr class="odd">
28−<td style="text-align: right;"><em>blah</em></td>
28+<td style="text-align: right;">
29+<p><em>blah</em></p></td>
2930<td style="text-align: left;"><em>blah</em></td>
3031<td style="text-align: left;"><em>blah</em></td>
3132</tr>
test/command/3537.mdnew file+28
@@ -0,0 +1,28 @@
1+Generalized raw attributes.
2+
3+````
4+% pandoc -t native
5+```{=ms}
6+.MACRO
7+foo bar
8+```
9+^D
10+[RawBlock (Format "ms") ".MACRO\nfoo bar"]
11+````
12+
13+````
14+% pandoc -t native
15+Hi `there`{=ms}.
16+^D
17+[Para [Str "Hi",Space,RawInline (Format "ms") "there",Str "."]]
18+````
19+
20+````
21+% pandoc -t native
22+~~~ {=ms}
23+.MACRO
24+foo bar
25+~~~
26+^D
27+[RawBlock (Format "ms") ".MACRO\nfoo bar"]
28+````
test/command/3558.md+6−2
@@ -1,8 +1,12 @@
11```
22% pandoc -t native
3−\startmulti
3+\multi
4+
45hello
6+
57\endmulti
68^D
7−[Para [RawInline (Format "tex") "\\startmulti\n",Str "hello",SoftBreak,RawInline (Format "tex") "\\endmulti"]]
9+[RawBlock (Format "latex") "\\multi"
10+,Para [Str "hello"]
11+,RawBlock (Format "latex") "\\endmulti"]
812```
test/command/3577.md+2
@@ -15,9 +15,11 @@
1515 \caption{Subfigure with Subfloat}
1616\end{figure}
1717^D
18+
1819<figure>
1920<img src="img1.jpg" alt="Caption 1" /><figcaption>Caption 1</figcaption>
2021</figure>
22+
2123<figure>
2224<img src="img2.jpg" alt="Caption 2" /><figcaption>Caption 2</figcaption>
2325</figure>
test/command/3755.mdnew file+23
@@ -0,0 +1,23 @@
1+```
2+% pandoc -t native -s
3+---
4+title: 'Titel'
5+date: '22. Juni 2017'
6+---
7+^D
8+Pandoc (Meta {unMeta = fromList [("date",MetaInlines [Str "22.",Space,Str "Juni",Space,Str "2017"]),("title",MetaInlines [Str "Titel"])]})
9+[]
10+```
11+
12+```
13+% pandoc -t native -s
14+---
15+title: '<div>foo</div>'
16+date: |
17+ 22. Juni 2017
18+---
19+^D
20+Pandoc (Meta {unMeta = fromList [("date",MetaBlocks [OrderedList (22,Decimal,Period) [[Plain [Str "Juni",Space,Str "2017"]]]]),("title",MetaBlocks [Div ("",[],[]) [Plain [Str "foo"]]])]})
21+[]
22+```
23+
test/command/3773.mdnew file+14
@@ -0,0 +1,14 @@
1+```
2+% pandoc -t markdown
3+A. \#
4+B. \+
5+C. \*
6+D. o
7+E. o or \*
8+^D
9+A. \#
10+B. \+
11+C. \*
12+D. o
13+E. o or \*
14+```
test/command/3779.mdnew file+28
@@ -0,0 +1,28 @@
1+```
2+% pandoc -f latex -t native
3+\newcommand{\fakeitemize}[1]{
4+ \begin{itemize}
5+ #1
6+ \end{itemize}
7+}
8+\newcommand{\testcmd}[1]{
9+ #1
10+}
11+\fakeitemize{
12+ \item Pandoc is 100\% awesome.
13+}
14+
15+\begin{itemize}
16+ \item Pandoc is 200\% awesome.
17+\end{itemize}
18+
19+\testcmd{
20+ Pandoc is 300\% awesome.
21+}
22+^D
23+[BulletList
24+ [[Para [Str "Pandoc",Space,Str "is",Space,Str "100%",Space,Str "awesome."]]]
25+,BulletList
26+ [[Para [Str "Pandoc",Space,Str "is",Space,Str "200%",Space,Str "awesome."]]]
27+,Para [Str "Pandoc",Space,Str "is",Space,Str "300%",Space,Str "awesome."]]
28+```
test/command/3794.mdnew file+7
@@ -0,0 +1,7 @@
1+```
2+% pandoc -f html -t native
3+<div><p>hello</div>
4+^D
5+[Div ("",[],[])
6+ [Para [Str "hello"]]]
7+```
test/command/934.mdnew file+12
@@ -0,0 +1,12 @@
1+```
2+% pandoc -f latex -t native
3+\newcommand{\ddb}[2]{
4+ \textit{``#1''}
5+
6+ \textbf{#2}
7+}
8+\ddb{This should be italic and in quotes}{And this is the attribution}
9+^D
10+[Para [Emph [Quoted DoubleQuote [Str "This",Space,Str "should",Space,Str "be",Space,Str "italic",Space,Str "and",Space,Str "in",Space,Str "quotes"]]]
11+,Para [Strong [Str "And",Space,Str "this",Space,Str "is",Space,Str "the",Space,Str "attribution"]]]
12+```
test/command/982.mdnew file+11
@@ -0,0 +1,11 @@
1+```
2+% pandoc -f latex -t native
3+\newcommand{\BEQ}{\begin{equation}}
4+\newcommand{\EEQ}{\end{equation}}
5+
6+\BEQ
7+y=x^2
8+\EEQ
9+^D
10+[Para [Math DisplayMath "y=x^2"]]
11+```
test/command/write18.mdnew file+14
@@ -0,0 +1,14 @@
1+Handle \write18{..} as raw tex:
2+```
3+% pandoc -t native
4+\write18{git --version}
5+^D
6+[RawBlock (Format "latex") "\\write18{git --version}"]
7+```
8+
9+```
10+% pandoc -f latex+raw_tex -t native
11+\write18{git --version}
12+^D
13+[RawBlock (Format "latex") "\\write18{git --version}"]
14+```
test/latex-reader.latex−1
@@ -4,7 +4,6 @@
44\setlength{\parindent}{0pt}
55\setlength{\parskip}{6pt plus 2pt minus 1pt}
66
7−\newcommand{\textsubscript}[1]{\ensuremath{_{\scriptsize\textrm{#1}}}}
87\usepackage[breaklinks=true,unicode=true]{hyperref}
98\usepackage[normalem]{ulem}
109% avoid problems with \sout in headers with hyperref:
test/latex-reader.native+1−1
@@ -261,7 +261,7 @@ Pandoc (Meta {unMeta = fromList [("author",MetaList [MetaInlines [Str "John",Spa
261261,Header 1 ("latex",[],[]) [Str "LaTeX"]
262262,BulletList
263263 [[Para [Cite [Citation {citationId = "smith.1899", citationPrefix = [], citationSuffix = [Str "22-23"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [RawInline (Format "latex") "\\cite[22-23]{smith.1899}"]]]
264− ,[Para [RawInline (Format "latex") "\\doublespacing\n"]]
264+ ,[RawBlock (Format "latex") "\\doublespacing"]
265265 ,[Para [Math InlineMath "2+2=4"]]
266266 ,[Para [Math InlineMath "x \\in y"]]
267267 ,[Para [Math InlineMath "\\alpha \\wedge \\omega"]]
test/lua/hello-world-doc.lua+2−2
@@ -1,10 +1,10 @@
11return {
22 {
3− Doc = function(doc)
3+ Pandoc = function(doc)
44 local meta = {}
55 local hello = { pandoc.Str "Hello,", pandoc.Space(), pandoc.Str "World!" }
66 local blocks = { pandoc.Para(hello) }
7− return pandoc.Doc(blocks, meta)
7+ return pandoc.Pandoc(blocks, meta)
88 end
99 }
1010}
test/lua/implicit-doc-filter.lua+1−1
@@ -2,5 +2,5 @@ function Doc (doc)
22 local meta = {}
33 local hello = { pandoc.Str "Hello,", pandoc.Space(), pandoc.Str "World!" }
44 local blocks = { pandoc.Para(hello) }
5− return pandoc.Doc(blocks, meta)
5+ return pandoc.Pandoc(blocks, meta)
66end
test/lua/single-to-double-quoted.lua+4−2
@@ -1,7 +1,9 @@
11return {
22 {
3− SingleQuoted = function (elem)
4− elem.quotetype = "DoubleQuote"
3+ Quoted = function (elem)
4+ if elem.quotetype == "SingleQuote" then
5+ elem.quotetype = "DoubleQuote"
6+ end
57 return elem
68 end,
79 }
test/markdown-reader-more.native+2−2
@@ -3,7 +3,7 @@ Pandoc (Meta {unMeta = fromList [("author",MetaList [MetaInlines [Str "Author",S
33,Header 2 ("blank-line-before-url-in-link-reference",[],[]) [Str "Blank",Space,Str "line",Space,Str "before",Space,Str "URL",Space,Str "in",Space,Str "link",Space,Str "reference"]
44,Para [Link ("",[],[]) [Str "foo"] ("/url",""),Space,Str "and",Space,Link ("",[],[]) [Str "bar"] ("/url","title")]
55,Header 2 ("raw-context-environments",[],[]) [Str "Raw",Space,Str "ConTeXt",Space,Str "environments"]
6−,Plain [RawInline (Format "tex") "\\placeformula "]
6+,RawBlock (Format "latex") "\\placeformula "
77,RawBlock (Format "context") "\\startformula\n L_{1} = L_{2}\n \\stopformula"
88,RawBlock (Format "context") "\\start[a2]\n\\start[a2]\n\\stop[a2]\n\\stop[a2]"
99,Header 2 ("raw-latex-environments",[],[]) [Str "Raw",Space,Str "LaTeX",Space,Str "environments"]
@@ -56,7 +56,7 @@ Pandoc (Meta {unMeta = fromList [("author",MetaList [MetaInlines [Str "Author",S
5656,OrderedList (3,Example,TwoParens)
5757 [[Plain [Str "Third",Space,Str "example."]]]
5858,Header 2 ("macros",[],[]) [Str "Macros"]
59−,Para [Math InlineMath "{\\langle x,y \\rangle}"]
59+,Para [Math InlineMath "\\langle x,y \\rangle"]
6060,Header 2 ("case-insensitive-references",[],[]) [Str "Case-insensitive",Space,Str "references"]
6161,Para [Link ("",[],[]) [Str "Fum"] ("/fum","")]
6262,Para [Link ("",[],[]) [Str "FUM"] ("/fum","")]
test/test-pandoc.hs+2
@@ -16,6 +16,7 @@ import qualified Tests.Readers.Odt
1616import qualified Tests.Readers.Org
1717import qualified Tests.Readers.RST
1818import qualified Tests.Readers.Txt2Tags
19+import qualified Tests.Readers.Muse
1920import qualified Tests.Shared
2021import qualified Tests.Writers.AsciiDoc
2122import qualified Tests.Writers.ConTeXt
@@ -61,6 +62,7 @@ tests = testGroup "pandoc tests" [ Tests.Command.tests
6162 , testGroup "Odt" Tests.Readers.Odt.tests
6263 , testGroup "Txt2Tags" Tests.Readers.Txt2Tags.tests
6364 , testGroup "EPUB" Tests.Readers.EPUB.tests
65+ , testGroup "Muse" Tests.Readers.Muse.tests
6466 ]
6567 , testGroup "Lua filters" Tests.Lua.tests
6668 ]
test/vimwiki-reader.nativenew file+309
@@ -0,0 +1,309 @@
1+Pandoc (Meta {unMeta = fromList [("date",MetaInlines [Str "2017-05-01"]),("title",MetaInlines [Str "title"])]})
2+[Header 1 ("implemented",[],[]) [Emph [Span ("implemented",[],[]) [],Strong [Str "implemented"]]]
3+,Header 1 ("header",[],[]) [Str "header"]
4+,Header 2 ("header level two",[],[]) [Str "header",Space,Str "level",Space,Str "two"]
5+,Header 3 ("header level 3",[],[]) [Str "header",Space,Code ("",[],[]) "level",Space,Str "3"]
6+,Header 4 ("header level four",[],[]) [Str "header",Space,Strikeout [Str "level"],Space,Str "four"]
7+,Header 5 ("header level 5",[],[]) [Str "header",Space,Emph [Span ("level",[],[]) [],Strong [Str "level"],Space,Str "5"]]
8+,Header 6 ("header level 6",[],[]) [Str "header",Space,Str "level",Space,Str "6"]
9+,Para [Str "=======",Space,Str "not",Space,Str "a",Space,Str "header",Space,Str "========"]
10+,Para [Str "hi==",Space,Str "not",Space,Str "a",Space,Str "header",Space,Str "=="]
11+,Para [Str "===",Space,Str "not",Space,Str "a",Space,Str "header",Space,Str "=="]
12+,Para [Str "===",Space,Str "not",Space,Str "a",Space,Str "header",Space,Str "===-"]
13+,Para [Str "not",Space,Str "a",Space,Str "header:"]
14+,Para [Str "=n="]
15+,Para [Str "===",Space,Str "not",Space,Str "a",Space,Str "header",Space,Str "===="]
16+,Header 2 ("centred header",["justcenter"],[]) [Str "centred",Space,Str "header"]
17+,Header 2 ("header with some == in between",[],[]) [Str "header",Space,Str "with",Space,Str "some",Space,Code ("",[],[]) "==",Space,Str "in",Space,Str "between"]
18+,Header 2 ("header with some == in between",[],[]) [Str "header",Space,Str "with",Space,Str "some",Space,Str "==",Space,Str "in",Space,Str "between"]
19+,Header 2 ("header with some ==in between",[],[]) [Str "header",Space,Str "with",Space,Str "some",Space,Str "==in",Space,Str "between"]
20+,Header 2 ("emph strong and strikeout",[],[]) [Str "emph",Space,Str "strong",Space,Str "and",Space,Str "strikeout"]
21+,Para [Emph [Str "emph"],Space,Span ("strong",[],[]) [],Strong [Str "strong"]]
22+,Para [Span ("strong and emph",[],[]) [],Strong [Emph [Str "strong",Space,Str "and",Space,Str "emph"]]]
23+,Para [Emph [Span ("emph and strong",[],[]) [],Strong [Str "emph",Space,Str "and",Space,Str "strong"]]]
24+,Para [Span ("emph inside strong",[],[]) [],Strong [Emph [Str "emph",Space,Str "inside"],Space,Str "strong"]]
25+,Para [Span ("strong with emph",[],[]) [],Strong [Str "strong",Space,Str "with",Space,Emph [Str "emph"]]]
26+,Para [Emph [Span ("strong inside",[],[]) [],Strong [Str "strong",Space,Str "inside"],Space,Str "emph"]]
27+,Para [Emph [Strikeout [Str "strikeout"],Space,Str "inside",Space,Str "emph"]]
28+,Para [Strikeout [Str "This",Space,Str "is",Space,Emph [Str "struck",Space,Str "out"],Space,Str "with",Space,Str "emph"]]
29+,Para [Str "*not",SoftBreak,Str "strong*"]
30+,Para [Str "just",Space,Str "two",Space,Str "stars:",Space,Str "**"]
31+,Para [Str "just",Space,Str "two",Space,Str "underscores:",Space,Str "__"]
32+,Para [Str "just",Space,Str "four",Space,Str "~s:",Space,Str "~~~~"]
33+,Para [Str "_not",SoftBreak,Str "emph_"]
34+,Para [Str "~~not",SoftBreak,Str "strikeout~~"]
35+,Header 2 ("horizontal rule",[],[]) [Str "horizontal",Space,Str "rule"]
36+,Para [Str "top"]
37+,HorizontalRule
38+,Para [Str "middle"]
39+,HorizontalRule
40+,Para [Str "not",Space,Str "a",Space,Str "rule-----"]
41+,Para [Str "not",Space,Str "a",Space,Str "rule",Space,Str "(trailing",Space,Str "spaces):",SoftBreak,Str "-----"]
42+,Para [Str "not",Space,Str "a",Space,Str "rule",Space,Str "(leading",Space,Str "spaces):",SoftBreak,Str "----"]
43+,Header 2 ("comments",[],[]) [Str "comments"]
44+,Para [Str "this",SoftBreak,Str "is",Space,Str "%%",Space,Str "not",Space,Str "secret"]
45+,Header 2 ("inline code",[],[]) [Str "inline",Space,Str "code"]
46+,Para [Str "Here",Space,Str "is",Space,Str "some",Space,Code ("",[],[]) "inline code",Str "."]
47+,Para [Str "Just",Space,Str "two",Space,Str "backticks:",Space,Str "``"]
48+,Header 2 ("preformatted text",[],[]) [Str "preformatted",Space,Str "text"]
49+,CodeBlock ("",[],[]) " Tyger! Tyger! burning bright\n In the forests of the night,\n What immortal hand or eye\n Could frame thy fearful symmetry?\n In what distant deeps or skies\n Burnt the fire of thine eyes?\n On what wings dare he aspire?\n What the hand dare sieze the fire?"
50+,Header 3 ("preformatted text with attributes",[],[]) [Str "preformatted",Space,Str "text",Space,Str "with",Space,Str "attributes"]
51+,CodeBlock ("",[],[("class","python"),("style","color:blue")]) " for i in range(1, 5):\n print(i)"
52+,Header 2 ("block quotes",[],[]) [Str "block",Space,Str "quotes"]
53+,BlockQuote
54+ [Plain [Str "(indentation",Space,Str "4",Space,Str "spaces)",Space,Str "This",Space,Str "would",Space,Str "be",Space,Str "a",Space,Str "blockquote",Space,Str "in",Space,Str "Vimwiki.",Space,Str "It",Space,Str "is",Space,Str "not",Space,Span ("highlighted",[],[]) [],Strong [Str "highlighted"],Space,Str "in",Space,Str "Vim",Space,Str "but",SoftBreak,Str "(indentation",Space,Str "1",Space,Str "space",Space,Str "followed",Space,Str "by",Space,Str "1",Space,Str "tab",Space,Str "of",Space,Str "width",Space,Str "4)",Space,Str "could",Space,Str "be",Space,Str "styled",Space,Str "by",Space,Str "CSS",Space,Str "in",Space,Str "HTML.",Space,Str "Blockquotes",Space,Str "are",Space,Str "usually",Space,Str "used",Space,Str "to",Space,Str "quote",Space,Str "a",SoftBreak,Str "(indentation",Space,Str "1",Space,Str "tab",Space,Str "of",Space,Str "width",Space,Str "4)",Space,Str "long",Space,Str "piece",Space,Str "of",Space,Str "text",Space,Str "from",Space,Str "another",Space,Str "source.",Space,Strikeout [Str "blah",Space,Str "blah"],Space,Span ("-blockquote",[],[]) [Str ""],Span ("blockquote",["tag"],[]) [Str "blockquote"]]]
55+,Header 2 ("external links",[],[]) [Str "external",Space,Str "links"]
56+,Para [Link ("",[],[]) [Emph [Str "Google"],Space,Str "search",Space,Str "engine"] ("http://google.com","")]
57+,Para [Link ("",[],[]) [Str "http://pandoc.org"] ("http://pandoc.org","")]
58+,Para [Link ("",[],[]) [Str "ftp://vim.org"] ("ftp://vim.org","")]
59+,Para [Link ("",[],[]) [Str "http://google.com"] ("http://google.com","")]
60+,Para [Link ("",[],[]) [Str "email",Space,Str "me"] ("mailto:info@example.org","")]
61+,Para [Link ("",[],[]) [Str "mailto:hello@bye.com"] ("mailto:hello@bye.com","")]
62+,Header 2 ("internal links",[],[]) [Str "internal",Space,Str "links"]
63+,Para [Link ("",[],[]) [Str "This is a link"] ("This is a link.html","")]
64+,Para [Link ("",[],[]) [Str "Description",Space,Str "of",Space,Str "the",Space,Str "link"] ("This is a link source.html","")]
65+,Para [Link ("",[],[]) [Str "projects/Important Project 1"] ("projects/Important Project 1.html",""),SoftBreak,Link ("",[],[]) [Str "../index"] ("../index.html",""),SoftBreak,Link ("",[],[]) [Str "Other",Space,Str "files"] ("a subdirectory/","")]
66+,Para [Link ("",[],[]) [Str "try",Space,Str "me",Space,Str "to",Space,Str "test",Space,Str "tag",Space,Str "anchors"] ("#tag-one","")]
67+,Para [Link ("",[],[]) [Str "try",Space,Str "me",Space,Str "to",Space,Str "test",Space,Str "header",Space,Str "anchors"] ("#block quotes","")]
68+,Para [Link ("",[],[]) [Str "try",Space,Str "me",Space,Str "to",Space,Str "test",Space,Str "strong",Space,Str "anchors"] ("#strong","")]
69+,Para [Link ("",[],[]) [Str "Tasks",Space,Str "for",Space,Str "tomorrow"] ("Todo List.html#Tomorrow","")]
70+,Para [Link ("",[],[]) [Str "diary:2017-05-01"] ("diary/2017-05-01.html","")]
71+,Para [Link ("",[],[]) [Str "Important",Space,Str "Data"] ("file:../assets/data.csv","")]
72+,Header 3 ("links with thumbnails",[],[]) [Str "links",Space,Str "with",Space,Str "thumbnails"]
73+,Para [Link ("",[],[]) [Image ("",[],[]) [Str ""] ("./movie.jpg","")] ("http://www.google.com","")]
74+,Header 2 ("images",[],[]) [Str "images"]
75+,Para [Image ("",[],[]) [Str ""] ("file:./lalune.jpg","")]
76+,Para [Image ("",[],[]) [Str "Vimwiki"] ("http://vimwiki.googlecode.com/hg/images/vimwiki_logo.png",""),SoftBreak,Image ("",[],[]) [Str ""] ("file:./movie.jpg","")]
77+,Header 3 ("image with attributes",[],[]) [Str "image",Space,Str "with",Space,Str "attributes"]
78+,Para [Image ("",[],[("style","width:150px;height:120px;")]) [Emph [Str "cool",Space,Str "stuff"]] ("lalune.jpg","")]
79+,Para [Image ("",[],[("style","font-color:red")]) [Span ("Non-existing",[],[]) [],Strong [Str "Non-existing"],Space,Str "image"] ("nonexist.jpg","")]
80+,Para [Image ("",[],[("style","width:150px;height:120px;")]) [Emph [Str "cool",Space,Str "stuff"]] ("lalune.jpg","")]
81+,Header 2 ("lists",[],[]) [Str "lists"]
82+,OrderedList (1,DefaultStyle,DefaultDelim)
83+ [[Plain [Str "ordered",Space,Str "list",Space,Str "item",Space,Str "1,",Space,Str "and",Space,Str "here",Space,Str "is",Space,Str "some",Space,Str "math",Space,Str "belonging",Space,Str "to",Space,Str "list",Space,Str "item",Space,Str "1"]
84+ ,Para [Math DisplayMath "a^2 + b^2 = c^2"]
85+ ,Plain [Str "and",Space,Str "some",Space,Str "preformatted",Space,Str "and",Space,Str "tables",Space,Str "belonging",Space,Str "to",Space,Str "item",Space,Str "1",Space,Str "as",Space,Str "well"]
86+ ,CodeBlock ("",[],[]) "I'm part of item 1."
87+ ,Table [] [AlignDefault,AlignDefault] [0.0,0.0]
88+ [[]
89+ ,[]]
90+ [[[Plain [Str "this",Space,Str "table"]]
91+ ,[Plain [Str "is"]]]
92+ ,[[Plain [Str "also",Space,Str "a",Space,Str "part"]]
93+ ,[Plain [Str "of",Space,Str "item",Space,Str "1"]]]]
94+ ,Plain [Str "and",Space,Str "some",Space,Str "more",Space,Str "text",Space,Str "belonging",Space,Str "to",Space,Str "item",Space,Str "1."]]
95+ ,[Plain [Str "ordered",Space,Str "list",Space,Str "item",Space,Str "2"]]]
96+,BulletList
97+ [[Plain [Str "Bulleted",Space,Str "list",Space,Str "item",Space,Str "1"]]
98+ ,[Plain [Str "Bulleted",Space,Str "list",Space,Str "item",Space,Str "2"]]]
99+,OrderedList (1,DefaultStyle,DefaultDelim)
100+ [[Plain [Str "Bulleted",Space,Str "list",Space,Str "item",Space,Str "1"]]
101+ ,[Plain [Str "the",Space,Str "#",Space,Str "become",Space,Str "numbers",Space,Str "when",Space,Str "converted",Space,Str "to",Space,Str "HTML"]]]
102+,BulletList
103+ [[Plain [Str "Bulleted",Space,Str "list",Space,Str "item",Space,Str "1"]]
104+ ,[Plain [Str "Bulleted",Space,Str "list",Space,Str "item",Space,Str "2"]]]
105+,BulletList
106+ [[Plain [Str "Item",Space,Str "1"]]
107+ ,[Plain [Str "Item",Space,Str "2"]
108+ ,OrderedList (1,DefaultStyle,DefaultDelim)
109+ [[Plain [Str "Sub",Space,Str "item",Space,Str "1",Space,Str "(indentation",Space,Str "4",Space,Str "spaces)",SoftBreak,Str "Sub",Space,Str "item",Space,Str "1",Space,Str "continued",Space,Str "line.",SoftBreak,Str "Sub",Space,Str "item",Space,Str "1",Space,Str "next",Space,Str "continued",Space,Str "line."]]
110+ ,[Plain [Str "Sub",Space,Str "item",Space,Str "2,",Space,Str "as",Space,Str "an",Space,Str "ordered",Space,Str "list",Space,Str "item",Space,Str "even",Space,Str "though",Space,Str "the",Space,Str "identifier",Space,Str "is",Space,Code ("",[],[]) "*",Space,Str "(indentation",Space,Str "2",Space,Str "spaces",Space,Str "followed",Space,Str "by",Space,Str "one",Space,Str "tab",Space,Str "of",Space,Str "width",Space,Str "4)"]]
111+ ,[Plain [Str "etc.",SoftBreak,Str "Continuation",Space,Str "of",Space,Str "Item",Space,Str "2",SoftBreak,Str "Next",Space,Str "continuation",Space,Str "of",Space,Str "Item",Space,Str "2"]]]]]
112+,Para [Str "But",Space,Str "this",Space,Str "is",Space,Str "a",Space,Str "new",Space,Str "paragraph."]
113+,OrderedList (1,DefaultStyle,DefaultDelim)
114+ [[Plain [Str "1"]
115+ ,BulletList
116+ [[Plain [Code ("",[],[]) "1.1"]]]]
117+ ,[Plain [Str "2"]
118+ ,BulletList
119+ [[Plain [Str "2.1"]]]]]
120+,BulletList
121+ [[Plain [Str "3"]]]
122+,Header 3 ("ordered lists with non-# identifiers",[],[]) [Str "ordered",Space,Str "lists",Space,Str "with",Space,Str "non-#",Space,Str "identifiers"]
123+,OrderedList (1,DefaultStyle,DefaultDelim)
124+ [[Plain [Str "Numbered",Space,Str "list",Space,Str "item",Space,Str "1"]]
125+ ,[Plain [Str "Numbered",Space,Str "list",Space,Str "item",Space,Str "2"]]
126+ ,[Plain [Str "Numbered",Space,Str "list",Space,Str "item",Space,Str "3"]]]
127+,OrderedList (1,DefaultStyle,DefaultDelim)
128+ [[Plain [Str "Numbered",Space,Str "list",Space,Str "item",Space,Str "1"]]
129+ ,[Plain [Str "Numbered",Space,Str "list",Space,Str "item",Space,Str "2"]]
130+ ,[Plain [Str "Numbered",Space,Str "list",Space,Str "item",Space,Str "3"]]]
131+,OrderedList (1,DefaultStyle,DefaultDelim)
132+ [[Plain [Str "Numbered",Space,Str "list",Space,Str "item",Space,Str "1"]]
133+ ,[Plain [Str "Numbered",Space,Str "list",Space,Str "item",Space,Str "2"]]
134+ ,[Plain [Str "Numbered",Space,Str "list",Space,Str "item",Space,Str "3"]]]
135+,OrderedList (1,DefaultStyle,DefaultDelim)
136+ [[Plain [Str "Numbered",Space,Str "list",Space,Str "item",Space,Str "1"]]
137+ ,[Plain [Str "Numbered",Space,Str "list",Space,Str "item",Space,Str "2"]]
138+ ,[Plain [Str "Numbered",Space,Str "list",Space,Str "item",Space,Str "3"]]]
139+,OrderedList (1,DefaultStyle,DefaultDelim)
140+ [[Plain [Str "Numbered",Space,Str "list",Space,Str "item",Space,Str "1"]]
141+ ,[Plain [Str "Numbered",Space,Str "list",Space,Str "item",Space,Str "2"]]
142+ ,[Plain [Str "Numbered",Space,Str "list",Space,Str "item",Space,Str "3"]]]
143+,OrderedList (1,DefaultStyle,DefaultDelim)
144+ [[Plain [Str "Numbered",Space,Str "list",Space,Str "item",Space,Str "1"]]
145+ ,[Plain [Str "Numbered",Space,Str "list",Space,Str "item",Space,Str "2"]]
146+ ,[Plain [Str "Numbered",Space,Str "list",Space,Str "item",Space,Str "3"]]]
147+,OrderedList (1,DefaultStyle,DefaultDelim)
148+ [[Plain [Str "Numbered",Space,Str "list",Space,Str "item",Space,Str "1"]]
149+ ,[Plain [Str "Numbered",Space,Str "list",Space,Str "item",Space,Str "2"]]
150+ ,[Plain [Str "Numbered",Space,Str "list",Space,Str "item",Space,Str "3"]]]
151+,BulletList
152+ [[Plain [Str "Bulleted",Space,Str "list",Space,Str "item",Space,Str "1"]]
153+ ,[Plain [Str "Bulleted",Space,Str "list",Space,Str "item",Space,Str "2"]
154+ ,OrderedList (1,DefaultStyle,DefaultDelim)
155+ [[Plain [Str "Numbered",Space,Str "list",Space,Str "sub",Space,Str "item",Space,Str "1"]]
156+ ,[Plain [Str "more",Space,Str "..."]
157+ ,BulletList
158+ [[Plain [Str "and",Space,Str "more",Space,Str "..."]]
159+ ,[Plain [Str "..."]]]]
160+ ,[Plain [Str "Numbered",Space,Str "list",Space,Str "sub",Space,Str "item",Space,Str "3"]
161+ ,OrderedList (1,DefaultStyle,DefaultDelim)
162+ [[Plain [Str "Numbered",Space,Str "list",Space,Str "sub",Space,Str "sub",Space,Str "item",Space,Str "1"]]
163+ ,[Plain [Str "Numbered",Space,Str "list",Space,Str "sub",Space,Str "sub",Space,Str "item",Space,Str "2"]]]]
164+ ,[Plain [Str "etc."]]]]
165+ ,[Plain [Str "Bulleted",Space,Str "list",Space,Str "item",Space,Str "3"]]]
166+,Header 2 ("todo lists",[],[]) [Str "todo",Space,Str "lists"]
167+,BulletList
168+ [[Plain [Span ("",["done0"],[]) [],Str "task",Space,Str "1"]
169+ ,OrderedList (1,DefaultStyle,DefaultDelim)
170+ [[Plain [Span ("",["done1"],[]) [],Str "5"]]]]
171+ ,[Plain [Span ("",["done2"],[]) [],Str "3"]]
172+ ,[Plain [Str "[]",Space,Str "not",Space,Str "a",Space,Str "todo",Space,Str "item"]]
173+ ,[Plain [Str "[",Space,Str "]not",Space,Str "a",Space,Str "todo",Space,Str "item"]]
174+ ,[Plain [Str "[r]",Space,Str "not",Space,Str "a",Space,Str "todo",Space,Str "item"]]
175+ ,[Plain [Str "[",Space,Str "]",Space,Str "not",Space,Str "a",Space,Str "todo",Space,Str "item"]]
176+ ,[Plain [Span ("",["done2"],[]) [],Str "a",Space,Str "tab",Space,Str "in",Space,Str "the",Space,Str "todo",Space,Str "list",Space,Str "marker",Space,Code ("",[],[]) "[ ]"]
177+ ,OrderedList (1,DefaultStyle,DefaultDelim)
178+ [[Plain [Span ("",["done3"],[]) [],Str "4",SoftBreak,Str "5"]]
179+ ,[Plain [Span ("",["done4"],[]) []]
180+ ,Table [] [AlignDefault,AlignDefault] [0.0,0.0]
181+ [[]
182+ ,[]]
183+ [[[Plain [Str "a"]]
184+ ,[Plain [Str "b"]]]]]]]
185+ ,[Plain [Span ("",["done4"],[]) [],Str "task",Space,Str "2"]]]
186+,Header 2 ("math",[],[]) [Str "math"]
187+,Para [Math InlineMath " \\sum_i a_i^2 = 1 "]
188+,Para [Math DisplayMath "\\sum_i a_i^2\n=\n1"]
189+,Para [Math DisplayMath "\\begin{aligned}\n\\sum_i a_i^2 &= 1 + 1 \\\\\n&= 2.\n\\end{aligned}"]
190+,Para [Str "edge",Space,Str "case",Space,Str "(the",Space,Code ("",[],[]) "c^2 + ",Space,Str "after",Space,Str "the",Space,Str "multline",Space,Str "tag",Space,Str "is",Space,Str "in",Space,Str "the",Space,Str "equation):"]
191+,Para [Math DisplayMath "\\begin{gathered}\nc^2 + \na^2 + b^2\n\\end{gathered}"]
192+,Para [Str "edge",Space,Str "case",Space,Str "(the",Space,Str "tag",Space,Str "is",Space,Code ("",[],[]) "hello%bye",Str ")"]
193+,Para [Math DisplayMath "\\begin{hello%bye}\n\\int_a^b f(x) dx\n\\end{hello%bye}"]
194+,Para [Str "Just",Space,Str "two",Space,Str "dollar",Space,Str "signs:",Space,Str "$$"]
195+,Para [Str "[not",Space,Str "math]",Space,Str "You",Space,Str "have",Space,Str "$1",SoftBreak,Str "and",Space,Str "I",Space,Str "have",Space,Str "$1."]
196+,Header 2 ("tags",[],[]) [Str "tags"]
197+,Para [Span ("-tag-one",[],[]) [Str ""],Span ("tag-one",["tag"],[]) [Str "tag-one"],Space,Span ("-tag-two",[],[]) [Str ""],Span ("tag-two",["tag"],[]) [Str "tag-two"]]
198+,Header 2 ("tables",[],[]) [Str "tables"]
199+,Table [] [AlignDefault,AlignDefault,AlignDefault] [0.0,0.0,0.0]
200+ [[Plain [Str "Year"]]
201+ ,[Plain [Str "Temperature",Space,Str "(low)"]]
202+ ,[Plain [Str "Temperature",Space,Str "(high)"]]]
203+ [[[Plain [Str "1900"]]
204+ ,[Plain [Str "-10"]]
205+ ,[Plain [Str "25"]]]
206+ ,[[Plain [Str "1910"]]
207+ ,[Plain [Str "-15"]]
208+ ,[Plain [Str "30"]]]
209+ ,[[Plain [Str "1920"]]
210+ ,[Plain [Str "-10"]]
211+ ,[Plain [Str "32"]]]
212+ ,[[Plain [Str "1930"]]
213+ ,[Plain [Emph [Str "N/A"]]]
214+ ,[Plain [Emph [Str "N/A"]]]]
215+ ,[[Plain [Str "1940"]]
216+ ,[Plain [Str "-2"]]
217+ ,[Plain [Str "40"]]]]
218+,Header 3 ("centered headerless tables",[],[]) [Str "centered",Space,Str "headerless",Space,Str "tables"]
219+,Div ("",["center"],[])
220+ [Table [] [AlignDefault,AlignDefault] [0.0,0.0]
221+ [[]
222+ ,[]]
223+ [[[Plain [Str "a"]]
224+ ,[Plain [Str "b"]]]
225+ ,[[Plain [Str "c"]]
226+ ,[Plain [Str "d"]]]]]
227+,Header 2 ("paragraphs",[],[]) [Str "paragraphs"]
228+,Para [Str "This",Space,Str "is",Space,Str "first",Space,Str "paragraph",SoftBreak,Str "with",Space,Str "two",Space,Str "lines."]
229+,Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "second",Space,Str "paragraph",Space,Str "with",SoftBreak,Str "two",Space,Str "lines",Space,Str "after",Space,Str "many",Space,Str "blank",Space,Str "lines."]
230+,Header 2 ("definition list",[],[]) [Str "definition",Space,Str "list"]
231+,DefinitionList
232+ [([Str "Term",Space,Str "1"],
233+ [[Plain [Str "Definition",Space,Str "1"]]])
234+ ,([Str "Term",Space,Str "2"],
235+ [[Plain [Str "Definition",Space,Str "2"]]
236+ ,[Plain [Str "Definition",Space,Str "3"]]])
237+ ,([Str "Term",Space,Str "::",Space,Span ("separated",[],[]) [],Strong [Str "separated"],Space,Str "by",Space,Str "::",Space,Emph [Str "double",Space,Str "colons"]],
238+ [[Plain [Str "Def1"]]
239+ ,[Plain [Str "Def2"]]])
240+ ,([Str "Term",Space,Str "with",Space,Str "lots",Space,Str "of",Space,Str "trailing",Space,Str "colons:::::::"],
241+ [[Plain [Str "Definition"]]])
242+ ,([Str "::",Space,Str "This",Space,Str "is",Space,Str "::",Space,Str "A",Space,Str "term",Space,Str "(rather",Space,Str "than",Space,Str "a",Space,Str "definition)"],
243+ [[Plain [Str "and",Space,Str "this",Space,Str "is",Space,Str "a",Space,Str "definition"]]])
244+ ,([Str "Term",Space,Str "Without",Space,Str "definitions"],
245+ [[]])
246+ ,([Str "Part",Space,Str "::",Space,Str "of",Space,Str "::",Space,Str "dt"],
247+ [[Plain [Str "part",Space,Str "of",Space,Str "::dd"]]])]
248+,DefinitionList
249+ [([],
250+ [[Plain [Str "Definition",Space,Str "1",Space,Str "without",Space,Str "a",Space,Str "term"]]
251+ ,[Plain [Str "Definition",Space,Str "2",Space,Str "without",Space,Str "a",Space,Str "term"]]])]
252+,DefinitionList
253+ [([Str "T1"],
254+ [[Plain [Str "D1"]]])]
255+,Para [Str "new",Space,Str "paragraph"]
256+,DefinitionList
257+ [([Str "T1"],
258+ [[Plain [Str "D1"]]])]
259+,Para [Str "Not::Definition"]
260+,Para [Str "Not",Space,Str "::Definition"]
261+,Para [Str "::Not",Space,Str "definition"]
262+,BlockQuote
263+ [Plain [Str "::",Space,Str "blockquote"]]
264+,BlockQuote
265+ [Plain [Str "block",Space,Str "::",Space,Str "quote"]]
266+,Header 2 ("metadata placeholders",[],[]) [Str "metadata",Space,Str "placeholders"]
267+,Para [Str "%this",Space,Str "is",Space,Str "not",Space,Str "a",Space,Str "placeholder"]
268+,Para [Str "placeholders",SoftBreak,Str "serves",Space,Str "as",Space,Str "space",Space,Str "/",Space,Str "softbreak",Space,Str "in",Space,Str "paragraphs"]
269+,Header 2 ("sup, sub",[],[]) [Str "sup,",Space,Str "sub"]
270+,Para [Str "super",Superscript [Str "script"]]
271+,Para [Str "sub",Subscript [Str "script"]]
272+,Header 2 ("the todo mark",[],[]) [Str "the",Space,Str "todo",Space,Str "mark"]
273+,Para [Span ("",["todo"],[]) [Str "TODO:"]]
274+,Header 1 ("not implemented yet",[],[]) [Emph [Span ("not implemented yet",[],[]) [],Strong [Str "not",Space,Str "implemented",Space,Str "yet"]]]
275+,Header 2 ("tables with spans",[],[]) [Str "tables",Space,Str "with",Space,Str "spans"]
276+,Table [] [AlignDefault,AlignDefault,AlignDefault,AlignDefault] [0.0,0.0,0.0,0.0]
277+ [[]
278+ ,[]
279+ ,[]
280+ ,[]]
281+ [[[Plain [Str "a"]]
282+ ,[Plain [Str "b"]]
283+ ,[Plain [Str "c"]]
284+ ,[Plain [Str "d"]]]
285+ ,[[Plain [Str "\\/"]]
286+ ,[Plain [Str "e"]]
287+ ,[Plain [Str ">"]]
288+ ,[Plain [Str "f"]]]
289+ ,[[Plain [Str "\\/"]]
290+ ,[Plain [Str "\\/"]]
291+ ,[Plain [Str ">"]]
292+ ,[Plain [Str "g"]]]
293+ ,[[Plain [Str "h"]]
294+ ,[Plain [Str ">"]]
295+ ,[Plain [Str ">"]]
296+ ,[Plain [Str ">"]]]]
297+,Header 2 ("tables with multiple lines of headers",[],[]) [Str "tables",Space,Str "with",Space,Str "multiple",Space,Str "lines",Space,Str "of",Space,Str "headers"]
298+,Table [] [AlignDefault,AlignDefault] [0.0,0.0]
299+ [[]
300+ ,[]]
301+ [[[Plain [Str "a"]]
302+ ,[Plain [Str "b"]]]
303+ ,[[Plain [Str "c"]]
304+ ,[Plain [Str "d"]]]
305+ ,[[Plain [Str "---"]]
306+ ,[Plain [Str "---"]]]]
307+,Header 2 ("some other placeholders",[],[]) [Str "some",Space,Str "other",Space,Str "placeholders"]
308+,Para [Code ("",[],[]) "template",Space,Str "placeholder",Space,Str "is",Space,Str "ignored."]
309+,Para [Code ("",[],[]) "nohtml",Space,Str "placeholder",Space,Str "is",Space,Str "ignored."]]
test/vimwiki-reader.wikinew file+424
@@ -0,0 +1,424 @@
1+= _*implemented*_ =
2+= header =
3+
4+== header level two ==
5+
6+=== header `level` 3 ===
7+
8+==== header ~~level~~ four ====
9+
10+===== header _*level* 5_ =====
11+
12+====== header level 6 ======
13+
14+======= not a header ========
15+
16+hi== not a header ==
17+
18+=== not a header ==
19+
20+=== not a header ===-
21+
22+not a header:
23+
24+=n=
25+
26+=== not a header ====
27+
28+ == centred header ==
29+
30+== header with some `==` in between ==
31+== header with some == in between ==
32+== header with some ==in between ==
33+
34+== emph strong and strikeout ==
35+
36+_emph_ *strong*
37+
38+*_strong and emph_*
39+
40+_*emph and strong*_
41+
42+*_emph inside_ strong*
43+
44+*strong with _emph_*
45+
46+_*strong inside* emph_
47+
48+_~~strikeout~~ inside emph_
49+
50+~~This is _struck out_ with emph~~
51+
52+*not
53+strong*
54+
55+just two stars: **
56+
57+just two underscores: __
58+
59+just four ~s: ~~~~
60+
61+_not
62+%%comment
63+emph_
64+
65+~~not
66+ %%comment
67+ %%comment
68+strikeout~~
69+
70+== horizontal rule ==
71+
72+top
73+----
74+middle
75+
76+-------
77+
78+not a rule-----
79+
80+not a rule (trailing spaces):
81+-----
82+
83+not a rule (leading spaces):
84+ ----
85+
86+== comments ==
87+
88+%% you can't see me.
89+
90+this
91+%% secret
92+is %% not secret
93+
94+== inline code ==
95+
96+Here is some `inline code`.
97+
98+Just two backticks: ``
99+
100+== preformatted text ==
101+
102+{{{
103+ Tyger! Tyger! burning bright
104+ In the forests of the night,
105+ What immortal hand or eye
106+ Could frame thy fearful symmetry?
107+ In what distant deeps or skies
108+ Burnt the fire of thine eyes?
109+ On what wings dare he aspire?
110+ What the hand dare sieze the fire?
111+}}}
112+
113+=== preformatted text with attributes ===
114+
115+ {{{class="python" style="color:blue"
116+ for i in range(1, 5):
117+ print(i)
118+ }}}
119+
120+== block quotes ==
121+
122+ (indentation 4 spaces) This would be a blockquote in Vimwiki. It is not *highlighted* in Vim but
123+ (indentation 1 space followed by 1 tab of width 4) could be styled by CSS in HTML. Blockquotes are usually used to quote a
124+ (indentation 1 tab of width 4) long piece of text from another source. ~~blah blah~~ :blockquote:
125+
126+== external links ==
127+
128+[[http://google.com|_Google_ search engine]]
129+
130+http://pandoc.org
131+
132+ftp://vim.org
133+
134+[[http://google.com]]
135+
136+[[mailto:info@example.org|email me]]
137+
138+mailto:hello@bye.com
139+
140+== internal links ==
141+
142+[[This is a link]]
143+
144+[[This is a link source|Description of the link]]
145+
146+[[projects/Important Project 1]]
147+
148+[[../index]]
149+
150+[[a subdirectory/|Other files]]
151+
152+[[#tag-one|try me to test tag anchors]]
153+
154+[[#block quotes|try me to test header anchors]]
155+
156+[[#strong|try me to test strong anchors]]
157+
158+[[Todo List#Tomorrow|Tasks for tomorrow]]
159+
160+[[diary:2017-05-01]]
161+
162+[[file:../assets/data.csv|Important Data]]
163+
164+=== links with thumbnails ===
165+[[http://www.google.com|{{./movie.jpg}}]]
166+
167+== images ==
168+
169+{{file:./lalune.jpg}}
170+
171+{{http://vimwiki.googlecode.com/hg/images/vimwiki_logo.png|Vimwiki}}
172+
173+{{local:./movie.jpg}}
174+
175+
176+=== image with attributes ===
177+{{lalune.jpg|_cool stuff_|style="width:150px;height:120px;"}}
178+
179+{{nonexist.jpg|*Non-existing* image|class="center flow blabla" style="font-color:red"}}
180+
181+{{lalune.jpg|_cool stuff_|style="width:150px;height:120px;"|anything in this segment is ignored}}
182+
183+
184+== lists ==
185+
186+
187+# ordered list item 1, and here is some math belonging to list item 1
188+ {{$
189+ a^2 + b^2 = c^2
190+ }}$
191+ and some preformatted and tables belonging to item 1 as well
192+{{{
193+I'm part of item 1.
194+}}}
195+| this table | is |
196+| also a part | of item 1 |
197+ and some more text belonging to item 1.
198+# ordered list item 2
199+
200+
201+* Bulleted list item 1
202+* Bulleted list item 2
203+
204+
205+# Bulleted list item 1
206+# the # become numbers when converted to HTML
207+
208+- Bulleted list item 1
209+- Bulleted list item 2
210+
211+* Item 1
212+* Item 2
213+ # Sub item 1 (indentation 4 spaces)
214+ Sub item 1 continued line.
215+%%comments
216+ Sub item 1 next continued line.
217+ * Sub item 2, as an ordered list item even though the identifier is `*` (indentation 2 spaces followed by one tab of width 4)
218+ * etc.
219+ Continuation of Item 2
220+ Next continuation of Item 2
221+But this is a new paragraph.
222+
223+# 1
224+ * `1.1`
225+ * 2
226+ * 2.1
227+ * 3
228+
229+=== ordered lists with non-# identifiers ===
230+1. Numbered list item 1
231+2. Numbered list item 2
232+3. Numbered list item 3
233+
234+4. Numbered list item 1
235+5. Numbered list item 2
236+6. Numbered list item 3
237+
238+1) Numbered list item 1
239+2) Numbered list item 2
240+3) Numbered list item 3
241+
242+a) Numbered list item 1
243+b) Numbered list item 2
244+c) Numbered list item 3
245+
246+A) Numbered list item 1
247+B) Numbered list item 2
248+C) Numbered list item 3
249+
250+i) Numbered list item 1
251+ii) Numbered list item 2
252+iii) Numbered list item 3
253+
254+I) Numbered list item 1
255+II) Numbered list item 2
256+III) Numbered list item 3
257+
258+- Bulleted list item 1
259+- Bulleted list item 2
260+ a) Numbered list sub item 1
261+ b) more ...
262+ * and more ...
263+ * ...
264+ c) Numbered list sub item 3
265+ 1. Numbered list sub sub item 1
266+ 2. Numbered list sub sub item 2
267+ d) etc.
268+- Bulleted list item 3
269+
270+== todo lists ==
271+* [ ] task 1
272+ 1. [.] 5
273+* [o] 3
274+* [] not a todo item
275+* [ ]not a todo item
276+* [r] not a todo item
277+* [ ] not a todo item
278+* [o] a tab in the todo list marker `[ ]`
279+ III) [O] 4
280+ 5
281+ i) [X]
282+| a | b |
283+* [X] task 2
284+
285+== math ==
286+
287+$ \sum_i a_i^2 = 1 $
288+
289+{{$
290+\sum_i a_i^2
291+=
292+1
293+}}$
294+
295+{{$%align%
296+\sum_i a_i^2 &= 1 + 1 \\
297+&= 2.
298+}}$
299+
300+edge case (the `c^2 + ` after the multline tag is in the equation):
301+{{$%multline%c^2 +
302+a^2 + b^2
303+}}$
304+
305+edge case (the tag is `hello%bye`)
306+{{$%hello%bye%
307+\int_a^b f(x) dx
308+}}$
309+
310+Just two dollar signs: $$
311+
312+[not math] You have $1
313+and I have $1.
314+
315+== tags ==
316+
317+:tag-one:tag-two:
318+
319+== tables ==
320+
321+| Year | Temperature (low) | Temperature (high) |
322+|------|-------------------|--------------------|
323+| 1900 | -10 | 25 |
324+| 1910 | -15 | 30 |
325+| 1920 | -10 | 32 |
326+| 1930 | _N/A_ | _N/A_ |
327+| 1940 | -2 | 40 |
328+
329+
330+=== centered headerless tables ===
331+ | a | b |
332+ | c | d |
333+
334+
335+== paragraphs ==
336+
337+This is first paragraph
338+with two lines.
339+
340+
341+
342+
343+
344+
345+
346+
347+This is a second paragraph with
348+two lines after many blank lines.
349+
350+== definition list ==
351+
352+Term 1:: Definition 1
353+Term 2::
354+:: Definition 2
355+ :: Definition 3
356+Term :: *separated* by :: _double colons_ :: Def1
357+:: Def2
358+Term with lots of trailing colons::::::::: Definition
359+:: This is :: A term (rather than a definition) :: and this is a definition
360+Term Without definitions ::
361+::
362+Part :: of :: dt :: part of ::dd
363+
364+:: Definition 1 without a term
365+:: Definition 2 without a term
366+
367+T1 :: D1
368+new paragraph
369+T1 :: D1
370+
371+Not::Definition
372+
373+Not ::Definition
374+
375+::Not definition
376+
377+ :: blockquote
378+
379+ block :: quote
380+
381+== metadata placeholders ==
382+%title title
383+%date 2017-05-01
384+
385+%title second title is ignored
386+%date second date is ignored
387+
388+%this is not a placeholder
389+
390+placeholders
391+%title another title
392+%date 2017-04-23
393+serves as space / softbreak in paragraphs
394+
395+
396+== sup, sub ==
397+
398+super^script^
399+
400+sub,,script,,
401+
402+== the todo mark ==
403+TODO:
404+
405+= _*not implemented yet*_ =
406+== tables with spans ==
407+| a | b | c | d |
408+| \/ | e | > | f |
409+| \/ | \/ | > | g |
410+| h | > | > | > |
411+
412+== tables with multiple lines of headers ==
413+| a | b |
414+| c | d |
415+|---|---|
416+
417+== some other placeholders ==
418+`template` placeholder is ignored.
419+%template template
420+
421+`nohtml` placeholder is ignored.
422+%nohtml
423+
424+
test/writer.jats+1−1
@@ -1085,7 +1085,7 @@ These should not be escaped: \$ \\ \&gt; \[ \{</preformat>
10851085 <p>
10861086 Here’s some display math: <disp-formula><alternatives>
10871087 <tex-math><![CDATA[\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}]]></tex-math>
1088− <mml:math display="block" xmlns:mml="http://www.w3.org/1998/Math/MathML"><mml:mrow><mml:mfrac><mml:mi>d</mml:mi><mml:mrow><mml:mi>d</mml:mi><mml:mi>x</mml:mi></mml:mrow></mml:mfrac><mml:mi>f</mml:mi><mml:mo stretchy="false" form="prefix">(</mml:mo><mml:mi>x</mml:mi><mml:mo stretchy="false" form="postfix">)</mml:mo><mml:mo>=</mml:mo><mml:munder><mml:mo>lim</mml:mo><mml:mrow><mml:mi>h</mml:mi><mml:mo accent="false">→</mml:mo><mml:mn>0</mml:mn></mml:mrow></mml:munder><mml:mfrac><mml:mrow><mml:mi>f</mml:mi><mml:mo stretchy="false" form="prefix">(</mml:mo><mml:mi>x</mml:mi><mml:mo>+</mml:mo><mml:mi>h</mml:mi><mml:mo stretchy="false" form="postfix">)</mml:mo><mml:mo>−</mml:mo><mml:mi>f</mml:mi><mml:mo stretchy="false" form="prefix">(</mml:mo><mml:mi>x</mml:mi><mml:mo stretchy="false" form="postfix">)</mml:mo></mml:mrow><mml:mi>h</mml:mi></mml:mfrac></mml:mrow></mml:math></alternatives></disp-formula>
1088+ <mml:math display="block" xmlns:mml="http://www.w3.org/1998/Math/MathML"><mml:mrow><mml:mfrac><mml:mi>d</mml:mi><mml:mrow><mml:mi>d</mml:mi><mml:mi>x</mml:mi></mml:mrow></mml:mfrac><mml:mi>f</mml:mi><mml:mo stretchy="false" form="prefix">(</mml:mo><mml:mi>x</mml:mi><mml:mo stretchy="false" form="postfix">)</mml:mo><mml:mo>=</mml:mo><mml:munder><mml:mo>lim</mml:mo><mml:mrow><mml:mi>h</mml:mi><mml:mo>→</mml:mo><mml:mn>0</mml:mn></mml:mrow></mml:munder><mml:mfrac><mml:mrow><mml:mi>f</mml:mi><mml:mo stretchy="false" form="prefix">(</mml:mo><mml:mi>x</mml:mi><mml:mo>+</mml:mo><mml:mi>h</mml:mi><mml:mo stretchy="false" form="postfix">)</mml:mo><mml:mo>−</mml:mo><mml:mi>f</mml:mi><mml:mo stretchy="false" form="prefix">(</mml:mo><mml:mi>x</mml:mi><mml:mo stretchy="false" form="postfix">)</mml:mo></mml:mrow><mml:mi>h</mml:mi></mml:mfrac></mml:mrow></mml:math></alternatives></disp-formula>
10891089 </p>
10901090 </list-item>
10911091 <list-item>
test/writer.muse+3−3
@@ -65,8 +65,8 @@ sub status {
6565
6666A list:
6767
68−1. item one
69−2. item two
68+ 1. item one
69+ 2. item two
7070
7171Nested block quotes:
7272
@@ -281,7 +281,7 @@ Multiple blocks with italics:
281281 </example>
282282
283283 <quote>
284−orange block quote
284+ orange block quote
285285 </quote>
286286
287287Multiple definitions, tight:
trypandoc/Makefile+1−1
@@ -1,5 +1,5 @@
11CGIBIN=/home/website/cgi-bin
2−TRYPANDOC=/home/website/html/pandoc/try/
2+TRYPANDOC=/home/website/pandoc.org/try/
33CGI=${CGIBIN}/trypandoc
44BIN=/home/jgm/.local/bin/trypandoc
55
trypandoc/index.html+2
@@ -88,11 +88,13 @@ $(document).ready(function() {
8888 <option value="markdown_github">Markdown (GitHub)</option>
8989 <option value="mediawiki">MediaWiki</option>
9090 <option value="markdown_mmd">MultiMarkdown</option>
91+ <option value="muse">Muse</option>
9192 <option value="opml">OPML</option>
9293 <option value="org">Org Mode</option>
9394 <option value="rst">reStructuredText</option>
9495 <option value="textile">Textile</option>
9596 <option value="t2t">Txt2Tags</option>
97+ <option value="vimwiki">Vimwiki</option>
9698 </select>
9799 <br/>
98100 <textarea id="text" maxlength="3000" rows="15"></textarea>
trypandoc/trypandoc.hs+8−5
@@ -3,15 +3,15 @@ module Main where
33import Network.Wai.Handler.CGI
44import Network.Wai
55import Control.Applicative ((<$>))
6−import Data.Maybe (mapMaybe, fromMaybe)
6+import Data.Maybe (fromMaybe)
77import Network.HTTP.Types.Status (status200)
88import Network.HTTP.Types.Header (hContentType)
99import Network.HTTP.Types.URI (queryToQueryText)
1010import Text.Pandoc
11+import Text.Pandoc.Writers.Math (defaultMathJaxURL)
1112import Text.Pandoc.Highlighting (pygments)
1213import Text.Pandoc.Readers (getReader, Reader(..))
1314import Text.Pandoc.Writers (getWriter, Writer(..))
14−import Text.Pandoc.Error (PandocError)
1515import Text.Pandoc.Shared (tabFilter)
1616import Data.Aeson
1717import qualified Data.Text as T
@@ -29,11 +29,13 @@ app req respond = do
2929 fromFormat <- fromMaybe "" <$> getParam "from"
3030 toFormat <- fromMaybe "" <$> getParam "to"
3131 let reader = case getReader (T.unpack fromFormat) of
32− Right (TextReader r) -> r readerOpts
32+ Right (TextReader r, es) -> r readerOpts{
33+ readerExtensions = es }
3334 _ -> error $ "could not find reader for "
3435 ++ T.unpack fromFormat
3536 let writer = case getWriter (T.unpack toFormat) of
36− Right (TextWriter w) -> w writerOpts
37+ Right (TextWriter w, es) -> w writerOpts{
38+ writerExtensions = es }
3739 _ -> error $ "could not find writer for " ++
3840 T.unpack toFormat
3941 let result = case runPure $ reader (tabFilter 4 text) >>= writer of
@@ -56,7 +58,8 @@ checkLength t =
5658writerOpts :: WriterOptions
5759writerOpts = def { writerReferenceLinks = True,
5860 writerEmailObfuscation = NoObfuscation,
59− writerHTMLMathMethod = MathJax "http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML",
61+ writerHTMLMathMethod = MathJax (defaultMathJaxURL ++
62+ "MathJax.js?config=TeX-AMS_CHTML-full"),
6063 writerHighlightStyle = Just pygments }
6164
6265readerOpts :: ReaderOptions