diff options
25 files changed, 396 insertions, 276 deletions
@@ -77,6 +77,7 @@ import Text.Printf (printf) import System.Posix.Terminal (queryTerminal) import System.Posix.IO (stdOutput) #endif +import Text.Pandoc.Class (runIOorExplode, PandocIO) type Transform = Pandoc -> Pandoc @@ -914,7 +915,7 @@ options = let allopts = unwords (concatMap optnames options) UTF8.hPutStrLn stdout $ printf tpl allopts (unwords (map fst readers)) - (unwords (map fst writers)) + (unwords (map fst (writers' :: [(String, Writer' PandocIO)]))) (unwords $ map fst highlightingStyles) ddir exitSuccess )) @@ -931,7 +932,7 @@ options = , Option "" ["list-output-formats"] (NoArg (\_ -> do - let writers'names = sort (map fst writers) + let writers'names = sort (map fst (writers' :: [(String, Writer' PandocIO)])) mapM_ (UTF8.hPutStrLn stdout) writers'names exitSuccess )) "" @@ -1268,10 +1269,12 @@ convertWithOpts opts args = do let laTeXInput = "latex" `isPrefixOf` readerName' || "beamer" `isPrefixOf` readerName' + + -- disabling the custom writer for now writer <- if ".lua" `isSuffixOf` format -- note: use non-lowercased version writerName - then return $ IOStringWriter $ writeCustom writerName - else case getWriter writerName' of + then error "custom writers disabled for now" + else case getWriter' writerName' of Left e -> err 9 $ if format == "pdf" then e ++ @@ -1477,9 +1480,9 @@ convertWithOpts opts args = do writerFn f = UTF8.writeFile f case writer of - IOStringWriter f -> f writerOptions doc' >>= writerFn outputFile - IOByteStringWriter f -> f writerOptions doc' >>= writeFnBinary outputFile - PureStringWriter f + -- StringWriter f -> f writerOptions doc' >>= writerFn outputFile + ByteStringWriter' f -> (runIOorExplode $ f writerOptions doc') >>= writeFnBinary outputFile + StringWriter' f | pdfOutput -> do -- make sure writer is latex or beamer or context or html5 unless (laTeXOutput || conTeXtOutput || html5Output) $ @@ -1503,14 +1506,14 @@ convertWithOpts opts args = do B.hPutStr stderr err' B.hPut stderr $ B.pack [10] err 43 "Error producing PDF" - | otherwise -> selfcontain (f writerOptions doc' ++ - ['\n' | not standalone']) - >>= writerFn outputFile . handleEntities - where htmlFormat = format `elem` - ["html","html5","s5","slidy","slideous","dzslides","revealjs"] - selfcontain = if selfContained && htmlFormat - then makeSelfContained writerOptions - else return - handleEntities = if htmlFormat && ascii - then toEntities - else id + | otherwise -> do + let htmlFormat = format `elem` + ["html","html5","s5","slidy","slideous","dzslides","revealjs"] + selfcontain = if selfContained && htmlFormat + then makeSelfContained writerOptions + else return + handleEntities = if htmlFormat && ascii + then toEntities + else id + output <- runIOorExplode $ f writerOptions doc' + selfcontain (output ++ ['\n' | not standalone']) >>= writerFn outputFile . handleEntities diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index 703d0a002..5bb015fc2 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE ScopedTypeVariables, FlexibleInstances #-} +{-# LANGUAGE ScopedTypeVariables, FlexibleInstances, GADTs #-} {- Copyright (C) 2006-2016 John MacFarlane <jgm@berkeley.edu> @@ -63,7 +63,8 @@ module Text.Pandoc , module Text.Pandoc.Error -- * Lists of readers and writers , readers - , writers + -- , writers + , writers' -- * Readers: converting /to/ Pandoc format , Reader (..) , mkStringReader @@ -87,7 +88,8 @@ module Text.Pandoc , readTxt2TagsNoMacros , readEPUB -- * Writers: converting /from/ Pandoc format - , Writer (..) + -- , Writer (..) + , Writer'(..) , writeNative , writeJSON , writeMarkdown @@ -122,7 +124,8 @@ module Text.Pandoc , module Text.Pandoc.Templates -- * Miscellaneous , getReader - , getWriter + -- , getWriter + , getWriter' , getDefaultExtensions , ToJsonFilter(..) , pandocVersion @@ -180,7 +183,7 @@ import Text.Pandoc.Options import Text.Pandoc.Shared (safeRead, warn, mapLeft, pandocVersion) import Text.Pandoc.MediaBag (MediaBag) import Text.Pandoc.Error -import Text.Pandoc.Class (runIOorExplode) +import Text.Pandoc.Class (PandocMonad) import Data.Aeson import qualified Data.ByteString.Lazy as BL import Data.List (intercalate) @@ -262,74 +265,137 @@ readers = [ ("native" , StringReader $ \_ s -> return $ readNative s) ,("epub" , mkBSReader readEPUB) ] -data Writer = PureStringWriter (WriterOptions -> Pandoc -> String) - | IOStringWriter (WriterOptions -> Pandoc -> IO String) - | IOByteStringWriter (WriterOptions -> Pandoc -> IO BL.ByteString) +-- data Writer = PureStringWriter (WriterOptions -> Pandoc -> String) +-- | IOStringWriter (WriterOptions -> Pandoc -> IO String) +-- | IOByteStringWriter (WriterOptions -> Pandoc -> IO BL.ByteString) + +-- -- | Association list of formats and writers. +-- writers :: [ ( String, Writer ) ] +-- writers = [ +-- ("native" , PureStringWriter writeNative) +-- ,("json" , PureStringWriter writeJSON) +-- ,("docx" , IOByteStringWriter $ \o doc -> +-- runIOorExplode $ writeDocx o doc) +-- ,("odt" , IOByteStringWriter $ \o doc -> +-- runIOorExplode $ writeODT o doc) +-- ,("epub" , IOByteStringWriter $ \o doc -> +-- runIOorExplode $ +-- writeEPUB o{ writerEpubVersion = Just EPUB2 } doc) +-- ,("epub3" , IOByteStringWriter $ \o doc -> +-- runIOorExplode $ +-- writeEPUB o{ writerEpubVersion = Just EPUB3 } doc) +-- ,("fb2" , IOStringWriter $ \o doc -> +-- runIOorExplode $ writeFB2 o doc) +-- ,("html" , PureStringWriter writeHtmlString) +-- ,("html5" , PureStringWriter $ \o -> +-- writeHtmlString o{ writerHtml5 = True }) +-- ,("icml" , IOStringWriter $ \o doc -> +-- runIOorExplode $ writeICML o doc) +-- ,("s5" , PureStringWriter $ \o -> +-- writeHtmlString o{ writerSlideVariant = S5Slides +-- , writerTableOfContents = False }) +-- ,("slidy" , PureStringWriter $ \o -> +-- writeHtmlString o{ writerSlideVariant = SlidySlides }) +-- ,("slideous" , PureStringWriter $ \o -> +-- writeHtmlString o{ writerSlideVariant = SlideousSlides }) +-- ,("dzslides" , PureStringWriter $ \o -> +-- writeHtmlString o{ writerSlideVariant = DZSlides +-- , writerHtml5 = True }) +-- ,("revealjs" , PureStringWriter $ \o -> +-- writeHtmlString o{ writerSlideVariant = RevealJsSlides +-- , writerHtml5 = True }) +-- ,("docbook" , PureStringWriter writeDocbook) +-- ,("docbook5" , PureStringWriter $ \o -> +-- writeDocbook o{ writerDocbook5 = True }) +-- ,("opml" , PureStringWriter writeOPML) +-- ,("opendocument" , PureStringWriter writeOpenDocument) +-- ,("latex" , PureStringWriter writeLaTeX) +-- ,("beamer" , PureStringWriter $ \o -> +-- writeLaTeX o{ writerBeamer = True }) +-- ,("context" , PureStringWriter writeConTeXt) +-- ,("texinfo" , PureStringWriter writeTexinfo) +-- ,("man" , PureStringWriter writeMan) +-- ,("markdown" , PureStringWriter writeMarkdown) +-- ,("markdown_strict" , PureStringWriter writeMarkdown) +-- ,("markdown_phpextra" , PureStringWriter writeMarkdown) +-- ,("markdown_github" , PureStringWriter writeMarkdown) +-- ,("markdown_mmd" , PureStringWriter writeMarkdown) +-- ,("plain" , PureStringWriter writePlain) +-- ,("rst" , PureStringWriter writeRST) +-- ,("mediawiki" , PureStringWriter writeMediaWiki) +-- ,("dokuwiki" , PureStringWriter writeDokuWiki) +-- ,("zimwiki" , PureStringWriter writeZimWiki) +-- ,("textile" , PureStringWriter writeTextile) +-- ,("rtf" , IOStringWriter $ \o doc -> +-- runIOorExplode $ writeRTFWithEmbeddedImages o doc) +-- ,("org" , PureStringWriter writeOrg) +-- ,("asciidoc" , PureStringWriter writeAsciiDoc) +-- ,("haddock" , PureStringWriter writeHaddock) +-- ,("commonmark" , PureStringWriter writeCommonMark) +-- ,("tei" , PureStringWriter writeTEI) +-- ] + +data Writer' m = StringWriter' (WriterOptions -> Pandoc -> m String) + | ByteStringWriter' (WriterOptions -> Pandoc -> m BL.ByteString) -- | Association list of formats and writers. -writers :: [ ( String, Writer ) ] -writers = [ - ("native" , PureStringWriter writeNative) - ,("json" , PureStringWriter writeJSON) - ,("docx" , IOByteStringWriter $ \o doc -> - runIOorExplode $ writeDocx o doc) - ,("odt" , IOByteStringWriter $ \o doc -> - runIOorExplode $ writeODT o doc) - ,("epub" , IOByteStringWriter $ \o doc -> - runIOorExplode $ - writeEPUB o{ writerEpubVersion = Just EPUB2 } doc) - ,("epub3" , IOByteStringWriter $ \o doc -> - runIOorExplode $ - writeEPUB o{ writerEpubVersion = Just EPUB3 } doc) - ,("fb2" , IOStringWriter $ \o doc -> - runIOorExplode $ writeFB2 o doc) - ,("html" , PureStringWriter writeHtmlString) - ,("html5" , PureStringWriter $ \o -> +writers' :: PandocMonad m => [ ( String, Writer' m) ] +writers' = [ + ("native" , StringWriter' writeNative) + ,("json" , StringWriter' $ \o d -> return $ writeJSON o d) + ,("docx" , ByteStringWriter' writeDocx) + ,("odt" , ByteStringWriter' writeODT) + ,("epub" , ByteStringWriter' $ \o -> + writeEPUB o{ writerEpubVersion = Just EPUB2 }) + ,("epub3" , ByteStringWriter' $ \o -> + writeEPUB o{ writerEpubVersion = Just EPUB3 }) + ,("fb2" , StringWriter' writeFB2) + ,("html" , StringWriter' writeHtmlString) + ,("html5" , StringWriter' $ \o -> writeHtmlString o{ writerHtml5 = True }) - ,("icml" , IOStringWriter $ \o doc -> - runIOorExplode $ writeICML o doc) - ,("s5" , PureStringWriter $ \o -> + ,("icml" , StringWriter' writeICML) + ,("s5" , StringWriter' $ \o -> writeHtmlString o{ writerSlideVariant = S5Slides , writerTableOfContents = False }) - ,("slidy" , PureStringWriter $ \o -> + ,("slidy" , StringWriter' $ \o -> writeHtmlString o{ writerSlideVariant = SlidySlides }) - ,("slideous" , PureStringWriter $ \o -> + ,("slideous" , StringWriter' $ \o -> writeHtmlString o{ writerSlideVariant = SlideousSlides }) - ,("dzslides" , PureStringWriter $ \o -> + ,("dzslides" , StringWriter' $ \o -> writeHtmlString o{ writerSlideVariant = DZSlides , writerHtml5 = True }) - ,("revealjs" , PureStringWriter $ \o -> + ,("revealjs" , StringWriter' $ \o -> writeHtmlString o{ writerSlideVariant = RevealJsSlides , writerHtml5 = True }) - ,("docbook" , PureStringWriter writeDocbook) - ,("docbook5" , PureStringWriter $ \o -> + ,("docbook" , StringWriter' writeDocbook) + ,("docbook5" , StringWriter' $ \o -> writeDocbook o{ writerDocbook5 = True }) - ,("opml" , PureStringWriter writeOPML) - ,("opendocument" , PureStringWriter writeOpenDocument) - ,("latex" , PureStringWriter writeLaTeX) - ,("beamer" , PureStringWriter $ \o -> + ,("opml" , StringWriter' writeOPML) + ,("opendocument" , StringWriter' writeOpenDocument) + ,("latex" , StringWriter' writeLaTeX) + ,("beamer" , StringWriter' $ \o -> writeLaTeX o{ writerBeamer = True }) - ,("context" , PureStringWriter writeConTeXt) - ,("texinfo" , PureStringWriter writeTexinfo) - ,("man" , PureStringWriter writeMan) - ,("markdown" , PureStringWriter writeMarkdown) - ,("markdown_strict" , PureStringWriter writeMarkdown) - ,("markdown_phpextra" , PureStringWriter writeMarkdown) - ,("markdown_github" , PureStringWriter writeMarkdown) - ,("markdown_mmd" , PureStringWriter writeMarkdown) - ,("plain" , PureStringWriter writePlain) - ,("rst" , PureStringWriter writeRST) - ,("mediawiki" , PureStringWriter writeMediaWiki) - ,("dokuwiki" , PureStringWriter writeDokuWiki) - ,("zimwiki" , PureStringWriter writeZimWiki) - ,("textile" , PureStringWriter writeTextile) - ,("rtf" , IOStringWriter $ \o doc -> - runIOorExplode $ writeRTFWithEmbeddedImages o doc) - ,("org" , PureStringWriter writeOrg) - ,("asciidoc" , PureStringWriter writeAsciiDoc) - ,("haddock" , PureStringWriter writeHaddock) - ,("commonmark" , PureStringWriter writeCommonMark) - ,("tei" , PureStringWriter writeTEI) + ,("context" , StringWriter' writeConTeXt) + ,("texinfo" , StringWriter' writeTexinfo) + ,("man" , StringWriter' writeMan) + ,("markdown" , StringWriter' writeMarkdown) + ,("markdown_strict" , StringWriter' writeMarkdown) + ,("markdown_phpextra" , StringWriter' writeMarkdown) + ,("markdown_github" , StringWriter' writeMarkdown) + ,("markdown_mmd" , StringWriter' writeMarkdown) + ,("plain" , StringWriter' writePlain) + ,("rst" , StringWriter' writeRST) + ,("mediawiki" , StringWriter' writeMediaWiki) + ,("dokuwiki" , StringWriter' writeDokuWiki) + ,("zimwiki" , StringWriter' writeZimWiki) + ,("textile" , StringWriter' writeTextile) + ,("rtf" , StringWriter' $ \o -> + writeRTFWithEmbeddedImages o) + ,("org" , StringWriter' writeOrg) + ,("asciidoc" , StringWriter' writeAsciiDoc) + ,("haddock" , StringWriter' writeHaddock) + ,("commonmark" , StringWriter' writeCommonMark) + ,("tei" , StringWriter' writeTEI) ] getDefaultExtensions :: String -> Set Extension @@ -368,20 +434,34 @@ getReader s = getDefaultExtensions readerName } -- | Retrieve writer based on formatSpec (format+extensions). -getWriter :: String -> Either String Writer -getWriter s +-- getWriter :: String -> Either String Writer +-- getWriter s +-- = case parseFormatSpec s of +-- Left e -> Left $ intercalate "\n" [m | Message m <- errorMessages e] +-- Right (writerName, setExts) -> +-- case lookup writerName writers of +-- Nothing -> Left $ "Unknown writer: " ++ writerName +-- Just (PureStringWriter r) -> Right $ PureStringWriter $ +-- \o -> r o{ writerExtensions = setExts $ +-- getDefaultExtensions writerName } +-- Just (IOStringWriter r) -> Right $ IOStringWriter $ +-- \o -> r o{ writerExtensions = setExts $ +-- getDefaultExtensions writerName } +-- Just (IOByteStringWriter r) -> Right $ IOByteStringWriter $ +-- \o -> r o{ writerExtensions = setExts $ +-- getDefaultExtensions writerName } + +getWriter' :: PandocMonad m => String -> Either String (Writer' m) +getWriter' s = case parseFormatSpec s of Left e -> Left $ intercalate "\n" [m | Message m <- errorMessages e] Right (writerName, setExts) -> - case lookup writerName writers of + case lookup writerName writers' of Nothing -> Left $ "Unknown writer: " ++ writerName - Just (PureStringWriter r) -> Right $ PureStringWriter $ - \o -> r o{ writerExtensions = setExts $ - getDefaultExtensions writerName } - Just (IOStringWriter r) -> Right $ IOStringWriter $ + Just (StringWriter' r) -> Right $ StringWriter' $ \o -> r o{ writerExtensions = setExts $ getDefaultExtensions writerName } - Just (IOByteStringWriter r) -> Right $ IOByteStringWriter $ + Just (ByteStringWriter' r) -> Right $ ByteStringWriter' $ \o -> r o{ writerExtensions = setExts $ getDefaultExtensions writerName } diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index 9faff1816..7aaa257fa 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -60,6 +60,7 @@ import qualified Codec.Picture as JP #ifdef _WINDOWS import Data.List (intercalate) #endif +import Text.Pandoc.Class (PandocIO, runIOorExplode) #ifdef _WINDOWS changePathSeparators :: FilePath -> FilePath @@ -68,7 +69,7 @@ changePathSeparators = intercalate "/" . splitDirectories makePDF :: String -- ^ pdf creator (pdflatex, lualatex, -- xelatex, context, wkhtmltopdf) - -> (WriterOptions -> Pandoc -> String) -- ^ writer + -> (WriterOptions -> Pandoc -> PandocIO String) -- ^ writer -> WriterOptions -- ^ options -> Pandoc -- ^ document -> IO (Either ByteString ByteString) @@ -93,12 +94,12 @@ makePDF "wkhtmltopdf" writer opts doc@(Pandoc meta _) = do ,("margin-left", fromMaybe (Just "1.25in") (getField "margin-left" meta')) ] - let source = writer opts doc + source <- runIOorExplode $ writer opts doc html2pdf (writerVerbose opts) args source makePDF program writer opts doc = withTempDir "tex2pdf." $ \tmpdir -> do doc' <- handleImages opts tmpdir doc - let source = writer opts doc' - args = writerLaTeXArgs opts + source <- runIOorExplode $ writer opts doc' + let args = writerLaTeXArgs opts case takeBaseName program of "context" -> context2pdf (writerVerbose opts) tmpdir source prog | prog `elem` ["pdflatex", "lualatex", "xelatex"] diff --git a/src/Text/Pandoc/Writers/AsciiDoc.hs b/src/Text/Pandoc/Writers/AsciiDoc.hs index 88fab171f..eed6183b4 100644 --- a/src/Text/Pandoc/Writers/AsciiDoc.hs +++ b/src/Text/Pandoc/Writers/AsciiDoc.hs @@ -52,6 +52,7 @@ import qualified Data.Map as M import Data.Aeson (Value(String), fromJSON, toJSON, Result(..)) import qualified Data.Text as T import Data.Char (isSpace, isPunctuation) +import Text.Pandoc.Class (PandocMonad) data WriterState = WriterState { defListMarker :: String , orderedListLevel :: Int @@ -60,8 +61,8 @@ data WriterState = WriterState { defListMarker :: String } -- | Convert Pandoc to AsciiDoc. -writeAsciiDoc :: WriterOptions -> Pandoc -> String -writeAsciiDoc opts document = +writeAsciiDoc :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeAsciiDoc opts document = return $ evalState (pandocToAsciiDoc opts document) WriterState{ defListMarker = "::" , orderedListLevel = 1 diff --git a/src/Text/Pandoc/Writers/CommonMark.hs b/src/Text/Pandoc/Writers/CommonMark.hs index e0591de83..b6ff35bbe 100644 --- a/src/Text/Pandoc/Writers/CommonMark.hs +++ b/src/Text/Pandoc/Writers/CommonMark.hs @@ -39,26 +39,27 @@ import Text.Pandoc.Writers.Shared import Text.Pandoc.Options import CMark import qualified Data.Text as T -import Control.Monad.Identity (runIdentity, Identity) import Control.Monad.State (runState, State, modify, get) import Text.Pandoc.Walk (walkM) +import Text.Pandoc.Class (PandocMonad) +import Data.Foldable (foldrM) -- | Convert Pandoc to CommonMark. -writeCommonMark :: WriterOptions -> Pandoc -> String -writeCommonMark opts (Pandoc meta blocks) = rendered - where main = runIdentity $ blocksToCommonMark opts (blocks' ++ notes') - (blocks', notes) = runState (walkM processNotes blocks) [] - notes' = if null notes - then [] - else [OrderedList (1, Decimal, Period) $ reverse notes] - metadata = runIdentity $ metaToJSON opts - (blocksToCommonMark opts) - (inlinesToCommonMark opts) - meta - context = defField "body" main $ metadata - rendered = case writerTemplate opts of - Nothing -> main - Just tpl -> renderTemplate' tpl context +writeCommonMark :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeCommonMark opts (Pandoc meta blocks) = do + let (blocks', notes) = runState (walkM processNotes blocks) [] + notes' = if null notes + then [] + else [OrderedList (1, Decimal, Period) $ reverse notes] + main <- blocksToCommonMark opts (blocks' ++ notes') + metadata <- metaToJSON opts + (blocksToCommonMark opts) + (inlinesToCommonMark opts) + meta + let context = defField "body" main $ metadata + return $ case writerTemplate opts of + Nothing -> main + Just tpl -> renderTemplate' tpl context processNotes :: Inline -> State [[Block]] Inline processNotes (Note bs) = do @@ -70,16 +71,19 @@ processNotes x = return x node :: NodeType -> [Node] -> Node node = Node Nothing -blocksToCommonMark :: WriterOptions -> [Block] -> Identity String -blocksToCommonMark opts bs = return $ - T.unpack $ nodeToCommonmark cmarkOpts colwidth - $ node DOCUMENT (blocksToNodes bs) - where cmarkOpts = [optHardBreaks | isEnabled Ext_hard_line_breaks opts] - colwidth = if writerWrapText opts == WrapAuto - then Just $ writerColumns opts - else Nothing - -inlinesToCommonMark :: WriterOptions -> [Inline] -> Identity String +blocksToCommonMark :: PandocMonad m => WriterOptions -> [Block] -> m String +blocksToCommonMark opts bs = do + let cmarkOpts = [optHardBreaks | isEnabled Ext_hard_line_breaks opts] + colwidth = if writerWrapText opts == WrapAuto + then Just $ writerColumns opts + else Nothing + nodes <- blocksToNodes bs + return $ + T.unpack $ + nodeToCommonmark cmarkOpts colwidth $ + node DOCUMENT nodes + +inlinesToCommonMark :: PandocMonad m => WriterOptions -> [Inline] -> m String inlinesToCommonMark opts ils = return $ T.unpack $ nodeToCommonmark cmarkOpts colwidth $ node PARAGRAPH (inlinesToNodes ils) @@ -88,39 +92,44 @@ inlinesToCommonMark opts ils = return $ then Just $ writerColumns opts else Nothing -blocksToNodes :: [Block] -> [Node] -blocksToNodes = foldr blockToNodes [] - -blockToNodes :: Block -> [Node] -> [Node] -blockToNodes (Plain xs) = (node PARAGRAPH (inlinesToNodes xs) :) -blockToNodes (Para xs) = (node PARAGRAPH (inlinesToNodes xs) :) -blockToNodes (LineBlock lns) = blockToNodes $ linesToPara lns -blockToNodes (CodeBlock (_,classes,_) xs) = - (node (CODE_BLOCK (T.pack (unwords classes)) (T.pack xs)) [] :) -blockToNodes (RawBlock fmt xs) - | fmt == Format "html" = (node (HTML_BLOCK (T.pack xs)) [] :) - | otherwise = (node (CUSTOM_BLOCK (T.pack xs) (T.empty)) [] :) -blockToNodes (BlockQuote bs) = - (node BLOCK_QUOTE (blocksToNodes bs) :) -blockToNodes (BulletList items) = - (node (LIST ListAttributes{ - listType = BULLET_LIST, - listDelim = PERIOD_DELIM, - listTight = isTightList items, - listStart = 1 }) (map (node ITEM . blocksToNodes) items) :) -blockToNodes (OrderedList (start, _sty, delim) items) = - (node (LIST ListAttributes{ - listType = ORDERED_LIST, - listDelim = case delim of - OneParen -> PAREN_DELIM - TwoParens -> PAREN_DELIM - _ -> PERIOD_DELIM, - listTight = isTightList items, - listStart = start }) (map (node ITEM . blocksToNodes) items) :) -blockToNodes HorizontalRule = (node THEMATIC_BREAK [] :) -blockToNodes (Header lev _ ils) = (node (HEADING lev) (inlinesToNodes ils) :) -blockToNodes (Div _ bs) = (blocksToNodes bs ++) -blockToNodes (DefinitionList items) = blockToNodes (BulletList items') +blocksToNodes :: PandocMonad m => [Block] -> m [Node] +blocksToNodes = foldrM blockToNodes [] + +blockToNodes :: PandocMonad m => Block -> [Node] -> m [Node] +blockToNodes (Plain xs) ns = return (node PARAGRAPH (inlinesToNodes xs) : ns) +blockToNodes (Para xs) ns = return (node PARAGRAPH (inlinesToNodes xs) : ns) +blockToNodes (LineBlock lns) ns = blockToNodes (linesToPara lns) ns +blockToNodes (CodeBlock (_,classes,_) xs) ns = return $ + (node (CODE_BLOCK (T.pack (unwords classes)) (T.pack xs)) [] : ns) +blockToNodes (RawBlock fmt xs) ns + | fmt == Format "html" = return (node (HTML_BLOCK (T.pack xs)) [] : ns) + | otherwise = return (node (CUSTOM_BLOCK (T.pack xs) (T.empty)) [] : ns) +blockToNodes (BlockQuote bs) ns = do + nodes <- blocksToNodes bs + return (node BLOCK_QUOTE nodes : ns) +blockToNodes (BulletList items) ns = do + nodes <- mapM blocksToNodes items + return (node (LIST ListAttributes{ + listType = BULLET_LIST, + listDelim = PERIOD_DELIM, + listTight = isTightList items, + listStart = 1 }) (map (node ITEM) nodes) : ns) +blockToNodes (OrderedList (start, _sty, delim) items) ns = do + nodes <- mapM blocksToNodes items + return (node (LIST ListAttributes{ + listType = ORDERED_LIST, + listDelim = case delim of + OneParen -> PAREN_DELIM + TwoParens -> PAREN_DELIM + _ -> PERIOD_DELIM, + listTight = isTightList items, + listStart = start }) (map (node ITEM) nodes) : ns) +blockToNodes HorizontalRule ns = return (node THEMATIC_BREAK [] : ns) +blockToNodes (Header lev _ ils) ns = return (node (HEADING lev) (inlinesToNodes ils) : ns) +blockToNodes (Div _ bs) ns = do + nodes <- blocksToNodes bs + return (nodes ++ ns) +blockToNodes (DefinitionList items) ns = blockToNodes (BulletList items') ns where items' = map dlToBullet items dlToBullet (term, ((Para xs : ys) : zs)) = Para (term ++ [LineBreak] ++ xs) : ys ++ concat zs @@ -128,9 +137,10 @@ blockToNodes (DefinitionList items) = blockToNodes (BulletList items') Plain (term ++ [LineBreak] ++ xs) : ys ++ concat zs dlToBullet (term, xs) = Para term : concat xs -blockToNodes t@(Table _ _ _ _ _) = - (node (HTML_BLOCK (T.pack $! writeHtmlString def $! Pandoc nullMeta [t])) [] :) -blockToNodes Null = id +blockToNodes t@(Table _ _ _ _ _) ns = do + s <- writeHtmlString def $! Pandoc nullMeta [t] + return (node (HTML_BLOCK (T.pack $! s)) [] : ns) +blockToNodes Null ns = return ns inlinesToNodes :: [Inline] -> [Node] inlinesToNodes = foldr inlineToNodes [] diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index ee2cc3f34..c8a4abfd5 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -43,6 +43,7 @@ import Text.Pandoc.Pretty import Text.Pandoc.ImageSize import Text.Pandoc.Templates ( renderTemplate' ) import Network.URI ( isURI, unEscapeString ) +import Text.Pandoc.Class (PandocMonad) data WriterState = WriterState { stNextRef :: Int -- number of next URL reference @@ -54,8 +55,8 @@ orderedListStyles :: [Char] orderedListStyles = cycle "narg" -- | Convert Pandoc to ConTeXt. -writeConTeXt :: WriterOptions -> Pandoc -> String -writeConTeXt options document = +writeConTeXt :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeConTeXt options document = return $ let defaultWriterState = WriterState { stNextRef = 1 , stOrderedListLevel = 0 , stOptions = options diff --git a/src/Text/Pandoc/Writers/Docbook.hs b/src/Text/Pandoc/Writers/Docbook.hs index 5c03d449d..74e3bff3d 100644 --- a/src/Text/Pandoc/Writers/Docbook.hs +++ b/src/Text/Pandoc/Writers/Docbook.hs @@ -47,6 +47,7 @@ import qualified Text.Pandoc.Builder as B import Text.TeXMath import qualified Text.XML.Light as Xml import Data.Generics (everywhere, mkT) +import Text.Pandoc.Class (PandocMonad) -- | Convert list of authors to a docbook <author> section authorToDocbook :: WriterOptions -> [Inline] -> B.Inlines @@ -73,8 +74,8 @@ authorToDocbook opts name' = inTagsSimple "surname" (text $ escapeStringForXML lastname) -- | Convert Pandoc document to string in Docbook format. -writeDocbook :: WriterOptions -> Pandoc -> String -writeDocbook opts (Pandoc meta blocks) = +writeDocbook :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeDocbook opts (Pandoc meta blocks) = return $ let elements = hierarchicalize blocks colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts diff --git a/src/Text/Pandoc/Writers/DokuWiki.hs b/src/Text/Pandoc/Writers/DokuWiki.hs index c90dc9078..c7a09fe50 100644 --- a/src/Text/Pandoc/Writers/DokuWiki.hs +++ b/src/Text/Pandoc/Writers/DokuWiki.hs @@ -55,6 +55,7 @@ import Network.URI ( isURI ) import Control.Monad ( zipWithM ) import Control.Monad.State ( modify, State, get, evalState ) import Control.Monad.Reader ( ReaderT, runReaderT, ask, local ) +import Text.Pandoc.Class (PandocMonad) data WriterState = WriterState { stNotes :: Bool -- True if there are notes @@ -77,8 +78,8 @@ instance Default WriterEnvironment where type DokuWiki = ReaderT WriterEnvironment (State WriterState) -- | Convert Pandoc to DokuWiki. -writeDokuWiki :: WriterOptions -> Pandoc -> String -writeDokuWiki opts document = +writeDokuWiki :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeDokuWiki opts document = return $ runDokuWiki (pandocToDokuWiki opts $ normalize document) runDokuWiki :: DokuWiki a -> a diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index 397aa5847..298561db6 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -55,7 +55,7 @@ import Text.Pandoc.Definition import Text.Pandoc.Walk (walk, walkM, query) import Text.Pandoc.UUID (getUUID) import Control.Monad.State (modify, get, gets, State, StateT, put, evalState, evalStateT, lift) -import Control.Monad (mplus, when) +import Control.Monad (mplus, when, zipWithM) import Text.XML.Light ( unode, Element(..), unqual, Attr(..), add_attrs , strContent, lookupAttr, Node(..), QName(..), parseXML , onlyElems, node, ppElement) @@ -374,17 +374,17 @@ pandocToEPUB opts doc@(Pandoc meta _) = do Nothing -> return ([],[]) Just img -> do let coverImage = "media/" ++ takeFileName img - let cpContent = renderHtml $ writeHtml + cpContent <- renderHtml <$> (lift $ writeHtml opts'{ writerVariables = ("coverpage","true"):vars } - (Pandoc meta [RawBlock (Format "html") $ "<div id=\"cover-image\">\n<img src=\"" ++ coverImage ++ "\" alt=\"cover image\" />\n</div>"]) + (Pandoc meta [RawBlock (Format "html") $ "<div id=\"cover-image\">\n<img src=\"" ++ coverImage ++ "\" alt=\"cover image\" />\n</div>"])) imgContent <- lift $ P.readFileLazy img return ( [mkEntry "cover.xhtml" cpContent] , [mkEntry coverImage imgContent] ) -- title page - let tpContent = renderHtml $ writeHtml opts'{ - writerVariables = ("titlepage","true"):vars } - (Pandoc meta []) + tpContent <- renderHtml <$> (lift $ writeHtml opts'{ + writerVariables = ("titlepage","true"):vars } + (Pandoc meta [])) let tpEntry = mkEntry "title_page.xhtml" tpContent -- handle pictures @@ -482,20 +482,20 @@ pandocToEPUB opts doc@(Pandoc meta _) = do Chapter mbnum $ walk fixInternalReferences bs) chapters' - let chapToEntry :: Int -> Chapter -> Entry - chapToEntry num (Chapter mbnum bs) = mkEntry (showChapter num) - $ renderHtml - $ writeHtml opts'{ writerNumberOffset = - fromMaybe [] mbnum } - $ case bs of - (Header _ _ xs : _) -> - -- remove notes or we get doubled footnotes - Pandoc (setMeta "title" (walk removeNote $ fromList xs) - nullMeta) bs - _ -> - Pandoc nullMeta bs - - let chapterEntries = zipWith chapToEntry [1..] chapters + let chapToEntry :: PandocMonad m => Int -> Chapter -> m Entry + chapToEntry num (Chapter mbnum bs) = + (mkEntry (showChapter num) . renderHtml) <$> + (writeHtml opts'{ writerNumberOffset = + fromMaybe [] mbnum } + $ case bs of + (Header _ _ xs : _) -> + -- remove notes or we get doubled footnotes + Pandoc (setMeta "title" (walk removeNote $ fromList xs) + nullMeta) bs + _ -> + Pandoc nullMeta bs) + + chapterEntries <- lift $ zipWithM chapToEntry [1..] chapters -- incredibly inefficient (TODO): let containsMathML ent = epub3 && @@ -679,11 +679,11 @@ pandocToEPUB opts doc@(Pandoc meta _) = do ] ] else [] - let navData = renderHtml $ writeHtml + navData <- renderHtml <$> (lift $ writeHtml opts'{ writerVariables = ("navpage","true"):vars } (Pandoc (setMeta "title" (walk removeNote $ fromList $ docTitle' meta) nullMeta) - (navBlocks ++ landmarks)) + (navBlocks ++ landmarks))) let navEntry = mkEntry "nav.xhtml" navData -- mimetype diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index e0b0234fb..6f25939f0 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -68,6 +68,7 @@ import Text.XML.Light (unode, elChildren, unqual) import qualified Text.XML.Light as XML import System.FilePath (takeExtension) import Data.Aeson (Value) +import Text.Pandoc.Class (PandocMonad) data WriterState = WriterState { stNotes :: [Html] -- ^ List of notes @@ -99,8 +100,8 @@ nl opts = if writerWrapText opts == WrapNone else preEscapedString "\n" -- | Convert Pandoc document to Html string. -writeHtmlString :: WriterOptions -> Pandoc -> String -writeHtmlString opts d = +writeHtmlString :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeHtmlString opts d = return $ let (body, context) = evalState (pandocToHtml opts d) defaultWriterState in case writerTemplate opts of Nothing -> renderHtml body @@ -108,8 +109,8 @@ writeHtmlString opts d = defField "body" (renderHtml body) context -- | Convert Pandoc document to Html structure. -writeHtml :: WriterOptions -> Pandoc -> Html -writeHtml opts d = +writeHtml :: PandocMonad m => WriterOptions -> Pandoc -> m Html +writeHtml opts d = return $ let (body, context) = evalState (pandocToHtml opts d) defaultWriterState in case writerTemplate opts of Nothing -> body diff --git a/src/Text/Pandoc/Writers/Haddock.hs b/src/Text/Pandoc/Writers/Haddock.hs index 4e93cc4e4..03ce8c0eb 100644 --- a/src/Text/Pandoc/Writers/Haddock.hs +++ b/src/Text/Pandoc/Writers/Haddock.hs @@ -42,6 +42,7 @@ import Control.Monad.State import Text.Pandoc.Readers.TeXMath (texMathToInlines) import Network.URI (isURI) import Data.Default +import Text.Pandoc.Class (PandocMonad) type Notes = [[Block]] data WriterState = WriterState { stNotes :: Notes } @@ -49,8 +50,8 @@ instance Default WriterState where def = WriterState{ stNotes = [] } -- | Convert Pandoc to Haddock. -writeHaddock :: WriterOptions -> Pandoc -> String -writeHaddock opts document = +writeHaddock :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeHaddock opts document = return $ evalState (pandocToHaddock opts{ writerWrapText = writerWrapText opts } document) def diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 50e99fe15..dbb8e4326 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -54,6 +54,7 @@ import Text.Pandoc.Slides import Text.Pandoc.Highlighting (highlight, styleToLaTeX, formatLaTeXInline, formatLaTeXBlock, toListingsLanguage) +import Text.Pandoc.Class (PandocMonad) data WriterState = WriterState { stInNote :: Bool -- true if we're in a note @@ -78,8 +79,8 @@ data WriterState = } -- | Convert Pandoc to LaTeX. -writeLaTeX :: WriterOptions -> Pandoc -> String -writeLaTeX options document = +writeLaTeX :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeLaTeX options document = return $ evalState (pandocToLaTeX options document) $ WriterState { stInNote = False, stInQuote = False, stInMinipage = False, stInHeading = False, diff --git a/src/Text/Pandoc/Writers/Man.hs b/src/Text/Pandoc/Writers/Man.hs index 304995ec8..75c026463 100644 --- a/src/Text/Pandoc/Writers/Man.hs +++ b/src/Text/Pandoc/Writers/Man.hs @@ -41,14 +41,15 @@ import Data.Maybe (fromMaybe) import Text.Pandoc.Pretty import Text.Pandoc.Builder (deleteMeta) import Control.Monad.State +import Text.Pandoc.Class (PandocMonad) type Notes = [[Block]] data WriterState = WriterState { stNotes :: Notes , stHasTables :: Bool } -- | Convert Pandoc to Man. -writeMan :: WriterOptions -> Pandoc -> String -writeMan opts document = evalState (pandocToMan opts document) (WriterState [] False) +writeMan :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeMan opts document = return $ evalState (pandocToMan opts document) (WriterState [] False) -- | Return groff man representation of document. pandocToMan :: WriterOptions -> Pandoc -> State WriterState String diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index f9c7c326e..787db10f9 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -57,15 +57,16 @@ import qualified Data.Vector as V import qualified Data.Text as T import qualified Data.Set as Set import Network.HTTP ( urlEncode ) +import Text.Pandoc.Class (PandocMonad) type Notes = [[Block]] type Ref = ([Inline], Target, Attr) type Refs = [Ref] -type MD = ReaderT WriterEnv (State WriterState) +type MD m = ReaderT WriterEnv (StateT WriterState m) -evalMD :: MD a -> WriterEnv -> WriterState -> a -evalMD md env st = evalState (runReaderT md env) st +evalMD :: PandocMonad m => MD m a -> WriterEnv -> WriterState -> m a +evalMD md env st = evalStateT (runReaderT md env) st data WriterEnv = WriterEnv { envInList :: Bool , envPlain :: Bool @@ -96,7 +97,7 @@ instance Default WriterState } -- | Convert Pandoc to Markdown. -writeMarkdown :: WriterOptions -> Pandoc -> String +writeMarkdown :: PandocMonad m => WriterOptions -> Pandoc -> m String writeMarkdown opts document = evalMD (pandocToMarkdown opts{ writerWrapText = if isEnabled Ext_hard_line_breaks opts @@ -106,7 +107,7 @@ writeMarkdown opts document = -- | Convert Pandoc to plain text (like markdown, but without links, -- pictures, or inline formatting). -writePlain :: WriterOptions -> Pandoc -> String +writePlain :: PandocMonad m => WriterOptions -> Pandoc -> m String writePlain opts document = evalMD (pandocToMarkdown opts document) def{ envPlain = True } def @@ -171,7 +172,7 @@ jsonToYaml (Number n) = text $ show n jsonToYaml _ = empty -- | Return markdown representation of document. -pandocToMarkdown :: WriterOptions -> Pandoc -> MD String +pandocToMarkdown :: PandocMonad m => WriterOptions -> Pandoc -> MD m String pandocToMarkdown opts (Pandoc meta blocks) = do let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts @@ -196,9 +197,9 @@ pandocToMarkdown opts (Pandoc meta blocks) = do | otherwise -> empty Nothing -> empty let headerBlocks = filter isHeaderBlock blocks - let toc = if writerTableOfContents opts - then tableOfContents opts headerBlocks - else empty + toc <- if writerTableOfContents opts + then lift $ lift $ tableOfContents opts headerBlocks + else return empty -- Strip off final 'references' header if markdown citations enabled let blocks' = if isEnabled Ext_citations opts then case reverse blocks of @@ -221,13 +222,14 @@ pandocToMarkdown opts (Pandoc meta blocks) = do Just tpl -> return $ renderTemplate' tpl context -- | Return markdown representation of reference key table. -refsToMarkdown :: WriterOptions -> Refs -> MD Doc +refsToMarkdown :: PandocMonad m => WriterOptions -> Refs -> MD m Doc refsToMarkdown opts refs = mapM (keyToMarkdown opts) refs >>= return . vcat -- | Return markdown representation of a reference key. -keyToMarkdown :: WriterOptions +keyToMarkdown :: PandocMonad m + => WriterOptions -> Ref - -> MD Doc + -> MD m Doc keyToMarkdown opts (label, (src, tit), attr) = do label' <- inlineListToMarkdown opts label let tit' = if null tit @@ -238,7 +240,7 @@ keyToMarkdown opts (label, (src, tit), attr) = do <> linkAttributes opts attr -- | Return markdown representation of notes. -notesToMarkdown :: WriterOptions -> [[Block]] -> MD Doc +notesToMarkdown :: PandocMonad m => WriterOptions -> [[Block]] -> MD m Doc notesToMarkdown opts notes = do n <- gets stNoteNum notes' <- mapM (\(num, note) -> noteToMarkdown opts num note) (zip [n..] notes) @@ -246,7 +248,7 @@ notesToMarkdown opts notes = do return $ vsep notes' -- | Return markdown representation of a note. -noteToMarkdown :: WriterOptions -> Int -> [Block] -> MD Doc +noteToMarkdown :: PandocMonad m => WriterOptions -> Int -> [Block] -> MD m Doc noteToMarkdown opts num blocks = do contents <- blockListToMarkdown opts blocks let num' = text $ writerIdentifierPrefix opts ++ show num @@ -279,7 +281,7 @@ escapeString opts = escapeStringUsing markdownEscapes "\\`*_[]#" -- | Construct table of contents from list of header blocks. -tableOfContents :: WriterOptions -> [Block] -> Doc +tableOfContents :: PandocMonad m => WriterOptions -> [Block] -> m Doc tableOfContents opts headers = let opts' = opts { writerIgnoreNotes = True } contents = BulletList $ map (elementToListItem opts) $ hierarchicalize headers @@ -334,7 +336,7 @@ beginsWithOrderedListMarker str = Left _ -> False Right _ -> True -notesAndRefs :: WriterOptions -> MD Doc +notesAndRefs :: PandocMonad m => WriterOptions -> MD m Doc notesAndRefs opts = do notes' <- reverse <$> gets stNotes >>= notesToMarkdown opts modify $ \s -> s { stNotes = [] } @@ -352,9 +354,10 @@ notesAndRefs opts = do endSpacing -- | Convert Pandoc block element to markdown. -blockToMarkdown :: WriterOptions -- ^ Options +blockToMarkdown :: PandocMonad m + => WriterOptions -- ^ Options -> Block -- ^ Block element - -> MD Doc + -> MD m Doc blockToMarkdown opts blk = local (\env -> env {envBlockLevel = envBlockLevel env + 1}) $ do doc <- blockToMarkdown' opts blk @@ -363,9 +366,10 @@ blockToMarkdown opts blk = then notesAndRefs opts >>= (\d -> return $ doc <> d) else return doc -blockToMarkdown' :: WriterOptions -- ^ Options - -> Block -- ^ Block element - -> MD Doc +blockToMarkdown' :: PandocMonad m + => WriterOptions -- ^ Options + -> Block -- ^ Block element + -> MD m Doc blockToMarkdown' _ Null = return empty blockToMarkdown' opts (Div attrs ils) = do contents <- blockListToMarkdown opts ils @@ -526,8 +530,8 @@ blockToMarkdown' opts t@(Table caption aligns widths headers rows) = do gridTable opts (all null headers) aligns widths rawHeaders rawRows | isEnabled Ext_raw_html opts -> fmap (id,) $ - return $ text $ writeHtmlString def - $ Pandoc nullMeta [t] + text <$> + (lift $ lift $ writeHtmlString def $ Pandoc nullMeta [t]) | otherwise -> return $ (id, text "[TABLE]") return $ nst $ tbl $$ blankline $$ caption'' $$ blankline blockToMarkdown' opts (BulletList items) = do @@ -550,7 +554,7 @@ blockToMarkdown' opts (DefinitionList items) = do contents <- inList $ mapM (definitionListItemToMarkdown opts) items return $ cat contents <> blankline -inList :: MD a -> MD a +inList :: Monad m => MD m a -> MD m a inList p = local (\env -> env {envInList = True}) p addMarkdownAttribute :: String -> String @@ -562,7 +566,7 @@ addMarkdownAttribute s = x /= "markdown"] _ -> s -pipeTable :: Bool -> [Alignment] -> [Doc] -> [[Doc]] -> MD Doc +pipeTable :: PandocMonad m => Bool -> [Alignment] -> [Doc] -> [[Doc]] -> MD m Doc pipeTable headless aligns rawHeaders rawRows = do let sp = text " " let blockFor AlignLeft x y = lblock (x + 2) (sp <> y) <> lblock 0 empty @@ -590,8 +594,8 @@ pipeTable headless aligns rawHeaders rawRows = do let body = vcat $ map torow rawRows return $ header $$ border $$ body -pandocTable :: WriterOptions -> Bool -> [Alignment] -> [Double] - -> [Doc] -> [[Doc]] -> MD Doc +pandocTable :: PandocMonad m => WriterOptions -> Bool -> [Alignment] -> [Double] + -> [Doc] -> [[Doc]] -> MD m Doc pandocTable opts headless aligns widths rawHeaders rawRows = do let isSimple = all (==0) widths let alignHeader alignment = case alignment of @@ -642,8 +646,8 @@ pandocTable opts headless aligns widths rawHeaders rawRows = do else border return $ head'' $$ underline $$ body $$ bottom -gridTable :: WriterOptions -> Bool -> [Alignment] -> [Double] - -> [Doc] -> [[Doc]] -> MD Doc +gridTable :: PandocMonad m => WriterOptions -> Bool -> [Alignment] -> [Double] + -> [Doc] -> [[Doc]] -> MD m Doc gridTable opts headless aligns widths headers' rawRows = do let numcols = length headers' let widths' = if all (==0) widths @@ -697,7 +701,7 @@ itemEndsWithTightList bs = _ -> False -- | Convert bullet list item (list of blocks) to markdown. -bulletListItemToMarkdown :: WriterOptions -> [Block] -> MD Doc +bulletListItemToMarkdown :: PandocMonad m => WriterOptions -> [Block] -> MD m Doc bulletListItemToMarkdown opts bs = do contents <- blockListToMarkdown opts bs let sps = replicate (writerTabStop opts - 2) ' ' @@ -709,10 +713,11 @@ bulletListItemToMarkdown opts bs = do return $ hang (writerTabStop opts) start $ contents' <> cr -- | Convert ordered list item (a list of blocks) to markdown. -orderedListItemToMarkdown :: WriterOptions -- ^ options +orderedListItemToMarkdown :: PandocMonad m + => WriterOptions -- ^ options -> String -- ^ list item marker -> [Block] -- ^ list item (list of blocks) - -> MD Doc + -> MD m Doc orderedListItemToMarkdown opts marker bs = do contents <- blockListToMarkdown opts bs let sps = case length marker - writerTabStop opts of @@ -726,9 +731,10 @@ orderedListItemToMarkdown opts marker bs = do return $ hang (writerTabStop opts) start $ contents' <> cr -- | Convert definition list item (label, list of blocks) to markdown. -definitionListItemToMarkdown :: WriterOptions +definitionListItemToMarkdown :: PandocMonad m + => WriterOptions -> ([Inline],[[Block]]) - -> MD Doc + -> MD m Doc definitionListItemToMarkdown opts (label, defs) = do labelText <- inlineListToMarkdown opts label defs' <- mapM (mapM (blockToMarkdown opts)) defs @@ -758,9 +764,10 @@ definitionListItemToMarkdown opts (label, defs) = do vsep (map vsep defs') <> blankline -- | Convert list of Pandoc block elements to markdown. -blockListToMarkdown :: WriterOptions -- ^ Options +blockListToMarkdown :: PandocMonad m + => WriterOptions -- ^ Options -> [Block] -- ^ List of block elements - -> MD Doc + -> MD m Doc blockListToMarkdown opts blocks = mapM (blockToMarkdown opts) (fixBlocks blocks) >>= return . cat -- insert comment between list and indented code block, or the @@ -787,7 +794,7 @@ blockListToMarkdown opts blocks = -- | Get reference for target; if none exists, create unique one and return. -- Prefer label if possible; otherwise, generate a unique key. -getReference :: Attr -> [Inline] -> Target -> MD [Inline] +getReference :: PandocMonad m => Attr -> [Inline] -> Target -> MD m [Inline] getReference attr label target = do st <- get case find (\(_,t,a) -> t == target && a == attr) (stRefs st) of @@ -805,7 +812,7 @@ getReference attr label target = do return label' -- | Convert list of Pandoc inline elements to markdown. -inlineListToMarkdown :: WriterOptions -> [Inline] -> MD Doc +inlineListToMarkdown :: PandocMonad m => WriterOptions -> [Inline] -> MD m Doc inlineListToMarkdown opts lst = do inlist <- asks envInList go (if inlist then avoidBadWrapsInList lst else lst) @@ -866,7 +873,7 @@ isRight (Right _) = True isRight (Left _) = False -- | Convert Pandoc inline element to markdown. -inlineToMarkdown :: WriterOptions -> Inline -> MD Doc +inlineToMarkdown :: PandocMonad m => WriterOptions -> Inline -> MD m Doc inlineToMarkdown opts (Span attrs ils) = do plain <- asks envPlain contents <- inlineListToMarkdown opts ils @@ -1053,7 +1060,7 @@ inlineToMarkdown opts lnk@(Link attr txt (src, tit)) | isEnabled Ext_raw_html opts && not (isEnabled Ext_link_attributes opts) && attr /= nullAttr = -- use raw HTML - return $ text $ trim $ writeHtmlString def $ Pandoc nullMeta [Plain [lnk]] + (text . trim) <$> (lift $ lift $ writeHtmlString def $ Pandoc nullMeta [Plain [lnk]]) | otherwise = do plain <- asks envPlain linktext <- inlineListToMarkdown opts txt @@ -1092,7 +1099,7 @@ inlineToMarkdown opts img@(Image attr alternate (source, tit)) | isEnabled Ext_raw_html opts && not (isEnabled Ext_link_attributes opts) && attr /= nullAttr = -- use raw HTML - return $ text $ trim $ writeHtmlString def $ Pandoc nullMeta [Plain [img]] + (text . trim) <$> (lift $ lift $ writeHtmlString def $ Pandoc nullMeta [Plain [img]]) | otherwise = do plain <- asks envPlain let txt = if null alternate || alternate == [Str source] diff --git a/src/Text/Pandoc/Writers/MediaWiki.hs b/src/Text/Pandoc/Writers/MediaWiki.hs index 95b649dd2..774139c43 100644 --- a/src/Text/Pandoc/Writers/MediaWiki.hs +++ b/src/Text/Pandoc/Writers/MediaWiki.hs @@ -42,6 +42,7 @@ import Data.List ( intersect, intercalate ) import Network.URI ( isURI ) import Control.Monad.Reader import Control.Monad.State +import Text.Pandoc.Class (PandocMonad) data WriterState = WriterState { stNotes :: Bool -- True if there are notes @@ -57,8 +58,8 @@ data WriterReader = WriterReader { type MediaWikiWriter = ReaderT WriterReader (State WriterState) -- | Convert Pandoc to MediaWiki. -writeMediaWiki :: WriterOptions -> Pandoc -> String -writeMediaWiki opts document = +writeMediaWiki :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeMediaWiki opts document = return $ let initialState = WriterState { stNotes = False, stOptions = opts } env = WriterReader { options = opts, listLevel = [], useTags = False } in evalState (runReaderT (pandocToMediaWiki document) env) initialState diff --git a/src/Text/Pandoc/Writers/Native.hs b/src/Text/Pandoc/Writers/Native.hs index 87e23aeeb..2421fd94d 100644 --- a/src/Text/Pandoc/Writers/Native.hs +++ b/src/Text/Pandoc/Writers/Native.hs @@ -34,6 +34,7 @@ import Text.Pandoc.Options ( WriterOptions(..), WrapOption(..) ) import Data.List ( intersperse ) import Text.Pandoc.Definition import Text.Pandoc.Pretty +import Text.Pandoc.Class (PandocMonad) prettyList :: [Doc] -> Doc prettyList ds = @@ -66,8 +67,8 @@ prettyBlock (Div attr blocks) = prettyBlock block = text $ show block -- | Prettyprint Pandoc document. -writeNative :: WriterOptions -> Pandoc -> String -writeNative opts (Pandoc meta blocks) = +writeNative :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeNative opts (Pandoc meta blocks) = return $ let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index 8013763c2..02e84e26e 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -83,7 +83,7 @@ pandocToODT opts doc@(Pandoc meta _) = do -- handle formulas and pictures -- picEntriesRef <- P.newIORef ([] :: [Entry]) doc' <- walkM (transformPicMath opts) $ walk fixDisplayMath doc - let newContents = writeOpenDocument opts{writerWrapText = WrapNone} doc' + newContents <- lift $ writeOpenDocument opts{writerWrapText = WrapNone} doc' epochtime <- floor `fmap` (lift P.getPOSIXTime) let contentEntry = toEntry "content.xml" epochtime $ fromStringLazy newContents diff --git a/src/Text/Pandoc/Writers/OPML.hs b/src/Text/Pandoc/Writers/OPML.hs index 20c2c5cbc..ce415264d 100644 --- a/src/Text/Pandoc/Writers/OPML.hs +++ b/src/Text/Pandoc/Writers/OPML.hs @@ -40,29 +40,30 @@ import Text.Pandoc.Writers.Markdown (writeMarkdown) import Text.Pandoc.Pretty import Text.Pandoc.Compat.Time import qualified Text.Pandoc.Builder as B +import Text.Pandoc.Class (PandocMonad) -- | Convert Pandoc document to string in OPML format. -writeOPML :: WriterOptions -> Pandoc -> String -writeOPML opts (Pandoc meta blocks) = +writeOPML :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeOPML opts (Pandoc meta blocks) = do let elements = hierarchicalize blocks colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing meta' = B.setMeta "date" (B.str $ convertDate $ docDate meta) meta - Just metadata = metaToJSON opts - (Just . writeMarkdown def . Pandoc nullMeta) - (Just . trimr . writeMarkdown def . Pandoc nullMeta . - (\ils -> [Plain ils])) - meta' - main = render colwidth $ vcat (map (elementToOPML opts) elements) - context = defField "body" main metadata - in case writerTemplate opts of - Nothing -> main - Just tpl -> renderTemplate' tpl context + metadata <- metaToJSON opts + (writeMarkdown def . Pandoc nullMeta) + (\ils -> trimr <$> (writeMarkdown def $ Pandoc nullMeta [Plain ils])) + meta' + main <- (render colwidth . vcat) <$> (mapM (elementToOPML opts) elements) + let context = defField "body" main metadata + return $ case writerTemplate opts of + Nothing -> main + Just tpl -> renderTemplate' tpl context -writeHtmlInlines :: [Inline] -> String -writeHtmlInlines ils = trim $ writeHtmlString def - $ Pandoc nullMeta [Plain ils] + +writeHtmlInlines :: PandocMonad m => [Inline] -> m String +writeHtmlInlines ils = + trim <$> (writeHtmlString def $ Pandoc nullMeta [Plain ils]) -- date format: RFC 822: Thu, 14 Jul 2005 23:41:05 GMT showDateTimeRFC822 :: UTCTime -> String @@ -78,17 +79,18 @@ convertDate ils = maybe "" showDateTimeRFC822 $ defaultTimeLocale "%F" =<< (normalizeDate $ stringify ils) -- | Convert an Element to OPML. -elementToOPML :: WriterOptions -> Element -> Doc -elementToOPML _ (Blk _) = empty -elementToOPML opts (Sec _ _num _ title elements) = +elementToOPML :: PandocMonad m => WriterOptions -> Element -> m Doc +elementToOPML _ (Blk _) = return empty +elementToOPML opts (Sec _ _num _ title elements) = do let isBlk (Blk _) = True isBlk _ = False fromBlk (Blk x) = x fromBlk _ = error "fromBlk called on non-block" (blocks, rest) = span isBlk elements - attrs = [("text", writeHtmlInlines title)] ++ - [("_note", writeMarkdown def (Pandoc nullMeta - (map fromBlk blocks))) - | not (null blocks)] - in inTags True "outline" attrs $ - vcat (map (elementToOPML opts) rest) + htmlIls <- writeHtmlInlines title + md <- if null blocks + then return [] + else writeMarkdown def $ Pandoc nullMeta $ map fromBlk blocks + let attrs = [("text", htmlIls)] ++ [("_note", md)] + o <- mapM (elementToOPML opts) rest + return $ inTags True "outline" attrs $ vcat o diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index 444a09587..903c94828 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -47,6 +47,7 @@ import qualified Data.Map as Map import Text.Pandoc.Writers.Shared import Data.List (sortBy) import Data.Ord (comparing) +import Text.Pandoc.Class (PandocMonad) -- | Auxiliary function to convert Plain block to Para. plainToPara :: Block -> Block @@ -190,8 +191,8 @@ handleSpaces s rm [] = empty -- | Convert Pandoc document to string in OpenDocument format. -writeOpenDocument :: WriterOptions -> Pandoc -> String -writeOpenDocument opts (Pandoc meta blocks) = +writeOpenDocument :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeOpenDocument opts (Pandoc meta blocks) = return $ let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index 330f24b0b..febb2e98f 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -42,6 +42,7 @@ import Text.Pandoc.Templates (renderTemplate') import Data.Char ( isAlphaNum, toLower ) import Data.List ( isPrefixOf, intersect, intersperse, partition, transpose ) import Control.Monad.State +import Text.Pandoc.Class (PandocMonad) data WriterState = WriterState { stNotes :: [[Block]] @@ -52,8 +53,8 @@ data WriterState = } -- | Convert Pandoc to Org. -writeOrg :: WriterOptions -> Pandoc -> String -writeOrg opts document = +writeOrg :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeOrg opts document = return $ let st = WriterState { stNotes = [], stLinks = False, stImages = False, stHasMath = False, stOptions = opts } diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index c170889cc..438407cce 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -44,6 +44,7 @@ import Network.URI (isURI) import Text.Pandoc.Pretty import Control.Monad.State import Data.Char (isSpace, toLower) +import Text.Pandoc.Class (PandocMonad) type Refs = [([Inline], Target)] @@ -58,8 +59,8 @@ data WriterState = } -- | Convert Pandoc to RST. -writeRST :: WriterOptions -> Pandoc -> String -writeRST opts document = +writeRST :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeRST opts document = return $ let st = WriterState { stNotes = [], stLinks = [], stImages = [], stHasMath = False, stHasRawTeX = False, stOptions = opts, diff --git a/src/Text/Pandoc/Writers/TEI.hs b/src/Text/Pandoc/Writers/TEI.hs index 27a2819a0..0a22ae085 100644 --- a/src/Text/Pandoc/Writers/TEI.hs +++ b/src/Text/Pandoc/Writers/TEI.hs @@ -41,6 +41,7 @@ import Text.Pandoc.Highlighting ( languages, languagesByExtension ) import Text.Pandoc.Pretty import Text.Pandoc.ImageSize import qualified Text.Pandoc.Builder as B +import Text.Pandoc.Class ( PandocMonad ) -- | Convert list of authors to a docbook <author> section authorToTEI :: WriterOptions -> [Inline] -> B.Inlines @@ -53,8 +54,8 @@ authorToTEI opts name' = inTagsSimple "author" (text $ escapeStringForXML name) -- | Convert Pandoc document to string in Docbook format. -writeTEI :: WriterOptions -> Pandoc -> String -writeTEI opts (Pandoc meta blocks) = +writeTEI :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeTEI opts (Pandoc meta blocks) = return $ let elements = hierarchicalize blocks colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts diff --git a/src/Text/Pandoc/Writers/Texinfo.hs b/src/Text/Pandoc/Writers/Texinfo.hs index 993e6fbfd..fac7f02ab 100644 --- a/src/Text/Pandoc/Writers/Texinfo.hs +++ b/src/Text/Pandoc/Writers/Texinfo.hs @@ -44,6 +44,7 @@ import Text.Pandoc.ImageSize import Network.URI ( isURI, unEscapeString ) import System.FilePath import qualified Data.Set as Set +import Text.Pandoc.Class ( PandocMonad ) data WriterState = WriterState { stStrikeout :: Bool -- document contains strikeout @@ -60,8 +61,8 @@ data WriterState = -} -- | Convert Pandoc to Texinfo. -writeTexinfo :: WriterOptions -> Pandoc -> String -writeTexinfo options document = +writeTexinfo :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeTexinfo options document = return $ evalState (pandocToTexinfo options $ wrapTop document) $ WriterState { stStrikeout = False, stSuperscript = False, stEscapeComma = False, stSubscript = False, diff --git a/src/Text/Pandoc/Writers/Textile.hs b/src/Text/Pandoc/Writers/Textile.hs index 4283e29cc..9691b7705 100644 --- a/src/Text/Pandoc/Writers/Textile.hs +++ b/src/Text/Pandoc/Writers/Textile.hs @@ -41,6 +41,7 @@ import Text.Pandoc.XML ( escapeStringForXML ) import Data.List ( intercalate ) import Control.Monad.State import Data.Char ( isSpace ) +import Text.Pandoc.Class ( PandocMonad ) data WriterState = WriterState { stNotes :: [String] -- Footnotes @@ -50,8 +51,8 @@ data WriterState = WriterState { } -- | Convert Pandoc to Textile. -writeTextile :: WriterOptions -> Pandoc -> String -writeTextile opts document = +writeTextile :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeTextile opts document = return $ evalState (pandocToTextile opts document) WriterState { stNotes = [], stListLevel = [], stStartNum = Nothing, stUseTags = False } diff --git a/src/Text/Pandoc/Writers/ZimWiki.hs b/src/Text/Pandoc/Writers/ZimWiki.hs index 56a5d5455..f15b290e4 100644 --- a/src/Text/Pandoc/Writers/ZimWiki.hs +++ b/src/Text/Pandoc/Writers/ZimWiki.hs @@ -45,6 +45,7 @@ import Network.URI ( isURI ) import Control.Monad ( zipWithM ) import Control.Monad.State ( modify, State, get, evalState ) --import Control.Monad.Reader ( ReaderT, runReaderT, ask, local ) +import Text.Pandoc.Class ( PandocMonad ) data WriterState = WriterState { stItemNum :: Int, @@ -55,8 +56,8 @@ instance Default WriterState where def = WriterState { stItemNum = 1, stIndent = "" } -- | Convert Pandoc to ZimWiki. -writeZimWiki :: WriterOptions -> Pandoc -> String -writeZimWiki opts document = evalState (pandocToZimWiki opts document) (WriterState 1 "") +writeZimWiki :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeZimWiki opts document = return $ evalState (pandocToZimWiki opts document) (WriterState 1 "") -- | Return ZimWiki representation of document. pandocToZimWiki :: WriterOptions -> Pandoc -> State WriterState String |