{- Copyright (C) 2015 John MacFarlane This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | Module : Text.Pandoc.Readers.CommonMark Copyright : Copyright (C) 2015 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Conversion of CommonMark-formatted plain text to 'Pandoc' document. CommonMark is a strongly specified variant of Markdown: http://commonmark.org. -} module Text.Pandoc.Readers.CommonMark (readCommonMark) where import CMark import Data.Text (unpack, pack) import Data.List (groupBy) import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Error -- | Parse a CommonMark formatted string into a 'Pandoc' structure. readCommonMark :: ReaderOptions -> String -> Either PandocError Pandoc readCommonMark opts = Right . nodeToPandoc . commonmarkToNode opts' . pack where opts' = if readerSmart opts then [optNormalize, optSmart] else [optNormalize] nodeToPandoc :: Node -> Pandoc nodeToPandoc (Node _ DOCUMENT nodes) = Pandoc nullMeta $ foldr addBlock [] nodes nodeToPandoc n = -- shouldn't happen Pandoc nullMeta $ foldr addBlock [] [n] addBlocks :: [Node] -> [Block] addBlocks = foldr addBlock [] addBlock :: Node -> [Block] -> [Block] addBlock (Node _ PARAGRAPH nodes) = (Para (addInlines nodes) :) addBlock (Node _ THEMATIC_BREAK _) = (HorizontalRule :) addBlock (Node _ BLOCK_QUOTE nodes) = (BlockQuote (addBlocks nodes) :) addBlock (Node _ (HTML_BLOCK t) _) = (RawBlock (Format "html") (unpack t) :) -- Note: the cmark parser will never generate CUSTOM_BLOCK, -- so we don't need to handle it: addBlock (Node _ (CUSTOM_BLOCK _onEnter _onExit) _nodes) = id addBlock (Node _ (CODE_BLOCK info t) _) = (CodeBlock ("", take 1 (words (unpack info)), []) (unpack t) :) addBlock (Node _ (HEADING lev) nodes) = (Header lev ("",[],[]) (addInlines nodes) :) addBlock (Node _ (LIST listAttrs) nodes) = (constructor (map (setTightness . addBlocks . children) nodes) :) where constructor = case listType listAttrs of BULLET_LIST -> BulletList ORDERED_LIST -> OrderedList (start, DefaultStyle, delim) start = listStart listAttrs setTightness = if listTight listAttrs then map paraToPlain else id paraToPlain (Para xs) = Plain (xs) paraToPlain x = x delim = case listDelim listAttrs of PERIOD_DELIM -> Period PAREN_DELIM -> OneParen addBlock (Node _ ITEM _) = id -- handled in LIST addBlock _ = id children :: Node -> [Node] children (Node _ _ ns) = ns addInlines :: [Node] -> [Inline] addInlines = foldr addInline [] addInline :: Node -> [Inline] -> [Inline] addInline (Node _ (TEXT t) _) = (map toinl clumps ++) where raw = unpack t clumps = groupBy samekind raw samekind ' ' ' ' = True samekind ' ' _ = False samekind _ ' ' = False samekind _ _ = True toinl (' ':_) = Space toinl xs = Str xs addInline (Node _ LINEBREAK _) = (LineBreak :) addInline (Node _ SOFTBREAK _) = (SoftBreak :) addInline (Node _ (HTML_INLINE t) _) = (RawInline (Format "html") (unpack t) :) -- Note: the cmark parser will never generate CUSTOM_BLOCK, -- so we don't need to handle it: addInline (Node _ (CUSTOM_INLINE _onEnter _onExit) _nodes) = id addInline (Node _ (CODE t) _) = (Code ("",[],[]) (unpack t) :) addInline (Node _ EMPH nodes) = (Emph (addInlines nodes) :) addInline (Node _ STRONG nodes) = (Strong (addInlines nodes) :) addInline (Node _ (LINK url title) nodes) = (Link nullAttr (addInlines nodes) (unpack url, unpack title) :) addInline (Node _ (IMAGE url title) nodes) = (Image nullAttr (addInlines nodes) (unpack url, unpack title) :) addInline _ = id