git.iko.soy/typeable/pandoc
on master help sign in
Added CommonMark writer.

Added `Text.Pandoc.Writers.CommonMark`, exporting `writeCommonMark`.

commitccb828894b6c9aae056a6861c20c4d6807035d9f authorJohn MacFarlane <jgm@berkeley.edu> dateMon 23 Mar 2015, 11:35 (12 years ago) parentcbe32c2
4 files changed+225−40
README+43−40 pandoc.cabal+1 src/Text/Pandoc.hs+3 src/Text/Pandoc/Writers/CommonMark.hs+178
README+43−40
@@ -158,22 +158,22 @@ General options
158158
159159: Specify input format. *FORMAT* can be `native` (native Haskell),
160160 `json` (JSON version of native AST), `markdown` (pandoc's
161− extended markdown), `markdown_strict` (original unextended markdown),
162− `markdown_phpextra` (PHP Markdown Extra extended markdown),
163− `markdown_github` (github extended markdown),
164− `commonmark` (CommonMark markdown),
165− `textile` (Textile), `rst` (reStructuredText), `html` (HTML),
166− `docbook` (DocBook), `t2t` (txt2tags), `docx` (docx), `epub` (EPUB),
167− `opml` (OPML), `org` (Emacs Org-mode), `mediawiki` (MediaWiki markup),
168− `twiki` (TWiki markup), `haddock` (Haddock markup), or `latex` (LaTeX).
169− If `+lhs` is appended to `markdown`, `rst`,
170− `latex`, or `html`, the input will be treated as literate Haskell
171− source: see [Literate Haskell support](#literate-haskell-support),
172− below. Markdown syntax extensions can be individually enabled or
173− disabled by appending `+EXTENSION` or `-EXTENSION` to the format
174− name. So, for example, `markdown_strict+footnotes+definition_lists`
175− is strict markdown with footnotes and definition lists enabled,
176− and `markdown-pipe_tables+hard_line_breaks` is pandoc's markdown
161+ extended markdown), `markdown_strict` (original unextended
162+ markdown), `markdown_phpextra` (PHP Markdown Extra extended
163+ markdown), `markdown_github` (github extended markdown),
164+ `commonmark` (CommonMark markdown), `textile` (Textile), `rst`
165+ (reStructuredText), `html` (HTML), `docbook` (DocBook), `t2t`
166+ (txt2tags), `docx` (docx), `epub` (EPUB), `opml` (OPML), `org`
167+ (Emacs Org-mode), `mediawiki` (MediaWiki markup), `twiki` (TWiki
168+ markup), `haddock` (Haddock markup), or `latex` (LaTeX). If
169+ `+lhs` is appended to `markdown`, `rst`, `latex`, or `html`, the
170+ input will be treated as literate Haskell source: see [Literate
171+ Haskell support](#literate-haskell-support), below. Markdown
172+ syntax extensions can be individually enabled or disabled by
173+ appending `+EXTENSION` or `-EXTENSION` to the format name. So, for
174+ example, `markdown_strict+footnotes+definition_lists` is strict
175+ markdown with footnotes and definition lists enabled, and
176+ `markdown-pipe_tables+hard_line_breaks` is pandoc's markdown
177177 without pipe tables and with hard line breaks. See [Pandoc's
178178 markdown](#pandocs-markdown), below, for a list of extensions and
179179 their names.
@@ -182,30 +182,33 @@ General options
182182
183183: Specify output format. *FORMAT* can be `native` (native Haskell),
184184 `json` (JSON version of native AST), `plain` (plain text),
185− `markdown` (pandoc's extended markdown), `markdown_strict` (original
186− unextended markdown), `markdown_phpextra` (PHP Markdown extra
187− extended markdown), `markdown_github` (github extended markdown),
188− `rst` (reStructuredText), `html` (XHTML 1), `html5` (HTML 5),
189− `latex` (LaTeX), `beamer` (LaTeX beamer slide show),
190− `context` (ConTeXt), `man` (groff man), `mediawiki` (MediaWiki markup),
191− `dokuwiki` (DokuWiki markup),
192− `textile` (Textile), `org` (Emacs Org-Mode), `texinfo` (GNU Texinfo),
193− `opml` (OPML), `docbook` (DocBook), `opendocument` (OpenDocument), `odt`
194− (OpenOffice text document), `docx` (Word docx), `haddock` (Haddock
195− markup), `rtf` (rich text format), `epub` (EPUB v2 book), `epub3`
196− (EPUB v3), `fb2` (FictionBook2 e-book), `asciidoc` (AsciiDoc),
197− `icml` (InDesign ICML), `slidy` (Slidy HTML and javascript slide show),
198− `slideous` (Slideous HTML and javascript slide show), `dzslides`
199− (DZSlides HTML5 + javascript slide show), `revealjs` (reveal.js
200− HTML5 + javascript slide show), `s5` (S5 HTML and javascript slide show),
201− or the path of a custom lua writer (see [Custom writers](#custom-writers),
202− below). Note that `odt`, `epub`, and `epub3` output will not be directed
203− to *stdout*; an output filename must be specified using the `-o/--output`
204− option. If `+lhs` is appended to `markdown`, `rst`, `latex`, `beamer`,
205− `html`, or `html5`, the output will be rendered as literate Haskell
206− source: see [Literate Haskell support](#literate-haskell-support), below.
207− Markdown syntax extensions can be individually enabled or disabled by
208− appending `+EXTENSION` or `-EXTENSION` to the format name, as described
185+ `markdown` (pandoc's extended markdown), `markdown_strict`
186+ (original unextended markdown), `markdown_phpextra` (PHP Markdown
187+ extra extended markdown), `markdown_github` (github extended
188+ markdown), `commonmark` (CommonMark markdown), `rst`
189+ (reStructuredText), `html` (XHTML 1), `html5` (HTML 5), `latex`
190+ (LaTeX), `beamer` (LaTeX beamer slide show), `context` (ConTeXt),
191+ `man` (groff man), `mediawiki` (MediaWiki markup), `dokuwiki`
192+ (DokuWiki markup), `textile` (Textile), `org` (Emacs Org-Mode),
193+ `texinfo` (GNU Texinfo), `opml` (OPML), `docbook` (DocBook),
194+ `opendocument` (OpenDocument), `odt` (OpenOffice text document),
195+ `docx` (Word docx), `haddock` (Haddock markup), `rtf` (rich text
196+ format), `epub` (EPUB v2 book), `epub3` (EPUB v3), `fb2`
197+ (FictionBook2 e-book), `asciidoc` (AsciiDoc), `icml` (InDesign
198+ ICML), `slidy` (Slidy HTML and javascript slide show), `slideous`
199+ (Slideous HTML and javascript slide show), `dzslides` (DZSlides
200+ HTML5 + javascript slide show), `revealjs` (reveal.js HTML5 +
201+ javascript slide show), `s5` (S5 HTML and javascript slide show),
202+ or the path of a custom lua writer (see [Custom
203+ writers](#custom-writers), below). Note that `odt`, `epub`, and
204+ `epub3` output will not be directed to *stdout*; an output
205+ filename must be specified using the `-o/--output` option. If
206+ `+lhs` is appended to `markdown`, `rst`, `latex`, `beamer`,
207+ `html`, or `html5`, the output will be rendered as literate
208+ Haskell source: see [Literate Haskell
209+ support](#literate-haskell-support), below. Markdown syntax
210+ extensions can be individually enabled or disabled by appending
211+ `+EXTENSION` or `-EXTENSION` to the format name, as described
209212 above under `-f`.
210213
211214`-o` *FILE*, `--output=`*FILE*
pandoc.cabal+1
@@ -342,6 +342,7 @@ Library
342342 Text.Pandoc.Writers.Texinfo,
343343 Text.Pandoc.Writers.Man,
344344 Text.Pandoc.Writers.Markdown,
345+ Text.Pandoc.Writers.CommonMark,
345346 Text.Pandoc.Writers.Haddock,
346347 Text.Pandoc.Writers.RST,
347348 Text.Pandoc.Writers.Org,
src/Text/Pandoc.hs+3
@@ -112,6 +112,7 @@ module Text.Pandoc
112112 , writeOrg
113113 , writeAsciiDoc
114114 , writeHaddock
115+ , writeCommonMark
115116 , writeCustom
116117 -- * Rendering templates and default templates
117118 , module Text.Pandoc.Templates
@@ -165,6 +166,7 @@ import Text.Pandoc.Writers.Textile
165166import Text.Pandoc.Writers.Org
166167import Text.Pandoc.Writers.AsciiDoc
167168import Text.Pandoc.Writers.Haddock
169+import Text.Pandoc.Writers.CommonMark
168170import Text.Pandoc.Writers.Custom
169171import Text.Pandoc.Templates
170172import Text.Pandoc.Options
@@ -305,6 +307,7 @@ writers = [
305307 ,("org" , PureStringWriter writeOrg)
306308 ,("asciidoc" , PureStringWriter writeAsciiDoc)
307309 ,("haddock" , PureStringWriter writeHaddock)
310+ ,("commonmark" , PureStringWriter writeCommonMark)
308311 ]
309312
310313getDefaultExtensions :: String -> Set Extension
src/Text/Pandoc/Writers/CommonMark.hsnew file+178
@@ -0,0 +1,178 @@
1+{-
2+Copyright (C) 2015 John MacFarlane <jgm@berkeley.edu>
3+
4+This program is free software; you can redistribute it and/or modify
5+it under the terms of the GNU General Public License as published by
6+the Free Software Foundation; either version 2 of the License, or
7+(at your option) any later version.
8+
9+This program is distributed in the hope that it will be useful,
10+but WITHOUT ANY WARRANTY; without even the implied warranty of
11+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+GNU General Public License for more details.
13+
14+You should have received a copy of the GNU General Public License
15+along with this program; if not, write to the Free Software
16+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17+-}
18+
19+{- |
20+ Module : Text.Pandoc.Writers.CommonMark
21+ Copyright : Copyright (C) 2015 John MacFarlane
22+ License : GNU GPL, version 2 or above
23+
24+ Maintainer : John MacFarlane <jgm@berkeley.edu>
25+ Stability : alpha
26+ Portability : portable
27+
28+Conversion of 'Pandoc' documents to CommonMark.
29+
30+CommonMark: <http://commonmark.org>
31+-}
32+module Text.Pandoc.Writers.CommonMark (writeCommonMark) where
33+
34+import Text.Pandoc.Writers.HTML (writeHtmlString)
35+import Text.Pandoc.Definition
36+import Text.Pandoc.Shared (isTightList)
37+import Text.Pandoc.Templates (renderTemplate')
38+import Text.Pandoc.Writers.Shared
39+import Text.Pandoc.Options
40+import CMark
41+import qualified Data.Text as T
42+import Control.Monad.Identity (runIdentity, Identity)
43+import Control.Monad.State (runState, State, modify, get)
44+import Text.Pandoc.Walk (walkM)
45+
46+-- | Convert Pandoc to CommonMark.
47+writeCommonMark :: WriterOptions -> Pandoc -> String
48+writeCommonMark opts (Pandoc meta blocks) = rendered
49+ where main = runIdentity $ blocksToCommonMark opts (blocks' ++ notes')
50+ (blocks', notes) = runState (walkM processNotes blocks) []
51+ notes' = if null notes
52+ then []
53+ else [OrderedList (1, Decimal, Period) $ reverse notes]
54+ metadata = runIdentity $ metaToJSON opts
55+ (blocksToCommonMark opts)
56+ (inlinesToCommonMark opts)
57+ meta
58+ context = defField "body" main $ metadata
59+ rendered = if writerStandalone opts
60+ then renderTemplate' (writerTemplate opts) context
61+ else main
62+
63+processNotes :: Inline -> State [[Block]] Inline
64+processNotes (Note bs) = do
65+ modify (bs :)
66+ notes <- get
67+ return $ Str $ "[" ++ show (length notes) ++ "]"
68+processNotes x = return x
69+
70+node :: NodeType -> [Node] -> Node
71+node = Node Nothing
72+
73+blocksToCommonMark :: WriterOptions -> [Block] -> Identity String
74+blocksToCommonMark opts bs = return $
75+ T.unpack $ nodeToCommonmark cmarkOpts colwidth
76+ $ node DOCUMENT (blocksToNodes bs)
77+ where cmarkOpts = [optHardBreaks | isEnabled Ext_hard_line_breaks opts]
78+ colwidth = if writerWrapText opts
79+ then writerColumns opts
80+ else 0
81+
82+inlinesToCommonMark :: WriterOptions -> [Inline] -> Identity String
83+inlinesToCommonMark opts ils = return $
84+ T.unpack $ nodeToCommonmark cmarkOpts colwidth
85+ $ node PARAGRAPH (inlinesToNodes ils)
86+ where cmarkOpts = [optHardBreaks | isEnabled Ext_hard_line_breaks opts]
87+ colwidth = if writerWrapText opts
88+ then writerColumns opts
89+ else 0
90+
91+blocksToNodes :: [Block] -> [Node]
92+blocksToNodes = foldr blockToNodes []
93+
94+blockToNodes :: Block -> [Node] -> [Node]
95+blockToNodes (Plain xs) = (node PARAGRAPH (inlinesToNodes xs) :)
96+blockToNodes (Para xs) = (node PARAGRAPH (inlinesToNodes xs) :)
97+blockToNodes (CodeBlock (_,classes,_) xs) =
98+ (node (CODE_BLOCK (T.pack (unwords classes)) (T.pack xs)) [] :)
99+blockToNodes (RawBlock fmt xs)
100+ | fmt == Format "html" = (node (HTML (T.pack xs)) [] :)
101+ | otherwise = id
102+blockToNodes (BlockQuote bs) =
103+ (node BLOCK_QUOTE (blocksToNodes bs) :)
104+blockToNodes (BulletList items) =
105+ (node (LIST ListAttributes{
106+ listType = BULLET_LIST,
107+ listDelim = PERIOD_DELIM,
108+ listTight = isTightList items,
109+ listStart = 1 }) (map (node ITEM . blocksToNodes) items) :)
110+blockToNodes (OrderedList (start, _sty, delim) items) =
111+ (node (LIST ListAttributes{
112+ listType = ORDERED_LIST,
113+ listDelim = case delim of
114+ OneParen -> PAREN_DELIM
115+ TwoParens -> PAREN_DELIM
116+ _ -> PERIOD_DELIM,
117+ listTight = isTightList items,
118+ listStart = start }) (map (node ITEM . blocksToNodes) items) :)
119+blockToNodes HorizontalRule = (node HRULE [] :)
120+blockToNodes (Header lev _ ils) = (node (HEADER lev) (inlinesToNodes ils) :)
121+blockToNodes (Div _ bs) = (blocksToNodes bs ++)
122+blockToNodes (DefinitionList items) = blockToNodes (BulletList items')
123+ where items' = map dlToBullet items
124+ dlToBullet (term, ((Para xs : ys) : zs)) =
125+ Para (term ++ [LineBreak] ++ xs) : ys ++ concat zs
126+ dlToBullet (term, ((Plain xs : ys) : zs)) =
127+ Plain (term ++ [LineBreak] ++ xs) : ys ++ concat zs
128+ dlToBullet (term, xs) =
129+ Para term : concat xs
130+blockToNodes t@(Table _ _ _ _ _) =
131+ (node (HTML (T.pack $! writeHtmlString def $! Pandoc nullMeta [t])) [] :)
132+blockToNodes Null = id
133+
134+inlinesToNodes :: [Inline] -> [Node]
135+inlinesToNodes = foldr inlineToNodes []
136+
137+inlineToNodes :: Inline -> [Node] -> [Node]
138+inlineToNodes (Str s) = (node (TEXT (T.pack s)) [] :)
139+inlineToNodes Space = (node (TEXT (T.pack " ")) [] :)
140+inlineToNodes LineBreak = (node LINEBREAK [] :)
141+inlineToNodes (Emph xs) = (node EMPH (inlinesToNodes xs) :)
142+inlineToNodes (Strong xs) = (node STRONG (inlinesToNodes xs) :)
143+inlineToNodes (Strikeout xs) =
144+ ((node (INLINE_HTML (T.pack "<s>")) [] : inlinesToNodes xs ++
145+ [node (INLINE_HTML (T.pack "</s>")) []]) ++ )
146+inlineToNodes (Superscript xs) =
147+ ((node (INLINE_HTML (T.pack "<sub>")) [] : inlinesToNodes xs ++
148+ [node (INLINE_HTML (T.pack "</sub>")) []]) ++ )
149+inlineToNodes (Subscript xs) =
150+ ((node (INLINE_HTML (T.pack "<sup>")) [] : inlinesToNodes xs ++
151+ [node (INLINE_HTML (T.pack "</sup>")) []]) ++ )
152+inlineToNodes (SmallCaps xs) =
153+ ((node (INLINE_HTML (T.pack "<span style=\"font-variant:small-caps;\">")) []
154+ : inlinesToNodes xs ++
155+ [node (INLINE_HTML (T.pack "</span>")) []]) ++ )
156+inlineToNodes (Link ils (url,tit)) =
157+ (node (LINK (T.pack url) (T.pack tit)) (inlinesToNodes ils) :)
158+inlineToNodes (Image ils (url,tit)) =
159+ (node (IMAGE (T.pack url) (T.pack tit)) (inlinesToNodes ils) :)
160+inlineToNodes (RawInline fmt xs)
161+ | fmt == Format "html" = (node (INLINE_HTML (T.pack xs)) [] :)
162+ | otherwise = id
163+inlineToNodes (Quoted qt ils) =
164+ ((node (TEXT start) [] : inlinesToNodes ils ++ [node (TEXT end) []]) ++)
165+ where (start, end) = case qt of
166+ SingleQuote -> (T.pack "‘", T.pack "’")
167+ DoubleQuote -> (T.pack "“", T.pack "”")
168+inlineToNodes (Code _ str) = (node (CODE (T.pack str)) [] :)
169+inlineToNodes (Math mt str) =
170+ case mt of
171+ InlineMath ->
172+ (node (INLINE_HTML (T.pack ("\\(" ++ str ++ "\\)"))) [] :)
173+ DisplayMath ->
174+ (node (INLINE_HTML (T.pack ("\\[" ++ str ++ "\\]"))) [] :)
175+inlineToNodes (Span _ ils) = (inlinesToNodes ils ++)
176+inlineToNodes (Cite _ ils) = (inlinesToNodes ils ++)
177+inlineToNodes (Note _) = id -- should not occur
178+-- we remove Note elements in preprocessing