git.iko.soy/typeable/pandoc
on master help sign in
Merge branch 'typeclass'
commitae8ac926a43ed48316081b7272701fba3884dbf5 authorJohn MacFarlane <jgm@berkeley.edu> dateSun 29 Jan 2017, 22:13 (10 years ago) parent661f1ada62550f
139 files changed+5962−4407
.travis.yml+1 CONTRIBUTING.md+22 HCAR-Pandoc.tex−35 Interact.hs−34 MANUAL.txt+92−110 PROFILING−6 RELEASE-CHECKLIST.md+27 benchmark/benchmark-pandoc.hs+42−9 benchmark/weigh-pandoc.hs+4−4 data/templates+1−1 pandoc.cabal+24−17 pandoc.hs+410−465 src/Text/Pandoc.hs+139−154 src/Text/Pandoc/Class.hs+585 src/Text/Pandoc/Error.hs+25−14 src/Text/Pandoc/Extensions.hs+267 src/Text/Pandoc/ImageSize.hs+3−3 src/Text/Pandoc/MediaBag.hs+7−3 src/Text/Pandoc/Options.hs+17−248 src/Text/Pandoc/PDF.hs+58−52 src/Text/Pandoc/Parsing.hs+26−44 src/Text/Pandoc/Process.hs+8−14 src/Text/Pandoc/Readers/CommonMark.hs+5−4 src/Text/Pandoc/Readers/DocBook.hs+16−16 src/Text/Pandoc/Readers/Docx.hs+46−53 src/Text/Pandoc/Readers/Docx/Parse.hs+1−1 src/Text/Pandoc/Readers/EPUB.hs+28−34 src/Text/Pandoc/Readers/HTML.hs+119−105 src/Text/Pandoc/Readers/Haddock.hs+17−11 src/Text/Pandoc/Readers/LaTeX.hs+146−211 src/Text/Pandoc/Readers/Markdown.hs+272−249 src/Text/Pandoc/Readers/MediaWiki.hs+77−76 src/Text/Pandoc/Readers/Native.hs+12−4 src/Text/Pandoc/Readers/OPML.hs+20−18 src/Text/Pandoc/Readers/Odt.hs+20−6 src/Text/Pandoc/Readers/Odt/ContentReader.hs+2−2 src/Text/Pandoc/Readers/Org.hs+13−6 src/Text/Pandoc/Readers/Org/BlockStarts.hs+17−16 src/Text/Pandoc/Readers/Org/Blocks.hs+87−84 src/Text/Pandoc/Readers/Org/ExportSettings.hs+18−15 src/Text/Pandoc/Readers/Org/Inlines.hs+130−118 src/Text/Pandoc/Readers/Org/Meta.hs+23−22 src/Text/Pandoc/Readers/Org/ParserState.hs+2−2 src/Text/Pandoc/Readers/Org/Parsing.hs+19−17 src/Text/Pandoc/Readers/RST.hs+224−136 src/Text/Pandoc/Readers/TWiki.hs+87−86 src/Text/Pandoc/Readers/TeXMath.hs−48 src/Text/Pandoc/Readers/Textile.hs+96−93 src/Text/Pandoc/Readers/Txt2Tags.hs+41−22 src/Text/Pandoc/SelfContained.hs+8−5 src/Text/Pandoc/Shared.hs+19−256 src/Text/Pandoc/Templates.hs+8−5 src/Text/Pandoc/UUID.hs+12−11 src/Text/Pandoc/Writers/AsciiDoc.hs+4−3 src/Text/Pandoc/Writers/CommonMark.hs+73−63 src/Text/Pandoc/Writers/ConTeXt.hs+4−3 src/Text/Pandoc/Writers/Docbook.hs+179−143 src/Text/Pandoc/Writers/Docx.hs+80−87 src/Text/Pandoc/Writers/DokuWiki.hs+15−7 src/Text/Pandoc/Writers/EPUB.hs+150−116 src/Text/Pandoc/Writers/FB2.hs+101−107 src/Text/Pandoc/Writers/HTML.hs+244−127 src/Text/Pandoc/Writers/Haddock.hs+39−27 src/Text/Pandoc/Writers/ICML.hs+26−22 src/Text/Pandoc/Writers/LaTeX.hs+68−41 src/Text/Pandoc/Writers/Man.hs+37−30 src/Text/Pandoc/Writers/Markdown.hs+100−67 src/Text/Pandoc/Writers/Math.hs+49 src/Text/Pandoc/Writers/MediaWiki.hs+4−3 src/Text/Pandoc/Writers/Native.hs+3−2 src/Text/Pandoc/Writers/ODT.hs+49−31 src/Text/Pandoc/Writers/OPML.hs+37−28 src/Text/Pandoc/Writers/OpenDocument.hs+77−59 src/Text/Pandoc/Writers/Org.hs+4−3 src/Text/Pandoc/Writers/RST.hs+3−2 src/Text/Pandoc/Writers/RTF.hs+172−126 src/Text/Pandoc/Writers/TEI.hs+3−2 src/Text/Pandoc/Writers/Texinfo.hs+67−46 src/Text/Pandoc/Writers/Textile.hs+4−3 src/Text/Pandoc/Writers/ZimWiki.hs+4−3 stack.yaml+1−1 tests/Tests/Helpers.hs+12−7 tests/Tests/Old.hs+12−17 tests/Tests/Readers/Docx.hs+15−12 tests/Tests/Readers/EPUB.hs+4−2 tests/Tests/Readers/HTML.hs+1−1 tests/Tests/Readers/LaTeX.hs+2−1 tests/Tests/Readers/Markdown.hs+12−10 tests/Tests/Readers/Odt.hs+13−12 tests/Tests/Readers/Org.hs+5−4 tests/Tests/Readers/RST.hs+1−1 tests/Tests/Readers/Txt2Tags.hs+11−4 tests/Tests/Shared.hs+4−24 tests/Tests/Walk.hs−46 tests/Tests/Writers/AsciiDoc.hs+1−1 tests/Tests/Writers/ConTeXt.hs+2−2 tests/Tests/Writers/Docbook.hs+1−1 tests/Tests/Writers/Docx.hs+8−6 tests/Tests/Writers/HTML.hs+2−2 tests/Tests/Writers/LaTeX.hs+6−4 tests/Tests/Writers/Markdown.hs+10−7 tests/Tests/Writers/Native.hs+2−2 tests/Tests/Writers/Plain.hs+1−1 tests/Tests/Writers/RST.hs+3−3 tests/Tests/Writers/TEI.hs+1−1 tests/fb2/basic.fb2+2−1 tests/fb2/titles.fb2+2−1 tests/lhs-test.html+8−5 tests/lhs-test.html+lhs+8−5 tests/markdown-citations.native+8−8 tests/rst-reader.native−6 tests/tables-rstsubset.native+4−4 tests/tables.asciidoc+4−4 tests/tables.docbook → tests/tables.docbook4+4−4 tests/tables.docbook5+4−4 tests/tables.dokuwiki+4−4 tests/tables.fb2+2−1 tests/tables.haddock+4−4 tests/tables.html → tests/tables.html4+4−4 tests/tables.html5+204 tests/tables.icml+5−5 tests/tables.man+4−4 tests/tables.mediawiki+4−4 tests/tables.native+4−4 tests/tables.opendocument+4−4 tests/tables.plain+4−4 tests/tables.rst+4−4 tests/tables.rtf+5−4 tests/tables.tei+3−3 tests/tables.zimwiki+4−4 tests/test-pandoc.hs−2 tests/textile-reader.native+8−8 tests/writer.docbook → tests/writer.docbook4 tests/writer.html → tests/writer.html4 tests/writer.html5+548 tests/writer.markdown+27−27 tests/writer.opml+7−7 extract-changes.hs → tools/extract-changes.hs github-upload.sh → tools/github-upload.sh
.travis.yml+1
@@ -73,6 +73,7 @@ matrix:
7373
7474 allow_failures:
7575 - env: BUILD=cabal GHCVER=8.0.2 CABALVER=1.24
76+ - env: BUILD=cabal GHCVER=7.8.4 CABALVER=1.18
7677 - env: BUILD=cabal GHCVER=head CABALVER=head
7778 - env: BUILD=stack ARGS="--resolver nightly"
7879
CONTRIBUTING.md+22
@@ -176,6 +176,11 @@ With stack:
176176
177177 stack bench
178178
179+You can also build pandoc with the `weigh-pandoc` flag and
180+run `weigh-pandoc` to get some statistics on memory usage.
181+(Eventually this should be incorporated into the benchmark
182+suite.)
183+
179184Using the REPL
180185--------------
181186
@@ -193,6 +198,23 @@ placed in the source directory):
193198:set -XOverloadedStrings
194199```
195200
201+Profiling
202+---------
203+
204+To use the GHC profiler with cabal:
205+
206+ cabal clean
207+ cabal install --enable-library-profiling --enable-executable-profiling
208+ pandoc +RTS -p -RTS [file]...
209+ less pandoc.prof
210+
211+With stack:
212+
213+ stack clean
214+ stack install --profile
215+ pandoc +RTS -p -RTS [file]...
216+ less pandoc.prof
217+
196218The code
197219--------
198220
HCAR-Pandoc.texdeleted−35
@@ -1,35 +0,0 @@
1−% Pandoc-JP.tex
2−\begin{hcarentry}{Pandoc}
3−\label{pandoc}
4−\report{John MacFarlane}%05/11
5−\status{active development}
6−\participants{Andrea Rossato, Peter Wang, Paulo Tanimoto, Eric Kow,
7−Luke Plant, Justin Bogner, Paul Rivier, Nathan Gass, Puneeth Chaganti,
8−Josef Svenningsson, Etienne Millon, Joost Kremers}
9−\makeheader
10−
11−Pandoc aspires to be the swiss army knife of text markup formats: it
12−can read markdown and (with some limitations) HTML, LaTeX, Textile, and
13−reStructuredText, and it can write markdown, reStructuredText, HTML,
14−DocBook XML, OpenDocument XML, ODT, RTF, groff man, MediaWiki markup,
15−GNU Texinfo, LaTeX, ConTeXt, EPUB, Textile, Emacs org-mode,
16−Slidy, and S5. Pandoc's markdown syntax includes extensions for LaTeX math,
17−tables, definition lists, footnotes, and more.
18−
19−Since the last report, many new features have been added and improvements
20−made. Some highlights:
21−\begin{compactitem}
22−\item Support for Textile input and output.
23−\item Support for Emacs org-mode output.
24−\item A new ``builder'' module for constructing Pandoc documents programatically.
25−\item Support for \LaTeX math macros in markdown documents.
26−\item Support for automatic citations and bibliographies using Andrea
27−Rossato's citeproc-hs library.
28−\end{compactitem}
29−
30−These last two changes bring two of the most powerful features of \LaTeX
31−to pandoc.
32−
33−\FurtherReading
34− \url{http://pandoc.org}
35−\end{hcarentry}
Interact.hsdeleted−34
@@ -1,34 +0,0 @@
1−-- Get an interactive shell with the right packages to load
2−-- pandoc modules.
3−
4−-- To use:
5−-- runghc Interact.hs
6−-- then,
7−-- :l Text/Pandoc.hs
8−-- (or whichever package you like)
9−
10−-- You must have first done a 'cabal configure' or 'cabal install'
11−
12−-- Note: Interact.hs doesn't work with Cabal >= 1.18. I recommend
13−-- using cabal sandboxes and the new 'cabal repl' command if you are
14−-- using a recent version.
15−
16−import System.Process
17−import Distribution.Simple.LocalBuildInfo
18−import Distribution.Package
19−import Distribution.Version
20−import Data.List (intercalate)
21−
22−main = do
23− setupConfig' <- readFile "dist/setup-config"
24− let setupConfig = read $ unlines $ drop 1 $ lines setupConfig'
25− let (Just (ComponentLocalBuildInfo { componentPackageDeps = deps })) = libraryConfig setupConfig
26− let packageSpecs = map (toPackageSpec . snd) deps
27− let args = ["-optP-include", "-optP../dist/build/autogen/cabal_macros.h","-cpp","-I../dist/build/autogen","-i../dist/build/autogen"] ++ concatMap (\p -> ["-package",p]) packageSpecs
28− print args
29− ph <- runProcess "ghci" args (Just "src") Nothing Nothing Nothing Nothing
30− waitForProcess ph
31−
32−toPackageSpec pkg = pkgN ++ "-" ++ pkgV
33− where (PackageName pkgN) = pkgName pkg
34− pkgV = intercalate "." $ map show $ versionBranch $ pkgVersion pkg
MANUAL.txt+92−110
@@ -101,7 +101,7 @@ If no *input-file* is specified, input is read from *stdin*.
101101Otherwise, the *input-files* are concatenated (with a blank
102102line between each) and used as input. Output goes to *stdout* by
103103default (though output to *stdout* is disabled for the `odt`, `docx`,
104−`epub`, and `epub3` output formats). For output to a file, use the
104+`epub2`, and `epub3` output formats). For output to a file, use the
105105`-o` option:
106106
107107 pandoc -o output.html input.txt
@@ -273,15 +273,15 @@ General options
273273 (original unextended Markdown), `markdown_phpextra` (PHP Markdown
274274 Extra), `markdown_github` (GitHub-Flavored Markdown), `markdown_mmd`
275275 (MultiMarkdown), `commonmark` (CommonMark Markdown), `rst`
276− (reStructuredText), `html` (XHTML), `html5` (HTML5), `latex`
276+ (reStructuredText), `html4` (XHTML4), `html` or `html5` (HTML5), `latex`
277277 (LaTeX), `beamer` (LaTeX beamer slide show), `context` (ConTeXt),
278278 `man` (groff man), `mediawiki` (MediaWiki markup),
279279 `dokuwiki` (DokuWiki markup), `zimwiki` (ZimWiki markup),
280280 `textile` (Textile), `org` (Emacs Org mode),
281− `texinfo` (GNU Texinfo), `opml` (OPML), `docbook` (DocBook 4),
282− `docbook5` (DocBook 5), `opendocument` (OpenDocument), `odt`
283− (OpenOffice text document), `docx` (Word docx), `haddock`
284− (Haddock markup), `rtf` (rich text format), `epub` (EPUB v2
281+ `texinfo` (GNU Texinfo), `opml` (OPML), `docbook` or `docbook4`
282+ (DocBook 4), `docbook5` (DocBook 5), `opendocument` (OpenDocument),
283+ `odt` (OpenOffice text document), `docx` (Word docx), `haddock`
284+ (Haddock markup), `rtf` (rich text format), `epub` or `epub2` (EPUB v2
285285 book), `epub3` (EPUB v3), `fb2` (FictionBook2 e-book),
286286 `asciidoc` (AsciiDoc), `icml` (InDesign ICML), `tei` (TEI
287287 Simple), `slidy` (Slidy HTML and JavaScript slide show),
@@ -293,7 +293,7 @@ General options
293293 `epub`, and `epub3` output will not be directed to *stdout*;
294294 an output filename must be specified using the `-o/--output`
295295 option. If `+lhs` is appended to `markdown`, `rst`, `latex`,
296− `beamer`, `html`, or `html5`, the output will be rendered as
296+ `beamer`, `html4`, or `html5`, the output will be rendered as
297297 literate Haskell source: see [Literate Haskell support],
298298 below. Markdown syntax extensions can be individually
299299 enabled or disabled by appending `+EXTENSION` or
@@ -340,6 +340,14 @@ General options
340340: Give verbose debugging output. Currently this only has an effect
341341 with PDF output.
342342
343+`--quiet`
344+
345+: Suppress warning messages.
346+
347+`--fail-if-warnings`
348+
349+: Exit with error status if there are any warnings.
350+
343351`--list-input-formats`
344352
345353: List supported input formats, one per line.
@@ -386,21 +394,6 @@ Reader options
386394 HTML codes and LaTeX environments. (The LaTeX reader does pass through
387395 untranslatable LaTeX *commands*, even if `-R` is not specified.)
388396
389−`-S`, `--smart`
390−
391−: Produce typographically correct output, converting straight quotes
392− to curly quotes, `---` to em-dashes, `--` to en-dashes, and
393− `...` to ellipses. Nonbreaking spaces are inserted after certain
394− abbreviations, such as "Mr." (Note: This option is selected automatically
395− when the output format is `latex` or `context`, unless `--no-tex-ligatures`
396− is used. It has no effect for `latex` input.)
397−
398−`--old-dashes`
399−
400−: Selects the pandoc <= 1.8.2.1 behavior for parsing smart dashes: `-` before
401− a numeral is an en-dash, and `--` is an em-dash. This option is selected
402− automatically for `textile` input.
403−
404397`--base-header-level=`*NUMBER*
405398
406399: Specify the base level for headers (defaults to 1).
@@ -470,11 +463,6 @@ Reader options
470463 underlying document (which is accessible from filters and may be
471464 printed in some output formats).
472465
473−`--normalize`
474−
475−: Normalize the document after reading: merge adjacent
476− `Str` or `Emph` elements, for example, and remove repeated `Space`s.
477−
478466`-p`, `--preserve-tabs`
479467
480468: Preserve tabs instead of converting them to spaces (the default).
@@ -570,10 +558,6 @@ General writer options
570558 will be nonsemantic newlines in the output as well).
571559 Automatic wrapping does not currently work in HTML output.
572560
573−`--no-wrap`
574−
575−: Deprecated synonym for `--wrap=none`.
576−
577561`--columns=`*NUMBER*
578562
579563: Specify length of lines in characters. This affects text wrapping
@@ -585,7 +569,7 @@ General writer options
585569: Include an automatically generated table of contents (or, in
586570 the case of `latex`, `context`, `docx`, and `rst`, an instruction to create
587571 one) in the output document. This option has no effect on `man`,
588− `docbook`, `docbook5`, `slidy`, `slideous`, `s5`, or `odt` output.
572+ `docbook4`, `docbook5`, `slidy`, `slideous`, `s5`, or `odt` output.
589573
590574`--toc-depth=`*NUMBER*
591575
@@ -642,7 +626,7 @@ Options affecting specific writers
642626 images, and videos. The resulting file should be "self-contained,"
643627 in the sense that it needs no external files and no net access to be
644628 displayed properly by a browser. This option works only with HTML output
645− formats, including `html`, `html5`, `html+lhs`, `html5+lhs`, `s5`,
629+ formats, including `html4`, `html5`, `html+lhs`, `html5+lhs`, `s5`,
646630 `slidy`, `slideous`, `dzslides`, and `revealjs`. Scripts, images, and
647631 stylesheets at absolute URLs will be downloaded; those at relative URLs
648632 will be sought relative to the working directory (if the first source
@@ -682,10 +666,6 @@ Options affecting specific writers
682666: Use ATX-style headers in Markdown and AsciiDoc output. The default is
683667 to use setext-style headers for levels 1-2, and then ATX headers.
684668
685−`--chapters`
686−
687−: Deprecated synonym for `--top-level-division=chapter`.
688−
689669`--top-level-division=[default|section|chapter|part]`
690670
691671: Treat top-level headers as the given division type in LaTeX, ConTeXt,
@@ -717,22 +697,6 @@ Options affecting specific writers
717697 be numbered "1.5", specify `--number-offset=1,4`.
718698 Offsets are 0 by default. Implies `--number-sections`.
719699
720−`--no-tex-ligatures`
721−
722−: Do not use the TeX ligatures for quotation marks, apostrophes,
723− and dashes (`` `...' ``, ` ``..'' `, `--`, `---`) when
724− writing or reading LaTeX or ConTeXt. In reading LaTeX,
725− parse the characters `` ` ``, `'`, and `-` literally, rather
726− than parsing ligatures for quotation marks and dashes. In
727− writing LaTeX or ConTeXt, print unicode quotation mark and
728− dash characters literally, rather than converting them to
729− the standard ASCII TeX ligatures. Note: normally `--smart`
730− is selected automatically for LaTeX and ConTeXt output, but
731− it must be specified explicitly if `--no-tex-ligatures` is
732− selected. If you use literal curly quotes, dashes, and
733− ellipses in your source, then you may want to use
734− `--no-tex-ligatures` without `--smart`.
735−
736700`--listings`
737701
738702: Use the [`listings`] package for LaTeX code blocks
@@ -786,35 +750,20 @@ Options affecting specific writers
786750: Link to a CSS style sheet. This option can be used repeatedly to
787751 include multiple files. They will be included in the order specified.
788752
789−`--reference-odt=`*FILE*
790−
791−: Use the specified file as a style reference in producing an ODT.
792− For best results, the reference ODT should be a modified version
793− of an ODT produced using pandoc. The contents of the reference ODT
794− are ignored, but its stylesheets are used in the new ODT. If no
795− reference ODT is specified on the command line, pandoc will look
796− for a file `reference.odt` in the user data directory (see
797− `--data-dir`). If this is not found either, sensible defaults will be
798− used.
799−
800− To produce a custom `reference.odt`, first get a copy of
801− the default `reference.odt`: `pandoc
802− --print-default-data-file reference.odt >
803− custom-reference.odt`. Then open `custom-reference.docx` in
804− LibreOffice, modify the styles as you wish, and save the
805− file.
753+`--reference-doc=`*FILE*
806754
807−`--reference-docx=`*FILE*
755+: Use the specified file as a style reference in producing a
756+ docx or ODT file.
808757
809−: Use the specified file as a style reference in producing a docx file.
810− For best results, the reference docx should be a modified version
811− of a docx file produced using pandoc. The contents of the reference docx
812− are ignored, but its stylesheets and document properties (including
813− margins, page size, header, and footer) are used in the new docx. If no
814− reference docx is specified on the command line, pandoc will look
815− for a file `reference.docx` in the user data directory (see
816− `--data-dir`). If this is not found either, sensible defaults will be
817− used.
758+ Docx: For best results, the reference docx should be a modified
759+ version of a docx file produced using pandoc. The contents
760+ of the reference docx are ignored, but its stylesheets and
761+ document properties (including margins, page size, header,
762+ and footer) are used in the new docx. If no reference docx
763+ is specified on the command line, pandoc will look for a
764+ file `reference.docx` in the user data directory (see
765+ `--data-dir`). If this is not found either, sensible
766+ defaults will be used.
818767
819768 To produce a custom `reference.docx`, first get a copy of
820769 the default `reference.docx`: `pandoc
@@ -822,15 +771,30 @@ Options affecting specific writers
822771 custom-reference.docx`. Then open `custom-reference.docx`
823772 in Word, modify the styles as you wish, and save the file.
824773 For best results, do not make changes to this file other
825− than modifying the styles used by pandoc: [paragraph] Normal,
826− Body Text, First Paragraph, Compact, Title, Subtitle,
827− Author, Date, Abstract, Bibliography, Heading 1, Heading 2,
828− Heading 3, Heading 4, Heading 5, Heading 6, Block Text,
829− Footnote Text, Definition Term, Definition, Caption, Table
830− Caption, Image Caption, Figure, Figure With Caption, TOC
831− Heading; [character] Default Paragraph Font, Body Text Char,
832− Verbatim Char, Footnote Reference, Hyperlink; [table] Normal
833− Table.
774+ than modifying the styles used by pandoc: [paragraph]
775+ Normal, Body Text, First Paragraph, Compact, Title,
776+ Subtitle, Author, Date, Abstract, Bibliography, Heading 1,
777+ Heading 2, Heading 3, Heading 4, Heading 5, Heading 6, Block
778+ Text, Footnote Text, Definition Term, Definition, Caption,
779+ Table Caption, Image Caption, Figure, Figure With Caption,
780+ TOC Heading; [character] Default Paragraph Font, Body Text
781+ Char, Verbatim Char, Footnote Reference, Hyperlink; [table]
782+ Normal Table.
783+
784+ ODT: For best results, the reference ODT should be a modified
785+ version of an ODT produced using pandoc. The contents of
786+ the reference ODT are ignored, but its stylesheets are used
787+ in the new ODT. If no reference ODT is specified on the
788+ command line, pandoc will look for a file `reference.odt` in
789+ the user data directory (see `--data-dir`). If this is not
790+ found either, sensible defaults will be used.
791+
792+ To produce a custom `reference.odt`, first get a copy of
793+ the default `reference.odt`: `pandoc
794+ --print-default-data-file reference.odt >
795+ custom-reference.odt`. Then open `custom-reference.docx` in
796+ LibreOffice, modify the styles as you wish, and save the
797+ file.
834798
835799`--epub-stylesheet=`*FILE*
836800
@@ -983,10 +947,11 @@ Math rendering in HTML
983947
984948`--mathml`[`=`*URL*]
985949
986−: Convert TeX math to [MathML] (in `docbook`, `docbook5`, `html` and `html5`).
987− In standalone `html` output, a small JavaScript (or a link to such a
988− script if a *URL* is supplied) will be inserted that allows the MathML to
989− be viewed on some browsers.
950+: Convert TeX math to [MathML] (in `docbook4`, `docbook5`,
951+ `html4` and `html5`). In standalone HTML output, a small
952+ JavaScript (or a link to such a script if a *URL* is
953+ supplied) will be inserted that allows the MathML to be
954+ viewed on some browsers.
990955
991956`--jsmath`[`=`*URL*]
992957
@@ -1091,7 +1056,7 @@ directory (see `--data-dir`, above). *Exceptions:*
10911056 (or the `default.beamer` template, if you use `-t beamer`,
10921057 or the `default.context` template, if you use `-t context`).
10931058- `docx` has no template (however, you can use
1094− `--reference-docx` to customize the output).
1059+ `--reference-doc` to customize the output).
10951060
10961061Templates contain *variables*, which allow for the inclusion of
10971062arbitrary information at any point in the file. Variables may be set
@@ -1683,7 +1648,7 @@ Note, however, that this method of providing links to sections works
16831648only in HTML, LaTeX, and ConTeXt formats.
16841649
16851650If the `--section-divs` option is specified, then each section will
1686−be wrapped in a `div` (or a `section`, if `--html5` was specified),
1651+be wrapped in a `div` (or a `section`, if `html5` was specified),
16871652and the identifier will be attached to the enclosing `<div>`
16881653(or `<section>`) tag rather than the header itself. This allows entire
16891654sections to be manipulated using JavaScript or treated differently in
@@ -2644,20 +2609,6 @@ two trailing spaces on a line.
26442609
26452610Backslash escapes do not work in verbatim contexts.
26462611
2647−Smart punctuation
2648−-----------------
2649−
2650−#### Extension ####
2651−
2652−If the `--smart` option is specified, pandoc will produce typographically
2653−correct output, converting straight quotes to curly quotes, `---` to
2654−em-dashes, `--` to en-dashes, and `...` to ellipses. Nonbreaking spaces
2655−are inserted after certain abbreviations, such as "Mr."
2656−
2657−Note: if your LaTeX template or any included header file call for the
2658−[`csquotes`] package, pandoc will detect this automatically and use
2659−`\enquote{...}` for quoted text.
2660−
26612612Inline formatting
26622613-----------------
26632614
@@ -3214,6 +3165,30 @@ they cannot contain multiple paragraphs). The syntax is as follows:
32143165
32153166Inline and regular footnotes may be mixed freely.
32163167
3168+Typography
3169+----------
3170+
3171+#### Extension: `smart` ####
3172+
3173+Interpret straight quotes as curly quotes, `---` as em-dashes,
3174+`--` as en-dashes, and `...` as ellipses. Nonbreaking spaces are
3175+inserted after certain abbreviations, such as "Mr." This
3176+option currently affects the input formats `markdown`,
3177+`commonmark`, `latex`, `mediawiki`, `org`, `rst`, and `twiki`,
3178+and the output formats `markdown`, `latex`, and `context`.
3179+
3180+Note: If you are *writing* Markdown, then the `smart` extension
3181+has the reverse effect: what would have been curly quotes comes
3182+out straight.
3183+
3184+In LaTeX, `smart` means to use the standard TeX ligatures
3185+for quotation marks (` `` ` and ` '' ` for double quotes,
3186+`` ` `` and `` ' `` for single quotes) and dashes (`--` for
3187+en-dash and `---` for em-dash). If `smart` is disabled,
3188+then in reading LaTeX pandoc will parse these characters
3189+literally. In writing LaTeX, enabling `smart` tells pandoc
3190+to use the ligatures when possible; if `smart` is disabled
3191+pandoc will use unicode quotation mark and dash characters.
32173192
32183193Citations
32193194---------
@@ -3415,6 +3390,13 @@ in pandoc, but may be enabled by adding `+EXTENSION` to the format
34153390name, where `EXTENSION` is the name of the extension. Thus, for
34163391example, `markdown+hard_line_breaks` is Markdown with hard line breaks.
34173392
3393+#### Extension: `old_dashes` ####
3394+
3395+Selects the pandoc <= 1.8.2.1 behavior for parsing smart dashes:
3396+`-` before a numeral is an en-dash, and `--` is an em-dash.
3397+This option only has an effect if `smart` is enabled. It is
3398+selected automatically for `textile` input.
3399+
34183400#### Extension: `angle_brackets_escapable` ####
34193401
34203402Allow `<` and `>` to be backslash-escaped, as they can be in
@@ -3910,7 +3892,7 @@ Literate Haskell support
39103892
39113893If you append `+lhs` (or `+literate_haskell`) to an appropriate input or output
39123894format (`markdown`, `markdown_strict`, `rst`, or `latex` for input or output;
3913−`beamer`, `html` or `html5` for output only), pandoc will treat the document as
3895+`beamer`, `html4` or `html5` for output only), pandoc will treat the document as
39143896literate Haskell source. This means that
39153897
39163898 - In Markdown input, "bird track" sections will be parsed as Haskell
PROFILINGdeleted−6
@@ -1,6 +0,0 @@
1−To use the GHC profiler:
2−
3−cabal clean
4−cabal install --enable-library-profiling --enable-executable-profiling
5−pandoc +RTS -p -RTS [file]...
6−less pandoc.prof
RELEASE-CHECKLIST.mdnew file+27
@@ -0,0 +1,27 @@
1+- [ ] Test, on linux, windows, mac (inc. website demos)
2+
3+- [ ] Finalize changelog:
4+ `git log --pretty='format:%n%n* %s (%an)%n%b%n%h%n' --reverse --name-only 1.17.0.3..HEAD > LOG`
5+
6+- [ ] `make man/pandoc.1` and commit if needed
7+
8+- [ ] Tag release in git
9+
10+- [ ] Tag templates
11+
12+- [ ] Generate Windows package (`make winpkg`)
13+
14+- [ ] Generate Mac OSX package (`make osxpkg`)
15+
16+- [ ] Generate Ubuntu/Debian deb package (`make debpkg`)
17+
18+- [ ] Add release on github (use `make changes_github` and upload files)
19+
20+- [ ] Upload to HackageDB
21+
22+- [ ] Update website (`make update`), including short description of changes (`make changes`)
23+
24+- [ ] on server, `cabal install --enable-tests -ftrypandoc`
25+ and then `cd trypandoc; sudo make install`
26+
27+- [ ] Announce on pandoc-announce, pandoc-discuss
benchmark/benchmark-pandoc.hs+42−9
@@ -16,20 +16,26 @@ 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)
20+import Data.Time (getCurrentTime)
21+import qualified Data.ByteString as B
22+import qualified Data.Map as Map
1923import Criterion.Main
2024import Criterion.Types (Config(..))
2125import Data.Maybe (mapMaybe)
2226import Debug.Trace (trace)
27+import System.Environment (getArgs)
2328
2429readerBench :: Pandoc
25− -> (String, ReaderOptions -> String -> IO (Either PandocError Pandoc))
30+ -> (String, ReaderOptions -> String -> Pandoc)
2631 -> Maybe Benchmark
2732readerBench doc (name, reader) =
2833 case lookup name writers of
29− Just (PureStringWriter writer) ->
30− let inp = writer def{ writerWrapText = WrapAuto} doc
31− in return $ bench (name ++ " reader") $ nfIO $
32− (fmap handleError <$> reader def{ readerSmart = True }) inp
34+ Just (StringWriter writer) ->
35+ let inp = either (error . show) id $ runPure
36+ $ writer def{ writerWrapText = WrapAuto} doc
37+ in return $ bench (name ++ " reader") $ nf
38+ (reader def) inp
3339 _ -> trace ("\nCould not find writer for " ++ name ++ "\n") Nothing
3440
3541writerBench :: Pandoc
@@ -40,13 +46,40 @@ writerBench doc (name, writer) = bench (name ++ " writer") $ nf
4046
4147main :: IO ()
4248main = do
49+ args <- getArgs
50+ let matchReader (n, StringReader _) =
51+ case args of
52+ [] -> True
53+ [x] -> x == n
54+ (x:y:_) -> x == n && y == "reader"
55+ matchReader (_, _) = False
56+ let matchWriter (n, StringWriter _) =
57+ case args of
58+ [] -> True
59+ [x] -> x == n
60+ (x:y:_) -> x == n && y == "writer"
61+ matchWriter (_, _) = False
62+ let matchedReaders = filter matchReader readers
63+ let matchedWriters = filter matchWriter writers
4364 inp <- readFile "tests/testsuite.txt"
44− let opts = def{ readerSmart = True }
45− let doc = handleError $ readMarkdown opts inp
46− let readers' = [(n,r) | (n, StringReader r) <- readers]
65+ lalune <- B.readFile "tests/lalune.jpg"
66+ movie <- B.readFile "tests/movie.jpg"
67+ time <- getCurrentTime
68+ let setupFakeFiles = modifyPureState $ \st -> st{ stFiles =
69+ FileTree $ Map.fromList [
70+ ("lalune.jpg", FileInfo time lalune),
71+ ("movie.jpg", FileInfo time movie)
72+ ]}
73+ let opts = def
74+ let doc = either (error . show) id $ runPure $ readMarkdown opts inp
75+ let readers' = [(n, \o d ->
76+ either (error . show) id $ runPure $ r o d)
77+ | (n, StringReader r) <- matchedReaders]
4778 let readerBs = mapMaybe (readerBench doc)
4879 $ filter (\(n,_) -> n /="haddock") readers'
49− let writers' = [(n,w) | (n, PureStringWriter w) <- writers]
80+ let writers' = [(n, \o d ->
81+ either (error . show) id $ runPure $ setupFakeFiles >> w o d)
82+ | (n, StringWriter w) <- matchedWriters]
5083 let writerBs = map (writerBench doc)
5184 $ writers'
5285 defaultMainWith defaultConfig{ timeLimit = 6.0 }
benchmark/weigh-pandoc.hs+4−4
@@ -7,7 +7,7 @@ main = do
77 mainWith $ do
88 func "Pandoc document" id doc
99 mapM_
10− (\(n,r) -> weighReader doc n (handleError . r def{ readerSmart = True }))
10+ (\(n,r) -> weighReader doc n (either (error . show) id . runPure . r def{ readerSmart = True }))
1111 [("markdown", readMarkdown)
1212 ,("html", readHtml)
1313 ,("docbook", readDocBook)
@@ -15,7 +15,7 @@ main = do
1515 ,("commonmark", readCommonMark)
1616 ]
1717 mapM_
18− (\(n,w) -> weighWriter doc n (w def))
18+ (\(n,w) -> weighWriter doc n (either (error . show) id . runPure . w def))
1919 [("markdown", writeMarkdown)
2020 ,("html", writeHtmlString)
2121 ,("docbook", writeDocbook)
@@ -29,8 +29,8 @@ weighWriter doc name writer = func (name ++ " writer") writer doc
2929weighReader :: Pandoc -> String -> (String -> Pandoc) -> Weigh ()
3030weighReader doc name reader = do
3131 case lookup name writers of
32− Just (PureStringWriter writer) ->
33− let inp = writer def{ writerWrapText = WrapAuto} doc
32+ Just (StringWriter writer) ->
33+ let inp = either (error . show) id $ runPure $ writer def{ writerWrapText = WrapAuto} doc
3434 in func (name ++ " reader") reader inp
3535 _ -> return () -- no writer for reader
3636
data/templates+1−1

Submodule changed.

pandoc.cabal+24−17
@@ -1,5 +1,5 @@
11Name: pandoc
2−Version: 1.19.2
2+Version: 2.0
33Cabal-Version: >= 1.10
44Build-Type: Custom
55License: GPL
@@ -36,9 +36,9 @@ Description: Pandoc is a Haskell library for converting from one markup
3636 only adding a reader or writer.
3737Data-Files:
3838 -- templates
39− data/templates/default.html
39+ data/templates/default.html4
4040 data/templates/default.html5
41− data/templates/default.docbook
41+ data/templates/default.docbook4
4242 data/templates/default.docbook5
4343 data/templates/default.tei
4444 data/templates/default.beamer
@@ -66,7 +66,7 @@ Data-Files:
6666 data/templates/default.haddock
6767 data/templates/default.textile
6868 data/templates/default.org
69− data/templates/default.epub
69+ data/templates/default.epub2
7070 data/templates/default.epub3
7171 -- source files for reference.docx
7272 data/docx/[Content_Types].xml
@@ -145,12 +145,13 @@ Extra-Source-Files:
145145 tests/s5-fragment.html
146146 tests/s5-inserts.html
147147 tests/tables.context
148− tests/tables.docbook
148+ tests/tables.docbook4
149149 tests/tables.docbook5
150150 tests/tables.dokuwiki
151151 tests/tables.zimwiki
152152 tests/tables.icml
153− tests/tables.html
153+ tests/tables.html4
154+ tests/tables.html5
154155 tests/tables.latex
155156 tests/tables.man
156157 tests/tables.plain
@@ -170,9 +171,10 @@ Extra-Source-Files:
170171 tests/testsuite.txt
171172 tests/writer.latex
172173 tests/writer.context
173− tests/writer.docbook
174+ tests/writer.docbook4
174175 tests/writer.docbook5
175− tests/writer.html
176+ tests/writer.html4
177+ tests/writer.html5
176178 tests/writer.man
177179 tests/writer.markdown
178180 tests/writer.plain
@@ -290,10 +292,10 @@ Library
290292 old-time,
291293 deepseq >= 1.3 && < 1.5,
292294 JuicyPixels >= 3.1.6.1 && < 3.3,
293− filemanip >= 0.3 && < 0.4,
295+ Glob >= 0.7 && < 0.8,
294296 cmark >= 0.5 && < 0.6,
295297 doctemplates >= 0.1 && < 0.2,
296− ghc-prim >= 0.2
298+ free >= 4
297299 if flag(old-locale)
298300 Build-Depends: old-locale >= 1 && < 1.1,
299301 time >= 1.2 && < 1.5
@@ -328,6 +330,7 @@ Library
328330
329331 Exposed-Modules: Text.Pandoc,
330332 Text.Pandoc.Options,
333+ Text.Pandoc.Extensions,
331334 Text.Pandoc.Pretty,
332335 Text.Pandoc.Shared,
333336 Text.Pandoc.MediaBag,
@@ -341,7 +344,6 @@ Library
341344 Text.Pandoc.Readers.Org,
342345 Text.Pandoc.Readers.DocBook,
343346 Text.Pandoc.Readers.OPML,
344− Text.Pandoc.Readers.TeXMath,
345347 Text.Pandoc.Readers.Textile,
346348 Text.Pandoc.Readers.Native,
347349 Text.Pandoc.Readers.Haddock,
@@ -377,13 +379,15 @@ Library
377379 Text.Pandoc.Writers.EPUB,
378380 Text.Pandoc.Writers.FB2,
379381 Text.Pandoc.Writers.TEI,
382+ Text.Pandoc.Writers.Math,
380383 Text.Pandoc.PDF,
381384 Text.Pandoc.UTF8,
382385 Text.Pandoc.Templates,
383386 Text.Pandoc.XML,
384387 Text.Pandoc.SelfContained,
385388 Text.Pandoc.Process,
386− Text.Pandoc.CSS
389+ Text.Pandoc.MIME,
390+ Text.Pandoc.Class
387391 Other-Modules: Text.Pandoc.Readers.Docx.Lists,
388392 Text.Pandoc.Readers.Docx.Combine,
389393 Text.Pandoc.Readers.Docx.Parse,
@@ -410,7 +414,7 @@ Library
410414 Text.Pandoc.Readers.Org.Shared,
411415 Text.Pandoc.Writers.Shared,
412416 Text.Pandoc.Asciify,
413− Text.Pandoc.MIME,
417+ Text.Pandoc.CSS,
414418 Text.Pandoc.Emoji,
415419 Text.Pandoc.Parsing,
416420 Text.Pandoc.UUID,
@@ -435,7 +439,8 @@ Executable pandoc
435439 aeson >= 0.7.0.5 && < 1.2,
436440 yaml >= 0.8.8.2 && < 0.9,
437441 containers >= 0.1 && < 0.6,
438− HTTP >= 4000.0.5 && < 4000.4
442+ HTTP >= 4000.0.5 && < 4000.4,
443+ mtl >= 2.2 && < 2.3
439444 if flag(network-uri)
440445 Build-Depends: network-uri >= 2.6 && < 2.7, network >= 2.6
441446 else
@@ -483,7 +488,8 @@ Executable weigh-pandoc
483488 if flag(weigh-pandoc)
484489 Build-Depends: pandoc,
485490 base >= 4.2 && < 5,
486− weigh >= 0.0 && < 0.1
491+ weigh >= 0.0 && < 0.1,
492+ mtl >= 2.2 && < 2.3
487493 Buildable: True
488494 else
489495 Buildable: False
@@ -516,11 +522,11 @@ Test-Suite test-pandoc
516522 containers >= 0.1 && < 0.6,
517523 ansi-terminal >= 0.5 && < 0.7,
518524 executable-path >= 0.0 && < 0.1,
519− zip-archive >= 0.2.3.4 && < 0.4
525+ zip-archive >= 0.2.3.4 && < 0.4,
526+ mtl >= 2.2 && < 2.3
520527 Other-Modules: Tests.Old
521528 Tests.Helpers
522529 Tests.Shared
523− Tests.Walk
524530 Tests.Readers.LaTeX
525531 Tests.Readers.HTML
526532 Tests.Readers.Markdown
@@ -552,6 +558,7 @@ benchmark benchmark-pandoc
552558 Hs-Source-Dirs: prelude
553559 Other-Modules: Prelude
554560 Build-Depends: pandoc,
561+ time, bytestring, containers,
555562 base >= 4.2 && < 5,
556563 syb >= 0.1 && < 0.7,
557564 criterion >= 1.0 && < 1.2
pandoc.hs+410−465
@@ -34,10 +34,8 @@ import Text.Pandoc
3434import Text.Pandoc.Builder (setMeta)
3535import Text.Pandoc.PDF (makePDF)
3636import Text.Pandoc.Walk (walk)
37−import Text.Pandoc.Readers.LaTeX (handleIncludes)
3837import Text.Pandoc.Shared ( tabFilter, readDataFileUTF8, readDataFile,
39− safeRead, headerShift, normalize, err, warn,
40− openURL )
38+ safeRead, headerShift, err, openURL )
4139import Text.Pandoc.MediaBag ( mediaDirectory, extractMediaBag, MediaBag )
4240import Text.Pandoc.XML ( toEntities )
4341import Text.Pandoc.SelfContained ( makeSelfContained )
@@ -48,7 +46,6 @@ import System.Environment ( getArgs, getProgName, getEnvironment )
4846import System.Exit ( ExitCode (..), exitSuccess )
4947import System.FilePath
5048import System.Console.GetOpt
51−import qualified Data.Set as Set
5249import Data.Char ( toLower, toUpper )
5350import Data.List ( intercalate, isPrefixOf, isSuffixOf, sort )
5451import System.Directory ( getAppUserDataDirectory, findExecutable,
@@ -70,13 +67,357 @@ import Data.Yaml (decode)
7067import qualified Data.Yaml as Yaml
7168import qualified Data.Text as T
7269import Control.Applicative ((<|>))
73−import Text.Pandoc.Readers.Txt2Tags (getT2TMeta)
7470import Paths_pandoc (getDataDir)
7571import Text.Printf (printf)
7672#ifndef _WINDOWS
7773import System.Posix.Terminal (queryTerminal)
7874import System.Posix.IO (stdOutput)
7975#endif
76+import Control.Monad.Trans
77+import Text.Pandoc.Class (withMediaBag, PandocIO, getLog, setVerbosity)
78+
79+main :: IO ()
80+main = do
81+
82+ rawArgs <- map UTF8.decodeArg <$> getArgs
83+ prg <- getProgName
84+
85+ let (actions, args, errors) = getOpt Permute options rawArgs
86+
87+ unless (null errors) $
88+ err 2 $ concat $ errors ++
89+ ["Try " ++ prg ++ " --help for more information."]
90+
91+ -- thread option data structure through all supplied option actions
92+ opts <- foldl (>>=) (return defaultOpts) actions
93+ convertWithOpts opts args
94+
95+convertWithOpts :: Opt -> [FilePath] -> IO ()
96+convertWithOpts opts args = do
97+ let Opt { optTabStop = tabStop
98+ , optPreserveTabs = preserveTabs
99+ , optStandalone = standalone
100+ , optReader = readerName
101+ , optWriter = writerName
102+ , optParseRaw = parseRaw
103+ , optVariables = variables
104+ , optMetadata = metadata
105+ , optTableOfContents = toc
106+ , optTransforms = transforms
107+ , optTemplate = templatePath
108+ , optOutputFile = outputFile
109+ , optNumberSections = numberSections
110+ , optNumberOffset = numberFrom
111+ , optSectionDivs = sectionDivs
112+ , optIncremental = incremental
113+ , optSelfContained = selfContained
114+ , optHtmlQTags = htmlQTags
115+ , optHighlightStyle = highlightStyle
116+ , optTopLevelDivision = topLevelDivision
117+ , optHTMLMathMethod = mathMethod'
118+ , optReferenceDoc = referenceDoc
119+ , optEpubStylesheet = epubStylesheet
120+ , optEpubMetadata = epubMetadata
121+ , optEpubFonts = epubFonts
122+ , optEpubChapterLevel = epubChapterLevel
123+ , optTOCDepth = epubTOCDepth
124+ , optDumpArgs = dumpArgs
125+ , optIgnoreArgs = ignoreArgs
126+ , optVerbosity = verbosity
127+ , optFailIfWarnings = failIfWarnings
128+ , optReferenceLinks = referenceLinks
129+ , optReferenceLocation = referenceLocation
130+ , optDpi = dpi
131+ , optWrapText = wrap
132+ , optColumns = columns
133+ , optFilters = filters
134+ , optEmailObfuscation = obfuscationMethod
135+ , optIdentifierPrefix = idPrefix
136+ , optIndentedCodeClasses = codeBlockClasses
137+ , optDataDir = mbDataDir
138+ , optCiteMethod = citeMethod
139+ , optListings = listings
140+ , optLaTeXEngine = latexEngine
141+ , optLaTeXEngineArgs = latexEngineArgs
142+ , optSlideLevel = slideLevel
143+ , optSetextHeaders = setextHeaders
144+ , optAscii = ascii
145+ , optDefaultImageExtension = defaultImageExtension
146+ , optExtractMedia = mbExtractMedia
147+ , optTrackChanges = trackChanges
148+ , optFileScope = fileScope
149+ , optKaTeXStylesheet = katexStylesheet
150+ , optKaTeXJS = katexJS
151+ } = opts
152+
153+ when dumpArgs $
154+ do UTF8.hPutStrLn stdout outputFile
155+ mapM_ (UTF8.hPutStrLn stdout) args
156+ exitSuccess
157+
158+ let csscdn = "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.css"
159+ let mathMethod =
160+ case (katexJS, katexStylesheet) of
161+ (Nothing, _) -> mathMethod'
162+ (Just js, ss) -> KaTeX js (fromMaybe csscdn ss)
163+
164+
165+ -- --bibliography implies -F pandoc-citeproc for backwards compatibility:
166+ let needsCiteproc = isJust (M.lookup "bibliography" (optMetadata opts)) &&
167+ optCiteMethod opts `notElem` [Natbib, Biblatex] &&
168+ "pandoc-citeproc" `notElem` map takeBaseName filters
169+ let filters' = if needsCiteproc then "pandoc-citeproc" : filters
170+ else filters
171+
172+ let sources = case args of
173+ [] -> ["-"]
174+ xs | ignoreArgs -> ["-"]
175+ | otherwise -> xs
176+
177+ datadir <- case mbDataDir of
178+ Nothing -> E.catch
179+ (Just <$> getAppUserDataDirectory "pandoc")
180+ (\e -> let _ = (e :: E.SomeException)
181+ in return Nothing)
182+ Just _ -> return mbDataDir
183+
184+ -- assign reader and writer based on options and filenames
185+ let readerName' = case map toLower readerName of
186+ [] -> defaultReaderName
187+ (if any isURI sources
188+ then "html"
189+ else "markdown") sources
190+ x -> x
191+
192+ let writerName' = case map toLower writerName of
193+ [] -> defaultWriterName outputFile
194+ "epub2" -> "epub"
195+ x -> x
196+ let format = takeWhile (`notElem` ['+','-'])
197+ $ takeFileName writerName' -- in case path to lua script
198+
199+ let pdfOutput = map toLower (takeExtension outputFile) == ".pdf"
200+
201+ let laTeXOutput = format `elem` ["latex", "beamer"]
202+ let conTeXtOutput = format == "context"
203+ let html5Output = format == "html5" || format == "html"
204+
205+ -- disabling the custom writer for now
206+ writer <- if ".lua" `isSuffixOf` format
207+ -- note: use non-lowercased version writerName
208+ then error "custom writers disabled for now"
209+ else case getWriter writerName' of
210+ Left e -> err 9 $
211+ if format == "pdf"
212+ then e ++
213+ "\nTo create a pdf with pandoc, use " ++
214+ "the latex or beamer writer and specify\n" ++
215+ "an output file with .pdf extension " ++
216+ "(pandoc -t latex -o filename.pdf)."
217+ else e
218+ Right w -> return (w :: Writer PandocIO)
219+
220+ -- TODO: we have to get the input and the output into the state for
221+ -- the sake of the text2tags reader.
222+ reader <- case getReader readerName' of
223+ Right r -> return (r :: Reader PandocIO)
224+ Left e -> err 7 e'
225+ where e' = case readerName' of
226+ "pdf" -> e ++
227+ "\nPandoc can convert to PDF, but not from PDF."
228+ "doc" -> e ++
229+ "\nPandoc can convert from DOCX, but not from DOC.\nTry using Word to save your DOC file as DOCX, and convert that with pandoc."
230+ _ -> e
231+
232+ let standalone' = standalone || not (isTextFormat format) || pdfOutput
233+
234+ templ <- case templatePath of
235+ _ | not standalone' -> return Nothing
236+ Nothing -> do
237+ deftemp <- getDefaultTemplate datadir format
238+ case deftemp of
239+ Left e -> throwIO e
240+ Right t -> return (Just t)
241+ Just tp -> do
242+ -- strip off extensions
243+ let tp' = case takeExtension tp of
244+ "" -> tp <.> format
245+ _ -> tp
246+ Just <$> E.catch (UTF8.readFile tp')
247+ (\e -> if isDoesNotExistError e
248+ then E.catch
249+ (readDataFileUTF8 datadir
250+ ("templates" </> tp'))
251+ (\e' -> let _ = (e' :: E.SomeException)
252+ in throwIO e')
253+ else throwIO e)
254+
255+ variables' <- case mathMethod of
256+ LaTeXMathML Nothing -> do
257+ s <- readDataFileUTF8 datadir "LaTeXMathML.js"
258+ return $ ("mathml-script", s) : variables
259+ MathML Nothing -> do
260+ s <- readDataFileUTF8 datadir "MathMLinHTML.js"
261+ return $ ("mathml-script", s) : variables
262+ _ -> return variables
263+
264+ variables'' <- if format == "dzslides"
265+ then do
266+ dztempl <- readDataFileUTF8 datadir
267+ ("dzslides" </> "template.html")
268+ let dzline = "<!-- {{{{ dzslides core"
269+ let dzcore = unlines
270+ $ dropWhile (not . (dzline `isPrefixOf`))
271+ $ lines dztempl
272+ return $ ("dzslides-core", dzcore) : variables'
273+ else return variables'
274+
275+ let sourceURL = case sources of
276+ [] -> Nothing
277+ (x:_) -> case parseURI x of
278+ Just u
279+ | uriScheme u `elem` ["http:","https:"] ->
280+ Just $ show u{ uriQuery = "",
281+ uriFragment = "" }
282+ _ -> Nothing
283+
284+ let readerOpts = def{ readerStandalone = standalone'
285+ , readerParseRaw = parseRaw
286+ , readerColumns = columns
287+ , readerTabStop = tabStop
288+ , readerIndentedCodeClasses = codeBlockClasses
289+ , readerApplyMacros = not laTeXOutput
290+ , readerDefaultImageExtension = defaultImageExtension
291+ , readerTrackChanges = trackChanges
292+ }
293+
294+ let writerOptions = def { writerTemplate = templ,
295+ writerVariables = variables'',
296+ writerTabStop = tabStop,
297+ writerTableOfContents = toc,
298+ writerHTMLMathMethod = mathMethod,
299+ writerIncremental = incremental,
300+ writerCiteMethod = citeMethod,
301+ writerNumberSections = numberSections,
302+ writerNumberOffset = numberFrom,
303+ writerSectionDivs = sectionDivs,
304+ writerReferenceLinks = referenceLinks,
305+ writerReferenceLocation = referenceLocation,
306+ writerDpi = dpi,
307+ writerWrapText = wrap,
308+ writerColumns = columns,
309+ writerEmailObfuscation = obfuscationMethod,
310+ writerIdentifierPrefix = idPrefix,
311+ writerSourceURL = sourceURL,
312+ writerUserDataDir = datadir,
313+ writerHtmlQTags = htmlQTags,
314+ writerTopLevelDivision = topLevelDivision,
315+ writerListings = listings,
316+ writerSlideLevel = slideLevel,
317+ writerHighlightStyle = highlightStyle,
318+ writerSetextHeaders = setextHeaders,
319+ writerEpubMetadata = epubMetadata,
320+ writerEpubStylesheet = epubStylesheet,
321+ writerEpubFonts = epubFonts,
322+ writerEpubChapterLevel = epubChapterLevel,
323+ writerTOCDepth = epubTOCDepth,
324+ writerReferenceDoc = referenceDoc,
325+ writerLaTeXArgs = latexEngineArgs
326+ }
327+
328+
329+#ifdef _WINDOWS
330+ let istty = True
331+#else
332+ istty <- queryTerminal stdOutput
333+#endif
334+ when (istty && not (isTextFormat format) && outputFile == "-") $
335+ err 5 $ "Cannot write " ++ format ++ " output to stdout.\n" ++
336+ "Specify an output file using the -o option."
337+
338+
339+ let convertTabs = tabFilter (if preserveTabs || readerName' == "t2t"
340+ then 0
341+ else tabStop)
342+
343+ readSources :: MonadIO m => [FilePath] -> m String
344+ readSources srcs = convertTabs . intercalate "\n" <$>
345+ mapM readSource srcs
346+
347+ let runIO' :: PandocIO a -> IO a
348+ runIO' f = do
349+ (res, reports) <- runIOorExplode $ do
350+ setVerbosity verbosity
351+ x <- f
352+ rs <- getLog
353+ return (x, rs)
354+ let isWarning (WARNING, _) = True
355+ isWarning _ = False
356+ when (failIfWarnings && any isWarning reports) $
357+ err 3 "Failing because there were warnings."
358+ return res
359+
360+ let sourceToDoc :: [FilePath] -> PandocIO (Pandoc, MediaBag)
361+ sourceToDoc sources' =
362+ case reader of
363+ StringReader r
364+ | fileScope || readerName' == "json" -> do
365+ pairs <- mapM
366+ (readSource >=> withMediaBag . r readerOpts) sources
367+ return (mconcat (map fst pairs), mconcat (map snd pairs))
368+ | otherwise ->
369+ readSources sources' >>= withMediaBag . r readerOpts
370+ ByteStringReader r -> do
371+ pairs <- mapM (readFile' >=>
372+ withMediaBag . r readerOpts) sources
373+ return (mconcat (map fst pairs), mconcat (map snd pairs))
374+
375+ runIO' $ do
376+ (doc, media) <- sourceToDoc sources
377+ doc' <- (maybe return (extractMedia media) mbExtractMedia >=>
378+ adjustMetadata metadata >=>
379+ applyTransforms transforms >=>
380+ applyFilters datadir filters' [format]) doc
381+
382+ case writer of
383+ -- StringWriter f -> f writerOptions doc' >>= writerFn outputFile
384+ ByteStringWriter f -> f writerOptions doc' >>= writeFnBinary outputFile
385+ StringWriter f
386+ | pdfOutput -> do
387+ -- make sure writer is latex or beamer or context or html5
388+ unless (laTeXOutput || conTeXtOutput || html5Output) $
389+ err 47 $ "cannot produce pdf output with " ++ format ++
390+ " writer"
391+
392+ let pdfprog = case () of
393+ _ | conTeXtOutput -> "context"
394+ _ | html5Output -> "wkhtmltopdf"
395+ _ -> latexEngine
396+ -- check for pdf creating program
397+ mbPdfProg <- liftIO $ findExecutable pdfprog
398+ when (isNothing mbPdfProg) $
399+ err 41 $ pdfprog ++ " not found. " ++
400+ pdfprog ++ " is needed for pdf output."
401+
402+ res <- makePDF pdfprog f writerOptions verbosity media doc'
403+ case res of
404+ Right pdf -> writeFnBinary outputFile pdf
405+ Left err' -> liftIO $ do
406+ B.hPutStr stderr err'
407+ B.hPut stderr $ B.pack [10]
408+ err 43 "Error producing PDF"
409+ | otherwise -> do
410+ let htmlFormat = format `elem`
411+ ["html","html4","html5","s5","slidy","slideous","dzslides","revealjs"]
412+ selfcontain = if selfContained && htmlFormat
413+ then makeSelfContained writerOptions media
414+ else return
415+ handleEntities = if htmlFormat && ascii
416+ then toEntities
417+ else id
418+ output <- f writerOptions doc'
419+ selfcontain (output ++ ['\n' | not standalone']) >>=
420+ writerFn outputFile . handleEntities
80421
81422type Transform = Pandoc -> Pandoc
82423
@@ -113,8 +454,8 @@ wrapWords indent c = wrap' (c - indent) (c - indent)
113454isTextFormat :: String -> Bool
114455isTextFormat s = s `notElem` ["odt","docx","epub","epub3"]
115456
116−externalFilter :: FilePath -> [String] -> Pandoc -> IO Pandoc
117−externalFilter f args' d = do
457+externalFilter :: MonadIO m => FilePath -> [String] -> Pandoc -> m Pandoc
458+externalFilter f args' d = liftIO $ do
118459 exists <- doesFileExist f
119460 isExecutable <- if exists
120461 then executable <$> getPermissions f
@@ -137,9 +478,8 @@ externalFilter f args' d = do
137478 "Could not find executable '" ++ f' ++ "'."
138479 env <- getEnvironment
139480 let env' = Just $ ("PANDOC_VERSION", pandocVersion) : env
140− (exitcode, outbs, errbs) <- E.handle filterException $
481+ (exitcode, outbs) <- E.handle filterException $
141482 pipeProcess env' f' args'' $ encode d
142− unless (B.null errbs) $ B.hPutStr stderr errbs
143483 case exitcode of
144484 ExitSuccess -> return $ either error id $ eitherDecode' outbs
145485 ExitFailure ec -> err 83 $ "Error running filter " ++ f ++ "\n" ++
@@ -178,16 +518,11 @@ data Opt = Opt
178518 , optSectionDivs :: Bool -- ^ Put sections in div tags in HTML
179519 , optIncremental :: Bool -- ^ Use incremental lists in Slidy/Slideous/S5
180520 , optSelfContained :: Bool -- ^ Make HTML accessible offline
181− , optSmart :: Bool -- ^ Use smart typography
182− , optOldDashes :: Bool -- ^ Parse dashes like pandoc <=1.8.2.1
183− , optHtml5 :: Bool -- ^ Produce HTML5 in HTML
184521 , optHtmlQTags :: Bool -- ^ Use <q> tags in HTML
185− , optHighlight :: Bool -- ^ Highlight source code
186− , optHighlightStyle :: Style -- ^ Style to use for highlighted code
522+ , optHighlightStyle :: Maybe Style -- ^ Style to use for highlighted code
187523 , optTopLevelDivision :: TopLevelDivision -- ^ Type of the top-level divisions
188524 , optHTMLMathMethod :: HTMLMathMethod -- ^ Method to print HTML math
189− , optReferenceODT :: Maybe FilePath -- ^ Path of reference.odt
190− , optReferenceDocx :: Maybe FilePath -- ^ Path of reference.docx
525+ , optReferenceDoc :: Maybe FilePath -- ^ Path of reference doc
191526 , optEpubStylesheet :: Maybe String -- ^ EPUB stylesheet
192527 , optEpubMetadata :: String -- ^ EPUB metadata
193528 , optEpubFonts :: [FilePath] -- ^ EPUB fonts to embed
@@ -195,7 +530,8 @@ data Opt = Opt
195530 , optTOCDepth :: Int -- ^ Number of levels to include in TOC
196531 , optDumpArgs :: Bool -- ^ Output command-line arguments
197532 , optIgnoreArgs :: Bool -- ^ Ignore command-line arguments
198− , optVerbose :: Bool -- ^ Verbose diagnostic output
533+ , optVerbosity :: Verbosity -- ^ Verbosity of diagnostic output
534+ , optFailIfWarnings :: Bool -- ^ Fail on warnings
199535 , optReferenceLinks :: Bool -- ^ Use reference links in writing markdown, rst
200536 , optReferenceLocation :: ReferenceLocation -- ^ location for footnotes and link references in markdown output
201537 , optDpi :: Int -- ^ Dpi
@@ -213,10 +549,8 @@ data Opt = Opt
213549 , optSlideLevel :: Maybe Int -- ^ Header level that creates slides
214550 , optSetextHeaders :: Bool -- ^ Use atx headers for markdown level 1-2
215551 , optAscii :: Bool -- ^ Use ascii characters only in html
216− , optTeXLigatures :: Bool -- ^ Use TeX ligatures for quotes/dashes
217552 , optDefaultImageExtension :: String -- ^ Default image extension
218553 , optExtractMedia :: Maybe FilePath -- ^ Path to extract embedded media
219− , optTrace :: Bool -- ^ Print debug information
220554 , optTrackChanges :: TrackChanges -- ^ Accept or reject MS Word track-changes.
221555 , optFileScope :: Bool -- ^ Parse input files before combining
222556 , optKaTeXStylesheet :: Maybe String -- ^ Path to stylesheet for KaTeX
@@ -243,16 +577,11 @@ defaultOpts = Opt
243577 , optSectionDivs = False
244578 , optIncremental = False
245579 , optSelfContained = False
246− , optSmart = False
247− , optOldDashes = False
248− , optHtml5 = False
249580 , optHtmlQTags = False
250− , optHighlight = True
251− , optHighlightStyle = pygments
581+ , optHighlightStyle = Just pygments
252582 , optTopLevelDivision = TopLevelDefault
253583 , optHTMLMathMethod = PlainMath
254− , optReferenceODT = Nothing
255− , optReferenceDocx = Nothing
584+ , optReferenceDoc = Nothing
256585 , optEpubStylesheet = Nothing
257586 , optEpubMetadata = ""
258587 , optEpubFonts = []
@@ -260,7 +589,8 @@ defaultOpts = Opt
260589 , optTOCDepth = 3
261590 , optDumpArgs = False
262591 , optIgnoreArgs = False
263− , optVerbose = False
592+ , optVerbosity = WARNING
593+ , optFailIfWarnings = False
264594 , optReferenceLinks = False
265595 , optReferenceLocation = EndOfDocument
266596 , optDpi = 96
@@ -278,12 +608,10 @@ defaultOpts = Opt
278608 , optSlideLevel = Nothing
279609 , optSetextHeaders = True
280610 , optAscii = False
281− , optTeXLigatures = True
282611 , optDefaultImageExtension = ""
283612 , optExtractMedia = Nothing
284− , optTrace = False
285613 , optTrackChanges = AcceptChanges
286− , optFileScope = False
614+ , optFileScope = False
287615 , optKaTeXStylesheet = Nothing
288616 , optKaTeXJS = Nothing
289617 }
@@ -321,17 +649,6 @@ options =
321649 (\opt -> return opt { optParseRaw = True }))
322650 "" -- "Parse untranslatable HTML codes and LaTeX environments as raw"
323651
324− , Option "S" ["smart"]
325− (NoArg
326− (\opt -> return opt { optSmart = True }))
327− "" -- "Use smart quotes, dashes, and ellipses"
328−
329− , Option "" ["old-dashes"]
330− (NoArg
331− (\opt -> return opt { optSmart = True
332− , optOldDashes = True }))
333− "" -- "Use smart quotes, dashes, and ellipses"
334−
335652 , Option "" ["base-header-level"]
336653 (ReqArg
337654 (\arg opt ->
@@ -359,12 +676,6 @@ options =
359676 "PROGRAM")
360677 "" -- "External JSON filter"
361678
362− , Option "" ["normalize"]
363− (NoArg
364− (\opt -> return opt { optTransforms =
365− normalize : optTransforms opt } ))
366− "" -- "Normalize the Pandoc AST"
367−
368679 , Option "p" ["preserve-tabs"]
369680 (NoArg
370681 (\opt -> return opt { optPreserveTabs = True }))
@@ -468,13 +779,6 @@ options =
468779 "NUMBER")
469780 "" -- "Dpi (default 96)"
470781
471− , Option "" ["no-wrap"]
472− (NoArg
473− (\opt -> do warn $ "--no-wrap is deprecated. " ++
474− "Use --wrap=none or --wrap=preserve instead."
475− return opt { optWrapText = WrapNone }))
476− ""
477−
478782 , Option "" ["wrap"]
479783 (ReqArg
480784 (\arg opt ->
@@ -512,14 +816,14 @@ options =
512816
513817 , Option "" ["no-highlight"]
514818 (NoArg
515− (\opt -> return opt { optHighlight = False }))
819+ (\opt -> return opt { optHighlightStyle = Nothing }))
516820 "" -- "Don't highlight source code"
517821
518822 , Option "" ["highlight-style"]
519823 (ReqArg
520824 (\arg opt -> do
521825 case lookup (map toLower arg) highlightingStyles of
522− Just s -> return opt{ optHighlightStyle = s }
826+ Just s -> return opt{ optHighlightStyle = Just s }
523827 Nothing -> err 39 $ "Unknown style: " ++ arg)
524828 "STYLE")
525829 "" -- "Style for highlighted code"
@@ -597,13 +901,6 @@ options =
597901 (\opt -> return opt { optSetextHeaders = False } ))
598902 "" -- "Use atx-style headers for markdown"
599903
600− , Option "" ["chapters"]
601− (NoArg
602− (\opt -> do warn $ "--chapters is deprecated. " ++
603− "Use --top-level-division=chapter instead."
604− return opt { optTopLevelDivision = TopLevelChapter }))
605− "" -- "Use chapter for top-level sections in LaTeX, DocBook"
606−
607904 , Option "" ["top-level-division"]
608905 (ReqArg
609906 (\arg opt -> do
@@ -630,11 +927,6 @@ options =
630927 "NUMBERS")
631928 "" -- "Starting number for sections, subsections, etc."
632929
633− , Option "" ["no-tex-ligatures"]
634− (NoArg
635− (\opt -> return opt { optTeXLigatures = False }))
636− "" -- "Don't use tex ligatures for quotes, dashes"
637−
638930 , Option "" ["listings"]
639931 (NoArg
640932 (\opt -> return opt { optListings = True }))
@@ -705,19 +997,12 @@ options =
705997 "URL")
706998 "" -- "Link to CSS style sheet"
707999
708− , Option "" ["reference-odt"]
709− (ReqArg
710− (\arg opt ->
711− return opt { optReferenceODT = Just arg })
712− "FILENAME")
713− "" -- "Path of custom reference.odt"
714−
715− , Option "" ["reference-docx"]
1000+ , Option "" ["reference-doc"]
7161001 (ReqArg
7171002 (\arg opt ->
718− return opt { optReferenceDocx = Just arg })
1003+ return opt { optReferenceDoc = Just arg })
7191004 "FILENAME")
720− "" -- "Path of custom reference.docx"
1005+ "" -- "Path of custom reference doc"
7211006
7221007 , Option "" ["epub-stylesheet"]
7231008 (ReqArg
@@ -885,7 +1170,7 @@ options =
8851170
8861171 , Option "" ["trace"]
8871172 (NoArg
888− (\opt -> return opt { optTrace = True }))
1173+ (\opt -> return opt { optVerbosity = DEBUG }))
8891174 "" -- "Turn on diagnostic tracing in readers."
8901175
8911176 , Option "" ["dump-args"]
@@ -900,9 +1185,19 @@ options =
9001185
9011186 , Option "" ["verbose"]
9021187 (NoArg
903− (\opt -> return opt { optVerbose = True }))
1188+ (\opt -> return opt { optVerbosity = INFO }))
9041189 "" -- "Verbose diagnostic output."
9051190
1191+ , Option "" ["quiet"]
1192+ (NoArg
1193+ (\opt -> return opt { optVerbosity = ERROR }))
1194+ "" -- "Suppress warnings."
1195+
1196+ , Option "" ["fail-if-warnings"]
1197+ (NoArg
1198+ (\opt -> return opt { optFailIfWarnings = True }))
1199+ "" -- "Exit with error status if there were warnings."
1200+
9061201 , Option "" ["bash-completion"]
9071202 (NoArg
9081203 (\_ -> do
@@ -913,8 +1208,8 @@ options =
9131208 map ("--" ++) longs
9141209 let allopts = unwords (concatMap optnames options)
9151210 UTF8.hPutStrLn stdout $ printf tpl allopts
916− (unwords (map fst readers))
917− (unwords (map fst writers))
1211+ (unwords (map fst (readers :: [(String, Reader PandocIO)])))
1212+ (unwords (map fst (writers :: [(String, Writer PandocIO)])))
9181213 (unwords $ map fst highlightingStyles)
9191214 ddir
9201215 exitSuccess ))
@@ -923,7 +1218,7 @@ options =
9231218 , Option "" ["list-input-formats"]
9241219 (NoArg
9251220 (\_ -> do
926− let readers'names = sort (map fst readers)
1221+ let readers'names = sort (map fst (readers :: [(String, Reader PandocIO)]))
9271222 mapM_ (UTF8.hPutStrLn stdout) readers'names
9281223 exitSuccess ))
9291224 ""
@@ -931,7 +1226,7 @@ options =
9311226 , Option "" ["list-output-formats"]
9321227 (NoArg
9331228 (\_ -> do
934− let writers'names = sort (map fst writers)
1229+ let writers'names = sort (map fst (writers :: [(String, Writer PandocIO)]))
9351230 mapM_ (UTF8.hPutStrLn stdout) writers'names
9361231 exitSuccess ))
9371232 ""
@@ -940,7 +1235,7 @@ options =
9401235 (NoArg
9411236 (\_ -> do
9421237 let showExt x = drop 4 (show x) ++
943− if x `Set.member` pandocExtensions
1238+ if extensionEnabled x pandocExtensions
9441239 then " +"
9451240 else " -"
9461241 mapM_ (UTF8.hPutStrLn stdout . showExt)
@@ -1089,7 +1384,7 @@ defaultWriterName x =
10891384
10901385-- Transformations of a Pandoc document post-parsing:
10911386
1092−extractMedia :: MediaBag -> FilePath -> Pandoc -> IO Pandoc
1387+extractMedia :: MonadIO m => MediaBag -> FilePath -> Pandoc -> m Pandoc
10931388extractMedia media dir d =
10941389 case [fp | (fp, _, _) <- mediaDirectory media] of
10951390 [] -> return d
@@ -1102,17 +1397,17 @@ adjustImagePath dir paths (Image attr lab (src, tit))
11021397 | src `elem` paths = Image attr lab (dir ++ "/" ++ src, tit)
11031398adjustImagePath _ _ x = x
11041399
1105−adjustMetadata :: M.Map String MetaValue -> Pandoc -> IO Pandoc
1400+adjustMetadata :: Monad m => M.Map String MetaValue -> Pandoc -> m Pandoc
11061401adjustMetadata metadata d = return $ M.foldWithKey setMeta d metadata
11071402
1108−applyTransforms :: [Transform] -> Pandoc -> IO Pandoc
1403+applyTransforms :: Monad m => [Transform] -> Pandoc -> m Pandoc
11091404applyTransforms transforms d = return $ foldr ($) d transforms
11101405
11111406 -- First we check to see if a filter is found. If not, and if it's
11121407 -- not an absolute path, we check to see whether it's in `userdir/filters`.
11131408 -- If not, we leave it unchanged.
1114−expandFilterPath :: Maybe FilePath -> FilePath -> IO FilePath
1115−expandFilterPath mbDatadir fp = do
1409+expandFilterPath :: MonadIO m => Maybe FilePath -> FilePath -> m FilePath
1410+expandFilterPath mbDatadir fp = liftIO $ do
11161411 fpExists <- doesFileExist fp
11171412 if fpExists
11181413 then return fp
@@ -1125,7 +1420,8 @@ expandFilterPath mbDatadir fp = do
11251420 else return fp
11261421 _ -> return fp
11271422
1128−applyFilters :: Maybe FilePath -> [FilePath] -> [String] -> Pandoc -> IO Pandoc
1423+applyFilters :: MonadIO m
1424+ => Maybe FilePath -> [FilePath] -> [String] -> Pandoc -> m Pandoc
11291425applyFilters mbDatadir filters args d = do
11301426 expandedFilters <- mapM (expandFilterPath mbDatadir) filters
11311427 foldrM ($) d $ map (flip externalFilter args) expandedFilters
@@ -1134,383 +1430,32 @@ uppercaseFirstLetter :: String -> String
11341430uppercaseFirstLetter (c:cs) = toUpper c : cs
11351431uppercaseFirstLetter [] = []
11361432
1137−main :: IO ()
1138−main = do
1139−
1140− rawArgs <- map UTF8.decodeArg <$> getArgs
1141− prg <- getProgName
1433+readSource :: MonadIO m => FilePath -> m String
1434+readSource "-" = liftIO UTF8.getContents
1435+readSource src = case parseURI src of
1436+ Just u | uriScheme u `elem` ["http:","https:"] ->
1437+ readURI src
1438+ | uriScheme u == "file:" ->
1439+ liftIO $ UTF8.readFile (uriPath u)
1440+ _ -> liftIO $ UTF8.readFile src
11421441
1143− let (actions, args, errors) = getOpt Permute options rawArgs
1442+readURI :: MonadIO m => FilePath -> m String
1443+readURI src = do
1444+ res <- liftIO $ openURL src
1445+ case res of
1446+ Left e -> liftIO $ throwIO e
1447+ Right (bs,_) -> return $ UTF8.toString bs
11441448
1145− unless (null errors) $
1146− err 2 $ concat $ errors ++
1147− ["Try " ++ prg ++ " --help for more information."]
1449+readFile' :: MonadIO m => FilePath -> m B.ByteString
1450+readFile' "-" = liftIO $ B.getContents
1451+readFile' f = liftIO $ B.readFile f
11481452
1149− -- thread option data structure through all supplied option actions
1150− opts <- foldl (>>=) (return defaultOpts) actions
1151− convertWithOpts opts args
1453+writeFnBinary :: MonadIO m => FilePath -> B.ByteString -> m ()
1454+writeFnBinary "-" = liftIO . B.putStr
1455+writeFnBinary f = liftIO . B.writeFile (UTF8.encodePath f)
11521456
1153−convertWithOpts :: Opt -> [FilePath] -> IO ()
1154−convertWithOpts opts args = do
1155− let Opt { optTabStop = tabStop
1156− , optPreserveTabs = preserveTabs
1157− , optStandalone = standalone
1158− , optReader = readerName
1159− , optWriter = writerName
1160− , optParseRaw = parseRaw
1161− , optVariables = variables
1162− , optMetadata = metadata
1163− , optTableOfContents = toc
1164− , optTransforms = transforms
1165− , optTemplate = templatePath
1166− , optOutputFile = outputFile
1167− , optNumberSections = numberSections
1168− , optNumberOffset = numberFrom
1169− , optSectionDivs = sectionDivs
1170− , optIncremental = incremental
1171− , optSelfContained = selfContained
1172− , optSmart = smart
1173− , optOldDashes = oldDashes
1174− , optHtml5 = html5
1175− , optHtmlQTags = htmlQTags
1176− , optHighlight = highlight
1177− , optHighlightStyle = highlightStyle
1178− , optTopLevelDivision = topLevelDivision
1179− , optHTMLMathMethod = mathMethod'
1180− , optReferenceODT = referenceODT
1181− , optReferenceDocx = referenceDocx
1182− , optEpubStylesheet = epubStylesheet
1183− , optEpubMetadata = epubMetadata
1184− , optEpubFonts = epubFonts
1185− , optEpubChapterLevel = epubChapterLevel
1186− , optTOCDepth = epubTOCDepth
1187− , optDumpArgs = dumpArgs
1188− , optIgnoreArgs = ignoreArgs
1189− , optVerbose = verbose
1190− , optReferenceLinks = referenceLinks
1191− , optReferenceLocation = referenceLocation
1192− , optDpi = dpi
1193− , optWrapText = wrap
1194− , optColumns = columns
1195− , optFilters = filters
1196− , optEmailObfuscation = obfuscationMethod
1197− , optIdentifierPrefix = idPrefix
1198− , optIndentedCodeClasses = codeBlockClasses
1199− , optDataDir = mbDataDir
1200− , optCiteMethod = citeMethod
1201− , optListings = listings
1202− , optLaTeXEngine = latexEngine
1203− , optLaTeXEngineArgs = latexEngineArgs
1204− , optSlideLevel = slideLevel
1205− , optSetextHeaders = setextHeaders
1206− , optAscii = ascii
1207− , optTeXLigatures = texLigatures
1208− , optDefaultImageExtension = defaultImageExtension
1209− , optExtractMedia = mbExtractMedia
1210− , optTrace = trace
1211− , optTrackChanges = trackChanges
1212− , optFileScope = fileScope
1213− , optKaTeXStylesheet = katexStylesheet
1214− , optKaTeXJS = katexJS
1215− } = opts
1216−
1217− when dumpArgs $
1218− do UTF8.hPutStrLn stdout outputFile
1219− mapM_ (UTF8.hPutStrLn stdout) args
1220− exitSuccess
1221−
1222− let csscdn = "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.css"
1223− let mathMethod =
1224− case (katexJS, katexStylesheet) of
1225− (Nothing, _) -> mathMethod'
1226− (Just js, ss) -> KaTeX js (fromMaybe csscdn ss)
1227−
1228−
1229− -- --bibliography implies -F pandoc-citeproc for backwards compatibility:
1230− let needsCiteproc = isJust (M.lookup "bibliography" (optMetadata opts)) &&
1231− optCiteMethod opts `notElem` [Natbib, Biblatex] &&
1232− "pandoc-citeproc" `notElem` map takeBaseName filters
1233− let filters' = if needsCiteproc then "pandoc-citeproc" : filters
1234− else filters
1235−
1236− let sources = if ignoreArgs then [] else args
1237−
1238− datadir <- case mbDataDir of
1239− Nothing -> E.catch
1240− (Just <$> getAppUserDataDirectory "pandoc")
1241− (\e -> let _ = (e :: E.SomeException)
1242− in return Nothing)
1243− Just _ -> return mbDataDir
1244−
1245− -- assign reader and writer based on options and filenames
1246− let readerName' = case map toLower readerName of
1247− [] -> defaultReaderName
1248− (if any isURI sources
1249− then "html"
1250− else "markdown") sources
1251− "html4" -> "html"
1252− x -> x
1253−
1254− let writerName' = case map toLower writerName of
1255− [] -> defaultWriterName outputFile
1256− "epub2" -> "epub"
1257− "html4" -> "html"
1258− x -> x
1259− let format = takeWhile (`notElem` ['+','-'])
1260− $ takeFileName writerName' -- in case path to lua script
1261−
1262− let pdfOutput = map toLower (takeExtension outputFile) == ".pdf"
1263−
1264− let laTeXOutput = format `elem` ["latex", "beamer"]
1265− let conTeXtOutput = format == "context"
1266− let html5Output = format == "html5"
1267−
1268− let laTeXInput = "latex" `isPrefixOf` readerName' ||
1269− "beamer" `isPrefixOf` readerName'
1270−
1271− writer <- if ".lua" `isSuffixOf` format
1272− -- note: use non-lowercased version writerName
1273− then return $ IOStringWriter $ writeCustom writerName
1274− else case getWriter writerName' of
1275− Left e -> err 9 $
1276− if format == "pdf"
1277− then e ++
1278− "\nTo create a pdf with pandoc, use " ++
1279− "the latex or beamer writer and specify\n" ++
1280− "an output file with .pdf extension " ++
1281− "(pandoc -t latex -o filename.pdf)."
1282− else e
1283− Right w -> return w
1284−
1285− reader <- if "t2t" == readerName'
1286− then (mkStringReader .
1287− readTxt2Tags) <$>
1288− getT2TMeta sources outputFile
1289− else case getReader readerName' of
1290− Right r -> return r
1291− Left e -> err 7 e'
1292− where e' = case readerName' of
1293− "pdf" -> e ++
1294− "\nPandoc can convert to PDF, but not from PDF."
1295− "doc" -> e ++
1296− "\nPandoc can convert from DOCX, but not from DOC.\nTry using Word to save your DOC file as DOCX, and convert that with pandoc."
1297− _ -> e
1298−
1299− let standalone' = standalone || not (isTextFormat format) || pdfOutput
1300−
1301− templ <- case templatePath of
1302− _ | not standalone' -> return Nothing
1303− Nothing -> do
1304− deftemp <- getDefaultTemplate datadir format
1305− case deftemp of
1306− Left e -> throwIO e
1307− Right t -> return (Just t)
1308− Just tp -> do
1309− -- strip off extensions
1310− let tp' = case takeExtension tp of
1311− "" -> tp <.> format
1312− _ -> tp
1313− Just <$> E.catch (UTF8.readFile tp')
1314− (\e -> if isDoesNotExistError e
1315− then E.catch
1316− (readDataFileUTF8 datadir
1317− ("templates" </> tp'))
1318− (\e' -> let _ = (e' :: E.SomeException)
1319− in throwIO e')
1320− else throwIO e)
1321−
1322− variables' <- case mathMethod of
1323− LaTeXMathML Nothing -> do
1324− s <- readDataFileUTF8 datadir "LaTeXMathML.js"
1325− return $ ("mathml-script", s) : variables
1326− _ -> return variables
1327−
1328− variables'' <- if format == "dzslides"
1329− then do
1330− dztempl <- readDataFileUTF8 datadir
1331− ("dzslides" </> "template.html")
1332− let dzline = "<!-- {{{{ dzslides core"
1333− let dzcore = unlines
1334− $ dropWhile (not . (dzline `isPrefixOf`))
1335− $ lines dztempl
1336− return $ ("dzslides-core", dzcore) : variables'
1337− else return variables'
1338−
1339− let sourceURL = case sources of
1340− [] -> Nothing
1341− (x:_) -> case parseURI x of
1342− Just u
1343− | uriScheme u `elem` ["http:","https:"] ->
1344− Just $ show u{ uriQuery = "",
1345− uriFragment = "" }
1346− _ -> Nothing
1347−
1348− let readerOpts = def{ readerSmart = if laTeXInput
1349− then texLigatures
1350− else smart || (texLigatures &&
1351− (laTeXOutput || conTeXtOutput))
1352− , readerStandalone = standalone'
1353− , readerParseRaw = parseRaw
1354− , readerColumns = columns
1355− , readerTabStop = tabStop
1356− , readerOldDashes = oldDashes
1357− , readerIndentedCodeClasses = codeBlockClasses
1358− , readerApplyMacros = not laTeXOutput
1359− , readerDefaultImageExtension = defaultImageExtension
1360− , readerTrace = trace
1361− , readerTrackChanges = trackChanges
1362− , readerFileScope = fileScope
1363− }
1364−
1365−#ifdef _WINDOWS
1366− let istty = True
1367−#else
1368− istty <- queryTerminal stdOutput
1369−#endif
1370− when (istty && not (isTextFormat format) && outputFile == "-") $
1371− err 5 $ "Cannot write " ++ format ++ " output to stdout.\n" ++
1372− "Specify an output file using the -o option."
1373−
1374− let readSources [] = mapM readSource ["-"]
1375− readSources srcs = mapM readSource srcs
1376− readSource "-" = UTF8.getContents
1377− readSource src = case parseURI src of
1378− Just u | uriScheme u `elem` ["http:","https:"] ->
1379− readURI src
1380− | uriScheme u == "file:" ->
1381− UTF8.readFile (uriPath u)
1382− _ -> UTF8.readFile src
1383− readURI src = do
1384− res <- openURL src
1385− case res of
1386− Left e -> throwIO e
1387− Right (bs,_) -> return $ UTF8.toString bs
1388−
1389− let readFiles [] = error "Cannot read archive from stdin"
1390− readFiles [x] = B.readFile x
1391− readFiles (x:xs) = mapM_ (warn . ("Ignoring: " ++)) xs >> B.readFile x
1392−
1393− let convertTabs = tabFilter (if preserveTabs || readerName' == "t2t"
1394− then 0
1395− else tabStop)
1396−
1397− let handleIncludes' :: String -> IO (Either PandocError String)
1398− handleIncludes' = if readerName' `elem` ["latex", "latex+lhs"]
1399− then handleIncludes
1400− else return . Right
1401−
1402− let sourceToDoc :: [FilePath] -> IO (Pandoc, MediaBag)
1403− sourceToDoc sources' = fmap handleError $
1404− case reader of
1405− StringReader r-> do
1406− srcs <- convertTabs . intercalate "\n" <$> readSources sources'
1407− doc <- handleIncludes' srcs
1408− either (return . Left) (\s -> fmap (,mempty) <$> r readerOpts s) doc
1409− ByteStringReader r -> readFiles sources' >>= r readerOpts
1410−
1411− -- We parse first if (1) fileScope is set, (2), it's a binary
1412− -- reader, or (3) we're reading JSON. This is easier to do of an AND
1413− -- of negatives as opposed to an OR of positives, so we do default
1414− -- parsing if it's a StringReader AND (fileScope is set AND it's not
1415− -- a JSON reader).
1416− (doc, media) <- case reader of
1417− (StringReader _) | not fileScope && readerName' /= "json" ->
1418− sourceToDoc sources
1419− _ | null sources -> sourceToDoc sources
1420− _ -> do pairs <- mapM (\s -> sourceToDoc [s]) sources
1421− return (mconcat $ map fst pairs, mconcat $ map snd pairs)
1422−
1423− let writerOptions = def { writerTemplate = templ,
1424− writerVariables = variables'',
1425− writerTabStop = tabStop,
1426− writerTableOfContents = toc,
1427− writerHTMLMathMethod = mathMethod,
1428− writerIncremental = incremental,
1429− writerCiteMethod = citeMethod,
1430− writerIgnoreNotes = False,
1431− writerNumberSections = numberSections,
1432− writerNumberOffset = numberFrom,
1433− writerSectionDivs = sectionDivs,
1434− writerReferenceLinks = referenceLinks,
1435− writerReferenceLocation = referenceLocation,
1436− writerDpi = dpi,
1437− writerWrapText = wrap,
1438− writerColumns = columns,
1439− writerEmailObfuscation = obfuscationMethod,
1440− writerIdentifierPrefix = idPrefix,
1441− writerSourceURL = sourceURL,
1442− writerUserDataDir = datadir,
1443− writerHtml5 = html5,
1444− writerHtmlQTags = htmlQTags,
1445− writerTopLevelDivision = topLevelDivision,
1446− writerListings = listings,
1447− writerBeamer = False,
1448− writerSlideLevel = slideLevel,
1449− writerHighlight = highlight,
1450− writerHighlightStyle = highlightStyle,
1451− writerSetextHeaders = setextHeaders,
1452− writerTeXLigatures = texLigatures,
1453− writerEpubMetadata = epubMetadata,
1454− writerEpubStylesheet = epubStylesheet,
1455− writerEpubFonts = epubFonts,
1456− writerEpubChapterLevel = epubChapterLevel,
1457− writerTOCDepth = epubTOCDepth,
1458− writerReferenceODT = referenceODT,
1459− writerReferenceDocx = referenceDocx,
1460− writerMediaBag = media,
1461− writerVerbose = verbose,
1462− writerLaTeXArgs = latexEngineArgs
1463− }
1457+writerFn :: MonadIO m => FilePath -> String -> m ()
1458+writerFn "-" = liftIO . UTF8.putStr
1459+writerFn f = liftIO . UTF8.writeFile f
14641460
14651461
1466− doc' <- (maybe return (extractMedia media) mbExtractMedia >=>
1467− adjustMetadata metadata >=>
1468− applyTransforms transforms >=>
1469− applyFilters datadir filters' [format]) doc
1470−
1471− let writeFnBinary :: FilePath -> B.ByteString -> IO ()
1472− writeFnBinary "-" = B.putStr
1473− writeFnBinary f = B.writeFile (UTF8.encodePath f)
1474−
1475− let writerFn :: FilePath -> String -> IO ()
1476− writerFn "-" = UTF8.putStr
1477− writerFn f = UTF8.writeFile f
1478−
1479− case writer of
1480− IOStringWriter f -> f writerOptions doc' >>= writerFn outputFile
1481− IOByteStringWriter f -> f writerOptions doc' >>= writeFnBinary outputFile
1482− PureStringWriter f
1483− | pdfOutput -> do
1484− -- make sure writer is latex or beamer or context or html5
1485− unless (laTeXOutput || conTeXtOutput || html5Output) $
1486− err 47 $ "cannot produce pdf output with " ++ format ++
1487− " writer"
1488−
1489− let pdfprog = case () of
1490− _ | conTeXtOutput -> "context"
1491− _ | html5Output -> "wkhtmltopdf"
1492− _ -> latexEngine
1493− -- check for pdf creating program
1494− mbPdfProg <- findExecutable pdfprog
1495− when (isNothing mbPdfProg) $
1496− err 41 $ pdfprog ++ " not found. " ++
1497− pdfprog ++ " is needed for pdf output."
1498−
1499− res <- makePDF pdfprog f writerOptions doc'
1500− case res of
1501− Right pdf -> writeFnBinary outputFile pdf
1502− Left err' -> do
1503− B.hPutStr stderr err'
1504− B.hPut stderr $ B.pack [10]
1505− err 43 "Error producing PDF"
1506− | otherwise -> selfcontain (f writerOptions doc' ++
1507− ['\n' | not standalone'])
1508− >>= writerFn outputFile . handleEntities
1509− where htmlFormat = format `elem`
1510− ["html","html5","s5","slidy","slideous","dzslides","revealjs"]
1511− selfcontain = if selfContained && htmlFormat
1512− then makeSelfContained writerOptions
1513− else return
1514− handleEntities = if htmlFormat && ascii
1515− then toEntities
1516− else id
src/Text/Pandoc.hs+139−154
@@ -1,4 +1,4 @@
1−{-# LANGUAGE ScopedTypeVariables, FlexibleInstances #-}
1+{-# LANGUAGE ScopedTypeVariables, FlexibleInstances, GADTs #-}
22{-
33Copyright (C) 2006-2016 John MacFarlane <jgm@berkeley.edu>
44
@@ -38,12 +38,11 @@ inline links:
3838> module Main where
3939> import Text.Pandoc
4040>
41−> markdownToRST :: String -> String
41+> markdownToRST :: String -> Either PandocError String
4242> markdownToRST =
43−> writeRST def {writerReferenceLinks = True} .
44−> handleError . readMarkdown def
43+> writeRST def {writerReferenceLinks = True} . readMarkdown def
4544>
46−> main = getContents >>= putStrLn . markdownToRST
45+> main = getContents >>= either error return markdownToRST >>= putStrLn
4746
4847Note: all of the readers assume that the input text has @'\n'@
4948line endings. So if you get your input text from a web form,
@@ -59,14 +58,19 @@ module Text.Pandoc
5958 , module Text.Pandoc.Generic
6059 -- * Options
6160 , module Text.Pandoc.Options
61+ -- * Typeclass
62+ , PandocMonad
63+ , runIO
64+ , runPure
65+ , runIOorExplode
6266 -- * Error handling
6367 , module Text.Pandoc.Error
6468 -- * Lists of readers and writers
6569 , readers
70+ -- , writers
6671 , writers
6772 -- * Readers: converting /to/ Pandoc format
6873 , Reader (..)
69− , mkStringReader
7074 , readDocx
7175 , readOdt
7276 , readMarkdown
@@ -84,22 +88,30 @@ module Text.Pandoc
8488 , readJSON
8589 , readTWiki
8690 , readTxt2Tags
87− , readTxt2TagsNoMacros
8891 , readEPUB
8992 -- * Writers: converting /from/ Pandoc format
90− , Writer (..)
93+ , Writer(..)
9194 , writeNative
9295 , writeJSON
9396 , writeMarkdown
9497 , writePlain
9598 , writeRST
9699 , writeLaTeX
100+ , writeBeamer
97101 , writeConTeXt
98102 , writeTexinfo
99− , writeHtml
100− , writeHtmlString
103+ , writeHtml4
104+ , writeHtml4String
105+ , writeHtml5
106+ , writeHtml5String
107+ , writeRevealJs
108+ , writeS5
109+ , writeSlidy
110+ , writeSlideous
111+ , writeDZSlides
101112 , writeICML
102− , writeDocbook
113+ , writeDocbook4
114+ , writeDocbook5
103115 , writeOPML
104116 , writeOpenDocument
105117 , writeMan
@@ -110,7 +122,8 @@ module Text.Pandoc
110122 , writeRTF
111123 , writeODT
112124 , writeDocx
113− , writeEPUB
125+ , writeEPUB2
126+ , writeEPUB3
114127 , writeFB2
115128 , writeOrg
116129 , writeAsciiDoc
@@ -124,13 +137,11 @@ module Text.Pandoc
124137 , getReader
125138 , getWriter
126139 , getDefaultExtensions
127− , ToJsonFilter(..)
128140 , pandocVersion
129141 ) where
130142
131143import Text.Pandoc.Definition
132144import Text.Pandoc.Generic
133−import Text.Pandoc.JSON
134145import Text.Pandoc.Readers.Markdown
135146import Text.Pandoc.Readers.CommonMark
136147import Text.Pandoc.Readers.MediaWiki
@@ -177,20 +188,19 @@ import Text.Pandoc.Writers.Custom
177188import Text.Pandoc.Writers.TEI
178189import Text.Pandoc.Templates
179190import Text.Pandoc.Options
180−import Text.Pandoc.Shared (safeRead, warn, mapLeft, pandocVersion)
181−import Text.Pandoc.MediaBag (MediaBag)
191+import Text.Pandoc.Shared (safeRead, mapLeft, pandocVersion)
182192import Text.Pandoc.Error
193+import Text.Pandoc.Class
183194import Data.Aeson
184195import qualified Data.ByteString.Lazy as BL
185196import Data.List (intercalate)
186−import Data.Set (Set)
187−import qualified Data.Set as Set
188197import Text.Parsec
189198import Text.Parsec.Error
190199import qualified Text.Pandoc.UTF8 as UTF8
200+import Control.Monad.Except (throwError)
191201
192202parseFormatSpec :: String
193− -> Either ParseError (String, Set Extension -> Set Extension)
203+ -> Either ParseError (String, Extensions -> Extensions)
194204parseFormatSpec = parse formatSpec ""
195205 where formatSpec = do
196206 name <- formatName
@@ -206,146 +216,131 @@ parseFormatSpec = parse formatSpec ""
206216 | name == "lhs" -> return Ext_literate_haskell
207217 | otherwise -> fail $ "Unknown extension: " ++ name
208218 return $ case polarity of
209− '-' -> Set.delete ext
210− _ -> Set.insert ext
219+ '-' -> disableExtension ext
220+ _ -> enableExtension ext
211221
212−
213−data Reader = StringReader (ReaderOptions -> String -> IO (Either PandocError Pandoc))
214− | ByteStringReader (ReaderOptions -> BL.ByteString -> IO (Either PandocError (Pandoc,MediaBag)))
215−
216−mkStringReader :: (ReaderOptions -> String -> Either PandocError Pandoc) -> Reader
217−mkStringReader r = StringReader (\o s -> return $ r o s)
218−
219−mkStringReaderWithWarnings :: (ReaderOptions -> String -> Either PandocError (Pandoc, [String])) -> Reader
220−mkStringReaderWithWarnings r = StringReader $ \o s ->
221− case r o s of
222− Left err -> return $ Left err
223− Right (doc, warnings) -> do
224− mapM_ warn warnings
225− return (Right doc)
226−
227−mkBSReader :: (ReaderOptions -> BL.ByteString -> Either PandocError (Pandoc, MediaBag)) -> Reader
228−mkBSReader r = ByteStringReader (\o s -> return $ r o s)
229−
230−mkBSReaderWithWarnings :: (ReaderOptions -> BL.ByteString -> Either PandocError (Pandoc, MediaBag, [String])) -> Reader
231−mkBSReaderWithWarnings r = ByteStringReader $ \o s ->
232− case r o s of
233− Left err -> return $ Left err
234− Right (doc, mediaBag, warnings) -> do
235− mapM_ warn warnings
236− return $ Right (doc, mediaBag)
222+data Reader m = StringReader (ReaderOptions -> String -> m Pandoc)
223+ | ByteStringReader (ReaderOptions -> BL.ByteString -> m Pandoc)
237224
238225-- | Association list of formats and readers.
239−readers :: [(String, Reader)]
240−readers = [ ("native" , StringReader $ \_ s -> return $ readNative s)
241− ,("json" , mkStringReader readJSON )
242− ,("markdown" , mkStringReaderWithWarnings readMarkdownWithWarnings)
243− ,("markdown_strict" , mkStringReaderWithWarnings readMarkdownWithWarnings)
244− ,("markdown_phpextra" , mkStringReaderWithWarnings readMarkdownWithWarnings)
245− ,("markdown_github" , mkStringReaderWithWarnings readMarkdownWithWarnings)
246− ,("markdown_mmd", mkStringReaderWithWarnings readMarkdownWithWarnings)
247− ,("commonmark" , mkStringReader readCommonMark)
248− ,("rst" , mkStringReaderWithWarnings readRSTWithWarnings )
249− ,("mediawiki" , mkStringReader readMediaWiki)
250− ,("docbook" , mkStringReader readDocBook)
251− ,("opml" , mkStringReader readOPML)
252− ,("org" , mkStringReader readOrg)
253− ,("textile" , mkStringReader readTextile) -- TODO : textile+lhs
254− ,("html" , mkStringReader readHtml)
255− ,("latex" , mkStringReader readLaTeX)
256− ,("haddock" , mkStringReader readHaddock)
257− ,("twiki" , mkStringReader readTWiki)
258− ,("docx" , mkBSReaderWithWarnings readDocxWithWarnings)
259− ,("odt" , mkBSReader readOdt)
260− ,("t2t" , mkStringReader readTxt2TagsNoMacros)
261− ,("epub" , mkBSReader readEPUB)
226+readers :: PandocMonad m => [(String, Reader m)]
227+readers = [ ("native" , StringReader readNative)
228+ ,("json" , StringReader $ \o s ->
229+ case readJSON o s of
230+ Right doc -> return doc
231+ Left _ -> throwError $ PandocParseError "JSON parse error")
232+ ,("markdown" , StringReader readMarkdown)
233+ ,("markdown_strict" , StringReader readMarkdown)
234+ ,("markdown_phpextra" , StringReader readMarkdown)
235+ ,("markdown_github" , StringReader readMarkdown)
236+ ,("markdown_mmd", StringReader readMarkdown)
237+ ,("commonmark" , StringReader readCommonMark)
238+ ,("rst" , StringReader readRST)
239+ ,("mediawiki" , StringReader readMediaWiki)
240+ ,("docbook" , StringReader readDocBook)
241+ ,("opml" , StringReader readOPML)
242+ ,("org" , StringReader readOrg)
243+ ,("textile" , StringReader readTextile) -- TODO : textile+lhs
244+ ,("html" , StringReader readHtml)
245+ ,("latex" , StringReader readLaTeX)
246+ ,("haddock" , StringReader readHaddock)
247+ ,("twiki" , StringReader readTWiki)
248+ ,("docx" , ByteStringReader readDocx)
249+ ,("odt" , ByteStringReader readOdt)
250+ ,("t2t" , StringReader readTxt2Tags)
251+ ,("epub" , ByteStringReader readEPUB)
262252 ]
263253
264−data Writer = PureStringWriter (WriterOptions -> Pandoc -> String)
265− | IOStringWriter (WriterOptions -> Pandoc -> IO String)
266− | IOByteStringWriter (WriterOptions -> Pandoc -> IO BL.ByteString)
254+data Writer m = StringWriter (WriterOptions -> Pandoc -> m String)
255+ | ByteStringWriter (WriterOptions -> Pandoc -> m BL.ByteString)
267256
268257-- | Association list of formats and writers.
269−writers :: [ ( String, Writer ) ]
258+writers :: PandocMonad m => [ ( String, Writer m) ]
270259writers = [
271− ("native" , PureStringWriter writeNative)
272− ,("json" , PureStringWriter writeJSON)
273− ,("docx" , IOByteStringWriter writeDocx)
274− ,("odt" , IOByteStringWriter writeODT)
275− ,("epub" , IOByteStringWriter $ \o ->
276− writeEPUB o{ writerEpubVersion = Just EPUB2 })
277− ,("epub3" , IOByteStringWriter $ \o ->
278− writeEPUB o{ writerEpubVersion = Just EPUB3 })
279− ,("fb2" , IOStringWriter writeFB2)
280− ,("html" , PureStringWriter writeHtmlString)
281− ,("html5" , PureStringWriter $ \o ->
282− writeHtmlString o{ writerHtml5 = True })
283− ,("icml" , IOStringWriter writeICML)
284− ,("s5" , PureStringWriter $ \o ->
285− writeHtmlString o{ writerSlideVariant = S5Slides
286− , writerTableOfContents = False })
287− ,("slidy" , PureStringWriter $ \o ->
288− writeHtmlString o{ writerSlideVariant = SlidySlides })
289− ,("slideous" , PureStringWriter $ \o ->
290− writeHtmlString o{ writerSlideVariant = SlideousSlides })
291− ,("dzslides" , PureStringWriter $ \o ->
292− writeHtmlString o{ writerSlideVariant = DZSlides
293− , writerHtml5 = True })
294− ,("revealjs" , PureStringWriter $ \o ->
295− writeHtmlString o{ writerSlideVariant = RevealJsSlides
296− , writerHtml5 = True })
297− ,("docbook" , PureStringWriter writeDocbook)
298− ,("docbook5" , PureStringWriter $ \o ->
299− writeDocbook o{ writerDocbook5 = True })
300− ,("opml" , PureStringWriter writeOPML)
301− ,("opendocument" , PureStringWriter writeOpenDocument)
302− ,("latex" , PureStringWriter writeLaTeX)
303− ,("beamer" , PureStringWriter $ \o ->
304− writeLaTeX o{ writerBeamer = True })
305− ,("context" , PureStringWriter writeConTeXt)
306− ,("texinfo" , PureStringWriter writeTexinfo)
307− ,("man" , PureStringWriter writeMan)
308− ,("markdown" , PureStringWriter writeMarkdown)
309− ,("markdown_strict" , PureStringWriter writeMarkdown)
310− ,("markdown_phpextra" , PureStringWriter writeMarkdown)
311− ,("markdown_github" , PureStringWriter writeMarkdown)
312− ,("markdown_mmd" , PureStringWriter writeMarkdown)
313− ,("plain" , PureStringWriter writePlain)
314− ,("rst" , PureStringWriter writeRST)
315− ,("mediawiki" , PureStringWriter writeMediaWiki)
316− ,("dokuwiki" , PureStringWriter writeDokuWiki)
317− ,("zimwiki" , PureStringWriter writeZimWiki)
318− ,("textile" , PureStringWriter writeTextile)
319− ,("rtf" , IOStringWriter writeRTFWithEmbeddedImages)
320− ,("org" , PureStringWriter writeOrg)
321− ,("asciidoc" , PureStringWriter writeAsciiDoc)
322− ,("haddock" , PureStringWriter writeHaddock)
323− ,("commonmark" , PureStringWriter writeCommonMark)
324− ,("tei" , PureStringWriter writeTEI)
260+ ("native" , StringWriter writeNative)
261+ ,("json" , StringWriter $ \o d -> return $ writeJSON o d)
262+ ,("docx" , ByteStringWriter writeDocx)
263+ ,("odt" , ByteStringWriter writeODT)
264+ ,("epub" , ByteStringWriter writeEPUB2)
265+ ,("epub2" , ByteStringWriter writeEPUB2)
266+ ,("epub3" , ByteStringWriter writeEPUB3)
267+ ,("fb2" , StringWriter writeFB2)
268+ ,("html" , StringWriter writeHtml5String)
269+ ,("html4" , StringWriter writeHtml4String)
270+ ,("html5" , StringWriter writeHtml5String)
271+ ,("icml" , StringWriter writeICML)
272+ ,("s5" , StringWriter writeS5)
273+ ,("slidy" , StringWriter writeSlidy)
274+ ,("slideous" , StringWriter writeSlideous)
275+ ,("dzslides" , StringWriter writeDZSlides)
276+ ,("revealjs" , StringWriter writeRevealJs)
277+ ,("docbook" , StringWriter writeDocbook5)
278+ ,("docbook4" , StringWriter writeDocbook4)
279+ ,("docbook5" , StringWriter writeDocbook5)
280+ ,("opml" , StringWriter writeOPML)
281+ ,("opendocument" , StringWriter writeOpenDocument)
282+ ,("latex" , StringWriter writeLaTeX)
283+ ,("beamer" , StringWriter writeBeamer)
284+ ,("context" , StringWriter writeConTeXt)
285+ ,("texinfo" , StringWriter writeTexinfo)
286+ ,("man" , StringWriter writeMan)
287+ ,("markdown" , StringWriter writeMarkdown)
288+ ,("markdown_strict" , StringWriter writeMarkdown)
289+ ,("markdown_phpextra" , StringWriter writeMarkdown)
290+ ,("markdown_github" , StringWriter writeMarkdown)
291+ ,("markdown_mmd" , StringWriter writeMarkdown)
292+ ,("plain" , StringWriter writePlain)
293+ ,("rst" , StringWriter writeRST)
294+ ,("mediawiki" , StringWriter writeMediaWiki)
295+ ,("dokuwiki" , StringWriter writeDokuWiki)
296+ ,("zimwiki" , StringWriter writeZimWiki)
297+ ,("textile" , StringWriter writeTextile)
298+ ,("rtf" , StringWriter writeRTF)
299+ ,("org" , StringWriter writeOrg)
300+ ,("asciidoc" , StringWriter writeAsciiDoc)
301+ ,("haddock" , StringWriter writeHaddock)
302+ ,("commonmark" , StringWriter writeCommonMark)
303+ ,("tei" , StringWriter writeTEI)
325304 ]
326305
327−getDefaultExtensions :: String -> Set Extension
306+getDefaultExtensions :: String -> Extensions
328307getDefaultExtensions "markdown_strict" = strictExtensions
329308getDefaultExtensions "markdown_phpextra" = phpMarkdownExtraExtensions
330309getDefaultExtensions "markdown_mmd" = multimarkdownExtensions
331310getDefaultExtensions "markdown_github" = githubMarkdownExtensions
332311getDefaultExtensions "markdown" = pandocExtensions
333312getDefaultExtensions "plain" = plainExtensions
334−getDefaultExtensions "org" = Set.fromList [Ext_citations,
335− Ext_auto_identifiers]
336−getDefaultExtensions "textile" = Set.fromList [Ext_auto_identifiers]
337−getDefaultExtensions "html" = Set.fromList [Ext_auto_identifiers,
338− Ext_native_divs,
339− Ext_native_spans]
313+getDefaultExtensions "org" = extensionsFromList
314+ [Ext_citations,
315+ Ext_auto_identifiers]
316+getDefaultExtensions "html" = extensionsFromList
317+ [Ext_auto_identifiers,
318+ Ext_native_divs,
319+ Ext_native_spans]
320+getDefaultExtensions "html4" = getDefaultExtensions "html"
340321getDefaultExtensions "html5" = getDefaultExtensions "html"
341−getDefaultExtensions "epub" = Set.fromList [Ext_raw_html,
342− Ext_native_divs,
343− Ext_native_spans,
344− Ext_epub_html_exts]
345−getDefaultExtensions _ = Set.fromList [Ext_auto_identifiers]
322+getDefaultExtensions "epub" = extensionsFromList
323+ [Ext_raw_html,
324+ Ext_native_divs,
325+ Ext_native_spans,
326+ Ext_epub_html_exts]
327+getDefaultExtensions "epub2" = getDefaultExtensions "epub"
328+getDefaultExtensions "epub3" = getDefaultExtensions "epub"
329+getDefaultExtensions "latex" = extensionsFromList
330+ [Ext_smart,
331+ Ext_auto_identifiers]
332+getDefaultExtensions "context" = extensionsFromList
333+ [Ext_smart,
334+ Ext_auto_identifiers]
335+getDefaultExtensions "textile" = extensionsFromList
336+ [Ext_old_dashes,
337+ Ext_smart,
338+ Ext_auto_identifiers]
339+getDefaultExtensions _ = extensionsFromList
340+ [Ext_auto_identifiers]
346341
347342-- | Retrieve reader based on formatSpec (format+extensions).
348−getReader :: String -> Either String Reader
343+getReader :: PandocMonad m => String -> Either String (Reader m)
349344getReader s =
350345 case parseFormatSpec s of
351346 Left e -> Left $ intercalate "\n" [m | Message m <- errorMessages e]
@@ -359,32 +354,22 @@ getReader s =
359354 r o{ readerExtensions = setExts $
360355 getDefaultExtensions readerName }
361356
362−-- | Retrieve writer based on formatSpec (format+extensions).
363−getWriter :: String -> Either String Writer
357+getWriter :: PandocMonad m => String -> Either String (Writer m)
364358getWriter s
365359 = case parseFormatSpec s of
366360 Left e -> Left $ intercalate "\n" [m | Message m <- errorMessages e]
367361 Right (writerName, setExts) ->
368362 case lookup writerName writers of
369363 Nothing -> Left $ "Unknown writer: " ++ writerName
370− Just (PureStringWriter r) -> Right $ PureStringWriter $
371− \o -> r o{ writerExtensions = setExts $
372− getDefaultExtensions writerName }
373− Just (IOStringWriter r) -> Right $ IOStringWriter $
364+ Just (StringWriter r) -> Right $ StringWriter $
374365 \o -> r o{ writerExtensions = setExts $
375366 getDefaultExtensions writerName }
376− Just (IOByteStringWriter r) -> Right $ IOByteStringWriter $
367+ Just (ByteStringWriter r) -> Right $ ByteStringWriter $
377368 \o -> r o{ writerExtensions = setExts $
378369 getDefaultExtensions writerName }
379370
380−{-# DEPRECATED toJsonFilter "Use 'toJSONFilter' from 'Text.Pandoc.JSON' instead" #-}
381−-- | Deprecated. Use @toJSONFilter@ from @Text.Pandoc.JSON@ instead.
382−class ToJSONFilter a => ToJsonFilter a
383− where toJsonFilter :: a -> IO ()
384− toJsonFilter = toJSONFilter
385−
386371readJSON :: ReaderOptions -> String -> Either PandocError Pandoc
387−readJSON _ = mapLeft ParseFailure . eitherDecode' . UTF8.fromStringLazy
372+readJSON _ = mapLeft PandocParseError . eitherDecode' . UTF8.fromStringLazy
388373
389374writeJSON :: WriterOptions -> Pandoc -> String
390375writeJSON _ = UTF8.toStringLazy . encode
src/Text/Pandoc/Class.hsnew file+585
@@ -0,0 +1,585 @@
1+{-# LANGUAGE DeriveFunctor, DeriveDataTypeable, TypeSynonymInstances,
2+FlexibleInstances, GeneralizedNewtypeDeriving, FlexibleContexts #-}
3+
4+{-
5+Copyright (C) 2016 Jesse Rosenthal <jrosenthal@jhu.edu>
6+
7+This program is free software; you can redistribute it and/or modify
8+it under the terms of the GNU General Public License as published by
9+the Free Software Foundation; either version 2 of the License, or
10+(at your option) any later version.
11+
12+This program is distributed in the hope that it will be useful,
13+but WITHOUT ANY WARRANTY; without even the implied warranty of
14+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+GNU General Public License for more details.
16+
17+You should have received a copy of the GNU General Public License
18+along with this program; if not, write to the Free Software
19+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20+-}
21+
22+{- |
23+ Module : Text.Pandoc.Class
24+ Copyright : Copyright (C) 2016 Jesse Rosenthal
25+ License : GNU GPL, version 2 or above
26+
27+ Maintainer : Jesse Rosenthal <jrosenthal@jhu.edu>
28+ Stability : alpha
29+ Portability : portable
30+
31+Typeclass for pandoc readers and writers, allowing both IO and pure instances.
32+-}
33+
34+module Text.Pandoc.Class ( PandocMonad(..)
35+ , CommonState(..)
36+ , PureState(..)
37+ , getPureState
38+ , getsPureState
39+ , putPureState
40+ , modifyPureState
41+ , getPOSIXTime
42+ , getZonedTime
43+ , warning
44+ , warningWithPos
45+ , report
46+ , getLog
47+ , setVerbosity
48+ , getMediaBag
49+ , setMediaBag
50+ , insertMedia
51+ , insertDeferredMedia
52+ , fetchItem
53+ , getInputFiles
54+ , getOutputFile
55+ , PandocIO(..)
56+ , PandocPure(..)
57+ , FileTree(..)
58+ , FileInfo(..)
59+ , runIO
60+ , runIOorExplode
61+ , runPure
62+ , withMediaBag
63+ ) where
64+
65+import Prelude hiding (readFile)
66+import System.Random (StdGen, next, mkStdGen)
67+import qualified System.Random as IO (newStdGen)
68+import Codec.Archive.Zip (Archive, fromArchive, emptyArchive)
69+import Data.Unique (hashUnique)
70+import qualified Data.Unique as IO (newUnique)
71+import qualified Text.Pandoc.Shared as IO ( readDataFile
72+ , openURL )
73+import qualified Text.Pandoc.UTF8 as UTF8
74+import Text.Pandoc.Compat.Time (UTCTime)
75+import Text.Pandoc.Options (Verbosity(..))
76+import Text.Pandoc.Parsing (ParserT, SourcePos)
77+import qualified Text.Pandoc.Compat.Time as IO (getCurrentTime)
78+import Text.Pandoc.MIME (MimeType, getMimeType)
79+import Data.Time.Clock.POSIX ( utcTimeToPOSIXSeconds
80+ , posixSecondsToUTCTime
81+ , POSIXTime )
82+import Data.Time.LocalTime (TimeZone, ZonedTime, utcToZonedTime, utc)
83+import Network.URI ( escapeURIString, nonStrictRelativeTo,
84+ unEscapeString, parseURIReference, isAllowedInURI,
85+ parseURI, URI(..) )
86+import qualified Data.Time.LocalTime as IO (getCurrentTimeZone)
87+import Text.Pandoc.MediaBag (MediaBag, lookupMedia)
88+import qualified Text.Pandoc.MediaBag as MB
89+import qualified Data.ByteString as B
90+import qualified Data.ByteString.Lazy as BL
91+import qualified System.Environment as IO (lookupEnv)
92+import System.FilePath.Glob (match, compile)
93+import System.FilePath ((</>), takeExtension, dropExtension)
94+import qualified System.FilePath.Glob as IO (glob)
95+import qualified System.Directory as IO (getModificationTime)
96+import Control.Monad as M (fail)
97+import Control.Monad.Reader (ReaderT)
98+import Control.Monad.State
99+import Control.Monad.Except
100+import Control.Monad.Writer (WriterT)
101+import Control.Monad.RWS (RWST)
102+import Data.Word (Word8)
103+import Data.Default
104+import System.IO.Error
105+import System.IO (stderr)
106+import qualified Data.Map as M
107+import Text.Pandoc.Error
108+import Data.Monoid
109+import Data.Maybe (catMaybes)
110+import Text.Printf (printf)
111+
112+class (Functor m, Applicative m, Monad m, MonadError PandocError m)
113+ => PandocMonad m where
114+ lookupEnv :: String -> m (Maybe String)
115+ getCurrentTime :: m UTCTime
116+ getCurrentTimeZone :: m TimeZone
117+ newStdGen :: m StdGen
118+ newUniqueHash :: m Int
119+ openURL :: String -> m (B.ByteString, Maybe MimeType)
120+ readFileLazy :: FilePath -> m BL.ByteString
121+ readFileStrict :: FilePath -> m B.ByteString
122+ readDataFile :: Maybe FilePath
123+ -> FilePath
124+ -> m B.ByteString
125+ glob :: String -> m [FilePath]
126+ getModificationTime :: FilePath -> m UTCTime
127+ getCommonState :: m CommonState
128+ putCommonState :: CommonState -> m ()
129+
130+ getsCommonState :: (CommonState -> a) -> m a
131+ getsCommonState f = f <$> getCommonState
132+
133+ modifyCommonState :: (CommonState -> CommonState) -> m ()
134+ modifyCommonState f = getCommonState >>= putCommonState . f
135+
136+ logOutput :: Verbosity -> String -> m ()
137+
138+-- Functions defined for all PandocMonad instances
139+
140+setVerbosity :: PandocMonad m => Verbosity -> m ()
141+setVerbosity verbosity =
142+ modifyCommonState $ \st -> st{ stVerbosity = verbosity }
143+
144+getLog :: PandocMonad m => m [(Verbosity, String)]
145+getLog = reverse <$> getsCommonState stLog
146+
147+warning :: PandocMonad m => String -> m ()
148+warning msg = report WARNING msg
149+
150+warningWithPos :: PandocMonad m
151+ => SourcePos
152+ -> String
153+ -> ParserT s st m ()
154+warningWithPos pos msg = lift $ warning $ msg ++ " " ++ show pos
155+
156+report :: PandocMonad m => Verbosity -> String -> m ()
157+report level msg = do
158+ verbosity <- getsCommonState stVerbosity
159+ when (level <= verbosity) $ do
160+ logOutput verbosity msg
161+ unless (level == DEBUG) $
162+ modifyCommonState $ \st -> st{ stLog = (level, msg) : stLog st }
163+
164+setMediaBag :: PandocMonad m => MediaBag -> m ()
165+setMediaBag mb = modifyCommonState $
166+ \st -> st{stDeferredMediaBag = DeferredMediaBag mb mempty}
167+
168+getMediaBag :: PandocMonad m => m MediaBag
169+getMediaBag = do
170+ fetchDeferredMedia
171+ DeferredMediaBag mb' _ <- getsCommonState stDeferredMediaBag
172+ return mb'
173+
174+fetchDeferredMedia :: PandocMonad m => m ()
175+fetchDeferredMedia = do
176+ (DeferredMediaBag mb defMedia) <- getsCommonState stDeferredMediaBag
177+ fetchedMedia <- catMaybes <$> mapM fetchMediaItem defMedia
178+ setMediaBag $ foldr
179+ (\(fp, bs, mbMime) mb' -> MB.insertMedia fp mbMime (BL.fromStrict bs) mb')
180+ mb fetchedMedia
181+
182+insertMedia :: PandocMonad m => FilePath -> Maybe MimeType -> BL.ByteString -> m ()
183+insertMedia fp mime bs = do
184+ (DeferredMediaBag mb dm) <- getsCommonState stDeferredMediaBag
185+ let mb' = MB.insertMedia fp mime bs mb
186+ modifyCommonState $ \st -> st{stDeferredMediaBag =DeferredMediaBag mb' dm }
187+
188+insertDeferredMedia :: PandocMonad m => FilePath -> m ()
189+insertDeferredMedia fp = do
190+ (DeferredMediaBag mb dm) <- getsCommonState stDeferredMediaBag
191+ modifyCommonState $
192+ \st -> st{stDeferredMediaBag = DeferredMediaBag mb ((DeferredMediaPath fp) : dm)}
193+
194+getInputFiles :: PandocMonad m => m (Maybe [FilePath])
195+getInputFiles = getsCommonState stInputFiles
196+
197+getOutputFile :: PandocMonad m => m (Maybe FilePath)
198+getOutputFile = getsCommonState stOutputFile
199+
200+getPOSIXTime :: (PandocMonad m) => m POSIXTime
201+getPOSIXTime = utcTimeToPOSIXSeconds <$> getCurrentTime
202+
203+getZonedTime :: (PandocMonad m) => m ZonedTime
204+getZonedTime = do
205+ t <- getCurrentTime
206+ tz <- getCurrentTimeZone
207+ return $ utcToZonedTime tz t
208+
209+--
210+
211+newtype DeferredMediaPath = DeferredMediaPath {unDefer :: String}
212+ deriving (Show, Eq)
213+
214+data DeferredMediaBag = DeferredMediaBag MediaBag [DeferredMediaPath]
215+ deriving (Show)
216+
217+instance Monoid DeferredMediaBag where
218+ mempty = DeferredMediaBag mempty mempty
219+ mappend (DeferredMediaBag mb lst) (DeferredMediaBag mb' lst') =
220+ DeferredMediaBag (mb <> mb') (lst <> lst')
221+
222+-- the internal function for downloading individual items. We want to
223+-- catch errors and return a Nothing with a warning, so we can
224+-- continue without erroring out.
225+fetchMediaItem :: PandocMonad m
226+ => DeferredMediaPath
227+ -> m (Maybe (FilePath, B.ByteString, Maybe MimeType))
228+fetchMediaItem dfp =
229+ (do (bs, mbmime) <- downloadOrRead Nothing (unDefer dfp)
230+ return $ Just $ (unDefer dfp, bs, mbmime))
231+ `catchError`
232+ (const $ do warning ("Couldn't access media at " ++ unDefer dfp)
233+ return Nothing)
234+
235+data CommonState = CommonState { stLog :: [(Verbosity, String)]
236+ , stDeferredMediaBag :: DeferredMediaBag
237+ , stInputFiles :: Maybe [FilePath]
238+ , stOutputFile :: Maybe FilePath
239+ , stVerbosity :: Verbosity
240+ }
241+
242+instance Default CommonState where
243+ def = CommonState { stLog = []
244+ , stDeferredMediaBag = mempty
245+ , stInputFiles = Nothing
246+ , stOutputFile = Nothing
247+ , stVerbosity = WARNING
248+ }
249+
250+runIO :: PandocIO a -> IO (Either PandocError a)
251+runIO ma = flip evalStateT def $ runExceptT $ unPandocIO ma
252+
253+withMediaBag :: PandocMonad m => m a -> m (a, MediaBag)
254+withMediaBag ma = ((,)) <$> ma <*> getMediaBag
255+
256+runIOorExplode :: PandocIO a -> IO a
257+runIOorExplode ma = runIO ma >>= handleError
258+
259+newtype PandocIO a = PandocIO {
260+ unPandocIO :: ExceptT PandocError (StateT CommonState IO) a
261+ } deriving ( MonadIO
262+ , Functor
263+ , Applicative
264+ , Monad
265+ , MonadError PandocError
266+ )
267+
268+instance PandocMonad PandocIO where
269+ lookupEnv = liftIO . IO.lookupEnv
270+ getCurrentTime = liftIO IO.getCurrentTime
271+ getCurrentTimeZone = liftIO IO.getCurrentTimeZone
272+ newStdGen = liftIO IO.newStdGen
273+ newUniqueHash = hashUnique <$> (liftIO IO.newUnique)
274+ openURL u = do
275+ eitherRes <- liftIO $ (tryIOError $ IO.openURL u)
276+ case eitherRes of
277+ Right (Right res) -> return res
278+ Right (Left _) -> throwError $ PandocFileReadError u
279+ Left _ -> throwError $ PandocFileReadError u
280+ readFileLazy s = do
281+ eitherBS <- liftIO (tryIOError $ BL.readFile s)
282+ case eitherBS of
283+ Right bs -> return bs
284+ Left _ -> throwError $ PandocFileReadError s
285+ readFileStrict s = do
286+ eitherBS <- liftIO (tryIOError $ B.readFile s)
287+ case eitherBS of
288+ Right bs -> return bs
289+ Left _ -> throwError $ PandocFileReadError s
290+ -- TODO: Make this more sensitive to the different sorts of failure
291+ readDataFile mfp fname = do
292+ eitherBS <- liftIO (tryIOError $ IO.readDataFile mfp fname)
293+ case eitherBS of
294+ Right bs -> return bs
295+ Left _ -> throwError $ PandocFileReadError fname
296+ glob = liftIO . IO.glob
297+ getModificationTime fp = do
298+ eitherMtime <- liftIO (tryIOError $ IO.getModificationTime fp)
299+ case eitherMtime of
300+ Right mtime -> return mtime
301+ Left _ -> throwError $ PandocFileReadError fp
302+ getCommonState = PandocIO $ lift get
303+ putCommonState x = PandocIO $ lift $ put x
304+ logOutput level msg =
305+ liftIO $ UTF8.hPutStrLn stderr $ printf "%-7s %s" (show level) msg
306+
307+-- | Specialized version of parseURIReference that disallows
308+-- single-letter schemes. Reason: these are usually windows absolute
309+-- paths.
310+parseURIReference' :: String -> Maybe URI
311+parseURIReference' s =
312+ case parseURIReference s of
313+ Just u
314+ | length (uriScheme u) > 2 -> Just u
315+ | null (uriScheme u) -> Just u -- protocol-relative
316+ _ -> Nothing
317+
318+-- | Fetch an image or other item from the local filesystem or the net.
319+-- Returns raw content and maybe mime type.
320+fetchItem :: PandocMonad m
321+ => Maybe String
322+ -> String
323+ -> m (B.ByteString, Maybe MimeType)
324+fetchItem sourceURL s = do
325+ mediabag <- getMediaBag
326+ case lookupMedia s mediabag of
327+ Just (mime, bs) -> return $ (BL.toStrict bs, Just mime)
328+ Nothing -> downloadOrRead sourceURL s
329+
330+downloadOrRead :: PandocMonad m
331+ => Maybe String
332+ -> String
333+ -> m (B.ByteString, Maybe MimeType)
334+downloadOrRead sourceURL s = do
335+ case (sourceURL >>= parseURIReference' .
336+ ensureEscaped, ensureEscaped s) of
337+ (Just u, s') -> -- try fetching from relative path at source
338+ case parseURIReference' s' of
339+ Just u' -> openURL $ show $ u' `nonStrictRelativeTo` u
340+ Nothing -> openURL s' -- will throw error
341+ (Nothing, s'@('/':'/':_)) -> -- protocol-relative URI
342+ case parseURIReference' s' of
343+ Just u' -> openURL $ show $ u' `nonStrictRelativeTo` httpcolon
344+ Nothing -> openURL s' -- will throw error
345+ (Nothing, s') ->
346+ case parseURI s' of -- requires absolute URI
347+ -- We don't want to treat C:/ as a scheme:
348+ Just u' | length (uriScheme u') > 2 -> openURL (show u')
349+ Just u' | uriScheme u' == "file:" ->
350+ readLocalFile $ dropWhile (=='/') (uriPath u')
351+ _ -> readLocalFile fp -- get from local file system
352+ where readLocalFile f = do
353+ cont <- readFileStrict f
354+ return (cont, mime)
355+ httpcolon = URI{ uriScheme = "http:",
356+ uriAuthority = Nothing,
357+ uriPath = "",
358+ uriQuery = "",
359+ uriFragment = "" }
360+ dropFragmentAndQuery = takeWhile (\c -> c /= '?' && c /= '#')
361+ fp = unEscapeString $ dropFragmentAndQuery s
362+ mime = case takeExtension fp of
363+ ".gz" -> getMimeType $ dropExtension fp
364+ ".svgz" -> getMimeType $ dropExtension fp ++ ".svg"
365+ x -> getMimeType x
366+ ensureEscaped = escapeURIString isAllowedInURI . map convertSlash
367+ convertSlash '\\' = '/'
368+ convertSlash x = x
369+
370+data PureState = PureState { stStdGen :: StdGen
371+ , stWord8Store :: [Word8] -- should be
372+ -- inifinite,
373+ -- i.e. [1..]
374+ , stUniqStore :: [Int] -- should be
375+ -- inifinite and
376+ -- contain every
377+ -- element at most
378+ -- once, e.g. [1..]
379+ , stEnv :: [(String, String)]
380+ , stTime :: UTCTime
381+ , stTimeZone :: TimeZone
382+ , stReferenceDocx :: Archive
383+ , stReferenceODT :: Archive
384+ , stFiles :: FileTree
385+ , stUserDataDir :: FileTree
386+ , stCabalDataDir :: FileTree
387+ , stFontFiles :: [FilePath]
388+ }
389+
390+instance Default PureState where
391+ def = PureState { stStdGen = mkStdGen 1848
392+ , stWord8Store = [1..]
393+ , stUniqStore = [1..]
394+ , stEnv = [("USER", "pandoc-user")]
395+ , stTime = posixSecondsToUTCTime 0
396+ , stTimeZone = utc
397+ , stReferenceDocx = emptyArchive
398+ , stReferenceODT = emptyArchive
399+ , stFiles = mempty
400+ , stUserDataDir = mempty
401+ , stCabalDataDir = mempty
402+ , stFontFiles = []
403+ }
404+
405+
406+getPureState :: PandocPure PureState
407+getPureState = PandocPure $ lift $ lift $ get
408+
409+getsPureState :: (PureState -> a) -> PandocPure a
410+getsPureState f = f <$> getPureState
411+
412+putPureState :: PureState -> PandocPure ()
413+putPureState ps= PandocPure $ lift $ lift $ put ps
414+
415+modifyPureState :: (PureState -> PureState) -> PandocPure ()
416+modifyPureState f = PandocPure $ lift $ lift $ modify f
417+
418+
419+data FileInfo = FileInfo { infoFileMTime :: UTCTime
420+ , infoFileContents :: B.ByteString
421+ }
422+
423+newtype FileTree = FileTree {unFileTree :: M.Map FilePath FileInfo}
424+ deriving (Monoid)
425+
426+getFileInfo :: FilePath -> FileTree -> Maybe FileInfo
427+getFileInfo fp tree = M.lookup fp $ unFileTree tree
428+
429+
430+newtype PandocPure a = PandocPure {
431+ unPandocPure :: ExceptT PandocError
432+ (StateT CommonState (State PureState)) a
433+ } deriving ( Functor
434+ , Applicative
435+ , Monad
436+ , MonadError PandocError
437+ )
438+
439+runPure :: PandocPure a -> Either PandocError a
440+runPure x = flip evalState def $
441+ flip evalStateT def $
442+ runExceptT $
443+ unPandocPure x
444+
445+instance PandocMonad PandocPure where
446+ lookupEnv s = do
447+ env <- getsPureState stEnv
448+ return (lookup s env)
449+
450+ getCurrentTime = getsPureState stTime
451+
452+ getCurrentTimeZone = getsPureState stTimeZone
453+
454+ newStdGen = do
455+ g <- getsPureState stStdGen
456+ let (_, nxtGen) = next g
457+ modifyPureState $ \st -> st { stStdGen = nxtGen }
458+ return g
459+
460+ newUniqueHash = do
461+ uniqs <- getsPureState stUniqStore
462+ case uniqs of
463+ u : us -> do
464+ modifyPureState $ \st -> st { stUniqStore = us }
465+ return u
466+ _ -> M.fail "uniq store ran out of elements"
467+ openURL _ = throwError $ PandocSomeError "Cannot open URL in PandocPure"
468+ readFileLazy fp = do
469+ fps <- getsPureState stFiles
470+ case infoFileContents <$> getFileInfo fp fps of
471+ Just bs -> return (BL.fromStrict bs)
472+ Nothing -> throwError $ PandocFileReadError fp
473+ readFileStrict fp = do
474+ fps <- getsPureState stFiles
475+ case infoFileContents <$> getFileInfo fp fps of
476+ Just bs -> return bs
477+ Nothing -> throwError $ PandocFileReadError fp
478+ readDataFile Nothing "reference.docx" = do
479+ (B.concat . BL.toChunks . fromArchive) <$> getsPureState stReferenceDocx
480+ readDataFile Nothing "reference.odt" = do
481+ (B.concat . BL.toChunks . fromArchive) <$> getsPureState stReferenceODT
482+ readDataFile Nothing fname = do
483+ let fname' = if fname == "MANUAL.txt" then fname else "data" </> fname
484+ readFileStrict fname'
485+ readDataFile (Just userDir) fname = do
486+ userDirFiles <- getsPureState stUserDataDir
487+ case infoFileContents <$> (getFileInfo (userDir </> fname) userDirFiles) of
488+ Just bs -> return bs
489+ Nothing -> readDataFile Nothing fname
490+
491+ glob s = do
492+ fontFiles <- getsPureState stFontFiles
493+ return (filter (match (compile s)) fontFiles)
494+
495+ getModificationTime fp = do
496+ fps <- getsPureState stFiles
497+ case infoFileMTime <$> (getFileInfo fp fps) of
498+ Just tm -> return tm
499+ Nothing -> throwError $ PandocFileReadError fp
500+
501+ getCommonState = PandocPure $ lift $ get
502+ putCommonState x = PandocPure $ lift $ put x
503+
504+ logOutput _level _msg = return ()
505+
506+instance PandocMonad m => PandocMonad (ParserT s st m) where
507+ lookupEnv = lift . lookupEnv
508+ getCurrentTime = lift getCurrentTime
509+ getCurrentTimeZone = lift getCurrentTimeZone
510+ newStdGen = lift newStdGen
511+ newUniqueHash = lift newUniqueHash
512+ openURL = lift . openURL
513+ readFileLazy = lift . readFileLazy
514+ readFileStrict = lift . readFileStrict
515+ readDataFile mbuserdir = lift . readDataFile mbuserdir
516+ glob = lift . glob
517+ getModificationTime = lift . getModificationTime
518+ getCommonState = lift getCommonState
519+ putCommonState = lift . putCommonState
520+ logOutput lvl = lift . logOutput lvl
521+
522+instance PandocMonad m => PandocMonad (ReaderT r m) where
523+ lookupEnv = lift . lookupEnv
524+ getCurrentTime = lift getCurrentTime
525+ getCurrentTimeZone = lift getCurrentTimeZone
526+ newStdGen = lift newStdGen
527+ newUniqueHash = lift newUniqueHash
528+ openURL = lift . openURL
529+ readFileLazy = lift . readFileLazy
530+ readFileStrict = lift . readFileStrict
531+ readDataFile mbuserdir = lift . readDataFile mbuserdir
532+ glob = lift . glob
533+ getModificationTime = lift . getModificationTime
534+ getCommonState = lift getCommonState
535+ putCommonState = lift . putCommonState
536+ logOutput lvl = lift . logOutput lvl
537+
538+instance (PandocMonad m, Monoid w) => PandocMonad (WriterT w m) where
539+ lookupEnv = lift . lookupEnv
540+ getCurrentTime = lift getCurrentTime
541+ getCurrentTimeZone = lift getCurrentTimeZone
542+ newStdGen = lift newStdGen
543+ newUniqueHash = lift newUniqueHash
544+ openURL = lift . openURL
545+ readFileLazy = lift . readFileLazy
546+ readFileStrict = lift . readFileStrict
547+ readDataFile mbuserdir = lift . readDataFile mbuserdir
548+ glob = lift . glob
549+ getModificationTime = lift . getModificationTime
550+ getCommonState = lift getCommonState
551+ putCommonState = lift . putCommonState
552+ logOutput lvl = lift . logOutput lvl
553+
554+instance (PandocMonad m, Monoid w) => PandocMonad (RWST r w st m) where
555+ lookupEnv = lift . lookupEnv
556+ getCurrentTime = lift getCurrentTime
557+ getCurrentTimeZone = lift getCurrentTimeZone
558+ newStdGen = lift newStdGen
559+ newUniqueHash = lift newUniqueHash
560+ openURL = lift . openURL
561+ readFileLazy = lift . readFileLazy
562+ readFileStrict = lift . readFileStrict
563+ readDataFile mbuserdir = lift . readDataFile mbuserdir
564+ glob = lift . glob
565+ getModificationTime = lift . getModificationTime
566+ getCommonState = lift getCommonState
567+ putCommonState = lift . putCommonState
568+ logOutput lvl = lift . logOutput lvl
569+
570+instance PandocMonad m => PandocMonad (StateT st m) where
571+ lookupEnv = lift . lookupEnv
572+ getCurrentTime = lift getCurrentTime
573+ getCurrentTimeZone = lift getCurrentTimeZone
574+ newStdGen = lift newStdGen
575+ newUniqueHash = lift newUniqueHash
576+ openURL = lift . openURL
577+ readFileLazy = lift . readFileLazy
578+ readFileStrict = lift . readFileStrict
579+ readDataFile mbuserdir = lift . readDataFile mbuserdir
580+ glob = lift . glob
581+ getModificationTime = lift . getModificationTime
582+ getCommonState = lift getCommonState
583+ putCommonState = lift . putCommonState
584+ logOutput lvl = lift . logOutput lvl
585+
src/Text/Pandoc/Error.hs+25−14
@@ -33,27 +33,39 @@ module Text.Pandoc.Error (PandocError(..), handleError) where
3333
3434import Text.Parsec.Error
3535import Text.Parsec.Pos hiding (Line)
36−import GHC.Generics (Generic)
3736import Data.Generics (Typeable)
37+import GHC.Generics (Generic)
3838import Control.Exception (Exception)
39+import Text.Pandoc.Shared (err)
3940
4041type Input = String
4142
42−data PandocError = -- | Generic parse failure
43− ParseFailure String
44− -- | Error thrown by a Parsec parser
45− | ParsecError Input ParseError
43+data PandocError = PandocFileReadError FilePath
44+ | PandocShouldNeverHappenError String
45+ | PandocSomeError String
46+ | PandocParseError String
47+ | PandocParsecError Input ParseError
4648 deriving (Show, Typeable, Generic)
4749
50+
51+-- data PandocError = -- | Generic parse failure
52+-- ParseFailure String
53+-- -- | Error thrown by a Parsec parser
54+-- | ParsecError Input ParseError
55+-- deriving (Show, Typeable, Generic)
56+
4857instance Exception PandocError
4958
50−-- | An unsafe method to handle `PandocError`s.
51−handleError :: Either PandocError a -> a
52−handleError (Right r) = r
53−handleError (Left err) =
54− case err of
55− ParseFailure string -> error string
56− ParsecError input err' ->
59+-- | Handle PandocError by exiting with an error message.
60+handleError :: Either PandocError a -> IO a
61+handleError (Right r) = return r
62+handleError (Left e) =
63+ case e of
64+ PandocFileReadError fp -> err 61 $ "problem reading " ++ fp
65+ PandocShouldNeverHappenError s -> err 62 s
66+ PandocSomeError s -> err 63 s
67+ PandocParseError s -> err 64 s
68+ PandocParsecError input err' ->
5769 let errPos = errorPos err'
5870 errLine = sourceLine errPos
5971 errColumn = sourceColumn errPos
@@ -63,6 +75,5 @@ handleError (Left err) =
6375 ,"\n", replicate (errColumn - 1) ' '
6476 ,"^"]
6577 else ""
66− in error $ "\nError at " ++ show err'
67− ++ errorInFile
78+ in err 65 $ "\nError at " ++ show err' ++ errorInFile
6879
src/Text/Pandoc/Extensions.hsnew file+267
@@ -0,0 +1,267 @@
1+{-# LANGUAGE DeriveDataTypeable, DeriveGeneric #-}
2+{-
3+Copyright (C) 2012-2016 John MacFarlane <jgm@berkeley.edu>
4+
5+This program is free software; you can redistribute it and/or modify
6+it under the terms of the GNU General Public License as published by
7+the Free Software Foundation; either version 2 of the License, or
8+(at your option) any later version.
9+
10+This program is distributed in the hope that it will be useful,
11+but WITHOUT ANY WARRANTY; without even the implied warranty of
12+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+GNU General Public License for more details.
14+
15+You should have received a copy of the GNU General Public License
16+along with this program; if not, write to the Free Software
17+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18+-}
19+
20+{- |
21+ Module : Text.Pandoc.Extensions
22+ Copyright : Copyright (C) 2012-2016 John MacFarlane
23+ License : GNU GPL, version 2 or above
24+
25+ Maintainer : John MacFarlane <jgm@berkeley.edu>
26+ Stability : alpha
27+ Portability : portable
28+
29+Data structures and functions for representing markup extensions.
30+-}
31+module Text.Pandoc.Extensions ( Extension(..)
32+ , Extensions
33+ , emptyExtensions
34+ , extensionsFromList
35+ , extensionEnabled
36+ , enableExtension
37+ , disableExtension
38+ , pandocExtensions
39+ , plainExtensions
40+ , strictExtensions
41+ , phpMarkdownExtraExtensions
42+ , githubMarkdownExtensions
43+ , multimarkdownExtensions )
44+where
45+import Data.Bits (testBit, setBit, clearBit)
46+import Data.Data (Data)
47+import Data.Typeable (Typeable)
48+import GHC.Generics (Generic)
49+
50+newtype Extensions = Extensions Integer
51+ deriving (Show, Read, Eq, Ord, Data, Typeable, Generic)
52+
53+extensionsFromList :: [Extension] -> Extensions
54+extensionsFromList = foldr enableExtension emptyExtensions
55+
56+emptyExtensions :: Extensions
57+emptyExtensions = Extensions 0
58+
59+extensionEnabled :: Extension -> Extensions -> Bool
60+extensionEnabled x (Extensions exts) = testBit exts (fromEnum x)
61+
62+enableExtension :: Extension -> Extensions -> Extensions
63+enableExtension x (Extensions exts) = Extensions (setBit exts (fromEnum x))
64+
65+disableExtension :: Extension -> Extensions -> Extensions
66+disableExtension x (Extensions exts) = Extensions (clearBit exts (fromEnum x))
67+
68+-- | Individually selectable syntax extensions.
69+data Extension =
70+ Ext_footnotes -- ^ Pandoc/PHP/MMD style footnotes
71+ | Ext_inline_notes -- ^ Pandoc-style inline notes
72+ | Ext_pandoc_title_block -- ^ Pandoc title block
73+ | Ext_yaml_metadata_block -- ^ YAML metadata block
74+ | Ext_mmd_title_block -- ^ Multimarkdown metadata block
75+ | Ext_table_captions -- ^ Pandoc-style table captions
76+ | Ext_implicit_figures -- ^ A paragraph with just an image is a figure
77+ | Ext_simple_tables -- ^ Pandoc-style simple tables
78+ | Ext_multiline_tables -- ^ Pandoc-style multiline tables
79+ | Ext_grid_tables -- ^ Grid tables (pandoc, reST)
80+ | Ext_pipe_tables -- ^ Pipe tables (as in PHP markdown extra)
81+ | Ext_citations -- ^ Pandoc/citeproc citations
82+ | Ext_raw_tex -- ^ Allow raw TeX (other than math)
83+ | Ext_raw_html -- ^ Allow raw HTML
84+ | Ext_tex_math_dollars -- ^ TeX math between $..$ or $$..$$
85+ | Ext_tex_math_single_backslash -- ^ TeX math btw \(..\) \[..\]
86+ | Ext_tex_math_double_backslash -- ^ TeX math btw \\(..\\) \\[..\\]
87+ | Ext_latex_macros -- ^ Parse LaTeX macro definitions (for math only)
88+ | Ext_fenced_code_blocks -- ^ Parse fenced code blocks
89+ | Ext_fenced_code_attributes -- ^ Allow attributes on fenced code blocks
90+ | Ext_backtick_code_blocks -- ^ GitHub style ``` code blocks
91+ | Ext_inline_code_attributes -- ^ Allow attributes on inline code
92+ | Ext_markdown_in_html_blocks -- ^ Interpret as markdown inside HTML blocks
93+ | Ext_native_divs -- ^ Use Div blocks for contents of <div> tags
94+ | Ext_native_spans -- ^ Use Span inlines for contents of <span>
95+ | Ext_bracketed_spans -- ^ Bracketed spans with attributes
96+ | Ext_markdown_attribute -- ^ Interpret text inside HTML as markdown
97+ -- iff container has attribute 'markdown'
98+ | Ext_escaped_line_breaks -- ^ Treat a backslash at EOL as linebreak
99+ | Ext_link_attributes -- ^ link and image attributes
100+ | Ext_mmd_link_attributes -- ^ MMD style reference link attributes
101+ | Ext_autolink_bare_uris -- ^ Make all absolute URIs into links
102+ | Ext_fancy_lists -- ^ Enable fancy list numbers and delimiters
103+ | Ext_lists_without_preceding_blankline -- ^ Allow lists without preceding blank
104+ | Ext_startnum -- ^ Make start number of ordered list significant
105+ | Ext_definition_lists -- ^ Definition lists as in pandoc, mmd, php
106+ | Ext_compact_definition_lists -- ^ Definition lists without
107+ -- space between items, and disallow laziness
108+ | Ext_example_lists -- ^ Markdown-style numbered examples
109+ | Ext_all_symbols_escapable -- ^ Make all non-alphanumerics escapable
110+ | Ext_angle_brackets_escapable -- ^ Make < and > escapable
111+ | Ext_intraword_underscores -- ^ Treat underscore inside word as literal
112+ | Ext_blank_before_blockquote -- ^ Require blank line before a blockquote
113+ | Ext_blank_before_header -- ^ Require blank line before a header
114+ | Ext_strikeout -- ^ Strikeout using ~~this~~ syntax
115+ | Ext_superscript -- ^ Superscript using ^this^ syntax
116+ | Ext_subscript -- ^ Subscript using ~this~ syntax
117+ | Ext_hard_line_breaks -- ^ All newlines become hard line breaks
118+ | Ext_ignore_line_breaks -- ^ Newlines in paragraphs are ignored
119+ | Ext_east_asian_line_breaks -- ^ Newlines in paragraphs are ignored between
120+ -- East Asian wide characters
121+ | Ext_literate_haskell -- ^ Enable literate Haskell conventions
122+ | Ext_abbreviations -- ^ PHP markdown extra abbreviation definitions
123+ | Ext_emoji -- ^ Support emoji like :smile:
124+ | Ext_auto_identifiers -- ^ Automatic identifiers for headers
125+ | Ext_ascii_identifiers -- ^ ascii-only identifiers for headers
126+ | Ext_header_attributes -- ^ Explicit header attributes {#id .class k=v}
127+ | Ext_mmd_header_identifiers -- ^ Multimarkdown style header identifiers [myid]
128+ | Ext_implicit_header_references -- ^ Implicit reference links for headers
129+ | Ext_line_blocks -- ^ RST style line blocks
130+ | Ext_epub_html_exts -- ^ Recognise the EPUB extended version of HTML
131+ | Ext_shortcut_reference_links -- ^ Shortcut reference links
132+ | Ext_smart -- ^ "Smart" quotes, apostrophes, ellipses, dashes
133+ | Ext_old_dashes -- ^ -- = em, - before number = en
134+ deriving (Show, Read, Enum, Eq, Ord, Bounded, Data, Typeable, Generic)
135+
136+pandocExtensions :: Extensions
137+pandocExtensions = extensionsFromList
138+ [ Ext_footnotes
139+ , Ext_inline_notes
140+ , Ext_pandoc_title_block
141+ , Ext_yaml_metadata_block
142+ , Ext_table_captions
143+ , Ext_implicit_figures
144+ , Ext_simple_tables
145+ , Ext_multiline_tables
146+ , Ext_grid_tables
147+ , Ext_pipe_tables
148+ , Ext_citations
149+ , Ext_raw_tex
150+ , Ext_raw_html
151+ , Ext_tex_math_dollars
152+ , Ext_latex_macros
153+ , Ext_fenced_code_blocks
154+ , Ext_fenced_code_attributes
155+ , Ext_backtick_code_blocks
156+ , Ext_inline_code_attributes
157+ , Ext_markdown_in_html_blocks
158+ , Ext_native_divs
159+ , Ext_native_spans
160+ , Ext_bracketed_spans
161+ , Ext_escaped_line_breaks
162+ , Ext_fancy_lists
163+ , Ext_startnum
164+ , Ext_definition_lists
165+ , Ext_example_lists
166+ , Ext_all_symbols_escapable
167+ , Ext_intraword_underscores
168+ , Ext_blank_before_blockquote
169+ , Ext_blank_before_header
170+ , Ext_strikeout
171+ , Ext_superscript
172+ , Ext_subscript
173+ , Ext_auto_identifiers
174+ , Ext_header_attributes
175+ , Ext_link_attributes
176+ , Ext_implicit_header_references
177+ , Ext_line_blocks
178+ , Ext_shortcut_reference_links
179+ , Ext_smart
180+ ]
181+
182+plainExtensions :: Extensions
183+plainExtensions = extensionsFromList
184+ [ Ext_table_captions
185+ , Ext_implicit_figures
186+ , Ext_simple_tables
187+ , Ext_multiline_tables
188+ , Ext_grid_tables
189+ , Ext_latex_macros
190+ , Ext_fancy_lists
191+ , Ext_startnum
192+ , Ext_definition_lists
193+ , Ext_example_lists
194+ , Ext_intraword_underscores
195+ , Ext_blank_before_blockquote
196+ , Ext_blank_before_header
197+ , Ext_strikeout
198+ ]
199+
200+phpMarkdownExtraExtensions :: Extensions
201+phpMarkdownExtraExtensions = extensionsFromList
202+ [ Ext_footnotes
203+ , Ext_pipe_tables
204+ , Ext_raw_html
205+ , Ext_markdown_attribute
206+ , Ext_fenced_code_blocks
207+ , Ext_definition_lists
208+ , Ext_intraword_underscores
209+ , Ext_header_attributes
210+ , Ext_link_attributes
211+ , Ext_abbreviations
212+ , Ext_shortcut_reference_links
213+ ]
214+
215+githubMarkdownExtensions :: Extensions
216+githubMarkdownExtensions = extensionsFromList
217+ [ Ext_angle_brackets_escapable
218+ , Ext_pipe_tables
219+ , Ext_raw_html
220+ , Ext_fenced_code_blocks
221+ , Ext_auto_identifiers
222+ , Ext_ascii_identifiers
223+ , Ext_backtick_code_blocks
224+ , Ext_autolink_bare_uris
225+ , Ext_intraword_underscores
226+ , Ext_strikeout
227+ , Ext_hard_line_breaks
228+ , Ext_emoji
229+ , Ext_lists_without_preceding_blankline
230+ , Ext_shortcut_reference_links
231+ ]
232+
233+multimarkdownExtensions :: Extensions
234+multimarkdownExtensions = extensionsFromList
235+ [ Ext_pipe_tables
236+ , Ext_raw_html
237+ , Ext_markdown_attribute
238+ , Ext_mmd_link_attributes
239+ -- , Ext_raw_tex
240+ -- Note: MMD's raw TeX syntax requires raw TeX to be
241+ -- enclosed in HTML comment
242+ , Ext_tex_math_double_backslash
243+ , Ext_intraword_underscores
244+ , Ext_mmd_title_block
245+ , Ext_footnotes
246+ , Ext_definition_lists
247+ , Ext_all_symbols_escapable
248+ , Ext_implicit_header_references
249+ , Ext_auto_identifiers
250+ , Ext_mmd_header_identifiers
251+ , Ext_implicit_figures
252+ -- Note: MMD's syntax for superscripts and subscripts
253+ -- is a bit more permissive than pandoc's, allowing
254+ -- e^2 and a~1 instead of e^2^ and a~1~, so even with
255+ -- these options we don't have full support for MMD
256+ -- superscripts and subscripts, but there's no reason
257+ -- not to include these:
258+ , Ext_superscript
259+ , Ext_subscript
260+ ]
261+
262+strictExtensions :: Extensions
263+strictExtensions = extensionsFromList
264+ [ Ext_raw_html
265+ , Ext_shortcut_reference_links
266+ ]
267+
src/Text/Pandoc/ImageSize.hs+3−3
@@ -53,7 +53,7 @@ import Control.Monad
5353import Data.Bits
5454import Data.Binary
5555import Data.Binary.Get
56−import Text.Pandoc.Shared (safeRead, hush)
56+import Text.Pandoc.Shared (safeRead)
5757import Data.Default (Default)
5858import Numeric (showFFloat)
5959import Text.Pandoc.Definition
@@ -240,7 +240,7 @@ pngSize img = do
240240 ([w1,w2,w3,w4,h1,h2,h3,h4] :: [Integer]) -> return
241241 ((shift w1 24) + (shift w2 16) + (shift w3 8) + w4,
242242 (shift h1 24) + (shift h2 16) + (shift h3 8) + h4)
243− _ -> (hush . Left) "PNG parse error"
243+ _ -> Nothing -- "PNG parse error"
244244 let (dpix, dpiy) = findpHYs rest''
245245 return $ ImageSize { pxX = x, pxY = y, dpiX = dpix, dpiY = dpiy }
246246
@@ -269,7 +269,7 @@ gifSize img = do
269269 dpiX = 72,
270270 dpiY = 72
271271 }
272− _ -> (hush . Left) "GIF parse error"
272+ _ -> Nothing -- "GIF parse error"
273273
274274jpegSize :: ByteString -> Either String ImageSize
275275jpegSize img =
src/Text/Pandoc/MediaBag.hs+7−3
@@ -42,6 +42,7 @@ import System.Directory (createDirectoryIfMissing)
4242import qualified Data.Map as M
4343import qualified Data.ByteString.Lazy as BL
4444import Control.Monad (when)
45+import Control.Monad.Trans (MonadIO(..))
4546import Text.Pandoc.MIME (MimeType, getMimeTypeDef)
4647import qualified Text.Pandoc.UTF8 as UTF8
4748import Data.Maybe (fromMaybe)
@@ -88,11 +89,14 @@ mediaDirectory (MediaBag mediamap) =
8889
8990-- | Extract contents of MediaBag to a given directory. Print informational
9091-- messages if 'verbose' is true.
91−extractMediaBag :: Bool
92+-- TODO: eventually we may want to put this into PandocMonad
93+-- In PandocPure, it could write to the fake file system...
94+extractMediaBag :: MonadIO m
95+ => Bool
9296 -> FilePath
9397 -> MediaBag
94− -> IO ()
95−extractMediaBag verbose dir (MediaBag mediamap) = do
98+ -> m ()
99+extractMediaBag verbose dir (MediaBag mediamap) = liftIO $ do
96100 sequence_ $ M.foldWithKey
97101 (\fp (_ ,contents) ->
98102 ((writeMedia verbose dir (Posix.joinPath fp, contents)):)) [] mediamap
src/Text/Pandoc/Options.hs+17−248
@@ -29,13 +29,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2929Data structures and functions for representing parser and writer
3030options.
3131-}
32−module Text.Pandoc.Options ( Extension(..)
33− , pandocExtensions
34− , plainExtensions
35− , strictExtensions
36− , phpMarkdownExtraExtensions
37− , githubMarkdownExtensions
38− , multimarkdownExtensions
32+module Text.Pandoc.Options ( module Text.Pandoc.Extensions
3933 , ReaderOptions(..)
4034 , HTMLMathMethod (..)
4135 , CiteMethod (..)
@@ -43,6 +37,7 @@ module Text.Pandoc.Options ( Extension(..)
4337 , HTMLSlideVariant (..)
4438 , EPUBVersion (..)
4539 , WrapOption (..)
40+ , Verbosity (..)
4641 , TopLevelDivision (..)
4742 , WriterOptions (..)
4843 , TrackChanges (..)
@@ -50,246 +45,37 @@ module Text.Pandoc.Options ( Extension(..)
5045 , def
5146 , isEnabled
5247 ) where
53−import Data.Set (Set)
54−import qualified Data.Set as Set
48+import Text.Pandoc.Extensions
5549import Data.Default
5650import Text.Pandoc.Highlighting (Style, pygments)
57−import Text.Pandoc.MediaBag (MediaBag)
5851import Data.Data (Data)
5952import Data.Typeable (Typeable)
6053import GHC.Generics (Generic)
6154
62−-- | Individually selectable syntax extensions.
63−data Extension =
64− Ext_footnotes -- ^ Pandoc/PHP/MMD style footnotes
65− | Ext_inline_notes -- ^ Pandoc-style inline notes
66− | Ext_pandoc_title_block -- ^ Pandoc title block
67− | Ext_yaml_metadata_block -- ^ YAML metadata block
68− | Ext_mmd_title_block -- ^ Multimarkdown metadata block
69− | Ext_table_captions -- ^ Pandoc-style table captions
70− | Ext_implicit_figures -- ^ A paragraph with just an image is a figure
71− | Ext_simple_tables -- ^ Pandoc-style simple tables
72− | Ext_multiline_tables -- ^ Pandoc-style multiline tables
73− | Ext_grid_tables -- ^ Grid tables (pandoc, reST)
74− | Ext_pipe_tables -- ^ Pipe tables (as in PHP markdown extra)
75− | Ext_citations -- ^ Pandoc/citeproc citations
76− | Ext_raw_tex -- ^ Allow raw TeX (other than math)
77− | Ext_raw_html -- ^ Allow raw HTML
78− | Ext_tex_math_dollars -- ^ TeX math between $..$ or $$..$$
79− | Ext_tex_math_single_backslash -- ^ TeX math btw \(..\) \[..\]
80− | Ext_tex_math_double_backslash -- ^ TeX math btw \\(..\\) \\[..\\]
81− | Ext_latex_macros -- ^ Parse LaTeX macro definitions (for math only)
82− | Ext_fenced_code_blocks -- ^ Parse fenced code blocks
83− | Ext_fenced_code_attributes -- ^ Allow attributes on fenced code blocks
84− | Ext_backtick_code_blocks -- ^ GitHub style ``` code blocks
85− | Ext_inline_code_attributes -- ^ Allow attributes on inline code
86− | Ext_markdown_in_html_blocks -- ^ Interpret as markdown inside HTML blocks
87− | Ext_native_divs -- ^ Use Div blocks for contents of <div> tags
88− | Ext_native_spans -- ^ Use Span inlines for contents of <span>
89− | Ext_bracketed_spans -- ^ Bracketed spans with attributes
90− | Ext_markdown_attribute -- ^ Interpret text inside HTML as markdown
91− -- iff container has attribute 'markdown'
92− | Ext_escaped_line_breaks -- ^ Treat a backslash at EOL as linebreak
93− | Ext_link_attributes -- ^ link and image attributes
94− | Ext_mmd_link_attributes -- ^ MMD style reference link attributes
95− | Ext_autolink_bare_uris -- ^ Make all absolute URIs into links
96− | Ext_fancy_lists -- ^ Enable fancy list numbers and delimiters
97− | Ext_lists_without_preceding_blankline -- ^ Allow lists without preceding blank
98− | Ext_startnum -- ^ Make start number of ordered list significant
99− | Ext_definition_lists -- ^ Definition lists as in pandoc, mmd, php
100− | Ext_compact_definition_lists -- ^ Definition lists without
101− -- space between items, and disallow laziness
102− | Ext_example_lists -- ^ Markdown-style numbered examples
103− | Ext_all_symbols_escapable -- ^ Make all non-alphanumerics escapable
104− | Ext_angle_brackets_escapable -- ^ Make < and > escapable
105− | Ext_intraword_underscores -- ^ Treat underscore inside word as literal
106− | Ext_blank_before_blockquote -- ^ Require blank line before a blockquote
107− | Ext_blank_before_header -- ^ Require blank line before a header
108− | Ext_strikeout -- ^ Strikeout using ~~this~~ syntax
109− | Ext_superscript -- ^ Superscript using ^this^ syntax
110− | Ext_subscript -- ^ Subscript using ~this~ syntax
111− | Ext_hard_line_breaks -- ^ All newlines become hard line breaks
112− | Ext_ignore_line_breaks -- ^ Newlines in paragraphs are ignored
113− | Ext_east_asian_line_breaks -- ^ Newlines in paragraphs are ignored between
114− -- East Asian wide characters
115− | Ext_literate_haskell -- ^ Enable literate Haskell conventions
116− | Ext_abbreviations -- ^ PHP markdown extra abbreviation definitions
117− | Ext_emoji -- ^ Support emoji like :smile:
118− | Ext_auto_identifiers -- ^ Automatic identifiers for headers
119− | Ext_ascii_identifiers -- ^ ascii-only identifiers for headers
120− | Ext_header_attributes -- ^ Explicit header attributes {#id .class k=v}
121− | Ext_mmd_header_identifiers -- ^ Multimarkdown style header identifiers [myid]
122− | Ext_implicit_header_references -- ^ Implicit reference links for headers
123− | Ext_line_blocks -- ^ RST style line blocks
124− | Ext_epub_html_exts -- ^ Recognise the EPUB extended version of HTML
125− | Ext_shortcut_reference_links -- ^ Shortcut reference links
126− deriving (Show, Read, Enum, Eq, Ord, Bounded, Data, Typeable, Generic)
127−
128−pandocExtensions :: Set Extension
129−pandocExtensions = Set.fromList
130− [ Ext_footnotes
131− , Ext_inline_notes
132− , Ext_pandoc_title_block
133− , Ext_yaml_metadata_block
134− , Ext_table_captions
135− , Ext_implicit_figures
136− , Ext_simple_tables
137− , Ext_multiline_tables
138− , Ext_grid_tables
139− , Ext_pipe_tables
140− , Ext_citations
141− , Ext_raw_tex
142− , Ext_raw_html
143− , Ext_tex_math_dollars
144− , Ext_latex_macros
145− , Ext_fenced_code_blocks
146− , Ext_fenced_code_attributes
147− , Ext_backtick_code_blocks
148− , Ext_inline_code_attributes
149− , Ext_markdown_in_html_blocks
150− , Ext_native_divs
151− , Ext_native_spans
152− , Ext_bracketed_spans
153− , Ext_escaped_line_breaks
154− , Ext_fancy_lists
155− , Ext_startnum
156− , Ext_definition_lists
157− , Ext_example_lists
158− , Ext_all_symbols_escapable
159− , Ext_intraword_underscores
160− , Ext_blank_before_blockquote
161− , Ext_blank_before_header
162− , Ext_strikeout
163− , Ext_superscript
164− , Ext_subscript
165− , Ext_auto_identifiers
166− , Ext_header_attributes
167− , Ext_link_attributes
168− , Ext_implicit_header_references
169− , Ext_line_blocks
170− , Ext_shortcut_reference_links
171− ]
172−
173−plainExtensions :: Set Extension
174−plainExtensions = Set.fromList
175− [ Ext_table_captions
176− , Ext_implicit_figures
177− , Ext_simple_tables
178− , Ext_multiline_tables
179− , Ext_grid_tables
180− , Ext_latex_macros
181− , Ext_fancy_lists
182− , Ext_startnum
183− , Ext_definition_lists
184− , Ext_example_lists
185− , Ext_intraword_underscores
186− , Ext_blank_before_blockquote
187− , Ext_blank_before_header
188− , Ext_strikeout
189− ]
190−
191−phpMarkdownExtraExtensions :: Set Extension
192−phpMarkdownExtraExtensions = Set.fromList
193− [ Ext_footnotes
194− , Ext_pipe_tables
195− , Ext_raw_html
196− , Ext_markdown_attribute
197− , Ext_fenced_code_blocks
198− , Ext_definition_lists
199− , Ext_intraword_underscores
200− , Ext_header_attributes
201− , Ext_link_attributes
202− , Ext_abbreviations
203− , Ext_shortcut_reference_links
204− ]
205−
206−githubMarkdownExtensions :: Set Extension
207−githubMarkdownExtensions = Set.fromList
208− [ Ext_angle_brackets_escapable
209− , Ext_pipe_tables
210− , Ext_raw_html
211− , Ext_fenced_code_blocks
212− , Ext_auto_identifiers
213− , Ext_ascii_identifiers
214− , Ext_backtick_code_blocks
215− , Ext_autolink_bare_uris
216− , Ext_intraword_underscores
217− , Ext_strikeout
218− , Ext_hard_line_breaks
219− , Ext_emoji
220− , Ext_lists_without_preceding_blankline
221− , Ext_shortcut_reference_links
222− ]
223−
224−multimarkdownExtensions :: Set Extension
225−multimarkdownExtensions = Set.fromList
226− [ Ext_pipe_tables
227− , Ext_raw_html
228− , Ext_markdown_attribute
229− , Ext_mmd_link_attributes
230− -- , Ext_raw_tex
231− -- Note: MMD's raw TeX syntax requires raw TeX to be
232− -- enclosed in HTML comment
233− , Ext_tex_math_double_backslash
234− , Ext_intraword_underscores
235− , Ext_mmd_title_block
236− , Ext_footnotes
237− , Ext_definition_lists
238− , Ext_all_symbols_escapable
239− , Ext_implicit_header_references
240− , Ext_auto_identifiers
241− , Ext_mmd_header_identifiers
242− , Ext_implicit_figures
243− -- Note: MMD's syntax for superscripts and subscripts
244− -- is a bit more permissive than pandoc's, allowing
245− -- e^2 and a~1 instead of e^2^ and a~1~, so even with
246− -- these options we don't have full support for MMD
247− -- superscripts and subscripts, but there's no reason
248− -- not to include these:
249− , Ext_superscript
250− , Ext_subscript
251− ]
252−
253−strictExtensions :: Set Extension
254−strictExtensions = Set.fromList
255− [ Ext_raw_html
256− , Ext_shortcut_reference_links
257− ]
258−
25955data ReaderOptions = ReaderOptions{
260− readerExtensions :: Set Extension -- ^ Syntax extensions
261− , readerSmart :: Bool -- ^ Smart punctuation
56+ readerExtensions :: Extensions -- ^ Syntax extensions
26257 , readerStandalone :: Bool -- ^ Standalone document with header
26358 , readerParseRaw :: Bool -- ^ Parse raw HTML, LaTeX
26459 , readerColumns :: Int -- ^ Number of columns in terminal
26560 , readerTabStop :: Int -- ^ Tab stop
266− , readerOldDashes :: Bool -- ^ Use pandoc <= 1.8.2.1 behavior
267− -- in parsing dashes; -- is em-dash;
268− -- - before numerial is en-dash
26961 , readerApplyMacros :: Bool -- ^ Apply macros to TeX math
27062 , readerIndentedCodeClasses :: [String] -- ^ Default classes for
27163 -- indented code blocks
27264 , readerDefaultImageExtension :: String -- ^ Default extension for images
273− , readerTrace :: Bool -- ^ Print debugging info
27465 , readerTrackChanges :: TrackChanges
275− , readerFileScope :: Bool -- ^ Parse before combining
27666} deriving (Show, Read, Data, Typeable, Generic)
27767
27868instance Default ReaderOptions
27969 where def = ReaderOptions{
280− readerExtensions = pandocExtensions
281− , readerSmart = False
70+ readerExtensions = emptyExtensions
28271 , readerStandalone = False
28372 , readerParseRaw = False
28473 , readerColumns = 80
28574 , readerTabStop = 4
286− , readerOldDashes = False
28775 , readerApplyMacros = True
28876 , readerIndentedCodeClasses = []
28977 , readerDefaultImageExtension = ""
290− , readerTrace = False
29178 , readerTrackChanges = AcceptChanges
292− , readerFileScope = False
29379 }
29480
29581--
@@ -354,20 +140,22 @@ data ReferenceLocation = EndOfBlock -- ^ End of block
354140 | EndOfDocument -- ^ at end of document
355141 deriving (Show, Read, Eq, Data, Typeable, Generic)
356142
143+-- | Verbosity level.
144+data Verbosity = ERROR | WARNING | INFO | DEBUG
145+ deriving (Show, Read, Eq, Data, Enum, Ord, Bounded, Typeable, Generic)
146+
357147-- | Options for writers
358148data WriterOptions = WriterOptions
359149 { writerTemplate :: Maybe String -- ^ Template to use
360150 , writerVariables :: [(String, String)] -- ^ Variables to set in template
361151 , writerTabStop :: Int -- ^ Tabstop for conversion btw spaces and tabs
362152 , writerTableOfContents :: Bool -- ^ Include table of contents
363− , writerSlideVariant :: HTMLSlideVariant -- ^ Are we writing S5, Slidy or Slideous?
364153 , writerIncremental :: Bool -- ^ True if lists should be incremental
365154 , writerHTMLMathMethod :: HTMLMathMethod -- ^ How to print math in HTML
366− , writerIgnoreNotes :: Bool -- ^ Ignore footnotes (used in making toc)
367155 , writerNumberSections :: Bool -- ^ Number sections in LaTeX
368156 , writerNumberOffset :: [Int] -- ^ Starting number for section, subsection, ...
369157 , writerSectionDivs :: Bool -- ^ Put sections in div tags in HTML
370− , writerExtensions :: Set Extension -- ^ Markdown extensions that can be used
158+ , writerExtensions :: Extensions -- ^ Markdown extensions that can be used
371159 , writerReferenceLinks :: Bool -- ^ Use reference links in writing markdown, rst
372160 , writerDpi :: Int -- ^ Dpi for pixel to/from inch/cm conversions
373161 , writerWrapText :: WrapOption -- ^ Option for wrapping text
@@ -378,27 +166,19 @@ data WriterOptions = WriterOptions
378166 , writerSourceURL :: Maybe String -- ^ Absolute URL + directory of 1st source file
379167 , writerUserDataDir :: Maybe FilePath -- ^ Path of user data directory
380168 , writerCiteMethod :: CiteMethod -- ^ How to print cites
381− , writerDocbook5 :: Bool -- ^ Produce DocBook5
382− , writerHtml5 :: Bool -- ^ Produce HTML5
383169 , writerHtmlQTags :: Bool -- ^ Use @<q>@ tags for quotes in HTML
384− , writerBeamer :: Bool -- ^ Produce beamer LaTeX slide show
385170 , writerSlideLevel :: Maybe Int -- ^ Force header level of slides
386171 , writerTopLevelDivision :: TopLevelDivision -- ^ Type of top-level divisions
387172 , writerListings :: Bool -- ^ Use listings package for code
388− , writerHighlight :: Bool -- ^ Highlight source code
389− , writerHighlightStyle :: Style -- ^ Style to use for highlighting
173+ , writerHighlightStyle :: Maybe Style -- ^ Style to use for highlighting
174+ -- (Nothing = no highlighting)
390175 , writerSetextHeaders :: Bool -- ^ Use setext headers for levels 1-2 in markdown
391− , writerTeXLigatures :: Bool -- ^ Use tex ligatures quotes, dashes in latex
392− , writerEpubVersion :: Maybe EPUBVersion -- ^ Nothing or EPUB version
393176 , writerEpubMetadata :: String -- ^ Metadata to include in EPUB
394177 , writerEpubStylesheet :: Maybe String -- ^ EPUB stylesheet specified at command line
395178 , writerEpubFonts :: [FilePath] -- ^ Paths to fonts to embed
396179 , writerEpubChapterLevel :: Int -- ^ Header level for chapters (separate files)
397180 , writerTOCDepth :: Int -- ^ Number of levels to include in TOC
398− , writerReferenceODT :: Maybe FilePath -- ^ Path to reference ODT if specified
399− , writerReferenceDocx :: Maybe FilePath -- ^ Path to reference DOCX if specified
400− , writerMediaBag :: MediaBag -- ^ Media collected by docx or epub reader
401− , writerVerbose :: Bool -- ^ Verbose debugging output
181+ , writerReferenceDoc :: Maybe FilePath -- ^ Path to reference document if specified
402182 , writerLaTeXArgs :: [String] -- ^ Flags to pass to latex-engine
403183 , writerReferenceLocation :: ReferenceLocation -- ^ Location of footnotes and references for writing markdown
404184 } deriving (Show, Data, Typeable, Generic)
@@ -408,14 +188,12 @@ instance Default WriterOptions where
408188 , writerVariables = []
409189 , writerTabStop = 4
410190 , writerTableOfContents = False
411− , writerSlideVariant = NoSlides
412191 , writerIncremental = False
413192 , writerHTMLMathMethod = PlainMath
414− , writerIgnoreNotes = False
415193 , writerNumberSections = False
416194 , writerNumberOffset = [0,0,0,0,0,0]
417195 , writerSectionDivs = False
418− , writerExtensions = pandocExtensions
196+ , writerExtensions = emptyExtensions
419197 , writerReferenceLinks = False
420198 , writerDpi = 96
421199 , writerWrapText = WrapAuto
@@ -425,31 +203,22 @@ instance Default WriterOptions where
425203 , writerSourceURL = Nothing
426204 , writerUserDataDir = Nothing
427205 , writerCiteMethod = Citeproc
428− , writerDocbook5 = False
429− , writerHtml5 = False
430206 , writerHtmlQTags = False
431− , writerBeamer = False
432207 , writerSlideLevel = Nothing
433208 , writerTopLevelDivision = TopLevelDefault
434209 , writerListings = False
435− , writerHighlight = False
436− , writerHighlightStyle = pygments
210+ , writerHighlightStyle = Just pygments
437211 , writerSetextHeaders = True
438− , writerTeXLigatures = True
439− , writerEpubVersion = Nothing
440212 , writerEpubMetadata = ""
441213 , writerEpubStylesheet = Nothing
442214 , writerEpubFonts = []
443215 , writerEpubChapterLevel = 1
444216 , writerTOCDepth = 3
445− , writerReferenceODT = Nothing
446− , writerReferenceDocx = Nothing
447− , writerMediaBag = mempty
448− , writerVerbose = False
217+ , writerReferenceDoc = Nothing
449218 , writerLaTeXArgs = []
450219 , writerReferenceLocation = EndOfDocument
451220 }
452221
453222-- | Returns True if the given extension is enabled.
454223isEnabled :: Extension -> WriterOptions -> Bool
455−isEnabled ext opts = ext `Set.member` (writerExtensions opts)
224+isEnabled ext opts = ext `extensionEnabled` (writerExtensions opts)
src/Text/Pandoc/PDF.hs+58−52
@@ -37,7 +37,7 @@ import qualified Data.ByteString as BS
3737import Data.Monoid ((<>))
3838import System.Exit (ExitCode (..))
3939import System.FilePath
40−import System.IO (stderr, stdout)
40+import System.IO (stdout)
4141import System.IO.Temp (withTempFile)
4242import System.Directory
4343import Data.Digest.Pure.SHA (showDigest, sha1)
@@ -48,31 +48,37 @@ import Data.List (isInfixOf)
4848import Data.Maybe (fromMaybe)
4949import qualified Text.Pandoc.UTF8 as UTF8
5050import Text.Pandoc.Definition
51+import Text.Pandoc.MediaBag
5152import Text.Pandoc.Walk (walkM)
52−import Text.Pandoc.Shared (fetchItem', warn, withTempDir, inDirectory,
53− stringify)
53+import Text.Pandoc.Shared (warn, withTempDir, inDirectory, stringify)
5454import Text.Pandoc.Writers.Shared (getField, metaToJSON)
55−import Text.Pandoc.Options (WriterOptions(..), HTMLMathMethod(..))
55+import Text.Pandoc.Options (WriterOptions(..), HTMLMathMethod(..),
56+ Verbosity(..))
5657import Text.Pandoc.MIME (extensionFromMimeType, getMimeType)
5758import Text.Pandoc.Process (pipeProcess)
59+import Control.Monad.Trans (MonadIO(..))
5860import qualified Data.ByteString.Lazy as BL
5961import qualified Codec.Picture as JP
6062#ifdef _WINDOWS
6163import Data.List (intercalate)
6264#endif
65+import Text.Pandoc.Class (PandocIO, runIOorExplode, fetchItem, setMediaBag, runIO)
6366
6467#ifdef _WINDOWS
6568changePathSeparators :: FilePath -> FilePath
6669changePathSeparators = intercalate "/" . splitDirectories
6770#endif
6871
69−makePDF :: String -- ^ pdf creator (pdflatex, lualatex,
72+makePDF :: MonadIO m
73+ => String -- ^ pdf creator (pdflatex, lualatex,
7074 -- xelatex, context, wkhtmltopdf)
71− -> (WriterOptions -> Pandoc -> String) -- ^ writer
75+ -> (WriterOptions -> Pandoc -> PandocIO String) -- ^ writer
7276 -> WriterOptions -- ^ options
77+ -> Verbosity -- ^ verbosity level
78+ -> MediaBag -- ^ media
7379 -> Pandoc -- ^ document
74− -> IO (Either ByteString ByteString)
75−makePDF "wkhtmltopdf" writer opts doc@(Pandoc meta _) = do
80+ -> m (Either ByteString ByteString)
81+makePDF "wkhtmltopdf" writer opts verbosity _ doc@(Pandoc meta _) = liftIO $ do
7682 let mathArgs = case writerHTMLMathMethod opts of
7783 -- with MathJax, wait til all math is rendered:
7884 MathJax _ -> ["--run-script", "MathJax.Hub.Register.StartupHook('End Typeset', function() { window.status = 'mathjax_loaded' });",
@@ -93,34 +99,40 @@ makePDF "wkhtmltopdf" writer opts doc@(Pandoc meta _) = do
9399 ,("margin-left", fromMaybe (Just "1.25in")
94100 (getField "margin-left" meta'))
95101 ]
96− let source = writer opts doc
97− html2pdf (writerVerbose opts) args source
98−makePDF program writer opts doc = withTempDir "tex2pdf." $ \tmpdir -> do
99− doc' <- handleImages opts tmpdir doc
100− let source = writer opts doc'
101− args = writerLaTeXArgs opts
102− case takeBaseName program of
103− "context" -> context2pdf (writerVerbose opts) tmpdir source
104− prog | prog `elem` ["pdflatex", "lualatex", "xelatex"]
105− -> tex2pdf' (writerVerbose opts) args tmpdir program source
106− _ -> return $ Left $ UTF8.fromStringLazy $ "Unknown program " ++ program
102+ source <- runIOorExplode $ writer opts doc
103+ html2pdf verbosity args source
104+makePDF program writer opts verbosity mediabag doc =
105+ liftIO $ withTempDir "tex2pdf." $ \tmpdir -> do
106+ doc' <- handleImages opts mediabag tmpdir doc
107+ source <- runIOorExplode $ writer opts doc'
108+ let args = writerLaTeXArgs opts
109+ case takeBaseName program of
110+ "context" -> context2pdf verbosity tmpdir source
111+ prog | prog `elem` ["pdflatex", "lualatex", "xelatex"]
112+ -> tex2pdf' verbosity args tmpdir program source
113+ _ -> return $ Left $ UTF8.fromStringLazy $ "Unknown program " ++ program
107114
108115handleImages :: WriterOptions
116+ -> MediaBag
109117 -> FilePath -- ^ temp dir to store images
110118 -> Pandoc -- ^ document
111119 -> IO Pandoc
112−handleImages opts tmpdir = walkM (convertImages tmpdir) <=< walkM (handleImage' opts tmpdir)
120+handleImages opts mediabag tmpdir =
121+ walkM (convertImages tmpdir) <=< walkM (handleImage' opts mediabag tmpdir)
113122
114123handleImage' :: WriterOptions
124+ -> MediaBag
115125 -> FilePath
116126 -> Inline
117127 -> IO Inline
118−handleImage' opts tmpdir (Image attr ils (src,tit)) = do
128+handleImage' opts mediabag tmpdir (Image attr ils (src,tit)) = do
119129 exists <- doesFileExist src
120130 if exists
121131 then return $ Image attr ils (src,tit)
122132 else do
123− res <- fetchItem' (writerMediaBag opts) (writerSourceURL opts) src
133+ res <- runIO $ do
134+ setMediaBag mediabag
135+ fetchItem (writerSourceURL opts) src
124136 case res of
125137 Right (contents, Just mime) -> do
126138 let ext = fromMaybe (takeExtension src) $
@@ -133,7 +145,7 @@ handleImage' opts tmpdir (Image attr ils (src,tit)) = do
133145 warn $ "Could not find image `" ++ src ++ "', skipping..."
134146 -- return alt text
135147 return $ Emph ils
136−handleImage' _ _ x = return x
148+handleImage' _ _ _ x = return x
137149
138150convertImages :: FilePath -> Inline -> IO Inline
139151convertImages tmpdir (Image attr ils (src, tit)) = do
@@ -164,17 +176,17 @@ convertImage tmpdir fname =
164176 mime = getMimeType fname
165177 doNothing = return (Right fname)
166178
167−tex2pdf' :: Bool -- ^ Verbose output
179+tex2pdf' :: Verbosity -- ^ Verbosity level
168180 -> [String] -- ^ Arguments to the latex-engine
169181 -> FilePath -- ^ temp directory for output
170182 -> String -- ^ tex program
171183 -> String -- ^ tex source
172184 -> IO (Either ByteString ByteString)
173−tex2pdf' verbose args tmpDir program source = do
185+tex2pdf' verbosity args tmpDir program source = do
174186 let numruns = if "\\tableofcontents" `isInfixOf` source
175187 then 3 -- to get page numbers
176188 else 2 -- 1 run won't give you PDF bookmarks
177− (exit, log', mbPdf) <- runTeXProgram verbose program args 1 numruns tmpDir source
189+ (exit, log', mbPdf) <- runTeXProgram verbosity program args 1 numruns tmpDir source
178190 case (exit, mbPdf) of
179191 (ExitFailure _, _) -> do
180192 let logmsg = extractMsg log'
@@ -212,9 +224,9 @@ extractConTeXtMsg log' = do
212224-- Run a TeX program on an input bytestring and return (exit code,
213225-- contents of stdout, contents of produced PDF if any). Rerun
214226-- a fixed number of times to resolve references.
215−runTeXProgram :: Bool -> String -> [String] -> Int -> Int -> FilePath -> String
216− -> IO (ExitCode, ByteString, Maybe ByteString)
217−runTeXProgram verbose program args runNumber numRuns tmpDir source = do
227+runTeXProgram :: Verbosity -> String -> [String] -> Int -> Int -> FilePath
228+ -> String -> IO (ExitCode, ByteString, Maybe ByteString)
229+runTeXProgram verbosity program args runNumber numRuns tmpDir source = do
218230 let file = tmpDir </> "input.tex"
219231 exists <- doesFileExist file
220232 unless exists $ UTF8.writeFile file source
@@ -234,7 +246,7 @@ runTeXProgram verbose program args runNumber numRuns tmpDir source = do
234246 $ lookup "TEXINPUTS" env'
235247 let env'' = ("TEXINPUTS", texinputs) :
236248 [(k,v) | (k,v) <- env', k /= "TEXINPUTS"]
237− when (verbose && runNumber == 1) $ do
249+ when (verbosity >= INFO && runNumber == 1) $ do
238250 putStrLn "[makePDF] temp dir:"
239251 putStrLn tmpDir'
240252 putStrLn "[makePDF] Command line:"
@@ -246,14 +258,13 @@ runTeXProgram verbose program args runNumber numRuns tmpDir source = do
246258 putStrLn $ "[makePDF] Contents of " ++ file' ++ ":"
247259 B.readFile file' >>= B.putStr
248260 putStr "\n"
249− (exit, out, err) <- pipeProcess (Just env'') program programArgs BL.empty
250− when verbose $ do
261+ (exit, out) <- pipeProcess (Just env'') program programArgs BL.empty
262+ when (verbosity >= INFO) $ do
251263 putStrLn $ "[makePDF] Run #" ++ show runNumber
252264 B.hPutStr stdout out
253− B.hPutStr stderr err
254265 putStr "\n"
255266 if runNumber <= numRuns
256− then runTeXProgram verbose program args (runNumber + 1) numRuns tmpDir source
267+ then runTeXProgram verbosity program args (runNumber + 1) numRuns tmpDir source
257268 else do
258269 let pdfFile = replaceDirectory (replaceExtension file ".pdf") tmpDir
259270 pdfExists <- doesFileExist pdfFile
@@ -263,19 +274,19 @@ runTeXProgram verbose program args runNumber numRuns tmpDir source = do
263274 -- See https://github.com/jgm/pandoc/issues/1192.
264275 then (Just . B.fromChunks . (:[])) `fmap` BS.readFile pdfFile
265276 else return Nothing
266− return (exit, out <> err, pdf)
277+ return (exit, out, pdf)
267278
268−html2pdf :: Bool -- ^ Verbose output
279+html2pdf :: Verbosity -- ^ Verbosity level
269280 -> [String] -- ^ Args to wkhtmltopdf
270281 -> String -- ^ HTML5 source
271282 -> IO (Either ByteString ByteString)
272−html2pdf verbose args source = do
283+html2pdf verbosity args source = do
273284 file <- withTempFile "." "html2pdf.html" $ \fp _ -> return fp
274285 pdfFile <- withTempFile "." "html2pdf.pdf" $ \fp _ -> return fp
275286 UTF8.writeFile file source
276287 let programArgs = args ++ [file, pdfFile]
277288 env' <- getEnvironment
278− when verbose $ do
289+ when (verbosity >= INFO) $ do
279290 putStrLn "[makePDF] Command line:"
280291 putStrLn $ "wkhtmltopdf" ++ " " ++ unwords (map show programArgs)
281292 putStr "\n"
@@ -285,12 +296,10 @@ html2pdf verbose args source = do
285296 putStrLn $ "[makePDF] Contents of " ++ file ++ ":"
286297 B.readFile file >>= B.putStr
287298 putStr "\n"
288− (exit, out, err) <- pipeProcess (Just env') "wkhtmltopdf"
289− programArgs BL.empty
299+ (exit, out) <- pipeProcess (Just env') "wkhtmltopdf" programArgs BL.empty
290300 removeFile file
291− when verbose $ do
301+ when (verbosity >= INFO) $ do
292302 B.hPutStr stdout out
293− B.hPutStr stderr err
294303 putStr "\n"
295304 pdfExists <- doesFileExist pdfFile
296305 mbPdf <- if pdfExists
@@ -302,17 +311,16 @@ html2pdf verbose args source = do
302311 removeFile pdfFile
303312 return res
304313 else return Nothing
305− let log' = out <> err
306314 return $ case (exit, mbPdf) of
307− (ExitFailure _, _) -> Left log'
315+ (ExitFailure _, _) -> Left out
308316 (ExitSuccess, Nothing) -> Left ""
309317 (ExitSuccess, Just pdf) -> Right pdf
310318
311−context2pdf :: Bool -- ^ Verbose output
319+context2pdf :: Verbosity -- ^ Verbosity level
312320 -> FilePath -- ^ temp directory for output
313321 -> String -- ^ ConTeXt source
314322 -> IO (Either ByteString ByteString)
315−context2pdf verbose tmpDir source = inDirectory tmpDir $ do
323+context2pdf verbosity tmpDir source = inDirectory tmpDir $ do
316324 let file = "input.tex"
317325 UTF8.writeFile file source
318326#ifdef _WINDOWS
@@ -328,7 +336,7 @@ context2pdf verbose tmpDir source = inDirectory tmpDir $ do
328336 $ lookup "TEXINPUTS" env'
329337 let env'' = ("TEXINPUTS", texinputs) :
330338 [(k,v) | (k,v) <- env', k /= "TEXINPUTS"]
331− when verbose $ do
339+ when (verbosity >= INFO) $ do
332340 putStrLn "[makePDF] temp dir:"
333341 putStrLn tmpDir'
334342 putStrLn "[makePDF] Command line:"
@@ -340,10 +348,9 @@ context2pdf verbose tmpDir source = inDirectory tmpDir $ do
340348 putStrLn $ "[makePDF] Contents of " ++ file ++ ":"
341349 B.readFile file >>= B.putStr
342350 putStr "\n"
343− (exit, out, err) <- pipeProcess (Just env'') "context" programArgs BL.empty
344− when verbose $ do
351+ (exit, out) <- pipeProcess (Just env'') "context" programArgs BL.empty
352+ when (verbosity >= INFO) $ do
345353 B.hPutStr stdout out
346− B.hPutStr stderr err
347354 putStr "\n"
348355 let pdfFile = replaceExtension file ".pdf"
349356 pdfExists <- doesFileExist pdfFile
@@ -353,10 +360,9 @@ context2pdf verbose tmpDir source = inDirectory tmpDir $ do
353360 -- See https://github.com/jgm/pandoc/issues/1192.
354361 then (Just . B.fromChunks . (:[])) `fmap` BS.readFile pdfFile
355362 else return Nothing
356− let log' = out <> err
357363 case (exit, mbPdf) of
358364 (ExitFailure _, _) -> do
359− let logmsg = extractConTeXtMsg log'
365+ let logmsg = extractConTeXtMsg out
360366 return $ Left logmsg
361367 (ExitSuccess, Nothing) -> return $ Left ""
362368 (ExitSuccess, Just pdf) -> return $ Right pdf
src/Text/Pandoc/Parsing.hs+26−44
@@ -3,7 +3,9 @@
33, GeneralizedNewtypeDeriving
44, TypeSynonymInstances
55, MultiParamTypeClasses
6−, FlexibleInstances #-}
6+, FlexibleInstances
7+, IncoherentInstances #-}
8+
79{-
810Copyright (C) 2006-2016 John MacFarlane <jgm@berkeley.edu>
911
@@ -65,7 +67,6 @@ module Text.Pandoc.Parsing ( anyLine,
6567 widthsFromIndices,
6668 gridTableWith,
6769 readWith,
68− readWithWarnings,
6970 readWithM,
7071 testStringWith,
7172 guardEnabled,
@@ -163,7 +164,6 @@ module Text.Pandoc.Parsing ( anyLine,
163164 setSourceColumn,
164165 setSourceLine,
165166 newPos,
166− addWarning,
167167 (<+?>),
168168 extractIdClass
169169 )
@@ -740,11 +740,11 @@ lineBlockLines = try $ do
740740-- | Parse a table using 'headerParser', 'rowParser',
741741-- 'lineParser', and 'footerParser'.
742742tableWith :: Stream s m Char
743− => ParserT s ParserState m ([[Block]], [Alignment], [Int])
744− -> ([Int] -> ParserT s ParserState m [[Block]])
743+ => ParserT s ParserState m ([Blocks], [Alignment], [Int])
744+ -> ([Int] -> ParserT s ParserState m [Blocks])
745745 -> ParserT s ParserState m sep
746746 -> ParserT s ParserState m end
747− -> ParserT s ParserState m Block
747+ -> ParserT s ParserState m Blocks
748748tableWith headerParser rowParser lineParser footerParser = try $ do
749749 (heads, aligns, indices) <- headerParser
750750 lines' <- rowParser indices `sepEndBy1` lineParser
@@ -753,7 +753,7 @@ tableWith headerParser rowParser lineParser footerParser = try $ do
753753 let widths = if (indices == [])
754754 then replicate (length aligns) 0.0
755755 else widthsFromIndices numColumns indices
756− return $ Table [] aligns widths heads lines'
756+ return $ B.table mempty (zip aligns widths) heads lines'
757757
758758-- Calculate relative widths of table columns, based on indices
759759widthsFromIndices :: Int -- Number of columns on terminal
@@ -787,9 +787,9 @@ widthsFromIndices numColumns' indices =
787787-- which may be grid, separated by blank lines, and
788788-- ending with a footer (dashed line followed by blank line).
789789gridTableWith :: Stream [Char] m Char
790− => ParserT [Char] ParserState m [Block] -- ^ Block list parser
790+ => ParserT [Char] ParserState m Blocks -- ^ Block list parser
791791 -> Bool -- ^ Headerless table
792− -> ParserT [Char] ParserState m Block
792+ -> ParserT [Char] ParserState m Blocks
793793gridTableWith blocks headless =
794794 tableWith (gridTableHeader headless blocks) (gridTableRow blocks)
795795 (gridTableSep '-') gridTableFooter
@@ -818,8 +818,8 @@ gridTableSep ch = try $ gridDashedLines ch >> return '\n'
818818-- | Parse header for a grid table.
819819gridTableHeader :: Stream [Char] m Char
820820 => Bool -- ^ Headerless table
821− -> ParserT [Char] ParserState m [Block]
822− -> ParserT [Char] ParserState m ([[Block]], [Alignment], [Int])
821+ -> ParserT [Char] ParserState m Blocks
822+ -> ParserT [Char] ParserState m ([Blocks], [Alignment], [Int])
823823gridTableHeader headless blocks = try $ do
824824 optional blanklines
825825 dashes <- gridDashedLines '-'
@@ -850,9 +850,9 @@ gridTableRawLine indices = do
850850
851851-- | Parse row of grid table.
852852gridTableRow :: Stream [Char] m Char
853− => ParserT [Char] ParserState m [Block]
853+ => ParserT [Char] ParserState m Blocks
854854 -> [Int]
855− -> ParserT [Char] ParserState m [[Block]]
855+ -> ParserT [Char] ParserState m [Blocks]
856856gridTableRow blocks indices = do
857857 colLines <- many1 (gridTableRawLine indices)
858858 let cols = map ((++ "\n") . unlines . removeOneLeadingSpace) $
@@ -867,7 +867,7 @@ removeOneLeadingSpace xs =
867867 where startsWithSpace "" = True
868868 startsWithSpace (y:_) = y == ' '
869869
870−compactifyCell :: [Block] -> [Block]
870+compactifyCell :: Blocks -> Blocks
871871compactifyCell bs = head $ compactify [bs]
872872
873873-- | Parse footer for a grid table.
@@ -883,7 +883,7 @@ readWithM :: (Monad m)
883883 -> String -- ^ input
884884 -> m (Either PandocError a)
885885readWithM parser state input =
886− mapLeft (ParsecError input) `liftM` runParserT parser state "source" input
886+ mapLeft (PandocParsecError input) `liftM` runParserT parser state "source" input
887887
888888
889889-- | Parse a string with a given parser and state
@@ -893,15 +893,6 @@ readWith :: Parser [Char] st a
893893 -> Either PandocError a
894894readWith p t inp = runIdentity $ readWithM p t inp
895895
896−readWithWarnings :: Parser [Char] ParserState a
897− -> ParserState
898− -> String
899− -> Either PandocError (a, [String])
900−readWithWarnings p = readWith $ do
901− doc <- p
902− warnings <- stateWarnings <$> getState
903− return (doc, warnings)
904−
905896-- | Parse a string with @parser@ (for testing).
906897testStringWith :: (Show a)
907898 => ParserT [Char] ParserState Identity a
@@ -938,8 +929,8 @@ data ParserState = ParserState
938929 -- roles), 3) Additional classes (rest of Attr is unused)).
939930 stateCaption :: Maybe Inlines, -- ^ Caption in current environment
940931 stateInHtmlBlock :: Maybe String, -- ^ Tag type of HTML block being parsed
941− stateMarkdownAttribute :: Bool, -- ^ True if in markdown=1 context
942− stateWarnings :: [String] -- ^ Warnings generated by the parser
932+ stateContainers :: [String], -- ^ parent include files
933+ stateMarkdownAttribute :: Bool -- ^ True if in markdown=1 context
943934 }
944935
945936instance Default ParserState where
@@ -1034,16 +1025,17 @@ defaultParserState =
10341025 stateRstCustomRoles = M.empty,
10351026 stateCaption = Nothing,
10361027 stateInHtmlBlock = Nothing,
1037− stateMarkdownAttribute = False,
1038− stateWarnings = []}
1028+ stateContainers = [],
1029+ stateMarkdownAttribute = False
1030+ }
10391031
10401032-- | Succeed only if the extension is enabled.
10411033guardEnabled :: (Stream s m a, HasReaderOptions st) => Extension -> ParserT s st m ()
1042−guardEnabled ext = getOption readerExtensions >>= guard . Set.member ext
1034+guardEnabled ext = getOption readerExtensions >>= guard . extensionEnabled ext
10431035
10441036-- | Succeed only if the extension is disabled.
10451037guardDisabled :: (Stream s m a, HasReaderOptions st) => Extension -> ParserT s st m ()
1046−guardDisabled ext = getOption readerExtensions >>= guard . not . Set.member ext
1038+guardDisabled ext = getOption readerExtensions >>= guard . not . extensionEnabled ext
10471039
10481040-- | Update the position on which the last string ended.
10491041updateLastStrPos :: (Stream s m a, HasLastStrPosition st) => ParserT s st m ()
@@ -1098,10 +1090,10 @@ registerHeader (ident,classes,kvs) header' = do
10981090 ids <- extractIdentifierList <$> getState
10991091 exts <- getOption readerExtensions
11001092 let insert' = M.insertWith (\_new old -> old)
1101− if null ident && Ext_auto_identifiers `Set.member` exts
1093+ if null ident && Ext_auto_identifiers `extensionEnabled` exts
11021094 then do
11031095 let id' = uniqueIdent (B.toList header') ids
1104− let id'' = if Ext_ascii_identifiers `Set.member` exts
1096+ let id'' = if Ext_ascii_identifiers `extensionEnabled` exts
11051097 then catMaybes $ map toAsciiChar id'
11061098 else id'
11071099 updateState $ updateIdentifierList $ Set.insert id'
@@ -1113,15 +1105,11 @@ registerHeader (ident,classes,kvs) header' = do
11131105 updateState $ updateHeaderMap $ insert' header' ident
11141106 return (ident,classes,kvs)
11151107
1116−-- | Fail unless we're in "smart typography" mode.
1117−failUnlessSmart :: (Stream s m a, HasReaderOptions st) => ParserT s st m ()
1118−failUnlessSmart = getOption readerSmart >>= guard
1119−
11201108smartPunctuation :: (HasReaderOptions st, HasLastStrPosition st, HasQuoteContext st m, Stream s m Char)
11211109 => ParserT s st m Inlines
11221110 -> ParserT s st m Inlines
11231111smartPunctuation inlineParser = do
1124− failUnlessSmart
1112+ guardEnabled Ext_smart
11251113 choice [ quoted inlineParser, apostrophe, dash, ellipses ]
11261114
11271115apostrophe :: Stream s m Char => ParserT s st m Inlines
@@ -1195,7 +1183,7 @@ ellipses = try (string "..." >> return (B.str "\8230"))
11951183dash :: (HasReaderOptions st, Stream s m Char)
11961184 => ParserT s st m Inlines
11971185dash = try $ do
1198− oldDashes <- getOption readerOldDashes
1186+ oldDashes <- extensionEnabled Ext_old_dashes <$> getOption readerExtensions
11991187 if oldDashes
12001188 then do
12011189 char '-'
@@ -1272,12 +1260,6 @@ applyMacros' target = do
12721260 return $ applyMacros macros target
12731261 else return target
12741262
1275−-- | Append a warning to the log.
1276−addWarning :: Maybe SourcePos -> String -> Parser [Char] ParserState ()
1277−addWarning mbpos msg =
1278− updateState $ \st -> st{
1279− stateWarnings = (msg ++ maybe "" (\pos -> " " ++ show pos) mbpos) :
1280− stateWarnings st }
12811263infixr 5 <+?>
12821264(<+?>) :: (Monoid a) => ParserT s st m a -> ParserT s st m a -> ParserT s st m a
12831265a <+?> b = a >>= flip fmap (try b <|> return mempty) . (<>)
src/Text/Pandoc/Process.hs+8−14
@@ -42,9 +42,9 @@ Version of 'System.Process.readProcessWithExitCode' that uses lazy bytestrings
4242instead of strings and allows setting environment variables.
4343
4444@readProcessWithExitCode@ creates an external process, reads its
45−standard output and standard error strictly, waits until the process
46−terminates, and then returns the 'ExitCode' of the process,
47−the standard output, and the standard error.
45+standard output strictly, waits until the process
46+terminates, and then returns the 'ExitCode' of the process
47+and the standard output. stderr is inherited from the parent.
4848
4949If an asynchronous exception is thrown to the thread executing
5050@readProcessWithExitCode@, the forked process will be terminated and
@@ -57,25 +57,21 @@ pipeProcess
5757 -> FilePath -- ^ Filename of the executable (see 'proc' for details)
5858 -> [String] -- ^ any arguments
5959 -> BL.ByteString -- ^ standard input
60− -> IO (ExitCode,BL.ByteString,BL.ByteString) -- ^ exitcode, stdout, stderr
60+ -> IO (ExitCode,BL.ByteString) -- ^ exitcode, stdout
6161pipeProcess mbenv cmd args input =
6262 mask $ \restore -> do
63− (Just inh, Just outh, Just errh, pid) <- createProcess (proc cmd args)
63+ (Just inh, Just outh, Nothing, pid) <- createProcess (proc cmd args)
6464 { env = mbenv,
6565 std_in = CreatePipe,
6666 std_out = CreatePipe,
67− std_err = CreatePipe }
67+ std_err = Inherit }
6868 flip onException
69− (do hClose inh; hClose outh; hClose errh;
69+ (do hClose inh; hClose outh;
7070 terminateProcess pid; waitForProcess pid) $ restore $ do
7171 -- fork off a thread to start consuming stdout
7272 out <- BL.hGetContents outh
7373 waitOut <- forkWait $ evaluate $ BL.length out
7474
75− -- fork off a thread to start consuming stderr
76− err <- BL.hGetContents errh
77− waitErr <- forkWait $ evaluate $ BL.length err
78−
7975 -- now write and flush any input
8076 let writeInput = do
8177 unless (BL.null input) $ do
@@ -87,15 +83,13 @@ pipeProcess mbenv cmd args input =
8783
8884 -- wait on the output
8985 waitOut
90− waitErr
9186
9287 hClose outh
93− hClose errh
9488
9589 -- wait on the process
9690 ex <- waitForProcess pid
9791
98− return (ex, out, err)
92+ return (ex, out)
9993
10094forkWait :: IO a -> IO (IO a)
10195forkWait a = do
src/Text/Pandoc/Readers/CommonMark.hs+5−4
@@ -37,12 +37,13 @@ import Data.Text (unpack, pack)
3737import Data.List (groupBy)
3838import Text.Pandoc.Definition
3939import Text.Pandoc.Options
40−import Text.Pandoc.Error
40+import Text.Pandoc.Class (PandocMonad)
4141
4242-- | Parse a CommonMark formatted string into a 'Pandoc' structure.
43−readCommonMark :: ReaderOptions -> String -> Either PandocError Pandoc
44−readCommonMark opts = Right . nodeToPandoc . commonmarkToNode opts' . pack
45− where opts' = if readerSmart opts
43+readCommonMark :: PandocMonad m => ReaderOptions -> String -> m Pandoc
44+readCommonMark opts s = return $
45+ nodeToPandoc $ commonmarkToNode opts' $ pack s
46+ where opts' = if extensionEnabled Ext_smart (readerExtensions opts)
4647 then [optNormalize, optSmart]
4748 else [optNormalize]
4849
src/Text/Pandoc/Readers/DocBook.hs+16−16
@@ -13,10 +13,9 @@ import Control.Monad.State
1313import Data.List (intersperse)
1414import Data.Maybe (fromMaybe)
1515import Text.TeXMath (readMathML, writeTeX)
16−import Text.Pandoc.Error (PandocError)
17−import Control.Monad.Except
1816import Data.Default
1917import Data.Foldable (asum)
18+import Text.Pandoc.Class (PandocMonad)
2019
2120{-
2221
@@ -502,7 +501,7 @@ List of all DocBook tags, with [x] indicating implemented,
502501[x] ?asciidoc-br? - line break from asciidoc docbook output
503502-}
504503
505−type DB = ExceptT PandocError (State DBState)
504+type DB m = StateT DBState m
506505
507506data DBState = DBState{ dbSectionLevel :: Int
508507 , dbQuoteType :: QuoteType
@@ -523,10 +522,11 @@ instance Default DBState where
523522 , dbContent = [] }
524523
525524
526−readDocBook :: ReaderOptions -> String -> Either PandocError Pandoc
527−readDocBook _ inp = (\blocks -> Pandoc (dbMeta st') (toList . mconcat $ blocks)) <$> bs
528− where (bs , st') = flip runState (def{ dbContent = tree }) . runExceptT . mapM parseBlock $ tree
529− tree = normalizeTree . parseXML . handleInstructions $ inp
525+readDocBook :: PandocMonad m => ReaderOptions -> String -> m Pandoc
526+readDocBook _ inp = do
527+ let tree = normalizeTree . parseXML . handleInstructions $ inp
528+ (bs, st') <- flip runStateT (def{ dbContent = tree }) $ mapM parseBlock $ tree
529+ return $ Pandoc (dbMeta st') (toList . mconcat $ bs)
530530
531531-- We treat <?asciidoc-br?> specially (issue #1236), converting it
532532-- to <br/>, since xml-light doesn't parse the instruction correctly.
@@ -538,7 +538,7 @@ handleInstructions xs = case break (=='<') xs of
538538 ([], '<':zs) -> '<' : handleInstructions zs
539539 (ys, zs) -> ys ++ handleInstructions zs
540540
541−getFigure :: Element -> DB Blocks
541+getFigure :: PandocMonad m => Element -> DB m Blocks
542542getFigure e = do
543543 tit <- case filterChild (named "title") e of
544544 Just t -> getInlines t
@@ -579,20 +579,20 @@ named s e = qName (elName e) == s
579579
580580--
581581
582−acceptingMetadata :: DB a -> DB a
582+acceptingMetadata :: PandocMonad m => DB m a -> DB m a
583583acceptingMetadata p = do
584584 modify (\s -> s { dbAcceptsMeta = True } )
585585 res <- p
586586 modify (\s -> s { dbAcceptsMeta = False })
587587 return res
588588
589−checkInMeta :: Monoid a => DB () -> DB a
589+checkInMeta :: (PandocMonad m, Monoid a) => DB m () -> DB m a
590590checkInMeta p = do
591591 accepts <- dbAcceptsMeta <$> get
592592 when accepts p
593593 return mempty
594594
595−addMeta :: ToMetaValue a => String -> a -> DB ()
595+addMeta :: PandocMonad m => ToMetaValue a => String -> a -> DB m ()
596596addMeta field val = modify (setMeta field val)
597597
598598instance HasMeta DBState where
@@ -631,7 +631,7 @@ addToStart toadd bs =
631631-- function that is used by both mediaobject (in parseBlock)
632632-- and inlinemediaobject (in parseInline)
633633-- A DocBook mediaobject is a wrapper around a set of alternative presentations
634−getMediaobject :: Element -> DB Inlines
634+getMediaobject :: PandocMonad m => Element -> DB m Inlines
635635getMediaobject e = do
636636 (imageUrl, attr) <-
637637 case filterChild (named "imageobject") e of
@@ -658,11 +658,11 @@ getMediaobject e = do
658658 else (return figTitle, "fig:")
659659 liftM (imageWith attr imageUrl title) caption
660660
661−getBlocks :: Element -> DB Blocks
661+getBlocks :: PandocMonad m => Element -> DB m Blocks
662662getBlocks e = mconcat <$> (mapM parseBlock $ elContent e)
663663
664664
665−parseBlock :: Content -> DB Blocks
665+parseBlock :: PandocMonad m => Content -> DB m Blocks
666666parseBlock (Text (CData CDataRaw _ _)) = return mempty -- DOCTYPE
667667parseBlock (Text (CData _ s _)) = if all isSpace s
668668 then return mempty
@@ -902,7 +902,7 @@ parseBlock (Elem e) =
902902 lineItems = mapM getInlines $ filterChildren (named "line") e
903903 metaBlock = acceptingMetadata (getBlocks e) >> return mempty
904904
905−getInlines :: Element -> DB Inlines
905+getInlines :: PandocMonad m => Element -> DB m Inlines
906906getInlines e' = (trimInlines . mconcat) <$> (mapM parseInline $ elContent e')
907907
908908strContentRecursive :: Element -> String
@@ -913,7 +913,7 @@ elementToStr :: Content -> Content
913913elementToStr (Elem e') = Text $ CData CDataText (strContentRecursive e') Nothing
914914elementToStr x = x
915915
916−parseInline :: Content -> DB Inlines
916+parseInline :: PandocMonad m => Content -> DB m Inlines
917917parseInline (Text (CData _ s _)) = return $ text s
918918parseInline (CRef ref) =
919919 return $ maybe (text $ map toUpper ref) (text) $ lookupEntity ref
src/Text/Pandoc/Readers/Docx.hs+46−53
@@ -64,7 +64,7 @@ implemented, [-] means partially implemented):
6464 - [X] Math
6565 - [X] Link (links to an arbitrary bookmark create a span with the target as
6666 id and "anchor" class)
67− - [X] Image
67+ - [X] Image
6868 - [X] Note (Footnotes and Endnotes are silently combined.)
6969-}
7070
@@ -82,7 +82,7 @@ import Text.Pandoc.Readers.Docx.Parse
8282import Text.Pandoc.Readers.Docx.Lists
8383import Text.Pandoc.Readers.Docx.Combine
8484import Text.Pandoc.Shared
85−import Text.Pandoc.MediaBag (insertMedia, MediaBag)
85+import Text.Pandoc.MediaBag (MediaBag)
8686import Data.List (delete, intersect)
8787import Text.TeXMath (writeTeX)
8888import Data.Default (Default)
@@ -96,27 +96,29 @@ import qualified Data.Sequence as Seq (null)
9696#if !(MIN_VERSION_base(4,8,0))
9797import Data.Traversable (traverse)
9898#endif
99−
10099import Text.Pandoc.Error
101−import Control.Monad.Except
100+import Control.Monad.Except (throwError)
101+import Text.Pandoc.Class (PandocMonad)
102+import qualified Text.Pandoc.Class as P
102103
103−readDocxWithWarnings :: ReaderOptions
104− -> B.ByteString
105− -> Either PandocError (Pandoc, MediaBag, [String])
106−readDocxWithWarnings opts bytes
104+readDocx :: PandocMonad m
105+ => ReaderOptions
106+ -> B.ByteString
107+ -> m Pandoc
108+readDocx opts bytes
107109 | Right archive <- toArchiveOrFail bytes
108110 , Right (docx, parserWarnings) <- archiveToDocxWithWarnings archive = do
109− (meta, blks, mediaBag, warnings) <- docxToOutput opts docx
110− return (Pandoc meta blks, mediaBag, parserWarnings ++ warnings)
111−readDocxWithWarnings _ _ =
112− Left (ParseFailure "couldn't parse docx file")
113−
114−readDocx :: ReaderOptions
111+ mapM_ P.warning parserWarnings
112+ (meta, blks) <- docxToOutput opts docx
113+ return $ Pandoc meta blks
114+readDocx _ _ =
115+ throwError $ PandocSomeError "couldn't parse docx file"
116+
117+readDocxWithWarnings :: PandocMonad m
118+ => ReaderOptions
115119 -> B.ByteString
116− -> Either PandocError (Pandoc, MediaBag)
117−readDocx opts bytes = do
118− (pandoc, mediaBag, _) <- readDocxWithWarnings opts bytes
119− return (pandoc, mediaBag)
120+ -> m Pandoc
121+readDocxWithWarnings = readDocx
120122
121123data DState = DState { docxAnchorMap :: M.Map String String
122124 , docxMediaBag :: MediaBag
@@ -137,15 +139,10 @@ data DEnv = DEnv { docxOptions :: ReaderOptions
137139instance Default DEnv where
138140 def = DEnv def False
139141
140−type DocxContext = ExceptT PandocError (ReaderT DEnv (State DState))
142+type DocxContext m = ReaderT DEnv (StateT DState m)
141143
142−evalDocxContext :: DocxContext a -> DEnv -> DState -> Either PandocError a
143−evalDocxContext ctx env st = flip evalState st . flip runReaderT env . runExceptT $ ctx
144−
145−addDocxWarning :: String -> DocxContext ()
146−addDocxWarning msg = do
147− warnings <- gets docxWarnings
148− modify $ \s -> s {docxWarnings = msg : warnings}
144+evalDocxContext :: PandocMonad m => DocxContext m a -> DEnv -> DState -> m a
145+evalDocxContext ctx env st = flip evalStateT st $ flip runReaderT env $ ctx
149146
150147-- This is empty, but we put it in for future-proofing.
151148spansToKeep :: [String]
@@ -179,7 +176,7 @@ isEmptyPar (Paragraph _ parParts) =
179176 isEmptyElem _ = True
180177isEmptyPar _ = False
181178
182−bodyPartsToMeta' :: [BodyPart] -> DocxContext (M.Map String MetaValue)
179+bodyPartsToMeta' :: PandocMonad m => [BodyPart] -> DocxContext m (M.Map String MetaValue)
183180bodyPartsToMeta' [] = return M.empty
184181bodyPartsToMeta' (bp : bps)
185182 | (Paragraph pPr parParts) <- bp
@@ -195,7 +192,7 @@ bodyPartsToMeta' (bp : bps)
195192 return $ M.insertWith f metaField (MetaInlines (toList inlines)) remaining
196193bodyPartsToMeta' (_ : bps) = bodyPartsToMeta' bps
197194
198−bodyPartsToMeta :: [BodyPart] -> DocxContext Meta
195+bodyPartsToMeta :: PandocMonad m => [BodyPart] -> DocxContext m Meta
199196bodyPartsToMeta bps = do
200197 mp <- bodyPartsToMeta' bps
201198 let mp' =
@@ -297,7 +294,7 @@ runStyleToTransform rPr
297294 emph . (runStyleToTransform rPr {rUnderline = Nothing})
298295 | otherwise = id
299296
300−runToInlines :: Run -> DocxContext Inlines
297+runToInlines :: PandocMonad m => Run -> DocxContext m Inlines
301298runToInlines (Run rs runElems)
302299 | Just (s, _) <- rStyle rs
303300 , s `elem` codeStyles =
@@ -318,8 +315,7 @@ runToInlines (Endnote bps) = do
318315 blksList <- smushBlocks <$> (mapM bodyPartToBlocks bps)
319316 return $ note blksList
320317runToInlines (InlineDrawing fp title alt bs ext) = do
321− mediaBag <- gets docxMediaBag
322− modify $ \s -> s { docxMediaBag = insertMedia fp Nothing bs mediaBag }
318+ (lift . lift) $ P.insertMedia fp Nothing bs
323319 return $ imageWith (extentToAttr ext) fp title $ text alt
324320runToInlines InlineChart = return $ spanWith ("", ["chart"], []) $ text "[CHART]"
325321
@@ -330,7 +326,7 @@ extentToAttr (Just (w, h)) =
330326 showDim d = show (d / 914400) ++ "in"
331327extentToAttr _ = nullAttr
332328
333−blocksToInlinesWarn :: String -> Blocks -> DocxContext Inlines
329+blocksToInlinesWarn :: PandocMonad m => String -> Blocks -> DocxContext m Inlines
334330blocksToInlinesWarn cmtId blks = do
335331 let blkList = toList blks
336332 notParaOrPlain :: Block -> Bool
@@ -338,10 +334,10 @@ blocksToInlinesWarn cmtId blks = do
338334 notParaOrPlain (Plain _) = False
339335 notParaOrPlain _ = True
340336 when (not $ null $ filter notParaOrPlain blkList)
341− (addDocxWarning $ "Docx comment " ++ cmtId ++ " will not retain formatting")
337+ ((lift . lift) $ P.warning $ "Docx comment " ++ cmtId ++ " will not retain formatting")
342338 return $ fromList $ blocksToInlines blkList
343339
344−parPartToInlines :: ParPart -> DocxContext Inlines
340+parPartToInlines :: PandocMonad m => ParPart -> DocxContext m Inlines
345341parPartToInlines (PlainRun r) = runToInlines r
346342parPartToInlines (Insertion _ author date runs) = do
347343 opts <- asks docxOptions
@@ -403,8 +399,7 @@ parPartToInlines (BookMark _ anchor) =
403399 (modify $ \s -> s { docxAnchorMap = M.insert anchor newAnchor anchorMap})
404400 return $ spanWith (newAnchor, ["anchor"], []) mempty
405401parPartToInlines (Drawing fp title alt bs ext) = do
406− mediaBag <- gets docxMediaBag
407− modify $ \s -> s { docxMediaBag = insertMedia fp Nothing bs mediaBag }
402+ (lift . lift) $ P.insertMedia fp Nothing bs
408403 return $ imageWith (extentToAttr ext) fp title $ text alt
409404parPartToInlines Chart = do
410405 return $ spanWith ("", ["chart"], []) $ text "[CHART]"
@@ -426,10 +421,10 @@ isAnchorSpan _ = False
426421dummyAnchors :: [String]
427422dummyAnchors = ["_GoBack"]
428423
429−makeHeaderAnchor :: Blocks -> DocxContext Blocks
424+makeHeaderAnchor :: PandocMonad m => Blocks -> DocxContext m Blocks
430425makeHeaderAnchor bs = traverse makeHeaderAnchor' bs
431426
432−makeHeaderAnchor' :: Block -> DocxContext Block
427+makeHeaderAnchor' :: PandocMonad m => Block -> DocxContext m Block
433428-- If there is an anchor already there (an anchor span in the header,
434429-- to be exact), we rename and associate the new id with the old one.
435430makeHeaderAnchor' (Header n (ident, classes, kvs) ils)
@@ -463,12 +458,12 @@ singleParaToPlain blks
463458 singleton $ Plain ils
464459singleParaToPlain blks = blks
465460
466−cellToBlocks :: Cell -> DocxContext Blocks
461+cellToBlocks :: PandocMonad m => Cell -> DocxContext m Blocks
467462cellToBlocks (Cell bps) = do
468463 blks <- smushBlocks <$> mapM bodyPartToBlocks bps
469464 return $ fromList $ blocksToDefinitions $ blocksToBullets $ toList blks
470465
471−rowToBlocksList :: Row -> DocxContext [Blocks]
466+rowToBlocksList :: PandocMonad m => Row -> DocxContext m [Blocks]
472467rowToBlocksList (Row cells) = do
473468 blksList <- mapM cellToBlocks cells
474469 return $ map singleParaToPlain blksList
@@ -518,7 +513,7 @@ parStyleToTransform pPr
518513 False -> parStyleToTransform pPr'
519514parStyleToTransform _ = id
520515
521−bodyPartToBlocks :: BodyPart -> DocxContext Blocks
516+bodyPartToBlocks :: PandocMonad m => BodyPart -> DocxContext m Blocks
522517bodyPartToBlocks (Paragraph pPr parparts)
523518 | not $ null $ codeDivs `intersect` (pStyle pPr) =
524519 return
@@ -559,7 +554,7 @@ bodyPartToBlocks (ListItem pPr numId lvl (Just levelInfo) parparts) = do
559554 ]
560555 blks <- bodyPartToBlocks (Paragraph pPr parparts)
561556 return $ divWith ("", ["list-item"], kvs) blks
562−bodyPartToBlocks (ListItem pPr _ _ _ parparts) =
557+bodyPartToBlocks (ListItem pPr _ _ _ parparts) =
563558 let pPr' = pPr {pStyle = "ListParagraph": (pStyle pPr)}
564559 in
565560 bodyPartToBlocks $ Paragraph pPr' parparts
@@ -597,7 +592,7 @@ bodyPartToBlocks (OMathPara e) = do
597592
598593
599594-- replace targets with generated anchors.
600−rewriteLink' :: Inline -> DocxContext Inline
595+rewriteLink' :: PandocMonad m => Inline -> DocxContext m Inline
601596rewriteLink' l@(Link attr ils ('#':target, title)) = do
602597 anchorMap <- gets docxAnchorMap
603598 return $ case M.lookup target anchorMap of
@@ -605,23 +600,21 @@ rewriteLink' l@(Link attr ils ('#':target, title)) = do
605600 Nothing -> l
606601rewriteLink' il = return il
607602
608−rewriteLinks :: [Block] -> DocxContext [Block]
603+rewriteLinks :: PandocMonad m => [Block] -> DocxContext m [Block]
609604rewriteLinks = mapM (walkM rewriteLink')
610605
611−bodyToOutput :: Body -> DocxContext (Meta, [Block], MediaBag, [String])
606+bodyToOutput :: PandocMonad m => Body -> DocxContext m (Meta, [Block])
612607bodyToOutput (Body bps) = do
613608 let (metabps, blkbps) = sepBodyParts bps
614609 meta <- bodyPartsToMeta metabps
615610 blks <- smushBlocks <$> mapM bodyPartToBlocks blkbps
616611 blks' <- rewriteLinks $ blocksToDefinitions $ blocksToBullets $ toList blks
617− mediaBag <- gets docxMediaBag
618− warnings <- gets docxWarnings
619− return $ (meta,
620− blks',
621− mediaBag,
622− warnings)
623−
624−docxToOutput :: ReaderOptions -> Docx -> Either PandocError (Meta, [Block], MediaBag, [String])
612+ return $ (meta, blks')
613+
614+docxToOutput :: PandocMonad m
615+ => ReaderOptions
616+ -> Docx
617+ -> m (Meta, [Block])
625618docxToOutput opts (Docx (Document _ body)) =
626619 let dEnv = def { docxOptions = opts} in
627620 evalDocxContext (bodyToOutput body) dEnv def
src/Text/Pandoc/Readers/Docx/Parse.hs+1−1
@@ -690,7 +690,7 @@ elemToParPart ns element
690690 , Just drawingElem <- findChild (elemName ns "w" "drawing") element
691691 , c_ns <- "http://schemas.openxmlformats.org/drawingml/2006/chart"
692692 , Just _ <- findElement (QName "chart" (Just c_ns) (Just "c")) drawingElem
693− = return Chart
693+ = return Chart
694694elemToParPart ns element
695695 | isElem ns "w" "r" element =
696696 elemToRun ns element >>= (\r -> return $ PlainRun r)
src/Text/Pandoc/Readers/EPUB.hs+28−34
@@ -11,13 +11,12 @@ module Text.Pandoc.Readers.EPUB
1111import Text.XML.Light
1212import Text.Pandoc.Definition hiding (Attr)
1313import Text.Pandoc.Readers.HTML (readHtml)
14−import Text.Pandoc.Error
1514import Text.Pandoc.Walk (walk, query)
16−import Text.Pandoc.Options ( ReaderOptions(..), readerTrace)
15+import Text.Pandoc.Options ( ReaderOptions(..), Verbosity(..))
1716import Text.Pandoc.Shared (escapeURI, collapseFilePath, addMetaField)
1817import Network.URI (unEscapeString)
1918import Text.Pandoc.MediaBag (MediaBag, insertMedia)
20−import Control.Monad.Except (MonadError, throwError, runExcept, Except)
19+import Control.Monad.Except (throwError)
2120import Text.Pandoc.MIME (MimeType)
2221import qualified Text.Pandoc.Builder as B
2322import Codec.Archive.Zip ( Archive (..), toArchiveOrFail, fromEntry
@@ -27,29 +26,30 @@ import System.FilePath ( takeFileName, (</>), dropFileName, normalise
2726 , dropFileName
2827 , splitFileName )
2928import qualified Text.Pandoc.UTF8 as UTF8 (toStringLazy)
30−import Control.Monad (guard, liftM, when)
29+import Control.Monad (guard, liftM)
3130import Data.List (isPrefixOf, isInfixOf)
3231import Data.Maybe (mapMaybe, fromMaybe)
3332import qualified Data.Map as M (Map, lookup, fromList, elems)
3433import Data.Monoid ((<>))
3534import Control.DeepSeq (deepseq, NFData)
36−
37−import Debug.Trace (trace)
35+import Text.Pandoc.Error
36+import Text.Pandoc.Class (PandocMonad, report)
37+import qualified Text.Pandoc.Class as P
3838
3939type Items = M.Map String (FilePath, MimeType)
4040
41−readEPUB :: ReaderOptions -> BL.ByteString -> Either PandocError (Pandoc, MediaBag)
41+readEPUB :: PandocMonad m => ReaderOptions -> BL.ByteString -> m Pandoc
4242readEPUB opts bytes = case toArchiveOrFail bytes of
43− Right archive -> runEPUB $ archiveToEPUB opts $ archive
44− Left _ -> Left $ ParseFailure "Couldn't extract ePub file"
43+ Right archive -> archiveToEPUB opts $ archive
44+ Left _ -> throwError $ PandocParseError "Couldn't extract ePub file"
4545
46−runEPUB :: Except PandocError a -> Either PandocError a
47−runEPUB = runExcept
46+-- runEPUB :: Except PandocError a -> Either PandocError a
47+-- runEPUB = runExcept
4848
4949-- Note that internal reference are aggresively normalised so that all ids
5050-- are of the form "filename#id"
5151--
52−archiveToEPUB :: (MonadError PandocError m) => ReaderOptions -> Archive -> m (Pandoc, MediaBag)
52+archiveToEPUB :: (PandocMonad m) => ReaderOptions -> Archive -> m Pandoc
5353archiveToEPUB os archive = do
5454 -- root is path to folder with manifest file in
5555 (root, content) <- getManifest archive
@@ -63,24 +63,21 @@ archiveToEPUB os archive = do
6363 foldM' (\a b -> ((a <>) . walk (prependHash escapedSpine))
6464 `liftM` parseSpineElem root b) mempty spine
6565 let ast = coverDoc <> (Pandoc meta bs)
66− let mediaBag = fetchImages (M.elems items) root archive ast
67− return $ (ast, mediaBag)
66+ P.setMediaBag $ fetchImages (M.elems items) root archive ast
67+ return ast
6868 where
6969 os' = os {readerParseRaw = True}
70− parseSpineElem :: MonadError PandocError m => FilePath -> (FilePath, MimeType) -> m Pandoc
70+ parseSpineElem :: PandocMonad m => FilePath -> (FilePath, MimeType) -> m Pandoc
7171 parseSpineElem (normalise -> r) (normalise -> path, mime) = do
72− when (readerTrace os) (traceM path)
72+ report DEBUG ("parseSpineElem called with path " ++ show path)
7373 doc <- mimeToReader mime r path
7474 let docSpan = B.doc $ B.para $ B.spanWith (takeFileName path, [], []) mempty
7575 return $ docSpan <> doc
76− mimeToReader :: MonadError PandocError m => MimeType -> FilePath -> FilePath -> m Pandoc
76+ mimeToReader :: PandocMonad m => MimeType -> FilePath -> FilePath -> m Pandoc
7777 mimeToReader "application/xhtml+xml" (unEscapeString -> root)
7878 (unEscapeString -> path) = do
7979 fname <- findEntryByPathE (root </> path) archive
80− html <- either throwError return .
81− readHtml os' .
82− UTF8.toStringLazy $
83− fromEntry fname
80+ html <- readHtml os' . UTF8.toStringLazy $ fromEntry fname
8481 return $ fixInternalReferences path html
8582 mimeToReader s _ (unEscapeString -> path)
8683 | s `elem` imageMimes = return $ imageToPandoc path
@@ -121,7 +118,7 @@ imageMimes = ["image/gif", "image/jpeg", "image/png"]
121118
122119type CoverImage = FilePath
123120
124−parseManifest :: (MonadError PandocError m) => Element -> m (Maybe CoverImage, Items)
121+parseManifest :: (PandocMonad m) => Element -> m (Maybe CoverImage, Items)
125122parseManifest content = do
126123 manifest <- findElementE (dfName "manifest") content
127124 let items = findChildren (dfName "item") manifest
@@ -137,7 +134,7 @@ parseManifest content = do
137134 mime <- findAttrE (emptyName "media-type") e
138135 return (uid, (href, mime))
139136
140−parseSpine :: MonadError PandocError m => Items -> Element -> m [(FilePath, MimeType)]
137+parseSpine :: PandocMonad m => Items -> Element -> m [(FilePath, MimeType)]
141138parseSpine is e = do
142139 spine <- findElementE (dfName "spine") e
143140 let itemRefs = findChildren (dfName "itemref") spine
@@ -148,7 +145,7 @@ parseSpine is e = do
148145 guard linear
149146 findAttr (emptyName "idref") ref
150147
151−parseMeta :: MonadError PandocError m => Element -> m Meta
148+parseMeta :: PandocMonad m => Element -> m Meta
152149parseMeta content = do
153150 meta <- findElementE (dfName "metadata") content
154151 let dcspace (QName _ (Just "http://purl.org/dc/elements/1.1/") (Just "dc")) = True
@@ -166,7 +163,7 @@ renameMeta :: String -> String
166163renameMeta "creator" = "author"
167164renameMeta s = s
168165
169−getManifest :: MonadError PandocError m => Archive -> m (String, Element)
166+getManifest :: PandocMonad m => Archive -> m (String, Element)
170167getManifest archive = do
171168 metaEntry <- findEntryByPathE ("META-INF" </> "container.xml") archive
172169 docElem <- (parseXMLDocE . UTF8.toStringLazy . fromEntry) metaEntry
@@ -242,9 +239,6 @@ foldM' f z (x:xs) = do
242239uncurry3 :: (a -> b -> c -> d) -> (a, b, c) -> d
243240uncurry3 f (a, b, c) = f a b c
244241
245−traceM :: Monad m => String -> m ()
246−traceM = flip trace (return ())
247−
248242-- Utility
249243
250244stripNamespace :: QName -> String
@@ -268,18 +262,18 @@ emptyName s = QName s Nothing Nothing
268262
269263-- Convert Maybe interface to Either
270264
271−findAttrE :: MonadError PandocError m => QName -> Element -> m String
265+findAttrE :: PandocMonad m => QName -> Element -> m String
272266findAttrE q e = mkE "findAttr" $ findAttr q e
273267
274−findEntryByPathE :: MonadError PandocError m => FilePath -> Archive -> m Entry
268+findEntryByPathE :: PandocMonad m => FilePath -> Archive -> m Entry
275269findEntryByPathE (normalise -> path) a =
276270 mkE ("No entry on path: " ++ path) $ findEntryByPath path a
277271
278−parseXMLDocE :: MonadError PandocError m => String -> m Element
272+parseXMLDocE :: PandocMonad m => String -> m Element
279273parseXMLDocE doc = mkE "Unable to parse XML doc" $ parseXMLDoc doc
280274
281−findElementE :: MonadError PandocError m => QName -> Element -> m Element
275+findElementE :: PandocMonad m => QName -> Element -> m Element
282276findElementE e x = mkE ("Unable to find element: " ++ show e) $ findElement e x
283277
284−mkE :: MonadError PandocError m => String -> Maybe a -> m a
285−mkE s = maybe (throwError . ParseFailure $ s) return
278+mkE :: PandocMonad m => String -> Maybe a -> m a
279+mkE s = maybe (throwError . PandocParseError $ s) return
src/Text/Pandoc/Readers/HTML.hs+119−105
@@ -44,9 +44,9 @@ import Text.Pandoc.Definition
4444import qualified Text.Pandoc.Builder as B
4545import Text.Pandoc.Builder (Blocks, Inlines, trimInlines, HasMeta(..))
4646import Text.Pandoc.Shared ( extractSpaces, renderTags', addMetaField
47− , escapeURI, safeRead, mapLeft )
48−import Text.Pandoc.Options (ReaderOptions(readerParseRaw, readerTrace)
49− , Extension (Ext_epub_html_exts,
47+ , escapeURI, safeRead )
48+import Text.Pandoc.Options (ReaderOptions(readerParseRaw),
49+ Verbosity(..), Extension (Ext_epub_html_exts,
5050 Ext_native_divs, Ext_native_spans))
5151import Text.Pandoc.Parsing hiding ((<|>))
5252import Text.Pandoc.Walk
@@ -54,46 +54,52 @@ import qualified Data.Map as M
5454import Data.Maybe ( fromMaybe, isJust)
5555import Data.List ( intercalate, isInfixOf, isPrefixOf )
5656import Data.Char ( isDigit )
57−import Control.Monad ( guard, when, mzero, void, unless )
57+import Control.Monad ( guard, mzero, void, unless )
5858import Control.Arrow ((***))
5959import Control.Applicative ( (<|>) )
6060import Data.Monoid (First (..))
6161import Text.Printf (printf)
62−import Debug.Trace (trace)
6362import Text.TeXMath (readMathML, writeTeX)
6463import Data.Default (Default (..), def)
65−import Control.Monad.Reader (Reader,ask, asks, local, runReader)
64+import Control.Monad.Reader (ask, asks, local, ReaderT, runReaderT, lift)
6665import Network.URI (URI, parseURIReference, nonStrictRelativeTo)
67−import Text.Pandoc.Error
6866import Text.Pandoc.CSS (foldOrElse, pickStyleAttrProps)
6967import Data.Monoid ((<>))
7068import Text.Parsec.Error
7169import qualified Data.Set as Set
70+import Text.Pandoc.Error
71+import Text.Pandoc.Class (PandocMonad, report)
72+import Control.Monad.Except (throwError)
73+
7274
7375-- | Convert HTML-formatted string to 'Pandoc' document.
74−readHtml :: ReaderOptions -- ^ Reader options
76+readHtml :: PandocMonad m
77+ => ReaderOptions -- ^ Reader options
7578 -> String -- ^ String to parse (assumes @'\n'@ line endings)
76− -> Either PandocError Pandoc
77−readHtml opts inp =
78− mapLeft (ParseFailure . getError) . flip runReader def $
79− runParserT parseDoc
80− (HTMLState def{ stateOptions = opts } [] Nothing Set.empty M.empty)
81− "source" tags
82− where tags = stripPrefixes . canonicalizeTags $
83− parseTagsOptions parseOptions{ optTagPosition = True } inp
84− parseDoc = do
85− blocks <- (fixPlains False) . mconcat <$> manyTill block eof
86− meta <- stateMeta . parserState <$> getState
87− bs' <- replaceNotes (B.toList blocks)
88− return $ Pandoc meta bs'
89− getError (errorMessages -> ms) = case ms of
90− [] -> ""
91− (m:_) -> messageString m
92−
93−replaceNotes :: [Block] -> TagParser [Block]
79+ -> m Pandoc
80+readHtml opts inp = do
81+ let tags = stripPrefixes . canonicalizeTags $
82+ parseTagsOptions parseOptions{ optTagPosition = True } inp
83+ parseDoc = do
84+ blocks <- (fixPlains False) . mconcat <$> manyTill block eof
85+ meta <- stateMeta . parserState <$> getState
86+ bs' <- replaceNotes (B.toList blocks)
87+ return $ Pandoc meta bs'
88+ getError (errorMessages -> ms) = case ms of
89+ [] -> ""
90+ (m:_) -> messageString m
91+ result <- flip runReaderT def $
92+ runParserT parseDoc
93+ (HTMLState def{ stateOptions = opts } [] Nothing Set.empty M.empty)
94+ "source" tags
95+ case result of
96+ Right doc -> return doc
97+ Left err -> throwError $ PandocParseError $ getError err
98+
99+replaceNotes :: PandocMonad m => [Block] -> TagParser m [Block]
94100replaceNotes = walkM replaceNotes'
95101
96−replaceNotes' :: Inline -> TagParser Inline
102+replaceNotes' :: PandocMonad m => Inline -> TagParser m Inline
97103replaceNotes' (RawInline (Format "noteref") ref) = maybe (Str "") (Note . B.toList) . lookup ref <$> getNotes
98104 where
99105 getNotes = noteTable <$> getState
@@ -113,20 +119,20 @@ data HTMLLocal = HTMLLocal { quoteContext :: QuoteContext
113119 , inPlain :: Bool -- ^ Set if in pPlain
114120 }
115121
116−setInChapter :: HTMLParser s a -> HTMLParser s a
122+setInChapter :: PandocMonad m => HTMLParser m s a -> HTMLParser m s a
117123setInChapter = local (\s -> s {inChapter = True})
118124
119−setInPlain :: HTMLParser s a -> HTMLParser s a
125+setInPlain :: PandocMonad m => HTMLParser m s a -> HTMLParser m s a
120126setInPlain = local (\s -> s {inPlain = True})
121127
122−type HTMLParser s = ParserT s HTMLState (Reader HTMLLocal)
128+type HTMLParser m s = ParserT s HTMLState (ReaderT HTMLLocal m)
123129
124−type TagParser = HTMLParser [Tag String]
130+type TagParser m = HTMLParser m [Tag String]
125131
126−pBody :: TagParser Blocks
132+pBody :: PandocMonad m => TagParser m Blocks
127133pBody = pInTags "body" block
128134
129−pHead :: TagParser Blocks
135+pHead :: PandocMonad m => TagParser m Blocks
130136pHead = pInTags "head" $ pTitle <|> pMetaTag <|> pBaseTag <|> (mempty <$ pAnyTag)
131137 where pTitle = pInTags "title" inline >>= setTitle . trimInlines
132138 setTitle t = mempty <$ (updateState $ B.setMeta "title" t)
@@ -149,9 +155,8 @@ pHead = pInTags "head" $ pTitle <|> pMetaTag <|> pBaseTag <|> (mempty <$ pAnyTag
149155 parseURIReference $ fromAttrib "href" bt }
150156 return mempty
151157
152−block :: TagParser Blocks
158+block :: PandocMonad m => TagParser m Blocks
153159block = do
154− tr <- getOption readerTrace
155160 pos <- getPosition
156161 res <- choice
157162 [ eSection
@@ -172,17 +177,20 @@ block = do
172177 , pPlain
173178 , pRawHtmlBlock
174179 ]
175− when tr $ trace (printf "line %d: %s" (sourceLine pos)
176− (take 60 $ show $ B.toList res)) (return ())
180+ report DEBUG $ printf "line %d: %s"
181+ (sourceLine pos) (take 60 $ show $ B.toList res)
177182 return res
178183
179−namespaces :: [(String, TagParser Inlines)]
184+namespaces :: PandocMonad m => [(String, TagParser m Inlines)]
180185namespaces = [(mathMLNamespace, pMath True)]
181186
182187mathMLNamespace :: String
183188mathMLNamespace = "http://www.w3.org/1998/Math/MathML"
184189
185−eSwitch :: Monoid a => (Inlines -> a) -> TagParser a -> TagParser a
190+eSwitch :: (PandocMonad m, Monoid a)
191+ => (Inlines -> a)
192+ -> TagParser m a
193+ -> TagParser m a
186194eSwitch constructor parser = try $ do
187195 guardEnabled Ext_epub_html_exts
188196 pSatisfy (~== TagOpen "switch" [])
@@ -195,7 +203,7 @@ eSwitch constructor parser = try $ do
195203 pSatisfy (~== TagClose "switch")
196204 return $ maybe fallback constructor cases
197205
198−eCase :: TagParser (Maybe Inlines)
206+eCase :: PandocMonad m => TagParser m (Maybe Inlines)
199207eCase = do
200208 skipMany pBlank
201209 TagOpen _ attr <- lookAhead $ pSatisfy $ (~== TagOpen "case" [])
@@ -203,7 +211,7 @@ eCase = do
203211 Just p -> Just <$> (pInTags "case" (skipMany pBlank *> p <* skipMany pBlank))
204212 Nothing -> Nothing <$ manyTill pAnyTag (pSatisfy (~== TagClose "case"))
205213
206−eFootnote :: TagParser ()
214+eFootnote :: PandocMonad m => TagParser m ()
207215eFootnote = try $ do
208216 let notes = ["footnote", "rearnote"]
209217 guardEnabled Ext_epub_html_exts
@@ -213,10 +221,10 @@ eFootnote = try $ do
213221 content <- pInTags tag block
214222 addNote ident content
215223
216−addNote :: String -> Blocks -> TagParser ()
224+addNote :: PandocMonad m => String -> Blocks -> TagParser m ()
217225addNote uid cont = updateState (\s -> s {noteTable = (uid, cont) : (noteTable s)})
218226
219−eNoteref :: TagParser Inlines
227+eNoteref :: PandocMonad m => TagParser m Inlines
220228eNoteref = try $ do
221229 guardEnabled Ext_epub_html_exts
222230 TagOpen tag attr <- lookAhead $ pAnyTag
@@ -227,17 +235,17 @@ eNoteref = try $ do
227235 return $ B.rawInline "noteref" ident
228236
229237-- Strip TOC if there is one, better to generate again
230−eTOC :: TagParser ()
238+eTOC :: PandocMonad m => TagParser m ()
231239eTOC = try $ do
232240 guardEnabled Ext_epub_html_exts
233241 (TagOpen tag attr) <- lookAhead $ pAnyTag
234242 guard (maybe False (== "toc") (lookup "type" attr))
235243 void (pInTags tag block)
236244
237−pList :: TagParser Blocks
245+pList :: PandocMonad m => TagParser m Blocks
238246pList = pBulletList <|> pOrderedList <|> pDefinitionList
239247
240−pBulletList :: TagParser Blocks
248+pBulletList :: PandocMonad m => TagParser m Blocks
241249pBulletList = try $ do
242250 pSatisfy (~== TagOpen "ul" [])
243251 let nonItem = pSatisfy (\t ->
@@ -249,7 +257,7 @@ pBulletList = try $ do
249257 items <- manyTill (pListItem nonItem) (pCloses "ul")
250258 return $ B.bulletList $ map (fixPlains True) items
251259
252−pListItem :: TagParser a -> TagParser Blocks
260+pListItem :: PandocMonad m => TagParser m a -> TagParser m Blocks
253261pListItem nonItem = do
254262 TagOpen _ attr <- lookAhead $ pSatisfy (~== TagOpen "li" [])
255263 let liDiv = maybe mempty (\x -> B.divWith (x, [], []) mempty) (lookup "id" attr)
@@ -271,7 +279,7 @@ parseTypeAttr "A" = UpperAlpha
271279parseTypeAttr "1" = Decimal
272280parseTypeAttr _ = DefaultStyle
273281
274−pOrderedList :: TagParser Blocks
282+pOrderedList :: PandocMonad m => TagParser m Blocks
275283pOrderedList = try $ do
276284 TagOpen _ attribs <- pSatisfy (~== TagOpen "ol" [])
277285 let (start, style) = (sta', sty')
@@ -302,13 +310,13 @@ pOrderedList = try $ do
302310 items <- manyTill (pListItem nonItem) (pCloses "ol")
303311 return $ B.orderedListWith (start, style, DefaultDelim) $ map (fixPlains True) items
304312
305−pDefinitionList :: TagParser Blocks
313+pDefinitionList :: PandocMonad m => TagParser m Blocks
306314pDefinitionList = try $ do
307315 pSatisfy (~== TagOpen "dl" [])
308316 items <- manyTill pDefListItem (pCloses "dl")
309317 return $ B.definitionList items
310318
311−pDefListItem :: TagParser (Inlines, [Blocks])
319+pDefListItem :: PandocMonad m => TagParser m (Inlines, [Blocks])
312320pDefListItem = try $ do
313321 let nonItem = pSatisfy (\t -> not (t ~== TagOpen "dt" []) &&
314322 not (t ~== TagOpen "dd" []) && not (t ~== TagClose "dl"))
@@ -334,7 +342,7 @@ fixPlains inList bs = if any isParaish bs'
334342 plainToPara x = x
335343 bs' = B.toList bs
336344
337−pRawTag :: TagParser String
345+pRawTag :: PandocMonad m => TagParser m String
338346pRawTag = do
339347 tag <- pAnyTag
340348 let ignorable x = x `elem` ["html","head","body","!DOCTYPE","?xml"]
@@ -342,7 +350,7 @@ pRawTag = do
342350 then return []
343351 else return $ renderTags' [tag]
344352
345−pDiv :: TagParser Blocks
353+pDiv :: PandocMonad m => TagParser m Blocks
346354pDiv = try $ do
347355 guardEnabled Ext_native_divs
348356 let isDivLike "div" = True
@@ -356,7 +364,7 @@ pDiv = try $ do
356364 else classes
357365 return $ B.divWith (ident, classes', kvs) contents
358366
359−pRawHtmlBlock :: TagParser Blocks
367+pRawHtmlBlock :: PandocMonad m => TagParser m Blocks
360368pRawHtmlBlock = do
361369 raw <- pHtmlBlock "script" <|> pHtmlBlock "style" <|> pRawTag
362370 parseRaw <- getOption readerParseRaw
@@ -364,21 +372,21 @@ pRawHtmlBlock = do
364372 then return $ B.rawBlock "html" raw
365373 else return mempty
366374
367−pHtmlBlock :: String -> TagParser String
375+pHtmlBlock :: PandocMonad m => String -> TagParser m String
368376pHtmlBlock t = try $ do
369377 open <- pSatisfy (~== TagOpen t [])
370378 contents <- manyTill pAnyTag (pSatisfy (~== TagClose t))
371379 return $ renderTags' $ [open] ++ contents ++ [TagClose t]
372380
373381-- Sets chapter context
374−eSection :: TagParser Blocks
382+eSection :: PandocMonad m => TagParser m Blocks
375383eSection = try $ do
376384 let matchChapter as = maybe False (isInfixOf "chapter") (lookup "type" as)
377385 let sectTag = tagOpen (`elem` sectioningContent) matchChapter
378386 TagOpen tag _ <- lookAhead $ pSatisfy sectTag
379387 setInChapter (pInTags tag block)
380388
381−headerLevel :: String -> TagParser Int
389+headerLevel :: PandocMonad m => String -> TagParser m Int
382390headerLevel tagtype = do
383391 let level = read (drop 1 tagtype)
384392 (try $ do
@@ -388,7 +396,7 @@ headerLevel tagtype = do
388396 <|>
389397 return level
390398
391−eTitlePage :: TagParser ()
399+eTitlePage :: PandocMonad m => TagParser m ()
392400eTitlePage = try $ do
393401 let isTitlePage as = maybe False (isInfixOf "titlepage") (lookup "type" as)
394402 let groupTag = tagOpen (\x -> x `elem` groupingContent || x == "section")
@@ -396,7 +404,7 @@ eTitlePage = try $ do
396404 TagOpen tag _ <- lookAhead $ pSatisfy groupTag
397405 () <$ pInTags tag block
398406
399−pHeader :: TagParser Blocks
407+pHeader :: PandocMonad m => TagParser m Blocks
400408pHeader = try $ do
401409 TagOpen tagtype attr <- pSatisfy $
402410 tagOpen (`elem` ["h1","h2","h3","h4","h5","h6"])
@@ -412,12 +420,12 @@ pHeader = try $ do
412420 then mempty -- skip a representation of the title in the body
413421 else B.headerWith attr' level contents
414422
415−pHrule :: TagParser Blocks
423+pHrule :: PandocMonad m => TagParser m Blocks
416424pHrule = do
417425 pSelfClosing (=="hr") (const True)
418426 return B.horizontalRule
419427
420−pTable :: TagParser Blocks
428+pTable :: PandocMonad m => TagParser m Blocks
421429pTable = try $ do
422430 TagOpen _ _ <- pSatisfy (~== TagOpen "table" [])
423431 skipMany pBlank
@@ -456,7 +464,7 @@ pTable = try $ do
456464 else widths'
457465 return $ B.table caption (zip aligns widths) head' rows
458466
459−pCol :: TagParser Double
467+pCol :: PandocMonad m => TagParser m Double
460468pCol = try $ do
461469 TagOpen _ attribs <- pSatisfy (~== TagOpen "col" [])
462470 skipMany pBlank
@@ -472,7 +480,7 @@ pCol = try $ do
472480 fromMaybe 0.0 $ safeRead ('0':'.':init x)
473481 _ -> 0.0
474482
475−pColgroup :: TagParser [Double]
483+pColgroup :: PandocMonad m => TagParser m [Double]
476484pColgroup = try $ do
477485 pSatisfy (~== TagOpen "colgroup" [])
478486 skipMany pBlank
@@ -485,31 +493,31 @@ noColOrRowSpans t = isNullOrOne "colspan" && isNullOrOne "rowspan"
485493 "1" -> True
486494 _ -> False
487495
488−pCell :: String -> TagParser [Blocks]
496+pCell :: PandocMonad m => String -> TagParser m [Blocks]
489497pCell celltype = try $ do
490498 skipMany pBlank
491499 res <- pInTags' celltype noColOrRowSpans block
492500 skipMany pBlank
493501 return [res]
494502
495−pBlockQuote :: TagParser Blocks
503+pBlockQuote :: PandocMonad m => TagParser m Blocks
496504pBlockQuote = do
497505 contents <- pInTags "blockquote" block
498506 return $ B.blockQuote $ fixPlains False contents
499507
500−pPlain :: TagParser Blocks
508+pPlain :: PandocMonad m => TagParser m Blocks
501509pPlain = do
502510 contents <- setInPlain $ trimInlines . mconcat <$> many1 inline
503511 if B.isNull contents
504512 then return mempty
505513 else return $ B.plain contents
506514
507−pPara :: TagParser Blocks
515+pPara :: PandocMonad m => TagParser m Blocks
508516pPara = do
509517 contents <- trimInlines <$> pInTags "p" inline
510518 return $ B.para contents
511519
512−pCodeBlock :: TagParser Blocks
520+pCodeBlock :: PandocMonad m => TagParser m Blocks
513521pCodeBlock = try $ do
514522 TagOpen _ attr <- pSatisfy (~== TagOpen "pre" [])
515523 contents <- manyTill pAnyTag (pCloses "pre" <|> eof)
@@ -529,7 +537,7 @@ tagToString (TagText s) = s
529537tagToString (TagOpen "br" _) = "\n"
530538tagToString _ = ""
531539
532−inline :: TagParser Inlines
540+inline :: PandocMonad m => TagParser m Inlines
533541inline = choice
534542 [ eNoteref
535543 , eSwitch id inline
@@ -549,30 +557,31 @@ inline = choice
549557 , pRawHtmlInline
550558 ]
551559
552−pLocation :: TagParser ()
560+pLocation :: PandocMonad m => TagParser m ()
553561pLocation = do
554562 (TagPosition r c) <- pSat isTagPosition
555563 setPosition $ newPos "input" r c
556564
557−pSat :: (Tag String -> Bool) -> TagParser (Tag String)
565+pSat :: PandocMonad m => (Tag String -> Bool) -> TagParser m (Tag String)
558566pSat f = do
559567 pos <- getPosition
560568 token show (const pos) (\x -> if f x then Just x else Nothing)
561569
562−pSatisfy :: (Tag String -> Bool) -> TagParser (Tag String)
570+pSatisfy :: PandocMonad m => (Tag String -> Bool) -> TagParser m (Tag String)
563571pSatisfy f = try $ optional pLocation >> pSat f
564572
565−pAnyTag :: TagParser (Tag String)
573+pAnyTag :: PandocMonad m => TagParser m (Tag String)
566574pAnyTag = pSatisfy (const True)
567575
568−pSelfClosing :: (String -> Bool) -> ([Attribute String] -> Bool)
569− -> TagParser (Tag String)
576+pSelfClosing :: PandocMonad m
577+ => (String -> Bool) -> ([Attribute String] -> Bool)
578+ -> TagParser m (Tag String)
570579pSelfClosing f g = do
571580 open <- pSatisfy (tagOpen f g)
572581 optional $ pSatisfy (tagClose f)
573582 return open
574583
575−pQ :: TagParser Inlines
584+pQ :: PandocMonad m => TagParser m Inlines
576585pQ = do
577586 context <- asks quoteContext
578587 let quoteType = case context of
@@ -587,19 +596,19 @@ pQ = do
587596 withQuoteContext innerQuoteContext $
588597 pInlinesInTags "q" constructor
589598
590−pEmph :: TagParser Inlines
599+pEmph :: PandocMonad m => TagParser m Inlines
591600pEmph = pInlinesInTags "em" B.emph <|> pInlinesInTags "i" B.emph
592601
593−pStrong :: TagParser Inlines
602+pStrong :: PandocMonad m => TagParser m Inlines
594603pStrong = pInlinesInTags "strong" B.strong <|> pInlinesInTags "b" B.strong
595604
596−pSuperscript :: TagParser Inlines
605+pSuperscript :: PandocMonad m => TagParser m Inlines
597606pSuperscript = pInlinesInTags "sup" B.superscript
598607
599−pSubscript :: TagParser Inlines
608+pSubscript :: PandocMonad m => TagParser m Inlines
600609pSubscript = pInlinesInTags "sub" B.subscript
601610
602−pStrikeout :: TagParser Inlines
611+pStrikeout :: PandocMonad m => TagParser m Inlines
603612pStrikeout = do
604613 pInlinesInTags "s" B.strikeout <|>
605614 pInlinesInTags "strike" B.strikeout <|>
@@ -608,7 +617,7 @@ pStrikeout = do
608617 contents <- mconcat <$> manyTill inline (pCloses "span")
609618 return $ B.strikeout contents)
610619
611−pLineBreak :: TagParser Inlines
620+pLineBreak :: PandocMonad m => TagParser m Inlines
612621pLineBreak = do
613622 pSelfClosing (=="br") (const True)
614623 return B.linebreak
@@ -619,7 +628,7 @@ maybeFromAttrib :: String -> Tag String -> Maybe String
619628maybeFromAttrib name (TagOpen _ attrs) = lookup name attrs
620629maybeFromAttrib _ _ = Nothing
621630
622−pLink :: TagParser Inlines
631+pLink :: PandocMonad m => TagParser m Inlines
623632pLink = try $ do
624633 tag <- pSatisfy $ tagOpenLit "a" (const True)
625634 let title = fromAttrib "title" tag
@@ -639,7 +648,7 @@ pLink = try $ do
639648 _ -> url'
640649 return $ B.linkWith (uid, cls, []) (escapeURI url) title lab
641650
642−pImage :: TagParser Inlines
651+pImage :: PandocMonad m => TagParser m Inlines
643652pImage = do
644653 tag <- pSelfClosing (=="img") (isJust . lookup "src")
645654 mbBaseHref <- baseHref <$> getState
@@ -657,13 +666,13 @@ pImage = do
657666 let kvs = concat $ map getAtt ["width", "height", "sizes", "srcset"]
658667 return $ B.imageWith (uid, cls, kvs) (escapeURI url) title (B.text alt)
659668
660−pCode :: TagParser Inlines
669+pCode :: PandocMonad m => TagParser m Inlines
661670pCode = try $ do
662671 (TagOpen open attr) <- pSatisfy $ tagOpen (`elem` ["code","tt"]) (const True)
663672 result <- manyTill pAnyTag (pCloses open)
664673 return $ B.codeWith (mkAttr attr) $ intercalate " " $ lines $ innerText result
665674
666−pSpan :: TagParser Inlines
675+pSpan :: PandocMonad m => TagParser m Inlines
667676pSpan = try $ do
668677 guardEnabled Ext_native_spans
669678 TagOpen _ attr <- lookAhead $ pSatisfy $ tagOpen (=="span") (const True)
@@ -674,7 +683,7 @@ pSpan = try $ do
674683 let tag = if isSmallCaps then B.smallcaps else B.spanWith (mkAttr attr)
675684 return $ tag contents
676685
677−pRawHtmlInline :: TagParser Inlines
686+pRawHtmlInline :: PandocMonad m => TagParser m Inlines
678687pRawHtmlInline = do
679688 inplain <- asks inPlain
680689 result <- pSatisfy (tagComment (const True))
@@ -689,7 +698,7 @@ pRawHtmlInline = do
689698mathMLToTeXMath :: String -> Either String String
690699mathMLToTeXMath s = writeTeX <$> readMathML s
691700
692−pMath :: Bool -> TagParser Inlines
701+pMath :: PandocMonad m => Bool -> TagParser m Inlines
693702pMath inCase = try $ do
694703 open@(TagOpen _ attr) <- pSatisfy $ tagOpen (=="math") (const True)
695704 -- we'll assume math tags are MathML unless specially marked
@@ -705,22 +714,25 @@ pMath inCase = try $ do
705714 Just "block" -> B.displayMath x
706715 _ -> B.math x
707716
708−pInlinesInTags :: String -> (Inlines -> Inlines)
709− -> TagParser Inlines
717+pInlinesInTags :: PandocMonad m => String -> (Inlines -> Inlines)
718+ -> TagParser m Inlines
710719pInlinesInTags tagtype f = extractSpaces f <$> pInTags tagtype inline
711720
712−pInTags :: (Monoid a) => String -> TagParser a -> TagParser a
721+pInTags :: (PandocMonad m, Monoid a) => String -> TagParser m a -> TagParser m a
713722pInTags tagtype parser = pInTags' tagtype (const True) parser
714723
715−pInTags' :: (Monoid a) => String -> (Tag String -> Bool) -> TagParser a
716− -> TagParser a
724+pInTags' :: (PandocMonad m, Monoid a)
725+ => String
726+ -> (Tag String -> Bool)
727+ -> TagParser m a
728+ -> TagParser m a
717729pInTags' tagtype tagtest parser = try $ do
718730 pSatisfy (\t -> t ~== TagOpen tagtype [] && tagtest t)
719731 mconcat <$> manyTill parser (pCloses tagtype <|> eof)
720732
721733-- parses p, preceeded by an optional opening tag
722734-- and followed by an optional closing tags
723−pOptInTag :: String -> TagParser a -> TagParser a
735+pOptInTag :: PandocMonad m => String -> TagParser m a -> TagParser m a
724736pOptInTag tagtype p = try $ do
725737 skipMany pBlank
726738 optional $ pSatisfy (~== TagOpen tagtype [])
@@ -731,7 +743,7 @@ pOptInTag tagtype p = try $ do
731743 skipMany pBlank
732744 return x
733745
734−pCloses :: String -> TagParser ()
746+pCloses :: PandocMonad m => String -> TagParser m ()
735747pCloses tagtype = try $ do
736748 t <- lookAhead $ pSatisfy $ \tag -> isTagClose tag || isTagOpen tag
737749 case t of
@@ -744,23 +756,25 @@ pCloses tagtype = try $ do
744756 (TagClose "table") | tagtype == "tr" -> return ()
745757 _ -> mzero
746758
747−pTagText :: TagParser Inlines
759+pTagText :: PandocMonad m => TagParser m Inlines
748760pTagText = try $ do
749761 (TagText str) <- pSatisfy isTagText
750762 st <- getState
751763 qu <- ask
752− case flip runReader qu $ runParserT (many pTagContents) st "text" str of
753− Left _ -> fail $ "Could not parse `" ++ str ++ "'"
764+ parsed <- lift $ lift $
765+ flip runReaderT qu $ runParserT (many pTagContents) st "text" str
766+ case parsed of
767+ Left _ -> throwError $ PandocParseError $ "Could not parse `" ++ str ++ "'"
754768 Right result -> return $ mconcat result
755769
756−pBlank :: TagParser ()
770+pBlank :: PandocMonad m => TagParser m ()
757771pBlank = try $ do
758772 (TagText str) <- pSatisfy isTagText
759773 guard $ all isSpace str
760774
761−type InlinesParser = HTMLParser String
775+type InlinesParser m = HTMLParser m String
762776
763−pTagContents :: InlinesParser Inlines
777+pTagContents :: PandocMonad m => InlinesParser m Inlines
764778pTagContents =
765779 B.displayMath <$> mathDisplay
766780 <|> B.math <$> mathInline
@@ -770,7 +784,7 @@ pTagContents =
770784 <|> pSymbol
771785 <|> pBad
772786
773−pStr :: InlinesParser Inlines
787+pStr :: PandocMonad m => InlinesParser m Inlines
774788pStr = do
775789 result <- many1 $ satisfy $ \c ->
776790 not (isSpace c) && not (isSpecial c) && not (isBad c)
@@ -789,13 +803,13 @@ isSpecial '\8220' = True
789803isSpecial '\8221' = True
790804isSpecial _ = False
791805
792−pSymbol :: InlinesParser Inlines
806+pSymbol :: PandocMonad m => InlinesParser m Inlines
793807pSymbol = satisfy isSpecial >>= return . B.str . (:[])
794808
795809isBad :: Char -> Bool
796810isBad c = c >= '\128' && c <= '\159' -- not allowed in HTML
797811
798−pBad :: InlinesParser Inlines
812+pBad :: PandocMonad m => InlinesParser m Inlines
799813pBad = do
800814 c <- satisfy isBad
801815 let c' = case c of
@@ -829,7 +843,7 @@ pBad = do
829843 _ -> '?'
830844 return $ B.str [c']
831845
832−pSpace :: InlinesParser Inlines
846+pSpace :: PandocMonad m => InlinesParser m Inlines
833847pSpace = many1 (satisfy isSpace) >>= \xs ->
834848 if '\n' `elem` xs
835849 then return B.softbreak
@@ -1070,7 +1084,7 @@ instance HasHeaderMap HTMLState where
10701084
10711085-- This signature should be more general
10721086-- MonadReader HTMLLocal m => HasQuoteContext st m
1073−instance HasQuoteContext st (Reader HTMLLocal) where
1087+instance PandocMonad m => HasQuoteContext st (ReaderT HTMLLocal m) where
10741088 getQuoteContext = asks quoteContext
10751089 withQuoteContext q = local (\s -> s{quoteContext = q})
10761090
src/Text/Pandoc/Readers/Haddock.hs+17−11
@@ -24,23 +24,29 @@ import Text.Pandoc.Definition
2424import Text.Pandoc.Options
2525import Documentation.Haddock.Parser
2626import Documentation.Haddock.Types
27−import Debug.Trace (trace)
28−
2927import Text.Pandoc.Error
28+import Control.Monad.Except (throwError)
29+import Text.Pandoc.Class (PandocMonad)
30+
3031
3132-- | Parse Haddock markup and return a 'Pandoc' document.
32−readHaddock :: ReaderOptions -- ^ Reader options
33− -> String -- ^ String to parse
34− -> Either PandocError Pandoc
35−readHaddock opts =
33+readHaddock :: PandocMonad m
34+ => ReaderOptions
35+ -> String
36+ -> m Pandoc
37+readHaddock opts s = case readHaddockEither opts s of
38+ Right result -> return result
39+ Left e -> throwError e
40+
41+readHaddockEither :: ReaderOptions -- ^ Reader options
42+ -> String -- ^ String to parse
43+ -> Either PandocError Pandoc
44+readHaddockEither _opts =
3645#if MIN_VERSION_haddock_library(1,2,0)
37− Right . B.doc . docHToBlocks . trace' . _doc . parseParas
46+ Right . B.doc . docHToBlocks . _doc . parseParas
3847#else
39− Right . B.doc . docHToBlocks . trace' . parseParas
48+ Right . B.doc . docHToBlocks . parseParas
4049#endif
41− where trace' x = if readerTrace opts
42− then trace (show x) x
43− else x
4450
4551docHToBlocks :: DocH String Identifier -> Blocks
4652docHToBlocks d' =
src/Text/Pandoc/Readers/LaTeX.hs+146−211
@@ -32,7 +32,6 @@ module Text.Pandoc.Readers.LaTeX ( readLaTeX,
3232 rawLaTeXInline,
3333 rawLaTeXBlock,
3434 inlineCommand,
35− handleIncludes
3635 ) where
3736
3837import Text.Pandoc.Definition
@@ -48,22 +47,28 @@ import Control.Monad
4847import Text.Pandoc.Builder
4948import Control.Applicative ((<|>), many, optional)
5049import Data.Maybe (fromMaybe, maybeToList)
51−import System.Environment (getEnv)
5250import System.FilePath (replaceExtension, (</>), takeExtension, addExtension)
5351import Data.List (intercalate)
5452import qualified Data.Map as M
55−import qualified Control.Exception as E
5653import Text.Pandoc.Highlighting (fromListingsLanguage)
5754import Text.Pandoc.ImageSize (numUnit, showFl)
5855import Text.Pandoc.Error
56+import Control.Monad.Except (throwError, catchError)
57+import Text.Pandoc.Class (PandocMonad, PandocPure, lookupEnv, readFileLazy,
58+ warning, warningWithPos)
5959
6060-- | Parse LaTeX from string and return 'Pandoc' document.
61−readLaTeX :: ReaderOptions -- ^ Reader options
61+readLaTeX :: PandocMonad m
62+ => ReaderOptions -- ^ Reader options
6263 -> String -- ^ String to parse (assumes @'\n'@ line endings)
63− -> Either PandocError Pandoc
64−readLaTeX opts = readWith parseLaTeX def{ stateOptions = opts }
65−
66−parseLaTeX :: LP Pandoc
64+ -> m Pandoc
65+readLaTeX opts ltx = do
66+ parsed <- readWithM parseLaTeX def{ stateOptions = opts } ltx
67+ case parsed of
68+ Right result -> return result
69+ Left e -> throwError e
70+
71+parseLaTeX :: PandocMonad m => LP m Pandoc
6772parseLaTeX = do
6873 bs <- blocks
6974 eof
@@ -72,9 +77,9 @@ parseLaTeX = do
7277 let (Pandoc _ bs') = doc bs
7378 return $ Pandoc meta bs'
7479
75−type LP = Parser String ParserState
80+type LP m = ParserT String ParserState m
7681
77−anyControlSeq :: LP String
82+anyControlSeq :: PandocMonad m => LP m String
7883anyControlSeq = do
7984 char '\\'
8085 next <- option '\n' anyChar
@@ -83,7 +88,7 @@ anyControlSeq = do
8388 c | isLetter c -> (c:) <$> (many letter <* optional sp)
8489 | otherwise -> return [c]
8590
86−controlSeq :: String -> LP String
91+controlSeq :: PandocMonad m => String -> LP m String
8792controlSeq name = try $ do
8893 char '\\'
8994 case name of
@@ -92,26 +97,26 @@ controlSeq name = try $ do
9297 cs -> string cs <* notFollowedBy letter <* optional sp
9398 return name
9499
95−dimenarg :: LP String
100+dimenarg :: PandocMonad m => LP m String
96101dimenarg = try $ do
97102 ch <- option "" $ string "="
98103 num <- many1 digit
99104 dim <- oneOfStrings ["pt","pc","in","bp","cm","mm","dd","cc","sp"]
100105 return $ ch ++ num ++ dim
101106
102−sp :: LP ()
107+sp :: PandocMonad m => LP m ()
103108sp = whitespace <|> endline
104109
105−whitespace :: LP ()
110+whitespace :: PandocMonad m => LP m ()
106111whitespace = skipMany1 $ satisfy (\c -> c == ' ' || c == '\t')
107112
108−endline :: LP ()
113+endline :: PandocMonad m => LP m ()
109114endline = try (newline >> lookAhead anyChar >> notFollowedBy blankline)
110115
111116isLowerHex :: Char -> Bool
112117isLowerHex x = x >= '0' && x <= '9' || x >= 'a' && x <= 'f'
113118
114−tildeEscape :: LP Char
119+tildeEscape :: PandocMonad m => LP m Char
115120tildeEscape = try $ do
116121 string "^^"
117122 c <- satisfy (\x -> x >= '\0' && x <= '\128')
@@ -124,29 +129,29 @@ tildeEscape = try $ do
124129 | otherwise -> return $ chr (x + 64)
125130 else return $ chr $ read ('0':'x':c:d)
126131
127−comment :: LP ()
132+comment :: PandocMonad m => LP m ()
128133comment = do
129134 char '%'
130135 skipMany (satisfy (/='\n'))
131136 optional newline
132137 return ()
133138
134−bgroup :: LP ()
139+bgroup :: PandocMonad m => LP m ()
135140bgroup = try $ do
136141 skipMany (spaceChar <|> try (newline <* notFollowedBy blankline))
137142 () <$ char '{'
138143 <|> () <$ controlSeq "bgroup"
139144 <|> () <$ controlSeq "begingroup"
140145
141−egroup :: LP ()
146+egroup :: PandocMonad m => LP m ()
142147egroup = () <$ char '}'
143148 <|> () <$ controlSeq "egroup"
144149 <|> () <$ controlSeq "endgroup"
145150
146−grouped :: Monoid a => LP a -> LP a
151+grouped :: PandocMonad m => Monoid a => LP m a -> LP m a
147152grouped parser = try $ bgroup *> (mconcat <$> manyTill parser egroup)
148153
149−braced :: LP String
154+braced :: PandocMonad m => LP m String
150155braced = bgroup *> (concat <$> manyTill
151156 ( many1 (satisfy (\c -> c /= '\\' && c /= '}' && c /= '{'))
152157 <|> try (string "\\}")
@@ -156,16 +161,16 @@ braced = bgroup *> (concat <$> manyTill
156161 <|> count 1 anyChar
157162 ) egroup)
158163
159−bracketed :: Monoid a => LP a -> LP a
164+bracketed :: PandocMonad m => Monoid a => LP m a -> LP m a
160165bracketed parser = try $ char '[' *> (mconcat <$> manyTill parser (char ']'))
161166
162−mathDisplay :: LP String -> LP Inlines
167+mathDisplay :: PandocMonad m => LP m String -> LP m Inlines
163168mathDisplay p = displayMath <$> (try p >>= applyMacros' . trim)
164169
165−mathInline :: LP String -> LP Inlines
170+mathInline :: PandocMonad m => LP m String -> LP m Inlines
166171mathInline p = math <$> (try p >>= applyMacros')
167172
168−mathChars :: LP String
173+mathChars :: PandocMonad m => LP m String
169174mathChars =
170175 concat <$> many (escapedChar
171176 <|> (snd <$> withRaw braced)
@@ -179,10 +184,10 @@ mathChars =
179184 isOrdChar '\\' = False
180185 isOrdChar _ = True
181186
182−quoted' :: (Inlines -> Inlines) -> LP String -> LP () -> LP Inlines
187+quoted' :: PandocMonad m => (Inlines -> Inlines) -> LP m String -> LP m () -> LP m Inlines
183188quoted' f starter ender = do
184189 startchs <- starter
185− smart <- getOption readerSmart
190+ smart <- extensionEnabled Ext_smart <$> getOption readerExtensions
186191 if smart
187192 then do
188193 ils <- many (notFollowedBy ender >> inline)
@@ -194,7 +199,7 @@ quoted' f starter ender = do
194199 _ -> startchs)
195200 else lit startchs
196201
197−doubleQuote :: LP Inlines
202+doubleQuote :: PandocMonad m => LP m Inlines
198203doubleQuote = do
199204 quoted' doubleQuoted (try $ string "``") (void $ try $ string "''")
200205 <|> quoted' doubleQuoted (string "“") (void $ char '”')
@@ -202,15 +207,15 @@ doubleQuote = do
202207 <|> quoted' doubleQuoted (try $ string "\"`") (void $ try $ string "\"'")
203208 <|> quoted' doubleQuoted (string "\"") (void $ char '"')
204209
205−singleQuote :: LP Inlines
210+singleQuote :: PandocMonad m => LP m Inlines
206211singleQuote = do
207− smart <- getOption readerSmart
212+ smart <- extensionEnabled Ext_smart <$> getOption readerExtensions
208213 if smart
209214 then quoted' singleQuoted (string "`") (try $ char '\'' >> notFollowedBy letter)
210215 <|> quoted' singleQuoted (string "‘") (try $ char '’' >> notFollowedBy letter)
211216 else str <$> many1 (oneOf "`\'‘’")
212217
213−inline :: LP Inlines
218+inline :: PandocMonad m => LP m Inlines
214219inline = (mempty <$ comment)
215220 <|> (space <$ whitespace)
216221 <|> (softbreak <$ endline)
@@ -231,14 +236,15 @@ inline = (mempty <$ comment)
231236 <|> mathInline (char '$' *> mathChars <* char '$')
232237 <|> (guardEnabled Ext_literate_haskell *> char '|' *> doLHSverb)
233238 <|> (str . (:[]) <$> tildeEscape)
234− <|> (str . (:[]) <$> oneOf "[]")
235− <|> (str . (:[]) <$> oneOf "#&~^'`\"[]") -- TODO print warning?
236− -- <|> (str <$> count 1 (satisfy (\c -> c /= '\\' && c /='\n' && c /='}' && c /='{'))) -- eat random leftover characters
239+ <|> (do res <- oneOf "#&~^'`\"[]"
240+ pos <- getPosition
241+ warningWithPos pos ("Parsing unescaped '" ++ [res] ++ "'")
242+ return $ str [res])
237243
238−inlines :: LP Inlines
244+inlines :: PandocMonad m => LP m Inlines
239245inlines = mconcat <$> many (notFollowedBy (char '}') *> inline)
240246
241−inlineGroup :: LP Inlines
247+inlineGroup :: PandocMonad m => LP m Inlines
242248inlineGroup = do
243249 ils <- grouped inline
244250 if isNull ils
@@ -247,10 +253,11 @@ inlineGroup = do
247253 -- we need the span so we can detitlecase bibtex entries;
248254 -- we need to know when something is {C}apitalized
249255
250−block :: LP Blocks
256+block :: PandocMonad m => LP m Blocks
251257block = (mempty <$ comment)
252258 <|> (mempty <$ ((spaceChar <|> newline) *> spaces))
253259 <|> environment
260+ <|> include
254261 <|> macro
255262 <|> blockCommand
256263 <|> paragraph
@@ -258,10 +265,10 @@ block = (mempty <$ comment)
258265 <|> (mempty <$ char '&') -- loose & in table environment
259266
260267
261−blocks :: LP Blocks
268+blocks :: PandocMonad m => LP m Blocks
262269blocks = mconcat <$> many block
263270
264−getRawCommand :: String -> LP String
271+getRawCommand :: PandocMonad m => String -> LP m String
265272getRawCommand name' = do
266273 rawargs <- withRaw (many (try (optional sp *> opt)) *>
267274 option "" (try (optional sp *> dimenarg)) *>
@@ -273,7 +280,7 @@ lookupListDefault d = (fromMaybe d .) . lookupList
273280 where
274281 lookupList l m = msum $ map (`M.lookup` m) l
275282
276−blockCommand :: LP Blocks
283+blockCommand :: PandocMonad m => LP m Blocks
277284blockCommand = try $ do
278285 name <- anyControlSeq
279286 guard $ name /= "begin" && name /= "end"
@@ -291,21 +298,21 @@ inBrackets :: Inlines -> Inlines
291298inBrackets x = str "[" <> x <> str "]"
292299
293300-- eat an optional argument and one or more arguments in braces
294−ignoreInlines :: String -> (String, LP Inlines)
301+ignoreInlines :: PandocMonad m => String -> (String, LP m Inlines)
295302ignoreInlines name = (name, doraw <|> (mempty <$ optargs))
296303 where optargs = skipopts *> skipMany (try $ optional sp *> braced)
297304 contseq = '\\':name
298305 doraw = (rawInline "latex" . (contseq ++) . snd) <$>
299306 (getOption readerParseRaw >>= guard >> withRaw optargs)
300307
301−ignoreBlocks :: String -> (String, LP Blocks)
308+ignoreBlocks :: PandocMonad m => String -> (String, LP m Blocks)
302309ignoreBlocks name = (name, doraw <|> (mempty <$ optargs))
303310 where optargs = skipopts *> skipMany (try $ optional sp *> braced)
304311 contseq = '\\':name
305312 doraw = (rawBlock "latex" . (contseq ++) . snd) <$>
306313 (getOption readerParseRaw >>= guard >> withRaw optargs)
307314
308−blockCommands :: M.Map String (LP Blocks)
315+blockCommands :: PandocMonad m => M.Map String (LP m Blocks)
309316blockCommands = M.fromList $
310317 [ ("par", mempty <$ skipopts)
311318 , ("title", mempty <$ (skipopts *>
@@ -346,8 +353,6 @@ blockCommands = M.fromList $
346353 , ("documentclass", skipopts *> braced *> preamble)
347354 , ("centerline", (para . trimInlines) <$> (skipopts *> tok))
348355 , ("caption", skipopts *> setCaption)
349− , ("PandocStartInclude", startInclude)
350− , ("PandocEndInclude", endInclude)
351356 , ("bibliography", mempty <$ (skipopts *> braced >>=
352357 addMeta "bibliography" . splitBibs))
353358 , ("addbibresource", mempty <$ (skipopts *> braced >>=
@@ -370,14 +375,14 @@ blockCommands = M.fromList $
370375 , "newpage"
371376 ]
372377
373−addMeta :: ToMetaValue a => String -> a -> LP ()
378+addMeta :: PandocMonad m => ToMetaValue a => String -> a -> LP m ()
374379addMeta field val = updateState $ \st ->
375380 st{ stateMeta = addMetaField field val $ stateMeta st }
376381
377382splitBibs :: String -> [Inlines]
378383splitBibs = map (str . flip replaceExtension "bib" . trim) . splitBy (==',')
379384
380−setCaption :: LP Blocks
385+setCaption :: PandocMonad m => LP m Blocks
381386setCaption = do
382387 ils <- tok
383388 mblabel <- option Nothing $
@@ -389,10 +394,10 @@ setCaption = do
389394 updateState $ \st -> st{ stateCaption = Just ils' }
390395 return mempty
391396
392−resetCaption :: LP ()
397+resetCaption :: PandocMonad m => LP m ()
393398resetCaption = updateState $ \st -> st{ stateCaption = Nothing }
394399
395−authors :: LP ()
400+authors :: PandocMonad m => LP m ()
396401authors = try $ do
397402 char '{'
398403 let oneAuthor = mconcat <$>
@@ -403,7 +408,7 @@ authors = try $ do
403408 char '}'
404409 addMeta "author" (map trimInlines auths)
405410
406−section :: Attr -> Int -> LP Blocks
411+section :: PandocMonad m => Attr -> Int -> LP m Blocks
407412section (ident, classes, kvs) lvl = do
408413 hasChapters <- stateHasChapters `fmap` getState
409414 let lvl' = if hasChapters then lvl + 1 else lvl
@@ -413,7 +418,7 @@ section (ident, classes, kvs) lvl = do
413418 attr' <- registerHeader (lab, classes, kvs) contents
414419 return $ headerWith attr' lvl' contents
415420
416−inlineCommand :: LP Inlines
421+inlineCommand :: PandocMonad m => LP m Inlines
417422inlineCommand = try $ do
418423 name <- anyControlSeq
419424 guard $ name /= "begin" && name /= "end"
@@ -435,14 +440,14 @@ inlineCommand = try $ do
435440 optional (try (string "{}")))
436441 <|> raw
437442
438−unlessParseRaw :: LP ()
443+unlessParseRaw :: PandocMonad m => LP m ()
439444unlessParseRaw = getOption readerParseRaw >>= guard . not
440445
441446isBlockCommand :: String -> Bool
442−isBlockCommand s = s `M.member` blockCommands
447+isBlockCommand s = s `M.member` (blockCommands :: M.Map String (LP PandocPure Blocks))
443448
444449
445−inlineEnvironments :: M.Map String (LP Inlines)
450+inlineEnvironments :: PandocMonad m => M.Map String (LP m Inlines)
446451inlineEnvironments = M.fromList
447452 [ ("displaymath", mathEnv id Nothing "displaymath")
448453 , ("math", math <$> verbEnv "math")
@@ -460,7 +465,7 @@ inlineEnvironments = M.fromList
460465 , ("alignat*", mathEnv id (Just "aligned") "alignat*")
461466 ]
462467
463−inlineCommands :: M.Map String (LP Inlines)
468+inlineCommands :: PandocMonad m => M.Map String (LP m Inlines)
464469inlineCommands = M.fromList $
465470 [ ("emph", extractSpaces emph <$> tok)
466471 , ("textit", extractSpaces emph <$> tok)
@@ -621,7 +626,7 @@ inlineCommands = M.fromList $
621626 -- in which case they will appear as raw latex blocks:
622627 [ "index" ]
623628
624−mkImage :: [(String, String)] -> String -> LP Inlines
629+mkImage :: PandocMonad m => [(String, String)] -> String -> LP m Inlines
625630mkImage options src = do
626631 let replaceTextwidth (k,v) = case numUnit v of
627632 Just (num, "\\textwidth") -> (k, showFl (num * 100) ++ "%")
@@ -645,7 +650,7 @@ unescapeURL ('\\':x:xs) | isEscapable x = x:unescapeURL xs
645650unescapeURL (x:xs) = x:unescapeURL xs
646651unescapeURL [] = ""
647652
648−enquote :: LP Inlines
653+enquote :: PandocMonad m => LP m Inlines
649654enquote = do
650655 skipopts
651656 context <- stateQuoteContext <$> getState
@@ -653,18 +658,18 @@ enquote = do
653658 then singleQuoted <$> withQuoteContext InSingleQuote tok
654659 else doubleQuoted <$> withQuoteContext InDoubleQuote tok
655660
656−doverb :: LP Inlines
661+doverb :: PandocMonad m => LP m Inlines
657662doverb = do
658663 marker <- anyChar
659664 code <$> manyTill (satisfy (/='\n')) (char marker)
660665
661−doLHSverb :: LP Inlines
666+doLHSverb :: PandocMonad m => LP m Inlines
662667doLHSverb = codeWith ("",["haskell"],[]) <$> manyTill (satisfy (/='\n')) (char '|')
663668
664−lit :: String -> LP Inlines
669+lit :: String -> LP m Inlines
665670lit = pure . str
666671
667−accent :: (Char -> String) -> Inlines -> LP Inlines
672+accent :: (Char -> String) -> Inlines -> LP m Inlines
668673accent f ils =
669674 case toList ils of
670675 (Str (x:xs) : ys) -> return $ fromList (Str (f x ++ xs) : ys)
@@ -870,53 +875,53 @@ breve 'U' = "Ŭ"
870875breve 'u' = "ŭ"
871876breve c = [c]
872877
873−tok :: LP Inlines
878+tok :: PandocMonad m => LP m Inlines
874879tok = try $ grouped inline <|> inlineCommand <|> str <$> count 1 inlineChar
875880
876−opt :: LP Inlines
881+opt :: PandocMonad m => LP m Inlines
877882opt = bracketed inline
878883
879−rawopt :: LP String
884+rawopt :: PandocMonad m => LP m String
880885rawopt = do
881886 contents <- bracketed (many1 (noneOf "[]") <|> try (string "\\]") <|>
882887 try (string "\\[") <|> rawopt)
883888 optional sp
884889 return $ "[" ++ contents ++ "]"
885890
886−skipopts :: LP ()
891+skipopts :: PandocMonad m => LP m ()
887892skipopts = skipMany rawopt
888893
889894-- opts in angle brackets are used in beamer
890−rawangle :: LP ()
895+rawangle :: PandocMonad m => LP m ()
891896rawangle = try $ do
892897 char '<'
893898 skipMany (noneOf ">")
894899 char '>'
895900 return ()
896901
897−skipangles :: LP ()
902+skipangles :: PandocMonad m => LP m ()
898903skipangles = skipMany rawangle
899904
900−inlineText :: LP Inlines
905+inlineText :: PandocMonad m => LP m Inlines
901906inlineText = str <$> many1 inlineChar
902907
903−inlineChar :: LP Char
908+inlineChar :: PandocMonad m => LP m Char
904909inlineChar = noneOf "\\$%&~#{}^'`\"‘’“”-[] \t\n"
905910
906−environment :: LP Blocks
911+environment :: PandocMonad m => LP m Blocks
907912environment = do
908913 controlSeq "begin"
909914 name <- braced
910915 M.findWithDefault mzero name environments
911916 <|> rawEnv name
912917
913−inlineEnvironment :: LP Inlines
918+inlineEnvironment :: PandocMonad m => LP m Inlines
914919inlineEnvironment = try $ do
915920 controlSeq "begin"
916921 name <- braced
917922 M.findWithDefault mzero name inlineEnvironments
918923
919−rawEnv :: String -> LP Blocks
924+rawEnv :: PandocMonad m => String -> LP m Blocks
920925rawEnv name = do
921926 parseRaw <- getOption readerParseRaw
922927 rawOptions <- mconcat <$> many rawopt
@@ -928,50 +933,7 @@ rawEnv name = do
928933
929934----
930935
931−type IncludeParser = ParserT String [String] IO String
932−
933−-- | Replace "include" commands with file contents.
934−handleIncludes :: String -> IO (Either PandocError String)
935−handleIncludes s = mapLeft (ParsecError s) <$> runParserT includeParser' [] "input" s
936−
937−includeParser' :: IncludeParser
938−includeParser' =
939− concat <$> many (comment' <|> escaped' <|> blob' <|> include'
940− <|> startMarker' <|> endMarker'
941− <|> verbCmd' <|> verbatimEnv' <|> backslash')
942−
943−comment' :: IncludeParser
944−comment' = do
945− char '%'
946− xs <- manyTill anyChar newline
947− return ('%':xs ++ "\n")
948−
949−escaped' :: IncludeParser
950−escaped' = try $ string "\\%" <|> string "\\\\"
951−
952−verbCmd' :: IncludeParser
953−verbCmd' = fmap snd <$>
954− withRaw $ try $ do
955− string "\\verb"
956− c <- anyChar
957− manyTill anyChar (char c)
958−
959−verbatimEnv' :: IncludeParser
960−verbatimEnv' = fmap snd <$>
961− withRaw $ try $ do
962− string "\\begin"
963− name <- braced'
964− guard $ name `elem` ["verbatim", "Verbatim", "BVerbatim",
965− "lstlisting", "minted", "alltt", "comment"]
966− manyTill anyChar (try $ string $ "\\end{" ++ name ++ "}")
967−
968−blob' :: IncludeParser
969−blob' = try $ many1 (noneOf "\\%")
970−
971−backslash' :: IncludeParser
972−backslash' = string "\\"
973−
974−braced' :: IncludeParser
936+braced' :: PandocMonad m => LP m String
975937braced' = try $ char '{' *> manyTill (satisfy (/='}')) (char '}')
976938
977939maybeAddExtension :: String -> FilePath -> FilePath
@@ -980,8 +942,8 @@ maybeAddExtension ext fp =
980942 then addExtension fp ext
981943 else fp
982944
983−include' :: IncludeParser
984−include' = do
945+include :: PandocMonad m => LP m Blocks
946+include = do
985947 fs' <- try $ do
986948 char '\\'
987949 name <- try (string "include")
@@ -993,59 +955,45 @@ include' = do
993955 return $ if name == "usepackage"
994956 then map (maybeAddExtension ".sty") fs
995957 else map (maybeAddExtension ".tex") fs
996− pos <- getPosition
997− containers <- getState
998− let fn = case containers of
999− (f':_) -> f'
1000− [] -> "input"
958+ oldPos <- getPosition
959+ oldInput <- getInput
1001960 -- now process each include file in order...
1002− rest <- getInput
1003− results' <- forM fs' (\f -> do
961+ mconcat <$> forM fs' (\f -> do
962+ containers <- stateContainers <$> getState
1004963 when (f `elem` containers) $
1005− fail "Include file loop!"
964+ throwError $ PandocParseError $ "Include file loop at " ++ show oldPos
965+ updateState $ \s -> s{ stateContainers = f : stateContainers s }
1006966 contents <- lift $ readTeXFile f
1007− return $ "\\PandocStartInclude{" ++ f ++ "}" ++
1008− contents ++ "\\PandocEndInclude{" ++
1009− fn ++ "}{" ++ show (sourceLine pos) ++ "}{"
1010− ++ show (sourceColumn pos) ++ "}")
1011− setInput $ concat results' ++ rest
1012− return ""
1013−
1014−startMarker' :: IncludeParser
1015−startMarker' = try $ do
1016− string "\\PandocStartInclude"
1017− fn <- braced'
1018− updateState (fn:)
1019− setPosition $ newPos fn 1 1
1020− return $ "\\PandocStartInclude{" ++ fn ++ "}"
1021−
1022−endMarker' :: IncludeParser
1023−endMarker' = try $ do
1024− string "\\PandocEndInclude"
1025− fn <- braced'
1026− ln <- braced'
1027− co <- braced'
1028− updateState tail
1029− setPosition $ newPos fn (fromMaybe 1 $ safeRead ln) (fromMaybe 1 $ safeRead co)
1030− return $ "\\PandocEndInclude{" ++ fn ++ "}{" ++ ln ++ "}{" ++
1031− co ++ "}"
1032−
1033−readTeXFile :: FilePath -> IO String
967+ setPosition $ newPos f 1 1
968+ setInput contents
969+ bs <- blocks
970+ setInput oldInput
971+ setPosition oldPos
972+ updateState $ \s -> s{ stateContainers = tail $ stateContainers s }
973+ return bs)
974+
975+readTeXFile :: PandocMonad m => FilePath -> m String
1034976readTeXFile f = do
1035− texinputs <- E.catch (getEnv "TEXINPUTS") $ \(_ :: E.SomeException) ->
1036− return "."
1037− let ds = splitBy (==':') texinputs
1038− readFileFromDirs ds f
1039−
1040−readFileFromDirs :: [FilePath] -> FilePath -> IO String
1041−readFileFromDirs [] _ = return ""
1042−readFileFromDirs (d:ds) f =
1043− E.catch (UTF8.readFile $ d </> f) $ \(_ :: E.SomeException) ->
1044− readFileFromDirs ds f
977+ texinputs <- fromMaybe "." <$> lookupEnv "TEXINPUTS"
978+ readFileFromDirs (splitBy (==':') texinputs) f
979+
980+readFileFromDirs :: PandocMonad m => [FilePath] -> FilePath -> m String
981+readFileFromDirs [] f = do
982+ warning $ "Could not load include file " ++ f ++ ", skipping."
983+ return ""
984+readFileFromDirs (d:ds) f = do
985+ res <- readFileLazy' (d </> f)
986+ case res of
987+ Right s -> return s
988+ Left _ -> readFileFromDirs ds f
989+
990+readFileLazy' :: PandocMonad m => FilePath -> m (Either PandocError String)
991+readFileLazy' f = catchError ((Right . UTF8.toStringLazy) <$> readFileLazy f) $
992+ \(e :: PandocError) -> return (Left e)
1045993
1046994----
1047995
1048−keyval :: LP (String, String)
996+keyval :: PandocMonad m => LP m (String, String)
1049997keyval = try $ do
1050998 key <- many1 alphaNum
1051999 val <- option "" $ char '=' >> many1 (alphaNum <|> char '.' <|> char '\\')
@@ -1055,25 +1003,25 @@ keyval = try $ do
10551003 return (key, val)
10561004
10571005
1058−keyvals :: LP [(String, String)]
1006+keyvals :: PandocMonad m => LP m [(String, String)]
10591007keyvals = try $ char '[' *> manyTill keyval (char ']')
10601008
1061−alltt :: String -> LP Blocks
1009+alltt :: PandocMonad m => String -> LP m Blocks
10621010alltt t = walk strToCode <$> parseFromString blocks
10631011 (substitute " " "\\ " $ substitute "%" "\\%" $
10641012 intercalate "\\\\\n" $ lines t)
10651013 where strToCode (Str s) = Code nullAttr s
10661014 strToCode x = x
10671015
1068−rawLaTeXBlock :: LP String
1016+rawLaTeXBlock :: PandocMonad m => LP m String
10691017rawLaTeXBlock = snd <$> try (withRaw (environment <|> blockCommand))
10701018
1071−rawLaTeXInline :: LP Inline
1019+rawLaTeXInline :: PandocMonad m => LP m Inline
10721020rawLaTeXInline = do
10731021 raw <- (snd <$> withRaw inlineCommand) <|> (snd <$> withRaw blockCommand)
10741022 RawInline "latex" <$> applyMacros' raw
10751023
1076−addImageCaption :: Blocks -> LP Blocks
1024+addImageCaption :: PandocMonad m => Blocks -> LP m Blocks
10771025addImageCaption = walkM go
10781026 where go (Image attr alt (src,tit)) = do
10791027 mbcapt <- stateCaption <$> getState
@@ -1082,7 +1030,7 @@ addImageCaption = walkM go
10821030 Nothing -> Image attr alt (src,tit)
10831031 go x = return x
10841032
1085−addTableCaption :: Blocks -> LP Blocks
1033+addTableCaption :: PandocMonad m => Blocks -> LP m Blocks
10861034addTableCaption = walkM go
10871035 where go (Table c als ws hs rs) = do
10881036 mbcapt <- stateCaption <$> getState
@@ -1091,7 +1039,7 @@ addTableCaption = walkM go
10911039 Nothing -> Table c als ws hs rs
10921040 go x = return x
10931041
1094−environments :: M.Map String (LP Blocks)
1042+environments :: PandocMonad m => M.Map String (LP m Blocks)
10951043environments = M.fromList
10961044 [ ("document", env "document" blocks <* skipMany anyChar)
10971045 , ("abstract", mempty <$ (env "abstract" blocks >>= addMeta "abstract"))
@@ -1159,7 +1107,7 @@ environments = M.fromList
11591107 , ("alignat*", mathEnv para (Just "aligned") "alignat*")
11601108 ]
11611109
1162−letterContents :: LP Blocks
1110+letterContents :: PandocMonad m => LP m Blocks
11631111letterContents = do
11641112 bs <- blocks
11651113 st <- getState
@@ -1170,7 +1118,7 @@ letterContents = do
11701118 _ -> mempty
11711119 return $ addr <> bs -- sig added by \closing
11721120
1173−closing :: LP Blocks
1121+closing :: PandocMonad m => LP m Blocks
11741122closing = do
11751123 contents <- tok
11761124 st <- getState
@@ -1184,17 +1132,17 @@ closing = do
11841132 _ -> mempty
11851133 return $ para (trimInlines contents) <> sigs
11861134
1187−item :: LP Blocks
1135+item :: PandocMonad m => LP m Blocks
11881136item = blocks *> controlSeq "item" *> skipopts *> blocks
11891137
1190−looseItem :: LP Blocks
1138+looseItem :: PandocMonad m => LP m Blocks
11911139looseItem = do
11921140 ctx <- stateParserContext `fmap` getState
11931141 if ctx == ListItemState
11941142 then mzero
11951143 else return mempty
11961144
1197−descItem :: LP (Inlines, [Blocks])
1145+descItem :: PandocMonad m => LP m (Inlines, [Blocks])
11981146descItem = do
11991147 blocks -- skip blocks before item
12001148 controlSeq "item"
@@ -1203,12 +1151,12 @@ descItem = do
12031151 bs <- blocks
12041152 return (ils, [bs])
12051153
1206−env :: String -> LP a -> LP a
1154+env :: PandocMonad m => String -> LP m a -> LP m a
12071155env name p = p <*
12081156 (try (controlSeq "end" *> braced >>= guard . (== name))
12091157 <?> ("\\end{" ++ name ++ "}"))
12101158
1211−listenv :: String -> LP a -> LP a
1159+listenv :: PandocMonad m => String -> LP m a -> LP m a
12121160listenv name p = try $ do
12131161 oldCtx <- stateParserContext `fmap` getState
12141162 updateState $ \st -> st{ stateParserContext = ListItemState }
@@ -1216,14 +1164,14 @@ listenv name p = try $ do
12161164 updateState $ \st -> st{ stateParserContext = oldCtx }
12171165 return res
12181166
1219−mathEnv :: (Inlines -> a) -> Maybe String -> String -> LP a
1167+mathEnv :: PandocMonad m => (Inlines -> a) -> Maybe String -> String -> LP m a
12201168mathEnv f innerEnv name = f <$> mathDisplay (inner <$> verbEnv name)
12211169 where inner x = case innerEnv of
12221170 Nothing -> x
12231171 Just y -> "\\begin{" ++ y ++ "}\n" ++ x ++
12241172 "\\end{" ++ y ++ "}"
12251173
1226−verbEnv :: String -> LP String
1174+verbEnv :: PandocMonad m => String -> LP m String
12271175verbEnv name = do
12281176 skipopts
12291177 optional blankline
@@ -1231,7 +1179,7 @@ verbEnv name = do
12311179 res <- manyTill anyChar endEnv
12321180 return $ stripTrailingNewlines res
12331181
1234−fancyverbEnv :: String -> LP Blocks
1182+fancyverbEnv :: PandocMonad m => String -> LP m Blocks
12351183fancyverbEnv name = do
12361184 options <- option [] keyvals
12371185 let kvs = [ (if k == "firstnumber"
@@ -1242,7 +1190,7 @@ fancyverbEnv name = do
12421190 let attr = ("",classes,kvs)
12431191 codeBlockWith attr <$> verbEnv name
12441192
1245−orderedList' :: LP Blocks
1193+orderedList' :: PandocMonad m => LP m Blocks
12461194orderedList' = do
12471195 optional sp
12481196 (_, style, delim) <- option (1, DefaultStyle, DefaultDelim) $
@@ -1259,19 +1207,20 @@ orderedList' = do
12591207 bs <- listenv "enumerate" (many item)
12601208 return $ orderedListWith (start, style, delim) bs
12611209
1262−paragraph :: LP Blocks
1210+paragraph :: PandocMonad m => LP m Blocks
12631211paragraph = do
12641212 x <- trimInlines . mconcat <$> many1 inline
12651213 if x == mempty
12661214 then return mempty
12671215 else return $ para x
12681216
1269−preamble :: LP Blocks
1217+preamble :: PandocMonad m => LP m Blocks
12701218preamble = mempty <$> manyTill preambleBlock beginDoc
12711219 where beginDoc = lookAhead $ try $ controlSeq "begin" *> string "{document}"
12721220 preambleBlock = void comment
12731221 <|> void sp
12741222 <|> void blanklines
1223+ <|> void include
12751224 <|> void macro
12761225 <|> void blockCommand
12771226 <|> void anyControlSeq
@@ -1292,7 +1241,7 @@ addSuffix s ks@(_:_) =
12921241 in init ks ++ [k {citationSuffix = citationSuffix k ++ s}]
12931242addSuffix _ _ = []
12941243
1295−simpleCiteArgs :: LP [Citation]
1244+simpleCiteArgs :: PandocMonad m => LP m [Citation]
12961245simpleCiteArgs = try $ do
12971246 first <- optionMaybe $ toList <$> opt
12981247 second <- optionMaybe $ toList <$> opt
@@ -1312,7 +1261,7 @@ simpleCiteArgs = try $ do
13121261 }
13131262 return $ addPrefix pre $ addSuffix suf $ map conv keys
13141263
1315−citationLabel :: LP String
1264+citationLabel :: PandocMonad m => LP m String
13161265citationLabel = optional sp *>
13171266 (many1 (satisfy isBibtexKeyChar)
13181267 <* optional sp
@@ -1320,7 +1269,7 @@ citationLabel = optional sp *>
13201269 <* optional sp)
13211270 where isBibtexKeyChar c = isAlphaNum c || c `elem` (".:;?!`'()/*@_+=-[]" :: String)
13221271
1323−cites :: CitationMode -> Bool -> LP [Citation]
1272+cites :: PandocMonad m => CitationMode -> Bool -> LP m [Citation]
13241273cites mode multi = try $ do
13251274 cits <- if multi
13261275 then many1 simpleCiteArgs
@@ -1332,12 +1281,12 @@ cites mode multi = try $ do
13321281 [] -> []
13331282 _ -> map (\a -> a {citationMode = mode}) cs
13341283
1335−citation :: String -> CitationMode -> Bool -> LP Inlines
1284+citation :: PandocMonad m => String -> CitationMode -> Bool -> LP m Inlines
13361285citation name mode multi = do
13371286 (c,raw) <- withRaw $ cites mode multi
13381287 return $ cite c (rawInline "latex" $ "\\" ++ name ++ raw)
13391288
1340−complexNatbibCitation :: CitationMode -> LP Inlines
1289+complexNatbibCitation :: PandocMonad m => CitationMode -> LP m Inlines
13411290complexNatbibCitation mode = try $ do
13421291 let ils = (toList . trimInlines . mconcat) <$>
13431292 many (notFollowedBy (oneOf "\\};") >> inline)
@@ -1359,7 +1308,7 @@ complexNatbibCitation mode = try $ do
13591308
13601309-- tables
13611310
1362−parseAligns :: LP [Alignment]
1311+parseAligns :: PandocMonad m => LP m [Alignment]
13631312parseAligns = try $ do
13641313 char '{'
13651314 let maybeBar = skipMany $ sp <|> () <$ char '|' <|> () <$ (char '@' >> braced)
@@ -1375,7 +1324,7 @@ parseAligns = try $ do
13751324 spaces
13761325 return aligns'
13771326
1378−hline :: LP ()
1327+hline :: PandocMonad m => LP m ()
13791328hline = try $ do
13801329 spaces'
13811330 controlSeq "hline" <|>
@@ -1389,16 +1338,16 @@ hline = try $ do
13891338 optional $ bracketed (many1 (satisfy (/=']')))
13901339 return ()
13911340
1392−lbreak :: LP ()
1341+lbreak :: PandocMonad m => LP m ()
13931342lbreak = () <$ try (spaces' *>
13941343 (controlSeq "\\" <|> controlSeq "tabularnewline") <*
13951344 spaces')
13961345
1397−amp :: LP ()
1346+amp :: PandocMonad m => LP m ()
13981347amp = () <$ try (spaces' *> char '&' <* spaces')
13991348
1400−parseTableRow :: Int -- ^ number of columns
1401− -> LP [Blocks]
1349+parseTableRow :: PandocMonad m => Int -- ^ number of columns
1350+ -> LP m [Blocks]
14021351parseTableRow cols = try $ do
14031352 let tableCellInline = notFollowedBy (amp <|> lbreak) >> inline
14041353 let minipage = try $ controlSeq "begin" *> string "{minipage}" *>
@@ -1415,10 +1364,10 @@ parseTableRow cols = try $ do
14151364 spaces'
14161365 return cells''
14171366
1418−spaces' :: LP ()
1367+spaces' :: PandocMonad m => LP m ()
14191368spaces' = spaces *> skipMany (comment *> spaces)
14201369
1421−simpTable :: Bool -> LP Blocks
1370+simpTable :: PandocMonad m => Bool -> LP m Blocks
14221371simpTable hasWidthParameter = try $ do
14231372 when hasWidthParameter $ () <$ (spaces' >> tok)
14241373 skipopts
@@ -1442,20 +1391,6 @@ simpTable hasWidthParameter = try $ do
14421391 lookAhead $ controlSeq "end" -- make sure we're at end
14431392 return $ table mempty (zip aligns (repeat 0)) header'' rows
14441393
1445−startInclude :: LP Blocks
1446−startInclude = do
1447− fn <- braced
1448− setPosition $ newPos fn 1 1
1449− return mempty
1450−
1451−endInclude :: LP Blocks
1452−endInclude = do
1453− fn <- braced
1454− ln <- braced
1455− co <- braced
1456− setPosition $ newPos fn (fromMaybe 1 $ safeRead ln) (fromMaybe 1 $ safeRead co)
1457− return mempty
1458−
14591394removeDoubleQuotes :: String -> String
14601395removeDoubleQuotes ('"':xs) =
14611396 case reverse xs of
src/Text/Pandoc/Readers/Markdown.hs+272−249
@@ -1,5 +1,6 @@
11{-# LANGUAGE RelaxedPolyRec #-} -- needed for inlinesBetween on GHC < 7
22{-# LANGUAGE ScopedTypeVariables #-}
3+
34{-
45Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu>
56
@@ -29,8 +30,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2930
3031Conversion of markdown-formatted plain text to 'Pandoc' document.
3132-}
32−module Text.Pandoc.Readers.Markdown ( readMarkdown,
33− readMarkdownWithWarnings ) where
33+module Text.Pandoc.Readers.Markdown ( readMarkdown ) where
3434
3535import Data.List ( transpose, sortBy, findIndex, intercalate )
3636import qualified Data.Map as M
@@ -61,28 +61,25 @@ import Text.Pandoc.Readers.HTML ( htmlTag, htmlInBalanced, isInlineTag, isBlockT
6161import Control.Monad
6262import System.FilePath (takeExtension, addExtension)
6363import Text.HTML.TagSoup
64−import qualified Data.Set as Set
6564import Text.Printf (printf)
66−import Debug.Trace (trace)
6765import Data.Monoid ((<>))
68−import Text.Pandoc.Error
66+import Control.Monad.Trans (lift)
67+import Control.Monad.Except (throwError, catchError)
68+import Text.Pandoc.Class (PandocMonad, report)
69+import qualified Text.Pandoc.Class as P
6970
70−type MarkdownParser = Parser [Char] ParserState
71+type MarkdownParser m = ParserT [Char] ParserState m
7172
7273-- | Read markdown from an input string and return a Pandoc document.
73−readMarkdown :: ReaderOptions -- ^ Reader options
74+readMarkdown :: PandocMonad m
75+ => ReaderOptions -- ^ Reader options
7476 -> String -- ^ String to parse (assuming @'\n'@ line endings)
75− -> Either PandocError Pandoc
76−readMarkdown opts s =
77− (readWith parseMarkdown) def{ stateOptions = opts } (s ++ "\n\n")
78−
79−-- | Read markdown from an input string and return a pair of a Pandoc document
80−-- and a list of warnings.
81−readMarkdownWithWarnings :: ReaderOptions -- ^ Reader options
82− -> String -- ^ String to parse (assuming @'\n'@ line endings)
83− -> Either PandocError (Pandoc, [String])
84−readMarkdownWithWarnings opts s =
85− (readWithWarnings parseMarkdown) def{ stateOptions = opts } (s ++ "\n\n")
77+ -> m Pandoc
78+readMarkdown opts s = do
79+ parsed <- (readWithM parseMarkdown) def{ stateOptions = opts } (s ++ "\n\n")
80+ case parsed of
81+ Right result -> return result
82+ Left e -> throwError e
8683
8784trimInlinesF :: F Inlines -> F Inlines
8885trimInlinesF = liftM trimInlines
@@ -117,25 +114,25 @@ isBlank _ = False
117114--
118115
119116-- | Succeeds when we're in list context.
120−inList :: MarkdownParser ()
117+inList :: PandocMonad m => MarkdownParser m ()
121118inList = do
122119 ctx <- stateParserContext <$> getState
123120 guard (ctx == ListItemState)
124121
125−spnl :: Parser [Char] st ()
122+spnl :: PandocMonad m => ParserT [Char] st m ()
126123spnl = try $ do
127124 skipSpaces
128125 optional newline
129126 skipSpaces
130127 notFollowedBy (char '\n')
131128
132−indentSpaces :: MarkdownParser String
129+indentSpaces :: PandocMonad m => MarkdownParser m String
133130indentSpaces = try $ do
134131 tabStop <- getOption readerTabStop
135132 count tabStop (char ' ') <|>
136133 string "\t" <?> "indentation"
137134
138−nonindentSpaces :: MarkdownParser String
135+nonindentSpaces :: PandocMonad m => MarkdownParser m String
139136nonindentSpaces = do
140137 tabStop <- getOption readerTabStop
141138 sps <- many (char ' ')
@@ -144,17 +141,17 @@ nonindentSpaces = do
144141 else unexpected "indented line"
145142
146143-- returns number of spaces parsed
147−skipNonindentSpaces :: MarkdownParser Int
144+skipNonindentSpaces :: PandocMonad m => MarkdownParser m Int
148145skipNonindentSpaces = do
149146 tabStop <- getOption readerTabStop
150147 atMostSpaces (tabStop - 1) <* notFollowedBy (char ' ')
151148
152−atMostSpaces :: Int -> MarkdownParser Int
149+atMostSpaces :: PandocMonad m => Int -> MarkdownParser m Int
153150atMostSpaces n
154151 | n > 0 = (char ' ' >> (+1) <$> atMostSpaces (n-1)) <|> return 0
155152 | otherwise = return 0
156153
157−litChar :: MarkdownParser Char
154+litChar :: PandocMonad m => MarkdownParser m Char
158155litChar = escapedChar'
159156 <|> characterReference
160157 <|> noneOf "\n"
@@ -162,14 +159,14 @@ litChar = escapedChar'
162159
163160-- | Parse a sequence of inline elements between square brackets,
164161-- including inlines between balanced pairs of square brackets.
165−inlinesInBalancedBrackets :: MarkdownParser (F Inlines)
162+inlinesInBalancedBrackets :: PandocMonad m => MarkdownParser m (F Inlines)
166163inlinesInBalancedBrackets = do
167164 char '['
168165 (_, raw) <- withRaw $ charsInBalancedBrackets 1
169166 guard $ not $ null raw
170167 parseFromString (trimInlinesF . mconcat <$> many inline) (init raw)
171168
172−charsInBalancedBrackets :: Int -> MarkdownParser ()
169+charsInBalancedBrackets :: PandocMonad m => Int -> MarkdownParser m ()
173170charsInBalancedBrackets 0 = return ()
174171charsInBalancedBrackets openBrackets =
175172 (char '[' >> charsInBalancedBrackets (openBrackets + 1))
@@ -185,7 +182,7 @@ charsInBalancedBrackets openBrackets =
185182-- document structure
186183--
187184
188−rawTitleBlockLine :: MarkdownParser String
185+rawTitleBlockLine :: PandocMonad m => MarkdownParser m String
189186rawTitleBlockLine = do
190187 char '%'
191188 skipSpaces
@@ -196,13 +193,13 @@ rawTitleBlockLine = do
196193 anyLine
197194 return $ trim $ unlines (first:rest)
198195
199−titleLine :: MarkdownParser (F Inlines)
196+titleLine :: PandocMonad m => MarkdownParser m (F Inlines)
200197titleLine = try $ do
201198 raw <- rawTitleBlockLine
202199 res <- parseFromString (many inline) raw
203200 return $ trimInlinesF $ mconcat res
204201
205−authorsLine :: MarkdownParser (F [Inlines])
202+authorsLine :: PandocMonad m => MarkdownParser m (F [Inlines])
206203authorsLine = try $ do
207204 raw <- rawTitleBlockLine
208205 let sep = (char ';' <* spaces) <|> newline
@@ -212,16 +209,16 @@ authorsLine = try $ do
212209 sep
213210 sequence <$> parseFromString pAuthors raw
214211
215−dateLine :: MarkdownParser (F Inlines)
212+dateLine :: PandocMonad m => MarkdownParser m (F Inlines)
216213dateLine = try $ do
217214 raw <- rawTitleBlockLine
218215 res <- parseFromString (many inline) raw
219216 return $ trimInlinesF $ mconcat res
220217
221−titleBlock :: MarkdownParser ()
218+titleBlock :: PandocMonad m => MarkdownParser m ()
222219titleBlock = pandocTitleBlock <|> mmdTitleBlock
223220
224−pandocTitleBlock :: MarkdownParser ()
221+pandocTitleBlock :: PandocMonad m => MarkdownParser m ()
225222pandocTitleBlock = try $ do
226223 guardEnabled Ext_pandoc_title_block
227224 lookAhead (char '%')
@@ -239,7 +236,15 @@ pandocTitleBlock = try $ do
239236 $ nullMeta
240237 updateState $ \st -> st{ stateMeta' = stateMeta' st <> meta' }
241238
242−yamlMetaBlock :: MarkdownParser (F Blocks)
239+
240+-- Adapted from solution at
241+-- http://stackoverflow.com/a/29448764/1901888
242+foldrWithKeyM :: Monad m => (k -> b -> a -> m a) -> a -> H.HashMap k b -> m a
243+foldrWithKeyM f acc = H.foldrWithKey f' (return acc)
244+ where
245+ f' k b ma = ma >>= \a -> f k b a
246+
247+yamlMetaBlock :: PandocMonad m => MarkdownParser m (F Blocks)
243248yamlMetaBlock = try $ do
244249 guardEnabled Ext_yaml_metadata_block
245250 pos <- getPosition
@@ -252,18 +257,20 @@ yamlMetaBlock = try $ do
252257 optional blanklines
253258 opts <- stateOptions <$> getState
254259 meta' <- case Yaml.decodeEither' $ UTF8.fromString rawYaml of
255− Right (Yaml.Object hashmap) -> return $ return $
256− H.foldrWithKey (\k v m ->
257− if ignorable k
258− then m
259− else case yamlToMeta opts v of
260− Left _ -> m
261− Right v' -> B.setMeta (T.unpack k) v' m)
262− nullMeta hashmap
263− Right Yaml.Null -> return $ return nullMeta
260+ Right (Yaml.Object hashmap) ->
261+ foldrWithKeyM
262+ (\k v m -> do
263+ if ignorable k
264+ then return m
265+ else (do v' <- lift $ yamlToMeta opts v
266+ return $ B.setMeta (T.unpack k) v' m)
267+ `catchError`
268+ (\_ -> return m)
269+ ) nullMeta hashmap
270+ Right Yaml.Null -> return nullMeta
264271 Right _ -> do
265− addWarning (Just pos) "YAML header is not an object"
266− return $ return nullMeta
272+ P.warningWithPos pos "YAML header is not an object"
273+ return nullMeta
267274 Left err' -> do
268275 case err' of
269276 InvalidYaml (Just YamlParseException{
@@ -273,24 +280,24 @@ yamlMetaBlock = try $ do
273280 yamlLine = yline
274281 , yamlColumn = ycol
275282 }}) ->
276− addWarning (Just $ setSourceLine
283+ P.warningWithPos (setSourceLine
277284 (setSourceColumn pos
278285 (sourceColumn pos + ycol))
279286 (sourceLine pos + 1 + yline))
280287 $ "Could not parse YAML header: " ++
281288 problem
282− _ -> addWarning (Just pos)
289+ _ -> P.warningWithPos pos
283290 $ "Could not parse YAML header: " ++
284291 show err'
285− return $ return nullMeta
286− updateState $ \st -> st{ stateMeta' = stateMeta' st <> meta' }
292+ return nullMeta
293+ updateState $ \st -> st{ stateMeta' = stateMeta' st <> (return meta') }
287294 return mempty
288295
289296-- ignore fields ending with _
290297ignorable :: Text -> Bool
291298ignorable t = (T.pack "_") `T.isSuffixOf` t
292299
293−toMetaValue :: ReaderOptions -> Text -> Either PandocError MetaValue
300+toMetaValue :: PandocMonad m => ReaderOptions -> Text -> m MetaValue
294301toMetaValue opts x = toMeta <$> readMarkdown opts' (T.unpack x)
295302 where
296303 toMeta p =
@@ -301,13 +308,13 @@ toMetaValue opts x = toMeta <$> readMarkdown opts' (T.unpack x)
301308 | otherwise -> MetaInlines xs
302309 Pandoc _ bs -> MetaBlocks bs
303310 endsWithNewline t = T.pack "\n" `T.isSuffixOf` t
304− opts' = opts{readerExtensions=readerExtensions opts `Set.difference` meta_exts}
305− meta_exts = Set.fromList [ Ext_pandoc_title_block
306− , Ext_mmd_title_block
307− , Ext_yaml_metadata_block
308− ]
311+ opts' = opts{readerExtensions =
312+ disableExtension Ext_pandoc_title_block $
313+ disableExtension Ext_mmd_title_block $
314+ disableExtension Ext_yaml_metadata_block $
315+ readerExtensions opts }
309316
310−yamlToMeta :: ReaderOptions -> Yaml.Value -> Either PandocError MetaValue
317+yamlToMeta :: PandocMonad m => ReaderOptions -> Yaml.Value -> m MetaValue
311318yamlToMeta opts (Yaml.String t) = toMetaValue opts t
312319yamlToMeta _ (Yaml.Number n)
313320 -- avoid decimal points for numbers that don't need them:
@@ -327,10 +334,10 @@ yamlToMeta opts (Yaml.Object o) = MetaMap <$> H.foldrWithKey (\k v m ->
327334 (return M.empty) o
328335yamlToMeta _ _ = return $ MetaString ""
329336
330−stopLine :: MarkdownParser ()
337+stopLine :: PandocMonad m => MarkdownParser m ()
331338stopLine = try $ (string "---" <|> string "...") >> blankline >> return ()
332339
333−mmdTitleBlock :: MarkdownParser ()
340+mmdTitleBlock :: PandocMonad m => MarkdownParser m ()
334341mmdTitleBlock = try $ do
335342 guardEnabled Ext_mmd_title_block
336343 firstPair <- kvPair False
@@ -340,7 +347,7 @@ mmdTitleBlock = try $ do
340347 updateState $ \st -> st{ stateMeta' = stateMeta' st <>
341348 return (Meta $ M.fromList kvPairs) }
342349
343−kvPair :: Bool -> MarkdownParser (String, MetaValue)
350+kvPair :: PandocMonad m => Bool -> MarkdownParser m (String, MetaValue)
344351kvPair allowEmpty = try $ do
345352 key <- many1Till (alphaNum <|> oneOf "_- ") (char ':')
346353 val <- trim <$> manyTill anyChar
@@ -350,7 +357,7 @@ kvPair allowEmpty = try $ do
350357 let val' = MetaBlocks $ B.toList $ B.plain $ B.text $ val
351358 return (key',val')
352359
353−parseMarkdown :: MarkdownParser Pandoc
360+parseMarkdown :: PandocMonad m => MarkdownParser m Pandoc
354361parseMarkdown = do
355362 -- markdown allows raw HTML
356363 updateState $ \state -> state { stateOptions =
@@ -375,7 +382,7 @@ softBreakFilter (x:SoftBreak:y:zs) =
375382 _ -> x:SoftBreak:y:zs
376383softBreakFilter xs = xs
377384
378−referenceKey :: MarkdownParser (F Blocks)
385+referenceKey :: PandocMonad m => MarkdownParser m (F Blocks)
379386referenceKey = try $ do
380387 pos <- getPosition
381388 skipNonindentSpaces
@@ -402,18 +409,18 @@ referenceKey = try $ do
402409 let oldkeys = stateKeys st
403410 let key = toKey raw
404411 case M.lookup key oldkeys of
405− Just _ -> addWarning (Just pos) $ "Duplicate link reference `" ++ raw ++ "'"
412+ Just _ -> P.warningWithPos pos $ "Duplicate link reference `" ++ raw ++ "'"
406413 Nothing -> return ()
407414 updateState $ \s -> s { stateKeys = M.insert key (target, attr') oldkeys }
408415 return $ return mempty
409416
410−referenceTitle :: MarkdownParser String
417+referenceTitle :: PandocMonad m => MarkdownParser m String
411418referenceTitle = try $ do
412419 skipSpaces >> optional newline >> skipSpaces
413420 quotedTitle '"' <|> quotedTitle '\'' <|> charsInBalanced '(' ')' litChar
414421
415422-- A link title in quotes
416−quotedTitle :: Char -> MarkdownParser String
423+quotedTitle :: PandocMonad m => Char -> MarkdownParser m String
417424quotedTitle c = try $ do
418425 char c
419426 notFollowedBy spaces
@@ -425,7 +432,7 @@ quotedTitle c = try $ do
425432-- | PHP Markdown Extra style abbreviation key. Currently
426433-- we just skip them, since Pandoc doesn't have an element for
427434-- an abbreviation.
428−abbrevKey :: MarkdownParser (F Blocks)
435+abbrevKey :: PandocMonad m => MarkdownParser m (F Blocks)
429436abbrevKey = do
430437 guardEnabled Ext_abbreviations
431438 try $ do
@@ -436,23 +443,23 @@ abbrevKey = do
436443 blanklines
437444 return $ return mempty
438445
439−noteMarker :: MarkdownParser String
446+noteMarker :: PandocMonad m => MarkdownParser m String
440447noteMarker = string "[^" >> many1Till (satisfy $ not . isBlank) (char ']')
441448
442−rawLine :: MarkdownParser String
449+rawLine :: PandocMonad m => MarkdownParser m String
443450rawLine = try $ do
444451 notFollowedBy blankline
445452 notFollowedBy' $ try $ skipNonindentSpaces >> noteMarker
446453 optional indentSpaces
447454 anyLine
448455
449−rawLines :: MarkdownParser String
456+rawLines :: PandocMonad m => MarkdownParser m String
450457rawLines = do
451458 first <- anyLine
452459 rest <- many rawLine
453460 return $ unlines (first:rest)
454461
455−noteBlock :: MarkdownParser (F Blocks)
462+noteBlock :: PandocMonad m => MarkdownParser m (F Blocks)
456463noteBlock = try $ do
457464 pos <- getPosition
458465 skipNonindentSpaces
@@ -468,7 +475,7 @@ noteBlock = try $ do
468475 let newnote = (ref, parsed)
469476 oldnotes <- stateNotes' <$> getState
470477 case lookup ref oldnotes of
471− Just _ -> addWarning (Just pos) $ "Duplicate note reference `" ++ ref ++ "'"
478+ Just _ -> P.warningWithPos pos $ "Duplicate note reference `" ++ ref ++ "'"
472479 Nothing -> return ()
473480 updateState $ \s -> s { stateNotes' = newnote : oldnotes }
474481 return mempty
@@ -477,12 +484,11 @@ noteBlock = try $ do
477484-- parsing blocks
478485--
479486
480−parseBlocks :: MarkdownParser (F Blocks)
487+parseBlocks :: PandocMonad m => MarkdownParser m (F Blocks)
481488parseBlocks = mconcat <$> manyTill block eof
482489
483−block :: MarkdownParser (F Blocks)
490+block :: PandocMonad m => MarkdownParser m (F Blocks)
484491block = do
485− tr <- getOption readerTrace
486492 pos <- getPosition
487493 res <- choice [ mempty <$ blanklines
488494 , codeBlockFenced
@@ -509,26 +515,25 @@ block = do
509515 , para
510516 , plain
511517 ] <?> "block"
512− when tr $ do
513− st <- getState
514− trace (printf "line %d: %s" (sourceLine pos)
515− (take 60 $ show $ B.toList $ runF res st)) (return ())
518+ report DEBUG $ printf "line %d: %s" (sourceLine pos)
519+ (take 60 $ show $ B.toList $ runF res defaultParserState)
516520 return res
517521
518522--
519523-- header blocks
520524--
521525
522−header :: MarkdownParser (F Blocks)
526+header :: PandocMonad m => MarkdownParser m (F Blocks)
523527header = setextHeader <|> atxHeader <?> "header"
524528
525−atxChar :: MarkdownParser Char
529+atxChar :: PandocMonad m => MarkdownParser m Char
526530atxChar = do
527531 exts <- getOption readerExtensions
528− return $ if Set.member Ext_literate_haskell exts
529− then '=' else '#'
532+ return $ if extensionEnabled Ext_literate_haskell exts
533+ then '='
534+ else '#'
530535
531−atxHeader :: MarkdownParser (F Blocks)
536+atxHeader :: PandocMonad m => MarkdownParser m (F Blocks)
532537atxHeader = try $ do
533538 level <- atxChar >>= many1 . char >>= return . length
534539 notFollowedBy $ guardEnabled Ext_fancy_lists >>
@@ -542,7 +547,7 @@ atxHeader = try $ do
542547 <|> registerImplicitHeader raw attr'
543548 return $ B.headerWith attr' level <$> text
544549
545−atxClosing :: MarkdownParser Attr
550+atxClosing :: PandocMonad m => MarkdownParser m Attr
546551atxClosing = try $ do
547552 attr' <- option nullAttr
548553 (guardEnabled Ext_mmd_header_identifiers >> mmdHeaderIdentifier)
@@ -553,7 +558,7 @@ atxClosing = try $ do
553558 blanklines
554559 return attr
555560
556−setextHeaderEnd :: MarkdownParser Attr
561+setextHeaderEnd :: PandocMonad m => MarkdownParser m Attr
557562setextHeaderEnd = try $ do
558563 attr <- option nullAttr
559564 $ (guardEnabled Ext_mmd_header_identifiers >> mmdHeaderIdentifier)
@@ -561,13 +566,13 @@ setextHeaderEnd = try $ do
561566 blanklines
562567 return attr
563568
564−mmdHeaderIdentifier :: MarkdownParser Attr
569+mmdHeaderIdentifier :: PandocMonad m => MarkdownParser m Attr
565570mmdHeaderIdentifier = do
566571 ident <- stripFirstAndLast . snd <$> reference
567572 skipSpaces
568573 return (ident,[],[])
569574
570−setextHeader :: MarkdownParser (F Blocks)
575+setextHeader :: PandocMonad m => MarkdownParser m (F Blocks)
571576setextHeader = try $ do
572577 -- This lookahead prevents us from wasting time parsing Inlines
573578 -- unless necessary -- it gives a significant performance boost.
@@ -585,7 +590,7 @@ setextHeader = try $ do
585590 <|> registerImplicitHeader raw attr'
586591 return $ B.headerWith attr' level <$> text
587592
588−registerImplicitHeader :: String -> Attr -> MarkdownParser ()
593+registerImplicitHeader :: PandocMonad m => String -> Attr -> MarkdownParser m ()
589594registerImplicitHeader raw attr@(ident, _, _) = do
590595 let key = toKey $ "[" ++ raw ++ "]"
591596 updateState (\s -> s { stateHeaderKeys =
@@ -595,7 +600,7 @@ registerImplicitHeader raw attr@(ident, _, _) = do
595600-- hrule block
596601--
597602
598−hrule :: Parser [Char] st (F Blocks)
603+hrule :: PandocMonad m => ParserT [Char] st m (F Blocks)
599604hrule = try $ do
600605 skipSpaces
601606 start <- satisfy isHruleChar
@@ -609,12 +614,13 @@ hrule = try $ do
609614-- code blocks
610615--
611616
612−indentedLine :: MarkdownParser String
617+indentedLine :: PandocMonad m => MarkdownParser m String
613618indentedLine = indentSpaces >> anyLine >>= return . (++ "\n")
614619
615−blockDelimiter :: (Char -> Bool)
620+blockDelimiter :: PandocMonad m
621+ => (Char -> Bool)
616622 -> Maybe Int
617− -> Parser [Char] st Int
623+ -> ParserT [Char] st m Int
618624blockDelimiter f len = try $ do
619625 c <- lookAhead (satisfy f)
620626 case len of
@@ -622,7 +628,7 @@ blockDelimiter f len = try $ do
622628 Nothing -> count 3 (char c) >> many (char c) >>=
623629 return . (+ 3) . length
624630
625−attributes :: MarkdownParser Attr
631+attributes :: PandocMonad m => MarkdownParser m Attr
626632attributes = try $ do
627633 char '{'
628634 spnl
@@ -630,28 +636,28 @@ attributes = try $ do
630636 char '}'
631637 return $ foldl (\x f -> f x) nullAttr attrs
632638
633−attribute :: MarkdownParser (Attr -> Attr)
639+attribute :: PandocMonad m => MarkdownParser m (Attr -> Attr)
634640attribute = identifierAttr <|> classAttr <|> keyValAttr <|> specialAttr
635641
636−identifier :: MarkdownParser String
642+identifier :: PandocMonad m => MarkdownParser m String
637643identifier = do
638644 first <- letter
639645 rest <- many $ alphaNum <|> oneOf "-_:."
640646 return (first:rest)
641647
642−identifierAttr :: MarkdownParser (Attr -> Attr)
648+identifierAttr :: PandocMonad m => MarkdownParser m (Attr -> Attr)
643649identifierAttr = try $ do
644650 char '#'
645651 result <- identifier
646652 return $ \(_,cs,kvs) -> (result,cs,kvs)
647653
648−classAttr :: MarkdownParser (Attr -> Attr)
654+classAttr :: PandocMonad m => MarkdownParser m (Attr -> Attr)
649655classAttr = try $ do
650656 char '.'
651657 result <- identifier
652658 return $ \(id',cs,kvs) -> (id',cs ++ [result],kvs)
653659
654−keyValAttr :: MarkdownParser (Attr -> Attr)
660+keyValAttr :: PandocMonad m => MarkdownParser m (Attr -> Attr)
655661keyValAttr = try $ do
656662 key <- identifier
657663 char '='
@@ -664,12 +670,12 @@ keyValAttr = try $ do
664670 "class" -> (id',cs ++ words val,kvs)
665671 _ -> (id',cs,kvs ++ [(key,val)])
666672
667−specialAttr :: MarkdownParser (Attr -> Attr)
673+specialAttr :: PandocMonad m => MarkdownParser m (Attr -> Attr)
668674specialAttr = do
669675 char '-'
670676 return $ \(id',cs,kvs) -> (id',cs ++ ["unnumbered"],kvs)
671677
672−codeBlockFenced :: MarkdownParser (F Blocks)
678+codeBlockFenced :: PandocMonad m => MarkdownParser m (F Blocks)
673679codeBlockFenced = try $ do
674680 c <- try (guardEnabled Ext_fenced_code_blocks >> lookAhead (char '~'))
675681 <|> (guardEnabled Ext_backtick_code_blocks >> lookAhead (char '`'))
@@ -690,7 +696,7 @@ toLanguageId = map toLower . go
690696 go "objective-c" = "objectivec"
691697 go x = x
692698
693−codeBlockIndented :: MarkdownParser (F Blocks)
699+codeBlockIndented :: PandocMonad m => MarkdownParser m (F Blocks)
694700codeBlockIndented = do
695701 contents <- many1 (indentedLine <|>
696702 try (do b <- blanklines
@@ -701,7 +707,7 @@ codeBlockIndented = do
701707 return $ return $ B.codeBlockWith ("", classes, []) $
702708 stripTrailingNewlines $ concat contents
703709
704−lhsCodeBlock :: MarkdownParser (F Blocks)
710+lhsCodeBlock :: PandocMonad m => MarkdownParser m (F Blocks)
705711lhsCodeBlock = do
706712 guardEnabled Ext_literate_haskell
707713 (return . B.codeBlockWith ("",["sourceCode","literate","haskell"],[]) <$>
@@ -709,7 +715,7 @@ lhsCodeBlock = do
709715 <|> (return . B.codeBlockWith ("",["sourceCode","haskell"],[]) <$>
710716 lhsCodeBlockInverseBird)
711717
712−lhsCodeBlockLaTeX :: MarkdownParser String
718+lhsCodeBlockLaTeX :: PandocMonad m => MarkdownParser m String
713719lhsCodeBlockLaTeX = try $ do
714720 string "\\begin{code}"
715721 manyTill spaceChar newline
@@ -717,13 +723,13 @@ lhsCodeBlockLaTeX = try $ do
717723 blanklines
718724 return $ stripTrailingNewlines contents
719725
720−lhsCodeBlockBird :: MarkdownParser String
726+lhsCodeBlockBird :: PandocMonad m => MarkdownParser m String
721727lhsCodeBlockBird = lhsCodeBlockBirdWith '>'
722728
723−lhsCodeBlockInverseBird :: MarkdownParser String
729+lhsCodeBlockInverseBird :: PandocMonad m => MarkdownParser m String
724730lhsCodeBlockInverseBird = lhsCodeBlockBirdWith '<'
725731
726−lhsCodeBlockBirdWith :: Char -> MarkdownParser String
732+lhsCodeBlockBirdWith :: PandocMonad m => Char -> MarkdownParser m String
727733lhsCodeBlockBirdWith c = try $ do
728734 pos <- getPosition
729735 when (sourceColumn pos /= 1) $ fail "Not in first column"
@@ -735,7 +741,7 @@ lhsCodeBlockBirdWith c = try $ do
735741 blanklines
736742 return $ intercalate "\n" lns'
737743
738−birdTrackLine :: Char -> Parser [Char] st String
744+birdTrackLine :: PandocMonad m => Char -> ParserT [Char] st m String
739745birdTrackLine c = try $ do
740746 char c
741747 -- allow html tags on left margin:
@@ -746,10 +752,10 @@ birdTrackLine c = try $ do
746752-- block quotes
747753--
748754
749−emailBlockQuoteStart :: MarkdownParser Char
755+emailBlockQuoteStart :: PandocMonad m => MarkdownParser m Char
750756emailBlockQuoteStart = try $ skipNonindentSpaces >> char '>' <* optional (char ' ')
751757
752−emailBlockQuote :: MarkdownParser [String]
758+emailBlockQuote :: PandocMonad m => MarkdownParser m [String]
753759emailBlockQuote = try $ do
754760 emailBlockQuoteStart
755761 let emailLine = many $ nonEndline <|> try
@@ -763,7 +769,7 @@ emailBlockQuote = try $ do
763769 optional blanklines
764770 return raw
765771
766−blockQuote :: MarkdownParser (F Blocks)
772+blockQuote :: PandocMonad m => MarkdownParser m (F Blocks)
767773blockQuote = do
768774 raw <- emailBlockQuote
769775 -- parse the extracted block, which may contain various block elements:
@@ -774,7 +780,7 @@ blockQuote = do
774780-- list blocks
775781--
776782
777−bulletListStart :: MarkdownParser ()
783+bulletListStart :: PandocMonad m => MarkdownParser m ()
778784bulletListStart = try $ do
779785 optional newline -- if preceded by a Plain block in a list context
780786 startpos <- sourceColumn <$> getPosition
@@ -786,7 +792,7 @@ bulletListStart = try $ do
786792 lookAhead (newline <|> spaceChar)
787793 () <$ atMostSpaces (tabStop - (endpos - startpos))
788794
789−anyOrderedListStart :: MarkdownParser (Int, ListNumberStyle, ListNumberDelim)
795+anyOrderedListStart :: PandocMonad m => MarkdownParser m (Int, ListNumberStyle, ListNumberDelim)
790796anyOrderedListStart = try $ do
791797 optional newline -- if preceded by a Plain block in a list context
792798 startpos <- sourceColumn <$> getPosition
@@ -810,10 +816,10 @@ anyOrderedListStart = try $ do
810816 atMostSpaces (tabStop - (endpos - startpos))
811817 return res
812818
813−listStart :: MarkdownParser ()
819+listStart :: PandocMonad m => MarkdownParser m ()
814820listStart = bulletListStart <|> (anyOrderedListStart >> return ())
815821
816−listLine :: MarkdownParser String
822+listLine :: PandocMonad m => MarkdownParser m String
817823listLine = try $ do
818824 notFollowedBy' (do indentSpaces
819825 many spaceChar
@@ -822,7 +828,7 @@ listLine = try $ do
822828 optional (() <$ indentSpaces)
823829 listLineCommon
824830
825−listLineCommon :: MarkdownParser String
831+listLineCommon :: PandocMonad m => MarkdownParser m String
826832listLineCommon = concat <$> manyTill
827833 ( many1 (satisfy $ \c -> c /= '\n' && c /= '<')
828834 <|> liftM snd (htmlTag isCommentTag)
@@ -830,8 +836,9 @@ listLineCommon = concat <$> manyTill
830836 ) newline
831837
832838-- parse raw text for one list item, excluding start marker and continuations
833−rawListItem :: MarkdownParser a
834− -> MarkdownParser String
839+rawListItem :: PandocMonad m
840+ => MarkdownParser m a
841+ -> MarkdownParser m String
835842rawListItem start = try $ do
836843 start
837844 first <- listLineCommon
@@ -842,21 +849,21 @@ rawListItem start = try $ do
842849-- continuation of a list item - indented and separated by blankline
843850-- or (in compact lists) endline.
844851-- note: nested lists are parsed as continuations
845−listContinuation :: MarkdownParser String
852+listContinuation :: PandocMonad m => MarkdownParser m String
846853listContinuation = try $ do
847854 lookAhead indentSpaces
848855 result <- many1 listContinuationLine
849856 blanks <- many blankline
850857 return $ concat result ++ blanks
851858
852−notFollowedByHtmlCloser :: MarkdownParser ()
859+notFollowedByHtmlCloser :: PandocMonad m => MarkdownParser m ()
853860notFollowedByHtmlCloser = do
854861 inHtmlBlock <- stateInHtmlBlock <$> getState
855862 case inHtmlBlock of
856863 Just t -> notFollowedBy' $ htmlTag (~== TagClose t)
857864 Nothing -> return ()
858865
859−listContinuationLine :: MarkdownParser String
866+listContinuationLine :: PandocMonad m => MarkdownParser m String
860867listContinuationLine = try $ do
861868 notFollowedBy blankline
862869 notFollowedBy' listStart
@@ -865,8 +872,9 @@ listContinuationLine = try $ do
865872 result <- anyLine
866873 return $ result ++ "\n"
867874
868−listItem :: MarkdownParser a
869− -> MarkdownParser (F Blocks)
875+listItem :: PandocMonad m
876+ => MarkdownParser m a
877+ -> MarkdownParser m (F Blocks)
870878listItem start = try $ do
871879 first <- rawListItem start
872880 continuations <- many listContinuation
@@ -882,7 +890,7 @@ listItem start = try $ do
882890 updateState (\st -> st {stateParserContext = oldContext})
883891 return contents
884892
885−orderedList :: MarkdownParser (F Blocks)
893+orderedList :: PandocMonad m => MarkdownParser m (F Blocks)
886894orderedList = try $ do
887895 (start, style, delim) <- lookAhead anyOrderedListStart
888896 unless (style `elem` [DefaultStyle, Decimal, Example] &&
@@ -901,16 +909,16 @@ orderedList = try $ do
901909 atMostSpaces (tabStop - (endpos - startpos))
902910 return res )
903911 start' <- option 1 $ guardEnabled Ext_startnum >> return start
904− return $ B.orderedListWith (start', style, delim) <$> fmap compactify' items
912+ return $ B.orderedListWith (start', style, delim) <$> fmap compactify items
905913
906−bulletList :: MarkdownParser (F Blocks)
914+bulletList :: PandocMonad m => MarkdownParser m (F Blocks)
907915bulletList = do
908916 items <- fmap sequence $ many1 $ listItem bulletListStart
909− return $ B.bulletList <$> fmap compactify' items
917+ return $ B.bulletList <$> fmap compactify items
910918
911919-- definition lists
912920
913−defListMarker :: MarkdownParser ()
921+defListMarker :: PandocMonad m => MarkdownParser m ()
914922defListMarker = do
915923 sps <- nonindentSpaces
916924 char ':' <|> char '~'
@@ -921,7 +929,7 @@ defListMarker = do
921929 else mzero
922930 return ()
923931
924−definitionListItem :: Bool -> MarkdownParser (F (Inlines, [Blocks]))
932+definitionListItem :: PandocMonad m => Bool -> MarkdownParser m (F (Inlines, [Blocks]))
925933definitionListItem compact = try $ do
926934 rawLine' <- anyLine
927935 raw <- many1 $ defRawBlock compact
@@ -930,7 +938,7 @@ definitionListItem compact = try $ do
930938 optional blanklines
931939 return $ liftM2 (,) term (sequence contents)
932940
933−defRawBlock :: Bool -> MarkdownParser String
941+defRawBlock :: PandocMonad m => Bool -> MarkdownParser m String
934942defRawBlock compact = try $ do
935943 hasBlank <- option False $ blankline >> return True
936944 defListMarker
@@ -952,7 +960,7 @@ defRawBlock compact = try $ do
952960 return $ trimr (firstline ++ "\n" ++ unlines rawlines ++ cont) ++
953961 if hasBlank || not (null cont) then "\n\n" else ""
954962
955−definitionList :: MarkdownParser (F Blocks)
963+definitionList :: PandocMonad m => MarkdownParser m (F Blocks)
956964definitionList = try $ do
957965 lookAhead (anyLine >>
958966 optional (blankline >> notFollowedBy (table >> return ())) >>
@@ -960,13 +968,13 @@ definitionList = try $ do
960968 defListMarker)
961969 compactDefinitionList <|> normalDefinitionList
962970
963−compactDefinitionList :: MarkdownParser (F Blocks)
971+compactDefinitionList :: PandocMonad m => MarkdownParser m (F Blocks)
964972compactDefinitionList = do
965973 guardEnabled Ext_compact_definition_lists
966974 items <- fmap sequence $ many1 $ definitionListItem True
967− return $ B.definitionList <$> fmap compactify'DL items
975+ return $ B.definitionList <$> fmap compactifyDL items
968976
969−normalDefinitionList :: MarkdownParser (F Blocks)
977+normalDefinitionList :: PandocMonad m => MarkdownParser m (F Blocks)
970978normalDefinitionList = do
971979 guardEnabled Ext_definition_lists
972980 items <- fmap sequence $ many1 $ definitionListItem False
@@ -976,7 +984,7 @@ normalDefinitionList = do
976984-- paragraph block
977985--
978986
979−para :: MarkdownParser (F Blocks)
987+para :: PandocMonad m => MarkdownParser m (F Blocks)
980988para = try $ do
981989 exts <- getOption readerExtensions
982990 result <- trimInlinesF . mconcat <$> many1 inline
@@ -1001,25 +1009,25 @@ para = try $ do
10011009 result' <- result
10021010 case B.toList result' of
10031011 [Image attr alt (src,tit)]
1004− | Ext_implicit_figures `Set.member` exts ->
1012+ | Ext_implicit_figures `extensionEnabled` exts ->
10051013 -- the fig: at beginning of title indicates a figure
10061014 return $ B.para $ B.singleton
10071015 $ Image attr alt (src,'f':'i':'g':':':tit)
10081016 _ -> return $ B.para result'
10091017
1010−plain :: MarkdownParser (F Blocks)
1018+plain :: PandocMonad m => MarkdownParser m (F Blocks)
10111019plain = fmap B.plain . trimInlinesF . mconcat <$> many1 inline
10121020
10131021--
10141022-- raw html
10151023--
10161024
1017−htmlElement :: MarkdownParser String
1025+htmlElement :: PandocMonad m => MarkdownParser m String
10181026htmlElement = rawVerbatimBlock
10191027 <|> strictHtmlBlock
10201028 <|> liftM snd (htmlTag isBlockTag)
10211029
1022−htmlBlock :: MarkdownParser (F Blocks)
1030+htmlBlock :: PandocMonad m => MarkdownParser m (F Blocks)
10231031htmlBlock = do
10241032 guardEnabled Ext_raw_html
10251033 try (do
@@ -1044,24 +1052,24 @@ htmlBlock = do
10441052 <|> (guardEnabled Ext_markdown_in_html_blocks >> rawHtmlBlocks))
10451053 <|> htmlBlock'
10461054
1047−htmlBlock' :: MarkdownParser (F Blocks)
1055+htmlBlock' :: PandocMonad m => MarkdownParser m (F Blocks)
10481056htmlBlock' = try $ do
10491057 first <- htmlElement
10501058 skipMany spaceChar
10511059 optional blanklines
10521060 return $ return $ B.rawBlock "html" first
10531061
1054−strictHtmlBlock :: MarkdownParser String
1062+strictHtmlBlock :: PandocMonad m => MarkdownParser m String
10551063strictHtmlBlock = htmlInBalanced (not . isInlineTag)
10561064
1057−rawVerbatimBlock :: MarkdownParser String
1065+rawVerbatimBlock :: PandocMonad m => MarkdownParser m String
10581066rawVerbatimBlock = htmlInBalanced isVerbTag
10591067 where isVerbTag (TagOpen "pre" _) = True
10601068 isVerbTag (TagOpen "style" _) = True
10611069 isVerbTag (TagOpen "script" _) = True
10621070 isVerbTag _ = False
10631071
1064−rawTeXBlock :: MarkdownParser (F Blocks)
1072+rawTeXBlock :: PandocMonad m => MarkdownParser m (F Blocks)
10651073rawTeXBlock = do
10661074 guardEnabled Ext_raw_tex
10671075 result <- (B.rawBlock "latex" . concat <$>
@@ -1071,7 +1079,7 @@ rawTeXBlock = do
10711079 spaces
10721080 return $ return result
10731081
1074−rawHtmlBlocks :: MarkdownParser (F Blocks)
1082+rawHtmlBlocks :: PandocMonad m => MarkdownParser m (F Blocks)
10751083rawHtmlBlocks = do
10761084 (TagOpen tagtype _, raw) <- htmlTag isBlockTag
10771085 -- try to find closing tag
@@ -1101,7 +1109,7 @@ stripMarkdownAttribute s = renderTags' $ map filterAttrib $ parseTags s
11011109-- line block
11021110--
11031111
1104−lineBlock :: MarkdownParser (F Blocks)
1112+lineBlock :: PandocMonad m => MarkdownParser m (F Blocks)
11051113lineBlock = try $ do
11061114 guardEnabled Ext_line_blocks
11071115 lines' <- lineBlockLines >>=
@@ -1114,8 +1122,9 @@ lineBlock = try $ do
11141122
11151123-- Parse a dashed line with optional trailing spaces; return its length
11161124-- and the length including trailing space.
1117−dashedLine :: Char
1118− -> Parser [Char] st (Int, Int)
1125+dashedLine :: PandocMonad m
1126+ => Char
1127+ -> ParserT [Char] st m (Int, Int)
11191128dashedLine ch = do
11201129 dashes <- many1 (char ch)
11211130 sp <- many spaceChar
@@ -1125,8 +1134,9 @@ dashedLine ch = do
11251134
11261135-- Parse a table header with dashed lines of '-' preceded by
11271136-- one (or zero) line of text.
1128−simpleTableHeader :: Bool -- ^ Headerless table
1129− -> MarkdownParser (F [Blocks], [Alignment], [Int])
1137+simpleTableHeader :: PandocMonad m
1138+ => Bool -- ^ Headerless table
1139+ -> MarkdownParser m (F [Blocks], [Alignment], [Int])
11301140simpleTableHeader headless = try $ do
11311141 rawContent <- if headless
11321142 then return ""
@@ -1170,16 +1180,17 @@ alignType strLst len =
11701180 (False, False) -> AlignDefault
11711181
11721182-- Parse a table footer - dashed lines followed by blank line.
1173−tableFooter :: MarkdownParser String
1183+tableFooter :: PandocMonad m => MarkdownParser m String
11741184tableFooter = try $ skipNonindentSpaces >> many1 (dashedLine '-') >> blanklines
11751185
11761186-- Parse a table separator - dashed line.
1177−tableSep :: MarkdownParser Char
1187+tableSep :: PandocMonad m => MarkdownParser m Char
11781188tableSep = try $ skipNonindentSpaces >> many1 (dashedLine '-') >> char '\n'
11791189
11801190-- Parse a raw line and split it into chunks by indices.
1181−rawTableLine :: [Int]
1182− -> MarkdownParser [String]
1191+rawTableLine :: PandocMonad m
1192+ => [Int]
1193+ -> MarkdownParser m [String]
11831194rawTableLine indices = do
11841195 notFollowedBy' (blanklines <|> tableFooter)
11851196 line <- many1Till anyChar newline
@@ -1187,14 +1198,16 @@ rawTableLine indices = do
11871198 splitStringByIndices (init indices) line
11881199
11891200-- Parse a table line and return a list of lists of blocks (columns).
1190−tableLine :: [Int]
1191− -> MarkdownParser (F [Blocks])
1201+tableLine :: PandocMonad m
1202+ => [Int]
1203+ -> MarkdownParser m (F [Blocks])
11921204tableLine indices = rawTableLine indices >>=
11931205 fmap sequence . mapM (parseFromString (mconcat <$> many plain))
11941206
11951207-- Parse a multiline table row and return a list of blocks (columns).
1196−multilineRow :: [Int]
1197− -> MarkdownParser (F [Blocks])
1208+multilineRow :: PandocMonad m
1209+ => [Int]
1210+ -> MarkdownParser m (F [Blocks])
11981211multilineRow indices = do
11991212 colLines <- many1 (rawTableLine indices)
12001213 let cols = map unlines $ transpose colLines
@@ -1202,7 +1215,7 @@ multilineRow indices = do
12021215
12031216-- Parses a table caption: inlines beginning with 'Table:'
12041217-- and followed by blank lines.
1205−tableCaption :: MarkdownParser (F Inlines)
1218+tableCaption :: PandocMonad m => MarkdownParser m (F Inlines)
12061219tableCaption = try $ do
12071220 guardEnabled Ext_table_captions
12081221 skipNonindentSpaces
@@ -1210,8 +1223,9 @@ tableCaption = try $ do
12101223 trimInlinesF . mconcat <$> many1 inline <* blanklines
12111224
12121225-- Parse a simple table with '---' header and one line per row.
1213−simpleTable :: Bool -- ^ Headerless table
1214− -> MarkdownParser ([Alignment], [Double], F [Blocks], F [[Blocks]])
1226+simpleTable :: PandocMonad m
1227+ => Bool -- ^ Headerless table
1228+ -> MarkdownParser m ([Alignment], [Double], F [Blocks], F [[Blocks]])
12151229simpleTable headless = do
12161230 (aligns, _widths, heads', lines') <-
12171231 tableWith (simpleTableHeader headless) tableLine
@@ -1224,13 +1238,15 @@ simpleTable headless = do
12241238-- (which may be multiline), then the rows,
12251239-- which may be multiline, separated by blank lines, and
12261240-- ending with a footer (dashed line followed by blank line).
1227−multilineTable :: Bool -- ^ Headerless table
1228− -> MarkdownParser ([Alignment], [Double], F [Blocks], F [[Blocks]])
1241+multilineTable :: PandocMonad m
1242+ => Bool -- ^ Headerless table
1243+ -> MarkdownParser m ([Alignment], [Double], F [Blocks], F [[Blocks]])
12291244multilineTable headless =
12301245 tableWith (multilineTableHeader headless) multilineRow blanklines tableFooter
12311246
1232−multilineTableHeader :: Bool -- ^ Headerless table
1233− -> MarkdownParser (F [Blocks], [Alignment], [Int])
1247+multilineTableHeader :: PandocMonad m
1248+ => Bool -- ^ Headerless table
1249+ -> MarkdownParser m (F [Blocks], [Alignment], [Int])
12341250multilineTableHeader headless = try $ do
12351251 unless headless $
12361252 tableSep >> notFollowedBy blankline
@@ -1261,8 +1277,8 @@ multilineTableHeader headless = try $ do
12611277-- (which may be grid), then the rows,
12621278-- which may be grid, separated by blank lines, and
12631279-- ending with a footer (dashed line followed by blank line).
1264−gridTable :: Bool -- ^ Headerless table
1265− -> MarkdownParser ([Alignment], [Double], F [Blocks], F [[Blocks]])
1280+gridTable :: PandocMonad m => Bool -- ^ Headerless table
1281+ -> MarkdownParser m ([Alignment], [Double], F [Blocks], F [[Blocks]])
12661282gridTable headless =
12671283 tableWith (gridTableHeader headless) gridTableRow
12681284 (gridTableSep '-') gridTableFooter
@@ -1271,7 +1287,7 @@ gridTableSplitLine :: [Int] -> String -> [String]
12711287gridTableSplitLine indices line = map removeFinalBar $ tail $
12721288 splitStringByIndices (init indices) $ trimr line
12731289
1274−gridPart :: Char -> Parser [Char] st ((Int, Int), Alignment)
1290+gridPart :: PandocMonad m => Char -> ParserT [Char] st m ((Int, Int), Alignment)
12751291gridPart ch = do
12761292 leftColon <- option False (True <$ char ':')
12771293 dashes <- many1 (char ch)
@@ -1286,7 +1302,7 @@ gridPart ch = do
12861302 (False, False) -> AlignDefault
12871303 return ((lengthDashes, lengthDashes + 1), alignment)
12881304
1289−gridDashedLines :: Char -> Parser [Char] st [((Int, Int), Alignment)]
1305+gridDashedLines :: PandocMonad m => Char -> ParserT [Char] st m [((Int, Int), Alignment)]
12901306gridDashedLines ch = try $ char '+' >> many1 (gridPart ch) <* blankline
12911307
12921308removeFinalBar :: String -> String
@@ -1294,12 +1310,12 @@ removeFinalBar =
12941310 reverse . dropWhile (`elem` " \t") . dropWhile (=='|') . reverse
12951311
12961312-- | Separator between rows of grid table.
1297−gridTableSep :: Char -> MarkdownParser Char
1313+gridTableSep :: PandocMonad m => Char -> MarkdownParser m Char
12981314gridTableSep ch = try $ gridDashedLines ch >> return '\n'
12991315
13001316-- | Parse header for a grid table.
1301−gridTableHeader :: Bool -- ^ Headerless table
1302− -> MarkdownParser (F [Blocks], [Alignment], [Int])
1317+gridTableHeader :: PandocMonad m => Bool -- ^ Headerless table
1318+ -> MarkdownParser m (F [Blocks], [Alignment], [Int])
13031319gridTableHeader headless = try $ do
13041320 optional blanklines
13051321 dashes <- gridDashedLines '-'
@@ -1320,20 +1336,20 @@ gridTableHeader headless = try $ do
13201336 heads <- fmap sequence $ mapM (parseFromString parseBlocks . trim) rawHeads
13211337 return (heads, aligns, indices)
13221338
1323−gridTableRawLine :: [Int] -> MarkdownParser [String]
1339+gridTableRawLine :: PandocMonad m => [Int] -> MarkdownParser m [String]
13241340gridTableRawLine indices = do
13251341 char '|'
13261342 line <- anyLine
13271343 return (gridTableSplitLine indices line)
13281344
13291345-- | Parse row of grid table.
1330−gridTableRow :: [Int]
1331− -> MarkdownParser (F [Blocks])
1346+gridTableRow :: PandocMonad m => [Int]
1347+ -> MarkdownParser m (F [Blocks])
13321348gridTableRow indices = do
13331349 colLines <- many1 (gridTableRawLine indices)
13341350 let cols = map ((++ "\n") . unlines . removeOneLeadingSpace) $
13351351 transpose colLines
1336− fmap compactify' <$> fmap sequence (mapM (parseFromString parseBlocks) cols)
1352+ fmap compactify <$> fmap sequence (mapM (parseFromString parseBlocks) cols)
13371353
13381354removeOneLeadingSpace :: [String] -> [String]
13391355removeOneLeadingSpace xs =
@@ -1344,10 +1360,10 @@ removeOneLeadingSpace xs =
13441360 startsWithSpace (y:_) = y == ' '
13451361
13461362-- | Parse footer for a grid table.
1347−gridTableFooter :: MarkdownParser [Char]
1363+gridTableFooter :: PandocMonad m => MarkdownParser m [Char]
13481364gridTableFooter = blanklines
13491365
1350−pipeBreak :: MarkdownParser ([Alignment], [Int])
1366+pipeBreak :: PandocMonad m => MarkdownParser m ([Alignment], [Int])
13511367pipeBreak = try $ do
13521368 nonindentSpaces
13531369 openPipe <- (True <$ char '|') <|> return False
@@ -1359,7 +1375,7 @@ pipeBreak = try $ do
13591375 blankline
13601376 return $ unzip (first:rest)
13611377
1362−pipeTable :: MarkdownParser ([Alignment], [Double], F [Blocks], F [[Blocks]])
1378+pipeTable :: PandocMonad m => MarkdownParser m ([Alignment], [Double], F [Blocks], F [[Blocks]])
13631379pipeTable = try $ do
13641380 nonindentSpaces
13651381 lookAhead nonspaceChar
@@ -1377,13 +1393,13 @@ pipeTable = try $ do
13771393 else replicate (length aligns) 0.0
13781394 return $ (aligns, widths, heads', sequence lines'')
13791395
1380−sepPipe :: MarkdownParser ()
1396+sepPipe :: PandocMonad m => MarkdownParser m ()
13811397sepPipe = try $ do
13821398 char '|' <|> char '+'
13831399 notFollowedBy blankline
13841400
13851401-- parse a row, also returning probable alignments for org-table cells
1386−pipeTableRow :: MarkdownParser (F [Blocks])
1402+pipeTableRow :: PandocMonad m => MarkdownParser m (F [Blocks])
13871403pipeTableRow = try $ do
13881404 scanForPipe
13891405 skipMany spaceChar
@@ -1399,14 +1415,14 @@ pipeTableRow = try $ do
13991415 blankline
14001416 return $ sequence cells
14011417
1402−pipeTableCell :: MarkdownParser (F Blocks)
1418+pipeTableCell :: PandocMonad m => MarkdownParser m (F Blocks)
14031419pipeTableCell = do
14041420 result <- many inline
14051421 if null result
14061422 then return mempty
14071423 else return $ B.plain . mconcat <$> sequence result
14081424
1409−pipeTableHeaderPart :: Parser [Char] st (Alignment, Int)
1425+pipeTableHeaderPart :: PandocMonad m => ParserT [Char] st m (Alignment, Int)
14101426pipeTableHeaderPart = try $ do
14111427 skipMany spaceChar
14121428 left <- optionMaybe (char ':')
@@ -1422,7 +1438,7 @@ pipeTableHeaderPart = try $ do
14221438 (Just _,Just _) -> AlignCenter), len)
14231439
14241440-- Succeed only if current line contains a pipe.
1425−scanForPipe :: Parser [Char] st ()
1441+scanForPipe :: PandocMonad m => ParserT [Char] st m ()
14261442scanForPipe = do
14271443 inp <- getInput
14281444 case break (\c -> c == '\n' || c == '|') inp of
@@ -1432,11 +1448,12 @@ scanForPipe = do
14321448-- | Parse a table using 'headerParser', 'rowParser',
14331449-- 'lineParser', and 'footerParser'. Variant of the version in
14341450-- Text.Pandoc.Parsing.
1435−tableWith :: MarkdownParser (F [Blocks], [Alignment], [Int])
1436− -> ([Int] -> MarkdownParser (F [Blocks]))
1437− -> MarkdownParser sep
1438− -> MarkdownParser end
1439− -> MarkdownParser ([Alignment], [Double], F [Blocks], F [[Blocks]])
1451+tableWith :: PandocMonad m
1452+ => MarkdownParser m (F [Blocks], [Alignment], [Int])
1453+ -> ([Int] -> MarkdownParser m (F [Blocks]))
1454+ -> MarkdownParser m sep
1455+ -> MarkdownParser m end
1456+ -> MarkdownParser m ([Alignment], [Double], F [Blocks], F [[Blocks]])
14401457tableWith headerParser rowParser lineParser footerParser = try $ do
14411458 (heads, aligns, indices) <- headerParser
14421459 lines' <- fmap sequence $ rowParser indices `sepEndBy1` lineParser
@@ -1447,7 +1464,7 @@ tableWith headerParser rowParser lineParser footerParser = try $ do
14471464 else widthsFromIndices numColumns indices
14481465 return $ (aligns, widths, heads, lines')
14491466
1450−table :: MarkdownParser (F Blocks)
1467+table :: PandocMonad m => MarkdownParser m (F Blocks)
14511468table = try $ do
14521469 frontCaption <- option Nothing (Just <$> tableCaption)
14531470 (aligns, widths, heads, lns) <-
@@ -1479,7 +1496,7 @@ table = try $ do
14791496-- inline
14801497--
14811498
1482−inline :: MarkdownParser (F Inlines)
1499+inline :: PandocMonad m => MarkdownParser m (F Inlines)
14831500inline = choice [ whitespace
14841501 , bareURL
14851502 , str
@@ -1509,7 +1526,7 @@ inline = choice [ whitespace
15091526 , ltSign
15101527 ] <?> "inline"
15111528
1512−escapedChar' :: MarkdownParser Char
1529+escapedChar' :: PandocMonad m => MarkdownParser m Char
15131530escapedChar' = try $ do
15141531 char '\\'
15151532 (guardEnabled Ext_all_symbols_escapable >> satisfy (not . isAlphaNum))
@@ -1518,7 +1535,7 @@ escapedChar' = try $ do
15181535 <|> (guardEnabled Ext_escaped_line_breaks >> char '\n')
15191536 <|> oneOf "\\`*_{}[]()>#+-.!~\""
15201537
1521−escapedChar :: MarkdownParser (F Inlines)
1538+escapedChar :: PandocMonad m => MarkdownParser m (F Inlines)
15221539escapedChar = do
15231540 result <- escapedChar'
15241541 case result of
@@ -1527,14 +1544,14 @@ escapedChar = do
15271544 return (return B.linebreak) -- "\[newline]" is a linebreak
15281545 _ -> return $ return $ B.str [result]
15291546
1530−ltSign :: MarkdownParser (F Inlines)
1547+ltSign :: PandocMonad m => MarkdownParser m (F Inlines)
15311548ltSign = do
15321549 guardDisabled Ext_raw_html
15331550 <|> (notFollowedByHtmlCloser >> notFollowedBy' (htmlTag isBlockTag))
15341551 char '<'
15351552 return $ return $ B.str "<"
15361553
1537−exampleRef :: MarkdownParser (F Inlines)
1554+exampleRef :: PandocMonad m => MarkdownParser m (F Inlines)
15381555exampleRef = try $ do
15391556 guardEnabled Ext_example_lists
15401557 char '@'
@@ -1545,7 +1562,7 @@ exampleRef = try $ do
15451562 Just n -> B.str (show n)
15461563 Nothing -> B.str ('@':lab)
15471564
1548−symbol :: MarkdownParser (F Inlines)
1565+symbol :: PandocMonad m => MarkdownParser m (F Inlines)
15491566symbol = do
15501567 result <- noneOf "<\\\n\t "
15511568 <|> try (do lookAhead $ char '\\'
@@ -1554,7 +1571,7 @@ symbol = do
15541571 return $ return $ B.str [result]
15551572
15561573-- parses inline code, between n `s and n `s
1557−code :: MarkdownParser (F Inlines)
1574+code :: PandocMonad m => MarkdownParser m (F Inlines)
15581575code = try $ do
15591576 starts <- many1 (char '`')
15601577 skipSpaces
@@ -1566,16 +1583,17 @@ code = try $ do
15661583 >> attributes)
15671584 return $ return $ B.codeWith attr $ trim $ concat result
15681585
1569−math :: MarkdownParser (F Inlines)
1586+math :: PandocMonad m => MarkdownParser m (F Inlines)
15701587math = (return . B.displayMath <$> (mathDisplay >>= applyMacros'))
15711588 <|> (return . B.math <$> (mathInline >>= applyMacros')) <+?>
1572− ((getOption readerSmart >>= guard) *> (return <$> apostrophe)
1589+ (guardEnabled Ext_smart *> (return <$> apostrophe)
15731590 <* notFollowedBy (space <|> satisfy isPunctuation))
15741591
15751592-- Parses material enclosed in *s, **s, _s, or __s.
15761593-- Designed to avoid backtracking.
1577−enclosure :: Char
1578− -> MarkdownParser (F Inlines)
1594+enclosure :: PandocMonad m
1595+ => Char
1596+ -> MarkdownParser m (F Inlines)
15791597enclosure c = do
15801598 -- we can't start an enclosure with _ if after a string and
15811599 -- the intraword_underscores extension is enabled:
@@ -1591,7 +1609,7 @@ enclosure c = do
15911609 1 -> one c mempty
15921610 _ -> return (return $ B.str cs)
15931611
1594−ender :: Char -> Int -> MarkdownParser ()
1612+ender :: PandocMonad m => Char -> Int -> MarkdownParser m ()
15951613ender c n = try $ do
15961614 count n (char c)
15971615 guard (c == '*')
@@ -1602,7 +1620,7 @@ ender c n = try $ do
16021620-- If one c, emit emph and then parse two.
16031621-- If two cs, emit strong and then parse one.
16041622-- Otherwise, emit ccc then the results.
1605−three :: Char -> MarkdownParser (F Inlines)
1623+three :: PandocMonad m => Char -> MarkdownParser m (F Inlines)
16061624three c = do
16071625 contents <- mconcat <$> many (notFollowedBy (ender c 1) >> inline)
16081626 (ender c 3 >> return ((B.strong . B.emph) <$> contents))
@@ -1612,7 +1630,7 @@ three c = do
16121630
16131631-- Parse inlines til you hit two c's, and emit strong.
16141632-- If you never do hit two cs, emit ** plus inlines parsed.
1615−two :: Char -> F Inlines -> MarkdownParser (F Inlines)
1633+two :: PandocMonad m => Char -> F Inlines -> MarkdownParser m (F Inlines)
16161634two c prefix' = do
16171635 contents <- mconcat <$> many (try $ notFollowedBy (ender c 2) >> inline)
16181636 (ender c 2 >> return (B.strong <$> (prefix' <> contents)))
@@ -1620,7 +1638,7 @@ two c prefix' = do
16201638
16211639-- Parse inlines til you hit a c, and emit emph.
16221640-- If you never hit a c, emit * plus inlines parsed.
1623−one :: Char -> F Inlines -> MarkdownParser (F Inlines)
1641+one :: PandocMonad m => Char -> F Inlines -> MarkdownParser m (F Inlines)
16241642one c prefix' = do
16251643 contents <- mconcat <$> many ( (notFollowedBy (ender c 1) >> inline)
16261644 <|> try (string [c,c] >>
@@ -1629,52 +1647,53 @@ one c prefix' = do
16291647 (ender c 1 >> return (B.emph <$> (prefix' <> contents)))
16301648 <|> return (return (B.str [c]) <> (prefix' <> contents))
16311649
1632−strongOrEmph :: MarkdownParser (F Inlines)
1650+strongOrEmph :: PandocMonad m => MarkdownParser m (F Inlines)
16331651strongOrEmph = enclosure '*' <|> enclosure '_'
16341652
16351653-- | Parses a list of inlines between start and end delimiters.
1636−inlinesBetween :: (Show b)
1637− => MarkdownParser a
1638− -> MarkdownParser b
1639− -> MarkdownParser (F Inlines)
1654+inlinesBetween :: PandocMonad m
1655+ => (Show b)
1656+ => MarkdownParser m a
1657+ -> MarkdownParser m b
1658+ -> MarkdownParser m (F Inlines)
16401659inlinesBetween start end =
16411660 (trimInlinesF . mconcat) <$> try (start >> many1Till inner end)
16421661 where inner = innerSpace <|> (notFollowedBy' (() <$ whitespace) >> inline)
16431662 innerSpace = try $ whitespace <* notFollowedBy' end
16441663
1645−strikeout :: MarkdownParser (F Inlines)
1664+strikeout :: PandocMonad m => MarkdownParser m (F Inlines)
16461665strikeout = fmap B.strikeout <$>
16471666 (guardEnabled Ext_strikeout >> inlinesBetween strikeStart strikeEnd)
16481667 where strikeStart = string "~~" >> lookAhead nonspaceChar
16491668 >> notFollowedBy (char '~')
16501669 strikeEnd = try $ string "~~"
16511670
1652−superscript :: MarkdownParser (F Inlines)
1671+superscript :: PandocMonad m => MarkdownParser m (F Inlines)
16531672superscript = fmap B.superscript <$> try (do
16541673 guardEnabled Ext_superscript
16551674 char '^'
16561675 mconcat <$> many1Till (notFollowedBy spaceChar >> inline) (char '^'))
16571676
1658−subscript :: MarkdownParser (F Inlines)
1677+subscript :: PandocMonad m => MarkdownParser m (F Inlines)
16591678subscript = fmap B.subscript <$> try (do
16601679 guardEnabled Ext_subscript
16611680 char '~'
16621681 mconcat <$> many1Till (notFollowedBy spaceChar >> inline) (char '~'))
16631682
1664−whitespace :: MarkdownParser (F Inlines)
1683+whitespace :: PandocMonad m => MarkdownParser m (F Inlines)
16651684whitespace = spaceChar >> return <$> (lb <|> regsp) <?> "whitespace"
16661685 where lb = spaceChar >> skipMany spaceChar >> option B.space (endline >> return B.linebreak)
16671686 regsp = skipMany spaceChar >> return B.space
16681687
1669−nonEndline :: Parser [Char] st Char
1688+nonEndline :: PandocMonad m => ParserT [Char] st m Char
16701689nonEndline = satisfy (/='\n')
16711690
1672−str :: MarkdownParser (F Inlines)
1691+str :: PandocMonad m => MarkdownParser m (F Inlines)
16731692str = do
16741693 result <- many1 alphaNum
16751694 updateLastStrPos
16761695 let spacesToNbr = map (\c -> if c == ' ' then '\160' else c)
1677− isSmart <- getOption readerSmart
1696+ isSmart <- extensionEnabled Ext_smart <$> getOption readerExtensions
16781697 if isSmart
16791698 then case likelyAbbrev result of
16801699 [] -> return $ return $ B.str result
@@ -1699,7 +1718,7 @@ likelyAbbrev x =
16991718 in map snd $ filter (\(y,_) -> y == x) abbrPairs
17001719
17011720-- an endline character that can be treated as a space, not a structural break
1702−endline :: MarkdownParser (F Inlines)
1721+endline :: PandocMonad m => MarkdownParser m (F Inlines)
17031722endline = try $ do
17041723 newline
17051724 notFollowedBy blankline
@@ -1721,17 +1740,17 @@ endline = try $ do
17211740--
17221741
17231742-- a reference label for a link
1724−reference :: MarkdownParser (F Inlines, String)
1743+reference :: PandocMonad m => MarkdownParser m (F Inlines, String)
17251744reference = do notFollowedBy' (string "[^") -- footnote reference
17261745 withRaw $ trimInlinesF <$> inlinesInBalancedBrackets
17271746
1728−parenthesizedChars :: MarkdownParser [Char]
1747+parenthesizedChars :: PandocMonad m => MarkdownParser m [Char]
17291748parenthesizedChars = do
17301749 result <- charsInBalanced '(' ')' litChar
17311750 return $ '(' : result ++ ")"
17321751
17331752-- source for a link, with optional title
1734−source :: MarkdownParser (String, String)
1753+source :: PandocMonad m => MarkdownParser m (String, String)
17351754source = do
17361755 char '('
17371756 skipSpaces
@@ -1748,10 +1767,10 @@ source = do
17481767 char ')'
17491768 return (escapeURI $ trimr src, tit)
17501769
1751−linkTitle :: MarkdownParser String
1770+linkTitle :: PandocMonad m => MarkdownParser m String
17521771linkTitle = quotedTitle '"' <|> quotedTitle '\''
17531772
1754−link :: MarkdownParser (F Inlines)
1773+link :: PandocMonad m => MarkdownParser m (F Inlines)
17551774link = try $ do
17561775 st <- getState
17571776 guard $ stateAllowLinks st
@@ -1760,7 +1779,7 @@ link = try $ do
17601779 setState $ st{ stateAllowLinks = True }
17611780 regLink B.linkWith lab <|> referenceLink B.linkWith (lab,raw)
17621781
1763−bracketedSpan :: MarkdownParser (F Inlines)
1782+bracketedSpan :: PandocMonad m => MarkdownParser m (F Inlines)
17641783bracketedSpan = try $ do
17651784 guardEnabled Ext_bracketed_spans
17661785 (lab,_) <- reference
@@ -1773,8 +1792,10 @@ bracketedSpan = try $ do
17731792 -> return $ B.smallcaps <$> lab
17741793 _ -> return $ B.spanWith attr <$> lab
17751794
1776−regLink :: (Attr -> String -> String -> Inlines -> Inlines)
1777− -> F Inlines -> MarkdownParser (F Inlines)
1795+regLink :: PandocMonad m
1796+ => (Attr -> String -> String -> Inlines -> Inlines)
1797+ -> F Inlines
1798+ -> MarkdownParser m (F Inlines)
17781799regLink constructor lab = try $ do
17791800 (src, tit) <- source
17801801 attr <- option nullAttr $
@@ -1782,8 +1803,10 @@ regLink constructor lab = try $ do
17821803 return $ constructor attr src tit <$> lab
17831804
17841805-- a link like [this][ref] or [this][] or [this]
1785−referenceLink :: (Attr -> String -> String -> Inlines -> Inlines)
1786− -> (F Inlines, String) -> MarkdownParser (F Inlines)
1806+referenceLink :: PandocMonad m
1807+ => (Attr -> String -> String -> Inlines -> Inlines)
1808+ -> (F Inlines, String)
1809+ -> MarkdownParser m (F Inlines)
17871810referenceLink constructor (lab, raw) = do
17881811 sp <- (True <$ lookAhead (char ' ')) <|> return False
17891812 (_,raw') <- option (mempty, "") $
@@ -1824,7 +1847,7 @@ dropBrackets = reverse . dropRB . reverse . dropLB
18241847 dropLB ('[':xs) = xs
18251848 dropLB xs = xs
18261849
1827−bareURL :: MarkdownParser (F Inlines)
1850+bareURL :: PandocMonad m => MarkdownParser m (F Inlines)
18281851bareURL = try $ do
18291852 guardEnabled Ext_autolink_bare_uris
18301853 getState >>= guard . stateAllowLinks
@@ -1832,7 +1855,7 @@ bareURL = try $ do
18321855 notFollowedBy $ try $ spaces >> htmlTag (~== TagClose "a")
18331856 return $ return $ B.link src "" (B.str orig)
18341857
1835−autoLink :: MarkdownParser (F Inlines)
1858+autoLink :: PandocMonad m => MarkdownParser m (F Inlines)
18361859autoLink = try $ do
18371860 getState >>= guard . stateAllowLinks
18381861 char '<'
@@ -1846,7 +1869,7 @@ autoLink = try $ do
18461869 guardEnabled Ext_link_attributes >> attributes
18471870 return $ return $ B.linkWith attr (src ++ escapeURI extra) "" (B.str $ orig ++ extra)
18481871
1849−image :: MarkdownParser (F Inlines)
1872+image :: PandocMonad m => MarkdownParser m (F Inlines)
18501873image = try $ do
18511874 char '!'
18521875 (lab,raw) <- reference
@@ -1856,7 +1879,7 @@ image = try $ do
18561879 _ -> B.imageWith attr' src
18571880 regLink constructor lab <|> referenceLink constructor (lab,raw)
18581881
1859−note :: MarkdownParser (F Inlines)
1882+note :: PandocMonad m => MarkdownParser m (F Inlines)
18601883note = try $ do
18611884 guardEnabled Ext_footnotes
18621885 ref <- noteMarker
@@ -1872,14 +1895,14 @@ note = try $ do
18721895 let contents' = runF contents st{ stateNotes' = [] }
18731896 return $ B.note contents'
18741897
1875−inlineNote :: MarkdownParser (F Inlines)
1898+inlineNote :: PandocMonad m => MarkdownParser m (F Inlines)
18761899inlineNote = try $ do
18771900 guardEnabled Ext_inline_notes
18781901 char '^'
18791902 contents <- inlinesInBalancedBrackets
18801903 return $ B.note . B.para <$> contents
18811904
1882−rawLaTeXInline' :: MarkdownParser (F Inlines)
1905+rawLaTeXInline' :: PandocMonad m => MarkdownParser m (F Inlines)
18831906rawLaTeXInline' = try $ do
18841907 guardEnabled Ext_raw_tex
18851908 lookAhead $ char '\\' >> notFollowedBy' (string "start") -- context env
@@ -1887,7 +1910,7 @@ rawLaTeXInline' = try $ do
18871910 return $ return $ B.rawInline "tex" s
18881911 -- "tex" because it might be context or latex
18891912
1890−rawConTeXtEnvironment :: Parser [Char] st String
1913+rawConTeXtEnvironment :: PandocMonad m => ParserT [Char] st m String
18911914rawConTeXtEnvironment = try $ do
18921915 string "\\start"
18931916 completion <- inBrackets (letter <|> digit <|> spaceChar)
@@ -1896,14 +1919,14 @@ rawConTeXtEnvironment = try $ do
18961919 (try $ string "\\stop" >> string completion)
18971920 return $ "\\start" ++ completion ++ concat contents ++ "\\stop" ++ completion
18981921
1899−inBrackets :: (Parser [Char] st Char) -> Parser [Char] st String
1922+inBrackets :: PandocMonad m => (ParserT [Char] st m Char) -> ParserT [Char] st m String
19001923inBrackets parser = do
19011924 char '['
19021925 contents <- many parser
19031926 char ']'
19041927 return $ "[" ++ contents ++ "]"
19051928
1906−spanHtml :: MarkdownParser (F Inlines)
1929+spanHtml :: PandocMonad m => MarkdownParser m (F Inlines)
19071930spanHtml = try $ do
19081931 guardEnabled Ext_native_spans
19091932 (TagOpen _ attrs, _) <- htmlTag (~== TagOpen "span" [])
@@ -1918,7 +1941,7 @@ spanHtml = try $ do
19181941 -> return $ B.smallcaps <$> contents
19191942 _ -> return $ B.spanWith (ident, classes, keyvals) <$> contents
19201943
1921−divHtml :: MarkdownParser (F Blocks)
1944+divHtml :: PandocMonad m => MarkdownParser m (F Blocks)
19221945divHtml = try $ do
19231946 guardEnabled Ext_native_divs
19241947 (TagOpen _ attrs, rawtag) <- htmlTag (~== TagOpen "div" [])
@@ -1940,7 +1963,7 @@ divHtml = try $ do
19401963 else -- avoid backtracing
19411964 return $ return (B.rawBlock "html" (rawtag <> bls)) <> contents
19421965
1943−rawHtmlInline :: MarkdownParser (F Inlines)
1966+rawHtmlInline :: PandocMonad m => MarkdownParser m (F Inlines)
19441967rawHtmlInline = do
19451968 guardEnabled Ext_raw_html
19461969 inHtmlBlock <- stateInHtmlBlock <$> getState
@@ -1962,7 +1985,7 @@ rawHtmlInline = do
19621985emojiChars :: [Char]
19631986emojiChars = ['a'..'z'] ++ ['0'..'9'] ++ ['_','+','-']
19641987
1965−emoji :: MarkdownParser (F Inlines)
1988+emoji :: PandocMonad m => MarkdownParser m (F Inlines)
19661989emoji = try $ do
19671990 guardEnabled Ext_emoji
19681991 char ':'
@@ -1974,7 +1997,7 @@ emoji = try $ do
19741997
19751998-- Citations
19761999
1977−cite :: MarkdownParser (F Inlines)
2000+cite :: PandocMonad m => MarkdownParser m (F Inlines)
19782001cite = do
19792002 guardEnabled Ext_citations
19802003 citations <- textualCite
@@ -1982,7 +2005,7 @@ cite = do
19822005 return $ (flip B.cite (B.text raw)) <$> cs
19832006 return citations
19842007
1985−textualCite :: MarkdownParser (F Inlines)
2008+textualCite :: PandocMonad m => MarkdownParser m (F Inlines)
19862009textualCite = try $ do
19872010 (_, key) <- citeKey
19882011 let first = Citation{ citationId = key
@@ -2017,7 +2040,7 @@ textualCite = try $ do
20172040 Just n -> B.str (show n)
20182041 _ -> B.cite [first] $ B.str $ '@':key)
20192042
2020−bareloc :: Citation -> MarkdownParser (F [Citation])
2043+bareloc :: PandocMonad m => Citation -> MarkdownParser m (F [Citation])
20212044bareloc c = try $ do
20222045 spnl
20232046 char '['
@@ -2032,7 +2055,7 @@ bareloc c = try $ do
20322055 rest' <- rest
20332056 return $ c{ citationSuffix = B.toList suff' } : rest'
20342057
2035−normalCite :: MarkdownParser (F [Citation])
2058+normalCite :: PandocMonad m => MarkdownParser m (F [Citation])
20362059normalCite = try $ do
20372060 char '['
20382061 spnl
@@ -2041,7 +2064,7 @@ normalCite = try $ do
20412064 char ']'
20422065 return citations
20432066
2044−suffix :: MarkdownParser (F Inlines)
2067+suffix :: PandocMonad m => MarkdownParser m (F Inlines)
20452068suffix = try $ do
20462069 hasSpace <- option False (notFollowedBy nonspaceChar >> return True)
20472070 spnl
@@ -2050,14 +2073,14 @@ suffix = try $ do
20502073 then (B.space <>) <$> rest
20512074 else rest
20522075
2053−prefix :: MarkdownParser (F Inlines)
2076+prefix :: PandocMonad m => MarkdownParser m (F Inlines)
20542077prefix = trimInlinesF . mconcat <$>
20552078 manyTill inline (char ']' <|> liftM (const ']') (lookAhead citeKey))
20562079
2057−citeList :: MarkdownParser (F [Citation])
2080+citeList :: PandocMonad m => MarkdownParser m (F [Citation])
20582081citeList = fmap sequence $ sepBy1 citation (try $ char ';' >> spnl)
20592082
2060−citation :: MarkdownParser (F Citation)
2083+citation :: PandocMonad m => MarkdownParser m (F Citation)
20612084citation = try $ do
20622085 pref <- prefix
20632086 (suppress_author, key) <- citeKey
@@ -2075,13 +2098,13 @@ citation = try $ do
20752098 , citationHash = 0
20762099 }
20772100
2078−smart :: MarkdownParser (F Inlines)
2101+smart :: PandocMonad m => MarkdownParser m (F Inlines)
20792102smart = do
2080− getOption readerSmart >>= guard
2103+ guardEnabled Ext_smart
20812104 doubleQuoted <|> singleQuoted <|>
20822105 choice (map (return <$>) [apostrophe, dash, ellipses])
20832106
2084−singleQuoted :: MarkdownParser (F Inlines)
2107+singleQuoted :: PandocMonad m => MarkdownParser m (F Inlines)
20852108singleQuoted = try $ do
20862109 singleQuoteStart
20872110 withQuoteContext InSingleQuote $
@@ -2091,7 +2114,7 @@ singleQuoted = try $ do
20912114-- doubleQuoted will handle regular double-quoted sections, as well
20922115-- as dialogues with an open double-quote without a close double-quote
20932116-- in the same paragraph.
2094−doubleQuoted :: MarkdownParser (F Inlines)
2117+doubleQuoted :: PandocMonad m => MarkdownParser m (F Inlines)
20952118doubleQuoted = try $ do
20962119 doubleQuoteStart
20972120 contents <- mconcat <$> many (try $ notFollowedBy doubleQuoteEnd >> inline)
src/Text/Pandoc/Readers/MediaWiki.hs+77−76
@@ -56,23 +56,26 @@ import qualified Data.Set as Set
5656import Data.Char (isDigit, isSpace)
5757import Data.Maybe (fromMaybe)
5858import Text.Printf (printf)
59−import Debug.Trace (trace)
60−
61−import Text.Pandoc.Error
59+import Control.Monad.Except (throwError)
60+import Text.Pandoc.Class (PandocMonad, report)
6261
6362-- | Read mediawiki from an input string and return a Pandoc document.
64−readMediaWiki :: ReaderOptions -- ^ Reader options
63+readMediaWiki :: PandocMonad m
64+ => ReaderOptions -- ^ Reader options
6565 -> String -- ^ String to parse (assuming @'\n'@ line endings)
66− -> Either PandocError Pandoc
67−readMediaWiki opts s =
68− readWith parseMediaWiki MWState{ mwOptions = opts
69− , mwMaxNestingLevel = 4
70− , mwNextLinkNumber = 1
71− , mwCategoryLinks = []
72− , mwHeaderMap = M.empty
73− , mwIdentifierList = Set.empty
74− }
75− (s ++ "\n")
66+ -> m Pandoc
67+readMediaWiki opts s = do
68+ parsed <- readWithM parseMediaWiki MWState{ mwOptions = opts
69+ , mwMaxNestingLevel = 4
70+ , mwNextLinkNumber = 1
71+ , mwCategoryLinks = []
72+ , mwHeaderMap = M.empty
73+ , mwIdentifierList = Set.empty
74+ }
75+ (s ++ "\n")
76+ case parsed of
77+ Right result -> return result
78+ Left e -> throwError e
7679
7780data MWState = MWState { mwOptions :: ReaderOptions
7881 , mwMaxNestingLevel :: Int
@@ -82,7 +85,7 @@ data MWState = MWState { mwOptions :: ReaderOptions
8285 , mwIdentifierList :: Set.Set String
8386 }
8487
85−type MWParser = Parser [Char] MWState
88+type MWParser m = ParserT [Char] MWState m
8689
8790instance HasReaderOptions MWState where
8891 extractReaderOptions = mwOptions
@@ -101,7 +104,7 @@ instance HasIdentifierList MWState where
101104
102105-- This is used to prevent exponential blowups for things like:
103106-- ''a'''a''a'''a''a'''a''a'''a
104−nested :: MWParser a -> MWParser a
107+nested :: PandocMonad m => MWParser m a -> MWParser m a
105108nested p = do
106109 nestlevel <- mwMaxNestingLevel `fmap` getState
107110 guard $ nestlevel > 0
@@ -116,7 +119,7 @@ specialChars = "'[]<=&*{}|\":\\"
116119spaceChars :: [Char]
117120spaceChars = " \n\t"
118121
119−sym :: String -> MWParser ()
122+sym :: PandocMonad m => String -> MWParser m ()
120123sym s = () <$ try (string s)
121124
122125newBlockTags :: [String]
@@ -137,10 +140,10 @@ eitherBlockOrInline :: [String]
137140eitherBlockOrInline = ["applet", "button", "del", "iframe", "ins",
138141 "map", "area", "object"]
139142
140−htmlComment :: MWParser ()
143+htmlComment :: PandocMonad m => MWParser m ()
141144htmlComment = () <$ htmlTag isCommentTag
142145
143−inlinesInTags :: String -> MWParser Inlines
146+inlinesInTags :: PandocMonad m => String -> MWParser m Inlines
144147inlinesInTags tag = try $ do
145148 (_,raw) <- htmlTag (~== TagOpen tag [])
146149 if '/' `elem` raw -- self-closing tag
@@ -148,7 +151,7 @@ inlinesInTags tag = try $ do
148151 else trimInlines . mconcat <$>
149152 manyTill inline (htmlTag (~== TagClose tag))
150153
151−blocksInTags :: String -> MWParser Blocks
154+blocksInTags :: PandocMonad m => String -> MWParser m Blocks
152155blocksInTags tag = try $ do
153156 (_,raw) <- htmlTag (~== TagOpen tag [])
154157 let closer = if tag == "li"
@@ -162,7 +165,7 @@ blocksInTags tag = try $ do
162165 then return mempty
163166 else mconcat <$> manyTill block closer
164167
165−charsInTags :: String -> MWParser [Char]
168+charsInTags :: PandocMonad m => String -> MWParser m [Char]
166169charsInTags tag = try $ do
167170 (_,raw) <- htmlTag (~== TagOpen tag [])
168171 if '/' `elem` raw -- self-closing tag
@@ -173,7 +176,7 @@ charsInTags tag = try $ do
173176-- main parser
174177--
175178
176−parseMediaWiki :: MWParser Pandoc
179+parseMediaWiki :: PandocMonad m => MWParser m Pandoc
177180parseMediaWiki = do
178181 bs <- mconcat <$> many block
179182 spaces
@@ -188,9 +191,8 @@ parseMediaWiki = do
188191-- block parsers
189192--
190193
191−block :: MWParser Blocks
194+block :: PandocMonad m => MWParser m Blocks
192195block = do
193− tr <- getOption readerTrace
194196 pos <- getPosition
195197 res <- mempty <$ skipMany1 blankline
196198 <|> table
@@ -204,19 +206,18 @@ block = do
204206 <|> blockTag
205207 <|> (B.rawBlock "mediawiki" <$> template)
206208 <|> para
207− when tr $
208− trace (printf "line %d: %s" (sourceLine pos)
209− (take 60 $ show $ B.toList res)) (return ())
209+ report DEBUG $ printf "line %d: %s" (sourceLine pos)
210+ (take 60 $ show $ B.toList res)
210211 return res
211212
212−para :: MWParser Blocks
213+para :: PandocMonad m => MWParser m Blocks
213214para = do
214215 contents <- trimInlines . mconcat <$> many1 inline
215216 if F.all (==Space) contents
216217 then return mempty
217218 else return $ B.para contents
218219
219−table :: MWParser Blocks
220+table :: PandocMonad m => MWParser m Blocks
220221table = do
221222 tableStart
222223 styles <- option [] parseAttrs <* blankline
@@ -244,10 +245,10 @@ table = do
244245 else (replicate cols mempty, hdr:rows')
245246 return $ B.table caption cellspecs headers rows
246247
247−parseAttrs :: MWParser [(String,String)]
248+parseAttrs :: PandocMonad m => MWParser m [(String,String)]
248249parseAttrs = many1 parseAttr
249250
250−parseAttr :: MWParser (String, String)
251+parseAttr :: PandocMonad m => MWParser m (String, String)
251252parseAttr = try $ do
252253 skipMany spaceChar
253254 k <- many1 letter
@@ -256,17 +257,17 @@ parseAttr = try $ do
256257 <|> many1 (satisfy $ \c -> not (isSpace c) && c /= '|')
257258 return (k,v)
258259
259−tableStart :: MWParser ()
260+tableStart :: PandocMonad m => MWParser m ()
260261tableStart = try $ guardColumnOne *> skipSpaces *> sym "{|"
261262
262−tableEnd :: MWParser ()
263+tableEnd :: PandocMonad m => MWParser m ()
263264tableEnd = try $ guardColumnOne *> skipSpaces *> sym "|}"
264265
265−rowsep :: MWParser ()
266+rowsep :: PandocMonad m => MWParser m ()
266267rowsep = try $ guardColumnOne *> skipSpaces *> sym "|-" <*
267268 optional parseAttr <* blanklines
268269
269−cellsep :: MWParser ()
270+cellsep :: PandocMonad m => MWParser m ()
270271cellsep = try $
271272 (guardColumnOne *> skipSpaces <*
272273 ( (char '|' <* notFollowedBy (oneOf "-}+"))
@@ -276,7 +277,7 @@ cellsep = try $
276277 <|> (() <$ try (string "||"))
277278 <|> (() <$ try (string "!!"))
278279
279−tableCaption :: MWParser Inlines
280+tableCaption :: PandocMonad m => MWParser m Inlines
280281tableCaption = try $ do
281282 guardColumnOne
282283 skipSpaces
@@ -284,10 +285,10 @@ tableCaption = try $ do
284285 optional (try $ parseAttr *> skipSpaces *> char '|' *> skipSpaces)
285286 (trimInlines . mconcat) <$> many (notFollowedBy (cellsep <|> rowsep) *> inline)
286287
287−tableRow :: MWParser [((Alignment, Double), Blocks)]
288+tableRow :: PandocMonad m => MWParser m [((Alignment, Double), Blocks)]
288289tableRow = try $ skipMany htmlComment *> many tableCell
289290
290−tableCell :: MWParser ((Alignment, Double), Blocks)
291+tableCell :: PandocMonad m => MWParser m ((Alignment, Double), Blocks)
291292tableCell = try $ do
292293 cellsep
293294 skipMany spaceChar
@@ -313,7 +314,7 @@ parseWidth s =
313314 ('%':ds) | all isDigit ds -> safeRead ('0':'.':reverse ds)
314315 _ -> Nothing
315316
316−template :: MWParser String
317+template :: PandocMonad m => MWParser m String
317318template = try $ do
318319 string "{{"
319320 notFollowedBy (char '{')
@@ -322,7 +323,7 @@ template = try $ do
322323 contents <- manyTill chunk (try $ string "}}")
323324 return $ "{{" ++ concat contents ++ "}}"
324325
325−blockTag :: MWParser Blocks
326+blockTag :: PandocMonad m => MWParser m Blocks
326327blockTag = do
327328 (tag, _) <- lookAhead $ htmlTag isBlockTag'
328329 case tag of
@@ -341,7 +342,7 @@ trimCode :: String -> String
341342trimCode ('\n':xs) = stripTrailingNewlines xs
342343trimCode xs = stripTrailingNewlines xs
343344
344−syntaxhighlight :: String -> [Attribute String] -> MWParser Blocks
345+syntaxhighlight :: PandocMonad m => String -> [Attribute String] -> MWParser m Blocks
345346syntaxhighlight tag attrs = try $ do
346347 let mblang = lookup "lang" attrs
347348 let mbstart = lookup "start" attrs
@@ -351,13 +352,13 @@ syntaxhighlight tag attrs = try $ do
351352 contents <- charsInTags tag
352353 return $ B.codeBlockWith ("",classes,kvs) $ trimCode contents
353354
354−hrule :: MWParser Blocks
355+hrule :: PandocMonad m => MWParser m Blocks
355356hrule = B.horizontalRule <$ try (string "----" *> many (char '-') *> newline)
356357
357−guardColumnOne :: MWParser ()
358+guardColumnOne :: PandocMonad m => MWParser m ()
358359guardColumnOne = getPosition >>= \pos -> guard (sourceColumn pos == 1)
359360
360−preformatted :: MWParser Blocks
361+preformatted :: PandocMonad m => MWParser m Blocks
361362preformatted = try $ do
362363 guardColumnOne
363364 char ' '
@@ -388,7 +389,7 @@ encode = B.fromList . normalizeCode . B.toList . walk strToCode
388389 normalizeCode $ (Code a1 (x ++ y)) : zs
389390 normalizeCode (x:xs) = x : normalizeCode xs
390391
391−header :: MWParser Blocks
392+header :: PandocMonad m => MWParser m Blocks
392393header = try $ do
393394 guardColumnOne
394395 eqs <- many1 (char '=')
@@ -398,13 +399,13 @@ header = try $ do
398399 attr <- registerHeader nullAttr contents
399400 return $ B.headerWith attr lev contents
400401
401−bulletList :: MWParser Blocks
402+bulletList :: PandocMonad m => MWParser m Blocks
402403bulletList = B.bulletList <$>
403404 ( many1 (listItem '*')
404405 <|> (htmlTag (~== TagOpen "ul" []) *> spaces *> many (listItem '*' <|> li) <*
405406 optional (htmlTag (~== TagClose "ul"))) )
406407
407−orderedList :: MWParser Blocks
408+orderedList :: PandocMonad m => MWParser m Blocks
408409orderedList =
409410 (B.orderedList <$> many1 (listItem '#'))
410411 <|> try
@@ -415,10 +416,10 @@ orderedList =
415416 let start = fromMaybe 1 $ safeRead $ fromAttrib "start" tag
416417 return $ B.orderedListWith (start, DefaultStyle, DefaultDelim) items)
417418
418−definitionList :: MWParser Blocks
419+definitionList :: PandocMonad m => MWParser m Blocks
419420definitionList = B.definitionList <$> many1 defListItem
420421
421−defListItem :: MWParser (Inlines, [Blocks])
422+defListItem :: PandocMonad m => MWParser m (Inlines, [Blocks])
422423defListItem = try $ do
423424 terms <- mconcat . intersperse B.linebreak <$> many defListTerm
424425 -- we allow dd with no dt, or dt with no dd
@@ -429,27 +430,27 @@ defListItem = try $ do
429430 else many (listItem ':')
430431 return (terms, defs)
431432
432−defListTerm :: MWParser Inlines
433+defListTerm :: PandocMonad m => MWParser m Inlines
433434defListTerm = char ';' >> skipMany spaceChar >> anyLine >>=
434435 parseFromString (trimInlines . mconcat <$> many inline)
435436
436−listStart :: Char -> MWParser ()
437+listStart :: PandocMonad m => Char -> MWParser m ()
437438listStart c = char c *> notFollowedBy listStartChar
438439
439−listStartChar :: MWParser Char
440+listStartChar :: PandocMonad m => MWParser m Char
440441listStartChar = oneOf "*#;:"
441442
442−anyListStart :: MWParser Char
443+anyListStart :: PandocMonad m => MWParser m Char
443444anyListStart = char '*'
444445 <|> char '#'
445446 <|> char ':'
446447 <|> char ';'
447448
448−li :: MWParser Blocks
449+li :: PandocMonad m => MWParser m Blocks
449450li = lookAhead (htmlTag (~== TagOpen "li" [])) *>
450451 (firstParaToPlain <$> blocksInTags "li") <* spaces
451452
452−listItem :: Char -> MWParser Blocks
453+listItem :: PandocMonad m => Char -> MWParser m Blocks
453454listItem c = try $ do
454455 extras <- many (try $ char c <* lookAhead listStartChar)
455456 if null extras
@@ -475,10 +476,10 @@ listItem c = try $ do
475476-- }}
476477-- * next list item
477478-- which seems to be valid mediawiki.
478−listChunk :: MWParser String
479+listChunk :: PandocMonad m => MWParser m String
479480listChunk = template <|> count 1 anyChar
480481
481−listItem' :: Char -> MWParser Blocks
482+listItem' :: PandocMonad m => Char -> MWParser m Blocks
482483listItem' c = try $ do
483484 listStart c
484485 skipMany spaceChar
@@ -498,7 +499,7 @@ firstParaToPlain contents =
498499-- inline parsers
499500--
500501
501−inline :: MWParser Inlines
502+inline :: PandocMonad m => MWParser m Inlines
502503inline = whitespace
503504 <|> url
504505 <|> str
@@ -516,10 +517,10 @@ inline = whitespace
516517 <|> (B.rawInline "mediawiki" <$> template)
517518 <|> special
518519
519−str :: MWParser Inlines
520+str :: PandocMonad m => MWParser m Inlines
520521str = B.str <$> many1 (noneOf $ specialChars ++ spaceChars)
521522
522−math :: MWParser Inlines
523+math :: PandocMonad m => MWParser m Inlines
523524math = (B.displayMath . trim <$> try (many1 (char ':') >> charsInTags "math"))
524525 <|> (B.math . trim <$> charsInTags "math")
525526 <|> (B.displayMath . trim <$> try (dmStart *> manyTill anyChar dmEnd))
@@ -529,13 +530,13 @@ math = (B.displayMath . trim <$> try (many1 (char ':') >> charsInTags "math"))
529530 mStart = string "\\("
530531 mEnd = try (string "\\)")
531532
532−variable :: MWParser String
533+variable :: PandocMonad m => MWParser m String
533534variable = try $ do
534535 string "{{{"
535536 contents <- manyTill anyChar (try $ string "}}}")
536537 return $ "{{{" ++ contents ++ "}}}"
537538
538−inlineTag :: MWParser Inlines
539+inlineTag :: PandocMonad m => MWParser m Inlines
539540inlineTag = do
540541 (tag, _) <- lookAhead $ htmlTag isInlineTag'
541542 case tag of
@@ -557,18 +558,18 @@ inlineTag = do
557558 TagOpen "hask" _ -> B.codeWith ("",["haskell"],[]) <$> charsInTags "hask"
558559 _ -> B.rawInline "html" . snd <$> htmlTag (~== tag)
559560
560−special :: MWParser Inlines
561+special :: PandocMonad m => MWParser m Inlines
561562special = B.str <$> count 1 (notFollowedBy' (htmlTag isBlockTag') *>
562563 oneOf specialChars)
563564
564−inlineHtml :: MWParser Inlines
565+inlineHtml :: PandocMonad m => MWParser m Inlines
565566inlineHtml = B.rawInline "html" . snd <$> htmlTag isInlineTag'
566567
567−whitespace :: MWParser Inlines
568+whitespace :: PandocMonad m => MWParser m Inlines
568569whitespace = B.space <$ (skipMany1 spaceChar <|> htmlComment)
569570 <|> B.softbreak <$ endline
570571
571−endline :: MWParser ()
572+endline :: PandocMonad m => MWParser m ()
572573endline = () <$ try (newline <*
573574 notFollowedBy spaceChar <*
574575 notFollowedBy newline <*
@@ -577,12 +578,12 @@ endline = () <$ try (newline <*
577578 notFollowedBy' header <*
578579 notFollowedBy anyListStart)
579580
580−imageIdentifiers :: [MWParser ()]
581+imageIdentifiers :: PandocMonad m => [MWParser m ()]
581582imageIdentifiers = [sym (identifier ++ ":") | identifier <- identifiers]
582583 where identifiers = ["File", "Image", "Archivo", "Datei", "Fichier",
583584 "Bild"]
584585
585−image :: MWParser Inlines
586+image :: PandocMonad m => MWParser m Inlines
586587image = try $ do
587588 sym "[["
588589 choice imageIdentifiers
@@ -600,7 +601,7 @@ image = try $ do
600601 <|> try (char '|' *> (mconcat <$> manyTill inline (sym "]]")))
601602 return $ B.imageWith attr fname ("fig:" ++ stringify caption) caption
602603
603−imageOption :: MWParser String
604+imageOption :: PandocMonad m => MWParser m String
604605imageOption = try $ char '|' *> opt
605606 where
606607 opt = try (oneOfStrings [ "border", "thumbnail", "frameless"
@@ -619,7 +620,7 @@ collapseUnderscores (x:xs) = x : collapseUnderscores xs
619620addUnderscores :: String -> String
620621addUnderscores = collapseUnderscores . intercalate "_" . words
621622
622−internalLink :: MWParser Inlines
623+internalLink :: PandocMonad m => MWParser m Inlines
623624internalLink = try $ do
624625 sym "[["
625626 pagename <- unwords . words <$> many (noneOf "|]")
@@ -637,7 +638,7 @@ internalLink = try $ do
637638 return mempty
638639 else return link
639640
640−externalLink :: MWParser Inlines
641+externalLink :: PandocMonad m => MWParser m Inlines
641642externalLink = try $ do
642643 char '['
643644 (_, src) <- uri
@@ -649,29 +650,29 @@ externalLink = try $ do
649650 return $ B.str $ show num
650651 return $ B.link src "" lab
651652
652−url :: MWParser Inlines
653+url :: PandocMonad m => MWParser m Inlines
653654url = do
654655 (orig, src) <- uri
655656 return $ B.link src "" (B.str orig)
656657
657658-- | Parses a list of inlines between start and end delimiters.
658−inlinesBetween :: (Show b) => MWParser a -> MWParser b -> MWParser Inlines
659+inlinesBetween :: (PandocMonad m, Show b) => MWParser m a -> MWParser m b -> MWParser m Inlines
659660inlinesBetween start end =
660661 (trimInlines . mconcat) <$> try (start >> many1Till inner end)
661662 where inner = innerSpace <|> (notFollowedBy' (() <$ whitespace) >> inline)
662663 innerSpace = try $ whitespace <* notFollowedBy' end
663664
664−emph :: MWParser Inlines
665+emph :: PandocMonad m => MWParser m Inlines
665666emph = B.emph <$> nested (inlinesBetween start end)
666667 where start = sym "''" >> lookAhead nonspaceChar
667668 end = try $ notFollowedBy' (() <$ strong) >> sym "''"
668669
669−strong :: MWParser Inlines
670+strong :: PandocMonad m => MWParser m Inlines
670671strong = B.strong <$> nested (inlinesBetween start end)
671672 where start = sym "'''" >> lookAhead nonspaceChar
672673 end = try $ sym "'''"
673674
674−doubleQuotes :: MWParser Inlines
675+doubleQuotes :: PandocMonad m => MWParser m Inlines
675676doubleQuotes = B.doubleQuoted <$> nested (inlinesBetween openDoubleQuote closeDoubleQuote)
676677 where openDoubleQuote = sym "\"" >> lookAhead nonspaceChar
677678 closeDoubleQuote = try $ sym "\""
src/Text/Pandoc/Readers/Native.hs+12−4
@@ -32,8 +32,11 @@ module Text.Pandoc.Readers.Native ( readNative ) where
3232
3333import Text.Pandoc.Definition
3434import Text.Pandoc.Shared (safeRead)
35+import Text.Pandoc.Options (ReaderOptions)
3536
37+import Control.Monad.Except (throwError)
3638import Text.Pandoc.Error
39+import Text.Pandoc.Class
3740
3841-- | Read native formatted text and return a Pandoc document.
3942-- The input may be a full pandoc document, a block list, a block,
@@ -45,9 +48,14 @@ import Text.Pandoc.Error
4548--
4649-- > Pandoc nullMeta [Plain [Str "hi"]]
4750--
48−readNative :: String -- ^ String to parse (assuming @'\n'@ line endings)
49− -> Either PandocError Pandoc
50−readNative s = maybe (Pandoc nullMeta <$> readBlocks s) Right (safeRead s)
51+readNative :: PandocMonad m
52+ => ReaderOptions
53+ -> String -- ^ String to parse (assuming @'\n'@ line endings)
54+ -> m Pandoc
55+readNative _ s =
56+ case maybe (Pandoc nullMeta <$> readBlocks s) Right (safeRead s) of
57+ Right doc -> return doc
58+ Left _ -> throwError $ PandocParseError "couldn't read native"
5159
5260readBlocks :: String -> Either PandocError [Block]
5361readBlocks s = maybe ((:[]) <$> readBlock s) Right (safeRead s)
@@ -59,5 +67,5 @@ readInlines :: String -> Either PandocError [Inline]
5967readInlines s = maybe ((:[]) <$> readInline s) Right (safeRead s)
6068
6169readInline :: String -> Either PandocError Inline
62−readInline s = maybe (Left . ParseFailure $ "Could not read: " ++ s) Right (safeRead s)
70+readInline s = maybe (Left . PandocParseError $ "Could not read: " ++ s) Right (safeRead s)
6371
src/Text/Pandoc/Readers/OPML.hs+20−18
@@ -11,10 +11,9 @@ import Text.HTML.TagSoup.Entity (lookupEntity)
1111import Data.Generics
1212import Control.Monad.State
1313import Data.Default
14−import Control.Monad.Except
15−import Text.Pandoc.Error
14+import Text.Pandoc.Class (PandocMonad)
1615
17−type OPML = ExceptT PandocError (State OPMLState)
16+type OPML m = StateT OPMLState m
1817
1918data OPMLState = OPMLState{
2019 opmlSectionLevel :: Int
@@ -30,12 +29,14 @@ instance Default OPMLState where
3029 , opmlDocDate = mempty
3130 }
3231
33−readOPML :: ReaderOptions -> String -> Either PandocError Pandoc
34−readOPML _ inp = setTitle (opmlDocTitle st')
35− . setAuthors (opmlDocAuthors st')
36− . setDate (opmlDocDate st')
37− . doc . mconcat <$> bs
38− where (bs, st') = flip runState def . runExceptT $ (mapM parseBlock $ normalizeTree $ parseXML inp)
32+readOPML :: PandocMonad m => ReaderOptions -> String -> m Pandoc
33+readOPML _ inp = do
34+ (bs, st') <- flip runStateT def (mapM parseBlock $ normalizeTree $ parseXML inp)
35+ return $
36+ setTitle (opmlDocTitle st') $
37+ setAuthors (opmlDocAuthors st') $
38+ setDate (opmlDocDate st') $
39+ doc $ mconcat bs
3940
4041-- normalize input, consolidating adjacent Text and CRef elements
4142normalizeTree :: [Content] -> [Content]
@@ -62,21 +63,22 @@ attrValue attr elt =
6263 Just z -> z
6364 Nothing -> ""
6465
65−exceptT :: Either PandocError a -> OPML a
66−exceptT = either throwError return
66+-- exceptT :: PandocMonad m => Either PandocError a -> OPML m a
67+-- exceptT = either throwError return
6768
68−asHtml :: String -> OPML Inlines
69−asHtml s = (\(Pandoc _ bs) -> case bs of
69+asHtml :: PandocMonad m => String -> OPML m Inlines
70+asHtml s =
71+ (\(Pandoc _ bs) -> case bs of
7072 [Plain ils] -> fromList ils
71− _ -> mempty) <$> exceptT (readHtml def s)
73+ _ -> mempty) <$> (lift $ readHtml def s)
7274
73−asMarkdown :: String -> OPML Blocks
74−asMarkdown s = (\(Pandoc _ bs) -> fromList bs) <$> exceptT (readMarkdown def s)
75+asMarkdown :: PandocMonad m => String -> OPML m Blocks
76+asMarkdown s = (\(Pandoc _ bs) -> fromList bs) <$> (lift $ readMarkdown def s)
7577
76−getBlocks :: Element -> OPML Blocks
78+getBlocks :: PandocMonad m => Element -> OPML m Blocks
7779getBlocks e = mconcat <$> (mapM parseBlock $ elContent e)
7880
79−parseBlock :: Content -> OPML Blocks
81+parseBlock :: PandocMonad m => Content -> OPML m Blocks
8082parseBlock (Elem e) =
8183 case qName (elName e) of
8284 "ownerName" -> mempty <$ modify (\st ->
src/Text/Pandoc/Readers/Odt.hs+20−6
@@ -39,6 +39,10 @@ import qualified Data.ByteString.Lazy as B
3939
4040import System.FilePath
4141
42+import Control.Monad.Except (throwError)
43+
44+import Text.Pandoc.Class (PandocMonad)
45+import qualified Text.Pandoc.Class as P
4246import Text.Pandoc.Definition
4347import Text.Pandoc.Error
4448import Text.Pandoc.Options
@@ -52,11 +56,21 @@ import Text.Pandoc.Readers.Odt.Generic.XMLConverter
5256import Text.Pandoc.Readers.Odt.Generic.Fallible
5357import Text.Pandoc.Shared (filteredFilesFromArchive)
5458
55−--
56−readOdt :: ReaderOptions
59+readOdt :: PandocMonad m
60+ => ReaderOptions
5761 -> B.ByteString
58− -> Either PandocError (Pandoc, MediaBag)
59−readOdt _ bytes = bytesToOdt bytes-- of
62+ -> m Pandoc
63+readOdt opts bytes = case readOdt' opts bytes of
64+ Right (doc, mb) -> do
65+ P.setMediaBag mb
66+ return doc
67+ Left e -> throwError e
68+
69+--
70+readOdt' :: ReaderOptions
71+ -> B.ByteString
72+ -> Either PandocError (Pandoc, MediaBag)
73+readOdt' _ bytes = bytesToOdt bytes-- of
6074-- Right (pandoc, mediaBag) -> Right (pandoc , mediaBag)
6175-- Left err -> Left err
6276
@@ -64,7 +78,7 @@ readOdt _ bytes = bytesToOdt bytes-- of
6478bytesToOdt :: B.ByteString -> Either PandocError (Pandoc, MediaBag)
6579bytesToOdt bytes = case toArchiveOrFail bytes of
6680 Right archive -> archiveToOdt archive
67− Left _ -> Left $ ParseFailure "Couldn't parse odt file."
81+ Left _ -> Left $ PandocParseError "Couldn't parse odt file."
6882
6983--
7084archiveToOdt :: Archive -> Either PandocError (Pandoc, MediaBag)
@@ -85,7 +99,7 @@ archiveToOdt archive
8599
86100 | otherwise
87101 -- Not very detailed, but I don't think more information would be helpful
88− = Left $ ParseFailure "Couldn't parse odt file."
102+ = Left $ PandocParseError "Couldn't parse odt file."
89103 where
90104 filePathIsOdtMedia :: FilePath -> Bool
91105 filePathIsOdtMedia fp =
src/Text/Pandoc/Readers/Odt/ContentReader.hs+2−2
@@ -663,7 +663,7 @@ read_list = matchingElement NsText "list"
663663--
664664read_list_item :: ElementMatcher [Blocks]
665665read_list_item = matchingElement NsText "list-item"
666− $ liftA (compactify'.(:[]))
666+ $ liftA (compactify.(:[]))
667667 ( matchChildContent' [ read_paragraph
668668 , read_header
669669 , read_list
@@ -749,7 +749,7 @@ read_table_row = matchingElement NsTable "table-row"
749749--
750750read_table_cell :: ElementMatcher [Blocks]
751751read_table_cell = matchingElement NsTable "table-cell"
752− $ liftA (compactify'.(:[]))
752+ $ liftA (compactify.(:[]))
753753 $ matchChildContent' [ read_paragraph
754754 ]
755755
src/Text/Pandoc/Readers/Org.hs+13−6
@@ -31,24 +31,31 @@ import Text.Pandoc.Readers.Org.Blocks ( blockList, meta )
3131import Text.Pandoc.Readers.Org.Parsing ( OrgParser, readWithM )
3232import Text.Pandoc.Readers.Org.ParserState ( optionsToParserState )
3333
34+import Text.Pandoc.Class (PandocMonad)
3435import Text.Pandoc.Definition
3536import Text.Pandoc.Error
3637import Text.Pandoc.Options
3738
38−import Control.Monad.Reader ( runReader )
39+import Control.Monad.Except ( throwError )
40+import Control.Monad.Reader ( runReaderT )
3941
4042
4143-- | Parse org-mode string and return a Pandoc document.
42−readOrg :: ReaderOptions -- ^ Reader options
44+readOrg :: PandocMonad m
45+ => ReaderOptions -- ^ Reader options
4346 -> String -- ^ String to parse (assuming @'\n'@ line endings)
44− -> Either PandocError Pandoc
45−readOrg opts s = flip runReader def $
46− readWithM parseOrg (optionsToParserState opts) (s ++ "\n\n")
47+ -> m Pandoc
48+readOrg opts s = do
49+ parsed <- flip runReaderT def $
50+ readWithM parseOrg (optionsToParserState opts) (s ++ "\n\n")
51+ case parsed of
52+ Right result -> return result
53+ Left _ -> throwError $ PandocParseError "problem parsing org"
4754
4855--
4956-- Parser
5057--
51−parseOrg :: OrgParser Pandoc
58+parseOrg :: PandocMonad m => OrgParser m Pandoc
5259parseOrg = do
5360 blocks' <- blockList
5461 meta' <- meta
src/Text/Pandoc/Readers/Org/BlockStarts.hs+17−16
@@ -44,7 +44,7 @@ import Control.Monad ( void )
4444import Text.Pandoc.Readers.Org.Parsing
4545
4646-- | Horizontal Line (five -- dashes or more)
47−hline :: OrgParser ()
47+hline :: Monad m => OrgParser m ()
4848hline = try $ do
4949 skipSpaces
5050 string "-----"
@@ -54,58 +54,59 @@ hline = try $ do
5454 return ()
5555
5656-- | Read the start of a header line, return the header level
57−headerStart :: OrgParser Int
57+headerStart :: Monad m => OrgParser m Int
5858headerStart = try $
5959 (length <$> many1 (char '*')) <* many1 (char ' ') <* updateLastPreCharPos
6060
61−tableStart :: OrgParser Char
61+tableStart :: Monad m => OrgParser m Char
6262tableStart = try $ skipSpaces *> char '|'
6363
64−latexEnvStart :: OrgParser String
64+latexEnvStart :: Monad m => OrgParser m String
6565latexEnvStart = try $ do
6666 skipSpaces *> string "\\begin{"
6767 *> latexEnvName
6868 <* string "}"
6969 <* blankline
7070 where
71− latexEnvName :: OrgParser String
71+ latexEnvName :: Monad m => OrgParser m String
7272 latexEnvName = try $ mappend <$> many1 alphaNum <*> option "" (string "*")
7373
7474
7575-- | Parses bullet list marker.
76−bulletListStart :: OrgParser ()
76+bulletListStart :: Monad m => OrgParser m ()
7777bulletListStart = try $
7878 choice
7979 [ () <$ skipSpaces <* oneOf "+-" <* skipSpaces1
8080 , () <$ skipSpaces1 <* char '*' <* skipSpaces1
8181 ]
8282
83−genericListStart :: OrgParser String
84− -> OrgParser Int
83+genericListStart :: Monad m
84+ => OrgParser m String
85+ -> OrgParser m Int
8586genericListStart listMarker = try $
8687 (+) <$> (length <$> many spaceChar)
8788 <*> (length <$> listMarker <* many1 spaceChar)
8889
89−orderedListStart :: OrgParser Int
90+orderedListStart :: Monad m => OrgParser m Int
9091orderedListStart = genericListStart orderedListMarker
9192 -- Ordered list markers allowed in org-mode
9293 where orderedListMarker = mappend <$> many1 digit <*> (pure <$> oneOf ".)")
9394
94−drawerStart :: OrgParser String
95+drawerStart :: Monad m => OrgParser m String
9596drawerStart = try $
9697 skipSpaces *> drawerName <* skipSpaces <* newline
9798 where drawerName = char ':' *> manyTill nonspaceChar (char ':')
9899
99−metaLineStart :: OrgParser ()
100+metaLineStart :: Monad m => OrgParser m ()
100101metaLineStart = try $ skipSpaces <* string "#+"
101102
102−commentLineStart :: OrgParser ()
103+commentLineStart :: Monad m => OrgParser m ()
103104commentLineStart = try $ skipSpaces <* string "# "
104105
105−exampleLineStart :: OrgParser ()
106+exampleLineStart :: Monad m => OrgParser m ()
106107exampleLineStart = () <$ try (skipSpaces *> string ": ")
107108
108−noteMarker :: OrgParser String
109+noteMarker :: Monad m => OrgParser m String
109110noteMarker = try $ do
110111 char '['
111112 choice [ many1Till digit (char ']')
@@ -114,12 +115,12 @@ noteMarker = try $ do
114115 ]
115116
116117-- | Succeeds if the parser is at the end of a block.
117−endOfBlock :: OrgParser ()
118+endOfBlock :: Monad m => OrgParser m ()
118119endOfBlock = lookAhead . try $ do
119120 void blankline <|> anyBlockStart
120121 where
121122 -- Succeeds if there is a new block starting at this position.
122− anyBlockStart :: OrgParser ()
123+ anyBlockStart :: Monad m => OrgParser m ()
123124 anyBlockStart = try . choice $
124125 [ exampleLineStart
125126 , hline
src/Text/Pandoc/Readers/Org/Blocks.hs+87−84
@@ -44,9 +44,10 @@ import Text.Pandoc.Readers.Org.Shared
4444
4545import qualified Text.Pandoc.Builder as B
4646import Text.Pandoc.Builder ( Inlines, Blocks )
47+import Text.Pandoc.Class (PandocMonad)
4748import Text.Pandoc.Definition
4849import Text.Pandoc.Options
49−import Text.Pandoc.Shared ( compactify', compactify'DL, safeRead )
50+import Text.Pandoc.Shared ( compactify, compactifyDL, safeRead )
5051
5152import Control.Monad ( foldM, guard, mzero, void )
5253import Data.Char ( isSpace, toLower, toUpper)
@@ -105,7 +106,7 @@ data Headline = Headline
105106
106107-- | Read an Org mode headline and its contents (i.e. a document subtree).
107108-- @lvl@ gives the minimum acceptable level of the tree.
108−headline :: Int -> OrgParser (F Headline)
109+headline :: PandocMonad m => Int -> OrgParser m (F Headline)
109110headline lvl = try $ do
110111 level <- headerStart
111112 guard (lvl <= level)
@@ -130,16 +131,16 @@ headline lvl = try $ do
130131 , headlineChildren = children'
131132 }
132133 where
133− endOfTitle :: OrgParser ()
134+ endOfTitle :: Monad m => OrgParser m ()
134135 endOfTitle = void . lookAhead $ optional headerTags *> newline
135136
136− headerTags :: OrgParser [Tag]
137+ headerTags :: Monad m => OrgParser m [Tag]
137138 headerTags = try $
138139 let tag = many1 (alphaNum <|> oneOf "@%#_") <* char ':'
139140 in map toTag <$> (skipSpaces *> char ':' *> many1 tag <* skipSpaces)
140141
141142-- | Convert an Org mode headline (i.e. a document tree) into pandoc's Blocks
142−headlineToBlocks :: Headline -> OrgParser Blocks
143+headlineToBlocks :: Monad m => Headline -> OrgParser m Blocks
143144headlineToBlocks hdln@(Headline {..}) = do
144145 maxHeadlineLevels <- getExportSetting exportHeadlineLevels
145146 case () of
@@ -162,7 +163,7 @@ isCommentTitle :: Inlines -> Bool
162163isCommentTitle (B.toList -> (Str "COMMENT":_)) = True
163164isCommentTitle _ = False
164165
165−archivedHeadlineToBlocks :: Headline -> OrgParser Blocks
166+archivedHeadlineToBlocks :: Monad m => Headline -> OrgParser m Blocks
166167archivedHeadlineToBlocks hdln = do
167168 archivedTreesOption <- getExportSetting exportArchivedTrees
168169 case archivedTreesOption of
@@ -170,7 +171,7 @@ archivedHeadlineToBlocks hdln = do
170171 ArchivedTreesExport -> headlineToHeaderWithContents hdln
171172 ArchivedTreesHeadlineOnly -> headlineToHeader hdln
172173
173−headlineToHeaderWithList :: Headline -> OrgParser Blocks
174+headlineToHeaderWithList :: Monad m => Headline -> OrgParser m Blocks
174175headlineToHeaderWithList hdln@(Headline {..}) = do
175176 maxHeadlineLevels <- getExportSetting exportHeadlineLevels
176177 header <- headlineToHeader hdln
@@ -189,13 +190,13 @@ headlineToHeaderWithList hdln@(Headline {..}) = do
189190 (Header _ _ inlns:_) -> B.para (B.fromList inlns)
190191 _ -> mempty
191192
192−headlineToHeaderWithContents :: Headline -> OrgParser Blocks
193+headlineToHeaderWithContents :: Monad m => Headline -> OrgParser m Blocks
193194headlineToHeaderWithContents hdln@(Headline {..}) = do
194195 header <- headlineToHeader hdln
195196 childrenBlocks <- mconcat <$> sequence (map headlineToBlocks headlineChildren)
196197 return $ header <> headlineContents <> childrenBlocks
197198
198−headlineToHeader :: Headline -> OrgParser Blocks
199+headlineToHeader :: Monad m => Headline -> OrgParser m Blocks
199200headlineToHeader (Headline {..}) = do
200201 exportTodoKeyword <- getExportSetting exportWithTodoKeywords
201202 let todoText = if exportTodoKeyword
@@ -208,7 +209,7 @@ headlineToHeader (Headline {..}) = do
208209 attr <- registerHeader propAttr headlineText
209210 return $ B.headerWith attr headlineLevel text
210211
211−todoKeyword :: OrgParser TodoMarker
212+todoKeyword :: Monad m => OrgParser m TodoMarker
212213todoKeyword = try $ do
213214 taskStates <- activeTodoMarkers <$> getState
214215 let kwParser tdm = try $ (tdm <$ string (todoMarkerName tdm) <* spaceChar)
@@ -250,7 +251,7 @@ tagToInline t = B.spanWith ("", ["tag"], [("data-tag-name", fromTag t)]) mempty
250251--
251252
252253-- | Get a list of blocks.
253−blockList :: OrgParser [Block]
254+blockList :: PandocMonad m => OrgParser m [Block]
254255blockList = do
255256 initialBlocks <- blocks
256257 headlines <- sequence <$> manyTill (headline 1) eof
@@ -259,15 +260,15 @@ blockList = do
259260 return . B.toList $ (runF initialBlocks st) <> headlineBlocks
260261
261262-- | Get the meta information safed in the state.
262−meta :: OrgParser Meta
263+meta :: Monad m => OrgParser m Meta
263264meta = do
264265 meta' <- metaExport
265266 runF meta' <$> getState
266267
267−blocks :: OrgParser (F Blocks)
268+blocks :: PandocMonad m => OrgParser m (F Blocks)
268269blocks = mconcat <$> manyTill block (void (lookAhead headerStart) <|> eof)
269270
270−block :: OrgParser (F Blocks)
271+block :: PandocMonad m => OrgParser m (F Blocks)
271272block = choice [ mempty <$ blanklines
272273 , table
273274 , orgBlock
@@ -306,7 +307,7 @@ attrFromBlockAttributes (BlockAttributes{..}) =
306307 kv = filter ((`notElem` ["id", "class"]) . fst) blockAttrKeyValues
307308 in (ident, classes, kv)
308309
309−stringyMetaAttribute :: (String -> Bool) -> OrgParser (String, String)
310+stringyMetaAttribute :: Monad m => (String -> Bool) -> OrgParser m (String, String)
310311stringyMetaAttribute attrCheck = try $ do
311312 metaLineStart
312313 attrName <- map toUpper <$> many1Till nonspaceChar (char ':')
@@ -315,7 +316,7 @@ stringyMetaAttribute attrCheck = try $ do
315316 attrValue <- anyLine
316317 return (attrName, attrValue)
317318
318−blockAttributes :: OrgParser BlockAttributes
319+blockAttributes :: PandocMonad m => OrgParser m BlockAttributes
319320blockAttributes = try $ do
320321 kv <- many (stringyMetaAttribute attrCheck)
321322 let caption = foldl' (appendValues "CAPTION") Nothing kv
@@ -350,17 +351,17 @@ blockAttributes = try $ do
350351 Just acc -> Just $ acc ++ ' ':value
351352 Nothing -> Just value
352353
353−keyValues :: OrgParser [(String, String)]
354+keyValues :: Monad m => OrgParser m [(String, String)]
354355keyValues = try $
355356 manyTill ((,) <$> key <*> value) newline
356357 where
357− key :: OrgParser String
358+ key :: Monad m => OrgParser m String
358359 key = try $ skipSpaces *> char ':' *> many1 nonspaceChar
359360
360− value :: OrgParser String
361+ value :: Monad m => OrgParser m String
361362 value = skipSpaces *> manyTill anyChar endOfValue
362363
363− endOfValue :: OrgParser ()
364+ endOfValue :: Monad m => OrgParser m ()
364365 endOfValue =
365366 lookAhead $ (() <$ try (many1 spaceChar <* key))
366367 <|> () <$ newline
@@ -371,7 +372,7 @@ keyValues = try $
371372--
372373
373374-- | Read an org-mode block delimited by #+BEGIN_TYPE and #+END_TYPE.
374−orgBlock :: OrgParser (F Blocks)
375+orgBlock :: PandocMonad m => OrgParser m (F Blocks)
375376orgBlock = try $ do
376377 blockAttrs <- blockAttributes
377378 blkType <- blockHeaderStart
@@ -390,25 +391,25 @@ orgBlock = try $ do
390391 let (ident, classes, kv) = attrFromBlockAttributes blockAttrs
391392 in fmap $ B.divWith (ident, classes ++ [blkType], kv)
392393 where
393− blockHeaderStart :: OrgParser String
394+ blockHeaderStart :: Monad m => OrgParser m String
394395 blockHeaderStart = try $ skipSpaces *> stringAnyCase "#+begin_" *> orgArgWord
395396
396397 lowercase :: String -> String
397398 lowercase = map toLower
398399
399−rawBlockLines :: (String -> F Blocks) -> String -> OrgParser (F Blocks)
400+rawBlockLines :: Monad m => (String -> F Blocks) -> String -> OrgParser m (F Blocks)
400401rawBlockLines f blockType = (ignHeaders *> (f <$> rawBlockContent blockType))
401402
402−parseBlockLines :: (F Blocks -> F Blocks) -> String -> OrgParser (F Blocks)
403+parseBlockLines :: PandocMonad m => (F Blocks -> F Blocks) -> String -> OrgParser m (F Blocks)
403404parseBlockLines f blockType = (ignHeaders *> (f <$> parsedBlockContent))
404405 where
405− parsedBlockContent :: OrgParser (F Blocks)
406+ parsedBlockContent :: PandocMonad m => OrgParser m (F Blocks)
406407 parsedBlockContent = try $ do
407408 raw <- rawBlockContent blockType
408409 parseFromString blocks (raw ++ "\n")
409410
410411-- | Read the raw string content of a block
411−rawBlockContent :: String -> OrgParser String
412+rawBlockContent :: Monad m => String -> OrgParser m String
412413rawBlockContent blockType = try $ do
413414 blkLines <- manyTill rawLine blockEnder
414415 tabLen <- getOption readerTabStop
@@ -418,10 +419,10 @@ rawBlockContent blockType = try $ do
418419 . map (tabsToSpaces tabLen . commaEscaped)
419420 $ blkLines
420421 where
421− rawLine :: OrgParser String
422+ rawLine :: Monad m => OrgParser m String
422423 rawLine = try $ ("" <$ blankline) <|> anyLine
423424
424− blockEnder :: OrgParser ()
425+ blockEnder :: Monad m => OrgParser m ()
425426 blockEnder = try $ skipSpaces <* stringAnyCase ("#+end_" <> blockType)
426427
427428 stripIndent :: [String] -> [String]
@@ -448,18 +449,18 @@ rawBlockContent blockType = try $ do
448449 commaEscaped cs = cs
449450
450451-- | Read but ignore all remaining block headers.
451−ignHeaders :: OrgParser ()
452+ignHeaders :: Monad m => OrgParser m ()
452453ignHeaders = (() <$ newline) <|> (() <$ anyLine)
453454
454455-- | Read a block containing code intended for export in specific backends
455456-- only.
456−exportBlock :: String -> OrgParser (F Blocks)
457+exportBlock :: Monad m => String -> OrgParser m (F Blocks)
457458exportBlock blockType = try $ do
458459 exportType <- skipSpaces *> orgArgWord <* ignHeaders
459460 contents <- rawBlockContent blockType
460461 returnF (B.rawBlock (map toLower exportType) contents)
461462
462−verseBlock :: String -> OrgParser (F Blocks)
463+verseBlock :: PandocMonad m => String -> OrgParser m (F Blocks)
463464verseBlock blockType = try $ do
464465 ignHeaders
465466 content <- rawBlockContent blockType
@@ -468,7 +469,7 @@ verseBlock blockType = try $ do
468469 where
469470 -- replace initial spaces with nonbreaking spaces to preserve
470471 -- indentation, parse the rest as normal inline
471− parseVerseLine :: String -> OrgParser (F Inlines)
472+ parseVerseLine :: PandocMonad m => String -> OrgParser m (F Inlines)
472473 parseVerseLine cs = do
473474 let (initialSpaces, indentedLine) = span isSpace cs
474475 let nbspIndent = if null initialSpaces
@@ -480,7 +481,7 @@ verseBlock blockType = try $ do
480481-- | Read a code block and the associated results block if present. Which of
481482-- boths blocks is included in the output is determined using the "exports"
482483-- argument in the block header.
483−codeBlock :: BlockAttributes -> String -> OrgParser (F Blocks)
484+codeBlock :: PandocMonad m => BlockAttributes -> String -> OrgParser m (F Blocks)
484485codeBlock blockAttrs blockType = do
485486 skipSpaces
486487 (classes, kv) <- codeHeaderArgs <|> (mempty <$ ignHeaders)
@@ -513,7 +514,7 @@ exportsResults :: [(String, String)] -> Bool
513514exportsResults attrs = ("rundoc-exports", "results") `elem` attrs
514515 || ("rundoc-exports", "both") `elem` attrs
515516
516−trailingResultsBlock :: OrgParser (Maybe (F Blocks))
517+trailingResultsBlock :: PandocMonad m => OrgParser m (Maybe (F Blocks))
517518trailingResultsBlock = optionMaybe . try $ do
518519 blanklines
519520 stringAnyCase "#+RESULTS:"
@@ -522,7 +523,7 @@ trailingResultsBlock = optionMaybe . try $ do
522523
523524-- | Parse code block arguments
524525-- TODO: We currently don't handle switches.
525−codeHeaderArgs :: OrgParser ([String], [(String, String)])
526+codeHeaderArgs :: Monad m => OrgParser m ([String], [(String, String)])
526527codeHeaderArgs = try $ do
527528 language <- skipSpaces *> orgArgWord
528529 _ <- skipSpaces *> (try $ switch `sepBy` (many1 spaceChar))
@@ -537,27 +538,27 @@ codeHeaderArgs = try $ do
537538 where
538539 hasRundocParameters = not . null
539540
540−switch :: OrgParser (Char, Maybe String)
541+switch :: Monad m => OrgParser m (Char, Maybe String)
541542switch = try $ simpleSwitch <|> lineNumbersSwitch
542543 where
543544 simpleSwitch = (\c -> (c, Nothing)) <$> (oneOf "-+" *> letter)
544545 lineNumbersSwitch = (\ls -> ('l', Just ls)) <$>
545546 (string "-l \"" *> many1Till nonspaceChar (char '"'))
546547
547−blockOption :: OrgParser (String, String)
548+blockOption :: Monad m => OrgParser m (String, String)
548549blockOption = try $ do
549550 argKey <- orgArgKey
550551 paramValue <- option "yes" orgParamValue
551552 return (argKey, paramValue)
552553
553−orgParamValue :: OrgParser String
554+orgParamValue :: Monad m => OrgParser m String
554555orgParamValue = try $
555556 skipSpaces
556557 *> notFollowedBy (char ':' )
557558 *> many1 nonspaceChar
558559 <* skipSpaces
559560
560−horizontalRule :: OrgParser (F Blocks)
561+horizontalRule :: Monad m => OrgParser m (F Blocks)
561562horizontalRule = return B.horizontalRule <$ try hline
562563
563564
@@ -568,7 +569,7 @@ horizontalRule = return B.horizontalRule <$ try hline
568569-- | A generic drawer which has no special meaning for org-mode.
569570-- Whether or not this drawer is included in the output depends on the drawers
570571-- export setting.
571−genericDrawer :: OrgParser (F Blocks)
572+genericDrawer :: PandocMonad m => OrgParser m (F Blocks)
572573genericDrawer = try $ do
573574 name <- map toUpper <$> drawerStart
574575 content <- manyTill drawerLine (try drawerEnd)
@@ -582,35 +583,35 @@ genericDrawer = try $ do
582583 Right names | name `notElem` names -> return mempty
583584 _ -> drawerDiv name <$> parseLines content
584585 where
585− parseLines :: [String] -> OrgParser (F Blocks)
586+ parseLines :: PandocMonad m => [String] -> OrgParser m (F Blocks)
586587 parseLines = parseFromString blocks . (++ "\n") . unlines
587588
588589 drawerDiv :: String -> F Blocks -> F Blocks
589590 drawerDiv drawerName = fmap $ B.divWith (mempty, [drawerName, "drawer"], mempty)
590591
591−drawerLine :: OrgParser String
592+drawerLine :: Monad m => OrgParser m String
592593drawerLine = anyLine
593594
594−drawerEnd :: OrgParser String
595+drawerEnd :: Monad m => OrgParser m String
595596drawerEnd = try $
596597 skipSpaces *> stringAnyCase ":END:" <* skipSpaces <* newline
597598
598599-- | Read a :PROPERTIES: drawer and return the key/value pairs contained
599600-- within.
600−propertiesDrawer :: OrgParser Properties
601+propertiesDrawer :: Monad m => OrgParser m Properties
601602propertiesDrawer = try $ do
602603 drawerType <- drawerStart
603604 guard $ map toUpper drawerType == "PROPERTIES"
604605 manyTill property (try drawerEnd)
605606 where
606− property :: OrgParser (PropertyKey, PropertyValue)
607+ property :: Monad m => OrgParser m (PropertyKey, PropertyValue)
607608 property = try $ (,) <$> key <*> value
608609
609− key :: OrgParser PropertyKey
610+ key :: Monad m => OrgParser m PropertyKey
610611 key = fmap toPropertyKey . try $
611612 skipSpaces *> char ':' *> many1Till nonspaceChar (char ':')
612613
613− value :: OrgParser PropertyValue
614+ value :: Monad m => OrgParser m PropertyValue
614615 value = fmap toPropertyValue . try $
615616 skipSpaces *> manyTill anyChar (try $ skipSpaces *> newline)
616617
@@ -621,7 +622,7 @@ propertiesDrawer = try $ do
621622
622623-- | Figures or an image paragraph (i.e. an image on a line by itself). Only
623624-- images with a caption attribute are interpreted as figures.
624−figure :: OrgParser (F Blocks)
625+figure :: PandocMonad m => OrgParser m (F Blocks)
625626figure = try $ do
626627 figAttrs <- blockAttributes
627628 src <- skipSpaces *> selfTarget <* skipSpaces <* endOfParagraph
@@ -632,7 +633,7 @@ figure = try $ do
632633 let isFigure = not . isNothing $ blockAttrCaption figAttrs
633634 return $ imageBlock isFigure figAttrs imgSrc
634635 where
635− selfTarget :: OrgParser String
636+ selfTarget :: PandocMonad m => OrgParser m String
636637 selfTarget = try $ char '[' *> linkTarget <* char ']'
637638
638639 imageBlock :: Bool -> BlockAttributes -> String -> F Blocks
@@ -654,7 +655,7 @@ figure = try $ do
654655 else "fig:" ++ cs
655656
656657-- | Succeeds if looking at the end of the current paragraph
657−endOfParagraph :: OrgParser ()
658+endOfParagraph :: Monad m => OrgParser m ()
658659endOfParagraph = try $ skipSpaces *> newline *> endOfBlock
659660
660661
@@ -663,11 +664,11 @@ endOfParagraph = try $ skipSpaces *> newline *> endOfBlock
663664--
664665
665666-- | Example code marked up by a leading colon.
666−example :: OrgParser (F Blocks)
667+example :: Monad m => OrgParser m (F Blocks)
667668example = try $ do
668669 return . return . exampleCode =<< unlines <$> many1 exampleLine
669670 where
670− exampleLine :: OrgParser String
671+ exampleLine :: Monad m => OrgParser m String
671672 exampleLine = try $ exampleLineStart *> anyLine
672673
673674exampleCode :: String -> Blocks
@@ -678,10 +679,10 @@ exampleCode = B.codeBlockWith ("", ["example"], [])
678679-- Comments, Options and Metadata
679680--
680681
681−specialLine :: OrgParser (F Blocks)
682+specialLine :: PandocMonad m => OrgParser m (F Blocks)
682683specialLine = fmap return . try $ rawExportLine <|> metaLine <|> commentLine
683684
684−rawExportLine :: OrgParser Blocks
685+rawExportLine :: PandocMonad m => OrgParser m Blocks
685686rawExportLine = try $ do
686687 metaLineStart
687688 key <- metaKey
@@ -689,7 +690,7 @@ rawExportLine = try $ do
689690 then B.rawBlock key <$> anyLine
690691 else mzero
691692
692−commentLine :: OrgParser Blocks
693+commentLine :: Monad m => OrgParser m Blocks
693694commentLine = commentLineStart *> anyLine *> pure mempty
694695
695696
@@ -718,7 +719,7 @@ data OrgTable = OrgTable
718719 , orgTableRows :: [[Blocks]]
719720 }
720721
721−table :: OrgParser (F Blocks)
722+table :: PandocMonad m => OrgParser m (F Blocks)
722723table = try $ do
723724 blockAttrs <- blockAttributes
724725 lookAhead tableStart
@@ -745,18 +746,18 @@ orgToPandocTable (OrgTable colProps heads lns) caption =
745746 <*> totalWidth
746747 in (align', width')
747748
748−tableRows :: OrgParser [OrgTableRow]
749+tableRows :: PandocMonad m => OrgParser m [OrgTableRow]
749750tableRows = try $ many (tableAlignRow <|> tableHline <|> tableContentRow)
750751
751−tableContentRow :: OrgParser OrgTableRow
752+tableContentRow :: PandocMonad m => OrgParser m OrgTableRow
752753tableContentRow = try $
753754 OrgContentRow . sequence <$> (tableStart *> many1Till tableContentCell newline)
754755
755−tableContentCell :: OrgParser (F Blocks)
756+tableContentCell :: PandocMonad m => OrgParser m (F Blocks)
756757tableContentCell = try $
757758 fmap B.plain . trimInlinesF . mconcat <$> manyTill inline endOfCell
758759
759−tableAlignRow :: OrgParser OrgTableRow
760+tableAlignRow :: Monad m => OrgParser m OrgTableRow
760761tableAlignRow = try $ do
761762 tableStart
762763 colProps <- many1Till columnPropertyCell newline
@@ -764,7 +765,7 @@ tableAlignRow = try $ do
764765 guard $ any (/= def) colProps
765766 return $ OrgAlignRow colProps
766767
767−columnPropertyCell :: OrgParser ColumnProperty
768+columnPropertyCell :: Monad m => OrgParser m ColumnProperty
768769columnPropertyCell = emptyCell <|> propCell <?> "alignment info"
769770 where
770771 emptyCell = ColumnProperty Nothing Nothing <$ (try $ skipSpaces *> endOfCell)
@@ -776,18 +777,18 @@ columnPropertyCell = emptyCell <|> propCell <?> "alignment info"
776777 <* char '>'
777778 <* emptyCell)
778779
779−tableAlignFromChar :: OrgParser Alignment
780+tableAlignFromChar :: Monad m => OrgParser m Alignment
780781tableAlignFromChar = try $
781782 choice [ char 'l' *> return AlignLeft
782783 , char 'c' *> return AlignCenter
783784 , char 'r' *> return AlignRight
784785 ]
785786
786−tableHline :: OrgParser OrgTableRow
787+tableHline :: Monad m => OrgParser m OrgTableRow
787788tableHline = try $
788789 OrgHlineRow <$ (tableStart *> char '-' *> anyLine)
789790
790−endOfCell :: OrgParser Char
791+endOfCell :: Monad m => OrgParser m Char
791792endOfCell = try $ char '|' <|> lookAhead newline
792793
793794rowsToTable :: [OrgTableRow]
@@ -840,7 +841,7 @@ rowToContent orgTable row =
840841--
841842-- LaTeX fragments
842843--
843−latexFragment :: OrgParser (F Blocks)
844+latexFragment :: Monad m => OrgParser m (F Blocks)
844845latexFragment = try $ do
845846 envName <- latexEnvStart
846847 content <- mconcat <$> manyTill anyLineNewline (latexEnd envName)
@@ -851,7 +852,7 @@ latexFragment = try $ do
851852 , "\\end{", e, "}\n"
852853 ]
853854
854−latexEnd :: String -> OrgParser ()
855+latexEnd :: Monad m => String -> OrgParser m ()
855856latexEnd envName = try $
856857 () <$ skipSpaces
857858 <* string ("\\end{" ++ envName ++ "}")
@@ -861,7 +862,7 @@ latexEnd envName = try $
861862--
862863-- Footnote defintions
863864--
864−noteBlock :: OrgParser (F Blocks)
865+noteBlock :: PandocMonad m => OrgParser m (F Blocks)
865866noteBlock = try $ do
866867 ref <- noteMarker <* skipSpaces
867868 content <- mconcat <$> blocksTillHeaderOrNote
@@ -873,7 +874,7 @@ noteBlock = try $ do
873874 <|> () <$ lookAhead headerStart)
874875
875876-- Paragraphs or Plain text
876−paraOrPlain :: OrgParser (F Blocks)
877+paraOrPlain :: PandocMonad m => OrgParser m (F Blocks)
877878paraOrPlain = try $ do
878879 -- Make sure we are not looking at a headline
879880 notFollowedBy' (char '*' *> (oneOf " *"))
@@ -892,24 +893,24 @@ paraOrPlain = try $ do
892893-- list blocks
893894--
894895
895−list :: OrgParser (F Blocks)
896+list :: PandocMonad m => OrgParser m (F Blocks)
896897list = choice [ definitionList, bulletList, orderedList ] <?> "list"
897898
898−definitionList :: OrgParser (F Blocks)
899+definitionList :: PandocMonad m => OrgParser m (F Blocks)
899900definitionList = try $ do n <- lookAhead (bulletListStart' Nothing)
900− fmap B.definitionList . fmap compactify'DL . sequence
901+ fmap B.definitionList . fmap compactifyDL . sequence
901902 <$> many1 (definitionListItem $ bulletListStart' (Just n))
902903
903−bulletList :: OrgParser (F Blocks)
904+bulletList :: PandocMonad m => OrgParser m (F Blocks)
904905bulletList = try $ do n <- lookAhead (bulletListStart' Nothing)
905− fmap B.bulletList . fmap compactify' . sequence
906+ fmap B.bulletList . fmap compactify . sequence
906907 <$> many1 (listItem (bulletListStart' $ Just n))
907908
908−orderedList :: OrgParser (F Blocks)
909−orderedList = fmap B.orderedList . fmap compactify' . sequence
909+orderedList :: PandocMonad m => OrgParser m (F Blocks)
910+orderedList = fmap B.orderedList . fmap compactify . sequence
910911 <$> many1 (listItem orderedListStart)
911912
912−bulletListStart' :: Maybe Int -> OrgParser Int
913+bulletListStart' :: Monad m => Maybe Int -> OrgParser m Int
913914-- returns length of bulletList prefix, inclusive of marker
914915bulletListStart' Nothing = do ind <- length <$> many spaceChar
915916 oneOf (bullets $ ind == 0)
@@ -925,8 +926,9 @@ bulletListStart' (Just n) = do count (n-1) spaceChar
925926bullets :: Bool -> String
926927bullets unindented = if unindented then "+-" else "*+-"
927928
928−definitionListItem :: OrgParser Int
929− -> OrgParser (F (Inlines, [Blocks]))
929+definitionListItem :: PandocMonad m
930+ => OrgParser m Int
931+ -> OrgParser m (F (Inlines, [Blocks]))
930932definitionListItem parseMarkerGetLength = try $ do
931933 markerLength <- parseMarkerGetLength
932934 term <- manyTill (noneOf "\n\r") (try definitionMarker)
@@ -942,8 +944,9 @@ definitionListItem parseMarkerGetLength = try $ do
942944
943945
944946-- parse raw text for one list item, excluding start marker and continuations
945−listItem :: OrgParser Int
946− -> OrgParser (F Blocks)
947+listItem :: PandocMonad m
948+ => OrgParser m Int
949+ -> OrgParser m (F Blocks)
947950listItem start = try . withContext ListItemState $ do
948951 markerLength <- try start
949952 firstLine <- anyLineNewline
@@ -953,8 +956,8 @@ listItem start = try . withContext ListItemState $ do
953956
954957-- continuation of a list item - indented and separated by blankline or endline.
955958-- Note: nested lists are parsed as continuations.
956−listContinuation :: Int
957− -> OrgParser String
959+listContinuation :: Monad m => Int
960+ -> OrgParser m String
958961listContinuation markerLength = try $
959962 notFollowedBy' blankline
960963 *> (mappend <$> (concat <$> many1 listLine)
@@ -963,7 +966,7 @@ listContinuation markerLength = try $
963966 listLine = try $ indentWith markerLength *> anyLineNewline
964967
965968 -- indent by specified number of spaces (or equiv. tabs)
966− indentWith :: Int -> OrgParser String
969+ indentWith :: Monad m => Int -> OrgParser m String
967970 indentWith num = do
968971 tabStop <- getOption readerTabStop
969972 if num < tabStop
@@ -972,5 +975,5 @@ listContinuation markerLength = try $
972975 , try (char '\t' >> count (num - tabStop) (char ' ')) ]
973976
974977-- | Parse any line, include the final newline in the output.
975−anyLineNewline :: OrgParser String
978+anyLineNewline :: Monad m => OrgParser m String
976979anyLineNewline = (++ "\n") <$> anyLine
src/Text/Pandoc/Readers/Org/ExportSettings.hs+18−15
@@ -37,14 +37,14 @@ import Data.Char ( toLower )
3737import Data.Maybe ( listToMaybe )
3838
3939-- | Read and handle space separated org-mode export settings.
40−exportSettings :: OrgParser ()
40+exportSettings :: Monad m => OrgParser m ()
4141exportSettings = void $ sepBy spaces exportSetting
4242
4343-- | Setter function for export settings.
4444type ExportSettingSetter a = a -> ExportSettings -> ExportSettings
4545
4646-- | Read and process a single org-mode export option.
47−exportSetting :: OrgParser ()
47+exportSetting :: Monad m => OrgParser m ()
4848exportSetting = choice
4949 [ booleanSetting "^" (\val es -> es { exportSubSuperscripts = val })
5050 , booleanSetting "'" (\val es -> es { exportSmartQuotes = val })
@@ -81,10 +81,11 @@ exportSetting = choice
8181 , ignoredSetting "|"
8282 ] <?> "export setting"
8383
84−genericExportSetting :: OrgParser a
84+genericExportSetting :: Monad m
85+ => OrgParser m a
8586 -> String
8687 -> ExportSettingSetter a
87− -> OrgParser ()
88+ -> OrgParser m ()
8889genericExportSetting optionParser settingIdentifier setter = try $ do
8990 _ <- string settingIdentifier *> char ':'
9091 value <- optionParser
@@ -94,11 +95,11 @@ genericExportSetting optionParser settingIdentifier setter = try $ do
9495 st { orgStateExportSettings = setter val . orgStateExportSettings $ st }
9596
9697-- | A boolean option, either nil (False) or non-nil (True).
97−booleanSetting :: String -> ExportSettingSetter Bool -> OrgParser ()
98+booleanSetting :: Monad m => String -> ExportSettingSetter Bool -> OrgParser m ()
9899booleanSetting = genericExportSetting elispBoolean
99100
100101-- | An integer-valued option.
101−integerSetting :: String -> ExportSettingSetter Int -> OrgParser ()
102+integerSetting :: Monad m => String -> ExportSettingSetter Int -> OrgParser m ()
102103integerSetting = genericExportSetting parseInt
103104 where
104105 parseInt = try $
@@ -106,9 +107,10 @@ integerSetting = genericExportSetting parseInt
106107
107108-- | Either the string "headline" or an elisp boolean and treated as an
108109-- @ArchivedTreesOption@.
109−archivedTreeSetting :: String
110+archivedTreeSetting :: Monad m
111+ => String
110112 -> ExportSettingSetter ArchivedTreesOption
111− -> OrgParser ()
113+ -> OrgParser m ()
112114archivedTreeSetting =
113115 genericExportSetting $ archivedTreesHeadlineSetting <|> archivedTreesBoolean
114116 where
@@ -125,9 +127,10 @@ archivedTreeSetting =
125127 else ArchivedTreesNoExport
126128
127129-- | A list or a complement list (i.e. a list starting with `not`).
128−complementableListSetting :: String
130+complementableListSetting :: Monad m
131+ => String
129132 -> ExportSettingSetter (Either [String] [String])
130− -> OrgParser ()
133+ -> OrgParser m ()
131134complementableListSetting = genericExportSetting $ choice
132135 [ Left <$> complementStringList
133136 , Right <$> stringList
@@ -135,31 +138,31 @@ complementableListSetting = genericExportSetting $ choice
135138 ]
136139 where
137140 -- Read a plain list of strings.
138− stringList :: OrgParser [String]
141+ stringList :: Monad m => OrgParser m [String]
139142 stringList = try $
140143 char '('
141144 *> sepBy elispString spaces
142145 <* char ')'
143146
144147 -- Read an emacs lisp list specifying a complement set.
145− complementStringList :: OrgParser [String]
148+ complementStringList :: Monad m => OrgParser m [String]
146149 complementStringList = try $
147150 string "(not "
148151 *> sepBy elispString spaces
149152 <* char ')'
150153
151− elispString :: OrgParser String
154+ elispString :: Monad m => OrgParser m String
152155 elispString = try $
153156 char '"'
154157 *> manyTill alphaNum (char '"')
155158
156159-- | Read but ignore the export setting.
157−ignoredSetting :: String -> OrgParser ()
160+ignoredSetting :: Monad m => String -> OrgParser m ()
158161ignoredSetting s = try (() <$ string s <* char ':' <* many1 nonspaceChar)
159162
160163-- | Read an elisp boolean. Only NIL is treated as false, non-NIL values are
161164-- interpreted as true.
162−elispBoolean :: OrgParser Bool
165+elispBoolean :: Monad m => OrgParser m Bool
163166elispBoolean = try $ do
164167 value <- many1 nonspaceChar
165168 return $ case map toLower value of
src/Text/Pandoc/Readers/Org/Inlines.hs+130−118
@@ -47,9 +47,11 @@ import Text.Pandoc.Options
4747import Text.Pandoc.Readers.LaTeX ( inlineCommand, rawLaTeXInline )
4848import Text.TeXMath ( readTeX, writePandoc, DisplayType(..) )
4949import qualified Text.TeXMath.Readers.MathML.EntityMap as MathMLEntityMap
50+import Text.Pandoc.Class (PandocMonad)
5051
5152import Prelude hiding (sequence)
5253import Control.Monad ( guard, mplus, mzero, when, void )
54+import Control.Monad.Trans ( lift )
5355import Data.Char ( isAlphaNum, isSpace )
5456import Data.List ( intersperse )
5557import Data.Maybe ( fromMaybe )
@@ -60,46 +62,46 @@ import Data.Traversable (sequence)
6062--
6163-- Functions acting on the parser state
6264--
63−recordAnchorId :: String -> OrgParser ()
65+recordAnchorId :: PandocMonad m => String -> OrgParser m ()
6466recordAnchorId i = updateState $ \s ->
6567 s{ orgStateAnchorIds = i : (orgStateAnchorIds s) }
6668
67−pushToInlineCharStack :: Char -> OrgParser ()
69+pushToInlineCharStack :: PandocMonad m => Char -> OrgParser m ()
6870pushToInlineCharStack c = updateState $ \s ->
6971 s{ orgStateEmphasisCharStack = c:orgStateEmphasisCharStack s }
7072
71−popInlineCharStack :: OrgParser ()
73+popInlineCharStack :: PandocMonad m => OrgParser m ()
7274popInlineCharStack = updateState $ \s ->
7375 s{ orgStateEmphasisCharStack = drop 1 . orgStateEmphasisCharStack $ s }
7476
75−surroundingEmphasisChar :: OrgParser [Char]
77+surroundingEmphasisChar :: PandocMonad m => OrgParser m [Char]
7678surroundingEmphasisChar =
7779 take 1 . drop 1 . orgStateEmphasisCharStack <$> getState
7880
79−startEmphasisNewlinesCounting :: Int -> OrgParser ()
81+startEmphasisNewlinesCounting :: PandocMonad m => Int -> OrgParser m ()
8082startEmphasisNewlinesCounting maxNewlines = updateState $ \s ->
8183 s{ orgStateEmphasisNewlines = Just maxNewlines }
8284
83−decEmphasisNewlinesCount :: OrgParser ()
85+decEmphasisNewlinesCount :: PandocMonad m => OrgParser m ()
8486decEmphasisNewlinesCount = updateState $ \s ->
8587 s{ orgStateEmphasisNewlines = (\n -> n - 1) <$> orgStateEmphasisNewlines s }
8688
87−newlinesCountWithinLimits :: OrgParser Bool
89+newlinesCountWithinLimits :: PandocMonad m => OrgParser m Bool
8890newlinesCountWithinLimits = do
8991 st <- getState
9092 return $ ((< 0) <$> orgStateEmphasisNewlines st) /= Just True
9193
92−resetEmphasisNewlines :: OrgParser ()
94+resetEmphasisNewlines :: PandocMonad m => OrgParser m ()
9395resetEmphasisNewlines = updateState $ \s ->
9496 s{ orgStateEmphasisNewlines = Nothing }
9597
96−addToNotesTable :: OrgNoteRecord -> OrgParser ()
98+addToNotesTable :: PandocMonad m => OrgNoteRecord -> OrgParser m ()
9799addToNotesTable note = do
98100 oldnotes <- orgStateNotes' <$> getState
99101 updateState $ \s -> s{ orgStateNotes' = note:oldnotes }
100102
101103-- | Parse a single Org-mode inline element
102−inline :: OrgParser (F Inlines)
104+inline :: PandocMonad m => OrgParser m (F Inlines)
103105inline =
104106 choice [ whitespace
105107 , linebreak
@@ -125,7 +127,7 @@ inline =
125127 <?> "inline"
126128
127129-- | Read the rest of the input as inlines.
128−inlines :: OrgParser (F Inlines)
130+inlines :: PandocMonad m => OrgParser m (F Inlines)
129131inlines = trimInlinesF . mconcat <$> many1 inline
130132
131133-- treat these as potentially non-text when parsing inline:
@@ -133,23 +135,23 @@ specialChars :: [Char]
133135specialChars = "\"$'()*+-,./:;<=>@[\\]^_{|}~"
134136
135137
136−whitespace :: OrgParser (F Inlines)
138+whitespace :: PandocMonad m => OrgParser m (F Inlines)
137139whitespace = pure B.space <$ skipMany1 spaceChar
138140 <* updateLastPreCharPos
139141 <* updateLastForbiddenCharPos
140142 <?> "whitespace"
141143
142−linebreak :: OrgParser (F Inlines)
144+linebreak :: PandocMonad m => OrgParser m (F Inlines)
143145linebreak = try $ pure B.linebreak <$ string "\\\\" <* skipSpaces <* newline
144146
145−str :: OrgParser (F Inlines)
147+str :: PandocMonad m => OrgParser m (F Inlines)
146148str = return . B.str <$> many1 (noneOf $ specialChars ++ "\n\r ")
147149 <* updateLastStrPos
148150
149151-- | An endline character that can be treated as a space, not a structural
150152-- break. This should reflect the values of the Emacs variable
151153-- @org-element-pagaraph-separate@.
152−endline :: OrgParser (F Inlines)
154+endline :: PandocMonad m => OrgParser m (F Inlines)
153155endline = try $ do
154156 newline
155157 notFollowedBy' endOfBlock
@@ -174,7 +176,7 @@ endline = try $ do
174176-- contributors. All this should be consolidated once an official Org-mode
175177-- citation syntax has emerged.
176178
177−cite :: OrgParser (F Inlines)
179+cite :: PandocMonad m => OrgParser m (F Inlines)
178180cite = try $ berkeleyCite <|> do
179181 guardEnabled Ext_citations
180182 (cs, raw) <- withRaw $ choice
@@ -185,40 +187,41 @@ cite = try $ berkeleyCite <|> do
185187 return $ (flip B.cite (B.text raw)) <$> cs
186188
187189-- | A citation in Pandoc Org-mode style (@[prefix \@citekey suffix]@).
188−pandocOrgCite :: OrgParser (F [Citation])
190+pandocOrgCite :: PandocMonad m => OrgParser m (F [Citation])
189191pandocOrgCite = try $
190192 char '[' *> skipSpaces *> citeList <* skipSpaces <* char ']'
191193
192−orgRefCite :: OrgParser (F [Citation])
194+orgRefCite :: PandocMonad m => OrgParser m (F [Citation])
193195orgRefCite = try $ choice
194196 [ normalOrgRefCite
195197 , fmap (:[]) <$> linkLikeOrgRefCite
196198 ]
197199
198−normalOrgRefCite :: OrgParser (F [Citation])
200+normalOrgRefCite :: PandocMonad m => OrgParser m (F [Citation])
199201normalOrgRefCite = try $ do
200202 mode <- orgRefCiteMode
201− -- org-ref style citation key, parsed into a citation of the given mode
202− let orgRefCiteItem :: OrgParser (F Citation)
203− orgRefCiteItem = try $ do
204− key <- orgRefCiteKey
205− returnF $ Citation
206− { citationId = key
207− , citationPrefix = mempty
208− , citationSuffix = mempty
209− , citationMode = mode
210− , citationNoteNum = 0
211− , citationHash = 0
212− }
213− firstCitation <- orgRefCiteItem
214− moreCitations <- many (try $ char ',' *> orgRefCiteItem)
203+ firstCitation <- orgRefCiteList mode
204+ moreCitations <- many (try $ char ',' *> orgRefCiteList mode)
215205 return . sequence $ firstCitation : moreCitations
216− where
206+ where
207+ -- | A list of org-ref style citation keys, parsed as citation of the given
208+ -- citation mode.
209+ orgRefCiteList :: PandocMonad m => CitationMode -> OrgParser m (F Citation)
210+ orgRefCiteList citeMode = try $ do
211+ key <- orgRefCiteKey
212+ returnF $ Citation
213+ { citationId = key
214+ , citationPrefix = mempty
215+ , citationSuffix = mempty
216+ , citationMode = citeMode
217+ , citationNoteNum = 0
218+ , citationHash = 0
219+ }
217220
218221-- | Read an Berkeley-style Org-mode citation. Berkeley citation style was
219222-- develop and adjusted to Org-mode style by John MacFarlane and Richard
220223-- Lawrence, respectively, both philosophers at UC Berkeley.
221−berkeleyCite :: OrgParser (F Inlines)
224+berkeleyCite :: PandocMonad m => OrgParser m (F Inlines)
222225berkeleyCite = try $ do
223226 bcl <- berkeleyCitationList
224227 return $ do
@@ -260,7 +263,7 @@ data BerkeleyCitationList = BerkeleyCitationList
260263 , berkeleyCiteCommonSuffix :: Maybe Inlines
261264 , berkeleyCiteCitations :: [Citation]
262265 }
263−berkeleyCitationList :: OrgParser (F BerkeleyCitationList)
266+berkeleyCitationList :: PandocMonad m => OrgParser m (F BerkeleyCitationList)
264267berkeleyCitationList = try $ do
265268 char '['
266269 parens <- choice [ False <$ berkeleyBareTag, True <$ berkeleyParensTag ]
@@ -275,22 +278,22 @@ berkeleyCitationList = try $ do
275278 <*> sequence commonSuffix
276279 <*> citations)
277280 where
278− citationListPart :: OrgParser (F Inlines)
281+ citationListPart :: PandocMonad m => OrgParser m (F Inlines)
279282 citationListPart = fmap (trimInlinesF . mconcat) . try . many1 $ do
280283 notFollowedBy' citeKey
281284 notFollowedBy (oneOf ";]")
282285 inline
283286
284−berkeleyBareTag :: OrgParser ()
287+berkeleyBareTag :: PandocMonad m => OrgParser m ()
285288berkeleyBareTag = try $ void berkeleyBareTag'
286289
287−berkeleyParensTag :: OrgParser ()
290+berkeleyParensTag :: PandocMonad m => OrgParser m ()
288291berkeleyParensTag = try . void $ enclosedByPair '(' ')' berkeleyBareTag'
289292
290−berkeleyBareTag' :: OrgParser ()
293+berkeleyBareTag' :: PandocMonad m => OrgParser m ()
291294berkeleyBareTag' = try $ void (string "cite")
292295
293−berkeleyTextualCite :: OrgParser (F [Citation])
296+berkeleyTextualCite :: PandocMonad m => OrgParser m (F [Citation])
294297berkeleyTextualCite = try $ do
295298 (suppressAuthor, key) <- citeKey
296299 returnF . return $ Citation
@@ -305,14 +308,14 @@ berkeleyTextualCite = try $ do
305308-- The following is what a Berkeley-style bracketed textual citation parser
306309-- would look like. However, as these citations are a subset of Pandoc's Org
307310-- citation style, this isn't used.
308−-- berkeleyBracketedTextualCite :: OrgParser (F [Citation])
311+-- berkeleyBracketedTextualCite :: PandocMonad m => OrgParser m (F [Citation])
309312-- berkeleyBracketedTextualCite = try . (fmap head) $
310313-- enclosedByPair '[' ']' berkeleyTextualCite
311314
312315-- | Read a link-like org-ref style citation. The citation includes pre and
313316-- post text. However, multiple citations are not possible due to limitations
314317-- in the syntax.
315−linkLikeOrgRefCite :: OrgParser (F Citation)
318+linkLikeOrgRefCite :: PandocMonad m => OrgParser m (F Citation)
316319linkLikeOrgRefCite = try $ do
317320 _ <- string "[["
318321 mode <- orgRefCiteMode
@@ -335,13 +338,13 @@ linkLikeOrgRefCite = try $ do
335338
336339-- | Read a citation key. The characters allowed in citation keys are taken
337340-- from the `org-ref-cite-re` variable in `org-ref.el`.
338−orgRefCiteKey :: OrgParser String
341+orgRefCiteKey :: PandocMonad m => OrgParser m String
339342orgRefCiteKey = try . many1 . satisfy $ \c ->
340343 isAlphaNum c || c `elem` ("-_:\\./"::String)
341344
342345-- | Supported citation types. Only a small subset of org-ref types is
343346-- supported for now. TODO: rewrite this, use LaTeX reader as template.
344−orgRefCiteMode :: OrgParser CitationMode
347+orgRefCiteMode :: PandocMonad m => OrgParser m CitationMode
345348orgRefCiteMode =
346349 choice $ map (\(s, mode) -> mode <$ try (string s <* char ':'))
347350 [ ("cite", AuthorInText)
@@ -352,10 +355,10 @@ orgRefCiteMode =
352355 , ("citeyear", SuppressAuthor)
353356 ]
354357
355−citeList :: OrgParser (F [Citation])
358+citeList :: PandocMonad m => OrgParser m (F [Citation])
356359citeList = sequence <$> sepEndBy1 citation (try $ char ';' *> skipSpaces)
357360
358−citation :: OrgParser (F Citation)
361+citation :: PandocMonad m => OrgParser m (F Citation)
359362citation = try $ do
360363 pref <- prefix
361364 (suppress_author, key) <- citeKey
@@ -384,10 +387,10 @@ citation = try $ do
384387 then (B.space <>) <$> rest
385388 else rest
386389
387−footnote :: OrgParser (F Inlines)
390+footnote :: PandocMonad m => OrgParser m (F Inlines)
388391footnote = try $ inlineNote <|> referencedNote
389392
390−inlineNote :: OrgParser (F Inlines)
393+inlineNote :: PandocMonad m => OrgParser m (F Inlines)
391394inlineNote = try $ do
392395 string "[fn:"
393396 ref <- many alphaNum
@@ -397,7 +400,7 @@ inlineNote = try $ do
397400 addToNotesTable ("fn:" ++ ref, note)
398401 return $ B.note <$> note
399402
400−referencedNote :: OrgParser (F Inlines)
403+referencedNote :: PandocMonad m => OrgParser m (F Inlines)
401404referencedNote = try $ do
402405 ref <- noteMarker
403406 return $ do
@@ -409,14 +412,14 @@ referencedNote = try $ do
409412 let contents' = runF contents st{ orgStateNotes' = [] }
410413 return $ B.note contents'
411414
412−linkOrImage :: OrgParser (F Inlines)
415+linkOrImage :: PandocMonad m => OrgParser m (F Inlines)
413416linkOrImage = explicitOrImageLink
414417 <|> selflinkOrImage
415418 <|> angleLink
416419 <|> plainLink
417420 <?> "link or image"
418421
419−explicitOrImageLink :: OrgParser (F Inlines)
422+explicitOrImageLink :: PandocMonad m => OrgParser m (F Inlines)
420423explicitOrImageLink = try $ do
421424 char '['
422425 srcF <- applyCustomLinkFormat =<< possiblyEmptyLinkTarget
@@ -431,30 +434,30 @@ explicitOrImageLink = try $ do
431434 _ ->
432435 linkToInlinesF src =<< title'
433436
434−selflinkOrImage :: OrgParser (F Inlines)
437+selflinkOrImage :: PandocMonad m => OrgParser m (F Inlines)
435438selflinkOrImage = try $ do
436439 src <- char '[' *> linkTarget <* char ']'
437440 return $ linkToInlinesF src (B.str src)
438441
439−plainLink :: OrgParser (F Inlines)
442+plainLink :: PandocMonad m => OrgParser m (F Inlines)
440443plainLink = try $ do
441444 (orig, src) <- uri
442445 returnF $ B.link src "" (B.str orig)
443446
444−angleLink :: OrgParser (F Inlines)
447+angleLink :: PandocMonad m => OrgParser m (F Inlines)
445448angleLink = try $ do
446449 char '<'
447450 link <- plainLink
448451 char '>'
449452 return link
450453
451−linkTarget :: OrgParser String
454+linkTarget :: PandocMonad m => OrgParser m String
452455linkTarget = enclosedByPair '[' ']' (noneOf "\n\r[]")
453456
454−possiblyEmptyLinkTarget :: OrgParser String
457+possiblyEmptyLinkTarget :: PandocMonad m => OrgParser m String
455458possiblyEmptyLinkTarget = try linkTarget <|> ("" <$ string "[]")
456459
457−applyCustomLinkFormat :: String -> OrgParser (F String)
460+applyCustomLinkFormat :: String -> OrgParser m (F String)
458461applyCustomLinkFormat link = do
459462 let (linkType, rest) = break (== ':') link
460463 return $ do
@@ -487,7 +490,7 @@ internalLink link title = do
487490-- @anchor-id@ contains spaces, we are more restrictive in what is accepted as
488491-- an anchor.
489492
490−anchor :: OrgParser (F Inlines)
493+anchor :: PandocMonad m => OrgParser m (F Inlines)
491494anchor = try $ do
492495 anchorId <- parseAnchor
493496 recordAnchorId anchorId
@@ -509,7 +512,7 @@ solidify = map replaceSpecialChar
509512 | otherwise = '-'
510513
511514-- | Parses an inline code block and marks it as an babel block.
512−inlineCodeBlock :: OrgParser (F Inlines)
515+inlineCodeBlock :: PandocMonad m => OrgParser m (F Inlines)
513516inlineCodeBlock = try $ do
514517 string "src_"
515518 lang <- many1 orgArgWordChar
@@ -519,13 +522,13 @@ inlineCodeBlock = try $ do
519522 let attrKeyVal = map toRundocAttrib (("language", lang) : opts)
520523 returnF $ B.codeWith ("", attrClasses, attrKeyVal) inlineCode
521524 where
522− inlineBlockOption :: OrgParser (String, String)
525+ inlineBlockOption :: PandocMonad m => OrgParser m (String, String)
523526 inlineBlockOption = try $ do
524527 argKey <- orgArgKey
525528 paramValue <- option "yes" orgInlineParamValue
526529 return (argKey, paramValue)
527530
528− orgInlineParamValue :: OrgParser String
531+ orgInlineParamValue :: PandocMonad m => OrgParser m String
529532 orgInlineParamValue = try $
530533 skipSpaces
531534 *> notFollowedBy (char ':')
@@ -533,7 +536,7 @@ inlineCodeBlock = try $ do
533536 <* skipSpaces
534537
535538
536−emphasizedText :: OrgParser (F Inlines)
539+emphasizedText :: PandocMonad m => OrgParser m (F Inlines)
537540emphasizedText = do
538541 state <- getState
539542 guard . exportEmphasizedText . orgStateExportSettings $ state
@@ -544,60 +547,63 @@ emphasizedText = do
544547 , underline
545548 ]
546549
547−enclosedByPair :: Char -- ^ opening char
550+enclosedByPair :: PandocMonad m
551+ => Char -- ^ opening char
548552 -> Char -- ^ closing char
549− -> OrgParser a -- ^ parser
550− -> OrgParser [a]
553+ -> OrgParser m a -- ^ parser
554+ -> OrgParser m [a]
551555enclosedByPair s e p = char s *> many1Till p (char e)
552556
553−emph :: OrgParser (F Inlines)
557+emph :: PandocMonad m => OrgParser m (F Inlines)
554558emph = fmap B.emph <$> emphasisBetween '/'
555559
556−strong :: OrgParser (F Inlines)
560+strong :: PandocMonad m => OrgParser m (F Inlines)
557561strong = fmap B.strong <$> emphasisBetween '*'
558562
559−strikeout :: OrgParser (F Inlines)
563+strikeout :: PandocMonad m => OrgParser m (F Inlines)
560564strikeout = fmap B.strikeout <$> emphasisBetween '+'
561565
562566-- There is no underline, so we use strong instead.
563−underline :: OrgParser (F Inlines)
567+underline :: PandocMonad m => OrgParser m (F Inlines)
564568underline = fmap B.strong <$> emphasisBetween '_'
565569
566−verbatim :: OrgParser (F Inlines)
570+verbatim :: PandocMonad m => OrgParser m (F Inlines)
567571verbatim = return . B.code <$> verbatimBetween '='
568572
569−code :: OrgParser (F Inlines)
573+code :: PandocMonad m => OrgParser m (F Inlines)
570574code = return . B.code <$> verbatimBetween '~'
571575
572−subscript :: OrgParser (F Inlines)
576+subscript :: PandocMonad m => OrgParser m (F Inlines)
573577subscript = fmap B.subscript <$> try (char '_' *> subOrSuperExpr)
574578
575−superscript :: OrgParser (F Inlines)
579+superscript :: PandocMonad m => OrgParser m (F Inlines)
576580superscript = fmap B.superscript <$> try (char '^' *> subOrSuperExpr)
577581
578−math :: OrgParser (F Inlines)
582+math :: PandocMonad m => OrgParser m (F Inlines)
579583math = return . B.math <$> choice [ math1CharBetween '$'
580584 , mathStringBetween '$'
581585 , rawMathBetween "\\(" "\\)"
582586 ]
583587
584−displayMath :: OrgParser (F Inlines)
588+displayMath :: PandocMonad m => OrgParser m (F Inlines)
585589displayMath = return . B.displayMath <$> choice [ rawMathBetween "\\[" "\\]"
586590 , rawMathBetween "$$" "$$"
587591 ]
588592
589−updatePositions :: Char
590− -> OrgParser Char
593+updatePositions :: PandocMonad m
594+ => Char
595+ -> OrgParser m Char
591596updatePositions c = do
592597 when (c `elem` emphasisPreChars) updateLastPreCharPos
593598 when (c `elem` emphasisForbiddenBorderChars) updateLastForbiddenCharPos
594599 return c
595600
596−symbol :: OrgParser (F Inlines)
601+symbol :: PandocMonad m => OrgParser m (F Inlines)
597602symbol = return . B.str . (: "") <$> (oneOf specialChars >>= updatePositions)
598603
599−emphasisBetween :: Char
600− -> OrgParser (F Inlines)
604+emphasisBetween :: PandocMonad m
605+ => Char
606+ -> OrgParser m (F Inlines)
601607emphasisBetween c = try $ do
602608 startEmphasisNewlinesCounting emphasisAllowedNewlines
603609 res <- enclosedInlines (emphasisStart c) (emphasisEnd c)
@@ -606,8 +612,9 @@ emphasisBetween c = try $ do
606612 resetEmphasisNewlines
607613 return res
608614
609−verbatimBetween :: Char
610− -> OrgParser String
615+verbatimBetween :: PandocMonad m
616+ => Char
617+ -> OrgParser m String
611618verbatimBetween c = try $
612619 emphasisStart c *>
613620 many1TillNOrLessNewlines 1 verbatimChar (emphasisEnd c)
@@ -615,8 +622,9 @@ verbatimBetween c = try $
615622 verbatimChar = noneOf "\n\r" >>= updatePositions
616623
617624-- | Parses a raw string delimited by @c@ using Org's math rules
618−mathStringBetween :: Char
619− -> OrgParser String
625+mathStringBetween :: PandocMonad m
626+ => Char
627+ -> OrgParser m String
620628mathStringBetween c = try $ do
621629 mathStart c
622630 body <- many1TillNOrLessNewlines mathAllowedNewlines
@@ -626,8 +634,9 @@ mathStringBetween c = try $ do
626634 return $ body ++ [final]
627635
628636-- | Parse a single character between @c@ using math rules
629−math1CharBetween :: Char
630− -> OrgParser String
637+math1CharBetween :: PandocMonad m
638+ => Char
639+ -> OrgParser m String
631640math1CharBetween c = try $ do
632641 char c
633642 res <- noneOf $ c:mathForbiddenBorderChars
@@ -635,13 +644,14 @@ math1CharBetween c = try $ do
635644 eof <|> () <$ lookAhead (oneOf mathPostChars)
636645 return [res]
637646
638−rawMathBetween :: String
647+rawMathBetween :: PandocMonad m
648+ => String
639649 -> String
640− -> OrgParser String
650+ -> OrgParser m String
641651rawMathBetween s e = try $ string s *> manyTill anyChar (try $ string e)
642652
643653-- | Parses the start (opening character) of emphasis
644−emphasisStart :: Char -> OrgParser Char
654+emphasisStart :: PandocMonad m => Char -> OrgParser m Char
645655emphasisStart c = try $ do
646656 guard =<< afterEmphasisPreChar
647657 guard =<< notAfterString
@@ -654,7 +664,7 @@ emphasisStart c = try $ do
654664 return c
655665
656666-- | Parses the closing character of emphasis
657−emphasisEnd :: Char -> OrgParser Char
667+emphasisEnd :: PandocMonad m => Char -> OrgParser m Char
658668emphasisEnd c = try $ do
659669 guard =<< notAfterForbiddenBorderChar
660670 char c
@@ -665,11 +675,11 @@ emphasisEnd c = try $ do
665675 where acceptablePostChars =
666676 surroundingEmphasisChar >>= \x -> oneOf (x ++ emphasisPostChars)
667677
668−mathStart :: Char -> OrgParser Char
678+mathStart :: PandocMonad m => Char -> OrgParser m Char
669679mathStart c = try $
670680 char c <* notFollowedBy' (oneOf (c:mathForbiddenBorderChars))
671681
672−mathEnd :: Char -> OrgParser Char
682+mathEnd :: PandocMonad m => Char -> OrgParser m Char
673683mathEnd c = try $ do
674684 res <- noneOf (c:mathForbiddenBorderChars)
675685 char c
@@ -677,15 +687,15 @@ mathEnd c = try $ do
677687 return res
678688
679689
680−enclosedInlines :: OrgParser a
681− -> OrgParser b
682− -> OrgParser (F Inlines)
690+enclosedInlines :: PandocMonad m => OrgParser m a
691+ -> OrgParser m b
692+ -> OrgParser m (F Inlines)
683693enclosedInlines start end = try $
684694 trimInlinesF . mconcat <$> enclosed start end inline
685695
686−enclosedRaw :: OrgParser a
687− -> OrgParser b
688− -> OrgParser String
696+enclosedRaw :: PandocMonad m => OrgParser m a
697+ -> OrgParser m b
698+ -> OrgParser m String
689699enclosedRaw start end = try $
690700 start *> (onSingleLine <|> spanningTwoLines)
691701 where onSingleLine = try $ many1Till (noneOf "\n\r") end
@@ -694,10 +704,10 @@ enclosedRaw start end = try $
694704
695705-- | Like many1Till, but parses at most @n+1@ lines. @p@ must not consume
696706-- newlines.
697−many1TillNOrLessNewlines :: Int
698− -> OrgParser Char
699− -> OrgParser a
700− -> OrgParser String
707+many1TillNOrLessNewlines :: PandocMonad m => Int
708+ -> OrgParser m Char
709+ -> OrgParser m a
710+ -> OrgParser m String
701711many1TillNOrLessNewlines n p end = try $
702712 nMoreLines (Just n) mempty >>= oneOrMore
703713 where
@@ -746,21 +756,21 @@ mathAllowedNewlines :: Int
746756mathAllowedNewlines = 2
747757
748758-- | Whether we are right behind a char allowed before emphasis
749−afterEmphasisPreChar :: OrgParser Bool
759+afterEmphasisPreChar :: PandocMonad m => OrgParser m Bool
750760afterEmphasisPreChar = do
751761 pos <- getPosition
752762 lastPrePos <- orgStateLastPreCharPos <$> getState
753763 return . fromMaybe True $ (== pos) <$> lastPrePos
754764
755765-- | Whether the parser is right after a forbidden border char
756−notAfterForbiddenBorderChar :: OrgParser Bool
766+notAfterForbiddenBorderChar :: PandocMonad m => OrgParser m Bool
757767notAfterForbiddenBorderChar = do
758768 pos <- getPosition
759769 lastFBCPos <- orgStateLastForbiddenCharPos <$> getState
760770 return $ lastFBCPos /= Just pos
761771
762772-- | Read a sub- or superscript expression
763−subOrSuperExpr :: OrgParser (F Inlines)
773+subOrSuperExpr :: PandocMonad m => OrgParser m (F Inlines)
764774subOrSuperExpr = try $
765775 choice [ id <$> charsInBalanced '{' '}' (noneOf "\n\r")
766776 , enclosing ('(', ')') <$> charsInBalanced '(' ')' (noneOf "\n\r")
@@ -768,7 +778,7 @@ subOrSuperExpr = try $
768778 ] >>= parseFromString (mconcat <$> many inline)
769779 where enclosing (left, right) s = left : s ++ [right]
770780
771−simpleSubOrSuperString :: OrgParser String
781+simpleSubOrSuperString :: PandocMonad m => OrgParser m String
772782simpleSubOrSuperString = try $ do
773783 state <- getState
774784 guard . exportSubSuperscripts . orgStateExportSettings $ state
@@ -777,17 +787,18 @@ simpleSubOrSuperString = try $ do
777787 <*> many1 alphaNum
778788 ]
779789
780−inlineLaTeX :: OrgParser (F Inlines)
790+inlineLaTeX :: PandocMonad m => OrgParser m (F Inlines)
781791inlineLaTeX = try $ do
782792 cmd <- inlineLaTeXCommand
793+ ils <- (lift . lift) $ parseAsInlineLaTeX cmd
783794 maybe mzero returnF $
784− parseAsMath cmd `mplus` parseAsMathMLSym cmd `mplus` parseAsInlineLaTeX cmd
795+ parseAsMath cmd `mplus` parseAsMathMLSym cmd `mplus` ils
785796 where
786797 parseAsMath :: String -> Maybe Inlines
787798 parseAsMath cs = B.fromList <$> texMathToPandoc cs
788799
789− parseAsInlineLaTeX :: String -> Maybe Inlines
790− parseAsInlineLaTeX cs = maybeRight $ runParser inlineCommand state "" cs
800+ parseAsInlineLaTeX :: PandocMonad m => String -> m (Maybe Inlines)
801+ parseAsInlineLaTeX cs = maybeRight <$> runParserT inlineCommand state "" cs
791802
792803 parseAsMathMLSym :: String -> Maybe Inlines
793804 parseAsMathMLSym cs = B.str <$> MathMLEntityMap.getUnicode (clean cs)
@@ -803,10 +814,11 @@ inlineLaTeX = try $ do
803814maybeRight :: Either a b -> Maybe b
804815maybeRight = either (const Nothing) Just
805816
806−inlineLaTeXCommand :: OrgParser String
817+inlineLaTeXCommand :: PandocMonad m => OrgParser m String
807818inlineLaTeXCommand = try $ do
808819 rest <- getInput
809− case runParser rawLaTeXInline def "source" rest of
820+ parsed <- (lift . lift) $ runParserT rawLaTeXInline def "source" rest
821+ case parsed of
810822 Right (RawInline _ cs) -> do
811823 -- drop any trailing whitespace, those are not be part of the command as
812824 -- far as org mode is concerned.
@@ -820,16 +832,16 @@ inlineLaTeXCommand = try $ do
820832dropWhileEnd :: (a -> Bool) -> [a] -> [a]
821833dropWhileEnd p = foldr (\x xs -> if p x && null xs then [] else x : xs) []
822834
823−exportSnippet :: OrgParser (F Inlines)
835+exportSnippet :: PandocMonad m => OrgParser m (F Inlines)
824836exportSnippet = try $ do
825837 string "@@"
826838 format <- many1Till (alphaNum <|> char '-') (char ':')
827839 snippet <- manyTill anyChar (try $ string "@@")
828840 returnF $ B.rawInline format snippet
829841
830−smart :: OrgParser (F Inlines)
842+smart :: PandocMonad m => OrgParser m (F Inlines)
831843smart = do
832− getOption readerSmart >>= guard
844+ guardEnabled Ext_smart
833845 doubleQuoted <|> singleQuoted <|>
834846 choice (map (return <$>) [orgApostrophe, orgDash, orgEllipses])
835847 where
@@ -844,7 +856,7 @@ smart = do
844856 <* updateLastForbiddenCharPos
845857 *> return (B.str "\x2019")
846858
847−singleQuoted :: OrgParser (F Inlines)
859+singleQuoted :: PandocMonad m => OrgParser m (F Inlines)
848860singleQuoted = try $ do
849861 guard =<< getExportSetting exportSmartQuotes
850862 singleQuoteStart
@@ -856,7 +868,7 @@ singleQuoted = try $ do
856868-- doubleQuoted will handle regular double-quoted sections, as well
857869-- as dialogues with an open double-quote without a close double-quote
858870-- in the same paragraph.
859−doubleQuoted :: OrgParser (F Inlines)
871+doubleQuoted :: PandocMonad m => OrgParser m (F Inlines)
860872doubleQuoted = try $ do
861873 guard =<< getExportSetting exportSmartQuotes
862874 doubleQuoteStart
src/Text/Pandoc/Readers/Org/Meta.hs+23−22
@@ -41,6 +41,7 @@ import Text.Pandoc.Readers.Org.Parsing
4141
4242import qualified Text.Pandoc.Builder as B
4343import Text.Pandoc.Builder ( Blocks, Inlines )
44+import Text.Pandoc.Class ( PandocMonad )
4445import Text.Pandoc.Definition
4546
4647import Control.Monad ( mzero, void )
@@ -51,7 +52,7 @@ import Data.Monoid ( (<>) )
5152import Network.HTTP ( urlEncode )
5253
5354-- | Returns the current meta, respecting export options.
54−metaExport :: OrgParser (F Meta)
55+metaExport :: Monad m => OrgParser m (F Meta)
5556metaExport = do
5657 st <- getState
5758 let settings = orgStateExportSettings st
@@ -68,10 +69,10 @@ removeMeta key meta' =
6869-- | Parse and handle a single line containing meta information
6970-- The order, in which blocks are tried, makes sure that we're not looking at
7071-- the beginning of a block, so we don't need to check for it
71−metaLine :: OrgParser Blocks
72+metaLine :: PandocMonad m => OrgParser m Blocks
7273metaLine = mempty <$ metaLineStart <* (optionLine <|> declarationLine)
7374
74−declarationLine :: OrgParser ()
75+declarationLine :: PandocMonad m => OrgParser m ()
7576declarationLine = try $ do
7677 key <- map toLower <$> metaKey
7778 (key', value) <- metaValue key
@@ -79,12 +80,12 @@ declarationLine = try $ do
7980 let meta' = B.setMeta key' <$> value <*> pure nullMeta
8081 in st { orgStateMeta = meta' <> orgStateMeta st }
8182
82−metaKey :: OrgParser String
83+metaKey :: Monad m => OrgParser m String
8384metaKey = map toLower <$> many1 (noneOf ": \n\r")
8485 <* char ':'
8586 <* skipSpaces
8687
87−metaValue :: String -> OrgParser (String, (F MetaValue))
88+metaValue :: PandocMonad m => String -> OrgParser m (String, (F MetaValue))
8889metaValue key =
8990 let inclKey = "header-includes"
9091 in case key of
@@ -103,10 +104,10 @@ metaValue key =
103104 accumulatingList inclKey (metaExportSnippet "html")
104105 _ -> (key,) <$> metaString
105106
106−metaInlines :: OrgParser (F MetaValue)
107+metaInlines :: PandocMonad m => OrgParser m (F MetaValue)
107108metaInlines = fmap (MetaInlines . B.toList) <$> inlinesTillNewline
108109
109−metaInlinesCommaSeparated :: OrgParser (F MetaValue)
110+metaInlinesCommaSeparated :: PandocMonad m => OrgParser m (F MetaValue)
110111metaInlinesCommaSeparated = do
111112 authStrs <- (many1 (noneOf ",\n")) `sepBy1` (char ',')
112113 newline
@@ -114,21 +115,21 @@ metaInlinesCommaSeparated = do
114115 let toMetaInlines = MetaInlines . B.toList
115116 return $ MetaList . map toMetaInlines <$> sequence authors
116117
117−metaString :: OrgParser (F MetaValue)
118+metaString :: Monad m => OrgParser m (F MetaValue)
118119metaString = metaModifiedString id
119120
120−metaModifiedString :: (String -> String) -> OrgParser (F MetaValue)
121+metaModifiedString :: Monad m => (String -> String) -> OrgParser m (F MetaValue)
121122metaModifiedString f = return . MetaString . f <$> anyLine
122123
123124-- | Read an format specific meta definition
124−metaExportSnippet :: String -> OrgParser (F MetaValue)
125+metaExportSnippet :: Monad m => String -> OrgParser m (F MetaValue)
125126metaExportSnippet format =
126127 return . MetaInlines . B.toList . B.rawInline format <$> anyLine
127128
128129-- | Accumulate the result of the @parser@ in a list under @key@.
129−accumulatingList :: String
130− -> OrgParser (F MetaValue)
131− -> OrgParser (F MetaValue)
130+accumulatingList :: Monad m => String
131+ -> OrgParser m (F MetaValue)
132+ -> OrgParser m (F MetaValue)
132133accumulatingList key p = do
133134 value <- p
134135 meta' <- orgStateMeta <$> getState
@@ -141,7 +142,7 @@ accumulatingList key p = do
141142--
142143-- export options
143144--
144−optionLine :: OrgParser ()
145+optionLine :: Monad m => OrgParser m ()
145146optionLine = try $ do
146147 key <- metaKey
147148 case key of
@@ -152,14 +153,14 @@ optionLine = try $ do
152153 "typ_todo" -> todoSequence >>= updateState . registerTodoSequence
153154 _ -> mzero
154155
155−addLinkFormat :: String
156+addLinkFormat :: Monad m => String
156157 -> (String -> String)
157− -> OrgParser ()
158+ -> OrgParser m ()
158159addLinkFormat key formatter = updateState $ \s ->
159160 let fs = orgStateLinkFormatters s
160161 in s{ orgStateLinkFormatters = M.insert key formatter fs }
161162
162−parseLinkFormat :: OrgParser ((String, String -> String))
163+parseLinkFormat :: Monad m => OrgParser m ((String, String -> String))
163164parseLinkFormat = try $ do
164165 linkType <- (:) <$> letter <*> many (alphaNum <|> oneOf "-_") <* skipSpaces
165166 linkSubst <- parseFormat
@@ -167,7 +168,7 @@ parseLinkFormat = try $ do
167168
168169-- | An ad-hoc, single-argument-only implementation of a printf-style format
169170-- parser.
170−parseFormat :: OrgParser (String -> String)
171+parseFormat :: Monad m => OrgParser m (String -> String)
171172parseFormat = try $ do
172173 replacePlain <|> replaceUrl <|> justAppend
173174 where
@@ -181,13 +182,13 @@ parseFormat = try $ do
181182 rest = manyTill anyChar (eof <|> () <$ oneOf "\n\r")
182183 tillSpecifier c = manyTill (noneOf "\n\r") (try $ string ('%':c:""))
183184
184−inlinesTillNewline :: OrgParser (F Inlines)
185+inlinesTillNewline :: PandocMonad m => OrgParser m (F Inlines)
185186inlinesTillNewline = trimInlinesF . mconcat <$> manyTill inline newline
186187
187188--
188189-- ToDo Sequences and Keywords
189190--
190−todoSequence :: OrgParser TodoSequence
191+todoSequence :: Monad m => OrgParser m TodoSequence
191192todoSequence = try $ do
192193 todoKws <- todoKeywords
193194 doneKws <- optionMaybe $ todoDoneSep *> todoKeywords
@@ -201,13 +202,13 @@ todoSequence = try $ do
201202 (x:xs) -> return $ keywordsToSequence (reverse xs) [x]
202203
203204 where
204− todoKeywords :: OrgParser [String]
205+ todoKeywords :: Monad m => OrgParser m [String]
205206 todoKeywords = try $
206207 let keyword = many1 nonspaceChar <* skipSpaces
207208 endOfKeywords = todoDoneSep <|> void newline
208209 in manyTill keyword (lookAhead endOfKeywords)
209210
210− todoDoneSep :: OrgParser ()
211+ todoDoneSep :: Monad m => OrgParser m ()
211212 todoDoneSep = void . try $ skipSpaces *> char '|' <* skipSpaces1
212213
213214 keywordsToSequence :: [String] -> [String] -> TodoSequence
src/Text/Pandoc/Readers/Org/ParserState.hs+2−2
@@ -51,7 +51,7 @@ module Text.Pandoc.Readers.Org.ParserState
5151 ) where
5252
5353import Control.Monad (liftM, liftM2)
54−import Control.Monad.Reader (Reader, runReader, ask, asks, local)
54+import Control.Monad.Reader (Reader, runReader, ReaderT, ask, asks, local)
5555
5656import Data.Default (Default(..))
5757import qualified Data.Map as M
@@ -122,7 +122,7 @@ instance HasLastStrPosition OrgParserState where
122122 getLastStrPos = orgStateLastStrPos
123123 setLastStrPos pos st = st{ orgStateLastStrPos = Just pos }
124124
125−instance HasQuoteContext st (Reader OrgParserLocal) where
125+instance Monad m => HasQuoteContext st (ReaderT OrgParserLocal m) where
126126 getQuoteContext = asks orgLocalQuoteContext
127127 withQuoteContext q = local (\s -> s{orgLocalQuoteContext = q})
128128
src/Text/Pandoc/Readers/Org/Parsing.hs+19−17
@@ -72,6 +72,7 @@ module Text.Pandoc.Readers.Org.Parsing
7272 , citeKey
7373 -- * Re-exports from Text.Pandoc.Parsec
7474 , runParser
75+ , runParserT
7576 , getInput
7677 , char
7778 , letter
@@ -114,17 +115,17 @@ import Text.Pandoc.Parsing hiding ( anyLine, blanklines, newline
114115 , parseFromString )
115116
116117import Control.Monad ( guard )
117−import Control.Monad.Reader ( Reader )
118+import Control.Monad.Reader ( ReaderT )
118119
119120-- | The parser used to read org files.
120−type OrgParser = ParserT [Char] OrgParserState (Reader OrgParserLocal)
121+type OrgParser m = ParserT [Char] OrgParserState (ReaderT OrgParserLocal m)
121122
122123--
123124-- Adaptions and specializations of parsing utilities
124125--
125126
126127-- | Parse any line of text
127−anyLine :: OrgParser String
128+anyLine :: Monad m => OrgParser m String
128129anyLine =
129130 P.anyLine
130131 <* updateLastPreCharPos
@@ -132,7 +133,7 @@ anyLine =
132133
133134-- The version Text.Pandoc.Parsing cannot be used, as we need additional parts
134135-- of the state saved and restored.
135−parseFromString :: OrgParser a -> String -> OrgParser a
136+parseFromString :: Monad m => OrgParser m a -> String -> OrgParser m a
136137parseFromString parser str' = do
137138 oldLastPreCharPos <- orgStateLastPreCharPos <$> getState
138139 updateState $ \s -> s{ orgStateLastPreCharPos = Nothing }
@@ -141,33 +142,34 @@ parseFromString parser str' = do
141142 return result
142143
143144-- | Skip one or more tab or space characters.
144−skipSpaces1 :: OrgParser ()
145+skipSpaces1 :: Monad m => OrgParser m ()
145146skipSpaces1 = skipMany1 spaceChar
146147
147148-- | Like @Text.Parsec.Char.newline@, but causes additional state changes.
148−newline :: OrgParser Char
149+newline :: Monad m => OrgParser m Char
149150newline =
150151 P.newline
151152 <* updateLastPreCharPos
152153 <* updateLastForbiddenCharPos
153154
154155-- | Like @Text.Parsec.Char.blanklines@, but causes additional state changes.
155−blanklines :: OrgParser [Char]
156+blanklines :: Monad m => OrgParser m [Char]
156157blanklines =
157158 P.blanklines
158159 <* updateLastPreCharPos
159160 <* updateLastForbiddenCharPos
160161
161162-- | Succeeds when we're in list context.
162−inList :: OrgParser ()
163+inList :: Monad m => OrgParser m ()
163164inList = do
164165 ctx <- orgStateParserContext <$> getState
165166 guard (ctx == ListItemState)
166167
167168-- | Parse in different context
168−withContext :: ParserContext -- ^ New parser context
169− -> OrgParser a -- ^ Parser to run in that context
170− -> OrgParser a
169+withContext :: Monad m
170+ => ParserContext -- ^ New parser context
171+ -> OrgParser m a -- ^ Parser to run in that context
172+ -> OrgParser m a
171173withContext context parser = do
172174 oldContext <- orgStateParserContext <$> getState
173175 updateState $ \s -> s{ orgStateParserContext = context }
@@ -180,19 +182,19 @@ withContext context parser = do
180182--
181183
182184-- | Get an export setting.
183−getExportSetting :: (ExportSettings -> a) -> OrgParser a
185+getExportSetting :: Monad m => (ExportSettings -> a) -> OrgParser m a
184186getExportSetting s = s . orgStateExportSettings <$> getState
185187
186188-- | Set the current position as the last position at which a forbidden char
187189-- was found (i.e. a character which is not allowed at the inner border of
188190-- markup).
189−updateLastForbiddenCharPos :: OrgParser ()
191+updateLastForbiddenCharPos :: Monad m => OrgParser m ()
190192updateLastForbiddenCharPos = getPosition >>= \p ->
191193 updateState $ \s -> s{ orgStateLastForbiddenCharPos = Just p}
192194
193195-- | Set the current parser position as the position at which a character was
194196-- seen which allows inline markup to follow.
195−updateLastPreCharPos :: OrgParser ()
197+updateLastPreCharPos :: Monad m => OrgParser m ()
196198updateLastPreCharPos = getPosition >>= \p ->
197199 updateState $ \s -> s{ orgStateLastPreCharPos = Just p}
198200
@@ -201,15 +203,15 @@ updateLastPreCharPos = getPosition >>= \p ->
201203--
202204
203205-- | Read the key of a plist style key-value list.
204−orgArgKey :: OrgParser String
206+orgArgKey :: Monad m => OrgParser m String
205207orgArgKey = try $
206208 skipSpaces *> char ':'
207209 *> many1 orgArgWordChar
208210
209211-- | Read the value of a plist style key-value list.
210−orgArgWord :: OrgParser String
212+orgArgWord :: Monad m => OrgParser m String
211213orgArgWord = many1 orgArgWordChar
212214
213215-- | Chars treated as part of a word in plists.
214−orgArgWordChar :: OrgParser Char
216+orgArgWordChar :: Monad m => OrgParser m Char
215217orgArgWordChar = alphaNum <|> oneOf "-_"
src/Text/Pandoc/Readers/RST.hs+224−136
@@ -1,3 +1,4 @@
1+{-# LANGUAGE ScopedTypeVariables #-}
12{-# LANGUAGE OverloadedStrings #-}
23{-# LANGUAGE FlexibleContexts #-}
34{-
@@ -29,19 +30,18 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2930
3031Conversion from reStructuredText to 'Pandoc' document.
3132-}
32−module Text.Pandoc.Readers.RST (
33− readRST,
34− readRSTWithWarnings
35− ) where
33+module Text.Pandoc.Readers.RST ( readRST ) where
3634import Text.Pandoc.Definition
3735import Text.Pandoc.Builder (setMeta, fromList)
3836import Text.Pandoc.Shared
3937import Text.Pandoc.Parsing
4038import Text.Pandoc.Options
39+import Text.Pandoc.Error
40+import qualified Text.Pandoc.UTF8 as UTF8
4141import Control.Monad ( when, liftM, guard, mzero )
42−import Data.List ( findIndex, intercalate,
42+import Data.List ( findIndex, intercalate, isInfixOf,
4343 transpose, sort, deleteFirstsBy, isSuffixOf , nub, union)
44−import Data.Maybe (fromMaybe)
44+import Data.Maybe (fromMaybe, isJust)
4545import qualified Data.Map as M
4646import Text.Printf ( printf )
4747import Text.Pandoc.Builder (Inlines, Blocks, trimInlines)
@@ -49,18 +49,21 @@ import qualified Text.Pandoc.Builder as B
4949import Data.Sequence (viewr, ViewR(..))
5050import Data.Char (toLower, isHexDigit, isSpace)
5151import Data.Monoid ((<>))
52−import Text.Pandoc.Error
52+import Control.Monad.Except (throwError, catchError)
53+import Text.Pandoc.Class (PandocMonad, warning, readFileLazy, warningWithPos)
5354
5455-- | Parse reStructuredText string and return Pandoc document.
55−readRST :: ReaderOptions -- ^ Reader options
56+readRST :: PandocMonad m
57+ => ReaderOptions -- ^ Reader options
5658 -> String -- ^ String to parse (assuming @'\n'@ line endings)
57− -> Either PandocError Pandoc
58−readRST opts s = (readWith parseRST) def{ stateOptions = opts } (s ++ "\n\n")
59−
60−readRSTWithWarnings :: ReaderOptions -> String -> Either PandocError (Pandoc, [String])
61−readRSTWithWarnings opts s = (readWithWarnings parseRST) def{ stateOptions = opts } (s ++ "\n\n")
59+ -> m Pandoc
60+readRST opts s = do
61+ parsed <- (readWithM parseRST) def{ stateOptions = opts } (s ++ "\n\n")
62+ case parsed of
63+ Right result -> return result
64+ Left e -> throwError e
6265
63−type RSTParser = Parser [Char] ParserState
66+type RSTParser m = ParserT [Char] ParserState m
6467
6568--
6669-- Constants and data structure definitions
@@ -141,7 +144,7 @@ metaFromDefList ds meta = adjustAuthors $ foldr f meta ds
141144 factorSemi (Str ys)
142145 factorSemi x = [x]
143146
144−parseRST :: RSTParser Pandoc
147+parseRST :: PandocMonad m => RSTParser m Pandoc
145148parseRST = do
146149 optional blanklines -- skip blank lines at beginning of file
147150 startPos <- getPosition
@@ -168,13 +171,14 @@ parseRST = do
168171-- parsing blocks
169172--
170173
171−parseBlocks :: RSTParser Blocks
174+parseBlocks :: PandocMonad m => RSTParser m Blocks
172175parseBlocks = mconcat <$> manyTill block eof
173176
174−block :: RSTParser Blocks
177+block :: PandocMonad m => RSTParser m Blocks
175178block = choice [ codeBlock
176179 , blockQuote
177180 , fieldList
181+ , include
178182 , directive
179183 , comment
180184 , header
@@ -191,7 +195,7 @@ block = choice [ codeBlock
191195-- field list
192196--
193197
194−rawFieldListItem :: Int -> RSTParser (String, String)
198+rawFieldListItem :: Monad m => Int -> RSTParser m (String, String)
195199rawFieldListItem minIndent = try $ do
196200 indent <- length <$> many (char ' ')
197201 guard $ indent >= minIndent
@@ -204,7 +208,7 @@ rawFieldListItem minIndent = try $ do
204208 let raw = (if null first then "" else (first ++ "\n")) ++ rest ++ "\n"
205209 return (name, raw)
206210
207−fieldListItem :: Int -> RSTParser (Inlines, [Blocks])
211+fieldListItem :: PandocMonad m => Int -> RSTParser m (Inlines, [Blocks])
208212fieldListItem minIndent = try $ do
209213 (name, raw) <- rawFieldListItem minIndent
210214 term <- parseInlineFromString name
@@ -212,7 +216,7 @@ fieldListItem minIndent = try $ do
212216 optional blanklines
213217 return (term, [contents])
214218
215−fieldList :: RSTParser Blocks
219+fieldList :: PandocMonad m => RSTParser m Blocks
216220fieldList = try $ do
217221 indent <- length <$> lookAhead (many spaceChar)
218222 items <- many1 $ fieldListItem indent
@@ -224,7 +228,7 @@ fieldList = try $ do
224228-- line block
225229--
226230
227−lineBlock :: RSTParser Blocks
231+lineBlock :: PandocMonad m => RSTParser m Blocks
228232lineBlock = try $ do
229233 lines' <- lineBlockLines
230234 lines'' <- mapM parseInlineFromString lines'
@@ -235,7 +239,7 @@ lineBlock = try $ do
235239--
236240
237241-- note: paragraph can end in a :: starting a code block
238−para :: RSTParser Blocks
242+para :: PandocMonad m => RSTParser m Blocks
239243para = try $ do
240244 result <- trimInlines . mconcat <$> many1 inline
241245 option (B.plain result) $ try $ do
@@ -248,18 +252,18 @@ para = try $ do
248252 <> raw
249253 _ -> return (B.para result)
250254
251−plain :: RSTParser Blocks
255+plain :: PandocMonad m => RSTParser m Blocks
252256plain = B.plain . trimInlines . mconcat <$> many1 inline
253257
254258--
255259-- header blocks
256260--
257261
258−header :: RSTParser Blocks
262+header :: PandocMonad m => RSTParser m Blocks
259263header = doubleHeader <|> singleHeader <?> "header"
260264
261265-- a header with lines on top and bottom
262−doubleHeader :: RSTParser Blocks
266+doubleHeader :: PandocMonad m => RSTParser m Blocks
263267doubleHeader = try $ do
264268 c <- oneOf underlineChars
265269 rest <- many (char c) -- the top line
@@ -285,7 +289,7 @@ doubleHeader = try $ do
285289 return $ B.headerWith attr level txt
286290
287291-- a header with line on the bottom only
288−singleHeader :: RSTParser Blocks
292+singleHeader :: PandocMonad m => RSTParser m Blocks
289293singleHeader = try $ do
290294 notFollowedBy' whitespace
291295 txt <- trimInlines . mconcat <$> many1 (do {notFollowedBy blankline; inline})
@@ -309,7 +313,7 @@ singleHeader = try $ do
309313-- hrule block
310314--
311315
312−hrule :: Parser [Char] st Blocks
316+hrule :: Monad m => ParserT [Char] st m Blocks
313317hrule = try $ do
314318 chr <- oneOf underlineChars
315319 count 3 (char chr)
@@ -323,14 +327,14 @@ hrule = try $ do
323327--
324328
325329-- read a line indented by a given string
326−indentedLine :: String -> Parser [Char] st [Char]
330+indentedLine :: Monad m => String -> ParserT [Char] st m [Char]
327331indentedLine indents = try $ do
328332 string indents
329333 anyLine
330334
331335-- one or more indented lines, possibly separated by blank lines.
332336-- any amount of indentation will work.
333−indentedBlock :: Parser [Char] st [Char]
337+indentedBlock :: Monad m => ParserT [Char] st m [Char]
334338indentedBlock = try $ do
335339 indents <- lookAhead $ many1 spaceChar
336340 lns <- many1 $ try $ do b <- option "" blanklines
@@ -339,24 +343,24 @@ indentedBlock = try $ do
339343 optional blanklines
340344 return $ unlines lns
341345
342−quotedBlock :: Parser [Char] st [Char]
346+quotedBlock :: Monad m => ParserT [Char] st m [Char]
343347quotedBlock = try $ do
344348 quote <- lookAhead $ oneOf "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
345349 lns <- many1 $ lookAhead (char quote) >> anyLine
346350 optional blanklines
347351 return $ unlines lns
348352
349−codeBlockStart :: Parser [Char] st Char
353+codeBlockStart :: Monad m => ParserT [Char] st m Char
350354codeBlockStart = string "::" >> blankline >> blankline
351355
352−codeBlock :: Parser [Char] st Blocks
356+codeBlock :: Monad m => ParserT [Char] st m Blocks
353357codeBlock = try $ codeBlockStart >> codeBlockBody
354358
355−codeBlockBody :: Parser [Char] st Blocks
359+codeBlockBody :: Monad m => ParserT [Char] st m Blocks
356360codeBlockBody = try $ B.codeBlock . stripTrailingNewlines <$>
357361 (indentedBlock <|> quotedBlock)
358362
359−lhsCodeBlock :: RSTParser Blocks
363+lhsCodeBlock :: Monad m => RSTParser m Blocks
360364lhsCodeBlock = try $ do
361365 getPosition >>= guard . (==1) . sourceColumn
362366 guardEnabled Ext_literate_haskell
@@ -366,14 +370,14 @@ lhsCodeBlock = try $ do
366370 return $ B.codeBlockWith ("", ["sourceCode", "literate", "haskell"], [])
367371 $ intercalate "\n" lns
368372
369−latexCodeBlock :: Parser [Char] st [[Char]]
373+latexCodeBlock :: Monad m => ParserT [Char] st m [[Char]]
370374latexCodeBlock = try $ do
371375 try (latexBlockLine "\\begin{code}")
372376 many1Till anyLine (try $ latexBlockLine "\\end{code}")
373377 where
374378 latexBlockLine s = skipMany spaceChar >> string s >> blankline
375379
376−birdCodeBlock :: Parser [Char] st [[Char]]
380+birdCodeBlock :: Monad m => ParserT [Char] st m [[Char]]
377381birdCodeBlock = filterSpace <$> many1 birdTrackLine
378382 where filterSpace lns =
379383 -- if (as is normal) there is always a space after >, drop it
@@ -381,28 +385,103 @@ birdCodeBlock = filterSpace <$> many1 birdTrackLine
381385 then map (drop 1) lns
382386 else lns
383387
384−birdTrackLine :: Parser [Char] st [Char]
388+birdTrackLine :: Monad m => ParserT [Char] st m [Char]
385389birdTrackLine = char '>' >> anyLine
386390
387391--
388392-- block quotes
389393--
390394
391−blockQuote :: RSTParser Blocks
395+blockQuote :: PandocMonad m => RSTParser m Blocks
392396blockQuote = do
393397 raw <- indentedBlock
394398 -- parse the extracted block, which may contain various block elements:
395399 contents <- parseFromString parseBlocks $ raw ++ "\n\n"
396400 return $ B.blockQuote contents
397401
402+{-
403+Unsupported options for include:
404+tab-width
405+encoding
406+-}
407+
408+include :: PandocMonad m => RSTParser m Blocks
409+include = try $ do
410+ string ".. include::"
411+ skipMany spaceChar
412+ f <- trim <$> anyLine
413+ fields <- many $ rawFieldListItem 3
414+ -- options
415+ let (startLine :: Maybe Int) = lookup "start-line" fields >>= safeRead
416+ let (endLine :: Maybe Int) = lookup "end-line" fields >>= safeRead
417+ guard $ not (null f)
418+ oldPos <- getPosition
419+ oldInput <- getInput
420+ containers <- stateContainers <$> getState
421+ when (f `elem` containers) $
422+ throwError $ PandocParseError $ "Include file loop at " ++ show oldPos
423+ updateState $ \s -> s{ stateContainers = f : stateContainers s }
424+ res <- readFileLazy' f
425+ contents <- case res of
426+ Right x -> return x
427+ Left _e -> do
428+ warning $ "Could not read include file " ++ f ++ "."
429+ return ""
430+ let contentLines = lines contents
431+ let numLines = length contentLines
432+ let startLine' = case startLine of
433+ Nothing -> 1
434+ Just x | x >= 0 -> x
435+ | otherwise -> numLines + x -- negative from end
436+ let endLine' = case endLine of
437+ Nothing -> numLines + 1
438+ Just x | x >= 0 -> x
439+ | otherwise -> numLines + x -- negative from end
440+ let contentLines' = drop (startLine' - 1)
441+ $ take (endLine' - 1)
442+ $ contentLines
443+ let contentLines'' = (case trim <$> lookup "end-before" fields of
444+ Just patt -> takeWhile (not . (patt `isInfixOf`))
445+ Nothing -> id) .
446+ (case trim <$> lookup "start-after" fields of
447+ Just patt -> drop 1 .
448+ dropWhile (not . (patt `isInfixOf`))
449+ Nothing -> id) $ contentLines'
450+ let contents' = unlines contentLines''
451+ case lookup "code" fields of
452+ Just lang -> do
453+ let numberLines = lookup "number-lines" fields
454+ let classes = trimr lang : ["numberLines" | isJust numberLines] ++
455+ maybe [] words (lookup "class" fields)
456+ let kvs = maybe [] (\n -> [("startFrom", trimr n)]) numberLines
457+ let ident = maybe "" trimr $ lookup "name" fields
458+ let attribs = (ident, classes, kvs)
459+ return $ B.codeBlockWith attribs contents'
460+ Nothing -> case lookup "literal" fields of
461+ Just _ -> return $ B.rawBlock "rst" contents'
462+ Nothing -> do
463+ setPosition $ newPos f 1 1
464+ setInput contents'
465+ bs <- optional blanklines >>
466+ (mconcat <$> many block)
467+ setInput oldInput
468+ setPosition oldPos
469+ updateState $ \s -> s{ stateContainers =
470+ tail $ stateContainers s }
471+ return bs
472+
473+readFileLazy' :: PandocMonad m => FilePath -> m (Either PandocError String)
474+readFileLazy' f = catchError ((Right . UTF8.toStringLazy) <$> readFileLazy f) $
475+ \(e :: PandocError) -> return (Left e)
476+
398477--
399478-- list blocks
400479--
401480
402−list :: RSTParser Blocks
481+list :: PandocMonad m => RSTParser m Blocks
403482list = choice [ bulletList, orderedList, definitionList ] <?> "list"
404483
405−definitionListItem :: RSTParser (Inlines, [Blocks])
484+definitionListItem :: PandocMonad m => RSTParser m (Inlines, [Blocks])
406485definitionListItem = try $ do
407486 -- avoid capturing a directive or comment
408487 notFollowedBy (try $ char '.' >> char '.')
@@ -412,11 +491,11 @@ definitionListItem = try $ do
412491 contents <- parseFromString parseBlocks $ raw ++ "\n"
413492 return (term, [contents])
414493
415−definitionList :: RSTParser Blocks
494+definitionList :: PandocMonad m => RSTParser m Blocks
416495definitionList = B.definitionList <$> many1 definitionListItem
417496
418497-- parses bullet list start and returns its length (inc. following whitespace)
419−bulletListStart :: Parser [Char] st Int
498+bulletListStart :: Monad m => ParserT [Char] st m Int
420499bulletListStart = try $ do
421500 notFollowedBy' hrule -- because hrules start out just like lists
422501 marker <- oneOf bulletListMarkers
@@ -424,16 +503,16 @@ bulletListStart = try $ do
424503 return $ length (marker:white)
425504
426505-- parses ordered list start and returns its length (inc following whitespace)
427−orderedListStart :: ListNumberStyle
506+orderedListStart :: Monad m => ListNumberStyle
428507 -> ListNumberDelim
429− -> RSTParser Int
508+ -> RSTParser m Int
430509orderedListStart style delim = try $ do
431510 (_, markerLen) <- withHorizDisplacement (orderedListMarker style delim)
432511 white <- many1 spaceChar
433512 return $ markerLen + length white
434513
435514-- parse a line of a list item
436−listLine :: Int -> RSTParser [Char]
515+listLine :: Monad m => Int -> RSTParser m [Char]
437516listLine markerLength = try $ do
438517 notFollowedBy blankline
439518 indentWith markerLength
@@ -441,7 +520,7 @@ listLine markerLength = try $ do
441520 return $ line ++ "\n"
442521
443522-- indent by specified number of spaces (or equiv. tabs)
444−indentWith :: Int -> RSTParser [Char]
523+indentWith :: Monad m => Int -> RSTParser m [Char]
445524indentWith num = do
446525 tabStop <- getOption readerTabStop
447526 if (num < tabStop)
@@ -450,8 +529,8 @@ indentWith num = do
450529 (try (char '\t' >> count (num - tabStop) (char ' '))) ]
451530
452531-- parse raw text for one list item, excluding start marker and continuations
453−rawListItem :: RSTParser Int
454− -> RSTParser (Int, [Char])
532+rawListItem :: Monad m => RSTParser m Int
533+ -> RSTParser m (Int, [Char])
455534rawListItem start = try $ do
456535 markerLength <- start
457536 firstLine <- anyLine
@@ -461,14 +540,15 @@ rawListItem start = try $ do
461540-- continuation of a list item - indented and separated by blankline or
462541-- (in compact lists) endline.
463542-- Note: nested lists are parsed as continuations.
464−listContinuation :: Int -> RSTParser [Char]
543+listContinuation :: Monad m => Int -> RSTParser m [Char]
465544listContinuation markerLength = try $ do
466545 blanks <- many1 blankline
467546 result <- many1 (listLine markerLength)
468547 return $ blanks ++ concat result
469548
470−listItem :: RSTParser Int
471− -> RSTParser Blocks
549+listItem :: PandocMonad m
550+ => RSTParser m Int
551+ -> RSTParser m Blocks
472552listItem start = try $ do
473553 (markerLength, first) <- rawListItem start
474554 rest <- many (listContinuation markerLength)
@@ -490,21 +570,21 @@ listItem start = try $ do
490570 [Para xs, DefinitionList ys] -> B.fromList [Plain xs, DefinitionList ys]
491571 _ -> parsed
492572
493−orderedList :: RSTParser Blocks
573+orderedList :: PandocMonad m => RSTParser m Blocks
494574orderedList = try $ do
495575 (start, style, delim) <- lookAhead (anyOrderedListMarker <* spaceChar)
496576 items <- many1 (listItem (orderedListStart style delim))
497− let items' = compactify' items
577+ let items' = compactify items
498578 return $ B.orderedListWith (start, style, delim) items'
499579
500−bulletList :: RSTParser Blocks
501−bulletList = B.bulletList . compactify' <$> many1 (listItem bulletListStart)
580+bulletList :: PandocMonad m => RSTParser m Blocks
581+bulletList = B.bulletList . compactify <$> many1 (listItem bulletListStart)
502582
503583--
504584-- directive (e.g. comment, container, compound-paragraph)
505585--
506586
507−comment :: RSTParser Blocks
587+comment :: Monad m => RSTParser m Blocks
508588comment = try $ do
509589 string ".."
510590 skipMany1 spaceChar <|> (() <$ lookAhead newline)
@@ -513,11 +593,11 @@ comment = try $ do
513593 optional indentedBlock
514594 return mempty
515595
516−directiveLabel :: RSTParser String
596+directiveLabel :: Monad m => RSTParser m String
517597directiveLabel = map toLower
518598 <$> many1Till (letter <|> char '-') (try $ string "::")
519599
520−directive :: RSTParser Blocks
600+directive :: PandocMonad m => RSTParser m Blocks
521601directive = try $ do
522602 string ".."
523603 directive'
@@ -526,7 +606,7 @@ directive = try $ do
526606-- date
527607-- include
528608-- title
529−directive' :: RSTParser Blocks
609+directive' :: PandocMonad m => RSTParser m Blocks
530610directive' = do
531611 skipMany1 spaceChar
532612 label <- directiveLabel
@@ -614,13 +694,13 @@ directive' = do
614694 return $ B.divWith attrs children
615695 other -> do
616696 pos <- getPosition
617− addWarning (Just pos) $ "ignoring unknown directive: " ++ other
697+ warningWithPos pos $ "ignoring unknown directive: " ++ other
618698 return mempty
619699
620700-- TODO:
621701-- - Only supports :format: fields with a single format for :raw: roles,
622702-- change Text.Pandoc.Definition.Format to fix
623−addNewRole :: String -> [(String, String)] -> RSTParser Blocks
703+addNewRole :: PandocMonad m => String -> [(String, String)] -> RSTParser m Blocks
624704addNewRole roleString fields = do
625705 (role, parentRole) <- parseFromString inheritedRole roleString
626706 customRoles <- stateRstCustomRoles <$> getState
@@ -642,20 +722,20 @@ addNewRole roleString fields = do
642722
643723 -- warn about syntax we ignore
644724 flip mapM_ fields $ \(key, _) -> case key of
645− "language" -> when (baseRole /= "code") $ addWarning Nothing $
725+ "language" -> when (baseRole /= "code") $ warning $
646726 "ignoring :language: field because the parent of role :" ++
647727 role ++ ": is :" ++ baseRole ++ ": not :code:"
648− "format" -> when (baseRole /= "raw") $ addWarning Nothing $
728+ "format" -> when (baseRole /= "raw") $ warning $
649729 "ignoring :format: field because the parent of role :" ++
650730 role ++ ": is :" ++ baseRole ++ ": not :raw:"
651− _ -> addWarning Nothing $ "ignoring unknown field :" ++ key ++
731+ _ -> warning $ "ignoring unknown field :" ++ key ++
652732 ": in definition of role :" ++ role ++ ": in"
653733 when (parentRole == "raw" && countKeys "format" > 1) $
654− addWarning Nothing $
734+ warning $
655735 "ignoring :format: fields after the first in the definition of role :"
656736 ++ role ++": in"
657737 when (parentRole == "code" && countKeys "language" > 1) $
658− addWarning Nothing $
738+ warning $
659739 "ignoring :language: fields after the first in the definition of role :"
660740 ++ role ++": in"
661741
@@ -664,7 +744,7 @@ addNewRole roleString fields = do
664744 M.insert role (baseRole, fmt, attr) customRoles
665745 }
666746
667− return $ B.singleton Null
747+ return mempty
668748 where
669749 countKeys k = length . filter (== k) . map fst $ fields
670750 inheritedRole =
@@ -700,7 +780,7 @@ extractUnicodeChar s = maybe Nothing (\c -> Just (c,rest)) mbc
700780 where (ds,rest) = span isHexDigit s
701781 mbc = safeRead ('\'':'\\':'x':ds ++ "'")
702782
703−extractCaption :: RSTParser (Inlines, Blocks)
783+extractCaption :: PandocMonad m => RSTParser m (Inlines, Blocks)
704784extractCaption = do
705785 capt <- trimInlines . mconcat <$> many inline
706786 legend <- optional blanklines >> (mconcat <$> many block)
@@ -712,7 +792,7 @@ toChunks = dropWhile null
712792 . map (trim . unlines)
713793 . splitBy (all (`elem` (" \t" :: String))) . lines
714794
715−codeblock :: [String] -> Maybe String -> String -> String -> RSTParser Blocks
795+codeblock :: [String] -> Maybe String -> String -> String -> RSTParser m Blocks
716796codeblock classes numberLines lang body =
717797 return $ B.codeBlockWith attribs $ stripTrailingNewlines body
718798 where attribs = ("", classes', kvs)
@@ -728,7 +808,7 @@ codeblock classes numberLines lang body =
728808--- note block
729809---
730810
731−noteBlock :: RSTParser [Char]
811+noteBlock :: Monad m => RSTParser m [Char]
732812noteBlock = try $ do
733813 startPos <- getPosition
734814 string ".."
@@ -747,7 +827,7 @@ noteBlock = try $ do
747827 -- return blanks so line count isn't affected
748828 return $ replicate (sourceLine endPos - sourceLine startPos) '\n'
749829
750−noteMarker :: RSTParser [Char]
830+noteMarker :: Monad m => RSTParser m [Char]
751831noteMarker = do
752832 char '['
753833 res <- many1 digit
@@ -760,13 +840,13 @@ noteMarker = do
760840-- reference key
761841--
762842
763−quotedReferenceName :: RSTParser Inlines
843+quotedReferenceName :: PandocMonad m => RSTParser m Inlines
764844quotedReferenceName = try $ do
765845 char '`' >> notFollowedBy (char '`') -- `` means inline code!
766846 label' <- trimInlines . mconcat <$> many1Till inline (char '`')
767847 return label'
768848
769−unquotedReferenceName :: RSTParser Inlines
849+unquotedReferenceName :: PandocMonad m => RSTParser m Inlines
770850unquotedReferenceName = try $ do
771851 label' <- trimInlines . mconcat <$> many1Till inline (lookAhead $ char ':')
772852 return label'
@@ -775,24 +855,24 @@ unquotedReferenceName = try $ do
775855-- plus isolated (no two adjacent) internal hyphens, underscores,
776856-- periods, colons and plus signs; no whitespace or other characters
777857-- are allowed.
778−simpleReferenceName' :: Parser [Char] st String
858+simpleReferenceName' :: Monad m => ParserT [Char] st m String
779859simpleReferenceName' = do
780860 x <- alphaNum
781861 xs <- many $ alphaNum
782862 <|> (try $ oneOf "-_:+." <* lookAhead alphaNum)
783863 return (x:xs)
784864
785−simpleReferenceName :: Parser [Char] st Inlines
865+simpleReferenceName :: Monad m => ParserT [Char] st m Inlines
786866simpleReferenceName = do
787867 raw <- simpleReferenceName'
788868 return $ B.str raw
789869
790−referenceName :: RSTParser Inlines
870+referenceName :: PandocMonad m => RSTParser m Inlines
791871referenceName = quotedReferenceName <|>
792872 (try $ simpleReferenceName <* lookAhead (char ':')) <|>
793873 unquotedReferenceName
794874
795−referenceKey :: RSTParser [Char]
875+referenceKey :: PandocMonad m => RSTParser m [Char]
796876referenceKey = do
797877 startPos <- getPosition
798878 choice [substKey, anonymousKey, regularKey]
@@ -801,7 +881,7 @@ referenceKey = do
801881 -- return enough blanks to replace key
802882 return $ replicate (sourceLine endPos - sourceLine startPos) '\n'
803883
804−targetURI :: Parser [Char] st [Char]
884+targetURI :: Monad m => ParserT [Char] st m [Char]
805885targetURI = do
806886 skipSpaces
807887 optional newline
@@ -810,7 +890,7 @@ targetURI = do
810890 blanklines
811891 return $ escapeURI $ trim $ contents
812892
813−substKey :: RSTParser ()
893+substKey :: PandocMonad m => RSTParser m ()
814894substKey = try $ do
815895 string ".."
816896 skipMany1 spaceChar
@@ -828,7 +908,7 @@ substKey = try $ do
828908 let key = toKey $ stripFirstAndLast ref
829909 updateState $ \s -> s{ stateSubstitutions = M.insert key il $ stateSubstitutions s }
830910
831−anonymousKey :: RSTParser ()
911+anonymousKey :: Monad m => RSTParser m ()
832912anonymousKey = try $ do
833913 oneOfStrings [".. __:", "__"]
834914 src <- targetURI
@@ -842,7 +922,7 @@ stripTicks = reverse . stripTick . reverse . stripTick
842922 where stripTick ('`':xs) = xs
843923 stripTick xs = xs
844924
845−regularKey :: RSTParser ()
925+regularKey :: PandocMonad m => RSTParser m ()
846926regularKey = try $ do
847927 string ".. _"
848928 (_,ref) <- withRaw referenceName
@@ -869,45 +949,46 @@ regularKey = try $ do
869949-- Grid tables TODO:
870950-- - column spans
871951
872−dashedLine :: Char -> Parser [Char] st (Int, Int)
952+dashedLine :: Monad m => Char -> ParserT [Char] st m (Int, Int)
873953dashedLine ch = do
874954 dashes <- many1 (char ch)
875955 sp <- many (char ' ')
876956 return (length dashes, length $ dashes ++ sp)
877957
878−simpleDashedLines :: Char -> Parser [Char] st [(Int,Int)]
958+simpleDashedLines :: Monad m => Char -> ParserT [Char] st m [(Int,Int)]
879959simpleDashedLines ch = try $ many1 (dashedLine ch)
880960
881961-- Parse a table row separator
882−simpleTableSep :: Char -> RSTParser Char
962+simpleTableSep :: Monad m => Char -> RSTParser m Char
883963simpleTableSep ch = try $ simpleDashedLines ch >> newline
884964
885965-- Parse a table footer
886−simpleTableFooter :: RSTParser [Char]
966+simpleTableFooter :: Monad m => RSTParser m [Char]
887967simpleTableFooter = try $ simpleTableSep '=' >> blanklines
888968
889969-- Parse a raw line and split it into chunks by indices.
890−simpleTableRawLine :: [Int] -> RSTParser [String]
970+simpleTableRawLine :: Monad m => [Int] -> RSTParser m [String]
891971simpleTableRawLine indices = do
892972 line <- many1Till anyChar newline
893973 return (simpleTableSplitLine indices line)
894974
895975-- Parse a table row and return a list of blocks (columns).
896−simpleTableRow :: [Int] -> RSTParser [[Block]]
976+simpleTableRow :: PandocMonad m => [Int] -> RSTParser m [Blocks]
897977simpleTableRow indices = do
898978 notFollowedBy' simpleTableFooter
899979 firstLine <- simpleTableRawLine indices
900980 colLines <- return [] -- TODO
901981 let cols = map unlines . transpose $ firstLine : colLines
902− mapM (parseFromString (B.toList . mconcat <$> many plain)) cols
982+ mapM (parseFromString (mconcat <$> many plain)) cols
903983
904984simpleTableSplitLine :: [Int] -> String -> [String]
905985simpleTableSplitLine indices line =
906986 map trim
907987 $ tail $ splitByIndices (init indices) line
908988
909−simpleTableHeader :: Bool -- ^ Headerless table
910− -> RSTParser ([[Block]], [Alignment], [Int])
989+simpleTableHeader :: PandocMonad m
990+ => Bool -- ^ Headerless table
991+ -> RSTParser m ([Blocks], [Alignment], [Int])
911992simpleTableHeader headless = try $ do
912993 optional blanklines
913994 rawContent <- if headless
@@ -921,26 +1002,33 @@ simpleTableHeader headless = try $ do
9211002 let rawHeads = if headless
9221003 then replicate (length dashes) ""
9231004 else simpleTableSplitLine indices rawContent
924− heads <- mapM (parseFromString (B.toList . mconcat <$> many plain)) $
1005+ heads <- mapM (parseFromString (mconcat <$> many plain)) $
9251006 map trim rawHeads
9261007 return (heads, aligns, indices)
9271008
9281009-- Parse a simple table.
929−simpleTable :: Bool -- ^ Headerless table
930− -> RSTParser Blocks
1010+simpleTable :: PandocMonad m
1011+ => Bool -- ^ Headerless table
1012+ -> RSTParser m Blocks
9311013simpleTable headless = do
932− Table c a _w h l <- tableWith (simpleTableHeader headless) simpleTableRow sep simpleTableFooter
1014+ tbl <- tableWith (simpleTableHeader headless) simpleTableRow
1015+ sep simpleTableFooter
9331016 -- Simple tables get 0s for relative column widths (i.e., use default)
934− return $ B.singleton $ Table c a (replicate (length a) 0) h l
1017+ case B.toList tbl of
1018+ [Table c a _w h l] -> return $ B.singleton $
1019+ Table c a (replicate (length a) 0) h l
1020+ _ -> do
1021+ warning "tableWith returned something unexpected"
1022+ return tbl -- TODO error?
9351023 where
9361024 sep = return () -- optional (simpleTableSep '-')
9371025
938−gridTable :: Bool -- ^ Headerless table
939− -> RSTParser Blocks
940−gridTable headerless = B.singleton
941− <$> gridTableWith (B.toList <$> parseBlocks) headerless
1026+gridTable :: PandocMonad m
1027+ => Bool -- ^ Headerless table
1028+ -> RSTParser m Blocks
1029+gridTable headerless = gridTableWith parseBlocks headerless
9421030
943−table :: RSTParser Blocks
1031+table :: PandocMonad m => RSTParser m Blocks
9441032table = gridTable False <|> simpleTable False <|>
9451033 gridTable True <|> simpleTable True <?> "table"
9461034
@@ -948,7 +1036,7 @@ table = gridTable False <|> simpleTable False <|>
9481036-- inline
9491037--
9501038
951−inline :: RSTParser Inlines
1039+inline :: PandocMonad m => RSTParser m Inlines
9521040inline = choice [ note -- can start with whitespace, so try before ws
9531041 , whitespace
9541042 , link
@@ -964,29 +1052,29 @@ inline = choice [ note -- can start with whitespace, so try before ws
9641052 , escapedChar
9651053 , symbol ] <?> "inline"
9661054
967−parseInlineFromString :: String -> RSTParser Inlines
1055+parseInlineFromString :: PandocMonad m => String -> RSTParser m Inlines
9681056parseInlineFromString = parseFromString (trimInlines . mconcat <$> many inline)
9691057
970−hyphens :: RSTParser Inlines
1058+hyphens :: Monad m => RSTParser m Inlines
9711059hyphens = do
9721060 result <- many1 (char '-')
9731061 optional endline
9741062 -- don't want to treat endline after hyphen or dash as a space
9751063 return $ B.str result
9761064
977−escapedChar :: Parser [Char] st Inlines
1065+escapedChar :: Monad m => ParserT [Char] st m Inlines
9781066escapedChar = do c <- escaped anyChar
9791067 return $ if c == ' ' -- '\ ' is null in RST
9801068 then mempty
9811069 else B.str [c]
9821070
983−symbol :: RSTParser Inlines
1071+symbol :: Monad m => RSTParser m Inlines
9841072symbol = do
9851073 result <- oneOf specialChars
9861074 return $ B.str [result]
9871075
9881076-- parses inline code, between codeStart and codeEnd
989−code :: RSTParser Inlines
1077+code :: Monad m => RSTParser m Inlines
9901078code = try $ do
9911079 string "``"
9921080 result <- manyTill anyChar (try (string "``"))
@@ -994,7 +1082,7 @@ code = try $ do
9941082 $ trim $ unwords $ lines result
9951083
9961084-- succeeds only if we're not right after a str (ie. in middle of word)
997−atStart :: RSTParser a -> RSTParser a
1085+atStart :: Monad m => RSTParser m a -> RSTParser m a
9981086atStart p = do
9991087 pos <- getPosition
10001088 st <- getState
@@ -1002,11 +1090,11 @@ atStart p = do
10021090 guard $ stateLastStrPos st /= Just pos
10031091 p
10041092
1005−emph :: RSTParser Inlines
1093+emph :: PandocMonad m => RSTParser m Inlines
10061094emph = B.emph . trimInlines . mconcat <$>
10071095 enclosed (atStart $ char '*') (char '*') inline
10081096
1009−strong :: RSTParser Inlines
1097+strong :: PandocMonad m => RSTParser m Inlines
10101098strong = B.strong . trimInlines . mconcat <$>
10111099 enclosed (atStart $ string "**") (try $ string "**") inline
10121100
@@ -1018,12 +1106,12 @@ strong = B.strong . trimInlines . mconcat <$>
10181106-- - Classes are silently discarded in addNewRole
10191107-- - Lacks sensible implementation for title-reference (which is the default)
10201108-- - Allows direct use of the :raw: role, rST only allows inherited use.
1021−interpretedRole :: RSTParser Inlines
1109+interpretedRole :: PandocMonad m => RSTParser m Inlines
10221110interpretedRole = try $ do
10231111 (role, contents) <- roleBefore <|> roleAfter
10241112 renderRole contents Nothing role nullAttr
10251113
1026−renderRole :: String -> Maybe String -> String -> Attr -> RSTParser Inlines
1114+renderRole :: PandocMonad m => String -> Maybe String -> String -> Attr -> RSTParser m Inlines
10271115renderRole contents fmt role attr = case role of
10281116 "sup" -> return $ B.superscript $ B.str contents
10291117 "superscript" -> return $ B.superscript $ B.str contents
@@ -1050,7 +1138,7 @@ renderRole contents fmt role attr = case role of
10501138 renderRole contents newFmt newRole newAttr
10511139 Nothing -> do
10521140 pos <- getPosition
1053− addWarning (Just pos) $ "ignoring unknown role :" ++ custom ++ ": in"
1141+ warningWithPos pos $ "ignoring unknown role :" ++ custom ++ ": in"
10541142 return $ B.str contents -- Undefined role
10551143 where
10561144 titleRef ref = return $ B.str ref -- FIXME: Not a sensible behaviour
@@ -1063,31 +1151,31 @@ renderRole contents fmt role attr = case role of
10631151addClass :: String -> Attr -> Attr
10641152addClass c (ident, classes, keyValues) = (ident, union classes [c], keyValues)
10651153
1066−roleName :: RSTParser String
1154+roleName :: PandocMonad m => RSTParser m String
10671155roleName = many1 (letter <|> char '-')
10681156
1069−roleMarker :: RSTParser String
1157+roleMarker :: PandocMonad m => RSTParser m String
10701158roleMarker = char ':' *> roleName <* char ':'
10711159
1072−roleBefore :: RSTParser (String,String)
1160+roleBefore :: PandocMonad m => RSTParser m (String,String)
10731161roleBefore = try $ do
10741162 role <- roleMarker
10751163 contents <- unmarkedInterpretedText
10761164 return (role,contents)
10771165
1078−roleAfter :: RSTParser (String,String)
1166+roleAfter :: PandocMonad m => RSTParser m (String,String)
10791167roleAfter = try $ do
10801168 contents <- unmarkedInterpretedText
10811169 role <- roleMarker <|> (stateRstDefaultRole <$> getState)
10821170 return (role,contents)
10831171
1084−unmarkedInterpretedText :: RSTParser [Char]
1172+unmarkedInterpretedText :: PandocMonad m => RSTParser m [Char]
10851173unmarkedInterpretedText = enclosed (atStart $ char '`') (char '`') anyChar
10861174
1087−whitespace :: RSTParser Inlines
1175+whitespace :: PandocMonad m => RSTParser m Inlines
10881176whitespace = B.space <$ skipMany1 spaceChar <?> "whitespace"
10891177
1090−str :: RSTParser Inlines
1178+str :: Monad m => RSTParser m Inlines
10911179str = do
10921180 let strChar = noneOf ("\t\n " ++ specialChars)
10931181 result <- many1 strChar
@@ -1095,7 +1183,7 @@ str = do
10951183 return $ B.str result
10961184
10971185-- an endline character that can be treated as a space, not a structural break
1098−endline :: RSTParser Inlines
1186+endline :: Monad m => RSTParser m Inlines
10991187endline = try $ do
11001188 newline
11011189 notFollowedBy blankline
@@ -1111,10 +1199,10 @@ endline = try $ do
11111199-- links
11121200--
11131201
1114−link :: RSTParser Inlines
1202+link :: PandocMonad m => RSTParser m Inlines
11151203link = choice [explicitLink, referenceLink, autoLink] <?> "link"
11161204
1117−explicitLink :: RSTParser Inlines
1205+explicitLink :: PandocMonad m => RSTParser m Inlines
11181206explicitLink = try $ do
11191207 char '`'
11201208 notFollowedBy (char '`') -- `` marks start of inline code
@@ -1135,7 +1223,7 @@ explicitLink = try $ do
11351223 case M.lookup key keyTable of
11361224 Nothing -> do
11371225 pos <- getPosition
1138− addWarning (Just pos) $
1226+ warningWithPos pos $
11391227 "Could not find reference for " ++
11401228 show key
11411229 return ("","",nullAttr)
@@ -1143,7 +1231,7 @@ explicitLink = try $ do
11431231 _ -> return (src, "", nullAttr)
11441232 return $ B.linkWith attr (escapeURI src') tit label''
11451233
1146−referenceLink :: RSTParser Inlines
1234+referenceLink :: PandocMonad m => RSTParser m Inlines
11471235referenceLink = try $ do
11481236 (label',ref) <- withRaw (quotedReferenceName <|> simpleReferenceName) <*
11491237 char '_'
@@ -1160,7 +1248,7 @@ referenceLink = try $ do
11601248 ((src,tit), attr) <- case M.lookup key keyTable of
11611249 Nothing -> do
11621250 pos <- getPosition
1163− addWarning (Just pos) $
1251+ warningWithPos pos $
11641252 "Could not find reference for " ++
11651253 show key
11661254 return (("",""),nullAttr)
@@ -1169,20 +1257,20 @@ referenceLink = try $ do
11691257 when (isAnonKey key) $ updateState $ \s -> s{ stateKeys = M.delete key keyTable }
11701258 return $ B.linkWith attr src tit label'
11711259
1172−autoURI :: RSTParser Inlines
1260+autoURI :: Monad m => RSTParser m Inlines
11731261autoURI = do
11741262 (orig, src) <- uri
11751263 return $ B.link src "" $ B.str orig
11761264
1177−autoEmail :: RSTParser Inlines
1265+autoEmail :: Monad m => RSTParser m Inlines
11781266autoEmail = do
11791267 (orig, src) <- emailAddress
11801268 return $ B.link src "" $ B.str orig
11811269
1182−autoLink :: RSTParser Inlines
1270+autoLink :: PandocMonad m => RSTParser m Inlines
11831271autoLink = autoURI <|> autoEmail
11841272
1185−subst :: RSTParser Inlines
1273+subst :: PandocMonad m => RSTParser m Inlines
11861274subst = try $ do
11871275 (_,ref) <- withRaw $ enclosed (char '|') (char '|') inline
11881276 state <- getState
@@ -1191,12 +1279,12 @@ subst = try $ do
11911279 case M.lookup key substTable of
11921280 Nothing -> do
11931281 pos <- getPosition
1194− addWarning (Just pos) $
1282+ warningWithPos pos $
11951283 "Could not find reference for " ++ show key
11961284 return mempty
11971285 Just target -> return target
11981286
1199−note :: RSTParser Inlines
1287+note :: PandocMonad m => RSTParser m Inlines
12001288note = try $ do
12011289 optional whitespace
12021290 ref <- noteMarker
@@ -1206,7 +1294,7 @@ note = try $ do
12061294 case lookup ref notes of
12071295 Nothing -> do
12081296 pos <- getPosition
1209− addWarning (Just pos) $
1297+ warningWithPos pos $
12101298 "Could not find note for " ++ show ref
12111299 return mempty
12121300 Just raw -> do
@@ -1224,20 +1312,20 @@ note = try $ do
12241312 updateState $ \st -> st{ stateNotes = newnotes }
12251313 return $ B.note contents
12261314
1227−smart :: RSTParser Inlines
1315+smart :: PandocMonad m => RSTParser m Inlines
12281316smart = do
1229− getOption readerSmart >>= guard
1317+ guardEnabled Ext_smart
12301318 doubleQuoted <|> singleQuoted <|>
12311319 choice [apostrophe, dash, ellipses]
12321320
1233−singleQuoted :: RSTParser Inlines
1321+singleQuoted :: PandocMonad m => RSTParser m Inlines
12341322singleQuoted = try $ do
12351323 singleQuoteStart
12361324 withQuoteContext InSingleQuote $
12371325 B.singleQuoted . trimInlines . mconcat <$>
12381326 many1Till inline singleQuoteEnd
12391327
1240−doubleQuoted :: RSTParser Inlines
1328+doubleQuoted :: PandocMonad m => RSTParser m Inlines
12411329doubleQuoted = try $ do
12421330 doubleQuoteStart
12431331 withQuoteContext InDoubleQuote $
src/Text/Pandoc/Readers/TWiki.hs+87−86
@@ -30,7 +30,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
3030Conversion of twiki text to 'Pandoc' document.
3131-}
3232module Text.Pandoc.Readers.TWiki ( readTWiki
33− , readTWikiWithWarnings
3433 ) where
3534
3635import Text.Pandoc.Definition
@@ -40,44 +39,38 @@ import Text.Pandoc.Parsing hiding (enclosed, macro, nested)
4039import Text.Pandoc.Readers.HTML (htmlTag, isCommentTag)
4140import Control.Monad
4241import Text.Printf (printf)
43−import Debug.Trace (trace)
4442import Text.Pandoc.XML (fromEntities)
4543import Data.Maybe (fromMaybe)
4644import Text.HTML.TagSoup
4745import Data.Char (isAlphaNum)
4846import qualified Data.Foldable as F
49−import Text.Pandoc.Error
47+import Control.Monad.Except (throwError)
48+import Text.Pandoc.Class (PandocMonad, report)
5049
5150-- | Read twiki from an input string and return a Pandoc document.
52−readTWiki :: ReaderOptions -- ^ Reader options
53− -> String -- ^ String to parse (assuming @'\n'@ line endings)
54− -> Either PandocError Pandoc
55−readTWiki opts s =
56− (readWith parseTWiki) def{ stateOptions = opts } (s ++ "\n\n")
57−
58−readTWikiWithWarnings :: ReaderOptions -- ^ Reader options
59− -> String -- ^ String to parse (assuming @'\n'@ line endings)
60− -> Either PandocError (Pandoc, [String])
61−readTWikiWithWarnings opts s =
62− (readWith parseTWikiWithWarnings) def{ stateOptions = opts } (s ++ "\n\n")
63− where parseTWikiWithWarnings = do
64− doc <- parseTWiki
65− warnings <- stateWarnings <$> getState
66− return (doc, warnings)
67−
68−type TWParser = Parser [Char] ParserState
51+readTWiki :: PandocMonad m
52+ => ReaderOptions
53+ -> String
54+ -> m Pandoc
55+readTWiki opts s = do
56+ res <- readWithM parseTWiki def{ stateOptions = opts } (s ++ "\n\n")
57+ case res of
58+ Left e -> throwError e
59+ Right d -> return d
60+
61+type TWParser = ParserT [Char] ParserState
6962
7063--
7164-- utility functions
7265--
7366
74−tryMsg :: String -> TWParser a -> TWParser a
67+tryMsg :: String -> TWParser m a -> TWParser m a
7568tryMsg msg p = try p <?> msg
7669
77−skip :: TWParser a -> TWParser ()
70+skip :: TWParser m a -> TWParser m ()
7871skip parser = parser >> return ()
7972
80−nested :: TWParser a -> TWParser a
73+nested :: PandocMonad m => TWParser m a -> TWParser m a
8174nested p = do
8275 nestlevel <- stateMaxNestingLevel <$> getState
8376 guard $ nestlevel > 0
@@ -86,7 +79,7 @@ nested p = do
8679 updateState $ \st -> st{ stateMaxNestingLevel = nestlevel }
8780 return res
8881
89−htmlElement :: String -> TWParser (Attr, String)
82+htmlElement :: PandocMonad m => String -> TWParser m (Attr, String)
9083htmlElement tag = tryMsg tag $ do
9184 (TagOpen _ attr, _) <- htmlTag (~== TagOpen tag [])
9285 content <- manyTill anyChar (endtag <|> endofinput)
@@ -103,7 +96,8 @@ htmlAttrToPandoc attrs = (ident, classes, keyvals)
10396 classes = maybe [] words $ lookup "class" attrs
10497 keyvals = [(k,v) | (k,v) <- attrs, k /= "id" && k /= "class"]
10598
106−parseHtmlContentWithAttrs :: String -> TWParser a -> TWParser (Attr, [a])
99+parseHtmlContentWithAttrs :: PandocMonad m
100+ => String -> TWParser m a -> TWParser m (Attr, [a])
107101parseHtmlContentWithAttrs tag parser = do
108102 (attr, content) <- htmlElement tag
109103 parsedContent <- try $ parseContent content
@@ -112,14 +106,14 @@ parseHtmlContentWithAttrs tag parser = do
112106 parseContent = parseFromString $ nested $ manyTill parser endOfContent
113107 endOfContent = try $ skipMany blankline >> skipSpaces >> eof
114108
115−parseHtmlContent :: String -> TWParser a -> TWParser [a]
109+parseHtmlContent :: PandocMonad m => String -> TWParser m a -> TWParser m [a]
116110parseHtmlContent tag p = parseHtmlContentWithAttrs tag p >>= return . snd
117111
118112--
119113-- main parser
120114--
121115
122−parseTWiki :: TWParser Pandoc
116+parseTWiki :: PandocMonad m => TWParser m Pandoc
123117parseTWiki = do
124118 bs <- mconcat <$> many block
125119 spaces
@@ -131,20 +125,18 @@ parseTWiki = do
131125-- block parsers
132126--
133127
134−block :: TWParser B.Blocks
128+block :: PandocMonad m => TWParser m B.Blocks
135129block = do
136− tr <- getOption readerTrace
137130 pos <- getPosition
138131 res <- mempty <$ skipMany1 blankline
139132 <|> blockElements
140133 <|> para
141134 skipMany blankline
142− when tr $
143− trace (printf "line %d: %s" (sourceLine pos)
144− (take 60 $ show $ B.toList res)) (return ())
135+ report DEBUG $ printf "line %d: %s" (sourceLine pos)
136+ (take 60 $ show $ B.toList res)
145137 return res
146138
147−blockElements :: TWParser B.Blocks
139+blockElements :: PandocMonad m => TWParser m B.Blocks
148140blockElements = choice [ separator
149141 , header
150142 , verbatim
@@ -155,10 +147,10 @@ blockElements = choice [ separator
155147 , noautolink
156148 ]
157149
158−separator :: TWParser B.Blocks
150+separator :: PandocMonad m => TWParser m B.Blocks
159151separator = tryMsg "separator" $ string "---" >> newline >> return B.horizontalRule
160152
161−header :: TWParser B.Blocks
153+header :: PandocMonad m => TWParser m B.Blocks
162154header = tryMsg "header" $ do
163155 string "---"
164156 level <- many1 (char '+') >>= return . length
@@ -169,43 +161,45 @@ header = tryMsg "header" $ do
169161 attr <- registerHeader ("", classes, []) content
170162 return $ B.headerWith attr level $ content
171163
172−verbatim :: TWParser B.Blocks
164+verbatim :: PandocMonad m => TWParser m B.Blocks
173165verbatim = (htmlElement "verbatim" <|> htmlElement "pre")
174166 >>= return . (uncurry B.codeBlockWith)
175167
176−literal :: TWParser B.Blocks
168+literal :: PandocMonad m => TWParser m B.Blocks
177169literal = htmlElement "literal" >>= return . rawBlock
178170 where
179171 format (_, _, kvs) = fromMaybe "html" $ lookup "format" kvs
180172 rawBlock (attrs, content) = B.rawBlock (format attrs) content
181173
182−list :: String -> TWParser B.Blocks
174+list :: PandocMonad m => String -> TWParser m B.Blocks
183175list prefix = choice [ bulletList prefix
184176 , orderedList prefix
185177 , definitionList prefix]
186178
187−definitionList :: String -> TWParser B.Blocks
179+definitionList :: PandocMonad m => String -> TWParser m B.Blocks
188180definitionList prefix = tryMsg "definitionList" $ do
189181 indent <- lookAhead $ string prefix *> (many1 $ string " ") <* string "$ "
190182 elements <- many $ parseDefinitionListItem (prefix ++ concat indent)
191183 return $ B.definitionList elements
192184 where
193− parseDefinitionListItem :: String -> TWParser (B.Inlines, [B.Blocks])
185+ parseDefinitionListItem :: PandocMonad m
186+ => String -> TWParser m (B.Inlines, [B.Blocks])
194187 parseDefinitionListItem indent = do
195188 string (indent ++ "$ ") >> skipSpaces
196189 term <- many1Till inline $ string ": "
197190 line <- listItemLine indent $ string "$ "
198191 return $ (mconcat term, [line])
199192
200−bulletList :: String -> TWParser B.Blocks
193+bulletList :: PandocMonad m => String -> TWParser m B.Blocks
201194bulletList prefix = tryMsg "bulletList" $
202195 parseList prefix (char '*') (char ' ')
203196
204−orderedList :: String -> TWParser B.Blocks
197+orderedList :: PandocMonad m => String -> TWParser m B.Blocks
205198orderedList prefix = tryMsg "orderedList" $
206199 parseList prefix (oneOf "1iIaA") (string ". ")
207200
208−parseList :: String -> TWParser Char -> TWParser a -> TWParser B.Blocks
201+parseList :: PandocMonad m
202+ => String -> TWParser m Char -> TWParser m a -> TWParser m B.Blocks
209203parseList prefix marker delim = do
210204 (indent, style) <- lookAhead $ string prefix *> listStyle <* delim
211205 blocks <- many $ parseListItem (prefix ++ indent) (char style <* delim)
@@ -222,10 +216,12 @@ parseList prefix marker delim = do
222216 style <- marker
223217 return (concat indent, style)
224218
225−parseListItem :: Show a => String -> TWParser a -> TWParser B.Blocks
219+parseListItem :: (PandocMonad m, Show a)
220+ => String -> TWParser m a -> TWParser m B.Blocks
226221parseListItem prefix marker = string prefix >> marker >> listItemLine prefix marker
227222
228−listItemLine :: Show a => String -> TWParser a -> TWParser B.Blocks
223+listItemLine :: (PandocMonad m, Show a)
224+ => String -> TWParser m a -> TWParser m B.Blocks
229225listItemLine prefix marker = lineContent >>= parseContent >>= return . mconcat
230226 where
231227 lineContent = do
@@ -242,7 +238,7 @@ listItemLine prefix marker = lineContent >>= parseContent >>= return . mconcat
242238 lastNewline = try $ char '\n' <* eof
243239 newlineBeforeNestedList = try $ char '\n' <* lookAhead nestedList
244240
245−table :: TWParser B.Blocks
241+table :: PandocMonad m => TWParser m B.Blocks
246242table = try $ do
247243 tableHead <- optionMaybe $ many1Till tableParseHeader newline >>= return . unzip
248244 rows <- many1 tableParseRow
@@ -254,7 +250,7 @@ table = try $ do
254250 columns rows = replicate (columCount rows) mempty
255251 columCount rows = length $ head rows
256252
257−tableParseHeader :: TWParser ((Alignment, Double), B.Blocks)
253+tableParseHeader :: PandocMonad m => TWParser m ((Alignment, Double), B.Blocks)
258254tableParseHeader = try $ do
259255 char '|'
260256 leftSpaces <- many spaceChar >>= return . length
@@ -270,27 +266,27 @@ tableParseHeader = try $ do
270266 | left > right = (AlignRight, 0)
271267 | otherwise = (AlignLeft, 0)
272268
273−tableParseRow :: TWParser [B.Blocks]
269+tableParseRow :: PandocMonad m => TWParser m [B.Blocks]
274270tableParseRow = many1Till tableParseColumn newline
275271
276−tableParseColumn :: TWParser B.Blocks
272+tableParseColumn :: PandocMonad m => TWParser m B.Blocks
277273tableParseColumn = char '|' *> skipSpaces *>
278274 tableColumnContent (skipSpaces >> char '|')
279275 <* skipSpaces <* optional tableEndOfRow
280276
281−tableEndOfRow :: TWParser Char
277+tableEndOfRow :: PandocMonad m => TWParser m Char
282278tableEndOfRow = lookAhead (try $ char '|' >> char '\n') >> char '|'
283279
284−tableColumnContent :: TWParser a -> TWParser B.Blocks
280+tableColumnContent :: PandocMonad m => TWParser m a -> TWParser m B.Blocks
285281tableColumnContent end = manyTill content (lookAhead $ try end) >>= return . B.plain . mconcat
286282 where
287283 content = continuation <|> inline
288284 continuation = try $ char '\\' >> newline >> return mempty
289285
290−blockQuote :: TWParser B.Blocks
286+blockQuote :: PandocMonad m => TWParser m B.Blocks
291287blockQuote = parseHtmlContent "blockquote" block >>= return . B.blockQuote . mconcat
292288
293−noautolink :: TWParser B.Blocks
289+noautolink :: PandocMonad m => TWParser m B.Blocks
294290noautolink = do
295291 (_, content) <- htmlElement "noautolink"
296292 st <- getState
@@ -301,7 +297,7 @@ noautolink = do
301297 where
302298 parseContent = parseFromString $ many $ block
303299
304−para :: TWParser B.Blocks
300+para :: PandocMonad m => TWParser m B.Blocks
305301para = many1Till inline endOfParaElement >>= return . result . mconcat
306302 where
307303 endOfParaElement = lookAhead $ endOfInput <|> endOfPara <|> newBlockElement
@@ -317,7 +313,7 @@ para = many1Till inline endOfParaElement >>= return . result . mconcat
317313-- inline parsers
318314--
319315
320−inline :: TWParser B.Inlines
316+inline :: PandocMonad m => TWParser m B.Inlines
321317inline = choice [ whitespace
322318 , br
323319 , macro
@@ -338,36 +334,39 @@ inline = choice [ whitespace
338334 , symbol
339335 ] <?> "inline"
340336
341−whitespace :: TWParser B.Inlines
337+whitespace :: PandocMonad m => TWParser m B.Inlines
342338whitespace = (lb <|> regsp) >>= return
343339 where lb = try $ skipMany spaceChar >> linebreak >> return B.space
344340 regsp = try $ skipMany1 spaceChar >> return B.space
345341
346−br :: TWParser B.Inlines
342+br :: PandocMonad m => TWParser m B.Inlines
347343br = try $ string "%BR%" >> return B.linebreak
348344
349−linebreak :: TWParser B.Inlines
345+linebreak :: PandocMonad m => TWParser m B.Inlines
350346linebreak = newline >> notFollowedBy newline >> (lastNewline <|> innerNewline)
351347 where lastNewline = eof >> return mempty
352348 innerNewline = return B.space
353349
354−between :: (Monoid c) => TWParser a -> TWParser b -> (TWParser b -> TWParser c) -> TWParser c
350+between :: (Monoid c, PandocMonad m)
351+ => TWParser m a -> TWParser m b -> (TWParser m b -> TWParser m c)
352+ -> TWParser m c
355353between start end p =
356354 mconcat <$> try (start >> notFollowedBy whitespace >> many1Till (p end) end)
357355
358−enclosed :: (Monoid b) => TWParser a -> (TWParser a -> TWParser b) -> TWParser b
356+enclosed :: (Monoid b, PandocMonad m)
357+ => TWParser m a -> (TWParser m a -> TWParser m b) -> TWParser m b
359358enclosed sep p = between sep (try $ sep <* endMarker) p
360359 where
361360 endMarker = lookAhead $ skip endSpace <|> skip (oneOf ".,!?:)|") <|> eof
362361 endSpace = (spaceChar <|> newline) >> return B.space
363362
364−macro :: TWParser B.Inlines
363+macro :: PandocMonad m => TWParser m B.Inlines
365364macro = macroWithParameters <|> withoutParameters
366365 where
367366 withoutParameters = enclosed (char '%') (\_ -> macroName) >>= return . emptySpan
368367 emptySpan name = buildSpan name [] mempty
369368
370−macroWithParameters :: TWParser B.Inlines
369+macroWithParameters :: PandocMonad m => TWParser m B.Inlines
371370macroWithParameters = try $ do
372371 char '%'
373372 name <- macroName
@@ -382,13 +381,13 @@ buildSpan className kvs = B.spanWith attrs
382381 additionalClasses = maybe [] words $ lookup "class" kvs
383382 kvsWithoutClasses = [(k,v) | (k,v) <- kvs, k /= "class"]
384383
385−macroName :: TWParser String
384+macroName :: PandocMonad m => TWParser m String
386385macroName = do
387386 first <- letter
388387 rest <- many $ alphaNum <|> char '_'
389388 return (first:rest)
390389
391−attributes :: TWParser (String, [(String, String)])
390+attributes :: PandocMonad m => TWParser m (String, [(String, String)])
392391attributes = char '{' *> spnl *> many (attribute <* spnl) <* char '}' >>=
393392 return . foldr (either mkContent mkKvs) ([], [])
394393 where
@@ -397,7 +396,7 @@ attributes = char '{' *> spnl *> many (attribute <* spnl) <* char '}' >>=
397396 mkContent c (rest, kvs) = (c ++ " " ++ rest, kvs)
398397 mkKvs kv (cont, rest) = (cont, (kv : rest))
399398
400−attribute :: TWParser (Either String (String, String))
399+attribute :: PandocMonad m => TWParser m (Either String (String, String))
401400attribute = withKey <|> withoutKey
402401 where
403402 withKey = try $ do
@@ -411,49 +410,51 @@ attribute = withKey <|> withoutKey
411410 | allowSpaces == True = many1 $ noneOf "}"
412411 | otherwise = many1 $ noneOf " }"
413412
414−nestedInlines :: Show a => TWParser a -> TWParser B.Inlines
413+nestedInlines :: (Show a, PandocMonad m)
414+ => TWParser m a -> TWParser m B.Inlines
415415nestedInlines end = innerSpace <|> nestedInline
416416 where
417417 innerSpace = try $ whitespace <* (notFollowedBy end)
418418 nestedInline = notFollowedBy whitespace >> nested inline
419419
420−strong :: TWParser B.Inlines
420+strong :: PandocMonad m => TWParser m B.Inlines
421421strong = try $ enclosed (char '*') nestedInlines >>= return . B.strong
422422
423−strongHtml :: TWParser B.Inlines
423+strongHtml :: PandocMonad m => TWParser m B.Inlines
424424strongHtml = (parseHtmlContent "strong" inline <|> parseHtmlContent "b" inline)
425425 >>= return . B.strong . mconcat
426426
427−strongAndEmph :: TWParser B.Inlines
427+strongAndEmph :: PandocMonad m => TWParser m B.Inlines
428428strongAndEmph = try $ enclosed (string "__") nestedInlines >>= return . B.emph . B.strong
429429
430−emph :: TWParser B.Inlines
430+emph :: PandocMonad m => TWParser m B.Inlines
431431emph = try $ enclosed (char '_') nestedInlines >>= return . B.emph
432432
433−emphHtml :: TWParser B.Inlines
433+emphHtml :: PandocMonad m => TWParser m B.Inlines
434434emphHtml = (parseHtmlContent "em" inline <|> parseHtmlContent "i" inline)
435435 >>= return . B.emph . mconcat
436436
437−nestedString :: Show a => TWParser a -> TWParser String
437+nestedString :: (Show a, PandocMonad m)
438+ => TWParser m a -> TWParser m String
438439nestedString end = innerSpace <|> (count 1 nonspaceChar)
439440 where
440441 innerSpace = try $ many1 spaceChar <* notFollowedBy end
441442
442−boldCode :: TWParser B.Inlines
443+boldCode :: PandocMonad m => TWParser m B.Inlines
443444boldCode = try $ enclosed (string "==") nestedString >>= return . B.strong . B.code . fromEntities
444445
445−htmlComment :: TWParser B.Inlines
446+htmlComment :: PandocMonad m => TWParser m B.Inlines
446447htmlComment = htmlTag isCommentTag >> return mempty
447448
448−code :: TWParser B.Inlines
449+code :: PandocMonad m => TWParser m B.Inlines
449450code = try $ enclosed (char '=') nestedString >>= return . B.code . fromEntities
450451
451−codeHtml :: TWParser B.Inlines
452+codeHtml :: PandocMonad m => TWParser m B.Inlines
452453codeHtml = do
453454 (attrs, content) <- parseHtmlContentWithAttrs "code" anyChar
454455 return $ B.codeWith attrs $ fromEntities content
455456
456−autoLink :: TWParser B.Inlines
457+autoLink :: PandocMonad m => TWParser m B.Inlines
457458autoLink = try $ do
458459 state <- getState
459460 guard $ stateAllowLinks state
@@ -467,36 +468,36 @@ autoLink = try $ do
467468 | c == '/' = True
468469 | otherwise = isAlphaNum c
469470
470−str :: TWParser B.Inlines
471+str :: PandocMonad m => TWParser m B.Inlines
471472str = (many1 alphaNum <|> count 1 characterReference) >>= return . B.str
472473
473−nop :: TWParser B.Inlines
474+nop :: PandocMonad m => TWParser m B.Inlines
474475nop = try $ (skip exclamation <|> skip nopTag) >> followContent
475476 where
476477 exclamation = char '!'
477478 nopTag = stringAnyCase "<nop>"
478479 followContent = many1 nonspaceChar >>= return . B.str . fromEntities
479480
480−symbol :: TWParser B.Inlines
481+symbol :: PandocMonad m => TWParser m B.Inlines
481482symbol = count 1 nonspaceChar >>= return . B.str
482483
483−smart :: TWParser B.Inlines
484+smart :: PandocMonad m => TWParser m B.Inlines
484485smart = do
485− getOption readerSmart >>= guard
486+ guardEnabled Ext_smart
486487 doubleQuoted <|> singleQuoted <|>
487488 choice [ apostrophe
488489 , dash
489490 , ellipses
490491 ]
491492
492−singleQuoted :: TWParser B.Inlines
493+singleQuoted :: PandocMonad m => TWParser m B.Inlines
493494singleQuoted = try $ do
494495 singleQuoteStart
495496 withQuoteContext InSingleQuote $
496497 many1Till inline singleQuoteEnd >>=
497498 (return . B.singleQuoted . B.trimInlines . mconcat)
498499
499−doubleQuoted :: TWParser B.Inlines
500+doubleQuoted :: PandocMonad m => TWParser m B.Inlines
500501doubleQuoted = try $ do
501502 doubleQuoteStart
502503 contents <- mconcat <$> many (try $ notFollowedBy doubleQuoteEnd >> inline)
@@ -504,7 +505,7 @@ doubleQuoted = try $ do
504505 return (B.doubleQuoted $ B.trimInlines contents))
505506 <|> (return $ (B.str "\8220") B.<> contents)
506507
507−link :: TWParser B.Inlines
508+link :: PandocMonad m => TWParser m B.Inlines
508509link = try $ do
509510 st <- getState
510511 guard $ stateAllowLinks st
@@ -513,7 +514,7 @@ link = try $ do
513514 setState $ st{ stateAllowLinks = True }
514515 return $ B.link url title content
515516
516−linkText :: TWParser (String, String, B.Inlines)
517+linkText :: PandocMonad m => TWParser m (String, String, B.Inlines)
517518linkText = do
518519 string "[["
519520 url <- many1Till anyChar (char ']')
src/Text/Pandoc/Readers/TeXMath.hsdeleted−48
@@ -1,48 +0,0 @@
1−{-
2−Copyright (C) 2007-2015 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.TeXMath
21− Copyright : Copyright (C) 2007-2015 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−Conversion of TeX math to a list of 'Pandoc' inline elements.
29−-}
30−module Text.Pandoc.Readers.TeXMath ( texMathToInlines ) where
31−
32−import Text.Pandoc.Definition
33−import Text.TeXMath
34−
35−-- | Converts a raw TeX math formula to a list of 'Pandoc' inlines.
36−-- Defaults to raw formula between @$@ or @$$@ characters if entire formula
37−-- can't be converted.
38−texMathToInlines :: MathType
39− -> String -- ^ String to parse (assumes @'\n'@ line endings)
40− -> [Inline]
41−texMathToInlines mt inp =
42− case writePandoc dt `fmap` readTeX inp of
43− Right (Just ils) -> ils
44− _ -> [Str (delim ++ inp ++ delim)]
45− where (dt, delim) = case mt of
46− DisplayMath -> (DisplayBlock, "$$")
47− InlineMath -> (DisplayInline, "$")
48−
src/Text/Pandoc/Readers/Textile.hs+96−93
@@ -64,31 +64,33 @@ import Text.HTML.TagSoup (fromAttrib, Tag(..))
6464import Text.HTML.TagSoup.Match
6565import Data.List ( intercalate, transpose, intersperse )
6666import Data.Char ( digitToInt, isUpper )
67−import Control.Monad ( guard, liftM, when )
67+import Control.Monad ( guard, liftM )
6868import Data.Monoid ((<>))
6969import Text.Printf
70−import Debug.Trace (trace)
71−import Text.Pandoc.Error
70+import Text.Pandoc.Class (PandocMonad, report)
71+import Control.Monad.Except (throwError)
7272
7373-- | Parse a Textile text and return a Pandoc document.
74−readTextile :: ReaderOptions -- ^ Reader options
74+readTextile :: PandocMonad m
75+ => ReaderOptions -- ^ Reader options
7576 -> String -- ^ String to parse (assuming @'\n'@ line endings)
76− -> Either PandocError Pandoc
77−readTextile opts s =
78− (readWith parseTextile) def{ stateOptions = opts } (s ++ "\n\n")
77+ -> m Pandoc
78+readTextile opts s = do
79+ parsed <- readWithM parseTextile def{ stateOptions = opts } (s ++ "\n\n")
80+ case parsed of
81+ Right result -> return result
82+ Left e -> throwError e
7983
8084
8185-- | Generate a Pandoc ADT from a textile document
82−parseTextile :: Parser [Char] ParserState Pandoc
86+parseTextile :: PandocMonad m => ParserT [Char] ParserState m Pandoc
8387parseTextile = do
8488 -- textile allows raw HTML and does smart punctuation by default,
8589 -- but we do not enable smart punctuation unless it is explicitly
8690 -- asked for, for better conversion to other light markup formats
8791 oldOpts <- stateOptions `fmap` getState
8892 updateState $ \state -> state{ stateOptions =
89− oldOpts{ readerParseRaw = True
90− , readerOldDashes = True
91− } }
93+ oldOpts{ readerParseRaw = True } }
9294 many blankline
9395 startPos <- getPosition
9496 -- go through once just to get list of reference keys and notes
@@ -103,10 +105,10 @@ parseTextile = do
103105 blocks <- parseBlocks
104106 return $ Pandoc nullMeta (B.toList blocks) -- FIXME
105107
106−noteMarker :: Parser [Char] ParserState [Char]
108+noteMarker :: PandocMonad m => ParserT [Char] ParserState m [Char]
107109noteMarker = skipMany spaceChar >> string "fn" >> manyTill digit (char '.')
108110
109−noteBlock :: Parser [Char] ParserState [Char]
111+noteBlock :: PandocMonad m => ParserT [Char] ParserState m [Char]
110112noteBlock = try $ do
111113 startPos <- getPosition
112114 ref <- noteMarker
@@ -121,11 +123,11 @@ noteBlock = try $ do
121123 return $ replicate (sourceLine endPos - sourceLine startPos) '\n'
122124
123125-- | Parse document blocks
124−parseBlocks :: Parser [Char] ParserState Blocks
126+parseBlocks :: PandocMonad m => ParserT [Char] ParserState m Blocks
125127parseBlocks = mconcat <$> manyTill block eof
126128
127129-- | Block parsers list tried in definition order
128−blockParsers :: [Parser [Char] ParserState Blocks]
130+blockParsers :: PandocMonad m => [ParserT [Char] ParserState m Blocks]
129131blockParsers = [ codeBlock
130132 , header
131133 , blockQuote
@@ -140,26 +142,24 @@ blockParsers = [ codeBlock
140142 ]
141143
142144-- | Any block in the order of definition of blockParsers
143−block :: Parser [Char] ParserState Blocks
145+block :: PandocMonad m => ParserT [Char] ParserState m Blocks
144146block = do
145147 res <- choice blockParsers <?> "block"
146148 pos <- getPosition
147− tr <- getOption readerTrace
148− when tr $
149− trace (printf "line %d: %s" (sourceLine pos)
150− (take 60 $ show $ B.toList res)) (return ())
149+ report DEBUG $ printf "line %d: %s" (sourceLine pos)
150+ (take 60 $ show $ B.toList res)
151151 return res
152152
153−commentBlock :: Parser [Char] ParserState Blocks
153+commentBlock :: PandocMonad m => ParserT [Char] ParserState m Blocks
154154commentBlock = try $ do
155155 string "###."
156156 manyTill anyLine blanklines
157157 return mempty
158158
159−codeBlock :: Parser [Char] ParserState Blocks
159+codeBlock :: PandocMonad m => ParserT [Char] ParserState m Blocks
160160codeBlock = codeBlockBc <|> codeBlockPre
161161
162−codeBlockBc :: Parser [Char] ParserState Blocks
162+codeBlockBc :: PandocMonad m => ParserT [Char] ParserState m Blocks
163163codeBlockBc = try $ do
164164 string "bc."
165165 extended <- option False (True <$ char '.')
@@ -179,7 +179,7 @@ trimTrailingNewlines :: String -> String
179179trimTrailingNewlines = reverse . dropWhile (=='\n') . reverse
180180
181181-- | Code Blocks in Textile are between <pre> and </pre>
182−codeBlockPre :: Parser [Char] ParserState Blocks
182+codeBlockPre :: PandocMonad m => ParserT [Char] ParserState m Blocks
183183codeBlockPre = try $ do
184184 (t@(TagOpen _ attrs),_) <- htmlTag (tagOpen (=="pre") (const True))
185185 result' <- manyTill anyChar (htmlTag (tagClose (=="pre")))
@@ -198,7 +198,7 @@ codeBlockPre = try $ do
198198 return $ B.codeBlockWith (ident,classes,kvs) result'''
199199
200200-- | Header of the form "hN. content" with N in 1..6
201−header :: Parser [Char] ParserState Blocks
201+header :: PandocMonad m => ParserT [Char] ParserState m Blocks
202202header = try $ do
203203 char 'h'
204204 level <- digitToInt <$> oneOf "123456"
@@ -210,14 +210,14 @@ header = try $ do
210210 return $ B.headerWith attr' level name
211211
212212-- | Blockquote of the form "bq. content"
213−blockQuote :: Parser [Char] ParserState Blocks
213+blockQuote :: PandocMonad m => ParserT [Char] ParserState m Blocks
214214blockQuote = try $ do
215215 string "bq" >> attributes >> char '.' >> whitespace
216216 B.blockQuote <$> para
217217
218218-- Horizontal rule
219219
220−hrule :: Parser [Char] st Blocks
220+hrule :: PandocMonad m => ParserT [Char] st m Blocks
221221hrule = try $ do
222222 skipSpaces
223223 start <- oneOf "-*"
@@ -232,39 +232,39 @@ hrule = try $ do
232232-- | Can be a bullet list or an ordered list. This implementation is
233233-- strict in the nesting, sublist must start at exactly "parent depth
234234-- plus one"
235−anyList :: Parser [Char] ParserState Blocks
235+anyList :: PandocMonad m => ParserT [Char] ParserState m Blocks
236236anyList = try $ anyListAtDepth 1 <* blanklines
237237
238238-- | This allow one type of list to be nested into an other type,
239239-- provided correct nesting
240−anyListAtDepth :: Int -> Parser [Char] ParserState Blocks
240+anyListAtDepth :: PandocMonad m => Int -> ParserT [Char] ParserState m Blocks
241241anyListAtDepth depth = choice [ bulletListAtDepth depth,
242242 orderedListAtDepth depth,
243243 definitionList ]
244244
245245-- | Bullet List of given depth, depth being the number of leading '*'
246−bulletListAtDepth :: Int -> Parser [Char] ParserState Blocks
246+bulletListAtDepth :: PandocMonad m => Int -> ParserT [Char] ParserState m Blocks
247247bulletListAtDepth depth = try $ B.bulletList <$> many1 (bulletListItemAtDepth depth)
248248
249249-- | Bullet List Item of given depth, depth being the number of
250250-- leading '*'
251−bulletListItemAtDepth :: Int -> Parser [Char] ParserState Blocks
251+bulletListItemAtDepth :: PandocMonad m => Int -> ParserT [Char] ParserState m Blocks
252252bulletListItemAtDepth = genericListItemAtDepth '*'
253253
254254-- | Ordered List of given depth, depth being the number of
255255-- leading '#'
256−orderedListAtDepth :: Int -> Parser [Char] ParserState Blocks
256+orderedListAtDepth :: PandocMonad m => Int -> ParserT [Char] ParserState m Blocks
257257orderedListAtDepth depth = try $ do
258258 items <- many1 (orderedListItemAtDepth depth)
259259 return $ B.orderedList items
260260
261261-- | Ordered List Item of given depth, depth being the number of
262262-- leading '#'
263−orderedListItemAtDepth :: Int -> Parser [Char] ParserState Blocks
263+orderedListItemAtDepth :: PandocMonad m => Int -> ParserT [Char] ParserState m Blocks
264264orderedListItemAtDepth = genericListItemAtDepth '#'
265265
266266-- | Common implementation of list items
267−genericListItemAtDepth :: Char -> Int -> Parser [Char] ParserState Blocks
267+genericListItemAtDepth :: PandocMonad m => Char -> Int -> ParserT [Char] ParserState m Blocks
268268genericListItemAtDepth c depth = try $ do
269269 count depth (char c) >> attributes >> whitespace
270270 p <- mconcat <$> many listInline
@@ -273,25 +273,25 @@ genericListItemAtDepth c depth = try $ do
273273 return $ (B.plain p) <> sublist
274274
275275-- | A definition list is a set of consecutive definition items
276−definitionList :: Parser [Char] ParserState Blocks
276+definitionList :: PandocMonad m => ParserT [Char] ParserState m Blocks
277277definitionList = try $ B.definitionList <$> many1 definitionListItem
278278
279279-- | List start character.
280−listStart :: Parser [Char] ParserState ()
280+listStart :: PandocMonad m => ParserT [Char] ParserState m ()
281281listStart = genericListStart '*'
282282 <|> () <$ genericListStart '#'
283283 <|> () <$ definitionListStart
284284
285−genericListStart :: Char -> Parser [Char] st ()
285+genericListStart :: PandocMonad m => Char -> ParserT [Char] st m ()
286286genericListStart c = () <$ try (many1 (char c) >> whitespace)
287287
288−basicDLStart :: Parser [Char] ParserState ()
288+basicDLStart :: PandocMonad m => ParserT [Char] ParserState m ()
289289basicDLStart = do
290290 char '-'
291291 whitespace
292292 notFollowedBy newline
293293
294−definitionListStart :: Parser [Char] ParserState Inlines
294+definitionListStart :: PandocMonad m => ParserT [Char] ParserState m Inlines
295295definitionListStart = try $ do
296296 basicDLStart
297297 trimInlines . mconcat <$>
@@ -300,7 +300,7 @@ definitionListStart = try $ do
300300 <|> try (lookAhead (() <$ string ":="))
301301 )
302302
303−listInline :: Parser [Char] ParserState Inlines
303+listInline :: PandocMonad m => ParserT [Char] ParserState m Inlines
304304listInline = try (notFollowedBy newline >> inline)
305305 <|> try (endline <* notFollowedBy listStart)
306306
@@ -308,15 +308,15 @@ listInline = try (notFollowedBy newline >> inline)
308308-- the term defined, then spaces and ":=". The definition follows, on
309309-- the same single line, or spaned on multiple line, after a line
310310-- break.
311−definitionListItem :: Parser [Char] ParserState (Inlines, [Blocks])
311+definitionListItem :: PandocMonad m => ParserT [Char] ParserState m (Inlines, [Blocks])
312312definitionListItem = try $ do
313313 term <- (mconcat . intersperse B.linebreak) <$> many1 definitionListStart
314314 def' <- string ":=" *> optional whitespace *> (multilineDef <|> inlineDef)
315315 return (term, def')
316− where inlineDef :: Parser [Char] ParserState [Blocks]
316+ where inlineDef :: PandocMonad m => ParserT [Char] ParserState m [Blocks]
317317 inlineDef = liftM (\d -> [B.plain d])
318318 $ optional whitespace >> (trimInlines . mconcat <$> many listInline) <* newline
319− multilineDef :: Parser [Char] ParserState [Blocks]
319+ multilineDef :: PandocMonad m => ParserT [Char] ParserState m [Blocks]
320320 multilineDef = try $ do
321321 optional whitespace >> newline
322322 s <- many1Till anyChar (try (string "=:" >> newline))
@@ -327,7 +327,7 @@ definitionListItem = try $ do
327327-- raw content
328328
329329-- | A raw Html Block, optionally followed by blanklines
330−rawHtmlBlock :: Parser [Char] ParserState Blocks
330+rawHtmlBlock :: PandocMonad m => ParserT [Char] ParserState m Blocks
331331rawHtmlBlock = try $ do
332332 skipMany spaceChar
333333 (_,b) <- htmlTag isBlockTag
@@ -335,14 +335,14 @@ rawHtmlBlock = try $ do
335335 return $ B.rawBlock "html" b
336336
337337-- | Raw block of LaTeX content
338−rawLaTeXBlock' :: Parser [Char] ParserState Blocks
338+rawLaTeXBlock' :: PandocMonad m => ParserT [Char] ParserState m Blocks
339339rawLaTeXBlock' = do
340340 guardEnabled Ext_raw_tex
341341 B.rawBlock "latex" <$> (rawLaTeXBlock <* spaces)
342342
343343
344344-- | In textile, paragraphs are separated by blank lines.
345−para :: Parser [Char] ParserState Blocks
345+para :: PandocMonad m => ParserT [Char] ParserState m Blocks
346346para = B.para . trimInlines . mconcat <$> many1 inline
347347
348348-- Tables
@@ -353,7 +353,7 @@ toAlignment '>' = AlignRight
353353toAlignment '=' = AlignCenter
354354toAlignment _ = AlignDefault
355355
356−cellAttributes :: Parser [Char] ParserState (Bool, Alignment)
356+cellAttributes :: PandocMonad m => ParserT [Char] ParserState m (Bool, Alignment)
357357cellAttributes = try $ do
358358 isHeader <- option False (True <$ char '_')
359359 -- we just ignore colspan and rowspan markers:
@@ -366,7 +366,7 @@ cellAttributes = try $ do
366366 return (isHeader, alignment)
367367
368368-- | A table cell spans until a pipe |
369−tableCell :: Parser [Char] ParserState ((Bool, Alignment), Blocks)
369+tableCell :: PandocMonad m => ParserT [Char] ParserState m ((Bool, Alignment), Blocks)
370370tableCell = try $ do
371371 char '|'
372372 (isHeader, alignment) <- option (False, AlignDefault) $ cellAttributes
@@ -377,7 +377,7 @@ tableCell = try $ do
377377 return ((isHeader, alignment), B.plain content)
378378
379379-- | A table row is made of many table cells
380−tableRow :: Parser [Char] ParserState [((Bool, Alignment), Blocks)]
380+tableRow :: PandocMonad m => ParserT [Char] ParserState m [((Bool, Alignment), Blocks)]
381381tableRow = try $ do
382382 -- skip optional row attributes
383383 optional $ try $ do
@@ -387,7 +387,7 @@ tableRow = try $ do
387387 many1 tableCell <* char '|' <* blankline
388388
389389-- | A table with an optional header.
390−table :: Parser [Char] ParserState Blocks
390+table :: PandocMonad m => ParserT [Char] ParserState m Blocks
391391table = try $ do
392392 -- ignore table attributes
393393 caption <- option mempty $ try $ do
@@ -411,7 +411,7 @@ table = try $ do
411411 (map (map snd) rows)
412412
413413-- | Ignore markers for cols, thead, tfoot.
414−ignorableRow :: Parser [Char] ParserState ()
414+ignorableRow :: PandocMonad m => ParserT [Char] ParserState m ()
415415ignorableRow = try $ do
416416 char '|'
417417 oneOf ":^-~"
@@ -420,7 +420,7 @@ ignorableRow = try $ do
420420 _ <- anyLine
421421 return ()
422422
423−explicitBlockStart :: String -> Parser [Char] ParserState ()
423+explicitBlockStart :: PandocMonad m => String -> ParserT [Char] ParserState m ()
424424explicitBlockStart name = try $ do
425425 string name
426426 attributes
@@ -430,9 +430,10 @@ explicitBlockStart name = try $ do
430430
431431-- | Blocks like 'p' and 'table' do not need explicit block tag.
432432-- However, they can be used to set HTML/CSS attributes when needed.
433−maybeExplicitBlock :: String -- ^ block tag name
434− -> Parser [Char] ParserState Blocks -- ^ implicit block
435− -> Parser [Char] ParserState Blocks
433+maybeExplicitBlock :: PandocMonad m
434+ => String -- ^ block tag name
435+ -> ParserT [Char] ParserState m Blocks -- ^ implicit block
436+ -> ParserT [Char] ParserState m Blocks
436437maybeExplicitBlock name blk = try $ do
437438 optional $ explicitBlockStart name
438439 blk
@@ -445,12 +446,12 @@ maybeExplicitBlock name blk = try $ do
445446
446447
447448-- | Any inline element
448−inline :: Parser [Char] ParserState Inlines
449+inline :: PandocMonad m => ParserT [Char] ParserState m Inlines
449450inline = do
450451 choice inlineParsers <?> "inline"
451452
452453-- | Inline parsers tried in order
453−inlineParsers :: [Parser [Char] ParserState Inlines]
454+inlineParsers :: PandocMonad m => [ParserT [Char] ParserState m Inlines]
454455inlineParsers = [ str
455456 , whitespace
456457 , endline
@@ -470,7 +471,7 @@ inlineParsers = [ str
470471 ]
471472
472473-- | Inline markups
473−inlineMarkup :: Parser [Char] ParserState Inlines
474+inlineMarkup :: PandocMonad m => ParserT [Char] ParserState m Inlines
474475inlineMarkup = choice [ simpleInline (string "??") (B.cite [])
475476 , simpleInline (string "**") B.strong
476477 , simpleInline (string "__") B.emph
@@ -484,29 +485,29 @@ inlineMarkup = choice [ simpleInline (string "??") (B.cite [])
484485 ]
485486
486487-- | Trademark, registered, copyright
487−mark :: Parser [Char] st Inlines
488+mark :: PandocMonad m => ParserT [Char] st m Inlines
488489mark = try $ char '(' >> (try tm <|> try reg <|> copy)
489490
490−reg :: Parser [Char] st Inlines
491+reg :: PandocMonad m => ParserT [Char] st m Inlines
491492reg = do
492493 oneOf "Rr"
493494 char ')'
494495 return $ B.str "\174"
495496
496−tm :: Parser [Char] st Inlines
497+tm :: PandocMonad m => ParserT [Char] st m Inlines
497498tm = do
498499 oneOf "Tt"
499500 oneOf "Mm"
500501 char ')'
501502 return $ B.str "\8482"
502503
503−copy :: Parser [Char] st Inlines
504+copy :: PandocMonad m => ParserT [Char] st m Inlines
504505copy = do
505506 oneOf "Cc"
506507 char ')'
507508 return $ B.str "\169"
508509
509−note :: Parser [Char] ParserState Inlines
510+note :: PandocMonad m => ParserT [Char] ParserState m Inlines
510511note = try $ do
511512 ref <- (char '[' *> many1 digit <* char ']')
512513 notes <- stateNotes <$> getState
@@ -530,13 +531,13 @@ wordBoundaries :: [Char]
530531wordBoundaries = markupChars ++ stringBreakers
531532
532533-- | Parse a hyphened sequence of words
533−hyphenedWords :: Parser [Char] ParserState String
534+hyphenedWords :: PandocMonad m => ParserT [Char] ParserState m String
534535hyphenedWords = do
535536 x <- wordChunk
536537 xs <- many (try $ char '-' >> wordChunk)
537538 return $ intercalate "-" (x:xs)
538539
539−wordChunk :: Parser [Char] ParserState String
540+wordChunk :: PandocMonad m => ParserT [Char] ParserState m String
540541wordChunk = try $ do
541542 hd <- noneOf wordBoundaries
542543 tl <- many ( (noneOf wordBoundaries) <|>
@@ -545,7 +546,7 @@ wordChunk = try $ do
545546 return $ hd:tl
546547
547548-- | Any string
548−str :: Parser [Char] ParserState Inlines
549+str :: PandocMonad m => ParserT [Char] ParserState m Inlines
549550str = do
550551 baseStr <- hyphenedWords
551552 -- RedCloth compliance : if parsed word is uppercase and immediatly
@@ -558,11 +559,11 @@ str = do
558559 return $ B.str fullStr
559560
560561-- | Some number of space chars
561−whitespace :: Parser [Char] st Inlines
562+whitespace :: PandocMonad m => ParserT [Char] st m Inlines
562563whitespace = many1 spaceChar >> return B.space <?> "whitespace"
563564
564565-- | In Textile, an isolated endline character is a line break
565−endline :: Parser [Char] ParserState Inlines
566+endline :: PandocMonad m => ParserT [Char] ParserState m Inlines
566567endline = try $ do
567568 newline
568569 notFollowedBy blankline
@@ -570,18 +571,18 @@ endline = try $ do
570571 notFollowedBy rawHtmlBlock
571572 return B.linebreak
572573
573−rawHtmlInline :: Parser [Char] ParserState Inlines
574+rawHtmlInline :: PandocMonad m => ParserT [Char] ParserState m Inlines
574575rawHtmlInline = B.rawInline "html" . snd <$> htmlTag isInlineTag
575576
576577-- | Raw LaTeX Inline
577−rawLaTeXInline' :: Parser [Char] ParserState Inlines
578+rawLaTeXInline' :: PandocMonad m => ParserT [Char] ParserState m Inlines
578579rawLaTeXInline' = try $ do
579580 guardEnabled Ext_raw_tex
580581 B.singleton <$> rawLaTeXInline
581582
582583-- | Textile standard link syntax is "label":target. But we
583584-- can also have ["label":target].
584−link :: Parser [Char] ParserState Inlines
585+link :: PandocMonad m => ParserT [Char] ParserState m Inlines
585586link = try $ do
586587 bracketed <- (True <$ char '[') <|> return False
587588 char '"' *> notFollowedBy (oneOf " \t\n\r")
@@ -600,7 +601,7 @@ link = try $ do
600601 else B.spanWith attr $ B.link url "" name'
601602
602603-- | image embedding
603−image :: Parser [Char] ParserState Inlines
604+image :: PandocMonad m => ParserT [Char] ParserState m Inlines
604605image = try $ do
605606 char '!' >> notFollowedBy space
606607 (ident, cls, kvs) <- attributes
@@ -612,50 +613,50 @@ image = try $ do
612613 char '!'
613614 return $ B.imageWith attr src alt (B.str alt)
614615
615−escapedInline :: Parser [Char] ParserState Inlines
616+escapedInline :: PandocMonad m => ParserT [Char] ParserState m Inlines
616617escapedInline = escapedEqs <|> escapedTag
617618
618−escapedEqs :: Parser [Char] ParserState Inlines
619+escapedEqs :: PandocMonad m => ParserT [Char] ParserState m Inlines
619620escapedEqs = B.str <$>
620621 (try $ string "==" *> manyTill anyChar' (try $ string "=="))
621622
622623-- | literal text escaped btw <notextile> tags
623−escapedTag :: Parser [Char] ParserState Inlines
624+escapedTag :: PandocMonad m => ParserT [Char] ParserState m Inlines
624625escapedTag = B.str <$>
625626 (try $ string "<notextile>" *>
626627 manyTill anyChar' (try $ string "</notextile>"))
627628
628629-- | Any special symbol defined in wordBoundaries
629−symbol :: Parser [Char] ParserState Inlines
630+symbol :: PandocMonad m => ParserT [Char] ParserState m Inlines
630631symbol = B.str . singleton <$> (notFollowedBy newline *>
631632 notFollowedBy rawHtmlBlock *>
632633 oneOf wordBoundaries)
633634
634635-- | Inline code
635−code :: Parser [Char] ParserState Inlines
636+code :: PandocMonad m => ParserT [Char] ParserState m Inlines
636637code = code1 <|> code2
637638
638639-- any character except a newline before a blank line
639−anyChar' :: Parser [Char] ParserState Char
640+anyChar' :: PandocMonad m => ParserT [Char] ParserState m Char
640641anyChar' =
641642 satisfy (/='\n') <|> (try $ char '\n' <* notFollowedBy blankline)
642643
643−code1 :: Parser [Char] ParserState Inlines
644+code1 :: PandocMonad m => ParserT [Char] ParserState m Inlines
644645code1 = B.code <$> surrounded (char '@') anyChar'
645646
646−code2 :: Parser [Char] ParserState Inlines
647+code2 :: PandocMonad m => ParserT [Char] ParserState m Inlines
647648code2 = do
648649 htmlTag (tagOpen (=="tt") null)
649650 B.code <$> manyTill anyChar' (try $ htmlTag $ tagClose (=="tt"))
650651
651652-- | Html / CSS attributes
652−attributes :: Parser [Char] ParserState Attr
653+attributes :: PandocMonad m => ParserT [Char] ParserState m Attr
653654attributes = (foldl (flip ($)) ("",[],[])) <$>
654655 try (do special <- option id specialAttribute
655656 attrs <- many attribute
656657 return (special : attrs))
657658
658−specialAttribute :: Parser [Char] ParserState (Attr -> Attr)
659+specialAttribute :: PandocMonad m => ParserT [Char] ParserState m (Attr -> Attr)
659660specialAttribute = do
660661 alignStr <- ("center" <$ char '=') <|>
661662 ("justify" <$ try (string "<>")) <|>
@@ -664,11 +665,11 @@ specialAttribute = do
664665 notFollowedBy spaceChar
665666 return $ addStyle ("text-align:" ++ alignStr)
666667
667−attribute :: Parser [Char] ParserState (Attr -> Attr)
668+attribute :: PandocMonad m => ParserT [Char] ParserState m (Attr -> Attr)
668669attribute = try $
669670 (classIdAttr <|> styleAttr <|> langAttr) <* notFollowedBy spaceChar
670671
671−classIdAttr :: Parser [Char] ParserState (Attr -> Attr)
672+classIdAttr :: PandocMonad m => ParserT [Char] ParserState m (Attr -> Attr)
672673classIdAttr = try $ do -- (class class #id)
673674 char '('
674675 ws <- words `fmap` manyTill anyChar' (char ')')
@@ -679,7 +680,7 @@ classIdAttr = try $ do -- (class class #id)
679680 classes' -> return $ \(_,_,keyvals) ->
680681 ("",classes',keyvals)
681682
682−styleAttr :: Parser [Char] ParserState (Attr -> Attr)
683+styleAttr :: PandocMonad m => ParserT [Char] ParserState m (Attr -> Attr)
683684styleAttr = do
684685 style <- try $ enclosed (char '{') (char '}') anyChar'
685686 return $ addStyle style
@@ -690,21 +691,23 @@ addStyle style (id',classes,keyvals) =
690691 where keyvals' = ("style", style') : [(k,v) | (k,v) <- keyvals, k /= "style"]
691692 style' = style ++ ";" ++ concat [v | ("style",v) <- keyvals]
692693
693−langAttr :: Parser [Char] ParserState (Attr -> Attr)
694+langAttr :: PandocMonad m => ParserT [Char] ParserState m (Attr -> Attr)
694695langAttr = do
695696 lang <- try $ enclosed (char '[') (char ']') alphaNum
696697 return $ \(id',classes,keyvals) -> (id',classes,("lang",lang):keyvals)
697698
698699-- | Parses material surrounded by a parser.
699−surrounded :: Parser [Char] st t -- ^ surrounding parser
700− -> Parser [Char] st a -- ^ content parser (to be used repeatedly)
701− -> Parser [Char] st [a]
700+surrounded :: PandocMonad m
701+ => ParserT [Char] st m t -- ^ surrounding parser
702+ -> ParserT [Char] st m a -- ^ content parser (to be used repeatedly)
703+ -> ParserT [Char] st m [a]
702704surrounded border =
703705 enclosed (border *> notFollowedBy (oneOf " \t\n\r")) (try border)
704706
705−simpleInline :: Parser [Char] ParserState t -- ^ surrounding parser
706− -> (Inlines -> Inlines) -- ^ Inline constructor
707− -> Parser [Char] ParserState Inlines -- ^ content parser (to be used repeatedly)
707+simpleInline :: PandocMonad m
708+ => ParserT [Char] ParserState m t -- ^ surrounding parser
709+ -> (Inlines -> Inlines) -- ^ Inline constructor
710+ -> ParserT [Char] ParserState m Inlines -- ^ content parser (to be used repeatedly)
708711simpleInline border construct = try $ do
709712 notAfterString
710713 border *> notFollowedBy (oneOf " \t\n\r")
@@ -718,7 +721,7 @@ simpleInline border construct = try $ do
718721 then body
719722 else B.spanWith attr body
720723
721−groupedInlineMarkup :: Parser [Char] ParserState Inlines
724+groupedInlineMarkup :: PandocMonad m => ParserT [Char] ParserState m Inlines
722725groupedInlineMarkup = try $ do
723726 char '['
724727 sp1 <- option mempty $ B.space <$ whitespace
src/Text/Pandoc/Readers/Txt2Tags.hs+41−22
@@ -29,7 +29,7 @@ Conversion of txt2tags formatted plain text to 'Pandoc' document.
2929module Text.Pandoc.Readers.Txt2Tags ( readTxt2Tags
3030 , getT2TMeta
3131 , T2TMeta (..)
32− , readTxt2TagsNoMacros)
32+ )
3333 where
3434
3535import qualified Text.Pandoc.Builder as B
@@ -37,7 +37,7 @@ import Text.Pandoc.Builder ( Inlines, Blocks, trimInlines )
3737import Data.Monoid ((<>))
3838import Text.Pandoc.Definition
3939import Text.Pandoc.Options
40−import Text.Pandoc.Shared (escapeURI,compactify', compactify'DL)
40+import Text.Pandoc.Shared (escapeURI,compactify, compactifyDL)
4141import Text.Pandoc.Parsing hiding (space, spaces, uri, macro)
4242import Data.Char (toLower)
4343import Data.List (transpose, intersperse, intercalate)
@@ -46,13 +46,12 @@ import Data.Maybe (fromMaybe)
4646import Control.Monad (void, guard, when)
4747import Data.Default
4848import Control.Monad.Reader (Reader, runReader, asks)
49−import Text.Pandoc.Error
5049
51−import Data.Time.LocalTime (getZonedTime)
52−import System.Directory(getModificationTime)
5350import Data.Time.Format (formatTime)
5451import Text.Pandoc.Compat.Time (defaultTimeLocale)
55−import System.IO.Error (catchIOError)
52+import Control.Monad.Except (throwError, catchError)
53+import Text.Pandoc.Class (PandocMonad)
54+import qualified Text.Pandoc.Class as P
5655
5756type T2T = ParserT String ParserState (Reader T2TMeta)
5857
@@ -69,26 +68,42 @@ instance Default T2TMeta where
6968 def = T2TMeta "" "" "" ""
7069
7170-- | Get the meta information required by Txt2Tags macros
72−getT2TMeta :: [FilePath] -> FilePath -> IO T2TMeta
73−getT2TMeta inps out = do
74− curDate <- formatTime defaultTimeLocale "%F" <$> getZonedTime
71+getT2TMeta :: PandocMonad m => m T2TMeta
72+getT2TMeta = do
73+ mbInps <- P.getInputFiles
74+ let inps = case mbInps of
75+ Just x -> x
76+ Nothing -> []
77+ mbOutp <- P.getOutputFile
78+ let outp = case mbOutp of
79+ Just x -> x
80+ Nothing -> ""
81+ curDate <- formatTime defaultTimeLocale "%F" <$> P.getZonedTime
7582 let getModTime = fmap (formatTime defaultTimeLocale "%T") .
76− getModificationTime
83+ P.getModificationTime
7784 curMtime <- case inps of
78− [] -> formatTime defaultTimeLocale "%T" <$> getZonedTime
79− _ -> catchIOError
85+ [] -> formatTime defaultTimeLocale "%T" <$> P.getZonedTime
86+ _ -> catchError
8087 (maximum <$> mapM getModTime inps)
8188 (const (return ""))
82− return $ T2TMeta curDate curMtime (intercalate ", " inps) out
89+ return $ T2TMeta curDate curMtime (intercalate ", " inps) outp
8390
8491-- | Read Txt2Tags from an input string returning a Pandoc document
85−readTxt2Tags :: T2TMeta -> ReaderOptions -> String -> Either PandocError Pandoc
86−readTxt2Tags t opts s = flip runReader t $ readWithM parseT2T (def {stateOptions = opts}) (s ++ "\n\n")
92+readTxt2Tags :: PandocMonad m
93+ => ReaderOptions
94+ -> String
95+ -> m Pandoc
96+readTxt2Tags opts s = do
97+ meta <- getT2TMeta
98+ let parsed = flip runReader meta $ readWithM parseT2T (def {stateOptions = opts}) (s ++ "\n\n")
99+ case parsed of
100+ Right result -> return $ result
101+ Left e -> throwError e
87102
88103-- | Read Txt2Tags (ignoring all macros) from an input string returning
89104-- a Pandoc document
90−readTxt2TagsNoMacros :: ReaderOptions -> String -> Either PandocError Pandoc
91−readTxt2TagsNoMacros = readTxt2Tags def
105+-- readTxt2TagsNoMacros :: PandocMonad m => ReaderOptions -> String -> m Pandoc
106+-- readTxt2TagsNoMacros = readTxt2Tags
92107
93108parseT2T :: T2T Pandoc
94109parseT2T = do
@@ -210,16 +225,16 @@ list :: T2T Blocks
210225list = choice [bulletList, orderedList, definitionList]
211226
212227bulletList :: T2T Blocks
213−bulletList = B.bulletList . compactify'
228+bulletList = B.bulletList . compactify
214229 <$> many1 (listItem bulletListStart parseBlocks)
215230
216231orderedList :: T2T Blocks
217−orderedList = B.orderedList . compactify'
232+orderedList = B.orderedList . compactify
218233 <$> many1 (listItem orderedListStart parseBlocks)
219234
220235definitionList :: T2T Blocks
221236definitionList = try $ do
222− B.definitionList . compactify'DL <$>
237+ B.definitionList . compactifyDL <$>
223238 many1 (listItem definitionListStart definitionListEnd)
224239
225240definitionListEnd :: T2T (Inlines, [Blocks])
@@ -432,9 +447,13 @@ inlineMarkup p f c special = try $ do
432447 lastChar <- anyChar
433448 end <- many1 (char c)
434449 let parser inp = parseFromString (mconcat <$> many p) inp
435− let start' = special (drop 2 start)
450+ let start' = case drop 2 start of
451+ "" -> mempty
452+ xs -> special xs
436453 body' <- parser (middle ++ [lastChar])
437− let end' = special (drop 2 end)
454+ let end' = case drop 2 end of
455+ "" -> mempty
456+ xs -> special xs
438457 return $ f (start' <> body' <> end')
439458 Nothing -> do -- Either bad or case such as *****
440459 guard (l >= 5)
src/Text/Pandoc/SelfContained.hs+8−5
@@ -40,7 +40,8 @@ import System.FilePath (takeExtension, takeDirectory, (</>))
4040import Data.Char (toLower, isAscii, isAlphaNum)
4141import Codec.Compression.GZip as Gzip
4242import qualified Data.ByteString.Lazy as L
43−import Text.Pandoc.Shared (renderTags', err, fetchItem', warn, trim)
43+import Control.Monad.Trans (MonadIO(..))
44+import Text.Pandoc.Shared (renderTags', err, warn, trim)
4445import Text.Pandoc.MediaBag (MediaBag)
4546import Text.Pandoc.MIME (MimeType)
4647import Text.Pandoc.UTF8 (toString)
@@ -50,6 +51,7 @@ import Control.Applicative ((<|>))
5051import Text.Parsec (runParserT, ParsecT)
5152import qualified Text.Parsec as P
5253import Control.Monad.Trans (lift)
54+import Text.Pandoc.Class (fetchItem, runIO, setMediaBag)
5355
5456isOk :: Char -> Bool
5557isOk c = isAscii c && isAlphaNum c
@@ -143,7 +145,8 @@ getDataURI :: MediaBag -> Maybe String -> MimeType -> String
143145getDataURI _ _ _ src@('d':'a':'t':'a':':':_) = return src -- already data: uri
144146getDataURI media sourceURL mimetype src = do
145147 let ext = map toLower $ takeExtension src
146− fetchResult <- fetchItem' media sourceURL src
148+ fetchResult <- runIO $ do setMediaBag media
149+ fetchItem sourceURL src
147150 (raw, respMime) <- case fetchResult of
148151 Left msg -> err 67 $ "Could not fetch " ++ src ++
149152 "\n" ++ show msg
@@ -171,8 +174,8 @@ getDataURI media sourceURL mimetype src = do
171174
172175-- | Convert HTML into self-contained HTML, incorporating images,
173176-- scripts, and CSS using data: URIs.
174−makeSelfContained :: WriterOptions -> String -> IO String
175−makeSelfContained opts inp = do
177+makeSelfContained :: MonadIO m => WriterOptions -> MediaBag -> String -> m String
178+makeSelfContained opts mediabag inp = liftIO $ do
176179 let tags = parseTags inp
177− out' <- mapM (convertTag (writerMediaBag opts) (writerSourceURL opts)) tags
180+ out' <- mapM (convertTag mediabag (writerSourceURL opts)) tags
178181 return $ renderTags' out'
src/Text/Pandoc/Shared.hs+19−256
@@ -55,15 +55,12 @@ module Text.Pandoc.Shared (
5555 orderedListMarkers,
5656 normalizeSpaces,
5757 extractSpaces,
58− normalize,
59− normalizeInlines,
60− normalizeBlocks,
6158 removeFormatting,
59+ deNote,
6260 stringify,
6361 capitalize,
6462 compactify,
65− compactify',
66− compactify'DL,
63+ compactifyDL,
6764 linesToPara,
6865 Element (..),
6966 hierarchicalize,
@@ -82,8 +79,6 @@ module Text.Pandoc.Shared (
8279 getDefaultReferenceODT,
8380 readDataFile,
8481 readDataFileUTF8,
85− fetchItem,
86− fetchItem',
8782 openURL,
8883 collapseFilePath,
8984 filteredFilesFromArchive,
@@ -91,7 +86,6 @@ module Text.Pandoc.Shared (
9186 err,
9287 warn,
9388 mapLeft,
94− hush,
9589 -- * for squashing blocks
9690 blocksToInlines,
9791 -- * Safe read
@@ -104,11 +98,9 @@ module Text.Pandoc.Shared (
10498
10599import Text.Pandoc.Definition
106100import Text.Pandoc.Walk
107−import Text.Pandoc.MediaBag (MediaBag, lookupMedia)
108101import Text.Pandoc.Builder (Inlines, Blocks, ToMetaValue(..))
109102import qualified Text.Pandoc.Builder as B
110103import qualified Text.Pandoc.UTF8 as UTF8
111−import System.Environment (getProgName)
112104import System.Exit (exitWith, ExitCode(..))
113105import Data.Char ( toLower, isLower, isUpper, isAlpha,
114106 isLetter, isDigit, isSpace )
@@ -116,15 +108,13 @@ import Data.List ( find, stripPrefix, intercalate )
116108import Data.Maybe (mapMaybe)
117109import Data.Version ( showVersion )
118110import qualified Data.Map as M
119−import Network.URI ( escapeURIString, nonStrictRelativeTo,
120− unEscapeString, parseURIReference, isAllowedInURI,
121− parseURI, URI(..) )
111+import Network.URI ( escapeURIString, unEscapeString )
122112import qualified Data.Set as Set
123113import System.Directory
124114import System.FilePath (splitDirectories, isPathSeparator)
125115import qualified System.FilePath.Posix as Posix
126−import Text.Pandoc.MIME (MimeType, getMimeType)
127−import System.FilePath ( (</>), takeExtension, dropExtension)
116+import Text.Pandoc.MIME (MimeType)
117+import System.FilePath ( (</>) )
128118import Data.Generics (Typeable, Data)
129119import qualified Control.Monad.State as S
130120import Control.Monad.Trans (MonadIO (..))
@@ -399,153 +389,6 @@ extractSpaces f is =
399389 _ -> mempty in
400390 (left <> f (B.trimInlines . B.Many $ contents) <> right)
401391
402−-- | Normalize @Pandoc@ document, consolidating doubled 'Space's,
403−-- combining adjacent 'Str's and 'Emph's, remove 'Null's and
404−-- empty elements, etc.
405−normalize :: Pandoc -> Pandoc
406−normalize (Pandoc (Meta meta) blocks) =
407− Pandoc (Meta $ M.map go meta) (normalizeBlocks blocks)
408− where go (MetaInlines xs) = MetaInlines $ normalizeInlines xs
409− go (MetaBlocks xs) = MetaBlocks $ normalizeBlocks xs
410− go (MetaList ms) = MetaList $ map go ms
411− go (MetaMap m) = MetaMap $ M.map go m
412− go x = x
413−
414−normalizeBlocks :: [Block] -> [Block]
415−normalizeBlocks (Null : xs) = normalizeBlocks xs
416−normalizeBlocks (Div attr bs : xs) =
417− Div attr (normalizeBlocks bs) : normalizeBlocks xs
418−normalizeBlocks (BlockQuote bs : xs) =
419− case normalizeBlocks bs of
420− [] -> normalizeBlocks xs
421− bs' -> BlockQuote bs' : normalizeBlocks xs
422−normalizeBlocks (BulletList [] : xs) = normalizeBlocks xs
423−normalizeBlocks (BulletList items : xs) =
424− BulletList (map normalizeBlocks items) : normalizeBlocks xs
425−normalizeBlocks (OrderedList _ [] : xs) = normalizeBlocks xs
426−normalizeBlocks (OrderedList attr items : xs) =
427− OrderedList attr (map normalizeBlocks items) : normalizeBlocks xs
428−normalizeBlocks (DefinitionList [] : xs) = normalizeBlocks xs
429−normalizeBlocks (DefinitionList items : xs) =
430− DefinitionList (map go items) : normalizeBlocks xs
431− where go (ils, bs) = (normalizeInlines ils, map normalizeBlocks bs)
432−normalizeBlocks (RawBlock _ "" : xs) = normalizeBlocks xs
433−normalizeBlocks (RawBlock f x : xs) =
434− case normalizeBlocks xs of
435− (RawBlock f' x' : rest) | f' == f ->
436− RawBlock f (x ++ ('\n':x')) : rest
437− rest -> RawBlock f x : rest
438−normalizeBlocks (Para ils : xs) =
439− case normalizeInlines ils of
440− [] -> normalizeBlocks xs
441− ils' -> Para ils' : normalizeBlocks xs
442−normalizeBlocks (Plain ils : xs) =
443− case normalizeInlines ils of
444− [] -> normalizeBlocks xs
445− ils' -> Plain ils' : normalizeBlocks xs
446−normalizeBlocks (Header lev attr ils : xs) =
447− Header lev attr (normalizeInlines ils) : normalizeBlocks xs
448−normalizeBlocks (Table capt aligns widths hdrs rows : xs) =
449− Table (normalizeInlines capt) aligns widths
450− (map normalizeBlocks hdrs) (map (map normalizeBlocks) rows)
451− : normalizeBlocks xs
452−normalizeBlocks (x:xs) = x : normalizeBlocks xs
453−normalizeBlocks [] = []
454−
455−normalizeInlines :: [Inline] -> [Inline]
456−normalizeInlines (Str x : ys) =
457− case concat (x : map fromStr strs) of
458− "" -> rest
459− n -> Str n : rest
460− where
461− (strs, rest) = span isStr $ normalizeInlines ys
462− isStr (Str _) = True
463− isStr _ = False
464− fromStr (Str z) = z
465− fromStr _ = error "normalizeInlines - fromStr - not a Str"
466−normalizeInlines (Space : SoftBreak : ys) =
467− SoftBreak : normalizeInlines ys
468−normalizeInlines (Space : ys) =
469− if null rest
470− then []
471− else Space : rest
472− where isSp Space = True
473− isSp _ = False
474− rest = dropWhile isSp $ normalizeInlines ys
475−normalizeInlines (Emph xs : zs) =
476− case normalizeInlines zs of
477− (Emph ys : rest) -> normalizeInlines $
478− Emph (normalizeInlines $ xs ++ ys) : rest
479− rest -> case normalizeInlines xs of
480− [] -> rest
481− xs' -> Emph xs' : rest
482−normalizeInlines (Strong xs : zs) =
483− case normalizeInlines zs of
484− (Strong ys : rest) -> normalizeInlines $
485− Strong (normalizeInlines $ xs ++ ys) : rest
486− rest -> case normalizeInlines xs of
487− [] -> rest
488− xs' -> Strong xs' : rest
489−normalizeInlines (Subscript xs : zs) =
490− case normalizeInlines zs of
491− (Subscript ys : rest) -> normalizeInlines $
492− Subscript (normalizeInlines $ xs ++ ys) : rest
493− rest -> case normalizeInlines xs of
494− [] -> rest
495− xs' -> Subscript xs' : rest
496−normalizeInlines (Superscript xs : zs) =
497− case normalizeInlines zs of
498− (Superscript ys : rest) -> normalizeInlines $
499− Superscript (normalizeInlines $ xs ++ ys) : rest
500− rest -> case normalizeInlines xs of
501− [] -> rest
502− xs' -> Superscript xs' : rest
503−normalizeInlines (SmallCaps xs : zs) =
504− case normalizeInlines zs of
505− (SmallCaps ys : rest) -> normalizeInlines $
506− SmallCaps (normalizeInlines $ xs ++ ys) : rest
507− rest -> case normalizeInlines xs of
508− [] -> rest
509− xs' -> SmallCaps xs' : rest
510−normalizeInlines (Strikeout xs : zs) =
511− case normalizeInlines zs of
512− (Strikeout ys : rest) -> normalizeInlines $
513− Strikeout (normalizeInlines $ xs ++ ys) : rest
514− rest -> case normalizeInlines xs of
515− [] -> rest
516− xs' -> Strikeout xs' : rest
517−normalizeInlines (RawInline _ [] : ys) = normalizeInlines ys
518−normalizeInlines (RawInline f xs : zs) =
519− case normalizeInlines zs of
520− (RawInline f' ys : rest) | f == f' -> normalizeInlines $
521− RawInline f (xs ++ ys) : rest
522− rest -> RawInline f xs : rest
523−normalizeInlines (Code _ "" : ys) = normalizeInlines ys
524−normalizeInlines (Code attr xs : zs) =
525− case normalizeInlines zs of
526− (Code attr' ys : rest) | attr == attr' -> normalizeInlines $
527− Code attr (xs ++ ys) : rest
528− rest -> Code attr xs : rest
529−-- allow empty spans, they may carry identifiers etc.
530−-- normalizeInlines (Span _ [] : ys) = normalizeInlines ys
531−normalizeInlines (Span attr xs : zs) =
532− case normalizeInlines zs of
533− (Span attr' ys : rest) | attr == attr' -> normalizeInlines $
534− Span attr (normalizeInlines $ xs ++ ys) : rest
535− rest -> Span attr (normalizeInlines xs) : rest
536−normalizeInlines (Note bs : ys) = Note (normalizeBlocks bs) :
537− normalizeInlines ys
538−normalizeInlines (Quoted qt ils : ys) =
539− Quoted qt (normalizeInlines ils) : normalizeInlines ys
540−normalizeInlines (Link attr ils t : ys) =
541− Link attr (normalizeInlines ils) t : normalizeInlines ys
542−normalizeInlines (Image attr ils t : ys) =
543− Image attr (normalizeInlines ils) t : normalizeInlines ys
544−normalizeInlines (Cite cs ils : ys) =
545− Cite cs (normalizeInlines ils) : normalizeInlines ys
546−normalizeInlines (x : xs) = x : normalizeInlines xs
547−normalizeInlines [] = []
548−
549392-- | Extract inlines, removing formatting.
550393removeFormatting :: Walkable Inline a => a -> [Inline]
551394removeFormatting = query go . walk deNote
@@ -557,8 +400,10 @@ removeFormatting = query go . walk deNote
557400 go (Math _ x) = [Str x]
558401 go LineBreak = [Space]
559402 go _ = []
560− deNote (Note _) = Str ""
561− deNote x = x
403+
404+deNote :: Inline -> Inline
405+deNote (Note _) = Str ""
406+deNote x = x
562407
563408-- | Convert pandoc structure to a string with formatting removed.
564409-- Footnotes are skipped (since we don't want their contents in link
@@ -574,8 +419,6 @@ stringify = query go . walk deNote
574419 go (RawInline (Format "html") ('<':'b':'r':_)) = " " -- see #2105
575420 go LineBreak = " "
576421 go _ = ""
577− deNote (Note _) = Str ""
578− deNote x = x
579422
580423-- | Bring all regular text in a pandoc structure to uppercase.
581424--
@@ -588,29 +431,13 @@ capitalize = walk go
588431 go (Str s) = Str (T.unpack $ T.toUpper $ T.pack s)
589432 go x = x
590433
591−-- | Change final list item from @Para@ to @Plain@ if the list contains
592−-- no other @Para@ blocks.
593−compactify :: [[Block]] -- ^ List of list items (each a list of blocks)
594− -> [[Block]]
595−compactify [] = []
596−compactify items =
597− case (init items, last items) of
598− (_,[]) -> items
599− (others, final) ->
600− case last final of
601− Para a -> case (filter isPara $ concat items) of
602− -- if this is only Para, change to Plain
603− [_] -> others ++ [init final ++ [Plain a]]
604− _ -> items
605− _ -> items
606−
607434-- | Change final list item from @Para@ to @Plain@ if the list contains
608435-- no other @Para@ blocks. Like compactify, but operates on @Blocks@ rather
609436-- than @[Block]@.
610−compactify' :: [Blocks] -- ^ List of list items (each a list of blocks)
437+compactify :: [Blocks] -- ^ List of list items (each a list of blocks)
611438 -> [Blocks]
612−compactify' [] = []
613−compactify' items =
439+compactify [] = []
440+compactify items =
614441 let (others, final) = (init items, last items)
615442 in case reverse (B.toList final) of
616443 (Para a:xs) -> case [Para x | Para x <- concatMap B.toList items] of
@@ -619,9 +446,9 @@ compactify' items =
619446 _ -> items
620447 _ -> items
621448
622−-- | Like @compactify'@, but acts on items of definition lists.
623−compactify'DL :: [(Inlines, [Blocks])] -> [(Inlines, [Blocks])]
624−compactify'DL items =
449+-- | Like @compactify@, but acts on items of definition lists.
450+compactifyDL :: [(Inlines, [Blocks])] -> [(Inlines, [Blocks])]
451+compactifyDL items =
625452 let defs = concatMap snd items
626453 in case reverse (concatMap B.toList defs) of
627454 (Para x:xs)
@@ -904,64 +731,6 @@ readDataFileUTF8 :: Maybe FilePath -> FilePath -> IO String
904731readDataFileUTF8 userDir fname =
905732 UTF8.toString `fmap` readDataFile userDir fname
906733
907−-- | Specialized version of parseURIReference that disallows
908−-- single-letter schemes. Reason: these are usually windows absolute
909−-- paths.
910−parseURIReference' :: String -> Maybe URI
911−parseURIReference' s =
912− case parseURIReference s of
913− Just u
914− | length (uriScheme u) > 2 -> Just u
915− | null (uriScheme u) -> Just u -- protocol-relative
916− _ -> Nothing
917−
918−-- | Fetch an image or other item from the local filesystem or the net.
919−-- Returns raw content and maybe mime type.
920−fetchItem :: Maybe String -> String
921− -> IO (Either E.SomeException (BS.ByteString, Maybe MimeType))
922−fetchItem sourceURL s =
923− case (sourceURL >>= parseURIReference' . ensureEscaped, ensureEscaped s) of
924− (Just u, s') -> -- try fetching from relative path at source
925− case parseURIReference' s' of
926− Just u' -> openURL $ show $ u' `nonStrictRelativeTo` u
927− Nothing -> openURL s' -- will throw error
928− (Nothing, s'@('/':'/':_)) -> -- protocol-relative URI
929− case parseURIReference' s' of
930− Just u' -> openURL $ show $ u' `nonStrictRelativeTo` httpcolon
931− Nothing -> openURL s' -- will throw error
932− (Nothing, s') ->
933− case parseURI s' of -- requires absolute URI
934− -- We don't want to treat C:/ as a scheme:
935− Just u' | length (uriScheme u') > 2 -> openURL (show u')
936− Just u' | uriScheme u' == "file:" ->
937− E.try $ readLocalFile $ dropWhile (=='/') (uriPath u')
938− _ -> E.try $ readLocalFile fp -- get from local file system
939− where readLocalFile f = do
940− cont <- BS.readFile f
941− return (cont, mime)
942− httpcolon = URI{ uriScheme = "http:",
943− uriAuthority = Nothing,
944− uriPath = "",
945− uriQuery = "",
946− uriFragment = "" }
947− dropFragmentAndQuery = takeWhile (\c -> c /= '?' && c /= '#')
948− fp = unEscapeString $ dropFragmentAndQuery s
949− mime = case takeExtension fp of
950− ".gz" -> getMimeType $ dropExtension fp
951− ".svgz" -> getMimeType $ dropExtension fp ++ ".svg"
952− x -> getMimeType x
953− ensureEscaped = escapeURIString isAllowedInURI . map convertSlash
954− convertSlash '\\' = '/'
955− convertSlash x = x
956−
957−-- | Like 'fetchItem', but also looks for items in a 'MediaBag'.
958−fetchItem' :: MediaBag -> Maybe String -> String
959− -> IO (Either E.SomeException (BS.ByteString, Maybe MimeType))
960−fetchItem' media sourceURL s = do
961− case lookupMedia s media of
962− Nothing -> fetchItem sourceURL s
963− Just (mime, bs) -> return $ Right (BS.concat $ toChunks bs, Just mime)
964−
965734-- | Read from a URL and return raw data and maybe mime type.
966735openURL :: String -> IO (Either E.SomeException (BS.ByteString, Maybe MimeType))
967736openURL u
@@ -1000,26 +769,20 @@ openURL u
1000769-- Error reporting
1001770--
1002771
1003−err :: Int -> String -> IO a
1004−err exitCode msg = do
1005− name <- getProgName
1006− UTF8.hPutStrLn stderr $ name ++ ": " ++ msg
772+err :: MonadIO m => Int -> String -> m a
773+err exitCode msg = liftIO $ do
774+ UTF8.hPutStrLn stderr msg
1007775 exitWith $ ExitFailure exitCode
1008776 return undefined
1009777
1010778warn :: MonadIO m => String -> m ()
1011779warn msg = liftIO $ do
1012− name <- getProgName
1013− UTF8.hPutStrLn stderr $ "[" ++ name ++ " warning] " ++ msg
780+ UTF8.hPutStrLn stderr $ "[warning] " ++ msg
1014781
1015782mapLeft :: (a -> b) -> Either a c -> Either b c
1016783mapLeft f (Left x) = Left (f x)
1017784mapLeft _ (Right x) = Right x
1018785
1019−hush :: Either a b -> Maybe b
1020−hush (Left _) = Nothing
1021−hush (Right x) = Just x
1022−
1023786-- | Remove intermediate "." and ".." directories from a path.
1024787--
1025788-- > collapseFilePath "./foo" == "foo"
src/Text/Pandoc/Templates.hs+8−5
@@ -55,11 +55,14 @@ getDefaultTemplate :: (Maybe FilePath) -- ^ User data directory to search first
5555getDefaultTemplate user writer = do
5656 let format = takeWhile (`notElem` ("+-" :: String)) writer -- strip off extensions
5757 case format of
58− "native" -> return $ Right ""
59− "json" -> return $ Right ""
60− "docx" -> return $ Right ""
61− "fb2" -> return $ Right ""
62− "odt" -> getDefaultTemplate user "opendocument"
58+ "native" -> return $ Right ""
59+ "json" -> return $ Right ""
60+ "docx" -> return $ Right ""
61+ "fb2" -> return $ Right ""
62+ "odt" -> getDefaultTemplate user "opendocument"
63+ "html" -> getDefaultTemplate user "html5"
64+ "docbook" -> getDefaultTemplate user "docbook5"
65+ "epub" -> getDefaultTemplate user "epub2"
6366 "markdown_strict" -> getDefaultTemplate user "markdown"
6467 "multimarkdown" -> getDefaultTemplate user "markdown"
6568 "markdown_github" -> getDefaultTemplate user "markdown"
src/Text/Pandoc/UUID.hs+12−11
@@ -29,13 +29,12 @@ UUID generation using Version 4 (random method) described
2929in RFC4122. See http://tools.ietf.org/html/rfc4122
3030-}
3131
32−module Text.Pandoc.UUID ( UUID, getRandomUUID ) where
32+module Text.Pandoc.UUID ( UUID(..), getRandomUUID, getUUID ) where
3333
3434import Text.Printf ( printf )
35−import System.Random ( randomIO )
35+import System.Random ( RandomGen, randoms, getStdGen )
3636import Data.Word
3737import Data.Bits ( setBit, clearBit )
38−import Control.Monad ( liftM )
3938
4039data UUID = UUID Word8 Word8 Word8 Word8 Word8 Word8 Word8 Word8
4140 Word8 Word8 Word8 Word8 Word8 Word8 Word8 Word8
@@ -64,14 +63,16 @@ instance Show UUID where
6463 printf "%02x" o ++
6564 printf "%02x" p
6665
67−getRandomUUID :: IO UUID
68−getRandomUUID = do
69− let getRN :: a -> IO Word8
70− getRN _ = liftM fromIntegral (randomIO :: IO Int)
71− [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p] <- mapM getRN ([1..16] :: [Int])
66+getUUID :: RandomGen g => g -> UUID
67+getUUID gen =
68+ let [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p] = take 16 $ randoms gen :: [Word8]
7269 -- set variant
73− let i' = i `setBit` 7 `clearBit` 6
70+ i' = i `setBit` 7 `clearBit` 6
7471 -- set version (0100 for random)
75− let g' = g `clearBit` 7 `setBit` 6 `clearBit` 5 `clearBit` 4
76− return $ UUID a b c d e f g' h i' j k l m n o p
72+ g' = g `clearBit` 7 `setBit` 6 `clearBit` 5 `clearBit` 4
73+ in
74+ UUID a b c d e f g' h i' j k l m n o p
75+
76+getRandomUUID :: IO UUID
77+getRandomUUID = getUUID <$> getStdGen
7778
src/Text/Pandoc/Writers/AsciiDoc.hs+4−3
@@ -52,6 +52,7 @@ import qualified Data.Map as M
5252import Data.Aeson (Value(String), fromJSON, toJSON, Result(..))
5353import qualified Data.Text as T
5454import Data.Char (isSpace, isPunctuation)
55+import Text.Pandoc.Class (PandocMonad)
5556
5657data WriterState = WriterState { defListMarker :: String
5758 , orderedListLevel :: Int
@@ -60,8 +61,8 @@ data WriterState = WriterState { defListMarker :: String
6061 }
6162
6263-- | Convert Pandoc to AsciiDoc.
63−writeAsciiDoc :: WriterOptions -> Pandoc -> String
64−writeAsciiDoc opts document =
64+writeAsciiDoc :: PandocMonad m => WriterOptions -> Pandoc -> m String
65+writeAsciiDoc opts document = return $
6566 evalState (pandocToAsciiDoc opts document) WriterState{
6667 defListMarker = "::"
6768 , orderedListLevel = 1
@@ -411,7 +412,7 @@ inlineToAsciiDoc _ (Math DisplayMath str) =
411412inlineToAsciiDoc _ (RawInline f s)
412413 | f == "asciidoc" = return $ text s
413414 | otherwise = return empty
414−inlineToAsciiDoc _ (LineBreak) = return $ " +" <> cr
415+inlineToAsciiDoc _ LineBreak = return $ " +" <> cr
415416inlineToAsciiDoc _ Space = return space
416417inlineToAsciiDoc opts SoftBreak =
417418 case writerWrapText opts of
src/Text/Pandoc/Writers/CommonMark.hs+73−63
@@ -31,7 +31,7 @@ CommonMark: <http://commonmark.org>
3131-}
3232module Text.Pandoc.Writers.CommonMark (writeCommonMark) where
3333
34−import Text.Pandoc.Writers.HTML (writeHtmlString)
34+import Text.Pandoc.Writers.HTML (writeHtml5String)
3535import Text.Pandoc.Definition
3636import Text.Pandoc.Shared (isTightList, linesToPara)
3737import Text.Pandoc.Templates (renderTemplate')
@@ -39,26 +39,27 @@ import Text.Pandoc.Writers.Shared
3939import Text.Pandoc.Options
4040import CMark
4141import qualified Data.Text as T
42−import Control.Monad.Identity (runIdentity, Identity)
4342import Control.Monad.State (runState, State, modify, get)
4443import Text.Pandoc.Walk (walkM)
44+import Text.Pandoc.Class (PandocMonad)
45+import Data.Foldable (foldrM)
4546
4647-- | Convert Pandoc to CommonMark.
47−writeCommonMark :: WriterOptions -> Pandoc -> String
48−writeCommonMark opts (Pandoc meta blocks) = rendered
49− where main = runIdentity $ blocksToCommonMark opts (blocks' ++ notes')
50− (blocks', notes) = runState (walkM processNotes blocks) []
51− notes' = if null notes
52− then []
53− else [OrderedList (1, Decimal, Period) $ reverse notes]
54− metadata = runIdentity $ metaToJSON opts
55− (blocksToCommonMark opts)
56− (inlinesToCommonMark opts)
57− meta
58− context = defField "body" main $ metadata
59− rendered = case writerTemplate opts of
60− Nothing -> main
61− Just tpl -> renderTemplate' tpl context
48+writeCommonMark :: PandocMonad m => WriterOptions -> Pandoc -> m String
49+writeCommonMark opts (Pandoc meta blocks) = do
50+ let (blocks', notes) = runState (walkM processNotes blocks) []
51+ notes' = if null notes
52+ then []
53+ else [OrderedList (1, Decimal, Period) $ reverse notes]
54+ main <- blocksToCommonMark opts (blocks' ++ notes')
55+ metadata <- metaToJSON opts
56+ (blocksToCommonMark opts)
57+ (inlinesToCommonMark opts)
58+ meta
59+ let context = defField "body" main $ metadata
60+ return $ case writerTemplate opts of
61+ Nothing -> main
62+ Just tpl -> renderTemplate' tpl context
6263
6364processNotes :: Inline -> State [[Block]] Inline
6465processNotes (Note bs) = do
@@ -70,16 +71,19 @@ processNotes x = return x
7071node :: NodeType -> [Node] -> Node
7172node = Node Nothing
7273
73−blocksToCommonMark :: WriterOptions -> [Block] -> Identity String
74−blocksToCommonMark opts bs = return $
75− T.unpack $ nodeToCommonmark cmarkOpts colwidth
76− $ node DOCUMENT (blocksToNodes bs)
77− where cmarkOpts = [optHardBreaks | isEnabled Ext_hard_line_breaks opts]
78− colwidth = if writerWrapText opts == WrapAuto
79− then Just $ writerColumns opts
80− else Nothing
81−
82−inlinesToCommonMark :: WriterOptions -> [Inline] -> Identity String
74+blocksToCommonMark :: PandocMonad m => WriterOptions -> [Block] -> m String
75+blocksToCommonMark opts bs = do
76+ let cmarkOpts = [optHardBreaks | isEnabled Ext_hard_line_breaks opts]
77+ colwidth = if writerWrapText opts == WrapAuto
78+ then Just $ writerColumns opts
79+ else Nothing
80+ nodes <- blocksToNodes bs
81+ return $
82+ T.unpack $
83+ nodeToCommonmark cmarkOpts colwidth $
84+ node DOCUMENT nodes
85+
86+inlinesToCommonMark :: PandocMonad m => WriterOptions -> [Inline] -> m String
8387inlinesToCommonMark opts ils = return $
8488 T.unpack $ nodeToCommonmark cmarkOpts colwidth
8589 $ node PARAGRAPH (inlinesToNodes ils)
@@ -88,39 +92,44 @@ inlinesToCommonMark opts ils = return $
8892 then Just $ writerColumns opts
8993 else Nothing
9094
91−blocksToNodes :: [Block] -> [Node]
92−blocksToNodes = foldr blockToNodes []
93−
94−blockToNodes :: Block -> [Node] -> [Node]
95−blockToNodes (Plain xs) = (node PARAGRAPH (inlinesToNodes xs) :)
96−blockToNodes (Para xs) = (node PARAGRAPH (inlinesToNodes xs) :)
97−blockToNodes (LineBlock lns) = blockToNodes $ linesToPara lns
98−blockToNodes (CodeBlock (_,classes,_) xs) =
99− (node (CODE_BLOCK (T.pack (unwords classes)) (T.pack xs)) [] :)
100−blockToNodes (RawBlock fmt xs)
101− | fmt == Format "html" = (node (HTML_BLOCK (T.pack xs)) [] :)
102− | otherwise = (node (CUSTOM_BLOCK (T.pack xs) (T.empty)) [] :)
103−blockToNodes (BlockQuote bs) =
104− (node BLOCK_QUOTE (blocksToNodes bs) :)
105−blockToNodes (BulletList items) =
106− (node (LIST ListAttributes{
107− listType = BULLET_LIST,
108− listDelim = PERIOD_DELIM,
109− listTight = isTightList items,
110− listStart = 1 }) (map (node ITEM . blocksToNodes) items) :)
111−blockToNodes (OrderedList (start, _sty, delim) items) =
112− (node (LIST ListAttributes{
113− listType = ORDERED_LIST,
114− listDelim = case delim of
115− OneParen -> PAREN_DELIM
116− TwoParens -> PAREN_DELIM
117− _ -> PERIOD_DELIM,
118− listTight = isTightList items,
119− listStart = start }) (map (node ITEM . blocksToNodes) items) :)
120−blockToNodes HorizontalRule = (node THEMATIC_BREAK [] :)
121−blockToNodes (Header lev _ ils) = (node (HEADING lev) (inlinesToNodes ils) :)
122−blockToNodes (Div _ bs) = (blocksToNodes bs ++)
123−blockToNodes (DefinitionList items) = blockToNodes (BulletList items')
95+blocksToNodes :: PandocMonad m => [Block] -> m [Node]
96+blocksToNodes = foldrM blockToNodes []
97+
98+blockToNodes :: PandocMonad m => Block -> [Node] -> m [Node]
99+blockToNodes (Plain xs) ns = return (node PARAGRAPH (inlinesToNodes xs) : ns)
100+blockToNodes (Para xs) ns = return (node PARAGRAPH (inlinesToNodes xs) : ns)
101+blockToNodes (LineBlock lns) ns = blockToNodes (linesToPara lns) ns
102+blockToNodes (CodeBlock (_,classes,_) xs) ns = return $
103+ (node (CODE_BLOCK (T.pack (unwords classes)) (T.pack xs)) [] : ns)
104+blockToNodes (RawBlock fmt xs) ns
105+ | fmt == Format "html" = return (node (HTML_BLOCK (T.pack xs)) [] : ns)
106+ | otherwise = return (node (CUSTOM_BLOCK (T.pack xs) (T.empty)) [] : ns)
107+blockToNodes (BlockQuote bs) ns = do
108+ nodes <- blocksToNodes bs
109+ return (node BLOCK_QUOTE nodes : ns)
110+blockToNodes (BulletList items) ns = do
111+ nodes <- mapM blocksToNodes items
112+ return (node (LIST ListAttributes{
113+ listType = BULLET_LIST,
114+ listDelim = PERIOD_DELIM,
115+ listTight = isTightList items,
116+ listStart = 1 }) (map (node ITEM) nodes) : ns)
117+blockToNodes (OrderedList (start, _sty, delim) items) ns = do
118+ nodes <- mapM blocksToNodes items
119+ return (node (LIST ListAttributes{
120+ listType = ORDERED_LIST,
121+ listDelim = case delim of
122+ OneParen -> PAREN_DELIM
123+ TwoParens -> PAREN_DELIM
124+ _ -> PERIOD_DELIM,
125+ listTight = isTightList items,
126+ listStart = start }) (map (node ITEM) nodes) : ns)
127+blockToNodes HorizontalRule ns = return (node THEMATIC_BREAK [] : ns)
128+blockToNodes (Header lev _ ils) ns = return (node (HEADING lev) (inlinesToNodes ils) : ns)
129+blockToNodes (Div _ bs) ns = do
130+ nodes <- blocksToNodes bs
131+ return (nodes ++ ns)
132+blockToNodes (DefinitionList items) ns = blockToNodes (BulletList items') ns
124133 where items' = map dlToBullet items
125134 dlToBullet (term, ((Para xs : ys) : zs)) =
126135 Para (term ++ [LineBreak] ++ xs) : ys ++ concat zs
@@ -128,9 +137,10 @@ blockToNodes (DefinitionList items) = blockToNodes (BulletList items')
128137 Plain (term ++ [LineBreak] ++ xs) : ys ++ concat zs
129138 dlToBullet (term, xs) =
130139 Para term : concat xs
131−blockToNodes t@(Table _ _ _ _ _) =
132− (node (HTML_BLOCK (T.pack $! writeHtmlString def $! Pandoc nullMeta [t])) [] :)
133−blockToNodes Null = id
140+blockToNodes t@(Table _ _ _ _ _) ns = do
141+ s <- writeHtml5String def $! Pandoc nullMeta [t]
142+ return (node (HTML_BLOCK (T.pack $! s)) [] : ns)
143+blockToNodes Null ns = return ns
134144
135145inlinesToNodes :: [Inline] -> [Node]
136146inlinesToNodes = foldr inlineToNodes []
src/Text/Pandoc/Writers/ConTeXt.hs+4−3
@@ -43,6 +43,7 @@ import Text.Pandoc.Pretty
4343import Text.Pandoc.ImageSize
4444import Text.Pandoc.Templates ( renderTemplate' )
4545import Network.URI ( isURI, unEscapeString )
46+import Text.Pandoc.Class (PandocMonad)
4647
4748data WriterState =
4849 WriterState { stNextRef :: Int -- number of next URL reference
@@ -54,8 +55,8 @@ orderedListStyles :: [Char]
5455orderedListStyles = cycle "narg"
5556
5657-- | Convert Pandoc to ConTeXt.
57−writeConTeXt :: WriterOptions -> Pandoc -> String
58−writeConTeXt options document =
58+writeConTeXt :: PandocMonad m => WriterOptions -> Pandoc -> m String
59+writeConTeXt options document = return $
5960 let defaultWriterState = WriterState { stNextRef = 1
6061 , stOrderedListLevel = 0
6162 , stOptions = options
@@ -110,7 +111,7 @@ toContextDir _ = ""
110111-- | escape things as needed for ConTeXt
111112escapeCharForConTeXt :: WriterOptions -> Char -> String
112113escapeCharForConTeXt opts ch =
113− let ligatures = writerTeXLigatures opts in
114+ let ligatures = isEnabled Ext_smart opts in
114115 case ch of
115116 '{' -> "\\{"
116117 '}' -> "\\}"
src/Text/Pandoc/Writers/Docbook.hs+179−143
@@ -28,7 +28,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2828
2929Conversion of 'Pandoc' documents to Docbook XML.
3030-}
31−module Text.Pandoc.Writers.Docbook ( writeDocbook) where
31+module Text.Pandoc.Writers.Docbook ( writeDocbook4, writeDocbook5 ) where
3232import Text.Pandoc.Definition
3333import Text.Pandoc.XML
3434import Text.Pandoc.Shared
@@ -36,7 +36,7 @@ import Text.Pandoc.Walk
3636import Text.Pandoc.Writers.Shared
3737import Text.Pandoc.Options
3838import Text.Pandoc.Templates (renderTemplate')
39−import Text.Pandoc.Readers.TeXMath
39+import Text.Pandoc.Writers.Math
4040import Data.List ( stripPrefix, isPrefixOf, intercalate, isSuffixOf )
4141import Data.Char ( toLower )
4242import Data.Monoid ( Any(..) )
@@ -47,15 +47,22 @@ import qualified Text.Pandoc.Builder as B
4747import Text.TeXMath
4848import qualified Text.XML.Light as Xml
4949import Data.Generics (everywhere, mkT)
50+import Text.Pandoc.Class (PandocMonad)
51+import Control.Monad.Reader
52+
53+data DocBookVersion = DocBook4 | DocBook5
54+ deriving (Eq, Show)
55+
56+type DB = ReaderT DocBookVersion
5057
5158-- | Convert list of authors to a docbook <author> section
52−authorToDocbook :: WriterOptions -> [Inline] -> B.Inlines
53−authorToDocbook opts name' =
54− let name = render Nothing $ inlinesToDocbook opts name'
55− colwidth = if writerWrapText opts == WrapAuto
59+authorToDocbook :: PandocMonad m => WriterOptions -> [Inline] -> DB m B.Inlines
60+authorToDocbook opts name' = do
61+ name <- render Nothing <$> inlinesToDocbook opts name'
62+ let colwidth = if writerWrapText opts == WrapAuto
5663 then Just $ writerColumns opts
5764 else Nothing
58− in B.rawInline "docbook" $ render colwidth $
65+ return $ B.rawInline "docbook" $ render colwidth $
5966 if ',' `elem` name
6067 then -- last name first
6168 let (lastname, rest) = break (==',') name
@@ -72,46 +79,56 @@ authorToDocbook opts name' =
7279 in inTagsSimple "firstname" (text $ escapeStringForXML firstname) $$
7380 inTagsSimple "surname" (text $ escapeStringForXML lastname)
7481
82+writeDocbook4 :: PandocMonad m => WriterOptions -> Pandoc -> m String
83+writeDocbook4 opts d =
84+ runReaderT (writeDocbook opts d) DocBook4
85+
86+writeDocbook5 :: PandocMonad m => WriterOptions -> Pandoc -> m String
87+writeDocbook5 opts d =
88+ runReaderT (writeDocbook opts d) DocBook5
89+
7590-- | Convert Pandoc document to string in Docbook format.
76−writeDocbook :: WriterOptions -> Pandoc -> String
77−writeDocbook opts (Pandoc meta blocks) =
91+writeDocbook :: PandocMonad m => WriterOptions -> Pandoc -> DB m String
92+writeDocbook opts (Pandoc meta blocks) = do
7893 let elements = hierarchicalize blocks
79− colwidth = if writerWrapText opts == WrapAuto
94+ let colwidth = if writerWrapText opts == WrapAuto
8095 then Just $ writerColumns opts
8196 else Nothing
82− render' = render colwidth
83− opts' = if (maybe False (("/book>" `isSuffixOf`) . trimr)
97+ let render' = render colwidth
98+ let opts' = if (maybe False (("/book>" `isSuffixOf`) . trimr)
8499 (writerTemplate opts) &&
85100 TopLevelDefault == writerTopLevelDivision opts)
86101 then opts{ writerTopLevelDivision = TopLevelChapter }
87102 else opts
88− -- The numbering here follows LaTeX's internal numbering
89− startLvl = case writerTopLevelDivision opts' of
103+ -- The numbering here follows LaTeX's internal numbering
104+ let startLvl = case writerTopLevelDivision opts' of
90105 TopLevelPart -> -1
91106 TopLevelChapter -> 0
92107 TopLevelSection -> 1
93108 TopLevelDefault -> 1
94− auths' = map (authorToDocbook opts) $ docAuthors meta
95− meta' = B.setMeta "author" auths' meta
96− Just metadata = metaToJSON opts
97− (Just . render colwidth . (vcat .
98− (map (elementToDocbook opts' startLvl)) . hierarchicalize))
99− (Just . render colwidth . inlinesToDocbook opts')
109+ auths' <- mapM (authorToDocbook opts) $ docAuthors meta
110+ let meta' = B.setMeta "author" auths' meta
111+ metadata <- metaToJSON opts
112+ (fmap (render colwidth . vcat) .
113+ (mapM (elementToDocbook opts' startLvl) .
114+ hierarchicalize))
115+ (fmap (render colwidth) . inlinesToDocbook opts')
100116 meta'
101− main = render' $ vcat (map (elementToDocbook opts' startLvl) elements)
102− context = defField "body" main
117+ main <- (render' . vcat) <$> (mapM (elementToDocbook opts' startLvl) elements)
118+ let context = defField "body" main
103119 $ defField "mathml" (case writerHTMLMathMethod opts of
104120 MathML _ -> True
105121 _ -> False)
106122 $ metadata
107− in case writerTemplate opts of
123+ return $ case writerTemplate opts of
108124 Nothing -> main
109125 Just tpl -> renderTemplate' tpl context
110126
111127-- | Convert an Element to Docbook.
112−elementToDocbook :: WriterOptions -> Int -> Element -> Doc
128+elementToDocbook :: PandocMonad m => WriterOptions -> Int -> Element -> DB m Doc
113129elementToDocbook opts _ (Blk block) = blockToDocbook opts block
114−elementToDocbook opts lvl (Sec _ _num (id',_,_) title elements) =
130+elementToDocbook opts lvl (Sec _ _num (id',_,_) title elements) = do
131+ version <- ask
115132 -- Docbook doesn't allow sections with no content, so insert some if needed
116133 let elements' = if null elements
117134 then [Blk (Para [])]
@@ -119,24 +136,25 @@ elementToDocbook opts lvl (Sec _ _num (id',_,_) title elements) =
119136 tag = case lvl of
120137 -1 -> "part"
121138 0 -> "chapter"
122− n | n >= 1 && n <= 5 -> if writerDocbook5 opts
139+ n | n >= 1 && n <= 5 -> if version == DocBook5
123140 then "section"
124141 else "sect" ++ show n
125142 _ -> "simplesect"
126− idName = if writerDocbook5 opts
143+ idName = if version == DocBook5
127144 then "xml:id"
128145 else "id"
129146 idAttr = [(idName, writerIdentifierPrefix opts ++ id') | not (null id')]
130− nsAttr = if writerDocbook5 opts && lvl == 0 then [("xmlns", "http://docbook.org/ns/docbook"),("xmlns:xlink", "http://www.w3.org/1999/xlink")]
147+ nsAttr = if version == DocBook5 && lvl == 0 then [("xmlns", "http://docbook.org/ns/docbook"),("xmlns:xlink", "http://www.w3.org/1999/xlink")]
131148 else []
132149 attribs = nsAttr ++ idAttr
133− in inTags True tag attribs $
134− inTagsSimple "title" (inlinesToDocbook opts title) $$
135− vcat (map (elementToDocbook opts (lvl + 1)) elements')
150+ contents <- mapM (elementToDocbook opts (lvl + 1)) elements'
151+ title' <- inlinesToDocbook opts title
152+ return $ inTags True tag attribs $
153+ inTagsSimple "title" title' $$ vcat contents
136154
137155-- | Convert a list of Pandoc blocks to Docbook.
138−blocksToDocbook :: WriterOptions -> [Block] -> Doc
139−blocksToDocbook opts = vcat . map (blockToDocbook opts)
156+blocksToDocbook :: PandocMonad m => WriterOptions -> [Block] -> DB m Doc
157+blocksToDocbook opts = fmap vcat . mapM (blockToDocbook opts)
140158
141159-- | Auxiliary function to convert Plain block to Para.
142160plainToPara :: Block -> Block
@@ -145,26 +163,29 @@ plainToPara x = x
145163
146164-- | Convert a list of pairs of terms and definitions into a list of
147165-- Docbook varlistentrys.
148−deflistItemsToDocbook :: WriterOptions -> [([Inline],[[Block]])] -> Doc
166+deflistItemsToDocbook :: PandocMonad m
167+ => WriterOptions -> [([Inline],[[Block]])] -> DB m Doc
149168deflistItemsToDocbook opts items =
150− vcat $ map (\(term, defs) -> deflistItemToDocbook opts term defs) items
169+ vcat <$> mapM (\(term, defs) -> deflistItemToDocbook opts term defs) items
151170
152171-- | Convert a term and a list of blocks into a Docbook varlistentry.
153−deflistItemToDocbook :: WriterOptions -> [Inline] -> [[Block]] -> Doc
154−deflistItemToDocbook opts term defs =
155− let def' = concatMap (map plainToPara) defs
156− in inTagsIndented "varlistentry" $
157− inTagsIndented "term" (inlinesToDocbook opts term) $$
158− inTagsIndented "listitem" (blocksToDocbook opts def')
172+deflistItemToDocbook :: PandocMonad m
173+ => WriterOptions -> [Inline] -> [[Block]] -> DB m Doc
174+deflistItemToDocbook opts term defs = do
175+ term' <- inlinesToDocbook opts term
176+ def' <- blocksToDocbook opts $ concatMap (map plainToPara) defs
177+ return $ inTagsIndented "varlistentry" $
178+ inTagsIndented "term" term' $$
179+ inTagsIndented "listitem" def'
159180
160181-- | Convert a list of lists of blocks to a list of Docbook list items.
161−listItemsToDocbook :: WriterOptions -> [[Block]] -> Doc
162−listItemsToDocbook opts items = vcat $ map (listItemToDocbook opts) items
182+listItemsToDocbook :: PandocMonad m => WriterOptions -> [[Block]] -> DB m Doc
183+listItemsToDocbook opts items = vcat <$> mapM (listItemToDocbook opts) items
163184
164185-- | Convert a list of blocks into a Docbook list item.
165−listItemToDocbook :: WriterOptions -> [Block] -> Doc
186+listItemToDocbook :: PandocMonad m => WriterOptions -> [Block] -> DB m Doc
166187listItemToDocbook opts item =
167− inTagsIndented "listitem" $ blocksToDocbook opts $ map plainToPara item
188+ inTagsIndented "listitem" <$> blocksToDocbook opts (map plainToPara item)
168189
169190imageToDocbook :: WriterOptions -> Attr -> String -> Doc
170191imageToDocbook _ attr src = selfClosingTag "imagedata" $
@@ -176,43 +197,46 @@ imageToDocbook _ attr src = selfClosingTag "imagedata" $
176197 Nothing -> []
177198
178199-- | Convert a Pandoc block element to Docbook.
179−blockToDocbook :: WriterOptions -> Block -> Doc
180−blockToDocbook _ Null = empty
200+blockToDocbook :: PandocMonad m => WriterOptions -> Block -> DB m Doc
201+blockToDocbook _ Null = return empty
181202-- Add ids to paragraphs in divs with ids - this is needed for
182203-- pandoc-citeproc to get link anchors in bibliographies:
183204blockToDocbook opts (Div (ident,_,_) [Para lst]) =
184205 let attribs = [("id", ident) | not (null ident)] in
185206 if hasLineBreaks lst
186− then flush $ nowrap $ inTags False "literallayout" attribs
187− $ inlinesToDocbook opts lst
188− else inTags True "para" attribs $ inlinesToDocbook opts lst
189−blockToDocbook opts (Div (ident,_,_) bs) =
190− (if null ident
191− then mempty
192− else selfClosingTag "anchor" [("id", ident)]) $$
193− blocksToDocbook opts (map plainToPara bs)
194−blockToDocbook _ (Header _ _ _) = empty -- should not occur after hierarchicalize
207+ then (flush . nowrap . inTags False "literallayout" attribs)
208+ <$> inlinesToDocbook opts lst
209+ else inTags True "para" attribs <$> inlinesToDocbook opts lst
210+blockToDocbook opts (Div (ident,_,_) bs) = do
211+ contents <- blocksToDocbook opts (map plainToPara bs)
212+ return $
213+ (if null ident
214+ then mempty
215+ else selfClosingTag "anchor" [("id", ident)]) $$ contents
216+blockToDocbook _ (Header _ _ _) =
217+ return empty -- should not occur after hierarchicalize
195218blockToDocbook opts (Plain lst) = inlinesToDocbook opts lst
196219-- title beginning with fig: indicates that the image is a figure
197−blockToDocbook opts (Para [Image attr txt (src,'f':'i':'g':':':_)]) =
198− let alt = inlinesToDocbook opts txt
199− capt = if null txt
220+blockToDocbook opts (Para [Image attr txt (src,'f':'i':'g':':':_)]) = do
221+ alt <- inlinesToDocbook opts txt
222+ let capt = if null txt
200223 then empty
201224 else inTagsSimple "title" alt
202− in inTagsIndented "figure" $
225+ return $ inTagsIndented "figure" $
203226 capt $$
204227 (inTagsIndented "mediaobject" $
205228 (inTagsIndented "imageobject"
206229 (imageToDocbook opts attr src)) $$
207230 inTagsSimple "textobject" (inTagsSimple "phrase" alt))
208231blockToDocbook opts (Para lst)
209− | hasLineBreaks lst = flush $ nowrap $ inTagsSimple "literallayout" $ inlinesToDocbook opts lst
210− | otherwise = inTagsIndented "para" $ inlinesToDocbook opts lst
232+ | hasLineBreaks lst = (flush . nowrap . inTagsSimple "literallayout")
233+ <$> inlinesToDocbook opts lst
234+ | otherwise = inTagsIndented "para" <$> inlinesToDocbook opts lst
211235blockToDocbook opts (LineBlock lns) =
212236 blockToDocbook opts $ linesToPara lns
213237blockToDocbook opts (BlockQuote blocks) =
214− inTagsIndented "blockquote" $ blocksToDocbook opts blocks
215−blockToDocbook _ (CodeBlock (_,classes,_) str) =
238+ inTagsIndented "blockquote" <$> blocksToDocbook opts blocks
239+blockToDocbook _ (CodeBlock (_,classes,_) str) = return $
216240 text ("<programlisting" ++ lang ++ ">") <> cr <>
217241 flush (text (escapeStringForXML str) <> cr <> text "</programlisting>")
218242 where lang = if null langs
@@ -224,11 +248,11 @@ blockToDocbook _ (CodeBlock (_,classes,_) str) =
224248 then [s]
225249 else languagesByExtension . map toLower $ s
226250 langs = concatMap langsFrom classes
227−blockToDocbook opts (BulletList lst) =
251+blockToDocbook opts (BulletList lst) = do
228252 let attribs = [("spacing", "compact") | isTightList lst]
229− in inTags True "itemizedlist" attribs $ listItemsToDocbook opts lst
230−blockToDocbook _ (OrderedList _ []) = empty
231−blockToDocbook opts (OrderedList (start, numstyle, _) (first:rest)) =
253+ inTags True "itemizedlist" attribs <$> listItemsToDocbook opts lst
254+blockToDocbook _ (OrderedList _ []) = return empty
255+blockToDocbook opts (OrderedList (start, numstyle, _) (first:rest)) = do
232256 let numeration = case numstyle of
233257 DefaultStyle -> []
234258 Decimal -> [("numeration", "arabic")]
@@ -239,39 +263,43 @@ blockToDocbook opts (OrderedList (start, numstyle, _) (first:rest)) =
239263 LowerRoman -> [("numeration", "lowerroman")]
240264 spacing = [("spacing", "compact") | isTightList (first:rest)]
241265 attribs = numeration ++ spacing
242− items = if start == 1
243− then listItemsToDocbook opts (first:rest)
244− else (inTags True "listitem" [("override",show start)]
245− (blocksToDocbook opts $ map plainToPara first)) $$
246− listItemsToDocbook opts rest
247− in inTags True "orderedlist" attribs items
248−blockToDocbook opts (DefinitionList lst) =
266+ items <- if start == 1
267+ then listItemsToDocbook opts (first:rest)
268+ else do
269+ first' <- blocksToDocbook opts (map plainToPara first)
270+ rest' <- listItemsToDocbook opts rest
271+ return $
272+ (inTags True "listitem" [("override",show start)] first') $$
273+ rest'
274+ return $ inTags True "orderedlist" attribs items
275+blockToDocbook opts (DefinitionList lst) = do
249276 let attribs = [("spacing", "compact") | isTightList $ concatMap snd lst]
250− in inTags True "variablelist" attribs $ deflistItemsToDocbook opts lst
251−blockToDocbook opts (RawBlock f str)
252− | f == "docbook" = text str -- raw XML block
253− | f == "html" = if writerDocbook5 opts
254− then empty -- No html in Docbook5
255− else text str -- allow html for backwards compatibility
256− | otherwise = empty
257−blockToDocbook _ HorizontalRule = empty -- not semantic
258−blockToDocbook opts (Table caption aligns widths headers rows) =
259− let captionDoc = if null caption
260− then empty
261− else inTagsIndented "title"
262− (inlinesToDocbook opts caption)
263− tableType = if isEmpty captionDoc then "informaltable" else "table"
277+ inTags True "variablelist" attribs <$> deflistItemsToDocbook opts lst
278+blockToDocbook _ (RawBlock f str)
279+ | f == "docbook" = return $ text str -- raw XML block
280+ | f == "html" = do
281+ version <- ask
282+ if version == DocBook5
283+ then return empty -- No html in Docbook5
284+ else return $ text str -- allow html for backwards compatibility
285+ | otherwise = return empty
286+blockToDocbook _ HorizontalRule = return empty -- not semantic
287+blockToDocbook opts (Table caption aligns widths headers rows) = do
288+ captionDoc <- if null caption
289+ then return empty
290+ else inTagsIndented "title" <$>
291+ inlinesToDocbook opts caption
292+ let tableType = if isEmpty captionDoc then "informaltable" else "table"
264293 percent w = show (truncate (100*w) :: Integer) ++ "*"
265294 coltags = vcat $ zipWith (\w al -> selfClosingTag "colspec"
266295 ([("colwidth", percent w) | w > 0] ++
267296 [("align", alignmentToString al)])) widths aligns
268− head' = if all null headers
269− then empty
270− else inTagsIndented "thead" $
271− tableRowToDocbook opts headers
272− body' = inTagsIndented "tbody" $
273− vcat $ map (tableRowToDocbook opts) rows
274− in inTagsIndented tableType $ captionDoc $$
297+ head' <- if all null headers
298+ then return empty
299+ else inTagsIndented "thead" <$> tableRowToDocbook opts headers
300+ body' <- (inTagsIndented "tbody" . vcat) <$>
301+ mapM (tableRowToDocbook opts) rows
302+ return $ inTagsIndented tableType $ captionDoc $$
275303 (inTags True "tgroup" [("cols", show (length headers))] $
276304 coltags $$ head' $$ body')
277305
@@ -292,89 +320,97 @@ alignmentToString alignment = case alignment of
292320 AlignCenter -> "center"
293321 AlignDefault -> "left"
294322
295−tableRowToDocbook :: WriterOptions
323+tableRowToDocbook :: PandocMonad m
324+ => WriterOptions
296325 -> [[Block]]
297− -> Doc
326+ -> DB m Doc
298327tableRowToDocbook opts cols =
299− inTagsIndented "row" $ vcat $ map (tableItemToDocbook opts) cols
328+ (inTagsIndented "row" . vcat) <$> mapM (tableItemToDocbook opts) cols
300329
301−tableItemToDocbook :: WriterOptions
330+tableItemToDocbook :: PandocMonad m
331+ => WriterOptions
302332 -> [Block]
303− -> Doc
333+ -> DB m Doc
304334tableItemToDocbook opts item =
305− inTags True "entry" [] $ vcat $ map (blockToDocbook opts) item
335+ (inTags True "entry" [] . vcat) <$> mapM (blockToDocbook opts) item
306336
307337-- | Convert a list of inline elements to Docbook.
308−inlinesToDocbook :: WriterOptions -> [Inline] -> Doc
309−inlinesToDocbook opts lst = hcat $ map (inlineToDocbook opts) lst
338+inlinesToDocbook :: PandocMonad m => WriterOptions -> [Inline] -> DB m Doc
339+inlinesToDocbook opts lst = hcat <$> mapM (inlineToDocbook opts) lst
310340
311341-- | Convert an inline element to Docbook.
312−inlineToDocbook :: WriterOptions -> Inline -> Doc
313−inlineToDocbook _ (Str str) = text $ escapeStringForXML str
342+inlineToDocbook :: PandocMonad m => WriterOptions -> Inline -> DB m Doc
343+inlineToDocbook _ (Str str) = return $ text $ escapeStringForXML str
314344inlineToDocbook opts (Emph lst) =
315− inTagsSimple "emphasis" $ inlinesToDocbook opts lst
345+ inTagsSimple "emphasis" <$> inlinesToDocbook opts lst
316346inlineToDocbook opts (Strong lst) =
317− inTags False "emphasis" [("role", "strong")] $ inlinesToDocbook opts lst
347+ inTags False "emphasis" [("role", "strong")] <$> inlinesToDocbook opts lst
318348inlineToDocbook opts (Strikeout lst) =
319− inTags False "emphasis" [("role", "strikethrough")] $
349+ inTags False "emphasis" [("role", "strikethrough")] <$>
320350 inlinesToDocbook opts lst
321351inlineToDocbook opts (Superscript lst) =
322− inTagsSimple "superscript" $ inlinesToDocbook opts lst
352+ inTagsSimple "superscript" <$> inlinesToDocbook opts lst
323353inlineToDocbook opts (Subscript lst) =
324− inTagsSimple "subscript" $ inlinesToDocbook opts lst
354+ inTagsSimple "subscript" <$> inlinesToDocbook opts lst
325355inlineToDocbook opts (SmallCaps lst) =
326− inTags False "emphasis" [("role", "smallcaps")] $
356+ inTags False "emphasis" [("role", "smallcaps")] <$>
327357 inlinesToDocbook opts lst
328358inlineToDocbook opts (Quoted _ lst) =
329− inTagsSimple "quote" $ inlinesToDocbook opts lst
359+ inTagsSimple "quote" <$> inlinesToDocbook opts lst
330360inlineToDocbook opts (Cite _ lst) =
331361 inlinesToDocbook opts lst
332362inlineToDocbook opts (Span (ident,_,_) ils) =
333− (if null ident
334− then mempty
335− else selfClosingTag "anchor" [("id", ident)]) <>
363+ ((if null ident
364+ then mempty
365+ else selfClosingTag "anchor" [("id", ident)]) <>) <$>
336366 inlinesToDocbook opts ils
337367inlineToDocbook _ (Code _ str) =
338− inTagsSimple "literal" $ text (escapeStringForXML str)
368+ return $ inTagsSimple "literal" $ text (escapeStringForXML str)
339369inlineToDocbook opts (Math t str)
340− | isMathML (writerHTMLMathMethod opts) =
341− case writeMathML dt <$> readTeX str of
342− Right r -> inTagsSimple tagtype
343− $ text $ Xml.ppcElement conf
344− $ fixNS
345− $ removeAttr r
346− Left _ -> inlinesToDocbook opts
347− $ texMathToInlines t str
348− | otherwise = inlinesToDocbook opts $ texMathToInlines t str
349− where (dt, tagtype) = case t of
350− InlineMath -> (DisplayInline,"inlineequation")
351− DisplayMath -> (DisplayBlock,"informalequation")
370+ | isMathML (writerHTMLMathMethod opts) = do
371+ res <- convertMath writeMathML t str
372+ case res of
373+ Right r -> return $ inTagsSimple tagtype
374+ $ text $ Xml.ppcElement conf
375+ $ fixNS
376+ $ removeAttr r
377+ Left il -> inlineToDocbook opts il
378+ | otherwise =
379+ texMathToInlines t str >>= inlinesToDocbook opts
380+ where tagtype = case t of
381+ InlineMath -> "inlineequation"
382+ DisplayMath -> "informalequation"
352383 conf = Xml.useShortEmptyTags (const False) Xml.defaultConfigPP
353384 removeAttr e = e{ Xml.elAttribs = [] }
354385 fixNS' qname = qname{ Xml.qPrefix = Just "mml" }
355386 fixNS = everywhere (mkT fixNS')
356−inlineToDocbook _ (RawInline f x) | f == "html" || f == "docbook" = text x
357− | otherwise = empty
358−inlineToDocbook _ LineBreak = text "\n"
359−inlineToDocbook _ Space = space
387+inlineToDocbook _ (RawInline f x)
388+ | f == "html" || f == "docbook" = return $ text x
389+ | otherwise = return empty
390+inlineToDocbook _ LineBreak = return $ text "\n"
391+-- currently ignore, would require the option to add custom
392+-- styles to the document
393+inlineToDocbook _ Space = return space
360394-- because we use \n for LineBreak, we can't do soft breaks:
361−inlineToDocbook _ SoftBreak = space
395+inlineToDocbook _ SoftBreak = return space
362396inlineToDocbook opts (Link attr txt (src, _))
363397 | Just email <- stripPrefix "mailto:" src =
364398 let emailLink = inTagsSimple "email" $ text $
365399 escapeStringForXML $ email
366400 in case txt of
367− [Str s] | escapeURI s == email -> emailLink
368− _ -> inlinesToDocbook opts txt <+>
369− char '(' <> emailLink <> char ')'
370− | otherwise =
401+ [Str s] | escapeURI s == email -> return emailLink
402+ _ -> do contents <- inlinesToDocbook opts txt
403+ return $ contents <+>
404+ char '(' <> emailLink <> char ')'
405+ | otherwise = do
406+ version <- ask
371407 (if isPrefixOf "#" src
372408 then inTags False "link" $ ("linkend", drop 1 src) : idAndRole attr
373− else if writerDocbook5 opts
409+ else if version == DocBook5
374410 then inTags False "link" $ ("xlink:href", src) : idAndRole attr
375− else inTags False "ulink" $ ("url", src) : idAndRole attr ) $
376− inlinesToDocbook opts txt
377−inlineToDocbook opts (Image attr _ (src, tit)) =
411+ else inTags False "ulink" $ ("url", src) : idAndRole attr )
412+ <$> inlinesToDocbook opts txt
413+inlineToDocbook opts (Image attr _ (src, tit)) = return $
378414 let titleDoc = if null tit
379415 then empty
380416 else inTagsIndented "objectinfo" $
@@ -382,7 +418,7 @@ inlineToDocbook opts (Image attr _ (src, tit)) =
382418 in inTagsIndented "inlinemediaobject" $ inTagsIndented "imageobject" $
383419 titleDoc $$ imageToDocbook opts attr src
384420inlineToDocbook opts (Note contents) =
385− inTagsIndented "footnote" $ blocksToDocbook opts contents
421+ inTagsIndented "footnote" <$> blocksToDocbook opts contents
386422
387423isMathML :: HTMLMathMethod -> Bool
388424isMathML (MathML _) = True
src/Text/Pandoc/Writers/Docx.hs+80−87
@@ -1,4 +1,4 @@
1−{-# LANGUAGE ScopedTypeVariables, PatternGuards, ViewPatterns #-}
1+{-# LANGUAGE ScopedTypeVariables, PatternGuards, ViewPatterns, DeriveFunctor #-}
22{-
33Copyright (C) 2012-2015 John MacFarlane <jgm@berkeley.edu>
44
@@ -38,7 +38,6 @@ import qualified Data.Set as Set
3838import qualified Text.Pandoc.UTF8 as UTF8
3939import Codec.Archive.Zip
4040import Data.Time.Clock.POSIX
41−import System.Environment
4241import Text.Pandoc.Compat.Time
4342import Text.Pandoc.Definition
4443import Text.Pandoc.Generic
@@ -46,20 +45,19 @@ import Text.Pandoc.ImageSize
4645import Text.Pandoc.Shared hiding (Element)
4746import Text.Pandoc.Writers.Shared (fixDisplayMath)
4847import Text.Pandoc.Options
49−import Text.Pandoc.Readers.TeXMath
48+import Text.Pandoc.Writers.Math
5049import Text.Pandoc.Highlighting ( highlight )
5150import Text.Pandoc.Walk
51+import Text.Pandoc.Error (PandocError)
5252import Text.XML.Light as XML
5353import Text.TeXMath
5454import Text.Pandoc.Readers.Docx.StyleMap
55−import Text.Pandoc.Readers.Docx.Util (elemName)
5655import Control.Monad.Reader
5756import Control.Monad.State
5857import Skylighting
59−import Data.Unique (hashUnique, newUnique)
60−import System.Random (randomRIO)
58+import Control.Monad.Except (runExceptT)
59+import System.Random (randomR)
6160import Text.Printf (printf)
62−import qualified Control.Exception as E
6361import Data.Monoid ((<>))
6462import qualified Data.Text as T
6563import Text.Pandoc.MIME (MimeType, getMimeType, getMimeTypeDef,
@@ -67,6 +65,8 @@ import Text.Pandoc.MIME (MimeType, getMimeType, getMimeTypeDef,
6765import Control.Applicative ((<|>))
6866import Data.Maybe (fromMaybe, mapMaybe, maybeToList, isNothing)
6967import Data.Char (ord, isSpace, toLower)
68+import Text.Pandoc.Class (PandocMonad)
69+import qualified Text.Pandoc.Class as P
7070
7171data ListMarker = NoMarker
7272 | BulletMarker
@@ -141,12 +141,12 @@ defaultWriterState = WriterState{
141141 , stDelId = 1
142142 , stStyleMaps = defaultStyleMaps
143143 , stFirstPara = False
144− , stTocTitle = normalizeInlines [Str "Table of Contents"]
144+ , stTocTitle = [Str "Table of Contents"]
145145 , stDynamicParaProps = []
146146 , stDynamicTextProps = []
147147 }
148148
149−type WS = ReaderT WriterEnv (StateT WriterState IO)
149+type WS m = ReaderT WriterEnv (StateT WriterState m)
150150
151151mknode :: Node t => String -> [(String,String)] -> t -> Element
152152mknode s attrs =
@@ -207,25 +207,28 @@ isValidChar (ord -> c)
207207 | otherwise = False
208208
209209metaValueToInlines :: MetaValue -> [Inline]
210−metaValueToInlines (MetaString s) = normalizeInlines [Str s]
210+metaValueToInlines (MetaString s) = [Str s]
211211metaValueToInlines (MetaInlines ils) = ils
212212metaValueToInlines (MetaBlocks bs) = query return bs
213213metaValueToInlines (MetaBool b) = [Str $ show b]
214214metaValueToInlines _ = []
215215
216−-- | Produce an Docx file from a Pandoc document.
217−writeDocx :: WriterOptions -- ^ Writer options
216+
217+
218+writeDocx :: (PandocMonad m)
219+ => WriterOptions -- ^ Writer options
218220 -> Pandoc -- ^ Document to convert
219− -> IO BL.ByteString
221+ -> m BL.ByteString
220222writeDocx opts doc@(Pandoc meta _) = do
221223 let datadir = writerUserDataDir opts
222224 let doc' = walk fixDisplayMath $ doc
223− username <- lookup "USERNAME" <$> getEnvironment
224− utctime <- getCurrentTime
225− distArchive <- getDefaultReferenceDocx datadir
226− refArchive <- case writerReferenceDocx opts of
227− Just f -> liftM (toArchive . toLazy) $ B.readFile f
228− Nothing -> getDefaultReferenceDocx datadir
225+ username <- P.lookupEnv "USERNAME"
226+ utctime <- P.getCurrentTime
227+ distArchive <- (toArchive . BL.fromStrict) <$>
228+ P.readDataFile datadir "reference.docx"
229+ refArchive <- case writerReferenceDoc opts of
230+ Just f -> toArchive <$> P.readFileLazy f
231+ Nothing -> return distArchive
229232
230233 parsedDoc <- parseXml refArchive distArchive "word/document.xml"
231234 let wname f qn = qPrefix qn == Just "w" && f (qName qn)
@@ -446,18 +449,11 @@ writeDocx opts doc@(Pandoc meta _) = do
446449
447450 let newstyles = map newParaPropToOpenXml newDynamicParaProps ++
448451 map newTextPropToOpenXml newDynamicTextProps ++
449− (styleToOpenXml styleMaps $ writerHighlightStyle opts)
450− let styledoc' = styledoc{ elContent = modifyContent (elContent styledoc) }
451− where
452− modifyContent
453− | writerHighlight opts = (++ map Elem newstyles)
454− | otherwise = filter notTokStyle
455− notTokStyle (Elem el) = notStyle el || notTokId el
456− notTokStyle _ = True
457− notStyle = (/= elemName' "style") . elName
458− notTokId = maybe True (`notElem` tokStys) . findAttr (elemName' "styleId")
459− tokStys = "SourceCode" : map show (enumFromTo KeywordTok NormalTok)
460− elemName' = elemName (sNameSpaces styleMaps) "w"
452+ (case writerHighlightStyle opts of
453+ Nothing -> []
454+ Just sty -> (styleToOpenXml styleMaps sty))
455+ let styledoc' = styledoc{ elContent = elContent styledoc ++
456+ map Elem newstyles }
461457 let styleEntry = toEntry stylepath epochtime $ renderXml styledoc'
462458
463459 -- construct word/numbering.xml
@@ -603,7 +599,7 @@ styleToOpenXml sm style =
603599 $ backgroundColor style )
604600 ]
605601
606−copyChildren :: Archive -> Archive -> String -> Integer -> [String] -> IO Entry
602+copyChildren :: (PandocMonad m) => Archive -> Archive -> String -> Integer -> [String] -> m Entry
607603copyChildren refArchive distArchive path timestamp elNames = do
608604 ref <- parseXml refArchive distArchive path
609605 dist <- parseXml distArchive distArchive path
@@ -622,7 +618,7 @@ copyChildren refArchive distArchive path timestamp elNames = do
622618baseListId :: Int
623619baseListId = 1000
624620
625−mkNumbering :: [ListMarker] -> IO [Element]
621+mkNumbering :: (PandocMonad m) => [ListMarker] -> m [Element]
626622mkNumbering lists = do
627623 elts <- mapM mkAbstractNum (ordNub lists)
628624 return $ elts ++ zipWith mkNum lists [baseListId..(baseListId + length lists - 1)]
@@ -638,9 +634,10 @@ mkNum marker numid =
638634 map (\lvl -> mknode "w:lvlOverride" [("w:ilvl",show (lvl :: Int))]
639635 $ mknode "w:startOverride" [("w:val",show start)] ()) [0..6]
640636
641−mkAbstractNum :: ListMarker -> IO Element
637+mkAbstractNum :: (PandocMonad m) => ListMarker -> m Element
642638mkAbstractNum marker = do
643− nsid <- randomRIO (0x10000000 :: Integer, 0xFFFFFFFF :: Integer)
639+ gen <- P.newStdGen
640+ let (nsid, _) = randomR (0x10000000 :: Integer, 0xFFFFFFFF :: Integer) gen
644641 return $ mknode "w:abstractNum" [("w:abstractNumId",listMarkerToId marker)]
645642 $ mknode "w:nsid" [("w:val", printf "%8x" nsid)] ()
646643 : mknode "w:multiLevelType" [("w:val","multilevel")] ()
@@ -692,10 +689,11 @@ mkLvl marker lvl =
692689 patternFor TwoParens s = "(" ++ s ++ ")"
693690 patternFor _ s = s ++ "."
694691
695−getNumId :: WS Int
692+getNumId :: (PandocMonad m) => WS m Int
696693getNumId = (((baseListId - 1) +) . length) `fmap` gets stLists
697694
698−makeTOC :: WriterOptions -> WS [Element]
695+
696+makeTOC :: (PandocMonad m) => WriterOptions -> WS m [Element]
699697makeTOC opts | writerTableOfContents opts = do
700698 let depth = "1-"++(show (writerTOCDepth opts))
701699 let tocCmd = "TOC \\o \""++depth++"\" \\h \\z \\u"
@@ -725,7 +723,7 @@ makeTOC _ = return []
725723
726724-- | Convert Pandoc document to two lists of
727725-- OpenXML elements (the main document and footnotes).
728−writeOpenXML :: WriterOptions -> Pandoc -> WS ([Element], [Element])
726+writeOpenXML :: (PandocMonad m) => WriterOptions -> Pandoc -> WS m ([Element], [Element])
729727writeOpenXML opts (Pandoc meta blocks) = do
730728 let tit = docTitle meta ++ case lookupMeta "subtitle" meta of
731729 Just (MetaBlocks [Plain xs]) -> LineBreak : xs
@@ -760,13 +758,13 @@ writeOpenXML opts (Pandoc meta blocks) = do
760758 return (meta' ++ doc', notes')
761759
762760-- | Convert a list of Pandoc blocks to OpenXML.
763−blocksToOpenXML :: WriterOptions -> [Block] -> WS [Element]
761+blocksToOpenXML :: (PandocMonad m) => WriterOptions -> [Block] -> WS m [Element]
764762blocksToOpenXML opts bls = concat `fmap` mapM (blockToOpenXML opts) bls
765763
766764pCustomStyle :: String -> Element
767765pCustomStyle sty = mknode "w:pStyle" [("w:val",sty)] ()
768766
769−pStyleM :: String -> WS XML.Element
767+pStyleM :: (PandocMonad m) => String -> WS m XML.Element
770768pStyleM styleName = do
771769 styleMaps <- gets stStyleMaps
772770 let sty' = getStyleId styleName $ sParaStyleMap styleMaps
@@ -775,26 +773,26 @@ pStyleM styleName = do
775773rCustomStyle :: String -> Element
776774rCustomStyle sty = mknode "w:rStyle" [("w:val",sty)] ()
777775
778−rStyleM :: String -> WS XML.Element
776+rStyleM :: (PandocMonad m) => String -> WS m XML.Element
779777rStyleM styleName = do
780778 styleMaps <- gets stStyleMaps
781779 let sty' = getStyleId styleName $ sCharStyleMap styleMaps
782780 return $ mknode "w:rStyle" [("w:val",sty')] ()
783781
784−getUniqueId :: MonadIO m => m String
782+getUniqueId :: (PandocMonad m) => m String
785783-- the + 20 is to ensure that there are no clashes with the rIds
786784-- already in word/document.xml.rel
787−getUniqueId = liftIO $ (show . (+ 20) . hashUnique) `fmap` newUnique
785+getUniqueId = (show . (+ 20)) <$> P.newUniqueHash
788786
789787-- | Key for specifying user-defined docx styles.
790788dynamicStyleKey :: String
791789dynamicStyleKey = "custom-style"
792790
793791-- | Convert a Pandoc block element to OpenXML.
794−blockToOpenXML :: WriterOptions -> Block -> WS [Element]
792+blockToOpenXML :: (PandocMonad m) => WriterOptions -> Block -> WS m [Element]
795793blockToOpenXML opts blk = withDirection $ blockToOpenXML' opts blk
796794
797−blockToOpenXML' :: WriterOptions -> Block -> WS [Element]
795+blockToOpenXML' :: (PandocMonad m) => WriterOptions -> Block -> WS m [Element]
798796blockToOpenXML' _ Null = return []
799797blockToOpenXML' opts (Div (ident,classes,kvs) bs)
800798 | Just sty <- lookup dynamicStyleKey kvs = do
@@ -825,7 +823,7 @@ blockToOpenXML' opts (Header lev (ident,_,_) lst) = do
825823 then uniqueIdent lst usedIdents
826824 else ident
827825 modify $ \s -> s{ stSectionIds = Set.insert bookmarkName $ stSectionIds s }
828− id' <- getUniqueId
826+ id' <- (lift . lift) getUniqueId
829827 let bookmarkStart = mknode "w:bookmarkStart" [("w:id", id')
830828 ,("w:name",bookmarkName)] ()
831829 let bookmarkEnd = mknode "w:bookmarkEnd" [("w:id", id')] ()
@@ -945,7 +943,7 @@ blockToOpenXML' opts (DefinitionList items) = do
945943 setFirstPara
946944 return l
947945
948−definitionListItemToOpenXML :: WriterOptions -> ([Inline],[[Block]]) -> WS [Element]
946+definitionListItemToOpenXML :: (PandocMonad m) => WriterOptions -> ([Inline],[[Block]]) -> WS m [Element]
949947definitionListItemToOpenXML opts (term,defs) = do
950948 term' <- withParaProp (pCustomStyle "DefinitionTerm")
951949 $ blockToOpenXML opts (Para term)
@@ -953,12 +951,12 @@ definitionListItemToOpenXML opts (term,defs) = do
953951 $ concat `fmap` mapM (blocksToOpenXML opts) defs
954952 return $ term' ++ defs'
955953
956−addList :: ListMarker -> WS ()
954+addList :: (PandocMonad m) => ListMarker -> WS m ()
957955addList marker = do
958956 lists <- gets stLists
959957 modify $ \st -> st{ stLists = lists ++ [marker] }
960958
961−listItemToOpenXML :: WriterOptions -> Int -> [Block] -> WS [Element]
959+listItemToOpenXML :: (PandocMonad m) => WriterOptions -> Int -> [Block] -> WS m [Element]
962960listItemToOpenXML _ _ [] = return []
963961listItemToOpenXML opts numid (first:rest) = do
964962 first' <- withNumId numid $ blockToOpenXML opts first
@@ -974,30 +972,30 @@ alignmentToString alignment = case alignment of
974972 AlignDefault -> "left"
975973
976974-- | Convert a list of inline elements to OpenXML.
977−inlinesToOpenXML :: WriterOptions -> [Inline] -> WS [Element]
975+inlinesToOpenXML :: (PandocMonad m) => WriterOptions -> [Inline] -> WS m [Element]
978976inlinesToOpenXML opts lst = concat `fmap` mapM (inlineToOpenXML opts) lst
979977
980−withNumId :: Int -> WS a -> WS a
978+withNumId :: (PandocMonad m) => Int -> WS m a -> WS m a
981979withNumId numid = local $ \env -> env{ envListNumId = numid }
982980
983−asList :: WS a -> WS a
981+asList :: (PandocMonad m) => WS m a -> WS m a
984982asList = local $ \env -> env{ envListLevel = envListLevel env + 1 }
985983
986−getTextProps :: WS [Element]
984+getTextProps :: (PandocMonad m) => WS m [Element]
987985getTextProps = do
988986 props <- asks envTextProperties
989987 return $ if null props
990988 then []
991989 else [mknode "w:rPr" [] props]
992990
993−withTextProp :: Element -> WS a -> WS a
991+withTextProp :: PandocMonad m => Element -> WS m a -> WS m a
994992withTextProp d p =
995993 local (\env -> env {envTextProperties = d : envTextProperties env}) p
996994
997−withTextPropM :: WS Element -> WS a -> WS a
995+withTextPropM :: PandocMonad m => WS m Element -> WS m a -> WS m a
998996withTextPropM = (. flip withTextProp) . (>>=)
999997
1000−getParaProps :: Bool -> WS [Element]
998+getParaProps :: PandocMonad m => Bool -> WS m [Element]
1001999getParaProps displayMathPara = do
10021000 props <- asks envParaProperties
10031001 listLevel <- asks envListLevel
@@ -1012,14 +1010,14 @@ getParaProps displayMathPara = do
10121010 [] -> []
10131011 ps -> [mknode "w:pPr" [] ps]
10141012
1015−withParaProp :: Element -> WS a -> WS a
1013+withParaProp :: PandocMonad m => Element -> WS m a -> WS m a
10161014withParaProp d p =
10171015 local (\env -> env {envParaProperties = d : envParaProperties env}) p
10181016
1019−withParaPropM :: WS Element -> WS a -> WS a
1017+withParaPropM :: PandocMonad m => WS m Element -> WS m a -> WS m a
10201018withParaPropM = (. flip withParaProp) . (>>=)
10211019
1022−formattedString :: String -> WS [Element]
1020+formattedString :: PandocMonad m => String -> WS m [Element]
10231021formattedString str = do
10241022 props <- getTextProps
10251023 inDel <- asks envInDel
@@ -1028,14 +1026,14 @@ formattedString str = do
10281026 [ mknode (if inDel then "w:delText" else "w:t")
10291027 [("xml:space","preserve")] (stripInvalidChars str) ] ]
10301028
1031−setFirstPara :: WS ()
1029+setFirstPara :: PandocMonad m => WS m ()
10321030setFirstPara = modify $ \s -> s { stFirstPara = True }
10331031
10341032-- | Convert an inline element to OpenXML.
1035−inlineToOpenXML :: WriterOptions -> Inline -> WS [Element]
1033+inlineToOpenXML :: PandocMonad m => WriterOptions -> Inline -> WS m [Element]
10361034inlineToOpenXML opts il = withDirection $ inlineToOpenXML' opts il
10371035
1038−inlineToOpenXML' :: WriterOptions -> Inline -> WS [Element]
1036+inlineToOpenXML' :: PandocMonad m => WriterOptions -> Inline -> WS m [Element]
10391037inlineToOpenXML' _ (Str str) = formattedString str
10401038inlineToOpenXML' opts Space = inlineToOpenXML opts (Str " ")
10411039inlineToOpenXML' opts SoftBreak = inlineToOpenXML opts (Str " ")
@@ -1109,16 +1107,11 @@ inlineToOpenXML' opts (Quoted quoteType lst) =
11091107 SingleQuote -> ("\x2018", "\x2019")
11101108 DoubleQuote -> ("\x201C", "\x201D")
11111109inlineToOpenXML' opts (Math mathType str) = do
1112− let displayType = if mathType == DisplayMath
1113− then DisplayBlock
1114− else DisplayInline
1115− when (displayType == DisplayBlock) setFirstPara
1116− case writeOMML displayType <$> readTeX str of
1117− Right r -> return [r]
1118− Left e -> do
1119− warn $ "Cannot convert the following TeX math, skipping:\n" ++ str ++
1120− "\n" ++ e
1121− inlinesToOpenXML opts (texMathToInlines mathType str)
1110+ when (mathType == DisplayMath) setFirstPara
1111+ res <- (lift . lift) (convertMath writeOMML mathType str)
1112+ case res of
1113+ Right r -> return [r]
1114+ Left il -> inlineToOpenXML' opts il
11221115inlineToOpenXML' opts (Cite _ lst) = inlinesToOpenXML opts lst
11231116inlineToOpenXML' opts (Code attrs str) = do
11241117 let unhighlighted = intercalate [br] `fmap`
@@ -1129,14 +1122,12 @@ inlineToOpenXML' opts (Code attrs str) = do
11291122 [ rCustomStyle (show toktype) ]
11301123 , mknode "w:t" [("xml:space","preserve")] (T.unpack tok) ]
11311124 withTextProp (rCustomStyle "VerbatimChar")
1132− $ if writerHighlight opts
1133− then case highlight formatOpenXML attrs str of
1134− Nothing -> unhighlighted
1135− Just h -> return h
1136− else unhighlighted
1125+ $ case writerHighlightStyle opts >> highlight formatOpenXML attrs str of
1126+ Just h -> return h
1127+ Nothing -> unhighlighted
11371128inlineToOpenXML' opts (Note bs) = do
11381129 notes <- gets stFootnotes
1139− notenum <- getUniqueId
1130+ notenum <- (lift . lift) getUniqueId
11401131 footnoteStyle <- rStyleM "Footnote Reference"
11411132 let notemarker = mknode "w:r" []
11421133 [ mknode "w:rPr" [] footnoteStyle
@@ -1167,7 +1158,7 @@ inlineToOpenXML' opts (Link _ txt (src,_)) = do
11671158 id' <- case M.lookup src extlinks of
11681159 Just i -> return i
11691160 Nothing -> do
1170− i <- ("rId"++) `fmap` getUniqueId
1161+ i <- ("rId"++) `fmap` ((lift . lift) getUniqueId)
11711162 modify $ \st -> st{ stExternalLinks =
11721163 M.insert src i extlinks }
11731164 return i
@@ -1179,15 +1170,14 @@ inlineToOpenXML' opts (Image attr alt (src, title)) = do
11791170 case M.lookup src imgs of
11801171 Just (_,_,_,elt,_) -> return [elt]
11811172 Nothing -> do
1182− res <- liftIO $
1183− fetchItem' (writerMediaBag opts) (writerSourceURL opts) src
1173+ res <- runExceptT $ lift (P.fetchItem (writerSourceURL opts) src)
11841174 case res of
1185− Left (_ :: E.SomeException) -> do
1186− warn $ "Could not find image `" ++ src ++ "', skipping..."
1175+ Left (_ :: PandocError) -> do
1176+ P.warning ("Could not find image `" ++ src ++ "', skipping...")
11871177 -- emit alt text
11881178 inlinesToOpenXML opts alt
11891179 Right (img, mt) -> do
1190− ident <- ("rId"++) `fmap` getUniqueId
1180+ ident <- ("rId"++) `fmap` ((lift . lift) getUniqueId)
11911181 let (xpt,ypt) = desiredSizeInPoints opts attr
11921182 (either (const def) id (imageSize img))
11931183 -- 12700 emu = 1 pt
@@ -1247,7 +1237,10 @@ inlineToOpenXML' opts (Image attr alt (src, title)) = do
12471237 return [imgElt]
12481238
12491239br :: Element
1250−br = mknode "w:r" [] [mknode "w:br" [("w:type","textWrapping")] () ]
1240+br = breakElement "textWrapping"
1241+
1242+breakElement :: String -> Element
1243+breakElement kind = mknode "w:r" [] [mknode "w:br" [("w:type",kind)] () ]
12511244
12521245-- Word will insert these footnotes into the settings.xml file
12531246-- (whether or not they're visible in the document). If they're in the
@@ -1265,7 +1258,7 @@ defaultFootnotes = [ mknode "w:footnote"
12651258 [ mknode "w:r" [] $
12661259 [ mknode "w:continuationSeparator" [] ()]]]]
12671260
1268−parseXml :: Archive -> Archive -> String -> IO Element
1261+parseXml :: (PandocMonad m) => Archive -> Archive -> String -> m Element
12691262parseXml refArchive distArchive relpath =
12701263 case findEntryByPath relpath refArchive `mplus`
12711264 findEntryByPath relpath distArchive of
@@ -1283,7 +1276,7 @@ fitToPage (x, y) pageWidth
12831276 (pageWidth, floor $ ((fromIntegral pageWidth) / x) * y)
12841277 | otherwise = (floor x, floor y)
12851278
1286−withDirection :: WS a -> WS a
1279+withDirection :: PandocMonad m => WS m a -> WS m a
12871280withDirection x = do
12881281 isRTL <- asks envRTL
12891282 paraProps <- asks envParaProperties
src/Text/Pandoc/Writers/DokuWiki.hs+15−7
@@ -45,7 +45,7 @@ import Text.Pandoc.Options ( WriterOptions(
4545 , writerTemplate
4646 , writerWrapText), WrapOption(..) )
4747import Text.Pandoc.Shared ( escapeURI, linesToPara, removeFormatting
48− , camelCaseToHyphenated, trimr, normalize, substitute )
48+ , camelCaseToHyphenated, trimr, substitute )
4949import Text.Pandoc.Writers.Shared ( defField, metaToJSON )
5050import Text.Pandoc.ImageSize
5151import Text.Pandoc.Templates ( renderTemplate' )
@@ -55,6 +55,7 @@ import Network.URI ( isURI )
5555import Control.Monad ( zipWithM )
5656import Control.Monad.State ( modify, State, get, evalState )
5757import Control.Monad.Reader ( ReaderT, runReaderT, ask, local )
58+import Text.Pandoc.Class (PandocMonad)
5859
5960data WriterState = WriterState {
6061 stNotes :: Bool -- True if there are notes
@@ -77,9 +78,9 @@ instance Default WriterEnvironment where
7778type DokuWiki = ReaderT WriterEnvironment (State WriterState)
7879
7980-- | Convert Pandoc to DokuWiki.
80−writeDokuWiki :: WriterOptions -> Pandoc -> String
81−writeDokuWiki opts document =
82− runDokuWiki (pandocToDokuWiki opts $ normalize document)
81+writeDokuWiki :: PandocMonad m => WriterOptions -> Pandoc -> m String
82+writeDokuWiki opts document = return $
83+ runDokuWiki (pandocToDokuWiki opts document)
8384
8485runDokuWiki :: DokuWiki a -> a
8586runDokuWiki = flip evalState def . flip runReaderT def
@@ -393,9 +394,16 @@ blockListToDokuWiki :: WriterOptions -- ^ Options
393394 -> DokuWiki String
394395blockListToDokuWiki opts blocks = do
395396 backSlash <- stBackSlashLB <$> ask
397+ let blocks' = consolidateRawBlocks blocks
396398 if backSlash
397− then (backSlashLineBreaks . vcat) <$> mapM (blockToDokuWiki opts) blocks
398− else vcat <$> mapM (blockToDokuWiki opts) blocks
399+ then (backSlashLineBreaks . vcat) <$> mapM (blockToDokuWiki opts) blocks'
400+ else vcat <$> mapM (blockToDokuWiki opts) blocks'
401+
402+consolidateRawBlocks :: [Block] -> [Block]
403+consolidateRawBlocks [] = []
404+consolidateRawBlocks (RawBlock f1 b1 : RawBlock f2 b2 : xs)
405+ | f1 == f2 = consolidateRawBlocks (RawBlock f1 (b1 ++ "\n" ++ b2) : xs)
406+consolidateRawBlocks (x:xs) = x : consolidateRawBlocks xs
399407
400408-- | Convert list of Pandoc inline elements to DokuWiki.
401409inlineListToDokuWiki :: WriterOptions -> [Inline] -> DokuWiki String
@@ -465,7 +473,7 @@ inlineToDokuWiki _ (RawInline f str)
465473 | f == Format "html" = return $ "<html>" ++ str ++ "</html>"
466474 | otherwise = return ""
467475
468−inlineToDokuWiki _ (LineBreak) = return "\\\\\n"
476+inlineToDokuWiki _ LineBreak = return "\\\\\n"
469477
470478inlineToDokuWiki opts SoftBreak =
471479 case writerWrapText opts of
src/Text/Pandoc/Writers/EPUB.hs+150−116
@@ -28,26 +28,22 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2828
2929Conversion of 'Pandoc' documents to EPUB.
3030-}
31−module Text.Pandoc.Writers.EPUB ( writeEPUB ) where
32−import Data.IORef ( IORef, newIORef, readIORef, modifyIORef )
31+module Text.Pandoc.Writers.EPUB ( writeEPUB2, writeEPUB3 ) where
3332import qualified Data.Map as M
3433import qualified Data.Set as Set
3534import Data.Maybe ( fromMaybe, catMaybes )
3635import Data.List ( isPrefixOf, isInfixOf, intercalate )
37−import System.Environment ( getEnv )
3836import Text.Printf (printf)
3937import System.FilePath ( takeExtension, takeFileName )
40−import System.FilePath.Glob ( namesMatching )
4138import Network.HTTP ( urlEncode )
4239import qualified Data.ByteString.Lazy as B
4340import qualified Data.ByteString.Lazy.Char8 as B8
4441import qualified Text.Pandoc.UTF8 as UTF8
4542import Codec.Archive.Zip ( emptyArchive, addEntryToArchive, eRelativePath, fromEntry , Entry, toEntry, fromArchive)
46−import Data.Time.Clock.POSIX ( getPOSIXTime )
4743import Text.Pandoc.Compat.Time
4844import Text.Pandoc.Shared ( renderTags', safeRead, uniqueIdent, trim
49− , normalizeDate, readDataFile, stringify, warn
50− , hierarchicalize, fetchItem' )
45+ , normalizeDate, stringify
46+ , hierarchicalize )
5147import qualified Text.Pandoc.Shared as S (Element(..))
5248import Text.Pandoc.Builder (fromList, setMeta)
5349import Text.Pandoc.Options ( WriterOptions(..)
@@ -57,18 +53,20 @@ import Text.Pandoc.Options ( WriterOptions(..)
5753 , ObfuscationMethod(NoObfuscation) )
5854import Text.Pandoc.Definition
5955import Text.Pandoc.Walk (walk, walkM, query)
60−import Control.Monad.State (modify, get, State, put, evalState)
61−import Control.Monad (mplus, liftM, when)
56+import Text.Pandoc.UUID (getUUID)
57+import Control.Monad.State (modify, get, gets, State, StateT, put, evalState, evalStateT, lift)
58+import Control.Monad (mplus, when, zipWithM)
6259import Text.XML.Light ( unode, Element(..), unqual, Attr(..), add_attrs
6360 , strContent, lookupAttr, Node(..), QName(..), parseXML
6461 , onlyElems, node, ppElement)
65−import Text.Pandoc.UUID (getRandomUUID)
66−import Text.Pandoc.Writers.HTML ( writeHtml )
62+import Text.Pandoc.Writers.HTML ( writeHtmlStringForEPUB )
6763import Data.Char ( toLower, isDigit, isAlphaNum )
6864import Text.Pandoc.MIME (MimeType, getMimeType, extensionFromMimeType)
69−import qualified Control.Exception as E
70−import Text.Blaze.Html.Renderer.Utf8 (renderHtml)
7165import Text.HTML.TagSoup (Tag(TagOpen), fromAttrib, parseTags)
66+import Control.Monad.Except (throwError, catchError)
67+import Text.Pandoc.Error
68+import Text.Pandoc.Class (PandocMonad)
69+import qualified Text.Pandoc.Class as P
7270
7371-- A Chapter includes a list of blocks and maybe a section
7472-- number offset. Note, some chapters are unnumbered. The section
@@ -76,6 +74,12 @@ import Text.HTML.TagSoup (Tag(TagOpen), fromAttrib, parseTags)
7674-- in filenames, chapter0003.xhtml.
7775data Chapter = Chapter (Maybe [Int]) [Block]
7876
77+data EPUBState = EPUBState {
78+ stMediaPaths :: [(FilePath, (FilePath, Maybe Entry))]
79+ }
80+
81+type E m = StateT EPUBState m
82+
7983data EPUBMetadata = EPUBMetadata{
8084 epubIdentifier :: [Identifier]
8185 , epubTitle :: [Title]
@@ -143,7 +147,7 @@ removeNote :: Inline -> Inline
143147removeNote (Note _) = Str ""
144148removeNote x = x
145149
146−getEPUBMetadata :: WriterOptions -> Meta -> IO EPUBMetadata
150+getEPUBMetadata :: PandocMonad m => WriterOptions -> Meta -> E m EPUBMetadata
147151getEPUBMetadata opts meta = do
148152 let md = metadataFromMeta opts meta
149153 let elts = onlyElems $ parseXML $ writerEpubMetadata opts
@@ -151,7 +155,7 @@ getEPUBMetadata opts meta = do
151155 let addIdentifier m =
152156 if null (epubIdentifier m)
153157 then do
154− randomId <- fmap show getRandomUUID
158+ randomId <- (show . getUUID) <$> lift P.newStdGen
155159 return $ m{ epubIdentifier = [Identifier randomId Nothing] }
156160 else return m
157161 let addLanguage m =
@@ -159,16 +163,19 @@ getEPUBMetadata opts meta = do
159163 then case lookup "lang" (writerVariables opts) of
160164 Just x -> return m{ epubLanguage = x }
161165 Nothing -> do
162− localeLang <- E.catch (liftM
163− (map (\c -> if c == '_' then '-' else c) .
164− takeWhile (/='.')) $ getEnv "LANG")
165− (\e -> let _ = (e :: E.SomeException) in return "en-US")
166+ mLang <- lift $ P.lookupEnv "LANG"
167+ let localeLang =
168+ case mLang of
169+ Just lang ->
170+ map (\c -> if c == '_' then '-' else c) $
171+ takeWhile (/='.') lang
172+ Nothing -> "en-US"
166173 return m{ epubLanguage = localeLang }
167174 else return m
168175 let fixDate m =
169176 if null (epubDate m)
170177 then do
171− currentTime <- getCurrentTime
178+ currentTime <- lift P.getCurrentTime
172179 return $ m{ epubDate = [ Date{
173180 dateText = showDateTimeISO8601 currentTime
174181 , dateEvent = Nothing } ] }
@@ -329,21 +336,49 @@ metadataFromMeta opts meta = EPUBMetadata{
329336 Just "rtl" -> Just RTL
330337 _ -> Nothing
331338
339+-- | Produce an EPUB2 file from a Pandoc document.
340+writeEPUB2 :: PandocMonad m
341+ => WriterOptions -- ^ Writer options
342+ -> Pandoc -- ^ Document to convert
343+ -> m B.ByteString
344+writeEPUB2 = writeEPUB EPUB2
345+
346+-- | Produce an EPUB3 file from a Pandoc document.
347+writeEPUB3 :: PandocMonad m
348+ => WriterOptions -- ^ Writer options
349+ -> Pandoc -- ^ Document to convert
350+ -> m B.ByteString
351+writeEPUB3 = writeEPUB EPUB3
352+
332353-- | Produce an EPUB file from a Pandoc document.
333−writeEPUB :: WriterOptions -- ^ Writer options
354+writeEPUB :: PandocMonad m
355+ => EPUBVersion
356+ -> WriterOptions -- ^ Writer options
334357 -> Pandoc -- ^ Document to convert
335− -> IO B.ByteString
336−writeEPUB opts doc@(Pandoc meta _) = do
337− let version = fromMaybe EPUB2 (writerEpubVersion opts)
358+ -> m B.ByteString
359+writeEPUB epubVersion opts doc =
360+ let initState = EPUBState { stMediaPaths = []
361+ }
362+ in
363+ evalStateT (pandocToEPUB epubVersion opts doc)
364+ initState
365+
366+pandocToEPUB :: PandocMonad m
367+ => EPUBVersion
368+ -> WriterOptions
369+ -> Pandoc
370+ -> E m B.ByteString
371+pandocToEPUB version opts doc@(Pandoc meta _) = do
338372 let epub3 = version == EPUB3
339− epochtime <- floor `fmap` getPOSIXTime
373+ let writeHtml o = fmap UTF8.fromStringLazy .
374+ writeHtmlStringForEPUB version o
375+ epochtime <- floor <$> lift P.getPOSIXTime
340376 let mkEntry path content = toEntry path epochtime content
341377 let vars = ("epub3", if epub3 then "true" else "false")
342378 : ("css", "stylesheet.css")
343379 : writerVariables opts
344380 let opts' = opts{ writerEmailObfuscation = NoObfuscation
345381 , writerSectionDivs = True
346− , writerHtml5 = epub3
347382 , writerVariables = vars
348383 , writerHTMLMathMethod =
349384 if epub3
@@ -358,32 +393,31 @@ writeEPUB opts doc@(Pandoc meta _) = do
358393 Nothing -> return ([],[])
359394 Just img -> do
360395 let coverImage = "media/" ++ takeFileName img
361− let cpContent = renderHtml $ writeHtml
396+ cpContent <- lift $ writeHtml
362397 opts'{ writerVariables = ("coverpage","true"):vars }
363398 (Pandoc meta [RawBlock (Format "html") $ "<div id=\"cover-image\">\n<img src=\"" ++ coverImage ++ "\" alt=\"cover image\" />\n</div>"])
364− imgContent <- B.readFile img
399+ imgContent <- lift $ P.readFileLazy img
365400 return ( [mkEntry "cover.xhtml" cpContent]
366401 , [mkEntry coverImage imgContent] )
367402
368403 -- title page
369− let tpContent = renderHtml $ writeHtml opts'{
370− writerVariables = ("titlepage","true"):vars }
371− (Pandoc meta [])
404+ tpContent <- lift $ writeHtml opts'{
405+ writerVariables = ("titlepage","true"):vars }
406+ (Pandoc meta [])
372407 let tpEntry = mkEntry "title_page.xhtml" tpContent
373408
374409 -- handle pictures
375− mediaRef <- newIORef []
376− Pandoc _ blocks <- walkM (transformInline opts' mediaRef) doc >>=
377− walkM (transformBlock opts' mediaRef)
378− picEntries <- (catMaybes . map (snd . snd)) <$> readIORef mediaRef
379−
410+ -- mediaRef <- P.newIORef []
411+ Pandoc _ blocks <- walkM (transformInline opts') doc >>=
412+ walkM (transformBlock opts')
413+ picEntries <- (catMaybes . map (snd . snd)) <$> (gets stMediaPaths)
380414 -- handle fonts
381415 let matchingGlob f = do
382− xs <- namesMatching f
416+ xs <- lift $ P.glob f
383417 when (null xs) $
384− warn $ f ++ " did not match any font files."
418+ lift $ P.warning $ f ++ " did not match any font files."
385419 return xs
386− let mkFontEntry f = mkEntry (takeFileName f) `fmap` B.readFile f
420+ let mkFontEntry f = mkEntry (takeFileName f) `fmap` (lift $ P.readFileLazy f)
387421 fontFiles <- concat <$> mapM matchingGlob (writerEpubFonts opts')
388422 fontEntries <- mapM mkFontEntry fontFiles
389423
@@ -467,20 +501,18 @@ writeEPUB opts doc@(Pandoc meta _) = do
467501 Chapter mbnum $ walk fixInternalReferences bs)
468502 chapters'
469503
470− let chapToEntry :: Int -> Chapter -> Entry
471− chapToEntry num (Chapter mbnum bs) = mkEntry (showChapter num)
472− $ renderHtml
473− $ writeHtml opts'{ writerNumberOffset =
474− fromMaybe [] mbnum }
475− $ case bs of
476− (Header _ _ xs : _) ->
477− -- remove notes or we get doubled footnotes
478− Pandoc (setMeta "title" (walk removeNote $ fromList xs)
479− nullMeta) bs
480− _ ->
481− Pandoc nullMeta bs
482−
483− let chapterEntries = zipWith chapToEntry [1..] chapters
504+ let chapToEntry num (Chapter mbnum bs) =
505+ mkEntry (showChapter num) <$>
506+ (writeHtml opts'{ writerNumberOffset = fromMaybe [] mbnum }
507+ $ case bs of
508+ (Header _ _ xs : _) ->
509+ -- remove notes or we get doubled footnotes
510+ Pandoc (setMeta "title" (walk removeNote $ fromList xs)
511+ nullMeta) bs
512+ _ ->
513+ Pandoc nullMeta bs)
514+
515+ chapterEntries <- lift $ zipWithM chapToEntry [1..] chapters
484516
485517 -- incredibly inefficient (TODO):
486518 let containsMathML ent = epub3 &&
@@ -517,10 +549,10 @@ writeEPUB opts doc@(Pandoc meta _) = do
517549
518550 let tocTitle = fromMaybe plainTitle $
519551 metaValueToString <$> lookupMeta "toc-title" meta
520− let uuid = case epubIdentifier metadata of
521− (x:_) -> identifierText x -- use first identifier as UUID
522− [] -> error "epubIdentifier is null" -- shouldn't happen
523− currentTime <- getCurrentTime
552+ uuid <- case epubIdentifier metadata of
553+ (x:_) -> return $ identifierText x -- use first identifier as UUID
554+ [] -> throwError $ PandocShouldNeverHappenError "epubIdentifier is null" -- shouldn't happen
555+ currentTime <- lift $ P.getCurrentTime
524556 let contentsData = UTF8.fromStringLazy $ ppTopElement $
525557 unode "package" ! [("version", case version of
526558 EPUB2 -> "2.0"
@@ -575,8 +607,9 @@ writeEPUB opts doc@(Pandoc meta _) = do
575607
576608 let tocLevel = writerTOCDepth opts
577609
578− let navPointNode :: (Int -> String -> String -> [Element] -> Element)
579− -> S.Element -> State Int Element
610+ let navPointNode :: PandocMonad m
611+ => (Int -> String -> String -> [Element] -> Element)
612+ -> S.Element -> StateT Int m Element
580613 navPointNode formatter (S.Sec _ nums (ident,_,_) ils children) = do
581614 n <- get
582615 modify (+1)
@@ -586,15 +619,15 @@ writeEPUB opts doc@(Pandoc meta _) = do
586619 let tit = if writerNumberSections opts && not (null nums)
587620 then showNums nums ++ " " ++ tit'
588621 else tit'
589− let src = case lookup ident reftable of
590− Just x -> x
591− Nothing -> error (ident ++ " not found in reftable")
622+ src <- case lookup ident reftable of
623+ Just x -> return x
624+ Nothing -> throwError $ PandocSomeError $ ident ++ " not found in reftable"
592625 let isSec (S.Sec lev _ _ _ _) = lev <= tocLevel
593626 isSec _ = False
594627 let subsecs = filter isSec children
595628 subs <- mapM (navPointNode formatter) subsecs
596629 return $ formatter n tit src subs
597− navPointNode _ (S.Blk _) = error "navPointNode encountered Blk"
630+ navPointNode _ (S.Blk _) = throwError $ PandocSomeError "navPointNode encountered Blk"
598631
599632 let navMapFormatter :: Int -> String -> String -> [Element] -> Element
600633 navMapFormatter n tit src subs = unode "navPoint" !
@@ -607,6 +640,7 @@ writeEPUB opts doc@(Pandoc meta _) = do
607640 [ unode "navLabel" $ unode "text" (stringify $ docTitle' meta)
608641 , unode "content" ! [("src","title_page.xhtml")] $ () ]
609642
643+ navMap <- lift $ evalStateT (mapM (navPointNode navMapFormatter) secs) 1
610644 let tocData = UTF8.fromStringLazy $ ppTopElement $
611645 unode "ncx" ! [("version","2005-1")
612646 ,("xmlns","http://www.daisy.org/z3986/2005/ncx/")] $
@@ -625,7 +659,7 @@ writeEPUB opts doc@(Pandoc meta _) = do
625659 ("content", toId img)] $ ()]
626660 , unode "docTitle" $ unode "text" $ plainTitle
627661 , unode "navMap" $
628− tpNode : evalState (mapM (navPointNode navMapFormatter) secs) 1
662+ tpNode : navMap
629663 ]
630664 let tocEntry = mkEntry "toc.ncx" tocData
631665
@@ -639,11 +673,12 @@ writeEPUB opts doc@(Pandoc meta _) = do
639673 (_:_) -> [unode "ol" ! [("class","toc")] $ subs]
640674
641675 let navtag = if epub3 then "nav" else "div"
676+ tocBlocks <- lift $ evalStateT (mapM (navPointNode navXhtmlFormatter) secs) 1
642677 let navBlocks = [RawBlock (Format "html") $ ppElement $
643678 unode navtag ! ([("epub:type","toc") | epub3] ++
644679 [("id","toc")]) $
645680 [ unode "h1" ! [("id","toc-title")] $ tocTitle
646− , unode "ol" ! [("class","toc")] $ evalState (mapM (navPointNode navXhtmlFormatter) secs) 1]]
681+ , unode "ol" ! [("class","toc")] $ tocBlocks ]]
647682 let landmarks = if epub3
648683 then [RawBlock (Format "html") $ ppElement $
649684 unode "nav" ! [("epub:type","landmarks")
@@ -664,8 +699,7 @@ writeEPUB opts doc@(Pandoc meta _) = do
664699 ]
665700 ]
666701 else []
667− let navData = renderHtml $ writeHtml
668− opts'{ writerVariables = ("navpage","true"):vars }
702+ navData <- lift $ writeHtml opts'{ writerVariables = ("navpage","true"):vars }
669703 (Pandoc (setMeta "title"
670704 (walk removeNote $ fromList $ docTitle' meta) nullMeta)
671705 (navBlocks ++ landmarks))
@@ -692,10 +726,10 @@ writeEPUB opts doc@(Pandoc meta _) = do
692726
693727 -- stylesheet
694728 stylesheet <- case epubStylesheet metadata of
695− Just (StylesheetPath fp) -> UTF8.readFile fp
729+ Just (StylesheetPath fp) -> UTF8.toStringLazy <$> (lift $ P.readFileLazy fp)
696730 Just (StylesheetContents s) -> return s
697731 Nothing -> UTF8.toString `fmap`
698− readDataFile (writerUserDataDir opts) "epub.css"
732+ (lift $ P.readDataFile (writerUserDataDir opts) "epub.css")
699733 let stylesheetEntry = mkEntry "stylesheet.css" $ UTF8.fromStringLazy stylesheet
700734
701735 -- construct archive
@@ -811,79 +845,79 @@ metadataElement version md currentTime =
811845showDateTimeISO8601 :: UTCTime -> String
812846showDateTimeISO8601 = formatTime defaultTimeLocale "%FT%TZ"
813847
814−transformTag :: WriterOptions
815− -> IORef [(FilePath, (FilePath, Maybe Entry))] -- ^ (oldpath, newpath, entry) media
848+transformTag :: PandocMonad m
849+ => WriterOptions
850+ -- -> IORef [(FilePath, (FilePath, Maybe Entry))] -- ^ (oldpath, newpath, entry) media
816851 -> Tag String
817− -> IO (Tag String)
818−transformTag opts mediaRef tag@(TagOpen name attr)
852+ -> E m (Tag String)
853+transformTag opts tag@(TagOpen name attr)
819854 | name `elem` ["video", "source", "img", "audio"] &&
820855 lookup "data-external" attr == Nothing = do
821856 let src = fromAttrib "src" tag
822857 let poster = fromAttrib "poster" tag
823− newsrc <- modifyMediaRef opts mediaRef src
824− newposter <- modifyMediaRef opts mediaRef poster
858+ newsrc <- modifyMediaRef opts src
859+ newposter <- modifyMediaRef opts poster
825860 let attr' = filter (\(x,_) -> x /= "src" && x /= "poster") attr ++
826861 [("src", newsrc) | not (null newsrc)] ++
827862 [("poster", newposter) | not (null newposter)]
828863 return $ TagOpen name attr'
829−transformTag _ _ tag = return tag
864+transformTag _ tag = return tag
830865
831−modifyMediaRef :: WriterOptions
832− -> IORef [(FilePath, (FilePath, Maybe Entry))]
866+modifyMediaRef :: PandocMonad m
867+ => WriterOptions
833868 -> FilePath
834− -> IO FilePath
835−modifyMediaRef _ _ "" = return ""
836−modifyMediaRef opts mediaRef oldsrc = do
837− media <- readIORef mediaRef
869+ -> E m FilePath
870+modifyMediaRef _ "" = return ""
871+modifyMediaRef opts oldsrc = do
872+ media <- gets stMediaPaths
838873 case lookup oldsrc media of
839874 Just (n,_) -> return n
840− Nothing -> do
841− res <- fetchItem' (writerMediaBag opts)
842− (writerSourceURL opts) oldsrc
843− (new, mbEntry) <-
844− case res of
845− Left _ -> do
846− warn $ "Could not find media `" ++ oldsrc ++ "', skipping..."
847− return (oldsrc, Nothing)
848− Right (img,mbMime) -> do
849− let new = "media/file" ++ show (length media) ++
850− fromMaybe (takeExtension (takeWhile (/='?') oldsrc))
851− (('.':) <$> (mbMime >>= extensionFromMimeType))
852− epochtime <- floor `fmap` getPOSIXTime
853− let entry = toEntry new epochtime $ B.fromChunks . (:[]) $ img
854− return (new, Just entry)
855− modifyIORef mediaRef ( (oldsrc, (new, mbEntry)): )
856− return new
857−
858−transformBlock :: WriterOptions
859− -> IORef [(FilePath, (FilePath, Maybe Entry))] -- ^ (oldpath, newpath, entry) media
875+ Nothing -> catchError
876+ (do (img, mbMime) <- P.fetchItem (writerSourceURL opts) oldsrc
877+ let new = "media/file" ++ show (length media) ++
878+ fromMaybe (takeExtension (takeWhile (/='?') oldsrc))
879+ (('.':) <$> (mbMime >>= extensionFromMimeType))
880+ epochtime <- floor `fmap` lift P.getPOSIXTime
881+ let entry = toEntry new epochtime $ B.fromChunks . (:[]) $ img
882+ modify $ \st -> st{ stMediaPaths =
883+ (oldsrc, (new, Just entry)):media}
884+ return new)
885+ (\e -> do
886+ P.warning $ "Could not find media `" ++ oldsrc ++
887+ "', skipping...\n" ++ show e
888+ return oldsrc)
889+
890+transformBlock :: PandocMonad m
891+ => WriterOptions
892+ -- -> IORef [(FilePath, (FilePath, Maybe Entry))] -- ^ (oldpath, newpath, entry) media
860893 -> Block
861− -> IO Block
862−transformBlock opts mediaRef (RawBlock fmt raw)
894+ -> E m Block
895+transformBlock opts (RawBlock fmt raw)
863896 | fmt == Format "html" = do
864897 let tags = parseTags raw
865− tags' <- mapM (transformTag opts mediaRef) tags
898+ tags' <- mapM (transformTag opts) tags
866899 return $ RawBlock fmt (renderTags' tags')
867−transformBlock _ _ b = return b
900+transformBlock _ b = return b
868901
869−transformInline :: WriterOptions
870− -> IORef [(FilePath, (FilePath, Maybe Entry))] -- ^ (oldpath, newpath) media
902+transformInline :: PandocMonad m
903+ => WriterOptions
904+ -- -> IORef [(FilePath, (FilePath, Maybe Entry))] -- ^ (oldpath, newpath) media
871905 -> Inline
872− -> IO Inline
873−transformInline opts mediaRef (Image attr lab (src,tit)) = do
874− newsrc <- modifyMediaRef opts mediaRef src
906+ -> E m Inline
907+transformInline opts (Image attr lab (src,tit)) = do
908+ newsrc <- modifyMediaRef opts src
875909 return $ Image attr lab (newsrc, tit)
876−transformInline opts mediaRef (x@(Math t m))
910+transformInline opts (x@(Math t m))
877911 | WebTeX url <- writerHTMLMathMethod opts = do
878− newsrc <- modifyMediaRef opts mediaRef (url ++ urlEncode m)
912+ newsrc <- modifyMediaRef opts (url ++ urlEncode m)
879913 let mathclass = if t == DisplayMath then "display" else "inline"
880914 return $ Span ("",["math",mathclass],[]) [Image nullAttr [x] (newsrc, "")]
881−transformInline opts mediaRef (RawInline fmt raw)
915+transformInline opts (RawInline fmt raw)
882916 | fmt == Format "html" = do
883917 let tags = parseTags raw
884− tags' <- mapM (transformTag opts mediaRef) tags
918+ tags' <- mapM (transformTag opts) tags
885919 return $ RawInline fmt (renderTags' tags')
886−transformInline _ _ x = return x
920+transformInline _ x = return x
887921
888922(!) :: (t -> Element) -> [(String, String)] -> t -> Element
889923(!) f attrs n = add_attrs (map (\(k,v) -> Attr (unqual k) v) attrs) (f n)
src/Text/Pandoc/Writers/FB2.hs+101−107
@@ -27,27 +27,28 @@ FictionBook is an XML-based e-book format. For more information see:
2727-}
2828module Text.Pandoc.Writers.FB2 (writeFB2) where
2929
30−import Control.Monad.State (StateT, evalStateT, get, modify)
31−import Control.Monad.State (liftM, liftM2, liftIO)
30+import Control.Monad.State (StateT, evalStateT, get, modify, lift)
31+import Control.Monad.State (liftM)
3232import Data.ByteString.Base64 (encode)
3333import Data.Char (toLower, isSpace, isAscii, isControl)
3434import Data.List (intersperse, intercalate, isPrefixOf, stripPrefix)
3535import Data.Either (lefts, rights)
36−import Network.Browser (browse, request, setAllowRedirects, setOutHandler)
37−import Network.HTTP (catchIO_, getRequest, getHeaders, getResponseBody)
38−import Network.HTTP (lookupHeader, HeaderName(..), urlEncode)
39−import Network.URI (isURI, unEscapeString)
40−import System.FilePath (takeExtension)
36+import Network.HTTP (urlEncode)
37+import Network.URI (isURI)
4138import Text.XML.Light
42−import qualified Control.Exception as E
43−import qualified Data.ByteString as B
4439import qualified Text.XML.Light as X
4540import qualified Text.XML.Light.Cursor as XC
41+import qualified Data.ByteString.Char8 as B8
42+import Control.Monad.Except (throwError, catchError)
43+
4644
4745import Text.Pandoc.Definition
4846import Text.Pandoc.Options (WriterOptions(..), HTMLMathMethod(..), def)
4947import Text.Pandoc.Shared (orderedListMarkers, isHeaderBlock, capitalize,
5048 linesToPara)
49+import Text.Pandoc.Error
50+import Text.Pandoc.Class (PandocMonad)
51+import qualified Text.Pandoc.Class as P
5152
5253-- | Data to be written at the end of the document:
5354-- (foot)notes, URLs, references, images.
@@ -60,7 +61,7 @@ data FbRenderState = FbRenderState
6061 } deriving (Show)
6162
6263-- | FictionBook building monad.
63−type FBM = StateT FbRenderState IO
64+type FBM m = StateT FbRenderState m
6465
6566newFB :: FbRenderState
6667newFB = FbRenderState { footnotes = [], imagesToFetch = []
@@ -73,17 +74,24 @@ instance Show ImageMode where
7374 show InlineImage = "inlineImageType"
7475
7576-- | Produce an FB2 document from a 'Pandoc' document.
76−writeFB2 :: WriterOptions -- ^ conversion options
77+writeFB2 :: PandocMonad m
78+ => WriterOptions -- ^ conversion options
7779 -> Pandoc -- ^ document to convert
78− -> IO String -- ^ FictionBook2 document (not encoded yet)
79−writeFB2 opts (Pandoc meta blocks) = flip evalStateT newFB $ do
80+ -> m String -- ^ FictionBook2 document (not encoded yet)
81+writeFB2 opts doc = flip evalStateT newFB $ pandocToFB2 opts doc
82+
83+pandocToFB2 :: PandocMonad m
84+ => WriterOptions
85+ -> Pandoc
86+ -> FBM m String
87+pandocToFB2 opts (Pandoc meta blocks) = do
8088 modify (\s -> s { writerOptions = opts })
8189 desc <- description meta
8290 fp <- frontpage meta
8391 secs <- renderSections 1 blocks
8492 let body = el "body" $ fp ++ secs
8593 notes <- renderFootnotes
86− (imgs,missing) <- liftM imagesToFetch get >>= \s -> liftIO (fetchImages s)
94+ (imgs,missing) <- liftM imagesToFetch get >>= \s -> lift (fetchImages s)
8795 let body' = replaceImagesWithAlt missing body
8896 let fb2_xml = el "FictionBook" (fb2_attrs, [desc, body'] ++ notes ++ imgs)
8997 return $ xml_head ++ (showContent fb2_xml) ++ "\n"
@@ -94,62 +102,67 @@ writeFB2 opts (Pandoc meta blocks) = flip evalStateT newFB $ do
94102 xlink = "http://www.w3.org/1999/xlink"
95103 in [ uattr "xmlns" xmlns
96104 , attr ("xmlns", "l") xlink ]
97− --
98− frontpage :: Meta -> FBM [Content]
99− frontpage meta' = do
100− t <- cMapM toXml . docTitle $ meta'
101− return $
102− [ el "title" (el "p" t)
103− , el "annotation" (map (el "p" . cMap plain)
104− (docAuthors meta' ++ [docDate meta']))
105− ]
106− description :: Meta -> FBM Content
107− description meta' = do
108− bt <- booktitle meta'
109− let as = authors meta'
110− dd <- docdate meta'
111− return $ el "description"
112− [ el "title-info" (bt ++ as ++ dd)
113− , el "document-info" [ el "program-used" "pandoc" ] -- FIXME: +version
114− ]
115− booktitle :: Meta -> FBM [Content]
116− booktitle meta' = do
117− t <- cMapM toXml . docTitle $ meta'
118− return $ if null t
119− then []
120− else [ el "book-title" t ]
121− authors :: Meta -> [Content]
122− authors meta' = cMap author (docAuthors meta')
123− author :: [Inline] -> [Content]
124− author ss =
125− let ws = words . cMap plain $ ss
126− email = (el "email") `fmap` (take 1 $ filter ('@' `elem`) ws)
127− ws' = filter ('@' `notElem`) ws
128− names = case ws' of
129− (nickname:[]) -> [ el "nickname" nickname ]
130− (fname:lname:[]) -> [ el "first-name" fname
131− , el "last-name" lname ]
132− (fname:rest) -> [ el "first-name" fname
133− , el "middle-name" (concat . init $ rest)
134− , el "last-name" (last rest) ]
135− ([]) -> []
136− in list $ el "author" (names ++ email)
137− docdate :: Meta -> FBM [Content]
138− docdate meta' = do
139− let ss = docDate meta'
140− d <- cMapM toXml ss
141− return $ if null d
142− then []
143− else [el "date" d]
105+
106+frontpage :: PandocMonad m => Meta -> FBM m [Content]
107+frontpage meta' = do
108+ t <- cMapM toXml . docTitle $ meta'
109+ return $
110+ [ el "title" (el "p" t)
111+ , el "annotation" (map (el "p" . cMap plain)
112+ (docAuthors meta' ++ [docDate meta']))
113+ ]
114+
115+description :: PandocMonad m => Meta -> FBM m Content
116+description meta' = do
117+ bt <- booktitle meta'
118+ let as = authors meta'
119+ dd <- docdate meta'
120+ return $ el "description"
121+ [ el "title-info" (bt ++ as ++ dd)
122+ , el "document-info" [ el "program-used" "pandoc" ] -- FIXME: +version
123+ ]
124+
125+booktitle :: PandocMonad m => Meta -> FBM m [Content]
126+booktitle meta' = do
127+ t <- cMapM toXml . docTitle $ meta'
128+ return $ if null t
129+ then []
130+ else [ el "book-title" t ]
131+
132+authors :: Meta -> [Content]
133+authors meta' = cMap author (docAuthors meta')
134+
135+author :: [Inline] -> [Content]
136+author ss =
137+ let ws = words . cMap plain $ ss
138+ email = (el "email") `fmap` (take 1 $ filter ('@' `elem`) ws)
139+ ws' = filter ('@' `notElem`) ws
140+ names = case ws' of
141+ (nickname:[]) -> [ el "nickname" nickname ]
142+ (fname:lname:[]) -> [ el "first-name" fname
143+ , el "last-name" lname ]
144+ (fname:rest) -> [ el "first-name" fname
145+ , el "middle-name" (concat . init $ rest)
146+ , el "last-name" (last rest) ]
147+ ([]) -> []
148+ in list $ el "author" (names ++ email)
149+
150+docdate :: PandocMonad m => Meta -> FBM m [Content]
151+docdate meta' = do
152+ let ss = docDate meta'
153+ d <- cMapM toXml ss
154+ return $ if null d
155+ then []
156+ else [el "date" d]
144157
145158-- | Divide the stream of blocks into sections and convert to XML
146159-- representation.
147−renderSections :: Int -> [Block] -> FBM [Content]
160+renderSections :: PandocMonad m => Int -> [Block] -> FBM m [Content]
148161renderSections level blocks = do
149162 let secs = splitSections level blocks
150163 mapM (renderSection level) secs
151164
152−renderSection :: Int -> ([Inline], [Block]) -> FBM Content
165+renderSection :: PandocMonad m => Int -> ([Inline], [Block]) -> FBM m Content
153166renderSection level (ttl, body) = do
154167 title <- if null ttl
155168 then return []
@@ -196,7 +209,7 @@ splitSections level blocks = reverse $ revSplit (reverse blocks)
196209 sameLevel _ = False
197210
198211-- | Make another FictionBook body with footnotes.
199−renderFootnotes :: FBM [Content]
212+renderFootnotes :: PandocMonad m => FBM m [Content]
200213renderFootnotes = do
201214 fns <- footnotes `liftM` get
202215 if null fns
@@ -210,14 +223,14 @@ renderFootnotes = do
210223
211224-- | Fetch images and encode them for the FictionBook XML.
212225-- Return image data and a list of hrefs of the missing images.
213−fetchImages :: [(String,String)] -> IO ([Content],[String])
226+fetchImages :: PandocMonad m => [(String,String)] -> m ([Content],[String])
214227fetchImages links = do
215228 imgs <- mapM (uncurry fetchImage) links
216229 return $ (rights imgs, lefts imgs)
217230
218231-- | Fetch image data from disk or from network and make a <binary> XML section.
219232-- Return either (Left hrefOfMissingImage) or (Right xmlContent).
220−fetchImage :: String -> String -> IO (Either String Content)
233+fetchImage :: PandocMonad m => String -> String -> m (Either String Content)
221234fetchImage href link = do
222235 mbimg <-
223236 case (isURI link, readDataURI link) of
@@ -227,16 +240,19 @@ fetchImage href link = do
227240 then return (Just (mime',base64))
228241 else return Nothing
229242 (True, Just _) -> return Nothing -- not base64-encoded
230− (True, Nothing) -> fetchURL link
231− (False, _) -> do
232− d <- nothingOnError $ B.readFile (unEscapeString link)
233− let t = case map toLower (takeExtension link) of
234− ".png" -> Just "image/png"
235− ".jpg" -> Just "image/jpeg"
236− ".jpeg" -> Just "image/jpeg"
237− ".jpe" -> Just "image/jpeg"
238− _ -> Nothing -- only PNG and JPEG are supported in FB2
239− return $ liftM2 (,) t (liftM (toStr . encode) d)
243+ _ -> do
244+ catchError (do (bs, mbmime) <- P.fetchItem Nothing link
245+ case mbmime of
246+ Nothing -> do
247+ P.warning ("Could not determine mime type for "
248+ ++ link)
249+ return Nothing
250+ Just mime -> return $ Just (mime,
251+ B8.unpack $ encode bs))
252+ (\e ->
253+ do P.warning ("Could not fetch " ++ link ++
254+ ":\n" ++ show e)
255+ return Nothing)
240256 case mbimg of
241257 Just (imgtype, imgdata) -> do
242258 return . Right $ el "binary"
@@ -244,11 +260,7 @@ fetchImage href link = do
244260 , uattr "content-type" imgtype]
245261 , txt imgdata )
246262 _ -> return (Left ('#':href))
247− where
248− nothingOnError :: (IO B.ByteString) -> (IO (Maybe B.ByteString))
249− nothingOnError action = liftM Just action `E.catch` omnihandler
250− omnihandler :: E.SomeException -> IO (Maybe B.ByteString)
251− omnihandler _ = return Nothing
263+
252264
253265-- | Extract mime type and encoded data from the Data URI.
254266readDataURI :: String -- ^ URI
@@ -286,24 +298,6 @@ isMimeType s =
286298 valid c = isAscii c && not (isControl c) && not (isSpace c) &&
287299 c `notElem` "()<>@,;:\\\"/[]?="
288300
289−-- | Fetch URL, return its Content-Type and binary data on success.
290−fetchURL :: String -> IO (Maybe (String, String))
291−fetchURL url = do
292− flip catchIO_ (return Nothing) $ do
293− r <- browse $ do
294− setOutHandler (const (return ()))
295− setAllowRedirects True
296− liftM snd . request . getRequest $ url
297− let content_type = lookupHeader HdrContentType (getHeaders r)
298− content <- liftM (Just . toStr . encode . toBS) . getResponseBody $ Right r
299− return $ liftM2 (,) content_type content
300−
301−toBS :: String -> B.ByteString
302−toBS = B.pack . map (toEnum . fromEnum)
303−
304−toStr :: B.ByteString -> String
305−toStr = map (toEnum . fromEnum) . B.unpack
306−
307301footnoteID :: Int -> String
308302footnoteID i = "n" ++ (show i)
309303
@@ -311,7 +305,7 @@ linkID :: Int -> String
311305linkID i = "l" ++ (show i)
312306
313307-- | Convert a block-level Pandoc's element to FictionBook XML representation.
314−blockToXml :: Block -> FBM [Content]
308+blockToXml :: PandocMonad m => Block -> FBM m [Content]
315309blockToXml (Plain ss) = cMapM toXml ss -- FIXME: can lead to malformed FB2
316310blockToXml (Para [Math DisplayMath formula]) = insertMath NormalImage formula
317311-- title beginning with fig: indicates that the image is a figure
@@ -364,7 +358,7 @@ blockToXml (DefinitionList defs) =
364358 needsBreak (Plain ins) = LineBreak `notElem` ins
365359 needsBreak _ = True
366360blockToXml (Header _ _ _) = -- should never happen, see renderSections
367− error "unexpected header in section text"
361+ throwError $ PandocShouldNeverHappenError "unexpected header in section text"
368362blockToXml HorizontalRule = return
369363 [ el "empty-line" ()
370364 , el "p" (txt (replicate 10 '—'))
@@ -375,11 +369,11 @@ blockToXml (Table caption aligns _ headers rows) = do
375369 c <- return . el "emphasis" =<< cMapM toXml caption
376370 return [el "table" (hd : bd), el "p" c]
377371 where
378− mkrow :: String -> [TableCell] -> [Alignment] -> FBM Content
372+ mkrow :: PandocMonad m => String -> [TableCell] -> [Alignment] -> FBM m Content
379373 mkrow tag cells aligns' =
380374 (el "tr") `liftM` (mapM (mkcell tag) (zip cells aligns'))
381375 --
382− mkcell :: String -> (TableCell, Alignment) -> FBM Content
376+ mkcell :: PandocMonad m => String -> (TableCell, Alignment) -> FBM m Content
383377 mkcell tag (cell, align) = do
384378 cblocks <- cMapM blockToXml cell
385379 return $ el tag ([align_attr align], cblocks)
@@ -423,7 +417,7 @@ indent = indentBlock
423417 in intercalate [LineBreak] $ map ((Str spacer):) lns
424418
425419-- | Convert a Pandoc's Inline element to FictionBook XML representation.
426−toXml :: Inline -> FBM [Content]
420+toXml :: PandocMonad m => Inline -> FBM m [Content]
427421toXml (Str s) = return [txt s]
428422toXml (Span _ ils) = cMapM toXml ils
429423toXml (Emph ss) = list `liftM` wrap "emphasis" ss
@@ -474,7 +468,7 @@ toXml (Note bs) = do
474468 , uattr "type" "note" ]
475469 , fn_ref )
476470
477−insertMath :: ImageMode -> String -> FBM [Content]
471+insertMath :: PandocMonad m => ImageMode -> String -> FBM m [Content]
478472insertMath immode formula = do
479473 htmlMath <- return . writerHTMLMathMethod . writerOptions =<< get
480474 case htmlMath of
@@ -485,7 +479,7 @@ insertMath immode formula = do
485479 insertImage immode img
486480 _ -> return [el "code" formula]
487481
488−insertImage :: ImageMode -> Inline -> FBM [Content]
482+insertImage :: PandocMonad m => ImageMode -> Inline -> FBM m [Content]
489483insertImage immode (Image _ alt (url,ttl)) = do
490484 images <- imagesToFetch `liftM` get
491485 let n = 1 + length images
@@ -551,7 +545,7 @@ replaceImagesWithAlt missingHrefs body =
551545
552546
553547-- | Wrap all inlines with an XML tag (given its unqualified name).
554−wrap :: String -> [Inline] -> FBM Content
548+wrap :: PandocMonad m => String -> [Inline] -> FBM m Content
555549wrap tagname inlines = el tagname `liftM` cMapM toXml inlines
556550
557551-- " Create a singleton list.
src/Text/Pandoc/Writers/HTML.hs+244−127
@@ -28,15 +28,27 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2828
2929Conversion of 'Pandoc' documents to HTML.
3030-}
31−module Text.Pandoc.Writers.HTML ( writeHtml , writeHtmlString ) where
31+module Text.Pandoc.Writers.HTML (
32+ writeHtml4,
33+ writeHtml4String,
34+ writeHtml5,
35+ writeHtml5String,
36+ writeHtmlStringForEPUB,
37+ writeS5,
38+ writeSlidy,
39+ writeSlideous,
40+ writeDZSlides,
41+ writeRevealJs
42+ ) where
3243import Text.Pandoc.Definition
44+import Text.Pandoc.Walk
3345import Data.Monoid ((<>))
3446import Text.Pandoc.Shared
3547import Text.Pandoc.Writers.Shared
3648import Text.Pandoc.Options
3749import Text.Pandoc.ImageSize
3850import Text.Pandoc.Templates
39−import Text.Pandoc.Readers.TeXMath
51+import Text.Pandoc.Writers.Math
4052import Text.Pandoc.Slides
4153import Text.Pandoc.Highlighting ( highlight, styleToCss,
4254 formatHtmlInline, formatHtmlBlock )
@@ -68,6 +80,9 @@ import Text.XML.Light (unode, elChildren, unqual)
6880import qualified Text.XML.Light as XML
6981import System.FilePath (takeExtension)
7082import Data.Aeson (Value)
83+import Control.Monad.Except (throwError)
84+import Text.Pandoc.Error
85+import Text.Pandoc.Class (PandocMonad)
7186
7287data WriterState = WriterState
7388 { stNotes :: [Html] -- ^ List of notes
@@ -76,12 +91,17 @@ data WriterState = WriterState
7691 , stHighlighting :: Bool -- ^ Syntax highlighting is used
7792 , stSecNum :: [Int] -- ^ Number of current section
7893 , stElement :: Bool -- ^ Processing an Element
94+ , stHtml5 :: Bool -- ^ Use HTML5
95+ , stEPUBVersion :: Maybe EPUBVersion -- ^ EPUB version if for epub
96+ , stSlideVariant :: HTMLSlideVariant
7997 }
8098
8199defaultWriterState :: WriterState
82100defaultWriterState = WriterState {stNotes= [], stMath = False, stQuotes = False,
83101 stHighlighting = False, stSecNum = [],
84− stElement = False}
102+ stElement = False, stHtml5 = False,
103+ stEPUBVersion = Nothing,
104+ stSlideVariant = NoSlides}
85105
86106-- Helpers to render HTML with the appropriate function.
87107
@@ -98,28 +118,91 @@ nl opts = if writerWrapText opts == WrapNone
98118 then mempty
99119 else preEscapedString "\n"
100120
101−-- | Convert Pandoc document to Html string.
102−writeHtmlString :: WriterOptions -> Pandoc -> String
103−writeHtmlString opts d =
104− let (body, context) = evalState (pandocToHtml opts d) defaultWriterState
105− in case writerTemplate opts of
106− Nothing -> renderHtml body
107− Just tpl -> renderTemplate' tpl $
108− defField "body" (renderHtml body) context
109−
110−-- | Convert Pandoc document to Html structure.
111−writeHtml :: WriterOptions -> Pandoc -> Html
112−writeHtml opts d =
113− let (body, context) = evalState (pandocToHtml opts d) defaultWriterState
114− in case writerTemplate opts of
115− Nothing -> body
116− Just tpl -> renderTemplate' tpl $
117− defField "body" (renderHtml body) context
121+-- | Convert Pandoc document to Html 5 string.
122+writeHtml5String :: PandocMonad m => WriterOptions -> Pandoc -> m String
123+writeHtml5String = writeHtmlString'
124+ defaultWriterState{ stHtml5 = True }
125+
126+-- | Convert Pandoc document to Html 5 structure.
127+writeHtml5 :: PandocMonad m => WriterOptions -> Pandoc -> m Html
128+writeHtml5 = writeHtml' defaultWriterState{ stHtml5 = True }
129+
130+-- | Convert Pandoc document to Html 4 string.
131+writeHtml4String :: PandocMonad m => WriterOptions -> Pandoc -> m String
132+writeHtml4String = writeHtmlString'
133+ defaultWriterState{ stHtml5 = False }
134+
135+-- | Convert Pandoc document to Html 4 structure.
136+writeHtml4 :: PandocMonad m => WriterOptions -> Pandoc -> m Html
137+writeHtml4 = writeHtml' defaultWriterState{ stHtml5 = False }
138+
139+-- | Convert Pandoc document to Html appropriate for an epub version.
140+writeHtmlStringForEPUB :: PandocMonad m
141+ => EPUBVersion -> WriterOptions -> Pandoc -> m String
142+writeHtmlStringForEPUB version = writeHtmlString'
143+ defaultWriterState{ stHtml5 = version == EPUB3,
144+ stEPUBVersion = Just version }
145+
146+-- | Convert Pandoc document to Reveal JS HTML slide show.
147+writeRevealJs :: PandocMonad m
148+ => WriterOptions -> Pandoc -> m String
149+writeRevealJs = writeHtmlSlideShow' RevealJsSlides
150+
151+-- | Convert Pandoc document to S5 HTML slide show.
152+writeS5 :: PandocMonad m
153+ => WriterOptions -> Pandoc -> m String
154+writeS5 = writeHtmlSlideShow' S5Slides
155+
156+-- | Convert Pandoc document to Slidy HTML slide show.
157+writeSlidy :: PandocMonad m
158+ => WriterOptions -> Pandoc -> m String
159+writeSlidy = writeHtmlSlideShow' SlidySlides
160+
161+-- | Convert Pandoc document to Slideous HTML slide show.
162+writeSlideous :: PandocMonad m
163+ => WriterOptions -> Pandoc -> m String
164+writeSlideous = writeHtmlSlideShow' SlideousSlides
165+
166+-- | Convert Pandoc document to DZSlides HTML slide show.
167+writeDZSlides :: PandocMonad m
168+ => WriterOptions -> Pandoc -> m String
169+writeDZSlides = writeHtmlSlideShow' DZSlides
170+
171+writeHtmlSlideShow' :: PandocMonad m
172+ => HTMLSlideVariant -> WriterOptions -> Pandoc -> m String
173+writeHtmlSlideShow' variant = writeHtmlString'
174+ defaultWriterState{ stSlideVariant = variant
175+ , stHtml5 = case variant of
176+ RevealJsSlides -> True
177+ S5Slides -> False
178+ SlidySlides -> False
179+ DZSlides -> True
180+ SlideousSlides -> False
181+ NoSlides -> False
182+ }
183+
184+writeHtmlString' :: PandocMonad m
185+ => WriterState -> WriterOptions -> Pandoc -> m String
186+writeHtmlString' st opts d = do
187+ (body, context) <- evalStateT (pandocToHtml opts d) st
188+ return $ case writerTemplate opts of
189+ Nothing -> renderHtml body
190+ Just tpl -> renderTemplate' tpl $
191+ defField "body" (renderHtml body) context
192+
193+writeHtml' :: PandocMonad m => WriterState -> WriterOptions -> Pandoc -> m Html
194+writeHtml' st opts d = do
195+ (body, context) <- evalStateT (pandocToHtml opts d) st
196+ return $ case writerTemplate opts of
197+ Nothing -> body
198+ Just tpl -> renderTemplate' tpl $
199+ defField "body" (renderHtml body) context
118200
119201-- result is (title, authors, date, toc, body, new variables)
120−pandocToHtml :: WriterOptions
202+pandocToHtml :: PandocMonad m
203+ => WriterOptions
121204 -> Pandoc
122− -> State WriterState (Html, Value)
205+ -> StateT WriterState m (Html, Value)
123206pandocToHtml opts (Pandoc meta blocks) = do
124207 metadata <- metaToJSON opts
125208 (fmap renderHtml . blockListToHtml opts)
@@ -129,18 +212,19 @@ pandocToHtml opts (Pandoc meta blocks) = do
129212 let authsMeta = map stringifyHTML $ docAuthors meta
130213 let dateMeta = stringifyHTML $ docDate meta
131214 let slideLevel = fromMaybe (getSlideLevel blocks) $ writerSlideLevel opts
215+ slideVariant <- gets stSlideVariant
132216 let sects = hierarchicalize $
133− if writerSlideVariant opts == NoSlides
217+ if slideVariant == NoSlides
134218 then blocks
135219 else prepSlides slideLevel blocks
136− toc <- if writerTableOfContents opts
220+ toc <- if writerTableOfContents opts && slideVariant /= S5Slides
137221 then tableOfContents opts sects
138222 else return Nothing
139223 blocks' <- liftM (mconcat . intersperse (nl opts)) $
140224 mapM (elementToHtml slideLevel opts) sects
141225 st <- get
142− let notes = reverse (stNotes st)
143− let thebody = blocks' >> footnoteSection opts notes
226+ notes <- footnoteSection opts (reverse (stNotes st))
227+ let thebody = blocks' >> notes
144228 let math = case writerHTMLMathMethod opts of
145229 LaTeXMathML (Just url) ->
146230 H.script ! A.src (toValue url)
@@ -153,7 +237,7 @@ pandocToHtml opts (Pandoc meta blocks) = do
153237 MathJax url ->
154238 H.script ! A.src (toValue url)
155239 ! A.type_ "text/javascript"
156− $ case writerSlideVariant opts of
240+ $ case slideVariant of
157241 SlideousSlides ->
158242 preEscapedString
159243 "MathJax.Hub.Queue([\"Typeset\",MathJax.Hub]);"
@@ -167,15 +251,17 @@ pandocToHtml opts (Pandoc meta blocks) = do
167251 (H.link ! A.rel "stylesheet" ! A.href (toValue css)) <>
168252 (H.script ! A.type_ "text/javascript" $ toHtml renderKaTeX)
169253 _ -> case lookup "mathml-script" (writerVariables opts) of
170− Just s | not (writerHtml5 opts) ->
254+ Just s | not (stHtml5 st) ->
171255 H.script ! A.type_ "text/javascript"
172256 $ preEscapedString
173257 ("/*<![CDATA[*/\n" ++ s ++ "/*]]>*/\n")
174258 | otherwise -> mempty
175259 Nothing -> mempty
176260 let context = (if stHighlighting st
177− then defField "highlighting-css"
178− (styleToCss $ writerHighlightStyle opts)
261+ then case writerHighlightStyle opts of
262+ Just sty -> defField "highlighting-css"
263+ (styleToCss sty)
264+ Nothing -> id
179265 else id) $
180266 (if stMath st
181267 then defField "math" (renderHtml math)
@@ -192,7 +278,7 @@ pandocToHtml opts (Pandoc meta blocks) = do
192278 defField "slideous-url" ("slideous" :: String) $
193279 defField "revealjs-url" ("reveal.js" :: String) $
194280 defField "s5-url" ("s5/default" :: String) $
195− defField "html5" (writerHtml5 opts) $
281+ defField "html5" (stHtml5 st) $
196282 metadata
197283 return (thebody, context)
198284
@@ -203,33 +289,41 @@ prefixedId opts s =
203289 "" -> mempty
204290 _ -> A.id $ toValue $ writerIdentifierPrefix opts ++ s
205291
206−toList :: (Html -> Html) -> WriterOptions -> ([Html] -> Html)
292+toList :: PandocMonad m
293+ => (Html -> Html)
294+ -> WriterOptions
295+ -> [Html]
296+ -> StateT WriterState m Html
207297toList listop opts items = do
208− if (writerIncremental opts)
209− then if (writerSlideVariant opts /= RevealJsSlides)
210− then (listop $ mconcat items) ! A.class_ "incremental"
211− else listop $ mconcat $ map (! A.class_ "fragment") items
212− else listop $ mconcat items
298+ slideVariant <- gets stSlideVariant
299+ return $
300+ if (writerIncremental opts)
301+ then if (slideVariant /= RevealJsSlides)
302+ then (listop $ mconcat items) ! A.class_ "incremental"
303+ else listop $ mconcat $ map (! A.class_ "fragment") items
304+ else listop $ mconcat items
213305
214−unordList :: WriterOptions -> [Html] -> Html
306+unordList :: PandocMonad m
307+ => WriterOptions -> [Html] -> StateT WriterState m Html
215308unordList opts = toList H.ul opts . toListItems opts
216309
217−ordList :: WriterOptions -> [Html] -> Html
310+ordList :: PandocMonad m
311+ => WriterOptions -> [Html] -> StateT WriterState m Html
218312ordList opts = toList H.ol opts . toListItems opts
219313
220−defList :: WriterOptions -> [Html] -> Html
314+defList :: PandocMonad m
315+ => WriterOptions -> [Html] -> StateT WriterState m Html
221316defList opts items = toList H.dl opts (items ++ [nl opts])
222317
223318-- | Construct table of contents from list of elements.
224−tableOfContents :: WriterOptions -> [Element] -> State WriterState (Maybe Html)
319+tableOfContents :: PandocMonad m => WriterOptions -> [Element] -> StateT WriterState m (Maybe Html)
225320tableOfContents _ [] = return Nothing
226321tableOfContents opts sects = do
227− let opts' = opts { writerIgnoreNotes = True }
228− contents <- mapM (elementToListItem opts') sects
322+ contents <- mapM (elementToListItem opts) sects
229323 let tocList = catMaybes contents
230− return $ if null tocList
231− then Nothing
232− else Just $ unordList opts tocList
324+ if null tocList
325+ then return Nothing
326+ else Just <$> unordList opts tocList
233327
234328-- | Convert section number to string
235329showSecNum :: [Int] -> String
@@ -237,7 +331,7 @@ showSecNum = concat . intersperse "." . map show
237331
238332-- | Converts an Element to a list item for a table of contents,
239333-- retrieving the appropriate identifier from state.
240−elementToListItem :: WriterOptions -> Element -> State WriterState (Maybe Html)
334+elementToListItem :: PandocMonad m => WriterOptions -> Element -> StateT WriterState m (Maybe Html)
241335-- Don't include the empty headers created in slide shows
242336-- shows when an hrule is used to separate slides without a new title:
243337elementToListItem _ (Sec _ _ _ [Str "\0"] _) = return Nothing
@@ -249,13 +343,14 @@ elementToListItem opts (Sec lev num (id',classes,_) headerText subsecs)
249343 then (H.span ! A.class_ "toc-section-number"
250344 $ toHtml $ showSecNum num') >> preEscapedString " "
251345 else mempty
252− txt <- liftM (sectnum >>) $ inlineListToHtml opts headerText
346+ txt <- liftM (sectnum >>) $ inlineListToHtml opts $ walk deNote headerText
253347 subHeads <- mapM (elementToListItem opts) subsecs >>= return . catMaybes
254− let subList = if null subHeads
255− then mempty
256− else unordList opts subHeads
348+ subList <- if null subHeads
349+ then return mempty
350+ else unordList opts subHeads
257351 -- in reveal.js, we need #/apples, not #apples:
258− let revealSlash = ['/' | writerSlideVariant opts == RevealJsSlides]
352+ slideVariant <- gets stSlideVariant
353+ let revealSlash = ['/' | slideVariant== RevealJsSlides]
259354 return $ Just
260355 $ if null id'
261356 then (H.a $ toHtml txt) >> subList
@@ -265,12 +360,14 @@ elementToListItem opts (Sec lev num (id',classes,_) headerText subsecs)
265360elementToListItem _ _ = return Nothing
266361
267362-- | Convert an Element to Html.
268−elementToHtml :: Int -> WriterOptions -> Element -> State WriterState Html
363+elementToHtml :: PandocMonad m => Int -> WriterOptions -> Element -> StateT WriterState m Html
269364elementToHtml _slideLevel opts (Blk block) = blockToHtml opts block
270365elementToHtml slideLevel opts (Sec level num (id',classes,keyvals) title' elements) = do
271− let slide = writerSlideVariant opts /= NoSlides && level <= slideLevel
366+ slideVariant <- gets stSlideVariant
367+ let slide = slideVariant /= NoSlides && level <= slideLevel
272368 let num' = zipWith (+) num (writerNumberOffset opts ++ repeat 0)
273369 modify $ \st -> st{stSecNum = num'} -- update section number
370+ html5 <- gets stHtml5
274371 let titleSlide = slide && level < slideLevel
275372 header' <- if title' == [Str "\0"] -- marker for hrule
276373 then return mempty
@@ -285,7 +382,7 @@ elementToHtml slideLevel opts (Sec level num (id',classes,keyvals) title' elemen
285382 isSec (Blk _) = False
286383 let isPause (Blk x) = x == Para [Str ".",Space,Str ".",Space,Str "."]
287384 isPause _ = False
288− let fragmentClass = case writerSlideVariant opts of
385+ let fragmentClass = case slideVariant of
289386 RevealJsSlides -> "fragment"
290387 _ -> "incremental"
291388 let inDiv xs = Blk (RawBlock (Format "html") ("<div class=\""
@@ -301,15 +398,15 @@ elementToHtml slideLevel opts (Sec level num (id',classes,keyvals) title' elemen
301398 let inNl x = mconcat $ nl opts : intersperse (nl opts) x ++ [nl opts]
302399 let classes' = ["titleslide" | titleSlide] ++ ["slide" | slide] ++
303400 ["section" | (slide || writerSectionDivs opts) &&
304− not (writerHtml5 opts) ] ++
401+ not html5 ] ++
305402 ["level" ++ show level | slide || writerSectionDivs opts ]
306403 ++ classes
307− let secttag = if writerHtml5 opts
404+ let secttag = if html5
308405 then H5.section
309406 else H.div
310407 let attr = (id',classes',keyvals)
311408 return $ if titleSlide
312− then (if writerSlideVariant opts == RevealJsSlides
409+ then (if slideVariant == RevealJsSlides
313410 then H5.section
314411 else id) $ mconcat $
315412 (addAttrs opts attr $ secttag $ header') : innerContents
@@ -321,19 +418,23 @@ elementToHtml slideLevel opts (Sec level num (id',classes,keyvals) title' elemen
321418
322419-- | Convert list of Note blocks to a footnote <div>.
323420-- Assumes notes are sorted.
324−footnoteSection :: WriterOptions -> [Html] -> Html
325−footnoteSection opts notes =
326− if null notes
327− then mempty
328− else nl opts >> (container
329− $ nl opts >> hrtag >> nl opts >>
330− H.ol (mconcat notes >> nl opts) >> nl opts)
331− where container x = if writerHtml5 opts
332− then H5.section ! A.class_ "footnotes" $ x
333− else if writerSlideVariant opts /= NoSlides
334− then H.div ! A.class_ "footnotes slide" $ x
335− else H.div ! A.class_ "footnotes" $ x
336− hrtag = if writerHtml5 opts then H5.hr else H.hr
421+footnoteSection :: PandocMonad m
422+ => WriterOptions -> [Html] -> StateT WriterState m Html
423+footnoteSection opts notes = do
424+ html5 <- gets stHtml5
425+ slideVariant <- gets stSlideVariant
426+ let hrtag = if html5 then H5.hr else H.hr
427+ let container x = if html5
428+ then H5.section ! A.class_ "footnotes" $ x
429+ else if slideVariant /= NoSlides
430+ then H.div ! A.class_ "footnotes slide" $ x
431+ else H.div ! A.class_ "footnotes" $ x
432+ return $
433+ if null notes
434+ then mempty
435+ else nl opts >> (container
436+ $ nl opts >> hrtag >> nl opts >>
437+ H.ol (mconcat notes >> nl opts) >> nl opts)
337438
338439-- | Parse a mailto link; return Just (name, domain) or Nothing.
339440parseMailto :: String -> Maybe (String, String)
@@ -346,9 +447,9 @@ parseMailto s = do
346447 _ -> fail "not a mailto: URL"
347448
348449-- | Obfuscate a "mailto:" link.
349−obfuscateLink :: WriterOptions -> Attr -> Html -> String -> Html
450+obfuscateLink :: PandocMonad m => WriterOptions -> Attr -> Html -> String -> m Html
350451obfuscateLink opts attr txt s | writerEmailObfuscation opts == NoObfuscation =
351− addAttrs opts attr $ H.a ! A.href (toValue s) $ txt
452+ return $ addAttrs opts attr $ H.a ! A.href (toValue s) $ txt
352453obfuscateLink opts attr (renderHtml -> txt) s =
353454 let meth = writerEmailObfuscation opts
354455 s' = map toLower (take 7 s) ++ drop 7 s
@@ -364,9 +465,11 @@ obfuscateLink opts attr (renderHtml -> txt) s =
364465 in case meth of
365466 ReferenceObfuscation ->
366467 -- need to use preEscapedString or &'s are escaped to &amp; in URL
468+ return $
367469 preEscapedString $ "<a href=\"" ++ (obfuscateString s')
368470 ++ "\" class=\"email\">" ++ (obfuscateString txt) ++ "</a>"
369471 JavascriptObfuscation ->
472+ return $
370473 (H.script ! A.type_ "text/javascript" $
371474 preEscapedString ("\n<!--\nh='" ++
372475 obfuscateString domain ++ "';a='" ++ at' ++ "';n='" ++
@@ -374,8 +477,8 @@ obfuscateLink opts attr (renderHtml -> txt) s =
374477 "document.write('<a h'+'ref'+'=\"ma'+'ilto'+':'+e+'\" clas'+'s=\"em' + 'ail\">'+" ++
375478 linkText ++ "+'<\\/'+'a'+'>');\n// -->\n")) >>
376479 H.noscript (preEscapedString $ obfuscateString altText)
377− _ -> error $ "Unknown obfuscation method: " ++ show meth
378− _ -> addAttrs opts attr $ H.a ! A.href (toValue s) $ toHtml txt -- malformed email
480+ _ -> throwError $ PandocSomeError $ "Unknown obfuscation method: " ++ show meth
481+ _ -> return $ addAttrs opts attr $ H.a ! A.href (toValue s) $ toHtml txt -- malformed email
379482
380483-- | Obfuscate character as entity.
381484obfuscateChar :: Char -> String
@@ -434,19 +537,20 @@ treatAsImage fp =
434537 in null ext || ext `elem` imageExts
435538
436539-- | Convert Pandoc block element to HTML.
437−blockToHtml :: WriterOptions -> Block -> State WriterState Html
540+blockToHtml :: PandocMonad m => WriterOptions -> Block -> StateT WriterState m Html
438541blockToHtml _ Null = return mempty
439542blockToHtml opts (Plain lst) = inlineListToHtml opts lst
440543-- title beginning with fig: indicates that the image is a figure
441544blockToHtml opts (Para [Image attr txt (s,'f':'i':'g':':':tit)]) = do
442545 img <- inlineToHtml opts (Image attr txt (s,tit))
443− let tocapt = if writerHtml5 opts
546+ html5 <- gets stHtml5
547+ let tocapt = if html5
444548 then H5.figcaption
445549 else H.p ! A.class_ "caption"
446550 capt <- if null txt
447551 then return mempty
448552 else tocapt `fmap` inlineListToHtml opts txt
449− return $ if writerHtml5 opts
553+ return $ if html5
450554 then H5.figure $ mconcat
451555 [nl opts, img, capt, nl opts]
452556 else H.div ! A.class_ "figure" $ mconcat
@@ -467,17 +571,19 @@ blockToHtml opts (LineBlock lns) =
467571 htmlLines <- mconcat . intersperse lf <$> mapM (inlineListToHtml opts) lns
468572 return $ H.div ! A.style "white-space: pre-line;" $ htmlLines
469573blockToHtml opts (Div attr@(ident, classes, kvs) bs) = do
574+ html5 <- gets stHtml5
470575 let speakerNotes = "notes" `elem` classes
471576 -- we don't want incremental output inside speaker notes, see #1394
472577 let opts' = if speakerNotes then opts{ writerIncremental = False } else opts
473578 contents <- blockListToHtml opts' bs
474579 let contents' = nl opts >> contents >> nl opts
475− let (divtag, classes') = if writerHtml5 opts && "section" `elem` classes
580+ let (divtag, classes') = if html5 && "section" `elem` classes
476581 then (H5.section, filter (/= "section") classes)
477582 else (H.div, classes)
583+ slideVariant <- gets stSlideVariant
478584 return $
479585 if speakerNotes
480− then case writerSlideVariant opts of
586+ then case slideVariant of
481587 RevealJsSlides -> addAttrs opts' attr $ H5.aside $ contents'
482588 DZSlides -> (addAttrs opts' attr $ H5.div $ contents')
483589 ! (H5.customAttribute "role" "note")
@@ -490,7 +596,9 @@ blockToHtml opts (RawBlock f str)
490596 allowsMathEnvironments (writerHTMLMathMethod opts) &&
491597 isMathEnvironment str = blockToHtml opts $ Plain [Math DisplayMath str]
492598 | otherwise = return mempty
493−blockToHtml opts (HorizontalRule) = return $ if writerHtml5 opts then H5.hr else H.hr
599+blockToHtml _ (HorizontalRule) = do
600+ html5 <- gets stHtml5
601+ return $ if html5 then H5.hr else H.hr
494602blockToHtml opts (CodeBlock (id',classes,keyvals) rawCode) = do
495603 let tolhs = isEnabled Ext_literate_haskell opts &&
496604 any (\c -> map toLower c == "haskell") classes &&
@@ -503,19 +611,21 @@ blockToHtml opts (CodeBlock (id',classes,keyvals) rawCode) = do
503611 adjCode = if tolhs
504612 then unlines . map ("> " ++) . lines $ rawCode
505613 else rawCode
506− hlCode = if writerHighlight opts -- check highlighting options
507− then highlight formatHtmlBlock (id',classes',keyvals) adjCode
614+ hlCode = if isJust (writerHighlightStyle opts)
615+ then highlight formatHtmlBlock
616+ (id',classes',keyvals) adjCode
508617 else Nothing
509618 case hlCode of
510619 Nothing -> return $ addAttrs opts (id',classes,keyvals)
511620 $ H.pre $ H.code $ toHtml adjCode
512621 Just h -> modify (\st -> st{ stHighlighting = True }) >>
513622 return (addAttrs opts (id',[],keyvals) h)
514−blockToHtml opts (BlockQuote blocks) =
623+blockToHtml opts (BlockQuote blocks) = do
515624 -- in S5, treat list in blockquote specially
516625 -- if default is incremental, make it nonincremental;
517626 -- otherwise incremental
518− if writerSlideVariant opts /= NoSlides
627+ slideVariant <- gets stSlideVariant
628+ if slideVariant /= NoSlides
519629 then let inc = not (writerIncremental opts) in
520630 case blocks of
521631 [BulletList lst] -> blockToHtml (opts {writerIncremental = inc})
@@ -552,9 +662,10 @@ blockToHtml opts (Header level attr@(_,classes,_) lst) = do
552662 _ -> H.p contents'
553663blockToHtml opts (BulletList lst) = do
554664 contents <- mapM (blockListToHtml opts) lst
555− return $ unordList opts contents
665+ unordList opts contents
556666blockToHtml opts (OrderedList (startnum, numstyle, _) lst) = do
557667 contents <- mapM (blockListToHtml opts) lst
668+ html5 <- gets stHtml5
558669 let numstyle' = case numstyle of
559670 Example -> "decimal"
560671 _ -> camelCaseToHyphenated $ show numstyle
@@ -565,7 +676,7 @@ blockToHtml opts (OrderedList (startnum, numstyle, _) lst) = do
565676 then [A.class_ "example"]
566677 else []) ++
567678 (if numstyle /= DefaultStyle
568− then if writerHtml5 opts
679+ then if html5
569680 then [A.type_ $
570681 case numstyle of
571682 Decimal -> "1"
@@ -577,7 +688,8 @@ blockToHtml opts (OrderedList (startnum, numstyle, _) lst) = do
577688 else [A.style $ toValue $ "list-style-type: " ++
578689 numstyle']
579690 else [])
580− return $ foldl (!) (ordList opts contents) attribs
691+ l <- ordList opts contents
692+ return $ foldl (!) l attribs
581693blockToHtml opts (DefinitionList lst) = do
582694 contents <- mapM (\(term, defs) ->
583695 do term' <- if null term
@@ -587,13 +699,14 @@ blockToHtml opts (DefinitionList lst) = do
587699 blockListToHtml opts) defs
588700 return $ mconcat $ nl opts : term' : nl opts :
589701 intersperse (nl opts) defs') lst
590− return $ defList opts contents
702+ defList opts contents
591703blockToHtml opts (Table capt aligns widths headers rows') = do
592704 captionDoc <- if null capt
593705 then return mempty
594706 else do
595707 cs <- inlineListToHtml opts capt
596708 return $ H.caption cs >> nl opts
709+ html5 <- gets stHtml5
597710 let percent w = show (truncate (100*w) :: Integer) ++ "%"
598711 let coltags = if all (== 0.0) widths
599712 then mempty
@@ -601,7 +714,7 @@ blockToHtml opts (Table capt aligns widths headers rows') = do
601714 H.colgroup $ do
602715 nl opts
603716 mapM_ (\w -> do
604− if writerHtml5 opts
717+ if html5
605718 then H.col ! A.style (toValue $ "width: " ++
606719 percent w)
607720 else H.col ! A.width (toValue $ percent w)
@@ -624,11 +737,12 @@ blockToHtml opts (Table capt aligns widths headers rows') = do
624737 else tbl ! A.style (toValue $ "width:" ++
625738 show (round (totalWidth * 100) :: Int) ++ "%;")
626739
627−tableRowToHtml :: WriterOptions
740+tableRowToHtml :: PandocMonad m
741+ => WriterOptions
628742 -> [Alignment]
629743 -> Int
630744 -> [[Block]]
631− -> State WriterState Html
745+ -> StateT WriterState m Html
632746tableRowToHtml opts aligns rownum cols' = do
633747 let mkcell = if rownum == 0 then H.th else H.td
634748 let rowclass = case rownum of
@@ -648,15 +762,17 @@ alignmentToString alignment = case alignment of
648762 AlignCenter -> "center"
649763 AlignDefault -> ""
650764
651−tableItemToHtml :: WriterOptions
765+tableItemToHtml :: PandocMonad m
766+ => WriterOptions
652767 -> (Html -> Html)
653768 -> Alignment
654769 -> [Block]
655− -> State WriterState Html
770+ -> StateT WriterState m Html
656771tableItemToHtml opts tag' align' item = do
657772 contents <- blockListToHtml opts item
773+ html5 <- gets stHtml5
658774 let alignStr = alignmentToString align'
659− let attribs = if writerHtml5 opts
775+ let attribs = if html5
660776 then A.style (toValue $ "text-align: " ++ alignStr ++ ";")
661777 else A.align (toValue alignStr)
662778 let tag'' = if null alignStr
@@ -670,12 +786,12 @@ toListItems opts items = map (toListItem opts) items ++ [nl opts]
670786toListItem :: WriterOptions -> Html -> Html
671787toListItem opts item = nl opts >> H.li item
672788
673−blockListToHtml :: WriterOptions -> [Block] -> State WriterState Html
789+blockListToHtml :: PandocMonad m => WriterOptions -> [Block] -> StateT WriterState m Html
674790blockListToHtml opts lst =
675791 fmap (mconcat . intersperse (nl opts)) $ mapM (blockToHtml opts) lst
676792
677793-- | Convert list of Pandoc inline elements to HTML.
678−inlineListToHtml :: WriterOptions -> [Inline] -> State WriterState Html
794+inlineListToHtml :: PandocMonad m => WriterOptions -> [Inline] -> StateT WriterState m Html
679795inlineListToHtml opts lst =
680796 mapM (inlineToHtml opts) lst >>= return . mconcat
681797
@@ -694,8 +810,10 @@ annotateMML e tex = math (unode "semantics" [cs, unode "annotation" (annotAttrs,
694810
695811
696812-- | Convert Pandoc inline element to HTML.
697−inlineToHtml :: WriterOptions -> Inline -> State WriterState Html
698−inlineToHtml opts inline =
813+inlineToHtml :: PandocMonad m
814+ => WriterOptions -> Inline -> StateT WriterState m Html
815+inlineToHtml opts inline = do
816+ html5 <- gets stHtml5
699817 case inline of
700818 (Str str) -> return $ strToHtml str
701819 (Space) -> return $ strToHtml " "
@@ -703,7 +821,7 @@ inlineToHtml opts inline =
703821 WrapNone -> preEscapedString " "
704822 WrapAuto -> preEscapedString " "
705823 WrapPreserve -> preEscapedString "\n"
706− (LineBreak) -> return $ (if writerHtml5 opts then H5.br else H.br)
824+ (LineBreak) -> return $ (if html5 then H5.br else H.br)
707825 <> strToHtml "\n"
708826 (Span (id',classes,kvs) ils)
709827 -> inlineListToHtml opts ils >>=
@@ -731,8 +849,9 @@ inlineToHtml opts inline =
731849 modify $ \st -> st{ stHighlighting = True }
732850 return $ addAttrs opts (id',[],keyvals) h
733851 where (id',_,keyvals) = attr
734− hlCode = if writerHighlight opts
735− then highlight formatHtmlInline attr str
852+ hlCode = if isJust (writerHighlightStyle opts)
853+ then highlight formatHtmlInline
854+ attr str
736855 else Nothing
737856 (Strikeout lst) -> inlineListToHtml opts lst >>=
738857 return . H.del
@@ -771,12 +890,12 @@ inlineToHtml opts inline =
771890 InlineMath -> H.span ! A.class_ mathClass $ m
772891 DisplayMath -> H.div ! A.class_ mathClass $ m
773892 WebTeX url -> do
774− let imtag = if writerHtml5 opts then H5.img else H.img
893+ let imtag = if html5 then H5.img else H.img
775894 let m = imtag ! A.style "vertical-align:middle"
776895 ! A.src (toValue $ url ++ urlEncode str)
777896 ! A.alt (toValue str)
778897 ! A.title (toValue str)
779− let brtag = if writerHtml5 opts then H5.br else H.br
898+ let brtag = if html5 then H5.br else H.br
780899 return $ case t of
781900 InlineMath -> m
782901 DisplayMath -> brtag >> m >> brtag
@@ -785,17 +904,14 @@ inlineToHtml opts inline =
785904 InlineMath -> preEscapedString $ "<EQ ENV=\"math\">" ++ str ++ "</EQ>"
786905 DisplayMath -> preEscapedString $ "<EQ ENV=\"displaymath\">" ++ str ++ "</EQ>"
787906 MathML _ -> do
788− let dt = if t == InlineMath
789− then DisplayInline
790− else DisplayBlock
791907 let conf = useShortEmptyTags (const False)
792908 defaultConfigPP
793− case writeMathML dt <$> readTeX str of
909+ res <- lift $ convertMath writeMathML t str
910+ case res of
794911 Right r -> return $ preEscapedString $
795912 ppcElement conf (annotateMML r str)
796− Left _ -> inlineListToHtml opts
797− (texMathToInlines t str) >>=
798− return . (H.span ! A.class_ mathClass)
913+ Left il -> (H.span ! A.class_ mathClass) <$>
914+ inlineToHtml opts il
799915 MathJax _ -> return $ H.span ! A.class_ mathClass $ toHtml $
800916 case t of
801917 InlineMath -> "\\(" ++ str ++ "\\)"
@@ -805,9 +921,9 @@ inlineToHtml opts inline =
805921 InlineMath -> str
806922 DisplayMath -> "\\displaystyle " ++ str)
807923 PlainMath -> do
808− x <- inlineListToHtml opts (texMathToInlines t str)
924+ x <- lift (texMathToInlines t str) >>= inlineListToHtml opts
809925 let m = H.span ! A.class_ mathClass $ x
810− let brtag = if writerHtml5 opts then H5.br else H.br
926+ let brtag = if html5 then H5.br else H.br
811927 return $ case t of
812928 InlineMath -> m
813929 DisplayMath -> brtag >> m >> brtag
@@ -816,12 +932,13 @@ inlineToHtml opts inline =
816932 | otherwise -> return mempty
817933 (Link attr txt (s,_)) | "mailto:" `isPrefixOf` s -> do
818934 linkText <- inlineListToHtml opts txt
819− return $ obfuscateLink opts attr linkText s
935+ lift $ obfuscateLink opts attr linkText s
820936 (Link attr txt (s,tit)) -> do
821937 linkText <- inlineListToHtml opts txt
938+ slideVariant <- gets stSlideVariant
822939 let s' = case s of
823− '#':xs | writerSlideVariant opts ==
824− RevealJsSlides -> '#':'/':xs
940+ '#':xs | slideVariant == RevealJsSlides
941+ -> '#':'/':xs
825942 _ -> s
826943 let link = H.a ! A.href (toValue s') $ linkText
827944 let link' = if txt == [Str (unEscapeString s)]
@@ -837,7 +954,7 @@ inlineToHtml opts inline =
837954 [A.title $ toValue tit | not (null tit)] ++
838955 [A.alt $ toValue alternate' | not (null txt)] ++
839956 imgAttrsToHtml opts attr
840− let tag = if writerHtml5 opts then H5.img else H.img
957+ let tag = if html5 then H5.img else H.img
841958 return $ foldl (!) tag attributes
842959 -- note: null title included, as in Markdown.pl
843960 (Image attr _ (s,tit)) -> do
@@ -846,37 +963,36 @@ inlineToHtml opts inline =
846963 imgAttrsToHtml opts attr
847964 return $ foldl (!) H5.embed attributes
848965 -- note: null title included, as in Markdown.pl
849− (Note contents)
850− | writerIgnoreNotes opts -> return mempty
851− | otherwise -> do
966+ (Note contents) -> do
852967 notes <- gets stNotes
853968 let number = (length notes) + 1
854969 let ref = show number
855970 htmlContents <- blockListToNote opts ref contents
971+ epubVersion <- gets stEPUBVersion
856972 -- push contents onto front of notes
857973 modify $ \st -> st {stNotes = (htmlContents:notes)}
858− let revealSlash = ['/' | writerSlideVariant opts
859− == RevealJsSlides]
974+ slideVariant <- gets stSlideVariant
975+ let revealSlash = ['/' | slideVariant == RevealJsSlides]
860976 let link = H.a ! A.href (toValue $ "#" ++
861977 revealSlash ++
862978 writerIdentifierPrefix opts ++ "fn" ++ ref)
863979 ! A.class_ "footnoteRef"
864980 ! prefixedId opts ("fnref" ++ ref)
865− $ (if isJust (writerEpubVersion opts)
981+ $ (if isJust epubVersion
866982 then id
867983 else H.sup)
868984 $ toHtml ref
869− return $ case writerEpubVersion opts of
985+ return $ case epubVersion of
870986 Just EPUB3 -> link ! customAttribute "epub:type" "noteref"
871987 _ -> link
872988 (Cite cits il)-> do contents <- inlineListToHtml opts il
873989 let citationIds = unwords $ map citationId cits
874990 let result = H.span ! A.class_ "citation" $ contents
875− return $ if writerHtml5 opts
991+ return $ if html5
876992 then result ! customAttribute "data-cites" (toValue citationIds)
877993 else result
878994
879−blockListToNote :: WriterOptions -> String -> [Block] -> State WriterState Html
995+blockListToNote :: PandocMonad m => WriterOptions -> String -> [Block] -> StateT WriterState m Html
880996blockListToNote opts ref blocks =
881997 -- If last block is Para or Plain, include the backlink at the end of
882998 -- that block. Otherwise, insert a new Plain block with the backlink.
@@ -894,7 +1010,8 @@ blockListToNote opts ref blocks =
8941010 Plain backlink]
8951011 in do contents <- blockListToHtml opts blocks'
8961012 let noteItem = H.li ! (prefixedId opts ("fn" ++ ref)) $ contents
897− let noteItem' = case writerEpubVersion opts of
1013+ epubVersion <- gets stEPUBVersion
1014+ let noteItem' = case epubVersion of
8981015 Just EPUB3 -> noteItem ! customAttribute "epub:type" "footnote"
8991016 _ -> noteItem
9001017 return $ nl opts >> noteItem'
src/Text/Pandoc/Writers/Haddock.hs+39−27
@@ -39,9 +39,10 @@ import Text.Pandoc.Options
3939import Data.List ( intersperse, transpose )
4040import Text.Pandoc.Pretty
4141import Control.Monad.State
42−import Text.Pandoc.Readers.TeXMath (texMathToInlines)
42+import Text.Pandoc.Writers.Math (texMathToInlines)
4343import Network.URI (isURI)
4444import Data.Default
45+import Text.Pandoc.Class (PandocMonad)
4546
4647type Notes = [[Block]]
4748data WriterState = WriterState { stNotes :: Notes }
@@ -49,13 +50,14 @@ instance Default WriterState
4950 where def = WriterState{ stNotes = [] }
5051
5152-- | Convert Pandoc to Haddock.
52−writeHaddock :: WriterOptions -> Pandoc -> String
53+writeHaddock :: PandocMonad m => WriterOptions -> Pandoc -> m String
5354writeHaddock opts document =
54− evalState (pandocToHaddock opts{
55+ evalStateT (pandocToHaddock opts{
5556 writerWrapText = writerWrapText opts } document) def
5657
5758-- | Return haddock representation of document.
58−pandocToHaddock :: WriterOptions -> Pandoc -> State WriterState String
59+pandocToHaddock :: PandocMonad m
60+ => WriterOptions -> Pandoc -> StateT WriterState m String
5961pandocToHaddock opts (Pandoc meta blocks) = do
6062 let colwidth = if writerWrapText opts == WrapAuto
6163 then Just $ writerColumns opts
@@ -78,7 +80,8 @@ pandocToHaddock opts (Pandoc meta blocks) = do
7880 Just tpl -> return $ renderTemplate' tpl context
7981
8082-- | Return haddock representation of notes.
81−notesToHaddock :: WriterOptions -> [[Block]] -> State WriterState Doc
83+notesToHaddock :: PandocMonad m
84+ => WriterOptions -> [[Block]] -> StateT WriterState m Doc
8285notesToHaddock opts notes =
8386 if null notes
8487 then return empty
@@ -92,9 +95,10 @@ escapeString = escapeStringUsing haddockEscapes
9295 where haddockEscapes = backslashEscapes "\\/'`\"@<"
9396
9497-- | Convert Pandoc block element to haddock.
95−blockToHaddock :: WriterOptions -- ^ Options
96− -> Block -- ^ Block element
97− -> State WriterState Doc
98+blockToHaddock :: PandocMonad m
99+ => WriterOptions -- ^ Options
100+ -> Block -- ^ Block element
101+ -> StateT WriterState m Doc
98102blockToHaddock _ Null = return empty
99103blockToHaddock opts (Div _ ils) = do
100104 contents <- blockListToHaddock opts ils
@@ -167,8 +171,9 @@ blockToHaddock opts (DefinitionList items) = do
167171 contents <- mapM (definitionListItemToHaddock opts) items
168172 return $ cat contents <> blankline
169173
170−pandocTable :: WriterOptions -> Bool -> [Alignment] -> [Double]
171− -> [Doc] -> [[Doc]] -> State WriterState Doc
174+pandocTable :: PandocMonad m
175+ => WriterOptions -> Bool -> [Alignment] -> [Double]
176+ -> [Doc] -> [[Doc]] -> StateT WriterState m Doc
172177pandocTable opts headless aligns widths rawHeaders rawRows = do
173178 let isSimple = all (==0) widths
174179 let alignHeader alignment = case alignment of
@@ -207,8 +212,9 @@ pandocTable opts headless aligns widths rawHeaders rawRows = do
207212 else border
208213 return $ head'' $$ underline $$ body $$ bottom
209214
210−gridTable :: WriterOptions -> Bool -> [Alignment] -> [Double]
211− -> [Doc] -> [[Doc]] -> State WriterState Doc
215+gridTable :: PandocMonad m
216+ => WriterOptions -> Bool -> [Alignment] -> [Double]
217+ -> [Doc] -> [[Doc]] -> StateT WriterState m Doc
212218gridTable opts headless _aligns widths headers' rawRows = do
213219 let numcols = length headers'
214220 let widths' = if all (==0) widths
@@ -235,7 +241,8 @@ gridTable opts headless _aligns widths headers' rawRows = do
235241 return $ border '-' $$ head'' $$ body $$ border '-'
236242
237243-- | Convert bullet list item (list of blocks) to haddock
238−bulletListItemToHaddock :: WriterOptions -> [Block] -> State WriterState Doc
244+bulletListItemToHaddock :: PandocMonad m
245+ => WriterOptions -> [Block] -> StateT WriterState m Doc
239246bulletListItemToHaddock opts items = do
240247 contents <- blockListToHaddock opts items
241248 let sps = replicate (writerTabStop opts - 2) ' '
@@ -250,10 +257,11 @@ bulletListItemToHaddock opts items = do
250257 return $ hang (writerTabStop opts) start $ contents' <> cr
251258
252259-- | Convert ordered list item (a list of blocks) to haddock
253−orderedListItemToHaddock :: WriterOptions -- ^ options
254− -> String -- ^ list item marker
255− -> [Block] -- ^ list item (list of blocks)
256− -> State WriterState Doc
260+orderedListItemToHaddock :: PandocMonad m
261+ => WriterOptions -- ^ options
262+ -> String -- ^ list item marker
263+ -> [Block] -- ^ list item (list of blocks)
264+ -> StateT WriterState m Doc
257265orderedListItemToHaddock opts marker items = do
258266 contents <- blockListToHaddock opts items
259267 let sps = case length marker - writerTabStop opts of
@@ -263,9 +271,10 @@ orderedListItemToHaddock opts marker items = do
263271 return $ hang (writerTabStop opts) start $ contents <> cr
264272
265273-- | Convert definition list item (label, list of blocks) to haddock
266−definitionListItemToHaddock :: WriterOptions
267− -> ([Inline],[[Block]])
268− -> State WriterState Doc
274+definitionListItemToHaddock :: PandocMonad m
275+ => WriterOptions
276+ -> ([Inline],[[Block]])
277+ -> StateT WriterState m Doc
269278definitionListItemToHaddock opts (label, defs) = do
270279 labelText <- inlineListToHaddock opts label
271280 defs' <- mapM (mapM (blockToHaddock opts)) defs
@@ -273,19 +282,22 @@ definitionListItemToHaddock opts (label, defs) = do
273282 return $ nowrap (brackets labelText) <> cr <> contents <> cr
274283
275284-- | Convert list of Pandoc block elements to haddock
276−blockListToHaddock :: WriterOptions -- ^ Options
277− -> [Block] -- ^ List of block elements
278− -> State WriterState Doc
285+blockListToHaddock :: PandocMonad m
286+ => WriterOptions -- ^ Options
287+ -> [Block] -- ^ List of block elements
288+ -> StateT WriterState m Doc
279289blockListToHaddock opts blocks =
280290 mapM (blockToHaddock opts) blocks >>= return . cat
281291
282292-- | Convert list of Pandoc inline elements to haddock.
283−inlineListToHaddock :: WriterOptions -> [Inline] -> State WriterState Doc
293+inlineListToHaddock :: PandocMonad m
294+ => WriterOptions -> [Inline] -> StateT WriterState m Doc
284295inlineListToHaddock opts lst =
285296 mapM (inlineToHaddock opts) lst >>= return . cat
286297
287298-- | Convert Pandoc inline element to haddock.
288−inlineToHaddock :: WriterOptions -> Inline -> State WriterState Doc
299+inlineToHaddock :: PandocMonad m
300+ => WriterOptions -> Inline -> StateT WriterState m Doc
289301inlineToHaddock opts (Span (ident,_,_) ils) = do
290302 contents <- inlineListToHaddock opts ils
291303 if not (null ident) && null ils
@@ -321,12 +333,12 @@ inlineToHaddock opts (Math mt str) = do
321333 let adjust x = case mt of
322334 DisplayMath -> cr <> x <> cr
323335 InlineMath -> x
324− adjust `fmap` (inlineListToHaddock opts $ texMathToInlines mt str)
336+ adjust <$> (lift (texMathToInlines mt str) >>= inlineListToHaddock opts)
325337inlineToHaddock _ (RawInline f str)
326338 | f == "haddock" = return $ text str
327339 | otherwise = return empty
328340-- no line break in haddock (see above on CodeBlock)
329−inlineToHaddock _ (LineBreak) = return cr
341+inlineToHaddock _ LineBreak = return cr
330342inlineToHaddock opts SoftBreak =
331343 case writerWrapText opts of
332344 WrapAuto -> return space
src/Text/Pandoc/Writers/ICML.hs+26−22
@@ -1,4 +1,4 @@
1−{-# LANGUAGE OverloadedStrings, FlexibleContexts #-}
1+{-# LANGUAGE OverloadedStrings, FlexibleContexts, ScopedTypeVariables #-}
22
33{- |
44 Module : Text.Pandoc.Writers.ICML
@@ -15,10 +15,11 @@ into InDesign with File -> Place.
1515-}
1616module Text.Pandoc.Writers.ICML (writeICML) where
1717import Text.Pandoc.Definition
18+import Text.Pandoc.Error (PandocError)
1819import Text.Pandoc.XML
19−import Text.Pandoc.Readers.TeXMath (texMathToInlines)
20+import Text.Pandoc.Writers.Math (texMathToInlines)
2021import Text.Pandoc.Writers.Shared
21−import Text.Pandoc.Shared (linesToPara, splitBy, fetchItem, warn)
22+import Text.Pandoc.Shared (linesToPara, splitBy)
2223import Text.Pandoc.Options
2324import Text.Pandoc.Templates (renderTemplate')
2425import Text.Pandoc.Pretty
@@ -26,8 +27,11 @@ import Text.Pandoc.ImageSize
2627import Data.List (isPrefixOf, isInfixOf, stripPrefix, intersperse)
2728import Data.Text as Text (breakOnAll, pack)
2829import Control.Monad.State
30+import Control.Monad.Except (runExceptT)
2931import Network.URI (isURI)
3032import qualified Data.Set as Set
33+import Text.Pandoc.Class (PandocMonad)
34+import qualified Text.Pandoc.Class as P
3135
3236type Style = [String]
3337type Hyperlink = [(Int, String)]
@@ -40,7 +44,7 @@ data WriterState = WriterState{
4044 , maxListDepth :: Int
4145 }
4246
43−type WS a = StateT WriterState IO a
47+type WS m = StateT WriterState m
4448
4549defaultWriterState :: WriterState
4650defaultWriterState = WriterState{
@@ -121,9 +125,8 @@ subListParName = "subParagraph"
121125footnoteName = "Footnote"
122126citeName = "Cite"
123127
124−
125128-- | Convert Pandoc document to string in ICML format.
126−writeICML :: WriterOptions -> Pandoc -> IO String
129+writeICML :: PandocMonad m => WriterOptions -> Pandoc -> m String
127130writeICML opts (Pandoc meta blocks) = do
128131 let colwidth = if writerWrapText opts == WrapAuto
129132 then Just $ writerColumns opts
@@ -283,13 +286,13 @@ hyperlinksToDoc (x:xs) = hyp x $$ hyperlinksToDoc xs
283286
284287
285288-- | Convert a list of Pandoc blocks to ICML.
286−blocksToICML :: WriterOptions -> Style -> [Block] -> WS Doc
289+blocksToICML :: PandocMonad m => WriterOptions -> Style -> [Block] -> WS m Doc
287290blocksToICML opts style lst = do
288291 docs <- mapM (blockToICML opts style) lst
289292 return $ intersperseBrs docs
290293
291294-- | Convert a Pandoc block element to ICML.
292−blockToICML :: WriterOptions -> Style -> Block -> WS Doc
295+blockToICML :: PandocMonad m => WriterOptions -> Style -> Block -> WS m Doc
293296blockToICML opts style (Plain lst) = parStyle opts style lst
294297-- title beginning with fig: indicates that the image is a figure
295298blockToICML opts style (Para img@[Image _ txt (_,'f':'i':'g':':':_)]) = do
@@ -359,7 +362,7 @@ blockToICML opts style (Div _ lst) = blocksToICML opts style lst
359362blockToICML _ _ Null = return empty
360363
361364-- | Convert a list of lists of blocks to ICML list items.
362−listItemsToICML :: WriterOptions -> String -> Style -> Maybe ListAttributes -> [[Block]] -> WS Doc
365+listItemsToICML :: PandocMonad m => WriterOptions -> String -> Style -> Maybe ListAttributes -> [[Block]] -> WS m Doc
363366listItemsToICML _ _ _ _ [] = return empty
364367listItemsToICML opts listType style attribs (first:rest) = do
365368 st <- get
@@ -374,7 +377,7 @@ listItemsToICML opts listType style attribs (first:rest) = do
374377 return $ intersperseBrs docs
375378
376379-- | Convert a list of blocks to ICML list items.
377−listItemToICML :: WriterOptions -> Style -> Bool-> Maybe ListAttributes -> [Block] -> WS Doc
380+listItemToICML :: PandocMonad m => WriterOptions -> Style -> Bool-> Maybe ListAttributes -> [Block] -> WS m Doc
378381listItemToICML opts style isFirst attribs item =
379382 let makeNumbStart (Just (beginsWith, numbStl, _)) =
380383 let doN DefaultStyle = []
@@ -401,7 +404,7 @@ listItemToICML opts style isFirst attribs item =
401404 return $ intersperseBrs (f : r)
402405 else blocksToICML opts stl' item
403406
404−definitionListItemToICML :: WriterOptions -> Style -> ([Inline],[[Block]]) -> WS Doc
407+definitionListItemToICML :: PandocMonad m => WriterOptions -> Style -> ([Inline],[[Block]]) -> WS m Doc
405408definitionListItemToICML opts style (term,defs) = do
406409 term' <- parStyle opts (defListTermName:style) term
407410 defs' <- mapM (blocksToICML opts (defListDefName:style)) defs
@@ -409,11 +412,11 @@ definitionListItemToICML opts style (term,defs) = do
409412
410413
411414-- | Convert a list of inline elements to ICML.
412−inlinesToICML :: WriterOptions -> Style -> [Inline] -> WS Doc
415+inlinesToICML :: PandocMonad m => WriterOptions -> Style -> [Inline] -> WS m Doc
413416inlinesToICML opts style lst = vcat `fmap` mapM (inlineToICML opts style) (mergeSpaces lst)
414417
415418-- | Convert an inline element to ICML.
416−inlineToICML :: WriterOptions -> Style -> Inline -> WS Doc
419+inlineToICML :: PandocMonad m => WriterOptions -> Style -> Inline -> WS m Doc
417420inlineToICML _ style (Str str) = charStyle style $ text $ escapeStringForXML str
418421inlineToICML opts style (Emph lst) = inlinesToICML opts (emphName:style) lst
419422inlineToICML opts style (Strong lst) = inlinesToICML opts (strongName:style) lst
@@ -433,7 +436,8 @@ inlineToICML opts style SoftBreak =
433436 WrapPreserve -> charStyle style cr
434437inlineToICML _ style LineBreak = charStyle style $ text lineSeparator
435438inlineToICML opts style (Math mt str) =
436− cat <$> mapM (inlineToICML opts style) (texMathToInlines mt str)
439+ lift (texMathToInlines mt str) >>=
440+ (fmap cat . mapM (inlineToICML opts style))
437441inlineToICML _ _ (RawInline f str)
438442 | f == Format "icml" = return $ text str
439443 | otherwise = return empty
@@ -452,7 +456,7 @@ inlineToICML opts style (Note lst) = footnoteToICML opts style lst
452456inlineToICML opts style (Span _ lst) = inlinesToICML opts style lst
453457
454458-- | Convert a list of block elements to an ICML footnote.
455−footnoteToICML :: WriterOptions -> Style -> [Block] -> WS Doc
459+footnoteToICML :: PandocMonad m => WriterOptions -> Style -> [Block] -> WS m Doc
456460footnoteToICML opts style lst =
457461 let insertTab (Para ls) = blockToICML opts (footnoteName:style) $ Para $ (Str "\t"):ls
458462 insertTab block = blockToICML opts (footnoteName:style) block
@@ -483,7 +487,7 @@ intersperseBrs :: [Doc] -> Doc
483487intersperseBrs = vcat . intersperse (selfClosingTag "Br" []) . filter (not . isEmpty)
484488
485489-- | Wrap a list of inline elements in an ICML Paragraph Style
486−parStyle :: WriterOptions -> Style -> [Inline] -> WS Doc
490+parStyle :: PandocMonad m => WriterOptions -> Style -> [Inline] -> WS m Doc
487491parStyle opts style lst =
488492 let slipIn x y = if null y
489493 then x
@@ -507,7 +511,7 @@ parStyle opts style lst =
507511 state $ \st -> (cont, st{ blockStyles = Set.insert stlStr $ blockStyles st })
508512
509513-- | Wrap a Doc in an ICML Character Style.
510−charStyle :: Style -> Doc -> WS Doc
514+charStyle :: PandocMonad m => Style -> Doc -> WS m Doc
511515charStyle style content =
512516 let (stlStr, attrs) = styleToStrAttr style
513517 doc = inTags True "CharacterStyleRange" attrs $ inTagsSimple "Content" $ flush content
@@ -529,18 +533,18 @@ styleToStrAttr style =
529533 in (stlStr, attrs)
530534
531535-- | Assemble an ICML Image.
532−imageICML :: WriterOptions -> Style -> Attr -> Target -> WS Doc
536+imageICML :: PandocMonad m => WriterOptions -> Style -> Attr -> Target -> WS m Doc
533537imageICML opts style attr (src, _) = do
534− res <- liftIO $ fetchItem (writerSourceURL opts) src
538+ res <- runExceptT $ lift $ P.fetchItem (writerSourceURL opts) src
535539 imgS <- case res of
536− Left (_) -> do
537− warn $ "Could not find image `" ++ src ++ "', skipping..."
540+ Left (_ :: PandocError) -> do
541+ lift $ P.warning $ "Could not find image `" ++ src ++ "', skipping..."
538542 return def
539543 Right (img, _) -> do
540544 case imageSize img of
541545 Right size -> return size
542546 Left msg -> do
543− warn $ "Could not determine image size in `" ++
547+ lift $ P.warning $ "Could not determine image size in `" ++
544548 src ++ "': " ++ msg
545549 return def
546550 let (ow, oh) = sizeInPoints imgS
src/Text/Pandoc/Writers/LaTeX.hs+68−41
@@ -29,7 +29,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2929
3030Conversion of 'Pandoc' format into LaTeX.
3131-}
32−module Text.Pandoc.Writers.LaTeX ( writeLaTeX ) where
32+module Text.Pandoc.Writers.LaTeX (
33+ writeLaTeX
34+ , writeBeamer
35+ ) where
3336import Text.Pandoc.Definition
3437import Text.Pandoc.Walk
3538import Text.Pandoc.Shared
@@ -54,6 +57,7 @@ import Text.Pandoc.Slides
5457import Text.Pandoc.Highlighting (highlight, styleToLaTeX,
5558 formatLaTeXInline, formatLaTeXBlock,
5659 toListingsLanguage)
60+import Text.Pandoc.Class (PandocMonad)
5761
5862data WriterState =
5963 WriterState { stInNote :: Bool -- true if we're in a note
@@ -75,26 +79,46 @@ data WriterState =
7579 , stIncremental :: Bool -- true if beamer lists should be displayed bit by bit
7680 , stInternalLinks :: [String] -- list of internal link targets
7781 , stUsesEuro :: Bool -- true if euro symbol used
82+ , stBeamer :: Bool -- produce beamer
7883 }
7984
85+startingState :: WriterOptions -> WriterState
86+startingState options = WriterState {
87+ stInNote = False
88+ , stInQuote = False
89+ , stInMinipage = False
90+ , stInHeading = False
91+ , stNotes = []
92+ , stOLLevel = 1
93+ , stOptions = options
94+ , stVerbInNote = False
95+ , stTable = False
96+ , stStrikeout = False
97+ , stUrl = False
98+ , stGraphics = False
99+ , stLHS = False
100+ , stBook = (case writerTopLevelDivision options of
101+ TopLevelPart -> True
102+ TopLevelChapter -> True
103+ _ -> False)
104+ , stCsquotes = False
105+ , stHighlighting = False
106+ , stIncremental = writerIncremental options
107+ , stInternalLinks = []
108+ , stUsesEuro = False
109+ , stBeamer = False }
110+
80111-- | Convert Pandoc to LaTeX.
81−writeLaTeX :: WriterOptions -> Pandoc -> String
82−writeLaTeX options document =
112+writeLaTeX :: PandocMonad m => WriterOptions -> Pandoc -> m String
113+writeLaTeX options document = return $
114+ evalState (pandocToLaTeX options document) $
115+ startingState options
116+
117+-- | Convert Pandoc to LaTeX Beamer.
118+writeBeamer :: PandocMonad m => WriterOptions -> Pandoc -> m String
119+writeBeamer options document = return $
83120 evalState (pandocToLaTeX options document) $
84− WriterState { stInNote = False, stInQuote = False,
85− stInMinipage = False, stInHeading = False,
86− stNotes = [], stOLLevel = 1,
87− stOptions = options, stVerbInNote = False,
88− stTable = False, stStrikeout = False,
89− stUrl = False, stGraphics = False,
90− stLHS = False,
91− stBook = (case writerTopLevelDivision options of
92− TopLevelPart -> True
93− TopLevelChapter -> True
94− _ -> False),
95− stCsquotes = False, stHighlighting = False,
96− stIncremental = writerIncremental options,
97− stInternalLinks = [], stUsesEuro = False }
121+ (startingState options){ stBeamer = True }
98122
99123pandocToLaTeX :: WriterOptions -> Pandoc -> State WriterState String
100124pandocToLaTeX options (Pandoc meta blocks) = do
@@ -143,7 +167,8 @@ pandocToLaTeX options (Pandoc meta blocks) = do
143167 else case last blocks' of
144168 Header 1 _ il -> (init blocks', il)
145169 _ -> (blocks', [])
146− blocks''' <- if writerBeamer options
170+ beamer <- gets stBeamer
171+ blocks''' <- if beamer
147172 then toSlides blocks''
148173 else return blocks''
149174 body <- mapM (elementToLaTeX options) $ hierarchicalize blocks'''
@@ -170,7 +195,7 @@ pandocToLaTeX options (Pandoc meta blocks) = do
170195 defField "body" main $
171196 defField "title-meta" titleMeta $
172197 defField "author-meta" (intercalate "; " authorsMeta) $
173− defField "documentclass" (if writerBeamer options
198+ defField "documentclass" (if beamer
174199 then ("beamer" :: String)
175200 else if stBook st
176201 then "book"
@@ -185,10 +210,13 @@ pandocToLaTeX options (Pandoc meta blocks) = do
185210 defField "book-class" (stBook st) $
186211 defField "euro" (stUsesEuro st) $
187212 defField "listings" (writerListings options || stLHS st) $
188− defField "beamer" (writerBeamer options) $
213+ defField "beamer" beamer $
189214 (if stHighlighting st
190− then defField "highlighting-macros" (styleToLaTeX
191− $ writerHighlightStyle options )
215+ then case writerHighlightStyle options of
216+ Just sty ->
217+ defField "highlighting-macros"
218+ (styleToLaTeX sty)
219+ Nothing -> id
192220 else id) $
193221 (case writerCiteMethod options of
194222 Natbib -> defField "biblio-title" biblioTitle .
@@ -271,7 +299,7 @@ stringToLaTeX _ [] = return ""
271299stringToLaTeX ctx (x:xs) = do
272300 opts <- gets stOptions
273301 rest <- stringToLaTeX ctx xs
274− let ligatures = writerTeXLigatures opts && ctx == TextString
302+ let ligatures = isEnabled Ext_smart opts && ctx == TextString
275303 let isUrl = ctx == URLString
276304 when (x == '€') $
277305 modify $ \st -> st{ stUsesEuro = True }
@@ -384,7 +412,7 @@ blockToLaTeX :: Block -- ^ Block to convert
384412 -> State WriterState Doc
385413blockToLaTeX Null = return empty
386414blockToLaTeX (Div (identifier,classes,kvs) bs) = do
387− beamer <- writerBeamer `fmap` gets stOptions
415+ beamer <- gets stBeamer
388416 ref <- toLabel identifier
389417 let linkAnchor = if null identifier
390418 then empty
@@ -435,7 +463,7 @@ blockToLaTeX (Para [Image attr@(ident, _, _) txt (src,'f':'i':'g':':':tit)]) = d
435463 else figure $$ footnotes
436464-- . . . indicates pause in beamer slides
437465blockToLaTeX (Para [Str ".",Space,Str ".",Space,Str "."]) = do
438− beamer <- writerBeamer `fmap` gets stOptions
466+ beamer <- gets stBeamer
439467 if beamer
440468 then blockToLaTeX (RawBlock "latex" "\\pause")
441469 else inlineListToLaTeX [Str ".",Space,Str ".",Space,Str "."]
@@ -444,7 +472,7 @@ blockToLaTeX (Para lst) =
444472blockToLaTeX (LineBlock lns) = do
445473 blockToLaTeX $ linesToPara lns
446474blockToLaTeX (BlockQuote lst) = do
447− beamer <- writerBeamer `fmap` gets stOptions
475+ beamer <- gets stBeamer
448476 case lst of
449477 [b] | beamer && isListBlock b -> do
450478 oldIncremental <- gets stIncremental
@@ -511,10 +539,11 @@ blockToLaTeX (CodeBlock (identifier,classes,keyvalAttr) str) = do
511539 return (flush $ linkAnchor $$ text (T.unpack h))
512540 case () of
513541 _ | isEnabled Ext_literate_haskell opts && "haskell" `elem` classes &&
514− "literate" `elem` classes -> lhsCodeBlock
515− | writerListings opts -> listingsCodeBlock
516− | writerHighlight opts && not (null classes) -> highlightedCodeBlock
517− | otherwise -> rawCodeBlock
542+ "literate" `elem` classes -> lhsCodeBlock
543+ | writerListings opts -> listingsCodeBlock
544+ | not (null classes) && isJust (writerHighlightStyle opts)
545+ -> highlightedCodeBlock
546+ | otherwise -> rawCodeBlock
518547blockToLaTeX (RawBlock f x)
519548 | f == Format "latex" || f == Format "tex"
520549 = return $ text x
@@ -522,7 +551,7 @@ blockToLaTeX (RawBlock f x)
522551blockToLaTeX (BulletList []) = return empty -- otherwise latex error
523552blockToLaTeX (BulletList lst) = do
524553 incremental <- gets stIncremental
525− beamer <- writerBeamer `fmap` gets stOptions
554+ beamer <- gets stBeamer
526555 let inc = if beamer && incremental then "[<+->]" else ""
527556 items <- mapM listItemToLaTeX lst
528557 let spacing = if isTightList lst
@@ -767,7 +796,8 @@ sectionHeader unnumbered ident level lst = do
767796 let topLevelDivision = if book && writerTopLevelDivision opts == TopLevelDefault
768797 then TopLevelChapter
769798 else writerTopLevelDivision opts
770− let level' = if writerBeamer opts &&
799+ beamer <- gets stBeamer
800+ let level' = if beamer &&
771801 topLevelDivision `elem` [TopLevelPart, TopLevelChapter]
772802 -- beamer has parts but no chapters
773803 then if level == 1 then -1 else level - 1
@@ -903,7 +933,8 @@ inlineToLaTeX (Code (_,classes,_) str) = do
903933 inHeading <- gets stInHeading
904934 case () of
905935 _ | writerListings opts && not inHeading -> listingsCode
906− | writerHighlight opts && not (null classes) -> highlightCode
936+ | isJust (writerHighlightStyle opts) && not (null classes)
937+ -> highlightCode
907938 | otherwise -> rawCode
908939 where listingsCode = do
909940 inNote <- gets stInNote
@@ -937,11 +968,11 @@ inlineToLaTeX (Quoted qt lst) = do
937968 let inner = s1 <> contents <> s2
938969 return $ case qt of
939970 DoubleQuote ->
940− if writerTeXLigatures opts
971+ if isEnabled Ext_smart opts
941972 then text "``" <> inner <> text "''"
942973 else char '\x201C' <> inner <> char '\x201D'
943974 SingleQuote ->
944− if writerTeXLigatures opts
975+ if isEnabled Ext_smart opts
945976 then char '`' <> inner <> char '\''
946977 else char '\x2018' <> inner <> char '\x2019'
947978inlineToLaTeX (Str str) = liftM text $ stringToLaTeX TextString str
@@ -1016,9 +1047,9 @@ inlineToLaTeX (Note contents) = do
10161047 (CodeBlock _ _ : _) -> cr
10171048 _ -> empty
10181049 let noteContents = nest 2 contents' <> optnl
1019− opts <- gets stOptions
1050+ beamer <- gets stBeamer
10201051 -- in beamer slides, display footnote from current overlay forward
1021− let beamerMark = if writerBeamer opts
1052+ let beamerMark = if beamer
10221053 then text "<.->"
10231054 else empty
10241055 modify $ \st -> st{ stNotes = noteContents : stNotes st }
@@ -1316,10 +1347,6 @@ commonFromBcp47 x = fromIso $ head x
13161347 fromIso "vi" = "vietnamese"
13171348 fromIso _ = ""
13181349
1319−deNote :: Inline -> Inline
1320−deNote (Note _) = RawInline (Format "latex") ""
1321−deNote x = x
1322−
13231350pDocumentOptions :: P.Parsec String () [String]
13241351pDocumentOptions = do
13251352 P.char '['
src/Text/Pandoc/Writers/Man.hs+37−30
@@ -34,24 +34,27 @@ import Text.Pandoc.Templates
3434import Text.Pandoc.Shared
3535import Text.Pandoc.Writers.Shared
3636import Text.Pandoc.Options
37−import Text.Pandoc.Readers.TeXMath
37+import Text.Pandoc.Writers.Math
3838import Text.Printf ( printf )
3939import Data.List ( stripPrefix, intersperse, intercalate )
4040import Data.Maybe (fromMaybe)
4141import Text.Pandoc.Pretty
4242import Text.Pandoc.Builder (deleteMeta)
4343import Control.Monad.State
44+import Text.Pandoc.Error
45+import Control.Monad.Except (throwError)
46+import Text.Pandoc.Class (PandocMonad)
4447
4548type Notes = [[Block]]
4649data WriterState = WriterState { stNotes :: Notes
4750 , stHasTables :: Bool }
4851
4952-- | Convert Pandoc to Man.
50−writeMan :: WriterOptions -> Pandoc -> String
51−writeMan opts document = evalState (pandocToMan opts document) (WriterState [] False)
53+writeMan :: PandocMonad m => WriterOptions -> Pandoc -> m String
54+writeMan opts document = evalStateT (pandocToMan opts document) (WriterState [] False)
5255
5356-- | Return groff man representation of document.
54−pandocToMan :: WriterOptions -> Pandoc -> State WriterState String
57+pandocToMan :: PandocMonad m => WriterOptions -> Pandoc -> StateT WriterState m String
5558pandocToMan opts (Pandoc meta blocks) = do
5659 let colwidth = if writerWrapText opts == WrapAuto
5760 then Just $ writerColumns opts
@@ -93,7 +96,7 @@ pandocToMan opts (Pandoc meta blocks) = do
9396 Just tpl -> return $ renderTemplate' tpl context
9497
9598-- | Return man representation of notes.
96−notesToMan :: WriterOptions -> [[Block]] -> State WriterState Doc
99+notesToMan :: PandocMonad m => WriterOptions -> [[Block]] -> StateT WriterState m Doc
97100notesToMan opts notes =
98101 if null notes
99102 then return empty
@@ -101,7 +104,7 @@ notesToMan opts notes =
101104 return . (text ".SH NOTES" $$) . vcat
102105
103106-- | Return man representation of a note.
104−noteToMan :: WriterOptions -> Int -> [Block] -> State WriterState Doc
107+noteToMan :: PandocMonad m => WriterOptions -> Int -> [Block] -> StateT WriterState m Doc
105108noteToMan opts num note = do
106109 contents <- blockListToMan opts note
107110 let marker = cr <> text ".SS " <> brackets (text (show num))
@@ -160,9 +163,10 @@ splitSentences xs =
160163 in if null rest then [sent] else sent : splitSentences rest
161164
162165-- | Convert Pandoc block element to man.
163−blockToMan :: WriterOptions -- ^ Options
164− -> Block -- ^ Block element
165− -> State WriterState Doc
166+blockToMan :: PandocMonad m
167+ => WriterOptions -- ^ Options
168+ -> Block -- ^ Block element
169+ -> StateT WriterState m Doc
166170blockToMan _ Null = return empty
167171blockToMan opts (Div _ bs) = blockListToMan opts bs
168172blockToMan opts (Plain inlines) =
@@ -236,7 +240,7 @@ blockToMan opts (DefinitionList items) = do
236240 return (vcat contents)
237241
238242-- | Convert bullet list item (list of blocks) to man.
239−bulletListItemToMan :: WriterOptions -> [Block] -> State WriterState Doc
243+bulletListItemToMan :: PandocMonad m => WriterOptions -> [Block] -> StateT WriterState m Doc
240244bulletListItemToMan _ [] = return empty
241245bulletListItemToMan opts ((Para first):rest) =
242246 bulletListItemToMan opts ((Plain first):rest)
@@ -254,11 +258,12 @@ bulletListItemToMan opts (first:rest) = do
254258 return $ text "\\[bu] .RS 2" $$ first' $$ rest' $$ text ".RE"
255259
256260-- | Convert ordered list item (a list of blocks) to man.
257−orderedListItemToMan :: WriterOptions -- ^ options
258− -> String -- ^ order marker for list item
259− -> Int -- ^ number of spaces to indent
260− -> [Block] -- ^ list item (list of blocks)
261− -> State WriterState Doc
261+orderedListItemToMan :: PandocMonad m
262+ => WriterOptions -- ^ options
263+ -> String -- ^ order marker for list item
264+ -> Int -- ^ number of spaces to indent
265+ -> [Block] -- ^ list item (list of blocks)
266+ -> StateT WriterState m Doc
262267orderedListItemToMan _ _ _ [] = return empty
263268orderedListItemToMan opts num indent ((Para first):rest) =
264269 orderedListItemToMan opts num indent ((Plain first):rest)
@@ -273,18 +278,19 @@ orderedListItemToMan opts num indent (first:rest) = do
273278 return $ first'' $$ rest''
274279
275280-- | Convert definition list item (label, list of blocks) to man.
276−definitionListItemToMan :: WriterOptions
277− -> ([Inline],[[Block]])
278− -> State WriterState Doc
281+definitionListItemToMan :: PandocMonad m
282+ => WriterOptions
283+ -> ([Inline],[[Block]])
284+ -> StateT WriterState m Doc
279285definitionListItemToMan opts (label, defs) = do
280286 labelText <- inlineListToMan opts label
281287 contents <- if null defs
282288 then return empty
283289 else liftM vcat $ forM defs $ \blocks -> do
284− let (first, rest) = case blocks of
285− ((Para x):y) -> (Plain x,y)
286− (x:y) -> (x,y)
287− [] -> error "blocks is null"
290+ (first, rest) <- case blocks of
291+ ((Para x):y) -> return (Plain x,y)
292+ (x:y) -> return (x,y)
293+ [] -> throwError $ PandocSomeError "blocks is null"
288294 rest' <- liftM vcat $
289295 mapM (\item -> blockToMan opts item) rest
290296 first' <- blockToMan opts first
@@ -292,18 +298,19 @@ definitionListItemToMan opts (label, defs) = do
292298 return $ text ".TP" $$ nowrap (text ".B " <> labelText) $$ contents
293299
294300-- | Convert list of Pandoc block elements to man.
295−blockListToMan :: WriterOptions -- ^ Options
296− -> [Block] -- ^ List of block elements
297− -> State WriterState Doc
301+blockListToMan :: PandocMonad m
302+ => WriterOptions -- ^ Options
303+ -> [Block] -- ^ List of block elements
304+ -> StateT WriterState m Doc
298305blockListToMan opts blocks =
299306 mapM (blockToMan opts) blocks >>= (return . vcat)
300307
301308-- | Convert list of Pandoc inline elements to man.
302−inlineListToMan :: WriterOptions -> [Inline] -> State WriterState Doc
309+inlineListToMan :: PandocMonad m => WriterOptions -> [Inline] -> StateT WriterState m Doc
303310inlineListToMan opts lst = mapM (inlineToMan opts) lst >>= (return . hcat)
304311
305312-- | Convert Pandoc inline element to man.
306−inlineToMan :: WriterOptions -> Inline -> State WriterState Doc
313+inlineToMan :: PandocMonad m => WriterOptions -> Inline -> StateT WriterState m Doc
307314inlineToMan opts (Span _ ils) = inlineListToMan opts ils
308315inlineToMan opts (Emph lst) = do
309316 contents <- inlineListToMan opts lst
@@ -335,14 +342,14 @@ inlineToMan _ (Str str@('.':_)) =
335342 return $ afterBreak "\\&" <> text (escapeString str)
336343inlineToMan _ (Str str) = return $ text $ escapeString str
337344inlineToMan opts (Math InlineMath str) =
338− inlineListToMan opts $ texMathToInlines InlineMath str
345+ lift (texMathToInlines InlineMath str) >>= inlineListToMan opts
339346inlineToMan opts (Math DisplayMath str) = do
340− contents <- inlineListToMan opts $ texMathToInlines DisplayMath str
347+ contents <- lift (texMathToInlines DisplayMath str) >>= inlineListToMan opts
341348 return $ cr <> text ".RS" $$ contents $$ text ".RE"
342349inlineToMan _ (RawInline f str)
343350 | f == Format "man" = return $ text str
344351 | otherwise = return empty
345−inlineToMan _ (LineBreak) = return $
352+inlineToMan _ LineBreak = return $
346353 cr <> text ".PD 0" $$ text ".P" $$ text ".PD" <> cr
347354inlineToMan _ SoftBreak = return space
348355inlineToMan _ Space = return space
src/Text/Pandoc/Writers/Markdown.hs+100−67
@@ -46,8 +46,9 @@ import Data.Ord ( comparing )
4646import Text.Pandoc.Pretty
4747import Control.Monad.Reader
4848import Control.Monad.State
49−import Text.Pandoc.Writers.HTML (writeHtmlString)
50−import Text.Pandoc.Readers.TeXMath (texMathToInlines)
49+import Control.Monad.Except (throwError)
50+import Text.Pandoc.Writers.HTML (writeHtml5String)
51+import Text.Pandoc.Writers.Math (texMathToInlines)
5152import Text.HTML.TagSoup (parseTags, isTagText, Tag(..))
5253import Network.URI (isURI)
5354import Data.Default
@@ -57,15 +58,17 @@ import qualified Data.Vector as V
5758import qualified Data.Text as T
5859import qualified Data.Set as Set
5960import Network.HTTP ( urlEncode )
61+import Text.Pandoc.Error
62+import Text.Pandoc.Class (PandocMonad)
6063
6164type Notes = [[Block]]
6265type Ref = ([Inline], Target, Attr)
6366type Refs = [Ref]
6467
65−type MD = ReaderT WriterEnv (State WriterState)
68+type MD m = ReaderT WriterEnv (StateT WriterState m)
6669
67−evalMD :: MD a -> WriterEnv -> WriterState -> a
68−evalMD md env st = evalState (runReaderT md env) st
70+evalMD :: PandocMonad m => MD m a -> WriterEnv -> WriterState -> m a
71+evalMD md env st = evalStateT (runReaderT md env) st
6972
7073data WriterEnv = WriterEnv { envInList :: Bool
7174 , envPlain :: Bool
@@ -96,7 +99,7 @@ instance Default WriterState
9699 }
97100
98101-- | Convert Pandoc to Markdown.
99−writeMarkdown :: WriterOptions -> Pandoc -> String
102+writeMarkdown :: PandocMonad m => WriterOptions -> Pandoc -> m String
100103writeMarkdown opts document =
101104 evalMD (pandocToMarkdown opts{
102105 writerWrapText = if isEnabled Ext_hard_line_breaks opts
@@ -106,7 +109,7 @@ writeMarkdown opts document =
106109
107110-- | Convert Pandoc to plain text (like markdown, but without links,
108111-- pictures, or inline formatting).
109−writePlain :: WriterOptions -> Pandoc -> String
112+writePlain :: PandocMonad m => WriterOptions -> Pandoc -> m String
110113writePlain opts document =
111114 evalMD (pandocToMarkdown opts document) def{ envPlain = True } def
112115
@@ -171,7 +174,7 @@ jsonToYaml (Number n) = text $ show n
171174jsonToYaml _ = empty
172175
173176-- | Return markdown representation of document.
174−pandocToMarkdown :: WriterOptions -> Pandoc -> MD String
177+pandocToMarkdown :: PandocMonad m => WriterOptions -> Pandoc -> MD m String
175178pandocToMarkdown opts (Pandoc meta blocks) = do
176179 let colwidth = if writerWrapText opts == WrapAuto
177180 then Just $ writerColumns opts
@@ -196,9 +199,9 @@ pandocToMarkdown opts (Pandoc meta blocks) = do
196199 | otherwise -> empty
197200 Nothing -> empty
198201 let headerBlocks = filter isHeaderBlock blocks
199− let toc = if writerTableOfContents opts
200− then tableOfContents opts headerBlocks
201− else empty
202+ toc <- if writerTableOfContents opts
203+ then tableOfContents opts headerBlocks
204+ else return empty
202205 -- Strip off final 'references' header if markdown citations enabled
203206 let blocks' = if isEnabled Ext_citations opts
204207 then case reverse blocks of
@@ -221,13 +224,14 @@ pandocToMarkdown opts (Pandoc meta blocks) = do
221224 Just tpl -> return $ renderTemplate' tpl context
222225
223226-- | Return markdown representation of reference key table.
224−refsToMarkdown :: WriterOptions -> Refs -> MD Doc
227+refsToMarkdown :: PandocMonad m => WriterOptions -> Refs -> MD m Doc
225228refsToMarkdown opts refs = mapM (keyToMarkdown opts) refs >>= return . vcat
226229
227230-- | Return markdown representation of a reference key.
228−keyToMarkdown :: WriterOptions
231+keyToMarkdown :: PandocMonad m
232+ => WriterOptions
229233 -> Ref
230− -> MD Doc
234+ -> MD m Doc
231235keyToMarkdown opts (label, (src, tit), attr) = do
232236 label' <- inlineListToMarkdown opts label
233237 let tit' = if null tit
@@ -238,7 +242,7 @@ keyToMarkdown opts (label, (src, tit), attr) = do
238242 <> linkAttributes opts attr
239243
240244-- | Return markdown representation of notes.
241−notesToMarkdown :: WriterOptions -> [[Block]] -> MD Doc
245+notesToMarkdown :: PandocMonad m => WriterOptions -> [[Block]] -> MD m Doc
242246notesToMarkdown opts notes = do
243247 n <- gets stNoteNum
244248 notes' <- mapM (\(num, note) -> noteToMarkdown opts num note) (zip [n..] notes)
@@ -246,7 +250,7 @@ notesToMarkdown opts notes = do
246250 return $ vsep notes'
247251
248252-- | Return markdown representation of a note.
249−noteToMarkdown :: WriterOptions -> Int -> [Block] -> MD Doc
253+noteToMarkdown :: PandocMonad m => WriterOptions -> Int -> [Block] -> MD m Doc
250254noteToMarkdown opts num blocks = do
251255 contents <- blockListToMarkdown opts blocks
252256 let num' = text $ writerIdentifierPrefix opts ++ show num
@@ -276,14 +280,16 @@ escapeString opts = escapeStringUsing markdownEscapes
276280 (if isEnabled Ext_tex_math_dollars opts
277281 then ('$':)
278282 else id) $
279− "\\`*_[]#"
283+ "\\`*_[]#" ++
284+ if isEnabled Ext_smart opts
285+ then "\"'"
286+ else ""
280287
281288-- | Construct table of contents from list of header blocks.
282−tableOfContents :: WriterOptions -> [Block] -> Doc
289+tableOfContents :: PandocMonad m => WriterOptions -> [Block] -> m Doc
283290tableOfContents opts headers =
284− let opts' = opts { writerIgnoreNotes = True }
285− contents = BulletList $ map (elementToListItem opts) $ hierarchicalize headers
286− in evalMD (blockToMarkdown opts' contents) def def
291+ let contents = BulletList $ map (elementToListItem opts) $ hierarchicalize headers
292+ in evalMD (blockToMarkdown opts contents) def def
287293
288294-- | Converts an Element to a list item for a table of contents,
289295elementToListItem :: WriterOptions -> Element -> [Block]
@@ -292,8 +298,9 @@ elementToListItem opts (Sec lev _nums (ident,_,_) headerText subsecs)
292298 [ BulletList (map (elementToListItem opts) subsecs) |
293299 not (null subsecs) && lev < writerTOCDepth opts ]
294300 where headerLink = if null ident
295− then headerText
296− else [Link nullAttr headerText ('#':ident, "")]
301+ then walk deNote headerText
302+ else [Link nullAttr (walk deNote headerText)
303+ ('#':ident, "")]
297304elementToListItem _ (Blk _) = []
298305
299306attrsToMarkdown :: Attr -> Doc
@@ -334,7 +341,7 @@ beginsWithOrderedListMarker str =
334341 Left _ -> False
335342 Right _ -> True
336343
337−notesAndRefs :: WriterOptions -> MD Doc
344+notesAndRefs :: PandocMonad m => WriterOptions -> MD m Doc
338345notesAndRefs opts = do
339346 notes' <- reverse <$> gets stNotes >>= notesToMarkdown opts
340347 modify $ \s -> s { stNotes = [] }
@@ -345,16 +352,17 @@ notesAndRefs opts = do
345352 if | writerReferenceLocation opts == EndOfDocument -> empty
346353 | isEmpty notes' && isEmpty refs' -> empty
347354 | otherwise -> blankline
348−
355+
349356 return $
350357 (if isEmpty notes' then empty else blankline <> notes') <>
351358 (if isEmpty refs' then empty else blankline <> refs') <>
352359 endSpacing
353360
354361-- | Convert Pandoc block element to markdown.
355−blockToMarkdown :: WriterOptions -- ^ Options
362+blockToMarkdown :: PandocMonad m
363+ => WriterOptions -- ^ Options
356364 -> Block -- ^ Block element
357− -> MD Doc
365+ -> MD m Doc
358366blockToMarkdown opts blk =
359367 local (\env -> env {envBlockLevel = envBlockLevel env + 1}) $
360368 do doc <- blockToMarkdown' opts blk
@@ -363,9 +371,10 @@ blockToMarkdown opts blk =
363371 then notesAndRefs opts >>= (\d -> return $ doc <> d)
364372 else return doc
365373
366−blockToMarkdown' :: WriterOptions -- ^ Options
367− -> Block -- ^ Block element
368− -> MD Doc
374+blockToMarkdown' :: PandocMonad m
375+ => WriterOptions -- ^ Options
376+ -> Block -- ^ Block element
377+ -> MD m Doc
369378blockToMarkdown' _ Null = return empty
370379blockToMarkdown' opts (Div attrs ils) = do
371380 contents <- blockListToMarkdown opts ils
@@ -526,8 +535,8 @@ blockToMarkdown' opts t@(Table caption aligns widths headers rows) = do
526535 gridTable opts (all null headers) aligns widths
527536 rawHeaders rawRows
528537 | isEnabled Ext_raw_html opts -> fmap (id,) $
529− return $ text $ writeHtmlString def
530− $ Pandoc nullMeta [t]
538+ text <$>
539+ (writeHtml5String def $ Pandoc nullMeta [t])
531540 | otherwise -> return $ (id, text "[TABLE]")
532541 return $ nst $ tbl $$ blankline $$ caption'' $$ blankline
533542blockToMarkdown' opts (BulletList items) = do
@@ -550,7 +559,7 @@ blockToMarkdown' opts (DefinitionList items) = do
550559 contents <- inList $ mapM (definitionListItemToMarkdown opts) items
551560 return $ cat contents <> blankline
552561
553−inList :: MD a -> MD a
562+inList :: Monad m => MD m a -> MD m a
554563inList p = local (\env -> env {envInList = True}) p
555564
556565addMarkdownAttribute :: String -> String
@@ -562,7 +571,7 @@ addMarkdownAttribute s =
562571 x /= "markdown"]
563572 _ -> s
564573
565−pipeTable :: Bool -> [Alignment] -> [Doc] -> [[Doc]] -> MD Doc
574+pipeTable :: PandocMonad m => Bool -> [Alignment] -> [Doc] -> [[Doc]] -> MD m Doc
566575pipeTable headless aligns rawHeaders rawRows = do
567576 let sp = text " "
568577 let blockFor AlignLeft x y = lblock (x + 2) (sp <> y) <> lblock 0 empty
@@ -590,8 +599,8 @@ pipeTable headless aligns rawHeaders rawRows = do
590599 let body = vcat $ map torow rawRows
591600 return $ header $$ border $$ body
592601
593−pandocTable :: WriterOptions -> Bool -> [Alignment] -> [Double]
594− -> [Doc] -> [[Doc]] -> MD Doc
602+pandocTable :: PandocMonad m => WriterOptions -> Bool -> [Alignment] -> [Double]
603+ -> [Doc] -> [[Doc]] -> MD m Doc
595604pandocTable opts headless aligns widths rawHeaders rawRows = do
596605 let isSimple = all (==0) widths
597606 let alignHeader alignment = case alignment of
@@ -642,8 +651,8 @@ pandocTable opts headless aligns widths rawHeaders rawRows = do
642651 else border
643652 return $ head'' $$ underline $$ body $$ bottom
644653
645−gridTable :: WriterOptions -> Bool -> [Alignment] -> [Double]
646− -> [Doc] -> [[Doc]] -> MD Doc
654+gridTable :: PandocMonad m => WriterOptions -> Bool -> [Alignment] -> [Double]
655+ -> [Doc] -> [[Doc]] -> MD m Doc
647656gridTable opts headless aligns widths headers' rawRows = do
648657 let numcols = length headers'
649658 let widths' = if all (==0) widths
@@ -697,7 +706,7 @@ itemEndsWithTightList bs =
697706 _ -> False
698707
699708-- | Convert bullet list item (list of blocks) to markdown.
700−bulletListItemToMarkdown :: WriterOptions -> [Block] -> MD Doc
709+bulletListItemToMarkdown :: PandocMonad m => WriterOptions -> [Block] -> MD m Doc
701710bulletListItemToMarkdown opts bs = do
702711 contents <- blockListToMarkdown opts bs
703712 let sps = replicate (writerTabStop opts - 2) ' '
@@ -709,10 +718,11 @@ bulletListItemToMarkdown opts bs = do
709718 return $ hang (writerTabStop opts) start $ contents' <> cr
710719
711720-- | Convert ordered list item (a list of blocks) to markdown.
712−orderedListItemToMarkdown :: WriterOptions -- ^ options
721+orderedListItemToMarkdown :: PandocMonad m
722+ => WriterOptions -- ^ options
713723 -> String -- ^ list item marker
714724 -> [Block] -- ^ list item (list of blocks)
715− -> MD Doc
725+ -> MD m Doc
716726orderedListItemToMarkdown opts marker bs = do
717727 contents <- blockListToMarkdown opts bs
718728 let sps = case length marker - writerTabStop opts of
@@ -726,9 +736,10 @@ orderedListItemToMarkdown opts marker bs = do
726736 return $ hang (writerTabStop opts) start $ contents' <> cr
727737
728738-- | Convert definition list item (label, list of blocks) to markdown.
729−definitionListItemToMarkdown :: WriterOptions
739+definitionListItemToMarkdown :: PandocMonad m
740+ => WriterOptions
730741 -> ([Inline],[[Block]])
731− -> MD Doc
742+ -> MD m Doc
732743definitionListItemToMarkdown opts (label, defs) = do
733744 labelText <- inlineListToMarkdown opts label
734745 defs' <- mapM (mapM (blockToMarkdown opts)) defs
@@ -758,9 +769,10 @@ definitionListItemToMarkdown opts (label, defs) = do
758769 vsep (map vsep defs') <> blankline
759770
760771-- | Convert list of Pandoc block elements to markdown.
761−blockListToMarkdown :: WriterOptions -- ^ Options
772+blockListToMarkdown :: PandocMonad m
773+ => WriterOptions -- ^ Options
762774 -> [Block] -- ^ List of block elements
763− -> MD Doc
775+ -> MD m Doc
764776blockListToMarkdown opts blocks =
765777 mapM (blockToMarkdown opts) (fixBlocks blocks) >>= return . cat
766778 -- insert comment between list and indented code block, or the
@@ -787,25 +799,25 @@ blockListToMarkdown opts blocks =
787799
788800-- | Get reference for target; if none exists, create unique one and return.
789801-- Prefer label if possible; otherwise, generate a unique key.
790−getReference :: Attr -> [Inline] -> Target -> MD [Inline]
802+getReference :: PandocMonad m => Attr -> [Inline] -> Target -> MD m [Inline]
791803getReference attr label target = do
792804 st <- get
793805 case find (\(_,t,a) -> t == target && a == attr) (stRefs st) of
794806 Just (ref, _, _) -> return ref
795807 Nothing -> do
796− let label' = case find (\(l,_,_) -> l == label) (stRefs st) of
797− Just _ -> -- label is used; generate numerical label
798− case find (\n -> notElem [Str (show n)]
799− (map (\(l,_,_) -> l) (stRefs st)))
800− [1..(10000 :: Integer)] of
801− Just x -> [Str (show x)]
802− Nothing -> error "no unique label"
803− Nothing -> label
808+ label' <- case find (\(l,_,_) -> l == label) (stRefs st) of
809+ Just _ -> -- label is used; generate numerical label
810+ case find (\n -> notElem [Str (show n)]
811+ (map (\(l,_,_) -> l) (stRefs st)))
812+ [1..(10000 :: Integer)] of
813+ Just x -> return [Str (show x)]
814+ Nothing -> throwError $ PandocSomeError "no unique label"
815+ Nothing -> return label
804816 modify (\s -> s{ stRefs = (label', target, attr) : stRefs st })
805817 return label'
806818
807819-- | Convert list of Pandoc inline elements to markdown.
808−inlineListToMarkdown :: WriterOptions -> [Inline] -> MD Doc
820+inlineListToMarkdown :: PandocMonad m => WriterOptions -> [Inline] -> MD m Doc
809821inlineListToMarkdown opts lst = do
810822 inlist <- asks envInList
811823 go (if inlist then avoidBadWrapsInList lst else lst)
@@ -866,7 +878,7 @@ isRight (Right _) = True
866878isRight (Left _) = False
867879
868880-- | Convert Pandoc inline element to markdown.
869−inlineToMarkdown :: WriterOptions -> Inline -> MD Doc
881+inlineToMarkdown :: PandocMonad m => WriterOptions -> Inline -> MD m Doc
870882inlineToMarkdown opts (Span attrs ils) = do
871883 plain <- asks envPlain
872884 contents <- inlineListToMarkdown opts ils
@@ -940,10 +952,14 @@ inlineToMarkdown opts (SmallCaps lst) = do
940952 else inlineListToMarkdown opts $ capitalize lst
941953inlineToMarkdown opts (Quoted SingleQuote lst) = do
942954 contents <- inlineListToMarkdown opts lst
943− return $ "‘" <> contents <> "’"
955+ return $ if isEnabled Ext_smart opts
956+ then "'" <> contents <> "'"
957+ else "‘" <> contents <> "’"
944958inlineToMarkdown opts (Quoted DoubleQuote lst) = do
945959 contents <- inlineListToMarkdown opts lst
946− return $ "“" <> contents <> "”"
960+ return $ if isEnabled Ext_smart opts
961+ then "\"" <> contents <> "\""
962+ else "“" <> contents <> "”"
947963inlineToMarkdown opts (Code attr str) = do
948964 let tickGroups = filter (\s -> '`' `elem` s) $ group str
949965 let longest = if null tickGroups
@@ -960,9 +976,13 @@ inlineToMarkdown opts (Code attr str) = do
960976 else return $ text (marker ++ spacer ++ str ++ spacer ++ marker) <> attrs
961977inlineToMarkdown opts (Str str) = do
962978 isPlain <- asks envPlain
963− if isPlain
964− then return $ text str
965− else return $ text $ escapeString opts str
979+ let str' = (if isEnabled Ext_smart opts
980+ then unsmartify opts
981+ else id) $
982+ if isPlain
983+ then str
984+ else escapeString opts str
985+ return $ text str'
966986inlineToMarkdown opts (Math InlineMath str) =
967987 case writerHTMLMathMethod opts of
968988 WebTeX url ->
@@ -976,9 +996,9 @@ inlineToMarkdown opts (Math InlineMath str) =
976996 return $ "\\\\(" <> text str <> "\\\\)"
977997 | otherwise -> do
978998 plain <- asks envPlain
979− inlineListToMarkdown opts $
980− (if plain then makeMathPlainer else id) $
981− texMathToInlines InlineMath str
999+ texMathToInlines InlineMath str >>=
1000+ inlineListToMarkdown opts .
1001+ (if plain then makeMathPlainer else id)
9821002inlineToMarkdown opts (Math DisplayMath str) =
9831003 case writerHTMLMathMethod opts of
9841004 WebTeX url -> (\x -> blankline <> x <> blankline) `fmap`
@@ -991,7 +1011,7 @@ inlineToMarkdown opts (Math DisplayMath str) =
9911011 | isEnabled Ext_tex_math_double_backslash opts ->
9921012 return $ "\\\\[" <> text str <> "\\\\]"
9931013 | otherwise -> (\x -> cr <> x <> cr) `fmap`
994− inlineListToMarkdown opts (texMathToInlines DisplayMath str)
1014+ (texMathToInlines DisplayMath str >>= inlineListToMarkdown opts)
9951015inlineToMarkdown opts (RawInline f str) = do
9961016 plain <- asks envPlain
9971017 if not plain &&
@@ -1052,7 +1072,7 @@ inlineToMarkdown opts lnk@(Link attr txt (src, tit))
10521072 | isEnabled Ext_raw_html opts &&
10531073 not (isEnabled Ext_link_attributes opts) &&
10541074 attr /= nullAttr = -- use raw HTML
1055− return $ text $ trim $ writeHtmlString def $ Pandoc nullMeta [Plain [lnk]]
1075+ (text . trim) <$> writeHtml5String def (Pandoc nullMeta [Plain [lnk]])
10561076 | otherwise = do
10571077 plain <- asks envPlain
10581078 linktext <- inlineListToMarkdown opts txt
@@ -1091,7 +1111,7 @@ inlineToMarkdown opts img@(Image attr alternate (source, tit))
10911111 | isEnabled Ext_raw_html opts &&
10921112 not (isEnabled Ext_link_attributes opts) &&
10931113 attr /= nullAttr = -- use raw HTML
1094− return $ text $ trim $ writeHtmlString def $ Pandoc nullMeta [Plain [img]]
1114+ (text . trim) <$> writeHtml5String def (Pandoc nullMeta [Plain [img]])
10951115 | otherwise = do
10961116 plain <- asks envPlain
10971117 let txt = if null alternate || alternate == [Str source]
@@ -1115,3 +1135,16 @@ makeMathPlainer = walk go
11151135 where
11161136 go (Emph xs) = Span nullAttr xs
11171137 go x = x
1138+
1139+unsmartify :: WriterOptions -> String -> String
1140+unsmartify opts ('\8217':xs) = '\'' : unsmartify opts xs
1141+unsmartify opts ('\8230':xs) = "..." ++ unsmartify opts xs
1142+unsmartify opts ('\8211':xs)
1143+ | isEnabled Ext_old_dashes opts = '-' : unsmartify opts xs
1144+ | otherwise = "--" ++ unsmartify opts xs
1145+unsmartify opts ('\8212':xs)
1146+ | isEnabled Ext_old_dashes opts = "--" ++ unsmartify opts xs
1147+ | otherwise = "---" ++ unsmartify opts xs
1148+unsmartify opts (x:xs) = x : unsmartify opts xs
1149+unsmartify _ [] = []
1150+
src/Text/Pandoc/Writers/Math.hsnew file+49
@@ -0,0 +1,49 @@
1+module Text.Pandoc.Writers.Math
2+ ( texMathToInlines
3+ , convertMath
4+ )
5+where
6+
7+import Text.Pandoc.Class
8+import Text.Pandoc.Definition
9+import Text.TeXMath (Exp, writePandoc, DisplayType(..), readTeX)
10+
11+-- | Converts a raw TeX math formula to a list of 'Pandoc' inlines.
12+-- Defaults to raw formula between @$@ or @$$@ characters if entire formula
13+-- can't be converted.
14+texMathToInlines :: PandocMonad m
15+ => MathType
16+ -> String -- ^ String to parse (assumes @'\n'@ line endings)
17+ -> m [Inline]
18+texMathToInlines mt inp = do
19+ res <- convertMath writePandoc mt inp
20+ case res of
21+ Right (Just ils) -> return ils
22+ Right (Nothing) -> do
23+ warning $ "Could not render TeX math as unicode, rendering as raw TeX:\n" ++ inp
24+ return [mkFallback mt inp]
25+ Left il -> return [il]
26+
27+mkFallback :: MathType -> String -> Inline
28+mkFallback mt str = Str (delim ++ str ++ delim)
29+ where delim = case mt of
30+ DisplayMath -> "$$"
31+ InlineMath -> "$"
32+
33+-- | Converts a raw TeX math formula using a writer function,
34+-- issuing a warning and producing a fallback (a raw string)
35+-- on failure.
36+convertMath :: PandocMonad m
37+ => (DisplayType -> [Exp] -> a) -> MathType -> String
38+ -> m (Either Inline a)
39+convertMath writer mt str = do
40+ case writer dt <$> readTeX str of
41+ Right r -> return (Right r)
42+ Left e -> do
43+ warning $ "Could not convert TeX math, rendering as raw TeX:\n" ++
44+ str ++ "\n" ++ e
45+ return (Left $ mkFallback mt str)
46+ where dt = case mt of
47+ DisplayMath -> DisplayBlock
48+ InlineMath -> DisplayInline
49+
src/Text/Pandoc/Writers/MediaWiki.hs+4−3
@@ -42,6 +42,7 @@ import Data.List ( intersect, intercalate )
4242import Network.URI ( isURI )
4343import Control.Monad.Reader
4444import Control.Monad.State
45+import Text.Pandoc.Class (PandocMonad)
4546
4647data WriterState = WriterState {
4748 stNotes :: Bool -- True if there are notes
@@ -57,8 +58,8 @@ data WriterReader = WriterReader {
5758type MediaWikiWriter = ReaderT WriterReader (State WriterState)
5859
5960-- | Convert Pandoc to MediaWiki.
60−writeMediaWiki :: WriterOptions -> Pandoc -> String
61−writeMediaWiki opts document =
61+writeMediaWiki :: PandocMonad m => WriterOptions -> Pandoc -> m String
62+writeMediaWiki opts document = return $
6263 let initialState = WriterState { stNotes = False, stOptions = opts }
6364 env = WriterReader { options = opts, listLevel = [], useTags = False }
6465 in evalState (runReaderT (pandocToMediaWiki document) env) initialState
@@ -402,7 +403,7 @@ inlineToMediaWiki (RawInline f str)
402403 | f == Format "html" = return str
403404 | otherwise = return ""
404405
405−inlineToMediaWiki (LineBreak) = return "<br />\n"
406+inlineToMediaWiki LineBreak = return "<br />\n"
406407
407408inlineToMediaWiki SoftBreak = do
408409 wrapText <- gets (writerWrapText . stOptions)
src/Text/Pandoc/Writers/Native.hs+3−2
@@ -34,6 +34,7 @@ import Text.Pandoc.Options ( WriterOptions(..), WrapOption(..) )
3434import Data.List ( intersperse )
3535import Text.Pandoc.Definition
3636import Text.Pandoc.Pretty
37+import Text.Pandoc.Class (PandocMonad)
3738
3839prettyList :: [Doc] -> Doc
3940prettyList ds =
@@ -66,8 +67,8 @@ prettyBlock (Div attr blocks) =
6667prettyBlock block = text $ show block
6768
6869-- | Prettyprint Pandoc document.
69−writeNative :: WriterOptions -> Pandoc -> String
70−writeNative opts (Pandoc meta blocks) =
70+writeNative :: PandocMonad m => WriterOptions -> Pandoc -> m String
71+writeNative opts (Pandoc meta blocks) = return $
7172 let colwidth = if writerWrapText opts == WrapAuto
7273 then Just $ writerColumns opts
7374 else Nothing
src/Text/Pandoc/Writers/ODT.hs+49−31
@@ -29,7 +29,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2929Conversion of 'Pandoc' documents to ODT.
3030-}
3131module Text.Pandoc.Writers.ODT ( writeODT ) where
32−import Data.IORef
3332import Data.List ( isPrefixOf )
3433import Data.Maybe ( fromMaybe )
3534import Text.XML.Light.Output
@@ -38,40 +37,59 @@ import qualified Data.ByteString.Lazy as B
3837import Text.Pandoc.UTF8 ( fromStringLazy )
3938import Codec.Archive.Zip
4039import Text.Pandoc.Options ( WriterOptions(..), WrapOption(..) )
41−import Text.Pandoc.Shared ( stringify, fetchItem', warn,
42− getDefaultReferenceODT )
40+import Text.Pandoc.Shared ( stringify )
4341import Text.Pandoc.ImageSize
4442import Text.Pandoc.MIME ( getMimeType, extensionFromMimeType )
4543import Text.Pandoc.Definition
4644import Text.Pandoc.Walk
4745import Text.Pandoc.Writers.Shared ( fixDisplayMath )
4846import Text.Pandoc.Writers.OpenDocument ( writeOpenDocument )
49−import Control.Monad (liftM)
47+import Control.Monad.State
48+import Control.Monad.Except (runExceptT)
49+import Text.Pandoc.Error (PandocError)
5050import Text.Pandoc.XML
5151import Text.Pandoc.Pretty
52−import qualified Control.Exception as E
53−import Data.Time.Clock.POSIX ( getPOSIXTime )
5452import System.FilePath ( takeExtension, takeDirectory, (<.>))
53+import Text.Pandoc.Class ( PandocMonad )
54+import qualified Text.Pandoc.Class as P
55+
56+data ODTState = ODTState { stEntries :: [Entry]
57+ }
58+
59+type O m = StateT ODTState m
5560
5661-- | Produce an ODT file from a Pandoc document.
57−writeODT :: WriterOptions -- ^ Writer options
62+writeODT :: PandocMonad m
63+ => WriterOptions -- ^ Writer options
5864 -> Pandoc -- ^ Document to convert
59− -> IO B.ByteString
60−writeODT opts doc@(Pandoc meta _) = do
65+ -> m B.ByteString
66+writeODT opts doc =
67+ let initState = ODTState{ stEntries = []
68+ }
69+ in
70+ evalStateT (pandocToODT opts doc) initState
71+
72+-- | Produce an ODT file from a Pandoc document.
73+pandocToODT :: PandocMonad m
74+ => WriterOptions -- ^ Writer options
75+ -> Pandoc -- ^ Document to convert
76+ -> O m B.ByteString
77+pandocToODT opts doc@(Pandoc meta _) = do
6178 let datadir = writerUserDataDir opts
6279 let title = docTitle meta
6380 refArchive <-
64− case writerReferenceODT opts of
65− Just f -> liftM toArchive $ B.readFile f
66− Nothing -> getDefaultReferenceODT datadir
81+ case writerReferenceDoc opts of
82+ Just f -> liftM toArchive $ lift $ P.readFileLazy f
83+ Nothing -> lift $ (toArchive . B.fromStrict) <$>
84+ P.readDataFile datadir "reference.odt"
6785 -- handle formulas and pictures
68− picEntriesRef <- newIORef ([] :: [Entry])
69− doc' <- walkM (transformPicMath opts picEntriesRef) $ walk fixDisplayMath doc
70− let newContents = writeOpenDocument opts{writerWrapText = WrapNone} doc'
71− epochtime <- floor `fmap` getPOSIXTime
86+ -- picEntriesRef <- P.newIORef ([] :: [Entry])
87+ doc' <- walkM (transformPicMath opts) $ walk fixDisplayMath doc
88+ newContents <- lift $ writeOpenDocument opts{writerWrapText = WrapNone} doc'
89+ epochtime <- floor `fmap` (lift P.getPOSIXTime)
7290 let contentEntry = toEntry "content.xml" epochtime
7391 $ fromStringLazy newContents
74− picEntries <- readIORef picEntriesRef
92+ picEntries <- gets stEntries
7593 let archive = foldr addEntryToArchive refArchive
7694 $ contentEntry : picEntries
7795 -- construct META-INF/manifest.xml based on archive
@@ -126,18 +144,18 @@ writeODT opts doc@(Pandoc meta _) = do
126144 return $ fromArchive archive''
127145
128146-- | transform both Image and Math elements
129−transformPicMath :: WriterOptions -> IORef [Entry] -> Inline -> IO Inline
130−transformPicMath opts entriesRef (Image attr@(id', cls, _) lab (src,t)) = do
131− res <- fetchItem' (writerMediaBag opts) (writerSourceURL opts) src
147+transformPicMath :: PandocMonad m => WriterOptions ->Inline -> O m Inline
148+transformPicMath opts (Image attr@(id', cls, _) lab (src,t)) = do
149+ res <- runExceptT $ lift $ P.fetchItem (writerSourceURL opts) src
132150 case res of
133− Left (_ :: E.SomeException) -> do
134− warn $ "Could not find image `" ++ src ++ "', skipping..."
151+ Left (_ :: PandocError) -> do
152+ lift $ P.warning $ "Could not find image `" ++ src ++ "', skipping..."
135153 return $ Emph lab
136154 Right (img, mbMimeType) -> do
137155 (ptX, ptY) <- case imageSize img of
138156 Right s -> return $ sizeInPoints s
139157 Left msg -> do
140− warn $ "Could not determine image size in `" ++
158+ lift $ P.warning $ "Could not determine image size in `" ++
141159 src ++ "': " ++ msg
142160 return (100, 100)
143161 let dims =
@@ -155,28 +173,28 @@ transformPicMath opts entriesRef (Image attr@(id', cls, _) lab (src,t)) = do
155173 Just dim -> Just $ Inch $ inInch opts dim
156174 Nothing -> Nothing
157175 let newattr = (id', cls, dims)
158− entries <- readIORef entriesRef
176+ entries <- gets stEntries
159177 let extension = fromMaybe (takeExtension $ takeWhile (/='?') src)
160178 (mbMimeType >>= extensionFromMimeType)
161179 let newsrc = "Pictures/" ++ show (length entries) <.> extension
162180 let toLazy = B.fromChunks . (:[])
163− epochtime <- floor `fmap` getPOSIXTime
181+ epochtime <- floor `fmap` (lift P.getPOSIXTime)
164182 let entry = toEntry newsrc epochtime $ toLazy img
165− modifyIORef entriesRef (entry:)
183+ modify $ \st -> st{ stEntries = entry : entries }
166184 return $ Image newattr lab (newsrc, t)
167−transformPicMath _ entriesRef (Math t math) = do
168− entries <- readIORef entriesRef
185+transformPicMath _ (Math t math) = do
186+ entries <- gets stEntries
169187 let dt = if t == InlineMath then DisplayInline else DisplayBlock
170188 case writeMathML dt <$> readTeX math of
171189 Left _ -> return $ Math t math
172190 Right r -> do
173191 let conf = useShortEmptyTags (const False) defaultConfigPP
174192 let mathml = ppcTopElement conf r
175− epochtime <- floor `fmap` getPOSIXTime
193+ epochtime <- floor `fmap` (lift $ P.getPOSIXTime)
176194 let dirname = "Formula-" ++ show (length entries) ++ "/"
177195 let fname = dirname ++ "content.xml"
178196 let entry = toEntry fname epochtime (fromStringLazy mathml)
179− modifyIORef entriesRef (entry:)
197+ modify $ \st -> st{ stEntries = entry : entries }
180198 return $ RawInline (Format "opendocument") $ render Nothing $
181199 inTags False "draw:frame" [("text:anchor-type",
182200 if t == DisplayMath
@@ -189,4 +207,4 @@ transformPicMath _ entriesRef (Math t math) = do
189207 , ("xlink:show", "embed")
190208 , ("xlink:actuate", "onLoad")]
191209
192−transformPicMath _ _ x = return x
210+transformPicMath _ x = return x
src/Text/Pandoc/Writers/OPML.hs+37−28
@@ -35,34 +35,37 @@ import Text.Pandoc.Writers.Shared
3535import Text.Pandoc.Shared
3636import Text.Pandoc.Options
3737import Text.Pandoc.Templates (renderTemplate')
38−import Text.Pandoc.Writers.HTML (writeHtmlString)
38+import Text.Pandoc.Writers.HTML (writeHtml5String)
3939import Text.Pandoc.Writers.Markdown (writeMarkdown)
4040import Text.Pandoc.Pretty
4141import Text.Pandoc.Compat.Time
4242import qualified Text.Pandoc.Builder as B
43+import Text.Pandoc.Error
44+import Control.Monad.Except (throwError)
45+import Text.Pandoc.Class (PandocMonad)
4346
4447-- | Convert Pandoc document to string in OPML format.
45−writeOPML :: WriterOptions -> Pandoc -> String
46−writeOPML opts (Pandoc meta blocks) =
48+writeOPML :: PandocMonad m => WriterOptions -> Pandoc -> m String
49+writeOPML opts (Pandoc meta blocks) = do
4750 let elements = hierarchicalize blocks
4851 colwidth = if writerWrapText opts == WrapAuto
4952 then Just $ writerColumns opts
5053 else Nothing
5154 meta' = B.setMeta "date" (B.str $ convertDate $ docDate meta) meta
52− Just metadata = metaToJSON opts
53− (Just . writeMarkdown def . Pandoc nullMeta)
54− (Just . trimr . writeMarkdown def . Pandoc nullMeta .
55− (\ils -> [Plain ils]))
56− meta'
57− main = render colwidth $ vcat (map (elementToOPML opts) elements)
58− context = defField "body" main metadata
59− in case writerTemplate opts of
60− Nothing -> main
61− Just tpl -> renderTemplate' tpl context
55+ metadata <- metaToJSON opts
56+ (writeMarkdown def . Pandoc nullMeta)
57+ (\ils -> trimr <$> (writeMarkdown def $ Pandoc nullMeta [Plain ils]))
58+ meta'
59+ main <- (render colwidth . vcat) <$> (mapM (elementToOPML opts) elements)
60+ let context = defField "body" main metadata
61+ return $ case writerTemplate opts of
62+ Nothing -> main
63+ Just tpl -> renderTemplate' tpl context
6264
63−writeHtmlInlines :: [Inline] -> String
64−writeHtmlInlines ils = trim $ writeHtmlString def
65− $ Pandoc nullMeta [Plain ils]
65+
66+writeHtmlInlines :: PandocMonad m => [Inline] -> m String
67+writeHtmlInlines ils =
68+ trim <$> (writeHtml5String def $ Pandoc nullMeta [Plain ils])
6669
6770-- date format: RFC 822: Thu, 14 Jul 2005 23:41:05 GMT
6871showDateTimeRFC822 :: UTCTime -> String
@@ -78,17 +81,23 @@ convertDate ils = maybe "" showDateTimeRFC822 $
7881 defaultTimeLocale "%F" =<< (normalizeDate $ stringify ils)
7982
8083-- | Convert an Element to OPML.
81−elementToOPML :: WriterOptions -> Element -> Doc
82−elementToOPML _ (Blk _) = empty
83−elementToOPML opts (Sec _ _num _ title elements) =
84− let isBlk (Blk _) = True
84+elementToOPML :: PandocMonad m => WriterOptions -> Element -> m Doc
85+elementToOPML _ (Blk _) = return empty
86+elementToOPML opts (Sec _ _num _ title elements) = do
87+ let isBlk :: Element -> Bool
88+ isBlk (Blk _) = True
8589 isBlk _ = False
86− fromBlk (Blk x) = x
87− fromBlk _ = error "fromBlk called on non-block"
90+
91+ fromBlk :: PandocMonad m => Element -> m Block
92+ fromBlk (Blk x) = return x
93+ fromBlk _ = throwError $ PandocSomeError "fromBlk called on non-block"
94+
8895 (blocks, rest) = span isBlk elements
89− attrs = [("text", writeHtmlInlines title)] ++
90− [("_note", writeMarkdown def (Pandoc nullMeta
91− (map fromBlk blocks)))
92− | not (null blocks)]
93− in inTags True "outline" attrs $
94− vcat (map (elementToOPML opts) rest)
96+ htmlIls <- writeHtmlInlines title
97+ md <- if null blocks
98+ then return []
99+ else do blks <- mapM fromBlk blocks
100+ writeMarkdown def $ Pandoc nullMeta blks
101+ let attrs = [("text", htmlIls)] ++ [("_note", md) | not (null blocks)]
102+ o <- mapM (elementToOPML opts) rest
103+ return $ inTags True "outline" attrs $ vcat o
src/Text/Pandoc/Writers/OpenDocument.hs+77−59
@@ -35,7 +35,7 @@ import Text.Pandoc.Options
3535import Text.Pandoc.XML
3636import Text.Pandoc.Shared (linesToPara)
3737import Text.Pandoc.Templates (renderTemplate')
38−import Text.Pandoc.Readers.TeXMath
38+import Text.Pandoc.Writers.Math
3939import Text.Pandoc.Pretty
4040import Text.Printf ( printf )
4141import Control.Arrow ( (***), (>>>) )
@@ -46,6 +46,7 @@ import qualified Data.Map as Map
4646import Text.Pandoc.Writers.Shared
4747import Data.List (sortBy)
4848import Data.Ord (comparing)
49+import Text.Pandoc.Class (PandocMonad)
4950
5051-- | Auxiliary function to convert Plain block to Para.
5152plainToPara :: Block -> Block
@@ -56,6 +57,8 @@ plainToPara x = x
5657-- OpenDocument writer
5758--
5859
60+type OD m = StateT WriterState m
61+
5962data WriterState =
6063 WriterState { stNotes :: [Doc]
6164 , stTableStyles :: [Doc]
@@ -88,40 +91,40 @@ defaultWriterState =
8891when :: Bool -> Doc -> Doc
8992when p a = if p then a else empty
9093
91−addTableStyle :: Doc -> State WriterState ()
94+addTableStyle :: PandocMonad m => Doc -> OD m ()
9295addTableStyle i = modify $ \s -> s { stTableStyles = i : stTableStyles s }
9396
94−addNote :: Doc -> State WriterState ()
97+addNote :: PandocMonad m => Doc -> OD m ()
9598addNote i = modify $ \s -> s { stNotes = i : stNotes s }
9699
97−addParaStyle :: Doc -> State WriterState ()
100+addParaStyle :: PandocMonad m => Doc -> OD m ()
98101addParaStyle i = modify $ \s -> s { stParaStyles = i : stParaStyles s }
99102
100−addTextStyle :: Set.Set TextStyle -> (String, Doc) -> State WriterState ()
103+addTextStyle :: PandocMonad m => Set.Set TextStyle -> (String, Doc) -> OD m ()
101104addTextStyle attrs i = modify $ \s ->
102105 s { stTextStyles = Map.insert attrs i (stTextStyles s) }
103106
104−addTextStyleAttr :: TextStyle -> State WriterState ()
107+addTextStyleAttr :: PandocMonad m => TextStyle -> OD m ()
105108addTextStyleAttr t = modify $ \s ->
106109 s { stTextStyleAttr = Set.insert t (stTextStyleAttr s) }
107110
108−increaseIndent :: State WriterState ()
111+increaseIndent :: PandocMonad m => OD m ()
109112increaseIndent = modify $ \s -> s { stIndentPara = 1 + stIndentPara s }
110113
111−resetIndent :: State WriterState ()
114+resetIndent :: PandocMonad m => OD m ()
112115resetIndent = modify $ \s -> s { stIndentPara = (stIndentPara s) - 1 }
113116
114−inTightList :: State WriterState a -> State WriterState a
117+inTightList :: PandocMonad m => OD m a -> OD m a
115118inTightList f = modify (\s -> s { stTight = True }) >> f >>= \r ->
116119 modify (\s -> s { stTight = False }) >> return r
117120
118−setInDefinitionList :: Bool -> State WriterState ()
121+setInDefinitionList :: PandocMonad m => Bool -> OD m ()
119122setInDefinitionList b = modify $ \s -> s { stInDefinition = b }
120123
121−setFirstPara :: State WriterState ()
124+setFirstPara :: PandocMonad m => OD m ()
122125setFirstPara = modify $ \s -> s { stFirstPara = True }
123126
124−inParagraphTags :: Doc -> State WriterState Doc
127+inParagraphTags :: PandocMonad m => Doc -> OD m Doc
125128inParagraphTags d | isEmpty d = return empty
126129inParagraphTags d = do
127130 b <- gets stFirstPara
@@ -137,7 +140,7 @@ inParagraphTagsWithStyle sty = inTags False "text:p" [("text:style-name", sty)]
137140inSpanTags :: String -> Doc -> Doc
138141inSpanTags s = inTags False "text:span" [("text:style-name",s)]
139142
140−withTextStyle :: TextStyle -> State WriterState a -> State WriterState a
143+withTextStyle :: PandocMonad m => TextStyle -> OD m a -> OD m a
141144withTextStyle s f = do
142145 oldTextStyleAttr <- gets stTextStyleAttr
143146 addTextStyleAttr s
@@ -145,7 +148,7 @@ withTextStyle s f = do
145148 modify $ \st -> st{ stTextStyleAttr = oldTextStyleAttr }
146149 return res
147150
148−inTextStyle :: Doc -> State WriterState Doc
151+inTextStyle :: PandocMonad m => Doc -> OD m Doc
149152inTextStyle d = do
150153 at <- gets stTextStyleAttr
151154 if Set.null at
@@ -166,7 +169,7 @@ inTextStyle d = do
166169 return $ inTags False
167170 "text:span" [("text:style-name",styleName)] d
168171
169−inHeaderTags :: Int -> Doc -> State WriterState Doc
172+inHeaderTags :: PandocMonad m => Int -> Doc -> OD m Doc
170173inHeaderTags i d =
171174 return $ inTags False "text:h" [ ("text:style-name", "Heading_20_" ++ show i)
172175 , ("text:outline-level", show i)] d
@@ -189,13 +192,13 @@ handleSpaces s
189192 rm [] = empty
190193
191194-- | Convert Pandoc document to string in OpenDocument format.
192−writeOpenDocument :: WriterOptions -> Pandoc -> String
193−writeOpenDocument opts (Pandoc meta blocks) =
195+writeOpenDocument :: PandocMonad m => WriterOptions -> Pandoc -> m String
196+writeOpenDocument opts (Pandoc meta blocks) = do
194197 let colwidth = if writerWrapText opts == WrapAuto
195198 then Just $ writerColumns opts
196199 else Nothing
197− render' = render colwidth
198− ((body, metadata),s) = flip runState
200+ let render' = render colwidth
201+ ((body, metadata),s) <- flip runStateT
199202 defaultWriterState $ do
200203 m <- metaToJSON opts
201204 (fmap (render colwidth) . blocksToOpenDocument opts)
@@ -203,38 +206,41 @@ writeOpenDocument opts (Pandoc meta blocks) =
203206 meta
204207 b <- render' `fmap` blocksToOpenDocument opts blocks
205208 return (b, m)
206− styles = stTableStyles s ++ stParaStyles s ++
209+ let styles = stTableStyles s ++ stParaStyles s ++
207210 map snd (reverse $ sortBy (comparing fst) $
208211 Map.elems (stTextStyles s))
209212 listStyle (n,l) = inTags True "text:list-style"
210213 [("style:name", "L" ++ show n)] (vcat l)
211− listStyles = map listStyle (stListStyles s)
212− automaticStyles = vcat $ reverse $ styles ++ listStyles
213− context = defField "body" body
214+ let listStyles = map listStyle (stListStyles s)
215+ let automaticStyles = vcat $ reverse $ styles ++ listStyles
216+ let context = defField "body" body
214217 $ defField "automatic-styles" (render' automaticStyles)
215218 $ metadata
216− in case writerTemplate opts of
217− Nothing -> body
218− Just tpl -> renderTemplate' tpl context
219+ return $ case writerTemplate opts of
220+ Nothing -> body
221+ Just tpl -> renderTemplate' tpl context
219222
220−withParagraphStyle :: WriterOptions -> String -> [Block] -> State WriterState Doc
223+withParagraphStyle :: PandocMonad m
224+ => WriterOptions -> String -> [Block] -> OD m Doc
221225withParagraphStyle o s (b:bs)
222226 | Para l <- b = go =<< inParagraphTagsWithStyle s <$> inlinesToOpenDocument o l
223227 | otherwise = go =<< blockToOpenDocument o b
224228 where go i = (<>) i <$> withParagraphStyle o s bs
225229withParagraphStyle _ _ [] = return empty
226230
227−inPreformattedTags :: String -> State WriterState Doc
231+inPreformattedTags :: PandocMonad m => String -> OD m Doc
228232inPreformattedTags s = do
229233 n <- paraStyle [("style:parent-style-name","Preformatted_20_Text")]
230234 return . inParagraphTagsWithStyle ("P" ++ show n) . handleSpaces $ s
231235
232−orderedListToOpenDocument :: WriterOptions -> Int -> [[Block]] -> State WriterState Doc
236+orderedListToOpenDocument :: PandocMonad m
237+ => WriterOptions -> Int -> [[Block]] -> OD m Doc
233238orderedListToOpenDocument o pn bs =
234239 vcat . map (inTagsIndented "text:list-item") <$>
235240 mapM (orderedItemToOpenDocument o pn . map plainToPara) bs
236241
237−orderedItemToOpenDocument :: WriterOptions -> Int -> [Block] -> State WriterState Doc
242+orderedItemToOpenDocument :: PandocMonad m
243+ => WriterOptions -> Int -> [Block] -> OD m Doc
238244orderedItemToOpenDocument o n (b:bs)
239245 | OrderedList a l <- b = newLevel a l
240246 | Para l <- b = go =<< inParagraphTagsWithStyle ("P" ++ show n) <$> inlinesToOpenDocument o l
@@ -254,7 +260,8 @@ isTightList (b:_)
254260 | Plain {} : _ <- b = True
255261 | otherwise = False
256262
257−newOrderedListStyle :: Bool -> ListAttributes -> State WriterState (Int,Int)
263+newOrderedListStyle :: PandocMonad m
264+ => Bool -> ListAttributes -> OD m (Int,Int)
258265newOrderedListStyle b a = do
259266 ln <- (+) 1 . length <$> gets stListStyles
260267 let nbs = orderedListLevelStyle a (ln, [])
@@ -262,7 +269,8 @@ newOrderedListStyle b a = do
262269 modify $ \s -> s { stListStyles = nbs : stListStyles s }
263270 return (ln,pn)
264271
265−bulletListToOpenDocument :: WriterOptions -> [[Block]] -> State WriterState Doc
272+bulletListToOpenDocument :: PandocMonad m
273+ => WriterOptions -> [[Block]] -> OD m Doc
266274bulletListToOpenDocument o b = do
267275 ln <- (+) 1 . length <$> gets stListStyles
268276 (pn,ns) <- if isTightList b then inTightList (bulletListStyle ln) else bulletListStyle ln
@@ -270,11 +278,13 @@ bulletListToOpenDocument o b = do
270278 is <- listItemsToOpenDocument ("P" ++ show pn) o b
271279 return $ inTags True "text:list" [("text:style-name", "L" ++ show ln)] is
272280
273−listItemsToOpenDocument :: String -> WriterOptions -> [[Block]] -> State WriterState Doc
281+listItemsToOpenDocument :: PandocMonad m
282+ => String -> WriterOptions -> [[Block]] -> OD m Doc
274283listItemsToOpenDocument s o is =
275284 vcat . map (inTagsIndented "text:list-item") <$> mapM (withParagraphStyle o s . map plainToPara) is
276285
277−deflistItemToOpenDocument :: WriterOptions -> ([Inline],[[Block]]) -> State WriterState Doc
286+deflistItemToOpenDocument :: PandocMonad m
287+ => WriterOptions -> ([Inline],[[Block]]) -> OD m Doc
278288deflistItemToOpenDocument o (t,d) = do
279289 let ts = if isTightList d
280290 then "Definition_20_Term_20_Tight" else "Definition_20_Term"
@@ -284,7 +294,8 @@ deflistItemToOpenDocument o (t,d) = do
284294 d' <- liftM vcat $ mapM (withParagraphStyle o ds . (map plainToPara)) d
285295 return $ t' $$ d'
286296
287−inBlockQuote :: WriterOptions -> Int -> [Block] -> State WriterState Doc
297+inBlockQuote :: PandocMonad m
298+ => WriterOptions -> Int -> [Block] -> OD m Doc
288299inBlockQuote o i (b:bs)
289300 | BlockQuote l <- b = do increaseIndent
290301 ni <- paraStyle
@@ -296,11 +307,11 @@ inBlockQuote o i (b:bs)
296307inBlockQuote _ _ [] = resetIndent >> return empty
297308
298309-- | Convert a list of Pandoc blocks to OpenDocument.
299−blocksToOpenDocument :: WriterOptions -> [Block] -> State WriterState Doc
310+blocksToOpenDocument :: PandocMonad m => WriterOptions -> [Block] -> OD m Doc
300311blocksToOpenDocument o b = vcat <$> mapM (blockToOpenDocument o) b
301312
302313-- | Convert a Pandoc block element to OpenDocument.
303−blockToOpenDocument :: WriterOptions -> Block -> State WriterState Doc
314+blockToOpenDocument :: PandocMonad m => WriterOptions -> Block -> OD m Doc
304315blockToOpenDocument o bs
305316 | Plain b <- bs = if null b
306317 then return empty
@@ -370,17 +381,23 @@ blockToOpenDocument o bs
370381 captionDoc <- withParagraphStyle o "FigureCaption" [Para caption]
371382 return $ imageDoc $$ captionDoc
372383
373−colHeadsToOpenDocument :: WriterOptions -> String -> [String] -> [[Block]] -> State WriterState Doc
384+colHeadsToOpenDocument :: PandocMonad m
385+ => WriterOptions -> String -> [String] -> [[Block]]
386+ -> OD m Doc
374387colHeadsToOpenDocument o tn ns hs =
375388 inTagsIndented "table:table-header-rows" . inTagsIndented "table:table-row" . vcat <$>
376389 mapM (tableItemToOpenDocument o tn) (zip ns hs)
377390
378−tableRowToOpenDocument :: WriterOptions -> String -> [String] -> [[Block]] -> State WriterState Doc
391+tableRowToOpenDocument :: PandocMonad m
392+ => WriterOptions -> String -> [String] -> [[Block]]
393+ -> OD m Doc
379394tableRowToOpenDocument o tn ns cs =
380395 inTagsIndented "table:table-row" . vcat <$>
381396 mapM (tableItemToOpenDocument o tn) (zip ns cs)
382397
383−tableItemToOpenDocument :: WriterOptions -> String -> (String,[Block])-> State WriterState Doc
398+tableItemToOpenDocument :: PandocMonad m
399+ => WriterOptions -> String -> (String,[Block])
400+ -> OD m Doc
384401tableItemToOpenDocument o tn (n,i) =
385402 let a = [ ("table:style-name" , tn ++ ".A1" )
386403 , ("office:value-type", "string" )
@@ -389,10 +406,10 @@ tableItemToOpenDocument o tn (n,i) =
389406 withParagraphStyle o n (map plainToPara i)
390407
391408-- | Convert a list of inline elements to OpenDocument.
392−inlinesToOpenDocument :: WriterOptions -> [Inline] -> State WriterState Doc
409+inlinesToOpenDocument :: PandocMonad m => WriterOptions -> [Inline] -> OD m Doc
393410inlinesToOpenDocument o l = hcat <$> toChunks o l
394411
395−toChunks :: WriterOptions -> [Inline] -> State WriterState [Doc]
412+toChunks :: PandocMonad m => WriterOptions -> [Inline] -> OD m [Doc]
396413toChunks _ [] = return []
397414toChunks o (x : xs)
398415 | isChunkable x = do
@@ -413,7 +430,7 @@ isChunkable SoftBreak = True
413430isChunkable _ = False
414431
415432-- | Convert an inline element to OpenDocument.
416−inlineToOpenDocument :: WriterOptions -> Inline -> State WriterState Doc
433+inlineToOpenDocument :: PandocMonad m => WriterOptions -> Inline -> OD m Doc
417434inlineToOpenDocument o ils
418435 = case ils of
419436 Space -> return space
@@ -432,7 +449,8 @@ inlineToOpenDocument o ils
432449 SmallCaps l -> withTextStyle SmallC $ inlinesToOpenDocument o l
433450 Quoted t l -> inQuotes t <$> inlinesToOpenDocument o l
434451 Code _ s -> inlinedCode $ preformatted s
435− Math t s -> inlinesToOpenDocument o (texMathToInlines t s)
452+ Math t s -> lift (texMathToInlines t s) >>=
453+ inlinesToOpenDocument o
436454 Cite _ l -> inlinesToOpenDocument o l
437455 RawInline f s -> if f == Format "opendocument"
438456 then return $ text s
@@ -473,18 +491,18 @@ inlineToOpenDocument o ils
473491 addNote nn
474492 return nn
475493
476−bulletListStyle :: Int -> State WriterState (Int,(Int,[Doc]))
477−bulletListStyle l =
478− let doStyles i = inTags True "text:list-level-style-bullet"
479− [ ("text:level" , show (i + 1) )
480− , ("text:style-name" , "Bullet_20_Symbols")
481− , ("style:num-suffix", "." )
482− , ("text:bullet-char", [bulletList !! i] )
483− ] (listLevelStyle (1 + i))
484− bulletList = map chr $ cycle [8226,8227,8259]
485− listElStyle = map doStyles [0..9]
486− in do pn <- paraListStyle l
487− return (pn, (l, listElStyle))
494+bulletListStyle :: PandocMonad m => Int -> OD m (Int,(Int,[Doc]))
495+bulletListStyle l = do
496+ let doStyles i = inTags True "text:list-level-style-bullet"
497+ [ ("text:level" , show (i + 1) )
498+ , ("text:style-name" , "Bullet_20_Symbols")
499+ , ("style:num-suffix", "." )
500+ , ("text:bullet-char", [bulletList !! i] )
501+ ] (listLevelStyle (1 + i))
502+ bulletList = map chr $ cycle [8226,8227,8259]
503+ listElStyle = map doStyles [0..9]
504+ pn <- paraListStyle l
505+ return (pn, (l, listElStyle))
488506
489507orderedListLevelStyle :: ListAttributes -> (Int, [Doc]) -> (Int,[Doc])
490508orderedListLevelStyle (s,n, d) (l,ls) =
@@ -538,10 +556,10 @@ tableStyle num wcs =
538556 columnStyles = map colStyle wcs
539557 in table $$ vcat columnStyles $$ cellStyle
540558
541−paraStyle :: [(String,String)] -> State WriterState Int
559+paraStyle :: PandocMonad m => [(String,String)] -> OD m Int
542560paraStyle attrs = do
543561 pn <- (+) 1 . length <$> gets stParaStyles
544− i <- (*) 0.5 . fromIntegral <$> gets stIndentPara :: State WriterState Double
562+ i <- (*) (0.5 :: Double) . fromIntegral <$> gets stIndentPara
545563 b <- gets stInDefinition
546564 t <- gets stTight
547565 let styleAttr = [ ("style:name" , "P" ++ show pn)
@@ -562,7 +580,7 @@ paraStyle attrs = do
562580 addParaStyle $ inTags True "style:style" (styleAttr ++ attrs) paraProps
563581 return pn
564582
565−paraListStyle :: Int -> State WriterState Int
583+paraListStyle :: PandocMonad m => Int -> OD m Int
566584paraListStyle l = paraStyle
567585 [("style:parent-style-name","Text_20_body")
568586 ,("style:list-style-name", "L" ++ show l )]
src/Text/Pandoc/Writers/Org.hs+4−3
@@ -42,6 +42,7 @@ import Text.Pandoc.Templates (renderTemplate')
4242import Data.Char ( isAlphaNum, toLower )
4343import Data.List ( isPrefixOf, intersect, intersperse, partition, transpose )
4444import Control.Monad.State
45+import Text.Pandoc.Class (PandocMonad)
4546
4647data WriterState =
4748 WriterState { stNotes :: [[Block]]
@@ -52,8 +53,8 @@ data WriterState =
5253 }
5354
5455-- | Convert Pandoc to Org.
55−writeOrg :: WriterOptions -> Pandoc -> String
56−writeOrg opts document =
56+writeOrg :: PandocMonad m => WriterOptions -> Pandoc -> m String
57+writeOrg opts document = return $
5758 let st = WriterState { stNotes = [], stLinks = False,
5859 stImages = False, stHasMath = False,
5960 stOptions = opts }
@@ -349,7 +350,7 @@ inlineToOrg (RawInline f@(Format f') str) =
349350 return $ if isRawFormat f
350351 then text str
351352 else "@@" <> text f' <> ":" <> text str <> "@@"
352−inlineToOrg (LineBreak) = return (text "\\\\" <> cr)
353+inlineToOrg LineBreak = return (text "\\\\" <> cr)
353354inlineToOrg Space = return space
354355inlineToOrg SoftBreak = do
355356 wrapText <- gets (writerWrapText . stOptions)
src/Text/Pandoc/Writers/RST.hs+3−2
@@ -44,6 +44,7 @@ import Network.URI (isURI)
4444import Text.Pandoc.Pretty
4545import Control.Monad.State
4646import Data.Char (isSpace, toLower)
47+import Text.Pandoc.Class (PandocMonad)
4748
4849type Refs = [([Inline], Target)]
4950
@@ -58,8 +59,8 @@ data WriterState =
5859 }
5960
6061-- | Convert Pandoc to RST.
61−writeRST :: WriterOptions -> Pandoc -> String
62−writeRST opts document =
62+writeRST :: PandocMonad m => WriterOptions -> Pandoc -> m String
63+writeRST opts document = return $
6364 let st = WriterState { stNotes = [], stLinks = [],
6465 stImages = [], stHasMath = False,
6566 stHasRawTeX = False, stOptions = opts,
src/Text/Pandoc/Writers/RTF.hs+172−126
@@ -1,3 +1,4 @@
1+{-# LANGUAGE ScopedTypeVariables #-}
12{-
23Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu>
34
@@ -27,38 +28,44 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2728
2829Conversion of 'Pandoc' documents to RTF (rich text format).
2930-}
30−module Text.Pandoc.Writers.RTF ( writeRTF, writeRTFWithEmbeddedImages ) where
31+module Text.Pandoc.Writers.RTF ( writeRTF
32+ ) where
3133import Text.Pandoc.Definition
3234import Text.Pandoc.Options
3335import Text.Pandoc.Shared
3436import Text.Pandoc.Writers.Shared
35−import Text.Pandoc.Readers.TeXMath
37+import Text.Pandoc.Writers.Math
3638import Text.Pandoc.Templates (renderTemplate')
3739import Text.Pandoc.Walk
40+import Text.Pandoc.Class (warning)
3841import Data.List ( isSuffixOf, intercalate )
3942import Data.Char ( ord, chr, isDigit )
4043import qualified Data.ByteString as B
4144import qualified Data.Map as M
4245import Text.Printf ( printf )
4346import Text.Pandoc.ImageSize
47+import Control.Monad.Except (throwError, runExceptT, lift)
48+import Text.Pandoc.Error
49+import Text.Pandoc.Class (PandocMonad)
50+import qualified Text.Pandoc.Class as P
4451
4552-- | Convert Image inlines into a raw RTF embedded image, read from a file,
4653-- or a MediaBag, or the internet.
4754-- If file not found or filetype not jpeg or png, leave the inline unchanged.
48−rtfEmbedImage :: WriterOptions -> Inline -> IO Inline
55+rtfEmbedImage :: PandocMonad m => WriterOptions -> Inline -> m Inline
4956rtfEmbedImage opts x@(Image attr _ (src,_)) = do
50− result <- fetchItem' (writerMediaBag opts) (writerSourceURL opts) src
57+ result <- runExceptT $ lift $ P.fetchItem (writerSourceURL opts) src
5158 case result of
5259 Right (imgdata, Just mime)
5360 | mime == "image/jpeg" || mime == "image/png" -> do
5461 let bytes = map (printf "%02x") $ B.unpack imgdata
55− let filetype = case mime of
56− "image/jpeg" -> "\\jpegblip"
57− "image/png" -> "\\pngblip"
58− _ -> error "Unknown file type"
62+ filetype <- case mime of
63+ "image/jpeg" -> return "\\jpegblip"
64+ "image/png" -> return "\\pngblip"
65+ _ -> throwError $ PandocSomeError "Unknown file type"
5966 sizeSpec <- case imageSize imgdata of
6067 Left msg -> do
61− warn $ "Could not determine image size in `" ++
68+ warning $ "Could not determine image size in `" ++
6269 src ++ "': " ++ msg
6370 return ""
6471 Right sz -> return $ "\\picw" ++ show xpx ++
@@ -70,56 +77,61 @@ rtfEmbedImage opts x@(Image attr _ (src,_)) = do
7077 (xpt, ypt) = desiredSizeInPoints opts attr sz
7178 let raw = "{\\pict" ++ filetype ++ sizeSpec ++ "\\bin " ++
7279 concat bytes ++ "}"
73− return $ if B.null imgdata
74− then x
75− else RawInline (Format "rtf") raw
76− _ -> return x
80+ if B.null imgdata
81+ then do
82+ warning $ "Image " ++ src ++ " contained no data, skipping."
83+ return x
84+ else return $ RawInline (Format "rtf") raw
85+ | otherwise -> do
86+ warning $ "Image " ++ src ++ " is not a jpeg or png, skipping."
87+ return x
88+ Right (_, Nothing) -> do
89+ warning $ "Could not determine image type for " ++ src ++ ", skipping."
90+ return x
91+ Left ( e :: PandocError ) -> do
92+ warning $ "Could not fetch image " ++ src ++ "\n" ++ show e
93+ return x
7794rtfEmbedImage _ x = return x
7895
79−-- | Convert Pandoc to a string in rich text format, with
80−-- images embedded as encoded binary data.
81−writeRTFWithEmbeddedImages :: WriterOptions -> Pandoc -> IO String
82−writeRTFWithEmbeddedImages options doc =
83− writeRTF options `fmap` walkM (rtfEmbedImage options) doc
84−
8596-- | Convert Pandoc to a string in rich text format.
86−writeRTF :: WriterOptions -> Pandoc -> String
87−writeRTF options (Pandoc meta@(Meta metamap) blocks) =
97+writeRTF :: PandocMonad m => WriterOptions -> Pandoc -> m String
98+writeRTF options doc = do
99+ -- handle images
100+ Pandoc meta@(Meta metamap) blocks <- walkM (rtfEmbedImage options) doc
88101 let spacer = not $ all null $ docTitle meta : docDate meta : docAuthors meta
89− toPlain (MetaBlocks [Para ils]) = MetaInlines ils
102+ let toPlain (MetaBlocks [Para ils]) = MetaInlines ils
90103 toPlain x = x
91− -- adjust title, author, date so we don't get para inside para
92− meta' = Meta $ M.adjust toPlain "title"
104+ -- adjust title, author, date so we don't get para inside para
105+ let meta' = Meta $ M.adjust toPlain "title"
93106 . M.adjust toPlain "author"
94107 . M.adjust toPlain "date"
95108 $ metamap
96− Just metadata = metaToJSON options
97− (Just . concatMap (blockToRTF 0 AlignDefault))
98− (Just . inlineListToRTF)
109+ metadata <- metaToJSON options
110+ (fmap concat . mapM (blockToRTF 0 AlignDefault))
111+ (inlinesToRTF)
99112 meta'
100− body = concatMap (blockToRTF 0 AlignDefault) blocks
101− isTOCHeader (Header lev _ _) = lev <= writerTOCDepth options
113+ body <- blocksToRTF 0 AlignDefault blocks
114+ let isTOCHeader (Header lev _ _) = lev <= writerTOCDepth options
102115 isTOCHeader _ = False
103− context = defField "body" body
116+ toc <- tableOfContents $ filter isTOCHeader blocks
117+ let context = defField "body" body
104118 $ defField "spacer" spacer
105119 $ (if writerTableOfContents options
106− then defField "toc"
107− (tableOfContents $ filter isTOCHeader blocks)
120+ then defField "toc" toc
108121 else id)
109122 $ metadata
110− in case writerTemplate options of
123+ return $ case writerTemplate options of
111124 Just tpl -> renderTemplate' tpl context
112125 Nothing -> case reverse body of
113126 ('\n':_) -> body
114127 _ -> body ++ "\n"
115128
116129-- | Construct table of contents from list of header blocks.
117−tableOfContents :: [Block] -> String
118−tableOfContents headers =
119− let contentsTree = hierarchicalize headers
120− in concatMap (blockToRTF 0 AlignDefault) $
121− [Header 1 nullAttr [Str "Contents"],
122− BulletList (map elementToListItem contentsTree)]
130+tableOfContents :: PandocMonad m => [Block] -> m String
131+tableOfContents headers = do
132+ let contents = map elementToListItem $ hierarchicalize headers
133+ blocksToRTF 0 AlignDefault $
134+ [Header 1 nullAttr [Str "Contents"], BulletList contents]
123135
124136elementToListItem :: Element -> [Block]
125137elementToListItem (Blk _) = []
@@ -221,66 +233,81 @@ orderedMarkers indent (start, style, delim) =
221233 _ -> orderedListMarkers (start, LowerAlpha, Period)
222234 else orderedListMarkers (start, style, delim)
223235
236+blocksToRTF :: PandocMonad m
237+ => Int
238+ -> Alignment
239+ -> [Block]
240+ -> m String
241+blocksToRTF indent align = fmap concat . mapM (blockToRTF indent align)
242+
224243-- | Convert Pandoc block element to RTF.
225−blockToRTF :: Int -- ^ indent level
244+blockToRTF :: PandocMonad m
245+ => Int -- ^ indent level
226246 -> Alignment -- ^ alignment
227247 -> Block -- ^ block to convert
228− -> String
229−blockToRTF _ _ Null = ""
248+ -> m String
249+blockToRTF _ _ Null = return ""
230250blockToRTF indent alignment (Div _ bs) =
231− concatMap (blockToRTF indent alignment) bs
251+ blocksToRTF indent alignment bs
232252blockToRTF indent alignment (Plain lst) =
233− rtfCompact indent 0 alignment $ inlineListToRTF lst
253+ rtfCompact indent 0 alignment <$> inlinesToRTF lst
234254blockToRTF indent alignment (Para lst) =
235− rtfPar indent 0 alignment $ inlineListToRTF lst
255+ rtfPar indent 0 alignment <$> inlinesToRTF lst
236256blockToRTF indent alignment (LineBlock lns) =
237257 blockToRTF indent alignment $ linesToPara lns
238258blockToRTF indent alignment (BlockQuote lst) =
239− concatMap (blockToRTF (indent + indentIncrement) alignment) lst
259+ blocksToRTF (indent + indentIncrement) alignment lst
240260blockToRTF indent _ (CodeBlock _ str) =
241− rtfPar indent 0 AlignLeft ("\\f1 " ++ (codeStringToRTF str))
261+ return $ rtfPar indent 0 AlignLeft ("\\f1 " ++ (codeStringToRTF str))
242262blockToRTF _ _ (RawBlock f str)
243− | f == Format "rtf" = str
244− | otherwise = ""
245−blockToRTF indent alignment (BulletList lst) = spaceAtEnd $
246− concatMap (listItemToRTF alignment indent (bulletMarker indent)) lst
247−blockToRTF indent alignment (OrderedList attribs lst) = spaceAtEnd $ concat $
248− zipWith (listItemToRTF alignment indent) (orderedMarkers indent attribs) lst
249−blockToRTF indent alignment (DefinitionList lst) = spaceAtEnd $
250− concatMap (definitionListItemToRTF alignment indent) lst
251−blockToRTF indent _ HorizontalRule =
263+ | f == Format "rtf" = return str
264+ | otherwise = return ""
265+blockToRTF indent alignment (BulletList lst) = (spaceAtEnd . concat) <$>
266+ mapM (listItemToRTF alignment indent (bulletMarker indent)) lst
267+blockToRTF indent alignment (OrderedList attribs lst) =
268+ (spaceAtEnd . concat) <$>
269+ mapM (\(x,y) -> listItemToRTF alignment indent x y)
270+ (zip (orderedMarkers indent attribs) lst)
271+blockToRTF indent alignment (DefinitionList lst) = (spaceAtEnd . concat) <$>
272+ mapM (definitionListItemToRTF alignment indent) lst
273+blockToRTF indent _ HorizontalRule = return $
252274 rtfPar indent 0 AlignCenter "\\emdash\\emdash\\emdash\\emdash\\emdash"
253−blockToRTF indent alignment (Header level _ lst) = rtfPar indent 0 alignment $
254− "\\b \\fs" ++ (show (40 - (level * 4))) ++ " " ++ inlineListToRTF lst
255−blockToRTF indent alignment (Table caption aligns sizes headers rows) =
256− (if all null headers
257− then ""
258− else tableRowToRTF True indent aligns sizes headers) ++
259− concatMap (tableRowToRTF False indent aligns sizes) rows ++
260− rtfPar indent 0 alignment (inlineListToRTF caption)
275+blockToRTF indent alignment (Header level _ lst) = do
276+ contents <- inlinesToRTF lst
277+ return $ rtfPar indent 0 alignment $
278+ "\\b \\fs" ++ (show (40 - (level * 4))) ++ " " ++ contents
279+blockToRTF indent alignment (Table caption aligns sizes headers rows) = do
280+ caption' <- inlinesToRTF caption
281+ header' <- if all null headers
282+ then return ""
283+ else tableRowToRTF True indent aligns sizes headers
284+ rows' <- concat <$> mapM (tableRowToRTF False indent aligns sizes) rows
285+ return $ header' ++ rows' ++ rtfPar indent 0 alignment caption'
261286
262−tableRowToRTF :: Bool -> Int -> [Alignment] -> [Double] -> [[Block]] -> String
263−tableRowToRTF header indent aligns sizes' cols =
287+tableRowToRTF :: PandocMonad m
288+ => Bool -> Int -> [Alignment] -> [Double] -> [[Block]] -> m String
289+tableRowToRTF header indent aligns sizes' cols = do
264290 let totalTwips = 6 * 1440 -- 6 inches
265− sizes = if all (== 0) sizes'
291+ let sizes = if all (== 0) sizes'
266292 then take (length cols) $ repeat (1.0 / fromIntegral (length cols))
267293 else sizes'
268− columns = concat $ zipWith (tableItemToRTF indent) aligns cols
269− rightEdges = tail $ scanl (\sofar new -> sofar + floor (new * totalTwips))
294+ columns <- concat <$> mapM (\(x,y) -> tableItemToRTF indent x y)
295+ (zip aligns cols)
296+ let rightEdges = tail $ scanl (\sofar new -> sofar + floor (new * totalTwips))
270297 (0 :: Integer) sizes
271− cellDefs = map (\edge -> (if header
298+ let cellDefs = map (\edge -> (if header
272299 then "\\clbrdrb\\brdrs"
273300 else "") ++ "\\cellx" ++ show edge)
274301 rightEdges
275− start = "{\n\\trowd \\trgaph120\n" ++ concat cellDefs ++ "\n" ++
302+ let start = "{\n\\trowd \\trgaph120\n" ++ concat cellDefs ++ "\n" ++
276303 "\\trkeep\\intbl\n{\n"
277− end = "}\n\\intbl\\row}\n"
278− in start ++ columns ++ end
304+ let end = "}\n\\intbl\\row}\n"
305+ return $ start ++ columns ++ end
279306
280−tableItemToRTF :: Int -> Alignment -> [Block] -> String
281−tableItemToRTF indent alignment item =
282− let contents = concatMap (blockToRTF indent alignment) item
283− in "{" ++ substitute "\\pard" "\\pard\\intbl" contents ++ "\\cell}\n"
307+tableItemToRTF :: PandocMonad m => Int -> Alignment -> [Block] -> m String
308+tableItemToRTF indent alignment item = do
309+ contents <- blocksToRTF indent alignment item
310+ return $ "{" ++ substitute "\\pard" "\\pard\\intbl" contents ++ "\\cell}\n"
284311
285312-- | Ensure that there's the same amount of space after compact
286313-- lists as after regular lists.
@@ -291,73 +318,92 @@ spaceAtEnd str =
291318 else str
292319
293320-- | Convert list item (list of blocks) to RTF.
294−listItemToRTF :: Alignment -- ^ alignment
321+listItemToRTF :: PandocMonad m
322+ => Alignment -- ^ alignment
295323 -> Int -- ^ indent level
296324 -> String -- ^ list start marker
297325 -> [Block] -- ^ list item (list of blocks)
298− -> [Char]
299−listItemToRTF alignment indent marker [] =
326+ -> m String
327+listItemToRTF alignment indent marker [] = return $
300328 rtfCompact (indent + listIncrement) (0 - listIncrement) alignment
301329 (marker ++ "\\tx" ++ (show listIncrement) ++ "\\tab ")
302−listItemToRTF alignment indent marker list =
303− let (first:rest) = map (blockToRTF (indent + listIncrement) alignment) list
304− listMarker = "\\fi" ++ show (0 - listIncrement) ++ " " ++ marker ++ "\\tx" ++
305− show listIncrement ++ "\\tab"
306− insertListMarker ('\\':'f':'i':'-':d:xs) | isDigit d =
330+listItemToRTF alignment indent marker list = do
331+ (first:rest) <- mapM (blockToRTF (indent + listIncrement) alignment) list
332+ let listMarker = "\\fi" ++ show (0 - listIncrement) ++ " " ++ marker ++
333+ "\\tx" ++ show listIncrement ++ "\\tab"
334+ let insertListMarker ('\\':'f':'i':'-':d:xs) | isDigit d =
307335 listMarker ++ dropWhile isDigit xs
308336 insertListMarker ('\\':'f':'i':d:xs) | isDigit d =
309337 listMarker ++ dropWhile isDigit xs
310338 insertListMarker (x:xs) =
311339 x : insertListMarker xs
312340 insertListMarker [] = []
313− -- insert the list marker into the (processed) first block
314− in insertListMarker first ++ concat rest
341+ -- insert the list marker into the (processed) first block
342+ return $ insertListMarker first ++ concat rest
315343
316344-- | Convert definition list item (label, list of blocks) to RTF.
317−definitionListItemToRTF :: Alignment -- ^ alignment
345+definitionListItemToRTF :: PandocMonad m
346+ => Alignment -- ^ alignment
318347 -> Int -- ^ indent level
319348 -> ([Inline],[[Block]]) -- ^ list item (list of blocks)
320− -> [Char]
321−definitionListItemToRTF alignment indent (label, defs) =
322− let labelText = blockToRTF indent alignment (Plain label)
323− itemsText = concatMap (blockToRTF (indent + listIncrement) alignment) $
324− concat defs
325− in labelText ++ itemsText
349+ -> m String
350+definitionListItemToRTF alignment indent (label, defs) = do
351+ labelText <- blockToRTF indent alignment (Plain label)
352+ itemsText <- blocksToRTF (indent + listIncrement) alignment (concat defs)
353+ return $ labelText ++ itemsText
326354
327355-- | Convert list of inline items to RTF.
328−inlineListToRTF :: [Inline] -- ^ list of inlines to convert
329− -> String
330−inlineListToRTF lst = concatMap inlineToRTF lst
356+inlinesToRTF :: PandocMonad m
357+ => [Inline] -- ^ list of inlines to convert
358+ -> m String
359+inlinesToRTF lst = concat <$> mapM inlineToRTF lst
331360
332361-- | Convert inline item to RTF.
333−inlineToRTF :: Inline -- ^ inline to convert
334− -> String
335−inlineToRTF (Span _ lst) = inlineListToRTF lst
336−inlineToRTF (Emph lst) = "{\\i " ++ (inlineListToRTF lst) ++ "}"
337−inlineToRTF (Strong lst) = "{\\b " ++ (inlineListToRTF lst) ++ "}"
338−inlineToRTF (Strikeout lst) = "{\\strike " ++ (inlineListToRTF lst) ++ "}"
339−inlineToRTF (Superscript lst) = "{\\super " ++ (inlineListToRTF lst) ++ "}"
340−inlineToRTF (Subscript lst) = "{\\sub " ++ (inlineListToRTF lst) ++ "}"
341−inlineToRTF (SmallCaps lst) = "{\\scaps " ++ (inlineListToRTF lst) ++ "}"
342−inlineToRTF (Quoted SingleQuote lst) =
343− "\\u8216'" ++ (inlineListToRTF lst) ++ "\\u8217'"
344−inlineToRTF (Quoted DoubleQuote lst) =
345− "\\u8220\"" ++ (inlineListToRTF lst) ++ "\\u8221\""
346−inlineToRTF (Code _ str) = "{\\f1 " ++ (codeStringToRTF str) ++ "}"
347−inlineToRTF (Str str) = stringToRTF str
348−inlineToRTF (Math t str) = inlineListToRTF $ texMathToInlines t str
349−inlineToRTF (Cite _ lst) = inlineListToRTF lst
362+inlineToRTF :: PandocMonad m
363+ => Inline -- ^ inline to convert
364+ -> m String
365+inlineToRTF (Span _ lst) = inlinesToRTF lst
366+inlineToRTF (Emph lst) = do
367+ contents <- inlinesToRTF lst
368+ return $ "{\\i " ++ contents ++ "}"
369+inlineToRTF (Strong lst) = do
370+ contents <- inlinesToRTF lst
371+ return $ "{\\b " ++ contents ++ "}"
372+inlineToRTF (Strikeout lst) = do
373+ contents <- inlinesToRTF lst
374+ return $ "{\\strike " ++ contents ++ "}"
375+inlineToRTF (Superscript lst) = do
376+ contents <- inlinesToRTF lst
377+ return $ "{\\super " ++ contents ++ "}"
378+inlineToRTF (Subscript lst) = do
379+ contents <- inlinesToRTF lst
380+ return $ "{\\sub " ++ contents ++ "}"
381+inlineToRTF (SmallCaps lst) = do
382+ contents <- inlinesToRTF lst
383+ return $ "{\\scaps " ++ contents ++ "}"
384+inlineToRTF (Quoted SingleQuote lst) = do
385+ contents <- inlinesToRTF lst
386+ return $ "\\u8216'" ++ contents ++ "\\u8217'"
387+inlineToRTF (Quoted DoubleQuote lst) = do
388+ contents <- inlinesToRTF lst
389+ return $ "\\u8220\"" ++ contents ++ "\\u8221\""
390+inlineToRTF (Code _ str) = return $ "{\\f1 " ++ (codeStringToRTF str) ++ "}"
391+inlineToRTF (Str str) = return $ stringToRTF str
392+inlineToRTF (Math t str) = texMathToInlines t str >>= inlinesToRTF
393+inlineToRTF (Cite _ lst) = inlinesToRTF lst
350394inlineToRTF (RawInline f str)
351− | f == Format "rtf" = str
352− | otherwise = ""
353−inlineToRTF (LineBreak) = "\\line "
354−inlineToRTF SoftBreak = " "
355−inlineToRTF Space = " "
356−inlineToRTF (Link _ text (src, _)) =
357− "{\\field{\\*\\fldinst{HYPERLINK \"" ++ (codeStringToRTF src) ++
358− "\"}}{\\fldrslt{\\ul\n" ++ (inlineListToRTF text) ++ "\n}}}\n"
395+ | f == Format "rtf" = return str
396+ | otherwise = return ""
397+inlineToRTF (LineBreak) = return "\\line "
398+inlineToRTF SoftBreak = return " "
399+inlineToRTF Space = return " "
400+inlineToRTF (Link _ text (src, _)) = do
401+ contents <- inlinesToRTF text
402+ return $ "{\\field{\\*\\fldinst{HYPERLINK \"" ++ (codeStringToRTF src) ++
403+ "\"}}{\\fldrslt{\\ul\n" ++ contents ++ "\n}}}\n"
359404inlineToRTF (Image _ _ (source, _)) =
360− "{\\cf1 [image: " ++ source ++ "]\\cf0}"
361−inlineToRTF (Note contents) =
362− "{\\super\\chftn}{\\*\\footnote\\chftn\\~\\plain\\pard " ++
363− (concatMap (blockToRTF 0 AlignDefault) contents) ++ "}"
405+ return $ "{\\cf1 [image: " ++ source ++ "]\\cf0}"
406+inlineToRTF (Note contents) = do
407+ body <- concat <$> mapM (blockToRTF 0 AlignDefault) contents
408+ return $ "{\\super\\chftn}{\\*\\footnote\\chftn\\~\\plain\\pard " ++
409+ body ++ "}"
src/Text/Pandoc/Writers/TEI.hs+3−2
@@ -41,6 +41,7 @@ import Text.Pandoc.Highlighting ( languages, languagesByExtension )
4141import Text.Pandoc.Pretty
4242import Text.Pandoc.ImageSize
4343import qualified Text.Pandoc.Builder as B
44+import Text.Pandoc.Class ( PandocMonad )
4445
4546-- | Convert list of authors to a docbook <author> section
4647authorToTEI :: WriterOptions -> [Inline] -> B.Inlines
@@ -53,8 +54,8 @@ authorToTEI opts name' =
5354 inTagsSimple "author" (text $ escapeStringForXML name)
5455
5556-- | Convert Pandoc document to string in Docbook format.
56−writeTEI :: WriterOptions -> Pandoc -> String
57−writeTEI opts (Pandoc meta blocks) =
57+writeTEI :: PandocMonad m => WriterOptions -> Pandoc -> m String
58+writeTEI opts (Pandoc meta blocks) = return $
5859 let elements = hierarchicalize blocks
5960 colwidth = if writerWrapText opts == WrapAuto
6061 then Just $ writerColumns opts
src/Text/Pandoc/Writers/Texinfo.hs+67−46
@@ -44,6 +44,9 @@ import Text.Pandoc.ImageSize
4444import Network.URI ( isURI, unEscapeString )
4545import System.FilePath
4646import qualified Data.Set as Set
47+import Control.Monad.Except (throwError)
48+import Text.Pandoc.Error
49+import Text.Pandoc.Class ( PandocMonad)
4750
4851data WriterState =
4952 WriterState { stStrikeout :: Bool -- document contains strikeout
@@ -59,10 +62,12 @@ data WriterState =
5962 - generated .texi files don't work when run through texi2dvi
6063 -}
6164
65+type TI m = StateT WriterState m
66+
6267-- | Convert Pandoc to Texinfo.
63−writeTexinfo :: WriterOptions -> Pandoc -> String
68+writeTexinfo :: PandocMonad m => WriterOptions -> Pandoc -> m String
6469writeTexinfo options document =
65− evalState (pandocToTexinfo options $ wrapTop document) $
70+ evalStateT (pandocToTexinfo options $ wrapTop document) $
6671 WriterState { stStrikeout = False, stSuperscript = False,
6772 stEscapeComma = False, stSubscript = False,
6873 stIdentifiers = Set.empty, stOptions = options}
@@ -72,7 +77,7 @@ wrapTop :: Pandoc -> Pandoc
7277wrapTop (Pandoc meta blocks) =
7378 Pandoc meta (Header 0 nullAttr (docTitle meta) : blocks)
7479
75−pandocToTexinfo :: WriterOptions -> Pandoc -> State WriterState String
80+pandocToTexinfo :: PandocMonad m => WriterOptions -> Pandoc -> TI m String
7681pandocToTexinfo options (Pandoc meta blocks) = do
7782 let titlePage = not $ all null
7883 $ docTitle meta : docDate meta : docAuthors meta
@@ -110,7 +115,7 @@ stringToTexinfo = escapeStringUsing texinfoEscapes
110115 , ('\x2019', "'")
111116 ]
112117
113−escapeCommas :: State WriterState Doc -> State WriterState Doc
118+escapeCommas :: PandocMonad m => TI m Doc -> TI m Doc
114119escapeCommas parser = do
115120 oldEscapeComma <- gets stEscapeComma
116121 modify $ \st -> st{ stEscapeComma = True }
@@ -123,8 +128,9 @@ inCmd :: String -> Doc -> Doc
123128inCmd cmd contents = char '@' <> text cmd <> braces contents
124129
125130-- | Convert Pandoc block element to Texinfo.
126−blockToTexinfo :: Block -- ^ Block to convert
127− -> State WriterState Doc
131+blockToTexinfo :: PandocMonad m
132+ => Block -- ^ Block to convert
133+ -> TI m Doc
128134
129135blockToTexinfo Null = return empty
130136
@@ -214,23 +220,27 @@ blockToTexinfo (Header 0 _ lst) = do
214220 return $ text "@node Top" $$
215221 text "@top " <> txt <> blankline
216222
217−blockToTexinfo (Header level _ lst) = do
218− node <- inlineListForNode lst
219− txt <- inlineListToTexinfo lst
220− idsUsed <- gets stIdentifiers
221− let id' = uniqueIdent lst idsUsed
222− modify $ \st -> st{ stIdentifiers = Set.insert id' idsUsed }
223− return $ if (level > 0) && (level <= 4)
224− then blankline <> text "@node " <> node $$
225− text (seccmd level) <> txt $$
226− text "@anchor" <> braces (text $ '#':id')
227− else txt
228− where
229− seccmd 1 = "@chapter "
230− seccmd 2 = "@section "
231− seccmd 3 = "@subsection "
232− seccmd 4 = "@subsubsection "
233− seccmd _ = error "illegal seccmd level"
223+blockToTexinfo (Header level _ lst)
224+ | level < 1 || level > 4 = blockToTexinfo (Para lst)
225+ | otherwise = do
226+ node <- inlineListForNode lst
227+ txt <- inlineListToTexinfo lst
228+ idsUsed <- gets stIdentifiers
229+ let id' = uniqueIdent lst idsUsed
230+ modify $ \st -> st{ stIdentifiers = Set.insert id' idsUsed }
231+ sec <- seccmd level
232+ return $ if (level > 0) && (level <= 4)
233+ then blankline <> text "@node " <> node $$
234+ text sec <> txt $$
235+ text "@anchor" <> braces (text $ '#':id')
236+ else txt
237+ where
238+ seccmd :: PandocMonad m => Int -> TI m String
239+ seccmd 1 = return "@chapter "
240+ seccmd 2 = return "@section "
241+ seccmd 3 = return "@subsection "
242+ seccmd 4 = return "@subsubsection "
243+ seccmd _ = throwError $ PandocSomeError "illegal seccmd level"
234244
235245blockToTexinfo (Table caption aligns widths heads rows) = do
236246 headers <- if all null heads
@@ -256,28 +266,32 @@ blockToTexinfo (Table caption aligns widths heads rows) = do
256266 inCmd "caption" captionText $$
257267 text "@end float"
258268
259−tableHeadToTexinfo :: [Alignment]
269+tableHeadToTexinfo :: PandocMonad m
270+ => [Alignment]
260271 -> [[Block]]
261− -> State WriterState Doc
272+ -> TI m Doc
262273tableHeadToTexinfo = tableAnyRowToTexinfo "@headitem "
263274
264−tableRowToTexinfo :: [Alignment]
275+tableRowToTexinfo :: PandocMonad m
276+ => [Alignment]
265277 -> [[Block]]
266− -> State WriterState Doc
278+ -> TI m Doc
267279tableRowToTexinfo = tableAnyRowToTexinfo "@item "
268280
269−tableAnyRowToTexinfo :: String
281+tableAnyRowToTexinfo :: PandocMonad m
282+ => String
270283 -> [Alignment]
271284 -> [[Block]]
272− -> State WriterState Doc
285+ -> TI m Doc
273286tableAnyRowToTexinfo itemtype aligns cols =
274287 zipWithM alignedBlock aligns cols >>=
275288 return . (text itemtype $$) . foldl (\row item -> row $$
276289 (if isEmpty row then empty else text " @tab ") <> item) empty
277290
278−alignedBlock :: Alignment
291+alignedBlock :: PandocMonad m
292+ => Alignment
279293 -> [Block]
280− -> State WriterState Doc
294+ -> TI m Doc
281295-- XXX @flushleft and @flushright text won't get word wrapped. Since word
282296-- wrapping is more important than alignment, we ignore the alignment.
283297alignedBlock _ = blockListToTexinfo
@@ -292,8 +306,9 @@ alignedBlock _ col = blockListToTexinfo col
292306-}
293307
294308-- | Convert Pandoc block elements to Texinfo.
295−blockListToTexinfo :: [Block]
296− -> State WriterState Doc
309+blockListToTexinfo :: PandocMonad m
310+ => [Block]
311+ -> TI m Doc
297312blockListToTexinfo [] = return empty
298313blockListToTexinfo (x:xs) = do
299314 x' <- blockToTexinfo x
@@ -335,15 +350,17 @@ collectNodes level (x:xs) =
335350 _ ->
336351 collectNodes level xs
337352
338−makeMenuLine :: Block
339− -> State WriterState Doc
353+makeMenuLine :: PandocMonad m
354+ => Block
355+ -> TI m Doc
340356makeMenuLine (Header _ _ lst) = do
341357 txt <- inlineListForNode lst
342358 return $ text "* " <> txt <> text "::"
343−makeMenuLine _ = error "makeMenuLine called with non-Header block"
359+makeMenuLine _ = throwError $ PandocSomeError "makeMenuLine called with non-Header block"
344360
345−listItemToTexinfo :: [Block]
346− -> State WriterState Doc
361+listItemToTexinfo :: PandocMonad m
362+ => [Block]
363+ -> TI m Doc
347364listItemToTexinfo lst = do
348365 contents <- blockListToTexinfo lst
349366 let spacer = case reverse lst of
@@ -351,8 +368,9 @@ listItemToTexinfo lst = do
351368 _ -> empty
352369 return $ text "@item" $$ contents <> spacer
353370
354−defListItemToTexinfo :: ([Inline], [[Block]])
355− -> State WriterState Doc
371+defListItemToTexinfo :: PandocMonad m
372+ => ([Inline], [[Block]])
373+ -> TI m Doc
356374defListItemToTexinfo (term, defs) = do
357375 term' <- inlineListToTexinfo term
358376 let defToTexinfo bs = do d <- blockListToTexinfo bs
@@ -363,13 +381,15 @@ defListItemToTexinfo (term, defs) = do
363381 return $ text "@item " <> term' $+$ vcat defs'
364382
365383-- | Convert list of inline elements to Texinfo.
366−inlineListToTexinfo :: [Inline] -- ^ Inlines to convert
367− -> State WriterState Doc
384+inlineListToTexinfo :: PandocMonad m
385+ => [Inline] -- ^ Inlines to convert
386+ -> TI m Doc
368387inlineListToTexinfo lst = mapM inlineToTexinfo lst >>= return . hcat
369388
370389-- | Convert list of inline elements to Texinfo acceptable for a node name.
371−inlineListForNode :: [Inline] -- ^ Inlines to convert
372− -> State WriterState Doc
390+inlineListForNode :: PandocMonad m
391+ => [Inline] -- ^ Inlines to convert
392+ -> TI m Doc
373393inlineListForNode = return . text . stringToTexinfo .
374394 filter (not . disallowedInNode) . stringify
375395
@@ -378,8 +398,9 @@ disallowedInNode :: Char -> Bool
378398disallowedInNode c = c `elem` (".,:()" :: String)
379399
380400-- | Convert inline element to Texinfo
381−inlineToTexinfo :: Inline -- ^ Inline to convert
382− -> State WriterState Doc
401+inlineToTexinfo :: PandocMonad m
402+ => Inline -- ^ Inline to convert
403+ -> TI m Doc
383404
384405inlineToTexinfo (Span _ lst) =
385406 inlineListToTexinfo lst
src/Text/Pandoc/Writers/Textile.hs+4−3
@@ -41,6 +41,7 @@ import Text.Pandoc.XML ( escapeStringForXML )
4141import Data.List ( intercalate )
4242import Control.Monad.State
4343import Data.Char ( isSpace )
44+import Text.Pandoc.Class ( PandocMonad )
4445
4546data WriterState = WriterState {
4647 stNotes :: [String] -- Footnotes
@@ -50,8 +51,8 @@ data WriterState = WriterState {
5051 }
5152
5253-- | Convert Pandoc to Textile.
53−writeTextile :: WriterOptions -> Pandoc -> String
54−writeTextile opts document =
54+writeTextile :: PandocMonad m => WriterOptions -> Pandoc -> m String
55+writeTextile opts document = return $
5556 evalState (pandocToTextile opts document)
5657 WriterState { stNotes = [], stListLevel = [], stStartNum = Nothing,
5758 stUseTags = False }
@@ -435,7 +436,7 @@ inlineToTextile opts (RawInline f str)
435436 isEnabled Ext_raw_tex opts = return str
436437 | otherwise = return ""
437438
438−inlineToTextile _ (LineBreak) = return "\n"
439+inlineToTextile _ LineBreak = return "\n"
439440
440441inlineToTextile _ SoftBreak = return " "
441442
src/Text/Pandoc/Writers/ZimWiki.hs+4−3
@@ -45,6 +45,7 @@ import Network.URI ( isURI )
4545import Control.Monad ( zipWithM )
4646import Control.Monad.State ( modify, State, get, evalState )
4747--import Control.Monad.Reader ( ReaderT, runReaderT, ask, local )
48+import Text.Pandoc.Class ( PandocMonad )
4849
4950data WriterState = WriterState {
5051 stItemNum :: Int,
@@ -55,8 +56,8 @@ instance Default WriterState where
5556 def = WriterState { stItemNum = 1, stIndent = "" }
5657
5758-- | Convert Pandoc to ZimWiki.
58−writeZimWiki :: WriterOptions -> Pandoc -> String
59−writeZimWiki opts document = evalState (pandocToZimWiki opts document) (WriterState 1 "")
59+writeZimWiki :: PandocMonad m => WriterOptions -> Pandoc -> m String
60+writeZimWiki opts document = return $ evalState (pandocToZimWiki opts document) (WriterState 1 "")
6061
6162-- | Return ZimWiki representation of document.
6263pandocToZimWiki :: WriterOptions -> Pandoc -> State WriterState String
@@ -317,7 +318,7 @@ inlineToZimWiki opts (RawInline f str)
317318 | f == Format "html" = do cont <- indentFromHTML opts str; return cont
318319 | otherwise = return ""
319320
320−inlineToZimWiki _ (LineBreak) = return "\n" -- was \\\\
321+inlineToZimWiki _ LineBreak = return "\n" -- was \\\\
321322
322323inlineToZimWiki opts SoftBreak =
323324 case writerWrapText opts of
stack.yaml+1−1
@@ -8,8 +8,8 @@ flags:
88packages:
99- '.'
1010extra-deps:
11−- texmath-0.9
1211- doctemplates-0.1.0.2
1312- pandoc-types-1.17.0.4
1413- skylighting-0.1.1.2
14+- texmath-0.9
1515resolver: lts-7.14
tests/Tests/Helpers.hs+12−7
@@ -3,6 +3,7 @@
33
44module Tests.Helpers ( test
55 , (=?>)
6+ , purely
67 , property
78 , ToString(..)
89 , ToPandoc(..)
@@ -11,11 +12,12 @@ module Tests.Helpers ( test
1112
1213import Text.Pandoc.Definition
1314import Text.Pandoc.Builder (Inlines, Blocks, doc, plain)
15+import Text.Pandoc.Class
1416import Test.Framework
1517import Test.Framework.Providers.HUnit
1618import Test.Framework.Providers.QuickCheck2
1719import Test.HUnit (assertBool)
18−import Text.Pandoc.Shared (normalize, trimr)
20+import Text.Pandoc.Shared (trimr)
1921import Text.Pandoc.Options
2022import Text.Pandoc.Writers.Native (writeNative)
2123import qualified Test.QuickCheck.Property as QP
@@ -49,6 +51,9 @@ vividize (Second s) = "+ " ++ s
4951property :: QP.Testable a => TestName -> a -> Test
5052property = testProperty
5153
54+purely :: (b -> PandocPure a) -> b -> a
55+purely f = either (error . show) id . runPure . f
56+
5257infix 5 =?>
5358(=?>) :: a -> b -> (a,b)
5459x =?> y = (x, y)
@@ -57,17 +62,17 @@ class ToString a where
5762 toString :: a -> String
5863
5964instance ToString Pandoc where
60− toString d = writeNative def{ writerTemplate = s } $ toPandoc d
65+ toString d = purely (writeNative def{ writerTemplate = s }) $ toPandoc d
6166 where s = case d of
6267 (Pandoc (Meta m) _)
6368 | M.null m -> Nothing
6469 | otherwise -> Just "" -- need this to get meta output
6570
6671instance ToString Blocks where
67− toString = writeNative def . toPandoc
72+ toString = purely (writeNative def) . toPandoc
6873
6974instance ToString Inlines where
70− toString = trimr . writeNative def . toPandoc
75+ toString = trimr . purely (writeNative def) . toPandoc
7176
7277instance ToString String where
7378 toString = id
@@ -76,10 +81,10 @@ class ToPandoc a where
7681 toPandoc :: a -> Pandoc
7782
7883instance ToPandoc Pandoc where
79− toPandoc = normalize
84+ toPandoc = id
8085
8186instance ToPandoc Blocks where
82− toPandoc = normalize . doc
87+ toPandoc = doc
8388
8489instance ToPandoc Inlines where
85− toPandoc = normalize . doc . plain
90+ toPandoc = doc . plain
tests/Tests/Old.hs+12−17
@@ -11,15 +11,10 @@ import System.FilePath ( (</>), (<.>), takeDirectory, splitDirectories,
1111import System.Directory
1212import System.Exit
1313import Data.Algorithm.Diff
14−import Text.Pandoc.Shared ( normalize )
15−import Text.Pandoc.Options
16−import Text.Pandoc.Writers.Native ( writeNative )
17−import Text.Pandoc.Readers.Native ( readNative )
1814import Prelude hiding ( readFile )
1915import qualified Data.ByteString.Lazy as B
2016import Text.Pandoc.UTF8 (toStringLazy)
2117import Text.Printf
22−import Text.Pandoc.Error
2318
2419readFileUTF8 :: FilePath -> IO String
2520readFileUTF8 f = B.readFile f >>= return . toStringLazy
@@ -52,13 +47,13 @@ tests = [ testGroup "markdown"
5247 [ testGroup "writer"
5348 $ writerTests "markdown" ++ lhsWriterTests "markdown"
5449 , testGroup "reader"
55− [ test "basic" ["-r", "markdown", "-w", "native", "-s", "-S"]
50+ [ test "basic" ["-r", "markdown", "-w", "native", "-s"]
5651 "testsuite.txt" "testsuite.native"
5752 , test "tables" ["-r", "markdown", "-w", "native", "--columns=80"]
5853 "tables.txt" "tables.native"
5954 , test "pipe tables" ["-r", "markdown", "-w", "native", "--columns=80"]
6055 "pipe-tables.txt" "pipe-tables.native"
61− , test "more" ["-r", "markdown", "-w", "native", "-s", "-S"]
56+ , test "more" ["-r", "markdown", "-w", "native", "-s"]
6257 "markdown-reader-more.txt" "markdown-reader-more.native"
6358 , lhsReaderTest "markdown+lhs"
6459 ]
@@ -70,8 +65,8 @@ tests = [ testGroup "markdown"
7065 , testGroup "rst"
7166 [ testGroup "writer" (writerTests "rst" ++ lhsWriterTests "rst")
7267 , testGroup "reader"
73− [ test "basic" ["-r", "rst", "-w", "native",
74− "-s", "-S", "--columns=80"] "rst-reader.rst" "rst-reader.native"
68+ [ test "basic" ["-r", "rst+smart", "-w", "native",
69+ "-s", "--columns=80"] "rst-reader.rst" "rst-reader.native"
7570 , test "tables" ["-r", "rst", "-w", "native", "--columns=80"]
7671 "tables.rst" "tables-rstsubset.native"
7772 , lhsReaderTest "rst+lhs"
@@ -86,16 +81,17 @@ tests = [ testGroup "markdown"
8681 ]
8782 ]
8883 , testGroup "html"
89− [ testGroup "writer" (writerTests "html" ++ lhsWriterTests "html")
84+ [ testGroup "writer" (writerTests "html4" ++ writerTests "html5" ++
85+ lhsWriterTests "html")
9086 , test "reader" ["-r", "html", "-w", "native", "-s"]
9187 "html-reader.html" "html-reader.native"
9288 ]
9389 , testGroup "s5"
9490 [ s5WriterTest "basic" ["-s"] "s5"
9591 , s5WriterTest "fancy" ["-s","-m","-i"] "s5"
96− , s5WriterTest "fragment" [] "html"
92+ , s5WriterTest "fragment" [] "html4"
9793 , s5WriterTest "inserts" ["-s", "-H", "insert",
98− "-B", "insert", "-A", "insert", "-c", "main.css"] "html"
94+ "-B", "insert", "-A", "insert", "-c", "main.css"] "html4"
9995 ]
10096 , testGroup "textile"
10197 [ testGroup "writer" $ writerTests "textile"
@@ -103,7 +99,7 @@ tests = [ testGroup "markdown"
10399 "textile-reader.textile" "textile-reader.native"
104100 ]
105101 , testGroup "docbook"
106− [ testGroup "writer" $ writerTests "docbook"
102+ [ testGroup "writer" $ writerTests "docbook4"
107103 , test "reader" ["-r", "docbook", "-w", "native", "-s"]
108104 "docbook-reader.docbook" "docbook-reader.native"
109105 , test "reader" ["-r", "docbook", "-w", "native", "-s"]
@@ -193,10 +189,9 @@ lhsWriterTests format
193189
194190lhsReaderTest :: String -> Test
195191lhsReaderTest format =
196− testWithNormalize normalizer "lhs" ["-r", format, "-w", "native"]
192+ test "lhs" ["-r", format, "-w", "native"]
197193 ("lhs-test" <.> format) norm
198− where normalizer = writeNative def . normalize . handleError . readNative
199− norm = if format == "markdown+lhs"
194+ where norm = if format == "markdown+lhs"
200195 then "lhs-test-markdown.native"
201196 else "lhs-test.native"
202197
@@ -259,7 +254,7 @@ testWithNormalize normalizer testname opts inp norm = testCase testname $ do
259254 (outputPath, hOut) <- openTempFile "" "pandoc-test"
260255 let inpPath = inp
261256 let normPath = norm
262− let options = ["--data-dir", ".." </> "data"] ++ [inpPath] ++ opts
257+ let options = ["--quiet", "--data-dir", ".." </> "data"] ++ [inpPath] ++ opts
263258 let cmd = pandocPath ++ " " ++ unwords options
264259 let findDynlibDir [] = Nothing
265260 findDynlibDir ("build":xs) = Just $ joinPath (reverse xs) </> "build"
tests/Tests/Readers/Docx.hs+15−12
@@ -1,19 +1,17 @@
11module Tests.Readers.Docx (tests) where
22
3−import Text.Pandoc.Options
4−import Text.Pandoc.Readers.Native
5−import Text.Pandoc.Definition
3+import Text.Pandoc
64import Tests.Helpers
75import Test.Framework
86import Test.HUnit (assertBool)
97import Test.Framework.Providers.HUnit
108import qualified Data.ByteString.Lazy as B
11−import Text.Pandoc.Readers.Docx
129import Text.Pandoc.Writers.Native (writeNative)
1310import qualified Data.Map as M
1411import Text.Pandoc.MediaBag (MediaBag, lookupMedia, mediaDirectory)
1512import Codec.Archive.Zip
16−import Text.Pandoc.Error
13+import Text.Pandoc.Class (runIOorExplode)
14+import qualified Text.Pandoc.Class as P
1715
1816-- We define a wrapper around pandoc that doesn't normalize in the
1917-- tests. Since we do our own normalization, we want to make sure
@@ -25,8 +23,11 @@ data NoNormPandoc = NoNormPandoc {unNoNorm :: Pandoc}
2523noNorm :: Pandoc -> NoNormPandoc
2624noNorm = NoNormPandoc
2725
26+defopts :: ReaderOptions
27+defopts = def{ readerExtensions = getDefaultExtensions "docx" }
28+
2829instance ToString NoNormPandoc where
29− toString d = writeNative def{ writerTemplate = s } $ toPandoc d
30+ toString d = purely (writeNative def{ writerTemplate = s }) $ toPandoc d
3031 where s = case d of
3132 NoNormPandoc (Pandoc (Meta m) _)
3233 | M.null m -> Nothing
@@ -42,8 +43,9 @@ compareOutput :: ReaderOptions
4243compareOutput opts docxFile nativeFile = do
4344 df <- B.readFile docxFile
4445 nf <- Prelude.readFile nativeFile
45− let (p, _) = handleError $ readDocx opts df
46− return $ (noNorm p, noNorm (handleError $ readNative nf))
46+ p <- runIOorExplode $ readDocx opts df
47+ df' <- runIOorExplode $ readNative def nf
48+ return $ (noNorm p, noNorm df')
4749
4850testCompareWithOptsIO :: ReaderOptions -> String -> FilePath -> FilePath -> IO Test
4951testCompareWithOptsIO opts name docxFile nativeFile = do
@@ -55,12 +57,13 @@ testCompareWithOpts opts name docxFile nativeFile =
5557 buildTest $ testCompareWithOptsIO opts name docxFile nativeFile
5658
5759testCompare :: String -> FilePath -> FilePath -> Test
58−testCompare = testCompareWithOpts def
60+testCompare = testCompareWithOpts defopts
5961
6062testForWarningsWithOptsIO :: ReaderOptions -> String -> FilePath -> [String] -> IO Test
6163testForWarningsWithOptsIO opts name docxFile expected = do
6264 df <- B.readFile docxFile
63− let (_, _, warns) = handleError $ readDocxWithWarnings opts df
65+ logs <- runIOorExplode (readDocx opts df >> P.getLog)
66+ let warns = [s | (WARNING, s) <- logs]
6467 return $ test id name (unlines warns, unlines expected)
6568
6669testForWarningsWithOpts :: ReaderOptions -> String -> FilePath -> [String] -> Test
@@ -68,7 +71,7 @@ testForWarningsWithOpts opts name docxFile expected =
6871 buildTest $ testForWarningsWithOptsIO opts name docxFile expected
6972
7073-- testForWarnings :: String -> FilePath -> [String] -> Test
71−-- testForWarnings = testForWarningsWithOpts def
74+-- testForWarnings = testForWarningsWithOpts defopts
7275
7376getMedia :: FilePath -> FilePath -> IO (Maybe B.ByteString)
7477getMedia archivePath mediaPath = do
@@ -93,7 +96,7 @@ compareMediaPathIO mediaPath mediaBag docxPath = do
9396compareMediaBagIO :: FilePath -> IO Bool
9497compareMediaBagIO docxFile = do
9598 df <- B.readFile docxFile
96− let (_, mb) = handleError $ readDocx def df
99+ mb <- runIOorExplode (readDocx defopts df >> P.getMediaBag)
97100 bools <- mapM
98101 (\(fp, _, _) -> compareMediaPathIO fp mb docxFile)
99102 (mediaDirectory mb)
tests/Tests/Readers/EPUB.hs+4−2
@@ -7,10 +7,12 @@ import Test.Framework.Providers.HUnit
77import qualified Data.ByteString.Lazy as BL
88import Text.Pandoc.Readers.EPUB
99import Text.Pandoc.MediaBag (MediaBag, mediaDirectory)
10−import Text.Pandoc.Error
10+import qualified Text.Pandoc.Class as P
1111
1212getMediaBag :: FilePath -> IO MediaBag
13−getMediaBag fp = snd . handleError . readEPUB def <$> BL.readFile fp
13+getMediaBag fp = do
14+ bs <- BL.readFile fp
15+ snd <$> (P.runIOorExplode $ P.withMediaBag $ readEPUB def bs)
1416
1517testMediaBag :: FilePath -> [(String, String, Int)] -> IO ()
1618testMediaBag fp bag = do
tests/Tests/Readers/HTML.hs+1−1
@@ -9,7 +9,7 @@ import Text.Pandoc.Builder
99import Text.Pandoc
1010
1111html :: String -> Pandoc
12−html = handleError . readHtml def
12+html = purely $ readHtml def
1313
1414tests :: [Test]
1515tests = [ testGroup "base tag"
tests/Tests/Readers/LaTeX.hs+2−1
@@ -9,7 +9,8 @@ import Text.Pandoc.Builder
99import Text.Pandoc
1010
1111latex :: String -> Pandoc
12−latex = handleError . readLaTeX def
12+latex = purely $ readLaTeX def{
13+ readerExtensions = getDefaultExtensions "latex" }
1314
1415infix 4 =:
1516(=:) :: ToString c
tests/Tests/Readers/Markdown.hs+12−10
@@ -6,21 +6,23 @@ import Test.Framework
66import Tests.Helpers
77import Text.Pandoc.Arbitrary()
88import Text.Pandoc.Builder
9−import qualified Data.Set as Set
109import Text.Pandoc
1110
1211markdown :: String -> Pandoc
13−markdown = handleError . readMarkdown def
12+markdown = purely $ readMarkdown def { readerExtensions =
13+ disableExtension Ext_smart pandocExtensions }
1414
1515markdownSmart :: String -> Pandoc
16−markdownSmart = handleError . readMarkdown def { readerSmart = True }
16+markdownSmart = purely $ readMarkdown def { readerExtensions =
17+ enableExtension Ext_smart pandocExtensions }
1718
1819markdownCDL :: String -> Pandoc
19−markdownCDL = handleError . readMarkdown def { readerExtensions = Set.insert
20− Ext_compact_definition_lists $ readerExtensions def }
20+markdownCDL = purely $ readMarkdown def { readerExtensions = enableExtension
21+ Ext_compact_definition_lists pandocExtensions }
2122
2223markdownGH :: String -> Pandoc
23−markdownGH = handleError . readMarkdown def { readerExtensions = githubMarkdownExtensions }
24+markdownGH = purely $ readMarkdown def {
25+ readerExtensions = githubMarkdownExtensions }
2426
2527infix 4 =:
2628(=:) :: ToString c
@@ -29,8 +31,8 @@ infix 4 =:
2931
3032testBareLink :: (String, Inlines) -> Test
3133testBareLink (inp, ils) =
32− test (handleError . readMarkdown def{ readerExtensions =
33− Set.fromList [Ext_autolink_bare_uris, Ext_raw_html] })
34+ test (purely $ readMarkdown def{ readerExtensions =
35+ extensionsFromList [Ext_autolink_bare_uris, Ext_raw_html] })
3436 inp (inp, doc $ para ils)
3537
3638autolink :: String -> Inlines
@@ -303,8 +305,8 @@ tests = [ testGroup "inline code"
303305 =?> para (note (para "See [^1]"))
304306 ]
305307 , testGroup "lhs"
306− [ test (handleError . readMarkdown def{ readerExtensions = Set.insert
307− Ext_literate_haskell $ readerExtensions def })
308+ [ test (purely $ readMarkdown def{ readerExtensions = enableExtension
309+ Ext_literate_haskell pandocExtensions })
308310 "inverse bird tracks and html" $
309311 "> a\n\n< b\n\n<div>\n"
310312 =?> codeBlockWith ("",["sourceCode","literate","haskell"],[]) "a"
tests/Tests/Readers/Odt.hs+13−12
@@ -1,17 +1,16 @@
11module Tests.Readers.Odt (tests) where
22
33import Control.Monad ( liftM )
4−import Text.Pandoc.Options
5−import Text.Pandoc.Readers.Native
6−import Text.Pandoc.Readers.Markdown
7−import Text.Pandoc.Definition
4+import Text.Pandoc
5+import Text.Pandoc.Class (runIO)
86import Tests.Helpers
97import Test.Framework
108import qualified Data.ByteString.Lazy as B
11−import Text.Pandoc.Readers.Odt
129import Text.Pandoc.Writers.Native (writeNative)
1310import qualified Data.Map as M
14−import Text.Pandoc.Error
11+
12+defopts :: ReaderOptions
13+defopts = def{ readerExtensions = getDefaultExtensions "odt" }
1514
1615tests :: [Test]
1716tests = testsComparingToMarkdown ++ testsComparingToNative
@@ -41,7 +40,7 @@ newtype NoNormPandoc = NoNormPandoc {unNoNorm :: Pandoc}
4140 deriving ( Show )
4241
4342instance ToString NoNormPandoc where
44− toString d = writeNative def{ writerTemplate = s } $ toPandoc d
43+ toString d = purely (writeNative def{ writerTemplate = s }) $ toPandoc d
4544 where s = case d of
4645 NoNormPandoc (Pandoc (Meta m) _)
4746 | M.null m -> Nothing
@@ -62,16 +61,18 @@ compareOdtToNative :: TestCreator
6261compareOdtToNative opts odtPath nativePath = do
6362 nativeFile <- Prelude.readFile nativePath
6463 odtFile <- B.readFile odtPath
65− let native = getNoNormVia id "native" $ readNative nativeFile
66− let odt = getNoNormVia fst "odt" $ readOdt opts odtFile
64+ native <- getNoNormVia id "native" <$> runIO (readNative def nativeFile)
65+ odt <- getNoNormVia id "odt" <$> runIO (readOdt opts odtFile)
6766 return (odt,native)
6867
6968compareOdtToMarkdown :: TestCreator
7069compareOdtToMarkdown opts odtPath markdownPath = do
7170 markdownFile <- Prelude.readFile markdownPath
7271 odtFile <- B.readFile odtPath
73− let markdown = getNoNormVia id "markdown" $ readMarkdown opts markdownFile
74− let odt = getNoNormVia fst "odt" $ readOdt opts odtFile
72+ markdown <- getNoNormVia id "markdown" <$>
73+ runIO (readMarkdown def{ readerExtensions = pandocExtensions }
74+ markdownFile)
75+ odt <- getNoNormVia id "odt" <$> runIO (readOdt opts odtFile)
7576 return (odt,markdown)
7677
7778
@@ -80,7 +81,7 @@ createTest :: TestCreator
8081 -> FilePath -> FilePath
8182 -> Test
8283createTest creator name path1 path2 =
83− buildTest $ liftM (test id name) (creator def path1 path2)
84+ buildTest $ liftM (test id name) (creator defopts path1 path2)
8485
8586{-
8687--
tests/Tests/Readers/Org.hs+5−4
@@ -9,10 +9,11 @@ import Text.Pandoc
99import Data.List (intersperse)
1010
1111org :: String -> Pandoc
12−org = handleError . readOrg def
13−
12+org = purely $ readOrg def{ readerExtensions = getDefaultExtensions "org" }
13+
1414orgSmart :: String -> Pandoc
15−orgSmart = handleError . readOrg def { readerSmart = True }
15+orgSmart = purely $ readOrg def { readerExtensions =
16+ enableExtension Ext_smart $ getDefaultExtensions "org" }
1617
1718infix 4 =:
1819(=:) :: ToString c
@@ -1525,7 +1526,7 @@ tests =
15251526 , ""
15261527 , "#+RESULTS:"
15271528 , ": 65" ] =?>
1528− rawBlock "html" ""
1529+ (mempty :: Blocks)
15291530
15301531 , "Source block with toggling header arguments" =:
15311532 unlines [ "#+BEGIN_SRC sh :noeval"
tests/Tests/Readers/RST.hs+1−1
@@ -9,7 +9,7 @@ import Text.Pandoc.Builder
99import Text.Pandoc
1010
1111rst :: String -> Pandoc
12−rst = handleError . readRST def{ readerStandalone = True }
12+rst = purely $ readRST def{ readerStandalone = True }
1313
1414infix 4 =:
1515(=:) :: ToString c
tests/Tests/Readers/Txt2Tags.hs+11−4
@@ -8,10 +8,17 @@ import Text.Pandoc.Arbitrary()
88import Text.Pandoc.Builder
99import Text.Pandoc
1010import Data.List (intersperse)
11−import Text.Pandoc.Readers.Txt2Tags
11+import Text.Pandoc.Class
12+
1213
1314t2t :: String -> Pandoc
14−t2t = handleError . readTxt2Tags (T2TMeta "date" "mtime" "in" "out") def
15+-- t2t = handleError . readTxt2Tags (T2TMeta "date" "mtime" "in" "out") def
16+t2t = purely $ \s -> do
17+ putCommonState
18+ def { stInputFiles = Just ["in"]
19+ , stOutputFile = Just "out"
20+ }
21+ readTxt2Tags def s
1522
1623infix 4 =:
1724(=:) :: ToString c
@@ -78,10 +85,10 @@ tests =
7885
7986 , "Macros: Date" =:
8087 "%%date" =?>
81− para "date"
88+ para "1970-01-01"
8289 , "Macros: Mod Time" =:
8390 "%%mtime" =?>
84− para "mtime"
91+ para (str "")
8592 , "Macros: Infile" =:
8693 "%%infile" =?>
8794 para "in"
tests/Tests/Shared.hs+4−24
@@ -1,9 +1,7 @@
11module Tests.Shared (tests) where
22
3−import Text.Pandoc.Definition
43import Text.Pandoc.Shared
54import Test.Framework
6−import Tests.Helpers
75import Text.Pandoc.Arbitrary()
86import Test.Framework.Providers.HUnit
97import Test.HUnit ( assertBool, (@?=) )
@@ -11,33 +9,15 @@ import Text.Pandoc.Builder
119import System.FilePath.Posix (joinPath)
1210
1311tests :: [Test]
14−tests = [ testGroup "normalize"
15− [ property "p_normalize_blocks_rt" p_normalize_blocks_rt
16− , property "p_normalize_inlines_rt" p_normalize_inlines_rt
17− , property "p_normalize_no_trailing_spaces"
18− p_normalize_no_trailing_spaces
19− ]
20− , testGroup "compactify'DL"
21− [ testCase "compactify'DL with empty def" $
22− assertBool "compactify'DL"
12+tests = [ testGroup "compactifyDL"
13+ [ testCase "compactifyDL with empty def" $
14+ assertBool "compactifyDL"
2315 (let x = [(str "word", [para (str "def"), mempty])]
24− in compactify'DL x == x)
16+ in compactifyDL x == x)
2517 ]
2618 , testGroup "collapseFilePath" testCollapse
2719 ]
2820
29−p_normalize_blocks_rt :: [Block] -> Bool
30−p_normalize_blocks_rt bs =
31− normalizeBlocks bs == normalizeBlocks (normalizeBlocks bs)
32−
33−p_normalize_inlines_rt :: [Inline] -> Bool
34−p_normalize_inlines_rt ils =
35− normalizeInlines ils == normalizeInlines (normalizeInlines ils)
36−
37−p_normalize_no_trailing_spaces :: [Inline] -> Bool
38−p_normalize_no_trailing_spaces ils = null ils' || last ils' /= Space
39− where ils' = normalizeInlines $ ils ++ [Space]
40−
4121testCollapse :: [Test]
4222testCollapse = map (testCase "collapse")
4323 [ (collapseFilePath (joinPath [ ""]) @?= (joinPath [ ""]))
tests/Tests/Walk.hsdeleted−46
@@ -1,46 +0,0 @@
1−{-# LANGUAGE ScopedTypeVariables, FlexibleContexts #-}
2−module Tests.Walk (tests) where
3−
4−import Text.Pandoc.Definition
5−import Text.Pandoc.Walk
6−import Test.Framework
7−import Tests.Helpers
8−import Data.Char (toUpper)
9−import Text.Pandoc.Arbitrary()
10−import Data.Generics
11−
12−tests :: [Test]
13−tests = [ testGroup "Walk"
14− [ property "p_walk inlineTrans" (p_walk inlineTrans)
15− , property "p_walk blockTrans" (p_walk blockTrans)
16− , property "p_query inlineQuery" (p_query inlineQuery)
17− , property "p_query blockQuery" (p_query blockQuery)
18− ]
19− ]
20−
21−p_walk :: (Typeable a, Walkable a Pandoc)
22− => (a -> a) -> Pandoc -> Bool
23−p_walk f d = everywhere (mkT f) d == walk f d
24−
25−p_query :: (Eq a, Typeable a1, Monoid a, Walkable a1 Pandoc)
26− => (a1 -> a) -> Pandoc -> Bool
27−p_query f d = everything mappend (mempty `mkQ` f) d == query f d
28−
29−inlineTrans :: Inline -> Inline
30−inlineTrans (Str xs) = Str $ map toUpper xs
31−inlineTrans (Emph xs) = Strong xs
32−inlineTrans x = x
33−
34−blockTrans :: Block -> Block
35−blockTrans (Plain xs) = Para xs
36−blockTrans (BlockQuote xs) = Div ("",["special"],[]) xs
37−blockTrans x = x
38−
39−inlineQuery :: Inline -> String
40−inlineQuery (Str xs) = xs
41−inlineQuery _ = ""
42−
43−blockQuery :: Block -> [Int]
44−blockQuery (Header lev _ _) = [lev]
45−blockQuery _ = []
46−
tests/Tests/Writers/AsciiDoc.hs+1−1
@@ -7,7 +7,7 @@ import Tests.Helpers
77import Text.Pandoc.Arbitrary()
88
99asciidoc :: (ToPandoc a) => a -> String
10−asciidoc = writeAsciiDoc def{ writerWrapText = WrapNone } . toPandoc
10+asciidoc = purely (writeAsciiDoc def{ writerWrapText = WrapNone }) . toPandoc
1111
1212tests :: [Test]
1313tests = [ testGroup "emphasis"
tests/Tests/Writers/ConTeXt.hs+2−2
@@ -8,10 +8,10 @@ import Tests.Helpers
88import Text.Pandoc.Arbitrary()
99
1010context :: (ToPandoc a) => a -> String
11−context = writeConTeXt def . toPandoc
11+context = purely (writeConTeXt def) . toPandoc
1212
1313context' :: (ToPandoc a) => a -> String
14−context' = writeConTeXt def{ writerWrapText = WrapNone } . toPandoc
14+context' = purely (writeConTeXt def{ writerWrapText = WrapNone }) . toPandoc
1515
1616{-
1717 "my test" =: X =?> Y
tests/Tests/Writers/Docbook.hs+1−1
@@ -11,7 +11,7 @@ docbook :: (ToPandoc a) => a -> String
1111docbook = docbookWithOpts def{ writerWrapText = WrapNone }
1212
1313docbookWithOpts :: ToPandoc a => WriterOptions -> a -> String
14−docbookWithOpts opts = writeDocbook opts . toPandoc
14+docbookWithOpts opts = purely (writeDocbook4 opts) . toPandoc
1515
1616{-
1717 "my test" =: X =?> Y
tests/Tests/Writers/Docx.hs+8−6
@@ -7,8 +7,8 @@ import Tests.Helpers
77import Test.Framework
88import Text.Pandoc.Readers.Docx
99import Text.Pandoc.Writers.Docx
10−import Text.Pandoc.Error
1110import System.FilePath ((</>))
11+import Text.Pandoc.Class (runIOorExplode)
1212
1313type Options = (WriterOptions, ReaderOptions)
1414
@@ -20,10 +20,12 @@ compareOutput opts nativeFileIn nativeFileOut = do
2020 nf <- Prelude.readFile nativeFileIn
2121 nf' <- Prelude.readFile nativeFileOut
2222 let wopts = fst opts
23− df <- writeDocx wopts{writerUserDataDir = Just (".." </> "data")}
24− (handleError $ readNative nf)
25− let (p, _) = handleError $ readDocx (snd opts) df
26− return (p, handleError $ readNative nf')
23+ df <- runIOorExplode $ do
24+ d <- readNative def nf
25+ writeDocx wopts{writerUserDataDir = Just (".." </> "data")} d
26+ df' <- runIOorExplode (readNative def nf')
27+ p <- runIOorExplode $ readDocx (snd opts) df
28+ return (p, df')
2729
2830testCompareWithOptsIO :: Options -> String -> FilePath -> FilePath -> IO Test
2931testCompareWithOptsIO opts name nativeFileIn nativeFileOut = do
@@ -139,7 +141,7 @@ tests = [ testGroup "inlines"
139141 ]
140142 , testGroup "customized styles"
141143 [ testCompareWithOpts
142− ( def{writerReferenceDocx=Just "docx/custom-style-reference.docx"}
144+ ( def{writerReferenceDoc=Just "docx/custom-style-reference.docx"}
143145 , def)
144146 "simple customized blocks and inlines"
145147 "docx/custom-style-roundtrip-start.native"
tests/Tests/Writers/HTML.hs+2−2
@@ -8,7 +8,7 @@ import Tests.Helpers
88import Text.Pandoc.Arbitrary()
99
1010html :: (ToPandoc a) => a -> String
11−html = writeHtmlString def{ writerWrapText = WrapNone } . toPandoc
11+html = purely (writeHtml4String def{ writerWrapText = WrapNone }) . toPandoc
1212
1313{-
1414 "my test" =: X =?> Y
@@ -31,7 +31,7 @@ tests :: [Test]
3131tests = [ testGroup "inline code"
3232 [ "basic" =: code "@&" =?> "<code>@&amp;</code>"
3333 , "haskell" =: codeWith ("",["haskell"],[]) ">>="
34− =?> "<code class=\"haskell\">&gt;&gt;=</code>"
34+ =?> "<code class=\"sourceCode haskell\"><span class=\"fu\">&gt;&gt;=</span></code>"
3535 , "nolanguage" =: codeWith ("",["nolanguage"],[]) ">>="
3636 =?> "<code class=\"nolanguage\">&gt;&gt;=</code>"
3737 ]
tests/Tests/Writers/LaTeX.hs+6−4
@@ -8,13 +8,16 @@ import Text.Pandoc.Arbitrary ()
88import Text.Pandoc.Builder
99
1010latex :: (ToPandoc a) => a -> String
11−latex = latexWithOpts def{ writerHighlight = True }
11+latex = latexWithOpts def
1212
1313latexListing :: (ToPandoc a) => a -> String
1414latexListing = latexWithOpts def{ writerListings = True }
1515
1616latexWithOpts :: (ToPandoc a) => WriterOptions -> a -> String
17−latexWithOpts opts = writeLaTeX opts . toPandoc
17+latexWithOpts opts = purely (writeLaTeX opts) . toPandoc
18+
19+beamerWithOpts :: (ToPandoc a) => WriterOptions -> a -> String
20+beamerWithOpts opts = purely (writeBeamer opts) . toPandoc
1821
1922{-
2023 "my test" =: X =?> Y
@@ -95,8 +98,7 @@ tests = [ testGroup "code blocks"
9598 beamerTopLevelDiv :: (ToPandoc a)
9699 => TopLevelDivision -> a -> String
97100 beamerTopLevelDiv division =
98− latexWithOpts def { writerTopLevelDivision = division
99− , writerBeamer = True }
101+ beamerWithOpts def { writerTopLevelDivision = division }
100102 in
101103 [ test (latexTopLevelDiv TopLevelSection)
102104 "sections as top-level" $ headers =?>
tests/Tests/Writers/Markdown.hs+10−7
@@ -8,11 +8,14 @@ import Text.Pandoc
88import Tests.Helpers
99import Text.Pandoc.Arbitrary()
1010
11+defopts :: WriterOptions
12+defopts = def{ writerExtensions = pandocExtensions }
13+
1114markdown :: (ToPandoc a) => a -> String
12−markdown = writeMarkdown def . toPandoc
15+markdown = purely (writeMarkdown defopts) . toPandoc
1316
1417markdownWithOpts :: (ToPandoc a) => WriterOptions -> a -> String
15−markdownWithOpts opts x = writeMarkdown opts $ toPandoc x
18+markdownWithOpts opts x = purely (writeMarkdown opts) $ toPandoc x
1619
1720{-
1821 "my test" =: X =?> Y
@@ -84,7 +87,7 @@ noteTestDoc =
8487
8588noteTests :: Test
8689noteTests = testGroup "note and reference location"
87− [ test (markdownWithOpts def)
90+ [ test (markdownWithOpts defopts)
8891 "footnotes at the end of a document" $
8992 noteTestDoc =?>
9093 (unlines $ [ "First Header"
@@ -105,7 +108,7 @@ noteTests = testGroup "note and reference location"
105108 , ""
106109 , "[^2]: The second note."
107110 ])
108− , test (markdownWithOpts def{writerReferenceLocation=EndOfBlock})
111+ , test (markdownWithOpts defopts{writerReferenceLocation=EndOfBlock})
109112 "footnotes at the end of blocks" $
110113 noteTestDoc =?>
111114 (unlines $ [ "First Header"
@@ -126,7 +129,7 @@ noteTests = testGroup "note and reference location"
126129 , ""
127130 , "Some more text."
128131 ])
129− , test (markdownWithOpts def{writerReferenceLocation=EndOfBlock, writerReferenceLinks=True})
132+ , test (markdownWithOpts defopts{writerReferenceLocation=EndOfBlock, writerReferenceLinks=True})
130133 "footnotes and reference links at the end of blocks" $
131134 noteTestDoc =?>
132135 (unlines $ [ "First Header"
@@ -149,7 +152,7 @@ noteTests = testGroup "note and reference location"
149152 , ""
150153 , "Some more text."
151154 ])
152− , test (markdownWithOpts def{writerReferenceLocation=EndOfSection})
155+ , test (markdownWithOpts defopts{writerReferenceLocation=EndOfSection})
153156 "footnotes at the end of section" $
154157 noteTestDoc =?>
155158 (unlines $ [ "First Header"
@@ -179,7 +182,7 @@ shortcutLinkRefsTests =
179182 (=:) :: (ToString a, ToPandoc a)
180183
181184 => String -> (a, String) -> Test
182− (=:) = test (writeMarkdown (def {writerReferenceLinks = True}) . toPandoc)
185+ (=:) = test (purely (writeMarkdown defopts{writerReferenceLinks = True}) . toPandoc)
183186 in testGroup "Shortcut reference links"
184187 [ "Simple link (shortcutable)"
185188 =: (para (link "/url" "title" "foo"))
tests/Tests/Writers/Native.hs+2−2
@@ -8,11 +8,11 @@ import Text.Pandoc.Arbitrary()
88
99p_write_rt :: Pandoc -> Bool
1010p_write_rt d =
11− read (writeNative def{ writerTemplate = Just "" } d) == d
11+ read (purely (writeNative def{ writerTemplate = Just "" }) d) == d
1212
1313p_write_blocks_rt :: [Block] -> Bool
1414p_write_blocks_rt bs = length bs > 20 ||
15− read (writeNative def (Pandoc nullMeta bs)) ==
15+ read (purely (writeNative def) (Pandoc nullMeta bs)) ==
1616 bs
1717
1818tests :: [Test]
tests/Tests/Writers/Plain.hs+1−1
@@ -11,7 +11,7 @@ import Text.Pandoc.Arbitrary()
1111infix 4 =:
1212(=:) :: (ToString a, ToPandoc a)
1313 => String -> (a, String) -> Test
14−(=:) = test (writePlain def . toPandoc)
14+(=:) = test (purely (writePlain def) . toPandoc)
1515
1616
1717tests :: [Test]
tests/Tests/Writers/RST.hs+3−3
@@ -10,7 +10,7 @@ import Text.Pandoc.Arbitrary()
1010infix 4 =:
1111(=:) :: (ToString a, ToPandoc a)
1212 => String -> (a, String) -> Test
13−(=:) = test (writeRST def{ writerHighlight = True } . toPandoc)
13+(=:) = test (purely (writeRST def . toPandoc))
1414
1515tests :: [Test]
1616tests = [ testGroup "rubrics"
@@ -47,7 +47,7 @@ tests = [ testGroup "rubrics"
4747 [ "foo"
4848 , "==="]
4949 -- note: heading normalization is only done in standalone mode
50− , test (writeRST def{ writerTemplate = Just "$body$\n" } . toPandoc)
50+ , test (purely (writeRST def{ writerTemplate = Just "$body$\n" }) . toPandoc)
5151 "heading levels" $
5252 header 1 (text "Header 1") <>
5353 header 3 (text "Header 2") <>
@@ -77,7 +77,7 @@ tests = [ testGroup "rubrics"
7777 , ""
7878 , "Header 2"
7979 , "--------"]
80− , test (writeRST def{ writerTemplate = Just "$body$\n" } . toPandoc)
80+ , test (purely (writeRST def{ writerTemplate = Just "$body$\n" }) . toPandoc)
8181 "minimal heading levels" $
8282 header 2 (text "Header 1") <>
8383 header 3 (text "Header 2") <>
tests/Tests/Writers/TEI.hs+1−1
@@ -22,7 +22,7 @@ which is in turn shorthand for
2222infix 4 =:
2323(=:) :: (ToString a, ToPandoc a)
2424 => String -> (a, String) -> Test
25−(=:) = test (writeTEI def . toPandoc)
25+(=:) = test (purely (writeTEI def) . toPandoc)
2626
2727tests :: [Test]
2828tests = [ testGroup "block elements"
tests/fb2/basic.fb2+2−1
@@ -1,2 +1,3 @@
11<?xml version="1.0" encoding="UTF-8"?>
2−<FictionBook xmlns="http://www.gribuser.ru/xml/fictionbook/2.0" xmlns:l="http://www.w3.org/1999/xlink"><description><title-info /><document-info><program-used>pandoc</program-used></document-info></description><body><title><p /></title><annotation><p></p></annotation><section><title><p>Top-level title</p></title><section><title><p>Section</p></title><section><title><p>Subsection</p></title><p>This <emphasis>emphasized</emphasis> <strong>strong</strong> <code>verbatim</code> markdown. See this link<a l:href="#l1" type="note"><sup>[1]</sup></a>.</p><p>Ordered list:</p><p> 1. one</p><p> 2. two</p><p> 3. three</p><cite><p>Blockquote is for citatons.</p></cite><empty-line /><p><code>Code</code></p><p><code>block</code></p><p><code>is</code></p><p><code>for</code></p><p><code>code.</code></p><empty-line /><p><strikethrough>Strikeout</strikethrough> is Pandoc&#39;s extension. Superscript and subscripts too: H<sub>2</sub>O is a liquid<a l:href="#n2" type="note"><sup>[2]</sup></a>. 2<sup>10</sup> is 1024.</p><p>Math is another Pandoc extension: <code>E = m c^2</code>.</p></section></section></section></body><body name="notes"><section id="l1"><title><p>1</p></title><p><code>http://example.com/</code></p></section><section id="n2"><title><p>2</p></title><p>Sometimes.</p></section></body></FictionBook>
2+<FictionBook xmlns="http://www.gribuser.ru/xml/fictionbook/2.0" xmlns:l="http://www.w3.org/1999/xlink"><description><title-info /><document-info><program-used>pandoc</program-used></document-info></description><body><title><p /></title><annotation><p></p></annotation><section><title><p>Top-level title</p></title><section><title><p>Section</p></title><section><title><p>Subsection</p></title><p>This <emphasis>emphasized</emphasis> <strong>strong</strong> <code>verbatim</code> markdown. See this link<a l:href="#l1" type="note"><sup>[1]</sup></a>.</p><p>Ordered list:</p><p> 1. one</p><p> 2. two</p><p> 3. three</p><cite><p>Blockquote is for citatons.</p></cite><empty-line /><p><code>Code</code></p><p><code>block</code></p><p><code>is</code></p><p><code>for</code></p><p><code>code.</code></p><empty-line /><p><strikethrough>Strikeout</strikethrough> is Pandoc’s extension. Superscript and subscripts too: H<sub>2</sub>O is a liquid<a l:href="#n2" type="note"><sup>[2]</sup></a>. 2<sup>10</sup> is 1024.</p><p>Math is another Pandoc extension: <code>E = m c^2</code>.</p></section></section></section></body><body name="notes"><section id="l1"><title><p>1</p></title><p><code>http://example.com/</code></p></section><section id="n2"><title><p>2</p></title><p>Sometimes.</p></section></body></FictionBook>
3+
tests/fb2/titles.fb2+2−1
@@ -1,2 +1,3 @@
11<?xml version="1.0" encoding="UTF-8"?>
2−<FictionBook xmlns="http://www.gribuser.ru/xml/fictionbook/2.0" xmlns:l="http://www.w3.org/1999/xlink"><description><title-info /><document-info><program-used>pandoc</program-used></document-info></description><body><title><p /></title><annotation><p></p></annotation><section><title><p>Simple title</p></title><p>This example tests if Pandoc doesn&#39;t insert forbidden elements in FictionBook titles.</p></section><section><title><p>Emphasized Strong Title</p></title></section><section><title><p>Title with</p><empty-line /><p>line break</p></title></section></body></FictionBook>
2+<FictionBook xmlns="http://www.gribuser.ru/xml/fictionbook/2.0" xmlns:l="http://www.w3.org/1999/xlink"><description><title-info /><document-info><program-used>pandoc</program-used></document-info></description><body><title><p /></title><annotation><p></p></annotation><section><title><p>Simple title</p></title><p>This example tests if Pandoc doesn’t insert forbidden elements in FictionBook titles.</p></section><section><title><p>Emphasized Strong Title</p></title></section><section><title><p>Title with</p><empty-line /><p>line break</p></title></section></body></FictionBook>
3+
tests/lhs-test.html+8−5
@@ -1,9 +1,9 @@
1−<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2−<html xmlns="http://www.w3.org/1999/xhtml">
1+<!DOCTYPE html>
2+<html>
33<head>
4− <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5− <meta http-equiv="Content-Style-Type" content="text/css" />
6− <meta name="generator" content="pandoc" />
4+ <meta charset="utf-8">
5+ <meta name="generator" content="pandoc">
6+ <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
77 <title></title>
88 <style type="text/css">code{white-space: pre;}</style>
99 <style type="text/css">
@@ -43,6 +43,9 @@ code > span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Ann
4343code > span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
4444code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
4545 </style>
46+ <!--[if lt IE 9]>
47+ <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
48+ <![endif]-->
4649</head>
4750<body>
4851<h1 id="lhs-test">lhs test</h1>
tests/lhs-test.html+lhs+8−5
@@ -1,9 +1,9 @@
1−<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2−<html xmlns="http://www.w3.org/1999/xhtml">
1+<!DOCTYPE html>
2+<html>
33<head>
4− <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5− <meta http-equiv="Content-Style-Type" content="text/css" />
6− <meta name="generator" content="pandoc" />
4+ <meta charset="utf-8">
5+ <meta name="generator" content="pandoc">
6+ <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
77 <title></title>
88 <style type="text/css">code{white-space: pre;}</style>
99 <style type="text/css">
@@ -43,6 +43,9 @@ code > span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Ann
4343code > span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
4444code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
4545 </style>
46+ <!--[if lt IE 9]>
47+ <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
48+ <![endif]-->
4649</head>
4750<body>
4851<h1 id="lhs-test">lhs test</h1>
tests/markdown-citations.native+8−8
@@ -3,15 +3,15 @@
33 [[Para [Cite [Citation {citationId = "nonexistent", citationPrefix = [], citationSuffix = [], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@nonexistent]"]]]
44 ,[Para [Cite [Citation {citationId = "nonexistent", citationPrefix = [], citationSuffix = [], citationMode = AuthorInText, citationNoteNum = 0, citationHash = 0}] [Str "@nonexistent"]]]
55 ,[Para [Cite [Citation {citationId = "item1", citationPrefix = [], citationSuffix = [], citationMode = AuthorInText, citationNoteNum = 0, citationHash = 0}] [Str "@item1"],Space,Str "says",Space,Str "blah."]]
6− ,[Para [Cite [Citation {citationId = "item1", citationPrefix = [], citationSuffix = [Str "p.",Space,Str "30"], citationMode = AuthorInText, citationNoteNum = 0, citationHash = 0}] [Str "@item1",Space,Str "[p.",Space,Str "30]"],Space,Str "says",Space,Str "blah."]]
7− ,[Para [Cite [Citation {citationId = "item1", citationPrefix = [], citationSuffix = [Str "p.",Space,Str "30,",Space,Str "with",Space,Str "suffix"], citationMode = AuthorInText, citationNoteNum = 0, citationHash = 0}] [Str "@item1",Space,Str "[p.",Space,Str "30,",Space,Str "with",Space,Str "suffix]"],Space,Str "says",Space,Str "blah."]]
8− ,[Para [Cite [Citation {citationId = "item1", citationPrefix = [], citationSuffix = [], citationMode = AuthorInText, citationNoteNum = 0, citationHash = 0},Citation {citationId = "item2", citationPrefix = [], citationSuffix = [Space,Str "p.",Space,Str "30"], citationMode = SuppressAuthor, citationNoteNum = 0, citationHash = 0},Citation {citationId = "\1087\1091\1085\1082\1090\&3", citationPrefix = [Str "see",Space,Str "also"], citationSuffix = [], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "@item1",Space,Str "[-@item2",Space,Str "p.",Space,Str "30;",Space,Str "see",Space,Str "also",Space,Str "@\1087\1091\1085\1082\1090\&3]"],Space,Str "says",Space,Str "blah."]]
9− ,[Para [Str "In",Space,Str "a",Space,Str "note.",Note [Para [Cite [Citation {citationId = "\1087\1091\1085\1082\1090\&3", citationPrefix = [], citationSuffix = [Str "p.",Space,Str "12"], citationMode = AuthorInText, citationNoteNum = 0, citationHash = 0}] [Str "@\1087\1091\1085\1082\1090\&3",Space,Str "[p.",Space,Str "12]"],Space,Str "and",Space,Str "a",Space,Str "citation",Space,Str "without",Space,Str "locators",Space,Cite [Citation {citationId = "\1087\1091\1085\1082\1090\&3", citationPrefix = [], citationSuffix = [], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@\1087\1091\1085\1082\1090\&3]"],Str "."]]]]
10− ,[Para [Str "A",Space,Str "citation",Space,Str "group",Space,Cite [Citation {citationId = "item1", citationPrefix = [Str "see"], citationSuffix = [Space,Str "chap.",Space,Str "3"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0},Citation {citationId = "\1087\1091\1085\1082\1090\&3", citationPrefix = [Str "also"], citationSuffix = [Space,Str "p.",Space,Str "34-35"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[see",Space,Str "@item1",Space,Str "chap.",Space,Str "3;",Space,Str "also",Space,Str "@\1087\1091\1085\1082\1090\&3",Space,Str "p.",Space,Str "34-35]"],Str "."]]
11− ,[Para [Str "Another",Space,Str "one",Space,Cite [Citation {citationId = "item1", citationPrefix = [Str "see"], citationSuffix = [Space,Str "p.",Space,Str "34-35"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[see",Space,Str "@item1",Space,Str "p.",Space,Str "34-35]"],Str "."]]
6+ ,[Para [Cite [Citation {citationId = "item1", citationPrefix = [], citationSuffix = [Str "p.\160\&30"], citationMode = AuthorInText, citationNoteNum = 0, citationHash = 0}] [Str "@item1",Space,Str "[p.",Space,Str "30]"],Space,Str "says",Space,Str "blah."]]
7+ ,[Para [Cite [Citation {citationId = "item1", citationPrefix = [], citationSuffix = [Str "p.\160\&30,",Space,Str "with",Space,Str "suffix"], citationMode = AuthorInText, citationNoteNum = 0, citationHash = 0}] [Str "@item1",Space,Str "[p.",Space,Str "30,",Space,Str "with",Space,Str "suffix]"],Space,Str "says",Space,Str "blah."]]
8+ ,[Para [Cite [Citation {citationId = "item1", citationPrefix = [], citationSuffix = [], citationMode = AuthorInText, citationNoteNum = 0, citationHash = 0},Citation {citationId = "item2", citationPrefix = [], citationSuffix = [Space,Str "p.\160\&30"], citationMode = SuppressAuthor, citationNoteNum = 0, citationHash = 0},Citation {citationId = "\1087\1091\1085\1082\1090\&3", citationPrefix = [Str "see",Space,Str "also"], citationSuffix = [], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "@item1",Space,Str "[-@item2",Space,Str "p.",Space,Str "30;",Space,Str "see",Space,Str "also",Space,Str "@\1087\1091\1085\1082\1090\&3]"],Space,Str "says",Space,Str "blah."]]
9+ ,[Para [Str "In",Space,Str "a",Space,Str "note.",Note [Para [Cite [Citation {citationId = "\1087\1091\1085\1082\1090\&3", citationPrefix = [], citationSuffix = [Str "p.\160\&12"], citationMode = AuthorInText, citationNoteNum = 0, citationHash = 0}] [Str "@\1087\1091\1085\1082\1090\&3",Space,Str "[p.",Space,Str "12]"],Space,Str "and",Space,Str "a",Space,Str "citation",Space,Str "without",Space,Str "locators",Space,Cite [Citation {citationId = "\1087\1091\1085\1082\1090\&3", citationPrefix = [], citationSuffix = [], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@\1087\1091\1085\1082\1090\&3]"],Str "."]]]]
10+ ,[Para [Str "A",Space,Str "citation",Space,Str "group",Space,Cite [Citation {citationId = "item1", citationPrefix = [Str "see"], citationSuffix = [Space,Str "chap.",Space,Str "3"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0},Citation {citationId = "\1087\1091\1085\1082\1090\&3", citationPrefix = [Str "also"], citationSuffix = [Space,Str "p.\160\&34-35"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[see",Space,Str "@item1",Space,Str "chap.",Space,Str "3;",Space,Str "also",Space,Str "@\1087\1091\1085\1082\1090\&3",Space,Str "p.",Space,Str "34-35]"],Str "."]]
11+ ,[Para [Str "Another",Space,Str "one",Space,Cite [Citation {citationId = "item1", citationPrefix = [Str "see"], citationSuffix = [Space,Str "p.\160\&34-35"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[see",Space,Str "@item1",Space,Str "p.",Space,Str "34-35]"],Str "."]]
1212 ,[Para [Str "And",Space,Str "another",Space,Str "one",Space,Str "in",Space,Str "a",Space,Str "note.",Note [Para [Str "Some",Space,Str "citations",Space,Cite [Citation {citationId = "item1", citationPrefix = [Str "see"], citationSuffix = [Space,Str "chap.",Space,Str "3"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0},Citation {citationId = "\1087\1091\1085\1082\1090\&3", citationPrefix = [], citationSuffix = [], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0},Citation {citationId = "item2", citationPrefix = [], citationSuffix = [], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[see",Space,Str "@item1",Space,Str "chap.",Space,Str "3;",Space,Str "@\1087\1091\1085\1082\1090\&3;",Space,Str "@item2]"],Str "."]]]]
13− ,[Para [Str "Citation",Space,Str "with",Space,Str "a",Space,Str "suffix",Space,Str "and",Space,Str "locator",Space,Cite [Citation {citationId = "item1", citationPrefix = [], citationSuffix = [Space,Str "pp.",Space,Str "33,",Space,Str "35-37,",Space,Str "and",Space,Str "nowhere",Space,Str "else"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@item1",Space,Str "pp.",Space,Str "33,",Space,Str "35-37,",Space,Str "and",Space,Str "nowhere",Space,Str "else]"],Str "."]]
13+ ,[Para [Str "Citation",Space,Str "with",Space,Str "a",Space,Str "suffix",Space,Str "and",Space,Str "locator",Space,Cite [Citation {citationId = "item1", citationPrefix = [], citationSuffix = [Space,Str "pp.\160\&33,",Space,Str "35-37,",Space,Str "and",Space,Str "nowhere",Space,Str "else"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@item1",Space,Str "pp.",Space,Str "33,",Space,Str "35-37,",Space,Str "and",Space,Str "nowhere",Space,Str "else]"],Str "."]]
1414 ,[Para [Str "Citation",Space,Str "with",Space,Str "suffix",Space,Str "only",Space,Cite [Citation {citationId = "item1", citationPrefix = [], citationSuffix = [Space,Str "and",Space,Str "nowhere",Space,Str "else"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@item1",Space,Str "and",Space,Str "nowhere",Space,Str "else]"],Str "."]]
15− ,[Para [Str "Now",Space,Str "some",Space,Str "modifiers.",Note [Para [Str "Like",Space,Str "a",Space,Str "citation",Space,Str "without",Space,Str "author:",Space,Cite [Citation {citationId = "item1", citationPrefix = [], citationSuffix = [], citationMode = SuppressAuthor, citationNoteNum = 0, citationHash = 0}] [Str "[-@item1]"],Str ",",Space,Str "and",Space,Str "now",Space,Str "Doe",Space,Str "with",Space,Str "a",Space,Str "locator",Space,Cite [Citation {citationId = "item2", citationPrefix = [], citationSuffix = [Space,Str "p.",Space,Str "44"], citationMode = SuppressAuthor, citationNoteNum = 0, citationHash = 0}] [Str "[-@item2",Space,Str "p.",Space,Str "44]"],Str "."]]]]
15+ ,[Para [Str "Now",Space,Str "some",Space,Str "modifiers.",Note [Para [Str "Like",Space,Str "a",Space,Str "citation",Space,Str "without",Space,Str "author:",Space,Cite [Citation {citationId = "item1", citationPrefix = [], citationSuffix = [], citationMode = SuppressAuthor, citationNoteNum = 0, citationHash = 0}] [Str "[-@item1]"],Str ",",Space,Str "and",Space,Str "now",Space,Str "Doe",Space,Str "with",Space,Str "a",Space,Str "locator",Space,Cite [Citation {citationId = "item2", citationPrefix = [], citationSuffix = [Space,Str "p.\160\&44"], citationMode = SuppressAuthor, citationNoteNum = 0, citationHash = 0}] [Str "[-@item2",Space,Str "p.",Space,Str "44]"],Str "."]]]]
1616 ,[Para [Str "With",Space,Str "some",Space,Str "markup",Space,Cite [Citation {citationId = "item1", citationPrefix = [Emph [Str "see"]], citationSuffix = [Space,Str "p.",Space,Strong [Str "32"]], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[*see*",Space,Str "@item1",Space,Str "p.",Space,Str "**32**]"],Str "."]]]
1717,Header 1 ("references",[],[]) [Str "References"]]
tests/rst-reader.native−6
@@ -327,15 +327,9 @@ Pandoc (Meta {unMeta = fromList [("author",MetaList [MetaInlines [Str "John",Spa
327327,Para [Str "Some",Space,Superscript [Str "of"],Space,Str "these",Space,Superscript [Str "words"],Space,Str "are",Space,Str "in",Space,Superscript [Str "superscript"],Str "."]
328328,Para [Str "Reset",Space,Str "default-role",Space,Str "to",Space,Str "the",Space,Str "default",Space,Str "default."]
329329,Para [Str "And",Space,Str "now",Space,Str "some-invalid-string-3231231",Space,Str "is",Space,Str "nonsense."]
330−,Null
331330,Para [Str "And",Space,Str "now",Space,Str "with",Space,RawInline (Format "html") "<b>inline</b> <span id=\"test\">HTML</span>",Str "."]
332−,Null
333331,Para [Str "And",Space,Str "some",Space,Str "inline",Space,Str "haskell",Space,Code ("",["haskell","sourceCode"],[]) "fmap id [1,2..10]",Str "."]
334−,Null
335−,Null
336332,Para [Str "Indirect",Space,Str "python",Space,Str "role",Space,Code ("",["py","python","indirect","sourceCode"],[]) "[x*x for x in [1,2,3,4,5]]",Str "."]
337−,Null
338−,Null
339333,Para [Str "Different",Space,Str "indirect",Space,Str "C",Space,Code ("",["c","different-indirect","sourceCode"],[]) "int x = 15;",Str "."]
340334,Header 2 ("literal-symbols",[],[]) [Str "Literal",Space,Str "symbols"]
341335,Para [Str "2*2",Space,Str "=",Space,Str "4*1"]]
tests/tables-rstsubset.native+4−4
@@ -67,8 +67,8 @@
6767 ,[[Plain [Str "Second"]]
6868 ,[Plain [Str "row"]]
6969 ,[Plain [Str "5.0"]]
70− ,[Plain [Str "Here's",Space,Str "another",Space,Str "one.",Space,Str "Note",SoftBreak,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",SoftBreak,Str "rows."]]]]
71−,Para [Str "Table:",Space,Str "Here's",Space,Str "the",Space,Str "caption.",Space,Str "It",Space,Str "may",Space,Str "span",Space,Str "multiple",Space,Str "lines."]
70+ ,[Plain [Str "Here\8217s",Space,Str "another",Space,Str "one.",Space,Str "Note",SoftBreak,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",SoftBreak,Str "rows."]]]]
71+,Para [Str "Table:",Space,Str "Here\8217s",Space,Str "the",Space,Str "caption.",Space,Str "It",Space,Str "may",Space,Str "span",Space,Str "multiple",Space,Str "lines."]
7272,Para [Str "Multiline",Space,Str "table",Space,Str "without",Space,Str "caption:"]
7373,Table [] [AlignDefault,AlignDefault,AlignDefault,AlignDefault] [0.175,0.1625,0.1875,0.3625]
7474 [[Plain [Str "Centered",Space,Str "Header"]]
@@ -82,7 +82,7 @@
8282 ,[[Plain [Str "Second"]]
8383 ,[Plain [Str "row"]]
8484 ,[Plain [Str "5.0"]]
85− ,[Plain [Str "Here's",Space,Str "another",Space,Str "one.",Space,Str "Note",SoftBreak,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",SoftBreak,Str "rows."]]]]
85+ ,[Plain [Str "Here\8217s",Space,Str "another",Space,Str "one.",Space,Str "Note",SoftBreak,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",SoftBreak,Str "rows."]]]]
8686,Para [Str "Table",Space,Str "without",Space,Str "column",Space,Str "headers:"]
8787,Table [] [AlignDefault,AlignDefault,AlignDefault,AlignDefault] [0.1,0.1,0.1,0.1]
8888 [[]
@@ -114,4 +114,4 @@
114114 ,[[Plain [Str "Second"]]
115115 ,[Plain [Str "row"]]
116116 ,[Plain [Str "5.0"]]
117− ,[Plain [Str "Here's",Space,Str "another",Space,Str "one.",Space,Str "Note",SoftBreak,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",SoftBreak,Str "rows."]]]]]
117+ ,[Plain [Str "Here\8217s",Space,Str "another",Space,Str "one.",Space,Str "Note",SoftBreak,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",SoftBreak,Str "rows."]]]]]
tests/tables.asciidoc+4−4
@@ -32,12 +32,12 @@ Simple table indented two spaces:
3232
3333Multiline table with caption:
3434
35−.Here's the caption. It may span multiple lines.
35+.Here’s the caption. It may span multiple lines.
3636[width="78%",cols="^21%,<17%,>20%,<42%",options="header",]
3737|=======================================================================
3838|Centered Header |Left Aligned |Right Aligned |Default aligned
3939|First |row |12.0 |Example of a row that spans multiple lines.
40−|Second |row |5.0 |Here's another one. Note the blank line between rows.
40+|Second |row |5.0 |Here’s another one. Note the blank line between rows.
4141|=======================================================================
4242
4343Multiline table without caption:
@@ -46,7 +46,7 @@ Multiline table without caption:
4646|=======================================================================
4747|Centered Header |Left Aligned |Right Aligned |Default aligned
4848|First |row |12.0 |Example of a row that spans multiple lines.
49−|Second |row |5.0 |Here's another one. Note the blank line between rows.
49+|Second |row |5.0 |Here’s another one. Note the blank line between rows.
5050|=======================================================================
5151
5252Table without column headers:
@@ -63,5 +63,5 @@ Multiline table without column headers:
6363[width="78%",cols="^21%,<17%,>20%,42%",]
6464|=======================================================================
6565|First |row |12.0 |Example of a row that spans multiple lines.
66−|Second |row |5.0 |Here's another one. Note the blank line between rows.
66+|Second |row |5.0 |Here’s another one. Note the blank line between rows.
6767|=======================================================================
tests/tables.docbook → tests/tables.docbook4renamed+4−4
@@ -222,7 +222,7 @@
222222</para>
223223<table>
224224 <title>
225− Here's the caption. It may span multiple lines.
225+ Here’s the caption. It may span multiple lines.
226226 </title>
227227 <tgroup cols="4">
228228 <colspec colwidth="15*" align="center" />
@@ -271,7 +271,7 @@
271271 5.0
272272 </entry>
273273 <entry>
274− Here's another one. Note the blank line between rows.
274+ Here’s another one. Note the blank line between rows.
275275 </entry>
276276 </row>
277277 </tbody>
@@ -328,7 +328,7 @@
328328 5.0
329329 </entry>
330330 <entry>
331− Here's another one. Note the blank line between rows.
331+ Here’s another one. Note the blank line between rows.
332332 </entry>
333333 </row>
334334 </tbody>
@@ -424,7 +424,7 @@
424424 5.0
425425 </entry>
426426 <entry>
427− Here's another one. Note the blank line between rows.
427+ Here’s another one. Note the blank line between rows.
428428 </entry>
429429 </row>
430430 </tbody>
tests/tables.docbook5+4−4
@@ -222,7 +222,7 @@
222222</para>
223223<table>
224224 <title>
225− Here's the caption. It may span multiple lines.
225+ Here’s the caption. It may span multiple lines.
226226 </title>
227227 <tgroup cols="4">
228228 <colspec colwidth="15*" align="center" />
@@ -271,7 +271,7 @@
271271 5.0
272272 </entry>
273273 <entry>
274− Here's another one. Note the blank line between rows.
274+ Here’s another one. Note the blank line between rows.
275275 </entry>
276276 </row>
277277 </tbody>
@@ -328,7 +328,7 @@
328328 5.0
329329 </entry>
330330 <entry>
331− Here's another one. Note the blank line between rows.
331+ Here’s another one. Note the blank line between rows.
332332 </entry>
333333 </row>
334334 </tbody>
@@ -424,7 +424,7 @@
424424 5.0
425425 </entry>
426426 <entry>
427− Here's another one. Note the blank line between rows.
427+ Here’s another one. Note the blank line between rows.
428428 </entry>
429429 </row>
430430 </tbody>
tests/tables.dokuwiki+4−4
@@ -23,16 +23,16 @@ Demonstration of simple table syntax.
2323
2424Multiline table with caption:
2525
26−Here's the caption. It may span multiple lines.
26+Here’s the caption. It may span multiple lines.
2727^ Centered Header ^Left Aligned ^ Right Aligned^Default aligned ^
2828| First |row | 12.0|Example of a row that spans multiple lines. |
29−| Second |row | 5.0|Here's another one. Note the blank line between rows. |
29+| Second |row | 5.0|Here’s another one. Note the blank line between rows. |
3030
3131Multiline table without caption:
3232
3333^ Centered Header ^Left Aligned ^ Right Aligned^Default aligned ^
3434| First |row | 12.0|Example of a row that spans multiple lines. |
35−| Second |row | 5.0|Here's another one. Note the blank line between rows. |
35+| Second |row | 5.0|Here’s another one. Note the blank line between rows. |
3636
3737Table without column headers:
3838
@@ -43,5 +43,5 @@ Table without column headers:
4343Multiline table without column headers:
4444
4545| First |row | 12.0|Example of a row that spans multiple lines. |
46−| Second |row | 5.0|Here's another one. Note the blank line between rows.|
46+| Second |row | 5.0|Here’s another one. Note the blank line between rows.|
4747
tests/tables.fb2+2−1
@@ -1,2 +1,3 @@
11<?xml version="1.0" encoding="UTF-8"?>
2−<FictionBook xmlns="http://www.gribuser.ru/xml/fictionbook/2.0" xmlns:l="http://www.w3.org/1999/xlink"><description><title-info /><document-info><program-used>pandoc</program-used></document-info></description><body><title><p /></title><annotation><p></p></annotation><section><p>Simple table with caption:</p><table><tr><th align="right">Right</th><th align="left">Left</th><th align="center">Center</th><th align="left">Default</th></tr><tr><td align="right">12</td><td align="left">12</td><td align="center">12</td><td align="left">12</td></tr><tr><td align="right">123</td><td align="left">123</td><td align="center">123</td><td align="left">123</td></tr><tr><td align="right">1</td><td align="left">1</td><td align="center">1</td><td align="left">1</td></tr></table><p><emphasis>Demonstration of simple table syntax.</emphasis></p><p>Simple table without caption:</p><table><tr><th align="right">Right</th><th align="left">Left</th><th align="center">Center</th><th align="left">Default</th></tr><tr><td align="right">12</td><td align="left">12</td><td align="center">12</td><td align="left">12</td></tr><tr><td align="right">123</td><td align="left">123</td><td align="center">123</td><td align="left">123</td></tr><tr><td align="right">1</td><td align="left">1</td><td align="center">1</td><td align="left">1</td></tr></table><p><emphasis /></p><p>Simple table indented two spaces:</p><table><tr><th align="right">Right</th><th align="left">Left</th><th align="center">Center</th><th align="left">Default</th></tr><tr><td align="right">12</td><td align="left">12</td><td align="center">12</td><td align="left">12</td></tr><tr><td align="right">123</td><td align="left">123</td><td align="center">123</td><td align="left">123</td></tr><tr><td align="right">1</td><td align="left">1</td><td align="center">1</td><td align="left">1</td></tr></table><p><emphasis>Demonstration of simple table syntax.</emphasis></p><p>Multiline table with caption:</p><table><tr><th align="center">Centered Header</th><th align="left">Left Aligned</th><th align="right">Right Aligned</th><th align="left">Default aligned</th></tr><tr><td align="center">First</td><td align="left">row</td><td align="right">12.0</td><td align="left">Example of a row that spans multiple lines.</td></tr><tr><td align="center">Second</td><td align="left">row</td><td align="right">5.0</td><td align="left">Here&#39;s another one. Note the blank line between rows.</td></tr></table><p><emphasis>Here&#39;s the caption. It may span multiple lines.</emphasis></p><p>Multiline table without caption:</p><table><tr><th align="center">Centered Header</th><th align="left">Left Aligned</th><th align="right">Right Aligned</th><th align="left">Default aligned</th></tr><tr><td align="center">First</td><td align="left">row</td><td align="right">12.0</td><td align="left">Example of a row that spans multiple lines.</td></tr><tr><td align="center">Second</td><td align="left">row</td><td align="right">5.0</td><td align="left">Here&#39;s another one. Note the blank line between rows.</td></tr></table><p><emphasis /></p><p>Table without column headers:</p><table><tr><th align="right" /><th align="left" /><th align="center" /><th align="right" /></tr><tr><td align="right">12</td><td align="left">12</td><td align="center">12</td><td align="right">12</td></tr><tr><td align="right">123</td><td align="left">123</td><td align="center">123</td><td align="right">123</td></tr><tr><td align="right">1</td><td align="left">1</td><td align="center">1</td><td align="right">1</td></tr></table><p><emphasis /></p><p>Multiline table without column headers:</p><table><tr><th align="center" /><th align="left" /><th align="right" /><th align="left" /></tr><tr><td align="center">First</td><td align="left">row</td><td align="right">12.0</td><td align="left">Example of a row that spans multiple lines.</td></tr><tr><td align="center">Second</td><td align="left">row</td><td align="right">5.0</td><td align="left">Here&#39;s another one. Note the blank line between rows.</td></tr></table><p><emphasis /></p></section></body></FictionBook>
2+<FictionBook xmlns="http://www.gribuser.ru/xml/fictionbook/2.0" xmlns:l="http://www.w3.org/1999/xlink"><description><title-info /><document-info><program-used>pandoc</program-used></document-info></description><body><title><p /></title><annotation><p></p></annotation><section><p>Simple table with caption:</p><table><tr><th align="right">Right</th><th align="left">Left</th><th align="center">Center</th><th align="left">Default</th></tr><tr><td align="right">12</td><td align="left">12</td><td align="center">12</td><td align="left">12</td></tr><tr><td align="right">123</td><td align="left">123</td><td align="center">123</td><td align="left">123</td></tr><tr><td align="right">1</td><td align="left">1</td><td align="center">1</td><td align="left">1</td></tr></table><p><emphasis>Demonstration of simple table syntax.</emphasis></p><p>Simple table without caption:</p><table><tr><th align="right">Right</th><th align="left">Left</th><th align="center">Center</th><th align="left">Default</th></tr><tr><td align="right">12</td><td align="left">12</td><td align="center">12</td><td align="left">12</td></tr><tr><td align="right">123</td><td align="left">123</td><td align="center">123</td><td align="left">123</td></tr><tr><td align="right">1</td><td align="left">1</td><td align="center">1</td><td align="left">1</td></tr></table><p><emphasis /></p><p>Simple table indented two spaces:</p><table><tr><th align="right">Right</th><th align="left">Left</th><th align="center">Center</th><th align="left">Default</th></tr><tr><td align="right">12</td><td align="left">12</td><td align="center">12</td><td align="left">12</td></tr><tr><td align="right">123</td><td align="left">123</td><td align="center">123</td><td align="left">123</td></tr><tr><td align="right">1</td><td align="left">1</td><td align="center">1</td><td align="left">1</td></tr></table><p><emphasis>Demonstration of simple table syntax.</emphasis></p><p>Multiline table with caption:</p><table><tr><th align="center">Centered Header</th><th align="left">Left Aligned</th><th align="right">Right Aligned</th><th align="left">Default aligned</th></tr><tr><td align="center">First</td><td align="left">row</td><td align="right">12.0</td><td align="left">Example of a row that spans multiple lines.</td></tr><tr><td align="center">Second</td><td align="left">row</td><td align="right">5.0</td><td align="left">Here’s another one. Note the blank line between rows.</td></tr></table><p><emphasis>Here’s the caption. It may span multiple lines.</emphasis></p><p>Multiline table without caption:</p><table><tr><th align="center">Centered Header</th><th align="left">Left Aligned</th><th align="right">Right Aligned</th><th align="left">Default aligned</th></tr><tr><td align="center">First</td><td align="left">row</td><td align="right">12.0</td><td align="left">Example of a row that spans multiple lines.</td></tr><tr><td align="center">Second</td><td align="left">row</td><td align="right">5.0</td><td align="left">Here’s another one. Note the blank line between rows.</td></tr></table><p><emphasis /></p><p>Table without column headers:</p><table><tr><th align="right" /><th align="left" /><th align="center" /><th align="right" /></tr><tr><td align="right">12</td><td align="left">12</td><td align="center">12</td><td align="right">12</td></tr><tr><td align="right">123</td><td align="left">123</td><td align="center">123</td><td align="right">123</td></tr><tr><td align="right">1</td><td align="left">1</td><td align="center">1</td><td align="right">1</td></tr></table><p><emphasis /></p><p>Multiline table without column headers:</p><table><tr><th align="center" /><th align="left" /><th align="right" /><th align="left" /></tr><tr><td align="center">First</td><td align="left">row</td><td align="right">12.0</td><td align="left">Example of a row that spans multiple lines.</td></tr><tr><td align="center">Second</td><td align="left">row</td><td align="right">5.0</td><td align="left">Here’s another one. Note the blank line between rows.</td></tr></table><p><emphasis /></p></section></body></FictionBook>
3+
tests/tables.haddock+4−4
@@ -35,12 +35,12 @@ Multiline table with caption:
3535> First row 12.0 Example of a row that
3636> spans multiple lines.
3737>
38−> Second row 5.0 Here\'s another one. Note
38+> Second row 5.0 Here’s another one. Note
3939> the blank line between
4040> rows.
4141> --------------------------------------------------------------
4242>
43−> Here\'s the caption. It may span multiple lines.
43+> Here’s the caption. It may span multiple lines.
4444
4545Multiline table without caption:
4646
@@ -51,7 +51,7 @@ Multiline table without caption:
5151> First row 12.0 Example of a row that
5252> spans multiple lines.
5353>
54−> Second row 5.0 Here\'s another one. Note
54+> Second row 5.0 Here’s another one. Note
5555> the blank line between
5656> rows.
5757> --------------------------------------------------------------
@@ -70,7 +70,7 @@ Multiline table without column headers:
7070> First row 12.0 Example of a row that
7171> spans multiple lines.
7272>
73−> Second row 5.0 Here\'s another one. Note
73+> Second row 5.0 Here’s another one. Note
7474> the blank line between
7575> rows.
7676> ----------- ---------- ------------ --------------------------
tests/tables.html → tests/tables.html4renamed+4−4
@@ -95,7 +95,7 @@
9595</table>
9696<p>Multiline table with caption:</p>
9797<table style="width:79%;">
98−<caption>Here's the caption. It may span multiple lines.</caption>
98+<caption>Here’s the caption. It may span multiple lines.</caption>
9999<colgroup>
100100<col width="15%" />
101101<col width="13%" />
@@ -121,7 +121,7 @@
121121<td align="center">Second</td>
122122<td align="left">row</td>
123123<td align="right">5.0</td>
124−<td align="left">Here's another one. Note the blank line between rows.</td>
124+<td align="left">Here’s another one. Note the blank line between rows.</td>
125125</tr>
126126</tbody>
127127</table>
@@ -152,7 +152,7 @@
152152<td align="center">Second</td>
153153<td align="left">row</td>
154154<td align="right">5.0</td>
155−<td align="left">Here's another one. Note the blank line between rows.</td>
155+<td align="left">Here’s another one. Note the blank line between rows.</td>
156156</tr>
157157</tbody>
158158</table>
@@ -198,7 +198,7 @@
198198<td align="center">Second</td>
199199<td align="left">row</td>
200200<td align="right">5.0</td>
201−<td>Here's another one. Note the blank line between rows.</td>
201+<td>Here’s another one. Note the blank line between rows.</td>
202202</tr>
203203</tbody>
204204</table>
tests/tables.html5new file+204
@@ -0,0 +1,204 @@
1+<p>Simple table with caption:</p>
2+<table>
3+<caption>Demonstration of simple table syntax.</caption>
4+<thead>
5+<tr class="header">
6+<th style="text-align: right;">Right</th>
7+<th style="text-align: left;">Left</th>
8+<th style="text-align: center;">Center</th>
9+<th>Default</th>
10+</tr>
11+</thead>
12+<tbody>
13+<tr class="odd">
14+<td style="text-align: right;">12</td>
15+<td style="text-align: left;">12</td>
16+<td style="text-align: center;">12</td>
17+<td>12</td>
18+</tr>
19+<tr class="even">
20+<td style="text-align: right;">123</td>
21+<td style="text-align: left;">123</td>
22+<td style="text-align: center;">123</td>
23+<td>123</td>
24+</tr>
25+<tr class="odd">
26+<td style="text-align: right;">1</td>
27+<td style="text-align: left;">1</td>
28+<td style="text-align: center;">1</td>
29+<td>1</td>
30+</tr>
31+</tbody>
32+</table>
33+<p>Simple table without caption:</p>
34+<table>
35+<thead>
36+<tr class="header">
37+<th style="text-align: right;">Right</th>
38+<th style="text-align: left;">Left</th>
39+<th style="text-align: center;">Center</th>
40+<th>Default</th>
41+</tr>
42+</thead>
43+<tbody>
44+<tr class="odd">
45+<td style="text-align: right;">12</td>
46+<td style="text-align: left;">12</td>
47+<td style="text-align: center;">12</td>
48+<td>12</td>
49+</tr>
50+<tr class="even">
51+<td style="text-align: right;">123</td>
52+<td style="text-align: left;">123</td>
53+<td style="text-align: center;">123</td>
54+<td>123</td>
55+</tr>
56+<tr class="odd">
57+<td style="text-align: right;">1</td>
58+<td style="text-align: left;">1</td>
59+<td style="text-align: center;">1</td>
60+<td>1</td>
61+</tr>
62+</tbody>
63+</table>
64+<p>Simple table indented two spaces:</p>
65+<table>
66+<caption>Demonstration of simple table syntax.</caption>
67+<thead>
68+<tr class="header">
69+<th style="text-align: right;">Right</th>
70+<th style="text-align: left;">Left</th>
71+<th style="text-align: center;">Center</th>
72+<th>Default</th>
73+</tr>
74+</thead>
75+<tbody>
76+<tr class="odd">
77+<td style="text-align: right;">12</td>
78+<td style="text-align: left;">12</td>
79+<td style="text-align: center;">12</td>
80+<td>12</td>
81+</tr>
82+<tr class="even">
83+<td style="text-align: right;">123</td>
84+<td style="text-align: left;">123</td>
85+<td style="text-align: center;">123</td>
86+<td>123</td>
87+</tr>
88+<tr class="odd">
89+<td style="text-align: right;">1</td>
90+<td style="text-align: left;">1</td>
91+<td style="text-align: center;">1</td>
92+<td>1</td>
93+</tr>
94+</tbody>
95+</table>
96+<p>Multiline table with caption:</p>
97+<table style="width:79%;">
98+<caption>Here’s the caption. It may span multiple lines.</caption>
99+<colgroup>
100+<col style="width: 15%" />
101+<col style="width: 13%" />
102+<col style="width: 16%" />
103+<col style="width: 33%" />
104+</colgroup>
105+<thead>
106+<tr class="header">
107+<th style="text-align: center;">Centered Header</th>
108+<th style="text-align: left;">Left Aligned</th>
109+<th style="text-align: right;">Right Aligned</th>
110+<th style="text-align: left;">Default aligned</th>
111+</tr>
112+</thead>
113+<tbody>
114+<tr class="odd">
115+<td style="text-align: center;">First</td>
116+<td style="text-align: left;">row</td>
117+<td style="text-align: right;">12.0</td>
118+<td style="text-align: left;">Example of a row that spans multiple lines.</td>
119+</tr>
120+<tr class="even">
121+<td style="text-align: center;">Second</td>
122+<td style="text-align: left;">row</td>
123+<td style="text-align: right;">5.0</td>
124+<td style="text-align: left;">Here’s another one. Note the blank line between rows.</td>
125+</tr>
126+</tbody>
127+</table>
128+<p>Multiline table without caption:</p>
129+<table style="width:79%;">
130+<colgroup>
131+<col style="width: 15%" />
132+<col style="width: 13%" />
133+<col style="width: 16%" />
134+<col style="width: 33%" />
135+</colgroup>
136+<thead>
137+<tr class="header">
138+<th style="text-align: center;">Centered Header</th>
139+<th style="text-align: left;">Left Aligned</th>
140+<th style="text-align: right;">Right Aligned</th>
141+<th style="text-align: left;">Default aligned</th>
142+</tr>
143+</thead>
144+<tbody>
145+<tr class="odd">
146+<td style="text-align: center;">First</td>
147+<td style="text-align: left;">row</td>
148+<td style="text-align: right;">12.0</td>
149+<td style="text-align: left;">Example of a row that spans multiple lines.</td>
150+</tr>
151+<tr class="even">
152+<td style="text-align: center;">Second</td>
153+<td style="text-align: left;">row</td>
154+<td style="text-align: right;">5.0</td>
155+<td style="text-align: left;">Here’s another one. Note the blank line between rows.</td>
156+</tr>
157+</tbody>
158+</table>
159+<p>Table without column headers:</p>
160+<table>
161+<tbody>
162+<tr class="odd">
163+<td style="text-align: right;">12</td>
164+<td style="text-align: left;">12</td>
165+<td style="text-align: center;">12</td>
166+<td style="text-align: right;">12</td>
167+</tr>
168+<tr class="even">
169+<td style="text-align: right;">123</td>
170+<td style="text-align: left;">123</td>
171+<td style="text-align: center;">123</td>
172+<td style="text-align: right;">123</td>
173+</tr>
174+<tr class="odd">
175+<td style="text-align: right;">1</td>
176+<td style="text-align: left;">1</td>
177+<td style="text-align: center;">1</td>
178+<td style="text-align: right;">1</td>
179+</tr>
180+</tbody>
181+</table>
182+<p>Multiline table without column headers:</p>
183+<table style="width:79%;">
184+<colgroup>
185+<col style="width: 15%" />
186+<col style="width: 13%" />
187+<col style="width: 16%" />
188+<col style="width: 33%" />
189+</colgroup>
190+<tbody>
191+<tr class="odd">
192+<td style="text-align: center;">First</td>
193+<td style="text-align: left;">row</td>
194+<td style="text-align: right;">12.0</td>
195+<td>Example of a row that spans multiple lines.</td>
196+</tr>
197+<tr class="even">
198+<td style="text-align: center;">Second</td>
199+<td style="text-align: left;">row</td>
200+<td style="text-align: right;">5.0</td>
201+<td>Here’s another one. Note the blank line between rows.</td>
202+</tr>
203+</tbody>
204+</table>
tests/tables.icml+5−5
@@ -476,14 +476,14 @@
476476 <Cell Name="3:2" AppliedCellStyle="CellStyle/Cell">
477477 <ParagraphStyleRange AppliedParagraphStyle="ParagraphStyle/TablePar &gt; LeftAlign">
478478 <CharacterStyleRange AppliedCharacterStyle="$ID/NormalCharacterStyle">
479− <Content>Here's another one. Note the blank line between rows.</Content>
479+ <Content>Here’s another one. Note the blank line between rows.</Content>
480480 </CharacterStyleRange>
481481 </ParagraphStyleRange>
482482 </Cell>
483483</Table>
484484<ParagraphStyleRange AppliedParagraphStyle="ParagraphStyle/TableCaption">
485485 <CharacterStyleRange AppliedCharacterStyle="$ID/NormalCharacterStyle">
486− <Content>Here's the caption. It may span multiple lines.</Content>
486+ <Content>Here’s the caption. It may span multiple lines.</Content>
487487 </CharacterStyleRange>
488488</ParagraphStyleRange>
489489<Br />
@@ -578,7 +578,7 @@
578578 <Cell Name="3:2" AppliedCellStyle="CellStyle/Cell">
579579 <ParagraphStyleRange AppliedParagraphStyle="ParagraphStyle/TablePar &gt; LeftAlign">
580580 <CharacterStyleRange AppliedCharacterStyle="$ID/NormalCharacterStyle">
581− <Content>Here's another one. Note the blank line between rows.</Content>
581+ <Content>Here’s another one. Note the blank line between rows.</Content>
582582 </CharacterStyleRange>
583583 </ParagraphStyleRange>
584584 </Cell>
@@ -748,10 +748,10 @@
748748 <Cell Name="3:1" AppliedCellStyle="CellStyle/Cell">
749749 <ParagraphStyleRange AppliedParagraphStyle="ParagraphStyle/TablePar">
750750 <CharacterStyleRange AppliedCharacterStyle="$ID/NormalCharacterStyle">
751− <Content>Here's another one. Note the blank line between rows.</Content>
751+ <Content>Here’s another one. Note the blank line between rows.</Content>
752752 </CharacterStyleRange>
753753 </ParagraphStyleRange>
754754 </Cell>
755755</Table>
756756<ParagraphStyleRange AppliedParagraphStyle="ParagraphStyle/TableCaption">
757−</ParagraphStyleRange>
757+</ParagraphStyleRange>
tests/tables.man+4−4
@@ -135,7 +135,7 @@ T}
135135.PP
136136Multiline table with caption:
137137.PP
138−Here\[aq]s the caption. It may span multiple lines.
138+Here's the caption. It may span multiple lines.
139139.TS
140140tab(@);
141141cw(10.5n) lw(9.6n) rw(11.4n) lw(23.6n).
@@ -165,7 +165,7 @@ row
165165T}@T{
1661665.0
167167T}@T{
168−Here\[aq]s another one.
168+Here's another one.
169169Note the blank line between rows.
170170T}
171171.TE
@@ -201,7 +201,7 @@ row
201201T}@T{
2022025.0
203203T}@T{
204−Here\[aq]s another one.
204+Here's another one.
205205Note the blank line between rows.
206206T}
207207.TE
@@ -261,7 +261,7 @@ row
261261T}@T{
2622625.0
263263T}@T{
264−Here\[aq]s another one.
264+Here's another one.
265265Note the blank line between rows.
266266T}
267267.TE
tests/tables.mediawiki+4−4
@@ -75,7 +75,7 @@ Simple table indented two spaces:
7575Multiline table with caption:
7676
7777{|
78−|+ Here's the caption. It may span multiple lines.
78+|+ Here’s the caption. It may span multiple lines.
7979!align="center" width="15%"| Centered Header
8080!width="13%"| Left Aligned
8181!align="right" width="16%"| Right Aligned
@@ -89,7 +89,7 @@ Multiline table with caption:
8989|align="center"| Second
9090| row
9191|align="right"| 5.0
92−| Here's another one. Note the blank line between rows.
92+| Here’s another one. Note the blank line between rows.
9393|}
9494
9595Multiline table without caption:
@@ -108,7 +108,7 @@ Multiline table without caption:
108108|align="center"| Second
109109| row
110110|align="right"| 5.0
111−| Here's another one. Note the blank line between rows.
111+| Here’s another one. Note the blank line between rows.
112112|}
113113
114114Table without column headers:
@@ -141,6 +141,6 @@ Multiline table without column headers:
141141|align="center"| Second
142142| row
143143|align="right"| 5.0
144−| Here's another one. Note the blank line between rows.
144+| Here’s another one. Note the blank line between rows.
145145|}
146146
tests/tables.native+4−4
@@ -53,7 +53,7 @@
5353 ,[Plain [Str "1"]]
5454 ,[Plain [Str "1"]]]]
5555,Para [Str "Multiline",Space,Str "table",Space,Str "with",Space,Str "caption:"]
56−,Table [Str "Here's",Space,Str "the",Space,Str "caption.",SoftBreak,Str "It",Space,Str "may",Space,Str "span",Space,Str "multiple",Space,Str "lines."] [AlignCenter,AlignLeft,AlignRight,AlignLeft] [0.15,0.1375,0.1625,0.3375]
56+,Table [Str "Here\8217s",Space,Str "the",Space,Str "caption.",SoftBreak,Str "It",Space,Str "may",Space,Str "span",Space,Str "multiple",Space,Str "lines."] [AlignCenter,AlignLeft,AlignRight,AlignLeft] [0.15,0.1375,0.1625,0.3375]
5757 [[Plain [Str "Centered",SoftBreak,Str "Header"]]
5858 ,[Plain [Str "Left",SoftBreak,Str "Aligned"]]
5959 ,[Plain [Str "Right",SoftBreak,Str "Aligned"]]
@@ -65,7 +65,7 @@
6565 ,[[Plain [Str "Second"]]
6666 ,[Plain [Str "row"]]
6767 ,[Plain [Str "5.0"]]
68− ,[Plain [Str "Here's",Space,Str "another",Space,Str "one.",Space,Str "Note",SoftBreak,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",Space,Str "rows."]]]]
68+ ,[Plain [Str "Here\8217s",Space,Str "another",Space,Str "one.",Space,Str "Note",SoftBreak,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",Space,Str "rows."]]]]
6969,Para [Str "Multiline",Space,Str "table",Space,Str "without",Space,Str "caption:"]
7070,Table [] [AlignCenter,AlignLeft,AlignRight,AlignLeft] [0.15,0.1375,0.1625,0.3375]
7171 [[Plain [Str "Centered",SoftBreak,Str "Header"]]
@@ -79,7 +79,7 @@
7979 ,[[Plain [Str "Second"]]
8080 ,[Plain [Str "row"]]
8181 ,[Plain [Str "5.0"]]
82− ,[Plain [Str "Here's",Space,Str "another",Space,Str "one.",Space,Str "Note",SoftBreak,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",Space,Str "rows."]]]]
82+ ,[Plain [Str "Here\8217s",Space,Str "another",Space,Str "one.",Space,Str "Note",SoftBreak,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",Space,Str "rows."]]]]
8383,Para [Str "Table",Space,Str "without",Space,Str "column",Space,Str "headers:"]
8484,Table [] [AlignRight,AlignLeft,AlignCenter,AlignRight] [0.0,0.0,0.0,0.0]
8585 [[]
@@ -111,4 +111,4 @@
111111 ,[[Plain [Str "Second"]]
112112 ,[Plain [Str "row"]]
113113 ,[Plain [Str "5.0"]]
114− ,[Plain [Str "Here's",Space,Str "another",Space,Str "one.",Space,Str "Note",SoftBreak,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",Space,Str "rows."]]]]]
114+ ,[Plain [Str "Here\8217s",Space,Str "another",Space,Str "one.",Space,Str "Note",SoftBreak,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",Space,Str "rows."]]]]]
tests/tables.opendocument+4−4
@@ -246,12 +246,12 @@ caption:</text:p>
246246 <text:p text:style-name="P16">5.0</text:p>
247247 </table:table-cell>
248248 <table:table-cell table:style-name="Table4.A1" office:value-type="string">
249− <text:p text:style-name="Table_20_Contents">Here's another one. Note the
249+ <text:p text:style-name="Table_20_Contents">Here’s another one. Note the
250250 blank line between rows.</text:p>
251251 </table:table-cell>
252252 </table:table-row>
253253</table:table>
254−<text:p text:style-name="Table">Here's the caption. It may span multiple
254+<text:p text:style-name="Table">Here’s the caption. It may span multiple
255255lines.</text:p>
256256<text:p text:style-name="First_20_paragraph">Multiline table without
257257caption:</text:p>
@@ -302,7 +302,7 @@ caption:</text:p>
302302 <text:p text:style-name="P20">5.0</text:p>
303303 </table:table-cell>
304304 <table:table-cell table:style-name="Table5.A1" office:value-type="string">
305− <text:p text:style-name="Table_20_Contents">Here's another one. Note the
305+ <text:p text:style-name="Table_20_Contents">Here’s another one. Note the
306306 blank line between rows.</text:p>
307307 </table:table-cell>
308308 </table:table-row>
@@ -390,7 +390,7 @@ headers:</text:p>
390390 <text:p text:style-name="P30">5.0</text:p>
391391 </table:table-cell>
392392 <table:table-cell table:style-name="Table7.A1" office:value-type="string">
393− <text:p text:style-name="Table_20_Contents">Here's another one. Note the
393+ <text:p text:style-name="Table_20_Contents">Here’s another one. Note the
394394 blank line between rows.</text:p>
395395 </table:table-cell>
396396 </table:table-row>
tests/tables.plain+4−4
@@ -35,12 +35,12 @@ Multiline table with caption:
3535 First row 12.0 Example of a row that
3636 spans multiple lines.
3737
38− Second row 5.0 Here's another one. Note
38+ Second row 5.0 Here’s another one. Note
3939 the blank line between
4040 rows.
4141 --------------------------------------------------------------
4242
43− : Here's the caption. It may span multiple lines.
43+ : Here’s the caption. It may span multiple lines.
4444
4545Multiline table without caption:
4646
@@ -51,7 +51,7 @@ Multiline table without caption:
5151 First row 12.0 Example of a row that
5252 spans multiple lines.
5353
54− Second row 5.0 Here's another one. Note
54+ Second row 5.0 Here’s another one. Note
5555 the blank line between
5656 rows.
5757 --------------------------------------------------------------
@@ -70,7 +70,7 @@ Multiline table without column headers:
7070 First row 12.0 Example of a row that
7171 spans multiple lines.
7272
73− Second row 5.0 Here's another one. Note
73+ Second row 5.0 Here’s another one. Note
7474 the blank line between
7575 rows.
7676 ----------- ---------- ------------ --------------------------
tests/tables.rst+4−4
@@ -47,12 +47,12 @@ Multiline table with caption:
4747| First | row | 12.0 | Example of a row that |
4848| | | | spans multiple lines. |
4949+-------------+------------+--------------+----------------------------+
50−| Second | row | 5.0 | Here's another one. Note |
50+| Second | row | 5.0 | Here’s another one. Note |
5151| | | | the blank line between |
5252| | | | rows. |
5353+-------------+------------+--------------+----------------------------+
5454
55−Table: Here's the caption. It may span multiple lines.
55+Table: Here’s the caption. It may span multiple lines.
5656
5757Multiline table without caption:
5858
@@ -63,7 +63,7 @@ Multiline table without caption:
6363| First | row | 12.0 | Example of a row that |
6464| | | | spans multiple lines. |
6565+-------------+------------+--------------+----------------------------+
66−| Second | row | 5.0 | Here's another one. Note |
66+| Second | row | 5.0 | Here’s another one. Note |
6767| | | | the blank line between |
6868| | | | rows. |
6969+-------------+------------+--------------+----------------------------+
@@ -84,7 +84,7 @@ Multiline table without column headers:
8484| First | row | 12.0 | Example of a row that |
8585| | | | spans multiple lines. |
8686+-------------+------------+--------------+----------------------------+
87−| Second | row | 5.0 | Here's another one. Note |
87+| Second | row | 5.0 | Here’s another one. Note |
8888| | | | the blank line between |
8989| | | | rows. |
9090+-------------+------------+--------------+----------------------------+
tests/tables.rtf+5−4
@@ -226,11 +226,11 @@
226226\cell}
227227{{\pard\intbl \qr \f0 \sa0 \li0 \fi0 5.0\par}
228228\cell}
229−{{\pard\intbl \ql \f0 \sa0 \li0 \fi0 Here's another one. Note the blank line between rows.\par}
229+{{\pard\intbl \ql \f0 \sa0 \li0 \fi0 Here\u8217's another one. Note the blank line between rows.\par}
230230\cell}
231231}
232232\intbl\row}
233−{\pard \ql \f0 \sa180 \li0 \fi0 Here's the caption. It may span multiple lines.\par}
233+{\pard \ql \f0 \sa180 \li0 \fi0 Here\u8217's the caption. It may span multiple lines.\par}
234234{\pard \ql \f0 \sa180 \li0 \fi0 Multiline table without caption:\par}
235235{
236236\trowd \trgaph120
@@ -273,7 +273,7 @@
273273\cell}
274274{{\pard\intbl \qr \f0 \sa0 \li0 \fi0 5.0\par}
275275\cell}
276−{{\pard\intbl \ql \f0 \sa0 \li0 \fi0 Here's another one. Note the blank line between rows.\par}
276+{{\pard\intbl \ql \f0 \sa0 \li0 \fi0 Here\u8217's another one. Note the blank line between rows.\par}
277277\cell}
278278}
279279\intbl\row}
@@ -352,8 +352,9 @@
352352\cell}
353353{{\pard\intbl \qr \f0 \sa0 \li0 \fi0 5.0\par}
354354\cell}
355−{{\pard\intbl \ql \f0 \sa0 \li0 \fi0 Here's another one. Note the blank line between rows.\par}
355+{{\pard\intbl \ql \f0 \sa0 \li0 \fi0 Here\u8217's another one. Note the blank line between rows.\par}
356356\cell}
357357}
358358\intbl\row}
359359{\pard \ql \f0 \sa180 \li0 \fi0 \par}
360+
tests/tables.tei+3−3
@@ -97,7 +97,7 @@
9797 <cell><p>Second</p></cell>
9898 <cell><p>row</p></cell>
9999 <cell><p>5.0</p></cell>
100− <cell><p>Here's another one. Note the blank line between rows.</p></cell>
100+ <cell><p>Here’s another one. Note the blank line between rows.</p></cell>
101101 </row>
102102</table>
103103<p>Multiline table without caption:</p>
@@ -118,7 +118,7 @@
118118 <cell><p>Second</p></cell>
119119 <cell><p>row</p></cell>
120120 <cell><p>5.0</p></cell>
121− <cell><p>Here's another one. Note the blank line between rows.</p></cell>
121+ <cell><p>Here’s another one. Note the blank line between rows.</p></cell>
122122 </row>
123123</table>
124124<p>Table without column headers:</p>
@@ -166,6 +166,6 @@
166166 <cell><p>Second</p></cell>
167167 <cell><p>row</p></cell>
168168 <cell><p>5.0</p></cell>
169− <cell><p>Here's another one. Note the blank line between rows.</p></cell>
169+ <cell><p>Here’s another one. Note the blank line between rows.</p></cell>
170170 </row>
171171</table>
tests/tables.zimwiki+4−4
@@ -26,18 +26,18 @@ Demonstration of simple table syntax.
2626
2727Multiline table with caption:
2828
29−Here's the caption. It may span multiple lines.
29+Here’s the caption. It may span multiple lines.
3030| Centered Header |Left Aligned | Right Aligned|Default aligned |
3131|:-----------------:|:-------------|--------------:|:------------------------------------------------------|
3232| First |row | 12.0|Example of a row that spans multiple lines. |
33−| Second |row | 5.0|Here's another one. Note the blank line between rows. |
33+| Second |row | 5.0|Here’s another one. Note the blank line between rows. |
3434
3535Multiline table without caption:
3636
3737| Centered Header |Left Aligned | Right Aligned|Default aligned |
3838|:-----------------:|:-------------|--------------:|:------------------------------------------------------|
3939| First |row | 12.0|Example of a row that spans multiple lines. |
40−| Second |row | 5.0|Here's another one. Note the blank line between rows. |
40+| Second |row | 5.0|Here’s another one. Note the blank line between rows. |
4141
4242Table without column headers:
4343
@@ -52,5 +52,5 @@ Multiline table without column headers:
5252| First |row | 12.0|Example of a row that spans multiple lines. |
5353|:--------:|:----|-----:|-----------------------------------------------------|
5454| First |row | 12.0|Example of a row that spans multiple lines. |
55−| Second |row | 5.0|Here's another one. Note the blank line between rows.|
55+| Second |row | 5.0|Here’s another one. Note the blank line between rows.|
5656
tests/test-pandoc.hs−2
@@ -26,14 +26,12 @@ import qualified Tests.Writers.Docx
2626import qualified Tests.Writers.RST
2727import qualified Tests.Writers.TEI
2828import qualified Tests.Shared
29−import qualified Tests.Walk
3029import Text.Pandoc.Shared (inDirectory)
3130import System.Environment (getArgs)
3231
3332tests :: [Test]
3433tests = [ testGroup "Old" Tests.Old.tests
3534 , testGroup "Shared" Tests.Shared.tests
36− , testGroup "Walk" Tests.Walk.tests
3735 , testGroup "Writers"
3836 [ testGroup "Native" Tests.Writers.Native.tests
3937 , testGroup "ConTeXt" Tests.Writers.ConTeXt.tests
tests/textile-reader.native+8−8
@@ -1,5 +1,5 @@
11Pandoc (Meta {unMeta = fromList []})
2−[Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "set",Space,Str "of",Space,Str "tests",Space,Str "for",Space,Str "pandoc",Space,Str "Textile",Space,Str "Reader.",Space,Str "Part",Space,Str "of",Space,Str "it",Space,Str "comes",LineBreak,Str "from",Space,Str "John",Space,Str "Gruber's",Space,Str "markdown",Space,Str "test",Space,Str "suite."]
2+[Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "set",Space,Str "of",Space,Str "tests",Space,Str "for",Space,Str "pandoc",Space,Str "Textile",Space,Str "Reader.",Space,Str "Part",Space,Str "of",Space,Str "it",Space,Str "comes",LineBreak,Str "from",Space,Str "John",Space,Str "Gruber\8217s",Space,Str "markdown",Space,Str "test",Space,Str "suite."]
33,HorizontalRule
44,Header 1 ("headers",[],[]) [Str "Headers"]
55,Header 2 ("level-2-with-an-embeded-link",[],[]) [Str "Level",Space,Str "2",Space,Str "with",Space,Str "an",Space,Link ("",[],[]) [Str "embeded",Space,Str "link"] ("http://www.example.com","")]
@@ -8,9 +8,9 @@ Pandoc (Meta {unMeta = fromList []})
88,Header 5 ("level-5",[],[]) [Str "Level",Space,Str "5"]
99,Header 6 ("level-6",[],[]) [Str "Level",Space,Str "6"]
1010,Header 1 ("paragraphs",[],[]) [Str "Paragraphs"]
11−,Para [Str "Here's",Space,Str "a",Space,Str "regular",Space,Str "paragraph."]
11+,Para [Str "Here\8217s",Space,Str "a",Space,Str "regular",Space,Str "paragraph."]
1212,Para [Str "Line",Space,Str "breaks",Space,Str "are",Space,Str "preserved",Space,Str "in",Space,Str "textile,",Space,Str "so",Space,Str "you",Space,Str "can",Space,Str "not",Space,Str "wrap",Space,Str "your",Space,Str "very",LineBreak,Str "long",Space,Str "paragraph",Space,Str "with",Space,Str "your",Space,Str "favourite",Space,Str "text",Space,Str "editor",Space,Str "and",Space,Str "have",Space,Str "it",Space,Str "rendered",LineBreak,Str "with",Space,Str "no",Space,Str "break."]
13−,Para [Str "Here's",Space,Str "one",Space,Str "with",Space,Str "a",Space,Str "bullet."]
13+,Para [Str "Here\8217s",Space,Str "one",Space,Str "with",Space,Str "a",Space,Str "bullet."]
1414,BulletList
1515 [[Plain [Str "criminey."]]]
1616,Para [Str "There",Space,Str "should",Space,Str "be",Space,Str "a",Space,Str "paragraph",Space,Str "break",Space,Str "between",Space,Str "here"]
@@ -89,14 +89,14 @@ Pandoc (Meta {unMeta = fromList []})
8989,Para [Str "This",Space,Str "is",Space,Emph [Str "emphasized"],Str ",",Space,Str "and",Space,Str "so",Space,Emph [Str "is",Space,Str "this"],Str ".",LineBreak,Str "This",Space,Str "is",Space,Strong [Str "strong"],Str ",",Space,Str "and",Space,Str "so",Space,Strong [Str "is",Space,Str "this"],Str ".",LineBreak,Str "Hyphenated-words-are-ok,",Space,Str "as",Space,Str "well",Space,Str "as",Space,Str "strange_underscore_notation.",LineBreak,Str "A",Space,Link ("",[],[]) [Strong [Str "strong",Space,Str "link"]] ("http://www.foobar.com",""),Str "."]
9090,Para [Emph [Strong [Str "This",Space,Str "is",Space,Str "strong",Space,Str "and",Space,Str "em."]],LineBreak,Str "So",Space,Str "is",Space,Strong [Emph [Str "this"]],Space,Str "word",Space,Str "and",Space,Emph [Strong [Str "that",Space,Str "one"]],Str ".",LineBreak,Strikeout [Str "This",Space,Str "is",Space,Str "strikeout",Space,Str "and",Space,Strong [Str "strong"]]]
9191,Para [Str "Superscripts:",Space,Str "a",Superscript [Str "bc"],Str "d",Space,Str "a",Space,Superscript [Strong [Str "hello"]],Space,Str "a",Superscript [Str "hello",Space,Str "there"],Str ".",LineBreak,Str "Subscripts:",Space,Subscript [Str "here"],Space,Str "H",Space,Subscript [Str "2"],Str "O,",Space,Str "H",Space,Subscript [Str "23"],Str "O,",Space,Str "H",Space,Subscript [Str "many",Space,Str "of",Space,Str "them"],Str "O."]
92−,Para [Str "Dashes",Space,Str ":",Space,Str "How",Space,Str "cool",Space,Str "--",Space,Str "automatic",Space,Str "dashes."]
93−,Para [Str "Elipses",Space,Str ":",Space,Str "He",Space,Str "thought",Space,Str "and",Space,Str "thought",Space,Str "...",Space,Str "and",Space,Str "then",Space,Str "thought",Space,Str "some",Space,Str "more."]
94−,Para [Str "Quotes",Space,Str "and",Space,Str "apostrophes",Space,Str ":",Space,Str "\"I'd",Space,Str "like",Space,Str "to",Space,Str "thank",Space,Str "you\"",Space,Str "for",Space,Str "example."]
92+,Para [Str "Dashes",Space,Str ":",Space,Str "How",Space,Str "cool",Space,Str "\8212",Space,Str "automatic",Space,Str "dashes."]
93+,Para [Str "Elipses",Space,Str ":",Space,Str "He",Space,Str "thought",Space,Str "and",Space,Str "thought",Space,Str "\8230",Space,Str "and",Space,Str "then",Space,Str "thought",Space,Str "some",Space,Str "more."]
94+,Para [Str "Quotes",Space,Str "and",Space,Str "apostrophes",Space,Str ":",Space,Quoted DoubleQuote [Str "I\8217d",Space,Str "like",Space,Str "to",Space,Str "thank",Space,Str "you"],Space,Str "for",Space,Str "example."]
9595,Header 1 ("links",[],[]) [Str "Links"]
9696,Header 2 ("explicit",[],[]) [Str "Explicit"]
9797,Para [Str "Just",Space,Str "a",Space,Link ("",[],[]) [Str "url"] ("http://www.url.com","")]
9898,Para [Link ("",[],[]) [Str "Email",Space,Str "link"] ("mailto:nobody@nowhere.net","")]
99−,Para [Str "\"not",Space,Str "a",Space,Str "link\":",Space,Str "foo"]
99+,Para [Quoted DoubleQuote [Str "not",Space,Str "a",Space,Str "link"],Str ":",Space,Str "foo"]
100100,Para [Str "Automatic",Space,Str "linking",Space,Str "to",Space,Link ("",[],[]) [Str "http://www.example.com"] ("http://www.example.com",""),Str "."]
101101,Para [Link ("",[],[]) [Str "Example"] ("http://www.example.com/",""),Str ":",Space,Str "Example",Space,Str "of",Space,Str "a",Space,Str "link",Space,Str "followed",Space,Str "by",Space,Str "a",Space,Str "colon."]
102102,Para [Str "A",Space,Str "link",Link ("",[],[]) [Str "with",Space,Str "brackets"] ("http://www.example.com",""),Str "and",Space,Str "no",Space,Str "spaces."]
@@ -117,7 +117,7 @@ Pandoc (Meta {unMeta = fromList []})
117117 ,[[Plain [Str "bella"]]
118118 ,[Plain [Str "45"]]
119119 ,[Plain [Str "f"]]]]
120−,Para [Str "and",Space,Str "some",Space,Str "text",Space,Str "following",Space,Str "..."]
120+,Para [Str "and",Space,Str "some",Space,Str "text",Space,Str "following",Space,Str "\8230"]
121121,Header 2 ("with-headers",[],[]) [Str "With",Space,Str "headers"]
122122,Table [] [AlignDefault,AlignDefault,AlignDefault] [0.0,0.0,0.0]
123123 [[Plain [Str "name"]]
tests/writer.docbook → tests/writer.docbook4renamed

The content is the same.

tests/writer.html → tests/writer.html4renamed

The content is the same.

tests/writer.html5new file+548
@@ -0,0 +1,548 @@
1+<!DOCTYPE html>
2+<html>
3+<head>
4+ <meta charset="utf-8">
5+ <meta name="generator" content="pandoc">
6+ <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
7+ <meta name="author" content="John MacFarlane">
8+ <meta name="author" content="Anonymous">
9+ <meta name="dcterms.date" content="2006-07-17">
10+ <title>Pandoc Test Suite</title>
11+ <style type="text/css">code{white-space: pre;}</style>
12+ <!--[if lt IE 9]>
13+ <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
14+ <![endif]-->
15+</head>
16+<body>
17+<header>
18+<h1 class="title">Pandoc Test Suite</h1>
19+<p class="author">John MacFarlane</p>
20+<p class="author">Anonymous</p>
21+<p class="date">July 17, 2006</p>
22+</header>
23+<p>This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite.</p>
24+<hr />
25+<h1 id="headers">Headers</h1>
26+<h2 id="level-2-with-an-embedded-link">Level 2 with an <a href="/url">embedded link</a></h2>
27+<h3 id="level-3-with-emphasis">Level 3 with <em>emphasis</em></h3>
28+<h4 id="level-4">Level 4</h4>
29+<h5 id="level-5">Level 5</h5>
30+<h1 id="level-1">Level 1</h1>
31+<h2 id="level-2-with-emphasis">Level 2 with <em>emphasis</em></h2>
32+<h3 id="level-3">Level 3</h3>
33+<p>with no blank line</p>
34+<h2 id="level-2">Level 2</h2>
35+<p>with no blank line</p>
36+<hr />
37+<h1 id="paragraphs">Paragraphs</h1>
38+<p>Here’s a regular paragraph.</p>
39+<p>In Markdown 1.0.0 and earlier. Version 8. This line turns into a list item. Because a hard-wrapped line in the middle of a paragraph looked like a list item.</p>
40+<p>Here’s one with a bullet. * criminey.</p>
41+<p>There should be a hard line break<br />
42+here.</p>
43+<hr />
44+<h1 id="block-quotes">Block Quotes</h1>
45+<p>E-mail style:</p>
46+<blockquote>
47+<p>This is a block quote. It is pretty short.</p>
48+</blockquote>
49+<blockquote>
50+<p>Code in a block quote:</p>
51+<pre><code>sub status {
52+ print &quot;working&quot;;
53+}</code></pre>
54+<p>A list:</p>
55+<ol type="1">
56+<li>item one</li>
57+<li>item two</li>
58+</ol>
59+<p>Nested block quotes:</p>
60+<blockquote>
61+<p>nested</p>
62+</blockquote>
63+<blockquote>
64+<p>nested</p>
65+</blockquote>
66+</blockquote>
67+<p>This should not be a block quote: 2 &gt; 1.</p>
68+<p>And a following paragraph.</p>
69+<hr />
70+<h1 id="code-blocks">Code Blocks</h1>
71+<p>Code:</p>
72+<pre><code>---- (should be four hyphens)
73+
74+sub status {
75+ print &quot;working&quot;;
76+}
77+
78+this code block is indented by one tab</code></pre>
79+<p>And:</p>
80+<pre><code> this code block is indented by two tabs
81+
82+These should not be escaped: \$ \\ \&gt; \[ \{</code></pre>
83+<hr />
84+<h1 id="lists">Lists</h1>
85+<h2 id="unordered">Unordered</h2>
86+<p>Asterisks tight:</p>
87+<ul>
88+<li>asterisk 1</li>
89+<li>asterisk 2</li>
90+<li>asterisk 3</li>
91+</ul>
92+<p>Asterisks loose:</p>
93+<ul>
94+<li><p>asterisk 1</p></li>
95+<li><p>asterisk 2</p></li>
96+<li><p>asterisk 3</p></li>
97+</ul>
98+<p>Pluses tight:</p>
99+<ul>
100+<li>Plus 1</li>
101+<li>Plus 2</li>
102+<li>Plus 3</li>
103+</ul>
104+<p>Pluses loose:</p>
105+<ul>
106+<li><p>Plus 1</p></li>
107+<li><p>Plus 2</p></li>
108+<li><p>Plus 3</p></li>
109+</ul>
110+<p>Minuses tight:</p>
111+<ul>
112+<li>Minus 1</li>
113+<li>Minus 2</li>
114+<li>Minus 3</li>
115+</ul>
116+<p>Minuses loose:</p>
117+<ul>
118+<li><p>Minus 1</p></li>
119+<li><p>Minus 2</p></li>
120+<li><p>Minus 3</p></li>
121+</ul>
122+<h2 id="ordered">Ordered</h2>
123+<p>Tight:</p>
124+<ol type="1">
125+<li>First</li>
126+<li>Second</li>
127+<li>Third</li>
128+</ol>
129+<p>and:</p>
130+<ol type="1">
131+<li>One</li>
132+<li>Two</li>
133+<li>Three</li>
134+</ol>
135+<p>Loose using tabs:</p>
136+<ol type="1">
137+<li><p>First</p></li>
138+<li><p>Second</p></li>
139+<li><p>Third</p></li>
140+</ol>
141+<p>and using spaces:</p>
142+<ol type="1">
143+<li><p>One</p></li>
144+<li><p>Two</p></li>
145+<li><p>Three</p></li>
146+</ol>
147+<p>Multiple paragraphs:</p>
148+<ol type="1">
149+<li><p>Item 1, graf one.</p>
150+<p>Item 1. graf two. The quick brown fox jumped over the lazy dog’s back.</p></li>
151+<li><p>Item 2.</p></li>
152+<li><p>Item 3.</p></li>
153+</ol>
154+<h2 id="nested">Nested</h2>
155+<ul>
156+<li>Tab
157+<ul>
158+<li>Tab
159+<ul>
160+<li>Tab</li>
161+</ul></li>
162+</ul></li>
163+</ul>
164+<p>Here’s another:</p>
165+<ol type="1">
166+<li>First</li>
167+<li>Second:
168+<ul>
169+<li>Fee</li>
170+<li>Fie</li>
171+<li>Foe</li>
172+</ul></li>
173+<li>Third</li>
174+</ol>
175+<p>Same thing but with paragraphs:</p>
176+<ol type="1">
177+<li><p>First</p></li>
178+<li><p>Second:</p>
179+<ul>
180+<li>Fee</li>
181+<li>Fie</li>
182+<li>Foe</li>
183+</ul></li>
184+<li><p>Third</p></li>
185+</ol>
186+<h2 id="tabs-and-spaces">Tabs and spaces</h2>
187+<ul>
188+<li><p>this is a list item indented with tabs</p></li>
189+<li><p>this is a list item indented with spaces</p>
190+<ul>
191+<li><p>this is an example list item indented with tabs</p></li>
192+<li><p>this is an example list item indented with spaces</p></li>
193+</ul></li>
194+</ul>
195+<h2 id="fancy-list-markers">Fancy list markers</h2>
196+<ol start="2" type="1">
197+<li>begins with 2</li>
198+<li><p>and now 3</p>
199+<p>with a continuation</p>
200+<ol start="4" type="i">
201+<li>sublist with roman numerals, starting with 4</li>
202+<li>more items
203+<ol type="A">
204+<li>a subsublist</li>
205+<li>a subsublist</li>
206+</ol></li>
207+</ol></li>
208+</ol>
209+<p>Nesting:</p>
210+<ol type="A">
211+<li>Upper Alpha
212+<ol type="I">
213+<li>Upper Roman.
214+<ol start="6" type="1">
215+<li>Decimal start with 6
216+<ol start="3" type="a">
217+<li>Lower alpha with paren</li>
218+</ol></li>
219+</ol></li>
220+</ol></li>
221+</ol>
222+<p>Autonumbering:</p>
223+<ol>
224+<li>Autonumber.</li>
225+<li>More.
226+<ol>
227+<li>Nested.</li>
228+</ol></li>
229+</ol>
230+<p>Should not be a list item:</p>
231+<p>M.A. 2007</p>
232+<p>B. Williams</p>
233+<hr />
234+<h1 id="definition-lists">Definition Lists</h1>
235+<p>Tight using spaces:</p>
236+<dl>
237+<dt>apple</dt>
238+<dd>red fruit
239+</dd>
240+<dt>orange</dt>
241+<dd>orange fruit
242+</dd>
243+<dt>banana</dt>
244+<dd>yellow fruit
245+</dd>
246+</dl>
247+<p>Tight using tabs:</p>
248+<dl>
249+<dt>apple</dt>
250+<dd>red fruit
251+</dd>
252+<dt>orange</dt>
253+<dd>orange fruit
254+</dd>
255+<dt>banana</dt>
256+<dd>yellow fruit
257+</dd>
258+</dl>
259+<p>Loose:</p>
260+<dl>
261+<dt>apple</dt>
262+<dd><p>red fruit</p>
263+</dd>
264+<dt>orange</dt>
265+<dd><p>orange fruit</p>
266+</dd>
267+<dt>banana</dt>
268+<dd><p>yellow fruit</p>
269+</dd>
270+</dl>
271+<p>Multiple blocks with italics:</p>
272+<dl>
273+<dt><em>apple</em></dt>
274+<dd><p>red fruit</p>
275+<p>contains seeds, crisp, pleasant to taste</p>
276+</dd>
277+<dt><em>orange</em></dt>
278+<dd><p>orange fruit</p>
279+<pre><code>{ orange code block }</code></pre>
280+<blockquote>
281+<p>orange block quote</p>
282+</blockquote>
283+</dd>
284+</dl>
285+<p>Multiple definitions, tight:</p>
286+<dl>
287+<dt>apple</dt>
288+<dd>red fruit
289+</dd>
290+<dd>computer
291+</dd>
292+<dt>orange</dt>
293+<dd>orange fruit
294+</dd>
295+<dd>bank
296+</dd>
297+</dl>
298+<p>Multiple definitions, loose:</p>
299+<dl>
300+<dt>apple</dt>
301+<dd><p>red fruit</p>
302+</dd>
303+<dd><p>computer</p>
304+</dd>
305+<dt>orange</dt>
306+<dd><p>orange fruit</p>
307+</dd>
308+<dd><p>bank</p>
309+</dd>
310+</dl>
311+<p>Blank line after term, indented marker, alternate markers:</p>
312+<dl>
313+<dt>apple</dt>
314+<dd><p>red fruit</p>
315+</dd>
316+<dd><p>computer</p>
317+</dd>
318+<dt>orange</dt>
319+<dd><p>orange fruit</p>
320+<ol type="1">
321+<li>sublist</li>
322+<li>sublist</li>
323+</ol>
324+</dd>
325+</dl>
326+<h1 id="html-blocks">HTML Blocks</h1>
327+<p>Simple block on one line:</p>
328+<div>
329+foo
330+</div>
331+<p>And nested without indentation:</p>
332+<div>
333+<div>
334+<div>
335+<p>foo</p>
336+</div>
337+</div>
338+<div>
339+bar
340+</div>
341+</div>
342+<p>Interpreted markdown in a table:</p>
343+<table>
344+<tr>
345+<td>
346+This is <em>emphasized</em>
347+</td>
348+<td>
349+And this is <strong>strong</strong>
350+</td>
351+</tr>
352+</table>
353+<script type="text/javascript">document.write('This *should not* be interpreted as markdown');</script>
354+<p>Here’s a simple block:</p>
355+<div>
356+<p>foo</p>
357+</div>
358+<p>This should be a code block, though:</p>
359+<pre><code>&lt;div&gt;
360+ foo
361+&lt;/div&gt;</code></pre>
362+<p>As should this:</p>
363+<pre><code>&lt;div&gt;foo&lt;/div&gt;</code></pre>
364+<p>Now, nested:</p>
365+<div>
366+<div>
367+<div>
368+foo
369+</div>
370+</div>
371+</div>
372+<p>This should just be an HTML comment:</p>
373+<!-- Comment -->
374+<p>Multiline:</p>
375+<!--
376+Blah
377+Blah
378+-->
379+<!--
380+ This is another comment.
381+-->
382+<p>Code block:</p>
383+<pre><code>&lt;!-- Comment --&gt;</code></pre>
384+<p>Just plain comment, with trailing spaces on the line:</p>
385+<!-- foo -->
386+<p>Code:</p>
387+<pre><code>&lt;hr /&gt;</code></pre>
388+<p>Hr’s:</p>
389+<hr>
390+<hr />
391+<hr />
392+<hr>
393+<hr />
394+<hr />
395+<hr class="foo" id="bar" />
396+<hr class="foo" id="bar" />
397+<hr class="foo" id="bar">
398+<hr />
399+<h1 id="inline-markup">Inline Markup</h1>
400+<p>This is <em>emphasized</em>, and so <em>is this</em>.</p>
401+<p>This is <strong>strong</strong>, and so <strong>is this</strong>.</p>
402+<p>An <em><a href="/url">emphasized link</a></em>.</p>
403+<p><strong><em>This is strong and em.</em></strong></p>
404+<p>So is <strong><em>this</em></strong> word.</p>
405+<p><strong><em>This is strong and em.</em></strong></p>
406+<p>So is <strong><em>this</em></strong> word.</p>
407+<p>This is code: <code>&gt;</code>, <code>$</code>, <code>\</code>, <code>\$</code>, <code>&lt;html&gt;</code>.</p>
408+<p><del>This is <em>strikeout</em>.</del></p>
409+<p>Superscripts: a<sup>bc</sup>d a<sup><em>hello</em></sup> a<sup>hello there</sup>.</p>
410+<p>Subscripts: H<sub>2</sub>O, H<sub>23</sub>O, H<sub>many of them</sub>O.</p>
411+<p>These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d.</p>
412+<hr />
413+<h1 id="smart-quotes-ellipses-dashes">Smart quotes, ellipses, dashes</h1>
414+<p>“Hello,” said the spider. “‘Shelob’ is my name.”</p>
415+<p>‘A’, ‘B’, and ‘C’ are letters.</p>
416+<p>‘Oak,’ ‘elm,’ and ‘beech’ are names of trees. So is ‘pine.’</p>
417+<p>‘He said, “I want to go.”’ Were you alive in the 70’s?</p>
418+<p>Here is some quoted ‘<code>code</code>’ and a “<a href="http://example.com/?foo=1&amp;bar=2">quoted link</a>”.</p>
419+<p>Some dashes: one—two — three—four — five.</p>
420+<p>Dashes between numbers: 5–7, 255–66, 1987–1999.</p>
421+<p>Ellipses…and…and….</p>
422+<hr />
423+<h1 id="latex">LaTeX</h1>
424+<ul>
425+<li></li>
426+<li><span class="math inline">2 + 2 = 4</span></li>
427+<li><span class="math inline"><em>x</em> ∈ <em>y</em></span></li>
428+<li><span class="math inline"><em>α</em> ∧ <em>ω</em></span></li>
429+<li><span class="math inline">223</span></li>
430+<li><span class="math inline"><em>p</em></span>-Tree</li>
431+<li>Here’s some display math: <br /><span class="math display">$$\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$$</span><br /></li>
432+<li>Here’s one that has a line break in it: <span class="math inline"><em>α</em> + <em>ω</em> × <em>x</em><sup>2</sup></span>.</li>
433+</ul>
434+<p>These shouldn’t be math:</p>
435+<ul>
436+<li>To get the famous equation, write <code>$e = mc^2$</code>.</li>
437+<li>$22,000 is a <em>lot</em> of money. So is $34,000. (It worked if “lot” is emphasized.)</li>
438+<li>Shoes ($20) and socks ($5).</li>
439+<li>Escaped <code>$</code>: $73 <em>this should be emphasized</em> 23$.</li>
440+</ul>
441+<p>Here’s a LaTeX table:</p>
442+
443+<hr />
444+<h1 id="special-characters">Special Characters</h1>
445+<p>Here is some unicode:</p>
446+<ul>
447+<li>I hat: Î</li>
448+<li>o umlaut: ö</li>
449+<li>section: §</li>
450+<li>set membership: ∈</li>
451+<li>copyright: ©</li>
452+</ul>
453+<p>AT&amp;T has an ampersand in their name.</p>
454+<p>AT&amp;T is another way to write it.</p>
455+<p>This &amp; that.</p>
456+<p>4 &lt; 5.</p>
457+<p>6 &gt; 5.</p>
458+<p>Backslash: \</p>
459+<p>Backtick: `</p>
460+<p>Asterisk: *</p>
461+<p>Underscore: _</p>
462+<p>Left brace: {</p>
463+<p>Right brace: }</p>
464+<p>Left bracket: [</p>
465+<p>Right bracket: ]</p>
466+<p>Left paren: (</p>
467+<p>Right paren: )</p>
468+<p>Greater-than: &gt;</p>
469+<p>Hash: #</p>
470+<p>Period: .</p>
471+<p>Bang: !</p>
472+<p>Plus: +</p>
473+<p>Minus: -</p>
474+<hr />
475+<h1 id="links">Links</h1>
476+<h2 id="explicit">Explicit</h2>
477+<p>Just a <a href="/url/">URL</a>.</p>
478+<p><a href="/url/" title="title">URL and title</a>.</p>
479+<p><a href="/url/" title="title preceded by two spaces">URL and title</a>.</p>
480+<p><a href="/url/" title="title preceded by a tab">URL and title</a>.</p>
481+<p><a href="/url/" title="title with &quot;quotes&quot; in it">URL and title</a></p>
482+<p><a href="/url/" title="title with single quotes">URL and title</a></p>
483+<p><a href="/url/with_underscore">with_underscore</a></p>
484+<p><a href="mailto:nobody@nowhere.net">Email link</a></p>
485+<p><a href="">Empty</a>.</p>
486+<h2 id="reference">Reference</h2>
487+<p>Foo <a href="/url/">bar</a>.</p>
488+<p>Foo <a href="/url/">bar</a>.</p>
489+<p>Foo <a href="/url/">bar</a>.</p>
490+<p>With <a href="/url/">embedded [brackets]</a>.</p>
491+<p><a href="/url/">b</a> by itself should be a link.</p>
492+<p>Indented <a href="/url">once</a>.</p>
493+<p>Indented <a href="/url">twice</a>.</p>
494+<p>Indented <a href="/url">thrice</a>.</p>
495+<p>This should [not][] be a link.</p>
496+<pre><code>[not]: /url</code></pre>
497+<p>Foo <a href="/url/" title="Title with &quot;quotes&quot; inside">bar</a>.</p>
498+<p>Foo <a href="/url/" title="Title with &quot;quote&quot; inside">biz</a>.</p>
499+<h2 id="with-ampersands">With ampersands</h2>
500+<p>Here’s a <a href="http://example.com/?foo=1&amp;bar=2">link with an ampersand in the URL</a>.</p>
501+<p>Here’s a link with an amersand in the link text: <a href="http://att.com/" title="AT&amp;T">AT&amp;T</a>.</p>
502+<p>Here’s an <a href="/script?foo=1&amp;bar=2">inline link</a>.</p>
503+<p>Here’s an <a href="/script?foo=1&amp;bar=2">inline link in pointy braces</a>.</p>
504+<h2 id="autolinks">Autolinks</h2>
505+<p>With an ampersand: <a href="http://example.com/?foo=1&amp;bar=2" class="uri">http://example.com/?foo=1&amp;bar=2</a></p>
506+<ul>
507+<li>In a list?</li>
508+<li><a href="http://example.com/" class="uri">http://example.com/</a></li>
509+<li>It should.</li>
510+</ul>
511+<p>An e-mail address: <a href="mailto:nobody@nowhere.net">nobody@nowhere.net</a></p>
512+<blockquote>
513+<p>Blockquoted: <a href="http://example.com/" class="uri">http://example.com/</a></p>
514+</blockquote>
515+<p>Auto-links should not occur here: <code>&lt;http://example.com/&gt;</code></p>
516+<pre><code>or here: &lt;http://example.com/&gt;</code></pre>
517+<hr />
518+<h1 id="images">Images</h1>
519+<p>From “Voyage dans la Lune” by Georges Melies (1902):</p>
520+<figure>
521+<img src="lalune.jpg" title="Voyage dans la Lune" alt="lalune" /><figcaption>lalune</figcaption>
522+</figure>
523+<p>Here is a movie <img src="movie.jpg" alt="movie" /> icon.</p>
524+<hr />
525+<h1 id="footnotes">Footnotes</h1>
526+<p>Here is a footnote reference,<a href="#fn1" class="footnoteRef" id="fnref1"><sup>1</sup></a> and another.<a href="#fn2" class="footnoteRef" id="fnref2"><sup>2</sup></a> This should <em>not</em> be a footnote reference, because it contains a space.[^my note] Here is an inline note.<a href="#fn3" class="footnoteRef" id="fnref3"><sup>3</sup></a></p>
527+<blockquote>
528+<p>Notes can go in quotes.<a href="#fn4" class="footnoteRef" id="fnref4"><sup>4</sup></a></p>
529+</blockquote>
530+<ol type="1">
531+<li>And in list items.<a href="#fn5" class="footnoteRef" id="fnref5"><sup>5</sup></a></li>
532+</ol>
533+<p>This paragraph should not be part of the note, as it is not indented.</p>
534+<section class="footnotes">
535+<hr />
536+<ol>
537+<li id="fn1"><p>Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document.<a href="#fnref1">↩</a></p></li>
538+<li id="fn2"><p>Here’s the long note. This one contains multiple blocks.</p>
539+<p>Subsequent blocks are indented to show that they belong to the footnote (as with list items).</p>
540+<pre><code> { &lt;code&gt; }</code></pre>
541+<p>If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.<a href="#fnref2">↩</a></p></li>
542+<li id="fn3"><p>This is <em>easier</em> to type. Inline notes may contain <a href="http://google.com">links</a> and <code>]</code> verbatim characters, as well as [bracketed text].<a href="#fnref3">↩</a></p></li>
543+<li id="fn4"><p>In quote.<a href="#fnref4">↩</a></p></li>
544+<li id="fn5"><p>In list.<a href="#fnref5">↩</a></p></li>
545+</ol>
546+</section>
547+</body>
548+</html>
tests/writer.markdown+27−27
@@ -6,7 +6,7 @@ date: 'July 17, 2006'
66title: Pandoc Test Suite
77---
88
9−This is a set of tests for pandoc. Most of them are adapted from John Gruber’s
9+This is a set of tests for pandoc. Most of them are adapted from John Gruber's
1010markdown test suite.
1111
1212------------------------------------------------------------------------------
@@ -43,13 +43,13 @@ with no blank line
4343Paragraphs
4444==========
4545
46−Here’s a regular paragraph.
46+Here's a regular paragraph.
4747
4848In Markdown 1.0.0 and earlier. Version 8. This line turns into a list item.
4949Because a hard-wrapped line in the middle of a paragraph looked like a list
5050item.
5151
52−Here’s one with a bullet. \* criminey.
52+Here's one with a bullet. \* criminey.
5353
5454There should be a hard line break\
5555here.
@@ -190,7 +190,7 @@ Multiple paragraphs:
190190
1911911. Item 1, graf one.
192192
193− Item 1. graf two. The quick brown fox jumped over the lazy dog’s back.
193+ Item 1. graf two. The quick brown fox jumped over the lazy dog's back.
194194
1951952. Item 2.
196196
@@ -203,7 +203,7 @@ Nested
203203 - Tab
204204 - Tab
205205
206−Here’s another:
206+Here's another:
207207
2082081. First
2092092. Second:
@@ -409,7 +409,7 @@ And this is **strong**
409409</tr>
410410</table>
411411<script type="text/javascript">document.write('This *should not* be interpreted as markdown');</script>
412−Here’s a simple block:
412+Here's a simple block:
413413
414414<div>
415415
@@ -466,7 +466,7 @@ Code:
466466
467467 <hr />
468468
469−Hr’s:
469+Hr's:
470470
471471<hr>
472472<hr />
@@ -513,22 +513,22 @@ spaces: a\^b c\^d, a\~b c\~d.
513513Smart quotes, ellipses, dashes
514514==============================
515515
516−“Hello,” said the spider. “‘Shelob’ is my name.”
516+"Hello," said the spider. "'Shelob' is my name."
517517
518−‘A’, ‘B’, and ‘C’ are letters.
518+'A', 'B', and 'C' are letters.
519519
520−‘Oak,’ ‘elm,’ and ‘beech’ are names of trees. So is ‘pine.’
520+'Oak,' 'elm,' and 'beech' are names of trees. So is 'pine.'
521521
522−‘He said, “I want to go.”’ Were you alive in the 70’s?
522+'He said, "I want to go."' Were you alive in the 70's?
523523
524−Here is some quoted ‘`code`’ and a “[quoted
525−link](http://example.com/?foo=1&bar=2)”.
524+Here is some quoted '`code`' and a "[quoted
525+link](http://example.com/?foo=1&bar=2)".
526526
527−Some dashes: one—two — three—four — five.
527+Some dashes: one---two --- three---four --- five.
528528
529−Dashes between numbers: 5–7, 255–66, 1987–1999.
529+Dashes between numbers: 5--7, 255--66, 1987--1999.
530530
531−Ellipses…and…and….
531+Ellipses...and...and....
532532
533533------------------------------------------------------------------------------
534534
@@ -541,19 +541,19 @@ LaTeX
541541- $\alpha \wedge \omega$
542542- $223$
543543- $p$-Tree
544−- Here’s some display math:
544+- Here's some display math:
545545 $$\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$$
546−- Here’s one that has a line break in it: $\alpha + \omega \times x^2$.
546+- Here's one that has a line break in it: $\alpha + \omega \times x^2$.
547547
548−These shouldn’t be math:
548+These shouldn't be math:
549549
550550- To get the famous equation, write `$e = mc^2$`.
551−- \$22,000 is a *lot* of money. So is \$34,000. (It worked if “lot” is
551+- \$22,000 is a *lot* of money. So is \$34,000. (It worked if "lot" is
552552 emphasized.)
553553- Shoes (\$20) and socks (\$5).
554554- Escaped `$`: \$73 *this should be emphasized* 23\$.
555555
556−Here’s a LaTeX table:
556+Here's a LaTeX table:
557557
558558\begin{tabular}{|l|l|}\hline
559559Animal & Number \\ \hline
@@ -672,14 +672,14 @@ Foo [biz](/url/ "Title with "quote" inside").
672672With ampersands
673673---------------
674674
675−Here’s a [link with an ampersand in the URL](http://example.com/?foo=1&bar=2).
675+Here's a [link with an ampersand in the URL](http://example.com/?foo=1&bar=2).
676676
677−Here’s a link with an amersand in the link text:
677+Here's a link with an amersand in the link text:
678678[AT&T](http://att.com/ "AT&T").
679679
680−Here’s an [inline link](/script?foo=1&bar=2).
680+Here's an [inline link](/script?foo=1&bar=2).
681681
682−Here’s an [inline link in pointy braces](/script?foo=1&bar=2).
682+Here's an [inline link in pointy braces](/script?foo=1&bar=2).
683683
684684Autolinks
685685---------
@@ -703,7 +703,7 @@ Auto-links should not occur here: `<http://example.com/>`
703703Images
704704======
705705
706−From “Voyage dans la Lune” by Georges Melies (1902):
706+From "Voyage dans la Lune" by Georges Melies (1902):
707707
708708![lalune](lalune.jpg "Voyage dans la Lune")
709709
@@ -727,7 +727,7 @@ This paragraph should not be part of the note, as it is not indented.
727727[^1]: Here is the footnote. It can go anywhere after the footnote reference.
728728 It need not be placed at the end of the document.
729729
730−[^2]: Here’s the long note. This one contains multiple blocks.
730+[^2]: Here's the long note. This one contains multiple blocks.
731731
732732 Subsequent blocks are indented to show that they belong to the footnote
733733 (as with list items).
tests/writer.opml+7−7
@@ -24,7 +24,7 @@
2424 <outline text="Level 2" _note="with no blank line&#10;&#10;------------------------------------------------------------------------">
2525 </outline>
2626</outline>
27−<outline text="Paragraphs" _note="Here’s a regular paragraph.&#10;&#10;In Markdown 1.0.0 and earlier. Version 8. This line turns into a list&#10;item. Because a hard-wrapped line in the middle of a paragraph looked&#10;like a list item.&#10;&#10;Here’s one with a bullet. \* criminey.&#10;&#10;There should be a hard line break\&#10;here.&#10;&#10;------------------------------------------------------------------------">
27+<outline text="Paragraphs" _note="Here’s a regular paragraph.&#10;&#10;In Markdown 1.0.0 and earlier. Version 8. This line turns into a list&#10;item. Because a hard-wrapped line in the middle of a paragraph looked&#10;like a list item.&#10;&#10;Here’s one with a bullet. \* criminey.&#10;&#10;There should be a hard line break &#10;here.&#10;&#10;------------------------------------------------------------------------">
2828</outline>
2929<outline text="Block Quotes" _note="E-mail style:&#10;&#10;&gt; This is a block quote. It is pretty short.&#10;&#10;&gt; Code in a block quote:&#10;&gt;&#10;&gt; sub status {&#10;&gt; print &quot;working&quot;;&#10;&gt; }&#10;&gt;&#10;&gt; A list:&#10;&gt;&#10;&gt; 1. item one&#10;&gt; 2. item two&#10;&gt;&#10;&gt; Nested block quotes:&#10;&gt;&#10;&gt; &gt; nested&#10;&gt;&#10;&gt; &gt; nested&#10;&#10;This should not be a block quote: 2 &amp;gt; 1.&#10;&#10;And a following paragraph.&#10;&#10;------------------------------------------------------------------------">
3030</outline>
@@ -39,18 +39,18 @@
3939 </outline>
4040 <outline text="Tabs and spaces" _note="- this is a list item indented with tabs&#10;&#10;- this is a list item indented with spaces&#10;&#10; - this is an example list item indented with tabs&#10;&#10; - this is an example list item indented with spaces&#10;&#10;">
4141 </outline>
42− <outline text="Fancy list markers" _note="(2) begins with 2&#10;(3) and now 3&#10;&#10; with a continuation&#10;&#10; iv. sublist with roman numerals, starting with 4&#10; v. more items&#10; (A) a subsublist&#10; (B) a subsublist&#10;&#10;Nesting:&#10;&#10;A. Upper Alpha&#10; I. Upper Roman.&#10; (6) Decimal start with 6&#10; c) Lower alpha with paren&#10;&#10;Autonumbering:&#10;&#10;1. Autonumber.&#10;2. More.&#10; 1. Nested.&#10;&#10;Should not be a list item:&#10;&#10;M.A. 2007&#10;&#10;B. Williams&#10;&#10;------------------------------------------------------------------------">
42+ <outline text="Fancy list markers" _note="1. begins with 2&#10;2. and now 3&#10;&#10; with a continuation&#10;&#10; 1. sublist with roman numerals, starting with 4&#10; 2. more items&#10; 1. a subsublist&#10; 2. a subsublist&#10;&#10;Nesting:&#10;&#10;1. Upper Alpha&#10; 1. Upper Roman.&#10; 1. Decimal start with 6&#10; 1. Lower alpha with paren&#10;&#10;Autonumbering:&#10;&#10;1. Autonumber.&#10;2. More.&#10; 1. Nested.&#10;&#10;Should not be a list item:&#10;&#10;M.A. 2007&#10;&#10;B. Williams&#10;&#10;------------------------------------------------------------------------">
4343 </outline>
4444</outline>
45−<outline text="Definition Lists" _note="Tight using spaces:&#10;&#10;apple&#10;: red fruit&#10;&#10;orange&#10;: orange fruit&#10;&#10;banana&#10;: yellow fruit&#10;&#10;Tight using tabs:&#10;&#10;apple&#10;: red fruit&#10;&#10;orange&#10;: orange fruit&#10;&#10;banana&#10;: yellow fruit&#10;&#10;Loose:&#10;&#10;apple&#10;&#10;: red fruit&#10;&#10;orange&#10;&#10;: orange fruit&#10;&#10;banana&#10;&#10;: yellow fruit&#10;&#10;Multiple blocks with italics:&#10;&#10;*apple*&#10;&#10;: red fruit&#10;&#10; contains seeds, crisp, pleasant to taste&#10;&#10;*orange*&#10;&#10;: orange fruit&#10;&#10; { orange code block }&#10;&#10; &gt; orange block quote&#10;&#10;Multiple definitions, tight:&#10;&#10;apple&#10;: red fruit&#10;: computer&#10;&#10;orange&#10;: orange fruit&#10;: bank&#10;&#10;Multiple definitions, loose:&#10;&#10;apple&#10;&#10;: red fruit&#10;&#10;: computer&#10;&#10;orange&#10;&#10;: orange fruit&#10;&#10;: bank&#10;&#10;Blank line after term, indented marker, alternate markers:&#10;&#10;apple&#10;&#10;: red fruit&#10;&#10;: computer&#10;&#10;orange&#10;&#10;: orange fruit&#10;&#10; 1. sublist&#10; 2. sublist&#10;&#10;">
45+<outline text="Definition Lists" _note="Tight using spaces:&#10;&#10;apple &#10;red fruit&#10;&#10;orange &#10;orange fruit&#10;&#10;banana &#10;yellow fruit&#10;&#10;Tight using tabs:&#10;&#10;apple &#10;red fruit&#10;&#10;orange &#10;orange fruit&#10;&#10;banana &#10;yellow fruit&#10;&#10;Loose:&#10;&#10;apple &#10;red fruit&#10;&#10;orange &#10;orange fruit&#10;&#10;banana &#10;yellow fruit&#10;&#10;Multiple blocks with italics:&#10;&#10;*apple* &#10;red fruit&#10;&#10;contains seeds, crisp, pleasant to taste&#10;&#10;*orange* &#10;orange fruit&#10;&#10; { orange code block }&#10;&#10;&gt; orange block quote&#10;&#10;Multiple definitions, tight:&#10;&#10;apple &#10;red fruit&#10;&#10;computer&#10;&#10;orange &#10;orange fruit&#10;&#10;bank&#10;&#10;Multiple definitions, loose:&#10;&#10;apple &#10;red fruit&#10;&#10;computer&#10;&#10;orange &#10;orange fruit&#10;&#10;bank&#10;&#10;Blank line after term, indented marker, alternate markers:&#10;&#10;apple &#10;red fruit&#10;&#10;computer&#10;&#10;orange &#10;orange fruit&#10;&#10;1. sublist&#10;2. sublist&#10;">
4646</outline>
47−<outline text="HTML Blocks" _note="Simple block on one line:&#10;&#10;&lt;div&gt;&#10;&#10;foo&#10;&#10;&lt;/div&gt;&#10;&#10;And nested without indentation:&#10;&#10;&lt;div&gt;&#10;&#10;&lt;div&gt;&#10;&#10;&lt;div&gt;&#10;&#10;foo&#10;&#10;&lt;/div&gt;&#10;&#10;&lt;/div&gt;&#10;&#10;&lt;div&gt;&#10;&#10;bar&#10;&#10;&lt;/div&gt;&#10;&#10;&lt;/div&gt;&#10;&#10;Interpreted markdown in a table:&#10;&#10;&lt;table&gt;&#10;&lt;tr&gt;&#10;&lt;td&gt;&#10;This is *emphasized*&#10;&lt;/td&gt;&#10;&lt;td&gt;&#10;And this is **strong**&#10;&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;/table&gt;&#10;&lt;script type=&quot;text/javascript&quot;&gt;document.write('This *should not* be interpreted as markdown');&lt;/script&gt;&#10;Here’s a simple block:&#10;&#10;&lt;div&gt;&#10;&#10;foo&#10;&#10;&lt;/div&gt;&#10;&#10;This should be a code block, though:&#10;&#10; &lt;div&gt;&#10; foo&#10; &lt;/div&gt;&#10;&#10;As should this:&#10;&#10; &lt;div&gt;foo&lt;/div&gt;&#10;&#10;Now, nested:&#10;&#10;&lt;div&gt;&#10;&#10;&lt;div&gt;&#10;&#10;&lt;div&gt;&#10;&#10;foo&#10;&#10;&lt;/div&gt;&#10;&#10;&lt;/div&gt;&#10;&#10;&lt;/div&gt;&#10;&#10;This should just be an HTML comment:&#10;&#10;&lt;!-- Comment --&gt;&#10;Multiline:&#10;&#10;&lt;!--&#10;Blah&#10;Blah&#10;--&gt;&#10;&lt;!--&#10; This is another comment.&#10;--&gt;&#10;Code block:&#10;&#10; &lt;!-- Comment --&gt;&#10;&#10;Just plain comment, with trailing spaces on the line:&#10;&#10;&lt;!-- foo --&gt;&#10;Code:&#10;&#10; &lt;hr /&gt;&#10;&#10;Hr’s:&#10;&#10;&lt;hr&gt;&#10;&lt;hr /&gt;&#10;&lt;hr /&gt;&#10;&lt;hr&gt;&#10;&lt;hr /&gt;&#10;&lt;hr /&gt;&#10;&lt;hr class=&quot;foo&quot; id=&quot;bar&quot; /&gt;&#10;&lt;hr class=&quot;foo&quot; id=&quot;bar&quot; /&gt;&#10;&lt;hr class=&quot;foo&quot; id=&quot;bar&quot;&gt;&#10;&#10;------------------------------------------------------------------------">
47+<outline text="HTML Blocks" _note="Simple block on one line:&#10;&#10;foo&#10;&#10;And nested without indentation:&#10;&#10;foo&#10;&#10;bar&#10;&#10;Interpreted markdown in a table:&#10;&#10;This is *emphasized*&#10;And this is **strong**&#10;Here’s a simple block:&#10;&#10;foo&#10;&#10;This should be a code block, though:&#10;&#10; &lt;div&gt;&#10; foo&#10; &lt;/div&gt;&#10;&#10;As should this:&#10;&#10; &lt;div&gt;foo&lt;/div&gt;&#10;&#10;Now, nested:&#10;&#10;foo&#10;&#10;This should just be an HTML comment:&#10;&#10;Multiline:&#10;&#10;Code block:&#10;&#10; &lt;!-- Comment --&gt;&#10;&#10;Just plain comment, with trailing spaces on the line:&#10;&#10;Code:&#10;&#10; &lt;hr /&gt;&#10;&#10;Hr’s:&#10;&#10;------------------------------------------------------------------------">
4848</outline>
49−<outline text="Inline Markup" _note="This is *emphasized*, and so *is this*.&#10;&#10;This is **strong**, and so **is this**.&#10;&#10;An *[emphasized link](/url)*.&#10;&#10;***This is strong and em.***&#10;&#10;So is ***this*** word.&#10;&#10;***This is strong and em.***&#10;&#10;So is ***this*** word.&#10;&#10;This is code: `&gt;`, `$`, `\`, `\$`, `&lt;html&gt;`.&#10;&#10;~~This is *strikeout*.~~&#10;&#10;Superscripts: a^bc^d a^*hello*^ a^hello there^.&#10;&#10;Subscripts: H~2~O, H~23~O, H~many of them~O.&#10;&#10;These should not be superscripts or subscripts, because of the unescaped&#10;spaces: a\^b c\^d, a\~b c\~d.&#10;&#10;------------------------------------------------------------------------">
49+<outline text="Inline Markup" _note="This is *emphasized*, and so *is this*.&#10;&#10;This is **strong**, and so **is this**.&#10;&#10;An *[emphasized link](/url)*.&#10;&#10;***This is strong and em.***&#10;&#10;So is ***this*** word.&#10;&#10;***This is strong and em.***&#10;&#10;So is ***this*** word.&#10;&#10;This is code: `&gt;`, `$`, `\`, `\$`, `&lt;html&gt;`.&#10;&#10;This is *strikeout*.&#10;&#10;Superscripts: abcd a*hello* ahello there.&#10;&#10;Subscripts: H₂O, H₂₃O, Hmany of themO.&#10;&#10;These should not be superscripts or subscripts, because of the unescaped&#10;spaces: a^b c^d, a~b c~d.&#10;&#10;------------------------------------------------------------------------">
5050</outline>
5151<outline text="Smart quotes, ellipses, dashes" _note="“Hello,” said the spider. “‘Shelob’ is my name.”&#10;&#10;‘A’, ‘B’, and ‘C’ are letters.&#10;&#10;‘Oak,’ ‘elm,’ and ‘beech’ are names of trees. So is ‘pine.’&#10;&#10;‘He said, “I want to go.”’ Were you alive in the 70’s?&#10;&#10;Here is some quoted ‘`code`’ and a “[quoted&#10;link](http://example.com/?foo=1&amp;bar=2)”.&#10;&#10;Some dashes: one—two — three—four — five.&#10;&#10;Dashes between numbers: 5–7, 255–66, 1987–1999.&#10;&#10;Ellipses…and…and….&#10;&#10;------------------------------------------------------------------------">
5252</outline>
53−<outline text="LaTeX" _note="- \cite[22-23]{smith.1899}&#10;- $2+2=4$&#10;- $x \in y$&#10;- $\alpha \wedge \omega$&#10;- $223$&#10;- $p$-Tree&#10;- Here’s some display math:&#10; $$\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$$&#10;- Here’s one that has a line break in it:&#10; $\alpha + \omega \times x^2$.&#10;&#10;These shouldn’t be math:&#10;&#10;- To get the famous equation, write `$e = mc^2$`.&#10;- \$22,000 is a *lot* of money. So is \$34,000. (It worked if “lot” is&#10; emphasized.)&#10;- Shoes (\$20) and socks (\$5).&#10;- Escaped `$`: \$73 *this should be emphasized* 23\$.&#10;&#10;Here’s a LaTeX table:&#10;&#10;\begin{tabular}{|l|l|}\hline&#10;Animal &amp; Number \\ \hline&#10;Dog &amp; 2 \\&#10;Cat &amp; 1 \\ \hline&#10;\end{tabular}&#10;&#10;------------------------------------------------------------------------">
53+<outline text="LaTeX" _note="- &#10;- 2 + 2 = 4&#10;- *x* ∈ *y*&#10;- *α* ∧ *ω*&#10;- 223&#10;- *p*-Tree&#10;- Here’s some display math:&#10; $$\\frac{d}{dx}f(x)=\\lim\_{h\\to 0}\\frac{f(x+h)-f(x)}{h}$$&#10;- Here’s one that has a line break in it: *α* + *ω* × *x*².&#10;&#10;These shouldn’t be math:&#10;&#10;- To get the famous equation, write `$e = mc^2$`.&#10;- $22,000 is a *lot* of money. So is $34,000. (It worked if “lot” is&#10; emphasized.)&#10;- Shoes ($20) and socks ($5).&#10;- Escaped `$`: $73 *this should be emphasized* 23$.&#10;&#10;Here’s a LaTeX table:&#10;&#10;------------------------------------------------------------------------">
5454</outline>
5555<outline text="Special Characters" _note="Here is some unicode:&#10;&#10;- I hat: Î&#10;- o umlaut: ö&#10;- section: §&#10;- set membership: ∈&#10;- copyright: ©&#10;&#10;AT&amp;T has an ampersand in their name.&#10;&#10;AT&amp;T is another way to write it.&#10;&#10;This &amp; that.&#10;&#10;4 &amp;lt; 5.&#10;&#10;6 &amp;gt; 5.&#10;&#10;Backslash: \\&#10;&#10;Backtick: \`&#10;&#10;Asterisk: \*&#10;&#10;Underscore: \_&#10;&#10;Left brace: {&#10;&#10;Right brace: }&#10;&#10;Left bracket: \[&#10;&#10;Right bracket: \]&#10;&#10;Left paren: (&#10;&#10;Right paren: )&#10;&#10;Greater-than: &amp;gt;&#10;&#10;Hash: \#&#10;&#10;Period: .&#10;&#10;Bang: !&#10;&#10;Plus: +&#10;&#10;Minus: -&#10;&#10;------------------------------------------------------------------------">
5656</outline>
@@ -66,7 +66,7 @@
6666</outline>
6767<outline text="Images" _note="From “Voyage dans la Lune” by Georges Melies (1902):&#10;&#10;![lalune](lalune.jpg &quot;Voyage dans la Lune&quot;)&#10;&#10;Here is a movie ![movie](movie.jpg) icon.&#10;&#10;------------------------------------------------------------------------">
6868</outline>
69−<outline text="Footnotes" _note="Here is a footnote reference,[^1] and another.[^2] This should *not* be&#10;a footnote reference, because it contains a space.\[\^my note\] Here is&#10;an inline note.[^3]&#10;&#10;&gt; Notes can go in quotes.[^4]&#10;&#10;1. And in list items.[^5]&#10;&#10;This paragraph should not be part of the note, as it is not indented.&#10;&#10;[^1]: Here is the footnote. It can go anywhere after the footnote&#10; reference. It need not be placed at the end of the document.&#10;&#10;[^2]: Here’s the long note. This one contains multiple blocks.&#10;&#10; Subsequent blocks are indented to show that they belong to the&#10; footnote (as with list items).&#10;&#10; { &lt;code&gt; }&#10;&#10; If you want, you can indent every line, but you can also be lazy and&#10; just indent the first line of each block.&#10;&#10;[^3]: This is *easier* to type. Inline notes may contain&#10; [links](http://google.com) and `]` verbatim characters, as well as&#10; \[bracketed text\].&#10;&#10;[^4]: In quote.&#10;&#10;[^5]: In list.">
69+<outline text="Footnotes" _note="Here is a footnote reference,[1] and another.[2] This should *not* be a&#10;footnote reference, because it contains a space.\[^my note\] Here is an&#10;inline note.[3]&#10;&#10;&gt; Notes can go in quotes.[4]&#10;&#10;1. And in list items.[5]&#10;&#10;This paragraph should not be part of the note, as it is not indented.&#10;&#10;[1] Here is the footnote. It can go anywhere after the footnote&#10;reference. It need not be placed at the end of the document.&#10;&#10;[2] Here’s the long note. This one contains multiple blocks.&#10;&#10;Subsequent blocks are indented to show that they belong to the footnote&#10;(as with list items).&#10;&#10; { &lt;code&gt; }&#10;&#10;If you want, you can indent every line, but you can also be lazy and&#10;just indent the first line of each block.&#10;&#10;[3] This is *easier* to type. Inline notes may contain&#10;[links](http://google.com) and `]` verbatim characters, as well as&#10;\[bracketed text\].&#10;&#10;[4] In quote.&#10;&#10;[5] In list.">
7070</outline>
7171 </body>
7272</opml>
extract-changes.hs → tools/extract-changes.hsrenamed

The content is the same.

github-upload.sh → tools/github-upload.shrenamed

The content is the same.