diff options
53 files changed, 4418 insertions, 2548 deletions
diff --git a/.travis.yml b/.travis.yml index 9c4177357..bb97802ac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -61,6 +61,10 @@ matrix: # The Stack builds. We can pass in arbitrary Stack arguments via the ARGS # variable, such as using --stack-yaml to point to a different file. + # - env: BUILD=stack ARGS="--stack-yaml stack.lts9.yaml" OPTS="-Wall -Wincomplete-record-updates -Wnoncanonical-monad-instances -Wnoncanonical-monadfail-instances -fno-warn-unused-do-bind -Werror" + # compiler: ": #stack 8.0.2" + # addons: {apt: {packages: [ghc-8.0.2], sources: [hvr-ghc]}} + - env: BUILD=stack ARGS="--resolver lts-10" OPTS="-Wall -Wincomplete-record-updates -Wnoncanonical-monad-instances -Wnoncanonical-monadfail-instances -fno-warn-unused-do-bind -Werror" compiler: ": #stack 8.2.2" addons: {apt: {packages: [ghc-8.2.2], sources: [hvr-ghc]}} @@ -107,6 +111,7 @@ install: case "$BUILD" in stack) ulimit -n 4096 + stack config set system-ghc --global true stack --no-terminal --install-ghc $ARGS build --only-dependencies --fast --flag 'pandoc:embed_data_files' --flag 'aeson:fast' --jobs 2 ;; cabal) @@ -121,6 +126,7 @@ script: case "$BUILD" in stack) ulimit -n 4096 + stack config set system-ghc --global true stack --no-terminal $ARGS test --fast --flag 'aeson:fast' --flag pandoc:embed_data_files --haddock --no-haddock-deps --ghc-options="$OPTS" ;; cabal) diff --git a/MANUAL.txt b/MANUAL.txt index 78bd057ed..5d60e2c19 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -1,6 +1,6 @@ % Pandoc User's Guide % John MacFarlane -% December 8, 2017 +% December 27, 2017 Synopsis ======== @@ -284,16 +284,9 @@ General options (`markdown_github` provides deprecated and less accurate support for Github-Flavored Markdown; please use `gfm` instead, unless you need to use extensions other than `smart`.) - If `+lhs` is appended to `markdown`, `rst`, `latex`, or - `html`, the input will be treated as literate Haskell source: see - [Literate Haskell support], below. Markdown - syntax extensions can be individually enabled or disabled by - appending `+EXTENSION` or `-EXTENSION` to the format name. So, for - example, `markdown_strict+footnotes+definition_lists` is strict - Markdown with footnotes and definition lists enabled, and - `markdown-pipe_tables+hard_line_breaks` is pandoc's Markdown - without pipe tables and with hard line breaks. See [Pandoc's - Markdown], below, for a list of extensions and + Extensions can be individually enabled or disabled by + appending `+EXTENSION` or `-EXTENSION` to the format name. + See [Extensions] below, for a list of extensions and their names. See `--list-input-formats` and `--list-extensions`, below. @@ -327,13 +320,10 @@ General options unless you use extensions that do not work with `gfm`.) Note that `odt`, `epub`, and `epub3` output will not be directed to *stdout*; an output filename must be specified using the - `-o/--output` option. If `+lhs` is appended to `markdown`, `rst`, - `latex`, `beamer`, `html4`, or `html5`, the output will be - rendered as literate Haskell source: see [Literate Haskell - support], below. Markdown syntax extensions can be individually - enabled or disabled by appending `+EXTENSION` or `-EXTENSION` to - the format name, as described above under `-f`. See - `--list-output-formats` and `--list-extensions`, below. + `-o/--output` option. Extensions can be individually enabled or + disabled by appending `+EXTENSION` or `-EXTENSION` to the format + name. See [Extensions] below, for a list of extensions and their + names. See `--list-output-formats` and `--list-extensions`, below. `-o` *FILE*, `--output=`*FILE* @@ -397,11 +387,12 @@ General options : List supported output formats, one per line. -`--list-extensions` +`--list-extensions`[`=`*FORMAT*] -: List supported Markdown extensions, one per line, followed +: List supported Markdown extensions, one per line, preceded by a `+` or `-` indicating whether it is enabled by default - in pandoc's Markdown. + in *FORMAT*. If *FORMAT* is not specified, defaults for + pandoc's Markdown are given. `--list-highlight-languages` @@ -491,6 +482,9 @@ Reader options 3. `$PATH` (executable only) + Filters and lua-filters are applied in the order specified + on the command line. + `--lua-filter=`*SCRIPT* : Transform the document in a similar fashion as JSON filters (see @@ -864,8 +858,8 @@ Options affecting specific writers `--section-divs` -: Wrap sections in `<div>` tags (or `<section>` tags in HTML5), - and attach identifiers to the enclosing `<div>` (or `<section>`) +: Wrap sections in `<section>` tags (or `<div>` tags for `html4`), + and attach identifiers to the enclosing `<section>` (or `<div>`) rather than the header itself. See [Header identifiers], below. @@ -1106,7 +1100,7 @@ of the following options. The *URL* should point to the `MathJax.js` load script. If a *URL* is not provided, a link to the Cloudflare CDN will be inserted. - + `--mathml` : Convert TeX math to [MathML] (in `epub3`, `docbook4`, `docbook5`, `jats`, @@ -1698,6 +1692,269 @@ will be treated as a comment and ignored. [pandoc-templates]: https://github.com/jgm/pandoc-templates +Extensions +========== + +The behavior of some of the readers and writers can be adjusted by +enabling or disabling various extensions. + +An extension can be enabled by adding `+EXTENSION` +to the format name and disabled by adding `-EXTENSION`. For example, +`--from markdown_strict+footnotes` is strict Markdown with footnotes +enabled, while `--from markdown-footnotes-pipe_tables` is pandoc's +Markdown without footnotes or pipe tables. + +The markdown reader and writer make by far the most use of extensions. +Extensions only used by them are therefore covered in the +section [Pandoc's Markdown] below (See [Markdown variants] for +`commonmark` and `gfm`.) In the following, extensions that also work +for other formats are covered. + +Typography +---------- + +#### Extension: `smart` #### + +Interpret straight quotes as curly quotes, `---` as em-dashes, +`--` as en-dashes, and `...` as ellipses. Nonbreaking spaces are +inserted after certain abbreviations, such as "Mr." + +This extension can be enabled/disabled for the following formats: + +input formats +: `markdown`, `commonmark`, `latex`, `mediawiki`, `org`, `rst`, `twiki` + +output formats +: `markdown`, `latex`, `context`, `rst` + +enabled by default in +: `markdown`, `latex`, `context` (both input and output) + +Note: If you are *writing* Markdown, then the `smart` extension +has the reverse effect: what would have been curly quotes comes +out straight. + +In LaTeX, `smart` means to use the standard TeX ligatures +for quotation marks (` `` ` and ` '' ` for double quotes, +`` ` `` and `` ' `` for single quotes) and dashes (`--` for +en-dash and `---` for em-dash). If `smart` is disabled, +then in reading LaTeX pandoc will parse these characters +literally. In writing LaTeX, enabling `smart` tells pandoc +to use the ligatures when possible; if `smart` is disabled +pandoc will use unicode quotation mark and dash characters. + +Headers and sections +-------------------- + +#### Extension: `auto_identifiers` #### + +A header without an explicitly specified identifier will be +automatically assigned a unique identifier based on the header text. + +This extension can be enabled/disabled for the following formats: + +input formats +: `markdown`, `latex`, `rst`, `mediawiki`, `textile` + +output formats +: `markdown`, `muse` + +enabled by default in +: `markdown`, `muse` + +The algorithm used to derive the identifier from the header text is: + + - Remove all formatting, links, etc. + - Remove all footnotes. + - Remove all punctuation, except underscores, hyphens, and periods. + - Replace all spaces and newlines with hyphens. + - Convert all alphabetic characters to lowercase. + - Remove everything up to the first letter (identifiers may + not begin with a number or punctuation mark). + - If nothing is left after this, use the identifier `section`. + +Thus, for example, + + Header Identifier + ------------------------------- ---------------------------- + `Header identifiers in HTML` `header-identifiers-in-html` + `*Dogs*?--in *my* house?` `dogs--in-my-house` + `[HTML], [S5], or [RTF]?` `html-s5-or-rtf` + `3. Applications` `applications` + `33` `section` + +These rules should, in most cases, allow one to determine the identifier +from the header text. The exception is when several headers have the +same text; in this case, the first will get an identifier as described +above; the second will get the same identifier with `-1` appended; the +third with `-2`; and so on. + +These identifiers are used to provide link targets in the table of +contents generated by the `--toc|--table-of-contents` option. They +also make it easy to provide links from one section of a document to +another. A link to this section, for example, might look like this: + + See the section on + [header identifiers](#header-identifiers-in-html-latex-and-context). + +Note, however, that this method of providing links to sections works +only in HTML, LaTeX, and ConTeXt formats. + +If the `--section-divs` option is specified, then each section will +be wrapped in a `section` (or a `div`, if `html4` was specified), +and the identifier will be attached to the enclosing `<section>` +(or `<div>`) tag rather than the header itself. This allows entire +sections to be manipulated using JavaScript or treated differently in +CSS. + +#### Extension: `ascii_identifiers` #### + +Causes the identifiers produced by `auto_identifiers` to be pure ASCII. +Accents are stripped off of accented Latin letters, and non-Latin +letters are omitted. + +Math Input +---------- + +The extensions [`tex_math_dollars`](#extension-tex_math_dollars), +[`tex_math_single_backslash`](#extension-tex_math_single_backslash), and +[`tex_math_double_backslash`](#extension-tex_math_double_backslash) +are described in the section about Pandoc's Markdown. + +However, they can also be used with HTML input. This is handy for +reading web pages formatted using MathJax, for example. + +Raw HTML/TeX +------------ + +The following extensions (especially how they affect Markdown +input/output) are also described in more detail in their respective +sections of [Pandoc's Markdown]. + +#### [Extension: `raw_html`] {#raw_html} + +When converting from HTML, parse elements to raw HTML which are not +representable in pandoc's AST. +By default, this is disabled for HTML input. + +#### [Extension: `raw_tex`] {#raw_tex} + +Allows raw LaTeX, TeX, and ConTeXt to be included in a document. + +This extension can be enabled/disabled for the following formats +(in addition to `markdown`): + +input formats +: `latex`, `org`, `textile` + +output formats +: `textile` + +#### [Extension: `native_divs`] {#native_divs} + +This extension is enabled by default for HTML input. This means that +`div`s are parsed to pandoc native elements. (Alternatively, you +can parse them to raw HTML using `-f html-native_divs+raw_html`.) + +When converting HTML to Markdown, for example, you may want to drop all +`div`s and `span`s: + + pandoc -f html-native_divs-native_spans -t markdown + +#### [Extension: `native_spans`] {#native_spans} + +Analogous to `native_divs` above. + + +Literate Haskell support +------------------------ + +#### Extension: `literate_haskell` #### + +Treat the document as literate Haskell source. + +This extension can be enabled/disabled for the following formats: + +input formats +: `markdown`, `rst`, `latex` + +output formats +: `markdown`, `rst`, `latex`, `html` + +If you append `+lhs` (or `+literate_haskell`) to one of the formats +above, pandoc will treat the document as literate Haskell source. +This means that + + - In Markdown input, "bird track" sections will be parsed as Haskell + code rather than block quotations. Text between `\begin{code}` + and `\end{code}` will also be treated as Haskell code. For + ATX-style headers the character '=' will be used instead of '#'. + + - In Markdown output, code blocks with classes `haskell` and `literate` + will be rendered using bird tracks, and block quotations will be + indented one space, so they will not be treated as Haskell code. + In addition, headers will be rendered setext-style (with underlines) + rather than ATX-style (with '#' characters). (This is because ghc + treats '#' characters in column 1 as introducing line numbers.) + + - In restructured text input, "bird track" sections will be parsed + as Haskell code. + + - In restructured text output, code blocks with class `haskell` will + be rendered using bird tracks. + + - In LaTeX input, text in `code` environments will be parsed as + Haskell code. + + - In LaTeX output, code blocks with class `haskell` will be rendered + inside `code` environments. + + - In HTML output, code blocks with class `haskell` will be rendered + with class `literatehaskell` and bird tracks. + +Examples: + + pandoc -f markdown+lhs -t html + +reads literate Haskell source formatted with Markdown conventions and writes +ordinary HTML (without bird tracks). + + pandoc -f markdown+lhs -t html+lhs + +writes HTML with the Haskell code in bird tracks, so it can be copied +and pasted as literate Haskell source. + +Note that GHC expects the bird tracks in the first column, so indentend literate +code blocks (e.g. inside an itemized environment) will not be picked up by the +Haskell compiler. + +Other extensions +---------------- + +#### Extension: `empty_paragraphs` #### + +Allows empty paragraphs. By default empty paragraphs are +omitted. + +This extension can be enabled/disabled for the following formats: + +input formats +: `docx`, `html` + +output formats +: `markdown`, `docx`, `odt`, `opendocument`, `html` + +#### Extension: `amuse` #### + +In the `muse` input format, this enables Text::Amuse +extensions to Emacs Muse markup. + +#### Extension: `citations` {#org-citations} + +Some aspects of [Pandoc's Markdown citation syntax](#citations) are also accepted +in `org` input. + + Pandoc's Markdown ================= @@ -1705,11 +1962,9 @@ Pandoc understands an extended and slightly revised version of John Gruber's [Markdown] syntax. This document explains the syntax, noting differences from standard Markdown. Except where noted, these differences can be suppressed by using the `markdown_strict` format instead -of `markdown`. An extensions can be enabled by adding `+EXTENSION` -to the format name and disabled by adding `-EXTENSION`. For example, -`markdown_strict+footnotes` is strict Markdown with footnotes -enabled, while `markdown-footnotes-pipe_tables` is pandoc's -Markdown without footnotes or pipe tables. +of `markdown`. Extensions can be enabled or disabled to specify the +behavior more granularly. They are described in the following. See also +[Extensions] above, for extensions that work also on other formats. Philosophy ---------- @@ -1801,6 +2056,8 @@ pandoc does require the space. ### Header identifiers ### +See also the [`auto_identifiers` extension](#extension-auto_identifiers) above. + #### Extension: `header_attributes` #### Headers can be assigned attributes using this syntax at the end @@ -1837,55 +2094,6 @@ is just the same as # My header {.unnumbered} -#### Extension: `auto_identifiers` #### - -A header without an explicitly specified identifier will be -automatically assigned a unique identifier based on the header text. -To derive the identifier from the header text, - - - Remove all formatting, links, etc. - - Remove all footnotes. - - Remove all punctuation, except underscores, hyphens, and periods. - - Replace all spaces and newlines with hyphens. - - Convert all alphabetic characters to lowercase. - - Remove everything up to the first letter (identifiers may - not begin with a number or punctuation mark). - - If nothing is left after this, use the identifier `section`. - -Thus, for example, - - Header Identifier - ------------------------------- ---------------------------- - `Header identifiers in HTML` `header-identifiers-in-html` - `*Dogs*?--in *my* house?` `dogs--in-my-house` - `[HTML], [S5], or [RTF]?` `html-s5-or-rtf` - `3. Applications` `applications` - `33` `section` - -These rules should, in most cases, allow one to determine the identifier -from the header text. The exception is when several headers have the -same text; in this case, the first will get an identifier as described -above; the second will get the same identifier with `-1` appended; the -third with `-2`; and so on. - -These identifiers are used to provide link targets in the table of -contents generated by the `--toc|--table-of-contents` option. They -also make it easy to provide links from one section of a document to -another. A link to this section, for example, might look like this: - - See the section on - [header identifiers](#header-identifiers-in-html-latex-and-context). - -Note, however, that this method of providing links to sections works -only in HTML, LaTeX, and ConTeXt formats. - -If the `--section-divs` option is specified, then each section will -be wrapped in a `div` (or a `section`, if `html5` was specified), -and the identifier will be attached to the enclosing `<div>` -(or `<section>`) tag rather than the header itself. This allows entire -sections to be manipulated using JavaScript or treated differently in -CSS. - #### Extension: `implicit_header_references` #### Pandoc behaves as if reference links have been defined for each header. @@ -3028,8 +3236,6 @@ HTML, Slidy, DZSlides, S5, EPUB command-line options selected. Therefore see [Math rendering in HTML] above. -This extension can be used with both `markdown` and `html` input. - [interpreted text role `:math:`]: http://docutils.sourceforge.net/docs/ref/rst/roles.html#math Raw HTML @@ -3457,33 +3663,6 @@ they cannot contain multiple paragraphs). The syntax is as follows: Inline and regular footnotes may be mixed freely. -Typography ----------- - -#### Extension: `smart` #### - -Interpret straight quotes as curly quotes, `---` as em-dashes, -`--` as en-dashes, and `...` as ellipses. Nonbreaking spaces are -inserted after certain abbreviations, such as "Mr." This -option currently affects the input formats `markdown`, -`commonmark`, `latex`, `mediawiki`, `org`, `rst`, and `twiki`, -and the output formats `markdown`, `latex`, and `context`. -It is enabled by default for `markdown`, `latex`, and `context` -(in both input and output). - -Note: If you are *writing* Markdown, then the `smart` extension -has the reverse effect: what would have been curly quotes comes -out straight. - -In LaTeX, `smart` means to use the standard TeX ligatures -for quotation marks (` `` ` and ` '' ` for double quotes, -`` ` `` and `` ' `` for single quotes) and dashes (`--` for -en-dash and `---` for em-dash). If `smart` is disabled, -then in reading LaTeX pandoc will parse these characters -literally. In writing LaTeX, enabling `smart` tells pandoc -to use the ligatures when possible; if `smart` is disabled -pandoc will use unicode quotation mark and dash characters. - Citations --------- @@ -3746,8 +3925,6 @@ TeX math, and anything between `\[` and `\]` to be interpreted as display TeX math. Note: a drawback of this extension is that it precludes escaping `(` and `[`. -This extension can be used with both `markdown` and `html` input. - #### Extension: `tex_math_double_backslash` #### Causes anything between `\\(` and `\\)` to be interpreted as inline @@ -3790,12 +3967,6 @@ simply skipped (as opposed to being parsed as paragraphs). Makes all absolute URIs into links, even when not surrounded by pointy braces `<...>`. -#### Extension: `ascii_identifiers` #### - -Causes the identifiers produced by `auto_identifiers` to be pure ASCII. -Accents are stripped off of accented Latin letters, and non-Latin -letters are omitted. - #### Extension: `mmd_link_attributes` #### Parses multimarkdown style key-value attributes on link @@ -3839,12 +4010,6 @@ in several respects: we must either disallow lazy wrapping or require a blank line between list items. -#### Extension: `empty_paragraphs` #### - -Allows empty paragraphs. By default empty paragraphs are -omitted. This affects the `docx` reader and writer, the -`opendocument` and `odt` writer, and all HTML-based readers and writers. - Markdown variants ----------------- @@ -3878,34 +4043,21 @@ variants are supported: : `raw_html`, `shortcut_reference_links`, `spaced_reference_links`. -We also support `gfm` (GitHub-Flavored Markdown) as a set of -extensions on `commonmark`: +We also support `commonmark` and `gfm` (GitHub-Flavored Markdown, +which is implemented as a set of extensions on `commonmark`). +Note, however, that `commonmark` and `gfm` have limited support +for extensions. Only those listed below (and `smart` and +`raw_tex`) will work. The extensions can, however, all be +individually disabled. +Also, `raw_tex` only affects `gfm` output, not input. + +`gfm` (GitHub-Flavored Markdown) : `pipe_tables`, `raw_html`, `fenced_code_blocks`, `auto_identifiers`, `ascii_identifiers`, `backtick_code_blocks`, `autolink_bare_uris`, `intraword_underscores`, `strikeout`, `hard_line_breaks`, `emoji`, `shortcut_reference_links`, `angle_brackets_escapable`. - These can all be individually disabled. Note, however, that - `commonmark` and `gfm` have limited support for extensions: - extensions other than those listed above (and `smart` and - `raw_tex`) will have no effect on `commonmark` or `gfm`. - And `raw_tex` only affects `gfm` output, not input. - -Extensions with formats other than Markdown -------------------------------------------- - -Some of the extensions discussed above can be used with formats -other than Markdown: - -* `auto_identifiers` can be used with `latex`, `rst`, `mediawiki`, - and `textile` input (and is used by default). - -* `tex_math_dollars`, `tex_math_single_backslash`, and - `tex_math_double_backslash` can be used with `html` input. - (This is handy for reading web pages formatted using MathJax, - for example.) - Producing slide shows with pandoc ================================= @@ -4257,57 +4409,6 @@ with the `src` attribute. For example: </source> </audio> -Literate Haskell support -======================== - -If you append `+lhs` (or `+literate_haskell`) to an appropriate input or output -format (`markdown`, `markdown_strict`, `rst`, or `latex` for input or output; -`beamer`, `html4` or `html5` for output only), pandoc will treat the document as -literate Haskell source. This means that - - - In Markdown input, "bird track" sections will be parsed as Haskell - code rather than block quotations. Text between `\begin{code}` - and `\end{code}` will also be treated as Haskell code. For - ATX-style headers the character '=' will be used instead of '#'. - - - In Markdown output, code blocks with classes `haskell` and `literate` - will be rendered using bird tracks, and block quotations will be - indented one space, so they will not be treated as Haskell code. - In addition, headers will be rendered setext-style (with underlines) - rather than ATX-style (with '#' characters). (This is because ghc - treats '#' characters in column 1 as introducing line numbers.) - - - In restructured text input, "bird track" sections will be parsed - as Haskell code. - - - In restructured text output, code blocks with class `haskell` will - be rendered using bird tracks. - - - In LaTeX input, text in `code` environments will be parsed as - Haskell code. - - - In LaTeX output, code blocks with class `haskell` will be rendered - inside `code` environments. - - - In HTML output, code blocks with class `haskell` will be rendered - with class `literatehaskell` and bird tracks. - -Examples: - - pandoc -f markdown+lhs -t html - -reads literate Haskell source formatted with Markdown conventions and writes -ordinary HTML (without bird tracks). - - pandoc -f markdown+lhs -t html+lhs - -writes HTML with the Haskell code in bird tracks, so it can be copied -and pasted as literate Haskell source. - -Note that GHC expects the bird tracks in the first column, so indentend literate -code blocks (e.g. inside an itemized environment) will not be picked up by the -Haskell compiler. - Syntax highlighting =================== @@ -4,6 +4,7 @@ SOURCEFILES?=$(shell find pandoc.hs src test -name '*.hs') BRANCH?=master RESOLVER=lts-10 GHCOPTS=-fdiagnostics-color=always -Wall -fno-warn-unused-do-bind -Wincomplete-record-updates -Wnoncanonical-monad-instances -Wnoncanonical-monadfail-instances +WEBSITE=../../web/pandoc.org quick: stack install --resolver=$(RESOLVER) --ghc-options='$(GHCOPTS)' --install-ghc --flag 'pandoc:embed_data_files' --fast --test --test-arguments='-j4 --hide-successes $(TESTARGS)' @@ -32,7 +33,7 @@ lint: for f in $(SOURCEFILES); do echo $$f; hlint --verbose --refactor --refactor-options='-i -s' $$f; done changes_github: - pandoc --filter tools/extract-changes.hs changelog -t gfm+hard_line_breaks | sed -e 's/\\#/#/g' | pbcopy + pandoc --filter tools/extract-changes.hs changelog -t gfm --wrap=none | sed -e 's/\\#/#/g' | pbcopy dist: man/pandoc.1 cabal sdist @@ -85,7 +86,23 @@ download_stats: curl https://api.github.com/repos/jgm/pandoc/releases | \ jq -r '.[] | .assets | .[] | "\(.download_count)\t\(.name)"' +pandoc-templates: + rm ../pandoc-templates/default.* ; \ + cp data/templates/default.* ../pandoc-templates/ ; \ + pushd ../pandoc-templates/ && \ + git add default.* && \ + git commit -m "Updated templates for pandoc $(version)" && \ + popd + +trypandoc: + ssh -t macfarlane 'cd src/pandoc && git pull && ~/.local/bin/stack install --flag pandoc:trypandoc --flag pandoc:embed_data_files && cd trypandoc && sudo make install' + +update-website: + make -C $(WEBSITE) update + make -C $(WEBSITE) + make -C $(WEBSITE) upload + clean: stack clean -.PHONY: deps quick full haddock install clean test bench changes_github macospkg dist prof download_stats reformat lint weigh doc/lua-filters.md packages +.PHONY: deps quick full haddock install clean test bench changes_github macospkg dist prof download_stats reformat lint weigh doc/lua-filters.md packages pandoc-templates trypandoc update-website diff --git a/RELEASE-CHECKLIST b/RELEASE-CHECKLIST index f3e42a55e..dd8789b99 100644 --- a/RELEASE-CHECKLIST +++ b/RELEASE-CHECKLIST @@ -7,11 +7,10 @@ _ make man/pandoc.1 and commit if needed _ Tag release in git -_ Push templates: - git subtree push --prefix=data/templates git@github.com:jgm/pandoc-templates.git master +_ make pandoc-templates cd ../pandoc-templates - git pull git tag REL + git push git push --tags _ Generate Windows package (make winpkg) @@ -27,12 +26,9 @@ _ Upload to HackageDB: stack upload . _ if docs don't build on Hackage: 'cabal install neil && neil docs --username=MYUSERNAME' -_ Update website, including short description of changes ('make changes') +_ make update-website -_ Announce on pandoc-announce, pandoc-discuss - -_ on server, stack install --flag 'pandoc:trypandoc' - and then 'cd trypandoc; sudo make install' +_ make trypandoc -_ recompile gitit +_ Announce on pandoc-announce, pandoc-discuss diff --git a/appveyor.yml b/appveyor.yml index 534995891..9401d6666 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,7 @@ clone_folder: "c:\\pandoc" environment: global: WIXBIN: "c:\\Program Files (x86)\\WiX Toolset v3.11\\bin" - STACK_YAML: "c:\\pandoc\\stack.pkg.yaml" + STACK_YAML: "c:\\pandoc\\stack.yaml" STACK_BUILD_OPTS: "-j1 --no-terminal --test --local-bin-path=.\\windows" matrix: - STACK_VERSION: "windows-i386" diff --git a/benchmark/benchmark-pandoc.hs b/benchmark/benchmark-pandoc.hs index a667a6f81..645801c85 100644 --- a/benchmark/benchmark-pandoc.hs +++ b/benchmark/benchmark-pandoc.hs @@ -68,10 +68,11 @@ main = do movie <- B.readFile "test/movie.jpg" time <- Data.Time.getCurrentTime let setupFakeFiles = modifyPureState $ \st -> st{ stFiles = - FileTree $ Map.fromList [ - ("lalune.jpg", FileInfo time lalune), - ("movie.jpg", FileInfo time movie) - ]} + insertInFileTree "lalune.jpg" + (FileInfo time lalune) $ + insertInFileTree "movie.jpg" + (FileInfo time movie) mempty + } let opts = def let doc = either (error . show) id $ runPure $ readMarkdown opts inp let readers' = [(n, \o d -> @@ -1,3 +1,15 @@ +pandoc (2.1) + + * Filter changes (#4196). Previously we ran all lua filters before + JSON filters. Now we run filters in the order they are presented on + the command line, whether lua or JSON. There are two incompatible API + changes: The type of `applyFilters` has changed, and `applyLuaFilters` + has been removed. + + * Text.Pandoc.Class: make `FileTree` opaque (don't export + `FileTree` cosntructor). This forces users to interact with it using + `insertInFileTree` and `getFileInfo`, which normalize file names. + pandoc (2.0.6) * Added `jats` as an input format. @@ -11,6 +23,12 @@ pandoc (2.0.6) bibliographic information can be processed with pandoc and pandoc-citeproc to produce a formatted bibliography. + * Allow `--list-extensions` to take an optional FORMAT argument. + This lists the extensions set by default for the selected FORMAT. + The extensions are now alphabetized, and the `+` or `-` + indicating the default setting comes before, rather than after, + the extension. + * Markdown reader: + Preserve original whitespace between blocks. @@ -46,6 +64,8 @@ pandoc (2.0.6) Markdown reader when parsing raw LaTeX with escaped spaces. + Add tests of LaTeX tokenizer. + Support `\foreignlanguage` from babel. + + Be more tolerant of `&` character (#4208). This allows us to parse + unknown tabular environments as raw LaTeX. * Muse reader (Alexander Krotov): @@ -56,6 +76,12 @@ pandoc (2.0.6) * Org reader (Albert Krewinkel): + Fix asterisks-related parsing error (#4180). + + Support minlevel option for includes (#4154). The level of headers + in included files can be shifted to a higher level by specifying a + minimum header level via the `:minlevel` parameter. E.g. + `#+include: "tour.org" :minlevel 1` will shift the headers in + tour.org such that the topmost headers become level 1 headers. + + Break-up org reader test file into multiple modules. * OPML reader: @@ -78,6 +104,9 @@ pandoc (2.0.6) interruption and introduces a new id if a list is starting again. So we keep track of the state of lists and use them to define a "start" attribute, if necessary. + + Add tests for structured document tags unwrapping (Jesse Rosenthal). + + Preprocess Document body to unwrap `w:sdt` elements (Jesse Rosenthal, + #4190). * Plain writer: @@ -102,6 +131,8 @@ pandoc (2.0.6) + Don't look for default template file for Powerpoint (#4181). + Add pptx to isTextFormat list. This is used to check standalone and not writing to the terminal. + + Obey slide level option (Jesse Rosenthal). + + Introduce tests. * Docx writer: @@ -136,6 +167,12 @@ pandoc (2.0.6) This avoids a clash with a deprecated `\textlatin` command defined in Babel. + Allow fragile=singleslide attribute in beamer slides (#4169). + + Use `\endhead` after `\toprule` in headerless tables (#4207). + + * FB2 writer: + + + Add cover image specified by `cover-image` meta (Alexander Krotov, + #4195). * JATS writer (Hamish Mackenzie): @@ -150,16 +187,19 @@ pandoc (2.0.6) + Self closing tags for empty xref (#4187). + Improve support for code language. - * Custom writer (Albert Krewinkel): + * Custom writer: + Use init file to setup Lua interpreter (Albert Krewinkel). The same init file (`data/init`) that is used to setup the Lua interpreter for Lua filters is also used to setup the interpreter of custom writers.lua. - + Define instances for newtype wrapper. The custom writer used its own - `ToLuaStack` instance definitions, which made it difficult to share - code with Lua filters, as this could result in conflicting instances. - A `Stringify` wrapper is introduced to avoid this problem. + + Define instances for newtype wrapper (Albert Krewinkel). The custom + writer used its own `ToLuaStack` instance definitions, which made + it difficult to share code with Lua filters, as this could result + in conflicting instances. A `Stringify` wrapper is introduced to + avoid this problem. + + Added tests for custom writer. + + Fixed definition lists and tables in `data/sample.lua`. * Fixed regression: when target is PDF, writer extensions were being ignored. So, for example, `pandoc -t latex-smart -o file.pdf` @@ -189,10 +229,20 @@ pandoc (2.0.6) + pandoc.lua: re-add missing MetaMap function. This was a bug introduced in version 2.0.4. + * Text.Pandoc.Class: Add `insertInFileTree` [API change]. This gives + a pure way to insert an ersatz file into a `FileTree`. In addition, we + normalize paths both on insertion and on lookup. + * Text.Pandoc.Shared: export `blocksToInlines'` (API change, Maura Bieg). * Text.Pandoc.MIME: Add opus to MIME type table as audio/ogg (#4198). + * Text.Pandoc.Extensions: Alphabetical order constructors for + `Extension`. This makes them appear in order in `--list-extensions`. + + * Allow lenient decoding of latex error logs, which are not always + properly UTF8-encoded (#4200). + * Update latex template to work with recent versions of beamer. The old template produced numbered sections with some recent versions of beamer. Thanks to Thomas Hodgson. @@ -205,10 +255,23 @@ pandoc (2.0.6) * Removed `default.theme` data file (#4096). It is no longer needed now that we have `--print-highlight-style`. + * Added `stack.lts9.yaml` for building with lts 9 and ghc 8.0.2. + We still need this for the alpine static linux build, since + we don't have ghc 8.2.2 for that yet. + + * Removed `stack.pkg.yaml`. We only really need `stack.yaml`; we + can put flag settings for pandoc-citeproc there. + + * Makefile: Add 'trypandoc' and 'pandoc-templates' targets to + make releases easier. + * MANUAL.txt: + Add note on what formats have `+smart` by default. + Use native syntax for custom-style (#4174, Mauro Bieg). + + Introduce dedicated Extensions section, since some extensions + affect formats other than markdown (Mauro Bieg, #4204). + + Clarify default html output for `--section-divs` (Richard Edwards). * filters.md: say that Text.Pandoc.JSON comes form pandoc-types. Closes jgm/pandoc-website#16. @@ -255,7 +318,7 @@ pandoc (2.0.5) + Revert "Docx reader: don't strip out empty paragraphs." This reverts commit d6c58eb836f033a48955796de4d9ffb3b30e297b. + Implement `empty_paragraphs` extension in docx reader and writer, - opendocument writer, html reader and writer. + opendocument writer, HTML reader and writer. + Add tests for `empty_paragraphs` extension. * Markdown reader: @@ -535,6 +598,8 @@ pandoc (2.0.3) + Allow spaces after `\(` and before `\)` with `tex_math_single_backslash`. Previously `\( \frac{1}{a} < \frac{1}{b} \)` was not parsed as math in `markdown` or `html` `+tex_math_single_backslash`. + + Parse div with class `line-block` as LineBlock. + + Don't fail with block-level content in figcaption (Mauro Bieg, #4183). * MANUAL: clarify that math extensions work with HTML. Clarify that `tex_math_dollars` and `tex_math_single_backslash` diff --git a/data/sample.lua b/data/sample.lua index 1e3a08731..6c09442b5 100644 --- a/data/sample.lua +++ b/data/sample.lua @@ -242,14 +242,12 @@ function OrderedList(items) return "<ol>\n" .. table.concat(buffer, "\n") .. "\n</ol>" end --- Revisit association list STackValue instance. function DefinitionList(items) local buffer = {} for _,item in pairs(items) do - for k, v in pairs(item) do - table.insert(buffer,"<dt>" .. k .. "</dt>\n<dd>" .. - table.concat(v,"</dd>\n<dd>") .. "</dd>") - end + local k, v = next(item) + table.insert(buffer, "<dt>" .. k .. "</dt>\n<dd>" .. + table.concat(v, "</dd>\n<dd>") .. "</dd>") end return "<dl>\n" .. table.concat(buffer, "\n") .. "\n</dl>" end @@ -288,7 +286,7 @@ function Table(caption, aligns, widths, headers, rows) end if widths and widths[1] ~= 0 then for _, w in pairs(widths) do - add('<col width="' .. string.format("%d%%", w * 100) .. '" />') + add('<col width="' .. string.format("%.0f%%", w * 100) .. '" />') end end local header_row = {} diff --git a/doc/lua-filters.md b/doc/lua-filters.md index e9ed704ad..07de33259 100644 --- a/doc/lua-filters.md +++ b/doc/lua-filters.md @@ -88,11 +88,10 @@ then it would be applied like this: pandoc --lua-filter=current-date.lua -f markdown MANUAL.txt -The `--lua-filter` can be supplied multiple times, causing the -filters to be applied sequentially in the order they were given. -If other, non-Lua filters are given as well (via `--filter`), -then those are executed *after* all Lua filters have been -applied. +The `--lua-filter` option may be supplied multiple times. Pandoc +applies all filters (including JSON filters specified via +`--filter` and lua filters specified via `--lua-filter`) +in the order they appear on the command line. Pandoc expects each lua file to return a list of filters. The filters in that list are called sequentially, each on the result diff --git a/linux/Dockerfile b/linux/Dockerfile index 3660aa4fc..40d37d13d 100644 --- a/linux/Dockerfile +++ b/linux/Dockerfile @@ -12,9 +12,28 @@ RUN stack config set system-ghc --global true #RUN echo "build: { split-objs: true }" > /etc/stack/config.yaml RUN mkdir -p /usr/src/ WORKDIR /usr/src/ -RUN git clone https://github.com/jgm/pandoc +RUN git clone https://github.com/jgm/pandoc WORKDIR /usr/src/pandoc -RUN stack install --stack-yaml stack.pkg.yaml --only-dependencies \ +RUN stack install --stack-yaml stack.lts9.yaml \ + --only-dependencies \ + --flag 'pandoc:static' \ + --flag 'pandoc:embed_data_files' \ + --flag 'pandoc-citeproc:static' \ + --flag 'pandoc-citeproc:embed_data_files' \ + --flag 'hslua:-export-dynamic' \ + --ghc-options '-O2 -optc-Os -optl=-pthread -optl=-static -fPIC' \ + aeson +RUN stack install --stack-yaml stack.lts9.yaml \ + --only-dependencies \ + --flag 'pandoc:static' \ + --flag 'pandoc:embed_data_files' \ + --flag 'pandoc-citeproc:static' \ + --flag 'pandoc-citeproc:embed_data_files' \ + --flag 'hslua:-export-dynamic' \ + --ghc-options '-O2 -optc-Os -optl=-pthread -optl=-static -fPIC' \ + http-client-tls +RUN stack install --stack-yaml stack.lts9.yaml \ + --only-dependencies \ --flag 'pandoc:static' \ --flag 'pandoc:embed_data_files' \ --flag 'pandoc-citeproc:static' \ @@ -24,7 +43,7 @@ RUN stack install --stack-yaml stack.pkg.yaml --only-dependencies \ pandoc pandoc-citeproc CMD git pull && \ git checkout -b work $TREE && \ - stack install --stack-yaml stack.pkg.yaml \ + stack install --stack-yaml stack.lts9.yaml \ --flag 'pandoc:static' \ --flag 'pandoc:embed_data_files' \ --flag 'pandoc-citeproc:static' \ diff --git a/macos/make_macos_package.sh b/macos/make_macos_package.sh index 4b565b0de..85155c66c 100755 --- a/macos/make_macos_package.sh +++ b/macos/make_macos_package.sh @@ -27,7 +27,7 @@ stack setup echo Building pandoc... stack clean -stack install --ghc-options="-O2" --stack-yaml=stack.pkg.yaml --local-bin-path $DEST/bin/ pandoc pandoc-citeproc +stack install --ghc-options="-O2" --local-bin-path $DEST/bin/ pandoc pandoc-citeproc strip $DEST/bin/pandoc strip $DEST/bin/pandoc-citeproc diff --git a/man/pandoc.1 b/man/pandoc.1 index 64db3ae51..ba809e78b 100644 --- a/man/pandoc.1 +++ b/man/pandoc.1 @@ -1,5 +1,5 @@ .\"t -.TH PANDOC 1 "December 8, 2017" "pandoc 2.0.5" +.TH PANDOC 1 "December 27, 2017" "pandoc 2.0.6" .SH NAME pandoc - general markup converter .SH SYNOPSIS @@ -12,16 +12,16 @@ another, and a command\-line tool that uses this library. It can read Markdown, CommonMark, PHP Markdown Extra, GitHub\-Flavored Markdown, MultiMarkdown, and (subsets of) Textile, reStructuredText, HTML, LaTeX, MediaWiki markup, TWiki markup, TikiWiki markup, Creole -1.0, Haddock markup, OPML, Emacs Org mode, DocBook, Muse, txt2tags, -Vimwiki, EPUB, ODT, and Word docx; and it can write plain text, -Markdown, CommonMark, PHP Markdown Extra, GitHub\-Flavored Markdown, -MultiMarkdown, reStructuredText, XHTML, HTML5, LaTeX (including -\f[C]beamer\f[] slide shows), ConTeXt, RTF, OPML, DocBook, OpenDocument, -ODT, Word docx, GNU Texinfo, MediaWiki markup, DokuWiki markup, ZimWiki -markup, Haddock markup, EPUB (v2 or v3), FictionBook2, Textile, groff -man, groff ms, Emacs Org mode, AsciiDoc, InDesign ICML, TEI Simple, -Muse, PowerPoint slide shows and Slidy, Slideous, DZSlides, reveal.js or -S5 HTML slide shows. +1.0, Haddock markup, OPML, Emacs Org mode, DocBook, JATS, Muse, +txt2tags, Vimwiki, EPUB, ODT, and Word docx; and it can write plain +text, Markdown, CommonMark, PHP Markdown Extra, GitHub\-Flavored +Markdown, MultiMarkdown, reStructuredText, XHTML, HTML5, LaTeX +(including \f[C]beamer\f[] slide shows), ConTeXt, RTF, OPML, DocBook, +JATS, OpenDocument, ODT, Word docx, GNU Texinfo, MediaWiki markup, +DokuWiki markup, ZimWiki markup, Haddock markup, EPUB (v2 or v3), +FictionBook2, Textile, groff man, groff ms, Emacs Org mode, AsciiDoc, +InDesign ICML, TEI Simple, Muse, PowerPoint slide shows and Slidy, +Slideous, DZSlides, reveal.js or S5 HTML slide shows. It can also produce PDF output on systems where LaTeX, ConTeXt, \f[C]pdfroff\f[], \f[C]wkhtmltopdf\f[], \f[C]prince\f[], or \f[C]weasyprint\f[] is installed. @@ -242,19 +242,10 @@ markup), \f[C]tikiwiki\f[] (TikiWiki markup), \f[C]creole\f[] (Creole 1.0), \f[C]haddock\f[] (Haddock markup), or \f[C]latex\f[] (LaTeX). (\f[C]markdown_github\f[] provides deprecated and less accurate support for Github\-Flavored Markdown; please use \f[C]gfm\f[] instead, unless -you need to use extensions other than \f[C]smart\f[].) If \f[C]+lhs\f[] -is appended to \f[C]markdown\f[], \f[C]rst\f[], \f[C]latex\f[], or -\f[C]html\f[], the input will be treated as literate Haskell source: see -Literate Haskell support, below. -Markdown syntax extensions can be individually enabled or disabled by -appending \f[C]+EXTENSION\f[] or \f[C]\-EXTENSION\f[] to the format -name. -So, for example, \f[C]markdown_strict+footnotes+definition_lists\f[] is -strict Markdown with footnotes and definition lists enabled, and -\f[C]markdown\-pipe_tables+hard_line_breaks\f[] is pandoc\[aq]s Markdown -without pipe tables and with hard line breaks. -See Pandoc\[aq]s Markdown, below, for a list of extensions and their -names. +you need to use extensions other than \f[C]smart\f[].) Extensions can be +individually enabled or disabled by appending \f[C]+EXTENSION\f[] or +\f[C]\-EXTENSION\f[] to the format name. +See Extensions below, for a list of extensions and their names. See \f[C]\-\-list\-input\-formats\f[] and \f[C]\-\-list\-extensions\f[], below. .RS @@ -295,13 +286,9 @@ you use extensions that do not work with \f[C]gfm\f[].) Note that \f[C]odt\f[], \f[C]epub\f[], and \f[C]epub3\f[] output will not be directed to \f[I]stdout\f[]; an output filename must be specified using the \f[C]\-o/\-\-output\f[] option. -If \f[C]+lhs\f[] is appended to \f[C]markdown\f[], \f[C]rst\f[], -\f[C]latex\f[], \f[C]beamer\f[], \f[C]html4\f[], or \f[C]html5\f[], the -output will be rendered as literate Haskell source: see Literate Haskell -support, below. -Markdown syntax extensions can be individually enabled or disabled by -appending \f[C]+EXTENSION\f[] or \f[C]\-EXTENSION\f[] to the format -name, as described above under \f[C]\-f\f[]. +Extensions can be individually enabled or disabled by appending +\f[C]+EXTENSION\f[] or \f[C]\-EXTENSION\f[] to the format name. +See Extensions below, for a list of extensions and their names. See \f[C]\-\-list\-output\-formats\f[] and \f[C]\-\-list\-extensions\f[], below. .RS @@ -398,10 +385,12 @@ List supported output formats, one per line. .RS .RE .TP -.B \f[C]\-\-list\-extensions\f[] -List supported Markdown extensions, one per line, followed by a +.B \f[C]\-\-list\-extensions\f[][\f[C]=\f[]\f[I]FORMAT\f[]] +List supported Markdown extensions, one per line, preceded by a \f[C]+\f[] or \f[C]\-\f[] indicating whether it is enabled by default in -pandoc\[aq]s Markdown. +\f[I]FORMAT\f[]. +If \f[I]FORMAT\f[] is not specified, defaults for pandoc\[aq]s Markdown +are given. .RS .RE .TP @@ -951,9 +940,9 @@ document; see Structuring the slide show. .RE .TP .B \f[C]\-\-section\-divs\f[] -Wrap sections in \f[C]<div>\f[] tags (or \f[C]<section>\f[] tags in -HTML5), and attach identifiers to the enclosing \f[C]<div>\f[] (or -\f[C]<section>\f[]) rather than the header itself. +Wrap sections in \f[C]<section>\f[] tags (or \f[C]<div>\f[] tags for +\f[C]html4\f[]), and attach identifiers to the enclosing +\f[C]<section>\f[] (or \f[C]<div>\f[]) rather than the header itself. See Header identifiers, below. .RS .RE @@ -2035,6 +2024,336 @@ merge in changes after each pandoc release. .PP Templates may contain comments: anything on a line after \f[C]$\-\-\f[] will be treated as a comment and ignored. +.SH EXTENSIONS +.PP +The behavior of some of the readers and writers can be adjusted by +enabling or disabling various extensions. +.PP +An extension can be enabled by adding \f[C]+EXTENSION\f[] to the format +name and disabled by adding \f[C]\-EXTENSION\f[]. +For example, \f[C]\-\-from\ markdown_strict+footnotes\f[] is strict +Markdown with footnotes enabled, while +\f[C]\-\-from\ markdown\-footnotes\-pipe_tables\f[] is pandoc\[aq]s +Markdown without footnotes or pipe tables. +.PP +The markdown reader and writer make by far the most use of extensions. +Extensions only used by them are therefore covered in the section +Pandoc\[aq]s Markdown below (See Markdown variants for +\f[C]commonmark\f[] and \f[C]gfm\f[].) In the following, extensions that +also work for other formats are covered. +.SS Typography +.SS Extension: \f[C]smart\f[] +.PP +Interpret straight quotes as curly quotes, \f[C]\-\-\-\f[] as +em\-dashes, \f[C]\-\-\f[] as en\-dashes, and \f[C]\&...\f[] as ellipses. +Nonbreaking spaces are inserted after certain abbreviations, such as +"Mr." +.PP +This extension can be enabled/disabled for the following formats: +.TP +.B input formats +\f[C]markdown\f[], \f[C]commonmark\f[], \f[C]latex\f[], +\f[C]mediawiki\f[], \f[C]org\f[], \f[C]rst\f[], \f[C]twiki\f[] +.RS +.RE +.TP +.B output formats +\f[C]markdown\f[], \f[C]latex\f[], \f[C]context\f[], \f[C]rst\f[] +.RS +.RE +.TP +.B enabled by default in +\f[C]markdown\f[], \f[C]latex\f[], \f[C]context\f[] (both input and +output) +.RS +.RE +.PP +Note: If you are \f[I]writing\f[] Markdown, then the \f[C]smart\f[] +extension has the reverse effect: what would have been curly quotes +comes out straight. +.PP +In LaTeX, \f[C]smart\f[] means to use the standard TeX ligatures for +quotation marks (\f[C]``\f[] and \f[C]\[aq]\[aq]\f[] for double quotes, +\f[C]`\f[] and \f[C]\[aq]\f[] for single quotes) and dashes +(\f[C]\-\-\f[] for en\-dash and \f[C]\-\-\-\f[] for em\-dash). +If \f[C]smart\f[] is disabled, then in reading LaTeX pandoc will parse +these characters literally. +In writing LaTeX, enabling \f[C]smart\f[] tells pandoc to use the +ligatures when possible; if \f[C]smart\f[] is disabled pandoc will use +unicode quotation mark and dash characters. +.SS Headers and sections +.SS Extension: \f[C]auto_identifiers\f[] +.PP +A header without an explicitly specified identifier will be +automatically assigned a unique identifier based on the header text. +.PP +This extension can be enabled/disabled for the following formats: +.TP +.B input formats +\f[C]markdown\f[], \f[C]latex\f[], \f[C]rst\f[], \f[C]mediawiki\f[], +\f[C]textile\f[] +.RS +.RE +.TP +.B output formats +\f[C]markdown\f[], \f[C]muse\f[] +.RS +.RE +.TP +.B enabled by default in +\f[C]markdown\f[], \f[C]muse\f[] +.RS +.RE +.PP +The algorithm used to derive the identifier from the header text is: +.IP \[bu] 2 +Remove all formatting, links, etc. +.IP \[bu] 2 +Remove all footnotes. +.IP \[bu] 2 +Remove all punctuation, except underscores, hyphens, and periods. +.IP \[bu] 2 +Replace all spaces and newlines with hyphens. +.IP \[bu] 2 +Convert all alphabetic characters to lowercase. +.IP \[bu] 2 +Remove everything up to the first letter (identifiers may not begin with +a number or punctuation mark). +.IP \[bu] 2 +If nothing is left after this, use the identifier \f[C]section\f[]. +.PP +Thus, for example, +.PP +.TS +tab(@); +l l. +T{ +Header +T}@T{ +Identifier +T} +_ +T{ +\f[C]Header\ identifiers\ in\ HTML\f[] +T}@T{ +\f[C]header\-identifiers\-in\-html\f[] +T} +T{ +\f[C]*Dogs*?\-\-in\ *my*\ house?\f[] +T}@T{ +\f[C]dogs\-\-in\-my\-house\f[] +T} +T{ +\f[C][HTML],\ [S5],\ or\ [RTF]?\f[] +T}@T{ +\f[C]html\-s5\-or\-rtf\f[] +T} +T{ +\f[C]3.\ Applications\f[] +T}@T{ +\f[C]applications\f[] +T} +T{ +\f[C]33\f[] +T}@T{ +\f[C]section\f[] +T} +.TE +.PP +These rules should, in most cases, allow one to determine the identifier +from the header text. +The exception is when several headers have the same text; in this case, +the first will get an identifier as described above; the second will get +the same identifier with \f[C]\-1\f[] appended; the third with +\f[C]\-2\f[]; and so on. +.PP +These identifiers are used to provide link targets in the table of +contents generated by the \f[C]\-\-toc|\-\-table\-of\-contents\f[] +option. +They also make it easy to provide links from one section of a document +to another. +A link to this section, for example, might look like this: +.IP +.nf +\f[C] +See\ the\ section\ on +[header\ identifiers](#header\-identifiers\-in\-html\-latex\-and\-context). +\f[] +.fi +.PP +Note, however, that this method of providing links to sections works +only in HTML, LaTeX, and ConTeXt formats. +.PP +If the \f[C]\-\-section\-divs\f[] option is specified, then each section +will be wrapped in a \f[C]section\f[] (or a \f[C]div\f[], if +\f[C]html4\f[] was specified), and the identifier will be attached to +the enclosing \f[C]<section>\f[] (or \f[C]<div>\f[]) tag rather than the +header itself. +This allows entire sections to be manipulated using JavaScript or +treated differently in CSS. +.SS Extension: \f[C]ascii_identifiers\f[] +.PP +Causes the identifiers produced by \f[C]auto_identifiers\f[] to be pure +ASCII. +Accents are stripped off of accented Latin letters, and non\-Latin +letters are omitted. +.SS Math Input +.PP +The extensions \f[C]tex_math_dollars\f[], +\f[C]tex_math_single_backslash\f[], and +\f[C]tex_math_double_backslash\f[] are described in the section about +Pandoc\[aq]s Markdown. +.PP +However, they can also be used with HTML input. +This is handy for reading web pages formatted using MathJax, for +example. +.SS Raw HTML/TeX +.PP +The following extensions (especially how they affect Markdown +input/output) are also described in more detail in their respective +sections of Pandoc\[aq]s Markdown. +.SS Extension: \f[C]raw_html\f[] +.PP +When converting from HTML, parse elements to raw HTML which are not +representable in pandoc\[aq]s AST. +By default, this is disabled for HTML input. +.SS Extension: \f[C]raw_tex\f[] +.PP +Allows raw LaTeX, TeX, and ConTeXt to be included in a document. +.PP +This extension can be enabled/disabled for the following formats (in +addition to \f[C]markdown\f[]): +.TP +.B input formats +\f[C]latex\f[], \f[C]org\f[], \f[C]textile\f[] +.RS +.RE +.TP +.B output formats +\f[C]textile\f[] +.RS +.RE +.SS Extension: \f[C]native_divs\f[] +.PP +This extension is enabled by default for HTML input. +This means that \f[C]div\f[]s are parsed to pandoc native elements. +(Alternatively, you can parse them to raw HTML using +\f[C]\-f\ html\-native_divs+raw_html\f[].) +.PP +When converting HTML to Markdown, for example, you may want to drop all +\f[C]div\f[]s and \f[C]span\f[]s: +.IP +.nf +\f[C] +pandoc\ \-f\ html\-native_divs\-native_spans\ \-t\ markdown +\f[] +.fi +.SS Extension: \f[C]native_spans\f[] +.PP +Analogous to \f[C]native_divs\f[] above. +.SS Literate Haskell support +.SS Extension: \f[C]literate_haskell\f[] +.PP +Treat the document as literate Haskell source. +.PP +This extension can be enabled/disabled for the following formats: +.TP +.B input formats +\f[C]markdown\f[], \f[C]rst\f[], \f[C]latex\f[] +.RS +.RE +.TP +.B output formats +\f[C]markdown\f[], \f[C]rst\f[], \f[C]latex\f[], \f[C]html\f[] +.RS +.RE +.PP +If you append \f[C]+lhs\f[] (or \f[C]+literate_haskell\f[]) to one of +the formats above, pandoc will treat the document as literate Haskell +source. +This means that +.IP \[bu] 2 +In Markdown input, "bird track" sections will be parsed as Haskell code +rather than block quotations. +Text between \f[C]\\begin{code}\f[] and \f[C]\\end{code}\f[] will also +be treated as Haskell code. +For ATX\-style headers the character \[aq]=\[aq] will be used instead of +\[aq]#\[aq]. +.IP \[bu] 2 +In Markdown output, code blocks with classes \f[C]haskell\f[] and +\f[C]literate\f[] will be rendered using bird tracks, and block +quotations will be indented one space, so they will not be treated as +Haskell code. +In addition, headers will be rendered setext\-style (with underlines) +rather than ATX\-style (with \[aq]#\[aq] characters). +(This is because ghc treats \[aq]#\[aq] characters in column 1 as +introducing line numbers.) +.IP \[bu] 2 +In restructured text input, "bird track" sections will be parsed as +Haskell code. +.IP \[bu] 2 +In restructured text output, code blocks with class \f[C]haskell\f[] +will be rendered using bird tracks. +.IP \[bu] 2 +In LaTeX input, text in \f[C]code\f[] environments will be parsed as +Haskell code. +.IP \[bu] 2 +In LaTeX output, code blocks with class \f[C]haskell\f[] will be +rendered inside \f[C]code\f[] environments. +.IP \[bu] 2 +In HTML output, code blocks with class \f[C]haskell\f[] will be rendered +with class \f[C]literatehaskell\f[] and bird tracks. +.PP +Examples: +.IP +.nf +\f[C] +pandoc\ \-f\ markdown+lhs\ \-t\ html +\f[] +.fi +.PP +reads literate Haskell source formatted with Markdown conventions and +writes ordinary HTML (without bird tracks). +.IP +.nf +\f[C] +pandoc\ \-f\ markdown+lhs\ \-t\ html+lhs +\f[] +.fi +.PP +writes HTML with the Haskell code in bird tracks, so it can be copied +and pasted as literate Haskell source. +.PP +Note that GHC expects the bird tracks in the first column, so indentend +literate code blocks (e.g. +inside an itemized environment) will not be picked up by the Haskell +compiler. +.SS Other extensions +.SS Extension: \f[C]empty_paragraphs\f[] +.PP +Allows empty paragraphs. +By default empty paragraphs are omitted. +.PP +This extension can be enabled/disabled for the following formats: +.TP +.B input formats +\f[C]docx\f[], \f[C]html\f[] +.RS +.RE +.TP +.B output formats +\f[C]markdown\f[], \f[C]docx\f[], \f[C]odt\f[], \f[C]opendocument\f[], +\f[C]html\f[] +.RS +.RE +.SS Extension: \f[C]amuse\f[] +.PP +In the \f[C]muse\f[] input format, this enables Text::Amuse extensions +to Emacs Muse markup. +.SS Extension: \f[C]citations\f[] +.PP +Some aspects of Pandoc\[aq]s Markdown citation syntax are also accepted +in \f[C]org\f[] input. .SH PANDOC\[aq]S MARKDOWN .PP Pandoc understands an extended and slightly revised version of John @@ -2043,11 +2362,11 @@ This document explains the syntax, noting differences from standard Markdown. Except where noted, these differences can be suppressed by using the \f[C]markdown_strict\f[] format instead of \f[C]markdown\f[]. -An extensions can be enabled by adding \f[C]+EXTENSION\f[] to the format -name and disabled by adding \f[C]\-EXTENSION\f[]. -For example, \f[C]markdown_strict+footnotes\f[] is strict Markdown with -footnotes enabled, while \f[C]markdown\-footnotes\-pipe_tables\f[] is -pandoc\[aq]s Markdown without footnotes or pipe tables. +Extensions can be enabled or disabled to specify the behavior more +granularly. +They are described in the following. +See also Extensions above, for extensions that work also on other +formats. .SS Philosophy .PP Markdown is designed to be easy to write, and, even more importantly, @@ -2149,6 +2468,8 @@ Many Markdown implementations do not require a space between the opening \f[C]#5\ bolt\f[] and \f[C]#hashtag\f[] count as headers. With this extension, pandoc does require the space. .SS Header identifiers +.PP +See also the \f[C]auto_identifiers\f[] extension above. .SS Extension: \f[C]header_attributes\f[] .PP Headers can be assigned attributes using this syntax at the end of the @@ -2203,96 +2524,6 @@ is just the same as #\ My\ header\ {.unnumbered} \f[] .fi -.SS Extension: \f[C]auto_identifiers\f[] -.PP -A header without an explicitly specified identifier will be -automatically assigned a unique identifier based on the header text. -To derive the identifier from the header text, -.IP \[bu] 2 -Remove all formatting, links, etc. -.IP \[bu] 2 -Remove all footnotes. -.IP \[bu] 2 -Remove all punctuation, except underscores, hyphens, and periods. -.IP \[bu] 2 -Replace all spaces and newlines with hyphens. -.IP \[bu] 2 -Convert all alphabetic characters to lowercase. -.IP \[bu] 2 -Remove everything up to the first letter (identifiers may not begin with -a number or punctuation mark). -.IP \[bu] 2 -If nothing is left after this, use the identifier \f[C]section\f[]. -.PP -Thus, for example, -.PP -.TS -tab(@); -l l. -T{ -Header -T}@T{ -Identifier -T} -_ -T{ -\f[C]Header\ identifiers\ in\ HTML\f[] -T}@T{ -\f[C]header\-identifiers\-in\-html\f[] -T} -T{ -\f[C]*Dogs*?\-\-in\ *my*\ house?\f[] -T}@T{ -\f[C]dogs\-\-in\-my\-house\f[] -T} -T{ -\f[C][HTML],\ [S5],\ or\ [RTF]?\f[] -T}@T{ -\f[C]html\-s5\-or\-rtf\f[] -T} -T{ -\f[C]3.\ Applications\f[] -T}@T{ -\f[C]applications\f[] -T} -T{ -\f[C]33\f[] -T}@T{ -\f[C]section\f[] -T} -.TE -.PP -These rules should, in most cases, allow one to determine the identifier -from the header text. -The exception is when several headers have the same text; in this case, -the first will get an identifier as described above; the second will get -the same identifier with \f[C]\-1\f[] appended; the third with -\f[C]\-2\f[]; and so on. -.PP -These identifiers are used to provide link targets in the table of -contents generated by the \f[C]\-\-toc|\-\-table\-of\-contents\f[] -option. -They also make it easy to provide links from one section of a document -to another. -A link to this section, for example, might look like this: -.IP -.nf -\f[C] -See\ the\ section\ on -[header\ identifiers](#header\-identifiers\-in\-html\-latex\-and\-context). -\f[] -.fi -.PP -Note, however, that this method of providing links to sections works -only in HTML, LaTeX, and ConTeXt formats. -.PP -If the \f[C]\-\-section\-divs\f[] option is specified, then each section -will be wrapped in a \f[C]div\f[] (or a \f[C]section\f[], if -\f[C]html5\f[] was specified), and the identifier will be attached to -the enclosing \f[C]<div>\f[] (or \f[C]<section>\f[]) tag rather than the -header itself. -This allows entire sections to be manipulated using JavaScript or -treated differently in CSS. .SS Extension: \f[C]implicit_header_references\f[] .PP Pandoc behaves as if reference links have been defined for each header. @@ -2911,6 +3142,13 @@ As\ (\@good)\ illustrates,\ ... .PP The label can be any string of alphanumeric characters, underscores, or hyphens. +.PP +Note: continuation paragraphs in example lists must always be indented +four spaces, regardless of the length of the list marker. +That is, example lists always behave as if the \f[C]four_space_rule\f[] +extension is set. +This is because example labels tend to be long, and indenting content to +the first non\-space character after the label would be awkward. .SS Compact and loose lists .PP Pandoc behaves differently from \f[C]Markdown.pl\f[] on some "edge @@ -3756,9 +3994,6 @@ options selected. Therefore see Math rendering in HTML above. .RS .RE -.PP -This extension can be used with both \f[C]markdown\f[] and \f[C]html\f[] -input. .SS Raw HTML .SS Extension: \f[C]raw_html\f[] .PP @@ -3951,9 +4186,9 @@ The raw attribute cannot be combined with regular attributes. .SS LaTeX macros .SS Extension: \f[C]latex_macros\f[] .PP -For output formats other than LaTeX, pandoc will parse LaTeX -\f[C]\\newcommand\f[] and \f[C]\\renewcommand\f[] definitions and apply -the resulting macros to all LaTeX math. +For output formats other than LaTeX, pandoc will parse LaTeX macro +definitions and apply the resulting macros to all LaTeX math and raw +LaTeX. So, for example, the following will work in all output formats, not just LaTeX: .IP @@ -3965,8 +4200,13 @@ $\\tuple{a,\ b,\ c}$ \f[] .fi .PP -In LaTeX output, the \f[C]\\newcommand\f[] definition will simply be -passed unchanged to the output. +In LaTeX output, the macro definitions will not be passed through as raw +LaTeX. +.PP +When \f[C]latex_macros\f[] is disabled, the macro definitions will be +passed through as raw LaTeX, and the raw LaTeX and math will not have +macros applied. +This is usually a better approach when you are targeting LaTeX or PDF. .SS Links .PP Markdown allows links to be specified in several ways. @@ -4288,30 +4528,6 @@ note.] .fi .PP Inline and regular footnotes may be mixed freely. -.SS Typography -.SS Extension: \f[C]smart\f[] -.PP -Interpret straight quotes as curly quotes, \f[C]\-\-\-\f[] as -em\-dashes, \f[C]\-\-\f[] as en\-dashes, and \f[C]\&...\f[] as ellipses. -Nonbreaking spaces are inserted after certain abbreviations, such as -"Mr." This option currently affects the input formats \f[C]markdown\f[], -\f[C]commonmark\f[], \f[C]latex\f[], \f[C]mediawiki\f[], \f[C]org\f[], -\f[C]rst\f[], and \f[C]twiki\f[], and the output formats -\f[C]markdown\f[], \f[C]latex\f[], and \f[C]context\f[]. -.PP -Note: If you are \f[I]writing\f[] Markdown, then the \f[C]smart\f[] -extension has the reverse effect: what would have been curly quotes -comes out straight. -.PP -In LaTeX, \f[C]smart\f[] means to use the standard TeX ligatures for -quotation marks (\f[C]``\f[] and \f[C]\[aq]\[aq]\f[] for double quotes, -\f[C]`\f[] and \f[C]\[aq]\f[] for single quotes) and dashes -(\f[C]\-\-\f[] for en\-dash and \f[C]\-\-\-\f[] for em\-dash). -If \f[C]smart\f[] is disabled, then in reading LaTeX pandoc will parse -these characters literally. -In writing LaTeX, enabling \f[C]smart\f[] tells pandoc to use the -ligatures when possible; if \f[C]smart\f[] is disabled pandoc will use -unicode quotation mark and dash characters. .SS Citations .SS Extension: \f[C]citations\f[] .PP @@ -4675,9 +4891,6 @@ as inline TeX math, and anything between \f[C]\\[\f[] and \f[C]\\]\f[] to be interpreted as display TeX math. Note: a drawback of this extension is that it precludes escaping \f[C](\f[] and \f[C][\f[]. -.PP -This extension can be used with both \f[C]markdown\f[] and \f[C]html\f[] -input. .SS Extension: \f[C]tex_math_double_backslash\f[] .PP Causes anything between \f[C]\\\\(\f[] and \f[C]\\\\)\f[] to be @@ -4725,12 +4938,6 @@ opposed to being parsed as paragraphs). .PP Makes all absolute URIs into links, even when not surrounded by pointy braces \f[C]<...>\f[]. -.SS Extension: \f[C]ascii_identifiers\f[] -.PP -Causes the identifiers produced by \f[C]auto_identifiers\f[] to be pure -ASCII. -Accents are stripped off of accented Latin letters, and non\-Latin -letters are omitted. .SS Extension: \f[C]mmd_link_attributes\f[] .PP Parses multimarkdown style key\-value attributes on link and image @@ -4765,13 +4972,6 @@ anything. .IP \[bu] 2 Lazy wrapping of paragraphs is not allowed: the entire definition must be indented four spaces. -.SS Extension: \f[C]empty_paragraphs\f[] -.PP -Allows empty paragraphs. -By default empty paragraphs are omitted. -This affects the \f[C]docx\f[] reader and writer, the -\f[C]opendocument\f[] and \f[C]odt\f[] writer, and all HTML\-based -readers and writers. .SS Markdown variants .PP In addition to pandoc\[aq]s extended Markdown, the following Markdown @@ -4817,37 +5017,26 @@ variants are supported: .RS .RE .PP -We also support \f[C]gfm\f[] (GitHub\-Flavored Markdown) as a set of -extensions on \f[C]commonmark\f[]: +We also support \f[C]commonmark\f[] and \f[C]gfm\f[] (GitHub\-Flavored +Markdown, which is implemented as a set of extensions on +\f[C]commonmark\f[]). .PP -: \f[C]pipe_tables\f[], \f[C]raw_html\f[], \f[C]fenced_code_blocks\f[], +Note, however, that \f[C]commonmark\f[] and \f[C]gfm\f[] have limited +support for extensions. +Only those listed below (and \f[C]smart\f[] and \f[C]raw_tex\f[]) will +work. +The extensions can, however, all be individually disabled. +Also, \f[C]raw_tex\f[] only affects \f[C]gfm\f[] output, not input. +.TP +.B \f[C]gfm\f[] (GitHub\-Flavored Markdown) +\f[C]pipe_tables\f[], \f[C]raw_html\f[], \f[C]fenced_code_blocks\f[], \f[C]auto_identifiers\f[], \f[C]ascii_identifiers\f[], \f[C]backtick_code_blocks\f[], \f[C]autolink_bare_uris\f[], \f[C]intraword_underscores\f[], \f[C]strikeout\f[], \f[C]hard_line_breaks\f[], \f[C]emoji\f[], \f[C]shortcut_reference_links\f[], \f[C]angle_brackets_escapable\f[]. -.IP -.nf -\f[C] -These\ can\ all\ be\ individually\ disabled.\ Note,\ however,\ that -`commonmark`\ and\ `gfm`\ have\ limited\ support\ for\ extensions: -extensions\ other\ than\ those\ listed\ above\ (and\ `smart`\ and -`raw_tex`)\ will\ have\ no\ effect\ on\ `commonmark`\ or\ `gfm`. -And\ `raw_tex`\ only\ affects\ `gfm`\ output,\ not\ input. -\f[] -.fi -.SS Extensions with formats other than Markdown -.PP -Some of the extensions discussed above can be used with formats other -than Markdown: -.IP \[bu] 2 -\f[C]auto_identifiers\f[] can be used with \f[C]latex\f[], \f[C]rst\f[], -\f[C]mediawiki\f[], and \f[C]textile\f[] input (and is used by default). -.IP \[bu] 2 -\f[C]tex_math_dollars\f[], \f[C]tex_math_single_backslash\f[], and -\f[C]tex_math_double_backslash\f[] can be used with \f[C]html\f[] input. -(This is handy for reading web pages formatted using MathJax, for -example.) +.RS +.RE .SH PRODUCING SLIDE SHOWS WITH PANDOC .PP You can use pandoc to produce an HTML + JavaScript slide presentation @@ -5299,70 +5488,6 @@ For example: </audio> \f[] .fi -.SH LITERATE HASKELL SUPPORT -.PP -If you append \f[C]+lhs\f[] (or \f[C]+literate_haskell\f[]) to an -appropriate input or output format (\f[C]markdown\f[], -\f[C]markdown_strict\f[], \f[C]rst\f[], or \f[C]latex\f[] for input or -output; \f[C]beamer\f[], \f[C]html4\f[] or \f[C]html5\f[] for output -only), pandoc will treat the document as literate Haskell source. -This means that -.IP \[bu] 2 -In Markdown input, "bird track" sections will be parsed as Haskell code -rather than block quotations. -Text between \f[C]\\begin{code}\f[] and \f[C]\\end{code}\f[] will also -be treated as Haskell code. -For ATX\-style headers the character \[aq]=\[aq] will be used instead of -\[aq]#\[aq]. -.IP \[bu] 2 -In Markdown output, code blocks with classes \f[C]haskell\f[] and -\f[C]literate\f[] will be rendered using bird tracks, and block -quotations will be indented one space, so they will not be treated as -Haskell code. -In addition, headers will be rendered setext\-style (with underlines) -rather than ATX\-style (with \[aq]#\[aq] characters). -(This is because ghc treats \[aq]#\[aq] characters in column 1 as -introducing line numbers.) -.IP \[bu] 2 -In restructured text input, "bird track" sections will be parsed as -Haskell code. -.IP \[bu] 2 -In restructured text output, code blocks with class \f[C]haskell\f[] -will be rendered using bird tracks. -.IP \[bu] 2 -In LaTeX input, text in \f[C]code\f[] environments will be parsed as -Haskell code. -.IP \[bu] 2 -In LaTeX output, code blocks with class \f[C]haskell\f[] will be -rendered inside \f[C]code\f[] environments. -.IP \[bu] 2 -In HTML output, code blocks with class \f[C]haskell\f[] will be rendered -with class \f[C]literatehaskell\f[] and bird tracks. -.PP -Examples: -.IP -.nf -\f[C] -pandoc\ \-f\ markdown+lhs\ \-t\ html -\f[] -.fi -.PP -reads literate Haskell source formatted with Markdown conventions and -writes ordinary HTML (without bird tracks). -.IP -.nf -\f[C] -pandoc\ \-f\ markdown+lhs\ \-t\ html+lhs -\f[] -.fi -.PP -writes HTML with the Haskell code in bird tracks, so it can be copied -and pasted as literate Haskell source. -.PP -Note that GHC expects the bird tracks in the first column, so indentend -literate code blocks (e.g. -inside an itemized environment) will not be picked up by the Haskell -compiler. .SH SYNTAX HIGHLIGHTING .PP Pandoc will automatically highlight syntax in fenced code blocks that @@ -5395,26 +5520,26 @@ blocks and text using \f[C]div\f[]s and \f[C]span\f[]s, respectively. If you define a \f[C]div\f[] or \f[C]span\f[] with the attribute \f[C]custom\-style\f[], pandoc will apply your specified style to the contained elements. -So, for example, +So, for example using the \f[C]bracketed_spans\f[] syntax, .IP .nf \f[C] -<span\ custom\-style="Emphatically">Get\ out,</span>\ he\ said. +[Get\ out]{custom\-style="Emphatically"},\ he\ said. \f[] .fi .PP -would produce a docx file with "Get out," styled with character style +would produce a docx file with "Get out" styled with character style \f[C]Emphatically\f[]. -Similarly, +Similarly, using the \f[C]fenced_divs\f[] syntax, .IP .nf \f[C] Dickinson\ starts\ the\ poem\ simply: -<div\ custom\-style="Poetry"> +:::\ {custom\-style="Poetry"} |\ A\ Bird\ came\ down\ the\ Walk\-\-\- |\ He\ did\ not\ know\ I\ saw\-\-\- -</div> +::: \f[] .fi .PP diff --git a/pandoc.cabal b/pandoc.cabal index 73f51af0f..dea141a8f 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -1,5 +1,5 @@ name: pandoc -version: 2.0.6 +version: 2.1 cabal-version: >= 1.10 build-type: Custom license: GPL @@ -81,6 +81,7 @@ data-files: data/docx/docProps/core.xml data/docx/word/document.xml data/docx/word/fontTable.xml + data/docx/word/comments.xml data/docx/word/footnotes.xml data/docx/word/numbering.xml data/docx/word/settings.xml @@ -104,28 +105,28 @@ data-files: data/pptx/docProps/thumbnail.jpeg data/pptx/docProps/app.xml data/pptx/docProps/core.xml - data/pptx/ppt/slideLayouts/slideLayout4.xml - data/pptx/ppt/slideLayouts/_rels/slideLayout9.xml.rels - data/pptx/ppt/slideLayouts/_rels/slideLayout2.xml.rels - data/pptx/ppt/slideLayouts/_rels/slideLayout10.xml.rels data/pptx/ppt/slideLayouts/_rels/slideLayout1.xml.rels + data/pptx/ppt/slideLayouts/_rels/slideLayout2.xml.rels data/pptx/ppt/slideLayouts/_rels/slideLayout3.xml.rels + data/pptx/ppt/slideLayouts/_rels/slideLayout4.xml.rels data/pptx/ppt/slideLayouts/_rels/slideLayout5.xml.rels + data/pptx/ppt/slideLayouts/_rels/slideLayout6.xml.rels data/pptx/ppt/slideLayouts/_rels/slideLayout7.xml.rels data/pptx/ppt/slideLayouts/_rels/slideLayout8.xml.rels + data/pptx/ppt/slideLayouts/_rels/slideLayout9.xml.rels + data/pptx/ppt/slideLayouts/_rels/slideLayout10.xml.rels data/pptx/ppt/slideLayouts/_rels/slideLayout11.xml.rels - data/pptx/ppt/slideLayouts/_rels/slideLayout4.xml.rels - data/pptx/ppt/slideLayouts/_rels/slideLayout6.xml.rels + data/pptx/ppt/slideLayouts/slideLayout1.xml data/pptx/ppt/slideLayouts/slideLayout2.xml - data/pptx/ppt/slideLayouts/slideLayout8.xml - data/pptx/ppt/slideLayouts/slideLayout11.xml data/pptx/ppt/slideLayouts/slideLayout3.xml - data/pptx/ppt/slideLayouts/slideLayout6.xml - data/pptx/ppt/slideLayouts/slideLayout9.xml + data/pptx/ppt/slideLayouts/slideLayout4.xml data/pptx/ppt/slideLayouts/slideLayout5.xml + data/pptx/ppt/slideLayouts/slideLayout6.xml data/pptx/ppt/slideLayouts/slideLayout7.xml - data/pptx/ppt/slideLayouts/slideLayout1.xml + data/pptx/ppt/slideLayouts/slideLayout8.xml + data/pptx/ppt/slideLayouts/slideLayout9.xml data/pptx/ppt/slideLayouts/slideLayout10.xml + data/pptx/ppt/slideLayouts/slideLayout11.xml data/pptx/ppt/_rels/presentation.xml.rels data/pptx/ppt/theme/theme1.xml data/pptx/ppt/presProps.xml @@ -244,6 +245,7 @@ extra-source-files: test/tables.txt test/tables.fb2 test/tables.muse + test/tables.custom test/testsuite.txt test/writer.latex test/writer.context @@ -272,6 +274,7 @@ extra-source-files: test/writer.dokuwiki test/writer.zimwiki test/writer.muse + test/writer.custom test/writers-lang-and-dir.latex test/writers-lang-and-dir.context test/dokuwiki_inline_formatting.dokuwiki @@ -603,7 +606,9 @@ test-suite test-pandoc pandoc, pandoc-types >= 1.17.3 && < 1.18, bytestring >= 0.9 && < 0.11, + base64-bytestring >= 0.1 && < 1.1, text >= 0.11 && < 1.3, + time >= 1.5 && < 1.9, directory >= 1 && < 1.4, filepath >= 1.1 && < 1.5, hslua >= 0.9 && < 0.10, @@ -618,6 +623,11 @@ test-suite test-pandoc containers >= 0.4.2.1 && < 0.6, executable-path >= 0.0 && < 0.1, zip-archive >= 0.2.3.4 && < 0.4 + if flag(old-locale) + build-depends: old-locale >= 1 && < 1.1, + time >= 1.2 && < 1.5 + else + build-depends: time >= 1.5 && < 1.9 other-modules: Tests.Old Tests.Command Tests.Helpers @@ -628,6 +638,18 @@ test-suite test-pandoc Tests.Readers.JATS Tests.Readers.Markdown Tests.Readers.Org + Tests.Readers.Org.Block + Tests.Readers.Org.Block.CodeBlock + Tests.Readers.Org.Block.Figure + Tests.Readers.Org.Block.Header + Tests.Readers.Org.Block.List + Tests.Readers.Org.Block.Table + Tests.Readers.Org.Directive + Tests.Readers.Org.Inline + Tests.Readers.Org.Inline.Note + Tests.Readers.Org.Inline.Smart + Tests.Readers.Org.Meta + Tests.Readers.Org.Shared Tests.Readers.RST Tests.Readers.Docx Tests.Readers.Odt @@ -650,6 +672,7 @@ test-suite test-pandoc Tests.Writers.TEI Tests.Writers.Muse Tests.Writers.FB2 + Tests.Writers.Powerpoint ghc-options: -rtsopts -Wall -fno-warn-unused-do-bind -threaded default-language: Haskell98 diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index e597c56d6..7c463d743 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -35,11 +35,12 @@ Does a pandoc conversion based on command-line options. module Text.Pandoc.App ( convertWithOpts , Opt(..) + , LineEnding(..) + , Filter(..) , defaultOpts , parseOptions , options , applyFilters - , applyLuaFilters ) where import qualified Control.Exception as E import Control.Monad @@ -58,6 +59,9 @@ import Data.Monoid import qualified Data.Set as Set import Data.Text (Text) import qualified Data.Text as T +import qualified Data.Text.Lazy as TL +import qualified Data.Text.Lazy.Encoding as TE +import qualified Data.Text.Encoding.Error as TE import Data.Yaml (decode) import qualified Data.Yaml as Yaml import GHC.Generics @@ -181,11 +185,13 @@ convertWithOpts opts = do Nothing -> return Nothing Just fp -> Just <$> UTF8.readFile fp + let isPandocCiteproc (JSONFilter f) = takeBaseName f == "pandoc-citeproc" + isPandocCiteproc _ = False -- --bibliography implies -F pandoc-citeproc for backwards compatibility: let needsCiteproc = isJust (lookup "bibliography" (optMetadata opts)) && optCiteMethod opts `notElem` [Natbib, Biblatex] && - "pandoc-citeproc" `notElem` map takeBaseName filters - let filters' = if needsCiteproc then "pandoc-citeproc" : filters + all (not . isPandocCiteproc) filters + let filters' = if needsCiteproc then JSONFilter "pandoc-citeproc" : filters else filters let sources = case optInputFiles opts of @@ -498,10 +504,9 @@ convertWithOpts opts = do then fillMediaBag else return) >=> return . addMetadata metadata - >=> applyLuaFilters datadir (optLuaFilters opts) format - >=> maybe return extractMedia (optExtractMedia opts) >=> applyTransforms transforms - >=> applyFilters readerOpts datadir filters' [format] + >=> applyFilters readerOpts filters' [format] + >=> maybe return extractMedia (optExtractMedia opts) ) case writer of @@ -513,7 +518,9 @@ convertWithOpts opts = do case res of Right pdf -> writeFnBinary outputFile pdf Left err' -> liftIO $ - E.throwIO $ PandocPDFError (UTF8.toStringLazy err') + E.throwIO $ PandocPDFError $ + TL.unpack (TE.decodeUtf8With TE.lenientDecode err') + Nothing -> do let htmlFormat = format `elem` ["html","html4","html5","s5","slidy", @@ -578,6 +585,10 @@ externalFilter ropts f args' d = liftIO $ do where filterException :: E.SomeException -> IO a filterException e = E.throwIO $ PandocFilterError f (show e) +data Filter = LuaFilter FilePath + | JSONFilter FilePath + deriving (Show) + -- | Data structure for command line options. data Opt = Opt { optTabStop :: Int -- ^ Number of spaces per tab @@ -621,8 +632,7 @@ data Opt = Opt , optDpi :: Int -- ^ Dpi , optWrapText :: WrapOption -- ^ Options for wrapping text , optColumns :: Int -- ^ Line length in characters - , optFilters :: [FilePath] -- ^ Filters to apply - , optLuaFilters :: [FilePath] -- ^ Lua filters to apply + , optFilters :: [Filter] -- ^ Filters to apply , optEmailObfuscation :: ObfuscationMethod , optIdentifierPrefix :: String , optStripEmptyParagraphs :: Bool -- ^ Strip empty paragraphs @@ -695,7 +705,6 @@ defaultOpts = Opt , optWrapText = WrapAuto , optColumns = 72 , optFilters = [] - , optLuaFilters = [] , optEmailObfuscation = NoObfuscation , optIdentifierPrefix = "" , optStripEmptyParagraphs = False @@ -827,41 +836,46 @@ applyTransforms transforms d = return $ foldr ($) d transforms -- First we check to see if a filter is found. If not, and if it's -- not an absolute path, we check to see whether it's in `userdir/filters`. -- If not, we leave it unchanged. -expandFilterPath :: MonadIO m => Maybe FilePath -> FilePath -> m FilePath -expandFilterPath mbDatadir fp = liftIO $ do - fpExists <- doesFileExist fp +expandFilterPath :: PandocMonad m => FilePath -> m FilePath +expandFilterPath fp = do + mbDatadir <- getUserDataDir + fpExists <- fileExists fp if fpExists then return fp else case mbDatadir of Just datadir | isRelative fp -> do let filterPath = datadir </> "filters" </> fp - filterPathExists <- doesFileExist filterPath + filterPathExists <- fileExists filterPath if filterPathExists then return filterPath else return fp _ -> return fp -applyLuaFilters :: Maybe FilePath -> [FilePath] -> String -> Pandoc - -> PandocIO Pandoc -applyLuaFilters mbDatadir filters format d = do - expandedFilters <- mapM (expandFilterPath mbDatadir) filters - let go f d' = do - res <- runLuaFilter f format d' - case res of - Right x -> return x - Left (LuaException s) -> E.throw (PandocFilterError f s) - foldrM ($) d $ map go expandedFilters - -applyFilters :: MonadIO m - => ReaderOptions - -> Maybe FilePath - -> [FilePath] +applyFilters :: ReaderOptions + -> [Filter] -> [String] -> Pandoc - -> m Pandoc -applyFilters ropts mbDatadir filters args d = do - expandedFilters <- mapM (expandFilterPath mbDatadir) filters - foldrM ($) d $ map (flip (externalFilter ropts) args) expandedFilters + -> PandocIO Pandoc +applyFilters ropts filters args d = do + foldrM ($) d $ map (applyFilter ropts args) filters + +applyFilter :: ReaderOptions + -> [String] + -> Filter + -> Pandoc + -> PandocIO Pandoc +applyFilter _ropts args (LuaFilter f) d = do + f' <- expandFilterPath f + let format = case args of + (x:_) -> x + _ -> error "Format not supplied for lua filter" + res <- runLuaFilter f' format d + case res of + Right x -> return x + Left (LuaException s) -> E.throw (PandocFilterError f s) +applyFilter ropts args (JSONFilter f) d = do + f' <- expandFilterPath f + liftIO $ externalFilter ropts f' args d readSource :: FilePath -> PandocIO Text readSource "-" = liftIO (UTF8.toText <$> BS.getContents) @@ -963,13 +977,15 @@ options = , Option "F" ["filter"] (ReqArg - (\arg opt -> return opt { optFilters = arg : optFilters opt }) + (\arg opt -> return opt { optFilters = + JSONFilter arg : optFilters opt }) "PROGRAM") "" -- "External JSON filter" , Option "" ["lua-filter"] (ReqArg - (\arg opt -> return opt { optLuaFilters = arg : optLuaFilters opt }) + (\arg opt -> return opt { optFilters = + LuaFilter arg : optFilters opt }) "SCRIPTPATH") "" -- "Lua filter" @@ -1584,15 +1600,16 @@ options = "" , Option "" ["list-extensions"] - (NoArg - (\_ -> do - let showExt x = drop 4 (show x) ++ - if extensionEnabled x pandocExtensions - then " +" - else " -" + (OptArg + (\arg _ -> do + let exts = getDefaultExtensions (fromMaybe "markdown" arg) + let showExt x = (if extensionEnabled x exts + then '+' + else '-') : drop 4 (show x) mapM_ (UTF8.hPutStrLn stdout . showExt) ([minBound..maxBound] :: [Extension]) - exitSuccess )) + exitSuccess ) + "FORMAT") "" , Option "" ["list-highlight-languages"] @@ -1714,4 +1731,5 @@ deprecatedOption o msg = -- see https://github.com/jgm/pandoc/pull/4083 -- using generic deriving caused long compilation times $(deriveJSON defaultOptions ''LineEnding) +$(deriveJSON defaultOptions ''Filter) $(deriveJSON defaultOptions ''Opt) diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index c63781adf..f8d6b6737 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -78,9 +78,10 @@ module Text.Pandoc.Class ( PandocMonad(..) , getResourcePath , PandocIO(..) , PandocPure(..) - , FileTree(..) + , FileTree , FileInfo(..) , addToFileTree + , insertInFileTree , runIO , runIOorExplode , runPure @@ -144,6 +145,8 @@ import System.Directory (createDirectoryIfMissing, getDirectoryContents, import System.FilePath ((</>), (<.>), takeDirectory, takeExtension, dropExtension, isRelative, normalise) import qualified System.FilePath.Glob as IO (glob) +import qualified System.FilePath.Posix as Posix +import System.FilePath (splitDirectories) import qualified System.Directory as IO (getModificationTime) import Control.Monad as M (fail) import Control.Monad.State.Strict @@ -160,8 +163,6 @@ import Text.Pandoc.Translations (Term(..), Translations, lookupTerm, import qualified Debug.Trace #ifdef EMBED_DATA_FILES import Text.Pandoc.Data (dataFiles) -import qualified System.FilePath.Posix as Posix -import System.FilePath (splitDirectories) #else import qualified Paths_pandoc as Paths #endif @@ -620,6 +621,7 @@ getDefaultReferenceDocx = do "word/document.xml", "word/fontTable.xml", "word/footnotes.xml", + "word/comments.xml", "word/numbering.xml", "word/settings.xml", "word/webSettings.xml", @@ -686,8 +688,6 @@ getDefaultReferencePptx = do , "ppt/presProps.xml" , "ppt/presentation.xml" , "ppt/slideLayouts/_rels/slideLayout1.xml.rels" - , "ppt/slideLayouts/_rels/slideLayout10.xml.rels" - , "ppt/slideLayouts/_rels/slideLayout11.xml.rels" , "ppt/slideLayouts/_rels/slideLayout2.xml.rels" , "ppt/slideLayouts/_rels/slideLayout3.xml.rels" , "ppt/slideLayouts/_rels/slideLayout4.xml.rels" @@ -696,6 +696,8 @@ getDefaultReferencePptx = do , "ppt/slideLayouts/_rels/slideLayout7.xml.rels" , "ppt/slideLayouts/_rels/slideLayout8.xml.rels" , "ppt/slideLayouts/_rels/slideLayout9.xml.rels" + , "ppt/slideLayouts/_rels/slideLayout10.xml.rels" + , "ppt/slideLayouts/_rels/slideLayout11.xml.rels" , "ppt/slideLayouts/slideLayout1.xml" , "ppt/slideLayouts/slideLayout10.xml" , "ppt/slideLayouts/slideLayout11.xml" @@ -711,6 +713,8 @@ getDefaultReferencePptx = do , "ppt/slideMasters/slideMaster1.xml" , "ppt/slides/_rels/slide1.xml.rels" , "ppt/slides/slide1.xml" + , "ppt/slides/_rels/slide2.xml.rels" + , "ppt/slides/slide2.xml" , "ppt/tableStyles.xml" , "ppt/theme/theme1.xml" , "ppt/viewProps.xml" @@ -760,11 +764,6 @@ readDefaultDataFile fname = case lookup (makeCanonical fname) dataFiles of Nothing -> throwError $ PandocCouldNotFindDataFileError fname Just contents -> return contents - where makeCanonical = Posix.joinPath . transformPathParts . splitDirectories - transformPathParts = reverse . foldl go [] - go as "." = as - go (_:as) ".." = as - go as x = x : as #else getDataFileName fname' >>= checkExistence >>= readFileStrict where fname' = if fname == "MANUAL.txt" then fname else "data" </> fname @@ -777,6 +776,13 @@ checkExistence fn = do else throwError $ PandocCouldNotFindDataFileError fn #endif +makeCanonical :: FilePath -> FilePath +makeCanonical = Posix.joinPath . transformPathParts . splitDirectories + where transformPathParts = reverse . foldl go [] + go as "." = as + go (_:as) ".." = as + go as x = x : as + withPaths :: PandocMonad m => [FilePath] -> (FilePath -> m a) -> FilePath -> m a withPaths [] _ fp = throwError $ PandocResourceNotFound fp withPaths (p:ps) action fp = @@ -912,12 +918,13 @@ newtype FileTree = FileTree {unFileTree :: M.Map FilePath FileInfo} deriving (Monoid) getFileInfo :: FilePath -> FileTree -> Maybe FileInfo -getFileInfo fp tree = M.lookup fp $ unFileTree tree +getFileInfo fp tree = + M.lookup (makeCanonical fp) (unFileTree tree) -- | Add the specified file to the FileTree. If file -- is a directory, add its contents recursively. addToFileTree :: FileTree -> FilePath -> IO FileTree -addToFileTree (FileTree treemap) fp = do +addToFileTree tree fp = do isdir <- doesDirectoryExist fp if isdir then do -- recursively add contents of directories @@ -925,13 +932,17 @@ addToFileTree (FileTree treemap) fp = do isSpecial "." = True isSpecial _ = False fs <- (map (fp </>) . filter (not . isSpecial)) <$> getDirectoryContents fp - foldM addToFileTree (FileTree treemap) fs + foldM addToFileTree tree fs else do contents <- B.readFile fp mtime <- IO.getModificationTime fp - return $ FileTree $ - M.insert fp FileInfo{ infoFileMTime = mtime - , infoFileContents = contents } treemap + return $ insertInFileTree fp FileInfo{ infoFileMTime = mtime + , infoFileContents = contents } tree + +-- | Insert an ersatz file into the 'FileTree'. +insertInFileTree :: FilePath -> FileInfo -> FileTree -> FileTree +insertInFileTree fp info (FileTree treemap) = + FileTree $ M.insert (makeCanonical fp) info treemap newtype PandocPure a = PandocPure { unPandocPure :: ExceptT PandocError diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index bea293891..31fddb148 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -80,79 +80,79 @@ disableExtension x (Extensions exts) = Extensions (clearBit exts (fromEnum x)) -- | Individually selectable syntax extensions. data Extension = - Ext_footnotes -- ^ Pandoc/PHP/MMD style footnotes - | Ext_inline_notes -- ^ Pandoc-style inline notes - | Ext_pandoc_title_block -- ^ Pandoc title block - | Ext_yaml_metadata_block -- ^ YAML metadata block - | Ext_mmd_title_block -- ^ Multimarkdown metadata block - | Ext_table_captions -- ^ Pandoc-style table captions - | Ext_implicit_figures -- ^ A paragraph with just an image is a figure - | Ext_simple_tables -- ^ Pandoc-style simple tables - | Ext_multiline_tables -- ^ Pandoc-style multiline tables - | Ext_grid_tables -- ^ Grid tables (pandoc, reST) - | Ext_pipe_tables -- ^ Pipe tables (as in PHP markdown extra) - | Ext_citations -- ^ Pandoc/citeproc citations - | Ext_raw_tex -- ^ Allow raw TeX (other than math) - | Ext_raw_html -- ^ Allow raw HTML - | Ext_tex_math_dollars -- ^ TeX math between $..$ or $$..$$ - | Ext_tex_math_single_backslash -- ^ TeX math btw \(..\) \[..\] - | Ext_tex_math_double_backslash -- ^ TeX math btw \\(..\\) \\[..\\] - | Ext_latex_macros -- ^ Parse LaTeX macro definitions (for math only) - | Ext_fenced_code_blocks -- ^ Parse fenced code blocks - | Ext_fenced_code_attributes -- ^ Allow attributes on fenced code blocks - | Ext_backtick_code_blocks -- ^ GitHub style ``` code blocks - | Ext_inline_code_attributes -- ^ Allow attributes on inline code - | Ext_raw_attribute -- ^ Allow explicit raw blocks/inlines - | Ext_markdown_in_html_blocks -- ^ Interpret as markdown inside HTML blocks - | Ext_native_divs -- ^ Use Div blocks for contents of <div> tags - | Ext_fenced_divs -- ^ Allow fenced div syntax ::: - | Ext_native_spans -- ^ Use Span inlines for contents of <span> - | Ext_bracketed_spans -- ^ Bracketed spans with attributes - | Ext_markdown_attribute -- ^ Interpret text inside HTML as markdown - -- iff container has attribute 'markdown' - | Ext_escaped_line_breaks -- ^ Treat a backslash at EOL as linebreak - | Ext_link_attributes -- ^ link and image attributes - | Ext_mmd_link_attributes -- ^ MMD style reference link attributes - | Ext_autolink_bare_uris -- ^ Make all absolute URIs into links - | Ext_fancy_lists -- ^ Enable fancy list numbers and delimiters - | Ext_lists_without_preceding_blankline -- ^ Allow lists without preceding blank - | Ext_four_space_rule -- ^ Require 4-space indent for list contents - | Ext_startnum -- ^ Make start number of ordered list significant - | Ext_definition_lists -- ^ Definition lists as in pandoc, mmd, php - | Ext_compact_definition_lists -- ^ Definition lists without - -- space between items, and disallow laziness - | Ext_example_lists -- ^ Markdown-style numbered examples + Ext_abbreviations -- ^ PHP markdown extra abbreviation definitions | Ext_all_symbols_escapable -- ^ Make all non-alphanumerics escapable + | Ext_amuse -- ^ Enable Text::Amuse extensions to Emacs Muse markup | Ext_angle_brackets_escapable -- ^ Make < and > escapable - | Ext_intraword_underscores -- ^ Treat underscore inside word as literal + | Ext_ascii_identifiers -- ^ ascii-only identifiers for headers + | Ext_auto_identifiers -- ^ Automatic identifiers for headers + | Ext_autolink_bare_uris -- ^ Make all absolute URIs into links + | Ext_backtick_code_blocks -- ^ GitHub style ``` code blocks | Ext_blank_before_blockquote -- ^ Require blank line before a blockquote | Ext_blank_before_header -- ^ Require blank line before a header - | Ext_space_in_atx_header -- ^ Require space between # and header text - | Ext_strikeout -- ^ Strikeout using ~~this~~ syntax - | Ext_superscript -- ^ Superscript using ^this^ syntax - | Ext_subscript -- ^ Subscript using ~this~ syntax - | Ext_hard_line_breaks -- ^ All newlines become hard line breaks - | Ext_ignore_line_breaks -- ^ Newlines in paragraphs are ignored + | Ext_bracketed_spans -- ^ Bracketed spans with attributes + | Ext_citations -- ^ Pandoc/citeproc citations + | Ext_compact_definition_lists -- ^ Definition lists without space between items, + -- and disallow laziness + | Ext_definition_lists -- ^ Definition lists as in pandoc, mmd, php | Ext_east_asian_line_breaks -- ^ Newlines in paragraphs are ignored between - -- East Asian wide characters - | Ext_literate_haskell -- ^ Enable literate Haskell conventions - | Ext_abbreviations -- ^ PHP markdown extra abbreviation definitions + -- East Asian wide characters | Ext_emoji -- ^ Support emoji like :smile: - | Ext_auto_identifiers -- ^ Automatic identifiers for headers - | Ext_gfm_auto_identifiers -- ^ Automatic identifiers for headers, - -- using GitHub's method for generating identifiers - | Ext_ascii_identifiers -- ^ ascii-only identifiers for headers + | Ext_empty_paragraphs -- ^ Allow empty paragraphs + | Ext_epub_html_exts -- ^ Recognise the EPUB extended version of HTML + | Ext_escaped_line_breaks -- ^ Treat a backslash at EOL as linebreak + | Ext_example_lists -- ^ Markdown-style numbered examples + | Ext_fancy_lists -- ^ Enable fancy list numbers and delimiters + | Ext_fenced_code_attributes -- ^ Allow attributes on fenced code blocks + | Ext_fenced_code_blocks -- ^ Parse fenced code blocks + | Ext_fenced_divs -- ^ Allow fenced div syntax ::: + | Ext_footnotes -- ^ Pandoc/PHP/MMD style footnotes + | Ext_four_space_rule -- ^ Require 4-space indent for list contents + | Ext_gfm_auto_identifiers -- ^ Automatic identifiers for headers, using + -- GitHub's method for generating identifiers + | Ext_grid_tables -- ^ Grid tables (pandoc, reST) + | Ext_hard_line_breaks -- ^ All newlines become hard line breaks | Ext_header_attributes -- ^ Explicit header attributes {#id .class k=v} - | Ext_mmd_header_identifiers -- ^ Multimarkdown style header identifiers [myid] + | Ext_ignore_line_breaks -- ^ Newlines in paragraphs are ignored + | Ext_implicit_figures -- ^ A paragraph with just an image is a figure | Ext_implicit_header_references -- ^ Implicit reference links for headers + | Ext_inline_code_attributes -- ^ Allow attributes on inline code + | Ext_inline_notes -- ^ Pandoc-style inline notes + | Ext_intraword_underscores -- ^ Treat underscore inside word as literal + | Ext_latex_macros -- ^ Parse LaTeX macro definitions (for math only) | Ext_line_blocks -- ^ RST style line blocks - | Ext_epub_html_exts -- ^ Recognise the EPUB extended version of HTML + | Ext_link_attributes -- ^ link and image attributes + | Ext_lists_without_preceding_blankline -- ^ Allow lists without preceding blank + | Ext_literate_haskell -- ^ Enable literate Haskell conventions + | Ext_markdown_attribute -- ^ Interpret text inside HTML as markdown iff + -- container has attribute 'markdown' + | Ext_markdown_in_html_blocks -- ^ Interpret as markdown inside HTML blocks + | Ext_mmd_header_identifiers -- ^ Multimarkdown style header identifiers [myid] + | Ext_mmd_link_attributes -- ^ MMD style reference link attributes + | Ext_mmd_title_block -- ^ Multimarkdown metadata block + | Ext_multiline_tables -- ^ Pandoc-style multiline tables + | Ext_native_divs -- ^ Use Div blocks for contents of <div> tags + | Ext_native_spans -- ^ Use Span inlines for contents of <span> + | Ext_old_dashes -- ^ -- = em, - before number = en + | Ext_pandoc_title_block -- ^ Pandoc title block + | Ext_pipe_tables -- ^ Pipe tables (as in PHP markdown extra) + | Ext_raw_attribute -- ^ Allow explicit raw blocks/inlines + | Ext_raw_html -- ^ Allow raw HTML + | Ext_raw_tex -- ^ Allow raw TeX (other than math) | Ext_shortcut_reference_links -- ^ Shortcut reference links + | Ext_simple_tables -- ^ Pandoc-style simple tables | Ext_smart -- ^ "Smart" quotes, apostrophes, ellipses, dashes - | Ext_old_dashes -- ^ -- = em, - before number = en + | Ext_space_in_atx_header -- ^ Require space between # and header text | Ext_spaced_reference_links -- ^ Allow space between two parts of ref link - | Ext_amuse -- ^ Enable Text::Amuse extensions to Emacs Muse markup - | Ext_empty_paragraphs -- ^ Allow empty paragraphs + | Ext_startnum -- ^ Make start number of ordered list significant + | Ext_strikeout -- ^ Strikeout using ~~this~~ syntax + | Ext_subscript -- ^ Subscript using ~this~ syntax + | Ext_superscript -- ^ Superscript using ^this^ syntax + | Ext_table_captions -- ^ Pandoc-style table captions + | Ext_tex_math_dollars -- ^ TeX math between $..$ or $$..$$ + | Ext_tex_math_double_backslash -- ^ TeX math btw \\(..\\) \\[..\\] + | Ext_tex_math_single_backslash -- ^ TeX math btw \(..\) \[..\] + | Ext_yaml_metadata_block -- ^ YAML metadata block deriving (Show, Read, Enum, Eq, Ord, Bounded, Data, Typeable, Generic) -- | Extensions to be used with pandoc-flavored markdown. @@ -321,6 +321,7 @@ getDefaultExtensions "org" = extensionsFromList getDefaultExtensions "html" = extensionsFromList [Ext_auto_identifiers, Ext_native_divs, + Ext_line_blocks, Ext_native_spans] getDefaultExtensions "html4" = getDefaultExtensions "html" getDefaultExtensions "html5" = getDefaultExtensions "html" diff --git a/src/Text/Pandoc/Readers/Docx/Parse.hs b/src/Text/Pandoc/Readers/Docx/Parse.hs index 99e6f99e6..48a512be2 100644 --- a/src/Text/Pandoc/Readers/Docx/Parse.hs +++ b/src/Text/Pandoc/Readers/Docx/Parse.hs @@ -73,6 +73,7 @@ import Text.TeXMath (Exp) import Text.TeXMath.Readers.OMML (readOMML) import Text.TeXMath.Unicode.Fonts (Font (..), getUnicode, stringToFont) import Text.XML.Light +import qualified Text.XML.Light.Cursor as XMLC data ReaderEnv = ReaderEnv { envNotes :: Notes , envComments :: Comments @@ -117,6 +118,32 @@ mapD f xs = in concatMapM handler xs +unwrapSDT :: NameSpaces -> Content -> Content +unwrapSDT ns (Elem element) + | isElem ns "w" "sdt" element + , Just sdtContent <- findChildByName ns "w" "sdtContent" element + , child : _ <- elChildren sdtContent + = Elem child +unwrapSDT _ content = content + +walkDocument' :: NameSpaces -> XMLC.Cursor -> XMLC.Cursor +walkDocument' ns cur = + let modifiedCur = XMLC.modifyContent (unwrapSDT ns) cur + in + case XMLC.nextDF modifiedCur of + Just cur' -> walkDocument' ns cur' + Nothing -> XMLC.root modifiedCur + +walkDocument :: NameSpaces -> Element -> Maybe Element +walkDocument ns element = + let cur = XMLC.fromContent (Elem element) + cur' = walkDocument' ns cur + in + case XMLC.toTree cur' of + Elem element' -> Just element' + _ -> Nothing + + data Docx = Docx Document deriving Show @@ -298,7 +325,10 @@ archiveToDocument zf = do docElem <- maybeToD $ (parseXMLDoc . UTF8.toStringLazy . fromEntry) entry let namespaces = elemToNameSpaces docElem bodyElem <- maybeToD $ findChildByName namespaces "w" "body" docElem - body <- elemToBody namespaces bodyElem + let bodyElem' = case walkDocument namespaces bodyElem of + Just e -> e + Nothing -> bodyElem + body <- elemToBody namespaces bodyElem' return $ Document namespaces body elemToBody :: NameSpaces -> Element -> D Body diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index f5f296712..65171d37a 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -51,7 +51,7 @@ import Data.Char (isAlphaNum, isDigit, isLetter) import Data.Default (Default (..), def) import Data.Foldable (for_) import Data.List (intercalate, isPrefixOf) -import Data.List.Split (wordsBy) +import Data.List.Split (wordsBy, splitWhen) import qualified Data.Map as M import Data.Maybe (fromMaybe, isJust, isNothing) import Data.Monoid (First (..), (<>)) @@ -70,12 +70,12 @@ import Text.Pandoc.Error import Text.Pandoc.Logging import Text.Pandoc.Options ( Extension (Ext_epub_html_exts, Ext_empty_paragraphs, Ext_native_divs, - Ext_native_spans, Ext_raw_html), + Ext_native_spans, Ext_raw_html, Ext_line_blocks), ReaderOptions (readerExtensions, readerStripComments), extensionEnabled) import Text.Pandoc.Parsing hiding ((<|>)) -import Text.Pandoc.Shared (addMetaField, crFilter, escapeURI, extractSpaces, - safeRead, underlineSpan) +import Text.Pandoc.Shared (addMetaField, blocksToInlines', crFilter, escapeURI, + extractSpaces, safeRead, underlineSpan) import Text.Pandoc.Walk import Text.Parsec.Error import Text.TeXMath (readMathML, writeTeX) @@ -191,6 +191,7 @@ block = do , pHtml , pHead , pBody + , pLineBlock , pDiv , pPlain , pFigure @@ -377,6 +378,16 @@ pRawTag = do then return mempty else return $ renderTags' [tag] +pLineBlock :: PandocMonad m => TagParser m Blocks +pLineBlock = try $ do + guardEnabled Ext_line_blocks + _ <- pSatisfy $ tagOpen (=="div") (== [("class","line-block")]) + ils <- trimInlines . mconcat <$> manyTill inline (pSatisfy (tagClose (=="div"))) + let lns = map B.fromList $ + splitWhen (== LineBreak) $ filter (/= SoftBreak) $ + B.toList ils + return $ B.lineBlock lns + pDiv :: PandocMonad m => TagParser m Blocks pDiv = try $ do guardEnabled Ext_native_divs @@ -588,8 +599,9 @@ pFigure = try $ do skipMany pBlank let pImg = (\x -> (Just x, Nothing)) <$> (pOptInTag "p" pImage <* skipMany pBlank) - pCapt = (\x -> (Nothing, Just x)) <$> - (pInTags "figcaption" inline <* skipMany pBlank) + pCapt = (\x -> (Nothing, Just x)) <$> do + bs <- pInTags "figcaption" block + return $ blocksToInlines' $ B.toList bs pSkip = (Nothing, Nothing) <$ pSatisfy (not . matchTagClose "figure") res <- many (pImg <|> pCapt <|> pSkip) let mbimg = msum $ map fst res diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 6c5567ffd..e0972bb6c 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1726,7 +1726,7 @@ inline = (mempty <$ comment) <|> (guardEnabled Ext_literate_haskell *> symbol '|' *> doLHSverb) <|> (str . (:[]) <$> primEscape) <|> regularSymbol - <|> (do res <- symbolIn "#^'`\"[]" + <|> (do res <- symbolIn "#^'`\"[]&" pos <- getPosition let s = T.unpack (untoken res) report $ ParsingUnescaped s pos diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index cc6abbfa5..a930652af 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -41,7 +41,6 @@ import Text.Pandoc.Readers.Org.Shared (cleanLinkString, isImageFilename, originalLang, translateLang) import Text.Pandoc.Builder (Blocks, Inlines) -import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Options @@ -54,6 +53,9 @@ import Data.List (foldl', isPrefixOf) import Data.Maybe (fromMaybe, isJust, isNothing) import Data.Monoid ((<>)) +import qualified Text.Pandoc.Builder as B +import qualified Text.Pandoc.Walk as Walk + -- -- parsing blocks -- @@ -509,19 +511,18 @@ include :: PandocMonad m => OrgParser m (F Blocks) include = try $ do metaLineStart <* stringAnyCase "include:" <* skipSpaces filename <- includeTarget - blockType <- optionMaybe $ skipSpaces *> many1 alphaNum - blocksParser <- case blockType of - Just "example" -> - return $ pure . B.codeBlock <$> parseRaw - Just "export" -> do - format <- skipSpaces *> many (noneOf "\n\r\t ") - return $ pure . B.rawBlock format <$> parseRaw - Just "src" -> do - language <- skipSpaces *> many (noneOf "\n\r\t ") - let attr = (mempty, [language], mempty) - return $ pure . B.codeBlockWith attr <$> parseRaw - _ -> return $ pure . B.fromList <$> blockList - anyLine + includeArgs <- many (try $ skipSpaces *> many1 alphaNum) + params <- keyValues + blocksParser <- case includeArgs of + ("example" : _) -> return $ pure . B.codeBlock <$> parseRaw + ["export"] -> return . returnF $ B.fromList [] + ("export" : format : []) -> return $ pure . B.rawBlock format <$> parseRaw + ("src" : rest) -> do + let attr = case rest of + [lang] -> (mempty, [lang], mempty) + _ -> nullAttr + return $ pure . B.codeBlockWith attr <$> parseRaw + _ -> return $ return . B.fromList . blockFilter params <$> blockList insertIncludedFileF blocksParser ["."] filename where includeTarget :: PandocMonad m => OrgParser m FilePath @@ -532,6 +533,28 @@ include = try $ do parseRaw :: PandocMonad m => OrgParser m String parseRaw = many anyChar + blockFilter :: [(String, String)] -> [Block] -> [Block] + blockFilter params blks = + let minlvl = lookup "minlevel" params + in case (minlvl >>= safeRead :: Maybe Int) of + Nothing -> blks + Just lvl -> let levels = Walk.query headerLevel blks + -- CAVE: partial function in else + curMin = if null levels then 0 else minimum levels + in Walk.walk (shiftHeader (curMin - lvl)) blks + + headerLevel :: Block -> [Int] + headerLevel (Header lvl _attr _content) = [lvl] + headerLevel _ = [] + + shiftHeader :: Int -> Block -> Block + shiftHeader shift blk = + if shift <= 0 + then blk + else case blk of + (Header lvl attr content) -> Header (lvl - shift) attr content + _ -> blk + rawExportLine :: PandocMonad m => OrgParser m Blocks rawExportLine = try $ do metaLineStart diff --git a/src/Text/Pandoc/Writers/Custom.hs b/src/Text/Pandoc/Writers/Custom.hs index 6a6fabf1d..a33196cbe 100644 --- a/src/Text/Pandoc/Writers/Custom.hs +++ b/src/Text/Pandoc/Writers/Custom.hs @@ -87,6 +87,15 @@ instance ToLuaStack (Stringify Citation) where addValue "citationNoteNum" $ citationNoteNum cit addValue "citationHash" $ citationHash cit +-- | Key-value pair, pushed as a table with @a@ as the only key and @v@ as the +-- associated value. +newtype KeyValue a b = KeyValue (a, b) + +instance (ToLuaStack a, ToLuaStack b) => ToLuaStack (KeyValue a b) where + push (KeyValue (k, v)) = do + newtable + addValue k v + data PandocLuaException = PandocLuaException String deriving (Show, Typeable) @@ -165,7 +174,8 @@ blockToCustom (OrderedList (num,sty,delim) items) = callFunc "OrderedList" (map Stringify items) num (show sty) (show delim) blockToCustom (DefinitionList items) = - callFunc "DefinitionList" (map (Stringify *** map Stringify) items) + callFunc "DefinitionList" + (map (KeyValue . (Stringify *** map Stringify)) items) blockToCustom (Div attr items) = callFunc "Div" (Stringify items) (attrToMap attr) diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index 633f42442..0a4130ca4 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -121,9 +121,18 @@ description meta' = do Just (MetaString s) -> [el "lang" $ iso639 s] _ -> [] where iso639 = takeWhile (/= '-') -- Convert BCP 47 to ISO 639 + let coverimage url = do + let img = Image nullAttr mempty (url, "") + im <- insertImage InlineImage img + return [el "coverpage" im] + coverpage <- case lookupMeta "cover-image" meta' of + Just (MetaInlines [Str s]) -> coverimage s + Just (MetaString s) -> coverimage s + _ -> return [] return $ el "description" [ el "title-info" (genre : (bt ++ as ++ dd ++ lang)) - , el "document-info" [ el "program-used" "pandoc" ] -- FIXME: +version + , el "document-info" ([ el "program-used" "pandoc" ] -- FIXME: +version + ++ coverpage) ] booktitle :: PandocMonad m => Meta -> FBM m [Content] diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index d6ccc1512..87ce65586 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -699,10 +699,9 @@ blockToLaTeX (Table caption aligns widths heads rows) = do then return empty else ($$ text "\\endfirsthead") <$> toHeaders heads head' <- if all null heads - then return empty + then return "\\toprule" -- avoid duplicate notes in head and firsthead: - else ($$ text "\\endhead") <$> - toHeaders (if isEmpty firsthead + else toHeaders (if isEmpty firsthead then heads else walk removeNote heads) let capt = if isEmpty captionText @@ -717,8 +716,8 @@ blockToLaTeX (Table caption aligns widths heads rows) = do -- the @{} removes extra space at beginning and end $$ capt $$ firsthead - $$ (if all null heads then "\\toprule" else empty) $$ head' + $$ "\\endhead" $$ vcat rows' $$ "\\bottomrule" $$ "\\end{longtable}" diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index d5627f51c..ab3b2eabf 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -86,6 +86,9 @@ writePowerpoint opts (Pandoc meta blks) = do , envDistArchive = distArchive , envUTCTime = utctime , envOpts = opts + , envSlideLevel = case writerSlideLevel opts of + Just n -> n + Nothing -> 2 } runP env def $ do pres <- blocksToPresentation blks' archv <- presentationToArchive pres diff --git a/stack.lts9.yaml b/stack.lts9.yaml new file mode 100644 index 000000000..43cc9f511 --- /dev/null +++ b/stack.lts9.yaml @@ -0,0 +1,29 @@ +flags: + pandoc: + trypandoc: false + embed_data_files: true + old-locale: false + network-uri: true + pandoc-citeproc: + bibutils: true + embed_data_files: true + unicode_collation: false + test_citeproc: false + debug: false +packages: +- '.' +extra-deps: +- pandoc-types-1.17.3 +- hslua-0.9.3 +- hslua-module-text-0.1.2 +- skylighting-0.5 +- texmath-0.10.1 +- cmark-gfm-0.1.1 +- QuickCheck-2.10.0.1 +- tasty-quickcheck-0.9.1 +- doctemplates-0.2.1 +- haddock-library-1.4.3 +- tagsoup-0.14.2 +- hs-bibutils-6.2.0.1 +- pandoc-citeproc-0.12.2.1 +resolver: lts-9.14 diff --git a/stack.pkg.yaml b/stack.pkg.yaml deleted file mode 100644 index 450c7c3ae..000000000 --- a/stack.pkg.yaml +++ /dev/null @@ -1,16 +0,0 @@ -flags: - pandoc: - trypandoc: false - embed_data_files: true - old-locale: false - network-uri: true - pandoc-citeproc: - bibutils: true - embed_data_files: true - unicode_collation: false - test_citeproc: false - debug: false -packages: -- '.' -extra-deps: [] -resolver: lts-10.1 diff --git a/stack.yaml b/stack.yaml index e04468cfc..60ecb0627 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,9 +1,17 @@ flags: pandoc: trypandoc: false - embed_data_files: false + embed_data_files: true old-locale: false network-uri: true + pandoc-citeproc: + bibutils: true + embed_data_files: true + unicode_collation: false + test_citeproc: false + debug: false packages: -extra-deps: [] +- '.' +extra-deps: +- pandoc-citeproc-0.12.2.1 resolver: lts-10.1 diff --git a/test/Tests/Old.hs b/test/Tests/Old.hs index bbd51ee98..b82251a56 100644 --- a/test/Tests/Old.hs +++ b/test/Tests/Old.hs @@ -162,6 +162,12 @@ tests = [ testGroup "markdown" [ test "reader" ["-r", "creole", "-w", "native", "-s"] "creole-reader.txt" "creole-reader.native" ] + , testGroup "custom writer" + [ test "basic" ["-f", "native", "-t", "../data/sample.lua"] + "testsuite.native" "writer.custom" + , test "tables" ["-f", "native", "-t", "../data/sample.lua"] + "tables.native" "tables.custom" + ] ] -- makes sure file is fully closed after reading diff --git a/test/Tests/Readers/Docx.hs b/test/Tests/Readers/Docx.hs index 6d91c36ae..5710a388f 100644 --- a/test/Tests/Readers/Docx.hs +++ b/test/Tests/Readers/Docx.hs @@ -171,6 +171,10 @@ tests = [ testGroup "inlines" "inline code in subscript and superscript" "docx/verbatim_subsuper.docx" "docx/verbatim_subsuper.native" + , testCompare + "inlines inside of Structured Document Tags" + "docx/sdt_elements.docx" + "docx/sdt_elements.native" ] , testGroup "blocks" [ testCompare diff --git a/test/Tests/Readers/Org.hs b/test/Tests/Readers/Org.hs index dbd56c880..de7f14e32 100644 --- a/test/Tests/Readers/Org.hs +++ b/test/Tests/Readers/Org.hs @@ -1,1871 +1,16 @@ {-# LANGUAGE OverloadedStrings #-} module Tests.Readers.Org (tests) where -import Data.List (intersperse) -import Data.Text (Text) -import qualified Data.Text as T -import Test.Tasty -import Tests.Helpers -import Text.Pandoc -import Text.Pandoc.Builder -import Text.Pandoc.Shared (underlineSpan) - -org :: Text -> Pandoc -org = purely $ readOrg def{ readerExtensions = getDefaultExtensions "org" } - -orgSmart :: Text -> Pandoc -orgSmart = purely $ readOrg def { readerExtensions = - enableExtension Ext_smart $ getDefaultExtensions "org" } - -infix 4 =: -(=:) :: ToString c - => String -> (Text, c) -> TestTree -(=:) = test org - -spcSep :: [Inlines] -> Inlines -spcSep = mconcat . intersperse space - -simpleTable' :: Int - -> [Blocks] - -> [[Blocks]] - -> Blocks -simpleTable' n = table "" (replicate n (AlignDefault, 0.0)) - --- | Create a span for the given tag. -tagSpan :: String -> Inlines -tagSpan t = spanWith ("", ["tag"], [("tag-name", t)]) . smallcaps $ str t +import Test.Tasty (TestTree, testGroup) +import qualified Tests.Readers.Org.Block as Block +import qualified Tests.Readers.Org.Directive as Directive +import qualified Tests.Readers.Org.Inline as Inline +import qualified Tests.Readers.Org.Meta as Meta tests :: [TestTree] tests = - [ testGroup "Inlines" $ - [ "Plain String" =: - "Hello, World" =?> - para (spcSep [ "Hello,", "World" ]) - - , "Emphasis" =: - "/Planet Punk/" =?> - para (emph . spcSep $ ["Planet", "Punk"]) - - , "Strong" =: - "*Cider*" =?> - para (strong "Cider") - - , "Strong Emphasis" =: - "/*strength*/" =?> - para (emph . strong $ "strength") - - , "Emphasized Strong preceded by space" =: - " */super/*" =?> - para (strong . emph $ "super") - - , "Underline" =: - "_underline_" =?> - para (underlineSpan $ "underline") - - , "Strikeout" =: - "+Kill Bill+" =?> - para (strikeout . spcSep $ [ "Kill", "Bill" ]) - - , "Verbatim" =: - "=Robot.rock()=" =?> - para (code "Robot.rock()") - - , "Code" =: - "~word for word~" =?> - para (code "word for word") - - , "Math $..$" =: - "$E=mc^2$" =?> - para (math "E=mc^2") - - , "Math $$..$$" =: - "$$E=mc^2$$" =?> - para (displayMath "E=mc^2") - - , "Math \\[..\\]" =: - "\\[E=ℎν\\]" =?> - para (displayMath "E=ℎν") - - , "Math \\(..\\)" =: - "\\(σ_x σ_p ≥ \\frac{ℏ}{2}\\)" =?> - para (math "σ_x σ_p ≥ \\frac{ℏ}{2}") - - , "Symbol" =: - "A * symbol" =?> - para (str "A" <> space <> str "*" <> space <> "symbol") - - , "Superscript simple expression" =: - "2^-λ" =?> - para (str "2" <> superscript "-λ") - - , "Superscript multi char" =: - "2^{n-1}" =?> - para (str "2" <> superscript "n-1") - - , "Subscript simple expression" =: - "a_n" =?> - para (str "a" <> subscript "n") - - , "Subscript multi char" =: - "a_{n+1}" =?> - para (str "a" <> subscript "n+1") - - , "Linebreak" =: - "line \\\\ \nbreak" =?> - para ("line" <> linebreak <> "break") - - , "Inline note" =: - "[fn::Schreib mir eine E-Mail]" =?> - para (note $ para "Schreib mir eine E-Mail") - - , "Markup-chars not occuring on word break are symbols" =: - T.unlines [ "this+that+ +so+on" - , "seven*eight* nine*" - , "+not+funny+" - ] =?> - para ("this+that+ +so+on" <> softbreak <> - "seven*eight* nine*" <> softbreak <> - strikeout "not+funny") - - , "No empty markup" =: - "// ** __ <> == ~~ $$" =?> - para (spcSep [ "//", "**", "__", "<>", "==", "~~", "$$" ]) - - , "Adherence to Org's rules for markup borders" =: - "/t/& a/ / ./r/ (*l*) /e/! /b/." =?> - para (spcSep [ emph $ "t/&" <> space <> "a" - , "/" - , "./r/" - , "(" <> strong "l" <> ")" - , emph "e" <> "!" - , emph "b" <> "." - ]) - - , "Quotes are allowed border chars" =: - "/'yep/ *sure\"*" =?> - para (emph "'yep" <> space <> strong "sure\"") - - , "Spaces are forbidden border chars" =: - "/nada /" =?> - para "/nada /" - - , "Markup should work properly after a blank line" =: - T.unlines ["foo", "", "/bar/"] =?> - (para $ text "foo") <> (para $ emph $ text "bar") - - , "Inline math must stay within three lines" =: - T.unlines [ "$a", "b", "c$", "$d", "e", "f", "g$" ] =?> - para ((math "a\nb\nc") <> softbreak <> - "$d" <> softbreak <> "e" <> softbreak <> - "f" <> softbreak <> "g$") - - , "Single-character math" =: - "$a$ $b$! $c$?" =?> - para (spcSep [ math "a" - , "$b$!" - , (math "c") <> "?" - ]) - - , "Markup may not span more than two lines" =: - "/this *is +totally\nnice+ not*\nemph/" =?> - para ("/this" <> space <> - strong ("is" <> space <> - strikeout ("totally" <> - softbreak <> "nice") <> - space <> "not") <> - softbreak <> "emph/") - - , "Sub- and superscript expressions" =: - T.unlines [ "a_(a(b)(c)d)" - , "e^(f(g)h)" - , "i_(jk)l)" - , "m^()n" - , "o_{p{q{}r}}" - , "s^{t{u}v}" - , "w_{xy}z}" - , "1^{}2" - , "3_{{}}" - , "4^(a(*b(c*)d))" - ] =?> - para (mconcat $ intersperse softbreak - [ "a" <> subscript "(a(b)(c)d)" - , "e" <> superscript "(f(g)h)" - , "i" <> (subscript "(jk)") <> "l)" - , "m" <> (superscript "()") <> "n" - , "o" <> subscript "p{q{}r}" - , "s" <> superscript "t{u}v" - , "w" <> (subscript "xy") <> "z}" - , "1" <> (superscript "") <> "2" - , "3" <> subscript "{}" - , "4" <> superscript ("(a(" <> strong "b(c" <> ")d))") - ]) - , "Verbatim text can contain equal signes (=)" =: - "=is_subst = True=" =?> - para (code "is_subst = True") - - , testGroup "Images" - [ "Image" =: - "[[./sunset.jpg]]" =?> - (para $ image "./sunset.jpg" "" "") - - , "Image with explicit file: prefix" =: - "[[file:sunrise.jpg]]" =?> - (para $ image "sunrise.jpg" "" "") - - , "Multiple images within a paragraph" =: - T.unlines [ "[[file:sunrise.jpg]]" - , "[[file:sunset.jpg]]" - ] =?> - (para $ (image "sunrise.jpg" "" "") - <> softbreak - <> (image "sunset.jpg" "" "")) - - , "Image with html attributes" =: - T.unlines [ "#+ATTR_HTML: :width 50%" - , "[[file:guinea-pig.gif]]" - ] =?> - (para $ imageWith ("", [], [("width", "50%")]) "guinea-pig.gif" "" "") - ] - - , "Explicit link" =: - "[[http://zeitlens.com/][pseudo-random /nonsense/]]" =?> - (para $ link "http://zeitlens.com/" "" - ("pseudo-random" <> space <> emph "nonsense")) - - , "Self-link" =: - "[[http://zeitlens.com/]]" =?> - (para $ link "http://zeitlens.com/" "" "http://zeitlens.com/") - - , "Absolute file link" =: - "[[/url][hi]]" =?> - (para $ link "file:///url" "" "hi") - - , "Link to file in parent directory" =: - "[[../file.txt][moin]]" =?> - (para $ link "../file.txt" "" "moin") - - , "Empty link (for gitit interop)" =: - "[[][New Link]]" =?> - (para $ link "" "" "New Link") - - , "Image link" =: - "[[sunset.png][file:dusk.svg]]" =?> - (para $ link "sunset.png" "" (image "dusk.svg" "" "")) - - , "Image link with non-image target" =: - "[[http://example.com][./logo.png]]" =?> - (para $ link "http://example.com" "" (image "./logo.png" "" "")) - - , "Plain link" =: - "Posts on http://zeitlens.com/ can be funny at times." =?> - (para $ spcSep [ "Posts", "on" - , link "http://zeitlens.com/" "" "http://zeitlens.com/" - , "can", "be", "funny", "at", "times." - ]) - - , "Angle link" =: - "Look at <http://moltkeplatz.de> for fnords." =?> - (para $ spcSep [ "Look", "at" - , link "http://moltkeplatz.de" "" "http://moltkeplatz.de" - , "for", "fnords." - ]) - - , "Absolute file link" =: - "[[file:///etc/passwd][passwd]]" =?> - (para $ link "file:///etc/passwd" "" "passwd") - - , "File link" =: - "[[file:target][title]]" =?> - (para $ link "target" "" "title") - - , "Anchor" =: - "<<anchor>> Link here later." =?> - (para $ spanWith ("anchor", [], []) mempty <> - "Link" <> space <> "here" <> space <> "later.") - - , "Inline code block" =: - "src_emacs-lisp{(message \"Hello\")}" =?> - (para $ codeWith ( "" - , [ "commonlisp" ] - , [ ("org-language", "emacs-lisp") ]) - "(message \"Hello\")") - - , "Inline code block with arguments" =: - "src_sh[:export both :results output]{echo 'Hello, World'}" =?> - (para $ codeWith ( "" - , [ "bash" ] - , [ ("org-language", "sh") - , ("export", "both") - , ("results", "output") - ] - ) - "echo 'Hello, World'") - - , "Inline code block with toggle" =: - "src_sh[:toggle]{echo $HOME}" =?> - (para $ codeWith ( "" - , [ "bash" ] - , [ ("org-language", "sh") - , ("toggle", "yes") - ] - ) - "echo $HOME") - - , "Citation" =: - "[@nonexistent]" =?> - let citation = Citation - { citationId = "nonexistent" - , citationPrefix = [] - , citationSuffix = [] - , citationMode = NormalCitation - , citationNoteNum = 0 - , citationHash = 0} - in (para $ cite [citation] "[@nonexistent]") - - , "Citation containing text" =: - "[see @item1 p. 34-35]" =?> - let citation = Citation - { citationId = "item1" - , citationPrefix = [Str "see"] - , citationSuffix = [Space ,Str "p.",Space,Str "34-35"] - , citationMode = NormalCitation - , citationNoteNum = 0 - , citationHash = 0} - in (para $ cite [citation] "[see @item1 p. 34-35]") - - , "Org-ref simple citation" =: - "cite:pandoc" =?> - let citation = Citation - { citationId = "pandoc" - , citationPrefix = mempty - , citationSuffix = mempty - , citationMode = AuthorInText - , citationNoteNum = 0 - , citationHash = 0 - } - in (para $ cite [citation] "cite:pandoc") - - , "Org-ref simple citation with underscores" =: - "cite:pandoc_org_ref" =?> - let citation = Citation - { citationId = "pandoc_org_ref" - , citationPrefix = mempty - , citationSuffix = mempty - , citationMode = AuthorInText - , citationNoteNum = 0 - , citationHash = 0 - } - in (para $ cite [citation] "cite:pandoc_org_ref") - - , "Org-ref simple citation succeeded by comma" =: - "cite:pandoc," =?> - let citation = Citation - { citationId = "pandoc" - , citationPrefix = mempty - , citationSuffix = mempty - , citationMode = AuthorInText - , citationNoteNum = 0 - , citationHash = 0 - } - in (para $ cite [citation] "cite:pandoc" <> str ",") - - , "Org-ref simple citation succeeded by dot" =: - "cite:pandoc." =?> - let citation = Citation - { citationId = "pandoc" - , citationPrefix = mempty - , citationSuffix = mempty - , citationMode = AuthorInText - , citationNoteNum = 0 - , citationHash = 0 - } - in (para $ cite [citation] "cite:pandoc" <> str ".") - - , "Org-ref simple citation succeeded by colon" =: - "cite:pandoc:" =?> - let citation = Citation - { citationId = "pandoc" - , citationPrefix = mempty - , citationSuffix = mempty - , citationMode = AuthorInText - , citationNoteNum = 0 - , citationHash = 0 - } - in (para $ cite [citation] "cite:pandoc" <> str ":") - - , "Org-ref simple citep citation" =: - "citep:pandoc" =?> - let citation = Citation - { citationId = "pandoc" - , citationPrefix = mempty - , citationSuffix = mempty - , citationMode = NormalCitation - , citationNoteNum = 0 - , citationHash = 0 - } - in (para $ cite [citation] "citep:pandoc") - - , "Org-ref extended citation" =: - "[[citep:Dominik201408][See page 20::, for example]]" =?> - let citation = Citation - { citationId = "Dominik201408" - , citationPrefix = toList "See page 20" - , citationSuffix = toList ", for example" - , citationMode = NormalCitation - , citationNoteNum = 0 - , citationHash = 0 - } - in (para $ cite [citation] "[[citep:Dominik201408][See page 20::, for example]]") - - , testGroup "Berkeley-style citations" $ - let pandocCite = Citation - { citationId = "Pandoc" - , citationPrefix = mempty - , citationSuffix = mempty - , citationMode = NormalCitation - , citationNoteNum = 0 - , citationHash = 0 - } - pandocInText = pandocCite { citationMode = AuthorInText } - dominikCite = Citation - { citationId = "Dominik201408" - , citationPrefix = mempty - , citationSuffix = mempty - , citationMode = NormalCitation - , citationNoteNum = 0 - , citationHash = 0 - } - dominikInText = dominikCite { citationMode = AuthorInText } - in [ - "Berkeley-style in-text citation" =: - "See @Dominik201408." =?> - (para $ "See " - <> cite [dominikInText] "@Dominik201408" - <> ".") - - , "Berkeley-style parenthetical citation list" =: - "[(cite): see; @Dominik201408;also @Pandoc; and others]" =?> - let pandocCite' = pandocCite { - citationPrefix = toList "also" - , citationSuffix = toList "and others" - } - dominikCite' = dominikCite { - citationPrefix = toList "see" - } - in (para $ cite [dominikCite', pandocCite'] "") - - , "Berkeley-style plain citation list" =: - "[cite: See; @Dominik201408; and @Pandoc; and others]" =?> - let pandocCite' = pandocInText { - citationPrefix = toList "and" - } - in (para $ "See " - <> cite [dominikInText] "" - <> "," <> space - <> cite [pandocCite'] "" - <> "," <> space <> "and others") - ] - - , "Inline LaTeX symbol" =: - "\\dots" =?> - para "…" - - , "Inline LaTeX command" =: - "\\textit{Emphasised}" =?> - para (emph "Emphasised") - - , "Inline LaTeX command with spaces" =: - "\\emph{Emphasis mine}" =?> - para (emph "Emphasis mine") - - , "Inline LaTeX math symbol" =: - "\\tau" =?> - para (emph "τ") - - , "Unknown inline LaTeX command" =: - "\\notacommand{foo}" =?> - para (rawInline "latex" "\\notacommand{foo}") - - , "Export snippet" =: - "@@html:<kbd>M-x org-agenda</kbd>@@" =?> - para (rawInline "html" "<kbd>M-x org-agenda</kbd>") - - , "MathML symbol in LaTeX-style" =: - "There is a hackerspace in Lübeck, Germany, called nbsp (unicode symbol: '\\nbsp')." =?> - para ("There is a hackerspace in Lübeck, Germany, called nbsp (unicode symbol: ' ').") - - , "MathML symbol in LaTeX-style, including braces" =: - "\\Aacute{}stor" =?> - para "Ástor" - - , "MathML copy sign" =: - "\\copy" =?> - para "©" - - , "MathML symbols, space separated" =: - "\\ForAll \\Auml" =?> - para "∀ Ä" - - , "LaTeX citation" =: - "\\cite{Coffee}" =?> - let citation = Citation - { citationId = "Coffee" - , citationPrefix = [] - , citationSuffix = [] - , citationMode = NormalCitation - , citationNoteNum = 0 - , citationHash = 0} - in (para . cite [citation] $ rawInline "latex" "\\cite{Coffee}") - - , "Macro" =: - T.unlines [ "#+MACRO: HELLO /Hello, $1/" - , "{{{HELLO(World)}}}" - ] =?> - para (emph "Hello, World") - - , "Macro repeting its argument" =: - T.unlines [ "#+MACRO: HELLO $1$1" - , "{{{HELLO(moin)}}}" - ] =?> - para "moinmoin" - - , "Macro called with too few arguments" =: - T.unlines [ "#+MACRO: HELLO Foo $1 $2 Bar" - , "{{{HELLO()}}}" - ] =?> - para "Foo Bar" - ] - - , testGroup "Meta Information" $ - [ "Comment" =: - "# Nothing to see here" =?> - (mempty::Blocks) - - , "Not a comment" =: - "#-tag" =?> - para "#-tag" - - , "Comment surrounded by Text" =: - T.unlines [ "Before" - , "# Comment" - , "After" - ] =?> - mconcat [ para "Before" - , para "After" - ] - - , "Title" =: - "#+TITLE: Hello, World" =?> - let titleInline = toList $ "Hello," <> space <> "World" - meta = setMeta "title" (MetaInlines titleInline) $ nullMeta - in Pandoc meta mempty - - , "Author" =: - "#+author: Albert /Emacs-Fanboy/ Krewinkel" =?> - let author = toList . spcSep $ [ "Albert", emph "Emacs-Fanboy", "Krewinkel" ] - meta = setMeta "author" (MetaList [MetaInlines author]) $ nullMeta - in Pandoc meta mempty - - , "Multiple authors" =: - "#+author: James Dewey Watson, Francis Harry Compton Crick " =?> - let watson = MetaInlines $ toList "James Dewey Watson" - crick = MetaInlines $ toList "Francis Harry Compton Crick" - meta = setMeta "author" (MetaList [watson, crick]) $ nullMeta - in Pandoc meta mempty - - , "Date" =: - "#+Date: Feb. *28*, 2014" =?> - let date = toList . spcSep $ [ "Feb.", (strong "28") <> ",", "2014" ] - meta = setMeta "date" (MetaInlines date) $ nullMeta - in Pandoc meta mempty - - , "Description" =: - "#+DESCRIPTION: Explanatory text" =?> - let description = "Explanatory text" - meta = setMeta "description" (MetaString description) $ nullMeta - in Pandoc meta mempty - - , "Properties drawer" =: - T.unlines [ " :PROPERTIES:" - , " :setting: foo" - , " :END:" - ] =?> - (mempty::Blocks) - - , "LaTeX_headers options are translated to header-includes" =: - "#+LaTeX_header: \\usepackage{tikz}" =?> - let latexInlines = rawInline "latex" "\\usepackage{tikz}" - inclList = MetaList [MetaInlines (toList latexInlines)] - meta = setMeta "header-includes" inclList nullMeta - in Pandoc meta mempty - - , "LaTeX_class option is translated to documentclass" =: - "#+LATEX_CLASS: article" =?> - let meta = setMeta "documentclass" (MetaString "article") nullMeta - in Pandoc meta mempty - - , "LaTeX_class_options is translated to classoption" =: - "#+LATEX_CLASS_OPTIONS: [a4paper]" =?> - let meta = setMeta "classoption" (MetaString "a4paper") nullMeta - in Pandoc meta mempty - - , "LaTeX_class_options is translated to classoption" =: - "#+html_head: <meta/>" =?> - let html = rawInline "html" "<meta/>" - inclList = MetaList [MetaInlines (toList html)] - meta = setMeta "header-includes" inclList nullMeta - in Pandoc meta mempty - - , "later meta definitions take precedence" =: - T.unlines [ "#+AUTHOR: this will not be used" - , "#+author: Max" - ] =?> - let author = MetaInlines [Str "Max"] - meta = setMeta "author" (MetaList [author]) $ nullMeta - in Pandoc meta mempty - - , "Logbook drawer" =: - T.unlines [ " :LogBook:" - , " - State \"DONE\" from \"TODO\" [2014-03-03 Mon 11:00]" - , " :END:" - ] =?> - (mempty::Blocks) - - , "Drawer surrounded by text" =: - T.unlines [ "Before" - , ":PROPERTIES:" - , ":END:" - , "After" - ] =?> - para "Before" <> para "After" - - , "Drawer markers must be the only text in the line" =: - T.unlines [ " :LOGBOOK: foo" - , " :END: bar" - ] =?> - para (":LOGBOOK: foo" <> softbreak <> ":END: bar") - - , "Drawers can be arbitrary" =: - T.unlines [ ":FOO:" - , "/bar/" - , ":END:" - ] =?> - divWith (mempty, ["FOO", "drawer"], mempty) (para $ emph "bar") - - , "Anchor reference" =: - T.unlines [ "<<link-here>> Target." - , "" - , "[[link-here][See here!]]" - ] =?> - (para (spanWith ("link-here", [], []) mempty <> "Target.") <> - para (link "#link-here" "" ("See" <> space <> "here!"))) - - , "Search links are read as emph" =: - "[[Wally][Where's Wally?]]" =?> - (para (emph $ "Where's" <> space <> "Wally?")) - - , "Link to nonexistent anchor" =: - T.unlines [ "<<link-here>> Target." - , "" - , "[[link$here][See here!]]" - ] =?> - (para (spanWith ("link-here", [], []) mempty <> "Target.") <> - para (emph ("See" <> space <> "here!"))) - - , "Link abbreviation" =: - T.unlines [ "#+LINK: wp https://en.wikipedia.org/wiki/%s" - , "[[wp:Org_mode][Wikipedia on Org-mode]]" - ] =?> - (para (link "https://en.wikipedia.org/wiki/Org_mode" "" - ("Wikipedia" <> space <> "on" <> space <> "Org-mode"))) - - , "Link abbreviation, defined after first use" =: - T.unlines [ "[[zl:non-sense][Non-sense articles]]" - , "#+LINK: zl http://zeitlens.com/tags/%s.html" - ] =?> - (para (link "http://zeitlens.com/tags/non-sense.html" "" - ("Non-sense" <> space <> "articles"))) - - , "Link abbreviation, URL encoded arguments" =: - T.unlines [ "#+link: expl http://example.com/%h/foo" - , "[[expl:Hello, World!][Moin!]]" - ] =?> - (para (link "http://example.com/Hello%2C%20World%21/foo" "" "Moin!")) - - , "Link abbreviation, append arguments" =: - T.unlines [ "#+link: expl http://example.com/" - , "[[expl:foo][bar]]" - ] =?> - (para (link "http://example.com/foo" "" "bar")) - - - , testGroup "export options" - - [ "disable simple sub/superscript syntax" =: - T.unlines [ "#+OPTIONS: ^:nil" - , "a^b" - ] =?> - para "a^b" - - , "directly select drawers to be exported" =: - T.unlines [ "#+OPTIONS: d:(\"IMPORTANT\")" - , ":IMPORTANT:" - , "23" - , ":END:" - , ":BORING:" - , "very boring" - , ":END:" - ] =?> - divWith (mempty, ["IMPORTANT", "drawer"], mempty) (para "23") - - , "exclude drawers from being exported" =: - T.unlines [ "#+OPTIONS: d:(not \"BORING\")" - , ":IMPORTANT:" - , "5" - , ":END:" - , ":BORING:" - , "very boring" - , ":END:" - ] =?> - divWith (mempty, ["IMPORTANT", "drawer"], mempty) (para "5") - - , "don't include archive trees" =: - T.unlines [ "#+OPTIONS: arch:nil" - , "* old :ARCHIVE:" - ] =?> - (mempty ::Blocks) - - , "include complete archive trees" =: - T.unlines [ "#+OPTIONS: arch:t" - , "* old :ARCHIVE:" - , " boring" - ] =?> - mconcat [ headerWith ("old", [], mempty) 1 - ("old" <> space <> tagSpan "ARCHIVE") - , para "boring" - ] - - , "include archive tree header only" =: - T.unlines [ "#+OPTIONS: arch:headline" - , "* old :ARCHIVE:" - , " boring" - ] =?> - headerWith ("old", [], mempty) 1 ("old" <> space <> tagSpan "ARCHIVE") - - , "limit headline depth" =: - T.unlines [ "#+OPTIONS: H:2" - , "* top-level section" - , "** subsection" - , "*** list item 1" - , "*** list item 2" - ] =?> - mconcat [ headerWith ("top-level-section", [], []) 1 "top-level section" - , headerWith ("subsection", [], []) 2 "subsection" - , orderedList [ para "list item 1", para "list item 2" ] - ] - - , "turn all headlines into lists" =: - T.unlines [ "#+OPTIONS: H:0" - , "first block" - , "* top-level section 1" - , "** subsection" - , "* top-level section 2" - ] =?> - mconcat [ para "first block" - , orderedList - [ (para "top-level section 1" <> - orderedList [ para "subsection" ]) - , para "top-level section 2" ] - ] - - , "preserve linebreaks as hard breaks" =: - T.unlines [ "#+OPTIONS: \\n:t" - , "first" - , "second" - ] =?> - para ("first" <> linebreak <> "second") - - , "disable author export" =: - T.unlines [ "#+OPTIONS: author:nil" - , "#+AUTHOR: ShyGuy" - ] =?> - Pandoc nullMeta mempty - - , "disable creator export" =: - T.unlines [ "#+OPTIONS: creator:nil" - , "#+creator: The Architect" - ] =?> - Pandoc nullMeta mempty - - , "disable email export" =: - T.unlines [ "#+OPTIONS: email:nil" - , "#+email: no-mail-please@example.com" - ] =?> - Pandoc nullMeta mempty - - , "disable inclusion of todo keywords" =: - T.unlines [ "#+OPTIONS: todo:nil" - , "** DONE todo export" - ] =?> - headerWith ("todo-export", [], []) 2 "todo export" - - , "remove tags from headlines" =: - T.unlines [ "#+OPTIONS: tags:nil" - , "* Headline :hello:world:" - ] =?> - headerWith ("headline", [], mempty) 1 "Headline" - ] - ] - - , testGroup "Basic Blocks" $ - [ "Paragraph" =: - "Paragraph\n" =?> - para "Paragraph" - - , testGroup "headers" $ - [ "First Level Header" =: - "* Headline\n" =?> - headerWith ("headline", [], []) 1 "Headline" - - , "Third Level Header" =: - "*** Third Level Headline\n" =?> - headerWith ("third-level-headline", [], []) - 3 - ("Third" <> space <> "Level" <> space <> "Headline") - - , "Compact Headers with Paragraph" =: - T.unlines [ "* First Level" - , "** Second Level" - , " Text" - ] =?> - mconcat [ headerWith ("first-level", [], []) - 1 - ("First" <> space <> "Level") - , headerWith ("second-level", [], []) - 2 - ("Second" <> space <> "Level") - , para "Text" - ] - - , "Separated Headers with Paragraph" =: - T.unlines [ "* First Level" - , "" - , "** Second Level" - , "" - , " Text" - ] =?> - mconcat [ headerWith ("first-level", [], []) - 1 - ("First" <> space <> "Level") - , headerWith ("second-level", [], []) - 2 - ("Second" <> space <> "Level") - , para "Text" - ] - - , "Headers not preceded by a blank line" =: - T.unlines [ "** eat dinner" - , "Spaghetti and meatballs tonight." - , "** walk dog" - ] =?> - mconcat [ headerWith ("eat-dinner", [], []) - 2 - ("eat" <> space <> "dinner") - , para $ spcSep [ "Spaghetti", "and", "meatballs", "tonight." ] - , headerWith ("walk-dog", [], []) - 2 - ("walk" <> space <> "dog") - ] - - , testGroup "Todo keywords" - [ "Header with known todo keyword" =: - "* TODO header" =?> - let todoSpan = spanWith ("", ["todo", "TODO"], []) "TODO" - in headerWith ("header", [], []) 1 (todoSpan <> space <> "header") - - , "Header marked as done" =: - "* DONE header" =?> - let todoSpan = spanWith ("", ["done", "DONE"], []) "DONE" - in headerWith ("header", [], []) 1 (todoSpan <> space <> "header") - - , "Header with unknown todo keyword" =: - "* WAITING header" =?> - headerWith ("waiting-header", [], []) 1 "WAITING header" - - , "Custom todo keywords" =: - T.unlines [ "#+TODO: WAITING CANCELLED" - , "* WAITING compile" - , "* CANCELLED lunch" - ] =?> - let todoSpan = spanWith ("", ["todo", "WAITING"], []) "WAITING" - doneSpan = spanWith ("", ["done", "CANCELLED"], []) "CANCELLED" - in headerWith ("compile", [], []) 1 (todoSpan <> space <> "compile") - <> headerWith ("lunch", [], []) 1 (doneSpan <> space <> "lunch") - - , "Custom todo keywords with multiple done-states" =: - T.unlines [ "#+TODO: WAITING | DONE CANCELLED " - , "* WAITING compile" - , "* CANCELLED lunch" - , "* DONE todo-feature" - ] =?> - let waiting = spanWith ("", ["todo", "WAITING"], []) "WAITING" - cancelled = spanWith ("", ["done", "CANCELLED"], []) "CANCELLED" - done = spanWith ("", ["done", "DONE"], []) "DONE" - in headerWith ("compile", [], []) 1 (waiting <> space <> "compile") - <> headerWith ("lunch", [], []) 1 (cancelled <> space <> "lunch") - <> headerWith ("todo-feature", [], []) 1 (done <> space <> "todo-feature") - ] - - , "Tagged headers" =: - T.unlines [ "* Personal :PERSONAL:" - , "** Call Mom :@PHONE:" - , "** Call John :@PHONE:JOHN: " - ] =?> - mconcat [ headerWith ("personal", [], []) - 1 - ("Personal " <> tagSpan "PERSONAL") - , headerWith ("call-mom", [], []) - 2 - ("Call Mom " <> tagSpan "@PHONE") - , headerWith ("call-john", [], []) - 2 - ("Call John " <> tagSpan "@PHONE" <> "\160" <> tagSpan "JOHN") - ] - - , "Untagged header containing colons" =: - "* This: is not: tagged" =?> - headerWith ("this-is-not-tagged", [], []) 1 "This: is not: tagged" - - , "Header starting with strokeout text" =: - T.unlines [ "foo" - , "" - , "* +thing+ other thing" - ] =?> - mconcat [ para "foo" - , headerWith ("thing-other-thing", [], []) - 1 - ((strikeout "thing") <> " other thing") - ] - - , "Comment Trees" =: - T.unlines [ "* COMMENT A comment tree" - , " Not much going on here" - , "** This will be dropped" - , "* Comment tree above" - ] =?> - headerWith ("comment-tree-above", [], []) 1 "Comment tree above" - - , "Nothing but a COMMENT header" =: - "* COMMENT Test" =?> - (mempty::Blocks) - - , "Tree with :noexport:" =: - T.unlines [ "* Should be ignored :archive:noexport:old:" - , "** Old stuff" - , " This is not going to be exported" - ] =?> - (mempty::Blocks) - - , "Subtree with :noexport:" =: - T.unlines [ "* Exported" - , "** This isn't exported :noexport:" - , "*** This neither" - , "** But this is" - ] =?> - mconcat [ headerWith ("exported", [], []) 1 "Exported" - , headerWith ("but-this-is", [], []) 2 "But this is" - ] - - , "Preferences are treated as header attributes" =: - T.unlines [ "* foo" - , " :PROPERTIES:" - , " :custom_id: fubar" - , " :bar: baz" - , " :END:" - ] =?> - headerWith ("fubar", [], [("bar", "baz")]) 1 "foo" - - - , "Headers marked with a unnumbered property get a class of the same name" =: - T.unlines [ "* Not numbered" - , " :PROPERTIES:" - , " :UNNUMBERED: t" - , " :END:" - ] =?> - headerWith ("not-numbered", ["unnumbered"], []) 1 "Not numbered" - ] - , "Paragraph starting with an asterisk" =: - "*five" =?> - para "*five" - - , "Paragraph containing asterisk at beginning of line" =: - T.unlines [ "lucky" - , "*star" - ] =?> - para ("lucky" <> softbreak <> "*star") - - , "Example block" =: - T.unlines [ ": echo hello" - , ": echo dear tester" - ] =?> - codeBlockWith ("", ["example"], []) "echo hello\necho dear tester\n" - - , "Example block surrounded by text" =: - T.unlines [ "Greetings" - , ": echo hello" - , ": echo dear tester" - , "Bye" - ] =?> - mconcat [ para "Greetings" - , codeBlockWith ("", ["example"], []) - "echo hello\necho dear tester\n" - , para "Bye" - ] - - , "Horizontal Rule" =: - T.unlines [ "before" - , "-----" - , "after" - ] =?> - mconcat [ para "before" - , horizontalRule - , para "after" - ] - - , "Not a Horizontal Rule" =: - "----- em and en dash" =?> - para "\8212\8211 em and en dash" - - , "Comment Block" =: - T.unlines [ "#+BEGIN_COMMENT" - , "stuff" - , "bla" - , "#+END_COMMENT"] =?> - (mempty::Blocks) - - , testGroup "Figures" $ - [ "Figure" =: - T.unlines [ "#+caption: A very courageous man." - , "#+name: goodguy" - , "[[file:edward.jpg]]" - ] =?> - para (image "edward.jpg" "fig:goodguy" "A very courageous man.") - - , "Figure with no name" =: - T.unlines [ "#+caption: I've been through the desert on this" - , "[[file:horse.png]]" - ] =?> - para (image "horse.png" "fig:" "I've been through the desert on this") - - , "Figure with `fig:` prefix in name" =: - T.unlines [ "#+caption: Used as a metapher in evolutionary biology." - , "#+name: fig:redqueen" - , "[[./the-red-queen.jpg]]" - ] =?> - para (image "./the-red-queen.jpg" "fig:redqueen" - "Used as a metapher in evolutionary biology.") - - , "Figure with HTML attributes" =: - T.unlines [ "#+CAPTION: mah brain just explodid" - , "#+NAME: lambdacat" - , "#+ATTR_HTML: :style color: blue :role button" - , "[[file:lambdacat.jpg]]" - ] =?> - let kv = [("style", "color: blue"), ("role", "button")] - name = "fig:lambdacat" - caption = "mah brain just explodid" - in para (imageWith (mempty, mempty, kv) "lambdacat.jpg" name caption) - - , "Labelled figure" =: - T.unlines [ "#+CAPTION: My figure" - , "#+LABEL: fig:myfig" - , "[[file:blub.png]]" - ] =?> - let attr = ("fig:myfig", mempty, mempty) - in para (imageWith attr "blub.png" "fig:" "My figure") - - , "Figure with empty caption" =: - T.unlines [ "#+CAPTION:" - , "[[file:guess.jpg]]" - ] =?> - para (image "guess.jpg" "fig:" "") - ] - - , testGroup "Footnotes" - [ "Footnote" =: - T.unlines [ "A footnote[1]" - , "" - , "[1] First paragraph" - , "" - , "second paragraph" - ] =?> - para (mconcat - [ "A", space, "footnote" - , note $ mconcat [ para ("First" <> space <> "paragraph") - , para ("second" <> space <> "paragraph") - ] - ]) - - , "Two footnotes" =: - T.unlines [ "Footnotes[fn:1][fn:2]" - , "" - , "[fn:1] First note." - , "" - , "[fn:2] Second note." - ] =?> - para (mconcat - [ "Footnotes" - , note $ para ("First" <> space <> "note.") - , note $ para ("Second" <> space <> "note.") - ]) - - , "Emphasized text before footnote" =: - T.unlines [ "/text/[fn:1]" - , "" - , "[fn:1] unicorn" - ] =?> - para (mconcat - [ emph "text" - , note . para $ "unicorn" - ]) - - , "Footnote that starts with emphasized text" =: - T.unlines [ "text[fn:1]" - , "" - , "[fn:1] /emphasized/" - ] =?> - para (mconcat - [ "text" - , note . para $ emph "emphasized" - ]) - - , "Footnote followed by header" =: - T.unlines [ "Another note[fn:yay]" - , "" - , "[fn:yay] This is great!" - , "" - , "** Headline" - ] =?> - mconcat - [ para (mconcat - [ "Another", space, "note" - , note $ para ("This" <> space <> "is" <> space <> "great!") - ]) - , headerWith ("headline", [], []) 2 "Headline" - ] - - , "Footnote followed by two blank lines" =: - T.unlines [ "footnote[fn:blanklines]" - , "" - , "[fn:blanklines] followed by blank lines" - , "" - , "" - , "next" - ] =?> - mconcat - [ para ("footnote" <> note (para "followed by blank lines")) - , para "next" - ] - ] - ] - - , testGroup "Lists" $ - [ "Simple Bullet Lists" =: - ("- Item1\n" <> - "- Item2\n") =?> - bulletList [ plain "Item1" - , plain "Item2" - ] - - , "Indented Bullet Lists" =: - (" - Item1\n" <> - " - Item2\n") =?> - bulletList [ plain "Item1" - , plain "Item2" - ] - - , "Unindented *" =: - ("- Item1\n" <> - "* Item2\n") =?> - bulletList [ plain "Item1" - ] <> - headerWith ("item2", [], []) 1 "Item2" - - , "Multi-line Bullet Lists" =: - ("- *Fat\n" <> - " Tony*\n" <> - "- /Sideshow\n" <> - " Bob/") =?> - bulletList [ plain $ strong ("Fat" <> softbreak <> "Tony") - , plain $ emph ("Sideshow" <> softbreak <> "Bob") - ] - - , "Nested Bullet Lists" =: - ("- Discovery\n" <> - " + One More Time\n" <> - " + Harder, Better, Faster, Stronger\n" <> - "- Homework\n" <> - " + Around the World\n"<> - "- Human After All\n" <> - " + Technologic\n" <> - " + Robot Rock\n") =?> - bulletList [ mconcat - [ plain "Discovery" - , bulletList [ plain ("One" <> space <> - "More" <> space <> - "Time") - , plain ("Harder," <> space <> - "Better," <> space <> - "Faster," <> space <> - "Stronger") - ] - ] - , mconcat - [ plain "Homework" - , bulletList [ plain ("Around" <> space <> - "the" <> space <> - "World") - ] - ] - , mconcat - [ plain ("Human" <> space <> "After" <> space <> "All") - , bulletList [ plain "Technologic" - , plain ("Robot" <> space <> "Rock") - ] - ] - ] - - , "Bullet List with Decreasing Indent" =: - (" - Discovery\n\ - \ - Human After All\n") =?> - mconcat [ bulletList [ plain "Discovery" ] - , bulletList [ plain ("Human" <> space <> "After" <> space <> "All")] - ] - - , "Header follows Bullet List" =: - (" - Discovery\n\ - \ - Human After All\n\ - \* Homework") =?> - mconcat [ bulletList [ plain "Discovery" - , plain ("Human" <> space <> "After" <> space <> "All") - ] - , headerWith ("homework", [], []) 1 "Homework" - ] - - , "Bullet List Unindented with trailing Header" =: - ("- Discovery\n\ - \- Homework\n\ - \* NotValidListItem") =?> - mconcat [ bulletList [ plain "Discovery" - , plain "Homework" - ] - , headerWith ("notvalidlistitem", [], []) 1 "NotValidListItem" - ] - - , "Empty bullet points" =: - T.unlines [ "-" - , "- " - ] =?> - bulletList [ plain "", plain "" ] - - , "Simple Ordered List" =: - ("1. Item1\n" <> - "2. Item2\n") =?> - let listStyle = (1, DefaultStyle, DefaultDelim) - listStructure = [ plain "Item1" - , plain "Item2" - ] - in orderedListWith listStyle listStructure - - , "Simple Ordered List with Parens" =: - ("1) Item1\n" <> - "2) Item2\n") =?> - let listStyle = (1, DefaultStyle, DefaultDelim) - listStructure = [ plain "Item1" - , plain "Item2" - ] - in orderedListWith listStyle listStructure - - , "Indented Ordered List" =: - (" 1. Item1\n" <> - " 2. Item2\n") =?> - let listStyle = (1, DefaultStyle, DefaultDelim) - listStructure = [ plain "Item1" - , plain "Item2" - ] - in orderedListWith listStyle listStructure - - , "Empty ordered list item" =: - T.unlines [ "1." - , "3. " - ] =?> - orderedList [ plain "", plain "" ] - - , "Nested Ordered Lists" =: - ("1. One\n" <> - " 1. One-One\n" <> - " 2. One-Two\n" <> - "2. Two\n" <> - " 1. Two-One\n"<> - " 2. Two-Two\n") =?> - let listStyle = (1, DefaultStyle, DefaultDelim) - listStructure = [ mconcat - [ plain "One" - , orderedList [ plain "One-One" - , plain "One-Two" - ] - ] - , mconcat - [ plain "Two" - , orderedList [ plain "Two-One" - , plain "Two-Two" - ] - ] - ] - in orderedListWith listStyle listStructure - - , "Ordered List in Bullet List" =: - ("- Emacs\n" <> - " 1. Org\n") =?> - bulletList [ (plain "Emacs") <> - (orderedList [ plain "Org"]) - ] - - , "Bullet List in Ordered List" =: - ("1. GNU\n" <> - " - Freedom\n") =?> - orderedList [ (plain "GNU") <> bulletList [ (plain "Freedom") ] ] - - , "Definition List" =: - T.unlines [ "- PLL :: phase-locked loop" - , "- TTL ::" - , " transistor-transistor logic" - , "- PSK :: phase-shift keying" - , "" - , " a digital modulation scheme" - ] =?> - definitionList [ ("PLL", [ plain $ "phase-locked" <> space <> "loop" ]) - , ("TTL", [ plain $ "transistor-transistor" <> space <> - "logic" ]) - , ("PSK", [ mconcat - [ para $ "phase-shift" <> space <> "keying" - , para $ spcSep [ "a", "digital" - , "modulation", "scheme" ] - ] - ]) - ] - , "Definition list with multi-word term" =: - " - Elijah Wood :: He plays Frodo" =?> - definitionList [ ("Elijah" <> space <> "Wood", [plain $ "He" <> space <> "plays" <> space <> "Frodo"])] - , "Compact definition list" =: - T.unlines [ "- ATP :: adenosine 5' triphosphate" - , "- DNA :: deoxyribonucleic acid" - , "- PCR :: polymerase chain reaction" - , "" - ] =?> - definitionList - [ ("ATP", [ plain $ spcSep [ "adenosine", "5'", "triphosphate" ] ]) - , ("DNA", [ plain $ spcSep [ "deoxyribonucleic", "acid" ] ]) - , ("PCR", [ plain $ spcSep [ "polymerase", "chain", "reaction" ] ]) - ] - - , "Definition List With Trailing Header" =: - "- definition :: list\n\ - \- cool :: defs\n\ - \* header" =?> - mconcat [ definitionList [ ("definition", [plain "list"]) - , ("cool", [plain "defs"]) - ] - , headerWith ("header", [], []) 1 "header" - ] - - , "Definition lists double-colon markers must be surrounded by whitespace" =: - "- std::cout" =?> - bulletList [ plain "std::cout" ] - - , "Loose bullet list" =: - T.unlines [ "- apple" - , "" - , "- orange" - , "" - , "- peach" - ] =?> - bulletList [ para "apple" - , para "orange" - , para "peach" - ] - - , "Recognize preceding paragraphs in non-list contexts" =: - T.unlines [ "CLOSED: [2015-10-19 Mon 15:03]" - , "- Note taken on [2015-10-19 Mon 13:24]" - ] =?> - mconcat [ para "CLOSED: [2015-10-19 Mon 15:03]" - , bulletList [ plain "Note taken on [2015-10-19 Mon 13:24]" ] - ] - ] - - , testGroup "Tables" - [ "Single cell table" =: - "|Test|" =?> - simpleTable' 1 mempty [[plain "Test"]] - - , "Multi cell table" =: - "| One | Two |" =?> - simpleTable' 2 mempty [ [ plain "One", plain "Two" ] ] - - , "Multi line table" =: - T.unlines [ "| One |" - , "| Two |" - , "| Three |" - ] =?> - simpleTable' 1 mempty - [ [ plain "One" ] - , [ plain "Two" ] - , [ plain "Three" ] - ] - - , "Empty table" =: - "||" =?> - simpleTable' 1 mempty [[mempty]] - - , "Glider Table" =: - T.unlines [ "| 1 | 0 | 0 |" - , "| 0 | 1 | 1 |" - , "| 1 | 1 | 0 |" - ] =?> - simpleTable' 3 mempty - [ [ plain "1", plain "0", plain "0" ] - , [ plain "0", plain "1", plain "1" ] - , [ plain "1", plain "1", plain "0" ] - ] - - , "Table between Paragraphs" =: - T.unlines [ "Before" - , "| One | Two |" - , "After" - ] =?> - mconcat [ para "Before" - , simpleTable' 2 mempty [ [ plain "One", plain "Two" ] ] - , para "After" - ] - - , "Table with Header" =: - T.unlines [ "| Species | Status |" - , "|--------------+--------------|" - , "| cervisiae | domesticated |" - , "| paradoxus | wild |" - ] =?> - simpleTable [ plain "Species", plain "Status" ] - [ [ plain "cervisiae", plain "domesticated" ] - , [ plain "paradoxus", plain "wild" ] - ] - - , "Table with final hline" =: - T.unlines [ "| cervisiae | domesticated |" - , "| paradoxus | wild |" - , "|--------------+--------------|" - ] =?> - simpleTable' 2 mempty - [ [ plain "cervisiae", plain "domesticated" ] - , [ plain "paradoxus", plain "wild" ] - ] - - , "Table in a box" =: - T.unlines [ "|---------|---------|" - , "| static | Haskell |" - , "| dynamic | Lisp |" - , "|---------+---------|" - ] =?> - simpleTable' 2 mempty - [ [ plain "static", plain "Haskell" ] - , [ plain "dynamic", plain "Lisp" ] - ] - - , "Table with empty cells" =: - "|||c|" =?> - simpleTable' 3 mempty [[mempty, mempty, plain "c"]] - - , "Table with empty rows" =: - T.unlines [ "| first |" - , "| |" - , "| third |" - ] =?> - simpleTable' 1 mempty [[plain "first"], [mempty], [plain "third"]] - - , "Table with alignment row" =: - T.unlines [ "| Numbers | Text | More |" - , "| <c> | <r> | |" - , "| 1 | One | foo |" - , "| 2 | Two | bar |" - ] =?> - table "" (zip [AlignCenter, AlignRight, AlignDefault] [0, 0, 0]) - [] - [ [ plain "Numbers", plain "Text", plain "More" ] - , [ plain "1" , plain "One" , plain "foo" ] - , [ plain "2" , plain "Two" , plain "bar" ] - ] - - , "Pipe within text doesn't start a table" =: - "Ceci n'est pas une | pipe " =?> - para (spcSep [ "Ceci", "n'est", "pas", "une", "|", "pipe" ]) - - , "Missing pipe at end of row" =: - "|incomplete-but-valid" =?> - simpleTable' 1 mempty [ [ plain "incomplete-but-valid" ] ] - - , "Table with differing row lengths" =: - T.unlines [ "| Numbers | Text " - , "|-" - , "| <c> | <r> |" - , "| 1 | One | foo |" - , "| 2" - ] =?> - table "" (zip [AlignCenter, AlignRight] [0, 0]) - [ plain "Numbers", plain "Text" ] - [ [ plain "1" , plain "One" , plain "foo" ] - , [ plain "2" ] - ] - - , "Table with caption" =: - T.unlines [ "#+CAPTION: Hitchhiker's Multiplication Table" - , "| x | 6 |" - , "| 9 | 42 |" - ] =?> - table "Hitchhiker's Multiplication Table" - [(AlignDefault, 0), (AlignDefault, 0)] - [] - [ [ plain "x", plain "6" ] - , [ plain "9", plain "42" ] - ] - ] - - , testGroup "Blocks and fragments" - [ "Source block" =: - T.unlines [ " #+BEGIN_SRC haskell" - , " main = putStrLn greeting" - , " where greeting = \"moin\"" - , " #+END_SRC" ] =?> - let attr' = ("", ["haskell"], []) - code' = "main = putStrLn greeting\n" <> - " where greeting = \"moin\"\n" - in codeBlockWith attr' code' - - , "Source block with indented code" =: - T.unlines [ " #+BEGIN_SRC haskell" - , " main = putStrLn greeting" - , " where greeting = \"moin\"" - , " #+END_SRC" ] =?> - let attr' = ("", ["haskell"], []) - code' = "main = putStrLn greeting\n" <> - " where greeting = \"moin\"\n" - in codeBlockWith attr' code' - - , "Source block with tab-indented code" =: - T.unlines [ "\t#+BEGIN_SRC haskell" - , "\tmain = putStrLn greeting" - , "\t where greeting = \"moin\"" - , "\t#+END_SRC" ] =?> - let attr' = ("", ["haskell"], []) - code' = "main = putStrLn greeting\n" <> - " where greeting = \"moin\"\n" - in codeBlockWith attr' code' - - , "Empty source block" =: - T.unlines [ " #+BEGIN_SRC haskell" - , " #+END_SRC" ] =?> - let attr' = ("", ["haskell"], []) - code' = "" - in codeBlockWith attr' code' - - , "Source block between paragraphs" =: - T.unlines [ "Low German greeting" - , " #+BEGIN_SRC haskell" - , " main = putStrLn greeting" - , " where greeting = \"Moin!\"" - , " #+END_SRC" ] =?> - let attr' = ("", ["haskell"], []) - code' = "main = putStrLn greeting\n" <> - " where greeting = \"Moin!\"\n" - in mconcat [ para $ spcSep [ "Low", "German", "greeting" ] - , codeBlockWith attr' code' - ] - , "Source block with babel arguments" =: - T.unlines [ "#+BEGIN_SRC emacs-lisp :exports both" - , "(progn (message \"Hello, World!\")" - , " (+ 23 42))" - , "#+END_SRC" ] =?> - let classes = [ "commonlisp" ] -- as kate doesn't know emacs-lisp syntax - params = [ ("org-language", "emacs-lisp") - , ("exports", "both") - ] - code' = unlines [ "(progn (message \"Hello, World!\")" - , " (+ 23 42))" ] - in codeBlockWith ("", classes, params) code' - - , "Source block with results and :exports both" =: - T.unlines [ "#+BEGIN_SRC emacs-lisp :exports both" - , "(progn (message \"Hello, World!\")" - , " (+ 23 42))" - , "#+END_SRC" - , "" - , "#+RESULTS:" - , ": 65"] =?> - let classes = [ "commonlisp" ] - params = [ ("org-language", "emacs-lisp") - , ("exports", "both") - ] - code' = unlines [ "(progn (message \"Hello, World!\")" - , " (+ 23 42))" ] - results' = "65\n" - in codeBlockWith ("", classes, params) code' - <> - codeBlockWith ("", ["example"], []) results' - - , "Source block with results and :exports code" =: - T.unlines [ "#+BEGIN_SRC emacs-lisp :exports code" - , "(progn (message \"Hello, World!\")" - , " (+ 23 42))" - , "#+END_SRC" - , "" - , "#+RESULTS:" - , ": 65" ] =?> - let classes = [ "commonlisp" ] - params = [ ("org-language", "emacs-lisp") - , ("exports", "code") - ] - code' = unlines [ "(progn (message \"Hello, World!\")" - , " (+ 23 42))" ] - in codeBlockWith ("", classes, params) code' - - , "Source block with results and :exports results" =: - T.unlines [ "#+BEGIN_SRC emacs-lisp :exports results" - , "(progn (message \"Hello, World!\")" - , " (+ 23 42))" - , "#+END_SRC" - , "" - , "#+RESULTS:" - , ": 65" ] =?> - let results' = "65\n" - in codeBlockWith ("", ["example"], []) results' - - , "Source block with results and :exports none" =: - T.unlines [ "#+BEGIN_SRC emacs-lisp :exports none" - , "(progn (message \"Hello, World!\")" - , " (+ 23 42))" - , "#+END_SRC" - , "" - , "#+RESULTS:" - , ": 65" ] =?> - (mempty :: Blocks) - - , "Source block with toggling header arguments" =: - T.unlines [ "#+BEGIN_SRC sh :noeval" - , "echo $HOME" - , "#+END_SRC" - ] =?> - let classes = [ "bash" ] - params = [ ("org-language", "sh"), ("noeval", "yes") ] - in codeBlockWith ("", classes, params) "echo $HOME\n" - - , "Source block with line number switch" =: - T.unlines [ "#+BEGIN_SRC sh -n 10" - , ":() { :|:& };:" - , "#+END_SRC" - ] =?> - let classes = [ "bash", "numberLines" ] - params = [ ("org-language", "sh"), ("startFrom", "10") ] - in codeBlockWith ("", classes, params) ":() { :|:& };:\n" - - , "Source block with multi-word parameter values" =: - T.unlines [ "#+BEGIN_SRC dot :cmdline -Kdot -Tpng " - , "digraph { id [label=\"ID\"] }" - , "#+END_SRC" - ] =?> - let classes = [ "dot" ] - params = [ ("cmdline", "-Kdot -Tpng") ] - in codeBlockWith ("", classes, params) "digraph { id [label=\"ID\"] }\n" - - , "Example block" =: - T.unlines [ "#+begin_example" - , "A chosen representation of" - , "a rule." - , "#+eND_exAMPle" - ] =?> - codeBlockWith ("", ["example"], []) - "A chosen representation of\na rule.\n" - - , "HTML block" =: - T.unlines [ "#+BEGIN_HTML" - , "<aside>HTML5 is pretty nice.</aside>" - , "#+END_HTML" - ] =?> - rawBlock "html" "<aside>HTML5 is pretty nice.</aside>\n" - - , "Quote block" =: - T.unlines [ "#+BEGIN_QUOTE" - , "/Niemand/ hat die Absicht, eine Mauer zu errichten!" - , "#+END_QUOTE" - ] =?> - blockQuote (para (spcSep [ emph "Niemand", "hat", "die", "Absicht," - , "eine", "Mauer", "zu", "errichten!" - ])) - - , "Verse block" =: - T.unlines [ "The first lines of Goethe's /Faust/:" - , "#+begin_verse" - , "Habe nun, ach! Philosophie," - , "Juristerei und Medizin," - , "Und leider auch Theologie!" - , "Durchaus studiert, mit heißem Bemühn." - , "#+end_verse" - ] =?> - mconcat - [ para $ spcSep [ "The", "first", "lines", "of" - , "Goethe's", emph "Faust" <> ":"] - , lineBlock - [ "Habe nun, ach! Philosophie," - , "Juristerei und Medizin," - , "Und leider auch Theologie!" - , "Durchaus studiert, mit heißem Bemühn." - ] - ] - - , "Verse block with blank lines" =: - T.unlines [ "#+BEGIN_VERSE" - , "foo" - , "" - , "bar" - , "#+END_VERSE" - ] =?> - lineBlock [ "foo", mempty, "bar" ] - - , "Verse block with varying indentation" =: - T.unlines [ "#+BEGIN_VERSE" - , " hello darkness" - , "my old friend" - , "#+END_VERSE" - ] =?> - lineBlock [ "\160\160hello darkness", "my old friend" ] - - , "Raw block LaTeX" =: - T.unlines [ "#+BEGIN_LaTeX" - , "The category $\\cat{Set}$ is adhesive." - , "#+END_LaTeX" - ] =?> - rawBlock "latex" "The category $\\cat{Set}$ is adhesive.\n" - - , "Raw LaTeX line" =: - "#+LATEX: \\let\\foo\\bar" =?> - rawBlock "latex" "\\let\\foo\\bar" - - , "Raw Beamer line" =: - "#+beamer: \\pause" =?> - rawBlock "beamer" "\\pause" - - , "Raw HTML line" =: - "#+HTML: <aside>not important</aside>" =?> - rawBlock "html" "<aside>not important</aside>" - - , "Export block HTML" =: - T.unlines [ "#+BEGIN_export html" - , "<samp>Hello, World!</samp>" - , "#+END_export" - ] =?> - rawBlock "html" "<samp>Hello, World!</samp>\n" - - , "LaTeX fragment" =: - T.unlines [ "\\begin{equation}" - , "X_i = \\begin{cases}" - , " G_{\\alpha(i)} & \\text{if }\\alpha(i-1) = \\alpha(i)\\\\" - , " C_{\\alpha(i)} & \\text{otherwise}" - , " \\end{cases}" - , "\\end{equation}" - ] =?> - rawBlock "latex" - (unlines [ "\\begin{equation}" - , "X_i = \\begin{cases}" - , " G_{\\alpha(i)} & \\text{if }\\alpha(i-1) =" <> - " \\alpha(i)\\\\" - , " C_{\\alpha(i)} & \\text{otherwise}" - , " \\end{cases}" - , "\\end{equation}" - ]) - - , "Code block with caption" =: - T.unlines [ "#+CAPTION: Functor laws in Haskell" - , "#+NAME: functor-laws" - , "#+BEGIN_SRC haskell" - , "fmap id = id" - , "fmap (p . q) = (fmap p) . (fmap q)" - , "#+END_SRC" - ] =?> - divWith - nullAttr - (mappend - (plain $ spanWith ("", ["label"], []) - (spcSep [ "Functor", "laws", "in", "Haskell" ])) - (codeBlockWith ("functor-laws", ["haskell"], []) - (unlines [ "fmap id = id" - , "fmap (p . q) = (fmap p) . (fmap q)" - ]))) - - , "Convert blank lines in blocks to single newlines" =: - T.unlines [ "#+begin_html" - , "" - , "<span>boring</span>" - , "" - , "#+end_html" - ] =?> - rawBlock "html" "\n<span>boring</span>\n\n" - - , "Accept `ATTR_HTML` attributes for generic block" =: - T.unlines [ "#+ATTR_HTML: :title hello, world :id test :class fun code" - , "#+BEGIN_TEST" - , "nonsense" - , "#+END_TEST" - ] =?> - let attr = ("test", ["fun", "code", "TEST"], [("title", "hello, world")]) - in divWith attr (para "nonsense") - - , "Non-letter chars in source block parameters" =: - T.unlines [ "#+BEGIN_SRC C :tangle xxxx.c :city Zürich" - , "code body" - , "#+END_SRC" - ] =?> - let params = [ ("org-language", "C") - , ("tangle", "xxxx.c") - , ("city", "Zürich") - ] - in codeBlockWith ( "", ["c"], params) "code body\n" - ] - - , testGroup "Smart punctuation" - [ test orgSmart "quote before ellipses" - ("'...hi'" - =?> para (singleQuoted "…hi")) - - , test orgSmart "apostrophe before emph" - ("D'oh! A l'/aide/!" - =?> para ("D’oh! A l’" <> emph "aide" <> "!")) - - , test orgSmart "apostrophe in French" - ("À l'arrivée de la guerre, le thème de l'«impossibilité du socialisme»" - =?> para "À l’arrivée de la guerre, le thème de l’«impossibilité du socialisme»") - - , test orgSmart "Quotes cannot occur at the end of emphasized text" - ("/say \"yes\"/" =?> - para ("/say" <> space <> doubleQuoted "yes" <> "/")) - - , test orgSmart "Dashes are allowed at the borders of emphasis'" - ("/foo---/" =?> - para (emph "foo—")) - - , test orgSmart "Single quotes can be followed by emphasized text" - ("Singles on the '/meat market/'" =?> - para ("Singles on the " <> (singleQuoted $ emph "meat market"))) - - , test orgSmart "Double quotes can be followed by emphasized text" - ("Double income, no kids: \"/DINK/\"" =?> - para ("Double income, no kids: " <> (doubleQuoted $ emph "DINK"))) - ] + [ testGroup "Inlines" Inline.tests + , testGroup "Basic Blocks" Block.tests + , testGroup "Meta Information" Meta.tests + , testGroup "Directives" Directive.tests ] diff --git a/test/Tests/Readers/Org/Block.hs b/test/Tests/Readers/Org/Block.hs new file mode 100644 index 000000000..15dc63554 --- /dev/null +++ b/test/Tests/Readers/Org/Block.hs @@ -0,0 +1,192 @@ +{-# LANGUAGE OverloadedStrings #-} +module Tests.Readers.Org.Block (tests) where + +import Test.Tasty (TestTree, testGroup) +import Tests.Helpers ((=?>)) +import Tests.Readers.Org.Shared ((=:), spcSep) +import Text.Pandoc.Builder +import qualified Data.Text as T +import qualified Tests.Readers.Org.Block.CodeBlock as CodeBlock +import qualified Tests.Readers.Org.Block.Figure as Figure +import qualified Tests.Readers.Org.Block.Header as Header +import qualified Tests.Readers.Org.Block.List as List +import qualified Tests.Readers.Org.Block.Table as Table + +tests :: [TestTree] +tests = + [ "Paragraph" =: + "Paragraph\n" =?> + para "Paragraph" + + , "Paragraph starting with an asterisk" =: + "*five" =?> + para "*five" + + , "Paragraph containing asterisk at beginning of line" =: + T.unlines [ "lucky" + , "*star" + ] =?> + para ("lucky" <> softbreak <> "*star") + + , "Example block" =: + T.unlines [ ": echo hello" + , ": echo dear tester" + ] =?> + codeBlockWith ("", ["example"], []) "echo hello\necho dear tester\n" + + , "Example block surrounded by text" =: + T.unlines [ "Greetings" + , ": echo hello" + , ": echo dear tester" + , "Bye" + ] =?> + mconcat [ para "Greetings" + , codeBlockWith ("", ["example"], []) + "echo hello\necho dear tester\n" + , para "Bye" + ] + + , "Horizontal Rule" =: + T.unlines [ "before" + , "-----" + , "after" + ] =?> + mconcat [ para "before" + , horizontalRule + , para "after" + ] + + , "Not a Horizontal Rule" =: + "----- em and en dash" =?> + para "\8212\8211 em and en dash" + + , "Comment Block" =: + T.unlines [ "#+BEGIN_COMMENT" + , "stuff" + , "bla" + , "#+END_COMMENT"] =?> + (mempty::Blocks) + + , testGroup "Blocks and fragments" + [ "HTML block" =: + T.unlines [ "#+BEGIN_HTML" + , "<aside>HTML5 is pretty nice.</aside>" + , "#+END_HTML" + ] =?> + rawBlock "html" "<aside>HTML5 is pretty nice.</aside>\n" + + , "Quote block" =: + T.unlines [ "#+BEGIN_QUOTE" + , "/Niemand/ hat die Absicht, eine Mauer zu errichten!" + , "#+END_QUOTE" + ] =?> + blockQuote (para (spcSep [ emph "Niemand", "hat", "die", "Absicht," + , "eine", "Mauer", "zu", "errichten!" + ])) + + , "Verse block" =: + T.unlines [ "The first lines of Goethe's /Faust/:" + , "#+begin_verse" + , "Habe nun, ach! Philosophie," + , "Juristerei und Medizin," + , "Und leider auch Theologie!" + , "Durchaus studiert, mit heißem Bemühn." + , "#+end_verse" + ] =?> + mconcat + [ para $ spcSep [ "The", "first", "lines", "of" + , "Goethe's", emph "Faust" <> ":"] + , lineBlock + [ "Habe nun, ach! Philosophie," + , "Juristerei und Medizin," + , "Und leider auch Theologie!" + , "Durchaus studiert, mit heißem Bemühn." + ] + ] + + , "Verse block with blank lines" =: + T.unlines [ "#+BEGIN_VERSE" + , "foo" + , "" + , "bar" + , "#+END_VERSE" + ] =?> + lineBlock [ "foo", mempty, "bar" ] + + , "Verse block with varying indentation" =: + T.unlines [ "#+BEGIN_VERSE" + , " hello darkness" + , "my old friend" + , "#+END_VERSE" + ] =?> + lineBlock [ "\160\160hello darkness", "my old friend" ] + + , "Raw block LaTeX" =: + T.unlines [ "#+BEGIN_LaTeX" + , "The category $\\cat{Set}$ is adhesive." + , "#+END_LaTeX" + ] =?> + rawBlock "latex" "The category $\\cat{Set}$ is adhesive.\n" + + , "Raw LaTeX line" =: + "#+LATEX: \\let\\foo\\bar" =?> + rawBlock "latex" "\\let\\foo\\bar" + + , "Raw Beamer line" =: + "#+beamer: \\pause" =?> + rawBlock "beamer" "\\pause" + + , "Raw HTML line" =: + "#+HTML: <aside>not important</aside>" =?> + rawBlock "html" "<aside>not important</aside>" + + , "Export block HTML" =: + T.unlines [ "#+BEGIN_export html" + , "<samp>Hello, World!</samp>" + , "#+END_export" + ] =?> + rawBlock "html" "<samp>Hello, World!</samp>\n" + + , "LaTeX fragment" =: + T.unlines [ "\\begin{equation}" + , "X_i = \\begin{cases}" + , " G_{\\alpha(i)} & \\text{if }\\alpha(i-1) = \\alpha(i)\\\\" + , " C_{\\alpha(i)} & \\text{otherwise}" + , " \\end{cases}" + , "\\end{equation}" + ] =?> + rawBlock "latex" + (unlines [ "\\begin{equation}" + , "X_i = \\begin{cases}" + , " G_{\\alpha(i)} & \\text{if }\\alpha(i-1) =" <> + " \\alpha(i)\\\\" + , " C_{\\alpha(i)} & \\text{otherwise}" + , " \\end{cases}" + , "\\end{equation}" + ]) + + , "Convert blank lines in blocks to single newlines" =: + T.unlines [ "#+begin_html" + , "" + , "<span>boring</span>" + , "" + , "#+end_html" + ] =?> + rawBlock "html" "\n<span>boring</span>\n\n" + + , "Accept `ATTR_HTML` attributes for generic block" =: + T.unlines [ "#+ATTR_HTML: :title hello, world :id test :class fun code" + , "#+BEGIN_TEST" + , "nonsense" + , "#+END_TEST" + ] =?> + let attr = ("test", ["fun", "code", "TEST"], [("title", "hello, world")]) + in divWith attr (para "nonsense") + ] + + , testGroup "Headers" Header.tests + , testGroup "Figures" Figure.tests + , testGroup "Lists" List.tests + , testGroup "CodeBlocks" CodeBlock.tests + , testGroup "Tables" Table.tests + ] diff --git a/test/Tests/Readers/Org/Block/CodeBlock.hs b/test/Tests/Readers/Org/Block/CodeBlock.hs new file mode 100644 index 000000000..8fa822089 --- /dev/null +++ b/test/Tests/Readers/Org/Block/CodeBlock.hs @@ -0,0 +1,194 @@ +{-# LANGUAGE OverloadedStrings #-} +module Tests.Readers.Org.Block.CodeBlock (tests) where + +import Test.Tasty (TestTree) +import Tests.Helpers ((=?>)) +import Tests.Readers.Org.Shared ((=:), spcSep) +import Text.Pandoc.Builder +import qualified Data.Text as T + +tests :: [TestTree] +tests = + [ "Source block" =: + T.unlines [ " #+BEGIN_SRC haskell" + , " main = putStrLn greeting" + , " where greeting = \"moin\"" + , " #+END_SRC" ] =?> + let attr' = ("", ["haskell"], []) + code' = "main = putStrLn greeting\n" <> + " where greeting = \"moin\"\n" + in codeBlockWith attr' code' + + , "Source block with indented code" =: + T.unlines [ " #+BEGIN_SRC haskell" + , " main = putStrLn greeting" + , " where greeting = \"moin\"" + , " #+END_SRC" ] =?> + let attr' = ("", ["haskell"], []) + code' = "main = putStrLn greeting\n" <> + " where greeting = \"moin\"\n" + in codeBlockWith attr' code' + + , "Source block with tab-indented code" =: + T.unlines [ "\t#+BEGIN_SRC haskell" + , "\tmain = putStrLn greeting" + , "\t where greeting = \"moin\"" + , "\t#+END_SRC" ] =?> + let attr' = ("", ["haskell"], []) + code' = "main = putStrLn greeting\n" <> + " where greeting = \"moin\"\n" + in codeBlockWith attr' code' + + , "Empty source block" =: + T.unlines [ " #+BEGIN_SRC haskell" + , " #+END_SRC" ] =?> + let attr' = ("", ["haskell"], []) + code' = "" + in codeBlockWith attr' code' + + , "Source block between paragraphs" =: + T.unlines [ "Low German greeting" + , " #+BEGIN_SRC haskell" + , " main = putStrLn greeting" + , " where greeting = \"Moin!\"" + , " #+END_SRC" ] =?> + let attr' = ("", ["haskell"], []) + code' = "main = putStrLn greeting\n" <> + " where greeting = \"Moin!\"\n" + in mconcat [ para $ spcSep [ "Low", "German", "greeting" ] + , codeBlockWith attr' code' + ] + , "Source block with babel arguments" =: + T.unlines [ "#+BEGIN_SRC emacs-lisp :exports both" + , "(progn (message \"Hello, World!\")" + , " (+ 23 42))" + , "#+END_SRC" ] =?> + let classes = [ "commonlisp" ] -- as kate doesn't know emacs-lisp syntax + params = [ ("org-language", "emacs-lisp") + , ("exports", "both") + ] + code' = unlines [ "(progn (message \"Hello, World!\")" + , " (+ 23 42))" ] + in codeBlockWith ("", classes, params) code' + + , "Source block with results and :exports both" =: + T.unlines [ "#+BEGIN_SRC emacs-lisp :exports both" + , "(progn (message \"Hello, World!\")" + , " (+ 23 42))" + , "#+END_SRC" + , "" + , "#+RESULTS:" + , ": 65"] =?> + let classes = [ "commonlisp" ] + params = [ ("org-language", "emacs-lisp") + , ("exports", "both") + ] + code' = unlines [ "(progn (message \"Hello, World!\")" + , " (+ 23 42))" ] + results' = "65\n" + in codeBlockWith ("", classes, params) code' + <> + codeBlockWith ("", ["example"], []) results' + + , "Source block with results and :exports code" =: + T.unlines [ "#+BEGIN_SRC emacs-lisp :exports code" + , "(progn (message \"Hello, World!\")" + , " (+ 23 42))" + , "#+END_SRC" + , "" + , "#+RESULTS:" + , ": 65" ] =?> + let classes = [ "commonlisp" ] + params = [ ("org-language", "emacs-lisp") + , ("exports", "code") + ] + code' = unlines [ "(progn (message \"Hello, World!\")" + , " (+ 23 42))" ] + in codeBlockWith ("", classes, params) code' + + , "Source block with results and :exports results" =: + T.unlines [ "#+BEGIN_SRC emacs-lisp :exports results" + , "(progn (message \"Hello, World!\")" + , " (+ 23 42))" + , "#+END_SRC" + , "" + , "#+RESULTS:" + , ": 65" ] =?> + let results' = "65\n" + in codeBlockWith ("", ["example"], []) results' + + , "Source block with results and :exports none" =: + T.unlines [ "#+BEGIN_SRC emacs-lisp :exports none" + , "(progn (message \"Hello, World!\")" + , " (+ 23 42))" + , "#+END_SRC" + , "" + , "#+RESULTS:" + , ": 65" ] =?> + (mempty :: Blocks) + + , "Source block with toggling header arguments" =: + T.unlines [ "#+BEGIN_SRC sh :noeval" + , "echo $HOME" + , "#+END_SRC" + ] =?> + let classes = [ "bash" ] + params = [ ("org-language", "sh"), ("noeval", "yes") ] + in codeBlockWith ("", classes, params) "echo $HOME\n" + + , "Source block with line number switch" =: + T.unlines [ "#+BEGIN_SRC sh -n 10" + , ":() { :|:& };:" + , "#+END_SRC" + ] =?> + let classes = [ "bash", "numberLines" ] + params = [ ("org-language", "sh"), ("startFrom", "10") ] + in codeBlockWith ("", classes, params) ":() { :|:& };:\n" + + , "Source block with multi-word parameter values" =: + T.unlines [ "#+BEGIN_SRC dot :cmdline -Kdot -Tpng " + , "digraph { id [label=\"ID\"] }" + , "#+END_SRC" + ] =?> + let classes = [ "dot" ] + params = [ ("cmdline", "-Kdot -Tpng") ] + in codeBlockWith ("", classes, params) "digraph { id [label=\"ID\"] }\n" + + , "Example block" =: + T.unlines [ "#+begin_example" + , "A chosen representation of" + , "a rule." + , "#+eND_exAMPle" + ] =?> + codeBlockWith ("", ["example"], []) + "A chosen representation of\na rule.\n" + + , "Code block with caption" =: + T.unlines [ "#+CAPTION: Functor laws in Haskell" + , "#+NAME: functor-laws" + , "#+BEGIN_SRC haskell" + , "fmap id = id" + , "fmap (p . q) = (fmap p) . (fmap q)" + , "#+END_SRC" + ] =?> + divWith + nullAttr + (mappend + (plain $ spanWith ("", ["label"], []) + (spcSep [ "Functor", "laws", "in", "Haskell" ])) + (codeBlockWith ("functor-laws", ["haskell"], []) + (unlines [ "fmap id = id" + , "fmap (p . q) = (fmap p) . (fmap q)" + ]))) + + , "Non-letter chars in source block parameters" =: + T.unlines [ "#+BEGIN_SRC C :tangle xxxx.c :city Zürich" + , "code body" + , "#+END_SRC" + ] =?> + let params = [ ("org-language", "C") + , ("tangle", "xxxx.c") + , ("city", "Zürich") + ] + in codeBlockWith ( "", ["c"], params) "code body\n" + ] diff --git a/test/Tests/Readers/Org/Block/Figure.hs b/test/Tests/Readers/Org/Block/Figure.hs new file mode 100644 index 000000000..cae6ef179 --- /dev/null +++ b/test/Tests/Readers/Org/Block/Figure.hs @@ -0,0 +1,57 @@ +{-# LANGUAGE OverloadedStrings #-} +module Tests.Readers.Org.Block.Figure (tests) where + +import Test.Tasty (TestTree) +import Tests.Helpers ((=?>)) +import Tests.Readers.Org.Shared ((=:)) +import Text.Pandoc.Builder (image, imageWith, para) +import qualified Data.Text as T + +tests :: [TestTree] +tests = + [ "Figure" =: + T.unlines [ "#+caption: A very courageous man." + , "#+name: goodguy" + , "[[file:edward.jpg]]" + ] =?> + para (image "edward.jpg" "fig:goodguy" "A very courageous man.") + + , "Figure with no name" =: + T.unlines [ "#+caption: I've been through the desert on this" + , "[[file:horse.png]]" + ] =?> + para (image "horse.png" "fig:" "I've been through the desert on this") + + , "Figure with `fig:` prefix in name" =: + T.unlines [ "#+caption: Used as a metapher in evolutionary biology." + , "#+name: fig:redqueen" + , "[[./the-red-queen.jpg]]" + ] =?> + para (image "./the-red-queen.jpg" "fig:redqueen" + "Used as a metapher in evolutionary biology.") + + , "Figure with HTML attributes" =: + T.unlines [ "#+CAPTION: mah brain just explodid" + , "#+NAME: lambdacat" + , "#+ATTR_HTML: :style color: blue :role button" + , "[[file:lambdacat.jpg]]" + ] =?> + let kv = [("style", "color: blue"), ("role", "button")] + name = "fig:lambdacat" + caption = "mah brain just explodid" + in para (imageWith (mempty, mempty, kv) "lambdacat.jpg" name caption) + + , "Labelled figure" =: + T.unlines [ "#+CAPTION: My figure" + , "#+LABEL: fig:myfig" + , "[[file:blub.png]]" + ] =?> + let attr = ("fig:myfig", mempty, mempty) + in para (imageWith attr "blub.png" "fig:" "My figure") + + , "Figure with empty caption" =: + T.unlines [ "#+CAPTION:" + , "[[file:guess.jpg]]" + ] =?> + para (image "guess.jpg" "fig:" "") + ] diff --git a/test/Tests/Readers/Org/Block/Header.hs b/test/Tests/Readers/Org/Block/Header.hs new file mode 100644 index 000000000..d895c86e2 --- /dev/null +++ b/test/Tests/Readers/Org/Block/Header.hs @@ -0,0 +1,182 @@ +{-# LANGUAGE OverloadedStrings #-} +module Tests.Readers.Org.Block.Header (tests) where + +import Test.Tasty (TestTree, testGroup) +import Tests.Helpers ((=?>)) +import Tests.Readers.Org.Shared ((=:), spcSep, tagSpan) +import Text.Pandoc.Builder +import qualified Data.Text as T + +tests :: [TestTree] +tests = + [ "First Level Header" =: + "* Headline\n" =?> + headerWith ("headline", [], []) 1 "Headline" + + , "Third Level Header" =: + "*** Third Level Headline\n" =?> + headerWith ("third-level-headline", [], []) + 3 + ("Third" <> space <> "Level" <> space <> "Headline") + + , "Compact Headers with Paragraph" =: + T.unlines [ "* First Level" + , "** Second Level" + , " Text" + ] =?> + mconcat [ headerWith ("first-level", [], []) + 1 + ("First" <> space <> "Level") + , headerWith ("second-level", [], []) + 2 + ("Second" <> space <> "Level") + , para "Text" + ] + + , "Separated Headers with Paragraph" =: + T.unlines [ "* First Level" + , "" + , "** Second Level" + , "" + , " Text" + ] =?> + mconcat [ headerWith ("first-level", [], []) + 1 + ("First" <> space <> "Level") + , headerWith ("second-level", [], []) + 2 + ("Second" <> space <> "Level") + , para "Text" + ] + + , "Headers not preceded by a blank line" =: + T.unlines [ "** eat dinner" + , "Spaghetti and meatballs tonight." + , "** walk dog" + ] =?> + mconcat [ headerWith ("eat-dinner", [], []) + 2 + ("eat" <> space <> "dinner") + , para $ spcSep [ "Spaghetti", "and", "meatballs", "tonight." ] + , headerWith ("walk-dog", [], []) + 2 + ("walk" <> space <> "dog") + ] + + , testGroup "Todo keywords" + [ "Header with known todo keyword" =: + "* TODO header" =?> + let todoSpan = spanWith ("", ["todo", "TODO"], []) "TODO" + in headerWith ("header", [], []) 1 (todoSpan <> space <> "header") + + , "Header marked as done" =: + "* DONE header" =?> + let todoSpan = spanWith ("", ["done", "DONE"], []) "DONE" + in headerWith ("header", [], []) 1 (todoSpan <> space <> "header") + + , "Header with unknown todo keyword" =: + "* WAITING header" =?> + headerWith ("waiting-header", [], []) 1 "WAITING header" + + , "Custom todo keywords" =: + T.unlines [ "#+TODO: WAITING CANCELLED" + , "* WAITING compile" + , "* CANCELLED lunch" + ] =?> + let todoSpan = spanWith ("", ["todo", "WAITING"], []) "WAITING" + doneSpan = spanWith ("", ["done", "CANCELLED"], []) "CANCELLED" + in headerWith ("compile", [], []) 1 (todoSpan <> space <> "compile") + <> headerWith ("lunch", [], []) 1 (doneSpan <> space <> "lunch") + + , "Custom todo keywords with multiple done-states" =: + T.unlines [ "#+TODO: WAITING | DONE CANCELLED " + , "* WAITING compile" + , "* CANCELLED lunch" + , "* DONE todo-feature" + ] =?> + let waiting = spanWith ("", ["todo", "WAITING"], []) "WAITING" + cancelled = spanWith ("", ["done", "CANCELLED"], []) "CANCELLED" + done = spanWith ("", ["done", "DONE"], []) "DONE" + in headerWith ("compile", [], []) 1 (waiting <> space <> "compile") + <> headerWith ("lunch", [], []) 1 (cancelled <> space <> "lunch") + <> headerWith ("todo-feature", [], []) 1 (done <> space <> "todo-feature") + ] + + , "Tagged headers" =: + T.unlines [ "* Personal :PERSONAL:" + , "** Call Mom :@PHONE:" + , "** Call John :@PHONE:JOHN: " + ] =?> + mconcat [ headerWith ("personal", [], []) + 1 + ("Personal " <> tagSpan "PERSONAL") + , headerWith ("call-mom", [], []) + 2 + ("Call Mom " <> tagSpan "@PHONE") + , headerWith ("call-john", [], []) + 2 + ("Call John " <> tagSpan "@PHONE" <> "\160" <> tagSpan "JOHN") + ] + + , "Untagged header containing colons" =: + "* This: is not: tagged" =?> + headerWith ("this-is-not-tagged", [], []) 1 "This: is not: tagged" + + , "Header starting with strokeout text" =: + T.unlines [ "foo" + , "" + , "* +thing+ other thing" + ] =?> + mconcat [ para "foo" + , headerWith ("thing-other-thing", [], []) + 1 + ((strikeout "thing") <> " other thing") + ] + + , "Comment Trees" =: + T.unlines [ "* COMMENT A comment tree" + , " Not much going on here" + , "** This will be dropped" + , "* Comment tree above" + ] =?> + headerWith ("comment-tree-above", [], []) 1 "Comment tree above" + + , "Nothing but a COMMENT header" =: + "* COMMENT Test" =?> + (mempty::Blocks) + + , "Tree with :noexport:" =: + T.unlines [ "* Should be ignored :archive:noexport:old:" + , "** Old stuff" + , " This is not going to be exported" + ] =?> + (mempty::Blocks) + + , "Subtree with :noexport:" =: + T.unlines [ "* Exported" + , "** This isn't exported :noexport:" + , "*** This neither" + , "** But this is" + ] =?> + mconcat [ headerWith ("exported", [], []) 1 "Exported" + , headerWith ("but-this-is", [], []) 2 "But this is" + ] + + , "Preferences are treated as header attributes" =: + T.unlines [ "* foo" + , " :PROPERTIES:" + , " :custom_id: fubar" + , " :bar: baz" + , " :END:" + ] =?> + headerWith ("fubar", [], [("bar", "baz")]) 1 "foo" + + + , "Headers marked with a unnumbered property get a class of the same name" =: + T.unlines [ "* Not numbered" + , " :PROPERTIES:" + , " :UNNUMBERED: t" + , " :END:" + ] =?> + headerWith ("not-numbered", ["unnumbered"], []) 1 "Not numbered" + ] diff --git a/test/Tests/Readers/Org/Block/List.hs b/test/Tests/Readers/Org/Block/List.hs new file mode 100644 index 000000000..32bb13294 --- /dev/null +++ b/test/Tests/Readers/Org/Block/List.hs @@ -0,0 +1,244 @@ +{-# LANGUAGE OverloadedStrings #-} +module Tests.Readers.Org.Block.List (tests) where + +import Test.Tasty (TestTree) +import Tests.Helpers ((=?>)) +import Tests.Readers.Org.Shared ((=:), spcSep) +import Text.Pandoc.Builder +import qualified Data.Text as T + +tests :: [TestTree] +tests = + [ "Simple Bullet Lists" =: + ("- Item1\n" <> + "- Item2\n") =?> + bulletList [ plain "Item1" + , plain "Item2" + ] + + , "Indented Bullet Lists" =: + (" - Item1\n" <> + " - Item2\n") =?> + bulletList [ plain "Item1" + , plain "Item2" + ] + + , "Unindented *" =: + ("- Item1\n" <> + "* Item2\n") =?> + bulletList [ plain "Item1" + ] <> + headerWith ("item2", [], []) 1 "Item2" + + , "Multi-line Bullet Lists" =: + ("- *Fat\n" <> + " Tony*\n" <> + "- /Sideshow\n" <> + " Bob/") =?> + bulletList [ plain $ strong ("Fat" <> softbreak <> "Tony") + , plain $ emph ("Sideshow" <> softbreak <> "Bob") + ] + + , "Nested Bullet Lists" =: + ("- Discovery\n" <> + " + One More Time\n" <> + " + Harder, Better, Faster, Stronger\n" <> + "- Homework\n" <> + " + Around the World\n"<> + "- Human After All\n" <> + " + Technologic\n" <> + " + Robot Rock\n") =?> + bulletList [ mconcat + [ plain "Discovery" + , bulletList [ plain ("One" <> space <> + "More" <> space <> + "Time") + , plain ("Harder," <> space <> + "Better," <> space <> + "Faster," <> space <> + "Stronger") + ] + ] + , mconcat + [ plain "Homework" + , bulletList [ plain ("Around" <> space <> + "the" <> space <> + "World") + ] + ] + , mconcat + [ plain ("Human" <> space <> "After" <> space <> "All") + , bulletList [ plain "Technologic" + , plain ("Robot" <> space <> "Rock") + ] + ] + ] + + , "Bullet List with Decreasing Indent" =: + (" - Discovery\n\ + \ - Human After All\n") =?> + mconcat [ bulletList [ plain "Discovery" ] + , bulletList [ plain ("Human" <> space <> "After" <> space <> "All")] + ] + + , "Header follows Bullet List" =: + (" - Discovery\n\ + \ - Human After All\n\ + \* Homework") =?> + mconcat [ bulletList [ plain "Discovery" + , plain ("Human" <> space <> "After" <> space <> "All") + ] + , headerWith ("homework", [], []) 1 "Homework" + ] + + , "Bullet List Unindented with trailing Header" =: + ("- Discovery\n\ + \- Homework\n\ + \* NotValidListItem") =?> + mconcat [ bulletList [ plain "Discovery" + , plain "Homework" + ] + , headerWith ("notvalidlistitem", [], []) 1 "NotValidListItem" + ] + + , "Empty bullet points" =: + T.unlines [ "-" + , "- " + ] =?> + bulletList [ plain "", plain "" ] + + , "Simple Ordered List" =: + ("1. Item1\n" <> + "2. Item2\n") =?> + let listStyle = (1, DefaultStyle, DefaultDelim) + listStructure = [ plain "Item1" + , plain "Item2" + ] + in orderedListWith listStyle listStructure + + , "Simple Ordered List with Parens" =: + ("1) Item1\n" <> + "2) Item2\n") =?> + let listStyle = (1, DefaultStyle, DefaultDelim) + listStructure = [ plain "Item1" + , plain "Item2" + ] + in orderedListWith listStyle listStructure + + , "Indented Ordered List" =: + (" 1. Item1\n" <> + " 2. Item2\n") =?> + let listStyle = (1, DefaultStyle, DefaultDelim) + listStructure = [ plain "Item1" + , plain "Item2" + ] + in orderedListWith listStyle listStructure + + , "Empty ordered list item" =: + T.unlines [ "1." + , "3. " + ] =?> + orderedList [ plain "", plain "" ] + + , "Nested Ordered Lists" =: + ("1. One\n" <> + " 1. One-One\n" <> + " 2. One-Two\n" <> + "2. Two\n" <> + " 1. Two-One\n"<> + " 2. Two-Two\n") =?> + let listStyle = (1, DefaultStyle, DefaultDelim) + listStructure = [ mconcat + [ plain "One" + , orderedList [ plain "One-One" + , plain "One-Two" + ] + ] + , mconcat + [ plain "Two" + , orderedList [ plain "Two-One" + , plain "Two-Two" + ] + ] + ] + in orderedListWith listStyle listStructure + + , "Ordered List in Bullet List" =: + ("- Emacs\n" <> + " 1. Org\n") =?> + bulletList [ (plain "Emacs") <> + (orderedList [ plain "Org"]) + ] + + , "Bullet List in Ordered List" =: + ("1. GNU\n" <> + " - Freedom\n") =?> + orderedList [ (plain "GNU") <> bulletList [ (plain "Freedom") ] ] + + , "Definition List" =: + T.unlines [ "- PLL :: phase-locked loop" + , "- TTL ::" + , " transistor-transistor logic" + , "- PSK :: phase-shift keying" + , "" + , " a digital modulation scheme" + ] =?> + definitionList [ ("PLL", [ plain $ "phase-locked" <> space <> "loop" ]) + , ("TTL", [ plain $ "transistor-transistor" <> space <> + "logic" ]) + , ("PSK", [ mconcat + [ para $ "phase-shift" <> space <> "keying" + , para $ spcSep [ "a", "digital" + , "modulation", "scheme" ] + ] + ]) + ] + , "Definition list with multi-word term" =: + " - Elijah Wood :: He plays Frodo" =?> + definitionList [ ("Elijah" <> space <> "Wood", [plain $ "He" <> space <> "plays" <> space <> "Frodo"])] + , "Compact definition list" =: + T.unlines [ "- ATP :: adenosine 5' triphosphate" + , "- DNA :: deoxyribonucleic acid" + , "- PCR :: polymerase chain reaction" + , "" + ] =?> + definitionList + [ ("ATP", [ plain $ spcSep [ "adenosine", "5'", "triphosphate" ] ]) + , ("DNA", [ plain $ spcSep [ "deoxyribonucleic", "acid" ] ]) + , ("PCR", [ plain $ spcSep [ "polymerase", "chain", "reaction" ] ]) + ] + + , "Definition List With Trailing Header" =: + "- definition :: list\n\ + \- cool :: defs\n\ + \* header" =?> + mconcat [ definitionList [ ("definition", [plain "list"]) + , ("cool", [plain "defs"]) + ] + , headerWith ("header", [], []) 1 "header" + ] + + , "Definition lists double-colon markers must be surrounded by whitespace" =: + "- std::cout" =?> + bulletList [ plain "std::cout" ] + + , "Loose bullet list" =: + T.unlines [ "- apple" + , "" + , "- orange" + , "" + , "- peach" + ] =?> + bulletList [ para "apple" + , para "orange" + , para "peach" + ] + + , "Recognize preceding paragraphs in non-list contexts" =: + T.unlines [ "CLOSED: [2015-10-19 Mon 15:03]" + , "- Note taken on [2015-10-19 Mon 13:24]" + ] =?> + mconcat [ para "CLOSED: [2015-10-19 Mon 15:03]" + , bulletList [ plain "Note taken on [2015-10-19 Mon 13:24]" ] + ] + ] diff --git a/test/Tests/Readers/Org/Block/Table.hs b/test/Tests/Readers/Org/Block/Table.hs new file mode 100644 index 000000000..db6e756f8 --- /dev/null +++ b/test/Tests/Readers/Org/Block/Table.hs @@ -0,0 +1,150 @@ +{-# LANGUAGE OverloadedStrings #-} +module Tests.Readers.Org.Block.Table (tests) where + +import Test.Tasty (TestTree) +import Tests.Helpers ((=?>)) +import Tests.Readers.Org.Shared ((=:), spcSep) +import Text.Pandoc.Builder +import qualified Data.Text as T + +simpleTable' :: Int + -> [Blocks] + -> [[Blocks]] + -> Blocks +simpleTable' n = table "" (replicate n (AlignDefault, 0.0)) + +tests :: [TestTree] +tests = + [ "Single cell table" =: + "|Test|" =?> + simpleTable' 1 mempty [[plain "Test"]] + + , "Multi cell table" =: + "| One | Two |" =?> + simpleTable' 2 mempty [ [ plain "One", plain "Two" ] ] + + , "Multi line table" =: + T.unlines [ "| One |" + , "| Two |" + , "| Three |" + ] =?> + simpleTable' 1 mempty + [ [ plain "One" ] + , [ plain "Two" ] + , [ plain "Three" ] + ] + + , "Empty table" =: + "||" =?> + simpleTable' 1 mempty [[mempty]] + + , "Glider Table" =: + T.unlines [ "| 1 | 0 | 0 |" + , "| 0 | 1 | 1 |" + , "| 1 | 1 | 0 |" + ] =?> + simpleTable' 3 mempty + [ [ plain "1", plain "0", plain "0" ] + , [ plain "0", plain "1", plain "1" ] + , [ plain "1", plain "1", plain "0" ] + ] + + , "Table between Paragraphs" =: + T.unlines [ "Before" + , "| One | Two |" + , "After" + ] =?> + mconcat [ para "Before" + , simpleTable' 2 mempty [ [ plain "One", plain "Two" ] ] + , para "After" + ] + + , "Table with Header" =: + T.unlines [ "| Species | Status |" + , "|--------------+--------------|" + , "| cervisiae | domesticated |" + , "| paradoxus | wild |" + ] =?> + simpleTable [ plain "Species", plain "Status" ] + [ [ plain "cervisiae", plain "domesticated" ] + , [ plain "paradoxus", plain "wild" ] + ] + + , "Table with final hline" =: + T.unlines [ "| cervisiae | domesticated |" + , "| paradoxus | wild |" + , "|--------------+--------------|" + ] =?> + simpleTable' 2 mempty + [ [ plain "cervisiae", plain "domesticated" ] + , [ plain "paradoxus", plain "wild" ] + ] + + , "Table in a box" =: + T.unlines [ "|---------|---------|" + , "| static | Haskell |" + , "| dynamic | Lisp |" + , "|---------+---------|" + ] =?> + simpleTable' 2 mempty + [ [ plain "static", plain "Haskell" ] + , [ plain "dynamic", plain "Lisp" ] + ] + + , "Table with empty cells" =: + "|||c|" =?> + simpleTable' 3 mempty [[mempty, mempty, plain "c"]] + + , "Table with empty rows" =: + T.unlines [ "| first |" + , "| |" + , "| third |" + ] =?> + simpleTable' 1 mempty [[plain "first"], [mempty], [plain "third"]] + + , "Table with alignment row" =: + T.unlines [ "| Numbers | Text | More |" + , "| <c> | <r> | |" + , "| 1 | One | foo |" + , "| 2 | Two | bar |" + ] =?> + table "" (zip [AlignCenter, AlignRight, AlignDefault] [0, 0, 0]) + [] + [ [ plain "Numbers", plain "Text", plain "More" ] + , [ plain "1" , plain "One" , plain "foo" ] + , [ plain "2" , plain "Two" , plain "bar" ] + ] + + , "Pipe within text doesn't start a table" =: + "Ceci n'est pas une | pipe " =?> + para (spcSep [ "Ceci", "n'est", "pas", "une", "|", "pipe" ]) + + , "Missing pipe at end of row" =: + "|incomplete-but-valid" =?> + simpleTable' 1 mempty [ [ plain "incomplete-but-valid" ] ] + + , "Table with differing row lengths" =: + T.unlines [ "| Numbers | Text " + , "|-" + , "| <c> | <r> |" + , "| 1 | One | foo |" + , "| 2" + ] =?> + table "" (zip [AlignCenter, AlignRight] [0, 0]) + [ plain "Numbers", plain "Text" ] + [ [ plain "1" , plain "One" , plain "foo" ] + , [ plain "2" ] + ] + + , "Table with caption" =: + T.unlines [ "#+CAPTION: Hitchhiker's Multiplication Table" + , "| x | 6 |" + , "| 9 | 42 |" + ] =?> + table "Hitchhiker's Multiplication Table" + [(AlignDefault, 0), (AlignDefault, 0)] + [] + [ [ plain "x", plain "6" ] + , [ plain "9", plain "42" ] + ] + ] diff --git a/test/Tests/Readers/Org/Directive.hs b/test/Tests/Readers/Org/Directive.hs new file mode 100644 index 000000000..862315ef3 --- /dev/null +++ b/test/Tests/Readers/Org/Directive.hs @@ -0,0 +1,199 @@ +{-# LANGUAGE OverloadedStrings #-} +module Tests.Readers.Org.Directive (tests) where + +import Data.Time (UTCTime (UTCTime), secondsToDiffTime) +import Data.Time.Calendar (Day (ModifiedJulianDay)) +import Test.Tasty (TestTree, testGroup) +import Tests.Helpers ((=?>), ToString, purely, test) +import Tests.Readers.Org.Shared ((=:), tagSpan) +import Text.Pandoc +import Text.Pandoc.Builder +import qualified Data.ByteString as BS +import qualified Data.Text as T + +testWithFiles :: (ToString c) + => [(FilePath, BS.ByteString)] + -> String -- ^ name of test case + -> (T.Text, c) -- ^ (input, expected value) + -> TestTree +testWithFiles fileDefs = test (orgWithFiles fileDefs) + where +orgWithFiles :: [(FilePath, BS.ByteString)] -> T.Text -> Pandoc +orgWithFiles fileDefs input = + let readOrg' = readOrg def{ readerExtensions = getDefaultExtensions "org" } + in flip purely input $ \inp -> do + modifyPureState (\st -> st { stFiles = files fileDefs }) + readOrg' inp + + +files :: [(FilePath, BS.ByteString)] -> FileTree +files fileDefs = + let dummyTime = UTCTime (ModifiedJulianDay 125) (secondsToDiffTime 0) + in foldr (\(fp, bs) -> insertInFileTree fp (FileInfo dummyTime bs)) + mempty fileDefs + +tests :: [TestTree] +tests = + [ testGroup "export options" + [ "disable simple sub/superscript syntax" =: + T.unlines [ "#+OPTIONS: ^:nil" + , "a^b" + ] =?> + para "a^b" + + , "directly select drawers to be exported" =: + T.unlines [ "#+OPTIONS: d:(\"IMPORTANT\")" + , ":IMPORTANT:" + , "23" + , ":END:" + , ":BORING:" + , "very boring" + , ":END:" + ] =?> + divWith (mempty, ["IMPORTANT", "drawer"], mempty) (para "23") + + , "exclude drawers from being exported" =: + T.unlines [ "#+OPTIONS: d:(not \"BORING\")" + , ":IMPORTANT:" + , "5" + , ":END:" + , ":BORING:" + , "very boring" + , ":END:" + ] =?> + divWith (mempty, ["IMPORTANT", "drawer"], mempty) (para "5") + + , "don't include archive trees" =: + T.unlines [ "#+OPTIONS: arch:nil" + , "* old :ARCHIVE:" + ] =?> + (mempty ::Blocks) + + , "include complete archive trees" =: + T.unlines [ "#+OPTIONS: arch:t" + , "* old :ARCHIVE:" + , " boring" + ] =?> + mconcat [ headerWith ("old", [], mempty) 1 + ("old" <> space <> tagSpan "ARCHIVE") + , para "boring" + ] + + , "include archive tree header only" =: + T.unlines [ "#+OPTIONS: arch:headline" + , "* old :ARCHIVE:" + , " boring" + ] =?> + headerWith ("old", [], mempty) 1 ("old" <> space <> tagSpan "ARCHIVE") + + , "limit headline depth" =: + T.unlines [ "#+OPTIONS: H:2" + , "* top-level section" + , "** subsection" + , "*** list item 1" + , "*** list item 2" + ] =?> + mconcat [ headerWith ("top-level-section", [], []) 1 "top-level section" + , headerWith ("subsection", [], []) 2 "subsection" + , orderedList [ para "list item 1", para "list item 2" ] + ] + + , "turn all headlines into lists" =: + T.unlines [ "#+OPTIONS: H:0" + , "first block" + , "* top-level section 1" + , "** subsection" + , "* top-level section 2" + ] =?> + mconcat [ para "first block" + , orderedList + [ (para "top-level section 1" <> + orderedList [ para "subsection" ]) + , para "top-level section 2" ] + ] + + , "preserve linebreaks as hard breaks" =: + T.unlines [ "#+OPTIONS: \\n:t" + , "first" + , "second" + ] =?> + para ("first" <> linebreak <> "second") + + , "disable author export" =: + T.unlines [ "#+OPTIONS: author:nil" + , "#+AUTHOR: ShyGuy" + ] =?> + Pandoc nullMeta mempty + + , "disable creator export" =: + T.unlines [ "#+OPTIONS: creator:nil" + , "#+creator: The Architect" + ] =?> + Pandoc nullMeta mempty + + , "disable email export" =: + T.unlines [ "#+OPTIONS: email:nil" + , "#+email: no-mail-please@example.com" + ] =?> + Pandoc nullMeta mempty + + , "disable inclusion of todo keywords" =: + T.unlines [ "#+OPTIONS: todo:nil" + , "** DONE todo export" + ] =?> + headerWith ("todo-export", [], []) 2 "todo export" + + , "remove tags from headlines" =: + T.unlines [ "#+OPTIONS: tags:nil" + , "* Headline :hello:world:" + ] =?> + headerWith ("headline", [], mempty) 1 "Headline" + ] + + , testGroup "Include" + [ testWithFiles [("./other.org", "content of other file\n")] + "file inclusion" + (T.unlines [ "#+include: \"other.org\"" ] =?> + plain "content of other file") + + , testWithFiles [("./world.org", "World\n\n")] + "Included file belongs to item" + (T.unlines [ "- Hello,\n #+include: \"world.org\"" ] =?> + bulletList [para "Hello," <> para "World"]) + + , testWithFiles [("./level3.org", "*** Level3\n\n")] + "Default include preserves level" + (T.unlines [ "#+include: \"level3.org\"" ] =?> + headerWith ("level3", [], []) 3 "Level3") + + , testWithFiles [("./level3.org", "*** Level3\n\n")] + "Minlevel shifts level" + (T.unlines [ "#+include: \"level3.org\" :minlevel 1" ] =?> + headerWith ("level3", [], []) 1 "Level3") + + , testWithFiles [("./src.hs", "putStrLn outString\n")] + "Include file as source code snippet" + (T.unlines [ "#+include: \"src.hs\" src haskell" ] =?> + codeBlockWith ("", ["haskell"], []) "putStrLn outString\n") + + , testWithFiles [("./export-latex.org", "\\emph{Hello}\n")] + "Include file as export snippet" + (T.unlines [ "#+include: \"export-latex.org\" export latex" ] =?> + rawBlock "latex" "\\emph{Hello}\n") + + , testWithFiles [("./subdir/foo-bar.latex", "foo\n"), + ("./hello.lisp", "(print \"Hello!\")\n") + ] + "include directive is limited to one line" + (T.unlines [ "#+INCLUDE: \"hello.lisp\" src lisp" + , "#+include: \"subdir/foo-bar.latex\" export latex" + , "bar" + ] =?> + mconcat + [ codeBlockWith ("", ["lisp"], []) "(print \"Hello!\")\n" + , rawBlock "latex" "foo\n" + , para "bar" + ] + ) + ] + ] diff --git a/test/Tests/Readers/Org/Inline.hs b/test/Tests/Readers/Org/Inline.hs new file mode 100644 index 000000000..cb50ba630 --- /dev/null +++ b/test/Tests/Readers/Org/Inline.hs @@ -0,0 +1,516 @@ +{-# LANGUAGE OverloadedStrings #-} +module Tests.Readers.Org.Inline (tests) where + +import Data.List (intersperse) +import Test.Tasty (TestTree, testGroup) +import Tests.Helpers ((=?>)) +import Tests.Readers.Org.Shared ((=:), spcSep) +import Text.Pandoc +import Text.Pandoc.Builder +import Text.Pandoc.Shared (underlineSpan) +import qualified Data.Text as T +import qualified Tests.Readers.Org.Inline.Note as Note +import qualified Tests.Readers.Org.Inline.Smart as Smart + +tests :: [TestTree] +tests = + [ "Plain String" =: + "Hello, World" =?> + para (spcSep [ "Hello,", "World" ]) + + , "Emphasis" =: + "/Planet Punk/" =?> + para (emph . spcSep $ ["Planet", "Punk"]) + + , "Strong" =: + "*Cider*" =?> + para (strong "Cider") + + , "Strong Emphasis" =: + "/*strength*/" =?> + para (emph . strong $ "strength") + + , "Emphasized Strong preceded by space" =: + " */super/*" =?> + para (strong . emph $ "super") + + , "Underline" =: + "_underline_" =?> + para (underlineSpan $ "underline") + + , "Strikeout" =: + "+Kill Bill+" =?> + para (strikeout . spcSep $ [ "Kill", "Bill" ]) + + , "Verbatim" =: + "=Robot.rock()=" =?> + para (code "Robot.rock()") + + , "Code" =: + "~word for word~" =?> + para (code "word for word") + + , "Math $..$" =: + "$E=mc^2$" =?> + para (math "E=mc^2") + + , "Math $$..$$" =: + "$$E=mc^2$$" =?> + para (displayMath "E=mc^2") + + , "Math \\[..\\]" =: + "\\[E=ℎν\\]" =?> + para (displayMath "E=ℎν") + + , "Math \\(..\\)" =: + "\\(σ_x σ_p ≥ \\frac{ℏ}{2}\\)" =?> + para (math "σ_x σ_p ≥ \\frac{ℏ}{2}") + + , "Symbol" =: + "A * symbol" =?> + para (str "A" <> space <> str "*" <> space <> "symbol") + + , "Superscript simple expression" =: + "2^-λ" =?> + para (str "2" <> superscript "-λ") + + , "Superscript multi char" =: + "2^{n-1}" =?> + para (str "2" <> superscript "n-1") + + , "Subscript simple expression" =: + "a_n" =?> + para (str "a" <> subscript "n") + + , "Subscript multi char" =: + "a_{n+1}" =?> + para (str "a" <> subscript "n+1") + + , "Linebreak" =: + "line \\\\ \nbreak" =?> + para ("line" <> linebreak <> "break") + + , "Inline note" =: + "[fn::Schreib mir eine E-Mail]" =?> + para (note $ para "Schreib mir eine E-Mail") + + , "Markup-chars not occuring on word break are symbols" =: + T.unlines [ "this+that+ +so+on" + , "seven*eight* nine*" + , "+not+funny+" + ] =?> + para ("this+that+ +so+on" <> softbreak <> + "seven*eight* nine*" <> softbreak <> + strikeout "not+funny") + + , "No empty markup" =: + "// ** __ <> == ~~ $$" =?> + para (spcSep [ "//", "**", "__", "<>", "==", "~~", "$$" ]) + + , "Adherence to Org's rules for markup borders" =: + "/t/& a/ / ./r/ (*l*) /e/! /b/." =?> + para (spcSep [ emph $ "t/&" <> space <> "a" + , "/" + , "./r/" + , "(" <> strong "l" <> ")" + , emph "e" <> "!" + , emph "b" <> "." + ]) + + , "Quotes are allowed border chars" =: + "/'yep/ *sure\"*" =?> + para (emph "'yep" <> space <> strong "sure\"") + + , "Spaces are forbidden border chars" =: + "/nada /" =?> + para "/nada /" + + , "Markup should work properly after a blank line" =: + T.unlines ["foo", "", "/bar/"] =?> + (para $ text "foo") <> (para $ emph $ text "bar") + + , "Inline math must stay within three lines" =: + T.unlines [ "$a", "b", "c$", "$d", "e", "f", "g$" ] =?> + para ((math "a\nb\nc") <> softbreak <> + "$d" <> softbreak <> "e" <> softbreak <> + "f" <> softbreak <> "g$") + + , "Single-character math" =: + "$a$ $b$! $c$?" =?> + para (spcSep [ math "a" + , "$b$!" + , (math "c") <> "?" + ]) + + , "Markup may not span more than two lines" =: + "/this *is +totally\nnice+ not*\nemph/" =?> + para ("/this" <> space <> + strong ("is" <> space <> + strikeout ("totally" <> + softbreak <> "nice") <> + space <> "not") <> + softbreak <> "emph/") + + , "Sub- and superscript expressions" =: + T.unlines [ "a_(a(b)(c)d)" + , "e^(f(g)h)" + , "i_(jk)l)" + , "m^()n" + , "o_{p{q{}r}}" + , "s^{t{u}v}" + , "w_{xy}z}" + , "1^{}2" + , "3_{{}}" + , "4^(a(*b(c*)d))" + ] =?> + para (mconcat $ intersperse softbreak + [ "a" <> subscript "(a(b)(c)d)" + , "e" <> superscript "(f(g)h)" + , "i" <> (subscript "(jk)") <> "l)" + , "m" <> (superscript "()") <> "n" + , "o" <> subscript "p{q{}r}" + , "s" <> superscript "t{u}v" + , "w" <> (subscript "xy") <> "z}" + , "1" <> (superscript "") <> "2" + , "3" <> subscript "{}" + , "4" <> superscript ("(a(" <> strong "b(c" <> ")d))") + ]) + , "Verbatim text can contain equal signes (=)" =: + "=is_subst = True=" =?> + para (code "is_subst = True") + + , testGroup "Images" + [ "Image" =: + "[[./sunset.jpg]]" =?> + (para $ image "./sunset.jpg" "" "") + + , "Image with explicit file: prefix" =: + "[[file:sunrise.jpg]]" =?> + (para $ image "sunrise.jpg" "" "") + + , "Multiple images within a paragraph" =: + T.unlines [ "[[file:sunrise.jpg]]" + , "[[file:sunset.jpg]]" + ] =?> + (para $ (image "sunrise.jpg" "" "") + <> softbreak + <> (image "sunset.jpg" "" "")) + + , "Image with html attributes" =: + T.unlines [ "#+ATTR_HTML: :width 50%" + , "[[file:guinea-pig.gif]]" + ] =?> + (para $ imageWith ("", [], [("width", "50%")]) "guinea-pig.gif" "" "") + ] + + , "Explicit link" =: + "[[http://zeitlens.com/][pseudo-random /nonsense/]]" =?> + (para $ link "http://zeitlens.com/" "" + ("pseudo-random" <> space <> emph "nonsense")) + + , "Self-link" =: + "[[http://zeitlens.com/]]" =?> + (para $ link "http://zeitlens.com/" "" "http://zeitlens.com/") + + , "Absolute file link" =: + "[[/url][hi]]" =?> + (para $ link "file:///url" "" "hi") + + , "Link to file in parent directory" =: + "[[../file.txt][moin]]" =?> + (para $ link "../file.txt" "" "moin") + + , "Empty link (for gitit interop)" =: + "[[][New Link]]" =?> + (para $ link "" "" "New Link") + + , "Image link" =: + "[[sunset.png][file:dusk.svg]]" =?> + (para $ link "sunset.png" "" (image "dusk.svg" "" "")) + + , "Image link with non-image target" =: + "[[http://example.com][./logo.png]]" =?> + (para $ link "http://example.com" "" (image "./logo.png" "" "")) + + , "Plain link" =: + "Posts on http://zeitlens.com/ can be funny at times." =?> + (para $ spcSep [ "Posts", "on" + , link "http://zeitlens.com/" "" "http://zeitlens.com/" + , "can", "be", "funny", "at", "times." + ]) + + , "Angle link" =: + "Look at <http://moltkeplatz.de> for fnords." =?> + (para $ spcSep [ "Look", "at" + , link "http://moltkeplatz.de" "" "http://moltkeplatz.de" + , "for", "fnords." + ]) + + , "Absolute file link" =: + "[[file:///etc/passwd][passwd]]" =?> + (para $ link "file:///etc/passwd" "" "passwd") + + , "File link" =: + "[[file:target][title]]" =?> + (para $ link "target" "" "title") + + , "Anchor" =: + "<<anchor>> Link here later." =?> + (para $ spanWith ("anchor", [], []) mempty <> + "Link" <> space <> "here" <> space <> "later.") + + , "Inline code block" =: + "src_emacs-lisp{(message \"Hello\")}" =?> + (para $ codeWith ( "" + , [ "commonlisp" ] + , [ ("org-language", "emacs-lisp") ]) + "(message \"Hello\")") + + , "Inline code block with arguments" =: + "src_sh[:export both :results output]{echo 'Hello, World'}" =?> + (para $ codeWith ( "" + , [ "bash" ] + , [ ("org-language", "sh") + , ("export", "both") + , ("results", "output") + ] + ) + "echo 'Hello, World'") + + , "Inline code block with toggle" =: + "src_sh[:toggle]{echo $HOME}" =?> + (para $ codeWith ( "" + , [ "bash" ] + , [ ("org-language", "sh") + , ("toggle", "yes") + ] + ) + "echo $HOME") + + , "Citation" =: + "[@nonexistent]" =?> + let citation = Citation + { citationId = "nonexistent" + , citationPrefix = [] + , citationSuffix = [] + , citationMode = NormalCitation + , citationNoteNum = 0 + , citationHash = 0} + in (para $ cite [citation] "[@nonexistent]") + + , "Citation containing text" =: + "[see @item1 p. 34-35]" =?> + let citation = Citation + { citationId = "item1" + , citationPrefix = [Str "see"] + , citationSuffix = [Space ,Str "p.",Space,Str "34-35"] + , citationMode = NormalCitation + , citationNoteNum = 0 + , citationHash = 0} + in (para $ cite [citation] "[see @item1 p. 34-35]") + + , "Org-ref simple citation" =: + "cite:pandoc" =?> + let citation = Citation + { citationId = "pandoc" + , citationPrefix = mempty + , citationSuffix = mempty + , citationMode = AuthorInText + , citationNoteNum = 0 + , citationHash = 0 + } + in (para $ cite [citation] "cite:pandoc") + + , "Org-ref simple citation with underscores" =: + "cite:pandoc_org_ref" =?> + let citation = Citation + { citationId = "pandoc_org_ref" + , citationPrefix = mempty + , citationSuffix = mempty + , citationMode = AuthorInText + , citationNoteNum = 0 + , citationHash = 0 + } + in (para $ cite [citation] "cite:pandoc_org_ref") + + , "Org-ref simple citation succeeded by comma" =: + "cite:pandoc," =?> + let citation = Citation + { citationId = "pandoc" + , citationPrefix = mempty + , citationSuffix = mempty + , citationMode = AuthorInText + , citationNoteNum = 0 + , citationHash = 0 + } + in (para $ cite [citation] "cite:pandoc" <> str ",") + + , "Org-ref simple citation succeeded by dot" =: + "cite:pandoc." =?> + let citation = Citation + { citationId = "pandoc" + , citationPrefix = mempty + , citationSuffix = mempty + , citationMode = AuthorInText + , citationNoteNum = 0 + , citationHash = 0 + } + in (para $ cite [citation] "cite:pandoc" <> str ".") + + , "Org-ref simple citation succeeded by colon" =: + "cite:pandoc:" =?> + let citation = Citation + { citationId = "pandoc" + , citationPrefix = mempty + , citationSuffix = mempty + , citationMode = AuthorInText + , citationNoteNum = 0 + , citationHash = 0 + } + in (para $ cite [citation] "cite:pandoc" <> str ":") + + , "Org-ref simple citep citation" =: + "citep:pandoc" =?> + let citation = Citation + { citationId = "pandoc" + , citationPrefix = mempty + , citationSuffix = mempty + , citationMode = NormalCitation + , citationNoteNum = 0 + , citationHash = 0 + } + in (para $ cite [citation] "citep:pandoc") + + , "Org-ref extended citation" =: + "[[citep:Dominik201408][See page 20::, for example]]" =?> + let citation = Citation + { citationId = "Dominik201408" + , citationPrefix = toList "See page 20" + , citationSuffix = toList ", for example" + , citationMode = NormalCitation + , citationNoteNum = 0 + , citationHash = 0 + } + in (para $ cite [citation] "[[citep:Dominik201408][See page 20::, for example]]") + + , testGroup "Berkeley-style citations" $ + let pandocCite = Citation + { citationId = "Pandoc" + , citationPrefix = mempty + , citationSuffix = mempty + , citationMode = NormalCitation + , citationNoteNum = 0 + , citationHash = 0 + } + pandocInText = pandocCite { citationMode = AuthorInText } + dominikCite = Citation + { citationId = "Dominik201408" + , citationPrefix = mempty + , citationSuffix = mempty + , citationMode = NormalCitation + , citationNoteNum = 0 + , citationHash = 0 + } + dominikInText = dominikCite { citationMode = AuthorInText } + in [ + "Berkeley-style in-text citation" =: + "See @Dominik201408." =?> + (para $ "See " + <> cite [dominikInText] "@Dominik201408" + <> ".") + + , "Berkeley-style parenthetical citation list" =: + "[(cite): see; @Dominik201408;also @Pandoc; and others]" =?> + let pandocCite' = pandocCite { + citationPrefix = toList "also" + , citationSuffix = toList "and others" + } + dominikCite' = dominikCite { + citationPrefix = toList "see" + } + in (para $ cite [dominikCite', pandocCite'] "") + + , "Berkeley-style plain citation list" =: + "[cite: See; @Dominik201408; and @Pandoc; and others]" =?> + let pandocCite' = pandocInText { + citationPrefix = toList "and" + } + in (para $ "See " + <> cite [dominikInText] "" + <> "," <> space + <> cite [pandocCite'] "" + <> "," <> space <> "and others") + ] + + , "Inline LaTeX symbol" =: + "\\dots" =?> + para "…" + + , "Inline LaTeX command" =: + "\\textit{Emphasised}" =?> + para (emph "Emphasised") + + , "Inline LaTeX command with spaces" =: + "\\emph{Emphasis mine}" =?> + para (emph "Emphasis mine") + + , "Inline LaTeX math symbol" =: + "\\tau" =?> + para (emph "τ") + + , "Unknown inline LaTeX command" =: + "\\notacommand{foo}" =?> + para (rawInline "latex" "\\notacommand{foo}") + + , "Export snippet" =: + "@@html:<kbd>M-x org-agenda</kbd>@@" =?> + para (rawInline "html" "<kbd>M-x org-agenda</kbd>") + + , "MathML symbol in LaTeX-style" =: + "There is a hackerspace in Lübeck, Germany, called nbsp (unicode symbol: '\\nbsp')." =?> + para ("There is a hackerspace in Lübeck, Germany, called nbsp (unicode symbol: ' ').") + + , "MathML symbol in LaTeX-style, including braces" =: + "\\Aacute{}stor" =?> + para "Ástor" + + , "MathML copy sign" =: + "\\copy" =?> + para "©" + + , "MathML symbols, space separated" =: + "\\ForAll \\Auml" =?> + para "∀ Ä" + + , "LaTeX citation" =: + "\\cite{Coffee}" =?> + let citation = Citation + { citationId = "Coffee" + , citationPrefix = [] + , citationSuffix = [] + , citationMode = NormalCitation + , citationNoteNum = 0 + , citationHash = 0} + in (para . cite [citation] $ rawInline "latex" "\\cite{Coffee}") + + , "Macro" =: + T.unlines [ "#+MACRO: HELLO /Hello, $1/" + , "{{{HELLO(World)}}}" + ] =?> + para (emph "Hello, World") + + , "Macro repeting its argument" =: + T.unlines [ "#+MACRO: HELLO $1$1" + , "{{{HELLO(moin)}}}" + ] =?> + para "moinmoin" + + , "Macro called with too few arguments" =: + T.unlines [ "#+MACRO: HELLO Foo $1 $2 Bar" + , "{{{HELLO()}}}" + ] =?> + para "Foo Bar" + + , testGroup "Footnotes" Note.tests + , testGroup "Smart punctuation" Smart.tests + ] diff --git a/test/Tests/Readers/Org/Inline/Note.hs b/test/Tests/Readers/Org/Inline/Note.hs new file mode 100644 index 000000000..46416d7d8 --- /dev/null +++ b/test/Tests/Readers/Org/Inline/Note.hs @@ -0,0 +1,87 @@ +{-# LANGUAGE OverloadedStrings #-} +module Tests.Readers.Org.Inline.Note (tests) where + +import Test.Tasty (TestTree) +import Tests.Helpers ((=?>)) +import Tests.Readers.Org.Shared ((=:)) +import Text.Pandoc.Builder +import qualified Data.Text as T + +tests :: [TestTree] +tests = + [ "Footnote" =: + T.unlines [ "A footnote[1]" + , "" + , "[1] First paragraph" + , "" + , "second paragraph" + ] =?> + para (mconcat + [ "A", space, "footnote" + , note $ mconcat [ para ("First" <> space <> "paragraph") + , para ("second" <> space <> "paragraph") + ] + ]) + + , "Two footnotes" =: + T.unlines [ "Footnotes[fn:1][fn:2]" + , "" + , "[fn:1] First note." + , "" + , "[fn:2] Second note." + ] =?> + para (mconcat + [ "Footnotes" + , note $ para ("First" <> space <> "note.") + , note $ para ("Second" <> space <> "note.") + ]) + + , "Emphasized text before footnote" =: + T.unlines [ "/text/[fn:1]" + , "" + , "[fn:1] unicorn" + ] =?> + para (mconcat + [ emph "text" + , note . para $ "unicorn" + ]) + + , "Footnote that starts with emphasized text" =: + T.unlines [ "text[fn:1]" + , "" + , "[fn:1] /emphasized/" + ] =?> + para (mconcat + [ "text" + , note . para $ emph "emphasized" + ]) + + , "Footnote followed by header" =: + T.unlines [ "Another note[fn:yay]" + , "" + , "[fn:yay] This is great!" + , "" + , "** Headline" + ] =?> + mconcat + [ para (mconcat + [ "Another", space, "note" + , note $ para ("This" <> space <> "is" <> space <> "great!") + ]) + , headerWith ("headline", [], []) 2 "Headline" + ] + + , "Footnote followed by two blank lines" =: + T.unlines [ "footnote[fn:blanklines]" + , "" + , "[fn:blanklines] followed by blank lines" + , "" + , "" + , "next" + ] =?> + mconcat + [ para ("footnote" <> note (para "followed by blank lines")) + , para "next" + ] + ] + diff --git a/test/Tests/Readers/Org/Inline/Smart.hs b/test/Tests/Readers/Org/Inline/Smart.hs new file mode 100644 index 000000000..7a5e653cf --- /dev/null +++ b/test/Tests/Readers/Org/Inline/Smart.hs @@ -0,0 +1,46 @@ +{-# LANGUAGE OverloadedStrings #-} +module Tests.Readers.Org.Inline.Smart (tests) where + +import Data.Text (Text) +import Test.Tasty (TestTree) +import Tests.Helpers ((=?>), purely, test) +import Text.Pandoc (ReaderOptions (readerExtensions), + Extension (Ext_smart), def, enableExtension, + getDefaultExtensions, readOrg) +import Text.Pandoc.Builder + +orgSmart :: Text -> Pandoc +orgSmart = purely $ + let extensionsSmart = enableExtension Ext_smart (getDefaultExtensions "org") + in readOrg def{ readerExtensions = extensionsSmart } + +tests :: [TestTree] +tests = + [ test orgSmart "quote before ellipses" + ("'...hi'" + =?> para (singleQuoted "…hi")) + + , test orgSmart "apostrophe before emph" + ("D'oh! A l'/aide/!" + =?> para ("D’oh! A l’" <> emph "aide" <> "!")) + + , test orgSmart "apostrophe in French" + ("À l'arrivée de la guerre, le thème de l'«impossibilité du socialisme»" + =?> para "À l’arrivée de la guerre, le thème de l’«impossibilité du socialisme»") + + , test orgSmart "Quotes cannot occur at the end of emphasized text" + ("/say \"yes\"/" =?> + para ("/say" <> space <> doubleQuoted "yes" <> "/")) + + , test orgSmart "Dashes are allowed at the borders of emphasis'" + ("/foo---/" =?> + para (emph "foo—")) + + , test orgSmart "Single quotes can be followed by emphasized text" + ("Singles on the '/meat market/'" =?> + para ("Singles on the " <> (singleQuoted $ emph "meat market"))) + + , test orgSmart "Double quotes can be followed by emphasized text" + ("Double income, no kids: \"/DINK/\"" =?> + para ("Double income, no kids: " <> (doubleQuoted $ emph "DINK"))) + ] diff --git a/test/Tests/Readers/Org/Meta.hs b/test/Tests/Readers/Org/Meta.hs new file mode 100644 index 000000000..3ad6f5d8b --- /dev/null +++ b/test/Tests/Readers/Org/Meta.hs @@ -0,0 +1,173 @@ +{-# LANGUAGE OverloadedStrings #-} +module Tests.Readers.Org.Meta (tests) where + +import Test.Tasty (TestTree) +import Tests.Helpers ((=?>)) +import Tests.Readers.Org.Shared ((=:), spcSep) +import Text.Pandoc +import Text.Pandoc.Builder +import qualified Data.Text as T + +tests :: [TestTree] +tests = + [ "Comment" =: + "# Nothing to see here" =?> + (mempty::Blocks) + + , "Not a comment" =: + "#-tag" =?> + para "#-tag" + + , "Comment surrounded by Text" =: + T.unlines [ "Before" + , "# Comment" + , "After" + ] =?> + mconcat [ para "Before" + , para "After" + ] + + , "Title" =: + "#+TITLE: Hello, World" =?> + let titleInline = toList $ "Hello," <> space <> "World" + meta = setMeta "title" (MetaInlines titleInline) $ nullMeta + in Pandoc meta mempty + + , "Author" =: + "#+author: John /Emacs-Fanboy/ Doe" =?> + let author = toList . spcSep $ [ "John", emph "Emacs-Fanboy", "Doe" ] + meta = setMeta "author" (MetaList [MetaInlines author]) $ nullMeta + in Pandoc meta mempty + + , "Multiple authors" =: + "#+author: James Dewey Watson, Francis Harry Compton Crick " =?> + let watson = MetaInlines $ toList "James Dewey Watson" + crick = MetaInlines $ toList "Francis Harry Compton Crick" + meta = setMeta "author" (MetaList [watson, crick]) $ nullMeta + in Pandoc meta mempty + + , "Date" =: + "#+Date: Feb. *28*, 2014" =?> + let date = toList . spcSep $ [ "Feb.", (strong "28") <> ",", "2014" ] + meta = setMeta "date" (MetaInlines date) $ nullMeta + in Pandoc meta mempty + + , "Description" =: + "#+DESCRIPTION: Explanatory text" =?> + let description = "Explanatory text" + meta = setMeta "description" (MetaString description) $ nullMeta + in Pandoc meta mempty + + , "Properties drawer" =: + T.unlines [ " :PROPERTIES:" + , " :setting: foo" + , " :END:" + ] =?> + (mempty::Blocks) + + , "LaTeX_headers options are translated to header-includes" =: + "#+LaTeX_header: \\usepackage{tikz}" =?> + let latexInlines = rawInline "latex" "\\usepackage{tikz}" + inclList = MetaList [MetaInlines (toList latexInlines)] + meta = setMeta "header-includes" inclList nullMeta + in Pandoc meta mempty + + , "LaTeX_class option is translated to documentclass" =: + "#+LATEX_CLASS: article" =?> + let meta = setMeta "documentclass" (MetaString "article") nullMeta + in Pandoc meta mempty + + , "LaTeX_class_options is translated to classoption" =: + "#+LATEX_CLASS_OPTIONS: [a4paper]" =?> + let meta = setMeta "classoption" (MetaString "a4paper") nullMeta + in Pandoc meta mempty + + , "LaTeX_class_options is translated to classoption" =: + "#+html_head: <meta/>" =?> + let html = rawInline "html" "<meta/>" + inclList = MetaList [MetaInlines (toList html)] + meta = setMeta "header-includes" inclList nullMeta + in Pandoc meta mempty + + , "later meta definitions take precedence" =: + T.unlines [ "#+AUTHOR: this will not be used" + , "#+author: Max" + ] =?> + let author = MetaInlines [Str "Max"] + meta = setMeta "author" (MetaList [author]) $ nullMeta + in Pandoc meta mempty + + , "Logbook drawer" =: + T.unlines [ " :LogBook:" + , " - State \"DONE\" from \"TODO\" [2014-03-03 Mon 11:00]" + , " :END:" + ] =?> + (mempty::Blocks) + + , "Drawer surrounded by text" =: + T.unlines [ "Before" + , ":PROPERTIES:" + , ":END:" + , "After" + ] =?> + para "Before" <> para "After" + + , "Drawer markers must be the only text in the line" =: + T.unlines [ " :LOGBOOK: foo" + , " :END: bar" + ] =?> + para (":LOGBOOK: foo" <> softbreak <> ":END: bar") + + , "Drawers can be arbitrary" =: + T.unlines [ ":FOO:" + , "/bar/" + , ":END:" + ] =?> + divWith (mempty, ["FOO", "drawer"], mempty) (para $ emph "bar") + + , "Anchor reference" =: + T.unlines [ "<<link-here>> Target." + , "" + , "[[link-here][See here!]]" + ] =?> + (para (spanWith ("link-here", [], []) mempty <> "Target.") <> + para (link "#link-here" "" ("See" <> space <> "here!"))) + + , "Search links are read as emph" =: + "[[Wally][Where's Wally?]]" =?> + (para (emph $ "Where's" <> space <> "Wally?")) + + , "Link to nonexistent anchor" =: + T.unlines [ "<<link-here>> Target." + , "" + , "[[link$here][See here!]]" + ] =?> + (para (spanWith ("link-here", [], []) mempty <> "Target.") <> + para (emph ("See" <> space <> "here!"))) + + , "Link abbreviation" =: + T.unlines [ "#+LINK: wp https://en.wikipedia.org/wiki/%s" + , "[[wp:Org_mode][Wikipedia on Org-mode]]" + ] =?> + (para (link "https://en.wikipedia.org/wiki/Org_mode" "" + ("Wikipedia" <> space <> "on" <> space <> "Org-mode"))) + + , "Link abbreviation, defined after first use" =: + T.unlines [ "[[zl:non-sense][Non-sense articles]]" + , "#+LINK: zl http://zeitlens.com/tags/%s.html" + ] =?> + (para (link "http://zeitlens.com/tags/non-sense.html" "" + ("Non-sense" <> space <> "articles"))) + + , "Link abbreviation, URL encoded arguments" =: + T.unlines [ "#+link: expl http://example.com/%h/foo" + , "[[expl:Hello, World!][Moin!]]" + ] =?> + (para (link "http://example.com/Hello%2C%20World%21/foo" "" "Moin!")) + + , "Link abbreviation, append arguments" =: + T.unlines [ "#+link: expl http://example.com/" + , "[[expl:foo][bar]]" + ] =?> + (para (link "http://example.com/foo" "" "bar")) + ] diff --git a/test/Tests/Readers/Org/Shared.hs b/test/Tests/Readers/Org/Shared.hs new file mode 100644 index 000000000..5e8f6dd54 --- /dev/null +++ b/test/Tests/Readers/Org/Shared.hs @@ -0,0 +1,29 @@ +module Tests.Readers.Org.Shared + ( (=:) + , org + , spcSep + , tagSpan + ) where + +import Data.List (intersperse) +import Data.Text (Text) +import Tests.Helpers (ToString, purely, test) +import Test.Tasty (TestTree) +import Text.Pandoc (Pandoc, ReaderOptions (readerExtensions), + def, getDefaultExtensions, readOrg) +import Text.Pandoc.Builder (Inlines, smallcaps, space, spanWith, str) + +org :: Text -> Pandoc +org = purely $ readOrg def{ readerExtensions = getDefaultExtensions "org" } + +infix 4 =: +(=:) :: ToString c + => String -> (Text, c) -> TestTree +(=:) = test org + +spcSep :: [Inlines] -> Inlines +spcSep = mconcat . intersperse space + +-- | Create a span for the given tag. +tagSpan :: String -> Inlines +tagSpan t = spanWith ("", ["tag"], [("tag-name", t)]) . smallcaps $ str t diff --git a/test/Tests/Writers/Powerpoint.hs b/test/Tests/Writers/Powerpoint.hs new file mode 100644 index 000000000..46ebd77bd --- /dev/null +++ b/test/Tests/Writers/Powerpoint.hs @@ -0,0 +1,101 @@ +{-# LANGUAGE OverloadedStrings #-} + +module Tests.Writers.Powerpoint (tests) where + +import Control.Exception (throwIO) +import Text.Pandoc +import Text.Pandoc.Builder +import Test.Tasty +import Test.Tasty.HUnit +import Codec.Archive.Zip +import Data.List (isPrefixOf, isSuffixOf) + +----- Number of Slides ----------- + +numberOfSlides :: WriterOptions -> Pandoc -> IO Int +numberOfSlides opts pd = do + mbs <- runIO $ + do setUserDataDir $ Just "../data" + writePowerpoint opts pd + case mbs of + Left e -> throwIO e + Right bs -> do + let archive = toArchive bs + return $ + length $ + filter (isSuffixOf ".xml") $ + filter (isPrefixOf "ppt/slides/slide") $ + filesInArchive archive + +testNumberOfSlides :: TestName -> Int -> WriterOptions -> Pandoc -> TestTree +testNumberOfSlides name n opts pd = + testCase name $ do + n' <- numberOfSlides opts pd + n' @=? n + +numSlideTests :: TestTree +numSlideTests = testGroup "Number of slides in output" + [ testNumberOfSlides + "simple one-slide deck" 1 + def + (doc $ para "foo") + , testNumberOfSlides + "with metadata (header slide)" 2 + def + (setTitle "My Title" $ doc $ para "foo") + , testNumberOfSlides + "With h1 slide (using default slide-level)" 2 + def + (doc $ header 1 "Header" <> para "foo") + , testNumberOfSlides + "With h2 slide (using default slide-level)" 2 + def + (doc $ header 1 "Header" <> header 2 "subeader" <> para "foo") + , testNumberOfSlides + "With h1 slide (using default slide-level)" 2 + def + (doc $ header 1 "Header" <> para "foo") + , testNumberOfSlides + "With h2 slide (using default slide-level)" 2 + def + (doc $ header 1 "Header" <> header 2 "subeader" <> para "foo") + , testNumberOfSlides + "With image slide, no header" 3 + def + (doc $ + para "first slide" <> + (para $ image "lalune.jpg" "" "") <> + para "foo") + , testNumberOfSlides + "With image slide, header" 3 + def + (doc $ + para "first slide" <> + header 2 "image header" <> + (para $ image "lalune.jpg" "" "") <> + para "foo") + , testNumberOfSlides + "With table, no header" 3 + def + (doc $ + para "first slide" <> + (simpleTable [para "foo" <> para "bar"] [[para "this" <> para "that"]]) <> + para "foo") + , testNumberOfSlides + "With table, header" 3 + def + (doc $ + para "first slide" <> + header 2 "table header" <> + (simpleTable [para "foo" <> para "bar"] [[para "this" <> para "that"]]) <> + para "foo") + , testNumberOfSlides + "hrule" 2 + def + (doc $ + para "first slide" <> horizontalRule <> para "last slide") + ] + + +tests :: [TestTree] +tests = [numSlideTests] diff --git a/test/command/4162.md b/test/command/4162.md new file mode 100644 index 000000000..d88e1ec4e --- /dev/null +++ b/test/command/4162.md @@ -0,0 +1,10 @@ +``` +% pandoc -f html -t native +<div class="line-block">hi<br /><br> + there</div> +^D +[LineBlock + [[Str "hi"] + ,[] + ,[Str "\160there"]]] +``` diff --git a/test/command/4183.md b/test/command/4183.md new file mode 100644 index 000000000..c18320882 --- /dev/null +++ b/test/command/4183.md @@ -0,0 +1,32 @@ +``` +% pandoc -f html -t native +<figure> + <img src="foo" alt="bar"> +</figure> +^D +[Para [Image ("",[],[]) [] ("foo","fig:")]] +``` + +``` +% pandoc -f html -t native +<figure> + <img src="foo" alt="bar"> + <figcaption> + <div> + baz + </div> + </figcaption> +</figure> +^D +[Para [Image ("",[],[]) [Str "baz"] ("foo","fig:")]] +``` + +``` +% pandoc -f html -t native +<figure> + <img src="foo"> + <figcaption><p><em>baz</em></p></figcaption> +</figure> +^D +[Para [Image ("",[],[]) [Emph [Str "baz"]] ("foo","fig:")]] +``` diff --git a/test/command/4208.md b/test/command/4208.md new file mode 100644 index 000000000..9bc519d90 --- /dev/null +++ b/test/command/4208.md @@ -0,0 +1,18 @@ +``` +% pandoc -t latex +What is a _piffle_? Mark the correct answer(s): + +\begin{TAB}(@)[6pt]{|l|c|}{|c|c|c|} +(a) a subnormal woffle & $\Box$ \\ +(b) an infinite-dimensional baffle & $\Box$ \\ +(c) an inverted first-order triffle & $\Box$ \\ +\end{TAB} +^D +What is a \emph{piffle}? Mark the correct answer(s): + +\begin{TAB}(@)[6pt]{|l|c|}{|c|c|c|} +(a) a subnormal woffle & $\Box$ \\ +(b) an infinite-dimensional baffle & $\Box$ \\ +(c) an inverted first-order triffle & $\Box$ \\ +\end{TAB} +``` diff --git a/test/docx/sdt_elements.docx b/test/docx/sdt_elements.docx Binary files differnew file mode 100644 index 000000000..9356a6b40 --- /dev/null +++ b/test/docx/sdt_elements.docx diff --git a/test/docx/sdt_elements.native b/test/docx/sdt_elements.native new file mode 100644 index 000000000..7f7768728 --- /dev/null +++ b/test/docx/sdt_elements.native @@ -0,0 +1,10 @@ +[Table [] [AlignDefault,AlignDefault,AlignDefault] [0.0,0.0,0.0] + [[] + ,[] + ,[]] + [[[Plain [Strong [Str "col1Header"]]] + ,[Plain [Strong [Str "col2Header"]]] + ,[Plain [Strong [Str "col3Header"]]]] + ,[[Plain [Str "col1",Space,Str "content"]] + ,[Plain [Str "Body",Space,Str "copy"]] + ,[Plain [Str "col3",Space,Str "content"]]]]] diff --git a/test/tables.custom b/test/tables.custom new file mode 100644 index 000000000..410b68d3f --- /dev/null +++ b/test/tables.custom @@ -0,0 +1,201 @@ +<p>Simple table with caption:</p> + +<table> +<caption>Demonstration of simple table syntax.</caption> +<tr class="header"> +<th align="right">Right</th> +<th align="left">Left</th> +<th align="center">Center</th> +<th align="left">Default</th> +</tr> +<tr class="odd"> +<td align="right">12</td> +<td align="left">12</td> +<td align="center">12</td> +<td align="left">12</td> +</tr> +<tr class="even"> +<td align="right">123</td> +<td align="left">123</td> +<td align="center">123</td> +<td align="left">123</td> +</tr> +<tr class="odd"> +<td align="right">1</td> +<td align="left">1</td> +<td align="center">1</td> +<td align="left">1</td> +</tr> +</table + +<p>Simple table without caption:</p> + +<table> +<tr class="header"> +<th align="right">Right</th> +<th align="left">Left</th> +<th align="center">Center</th> +<th align="left">Default</th> +</tr> +<tr class="odd"> +<td align="right">12</td> +<td align="left">12</td> +<td align="center">12</td> +<td align="left">12</td> +</tr> +<tr class="even"> +<td align="right">123</td> +<td align="left">123</td> +<td align="center">123</td> +<td align="left">123</td> +</tr> +<tr class="odd"> +<td align="right">1</td> +<td align="left">1</td> +<td align="center">1</td> +<td align="left">1</td> +</tr> +</table + +<p>Simple table indented two spaces:</p> + +<table> +<caption>Demonstration of simple table syntax.</caption> +<tr class="header"> +<th align="right">Right</th> +<th align="left">Left</th> +<th align="center">Center</th> +<th align="left">Default</th> +</tr> +<tr class="odd"> +<td align="right">12</td> +<td align="left">12</td> +<td align="center">12</td> +<td align="left">12</td> +</tr> +<tr class="even"> +<td align="right">123</td> +<td align="left">123</td> +<td align="center">123</td> +<td align="left">123</td> +</tr> +<tr class="odd"> +<td align="right">1</td> +<td align="left">1</td> +<td align="center">1</td> +<td align="left">1</td> +</tr> +</table + +<p>Multiline table with caption:</p> + +<table> +<caption>Here’s the caption. +It may span multiple lines.</caption> +<col width="15%" /> +<col width="14%" /> +<col width="16%" /> +<col width="34%" /> +<tr class="header"> +<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 class="odd"> +<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 class="even"> +<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>Multiline table without caption:</p> + +<table> +<col width="15%" /> +<col width="14%" /> +<col width="16%" /> +<col width="34%" /> +<tr class="header"> +<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 class="odd"> +<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 class="even"> +<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>Table without column headers:</p> + +<table> +<tr class="odd"> +<td align="right">12</td> +<td align="left">12</td> +<td align="center">12</td> +<td align="right">12</td> +</tr> +<tr class="even"> +<td align="right">123</td> +<td align="left">123</td> +<td align="center">123</td> +<td align="right">123</td> +</tr> +<tr class="odd"> +<td align="right">1</td> +<td align="left">1</td> +<td align="center">1</td> +<td align="right">1</td> +</tr> +</table + +<p>Multiline table without column headers:</p> + +<table> +<col width="15%" /> +<col width="14%" /> +<col width="16%" /> +<col width="34%" /> +<tr class="odd"> +<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 class="even"> +<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 + diff --git a/test/tables.latex b/test/tables.latex index 7e3d9613d..759b35dfa 100644 --- a/test/tables.latex +++ b/test/tables.latex @@ -136,6 +136,7 @@ Table without column headers: \begin{longtable}[]{@{}rlcr@{}} \toprule +\endhead 12 & 12 & 12 & 12\tabularnewline 123 & 123 & 123 & 123\tabularnewline 1 & 1 & 1 & 1\tabularnewline @@ -146,6 +147,7 @@ Multiline table without column headers: \begin{longtable}[]{@{}clrl@{}} \toprule +\endhead \begin{minipage}[t]{0.13\columnwidth}\centering First\strut \end{minipage} & \begin{minipage}[t]{0.12\columnwidth}\raggedright diff --git a/test/test-pandoc.hs b/test/test-pandoc.hs index ff852ee0e..4cf1a952d 100644 --- a/test/test-pandoc.hs +++ b/test/test-pandoc.hs @@ -33,6 +33,7 @@ import qualified Tests.Writers.Muse import qualified Tests.Writers.Native import qualified Tests.Writers.Org import qualified Tests.Writers.Plain +import qualified Tests.Writers.Powerpoint import qualified Tests.Writers.RST import qualified Tests.Writers.TEI import Text.Pandoc.Shared (inDirectory) @@ -57,6 +58,7 @@ tests = testGroup "pandoc tests" [ Tests.Command.tests , testGroup "TEI" Tests.Writers.TEI.tests , testGroup "Muse" Tests.Writers.Muse.tests , testGroup "FB2" Tests.Writers.FB2.tests + , testGroup "PowerPoint" Tests.Writers.Powerpoint.tests ] , testGroup "Readers" [ testGroup "LaTeX" Tests.Readers.LaTeX.tests diff --git a/test/writer.custom b/test/writer.custom new file mode 100644 index 000000000..b32d777de --- /dev/null +++ b/test/writer.custom @@ -0,0 +1,783 @@ +<p>This is a set of tests for pandoc. Most of them are adapted from +John Gruber’s markdown test suite.</p> + +<hr/> + +<h1 id="headers">Headers</h1> + +<h2 id="level-2-with-an-embedded-link">Level 2 with an <a href='/url' title=''>embedded link</a></h2> + +<h3 id="level-3-with-emphasis">Level 3 with <em>emphasis</em></h3> + +<h4 id="level-4">Level 4</h4> + +<h5 id="level-5">Level 5</h5> + +<h1 id="level-1">Level 1</h1> + +<h2 id="level-2-with-emphasis">Level 2 with <em>emphasis</em></h2> + +<h3 id="level-3">Level 3</h3> + +<p>with no blank line</p> + +<h2 id="level-2">Level 2</h2> + +<p>with no blank line</p> + +<hr/> + +<h1 id="paragraphs">Paragraphs</h1> + +<p>Here’s a regular paragraph.</p> + +<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> + +<p>Here’s one with a bullet. +* criminey.</p> + +<p>There should be a hard line break<br/>here.</p> + +<hr/> + +<h1 id="block-quotes">Block Quotes</h1> + +<p>E-mail style:</p> + +<blockquote> +<p>This is a block quote. +It is pretty short.</p> +</blockquote> + +<blockquote> +<p>Code in a block quote:</p> + +<pre><code>sub status { + print "working"; +}</code></pre> + +<p>A list:</p> + +<ol> +<li>item one</li> +<li>item two</li> +</ol> + +<p>Nested block quotes:</p> + +<blockquote> +<p>nested</p> +</blockquote> + +<blockquote> +<p>nested</p> +</blockquote> +</blockquote> + +<p>This should not be a block quote: 2 +> 1.</p> + +<p>And a following paragraph.</p> + +<hr/> + +<h1 id="code-blocks">Code Blocks</h1> + +<p>Code:</p> + +<pre><code>---- (should be four hyphens) + +sub status { + print "working"; +} + +this code block is indented by one tab</code></pre> + +<p>And:</p> + +<pre><code> this code block is indented by two tabs + +These should not be escaped: \$ \\ \> \[ \{</code></pre> + +<hr/> + +<h1 id="lists">Lists</h1> + +<h2 id="unordered">Unordered</h2> + +<p>Asterisks tight:</p> + +<ul> +<li>asterisk 1</li> +<li>asterisk 2</li> +<li>asterisk 3</li> +</ul> + +<p>Asterisks loose:</p> + +<ul> +<li><p>asterisk 1</p></li> +<li><p>asterisk 2</p></li> +<li><p>asterisk 3</p></li> +</ul> + +<p>Pluses tight:</p> + +<ul> +<li>Plus 1</li> +<li>Plus 2</li> +<li>Plus 3</li> +</ul> + +<p>Pluses loose:</p> + +<ul> +<li><p>Plus 1</p></li> +<li><p>Plus 2</p></li> +<li><p>Plus 3</p></li> +</ul> + +<p>Minuses tight:</p> + +<ul> +<li>Minus 1</li> +<li>Minus 2</li> +<li>Minus 3</li> +</ul> + +<p>Minuses loose:</p> + +<ul> +<li><p>Minus 1</p></li> +<li><p>Minus 2</p></li> +<li><p>Minus 3</p></li> +</ul> + +<h2 id="ordered">Ordered</h2> + +<p>Tight:</p> + +<ol> +<li>First</li> +<li>Second</li> +<li>Third</li> +</ol> + +<p>and:</p> + +<ol> +<li>One</li> +<li>Two</li> +<li>Three</li> +</ol> + +<p>Loose using tabs:</p> + +<ol> +<li><p>First</p></li> +<li><p>Second</p></li> +<li><p>Third</p></li> +</ol> + +<p>and using spaces:</p> + +<ol> +<li><p>One</p></li> +<li><p>Two</p></li> +<li><p>Three</p></li> +</ol> + +<p>Multiple paragraphs:</p> + +<ol> +<li><p>Item 1, graf one.</p> + +<p>Item 1. graf two. The quick brown fox jumped over the lazy dog’s +back.</p></li> +<li><p>Item 2.</p></li> +<li><p>Item 3.</p></li> +</ol> + +<h2 id="nested">Nested</h2> + +<ul> +<li>Tab + +<ul> +<li>Tab + +<ul> +<li>Tab</li> +</ul></li> +</ul></li> +</ul> + +<p>Here’s another:</p> + +<ol> +<li>First</li> +<li>Second: + +<ul> +<li>Fee</li> +<li>Fie</li> +<li>Foe</li> +</ul></li> +<li>Third</li> +</ol> + +<p>Same thing but with paragraphs:</p> + +<ol> +<li><p>First</p></li> +<li><p>Second:</p> + +<ul> +<li>Fee</li> +<li>Fie</li> +<li>Foe</li> +</ul></li> +<li><p>Third</p></li> +</ol> + +<h2 id="tabs-and-spaces">Tabs and spaces</h2> + +<ul> +<li><p>this is a list item +indented with tabs</p></li> +<li><p>this is a list item +indented with spaces</p> + +<ul> +<li><p>this is an example list item +indented with tabs</p></li> +<li><p>this is an example list item +indented with spaces</p></li> +</ul></li> +</ul> + +<h2 id="fancy-list-markers">Fancy list markers</h2> + +<ol> +<li>begins with 2</li> +<li><p>and now 3</p> + +<p>with a continuation</p> + +<ol> +<li>sublist with roman numerals, +starting with 4</li> +<li>more items + +<ol> +<li>a subsublist</li> +<li>a subsublist</li> +</ol></li> +</ol></li> +</ol> + +<p>Nesting:</p> + +<ol> +<li>Upper Alpha + +<ol> +<li>Upper Roman. + +<ol> +<li>Decimal start with 6 + +<ol> +<li>Lower alpha with paren</li> +</ol></li> +</ol></li> +</ol></li> +</ol> + +<p>Autonumbering:</p> + +<ol> +<li>Autonumber.</li> +<li>More. + +<ol> +<li>Nested.</li> +</ol></li> +</ol> + +<p>Should not be a list item:</p> + +<p>M.A. 2007</p> + +<p>B. Williams</p> + +<hr/> + +<h1 id="definition-lists">Definition Lists</h1> + +<p>Tight using spaces:</p> + +<dl> +<dt>apple</dt> +<dd>red fruit</dd> +<dt>orange</dt> +<dd>orange fruit</dd> +<dt>banana</dt> +<dd>yellow fruit</dd> +</dl> + +<p>Tight using tabs:</p> + +<dl> +<dt>apple</dt> +<dd>red fruit</dd> +<dt>orange</dt> +<dd>orange fruit</dd> +<dt>banana</dt> +<dd>yellow fruit</dd> +</dl> + +<p>Loose:</p> + +<dl> +<dt>apple</dt> +<dd><p>red fruit</p></dd> +<dt>orange</dt> +<dd><p>orange fruit</p></dd> +<dt>banana</dt> +<dd><p>yellow fruit</p></dd> +</dl> + +<p>Multiple blocks with italics:</p> + +<dl> +<dt><em>apple</em></dt> +<dd><p>red fruit</p> + +<p>contains seeds, +crisp, pleasant to taste</p></dd> +<dt><em>orange</em></dt> +<dd><p>orange fruit</p> + +<pre><code>{ orange code block }</code></pre> + +<blockquote> +<p>orange block quote</p> +</blockquote></dd> +</dl> + +<p>Multiple definitions, tight:</p> + +<dl> +<dt>apple</dt> +<dd>red fruit</dd> +<dd>computer</dd> +<dt>orange</dt> +<dd>orange fruit</dd> +<dd>bank</dd> +</dl> + +<p>Multiple definitions, loose:</p> + +<dl> +<dt>apple</dt> +<dd><p>red fruit</p></dd> +<dd><p>computer</p></dd> +<dt>orange</dt> +<dd><p>orange fruit</p></dd> +<dd><p>bank</p></dd> +</dl> + +<p>Blank line after term, indented marker, alternate markers:</p> + +<dl> +<dt>apple</dt> +<dd><p>red fruit</p></dd> +<dd><p>computer</p></dd> +<dt>orange</dt> +<dd><p>orange fruit</p> + +<ol> +<li>sublist</li> +<li>sublist</li> +</ol></dd> +</dl> + +<h1 id="html-blocks">HTML Blocks</h1> + +<p>Simple block on one line:</p> + +<div> +foo</div> + +<p>And nested without indentation:</p> + +<div> +<div> +<div> +<p>foo</p></div></div> + +<div> +bar</div></div> + +<p>Interpreted markdown in a table:</p> + +<table> + +<tr> + +<td> + +This is <em>emphasized</em> + +</td> + +<td> + +And this is <strong>strong</strong> + +</td> + +</tr> + +</table> + +<script type="text/javascript">document.write('This *should not* be interpreted as markdown');</script> + +<p>Here’s a simple block:</p> + +<div> +<p>foo</p></div> + +<p>This should be a code block, though:</p> + +<pre><code><div> + foo +</div></code></pre> + +<p>As should this:</p> + +<pre><code><div>foo</div></code></pre> + +<p>Now, nested:</p> + +<div> +<div> +<div> +foo</div></div></div> + +<p>This should just be an HTML comment:</p> + +<!-- Comment --> + +<p>Multiline:</p> + +<!-- +Blah +Blah +--> + +<!-- + This is another comment. +--> + +<p>Code block:</p> + +<pre><code><!-- Comment --></code></pre> + +<p>Just plain comment, with trailing spaces on the line:</p> + +<!-- foo --> + +<p>Code:</p> + +<pre><code><hr /></code></pre> + +<p>Hr’s:</p> + +<hr> + +<hr /> + +<hr /> + +<hr> + +<hr /> + +<hr /> + +<hr class="foo" id="bar" /> + +<hr class="foo" id="bar" /> + +<hr class="foo" id="bar"> + +<hr/> + +<h1 id="inline-markup">Inline Markup</h1> + +<p>This is <em>emphasized</em>, and so <em>is this</em>.</p> + +<p>This is <strong>strong</strong>, and so <strong>is this</strong>.</p> + +<p>An <em><a href='/url' title=''>emphasized link</a></em>.</p> + +<p><strong><em>This is strong and em.</em></strong></p> + +<p>So is <strong><em>this</em></strong> word.</p> + +<p><strong><em>This is strong and em.</em></strong></p> + +<p>So is <strong><em>this</em></strong> word.</p> + +<p>This is code: <code>></code>, <code>$</code>, <code>\</code>, <code>\$</code>, <code><html></code>.</p> + +<p><del>This is <em>strikeout</em>.</del></p> + +<p>Superscripts: a<sup>bc</sup>d a<sup><em>hello</em></sup> a<sup>hello there</sup>.</p> + +<p>Subscripts: H<sub>2</sub>O, H<sub>23</sub>O, H<sub>many of them</sub>O.</p> + +<p>These should not be superscripts or subscripts, +because of the unescaped spaces: a^b c^d, a~b c~d.</p> + +<hr/> + +<h1 id="smart-quotes-ellipses-dashes">Smart quotes, ellipses, dashes</h1> + +<p> said the spider. </p> + +<p>, , and are letters.</p> + +<p> and are names of trees. +So is </p> + +<p> Were you alive in the +70’s?</p> + +<p>Here is some quoted and a .</p> + +<p>Some dashes: one—two — three—four — five.</p> + +<p>Dashes between numbers: 5–7, 255–66, 1987–1999.</p> + +<p>Ellipses…and…and….</p> + +<hr/> + +<h1 id="latex">LaTeX</h1> + +<ul> +<li></li> +<li>\(2+2=4\)</li> +<li>\(x \in y\)</li> +<li>\(\alpha \wedge \omega\)</li> +<li>\(223\)</li> +<li>\(p\)-Tree</li> +<li>Here’s some display math: +\[\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}\]</li> +<li>Here’s one that has a line break in it: \(\alpha + \omega \times x^2\).</li> +</ul> + +<p>These shouldn’t be math:</p> + +<ul> +<li>To get the famous equation, write <code>$e = mc^2$</code>.</li> +<li>$22,000 is a <em>lot</em> of money. So is $34,000. +(It worked if is emphasized.)</li> +<li>Shoes ($20) and socks ($5).</li> +<li>Escaped <code>$</code>: $73 <em>this should be emphasized</em> 23$.</li> +</ul> + +<p>Here’s a LaTeX table:</p> + + + +<hr/> + +<h1 id="special-characters">Special Characters</h1> + +<p>Here is some unicode:</p> + +<ul> +<li>I hat: Î</li> +<li>o umlaut: ö</li> +<li>section: §</li> +<li>set membership: ∈</li> +<li>copyright: ©</li> +</ul> + +<p>AT&T has an ampersand in their name.</p> + +<p>AT&T is another way to write it.</p> + +<p>This & that.</p> + +<p>4 < 5.</p> + +<p>6 > 5.</p> + +<p>Backslash: \</p> + +<p>Backtick: `</p> + +<p>Asterisk: *</p> + +<p>Underscore: _</p> + +<p>Left brace: {</p> + +<p>Right brace: }</p> + +<p>Left bracket: [</p> + +<p>Right bracket: ]</p> + +<p>Left paren: (</p> + +<p>Right paren: )</p> + +<p>Greater-than: ></p> + +<p>Hash: #</p> + +<p>Period: .</p> + +<p>Bang: !</p> + +<p>Plus: +</p> + +<p>Minus: -</p> + +<hr/> + +<h1 id="links">Links</h1> + +<h2 id="explicit">Explicit</h2> + +<p>Just a <a href='/url/' title=''>URL</a>.</p> + +<p><a href='/url/' title='title'>URL and title</a>.</p> + +<p><a href='/url/' title='title preceded by two spaces'>URL and title</a>.</p> + +<p><a href='/url/' title='title preceded by a tab'>URL and title</a>.</p> + +<p><a href='/url/' title='title with "quotes" in it'>URL and title</a></p> + +<p><a href='/url/' title='title with single quotes'>URL and title</a></p> + +<p><a href='/url/with_underscore' title=''>with_underscore</a></p> + +<p><a href='mailto:nobody@nowhere.net' title=''>Email link</a></p> + +<p><a href='' title=''>Empty</a>.</p> + +<h2 id="reference">Reference</h2> + +<p>Foo <a href='/url/' title=''>bar</a>.</p> + +<p>With <a href='/url/' title=''>embedded [brackets]</a>.</p> + +<p><a href='/url/' title=''>b</a> by itself should be a link.</p> + +<p>Indented <a href='/url' title=''>once</a>.</p> + +<p>Indented <a href='/url' title=''>twice</a>.</p> + +<p>Indented <a href='/url' title=''>thrice</a>.</p> + +<p>This should [not][] be a link.</p> + +<pre><code>[not]: /url</code></pre> + +<p>Foo <a href='/url/' title='Title with "quotes" inside'>bar</a>.</p> + +<p>Foo <a href='/url/' title='Title with "quote" inside'>biz</a>.</p> + +<h2 id="with-ampersands">With ampersands</h2> + +<p>Here’s a <a href='http://example.com/?foo=1&bar=2' title=''>link with an ampersand in the URL</a>.</p> + +<p>Here’s a link with an amersand in the link text: <a href='http://att.com/' title='AT&T'>AT&T</a>.</p> + +<p>Here’s an <a href='/script?foo=1&bar=2' title=''>inline link</a>.</p> + +<p>Here’s an <a href='/script?foo=1&bar=2' title=''>inline link in pointy braces</a>.</p> + +<h2 id="autolinks">Autolinks</h2> + +<p>With an ampersand: <a href='http://example.com/?foo=1&bar=2' title=''>http://example.com/?foo=1&bar=2</a></p> + +<ul> +<li>In a list?</li> +<li><a href='http://example.com/' title=''>http://example.com/</a></li> +<li>It should.</li> +</ul> + +<p>An e-mail address: <a href='mailto:nobody@nowhere.net' title=''>nobody@nowhere.net</a></p> + +<blockquote> +<p>Blockquoted: <a href='http://example.com/' title=''>http://example.com/</a></p> +</blockquote> + +<p>Auto-links should not occur here: <code><http://example.com/></code></p> + +<pre><code>or here: <http://example.com/></code></pre> + +<hr/> + +<h1 id="images">Images</h1> + +<p>From by Georges Melies (1902):</p> + +<div class="figure"> +<img src="lalune.jpg" title="fig:Voyage dans la Lune"/> +<p class="caption">lalune</p> +</div> + +<p>Here is a movie <img src='movie.jpg' title=''/> icon.</p> + +<hr/> + +<h1 id="footnotes">Footnotes</h1> + +<p>Here is a footnote reference,<a id="fnref1" href="#fn1"><sup>1</sup></a> and another.<a id="fnref2" href="#fn2"><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 id="fnref3" href="#fn3"><sup>3</sup></a></p> + +<blockquote> +<p>Notes can go in quotes.<a id="fnref4" href="#fn4"><sup>4</sup></a></p> +</blockquote> + +<ol> +<li>And in list items.<a id="fnref5" href="#fn5"><sup>5</sup></a></li> +</ol> + +<p>This paragraph should not be part of the note, as it is not indented.</p> +<ol class="footnotes"> +<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> +<li id="fn2"><p>Here’s the long note. This one contains multiple +blocks.</p> + +<p>Subsequent blocks are indented to show that they belong to the +footnote (as with list items).</p> + +<pre><code> { <code> }</code></pre> + +<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> +<li id="fn3"><p>This +is <em>easier</em> to type. Inline notes may contain +<a href='http://google.com' title=''>links</a> and <code>]</code> verbatim characters, +as well as [bracketed text]. <a href="#fnref3">↩</a></p></li> +<li id="fn4"><p>In quote. <a href="#fnref4">↩</a></p></li> +<li id="fn5"><p>In list. <a href="#fnref5">↩</a></p></li> +</ol> + diff --git a/windows/make-windows-installer.bat b/windows/make-windows-installer.bat index 15d97d9d2..a64af6621 100644 --- a/windows/make-windows-installer.bat +++ b/windows/make-windows-installer.bat @@ -1,5 +1,5 @@ @echo off
-stack install --test --ghc-options="-O2" --stack-yaml=..\stack.pkg.yml
+stack install --test --ghc-options="-O2" --stack-yaml=..\stack.yaml
if %errorlevel% neq 0 exit /b %errorlevel%
for /f "delims=" %%a in ('stack path --local-bin-path') do @set BINPATH=%%a
%BINPATH%\pandoc.exe -s --toc ..\MANUAL.txt -o MANUAL.html
|