From 30b3412857fc604656aac53d57730ad2442a3599 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Fri, 11 Nov 2016 13:07:50 +0100 Subject: Added page breaks into Pandoc. This requires an updated version of pandoc-types that introduces PageBreak definition. Not that this initial commit only introduces ODT pagebreaks and distinguishes for it page breaks before, after, or both, the paragraph, as read from the style definition. --- src/Text/Pandoc/Readers/Odt/ContentReader.hs | 19 +++++++++++++++---- src/Text/Pandoc/Readers/Odt/StyleReader.hs | 27 +++++++++++++++++++++------ src/Text/Pandoc/Writers/OpenDocument.hs | 28 +++++++++++++++++++++++++--- 3 files changed, 61 insertions(+), 13 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Odt/ContentReader.hs b/src/Text/Pandoc/Readers/Odt/ContentReader.hs index 2672b01ef..0df86e2a5 100644 --- a/src/Text/Pandoc/Readers/Odt/ContentReader.hs +++ b/src/Text/Pandoc/Readers/Odt/ContentReader.hs @@ -381,9 +381,9 @@ getParaModifier :: Style -> ParaModifier getParaModifier Style{..} | Just props <- paraProperties styleProperties , isBlockQuote (indentation props) (margin_left props) - = blockQuote + = pageBreakMaybe (paraProperties styleProperties) blockQuote | otherwise - = id + = pageBreakMaybe (paraProperties styleProperties) id where isBlockQuote mIndent mMargin | LengthValueMM indent <- mIndent @@ -408,7 +408,19 @@ getParaModifier Style{..} | Just props <- paraProperties styleProperties | otherwise = False - + pageBreakMaybe :: Maybe ParaProperties -> ParaModifier -> ParaModifier + pageBreakMaybe (Just props) modifier = insertPageBreak (page_break props) modifier + pageBreakMaybe Nothing modifier = modifier + + insertPageBreak :: ParaBreak -> ParaModifier -> ParaModifier + insertPageBreak PageAfter modifier = + \x -> (fromList (toList (modifier x) ++ [Para (toList pageBreak)])) + insertPageBreak PageBefore modifier = + \x -> (fromList (Para (toList pageBreak) : toList (modifier x))) + insertPageBreak PageBoth modifier = + \x -> (fromList ((Para (toList pageBreak) : toList (modifier x)) ++ [Para (toList pageBreak)])) + insertPageBreak _ modifier = + modifier -- constructPara :: OdtReaderSafe Blocks Blocks -> OdtReaderSafe Blocks Blocks constructPara reader = proc blocks -> do @@ -894,7 +906,6 @@ read_reference_ref = matchingElement NsText "reference-ref" $ maybeInAnchorRef <<< matchChildContent [] read_plain_text - ---------------------- -- Entry point ---------------------- diff --git a/src/Text/Pandoc/Readers/Odt/StyleReader.hs b/src/Text/Pandoc/Readers/Odt/StyleReader.hs index 26ba6df82..cd31f50a8 100644 --- a/src/Text/Pandoc/Readers/Odt/StyleReader.hs +++ b/src/Text/Pandoc/Readers/Odt/StyleReader.hs @@ -43,6 +43,7 @@ module Text.Pandoc.Readers.Odt.StyleReader , TextProperties (..) , ParaProperties (..) , VerticalTextPosition (..) +, ParaBreak (..) , ListItemNumberFormat (..) , ListLevel , ListStyle (..) @@ -273,6 +274,7 @@ instance Default TextProperties where data ParaProperties = PropP { paraNumbering :: ParaNumbering , indentation :: LengthOrPercent , margin_left :: LengthOrPercent + , page_break :: ParaBreak } deriving ( Eq, Show ) @@ -280,6 +282,7 @@ instance Default ParaProperties where def = PropP { paraNumbering = NumberingNone , indentation = def , margin_left = def + , page_break = AutoNone } ---- @@ -314,6 +317,9 @@ instance Lookupable UnderlineMode where data ParaNumbering = NumberingNone | NumberingKeep | NumberingRestart Int deriving ( Eq, Show ) +data ParaBreak = AutoNone | PageBefore | PageAfter | PageBoth + deriving ( Eq, Show ) + data LengthOrPercent = LengthValueMM Int | PercentValue Int deriving ( Eq, Show ) @@ -533,16 +539,20 @@ readLineMode modeAttr styleAttr = proc x -> do readParaProperties :: StyleReader _x ParaProperties readParaProperties = executeIn NsStyle "paragraph-properties" $ liftAsSuccess - ( liftA3 PropP + ( liftA4 PropP ( liftA2 readNumbering - ( isSet' NsText "number-lines" ) - ( readAttr' NsText "line-number" ) + ( isSet' NsText "number-lines" ) + ( readAttr' NsText "line-number" ) ) ( liftA2 readIndentation - ( isSetWithDefault NsStyle "auto-text-indent" False ) - ( getAttr NsXSL_FO "text-indent" ) + ( isSetWithDefault NsStyle "auto-text-indent" False ) + ( getAttr NsXSL_FO "text-indent" ) + ) + ( getAttr NsXSL_FO "margin-left" ) + ( liftA2 readPageBreak + ( findAttrWithDefault NsXSL_FO "break-before" "auto" ) + ( findAttrWithDefault NsXSL_FO "break-after" "auto" ) ) - ( getAttr NsXSL_FO "margin-left" ) ) where readNumbering (Just True) (Just n) = NumberingRestart n readNumbering (Just True) _ = NumberingKeep @@ -551,6 +561,11 @@ readParaProperties = readIndentation False indent = indent readIndentation True _ = def + readPageBreak "page" "page" = PageBoth + readPageBreak "page" _ = PageBefore + readPageBreak _ "page" = PageAfter + readPageBreak _ _ = AutoNone + ---- -- List styles ---- diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index 8f0e037c5..444a09587 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -36,6 +36,7 @@ import Text.Pandoc.XML import Text.Pandoc.Shared (linesToPara) import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Readers.TeXMath +import Text.Pandoc.Readers.Odt.StyleReader import Text.Pandoc.Pretty import Text.Printf ( printf ) import Control.Arrow ( (***), (>>>) ) @@ -307,9 +308,7 @@ blockToOpenDocument o bs else inParagraphTags =<< inlinesToOpenDocument o b | Para [Image attr c (s,'f':'i':'g':':':t)] <- bs = figure attr c s t - | Para b <- bs = if null b - then return empty - else inParagraphTags =<< inlinesToOpenDocument o b + | Para b <- bs = paragraph b | LineBlock b <- bs = blockToOpenDocument o $ linesToPara b | Div _ xs <- bs = blocksToOpenDocument o xs | Header i _ b <- bs = setFirstPara >> @@ -370,6 +369,22 @@ blockToOpenDocument o bs captionDoc <- withParagraphStyle o "FigureCaption" [Para caption] return $ imageDoc $$ captionDoc + endsWithPageBreak [] = False + endsWithPageBreak [PageBreak] = True + endsWithPageBreak (_ : xs) = endsWithPageBreak xs + + paragraph :: [Inline] -> State WriterState Doc + paragraph [] = return empty + paragraph (PageBreak : rest) | endsWithPageBreak rest = paraWithBreak PageBoth rest + paragraph (PageBreak : rest) = paraWithBreak PageBefore rest + paragraph inlines | endsWithPageBreak inlines = paraWithBreak PageAfter inlines + paragraph inlines = inParagraphTags =<< inlinesToOpenDocument o inlines + + paraWithBreak :: ParaBreak -> [Inline] -> State WriterState Doc + paraWithBreak breakKind bs = do + pn <- paraBreakStyle breakKind + withParagraphStyle o ("P" ++ show pn) [Para bs] + colHeadsToOpenDocument :: WriterOptions -> String -> [String] -> [[Block]] -> State WriterState Doc colHeadsToOpenDocument o tn ns hs = inTagsIndented "table:table-header-rows" . inTagsIndented "table:table-row" . vcat <$> @@ -562,6 +577,13 @@ paraStyle attrs = do addParaStyle $ inTags True "style:style" (styleAttr ++ attrs) paraProps return pn +paraBreakStyle :: ParaBreak -> State WriterState Int +paraBreakStyle PageBefore = paraStyle "Text_20_body" [("fo:break-before", "page")] +paraBreakStyle PageAfter = paraStyle "Text_20_body" [("fo:break-after", "page")] +paraBreakStyle PageBoth = paraStyle "Text_20_body" [("fo:break-before", "page"), ("fo:break-after", "page")] +paraBreakStyle AutoNone = paraStyle "Text_20_body" [] + + paraListStyle :: Int -> State WriterState Int paraListStyle l = paraStyle [("style:parent-style-name","Text_20_body") -- cgit v1.2.3 From a6b469c02b3c21cfc1b5169ea3e75b7388f55691 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Fri, 11 Nov 2016 13:09:49 +0100 Subject: Adds support for pagebreaks (when it makes sense) Update all writers to take into account page breaks. A straightforwad, far from complete, implementation of page breaks in selected writers. Readers will have to follow in the future as well. --- src/Text/Pandoc/Writers/AsciiDoc.hs | 3 ++- src/Text/Pandoc/Writers/CommonMark.hs | 1 + src/Text/Pandoc/Writers/ConTeXt.hs | 1 + src/Text/Pandoc/Writers/Custom.hs | 2 ++ src/Text/Pandoc/Writers/Docbook.hs | 3 +++ src/Text/Pandoc/Writers/Docx.hs | 9 ++++++++- src/Text/Pandoc/Writers/DokuWiki.hs | 4 +++- src/Text/Pandoc/Writers/FB2.hs | 2 ++ src/Text/Pandoc/Writers/HTML.hs | 1 + src/Text/Pandoc/Writers/Haddock.hs | 3 ++- src/Text/Pandoc/Writers/ICML.hs | 1 + src/Text/Pandoc/Writers/LaTeX.hs | 1 + src/Text/Pandoc/Writers/Man.hs | 3 ++- src/Text/Pandoc/Writers/Markdown.hs | 3 ++- src/Text/Pandoc/Writers/MediaWiki.hs | 4 +++- src/Text/Pandoc/Writers/Org.hs | 3 ++- src/Text/Pandoc/Writers/RST.hs | 1 + src/Text/Pandoc/Writers/RTF.hs | 3 ++- src/Text/Pandoc/Writers/TEI.hs | 1 + src/Text/Pandoc/Writers/Texinfo.hs | 2 ++ src/Text/Pandoc/Writers/Textile.hs | 4 +++- src/Text/Pandoc/Writers/ZimWiki.hs | 4 +++- 22 files changed, 48 insertions(+), 11 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/AsciiDoc.hs b/src/Text/Pandoc/Writers/AsciiDoc.hs index e9d3dccf1..88fab171f 100644 --- a/src/Text/Pandoc/Writers/AsciiDoc.hs +++ b/src/Text/Pandoc/Writers/AsciiDoc.hs @@ -411,7 +411,8 @@ inlineToAsciiDoc _ (Math DisplayMath str) = inlineToAsciiDoc _ (RawInline f s) | f == "asciidoc" = return $ text s | otherwise = return empty -inlineToAsciiDoc _ (LineBreak) = return $ " +" <> cr +inlineToAsciiDoc _ LineBreak = return $ " +" <> cr +inlineToAsciiDoc _ PageBreak = return empty inlineToAsciiDoc _ Space = return space inlineToAsciiDoc opts SoftBreak = case writerWrapText opts of diff --git a/src/Text/Pandoc/Writers/CommonMark.hs b/src/Text/Pandoc/Writers/CommonMark.hs index 88a92eb47..e0591de83 100644 --- a/src/Text/Pandoc/Writers/CommonMark.hs +++ b/src/Text/Pandoc/Writers/CommonMark.hs @@ -139,6 +139,7 @@ inlineToNodes :: Inline -> [Node] -> [Node] inlineToNodes (Str s) = (node (TEXT (T.pack s)) [] :) inlineToNodes Space = (node (TEXT (T.pack " ")) [] :) inlineToNodes LineBreak = (node LINEBREAK [] :) +inlineToNodes PageBreak = id inlineToNodes SoftBreak = (node SOFTBREAK [] :) inlineToNodes (Emph xs) = (node EMPH (inlinesToNodes xs) :) inlineToNodes (Strong xs) = (node STRONG (inlinesToNodes xs) :) diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index c663c75ce..ee2cc3f34 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -346,6 +346,7 @@ inlineToConTeXt SoftBreak = do WrapAuto -> space WrapNone -> space WrapPreserve -> cr +inlineToConTeXt PageBreak = return empty inlineToConTeXt Space = return space -- Handle HTML-like internal document references to sections inlineToConTeXt (Link _ txt (('#' : ref), _)) = do diff --git a/src/Text/Pandoc/Writers/Custom.hs b/src/Text/Pandoc/Writers/Custom.hs index cf641dcd6..371dd21c3 100644 --- a/src/Text/Pandoc/Writers/Custom.hs +++ b/src/Text/Pandoc/Writers/Custom.hs @@ -310,6 +310,8 @@ inlineToCustom lua (RawInline format str) = inlineToCustom lua (LineBreak) = callfunc lua "LineBreak" +inlineToCustom lua (PageBreak) = callfunc lua "PageBreak" + inlineToCustom lua (Link attr txt (src,tit)) = callfunc lua "Link" txt src tit (attrToMap attr) diff --git a/src/Text/Pandoc/Writers/Docbook.hs b/src/Text/Pandoc/Writers/Docbook.hs index 44f96d700..5c03d449d 100644 --- a/src/Text/Pandoc/Writers/Docbook.hs +++ b/src/Text/Pandoc/Writers/Docbook.hs @@ -356,6 +356,9 @@ inlineToDocbook opts (Math t str) inlineToDocbook _ (RawInline f x) | f == "html" || f == "docbook" = text x | otherwise = empty inlineToDocbook _ LineBreak = text "\n" +-- currently ignore, would require the option to add custom +-- styles to the document +inlineToDocbook _ PageBreak = empty inlineToDocbook _ Space = space -- because we use \n for LineBreak, we can't do soft breaks: inlineToDocbook _ SoftBreak = space diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 3fc5d22a2..d425bbbca 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -1100,6 +1100,7 @@ inlineToOpenXML' opts (Strikeout lst) = withTextProp (mknode "w:strike" [] ()) $ inlinesToOpenXML opts lst inlineToOpenXML' _ LineBreak = return [br] +inlineToOpenXML' _ PageBreak = return [pageBreak] inlineToOpenXML' _ (RawInline f str) | f == Format "openxml" = return [ x | Elem x <- parseXML str ] | otherwise = return [] @@ -1247,7 +1248,13 @@ inlineToOpenXML' opts (Image attr alt (src, title)) = do return [imgElt] br :: Element -br = mknode "w:r" [] [mknode "w:br" [("w:type","textWrapping")] () ] +br = breakElement "textWrapping" + +pageBreak :: Element +pageBreak = breakElement "page" + +breakElement :: String -> Element +breakElement kind = mknode "w:r" [] [mknode "w:br" [("w:type",kind)] () ] -- Word will insert these footnotes into the settings.xml file -- (whether or not they're visible in the document). If they're in the diff --git a/src/Text/Pandoc/Writers/DokuWiki.hs b/src/Text/Pandoc/Writers/DokuWiki.hs index 7459f1b42..c90dc9078 100644 --- a/src/Text/Pandoc/Writers/DokuWiki.hs +++ b/src/Text/Pandoc/Writers/DokuWiki.hs @@ -465,7 +465,9 @@ inlineToDokuWiki _ (RawInline f str) | f == Format "html" = return $ "" ++ str ++ "" | otherwise = return "" -inlineToDokuWiki _ (LineBreak) = return "\\\\\n" +inlineToDokuWiki _ LineBreak = return "\\\\\n" + +inlineToDokuWiki _ PageBreak = return mempty inlineToDokuWiki opts SoftBreak = case writerWrapText opts of diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index 5538ca061..8c4817ac6 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -443,6 +443,7 @@ toXml (Code _ s) = return [el "code" s] toXml Space = return [txt " "] toXml SoftBreak = return [txt " "] toXml LineBreak = return [el "empty-line" ()] +toXml PageBreak = return [] toXml (Math _ formula) = insertMath InlineImage formula toXml (RawInline _ _) = return [] -- raw TeX and raw HTML are suppressed toXml (Link _ text (url,ttl)) = do @@ -574,6 +575,7 @@ plain (Code _ s) = s plain Space = " " plain SoftBreak = " " plain LineBreak = "\n" +plain PageBreak = "\n" plain (Math _ s) = s plain (RawInline _ s) = s plain (Link _ text (url,_)) = concat (map plain text ++ [" <", url, ">"]) diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 3c8c264d2..e0b0234fb 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -705,6 +705,7 @@ inlineToHtml opts inline = WrapPreserve -> preEscapedString "\n" (LineBreak) -> return $ (if writerHtml5 opts then H5.br else H.br) <> strToHtml "\n" + (PageBreak) -> return mempty (Span (id',classes,kvs) ils) -> inlineListToHtml opts ils >>= return . addAttrs opts attr' . H.span diff --git a/src/Text/Pandoc/Writers/Haddock.hs b/src/Text/Pandoc/Writers/Haddock.hs index 29fdafe15..4e93cc4e4 100644 --- a/src/Text/Pandoc/Writers/Haddock.hs +++ b/src/Text/Pandoc/Writers/Haddock.hs @@ -326,7 +326,8 @@ inlineToHaddock _ (RawInline f str) | f == "haddock" = return $ text str | otherwise = return empty -- no line break in haddock (see above on CodeBlock) -inlineToHaddock _ (LineBreak) = return cr +inlineToHaddock _ LineBreak = return cr +inlineToHaddock _ PageBreak = return empty inlineToHaddock opts SoftBreak = case writerWrapText opts of WrapAuto -> return space diff --git a/src/Text/Pandoc/Writers/ICML.hs b/src/Text/Pandoc/Writers/ICML.hs index 8f0d21cf5..e2c123fc2 100644 --- a/src/Text/Pandoc/Writers/ICML.hs +++ b/src/Text/Pandoc/Writers/ICML.hs @@ -432,6 +432,7 @@ inlineToICML opts style SoftBreak = WrapNone -> charStyle style space WrapPreserve -> charStyle style cr inlineToICML _ style LineBreak = charStyle style $ text lineSeparator +inlineToICML _ _ PageBreak = return empty inlineToICML opts style (Math mt str) = cat <$> mapM (inlineToICML opts style) (texMathToInlines mt str) inlineToICML _ _ (RawInline f str) diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 88934eb44..50e99fe15 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -960,6 +960,7 @@ inlineToLaTeX SoftBreak = do WrapAuto -> return space WrapNone -> return space WrapPreserve -> return cr +inlineToLaTeX PageBreak = return $ "\\clearpage{}" inlineToLaTeX Space = return space inlineToLaTeX (Link _ txt ('#':ident, _)) = do contents <- inlineListToLaTeX txt diff --git a/src/Text/Pandoc/Writers/Man.hs b/src/Text/Pandoc/Writers/Man.hs index 98b08b08b..304995ec8 100644 --- a/src/Text/Pandoc/Writers/Man.hs +++ b/src/Text/Pandoc/Writers/Man.hs @@ -342,8 +342,9 @@ inlineToMan opts (Math DisplayMath str) = do inlineToMan _ (RawInline f str) | f == Format "man" = return $ text str | otherwise = return empty -inlineToMan _ (LineBreak) = return $ +inlineToMan _ LineBreak = return $ cr <> text ".PD 0" $$ text ".P" $$ text ".PD" <> cr +inlineToMan _ PageBreak = return empty inlineToMan _ SoftBreak = return space inlineToMan _ Space = return space inlineToMan opts (Link _ txt (src, _)) = do diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index e3bb3eea0..f9c7c326e 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -345,7 +345,7 @@ notesAndRefs opts = do if | writerReferenceLocation opts == EndOfDocument -> empty | isEmpty notes' && isEmpty refs' -> empty | otherwise -> blankline - + return $ (if isEmpty notes' then empty else blankline <> notes') <> (if isEmpty refs' then empty else blankline <> refs') <> @@ -1018,6 +1018,7 @@ inlineToMarkdown opts SoftBreak = do WrapNone -> space' WrapAuto -> space' WrapPreserve -> cr +inlineToMarkdown _ PageBreak = return empty inlineToMarkdown opts (Cite [] lst) = inlineListToMarkdown opts lst inlineToMarkdown opts (Cite (c:cs) lst) | not (isEnabled Ext_citations opts) = inlineListToMarkdown opts lst diff --git a/src/Text/Pandoc/Writers/MediaWiki.hs b/src/Text/Pandoc/Writers/MediaWiki.hs index 78d4651e7..95b649dd2 100644 --- a/src/Text/Pandoc/Writers/MediaWiki.hs +++ b/src/Text/Pandoc/Writers/MediaWiki.hs @@ -402,7 +402,9 @@ inlineToMediaWiki (RawInline f str) | f == Format "html" = return str | otherwise = return "" -inlineToMediaWiki (LineBreak) = return "
\n" +inlineToMediaWiki LineBreak = return "
\n" + +inlineToMediaWiki PageBreak = return mempty inlineToMediaWiki SoftBreak = do wrapText <- gets (writerWrapText . stOptions) diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index 4302459cc..330f24b0b 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -349,7 +349,8 @@ inlineToOrg (RawInline f@(Format f') str) = return $ if isRawFormat f then text str else "@@" <> text f' <> ":" <> text str <> "@@" -inlineToOrg (LineBreak) = return (text "\\\\" <> cr) +inlineToOrg LineBreak = return (text "\\\\" <> cr) +inlineToOrg PageBreak = return empty inlineToOrg Space = return space inlineToOrg SoftBreak = do wrapText <- gets (writerWrapText . stOptions) diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index 064434483..c170889cc 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -461,6 +461,7 @@ inlineToRST SoftBreak = do WrapPreserve -> return cr WrapAuto -> return space WrapNone -> return space +inlineToRST PageBreak = return $ ".. pagebreak::" -- autolink inlineToRST (Link _ [Str str] (src, _)) | isURI src && diff --git a/src/Text/Pandoc/Writers/RTF.hs b/src/Text/Pandoc/Writers/RTF.hs index 8f942b4d0..6ca749a10 100644 --- a/src/Text/Pandoc/Writers/RTF.hs +++ b/src/Text/Pandoc/Writers/RTF.hs @@ -350,8 +350,9 @@ inlineToRTF (Cite _ lst) = inlineListToRTF lst inlineToRTF (RawInline f str) | f == Format "rtf" = str | otherwise = "" -inlineToRTF (LineBreak) = "\\line " +inlineToRTF LineBreak = "\\line " inlineToRTF SoftBreak = " " +inlineToRTF PageBreak = "\\page " inlineToRTF Space = " " inlineToRTF (Link _ text (src, _)) = "{\\field{\\*\\fldinst{HYPERLINK \"" ++ (codeStringToRTF src) ++ diff --git a/src/Text/Pandoc/Writers/TEI.hs b/src/Text/Pandoc/Writers/TEI.hs index 9bd23ac3b..27a2819a0 100644 --- a/src/Text/Pandoc/Writers/TEI.hs +++ b/src/Text/Pandoc/Writers/TEI.hs @@ -284,6 +284,7 @@ inlineToTEI _ (Math t str) = inlineToTEI _ (RawInline f x) | f == "tei" = text x | otherwise = empty inlineToTEI _ LineBreak = selfClosingTag "lb" [] +inlineToTEI _ PageBreak = selfClosingTag "pb" [] inlineToTEI _ Space = space -- because we use \n for LineBreak, we can't do soft breaks: inlineToTEI _ SoftBreak = space diff --git a/src/Text/Pandoc/Writers/Texinfo.hs b/src/Text/Pandoc/Writers/Texinfo.hs index f2b9aa15f..993e6fbfd 100644 --- a/src/Text/Pandoc/Writers/Texinfo.hs +++ b/src/Text/Pandoc/Writers/Texinfo.hs @@ -437,6 +437,8 @@ inlineToTexinfo SoftBreak = do WrapPreserve -> return cr inlineToTexinfo Space = return space +inlineToTexinfo PageBreak = return $ text "@page" + inlineToTexinfo (Link _ txt (src@('#':_), _)) = do contents <- escapeCommas $ inlineListToTexinfo txt return $ text "@ref" <> diff --git a/src/Text/Pandoc/Writers/Textile.hs b/src/Text/Pandoc/Writers/Textile.hs index f73876fd2..4283e29cc 100644 --- a/src/Text/Pandoc/Writers/Textile.hs +++ b/src/Text/Pandoc/Writers/Textile.hs @@ -435,7 +435,9 @@ inlineToTextile opts (RawInline f str) isEnabled Ext_raw_tex opts = return str | otherwise = return "" -inlineToTextile _ (LineBreak) = return "\n" +inlineToTextile _ LineBreak = return "\n" + +inlineToTextile _ PageBreak = return mempty inlineToTextile _ SoftBreak = return " " diff --git a/src/Text/Pandoc/Writers/ZimWiki.hs b/src/Text/Pandoc/Writers/ZimWiki.hs index 423928c8a..56a5d5455 100644 --- a/src/Text/Pandoc/Writers/ZimWiki.hs +++ b/src/Text/Pandoc/Writers/ZimWiki.hs @@ -317,7 +317,9 @@ inlineToZimWiki opts (RawInline f str) | f == Format "html" = do cont <- indentFromHTML opts str; return cont | otherwise = return "" -inlineToZimWiki _ (LineBreak) = return "\n" -- was \\\\ +inlineToZimWiki _ LineBreak = return "\n" -- was \\\\ + +inlineToZimWiki _ PageBreak = return mempty inlineToZimWiki opts SoftBreak = case writerWrapText opts of -- cgit v1.2.3 From 0ab4af2f03f4226714a39c959c161def679d9d57 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Sat, 24 Sep 2016 17:49:07 -0400 Subject: New Free module, with pure versions of IO funcs Introduce a new module, Text.Pandoc.Free, with pure versions, based on the free monad, of numerous IO functions used in writers and readers. These functions are in a pure Monad (PandocAction). PandocAction takes as a parameter the type of IORefs in it. It can be aliased in individual writers and readers to avoid this parameter. Note that this means that at the moment a reader can only use one type of IORef. If possible, it would be nice to remove this limitation. --- pandoc.cabal | 3 +- src/Text/Pandoc/Free.hs | 209 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 src/Text/Pandoc/Free.hs (limited to 'src/Text') diff --git a/pandoc.cabal b/pandoc.cabal index 9ed034fe5..a33b8571f 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -293,7 +293,7 @@ Library filemanip >= 0.3 && < 0.4, cmark >= 0.5 && < 0.6, doctemplates >= 0.1 && < 0.2, - ghc-prim >= 0.2 + free >= 4 if flag(old-locale) Build-Depends: old-locale >= 1 && < 1.1, time >= 1.2 && < 1.5 @@ -384,6 +384,7 @@ Library Text.Pandoc.SelfContained, Text.Pandoc.Process, Text.Pandoc.CSS + Text.Pandoc.Free Other-Modules: Text.Pandoc.Readers.Docx.Lists, Text.Pandoc.Readers.Docx.Combine, Text.Pandoc.Readers.Docx.Parse, diff --git a/src/Text/Pandoc/Free.hs b/src/Text/Pandoc/Free.hs new file mode 100644 index 000000000..d6a28e87f --- /dev/null +++ b/src/Text/Pandoc/Free.hs @@ -0,0 +1,209 @@ +{-# LANGUAGE DeriveFunctor #-} + +{- +Copyright (C) 2016 Jesse Rosenthal + +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.Free + Copyright : Copyright (C) 2016 Jesse Rosenthal + License : GNU GPL, version 2 or above + + Maintainer : Jesse Rosenthal + Stability : alpha + Portability : portable + +Pure implementations of the IO monads used in Pandoc's readers and writers. +-} + +module Text.Pandoc.Free ( PandocActionF(..) + , PandocAction + , runIO + , liftF + -- + , lookupEnv + , getCurrentTime + , getPOSIXTime + , getDefaultReferenceDocx + , getDefaultReferenceODT + , newStdGen + , newUnique + , newUUID + , readFileStrict + , readFileLazy + , readFileUTF8 + , readDataFile + , fetchItem + , fetchItem' + , warn + , fail + , newIORef + , modifyIORef + , readIORef + , namesMatching + ) where + +import Prelude hiding (readFile, fail) +import qualified Control.Monad as M (fail) +import System.Random (StdGen) +import qualified System.Random as IO (newStdGen) +import Codec.Archive.Zip (Archive) +import Data.Unique (Unique) +import qualified Data.Unique as IO (newUnique) +import qualified Text.Pandoc.Shared as IO ( fetchItem + , fetchItem' + , getDefaultReferenceDocx + , getDefaultReferenceODT + , warn + , readDataFile) +import Text.Pandoc.MediaBag (MediaBag) +import Data.Time.Clock.POSIX (POSIXTime) +import qualified Data.Time.Clock.POSIX as IO (getPOSIXTime) +import Text.Pandoc.Compat.Time (UTCTime) +import qualified Text.Pandoc.Compat.Time as IO (getCurrentTime) +import Text.Pandoc.MIME (MimeType) +import qualified Data.ByteString as B +import qualified Data.ByteString.Lazy as BL +import Control.Monad.Free +import qualified Control.Exception as E +import qualified System.Environment as IO (lookupEnv) +import Data.IORef (IORef) +import qualified Data.IORef as IO (newIORef, modifyIORef, readIORef) +import Text.Pandoc.UUID (UUID) +import qualified Text.Pandoc.UUID as IO (getRandomUUID) +import qualified Text.Pandoc.UTF8 as UTF8 (readFile) +import qualified System.FilePath.Glob as IO (namesMatching) + +data PandocActionF ref nxt = + LookupEnv String (Maybe String -> nxt) + | GetCurrentTime (UTCTime -> nxt) + | GetPOSIXTime (POSIXTime -> nxt) + | GetDefaultReferenceDocx (Maybe FilePath) (Archive -> nxt) + | GetDefaultReferenceODT (Maybe FilePath) (Archive -> nxt) + | NewStdGen (StdGen -> nxt) + | NewUnique (Unique -> nxt) + | NewUUID (UUID -> nxt) + | ReadFileStrict FilePath (B.ByteString -> nxt) + | ReadFileLazy FilePath (BL.ByteString -> nxt) + | ReadFileUTF8 FilePath (String -> nxt) + | ReadDataFile (Maybe FilePath) FilePath (B.ByteString -> nxt) + | FetchItem (Maybe String) (String) + (Either E.SomeException (B.ByteString, Maybe MimeType) -> nxt) + | FetchItem' MediaBag (Maybe String) (String) + (Either E.SomeException (B.ByteString, Maybe MimeType) -> nxt) + | NewIORef ref (IORef ref -> nxt) + | ModifyIORef (IORef ref) (ref -> ref) nxt + | ReadIORef (IORef ref) (ref -> nxt) + | NamesMatching String ([FilePath] -> nxt) + | Warn String nxt + | Fail String + deriving Functor + +type PandocAction a = Free (PandocActionF a) + +lookupEnv :: String -> PandocAction a (Maybe String) +lookupEnv s = liftF $ LookupEnv s id + +getCurrentTime :: PandocAction a UTCTime +getCurrentTime = liftF $ GetCurrentTime id + +getPOSIXTime :: PandocAction a POSIXTime +getPOSIXTime = liftF $ GetPOSIXTime id + +getDefaultReferenceDocx :: Maybe FilePath -> PandocAction a Archive +getDefaultReferenceDocx fp = liftF $ GetDefaultReferenceDocx fp id + +getDefaultReferenceODT :: Maybe FilePath -> PandocAction a Archive +getDefaultReferenceODT fp = liftF $ GetDefaultReferenceODT fp id + +newStdGen :: PandocAction a StdGen +newStdGen = liftF $ NewStdGen id + +newUnique :: PandocAction a Unique +newUnique = liftF $ NewUnique id + +newUUID :: PandocAction a UUID +newUUID = liftF $ NewUUID id + +readFileStrict :: FilePath -> PandocAction a B.ByteString +readFileStrict fp = liftF $ ReadFileStrict fp id + +readFileLazy :: FilePath -> PandocAction a BL.ByteString +readFileLazy fp = liftF $ ReadFileLazy fp id + +readFileUTF8 :: FilePath -> PandocAction a String +readFileUTF8 fp = liftF $ ReadFileUTF8 fp id + +readDataFile :: Maybe FilePath -> FilePath -> PandocAction a B.ByteString +readDataFile mfp fp = liftF $ ReadDataFile mfp fp id + +fetchItem :: Maybe String -> + String -> + PandocAction a (Either E.SomeException (B.ByteString, Maybe MimeType)) +fetchItem ms s = liftF $ FetchItem ms s id + + +fetchItem' :: MediaBag -> + Maybe String -> + String -> + PandocAction a (Either E.SomeException (B.ByteString, Maybe MimeType)) +fetchItem' mb ms s = liftF $ FetchItem' mb ms s id + +warn :: String -> PandocAction a () +warn s = liftF $ Warn s () + +fail :: String -> PandocAction a b +fail s = liftF $ Fail s + +newIORef :: a -> PandocAction a (IORef a) +newIORef v = liftF $ NewIORef v id + +modifyIORef :: (IORef a) -> (a -> a) -> PandocAction a () +modifyIORef ref f = liftF $ ModifyIORef ref f () + +readIORef :: (IORef a) -> PandocAction a a +readIORef ref = liftF $ ReadIORef ref id + +namesMatching :: String -> PandocAction a [FilePath] +namesMatching s = liftF $ NamesMatching s id + +runIO :: PandocAction ref nxt -> IO nxt +runIO (Free (LookupEnv s f)) = IO.lookupEnv s >>= runIO . f +runIO (Free (GetCurrentTime f)) = IO.getCurrentTime >>= runIO . f +runIO (Free (GetPOSIXTime f)) = IO.getPOSIXTime >>= runIO . f +runIO (Free (GetDefaultReferenceDocx mfp f)) = + IO.getDefaultReferenceDocx mfp >>= runIO . f +runIO (Free (GetDefaultReferenceODT mfp f)) = + IO.getDefaultReferenceODT mfp >>= runIO . f +runIO (Free (NewStdGen f)) = IO.newStdGen >>= runIO . f +runIO (Free (NewUnique f)) = IO.newUnique >>= runIO . f +runIO (Free (NewUUID f)) = IO.getRandomUUID >>= runIO . f +runIO (Free (ReadFileStrict fp f)) = B.readFile fp >>= runIO . f +runIO (Free (ReadFileLazy fp f)) = BL.readFile fp >>= runIO . f +runIO (Free (ReadFileUTF8 fp f)) = UTF8.readFile fp >>= runIO . f +runIO (Free (ReadDataFile mfp fp f)) = IO.readDataFile mfp fp >>= runIO . f +runIO (Free (Fail s)) = M.fail s +runIO (Free (FetchItem sourceUrl nm f)) = + IO.fetchItem sourceUrl nm >>= runIO . f +runIO (Free (FetchItem' media sourceUrl nm f)) = + IO.fetchItem' media sourceUrl nm >>= runIO . f +runIO (Free (Warn s nxt)) = IO.warn s >> runIO nxt +runIO (Free (NewIORef v f)) = IO.newIORef v >>= runIO . f +runIO (Free (ModifyIORef ref f nxt)) = IO.modifyIORef ref f >> runIO nxt +runIO (Free (ReadIORef ref f)) = IO.readIORef ref >>= runIO . f +runIO (Free (NamesMatching s f)) = IO.namesMatching s >>= runIO . f +runIO (Pure r) = return r -- cgit v1.2.3 From 32c68dada92eb142949c5be5224a3ddf20fcf484 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Sat, 24 Sep 2016 17:52:25 -0400 Subject: Introduce pure versions of IO Writers. Using Text.Pandoc.Free, introduce pure versions of Docx, EPUB, ICML, and ODT writers. Each of the pure versions is exported along with the IO version (produced by running `runIO` on the pure reader). Ideally, this should make the writers easier to test. --- src/Text/Pandoc/Writers/Docx.hs | 66 +++++++++++++++++++-------------- src/Text/Pandoc/Writers/EPUB.hs | 81 ++++++++++++++++++++++------------------- src/Text/Pandoc/Writers/ICML.hs | 19 +++++++--- src/Text/Pandoc/Writers/ODT.hs | 48 ++++++++++++++---------- 4 files changed, 124 insertions(+), 90 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index d425bbbca..cecee7e9e 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE ScopedTypeVariables, PatternGuards, ViewPatterns #-} +{-# LANGUAGE ScopedTypeVariables, PatternGuards, ViewPatterns, DeriveFunctor #-} {- Copyright (C) 2012-2015 John MacFarlane @@ -28,7 +28,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Conversion of 'Pandoc' documents to docx. -} -module Text.Pandoc.Writers.Docx ( writeDocx ) where +module Text.Pandoc.Writers.Docx ( writeDocx, writeDocxPure ) where import Data.List ( intercalate, isPrefixOf, isSuffixOf ) import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL @@ -38,7 +38,6 @@ import qualified Data.Set as Set import qualified Text.Pandoc.UTF8 as UTF8 import Codec.Archive.Zip import Data.Time.Clock.POSIX -import System.Environment import Text.Pandoc.Compat.Time import Text.Pandoc.Definition import Text.Pandoc.Generic @@ -57,7 +56,7 @@ import Control.Monad.Reader import Control.Monad.State import Skylighting import Data.Unique (hashUnique, newUnique) -import System.Random (randomRIO) +import System.Random (randomR) import Text.Printf (printf) import qualified Control.Exception as E import Data.Monoid ((<>)) @@ -67,6 +66,10 @@ import Text.Pandoc.MIME (MimeType, getMimeType, getMimeTypeDef, import Control.Applicative ((<|>)) import Data.Maybe (fromMaybe, mapMaybe, maybeToList, isNothing) import Data.Char (ord, isSpace, toLower) +import Text.Pandoc.Free (PandocAction, runIO) +import qualified Text.Pandoc.Free as P + +type DocxAction = PandocAction () data ListMarker = NoMarker | BulletMarker @@ -146,7 +149,7 @@ defaultWriterState = WriterState{ , stDynamicTextProps = [] } -type WS = ReaderT WriterEnv (StateT WriterState IO) +type WS = ReaderT WriterEnv (StateT WriterState (DocxAction)) mknode :: Node t => String -> [(String,String)] -> t -> Element mknode s attrs = @@ -213,19 +216,27 @@ metaValueToInlines (MetaBlocks bs) = query return bs metaValueToInlines (MetaBool b) = [Str $ show b] metaValueToInlines _ = [] --- | Produce an Docx file from a Pandoc document. + + writeDocx :: WriterOptions -- ^ Writer options -> Pandoc -- ^ Document to convert -> IO BL.ByteString -writeDocx opts doc@(Pandoc meta _) = do +writeDocx opts doc = runIO $ writeDocxPure opts doc + + +-- | Produce an Docx file from a Pandoc document. +writeDocxPure :: WriterOptions -- ^ Writer options + -> Pandoc -- ^ Document to convert + -> DocxAction BL.ByteString +writeDocxPure opts doc@(Pandoc meta _) = do let datadir = writerUserDataDir opts let doc' = walk fixDisplayMath $ doc - username <- lookup "USERNAME" <$> getEnvironment - utctime <- getCurrentTime - distArchive <- getDefaultReferenceDocx datadir + username <- P.lookupEnv "USERNAME" + utctime <- P.getCurrentTime + distArchive <- P.getDefaultReferenceDocx datadir refArchive <- case writerReferenceDocx opts of - Just f -> liftM (toArchive . toLazy) $ B.readFile f - Nothing -> getDefaultReferenceDocx datadir + Just f -> liftM (toArchive . toLazy) $ P.readFileStrict f + Nothing -> P.getDefaultReferenceDocx datadir parsedDoc <- parseXml refArchive distArchive "word/document.xml" let wname f qn = qPrefix qn == Just "w" && f (qName qn) @@ -603,7 +614,7 @@ styleToOpenXml sm style = $ backgroundColor style ) ] -copyChildren :: Archive -> Archive -> String -> Integer -> [String] -> IO Entry +copyChildren :: Archive -> Archive -> String -> Integer -> [String] -> DocxAction Entry copyChildren refArchive distArchive path timestamp elNames = do ref <- parseXml refArchive distArchive path dist <- parseXml distArchive distArchive path @@ -622,7 +633,7 @@ copyChildren refArchive distArchive path timestamp elNames = do baseListId :: Int baseListId = 1000 -mkNumbering :: [ListMarker] -> IO [Element] +mkNumbering :: [ListMarker] -> DocxAction [Element] mkNumbering lists = do elts <- mapM mkAbstractNum (ordNub lists) return $ elts ++ zipWith mkNum lists [baseListId..(baseListId + length lists - 1)] @@ -638,9 +649,10 @@ mkNum marker numid = map (\lvl -> mknode "w:lvlOverride" [("w:ilvl",show (lvl :: Int))] $ mknode "w:startOverride" [("w:val",show start)] ()) [0..6] -mkAbstractNum :: ListMarker -> IO Element +mkAbstractNum :: ListMarker -> DocxAction Element mkAbstractNum marker = do - nsid <- randomRIO (0x10000000 :: Integer, 0xFFFFFFFF :: Integer) + gen <- P.newStdGen + let (nsid, _) = randomR (0x10000000 :: Integer, 0xFFFFFFFF :: Integer) gen return $ mknode "w:abstractNum" [("w:abstractNumId",listMarkerToId marker)] $ mknode "w:nsid" [("w:val", printf "%8x" nsid)] () : mknode "w:multiLevelType" [("w:val","multilevel")] () @@ -695,6 +707,7 @@ mkLvl marker lvl = getNumId :: WS Int getNumId = (((baseListId - 1) +) . length) `fmap` gets stLists + makeTOC :: WriterOptions -> WS [Element] makeTOC opts | writerTableOfContents opts = do let depth = "1-"++(show (writerTOCDepth opts)) @@ -781,10 +794,10 @@ rStyleM styleName = do let sty' = getStyleId styleName $ sCharStyleMap styleMaps return $ mknode "w:rStyle" [("w:val",sty')] () -getUniqueId :: MonadIO m => m String +getUniqueId :: DocxAction String -- the + 20 is to ensure that there are no clashes with the rIds -- already in word/document.xml.rel -getUniqueId = liftIO $ (show . (+ 20) . hashUnique) `fmap` newUnique +getUniqueId = (show . (+ 20) . hashUnique) <$> P.newUnique -- | Key for specifying user-defined docx styles. dynamicStyleKey :: String @@ -825,7 +838,7 @@ blockToOpenXML' opts (Header lev (ident,_,_) lst) = do then uniqueIdent lst usedIdents else ident modify $ \s -> s{ stSectionIds = Set.insert bookmarkName $ stSectionIds s } - id' <- getUniqueId + id' <- (lift . lift) getUniqueId let bookmarkStart = mknode "w:bookmarkStart" [("w:id", id') ,("w:name",bookmarkName)] () let bookmarkEnd = mknode "w:bookmarkEnd" [("w:id", id')] () @@ -1137,7 +1150,7 @@ inlineToOpenXML' opts (Code attrs str) = do else unhighlighted inlineToOpenXML' opts (Note bs) = do notes <- gets stFootnotes - notenum <- getUniqueId + notenum <- (lift . lift) getUniqueId footnoteStyle <- rStyleM "Footnote Reference" let notemarker = mknode "w:r" [] [ mknode "w:rPr" [] footnoteStyle @@ -1168,7 +1181,7 @@ inlineToOpenXML' opts (Link _ txt (src,_)) = do id' <- case M.lookup src extlinks of Just i -> return i Nothing -> do - i <- ("rId"++) `fmap` getUniqueId + i <- ("rId"++) `fmap` ((lift . lift) getUniqueId) modify $ \st -> st{ stExternalLinks = M.insert src i extlinks } return i @@ -1180,15 +1193,14 @@ inlineToOpenXML' opts (Image attr alt (src, title)) = do case M.lookup src imgs of Just (_,_,_,elt,_) -> return [elt] Nothing -> do - res <- liftIO $ - fetchItem' (writerMediaBag opts) (writerSourceURL opts) src + res <- (lift . lift) $ P.fetchItem' (writerMediaBag opts) (writerSourceURL opts) src case res of Left (_ :: E.SomeException) -> do - warn $ "Could not find image `" ++ src ++ "', skipping..." + (lift . lift) $ P.warn ("Could not find image `" ++ src ++ "', skipping...") -- emit alt text inlinesToOpenXML opts alt Right (img, mt) -> do - ident <- ("rId"++) `fmap` getUniqueId + ident <- ("rId"++) `fmap` ((lift . lift) getUniqueId) let (xpt,ypt) = desiredSizeInPoints opts attr (either (const def) id (imageSize img)) -- 12700 emu = 1 pt @@ -1272,13 +1284,13 @@ defaultFootnotes = [ mknode "w:footnote" [ mknode "w:r" [] $ [ mknode "w:continuationSeparator" [] ()]]]] -parseXml :: Archive -> Archive -> String -> IO Element +parseXml :: Archive -> Archive -> String -> DocxAction Element parseXml refArchive distArchive relpath = case findEntryByPath relpath refArchive `mplus` findEntryByPath relpath distArchive of Nothing -> fail $ relpath ++ " missing in reference docx" Just e -> case parseXMLDoc . UTF8.toStringLazy . fromEntry $ e of - Nothing -> fail $ relpath ++ " corrupt in reference docx" + Nothing -> P.fail $ relpath ++ " corrupt in reference docx" Just d -> return d -- | Scales the image to fit the page diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index 00bf4a81c..4a93d52e2 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -28,26 +28,23 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Conversion of 'Pandoc' documents to EPUB. -} -module Text.Pandoc.Writers.EPUB ( writeEPUB ) where -import Data.IORef ( IORef, newIORef, readIORef, modifyIORef ) +module Text.Pandoc.Writers.EPUB ( writeEPUB, writeEPUBPure ) where +import Data.IORef ( IORef ) import qualified Data.Map as M import qualified Data.Set as Set import Data.Maybe ( fromMaybe, catMaybes ) import Data.List ( isPrefixOf, isInfixOf, intercalate ) -import System.Environment ( getEnv ) import Text.Printf (printf) import System.FilePath ( takeExtension, takeFileName ) -import System.FilePath.Glob ( namesMatching ) import Network.HTTP ( urlEncode ) import qualified Data.ByteString.Lazy as B import qualified Data.ByteString.Lazy.Char8 as B8 import qualified Text.Pandoc.UTF8 as UTF8 import Codec.Archive.Zip ( emptyArchive, addEntryToArchive, eRelativePath, fromEntry , Entry, toEntry, fromArchive) -import Data.Time.Clock.POSIX ( getPOSIXTime ) import Text.Pandoc.Compat.Time import Text.Pandoc.Shared ( renderTags', safeRead, uniqueIdent, trim - , normalizeDate, readDataFile, stringify, warn - , hierarchicalize, fetchItem' ) + , normalizeDate, stringify + , hierarchicalize ) import qualified Text.Pandoc.Shared as S (Element(..)) import Text.Pandoc.Builder (fromList, setMeta) import Text.Pandoc.Options ( WriterOptions(..) @@ -58,17 +55,19 @@ import Text.Pandoc.Options ( WriterOptions(..) import Text.Pandoc.Definition import Text.Pandoc.Walk (walk, walkM, query) import Control.Monad.State (modify, get, State, put, evalState) -import Control.Monad (mplus, liftM, when) +import Control.Monad (mplus, when) import Text.XML.Light ( unode, Element(..), unqual, Attr(..), add_attrs , strContent, lookupAttr, Node(..), QName(..), parseXML , onlyElems, node, ppElement) -import Text.Pandoc.UUID (getRandomUUID) import Text.Pandoc.Writers.HTML ( writeHtml ) import Data.Char ( toLower, isDigit, isAlphaNum ) import Text.Pandoc.MIME (MimeType, getMimeType, extensionFromMimeType) -import qualified Control.Exception as E import Text.Blaze.Html.Renderer.Utf8 (renderHtml) import Text.HTML.TagSoup (Tag(TagOpen), fromAttrib, parseTags) +import Text.Pandoc.Free (PandocAction, runIO) +import qualified Text.Pandoc.Free as P + +type EPUBAction = PandocAction [(FilePath, (FilePath, Maybe Entry))] -- A Chapter includes a list of blocks and maybe a section -- number offset. Note, some chapters are unnumbered. The section @@ -143,7 +142,7 @@ removeNote :: Inline -> Inline removeNote (Note _) = Str "" removeNote x = x -getEPUBMetadata :: WriterOptions -> Meta -> IO EPUBMetadata +getEPUBMetadata :: WriterOptions -> Meta -> EPUBAction EPUBMetadata getEPUBMetadata opts meta = do let md = metadataFromMeta opts meta let elts = onlyElems $ parseXML $ writerEpubMetadata opts @@ -151,7 +150,7 @@ getEPUBMetadata opts meta = do let addIdentifier m = if null (epubIdentifier m) then do - randomId <- fmap show getRandomUUID + randomId <- fmap show P.newUUID return $ m{ epubIdentifier = [Identifier randomId Nothing] } else return m let addLanguage m = @@ -159,16 +158,19 @@ getEPUBMetadata opts meta = do then case lookup "lang" (writerVariables opts) of Just x -> return m{ epubLanguage = x } Nothing -> do - localeLang <- E.catch (liftM - (map (\c -> if c == '_' then '-' else c) . - takeWhile (/='.')) $ getEnv "LANG") - (\e -> let _ = (e :: E.SomeException) in return "en-US") + mLang <- P.lookupEnv "LANG" + let localeLang = + case mLang of + Just lang -> + map (\c -> if c == '_' then '-' else c) $ + takeWhile (/='.') lang + Nothing -> "en-US" return m{ epubLanguage = localeLang } else return m let fixDate m = if null (epubDate m) then do - currentTime <- getCurrentTime + currentTime <- P.getCurrentTime return $ m{ epubDate = [ Date{ dateText = showDateTimeISO8601 currentTime , dateEvent = Nothing } ] } @@ -333,10 +335,15 @@ metadataFromMeta opts meta = EPUBMetadata{ writeEPUB :: WriterOptions -- ^ Writer options -> Pandoc -- ^ Document to convert -> IO B.ByteString -writeEPUB opts doc@(Pandoc meta _) = do +writeEPUB opts doc = runIO $ writeEPUBPure opts doc + +writeEPUBPure :: WriterOptions -- ^ Writer options + -> Pandoc -- ^ Document to convert + -> EPUBAction B.ByteString +writeEPUBPure opts doc@(Pandoc meta _) = do let version = fromMaybe EPUB2 (writerEpubVersion opts) let epub3 = version == EPUB3 - epochtime <- floor `fmap` getPOSIXTime + epochtime <- floor <$> P.getPOSIXTime let mkEntry path content = toEntry path epochtime content let vars = ("epub3", if epub3 then "true" else "false") : ("css", "stylesheet.css") @@ -361,7 +368,7 @@ writeEPUB opts doc@(Pandoc meta _) = do let cpContent = renderHtml $ writeHtml opts'{ writerVariables = ("coverpage","true"):vars } (Pandoc meta [RawBlock (Format "html") $ "
\n\"cover\n
"]) - imgContent <- B.readFile img + imgContent <- P.readFileLazy img return ( [mkEntry "cover.xhtml" cpContent] , [mkEntry coverImage imgContent] ) @@ -372,18 +379,18 @@ writeEPUB opts doc@(Pandoc meta _) = do let tpEntry = mkEntry "title_page.xhtml" tpContent -- handle pictures - mediaRef <- newIORef [] + mediaRef <- P.newIORef [] Pandoc _ blocks <- walkM (transformInline opts' mediaRef) doc >>= walkM (transformBlock opts' mediaRef) - picEntries <- (catMaybes . map (snd . snd)) <$> readIORef mediaRef + picEntries <- (catMaybes . map (snd . snd)) <$> P.readIORef mediaRef -- handle fonts let matchingGlob f = do - xs <- namesMatching f + xs <- P.namesMatching f when (null xs) $ - warn $ f ++ " did not match any font files." + P.warn $ f ++ " did not match any font files." return xs - let mkFontEntry f = mkEntry (takeFileName f) `fmap` B.readFile f + let mkFontEntry f = mkEntry (takeFileName f) `fmap` P.readFileLazy f fontFiles <- concat <$> mapM matchingGlob (writerEpubFonts opts') fontEntries <- mapM mkFontEntry fontFiles @@ -520,7 +527,7 @@ writeEPUB opts doc@(Pandoc meta _) = do let uuid = case epubIdentifier metadata of (x:_) -> identifierText x -- use first identifier as UUID [] -> error "epubIdentifier is null" -- shouldn't happen - currentTime <- getCurrentTime + currentTime <- P.getCurrentTime let contentsData = UTF8.fromStringLazy $ ppTopElement $ unode "package" ! [("version", case version of EPUB2 -> "2.0" @@ -692,10 +699,10 @@ writeEPUB opts doc@(Pandoc meta _) = do -- stylesheet stylesheet <- case epubStylesheet metadata of - Just (StylesheetPath fp) -> UTF8.readFile fp + Just (StylesheetPath fp) -> P.readFileUTF8 fp Just (StylesheetContents s) -> return s Nothing -> UTF8.toString `fmap` - readDataFile (writerUserDataDir opts) "epub.css" + P.readDataFile (writerUserDataDir opts) "epub.css" let stylesheetEntry = mkEntry "stylesheet.css" $ UTF8.fromStringLazy stylesheet -- construct archive @@ -814,7 +821,7 @@ showDateTimeISO8601 = formatTime defaultTimeLocale "%FT%TZ" transformTag :: WriterOptions -> IORef [(FilePath, (FilePath, Maybe Entry))] -- ^ (oldpath, newpath, entry) media -> Tag String - -> IO (Tag String) + -> EPUBAction (Tag String) transformTag opts mediaRef tag@(TagOpen name attr) | name `elem` ["video", "source", "img", "audio"] && lookup "data-external" attr == Nothing = do @@ -831,34 +838,34 @@ transformTag _ _ tag = return tag modifyMediaRef :: WriterOptions -> IORef [(FilePath, (FilePath, Maybe Entry))] -> FilePath - -> IO FilePath + -> EPUBAction FilePath modifyMediaRef _ _ "" = return "" modifyMediaRef opts mediaRef oldsrc = do - media <- readIORef mediaRef + media <- P.readIORef mediaRef case lookup oldsrc media of Just (n,_) -> return n Nothing -> do - res <- fetchItem' (writerMediaBag opts) + res <- P.fetchItem' (writerMediaBag opts) (writerSourceURL opts) oldsrc (new, mbEntry) <- case res of Left _ -> do - warn $ "Could not find media `" ++ oldsrc ++ "', skipping..." + P.warn $ "Could not find media `" ++ oldsrc ++ "', skipping..." return (oldsrc, Nothing) Right (img,mbMime) -> do let new = "media/file" ++ show (length media) ++ fromMaybe (takeExtension (takeWhile (/='?') oldsrc)) (('.':) <$> (mbMime >>= extensionFromMimeType)) - epochtime <- floor `fmap` getPOSIXTime + epochtime <- floor `fmap` P.getPOSIXTime let entry = toEntry new epochtime $ B.fromChunks . (:[]) $ img return (new, Just entry) - modifyIORef mediaRef ( (oldsrc, (new, mbEntry)): ) + P.modifyIORef mediaRef ( (oldsrc, (new, mbEntry)): ) return new transformBlock :: WriterOptions -> IORef [(FilePath, (FilePath, Maybe Entry))] -- ^ (oldpath, newpath, entry) media -> Block - -> IO Block + -> EPUBAction Block transformBlock opts mediaRef (RawBlock fmt raw) | fmt == Format "html" = do let tags = parseTags raw @@ -869,7 +876,7 @@ transformBlock _ _ b = return b transformInline :: WriterOptions -> IORef [(FilePath, (FilePath, Maybe Entry))] -- ^ (oldpath, newpath) media -> Inline - -> IO Inline + -> EPUBAction Inline transformInline opts mediaRef (Image attr lab (src,tit)) = do newsrc <- modifyMediaRef opts mediaRef src return $ Image attr lab (newsrc, tit) diff --git a/src/Text/Pandoc/Writers/ICML.hs b/src/Text/Pandoc/Writers/ICML.hs index e2c123fc2..3a1e772ce 100644 --- a/src/Text/Pandoc/Writers/ICML.hs +++ b/src/Text/Pandoc/Writers/ICML.hs @@ -18,7 +18,7 @@ import Text.Pandoc.Definition import Text.Pandoc.XML import Text.Pandoc.Readers.TeXMath (texMathToInlines) import Text.Pandoc.Writers.Shared -import Text.Pandoc.Shared (linesToPara, splitBy, fetchItem, warn) +import Text.Pandoc.Shared (linesToPara, splitBy, warn) import Text.Pandoc.Options import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Pretty @@ -28,6 +28,10 @@ import Data.Text as Text (breakOnAll, pack) import Control.Monad.State import Network.URI (isURI) import qualified Data.Set as Set +import Text.Pandoc.Free (runIO) +import qualified Text.Pandoc.Free as P + +type ICMLAction = P.PandocAction () type Style = [String] type Hyperlink = [(Int, String)] @@ -40,7 +44,7 @@ data WriterState = WriterState{ , maxListDepth :: Int } -type WS a = StateT WriterState IO a +type WS a = StateT WriterState ICMLAction a defaultWriterState :: WriterState defaultWriterState = WriterState{ @@ -121,10 +125,13 @@ subListParName = "subParagraph" footnoteName = "Footnote" citeName = "Cite" - -- | Convert Pandoc document to string in ICML format. writeICML :: WriterOptions -> Pandoc -> IO String -writeICML opts (Pandoc meta blocks) = do +writeICML opts doc = runIO $ writeICMLPure opts doc + +-- | Convert Pandoc document to string in ICML format. +writeICMLPure :: WriterOptions -> Pandoc -> ICMLAction String +writeICMLPure opts (Pandoc meta blocks) = do let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing @@ -532,10 +539,10 @@ styleToStrAttr style = -- | Assemble an ICML Image. imageICML :: WriterOptions -> Style -> Attr -> Target -> WS Doc imageICML opts style attr (src, _) = do - res <- liftIO $ fetchItem (writerSourceURL opts) src + res <- lift $ P.fetchItem (writerSourceURL opts) src imgS <- case res of Left (_) -> do - warn $ "Could not find image `" ++ src ++ "', skipping..." + lift $ P.warn $ "Could not find image `" ++ src ++ "', skipping..." return def Right (img, _) -> do case imageSize img of diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index ce4d456a3..0f1dd7cd3 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -28,7 +28,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Conversion of 'Pandoc' documents to ODT. -} -module Text.Pandoc.Writers.ODT ( writeODT ) where +module Text.Pandoc.Writers.ODT ( writeODTPure, writeODT ) where import Data.IORef import Data.List ( isPrefixOf ) import Data.Maybe ( fromMaybe ) @@ -38,8 +38,7 @@ import qualified Data.ByteString.Lazy as B import Text.Pandoc.UTF8 ( fromStringLazy ) import Codec.Archive.Zip import Text.Pandoc.Options ( WriterOptions(..), WrapOption(..) ) -import Text.Pandoc.Shared ( stringify, fetchItem', warn, - getDefaultReferenceODT ) +import Text.Pandoc.Shared ( stringify ) import Text.Pandoc.ImageSize import Text.Pandoc.MIME ( getMimeType, extensionFromMimeType ) import Text.Pandoc.Definition @@ -50,28 +49,37 @@ import Control.Monad (liftM) import Text.Pandoc.XML import Text.Pandoc.Pretty import qualified Control.Exception as E -import Data.Time.Clock.POSIX ( getPOSIXTime ) import System.FilePath ( takeExtension, takeDirectory, (<.>)) +import Text.Pandoc.Free ( PandocAction, runIO ) +import qualified Text.Pandoc.Free as P + +type ODTAction = PandocAction [Entry] -- | Produce an ODT file from a Pandoc document. writeODT :: WriterOptions -- ^ Writer options -> Pandoc -- ^ Document to convert -> IO B.ByteString -writeODT opts doc@(Pandoc meta _) = do +writeODT opts doc = runIO $ writeODTPure opts doc + +-- | Produce an ODT file from a Pandoc document. +writeODTPure :: WriterOptions -- ^ Writer options + -> Pandoc -- ^ Document to convert + -> ODTAction B.ByteString +writeODTPure opts doc@(Pandoc meta _) = do let datadir = writerUserDataDir opts let title = docTitle meta refArchive <- case writerReferenceODT opts of - Just f -> liftM toArchive $ B.readFile f - Nothing -> getDefaultReferenceODT datadir + Just f -> liftM toArchive $ P.readFileLazy f + Nothing -> P.getDefaultReferenceODT datadir -- handle formulas and pictures - picEntriesRef <- newIORef ([] :: [Entry]) + picEntriesRef <- P.newIORef ([] :: [Entry]) doc' <- walkM (transformPicMath opts picEntriesRef) $ walk fixDisplayMath doc let newContents = writeOpenDocument opts{writerWrapText = WrapNone} doc' - epochtime <- floor `fmap` getPOSIXTime + epochtime <- floor `fmap` P.getPOSIXTime let contentEntry = toEntry "content.xml" epochtime $ fromStringLazy newContents - picEntries <- readIORef picEntriesRef + picEntries <- P.readIORef picEntriesRef let archive = foldr addEntryToArchive refArchive $ contentEntry : picEntries -- construct META-INF/manifest.xml based on archive @@ -126,18 +134,18 @@ writeODT opts doc@(Pandoc meta _) = do return $ fromArchive archive'' -- | transform both Image and Math elements -transformPicMath :: WriterOptions -> IORef [Entry] -> Inline -> IO Inline +transformPicMath :: WriterOptions -> IORef [Entry] -> Inline -> ODTAction Inline transformPicMath opts entriesRef (Image attr@(id', cls, _) lab (src,t)) = do - res <- fetchItem' (writerMediaBag opts) (writerSourceURL opts) src + res <- P.fetchItem' (writerMediaBag opts) (writerSourceURL opts) src case res of Left (_ :: E.SomeException) -> do - warn $ "Could not find image `" ++ src ++ "', skipping..." + P.warn $ "Could not find image `" ++ src ++ "', skipping..." return $ Emph lab Right (img, mbMimeType) -> do (ptX, ptY) <- case imageSize img of Right s -> return $ sizeInPoints s Left msg -> do - warn $ "Could not determine image size in `" ++ + P.warn $ "Could not determine image size in `" ++ src ++ "': " ++ msg return (100, 100) let dims = @@ -155,28 +163,28 @@ transformPicMath opts entriesRef (Image attr@(id', cls, _) lab (src,t)) = do Just dim -> Just $ Inch $ inInch opts dim Nothing -> Nothing let newattr = (id', cls, dims) - entries <- readIORef entriesRef + entries <- P.readIORef entriesRef let extension = fromMaybe (takeExtension $ takeWhile (/='?') src) (mbMimeType >>= extensionFromMimeType) let newsrc = "Pictures/" ++ show (length entries) <.> extension let toLazy = B.fromChunks . (:[]) - epochtime <- floor `fmap` getPOSIXTime + epochtime <- floor `fmap` P.getPOSIXTime let entry = toEntry newsrc epochtime $ toLazy img - modifyIORef entriesRef (entry:) + P.modifyIORef entriesRef (entry:) return $ Image newattr lab (newsrc, t) transformPicMath _ entriesRef (Math t math) = do - entries <- readIORef entriesRef + entries <- P.readIORef entriesRef let dt = if t == InlineMath then DisplayInline else DisplayBlock case writeMathML dt <$> readTeX math of Left _ -> return $ Math t math Right r -> do let conf = useShortEmptyTags (const False) defaultConfigPP let mathml = ppcTopElement conf r - epochtime <- floor `fmap` getPOSIXTime + epochtime <- floor `fmap` P.getPOSIXTime let dirname = "Formula-" ++ show (length entries) ++ "/" let fname = dirname ++ "content.xml" let entry = toEntry fname epochtime (fromStringLazy mathml) - modifyIORef entriesRef (entry:) + P.modifyIORef entriesRef (entry:) return $ RawInline (Format "opendocument") $ render Nothing $ inTags False "draw:frame" [("text:anchor-type", if t == DisplayMath -- cgit v1.2.3 From 8d1d0eb9a509543c724292438e185e6ed24996b5 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Thu, 17 Nov 2016 08:04:25 -0500 Subject: Remove IORef from ODT writer. We want pure writers, so IORef shouldn't be in there. We switch to using a normal State Monad. If this produces performance problems, we can look into trying STRefs, but that seems like unnecessary complication at the moment. --- src/Text/Pandoc/Writers/ODT.hs | 62 ++++++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 24 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index 0f1dd7cd3..b139695db 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -29,7 +29,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Conversion of 'Pandoc' documents to ODT. -} module Text.Pandoc.Writers.ODT ( writeODTPure, writeODT ) where -import Data.IORef import Data.List ( isPrefixOf ) import Data.Maybe ( fromMaybe ) import Text.XML.Light.Output @@ -46,6 +45,7 @@ import Text.Pandoc.Walk import Text.Pandoc.Writers.Shared ( fixDisplayMath ) import Text.Pandoc.Writers.OpenDocument ( writeOpenDocument ) import Control.Monad (liftM) +import Control.Monad.State import Text.Pandoc.XML import Text.Pandoc.Pretty import qualified Control.Exception as E @@ -55,31 +55,45 @@ import qualified Text.Pandoc.Free as P type ODTAction = PandocAction [Entry] +data ODTState = ODTState { stEntries :: [Entry] + } + +type O = StateT ODTState ODTAction + -- | Produce an ODT file from a Pandoc document. writeODT :: WriterOptions -- ^ Writer options -> Pandoc -- ^ Document to convert -> IO B.ByteString writeODT opts doc = runIO $ writeODTPure opts doc --- | Produce an ODT file from a Pandoc document. -writeODTPure :: WriterOptions -- ^ Writer options - -> Pandoc -- ^ Document to convert +writeODTPure :: WriterOptions + -> Pandoc -> ODTAction B.ByteString -writeODTPure opts doc@(Pandoc meta _) = do +writeODTPure opts doc = + let initState = ODTState{ stEntries = [] + } + in + evalStateT (pandocToODT opts doc) initState + +-- | Produce an ODT file from a Pandoc document. +pandocToODT :: WriterOptions -- ^ Writer options + -> Pandoc -- ^ Document to convert + -> O B.ByteString +pandocToODT opts doc@(Pandoc meta _) = do let datadir = writerUserDataDir opts let title = docTitle meta refArchive <- case writerReferenceODT opts of - Just f -> liftM toArchive $ P.readFileLazy f - Nothing -> P.getDefaultReferenceODT datadir + Just f -> liftM toArchive $ lift $ P.readFileLazy f + Nothing -> lift $ P.getDefaultReferenceODT datadir -- handle formulas and pictures - picEntriesRef <- P.newIORef ([] :: [Entry]) - doc' <- walkM (transformPicMath opts picEntriesRef) $ walk fixDisplayMath doc + -- picEntriesRef <- P.newIORef ([] :: [Entry]) + doc' <- walkM (transformPicMath opts) $ walk fixDisplayMath doc let newContents = writeOpenDocument opts{writerWrapText = WrapNone} doc' - epochtime <- floor `fmap` P.getPOSIXTime + epochtime <- floor `fmap` (lift P.getPOSIXTime) let contentEntry = toEntry "content.xml" epochtime $ fromStringLazy newContents - picEntries <- P.readIORef picEntriesRef + picEntries <- gets stEntries let archive = foldr addEntryToArchive refArchive $ contentEntry : picEntries -- construct META-INF/manifest.xml based on archive @@ -134,18 +148,18 @@ writeODTPure opts doc@(Pandoc meta _) = do return $ fromArchive archive'' -- | transform both Image and Math elements -transformPicMath :: WriterOptions -> IORef [Entry] -> Inline -> ODTAction Inline -transformPicMath opts entriesRef (Image attr@(id', cls, _) lab (src,t)) = do - res <- P.fetchItem' (writerMediaBag opts) (writerSourceURL opts) src +transformPicMath :: WriterOptions ->Inline -> O Inline +transformPicMath opts (Image attr@(id', cls, _) lab (src,t)) = do + res <- lift $ P.fetchItem' (writerMediaBag opts) (writerSourceURL opts) src case res of Left (_ :: E.SomeException) -> do - P.warn $ "Could not find image `" ++ src ++ "', skipping..." + lift $ P.warn $ "Could not find image `" ++ src ++ "', skipping..." return $ Emph lab Right (img, mbMimeType) -> do (ptX, ptY) <- case imageSize img of Right s -> return $ sizeInPoints s Left msg -> do - P.warn $ "Could not determine image size in `" ++ + lift $ P.warn $ "Could not determine image size in `" ++ src ++ "': " ++ msg return (100, 100) let dims = @@ -163,28 +177,28 @@ transformPicMath opts entriesRef (Image attr@(id', cls, _) lab (src,t)) = do Just dim -> Just $ Inch $ inInch opts dim Nothing -> Nothing let newattr = (id', cls, dims) - entries <- P.readIORef entriesRef + entries <- gets stEntries let extension = fromMaybe (takeExtension $ takeWhile (/='?') src) (mbMimeType >>= extensionFromMimeType) let newsrc = "Pictures/" ++ show (length entries) <.> extension let toLazy = B.fromChunks . (:[]) - epochtime <- floor `fmap` P.getPOSIXTime + epochtime <- floor `fmap` (lift P.getPOSIXTime) let entry = toEntry newsrc epochtime $ toLazy img - P.modifyIORef entriesRef (entry:) + modify $ \st -> st{ stEntries = entry : entries } return $ Image newattr lab (newsrc, t) -transformPicMath _ entriesRef (Math t math) = do - entries <- P.readIORef entriesRef +transformPicMath _ (Math t math) = do + entries <- gets stEntries let dt = if t == InlineMath then DisplayInline else DisplayBlock case writeMathML dt <$> readTeX math of Left _ -> return $ Math t math Right r -> do let conf = useShortEmptyTags (const False) defaultConfigPP let mathml = ppcTopElement conf r - epochtime <- floor `fmap` P.getPOSIXTime + epochtime <- floor `fmap` (lift $ P.getPOSIXTime) let dirname = "Formula-" ++ show (length entries) ++ "/" let fname = dirname ++ "content.xml" let entry = toEntry fname epochtime (fromStringLazy mathml) - P.modifyIORef entriesRef (entry:) + modify $ \st -> st{ stEntries = entry : entries } return $ RawInline (Format "opendocument") $ render Nothing $ inTags False "draw:frame" [("text:anchor-type", if t == DisplayMath @@ -197,4 +211,4 @@ transformPicMath _ entriesRef (Math t math) = do , ("xlink:show", "embed") , ("xlink:actuate", "onLoad")] -transformPicMath _ _ x = return x +transformPicMath _ x = return x -- cgit v1.2.3 From 072107d1a2300afc7fb99263cc464048291d16d1 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Thu, 17 Nov 2016 09:04:44 -0500 Subject: Remove IORef from EPUB writer. --- src/Text/Pandoc/Writers/EPUB.hs | 113 ++++++++++++++++++++++------------------ 1 file changed, 62 insertions(+), 51 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index 4a93d52e2..8e283a66a 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -29,7 +29,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Conversion of 'Pandoc' documents to EPUB. -} module Text.Pandoc.Writers.EPUB ( writeEPUB, writeEPUBPure ) where -import Data.IORef ( IORef ) import qualified Data.Map as M import qualified Data.Set as Set import Data.Maybe ( fromMaybe, catMaybes ) @@ -54,7 +53,7 @@ import Text.Pandoc.Options ( WriterOptions(..) , ObfuscationMethod(NoObfuscation) ) import Text.Pandoc.Definition import Text.Pandoc.Walk (walk, walkM, query) -import Control.Monad.State (modify, get, State, put, evalState) +import Control.Monad.State (modify, get, gets, State, StateT, put, evalState, evalStateT, lift) import Control.Monad (mplus, when) import Text.XML.Light ( unode, Element(..), unqual, Attr(..), add_attrs , strContent, lookupAttr, Node(..), QName(..), parseXML @@ -75,6 +74,11 @@ type EPUBAction = PandocAction [(FilePath, (FilePath, Maybe Entry))] -- in filenames, chapter0003.xhtml. data Chapter = Chapter (Maybe [Int]) [Block] +data EPUBState = EPUBState { stMediaPaths :: [(FilePath, (FilePath, Maybe Entry))] + } + +type E = StateT EPUBState EPUBAction + data EPUBMetadata = EPUBMetadata{ epubIdentifier :: [Identifier] , epubTitle :: [Title] @@ -142,7 +146,7 @@ removeNote :: Inline -> Inline removeNote (Note _) = Str "" removeNote x = x -getEPUBMetadata :: WriterOptions -> Meta -> EPUBAction EPUBMetadata +getEPUBMetadata :: WriterOptions -> Meta -> E EPUBMetadata getEPUBMetadata opts meta = do let md = metadataFromMeta opts meta let elts = onlyElems $ parseXML $ writerEpubMetadata opts @@ -150,7 +154,7 @@ getEPUBMetadata opts meta = do let addIdentifier m = if null (epubIdentifier m) then do - randomId <- fmap show P.newUUID + randomId <- fmap show (lift P.newUUID) return $ m{ epubIdentifier = [Identifier randomId Nothing] } else return m let addLanguage m = @@ -158,7 +162,7 @@ getEPUBMetadata opts meta = do then case lookup "lang" (writerVariables opts) of Just x -> return m{ epubLanguage = x } Nothing -> do - mLang <- P.lookupEnv "LANG" + mLang <- lift $ P.lookupEnv "LANG" let localeLang = case mLang of Just lang -> @@ -170,7 +174,7 @@ getEPUBMetadata opts meta = do let fixDate m = if null (epubDate m) then do - currentTime <- P.getCurrentTime + currentTime <- lift P.getCurrentTime return $ m{ epubDate = [ Date{ dateText = showDateTimeISO8601 currentTime , dateEvent = Nothing } ] } @@ -338,12 +342,21 @@ writeEPUB :: WriterOptions -- ^ Writer options writeEPUB opts doc = runIO $ writeEPUBPure opts doc writeEPUBPure :: WriterOptions -- ^ Writer options - -> Pandoc -- ^ Document to convert - -> EPUBAction B.ByteString -writeEPUBPure opts doc@(Pandoc meta _) = do + -> Pandoc -- ^ Document to convert + -> EPUBAction B.ByteString +writeEPUBPure opts doc = + let initState = EPUBState { stMediaPaths = [] + } + in + evalStateT (pandocToEPUB opts doc) initState + +pandocToEPUB :: WriterOptions + -> Pandoc + -> E B.ByteString +pandocToEPUB opts doc@(Pandoc meta _) = do let version = fromMaybe EPUB2 (writerEpubVersion opts) let epub3 = version == EPUB3 - epochtime <- floor <$> P.getPOSIXTime + epochtime <- floor <$> lift P.getPOSIXTime let mkEntry path content = toEntry path epochtime content let vars = ("epub3", if epub3 then "true" else "false") : ("css", "stylesheet.css") @@ -368,7 +381,7 @@ writeEPUBPure opts doc@(Pandoc meta _) = do let cpContent = renderHtml $ writeHtml opts'{ writerVariables = ("coverpage","true"):vars } (Pandoc meta [RawBlock (Format "html") $ "
\n\"cover\n
"]) - imgContent <- P.readFileLazy img + imgContent <- lift $ P.readFileLazy img return ( [mkEntry "cover.xhtml" cpContent] , [mkEntry coverImage imgContent] ) @@ -379,18 +392,17 @@ writeEPUBPure opts doc@(Pandoc meta _) = do let tpEntry = mkEntry "title_page.xhtml" tpContent -- handle pictures - mediaRef <- P.newIORef [] - Pandoc _ blocks <- walkM (transformInline opts' mediaRef) doc >>= - walkM (transformBlock opts' mediaRef) - picEntries <- (catMaybes . map (snd . snd)) <$> P.readIORef mediaRef - + -- mediaRef <- P.newIORef [] + Pandoc _ blocks <- walkM (transformInline opts') doc >>= + walkM (transformBlock opts') + picEntries <- (catMaybes . map (snd . snd)) <$> (gets stMediaPaths) -- handle fonts let matchingGlob f = do - xs <- P.namesMatching f + xs <- lift $ P.namesMatching f when (null xs) $ - P.warn $ f ++ " did not match any font files." + lift $ P.warn $ f ++ " did not match any font files." return xs - let mkFontEntry f = mkEntry (takeFileName f) `fmap` P.readFileLazy f + let mkFontEntry f = mkEntry (takeFileName f) `fmap` (lift $ P.readFileLazy f) fontFiles <- concat <$> mapM matchingGlob (writerEpubFonts opts') fontEntries <- mapM mkFontEntry fontFiles @@ -527,7 +539,7 @@ writeEPUBPure opts doc@(Pandoc meta _) = do let uuid = case epubIdentifier metadata of (x:_) -> identifierText x -- use first identifier as UUID [] -> error "epubIdentifier is null" -- shouldn't happen - currentTime <- P.getCurrentTime + currentTime <- lift $ P.getCurrentTime let contentsData = UTF8.fromStringLazy $ ppTopElement $ unode "package" ! [("version", case version of EPUB2 -> "2.0" @@ -699,10 +711,10 @@ writeEPUBPure opts doc@(Pandoc meta _) = do -- stylesheet stylesheet <- case epubStylesheet metadata of - Just (StylesheetPath fp) -> P.readFileUTF8 fp + Just (StylesheetPath fp) -> lift $ P.readFileUTF8 fp Just (StylesheetContents s) -> return s Nothing -> UTF8.toString `fmap` - P.readDataFile (writerUserDataDir opts) "epub.css" + (lift $ P.readDataFile (writerUserDataDir opts) "epub.css") let stylesheetEntry = mkEntry "stylesheet.css" $ UTF8.fromStringLazy stylesheet -- construct archive @@ -819,78 +831,77 @@ showDateTimeISO8601 :: UTCTime -> String showDateTimeISO8601 = formatTime defaultTimeLocale "%FT%TZ" transformTag :: WriterOptions - -> IORef [(FilePath, (FilePath, Maybe Entry))] -- ^ (oldpath, newpath, entry) media + -- -> IORef [(FilePath, (FilePath, Maybe Entry))] -- ^ (oldpath, newpath, entry) media -> Tag String - -> EPUBAction (Tag String) -transformTag opts mediaRef tag@(TagOpen name attr) + -> E (Tag String) +transformTag opts tag@(TagOpen name attr) | name `elem` ["video", "source", "img", "audio"] && lookup "data-external" attr == Nothing = do let src = fromAttrib "src" tag let poster = fromAttrib "poster" tag - newsrc <- modifyMediaRef opts mediaRef src - newposter <- modifyMediaRef opts mediaRef poster + newsrc <- modifyMediaRef opts src + newposter <- modifyMediaRef opts poster let attr' = filter (\(x,_) -> x /= "src" && x /= "poster") attr ++ [("src", newsrc) | not (null newsrc)] ++ [("poster", newposter) | not (null newposter)] return $ TagOpen name attr' -transformTag _ _ tag = return tag +transformTag _ tag = return tag modifyMediaRef :: WriterOptions - -> IORef [(FilePath, (FilePath, Maybe Entry))] -> FilePath - -> EPUBAction FilePath -modifyMediaRef _ _ "" = return "" -modifyMediaRef opts mediaRef oldsrc = do - media <- P.readIORef mediaRef + -> E FilePath +modifyMediaRef _ "" = return "" +modifyMediaRef opts oldsrc = do + media <- gets stMediaPaths case lookup oldsrc media of Just (n,_) -> return n Nothing -> do - res <- P.fetchItem' (writerMediaBag opts) + res <- lift $ P.fetchItem' (writerMediaBag opts) (writerSourceURL opts) oldsrc (new, mbEntry) <- case res of Left _ -> do - P.warn $ "Could not find media `" ++ oldsrc ++ "', skipping..." + lift $ P.warn $ "Could not find media `" ++ oldsrc ++ "', skipping..." return (oldsrc, Nothing) Right (img,mbMime) -> do let new = "media/file" ++ show (length media) ++ fromMaybe (takeExtension (takeWhile (/='?') oldsrc)) (('.':) <$> (mbMime >>= extensionFromMimeType)) - epochtime <- floor `fmap` P.getPOSIXTime + epochtime <- floor `fmap` lift P.getPOSIXTime let entry = toEntry new epochtime $ B.fromChunks . (:[]) $ img return (new, Just entry) - P.modifyIORef mediaRef ( (oldsrc, (new, mbEntry)): ) + modify $ \st -> st{ stMediaPaths = (oldsrc, (new, mbEntry)):media} return new transformBlock :: WriterOptions - -> IORef [(FilePath, (FilePath, Maybe Entry))] -- ^ (oldpath, newpath, entry) media + -- -> IORef [(FilePath, (FilePath, Maybe Entry))] -- ^ (oldpath, newpath, entry) media -> Block - -> EPUBAction Block -transformBlock opts mediaRef (RawBlock fmt raw) + -> E Block +transformBlock opts (RawBlock fmt raw) | fmt == Format "html" = do let tags = parseTags raw - tags' <- mapM (transformTag opts mediaRef) tags + tags' <- mapM (transformTag opts) tags return $ RawBlock fmt (renderTags' tags') -transformBlock _ _ b = return b +transformBlock _ b = return b transformInline :: WriterOptions - -> IORef [(FilePath, (FilePath, Maybe Entry))] -- ^ (oldpath, newpath) media + -- -> IORef [(FilePath, (FilePath, Maybe Entry))] -- ^ (oldpath, newpath) media -> Inline - -> EPUBAction Inline -transformInline opts mediaRef (Image attr lab (src,tit)) = do - newsrc <- modifyMediaRef opts mediaRef src + -> E Inline +transformInline opts (Image attr lab (src,tit)) = do + newsrc <- modifyMediaRef opts src return $ Image attr lab (newsrc, tit) -transformInline opts mediaRef (x@(Math t m)) +transformInline opts (x@(Math t m)) | WebTeX url <- writerHTMLMathMethod opts = do - newsrc <- modifyMediaRef opts mediaRef (url ++ urlEncode m) + newsrc <- modifyMediaRef opts (url ++ urlEncode m) let mathclass = if t == DisplayMath then "display" else "inline" return $ Span ("",["math",mathclass],[]) [Image nullAttr [x] (newsrc, "")] -transformInline opts mediaRef (RawInline fmt raw) +transformInline opts (RawInline fmt raw) | fmt == Format "html" = do let tags = parseTags raw - tags' <- mapM (transformTag opts mediaRef) tags + tags' <- mapM (transformTag opts) tags return $ RawInline fmt (renderTags' tags') -transformInline _ _ x = return x +transformInline _ x = return x (!) :: (t -> Element) -> [(String, String)] -> t -> Element (!) f attrs n = add_attrs (map (\(k,v) -> Attr (unqual k) v) attrs) (f n) -- cgit v1.2.3 From e24d5a56a7d0b26b9f15185bb570836878927d16 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Wed, 16 Nov 2016 20:49:17 -0500 Subject: Implement runTest functions. These work with a State monad and a Reader monad to produce deterministic results. It can probably be simplified somewhat. --- pandoc.cabal | 2 +- src/Text/Pandoc/Free.hs | 217 +++++++++++++++++++++++++++++----------- src/Text/Pandoc/UUID.hs | 2 +- src/Text/Pandoc/Writers/Docx.hs | 19 ++-- src/Text/Pandoc/Writers/EPUB.hs | 8 +- src/Text/Pandoc/Writers/ICML.hs | 8 +- src/Text/Pandoc/Writers/ODT.hs | 6 +- 7 files changed, 177 insertions(+), 85 deletions(-) (limited to 'src/Text') diff --git a/pandoc.cabal b/pandoc.cabal index a33b8571f..d1fc56bed 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -290,7 +290,7 @@ Library old-time, deepseq >= 1.3 && < 1.5, JuicyPixels >= 3.1.6.1 && < 3.3, - filemanip >= 0.3 && < 0.4, + Glob >= 0.7 && < 0.8, cmark >= 0.5 && < 0.6, doctemplates >= 0.1 && < 0.2, free >= 4 diff --git a/src/Text/Pandoc/Free.hs b/src/Text/Pandoc/Free.hs index d6a28e87f..eb42b45c2 100644 --- a/src/Text/Pandoc/Free.hs +++ b/src/Text/Pandoc/Free.hs @@ -33,6 +33,7 @@ Pure implementations of the IO monads used in Pandoc's readers and writers. module Text.Pandoc.Free ( PandocActionF(..) , PandocAction , runIO + , runTest , liftF -- , lookupEnv @@ -41,7 +42,7 @@ module Text.Pandoc.Free ( PandocActionF(..) , getDefaultReferenceDocx , getDefaultReferenceODT , newStdGen - , newUnique + , newUniqueHash , newUUID , readFileStrict , readFileLazy @@ -51,18 +52,15 @@ module Text.Pandoc.Free ( PandocActionF(..) , fetchItem' , warn , fail - , newIORef - , modifyIORef - , readIORef - , namesMatching + , glob ) where import Prelude hiding (readFile, fail) import qualified Control.Monad as M (fail) -import System.Random (StdGen) +import System.Random (StdGen, next) import qualified System.Random as IO (newStdGen) -import Codec.Archive.Zip (Archive) -import Data.Unique (Unique) +import Codec.Archive.Zip (Archive, fromArchive) +import Data.Unique (Unique, hashUnique, newUnique) import qualified Data.Unique as IO (newUnique) import qualified Text.Pandoc.Shared as IO ( fetchItem , fetchItem' @@ -70,32 +68,35 @@ import qualified Text.Pandoc.Shared as IO ( fetchItem , getDefaultReferenceODT , warn , readDataFile) -import Text.Pandoc.MediaBag (MediaBag) -import Data.Time.Clock.POSIX (POSIXTime) +import Text.Pandoc.MediaBag (MediaBag, lookupMedia) +import Data.Time.Clock.POSIX (POSIXTime, utcTimeToPOSIXSeconds) import qualified Data.Time.Clock.POSIX as IO (getPOSIXTime) import Text.Pandoc.Compat.Time (UTCTime) import qualified Text.Pandoc.Compat.Time as IO (getCurrentTime) -import Text.Pandoc.MIME (MimeType) +import Text.Pandoc.MIME (MimeType, getMimeType) import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL import Control.Monad.Free import qualified Control.Exception as E import qualified System.Environment as IO (lookupEnv) -import Data.IORef (IORef) -import qualified Data.IORef as IO (newIORef, modifyIORef, readIORef) -import Text.Pandoc.UUID (UUID) +import Text.Pandoc.UUID import qualified Text.Pandoc.UUID as IO (getRandomUUID) -import qualified Text.Pandoc.UTF8 as UTF8 (readFile) -import qualified System.FilePath.Glob as IO (namesMatching) - -data PandocActionF ref nxt = +import qualified Text.Pandoc.UTF8 as UTF8 (readFile, toString) +import System.FilePath.Glob (match, compile) +import System.FilePath (()) +import qualified System.FilePath.Glob as IO (glob) +import Control.Monad.State hiding (fail) +import Control.Monad.Reader hiding (fail) +import Data.Word (Word8) + +data PandocActionF nxt = LookupEnv String (Maybe String -> nxt) | GetCurrentTime (UTCTime -> nxt) | GetPOSIXTime (POSIXTime -> nxt) | GetDefaultReferenceDocx (Maybe FilePath) (Archive -> nxt) | GetDefaultReferenceODT (Maybe FilePath) (Archive -> nxt) | NewStdGen (StdGen -> nxt) - | NewUnique (Unique -> nxt) + | NewUniqueHash (Int -> nxt) | NewUUID (UUID -> nxt) | ReadFileStrict FilePath (B.ByteString -> nxt) | ReadFileLazy FilePath (BL.ByteString -> nxt) @@ -105,83 +106,71 @@ data PandocActionF ref nxt = (Either E.SomeException (B.ByteString, Maybe MimeType) -> nxt) | FetchItem' MediaBag (Maybe String) (String) (Either E.SomeException (B.ByteString, Maybe MimeType) -> nxt) - | NewIORef ref (IORef ref -> nxt) - | ModifyIORef (IORef ref) (ref -> ref) nxt - | ReadIORef (IORef ref) (ref -> nxt) - | NamesMatching String ([FilePath] -> nxt) + | Glob String ([FilePath] -> nxt) | Warn String nxt | Fail String deriving Functor -type PandocAction a = Free (PandocActionF a) +type PandocAction = Free PandocActionF -lookupEnv :: String -> PandocAction a (Maybe String) +lookupEnv :: String -> PandocAction (Maybe String) lookupEnv s = liftF $ LookupEnv s id -getCurrentTime :: PandocAction a UTCTime +getCurrentTime :: PandocAction UTCTime getCurrentTime = liftF $ GetCurrentTime id -getPOSIXTime :: PandocAction a POSIXTime +getPOSIXTime :: PandocAction POSIXTime getPOSIXTime = liftF $ GetPOSIXTime id -getDefaultReferenceDocx :: Maybe FilePath -> PandocAction a Archive +getDefaultReferenceDocx :: Maybe FilePath -> PandocAction Archive getDefaultReferenceDocx fp = liftF $ GetDefaultReferenceDocx fp id -getDefaultReferenceODT :: Maybe FilePath -> PandocAction a Archive +getDefaultReferenceODT :: Maybe FilePath -> PandocAction Archive getDefaultReferenceODT fp = liftF $ GetDefaultReferenceODT fp id -newStdGen :: PandocAction a StdGen +newStdGen :: PandocAction StdGen newStdGen = liftF $ NewStdGen id -newUnique :: PandocAction a Unique -newUnique = liftF $ NewUnique id +newUniqueHash :: PandocAction Int +newUniqueHash = liftF $ NewUniqueHash id -newUUID :: PandocAction a UUID +newUUID :: PandocAction UUID newUUID = liftF $ NewUUID id -readFileStrict :: FilePath -> PandocAction a B.ByteString +readFileStrict :: FilePath -> PandocAction B.ByteString readFileStrict fp = liftF $ ReadFileStrict fp id -readFileLazy :: FilePath -> PandocAction a BL.ByteString +readFileLazy :: FilePath -> PandocAction BL.ByteString readFileLazy fp = liftF $ ReadFileLazy fp id -readFileUTF8 :: FilePath -> PandocAction a String +readFileUTF8 :: FilePath -> PandocAction String readFileUTF8 fp = liftF $ ReadFileUTF8 fp id -readDataFile :: Maybe FilePath -> FilePath -> PandocAction a B.ByteString +readDataFile :: Maybe FilePath -> FilePath -> PandocAction B.ByteString readDataFile mfp fp = liftF $ ReadDataFile mfp fp id fetchItem :: Maybe String -> String -> - PandocAction a (Either E.SomeException (B.ByteString, Maybe MimeType)) + PandocAction (Either E.SomeException (B.ByteString, Maybe MimeType)) fetchItem ms s = liftF $ FetchItem ms s id fetchItem' :: MediaBag -> Maybe String -> String -> - PandocAction a (Either E.SomeException (B.ByteString, Maybe MimeType)) + PandocAction (Either E.SomeException (B.ByteString, Maybe MimeType)) fetchItem' mb ms s = liftF $ FetchItem' mb ms s id -warn :: String -> PandocAction a () +warn :: String -> PandocAction () warn s = liftF $ Warn s () -fail :: String -> PandocAction a b +fail :: String -> PandocAction b fail s = liftF $ Fail s -newIORef :: a -> PandocAction a (IORef a) -newIORef v = liftF $ NewIORef v id - -modifyIORef :: (IORef a) -> (a -> a) -> PandocAction a () -modifyIORef ref f = liftF $ ModifyIORef ref f () - -readIORef :: (IORef a) -> PandocAction a a -readIORef ref = liftF $ ReadIORef ref id - -namesMatching :: String -> PandocAction a [FilePath] -namesMatching s = liftF $ NamesMatching s id +glob :: String -> PandocAction [FilePath] +glob s = liftF $ Glob s id -runIO :: PandocAction ref nxt -> IO nxt +runIO :: PandocAction nxt -> IO nxt runIO (Free (LookupEnv s f)) = IO.lookupEnv s >>= runIO . f runIO (Free (GetCurrentTime f)) = IO.getCurrentTime >>= runIO . f runIO (Free (GetPOSIXTime f)) = IO.getPOSIXTime >>= runIO . f @@ -190,7 +179,7 @@ runIO (Free (GetDefaultReferenceDocx mfp f)) = runIO (Free (GetDefaultReferenceODT mfp f)) = IO.getDefaultReferenceODT mfp >>= runIO . f runIO (Free (NewStdGen f)) = IO.newStdGen >>= runIO . f -runIO (Free (NewUnique f)) = IO.newUnique >>= runIO . f +runIO (Free (NewUniqueHash f)) = hashUnique <$> IO.newUnique >>= runIO . f runIO (Free (NewUUID f)) = IO.getRandomUUID >>= runIO . f runIO (Free (ReadFileStrict fp f)) = B.readFile fp >>= runIO . f runIO (Free (ReadFileLazy fp f)) = BL.readFile fp >>= runIO . f @@ -202,8 +191,120 @@ runIO (Free (FetchItem sourceUrl nm f)) = runIO (Free (FetchItem' media sourceUrl nm f)) = IO.fetchItem' media sourceUrl nm >>= runIO . f runIO (Free (Warn s nxt)) = IO.warn s >> runIO nxt -runIO (Free (NewIORef v f)) = IO.newIORef v >>= runIO . f -runIO (Free (ModifyIORef ref f nxt)) = IO.modifyIORef ref f >> runIO nxt -runIO (Free (ReadIORef ref f)) = IO.readIORef ref >>= runIO . f -runIO (Free (NamesMatching s f)) = IO.namesMatching s >>= runIO . f +runIO (Free (Glob s f)) = IO.glob s >>= runIO . f runIO (Pure r) = return r + +data TestState = TestState { stStdGen :: StdGen + , stWord8Store :: [Word8] -- should be + -- inifinite, + -- i.e. [1..] + , stWarnings :: [String] + , stUniqStore :: [Int] -- should be + -- inifinite and + -- contain every + -- element at most + -- once, e.g. [1..] + } + +data TestEnv = TestEnv { envEnv :: [(String, String)] + , envTime :: UTCTime + , envReferenceDocx :: Archive + , envReferenceODT :: Archive + , envFiles :: [(FilePath, B.ByteString)] + , envUserDataDir :: [(FilePath, B.ByteString)] + , envCabalDataDir :: [(FilePath, B.ByteString)] + , envFontFiles :: [FilePath] + } + +data TestException = TestException + deriving (Show) + +instance E.Exception TestException + +type Testing = ReaderT TestEnv (State TestState) + +runTest :: PandocAction nxt -> Testing nxt +runTest (Free (LookupEnv s f)) = do + env <- asks envEnv + return (lookup s env) >>= runTest . f +runTest (Free (GetCurrentTime f)) = + asks envTime >>= runTest . f +runTest (Free (GetPOSIXTime f)) = + (utcTimeToPOSIXSeconds <$> asks envTime) >>= runTest . f +runTest (Free (GetDefaultReferenceDocx _ f)) = + asks envReferenceDocx >>= runTest . f +runTest (Free (GetDefaultReferenceODT _ f)) = + asks envReferenceODT >>= runTest . f +runTest (Free (NewStdGen f)) = do + g <- gets stStdGen + let (_, nxtGen) = next g + modify $ \st -> st { stStdGen = nxtGen } + return g >>= runTest . f +runTest (Free (NewUniqueHash f)) = do + uniqs <- gets stUniqStore + case uniqs of + u : us -> do + modify $ \st -> st { stUniqStore = us } + return u >>= runTest . f + _ -> M.fail "uniq store ran out of elements" +runTest (Free (NewUUID f)) = do + word8s <- gets stWord8Store + case word8s of + -- note we use f' because f is a param of the function + a:b:c:d:e:f':g:h:i:j:k:l:m:n:o:p:remaining -> do + modify $ \st -> st { stWord8Store = remaining } + return (UUID a b c d e f' g h i j k l m n o p) >>= runTest . f + _ -> M.fail "word8 supply was not infinite" +runTest (Free (ReadFileStrict fp f)) = do + fps <- asks envFiles + case lookup fp fps of + Just bs -> return bs >>= runTest . f + Nothing -> error "openFile: does not exist" +runTest (Free (ReadFileLazy fp f)) = do + fps <- asks envFiles + case lookup fp fps of + Just bs -> return (BL.fromStrict bs) >>= runTest . f + Nothing -> error "openFile: does not exist" +runTest (Free (ReadFileUTF8 fp f)) = do + fps <- asks envFiles + case lookup fp fps of + Just bs -> return (UTF8.toString bs) >>= runTest . f + Nothing -> error "openFile: does not exist" +-- A few different cases of readDataFile to reimplement, for when +-- there is no filepath and it falls through to readDefaultDataFile +runTest (Free (ReadDataFile Nothing "reference.docx" f)) = do + (B.concat . BL.toChunks . fromArchive) <$> + (runTest $ getDefaultReferenceDocx Nothing) >>= + runTest . f +runTest (Free (ReadDataFile Nothing "reference.odt" f)) = do + (B.concat . BL.toChunks . fromArchive) <$> + (runTest $ getDefaultReferenceODT Nothing) >>= + runTest . f +runTest (Free (ReadDataFile Nothing fname f)) = do + let fname' = if fname == "MANUAL.txt" then fname else "data" fname + runTest (readFileStrict fname') >>= runTest . f +runTest (Free (ReadDataFile (Just userDir) fname f)) = do + userDirFiles <- asks envUserDataDir + case lookup (userDir fname) userDirFiles of + Just bs -> return bs >>= runTest . f + Nothing -> runTest (readDataFile Nothing fname) >>= runTest . f +runTest (Free (Fail s)) = M.fail s +runTest (Free (FetchItem _ fp f)) = do + fps <- asks envFiles + case lookup fp fps of + Just bs -> return (Right (bs, getMimeType fp)) >>= runTest . f + Nothing -> return (Left $ E.toException TestException) >>= runTest . f +runTest (Free (FetchItem' media sourceUrl nm f)) = do + case lookupMedia nm media of + Nothing -> runTest (fetchItem sourceUrl nm) >>= runTest . f + Just (mime, bs) -> return (Right (B.concat $ BL.toChunks bs, Just mime)) >>= runTest . f +runTest (Free (Warn s nxt)) = do + modify $ \st -> st { stWarnings = s : stWarnings st } + runTest nxt +runTest (Free (Glob s f)) = do + fontFiles <- asks envFontFiles + return (filter (match (compile s)) fontFiles) >>= runTest . f +runTest (Pure r) = return r + + + diff --git a/src/Text/Pandoc/UUID.hs b/src/Text/Pandoc/UUID.hs index 5d05fa303..6d6e22944 100644 --- a/src/Text/Pandoc/UUID.hs +++ b/src/Text/Pandoc/UUID.hs @@ -29,7 +29,7 @@ UUID generation using Version 4 (random method) described in RFC4122. See http://tools.ietf.org/html/rfc4122 -} -module Text.Pandoc.UUID ( UUID, getRandomUUID ) where +module Text.Pandoc.UUID ( UUID(..), getRandomUUID ) where import Text.Printf ( printf ) import System.Random ( randomIO ) diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index cecee7e9e..3f380a3ee 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -55,7 +55,6 @@ import Text.Pandoc.Readers.Docx.Util (elemName) import Control.Monad.Reader import Control.Monad.State import Skylighting -import Data.Unique (hashUnique, newUnique) import System.Random (randomR) import Text.Printf (printf) import qualified Control.Exception as E @@ -69,8 +68,6 @@ import Data.Char (ord, isSpace, toLower) import Text.Pandoc.Free (PandocAction, runIO) import qualified Text.Pandoc.Free as P -type DocxAction = PandocAction () - data ListMarker = NoMarker | BulletMarker | NumberMarker ListNumberStyle ListNumberDelim Int @@ -149,7 +146,7 @@ defaultWriterState = WriterState{ , stDynamicTextProps = [] } -type WS = ReaderT WriterEnv (StateT WriterState (DocxAction)) +type WS = ReaderT WriterEnv (StateT WriterState (PandocAction)) mknode :: Node t => String -> [(String,String)] -> t -> Element mknode s attrs = @@ -227,7 +224,7 @@ writeDocx opts doc = runIO $ writeDocxPure opts doc -- | Produce an Docx file from a Pandoc document. writeDocxPure :: WriterOptions -- ^ Writer options -> Pandoc -- ^ Document to convert - -> DocxAction BL.ByteString + -> PandocAction BL.ByteString writeDocxPure opts doc@(Pandoc meta _) = do let datadir = writerUserDataDir opts let doc' = walk fixDisplayMath $ doc @@ -614,7 +611,7 @@ styleToOpenXml sm style = $ backgroundColor style ) ] -copyChildren :: Archive -> Archive -> String -> Integer -> [String] -> DocxAction Entry +copyChildren :: Archive -> Archive -> String -> Integer -> [String] -> PandocAction Entry copyChildren refArchive distArchive path timestamp elNames = do ref <- parseXml refArchive distArchive path dist <- parseXml distArchive distArchive path @@ -633,7 +630,7 @@ copyChildren refArchive distArchive path timestamp elNames = do baseListId :: Int baseListId = 1000 -mkNumbering :: [ListMarker] -> DocxAction [Element] +mkNumbering :: [ListMarker] -> PandocAction [Element] mkNumbering lists = do elts <- mapM mkAbstractNum (ordNub lists) return $ elts ++ zipWith mkNum lists [baseListId..(baseListId + length lists - 1)] @@ -649,7 +646,7 @@ mkNum marker numid = map (\lvl -> mknode "w:lvlOverride" [("w:ilvl",show (lvl :: Int))] $ mknode "w:startOverride" [("w:val",show start)] ()) [0..6] -mkAbstractNum :: ListMarker -> DocxAction Element +mkAbstractNum :: ListMarker -> PandocAction Element mkAbstractNum marker = do gen <- P.newStdGen let (nsid, _) = randomR (0x10000000 :: Integer, 0xFFFFFFFF :: Integer) gen @@ -794,10 +791,10 @@ rStyleM styleName = do let sty' = getStyleId styleName $ sCharStyleMap styleMaps return $ mknode "w:rStyle" [("w:val",sty')] () -getUniqueId :: DocxAction String +getUniqueId :: PandocAction String -- the + 20 is to ensure that there are no clashes with the rIds -- already in word/document.xml.rel -getUniqueId = (show . (+ 20) . hashUnique) <$> P.newUnique +getUniqueId = (show . (+ 20)) <$> P.newUniqueHash -- | Key for specifying user-defined docx styles. dynamicStyleKey :: String @@ -1284,7 +1281,7 @@ defaultFootnotes = [ mknode "w:footnote" [ mknode "w:r" [] $ [ mknode "w:continuationSeparator" [] ()]]]] -parseXml :: Archive -> Archive -> String -> DocxAction Element +parseXml :: Archive -> Archive -> String -> PandocAction Element parseXml refArchive distArchive relpath = case findEntryByPath relpath refArchive `mplus` findEntryByPath relpath distArchive of diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index 8e283a66a..435893443 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -66,8 +66,6 @@ import Text.HTML.TagSoup (Tag(TagOpen), fromAttrib, parseTags) import Text.Pandoc.Free (PandocAction, runIO) import qualified Text.Pandoc.Free as P -type EPUBAction = PandocAction [(FilePath, (FilePath, Maybe Entry))] - -- A Chapter includes a list of blocks and maybe a section -- number offset. Note, some chapters are unnumbered. The section -- number is different from the index number, which will be used @@ -77,7 +75,7 @@ data Chapter = Chapter (Maybe [Int]) [Block] data EPUBState = EPUBState { stMediaPaths :: [(FilePath, (FilePath, Maybe Entry))] } -type E = StateT EPUBState EPUBAction +type E = StateT EPUBState PandocAction data EPUBMetadata = EPUBMetadata{ epubIdentifier :: [Identifier] @@ -343,7 +341,7 @@ writeEPUB opts doc = runIO $ writeEPUBPure opts doc writeEPUBPure :: WriterOptions -- ^ Writer options -> Pandoc -- ^ Document to convert - -> EPUBAction B.ByteString + -> PandocAction B.ByteString writeEPUBPure opts doc = let initState = EPUBState { stMediaPaths = [] } @@ -398,7 +396,7 @@ pandocToEPUB opts doc@(Pandoc meta _) = do picEntries <- (catMaybes . map (snd . snd)) <$> (gets stMediaPaths) -- handle fonts let matchingGlob f = do - xs <- lift $ P.namesMatching f + xs <- lift $ P.glob f when (null xs) $ lift $ P.warn $ f ++ " did not match any font files." return xs diff --git a/src/Text/Pandoc/Writers/ICML.hs b/src/Text/Pandoc/Writers/ICML.hs index 3a1e772ce..186bf0c8d 100644 --- a/src/Text/Pandoc/Writers/ICML.hs +++ b/src/Text/Pandoc/Writers/ICML.hs @@ -28,11 +28,9 @@ import Data.Text as Text (breakOnAll, pack) import Control.Monad.State import Network.URI (isURI) import qualified Data.Set as Set -import Text.Pandoc.Free (runIO) +import Text.Pandoc.Free (runIO, PandocAction) import qualified Text.Pandoc.Free as P -type ICMLAction = P.PandocAction () - type Style = [String] type Hyperlink = [(Int, String)] @@ -44,7 +42,7 @@ data WriterState = WriterState{ , maxListDepth :: Int } -type WS a = StateT WriterState ICMLAction a +type WS a = StateT WriterState PandocAction a defaultWriterState :: WriterState defaultWriterState = WriterState{ @@ -130,7 +128,7 @@ writeICML :: WriterOptions -> Pandoc -> IO String writeICML opts doc = runIO $ writeICMLPure opts doc -- | Convert Pandoc document to string in ICML format. -writeICMLPure :: WriterOptions -> Pandoc -> ICMLAction String +writeICMLPure :: WriterOptions -> Pandoc -> PandocAction String writeICMLPure opts (Pandoc meta blocks) = do let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index b139695db..561230b15 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -53,12 +53,10 @@ import System.FilePath ( takeExtension, takeDirectory, (<.>)) import Text.Pandoc.Free ( PandocAction, runIO ) import qualified Text.Pandoc.Free as P -type ODTAction = PandocAction [Entry] - data ODTState = ODTState { stEntries :: [Entry] } -type O = StateT ODTState ODTAction +type O = StateT ODTState PandocAction -- | Produce an ODT file from a Pandoc document. writeODT :: WriterOptions -- ^ Writer options @@ -68,7 +66,7 @@ writeODT opts doc = runIO $ writeODTPure opts doc writeODTPure :: WriterOptions -> Pandoc - -> ODTAction B.ByteString + -> PandocAction B.ByteString writeODTPure opts doc = let initState = ODTState{ stEntries = [] } -- cgit v1.2.3 From 590e119df08cde755c4aeeb7469776b7461c52fd Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Thu, 17 Nov 2016 13:22:15 -0500 Subject: Fix up compiler warnings. Export TestState and TestEnv, and remove redundant import. --- src/Text/Pandoc/Free.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Free.hs b/src/Text/Pandoc/Free.hs index eb42b45c2..12ab95898 100644 --- a/src/Text/Pandoc/Free.hs +++ b/src/Text/Pandoc/Free.hs @@ -34,6 +34,8 @@ module Text.Pandoc.Free ( PandocActionF(..) , PandocAction , runIO , runTest + , TestState(..) + , TestEnv(..) , liftF -- , lookupEnv @@ -60,7 +62,7 @@ import qualified Control.Monad as M (fail) import System.Random (StdGen, next) import qualified System.Random as IO (newStdGen) import Codec.Archive.Zip (Archive, fromArchive) -import Data.Unique (Unique, hashUnique, newUnique) +import Data.Unique (hashUnique) import qualified Data.Unique as IO (newUnique) import qualified Text.Pandoc.Shared as IO ( fetchItem , fetchItem' -- cgit v1.2.3 From 9ac1303660bc271054137d313b2c54bae60a59d4 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Thu, 17 Nov 2016 13:22:39 -0500 Subject: Make pure rtf writer using free. --- src/Text/Pandoc/Writers/RTF.hs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/RTF.hs b/src/Text/Pandoc/Writers/RTF.hs index 6ca749a10..8d7c643e0 100644 --- a/src/Text/Pandoc/Writers/RTF.hs +++ b/src/Text/Pandoc/Writers/RTF.hs @@ -27,7 +27,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Conversion of 'Pandoc' documents to RTF (rich text format). -} -module Text.Pandoc.Writers.RTF ( writeRTF, writeRTFWithEmbeddedImages ) where +module Text.Pandoc.Writers.RTF ( writeRTF + , writeRTFWithEmbeddedImages + , writeRTFWithEmbeddedImagesPure + ) where import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Shared @@ -41,13 +44,15 @@ import qualified Data.ByteString as B import qualified Data.Map as M import Text.Printf ( printf ) import Text.Pandoc.ImageSize +import Text.Pandoc.Free (PandocAction, runIO) +import qualified Text.Pandoc.Free as P -- | Convert Image inlines into a raw RTF embedded image, read from a file, -- or a MediaBag, or the internet. -- If file not found or filetype not jpeg or png, leave the inline unchanged. -rtfEmbedImage :: WriterOptions -> Inline -> IO Inline +rtfEmbedImage :: WriterOptions -> Inline -> PandocAction Inline rtfEmbedImage opts x@(Image attr _ (src,_)) = do - result <- fetchItem' (writerMediaBag opts) (writerSourceURL opts) src + result <- P.fetchItem' (writerMediaBag opts) (writerSourceURL opts) src case result of Right (imgdata, Just mime) | mime == "image/jpeg" || mime == "image/png" -> do @@ -58,7 +63,7 @@ rtfEmbedImage opts x@(Image attr _ (src,_)) = do _ -> error "Unknown file type" sizeSpec <- case imageSize imgdata of Left msg -> do - warn $ "Could not determine image size in `" ++ + P.warn $ "Could not determine image size in `" ++ src ++ "': " ++ msg return "" Right sz -> return $ "\\picw" ++ show xpx ++ @@ -80,6 +85,10 @@ rtfEmbedImage _ x = return x -- images embedded as encoded binary data. writeRTFWithEmbeddedImages :: WriterOptions -> Pandoc -> IO String writeRTFWithEmbeddedImages options doc = + runIO $ writeRTF options `fmap` walkM (rtfEmbedImage options) doc + +writeRTFWithEmbeddedImagesPure :: WriterOptions -> Pandoc -> PandocAction String +writeRTFWithEmbeddedImagesPure options doc = writeRTF options `fmap` walkM (rtfEmbedImage options) doc -- | Convert Pandoc to a string in rich text format. -- cgit v1.2.3 From d97fb5f3c600e9171bb80a7dde358282580da9ea Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Fri, 18 Nov 2016 06:18:12 -0500 Subject: FB2 writer: bring functions to toplevel. This is the first of a number of changes to bring the FB2 writer a bit closer to the idioms used elsewhere in pandoc, so it can be more easily converted to using the pure functions from Free. --- src/Text/Pandoc/Writers/FB2.hs | 100 ++++++++++++++++++++++------------------- 1 file changed, 53 insertions(+), 47 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index 8c4817ac6..70044bd96 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -94,53 +94,59 @@ writeFB2 opts (Pandoc meta blocks) = flip evalStateT newFB $ do xlink = "http://www.w3.org/1999/xlink" in [ uattr "xmlns" xmlns , attr ("xmlns", "l") xlink ] - -- - frontpage :: Meta -> FBM [Content] - frontpage meta' = do - t <- cMapM toXml . docTitle $ meta' - return $ - [ el "title" (el "p" t) - , el "annotation" (map (el "p" . cMap plain) - (docAuthors meta' ++ [docDate meta'])) - ] - description :: Meta -> FBM Content - description meta' = do - bt <- booktitle meta' - let as = authors meta' - dd <- docdate meta' - return $ el "description" - [ el "title-info" (bt ++ as ++ dd) - , el "document-info" [ el "program-used" "pandoc" ] -- FIXME: +version - ] - booktitle :: Meta -> FBM [Content] - booktitle meta' = do - t <- cMapM toXml . docTitle $ meta' - return $ if null t - then [] - else [ el "book-title" t ] - authors :: Meta -> [Content] - authors meta' = cMap author (docAuthors meta') - author :: [Inline] -> [Content] - author ss = - let ws = words . cMap plain $ ss - email = (el "email") `fmap` (take 1 $ filter ('@' `elem`) ws) - ws' = filter ('@' `notElem`) ws - names = case ws' of - (nickname:[]) -> [ el "nickname" nickname ] - (fname:lname:[]) -> [ el "first-name" fname - , el "last-name" lname ] - (fname:rest) -> [ el "first-name" fname - , el "middle-name" (concat . init $ rest) - , el "last-name" (last rest) ] - ([]) -> [] - in list $ el "author" (names ++ email) - docdate :: Meta -> FBM [Content] - docdate meta' = do - let ss = docDate meta' - d <- cMapM toXml ss - return $ if null d - then [] - else [el "date" d] + + +frontpage :: Meta -> FBM [Content] +frontpage meta' = do + t <- cMapM toXml . docTitle $ meta' + return $ + [ el "title" (el "p" t) + , el "annotation" (map (el "p" . cMap plain) + (docAuthors meta' ++ [docDate meta'])) + ] + +description :: Meta -> FBM Content +description meta' = do + bt <- booktitle meta' + let as = authors meta' + dd <- docdate meta' + return $ el "description" + [ el "title-info" (bt ++ as ++ dd) + , el "document-info" [ el "program-used" "pandoc" ] -- FIXME: +version + ] + +booktitle :: Meta -> FBM [Content] +booktitle meta' = do + t <- cMapM toXml . docTitle $ meta' + return $ if null t + then [] + else [ el "book-title" t ] + +authors :: Meta -> [Content] +authors meta' = cMap author (docAuthors meta') + +author :: [Inline] -> [Content] +author ss = + let ws = words . cMap plain $ ss + email = (el "email") `fmap` (take 1 $ filter ('@' `elem`) ws) + ws' = filter ('@' `notElem`) ws + names = case ws' of + (nickname:[]) -> [ el "nickname" nickname ] + (fname:lname:[]) -> [ el "first-name" fname + , el "last-name" lname ] + (fname:rest) -> [ el "first-name" fname + , el "middle-name" (concat . init $ rest) + , el "last-name" (last rest) ] + ([]) -> [] + in list $ el "author" (names ++ email) + +docdate :: Meta -> FBM [Content] +docdate meta' = do + let ss = docDate meta' + d <- cMapM toXml ss + return $ if null d + then [] + else [el "date" d] -- | Divide the stream of blocks into sections and convert to XML -- representation. -- cgit v1.2.3 From 30cfda7a71cd8397dc8d19c9b53bed39d5c1afa1 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Fri, 18 Nov 2016 09:41:36 -0500 Subject: Continue refactoring FB2 writer. --- src/Text/Pandoc/Writers/FB2.hs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index 70044bd96..41ad9bb2d 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -76,8 +76,13 @@ instance Show ImageMode where writeFB2 :: WriterOptions -- ^ conversion options -> Pandoc -- ^ document to convert -> IO String -- ^ FictionBook2 document (not encoded yet) -writeFB2 opts (Pandoc meta blocks) = flip evalStateT newFB $ do - modify (\s -> s { writerOptions = opts }) +writeFB2 opts doc = flip evalStateT newFB $ pandocToFB2 opts doc + +pandocToFB2 :: WriterOptions + -> Pandoc + -> FBM String +pandocToFB2 opts (Pandoc meta blocks) = do + modify (\s -> s { writerOptions = opts { writerOptions = opts } }) desc <- description meta fp <- frontpage meta secs <- renderSections 1 blocks @@ -95,7 +100,6 @@ writeFB2 opts (Pandoc meta blocks) = flip evalStateT newFB $ do in [ uattr "xmlns" xmlns , attr ("xmlns", "l") xlink ] - frontpage :: Meta -> FBM [Content] frontpage meta' = do t <- cMapM toXml . docTitle $ meta' @@ -250,11 +254,13 @@ fetchImage href link = do , uattr "content-type" imgtype] , txt imgdata ) _ -> return (Left ('#':href)) - where - nothingOnError :: (IO B.ByteString) -> (IO (Maybe B.ByteString)) - nothingOnError action = liftM Just action `E.catch` omnihandler - omnihandler :: E.SomeException -> IO (Maybe B.ByteString) - omnihandler _ = return Nothing + + +nothingOnError :: (IO B.ByteString) -> (IO (Maybe B.ByteString)) +nothingOnError action = liftM Just action `E.catch` omnihandler + +omnihandler :: E.SomeException -> IO (Maybe B.ByteString) +omnihandler _ = return Nothing -- | Extract mime type and encoded data from the Data URI. readDataURI :: String -- ^ URI -- cgit v1.2.3 From e711043dee212ced02323591623261ef743c5f2a Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Fri, 18 Nov 2016 16:35:36 -0500 Subject: FB2 writer: Rewrite image-fetching to use fetchItem. This uses the function from shared, which will allow us to convert it over to the free monad. --- src/Text/Pandoc/Writers/FB2.hs | 54 +++++++++--------------------------------- 1 file changed, 11 insertions(+), 43 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index 41ad9bb2d..f03fe5c7e 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -28,26 +28,23 @@ FictionBook is an XML-based e-book format. For more information see: module Text.Pandoc.Writers.FB2 (writeFB2) where import Control.Monad.State (StateT, evalStateT, get, modify) -import Control.Monad.State (liftM, liftM2, liftIO) +import Control.Monad.State (liftM, liftIO) import Data.ByteString.Base64 (encode) import Data.Char (toLower, isSpace, isAscii, isControl) import Data.List (intersperse, intercalate, isPrefixOf, stripPrefix) import Data.Either (lefts, rights) -import Network.Browser (browse, request, setAllowRedirects, setOutHandler) -import Network.HTTP (catchIO_, getRequest, getHeaders, getResponseBody) -import Network.HTTP (lookupHeader, HeaderName(..), urlEncode) -import Network.URI (isURI, unEscapeString) -import System.FilePath (takeExtension) +import Network.HTTP (urlEncode) +import Network.URI (isURI) import Text.XML.Light -import qualified Control.Exception as E -import qualified Data.ByteString as B import qualified Text.XML.Light as X import qualified Text.XML.Light.Cursor as XC +import qualified Data.ByteString.Char8 as B8 + import Text.Pandoc.Definition import Text.Pandoc.Options (WriterOptions(..), HTMLMathMethod(..), def) import Text.Pandoc.Shared (orderedListMarkers, isHeaderBlock, capitalize, - linesToPara) + linesToPara, fetchItem) -- | Data to be written at the end of the document: -- (foot)notes, URLs, references, images. @@ -237,16 +234,11 @@ fetchImage href link = do then return (Just (mime',base64)) else return Nothing (True, Just _) -> return Nothing -- not base64-encoded - (True, Nothing) -> fetchURL link - (False, _) -> do - d <- nothingOnError $ B.readFile (unEscapeString link) - let t = case map toLower (takeExtension link) of - ".png" -> Just "image/png" - ".jpg" -> Just "image/jpeg" - ".jpeg" -> Just "image/jpeg" - ".jpe" -> Just "image/jpeg" - _ -> Nothing -- only PNG and JPEG are supported in FB2 - return $ liftM2 (,) t (liftM (toStr . encode) d) + _ -> do + response <- fetchItem Nothing link + case response of + Right (bs, Just mime) -> return $ Just (mime, B8.unpack $ encode bs) + _ -> return $ Nothing case mbimg of Just (imgtype, imgdata) -> do return . Right $ el "binary" @@ -256,12 +248,6 @@ fetchImage href link = do _ -> return (Left ('#':href)) -nothingOnError :: (IO B.ByteString) -> (IO (Maybe B.ByteString)) -nothingOnError action = liftM Just action `E.catch` omnihandler - -omnihandler :: E.SomeException -> IO (Maybe B.ByteString) -omnihandler _ = return Nothing - -- | Extract mime type and encoded data from the Data URI. readDataURI :: String -- ^ URI -> Maybe (String,String,Bool,String) @@ -298,24 +284,6 @@ isMimeType s = valid c = isAscii c && not (isControl c) && not (isSpace c) && c `notElem` "()<>@,;:\\\"/[]?=" --- | Fetch URL, return its Content-Type and binary data on success. -fetchURL :: String -> IO (Maybe (String, String)) -fetchURL url = do - flip catchIO_ (return Nothing) $ do - r <- browse $ do - setOutHandler (const (return ())) - setAllowRedirects True - liftM snd . request . getRequest $ url - let content_type = lookupHeader HdrContentType (getHeaders r) - content <- liftM (Just . toStr . encode . toBS) . getResponseBody $ Right r - return $ liftM2 (,) content_type content - -toBS :: String -> B.ByteString -toBS = B.pack . map (toEnum . fromEnum) - -toStr :: B.ByteString -> String -toStr = map (toEnum . fromEnum) . B.unpack - footnoteID :: Int -> String footnoteID i = "n" ++ (show i) -- cgit v1.2.3 From 2ea3e77172837505f021ae014c898a244bd9c436 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Fri, 18 Nov 2016 16:54:15 -0500 Subject: Finish pure writer of FB2. --- src/Text/Pandoc/Writers/FB2.hs | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index f03fe5c7e..3c4970e75 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -25,10 +25,10 @@ FictionBook is an XML-based e-book format. For more information see: -} -module Text.Pandoc.Writers.FB2 (writeFB2) where +module Text.Pandoc.Writers.FB2 (writeFB2, writeFB2Pure) where -import Control.Monad.State (StateT, evalStateT, get, modify) -import Control.Monad.State (liftM, liftIO) +import Control.Monad.State (StateT, evalStateT, get, modify, lift) +import Control.Monad.State (liftM) import Data.ByteString.Base64 (encode) import Data.Char (toLower, isSpace, isAscii, isControl) import Data.List (intersperse, intercalate, isPrefixOf, stripPrefix) @@ -44,7 +44,9 @@ import qualified Data.ByteString.Char8 as B8 import Text.Pandoc.Definition import Text.Pandoc.Options (WriterOptions(..), HTMLMathMethod(..), def) import Text.Pandoc.Shared (orderedListMarkers, isHeaderBlock, capitalize, - linesToPara, fetchItem) + linesToPara) +import Text.Pandoc.Free (PandocAction, runIO) +import qualified Text.Pandoc.Free as P -- | Data to be written at the end of the document: -- (foot)notes, URLs, references, images. @@ -57,7 +59,7 @@ data FbRenderState = FbRenderState } deriving (Show) -- | FictionBook building monad. -type FBM = StateT FbRenderState IO +type FBM = StateT FbRenderState PandocAction newFB :: FbRenderState newFB = FbRenderState { footnotes = [], imagesToFetch = [] @@ -73,7 +75,12 @@ instance Show ImageMode where writeFB2 :: WriterOptions -- ^ conversion options -> Pandoc -- ^ document to convert -> IO String -- ^ FictionBook2 document (not encoded yet) -writeFB2 opts doc = flip evalStateT newFB $ pandocToFB2 opts doc +writeFB2 opts doc = runIO $ writeFB2Pure opts doc + +writeFB2Pure :: WriterOptions + -> Pandoc + -> PandocAction String +writeFB2Pure opts doc = flip evalStateT newFB $ pandocToFB2 opts doc pandocToFB2 :: WriterOptions -> Pandoc @@ -85,7 +92,7 @@ pandocToFB2 opts (Pandoc meta blocks) = do secs <- renderSections 1 blocks let body = el "body" $ fp ++ secs notes <- renderFootnotes - (imgs,missing) <- liftM imagesToFetch get >>= \s -> liftIO (fetchImages s) + (imgs,missing) <- liftM imagesToFetch get >>= \s -> lift (fetchImages s) let body' = replaceImagesWithAlt missing body let fb2_xml = el "FictionBook" (fb2_attrs, [desc, body'] ++ notes ++ imgs) return $ xml_head ++ (showContent fb2_xml) ++ "\n" @@ -217,14 +224,14 @@ renderFootnotes = do -- | Fetch images and encode them for the FictionBook XML. -- Return image data and a list of hrefs of the missing images. -fetchImages :: [(String,String)] -> IO ([Content],[String]) +fetchImages :: [(String,String)] -> PandocAction ([Content],[String]) fetchImages links = do imgs <- mapM (uncurry fetchImage) links return $ (rights imgs, lefts imgs) -- | Fetch image data from disk or from network and make a XML section. -- Return either (Left hrefOfMissingImage) or (Right xmlContent). -fetchImage :: String -> String -> IO (Either String Content) +fetchImage :: String -> String -> PandocAction (Either String Content) fetchImage href link = do mbimg <- case (isURI link, readDataURI link) of @@ -235,7 +242,7 @@ fetchImage href link = do else return Nothing (True, Just _) -> return Nothing -- not base64-encoded _ -> do - response <- fetchItem Nothing link + response <- P.fetchItem Nothing link case response of Right (bs, Just mime) -> return $ Just (mime, B8.unpack $ encode bs) _ -> return $ Nothing -- cgit v1.2.3 From f404412331bc6cf06c2cc248266f769391a57479 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Fri, 18 Nov 2016 20:39:26 -0500 Subject: Free: Add Typeable instance to PandocActionError --- src/Text/Pandoc/Free.hs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Free.hs b/src/Text/Pandoc/Free.hs index 12ab95898..33cb50c88 100644 --- a/src/Text/Pandoc/Free.hs +++ b/src/Text/Pandoc/Free.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE DeriveFunctor #-} +{-# LANGUAGE DeriveFunctor, DeriveDataTypeable #-} {- Copyright (C) 2016 Jesse Rosenthal @@ -90,6 +90,7 @@ import qualified System.FilePath.Glob as IO (glob) import Control.Monad.State hiding (fail) import Control.Monad.Reader hiding (fail) import Data.Word (Word8) +import Data.Typeable data PandocActionF nxt = LookupEnv String (Maybe String -> nxt) @@ -219,7 +220,7 @@ data TestEnv = TestEnv { envEnv :: [(String, String)] } data TestException = TestException - deriving (Show) + deriving (Show, Typeable) instance E.Exception TestException -- cgit v1.2.3 From 1c589c51b13aa6833cf6246b514ce8ddadf25dd5 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Fri, 18 Nov 2016 20:54:43 -0500 Subject: ODT Writer: fix compiler complaint. --- src/Text/Pandoc/Writers/ODT.hs | 1 - 1 file changed, 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index 561230b15..abd403cc9 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -44,7 +44,6 @@ import Text.Pandoc.Definition import Text.Pandoc.Walk import Text.Pandoc.Writers.Shared ( fixDisplayMath ) import Text.Pandoc.Writers.OpenDocument ( writeOpenDocument ) -import Control.Monad (liftM) import Control.Monad.State import Text.Pandoc.XML import Text.Pandoc.Pretty -- cgit v1.2.3 From 8b144db6e575e56205de881d8ae233fe2ff828da Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Sat, 19 Nov 2016 05:31:32 -0500 Subject: Write Pure uuid function taking stdgen. We're trying to cut down the necessarily IO functions. Since we alerady have a newStdGen function, we don't need this one. --- src/Text/Pandoc/UUID.hs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/UUID.hs b/src/Text/Pandoc/UUID.hs index 6d6e22944..9d8cd4434 100644 --- a/src/Text/Pandoc/UUID.hs +++ b/src/Text/Pandoc/UUID.hs @@ -29,13 +29,12 @@ UUID generation using Version 4 (random method) described in RFC4122. See http://tools.ietf.org/html/rfc4122 -} -module Text.Pandoc.UUID ( UUID(..), getRandomUUID ) where +module Text.Pandoc.UUID ( UUID(..), getRandomUUID, getUUID ) where import Text.Printf ( printf ) -import System.Random ( randomIO ) +import System.Random ( RandomGen, randoms, getStdGen ) import Data.Word import Data.Bits ( setBit, clearBit ) -import Control.Monad ( liftM ) data UUID = UUID Word8 Word8 Word8 Word8 Word8 Word8 Word8 Word8 Word8 Word8 Word8 Word8 Word8 Word8 Word8 Word8 @@ -64,14 +63,16 @@ instance Show UUID where printf "%02x" o ++ printf "%02x" p -getRandomUUID :: IO UUID -getRandomUUID = do - let getRN :: a -> IO Word8 - getRN _ = liftM fromIntegral (randomIO :: IO Int) - [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p] <- mapM getRN ([1..16] :: [Int]) +getUUID :: RandomGen g => g -> UUID +getUUID gen = + let [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p] = take 16 $ randoms gen :: [Word8] -- set variant - let i' = i `setBit` 7 `clearBit` 6 + i' = i `setBit` 7 `clearBit` 6 -- set version (0100 for random) - let g' = g `clearBit` 7 `setBit` 6 `clearBit` 5 `clearBit` 4 - return $ UUID a b c d e f g' h i' j k l m n o p + g' = g `clearBit` 7 `setBit` 6 `clearBit` 5 `clearBit` 4 + in + UUID a b c d e f g' h i' j k l m n o p + +getRandomUUID :: IO UUID +getRandomUUID = getUUID <$> getStdGen -- cgit v1.2.3 From c9e67163fd08f7eb1ef18aed47d7fab4614653b6 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Sat, 19 Nov 2016 05:42:00 -0500 Subject: Remove IO UUID functions. --- src/Text/Pandoc/Free.hs | 16 ---------------- src/Text/Pandoc/Writers/EPUB.hs | 3 ++- 2 files changed, 2 insertions(+), 17 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Free.hs b/src/Text/Pandoc/Free.hs index 33cb50c88..071482e32 100644 --- a/src/Text/Pandoc/Free.hs +++ b/src/Text/Pandoc/Free.hs @@ -45,7 +45,6 @@ module Text.Pandoc.Free ( PandocActionF(..) , getDefaultReferenceODT , newStdGen , newUniqueHash - , newUUID , readFileStrict , readFileLazy , readFileUTF8 @@ -81,8 +80,6 @@ import qualified Data.ByteString.Lazy as BL import Control.Monad.Free import qualified Control.Exception as E import qualified System.Environment as IO (lookupEnv) -import Text.Pandoc.UUID -import qualified Text.Pandoc.UUID as IO (getRandomUUID) import qualified Text.Pandoc.UTF8 as UTF8 (readFile, toString) import System.FilePath.Glob (match, compile) import System.FilePath (()) @@ -100,7 +97,6 @@ data PandocActionF nxt = | GetDefaultReferenceODT (Maybe FilePath) (Archive -> nxt) | NewStdGen (StdGen -> nxt) | NewUniqueHash (Int -> nxt) - | NewUUID (UUID -> nxt) | ReadFileStrict FilePath (B.ByteString -> nxt) | ReadFileLazy FilePath (BL.ByteString -> nxt) | ReadFileUTF8 FilePath (String -> nxt) @@ -137,9 +133,6 @@ newStdGen = liftF $ NewStdGen id newUniqueHash :: PandocAction Int newUniqueHash = liftF $ NewUniqueHash id -newUUID :: PandocAction UUID -newUUID = liftF $ NewUUID id - readFileStrict :: FilePath -> PandocAction B.ByteString readFileStrict fp = liftF $ ReadFileStrict fp id @@ -183,7 +176,6 @@ runIO (Free (GetDefaultReferenceODT mfp f)) = IO.getDefaultReferenceODT mfp >>= runIO . f runIO (Free (NewStdGen f)) = IO.newStdGen >>= runIO . f runIO (Free (NewUniqueHash f)) = hashUnique <$> IO.newUnique >>= runIO . f -runIO (Free (NewUUID f)) = IO.getRandomUUID >>= runIO . f runIO (Free (ReadFileStrict fp f)) = B.readFile fp >>= runIO . f runIO (Free (ReadFileLazy fp f)) = BL.readFile fp >>= runIO . f runIO (Free (ReadFileUTF8 fp f)) = UTF8.readFile fp >>= runIO . f @@ -250,14 +242,6 @@ runTest (Free (NewUniqueHash f)) = do modify $ \st -> st { stUniqStore = us } return u >>= runTest . f _ -> M.fail "uniq store ran out of elements" -runTest (Free (NewUUID f)) = do - word8s <- gets stWord8Store - case word8s of - -- note we use f' because f is a param of the function - a:b:c:d:e:f':g:h:i:j:k:l:m:n:o:p:remaining -> do - modify $ \st -> st { stWord8Store = remaining } - return (UUID a b c d e f' g h i j k l m n o p) >>= runTest . f - _ -> M.fail "word8 supply was not infinite" runTest (Free (ReadFileStrict fp f)) = do fps <- asks envFiles case lookup fp fps of diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index 435893443..35724dfef 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -53,6 +53,7 @@ import Text.Pandoc.Options ( WriterOptions(..) , ObfuscationMethod(NoObfuscation) ) 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 Text.XML.Light ( unode, Element(..), unqual, Attr(..), add_attrs @@ -152,7 +153,7 @@ getEPUBMetadata opts meta = do let addIdentifier m = if null (epubIdentifier m) then do - randomId <- fmap show (lift P.newUUID) + randomId <- (show . getUUID) <$> lift P.newStdGen return $ m{ epubIdentifier = [Identifier randomId Nothing] } else return m let addLanguage m = -- cgit v1.2.3 From 314a4c7296029753872164428667c63642762901 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Sat, 19 Nov 2016 05:57:33 -0500 Subject: Remove readFileStrict. We only used it once, and then immediately converted to lazy. --- src/Text/Pandoc/Free.hs | 13 +------------ src/Text/Pandoc/Writers/Docx.hs | 2 +- 2 files changed, 2 insertions(+), 13 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Free.hs b/src/Text/Pandoc/Free.hs index 071482e32..4294384d4 100644 --- a/src/Text/Pandoc/Free.hs +++ b/src/Text/Pandoc/Free.hs @@ -45,7 +45,6 @@ module Text.Pandoc.Free ( PandocActionF(..) , getDefaultReferenceODT , newStdGen , newUniqueHash - , readFileStrict , readFileLazy , readFileUTF8 , readDataFile @@ -97,7 +96,6 @@ data PandocActionF nxt = | GetDefaultReferenceODT (Maybe FilePath) (Archive -> nxt) | NewStdGen (StdGen -> nxt) | NewUniqueHash (Int -> nxt) - | ReadFileStrict FilePath (B.ByteString -> nxt) | ReadFileLazy FilePath (BL.ByteString -> nxt) | ReadFileUTF8 FilePath (String -> nxt) | ReadDataFile (Maybe FilePath) FilePath (B.ByteString -> nxt) @@ -133,9 +131,6 @@ newStdGen = liftF $ NewStdGen id newUniqueHash :: PandocAction Int newUniqueHash = liftF $ NewUniqueHash id -readFileStrict :: FilePath -> PandocAction B.ByteString -readFileStrict fp = liftF $ ReadFileStrict fp id - readFileLazy :: FilePath -> PandocAction BL.ByteString readFileLazy fp = liftF $ ReadFileLazy fp id @@ -176,7 +171,6 @@ runIO (Free (GetDefaultReferenceODT mfp f)) = IO.getDefaultReferenceODT mfp >>= runIO . f runIO (Free (NewStdGen f)) = IO.newStdGen >>= runIO . f runIO (Free (NewUniqueHash f)) = hashUnique <$> IO.newUnique >>= runIO . f -runIO (Free (ReadFileStrict fp f)) = B.readFile fp >>= runIO . f runIO (Free (ReadFileLazy fp f)) = BL.readFile fp >>= runIO . f runIO (Free (ReadFileUTF8 fp f)) = UTF8.readFile fp >>= runIO . f runIO (Free (ReadDataFile mfp fp f)) = IO.readDataFile mfp fp >>= runIO . f @@ -242,11 +236,6 @@ runTest (Free (NewUniqueHash f)) = do modify $ \st -> st { stUniqStore = us } return u >>= runTest . f _ -> M.fail "uniq store ran out of elements" -runTest (Free (ReadFileStrict fp f)) = do - fps <- asks envFiles - case lookup fp fps of - Just bs -> return bs >>= runTest . f - Nothing -> error "openFile: does not exist" runTest (Free (ReadFileLazy fp f)) = do fps <- asks envFiles case lookup fp fps of @@ -269,7 +258,7 @@ runTest (Free (ReadDataFile Nothing "reference.odt" f)) = do runTest . f runTest (Free (ReadDataFile Nothing fname f)) = do let fname' = if fname == "MANUAL.txt" then fname else "data" fname - runTest (readFileStrict fname') >>= runTest . f + runTest (BL.toStrict <$> readFileLazy fname') >>= runTest . f runTest (Free (ReadDataFile (Just userDir) fname f)) = do userDirFiles <- asks envUserDataDir case lookup (userDir fname) userDirFiles of diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 3f380a3ee..07041f189 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -232,7 +232,7 @@ writeDocxPure opts doc@(Pandoc meta _) = do utctime <- P.getCurrentTime distArchive <- P.getDefaultReferenceDocx datadir refArchive <- case writerReferenceDocx opts of - Just f -> liftM (toArchive . toLazy) $ P.readFileStrict f + Just f -> toArchive <$> P.readFileLazy f Nothing -> P.getDefaultReferenceDocx datadir parsedDoc <- parseXml refArchive distArchive "word/document.xml" -- cgit v1.2.3 From 2ffd630a43749794bf72591f41d6b523676bd5b1 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Sat, 19 Nov 2016 06:03:54 -0500 Subject: Free: Remove readFileUTF8. This is just defined in term of a bytestring, so we convert when necessary. --- src/Text/Pandoc/Free.hs | 12 ------------ src/Text/Pandoc/Writers/EPUB.hs | 2 +- 2 files changed, 1 insertion(+), 13 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Free.hs b/src/Text/Pandoc/Free.hs index 4294384d4..3a62270a7 100644 --- a/src/Text/Pandoc/Free.hs +++ b/src/Text/Pandoc/Free.hs @@ -46,7 +46,6 @@ module Text.Pandoc.Free ( PandocActionF(..) , newStdGen , newUniqueHash , readFileLazy - , readFileUTF8 , readDataFile , fetchItem , fetchItem' @@ -79,7 +78,6 @@ import qualified Data.ByteString.Lazy as BL import Control.Monad.Free import qualified Control.Exception as E import qualified System.Environment as IO (lookupEnv) -import qualified Text.Pandoc.UTF8 as UTF8 (readFile, toString) import System.FilePath.Glob (match, compile) import System.FilePath (()) import qualified System.FilePath.Glob as IO (glob) @@ -97,7 +95,6 @@ data PandocActionF nxt = | NewStdGen (StdGen -> nxt) | NewUniqueHash (Int -> nxt) | ReadFileLazy FilePath (BL.ByteString -> nxt) - | ReadFileUTF8 FilePath (String -> nxt) | ReadDataFile (Maybe FilePath) FilePath (B.ByteString -> nxt) | FetchItem (Maybe String) (String) (Either E.SomeException (B.ByteString, Maybe MimeType) -> nxt) @@ -134,9 +131,6 @@ newUniqueHash = liftF $ NewUniqueHash id readFileLazy :: FilePath -> PandocAction BL.ByteString readFileLazy fp = liftF $ ReadFileLazy fp id -readFileUTF8 :: FilePath -> PandocAction String -readFileUTF8 fp = liftF $ ReadFileUTF8 fp id - readDataFile :: Maybe FilePath -> FilePath -> PandocAction B.ByteString readDataFile mfp fp = liftF $ ReadDataFile mfp fp id @@ -172,7 +166,6 @@ runIO (Free (GetDefaultReferenceODT mfp f)) = runIO (Free (NewStdGen f)) = IO.newStdGen >>= runIO . f runIO (Free (NewUniqueHash f)) = hashUnique <$> IO.newUnique >>= runIO . f runIO (Free (ReadFileLazy fp f)) = BL.readFile fp >>= runIO . f -runIO (Free (ReadFileUTF8 fp f)) = UTF8.readFile fp >>= runIO . f runIO (Free (ReadDataFile mfp fp f)) = IO.readDataFile mfp fp >>= runIO . f runIO (Free (Fail s)) = M.fail s runIO (Free (FetchItem sourceUrl nm f)) = @@ -241,11 +234,6 @@ runTest (Free (ReadFileLazy fp f)) = do case lookup fp fps of Just bs -> return (BL.fromStrict bs) >>= runTest . f Nothing -> error "openFile: does not exist" -runTest (Free (ReadFileUTF8 fp f)) = do - fps <- asks envFiles - case lookup fp fps of - Just bs -> return (UTF8.toString bs) >>= runTest . f - Nothing -> error "openFile: does not exist" -- A few different cases of readDataFile to reimplement, for when -- there is no filepath and it falls through to readDefaultDataFile runTest (Free (ReadDataFile Nothing "reference.docx" f)) = do diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index 35724dfef..a0991e27b 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -710,7 +710,7 @@ pandocToEPUB opts doc@(Pandoc meta _) = do -- stylesheet stylesheet <- case epubStylesheet metadata of - Just (StylesheetPath fp) -> lift $ P.readFileUTF8 fp + Just (StylesheetPath fp) -> UTF8.toStringLazy <$> (lift $ P.readFileLazy fp) Just (StylesheetContents s) -> return s Nothing -> UTF8.toString `fmap` (lift $ P.readDataFile (writerUserDataDir opts) "epub.css") -- cgit v1.2.3 From f22bc52864d753179ae0ac16980fc2be1ba1781d Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Sat, 19 Nov 2016 06:29:58 -0500 Subject: Remove GetPOSIXTime from Free monad. We still export a P.getPOSIXTime function, but it's just internally defined in terms of P.getCurrentTime. --- src/Text/Pandoc/Free.hs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Free.hs b/src/Text/Pandoc/Free.hs index 3a62270a7..a1ea45cd6 100644 --- a/src/Text/Pandoc/Free.hs +++ b/src/Text/Pandoc/Free.hs @@ -68,10 +68,9 @@ import qualified Text.Pandoc.Shared as IO ( fetchItem , warn , readDataFile) import Text.Pandoc.MediaBag (MediaBag, lookupMedia) -import Data.Time.Clock.POSIX (POSIXTime, utcTimeToPOSIXSeconds) -import qualified Data.Time.Clock.POSIX as IO (getPOSIXTime) import Text.Pandoc.Compat.Time (UTCTime) import qualified Text.Pandoc.Compat.Time as IO (getCurrentTime) +import Data.Time.Clock.POSIX ( utcTimeToPOSIXSeconds, POSIXTime ) import Text.Pandoc.MIME (MimeType, getMimeType) import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL @@ -89,7 +88,6 @@ import Data.Typeable data PandocActionF nxt = LookupEnv String (Maybe String -> nxt) | GetCurrentTime (UTCTime -> nxt) - | GetPOSIXTime (POSIXTime -> nxt) | GetDefaultReferenceDocx (Maybe FilePath) (Archive -> nxt) | GetDefaultReferenceODT (Maybe FilePath) (Archive -> nxt) | NewStdGen (StdGen -> nxt) @@ -114,7 +112,7 @@ getCurrentTime :: PandocAction UTCTime getCurrentTime = liftF $ GetCurrentTime id getPOSIXTime :: PandocAction POSIXTime -getPOSIXTime = liftF $ GetPOSIXTime id +getPOSIXTime = utcTimeToPOSIXSeconds <$> getCurrentTime getDefaultReferenceDocx :: Maybe FilePath -> PandocAction Archive getDefaultReferenceDocx fp = liftF $ GetDefaultReferenceDocx fp id @@ -158,7 +156,6 @@ glob s = liftF $ Glob s id runIO :: PandocAction nxt -> IO nxt runIO (Free (LookupEnv s f)) = IO.lookupEnv s >>= runIO . f runIO (Free (GetCurrentTime f)) = IO.getCurrentTime >>= runIO . f -runIO (Free (GetPOSIXTime f)) = IO.getPOSIXTime >>= runIO . f runIO (Free (GetDefaultReferenceDocx mfp f)) = IO.getDefaultReferenceDocx mfp >>= runIO . f runIO (Free (GetDefaultReferenceODT mfp f)) = @@ -211,8 +208,6 @@ runTest (Free (LookupEnv s f)) = do return (lookup s env) >>= runTest . f runTest (Free (GetCurrentTime f)) = asks envTime >>= runTest . f -runTest (Free (GetPOSIXTime f)) = - (utcTimeToPOSIXSeconds <$> asks envTime) >>= runTest . f runTest (Free (GetDefaultReferenceDocx _ f)) = asks envReferenceDocx >>= runTest . f runTest (Free (GetDefaultReferenceODT _ f)) = -- cgit v1.2.3 From 239880f412f89a6647368d313e21718ade4d89fd Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Mon, 21 Nov 2016 09:30:08 -0500 Subject: Introduce PandocMonad typeclass. This can be instantiated by both an IO monad or a pure State monad. --- pandoc.cabal | 5 +- src/Text/Pandoc/Class.hs | 202 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 205 insertions(+), 2 deletions(-) create mode 100644 src/Text/Pandoc/Class.hs (limited to 'src/Text') diff --git a/pandoc.cabal b/pandoc.cabal index d1fc56bed..7296643c9 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -383,8 +383,9 @@ Library Text.Pandoc.XML, Text.Pandoc.SelfContained, Text.Pandoc.Process, - Text.Pandoc.CSS - Text.Pandoc.Free + Text.Pandoc.CSS, + Text.Pandoc.Free, + Text.Pandoc.Class Other-Modules: Text.Pandoc.Readers.Docx.Lists, Text.Pandoc.Readers.Docx.Combine, Text.Pandoc.Readers.Docx.Parse, diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs new file mode 100644 index 000000000..aca1067c6 --- /dev/null +++ b/src/Text/Pandoc/Class.hs @@ -0,0 +1,202 @@ +{-# LANGUAGE DeriveFunctor, DeriveDataTypeable, TypeSynonymInstances, FlexibleInstances #-} + +{- +Copyright (C) 2016 Jesse Rosenthal + +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.Class + Copyright : Copyright (C) 2016 Jesse Rosenthal + License : GNU GPL, version 2 or above + + Maintainer : Jesse Rosenthal + Stability : alpha + Portability : portable + +Typeclass for pandoc readers and writers, allowing both IO and pure instances. +-} + +module Text.Pandoc.Class ( PandocMonad(..) + , Testing + , TestState(..) + , TestEnv(..) + , getPOSIXTime + ) where + +import Prelude hiding (readFile, fail) +import qualified Control.Monad as M (fail) +import System.Random (StdGen, next) +import qualified System.Random as IO (newStdGen) +import Codec.Archive.Zip (Archive, fromArchive) +import Data.Unique (hashUnique) +import qualified Data.Unique as IO (newUnique) +import qualified Text.Pandoc.Shared as IO ( fetchItem + , fetchItem' + , getDefaultReferenceDocx + , getDefaultReferenceODT + , warn + , readDataFile) +import Text.Pandoc.MediaBag (MediaBag, lookupMedia) +import Text.Pandoc.Compat.Time (UTCTime) +import qualified Text.Pandoc.Compat.Time as IO (getCurrentTime) +import Data.Time.Clock.POSIX ( utcTimeToPOSIXSeconds, POSIXTime ) +import Text.Pandoc.MIME (MimeType, getMimeType) +import qualified Data.ByteString as B +import qualified Data.ByteString.Lazy as BL +import qualified Control.Exception as E +import qualified System.Environment as IO (lookupEnv) +import System.FilePath.Glob (match, compile) +import System.FilePath (()) +import qualified System.FilePath.Glob as IO (glob) +import Control.Monad.State hiding (fail) +import Control.Monad.Reader hiding (fail) +import Data.Word (Word8) +import Data.Typeable + +class Monad m => PandocMonad m where + lookupEnv :: String -> m (Maybe String) + getCurrentTime :: m UTCTime + getDefaultReferenceDocx :: Maybe FilePath -> m Archive + getDefaultReferenceODT :: Maybe FilePath -> m Archive + newStdGen :: m StdGen + newUniqueHash :: m Int + readFileLazy :: FilePath -> m BL.ByteString + readDataFile :: Maybe FilePath -> FilePath -> m B.ByteString + fetchItem :: Maybe String -> + String -> + m (Either E.SomeException (B.ByteString, Maybe MimeType)) + fetchItem' :: MediaBag -> + Maybe String -> + String -> + m (Either E.SomeException (B.ByteString, Maybe MimeType)) + warn :: String -> m () + fail :: String -> m b + glob :: String -> m [FilePath] + +--Some functions derived from Primitives: + +getPOSIXTime :: (PandocMonad m) => m POSIXTime +getPOSIXTime = utcTimeToPOSIXSeconds <$> getCurrentTime + +instance PandocMonad IO where + lookupEnv = IO.lookupEnv + getCurrentTime = IO.getCurrentTime + getDefaultReferenceDocx = IO.getDefaultReferenceDocx + getDefaultReferenceODT = IO.getDefaultReferenceODT + newStdGen = IO.newStdGen + newUniqueHash = hashUnique <$> IO.newUnique + readFileLazy = BL.readFile + readDataFile = IO.readDataFile + fail = M.fail + fetchItem = IO.fetchItem + fetchItem' = IO.fetchItem' + warn = IO.warn + glob = IO.glob + + + +data TestState = TestState { stStdGen :: StdGen + , stWord8Store :: [Word8] -- should be + -- inifinite, + -- i.e. [1..] + , stWarnings :: [String] + , stUniqStore :: [Int] -- should be + -- inifinite and + -- contain every + -- element at most + -- once, e.g. [1..] + } + +data TestEnv = TestEnv { envEnv :: [(String, String)] + , envTime :: UTCTime + , envReferenceDocx :: Archive + , envReferenceODT :: Archive + , envFiles :: [(FilePath, B.ByteString)] + , envUserDataDir :: [(FilePath, B.ByteString)] + , envCabalDataDir :: [(FilePath, B.ByteString)] + , envFontFiles :: [FilePath] + } + +data TestException = TestException + deriving (Show, Typeable) + +instance E.Exception TestException + +type Testing = ReaderT TestEnv (State TestState) + +instance PandocMonad Testing where + lookupEnv s = do + env <- asks envEnv + return (lookup s env) + + getCurrentTime = asks envTime + + getDefaultReferenceDocx _ = asks envReferenceDocx + + getDefaultReferenceODT _ = asks envReferenceODT + + newStdGen = do + g <- gets stStdGen + let (_, nxtGen) = next g + modify $ \st -> st { stStdGen = nxtGen } + return g + + newUniqueHash = do + uniqs <- gets stUniqStore + case uniqs of + u : us -> do + modify $ \st -> st { stUniqStore = us } + return u + _ -> M.fail "uniq store ran out of elements" + + readFileLazy fp = do + fps <- asks envFiles + case lookup fp fps of + Just bs -> return (BL.fromStrict bs) + Nothing -> error "openFile: does not exist" + + readDataFile Nothing "reference.docx" = do + (B.concat . BL.toChunks . fromArchive) <$> (getDefaultReferenceDocx Nothing) + readDataFile Nothing "reference.odt" = do + (B.concat . BL.toChunks . fromArchive) <$> (getDefaultReferenceODT Nothing) + readDataFile Nothing fname = do + let fname' = if fname == "MANUAL.txt" then fname else "data" fname + BL.toStrict <$> (readFileLazy fname') + readDataFile (Just userDir) fname = do + userDirFiles <- asks envUserDataDir + case lookup (userDir fname) userDirFiles of + Just bs -> return bs + Nothing -> readDataFile Nothing fname + + fail = M.fail + + fetchItem _ fp = do + fps <- asks envFiles + case lookup fp fps of + Just bs -> return (Right (bs, getMimeType fp)) + Nothing -> return (Left $ E.toException TestException) + + fetchItem' media sourceUrl nm = do + case lookupMedia nm media of + Nothing -> fetchItem sourceUrl nm + Just (mime, bs) -> return (Right (B.concat $ BL.toChunks bs, Just mime)) + + warn s = modify $ \st -> st { stWarnings = s : stWarnings st } + + glob s = do + fontFiles <- asks envFontFiles + return (filter (match (compile s)) fontFiles) -- cgit v1.2.3 From 957eee24ec9037a31574503fa1ca939567f23a90 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Mon, 21 Nov 2016 10:12:42 -0500 Subject: Convert writers to use PandocMonad typeclass. Instead of Free Monad with runIO --- src/Text/Pandoc/Writers/Docx.hs | 82 +++++++++++++++++++---------------------- src/Text/Pandoc/Writers/EPUB.hs | 49 ++++++++++++------------ src/Text/Pandoc/Writers/FB2.hs | 57 ++++++++++++++-------------- src/Text/Pandoc/Writers/ICML.hs | 36 ++++++++---------- src/Text/Pandoc/Writers/ODT.hs | 27 ++++++-------- src/Text/Pandoc/Writers/RTF.hs | 13 ++----- 6 files changed, 122 insertions(+), 142 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 07041f189..36816eaa1 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -28,7 +28,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Conversion of 'Pandoc' documents to docx. -} -module Text.Pandoc.Writers.Docx ( writeDocx, writeDocxPure ) where +module Text.Pandoc.Writers.Docx ( writeDocx ) where import Data.List ( intercalate, isPrefixOf, isSuffixOf ) import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL @@ -65,8 +65,8 @@ import Text.Pandoc.MIME (MimeType, getMimeType, getMimeTypeDef, import Control.Applicative ((<|>)) import Data.Maybe (fromMaybe, mapMaybe, maybeToList, isNothing) import Data.Char (ord, isSpace, toLower) -import Text.Pandoc.Free (PandocAction, runIO) -import qualified Text.Pandoc.Free as P +import Text.Pandoc.Class (PandocMonad) +import qualified Text.Pandoc.Class as P data ListMarker = NoMarker | BulletMarker @@ -146,7 +146,7 @@ defaultWriterState = WriterState{ , stDynamicTextProps = [] } -type WS = ReaderT WriterEnv (StateT WriterState (PandocAction)) +type WS m = ReaderT WriterEnv (StateT WriterState m) mknode :: Node t => String -> [(String,String)] -> t -> Element mknode s attrs = @@ -215,17 +215,11 @@ metaValueToInlines _ = [] -writeDocx :: WriterOptions -- ^ Writer options +writeDocx :: (PandocMonad m) + => WriterOptions -- ^ Writer options -> Pandoc -- ^ Document to convert - -> IO BL.ByteString -writeDocx opts doc = runIO $ writeDocxPure opts doc - - --- | Produce an Docx file from a Pandoc document. -writeDocxPure :: WriterOptions -- ^ Writer options - -> Pandoc -- ^ Document to convert - -> PandocAction BL.ByteString -writeDocxPure opts doc@(Pandoc meta _) = do + -> m BL.ByteString +writeDocx opts doc@(Pandoc meta _) = do let datadir = writerUserDataDir opts let doc' = walk fixDisplayMath $ doc username <- P.lookupEnv "USERNAME" @@ -611,7 +605,7 @@ styleToOpenXml sm style = $ backgroundColor style ) ] -copyChildren :: Archive -> Archive -> String -> Integer -> [String] -> PandocAction Entry +copyChildren :: (PandocMonad m) => Archive -> Archive -> String -> Integer -> [String] -> m Entry copyChildren refArchive distArchive path timestamp elNames = do ref <- parseXml refArchive distArchive path dist <- parseXml distArchive distArchive path @@ -630,7 +624,7 @@ copyChildren refArchive distArchive path timestamp elNames = do baseListId :: Int baseListId = 1000 -mkNumbering :: [ListMarker] -> PandocAction [Element] +mkNumbering :: (PandocMonad m) => [ListMarker] -> m [Element] mkNumbering lists = do elts <- mapM mkAbstractNum (ordNub lists) return $ elts ++ zipWith mkNum lists [baseListId..(baseListId + length lists - 1)] @@ -646,7 +640,7 @@ mkNum marker numid = map (\lvl -> mknode "w:lvlOverride" [("w:ilvl",show (lvl :: Int))] $ mknode "w:startOverride" [("w:val",show start)] ()) [0..6] -mkAbstractNum :: ListMarker -> PandocAction Element +mkAbstractNum :: (PandocMonad m) => ListMarker -> m Element mkAbstractNum marker = do gen <- P.newStdGen let (nsid, _) = randomR (0x10000000 :: Integer, 0xFFFFFFFF :: Integer) gen @@ -701,11 +695,11 @@ mkLvl marker lvl = patternFor TwoParens s = "(" ++ s ++ ")" patternFor _ s = s ++ "." -getNumId :: WS Int +getNumId :: (PandocMonad m) => WS m Int getNumId = (((baseListId - 1) +) . length) `fmap` gets stLists -makeTOC :: WriterOptions -> WS [Element] +makeTOC :: (PandocMonad m) => WriterOptions -> WS m [Element] makeTOC opts | writerTableOfContents opts = do let depth = "1-"++(show (writerTOCDepth opts)) let tocCmd = "TOC \\o \""++depth++"\" \\h \\z \\u" @@ -735,7 +729,7 @@ makeTOC _ = return [] -- | Convert Pandoc document to two lists of -- OpenXML elements (the main document and footnotes). -writeOpenXML :: WriterOptions -> Pandoc -> WS ([Element], [Element]) +writeOpenXML :: (PandocMonad m) => WriterOptions -> Pandoc -> WS m ([Element], [Element]) writeOpenXML opts (Pandoc meta blocks) = do let tit = docTitle meta ++ case lookupMeta "subtitle" meta of Just (MetaBlocks [Plain xs]) -> LineBreak : xs @@ -770,13 +764,13 @@ writeOpenXML opts (Pandoc meta blocks) = do return (meta' ++ doc', notes') -- | Convert a list of Pandoc blocks to OpenXML. -blocksToOpenXML :: WriterOptions -> [Block] -> WS [Element] +blocksToOpenXML :: (PandocMonad m) => WriterOptions -> [Block] -> WS m [Element] blocksToOpenXML opts bls = concat `fmap` mapM (blockToOpenXML opts) bls pCustomStyle :: String -> Element pCustomStyle sty = mknode "w:pStyle" [("w:val",sty)] () -pStyleM :: String -> WS XML.Element +pStyleM :: (PandocMonad m) => String -> WS m XML.Element pStyleM styleName = do styleMaps <- gets stStyleMaps let sty' = getStyleId styleName $ sParaStyleMap styleMaps @@ -785,13 +779,13 @@ pStyleM styleName = do rCustomStyle :: String -> Element rCustomStyle sty = mknode "w:rStyle" [("w:val",sty)] () -rStyleM :: String -> WS XML.Element +rStyleM :: (PandocMonad m) => String -> WS m XML.Element rStyleM styleName = do styleMaps <- gets stStyleMaps let sty' = getStyleId styleName $ sCharStyleMap styleMaps return $ mknode "w:rStyle" [("w:val",sty')] () -getUniqueId :: PandocAction String +getUniqueId :: (PandocMonad m) => m String -- the + 20 is to ensure that there are no clashes with the rIds -- already in word/document.xml.rel getUniqueId = (show . (+ 20)) <$> P.newUniqueHash @@ -801,10 +795,10 @@ dynamicStyleKey :: String dynamicStyleKey = "custom-style" -- | Convert a Pandoc block element to OpenXML. -blockToOpenXML :: WriterOptions -> Block -> WS [Element] +blockToOpenXML :: (PandocMonad m) => WriterOptions -> Block -> WS m [Element] blockToOpenXML opts blk = withDirection $ blockToOpenXML' opts blk -blockToOpenXML' :: WriterOptions -> Block -> WS [Element] +blockToOpenXML' :: (PandocMonad m) => WriterOptions -> Block -> WS m [Element] blockToOpenXML' _ Null = return [] blockToOpenXML' opts (Div (ident,classes,kvs) bs) | Just sty <- lookup dynamicStyleKey kvs = do @@ -955,7 +949,7 @@ blockToOpenXML' opts (DefinitionList items) = do setFirstPara return l -definitionListItemToOpenXML :: WriterOptions -> ([Inline],[[Block]]) -> WS [Element] +definitionListItemToOpenXML :: (PandocMonad m) => WriterOptions -> ([Inline],[[Block]]) -> WS m [Element] definitionListItemToOpenXML opts (term,defs) = do term' <- withParaProp (pCustomStyle "DefinitionTerm") $ blockToOpenXML opts (Para term) @@ -963,12 +957,12 @@ definitionListItemToOpenXML opts (term,defs) = do $ concat `fmap` mapM (blocksToOpenXML opts) defs return $ term' ++ defs' -addList :: ListMarker -> WS () +addList :: (PandocMonad m) => ListMarker -> WS m () addList marker = do lists <- gets stLists modify $ \st -> st{ stLists = lists ++ [marker] } -listItemToOpenXML :: WriterOptions -> Int -> [Block] -> WS [Element] +listItemToOpenXML :: (PandocMonad m) => WriterOptions -> Int -> [Block] -> WS m [Element] listItemToOpenXML _ _ [] = return [] listItemToOpenXML opts numid (first:rest) = do first' <- withNumId numid $ blockToOpenXML opts first @@ -984,30 +978,30 @@ alignmentToString alignment = case alignment of AlignDefault -> "left" -- | Convert a list of inline elements to OpenXML. -inlinesToOpenXML :: WriterOptions -> [Inline] -> WS [Element] +inlinesToOpenXML :: (PandocMonad m) => WriterOptions -> [Inline] -> WS m [Element] inlinesToOpenXML opts lst = concat `fmap` mapM (inlineToOpenXML opts) lst -withNumId :: Int -> WS a -> WS a +withNumId :: (PandocMonad m) => Int -> WS m a -> WS m a withNumId numid = local $ \env -> env{ envListNumId = numid } -asList :: WS a -> WS a +asList :: (PandocMonad m) => WS m a -> WS m a asList = local $ \env -> env{ envListLevel = envListLevel env + 1 } -getTextProps :: WS [Element] +getTextProps :: (PandocMonad m) => WS m [Element] getTextProps = do props <- asks envTextProperties return $ if null props then [] else [mknode "w:rPr" [] props] -withTextProp :: Element -> WS a -> WS a +withTextProp :: PandocMonad m => Element -> WS m a -> WS m a withTextProp d p = local (\env -> env {envTextProperties = d : envTextProperties env}) p -withTextPropM :: WS Element -> WS a -> WS a +withTextPropM :: PandocMonad m => WS m Element -> WS m a -> WS m a withTextPropM = (. flip withTextProp) . (>>=) -getParaProps :: Bool -> WS [Element] +getParaProps :: PandocMonad m => Bool -> WS m [Element] getParaProps displayMathPara = do props <- asks envParaProperties listLevel <- asks envListLevel @@ -1022,14 +1016,14 @@ getParaProps displayMathPara = do [] -> [] ps -> [mknode "w:pPr" [] ps] -withParaProp :: Element -> WS a -> WS a +withParaProp :: PandocMonad m => Element -> WS m a -> WS m a withParaProp d p = local (\env -> env {envParaProperties = d : envParaProperties env}) p -withParaPropM :: WS Element -> WS a -> WS a +withParaPropM :: PandocMonad m => WS m Element -> WS m a -> WS m a withParaPropM = (. flip withParaProp) . (>>=) -formattedString :: String -> WS [Element] +formattedString :: PandocMonad m => String -> WS m [Element] formattedString str = do props <- getTextProps inDel <- asks envInDel @@ -1038,14 +1032,14 @@ formattedString str = do [ mknode (if inDel then "w:delText" else "w:t") [("xml:space","preserve")] (stripInvalidChars str) ] ] -setFirstPara :: WS () +setFirstPara :: PandocMonad m => WS m () setFirstPara = modify $ \s -> s { stFirstPara = True } -- | Convert an inline element to OpenXML. -inlineToOpenXML :: WriterOptions -> Inline -> WS [Element] +inlineToOpenXML :: PandocMonad m => WriterOptions -> Inline -> WS m [Element] inlineToOpenXML opts il = withDirection $ inlineToOpenXML' opts il -inlineToOpenXML' :: WriterOptions -> Inline -> WS [Element] +inlineToOpenXML' :: PandocMonad m => WriterOptions -> Inline -> WS m [Element] inlineToOpenXML' _ (Str str) = formattedString str inlineToOpenXML' opts Space = inlineToOpenXML opts (Str " ") inlineToOpenXML' opts SoftBreak = inlineToOpenXML opts (Str " ") @@ -1281,7 +1275,7 @@ defaultFootnotes = [ mknode "w:footnote" [ mknode "w:r" [] $ [ mknode "w:continuationSeparator" [] ()]]]] -parseXml :: Archive -> Archive -> String -> PandocAction Element +parseXml :: (PandocMonad m) => Archive -> Archive -> String -> m Element parseXml refArchive distArchive relpath = case findEntryByPath relpath refArchive `mplus` findEntryByPath relpath distArchive of @@ -1299,7 +1293,7 @@ fitToPage (x, y) pageWidth (pageWidth, floor $ ((fromIntegral pageWidth) / x) * y) | otherwise = (floor x, floor y) -withDirection :: WS a -> WS a +withDirection :: PandocMonad m => WS m a -> WS m a withDirection x = do isRTL <- asks envRTL paraProps <- asks envParaProperties diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index a0991e27b..397aa5847 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -28,7 +28,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Conversion of 'Pandoc' documents to EPUB. -} -module Text.Pandoc.Writers.EPUB ( writeEPUB, writeEPUBPure ) where +module Text.Pandoc.Writers.EPUB ( writeEPUB ) where import qualified Data.Map as M import qualified Data.Set as Set import Data.Maybe ( fromMaybe, catMaybes ) @@ -64,8 +64,8 @@ import Data.Char ( toLower, isDigit, isAlphaNum ) import Text.Pandoc.MIME (MimeType, getMimeType, extensionFromMimeType) import Text.Blaze.Html.Renderer.Utf8 (renderHtml) import Text.HTML.TagSoup (Tag(TagOpen), fromAttrib, parseTags) -import Text.Pandoc.Free (PandocAction, runIO) -import qualified Text.Pandoc.Free as P +import Text.Pandoc.Class (PandocMonad) +import qualified Text.Pandoc.Class as P -- A Chapter includes a list of blocks and maybe a section -- number offset. Note, some chapters are unnumbered. The section @@ -76,7 +76,7 @@ data Chapter = Chapter (Maybe [Int]) [Block] data EPUBState = EPUBState { stMediaPaths :: [(FilePath, (FilePath, Maybe Entry))] } -type E = StateT EPUBState PandocAction +type E m = StateT EPUBState m data EPUBMetadata = EPUBMetadata{ epubIdentifier :: [Identifier] @@ -145,7 +145,7 @@ removeNote :: Inline -> Inline removeNote (Note _) = Str "" removeNote x = x -getEPUBMetadata :: WriterOptions -> Meta -> E EPUBMetadata +getEPUBMetadata :: PandocMonad m => WriterOptions -> Meta -> E m EPUBMetadata getEPUBMetadata opts meta = do let md = metadataFromMeta opts meta let elts = onlyElems $ parseXML $ writerEpubMetadata opts @@ -335,23 +335,20 @@ metadataFromMeta opts meta = EPUBMetadata{ _ -> Nothing -- | Produce an EPUB file from a Pandoc document. -writeEPUB :: WriterOptions -- ^ Writer options +writeEPUB :: PandocMonad m + => WriterOptions -- ^ Writer options -> Pandoc -- ^ Document to convert - -> IO B.ByteString -writeEPUB opts doc = runIO $ writeEPUBPure opts doc - -writeEPUBPure :: WriterOptions -- ^ Writer options - -> Pandoc -- ^ Document to convert - -> PandocAction B.ByteString -writeEPUBPure opts doc = + -> m B.ByteString +writeEPUB opts doc = let initState = EPUBState { stMediaPaths = [] } in evalStateT (pandocToEPUB opts doc) initState -pandocToEPUB :: WriterOptions - -> Pandoc - -> E B.ByteString +pandocToEPUB :: PandocMonad m + => WriterOptions + -> Pandoc + -> E m B.ByteString pandocToEPUB opts doc@(Pandoc meta _) = do let version = fromMaybe EPUB2 (writerEpubVersion opts) let epub3 = version == EPUB3 @@ -829,10 +826,11 @@ metadataElement version md currentTime = showDateTimeISO8601 :: UTCTime -> String showDateTimeISO8601 = formatTime defaultTimeLocale "%FT%TZ" -transformTag :: WriterOptions +transformTag :: PandocMonad m + => WriterOptions -- -> IORef [(FilePath, (FilePath, Maybe Entry))] -- ^ (oldpath, newpath, entry) media -> Tag String - -> E (Tag String) + -> E m (Tag String) transformTag opts tag@(TagOpen name attr) | name `elem` ["video", "source", "img", "audio"] && lookup "data-external" attr == Nothing = do @@ -846,9 +844,10 @@ transformTag opts tag@(TagOpen name attr) return $ TagOpen name attr' transformTag _ tag = return tag -modifyMediaRef :: WriterOptions +modifyMediaRef :: PandocMonad m + => WriterOptions -> FilePath - -> E FilePath + -> E m FilePath modifyMediaRef _ "" = return "" modifyMediaRef opts oldsrc = do media <- gets stMediaPaths @@ -872,10 +871,11 @@ modifyMediaRef opts oldsrc = do modify $ \st -> st{ stMediaPaths = (oldsrc, (new, mbEntry)):media} return new -transformBlock :: WriterOptions +transformBlock :: PandocMonad m + => WriterOptions -- -> IORef [(FilePath, (FilePath, Maybe Entry))] -- ^ (oldpath, newpath, entry) media -> Block - -> E Block + -> E m Block transformBlock opts (RawBlock fmt raw) | fmt == Format "html" = do let tags = parseTags raw @@ -883,10 +883,11 @@ transformBlock opts (RawBlock fmt raw) return $ RawBlock fmt (renderTags' tags') transformBlock _ b = return b -transformInline :: WriterOptions +transformInline :: PandocMonad m + => WriterOptions -- -> IORef [(FilePath, (FilePath, Maybe Entry))] -- ^ (oldpath, newpath) media -> Inline - -> E Inline + -> E m Inline transformInline opts (Image attr lab (src,tit)) = do newsrc <- modifyMediaRef opts src return $ Image attr lab (newsrc, tit) diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index 3c4970e75..58bfe7615 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -25,7 +25,7 @@ FictionBook is an XML-based e-book format. For more information see: -} -module Text.Pandoc.Writers.FB2 (writeFB2, writeFB2Pure) where +module Text.Pandoc.Writers.FB2 (writeFB2) where import Control.Monad.State (StateT, evalStateT, get, modify, lift) import Control.Monad.State (liftM) @@ -45,8 +45,8 @@ import Text.Pandoc.Definition import Text.Pandoc.Options (WriterOptions(..), HTMLMathMethod(..), def) import Text.Pandoc.Shared (orderedListMarkers, isHeaderBlock, capitalize, linesToPara) -import Text.Pandoc.Free (PandocAction, runIO) -import qualified Text.Pandoc.Free as P +import Text.Pandoc.Class (PandocMonad) +import qualified Text.Pandoc.Class as P -- | Data to be written at the end of the document: -- (foot)notes, URLs, references, images. @@ -59,7 +59,7 @@ data FbRenderState = FbRenderState } deriving (Show) -- | FictionBook building monad. -type FBM = StateT FbRenderState PandocAction +type FBM m = StateT FbRenderState m newFB :: FbRenderState newFB = FbRenderState { footnotes = [], imagesToFetch = [] @@ -72,19 +72,16 @@ instance Show ImageMode where show InlineImage = "inlineImageType" -- | Produce an FB2 document from a 'Pandoc' document. -writeFB2 :: WriterOptions -- ^ conversion options +writeFB2 :: PandocMonad m + => WriterOptions -- ^ conversion options -> Pandoc -- ^ document to convert - -> IO String -- ^ FictionBook2 document (not encoded yet) -writeFB2 opts doc = runIO $ writeFB2Pure opts doc + -> m String -- ^ FictionBook2 document (not encoded yet) +writeFB2 opts doc = flip evalStateT newFB $ pandocToFB2 opts doc -writeFB2Pure :: WriterOptions - -> Pandoc - -> PandocAction String -writeFB2Pure opts doc = flip evalStateT newFB $ pandocToFB2 opts doc - -pandocToFB2 :: WriterOptions +pandocToFB2 :: PandocMonad m + => WriterOptions -> Pandoc - -> FBM String + -> FBM m String pandocToFB2 opts (Pandoc meta blocks) = do modify (\s -> s { writerOptions = opts { writerOptions = opts } }) desc <- description meta @@ -104,7 +101,7 @@ pandocToFB2 opts (Pandoc meta blocks) = do in [ uattr "xmlns" xmlns , attr ("xmlns", "l") xlink ] -frontpage :: Meta -> FBM [Content] +frontpage :: PandocMonad m => Meta -> FBM m [Content] frontpage meta' = do t <- cMapM toXml . docTitle $ meta' return $ @@ -113,7 +110,7 @@ frontpage meta' = do (docAuthors meta' ++ [docDate meta'])) ] -description :: Meta -> FBM Content +description :: PandocMonad m => Meta -> FBM m Content description meta' = do bt <- booktitle meta' let as = authors meta' @@ -123,7 +120,7 @@ description meta' = do , el "document-info" [ el "program-used" "pandoc" ] -- FIXME: +version ] -booktitle :: Meta -> FBM [Content] +booktitle :: PandocMonad m => Meta -> FBM m [Content] booktitle meta' = do t <- cMapM toXml . docTitle $ meta' return $ if null t @@ -148,7 +145,7 @@ author ss = ([]) -> [] in list $ el "author" (names ++ email) -docdate :: Meta -> FBM [Content] +docdate :: PandocMonad m => Meta -> FBM m [Content] docdate meta' = do let ss = docDate meta' d <- cMapM toXml ss @@ -158,12 +155,12 @@ docdate meta' = do -- | Divide the stream of blocks into sections and convert to XML -- representation. -renderSections :: Int -> [Block] -> FBM [Content] +renderSections :: PandocMonad m => Int -> [Block] -> FBM m [Content] renderSections level blocks = do let secs = splitSections level blocks mapM (renderSection level) secs -renderSection :: Int -> ([Inline], [Block]) -> FBM Content +renderSection :: PandocMonad m => Int -> ([Inline], [Block]) -> FBM m Content renderSection level (ttl, body) = do title <- if null ttl then return [] @@ -210,7 +207,7 @@ splitSections level blocks = reverse $ revSplit (reverse blocks) sameLevel _ = False -- | Make another FictionBook body with footnotes. -renderFootnotes :: FBM [Content] +renderFootnotes :: PandocMonad m => FBM m [Content] renderFootnotes = do fns <- footnotes `liftM` get if null fns @@ -224,14 +221,14 @@ renderFootnotes = do -- | Fetch images and encode them for the FictionBook XML. -- Return image data and a list of hrefs of the missing images. -fetchImages :: [(String,String)] -> PandocAction ([Content],[String]) +fetchImages :: PandocMonad m => [(String,String)] -> m ([Content],[String]) fetchImages links = do imgs <- mapM (uncurry fetchImage) links return $ (rights imgs, lefts imgs) -- | Fetch image data from disk or from network and make a XML section. -- Return either (Left hrefOfMissingImage) or (Right xmlContent). -fetchImage :: String -> String -> PandocAction (Either String Content) +fetchImage :: PandocMonad m => String -> String -> m (Either String Content) fetchImage href link = do mbimg <- case (isURI link, readDataURI link) of @@ -298,7 +295,7 @@ linkID :: Int -> String linkID i = "l" ++ (show i) -- | Convert a block-level Pandoc's element to FictionBook XML representation. -blockToXml :: Block -> FBM [Content] +blockToXml :: PandocMonad m => Block -> FBM m [Content] blockToXml (Plain ss) = cMapM toXml ss -- FIXME: can lead to malformed FB2 blockToXml (Para [Math DisplayMath formula]) = insertMath NormalImage formula -- title beginning with fig: indicates that the image is a figure @@ -362,11 +359,11 @@ blockToXml (Table caption aligns _ headers rows) = do c <- return . el "emphasis" =<< cMapM toXml caption return [el "table" (hd : bd), el "p" c] where - mkrow :: String -> [TableCell] -> [Alignment] -> FBM Content + mkrow :: PandocMonad m => String -> [TableCell] -> [Alignment] -> FBM m Content mkrow tag cells aligns' = (el "tr") `liftM` (mapM (mkcell tag) (zip cells aligns')) -- - mkcell :: String -> (TableCell, Alignment) -> FBM Content + mkcell :: PandocMonad m => String -> (TableCell, Alignment) -> FBM m Content mkcell tag (cell, align) = do cblocks <- cMapM blockToXml cell return $ el tag ([align_attr align], cblocks) @@ -410,7 +407,7 @@ indent = indentBlock in intercalate [LineBreak] $ map ((Str spacer):) lns -- | Convert a Pandoc's Inline element to FictionBook XML representation. -toXml :: Inline -> FBM [Content] +toXml :: PandocMonad m => Inline -> FBM m [Content] toXml (Str s) = return [txt s] toXml (Span _ ils) = cMapM toXml ils toXml (Emph ss) = list `liftM` wrap "emphasis" ss @@ -462,7 +459,7 @@ toXml (Note bs) = do , uattr "type" "note" ] , fn_ref ) -insertMath :: ImageMode -> String -> FBM [Content] +insertMath :: PandocMonad m => ImageMode -> String -> FBM m [Content] insertMath immode formula = do htmlMath <- return . writerHTMLMathMethod . writerOptions =<< get case htmlMath of @@ -473,7 +470,7 @@ insertMath immode formula = do insertImage immode img _ -> return [el "code" formula] -insertImage :: ImageMode -> Inline -> FBM [Content] +insertImage :: PandocMonad m => ImageMode -> Inline -> FBM m [Content] insertImage immode (Image _ alt (url,ttl)) = do images <- imagesToFetch `liftM` get let n = 1 + length images @@ -539,7 +536,7 @@ replaceImagesWithAlt missingHrefs body = -- | Wrap all inlines with an XML tag (given its unqualified name). -wrap :: String -> [Inline] -> FBM Content +wrap :: PandocMonad m => String -> [Inline] -> FBM m Content wrap tagname inlines = el tagname `liftM` cMapM toXml inlines -- " Create a singleton list. diff --git a/src/Text/Pandoc/Writers/ICML.hs b/src/Text/Pandoc/Writers/ICML.hs index 186bf0c8d..c82a77452 100644 --- a/src/Text/Pandoc/Writers/ICML.hs +++ b/src/Text/Pandoc/Writers/ICML.hs @@ -28,8 +28,8 @@ import Data.Text as Text (breakOnAll, pack) import Control.Monad.State import Network.URI (isURI) import qualified Data.Set as Set -import Text.Pandoc.Free (runIO, PandocAction) -import qualified Text.Pandoc.Free as P +import Text.Pandoc.Class (PandocMonad) +import qualified Text.Pandoc.Class as P type Style = [String] type Hyperlink = [(Int, String)] @@ -42,7 +42,7 @@ data WriterState = WriterState{ , maxListDepth :: Int } -type WS a = StateT WriterState PandocAction a +type WS m = StateT WriterState m defaultWriterState :: WriterState defaultWriterState = WriterState{ @@ -124,12 +124,8 @@ footnoteName = "Footnote" citeName = "Cite" -- | Convert Pandoc document to string in ICML format. -writeICML :: WriterOptions -> Pandoc -> IO String -writeICML opts doc = runIO $ writeICMLPure opts doc - --- | Convert Pandoc document to string in ICML format. -writeICMLPure :: WriterOptions -> Pandoc -> PandocAction String -writeICMLPure opts (Pandoc meta blocks) = do +writeICML :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeICML opts (Pandoc meta blocks) = do let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing @@ -288,13 +284,13 @@ hyperlinksToDoc (x:xs) = hyp x $$ hyperlinksToDoc xs -- | Convert a list of Pandoc blocks to ICML. -blocksToICML :: WriterOptions -> Style -> [Block] -> WS Doc +blocksToICML :: PandocMonad m => WriterOptions -> Style -> [Block] -> WS m Doc blocksToICML opts style lst = do docs <- mapM (blockToICML opts style) lst return $ intersperseBrs docs -- | Convert a Pandoc block element to ICML. -blockToICML :: WriterOptions -> Style -> Block -> WS Doc +blockToICML :: PandocMonad m => WriterOptions -> Style -> Block -> WS m Doc blockToICML opts style (Plain lst) = parStyle opts style lst -- title beginning with fig: indicates that the image is a figure blockToICML opts style (Para img@[Image _ txt (_,'f':'i':'g':':':_)]) = do @@ -364,7 +360,7 @@ blockToICML opts style (Div _ lst) = blocksToICML opts style lst blockToICML _ _ Null = return empty -- | Convert a list of lists of blocks to ICML list items. -listItemsToICML :: WriterOptions -> String -> Style -> Maybe ListAttributes -> [[Block]] -> WS Doc +listItemsToICML :: PandocMonad m => WriterOptions -> String -> Style -> Maybe ListAttributes -> [[Block]] -> WS m Doc listItemsToICML _ _ _ _ [] = return empty listItemsToICML opts listType style attribs (first:rest) = do st <- get @@ -379,7 +375,7 @@ listItemsToICML opts listType style attribs (first:rest) = do return $ intersperseBrs docs -- | Convert a list of blocks to ICML list items. -listItemToICML :: WriterOptions -> Style -> Bool-> Maybe ListAttributes -> [Block] -> WS Doc +listItemToICML :: PandocMonad m => WriterOptions -> Style -> Bool-> Maybe ListAttributes -> [Block] -> WS m Doc listItemToICML opts style isFirst attribs item = let makeNumbStart (Just (beginsWith, numbStl, _)) = let doN DefaultStyle = [] @@ -406,7 +402,7 @@ listItemToICML opts style isFirst attribs item = return $ intersperseBrs (f : r) else blocksToICML opts stl' item -definitionListItemToICML :: WriterOptions -> Style -> ([Inline],[[Block]]) -> WS Doc +definitionListItemToICML :: PandocMonad m => WriterOptions -> Style -> ([Inline],[[Block]]) -> WS m Doc definitionListItemToICML opts style (term,defs) = do term' <- parStyle opts (defListTermName:style) term defs' <- mapM (blocksToICML opts (defListDefName:style)) defs @@ -414,11 +410,11 @@ definitionListItemToICML opts style (term,defs) = do -- | Convert a list of inline elements to ICML. -inlinesToICML :: WriterOptions -> Style -> [Inline] -> WS Doc +inlinesToICML :: PandocMonad m => WriterOptions -> Style -> [Inline] -> WS m Doc inlinesToICML opts style lst = vcat `fmap` mapM (inlineToICML opts style) (mergeSpaces lst) -- | Convert an inline element to ICML. -inlineToICML :: WriterOptions -> Style -> Inline -> WS Doc +inlineToICML :: PandocMonad m => WriterOptions -> Style -> Inline -> WS m Doc inlineToICML _ style (Str str) = charStyle style $ text $ escapeStringForXML str inlineToICML opts style (Emph lst) = inlinesToICML opts (emphName:style) lst inlineToICML opts style (Strong lst) = inlinesToICML opts (strongName:style) lst @@ -458,7 +454,7 @@ inlineToICML opts style (Note lst) = footnoteToICML opts style lst inlineToICML opts style (Span _ lst) = inlinesToICML opts style lst -- | Convert a list of block elements to an ICML footnote. -footnoteToICML :: WriterOptions -> Style -> [Block] -> WS Doc +footnoteToICML :: PandocMonad m => WriterOptions -> Style -> [Block] -> WS m Doc footnoteToICML opts style lst = let insertTab (Para ls) = blockToICML opts (footnoteName:style) $ Para $ (Str "\t"):ls insertTab block = blockToICML opts (footnoteName:style) block @@ -489,7 +485,7 @@ intersperseBrs :: [Doc] -> Doc intersperseBrs = vcat . intersperse (selfClosingTag "Br" []) . filter (not . isEmpty) -- | Wrap a list of inline elements in an ICML Paragraph Style -parStyle :: WriterOptions -> Style -> [Inline] -> WS Doc +parStyle :: PandocMonad m => WriterOptions -> Style -> [Inline] -> WS m Doc parStyle opts style lst = let slipIn x y = if null y then x @@ -513,7 +509,7 @@ parStyle opts style lst = state $ \st -> (cont, st{ blockStyles = Set.insert stlStr $ blockStyles st }) -- | Wrap a Doc in an ICML Character Style. -charStyle :: Style -> Doc -> WS Doc +charStyle :: PandocMonad m => Style -> Doc -> WS m Doc charStyle style content = let (stlStr, attrs) = styleToStrAttr style doc = inTags True "CharacterStyleRange" attrs $ inTagsSimple "Content" $ flush content @@ -535,7 +531,7 @@ styleToStrAttr style = in (stlStr, attrs) -- | Assemble an ICML Image. -imageICML :: WriterOptions -> Style -> Attr -> Target -> WS Doc +imageICML :: PandocMonad m => WriterOptions -> Style -> Attr -> Target -> WS m Doc imageICML opts style attr (src, _) = do res <- lift $ P.fetchItem (writerSourceURL opts) src imgS <- case res of diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index abd403cc9..8013763c2 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -28,7 +28,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Conversion of 'Pandoc' documents to ODT. -} -module Text.Pandoc.Writers.ODT ( writeODTPure, writeODT ) where +module Text.Pandoc.Writers.ODT ( writeODT ) where import Data.List ( isPrefixOf ) import Data.Maybe ( fromMaybe ) import Text.XML.Light.Output @@ -49,33 +49,30 @@ import Text.Pandoc.XML import Text.Pandoc.Pretty import qualified Control.Exception as E import System.FilePath ( takeExtension, takeDirectory, (<.>)) -import Text.Pandoc.Free ( PandocAction, runIO ) -import qualified Text.Pandoc.Free as P +import Text.Pandoc.Class ( PandocMonad ) +import qualified Text.Pandoc.Class as P data ODTState = ODTState { stEntries :: [Entry] } -type O = StateT ODTState PandocAction +type O m = StateT ODTState m -- | Produce an ODT file from a Pandoc document. -writeODT :: WriterOptions -- ^ Writer options +writeODT :: PandocMonad m + => WriterOptions -- ^ Writer options -> Pandoc -- ^ Document to convert - -> IO B.ByteString -writeODT opts doc = runIO $ writeODTPure opts doc - -writeODTPure :: WriterOptions - -> Pandoc - -> PandocAction B.ByteString -writeODTPure opts doc = + -> m B.ByteString +writeODT opts doc = let initState = ODTState{ stEntries = [] } in evalStateT (pandocToODT opts doc) initState -- | Produce an ODT file from a Pandoc document. -pandocToODT :: WriterOptions -- ^ Writer options +pandocToODT :: PandocMonad m + => WriterOptions -- ^ Writer options -> Pandoc -- ^ Document to convert - -> O B.ByteString + -> O m B.ByteString pandocToODT opts doc@(Pandoc meta _) = do let datadir = writerUserDataDir opts let title = docTitle meta @@ -145,7 +142,7 @@ pandocToODT opts doc@(Pandoc meta _) = do return $ fromArchive archive'' -- | transform both Image and Math elements -transformPicMath :: WriterOptions ->Inline -> O Inline +transformPicMath :: PandocMonad m => WriterOptions ->Inline -> O m Inline transformPicMath opts (Image attr@(id', cls, _) lab (src,t)) = do res <- lift $ P.fetchItem' (writerMediaBag opts) (writerSourceURL opts) src case res of diff --git a/src/Text/Pandoc/Writers/RTF.hs b/src/Text/Pandoc/Writers/RTF.hs index 8d7c643e0..75b97a648 100644 --- a/src/Text/Pandoc/Writers/RTF.hs +++ b/src/Text/Pandoc/Writers/RTF.hs @@ -29,7 +29,6 @@ Conversion of 'Pandoc' documents to RTF (rich text format). -} module Text.Pandoc.Writers.RTF ( writeRTF , writeRTFWithEmbeddedImages - , writeRTFWithEmbeddedImagesPure ) where import Text.Pandoc.Definition import Text.Pandoc.Options @@ -44,13 +43,13 @@ import qualified Data.ByteString as B import qualified Data.Map as M import Text.Printf ( printf ) import Text.Pandoc.ImageSize -import Text.Pandoc.Free (PandocAction, runIO) -import qualified Text.Pandoc.Free as P +import Text.Pandoc.Class (PandocMonad) +import qualified Text.Pandoc.Class as P -- | Convert Image inlines into a raw RTF embedded image, read from a file, -- or a MediaBag, or the internet. -- If file not found or filetype not jpeg or png, leave the inline unchanged. -rtfEmbedImage :: WriterOptions -> Inline -> PandocAction Inline +rtfEmbedImage :: PandocMonad m => WriterOptions -> Inline -> m Inline rtfEmbedImage opts x@(Image attr _ (src,_)) = do result <- P.fetchItem' (writerMediaBag opts) (writerSourceURL opts) src case result of @@ -83,12 +82,8 @@ rtfEmbedImage _ x = return x -- | Convert Pandoc to a string in rich text format, with -- images embedded as encoded binary data. -writeRTFWithEmbeddedImages :: WriterOptions -> Pandoc -> IO String +writeRTFWithEmbeddedImages :: PandocMonad m => WriterOptions -> Pandoc -> m String writeRTFWithEmbeddedImages options doc = - runIO $ writeRTF options `fmap` walkM (rtfEmbedImage options) doc - -writeRTFWithEmbeddedImagesPure :: WriterOptions -> Pandoc -> PandocAction String -writeRTFWithEmbeddedImagesPure options doc = writeRTF options `fmap` walkM (rtfEmbedImage options) doc -- | Convert Pandoc to a string in rich text format. -- cgit v1.2.3 From 65953181423559c20a79ef1407843143991f3a46 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Mon, 21 Nov 2016 10:13:14 -0500 Subject: Remove Text.Pandoc.Free --- pandoc.cabal | 1 - src/Text/Pandoc/Free.hs | 269 ------------------------------------------------ 2 files changed, 270 deletions(-) delete mode 100644 src/Text/Pandoc/Free.hs (limited to 'src/Text') diff --git a/pandoc.cabal b/pandoc.cabal index 7296643c9..77ef869a5 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -384,7 +384,6 @@ Library Text.Pandoc.SelfContained, Text.Pandoc.Process, Text.Pandoc.CSS, - Text.Pandoc.Free, Text.Pandoc.Class Other-Modules: Text.Pandoc.Readers.Docx.Lists, Text.Pandoc.Readers.Docx.Combine, diff --git a/src/Text/Pandoc/Free.hs b/src/Text/Pandoc/Free.hs deleted file mode 100644 index a1ea45cd6..000000000 --- a/src/Text/Pandoc/Free.hs +++ /dev/null @@ -1,269 +0,0 @@ -{-# LANGUAGE DeriveFunctor, DeriveDataTypeable #-} - -{- -Copyright (C) 2016 Jesse Rosenthal - -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.Free - Copyright : Copyright (C) 2016 Jesse Rosenthal - License : GNU GPL, version 2 or above - - Maintainer : Jesse Rosenthal - Stability : alpha - Portability : portable - -Pure implementations of the IO monads used in Pandoc's readers and writers. --} - -module Text.Pandoc.Free ( PandocActionF(..) - , PandocAction - , runIO - , runTest - , TestState(..) - , TestEnv(..) - , liftF - -- - , lookupEnv - , getCurrentTime - , getPOSIXTime - , getDefaultReferenceDocx - , getDefaultReferenceODT - , newStdGen - , newUniqueHash - , readFileLazy - , readDataFile - , fetchItem - , fetchItem' - , warn - , fail - , glob - ) where - -import Prelude hiding (readFile, fail) -import qualified Control.Monad as M (fail) -import System.Random (StdGen, next) -import qualified System.Random as IO (newStdGen) -import Codec.Archive.Zip (Archive, fromArchive) -import Data.Unique (hashUnique) -import qualified Data.Unique as IO (newUnique) -import qualified Text.Pandoc.Shared as IO ( fetchItem - , fetchItem' - , getDefaultReferenceDocx - , getDefaultReferenceODT - , warn - , readDataFile) -import Text.Pandoc.MediaBag (MediaBag, lookupMedia) -import Text.Pandoc.Compat.Time (UTCTime) -import qualified Text.Pandoc.Compat.Time as IO (getCurrentTime) -import Data.Time.Clock.POSIX ( utcTimeToPOSIXSeconds, POSIXTime ) -import Text.Pandoc.MIME (MimeType, getMimeType) -import qualified Data.ByteString as B -import qualified Data.ByteString.Lazy as BL -import Control.Monad.Free -import qualified Control.Exception as E -import qualified System.Environment as IO (lookupEnv) -import System.FilePath.Glob (match, compile) -import System.FilePath (()) -import qualified System.FilePath.Glob as IO (glob) -import Control.Monad.State hiding (fail) -import Control.Monad.Reader hiding (fail) -import Data.Word (Word8) -import Data.Typeable - -data PandocActionF nxt = - LookupEnv String (Maybe String -> nxt) - | GetCurrentTime (UTCTime -> nxt) - | GetDefaultReferenceDocx (Maybe FilePath) (Archive -> nxt) - | GetDefaultReferenceODT (Maybe FilePath) (Archive -> nxt) - | NewStdGen (StdGen -> nxt) - | NewUniqueHash (Int -> nxt) - | ReadFileLazy FilePath (BL.ByteString -> nxt) - | ReadDataFile (Maybe FilePath) FilePath (B.ByteString -> nxt) - | FetchItem (Maybe String) (String) - (Either E.SomeException (B.ByteString, Maybe MimeType) -> nxt) - | FetchItem' MediaBag (Maybe String) (String) - (Either E.SomeException (B.ByteString, Maybe MimeType) -> nxt) - | Glob String ([FilePath] -> nxt) - | Warn String nxt - | Fail String - deriving Functor - -type PandocAction = Free PandocActionF - -lookupEnv :: String -> PandocAction (Maybe String) -lookupEnv s = liftF $ LookupEnv s id - -getCurrentTime :: PandocAction UTCTime -getCurrentTime = liftF $ GetCurrentTime id - -getPOSIXTime :: PandocAction POSIXTime -getPOSIXTime = utcTimeToPOSIXSeconds <$> getCurrentTime - -getDefaultReferenceDocx :: Maybe FilePath -> PandocAction Archive -getDefaultReferenceDocx fp = liftF $ GetDefaultReferenceDocx fp id - -getDefaultReferenceODT :: Maybe FilePath -> PandocAction Archive -getDefaultReferenceODT fp = liftF $ GetDefaultReferenceODT fp id - -newStdGen :: PandocAction StdGen -newStdGen = liftF $ NewStdGen id - -newUniqueHash :: PandocAction Int -newUniqueHash = liftF $ NewUniqueHash id - -readFileLazy :: FilePath -> PandocAction BL.ByteString -readFileLazy fp = liftF $ ReadFileLazy fp id - -readDataFile :: Maybe FilePath -> FilePath -> PandocAction B.ByteString -readDataFile mfp fp = liftF $ ReadDataFile mfp fp id - -fetchItem :: Maybe String -> - String -> - PandocAction (Either E.SomeException (B.ByteString, Maybe MimeType)) -fetchItem ms s = liftF $ FetchItem ms s id - - -fetchItem' :: MediaBag -> - Maybe String -> - String -> - PandocAction (Either E.SomeException (B.ByteString, Maybe MimeType)) -fetchItem' mb ms s = liftF $ FetchItem' mb ms s id - -warn :: String -> PandocAction () -warn s = liftF $ Warn s () - -fail :: String -> PandocAction b -fail s = liftF $ Fail s - -glob :: String -> PandocAction [FilePath] -glob s = liftF $ Glob s id - -runIO :: PandocAction nxt -> IO nxt -runIO (Free (LookupEnv s f)) = IO.lookupEnv s >>= runIO . f -runIO (Free (GetCurrentTime f)) = IO.getCurrentTime >>= runIO . f -runIO (Free (GetDefaultReferenceDocx mfp f)) = - IO.getDefaultReferenceDocx mfp >>= runIO . f -runIO (Free (GetDefaultReferenceODT mfp f)) = - IO.getDefaultReferenceODT mfp >>= runIO . f -runIO (Free (NewStdGen f)) = IO.newStdGen >>= runIO . f -runIO (Free (NewUniqueHash f)) = hashUnique <$> IO.newUnique >>= runIO . f -runIO (Free (ReadFileLazy fp f)) = BL.readFile fp >>= runIO . f -runIO (Free (ReadDataFile mfp fp f)) = IO.readDataFile mfp fp >>= runIO . f -runIO (Free (Fail s)) = M.fail s -runIO (Free (FetchItem sourceUrl nm f)) = - IO.fetchItem sourceUrl nm >>= runIO . f -runIO (Free (FetchItem' media sourceUrl nm f)) = - IO.fetchItem' media sourceUrl nm >>= runIO . f -runIO (Free (Warn s nxt)) = IO.warn s >> runIO nxt -runIO (Free (Glob s f)) = IO.glob s >>= runIO . f -runIO (Pure r) = return r - -data TestState = TestState { stStdGen :: StdGen - , stWord8Store :: [Word8] -- should be - -- inifinite, - -- i.e. [1..] - , stWarnings :: [String] - , stUniqStore :: [Int] -- should be - -- inifinite and - -- contain every - -- element at most - -- once, e.g. [1..] - } - -data TestEnv = TestEnv { envEnv :: [(String, String)] - , envTime :: UTCTime - , envReferenceDocx :: Archive - , envReferenceODT :: Archive - , envFiles :: [(FilePath, B.ByteString)] - , envUserDataDir :: [(FilePath, B.ByteString)] - , envCabalDataDir :: [(FilePath, B.ByteString)] - , envFontFiles :: [FilePath] - } - -data TestException = TestException - deriving (Show, Typeable) - -instance E.Exception TestException - -type Testing = ReaderT TestEnv (State TestState) - -runTest :: PandocAction nxt -> Testing nxt -runTest (Free (LookupEnv s f)) = do - env <- asks envEnv - return (lookup s env) >>= runTest . f -runTest (Free (GetCurrentTime f)) = - asks envTime >>= runTest . f -runTest (Free (GetDefaultReferenceDocx _ f)) = - asks envReferenceDocx >>= runTest . f -runTest (Free (GetDefaultReferenceODT _ f)) = - asks envReferenceODT >>= runTest . f -runTest (Free (NewStdGen f)) = do - g <- gets stStdGen - let (_, nxtGen) = next g - modify $ \st -> st { stStdGen = nxtGen } - return g >>= runTest . f -runTest (Free (NewUniqueHash f)) = do - uniqs <- gets stUniqStore - case uniqs of - u : us -> do - modify $ \st -> st { stUniqStore = us } - return u >>= runTest . f - _ -> M.fail "uniq store ran out of elements" -runTest (Free (ReadFileLazy fp f)) = do - fps <- asks envFiles - case lookup fp fps of - Just bs -> return (BL.fromStrict bs) >>= runTest . f - Nothing -> error "openFile: does not exist" --- A few different cases of readDataFile to reimplement, for when --- there is no filepath and it falls through to readDefaultDataFile -runTest (Free (ReadDataFile Nothing "reference.docx" f)) = do - (B.concat . BL.toChunks . fromArchive) <$> - (runTest $ getDefaultReferenceDocx Nothing) >>= - runTest . f -runTest (Free (ReadDataFile Nothing "reference.odt" f)) = do - (B.concat . BL.toChunks . fromArchive) <$> - (runTest $ getDefaultReferenceODT Nothing) >>= - runTest . f -runTest (Free (ReadDataFile Nothing fname f)) = do - let fname' = if fname == "MANUAL.txt" then fname else "data" fname - runTest (BL.toStrict <$> readFileLazy fname') >>= runTest . f -runTest (Free (ReadDataFile (Just userDir) fname f)) = do - userDirFiles <- asks envUserDataDir - case lookup (userDir fname) userDirFiles of - Just bs -> return bs >>= runTest . f - Nothing -> runTest (readDataFile Nothing fname) >>= runTest . f -runTest (Free (Fail s)) = M.fail s -runTest (Free (FetchItem _ fp f)) = do - fps <- asks envFiles - case lookup fp fps of - Just bs -> return (Right (bs, getMimeType fp)) >>= runTest . f - Nothing -> return (Left $ E.toException TestException) >>= runTest . f -runTest (Free (FetchItem' media sourceUrl nm f)) = do - case lookupMedia nm media of - Nothing -> runTest (fetchItem sourceUrl nm) >>= runTest . f - Just (mime, bs) -> return (Right (B.concat $ BL.toChunks bs, Just mime)) >>= runTest . f -runTest (Free (Warn s nxt)) = do - modify $ \st -> st { stWarnings = s : stWarnings st } - runTest nxt -runTest (Free (Glob s f)) = do - fontFiles <- asks envFontFiles - return (filter (match (compile s)) fontFiles) >>= runTest . f -runTest (Pure r) = return r - - - -- cgit v1.2.3 From 45f3c53dd9d540abac958b13783677c263aa6658 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Mon, 21 Nov 2016 19:04:39 -0500 Subject: Class: Specify Functor and Applicative We're still compiling for 7.8 which is pre-AMP, so let's just be explicit about it so we can use applicative notation. --- src/Text/Pandoc/Class.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index aca1067c6..2f5d179fe 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -67,7 +67,7 @@ import Control.Monad.Reader hiding (fail) import Data.Word (Word8) import Data.Typeable -class Monad m => PandocMonad m where +class (Functor m, Applicative m, Monad m) => PandocMonad m where lookupEnv :: String -> m (Maybe String) getCurrentTime :: m UTCTime getDefaultReferenceDocx :: Maybe FilePath -> m Archive -- cgit v1.2.3 From a94f3dddee60360b8ba6a18ac2633fce2104cf02 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Thu, 24 Nov 2016 09:54:18 -0500 Subject: Make opaque typeclasses PandocPure and PandocIO --- src/Text/Pandoc/Class.hs | 108 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 75 insertions(+), 33 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 2f5d179fe..f7915b27d 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -1,4 +1,5 @@ -{-# LANGUAGE DeriveFunctor, DeriveDataTypeable, TypeSynonymInstances, FlexibleInstances #-} +{-# LANGUAGE DeriveFunctor, DeriveDataTypeable, TypeSynonymInstances, +FlexibleInstances, GeneralizedNewtypeDeriving, FlexibleContexts #-} {- Copyright (C) 2016 Jesse Rosenthal @@ -31,10 +32,14 @@ Typeclass for pandoc readers and writers, allowing both IO and pure instances. -} module Text.Pandoc.Class ( PandocMonad(..) - , Testing , TestState(..) , TestEnv(..) , getPOSIXTime + , PandocIO(..) + , PandocPure(..) + , PandocExecutionError(..) + , runIO + , runIOorExplode ) where import Prelude hiding (readFile, fail) @@ -64,10 +69,13 @@ import System.FilePath (()) import qualified System.FilePath.Glob as IO (glob) import Control.Monad.State hiding (fail) import Control.Monad.Reader hiding (fail) +import Control.Monad.Except hiding (fail) import Data.Word (Word8) import Data.Typeable +import Data.Default +import System.IO.Error -class (Functor m, Applicative m, Monad m) => PandocMonad m where +class (Functor m, Applicative m, Monad m, MonadError PandocExecutionError m) => PandocMonad m where lookupEnv :: String -> m (Maybe String) getCurrentTime :: m UTCTime getDefaultReferenceDocx :: Maybe FilePath -> m Archive @@ -75,14 +83,16 @@ class (Functor m, Applicative m, Monad m) => PandocMonad m where newStdGen :: m StdGen newUniqueHash :: m Int readFileLazy :: FilePath -> m BL.ByteString - readDataFile :: Maybe FilePath -> FilePath -> m B.ByteString - fetchItem :: Maybe String -> - String -> - m (Either E.SomeException (B.ByteString, Maybe MimeType)) - fetchItem' :: MediaBag -> - Maybe String -> - String -> - m (Either E.SomeException (B.ByteString, Maybe MimeType)) + readDataFile :: Maybe FilePath + -> FilePath + -> m B.ByteString + fetchItem :: Maybe String + -> String + -> m (Either E.SomeException (B.ByteString, Maybe MimeType)) + fetchItem' :: MediaBag + -> Maybe String + -> String + -> m (Either E.SomeException (B.ByteString, Maybe MimeType)) warn :: String -> m () fail :: String -> m b glob :: String -> m [FilePath] @@ -92,22 +102,55 @@ class (Functor m, Applicative m, Monad m) => PandocMonad m where getPOSIXTime :: (PandocMonad m) => m POSIXTime getPOSIXTime = utcTimeToPOSIXSeconds <$> getCurrentTime -instance PandocMonad IO where - lookupEnv = IO.lookupEnv - getCurrentTime = IO.getCurrentTime - getDefaultReferenceDocx = IO.getDefaultReferenceDocx - getDefaultReferenceODT = IO.getDefaultReferenceODT - newStdGen = IO.newStdGen - newUniqueHash = hashUnique <$> IO.newUnique - readFileLazy = BL.readFile - readDataFile = IO.readDataFile - fail = M.fail - fetchItem = IO.fetchItem - fetchItem' = IO.fetchItem' - warn = IO.warn - glob = IO.glob - +-- We can add to this as we go +data PandocExecutionError = PandocFileReadError String + deriving Show + +-- Nothing in this for now, but let's put it there anyway. +data PandocStateIO = PandocStateIO + deriving Show + +instance Default PandocStateIO where + def = PandocStateIO + +runIO :: PandocIO a -> IO (Either PandocExecutionError a) +runIO ma = flip evalStateT def $ runExceptT $ unPandocIO ma + +runIOorExplode :: PandocIO a -> IO a +runIOorExplode ma = do + eitherVal <- runIO ma + case eitherVal of + Right x -> return x + Left (PandocFileReadError s) -> error s + +newtype PandocIO a = PandocIO { + unPandocIO :: ExceptT PandocExecutionError (StateT PandocStateIO IO) a + } deriving (MonadIO, Functor, Applicative, Monad, MonadError PandocExecutionError) + +instance PandocMonad PandocIO where + lookupEnv = liftIO . IO.lookupEnv + getCurrentTime = liftIO IO.getCurrentTime + getDefaultReferenceDocx = liftIO . IO.getDefaultReferenceDocx + getDefaultReferenceODT = liftIO . IO.getDefaultReferenceODT + newStdGen = liftIO IO.newStdGen + newUniqueHash = hashUnique <$> (liftIO IO.newUnique) + readFileLazy s = do + eitherBS <- liftIO (tryIOError $ BL.readFile s) + case eitherBS of + Right bs -> return bs + Left _ -> throwError $ PandocFileReadError $ "file not found: " ++ s + -- TODO: Make this more sensitive to the different sorts of failure + readDataFile mfp fname = do + eitherBS <- liftIO (tryIOError $ IO.readDataFile mfp fname) + case eitherBS of + Right bs -> return bs + Left _ -> throwError $ PandocFileReadError $ "file not found: " ++ fname + fail = M.fail + fetchItem ms s = liftIO $ IO.fetchItem ms s + fetchItem' mb ms s = liftIO $ IO.fetchItem' mb ms s + warn = liftIO . IO.warn + glob = liftIO . IO.glob data TestState = TestState { stStdGen :: StdGen , stWord8Store :: [Word8] -- should be @@ -136,9 +179,12 @@ data TestException = TestException instance E.Exception TestException -type Testing = ReaderT TestEnv (State TestState) +newtype PandocPure a = PandocPure { + unPandocPure :: ExceptT PandocExecutionError + (ReaderT TestEnv (State TestState)) a + } deriving (Functor, Applicative, Monad, MonadReader TestEnv, MonadState TestState, MonadError PandocExecutionError) -instance PandocMonad Testing where +instance PandocMonad PandocPure where lookupEnv s = do env <- asks envEnv return (lookup s env) @@ -162,13 +208,11 @@ instance PandocMonad Testing where modify $ \st -> st { stUniqStore = us } return u _ -> M.fail "uniq store ran out of elements" - readFileLazy fp = do fps <- asks envFiles case lookup fp fps of Just bs -> return (BL.fromStrict bs) - Nothing -> error "openFile: does not exist" - + Nothing -> throwError $ PandocFileReadError "file not in state" readDataFile Nothing "reference.docx" = do (B.concat . BL.toChunks . fromArchive) <$> (getDefaultReferenceDocx Nothing) readDataFile Nothing "reference.odt" = do @@ -181,9 +225,7 @@ instance PandocMonad Testing where case lookup (userDir fname) userDirFiles of Just bs -> return bs Nothing -> readDataFile Nothing fname - fail = M.fail - fetchItem _ fp = do fps <- asks envFiles case lookup fp fps of -- cgit v1.2.3 From 989971fce1066db17f104fb52bf64d990d3767ab Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Thu, 24 Nov 2016 10:22:23 -0500 Subject: Pandoc.hs: Run `runIOorExplode` on IO functions. This is a compatibility layer to reintroduce something like the old errors into the functions. --- src/Text/Pandoc.hs | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index d83fa85e7..703d0a002 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -180,6 +180,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 Data.Aeson import qualified Data.ByteString.Lazy as BL import Data.List (intercalate) @@ -270,17 +271,23 @@ writers :: [ ( String, Writer ) ] writers = [ ("native" , PureStringWriter writeNative) ,("json" , PureStringWriter writeJSON) - ,("docx" , IOByteStringWriter writeDocx) - ,("odt" , IOByteStringWriter writeODT) - ,("epub" , IOByteStringWriter $ \o -> - writeEPUB o{ writerEpubVersion = Just EPUB2 }) - ,("epub3" , IOByteStringWriter $ \o -> - writeEPUB o{ writerEpubVersion = Just EPUB3 }) - ,("fb2" , IOStringWriter writeFB2) + ,("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 writeICML) + ,("icml" , IOStringWriter $ \o doc -> + runIOorExplode $ writeICML o doc) ,("s5" , PureStringWriter $ \o -> writeHtmlString o{ writerSlideVariant = S5Slides , writerTableOfContents = False }) @@ -316,7 +323,8 @@ writers = [ ,("dokuwiki" , PureStringWriter writeDokuWiki) ,("zimwiki" , PureStringWriter writeZimWiki) ,("textile" , PureStringWriter writeTextile) - ,("rtf" , IOStringWriter writeRTFWithEmbeddedImages) + ,("rtf" , IOStringWriter $ \o doc -> + runIOorExplode $ writeRTFWithEmbeddedImages o doc) ,("org" , PureStringWriter writeOrg) ,("asciidoc" , PureStringWriter writeAsciiDoc) ,("haddock" , PureStringWriter writeHaddock) -- cgit v1.2.3 From b2721c6b02c860553b5ec7c2596652adac2f2f0f Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Thu, 24 Nov 2016 11:39:09 -0500 Subject: Make PandocExecutionError an exception Until we fix fetchItem and fetchItem' to make use of MonadError, we have to thow an exception. We'll throw PandocFileReadError. Note that this is temporary. --- src/Text/Pandoc/Class.hs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index f7915b27d..0135ac6b3 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -105,7 +105,7 @@ getPOSIXTime = utcTimeToPOSIXSeconds <$> getCurrentTime -- We can add to this as we go data PandocExecutionError = PandocFileReadError String - deriving Show + deriving (Show, Typeable) -- Nothing in this for now, but let's put it there anyway. data PandocStateIO = PandocStateIO @@ -174,10 +174,7 @@ data TestEnv = TestEnv { envEnv :: [(String, String)] , envFontFiles :: [FilePath] } -data TestException = TestException - deriving (Show, Typeable) - -instance E.Exception TestException +instance E.Exception PandocExecutionError newtype PandocPure a = PandocPure { unPandocPure :: ExceptT PandocExecutionError @@ -230,7 +227,7 @@ instance PandocMonad PandocPure where fps <- asks envFiles case lookup fp fps of Just bs -> return (Right (bs, getMimeType fp)) - Nothing -> return (Left $ E.toException TestException) + Nothing -> return (Left $ E.toException $ PandocFileReadError "oops") fetchItem' media sourceUrl nm = do case lookupMedia nm media of -- cgit v1.2.3 From b19f79f672c49322328584fa339215e4234d98af Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Thu, 24 Nov 2016 11:52:06 -0500 Subject: Add runPure function. This requires a default environment. The state variables are pretty straightforward. The env variables are a little trickier. I'm just making most of them empty for now. Note that some of them (like defaultReferenceDocx/ODT) will be coming out soon anyway. --- src/Text/Pandoc/Class.hs | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 0135ac6b3..64fd7e907 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -40,13 +40,14 @@ module Text.Pandoc.Class ( PandocMonad(..) , PandocExecutionError(..) , runIO , runIOorExplode + , runPure ) where import Prelude hiding (readFile, fail) import qualified Control.Monad as M (fail) -import System.Random (StdGen, next) +import System.Random (StdGen, next, mkStdGen) import qualified System.Random as IO (newStdGen) -import Codec.Archive.Zip (Archive, fromArchive) +import Codec.Archive.Zip (Archive, fromArchive, emptyArchive) import Data.Unique (hashUnique) import qualified Data.Unique as IO (newUnique) import qualified Text.Pandoc.Shared as IO ( fetchItem @@ -58,7 +59,9 @@ import qualified Text.Pandoc.Shared as IO ( fetchItem import Text.Pandoc.MediaBag (MediaBag, lookupMedia) import Text.Pandoc.Compat.Time (UTCTime) import qualified Text.Pandoc.Compat.Time as IO (getCurrentTime) -import Data.Time.Clock.POSIX ( utcTimeToPOSIXSeconds, POSIXTime ) +import Data.Time.Clock.POSIX ( utcTimeToPOSIXSeconds + , posixSecondsToUTCTime + , POSIXTime ) import Text.Pandoc.MIME (MimeType, getMimeType) import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL @@ -164,6 +167,13 @@ data TestState = TestState { stStdGen :: StdGen -- once, e.g. [1..] } +instance Default TestState where + def = TestState { stStdGen = mkStdGen 1848 + , stWord8Store = [1..] + , stWarnings = [] + , stUniqStore = [1..] + } + data TestEnv = TestEnv { envEnv :: [(String, String)] , envTime :: UTCTime , envReferenceDocx :: Archive @@ -174,6 +184,19 @@ data TestEnv = TestEnv { envEnv :: [(String, String)] , envFontFiles :: [FilePath] } +-- We have to figure this out a bit more. But let's put some empty +-- values in for the time being. +instance Default TestEnv where + def = TestEnv { envEnv = [("USER", "pandoc-user")] + , envTime = posixSecondsToUTCTime 0 + , envReferenceDocx = emptyArchive + , envReferenceODT = emptyArchive + , envFiles = [] + , envUserDataDir = [] + , envCabalDataDir = [] + , envFontFiles = [] + } + instance E.Exception PandocExecutionError newtype PandocPure a = PandocPure { @@ -181,6 +204,9 @@ newtype PandocPure a = PandocPure { (ReaderT TestEnv (State TestState)) a } deriving (Functor, Applicative, Monad, MonadReader TestEnv, MonadState TestState, MonadError PandocExecutionError) +runPure :: PandocPure a -> Either PandocExecutionError a +runPure x = flip evalState def $ flip runReaderT def $ runExceptT $ unPandocPure x + instance PandocMonad PandocPure where lookupEnv s = do env <- asks envEnv -- cgit v1.2.3 From 04487779b26458597fb751325b24c576b5088662 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Sat, 26 Nov 2016 08:46:28 -0500 Subject: Convert all writers to use PandocMonad. Since PandocMonad is an instance of MonadError, this will allow us, in a future commit, to change all invocations of `error` to `throwError`, which will be preferable for the pure versions. At the moment, we're disabling the lua custom writers (this is temporary). This requires changing the type of the Writer in Text.Pandoc. Right now, we run `runIOorExplode` in pandoc.hs, to make the conversion easier. We can switch it to the safer `runIO` in the future. Note that this required a change to Text.Pandoc.PDF as well. Since running an external program is necessarily IO, we can be clearer about using PandocIO. --- pandoc.hs | 39 +++--- src/Text/Pandoc.hs | 216 ++++++++++++++++++++++---------- src/Text/Pandoc/PDF.hs | 9 +- src/Text/Pandoc/Writers/AsciiDoc.hs | 5 +- src/Text/Pandoc/Writers/CommonMark.hs | 134 +++++++++++--------- src/Text/Pandoc/Writers/ConTeXt.hs | 5 +- src/Text/Pandoc/Writers/Docbook.hs | 5 +- src/Text/Pandoc/Writers/DokuWiki.hs | 5 +- src/Text/Pandoc/Writers/EPUB.hs | 44 +++---- src/Text/Pandoc/Writers/HTML.hs | 9 +- src/Text/Pandoc/Writers/Haddock.hs | 5 +- src/Text/Pandoc/Writers/LaTeX.hs | 5 +- src/Text/Pandoc/Writers/Man.hs | 5 +- src/Text/Pandoc/Writers/Markdown.hs | 89 +++++++------ src/Text/Pandoc/Writers/MediaWiki.hs | 5 +- src/Text/Pandoc/Writers/Native.hs | 5 +- src/Text/Pandoc/Writers/ODT.hs | 2 +- src/Text/Pandoc/Writers/OPML.hs | 50 ++++---- src/Text/Pandoc/Writers/OpenDocument.hs | 5 +- src/Text/Pandoc/Writers/Org.hs | 5 +- src/Text/Pandoc/Writers/RST.hs | 5 +- src/Text/Pandoc/Writers/TEI.hs | 5 +- src/Text/Pandoc/Writers/Texinfo.hs | 5 +- src/Text/Pandoc/Writers/Textile.hs | 5 +- src/Text/Pandoc/Writers/ZimWiki.hs | 5 +- 25 files changed, 396 insertions(+), 276 deletions(-) (limited to 'src/Text') diff --git a/pandoc.hs b/pandoc.hs index a032922be..662dd3e3b 100644 --- a/pandoc.hs +++ b/pandoc.hs @@ -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 @@ -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 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") $ "
\n\"cover\n
"]) + (Pandoc meta [RawBlock (Format "html") $ "
\n\"cover\n
"])) 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 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 -- cgit v1.2.3 From 23c5b0d0f1901aa3ab68391f927de4f5278b5942 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Sat, 26 Nov 2016 23:43:54 -0500 Subject: Implement Errors in PandocMonad Errors can be thrown purely with `throwError`. At the moment there are only three kinds of errors: 1. PandocFileReadError FilePath (for problems reading a file from the filesystem) 2. PandocShouldNeverHappenError String (for stuff that should never happen but we need to pattern-match anyway) 3. PandocSomeError String (a grab bag of everything else) Of course, we need to subdivide the third item in this list. --- src/Text/Pandoc/Class.hs | 18 +++++--- src/Text/Pandoc/Writers/EPUB.hs | 28 +++++++----- src/Text/Pandoc/Writers/FB2.hs | 5 ++- src/Text/Pandoc/Writers/HTML.hs | 70 ++++++++++++++++------------- src/Text/Pandoc/Writers/Man.hs | 57 +++++++++++++----------- src/Text/Pandoc/Writers/Markdown.hs | 19 ++++---- src/Text/Pandoc/Writers/OPML.hs | 16 ++++--- src/Text/Pandoc/Writers/RTF.hs | 11 ++--- src/Text/Pandoc/Writers/Texinfo.hs | 89 ++++++++++++++++++++++--------------- 9 files changed, 179 insertions(+), 134 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 64fd7e907..69d2bb761 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -107,8 +107,10 @@ getPOSIXTime = utcTimeToPOSIXSeconds <$> getCurrentTime -- We can add to this as we go -data PandocExecutionError = PandocFileReadError String - deriving (Show, Typeable) +data PandocExecutionError = PandocFileReadError FilePath + | PandocShouldNeverHappenError String + | PandocSomeError String + deriving (Show, Typeable) -- Nothing in this for now, but let's put it there anyway. data PandocStateIO = PandocStateIO @@ -125,7 +127,9 @@ runIOorExplode ma = do eitherVal <- runIO ma case eitherVal of Right x -> return x - Left (PandocFileReadError s) -> error s + Left (PandocFileReadError fp) -> error $ "promple reading " ++ fp + Left (PandocShouldNeverHappenError s) -> error s + Left (PandocSomeError s) -> error s newtype PandocIO a = PandocIO { unPandocIO :: ExceptT PandocExecutionError (StateT PandocStateIO IO) a @@ -142,13 +146,13 @@ instance PandocMonad PandocIO where eitherBS <- liftIO (tryIOError $ BL.readFile s) case eitherBS of Right bs -> return bs - Left _ -> throwError $ PandocFileReadError $ "file not found: " ++ s + Left _ -> throwError $ PandocFileReadError s -- TODO: Make this more sensitive to the different sorts of failure readDataFile mfp fname = do eitherBS <- liftIO (tryIOError $ IO.readDataFile mfp fname) case eitherBS of Right bs -> return bs - Left _ -> throwError $ PandocFileReadError $ "file not found: " ++ fname + Left _ -> throwError $ PandocFileReadError fname fail = M.fail fetchItem ms s = liftIO $ IO.fetchItem ms s fetchItem' mb ms s = liftIO $ IO.fetchItem' mb ms s @@ -235,7 +239,7 @@ instance PandocMonad PandocPure where fps <- asks envFiles case lookup fp fps of Just bs -> return (BL.fromStrict bs) - Nothing -> throwError $ PandocFileReadError "file not in state" + Nothing -> throwError $ PandocFileReadError fp readDataFile Nothing "reference.docx" = do (B.concat . BL.toChunks . fromArchive) <$> (getDefaultReferenceDocx Nothing) readDataFile Nothing "reference.odt" = do @@ -253,7 +257,7 @@ instance PandocMonad PandocPure where fps <- asks envFiles case lookup fp fps of Just bs -> return (Right (bs, getMimeType fp)) - Nothing -> return (Left $ E.toException $ PandocFileReadError "oops") + Nothing -> return (Left $ E.toException $ PandocFileReadError fp) fetchItem' media sourceUrl nm = do case lookupMedia nm media of diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index 298561db6..580b12210 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -64,7 +64,8 @@ import Data.Char ( toLower, isDigit, isAlphaNum ) import Text.Pandoc.MIME (MimeType, getMimeType, extensionFromMimeType) import Text.Blaze.Html.Renderer.Utf8 (renderHtml) import Text.HTML.TagSoup (Tag(TagOpen), fromAttrib, parseTags) -import Text.Pandoc.Class (PandocMonad) +import Control.Monad.Except (throwError) +import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) import qualified Text.Pandoc.Class as P -- A Chapter includes a list of blocks and maybe a section @@ -532,9 +533,9 @@ pandocToEPUB opts doc@(Pandoc meta _) = do let tocTitle = fromMaybe plainTitle $ metaValueToString <$> lookupMeta "toc-title" meta - let uuid = case epubIdentifier metadata of - (x:_) -> identifierText x -- use first identifier as UUID - [] -> error "epubIdentifier is null" -- shouldn't happen + uuid <- case epubIdentifier metadata of + (x:_) -> return $ identifierText x -- use first identifier as UUID + [] -> throwError $ PandocShouldNeverHappenError "epubIdentifier is null" -- shouldn't happen currentTime <- lift $ P.getCurrentTime let contentsData = UTF8.fromStringLazy $ ppTopElement $ unode "package" ! [("version", case version of @@ -590,8 +591,9 @@ pandocToEPUB opts doc@(Pandoc meta _) = do let tocLevel = writerTOCDepth opts - let navPointNode :: (Int -> String -> String -> [Element] -> Element) - -> S.Element -> State Int Element + let navPointNode :: PandocMonad m + => (Int -> String -> String -> [Element] -> Element) + -> S.Element -> StateT Int m Element navPointNode formatter (S.Sec _ nums (ident,_,_) ils children) = do n <- get modify (+1) @@ -601,15 +603,15 @@ pandocToEPUB opts doc@(Pandoc meta _) = do let tit = if writerNumberSections opts && not (null nums) then showNums nums ++ " " ++ tit' else tit' - let src = case lookup ident reftable of - Just x -> x - Nothing -> error (ident ++ " not found in reftable") + src <- case lookup ident reftable of + Just x -> return x + Nothing -> throwError $ PandocSomeError $ ident ++ " not found in reftable" let isSec (S.Sec lev _ _ _ _) = lev <= tocLevel isSec _ = False let subsecs = filter isSec children subs <- mapM (navPointNode formatter) subsecs return $ formatter n tit src subs - navPointNode _ (S.Blk _) = error "navPointNode encountered Blk" + navPointNode _ (S.Blk _) = throwError $ PandocSomeError "navPointNode encountered Blk" let navMapFormatter :: Int -> String -> String -> [Element] -> Element navMapFormatter n tit src subs = unode "navPoint" ! @@ -622,6 +624,7 @@ pandocToEPUB opts doc@(Pandoc meta _) = do [ unode "navLabel" $ unode "text" (stringify $ docTitle' meta) , unode "content" ! [("src","title_page.xhtml")] $ () ] + navMap <- lift $ evalStateT (mapM (navPointNode navMapFormatter) secs) 1 let tocData = UTF8.fromStringLazy $ ppTopElement $ unode "ncx" ! [("version","2005-1") ,("xmlns","http://www.daisy.org/z3986/2005/ncx/")] $ @@ -640,7 +643,7 @@ pandocToEPUB opts doc@(Pandoc meta _) = do ("content", toId img)] $ ()] , unode "docTitle" $ unode "text" $ plainTitle , unode "navMap" $ - tpNode : evalState (mapM (navPointNode navMapFormatter) secs) 1 + tpNode : navMap ] let tocEntry = mkEntry "toc.ncx" tocData @@ -654,11 +657,12 @@ pandocToEPUB opts doc@(Pandoc meta _) = do (_:_) -> [unode "ol" ! [("class","toc")] $ subs] let navtag = if epub3 then "nav" else "div" + tocBlocks <- lift $ evalStateT (mapM (navPointNode navXhtmlFormatter) secs) 1 let navBlocks = [RawBlock (Format "html") $ ppElement $ unode navtag ! ([("epub:type","toc") | epub3] ++ [("id","toc")]) $ [ unode "h1" ! [("id","toc-title")] $ tocTitle - , unode "ol" ! [("class","toc")] $ evalState (mapM (navPointNode navXhtmlFormatter) secs) 1]] + , unode "ol" ! [("class","toc")] $ tocBlocks ]] let landmarks = if epub3 then [RawBlock (Format "html") $ ppElement $ unode "nav" ! [("epub:type","landmarks") diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index 58bfe7615..5c22c8586 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -39,13 +39,14 @@ import Text.XML.Light import qualified Text.XML.Light as X import qualified Text.XML.Light.Cursor as XC import qualified Data.ByteString.Char8 as B8 +import Control.Monad.Except (throwError) import Text.Pandoc.Definition import Text.Pandoc.Options (WriterOptions(..), HTMLMathMethod(..), def) import Text.Pandoc.Shared (orderedListMarkers, isHeaderBlock, capitalize, linesToPara) -import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) import qualified Text.Pandoc.Class as P -- | Data to be written at the end of the document: @@ -348,7 +349,7 @@ blockToXml (DefinitionList defs) = needsBreak (Plain ins) = LineBreak `notElem` ins needsBreak _ = True blockToXml (Header _ _ _) = -- should never happen, see renderSections - error "unexpected header in section text" + throwError $ PandocShouldNeverHappenError "unexpected header in section text" blockToXml HorizontalRule = return [ el "empty-line" () , el "p" (txt (replicate 10 '—')) diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 6f25939f0..4520708e4 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -68,7 +68,8 @@ 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) +import Control.Monad.Except (throwError) +import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) data WriterState = WriterState { stNotes :: [Html] -- ^ List of notes @@ -101,26 +102,27 @@ nl opts = if writerWrapText opts == WrapNone -- | Convert Pandoc document to Html string. 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 - Just tpl -> renderTemplate' tpl $ - defField "body" (renderHtml body) context +writeHtmlString opts d = do + (body, context) <- evalStateT (pandocToHtml opts d) defaultWriterState + return $ case writerTemplate opts of + Nothing -> renderHtml body + Just tpl -> renderTemplate' tpl $ + defField "body" (renderHtml body) context -- | Convert Pandoc document to Html structure. 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 - Just tpl -> renderTemplate' tpl $ - defField "body" (renderHtml body) context +writeHtml opts d = do + (body, context) <- evalStateT (pandocToHtml opts d) defaultWriterState + return $ case writerTemplate opts of + Nothing -> renderHtml body + Just tpl -> renderTemplate' tpl $ + defField "body" (renderHtml body) context -- result is (title, authors, date, toc, body, new variables) -pandocToHtml :: WriterOptions +pandocToHtml :: PandocMonad m + => WriterOptions -> Pandoc - -> State WriterState (Html, Value) + -> StateT WriterState m (Html, Value) pandocToHtml opts (Pandoc meta blocks) = do metadata <- metaToJSON opts (fmap renderHtml . blockListToHtml opts) @@ -222,7 +224,7 @@ defList :: WriterOptions -> [Html] -> Html defList opts items = toList H.dl opts (items ++ [nl opts]) -- | Construct table of contents from list of elements. -tableOfContents :: WriterOptions -> [Element] -> State WriterState (Maybe Html) +tableOfContents :: PandocMonad m => WriterOptions -> [Element] -> StateT WriterState m (Maybe Html) tableOfContents _ [] = return Nothing tableOfContents opts sects = do let opts' = opts { writerIgnoreNotes = True } @@ -238,7 +240,7 @@ showSecNum = concat . intersperse "." . map show -- | Converts an Element to a list item for a table of contents, -- retrieving the appropriate identifier from state. -elementToListItem :: WriterOptions -> Element -> State WriterState (Maybe Html) +elementToListItem :: PandocMonad m => WriterOptions -> Element -> StateT WriterState m (Maybe Html) -- Don't include the empty headers created in slide shows -- shows when an hrule is used to separate slides without a new title: elementToListItem _ (Sec _ _ _ [Str "\0"] _) = return Nothing @@ -266,7 +268,7 @@ elementToListItem opts (Sec lev num (id',classes,_) headerText subsecs) elementToListItem _ _ = return Nothing -- | Convert an Element to Html. -elementToHtml :: Int -> WriterOptions -> Element -> State WriterState Html +elementToHtml :: PandocMonad m => Int -> WriterOptions -> Element -> StateT WriterState m Html elementToHtml _slideLevel opts (Blk block) = blockToHtml opts block elementToHtml slideLevel opts (Sec level num (id',classes,keyvals) title' elements) = do let slide = writerSlideVariant opts /= NoSlides && level <= slideLevel @@ -347,9 +349,9 @@ parseMailto s = do _ -> fail "not a mailto: URL" -- | Obfuscate a "mailto:" link. -obfuscateLink :: WriterOptions -> Attr -> Html -> String -> Html +obfuscateLink :: PandocMonad m => WriterOptions -> Attr -> Html -> String -> m Html obfuscateLink opts attr txt s | writerEmailObfuscation opts == NoObfuscation = - addAttrs opts attr $ H.a ! A.href (toValue s) $ txt + return $ addAttrs opts attr $ H.a ! A.href (toValue s) $ txt obfuscateLink opts attr (renderHtml -> txt) s = let meth = writerEmailObfuscation opts s' = map toLower (take 7 s) ++ drop 7 s @@ -365,9 +367,11 @@ obfuscateLink opts attr (renderHtml -> txt) s = in case meth of ReferenceObfuscation -> -- need to use preEscapedString or &'s are escaped to & in URL + return $ preEscapedString $ "" ++ (obfuscateString txt) ++ "" JavascriptObfuscation -> + return $ (H.script ! A.type_ "text/javascript" $ preEscapedString ("\n\n")) >> H.noscript (preEscapedString $ obfuscateString altText) - _ -> error $ "Unknown obfuscation method: " ++ show meth - _ -> addAttrs opts attr $ H.a ! A.href (toValue s) $ toHtml txt -- malformed email + _ -> throwError $ PandocSomeError $ "Unknown obfuscation method: " ++ show meth + _ -> return $ addAttrs opts attr $ H.a ! A.href (toValue s) $ toHtml txt -- malformed email -- | Obfuscate character as entity. obfuscateChar :: Char -> String @@ -435,7 +439,7 @@ treatAsImage fp = in null ext || ext `elem` imageExts -- | Convert Pandoc block element to HTML. -blockToHtml :: WriterOptions -> Block -> State WriterState Html +blockToHtml :: PandocMonad m => WriterOptions -> Block -> StateT WriterState m Html blockToHtml _ Null = return mempty blockToHtml opts (Plain lst) = inlineListToHtml opts lst -- title beginning with fig: indicates that the image is a figure @@ -625,11 +629,12 @@ blockToHtml opts (Table capt aligns widths headers rows') = do else tbl ! A.style (toValue $ "width:" ++ show (round (totalWidth * 100) :: Int) ++ "%;") -tableRowToHtml :: WriterOptions +tableRowToHtml :: PandocMonad m + => WriterOptions -> [Alignment] -> Int -> [[Block]] - -> State WriterState Html + -> StateT WriterState m Html tableRowToHtml opts aligns rownum cols' = do let mkcell = if rownum == 0 then H.th else H.td let rowclass = case rownum of @@ -649,11 +654,12 @@ alignmentToString alignment = case alignment of AlignCenter -> "center" AlignDefault -> "" -tableItemToHtml :: WriterOptions +tableItemToHtml :: PandocMonad m + => WriterOptions -> (Html -> Html) -> Alignment -> [Block] - -> State WriterState Html + -> StateT WriterState m Html tableItemToHtml opts tag' align' item = do contents <- blockListToHtml opts item let alignStr = alignmentToString align' @@ -671,12 +677,12 @@ toListItems opts items = map (toListItem opts) items ++ [nl opts] toListItem :: WriterOptions -> Html -> Html toListItem opts item = nl opts >> H.li item -blockListToHtml :: WriterOptions -> [Block] -> State WriterState Html +blockListToHtml :: PandocMonad m => WriterOptions -> [Block] -> StateT WriterState m Html blockListToHtml opts lst = fmap (mconcat . intersperse (nl opts)) $ mapM (blockToHtml opts) lst -- | Convert list of Pandoc inline elements to HTML. -inlineListToHtml :: WriterOptions -> [Inline] -> State WriterState Html +inlineListToHtml :: PandocMonad m => WriterOptions -> [Inline] -> StateT WriterState m Html inlineListToHtml opts lst = mapM (inlineToHtml opts) lst >>= return . mconcat @@ -695,7 +701,7 @@ annotateMML e tex = math (unode "semantics" [cs, unode "annotation" (annotAttrs, -- | Convert Pandoc inline element to HTML. -inlineToHtml :: WriterOptions -> Inline -> State WriterState Html +inlineToHtml :: PandocMonad m => WriterOptions -> Inline -> StateT WriterState m Html inlineToHtml opts inline = case inline of (Str str) -> return $ strToHtml str @@ -818,7 +824,7 @@ inlineToHtml opts inline = | otherwise -> return mempty (Link attr txt (s,_)) | "mailto:" `isPrefixOf` s -> do linkText <- inlineListToHtml opts txt - return $ obfuscateLink opts attr linkText s + lift $ obfuscateLink opts attr linkText s (Link attr txt (s,tit)) -> do linkText <- inlineListToHtml opts txt let s' = case s of @@ -878,7 +884,7 @@ inlineToHtml opts inline = then result ! customAttribute "data-cites" (toValue citationIds) else result -blockListToNote :: WriterOptions -> String -> [Block] -> State WriterState Html +blockListToNote :: PandocMonad m => WriterOptions -> String -> [Block] -> StateT WriterState m Html blockListToNote opts ref blocks = -- If last block is Para or Plain, include the backlink at the end of -- that block. Otherwise, insert a new Plain block with the backlink. diff --git a/src/Text/Pandoc/Writers/Man.hs b/src/Text/Pandoc/Writers/Man.hs index 75c026463..c9530e4e1 100644 --- a/src/Text/Pandoc/Writers/Man.hs +++ b/src/Text/Pandoc/Writers/Man.hs @@ -41,7 +41,8 @@ import Data.Maybe (fromMaybe) import Text.Pandoc.Pretty import Text.Pandoc.Builder (deleteMeta) import Control.Monad.State -import Text.Pandoc.Class (PandocMonad) +import Control.Monad.Except (throwError) +import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) type Notes = [[Block]] data WriterState = WriterState { stNotes :: Notes @@ -49,10 +50,10 @@ data WriterState = WriterState { stNotes :: Notes -- | Convert Pandoc to Man. writeMan :: PandocMonad m => WriterOptions -> Pandoc -> m String -writeMan opts document = return $ evalState (pandocToMan opts document) (WriterState [] False) +writeMan opts document = evalStateT (pandocToMan opts document) (WriterState [] False) -- | Return groff man representation of document. -pandocToMan :: WriterOptions -> Pandoc -> State WriterState String +pandocToMan :: PandocMonad m => WriterOptions -> Pandoc -> StateT WriterState m String pandocToMan opts (Pandoc meta blocks) = do let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts @@ -94,7 +95,7 @@ pandocToMan opts (Pandoc meta blocks) = do Just tpl -> return $ renderTemplate' tpl context -- | Return man representation of notes. -notesToMan :: WriterOptions -> [[Block]] -> State WriterState Doc +notesToMan :: PandocMonad m => WriterOptions -> [[Block]] -> StateT WriterState m Doc notesToMan opts notes = if null notes then return empty @@ -102,7 +103,7 @@ notesToMan opts notes = return . (text ".SH NOTES" $$) . vcat -- | Return man representation of a note. -noteToMan :: WriterOptions -> Int -> [Block] -> State WriterState Doc +noteToMan :: PandocMonad m => WriterOptions -> Int -> [Block] -> StateT WriterState m Doc noteToMan opts num note = do contents <- blockListToMan opts note let marker = cr <> text ".SS " <> brackets (text (show num)) @@ -161,9 +162,10 @@ splitSentences xs = in if null rest then [sent] else sent : splitSentences rest -- | Convert Pandoc block element to man. -blockToMan :: WriterOptions -- ^ Options - -> Block -- ^ Block element - -> State WriterState Doc +blockToMan :: PandocMonad m + => WriterOptions -- ^ Options + -> Block -- ^ Block element + -> StateT WriterState m Doc blockToMan _ Null = return empty blockToMan opts (Div _ bs) = blockListToMan opts bs blockToMan opts (Plain inlines) = @@ -237,7 +239,7 @@ blockToMan opts (DefinitionList items) = do return (vcat contents) -- | Convert bullet list item (list of blocks) to man. -bulletListItemToMan :: WriterOptions -> [Block] -> State WriterState Doc +bulletListItemToMan :: PandocMonad m => WriterOptions -> [Block] -> StateT WriterState m Doc bulletListItemToMan _ [] = return empty bulletListItemToMan opts ((Para first):rest) = bulletListItemToMan opts ((Plain first):rest) @@ -255,11 +257,12 @@ bulletListItemToMan opts (first:rest) = do return $ text "\\[bu] .RS 2" $$ first' $$ rest' $$ text ".RE" -- | Convert ordered list item (a list of blocks) to man. -orderedListItemToMan :: WriterOptions -- ^ options - -> String -- ^ order marker for list item - -> Int -- ^ number of spaces to indent - -> [Block] -- ^ list item (list of blocks) - -> State WriterState Doc +orderedListItemToMan :: PandocMonad m + => WriterOptions -- ^ options + -> String -- ^ order marker for list item + -> Int -- ^ number of spaces to indent + -> [Block] -- ^ list item (list of blocks) + -> StateT WriterState m Doc orderedListItemToMan _ _ _ [] = return empty orderedListItemToMan opts num indent ((Para first):rest) = orderedListItemToMan opts num indent ((Plain first):rest) @@ -274,18 +277,19 @@ orderedListItemToMan opts num indent (first:rest) = do return $ first'' $$ rest'' -- | Convert definition list item (label, list of blocks) to man. -definitionListItemToMan :: WriterOptions - -> ([Inline],[[Block]]) - -> State WriterState Doc +definitionListItemToMan :: PandocMonad m + => WriterOptions + -> ([Inline],[[Block]]) + -> StateT WriterState m Doc definitionListItemToMan opts (label, defs) = do labelText <- inlineListToMan opts label contents <- if null defs then return empty else liftM vcat $ forM defs $ \blocks -> do - let (first, rest) = case blocks of - ((Para x):y) -> (Plain x,y) - (x:y) -> (x,y) - [] -> error "blocks is null" + (first, rest) <- case blocks of + ((Para x):y) -> return (Plain x,y) + (x:y) -> return (x,y) + [] -> throwError $ PandocSomeError "blocks is null" rest' <- liftM vcat $ mapM (\item -> blockToMan opts item) rest first' <- blockToMan opts first @@ -293,18 +297,19 @@ definitionListItemToMan opts (label, defs) = do return $ text ".TP" $$ nowrap (text ".B " <> labelText) $$ contents -- | Convert list of Pandoc block elements to man. -blockListToMan :: WriterOptions -- ^ Options - -> [Block] -- ^ List of block elements - -> State WriterState Doc +blockListToMan :: PandocMonad m + => WriterOptions -- ^ Options + -> [Block] -- ^ List of block elements + -> StateT WriterState m Doc blockListToMan opts blocks = mapM (blockToMan opts) blocks >>= (return . vcat) -- | Convert list of Pandoc inline elements to man. -inlineListToMan :: WriterOptions -> [Inline] -> State WriterState Doc +inlineListToMan :: PandocMonad m => WriterOptions -> [Inline] -> StateT WriterState m Doc inlineListToMan opts lst = mapM (inlineToMan opts) lst >>= (return . hcat) -- | Convert Pandoc inline element to man. -inlineToMan :: WriterOptions -> Inline -> State WriterState Doc +inlineToMan :: PandocMonad m => WriterOptions -> Inline -> StateT WriterState m Doc inlineToMan opts (Span _ ils) = inlineListToMan opts ils inlineToMan opts (Emph lst) = do contents <- inlineListToMan opts lst diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 787db10f9..4c33de65d 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -46,6 +46,7 @@ import Data.Ord ( comparing ) import Text.Pandoc.Pretty import Control.Monad.Reader import Control.Monad.State +import Control.Monad.Except (throwError) import Text.Pandoc.Writers.HTML (writeHtmlString) import Text.Pandoc.Readers.TeXMath (texMathToInlines) import Text.HTML.TagSoup (parseTags, isTagText, Tag(..)) @@ -57,7 +58,7 @@ 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) +import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) type Notes = [[Block]] type Ref = ([Inline], Target, Attr) @@ -800,14 +801,14 @@ getReference attr label target = do case find (\(_,t,a) -> t == target && a == attr) (stRefs st) of Just (ref, _, _) -> return ref Nothing -> do - let label' = case find (\(l,_,_) -> l == label) (stRefs st) of - Just _ -> -- label is used; generate numerical label - case find (\n -> notElem [Str (show n)] - (map (\(l,_,_) -> l) (stRefs st))) - [1..(10000 :: Integer)] of - Just x -> [Str (show x)] - Nothing -> error "no unique label" - Nothing -> label + label' <- case find (\(l,_,_) -> l == label) (stRefs st) of + Just _ -> -- label is used; generate numerical label + case find (\n -> notElem [Str (show n)] + (map (\(l,_,_) -> l) (stRefs st))) + [1..(10000 :: Integer)] of + Just x -> return [Str (show x)] + Nothing -> throwError $ PandocSomeError "no unique label" + Nothing -> return label modify (\s -> s{ stRefs = (label', target, attr) : stRefs st }) return label' diff --git a/src/Text/Pandoc/Writers/OPML.hs b/src/Text/Pandoc/Writers/OPML.hs index ce415264d..4f832f962 100644 --- a/src/Text/Pandoc/Writers/OPML.hs +++ b/src/Text/Pandoc/Writers/OPML.hs @@ -40,7 +40,8 @@ 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) +import Control.Monad.Except (throwError) +import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) -- | Convert Pandoc document to string in OPML format. writeOPML :: PandocMonad m => WriterOptions -> Pandoc -> m String @@ -82,15 +83,20 @@ convertDate ils = maybe "" showDateTimeRFC822 $ elementToOPML :: PandocMonad m => WriterOptions -> Element -> m Doc elementToOPML _ (Blk _) = return empty elementToOPML opts (Sec _ _num _ title elements) = do - let isBlk (Blk _) = True + let isBlk :: Element -> Bool + isBlk (Blk _) = True isBlk _ = False - fromBlk (Blk x) = x - fromBlk _ = error "fromBlk called on non-block" + + fromBlk :: PandocMonad m => Element -> m Block + fromBlk (Blk x) = return x + fromBlk _ = throwError $ PandocSomeError "fromBlk called on non-block" + (blocks, rest) = span isBlk elements htmlIls <- writeHtmlInlines title md <- if null blocks then return [] - else writeMarkdown def $ Pandoc nullMeta $ map fromBlk blocks + else do blks <- mapM fromBlk blocks + writeMarkdown def $ Pandoc nullMeta blks 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/RTF.hs b/src/Text/Pandoc/Writers/RTF.hs index 75b97a648..1ac906756 100644 --- a/src/Text/Pandoc/Writers/RTF.hs +++ b/src/Text/Pandoc/Writers/RTF.hs @@ -43,7 +43,8 @@ import qualified Data.ByteString as B import qualified Data.Map as M import Text.Printf ( printf ) import Text.Pandoc.ImageSize -import Text.Pandoc.Class (PandocMonad) +import Control.Monad.Except (throwError) +import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) import qualified Text.Pandoc.Class as P -- | Convert Image inlines into a raw RTF embedded image, read from a file, @@ -56,10 +57,10 @@ rtfEmbedImage opts x@(Image attr _ (src,_)) = do Right (imgdata, Just mime) | mime == "image/jpeg" || mime == "image/png" -> do let bytes = map (printf "%02x") $ B.unpack imgdata - let filetype = case mime of - "image/jpeg" -> "\\jpegblip" - "image/png" -> "\\pngblip" - _ -> error "Unknown file type" + filetype <- case mime of + "image/jpeg" -> return "\\jpegblip" + "image/png" -> return "\\pngblip" + _ -> throwError $ PandocSomeError "Unknown file type" sizeSpec <- case imageSize imgdata of Left msg -> do P.warn $ "Could not determine image size in `" ++ diff --git a/src/Text/Pandoc/Writers/Texinfo.hs b/src/Text/Pandoc/Writers/Texinfo.hs index fac7f02ab..dd5d5ee5d 100644 --- a/src/Text/Pandoc/Writers/Texinfo.hs +++ b/src/Text/Pandoc/Writers/Texinfo.hs @@ -44,7 +44,8 @@ import Text.Pandoc.ImageSize import Network.URI ( isURI, unEscapeString ) import System.FilePath import qualified Data.Set as Set -import Text.Pandoc.Class ( PandocMonad ) +import Control.Monad.Except (throwError) +import Text.Pandoc.Class ( PandocMonad, PandocExecutionError(..) ) data WriterState = WriterState { stStrikeout :: Bool -- document contains strikeout @@ -60,10 +61,12 @@ data WriterState = - generated .texi files don't work when run through texi2dvi -} +type TI m = StateT WriterState m + -- | Convert Pandoc to Texinfo. writeTexinfo :: PandocMonad m => WriterOptions -> Pandoc -> m String -writeTexinfo options document = return $ - evalState (pandocToTexinfo options $ wrapTop document) $ +writeTexinfo options document = + evalStateT (pandocToTexinfo options $ wrapTop document) $ WriterState { stStrikeout = False, stSuperscript = False, stEscapeComma = False, stSubscript = False, stIdentifiers = Set.empty, stOptions = options} @@ -73,7 +76,7 @@ wrapTop :: Pandoc -> Pandoc wrapTop (Pandoc meta blocks) = Pandoc meta (Header 0 nullAttr (docTitle meta) : blocks) -pandocToTexinfo :: WriterOptions -> Pandoc -> State WriterState String +pandocToTexinfo :: PandocMonad m => WriterOptions -> Pandoc -> TI m String pandocToTexinfo options (Pandoc meta blocks) = do let titlePage = not $ all null $ docTitle meta : docDate meta : docAuthors meta @@ -111,7 +114,7 @@ stringToTexinfo = escapeStringUsing texinfoEscapes , ('\x2019', "'") ] -escapeCommas :: State WriterState Doc -> State WriterState Doc +escapeCommas :: PandocMonad m => TI m Doc -> TI m Doc escapeCommas parser = do oldEscapeComma <- gets stEscapeComma modify $ \st -> st{ stEscapeComma = True } @@ -124,8 +127,9 @@ inCmd :: String -> Doc -> Doc inCmd cmd contents = char '@' <> text cmd <> braces contents -- | Convert Pandoc block element to Texinfo. -blockToTexinfo :: Block -- ^ Block to convert - -> State WriterState Doc +blockToTexinfo :: PandocMonad m + => Block -- ^ Block to convert + -> TI m Doc blockToTexinfo Null = return empty @@ -221,17 +225,19 @@ blockToTexinfo (Header level _ lst) = do idsUsed <- gets stIdentifiers let id' = uniqueIdent lst idsUsed modify $ \st -> st{ stIdentifiers = Set.insert id' idsUsed } + sec <- seccmd level return $ if (level > 0) && (level <= 4) then blankline <> text "@node " <> node $$ - text (seccmd level) <> txt $$ + text sec <> txt $$ text "@anchor" <> braces (text $ '#':id') else txt where - seccmd 1 = "@chapter " - seccmd 2 = "@section " - seccmd 3 = "@subsection " - seccmd 4 = "@subsubsection " - seccmd _ = error "illegal seccmd level" + seccmd :: PandocMonad m => Int -> TI m String + seccmd 1 = return "@chapter " + seccmd 2 = return "@section " + seccmd 3 = return "@subsection " + seccmd 4 = return "@subsubsection " + seccmd _ = throwError $ PandocSomeError "illegal seccmd level" blockToTexinfo (Table caption aligns widths heads rows) = do headers <- if all null heads @@ -257,28 +263,32 @@ blockToTexinfo (Table caption aligns widths heads rows) = do inCmd "caption" captionText $$ text "@end float" -tableHeadToTexinfo :: [Alignment] +tableHeadToTexinfo :: PandocMonad m + => [Alignment] -> [[Block]] - -> State WriterState Doc + -> TI m Doc tableHeadToTexinfo = tableAnyRowToTexinfo "@headitem " -tableRowToTexinfo :: [Alignment] +tableRowToTexinfo :: PandocMonad m + => [Alignment] -> [[Block]] - -> State WriterState Doc + -> TI m Doc tableRowToTexinfo = tableAnyRowToTexinfo "@item " -tableAnyRowToTexinfo :: String +tableAnyRowToTexinfo :: PandocMonad m + => String -> [Alignment] -> [[Block]] - -> State WriterState Doc + -> TI m Doc tableAnyRowToTexinfo itemtype aligns cols = zipWithM alignedBlock aligns cols >>= return . (text itemtype $$) . foldl (\row item -> row $$ (if isEmpty row then empty else text " @tab ") <> item) empty -alignedBlock :: Alignment +alignedBlock :: PandocMonad m + => Alignment -> [Block] - -> State WriterState Doc + -> TI m Doc -- XXX @flushleft and @flushright text won't get word wrapped. Since word -- wrapping is more important than alignment, we ignore the alignment. alignedBlock _ = blockListToTexinfo @@ -293,8 +303,9 @@ alignedBlock _ col = blockListToTexinfo col -} -- | Convert Pandoc block elements to Texinfo. -blockListToTexinfo :: [Block] - -> State WriterState Doc +blockListToTexinfo :: PandocMonad m + => [Block] + -> TI m Doc blockListToTexinfo [] = return empty blockListToTexinfo (x:xs) = do x' <- blockToTexinfo x @@ -336,15 +347,17 @@ collectNodes level (x:xs) = _ -> collectNodes level xs -makeMenuLine :: Block - -> State WriterState Doc +makeMenuLine :: PandocMonad m + => Block + -> TI m Doc makeMenuLine (Header _ _ lst) = do txt <- inlineListForNode lst return $ text "* " <> txt <> text "::" -makeMenuLine _ = error "makeMenuLine called with non-Header block" +makeMenuLine _ = throwError $ PandocSomeError "makeMenuLine called with non-Header block" -listItemToTexinfo :: [Block] - -> State WriterState Doc +listItemToTexinfo :: PandocMonad m + => [Block] + -> TI m Doc listItemToTexinfo lst = do contents <- blockListToTexinfo lst let spacer = case reverse lst of @@ -352,8 +365,9 @@ listItemToTexinfo lst = do _ -> empty return $ text "@item" $$ contents <> spacer -defListItemToTexinfo :: ([Inline], [[Block]]) - -> State WriterState Doc +defListItemToTexinfo :: PandocMonad m + => ([Inline], [[Block]]) + -> TI m Doc defListItemToTexinfo (term, defs) = do term' <- inlineListToTexinfo term let defToTexinfo bs = do d <- blockListToTexinfo bs @@ -364,13 +378,15 @@ defListItemToTexinfo (term, defs) = do return $ text "@item " <> term' $+$ vcat defs' -- | Convert list of inline elements to Texinfo. -inlineListToTexinfo :: [Inline] -- ^ Inlines to convert - -> State WriterState Doc +inlineListToTexinfo :: PandocMonad m + => [Inline] -- ^ Inlines to convert + -> TI m Doc inlineListToTexinfo lst = mapM inlineToTexinfo lst >>= return . hcat -- | Convert list of inline elements to Texinfo acceptable for a node name. -inlineListForNode :: [Inline] -- ^ Inlines to convert - -> State WriterState Doc +inlineListForNode :: PandocMonad m + => [Inline] -- ^ Inlines to convert + -> TI m Doc inlineListForNode = return . text . stringToTexinfo . filter (not . disallowedInNode) . stringify @@ -379,8 +395,9 @@ disallowedInNode :: Char -> Bool disallowedInNode c = c `elem` (".,:()" :: String) -- | Convert inline element to Texinfo -inlineToTexinfo :: Inline -- ^ Inline to convert - -> State WriterState Doc +inlineToTexinfo :: PandocMonad m + => Inline -- ^ Inline to convert + -> TI m Doc inlineToTexinfo (Span _ lst) = inlineListToTexinfo lst -- cgit v1.2.3 From 04545c92c8588f2487518fc45cffcf2df8935e7b Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Sun, 27 Nov 2016 00:05:53 -0500 Subject: Clean up Text.Pandoc We had primed versions of all the Writer types and getWriter functions, as we transitioned. Now that we're using the new ones exclusively, we'll get rid of the old ones, and get rid of the primes in the names. --- pandoc.hs | 10 ++--- src/Text/Pandoc.hs | 126 ++++++++++++++++++++++------------------------------- 2 files changed, 58 insertions(+), 78 deletions(-) (limited to 'src/Text') diff --git a/pandoc.hs b/pandoc.hs index 662dd3e3b..baea94e6c 100644 --- a/pandoc.hs +++ b/pandoc.hs @@ -915,7 +915,7 @@ options = let allopts = unwords (concatMap optnames options) UTF8.hPutStrLn stdout $ printf tpl allopts (unwords (map fst readers)) - (unwords (map fst (writers' :: [(String, Writer' PandocIO)]))) + (unwords (map fst (writers :: [(String, Writer PandocIO)]))) (unwords $ map fst highlightingStyles) ddir exitSuccess )) @@ -932,7 +932,7 @@ options = , Option "" ["list-output-formats"] (NoArg (\_ -> do - let writers'names = sort (map fst (writers' :: [(String, Writer' PandocIO)])) + let writers'names = sort (map fst (writers :: [(String, Writer PandocIO)])) mapM_ (UTF8.hPutStrLn stdout) writers'names exitSuccess )) "" @@ -1274,7 +1274,7 @@ convertWithOpts opts args = do writer <- if ".lua" `isSuffixOf` format -- note: use non-lowercased version writerName then error "custom writers disabled for now" - else case getWriter' writerName' of + else case getWriter writerName' of Left e -> err 9 $ if format == "pdf" then e ++ @@ -1481,8 +1481,8 @@ convertWithOpts opts args = do case writer of -- StringWriter f -> f writerOptions doc' >>= writerFn outputFile - ByteStringWriter' f -> (runIOorExplode $ f writerOptions doc') >>= writeFnBinary outputFile - StringWriter' f + 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) $ diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index 5bb015fc2..f912bc46d 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -64,7 +64,7 @@ module Text.Pandoc -- * Lists of readers and writers , readers -- , writers - , writers' + , writers -- * Readers: converting /to/ Pandoc format , Reader (..) , mkStringReader @@ -88,8 +88,7 @@ module Text.Pandoc , readTxt2TagsNoMacros , readEPUB -- * Writers: converting /from/ Pandoc format - -- , Writer (..) - , Writer'(..) + , Writer(..) , writeNative , writeJSON , writeMarkdown @@ -124,8 +123,7 @@ module Text.Pandoc , module Text.Pandoc.Templates -- * Miscellaneous , getReader - -- , getWriter - , getWriter' + , getWriter , getDefaultExtensions , ToJsonFilter(..) , pandocVersion @@ -335,67 +333,67 @@ readers = [ ("native" , StringReader $ \_ s -> return $ readNative s) -- ,("tei" , PureStringWriter writeTEI) -- ] -data Writer' m = StringWriter' (WriterOptions -> Pandoc -> m String) - | ByteStringWriter' (WriterOptions -> Pandoc -> m BL.ByteString) +data Writer m = StringWriter (WriterOptions -> Pandoc -> m String) + | ByteStringWriter (WriterOptions -> Pandoc -> m BL.ByteString) -- | Association list of formats and writers. -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 -> +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 -> + ,("epub3" , ByteStringWriter $ \o -> writeEPUB o{ writerEpubVersion = Just EPUB3 }) - ,("fb2" , StringWriter' writeFB2) - ,("html" , StringWriter' writeHtmlString) - ,("html5" , StringWriter' $ \o -> + ,("fb2" , StringWriter writeFB2) + ,("html" , StringWriter writeHtmlString) + ,("html5" , StringWriter $ \o -> writeHtmlString o{ writerHtml5 = True }) - ,("icml" , StringWriter' writeICML) - ,("s5" , StringWriter' $ \o -> + ,("icml" , StringWriter writeICML) + ,("s5" , StringWriter $ \o -> writeHtmlString o{ writerSlideVariant = S5Slides , writerTableOfContents = False }) - ,("slidy" , StringWriter' $ \o -> + ,("slidy" , StringWriter $ \o -> writeHtmlString o{ writerSlideVariant = SlidySlides }) - ,("slideous" , StringWriter' $ \o -> + ,("slideous" , StringWriter $ \o -> writeHtmlString o{ writerSlideVariant = SlideousSlides }) - ,("dzslides" , StringWriter' $ \o -> + ,("dzslides" , StringWriter $ \o -> writeHtmlString o{ writerSlideVariant = DZSlides , writerHtml5 = True }) - ,("revealjs" , StringWriter' $ \o -> + ,("revealjs" , StringWriter $ \o -> writeHtmlString o{ writerSlideVariant = RevealJsSlides , writerHtml5 = True }) - ,("docbook" , StringWriter' writeDocbook) - ,("docbook5" , StringWriter' $ \o -> + ,("docbook" , StringWriter writeDocbook) + ,("docbook5" , StringWriter $ \o -> writeDocbook o{ writerDocbook5 = True }) - ,("opml" , StringWriter' writeOPML) - ,("opendocument" , StringWriter' writeOpenDocument) - ,("latex" , StringWriter' writeLaTeX) - ,("beamer" , StringWriter' $ \o -> + ,("opml" , StringWriter writeOPML) + ,("opendocument" , StringWriter writeOpenDocument) + ,("latex" , StringWriter writeLaTeX) + ,("beamer" , StringWriter $ \o -> writeLaTeX o{ writerBeamer = True }) - ,("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 -> + ,("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) + ,("org" , StringWriter writeOrg) + ,("asciidoc" , StringWriter writeAsciiDoc) + ,("haddock" , StringWriter writeHaddock) + ,("commonmark" , StringWriter writeCommonMark) + ,("tei" , StringWriter writeTEI) ] getDefaultExtensions :: String -> Set Extension @@ -433,35 +431,17 @@ getReader s = r o{ readerExtensions = setExts $ getDefaultExtensions readerName } --- | Retrieve writer based on formatSpec (format+extensions). --- 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 +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 (StringWriter' r) -> Right $ StringWriter' $ + Just (StringWriter r) -> Right $ StringWriter $ \o -> r o{ writerExtensions = setExts $ getDefaultExtensions writerName } - Just (ByteStringWriter' r) -> Right $ ByteStringWriter' $ + Just (ByteStringWriter r) -> Right $ ByteStringWriter $ \o -> r o{ writerExtensions = setExts $ getDefaultExtensions writerName } -- cgit v1.2.3 From 33af62acc5f2219cb093b83694cd73dec33f210b Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 27 Nov 2016 11:52:32 +0100 Subject: Fixes to compile after rebase. --- src/Text/Pandoc/Writers/Docx.hs | 3 ++- src/Text/Pandoc/Writers/ICML.hs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 36816eaa1..cc0c180f2 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -1121,7 +1121,8 @@ inlineToOpenXML' opts (Math mathType str) = do case writeOMML displayType <$> readTeX str of Right r -> return [r] Left e -> do - warn $ "Cannot convert the following TeX math, skipping:\n" ++ str ++ + (lift . lift) $ P.warn $ + "Cannot convert the following TeX math, skipping:\n" ++ str ++ "\n" ++ e inlinesToOpenXML opts (texMathToInlines mathType str) inlineToOpenXML' opts (Cite _ lst) = inlinesToOpenXML opts lst diff --git a/src/Text/Pandoc/Writers/ICML.hs b/src/Text/Pandoc/Writers/ICML.hs index c82a77452..f624b7dec 100644 --- a/src/Text/Pandoc/Writers/ICML.hs +++ b/src/Text/Pandoc/Writers/ICML.hs @@ -18,7 +18,7 @@ import Text.Pandoc.Definition import Text.Pandoc.XML import Text.Pandoc.Readers.TeXMath (texMathToInlines) import Text.Pandoc.Writers.Shared -import Text.Pandoc.Shared (linesToPara, splitBy, warn) +import Text.Pandoc.Shared (linesToPara, splitBy) import Text.Pandoc.Options import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Pretty @@ -542,7 +542,7 @@ imageICML opts style attr (src, _) = do case imageSize img of Right size -> return size Left msg -> do - warn $ "Could not determine image size in `" ++ + lift $ P.warn $ "Could not determine image size in `" ++ src ++ "': " ++ msg return def let (ow, oh) = sizeInPoints imgS -- cgit v1.2.3 From b969863e0787ae560d55ca14238a94ac28d302cf Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 27 Nov 2016 11:51:40 +0100 Subject: Export Text.Pandoc.Class from Text.Pandoc. --- src/Text/Pandoc.hs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index f912bc46d..0a6b67f02 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -59,6 +59,8 @@ module Text.Pandoc , module Text.Pandoc.Generic -- * Options , module Text.Pandoc.Options + -- * Typeclass + , module Text.Pandoc.Class -- * Error handling , module Text.Pandoc.Error -- * Lists of readers and writers -- cgit v1.2.3 From 22ffbad9e8e99a59f24997d09d04b28c87d5ecba Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 27 Nov 2016 15:33:44 +0100 Subject: Texinfo writer: restore former behavior for headers level > 4. The recent changes made the writer fail with an error if it encountered a header with level 5. Better to do as we did before and just print a paragraph in that case. Eventually we should emit a warning here. --- src/Text/Pandoc/Writers/Texinfo.hs | 40 ++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 19 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Texinfo.hs b/src/Text/Pandoc/Writers/Texinfo.hs index dd5d5ee5d..9d5c80534 100644 --- a/src/Text/Pandoc/Writers/Texinfo.hs +++ b/src/Text/Pandoc/Writers/Texinfo.hs @@ -219,25 +219,27 @@ blockToTexinfo (Header 0 _ lst) = do return $ text "@node Top" $$ text "@top " <> txt <> blankline -blockToTexinfo (Header level _ lst) = do - node <- inlineListForNode lst - txt <- inlineListToTexinfo lst - idsUsed <- gets stIdentifiers - let id' = uniqueIdent lst idsUsed - modify $ \st -> st{ stIdentifiers = Set.insert id' idsUsed } - sec <- seccmd level - return $ if (level > 0) && (level <= 4) - then blankline <> text "@node " <> node $$ - text sec <> txt $$ - text "@anchor" <> braces (text $ '#':id') - else txt - where - seccmd :: PandocMonad m => Int -> TI m String - seccmd 1 = return "@chapter " - seccmd 2 = return "@section " - seccmd 3 = return "@subsection " - seccmd 4 = return "@subsubsection " - seccmd _ = throwError $ PandocSomeError "illegal seccmd level" +blockToTexinfo (Header level _ lst) + | level < 1 || level > 4 = blockToTexinfo (Para lst) + | otherwise = do + node <- inlineListForNode lst + txt <- inlineListToTexinfo lst + idsUsed <- gets stIdentifiers + let id' = uniqueIdent lst idsUsed + modify $ \st -> st{ stIdentifiers = Set.insert id' idsUsed } + sec <- seccmd level + return $ if (level > 0) && (level <= 4) + then blankline <> text "@node " <> node $$ + text sec <> txt $$ + text "@anchor" <> braces (text $ '#':id') + else txt + where + seccmd :: PandocMonad m => Int -> TI m String + seccmd 1 = return "@chapter " + seccmd 2 = return "@section " + seccmd 3 = return "@subsection " + seccmd 4 = return "@subsubsection " + seccmd _ = throwError $ PandocSomeError "illegal seccmd level" blockToTexinfo (Table caption aligns widths heads rows) = do headers <- if all null heads -- cgit v1.2.3 From 0e4f95998140c70b8eb77f636f81f10de0db4788 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 27 Nov 2016 15:41:55 +0100 Subject: Fixed regression in OPML writer. OPML writer should note include `_notes` attribute when there's no content. --- src/Text/Pandoc/Writers/OPML.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/OPML.hs b/src/Text/Pandoc/Writers/OPML.hs index 4f832f962..dee3a029c 100644 --- a/src/Text/Pandoc/Writers/OPML.hs +++ b/src/Text/Pandoc/Writers/OPML.hs @@ -97,6 +97,6 @@ elementToOPML opts (Sec _ _num _ title elements) = do then return [] else do blks <- mapM fromBlk blocks writeMarkdown def $ Pandoc nullMeta blks - let attrs = [("text", htmlIls)] ++ [("_note", md)] + let attrs = [("text", htmlIls)] ++ [("_note", md) | not (null blocks)] o <- mapM (elementToOPML opts) rest return $ inTags True "outline" attrs $ vcat o -- cgit v1.2.3 From 300d94ac249e7e70fb92fb21f6426d894fce61ce Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 27 Nov 2016 15:45:08 +0100 Subject: Deleted whitespace at end of source lines. --- pandoc.hs | 2 +- src/Text/Pandoc.hs | 2 +- src/Text/Pandoc/Class.hs | 2 +- src/Text/Pandoc/Readers/Docx.hs | 4 ++-- src/Text/Pandoc/Readers/Docx/Parse.hs | 2 +- src/Text/Pandoc/UUID.hs | 2 +- src/Text/Pandoc/Writers/CommonMark.hs | 2 +- src/Text/Pandoc/Writers/EPUB.hs | 4 ++-- src/Text/Pandoc/Writers/Texinfo.hs | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) (limited to 'src/Text') diff --git a/pandoc.hs b/pandoc.hs index baea94e6c..258b0c735 100644 --- a/pandoc.hs +++ b/pandoc.hs @@ -1269,7 +1269,7 @@ 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 diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index 0a6b67f02..b5e5bebcd 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -282,7 +282,7 @@ readers = [ ("native" , StringReader $ \_ s -> return $ readNative s) -- runIOorExplode $ -- writeEPUB o{ writerEpubVersion = Just EPUB2 } doc) -- ,("epub3" , IOByteStringWriter $ \o doc -> --- runIOorExplode $ +-- runIOorExplode $ -- writeEPUB o{ writerEpubVersion = Just EPUB3 } doc) -- ,("fb2" , IOStringWriter $ \o doc -> -- runIOorExplode $ writeFB2 o doc) diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 69d2bb761..279770e97 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -135,7 +135,7 @@ newtype PandocIO a = PandocIO { unPandocIO :: ExceptT PandocExecutionError (StateT PandocStateIO IO) a } deriving (MonadIO, Functor, Applicative, Monad, MonadError PandocExecutionError) -instance PandocMonad PandocIO where +instance PandocMonad PandocIO where lookupEnv = liftIO . IO.lookupEnv getCurrentTime = liftIO IO.getCurrentTime getDefaultReferenceDocx = liftIO . IO.getDefaultReferenceDocx diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index 595c805bf..a43043d84 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -64,7 +64,7 @@ implemented, [-] means partially implemented): - [X] Math - [X] Link (links to an arbitrary bookmark create a span with the target as id and "anchor" class) - - [X] Image + - [X] Image - [X] Note (Footnotes and Endnotes are silently combined.) -} @@ -559,7 +559,7 @@ bodyPartToBlocks (ListItem pPr numId lvl (Just levelInfo) parparts) = do ] blks <- bodyPartToBlocks (Paragraph pPr parparts) return $ divWith ("", ["list-item"], kvs) blks -bodyPartToBlocks (ListItem pPr _ _ _ parparts) = +bodyPartToBlocks (ListItem pPr _ _ _ parparts) = let pPr' = pPr {pStyle = "ListParagraph": (pStyle pPr)} in bodyPartToBlocks $ Paragraph pPr' parparts diff --git a/src/Text/Pandoc/Readers/Docx/Parse.hs b/src/Text/Pandoc/Readers/Docx/Parse.hs index deb2caccf..6cd3a49b6 100644 --- a/src/Text/Pandoc/Readers/Docx/Parse.hs +++ b/src/Text/Pandoc/Readers/Docx/Parse.hs @@ -690,7 +690,7 @@ elemToParPart ns element , Just drawingElem <- findChild (elemName ns "w" "drawing") element , c_ns <- "http://schemas.openxmlformats.org/drawingml/2006/chart" , Just _ <- findElement (QName "chart" (Just c_ns) (Just "c")) drawingElem - = return Chart + = return Chart elemToParPart ns element | isElem ns "w" "r" element = elemToRun ns element >>= (\r -> return $ PlainRun r) diff --git a/src/Text/Pandoc/UUID.hs b/src/Text/Pandoc/UUID.hs index 9d8cd4434..8de102742 100644 --- a/src/Text/Pandoc/UUID.hs +++ b/src/Text/Pandoc/UUID.hs @@ -64,7 +64,7 @@ instance Show UUID where printf "%02x" p getUUID :: RandomGen g => g -> UUID -getUUID gen = +getUUID gen = let [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p] = take 16 $ randoms gen :: [Word8] -- set variant i' = i `setBit` 7 `clearBit` 6 diff --git a/src/Text/Pandoc/Writers/CommonMark.hs b/src/Text/Pandoc/Writers/CommonMark.hs index b6ff35bbe..c1963a9a8 100644 --- a/src/Text/Pandoc/Writers/CommonMark.hs +++ b/src/Text/Pandoc/Writers/CommonMark.hs @@ -51,7 +51,7 @@ writeCommonMark opts (Pandoc meta blocks) = do notes' = if null notes then [] else [OrderedList (1, Decimal, Period) $ reverse notes] - main <- blocksToCommonMark opts (blocks' ++ notes') + main <- blocksToCommonMark opts (blocks' ++ notes') metadata <- metaToJSON opts (blocksToCommonMark opts) (inlinesToCommonMark opts) diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index 580b12210..f0dce739e 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -347,8 +347,8 @@ writeEPUB opts doc = evalStateT (pandocToEPUB opts doc) initState pandocToEPUB :: PandocMonad m - => WriterOptions - -> Pandoc + => WriterOptions + -> Pandoc -> E m B.ByteString pandocToEPUB opts doc@(Pandoc meta _) = do let version = fromMaybe EPUB2 (writerEpubVersion opts) diff --git a/src/Text/Pandoc/Writers/Texinfo.hs b/src/Text/Pandoc/Writers/Texinfo.hs index 9d5c80534..44a1fffd8 100644 --- a/src/Text/Pandoc/Writers/Texinfo.hs +++ b/src/Text/Pandoc/Writers/Texinfo.hs @@ -65,7 +65,7 @@ type TI m = StateT WriterState m -- | Convert Pandoc to Texinfo. writeTexinfo :: PandocMonad m => WriterOptions -> Pandoc -> m String -writeTexinfo options document = +writeTexinfo options document = evalStateT (pandocToTexinfo options $ wrapTop document) $ WriterState { stStrikeout = False, stSuperscript = False, stEscapeComma = False, stSubscript = False, -- cgit v1.2.3 From 8978689c0841d3c7cf47fc0de0ebb50a901f779d Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 27 Nov 2016 15:46:04 +0100 Subject: Removed some commented-out source. --- src/Text/Pandoc.hs | 70 ------------------------------------------------------ 1 file changed, 70 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index b5e5bebcd..106c8b6bb 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -265,76 +265,6 @@ 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) - --- -- | 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) -- cgit v1.2.3 From bf8fb78389c0d2dc06ad91bc379fde5bd7e1f768 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 27 Nov 2016 15:55:02 +0100 Subject: Text.Pandoc: Change Reader to Reader m. For now I just replaced occurences of Reader with Reader IO, so nothing is really different. When we move readers into instances of PandocMonad, though, we can change things here so that the readers will work with any instance of PandocMonad. --- src/Text/Pandoc.hs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index 106c8b6bb..70d1300b3 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -213,14 +213,16 @@ parseFormatSpec = parse formatSpec "" '-' -> Set.delete ext _ -> Set.insert ext +-- TODO: when we get the PandocMonad stuff all sorted out, +-- we can simply these types considerably. Errors/MediaBag can be +-- part of the monad's internal state. +data Reader m = StringReader (ReaderOptions -> String -> m (Either PandocError Pandoc)) + | ByteStringReader (ReaderOptions -> BL.ByteString -> m (Either PandocError (Pandoc,MediaBag))) -data Reader = StringReader (ReaderOptions -> String -> IO (Either PandocError Pandoc)) - | ByteStringReader (ReaderOptions -> BL.ByteString -> IO (Either PandocError (Pandoc,MediaBag))) - -mkStringReader :: (ReaderOptions -> String -> Either PandocError Pandoc) -> Reader +mkStringReader :: (ReaderOptions -> String -> Either PandocError Pandoc) -> Reader IO mkStringReader r = StringReader (\o s -> return $ r o s) -mkStringReaderWithWarnings :: (ReaderOptions -> String -> Either PandocError (Pandoc, [String])) -> Reader +mkStringReaderWithWarnings :: (ReaderOptions -> String -> Either PandocError (Pandoc, [String])) -> Reader IO mkStringReaderWithWarnings r = StringReader $ \o s -> case r o s of Left err -> return $ Left err @@ -228,10 +230,10 @@ mkStringReaderWithWarnings r = StringReader $ \o s -> mapM_ warn warnings return (Right doc) -mkBSReader :: (ReaderOptions -> BL.ByteString -> Either PandocError (Pandoc, MediaBag)) -> Reader +mkBSReader :: (ReaderOptions -> BL.ByteString -> Either PandocError (Pandoc, MediaBag)) -> Reader IO mkBSReader r = ByteStringReader (\o s -> return $ r o s) -mkBSReaderWithWarnings :: (ReaderOptions -> BL.ByteString -> Either PandocError (Pandoc, MediaBag, [String])) -> Reader +mkBSReaderWithWarnings :: (ReaderOptions -> BL.ByteString -> Either PandocError (Pandoc, MediaBag, [String])) -> Reader IO mkBSReaderWithWarnings r = ByteStringReader $ \o s -> case r o s of Left err -> return $ Left err @@ -240,7 +242,7 @@ mkBSReaderWithWarnings r = ByteStringReader $ \o s -> return $ Right (doc, mediaBag) -- | Association list of formats and readers. -readers :: [(String, Reader)] +readers :: [(String, Reader IO)] readers = [ ("native" , StringReader $ \_ s -> return $ readNative s) ,("json" , mkStringReader readJSON ) ,("markdown" , mkStringReaderWithWarnings readMarkdownWithWarnings) @@ -349,7 +351,7 @@ getDefaultExtensions "epub" = Set.fromList [Ext_raw_html, getDefaultExtensions _ = Set.fromList [Ext_auto_identifiers] -- | Retrieve reader based on formatSpec (format+extensions). -getReader :: String -> Either String Reader +getReader :: String -> Either String (Reader IO) getReader s = case parseFormatSpec s of Left e -> Left $ intercalate "\n" [m | Message m <- errorMessages e] -- cgit v1.2.3 From 18e85f8dfbf9323945969cdf831c9a16f90299a0 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 27 Nov 2016 16:38:46 +0100 Subject: Changed readNative to use PandocMonad. --- src/Text/Pandoc.hs | 4 ++-- src/Text/Pandoc/Readers/Native.hs | 9 ++++++--- tests/Tests/Old.hs | 4 +++- tests/Tests/Readers/Docx.hs | 4 +++- tests/Tests/Readers/Odt.hs | 4 +++- tests/Tests/Writers/Docx.hs | 8 +++++--- 6 files changed, 22 insertions(+), 11 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index 70d1300b3..34b6b8d0c 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -183,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 (PandocMonad) +import Text.Pandoc.Class (PandocMonad, runIOorExplode) import Data.Aeson import qualified Data.ByteString.Lazy as BL import Data.List (intercalate) @@ -243,7 +243,7 @@ mkBSReaderWithWarnings r = ByteStringReader $ \o s -> -- | Association list of formats and readers. readers :: [(String, Reader IO)] -readers = [ ("native" , StringReader $ \_ s -> return $ readNative s) +readers = [ ("native" , StringReader $ \_ s -> runIOorExplode (readNative s)) ,("json" , mkStringReader readJSON ) ,("markdown" , mkStringReaderWithWarnings readMarkdownWithWarnings) ,("markdown_strict" , mkStringReaderWithWarnings readMarkdownWithWarnings) diff --git a/src/Text/Pandoc/Readers/Native.hs b/src/Text/Pandoc/Readers/Native.hs index 4ec164e19..917a4a144 100644 --- a/src/Text/Pandoc/Readers/Native.hs +++ b/src/Text/Pandoc/Readers/Native.hs @@ -34,6 +34,7 @@ import Text.Pandoc.Definition import Text.Pandoc.Shared (safeRead) import Text.Pandoc.Error +import Text.Pandoc.Class -- | Read native formatted text and return a Pandoc document. -- The input may be a full pandoc document, a block list, a block, @@ -45,9 +46,11 @@ import Text.Pandoc.Error -- -- > Pandoc nullMeta [Plain [Str "hi"]] -- -readNative :: String -- ^ String to parse (assuming @'\n'@ line endings) - -> Either PandocError Pandoc -readNative s = maybe (Pandoc nullMeta <$> readBlocks s) Right (safeRead s) +readNative :: PandocMonad m + => String -- ^ String to parse (assuming @'\n'@ line endings) + -> m (Either PandocError Pandoc) +readNative s = + return $ maybe (Pandoc nullMeta <$> readBlocks s) Right (safeRead s) readBlocks :: String -> Either PandocError [Block] readBlocks s = maybe ((:[]) <$> readBlock s) Right (safeRead s) diff --git a/tests/Tests/Old.hs b/tests/Tests/Old.hs index bb0e2aac2..b76043887 100644 --- a/tests/Tests/Old.hs +++ b/tests/Tests/Old.hs @@ -196,7 +196,9 @@ lhsReaderTest :: String -> Test lhsReaderTest format = testWithNormalize normalizer "lhs" ["-r", format, "-w", "native"] ("lhs-test" <.> format) norm - where normalizer = purely $ writeNative def . normalize . handleError . readNative + where normalizer = purely $ \nat -> do + d <- handleError <$> readNative nat + writeNative def $ normalize d norm = if format == "markdown+lhs" then "lhs-test-markdown.native" else "lhs-test.native" diff --git a/tests/Tests/Readers/Docx.hs b/tests/Tests/Readers/Docx.hs index 3e630dd49..59147b664 100644 --- a/tests/Tests/Readers/Docx.hs +++ b/tests/Tests/Readers/Docx.hs @@ -14,6 +14,7 @@ import qualified Data.Map as M import Text.Pandoc.MediaBag (MediaBag, lookupMedia, mediaDirectory) import Codec.Archive.Zip import Text.Pandoc.Error +import Text.Pandoc.Class (runIOorExplode) -- We define a wrapper around pandoc that doesn't normalize in the -- tests. Since we do our own normalization, we want to make sure @@ -43,7 +44,8 @@ compareOutput opts docxFile nativeFile = do df <- B.readFile docxFile nf <- Prelude.readFile nativeFile let (p, _) = handleError $ readDocx opts df - return $ (noNorm p, noNorm (handleError $ readNative nf)) + df' <- runIOorExplode $ readNative nf + return $ (noNorm p, noNorm $ handleError df') testCompareWithOptsIO :: ReaderOptions -> String -> FilePath -> FilePath -> IO Test testCompareWithOptsIO opts name docxFile nativeFile = do diff --git a/tests/Tests/Readers/Odt.hs b/tests/Tests/Readers/Odt.hs index dff62c54b..0ff527130 100644 --- a/tests/Tests/Readers/Odt.hs +++ b/tests/Tests/Readers/Odt.hs @@ -5,6 +5,7 @@ import Text.Pandoc.Options import Text.Pandoc.Readers.Native import Text.Pandoc.Readers.Markdown import Text.Pandoc.Definition +import Text.Pandoc.Class (runIOorExplode) import Tests.Helpers import Test.Framework import qualified Data.ByteString.Lazy as B @@ -62,7 +63,8 @@ compareOdtToNative :: TestCreator compareOdtToNative opts odtPath nativePath = do nativeFile <- Prelude.readFile nativePath odtFile <- B.readFile odtPath - let native = getNoNormVia id "native" $ readNative nativeFile + native <- getNoNormVia id "native" <$> + runIOorExplode (readNative nativeFile) let odt = getNoNormVia fst "odt" $ readOdt opts odtFile return (odt,native) diff --git a/tests/Tests/Writers/Docx.hs b/tests/Tests/Writers/Docx.hs index a76583796..cdaa2c097 100644 --- a/tests/Tests/Writers/Docx.hs +++ b/tests/Tests/Writers/Docx.hs @@ -21,10 +21,12 @@ compareOutput opts nativeFileIn nativeFileOut = do nf <- Prelude.readFile nativeFileIn nf' <- Prelude.readFile nativeFileOut let wopts = fst opts - df <- runIOorExplode $ writeDocx wopts{writerUserDataDir = Just (".." "data")} - (handleError $ readNative nf) + df <- runIOorExplode $ do + d <- handleError <$> readNative nf + writeDocx wopts{writerUserDataDir = Just (".." "data")} d + df' <- handleError <$> runIOorExplode (readNative nf') let (p, _) = handleError $ readDocx (snd opts) df - return (p, handleError $ readNative nf') + return (p, df') testCompareWithOptsIO :: Options -> String -> FilePath -> FilePath -> IO Test testCompareWithOptsIO opts name nativeFileIn nativeFileOut = do -- cgit v1.2.3 From cc7191b3b17ce7c7010a021bf685753ed2019aa6 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Sun, 27 Nov 2016 13:17:00 -0500 Subject: Class: Add stateful IO warnings, and function to get warndings. Right now, the io warnings both print to stderr and write to the state. That can be easily modified. We also add a getWarnings function which pulls warnings out of the state for instances of PandocMonad. --- src/Text/Pandoc/Class.hs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 279770e97..899e18776 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -97,6 +97,7 @@ class (Functor m, Applicative m, Monad m, MonadError PandocExecutionError m) => -> String -> m (Either E.SomeException (B.ByteString, Maybe MimeType)) warn :: String -> m () + getWarnings :: m [String] fail :: String -> m b glob :: String -> m [FilePath] @@ -113,11 +114,11 @@ data PandocExecutionError = PandocFileReadError FilePath deriving (Show, Typeable) -- Nothing in this for now, but let's put it there anyway. -data PandocStateIO = PandocStateIO +data PandocStateIO = PandocStateIO { ioStWarnings :: [String] } deriving Show instance Default PandocStateIO where - def = PandocStateIO + def = PandocStateIO { ioStWarnings = [] } runIO :: PandocIO a -> IO (Either PandocExecutionError a) runIO ma = flip evalStateT def $ runExceptT $ unPandocIO ma @@ -156,7 +157,10 @@ instance PandocMonad PandocIO where fail = M.fail fetchItem ms s = liftIO $ IO.fetchItem ms s fetchItem' mb ms s = liftIO $ IO.fetchItem' mb ms s - warn = liftIO . IO.warn + warn msg = do + modify $ \st -> st{ioStWarnings = msg : ioStWarnings st} + liftIO $ IO.warn msg + getWarnings = gets ioStWarnings glob = liftIO . IO.glob data TestState = TestState { stStdGen :: StdGen @@ -266,6 +270,8 @@ instance PandocMonad PandocPure where warn s = modify $ \st -> st { stWarnings = s : stWarnings st } + getWarnings = gets stWarnings + glob s = do fontFiles <- asks envFontFiles return (filter (match (compile s)) fontFiles) -- cgit v1.2.3 From 2fc47ceebf5ec4e93e7a4395939c2da5248e1ef6 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Sun, 27 Nov 2016 15:29:46 -0500 Subject: Class: Add MediaBag to MonadState. --- src/Text/Pandoc/Class.hs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 899e18776..7de927bcc 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -56,13 +56,14 @@ import qualified Text.Pandoc.Shared as IO ( fetchItem , getDefaultReferenceODT , warn , readDataFile) -import Text.Pandoc.MediaBag (MediaBag, lookupMedia) import Text.Pandoc.Compat.Time (UTCTime) import qualified Text.Pandoc.Compat.Time as IO (getCurrentTime) import Data.Time.Clock.POSIX ( utcTimeToPOSIXSeconds , posixSecondsToUTCTime , POSIXTime ) import Text.Pandoc.MIME (MimeType, getMimeType) +import Text.Pandoc.MediaBag (MediaBag) +import qualified Text.Pandoc.MediaBag as MB import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL import qualified Control.Exception as E @@ -100,6 +101,7 @@ class (Functor m, Applicative m, Monad m, MonadError PandocExecutionError m) => getWarnings :: m [String] fail :: String -> m b glob :: String -> m [FilePath] + insertMedia :: FilePath -> Maybe MimeType -> BL.ByteString -> m () --Some functions derived from Primitives: @@ -114,11 +116,14 @@ data PandocExecutionError = PandocFileReadError FilePath deriving (Show, Typeable) -- Nothing in this for now, but let's put it there anyway. -data PandocStateIO = PandocStateIO { ioStWarnings :: [String] } - deriving Show +data PandocStateIO = PandocStateIO { ioStWarnings :: [String] + , ioStMediaBag :: MediaBag + } deriving Show instance Default PandocStateIO where - def = PandocStateIO { ioStWarnings = [] } + def = PandocStateIO { ioStWarnings = [] + , ioStMediaBag = mempty + } runIO :: PandocIO a -> IO (Either PandocExecutionError a) runIO ma = flip evalStateT def $ runExceptT $ unPandocIO ma @@ -134,7 +139,7 @@ runIOorExplode ma = do newtype PandocIO a = PandocIO { unPandocIO :: ExceptT PandocExecutionError (StateT PandocStateIO IO) a - } deriving (MonadIO, Functor, Applicative, Monad, MonadError PandocExecutionError) + } deriving (MonadIO, Functor, Applicative, Monad, MonadState PandocStateIO, MonadError PandocExecutionError) instance PandocMonad PandocIO where lookupEnv = liftIO . IO.lookupEnv @@ -162,6 +167,8 @@ instance PandocMonad PandocIO where liftIO $ IO.warn msg getWarnings = gets ioStWarnings glob = liftIO . IO.glob + insertMedia fp mime bs = + modify $ \st -> st{ioStMediaBag = MB.insertMedia fp mime bs (ioStMediaBag st) } data TestState = TestState { stStdGen :: StdGen , stWord8Store :: [Word8] -- should be @@ -173,6 +180,7 @@ data TestState = TestState { stStdGen :: StdGen -- contain every -- element at most -- once, e.g. [1..] + , stMediaBag :: MediaBag } instance Default TestState where @@ -180,6 +188,7 @@ instance Default TestState where , stWord8Store = [1..] , stWarnings = [] , stUniqStore = [1..] + , stMediaBag = mempty } data TestEnv = TestEnv { envEnv :: [(String, String)] @@ -264,7 +273,7 @@ instance PandocMonad PandocPure where Nothing -> return (Left $ E.toException $ PandocFileReadError fp) fetchItem' media sourceUrl nm = do - case lookupMedia nm media of + case MB.lookupMedia nm media of Nothing -> fetchItem sourceUrl nm Just (mime, bs) -> return (Right (B.concat $ BL.toChunks bs, Just mime)) @@ -275,3 +284,6 @@ instance PandocMonad PandocPure where glob s = do fontFiles <- asks envFontFiles return (filter (match (compile s)) fontFiles) + + insertMedia fp mime bs = + modify $ \st -> st{stMediaBag = MB.insertMedia fp mime bs (stMediaBag st) } -- cgit v1.2.3 From 97be338188b19abbe9931a4e4b765d9fd14583a4 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Sun, 27 Nov 2016 15:31:17 -0500 Subject: Change Test{State,Env} to Pure{State,Env} This was left over from when the pure function was called runTest. --- src/Text/Pandoc/Class.hs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 7de927bcc..ab1cc32b8 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -32,8 +32,8 @@ Typeclass for pandoc readers and writers, allowing both IO and pure instances. -} module Text.Pandoc.Class ( PandocMonad(..) - , TestState(..) - , TestEnv(..) + , PureState(..) + , PureEnv(..) , getPOSIXTime , PandocIO(..) , PandocPure(..) @@ -170,7 +170,7 @@ instance PandocMonad PandocIO where insertMedia fp mime bs = modify $ \st -> st{ioStMediaBag = MB.insertMedia fp mime bs (ioStMediaBag st) } -data TestState = TestState { stStdGen :: StdGen +data PureState = PureState { stStdGen :: StdGen , stWord8Store :: [Word8] -- should be -- inifinite, -- i.e. [1..] @@ -183,15 +183,15 @@ data TestState = TestState { stStdGen :: StdGen , stMediaBag :: MediaBag } -instance Default TestState where - def = TestState { stStdGen = mkStdGen 1848 +instance Default PureState where + def = PureState { stStdGen = mkStdGen 1848 , stWord8Store = [1..] , stWarnings = [] , stUniqStore = [1..] , stMediaBag = mempty } -data TestEnv = TestEnv { envEnv :: [(String, String)] +data PureEnv = PureEnv { envEnv :: [(String, String)] , envTime :: UTCTime , envReferenceDocx :: Archive , envReferenceODT :: Archive @@ -203,8 +203,8 @@ data TestEnv = TestEnv { envEnv :: [(String, String)] -- We have to figure this out a bit more. But let's put some empty -- values in for the time being. -instance Default TestEnv where - def = TestEnv { envEnv = [("USER", "pandoc-user")] +instance Default PureEnv where + def = PureEnv { envEnv = [("USER", "pandoc-user")] , envTime = posixSecondsToUTCTime 0 , envReferenceDocx = emptyArchive , envReferenceODT = emptyArchive @@ -218,8 +218,8 @@ instance E.Exception PandocExecutionError newtype PandocPure a = PandocPure { unPandocPure :: ExceptT PandocExecutionError - (ReaderT TestEnv (State TestState)) a - } deriving (Functor, Applicative, Monad, MonadReader TestEnv, MonadState TestState, MonadError PandocExecutionError) + (ReaderT PureEnv (State PureState)) a + } deriving (Functor, Applicative, Monad, MonadReader PureEnv, MonadState PureState, MonadError PandocExecutionError) runPure :: PandocPure a -> Either PandocExecutionError a runPure x = flip evalState def $ flip runReaderT def $ runExceptT $ unPandocPure x -- cgit v1.2.3 From b34bb8be015ab05f582bddf8f9acd2a5dbcce793 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Sun, 27 Nov 2016 15:32:28 -0500 Subject: List derived instances vertically one-per-line for readability. --- src/Text/Pandoc/Class.hs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index ab1cc32b8..bcd0d4172 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -139,7 +139,13 @@ runIOorExplode ma = do newtype PandocIO a = PandocIO { unPandocIO :: ExceptT PandocExecutionError (StateT PandocStateIO IO) a - } deriving (MonadIO, Functor, Applicative, Monad, MonadState PandocStateIO, MonadError PandocExecutionError) + } deriving ( MonadIO + , Functor + , Applicative + , Monad + , MonadState PandocStateIO + , MonadError PandocExecutionError + ) instance PandocMonad PandocIO where lookupEnv = liftIO . IO.lookupEnv @@ -219,7 +225,13 @@ instance E.Exception PandocExecutionError newtype PandocPure a = PandocPure { unPandocPure :: ExceptT PandocExecutionError (ReaderT PureEnv (State PureState)) a - } deriving (Functor, Applicative, Monad, MonadReader PureEnv, MonadState PureState, MonadError PandocExecutionError) + } deriving ( Functor + , Applicative + , Monad + , MonadReader PureEnv + , MonadState PureState + , MonadError PandocExecutionError + ) runPure :: PandocPure a -> Either PandocExecutionError a runPure x = flip evalState def $ flip runReaderT def $ runExceptT $ unPandocPure x -- cgit v1.2.3 From d447552be1c60d5a6ba15cd2a91395e32c8e0554 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Mon, 28 Nov 2016 16:54:00 -0500 Subject: Add ParseError to PandocExecutionError. This will be unified with Text.Pandoc.Error eventually. But I'm building it out here so as not to interfere with other modules that might be using the error module currently. --- src/Text/Pandoc/Class.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index bcd0d4172..13fdc3e50 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -112,6 +112,7 @@ getPOSIXTime = utcTimeToPOSIXSeconds <$> getCurrentTime -- We can add to this as we go data PandocExecutionError = PandocFileReadError FilePath | PandocShouldNeverHappenError String + | PandocParseError String | PandocSomeError String deriving (Show, Typeable) @@ -133,8 +134,9 @@ runIOorExplode ma = do eitherVal <- runIO ma case eitherVal of Right x -> return x - Left (PandocFileReadError fp) -> error $ "promple reading " ++ fp + Left (PandocFileReadError fp) -> error $ "problem reading " ++ fp Left (PandocShouldNeverHappenError s) -> error s + Left (PandocParseError s) -> error $ "parse error" ++ s Left (PandocSomeError s) -> error s newtype PandocIO a = PandocIO { -- cgit v1.2.3 From d9f5f551ddfde1c614df93125553421b82f43e76 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Tue, 29 Nov 2016 10:42:48 -0500 Subject: Class: add setMediaBag function. --- src/Text/Pandoc/Class.hs | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 13fdc3e50..12e6f900b 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -101,6 +101,7 @@ class (Functor m, Applicative m, Monad m, MonadError PandocExecutionError m) => getWarnings :: m [String] fail :: String -> m b glob :: String -> m [FilePath] + setMediaBag :: MediaBag -> m () insertMedia :: FilePath -> Maybe MimeType -> BL.ByteString -> m () --Some functions derived from Primitives: @@ -175,6 +176,8 @@ instance PandocMonad PandocIO where liftIO $ IO.warn msg getWarnings = gets ioStWarnings glob = liftIO . IO.glob + setMediaBag mb = + modify $ \st -> st{ioStMediaBag = mb} insertMedia fp mime bs = modify $ \st -> st{ioStMediaBag = MB.insertMedia fp mime bs (ioStMediaBag st) } @@ -299,5 +302,8 @@ instance PandocMonad PandocPure where fontFiles <- asks envFontFiles return (filter (match (compile s)) fontFiles) + setMediaBag mb = + modify $ \st -> st{stMediaBag = mb} + insertMedia fp mime bs = modify $ \st -> st{stMediaBag = MB.insertMedia fp mime bs (stMediaBag st) } -- cgit v1.2.3 From 840439ab2a4d44bc4d295df0d66003fbcc9bb18e Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Tue, 29 Nov 2016 00:36:36 -0500 Subject: Add IncoherentInstances pragma for HasQuotedContext. We can remove this if we can figure out a better way to do this. --- src/Text/Pandoc/Parsing.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 110e34c6a..90cc20ab6 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -3,7 +3,9 @@ , GeneralizedNewtypeDeriving , TypeSynonymInstances , MultiParamTypeClasses -, FlexibleInstances #-} +, FlexibleInstances +, IncoherentInstances #-} + {- Copyright (C) 2006-2016 John MacFarlane -- cgit v1.2.3 From b53ebcdf8e8e1f7098a0c93ead4b5bf99971c77f Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Mon, 28 Nov 2016 17:13:46 -0500 Subject: Working on readers. --- src/Text/Pandoc.hs | 95 ++--- src/Text/Pandoc/Class.hs | 10 + src/Text/Pandoc/Readers/CommonMark.hs | 7 +- src/Text/Pandoc/Readers/DocBook.hs | 32 +- src/Text/Pandoc/Readers/Docx.hs | 96 +++--- src/Text/Pandoc/Readers/EPUB.hs | 50 ++- src/Text/Pandoc/Readers/HTML.hs | 213 ++++++------ src/Text/Pandoc/Readers/Haddock.hs | 19 +- src/Text/Pandoc/Readers/LaTeX.hs | 203 +++++------ src/Text/Pandoc/Readers/Markdown.hs | 478 ++++++++++++++------------ src/Text/Pandoc/Readers/MediaWiki.hs | 146 ++++---- src/Text/Pandoc/Readers/Native.hs | 7 +- src/Text/Pandoc/Readers/OPML.hs | 36 +- src/Text/Pandoc/Readers/Odt.hs | 22 +- src/Text/Pandoc/Readers/Org.hs | 20 +- src/Text/Pandoc/Readers/Org/BlockStarts.hs | 33 +- src/Text/Pandoc/Readers/Org/Blocks.hs | 163 ++++----- src/Text/Pandoc/Readers/Org/ExportSettings.hs | 33 +- src/Text/Pandoc/Readers/Org/Inlines.hs | 246 ++++++------- src/Text/Pandoc/Readers/Org/Meta.hs | 45 +-- src/Text/Pandoc/Readers/Org/ParserState.hs | 4 +- src/Text/Pandoc/Readers/Org/Parsing.hs | 36 +- src/Text/Pandoc/Readers/RST.hs | 245 ++++++------- src/Text/Pandoc/Readers/TWiki.hs | 25 +- src/Text/Pandoc/Readers/Textile.hs | 176 +++++----- src/Text/Pandoc/Readers/Txt2Tags.hs | 45 ++- 26 files changed, 1328 insertions(+), 1157 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index 34b6b8d0c..02217c376 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -69,7 +69,6 @@ module Text.Pandoc , writers -- * Readers: converting /to/ Pandoc format , Reader (..) - , mkStringReader , readDocx , readOdt , readMarkdown @@ -183,7 +182,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 (PandocMonad, runIOorExplode) +import Text.Pandoc.Class (PandocMonad, runIOorExplode, PandocExecutionError(..)) import Data.Aeson import qualified Data.ByteString.Lazy as BL import Data.List (intercalate) @@ -192,6 +191,7 @@ import qualified Data.Set as Set import Text.Parsec import Text.Parsec.Error import qualified Text.Pandoc.UTF8 as UTF8 +import Control.Monad.Except (throwError) parseFormatSpec :: String -> Either ParseError (String, Set Extension -> Set Extension) @@ -216,55 +216,58 @@ parseFormatSpec = parse formatSpec "" -- TODO: when we get the PandocMonad stuff all sorted out, -- we can simply these types considerably. Errors/MediaBag can be -- part of the monad's internal state. -data Reader m = StringReader (ReaderOptions -> String -> m (Either PandocError Pandoc)) - | ByteStringReader (ReaderOptions -> BL.ByteString -> m (Either PandocError (Pandoc,MediaBag))) +data Reader m = StringReader (ReaderOptions -> String -> m Pandoc) + | ByteStringReader (ReaderOptions -> BL.ByteString -> m Pandoc) -mkStringReader :: (ReaderOptions -> String -> Either PandocError Pandoc) -> Reader IO -mkStringReader r = StringReader (\o s -> return $ r o s) +-- mkStringReader :: (ReaderOptions -> String -> Either PandocError Pandoc) -> Reader IO +-- mkStringReader r = StringReader (\o s -> return $ r o s) -mkStringReaderWithWarnings :: (ReaderOptions -> String -> Either PandocError (Pandoc, [String])) -> Reader IO -mkStringReaderWithWarnings r = StringReader $ \o s -> - case r o s of - Left err -> return $ Left err - Right (doc, warnings) -> do - mapM_ warn warnings - return (Right doc) +-- mkStringReaderWithWarnings :: (ReaderOptions -> String -> Either PandocError (Pandoc, [String])) -> Reader IO +-- mkStringReaderWithWarnings r = StringReader $ \o s -> +-- case r o s of +-- Left err -> return $ Left err +-- Right (doc, warnings) -> do +-- mapM_ warn warnings +-- return (Right doc) -mkBSReader :: (ReaderOptions -> BL.ByteString -> Either PandocError (Pandoc, MediaBag)) -> Reader IO -mkBSReader r = ByteStringReader (\o s -> return $ r o s) +-- mkBSReader :: (ReaderOptions -> BL.ByteString -> Either PandocError (Pandoc, MediaBag)) -> Reader IO +-- mkBSReader r = ByteStringReader (\o s -> return $ r o s) -mkBSReaderWithWarnings :: (ReaderOptions -> BL.ByteString -> Either PandocError (Pandoc, MediaBag, [String])) -> Reader IO -mkBSReaderWithWarnings r = ByteStringReader $ \o s -> - case r o s of - Left err -> return $ Left err - Right (doc, mediaBag, warnings) -> do - mapM_ warn warnings - return $ Right (doc, mediaBag) +-- mkBSReaderWithWarnings :: (ReaderOptions -> BL.ByteString -> Either PandocError (Pandoc, MediaBag, [String])) -> Reader IO +-- mkBSReaderWithWarnings r = ByteStringReader $ \o s -> +-- case r o s of +-- Left err -> return $ Left err +-- Right (doc, mediaBag, warnings) -> do +-- mapM_ warn warnings +-- return $ Right (doc, mediaBag) -- | Association list of formats and readers. -readers :: [(String, Reader IO)] -readers = [ ("native" , StringReader $ \_ s -> runIOorExplode (readNative s)) - ,("json" , mkStringReader readJSON ) - ,("markdown" , mkStringReaderWithWarnings readMarkdownWithWarnings) - ,("markdown_strict" , mkStringReaderWithWarnings readMarkdownWithWarnings) - ,("markdown_phpextra" , mkStringReaderWithWarnings readMarkdownWithWarnings) - ,("markdown_github" , mkStringReaderWithWarnings readMarkdownWithWarnings) - ,("markdown_mmd", mkStringReaderWithWarnings readMarkdownWithWarnings) - ,("commonmark" , mkStringReader readCommonMark) - ,("rst" , mkStringReaderWithWarnings readRSTWithWarnings ) - ,("mediawiki" , mkStringReader readMediaWiki) - ,("docbook" , mkStringReader readDocBook) - ,("opml" , mkStringReader readOPML) - ,("org" , mkStringReader readOrg) - ,("textile" , mkStringReader readTextile) -- TODO : textile+lhs - ,("html" , mkStringReader readHtml) - ,("latex" , mkStringReader readLaTeX) - ,("haddock" , mkStringReader readHaddock) - ,("twiki" , mkStringReader readTWiki) - ,("docx" , mkBSReaderWithWarnings readDocxWithWarnings) - ,("odt" , mkBSReader readOdt) - ,("t2t" , mkStringReader readTxt2TagsNoMacros) - ,("epub" , mkBSReader readEPUB) +readers :: PandocMonad m => [(String, Reader m)] +readers = [ ("native" , StringReader $ \_ s -> readNative s) + ,("json" , StringReader $ \o s -> + case readJSON o s of + Right doc -> return doc + Left _ -> throwError $ PandocParseError "JSON parse error") + ,("markdown" , StringReader readMarkdown) + ,("markdown_strict" , StringReader readMarkdown) + ,("markdown_phpextra" , StringReader readMarkdown) + ,("markdown_github" , StringReader readMarkdown) + ,("markdown_mmd", StringReader readMarkdown) + ,("commonmark" , StringReader readCommonMark) + ,("rst" , StringReader readRSTWithWarnings ) + ,("mediawiki" , StringReader readMediaWiki) + ,("docbook" , StringReader readDocBook) + ,("opml" , StringReader readOPML) + ,("org" , StringReader readOrg) + ,("textile" , StringReader readTextile) -- TODO : textile+lhs + ,("html" , StringReader readHtml) + ,("latex" , StringReader readLaTeX) + ,("haddock" , StringReader readHaddock) + ,("twiki" , StringReader readTWiki) + ,("docx" , ByteStringReader readDocx) + ,("odt" , ByteStringReader readOdt) + -- ,("t2t" , mkStringReader readTxt2TagsNoMacros) + ,("epub" , ByteStringReader readEPUB) ] data Writer m = StringWriter (WriterOptions -> Pandoc -> m String) @@ -351,7 +354,7 @@ getDefaultExtensions "epub" = Set.fromList [Ext_raw_html, getDefaultExtensions _ = Set.fromList [Ext_auto_identifiers] -- | Retrieve reader based on formatSpec (format+extensions). -getReader :: String -> Either String (Reader IO) +getReader :: PandocMonad m => String -> Either String (Reader m) getReader s = case parseFormatSpec s of Left e -> Left $ intercalate "\n" [m | Message m <- errorMessages e] diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 12e6f900b..5cef621dc 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -35,6 +35,7 @@ module Text.Pandoc.Class ( PandocMonad(..) , PureState(..) , PureEnv(..) , getPOSIXTime + , addWarningWithPos , PandocIO(..) , PandocPure(..) , PandocExecutionError(..) @@ -57,6 +58,7 @@ import qualified Text.Pandoc.Shared as IO ( fetchItem , warn , readDataFile) import Text.Pandoc.Compat.Time (UTCTime) +import Text.Pandoc.Parsing (ParserT, ParserState, SourcePos) import qualified Text.Pandoc.Compat.Time as IO (getCurrentTime) import Data.Time.Clock.POSIX ( utcTimeToPOSIXSeconds , posixSecondsToUTCTime @@ -109,6 +111,14 @@ class (Functor m, Applicative m, Monad m, MonadError PandocExecutionError m) => getPOSIXTime :: (PandocMonad m) => m POSIXTime getPOSIXTime = utcTimeToPOSIXSeconds <$> getCurrentTime +addWarningWithPos :: PandocMonad m + => Maybe SourcePos + -> String + -> ParserT [Char] ParserState m () +addWarningWithPos mbpos msg = + lift $ + warn $ + msg ++ maybe "" (\pos -> " " ++ show pos) mbpos -- We can add to this as we go data PandocExecutionError = PandocFileReadError FilePath diff --git a/src/Text/Pandoc/Readers/CommonMark.hs b/src/Text/Pandoc/Readers/CommonMark.hs index d20d386e7..38c54c8dc 100644 --- a/src/Text/Pandoc/Readers/CommonMark.hs +++ b/src/Text/Pandoc/Readers/CommonMark.hs @@ -37,11 +37,12 @@ import Data.Text (unpack, pack) import Data.List (groupBy) import Text.Pandoc.Definition import Text.Pandoc.Options -import Text.Pandoc.Error +import Text.Pandoc.Class (PandocMonad) -- | Parse a CommonMark formatted string into a 'Pandoc' structure. -readCommonMark :: ReaderOptions -> String -> Either PandocError Pandoc -readCommonMark opts = Right . nodeToPandoc . commonmarkToNode opts' . pack +readCommonMark :: PandocMonad m => ReaderOptions -> String -> m Pandoc +readCommonMark opts s = return $ + nodeToPandoc $ commonmarkToNode opts' $ pack s where opts' = if readerSmart opts then [optNormalize, optSmart] else [optNormalize] diff --git a/src/Text/Pandoc/Readers/DocBook.hs b/src/Text/Pandoc/Readers/DocBook.hs index 68552ccb3..bef256a93 100644 --- a/src/Text/Pandoc/Readers/DocBook.hs +++ b/src/Text/Pandoc/Readers/DocBook.hs @@ -13,10 +13,9 @@ import Control.Monad.State import Data.List (intersperse) import Data.Maybe (fromMaybe) import Text.TeXMath (readMathML, writeTeX) -import Text.Pandoc.Error (PandocError) -import Control.Monad.Except import Data.Default import Data.Foldable (asum) +import Text.Pandoc.Class (PandocMonad) {- @@ -502,7 +501,7 @@ List of all DocBook tags, with [x] indicating implemented, [x] ?asciidoc-br? - line break from asciidoc docbook output -} -type DB = ExceptT PandocError (State DBState) +type DB m = StateT DBState m data DBState = DBState{ dbSectionLevel :: Int , dbQuoteType :: QuoteType @@ -523,10 +522,11 @@ instance Default DBState where , dbContent = [] } -readDocBook :: ReaderOptions -> String -> Either PandocError Pandoc -readDocBook _ inp = (\blocks -> Pandoc (dbMeta st') (toList . mconcat $ blocks)) <$> bs - where (bs , st') = flip runState (def{ dbContent = tree }) . runExceptT . mapM parseBlock $ tree - tree = normalizeTree . parseXML . handleInstructions $ inp +readDocBook :: PandocMonad m => ReaderOptions -> String -> m Pandoc +readDocBook _ inp = do + let tree = normalizeTree . parseXML . handleInstructions $ inp + (bs, st') <- flip runStateT (def{ dbContent = tree }) $ mapM parseBlock $ tree + return $ Pandoc (dbMeta st') (toList . mconcat $ bs) -- We treat specially (issue #1236), converting it -- to
, since xml-light doesn't parse the instruction correctly. @@ -538,7 +538,7 @@ handleInstructions xs = case break (=='<') xs of ([], '<':zs) -> '<' : handleInstructions zs (ys, zs) -> ys ++ handleInstructions zs -getFigure :: Element -> DB Blocks +getFigure :: PandocMonad m => Element -> DB m Blocks getFigure e = do tit <- case filterChild (named "title") e of Just t -> getInlines t @@ -579,20 +579,20 @@ named s e = qName (elName e) == s -- -acceptingMetadata :: DB a -> DB a +acceptingMetadata :: PandocMonad m => DB m a -> DB m a acceptingMetadata p = do modify (\s -> s { dbAcceptsMeta = True } ) res <- p modify (\s -> s { dbAcceptsMeta = False }) return res -checkInMeta :: Monoid a => DB () -> DB a +checkInMeta :: (PandocMonad m, Monoid a) => DB m () -> DB m a checkInMeta p = do accepts <- dbAcceptsMeta <$> get when accepts p return mempty -addMeta :: ToMetaValue a => String -> a -> DB () +addMeta :: PandocMonad m => ToMetaValue a => String -> a -> DB m () addMeta field val = modify (setMeta field val) instance HasMeta DBState where @@ -631,7 +631,7 @@ addToStart toadd bs = -- function that is used by both mediaobject (in parseBlock) -- and inlinemediaobject (in parseInline) -- A DocBook mediaobject is a wrapper around a set of alternative presentations -getMediaobject :: Element -> DB Inlines +getMediaobject :: PandocMonad m => Element -> DB m Inlines getMediaobject e = do (imageUrl, attr) <- case filterChild (named "imageobject") e of @@ -658,11 +658,11 @@ getMediaobject e = do else (return figTitle, "fig:") liftM (imageWith attr imageUrl title) caption -getBlocks :: Element -> DB Blocks +getBlocks :: PandocMonad m => Element -> DB m Blocks getBlocks e = mconcat <$> (mapM parseBlock $ elContent e) -parseBlock :: Content -> DB Blocks +parseBlock :: PandocMonad m => Content -> DB m Blocks parseBlock (Text (CData CDataRaw _ _)) = return mempty -- DOCTYPE parseBlock (Text (CData _ s _)) = if all isSpace s then return mempty @@ -902,7 +902,7 @@ parseBlock (Elem e) = lineItems = mapM getInlines $ filterChildren (named "line") e metaBlock = acceptingMetadata (getBlocks e) >> return mempty -getInlines :: Element -> DB Inlines +getInlines :: PandocMonad m => Element -> DB m Inlines getInlines e' = (trimInlines . mconcat) <$> (mapM parseInline $ elContent e') strContentRecursive :: Element -> String @@ -913,7 +913,7 @@ elementToStr :: Content -> Content elementToStr (Elem e') = Text $ CData CDataText (strContentRecursive e') Nothing elementToStr x = x -parseInline :: Content -> DB Inlines +parseInline :: PandocMonad m => Content -> DB m Inlines parseInline (Text (CData _ s _)) = return $ text s parseInline (CRef ref) = return $ maybe (text $ map toUpper ref) (text) $ lookupEntity ref diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index a43043d84..87b64d544 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -82,7 +82,7 @@ import Text.Pandoc.Readers.Docx.Parse import Text.Pandoc.Readers.Docx.Lists import Text.Pandoc.Readers.Docx.Combine import Text.Pandoc.Shared -import Text.Pandoc.MediaBag (insertMedia, MediaBag) +import Text.Pandoc.MediaBag (MediaBag) import Data.List (delete, intersect) import Text.TeXMath (writeTeX) import Data.Default (Default) @@ -96,27 +96,28 @@ import qualified Data.Sequence as Seq (null) #if !(MIN_VERSION_base(4,8,0)) import Data.Traversable (traverse) #endif +import Control.Monad.Except (throwError) +import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) +import qualified Text.Pandoc.Class as P -import Text.Pandoc.Error -import Control.Monad.Except - -readDocxWithWarnings :: ReaderOptions - -> B.ByteString - -> Either PandocError (Pandoc, MediaBag, [String]) -readDocxWithWarnings opts bytes +readDocx :: PandocMonad m + => ReaderOptions + -> B.ByteString + -> m Pandoc +readDocx opts bytes | Right archive <- toArchiveOrFail bytes , Right (docx, parserWarnings) <- archiveToDocxWithWarnings archive = do - (meta, blks, mediaBag, warnings) <- docxToOutput opts docx - return (Pandoc meta blks, mediaBag, parserWarnings ++ warnings) -readDocxWithWarnings _ _ = - Left (ParseFailure "couldn't parse docx file") - -readDocx :: ReaderOptions + mapM_ P.warn parserWarnings + (meta, blks) <- docxToOutput opts docx + return $ Pandoc meta blks +readDocx _ _ = + throwError $ PandocSomeError "couldn't parse docx file" + +readDocxWithWarnings :: PandocMonad m + => ReaderOptions -> B.ByteString - -> Either PandocError (Pandoc, MediaBag) -readDocx opts bytes = do - (pandoc, mediaBag, _) <- readDocxWithWarnings opts bytes - return (pandoc, mediaBag) + -> m Pandoc +readDocxWithWarnings = readDocx data DState = DState { docxAnchorMap :: M.Map String String , docxMediaBag :: MediaBag @@ -137,15 +138,10 @@ data DEnv = DEnv { docxOptions :: ReaderOptions instance Default DEnv where def = DEnv def False -type DocxContext = ExceptT PandocError (ReaderT DEnv (State DState)) +type DocxContext m = ReaderT DEnv (StateT DState m) -evalDocxContext :: DocxContext a -> DEnv -> DState -> Either PandocError a -evalDocxContext ctx env st = flip evalState st . flip runReaderT env . runExceptT $ ctx - -addDocxWarning :: String -> DocxContext () -addDocxWarning msg = do - warnings <- gets docxWarnings - modify $ \s -> s {docxWarnings = msg : warnings} +evalDocxContext :: PandocMonad m => DocxContext m a -> DEnv -> DState -> m a +evalDocxContext ctx env st = flip evalStateT st $ flip runReaderT env $ ctx -- This is empty, but we put it in for future-proofing. spansToKeep :: [String] @@ -179,7 +175,7 @@ isEmptyPar (Paragraph _ parParts) = isEmptyElem _ = True isEmptyPar _ = False -bodyPartsToMeta' :: [BodyPart] -> DocxContext (M.Map String MetaValue) +bodyPartsToMeta' :: PandocMonad m => [BodyPart] -> DocxContext m (M.Map String MetaValue) bodyPartsToMeta' [] = return M.empty bodyPartsToMeta' (bp : bps) | (Paragraph pPr parParts) <- bp @@ -195,7 +191,7 @@ bodyPartsToMeta' (bp : bps) return $ M.insertWith f metaField (MetaInlines (toList inlines)) remaining bodyPartsToMeta' (_ : bps) = bodyPartsToMeta' bps -bodyPartsToMeta :: [BodyPart] -> DocxContext Meta +bodyPartsToMeta :: PandocMonad m => [BodyPart] -> DocxContext m Meta bodyPartsToMeta bps = do mp <- bodyPartsToMeta' bps let mp' = @@ -297,7 +293,7 @@ runStyleToTransform rPr emph . (runStyleToTransform rPr {rUnderline = Nothing}) | otherwise = id -runToInlines :: Run -> DocxContext Inlines +runToInlines :: PandocMonad m => Run -> DocxContext m Inlines runToInlines (Run rs runElems) | Just (s, _) <- rStyle rs , s `elem` codeStyles = @@ -318,8 +314,7 @@ runToInlines (Endnote bps) = do blksList <- smushBlocks <$> (mapM bodyPartToBlocks bps) return $ note blksList runToInlines (InlineDrawing fp title alt bs ext) = do - mediaBag <- gets docxMediaBag - modify $ \s -> s { docxMediaBag = insertMedia fp Nothing bs mediaBag } + (lift . lift) $ P.insertMedia fp Nothing bs return $ imageWith (extentToAttr ext) fp title $ text alt runToInlines InlineChart = return $ spanWith ("", ["chart"], []) $ text "[CHART]" @@ -330,7 +325,7 @@ extentToAttr (Just (w, h)) = showDim d = show (d / 914400) ++ "in" extentToAttr _ = nullAttr -blocksToInlinesWarn :: String -> Blocks -> DocxContext Inlines +blocksToInlinesWarn :: PandocMonad m => String -> Blocks -> DocxContext m Inlines blocksToInlinesWarn cmtId blks = do let blkList = toList blks notParaOrPlain :: Block -> Bool @@ -338,10 +333,10 @@ blocksToInlinesWarn cmtId blks = do notParaOrPlain (Plain _) = False notParaOrPlain _ = True when (not $ null $ filter notParaOrPlain blkList) - (addDocxWarning $ "Docx comment " ++ cmtId ++ " will not retain formatting") + ((lift . lift) $ P.warn $ "Docx comment " ++ cmtId ++ " will not retain formatting") return $ fromList $ blocksToInlines blkList -parPartToInlines :: ParPart -> DocxContext Inlines +parPartToInlines :: PandocMonad m => ParPart -> DocxContext m Inlines parPartToInlines (PlainRun r) = runToInlines r parPartToInlines (Insertion _ author date runs) = do opts <- asks docxOptions @@ -403,8 +398,7 @@ parPartToInlines (BookMark _ anchor) = (modify $ \s -> s { docxAnchorMap = M.insert anchor newAnchor anchorMap}) return $ spanWith (newAnchor, ["anchor"], []) mempty parPartToInlines (Drawing fp title alt bs ext) = do - mediaBag <- gets docxMediaBag - modify $ \s -> s { docxMediaBag = insertMedia fp Nothing bs mediaBag } + (lift . lift) $ P.insertMedia fp Nothing bs return $ imageWith (extentToAttr ext) fp title $ text alt parPartToInlines Chart = do return $ spanWith ("", ["chart"], []) $ text "[CHART]" @@ -426,10 +420,10 @@ isAnchorSpan _ = False dummyAnchors :: [String] dummyAnchors = ["_GoBack"] -makeHeaderAnchor :: Blocks -> DocxContext Blocks +makeHeaderAnchor :: PandocMonad m => Blocks -> DocxContext m Blocks makeHeaderAnchor bs = traverse makeHeaderAnchor' bs -makeHeaderAnchor' :: Block -> DocxContext Block +makeHeaderAnchor' :: PandocMonad m => Block -> DocxContext m Block -- If there is an anchor already there (an anchor span in the header, -- to be exact), we rename and associate the new id with the old one. makeHeaderAnchor' (Header n (ident, classes, kvs) ils) @@ -463,12 +457,12 @@ singleParaToPlain blks singleton $ Plain ils singleParaToPlain blks = blks -cellToBlocks :: Cell -> DocxContext Blocks +cellToBlocks :: PandocMonad m => Cell -> DocxContext m Blocks cellToBlocks (Cell bps) = do blks <- smushBlocks <$> mapM bodyPartToBlocks bps return $ fromList $ blocksToDefinitions $ blocksToBullets $ toList blks -rowToBlocksList :: Row -> DocxContext [Blocks] +rowToBlocksList :: PandocMonad m => Row -> DocxContext m [Blocks] rowToBlocksList (Row cells) = do blksList <- mapM cellToBlocks cells return $ map singleParaToPlain blksList @@ -518,7 +512,7 @@ parStyleToTransform pPr False -> parStyleToTransform pPr' parStyleToTransform _ = id -bodyPartToBlocks :: BodyPart -> DocxContext Blocks +bodyPartToBlocks :: PandocMonad m => BodyPart -> DocxContext m Blocks bodyPartToBlocks (Paragraph pPr parparts) | not $ null $ codeDivs `intersect` (pStyle pPr) = return @@ -597,7 +591,7 @@ bodyPartToBlocks (OMathPara e) = do -- replace targets with generated anchors. -rewriteLink' :: Inline -> DocxContext Inline +rewriteLink' :: PandocMonad m => Inline -> DocxContext m Inline rewriteLink' l@(Link attr ils ('#':target, title)) = do anchorMap <- gets docxAnchorMap return $ case M.lookup target anchorMap of @@ -605,23 +599,21 @@ rewriteLink' l@(Link attr ils ('#':target, title)) = do Nothing -> l rewriteLink' il = return il -rewriteLinks :: [Block] -> DocxContext [Block] +rewriteLinks :: PandocMonad m => [Block] -> DocxContext m [Block] rewriteLinks = mapM (walkM rewriteLink') -bodyToOutput :: Body -> DocxContext (Meta, [Block], MediaBag, [String]) +bodyToOutput :: PandocMonad m => Body -> DocxContext m (Meta, [Block]) bodyToOutput (Body bps) = do let (metabps, blkbps) = sepBodyParts bps meta <- bodyPartsToMeta metabps blks <- smushBlocks <$> mapM bodyPartToBlocks blkbps blks' <- rewriteLinks $ blocksToDefinitions $ blocksToBullets $ toList blks - mediaBag <- gets docxMediaBag - warnings <- gets docxWarnings - return $ (meta, - blks', - mediaBag, - warnings) - -docxToOutput :: ReaderOptions -> Docx -> Either PandocError (Meta, [Block], MediaBag, [String]) + return $ (meta, blks') + +docxToOutput :: PandocMonad m + => ReaderOptions + -> Docx + -> m (Meta, [Block]) docxToOutput opts (Docx (Document _ body)) = let dEnv = def { docxOptions = opts} in evalDocxContext (bodyToOutput body) dEnv def diff --git a/src/Text/Pandoc/Readers/EPUB.hs b/src/Text/Pandoc/Readers/EPUB.hs index 4c31bf1ae..0dbe87052 100644 --- a/src/Text/Pandoc/Readers/EPUB.hs +++ b/src/Text/Pandoc/Readers/EPUB.hs @@ -11,13 +11,12 @@ module Text.Pandoc.Readers.EPUB import Text.XML.Light import Text.Pandoc.Definition hiding (Attr) import Text.Pandoc.Readers.HTML (readHtml) -import Text.Pandoc.Error import Text.Pandoc.Walk (walk, query) import Text.Pandoc.Options ( ReaderOptions(..), readerTrace) import Text.Pandoc.Shared (escapeURI, collapseFilePath, addMetaField) import Network.URI (unEscapeString) import Text.Pandoc.MediaBag (MediaBag, insertMedia) -import Control.Monad.Except (MonadError, throwError, runExcept, Except) +import Control.Monad.Except (throwError) import Text.Pandoc.MIME (MimeType) import qualified Text.Pandoc.Builder as B import Codec.Archive.Zip ( Archive (..), toArchiveOrFail, fromEntry @@ -33,23 +32,25 @@ import Data.Maybe (mapMaybe, fromMaybe) import qualified Data.Map as M (Map, lookup, fromList, elems) import Data.Monoid ((<>)) import Control.DeepSeq (deepseq, NFData) +import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) +import qualified Text.Pandoc.Class as P import Debug.Trace (trace) type Items = M.Map String (FilePath, MimeType) -readEPUB :: ReaderOptions -> BL.ByteString -> Either PandocError (Pandoc, MediaBag) +readEPUB :: PandocMonad m => ReaderOptions -> BL.ByteString -> m Pandoc readEPUB opts bytes = case toArchiveOrFail bytes of - Right archive -> runEPUB $ archiveToEPUB opts $ archive - Left _ -> Left $ ParseFailure "Couldn't extract ePub file" + Right archive -> archiveToEPUB opts $ archive + Left _ -> throwError $ PandocParseError "Couldn't extract ePub file" -runEPUB :: Except PandocError a -> Either PandocError a -runEPUB = runExcept +-- runEPUB :: Except PandocError a -> Either PandocError a +-- runEPUB = runExcept -- Note that internal reference are aggresively normalised so that all ids -- are of the form "filename#id" -- -archiveToEPUB :: (MonadError PandocError m) => ReaderOptions -> Archive -> m (Pandoc, MediaBag) +archiveToEPUB :: (PandocMonad m) => ReaderOptions -> Archive -> m Pandoc archiveToEPUB os archive = do -- root is path to folder with manifest file in (root, content) <- getManifest archive @@ -63,24 +64,21 @@ archiveToEPUB os archive = do foldM' (\a b -> ((a <>) . walk (prependHash escapedSpine)) `liftM` parseSpineElem root b) mempty spine let ast = coverDoc <> (Pandoc meta bs) - let mediaBag = fetchImages (M.elems items) root archive ast - return $ (ast, mediaBag) + P.setMediaBag $ fetchImages (M.elems items) root archive ast + return ast where os' = os {readerParseRaw = True} - parseSpineElem :: MonadError PandocError m => FilePath -> (FilePath, MimeType) -> m Pandoc + parseSpineElem :: PandocMonad m => FilePath -> (FilePath, MimeType) -> m Pandoc parseSpineElem (normalise -> r) (normalise -> path, mime) = do when (readerTrace os) (traceM path) doc <- mimeToReader mime r path let docSpan = B.doc $ B.para $ B.spanWith (takeFileName path, [], []) mempty return $ docSpan <> doc - mimeToReader :: MonadError PandocError m => MimeType -> FilePath -> FilePath -> m Pandoc + mimeToReader :: PandocMonad m => MimeType -> FilePath -> FilePath -> m Pandoc mimeToReader "application/xhtml+xml" (unEscapeString -> root) (unEscapeString -> path) = do fname <- findEntryByPathE (root path) archive - html <- either throwError return . - readHtml os' . - UTF8.toStringLazy $ - fromEntry fname + html <- readHtml os' . UTF8.toStringLazy $ fromEntry fname return $ fixInternalReferences path html mimeToReader s _ (unEscapeString -> path) | s `elem` imageMimes = return $ imageToPandoc path @@ -121,7 +119,7 @@ imageMimes = ["image/gif", "image/jpeg", "image/png"] type CoverImage = FilePath -parseManifest :: (MonadError PandocError m) => Element -> m (Maybe CoverImage, Items) +parseManifest :: (PandocMonad m) => Element -> m (Maybe CoverImage, Items) parseManifest content = do manifest <- findElementE (dfName "manifest") content let items = findChildren (dfName "item") manifest @@ -137,7 +135,7 @@ parseManifest content = do mime <- findAttrE (emptyName "media-type") e return (uid, (href, mime)) -parseSpine :: MonadError PandocError m => Items -> Element -> m [(FilePath, MimeType)] +parseSpine :: PandocMonad m => Items -> Element -> m [(FilePath, MimeType)] parseSpine is e = do spine <- findElementE (dfName "spine") e let itemRefs = findChildren (dfName "itemref") spine @@ -148,7 +146,7 @@ parseSpine is e = do guard linear findAttr (emptyName "idref") ref -parseMeta :: MonadError PandocError m => Element -> m Meta +parseMeta :: PandocMonad m => Element -> m Meta parseMeta content = do meta <- findElementE (dfName "metadata") content let dcspace (QName _ (Just "http://purl.org/dc/elements/1.1/") (Just "dc")) = True @@ -166,7 +164,7 @@ renameMeta :: String -> String renameMeta "creator" = "author" renameMeta s = s -getManifest :: MonadError PandocError m => Archive -> m (String, Element) +getManifest :: PandocMonad m => Archive -> m (String, Element) getManifest archive = do metaEntry <- findEntryByPathE ("META-INF" "container.xml") archive docElem <- (parseXMLDocE . UTF8.toStringLazy . fromEntry) metaEntry @@ -268,18 +266,18 @@ emptyName s = QName s Nothing Nothing -- Convert Maybe interface to Either -findAttrE :: MonadError PandocError m => QName -> Element -> m String +findAttrE :: PandocMonad m => QName -> Element -> m String findAttrE q e = mkE "findAttr" $ findAttr q e -findEntryByPathE :: MonadError PandocError m => FilePath -> Archive -> m Entry +findEntryByPathE :: PandocMonad m => FilePath -> Archive -> m Entry findEntryByPathE (normalise -> path) a = mkE ("No entry on path: " ++ path) $ findEntryByPath path a -parseXMLDocE :: MonadError PandocError m => String -> m Element +parseXMLDocE :: PandocMonad m => String -> m Element parseXMLDocE doc = mkE "Unable to parse XML doc" $ parseXMLDoc doc -findElementE :: MonadError PandocError m => QName -> Element -> m Element +findElementE :: PandocMonad m => QName -> Element -> m Element findElementE e x = mkE ("Unable to find element: " ++ show e) $ findElement e x -mkE :: MonadError PandocError m => String -> Maybe a -> m a -mkE s = maybe (throwError . ParseFailure $ s) return +mkE :: PandocMonad m => String -> Maybe a -> m a +mkE s = maybe (throwError . PandocParseError $ s) return diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index abe5f66ce..ef28ff739 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -44,7 +44,7 @@ import Text.Pandoc.Definition import qualified Text.Pandoc.Builder as B import Text.Pandoc.Builder (Blocks, Inlines, trimInlines, HasMeta(..)) import Text.Pandoc.Shared ( extractSpaces, renderTags', addMetaField - , escapeURI, safeRead, mapLeft ) + , escapeURI, safeRead ) import Text.Pandoc.Options (ReaderOptions(readerParseRaw, readerTrace) , Extension (Ext_epub_html_exts, Ext_native_divs, Ext_native_spans)) @@ -62,38 +62,46 @@ import Text.Printf (printf) import Debug.Trace (trace) import Text.TeXMath (readMathML, writeTeX) import Data.Default (Default (..), def) -import Control.Monad.Reader (Reader,ask, asks, local, runReader) +import Control.Monad.Reader (ask, asks, local, ReaderT, runReaderT, lift) import Network.URI (URI, parseURIReference, nonStrictRelativeTo) -import Text.Pandoc.Error import Text.Pandoc.CSS (foldOrElse, pickStyleAttrProps) import Data.Monoid ((<>)) import Text.Parsec.Error import qualified Data.Set as Set +import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) +import Control.Monad.Except (throwError) + -- | Convert HTML-formatted string to 'Pandoc' document. -readHtml :: ReaderOptions -- ^ Reader options +readHtml :: PandocMonad m + => ReaderOptions -- ^ Reader options -> String -- ^ String to parse (assumes @'\n'@ line endings) - -> Either PandocError Pandoc -readHtml opts inp = - mapLeft (ParseFailure . getError) . flip runReader def $ - runParserT parseDoc - (HTMLState def{ stateOptions = opts } [] Nothing Set.empty M.empty) - "source" tags - where tags = stripPrefixes . canonicalizeTags $ - parseTagsOptions parseOptions{ optTagPosition = True } inp - parseDoc = do - blocks <- (fixPlains False) . mconcat <$> manyTill block eof - meta <- stateMeta . parserState <$> getState - bs' <- replaceNotes (B.toList blocks) - return $ Pandoc meta bs' - getError (errorMessages -> ms) = case ms of - [] -> "" - (m:_) -> messageString m - -replaceNotes :: [Block] -> TagParser [Block] + -> m Pandoc +readHtml opts inp = do + let tags = stripPrefixes . canonicalizeTags $ + parseTagsOptions parseOptions{ optTagPosition = True } inp + parseDoc = do + blocks <- (fixPlains False) . mconcat <$> manyTill block eof + meta <- stateMeta . parserState <$> getState + bs' <- replaceNotes (B.toList blocks) + return $ Pandoc meta bs' + getError (errorMessages -> ms) = case ms of + [] -> "" + (m:_) -> messageString m + result <- flip runReaderT def $ + runParserT parseDoc + (HTMLState def{ stateOptions = opts } [] Nothing Set.empty M.empty) + "source" tags + case result of + Right doc -> return doc + Left err -> throwError $ PandocParseError $ getError err + + where + +replaceNotes :: PandocMonad m => [Block] -> TagParser m [Block] replaceNotes = walkM replaceNotes' -replaceNotes' :: Inline -> TagParser Inline +replaceNotes' :: PandocMonad m => Inline -> TagParser m Inline replaceNotes' (RawInline (Format "noteref") ref) = maybe (Str "") (Note . B.toList) . lookup ref <$> getNotes where getNotes = noteTable <$> getState @@ -113,20 +121,20 @@ data HTMLLocal = HTMLLocal { quoteContext :: QuoteContext , inPlain :: Bool -- ^ Set if in pPlain } -setInChapter :: HTMLParser s a -> HTMLParser s a +setInChapter :: PandocMonad m => HTMLParser m s a -> HTMLParser m s a setInChapter = local (\s -> s {inChapter = True}) -setInPlain :: HTMLParser s a -> HTMLParser s a +setInPlain :: PandocMonad m => HTMLParser m s a -> HTMLParser m s a setInPlain = local (\s -> s {inPlain = True}) -type HTMLParser s = ParserT s HTMLState (Reader HTMLLocal) +type HTMLParser m s = ParserT s HTMLState (ReaderT HTMLLocal m) -type TagParser = HTMLParser [Tag String] +type TagParser m = HTMLParser m [Tag String] -pBody :: TagParser Blocks +pBody :: PandocMonad m => TagParser m Blocks pBody = pInTags "body" block -pHead :: TagParser Blocks +pHead :: PandocMonad m => TagParser m Blocks pHead = pInTags "head" $ pTitle <|> pMetaTag <|> pBaseTag <|> (mempty <$ pAnyTag) where pTitle = pInTags "title" inline >>= setTitle . trimInlines setTitle t = mempty <$ (updateState $ B.setMeta "title" t) @@ -149,7 +157,7 @@ pHead = pInTags "head" $ pTitle <|> pMetaTag <|> pBaseTag <|> (mempty <$ pAnyTag parseURIReference $ fromAttrib "href" bt } return mempty -block :: TagParser Blocks +block :: PandocMonad m => TagParser m Blocks block = do tr <- getOption readerTrace pos <- getPosition @@ -176,13 +184,16 @@ block = do (take 60 $ show $ B.toList res)) (return ()) return res -namespaces :: [(String, TagParser Inlines)] +namespaces :: PandocMonad m => [(String, TagParser m Inlines)] namespaces = [(mathMLNamespace, pMath True)] mathMLNamespace :: String mathMLNamespace = "http://www.w3.org/1998/Math/MathML" -eSwitch :: Monoid a => (Inlines -> a) -> TagParser a -> TagParser a +eSwitch :: (PandocMonad m, Monoid a) + => (Inlines -> a) + -> TagParser m a + -> TagParser m a eSwitch constructor parser = try $ do guardEnabled Ext_epub_html_exts pSatisfy (~== TagOpen "switch" []) @@ -195,7 +206,7 @@ eSwitch constructor parser = try $ do pSatisfy (~== TagClose "switch") return $ maybe fallback constructor cases -eCase :: TagParser (Maybe Inlines) +eCase :: PandocMonad m => TagParser m (Maybe Inlines) eCase = do skipMany pBlank TagOpen _ attr <- lookAhead $ pSatisfy $ (~== TagOpen "case" []) @@ -203,7 +214,7 @@ eCase = do Just p -> Just <$> (pInTags "case" (skipMany pBlank *> p <* skipMany pBlank)) Nothing -> Nothing <$ manyTill pAnyTag (pSatisfy (~== TagClose "case")) -eFootnote :: TagParser () +eFootnote :: PandocMonad m => TagParser m () eFootnote = try $ do let notes = ["footnote", "rearnote"] guardEnabled Ext_epub_html_exts @@ -213,10 +224,10 @@ eFootnote = try $ do content <- pInTags tag block addNote ident content -addNote :: String -> Blocks -> TagParser () +addNote :: PandocMonad m => String -> Blocks -> TagParser m () addNote uid cont = updateState (\s -> s {noteTable = (uid, cont) : (noteTable s)}) -eNoteref :: TagParser Inlines +eNoteref :: PandocMonad m => TagParser m Inlines eNoteref = try $ do guardEnabled Ext_epub_html_exts TagOpen tag attr <- lookAhead $ pAnyTag @@ -227,17 +238,17 @@ eNoteref = try $ do return $ B.rawInline "noteref" ident -- Strip TOC if there is one, better to generate again -eTOC :: TagParser () +eTOC :: PandocMonad m => TagParser m () eTOC = try $ do guardEnabled Ext_epub_html_exts (TagOpen tag attr) <- lookAhead $ pAnyTag guard (maybe False (== "toc") (lookup "type" attr)) void (pInTags tag block) -pList :: TagParser Blocks +pList :: PandocMonad m => TagParser m Blocks pList = pBulletList <|> pOrderedList <|> pDefinitionList -pBulletList :: TagParser Blocks +pBulletList :: PandocMonad m => TagParser m Blocks pBulletList = try $ do pSatisfy (~== TagOpen "ul" []) let nonItem = pSatisfy (\t -> @@ -249,7 +260,7 @@ pBulletList = try $ do items <- manyTill (pListItem nonItem) (pCloses "ul") return $ B.bulletList $ map (fixPlains True) items -pListItem :: TagParser a -> TagParser Blocks +pListItem :: PandocMonad m => TagParser m a -> TagParser m Blocks pListItem nonItem = do TagOpen _ attr <- lookAhead $ pSatisfy (~== TagOpen "li" []) let liDiv = maybe mempty (\x -> B.divWith (x, [], []) mempty) (lookup "id" attr) @@ -271,7 +282,7 @@ parseTypeAttr "A" = UpperAlpha parseTypeAttr "1" = Decimal parseTypeAttr _ = DefaultStyle -pOrderedList :: TagParser Blocks +pOrderedList :: PandocMonad m => TagParser m Blocks pOrderedList = try $ do TagOpen _ attribs <- pSatisfy (~== TagOpen "ol" []) let (start, style) = (sta', sty') @@ -302,13 +313,13 @@ pOrderedList = try $ do items <- manyTill (pListItem nonItem) (pCloses "ol") return $ B.orderedListWith (start, style, DefaultDelim) $ map (fixPlains True) items -pDefinitionList :: TagParser Blocks +pDefinitionList :: PandocMonad m => TagParser m Blocks pDefinitionList = try $ do pSatisfy (~== TagOpen "dl" []) items <- manyTill pDefListItem (pCloses "dl") return $ B.definitionList items -pDefListItem :: TagParser (Inlines, [Blocks]) +pDefListItem :: PandocMonad m => TagParser m (Inlines, [Blocks]) pDefListItem = try $ do let nonItem = pSatisfy (\t -> not (t ~== TagOpen "dt" []) && not (t ~== TagOpen "dd" []) && not (t ~== TagClose "dl")) @@ -334,7 +345,7 @@ fixPlains inList bs = if any isParaish bs' plainToPara x = x bs' = B.toList bs -pRawTag :: TagParser String +pRawTag :: PandocMonad m => TagParser m String pRawTag = do tag <- pAnyTag let ignorable x = x `elem` ["html","head","body","!DOCTYPE","?xml"] @@ -342,7 +353,7 @@ pRawTag = do then return [] else return $ renderTags' [tag] -pDiv :: TagParser Blocks +pDiv :: PandocMonad m => TagParser m Blocks pDiv = try $ do guardEnabled Ext_native_divs let isDivLike "div" = True @@ -356,7 +367,7 @@ pDiv = try $ do else classes return $ B.divWith (ident, classes', kvs) contents -pRawHtmlBlock :: TagParser Blocks +pRawHtmlBlock :: PandocMonad m => TagParser m Blocks pRawHtmlBlock = do raw <- pHtmlBlock "script" <|> pHtmlBlock "style" <|> pRawTag parseRaw <- getOption readerParseRaw @@ -364,21 +375,21 @@ pRawHtmlBlock = do then return $ B.rawBlock "html" raw else return mempty -pHtmlBlock :: String -> TagParser String +pHtmlBlock :: PandocMonad m => String -> TagParser m String pHtmlBlock t = try $ do open <- pSatisfy (~== TagOpen t []) contents <- manyTill pAnyTag (pSatisfy (~== TagClose t)) return $ renderTags' $ [open] ++ contents ++ [TagClose t] -- Sets chapter context -eSection :: TagParser Blocks +eSection :: PandocMonad m => TagParser m Blocks eSection = try $ do let matchChapter as = maybe False (isInfixOf "chapter") (lookup "type" as) let sectTag = tagOpen (`elem` sectioningContent) matchChapter TagOpen tag _ <- lookAhead $ pSatisfy sectTag setInChapter (pInTags tag block) -headerLevel :: String -> TagParser Int +headerLevel :: PandocMonad m => String -> TagParser m Int headerLevel tagtype = do let level = read (drop 1 tagtype) (try $ do @@ -388,7 +399,7 @@ headerLevel tagtype = do <|> return level -eTitlePage :: TagParser () +eTitlePage :: PandocMonad m => TagParser m () eTitlePage = try $ do let isTitlePage as = maybe False (isInfixOf "titlepage") (lookup "type" as) let groupTag = tagOpen (\x -> x `elem` groupingContent || x == "section") @@ -396,7 +407,7 @@ eTitlePage = try $ do TagOpen tag _ <- lookAhead $ pSatisfy groupTag () <$ pInTags tag block -pHeader :: TagParser Blocks +pHeader :: PandocMonad m => TagParser m Blocks pHeader = try $ do TagOpen tagtype attr <- pSatisfy $ tagOpen (`elem` ["h1","h2","h3","h4","h5","h6"]) @@ -412,12 +423,12 @@ pHeader = try $ do then mempty -- skip a representation of the title in the body else B.headerWith attr' level contents -pHrule :: TagParser Blocks +pHrule :: PandocMonad m => TagParser m Blocks pHrule = do pSelfClosing (=="hr") (const True) return B.horizontalRule -pTable :: TagParser Blocks +pTable :: PandocMonad m => TagParser m Blocks pTable = try $ do TagOpen _ _ <- pSatisfy (~== TagOpen "table" []) skipMany pBlank @@ -456,7 +467,7 @@ pTable = try $ do else widths' return $ B.table caption (zip aligns widths) head' rows -pCol :: TagParser Double +pCol :: PandocMonad m => TagParser m Double pCol = try $ do TagOpen _ attribs <- pSatisfy (~== TagOpen "col" []) skipMany pBlank @@ -472,7 +483,7 @@ pCol = try $ do fromMaybe 0.0 $ safeRead ('0':'.':init x) _ -> 0.0 -pColgroup :: TagParser [Double] +pColgroup :: PandocMonad m => TagParser m [Double] pColgroup = try $ do pSatisfy (~== TagOpen "colgroup" []) skipMany pBlank @@ -485,31 +496,31 @@ noColOrRowSpans t = isNullOrOne "colspan" && isNullOrOne "rowspan" "1" -> True _ -> False -pCell :: String -> TagParser [Blocks] +pCell :: PandocMonad m => String -> TagParser m [Blocks] pCell celltype = try $ do skipMany pBlank res <- pInTags' celltype noColOrRowSpans block skipMany pBlank return [res] -pBlockQuote :: TagParser Blocks +pBlockQuote :: PandocMonad m => TagParser m Blocks pBlockQuote = do contents <- pInTags "blockquote" block return $ B.blockQuote $ fixPlains False contents -pPlain :: TagParser Blocks +pPlain :: PandocMonad m => TagParser m Blocks pPlain = do contents <- setInPlain $ trimInlines . mconcat <$> many1 inline if B.isNull contents then return mempty else return $ B.plain contents -pPara :: TagParser Blocks +pPara :: PandocMonad m => TagParser m Blocks pPara = do contents <- trimInlines <$> pInTags "p" inline return $ B.para contents -pCodeBlock :: TagParser Blocks +pCodeBlock :: PandocMonad m => TagParser m Blocks pCodeBlock = try $ do TagOpen _ attr <- pSatisfy (~== TagOpen "pre" []) contents <- manyTill pAnyTag (pCloses "pre" <|> eof) @@ -529,7 +540,7 @@ tagToString (TagText s) = s tagToString (TagOpen "br" _) = "\n" tagToString _ = "" -inline :: TagParser Inlines +inline :: PandocMonad m => TagParser m Inlines inline = choice [ eNoteref , eSwitch id inline @@ -549,30 +560,31 @@ inline = choice , pRawHtmlInline ] -pLocation :: TagParser () +pLocation :: PandocMonad m => TagParser m () pLocation = do (TagPosition r c) <- pSat isTagPosition setPosition $ newPos "input" r c -pSat :: (Tag String -> Bool) -> TagParser (Tag String) +pSat :: PandocMonad m => (Tag String -> Bool) -> TagParser m (Tag String) pSat f = do pos <- getPosition token show (const pos) (\x -> if f x then Just x else Nothing) -pSatisfy :: (Tag String -> Bool) -> TagParser (Tag String) +pSatisfy :: PandocMonad m => (Tag String -> Bool) -> TagParser m (Tag String) pSatisfy f = try $ optional pLocation >> pSat f -pAnyTag :: TagParser (Tag String) +pAnyTag :: PandocMonad m => TagParser m (Tag String) pAnyTag = pSatisfy (const True) -pSelfClosing :: (String -> Bool) -> ([Attribute String] -> Bool) - -> TagParser (Tag String) +pSelfClosing :: PandocMonad m + => (String -> Bool) -> ([Attribute String] -> Bool) + -> TagParser m (Tag String) pSelfClosing f g = do open <- pSatisfy (tagOpen f g) optional $ pSatisfy (tagClose f) return open -pQ :: TagParser Inlines +pQ :: PandocMonad m => TagParser m Inlines pQ = do context <- asks quoteContext let quoteType = case context of @@ -587,19 +599,19 @@ pQ = do withQuoteContext innerQuoteContext $ pInlinesInTags "q" constructor -pEmph :: TagParser Inlines +pEmph :: PandocMonad m => TagParser m Inlines pEmph = pInlinesInTags "em" B.emph <|> pInlinesInTags "i" B.emph -pStrong :: TagParser Inlines +pStrong :: PandocMonad m => TagParser m Inlines pStrong = pInlinesInTags "strong" B.strong <|> pInlinesInTags "b" B.strong -pSuperscript :: TagParser Inlines +pSuperscript :: PandocMonad m => TagParser m Inlines pSuperscript = pInlinesInTags "sup" B.superscript -pSubscript :: TagParser Inlines +pSubscript :: PandocMonad m => TagParser m Inlines pSubscript = pInlinesInTags "sub" B.subscript -pStrikeout :: TagParser Inlines +pStrikeout :: PandocMonad m => TagParser m Inlines pStrikeout = do pInlinesInTags "s" B.strikeout <|> pInlinesInTags "strike" B.strikeout <|> @@ -608,7 +620,7 @@ pStrikeout = do contents <- mconcat <$> manyTill inline (pCloses "span") return $ B.strikeout contents) -pLineBreak :: TagParser Inlines +pLineBreak :: PandocMonad m => TagParser m Inlines pLineBreak = do pSelfClosing (=="br") (const True) return B.linebreak @@ -619,7 +631,7 @@ maybeFromAttrib :: String -> Tag String -> Maybe String maybeFromAttrib name (TagOpen _ attrs) = lookup name attrs maybeFromAttrib _ _ = Nothing -pLink :: TagParser Inlines +pLink :: PandocMonad m => TagParser m Inlines pLink = try $ do tag <- pSatisfy $ tagOpenLit "a" (const True) let title = fromAttrib "title" tag @@ -639,7 +651,7 @@ pLink = try $ do _ -> url' return $ B.linkWith (uid, cls, []) (escapeURI url) title lab -pImage :: TagParser Inlines +pImage :: PandocMonad m => TagParser m Inlines pImage = do tag <- pSelfClosing (=="img") (isJust . lookup "src") mbBaseHref <- baseHref <$> getState @@ -657,13 +669,13 @@ pImage = do let kvs = concat $ map getAtt ["width", "height", "sizes", "srcset"] return $ B.imageWith (uid, cls, kvs) (escapeURI url) title (B.text alt) -pCode :: TagParser Inlines +pCode :: PandocMonad m => TagParser m Inlines pCode = try $ do (TagOpen open attr) <- pSatisfy $ tagOpen (`elem` ["code","tt"]) (const True) result <- manyTill pAnyTag (pCloses open) return $ B.codeWith (mkAttr attr) $ intercalate " " $ lines $ innerText result -pSpan :: TagParser Inlines +pSpan :: PandocMonad m => TagParser m Inlines pSpan = try $ do guardEnabled Ext_native_spans TagOpen _ attr <- lookAhead $ pSatisfy $ tagOpen (=="span") (const True) @@ -674,7 +686,7 @@ pSpan = try $ do let tag = if isSmallCaps then B.smallcaps else B.spanWith (mkAttr attr) return $ tag contents -pRawHtmlInline :: TagParser Inlines +pRawHtmlInline :: PandocMonad m => TagParser m Inlines pRawHtmlInline = do inplain <- asks inPlain result <- pSatisfy (tagComment (const True)) @@ -689,7 +701,7 @@ pRawHtmlInline = do mathMLToTeXMath :: String -> Either String String mathMLToTeXMath s = writeTeX <$> readMathML s -pMath :: Bool -> TagParser Inlines +pMath :: PandocMonad m => Bool -> TagParser m Inlines pMath inCase = try $ do open@(TagOpen _ attr) <- pSatisfy $ tagOpen (=="math") (const True) -- we'll assume math tags are MathML unless specially marked @@ -705,22 +717,25 @@ pMath inCase = try $ do Just "block" -> B.displayMath x _ -> B.math x -pInlinesInTags :: String -> (Inlines -> Inlines) - -> TagParser Inlines +pInlinesInTags :: PandocMonad m => String -> (Inlines -> Inlines) + -> TagParser m Inlines pInlinesInTags tagtype f = extractSpaces f <$> pInTags tagtype inline -pInTags :: (Monoid a) => String -> TagParser a -> TagParser a +pInTags :: (PandocMonad m, Monoid a) => String -> TagParser m a -> TagParser m a pInTags tagtype parser = pInTags' tagtype (const True) parser -pInTags' :: (Monoid a) => String -> (Tag String -> Bool) -> TagParser a - -> TagParser a +pInTags' :: (PandocMonad m, Monoid a) + => String + -> (Tag String -> Bool) + -> TagParser m a + -> TagParser m a pInTags' tagtype tagtest parser = try $ do pSatisfy (\t -> t ~== TagOpen tagtype [] && tagtest t) mconcat <$> manyTill parser (pCloses tagtype <|> eof) -- parses p, preceeded by an optional opening tag -- and followed by an optional closing tags -pOptInTag :: String -> TagParser a -> TagParser a +pOptInTag :: PandocMonad m => String -> TagParser m a -> TagParser m a pOptInTag tagtype p = try $ do skipMany pBlank optional $ pSatisfy (~== TagOpen tagtype []) @@ -731,7 +746,7 @@ pOptInTag tagtype p = try $ do skipMany pBlank return x -pCloses :: String -> TagParser () +pCloses :: PandocMonad m => String -> TagParser m () pCloses tagtype = try $ do t <- lookAhead $ pSatisfy $ \tag -> isTagClose tag || isTagOpen tag case t of @@ -744,23 +759,25 @@ pCloses tagtype = try $ do (TagClose "table") | tagtype == "tr" -> return () _ -> mzero -pTagText :: TagParser Inlines +pTagText :: PandocMonad m => TagParser m Inlines pTagText = try $ do (TagText str) <- pSatisfy isTagText st <- getState qu <- ask - case flip runReader qu $ runParserT (many pTagContents) st "text" str of - Left _ -> fail $ "Could not parse `" ++ str ++ "'" + parsed <- lift $ lift $ + flip runReaderT qu $ runParserT (many pTagContents) st "text" str + case parsed of + Left _ -> throwError $ PandocParseError $ "Could not parse `" ++ str ++ "'" Right result -> return $ mconcat result -pBlank :: TagParser () +pBlank :: PandocMonad m => TagParser m () pBlank = try $ do (TagText str) <- pSatisfy isTagText guard $ all isSpace str -type InlinesParser = HTMLParser String +type InlinesParser m = HTMLParser m String -pTagContents :: InlinesParser Inlines +pTagContents :: PandocMonad m => InlinesParser m Inlines pTagContents = B.displayMath <$> mathDisplay <|> B.math <$> mathInline @@ -770,7 +787,7 @@ pTagContents = <|> pSymbol <|> pBad -pStr :: InlinesParser Inlines +pStr :: PandocMonad m => InlinesParser m Inlines pStr = do result <- many1 $ satisfy $ \c -> not (isSpace c) && not (isSpecial c) && not (isBad c) @@ -789,13 +806,13 @@ isSpecial '\8220' = True isSpecial '\8221' = True isSpecial _ = False -pSymbol :: InlinesParser Inlines +pSymbol :: PandocMonad m => InlinesParser m Inlines pSymbol = satisfy isSpecial >>= return . B.str . (:[]) isBad :: Char -> Bool isBad c = c >= '\128' && c <= '\159' -- not allowed in HTML -pBad :: InlinesParser Inlines +pBad :: PandocMonad m => InlinesParser m Inlines pBad = do c <- satisfy isBad let c' = case c of @@ -829,7 +846,7 @@ pBad = do _ -> '?' return $ B.str [c'] -pSpace :: InlinesParser Inlines +pSpace :: PandocMonad m => InlinesParser m Inlines pSpace = many1 (satisfy isSpace) >>= \xs -> if '\n' `elem` xs then return B.softbreak @@ -1070,7 +1087,7 @@ instance HasHeaderMap HTMLState where -- This signature should be more general -- MonadReader HTMLLocal m => HasQuoteContext st m -instance HasQuoteContext st (Reader HTMLLocal) where +instance PandocMonad m => HasQuoteContext st (ReaderT HTMLLocal m) where getQuoteContext = asks quoteContext withQuoteContext q = local (\s -> s{quoteContext = q}) diff --git a/src/Text/Pandoc/Readers/Haddock.hs b/src/Text/Pandoc/Readers/Haddock.hs index 12953bb72..4d33f657c 100644 --- a/src/Text/Pandoc/Readers/Haddock.hs +++ b/src/Text/Pandoc/Readers/Haddock.hs @@ -25,14 +25,23 @@ import Text.Pandoc.Options import Documentation.Haddock.Parser import Documentation.Haddock.Types import Debug.Trace (trace) +import Control.Monad.Except (throwError) +import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) -import Text.Pandoc.Error -- | Parse Haddock markup and return a 'Pandoc' document. -readHaddock :: ReaderOptions -- ^ Reader options - -> String -- ^ String to parse - -> Either PandocError Pandoc -readHaddock opts = +readHaddock :: PandocMonad m + => ReaderOptions + -> String + -> m Pandoc +readHaddock opts s = case readHaddockEither opts s of + Right result -> return result + Left e -> throwError e + +readHaddockEither :: ReaderOptions -- ^ Reader options + -> String -- ^ String to parse + -> Either PandocExecutionError Pandoc +readHaddockEither opts = #if MIN_VERSION_haddock_library(1,2,0) Right . B.doc . docHToBlocks . trace' . _doc . parseParas #else diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index edcf35e51..2506c17be 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -56,14 +56,21 @@ import qualified Control.Exception as E import Text.Pandoc.Highlighting (fromListingsLanguage) import Text.Pandoc.ImageSize (numUnit, showFl) import Text.Pandoc.Error +import Control.Monad.Except (throwError) +import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..), PandocPure) -- | Parse LaTeX from string and return 'Pandoc' document. -readLaTeX :: ReaderOptions -- ^ Reader options +readLaTeX :: PandocMonad m + => ReaderOptions -- ^ Reader options -> String -- ^ String to parse (assumes @'\n'@ line endings) - -> Either PandocError Pandoc -readLaTeX opts = readWith parseLaTeX def{ stateOptions = opts } - -parseLaTeX :: LP Pandoc + -> m Pandoc +readLaTeX opts ltx = do + parsed <- readWithM parseLaTeX def{ stateOptions = opts } ltx + case parsed of + Right result -> return result + Left _ -> throwError $ PandocParseError "parsing error" + +parseLaTeX :: PandocMonad m => LP m Pandoc parseLaTeX = do bs <- blocks eof @@ -72,9 +79,9 @@ parseLaTeX = do let (Pandoc _ bs') = doc bs return $ Pandoc meta bs' -type LP = Parser String ParserState +type LP m = ParserT String ParserState m -anyControlSeq :: LP String +anyControlSeq :: PandocMonad m => LP m String anyControlSeq = do char '\\' next <- option '\n' anyChar @@ -83,7 +90,7 @@ anyControlSeq = do c | isLetter c -> (c:) <$> (many letter <* optional sp) | otherwise -> return [c] -controlSeq :: String -> LP String +controlSeq :: PandocMonad m => String -> LP m String controlSeq name = try $ do char '\\' case name of @@ -92,26 +99,26 @@ controlSeq name = try $ do cs -> string cs <* notFollowedBy letter <* optional sp return name -dimenarg :: LP String +dimenarg :: PandocMonad m => LP m String dimenarg = try $ do ch <- option "" $ string "=" num <- many1 digit dim <- oneOfStrings ["pt","pc","in","bp","cm","mm","dd","cc","sp"] return $ ch ++ num ++ dim -sp :: LP () +sp :: PandocMonad m => LP m () sp = whitespace <|> endline -whitespace :: LP () +whitespace :: PandocMonad m => LP m () whitespace = skipMany1 $ satisfy (\c -> c == ' ' || c == '\t') -endline :: LP () +endline :: PandocMonad m => LP m () endline = try (newline >> lookAhead anyChar >> notFollowedBy blankline) isLowerHex :: Char -> Bool isLowerHex x = x >= '0' && x <= '9' || x >= 'a' && x <= 'f' -tildeEscape :: LP Char +tildeEscape :: PandocMonad m => LP m Char tildeEscape = try $ do string "^^" c <- satisfy (\x -> x >= '\0' && x <= '\128') @@ -124,29 +131,29 @@ tildeEscape = try $ do | otherwise -> return $ chr (x + 64) else return $ chr $ read ('0':'x':c:d) -comment :: LP () +comment :: PandocMonad m => LP m () comment = do char '%' skipMany (satisfy (/='\n')) optional newline return () -bgroup :: LP () +bgroup :: PandocMonad m => LP m () bgroup = try $ do skipMany (spaceChar <|> try (newline <* notFollowedBy blankline)) () <$ char '{' <|> () <$ controlSeq "bgroup" <|> () <$ controlSeq "begingroup" -egroup :: LP () +egroup :: PandocMonad m => LP m () egroup = () <$ char '}' <|> () <$ controlSeq "egroup" <|> () <$ controlSeq "endgroup" -grouped :: Monoid a => LP a -> LP a +grouped :: PandocMonad m => Monoid a => LP m a -> LP m a grouped parser = try $ bgroup *> (mconcat <$> manyTill parser egroup) -braced :: LP String +braced :: PandocMonad m => LP m String braced = bgroup *> (concat <$> manyTill ( many1 (satisfy (\c -> c /= '\\' && c /= '}' && c /= '{')) <|> try (string "\\}") @@ -156,16 +163,16 @@ braced = bgroup *> (concat <$> manyTill <|> count 1 anyChar ) egroup) -bracketed :: Monoid a => LP a -> LP a +bracketed :: PandocMonad m => Monoid a => LP m a -> LP m a bracketed parser = try $ char '[' *> (mconcat <$> manyTill parser (char ']')) -mathDisplay :: LP String -> LP Inlines +mathDisplay :: PandocMonad m => LP m String -> LP m Inlines mathDisplay p = displayMath <$> (try p >>= applyMacros' . trim) -mathInline :: LP String -> LP Inlines +mathInline :: PandocMonad m => LP m String -> LP m Inlines mathInline p = math <$> (try p >>= applyMacros') -mathChars :: LP String +mathChars :: PandocMonad m => LP m String mathChars = concat <$> many (escapedChar <|> (snd <$> withRaw braced) @@ -179,7 +186,7 @@ mathChars = isOrdChar '\\' = False isOrdChar _ = True -quoted' :: (Inlines -> Inlines) -> LP String -> LP () -> LP Inlines +quoted' :: PandocMonad m => (Inlines -> Inlines) -> LP m String -> LP m () -> LP m Inlines quoted' f starter ender = do startchs <- starter smart <- getOption readerSmart @@ -194,7 +201,7 @@ quoted' f starter ender = do _ -> startchs) else lit startchs -doubleQuote :: LP Inlines +doubleQuote :: PandocMonad m => LP m Inlines doubleQuote = do quoted' doubleQuoted (try $ string "``") (void $ try $ string "''") <|> quoted' doubleQuoted (string "“") (void $ char '”') @@ -202,7 +209,7 @@ doubleQuote = do <|> quoted' doubleQuoted (try $ string "\"`") (void $ try $ string "\"'") <|> quoted' doubleQuoted (string "\"") (void $ char '"') -singleQuote :: LP Inlines +singleQuote :: PandocMonad m => LP m Inlines singleQuote = do smart <- getOption readerSmart if smart @@ -210,7 +217,7 @@ singleQuote = do <|> quoted' singleQuoted (string "‘") (try $ char '’' >> notFollowedBy letter) else str <$> many1 (oneOf "`\'‘’") -inline :: LP Inlines +inline :: PandocMonad m => LP m Inlines inline = (mempty <$ comment) <|> (space <$ whitespace) <|> (softbreak <$ endline) @@ -235,10 +242,10 @@ inline = (mempty <$ comment) <|> (str . (:[]) <$> oneOf "#&~^'`\"[]") -- TODO print warning? -- <|> (str <$> count 1 (satisfy (\c -> c /= '\\' && c /='\n' && c /='}' && c /='{'))) -- eat random leftover characters -inlines :: LP Inlines +inlines :: PandocMonad m => LP m Inlines inlines = mconcat <$> many (notFollowedBy (char '}') *> inline) -inlineGroup :: LP Inlines +inlineGroup :: PandocMonad m => LP m Inlines inlineGroup = do ils <- grouped inline if isNull ils @@ -247,7 +254,7 @@ inlineGroup = do -- we need the span so we can detitlecase bibtex entries; -- we need to know when something is {C}apitalized -block :: LP Blocks +block :: PandocMonad m => LP m Blocks block = (mempty <$ comment) <|> (mempty <$ ((spaceChar <|> newline) *> spaces)) <|> environment @@ -258,10 +265,10 @@ block = (mempty <$ comment) <|> (mempty <$ char '&') -- loose & in table environment -blocks :: LP Blocks +blocks :: PandocMonad m => LP m Blocks blocks = mconcat <$> many block -getRawCommand :: String -> LP String +getRawCommand :: PandocMonad m => String -> LP m String getRawCommand name' = do rawargs <- withRaw (many (try (optional sp *> opt)) *> option "" (try (optional sp *> dimenarg)) *> @@ -273,7 +280,7 @@ lookupListDefault d = (fromMaybe d .) . lookupList where lookupList l m = msum $ map (`M.lookup` m) l -blockCommand :: LP Blocks +blockCommand :: PandocMonad m => LP m Blocks blockCommand = try $ do name <- anyControlSeq guard $ name /= "begin" && name /= "end" @@ -291,21 +298,21 @@ inBrackets :: Inlines -> Inlines inBrackets x = str "[" <> x <> str "]" -- eat an optional argument and one or more arguments in braces -ignoreInlines :: String -> (String, LP Inlines) +ignoreInlines :: PandocMonad m => String -> (String, LP m Inlines) ignoreInlines name = (name, doraw <|> (mempty <$ optargs)) where optargs = skipopts *> skipMany (try $ optional sp *> braced) contseq = '\\':name doraw = (rawInline "latex" . (contseq ++) . snd) <$> (getOption readerParseRaw >>= guard >> withRaw optargs) -ignoreBlocks :: String -> (String, LP Blocks) +ignoreBlocks :: PandocMonad m => String -> (String, LP m Blocks) ignoreBlocks name = (name, doraw <|> (mempty <$ optargs)) where optargs = skipopts *> skipMany (try $ optional sp *> braced) contseq = '\\':name doraw = (rawBlock "latex" . (contseq ++) . snd) <$> (getOption readerParseRaw >>= guard >> withRaw optargs) -blockCommands :: M.Map String (LP Blocks) +blockCommands :: PandocMonad m => M.Map String (LP m Blocks) blockCommands = M.fromList $ [ ("par", mempty <$ skipopts) , ("title", mempty <$ (skipopts *> @@ -370,14 +377,14 @@ blockCommands = M.fromList $ , "newpage" ] -addMeta :: ToMetaValue a => String -> a -> LP () +addMeta :: PandocMonad m => ToMetaValue a => String -> a -> LP m () addMeta field val = updateState $ \st -> st{ stateMeta = addMetaField field val $ stateMeta st } splitBibs :: String -> [Inlines] splitBibs = map (str . flip replaceExtension "bib" . trim) . splitBy (==',') -setCaption :: LP Blocks +setCaption :: PandocMonad m => LP m Blocks setCaption = do ils <- tok mblabel <- option Nothing $ @@ -389,10 +396,10 @@ setCaption = do updateState $ \st -> st{ stateCaption = Just ils' } return mempty -resetCaption :: LP () +resetCaption :: PandocMonad m => LP m () resetCaption = updateState $ \st -> st{ stateCaption = Nothing } -authors :: LP () +authors :: PandocMonad m => LP m () authors = try $ do char '{' let oneAuthor = mconcat <$> @@ -403,7 +410,7 @@ authors = try $ do char '}' addMeta "author" (map trimInlines auths) -section :: Attr -> Int -> LP Blocks +section :: PandocMonad m => Attr -> Int -> LP m Blocks section (ident, classes, kvs) lvl = do hasChapters <- stateHasChapters `fmap` getState let lvl' = if hasChapters then lvl + 1 else lvl @@ -413,7 +420,7 @@ section (ident, classes, kvs) lvl = do attr' <- registerHeader (lab, classes, kvs) contents return $ headerWith attr' lvl' contents -inlineCommand :: LP Inlines +inlineCommand :: PandocMonad m => LP m Inlines inlineCommand = try $ do name <- anyControlSeq guard $ name /= "begin" && name /= "end" @@ -435,14 +442,14 @@ inlineCommand = try $ do optional (try (string "{}"))) <|> raw -unlessParseRaw :: LP () +unlessParseRaw :: PandocMonad m => LP m () unlessParseRaw = getOption readerParseRaw >>= guard . not isBlockCommand :: String -> Bool -isBlockCommand s = s `M.member` blockCommands +isBlockCommand s = s `M.member` (blockCommands :: M.Map String (LP PandocPure Blocks)) -inlineEnvironments :: M.Map String (LP Inlines) +inlineEnvironments :: PandocMonad m => M.Map String (LP m Inlines) inlineEnvironments = M.fromList [ ("displaymath", mathEnv id Nothing "displaymath") , ("math", math <$> verbEnv "math") @@ -460,7 +467,7 @@ inlineEnvironments = M.fromList , ("alignat*", mathEnv id (Just "aligned") "alignat*") ] -inlineCommands :: M.Map String (LP Inlines) +inlineCommands :: PandocMonad m => M.Map String (LP m Inlines) inlineCommands = M.fromList $ [ ("emph", extractSpaces emph <$> tok) , ("textit", extractSpaces emph <$> tok) @@ -621,7 +628,7 @@ inlineCommands = M.fromList $ -- in which case they will appear as raw latex blocks: [ "index" ] -mkImage :: [(String, String)] -> String -> LP Inlines +mkImage :: PandocMonad m => [(String, String)] -> String -> LP m Inlines mkImage options src = do let replaceTextwidth (k,v) = case numUnit v of Just (num, "\\textwidth") -> (k, showFl (num * 100) ++ "%") @@ -645,7 +652,7 @@ unescapeURL ('\\':x:xs) | isEscapable x = x:unescapeURL xs unescapeURL (x:xs) = x:unescapeURL xs unescapeURL [] = "" -enquote :: LP Inlines +enquote :: PandocMonad m => LP m Inlines enquote = do skipopts context <- stateQuoteContext <$> getState @@ -653,18 +660,18 @@ enquote = do then singleQuoted <$> withQuoteContext InSingleQuote tok else doubleQuoted <$> withQuoteContext InDoubleQuote tok -doverb :: LP Inlines +doverb :: PandocMonad m => LP m Inlines doverb = do marker <- anyChar code <$> manyTill (satisfy (/='\n')) (char marker) -doLHSverb :: LP Inlines +doLHSverb :: PandocMonad m => LP m Inlines doLHSverb = codeWith ("",["haskell"],[]) <$> manyTill (satisfy (/='\n')) (char '|') -lit :: String -> LP Inlines +lit :: String -> LP m Inlines lit = pure . str -accent :: (Char -> String) -> Inlines -> LP Inlines +accent :: (Char -> String) -> Inlines -> LP m Inlines accent f ils = case toList ils of (Str (x:xs) : ys) -> return $ fromList (Str (f x ++ xs) : ys) @@ -870,53 +877,53 @@ breve 'U' = "Ŭ" breve 'u' = "ŭ" breve c = [c] -tok :: LP Inlines +tok :: PandocMonad m => LP m Inlines tok = try $ grouped inline <|> inlineCommand <|> str <$> count 1 inlineChar -opt :: LP Inlines +opt :: PandocMonad m => LP m Inlines opt = bracketed inline -rawopt :: LP String +rawopt :: PandocMonad m => LP m String rawopt = do contents <- bracketed (many1 (noneOf "[]") <|> try (string "\\]") <|> try (string "\\[") <|> rawopt) optional sp return $ "[" ++ contents ++ "]" -skipopts :: LP () +skipopts :: PandocMonad m => LP m () skipopts = skipMany rawopt -- opts in angle brackets are used in beamer -rawangle :: LP () +rawangle :: PandocMonad m => LP m () rawangle = try $ do char '<' skipMany (noneOf ">") char '>' return () -skipangles :: LP () +skipangles :: PandocMonad m => LP m () skipangles = skipMany rawangle -inlineText :: LP Inlines +inlineText :: PandocMonad m => LP m Inlines inlineText = str <$> many1 inlineChar -inlineChar :: LP Char +inlineChar :: PandocMonad m => LP m Char inlineChar = noneOf "\\$%&~#{}^'`\"‘’“”-[] \t\n" -environment :: LP Blocks +environment :: PandocMonad m => LP m Blocks environment = do controlSeq "begin" name <- braced M.findWithDefault mzero name environments <|> rawEnv name -inlineEnvironment :: LP Inlines +inlineEnvironment :: PandocMonad m => LP m Inlines inlineEnvironment = try $ do controlSeq "begin" name <- braced M.findWithDefault mzero name inlineEnvironments -rawEnv :: String -> LP Blocks +rawEnv :: PandocMonad m => String -> LP m Blocks rawEnv name = do parseRaw <- getOption readerParseRaw rawOptions <- mconcat <$> many rawopt @@ -1045,7 +1052,7 @@ readFileFromDirs (d:ds) f = ---- -keyval :: LP (String, String) +keyval :: PandocMonad m => LP m (String, String) keyval = try $ do key <- many1 alphaNum val <- option "" $ char '=' >> many1 (alphaNum <|> char '.' <|> char '\\') @@ -1055,25 +1062,25 @@ keyval = try $ do return (key, val) -keyvals :: LP [(String, String)] +keyvals :: PandocMonad m => LP m [(String, String)] keyvals = try $ char '[' *> manyTill keyval (char ']') -alltt :: String -> LP Blocks +alltt :: PandocMonad m => String -> LP m Blocks alltt t = walk strToCode <$> parseFromString blocks (substitute " " "\\ " $ substitute "%" "\\%" $ intercalate "\\\\\n" $ lines t) where strToCode (Str s) = Code nullAttr s strToCode x = x -rawLaTeXBlock :: LP String +rawLaTeXBlock :: PandocMonad m => LP m String rawLaTeXBlock = snd <$> try (withRaw (environment <|> blockCommand)) -rawLaTeXInline :: LP Inline +rawLaTeXInline :: PandocMonad m => LP m Inline rawLaTeXInline = do raw <- (snd <$> withRaw inlineCommand) <|> (snd <$> withRaw blockCommand) RawInline "latex" <$> applyMacros' raw -addImageCaption :: Blocks -> LP Blocks +addImageCaption :: PandocMonad m => Blocks -> LP m Blocks addImageCaption = walkM go where go (Image attr alt (src,tit)) = do mbcapt <- stateCaption <$> getState @@ -1082,7 +1089,7 @@ addImageCaption = walkM go Nothing -> Image attr alt (src,tit) go x = return x -addTableCaption :: Blocks -> LP Blocks +addTableCaption :: PandocMonad m => Blocks -> LP m Blocks addTableCaption = walkM go where go (Table c als ws hs rs) = do mbcapt <- stateCaption <$> getState @@ -1091,7 +1098,7 @@ addTableCaption = walkM go Nothing -> Table c als ws hs rs go x = return x -environments :: M.Map String (LP Blocks) +environments :: PandocMonad m => M.Map String (LP m Blocks) environments = M.fromList [ ("document", env "document" blocks <* skipMany anyChar) , ("abstract", mempty <$ (env "abstract" blocks >>= addMeta "abstract")) @@ -1159,7 +1166,7 @@ environments = M.fromList , ("alignat*", mathEnv para (Just "aligned") "alignat*") ] -letterContents :: LP Blocks +letterContents :: PandocMonad m => LP m Blocks letterContents = do bs <- blocks st <- getState @@ -1170,7 +1177,7 @@ letterContents = do _ -> mempty return $ addr <> bs -- sig added by \closing -closing :: LP Blocks +closing :: PandocMonad m => LP m Blocks closing = do contents <- tok st <- getState @@ -1184,17 +1191,17 @@ closing = do _ -> mempty return $ para (trimInlines contents) <> sigs -item :: LP Blocks +item :: PandocMonad m => LP m Blocks item = blocks *> controlSeq "item" *> skipopts *> blocks -looseItem :: LP Blocks +looseItem :: PandocMonad m => LP m Blocks looseItem = do ctx <- stateParserContext `fmap` getState if ctx == ListItemState then mzero else return mempty -descItem :: LP (Inlines, [Blocks]) +descItem :: PandocMonad m => LP m (Inlines, [Blocks]) descItem = do blocks -- skip blocks before item controlSeq "item" @@ -1203,12 +1210,12 @@ descItem = do bs <- blocks return (ils, [bs]) -env :: String -> LP a -> LP a +env :: PandocMonad m => String -> LP m a -> LP m a env name p = p <* (try (controlSeq "end" *> braced >>= guard . (== name)) ("\\end{" ++ name ++ "}")) -listenv :: String -> LP a -> LP a +listenv :: PandocMonad m => String -> LP m a -> LP m a listenv name p = try $ do oldCtx <- stateParserContext `fmap` getState updateState $ \st -> st{ stateParserContext = ListItemState } @@ -1216,14 +1223,14 @@ listenv name p = try $ do updateState $ \st -> st{ stateParserContext = oldCtx } return res -mathEnv :: (Inlines -> a) -> Maybe String -> String -> LP a +mathEnv :: PandocMonad m => (Inlines -> a) -> Maybe String -> String -> LP m a mathEnv f innerEnv name = f <$> mathDisplay (inner <$> verbEnv name) where inner x = case innerEnv of Nothing -> x Just y -> "\\begin{" ++ y ++ "}\n" ++ x ++ "\\end{" ++ y ++ "}" -verbEnv :: String -> LP String +verbEnv :: PandocMonad m => String -> LP m String verbEnv name = do skipopts optional blankline @@ -1231,7 +1238,7 @@ verbEnv name = do res <- manyTill anyChar endEnv return $ stripTrailingNewlines res -fancyverbEnv :: String -> LP Blocks +fancyverbEnv :: PandocMonad m => String -> LP m Blocks fancyverbEnv name = do options <- option [] keyvals let kvs = [ (if k == "firstnumber" @@ -1242,7 +1249,7 @@ fancyverbEnv name = do let attr = ("",classes,kvs) codeBlockWith attr <$> verbEnv name -orderedList' :: LP Blocks +orderedList' :: PandocMonad m => LP m Blocks orderedList' = do optional sp (_, style, delim) <- option (1, DefaultStyle, DefaultDelim) $ @@ -1259,14 +1266,14 @@ orderedList' = do bs <- listenv "enumerate" (many item) return $ orderedListWith (start, style, delim) bs -paragraph :: LP Blocks +paragraph :: PandocMonad m => LP m Blocks paragraph = do x <- trimInlines . mconcat <$> many1 inline if x == mempty then return mempty else return $ para x -preamble :: LP Blocks +preamble :: PandocMonad m => LP m Blocks preamble = mempty <$> manyTill preambleBlock beginDoc where beginDoc = lookAhead $ try $ controlSeq "begin" *> string "{document}" preambleBlock = void comment @@ -1292,7 +1299,7 @@ addSuffix s ks@(_:_) = in init ks ++ [k {citationSuffix = citationSuffix k ++ s}] addSuffix _ _ = [] -simpleCiteArgs :: LP [Citation] +simpleCiteArgs :: PandocMonad m => LP m [Citation] simpleCiteArgs = try $ do first <- optionMaybe $ toList <$> opt second <- optionMaybe $ toList <$> opt @@ -1312,7 +1319,7 @@ simpleCiteArgs = try $ do } return $ addPrefix pre $ addSuffix suf $ map conv keys -citationLabel :: LP String +citationLabel :: PandocMonad m => LP m String citationLabel = optional sp *> (many1 (satisfy isBibtexKeyChar) <* optional sp @@ -1320,7 +1327,7 @@ citationLabel = optional sp *> <* optional sp) where isBibtexKeyChar c = isAlphaNum c || c `elem` (".:;?!`'()/*@_+=-[]" :: String) -cites :: CitationMode -> Bool -> LP [Citation] +cites :: PandocMonad m => CitationMode -> Bool -> LP m [Citation] cites mode multi = try $ do cits <- if multi then many1 simpleCiteArgs @@ -1332,12 +1339,12 @@ cites mode multi = try $ do [] -> [] _ -> map (\a -> a {citationMode = mode}) cs -citation :: String -> CitationMode -> Bool -> LP Inlines +citation :: PandocMonad m => String -> CitationMode -> Bool -> LP m Inlines citation name mode multi = do (c,raw) <- withRaw $ cites mode multi return $ cite c (rawInline "latex" $ "\\" ++ name ++ raw) -complexNatbibCitation :: CitationMode -> LP Inlines +complexNatbibCitation :: PandocMonad m => CitationMode -> LP m Inlines complexNatbibCitation mode = try $ do let ils = (toList . trimInlines . mconcat) <$> many (notFollowedBy (oneOf "\\};") >> inline) @@ -1359,7 +1366,7 @@ complexNatbibCitation mode = try $ do -- tables -parseAligns :: LP [Alignment] +parseAligns :: PandocMonad m => LP m [Alignment] parseAligns = try $ do char '{' let maybeBar = skipMany $ sp <|> () <$ char '|' <|> () <$ (char '@' >> braced) @@ -1375,7 +1382,7 @@ parseAligns = try $ do spaces return aligns' -hline :: LP () +hline :: PandocMonad m => LP m () hline = try $ do spaces' controlSeq "hline" <|> @@ -1389,16 +1396,16 @@ hline = try $ do optional $ bracketed (many1 (satisfy (/=']'))) return () -lbreak :: LP () +lbreak :: PandocMonad m => LP m () lbreak = () <$ try (spaces' *> (controlSeq "\\" <|> controlSeq "tabularnewline") <* spaces') -amp :: LP () +amp :: PandocMonad m => LP m () amp = () <$ try (spaces' *> char '&' <* spaces') -parseTableRow :: Int -- ^ number of columns - -> LP [Blocks] +parseTableRow :: PandocMonad m => Int -- ^ number of columns + -> LP m [Blocks] parseTableRow cols = try $ do let tableCellInline = notFollowedBy (amp <|> lbreak) >> inline let minipage = try $ controlSeq "begin" *> string "{minipage}" *> @@ -1415,10 +1422,10 @@ parseTableRow cols = try $ do spaces' return cells'' -spaces' :: LP () +spaces' :: PandocMonad m => LP m () spaces' = spaces *> skipMany (comment *> spaces) -simpTable :: Bool -> LP Blocks +simpTable :: PandocMonad m => Bool -> LP m Blocks simpTable hasWidthParameter = try $ do when hasWidthParameter $ () <$ (spaces' >> tok) skipopts @@ -1442,13 +1449,13 @@ simpTable hasWidthParameter = try $ do lookAhead $ controlSeq "end" -- make sure we're at end return $ table mempty (zip aligns (repeat 0)) header'' rows -startInclude :: LP Blocks +startInclude :: PandocMonad m => LP m Blocks startInclude = do fn <- braced setPosition $ newPos fn 1 1 return mempty -endInclude :: LP Blocks +endInclude :: PandocMonad m => LP m Blocks endInclude = do fn <- braced ln <- braced diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index cd35a8738..e5df065ff 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -1,5 +1,6 @@ {-# LANGUAGE RelaxedPolyRec #-} -- needed for inlinesBetween on GHC < 7 {-# LANGUAGE ScopedTypeVariables #-} + {- Copyright (C) 2006-2015 John MacFarlane @@ -65,24 +66,31 @@ import qualified Data.Set as Set import Text.Printf (printf) import Debug.Trace (trace) import Data.Monoid ((<>)) -import Text.Pandoc.Error +import Control.Monad.Trans (lift) +import Control.Monad.Except (throwError, catchError) +import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) +import qualified Text.Pandoc.Class as P -type MarkdownParser = Parser [Char] ParserState +type MarkdownParser m = ParserT [Char] ParserState m -- | Read markdown from an input string and return a Pandoc document. -readMarkdown :: ReaderOptions -- ^ Reader options +readMarkdown :: PandocMonad m + => ReaderOptions -- ^ Reader options -> String -- ^ String to parse (assuming @'\n'@ line endings) - -> Either PandocError Pandoc -readMarkdown opts s = - (readWith parseMarkdown) def{ stateOptions = opts } (s ++ "\n\n") + -> m Pandoc +readMarkdown opts s = do + parsed <- (readWithM parseMarkdown) def{ stateOptions = opts } (s ++ "\n\n") + case parsed of + Right result -> return result + Left _ -> throwError $ PandocParseError "markdown parse error" -- | Read markdown from an input string and return a pair of a Pandoc document -- and a list of warnings. -readMarkdownWithWarnings :: ReaderOptions -- ^ Reader options +readMarkdownWithWarnings :: PandocMonad m + => ReaderOptions -- ^ Reader options -> String -- ^ String to parse (assuming @'\n'@ line endings) - -> Either PandocError (Pandoc, [String]) -readMarkdownWithWarnings opts s = - (readWithWarnings parseMarkdown) def{ stateOptions = opts } (s ++ "\n\n") + -> m Pandoc +readMarkdownWithWarnings = readMarkdown trimInlinesF :: F Inlines -> F Inlines trimInlinesF = liftM trimInlines @@ -117,25 +125,25 @@ isBlank _ = False -- -- | Succeeds when we're in list context. -inList :: MarkdownParser () +inList :: PandocMonad m => MarkdownParser m () inList = do ctx <- stateParserContext <$> getState guard (ctx == ListItemState) -spnl :: Parser [Char] st () +spnl :: PandocMonad m => ParserT [Char] st m () spnl = try $ do skipSpaces optional newline skipSpaces notFollowedBy (char '\n') -indentSpaces :: MarkdownParser String +indentSpaces :: PandocMonad m => MarkdownParser m String indentSpaces = try $ do tabStop <- getOption readerTabStop count tabStop (char ' ') <|> string "\t" "indentation" -nonindentSpaces :: MarkdownParser String +nonindentSpaces :: PandocMonad m => MarkdownParser m String nonindentSpaces = do tabStop <- getOption readerTabStop sps <- many (char ' ') @@ -144,17 +152,17 @@ nonindentSpaces = do else unexpected "indented line" -- returns number of spaces parsed -skipNonindentSpaces :: MarkdownParser Int +skipNonindentSpaces :: PandocMonad m => MarkdownParser m Int skipNonindentSpaces = do tabStop <- getOption readerTabStop atMostSpaces (tabStop - 1) <* notFollowedBy (char ' ') -atMostSpaces :: Int -> MarkdownParser Int +atMostSpaces :: PandocMonad m => Int -> MarkdownParser m Int atMostSpaces n | n > 0 = (char ' ' >> (+1) <$> atMostSpaces (n-1)) <|> return 0 | otherwise = return 0 -litChar :: MarkdownParser Char +litChar :: PandocMonad m => MarkdownParser m Char litChar = escapedChar' <|> characterReference <|> noneOf "\n" @@ -162,14 +170,14 @@ litChar = escapedChar' -- | Parse a sequence of inline elements between square brackets, -- including inlines between balanced pairs of square brackets. -inlinesInBalancedBrackets :: MarkdownParser (F Inlines) +inlinesInBalancedBrackets :: PandocMonad m => MarkdownParser m (F Inlines) inlinesInBalancedBrackets = do char '[' (_, raw) <- withRaw $ charsInBalancedBrackets 1 guard $ not $ null raw parseFromString (trimInlinesF . mconcat <$> many inline) (init raw) -charsInBalancedBrackets :: Int -> MarkdownParser () +charsInBalancedBrackets :: PandocMonad m => Int -> MarkdownParser m () charsInBalancedBrackets 0 = return () charsInBalancedBrackets openBrackets = (char '[' >> charsInBalancedBrackets (openBrackets + 1)) @@ -185,7 +193,7 @@ charsInBalancedBrackets openBrackets = -- document structure -- -rawTitleBlockLine :: MarkdownParser String +rawTitleBlockLine :: PandocMonad m => MarkdownParser m String rawTitleBlockLine = do char '%' skipSpaces @@ -196,13 +204,13 @@ rawTitleBlockLine = do anyLine return $ trim $ unlines (first:rest) -titleLine :: MarkdownParser (F Inlines) +titleLine :: PandocMonad m => MarkdownParser m (F Inlines) titleLine = try $ do raw <- rawTitleBlockLine res <- parseFromString (many inline) raw return $ trimInlinesF $ mconcat res -authorsLine :: MarkdownParser (F [Inlines]) +authorsLine :: PandocMonad m => MarkdownParser m (F [Inlines]) authorsLine = try $ do raw <- rawTitleBlockLine let sep = (char ';' <* spaces) <|> newline @@ -212,16 +220,16 @@ authorsLine = try $ do sep sequence <$> parseFromString pAuthors raw -dateLine :: MarkdownParser (F Inlines) +dateLine :: PandocMonad m => MarkdownParser m (F Inlines) dateLine = try $ do raw <- rawTitleBlockLine res <- parseFromString (many inline) raw return $ trimInlinesF $ mconcat res -titleBlock :: MarkdownParser () +titleBlock :: PandocMonad m => MarkdownParser m () titleBlock = pandocTitleBlock <|> mmdTitleBlock -pandocTitleBlock :: MarkdownParser () +pandocTitleBlock :: PandocMonad m => MarkdownParser m () pandocTitleBlock = try $ do guardEnabled Ext_pandoc_title_block lookAhead (char '%') @@ -239,7 +247,15 @@ pandocTitleBlock = try $ do $ nullMeta updateState $ \st -> st{ stateMeta' = stateMeta' st <> meta' } -yamlMetaBlock :: MarkdownParser (F Blocks) + +-- Adapted from solution at +-- http://stackoverflow.com/a/29448764/1901888 +foldrWithKeyM :: Monad m => (k -> b -> a -> m a) -> a -> H.HashMap k b -> m a +foldrWithKeyM f acc = H.foldrWithKey f' (return acc) + where + f' k b ma = ma >>= \a -> f k b a + +yamlMetaBlock :: PandocMonad m => MarkdownParser m (F Blocks) yamlMetaBlock = try $ do guardEnabled Ext_yaml_metadata_block pos <- getPosition @@ -252,18 +268,20 @@ yamlMetaBlock = try $ do optional blanklines opts <- stateOptions <$> getState meta' <- case Yaml.decodeEither' $ UTF8.fromString rawYaml of - Right (Yaml.Object hashmap) -> return $ return $ - H.foldrWithKey (\k v m -> - if ignorable k - then m - else case yamlToMeta opts v of - Left _ -> m - Right v' -> B.setMeta (T.unpack k) v' m) - nullMeta hashmap - Right Yaml.Null -> return $ return nullMeta + Right (Yaml.Object hashmap) -> + foldrWithKeyM + (\k v m -> do + if ignorable k + then return m + else (do v' <- lift $ yamlToMeta opts v + return $ B.setMeta (T.unpack k) v' m) + `catchError` + (\_ -> return m) + ) nullMeta hashmap + Right Yaml.Null -> return nullMeta Right _ -> do - addWarning (Just pos) "YAML header is not an object" - return $ return nullMeta + P.addWarningWithPos (Just pos) "YAML header is not an object" + return nullMeta Left err' -> do case err' of InvalidYaml (Just YamlParseException{ @@ -273,24 +291,24 @@ yamlMetaBlock = try $ do yamlLine = yline , yamlColumn = ycol }}) -> - addWarning (Just $ setSourceLine + P.addWarningWithPos (Just $ setSourceLine (setSourceColumn pos (sourceColumn pos + ycol)) (sourceLine pos + 1 + yline)) $ "Could not parse YAML header: " ++ problem - _ -> addWarning (Just pos) + _ -> P.addWarningWithPos (Just pos) $ "Could not parse YAML header: " ++ show err' - return $ return nullMeta - updateState $ \st -> st{ stateMeta' = stateMeta' st <> meta' } + return nullMeta + updateState $ \st -> st{ stateMeta' = stateMeta' st <> (return meta') } return mempty -- ignore fields ending with _ ignorable :: Text -> Bool ignorable t = (T.pack "_") `T.isSuffixOf` t -toMetaValue :: ReaderOptions -> Text -> Either PandocError MetaValue +toMetaValue :: PandocMonad m => ReaderOptions -> Text -> m MetaValue toMetaValue opts x = toMeta <$> readMarkdown opts' (T.unpack x) where toMeta p = @@ -307,7 +325,7 @@ toMetaValue opts x = toMeta <$> readMarkdown opts' (T.unpack x) , Ext_yaml_metadata_block ] -yamlToMeta :: ReaderOptions -> Yaml.Value -> Either PandocError MetaValue +yamlToMeta :: PandocMonad m => ReaderOptions -> Yaml.Value -> m MetaValue yamlToMeta opts (Yaml.String t) = toMetaValue opts t yamlToMeta _ (Yaml.Number n) -- avoid decimal points for numbers that don't need them: @@ -327,10 +345,10 @@ yamlToMeta opts (Yaml.Object o) = MetaMap <$> H.foldrWithKey (\k v m -> (return M.empty) o yamlToMeta _ _ = return $ MetaString "" -stopLine :: MarkdownParser () +stopLine :: PandocMonad m => MarkdownParser m () stopLine = try $ (string "---" <|> string "...") >> blankline >> return () -mmdTitleBlock :: MarkdownParser () +mmdTitleBlock :: PandocMonad m => MarkdownParser m () mmdTitleBlock = try $ do guardEnabled Ext_mmd_title_block firstPair <- kvPair False @@ -340,7 +358,7 @@ mmdTitleBlock = try $ do updateState $ \st -> st{ stateMeta' = stateMeta' st <> return (Meta $ M.fromList kvPairs) } -kvPair :: Bool -> MarkdownParser (String, MetaValue) +kvPair :: PandocMonad m => Bool -> MarkdownParser m (String, MetaValue) kvPair allowEmpty = try $ do key <- many1Till (alphaNum <|> oneOf "_- ") (char ':') val <- trim <$> manyTill anyChar @@ -350,7 +368,7 @@ kvPair allowEmpty = try $ do let val' = MetaBlocks $ B.toList $ B.plain $ B.text $ val return (key',val') -parseMarkdown :: MarkdownParser Pandoc +parseMarkdown :: PandocMonad m => MarkdownParser m Pandoc parseMarkdown = do -- markdown allows raw HTML updateState $ \state -> state { stateOptions = @@ -375,7 +393,7 @@ softBreakFilter (x:SoftBreak:y:zs) = _ -> x:SoftBreak:y:zs softBreakFilter xs = xs -referenceKey :: MarkdownParser (F Blocks) +referenceKey :: PandocMonad m => MarkdownParser m (F Blocks) referenceKey = try $ do pos <- getPosition skipNonindentSpaces @@ -402,18 +420,18 @@ referenceKey = try $ do let oldkeys = stateKeys st let key = toKey raw case M.lookup key oldkeys of - Just _ -> addWarning (Just pos) $ "Duplicate link reference `" ++ raw ++ "'" + Just _ -> P.addWarningWithPos (Just pos) $ "Duplicate link reference `" ++ raw ++ "'" Nothing -> return () updateState $ \s -> s { stateKeys = M.insert key (target, attr') oldkeys } return $ return mempty -referenceTitle :: MarkdownParser String +referenceTitle :: PandocMonad m => MarkdownParser m String referenceTitle = try $ do skipSpaces >> optional newline >> skipSpaces quotedTitle '"' <|> quotedTitle '\'' <|> charsInBalanced '(' ')' litChar -- A link title in quotes -quotedTitle :: Char -> MarkdownParser String +quotedTitle :: PandocMonad m => Char -> MarkdownParser m String quotedTitle c = try $ do char c notFollowedBy spaces @@ -425,7 +443,7 @@ quotedTitle c = try $ do -- | PHP Markdown Extra style abbreviation key. Currently -- we just skip them, since Pandoc doesn't have an element for -- an abbreviation. -abbrevKey :: MarkdownParser (F Blocks) +abbrevKey :: PandocMonad m => MarkdownParser m (F Blocks) abbrevKey = do guardEnabled Ext_abbreviations try $ do @@ -436,23 +454,23 @@ abbrevKey = do blanklines return $ return mempty -noteMarker :: MarkdownParser String +noteMarker :: PandocMonad m => MarkdownParser m String noteMarker = string "[^" >> many1Till (satisfy $ not . isBlank) (char ']') -rawLine :: MarkdownParser String +rawLine :: PandocMonad m => MarkdownParser m String rawLine = try $ do notFollowedBy blankline notFollowedBy' $ try $ skipNonindentSpaces >> noteMarker optional indentSpaces anyLine -rawLines :: MarkdownParser String +rawLines :: PandocMonad m => MarkdownParser m String rawLines = do first <- anyLine rest <- many rawLine return $ unlines (first:rest) -noteBlock :: MarkdownParser (F Blocks) +noteBlock :: PandocMonad m => MarkdownParser m (F Blocks) noteBlock = try $ do pos <- getPosition skipNonindentSpaces @@ -468,7 +486,7 @@ noteBlock = try $ do let newnote = (ref, parsed) oldnotes <- stateNotes' <$> getState case lookup ref oldnotes of - Just _ -> addWarning (Just pos) $ "Duplicate note reference `" ++ ref ++ "'" + Just _ -> P.addWarningWithPos (Just pos) $ "Duplicate note reference `" ++ ref ++ "'" Nothing -> return () updateState $ \s -> s { stateNotes' = newnote : oldnotes } return mempty @@ -477,10 +495,10 @@ noteBlock = try $ do -- parsing blocks -- -parseBlocks :: MarkdownParser (F Blocks) +parseBlocks :: PandocMonad m => MarkdownParser m (F Blocks) parseBlocks = mconcat <$> manyTill block eof -block :: MarkdownParser (F Blocks) +block :: PandocMonad m => MarkdownParser m (F Blocks) block = do tr <- getOption readerTrace pos <- getPosition @@ -519,16 +537,16 @@ block = do -- header blocks -- -header :: MarkdownParser (F Blocks) +header :: PandocMonad m => MarkdownParser m (F Blocks) header = setextHeader <|> atxHeader "header" -atxChar :: MarkdownParser Char +atxChar :: PandocMonad m => MarkdownParser m Char atxChar = do exts <- getOption readerExtensions return $ if Set.member Ext_literate_haskell exts then '=' else '#' -atxHeader :: MarkdownParser (F Blocks) +atxHeader :: PandocMonad m => MarkdownParser m (F Blocks) atxHeader = try $ do level <- atxChar >>= many1 . char >>= return . length notFollowedBy $ guardEnabled Ext_fancy_lists >> @@ -542,7 +560,7 @@ atxHeader = try $ do <|> registerImplicitHeader raw attr' return $ B.headerWith attr' level <$> text -atxClosing :: MarkdownParser Attr +atxClosing :: PandocMonad m => MarkdownParser m Attr atxClosing = try $ do attr' <- option nullAttr (guardEnabled Ext_mmd_header_identifiers >> mmdHeaderIdentifier) @@ -553,7 +571,7 @@ atxClosing = try $ do blanklines return attr -setextHeaderEnd :: MarkdownParser Attr +setextHeaderEnd :: PandocMonad m => MarkdownParser m Attr setextHeaderEnd = try $ do attr <- option nullAttr $ (guardEnabled Ext_mmd_header_identifiers >> mmdHeaderIdentifier) @@ -561,13 +579,13 @@ setextHeaderEnd = try $ do blanklines return attr -mmdHeaderIdentifier :: MarkdownParser Attr +mmdHeaderIdentifier :: PandocMonad m => MarkdownParser m Attr mmdHeaderIdentifier = do ident <- stripFirstAndLast . snd <$> reference skipSpaces return (ident,[],[]) -setextHeader :: MarkdownParser (F Blocks) +setextHeader :: PandocMonad m => MarkdownParser m (F Blocks) setextHeader = try $ do -- This lookahead prevents us from wasting time parsing Inlines -- unless necessary -- it gives a significant performance boost. @@ -585,7 +603,7 @@ setextHeader = try $ do <|> registerImplicitHeader raw attr' return $ B.headerWith attr' level <$> text -registerImplicitHeader :: String -> Attr -> MarkdownParser () +registerImplicitHeader :: PandocMonad m => String -> Attr -> MarkdownParser m () registerImplicitHeader raw attr@(ident, _, _) = do let key = toKey $ "[" ++ raw ++ "]" updateState (\s -> s { stateHeaderKeys = @@ -595,7 +613,7 @@ registerImplicitHeader raw attr@(ident, _, _) = do -- hrule block -- -hrule :: Parser [Char] st (F Blocks) +hrule :: PandocMonad m => ParserT [Char] st m (F Blocks) hrule = try $ do skipSpaces start <- satisfy isHruleChar @@ -609,12 +627,13 @@ hrule = try $ do -- code blocks -- -indentedLine :: MarkdownParser String +indentedLine :: PandocMonad m => MarkdownParser m String indentedLine = indentSpaces >> anyLine >>= return . (++ "\n") -blockDelimiter :: (Char -> Bool) +blockDelimiter :: PandocMonad m + => (Char -> Bool) -> Maybe Int - -> Parser [Char] st Int + -> ParserT [Char] st m Int blockDelimiter f len = try $ do c <- lookAhead (satisfy f) case len of @@ -622,7 +641,7 @@ blockDelimiter f len = try $ do Nothing -> count 3 (char c) >> many (char c) >>= return . (+ 3) . length -attributes :: MarkdownParser Attr +attributes :: PandocMonad m => MarkdownParser m Attr attributes = try $ do char '{' spnl @@ -630,28 +649,28 @@ attributes = try $ do char '}' return $ foldl (\x f -> f x) nullAttr attrs -attribute :: MarkdownParser (Attr -> Attr) +attribute :: PandocMonad m => MarkdownParser m (Attr -> Attr) attribute = identifierAttr <|> classAttr <|> keyValAttr <|> specialAttr -identifier :: MarkdownParser String +identifier :: PandocMonad m => MarkdownParser m String identifier = do first <- letter rest <- many $ alphaNum <|> oneOf "-_:." return (first:rest) -identifierAttr :: MarkdownParser (Attr -> Attr) +identifierAttr :: PandocMonad m => MarkdownParser m (Attr -> Attr) identifierAttr = try $ do char '#' result <- identifier return $ \(_,cs,kvs) -> (result,cs,kvs) -classAttr :: MarkdownParser (Attr -> Attr) +classAttr :: PandocMonad m => MarkdownParser m (Attr -> Attr) classAttr = try $ do char '.' result <- identifier return $ \(id',cs,kvs) -> (id',cs ++ [result],kvs) -keyValAttr :: MarkdownParser (Attr -> Attr) +keyValAttr :: PandocMonad m => MarkdownParser m (Attr -> Attr) keyValAttr = try $ do key <- identifier char '=' @@ -664,12 +683,12 @@ keyValAttr = try $ do "class" -> (id',cs ++ words val,kvs) _ -> (id',cs,kvs ++ [(key,val)]) -specialAttr :: MarkdownParser (Attr -> Attr) +specialAttr :: PandocMonad m => MarkdownParser m (Attr -> Attr) specialAttr = do char '-' return $ \(id',cs,kvs) -> (id',cs ++ ["unnumbered"],kvs) -codeBlockFenced :: MarkdownParser (F Blocks) +codeBlockFenced :: PandocMonad m => MarkdownParser m (F Blocks) codeBlockFenced = try $ do c <- try (guardEnabled Ext_fenced_code_blocks >> lookAhead (char '~')) <|> (guardEnabled Ext_backtick_code_blocks >> lookAhead (char '`')) @@ -690,7 +709,7 @@ toLanguageId = map toLower . go go "objective-c" = "objectivec" go x = x -codeBlockIndented :: MarkdownParser (F Blocks) +codeBlockIndented :: PandocMonad m => MarkdownParser m (F Blocks) codeBlockIndented = do contents <- many1 (indentedLine <|> try (do b <- blanklines @@ -701,7 +720,7 @@ codeBlockIndented = do return $ return $ B.codeBlockWith ("", classes, []) $ stripTrailingNewlines $ concat contents -lhsCodeBlock :: MarkdownParser (F Blocks) +lhsCodeBlock :: PandocMonad m => MarkdownParser m (F Blocks) lhsCodeBlock = do guardEnabled Ext_literate_haskell (return . B.codeBlockWith ("",["sourceCode","literate","haskell"],[]) <$> @@ -709,7 +728,7 @@ lhsCodeBlock = do <|> (return . B.codeBlockWith ("",["sourceCode","haskell"],[]) <$> lhsCodeBlockInverseBird) -lhsCodeBlockLaTeX :: MarkdownParser String +lhsCodeBlockLaTeX :: PandocMonad m => MarkdownParser m String lhsCodeBlockLaTeX = try $ do string "\\begin{code}" manyTill spaceChar newline @@ -717,13 +736,13 @@ lhsCodeBlockLaTeX = try $ do blanklines return $ stripTrailingNewlines contents -lhsCodeBlockBird :: MarkdownParser String +lhsCodeBlockBird :: PandocMonad m => MarkdownParser m String lhsCodeBlockBird = lhsCodeBlockBirdWith '>' -lhsCodeBlockInverseBird :: MarkdownParser String +lhsCodeBlockInverseBird :: PandocMonad m => MarkdownParser m String lhsCodeBlockInverseBird = lhsCodeBlockBirdWith '<' -lhsCodeBlockBirdWith :: Char -> MarkdownParser String +lhsCodeBlockBirdWith :: PandocMonad m => Char -> MarkdownParser m String lhsCodeBlockBirdWith c = try $ do pos <- getPosition when (sourceColumn pos /= 1) $ fail "Not in first column" @@ -735,7 +754,7 @@ lhsCodeBlockBirdWith c = try $ do blanklines return $ intercalate "\n" lns' -birdTrackLine :: Char -> Parser [Char] st String +birdTrackLine :: PandocMonad m => Char -> ParserT [Char] st m String birdTrackLine c = try $ do char c -- allow html tags on left margin: @@ -746,10 +765,10 @@ birdTrackLine c = try $ do -- block quotes -- -emailBlockQuoteStart :: MarkdownParser Char +emailBlockQuoteStart :: PandocMonad m => MarkdownParser m Char emailBlockQuoteStart = try $ skipNonindentSpaces >> char '>' <* optional (char ' ') -emailBlockQuote :: MarkdownParser [String] +emailBlockQuote :: PandocMonad m => MarkdownParser m [String] emailBlockQuote = try $ do emailBlockQuoteStart let emailLine = many $ nonEndline <|> try @@ -763,7 +782,7 @@ emailBlockQuote = try $ do optional blanklines return raw -blockQuote :: MarkdownParser (F Blocks) +blockQuote :: PandocMonad m => MarkdownParser m (F Blocks) blockQuote = do raw <- emailBlockQuote -- parse the extracted block, which may contain various block elements: @@ -774,7 +793,7 @@ blockQuote = do -- list blocks -- -bulletListStart :: MarkdownParser () +bulletListStart :: PandocMonad m => MarkdownParser m () bulletListStart = try $ do optional newline -- if preceded by a Plain block in a list context startpos <- sourceColumn <$> getPosition @@ -786,7 +805,7 @@ bulletListStart = try $ do lookAhead (newline <|> spaceChar) () <$ atMostSpaces (tabStop - (endpos - startpos)) -anyOrderedListStart :: MarkdownParser (Int, ListNumberStyle, ListNumberDelim) +anyOrderedListStart :: PandocMonad m => MarkdownParser m (Int, ListNumberStyle, ListNumberDelim) anyOrderedListStart = try $ do optional newline -- if preceded by a Plain block in a list context startpos <- sourceColumn <$> getPosition @@ -810,10 +829,10 @@ anyOrderedListStart = try $ do atMostSpaces (tabStop - (endpos - startpos)) return res -listStart :: MarkdownParser () +listStart :: PandocMonad m => MarkdownParser m () listStart = bulletListStart <|> (anyOrderedListStart >> return ()) -listLine :: MarkdownParser String +listLine :: PandocMonad m => MarkdownParser m String listLine = try $ do notFollowedBy' (do indentSpaces many spaceChar @@ -822,7 +841,7 @@ listLine = try $ do optional (() <$ indentSpaces) listLineCommon -listLineCommon :: MarkdownParser String +listLineCommon :: PandocMonad m => MarkdownParser m String listLineCommon = concat <$> manyTill ( many1 (satisfy $ \c -> c /= '\n' && c /= '<') <|> liftM snd (htmlTag isCommentTag) @@ -830,8 +849,9 @@ listLineCommon = concat <$> manyTill ) newline -- parse raw text for one list item, excluding start marker and continuations -rawListItem :: MarkdownParser a - -> MarkdownParser String +rawListItem :: PandocMonad m + => MarkdownParser m a + -> MarkdownParser m String rawListItem start = try $ do start first <- listLineCommon @@ -842,21 +862,21 @@ rawListItem start = try $ do -- continuation of a list item - indented and separated by blankline -- or (in compact lists) endline. -- note: nested lists are parsed as continuations -listContinuation :: MarkdownParser String +listContinuation :: PandocMonad m => MarkdownParser m String listContinuation = try $ do lookAhead indentSpaces result <- many1 listContinuationLine blanks <- many blankline return $ concat result ++ blanks -notFollowedByHtmlCloser :: MarkdownParser () +notFollowedByHtmlCloser :: PandocMonad m => MarkdownParser m () notFollowedByHtmlCloser = do inHtmlBlock <- stateInHtmlBlock <$> getState case inHtmlBlock of Just t -> notFollowedBy' $ htmlTag (~== TagClose t) Nothing -> return () -listContinuationLine :: MarkdownParser String +listContinuationLine :: PandocMonad m => MarkdownParser m String listContinuationLine = try $ do notFollowedBy blankline notFollowedBy' listStart @@ -865,8 +885,9 @@ listContinuationLine = try $ do result <- anyLine return $ result ++ "\n" -listItem :: MarkdownParser a - -> MarkdownParser (F Blocks) +listItem :: PandocMonad m + => MarkdownParser m a + -> MarkdownParser m (F Blocks) listItem start = try $ do first <- rawListItem start continuations <- many listContinuation @@ -882,7 +903,7 @@ listItem start = try $ do updateState (\st -> st {stateParserContext = oldContext}) return contents -orderedList :: MarkdownParser (F Blocks) +orderedList :: PandocMonad m => MarkdownParser m (F Blocks) orderedList = try $ do (start, style, delim) <- lookAhead anyOrderedListStart unless (style `elem` [DefaultStyle, Decimal, Example] && @@ -903,14 +924,14 @@ orderedList = try $ do start' <- option 1 $ guardEnabled Ext_startnum >> return start return $ B.orderedListWith (start', style, delim) <$> fmap compactify' items -bulletList :: MarkdownParser (F Blocks) +bulletList :: PandocMonad m => MarkdownParser m (F Blocks) bulletList = do items <- fmap sequence $ many1 $ listItem bulletListStart return $ B.bulletList <$> fmap compactify' items -- definition lists -defListMarker :: MarkdownParser () +defListMarker :: PandocMonad m => MarkdownParser m () defListMarker = do sps <- nonindentSpaces char ':' <|> char '~' @@ -921,7 +942,7 @@ defListMarker = do else mzero return () -definitionListItem :: Bool -> MarkdownParser (F (Inlines, [Blocks])) +definitionListItem :: PandocMonad m => Bool -> MarkdownParser m (F (Inlines, [Blocks])) definitionListItem compact = try $ do rawLine' <- anyLine raw <- many1 $ defRawBlock compact @@ -930,7 +951,7 @@ definitionListItem compact = try $ do optional blanklines return $ liftM2 (,) term (sequence contents) -defRawBlock :: Bool -> MarkdownParser String +defRawBlock :: PandocMonad m => Bool -> MarkdownParser m String defRawBlock compact = try $ do hasBlank <- option False $ blankline >> return True defListMarker @@ -952,7 +973,7 @@ defRawBlock compact = try $ do return $ trimr (firstline ++ "\n" ++ unlines rawlines ++ cont) ++ if hasBlank || not (null cont) then "\n\n" else "" -definitionList :: MarkdownParser (F Blocks) +definitionList :: PandocMonad m => MarkdownParser m (F Blocks) definitionList = try $ do lookAhead (anyLine >> optional (blankline >> notFollowedBy (table >> return ())) >> @@ -960,13 +981,13 @@ definitionList = try $ do defListMarker) compactDefinitionList <|> normalDefinitionList -compactDefinitionList :: MarkdownParser (F Blocks) +compactDefinitionList :: PandocMonad m => MarkdownParser m (F Blocks) compactDefinitionList = do guardEnabled Ext_compact_definition_lists items <- fmap sequence $ many1 $ definitionListItem True return $ B.definitionList <$> fmap compactify'DL items -normalDefinitionList :: MarkdownParser (F Blocks) +normalDefinitionList :: PandocMonad m => MarkdownParser m (F Blocks) normalDefinitionList = do guardEnabled Ext_definition_lists items <- fmap sequence $ many1 $ definitionListItem False @@ -976,7 +997,7 @@ normalDefinitionList = do -- paragraph block -- -para :: MarkdownParser (F Blocks) +para :: PandocMonad m => MarkdownParser m (F Blocks) para = try $ do exts <- getOption readerExtensions result <- trimInlinesF . mconcat <$> many1 inline @@ -1007,19 +1028,19 @@ para = try $ do $ Image attr alt (src,'f':'i':'g':':':tit) _ -> return $ B.para result' -plain :: MarkdownParser (F Blocks) +plain :: PandocMonad m => MarkdownParser m (F Blocks) plain = fmap B.plain . trimInlinesF . mconcat <$> many1 inline -- -- raw html -- -htmlElement :: MarkdownParser String +htmlElement :: PandocMonad m => MarkdownParser m String htmlElement = rawVerbatimBlock <|> strictHtmlBlock <|> liftM snd (htmlTag isBlockTag) -htmlBlock :: MarkdownParser (F Blocks) +htmlBlock :: PandocMonad m => MarkdownParser m (F Blocks) htmlBlock = do guardEnabled Ext_raw_html try (do @@ -1044,24 +1065,24 @@ htmlBlock = do <|> (guardEnabled Ext_markdown_in_html_blocks >> rawHtmlBlocks)) <|> htmlBlock' -htmlBlock' :: MarkdownParser (F Blocks) +htmlBlock' :: PandocMonad m => MarkdownParser m (F Blocks) htmlBlock' = try $ do first <- htmlElement skipMany spaceChar optional blanklines return $ return $ B.rawBlock "html" first -strictHtmlBlock :: MarkdownParser String +strictHtmlBlock :: PandocMonad m => MarkdownParser m String strictHtmlBlock = htmlInBalanced (not . isInlineTag) -rawVerbatimBlock :: MarkdownParser String +rawVerbatimBlock :: PandocMonad m => MarkdownParser m String rawVerbatimBlock = htmlInBalanced isVerbTag where isVerbTag (TagOpen "pre" _) = True isVerbTag (TagOpen "style" _) = True isVerbTag (TagOpen "script" _) = True isVerbTag _ = False -rawTeXBlock :: MarkdownParser (F Blocks) +rawTeXBlock :: PandocMonad m => MarkdownParser m (F Blocks) rawTeXBlock = do guardEnabled Ext_raw_tex result <- (B.rawBlock "latex" . concat <$> @@ -1071,7 +1092,7 @@ rawTeXBlock = do spaces return $ return result -rawHtmlBlocks :: MarkdownParser (F Blocks) +rawHtmlBlocks :: PandocMonad m => MarkdownParser m (F Blocks) rawHtmlBlocks = do (TagOpen tagtype _, raw) <- htmlTag isBlockTag -- try to find closing tag @@ -1101,7 +1122,7 @@ stripMarkdownAttribute s = renderTags' $ map filterAttrib $ parseTags s -- line block -- -lineBlock :: MarkdownParser (F Blocks) +lineBlock :: PandocMonad m => MarkdownParser m (F Blocks) lineBlock = try $ do guardEnabled Ext_line_blocks lines' <- lineBlockLines >>= @@ -1114,8 +1135,9 @@ lineBlock = try $ do -- Parse a dashed line with optional trailing spaces; return its length -- and the length including trailing space. -dashedLine :: Char - -> Parser [Char] st (Int, Int) +dashedLine :: PandocMonad m + => Char + -> ParserT [Char] st m (Int, Int) dashedLine ch = do dashes <- many1 (char ch) sp <- many spaceChar @@ -1125,8 +1147,9 @@ dashedLine ch = do -- Parse a table header with dashed lines of '-' preceded by -- one (or zero) line of text. -simpleTableHeader :: Bool -- ^ Headerless table - -> MarkdownParser (F [Blocks], [Alignment], [Int]) +simpleTableHeader :: PandocMonad m + => Bool -- ^ Headerless table + -> MarkdownParser m (F [Blocks], [Alignment], [Int]) simpleTableHeader headless = try $ do rawContent <- if headless then return "" @@ -1170,16 +1193,17 @@ alignType strLst len = (False, False) -> AlignDefault -- Parse a table footer - dashed lines followed by blank line. -tableFooter :: MarkdownParser String +tableFooter :: PandocMonad m => MarkdownParser m String tableFooter = try $ skipNonindentSpaces >> many1 (dashedLine '-') >> blanklines -- Parse a table separator - dashed line. -tableSep :: MarkdownParser Char +tableSep :: PandocMonad m => MarkdownParser m Char tableSep = try $ skipNonindentSpaces >> many1 (dashedLine '-') >> char '\n' -- Parse a raw line and split it into chunks by indices. -rawTableLine :: [Int] - -> MarkdownParser [String] +rawTableLine :: PandocMonad m + => [Int] + -> MarkdownParser m [String] rawTableLine indices = do notFollowedBy' (blanklines <|> tableFooter) line <- many1Till anyChar newline @@ -1187,14 +1211,16 @@ rawTableLine indices = do splitStringByIndices (init indices) line -- Parse a table line and return a list of lists of blocks (columns). -tableLine :: [Int] - -> MarkdownParser (F [Blocks]) +tableLine :: PandocMonad m + => [Int] + -> MarkdownParser m (F [Blocks]) tableLine indices = rawTableLine indices >>= fmap sequence . mapM (parseFromString (mconcat <$> many plain)) -- Parse a multiline table row and return a list of blocks (columns). -multilineRow :: [Int] - -> MarkdownParser (F [Blocks]) +multilineRow :: PandocMonad m + => [Int] + -> MarkdownParser m (F [Blocks]) multilineRow indices = do colLines <- many1 (rawTableLine indices) let cols = map unlines $ transpose colLines @@ -1202,7 +1228,7 @@ multilineRow indices = do -- Parses a table caption: inlines beginning with 'Table:' -- and followed by blank lines. -tableCaption :: MarkdownParser (F Inlines) +tableCaption :: PandocMonad m => MarkdownParser m (F Inlines) tableCaption = try $ do guardEnabled Ext_table_captions skipNonindentSpaces @@ -1210,8 +1236,9 @@ tableCaption = try $ do trimInlinesF . mconcat <$> many1 inline <* blanklines -- Parse a simple table with '---' header and one line per row. -simpleTable :: Bool -- ^ Headerless table - -> MarkdownParser ([Alignment], [Double], F [Blocks], F [[Blocks]]) +simpleTable :: PandocMonad m + => Bool -- ^ Headerless table + -> MarkdownParser m ([Alignment], [Double], F [Blocks], F [[Blocks]]) simpleTable headless = do (aligns, _widths, heads', lines') <- tableWith (simpleTableHeader headless) tableLine @@ -1224,13 +1251,15 @@ simpleTable headless = do -- (which may be multiline), then the rows, -- which may be multiline, separated by blank lines, and -- ending with a footer (dashed line followed by blank line). -multilineTable :: Bool -- ^ Headerless table - -> MarkdownParser ([Alignment], [Double], F [Blocks], F [[Blocks]]) +multilineTable :: PandocMonad m + => Bool -- ^ Headerless table + -> MarkdownParser m ([Alignment], [Double], F [Blocks], F [[Blocks]]) multilineTable headless = tableWith (multilineTableHeader headless) multilineRow blanklines tableFooter -multilineTableHeader :: Bool -- ^ Headerless table - -> MarkdownParser (F [Blocks], [Alignment], [Int]) +multilineTableHeader :: PandocMonad m + => Bool -- ^ Headerless table + -> MarkdownParser m (F [Blocks], [Alignment], [Int]) multilineTableHeader headless = try $ do unless headless $ tableSep >> notFollowedBy blankline @@ -1261,8 +1290,8 @@ multilineTableHeader headless = try $ do -- (which may be grid), then the rows, -- which may be grid, separated by blank lines, and -- ending with a footer (dashed line followed by blank line). -gridTable :: Bool -- ^ Headerless table - -> MarkdownParser ([Alignment], [Double], F [Blocks], F [[Blocks]]) +gridTable :: PandocMonad m => Bool -- ^ Headerless table + -> MarkdownParser m ([Alignment], [Double], F [Blocks], F [[Blocks]]) gridTable headless = tableWith (gridTableHeader headless) gridTableRow (gridTableSep '-') gridTableFooter @@ -1271,7 +1300,7 @@ gridTableSplitLine :: [Int] -> String -> [String] gridTableSplitLine indices line = map removeFinalBar $ tail $ splitStringByIndices (init indices) $ trimr line -gridPart :: Char -> Parser [Char] st ((Int, Int), Alignment) +gridPart :: PandocMonad m => Char -> ParserT [Char] st m ((Int, Int), Alignment) gridPart ch = do leftColon <- option False (True <$ char ':') dashes <- many1 (char ch) @@ -1286,7 +1315,7 @@ gridPart ch = do (False, False) -> AlignDefault return ((lengthDashes, lengthDashes + 1), alignment) -gridDashedLines :: Char -> Parser [Char] st [((Int, Int), Alignment)] +gridDashedLines :: PandocMonad m => Char -> ParserT [Char] st m [((Int, Int), Alignment)] gridDashedLines ch = try $ char '+' >> many1 (gridPart ch) <* blankline removeFinalBar :: String -> String @@ -1294,12 +1323,12 @@ removeFinalBar = reverse . dropWhile (`elem` " \t") . dropWhile (=='|') . reverse -- | Separator between rows of grid table. -gridTableSep :: Char -> MarkdownParser Char +gridTableSep :: PandocMonad m => Char -> MarkdownParser m Char gridTableSep ch = try $ gridDashedLines ch >> return '\n' -- | Parse header for a grid table. -gridTableHeader :: Bool -- ^ Headerless table - -> MarkdownParser (F [Blocks], [Alignment], [Int]) +gridTableHeader :: PandocMonad m => Bool -- ^ Headerless table + -> MarkdownParser m (F [Blocks], [Alignment], [Int]) gridTableHeader headless = try $ do optional blanklines dashes <- gridDashedLines '-' @@ -1320,15 +1349,15 @@ gridTableHeader headless = try $ do heads <- fmap sequence $ mapM (parseFromString parseBlocks . trim) rawHeads return (heads, aligns, indices) -gridTableRawLine :: [Int] -> MarkdownParser [String] +gridTableRawLine :: PandocMonad m => [Int] -> MarkdownParser m [String] gridTableRawLine indices = do char '|' line <- anyLine return (gridTableSplitLine indices line) -- | Parse row of grid table. -gridTableRow :: [Int] - -> MarkdownParser (F [Blocks]) +gridTableRow :: PandocMonad m => [Int] + -> MarkdownParser m (F [Blocks]) gridTableRow indices = do colLines <- many1 (gridTableRawLine indices) let cols = map ((++ "\n") . unlines . removeOneLeadingSpace) $ @@ -1344,10 +1373,10 @@ removeOneLeadingSpace xs = startsWithSpace (y:_) = y == ' ' -- | Parse footer for a grid table. -gridTableFooter :: MarkdownParser [Char] +gridTableFooter :: PandocMonad m => MarkdownParser m [Char] gridTableFooter = blanklines -pipeBreak :: MarkdownParser ([Alignment], [Int]) +pipeBreak :: PandocMonad m => MarkdownParser m ([Alignment], [Int]) pipeBreak = try $ do nonindentSpaces openPipe <- (True <$ char '|') <|> return False @@ -1359,7 +1388,7 @@ pipeBreak = try $ do blankline return $ unzip (first:rest) -pipeTable :: MarkdownParser ([Alignment], [Double], F [Blocks], F [[Blocks]]) +pipeTable :: PandocMonad m => MarkdownParser m ([Alignment], [Double], F [Blocks], F [[Blocks]]) pipeTable = try $ do nonindentSpaces lookAhead nonspaceChar @@ -1377,13 +1406,13 @@ pipeTable = try $ do else replicate (length aligns) 0.0 return $ (aligns, widths, heads', sequence lines'') -sepPipe :: MarkdownParser () +sepPipe :: PandocMonad m => MarkdownParser m () sepPipe = try $ do char '|' <|> char '+' notFollowedBy blankline -- parse a row, also returning probable alignments for org-table cells -pipeTableRow :: MarkdownParser (F [Blocks]) +pipeTableRow :: PandocMonad m => MarkdownParser m (F [Blocks]) pipeTableRow = try $ do scanForPipe skipMany spaceChar @@ -1399,14 +1428,14 @@ pipeTableRow = try $ do blankline return $ sequence cells -pipeTableCell :: MarkdownParser (F Blocks) +pipeTableCell :: PandocMonad m => MarkdownParser m (F Blocks) pipeTableCell = do result <- many inline if null result then return mempty else return $ B.plain . mconcat <$> sequence result -pipeTableHeaderPart :: Parser [Char] st (Alignment, Int) +pipeTableHeaderPart :: PandocMonad m => ParserT [Char] st m (Alignment, Int) pipeTableHeaderPart = try $ do skipMany spaceChar left <- optionMaybe (char ':') @@ -1422,7 +1451,7 @@ pipeTableHeaderPart = try $ do (Just _,Just _) -> AlignCenter), len) -- Succeed only if current line contains a pipe. -scanForPipe :: Parser [Char] st () +scanForPipe :: PandocMonad m => ParserT [Char] st m () scanForPipe = do inp <- getInput case break (\c -> c == '\n' || c == '|') inp of @@ -1432,11 +1461,12 @@ scanForPipe = do -- | Parse a table using 'headerParser', 'rowParser', -- 'lineParser', and 'footerParser'. Variant of the version in -- Text.Pandoc.Parsing. -tableWith :: MarkdownParser (F [Blocks], [Alignment], [Int]) - -> ([Int] -> MarkdownParser (F [Blocks])) - -> MarkdownParser sep - -> MarkdownParser end - -> MarkdownParser ([Alignment], [Double], F [Blocks], F [[Blocks]]) +tableWith :: PandocMonad m + => MarkdownParser m (F [Blocks], [Alignment], [Int]) + -> ([Int] -> MarkdownParser m (F [Blocks])) + -> MarkdownParser m sep + -> MarkdownParser m end + -> MarkdownParser m ([Alignment], [Double], F [Blocks], F [[Blocks]]) tableWith headerParser rowParser lineParser footerParser = try $ do (heads, aligns, indices) <- headerParser lines' <- fmap sequence $ rowParser indices `sepEndBy1` lineParser @@ -1447,7 +1477,7 @@ tableWith headerParser rowParser lineParser footerParser = try $ do else widthsFromIndices numColumns indices return $ (aligns, widths, heads, lines') -table :: MarkdownParser (F Blocks) +table :: PandocMonad m => MarkdownParser m (F Blocks) table = try $ do frontCaption <- option Nothing (Just <$> tableCaption) (aligns, widths, heads, lns) <- @@ -1479,7 +1509,7 @@ table = try $ do -- inline -- -inline :: MarkdownParser (F Inlines) +inline :: PandocMonad m => MarkdownParser m (F Inlines) inline = choice [ whitespace , bareURL , str @@ -1509,7 +1539,7 @@ inline = choice [ whitespace , ltSign ] "inline" -escapedChar' :: MarkdownParser Char +escapedChar' :: PandocMonad m => MarkdownParser m Char escapedChar' = try $ do char '\\' (guardEnabled Ext_all_symbols_escapable >> satisfy (not . isAlphaNum)) @@ -1518,7 +1548,7 @@ escapedChar' = try $ do <|> (guardEnabled Ext_escaped_line_breaks >> char '\n') <|> oneOf "\\`*_{}[]()>#+-.!~\"" -escapedChar :: MarkdownParser (F Inlines) +escapedChar :: PandocMonad m => MarkdownParser m (F Inlines) escapedChar = do result <- escapedChar' case result of @@ -1527,14 +1557,14 @@ escapedChar = do return (return B.linebreak) -- "\[newline]" is a linebreak _ -> return $ return $ B.str [result] -ltSign :: MarkdownParser (F Inlines) +ltSign :: PandocMonad m => MarkdownParser m (F Inlines) ltSign = do guardDisabled Ext_raw_html <|> (notFollowedByHtmlCloser >> notFollowedBy' (htmlTag isBlockTag)) char '<' return $ return $ B.str "<" -exampleRef :: MarkdownParser (F Inlines) +exampleRef :: PandocMonad m => MarkdownParser m (F Inlines) exampleRef = try $ do guardEnabled Ext_example_lists char '@' @@ -1545,7 +1575,7 @@ exampleRef = try $ do Just n -> B.str (show n) Nothing -> B.str ('@':lab) -symbol :: MarkdownParser (F Inlines) +symbol :: PandocMonad m => MarkdownParser m (F Inlines) symbol = do result <- noneOf "<\\\n\t " <|> try (do lookAhead $ char '\\' @@ -1554,7 +1584,7 @@ symbol = do return $ return $ B.str [result] -- parses inline code, between n `s and n `s -code :: MarkdownParser (F Inlines) +code :: PandocMonad m => MarkdownParser m (F Inlines) code = try $ do starts <- many1 (char '`') skipSpaces @@ -1566,7 +1596,7 @@ code = try $ do >> attributes) return $ return $ B.codeWith attr $ trim $ concat result -math :: MarkdownParser (F Inlines) +math :: PandocMonad m => MarkdownParser m (F Inlines) math = (return . B.displayMath <$> (mathDisplay >>= applyMacros')) <|> (return . B.math <$> (mathInline >>= applyMacros')) <+?> ((getOption readerSmart >>= guard) *> (return <$> apostrophe) @@ -1574,8 +1604,9 @@ math = (return . B.displayMath <$> (mathDisplay >>= applyMacros')) -- Parses material enclosed in *s, **s, _s, or __s. -- Designed to avoid backtracking. -enclosure :: Char - -> MarkdownParser (F Inlines) +enclosure :: PandocMonad m + => Char + -> MarkdownParser m (F Inlines) enclosure c = do -- we can't start an enclosure with _ if after a string and -- the intraword_underscores extension is enabled: @@ -1591,7 +1622,7 @@ enclosure c = do 1 -> one c mempty _ -> return (return $ B.str cs) -ender :: Char -> Int -> MarkdownParser () +ender :: PandocMonad m => Char -> Int -> MarkdownParser m () ender c n = try $ do count n (char c) guard (c == '*') @@ -1602,7 +1633,7 @@ ender c n = try $ do -- If one c, emit emph and then parse two. -- If two cs, emit strong and then parse one. -- Otherwise, emit ccc then the results. -three :: Char -> MarkdownParser (F Inlines) +three :: PandocMonad m => Char -> MarkdownParser m (F Inlines) three c = do contents <- mconcat <$> many (notFollowedBy (ender c 1) >> inline) (ender c 3 >> return ((B.strong . B.emph) <$> contents)) @@ -1612,7 +1643,7 @@ three c = do -- Parse inlines til you hit two c's, and emit strong. -- If you never do hit two cs, emit ** plus inlines parsed. -two :: Char -> F Inlines -> MarkdownParser (F Inlines) +two :: PandocMonad m => Char -> F Inlines -> MarkdownParser m (F Inlines) two c prefix' = do contents <- mconcat <$> many (try $ notFollowedBy (ender c 2) >> inline) (ender c 2 >> return (B.strong <$> (prefix' <> contents))) @@ -1620,7 +1651,7 @@ two c prefix' = do -- Parse inlines til you hit a c, and emit emph. -- If you never hit a c, emit * plus inlines parsed. -one :: Char -> F Inlines -> MarkdownParser (F Inlines) +one :: PandocMonad m => Char -> F Inlines -> MarkdownParser m (F Inlines) one c prefix' = do contents <- mconcat <$> many ( (notFollowedBy (ender c 1) >> inline) <|> try (string [c,c] >> @@ -1629,47 +1660,48 @@ one c prefix' = do (ender c 1 >> return (B.emph <$> (prefix' <> contents))) <|> return (return (B.str [c]) <> (prefix' <> contents)) -strongOrEmph :: MarkdownParser (F Inlines) +strongOrEmph :: PandocMonad m => MarkdownParser m (F Inlines) strongOrEmph = enclosure '*' <|> enclosure '_' -- | Parses a list of inlines between start and end delimiters. -inlinesBetween :: (Show b) - => MarkdownParser a - -> MarkdownParser b - -> MarkdownParser (F Inlines) +inlinesBetween :: PandocMonad m + => (Show b) + => MarkdownParser m a + -> MarkdownParser m b + -> MarkdownParser m (F Inlines) inlinesBetween start end = (trimInlinesF . mconcat) <$> try (start >> many1Till inner end) where inner = innerSpace <|> (notFollowedBy' (() <$ whitespace) >> inline) innerSpace = try $ whitespace <* notFollowedBy' end -strikeout :: MarkdownParser (F Inlines) +strikeout :: PandocMonad m => MarkdownParser m (F Inlines) strikeout = fmap B.strikeout <$> (guardEnabled Ext_strikeout >> inlinesBetween strikeStart strikeEnd) where strikeStart = string "~~" >> lookAhead nonspaceChar >> notFollowedBy (char '~') strikeEnd = try $ string "~~" -superscript :: MarkdownParser (F Inlines) +superscript :: PandocMonad m => MarkdownParser m (F Inlines) superscript = fmap B.superscript <$> try (do guardEnabled Ext_superscript char '^' mconcat <$> many1Till (notFollowedBy spaceChar >> inline) (char '^')) -subscript :: MarkdownParser (F Inlines) +subscript :: PandocMonad m => MarkdownParser m (F Inlines) subscript = fmap B.subscript <$> try (do guardEnabled Ext_subscript char '~' mconcat <$> many1Till (notFollowedBy spaceChar >> inline) (char '~')) -whitespace :: MarkdownParser (F Inlines) +whitespace :: PandocMonad m => MarkdownParser m (F Inlines) whitespace = spaceChar >> return <$> (lb <|> regsp) "whitespace" where lb = spaceChar >> skipMany spaceChar >> option B.space (endline >> return B.linebreak) regsp = skipMany spaceChar >> return B.space -nonEndline :: Parser [Char] st Char +nonEndline :: PandocMonad m => ParserT [Char] st m Char nonEndline = satisfy (/='\n') -str :: MarkdownParser (F Inlines) +str :: PandocMonad m => MarkdownParser m (F Inlines) str = do result <- many1 alphaNum updateLastStrPos @@ -1699,7 +1731,7 @@ likelyAbbrev x = in map snd $ filter (\(y,_) -> y == x) abbrPairs -- an endline character that can be treated as a space, not a structural break -endline :: MarkdownParser (F Inlines) +endline :: PandocMonad m => MarkdownParser m (F Inlines) endline = try $ do newline notFollowedBy blankline @@ -1721,17 +1753,17 @@ endline = try $ do -- -- a reference label for a link -reference :: MarkdownParser (F Inlines, String) +reference :: PandocMonad m => MarkdownParser m (F Inlines, String) reference = do notFollowedBy' (string "[^") -- footnote reference withRaw $ trimInlinesF <$> inlinesInBalancedBrackets -parenthesizedChars :: MarkdownParser [Char] +parenthesizedChars :: PandocMonad m => MarkdownParser m [Char] parenthesizedChars = do result <- charsInBalanced '(' ')' litChar return $ '(' : result ++ ")" -- source for a link, with optional title -source :: MarkdownParser (String, String) +source :: PandocMonad m => MarkdownParser m (String, String) source = do char '(' skipSpaces @@ -1748,10 +1780,10 @@ source = do char ')' return (escapeURI $ trimr src, tit) -linkTitle :: MarkdownParser String +linkTitle :: PandocMonad m => MarkdownParser m String linkTitle = quotedTitle '"' <|> quotedTitle '\'' -link :: MarkdownParser (F Inlines) +link :: PandocMonad m => MarkdownParser m (F Inlines) link = try $ do st <- getState guard $ stateAllowLinks st @@ -1760,7 +1792,7 @@ link = try $ do setState $ st{ stateAllowLinks = True } regLink B.linkWith lab <|> referenceLink B.linkWith (lab,raw) -bracketedSpan :: MarkdownParser (F Inlines) +bracketedSpan :: PandocMonad m => MarkdownParser m (F Inlines) bracketedSpan = try $ do guardEnabled Ext_bracketed_spans (lab,_) <- reference @@ -1773,8 +1805,10 @@ bracketedSpan = try $ do -> return $ B.smallcaps <$> lab _ -> return $ B.spanWith attr <$> lab -regLink :: (Attr -> String -> String -> Inlines -> Inlines) - -> F Inlines -> MarkdownParser (F Inlines) +regLink :: PandocMonad m + => (Attr -> String -> String -> Inlines -> Inlines) + -> F Inlines + -> MarkdownParser m (F Inlines) regLink constructor lab = try $ do (src, tit) <- source attr <- option nullAttr $ @@ -1782,8 +1816,10 @@ regLink constructor lab = try $ do return $ constructor attr src tit <$> lab -- a link like [this][ref] or [this][] or [this] -referenceLink :: (Attr -> String -> String -> Inlines -> Inlines) - -> (F Inlines, String) -> MarkdownParser (F Inlines) +referenceLink :: PandocMonad m + => (Attr -> String -> String -> Inlines -> Inlines) + -> (F Inlines, String) + -> MarkdownParser m (F Inlines) referenceLink constructor (lab, raw) = do sp <- (True <$ lookAhead (char ' ')) <|> return False (_,raw') <- option (mempty, "") $ @@ -1824,7 +1860,7 @@ dropBrackets = reverse . dropRB . reverse . dropLB dropLB ('[':xs) = xs dropLB xs = xs -bareURL :: MarkdownParser (F Inlines) +bareURL :: PandocMonad m => MarkdownParser m (F Inlines) bareURL = try $ do guardEnabled Ext_autolink_bare_uris getState >>= guard . stateAllowLinks @@ -1832,7 +1868,7 @@ bareURL = try $ do notFollowedBy $ try $ spaces >> htmlTag (~== TagClose "a") return $ return $ B.link src "" (B.str orig) -autoLink :: MarkdownParser (F Inlines) +autoLink :: PandocMonad m => MarkdownParser m (F Inlines) autoLink = try $ do getState >>= guard . stateAllowLinks char '<' @@ -1846,7 +1882,7 @@ autoLink = try $ do guardEnabled Ext_link_attributes >> attributes return $ return $ B.linkWith attr (src ++ escapeURI extra) "" (B.str $ orig ++ extra) -image :: MarkdownParser (F Inlines) +image :: PandocMonad m => MarkdownParser m (F Inlines) image = try $ do char '!' (lab,raw) <- reference @@ -1856,7 +1892,7 @@ image = try $ do _ -> B.imageWith attr' src regLink constructor lab <|> referenceLink constructor (lab,raw) -note :: MarkdownParser (F Inlines) +note :: PandocMonad m => MarkdownParser m (F Inlines) note = try $ do guardEnabled Ext_footnotes ref <- noteMarker @@ -1872,14 +1908,14 @@ note = try $ do let contents' = runF contents st{ stateNotes' = [] } return $ B.note contents' -inlineNote :: MarkdownParser (F Inlines) +inlineNote :: PandocMonad m => MarkdownParser m (F Inlines) inlineNote = try $ do guardEnabled Ext_inline_notes char '^' contents <- inlinesInBalancedBrackets return $ B.note . B.para <$> contents -rawLaTeXInline' :: MarkdownParser (F Inlines) +rawLaTeXInline' :: PandocMonad m => MarkdownParser m (F Inlines) rawLaTeXInline' = try $ do guardEnabled Ext_raw_tex lookAhead $ char '\\' >> notFollowedBy' (string "start") -- context env @@ -1887,7 +1923,7 @@ rawLaTeXInline' = try $ do return $ return $ B.rawInline "tex" s -- "tex" because it might be context or latex -rawConTeXtEnvironment :: Parser [Char] st String +rawConTeXtEnvironment :: PandocMonad m => ParserT [Char] st m String rawConTeXtEnvironment = try $ do string "\\start" completion <- inBrackets (letter <|> digit <|> spaceChar) @@ -1896,14 +1932,14 @@ rawConTeXtEnvironment = try $ do (try $ string "\\stop" >> string completion) return $ "\\start" ++ completion ++ concat contents ++ "\\stop" ++ completion -inBrackets :: (Parser [Char] st Char) -> Parser [Char] st String +inBrackets :: PandocMonad m => (ParserT [Char] st m Char) -> ParserT [Char] st m String inBrackets parser = do char '[' contents <- many parser char ']' return $ "[" ++ contents ++ "]" -spanHtml :: MarkdownParser (F Inlines) +spanHtml :: PandocMonad m => MarkdownParser m (F Inlines) spanHtml = try $ do guardEnabled Ext_native_spans (TagOpen _ attrs, _) <- htmlTag (~== TagOpen "span" []) @@ -1918,7 +1954,7 @@ spanHtml = try $ do -> return $ B.smallcaps <$> contents _ -> return $ B.spanWith (ident, classes, keyvals) <$> contents -divHtml :: MarkdownParser (F Blocks) +divHtml :: PandocMonad m => MarkdownParser m (F Blocks) divHtml = try $ do guardEnabled Ext_native_divs (TagOpen _ attrs, rawtag) <- htmlTag (~== TagOpen "div" []) @@ -1940,7 +1976,7 @@ divHtml = try $ do else -- avoid backtracing return $ return (B.rawBlock "html" (rawtag <> bls)) <> contents -rawHtmlInline :: MarkdownParser (F Inlines) +rawHtmlInline :: PandocMonad m => MarkdownParser m (F Inlines) rawHtmlInline = do guardEnabled Ext_raw_html inHtmlBlock <- stateInHtmlBlock <$> getState @@ -1962,7 +1998,7 @@ rawHtmlInline = do emojiChars :: [Char] emojiChars = ['a'..'z'] ++ ['0'..'9'] ++ ['_','+','-'] -emoji :: MarkdownParser (F Inlines) +emoji :: PandocMonad m => MarkdownParser m (F Inlines) emoji = try $ do guardEnabled Ext_emoji char ':' @@ -1974,7 +2010,7 @@ emoji = try $ do -- Citations -cite :: MarkdownParser (F Inlines) +cite :: PandocMonad m => MarkdownParser m (F Inlines) cite = do guardEnabled Ext_citations citations <- textualCite @@ -1982,7 +2018,7 @@ cite = do return $ (flip B.cite (B.text raw)) <$> cs return citations -textualCite :: MarkdownParser (F Inlines) +textualCite :: PandocMonad m => MarkdownParser m (F Inlines) textualCite = try $ do (_, key) <- citeKey let first = Citation{ citationId = key @@ -2017,7 +2053,7 @@ textualCite = try $ do Just n -> B.str (show n) _ -> B.cite [first] $ B.str $ '@':key) -bareloc :: Citation -> MarkdownParser (F [Citation]) +bareloc :: PandocMonad m => Citation -> MarkdownParser m (F [Citation]) bareloc c = try $ do spnl char '[' @@ -2032,7 +2068,7 @@ bareloc c = try $ do rest' <- rest return $ c{ citationSuffix = B.toList suff' } : rest' -normalCite :: MarkdownParser (F [Citation]) +normalCite :: PandocMonad m => MarkdownParser m (F [Citation]) normalCite = try $ do char '[' spnl @@ -2041,7 +2077,7 @@ normalCite = try $ do char ']' return citations -suffix :: MarkdownParser (F Inlines) +suffix :: PandocMonad m => MarkdownParser m (F Inlines) suffix = try $ do hasSpace <- option False (notFollowedBy nonspaceChar >> return True) spnl @@ -2050,14 +2086,14 @@ suffix = try $ do then (B.space <>) <$> rest else rest -prefix :: MarkdownParser (F Inlines) +prefix :: PandocMonad m => MarkdownParser m (F Inlines) prefix = trimInlinesF . mconcat <$> manyTill inline (char ']' <|> liftM (const ']') (lookAhead citeKey)) -citeList :: MarkdownParser (F [Citation]) +citeList :: PandocMonad m => MarkdownParser m (F [Citation]) citeList = fmap sequence $ sepBy1 citation (try $ char ';' >> spnl) -citation :: MarkdownParser (F Citation) +citation :: PandocMonad m => MarkdownParser m (F Citation) citation = try $ do pref <- prefix (suppress_author, key) <- citeKey @@ -2075,13 +2111,13 @@ citation = try $ do , citationHash = 0 } -smart :: MarkdownParser (F Inlines) +smart :: PandocMonad m => MarkdownParser m (F Inlines) smart = do getOption readerSmart >>= guard doubleQuoted <|> singleQuoted <|> choice (map (return <$>) [apostrophe, dash, ellipses]) -singleQuoted :: MarkdownParser (F Inlines) +singleQuoted :: PandocMonad m => MarkdownParser m (F Inlines) singleQuoted = try $ do singleQuoteStart withQuoteContext InSingleQuote $ @@ -2091,7 +2127,7 @@ singleQuoted = try $ do -- doubleQuoted will handle regular double-quoted sections, as well -- as dialogues with an open double-quote without a close double-quote -- in the same paragraph. -doubleQuoted :: MarkdownParser (F Inlines) +doubleQuoted :: PandocMonad m => MarkdownParser m (F Inlines) doubleQuoted = try $ do doubleQuoteStart contents <- mconcat <$> many (try $ notFollowedBy doubleQuoteEnd >> inline) diff --git a/src/Text/Pandoc/Readers/MediaWiki.hs b/src/Text/Pandoc/Readers/MediaWiki.hs index 0dea22c53..7f45cdb2a 100644 --- a/src/Text/Pandoc/Readers/MediaWiki.hs +++ b/src/Text/Pandoc/Readers/MediaWiki.hs @@ -57,22 +57,26 @@ import Data.Char (isDigit, isSpace) import Data.Maybe (fromMaybe) import Text.Printf (printf) import Debug.Trace (trace) - -import Text.Pandoc.Error +import Control.Monad.Except (throwError) +import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) -- | Read mediawiki from an input string and return a Pandoc document. -readMediaWiki :: ReaderOptions -- ^ Reader options +readMediaWiki :: PandocMonad m + => ReaderOptions -- ^ Reader options -> String -- ^ String to parse (assuming @'\n'@ line endings) - -> Either PandocError Pandoc -readMediaWiki opts s = - readWith parseMediaWiki MWState{ mwOptions = opts - , mwMaxNestingLevel = 4 - , mwNextLinkNumber = 1 - , mwCategoryLinks = [] - , mwHeaderMap = M.empty - , mwIdentifierList = Set.empty - } - (s ++ "\n") + -> m Pandoc +readMediaWiki opts s = do + parsed <- readWithM parseMediaWiki MWState{ mwOptions = opts + , mwMaxNestingLevel = 4 + , mwNextLinkNumber = 1 + , mwCategoryLinks = [] + , mwHeaderMap = M.empty + , mwIdentifierList = Set.empty + } + (s ++ "\n") + case parsed of + Right result -> return result + Left _ -> throwError $ PandocParseError "problem parsing mediawiki" data MWState = MWState { mwOptions :: ReaderOptions , mwMaxNestingLevel :: Int @@ -82,7 +86,7 @@ data MWState = MWState { mwOptions :: ReaderOptions , mwIdentifierList :: Set.Set String } -type MWParser = Parser [Char] MWState +type MWParser m = ParserT [Char] MWState m instance HasReaderOptions MWState where extractReaderOptions = mwOptions @@ -101,7 +105,7 @@ instance HasIdentifierList MWState where -- This is used to prevent exponential blowups for things like: -- ''a'''a''a'''a''a'''a''a'''a -nested :: MWParser a -> MWParser a +nested :: PandocMonad m => MWParser m a -> MWParser m a nested p = do nestlevel <- mwMaxNestingLevel `fmap` getState guard $ nestlevel > 0 @@ -116,7 +120,7 @@ specialChars = "'[]<=&*{}|\":\\" spaceChars :: [Char] spaceChars = " \n\t" -sym :: String -> MWParser () +sym :: PandocMonad m => String -> MWParser m () sym s = () <$ try (string s) newBlockTags :: [String] @@ -137,10 +141,10 @@ eitherBlockOrInline :: [String] eitherBlockOrInline = ["applet", "button", "del", "iframe", "ins", "map", "area", "object"] -htmlComment :: MWParser () +htmlComment :: PandocMonad m => MWParser m () htmlComment = () <$ htmlTag isCommentTag -inlinesInTags :: String -> MWParser Inlines +inlinesInTags :: PandocMonad m => String -> MWParser m Inlines inlinesInTags tag = try $ do (_,raw) <- htmlTag (~== TagOpen tag []) if '/' `elem` raw -- self-closing tag @@ -148,7 +152,7 @@ inlinesInTags tag = try $ do else trimInlines . mconcat <$> manyTill inline (htmlTag (~== TagClose tag)) -blocksInTags :: String -> MWParser Blocks +blocksInTags :: PandocMonad m => String -> MWParser m Blocks blocksInTags tag = try $ do (_,raw) <- htmlTag (~== TagOpen tag []) let closer = if tag == "li" @@ -162,7 +166,7 @@ blocksInTags tag = try $ do then return mempty else mconcat <$> manyTill block closer -charsInTags :: String -> MWParser [Char] +charsInTags :: PandocMonad m => String -> MWParser m [Char] charsInTags tag = try $ do (_,raw) <- htmlTag (~== TagOpen tag []) if '/' `elem` raw -- self-closing tag @@ -173,7 +177,7 @@ charsInTags tag = try $ do -- main parser -- -parseMediaWiki :: MWParser Pandoc +parseMediaWiki :: PandocMonad m => MWParser m Pandoc parseMediaWiki = do bs <- mconcat <$> many block spaces @@ -188,7 +192,7 @@ parseMediaWiki = do -- block parsers -- -block :: MWParser Blocks +block :: PandocMonad m => MWParser m Blocks block = do tr <- getOption readerTrace pos <- getPosition @@ -209,14 +213,14 @@ block = do (take 60 $ show $ B.toList res)) (return ()) return res -para :: MWParser Blocks +para :: PandocMonad m => MWParser m Blocks para = do contents <- trimInlines . mconcat <$> many1 inline if F.all (==Space) contents then return mempty else return $ B.para contents -table :: MWParser Blocks +table :: PandocMonad m => MWParser m Blocks table = do tableStart styles <- option [] parseAttrs <* blankline @@ -244,10 +248,10 @@ table = do else (replicate cols mempty, hdr:rows') return $ B.table caption cellspecs headers rows -parseAttrs :: MWParser [(String,String)] +parseAttrs :: PandocMonad m => MWParser m [(String,String)] parseAttrs = many1 parseAttr -parseAttr :: MWParser (String, String) +parseAttr :: PandocMonad m => MWParser m (String, String) parseAttr = try $ do skipMany spaceChar k <- many1 letter @@ -256,17 +260,17 @@ parseAttr = try $ do <|> many1 (satisfy $ \c -> not (isSpace c) && c /= '|') return (k,v) -tableStart :: MWParser () +tableStart :: PandocMonad m => MWParser m () tableStart = try $ guardColumnOne *> skipSpaces *> sym "{|" -tableEnd :: MWParser () +tableEnd :: PandocMonad m => MWParser m () tableEnd = try $ guardColumnOne *> skipSpaces *> sym "|}" -rowsep :: MWParser () +rowsep :: PandocMonad m => MWParser m () rowsep = try $ guardColumnOne *> skipSpaces *> sym "|-" <* optional parseAttr <* blanklines -cellsep :: MWParser () +cellsep :: PandocMonad m => MWParser m () cellsep = try $ (guardColumnOne *> skipSpaces <* ( (char '|' <* notFollowedBy (oneOf "-}+")) @@ -276,7 +280,7 @@ cellsep = try $ <|> (() <$ try (string "||")) <|> (() <$ try (string "!!")) -tableCaption :: MWParser Inlines +tableCaption :: PandocMonad m => MWParser m Inlines tableCaption = try $ do guardColumnOne skipSpaces @@ -284,10 +288,10 @@ tableCaption = try $ do optional (try $ parseAttr *> skipSpaces *> char '|' *> skipSpaces) (trimInlines . mconcat) <$> many (notFollowedBy (cellsep <|> rowsep) *> inline) -tableRow :: MWParser [((Alignment, Double), Blocks)] +tableRow :: PandocMonad m => MWParser m [((Alignment, Double), Blocks)] tableRow = try $ skipMany htmlComment *> many tableCell -tableCell :: MWParser ((Alignment, Double), Blocks) +tableCell :: PandocMonad m => MWParser m ((Alignment, Double), Blocks) tableCell = try $ do cellsep skipMany spaceChar @@ -313,7 +317,7 @@ parseWidth s = ('%':ds) | all isDigit ds -> safeRead ('0':'.':reverse ds) _ -> Nothing -template :: MWParser String +template :: PandocMonad m => MWParser m String template = try $ do string "{{" notFollowedBy (char '{') @@ -322,7 +326,7 @@ template = try $ do contents <- manyTill chunk (try $ string "}}") return $ "{{" ++ concat contents ++ "}}" -blockTag :: MWParser Blocks +blockTag :: PandocMonad m => MWParser m Blocks blockTag = do (tag, _) <- lookAhead $ htmlTag isBlockTag' case tag of @@ -341,7 +345,7 @@ trimCode :: String -> String trimCode ('\n':xs) = stripTrailingNewlines xs trimCode xs = stripTrailingNewlines xs -syntaxhighlight :: String -> [Attribute String] -> MWParser Blocks +syntaxhighlight :: PandocMonad m => String -> [Attribute String] -> MWParser m Blocks syntaxhighlight tag attrs = try $ do let mblang = lookup "lang" attrs let mbstart = lookup "start" attrs @@ -351,13 +355,13 @@ syntaxhighlight tag attrs = try $ do contents <- charsInTags tag return $ B.codeBlockWith ("",classes,kvs) $ trimCode contents -hrule :: MWParser Blocks +hrule :: PandocMonad m => MWParser m Blocks hrule = B.horizontalRule <$ try (string "----" *> many (char '-') *> newline) -guardColumnOne :: MWParser () +guardColumnOne :: PandocMonad m => MWParser m () guardColumnOne = getPosition >>= \pos -> guard (sourceColumn pos == 1) -preformatted :: MWParser Blocks +preformatted :: PandocMonad m => MWParser m Blocks preformatted = try $ do guardColumnOne char ' ' @@ -388,7 +392,7 @@ encode = B.fromList . normalizeCode . B.toList . walk strToCode normalizeCode $ (Code a1 (x ++ y)) : zs normalizeCode (x:xs) = x : normalizeCode xs -header :: MWParser Blocks +header :: PandocMonad m => MWParser m Blocks header = try $ do guardColumnOne eqs <- many1 (char '=') @@ -398,13 +402,13 @@ header = try $ do attr <- registerHeader nullAttr contents return $ B.headerWith attr lev contents -bulletList :: MWParser Blocks +bulletList :: PandocMonad m => MWParser m Blocks bulletList = B.bulletList <$> ( many1 (listItem '*') <|> (htmlTag (~== TagOpen "ul" []) *> spaces *> many (listItem '*' <|> li) <* optional (htmlTag (~== TagClose "ul"))) ) -orderedList :: MWParser Blocks +orderedList :: PandocMonad m => MWParser m Blocks orderedList = (B.orderedList <$> many1 (listItem '#')) <|> try @@ -415,10 +419,10 @@ orderedList = let start = fromMaybe 1 $ safeRead $ fromAttrib "start" tag return $ B.orderedListWith (start, DefaultStyle, DefaultDelim) items) -definitionList :: MWParser Blocks +definitionList :: PandocMonad m => MWParser m Blocks definitionList = B.definitionList <$> many1 defListItem -defListItem :: MWParser (Inlines, [Blocks]) +defListItem :: PandocMonad m => MWParser m (Inlines, [Blocks]) defListItem = try $ do terms <- mconcat . intersperse B.linebreak <$> many defListTerm -- we allow dd with no dt, or dt with no dd @@ -429,27 +433,27 @@ defListItem = try $ do else many (listItem ':') return (terms, defs) -defListTerm :: MWParser Inlines +defListTerm :: PandocMonad m => MWParser m Inlines defListTerm = char ';' >> skipMany spaceChar >> anyLine >>= parseFromString (trimInlines . mconcat <$> many inline) -listStart :: Char -> MWParser () +listStart :: PandocMonad m => Char -> MWParser m () listStart c = char c *> notFollowedBy listStartChar -listStartChar :: MWParser Char +listStartChar :: PandocMonad m => MWParser m Char listStartChar = oneOf "*#;:" -anyListStart :: MWParser Char +anyListStart :: PandocMonad m => MWParser m Char anyListStart = char '*' <|> char '#' <|> char ':' <|> char ';' -li :: MWParser Blocks +li :: PandocMonad m => MWParser m Blocks li = lookAhead (htmlTag (~== TagOpen "li" [])) *> (firstParaToPlain <$> blocksInTags "li") <* spaces -listItem :: Char -> MWParser Blocks +listItem :: PandocMonad m => Char -> MWParser m Blocks listItem c = try $ do extras <- many (try $ char c <* lookAhead listStartChar) if null extras @@ -475,10 +479,10 @@ listItem c = try $ do -- }} -- * next list item -- which seems to be valid mediawiki. -listChunk :: MWParser String +listChunk :: PandocMonad m => MWParser m String listChunk = template <|> count 1 anyChar -listItem' :: Char -> MWParser Blocks +listItem' :: PandocMonad m => Char -> MWParser m Blocks listItem' c = try $ do listStart c skipMany spaceChar @@ -498,7 +502,7 @@ firstParaToPlain contents = -- inline parsers -- -inline :: MWParser Inlines +inline :: PandocMonad m => MWParser m Inlines inline = whitespace <|> url <|> str @@ -516,10 +520,10 @@ inline = whitespace <|> (B.rawInline "mediawiki" <$> template) <|> special -str :: MWParser Inlines +str :: PandocMonad m => MWParser m Inlines str = B.str <$> many1 (noneOf $ specialChars ++ spaceChars) -math :: MWParser Inlines +math :: PandocMonad m => MWParser m Inlines math = (B.displayMath . trim <$> try (many1 (char ':') >> charsInTags "math")) <|> (B.math . trim <$> charsInTags "math") <|> (B.displayMath . trim <$> try (dmStart *> manyTill anyChar dmEnd)) @@ -529,13 +533,13 @@ math = (B.displayMath . trim <$> try (many1 (char ':') >> charsInTags "math")) mStart = string "\\(" mEnd = try (string "\\)") -variable :: MWParser String +variable :: PandocMonad m => MWParser m String variable = try $ do string "{{{" contents <- manyTill anyChar (try $ string "}}}") return $ "{{{" ++ contents ++ "}}}" -inlineTag :: MWParser Inlines +inlineTag :: PandocMonad m => MWParser m Inlines inlineTag = do (tag, _) <- lookAhead $ htmlTag isInlineTag' case tag of @@ -557,18 +561,18 @@ inlineTag = do TagOpen "hask" _ -> B.codeWith ("",["haskell"],[]) <$> charsInTags "hask" _ -> B.rawInline "html" . snd <$> htmlTag (~== tag) -special :: MWParser Inlines +special :: PandocMonad m => MWParser m Inlines special = B.str <$> count 1 (notFollowedBy' (htmlTag isBlockTag') *> oneOf specialChars) -inlineHtml :: MWParser Inlines +inlineHtml :: PandocMonad m => MWParser m Inlines inlineHtml = B.rawInline "html" . snd <$> htmlTag isInlineTag' -whitespace :: MWParser Inlines +whitespace :: PandocMonad m => MWParser m Inlines whitespace = B.space <$ (skipMany1 spaceChar <|> htmlComment) <|> B.softbreak <$ endline -endline :: MWParser () +endline :: PandocMonad m => MWParser m () endline = () <$ try (newline <* notFollowedBy spaceChar <* notFollowedBy newline <* @@ -577,12 +581,12 @@ endline = () <$ try (newline <* notFollowedBy' header <* notFollowedBy anyListStart) -imageIdentifiers :: [MWParser ()] +imageIdentifiers :: PandocMonad m => [MWParser m ()] imageIdentifiers = [sym (identifier ++ ":") | identifier <- identifiers] where identifiers = ["File", "Image", "Archivo", "Datei", "Fichier", "Bild"] -image :: MWParser Inlines +image :: PandocMonad m => MWParser m Inlines image = try $ do sym "[[" choice imageIdentifiers @@ -600,7 +604,7 @@ image = try $ do <|> try (char '|' *> (mconcat <$> manyTill inline (sym "]]"))) return $ B.imageWith attr fname ("fig:" ++ stringify caption) caption -imageOption :: MWParser String +imageOption :: PandocMonad m => MWParser m String imageOption = try $ char '|' *> opt where opt = try (oneOfStrings [ "border", "thumbnail", "frameless" @@ -619,7 +623,7 @@ collapseUnderscores (x:xs) = x : collapseUnderscores xs addUnderscores :: String -> String addUnderscores = collapseUnderscores . intercalate "_" . words -internalLink :: MWParser Inlines +internalLink :: PandocMonad m => MWParser m Inlines internalLink = try $ do sym "[[" pagename <- unwords . words <$> many (noneOf "|]") @@ -637,7 +641,7 @@ internalLink = try $ do return mempty else return link -externalLink :: MWParser Inlines +externalLink :: PandocMonad m => MWParser m Inlines externalLink = try $ do char '[' (_, src) <- uri @@ -649,29 +653,29 @@ externalLink = try $ do return $ B.str $ show num return $ B.link src "" lab -url :: MWParser Inlines +url :: PandocMonad m => MWParser m Inlines url = do (orig, src) <- uri return $ B.link src "" (B.str orig) -- | Parses a list of inlines between start and end delimiters. -inlinesBetween :: (Show b) => MWParser a -> MWParser b -> MWParser Inlines +inlinesBetween :: (PandocMonad m, Show b) => MWParser m a -> MWParser m b -> MWParser m Inlines inlinesBetween start end = (trimInlines . mconcat) <$> try (start >> many1Till inner end) where inner = innerSpace <|> (notFollowedBy' (() <$ whitespace) >> inline) innerSpace = try $ whitespace <* notFollowedBy' end -emph :: MWParser Inlines +emph :: PandocMonad m => MWParser m Inlines emph = B.emph <$> nested (inlinesBetween start end) where start = sym "''" >> lookAhead nonspaceChar end = try $ notFollowedBy' (() <$ strong) >> sym "''" -strong :: MWParser Inlines +strong :: PandocMonad m => MWParser m Inlines strong = B.strong <$> nested (inlinesBetween start end) where start = sym "'''" >> lookAhead nonspaceChar end = try $ sym "'''" -doubleQuotes :: MWParser Inlines +doubleQuotes :: PandocMonad m => MWParser m Inlines doubleQuotes = B.doubleQuoted <$> nested (inlinesBetween openDoubleQuote closeDoubleQuote) where openDoubleQuote = sym "\"" >> lookAhead nonspaceChar closeDoubleQuote = try $ sym "\"" diff --git a/src/Text/Pandoc/Readers/Native.hs b/src/Text/Pandoc/Readers/Native.hs index 917a4a144..489ddcd4a 100644 --- a/src/Text/Pandoc/Readers/Native.hs +++ b/src/Text/Pandoc/Readers/Native.hs @@ -33,6 +33,7 @@ module Text.Pandoc.Readers.Native ( readNative ) where import Text.Pandoc.Definition import Text.Pandoc.Shared (safeRead) +import Control.Monad.Except (throwError) import Text.Pandoc.Error import Text.Pandoc.Class @@ -48,9 +49,11 @@ import Text.Pandoc.Class -- readNative :: PandocMonad m => String -- ^ String to parse (assuming @'\n'@ line endings) - -> m (Either PandocError Pandoc) + -> m Pandoc readNative s = - return $ maybe (Pandoc nullMeta <$> readBlocks s) Right (safeRead s) + case maybe (Pandoc nullMeta <$> readBlocks s) Right (safeRead s) of + Right doc -> return doc + Left _ -> throwError $ PandocParseError "couldn't read native" readBlocks :: String -> Either PandocError [Block] readBlocks s = maybe ((:[]) <$> readBlock s) Right (safeRead s) diff --git a/src/Text/Pandoc/Readers/OPML.hs b/src/Text/Pandoc/Readers/OPML.hs index 4dcf5e5a0..608e9ae0f 100644 --- a/src/Text/Pandoc/Readers/OPML.hs +++ b/src/Text/Pandoc/Readers/OPML.hs @@ -13,8 +13,9 @@ import Control.Monad.State import Data.Default import Control.Monad.Except import Text.Pandoc.Error +import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) -type OPML = ExceptT PandocError (State OPMLState) +type OPML m = StateT OPMLState m data OPMLState = OPMLState{ opmlSectionLevel :: Int @@ -30,12 +31,14 @@ instance Default OPMLState where , opmlDocDate = mempty } -readOPML :: ReaderOptions -> String -> Either PandocError Pandoc -readOPML _ inp = setTitle (opmlDocTitle st') - . setAuthors (opmlDocAuthors st') - . setDate (opmlDocDate st') - . doc . mconcat <$> bs - where (bs, st') = flip runState def . runExceptT $ (mapM parseBlock $ normalizeTree $ parseXML inp) +readOPML :: PandocMonad m => ReaderOptions -> String -> m Pandoc +readOPML _ inp = do + (bs, st') <- flip runStateT def (mapM parseBlock $ normalizeTree $ parseXML inp) + return $ + setTitle (opmlDocTitle st') $ + setAuthors (opmlDocAuthors st') $ + setDate (opmlDocDate st') $ + doc $ mconcat bs -- normalize input, consolidating adjacent Text and CRef elements normalizeTree :: [Content] -> [Content] @@ -62,21 +65,22 @@ attrValue attr elt = Just z -> z Nothing -> "" -exceptT :: Either PandocError a -> OPML a -exceptT = either throwError return +-- exceptT :: PandocMonad m => Either PandocExecutionError a -> OPML m a +-- exceptT = either throwError return -asHtml :: String -> OPML Inlines -asHtml s = (\(Pandoc _ bs) -> case bs of +asHtml :: PandocMonad m => String -> OPML m Inlines +asHtml s = + (\(Pandoc _ bs) -> case bs of [Plain ils] -> fromList ils - _ -> mempty) <$> exceptT (readHtml def s) + _ -> mempty) <$> (lift $ readHtml def s) -asMarkdown :: String -> OPML Blocks -asMarkdown s = (\(Pandoc _ bs) -> fromList bs) <$> exceptT (readMarkdown def s) +asMarkdown :: PandocMonad m => String -> OPML m Blocks +asMarkdown s = (\(Pandoc _ bs) -> fromList bs) <$> (lift $ readMarkdown def s) -getBlocks :: Element -> OPML Blocks +getBlocks :: PandocMonad m => Element -> OPML m Blocks getBlocks e = mconcat <$> (mapM parseBlock $ elContent e) -parseBlock :: Content -> OPML Blocks +parseBlock :: PandocMonad m => Content -> OPML m Blocks parseBlock (Elem e) = case qName (elName e) of "ownerName" -> mempty <$ modify (\st -> diff --git a/src/Text/Pandoc/Readers/Odt.hs b/src/Text/Pandoc/Readers/Odt.hs index 046fb4d6d..898dda077 100644 --- a/src/Text/Pandoc/Readers/Odt.hs +++ b/src/Text/Pandoc/Readers/Odt.hs @@ -39,6 +39,10 @@ import qualified Data.ByteString.Lazy as B import System.FilePath +import Control.Monad.Except (throwError) + +import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) +import qualified Text.Pandoc.Class as P import Text.Pandoc.Definition import Text.Pandoc.Error import Text.Pandoc.Options @@ -52,11 +56,21 @@ import Text.Pandoc.Readers.Odt.Generic.XMLConverter import Text.Pandoc.Readers.Odt.Generic.Fallible import Text.Pandoc.Shared (filteredFilesFromArchive) --- -readOdt :: ReaderOptions +readOdt :: PandocMonad m + => ReaderOptions -> B.ByteString - -> Either PandocError (Pandoc, MediaBag) -readOdt _ bytes = bytesToOdt bytes-- of + -> m Pandoc +readOdt opts bytes = case readOdt' opts bytes of + Right (doc, mb) -> do + P.setMediaBag mb + return doc + Left _ -> throwError $ PandocParseError "couldn't parse odt" + +-- +readOdt' :: ReaderOptions + -> B.ByteString + -> Either PandocError (Pandoc, MediaBag) +readOdt' _ bytes = bytesToOdt bytes-- of -- Right (pandoc, mediaBag) -> Right (pandoc , mediaBag) -- Left err -> Left err diff --git a/src/Text/Pandoc/Readers/Org.hs b/src/Text/Pandoc/Readers/Org.hs index 4e1c926da..3a41ed317 100644 --- a/src/Text/Pandoc/Readers/Org.hs +++ b/src/Text/Pandoc/Readers/Org.hs @@ -31,24 +31,30 @@ import Text.Pandoc.Readers.Org.Blocks ( blockList, meta ) import Text.Pandoc.Readers.Org.Parsing ( OrgParser, readWithM ) import Text.Pandoc.Readers.Org.ParserState ( optionsToParserState ) +import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) import Text.Pandoc.Definition -import Text.Pandoc.Error import Text.Pandoc.Options -import Control.Monad.Reader ( runReader ) +import Control.Monad.Except ( throwError ) +import Control.Monad.Reader ( runReaderT ) -- | Parse org-mode string and return a Pandoc document. -readOrg :: ReaderOptions -- ^ Reader options +readOrg :: PandocMonad m + => ReaderOptions -- ^ Reader options -> String -- ^ String to parse (assuming @'\n'@ line endings) - -> Either PandocError Pandoc -readOrg opts s = flip runReader def $ - readWithM parseOrg (optionsToParserState opts) (s ++ "\n\n") + -> m Pandoc +readOrg opts s = do + parsed <- flip runReaderT def $ + readWithM parseOrg (optionsToParserState opts) (s ++ "\n\n") + case parsed of + Right result -> return result + Left _ -> throwError $ PandocParseError "problem parsing org" -- -- Parser -- -parseOrg :: OrgParser Pandoc +parseOrg :: PandocMonad m => OrgParser m Pandoc parseOrg = do blocks' <- blockList meta' <- meta diff --git a/src/Text/Pandoc/Readers/Org/BlockStarts.hs b/src/Text/Pandoc/Readers/Org/BlockStarts.hs index b1004dda6..5588c4552 100644 --- a/src/Text/Pandoc/Readers/Org/BlockStarts.hs +++ b/src/Text/Pandoc/Readers/Org/BlockStarts.hs @@ -44,7 +44,7 @@ import Control.Monad ( void ) import Text.Pandoc.Readers.Org.Parsing -- | Horizontal Line (five -- dashes or more) -hline :: OrgParser () +hline :: Monad m => OrgParser m () hline = try $ do skipSpaces string "-----" @@ -54,58 +54,59 @@ hline = try $ do return () -- | Read the start of a header line, return the header level -headerStart :: OrgParser Int +headerStart :: Monad m => OrgParser m Int headerStart = try $ (length <$> many1 (char '*')) <* many1 (char ' ') <* updateLastPreCharPos -tableStart :: OrgParser Char +tableStart :: Monad m => OrgParser m Char tableStart = try $ skipSpaces *> char '|' -latexEnvStart :: OrgParser String +latexEnvStart :: Monad m => OrgParser m String latexEnvStart = try $ do skipSpaces *> string "\\begin{" *> latexEnvName <* string "}" <* blankline where - latexEnvName :: OrgParser String + latexEnvName :: Monad m => OrgParser m String latexEnvName = try $ mappend <$> many1 alphaNum <*> option "" (string "*") -- | Parses bullet list marker. -bulletListStart :: OrgParser () +bulletListStart :: Monad m => OrgParser m () bulletListStart = try $ choice [ () <$ skipSpaces <* oneOf "+-" <* skipSpaces1 , () <$ skipSpaces1 <* char '*' <* skipSpaces1 ] -genericListStart :: OrgParser String - -> OrgParser Int +genericListStart :: Monad m + => OrgParser m String + -> OrgParser m Int genericListStart listMarker = try $ (+) <$> (length <$> many spaceChar) <*> (length <$> listMarker <* many1 spaceChar) -orderedListStart :: OrgParser Int +orderedListStart :: Monad m => OrgParser m Int orderedListStart = genericListStart orderedListMarker -- Ordered list markers allowed in org-mode where orderedListMarker = mappend <$> many1 digit <*> (pure <$> oneOf ".)") -drawerStart :: OrgParser String +drawerStart :: Monad m => OrgParser m String drawerStart = try $ skipSpaces *> drawerName <* skipSpaces <* newline where drawerName = char ':' *> manyTill nonspaceChar (char ':') -metaLineStart :: OrgParser () +metaLineStart :: Monad m => OrgParser m () metaLineStart = try $ skipSpaces <* string "#+" -commentLineStart :: OrgParser () +commentLineStart :: Monad m => OrgParser m () commentLineStart = try $ skipSpaces <* string "# " -exampleLineStart :: OrgParser () +exampleLineStart :: Monad m => OrgParser m () exampleLineStart = () <$ try (skipSpaces *> string ": ") -noteMarker :: OrgParser String +noteMarker :: Monad m => OrgParser m String noteMarker = try $ do char '[' choice [ many1Till digit (char ']') @@ -114,12 +115,12 @@ noteMarker = try $ do ] -- | Succeeds if the parser is at the end of a block. -endOfBlock :: OrgParser () +endOfBlock :: Monad m => OrgParser m () endOfBlock = lookAhead . try $ do void blankline <|> anyBlockStart where -- Succeeds if there is a new block starting at this position. - anyBlockStart :: OrgParser () + anyBlockStart :: Monad m => OrgParser m () anyBlockStart = try . choice $ [ exampleLineStart , hline diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index 484d97482..5176e0f6c 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -44,6 +44,7 @@ import Text.Pandoc.Readers.Org.Shared import qualified Text.Pandoc.Builder as B import Text.Pandoc.Builder ( Inlines, Blocks ) +import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Shared ( compactify', compactify'DL, safeRead ) @@ -105,7 +106,7 @@ data Headline = Headline -- | Read an Org mode headline and its contents (i.e. a document subtree). -- @lvl@ gives the minimum acceptable level of the tree. -headline :: Int -> OrgParser (F Headline) +headline :: PandocMonad m => Int -> OrgParser m (F Headline) headline lvl = try $ do level <- headerStart guard (lvl <= level) @@ -130,16 +131,16 @@ headline lvl = try $ do , headlineChildren = children' } where - endOfTitle :: OrgParser () + endOfTitle :: Monad m => OrgParser m () endOfTitle = void . lookAhead $ optional headerTags *> newline - headerTags :: OrgParser [Tag] + headerTags :: Monad m => OrgParser m [Tag] headerTags = try $ let tag = many1 (alphaNum <|> oneOf "@%#_") <* char ':' in map toTag <$> (skipSpaces *> char ':' *> many1 tag <* skipSpaces) -- | Convert an Org mode headline (i.e. a document tree) into pandoc's Blocks -headlineToBlocks :: Headline -> OrgParser Blocks +headlineToBlocks :: Monad m => Headline -> OrgParser m Blocks headlineToBlocks hdln@(Headline {..}) = do maxHeadlineLevels <- getExportSetting exportHeadlineLevels case () of @@ -162,7 +163,7 @@ isCommentTitle :: Inlines -> Bool isCommentTitle (B.toList -> (Str "COMMENT":_)) = True isCommentTitle _ = False -archivedHeadlineToBlocks :: Headline -> OrgParser Blocks +archivedHeadlineToBlocks :: Monad m => Headline -> OrgParser m Blocks archivedHeadlineToBlocks hdln = do archivedTreesOption <- getExportSetting exportArchivedTrees case archivedTreesOption of @@ -170,7 +171,7 @@ archivedHeadlineToBlocks hdln = do ArchivedTreesExport -> headlineToHeaderWithContents hdln ArchivedTreesHeadlineOnly -> headlineToHeader hdln -headlineToHeaderWithList :: Headline -> OrgParser Blocks +headlineToHeaderWithList :: Monad m => Headline -> OrgParser m Blocks headlineToHeaderWithList hdln@(Headline {..}) = do maxHeadlineLevels <- getExportSetting exportHeadlineLevels header <- headlineToHeader hdln @@ -189,13 +190,13 @@ headlineToHeaderWithList hdln@(Headline {..}) = do (Header _ _ inlns:_) -> B.para (B.fromList inlns) _ -> mempty -headlineToHeaderWithContents :: Headline -> OrgParser Blocks +headlineToHeaderWithContents :: Monad m => Headline -> OrgParser m Blocks headlineToHeaderWithContents hdln@(Headline {..}) = do header <- headlineToHeader hdln childrenBlocks <- mconcat <$> sequence (map headlineToBlocks headlineChildren) return $ header <> headlineContents <> childrenBlocks -headlineToHeader :: Headline -> OrgParser Blocks +headlineToHeader :: Monad m => Headline -> OrgParser m Blocks headlineToHeader (Headline {..}) = do exportTodoKeyword <- getExportSetting exportWithTodoKeywords let todoText = if exportTodoKeyword @@ -208,7 +209,7 @@ headlineToHeader (Headline {..}) = do attr <- registerHeader propAttr headlineText return $ B.headerWith attr headlineLevel text -todoKeyword :: OrgParser TodoMarker +todoKeyword :: Monad m => OrgParser m TodoMarker todoKeyword = try $ do taskStates <- activeTodoMarkers <$> getState let kwParser tdm = try $ (tdm <$ string (todoMarkerName tdm) <* spaceChar) @@ -250,7 +251,7 @@ tagToInline t = B.spanWith ("", ["tag"], [("data-tag-name", fromTag t)]) mempty -- -- | Get a list of blocks. -blockList :: OrgParser [Block] +blockList :: PandocMonad m => OrgParser m [Block] blockList = do initialBlocks <- blocks headlines <- sequence <$> manyTill (headline 1) eof @@ -259,15 +260,15 @@ blockList = do return . B.toList $ (runF initialBlocks st) <> headlineBlocks -- | Get the meta information safed in the state. -meta :: OrgParser Meta +meta :: Monad m => OrgParser m Meta meta = do meta' <- metaExport runF meta' <$> getState -blocks :: OrgParser (F Blocks) +blocks :: PandocMonad m => OrgParser m (F Blocks) blocks = mconcat <$> manyTill block (void (lookAhead headerStart) <|> eof) -block :: OrgParser (F Blocks) +block :: PandocMonad m => OrgParser m (F Blocks) block = choice [ mempty <$ blanklines , table , orgBlock @@ -306,7 +307,7 @@ attrFromBlockAttributes (BlockAttributes{..}) = kv = filter ((`notElem` ["id", "class"]) . fst) blockAttrKeyValues in (ident, classes, kv) -stringyMetaAttribute :: (String -> Bool) -> OrgParser (String, String) +stringyMetaAttribute :: Monad m => (String -> Bool) -> OrgParser m (String, String) stringyMetaAttribute attrCheck = try $ do metaLineStart attrName <- map toUpper <$> many1Till nonspaceChar (char ':') @@ -315,7 +316,7 @@ stringyMetaAttribute attrCheck = try $ do attrValue <- anyLine return (attrName, attrValue) -blockAttributes :: OrgParser BlockAttributes +blockAttributes :: PandocMonad m => OrgParser m BlockAttributes blockAttributes = try $ do kv <- many (stringyMetaAttribute attrCheck) let caption = foldl' (appendValues "CAPTION") Nothing kv @@ -350,17 +351,17 @@ blockAttributes = try $ do Just acc -> Just $ acc ++ ' ':value Nothing -> Just value -keyValues :: OrgParser [(String, String)] +keyValues :: Monad m => OrgParser m [(String, String)] keyValues = try $ manyTill ((,) <$> key <*> value) newline where - key :: OrgParser String + key :: Monad m => OrgParser m String key = try $ skipSpaces *> char ':' *> many1 nonspaceChar - value :: OrgParser String + value :: Monad m => OrgParser m String value = skipSpaces *> manyTill anyChar endOfValue - endOfValue :: OrgParser () + endOfValue :: Monad m => OrgParser m () endOfValue = lookAhead $ (() <$ try (many1 spaceChar <* key)) <|> () <$ newline @@ -371,7 +372,7 @@ keyValues = try $ -- -- | Read an org-mode block delimited by #+BEGIN_TYPE and #+END_TYPE. -orgBlock :: OrgParser (F Blocks) +orgBlock :: PandocMonad m => OrgParser m (F Blocks) orgBlock = try $ do blockAttrs <- blockAttributes blkType <- blockHeaderStart @@ -390,25 +391,25 @@ orgBlock = try $ do let (ident, classes, kv) = attrFromBlockAttributes blockAttrs in fmap $ B.divWith (ident, classes ++ [blkType], kv) where - blockHeaderStart :: OrgParser String + blockHeaderStart :: Monad m => OrgParser m String blockHeaderStart = try $ skipSpaces *> stringAnyCase "#+begin_" *> orgArgWord lowercase :: String -> String lowercase = map toLower -rawBlockLines :: (String -> F Blocks) -> String -> OrgParser (F Blocks) +rawBlockLines :: Monad m => (String -> F Blocks) -> String -> OrgParser m (F Blocks) rawBlockLines f blockType = (ignHeaders *> (f <$> rawBlockContent blockType)) -parseBlockLines :: (F Blocks -> F Blocks) -> String -> OrgParser (F Blocks) +parseBlockLines :: PandocMonad m => (F Blocks -> F Blocks) -> String -> OrgParser m (F Blocks) parseBlockLines f blockType = (ignHeaders *> (f <$> parsedBlockContent)) where - parsedBlockContent :: OrgParser (F Blocks) + parsedBlockContent :: PandocMonad m => OrgParser m (F Blocks) parsedBlockContent = try $ do raw <- rawBlockContent blockType parseFromString blocks (raw ++ "\n") -- | Read the raw string content of a block -rawBlockContent :: String -> OrgParser String +rawBlockContent :: Monad m => String -> OrgParser m String rawBlockContent blockType = try $ do blkLines <- manyTill rawLine blockEnder tabLen <- getOption readerTabStop @@ -418,10 +419,10 @@ rawBlockContent blockType = try $ do . map (tabsToSpaces tabLen . commaEscaped) $ blkLines where - rawLine :: OrgParser String + rawLine :: Monad m => OrgParser m String rawLine = try $ ("" <$ blankline) <|> anyLine - blockEnder :: OrgParser () + blockEnder :: Monad m => OrgParser m () blockEnder = try $ skipSpaces <* stringAnyCase ("#+end_" <> blockType) stripIndent :: [String] -> [String] @@ -448,18 +449,18 @@ rawBlockContent blockType = try $ do commaEscaped cs = cs -- | Read but ignore all remaining block headers. -ignHeaders :: OrgParser () +ignHeaders :: Monad m => OrgParser m () ignHeaders = (() <$ newline) <|> (() <$ anyLine) -- | Read a block containing code intended for export in specific backends -- only. -exportBlock :: String -> OrgParser (F Blocks) +exportBlock :: Monad m => String -> OrgParser m (F Blocks) exportBlock blockType = try $ do exportType <- skipSpaces *> orgArgWord <* ignHeaders contents <- rawBlockContent blockType returnF (B.rawBlock (map toLower exportType) contents) -verseBlock :: String -> OrgParser (F Blocks) +verseBlock :: PandocMonad m => String -> OrgParser m (F Blocks) verseBlock blockType = try $ do ignHeaders content <- rawBlockContent blockType @@ -468,7 +469,7 @@ verseBlock blockType = try $ do where -- replace initial spaces with nonbreaking spaces to preserve -- indentation, parse the rest as normal inline - parseVerseLine :: String -> OrgParser (F Inlines) + parseVerseLine :: PandocMonad m => String -> OrgParser m (F Inlines) parseVerseLine cs = do let (initialSpaces, indentedLine) = span isSpace cs let nbspIndent = if null initialSpaces @@ -480,7 +481,7 @@ verseBlock blockType = try $ do -- | Read a code block and the associated results block if present. Which of -- boths blocks is included in the output is determined using the "exports" -- argument in the block header. -codeBlock :: BlockAttributes -> String -> OrgParser (F Blocks) +codeBlock :: PandocMonad m => BlockAttributes -> String -> OrgParser m (F Blocks) codeBlock blockAttrs blockType = do skipSpaces (classes, kv) <- codeHeaderArgs <|> (mempty <$ ignHeaders) @@ -513,7 +514,7 @@ exportsResults :: [(String, String)] -> Bool exportsResults attrs = ("rundoc-exports", "results") `elem` attrs || ("rundoc-exports", "both") `elem` attrs -trailingResultsBlock :: OrgParser (Maybe (F Blocks)) +trailingResultsBlock :: PandocMonad m => OrgParser m (Maybe (F Blocks)) trailingResultsBlock = optionMaybe . try $ do blanklines stringAnyCase "#+RESULTS:" @@ -522,7 +523,7 @@ trailingResultsBlock = optionMaybe . try $ do -- | Parse code block arguments -- TODO: We currently don't handle switches. -codeHeaderArgs :: OrgParser ([String], [(String, String)]) +codeHeaderArgs :: Monad m => OrgParser m ([String], [(String, String)]) codeHeaderArgs = try $ do language <- skipSpaces *> orgArgWord _ <- skipSpaces *> (try $ switch `sepBy` (many1 spaceChar)) @@ -537,27 +538,27 @@ codeHeaderArgs = try $ do where hasRundocParameters = not . null -switch :: OrgParser (Char, Maybe String) +switch :: Monad m => OrgParser m (Char, Maybe String) switch = try $ simpleSwitch <|> lineNumbersSwitch where simpleSwitch = (\c -> (c, Nothing)) <$> (oneOf "-+" *> letter) lineNumbersSwitch = (\ls -> ('l', Just ls)) <$> (string "-l \"" *> many1Till nonspaceChar (char '"')) -blockOption :: OrgParser (String, String) +blockOption :: Monad m => OrgParser m (String, String) blockOption = try $ do argKey <- orgArgKey paramValue <- option "yes" orgParamValue return (argKey, paramValue) -orgParamValue :: OrgParser String +orgParamValue :: Monad m => OrgParser m String orgParamValue = try $ skipSpaces *> notFollowedBy (char ':' ) *> many1 nonspaceChar <* skipSpaces -horizontalRule :: OrgParser (F Blocks) +horizontalRule :: Monad m => OrgParser m (F Blocks) horizontalRule = return B.horizontalRule <$ try hline @@ -568,7 +569,7 @@ horizontalRule = return B.horizontalRule <$ try hline -- | A generic drawer which has no special meaning for org-mode. -- Whether or not this drawer is included in the output depends on the drawers -- export setting. -genericDrawer :: OrgParser (F Blocks) +genericDrawer :: PandocMonad m => OrgParser m (F Blocks) genericDrawer = try $ do name <- map toUpper <$> drawerStart content <- manyTill drawerLine (try drawerEnd) @@ -582,35 +583,35 @@ genericDrawer = try $ do Right names | name `notElem` names -> return mempty _ -> drawerDiv name <$> parseLines content where - parseLines :: [String] -> OrgParser (F Blocks) + parseLines :: PandocMonad m => [String] -> OrgParser m (F Blocks) parseLines = parseFromString blocks . (++ "\n") . unlines drawerDiv :: String -> F Blocks -> F Blocks drawerDiv drawerName = fmap $ B.divWith (mempty, [drawerName, "drawer"], mempty) -drawerLine :: OrgParser String +drawerLine :: Monad m => OrgParser m String drawerLine = anyLine -drawerEnd :: OrgParser String +drawerEnd :: Monad m => OrgParser m String drawerEnd = try $ skipSpaces *> stringAnyCase ":END:" <* skipSpaces <* newline -- | Read a :PROPERTIES: drawer and return the key/value pairs contained -- within. -propertiesDrawer :: OrgParser Properties +propertiesDrawer :: Monad m => OrgParser m Properties propertiesDrawer = try $ do drawerType <- drawerStart guard $ map toUpper drawerType == "PROPERTIES" manyTill property (try drawerEnd) where - property :: OrgParser (PropertyKey, PropertyValue) + property :: Monad m => OrgParser m (PropertyKey, PropertyValue) property = try $ (,) <$> key <*> value - key :: OrgParser PropertyKey + key :: Monad m => OrgParser m PropertyKey key = fmap toPropertyKey . try $ skipSpaces *> char ':' *> many1Till nonspaceChar (char ':') - value :: OrgParser PropertyValue + value :: Monad m => OrgParser m PropertyValue value = fmap toPropertyValue . try $ skipSpaces *> manyTill anyChar (try $ skipSpaces *> newline) @@ -621,7 +622,7 @@ propertiesDrawer = try $ do -- | Figures or an image paragraph (i.e. an image on a line by itself). Only -- images with a caption attribute are interpreted as figures. -figure :: OrgParser (F Blocks) +figure :: PandocMonad m => OrgParser m (F Blocks) figure = try $ do figAttrs <- blockAttributes src <- skipSpaces *> selfTarget <* skipSpaces <* endOfParagraph @@ -632,7 +633,7 @@ figure = try $ do let isFigure = not . isNothing $ blockAttrCaption figAttrs return $ imageBlock isFigure figAttrs imgSrc where - selfTarget :: OrgParser String + selfTarget :: PandocMonad m => OrgParser m String selfTarget = try $ char '[' *> linkTarget <* char ']' imageBlock :: Bool -> BlockAttributes -> String -> F Blocks @@ -654,7 +655,7 @@ figure = try $ do else "fig:" ++ cs -- | Succeeds if looking at the end of the current paragraph -endOfParagraph :: OrgParser () +endOfParagraph :: Monad m => OrgParser m () endOfParagraph = try $ skipSpaces *> newline *> endOfBlock @@ -663,11 +664,11 @@ endOfParagraph = try $ skipSpaces *> newline *> endOfBlock -- -- | Example code marked up by a leading colon. -example :: OrgParser (F Blocks) +example :: Monad m => OrgParser m (F Blocks) example = try $ do return . return . exampleCode =<< unlines <$> many1 exampleLine where - exampleLine :: OrgParser String + exampleLine :: Monad m => OrgParser m String exampleLine = try $ exampleLineStart *> anyLine exampleCode :: String -> Blocks @@ -678,10 +679,10 @@ exampleCode = B.codeBlockWith ("", ["example"], []) -- Comments, Options and Metadata -- -specialLine :: OrgParser (F Blocks) +specialLine :: PandocMonad m => OrgParser m (F Blocks) specialLine = fmap return . try $ rawExportLine <|> metaLine <|> commentLine -rawExportLine :: OrgParser Blocks +rawExportLine :: PnadocMonad m => OrgParser m Blocks rawExportLine = try $ do metaLineStart key <- metaKey @@ -689,7 +690,7 @@ rawExportLine = try $ do then B.rawBlock key <$> anyLine else mzero -commentLine :: OrgParser Blocks +commentLine :: Monad m => OrgParser m Blocks commentLine = commentLineStart *> anyLine *> pure mempty @@ -718,7 +719,7 @@ data OrgTable = OrgTable , orgTableRows :: [[Blocks]] } -table :: OrgParser (F Blocks) +table :: PandocMonad m => OrgParser m (F Blocks) table = try $ do blockAttrs <- blockAttributes lookAhead tableStart @@ -745,18 +746,18 @@ orgToPandocTable (OrgTable colProps heads lns) caption = <*> totalWidth in (align', width') -tableRows :: OrgParser [OrgTableRow] +tableRows :: PandocMonad m => OrgParser m [OrgTableRow] tableRows = try $ many (tableAlignRow <|> tableHline <|> tableContentRow) -tableContentRow :: OrgParser OrgTableRow +tableContentRow :: PandocMonad m => OrgParser m OrgTableRow tableContentRow = try $ OrgContentRow . sequence <$> (tableStart *> many1Till tableContentCell newline) -tableContentCell :: OrgParser (F Blocks) +tableContentCell :: PandocMonad m => OrgParser m (F Blocks) tableContentCell = try $ fmap B.plain . trimInlinesF . mconcat <$> manyTill inline endOfCell -tableAlignRow :: OrgParser OrgTableRow +tableAlignRow :: Monad m => OrgParser m OrgTableRow tableAlignRow = try $ do tableStart colProps <- many1Till columnPropertyCell newline @@ -764,7 +765,7 @@ tableAlignRow = try $ do guard $ any (/= def) colProps return $ OrgAlignRow colProps -columnPropertyCell :: OrgParser ColumnProperty +columnPropertyCell :: Monad m => OrgParser m ColumnProperty columnPropertyCell = emptyCell <|> propCell "alignment info" where emptyCell = ColumnProperty Nothing Nothing <$ (try $ skipSpaces *> endOfCell) @@ -776,18 +777,18 @@ columnPropertyCell = emptyCell <|> propCell "alignment info" <* char '>' <* emptyCell) -tableAlignFromChar :: OrgParser Alignment +tableAlignFromChar :: Monad m => OrgParser m Alignment tableAlignFromChar = try $ choice [ char 'l' *> return AlignLeft , char 'c' *> return AlignCenter , char 'r' *> return AlignRight ] -tableHline :: OrgParser OrgTableRow +tableHline :: Monad m => OrgParser m OrgTableRow tableHline = try $ OrgHlineRow <$ (tableStart *> char '-' *> anyLine) -endOfCell :: OrgParser Char +endOfCell :: Monad m => OrgParser m Char endOfCell = try $ char '|' <|> lookAhead newline rowsToTable :: [OrgTableRow] @@ -840,7 +841,7 @@ rowToContent orgTable row = -- -- LaTeX fragments -- -latexFragment :: OrgParser (F Blocks) +latexFragment :: Monad m => OrgParser m (F Blocks) latexFragment = try $ do envName <- latexEnvStart content <- mconcat <$> manyTill anyLineNewline (latexEnd envName) @@ -851,7 +852,7 @@ latexFragment = try $ do , "\\end{", e, "}\n" ] -latexEnd :: String -> OrgParser () +latexEnd :: Monad m => String -> OrgParser m () latexEnd envName = try $ () <$ skipSpaces <* string ("\\end{" ++ envName ++ "}") @@ -861,7 +862,7 @@ latexEnd envName = try $ -- -- Footnote defintions -- -noteBlock :: OrgParser (F Blocks) +noteBlock :: PandocMonad m => OrgParser m (F Blocks) noteBlock = try $ do ref <- noteMarker <* skipSpaces content <- mconcat <$> blocksTillHeaderOrNote @@ -873,7 +874,7 @@ noteBlock = try $ do <|> () <$ lookAhead headerStart) -- Paragraphs or Plain text -paraOrPlain :: OrgParser (F Blocks) +paraOrPlain :: PandocMonad m => OrgParser m (F Blocks) paraOrPlain = try $ do -- Make sure we are not looking at a headline notFollowedBy' (char '*' *> (oneOf " *")) @@ -892,24 +893,24 @@ paraOrPlain = try $ do -- list blocks -- -list :: OrgParser (F Blocks) +list :: PandocMonad m => OrgParser m (F Blocks) list = choice [ definitionList, bulletList, orderedList ] "list" -definitionList :: OrgParser (F Blocks) +definitionList :: PandocMonad m => OrgParser m (F Blocks) definitionList = try $ do n <- lookAhead (bulletListStart' Nothing) fmap B.definitionList . fmap compactify'DL . sequence <$> many1 (definitionListItem $ bulletListStart' (Just n)) -bulletList :: OrgParser (F Blocks) +bulletList :: PandocMonad m => OrgParser m (F Blocks) bulletList = try $ do n <- lookAhead (bulletListStart' Nothing) fmap B.bulletList . fmap compactify' . sequence <$> many1 (listItem (bulletListStart' $ Just n)) -orderedList :: OrgParser (F Blocks) +orderedList :: PandocMonad m => OrgParser m (F Blocks) orderedList = fmap B.orderedList . fmap compactify' . sequence <$> many1 (listItem orderedListStart) -bulletListStart' :: Maybe Int -> OrgParser Int +bulletListStart' :: Monad m => Maybe Int -> OrgParser m Int -- returns length of bulletList prefix, inclusive of marker bulletListStart' Nothing = do ind <- length <$> many spaceChar oneOf (bullets $ ind == 0) @@ -925,8 +926,9 @@ bulletListStart' (Just n) = do count (n-1) spaceChar bullets :: Bool -> String bullets unindented = if unindented then "+-" else "*+-" -definitionListItem :: OrgParser Int - -> OrgParser (F (Inlines, [Blocks])) +definitionListItem :: PandocMonad m + => OrgParser m Int + -> OrgParser m (F (Inlines, [Blocks])) definitionListItem parseMarkerGetLength = try $ do markerLength <- parseMarkerGetLength term <- manyTill (noneOf "\n\r") (try definitionMarker) @@ -942,8 +944,9 @@ definitionListItem parseMarkerGetLength = try $ do -- parse raw text for one list item, excluding start marker and continuations -listItem :: OrgParser Int - -> OrgParser (F Blocks) +listItem :: PandocMonad m + => OrgParser m Int + -> OrgParser m (F Blocks) listItem start = try . withContext ListItemState $ do markerLength <- try start firstLine <- anyLineNewline @@ -953,8 +956,8 @@ listItem start = try . withContext ListItemState $ do -- continuation of a list item - indented and separated by blankline or endline. -- Note: nested lists are parsed as continuations. -listContinuation :: Int - -> OrgParser String +listContinuation :: Monad m => Int + -> OrgParser m String listContinuation markerLength = try $ notFollowedBy' blankline *> (mappend <$> (concat <$> many1 listLine) @@ -963,7 +966,7 @@ listContinuation markerLength = try $ listLine = try $ indentWith markerLength *> anyLineNewline -- indent by specified number of spaces (or equiv. tabs) - indentWith :: Int -> OrgParser String + indentWith :: Monad m => Int -> OrgParser m String indentWith num = do tabStop <- getOption readerTabStop if num < tabStop @@ -972,5 +975,5 @@ listContinuation markerLength = try $ , try (char '\t' >> count (num - tabStop) (char ' ')) ] -- | Parse any line, include the final newline in the output. -anyLineNewline :: OrgParser String +anyLineNewline :: Monad m => OrgParser m String anyLineNewline = (++ "\n") <$> anyLine diff --git a/src/Text/Pandoc/Readers/Org/ExportSettings.hs b/src/Text/Pandoc/Readers/Org/ExportSettings.hs index 764e5b0d5..391877c03 100644 --- a/src/Text/Pandoc/Readers/Org/ExportSettings.hs +++ b/src/Text/Pandoc/Readers/Org/ExportSettings.hs @@ -37,14 +37,14 @@ import Data.Char ( toLower ) import Data.Maybe ( listToMaybe ) -- | Read and handle space separated org-mode export settings. -exportSettings :: OrgParser () +exportSettings :: Monad m => OrgParser m () exportSettings = void $ sepBy spaces exportSetting -- | Setter function for export settings. type ExportSettingSetter a = a -> ExportSettings -> ExportSettings -- | Read and process a single org-mode export option. -exportSetting :: OrgParser () +exportSetting :: Monad m => OrgParser m () exportSetting = choice [ booleanSetting "^" (\val es -> es { exportSubSuperscripts = val }) , booleanSetting "'" (\val es -> es { exportSmartQuotes = val }) @@ -81,10 +81,11 @@ exportSetting = choice , ignoredSetting "|" ] "export setting" -genericExportSetting :: OrgParser a +genericExportSetting :: Monad m + => OrgParser m a -> String -> ExportSettingSetter a - -> OrgParser () + -> OrgParser m () genericExportSetting optionParser settingIdentifier setter = try $ do _ <- string settingIdentifier *> char ':' value <- optionParser @@ -94,11 +95,11 @@ genericExportSetting optionParser settingIdentifier setter = try $ do st { orgStateExportSettings = setter val . orgStateExportSettings $ st } -- | A boolean option, either nil (False) or non-nil (True). -booleanSetting :: String -> ExportSettingSetter Bool -> OrgParser () +booleanSetting :: Monad m => String -> ExportSettingSetter Bool -> OrgParser m () booleanSetting = genericExportSetting elispBoolean -- | An integer-valued option. -integerSetting :: String -> ExportSettingSetter Int -> OrgParser () +integerSetting :: Monad m => String -> ExportSettingSetter Int -> OrgParser m () integerSetting = genericExportSetting parseInt where parseInt = try $ @@ -106,9 +107,10 @@ integerSetting = genericExportSetting parseInt -- | Either the string "headline" or an elisp boolean and treated as an -- @ArchivedTreesOption@. -archivedTreeSetting :: String +archivedTreeSetting :: Monad m + => String -> ExportSettingSetter ArchivedTreesOption - -> OrgParser () + -> OrgParser m () archivedTreeSetting = genericExportSetting $ archivedTreesHeadlineSetting <|> archivedTreesBoolean where @@ -125,9 +127,10 @@ archivedTreeSetting = else ArchivedTreesNoExport -- | A list or a complement list (i.e. a list starting with `not`). -complementableListSetting :: String +complementableListSetting :: Monad m + => String -> ExportSettingSetter (Either [String] [String]) - -> OrgParser () + -> OrgParser m () complementableListSetting = genericExportSetting $ choice [ Left <$> complementStringList , Right <$> stringList @@ -135,31 +138,31 @@ complementableListSetting = genericExportSetting $ choice ] where -- Read a plain list of strings. - stringList :: OrgParser [String] + stringList :: Monad m => OrgParser m [String] stringList = try $ char '(' *> sepBy elispString spaces <* char ')' -- Read an emacs lisp list specifying a complement set. - complementStringList :: OrgParser [String] + complementStringList :: Monad m => OrgParser m [String] complementStringList = try $ string "(not " *> sepBy elispString spaces <* char ')' - elispString :: OrgParser String + elispString :: Monad m => OrgParser m String elispString = try $ char '"' *> manyTill alphaNum (char '"') -- | Read but ignore the export setting. -ignoredSetting :: String -> OrgParser () +ignoredSetting :: Monad m => String -> OrgParser m () ignoredSetting s = try (() <$ string s <* char ':' <* many1 nonspaceChar) -- | Read an elisp boolean. Only NIL is treated as false, non-NIL values are -- interpreted as true. -elispBoolean :: OrgParser Bool +elispBoolean :: Monad m => OrgParser m Bool elispBoolean = try $ do value <- many1 nonspaceChar return $ case map toLower value of diff --git a/src/Text/Pandoc/Readers/Org/Inlines.hs b/src/Text/Pandoc/Readers/Org/Inlines.hs index 7e1bb61c2..5a02eb8eb 100644 --- a/src/Text/Pandoc/Readers/Org/Inlines.hs +++ b/src/Text/Pandoc/Readers/Org/Inlines.hs @@ -47,9 +47,11 @@ import Text.Pandoc.Options import Text.Pandoc.Readers.LaTeX ( inlineCommand, rawLaTeXInline ) import Text.TeXMath ( readTeX, writePandoc, DisplayType(..) ) import qualified Text.TeXMath.Readers.MathML.EntityMap as MathMLEntityMap +import Text.Pandoc.Class (PandocMonad) import Prelude hiding (sequence) import Control.Monad ( guard, mplus, mzero, when, void ) +import Control.Monad.Trans ( lift ) import Data.Char ( isAlphaNum, isSpace ) import Data.List ( intersperse ) import Data.Maybe ( fromMaybe ) @@ -60,46 +62,46 @@ import Data.Traversable (sequence) -- -- Functions acting on the parser state -- -recordAnchorId :: String -> OrgParser () +recordAnchorId :: PandocMonad m => String -> OrgParser m () recordAnchorId i = updateState $ \s -> s{ orgStateAnchorIds = i : (orgStateAnchorIds s) } -pushToInlineCharStack :: Char -> OrgParser () +pushToInlineCharStack :: PandocMonad m => Char -> OrgParser m () pushToInlineCharStack c = updateState $ \s -> s{ orgStateEmphasisCharStack = c:orgStateEmphasisCharStack s } -popInlineCharStack :: OrgParser () +popInlineCharStack :: PandocMonad m => OrgParser m () popInlineCharStack = updateState $ \s -> s{ orgStateEmphasisCharStack = drop 1 . orgStateEmphasisCharStack $ s } -surroundingEmphasisChar :: OrgParser [Char] +surroundingEmphasisChar :: PandocMonad m => OrgParser m [Char] surroundingEmphasisChar = take 1 . drop 1 . orgStateEmphasisCharStack <$> getState -startEmphasisNewlinesCounting :: Int -> OrgParser () +startEmphasisNewlinesCounting :: PandocMonad m => Int -> OrgParser m () startEmphasisNewlinesCounting maxNewlines = updateState $ \s -> s{ orgStateEmphasisNewlines = Just maxNewlines } -decEmphasisNewlinesCount :: OrgParser () +decEmphasisNewlinesCount :: PandocMonad m => OrgParser m () decEmphasisNewlinesCount = updateState $ \s -> s{ orgStateEmphasisNewlines = (\n -> n - 1) <$> orgStateEmphasisNewlines s } -newlinesCountWithinLimits :: OrgParser Bool +newlinesCountWithinLimits :: PandocMonad m => OrgParser m Bool newlinesCountWithinLimits = do st <- getState return $ ((< 0) <$> orgStateEmphasisNewlines st) /= Just True -resetEmphasisNewlines :: OrgParser () +resetEmphasisNewlines :: PandocMonad m => OrgParser m () resetEmphasisNewlines = updateState $ \s -> s{ orgStateEmphasisNewlines = Nothing } -addToNotesTable :: OrgNoteRecord -> OrgParser () +addToNotesTable :: PandocMonad m => OrgNoteRecord -> OrgParser m () addToNotesTable note = do oldnotes <- orgStateNotes' <$> getState updateState $ \s -> s{ orgStateNotes' = note:oldnotes } -- | Parse a single Org-mode inline element -inline :: OrgParser (F Inlines) +inline :: PandocMonad m => OrgParser m (F Inlines) inline = choice [ whitespace , linebreak @@ -125,7 +127,7 @@ inline = "inline" -- | Read the rest of the input as inlines. -inlines :: OrgParser (F Inlines) +inlines :: PandocMonad m => OrgParser m (F Inlines) inlines = trimInlinesF . mconcat <$> many1 inline -- treat these as potentially non-text when parsing inline: @@ -133,23 +135,23 @@ specialChars :: [Char] specialChars = "\"$'()*+-,./:;<=>@[\\]^_{|}~" -whitespace :: OrgParser (F Inlines) +whitespace :: PandocMonad m => OrgParser m (F Inlines) whitespace = pure B.space <$ skipMany1 spaceChar <* updateLastPreCharPos <* updateLastForbiddenCharPos "whitespace" -linebreak :: OrgParser (F Inlines) +linebreak :: PandocMonad m => OrgParser m (F Inlines) linebreak = try $ pure B.linebreak <$ string "\\\\" <* skipSpaces <* newline -str :: OrgParser (F Inlines) +str :: PandocMonad m => OrgParser m (F Inlines) str = return . B.str <$> many1 (noneOf $ specialChars ++ "\n\r ") <* updateLastStrPos -- | An endline character that can be treated as a space, not a structural -- break. This should reflect the values of the Emacs variable -- @org-element-pagaraph-separate@. -endline :: OrgParser (F Inlines) +endline :: PandocMonad m => OrgParser m (F Inlines) endline = try $ do newline notFollowedBy' endOfBlock @@ -174,7 +176,7 @@ endline = try $ do -- contributors. All this should be consolidated once an official Org-mode -- citation syntax has emerged. -cite :: OrgParser (F Inlines) +cite :: PandocMonad m => OrgParser m (F Inlines) cite = try $ berkeleyCite <|> do guardEnabled Ext_citations (cs, raw) <- withRaw $ choice @@ -185,40 +187,41 @@ cite = try $ berkeleyCite <|> do return $ (flip B.cite (B.text raw)) <$> cs -- | A citation in Pandoc Org-mode style (@[prefix \@citekey suffix]@). -pandocOrgCite :: OrgParser (F [Citation]) +pandocOrgCite :: PandocMonad m => OrgParser m (F [Citation]) pandocOrgCite = try $ char '[' *> skipSpaces *> citeList <* skipSpaces <* char ']' -orgRefCite :: OrgParser (F [Citation]) +orgRefCite :: PandocMonad m => OrgParser m (F [Citation]) orgRefCite = try $ choice [ normalOrgRefCite , fmap (:[]) <$> linkLikeOrgRefCite ] -normalOrgRefCite :: OrgParser (F [Citation]) +normalOrgRefCite :: PandocMonad m => OrgParser m (F [Citation]) normalOrgRefCite = try $ do mode <- orgRefCiteMode - -- org-ref style citation key, parsed into a citation of the given mode - let orgRefCiteItem :: OrgParser (F Citation) - orgRefCiteItem = try $ do - key <- orgRefCiteKey - returnF $ Citation - { citationId = key - , citationPrefix = mempty - , citationSuffix = mempty - , citationMode = mode - , citationNoteNum = 0 - , citationHash = 0 - } - firstCitation <- orgRefCiteItem - moreCitations <- many (try $ char ',' *> orgRefCiteItem) + firstCitation <- orgRefCiteList mode + moreCitations <- many (try $ char ',' *> orgRefCiteList mode) return . sequence $ firstCitation : moreCitations - where + where + -- | A list of org-ref style citation keys, parsed as citation of the given + -- citation mode. + orgRefCiteList :: PandocMonad m => CitationMode -> OrgParser m (F Citation) + orgRefCiteList citeMode = try $ do + key <- orgRefCiteKey + returnF $ Citation + { citationId = key + , citationPrefix = mempty + , citationSuffix = mempty + , citationMode = citeMode + , citationNoteNum = 0 + , citationHash = 0 + } -- | Read an Berkeley-style Org-mode citation. Berkeley citation style was -- develop and adjusted to Org-mode style by John MacFarlane and Richard -- Lawrence, respectively, both philosophers at UC Berkeley. -berkeleyCite :: OrgParser (F Inlines) +berkeleyCite :: PandocMonad m => OrgParser m (F Inlines) berkeleyCite = try $ do bcl <- berkeleyCitationList return $ do @@ -260,7 +263,7 @@ data BerkeleyCitationList = BerkeleyCitationList , berkeleyCiteCommonSuffix :: Maybe Inlines , berkeleyCiteCitations :: [Citation] } -berkeleyCitationList :: OrgParser (F BerkeleyCitationList) +berkeleyCitationList :: PandocMonad m => OrgParser m (F BerkeleyCitationList) berkeleyCitationList = try $ do char '[' parens <- choice [ False <$ berkeleyBareTag, True <$ berkeleyParensTag ] @@ -275,22 +278,22 @@ berkeleyCitationList = try $ do <*> sequence commonSuffix <*> citations) where - citationListPart :: OrgParser (F Inlines) + citationListPart :: PandocMonad m => OrgParser m (F Inlines) citationListPart = fmap (trimInlinesF . mconcat) . try . many1 $ do notFollowedBy' citeKey notFollowedBy (oneOf ";]") inline -berkeleyBareTag :: OrgParser () +berkeleyBareTag :: PandocMonad m => OrgParser m () berkeleyBareTag = try $ void berkeleyBareTag' -berkeleyParensTag :: OrgParser () +berkeleyParensTag :: PandocMonad m => OrgParser m () berkeleyParensTag = try . void $ enclosedByPair '(' ')' berkeleyBareTag' -berkeleyBareTag' :: OrgParser () +berkeleyBareTag' :: PandocMonad m => OrgParser m () berkeleyBareTag' = try $ void (string "cite") -berkeleyTextualCite :: OrgParser (F [Citation]) +berkeleyTextualCite :: PandocMonad m => OrgParser m (F [Citation]) berkeleyTextualCite = try $ do (suppressAuthor, key) <- citeKey returnF . return $ Citation @@ -305,14 +308,14 @@ berkeleyTextualCite = try $ do -- The following is what a Berkeley-style bracketed textual citation parser -- would look like. However, as these citations are a subset of Pandoc's Org -- citation style, this isn't used. --- berkeleyBracketedTextualCite :: OrgParser (F [Citation]) +-- berkeleyBracketedTextualCite :: PandocMonad m => OrgParser m (F [Citation]) -- berkeleyBracketedTextualCite = try . (fmap head) $ -- enclosedByPair '[' ']' berkeleyTextualCite -- | Read a link-like org-ref style citation. The citation includes pre and -- post text. However, multiple citations are not possible due to limitations -- in the syntax. -linkLikeOrgRefCite :: OrgParser (F Citation) +linkLikeOrgRefCite :: PandocMonad m => OrgParser m (F Citation) linkLikeOrgRefCite = try $ do _ <- string "[[" mode <- orgRefCiteMode @@ -335,13 +338,13 @@ linkLikeOrgRefCite = try $ do -- | Read a citation key. The characters allowed in citation keys are taken -- from the `org-ref-cite-re` variable in `org-ref.el`. -orgRefCiteKey :: OrgParser String +orgRefCiteKey :: PandocMonad m => OrgParser m String orgRefCiteKey = try . many1 . satisfy $ \c -> isAlphaNum c || c `elem` ("-_:\\./"::String) -- | Supported citation types. Only a small subset of org-ref types is -- supported for now. TODO: rewrite this, use LaTeX reader as template. -orgRefCiteMode :: OrgParser CitationMode +orgRefCiteMode :: PandocMonad m => OrgParser m CitationMode orgRefCiteMode = choice $ map (\(s, mode) -> mode <$ try (string s <* char ':')) [ ("cite", AuthorInText) @@ -352,10 +355,10 @@ orgRefCiteMode = , ("citeyear", SuppressAuthor) ] -citeList :: OrgParser (F [Citation]) +citeList :: PandocMonad m => OrgParser m (F [Citation]) citeList = sequence <$> sepEndBy1 citation (try $ char ';' *> skipSpaces) -citation :: OrgParser (F Citation) +citation :: PandocMonad m => OrgParser m (F Citation) citation = try $ do pref <- prefix (suppress_author, key) <- citeKey @@ -384,10 +387,10 @@ citation = try $ do then (B.space <>) <$> rest else rest -footnote :: OrgParser (F Inlines) +footnote :: PandocMonad m => OrgParser m (F Inlines) footnote = try $ inlineNote <|> referencedNote -inlineNote :: OrgParser (F Inlines) +inlineNote :: PandocMonad m => OrgParser m (F Inlines) inlineNote = try $ do string "[fn:" ref <- many alphaNum @@ -397,7 +400,7 @@ inlineNote = try $ do addToNotesTable ("fn:" ++ ref, note) return $ B.note <$> note -referencedNote :: OrgParser (F Inlines) +referencedNote :: PandocMonad m => OrgParser m (F Inlines) referencedNote = try $ do ref <- noteMarker return $ do @@ -409,14 +412,14 @@ referencedNote = try $ do let contents' = runF contents st{ orgStateNotes' = [] } return $ B.note contents' -linkOrImage :: OrgParser (F Inlines) +linkOrImage :: PandocMonad m => OrgParser m (F Inlines) linkOrImage = explicitOrImageLink <|> selflinkOrImage <|> angleLink <|> plainLink "link or image" -explicitOrImageLink :: OrgParser (F Inlines) +explicitOrImageLink :: PandocMonad m => OrgParser m (F Inlines) explicitOrImageLink = try $ do char '[' srcF <- applyCustomLinkFormat =<< possiblyEmptyLinkTarget @@ -431,30 +434,30 @@ explicitOrImageLink = try $ do _ -> linkToInlinesF src =<< title' -selflinkOrImage :: OrgParser (F Inlines) +selflinkOrImage :: PandocMonad m => OrgParser m (F Inlines) selflinkOrImage = try $ do src <- char '[' *> linkTarget <* char ']' return $ linkToInlinesF src (B.str src) -plainLink :: OrgParser (F Inlines) +plainLink :: PandocMonad m => OrgParser m (F Inlines) plainLink = try $ do (orig, src) <- uri returnF $ B.link src "" (B.str orig) -angleLink :: OrgParser (F Inlines) +angleLink :: PandocMonad m => OrgParser m (F Inlines) angleLink = try $ do char '<' link <- plainLink char '>' return link -linkTarget :: OrgParser String +linkTarget :: PandocMonad m => OrgParser m String linkTarget = enclosedByPair '[' ']' (noneOf "\n\r[]") -possiblyEmptyLinkTarget :: OrgParser String +possiblyEmptyLinkTarget :: PandocMonad m => OrgParser m String possiblyEmptyLinkTarget = try linkTarget <|> ("" <$ string "[]") -applyCustomLinkFormat :: String -> OrgParser (F String) +applyCustomLinkFormat :: String -> OrgParser m (F String) applyCustomLinkFormat link = do let (linkType, rest) = break (== ':') link return $ do @@ -487,7 +490,7 @@ internalLink link title = do -- @anchor-id@ contains spaces, we are more restrictive in what is accepted as -- an anchor. -anchor :: OrgParser (F Inlines) +anchor :: PandocMonad m => OrgParser m (F Inlines) anchor = try $ do anchorId <- parseAnchor recordAnchorId anchorId @@ -509,7 +512,7 @@ solidify = map replaceSpecialChar | otherwise = '-' -- | Parses an inline code block and marks it as an babel block. -inlineCodeBlock :: OrgParser (F Inlines) +inlineCodeBlock :: PandocMonad m => OrgParser m (F Inlines) inlineCodeBlock = try $ do string "src_" lang <- many1 orgArgWordChar @@ -519,13 +522,13 @@ inlineCodeBlock = try $ do let attrKeyVal = map toRundocAttrib (("language", lang) : opts) returnF $ B.codeWith ("", attrClasses, attrKeyVal) inlineCode where - inlineBlockOption :: OrgParser (String, String) + inlineBlockOption :: PandocMonad m => OrgParser m (String, String) inlineBlockOption = try $ do argKey <- orgArgKey paramValue <- option "yes" orgInlineParamValue return (argKey, paramValue) - orgInlineParamValue :: OrgParser String + orgInlineParamValue :: PandocMonad m => OrgParser m String orgInlineParamValue = try $ skipSpaces *> notFollowedBy (char ':') @@ -533,7 +536,7 @@ inlineCodeBlock = try $ do <* skipSpaces -emphasizedText :: OrgParser (F Inlines) +emphasizedText :: PandocMonad m => OrgParser m (F Inlines) emphasizedText = do state <- getState guard . exportEmphasizedText . orgStateExportSettings $ state @@ -544,60 +547,63 @@ emphasizedText = do , underline ] -enclosedByPair :: Char -- ^ opening char +enclosedByPair :: PandocMonad m + => Char -- ^ opening char -> Char -- ^ closing char - -> OrgParser a -- ^ parser - -> OrgParser [a] + -> OrgParser m a -- ^ parser + -> OrgParser m [a] enclosedByPair s e p = char s *> many1Till p (char e) -emph :: OrgParser (F Inlines) +emph :: PandocMonad m => OrgParser m (F Inlines) emph = fmap B.emph <$> emphasisBetween '/' -strong :: OrgParser (F Inlines) +strong :: PandocMonad m => OrgParser m (F Inlines) strong = fmap B.strong <$> emphasisBetween '*' -strikeout :: OrgParser (F Inlines) +strikeout :: PandocMonad m => OrgParser m (F Inlines) strikeout = fmap B.strikeout <$> emphasisBetween '+' -- There is no underline, so we use strong instead. -underline :: OrgParser (F Inlines) +underline :: PandocMonad m => OrgParser m (F Inlines) underline = fmap B.strong <$> emphasisBetween '_' -verbatim :: OrgParser (F Inlines) +verbatim :: PandocMonad m => OrgParser m (F Inlines) verbatim = return . B.code <$> verbatimBetween '=' -code :: OrgParser (F Inlines) +code :: PandocMonad m => OrgParser m (F Inlines) code = return . B.code <$> verbatimBetween '~' -subscript :: OrgParser (F Inlines) +subscript :: PandocMonad m => OrgParser m (F Inlines) subscript = fmap B.subscript <$> try (char '_' *> subOrSuperExpr) -superscript :: OrgParser (F Inlines) +superscript :: PandocMonad m => OrgParser m (F Inlines) superscript = fmap B.superscript <$> try (char '^' *> subOrSuperExpr) -math :: OrgParser (F Inlines) +math :: PandocMonad m => OrgParser m (F Inlines) math = return . B.math <$> choice [ math1CharBetween '$' , mathStringBetween '$' , rawMathBetween "\\(" "\\)" ] -displayMath :: OrgParser (F Inlines) +displayMath :: PandocMonad m => OrgParser m (F Inlines) displayMath = return . B.displayMath <$> choice [ rawMathBetween "\\[" "\\]" , rawMathBetween "$$" "$$" ] -updatePositions :: Char - -> OrgParser Char +updatePositions :: PandocMonad m + => Char + -> OrgParser m Char updatePositions c = do when (c `elem` emphasisPreChars) updateLastPreCharPos when (c `elem` emphasisForbiddenBorderChars) updateLastForbiddenCharPos return c -symbol :: OrgParser (F Inlines) +symbol :: PandocMonad m => OrgParser m (F Inlines) symbol = return . B.str . (: "") <$> (oneOf specialChars >>= updatePositions) -emphasisBetween :: Char - -> OrgParser (F Inlines) +emphasisBetween :: PandocMonad m + => Char + -> OrgParser m (F Inlines) emphasisBetween c = try $ do startEmphasisNewlinesCounting emphasisAllowedNewlines res <- enclosedInlines (emphasisStart c) (emphasisEnd c) @@ -606,8 +612,9 @@ emphasisBetween c = try $ do resetEmphasisNewlines return res -verbatimBetween :: Char - -> OrgParser String +verbatimBetween :: PandocMonad m + => Char + -> OrgParser m String verbatimBetween c = try $ emphasisStart c *> many1TillNOrLessNewlines 1 verbatimChar (emphasisEnd c) @@ -615,8 +622,9 @@ verbatimBetween c = try $ verbatimChar = noneOf "\n\r" >>= updatePositions -- | Parses a raw string delimited by @c@ using Org's math rules -mathStringBetween :: Char - -> OrgParser String +mathStringBetween :: PandocMonad m + => Char + -> OrgParser m String mathStringBetween c = try $ do mathStart c body <- many1TillNOrLessNewlines mathAllowedNewlines @@ -626,8 +634,9 @@ mathStringBetween c = try $ do return $ body ++ [final] -- | Parse a single character between @c@ using math rules -math1CharBetween :: Char - -> OrgParser String +math1CharBetween :: PandocMonad m + => Char + -> OrgParser m String math1CharBetween c = try $ do char c res <- noneOf $ c:mathForbiddenBorderChars @@ -635,13 +644,14 @@ math1CharBetween c = try $ do eof <|> () <$ lookAhead (oneOf mathPostChars) return [res] -rawMathBetween :: String +rawMathBetween :: PandocMonad m + => String -> String - -> OrgParser String + -> OrgParser m String rawMathBetween s e = try $ string s *> manyTill anyChar (try $ string e) -- | Parses the start (opening character) of emphasis -emphasisStart :: Char -> OrgParser Char +emphasisStart :: PandocMonad m => Char -> OrgParser m Char emphasisStart c = try $ do guard =<< afterEmphasisPreChar guard =<< notAfterString @@ -654,7 +664,7 @@ emphasisStart c = try $ do return c -- | Parses the closing character of emphasis -emphasisEnd :: Char -> OrgParser Char +emphasisEnd :: PandocMonad m => Char -> OrgParser m Char emphasisEnd c = try $ do guard =<< notAfterForbiddenBorderChar char c @@ -665,11 +675,11 @@ emphasisEnd c = try $ do where acceptablePostChars = surroundingEmphasisChar >>= \x -> oneOf (x ++ emphasisPostChars) -mathStart :: Char -> OrgParser Char +mathStart :: PandocMonad m => Char -> OrgParser m Char mathStart c = try $ char c <* notFollowedBy' (oneOf (c:mathForbiddenBorderChars)) -mathEnd :: Char -> OrgParser Char +mathEnd :: PandocMonad m => Char -> OrgParser m Char mathEnd c = try $ do res <- noneOf (c:mathForbiddenBorderChars) char c @@ -677,15 +687,15 @@ mathEnd c = try $ do return res -enclosedInlines :: OrgParser a - -> OrgParser b - -> OrgParser (F Inlines) +enclosedInlines :: PandocMonad m => OrgParser m a + -> OrgParser m b + -> OrgParser m (F Inlines) enclosedInlines start end = try $ trimInlinesF . mconcat <$> enclosed start end inline -enclosedRaw :: OrgParser a - -> OrgParser b - -> OrgParser String +enclosedRaw :: PandocMonad m => OrgParser m a + -> OrgParser m b + -> OrgParser m String enclosedRaw start end = try $ start *> (onSingleLine <|> spanningTwoLines) where onSingleLine = try $ many1Till (noneOf "\n\r") end @@ -694,10 +704,10 @@ enclosedRaw start end = try $ -- | Like many1Till, but parses at most @n+1@ lines. @p@ must not consume -- newlines. -many1TillNOrLessNewlines :: Int - -> OrgParser Char - -> OrgParser a - -> OrgParser String +many1TillNOrLessNewlines :: PandocMonad m => Int + -> OrgParser m Char + -> OrgParser m a + -> OrgParser m String many1TillNOrLessNewlines n p end = try $ nMoreLines (Just n) mempty >>= oneOrMore where @@ -746,21 +756,21 @@ mathAllowedNewlines :: Int mathAllowedNewlines = 2 -- | Whether we are right behind a char allowed before emphasis -afterEmphasisPreChar :: OrgParser Bool +afterEmphasisPreChar :: PandocMonad m => OrgParser m Bool afterEmphasisPreChar = do pos <- getPosition lastPrePos <- orgStateLastPreCharPos <$> getState return . fromMaybe True $ (== pos) <$> lastPrePos -- | Whether the parser is right after a forbidden border char -notAfterForbiddenBorderChar :: OrgParser Bool +notAfterForbiddenBorderChar :: PandocMonad m => OrgParser m Bool notAfterForbiddenBorderChar = do pos <- getPosition lastFBCPos <- orgStateLastForbiddenCharPos <$> getState return $ lastFBCPos /= Just pos -- | Read a sub- or superscript expression -subOrSuperExpr :: OrgParser (F Inlines) +subOrSuperExpr :: PandocMonad m => OrgParser m (F Inlines) subOrSuperExpr = try $ choice [ id <$> charsInBalanced '{' '}' (noneOf "\n\r") , enclosing ('(', ')') <$> charsInBalanced '(' ')' (noneOf "\n\r") @@ -768,7 +778,7 @@ subOrSuperExpr = try $ ] >>= parseFromString (mconcat <$> many inline) where enclosing (left, right) s = left : s ++ [right] -simpleSubOrSuperString :: OrgParser String +simpleSubOrSuperString :: PandocMonad m => OrgParser m String simpleSubOrSuperString = try $ do state <- getState guard . exportSubSuperscripts . orgStateExportSettings $ state @@ -777,17 +787,18 @@ simpleSubOrSuperString = try $ do <*> many1 alphaNum ] -inlineLaTeX :: OrgParser (F Inlines) +inlineLaTeX :: PandocMonad m => OrgParser m (F Inlines) inlineLaTeX = try $ do cmd <- inlineLaTeXCommand + ils <- (lift . lift) $ parseAsInlineLaTeX cmd maybe mzero returnF $ - parseAsMath cmd `mplus` parseAsMathMLSym cmd `mplus` parseAsInlineLaTeX cmd + parseAsMath cmd `mplus` parseAsMathMLSym cmd `mplus` ils where parseAsMath :: String -> Maybe Inlines parseAsMath cs = B.fromList <$> texMathToPandoc cs - parseAsInlineLaTeX :: String -> Maybe Inlines - parseAsInlineLaTeX cs = maybeRight $ runParser inlineCommand state "" cs + parseAsInlineLaTeX :: PandocMonad m => String -> m (Maybe Inlines) + parseAsInlineLaTeX cs = maybeRight <$> runParserT inlineCommand state "" cs parseAsMathMLSym :: String -> Maybe Inlines parseAsMathMLSym cs = B.str <$> MathMLEntityMap.getUnicode (clean cs) @@ -803,10 +814,11 @@ inlineLaTeX = try $ do maybeRight :: Either a b -> Maybe b maybeRight = either (const Nothing) Just -inlineLaTeXCommand :: OrgParser String +inlineLaTeXCommand :: PandocMonad m => OrgParser m String inlineLaTeXCommand = try $ do rest <- getInput - case runParser rawLaTeXInline def "source" rest of + parsed <- (lift . lift) $ runParserT rawLaTeXInline def "source" rest + case parsed of Right (RawInline _ cs) -> do -- drop any trailing whitespace, those are not be part of the command as -- far as org mode is concerned. @@ -820,14 +832,14 @@ inlineLaTeXCommand = try $ do dropWhileEnd :: (a -> Bool) -> [a] -> [a] dropWhileEnd p = foldr (\x xs -> if p x && null xs then [] else x : xs) [] -exportSnippet :: OrgParser (F Inlines) +exportSnippet :: PandocMonad m => OrgParser m (F Inlines) exportSnippet = try $ do string "@@" format <- many1Till (alphaNum <|> char '-') (char ':') snippet <- manyTill anyChar (try $ string "@@") returnF $ B.rawInline format snippet -smart :: OrgParser (F Inlines) +smart :: PandocMonad m => OrgParser m (F Inlines) smart = do getOption readerSmart >>= guard doubleQuoted <|> singleQuoted <|> @@ -844,7 +856,7 @@ smart = do <* updateLastForbiddenCharPos *> return (B.str "\x2019") -singleQuoted :: OrgParser (F Inlines) +singleQuoted :: PandocMonad m => OrgParser m (F Inlines) singleQuoted = try $ do guard =<< getExportSetting exportSmartQuotes singleQuoteStart @@ -856,7 +868,7 @@ singleQuoted = try $ do -- doubleQuoted will handle regular double-quoted sections, as well -- as dialogues with an open double-quote without a close double-quote -- in the same paragraph. -doubleQuoted :: OrgParser (F Inlines) +doubleQuoted :: PandocMonad m => OrgParser m (F Inlines) doubleQuoted = try $ do guard =<< getExportSetting exportSmartQuotes doubleQuoteStart diff --git a/src/Text/Pandoc/Readers/Org/Meta.hs b/src/Text/Pandoc/Readers/Org/Meta.hs index 1fea3e890..2f4e21248 100644 --- a/src/Text/Pandoc/Readers/Org/Meta.hs +++ b/src/Text/Pandoc/Readers/Org/Meta.hs @@ -41,6 +41,7 @@ import Text.Pandoc.Readers.Org.Parsing import qualified Text.Pandoc.Builder as B import Text.Pandoc.Builder ( Blocks, Inlines ) +import Text.Pandoc.Class ( PandocMonad ) import Text.Pandoc.Definition import Control.Monad ( mzero, void ) @@ -51,7 +52,7 @@ import Data.Monoid ( (<>) ) import Network.HTTP ( urlEncode ) -- | Returns the current meta, respecting export options. -metaExport :: OrgParser (F Meta) +metaExport :: Monad m => OrgParser m (F Meta) metaExport = do st <- getState let settings = orgStateExportSettings st @@ -68,10 +69,10 @@ removeMeta key meta' = -- | Parse and handle a single line containing meta information -- The order, in which blocks are tried, makes sure that we're not looking at -- the beginning of a block, so we don't need to check for it -metaLine :: OrgParser Blocks +metaLine :: PandocMonad m => OrgParser m Blocks metaLine = mempty <$ metaLineStart <* (optionLine <|> declarationLine) -declarationLine :: OrgParser () +declarationLine :: PandocMonad m => OrgParser m () declarationLine = try $ do key <- map toLower <$> metaKey (key', value) <- metaValue key @@ -79,12 +80,12 @@ declarationLine = try $ do let meta' = B.setMeta key' <$> value <*> pure nullMeta in st { orgStateMeta = meta' <> orgStateMeta st } -metaKey :: OrgParser String +metaKey :: Monad m => OrgParser m String metaKey = map toLower <$> many1 (noneOf ": \n\r") <* char ':' <* skipSpaces -metaValue :: String -> OrgParser (String, (F MetaValue)) +metaValue :: PandocMonad m => String -> OrgParser m (String, (F MetaValue)) metaValue key = let inclKey = "header-includes" in case key of @@ -103,10 +104,10 @@ metaValue key = accumulatingList inclKey (metaExportSnippet "html") _ -> (key,) <$> metaString -metaInlines :: OrgParser (F MetaValue) +metaInlines :: PandocMonad m => OrgParser m (F MetaValue) metaInlines = fmap (MetaInlines . B.toList) <$> inlinesTillNewline -metaInlinesCommaSeparated :: OrgParser (F MetaValue) +metaInlinesCommaSeparated :: PandocMonad m => OrgParser m (F MetaValue) metaInlinesCommaSeparated = do authStrs <- (many1 (noneOf ",\n")) `sepBy1` (char ',') newline @@ -114,21 +115,21 @@ metaInlinesCommaSeparated = do let toMetaInlines = MetaInlines . B.toList return $ MetaList . map toMetaInlines <$> sequence authors -metaString :: OrgParser (F MetaValue) +metaString :: Monad m => OrgParser m (F MetaValue) metaString = metaModifiedString id -metaModifiedString :: (String -> String) -> OrgParser (F MetaValue) +metaModifiedString :: Monad m => (String -> String) -> OrgParser m (F MetaValue) metaModifiedString f = return . MetaString . f <$> anyLine -- | Read an format specific meta definition -metaExportSnippet :: String -> OrgParser (F MetaValue) +metaExportSnippet :: Monad m => String -> OrgParser m (F MetaValue) metaExportSnippet format = return . MetaInlines . B.toList . B.rawInline format <$> anyLine -- | Accumulate the result of the @parser@ in a list under @key@. -accumulatingList :: String - -> OrgParser (F MetaValue) - -> OrgParser (F MetaValue) +accumulatingList :: Monad m => String + -> OrgParser m (F MetaValue) + -> OrgParser m (F MetaValue) accumulatingList key p = do value <- p meta' <- orgStateMeta <$> getState @@ -141,7 +142,7 @@ accumulatingList key p = do -- -- export options -- -optionLine :: OrgParser () +optionLine :: Monad m => OrgParser m () optionLine = try $ do key <- metaKey case key of @@ -152,14 +153,14 @@ optionLine = try $ do "typ_todo" -> todoSequence >>= updateState . registerTodoSequence _ -> mzero -addLinkFormat :: String +addLinkFormat :: Monad m => String -> (String -> String) - -> OrgParser () + -> OrgParser m () addLinkFormat key formatter = updateState $ \s -> let fs = orgStateLinkFormatters s in s{ orgStateLinkFormatters = M.insert key formatter fs } -parseLinkFormat :: OrgParser ((String, String -> String)) +parseLinkFormat :: Monad m => OrgParser m ((String, String -> String)) parseLinkFormat = try $ do linkType <- (:) <$> letter <*> many (alphaNum <|> oneOf "-_") <* skipSpaces linkSubst <- parseFormat @@ -167,7 +168,7 @@ parseLinkFormat = try $ do -- | An ad-hoc, single-argument-only implementation of a printf-style format -- parser. -parseFormat :: OrgParser (String -> String) +parseFormat :: Monad m => OrgParser m (String -> String) parseFormat = try $ do replacePlain <|> replaceUrl <|> justAppend where @@ -181,13 +182,13 @@ parseFormat = try $ do rest = manyTill anyChar (eof <|> () <$ oneOf "\n\r") tillSpecifier c = manyTill (noneOf "\n\r") (try $ string ('%':c:"")) -inlinesTillNewline :: OrgParser (F Inlines) +inlinesTillNewline :: PandocMonad m => OrgParser m (F Inlines) inlinesTillNewline = trimInlinesF . mconcat <$> manyTill inline newline -- -- ToDo Sequences and Keywords -- -todoSequence :: OrgParser TodoSequence +todoSequence :: Monad m => OrgParser m TodoSequence todoSequence = try $ do todoKws <- todoKeywords doneKws <- optionMaybe $ todoDoneSep *> todoKeywords @@ -201,13 +202,13 @@ todoSequence = try $ do (x:xs) -> return $ keywordsToSequence (reverse xs) [x] where - todoKeywords :: OrgParser [String] + todoKeywords :: Monad m => OrgParser m [String] todoKeywords = try $ let keyword = many1 nonspaceChar <* skipSpaces endOfKeywords = todoDoneSep <|> void newline in manyTill keyword (lookAhead endOfKeywords) - todoDoneSep :: OrgParser () + todoDoneSep :: Monad m => OrgParser m () todoDoneSep = void . try $ skipSpaces *> char '|' <* skipSpaces1 keywordsToSequence :: [String] -> [String] -> TodoSequence diff --git a/src/Text/Pandoc/Readers/Org/ParserState.hs b/src/Text/Pandoc/Readers/Org/ParserState.hs index 38f95ca95..181dd1d5c 100644 --- a/src/Text/Pandoc/Readers/Org/ParserState.hs +++ b/src/Text/Pandoc/Readers/Org/ParserState.hs @@ -51,7 +51,7 @@ module Text.Pandoc.Readers.Org.ParserState ) where import Control.Monad (liftM, liftM2) -import Control.Monad.Reader (Reader, runReader, ask, asks, local) +import Control.Monad.Reader (Reader, runReader, ReaderT, ask, asks, local) import Data.Default (Default(..)) import qualified Data.Map as M @@ -122,7 +122,7 @@ instance HasLastStrPosition OrgParserState where getLastStrPos = orgStateLastStrPos setLastStrPos pos st = st{ orgStateLastStrPos = Just pos } -instance HasQuoteContext st (Reader OrgParserLocal) where +instance Monad m => HasQuoteContext st (ReaderT OrgParserLocal m) where getQuoteContext = asks orgLocalQuoteContext withQuoteContext q = local (\s -> s{orgLocalQuoteContext = q}) diff --git a/src/Text/Pandoc/Readers/Org/Parsing.hs b/src/Text/Pandoc/Readers/Org/Parsing.hs index 95415f823..1eb8a3b00 100644 --- a/src/Text/Pandoc/Readers/Org/Parsing.hs +++ b/src/Text/Pandoc/Readers/Org/Parsing.hs @@ -72,6 +72,7 @@ module Text.Pandoc.Readers.Org.Parsing , citeKey -- * Re-exports from Text.Pandoc.Parsec , runParser + , runParserT , getInput , char , letter @@ -114,17 +115,17 @@ import Text.Pandoc.Parsing hiding ( anyLine, blanklines, newline , parseFromString ) import Control.Monad ( guard ) -import Control.Monad.Reader ( Reader ) +import Control.Monad.Reader ( ReaderT ) -- | The parser used to read org files. -type OrgParser = ParserT [Char] OrgParserState (Reader OrgParserLocal) +type OrgParser m = ParserT [Char] OrgParserState (ReaderT OrgParserLocal m) -- -- Adaptions and specializations of parsing utilities -- -- | Parse any line of text -anyLine :: OrgParser String +anyLine :: Monad m => OrgParser m String anyLine = P.anyLine <* updateLastPreCharPos @@ -132,7 +133,7 @@ anyLine = -- The version Text.Pandoc.Parsing cannot be used, as we need additional parts -- of the state saved and restored. -parseFromString :: OrgParser a -> String -> OrgParser a +parseFromString :: Monad m => OrgParser m a -> String -> OrgParser m a parseFromString parser str' = do oldLastPreCharPos <- orgStateLastPreCharPos <$> getState updateState $ \s -> s{ orgStateLastPreCharPos = Nothing } @@ -141,33 +142,34 @@ parseFromString parser str' = do return result -- | Skip one or more tab or space characters. -skipSpaces1 :: OrgParser () +skipSpaces1 :: Monad m => OrgParser m () skipSpaces1 = skipMany1 spaceChar -- | Like @Text.Parsec.Char.newline@, but causes additional state changes. -newline :: OrgParser Char +newline :: Monad m => OrgParser m Char newline = P.newline <* updateLastPreCharPos <* updateLastForbiddenCharPos -- | Like @Text.Parsec.Char.blanklines@, but causes additional state changes. -blanklines :: OrgParser [Char] +blanklines :: Monad m => OrgParser m [Char] blanklines = P.blanklines <* updateLastPreCharPos <* updateLastForbiddenCharPos -- | Succeeds when we're in list context. -inList :: OrgParser () +inList :: Monad m => OrgParser m () inList = do ctx <- orgStateParserContext <$> getState guard (ctx == ListItemState) -- | Parse in different context -withContext :: ParserContext -- ^ New parser context - -> OrgParser a -- ^ Parser to run in that context - -> OrgParser a +withContext :: Monad m + => ParserContext -- ^ New parser context + -> OrgParser m a -- ^ Parser to run in that context + -> OrgParser m a withContext context parser = do oldContext <- orgStateParserContext <$> getState updateState $ \s -> s{ orgStateParserContext = context } @@ -180,19 +182,19 @@ withContext context parser = do -- -- | Get an export setting. -getExportSetting :: (ExportSettings -> a) -> OrgParser a +getExportSetting :: Monad m => (ExportSettings -> a) -> OrgParser m a getExportSetting s = s . orgStateExportSettings <$> getState -- | Set the current position as the last position at which a forbidden char -- was found (i.e. a character which is not allowed at the inner border of -- markup). -updateLastForbiddenCharPos :: OrgParser () +updateLastForbiddenCharPos :: Monad m => OrgParser m () updateLastForbiddenCharPos = getPosition >>= \p -> updateState $ \s -> s{ orgStateLastForbiddenCharPos = Just p} -- | Set the current parser position as the position at which a character was -- seen which allows inline markup to follow. -updateLastPreCharPos :: OrgParser () +updateLastPreCharPos :: Monad m => OrgParser m () updateLastPreCharPos = getPosition >>= \p -> updateState $ \s -> s{ orgStateLastPreCharPos = Just p} @@ -201,15 +203,15 @@ updateLastPreCharPos = getPosition >>= \p -> -- -- | Read the key of a plist style key-value list. -orgArgKey :: OrgParser String +orgArgKey :: Monad m => OrgParser m String orgArgKey = try $ skipSpaces *> char ':' *> many1 orgArgWordChar -- | Read the value of a plist style key-value list. -orgArgWord :: OrgParser String +orgArgWord :: Monad m => OrgParser m String orgArgWord = many1 orgArgWordChar -- | Chars treated as part of a word in plists. -orgArgWordChar :: OrgParser Char +orgArgWordChar :: Monad m => OrgParser m Char orgArgWordChar = alphaNum <|> oneOf "-_" diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index e05b6cba2..4232f1c90 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -49,18 +49,29 @@ import qualified Text.Pandoc.Builder as B import Data.Sequence (viewr, ViewR(..)) import Data.Char (toLower, isHexDigit, isSpace) import Data.Monoid ((<>)) -import Text.Pandoc.Error +import Control.Monad.Except (throwError) +import Control.Monad.Trans (lift) +import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) +import qualified Text.Pandoc.Class as P -- | Parse reStructuredText string and return Pandoc document. -readRST :: ReaderOptions -- ^ Reader options +readRST :: PandocMonad m + => ReaderOptions -- ^ Reader options -> String -- ^ String to parse (assuming @'\n'@ line endings) - -> Either PandocError Pandoc -readRST opts s = (readWith parseRST) def{ stateOptions = opts } (s ++ "\n\n") + -> m Pandoc +readRST opts s = do + parsed <- (readWithM parseRST) def{ stateOptions = opts } (s ++ "\n\n") + case parsed of + Right result -> return result + Left _ -> throwError $ PandocParseError "error parsing rst" -readRSTWithWarnings :: ReaderOptions -> String -> Either PandocError (Pandoc, [String]) -readRSTWithWarnings opts s = (readWithWarnings parseRST) def{ stateOptions = opts } (s ++ "\n\n") +readRSTWithWarnings :: PandocMonad m + => ReaderOptions -- ^ Reader options + -> String -- ^ String to parse (assuming @'\n'@ line endings) + -> m Pandoc +readRSTWithWarnings = readRST -type RSTParser = Parser [Char] ParserState +type RSTParser m = ParserT [Char] ParserState m -- -- Constants and data structure definitions @@ -141,7 +152,7 @@ metaFromDefList ds meta = adjustAuthors $ foldr f meta ds factorSemi (Str ys) factorSemi x = [x] -parseRST :: RSTParser Pandoc +parseRST :: PandocMonad m => RSTParser m Pandoc parseRST = do optional blanklines -- skip blank lines at beginning of file startPos <- getPosition @@ -168,10 +179,10 @@ parseRST = do -- parsing blocks -- -parseBlocks :: RSTParser Blocks +parseBlocks :: PandocMonad m => RSTParser m Blocks parseBlocks = mconcat <$> manyTill block eof -block :: RSTParser Blocks +block :: PandocMonad m => RSTParser m Blocks block = choice [ codeBlock , blockQuote , fieldList @@ -191,7 +202,7 @@ block = choice [ codeBlock -- field list -- -rawFieldListItem :: Int -> RSTParser (String, String) +rawFieldListItem :: Monad m => Int -> RSTParser m (String, String) rawFieldListItem minIndent = try $ do indent <- length <$> many (char ' ') guard $ indent >= minIndent @@ -204,7 +215,7 @@ rawFieldListItem minIndent = try $ do let raw = (if null first then "" else (first ++ "\n")) ++ rest ++ "\n" return (name, raw) -fieldListItem :: Int -> RSTParser (Inlines, [Blocks]) +fieldListItem :: PandocMonad m => Int -> RSTParser m (Inlines, [Blocks]) fieldListItem minIndent = try $ do (name, raw) <- rawFieldListItem minIndent term <- parseInlineFromString name @@ -212,7 +223,7 @@ fieldListItem minIndent = try $ do optional blanklines return (term, [contents]) -fieldList :: RSTParser Blocks +fieldList :: PandocMonad m => RSTParser m Blocks fieldList = try $ do indent <- length <$> lookAhead (many spaceChar) items <- many1 $ fieldListItem indent @@ -224,7 +235,7 @@ fieldList = try $ do -- line block -- -lineBlock :: RSTParser Blocks +lineBlock :: PandocMonad m => RSTParser m Blocks lineBlock = try $ do lines' <- lineBlockLines lines'' <- mapM parseInlineFromString lines' @@ -235,7 +246,7 @@ lineBlock = try $ do -- -- note: paragraph can end in a :: starting a code block -para :: RSTParser Blocks +para :: PandocMonad m => RSTParser m Blocks para = try $ do result <- trimInlines . mconcat <$> many1 inline option (B.plain result) $ try $ do @@ -248,18 +259,18 @@ para = try $ do <> raw _ -> return (B.para result) -plain :: RSTParser Blocks +plain :: PandocMonad m => RSTParser m Blocks plain = B.plain . trimInlines . mconcat <$> many1 inline -- -- header blocks -- -header :: RSTParser Blocks +header :: PandocMonad m => RSTParser m Blocks header = doubleHeader <|> singleHeader "header" -- a header with lines on top and bottom -doubleHeader :: RSTParser Blocks +doubleHeader :: PandocMonad m => RSTParser m Blocks doubleHeader = try $ do c <- oneOf underlineChars rest <- many (char c) -- the top line @@ -285,7 +296,7 @@ doubleHeader = try $ do return $ B.headerWith attr level txt -- a header with line on the bottom only -singleHeader :: RSTParser Blocks +singleHeader :: PandocMonad m => RSTParser m Blocks singleHeader = try $ do notFollowedBy' whitespace txt <- trimInlines . mconcat <$> many1 (do {notFollowedBy blankline; inline}) @@ -309,7 +320,7 @@ singleHeader = try $ do -- hrule block -- -hrule :: Parser [Char] st Blocks +hrule :: Monad m => ParserT [Char] st m Blocks hrule = try $ do chr <- oneOf underlineChars count 3 (char chr) @@ -323,14 +334,14 @@ hrule = try $ do -- -- read a line indented by a given string -indentedLine :: String -> Parser [Char] st [Char] +indentedLine :: Monad m => String -> ParserT [Char] st m [Char] indentedLine indents = try $ do string indents anyLine -- one or more indented lines, possibly separated by blank lines. -- any amount of indentation will work. -indentedBlock :: Parser [Char] st [Char] +indentedBlock :: Monad m => ParserT [Char] st m [Char] indentedBlock = try $ do indents <- lookAhead $ many1 spaceChar lns <- many1 $ try $ do b <- option "" blanklines @@ -339,24 +350,24 @@ indentedBlock = try $ do optional blanklines return $ unlines lns -quotedBlock :: Parser [Char] st [Char] +quotedBlock :: Monad m => ParserT [Char] st m [Char] quotedBlock = try $ do quote <- lookAhead $ oneOf "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~" lns <- many1 $ lookAhead (char quote) >> anyLine optional blanklines return $ unlines lns -codeBlockStart :: Parser [Char] st Char +codeBlockStart :: Monad m => ParserT [Char] st m Char codeBlockStart = string "::" >> blankline >> blankline -codeBlock :: Parser [Char] st Blocks +codeBlock :: Monad m => ParserT [Char] st m Blocks codeBlock = try $ codeBlockStart >> codeBlockBody -codeBlockBody :: Parser [Char] st Blocks +codeBlockBody :: Monad m => ParserT [Char] st m Blocks codeBlockBody = try $ B.codeBlock . stripTrailingNewlines <$> (indentedBlock <|> quotedBlock) -lhsCodeBlock :: RSTParser Blocks +lhsCodeBlock :: Monad m => RSTParser m Blocks lhsCodeBlock = try $ do getPosition >>= guard . (==1) . sourceColumn guardEnabled Ext_literate_haskell @@ -366,14 +377,14 @@ lhsCodeBlock = try $ do return $ B.codeBlockWith ("", ["sourceCode", "literate", "haskell"], []) $ intercalate "\n" lns -latexCodeBlock :: Parser [Char] st [[Char]] +latexCodeBlock :: Monad m => ParserT [Char] st m [[Char]] latexCodeBlock = try $ do try (latexBlockLine "\\begin{code}") many1Till anyLine (try $ latexBlockLine "\\end{code}") where latexBlockLine s = skipMany spaceChar >> string s >> blankline -birdCodeBlock :: Parser [Char] st [[Char]] +birdCodeBlock :: Monad m => ParserT [Char] st m [[Char]] birdCodeBlock = filterSpace <$> many1 birdTrackLine where filterSpace lns = -- if (as is normal) there is always a space after >, drop it @@ -381,14 +392,14 @@ birdCodeBlock = filterSpace <$> many1 birdTrackLine then map (drop 1) lns else lns -birdTrackLine :: Parser [Char] st [Char] +birdTrackLine :: Monad m => ParserT [Char] st m [Char] birdTrackLine = char '>' >> anyLine -- -- block quotes -- -blockQuote :: RSTParser Blocks +blockQuote :: PandocMonad m => RSTParser m Blocks blockQuote = do raw <- indentedBlock -- parse the extracted block, which may contain various block elements: @@ -399,10 +410,10 @@ blockQuote = do -- list blocks -- -list :: RSTParser Blocks +list :: PandocMonad m => RSTParser m Blocks list = choice [ bulletList, orderedList, definitionList ] "list" -definitionListItem :: RSTParser (Inlines, [Blocks]) +definitionListItem :: PandocMonad m => RSTParser m (Inlines, [Blocks]) definitionListItem = try $ do -- avoid capturing a directive or comment notFollowedBy (try $ char '.' >> char '.') @@ -412,11 +423,11 @@ definitionListItem = try $ do contents <- parseFromString parseBlocks $ raw ++ "\n" return (term, [contents]) -definitionList :: RSTParser Blocks +definitionList :: PandocMonad m => RSTParser m Blocks definitionList = B.definitionList <$> many1 definitionListItem -- parses bullet list start and returns its length (inc. following whitespace) -bulletListStart :: Parser [Char] st Int +bulletListStart :: Monad m => ParserT [Char] st m Int bulletListStart = try $ do notFollowedBy' hrule -- because hrules start out just like lists marker <- oneOf bulletListMarkers @@ -424,16 +435,16 @@ bulletListStart = try $ do return $ length (marker:white) -- parses ordered list start and returns its length (inc following whitespace) -orderedListStart :: ListNumberStyle +orderedListStart :: Monad m => ListNumberStyle -> ListNumberDelim - -> RSTParser Int + -> RSTParser m Int orderedListStart style delim = try $ do (_, markerLen) <- withHorizDisplacement (orderedListMarker style delim) white <- many1 spaceChar return $ markerLen + length white -- parse a line of a list item -listLine :: Int -> RSTParser [Char] +listLine :: Monad m => Int -> RSTParser m [Char] listLine markerLength = try $ do notFollowedBy blankline indentWith markerLength @@ -441,7 +452,7 @@ listLine markerLength = try $ do return $ line ++ "\n" -- indent by specified number of spaces (or equiv. tabs) -indentWith :: Int -> RSTParser [Char] +indentWith :: Monad m => Int -> RSTParser m [Char] indentWith num = do tabStop <- getOption readerTabStop if (num < tabStop) @@ -450,8 +461,8 @@ indentWith num = do (try (char '\t' >> count (num - tabStop) (char ' '))) ] -- parse raw text for one list item, excluding start marker and continuations -rawListItem :: RSTParser Int - -> RSTParser (Int, [Char]) +rawListItem :: Monad m => RSTParser m Int + -> RSTParser m (Int, [Char]) rawListItem start = try $ do markerLength <- start firstLine <- anyLine @@ -461,14 +472,15 @@ rawListItem start = try $ do -- continuation of a list item - indented and separated by blankline or -- (in compact lists) endline. -- Note: nested lists are parsed as continuations. -listContinuation :: Int -> RSTParser [Char] +listContinuation :: Monad m => Int -> RSTParser m [Char] listContinuation markerLength = try $ do blanks <- many1 blankline result <- many1 (listLine markerLength) return $ blanks ++ concat result -listItem :: RSTParser Int - -> RSTParser Blocks +listItem :: PandocMonad m + => RSTParser m Int + -> RSTParser m Blocks listItem start = try $ do (markerLength, first) <- rawListItem start rest <- many (listContinuation markerLength) @@ -490,21 +502,21 @@ listItem start = try $ do [Para xs, DefinitionList ys] -> B.fromList [Plain xs, DefinitionList ys] _ -> parsed -orderedList :: RSTParser Blocks +orderedList :: PandocMonad m => RSTParser m Blocks orderedList = try $ do (start, style, delim) <- lookAhead (anyOrderedListMarker <* spaceChar) items <- many1 (listItem (orderedListStart style delim)) let items' = compactify' items return $ B.orderedListWith (start, style, delim) items' -bulletList :: RSTParser Blocks +bulletList :: PandocMonad m => RSTParser m Blocks bulletList = B.bulletList . compactify' <$> many1 (listItem bulletListStart) -- -- directive (e.g. comment, container, compound-paragraph) -- -comment :: RSTParser Blocks +comment :: Monad m => RSTParser m Blocks comment = try $ do string ".." skipMany1 spaceChar <|> (() <$ lookAhead newline) @@ -513,11 +525,11 @@ comment = try $ do optional indentedBlock return mempty -directiveLabel :: RSTParser String +directiveLabel :: Monad m => RSTParser m String directiveLabel = map toLower <$> many1Till (letter <|> char '-') (try $ string "::") -directive :: RSTParser Blocks +directive :: PandocMonad m => RSTParser m Blocks directive = try $ do string ".." directive' @@ -526,7 +538,7 @@ directive = try $ do -- date -- include -- title -directive' :: RSTParser Blocks +directive' :: PandocMonad m => RSTParser m Blocks directive' = do skipMany1 spaceChar label <- directiveLabel @@ -614,13 +626,13 @@ directive' = do return $ B.divWith attrs children other -> do pos <- getPosition - addWarning (Just pos) $ "ignoring unknown directive: " ++ other + P.addWarningWithPos (Just pos) $ "ignoring unknown directive: " ++ other return mempty -- TODO: -- - Only supports :format: fields with a single format for :raw: roles, -- change Text.Pandoc.Definition.Format to fix -addNewRole :: String -> [(String, String)] -> RSTParser Blocks +addNewRole :: PandocMonad m => String -> [(String, String)] -> RSTParser m Blocks addNewRole roleString fields = do (role, parentRole) <- parseFromString inheritedRole roleString customRoles <- stateRstCustomRoles <$> getState @@ -642,20 +654,20 @@ addNewRole roleString fields = do -- warn about syntax we ignore flip mapM_ fields $ \(key, _) -> case key of - "language" -> when (baseRole /= "code") $ addWarning Nothing $ + "language" -> when (baseRole /= "code") $ lift $ P.warn $ "ignoring :language: field because the parent of role :" ++ role ++ ": is :" ++ baseRole ++ ": not :code:" - "format" -> when (baseRole /= "raw") $ addWarning Nothing $ + "format" -> when (baseRole /= "raw") $ lift $ P.warn $ "ignoring :format: field because the parent of role :" ++ role ++ ": is :" ++ baseRole ++ ": not :raw:" - _ -> addWarning Nothing $ "ignoring unknown field :" ++ key ++ + _ -> lift $ P.warn $ "ignoring unknown field :" ++ key ++ ": in definition of role :" ++ role ++ ": in" when (parentRole == "raw" && countKeys "format" > 1) $ - addWarning Nothing $ + lift $ P.warn $ "ignoring :format: fields after the first in the definition of role :" ++ role ++": in" when (parentRole == "code" && countKeys "language" > 1) $ - addWarning Nothing $ + lift $ P.warn $ "ignoring :language: fields after the first in the definition of role :" ++ role ++": in" @@ -700,7 +712,7 @@ extractUnicodeChar s = maybe Nothing (\c -> Just (c,rest)) mbc where (ds,rest) = span isHexDigit s mbc = safeRead ('\'':'\\':'x':ds ++ "'") -extractCaption :: RSTParser (Inlines, Blocks) +extractCaption :: PandocMonad m => RSTParser m (Inlines, Blocks) extractCaption = do capt <- trimInlines . mconcat <$> many inline legend <- optional blanklines >> (mconcat <$> many block) @@ -712,7 +724,7 @@ toChunks = dropWhile null . map (trim . unlines) . splitBy (all (`elem` (" \t" :: String))) . lines -codeblock :: [String] -> Maybe String -> String -> String -> RSTParser Blocks +codeblock :: [String] -> Maybe String -> String -> String -> RSTParser m Blocks codeblock classes numberLines lang body = return $ B.codeBlockWith attribs $ stripTrailingNewlines body where attribs = ("", classes', kvs) @@ -728,7 +740,7 @@ codeblock classes numberLines lang body = --- note block --- -noteBlock :: RSTParser [Char] +noteBlock :: Monad m => RSTParser m [Char] noteBlock = try $ do startPos <- getPosition string ".." @@ -747,7 +759,7 @@ noteBlock = try $ do -- return blanks so line count isn't affected return $ replicate (sourceLine endPos - sourceLine startPos) '\n' -noteMarker :: RSTParser [Char] +noteMarker :: Monad m => RSTParser m [Char] noteMarker = do char '[' res <- many1 digit @@ -760,13 +772,13 @@ noteMarker = do -- reference key -- -quotedReferenceName :: RSTParser Inlines +quotedReferenceName :: PandocMonad m => RSTParser m Inlines quotedReferenceName = try $ do char '`' >> notFollowedBy (char '`') -- `` means inline code! label' <- trimInlines . mconcat <$> many1Till inline (char '`') return label' -unquotedReferenceName :: RSTParser Inlines +unquotedReferenceName :: PandocMonad m => RSTParser m Inlines unquotedReferenceName = try $ do label' <- trimInlines . mconcat <$> many1Till inline (lookAhead $ char ':') return label' @@ -775,24 +787,24 @@ unquotedReferenceName = try $ do -- plus isolated (no two adjacent) internal hyphens, underscores, -- periods, colons and plus signs; no whitespace or other characters -- are allowed. -simpleReferenceName' :: Parser [Char] st String +simpleReferenceName' :: Monad m => ParserT [Char] st m String simpleReferenceName' = do x <- alphaNum xs <- many $ alphaNum <|> (try $ oneOf "-_:+." <* lookAhead alphaNum) return (x:xs) -simpleReferenceName :: Parser [Char] st Inlines +simpleReferenceName :: Monad m => ParserT [Char] st m Inlines simpleReferenceName = do raw <- simpleReferenceName' return $ B.str raw -referenceName :: RSTParser Inlines +referenceName :: PandocMonad m => RSTParser m Inlines referenceName = quotedReferenceName <|> (try $ simpleReferenceName <* lookAhead (char ':')) <|> unquotedReferenceName -referenceKey :: RSTParser [Char] +referenceKey :: PandocMonad m => RSTParser m [Char] referenceKey = do startPos <- getPosition choice [substKey, anonymousKey, regularKey] @@ -801,7 +813,7 @@ referenceKey = do -- return enough blanks to replace key return $ replicate (sourceLine endPos - sourceLine startPos) '\n' -targetURI :: Parser [Char] st [Char] +targetURI :: Monad m => ParserT [Char] st m [Char] targetURI = do skipSpaces optional newline @@ -810,7 +822,7 @@ targetURI = do blanklines return $ escapeURI $ trim $ contents -substKey :: RSTParser () +substKey :: PandocMonad m => RSTParser m () substKey = try $ do string ".." skipMany1 spaceChar @@ -828,7 +840,7 @@ substKey = try $ do let key = toKey $ stripFirstAndLast ref updateState $ \s -> s{ stateSubstitutions = M.insert key il $ stateSubstitutions s } -anonymousKey :: RSTParser () +anonymousKey :: Monad m => RSTParser m () anonymousKey = try $ do oneOfStrings [".. __:", "__"] src <- targetURI @@ -842,7 +854,7 @@ stripTicks = reverse . stripTick . reverse . stripTick where stripTick ('`':xs) = xs stripTick xs = xs -regularKey :: RSTParser () +regularKey :: PandocMonad m => RSTParser m () regularKey = try $ do string ".. _" (_,ref) <- withRaw referenceName @@ -869,31 +881,31 @@ regularKey = try $ do -- Grid tables TODO: -- - column spans -dashedLine :: Char -> Parser [Char] st (Int, Int) +dashedLine :: Monad m => Char -> ParserT [Char] st m (Int, Int) dashedLine ch = do dashes <- many1 (char ch) sp <- many (char ' ') return (length dashes, length $ dashes ++ sp) -simpleDashedLines :: Char -> Parser [Char] st [(Int,Int)] +simpleDashedLines :: Monad m => Char -> ParserT [Char] st m [(Int,Int)] simpleDashedLines ch = try $ many1 (dashedLine ch) -- Parse a table row separator -simpleTableSep :: Char -> RSTParser Char +simpleTableSep :: Monad m => Char -> RSTParser m Char simpleTableSep ch = try $ simpleDashedLines ch >> newline -- Parse a table footer -simpleTableFooter :: RSTParser [Char] +simpleTableFooter :: Monad m => RSTParser m [Char] simpleTableFooter = try $ simpleTableSep '=' >> blanklines -- Parse a raw line and split it into chunks by indices. -simpleTableRawLine :: [Int] -> RSTParser [String] +simpleTableRawLine :: Monad m => [Int] -> RSTParser m [String] simpleTableRawLine indices = do line <- many1Till anyChar newline return (simpleTableSplitLine indices line) -- Parse a table row and return a list of blocks (columns). -simpleTableRow :: [Int] -> RSTParser [[Block]] +simpleTableRow :: PandocMonad m => [Int] -> RSTParser m [[Block]] simpleTableRow indices = do notFollowedBy' simpleTableFooter firstLine <- simpleTableRawLine indices @@ -906,8 +918,9 @@ simpleTableSplitLine indices line = map trim $ tail $ splitByIndices (init indices) line -simpleTableHeader :: Bool -- ^ Headerless table - -> RSTParser ([[Block]], [Alignment], [Int]) +simpleTableHeader :: PandocMonad m + => Bool -- ^ Headerless table + -> RSTParser m ([[Block]], [Alignment], [Int]) simpleTableHeader headless = try $ do optional blanklines rawContent <- if headless @@ -926,8 +939,9 @@ simpleTableHeader headless = try $ do return (heads, aligns, indices) -- Parse a simple table. -simpleTable :: Bool -- ^ Headerless table - -> RSTParser Blocks +simpleTable :: PandocMonad m + => Bool -- ^ Headerless table + -> RSTParser m Blocks simpleTable headless = do Table c a _w h l <- tableWith (simpleTableHeader headless) simpleTableRow sep simpleTableFooter -- Simple tables get 0s for relative column widths (i.e., use default) @@ -935,12 +949,13 @@ simpleTable headless = do where sep = return () -- optional (simpleTableSep '-') -gridTable :: Bool -- ^ Headerless table - -> RSTParser Blocks +gridTable :: PandocMonad m + => Bool -- ^ Headerless table + -> RSTParser m Blocks gridTable headerless = B.singleton <$> gridTableWith (B.toList <$> parseBlocks) headerless -table :: RSTParser Blocks +table :: PandocMonad m => RSTParser m Blocks table = gridTable False <|> simpleTable False <|> gridTable True <|> simpleTable True "table" @@ -948,7 +963,7 @@ table = gridTable False <|> simpleTable False <|> -- inline -- -inline :: RSTParser Inlines +inline :: PandocMonad m => RSTParser m Inlines inline = choice [ note -- can start with whitespace, so try before ws , whitespace , link @@ -964,29 +979,29 @@ inline = choice [ note -- can start with whitespace, so try before ws , escapedChar , symbol ] "inline" -parseInlineFromString :: String -> RSTParser Inlines +parseInlineFromString :: PandocMonad m => String -> RSTParser m Inlines parseInlineFromString = parseFromString (trimInlines . mconcat <$> many inline) -hyphens :: RSTParser Inlines +hyphens :: Monad m => RSTParser m Inlines hyphens = do result <- many1 (char '-') optional endline -- don't want to treat endline after hyphen or dash as a space return $ B.str result -escapedChar :: Parser [Char] st Inlines +escapedChar :: Monad m => ParserT [Char] st m Inlines escapedChar = do c <- escaped anyChar return $ if c == ' ' -- '\ ' is null in RST then mempty else B.str [c] -symbol :: RSTParser Inlines +symbol :: Monad m => RSTParser m Inlines symbol = do result <- oneOf specialChars return $ B.str [result] -- parses inline code, between codeStart and codeEnd -code :: RSTParser Inlines +code :: Monad m => RSTParser m Inlines code = try $ do string "``" result <- manyTill anyChar (try (string "``")) @@ -994,7 +1009,7 @@ code = try $ do $ trim $ unwords $ lines result -- succeeds only if we're not right after a str (ie. in middle of word) -atStart :: RSTParser a -> RSTParser a +atStart :: Monad m => RSTParser m a -> RSTParser m a atStart p = do pos <- getPosition st <- getState @@ -1002,11 +1017,11 @@ atStart p = do guard $ stateLastStrPos st /= Just pos p -emph :: RSTParser Inlines +emph :: PandocMonad m => RSTParser m Inlines emph = B.emph . trimInlines . mconcat <$> enclosed (atStart $ char '*') (char '*') inline -strong :: RSTParser Inlines +strong :: PandocMonad m => RSTParser m Inlines strong = B.strong . trimInlines . mconcat <$> enclosed (atStart $ string "**") (try $ string "**") inline @@ -1018,12 +1033,12 @@ strong = B.strong . trimInlines . mconcat <$> -- - Classes are silently discarded in addNewRole -- - Lacks sensible implementation for title-reference (which is the default) -- - Allows direct use of the :raw: role, rST only allows inherited use. -interpretedRole :: RSTParser Inlines +interpretedRole :: PandocMonad m => RSTParser m Inlines interpretedRole = try $ do (role, contents) <- roleBefore <|> roleAfter renderRole contents Nothing role nullAttr -renderRole :: String -> Maybe String -> String -> Attr -> RSTParser Inlines +renderRole :: PandocMonad m => String -> Maybe String -> String -> Attr -> RSTParser m Inlines renderRole contents fmt role attr = case role of "sup" -> return $ B.superscript $ B.str contents "superscript" -> return $ B.superscript $ B.str contents @@ -1050,7 +1065,7 @@ renderRole contents fmt role attr = case role of renderRole contents newFmt newRole newAttr Nothing -> do pos <- getPosition - addWarning (Just pos) $ "ignoring unknown role :" ++ custom ++ ": in" + P.addWarningWithPos (Just pos) $ "ignoring unknown role :" ++ custom ++ ": in" return $ B.str contents -- Undefined role where titleRef ref = return $ B.str ref -- FIXME: Not a sensible behaviour @@ -1063,31 +1078,31 @@ renderRole contents fmt role attr = case role of addClass :: String -> Attr -> Attr addClass c (ident, classes, keyValues) = (ident, union classes [c], keyValues) -roleName :: RSTParser String +roleName :: PandocMonad m => RSTParser m String roleName = many1 (letter <|> char '-') -roleMarker :: RSTParser String +roleMarker :: PandocMonad m => RSTParser m String roleMarker = char ':' *> roleName <* char ':' -roleBefore :: RSTParser (String,String) +roleBefore :: PandocMonad m => RSTParser m (String,String) roleBefore = try $ do role <- roleMarker contents <- unmarkedInterpretedText return (role,contents) -roleAfter :: RSTParser (String,String) +roleAfter :: PandocMonad m => RSTParser m (String,String) roleAfter = try $ do contents <- unmarkedInterpretedText role <- roleMarker <|> (stateRstDefaultRole <$> getState) return (role,contents) -unmarkedInterpretedText :: RSTParser [Char] +unmarkedInterpretedText :: PandocMonad m => RSTParser m [Char] unmarkedInterpretedText = enclosed (atStart $ char '`') (char '`') anyChar -whitespace :: RSTParser Inlines +whitespace :: PandocMonad m => RSTParser m Inlines whitespace = B.space <$ skipMany1 spaceChar "whitespace" -str :: RSTParser Inlines +str :: Monad m => RSTParser m Inlines str = do let strChar = noneOf ("\t\n " ++ specialChars) result <- many1 strChar @@ -1095,7 +1110,7 @@ str = do return $ B.str result -- an endline character that can be treated as a space, not a structural break -endline :: RSTParser Inlines +endline :: Monad m => RSTParser m Inlines endline = try $ do newline notFollowedBy blankline @@ -1111,10 +1126,10 @@ endline = try $ do -- links -- -link :: RSTParser Inlines +link :: PandocMonad m => RSTParser m Inlines link = choice [explicitLink, referenceLink, autoLink] "link" -explicitLink :: RSTParser Inlines +explicitLink :: PandocMonad m => RSTParser m Inlines explicitLink = try $ do char '`' notFollowedBy (char '`') -- `` marks start of inline code @@ -1143,7 +1158,7 @@ explicitLink = try $ do _ -> return (src, "", nullAttr) return $ B.linkWith attr (escapeURI src') tit label'' -referenceLink :: RSTParser Inlines +referenceLink :: PandocMonad m => RSTParser m Inlines referenceLink = try $ do (label',ref) <- withRaw (quotedReferenceName <|> simpleReferenceName) <* char '_' @@ -1169,20 +1184,20 @@ referenceLink = try $ do when (isAnonKey key) $ updateState $ \s -> s{ stateKeys = M.delete key keyTable } return $ B.linkWith attr src tit label' -autoURI :: RSTParser Inlines +autoURI :: Monad m => RSTParser m Inlines autoURI = do (orig, src) <- uri return $ B.link src "" $ B.str orig -autoEmail :: RSTParser Inlines +autoEmail :: Monad m => RSTParser m Inlines autoEmail = do (orig, src) <- emailAddress return $ B.link src "" $ B.str orig -autoLink :: RSTParser Inlines +autoLink :: PandocMonad m => RSTParser m Inlines autoLink = autoURI <|> autoEmail -subst :: RSTParser Inlines +subst :: PandocMonad m => RSTParser m Inlines subst = try $ do (_,ref) <- withRaw $ enclosed (char '|') (char '|') inline state <- getState @@ -1196,7 +1211,7 @@ subst = try $ do return mempty Just target -> return target -note :: RSTParser Inlines +note :: PandocMonad m => RSTParser m Inlines note = try $ do optional whitespace ref <- noteMarker @@ -1224,20 +1239,20 @@ note = try $ do updateState $ \st -> st{ stateNotes = newnotes } return $ B.note contents -smart :: RSTParser Inlines +smart :: PandocMonad m => RSTParser m Inlines smart = do getOption readerSmart >>= guard doubleQuoted <|> singleQuoted <|> choice [apostrophe, dash, ellipses] -singleQuoted :: RSTParser Inlines +singleQuoted :: PandocMonad m => RSTParser m Inlines singleQuoted = try $ do singleQuoteStart withQuoteContext InSingleQuote $ B.singleQuoted . trimInlines . mconcat <$> many1Till inline singleQuoteEnd -doubleQuoted :: RSTParser Inlines +doubleQuoted :: PandocMonad m => RSTParser m Inlines doubleQuoted = try $ do doubleQuoteStart withQuoteContext InDoubleQuote $ diff --git a/src/Text/Pandoc/Readers/TWiki.hs b/src/Text/Pandoc/Readers/TWiki.hs index 76a25ad82..091dcd7b1 100644 --- a/src/Text/Pandoc/Readers/TWiki.hs +++ b/src/Text/Pandoc/Readers/TWiki.hs @@ -30,7 +30,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Conversion of twiki text to 'Pandoc' document. -} module Text.Pandoc.Readers.TWiki ( readTWiki - , readTWikiWithWarnings ) where import Text.Pandoc.Definition @@ -48,17 +47,25 @@ import Data.Char (isAlphaNum) import qualified Data.Foldable as F import Text.Pandoc.Error --- | Read twiki from an input string and return a Pandoc document. -readTWiki :: ReaderOptions -- ^ Reader options - -> String -- ^ String to parse (assuming @'\n'@ line endings) - -> Either PandocError Pandoc -readTWiki opts s = - (readWith parseTWiki) def{ stateOptions = opts } (s ++ "\n\n") +import Control.Monad.Except (throwError) +import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) +import qualified Text.Pandoc.Class as P -readTWikiWithWarnings :: ReaderOptions -- ^ Reader options +-- | Read twiki from an input string and return a Pandoc document. +readTWiki :: PandocMonad m + => ReaderOptions + -> String + -> m Pandoc +readTWiki opts s = case readTWikiWithWarnings' opts s of + Right (doc, warns) -> do + mapM_ P.warn warns + return doc + Left _ -> throwError $ PandocParseError "couldn't parse TWiki" + +readTWikiWithWarnings' :: ReaderOptions -- ^ Reader options -> String -- ^ String to parse (assuming @'\n'@ line endings) -> Either PandocError (Pandoc, [String]) -readTWikiWithWarnings opts s = +readTWikiWithWarnings' opts s = (readWith parseTWikiWithWarnings) def{ stateOptions = opts } (s ++ "\n\n") where parseTWikiWithWarnings = do doc <- parseTWiki diff --git a/src/Text/Pandoc/Readers/Textile.hs b/src/Text/Pandoc/Readers/Textile.hs index 8dbbf7be2..5494695f5 100644 --- a/src/Text/Pandoc/Readers/Textile.hs +++ b/src/Text/Pandoc/Readers/Textile.hs @@ -68,18 +68,23 @@ import Control.Monad ( guard, liftM, when ) import Data.Monoid ((<>)) import Text.Printf import Debug.Trace (trace) -import Text.Pandoc.Error +import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) +import Control.Monad.Except (throwError) -- | Parse a Textile text and return a Pandoc document. -readTextile :: ReaderOptions -- ^ Reader options +readTextile :: PandocMonad m + => ReaderOptions -- ^ Reader options -> String -- ^ String to parse (assuming @'\n'@ line endings) - -> Either PandocError Pandoc -readTextile opts s = - (readWith parseTextile) def{ stateOptions = opts } (s ++ "\n\n") + -> m Pandoc +readTextile opts s = do + parsed <- readWithM parseTextile def{ stateOptions = opts } (s ++ "\n\n") + case parsed of + Right result -> return result + Left _ -> throwError $ PandocParseError "textile parse error" -- | Generate a Pandoc ADT from a textile document -parseTextile :: Parser [Char] ParserState Pandoc +parseTextile :: PandocMonad m => ParserT [Char] ParserState m Pandoc parseTextile = do -- textile allows raw HTML and does smart punctuation by default, -- but we do not enable smart punctuation unless it is explicitly @@ -103,10 +108,10 @@ parseTextile = do blocks <- parseBlocks return $ Pandoc nullMeta (B.toList blocks) -- FIXME -noteMarker :: Parser [Char] ParserState [Char] +noteMarker :: PandocMonad m => ParserT [Char] ParserState m [Char] noteMarker = skipMany spaceChar >> string "fn" >> manyTill digit (char '.') -noteBlock :: Parser [Char] ParserState [Char] +noteBlock :: PandocMonad m => ParserT [Char] ParserState m [Char] noteBlock = try $ do startPos <- getPosition ref <- noteMarker @@ -121,11 +126,11 @@ noteBlock = try $ do return $ replicate (sourceLine endPos - sourceLine startPos) '\n' -- | Parse document blocks -parseBlocks :: Parser [Char] ParserState Blocks +parseBlocks :: PandocMonad m => ParserT [Char] ParserState m Blocks parseBlocks = mconcat <$> manyTill block eof -- | Block parsers list tried in definition order -blockParsers :: [Parser [Char] ParserState Blocks] +blockParsers :: PandocMonad m => [ParserT [Char] ParserState m Blocks] blockParsers = [ codeBlock , header , blockQuote @@ -140,7 +145,7 @@ blockParsers = [ codeBlock ] -- | Any block in the order of definition of blockParsers -block :: Parser [Char] ParserState Blocks +block :: PandocMonad m => ParserT [Char] ParserState m Blocks block = do res <- choice blockParsers "block" pos <- getPosition @@ -150,16 +155,16 @@ block = do (take 60 $ show $ B.toList res)) (return ()) return res -commentBlock :: Parser [Char] ParserState Blocks +commentBlock :: PandocMonad m => ParserT [Char] ParserState m Blocks commentBlock = try $ do string "###." manyTill anyLine blanklines return mempty -codeBlock :: Parser [Char] ParserState Blocks +codeBlock :: PandocMonad m => ParserT [Char] ParserState m Blocks codeBlock = codeBlockBc <|> codeBlockPre -codeBlockBc :: Parser [Char] ParserState Blocks +codeBlockBc :: PandocMonad m => ParserT [Char] ParserState m Blocks codeBlockBc = try $ do string "bc." extended <- option False (True <$ char '.') @@ -179,7 +184,7 @@ trimTrailingNewlines :: String -> String trimTrailingNewlines = reverse . dropWhile (=='\n') . reverse -- | Code Blocks in Textile are between
 and 
-codeBlockPre :: Parser [Char] ParserState Blocks +codeBlockPre :: PandocMonad m => ParserT [Char] ParserState m Blocks codeBlockPre = try $ do (t@(TagOpen _ attrs),_) <- htmlTag (tagOpen (=="pre") (const True)) result' <- manyTill anyChar (htmlTag (tagClose (=="pre"))) @@ -198,7 +203,7 @@ codeBlockPre = try $ do return $ B.codeBlockWith (ident,classes,kvs) result''' -- | Header of the form "hN. content" with N in 1..6 -header :: Parser [Char] ParserState Blocks +header :: PandocMonad m => ParserT [Char] ParserState m Blocks header = try $ do char 'h' level <- digitToInt <$> oneOf "123456" @@ -210,14 +215,14 @@ header = try $ do return $ B.headerWith attr' level name -- | Blockquote of the form "bq. content" -blockQuote :: Parser [Char] ParserState Blocks +blockQuote :: PandocMonad m => ParserT [Char] ParserState m Blocks blockQuote = try $ do string "bq" >> attributes >> char '.' >> whitespace B.blockQuote <$> para -- Horizontal rule -hrule :: Parser [Char] st Blocks +hrule :: PandocMonad m => ParserT [Char] st m Blocks hrule = try $ do skipSpaces start <- oneOf "-*" @@ -232,39 +237,39 @@ hrule = try $ do -- | Can be a bullet list or an ordered list. This implementation is -- strict in the nesting, sublist must start at exactly "parent depth -- plus one" -anyList :: Parser [Char] ParserState Blocks +anyList :: PandocMonad m => ParserT [Char] ParserState m Blocks anyList = try $ anyListAtDepth 1 <* blanklines -- | This allow one type of list to be nested into an other type, -- provided correct nesting -anyListAtDepth :: Int -> Parser [Char] ParserState Blocks +anyListAtDepth :: PandocMonad m => Int -> ParserT [Char] ParserState m Blocks anyListAtDepth depth = choice [ bulletListAtDepth depth, orderedListAtDepth depth, definitionList ] -- | Bullet List of given depth, depth being the number of leading '*' -bulletListAtDepth :: Int -> Parser [Char] ParserState Blocks +bulletListAtDepth :: PandocMonad m => Int -> ParserT [Char] ParserState m Blocks bulletListAtDepth depth = try $ B.bulletList <$> many1 (bulletListItemAtDepth depth) -- | Bullet List Item of given depth, depth being the number of -- leading '*' -bulletListItemAtDepth :: Int -> Parser [Char] ParserState Blocks +bulletListItemAtDepth :: PandocMonad m => Int -> ParserT [Char] ParserState m Blocks bulletListItemAtDepth = genericListItemAtDepth '*' -- | Ordered List of given depth, depth being the number of -- leading '#' -orderedListAtDepth :: Int -> Parser [Char] ParserState Blocks +orderedListAtDepth :: PandocMonad m => Int -> ParserT [Char] ParserState m Blocks orderedListAtDepth depth = try $ do items <- many1 (orderedListItemAtDepth depth) return $ B.orderedList items -- | Ordered List Item of given depth, depth being the number of -- leading '#' -orderedListItemAtDepth :: Int -> Parser [Char] ParserState Blocks +orderedListItemAtDepth :: PandocMonad m => Int -> ParserT [Char] ParserState m Blocks orderedListItemAtDepth = genericListItemAtDepth '#' -- | Common implementation of list items -genericListItemAtDepth :: Char -> Int -> Parser [Char] ParserState Blocks +genericListItemAtDepth :: PandocMonad m => Char -> Int -> ParserT [Char] ParserState m Blocks genericListItemAtDepth c depth = try $ do count depth (char c) >> attributes >> whitespace p <- mconcat <$> many listInline @@ -273,25 +278,25 @@ genericListItemAtDepth c depth = try $ do return $ (B.plain p) <> sublist -- | A definition list is a set of consecutive definition items -definitionList :: Parser [Char] ParserState Blocks +definitionList :: PandocMonad m => ParserT [Char] ParserState m Blocks definitionList = try $ B.definitionList <$> many1 definitionListItem -- | List start character. -listStart :: Parser [Char] ParserState () +listStart :: PandocMonad m => ParserT [Char] ParserState m () listStart = genericListStart '*' <|> () <$ genericListStart '#' <|> () <$ definitionListStart -genericListStart :: Char -> Parser [Char] st () +genericListStart :: PandocMonad m => Char -> ParserT [Char] st m () genericListStart c = () <$ try (many1 (char c) >> whitespace) -basicDLStart :: Parser [Char] ParserState () +basicDLStart :: PandocMonad m => ParserT [Char] ParserState m () basicDLStart = do char '-' whitespace notFollowedBy newline -definitionListStart :: Parser [Char] ParserState Inlines +definitionListStart :: PandocMonad m => ParserT [Char] ParserState m Inlines definitionListStart = try $ do basicDLStart trimInlines . mconcat <$> @@ -300,7 +305,7 @@ definitionListStart = try $ do <|> try (lookAhead (() <$ string ":=")) ) -listInline :: Parser [Char] ParserState Inlines +listInline :: PandocMonad m => ParserT [Char] ParserState m Inlines listInline = try (notFollowedBy newline >> inline) <|> try (endline <* notFollowedBy listStart) @@ -308,15 +313,15 @@ listInline = try (notFollowedBy newline >> inline) -- the term defined, then spaces and ":=". The definition follows, on -- the same single line, or spaned on multiple line, after a line -- break. -definitionListItem :: Parser [Char] ParserState (Inlines, [Blocks]) +definitionListItem :: PandocMonad m => ParserT [Char] ParserState m (Inlines, [Blocks]) definitionListItem = try $ do term <- (mconcat . intersperse B.linebreak) <$> many1 definitionListStart def' <- string ":=" *> optional whitespace *> (multilineDef <|> inlineDef) return (term, def') - where inlineDef :: Parser [Char] ParserState [Blocks] + where inlineDef :: PandocMonad m => ParserT [Char] ParserState m [Blocks] inlineDef = liftM (\d -> [B.plain d]) $ optional whitespace >> (trimInlines . mconcat <$> many listInline) <* newline - multilineDef :: Parser [Char] ParserState [Blocks] + multilineDef :: PandocMonad m => ParserT [Char] ParserState m [Blocks] multilineDef = try $ do optional whitespace >> newline s <- many1Till anyChar (try (string "=:" >> newline)) @@ -327,7 +332,7 @@ definitionListItem = try $ do -- raw content -- | A raw Html Block, optionally followed by blanklines -rawHtmlBlock :: Parser [Char] ParserState Blocks +rawHtmlBlock :: PandocMonad m => ParserT [Char] ParserState m Blocks rawHtmlBlock = try $ do skipMany spaceChar (_,b) <- htmlTag isBlockTag @@ -335,14 +340,14 @@ rawHtmlBlock = try $ do return $ B.rawBlock "html" b -- | Raw block of LaTeX content -rawLaTeXBlock' :: Parser [Char] ParserState Blocks +rawLaTeXBlock' :: PandocMonad m => ParserT [Char] ParserState m Blocks rawLaTeXBlock' = do guardEnabled Ext_raw_tex B.rawBlock "latex" <$> (rawLaTeXBlock <* spaces) -- | In textile, paragraphs are separated by blank lines. -para :: Parser [Char] ParserState Blocks +para :: PandocMonad m => ParserT [Char] ParserState m Blocks para = B.para . trimInlines . mconcat <$> many1 inline -- Tables @@ -353,7 +358,7 @@ toAlignment '>' = AlignRight toAlignment '=' = AlignCenter toAlignment _ = AlignDefault -cellAttributes :: Parser [Char] ParserState (Bool, Alignment) +cellAttributes :: PandocMonad m => ParserT [Char] ParserState m (Bool, Alignment) cellAttributes = try $ do isHeader <- option False (True <$ char '_') -- we just ignore colspan and rowspan markers: @@ -366,7 +371,7 @@ cellAttributes = try $ do return (isHeader, alignment) -- | A table cell spans until a pipe | -tableCell :: Parser [Char] ParserState ((Bool, Alignment), Blocks) +tableCell :: PandocMonad m => ParserT [Char] ParserState m ((Bool, Alignment), Blocks) tableCell = try $ do char '|' (isHeader, alignment) <- option (False, AlignDefault) $ cellAttributes @@ -377,7 +382,7 @@ tableCell = try $ do return ((isHeader, alignment), B.plain content) -- | A table row is made of many table cells -tableRow :: Parser [Char] ParserState [((Bool, Alignment), Blocks)] +tableRow :: PandocMonad m => ParserT [Char] ParserState m [((Bool, Alignment), Blocks)] tableRow = try $ do -- skip optional row attributes optional $ try $ do @@ -387,7 +392,7 @@ tableRow = try $ do many1 tableCell <* char '|' <* blankline -- | A table with an optional header. -table :: Parser [Char] ParserState Blocks +table :: PandocMonad m => ParserT [Char] ParserState m Blocks table = try $ do -- ignore table attributes caption <- option mempty $ try $ do @@ -411,7 +416,7 @@ table = try $ do (map (map snd) rows) -- | Ignore markers for cols, thead, tfoot. -ignorableRow :: Parser [Char] ParserState () +ignorableRow :: PandocMonad m => ParserT [Char] ParserState m () ignorableRow = try $ do char '|' oneOf ":^-~" @@ -420,7 +425,7 @@ ignorableRow = try $ do _ <- anyLine return () -explicitBlockStart :: String -> Parser [Char] ParserState () +explicitBlockStart :: PandocMonad m => String -> ParserT [Char] ParserState m () explicitBlockStart name = try $ do string name attributes @@ -430,9 +435,10 @@ explicitBlockStart name = try $ do -- | Blocks like 'p' and 'table' do not need explicit block tag. -- However, they can be used to set HTML/CSS attributes when needed. -maybeExplicitBlock :: String -- ^ block tag name - -> Parser [Char] ParserState Blocks -- ^ implicit block - -> Parser [Char] ParserState Blocks +maybeExplicitBlock :: PandocMonad m + => String -- ^ block tag name + -> ParserT [Char] ParserState m Blocks -- ^ implicit block + -> ParserT [Char] ParserState m Blocks maybeExplicitBlock name blk = try $ do optional $ explicitBlockStart name blk @@ -445,12 +451,12 @@ maybeExplicitBlock name blk = try $ do -- | Any inline element -inline :: Parser [Char] ParserState Inlines +inline :: PandocMonad m => ParserT [Char] ParserState m Inlines inline = do choice inlineParsers "inline" -- | Inline parsers tried in order -inlineParsers :: [Parser [Char] ParserState Inlines] +inlineParsers :: PandocMonad m => [ParserT [Char] ParserState m Inlines] inlineParsers = [ str , whitespace , endline @@ -470,7 +476,7 @@ inlineParsers = [ str ] -- | Inline markups -inlineMarkup :: Parser [Char] ParserState Inlines +inlineMarkup :: PandocMonad m => ParserT [Char] ParserState m Inlines inlineMarkup = choice [ simpleInline (string "??") (B.cite []) , simpleInline (string "**") B.strong , simpleInline (string "__") B.emph @@ -484,29 +490,29 @@ inlineMarkup = choice [ simpleInline (string "??") (B.cite []) ] -- | Trademark, registered, copyright -mark :: Parser [Char] st Inlines +mark :: PandocMonad m => ParserT [Char] st m Inlines mark = try $ char '(' >> (try tm <|> try reg <|> copy) -reg :: Parser [Char] st Inlines +reg :: PandocMonad m => ParserT [Char] st m Inlines reg = do oneOf "Rr" char ')' return $ B.str "\174" -tm :: Parser [Char] st Inlines +tm :: PandocMonad m => ParserT [Char] st m Inlines tm = do oneOf "Tt" oneOf "Mm" char ')' return $ B.str "\8482" -copy :: Parser [Char] st Inlines +copy :: PandocMonad m => ParserT [Char] st m Inlines copy = do oneOf "Cc" char ')' return $ B.str "\169" -note :: Parser [Char] ParserState Inlines +note :: PandocMonad m => ParserT [Char] ParserState m Inlines note = try $ do ref <- (char '[' *> many1 digit <* char ']') notes <- stateNotes <$> getState @@ -530,13 +536,13 @@ wordBoundaries :: [Char] wordBoundaries = markupChars ++ stringBreakers -- | Parse a hyphened sequence of words -hyphenedWords :: Parser [Char] ParserState String +hyphenedWords :: PandocMonad m => ParserT [Char] ParserState m String hyphenedWords = do x <- wordChunk xs <- many (try $ char '-' >> wordChunk) return $ intercalate "-" (x:xs) -wordChunk :: Parser [Char] ParserState String +wordChunk :: PandocMonad m => ParserT [Char] ParserState m String wordChunk = try $ do hd <- noneOf wordBoundaries tl <- many ( (noneOf wordBoundaries) <|> @@ -545,7 +551,7 @@ wordChunk = try $ do return $ hd:tl -- | Any string -str :: Parser [Char] ParserState Inlines +str :: PandocMonad m => ParserT [Char] ParserState m Inlines str = do baseStr <- hyphenedWords -- RedCloth compliance : if parsed word is uppercase and immediatly @@ -558,11 +564,11 @@ str = do return $ B.str fullStr -- | Some number of space chars -whitespace :: Parser [Char] st Inlines +whitespace :: PandocMonad m => ParserT [Char] st m Inlines whitespace = many1 spaceChar >> return B.space "whitespace" -- | In Textile, an isolated endline character is a line break -endline :: Parser [Char] ParserState Inlines +endline :: PandocMonad m => ParserT [Char] ParserState m Inlines endline = try $ do newline notFollowedBy blankline @@ -570,18 +576,18 @@ endline = try $ do notFollowedBy rawHtmlBlock return B.linebreak -rawHtmlInline :: Parser [Char] ParserState Inlines +rawHtmlInline :: PandocMonad m => ParserT [Char] ParserState m Inlines rawHtmlInline = B.rawInline "html" . snd <$> htmlTag isInlineTag -- | Raw LaTeX Inline -rawLaTeXInline' :: Parser [Char] ParserState Inlines +rawLaTeXInline' :: PandocMonad m => ParserT [Char] ParserState m Inlines rawLaTeXInline' = try $ do guardEnabled Ext_raw_tex B.singleton <$> rawLaTeXInline -- | Textile standard link syntax is "label":target. But we -- can also have ["label":target]. -link :: Parser [Char] ParserState Inlines +link :: PandocMonad m => ParserT [Char] ParserState m Inlines link = try $ do bracketed <- (True <$ char '[') <|> return False char '"' *> notFollowedBy (oneOf " \t\n\r") @@ -600,7 +606,7 @@ link = try $ do else B.spanWith attr $ B.link url "" name' -- | image embedding -image :: Parser [Char] ParserState Inlines +image :: PandocMonad m => ParserT [Char] ParserState m Inlines image = try $ do char '!' >> notFollowedBy space (ident, cls, kvs) <- attributes @@ -612,50 +618,50 @@ image = try $ do char '!' return $ B.imageWith attr src alt (B.str alt) -escapedInline :: Parser [Char] ParserState Inlines +escapedInline :: PandocMonad m => ParserT [Char] ParserState m Inlines escapedInline = escapedEqs <|> escapedTag -escapedEqs :: Parser [Char] ParserState Inlines +escapedEqs :: PandocMonad m => ParserT [Char] ParserState m Inlines escapedEqs = B.str <$> (try $ string "==" *> manyTill anyChar' (try $ string "==")) -- | literal text escaped btw tags -escapedTag :: Parser [Char] ParserState Inlines +escapedTag :: PandocMonad m => ParserT [Char] ParserState m Inlines escapedTag = B.str <$> (try $ string "" *> manyTill anyChar' (try $ string "")) -- | Any special symbol defined in wordBoundaries -symbol :: Parser [Char] ParserState Inlines +symbol :: PandocMonad m => ParserT [Char] ParserState m Inlines symbol = B.str . singleton <$> (notFollowedBy newline *> notFollowedBy rawHtmlBlock *> oneOf wordBoundaries) -- | Inline code -code :: Parser [Char] ParserState Inlines +code :: PandocMonad m => ParserT [Char] ParserState m Inlines code = code1 <|> code2 -- any character except a newline before a blank line -anyChar' :: Parser [Char] ParserState Char +anyChar' :: PandocMonad m => ParserT [Char] ParserState m Char anyChar' = satisfy (/='\n') <|> (try $ char '\n' <* notFollowedBy blankline) -code1 :: Parser [Char] ParserState Inlines +code1 :: PandocMonad m => ParserT [Char] ParserState m Inlines code1 = B.code <$> surrounded (char '@') anyChar' -code2 :: Parser [Char] ParserState Inlines +code2 :: PandocMonad m => ParserT [Char] ParserState m Inlines code2 = do htmlTag (tagOpen (=="tt") null) B.code <$> manyTill anyChar' (try $ htmlTag $ tagClose (=="tt")) -- | Html / CSS attributes -attributes :: Parser [Char] ParserState Attr +attributes :: PandocMonad m => ParserT [Char] ParserState m Attr attributes = (foldl (flip ($)) ("",[],[])) <$> try (do special <- option id specialAttribute attrs <- many attribute return (special : attrs)) -specialAttribute :: Parser [Char] ParserState (Attr -> Attr) +specialAttribute :: PandocMonad m => ParserT [Char] ParserState m (Attr -> Attr) specialAttribute = do alignStr <- ("center" <$ char '=') <|> ("justify" <$ try (string "<>")) <|> @@ -664,11 +670,11 @@ specialAttribute = do notFollowedBy spaceChar return $ addStyle ("text-align:" ++ alignStr) -attribute :: Parser [Char] ParserState (Attr -> Attr) +attribute :: PandocMonad m => ParserT [Char] ParserState m (Attr -> Attr) attribute = try $ (classIdAttr <|> styleAttr <|> langAttr) <* notFollowedBy spaceChar -classIdAttr :: Parser [Char] ParserState (Attr -> Attr) +classIdAttr :: PandocMonad m => ParserT [Char] ParserState m (Attr -> Attr) classIdAttr = try $ do -- (class class #id) char '(' ws <- words `fmap` manyTill anyChar' (char ')') @@ -679,7 +685,7 @@ classIdAttr = try $ do -- (class class #id) classes' -> return $ \(_,_,keyvals) -> ("",classes',keyvals) -styleAttr :: Parser [Char] ParserState (Attr -> Attr) +styleAttr :: PandocMonad m => ParserT [Char] ParserState m (Attr -> Attr) styleAttr = do style <- try $ enclosed (char '{') (char '}') anyChar' return $ addStyle style @@ -690,21 +696,23 @@ addStyle style (id',classes,keyvals) = where keyvals' = ("style", style') : [(k,v) | (k,v) <- keyvals, k /= "style"] style' = style ++ ";" ++ concat [v | ("style",v) <- keyvals] -langAttr :: Parser [Char] ParserState (Attr -> Attr) +langAttr :: PandocMonad m => ParserT [Char] ParserState m (Attr -> Attr) langAttr = do lang <- try $ enclosed (char '[') (char ']') alphaNum return $ \(id',classes,keyvals) -> (id',classes,("lang",lang):keyvals) -- | Parses material surrounded by a parser. -surrounded :: Parser [Char] st t -- ^ surrounding parser - -> Parser [Char] st a -- ^ content parser (to be used repeatedly) - -> Parser [Char] st [a] +surrounded :: PandocMonad m + => ParserT [Char] st m t -- ^ surrounding parser + -> ParserT [Char] st m a -- ^ content parser (to be used repeatedly) + -> ParserT [Char] st m [a] surrounded border = enclosed (border *> notFollowedBy (oneOf " \t\n\r")) (try border) -simpleInline :: Parser [Char] ParserState t -- ^ surrounding parser - -> (Inlines -> Inlines) -- ^ Inline constructor - -> Parser [Char] ParserState Inlines -- ^ content parser (to be used repeatedly) +simpleInline :: PandocMonad m + => ParserT [Char] ParserState m t -- ^ surrounding parser + -> (Inlines -> Inlines) -- ^ Inline constructor + -> ParserT [Char] ParserState m Inlines -- ^ content parser (to be used repeatedly) simpleInline border construct = try $ do notAfterString border *> notFollowedBy (oneOf " \t\n\r") @@ -718,7 +726,7 @@ simpleInline border construct = try $ do then body else B.spanWith attr body -groupedInlineMarkup :: Parser [Char] ParserState Inlines +groupedInlineMarkup :: PandocMonad m => ParserT [Char] ParserState m Inlines groupedInlineMarkup = try $ do char '[' sp1 <- option mempty $ B.space <$ whitespace diff --git a/src/Text/Pandoc/Readers/Txt2Tags.hs b/src/Text/Pandoc/Readers/Txt2Tags.hs index 0aafc83c7..00b37503e 100644 --- a/src/Text/Pandoc/Readers/Txt2Tags.hs +++ b/src/Text/Pandoc/Readers/Txt2Tags.hs @@ -46,13 +46,12 @@ import Data.Maybe (fromMaybe) import Control.Monad (void, guard, when) import Data.Default import Control.Monad.Reader (Reader, runReader, asks) -import Text.Pandoc.Error -import Data.Time.LocalTime (getZonedTime) -import System.Directory(getModificationTime) import Data.Time.Format (formatTime) import Text.Pandoc.Compat.Time (defaultTimeLocale) -import System.IO.Error (catchIOError) +import Control.Monad.Except (throwError, catchError) +import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) +import qualified Text.Pandoc.Class as P type T2T = ParserT String ParserState (Reader T2TMeta) @@ -69,26 +68,42 @@ instance Default T2TMeta where def = T2TMeta "" "" "" "" -- | Get the meta information required by Txt2Tags macros -getT2TMeta :: [FilePath] -> FilePath -> IO T2TMeta -getT2TMeta inps out = do - curDate <- formatTime defaultTimeLocale "%F" <$> getZonedTime +getT2TMeta :: PandocMonad m => m T2TMeta +getT2TMeta = do + mbInps <- P.getInputFiles + let inps = case mbInps of + Just x -> x + Nothing -> [] + mbOutp <- P.getOutputFile + let outp = case mbOutp of + Just x -> x + Nothing -> "" + curDate <- formatTime defaultTimeLocale "%F" <$> P.getZonedTime let getModTime = fmap (formatTime defaultTimeLocale "%T") . - getModificationTime + P.getModificationTime curMtime <- case inps of - [] -> formatTime defaultTimeLocale "%T" <$> getZonedTime - _ -> catchIOError + [] -> formatTime defaultTimeLocale "%T" <$> P.getZonedTime + _ -> catchError (maximum <$> mapM getModTime inps) (const (return "")) - return $ T2TMeta curDate curMtime (intercalate ", " inps) out + return $ T2TMeta curDate curMtime (intercalate ", " inps) outp -- | Read Txt2Tags from an input string returning a Pandoc document -readTxt2Tags :: T2TMeta -> ReaderOptions -> String -> Either PandocError Pandoc -readTxt2Tags t opts s = flip runReader t $ readWithM parseT2T (def {stateOptions = opts}) (s ++ "\n\n") +readTxt2Tags :: PandocMonad m + => ReaderOptions + -> String + -> m Pandoc +readTxt2Tags opts s = do + meta <- getT2TMeta + let parsed = flip runReader meta $ readWithM parseT2T (def {stateOptions = opts}) (s ++ "\n\n") + case parsed of + Right result -> return $ result + Left _ -> throwError $ PandocParseError "error parsing t2t" -- | Read Txt2Tags (ignoring all macros) from an input string returning -- a Pandoc document -readTxt2TagsNoMacros :: ReaderOptions -> String -> Either PandocError Pandoc -readTxt2TagsNoMacros = readTxt2Tags def +readTxt2TagsNoMacros :: PandocMonad m => ReaderOptions -> String -> m Pandoc +readTxt2TagsNoMacros = readTxt2Tags parseT2T :: T2T Pandoc parseT2T = do -- cgit v1.2.3 From fe0b71a2f1505e265202fd9e07458ff1e9554651 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Wed, 30 Nov 2016 09:21:21 -0500 Subject: Class: Add getModificationTime This is to enable macros in T2T, but can be used for other stuff in the future, I imagine. This requires building up the info in our fake file trees. Note the version in IO is safe. --- src/Text/Pandoc/Class.hs | 46 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 5cef621dc..e6435eae3 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -39,6 +39,7 @@ module Text.Pandoc.Class ( PandocMonad(..) , PandocIO(..) , PandocPure(..) , PandocExecutionError(..) + , FileInfo(..) , runIO , runIOorExplode , runPure @@ -73,6 +74,7 @@ import qualified System.Environment as IO (lookupEnv) import System.FilePath.Glob (match, compile) import System.FilePath (()) import qualified System.FilePath.Glob as IO (glob) +import qualified System.Directory as IO (getModificationTime) import Control.Monad.State hiding (fail) import Control.Monad.Reader hiding (fail) import Control.Monad.Except hiding (fail) @@ -80,6 +82,8 @@ import Data.Word (Word8) import Data.Typeable import Data.Default import System.IO.Error +import Data.Map (Map) +import qualified Data.Map as M class (Functor m, Applicative m, Monad m, MonadError PandocExecutionError m) => PandocMonad m where lookupEnv :: String -> m (Maybe String) @@ -105,6 +109,8 @@ class (Functor m, Applicative m, Monad m, MonadError PandocExecutionError m) => glob :: String -> m [FilePath] setMediaBag :: MediaBag -> m () insertMedia :: FilePath -> Maybe MimeType -> BL.ByteString -> m () + getModificationTime :: FilePath -> m UTCTime + --Some functions derived from Primitives: @@ -190,6 +196,11 @@ instance PandocMonad PandocIO where modify $ \st -> st{ioStMediaBag = mb} insertMedia fp mime bs = modify $ \st -> st{ioStMediaBag = MB.insertMedia fp mime bs (ioStMediaBag st) } + getModificationTime fp = do + eitherMtime <- liftIO (tryIOError $ IO.getModificationTime fp) + case eitherMtime of + Right mtime -> return mtime + Left _ -> throwError $ PandocFileReadError fp data PureState = PureState { stStdGen :: StdGen , stWord8Store :: [Word8] -- should be @@ -210,15 +221,26 @@ instance Default PureState where , stWarnings = [] , stUniqStore = [1..] , stMediaBag = mempty + + } +data FileInfo = FileInfo { infoFileMTime :: UTCTime + , infoFileContents :: B.ByteString + } + +newtype FileTree = FileTree {unFileTree :: M.Map FilePath FileInfo} + deriving (Monoid) + +getFileInfo :: FilePath -> FileTree -> Maybe FileInfo +getFileInfo fp tree = M.lookup fp $ unFileTree tree data PureEnv = PureEnv { envEnv :: [(String, String)] , envTime :: UTCTime , envReferenceDocx :: Archive , envReferenceODT :: Archive - , envFiles :: [(FilePath, B.ByteString)] - , envUserDataDir :: [(FilePath, B.ByteString)] - , envCabalDataDir :: [(FilePath, B.ByteString)] + , envFiles :: FileTree + , envUserDataDir :: FileTree + , envCabalDataDir :: FileTree , envFontFiles :: [FilePath] } @@ -229,9 +251,9 @@ instance Default PureEnv where , envTime = posixSecondsToUTCTime 0 , envReferenceDocx = emptyArchive , envReferenceODT = emptyArchive - , envFiles = [] - , envUserDataDir = [] - , envCabalDataDir = [] + , envFiles = mempty + , envUserDataDir = mempty + , envCabalDataDir = mempty , envFontFiles = [] } @@ -277,7 +299,7 @@ instance PandocMonad PandocPure where _ -> M.fail "uniq store ran out of elements" readFileLazy fp = do fps <- asks envFiles - case lookup fp fps of + case infoFileContents <$> getFileInfo fp fps of Just bs -> return (BL.fromStrict bs) Nothing -> throwError $ PandocFileReadError fp readDataFile Nothing "reference.docx" = do @@ -289,13 +311,13 @@ instance PandocMonad PandocPure where BL.toStrict <$> (readFileLazy fname') readDataFile (Just userDir) fname = do userDirFiles <- asks envUserDataDir - case lookup (userDir fname) userDirFiles of + case infoFileContents <$> (getFileInfo (userDir fname) userDirFiles) of Just bs -> return bs Nothing -> readDataFile Nothing fname fail = M.fail fetchItem _ fp = do fps <- asks envFiles - case lookup fp fps of + case infoFileContents <$> (getFileInfo fp fps) of Just bs -> return (Right (bs, getMimeType fp)) Nothing -> return (Left $ E.toException $ PandocFileReadError fp) @@ -317,3 +339,9 @@ instance PandocMonad PandocPure where insertMedia fp mime bs = modify $ \st -> st{stMediaBag = MB.insertMedia fp mime bs (stMediaBag st) } + + getModificationTime fp = do + fps <- asks envFiles + case infoFileMTime <$> (getFileInfo fp fps) of + Just tm -> return tm + Nothing -> throwError $ PandocFileReadError fp -- cgit v1.2.3 From 9d9f615593dc2cd986063058e9d8d91f99456242 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Wed, 30 Nov 2016 09:46:08 -0500 Subject: Add Zoned time to class. --- src/Text/Pandoc/Class.hs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index e6435eae3..cee93c4fc 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -35,6 +35,7 @@ module Text.Pandoc.Class ( PandocMonad(..) , PureState(..) , PureEnv(..) , getPOSIXTime + , getZonedTime , addWarningWithPos , PandocIO(..) , PandocPure(..) @@ -64,6 +65,8 @@ import qualified Text.Pandoc.Compat.Time as IO (getCurrentTime) import Data.Time.Clock.POSIX ( utcTimeToPOSIXSeconds , posixSecondsToUTCTime , POSIXTime ) +import Data.Time.LocalTime (TimeZone, ZonedTime, utcToZonedTime, utc) +import qualified Data.Time.LocalTime as IO (getCurrentTimeZone) import Text.Pandoc.MIME (MimeType, getMimeType) import Text.Pandoc.MediaBag (MediaBag) import qualified Text.Pandoc.MediaBag as MB @@ -82,12 +85,12 @@ import Data.Word (Word8) import Data.Typeable import Data.Default import System.IO.Error -import Data.Map (Map) import qualified Data.Map as M class (Functor m, Applicative m, Monad m, MonadError PandocExecutionError m) => PandocMonad m where lookupEnv :: String -> m (Maybe String) getCurrentTime :: m UTCTime + getCurrentTimeZone :: m TimeZone getDefaultReferenceDocx :: Maybe FilePath -> m Archive getDefaultReferenceODT :: Maybe FilePath -> m Archive newStdGen :: m StdGen @@ -117,6 +120,12 @@ class (Functor m, Applicative m, Monad m, MonadError PandocExecutionError m) => getPOSIXTime :: (PandocMonad m) => m POSIXTime getPOSIXTime = utcTimeToPOSIXSeconds <$> getCurrentTime +getZonedTime :: (PandocMonad m) => m ZonedTime +getZonedTime = do + t <- getCurrentTime + tz <- getCurrentTimeZone + return $ utcToZonedTime tz t + addWarningWithPos :: PandocMonad m => Maybe SourcePos -> String @@ -169,6 +178,7 @@ newtype PandocIO a = PandocIO { instance PandocMonad PandocIO where lookupEnv = liftIO . IO.lookupEnv getCurrentTime = liftIO IO.getCurrentTime + getCurrentTimeZone = liftIO IO.getCurrentTimeZone getDefaultReferenceDocx = liftIO . IO.getDefaultReferenceDocx getDefaultReferenceODT = liftIO . IO.getDefaultReferenceODT newStdGen = liftIO IO.newStdGen @@ -236,6 +246,7 @@ getFileInfo fp tree = M.lookup fp $ unFileTree tree data PureEnv = PureEnv { envEnv :: [(String, String)] , envTime :: UTCTime + , envTimeZone :: TimeZone , envReferenceDocx :: Archive , envReferenceODT :: Archive , envFiles :: FileTree @@ -249,6 +260,7 @@ data PureEnv = PureEnv { envEnv :: [(String, String)] instance Default PureEnv where def = PureEnv { envEnv = [("USER", "pandoc-user")] , envTime = posixSecondsToUTCTime 0 + , envTimeZone = utc , envReferenceDocx = emptyArchive , envReferenceODT = emptyArchive , envFiles = mempty @@ -280,6 +292,8 @@ instance PandocMonad PandocPure where getCurrentTime = asks envTime + getCurrentTimeZone = asks envTimeZone + getDefaultReferenceDocx _ = asks envReferenceDocx getDefaultReferenceODT _ = asks envReferenceODT -- cgit v1.2.3 From 6a9a38c92d77c3c9c2f8e6bf43ad602fb35c29b5 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Wed, 30 Nov 2016 12:55:30 -0500 Subject: Add input and output filepaths to PandocMonad. We'll want these in a number of places, but right now it will be necessary for the macros in T2T. --- src/Text/Pandoc/Class.hs | 60 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 12 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index cee93c4fc..0abd0361e 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -110,9 +110,15 @@ class (Functor m, Applicative m, Monad m, MonadError PandocExecutionError m) => getWarnings :: m [String] fail :: String -> m b glob :: String -> m [FilePath] + getModificationTime :: FilePath -> m UTCTime + -- The following are common to all instantiations of the monad, up + -- to the record names, so I'd like to work out a better way to deal + -- with it. setMediaBag :: MediaBag -> m () insertMedia :: FilePath -> Maybe MimeType -> BL.ByteString -> m () - getModificationTime :: FilePath -> m UTCTime + getInputFiles :: m (Maybe [FilePath]) + getOutputFile :: m (Maybe FilePath) + --Some functions derived from Primitives: @@ -152,8 +158,16 @@ instance Default PandocStateIO where , ioStMediaBag = mempty } +data PandocEnvIO = PandocEnvIO { ioEnvInputFiles :: Maybe [FilePath] + , ioEnvOutputFile :: Maybe FilePath + } +instance Default PandocEnvIO where + def = PandocEnvIO { ioEnvInputFiles = Nothing -- stdin + , ioEnvOutputFile = Nothing -- stdout + } + runIO :: PandocIO a -> IO (Either PandocExecutionError a) -runIO ma = flip evalStateT def $ runExceptT $ unPandocIO ma +runIO ma = flip evalStateT def $ flip runReaderT def $ runExceptT $ unPandocIO ma runIOorExplode :: PandocIO a -> IO a runIOorExplode ma = do @@ -166,11 +180,12 @@ runIOorExplode ma = do Left (PandocSomeError s) -> error s newtype PandocIO a = PandocIO { - unPandocIO :: ExceptT PandocExecutionError (StateT PandocStateIO IO) a + unPandocIO :: ExceptT PandocExecutionError (ReaderT PandocEnvIO (StateT PandocStateIO IO)) a } deriving ( MonadIO , Functor , Applicative , Monad + , MonadReader PandocEnvIO , MonadState PandocStateIO , MonadError PandocExecutionError ) @@ -202,15 +217,20 @@ instance PandocMonad PandocIO where liftIO $ IO.warn msg getWarnings = gets ioStWarnings glob = liftIO . IO.glob - setMediaBag mb = - modify $ \st -> st{ioStMediaBag = mb} - insertMedia fp mime bs = - modify $ \st -> st{ioStMediaBag = MB.insertMedia fp mime bs (ioStMediaBag st) } getModificationTime fp = do eitherMtime <- liftIO (tryIOError $ IO.getModificationTime fp) case eitherMtime of Right mtime -> return mtime Left _ -> throwError $ PandocFileReadError fp + -- Common functions + setMediaBag mb = + modify $ \st -> st{ioStMediaBag = mb} + insertMedia fp mime bs = + modify $ \st -> st{ioStMediaBag = MB.insertMedia fp mime bs (ioStMediaBag st) } + getInputFiles = asks ioEnvInputFiles + getOutputFile = asks ioEnvOutputFile + + data PureState = PureState { stStdGen :: StdGen , stWord8Store :: [Word8] -- should be @@ -253,6 +273,8 @@ data PureEnv = PureEnv { envEnv :: [(String, String)] , envUserDataDir :: FileTree , envCabalDataDir :: FileTree , envFontFiles :: [FilePath] + , envInputFiles :: Maybe [FilePath] + , envOutputFile :: Maybe FilePath } -- We have to figure this out a bit more. But let's put some empty @@ -267,6 +289,8 @@ instance Default PureEnv where , envUserDataDir = mempty , envCabalDataDir = mempty , envFontFiles = [] + , envInputFiles = Nothing + , envOutputFile = Nothing } instance E.Exception PandocExecutionError @@ -348,14 +372,26 @@ instance PandocMonad PandocPure where fontFiles <- asks envFontFiles return (filter (match (compile s)) fontFiles) + getModificationTime fp = do + fps <- asks envFiles + case infoFileMTime <$> (getFileInfo fp fps) of + Just tm -> return tm + Nothing -> throwError $ PandocFileReadError fp + + -- Common files + setMediaBag mb = modify $ \st -> st{stMediaBag = mb} insertMedia fp mime bs = modify $ \st -> st{stMediaBag = MB.insertMedia fp mime bs (stMediaBag st) } - getModificationTime fp = do - fps <- asks envFiles - case infoFileMTime <$> (getFileInfo fp fps) of - Just tm -> return tm - Nothing -> throwError $ PandocFileReadError fp + getInputFiles = asks envInputFiles + + getOutputFile = asks envOutputFile + + + + + + -- cgit v1.2.3 From 3f7b3f5fd06d46c7824f482219ffa4a461f4fef2 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Wed, 30 Nov 2016 13:26:32 -0500 Subject: Add Text2Tags to Text.Pandoc --- src/Text/Pandoc.hs | 6 ++---- src/Text/Pandoc/Readers/Txt2Tags.hs | 6 +++--- 2 files changed, 5 insertions(+), 7 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index 02217c376..a49d52e25 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -86,7 +86,6 @@ module Text.Pandoc , readJSON , readTWiki , readTxt2Tags - , readTxt2TagsNoMacros , readEPUB -- * Writers: converting /from/ Pandoc format , Writer(..) @@ -179,8 +178,7 @@ import Text.Pandoc.Writers.Custom import Text.Pandoc.Writers.TEI import Text.Pandoc.Templates import Text.Pandoc.Options -import Text.Pandoc.Shared (safeRead, warn, mapLeft, pandocVersion) -import Text.Pandoc.MediaBag (MediaBag) +import Text.Pandoc.Shared (safeRead, mapLeft, pandocVersion) import Text.Pandoc.Error import Text.Pandoc.Class (PandocMonad, runIOorExplode, PandocExecutionError(..)) import Data.Aeson @@ -266,7 +264,7 @@ readers = [ ("native" , StringReader $ \_ s -> readNative s) ,("twiki" , StringReader readTWiki) ,("docx" , ByteStringReader readDocx) ,("odt" , ByteStringReader readOdt) - -- ,("t2t" , mkStringReader readTxt2TagsNoMacros) + ,("t2t" , StringReader readTxt2Tags) ,("epub" , ByteStringReader readEPUB) ] diff --git a/src/Text/Pandoc/Readers/Txt2Tags.hs b/src/Text/Pandoc/Readers/Txt2Tags.hs index 00b37503e..29457ee6a 100644 --- a/src/Text/Pandoc/Readers/Txt2Tags.hs +++ b/src/Text/Pandoc/Readers/Txt2Tags.hs @@ -29,7 +29,7 @@ Conversion of txt2tags formatted plain text to 'Pandoc' document. module Text.Pandoc.Readers.Txt2Tags ( readTxt2Tags , getT2TMeta , T2TMeta (..) - , readTxt2TagsNoMacros) + ) where import qualified Text.Pandoc.Builder as B @@ -102,8 +102,8 @@ readTxt2Tags opts s = do -- | Read Txt2Tags (ignoring all macros) from an input string returning -- a Pandoc document -readTxt2TagsNoMacros :: PandocMonad m => ReaderOptions -> String -> m Pandoc -readTxt2TagsNoMacros = readTxt2Tags +-- readTxt2TagsNoMacros :: PandocMonad m => ReaderOptions -> String -> m Pandoc +-- readTxt2TagsNoMacros = readTxt2Tags parseT2T :: T2T Pandoc parseT2T = do -- cgit v1.2.3 From 52859b98632e991e94a3d37c0e0ae6c5d3b3fa34 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Thu, 1 Dec 2016 10:00:21 -0500 Subject: Finish converting readers over. --- pandoc.hs | 17 +++++++++-------- src/Text/Pandoc/Class.hs | 12 ++++++++++++ 2 files changed, 21 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/pandoc.hs b/pandoc.hs index 646ec3a0e..ae6c654aa 100644 --- a/pandoc.hs +++ b/pandoc.hs @@ -70,7 +70,6 @@ import Data.Yaml (decode) import qualified Data.Yaml as Yaml import qualified Data.Text as T import Control.Applicative ((<|>)) -import Text.Pandoc.Readers.Txt2Tags (getT2TMeta) import Paths_pandoc (getDataDir) import Text.Printf (printf) #ifndef _WINDOWS @@ -78,8 +77,7 @@ import System.Posix.Terminal (queryTerminal) import System.Posix.IO (stdOutput) #endif import Control.Monad.Trans -import Text.Pandoc.Class (runIOorExplode, PandocMonad, PandocIO) -import qualified Text.Pandoc.Class as P +import Text.Pandoc.Class (runIOorExplode, withMediaBag, PandocIO) type Transform = Pandoc -> Pandoc @@ -916,7 +914,7 @@ options = map ("--" ++) longs let allopts = unwords (concatMap optnames options) UTF8.hPutStrLn stdout $ printf tpl allopts - (unwords (map fst readers)) + (unwords (map fst (readers :: [(String, Reader PandocIO)]))) (unwords (map fst (writers :: [(String, Writer PandocIO)]))) (unwords $ map fst highlightingStyles) ddir @@ -926,7 +924,7 @@ options = , Option "" ["list-input-formats"] (NoArg (\_ -> do - let readers'names = sort (map fst readers) + let readers'names = sort (map fst (readers :: [(String, Reader PandocIO)])) mapM_ (UTF8.hPutStrLn stdout) readers'names exitSuccess )) "" @@ -1410,13 +1408,16 @@ convertWithOpts opts args = do else return . Right let sourceToDoc :: [FilePath] -> IO (Pandoc, MediaBag) - sourceToDoc sources' = fmap handleError $ + sourceToDoc sources' = case reader of StringReader r-> do srcs <- convertTabs . intercalate "\n" <$> readSources sources' doc <- handleIncludes' srcs - either (return . Left) (\s -> fmap (,mempty) <$> r readerOpts s) doc - ByteStringReader r -> readFiles sources' >>= r readerOpts + case doc of + Right doc' -> runIOorExplode $ withMediaBag $ r readerOpts doc' + Left e -> error $ show e + ByteStringReader r -> readFiles sources' >>= + (\bs -> runIOorExplode $ withMediaBag $ r readerOpts bs) -- We parse first if (1) fileScope is set, (2), it's a binary -- reader, or (3) we're reading JSON. This is easier to do of an AND diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 0abd0361e..12566a51c 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -44,6 +44,7 @@ module Text.Pandoc.Class ( PandocMonad(..) , runIO , runIOorExplode , runPure + , withMediaBag ) where import Prelude hiding (readFile, fail) @@ -115,6 +116,7 @@ class (Functor m, Applicative m, Monad m, MonadError PandocExecutionError m) => -- to the record names, so I'd like to work out a better way to deal -- with it. setMediaBag :: MediaBag -> m () + getMediaBag :: m MediaBag insertMedia :: FilePath -> Maybe MimeType -> BL.ByteString -> m () getInputFiles :: m (Maybe [FilePath]) getOutputFile :: m (Maybe FilePath) @@ -169,6 +171,9 @@ instance Default PandocEnvIO where runIO :: PandocIO a -> IO (Either PandocExecutionError a) runIO ma = flip evalStateT def $ flip runReaderT def $ runExceptT $ unPandocIO ma +withMediaBag :: PandocMonad m => m a -> m (a, MediaBag) +withMediaBag ma = ((,)) <$> ma <*> getMediaBag + runIOorExplode :: PandocIO a -> IO a runIOorExplode ma = do eitherVal <- runIO ma @@ -179,6 +184,10 @@ runIOorExplode ma = do Left (PandocParseError s) -> error $ "parse error" ++ s Left (PandocSomeError s) -> error s + + + + newtype PandocIO a = PandocIO { unPandocIO :: ExceptT PandocExecutionError (ReaderT PandocEnvIO (StateT PandocStateIO IO)) a } deriving ( MonadIO @@ -225,6 +234,7 @@ instance PandocMonad PandocIO where -- Common functions setMediaBag mb = modify $ \st -> st{ioStMediaBag = mb} + getMediaBag = gets ioStMediaBag insertMedia fp mime bs = modify $ \st -> st{ioStMediaBag = MB.insertMedia fp mime bs (ioStMediaBag st) } getInputFiles = asks ioEnvInputFiles @@ -383,6 +393,8 @@ instance PandocMonad PandocPure where setMediaBag mb = modify $ \st -> st{stMediaBag = mb} + getMediaBag = gets stMediaBag + insertMedia fp mime bs = modify $ \st -> st{stMediaBag = MB.insertMedia fp mime bs (stMediaBag st) } -- cgit v1.2.3 From 3574b98f81c2c24f7ef31f8251ba88792a2c06f0 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Thu, 1 Dec 2016 12:13:51 -0500 Subject: Unify Errors. --- src/Text/Pandoc.hs | 4 ++-- src/Text/Pandoc/Class.hs | 41 ++++++++++++++---------------------- src/Text/Pandoc/Error.hs | 26 ++++++++++++++++------- src/Text/Pandoc/Parsing.hs | 2 +- src/Text/Pandoc/Readers/Docx.hs | 3 ++- src/Text/Pandoc/Readers/EPUB.hs | 3 ++- src/Text/Pandoc/Readers/HTML.hs | 3 ++- src/Text/Pandoc/Readers/Haddock.hs | 5 +++-- src/Text/Pandoc/Readers/LaTeX.hs | 4 ++-- src/Text/Pandoc/Readers/Markdown.hs | 3 ++- src/Text/Pandoc/Readers/MediaWiki.hs | 3 ++- src/Text/Pandoc/Readers/Native.hs | 2 +- src/Text/Pandoc/Readers/OPML.hs | 4 ++-- src/Text/Pandoc/Readers/Odt.hs | 6 +++--- src/Text/Pandoc/Readers/Org.hs | 3 ++- src/Text/Pandoc/Readers/RST.hs | 3 ++- src/Text/Pandoc/Readers/TWiki.hs | 2 +- src/Text/Pandoc/Readers/Textile.hs | 3 ++- src/Text/Pandoc/Readers/Txt2Tags.hs | 3 ++- src/Text/Pandoc/Writers/EPUB.hs | 3 ++- src/Text/Pandoc/Writers/FB2.hs | 3 ++- src/Text/Pandoc/Writers/HTML.hs | 3 ++- src/Text/Pandoc/Writers/Man.hs | 3 ++- src/Text/Pandoc/Writers/Markdown.hs | 3 ++- src/Text/Pandoc/Writers/OPML.hs | 3 ++- src/Text/Pandoc/Writers/RTF.hs | 3 ++- src/Text/Pandoc/Writers/Texinfo.hs | 3 ++- 27 files changed, 83 insertions(+), 64 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index a49d52e25..036d3cdf5 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -180,7 +180,7 @@ import Text.Pandoc.Templates import Text.Pandoc.Options import Text.Pandoc.Shared (safeRead, mapLeft, pandocVersion) import Text.Pandoc.Error -import Text.Pandoc.Class (PandocMonad, runIOorExplode, PandocExecutionError(..)) +import Text.Pandoc.Class (PandocMonad, runIOorExplode) import Data.Aeson import qualified Data.ByteString.Lazy as BL import Data.List (intercalate) @@ -387,7 +387,7 @@ class ToJSONFilter a => ToJsonFilter a toJsonFilter = toJSONFilter readJSON :: ReaderOptions -> String -> Either PandocError Pandoc -readJSON _ = mapLeft ParseFailure . eitherDecode' . UTF8.fromStringLazy +readJSON _ = mapLeft PandocParseError . eitherDecode' . UTF8.fromStringLazy writeJSON :: WriterOptions -> Pandoc -> String writeJSON _ = UTF8.toStringLazy . encode diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 12566a51c..b3bbc04bc 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -39,7 +39,6 @@ module Text.Pandoc.Class ( PandocMonad(..) , addWarningWithPos , PandocIO(..) , PandocPure(..) - , PandocExecutionError(..) , FileInfo(..) , runIO , runIOorExplode @@ -83,12 +82,12 @@ import Control.Monad.State hiding (fail) import Control.Monad.Reader hiding (fail) import Control.Monad.Except hiding (fail) import Data.Word (Word8) -import Data.Typeable import Data.Default import System.IO.Error import qualified Data.Map as M +import Text.Pandoc.Error -class (Functor m, Applicative m, Monad m, MonadError PandocExecutionError m) => PandocMonad m where +class (Functor m, Applicative m, Monad m, MonadError PandocError m) => PandocMonad m where lookupEnv :: String -> m (Maybe String) getCurrentTime :: m UTCTime getCurrentTimeZone :: m TimeZone @@ -143,12 +142,6 @@ addWarningWithPos mbpos msg = warn $ msg ++ maybe "" (\pos -> " " ++ show pos) mbpos --- We can add to this as we go -data PandocExecutionError = PandocFileReadError FilePath - | PandocShouldNeverHappenError String - | PandocParseError String - | PandocSomeError String - deriving (Show, Typeable) -- Nothing in this for now, but let's put it there anyway. data PandocStateIO = PandocStateIO { ioStWarnings :: [String] @@ -168,35 +161,35 @@ instance Default PandocEnvIO where , ioEnvOutputFile = Nothing -- stdout } -runIO :: PandocIO a -> IO (Either PandocExecutionError a) +runIO :: PandocIO a -> IO (Either PandocError a) runIO ma = flip evalStateT def $ flip runReaderT def $ runExceptT $ unPandocIO ma withMediaBag :: PandocMonad m => m a -> m (a, MediaBag) withMediaBag ma = ((,)) <$> ma <*> getMediaBag runIOorExplode :: PandocIO a -> IO a -runIOorExplode ma = do - eitherVal <- runIO ma - case eitherVal of - Right x -> return x - Left (PandocFileReadError fp) -> error $ "problem reading " ++ fp - Left (PandocShouldNeverHappenError s) -> error s - Left (PandocParseError s) -> error $ "parse error" ++ s - Left (PandocSomeError s) -> error s +runIOorExplode ma = handleError <$> runIO ma + -- eitherVal <- runIO ma + -- case eitherVal of + -- Right x -> return x + -- Left (PandocFileReadError fp) -> error $ "problem reading " ++ fp + -- Left (PandocShouldNeverHappenError s) -> error s + -- Left (PandocParseError s) -> error $ "parse error" ++ s + -- Left (PandocSomeError s) -> error s newtype PandocIO a = PandocIO { - unPandocIO :: ExceptT PandocExecutionError (ReaderT PandocEnvIO (StateT PandocStateIO IO)) a + unPandocIO :: ExceptT PandocError (ReaderT PandocEnvIO (StateT PandocStateIO IO)) a } deriving ( MonadIO , Functor , Applicative , Monad , MonadReader PandocEnvIO , MonadState PandocStateIO - , MonadError PandocExecutionError + , MonadError PandocError ) instance PandocMonad PandocIO where @@ -303,20 +296,18 @@ instance Default PureEnv where , envOutputFile = Nothing } -instance E.Exception PandocExecutionError - newtype PandocPure a = PandocPure { - unPandocPure :: ExceptT PandocExecutionError + unPandocPure :: ExceptT PandocError (ReaderT PureEnv (State PureState)) a } deriving ( Functor , Applicative , Monad , MonadReader PureEnv , MonadState PureState - , MonadError PandocExecutionError + , MonadError PandocError ) -runPure :: PandocPure a -> Either PandocExecutionError a +runPure :: PandocPure a -> Either PandocError a runPure x = flip evalState def $ flip runReaderT def $ runExceptT $ unPandocPure x instance PandocMonad PandocPure where diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs index 5e26771fe..c001b279a 100644 --- a/src/Text/Pandoc/Error.hs +++ b/src/Text/Pandoc/Error.hs @@ -33,17 +33,24 @@ module Text.Pandoc.Error (PandocError(..), handleError) where import Text.Parsec.Error import Text.Parsec.Pos hiding (Line) -import GHC.Generics (Generic) import Data.Generics (Typeable) import Control.Exception (Exception) type Input = String -data PandocError = -- | Generic parse failure - ParseFailure String - -- | Error thrown by a Parsec parser - | ParsecError Input ParseError - deriving (Show, Typeable, Generic) +data PandocError = PandocFileReadError FilePath + | PandocShouldNeverHappenError String + | PandocSomeError String + | PandocParseError String + | PandocParsecError Input ParseError + deriving (Show, Typeable) + + +-- data PandocError = -- | Generic parse failure +-- ParseFailure String +-- -- | Error thrown by a Parsec parser +-- | ParsecError Input ParseError +-- deriving (Show, Typeable, Generic) instance Exception PandocError @@ -52,8 +59,11 @@ handleError :: Either PandocError a -> a handleError (Right r) = r handleError (Left err) = case err of - ParseFailure string -> error string - ParsecError input err' -> + PandocFileReadError fp -> error $ "problem reading " ++ fp + PandocShouldNeverHappenError s -> error s + PandocSomeError s -> error s + PandocParseError s -> error s + PandocParsecError input err' -> let errPos = errorPos err' errLine = sourceLine errPos errColumn = sourceColumn errPos diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 90cc20ab6..796d09632 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -885,7 +885,7 @@ readWithM :: (Monad m) -> String -- ^ input -> m (Either PandocError a) readWithM parser state input = - mapLeft (ParsecError input) `liftM` runParserT parser state "source" input + mapLeft (PandocParsecError input) `liftM` runParserT parser state "source" input -- | Parse a string with a given parser and state diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index 87b64d544..37fe5c532 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -96,8 +96,9 @@ import qualified Data.Sequence as Seq (null) #if !(MIN_VERSION_base(4,8,0)) import Data.Traversable (traverse) #endif +import Text.Pandoc.Error import Control.Monad.Except (throwError) -import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) +import Text.Pandoc.Class (PandocMonad) import qualified Text.Pandoc.Class as P readDocx :: PandocMonad m diff --git a/src/Text/Pandoc/Readers/EPUB.hs b/src/Text/Pandoc/Readers/EPUB.hs index 0dbe87052..a76ed04ba 100644 --- a/src/Text/Pandoc/Readers/EPUB.hs +++ b/src/Text/Pandoc/Readers/EPUB.hs @@ -32,7 +32,8 @@ import Data.Maybe (mapMaybe, fromMaybe) import qualified Data.Map as M (Map, lookup, fromList, elems) import Data.Monoid ((<>)) import Control.DeepSeq (deepseq, NFData) -import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) +import Text.Pandoc.Error +import Text.Pandoc.Class (PandocMonad) import qualified Text.Pandoc.Class as P import Debug.Trace (trace) diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index ef28ff739..b66a712e0 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -68,7 +68,8 @@ import Text.Pandoc.CSS (foldOrElse, pickStyleAttrProps) import Data.Monoid ((<>)) import Text.Parsec.Error import qualified Data.Set as Set -import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) +import Text.Pandoc.Error +import Text.Pandoc.Class (PandocMonad) import Control.Monad.Except (throwError) diff --git a/src/Text/Pandoc/Readers/Haddock.hs b/src/Text/Pandoc/Readers/Haddock.hs index 4d33f657c..987342bf7 100644 --- a/src/Text/Pandoc/Readers/Haddock.hs +++ b/src/Text/Pandoc/Readers/Haddock.hs @@ -25,8 +25,9 @@ import Text.Pandoc.Options import Documentation.Haddock.Parser import Documentation.Haddock.Types import Debug.Trace (trace) +import Text.Pandoc.Error import Control.Monad.Except (throwError) -import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) +import Text.Pandoc.Class (PandocMonad) -- | Parse Haddock markup and return a 'Pandoc' document. @@ -40,7 +41,7 @@ readHaddock opts s = case readHaddockEither opts s of readHaddockEither :: ReaderOptions -- ^ Reader options -> String -- ^ String to parse - -> Either PandocExecutionError Pandoc + -> Either PandocError Pandoc readHaddockEither opts = #if MIN_VERSION_haddock_library(1,2,0) Right . B.doc . docHToBlocks . trace' . _doc . parseParas diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 2506c17be..882777c0e 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -57,7 +57,7 @@ import Text.Pandoc.Highlighting (fromListingsLanguage) import Text.Pandoc.ImageSize (numUnit, showFl) import Text.Pandoc.Error import Control.Monad.Except (throwError) -import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..), PandocPure) +import Text.Pandoc.Class (PandocMonad, PandocPure) -- | Parse LaTeX from string and return 'Pandoc' document. readLaTeX :: PandocMonad m @@ -939,7 +939,7 @@ type IncludeParser = ParserT String [String] IO String -- | Replace "include" commands with file contents. handleIncludes :: String -> IO (Either PandocError String) -handleIncludes s = mapLeft (ParsecError s) <$> runParserT includeParser' [] "input" s +handleIncludes s = mapLeft (PandocParsecError s) <$> runParserT includeParser' [] "input" s includeParser' :: IncludeParser includeParser' = diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index e5df065ff..0acfca980 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -68,7 +68,8 @@ import Debug.Trace (trace) import Data.Monoid ((<>)) import Control.Monad.Trans (lift) import Control.Monad.Except (throwError, catchError) -import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) +import Text.Pandoc.Error +import Text.Pandoc.Class (PandocMonad) import qualified Text.Pandoc.Class as P type MarkdownParser m = ParserT [Char] ParserState m diff --git a/src/Text/Pandoc/Readers/MediaWiki.hs b/src/Text/Pandoc/Readers/MediaWiki.hs index 7f45cdb2a..e22e88bcb 100644 --- a/src/Text/Pandoc/Readers/MediaWiki.hs +++ b/src/Text/Pandoc/Readers/MediaWiki.hs @@ -58,7 +58,8 @@ import Data.Maybe (fromMaybe) import Text.Printf (printf) import Debug.Trace (trace) import Control.Monad.Except (throwError) -import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) +import Text.Pandoc.Error(PandocError(..)) +import Text.Pandoc.Class (PandocMonad) -- | Read mediawiki from an input string and return a Pandoc document. readMediaWiki :: PandocMonad m diff --git a/src/Text/Pandoc/Readers/Native.hs b/src/Text/Pandoc/Readers/Native.hs index 489ddcd4a..3e934e43f 100644 --- a/src/Text/Pandoc/Readers/Native.hs +++ b/src/Text/Pandoc/Readers/Native.hs @@ -65,5 +65,5 @@ readInlines :: String -> Either PandocError [Inline] readInlines s = maybe ((:[]) <$> readInline s) Right (safeRead s) readInline :: String -> Either PandocError Inline -readInline s = maybe (Left . ParseFailure $ "Could not read: " ++ s) Right (safeRead s) +readInline s = maybe (Left . PandocParseError $ "Could not read: " ++ s) Right (safeRead s) diff --git a/src/Text/Pandoc/Readers/OPML.hs b/src/Text/Pandoc/Readers/OPML.hs index 608e9ae0f..627566609 100644 --- a/src/Text/Pandoc/Readers/OPML.hs +++ b/src/Text/Pandoc/Readers/OPML.hs @@ -13,7 +13,7 @@ import Control.Monad.State import Data.Default import Control.Monad.Except import Text.Pandoc.Error -import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) +import Text.Pandoc.Class (PandocMonad) type OPML m = StateT OPMLState m @@ -65,7 +65,7 @@ attrValue attr elt = Just z -> z Nothing -> "" --- exceptT :: PandocMonad m => Either PandocExecutionError a -> OPML m a +-- exceptT :: PandocMonad m => Either PandocError a -> OPML m a -- exceptT = either throwError return asHtml :: PandocMonad m => String -> OPML m Inlines diff --git a/src/Text/Pandoc/Readers/Odt.hs b/src/Text/Pandoc/Readers/Odt.hs index 898dda077..9c8e76081 100644 --- a/src/Text/Pandoc/Readers/Odt.hs +++ b/src/Text/Pandoc/Readers/Odt.hs @@ -41,7 +41,7 @@ import System.FilePath import Control.Monad.Except (throwError) -import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) +import Text.Pandoc.Class (PandocMonad) import qualified Text.Pandoc.Class as P import Text.Pandoc.Definition import Text.Pandoc.Error @@ -78,7 +78,7 @@ readOdt' _ bytes = bytesToOdt bytes-- of bytesToOdt :: B.ByteString -> Either PandocError (Pandoc, MediaBag) bytesToOdt bytes = case toArchiveOrFail bytes of Right archive -> archiveToOdt archive - Left _ -> Left $ ParseFailure "Couldn't parse odt file." + Left _ -> Left $ PandocParseError "Couldn't parse odt file." -- archiveToOdt :: Archive -> Either PandocError (Pandoc, MediaBag) @@ -99,7 +99,7 @@ archiveToOdt archive | otherwise -- Not very detailed, but I don't think more information would be helpful - = Left $ ParseFailure "Couldn't parse odt file." + = Left $ PandocParseError "Couldn't parse odt file." where filePathIsOdtMedia :: FilePath -> Bool filePathIsOdtMedia fp = diff --git a/src/Text/Pandoc/Readers/Org.hs b/src/Text/Pandoc/Readers/Org.hs index 3a41ed317..c8dbbf45a 100644 --- a/src/Text/Pandoc/Readers/Org.hs +++ b/src/Text/Pandoc/Readers/Org.hs @@ -31,8 +31,9 @@ import Text.Pandoc.Readers.Org.Blocks ( blockList, meta ) import Text.Pandoc.Readers.Org.Parsing ( OrgParser, readWithM ) import Text.Pandoc.Readers.Org.ParserState ( optionsToParserState ) -import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) +import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Definition +import Text.Pandoc.Error import Text.Pandoc.Options import Control.Monad.Except ( throwError ) diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 4232f1c90..a20e29e93 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -51,7 +51,8 @@ import Data.Char (toLower, isHexDigit, isSpace) import Data.Monoid ((<>)) import Control.Monad.Except (throwError) import Control.Monad.Trans (lift) -import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) +import Text.Pandoc.Error +import Text.Pandoc.Class (PandocMonad) import qualified Text.Pandoc.Class as P -- | Parse reStructuredText string and return Pandoc document. diff --git a/src/Text/Pandoc/Readers/TWiki.hs b/src/Text/Pandoc/Readers/TWiki.hs index 091dcd7b1..40ea8b75a 100644 --- a/src/Text/Pandoc/Readers/TWiki.hs +++ b/src/Text/Pandoc/Readers/TWiki.hs @@ -48,7 +48,7 @@ import qualified Data.Foldable as F import Text.Pandoc.Error import Control.Monad.Except (throwError) -import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) +import Text.Pandoc.Class (PandocMonad) import qualified Text.Pandoc.Class as P -- | Read twiki from an input string and return a Pandoc document. diff --git a/src/Text/Pandoc/Readers/Textile.hs b/src/Text/Pandoc/Readers/Textile.hs index 5494695f5..4b558b42e 100644 --- a/src/Text/Pandoc/Readers/Textile.hs +++ b/src/Text/Pandoc/Readers/Textile.hs @@ -68,7 +68,8 @@ import Control.Monad ( guard, liftM, when ) import Data.Monoid ((<>)) import Text.Printf import Debug.Trace (trace) -import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) +import Text.Pandoc.Error +import Text.Pandoc.Class (PandocMonad) import Control.Monad.Except (throwError) -- | Parse a Textile text and return a Pandoc document. diff --git a/src/Text/Pandoc/Readers/Txt2Tags.hs b/src/Text/Pandoc/Readers/Txt2Tags.hs index 29457ee6a..2769ecb42 100644 --- a/src/Text/Pandoc/Readers/Txt2Tags.hs +++ b/src/Text/Pandoc/Readers/Txt2Tags.hs @@ -50,7 +50,8 @@ import Control.Monad.Reader (Reader, runReader, asks) import Data.Time.Format (formatTime) import Text.Pandoc.Compat.Time (defaultTimeLocale) import Control.Monad.Except (throwError, catchError) -import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) +import Text.Pandoc.Error +import Text.Pandoc.Class (PandocMonad) import qualified Text.Pandoc.Class as P type T2T = ParserT String ParserState (Reader T2TMeta) diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index f0dce739e..e41aa96ad 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -65,7 +65,8 @@ import Text.Pandoc.MIME (MimeType, getMimeType, extensionFromMimeType) import Text.Blaze.Html.Renderer.Utf8 (renderHtml) import Text.HTML.TagSoup (Tag(TagOpen), fromAttrib, parseTags) import Control.Monad.Except (throwError) -import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) +import Text.Pandoc.Error +import Text.Pandoc.Class (PandocMonad) import qualified Text.Pandoc.Class as P -- A Chapter includes a list of blocks and maybe a section diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index 5c22c8586..2401d7eee 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -46,7 +46,8 @@ import Text.Pandoc.Definition import Text.Pandoc.Options (WriterOptions(..), HTMLMathMethod(..), def) import Text.Pandoc.Shared (orderedListMarkers, isHeaderBlock, capitalize, linesToPara) -import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) +import Text.Pandoc.Error +import Text.Pandoc.Class (PandocMonad) import qualified Text.Pandoc.Class as P -- | Data to be written at the end of the document: diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 4520708e4..4c8ccfe4a 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -69,7 +69,8 @@ import qualified Text.XML.Light as XML import System.FilePath (takeExtension) import Data.Aeson (Value) import Control.Monad.Except (throwError) -import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) +import Text.Pandoc.Error +import Text.Pandoc.Class (PandocMonad) data WriterState = WriterState { stNotes :: [Html] -- ^ List of notes diff --git a/src/Text/Pandoc/Writers/Man.hs b/src/Text/Pandoc/Writers/Man.hs index c9530e4e1..27cf22b41 100644 --- a/src/Text/Pandoc/Writers/Man.hs +++ b/src/Text/Pandoc/Writers/Man.hs @@ -41,8 +41,9 @@ import Data.Maybe (fromMaybe) import Text.Pandoc.Pretty import Text.Pandoc.Builder (deleteMeta) import Control.Monad.State +import Text.Pandoc.Error import Control.Monad.Except (throwError) -import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) +import Text.Pandoc.Class (PandocMonad) type Notes = [[Block]] data WriterState = WriterState { stNotes :: Notes diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 4c33de65d..092693ea4 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -58,7 +58,8 @@ 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, PandocExecutionError(..)) +import Text.Pandoc.Error +import Text.Pandoc.Class (PandocMonad) type Notes = [[Block]] type Ref = ([Inline], Target, Attr) diff --git a/src/Text/Pandoc/Writers/OPML.hs b/src/Text/Pandoc/Writers/OPML.hs index dee3a029c..38c96589a 100644 --- a/src/Text/Pandoc/Writers/OPML.hs +++ b/src/Text/Pandoc/Writers/OPML.hs @@ -40,8 +40,9 @@ 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.Error import Control.Monad.Except (throwError) -import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) +import Text.Pandoc.Class (PandocMonad) -- | Convert Pandoc document to string in OPML format. writeOPML :: PandocMonad m => WriterOptions -> Pandoc -> m String diff --git a/src/Text/Pandoc/Writers/RTF.hs b/src/Text/Pandoc/Writers/RTF.hs index 1ac906756..f5d56d021 100644 --- a/src/Text/Pandoc/Writers/RTF.hs +++ b/src/Text/Pandoc/Writers/RTF.hs @@ -44,7 +44,8 @@ import qualified Data.Map as M import Text.Printf ( printf ) import Text.Pandoc.ImageSize import Control.Monad.Except (throwError) -import Text.Pandoc.Class (PandocMonad, PandocExecutionError(..)) +import Text.Pandoc.Error +import Text.Pandoc.Class (PandocMonad) import qualified Text.Pandoc.Class as P -- | Convert Image inlines into a raw RTF embedded image, read from a file, diff --git a/src/Text/Pandoc/Writers/Texinfo.hs b/src/Text/Pandoc/Writers/Texinfo.hs index 44a1fffd8..783a01063 100644 --- a/src/Text/Pandoc/Writers/Texinfo.hs +++ b/src/Text/Pandoc/Writers/Texinfo.hs @@ -45,7 +45,8 @@ import Network.URI ( isURI, unEscapeString ) import System.FilePath import qualified Data.Set as Set import Control.Monad.Except (throwError) -import Text.Pandoc.Class ( PandocMonad, PandocExecutionError(..) ) +import Text.Pandoc.Error +import Text.Pandoc.Class ( PandocMonad) data WriterState = WriterState { stStrikeout :: Bool -- document contains strikeout -- cgit v1.2.3 From 4fe499d3f29c0ed6ffe23299ca581a11563f7c9d Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Thu, 1 Dec 2016 15:21:49 -0500 Subject: Have a common state for all PandocMonad instances. --- src/Text/Pandoc/Class.hs | 144 +++++++++++++++++++++-------------------------- 1 file changed, 63 insertions(+), 81 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index b3bbc04bc..a888861b8 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -36,6 +36,13 @@ module Text.Pandoc.Class ( PandocMonad(..) , PureEnv(..) , getPOSIXTime , getZonedTime + , warn + , getWarnings + , getMediaBag + , setMediaBag + , insertMedia + , getInputFiles + , getOutputFile , addWarningWithPos , PandocIO(..) , PandocPure(..) @@ -57,7 +64,6 @@ import qualified Text.Pandoc.Shared as IO ( fetchItem , fetchItem' , getDefaultReferenceDocx , getDefaultReferenceODT - , warn , readDataFile) import Text.Pandoc.Compat.Time (UTCTime) import Text.Pandoc.Parsing (ParserT, ParserState, SourcePos) @@ -87,7 +93,7 @@ import System.IO.Error import qualified Data.Map as M import Text.Pandoc.Error -class (Functor m, Applicative m, Monad m, MonadError PandocError m) => PandocMonad m where +class (Functor m, Applicative m, Monad m, MonadError PandocError m, MonadState CommonState m) => PandocMonad m where lookupEnv :: String -> m (Maybe String) getCurrentTime :: m UTCTime getCurrentTimeZone :: m TimeZone @@ -106,24 +112,44 @@ class (Functor m, Applicative m, Monad m, MonadError PandocError m) => PandocMon -> Maybe String -> String -> m (Either E.SomeException (B.ByteString, Maybe MimeType)) - warn :: String -> m () - getWarnings :: m [String] fail :: String -> m b glob :: String -> m [FilePath] getModificationTime :: FilePath -> m UTCTime -- The following are common to all instantiations of the monad, up -- to the record names, so I'd like to work out a better way to deal -- with it. - setMediaBag :: MediaBag -> m () - getMediaBag :: m MediaBag - insertMedia :: FilePath -> Maybe MimeType -> BL.ByteString -> m () - getInputFiles :: m (Maybe [FilePath]) - getOutputFile :: m (Maybe FilePath) --Some functions derived from Primitives: +warn :: PandocMonad m => String -> m () +warn msg = modify $ \st -> st{stWarnings = msg : stWarnings st} + +getWarnings :: PandocMonad m => m [String] +getWarnings = gets stWarnings + +setMediaBag :: PandocMonad m => MediaBag -> m () +setMediaBag mb = modify $ \st -> st{stMediaBag = mb} + +getMediaBag :: PandocMonad m => m MediaBag +getMediaBag = gets stMediaBag + +insertMedia :: PandocMonad m => FilePath -> Maybe MimeType -> BL.ByteString -> m () +insertMedia fp mime bs = + modify $ \st -> st{stMediaBag = MB.insertMedia fp mime bs (stMediaBag st) } + +getInputFiles :: PandocMonad m => m (Maybe [FilePath]) +getInputFiles = gets stInputFiles + +getOutputFile :: PandocMonad m => m (Maybe FilePath) +getOutputFile = gets stOutputFile + + + + + + getPOSIXTime :: (PandocMonad m) => m POSIXTime getPOSIXTime = utcTimeToPOSIXSeconds <$> getCurrentTime @@ -142,27 +168,23 @@ addWarningWithPos mbpos msg = warn $ msg ++ maybe "" (\pos -> " " ++ show pos) mbpos - --- Nothing in this for now, but let's put it there anyway. -data PandocStateIO = PandocStateIO { ioStWarnings :: [String] - , ioStMediaBag :: MediaBag - } deriving Show - -instance Default PandocStateIO where - def = PandocStateIO { ioStWarnings = [] - , ioStMediaBag = mempty - } - -data PandocEnvIO = PandocEnvIO { ioEnvInputFiles :: Maybe [FilePath] - , ioEnvOutputFile :: Maybe FilePath +data CommonState = CommonState { stWarnings :: [String] + , stMediaBag :: MediaBag + , stInputFiles :: Maybe [FilePath] + , stOutputFile :: Maybe FilePath } -instance Default PandocEnvIO where - def = PandocEnvIO { ioEnvInputFiles = Nothing -- stdin - , ioEnvOutputFile = Nothing -- stdout + +instance Default CommonState where + def = CommonState { stWarnings = [] + , stMediaBag = mempty + , stInputFiles = Nothing + , stOutputFile = Nothing } +-- Nothing in this for now, but let's put it there anyway. + runIO :: PandocIO a -> IO (Either PandocError a) -runIO ma = flip evalStateT def $ flip runReaderT def $ runExceptT $ unPandocIO ma +runIO ma = flip evalStateT def $ runExceptT $ unPandocIO ma withMediaBag :: PandocMonad m => m a -> m (a, MediaBag) withMediaBag ma = ((,)) <$> ma <*> getMediaBag @@ -177,18 +199,14 @@ runIOorExplode ma = handleError <$> runIO ma -- Left (PandocParseError s) -> error $ "parse error" ++ s -- Left (PandocSomeError s) -> error s - - - newtype PandocIO a = PandocIO { - unPandocIO :: ExceptT PandocError (ReaderT PandocEnvIO (StateT PandocStateIO IO)) a + unPandocIO :: ExceptT PandocError (StateT CommonState IO) a } deriving ( MonadIO , Functor , Applicative , Monad - , MonadReader PandocEnvIO - , MonadState PandocStateIO + , MonadState CommonState , MonadError PandocError ) @@ -214,48 +232,29 @@ instance PandocMonad PandocIO where fail = M.fail fetchItem ms s = liftIO $ IO.fetchItem ms s fetchItem' mb ms s = liftIO $ IO.fetchItem' mb ms s - warn msg = do - modify $ \st -> st{ioStWarnings = msg : ioStWarnings st} - liftIO $ IO.warn msg - getWarnings = gets ioStWarnings glob = liftIO . IO.glob getModificationTime fp = do eitherMtime <- liftIO (tryIOError $ IO.getModificationTime fp) case eitherMtime of Right mtime -> return mtime Left _ -> throwError $ PandocFileReadError fp - -- Common functions - setMediaBag mb = - modify $ \st -> st{ioStMediaBag = mb} - getMediaBag = gets ioStMediaBag - insertMedia fp mime bs = - modify $ \st -> st{ioStMediaBag = MB.insertMedia fp mime bs (ioStMediaBag st) } - getInputFiles = asks ioEnvInputFiles - getOutputFile = asks ioEnvOutputFile - data PureState = PureState { stStdGen :: StdGen , stWord8Store :: [Word8] -- should be -- inifinite, -- i.e. [1..] - , stWarnings :: [String] , stUniqStore :: [Int] -- should be -- inifinite and -- contain every -- element at most -- once, e.g. [1..] - , stMediaBag :: MediaBag } instance Default PureState where def = PureState { stStdGen = mkStdGen 1848 , stWord8Store = [1..] - , stWarnings = [] , stUniqStore = [1..] - , stMediaBag = mempty - - } data FileInfo = FileInfo { infoFileMTime :: UTCTime , infoFileContents :: B.ByteString @@ -276,8 +275,6 @@ data PureEnv = PureEnv { envEnv :: [(String, String)] , envUserDataDir :: FileTree , envCabalDataDir :: FileTree , envFontFiles :: [FilePath] - , envInputFiles :: Maybe [FilePath] - , envOutputFile :: Maybe FilePath } -- We have to figure this out a bit more. But let's put some empty @@ -292,23 +289,25 @@ instance Default PureEnv where , envUserDataDir = mempty , envCabalDataDir = mempty , envFontFiles = [] - , envInputFiles = Nothing - , envOutputFile = Nothing } newtype PandocPure a = PandocPure { unPandocPure :: ExceptT PandocError - (ReaderT PureEnv (State PureState)) a + (ReaderT PureEnv (StateT CommonState (State PureState))) a } deriving ( Functor , Applicative , Monad , MonadReader PureEnv - , MonadState PureState + , MonadState CommonState , MonadError PandocError ) runPure :: PandocPure a -> Either PandocError a -runPure x = flip evalState def $ flip runReaderT def $ runExceptT $ unPandocPure x +runPure x = flip evalState def $ + flip evalStateT def $ + flip runReaderT def $ + runExceptT $ + unPandocPure x instance PandocMonad PandocPure where lookupEnv s = do @@ -323,17 +322,17 @@ instance PandocMonad PandocPure where getDefaultReferenceODT _ = asks envReferenceODT - newStdGen = do - g <- gets stStdGen + newStdGen = PandocPure $ do + g <- lift $ lift $ lift $ gets stStdGen let (_, nxtGen) = next g - modify $ \st -> st { stStdGen = nxtGen } + lift $ lift $ lift $ modify $ \st -> st { stStdGen = nxtGen } return g - newUniqueHash = do - uniqs <- gets stUniqStore + newUniqueHash = PandocPure $ do + uniqs <- lift $ lift $ lift $ gets stUniqStore case uniqs of u : us -> do - modify $ \st -> st { stUniqStore = us } + lift $ lift $ lift $ modify $ \st -> st { stUniqStore = us } return u _ -> M.fail "uniq store ran out of elements" readFileLazy fp = do @@ -365,10 +364,6 @@ instance PandocMonad PandocPure where Nothing -> fetchItem sourceUrl nm Just (mime, bs) -> return (Right (B.concat $ BL.toChunks bs, Just mime)) - warn s = modify $ \st -> st { stWarnings = s : stWarnings st } - - getWarnings = gets stWarnings - glob s = do fontFiles <- asks envFontFiles return (filter (match (compile s)) fontFiles) @@ -379,19 +374,6 @@ instance PandocMonad PandocPure where Just tm -> return tm Nothing -> throwError $ PandocFileReadError fp - -- Common files - - setMediaBag mb = - modify $ \st -> st{stMediaBag = mb} - - getMediaBag = gets stMediaBag - - insertMedia fp mime bs = - modify $ \st -> st{stMediaBag = MB.insertMedia fp mime bs (stMediaBag st) } - - getInputFiles = asks envInputFiles - - getOutputFile = asks envOutputFile -- cgit v1.2.3 From 06eb9cfb349ca6ccfde3d1938fcd13ddc65f5cb6 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Thu, 1 Dec 2016 15:39:54 -0500 Subject: Make Txt2Tags test pass. We don't have a good way to set things that aren't in the common state. That will be the next order of business. --- pandoc.cabal | 3 ++- src/Text/Pandoc/Class.hs | 1 + tests/Tests/Readers/Txt2Tags.hs | 13 +++++++++---- 3 files changed, 12 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/pandoc.cabal b/pandoc.cabal index ef253f79a..dbd0a4d1c 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -518,7 +518,8 @@ Test-Suite test-pandoc containers >= 0.1 && < 0.6, ansi-terminal >= 0.5 && < 0.7, executable-path >= 0.0 && < 0.1, - zip-archive >= 0.2.3.4 && < 0.4 + zip-archive >= 0.2.3.4 && < 0.4, + mtl >= 2.2 && < 2.3 Other-Modules: Tests.Old Tests.Helpers Tests.Shared diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index a888861b8..49c2b788e 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -32,6 +32,7 @@ Typeclass for pandoc readers and writers, allowing both IO and pure instances. -} module Text.Pandoc.Class ( PandocMonad(..) + , CommonState(..) , PureState(..) , PureEnv(..) , getPOSIXTime diff --git a/tests/Tests/Readers/Txt2Tags.hs b/tests/Tests/Readers/Txt2Tags.hs index 33502ba78..bef1d4965 100644 --- a/tests/Tests/Readers/Txt2Tags.hs +++ b/tests/Tests/Readers/Txt2Tags.hs @@ -4,16 +4,21 @@ module Tests.Readers.Txt2Tags (tests) where import Text.Pandoc.Definition import Test.Framework import Tests.Helpers +import Control.Monad.State import Text.Pandoc.Arbitrary() import Text.Pandoc.Builder import Text.Pandoc import Data.List (intersperse) -import Text.Pandoc.Readers.Txt2Tags +import Text.Pandoc.Class t2t :: String -> Pandoc -- t2t = handleError . readTxt2Tags (T2TMeta "date" "mtime" "in" "out") def -t2t = purely $ readTxt2Tags def +t2t = purely $ \s -> do + put def { stInputFiles = Just ["in"] + , stOutputFile = Just "out" + } + readTxt2Tags def s infix 4 =: (=:) :: ToString c @@ -80,10 +85,10 @@ tests = , "Macros: Date" =: "%%date" =?> - para "date" + para "1970-01-01" , "Macros: Mod Time" =: "%%mtime" =?> - para "mtime" + para "" , "Macros: Infile" =: "%%infile" =?> para "in" -- cgit v1.2.3 From 5a81c914e700af75a0626ac7c7b2e318fb0aa039 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Thu, 1 Dec 2016 18:35:05 -0500 Subject: Remove reader from PandocPure. Make it all state. This will make it easier to set things. --- src/Text/Pandoc/Class.hs | 93 ++++++++++++++++++++++-------------------------- 1 file changed, 43 insertions(+), 50 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 49c2b788e..18f22a41b 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -34,7 +34,6 @@ Typeclass for pandoc readers and writers, allowing both IO and pure instances. module Text.Pandoc.Class ( PandocMonad(..) , CommonState(..) , PureState(..) - , PureEnv(..) , getPOSIXTime , getZonedTime , warn @@ -86,7 +85,6 @@ import System.FilePath (()) import qualified System.FilePath.Glob as IO (glob) import qualified System.Directory as IO (getModificationTime) import Control.Monad.State hiding (fail) -import Control.Monad.Reader hiding (fail) import Control.Monad.Except hiding (fail) import Data.Word (Word8) import Data.Default @@ -250,12 +248,30 @@ data PureState = PureState { stStdGen :: StdGen -- contain every -- element at most -- once, e.g. [1..] + , envEnv :: [(String, String)] + , envTime :: UTCTime + , envTimeZone :: TimeZone + , envReferenceDocx :: Archive + , envReferenceODT :: Archive + , envFiles :: FileTree + , envUserDataDir :: FileTree + , envCabalDataDir :: FileTree + , envFontFiles :: [FilePath] } instance Default PureState where def = PureState { stStdGen = mkStdGen 1848 , stWord8Store = [1..] , stUniqStore = [1..] + , envEnv = [("USER", "pandoc-user")] + , envTime = posixSecondsToUTCTime 0 + , envTimeZone = utc + , envReferenceDocx = emptyArchive + , envReferenceODT = emptyArchive + , envFiles = mempty + , envUserDataDir = mempty + , envCabalDataDir = mempty + , envFontFiles = [] } data FileInfo = FileInfo { infoFileMTime :: UTCTime , infoFileContents :: B.ByteString @@ -267,38 +283,13 @@ newtype FileTree = FileTree {unFileTree :: M.Map FilePath FileInfo} getFileInfo :: FilePath -> FileTree -> Maybe FileInfo getFileInfo fp tree = M.lookup fp $ unFileTree tree -data PureEnv = PureEnv { envEnv :: [(String, String)] - , envTime :: UTCTime - , envTimeZone :: TimeZone - , envReferenceDocx :: Archive - , envReferenceODT :: Archive - , envFiles :: FileTree - , envUserDataDir :: FileTree - , envCabalDataDir :: FileTree - , envFontFiles :: [FilePath] - } - --- We have to figure this out a bit more. But let's put some empty --- values in for the time being. -instance Default PureEnv where - def = PureEnv { envEnv = [("USER", "pandoc-user")] - , envTime = posixSecondsToUTCTime 0 - , envTimeZone = utc - , envReferenceDocx = emptyArchive - , envReferenceODT = emptyArchive - , envFiles = mempty - , envUserDataDir = mempty - , envCabalDataDir = mempty - , envFontFiles = [] - } newtype PandocPure a = PandocPure { unPandocPure :: ExceptT PandocError - (ReaderT PureEnv (StateT CommonState (State PureState))) a + (StateT CommonState (State PureState)) a } deriving ( Functor , Applicative , Monad - , MonadReader PureEnv , MonadState CommonState , MonadError PandocError ) @@ -306,38 +297,40 @@ newtype PandocPure a = PandocPure { runPure :: PandocPure a -> Either PandocError a runPure x = flip evalState def $ flip evalStateT def $ - flip runReaderT def $ runExceptT $ unPandocPure x +-- setPureState :: PureState -> PandocPure () +-- setPureState st = PandocPure $ lift $ lift $ lift $ put st + instance PandocMonad PandocPure where - lookupEnv s = do - env <- asks envEnv + lookupEnv s = PandocPure $ do + env <- lift $ lift $ gets envEnv return (lookup s env) - getCurrentTime = asks envTime + getCurrentTime = PandocPure $ lift $ lift $ gets envTime - getCurrentTimeZone = asks envTimeZone + getCurrentTimeZone = PandocPure $ lift $ lift $ gets envTimeZone - getDefaultReferenceDocx _ = asks envReferenceDocx + getDefaultReferenceDocx _ = PandocPure $ lift $ lift $ gets envReferenceDocx - getDefaultReferenceODT _ = asks envReferenceODT + getDefaultReferenceODT _ = PandocPure $ lift $ lift $ gets envReferenceODT newStdGen = PandocPure $ do - g <- lift $ lift $ lift $ gets stStdGen + g <- lift $ lift $ gets stStdGen let (_, nxtGen) = next g - lift $ lift $ lift $ modify $ \st -> st { stStdGen = nxtGen } + lift $ lift $ modify $ \st -> st { stStdGen = nxtGen } return g newUniqueHash = PandocPure $ do - uniqs <- lift $ lift $ lift $ gets stUniqStore + uniqs <- lift $ lift $ gets stUniqStore case uniqs of u : us -> do - lift $ lift $ lift $ modify $ \st -> st { stUniqStore = us } + lift $ lift $ modify $ \st -> st { stUniqStore = us } return u _ -> M.fail "uniq store ran out of elements" - readFileLazy fp = do - fps <- asks envFiles + readFileLazy fp = PandocPure $ do + fps <- lift $ lift $ gets envFiles case infoFileContents <$> getFileInfo fp fps of Just bs -> return (BL.fromStrict bs) Nothing -> throwError $ PandocFileReadError fp @@ -348,14 +341,14 @@ instance PandocMonad PandocPure where readDataFile Nothing fname = do let fname' = if fname == "MANUAL.txt" then fname else "data" fname BL.toStrict <$> (readFileLazy fname') - readDataFile (Just userDir) fname = do - userDirFiles <- asks envUserDataDir + readDataFile (Just userDir) fname = PandocPure $ do + userDirFiles <- lift $ lift $ gets envUserDataDir case infoFileContents <$> (getFileInfo (userDir fname) userDirFiles) of Just bs -> return bs - Nothing -> readDataFile Nothing fname + Nothing -> unPandocPure $ readDataFile Nothing fname fail = M.fail - fetchItem _ fp = do - fps <- asks envFiles + fetchItem _ fp = PandocPure $ do + fps <- lift $ lift $ gets envFiles case infoFileContents <$> (getFileInfo fp fps) of Just bs -> return (Right (bs, getMimeType fp)) Nothing -> return (Left $ E.toException $ PandocFileReadError fp) @@ -365,12 +358,12 @@ instance PandocMonad PandocPure where Nothing -> fetchItem sourceUrl nm Just (mime, bs) -> return (Right (B.concat $ BL.toChunks bs, Just mime)) - glob s = do - fontFiles <- asks envFontFiles + glob s = PandocPure $ do + fontFiles <- lift $ lift $ gets envFontFiles return (filter (match (compile s)) fontFiles) - getModificationTime fp = do - fps <- asks envFiles + getModificationTime fp = PandocPure $ do + fps <- lift $ lift $ gets envFiles case infoFileMTime <$> (getFileInfo fp fps) of Just tm -> return tm Nothing -> throwError $ PandocFileReadError fp -- cgit v1.2.3 From d5051ae1015f2fdb39d034b566c1296d066ad8bf Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Thu, 1 Dec 2016 18:51:57 -0500 Subject: Remove redundant imports from OPML reader. --- src/Text/Pandoc/Readers/OPML.hs | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/OPML.hs b/src/Text/Pandoc/Readers/OPML.hs index 627566609..cec64895c 100644 --- a/src/Text/Pandoc/Readers/OPML.hs +++ b/src/Text/Pandoc/Readers/OPML.hs @@ -11,8 +11,6 @@ import Text.HTML.TagSoup.Entity (lookupEntity) import Data.Generics import Control.Monad.State import Data.Default -import Control.Monad.Except -import Text.Pandoc.Error import Text.Pandoc.Class (PandocMonad) type OPML m = StateT OPMLState m -- cgit v1.2.3 From c4c56b8c0471b5051d334e8ccc3f2e6cb1efbf13 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Fri, 2 Dec 2016 07:27:42 -0500 Subject: Fix rebasing errors. --- src/Text/Pandoc/Writers/FB2.hs | 2 +- src/Text/Pandoc/Writers/HTML.hs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index 2401d7eee..20af67b62 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -85,7 +85,7 @@ pandocToFB2 :: PandocMonad m -> Pandoc -> FBM m String pandocToFB2 opts (Pandoc meta blocks) = do - modify (\s -> s { writerOptions = opts { writerOptions = opts } }) + modify (\s -> s { writerOptions = opts }) desc <- description meta fp <- frontpage meta secs <- renderSections 1 blocks diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 4c8ccfe4a..b2b0865bf 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -115,7 +115,7 @@ writeHtml :: PandocMonad m => WriterOptions -> Pandoc -> m Html writeHtml opts d = do (body, context) <- evalStateT (pandocToHtml opts d) defaultWriterState return $ case writerTemplate opts of - Nothing -> renderHtml body + Nothing -> body Just tpl -> renderTemplate' tpl $ defField "body" (renderHtml body) context -- cgit v1.2.3 From 650fa2078890462f39be8fb294031bb706dbb5a0 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Fri, 2 Dec 2016 08:15:10 -0500 Subject: Readers: pass errors straight up to PandocMonad. Since we've unified error types, we can just throw the same error at the toplevel. --- src/Text/Pandoc/Readers/LaTeX.hs | 2 +- src/Text/Pandoc/Readers/Markdown.hs | 3 +-- src/Text/Pandoc/Readers/MediaWiki.hs | 3 +-- src/Text/Pandoc/Readers/Odt.hs | 2 +- src/Text/Pandoc/Readers/RST.hs | 3 +-- src/Text/Pandoc/Readers/TWiki.hs | 2 +- src/Text/Pandoc/Readers/Textile.hs | 3 +-- src/Text/Pandoc/Readers/Txt2Tags.hs | 3 +-- 8 files changed, 8 insertions(+), 13 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 882777c0e..425e905f8 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -68,7 +68,7 @@ readLaTeX opts ltx = do parsed <- readWithM parseLaTeX def{ stateOptions = opts } ltx case parsed of Right result -> return result - Left _ -> throwError $ PandocParseError "parsing error" + Left e -> throwError e parseLaTeX :: PandocMonad m => LP m Pandoc parseLaTeX = do diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 0acfca980..b59e5a5f1 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -68,7 +68,6 @@ import Debug.Trace (trace) import Data.Monoid ((<>)) import Control.Monad.Trans (lift) import Control.Monad.Except (throwError, catchError) -import Text.Pandoc.Error import Text.Pandoc.Class (PandocMonad) import qualified Text.Pandoc.Class as P @@ -83,7 +82,7 @@ readMarkdown opts s = do parsed <- (readWithM parseMarkdown) def{ stateOptions = opts } (s ++ "\n\n") case parsed of Right result -> return result - Left _ -> throwError $ PandocParseError "markdown parse error" + Left e -> throwError e -- | Read markdown from an input string and return a pair of a Pandoc document -- and a list of warnings. diff --git a/src/Text/Pandoc/Readers/MediaWiki.hs b/src/Text/Pandoc/Readers/MediaWiki.hs index e22e88bcb..5bdf0ca4e 100644 --- a/src/Text/Pandoc/Readers/MediaWiki.hs +++ b/src/Text/Pandoc/Readers/MediaWiki.hs @@ -58,7 +58,6 @@ import Data.Maybe (fromMaybe) import Text.Printf (printf) import Debug.Trace (trace) import Control.Monad.Except (throwError) -import Text.Pandoc.Error(PandocError(..)) import Text.Pandoc.Class (PandocMonad) -- | Read mediawiki from an input string and return a Pandoc document. @@ -77,7 +76,7 @@ readMediaWiki opts s = do (s ++ "\n") case parsed of Right result -> return result - Left _ -> throwError $ PandocParseError "problem parsing mediawiki" + Left e -> throwError e data MWState = MWState { mwOptions :: ReaderOptions , mwMaxNestingLevel :: Int diff --git a/src/Text/Pandoc/Readers/Odt.hs b/src/Text/Pandoc/Readers/Odt.hs index 9c8e76081..ac22f2c09 100644 --- a/src/Text/Pandoc/Readers/Odt.hs +++ b/src/Text/Pandoc/Readers/Odt.hs @@ -64,7 +64,7 @@ readOdt opts bytes = case readOdt' opts bytes of Right (doc, mb) -> do P.setMediaBag mb return doc - Left _ -> throwError $ PandocParseError "couldn't parse odt" + Left e -> throwError e -- readOdt' :: ReaderOptions diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index a20e29e93..078d2963c 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -51,7 +51,6 @@ import Data.Char (toLower, isHexDigit, isSpace) import Data.Monoid ((<>)) import Control.Monad.Except (throwError) import Control.Monad.Trans (lift) -import Text.Pandoc.Error import Text.Pandoc.Class (PandocMonad) import qualified Text.Pandoc.Class as P @@ -64,7 +63,7 @@ readRST opts s = do parsed <- (readWithM parseRST) def{ stateOptions = opts } (s ++ "\n\n") case parsed of Right result -> return result - Left _ -> throwError $ PandocParseError "error parsing rst" + Left e -> throwError e readRSTWithWarnings :: PandocMonad m => ReaderOptions -- ^ Reader options diff --git a/src/Text/Pandoc/Readers/TWiki.hs b/src/Text/Pandoc/Readers/TWiki.hs index 40ea8b75a..b2b136f39 100644 --- a/src/Text/Pandoc/Readers/TWiki.hs +++ b/src/Text/Pandoc/Readers/TWiki.hs @@ -60,7 +60,7 @@ readTWiki opts s = case readTWikiWithWarnings' opts s of Right (doc, warns) -> do mapM_ P.warn warns return doc - Left _ -> throwError $ PandocParseError "couldn't parse TWiki" + Left e -> throwError e readTWikiWithWarnings' :: ReaderOptions -- ^ Reader options -> String -- ^ String to parse (assuming @'\n'@ line endings) diff --git a/src/Text/Pandoc/Readers/Textile.hs b/src/Text/Pandoc/Readers/Textile.hs index 4b558b42e..721b57f46 100644 --- a/src/Text/Pandoc/Readers/Textile.hs +++ b/src/Text/Pandoc/Readers/Textile.hs @@ -68,7 +68,6 @@ import Control.Monad ( guard, liftM, when ) import Data.Monoid ((<>)) import Text.Printf import Debug.Trace (trace) -import Text.Pandoc.Error import Text.Pandoc.Class (PandocMonad) import Control.Monad.Except (throwError) @@ -81,7 +80,7 @@ readTextile opts s = do parsed <- readWithM parseTextile def{ stateOptions = opts } (s ++ "\n\n") case parsed of Right result -> return result - Left _ -> throwError $ PandocParseError "textile parse error" + Left e -> throwError e -- | Generate a Pandoc ADT from a textile document diff --git a/src/Text/Pandoc/Readers/Txt2Tags.hs b/src/Text/Pandoc/Readers/Txt2Tags.hs index 2769ecb42..4abe13827 100644 --- a/src/Text/Pandoc/Readers/Txt2Tags.hs +++ b/src/Text/Pandoc/Readers/Txt2Tags.hs @@ -50,7 +50,6 @@ import Control.Monad.Reader (Reader, runReader, asks) import Data.Time.Format (formatTime) import Text.Pandoc.Compat.Time (defaultTimeLocale) import Control.Monad.Except (throwError, catchError) -import Text.Pandoc.Error import Text.Pandoc.Class (PandocMonad) import qualified Text.Pandoc.Class as P @@ -99,7 +98,7 @@ readTxt2Tags opts s = do let parsed = flip runReader meta $ readWithM parseT2T (def {stateOptions = opts}) (s ++ "\n\n") case parsed of Right result -> return $ result - Left _ -> throwError $ PandocParseError "error parsing t2t" + Left e -> throwError e -- | Read Txt2Tags (ignoring all macros) from an input string returning -- a Pandoc document -- cgit v1.2.3 From e35c6c9e4db6fe906e46f82938b1aecd021011b3 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Fri, 2 Dec 2016 08:26:56 -0500 Subject: Try adding OverlappingInstances pragma to parsing. It's having trouble figuring out HasQuoteContext. --- src/Text/Pandoc/Parsing.hs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 796d09632..3732eada8 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -4,6 +4,7 @@ , TypeSynonymInstances , MultiParamTypeClasses , FlexibleInstances +, OverlappingInstances , IncoherentInstances #-} {- -- cgit v1.2.3 From 912eee362b029bd1a5524434c55ff0496df5dfcf Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Fri, 2 Dec 2016 08:47:54 -0500 Subject: Remove OverlappingInstances pragma. It doesn't help to solve the problem in 7.8. --- src/Text/Pandoc/Parsing.hs | 1 - 1 file changed, 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 3732eada8..796d09632 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -4,7 +4,6 @@ , TypeSynonymInstances , MultiParamTypeClasses , FlexibleInstances -, OverlappingInstances , IncoherentInstances #-} {- -- cgit v1.2.3 From 221f878c0ec691dd09cf388d4d86ebecc8bf8355 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Fri, 2 Dec 2016 09:11:24 -0500 Subject: Class: cleanup and clarification. --- src/Text/Pandoc/Class.hs | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 18f22a41b..0881878ed 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -114,13 +114,10 @@ class (Functor m, Applicative m, Monad m, MonadError PandocError m, MonadState C fail :: String -> m b glob :: String -> m [FilePath] getModificationTime :: FilePath -> m UTCTime - -- The following are common to all instantiations of the monad, up - -- to the record names, so I'd like to work out a better way to deal - -- with it. ---Some functions derived from Primitives: +-- Functions defined for all PandocMonad instances warn :: PandocMonad m => String -> m () warn msg = modify $ \st -> st{stWarnings = msg : stWarnings st} @@ -144,11 +141,6 @@ getInputFiles = gets stInputFiles getOutputFile :: PandocMonad m => m (Maybe FilePath) getOutputFile = gets stOutputFile - - - - - getPOSIXTime :: (PandocMonad m) => m POSIXTime getPOSIXTime = utcTimeToPOSIXSeconds <$> getCurrentTime @@ -167,6 +159,11 @@ addWarningWithPos mbpos msg = warn $ msg ++ maybe "" (\pos -> " " ++ show pos) mbpos +-- + +-- All PandocMonad instances should be an instance MonadState of this +-- datatype: + data CommonState = CommonState { stWarnings :: [String] , stMediaBag :: MediaBag , stInputFiles :: Maybe [FilePath] @@ -180,8 +177,6 @@ instance Default CommonState where , stOutputFile = Nothing } --- Nothing in this for now, but let's put it there anyway. - runIO :: PandocIO a -> IO (Either PandocError a) runIO ma = flip evalStateT def $ runExceptT $ unPandocIO ma @@ -190,14 +185,6 @@ withMediaBag ma = ((,)) <$> ma <*> getMediaBag runIOorExplode :: PandocIO a -> IO a runIOorExplode ma = handleError <$> runIO ma - -- eitherVal <- runIO ma - -- case eitherVal of - -- Right x -> return x - -- Left (PandocFileReadError fp) -> error $ "problem reading " ++ fp - -- Left (PandocShouldNeverHappenError s) -> error s - -- Left (PandocParseError s) -> error $ "parse error" ++ s - -- Left (PandocSomeError s) -> error s - newtype PandocIO a = PandocIO { unPandocIO :: ExceptT PandocError (StateT CommonState IO) a @@ -300,9 +287,6 @@ runPure x = flip evalState def $ runExceptT $ unPandocPure x --- setPureState :: PureState -> PandocPure () --- setPureState st = PandocPure $ lift $ lift $ lift $ put st - instance PandocMonad PandocPure where lookupEnv s = PandocPure $ do env <- lift $ lift $ gets envEnv -- cgit v1.2.3 From 830be4d63204b918afd15615d965bfbc40886cbe Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 3 Dec 2016 16:15:13 +0100 Subject: Refactored math conversion in writers. * Remove exported module `Text.Pandoc.Readers.TeXMath` * Add exported module `Text.Pandoc.Writers.Math` * The function `texMathToInlines` now lives in `Text.Pandoc.Writers.Math` * Export helper function `convertMath` from `Text.Pandoc.Writers.Math` * Use these functions in all writers that do math conversion. This ensures that warnings will always be issued for failed math conversions. --- pandoc.cabal | 2 +- src/Text/Pandoc/Readers/TeXMath.hs | 48 ------ src/Text/Pandoc/Writers/Docbook.hs | 285 +++++++++++++++++--------------- src/Text/Pandoc/Writers/Docx.hs | 18 +- src/Text/Pandoc/Writers/HTML.hs | 15 +- src/Text/Pandoc/Writers/Haddock.hs | 63 ++++--- src/Text/Pandoc/Writers/ICML.hs | 5 +- src/Text/Pandoc/Writers/Man.hs | 6 +- src/Text/Pandoc/Writers/Markdown.hs | 22 ++- src/Text/Pandoc/Writers/Math.hs | 47 ++++++ src/Text/Pandoc/Writers/OpenDocument.hs | 136 ++++++++------- src/Text/Pandoc/Writers/RTF.hs | 259 ++++++++++++++++------------- 12 files changed, 489 insertions(+), 417 deletions(-) delete mode 100644 src/Text/Pandoc/Readers/TeXMath.hs create mode 100644 src/Text/Pandoc/Writers/Math.hs (limited to 'src/Text') diff --git a/pandoc.cabal b/pandoc.cabal index dbd0a4d1c..691c83099 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -341,7 +341,6 @@ Library Text.Pandoc.Readers.Org, Text.Pandoc.Readers.DocBook, Text.Pandoc.Readers.OPML, - Text.Pandoc.Readers.TeXMath, Text.Pandoc.Readers.Textile, Text.Pandoc.Readers.Native, Text.Pandoc.Readers.Haddock, @@ -377,6 +376,7 @@ Library Text.Pandoc.Writers.EPUB, Text.Pandoc.Writers.FB2, Text.Pandoc.Writers.TEI, + Text.Pandoc.Writers.Math, Text.Pandoc.PDF, Text.Pandoc.UTF8, Text.Pandoc.Templates, diff --git a/src/Text/Pandoc/Readers/TeXMath.hs b/src/Text/Pandoc/Readers/TeXMath.hs deleted file mode 100644 index e5778b123..000000000 --- a/src/Text/Pandoc/Readers/TeXMath.hs +++ /dev/null @@ -1,48 +0,0 @@ -{- -Copyright (C) 2007-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.TeXMath - Copyright : Copyright (C) 2007-2015 John MacFarlane - License : GNU GPL, version 2 or above - - Maintainer : John MacFarlane - Stability : alpha - Portability : portable - -Conversion of TeX math to a list of 'Pandoc' inline elements. --} -module Text.Pandoc.Readers.TeXMath ( texMathToInlines ) where - -import Text.Pandoc.Definition -import Text.TeXMath - --- | Converts a raw TeX math formula to a list of 'Pandoc' inlines. --- Defaults to raw formula between @$@ or @$$@ characters if entire formula --- can't be converted. -texMathToInlines :: MathType - -> String -- ^ String to parse (assumes @'\n'@ line endings) - -> [Inline] -texMathToInlines mt inp = - case writePandoc dt `fmap` readTeX inp of - Right (Just ils) -> ils - _ -> [Str (delim ++ inp ++ delim)] - where (dt, delim) = case mt of - DisplayMath -> (DisplayBlock, "$$") - InlineMath -> (DisplayInline, "$") - diff --git a/src/Text/Pandoc/Writers/Docbook.hs b/src/Text/Pandoc/Writers/Docbook.hs index 74e3bff3d..0ec7445be 100644 --- a/src/Text/Pandoc/Writers/Docbook.hs +++ b/src/Text/Pandoc/Writers/Docbook.hs @@ -36,7 +36,7 @@ import Text.Pandoc.Walk import Text.Pandoc.Writers.Shared import Text.Pandoc.Options import Text.Pandoc.Templates (renderTemplate') -import Text.Pandoc.Readers.TeXMath +import Text.Pandoc.Writers.Math import Data.List ( stripPrefix, isPrefixOf, intercalate, isSuffixOf ) import Data.Char ( toLower ) import Data.Monoid ( Any(..) ) @@ -50,13 +50,13 @@ import Data.Generics (everywhere, mkT) import Text.Pandoc.Class (PandocMonad) -- | Convert list of authors to a docbook section -authorToDocbook :: WriterOptions -> [Inline] -> B.Inlines -authorToDocbook opts name' = - let name = render Nothing $ inlinesToDocbook opts name' - colwidth = if writerWrapText opts == WrapAuto +authorToDocbook :: PandocMonad m => WriterOptions -> [Inline] -> m B.Inlines +authorToDocbook opts name' = do + name <- render Nothing <$> inlinesToDocbook opts name' + let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing - in B.rawInline "docbook" $ render colwidth $ + return $ B.rawInline "docbook" $ render colwidth $ if ',' `elem` name then -- last name first let (lastname, rest) = break (==',') name @@ -75,44 +75,45 @@ authorToDocbook opts name' = -- | Convert Pandoc document to string in Docbook format. writeDocbook :: PandocMonad m => WriterOptions -> Pandoc -> m String -writeDocbook opts (Pandoc meta blocks) = return $ +writeDocbook opts (Pandoc meta blocks) = do let elements = hierarchicalize blocks - colwidth = if writerWrapText opts == WrapAuto + let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing - render' = render colwidth - opts' = if (maybe False (("/book>" `isSuffixOf`) . trimr) + let render' = render colwidth + let opts' = if (maybe False (("/book>" `isSuffixOf`) . trimr) (writerTemplate opts) && TopLevelDefault == writerTopLevelDivision opts) then opts{ writerTopLevelDivision = TopLevelChapter } else opts - -- The numbering here follows LaTeX's internal numbering - startLvl = case writerTopLevelDivision opts' of + -- The numbering here follows LaTeX's internal numbering + let startLvl = case writerTopLevelDivision opts' of TopLevelPart -> -1 TopLevelChapter -> 0 TopLevelSection -> 1 TopLevelDefault -> 1 - auths' = map (authorToDocbook opts) $ docAuthors meta - meta' = B.setMeta "author" auths' meta - Just metadata = metaToJSON opts - (Just . render colwidth . (vcat . - (map (elementToDocbook opts' startLvl)) . hierarchicalize)) - (Just . render colwidth . inlinesToDocbook opts') + auths' <- mapM (authorToDocbook opts) $ docAuthors meta + let meta' = B.setMeta "author" auths' meta + metadata <- metaToJSON opts + (fmap (render colwidth . vcat) . + (mapM (elementToDocbook opts' startLvl) . + hierarchicalize)) + (fmap (render colwidth) . inlinesToDocbook opts') meta' - main = render' $ vcat (map (elementToDocbook opts' startLvl) elements) - context = defField "body" main + main <- (render' . vcat) <$> mapM (elementToDocbook opts' startLvl) elements + let context = defField "body" main $ defField "mathml" (case writerHTMLMathMethod opts of MathML _ -> True _ -> False) $ metadata - in case writerTemplate opts of + return $ case writerTemplate opts of Nothing -> main Just tpl -> renderTemplate' tpl context -- | Convert an Element to Docbook. -elementToDocbook :: WriterOptions -> Int -> Element -> Doc +elementToDocbook :: PandocMonad m => WriterOptions -> Int -> Element -> m Doc elementToDocbook opts _ (Blk block) = blockToDocbook opts block -elementToDocbook opts lvl (Sec _ _num (id',_,_) title elements) = +elementToDocbook opts lvl (Sec _ _num (id',_,_) title elements) = do -- Docbook doesn't allow sections with no content, so insert some if needed let elements' = if null elements then [Blk (Para [])] @@ -131,13 +132,14 @@ elementToDocbook opts lvl (Sec _ _num (id',_,_) title elements) = nsAttr = if writerDocbook5 opts && lvl == 0 then [("xmlns", "http://docbook.org/ns/docbook"),("xmlns:xlink", "http://www.w3.org/1999/xlink")] else [] attribs = nsAttr ++ idAttr - in inTags True tag attribs $ - inTagsSimple "title" (inlinesToDocbook opts title) $$ - vcat (map (elementToDocbook opts (lvl + 1)) elements') + contents <- mapM (elementToDocbook opts (lvl + 1)) elements' + title' <- inlinesToDocbook opts title + return $ inTags True tag attribs $ + inTagsSimple "title" title' $$ vcat contents -- | Convert a list of Pandoc blocks to Docbook. -blocksToDocbook :: WriterOptions -> [Block] -> Doc -blocksToDocbook opts = vcat . map (blockToDocbook opts) +blocksToDocbook :: PandocMonad m => WriterOptions -> [Block] -> m Doc +blocksToDocbook opts = fmap vcat . mapM (blockToDocbook opts) -- | Auxiliary function to convert Plain block to Para. plainToPara :: Block -> Block @@ -146,26 +148,29 @@ plainToPara x = x -- | Convert a list of pairs of terms and definitions into a list of -- Docbook varlistentrys. -deflistItemsToDocbook :: WriterOptions -> [([Inline],[[Block]])] -> Doc +deflistItemsToDocbook :: PandocMonad m + => WriterOptions -> [([Inline],[[Block]])] -> m Doc deflistItemsToDocbook opts items = - vcat $ map (\(term, defs) -> deflistItemToDocbook opts term defs) items + vcat <$> mapM (\(term, defs) -> deflistItemToDocbook opts term defs) items -- | Convert a term and a list of blocks into a Docbook varlistentry. -deflistItemToDocbook :: WriterOptions -> [Inline] -> [[Block]] -> Doc -deflistItemToDocbook opts term defs = - let def' = concatMap (map plainToPara) defs - in inTagsIndented "varlistentry" $ - inTagsIndented "term" (inlinesToDocbook opts term) $$ - inTagsIndented "listitem" (blocksToDocbook opts def') +deflistItemToDocbook :: PandocMonad m + => WriterOptions -> [Inline] -> [[Block]] -> m Doc +deflistItemToDocbook opts term defs = do + term' <- inlinesToDocbook opts term + def' <- blocksToDocbook opts $ concatMap (map plainToPara) defs + return $ inTagsIndented "varlistentry" $ + inTagsIndented "term" term' $$ + inTagsIndented "listitem" def' -- | Convert a list of lists of blocks to a list of Docbook list items. -listItemsToDocbook :: WriterOptions -> [[Block]] -> Doc -listItemsToDocbook opts items = vcat $ map (listItemToDocbook opts) items +listItemsToDocbook :: PandocMonad m => WriterOptions -> [[Block]] -> m Doc +listItemsToDocbook opts items = vcat <$> mapM (listItemToDocbook opts) items -- | Convert a list of blocks into a Docbook list item. -listItemToDocbook :: WriterOptions -> [Block] -> Doc +listItemToDocbook :: PandocMonad m => WriterOptions -> [Block] -> m Doc listItemToDocbook opts item = - inTagsIndented "listitem" $ blocksToDocbook opts $ map plainToPara item + inTagsIndented "listitem" <$> blocksToDocbook opts (map plainToPara item) imageToDocbook :: WriterOptions -> Attr -> String -> Doc imageToDocbook _ attr src = selfClosingTag "imagedata" $ @@ -177,43 +182,46 @@ imageToDocbook _ attr src = selfClosingTag "imagedata" $ Nothing -> [] -- | Convert a Pandoc block element to Docbook. -blockToDocbook :: WriterOptions -> Block -> Doc -blockToDocbook _ Null = empty +blockToDocbook :: PandocMonad m => WriterOptions -> Block -> m Doc +blockToDocbook _ Null = return empty -- Add ids to paragraphs in divs with ids - this is needed for -- pandoc-citeproc to get link anchors in bibliographies: blockToDocbook opts (Div (ident,_,_) [Para lst]) = let attribs = [("id", ident) | not (null ident)] in if hasLineBreaks lst - then flush $ nowrap $ inTags False "literallayout" attribs - $ inlinesToDocbook opts lst - else inTags True "para" attribs $ inlinesToDocbook opts lst -blockToDocbook opts (Div (ident,_,_) bs) = - (if null ident - then mempty - else selfClosingTag "anchor" [("id", ident)]) $$ - blocksToDocbook opts (map plainToPara bs) -blockToDocbook _ (Header _ _ _) = empty -- should not occur after hierarchicalize + then (flush . nowrap . inTags False "literallayout" attribs) + <$> inlinesToDocbook opts lst + else inTags True "para" attribs <$> inlinesToDocbook opts lst +blockToDocbook opts (Div (ident,_,_) bs) = do + contents <- blocksToDocbook opts (map plainToPara bs) + return $ + (if null ident + then mempty + else selfClosingTag "anchor" [("id", ident)]) $$ contents +blockToDocbook _ (Header _ _ _) = + return empty -- should not occur after hierarchicalize blockToDocbook opts (Plain lst) = inlinesToDocbook opts lst -- title beginning with fig: indicates that the image is a figure -blockToDocbook opts (Para [Image attr txt (src,'f':'i':'g':':':_)]) = - let alt = inlinesToDocbook opts txt - capt = if null txt +blockToDocbook opts (Para [Image attr txt (src,'f':'i':'g':':':_)]) = do + alt <- inlinesToDocbook opts txt + let capt = if null txt then empty else inTagsSimple "title" alt - in inTagsIndented "figure" $ + return $ inTagsIndented "figure" $ capt $$ (inTagsIndented "mediaobject" $ (inTagsIndented "imageobject" (imageToDocbook opts attr src)) $$ inTagsSimple "textobject" (inTagsSimple "phrase" alt)) blockToDocbook opts (Para lst) - | hasLineBreaks lst = flush $ nowrap $ inTagsSimple "literallayout" $ inlinesToDocbook opts lst - | otherwise = inTagsIndented "para" $ inlinesToDocbook opts lst + | hasLineBreaks lst = (flush . nowrap . inTagsSimple "literallayout") + <$> inlinesToDocbook opts lst + | otherwise = inTagsIndented "para" <$> inlinesToDocbook opts lst blockToDocbook opts (LineBlock lns) = blockToDocbook opts $ linesToPara lns blockToDocbook opts (BlockQuote blocks) = - inTagsIndented "blockquote" $ blocksToDocbook opts blocks -blockToDocbook _ (CodeBlock (_,classes,_) str) = + inTagsIndented "blockquote" <$> blocksToDocbook opts blocks +blockToDocbook _ (CodeBlock (_,classes,_) str) = return $ text ("") <> cr <> flush (text (escapeStringForXML str) <> cr <> text "") where lang = if null langs @@ -225,11 +233,11 @@ blockToDocbook _ (CodeBlock (_,classes,_) str) = then [s] else languagesByExtension . map toLower $ s langs = concatMap langsFrom classes -blockToDocbook opts (BulletList lst) = +blockToDocbook opts (BulletList lst) = do let attribs = [("spacing", "compact") | isTightList lst] - in inTags True "itemizedlist" attribs $ listItemsToDocbook opts lst -blockToDocbook _ (OrderedList _ []) = empty -blockToDocbook opts (OrderedList (start, numstyle, _) (first:rest)) = + inTags True "itemizedlist" attribs <$> listItemsToDocbook opts lst +blockToDocbook _ (OrderedList _ []) = return empty +blockToDocbook opts (OrderedList (start, numstyle, _) (first:rest)) = do let numeration = case numstyle of DefaultStyle -> [] Decimal -> [("numeration", "arabic")] @@ -240,39 +248,41 @@ blockToDocbook opts (OrderedList (start, numstyle, _) (first:rest)) = LowerRoman -> [("numeration", "lowerroman")] spacing = [("spacing", "compact") | isTightList (first:rest)] attribs = numeration ++ spacing - items = if start == 1 - then listItemsToDocbook opts (first:rest) - else (inTags True "listitem" [("override",show start)] - (blocksToDocbook opts $ map plainToPara first)) $$ - listItemsToDocbook opts rest - in inTags True "orderedlist" attribs items -blockToDocbook opts (DefinitionList lst) = + items <- if start == 1 + then listItemsToDocbook opts (first:rest) + else do + first' <- blocksToDocbook opts (map plainToPara first) + rest' <- listItemsToDocbook opts rest + return $ + (inTags True "listitem" [("override",show start)] first') $$ + rest' + return $ inTags True "orderedlist" attribs items +blockToDocbook opts (DefinitionList lst) = do let attribs = [("spacing", "compact") | isTightList $ concatMap snd lst] - in inTags True "variablelist" attribs $ deflistItemsToDocbook opts lst + inTags True "variablelist" attribs <$> deflistItemsToDocbook opts lst blockToDocbook opts (RawBlock f str) - | f == "docbook" = text str -- raw XML block + | f == "docbook" = return $ text str -- raw XML block | f == "html" = if writerDocbook5 opts - then empty -- No html in Docbook5 - else text str -- allow html for backwards compatibility - | otherwise = empty -blockToDocbook _ HorizontalRule = empty -- not semantic -blockToDocbook opts (Table caption aligns widths headers rows) = - let captionDoc = if null caption - then empty - else inTagsIndented "title" - (inlinesToDocbook opts caption) - tableType = if isEmpty captionDoc then "informaltable" else "table" + then return empty -- No html in Docbook5 + else return $ text str -- allow html for backwards compatibility + | otherwise = return empty +blockToDocbook _ HorizontalRule = return empty -- not semantic +blockToDocbook opts (Table caption aligns widths headers rows) = do + captionDoc <- if null caption + then return empty + else inTagsIndented "title" <$> + inlinesToDocbook opts caption + let tableType = if isEmpty captionDoc then "informaltable" else "table" percent w = show (truncate (100*w) :: Integer) ++ "*" coltags = vcat $ zipWith (\w al -> selfClosingTag "colspec" ([("colwidth", percent w) | w > 0] ++ [("align", alignmentToString al)])) widths aligns - head' = if all null headers - then empty - else inTagsIndented "thead" $ - tableRowToDocbook opts headers - body' = inTagsIndented "tbody" $ - vcat $ map (tableRowToDocbook opts) rows - in inTagsIndented tableType $ captionDoc $$ + head' <- if all null headers + then return empty + else inTagsIndented "thead" <$> tableRowToDocbook opts headers + body' <- (inTagsIndented "tbody" . vcat) <$> + mapM (tableRowToDocbook opts) rows + return $ inTagsIndented tableType $ captionDoc $$ (inTags True "tgroup" [("cols", show (length headers))] $ coltags $$ head' $$ body') @@ -293,92 +303,97 @@ alignmentToString alignment = case alignment of AlignCenter -> "center" AlignDefault -> "left" -tableRowToDocbook :: WriterOptions +tableRowToDocbook :: PandocMonad m + => WriterOptions -> [[Block]] - -> Doc + -> m Doc tableRowToDocbook opts cols = - inTagsIndented "row" $ vcat $ map (tableItemToDocbook opts) cols + (inTagsIndented "row" . vcat) <$> mapM (tableItemToDocbook opts) cols -tableItemToDocbook :: WriterOptions +tableItemToDocbook :: PandocMonad m + => WriterOptions -> [Block] - -> Doc + -> m Doc tableItemToDocbook opts item = - inTags True "entry" [] $ vcat $ map (blockToDocbook opts) item + (inTags True "entry" [] . vcat) <$> mapM (blockToDocbook opts) item -- | Convert a list of inline elements to Docbook. -inlinesToDocbook :: WriterOptions -> [Inline] -> Doc -inlinesToDocbook opts lst = hcat $ map (inlineToDocbook opts) lst +inlinesToDocbook :: PandocMonad m => WriterOptions -> [Inline] -> m Doc +inlinesToDocbook opts lst = hcat <$> mapM (inlineToDocbook opts) lst -- | Convert an inline element to Docbook. -inlineToDocbook :: WriterOptions -> Inline -> Doc -inlineToDocbook _ (Str str) = text $ escapeStringForXML str +inlineToDocbook :: PandocMonad m => WriterOptions -> Inline -> m Doc +inlineToDocbook _ (Str str) = return $ text $ escapeStringForXML str inlineToDocbook opts (Emph lst) = - inTagsSimple "emphasis" $ inlinesToDocbook opts lst + inTagsSimple "emphasis" <$> inlinesToDocbook opts lst inlineToDocbook opts (Strong lst) = - inTags False "emphasis" [("role", "strong")] $ inlinesToDocbook opts lst + inTags False "emphasis" [("role", "strong")] <$> inlinesToDocbook opts lst inlineToDocbook opts (Strikeout lst) = - inTags False "emphasis" [("role", "strikethrough")] $ + inTags False "emphasis" [("role", "strikethrough")] <$> inlinesToDocbook opts lst inlineToDocbook opts (Superscript lst) = - inTagsSimple "superscript" $ inlinesToDocbook opts lst + inTagsSimple "superscript" <$> inlinesToDocbook opts lst inlineToDocbook opts (Subscript lst) = - inTagsSimple "subscript" $ inlinesToDocbook opts lst + inTagsSimple "subscript" <$> inlinesToDocbook opts lst inlineToDocbook opts (SmallCaps lst) = - inTags False "emphasis" [("role", "smallcaps")] $ + inTags False "emphasis" [("role", "smallcaps")] <$> inlinesToDocbook opts lst inlineToDocbook opts (Quoted _ lst) = - inTagsSimple "quote" $ inlinesToDocbook opts lst + inTagsSimple "quote" <$> inlinesToDocbook opts lst inlineToDocbook opts (Cite _ lst) = inlinesToDocbook opts lst inlineToDocbook opts (Span (ident,_,_) ils) = - (if null ident - then mempty - else selfClosingTag "anchor" [("id", ident)]) <> + ((if null ident + then mempty + else selfClosingTag "anchor" [("id", ident)]) <>) <$> inlinesToDocbook opts ils inlineToDocbook _ (Code _ str) = - inTagsSimple "literal" $ text (escapeStringForXML str) + return $ inTagsSimple "literal" $ text (escapeStringForXML str) inlineToDocbook opts (Math t str) - | isMathML (writerHTMLMathMethod opts) = - case writeMathML dt <$> readTeX str of - Right r -> inTagsSimple tagtype - $ text $ Xml.ppcElement conf - $ fixNS - $ removeAttr r - Left _ -> inlinesToDocbook opts - $ texMathToInlines t str - | otherwise = inlinesToDocbook opts $ texMathToInlines t str - where (dt, tagtype) = case t of - InlineMath -> (DisplayInline,"inlineequation") - DisplayMath -> (DisplayBlock,"informalequation") + | isMathML (writerHTMLMathMethod opts) = do + res <- convertMath writeMathML t str + case res of + Right r -> return $ inTagsSimple tagtype + $ text $ Xml.ppcElement conf + $ fixNS + $ removeAttr r + Left il -> inlineToDocbook opts il + | otherwise = + texMathToInlines t str >>= inlinesToDocbook opts + where tagtype = case t of + InlineMath -> "inlineequation" + DisplayMath -> "informalequation" conf = Xml.useShortEmptyTags (const False) Xml.defaultConfigPP removeAttr e = e{ Xml.elAttribs = [] } fixNS' qname = qname{ Xml.qPrefix = Just "mml" } fixNS = everywhere (mkT fixNS') -inlineToDocbook _ (RawInline f x) | f == "html" || f == "docbook" = text x - | otherwise = empty -inlineToDocbook _ LineBreak = text "\n" +inlineToDocbook _ (RawInline f x) + | f == "html" || f == "docbook" = return $ text x + | otherwise = return empty +inlineToDocbook _ LineBreak = return $ text "\n" -- currently ignore, would require the option to add custom -- styles to the document -inlineToDocbook _ PageBreak = empty -inlineToDocbook _ Space = space +inlineToDocbook _ PageBreak = return empty +inlineToDocbook _ Space = return space -- because we use \n for LineBreak, we can't do soft breaks: -inlineToDocbook _ SoftBreak = space +inlineToDocbook _ SoftBreak = return space inlineToDocbook opts (Link attr txt (src, _)) | Just email <- stripPrefix "mailto:" src = let emailLink = inTagsSimple "email" $ text $ escapeStringForXML $ email in case txt of - [Str s] | escapeURI s == email -> emailLink - _ -> inlinesToDocbook opts txt <+> - char '(' <> emailLink <> char ')' + [Str s] | escapeURI s == email -> return emailLink + _ -> do contents <- inlinesToDocbook opts txt + return $ contents <+> + char '(' <> emailLink <> char ')' | otherwise = (if isPrefixOf "#" src then inTags False "link" $ ("linkend", drop 1 src) : idAndRole attr else if writerDocbook5 opts then inTags False "link" $ ("xlink:href", src) : idAndRole attr - else inTags False "ulink" $ ("url", src) : idAndRole attr ) $ - inlinesToDocbook opts txt -inlineToDocbook opts (Image attr _ (src, tit)) = + else inTags False "ulink" $ ("url", src) : idAndRole attr ) + <$> inlinesToDocbook opts txt +inlineToDocbook opts (Image attr _ (src, tit)) = return $ let titleDoc = if null tit then empty else inTagsIndented "objectinfo" $ @@ -386,7 +401,7 @@ inlineToDocbook opts (Image attr _ (src, tit)) = in inTagsIndented "inlinemediaobject" $ inTagsIndented "imageobject" $ titleDoc $$ imageToDocbook opts attr src inlineToDocbook opts (Note contents) = - inTagsIndented "footnote" $ blocksToDocbook opts contents + inTagsIndented "footnote" <$> blocksToDocbook opts contents isMathML :: HTMLMathMethod -> Bool isMathML (MathML _) = True diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index cc0c180f2..90261dede 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -45,7 +45,7 @@ import Text.Pandoc.ImageSize import Text.Pandoc.Shared hiding (Element) import Text.Pandoc.Writers.Shared (fixDisplayMath) import Text.Pandoc.Options -import Text.Pandoc.Readers.TeXMath +import Text.Pandoc.Writers.Math import Text.Pandoc.Highlighting ( highlight ) import Text.Pandoc.Walk import Text.XML.Light as XML @@ -1114,17 +1114,11 @@ inlineToOpenXML' opts (Quoted quoteType lst) = SingleQuote -> ("\x2018", "\x2019") DoubleQuote -> ("\x201C", "\x201D") inlineToOpenXML' opts (Math mathType str) = do - let displayType = if mathType == DisplayMath - then DisplayBlock - else DisplayInline - when (displayType == DisplayBlock) setFirstPara - case writeOMML displayType <$> readTeX str of - Right r -> return [r] - Left e -> do - (lift . lift) $ P.warn $ - "Cannot convert the following TeX math, skipping:\n" ++ str ++ - "\n" ++ e - inlinesToOpenXML opts (texMathToInlines mathType str) + when (mathType == DisplayMath) setFirstPara + res <- (lift . lift) (convertMath writeOMML mathType str) + case res of + Right r -> return [r] + Left il -> inlineToOpenXML' opts il inlineToOpenXML' opts (Cite _ lst) = inlinesToOpenXML opts lst inlineToOpenXML' opts (Code attrs str) = do let unhighlighted = intercalate [br] `fmap` diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index b2b0865bf..40658eaa8 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -36,7 +36,7 @@ import Text.Pandoc.Writers.Shared import Text.Pandoc.Options import Text.Pandoc.ImageSize import Text.Pandoc.Templates -import Text.Pandoc.Readers.TeXMath +import Text.Pandoc.Writers.Math import Text.Pandoc.Slides import Text.Pandoc.Highlighting ( highlight, styleToCss, formatHtmlInline, formatHtmlBlock ) @@ -794,17 +794,14 @@ inlineToHtml opts inline = InlineMath -> preEscapedString $ "" ++ str ++ "" DisplayMath -> preEscapedString $ "" ++ str ++ "" MathML _ -> do - let dt = if t == InlineMath - then DisplayInline - else DisplayBlock let conf = useShortEmptyTags (const False) defaultConfigPP - case writeMathML dt <$> readTeX str of + res <- lift $ convertMath writeMathML t str + case res of Right r -> return $ preEscapedString $ ppcElement conf (annotateMML r str) - Left _ -> inlineListToHtml opts - (texMathToInlines t str) >>= - return . (H.span ! A.class_ mathClass) + Left il -> (H.span ! A.class_ mathClass) <$> + inlineToHtml opts il MathJax _ -> return $ H.span ! A.class_ mathClass $ toHtml $ case t of InlineMath -> "\\(" ++ str ++ "\\)" @@ -814,7 +811,7 @@ inlineToHtml opts inline = InlineMath -> str DisplayMath -> "\\displaystyle " ++ str) PlainMath -> do - x <- inlineListToHtml opts (texMathToInlines t str) + x <- lift (texMathToInlines t str) >>= inlineListToHtml opts let m = H.span ! A.class_ mathClass $ x let brtag = if writerHtml5 opts then H5.br else H.br return $ case t of diff --git a/src/Text/Pandoc/Writers/Haddock.hs b/src/Text/Pandoc/Writers/Haddock.hs index 03ce8c0eb..115d5d8d8 100644 --- a/src/Text/Pandoc/Writers/Haddock.hs +++ b/src/Text/Pandoc/Writers/Haddock.hs @@ -39,7 +39,7 @@ import Text.Pandoc.Options import Data.List ( intersperse, transpose ) import Text.Pandoc.Pretty import Control.Monad.State -import Text.Pandoc.Readers.TeXMath (texMathToInlines) +import Text.Pandoc.Writers.Math (texMathToInlines) import Network.URI (isURI) import Data.Default import Text.Pandoc.Class (PandocMonad) @@ -51,12 +51,13 @@ instance Default WriterState -- | Convert Pandoc to Haddock. writeHaddock :: PandocMonad m => WriterOptions -> Pandoc -> m String -writeHaddock opts document = return $ - evalState (pandocToHaddock opts{ +writeHaddock opts document = + evalStateT (pandocToHaddock opts{ writerWrapText = writerWrapText opts } document) def -- | Return haddock representation of document. -pandocToHaddock :: WriterOptions -> Pandoc -> State WriterState String +pandocToHaddock :: PandocMonad m + => WriterOptions -> Pandoc -> StateT WriterState m String pandocToHaddock opts (Pandoc meta blocks) = do let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts @@ -79,7 +80,8 @@ pandocToHaddock opts (Pandoc meta blocks) = do Just tpl -> return $ renderTemplate' tpl context -- | Return haddock representation of notes. -notesToHaddock :: WriterOptions -> [[Block]] -> State WriterState Doc +notesToHaddock :: PandocMonad m + => WriterOptions -> [[Block]] -> StateT WriterState m Doc notesToHaddock opts notes = if null notes then return empty @@ -93,9 +95,10 @@ escapeString = escapeStringUsing haddockEscapes where haddockEscapes = backslashEscapes "\\/'`\"@<" -- | Convert Pandoc block element to haddock. -blockToHaddock :: WriterOptions -- ^ Options - -> Block -- ^ Block element - -> State WriterState Doc +blockToHaddock :: PandocMonad m + => WriterOptions -- ^ Options + -> Block -- ^ Block element + -> StateT WriterState m Doc blockToHaddock _ Null = return empty blockToHaddock opts (Div _ ils) = do contents <- blockListToHaddock opts ils @@ -168,8 +171,9 @@ blockToHaddock opts (DefinitionList items) = do contents <- mapM (definitionListItemToHaddock opts) items return $ cat contents <> blankline -pandocTable :: WriterOptions -> Bool -> [Alignment] -> [Double] - -> [Doc] -> [[Doc]] -> State WriterState Doc +pandocTable :: PandocMonad m + => WriterOptions -> Bool -> [Alignment] -> [Double] + -> [Doc] -> [[Doc]] -> StateT WriterState m Doc pandocTable opts headless aligns widths rawHeaders rawRows = do let isSimple = all (==0) widths let alignHeader alignment = case alignment of @@ -208,8 +212,9 @@ pandocTable opts headless aligns widths rawHeaders rawRows = do else border return $ head'' $$ underline $$ body $$ bottom -gridTable :: WriterOptions -> Bool -> [Alignment] -> [Double] - -> [Doc] -> [[Doc]] -> State WriterState Doc +gridTable :: PandocMonad m + => WriterOptions -> Bool -> [Alignment] -> [Double] + -> [Doc] -> [[Doc]] -> StateT WriterState m Doc gridTable opts headless _aligns widths headers' rawRows = do let numcols = length headers' let widths' = if all (==0) widths @@ -236,7 +241,8 @@ gridTable opts headless _aligns widths headers' rawRows = do return $ border '-' $$ head'' $$ body $$ border '-' -- | Convert bullet list item (list of blocks) to haddock -bulletListItemToHaddock :: WriterOptions -> [Block] -> State WriterState Doc +bulletListItemToHaddock :: PandocMonad m + => WriterOptions -> [Block] -> StateT WriterState m Doc bulletListItemToHaddock opts items = do contents <- blockListToHaddock opts items let sps = replicate (writerTabStop opts - 2) ' ' @@ -251,10 +257,11 @@ bulletListItemToHaddock opts items = do return $ hang (writerTabStop opts) start $ contents' <> cr -- | Convert ordered list item (a list of blocks) to haddock -orderedListItemToHaddock :: WriterOptions -- ^ options - -> String -- ^ list item marker - -> [Block] -- ^ list item (list of blocks) - -> State WriterState Doc +orderedListItemToHaddock :: PandocMonad m + => WriterOptions -- ^ options + -> String -- ^ list item marker + -> [Block] -- ^ list item (list of blocks) + -> StateT WriterState m Doc orderedListItemToHaddock opts marker items = do contents <- blockListToHaddock opts items let sps = case length marker - writerTabStop opts of @@ -264,9 +271,10 @@ orderedListItemToHaddock opts marker items = do return $ hang (writerTabStop opts) start $ contents <> cr -- | Convert definition list item (label, list of blocks) to haddock -definitionListItemToHaddock :: WriterOptions - -> ([Inline],[[Block]]) - -> State WriterState Doc +definitionListItemToHaddock :: PandocMonad m + => WriterOptions + -> ([Inline],[[Block]]) + -> StateT WriterState m Doc definitionListItemToHaddock opts (label, defs) = do labelText <- inlineListToHaddock opts label defs' <- mapM (mapM (blockToHaddock opts)) defs @@ -274,19 +282,22 @@ definitionListItemToHaddock opts (label, defs) = do return $ nowrap (brackets labelText) <> cr <> contents <> cr -- | Convert list of Pandoc block elements to haddock -blockListToHaddock :: WriterOptions -- ^ Options - -> [Block] -- ^ List of block elements - -> State WriterState Doc +blockListToHaddock :: PandocMonad m + => WriterOptions -- ^ Options + -> [Block] -- ^ List of block elements + -> StateT WriterState m Doc blockListToHaddock opts blocks = mapM (blockToHaddock opts) blocks >>= return . cat -- | Convert list of Pandoc inline elements to haddock. -inlineListToHaddock :: WriterOptions -> [Inline] -> State WriterState Doc +inlineListToHaddock :: PandocMonad m + => WriterOptions -> [Inline] -> StateT WriterState m Doc inlineListToHaddock opts lst = mapM (inlineToHaddock opts) lst >>= return . cat -- | Convert Pandoc inline element to haddock. -inlineToHaddock :: WriterOptions -> Inline -> State WriterState Doc +inlineToHaddock :: PandocMonad m + => WriterOptions -> Inline -> StateT WriterState m Doc inlineToHaddock opts (Span (ident,_,_) ils) = do contents <- inlineListToHaddock opts ils if not (null ident) && null ils @@ -322,7 +333,7 @@ inlineToHaddock opts (Math mt str) = do let adjust x = case mt of DisplayMath -> cr <> x <> cr InlineMath -> x - adjust `fmap` (inlineListToHaddock opts $ texMathToInlines mt str) + adjust <$> (lift (texMathToInlines mt str) >>= inlineListToHaddock opts) inlineToHaddock _ (RawInline f str) | f == "haddock" = return $ text str | otherwise = return empty diff --git a/src/Text/Pandoc/Writers/ICML.hs b/src/Text/Pandoc/Writers/ICML.hs index f624b7dec..7c42671f1 100644 --- a/src/Text/Pandoc/Writers/ICML.hs +++ b/src/Text/Pandoc/Writers/ICML.hs @@ -16,7 +16,7 @@ into InDesign with File -> Place. module Text.Pandoc.Writers.ICML (writeICML) where import Text.Pandoc.Definition import Text.Pandoc.XML -import Text.Pandoc.Readers.TeXMath (texMathToInlines) +import Text.Pandoc.Writers.Math (texMathToInlines) import Text.Pandoc.Writers.Shared import Text.Pandoc.Shared (linesToPara, splitBy) import Text.Pandoc.Options @@ -435,7 +435,8 @@ inlineToICML opts style SoftBreak = inlineToICML _ style LineBreak = charStyle style $ text lineSeparator inlineToICML _ _ PageBreak = return empty inlineToICML opts style (Math mt str) = - cat <$> mapM (inlineToICML opts style) (texMathToInlines mt str) + lift (texMathToInlines mt str) >>= + (fmap cat . mapM (inlineToICML opts style)) inlineToICML _ _ (RawInline f str) | f == Format "icml" = return $ text str | otherwise = return empty diff --git a/src/Text/Pandoc/Writers/Man.hs b/src/Text/Pandoc/Writers/Man.hs index 27cf22b41..a9a30fd45 100644 --- a/src/Text/Pandoc/Writers/Man.hs +++ b/src/Text/Pandoc/Writers/Man.hs @@ -34,7 +34,7 @@ import Text.Pandoc.Templates import Text.Pandoc.Shared import Text.Pandoc.Writers.Shared import Text.Pandoc.Options -import Text.Pandoc.Readers.TeXMath +import Text.Pandoc.Writers.Math import Text.Printf ( printf ) import Data.List ( stripPrefix, intersperse, intercalate ) import Data.Maybe (fromMaybe) @@ -342,9 +342,9 @@ inlineToMan _ (Str str@('.':_)) = return $ afterBreak "\\&" <> text (escapeString str) inlineToMan _ (Str str) = return $ text $ escapeString str inlineToMan opts (Math InlineMath str) = - inlineListToMan opts $ texMathToInlines InlineMath str + lift (texMathToInlines InlineMath str) >>= inlineListToMan opts inlineToMan opts (Math DisplayMath str) = do - contents <- inlineListToMan opts $ texMathToInlines DisplayMath str + contents <- lift (texMathToInlines DisplayMath str) >>= inlineListToMan opts return $ cr <> text ".RS" $$ contents $$ text ".RE" inlineToMan _ (RawInline f str) | f == Format "man" = return $ text str diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 092693ea4..66e0365d8 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -48,7 +48,7 @@ import Control.Monad.Reader import Control.Monad.State import Control.Monad.Except (throwError) import Text.Pandoc.Writers.HTML (writeHtmlString) -import Text.Pandoc.Readers.TeXMath (texMathToInlines) +import Text.Pandoc.Writers.Math (texMathToInlines) import Text.HTML.TagSoup (parseTags, isTagText, Tag(..)) import Network.URI (isURI) import Data.Default @@ -200,7 +200,7 @@ pandocToMarkdown opts (Pandoc meta blocks) = do Nothing -> empty let headerBlocks = filter isHeaderBlock blocks toc <- if writerTableOfContents opts - then lift $ lift $ tableOfContents opts headerBlocks + then liftPandoc $ tableOfContents opts headerBlocks else return empty -- Strip off final 'references' header if markdown citations enabled let blocks' = if isEnabled Ext_citations opts @@ -533,7 +533,7 @@ blockToMarkdown' opts t@(Table caption aligns widths headers rows) = do rawHeaders rawRows | isEnabled Ext_raw_html opts -> fmap (id,) $ text <$> - (lift $ lift $ writeHtmlString def $ Pandoc nullMeta [t]) + (liftPandoc $ writeHtmlString def $ Pandoc nullMeta [t]) | otherwise -> return $ (id, text "[TABLE]") return $ nst $ tbl $$ blankline $$ caption'' $$ blankline blockToMarkdown' opts (BulletList items) = do @@ -985,9 +985,9 @@ inlineToMarkdown opts (Math InlineMath str) = return $ "\\\\(" <> text str <> "\\\\)" | otherwise -> do plain <- asks envPlain - inlineListToMarkdown opts $ - (if plain then makeMathPlainer else id) $ - texMathToInlines InlineMath str + (liftPandoc (texMathToInlines InlineMath str)) >>= + inlineListToMarkdown opts . + (if plain then makeMathPlainer else id) inlineToMarkdown opts (Math DisplayMath str) = case writerHTMLMathMethod opts of WebTeX url -> (\x -> blankline <> x <> blankline) `fmap` @@ -1000,7 +1000,8 @@ inlineToMarkdown opts (Math DisplayMath str) = | isEnabled Ext_tex_math_double_backslash opts -> return $ "\\\\[" <> text str <> "\\\\]" | otherwise -> (\x -> cr <> x <> cr) `fmap` - inlineListToMarkdown opts (texMathToInlines DisplayMath str) + (liftPandoc (texMathToInlines DisplayMath str) >>= + inlineListToMarkdown opts) inlineToMarkdown opts (RawInline f str) = do plain <- asks envPlain if not plain && @@ -1062,7 +1063,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 - (text . trim) <$> (lift $ lift $ writeHtmlString def $ Pandoc nullMeta [Plain [lnk]]) + (text . trim) <$> (liftPandoc $ writeHtmlString def $ Pandoc nullMeta [Plain [lnk]]) | otherwise = do plain <- asks envPlain linktext <- inlineListToMarkdown opts txt @@ -1101,7 +1102,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 - (text . trim) <$> (lift $ lift $ writeHtmlString def $ Pandoc nullMeta [Plain [img]]) + (text . trim) <$> (liftPandoc $ writeHtmlString def $ Pandoc nullMeta [Plain [img]]) | otherwise = do plain <- asks envPlain let txt = if null alternate || alternate == [Str source] @@ -1125,3 +1126,6 @@ makeMathPlainer = walk go where go (Emph xs) = Span nullAttr xs go x = x + +liftPandoc :: PandocMonad m => m a -> MD m a +liftPandoc = lift . lift diff --git a/src/Text/Pandoc/Writers/Math.hs b/src/Text/Pandoc/Writers/Math.hs new file mode 100644 index 000000000..4540a2479 --- /dev/null +++ b/src/Text/Pandoc/Writers/Math.hs @@ -0,0 +1,47 @@ +module Text.Pandoc.Writers.Math + ( texMathToInlines + , convertMath + ) +where + +import Text.Pandoc.Class +import Text.Pandoc.Definition +import Text.TeXMath (Exp, writePandoc, DisplayType(..), readTeX) + +-- | Converts a raw TeX math formula to a list of 'Pandoc' inlines. +-- Defaults to raw formula between @$@ or @$$@ characters if entire formula +-- can't be converted. +texMathToInlines :: PandocMonad m + => MathType + -> String -- ^ String to parse (assumes @'\n'@ line endings) + -> m [Inline] +texMathToInlines mt inp = do + res <- convertMath writePandoc mt inp + case res of + Right (Just ils) -> return ils + Right (Nothing) -> return [mkFallback mt inp] + Left il -> return [il] + +mkFallback :: MathType -> String -> Inline +mkFallback mt str = Str (delim ++ str ++ delim) + where delim = case mt of + DisplayMath -> "$$" + InlineMath -> "$" + +-- | Converts a raw TeX math formula using a writer function, +-- issuing a warning and producing a fallback (a raw string) +-- on failure. +convertMath :: PandocMonad m + => (DisplayType -> [Exp] -> a) -> MathType -> String + -> m (Either Inline a) +convertMath writer mt str = do + case writer dt <$> readTeX str of + Right r -> return (Right r) + Left e -> do + warn $ "Could not convert TeX math, rendering as raw TeX:\n" ++ + str ++ "\n" ++ e + return (Left $ mkFallback mt str) + where dt = case mt of + DisplayMath -> DisplayBlock + InlineMath -> DisplayInline + diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index 903c94828..1a758193a 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -35,8 +35,8 @@ import Text.Pandoc.Options import Text.Pandoc.XML import Text.Pandoc.Shared (linesToPara) import Text.Pandoc.Templates (renderTemplate') -import Text.Pandoc.Readers.TeXMath import Text.Pandoc.Readers.Odt.StyleReader +import Text.Pandoc.Writers.Math import Text.Pandoc.Pretty import Text.Printf ( printf ) import Control.Arrow ( (***), (>>>) ) @@ -58,6 +58,8 @@ plainToPara x = x -- OpenDocument writer -- +type OD m = StateT WriterState m + data WriterState = WriterState { stNotes :: [Doc] , stTableStyles :: [Doc] @@ -90,40 +92,40 @@ defaultWriterState = when :: Bool -> Doc -> Doc when p a = if p then a else empty -addTableStyle :: Doc -> State WriterState () +addTableStyle :: PandocMonad m => Doc -> OD m () addTableStyle i = modify $ \s -> s { stTableStyles = i : stTableStyles s } -addNote :: Doc -> State WriterState () +addNote :: PandocMonad m => Doc -> OD m () addNote i = modify $ \s -> s { stNotes = i : stNotes s } -addParaStyle :: Doc -> State WriterState () +addParaStyle :: PandocMonad m => Doc -> OD m () addParaStyle i = modify $ \s -> s { stParaStyles = i : stParaStyles s } -addTextStyle :: Set.Set TextStyle -> (String, Doc) -> State WriterState () +addTextStyle :: PandocMonad m => Set.Set TextStyle -> (String, Doc) -> OD m () addTextStyle attrs i = modify $ \s -> s { stTextStyles = Map.insert attrs i (stTextStyles s) } -addTextStyleAttr :: TextStyle -> State WriterState () +addTextStyleAttr :: PandocMonad m => TextStyle -> OD m () addTextStyleAttr t = modify $ \s -> s { stTextStyleAttr = Set.insert t (stTextStyleAttr s) } -increaseIndent :: State WriterState () +increaseIndent :: PandocMonad m => OD m () increaseIndent = modify $ \s -> s { stIndentPara = 1 + stIndentPara s } -resetIndent :: State WriterState () +resetIndent :: PandocMonad m => OD m () resetIndent = modify $ \s -> s { stIndentPara = (stIndentPara s) - 1 } -inTightList :: State WriterState a -> State WriterState a +inTightList :: PandocMonad m => OD m a -> OD m a inTightList f = modify (\s -> s { stTight = True }) >> f >>= \r -> modify (\s -> s { stTight = False }) >> return r -setInDefinitionList :: Bool -> State WriterState () +setInDefinitionList :: PandocMonad m => Bool -> OD m () setInDefinitionList b = modify $ \s -> s { stInDefinition = b } -setFirstPara :: State WriterState () +setFirstPara :: PandocMonad m => OD m () setFirstPara = modify $ \s -> s { stFirstPara = True } -inParagraphTags :: Doc -> State WriterState Doc +inParagraphTags :: PandocMonad m => Doc -> OD m Doc inParagraphTags d | isEmpty d = return empty inParagraphTags d = do b <- gets stFirstPara @@ -139,7 +141,7 @@ inParagraphTagsWithStyle sty = inTags False "text:p" [("text:style-name", sty)] inSpanTags :: String -> Doc -> Doc inSpanTags s = inTags False "text:span" [("text:style-name",s)] -withTextStyle :: TextStyle -> State WriterState a -> State WriterState a +withTextStyle :: PandocMonad m => TextStyle -> OD m a -> OD m a withTextStyle s f = do oldTextStyleAttr <- gets stTextStyleAttr addTextStyleAttr s @@ -147,7 +149,7 @@ withTextStyle s f = do modify $ \st -> st{ stTextStyleAttr = oldTextStyleAttr } return res -inTextStyle :: Doc -> State WriterState Doc +inTextStyle :: PandocMonad m => Doc -> OD m Doc inTextStyle d = do at <- gets stTextStyleAttr if Set.null at @@ -168,7 +170,7 @@ inTextStyle d = do return $ inTags False "text:span" [("text:style-name",styleName)] d -inHeaderTags :: Int -> Doc -> State WriterState Doc +inHeaderTags :: PandocMonad m => Int -> Doc -> OD m Doc inHeaderTags i d = return $ inTags False "text:h" [ ("text:style-name", "Heading_20_" ++ show i) , ("text:outline-level", show i)] d @@ -192,12 +194,12 @@ handleSpaces s -- | Convert Pandoc document to string in OpenDocument format. writeOpenDocument :: PandocMonad m => WriterOptions -> Pandoc -> m String -writeOpenDocument opts (Pandoc meta blocks) = return $ +writeOpenDocument opts (Pandoc meta blocks) = do let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing - render' = render colwidth - ((body, metadata),s) = flip runState + let render' = render colwidth + ((body, metadata),s) <- flip runStateT defaultWriterState $ do m <- metaToJSON opts (fmap (render colwidth) . blocksToOpenDocument opts) @@ -210,33 +212,36 @@ writeOpenDocument opts (Pandoc meta blocks) = return $ Map.elems (stTextStyles s)) listStyle (n,l) = inTags True "text:list-style" [("style:name", "L" ++ show n)] (vcat l) - listStyles = map listStyle (stListStyles s) - automaticStyles = vcat $ reverse $ styles ++ listStyles - context = defField "body" body + let listStyles = map listStyle (stListStyles s) + let automaticStyles = vcat $ reverse $ styles ++ listStyles + let context = defField "body" body $ defField "automatic-styles" (render' automaticStyles) $ metadata - in case writerTemplate opts of - Nothing -> body - Just tpl -> renderTemplate' tpl context + return $ case writerTemplate opts of + Nothing -> body + Just tpl -> renderTemplate' tpl context -withParagraphStyle :: WriterOptions -> String -> [Block] -> State WriterState Doc +withParagraphStyle :: PandocMonad m + => WriterOptions -> String -> [Block] -> OD m Doc withParagraphStyle o s (b:bs) | Para l <- b = go =<< inParagraphTagsWithStyle s <$> inlinesToOpenDocument o l | otherwise = go =<< blockToOpenDocument o b where go i = (<>) i <$> withParagraphStyle o s bs withParagraphStyle _ _ [] = return empty -inPreformattedTags :: String -> State WriterState Doc +inPreformattedTags :: PandocMonad m => String -> OD m Doc inPreformattedTags s = do n <- paraStyle [("style:parent-style-name","Preformatted_20_Text")] return . inParagraphTagsWithStyle ("P" ++ show n) . handleSpaces $ s -orderedListToOpenDocument :: WriterOptions -> Int -> [[Block]] -> State WriterState Doc +orderedListToOpenDocument :: PandocMonad m + => WriterOptions -> Int -> [[Block]] -> OD m Doc orderedListToOpenDocument o pn bs = vcat . map (inTagsIndented "text:list-item") <$> mapM (orderedItemToOpenDocument o pn . map plainToPara) bs -orderedItemToOpenDocument :: WriterOptions -> Int -> [Block] -> State WriterState Doc +orderedItemToOpenDocument :: PandocMonad m + => WriterOptions -> Int -> [Block] -> OD m Doc orderedItemToOpenDocument o n (b:bs) | OrderedList a l <- b = newLevel a l | Para l <- b = go =<< inParagraphTagsWithStyle ("P" ++ show n) <$> inlinesToOpenDocument o l @@ -256,7 +261,8 @@ isTightList (b:_) | Plain {} : _ <- b = True | otherwise = False -newOrderedListStyle :: Bool -> ListAttributes -> State WriterState (Int,Int) +newOrderedListStyle :: PandocMonad m + => Bool -> ListAttributes -> OD m (Int,Int) newOrderedListStyle b a = do ln <- (+) 1 . length <$> gets stListStyles let nbs = orderedListLevelStyle a (ln, []) @@ -264,7 +270,8 @@ newOrderedListStyle b a = do modify $ \s -> s { stListStyles = nbs : stListStyles s } return (ln,pn) -bulletListToOpenDocument :: WriterOptions -> [[Block]] -> State WriterState Doc +bulletListToOpenDocument :: PandocMonad m + => WriterOptions -> [[Block]] -> OD m Doc bulletListToOpenDocument o b = do ln <- (+) 1 . length <$> gets stListStyles (pn,ns) <- if isTightList b then inTightList (bulletListStyle ln) else bulletListStyle ln @@ -272,11 +279,13 @@ bulletListToOpenDocument o b = do is <- listItemsToOpenDocument ("P" ++ show pn) o b return $ inTags True "text:list" [("text:style-name", "L" ++ show ln)] is -listItemsToOpenDocument :: String -> WriterOptions -> [[Block]] -> State WriterState Doc +listItemsToOpenDocument :: PandocMonad m + => String -> WriterOptions -> [[Block]] -> OD m Doc listItemsToOpenDocument s o is = vcat . map (inTagsIndented "text:list-item") <$> mapM (withParagraphStyle o s . map plainToPara) is -deflistItemToOpenDocument :: WriterOptions -> ([Inline],[[Block]]) -> State WriterState Doc +deflistItemToOpenDocument :: PandocMonad m + => WriterOptions -> ([Inline],[[Block]]) -> OD m Doc deflistItemToOpenDocument o (t,d) = do let ts = if isTightList d then "Definition_20_Term_20_Tight" else "Definition_20_Term" @@ -286,7 +295,8 @@ deflistItemToOpenDocument o (t,d) = do d' <- liftM vcat $ mapM (withParagraphStyle o ds . (map plainToPara)) d return $ t' $$ d' -inBlockQuote :: WriterOptions -> Int -> [Block] -> State WriterState Doc +inBlockQuote :: PandocMonad m + => WriterOptions -> Int -> [Block] -> OD m Doc inBlockQuote o i (b:bs) | BlockQuote l <- b = do increaseIndent ni <- paraStyle @@ -298,11 +308,11 @@ inBlockQuote o i (b:bs) inBlockQuote _ _ [] = resetIndent >> return empty -- | Convert a list of Pandoc blocks to OpenDocument. -blocksToOpenDocument :: WriterOptions -> [Block] -> State WriterState Doc +blocksToOpenDocument :: PandocMonad m => WriterOptions -> [Block] -> OD m Doc blocksToOpenDocument o b = vcat <$> mapM (blockToOpenDocument o) b -- | Convert a Pandoc block element to OpenDocument. -blockToOpenDocument :: WriterOptions -> Block -> State WriterState Doc +blockToOpenDocument :: PandocMonad m => WriterOptions -> Block -> OD m Doc blockToOpenDocument o bs | Plain b <- bs = if null b then return empty @@ -374,29 +384,35 @@ blockToOpenDocument o bs endsWithPageBreak [PageBreak] = True endsWithPageBreak (_ : xs) = endsWithPageBreak xs - paragraph :: [Inline] -> State WriterState Doc + paragraph :: PandocMonad m => [Inline] -> OD m Doc paragraph [] = return empty paragraph (PageBreak : rest) | endsWithPageBreak rest = paraWithBreak PageBoth rest paragraph (PageBreak : rest) = paraWithBreak PageBefore rest paragraph inlines | endsWithPageBreak inlines = paraWithBreak PageAfter inlines paragraph inlines = inParagraphTags =<< inlinesToOpenDocument o inlines - paraWithBreak :: ParaBreak -> [Inline] -> State WriterState Doc + paraWithBreak :: PandocMonad m => ParaBreak -> [Inline] -> OD m Doc paraWithBreak breakKind bs = do pn <- paraBreakStyle breakKind withParagraphStyle o ("P" ++ show pn) [Para bs] -colHeadsToOpenDocument :: WriterOptions -> String -> [String] -> [[Block]] -> State WriterState Doc +colHeadsToOpenDocument :: PandocMonad m + => WriterOptions -> String -> [String] -> [[Block]] + -> OD m Doc colHeadsToOpenDocument o tn ns hs = inTagsIndented "table:table-header-rows" . inTagsIndented "table:table-row" . vcat <$> mapM (tableItemToOpenDocument o tn) (zip ns hs) -tableRowToOpenDocument :: WriterOptions -> String -> [String] -> [[Block]] -> State WriterState Doc +tableRowToOpenDocument :: PandocMonad m + => WriterOptions -> String -> [String] -> [[Block]] + -> OD m Doc tableRowToOpenDocument o tn ns cs = inTagsIndented "table:table-row" . vcat <$> mapM (tableItemToOpenDocument o tn) (zip ns cs) -tableItemToOpenDocument :: WriterOptions -> String -> (String,[Block])-> State WriterState Doc +tableItemToOpenDocument :: PandocMonad m + => WriterOptions -> String -> (String,[Block]) + -> OD m Doc tableItemToOpenDocument o tn (n,i) = let a = [ ("table:style-name" , tn ++ ".A1" ) , ("office:value-type", "string" ) @@ -405,10 +421,10 @@ tableItemToOpenDocument o tn (n,i) = withParagraphStyle o n (map plainToPara i) -- | Convert a list of inline elements to OpenDocument. -inlinesToOpenDocument :: WriterOptions -> [Inline] -> State WriterState Doc +inlinesToOpenDocument :: PandocMonad m => WriterOptions -> [Inline] -> OD m Doc inlinesToOpenDocument o l = hcat <$> toChunks o l -toChunks :: WriterOptions -> [Inline] -> State WriterState [Doc] +toChunks :: PandocMonad m => WriterOptions -> [Inline] -> OD m [Doc] toChunks _ [] = return [] toChunks o (x : xs) | isChunkable x = do @@ -429,7 +445,7 @@ isChunkable SoftBreak = True isChunkable _ = False -- | Convert an inline element to OpenDocument. -inlineToOpenDocument :: WriterOptions -> Inline -> State WriterState Doc +inlineToOpenDocument :: PandocMonad m => WriterOptions -> Inline -> OD m Doc inlineToOpenDocument o ils = case ils of Space -> return space @@ -448,7 +464,8 @@ inlineToOpenDocument o ils SmallCaps l -> withTextStyle SmallC $ inlinesToOpenDocument o l Quoted t l -> inQuotes t <$> inlinesToOpenDocument o l Code _ s -> inlinedCode $ preformatted s - Math t s -> inlinesToOpenDocument o (texMathToInlines t s) + Math t s -> lift (texMathToInlines t s) >>= + inlinesToOpenDocument o Cite _ l -> inlinesToOpenDocument o l RawInline f s -> if f == Format "opendocument" then return $ text s @@ -489,18 +506,18 @@ inlineToOpenDocument o ils addNote nn return nn -bulletListStyle :: Int -> State WriterState (Int,(Int,[Doc])) -bulletListStyle l = - let doStyles i = inTags True "text:list-level-style-bullet" - [ ("text:level" , show (i + 1) ) - , ("text:style-name" , "Bullet_20_Symbols") - , ("style:num-suffix", "." ) - , ("text:bullet-char", [bulletList !! i] ) - ] (listLevelStyle (1 + i)) - bulletList = map chr $ cycle [8226,8227,8259] - listElStyle = map doStyles [0..9] - in do pn <- paraListStyle l - return (pn, (l, listElStyle)) +bulletListStyle :: PandocMonad m => Int -> OD m (Int,(Int,[Doc])) +bulletListStyle l = do + let doStyles i = inTags True "text:list-level-style-bullet" + [ ("text:level" , show (i + 1) ) + , ("text:style-name" , "Bullet_20_Symbols") + , ("style:num-suffix", "." ) + , ("text:bullet-char", [bulletList !! i] ) + ] (listLevelStyle (1 + i)) + bulletList = map chr $ cycle [8226,8227,8259] + listElStyle = map doStyles [0..9] + pn <- paraListStyle l + return (pn, (l, listElStyle)) orderedListLevelStyle :: ListAttributes -> (Int, [Doc]) -> (Int,[Doc]) orderedListLevelStyle (s,n, d) (l,ls) = @@ -554,7 +571,7 @@ tableStyle num wcs = columnStyles = map colStyle wcs in table $$ vcat columnStyles $$ cellStyle -paraStyle :: [(String,String)] -> State WriterState Int +paraStyle :: PandocMonad m => [(String,String)] -> OD m Int paraStyle attrs = do pn <- (+) 1 . length <$> gets stParaStyles i <- (*) 0.5 . fromIntegral <$> gets stIndentPara :: State WriterState Double @@ -578,14 +595,13 @@ paraStyle attrs = do addParaStyle $ inTags True "style:style" (styleAttr ++ attrs) paraProps return pn -paraBreakStyle :: ParaBreak -> State WriterState Int +paraBreakStyle :: PandocMonad m => ParaBreak -> OD m Int paraBreakStyle PageBefore = paraStyle "Text_20_body" [("fo:break-before", "page")] paraBreakStyle PageAfter = paraStyle "Text_20_body" [("fo:break-after", "page")] paraBreakStyle PageBoth = paraStyle "Text_20_body" [("fo:break-before", "page"), ("fo:break-after", "page")] paraBreakStyle AutoNone = paraStyle "Text_20_body" [] - -paraListStyle :: Int -> State WriterState Int +paraListStyle :: PandocMonad m => Int -> OD m Int paraListStyle l = paraStyle [("style:parent-style-name","Text_20_body") ,("style:list-style-name", "L" ++ show l )] diff --git a/src/Text/Pandoc/Writers/RTF.hs b/src/Text/Pandoc/Writers/RTF.hs index f5d56d021..f71c97334 100644 --- a/src/Text/Pandoc/Writers/RTF.hs +++ b/src/Text/Pandoc/Writers/RTF.hs @@ -34,7 +34,7 @@ import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Shared import Text.Pandoc.Writers.Shared -import Text.Pandoc.Readers.TeXMath +import Text.Pandoc.Writers.Math import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Walk import Data.List ( isSuffixOf, intercalate ) @@ -83,49 +83,50 @@ rtfEmbedImage opts x@(Image attr _ (src,_)) = do rtfEmbedImage _ x = return x -- | Convert Pandoc to a string in rich text format, with --- images embedded as encoded binary data. -writeRTFWithEmbeddedImages :: PandocMonad m => WriterOptions -> Pandoc -> m String +-- images embedded as encoded binary data. TODO get rid of this, +-- we don't need it now that we have writeRTF in PandocMonad. +writeRTFWithEmbeddedImages :: PandocMonad m + => WriterOptions -> Pandoc -> m String writeRTFWithEmbeddedImages options doc = - writeRTF options `fmap` walkM (rtfEmbedImage options) doc + writeRTF options =<< walkM (rtfEmbedImage options) doc -- | Convert Pandoc to a string in rich text format. -writeRTF :: WriterOptions -> Pandoc -> String -writeRTF options (Pandoc meta@(Meta metamap) blocks) = +writeRTF :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeRTF options (Pandoc meta@(Meta metamap) blocks) = do let spacer = not $ all null $ docTitle meta : docDate meta : docAuthors meta - toPlain (MetaBlocks [Para ils]) = MetaInlines ils + let toPlain (MetaBlocks [Para ils]) = MetaInlines ils toPlain x = x - -- adjust title, author, date so we don't get para inside para - meta' = Meta $ M.adjust toPlain "title" + -- adjust title, author, date so we don't get para inside para + let meta' = Meta $ M.adjust toPlain "title" . M.adjust toPlain "author" . M.adjust toPlain "date" $ metamap - Just metadata = metaToJSON options - (Just . concatMap (blockToRTF 0 AlignDefault)) - (Just . inlineListToRTF) + metadata <- metaToJSON options + (fmap concat . mapM (blockToRTF 0 AlignDefault)) + (inlinesToRTF) meta' - body = concatMap (blockToRTF 0 AlignDefault) blocks - isTOCHeader (Header lev _ _) = lev <= writerTOCDepth options + body <- blocksToRTF 0 AlignDefault blocks + let isTOCHeader (Header lev _ _) = lev <= writerTOCDepth options isTOCHeader _ = False - context = defField "body" body + toc <- tableOfContents $ filter isTOCHeader blocks + let context = defField "body" body $ defField "spacer" spacer $ (if writerTableOfContents options - then defField "toc" - (tableOfContents $ filter isTOCHeader blocks) + then defField "toc" toc else id) $ metadata - in case writerTemplate options of + return $ case writerTemplate options of Just tpl -> renderTemplate' tpl context Nothing -> case reverse body of ('\n':_) -> body _ -> body ++ "\n" -- | Construct table of contents from list of header blocks. -tableOfContents :: [Block] -> String -tableOfContents headers = - let contentsTree = hierarchicalize headers - in concatMap (blockToRTF 0 AlignDefault) $ - [Header 1 nullAttr [Str "Contents"], - BulletList (map elementToListItem contentsTree)] +tableOfContents :: PandocMonad m => [Block] -> m String +tableOfContents headers = do + let contents = map elementToListItem $ hierarchicalize headers + blocksToRTF 0 AlignDefault $ + [Header 1 nullAttr [Str "Contents"], BulletList contents] elementToListItem :: Element -> [Block] elementToListItem (Blk _) = [] @@ -227,66 +228,81 @@ orderedMarkers indent (start, style, delim) = _ -> orderedListMarkers (start, LowerAlpha, Period) else orderedListMarkers (start, style, delim) +blocksToRTF :: PandocMonad m + => Int + -> Alignment + -> [Block] + -> m String +blocksToRTF indent align = fmap concat . mapM (blockToRTF indent align) + -- | Convert Pandoc block element to RTF. -blockToRTF :: Int -- ^ indent level +blockToRTF :: PandocMonad m + => Int -- ^ indent level -> Alignment -- ^ alignment -> Block -- ^ block to convert - -> String -blockToRTF _ _ Null = "" + -> m String +blockToRTF _ _ Null = return "" blockToRTF indent alignment (Div _ bs) = - concatMap (blockToRTF indent alignment) bs + blocksToRTF indent alignment bs blockToRTF indent alignment (Plain lst) = - rtfCompact indent 0 alignment $ inlineListToRTF lst + rtfCompact indent 0 alignment <$> inlinesToRTF lst blockToRTF indent alignment (Para lst) = - rtfPar indent 0 alignment $ inlineListToRTF lst + rtfPar indent 0 alignment <$> inlinesToRTF lst blockToRTF indent alignment (LineBlock lns) = blockToRTF indent alignment $ linesToPara lns blockToRTF indent alignment (BlockQuote lst) = - concatMap (blockToRTF (indent + indentIncrement) alignment) lst + blocksToRTF (indent + indentIncrement) alignment lst blockToRTF indent _ (CodeBlock _ str) = - rtfPar indent 0 AlignLeft ("\\f1 " ++ (codeStringToRTF str)) + return $ rtfPar indent 0 AlignLeft ("\\f1 " ++ (codeStringToRTF str)) blockToRTF _ _ (RawBlock f str) - | f == Format "rtf" = str - | otherwise = "" -blockToRTF indent alignment (BulletList lst) = spaceAtEnd $ - concatMap (listItemToRTF alignment indent (bulletMarker indent)) lst -blockToRTF indent alignment (OrderedList attribs lst) = spaceAtEnd $ concat $ - zipWith (listItemToRTF alignment indent) (orderedMarkers indent attribs) lst -blockToRTF indent alignment (DefinitionList lst) = spaceAtEnd $ - concatMap (definitionListItemToRTF alignment indent) lst -blockToRTF indent _ HorizontalRule = + | f == Format "rtf" = return str + | otherwise = return "" +blockToRTF indent alignment (BulletList lst) = (spaceAtEnd . concat) <$> + mapM (listItemToRTF alignment indent (bulletMarker indent)) lst +blockToRTF indent alignment (OrderedList attribs lst) = + (spaceAtEnd . concat) <$> + mapM (\(x,y) -> listItemToRTF alignment indent x y) + (zip (orderedMarkers indent attribs) lst) +blockToRTF indent alignment (DefinitionList lst) = (spaceAtEnd . concat) <$> + mapM (definitionListItemToRTF alignment indent) lst +blockToRTF indent _ HorizontalRule = return $ rtfPar indent 0 AlignCenter "\\emdash\\emdash\\emdash\\emdash\\emdash" -blockToRTF indent alignment (Header level _ lst) = rtfPar indent 0 alignment $ - "\\b \\fs" ++ (show (40 - (level * 4))) ++ " " ++ inlineListToRTF lst -blockToRTF indent alignment (Table caption aligns sizes headers rows) = - (if all null headers - then "" - else tableRowToRTF True indent aligns sizes headers) ++ - concatMap (tableRowToRTF False indent aligns sizes) rows ++ - rtfPar indent 0 alignment (inlineListToRTF caption) +blockToRTF indent alignment (Header level _ lst) = do + contents <- inlinesToRTF lst + return $ rtfPar indent 0 alignment $ + "\\b \\fs" ++ (show (40 - (level * 4))) ++ " " ++ contents +blockToRTF indent alignment (Table caption aligns sizes headers rows) = do + caption' <- inlinesToRTF caption + header' <- if all null headers + then return "" + else tableRowToRTF True indent aligns sizes headers + rows' <- concat <$> mapM (tableRowToRTF False indent aligns sizes) rows + return $ header' ++ rows' ++ rtfPar indent 0 alignment caption' -tableRowToRTF :: Bool -> Int -> [Alignment] -> [Double] -> [[Block]] -> String -tableRowToRTF header indent aligns sizes' cols = +tableRowToRTF :: PandocMonad m + => Bool -> Int -> [Alignment] -> [Double] -> [[Block]] -> m String +tableRowToRTF header indent aligns sizes' cols = do let totalTwips = 6 * 1440 -- 6 inches - sizes = if all (== 0) sizes' + let sizes = if all (== 0) sizes' then take (length cols) $ repeat (1.0 / fromIntegral (length cols)) else sizes' - columns = concat $ zipWith (tableItemToRTF indent) aligns cols - rightEdges = tail $ scanl (\sofar new -> sofar + floor (new * totalTwips)) + columns <- concat <$> mapM (\(x,y) -> tableItemToRTF indent x y) + (zip aligns cols) + let rightEdges = tail $ scanl (\sofar new -> sofar + floor (new * totalTwips)) (0 :: Integer) sizes - cellDefs = map (\edge -> (if header + let cellDefs = map (\edge -> (if header then "\\clbrdrb\\brdrs" else "") ++ "\\cellx" ++ show edge) rightEdges - start = "{\n\\trowd \\trgaph120\n" ++ concat cellDefs ++ "\n" ++ + let start = "{\n\\trowd \\trgaph120\n" ++ concat cellDefs ++ "\n" ++ "\\trkeep\\intbl\n{\n" - end = "}\n\\intbl\\row}\n" - in start ++ columns ++ end + let end = "}\n\\intbl\\row}\n" + return $ start ++ columns ++ end -tableItemToRTF :: Int -> Alignment -> [Block] -> String -tableItemToRTF indent alignment item = - let contents = concatMap (blockToRTF indent alignment) item - in "{" ++ substitute "\\pard" "\\pard\\intbl" contents ++ "\\cell}\n" +tableItemToRTF :: PandocMonad m => Int -> Alignment -> [Block] -> m String +tableItemToRTF indent alignment item = do + contents <- blocksToRTF indent alignment item + return $ "{" ++ substitute "\\pard" "\\pard\\intbl" contents ++ "\\cell}\n" -- | Ensure that there's the same amount of space after compact -- lists as after regular lists. @@ -297,74 +313,93 @@ spaceAtEnd str = else str -- | Convert list item (list of blocks) to RTF. -listItemToRTF :: Alignment -- ^ alignment +listItemToRTF :: PandocMonad m + => Alignment -- ^ alignment -> Int -- ^ indent level -> String -- ^ list start marker -> [Block] -- ^ list item (list of blocks) - -> [Char] -listItemToRTF alignment indent marker [] = + -> m String +listItemToRTF alignment indent marker [] = return $ rtfCompact (indent + listIncrement) (0 - listIncrement) alignment (marker ++ "\\tx" ++ (show listIncrement) ++ "\\tab ") -listItemToRTF alignment indent marker list = - let (first:rest) = map (blockToRTF (indent + listIncrement) alignment) list - listMarker = "\\fi" ++ show (0 - listIncrement) ++ " " ++ marker ++ "\\tx" ++ - show listIncrement ++ "\\tab" - insertListMarker ('\\':'f':'i':'-':d:xs) | isDigit d = +listItemToRTF alignment indent marker list = do + (first:rest) <- mapM (blockToRTF (indent + listIncrement) alignment) list + let listMarker = "\\fi" ++ show (0 - listIncrement) ++ " " ++ marker ++ + "\\tx" ++ show listIncrement ++ "\\tab" + let insertListMarker ('\\':'f':'i':'-':d:xs) | isDigit d = listMarker ++ dropWhile isDigit xs insertListMarker ('\\':'f':'i':d:xs) | isDigit d = listMarker ++ dropWhile isDigit xs insertListMarker (x:xs) = x : insertListMarker xs insertListMarker [] = [] - -- insert the list marker into the (processed) first block - in insertListMarker first ++ concat rest + -- insert the list marker into the (processed) first block + return $ insertListMarker first ++ concat rest -- | Convert definition list item (label, list of blocks) to RTF. -definitionListItemToRTF :: Alignment -- ^ alignment +definitionListItemToRTF :: PandocMonad m + => Alignment -- ^ alignment -> Int -- ^ indent level -> ([Inline],[[Block]]) -- ^ list item (list of blocks) - -> [Char] -definitionListItemToRTF alignment indent (label, defs) = - let labelText = blockToRTF indent alignment (Plain label) - itemsText = concatMap (blockToRTF (indent + listIncrement) alignment) $ - concat defs - in labelText ++ itemsText + -> m String +definitionListItemToRTF alignment indent (label, defs) = do + labelText <- blockToRTF indent alignment (Plain label) + itemsText <- blocksToRTF (indent + listIncrement) alignment (concat defs) + return $ labelText ++ itemsText -- | Convert list of inline items to RTF. -inlineListToRTF :: [Inline] -- ^ list of inlines to convert - -> String -inlineListToRTF lst = concatMap inlineToRTF lst +inlinesToRTF :: PandocMonad m + => [Inline] -- ^ list of inlines to convert + -> m String +inlinesToRTF lst = concat <$> mapM inlineToRTF lst -- | Convert inline item to RTF. -inlineToRTF :: Inline -- ^ inline to convert - -> String -inlineToRTF (Span _ lst) = inlineListToRTF lst -inlineToRTF (Emph lst) = "{\\i " ++ (inlineListToRTF lst) ++ "}" -inlineToRTF (Strong lst) = "{\\b " ++ (inlineListToRTF lst) ++ "}" -inlineToRTF (Strikeout lst) = "{\\strike " ++ (inlineListToRTF lst) ++ "}" -inlineToRTF (Superscript lst) = "{\\super " ++ (inlineListToRTF lst) ++ "}" -inlineToRTF (Subscript lst) = "{\\sub " ++ (inlineListToRTF lst) ++ "}" -inlineToRTF (SmallCaps lst) = "{\\scaps " ++ (inlineListToRTF lst) ++ "}" -inlineToRTF (Quoted SingleQuote lst) = - "\\u8216'" ++ (inlineListToRTF lst) ++ "\\u8217'" -inlineToRTF (Quoted DoubleQuote lst) = - "\\u8220\"" ++ (inlineListToRTF lst) ++ "\\u8221\"" -inlineToRTF (Code _ str) = "{\\f1 " ++ (codeStringToRTF str) ++ "}" -inlineToRTF (Str str) = stringToRTF str -inlineToRTF (Math t str) = inlineListToRTF $ texMathToInlines t str -inlineToRTF (Cite _ lst) = inlineListToRTF lst +inlineToRTF :: PandocMonad m + => Inline -- ^ inline to convert + -> m String +inlineToRTF (Span _ lst) = inlinesToRTF lst +inlineToRTF (Emph lst) = do + contents <- inlinesToRTF lst + return $ "{\\i " ++ contents ++ "}" +inlineToRTF (Strong lst) = do + contents <- inlinesToRTF lst + return $ "{\\b " ++ contents ++ "}" +inlineToRTF (Strikeout lst) = do + contents <- inlinesToRTF lst + return $ "{\\strike " ++ contents ++ "}" +inlineToRTF (Superscript lst) = do + contents <- inlinesToRTF lst + return $ "{\\super " ++ contents ++ "}" +inlineToRTF (Subscript lst) = do + contents <- inlinesToRTF lst + return $ "{\\sub " ++ contents ++ "}" +inlineToRTF (SmallCaps lst) = do + contents <- inlinesToRTF lst + return $ "{\\scaps " ++ contents ++ "}" +inlineToRTF (Quoted SingleQuote lst) = do + contents <- inlinesToRTF lst + return $ "\\u8216'" ++ contents ++ "\\u8217'" +inlineToRTF (Quoted DoubleQuote lst) = do + contents <- inlinesToRTF lst + return $ "\\u8220\"" ++ contents ++ "\\u8221\"" +inlineToRTF (Code _ str) = return $ "{\\f1 " ++ (codeStringToRTF str) ++ "}" +inlineToRTF (Str str) = return $ stringToRTF str +inlineToRTF (Math t str) = texMathToInlines t str >>= inlinesToRTF +inlineToRTF (Cite _ lst) = inlinesToRTF lst inlineToRTF (RawInline f str) - | f == Format "rtf" = str - | otherwise = "" -inlineToRTF LineBreak = "\\line " -inlineToRTF SoftBreak = " " -inlineToRTF PageBreak = "\\page " -inlineToRTF Space = " " -inlineToRTF (Link _ text (src, _)) = - "{\\field{\\*\\fldinst{HYPERLINK \"" ++ (codeStringToRTF src) ++ - "\"}}{\\fldrslt{\\ul\n" ++ (inlineListToRTF text) ++ "\n}}}\n" + | f == Format "rtf" = return str + | otherwise = return "" +inlineToRTF (LineBreak) = return "\\line " +inlineToRTF SoftBreak = return " " +inlineToRTF PageBreak = return "\\page " +inlineToRTF Space = return " " +inlineToRTF (Link _ text (src, _)) = do + contents <- inlinesToRTF text + return $ "{\\field{\\*\\fldinst{HYPERLINK \"" ++ (codeStringToRTF src) ++ + "\"}}{\\fldrslt{\\ul\n" ++ contents ++ "\n}}}\n" inlineToRTF (Image _ _ (source, _)) = - "{\\cf1 [image: " ++ source ++ "]\\cf0}" -inlineToRTF (Note contents) = - "{\\super\\chftn}{\\*\\footnote\\chftn\\~\\plain\\pard " ++ - (concatMap (blockToRTF 0 AlignDefault) contents) ++ "}" + return $ "{\\cf1 [image: " ++ source ++ "]\\cf0}" +inlineToRTF (Note contents) = do + body <- concat <$> mapM (blockToRTF 0 AlignDefault) contents + return $ "{\\super\\chftn}{\\*\\footnote\\chftn\\~\\plain\\pard " ++ + body ++ "}" -- cgit v1.2.3 From 29b3975cbec5d393e404f96e5f68506587ee74de Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 3 Dec 2016 16:30:47 +0100 Subject: Make sure texMathToInlines issues warning. --- src/Text/Pandoc/Writers/Math.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Math.hs b/src/Text/Pandoc/Writers/Math.hs index 4540a2479..a7fe6d648 100644 --- a/src/Text/Pandoc/Writers/Math.hs +++ b/src/Text/Pandoc/Writers/Math.hs @@ -19,7 +19,9 @@ texMathToInlines mt inp = do res <- convertMath writePandoc mt inp case res of Right (Just ils) -> return ils - Right (Nothing) -> return [mkFallback mt inp] + Right (Nothing) -> do + warn $ "Could not render TeX math as unicode, rendering as raw TeX:\n" ++ inp + return [mkFallback mt inp] Left il -> return [il] mkFallback :: MathType -> String -> Inline -- cgit v1.2.3 From 5ab8909661242d992726411d6adc6490eacaafe3 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 3 Dec 2016 16:57:23 +0100 Subject: New withWarningsToStderr exported from Text.Pandoc.Class. And use this in pandoc.hs so that messages actually get printed. --- pandoc.hs | 17 ++++++++++++----- src/Text/Pandoc/Class.hs | 12 +++++++++++- 2 files changed, 23 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/pandoc.hs b/pandoc.hs index 3d1472bac..ffa9de47d 100644 --- a/pandoc.hs +++ b/pandoc.hs @@ -77,7 +77,7 @@ import System.Posix.Terminal (queryTerminal) import System.Posix.IO (stdOutput) #endif import Control.Monad.Trans -import Text.Pandoc.Class (withMediaBag, PandocIO) +import Text.Pandoc.Class (withMediaBag, PandocIO, withWarningsToStderr) type Transform = Pandoc -> Pandoc @@ -1414,10 +1414,14 @@ convertWithOpts opts args = do srcs <- convertTabs . intercalate "\n" <$> readSources sources' doc <- handleIncludes' srcs case doc of - Right doc' -> runIOorExplode $ withMediaBag $ r readerOpts doc' + Right doc' -> runIOorExplode $ withMediaBag + $ withWarningsToStderr + $ r readerOpts doc' Left e -> error $ show e ByteStringReader r -> readFiles sources' >>= - (\bs -> runIOorExplode $ withMediaBag $ r readerOpts bs) + (\bs -> runIOorExplode $ withMediaBag + $ withWarningsToStderr + $ r readerOpts bs) -- We parse first if (1) fileScope is set, (2), it's a binary -- reader, or (3) we're reading JSON. This is easier to do of an AND @@ -1489,7 +1493,9 @@ convertWithOpts opts args = do case writer of -- StringWriter f -> f writerOptions doc' >>= writerFn outputFile - ByteStringWriter f -> (runIOorExplode $ f writerOptions doc') >>= writeFnBinary outputFile + ByteStringWriter f -> (runIOorExplode $ withWarningsToStderr + $ f writerOptions doc') + >>= writeFnBinary outputFile StringWriter f | pdfOutput -> do -- make sure writer is latex or beamer or context or html5 @@ -1523,5 +1529,6 @@ convertWithOpts opts args = do handleEntities = if htmlFormat && ascii then toEntities else id - output <- runIOorExplode $ f writerOptions doc' + output <- runIOorExplode $ withWarningsToStderr + $ f writerOptions doc' selfcontain (output ++ ['\n' | not standalone']) >>= writerFn outputFile . handleEntities diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 0881878ed..1059f5324 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -51,6 +51,7 @@ module Text.Pandoc.Class ( PandocMonad(..) , runIOorExplode , runPure , withMediaBag + , withWarningsToStderr ) where import Prelude hiding (readFile, fail) @@ -64,7 +65,8 @@ import qualified Text.Pandoc.Shared as IO ( fetchItem , fetchItem' , getDefaultReferenceDocx , getDefaultReferenceODT - , readDataFile) + , readDataFile + , warn) import Text.Pandoc.Compat.Time (UTCTime) import Text.Pandoc.Parsing (ParserT, ParserState, SourcePos) import qualified Text.Pandoc.Compat.Time as IO (getCurrentTime) @@ -119,6 +121,8 @@ class (Functor m, Applicative m, Monad m, MonadError PandocError m, MonadState C -- Functions defined for all PandocMonad instances +-- TODO should we rename this to avoid conflict with the like-named +-- function from Shared? Perhaps "addWarning"? warn :: PandocMonad m => String -> m () warn msg = modify $ \st -> st{stWarnings = msg : stWarnings st} @@ -183,6 +187,12 @@ runIO ma = flip evalStateT def $ runExceptT $ unPandocIO ma withMediaBag :: PandocMonad m => m a -> m (a, MediaBag) withMediaBag ma = ((,)) <$> ma <*> getMediaBag +withWarningsToStderr :: PandocIO a -> PandocIO a +withWarningsToStderr f = do + x <- f + getWarnings >>= mapM_ IO.warn + return x + runIOorExplode :: PandocIO a -> IO a runIOorExplode ma = handleError <$> runIO ma -- cgit v1.2.3 From 2710fc426130738715fdf1ac6dd0c111a5ac8340 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 3 Dec 2016 17:10:50 +0100 Subject: Class: Renamed 'warn' to 'addWarning' and consolidated RTF writer. * Renaming Text.Pandoc.Class.warn to addWarning avoids conflict with Text.Pandoc.Shared.warn. * Removed writeRTFWithEmbeddedImages from Text.Pandoc.Writers.RTF. This is no longer needed; we automatically handle embedded images using the PandocM functions. [API change] --- src/Text/Pandoc.hs | 3 +-- src/Text/Pandoc/Class.hs | 12 +++++------- src/Text/Pandoc/Readers/Docx.hs | 4 ++-- src/Text/Pandoc/Readers/RST.hs | 10 +++++----- src/Text/Pandoc/Readers/TWiki.hs | 2 +- src/Text/Pandoc/Writers/Docx.hs | 2 +- src/Text/Pandoc/Writers/EPUB.hs | 4 ++-- src/Text/Pandoc/Writers/ICML.hs | 4 ++-- src/Text/Pandoc/Writers/Math.hs | 4 ++-- src/Text/Pandoc/Writers/ODT.hs | 4 ++-- src/Text/Pandoc/Writers/RTF.hs | 34 +++++++++++++++++++--------------- 11 files changed, 42 insertions(+), 41 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index 036d3cdf5..3c3a79bb7 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -322,8 +322,7 @@ writers = [ ,("dokuwiki" , StringWriter writeDokuWiki) ,("zimwiki" , StringWriter writeZimWiki) ,("textile" , StringWriter writeTextile) - ,("rtf" , StringWriter $ \o -> - writeRTFWithEmbeddedImages o) + ,("rtf" , StringWriter writeRTF) ,("org" , StringWriter writeOrg) ,("asciidoc" , StringWriter writeAsciiDoc) ,("haddock" , StringWriter writeHaddock) diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 1059f5324..3337de40a 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -36,14 +36,14 @@ module Text.Pandoc.Class ( PandocMonad(..) , PureState(..) , getPOSIXTime , getZonedTime - , warn + , addWarning + , addWarningWithPos , getWarnings , getMediaBag , setMediaBag , insertMedia , getInputFiles , getOutputFile - , addWarningWithPos , PandocIO(..) , PandocPure(..) , FileInfo(..) @@ -121,10 +121,8 @@ class (Functor m, Applicative m, Monad m, MonadError PandocError m, MonadState C -- Functions defined for all PandocMonad instances --- TODO should we rename this to avoid conflict with the like-named --- function from Shared? Perhaps "addWarning"? -warn :: PandocMonad m => String -> m () -warn msg = modify $ \st -> st{stWarnings = msg : stWarnings st} +addWarning :: PandocMonad m => String -> m () +addWarning msg = modify $ \st -> st{stWarnings = msg : stWarnings st} getWarnings :: PandocMonad m => m [String] getWarnings = gets stWarnings @@ -160,7 +158,7 @@ addWarningWithPos :: PandocMonad m -> ParserT [Char] ParserState m () addWarningWithPos mbpos msg = lift $ - warn $ + addWarning $ msg ++ maybe "" (\pos -> " " ++ show pos) mbpos -- diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index 37fe5c532..16542fd1f 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -108,7 +108,7 @@ readDocx :: PandocMonad m readDocx opts bytes | Right archive <- toArchiveOrFail bytes , Right (docx, parserWarnings) <- archiveToDocxWithWarnings archive = do - mapM_ P.warn parserWarnings + mapM_ P.addWarning parserWarnings (meta, blks) <- docxToOutput opts docx return $ Pandoc meta blks readDocx _ _ = @@ -334,7 +334,7 @@ blocksToInlinesWarn cmtId blks = do notParaOrPlain (Plain _) = False notParaOrPlain _ = True when (not $ null $ filter notParaOrPlain blkList) - ((lift . lift) $ P.warn $ "Docx comment " ++ cmtId ++ " will not retain formatting") + ((lift . lift) $ P.addWarning $ "Docx comment " ++ cmtId ++ " will not retain formatting") return $ fromList $ blocksToInlines blkList parPartToInlines :: PandocMonad m => ParPart -> DocxContext m Inlines diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 078d2963c..df6a8114b 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -654,20 +654,20 @@ addNewRole roleString fields = do -- warn about syntax we ignore flip mapM_ fields $ \(key, _) -> case key of - "language" -> when (baseRole /= "code") $ lift $ P.warn $ + "language" -> when (baseRole /= "code") $ lift $ P.addWarning $ "ignoring :language: field because the parent of role :" ++ role ++ ": is :" ++ baseRole ++ ": not :code:" - "format" -> when (baseRole /= "raw") $ lift $ P.warn $ + "format" -> when (baseRole /= "raw") $ lift $ P.addWarning $ "ignoring :format: field because the parent of role :" ++ role ++ ": is :" ++ baseRole ++ ": not :raw:" - _ -> lift $ P.warn $ "ignoring unknown field :" ++ key ++ + _ -> lift $ P.addWarning $ "ignoring unknown field :" ++ key ++ ": in definition of role :" ++ role ++ ": in" when (parentRole == "raw" && countKeys "format" > 1) $ - lift $ P.warn $ + lift $ P.addWarning $ "ignoring :format: fields after the first in the definition of role :" ++ role ++": in" when (parentRole == "code" && countKeys "language" > 1) $ - lift $ P.warn $ + lift $ P.addWarning $ "ignoring :language: fields after the first in the definition of role :" ++ role ++": in" diff --git a/src/Text/Pandoc/Readers/TWiki.hs b/src/Text/Pandoc/Readers/TWiki.hs index b2b136f39..cc4f8f39c 100644 --- a/src/Text/Pandoc/Readers/TWiki.hs +++ b/src/Text/Pandoc/Readers/TWiki.hs @@ -58,7 +58,7 @@ readTWiki :: PandocMonad m -> m Pandoc readTWiki opts s = case readTWikiWithWarnings' opts s of Right (doc, warns) -> do - mapM_ P.warn warns + mapM_ P.addWarning warns return doc Left e -> throwError e diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 90261dede..3b1df6bd9 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -1182,7 +1182,7 @@ inlineToOpenXML' opts (Image attr alt (src, title)) = do res <- (lift . lift) $ P.fetchItem' (writerMediaBag opts) (writerSourceURL opts) src case res of Left (_ :: E.SomeException) -> do - (lift . lift) $ P.warn ("Could not find image `" ++ src ++ "', skipping...") + (lift . lift) $ P.addWarning ("Could not find image `" ++ src ++ "', skipping...") -- emit alt text inlinesToOpenXML opts alt Right (img, mt) -> do diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index e41aa96ad..b1266c4c9 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -398,7 +398,7 @@ pandocToEPUB opts doc@(Pandoc meta _) = do let matchingGlob f = do xs <- lift $ P.glob f when (null xs) $ - lift $ P.warn $ f ++ " did not match any font files." + lift $ P.addWarning $ f ++ " did not match any font files." return xs let mkFontEntry f = mkEntry (takeFileName f) `fmap` (lift $ P.readFileLazy f) fontFiles <- concat <$> mapM matchingGlob (writerEpubFonts opts') @@ -864,7 +864,7 @@ modifyMediaRef opts oldsrc = do (new, mbEntry) <- case res of Left _ -> do - lift $ P.warn $ "Could not find media `" ++ oldsrc ++ "', skipping..." + lift $ P.addWarning $ "Could not find media `" ++ oldsrc ++ "', skipping..." return (oldsrc, Nothing) Right (img,mbMime) -> do let new = "media/file" ++ show (length media) ++ diff --git a/src/Text/Pandoc/Writers/ICML.hs b/src/Text/Pandoc/Writers/ICML.hs index 7c42671f1..482e20f4b 100644 --- a/src/Text/Pandoc/Writers/ICML.hs +++ b/src/Text/Pandoc/Writers/ICML.hs @@ -537,13 +537,13 @@ imageICML opts style attr (src, _) = do res <- lift $ P.fetchItem (writerSourceURL opts) src imgS <- case res of Left (_) -> do - lift $ P.warn $ "Could not find image `" ++ src ++ "', skipping..." + lift $ P.addWarning $ "Could not find image `" ++ src ++ "', skipping..." return def Right (img, _) -> do case imageSize img of Right size -> return size Left msg -> do - lift $ P.warn $ "Could not determine image size in `" ++ + lift $ P.addWarning $ "Could not determine image size in `" ++ src ++ "': " ++ msg return def let (ow, oh) = sizeInPoints imgS diff --git a/src/Text/Pandoc/Writers/Math.hs b/src/Text/Pandoc/Writers/Math.hs index a7fe6d648..552db8b32 100644 --- a/src/Text/Pandoc/Writers/Math.hs +++ b/src/Text/Pandoc/Writers/Math.hs @@ -20,7 +20,7 @@ texMathToInlines mt inp = do case res of Right (Just ils) -> return ils Right (Nothing) -> do - warn $ "Could not render TeX math as unicode, rendering as raw TeX:\n" ++ inp + addWarning $ "Could not render TeX math as unicode, rendering as raw TeX:\n" ++ inp return [mkFallback mt inp] Left il -> return [il] @@ -40,7 +40,7 @@ convertMath writer mt str = do case writer dt <$> readTeX str of Right r -> return (Right r) Left e -> do - warn $ "Could not convert TeX math, rendering as raw TeX:\n" ++ + addWarning $ "Could not convert TeX math, rendering as raw TeX:\n" ++ str ++ "\n" ++ e return (Left $ mkFallback mt str) where dt = case mt of diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index 02e84e26e..db9090e29 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -147,13 +147,13 @@ transformPicMath opts (Image attr@(id', cls, _) lab (src,t)) = do res <- lift $ P.fetchItem' (writerMediaBag opts) (writerSourceURL opts) src case res of Left (_ :: E.SomeException) -> do - lift $ P.warn $ "Could not find image `" ++ src ++ "', skipping..." + lift $ P.addWarning $ "Could not find image `" ++ src ++ "', skipping..." return $ Emph lab Right (img, mbMimeType) -> do (ptX, ptY) <- case imageSize img of Right s -> return $ sizeInPoints s Left msg -> do - lift $ P.warn $ "Could not determine image size in `" ++ + lift $ P.addWarning $ "Could not determine image size in `" ++ src ++ "': " ++ msg return (100, 100) let dims = diff --git a/src/Text/Pandoc/Writers/RTF.hs b/src/Text/Pandoc/Writers/RTF.hs index f71c97334..32f70cb31 100644 --- a/src/Text/Pandoc/Writers/RTF.hs +++ b/src/Text/Pandoc/Writers/RTF.hs @@ -28,7 +28,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Conversion of 'Pandoc' documents to RTF (rich text format). -} module Text.Pandoc.Writers.RTF ( writeRTF - , writeRTFWithEmbeddedImages ) where import Text.Pandoc.Definition import Text.Pandoc.Options @@ -37,6 +36,7 @@ import Text.Pandoc.Writers.Shared import Text.Pandoc.Writers.Math import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Walk +import Text.Pandoc.Class (addWarning) import Data.List ( isSuffixOf, intercalate ) import Data.Char ( ord, chr, isDigit ) import qualified Data.ByteString as B @@ -64,7 +64,7 @@ rtfEmbedImage opts x@(Image attr _ (src,_)) = do _ -> throwError $ PandocSomeError "Unknown file type" sizeSpec <- case imageSize imgdata of Left msg -> do - P.warn $ "Could not determine image size in `" ++ + addWarning $ "Could not determine image size in `" ++ src ++ "': " ++ msg return "" Right sz -> return $ "\\picw" ++ show xpx ++ @@ -76,23 +76,27 @@ rtfEmbedImage opts x@(Image attr _ (src,_)) = do (xpt, ypt) = desiredSizeInPoints opts attr sz let raw = "{\\pict" ++ filetype ++ sizeSpec ++ "\\bin " ++ concat bytes ++ "}" - return $ if B.null imgdata - then x - else RawInline (Format "rtf") raw - _ -> return x + if B.null imgdata + then do + addWarning $ "Image " ++ src ++ " contained no data, skipping." + return x + else return $ RawInline (Format "rtf") raw + | otherwise -> do + addWarning $ "Image " ++ src ++ " is not a jpeg or png, skipping." + return x + Right (_, Nothing) -> do + addWarning $ "Could not determine image type for " ++ src ++ ", skipping." + return x + Left e -> do + addWarning $ "Could not fetch image " ++ src ++ "\n" ++ show e + return x rtfEmbedImage _ x = return x --- | Convert Pandoc to a string in rich text format, with --- images embedded as encoded binary data. TODO get rid of this, --- we don't need it now that we have writeRTF in PandocMonad. -writeRTFWithEmbeddedImages :: PandocMonad m - => WriterOptions -> Pandoc -> m String -writeRTFWithEmbeddedImages options doc = - writeRTF options =<< walkM (rtfEmbedImage options) doc - -- | Convert Pandoc to a string in rich text format. writeRTF :: PandocMonad m => WriterOptions -> Pandoc -> m String -writeRTF options (Pandoc meta@(Meta metamap) blocks) = do +writeRTF options doc = do + -- handle images + Pandoc meta@(Meta metamap) blocks <- walkM (rtfEmbedImage options) doc let spacer = not $ all null $ docTitle meta : docDate meta : docAuthors meta let toPlain (MetaBlocks [Para ils]) = MetaInlines ils toPlain x = x -- cgit v1.2.3 From a4bd650277ac8fd2c952f2330e4d23a200d691a5 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 3 Dec 2016 18:42:17 +0100 Subject: Class: rename addWarning[WithPos] to warning[WithPos]. There's already a function addWarning in Parsing! Maybe we can dispense with that now, but I still like 'warning' better as a name. --- src/Text/Pandoc/Class.hs | 22 ++++++++++------------ src/Text/Pandoc/Readers/Docx.hs | 4 ++-- src/Text/Pandoc/Readers/Markdown.hs | 10 +++++----- src/Text/Pandoc/Readers/RST.hs | 14 +++++++------- src/Text/Pandoc/Readers/TWiki.hs | 2 +- src/Text/Pandoc/Writers/Docx.hs | 2 +- src/Text/Pandoc/Writers/EPUB.hs | 4 ++-- src/Text/Pandoc/Writers/ICML.hs | 4 ++-- src/Text/Pandoc/Writers/Math.hs | 4 ++-- src/Text/Pandoc/Writers/ODT.hs | 4 ++-- src/Text/Pandoc/Writers/RTF.hs | 12 ++++++------ 11 files changed, 40 insertions(+), 42 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 3337de40a..7227742b2 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -36,8 +36,8 @@ module Text.Pandoc.Class ( PandocMonad(..) , PureState(..) , getPOSIXTime , getZonedTime - , addWarning - , addWarningWithPos + , warning + , warningWithPos , getWarnings , getMediaBag , setMediaBag @@ -121,8 +121,8 @@ class (Functor m, Applicative m, Monad m, MonadError PandocError m, MonadState C -- Functions defined for all PandocMonad instances -addWarning :: PandocMonad m => String -> m () -addWarning msg = modify $ \st -> st{stWarnings = msg : stWarnings st} +warning :: PandocMonad m => String -> m () +warning msg = modify $ \st -> st{stWarnings = msg : stWarnings st} getWarnings :: PandocMonad m => m [String] getWarnings = gets stWarnings @@ -152,14 +152,12 @@ getZonedTime = do tz <- getCurrentTimeZone return $ utcToZonedTime tz t -addWarningWithPos :: PandocMonad m - => Maybe SourcePos - -> String - -> ParserT [Char] ParserState m () -addWarningWithPos mbpos msg = - lift $ - addWarning $ - msg ++ maybe "" (\pos -> " " ++ show pos) mbpos +warningWithPos :: PandocMonad m + => Maybe SourcePos + -> String + -> ParserT [Char] ParserState m () +warningWithPos mbpos msg = + lift $ warning $ msg ++ maybe "" (\pos -> " " ++ show pos) mbpos -- diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index 16542fd1f..490fdf878 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -108,7 +108,7 @@ readDocx :: PandocMonad m readDocx opts bytes | Right archive <- toArchiveOrFail bytes , Right (docx, parserWarnings) <- archiveToDocxWithWarnings archive = do - mapM_ P.addWarning parserWarnings + mapM_ P.warning parserWarnings (meta, blks) <- docxToOutput opts docx return $ Pandoc meta blks readDocx _ _ = @@ -334,7 +334,7 @@ blocksToInlinesWarn cmtId blks = do notParaOrPlain (Plain _) = False notParaOrPlain _ = True when (not $ null $ filter notParaOrPlain blkList) - ((lift . lift) $ P.addWarning $ "Docx comment " ++ cmtId ++ " will not retain formatting") + ((lift . lift) $ P.warning $ "Docx comment " ++ cmtId ++ " will not retain formatting") return $ fromList $ blocksToInlines blkList parPartToInlines :: PandocMonad m => ParPart -> DocxContext m Inlines diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index b59e5a5f1..012edfe3b 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -280,7 +280,7 @@ yamlMetaBlock = try $ do ) nullMeta hashmap Right Yaml.Null -> return nullMeta Right _ -> do - P.addWarningWithPos (Just pos) "YAML header is not an object" + P.warningWithPos (Just pos) "YAML header is not an object" return nullMeta Left err' -> do case err' of @@ -291,13 +291,13 @@ yamlMetaBlock = try $ do yamlLine = yline , yamlColumn = ycol }}) -> - P.addWarningWithPos (Just $ setSourceLine + P.warningWithPos (Just $ setSourceLine (setSourceColumn pos (sourceColumn pos + ycol)) (sourceLine pos + 1 + yline)) $ "Could not parse YAML header: " ++ problem - _ -> P.addWarningWithPos (Just pos) + _ -> P.warningWithPos (Just pos) $ "Could not parse YAML header: " ++ show err' return nullMeta @@ -420,7 +420,7 @@ referenceKey = try $ do let oldkeys = stateKeys st let key = toKey raw case M.lookup key oldkeys of - Just _ -> P.addWarningWithPos (Just pos) $ "Duplicate link reference `" ++ raw ++ "'" + Just _ -> P.warningWithPos (Just pos) $ "Duplicate link reference `" ++ raw ++ "'" Nothing -> return () updateState $ \s -> s { stateKeys = M.insert key (target, attr') oldkeys } return $ return mempty @@ -486,7 +486,7 @@ noteBlock = try $ do let newnote = (ref, parsed) oldnotes <- stateNotes' <$> getState case lookup ref oldnotes of - Just _ -> P.addWarningWithPos (Just pos) $ "Duplicate note reference `" ++ ref ++ "'" + Just _ -> P.warningWithPos (Just pos) $ "Duplicate note reference `" ++ ref ++ "'" Nothing -> return () updateState $ \s -> s { stateNotes' = newnote : oldnotes } return mempty diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index df6a8114b..5e8aa20f5 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -626,7 +626,7 @@ directive' = do return $ B.divWith attrs children other -> do pos <- getPosition - P.addWarningWithPos (Just pos) $ "ignoring unknown directive: " ++ other + P.warningWithPos (Just pos) $ "ignoring unknown directive: " ++ other return mempty -- TODO: @@ -654,20 +654,20 @@ addNewRole roleString fields = do -- warn about syntax we ignore flip mapM_ fields $ \(key, _) -> case key of - "language" -> when (baseRole /= "code") $ lift $ P.addWarning $ + "language" -> when (baseRole /= "code") $ lift $ P.warning $ "ignoring :language: field because the parent of role :" ++ role ++ ": is :" ++ baseRole ++ ": not :code:" - "format" -> when (baseRole /= "raw") $ lift $ P.addWarning $ + "format" -> when (baseRole /= "raw") $ lift $ P.warning $ "ignoring :format: field because the parent of role :" ++ role ++ ": is :" ++ baseRole ++ ": not :raw:" - _ -> lift $ P.addWarning $ "ignoring unknown field :" ++ key ++ + _ -> lift $ P.warning $ "ignoring unknown field :" ++ key ++ ": in definition of role :" ++ role ++ ": in" when (parentRole == "raw" && countKeys "format" > 1) $ - lift $ P.addWarning $ + lift $ P.warning $ "ignoring :format: fields after the first in the definition of role :" ++ role ++": in" when (parentRole == "code" && countKeys "language" > 1) $ - lift $ P.addWarning $ + lift $ P.warning $ "ignoring :language: fields after the first in the definition of role :" ++ role ++": in" @@ -1065,7 +1065,7 @@ renderRole contents fmt role attr = case role of renderRole contents newFmt newRole newAttr Nothing -> do pos <- getPosition - P.addWarningWithPos (Just pos) $ "ignoring unknown role :" ++ custom ++ ": in" + P.warningWithPos (Just pos) $ "ignoring unknown role :" ++ custom ++ ": in" return $ B.str contents -- Undefined role where titleRef ref = return $ B.str ref -- FIXME: Not a sensible behaviour diff --git a/src/Text/Pandoc/Readers/TWiki.hs b/src/Text/Pandoc/Readers/TWiki.hs index cc4f8f39c..b4546883b 100644 --- a/src/Text/Pandoc/Readers/TWiki.hs +++ b/src/Text/Pandoc/Readers/TWiki.hs @@ -58,7 +58,7 @@ readTWiki :: PandocMonad m -> m Pandoc readTWiki opts s = case readTWikiWithWarnings' opts s of Right (doc, warns) -> do - mapM_ P.addWarning warns + mapM_ P.warning warns return doc Left e -> throwError e diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 3b1df6bd9..0f040d19b 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -1182,7 +1182,7 @@ inlineToOpenXML' opts (Image attr alt (src, title)) = do res <- (lift . lift) $ P.fetchItem' (writerMediaBag opts) (writerSourceURL opts) src case res of Left (_ :: E.SomeException) -> do - (lift . lift) $ P.addWarning ("Could not find image `" ++ src ++ "', skipping...") + (lift . lift) $ P.warning ("Could not find image `" ++ src ++ "', skipping...") -- emit alt text inlinesToOpenXML opts alt Right (img, mt) -> do diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index b1266c4c9..1c3a44207 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -398,7 +398,7 @@ pandocToEPUB opts doc@(Pandoc meta _) = do let matchingGlob f = do xs <- lift $ P.glob f when (null xs) $ - lift $ P.addWarning $ f ++ " did not match any font files." + lift $ P.warning $ f ++ " did not match any font files." return xs let mkFontEntry f = mkEntry (takeFileName f) `fmap` (lift $ P.readFileLazy f) fontFiles <- concat <$> mapM matchingGlob (writerEpubFonts opts') @@ -864,7 +864,7 @@ modifyMediaRef opts oldsrc = do (new, mbEntry) <- case res of Left _ -> do - lift $ P.addWarning $ "Could not find media `" ++ oldsrc ++ "', skipping..." + lift $ P.warning $ "Could not find media `" ++ oldsrc ++ "', skipping..." return (oldsrc, Nothing) Right (img,mbMime) -> do let new = "media/file" ++ show (length media) ++ diff --git a/src/Text/Pandoc/Writers/ICML.hs b/src/Text/Pandoc/Writers/ICML.hs index 482e20f4b..6bc7436d8 100644 --- a/src/Text/Pandoc/Writers/ICML.hs +++ b/src/Text/Pandoc/Writers/ICML.hs @@ -537,13 +537,13 @@ imageICML opts style attr (src, _) = do res <- lift $ P.fetchItem (writerSourceURL opts) src imgS <- case res of Left (_) -> do - lift $ P.addWarning $ "Could not find image `" ++ src ++ "', skipping..." + lift $ P.warning $ "Could not find image `" ++ src ++ "', skipping..." return def Right (img, _) -> do case imageSize img of Right size -> return size Left msg -> do - lift $ P.addWarning $ "Could not determine image size in `" ++ + lift $ P.warning $ "Could not determine image size in `" ++ src ++ "': " ++ msg return def let (ow, oh) = sizeInPoints imgS diff --git a/src/Text/Pandoc/Writers/Math.hs b/src/Text/Pandoc/Writers/Math.hs index 552db8b32..b959ce972 100644 --- a/src/Text/Pandoc/Writers/Math.hs +++ b/src/Text/Pandoc/Writers/Math.hs @@ -20,7 +20,7 @@ texMathToInlines mt inp = do case res of Right (Just ils) -> return ils Right (Nothing) -> do - addWarning $ "Could not render TeX math as unicode, rendering as raw TeX:\n" ++ inp + warning $ "Could not render TeX math as unicode, rendering as raw TeX:\n" ++ inp return [mkFallback mt inp] Left il -> return [il] @@ -40,7 +40,7 @@ convertMath writer mt str = do case writer dt <$> readTeX str of Right r -> return (Right r) Left e -> do - addWarning $ "Could not convert TeX math, rendering as raw TeX:\n" ++ + warning $ "Could not convert TeX math, rendering as raw TeX:\n" ++ str ++ "\n" ++ e return (Left $ mkFallback mt str) where dt = case mt of diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index db9090e29..b17b18a21 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -147,13 +147,13 @@ transformPicMath opts (Image attr@(id', cls, _) lab (src,t)) = do res <- lift $ P.fetchItem' (writerMediaBag opts) (writerSourceURL opts) src case res of Left (_ :: E.SomeException) -> do - lift $ P.addWarning $ "Could not find image `" ++ src ++ "', skipping..." + lift $ P.warning $ "Could not find image `" ++ src ++ "', skipping..." return $ Emph lab Right (img, mbMimeType) -> do (ptX, ptY) <- case imageSize img of Right s -> return $ sizeInPoints s Left msg -> do - lift $ P.addWarning $ "Could not determine image size in `" ++ + lift $ P.warning $ "Could not determine image size in `" ++ src ++ "': " ++ msg return (100, 100) let dims = diff --git a/src/Text/Pandoc/Writers/RTF.hs b/src/Text/Pandoc/Writers/RTF.hs index 32f70cb31..a3351a705 100644 --- a/src/Text/Pandoc/Writers/RTF.hs +++ b/src/Text/Pandoc/Writers/RTF.hs @@ -36,7 +36,7 @@ import Text.Pandoc.Writers.Shared import Text.Pandoc.Writers.Math import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Walk -import Text.Pandoc.Class (addWarning) +import Text.Pandoc.Class (warning) import Data.List ( isSuffixOf, intercalate ) import Data.Char ( ord, chr, isDigit ) import qualified Data.ByteString as B @@ -64,7 +64,7 @@ rtfEmbedImage opts x@(Image attr _ (src,_)) = do _ -> throwError $ PandocSomeError "Unknown file type" sizeSpec <- case imageSize imgdata of Left msg -> do - addWarning $ "Could not determine image size in `" ++ + warning $ "Could not determine image size in `" ++ src ++ "': " ++ msg return "" Right sz -> return $ "\\picw" ++ show xpx ++ @@ -78,17 +78,17 @@ rtfEmbedImage opts x@(Image attr _ (src,_)) = do concat bytes ++ "}" if B.null imgdata then do - addWarning $ "Image " ++ src ++ " contained no data, skipping." + warning $ "Image " ++ src ++ " contained no data, skipping." return x else return $ RawInline (Format "rtf") raw | otherwise -> do - addWarning $ "Image " ++ src ++ " is not a jpeg or png, skipping." + warning $ "Image " ++ src ++ " is not a jpeg or png, skipping." return x Right (_, Nothing) -> do - addWarning $ "Could not determine image type for " ++ src ++ ", skipping." + warning $ "Could not determine image type for " ++ src ++ ", skipping." return x Left e -> do - addWarning $ "Could not fetch image " ++ src ++ "\n" ++ show e + warning $ "Could not fetch image " ++ src ++ "\n" ++ show e return x rtfEmbedImage _ x = return x -- cgit v1.2.3 From 1ed925ac20a5e6f9fae9848e80c29c7bec791ca8 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 3 Dec 2016 19:02:35 +0100 Subject: TWiki reader: Remove old readTWikiWithWarnings'. We get warnings for free now from PandocM. (And anyway, this reader doesn't generate any!) --- src/Text/Pandoc/Readers/TWiki.hs | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/TWiki.hs b/src/Text/Pandoc/Readers/TWiki.hs index b4546883b..da908a58c 100644 --- a/src/Text/Pandoc/Readers/TWiki.hs +++ b/src/Text/Pandoc/Readers/TWiki.hs @@ -45,32 +45,19 @@ import Data.Maybe (fromMaybe) import Text.HTML.TagSoup import Data.Char (isAlphaNum) import qualified Data.Foldable as F -import Text.Pandoc.Error - import Control.Monad.Except (throwError) import Text.Pandoc.Class (PandocMonad) -import qualified Text.Pandoc.Class as P -- | Read twiki from an input string and return a Pandoc document. readTWiki :: PandocMonad m => ReaderOptions -> String -> m Pandoc -readTWiki opts s = case readTWikiWithWarnings' opts s of - Right (doc, warns) -> do - mapM_ P.warning warns - return doc - Left e -> throwError e - -readTWikiWithWarnings' :: ReaderOptions -- ^ Reader options - -> String -- ^ String to parse (assuming @'\n'@ line endings) - -> Either PandocError (Pandoc, [String]) -readTWikiWithWarnings' opts s = - (readWith parseTWikiWithWarnings) def{ stateOptions = opts } (s ++ "\n\n") - where parseTWikiWithWarnings = do - doc <- parseTWiki - warnings <- stateWarnings <$> getState - return (doc, warnings) +readTWiki opts s = + case (readWith parseTWiki) def{ stateOptions = opts } (s ++ "\n\n") of + Left e -> throwError e + Right d -> return d + type TWParser = Parser [Char] ParserState -- cgit v1.2.3 From 38064498d98567340d3456bb130c1da8dccaebb2 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 3 Dec 2016 19:03:33 +0100 Subject: Parsing: Removed obsolete warnings stuff. Removed stateWarnings, addWarning, and readWithWarnings. --- src/Text/Pandoc/Parsing.hs | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 796d09632..ced20a8c7 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -67,7 +67,6 @@ module Text.Pandoc.Parsing ( anyLine, widthsFromIndices, gridTableWith, readWith, - readWithWarnings, readWithM, testStringWith, guardEnabled, @@ -165,7 +164,6 @@ module Text.Pandoc.Parsing ( anyLine, setSourceColumn, setSourceLine, newPos, - addWarning, (<+?>), extractIdClass ) @@ -895,15 +893,6 @@ readWith :: Parser [Char] st a -> Either PandocError a readWith p t inp = runIdentity $ readWithM p t inp -readWithWarnings :: Parser [Char] ParserState a - -> ParserState - -> String - -> Either PandocError (a, [String]) -readWithWarnings p = readWith $ do - doc <- p - warnings <- stateWarnings <$> getState - return (doc, warnings) - -- | Parse a string with @parser@ (for testing). testStringWith :: (Show a) => ParserT [Char] ParserState Identity a @@ -940,8 +929,7 @@ data ParserState = ParserState -- roles), 3) Additional classes (rest of Attr is unused)). stateCaption :: Maybe Inlines, -- ^ Caption in current environment stateInHtmlBlock :: Maybe String, -- ^ Tag type of HTML block being parsed - stateMarkdownAttribute :: Bool, -- ^ True if in markdown=1 context - stateWarnings :: [String] -- ^ Warnings generated by the parser + stateMarkdownAttribute :: Bool -- ^ True if in markdown=1 context } instance Default ParserState where @@ -1036,8 +1024,8 @@ defaultParserState = stateRstCustomRoles = M.empty, stateCaption = Nothing, stateInHtmlBlock = Nothing, - stateMarkdownAttribute = False, - stateWarnings = []} + stateMarkdownAttribute = False + } -- | Succeed only if the extension is enabled. guardEnabled :: (Stream s m a, HasReaderOptions st) => Extension -> ParserT s st m () @@ -1274,12 +1262,6 @@ applyMacros' target = do return $ applyMacros macros target else return target --- | Append a warning to the log. -addWarning :: Maybe SourcePos -> String -> Parser [Char] ParserState () -addWarning mbpos msg = - updateState $ \st -> st{ - stateWarnings = (msg ++ maybe "" (\pos -> " " ++ show pos) mbpos) : - stateWarnings st } infixr 5 <+?> (<+?>) :: (Monoid a) => ParserT s st m a -> ParserT s st m a -> ParserT s st m a a <+?> b = a >>= flip fmap (try b <|> return mempty) . (<>) -- cgit v1.2.3 From dc1bbaf58d4adea40f808749b19009a73135bada Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 3 Dec 2016 19:07:45 +0100 Subject: Removed readRSTWithWarnings (now useless). --- src/Text/Pandoc.hs | 2 +- src/Text/Pandoc/Readers/RST.hs | 11 +---------- 2 files changed, 2 insertions(+), 11 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index 3c3a79bb7..320af805a 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -252,7 +252,7 @@ readers = [ ("native" , StringReader $ \_ s -> readNative s) ,("markdown_github" , StringReader readMarkdown) ,("markdown_mmd", StringReader readMarkdown) ,("commonmark" , StringReader readCommonMark) - ,("rst" , StringReader readRSTWithWarnings ) + ,("rst" , StringReader readRST) ,("mediawiki" , StringReader readMediaWiki) ,("docbook" , StringReader readDocBook) ,("opml" , StringReader readOPML) diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 5e8aa20f5..6c844d274 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -29,10 +29,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Conversion from reStructuredText to 'Pandoc' document. -} -module Text.Pandoc.Readers.RST ( - readRST, - readRSTWithWarnings - ) where +module Text.Pandoc.Readers.RST ( readRST ) where import Text.Pandoc.Definition import Text.Pandoc.Builder (setMeta, fromList) import Text.Pandoc.Shared @@ -65,12 +62,6 @@ readRST opts s = do Right result -> return result Left e -> throwError e -readRSTWithWarnings :: PandocMonad m - => ReaderOptions -- ^ Reader options - -> String -- ^ String to parse (assuming @'\n'@ line endings) - -> m Pandoc -readRSTWithWarnings = readRST - type RSTParser m = ParserT [Char] ParserState m -- -- cgit v1.2.3 From 1a0d93a1d33b6b15be15690df9f8aa305cf965b3 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 3 Dec 2016 21:55:31 +0100 Subject: LaTeX reader: Proper include file processing. * Removed handleIncludes from LaTeX reader [API change]. * Now the ordinary LaTeX reader handles includes in a way that is appropriate to the monad it is run in. --- pandoc.hs | 14 +--- src/Text/Pandoc/Parsing.hs | 2 + src/Text/Pandoc/Readers/LaTeX.hs | 144 +++++++++------------------------------ 3 files changed, 37 insertions(+), 123 deletions(-) (limited to 'src/Text') diff --git a/pandoc.hs b/pandoc.hs index c327d8bfa..2cd95f73a 100644 --- a/pandoc.hs +++ b/pandoc.hs @@ -34,7 +34,6 @@ import Text.Pandoc import Text.Pandoc.Builder (setMeta) import Text.Pandoc.PDF (makePDF) import Text.Pandoc.Walk (walk) -import Text.Pandoc.Readers.LaTeX (handleIncludes) import Text.Pandoc.Shared ( tabFilter, readDataFileUTF8, readDataFile, safeRead, headerShift, normalize, err, warn, openURL ) @@ -1410,11 +1409,6 @@ convertWithOpts opts args = do then 0 else tabStop) - let handleIncludes' :: String -> IO (Either PandocError String) - handleIncludes' = if readerName' `elem` ["latex", "latex+lhs"] - then handleIncludes - else return . Right - let runIO' = runIOorExplode . (if quiet then id @@ -1424,12 +1418,8 @@ convertWithOpts opts args = do sourceToDoc sources' = case reader of StringReader r-> do - srcs <- convertTabs . intercalate "\n" <$> readSources sources' - doc <- handleIncludes' srcs - case doc of - Right doc' -> runIO' $ withMediaBag - $ r readerOpts doc' - Left e -> error $ show e + doc <- convertTabs . intercalate "\n" <$> readSources sources' + runIO' $ withMediaBag $ r readerOpts doc ByteStringReader r -> readFiles sources' >>= (\bs -> runIO' $ withMediaBag $ r readerOpts bs) diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index ced20a8c7..f53db1cbc 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -929,6 +929,7 @@ data ParserState = ParserState -- roles), 3) Additional classes (rest of Attr is unused)). stateCaption :: Maybe Inlines, -- ^ Caption in current environment stateInHtmlBlock :: Maybe String, -- ^ Tag type of HTML block being parsed + stateContainers :: [String], -- ^ parent include files stateMarkdownAttribute :: Bool -- ^ True if in markdown=1 context } @@ -1024,6 +1025,7 @@ defaultParserState = stateRstCustomRoles = M.empty, stateCaption = Nothing, stateInHtmlBlock = Nothing, + stateContainers = [], stateMarkdownAttribute = False } diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 425e905f8..abc37001c 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -32,7 +32,6 @@ module Text.Pandoc.Readers.LaTeX ( readLaTeX, rawLaTeXInline, rawLaTeXBlock, inlineCommand, - handleIncludes ) where import Text.Pandoc.Definition @@ -48,16 +47,15 @@ import Control.Monad import Text.Pandoc.Builder import Control.Applicative ((<|>), many, optional) import Data.Maybe (fromMaybe, maybeToList) -import System.Environment (getEnv) import System.FilePath (replaceExtension, (), takeExtension, addExtension) import Data.List (intercalate) import qualified Data.Map as M -import qualified Control.Exception as E import Text.Pandoc.Highlighting (fromListingsLanguage) import Text.Pandoc.ImageSize (numUnit, showFl) import Text.Pandoc.Error -import Control.Monad.Except (throwError) -import Text.Pandoc.Class (PandocMonad, PandocPure) +import Control.Monad.Except (throwError, catchError) +import Text.Pandoc.Class (PandocMonad, PandocPure, lookupEnv, readFileLazy, + warning) -- | Parse LaTeX from string and return 'Pandoc' document. readLaTeX :: PandocMonad m @@ -258,6 +256,7 @@ block :: PandocMonad m => LP m Blocks block = (mempty <$ comment) <|> (mempty <$ ((spaceChar <|> newline) *> spaces)) <|> environment + <|> include <|> macro <|> blockCommand <|> paragraph @@ -353,8 +352,6 @@ blockCommands = M.fromList $ , ("documentclass", skipopts *> braced *> preamble) , ("centerline", (para . trimInlines) <$> (skipopts *> tok)) , ("caption", skipopts *> setCaption) - , ("PandocStartInclude", startInclude) - , ("PandocEndInclude", endInclude) , ("bibliography", mempty <$ (skipopts *> braced >>= addMeta "bibliography" . splitBibs)) , ("addbibresource", mempty <$ (skipopts *> braced >>= @@ -935,50 +932,7 @@ rawEnv name = do ---- -type IncludeParser = ParserT String [String] IO String - --- | Replace "include" commands with file contents. -handleIncludes :: String -> IO (Either PandocError String) -handleIncludes s = mapLeft (PandocParsecError s) <$> runParserT includeParser' [] "input" s - -includeParser' :: IncludeParser -includeParser' = - concat <$> many (comment' <|> escaped' <|> blob' <|> include' - <|> startMarker' <|> endMarker' - <|> verbCmd' <|> verbatimEnv' <|> backslash') - -comment' :: IncludeParser -comment' = do - char '%' - xs <- manyTill anyChar newline - return ('%':xs ++ "\n") - -escaped' :: IncludeParser -escaped' = try $ string "\\%" <|> string "\\\\" - -verbCmd' :: IncludeParser -verbCmd' = fmap snd <$> - withRaw $ try $ do - string "\\verb" - c <- anyChar - manyTill anyChar (char c) - -verbatimEnv' :: IncludeParser -verbatimEnv' = fmap snd <$> - withRaw $ try $ do - string "\\begin" - name <- braced' - guard $ name `elem` ["verbatim", "Verbatim", "BVerbatim", - "lstlisting", "minted", "alltt", "comment"] - manyTill anyChar (try $ string $ "\\end{" ++ name ++ "}") - -blob' :: IncludeParser -blob' = try $ many1 (noneOf "\\%") - -backslash' :: IncludeParser -backslash' = string "\\" - -braced' :: IncludeParser +braced' :: PandocMonad m => LP m String braced' = try $ char '{' *> manyTill (satisfy (/='}')) (char '}') maybeAddExtension :: String -> FilePath -> FilePath @@ -987,8 +941,8 @@ maybeAddExtension ext fp = then addExtension fp ext else fp -include' :: IncludeParser -include' = do +include :: PandocMonad m => LP m Blocks +include = do fs' <- try $ do char '\\' name <- try (string "include") @@ -1000,55 +954,37 @@ include' = do return $ if name == "usepackage" then map (maybeAddExtension ".sty") fs else map (maybeAddExtension ".tex") fs - pos <- getPosition - containers <- getState - let fn = case containers of - (f':_) -> f' - [] -> "input" + oldPos <- getPosition + oldInput <- getInput -- now process each include file in order... - rest <- getInput - results' <- forM fs' (\f -> do + mconcat <$> forM fs' (\f -> do + containers <- stateContainers <$> getState when (f `elem` containers) $ - fail "Include file loop!" + throwError $ PandocParseError $ "Include file loop in " ++ f + updateState $ \s -> s{ stateContainers = f : stateContainers s } contents <- lift $ readTeXFile f - return $ "\\PandocStartInclude{" ++ f ++ "}" ++ - contents ++ "\\PandocEndInclude{" ++ - fn ++ "}{" ++ show (sourceLine pos) ++ "}{" - ++ show (sourceColumn pos) ++ "}") - setInput $ concat results' ++ rest - return "" - -startMarker' :: IncludeParser -startMarker' = try $ do - string "\\PandocStartInclude" - fn <- braced' - updateState (fn:) - setPosition $ newPos fn 1 1 - return $ "\\PandocStartInclude{" ++ fn ++ "}" - -endMarker' :: IncludeParser -endMarker' = try $ do - string "\\PandocEndInclude" - fn <- braced' - ln <- braced' - co <- braced' - updateState tail - setPosition $ newPos fn (fromMaybe 1 $ safeRead ln) (fromMaybe 1 $ safeRead co) - return $ "\\PandocEndInclude{" ++ fn ++ "}{" ++ ln ++ "}{" ++ - co ++ "}" - -readTeXFile :: FilePath -> IO String + setPosition $ newPos f 1 1 + setInput contents + bs <- blocks + setInput oldInput + setPosition oldPos + updateState $ \s -> s{ stateContainers = tail $ stateContainers s } + return bs) + +readTeXFile :: PandocMonad m => FilePath -> m String readTeXFile f = do - texinputs <- E.catch (getEnv "TEXINPUTS") $ \(_ :: E.SomeException) -> - return "." - let ds = splitBy (==':') texinputs - readFileFromDirs ds f + texinputs <- fromMaybe "." <$> lookupEnv "TEXINPUTS" + readFileFromDirs (splitBy (==':') texinputs) f + +readFileFromDirs :: PandocMonad m => [FilePath] -> FilePath -> m String +readFileFromDirs ds f = + mconcat <$> mapM (\d -> readFileLazy' (d f)) ds -readFileFromDirs :: [FilePath] -> FilePath -> IO String -readFileFromDirs [] _ = return "" -readFileFromDirs (d:ds) f = - E.catch (UTF8.readFile $ d f) $ \(_ :: E.SomeException) -> - readFileFromDirs ds f +readFileLazy' :: PandocMonad m => FilePath -> m String +readFileLazy' f = catchError (UTF8.toStringLazy <$> readFileLazy f) $ + \(e :: PandocError) -> do + warning $ "Could not load include file " ++ f ++ ", skipping.\n" ++ show e + return "" ---- @@ -1449,20 +1385,6 @@ simpTable hasWidthParameter = try $ do lookAhead $ controlSeq "end" -- make sure we're at end return $ table mempty (zip aligns (repeat 0)) header'' rows -startInclude :: PandocMonad m => LP m Blocks -startInclude = do - fn <- braced - setPosition $ newPos fn 1 1 - return mempty - -endInclude :: PandocMonad m => LP m Blocks -endInclude = do - fn <- braced - ln <- braced - co <- braced - setPosition $ newPos fn (fromMaybe 1 $ safeRead ln) (fromMaybe 1 $ safeRead co) - return mempty - removeDoubleQuotes :: String -> String removeDoubleQuotes ('"':xs) = case reverse xs of -- cgit v1.2.3 From d7583f365951373158a55ce344ba6b345ea481ec Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 3 Dec 2016 22:35:58 +0100 Subject: Error: change type of handleError. It now lives in IO and gives a proper message + exit instead of calling 'error'. We shouldn't be making it easier for people to raise error on pure code. And this is better for the main application in IO. --- src/Text/Pandoc.hs | 7 +++---- src/Text/Pandoc/Class.hs | 2 +- src/Text/Pandoc/Error.hs | 22 +++++++++++----------- 3 files changed, 15 insertions(+), 16 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index 320af805a..4990a77fe 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -38,12 +38,11 @@ inline links: > module Main where > import Text.Pandoc > -> markdownToRST :: String -> String +> markdownToRST :: String -> Either PandocError String > markdownToRST = -> writeRST def {writerReferenceLinks = True} . -> handleError . readMarkdown def +> writeRST def {writerReferenceLinks = True} . readMarkdown def > -> main = getContents >>= putStrLn . markdownToRST +> main = getContents >>= either error return markdownToRST >>= putStrLn Note: all of the readers assume that the input text has @'\n'@ line endings. So if you get your input text from a web form, diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 7227742b2..8d3a73d08 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -190,7 +190,7 @@ withWarningsToStderr f = do return x runIOorExplode :: PandocIO a -> IO a -runIOorExplode ma = handleError <$> runIO ma +runIOorExplode ma = runIO ma >>= handleError newtype PandocIO a = PandocIO { unPandocIO :: ExceptT PandocError (StateT CommonState IO) a diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs index c001b279a..f76749a80 100644 --- a/src/Text/Pandoc/Error.hs +++ b/src/Text/Pandoc/Error.hs @@ -35,6 +35,7 @@ import Text.Parsec.Error import Text.Parsec.Pos hiding (Line) import Data.Generics (Typeable) import Control.Exception (Exception) +import Text.Pandoc.Shared (err) type Input = String @@ -54,15 +55,15 @@ data PandocError = PandocFileReadError FilePath instance Exception PandocError --- | An unsafe method to handle `PandocError`s. -handleError :: Either PandocError a -> a -handleError (Right r) = r -handleError (Left err) = - case err of - PandocFileReadError fp -> error $ "problem reading " ++ fp - PandocShouldNeverHappenError s -> error s - PandocSomeError s -> error s - PandocParseError s -> error s +-- | Handle PandocError by exiting with an error message. +handleError :: Either PandocError a -> IO a +handleError (Right r) = return r +handleError (Left e) = + case e of + PandocFileReadError fp -> err 61 $ "problem reading " ++ fp + PandocShouldNeverHappenError s -> err 62 s + PandocSomeError s -> err 63 s + PandocParseError s -> err 64 s PandocParsecError input err' -> let errPos = errorPos err' errLine = sourceLine errPos @@ -73,6 +74,5 @@ handleError (Left err) = ,"\n", replicate (errColumn - 1) ' ' ,"^"] else "" - in error $ "\nError at " ++ show err' - ++ errorInFile + in err 65 $ "\nError at " ++ show err' ++ errorInFile -- cgit v1.2.3 From 62b30d841823f6b9452e5a3d26b2ef5b52ca531c Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 3 Dec 2016 22:39:25 +0100 Subject: Give source position for include file loop. --- src/Text/Pandoc/Readers/LaTeX.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index abc37001c..222b91e5d 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -960,7 +960,7 @@ include = do mconcat <$> forM fs' (\f -> do containers <- stateContainers <$> getState when (f `elem` containers) $ - throwError $ PandocParseError $ "Include file loop in " ++ f + throwError $ PandocParseError $ "Include file loop at " ++ show oldPos updateState $ \s -> s{ stateContainers = f : stateContainers s } contents <- lift $ readTeXFile f setPosition $ newPos f 1 1 -- cgit v1.2.3 From 7a686175567295a5169625b5e5428f5b3300ea2a Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 3 Dec 2016 22:59:05 +0100 Subject: LaTeX reader: further fixes to include. --- src/Text/Pandoc/Readers/LaTeX.hs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 222b91e5d..9dc043783 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -977,14 +977,18 @@ readTeXFile f = do readFileFromDirs (splitBy (==':') texinputs) f readFileFromDirs :: PandocMonad m => [FilePath] -> FilePath -> m String -readFileFromDirs ds f = - mconcat <$> mapM (\d -> readFileLazy' (d f)) ds - -readFileLazy' :: PandocMonad m => FilePath -> m String -readFileLazy' f = catchError (UTF8.toStringLazy <$> readFileLazy f) $ - \(e :: PandocError) -> do - warning $ "Could not load include file " ++ f ++ ", skipping.\n" ++ show e +readFileFromDirs [] f = do + warning $ "Could not load include file " ++ f ++ ", skipping." return "" +readFileFromDirs (d:ds) f = do + res <- readFileLazy' (d f) + case res of + Right s -> return s + Left _ -> readFileFromDirs ds f + +readFileLazy' :: PandocMonad m => FilePath -> m (Either PandocError String) +readFileLazy' f = catchError ((Right . UTF8.toStringLazy) <$> readFileLazy f) $ + \(e :: PandocError) -> return (Left e) ---- -- cgit v1.2.3 From c55a98ff0d3856221d9f4f93229ecea5a663fe42 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 3 Dec 2016 23:03:14 +0100 Subject: LaTeX reader: Make sure we process includes in preamble. --- src/Text/Pandoc/Readers/LaTeX.hs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 9dc043783..06269f398 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1219,6 +1219,7 @@ preamble = mempty <$> manyTill preambleBlock beginDoc preambleBlock = void comment <|> void sp <|> void blanklines + <|> void include <|> void macro <|> void blockCommand <|> void anyControlSeq -- cgit v1.2.3 From 7bf0813814eb3df65ba2190ecd9555df2ae251ae Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 3 Dec 2016 23:12:09 +0100 Subject: Shared: changed err and warn output. Don't print program name in either case. Print [warning] for warnings. --- src/Text/Pandoc/Shared.hs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index bd2da945e..4420199f2 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -108,7 +108,6 @@ import Text.Pandoc.MediaBag (MediaBag, lookupMedia) import Text.Pandoc.Builder (Inlines, Blocks, ToMetaValue(..)) import qualified Text.Pandoc.Builder as B import qualified Text.Pandoc.UTF8 as UTF8 -import System.Environment (getProgName) import System.Exit (exitWith, ExitCode(..)) import Data.Char ( toLower, isLower, isUpper, isAlpha, isLetter, isDigit, isSpace ) @@ -1002,15 +1001,13 @@ openURL u err :: Int -> String -> IO a err exitCode msg = do - name <- getProgName - UTF8.hPutStrLn stderr $ name ++ ": " ++ msg + UTF8.hPutStrLn stderr msg exitWith $ ExitFailure exitCode return undefined warn :: MonadIO m => String -> m () warn msg = liftIO $ do - name <- getProgName - UTF8.hPutStrLn stderr $ "[" ++ name ++ " warning] " ++ msg + UTF8.hPutStrLn stderr $ "[warning] " ++ msg mapLeft :: (a -> b) -> Either a c -> Either b c mapLeft f (Left x) = Left (f x) -- cgit v1.2.3 From 15708f0b0f04fa32ce4b4296e25a120f5f533e0d Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Sat, 3 Dec 2016 23:25:36 -0500 Subject: Class: rename env* prefixed fields to st*. This was left over from when they were part of an environment. --- src/Text/Pandoc/Class.hs | 56 ++++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 28 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 8d3a73d08..0307407ac 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -241,30 +241,30 @@ data PureState = PureState { stStdGen :: StdGen -- contain every -- element at most -- once, e.g. [1..] - , envEnv :: [(String, String)] - , envTime :: UTCTime - , envTimeZone :: TimeZone - , envReferenceDocx :: Archive - , envReferenceODT :: Archive - , envFiles :: FileTree - , envUserDataDir :: FileTree - , envCabalDataDir :: FileTree - , envFontFiles :: [FilePath] + , stEnv :: [(String, String)] + , stTime :: UTCTime + , stTimeZone :: TimeZone + , stReferenceDocx :: Archive + , stReferenceODT :: Archive + , stFiles :: FileTree + , stUserDataDir :: FileTree + , stCabalDataDir :: FileTree + , stFontFiles :: [FilePath] } instance Default PureState where def = PureState { stStdGen = mkStdGen 1848 , stWord8Store = [1..] , stUniqStore = [1..] - , envEnv = [("USER", "pandoc-user")] - , envTime = posixSecondsToUTCTime 0 - , envTimeZone = utc - , envReferenceDocx = emptyArchive - , envReferenceODT = emptyArchive - , envFiles = mempty - , envUserDataDir = mempty - , envCabalDataDir = mempty - , envFontFiles = [] + , stEnv = [("USER", "pandoc-user")] + , stTime = posixSecondsToUTCTime 0 + , stTimeZone = utc + , stReferenceDocx = emptyArchive + , stReferenceODT = emptyArchive + , stFiles = mempty + , stUserDataDir = mempty + , stCabalDataDir = mempty + , stFontFiles = [] } data FileInfo = FileInfo { infoFileMTime :: UTCTime , infoFileContents :: B.ByteString @@ -295,16 +295,16 @@ runPure x = flip evalState def $ instance PandocMonad PandocPure where lookupEnv s = PandocPure $ do - env <- lift $ lift $ gets envEnv + env <- lift $ lift $ gets stEnv return (lookup s env) - getCurrentTime = PandocPure $ lift $ lift $ gets envTime + getCurrentTime = PandocPure $ lift $ lift $ gets stTime - getCurrentTimeZone = PandocPure $ lift $ lift $ gets envTimeZone + getCurrentTimeZone = PandocPure $ lift $ lift $ gets stTimeZone - getDefaultReferenceDocx _ = PandocPure $ lift $ lift $ gets envReferenceDocx + getDefaultReferenceDocx _ = PandocPure $ lift $ lift $ gets stReferenceDocx - getDefaultReferenceODT _ = PandocPure $ lift $ lift $ gets envReferenceODT + getDefaultReferenceODT _ = PandocPure $ lift $ lift $ gets stReferenceODT newStdGen = PandocPure $ do g <- lift $ lift $ gets stStdGen @@ -320,7 +320,7 @@ instance PandocMonad PandocPure where return u _ -> M.fail "uniq store ran out of elements" readFileLazy fp = PandocPure $ do - fps <- lift $ lift $ gets envFiles + fps <- lift $ lift $ gets stFiles case infoFileContents <$> getFileInfo fp fps of Just bs -> return (BL.fromStrict bs) Nothing -> throwError $ PandocFileReadError fp @@ -332,13 +332,13 @@ instance PandocMonad PandocPure where let fname' = if fname == "MANUAL.txt" then fname else "data" fname BL.toStrict <$> (readFileLazy fname') readDataFile (Just userDir) fname = PandocPure $ do - userDirFiles <- lift $ lift $ gets envUserDataDir + userDirFiles <- lift $ lift $ gets stUserDataDir case infoFileContents <$> (getFileInfo (userDir fname) userDirFiles) of Just bs -> return bs Nothing -> unPandocPure $ readDataFile Nothing fname fail = M.fail fetchItem _ fp = PandocPure $ do - fps <- lift $ lift $ gets envFiles + fps <- lift $ lift $ gets stFiles case infoFileContents <$> (getFileInfo fp fps) of Just bs -> return (Right (bs, getMimeType fp)) Nothing -> return (Left $ E.toException $ PandocFileReadError fp) @@ -349,11 +349,11 @@ instance PandocMonad PandocPure where Just (mime, bs) -> return (Right (B.concat $ BL.toChunks bs, Just mime)) glob s = PandocPure $ do - fontFiles <- lift $ lift $ gets envFontFiles + fontFiles <- lift $ lift $ gets stFontFiles return (filter (match (compile s)) fontFiles) getModificationTime fp = PandocPure $ do - fps <- lift $ lift $ gets envFiles + fps <- lift $ lift $ gets stFiles case infoFileMTime <$> (getFileInfo fp fps) of Just tm -> return tm Nothing -> throwError $ PandocFileReadError fp -- cgit v1.2.3 From 57cff4b8ae75a2bbca86f5e3123cb890b629944e Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Sat, 3 Dec 2016 23:39:01 -0500 Subject: Class: Functions for dealing with PureState There are two states in PandocPure, but it is only easy to deal with CommonState. In the past, to do state monad operations on PureState (the state specific to PandocPure) you had to add (lift . lift) to the monadic operation and then rewrap in the newtype. This adds four functions ({get,gets,put,modify}PureState) corresponding to normal state monad operations. This allows the user to modify PureState in PandocPure without worrying about where it sits in the monad stack or rewrapping the newtype. --- src/Text/Pandoc/Class.hs | 65 +++++++++++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 23 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 0307407ac..d81d3b68b 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -34,6 +34,10 @@ Typeclass for pandoc readers and writers, allowing both IO and pure instances. module Text.Pandoc.Class ( PandocMonad(..) , CommonState(..) , PureState(..) + , getPureState + , getsPureState + , putPureState + , modifyPureState , getPOSIXTime , getZonedTime , warning @@ -266,6 +270,21 @@ instance Default PureState where , stCabalDataDir = mempty , stFontFiles = [] } + + +getPureState :: PandocPure PureState +getPureState = PandocPure $ lift $ lift $ get + +getsPureState :: (PureState -> a) -> PandocPure a +getsPureState f = f <$> getPureState + +putPureState :: PureState -> PandocPure () +putPureState ps= PandocPure $ lift $ lift $ put ps + +modifyPureState :: (PureState -> PureState) -> PandocPure () +modifyPureState f = PandocPure $ lift $ lift $ modify f + + data FileInfo = FileInfo { infoFileMTime :: UTCTime , infoFileContents :: B.ByteString } @@ -294,33 +313,33 @@ runPure x = flip evalState def $ unPandocPure x instance PandocMonad PandocPure where - lookupEnv s = PandocPure $ do - env <- lift $ lift $ gets stEnv + lookupEnv s = do + env <- getsPureState stEnv return (lookup s env) - getCurrentTime = PandocPure $ lift $ lift $ gets stTime + getCurrentTime = getsPureState stTime - getCurrentTimeZone = PandocPure $ lift $ lift $ gets stTimeZone + getCurrentTimeZone = getsPureState stTimeZone - getDefaultReferenceDocx _ = PandocPure $ lift $ lift $ gets stReferenceDocx + getDefaultReferenceDocx _ = getsPureState stReferenceDocx - getDefaultReferenceODT _ = PandocPure $ lift $ lift $ gets stReferenceODT + getDefaultReferenceODT _ = getsPureState stReferenceODT - newStdGen = PandocPure $ do - g <- lift $ lift $ gets stStdGen + newStdGen = do + g <- getsPureState stStdGen let (_, nxtGen) = next g - lift $ lift $ modify $ \st -> st { stStdGen = nxtGen } + modifyPureState $ \st -> st { stStdGen = nxtGen } return g - newUniqueHash = PandocPure $ do - uniqs <- lift $ lift $ gets stUniqStore + newUniqueHash = do + uniqs <- getsPureState stUniqStore case uniqs of u : us -> do - lift $ lift $ modify $ \st -> st { stUniqStore = us } + modifyPureState $ \st -> st { stUniqStore = us } return u _ -> M.fail "uniq store ran out of elements" - readFileLazy fp = PandocPure $ do - fps <- lift $ lift $ gets stFiles + readFileLazy fp = do + fps <- getsPureState stFiles case infoFileContents <$> getFileInfo fp fps of Just bs -> return (BL.fromStrict bs) Nothing -> throwError $ PandocFileReadError fp @@ -331,14 +350,14 @@ instance PandocMonad PandocPure where readDataFile Nothing fname = do let fname' = if fname == "MANUAL.txt" then fname else "data" fname BL.toStrict <$> (readFileLazy fname') - readDataFile (Just userDir) fname = PandocPure $ do - userDirFiles <- lift $ lift $ gets stUserDataDir + readDataFile (Just userDir) fname = do + userDirFiles <- getsPureState stUserDataDir case infoFileContents <$> (getFileInfo (userDir fname) userDirFiles) of Just bs -> return bs - Nothing -> unPandocPure $ readDataFile Nothing fname + Nothing -> readDataFile Nothing fname fail = M.fail - fetchItem _ fp = PandocPure $ do - fps <- lift $ lift $ gets stFiles + fetchItem _ fp = do + fps <- getsPureState stFiles case infoFileContents <$> (getFileInfo fp fps) of Just bs -> return (Right (bs, getMimeType fp)) Nothing -> return (Left $ E.toException $ PandocFileReadError fp) @@ -348,12 +367,12 @@ instance PandocMonad PandocPure where Nothing -> fetchItem sourceUrl nm Just (mime, bs) -> return (Right (B.concat $ BL.toChunks bs, Just mime)) - glob s = PandocPure $ do - fontFiles <- lift $ lift $ gets stFontFiles + glob s = do + fontFiles <- getsPureState stFontFiles return (filter (match (compile s)) fontFiles) - getModificationTime fp = PandocPure $ do - fps <- lift $ lift $ gets stFiles + getModificationTime fp = do + fps <- getsPureState stFiles case infoFileMTime <$> (getFileInfo fp fps) of Just tm -> return tm Nothing -> throwError $ PandocFileReadError fp -- cgit v1.2.3 From ad3ff342dd48e3c0699dd179250bfc049b2c22e9 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 4 Dec 2016 22:38:14 +0100 Subject: RST reader: Simple `.. include::` support. TODO: handle the options (see comment in code). See #223. --- src/Text/Pandoc/Readers/RST.hs | 67 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 6c844d274..9b94cbdd7 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE FlexibleContexts #-} {- @@ -35,6 +36,8 @@ import Text.Pandoc.Builder (setMeta, fromList) import Text.Pandoc.Shared import Text.Pandoc.Parsing import Text.Pandoc.Options +import Text.Pandoc.Error +import qualified Text.Pandoc.UTF8 as UTF8 import Control.Monad ( when, liftM, guard, mzero ) import Data.List ( findIndex, intercalate, transpose, sort, deleteFirstsBy, isSuffixOf , nub, union) @@ -46,9 +49,9 @@ import qualified Text.Pandoc.Builder as B import Data.Sequence (viewr, ViewR(..)) import Data.Char (toLower, isHexDigit, isSpace) import Data.Monoid ((<>)) -import Control.Monad.Except (throwError) +import Control.Monad.Except (throwError, catchError) import Control.Monad.Trans (lift) -import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Class (PandocMonad, warning, readFileLazy) import qualified Text.Pandoc.Class as P -- | Parse reStructuredText string and return Pandoc document. @@ -177,6 +180,7 @@ block :: PandocMonad m => RSTParser m Blocks block = choice [ codeBlock , blockQuote , fieldList + , include , directive , comment , header @@ -397,6 +401,65 @@ blockQuote = do contents <- parseFromString parseBlocks $ raw ++ "\n\n" return $ B.blockQuote contents +{- +From RST docs: +The following options are recognized: + +start-line : integer +Only the content starting from this line will be included. (As usual in Python, the first line has index 0 and negative values count from the end.) +end-line : integer +Only the content up to (but excluding) this line will be included. +start-after : text to find in the external data file +Only the content after the first occurrence of the specified text will be included. +end-before : text to find in the external data file +Only the content before the first occurrence of the specified text (but after any after text) will be included. +literal : flag (empty) +The entire included text is inserted into the document as a single literal block. +code : formal language (optional) +The argument and the content of the included file are passed to the code directive (useful for program listings). (New in Docutils 0.9) +number-lines : [start line number] +Precede every code line with a line number. The optional argument is the number of the first line (defaut 1). Works only with code or literal. (New in Docutils 0.9) +encoding : name of text encoding +The text encoding of the external data file. Defaults to the document's input_encoding. +tab-width : integer +Number of spaces for hard tab expansion. A negative value prevents expansion of hard tabs. Defaults to the tab_width configuration setting. +With code or literal the common options :class: and :name: are recognized as well. + +Combining start/end-line and start-after/end-before is possible. The text markers will be searched in the specified lines (further limiting the included content). + +-} + +include :: PandocMonad m => RSTParser m Blocks +include = try $ do + string ".. include::" + skipMany spaceChar + f <- trim <$> anyLine + -- TODO options + guard $ not (null f) + oldPos <- getPosition + oldInput <- getInput + containers <- stateContainers <$> getState + when (f `elem` containers) $ + throwError $ PandocParseError $ "Include file loop at " ++ show oldPos + updateState $ \s -> s{ stateContainers = f : stateContainers s } + res <- lift $ readFileLazy' f + contents <- case res of + Right x -> return x + Left _e -> do + lift $ warning $ "Could not read include file " ++ f ++ "." + return "" + setPosition $ newPos f 1 1 + setInput contents + bs <- optional blanklines >> (mconcat <$> many block) + setInput oldInput + setPosition oldPos + updateState $ \s -> s{ stateContainers = tail $ stateContainers s } + return bs + +readFileLazy' :: PandocMonad m => FilePath -> m (Either PandocError String) +readFileLazy' f = catchError ((Right . UTF8.toStringLazy) <$> readFileLazy f) $ + \(e :: PandocError) -> return (Left e) + -- -- list blocks -- -- cgit v1.2.3 From 223dff4d2927a90c62fc657a473020314e11a0f4 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 4 Dec 2016 22:55:57 +0100 Subject: RST reader: support start-line and end-line in include. Just skip other options for now. --- src/Text/Pandoc/Readers/RST.hs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 9b94cbdd7..571d1b75f 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -434,7 +434,10 @@ include = try $ do string ".. include::" skipMany spaceChar f <- trim <$> anyLine - -- TODO options + fields <- many $ rawFieldListItem 3 + -- options + let (startLine :: Maybe Int) = lookup "start-line" fields >>= safeRead + let (endLine :: Maybe Int) = lookup "end-line" fields >>= safeRead guard $ not (null f) oldPos <- getPosition oldInput <- getInput @@ -448,8 +451,11 @@ include = try $ do Left _e -> do lift $ warning $ "Could not read include file " ++ f ++ "." return "" + let contents' = unlines $ maybe id (drop . (\x -> x - 1)) startLine + $ maybe id (take . (\x -> x - 1)) endLine + $ lines contents setPosition $ newPos f 1 1 - setInput contents + setInput contents' bs <- optional blanklines >> (mconcat <$> many block) setInput oldInput setPosition oldPos -- cgit v1.2.3 From d595702b1702d6ab3813dee7bdc9e7c76da41920 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 4 Dec 2016 23:09:19 +0100 Subject: RST reader include: handle negative values for start-, end-line. --- src/Text/Pandoc/Readers/RST.hs | 53 ++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 23 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 571d1b75f..1abf7046b 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -405,28 +405,25 @@ blockQuote = do From RST docs: The following options are recognized: -start-line : integer -Only the content starting from this line will be included. (As usual in Python, the first line has index 0 and negative values count from the end.) -end-line : integer -Only the content up to (but excluding) this line will be included. -start-after : text to find in the external data file -Only the content after the first occurrence of the specified text will be included. -end-before : text to find in the external data file -Only the content before the first occurrence of the specified text (but after any after text) will be included. -literal : flag (empty) -The entire included text is inserted into the document as a single literal block. -code : formal language (optional) -The argument and the content of the included file are passed to the code directive (useful for program listings). (New in Docutils 0.9) -number-lines : [start line number] -Precede every code line with a line number. The optional argument is the number of the first line (defaut 1). Works only with code or literal. (New in Docutils 0.9) -encoding : name of text encoding +[x] start-line : integer +[x] end-line : integer +[ ] start-after : text to find in the external data file + Only the content after the first occurrence of the specified text will be included. +[ ] end-before : text to find in the external data file + Only the content before the first occurrence of the specified text (but after any after text) will be included. + Combining start/end-line and start-after/end-before is possible. The text markers will be searched in the specified lines (further limiting the included content). +[ ] literal : flag (empty) + The entire included text is inserted into the document as a single literal block. +[ ] code : formal language (optional) + The argument and the content of the included file are passed to the code directive (useful for program listings). (New in Docutils 0.9) +[ ] number-lines : [start line number] + Precede every code line with a line number. The optional argument is the number of the first line (defaut 1). Works only with code or literal. (New in Docutils 0.9) +[ ] encoding : name of text encoding The text encoding of the external data file. Defaults to the document's input_encoding. -tab-width : integer +[ ] tab-width : integer Number of spaces for hard tab expansion. A negative value prevents expansion of hard tabs. Defaults to the tab_width configuration setting. -With code or literal the common options :class: and :name: are recognized as well. - -Combining start/end-line and start-after/end-before is possible. The text markers will be searched in the specified lines (further limiting the included content). - +[ ] class (common option) - with code or literal +[ ] name (common option) - with code or literal -} include :: PandocMonad m => RSTParser m Blocks @@ -451,9 +448,19 @@ include = try $ do Left _e -> do lift $ warning $ "Could not read include file " ++ f ++ "." return "" - let contents' = unlines $ maybe id (drop . (\x -> x - 1)) startLine - $ maybe id (take . (\x -> x - 1)) endLine - $ lines contents + let contentLines = lines contents + let numLines = length contentLines + let startLine' = case startLine of + Nothing -> 1 + Just x | x >= 0 -> x + | otherwise -> numLines + x -- negative from end + let endLine' = case endLine of + Nothing -> numLines + 1 + Just x | x >= 0 -> x + | otherwise -> numLines + x -- negative from end + let contents' = unlines $ drop (startLine' - 1) + $ take (endLine' - 1) + $ contentLines setPosition $ newPos f 1 1 setInput contents' bs <- optional blanklines >> (mconcat <$> many block) -- cgit v1.2.3 From 03ede3e31263d6a4db1d726022d1e0bf22363540 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 4 Dec 2016 23:29:22 +0100 Subject: RST reader: handle code, literal, number-lines, class, name for include. --- src/Text/Pandoc/Readers/RST.hs | 57 ++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 30 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 1abf7046b..5185a1149 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -41,7 +41,7 @@ import qualified Text.Pandoc.UTF8 as UTF8 import Control.Monad ( when, liftM, guard, mzero ) import Data.List ( findIndex, intercalate, transpose, sort, deleteFirstsBy, isSuffixOf , nub, union) -import Data.Maybe (fromMaybe) +import Data.Maybe (fromMaybe, isJust) import qualified Data.Map as M import Text.Printf ( printf ) import Text.Pandoc.Builder (Inlines, Blocks, trimInlines) @@ -402,28 +402,11 @@ blockQuote = do return $ B.blockQuote contents {- -From RST docs: -The following options are recognized: - -[x] start-line : integer -[x] end-line : integer -[ ] start-after : text to find in the external data file - Only the content after the first occurrence of the specified text will be included. -[ ] end-before : text to find in the external data file - Only the content before the first occurrence of the specified text (but after any after text) will be included. - Combining start/end-line and start-after/end-before is possible. The text markers will be searched in the specified lines (further limiting the included content). -[ ] literal : flag (empty) - The entire included text is inserted into the document as a single literal block. -[ ] code : formal language (optional) - The argument and the content of the included file are passed to the code directive (useful for program listings). (New in Docutils 0.9) -[ ] number-lines : [start line number] - Precede every code line with a line number. The optional argument is the number of the first line (defaut 1). Works only with code or literal. (New in Docutils 0.9) -[ ] encoding : name of text encoding -The text encoding of the external data file. Defaults to the document's input_encoding. -[ ] tab-width : integer -Number of spaces for hard tab expansion. A negative value prevents expansion of hard tabs. Defaults to the tab_width configuration setting. -[ ] class (common option) - with code or literal -[ ] name (common option) - with code or literal +Unsupported options for include: +tab-width +encoding +start-after: text to find +end-before: text to find -} include :: PandocMonad m => RSTParser m Blocks @@ -461,13 +444,27 @@ include = try $ do let contents' = unlines $ drop (startLine' - 1) $ take (endLine' - 1) $ contentLines - setPosition $ newPos f 1 1 - setInput contents' - bs <- optional blanklines >> (mconcat <$> many block) - setInput oldInput - setPosition oldPos - updateState $ \s -> s{ stateContainers = tail $ stateContainers s } - return bs + case lookup "code" fields of + Just lang -> do + let numberLines = lookup "number-lines" fields + let classes = trimr lang : ["numberLines" | isJust numberLines] ++ + maybe [] words (lookup "class" fields) + let kvs = maybe [] (\n -> [("startFrom", trimr n)]) numberLines + let ident = maybe "" trimr $ lookup "name" fields + let attribs = (ident, classes, kvs) + return $ B.codeBlockWith attribs contents' + Nothing -> case lookup "literal" fields of + Just _ -> return $ B.rawBlock "rst" contents' + Nothing -> do + setPosition $ newPos f 1 1 + setInput contents' + bs <- optional blanklines >> + (mconcat <$> many block) + setInput oldInput + setPosition oldPos + updateState $ \s -> s{ stateContainers = + tail $ stateContainers s } + return bs readFileLazy' :: PandocMonad m => FilePath -> m (Either PandocError String) readFileLazy' f = catchError ((Right . UTF8.toStringLazy) <$> readFileLazy f) $ -- cgit v1.2.3 From 92cc80b58bc1c932e3e102a260388df83fd7e0c0 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 4 Dec 2016 23:45:09 +0100 Subject: RST reader: implement start-after, end-before fields for include. --- src/Text/Pandoc/Readers/RST.hs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 5185a1149..92d0e8670 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -39,7 +39,7 @@ import Text.Pandoc.Options import Text.Pandoc.Error import qualified Text.Pandoc.UTF8 as UTF8 import Control.Monad ( when, liftM, guard, mzero ) -import Data.List ( findIndex, intercalate, +import Data.List ( findIndex, intercalate, isInfixOf, transpose, sort, deleteFirstsBy, isSuffixOf , nub, union) import Data.Maybe (fromMaybe, isJust) import qualified Data.Map as M @@ -405,8 +405,6 @@ blockQuote = do Unsupported options for include: tab-width encoding -start-after: text to find -end-before: text to find -} include :: PandocMonad m => RSTParser m Blocks @@ -441,9 +439,17 @@ include = try $ do Nothing -> numLines + 1 Just x | x >= 0 -> x | otherwise -> numLines + x -- negative from end - let contents' = unlines $ drop (startLine' - 1) - $ take (endLine' - 1) - $ contentLines + let contentLines' = drop (startLine' - 1) + $ take (endLine' - 1) + $ contentLines + let contentLines'' = (case trim <$> lookup "end-before" fields of + Just patt -> takeWhile (not . (patt `isInfixOf`)) + Nothing -> id) . + (case trim <$> lookup "start-after" fields of + Just patt -> drop 1 . + dropWhile (not . (patt `isInfixOf`)) + Nothing -> id) $ contentLines' + let contents' = unlines contentLines'' case lookup "code" fields of Just lang -> do let numberLines = lookup "number-lines" fields -- cgit v1.2.3 From f1cec1dd0257c10fb291a7fb50e216a5218ebf77 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 5 Dec 2016 11:09:51 +0100 Subject: LaTeX reader: add warning when parsing unescaped characters that normally need escaping in LaTeX. --- src/Text/Pandoc/Readers/LaTeX.hs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 06269f398..49d2d702f 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -55,7 +55,7 @@ import Text.Pandoc.ImageSize (numUnit, showFl) import Text.Pandoc.Error import Control.Monad.Except (throwError, catchError) import Text.Pandoc.Class (PandocMonad, PandocPure, lookupEnv, readFileLazy, - warning) + warning, warningWithPos) -- | Parse LaTeX from string and return 'Pandoc' document. readLaTeX :: PandocMonad m @@ -236,9 +236,10 @@ inline = (mempty <$ comment) <|> mathInline (char '$' *> mathChars <* char '$') <|> (guardEnabled Ext_literate_haskell *> char '|' *> doLHSverb) <|> (str . (:[]) <$> tildeEscape) - <|> (str . (:[]) <$> oneOf "[]") - <|> (str . (:[]) <$> oneOf "#&~^'`\"[]") -- TODO print warning? - -- <|> (str <$> count 1 (satisfy (\c -> c /= '\\' && c /='\n' && c /='}' && c /='{'))) -- eat random leftover characters + <|> (do res <- oneOf "#&~^'`\"[]" + pos <- getPosition + warningWithPos (Just pos) ("Parsing unescaped '" ++ [res] ++ "'") + return $ str [res]) inlines :: PandocMonad m => LP m Inlines inlines = mconcat <$> many (notFollowedBy (char '}') *> inline) -- cgit v1.2.3 From e1d2da4c227a15427b82697d573d44bbd08ef906 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 5 Dec 2016 11:30:55 +0100 Subject: Have warningWithPos take a SourcePos rather than Maybe SourcePos. After all, we have warning if you don't want the source pos info. --- src/Text/Pandoc/Class.hs | 34 ++++++++++++++++++++++------------ src/Text/Pandoc/Readers/LaTeX.hs | 2 +- src/Text/Pandoc/Readers/Markdown.hs | 10 +++++----- src/Text/Pandoc/Readers/RST.hs | 4 ++-- 4 files changed, 30 insertions(+), 20 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index d81d3b68b..5121d3fe6 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -72,7 +72,7 @@ import qualified Text.Pandoc.Shared as IO ( fetchItem , readDataFile , warn) import Text.Pandoc.Compat.Time (UTCTime) -import Text.Pandoc.Parsing (ParserT, ParserState, SourcePos) +import Text.Pandoc.Parsing (ParserT, SourcePos) import qualified Text.Pandoc.Compat.Time as IO (getCurrentTime) import Data.Time.Clock.POSIX ( utcTimeToPOSIXSeconds , posixSecondsToUTCTime @@ -121,7 +121,7 @@ class (Functor m, Applicative m, Monad m, MonadError PandocError m, MonadState C glob :: String -> m [FilePath] getModificationTime :: FilePath -> m UTCTime - + -- Functions defined for all PandocMonad instances @@ -157,11 +157,10 @@ getZonedTime = do return $ utcToZonedTime tz t warningWithPos :: PandocMonad m - => Maybe SourcePos + => SourcePos -> String - -> ParserT [Char] ParserState m () -warningWithPos mbpos msg = - lift $ warning $ msg ++ maybe "" (\pos -> " " ++ show pos) mbpos + -> ParserT s st m () +warningWithPos pos msg = lift $ warning $ msg ++ " " ++ show pos -- @@ -377,9 +376,20 @@ instance PandocMonad PandocPure where Just tm -> return tm Nothing -> throwError $ PandocFileReadError fp - - - - - - +{- +instance PandocMonad m => PandocMonad (ParserT s st m) where + lookupEnv = lift . lookupEnv + getCurrentTime = lift . getCurrentTime + getCurrentTimeZone = lift . getCurrentTimeZone + getDefaultReferenceDocx = lift . getDefaultReferenceDocx + getDefaultReferenceODT = lift . getDefaultReferenceODT + newStdGen = lift . newStdGen + newUniqueHash = lift . newUniqueHash + readFileLazy = lift . readFileLazy + readDataFile mbuserdir = lift . readDataFile mbuserdir + fail = lift . fail + fetchItem media = lift . fetchItem media + fetchItem' media sourceUrl = lift . fetchItem' media sourceUrl + glob = lift . glob + getModificationTime = lift . getModificationTime +-} diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 49d2d702f..1c8536924 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -238,7 +238,7 @@ inline = (mempty <$ comment) <|> (str . (:[]) <$> tildeEscape) <|> (do res <- oneOf "#&~^'`\"[]" pos <- getPosition - warningWithPos (Just pos) ("Parsing unescaped '" ++ [res] ++ "'") + warningWithPos pos ("Parsing unescaped '" ++ [res] ++ "'") return $ str [res]) inlines :: PandocMonad m => LP m Inlines diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 012edfe3b..1923bca01 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -280,7 +280,7 @@ yamlMetaBlock = try $ do ) nullMeta hashmap Right Yaml.Null -> return nullMeta Right _ -> do - P.warningWithPos (Just pos) "YAML header is not an object" + P.warningWithPos pos "YAML header is not an object" return nullMeta Left err' -> do case err' of @@ -291,13 +291,13 @@ yamlMetaBlock = try $ do yamlLine = yline , yamlColumn = ycol }}) -> - P.warningWithPos (Just $ setSourceLine + P.warningWithPos (setSourceLine (setSourceColumn pos (sourceColumn pos + ycol)) (sourceLine pos + 1 + yline)) $ "Could not parse YAML header: " ++ problem - _ -> P.warningWithPos (Just pos) + _ -> P.warningWithPos pos $ "Could not parse YAML header: " ++ show err' return nullMeta @@ -420,7 +420,7 @@ referenceKey = try $ do let oldkeys = stateKeys st let key = toKey raw case M.lookup key oldkeys of - Just _ -> P.warningWithPos (Just pos) $ "Duplicate link reference `" ++ raw ++ "'" + Just _ -> P.warningWithPos pos $ "Duplicate link reference `" ++ raw ++ "'" Nothing -> return () updateState $ \s -> s { stateKeys = M.insert key (target, attr') oldkeys } return $ return mempty @@ -486,7 +486,7 @@ noteBlock = try $ do let newnote = (ref, parsed) oldnotes <- stateNotes' <$> getState case lookup ref oldnotes of - Just _ -> P.warningWithPos (Just pos) $ "Duplicate note reference `" ++ ref ++ "'" + Just _ -> P.warningWithPos pos $ "Duplicate note reference `" ++ ref ++ "'" Nothing -> return () updateState $ \s -> s { stateNotes' = newnote : oldnotes } return mempty diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 92d0e8670..82e50ce6e 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -696,7 +696,7 @@ directive' = do return $ B.divWith attrs children other -> do pos <- getPosition - P.warningWithPos (Just pos) $ "ignoring unknown directive: " ++ other + P.warningWithPos pos $ "ignoring unknown directive: " ++ other return mempty -- TODO: @@ -1135,7 +1135,7 @@ renderRole contents fmt role attr = case role of renderRole contents newFmt newRole newAttr Nothing -> do pos <- getPosition - P.warningWithPos (Just pos) $ "ignoring unknown role :" ++ custom ++ ": in" + P.warningWithPos pos $ "ignoring unknown role :" ++ custom ++ ": in" return $ B.str contents -- Undefined role where titleRef ref = return $ B.str ref -- FIXME: Not a sensible behaviour -- cgit v1.2.3 From 8753a91252ace9254d2aab82371c56c43b1f68b0 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 5 Dec 2016 11:36:23 +0100 Subject: Add PandocMonad m instance for ParserT based on PandocMonad. This will avoid the need for lift. --- src/Text/Pandoc/Class.hs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 5121d3fe6..b4161b964 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -376,15 +376,14 @@ instance PandocMonad PandocPure where Just tm -> return tm Nothing -> throwError $ PandocFileReadError fp -{- instance PandocMonad m => PandocMonad (ParserT s st m) where lookupEnv = lift . lookupEnv - getCurrentTime = lift . getCurrentTime - getCurrentTimeZone = lift . getCurrentTimeZone + getCurrentTime = lift getCurrentTime + getCurrentTimeZone = lift getCurrentTimeZone getDefaultReferenceDocx = lift . getDefaultReferenceDocx getDefaultReferenceODT = lift . getDefaultReferenceODT - newStdGen = lift . newStdGen - newUniqueHash = lift . newUniqueHash + newStdGen = lift newStdGen + newUniqueHash = lift newUniqueHash readFileLazy = lift . readFileLazy readDataFile mbuserdir = lift . readDataFile mbuserdir fail = lift . fail @@ -392,4 +391,3 @@ instance PandocMonad m => PandocMonad (ParserT s st m) where fetchItem' media sourceUrl = lift . fetchItem' media sourceUrl glob = lift . glob getModificationTime = lift . getModificationTime --} -- cgit v1.2.3 From 931528dba65529fdb948ec58d13fcd43f93b8c00 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 5 Dec 2016 11:38:36 +0100 Subject: Markdown reader: Removed readMarkdownWithWarnings [API change]. --- src/Text/Pandoc/Readers/Markdown.hs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 1923bca01..a6156e497 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -30,8 +30,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Conversion of markdown-formatted plain text to 'Pandoc' document. -} -module Text.Pandoc.Readers.Markdown ( readMarkdown, - readMarkdownWithWarnings ) where +module Text.Pandoc.Readers.Markdown ( readMarkdown ) where import Data.List ( transpose, sortBy, findIndex, intercalate ) import qualified Data.Map as M @@ -84,14 +83,6 @@ readMarkdown opts s = do Right result -> return result Left e -> throwError e --- | Read markdown from an input string and return a pair of a Pandoc document --- and a list of warnings. -readMarkdownWithWarnings :: PandocMonad m - => ReaderOptions -- ^ Reader options - -> String -- ^ String to parse (assuming @'\n'@ line endings) - -> m Pandoc -readMarkdownWithWarnings = readMarkdown - trimInlinesF :: F Inlines -> F Inlines trimInlinesF = liftM trimInlines -- cgit v1.2.3 From 7d21238d62e51fd7b36b50759324a6b18f11adc5 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 5 Dec 2016 11:47:20 +0100 Subject: RST reader: removed now unnecessary lifts. --- src/Text/Pandoc/Readers/RST.hs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 82e50ce6e..22478de72 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -50,7 +50,6 @@ import Data.Sequence (viewr, ViewR(..)) import Data.Char (toLower, isHexDigit, isSpace) import Data.Monoid ((<>)) import Control.Monad.Except (throwError, catchError) -import Control.Monad.Trans (lift) import Text.Pandoc.Class (PandocMonad, warning, readFileLazy) import qualified Text.Pandoc.Class as P @@ -423,11 +422,11 @@ include = try $ do when (f `elem` containers) $ throwError $ PandocParseError $ "Include file loop at " ++ show oldPos updateState $ \s -> s{ stateContainers = f : stateContainers s } - res <- lift $ readFileLazy' f + res <- readFileLazy' f contents <- case res of Right x -> return x Left _e -> do - lift $ warning $ "Could not read include file " ++ f ++ "." + warning $ "Could not read include file " ++ f ++ "." return "" let contentLines = lines contents let numLines = length contentLines @@ -724,20 +723,20 @@ addNewRole roleString fields = do -- warn about syntax we ignore flip mapM_ fields $ \(key, _) -> case key of - "language" -> when (baseRole /= "code") $ lift $ P.warning $ + "language" -> when (baseRole /= "code") $ P.warning $ "ignoring :language: field because the parent of role :" ++ role ++ ": is :" ++ baseRole ++ ": not :code:" - "format" -> when (baseRole /= "raw") $ lift $ P.warning $ + "format" -> when (baseRole /= "raw") $ P.warning $ "ignoring :format: field because the parent of role :" ++ role ++ ": is :" ++ baseRole ++ ": not :raw:" - _ -> lift $ P.warning $ "ignoring unknown field :" ++ key ++ + _ -> P.warning $ "ignoring unknown field :" ++ key ++ ": in definition of role :" ++ role ++ ": in" when (parentRole == "raw" && countKeys "format" > 1) $ - lift $ P.warning $ + P.warning $ "ignoring :format: fields after the first in the definition of role :" ++ role ++": in" when (parentRole == "code" && countKeys "language" > 1) $ - lift $ P.warning $ + P.warning $ "ignoring :language: fields after the first in the definition of role :" ++ role ++": in" -- cgit v1.2.3 From 40ac0cf133e2bb7f1504def48329bc67d2414225 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 8 Dec 2016 21:16:57 +0100 Subject: Whitespace. --- src/Text/Pandoc/Class.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index b4161b964..6beca82ba 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -252,7 +252,7 @@ data PureState = PureState { stStdGen :: StdGen , stFiles :: FileTree , stUserDataDir :: FileTree , stCabalDataDir :: FileTree - , stFontFiles :: [FilePath] + , stFontFiles :: [FilePath] } instance Default PureState where @@ -282,8 +282,8 @@ putPureState ps= PandocPure $ lift $ lift $ put ps modifyPureState :: (PureState -> PureState) -> PandocPure () modifyPureState f = PandocPure $ lift $ lift $ modify f - - + + data FileInfo = FileInfo { infoFileMTime :: UTCTime , infoFileContents :: B.ByteString } -- cgit v1.2.3 From 54932ade677b48ec42f6461028a3b58bb85aaa50 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 8 Dec 2016 21:32:25 +0100 Subject: Class: no more MonadState CommonState. - Added getCommonState, putCommonState, getsCommonState, modifyCommonState to PandocMonad interface. - Removed MonadState CommonState instances. --- src/Text/Pandoc/Class.hs | 37 +++++++++++++++++++++++-------------- tests/Tests/Readers/Txt2Tags.hs | 4 ++-- 2 files changed, 25 insertions(+), 16 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 6beca82ba..f6c4cd553 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -98,7 +98,7 @@ import System.IO.Error import qualified Data.Map as M import Text.Pandoc.Error -class (Functor m, Applicative m, Monad m, MonadError PandocError m, MonadState CommonState m) => PandocMonad m where +class (Functor m, Applicative m, Monad m, MonadError PandocError m) => PandocMonad m where lookupEnv :: String -> m (Maybe String) getCurrentTime :: m UTCTime getCurrentTimeZone :: m TimeZone @@ -120,32 +120,39 @@ class (Functor m, Applicative m, Monad m, MonadError PandocError m, MonadState C fail :: String -> m b glob :: String -> m [FilePath] getModificationTime :: FilePath -> m UTCTime + getCommonState :: m CommonState + putCommonState :: CommonState -> m () + getsCommonState :: (CommonState -> a) -> m a + getsCommonState f = f <$> getCommonState + modifyCommonState :: (CommonState -> CommonState) -> m () + modifyCommonState f = getCommonState >>= putCommonState . f -- Functions defined for all PandocMonad instances warning :: PandocMonad m => String -> m () -warning msg = modify $ \st -> st{stWarnings = msg : stWarnings st} +warning msg = modifyCommonState $ \st -> st{stWarnings = msg : stWarnings st} getWarnings :: PandocMonad m => m [String] -getWarnings = gets stWarnings +getWarnings = getsCommonState stWarnings setMediaBag :: PandocMonad m => MediaBag -> m () -setMediaBag mb = modify $ \st -> st{stMediaBag = mb} +setMediaBag mb = modifyCommonState $ \st -> st{stMediaBag = mb} getMediaBag :: PandocMonad m => m MediaBag -getMediaBag = gets stMediaBag +getMediaBag = getsCommonState stMediaBag insertMedia :: PandocMonad m => FilePath -> Maybe MimeType -> BL.ByteString -> m () insertMedia fp mime bs = - modify $ \st -> st{stMediaBag = MB.insertMedia fp mime bs (stMediaBag st) } + modifyCommonState $ \st -> + st{stMediaBag = MB.insertMedia fp mime bs (stMediaBag st) } getInputFiles :: PandocMonad m => m (Maybe [FilePath]) -getInputFiles = gets stInputFiles +getInputFiles = getsCommonState stInputFiles getOutputFile :: PandocMonad m => m (Maybe FilePath) -getOutputFile = gets stOutputFile +getOutputFile = getsCommonState stOutputFile getPOSIXTime :: (PandocMonad m) => m POSIXTime getPOSIXTime = utcTimeToPOSIXSeconds <$> getCurrentTime @@ -164,9 +171,6 @@ warningWithPos pos msg = lift $ warning $ msg ++ " " ++ show pos -- --- All PandocMonad instances should be an instance MonadState of this --- datatype: - data CommonState = CommonState { stWarnings :: [String] , stMediaBag :: MediaBag , stInputFiles :: Maybe [FilePath] @@ -201,7 +205,6 @@ newtype PandocIO a = PandocIO { , Functor , Applicative , Monad - , MonadState CommonState , MonadError PandocError ) @@ -233,7 +236,8 @@ instance PandocMonad PandocIO where case eitherMtime of Right mtime -> return mtime Left _ -> throwError $ PandocFileReadError fp - + getCommonState = PandocIO $ lift get + putCommonState x = PandocIO $ lift $ put x data PureState = PureState { stStdGen :: StdGen , stWord8Store :: [Word8] -- should be @@ -301,7 +305,6 @@ newtype PandocPure a = PandocPure { } deriving ( Functor , Applicative , Monad - , MonadState CommonState , MonadError PandocError ) @@ -376,6 +379,9 @@ instance PandocMonad PandocPure where Just tm -> return tm Nothing -> throwError $ PandocFileReadError fp + getCommonState = PandocPure $ lift $ get + putCommonState x = PandocPure $ lift $ put x + instance PandocMonad m => PandocMonad (ParserT s st m) where lookupEnv = lift . lookupEnv getCurrentTime = lift getCurrentTime @@ -391,3 +397,6 @@ instance PandocMonad m => PandocMonad (ParserT s st m) where fetchItem' media sourceUrl = lift . fetchItem' media sourceUrl glob = lift . glob getModificationTime = lift . getModificationTime + getCommonState = lift getCommonState + putCommonState = lift . putCommonState + diff --git a/tests/Tests/Readers/Txt2Tags.hs b/tests/Tests/Readers/Txt2Tags.hs index bef1d4965..77430601b 100644 --- a/tests/Tests/Readers/Txt2Tags.hs +++ b/tests/Tests/Readers/Txt2Tags.hs @@ -4,7 +4,6 @@ module Tests.Readers.Txt2Tags (tests) where import Text.Pandoc.Definition import Test.Framework import Tests.Helpers -import Control.Monad.State import Text.Pandoc.Arbitrary() import Text.Pandoc.Builder import Text.Pandoc @@ -15,7 +14,8 @@ import Text.Pandoc.Class t2t :: String -> Pandoc -- t2t = handleError . readTxt2Tags (T2TMeta "date" "mtime" "in" "out") def t2t = purely $ \s -> do - put def { stInputFiles = Just ["in"] + putCommonState + def { stInputFiles = Just ["in"] , stOutputFile = Just "out" } readTxt2Tags def s -- cgit v1.2.3 From 4111fdbaf0a21eb48177af8d9815f21008f505e9 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 8 Dec 2016 22:12:04 +0100 Subject: Instances of PandocMonad for common transformers. --- src/Text/Pandoc/Class.hs | 81 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index f6c4cd553..b1e05f42a 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -1,5 +1,6 @@ {-# LANGUAGE DeriveFunctor, DeriveDataTypeable, TypeSynonymInstances, -FlexibleInstances, GeneralizedNewtypeDeriving, FlexibleContexts #-} +FlexibleInstances, GeneralizedNewtypeDeriving, FlexibleContexts, +MultiParamTypeClasses, UndecidableInstances #-} {- Copyright (C) 2016 Jesse Rosenthal @@ -91,6 +92,9 @@ import System.FilePath (()) import qualified System.FilePath.Glob as IO (glob) import qualified System.Directory as IO (getModificationTime) import Control.Monad.State hiding (fail) +import Control.Monad.Reader (ReaderT) +import Control.Monad.Writer (WriterT) +import Control.Monad.RWS (RWST) import Control.Monad.Except hiding (fail) import Data.Word (Word8) import Data.Default @@ -98,7 +102,8 @@ import System.IO.Error import qualified Data.Map as M import Text.Pandoc.Error -class (Functor m, Applicative m, Monad m, MonadError PandocError m) => PandocMonad m where +class (Functor m, Applicative m, Monad m, MonadError PandocError m) + => PandocMonad m where lookupEnv :: String -> m (Maybe String) getCurrentTime :: m UTCTime getCurrentTimeZone :: m TimeZone @@ -400,3 +405,75 @@ instance PandocMonad m => PandocMonad (ParserT s st m) where getCommonState = lift getCommonState putCommonState = lift . putCommonState +instance PandocMonad m => PandocMonad (ReaderT r m) where + lookupEnv = lift . lookupEnv + getCurrentTime = lift getCurrentTime + getCurrentTimeZone = lift getCurrentTimeZone + getDefaultReferenceDocx = lift . getDefaultReferenceDocx + getDefaultReferenceODT = lift . getDefaultReferenceODT + newStdGen = lift newStdGen + newUniqueHash = lift newUniqueHash + readFileLazy = lift . readFileLazy + readDataFile mbuserdir = lift . readDataFile mbuserdir + fail = lift . fail + fetchItem media = lift . fetchItem media + fetchItem' media sourceUrl = lift . fetchItem' media sourceUrl + glob = lift . glob + getModificationTime = lift . getModificationTime + getCommonState = lift getCommonState + putCommonState = lift . putCommonState + +instance (PandocMonad m, Monoid w) => PandocMonad (WriterT w m) where + lookupEnv = lift . lookupEnv + getCurrentTime = lift getCurrentTime + getCurrentTimeZone = lift getCurrentTimeZone + getDefaultReferenceDocx = lift . getDefaultReferenceDocx + getDefaultReferenceODT = lift . getDefaultReferenceODT + newStdGen = lift newStdGen + newUniqueHash = lift newUniqueHash + readFileLazy = lift . readFileLazy + readDataFile mbuserdir = lift . readDataFile mbuserdir + fail = lift . fail + fetchItem media = lift . fetchItem media + fetchItem' media sourceUrl = lift . fetchItem' media sourceUrl + glob = lift . glob + getModificationTime = lift . getModificationTime + getCommonState = lift getCommonState + putCommonState = lift . putCommonState + +instance (PandocMonad m, Monoid w) => PandocMonad (RWST r w st m) where + lookupEnv = lift . lookupEnv + getCurrentTime = lift getCurrentTime + getCurrentTimeZone = lift getCurrentTimeZone + getDefaultReferenceDocx = lift . getDefaultReferenceDocx + getDefaultReferenceODT = lift . getDefaultReferenceODT + newStdGen = lift newStdGen + newUniqueHash = lift newUniqueHash + readFileLazy = lift . readFileLazy + readDataFile mbuserdir = lift . readDataFile mbuserdir + fail = lift . fail + fetchItem media = lift . fetchItem media + fetchItem' media sourceUrl = lift . fetchItem' media sourceUrl + glob = lift . glob + getModificationTime = lift . getModificationTime + getCommonState = lift getCommonState + putCommonState = lift . putCommonState + +instance PandocMonad m => PandocMonad (StateT st m) where + lookupEnv = lift . lookupEnv + getCurrentTime = lift getCurrentTime + getCurrentTimeZone = lift getCurrentTimeZone + getDefaultReferenceDocx = lift . getDefaultReferenceDocx + getDefaultReferenceODT = lift . getDefaultReferenceODT + newStdGen = lift newStdGen + newUniqueHash = lift newUniqueHash + readFileLazy = lift . readFileLazy + readDataFile mbuserdir = lift . readDataFile mbuserdir + fail = lift . fail + fetchItem media = lift . fetchItem media + fetchItem' media sourceUrl = lift . fetchItem' media sourceUrl + glob = lift . glob + getModificationTime = lift . getModificationTime + getCommonState = lift getCommonState + putCommonState = lift . putCommonState + -- cgit v1.2.3 From bc61c6a632ea8d3e39399074c6e447a9a17b0c94 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 8 Dec 2016 22:16:35 +0100 Subject: Remove now-unnecessary lifts in Markdown writer. Other writers still TBD. --- src/Text/Pandoc/Writers/Markdown.hs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 66e0365d8..7f4d37b1f 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -200,7 +200,7 @@ pandocToMarkdown opts (Pandoc meta blocks) = do Nothing -> empty let headerBlocks = filter isHeaderBlock blocks toc <- if writerTableOfContents opts - then liftPandoc $ tableOfContents opts headerBlocks + then tableOfContents opts headerBlocks else return empty -- Strip off final 'references' header if markdown citations enabled let blocks' = if isEnabled Ext_citations opts @@ -533,7 +533,7 @@ blockToMarkdown' opts t@(Table caption aligns widths headers rows) = do rawHeaders rawRows | isEnabled Ext_raw_html opts -> fmap (id,) $ text <$> - (liftPandoc $ writeHtmlString def $ Pandoc nullMeta [t]) + (writeHtmlString def $ Pandoc nullMeta [t]) | otherwise -> return $ (id, text "[TABLE]") return $ nst $ tbl $$ blankline $$ caption'' $$ blankline blockToMarkdown' opts (BulletList items) = do @@ -985,7 +985,7 @@ inlineToMarkdown opts (Math InlineMath str) = return $ "\\\\(" <> text str <> "\\\\)" | otherwise -> do plain <- asks envPlain - (liftPandoc (texMathToInlines InlineMath str)) >>= + texMathToInlines InlineMath str >>= inlineListToMarkdown opts . (if plain then makeMathPlainer else id) inlineToMarkdown opts (Math DisplayMath str) = @@ -1000,8 +1000,7 @@ inlineToMarkdown opts (Math DisplayMath str) = | isEnabled Ext_tex_math_double_backslash opts -> return $ "\\\\[" <> text str <> "\\\\]" | otherwise -> (\x -> cr <> x <> cr) `fmap` - (liftPandoc (texMathToInlines DisplayMath str) >>= - inlineListToMarkdown opts) + texMathToInlines DisplayMath str >>= inlineListToMarkdown opts inlineToMarkdown opts (RawInline f str) = do plain <- asks envPlain if not plain && @@ -1063,7 +1062,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 - (text . trim) <$> (liftPandoc $ writeHtmlString def $ Pandoc nullMeta [Plain [lnk]]) + (text . trim) <$> writeHtmlString def (Pandoc nullMeta [Plain [lnk]]) | otherwise = do plain <- asks envPlain linktext <- inlineListToMarkdown opts txt @@ -1102,7 +1101,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 - (text . trim) <$> (liftPandoc $ writeHtmlString def $ Pandoc nullMeta [Plain [img]]) + (text . trim) <$> writeHtmlString def (Pandoc nullMeta [Plain [img]]) | otherwise = do plain <- asks envPlain let txt = if null alternate || alternate == [Str source] @@ -1127,5 +1126,3 @@ makeMathPlainer = walk go go (Emph xs) = Span nullAttr xs go x = x -liftPandoc :: PandocMonad m => m a -> MD m a -liftPandoc = lift . lift -- cgit v1.2.3 From f328cfe6a71a7a0e1a316a2fe9bf32440708140e Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 8 Dec 2016 22:47:14 +0100 Subject: Removed unneeded pragmas. --- src/Text/Pandoc/Class.hs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index b1e05f42a..7f86e27b1 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -1,6 +1,5 @@ {-# LANGUAGE DeriveFunctor, DeriveDataTypeable, TypeSynonymInstances, -FlexibleInstances, GeneralizedNewtypeDeriving, FlexibleContexts, -MultiParamTypeClasses, UndecidableInstances #-} +FlexibleInstances, GeneralizedNewtypeDeriving, FlexibleContexts #-} {- Copyright (C) 2016 Jesse Rosenthal -- cgit v1.2.3 From da2055d709eec172d234f65e0aa9c75a7cfa9f30 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 8 Dec 2016 23:04:43 +0100 Subject: RST reader: rebase-related fixes to warnings. --- src/Text/Pandoc/Readers/RST.hs | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 22478de72..75cd03d30 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -50,8 +50,7 @@ import Data.Sequence (viewr, ViewR(..)) import Data.Char (toLower, isHexDigit, isSpace) import Data.Monoid ((<>)) import Control.Monad.Except (throwError, catchError) -import Text.Pandoc.Class (PandocMonad, warning, readFileLazy) -import qualified Text.Pandoc.Class as P +import Text.Pandoc.Class (PandocMonad, warning, readFileLazy, warningWithPos) -- | Parse reStructuredText string and return Pandoc document. readRST :: PandocMonad m @@ -695,7 +694,7 @@ directive' = do return $ B.divWith attrs children other -> do pos <- getPosition - P.warningWithPos pos $ "ignoring unknown directive: " ++ other + warningWithPos pos $ "ignoring unknown directive: " ++ other return mempty -- TODO: @@ -723,20 +722,20 @@ addNewRole roleString fields = do -- warn about syntax we ignore flip mapM_ fields $ \(key, _) -> case key of - "language" -> when (baseRole /= "code") $ P.warning $ + "language" -> when (baseRole /= "code") $ warning $ "ignoring :language: field because the parent of role :" ++ role ++ ": is :" ++ baseRole ++ ": not :code:" - "format" -> when (baseRole /= "raw") $ P.warning $ + "format" -> when (baseRole /= "raw") $ warning $ "ignoring :format: field because the parent of role :" ++ role ++ ": is :" ++ baseRole ++ ": not :raw:" - _ -> P.warning $ "ignoring unknown field :" ++ key ++ + _ -> warning $ "ignoring unknown field :" ++ key ++ ": in definition of role :" ++ role ++ ": in" when (parentRole == "raw" && countKeys "format" > 1) $ - P.warning $ + warning $ "ignoring :format: fields after the first in the definition of role :" ++ role ++": in" when (parentRole == "code" && countKeys "language" > 1) $ - P.warning $ + warning $ "ignoring :language: fields after the first in the definition of role :" ++ role ++": in" @@ -1134,7 +1133,7 @@ renderRole contents fmt role attr = case role of renderRole contents newFmt newRole newAttr Nothing -> do pos <- getPosition - P.warningWithPos pos $ "ignoring unknown role :" ++ custom ++ ": in" + warningWithPos pos $ "ignoring unknown role :" ++ custom ++ ": in" return $ B.str contents -- Undefined role where titleRef ref = return $ B.str ref -- FIXME: Not a sensible behaviour @@ -1219,7 +1218,7 @@ explicitLink = try $ do case M.lookup key keyTable of Nothing -> do pos <- getPosition - addWarning (Just pos) $ + warningWithPos pos $ "Could not find reference for " ++ show key return ("","",nullAttr) @@ -1244,7 +1243,7 @@ referenceLink = try $ do ((src,tit), attr) <- case M.lookup key keyTable of Nothing -> do pos <- getPosition - addWarning (Just pos) $ + warningWithPos pos $ "Could not find reference for " ++ show key return (("",""),nullAttr) @@ -1275,7 +1274,7 @@ subst = try $ do case M.lookup key substTable of Nothing -> do pos <- getPosition - addWarning (Just pos) $ + warningWithPos pos $ "Could not find reference for " ++ show key return mempty Just target -> return target @@ -1290,7 +1289,7 @@ note = try $ do case lookup ref notes of Nothing -> do pos <- getPosition - addWarning (Just pos) $ + warningWithPos pos $ "Could not find note for " ++ show ref return mempty Just raw -> do -- cgit v1.2.3 From 9570f59066c1e89500fcd8ab6ac6a401159ece27 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 9 Dec 2016 15:59:03 +0100 Subject: Process.pipeProcess: stream stderr rather than capturing. Signature of pipeProcess has changed: the return value is now IO (ExitCode, ByteString) -- with only stdout. Stderr is just inherited from the parent. This means that stderr from filters will now be streamed as the filters are run. Closes #2729. --- pandoc.hs | 3 +-- src/Text/Pandoc/PDF.hs | 20 +++++++------------- src/Text/Pandoc/Process.hs | 22 ++++++++-------------- 3 files changed, 16 insertions(+), 29 deletions(-) (limited to 'src/Text') diff --git a/pandoc.hs b/pandoc.hs index c979016f4..f1a9aac05 100644 --- a/pandoc.hs +++ b/pandoc.hs @@ -137,9 +137,8 @@ externalFilter f args' d = do "Could not find executable '" ++ f' ++ "'." env <- getEnvironment let env' = Just $ ("PANDOC_VERSION", pandocVersion) : env - (exitcode, outbs, errbs) <- E.handle filterException $ + (exitcode, outbs) <- E.handle filterException $ pipeProcess env' f' args'' $ encode d - unless (B.null errbs) $ B.hPutStr stderr errbs case exitcode of ExitSuccess -> return $ either error id $ eitherDecode' outbs ExitFailure ec -> err 83 $ "Error running filter " ++ f ++ "\n" ++ diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index 7aaa257fa..d1d1c803c 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -37,7 +37,7 @@ import qualified Data.ByteString as BS import Data.Monoid ((<>)) import System.Exit (ExitCode (..)) import System.FilePath -import System.IO (stderr, stdout) +import System.IO (stdout) import System.IO.Temp (withTempFile) import System.Directory import Data.Digest.Pure.SHA (showDigest, sha1) @@ -247,11 +247,10 @@ runTeXProgram verbose program args runNumber numRuns tmpDir source = do putStrLn $ "[makePDF] Contents of " ++ file' ++ ":" B.readFile file' >>= B.putStr putStr "\n" - (exit, out, err) <- pipeProcess (Just env'') program programArgs BL.empty + (exit, out) <- pipeProcess (Just env'') program programArgs BL.empty when verbose $ do putStrLn $ "[makePDF] Run #" ++ show runNumber B.hPutStr stdout out - B.hPutStr stderr err putStr "\n" if runNumber <= numRuns then runTeXProgram verbose program args (runNumber + 1) numRuns tmpDir source @@ -264,7 +263,7 @@ runTeXProgram verbose program args runNumber numRuns tmpDir source = do -- See https://github.com/jgm/pandoc/issues/1192. then (Just . B.fromChunks . (:[])) `fmap` BS.readFile pdfFile else return Nothing - return (exit, out <> err, pdf) + return (exit, out, pdf) html2pdf :: Bool -- ^ Verbose output -> [String] -- ^ Args to wkhtmltopdf @@ -286,12 +285,10 @@ html2pdf verbose args source = do putStrLn $ "[makePDF] Contents of " ++ file ++ ":" B.readFile file >>= B.putStr putStr "\n" - (exit, out, err) <- pipeProcess (Just env') "wkhtmltopdf" - programArgs BL.empty + (exit, out) <- pipeProcess (Just env') "wkhtmltopdf" programArgs BL.empty removeFile file when verbose $ do B.hPutStr stdout out - B.hPutStr stderr err putStr "\n" pdfExists <- doesFileExist pdfFile mbPdf <- if pdfExists @@ -303,9 +300,8 @@ html2pdf verbose args source = do removeFile pdfFile return res else return Nothing - let log' = out <> err return $ case (exit, mbPdf) of - (ExitFailure _, _) -> Left log' + (ExitFailure _, _) -> Left out (ExitSuccess, Nothing) -> Left "" (ExitSuccess, Just pdf) -> Right pdf @@ -341,10 +337,9 @@ context2pdf verbose tmpDir source = inDirectory tmpDir $ do putStrLn $ "[makePDF] Contents of " ++ file ++ ":" B.readFile file >>= B.putStr putStr "\n" - (exit, out, err) <- pipeProcess (Just env'') "context" programArgs BL.empty + (exit, out) <- pipeProcess (Just env'') "context" programArgs BL.empty when verbose $ do B.hPutStr stdout out - B.hPutStr stderr err putStr "\n" let pdfFile = replaceExtension file ".pdf" pdfExists <- doesFileExist pdfFile @@ -354,10 +349,9 @@ context2pdf verbose tmpDir source = inDirectory tmpDir $ do -- See https://github.com/jgm/pandoc/issues/1192. then (Just . B.fromChunks . (:[])) `fmap` BS.readFile pdfFile else return Nothing - let log' = out <> err case (exit, mbPdf) of (ExitFailure _, _) -> do - let logmsg = extractConTeXtMsg log' + let logmsg = extractConTeXtMsg out return $ Left logmsg (ExitSuccess, Nothing) -> return $ Left "" (ExitSuccess, Just pdf) -> return $ Right pdf diff --git a/src/Text/Pandoc/Process.hs b/src/Text/Pandoc/Process.hs index bc71f1392..294a38a1b 100644 --- a/src/Text/Pandoc/Process.hs +++ b/src/Text/Pandoc/Process.hs @@ -42,9 +42,9 @@ Version of 'System.Process.readProcessWithExitCode' that uses lazy bytestrings instead of strings and allows setting environment variables. @readProcessWithExitCode@ creates an external process, reads its -standard output and standard error strictly, waits until the process -terminates, and then returns the 'ExitCode' of the process, -the standard output, and the standard error. +standard output strictly, waits until the process +terminates, and then returns the 'ExitCode' of the process +and the standard output. stderr is inherited from the parent. If an asynchronous exception is thrown to the thread executing @readProcessWithExitCode@, the forked process will be terminated and @@ -57,25 +57,21 @@ pipeProcess -> FilePath -- ^ Filename of the executable (see 'proc' for details) -> [String] -- ^ any arguments -> BL.ByteString -- ^ standard input - -> IO (ExitCode,BL.ByteString,BL.ByteString) -- ^ exitcode, stdout, stderr + -> IO (ExitCode,BL.ByteString) -- ^ exitcode, stdout pipeProcess mbenv cmd args input = mask $ \restore -> do - (Just inh, Just outh, Just errh, pid) <- createProcess (proc cmd args) + (Just inh, Just outh, Nothing, pid) <- createProcess (proc cmd args) { env = mbenv, std_in = CreatePipe, std_out = CreatePipe, - std_err = CreatePipe } + std_err = Inherit } flip onException - (do hClose inh; hClose outh; hClose errh; + (do hClose inh; hClose outh; terminateProcess pid; waitForProcess pid) $ restore $ do -- fork off a thread to start consuming stdout out <- BL.hGetContents outh waitOut <- forkWait $ evaluate $ BL.length out - -- fork off a thread to start consuming stderr - err <- BL.hGetContents errh - waitErr <- forkWait $ evaluate $ BL.length err - -- now write and flush any input let writeInput = do unless (BL.null input) $ do @@ -87,15 +83,13 @@ pipeProcess mbenv cmd args input = -- wait on the output waitOut - waitErr hClose outh - hClose errh -- wait on the process ex <- waitForProcess pid - return (ex, out, err) + return (ex, out) forkWait :: IO a -> IO (IO a) forkWait a = do -- cgit v1.2.3 From ce1664cf2ba29c8b973d7a228744b43144c0859d Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 10 Dec 2016 10:39:44 +0100 Subject: Simplified reference-docx/reference-odt to reference-doc. * Text.Pandoc.Options.WriterOptions: removed writerReferenceDocx and writerReferenceODT, replaced them with writerReferenceDoc. This can hold either an ODT or a Docx. In this way, writerReferenceDoc is like writerTemplate, which can hold templates of different formats. [API change] * Removed `--reference-docx` and `--reference-odt` options. * Added `--reference-doc` option. --- MANUAL.txt | 74 ++++++++++++++++++++--------------------- pandoc.hs | 25 ++++---------- src/Text/Pandoc/Options.hs | 6 ++-- src/Text/Pandoc/Writers/Docx.hs | 2 +- src/Text/Pandoc/Writers/ODT.hs | 2 +- tests/Tests/Writers/Docx.hs | 2 +- 6 files changed, 49 insertions(+), 62 deletions(-) (limited to 'src/Text') diff --git a/MANUAL.txt b/MANUAL.txt index c9b6c0fb1..5d53c192d 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -794,35 +794,20 @@ Options affecting specific writers : Link to a CSS style sheet. This option can be used repeatedly to include multiple files. They will be included in the order specified. -`--reference-odt=`*FILE* +`--reference-doc=`*FILE* -: Use the specified file as a style reference in producing an ODT. - For best results, the reference ODT should be a modified version - of an ODT produced using pandoc. The contents of the reference ODT - are ignored, but its stylesheets are used in the new ODT. If no - reference ODT is specified on the command line, pandoc will look - for a file `reference.odt` in the user data directory (see - `--data-dir`). If this is not found either, sensible defaults will be - used. +: Use the specified file as a style reference in producing a + docx or ODT file. - To produce a custom `reference.odt`, first get a copy of - the default `reference.odt`: `pandoc - --print-default-data-file reference.odt > - custom-reference.odt`. Then open `custom-reference.docx` in - LibreOffice, modify the styles as you wish, and save the - file. - -`--reference-docx=`*FILE* - -: Use the specified file as a style reference in producing a docx file. - For best results, the reference docx should be a modified version - of a docx file produced using pandoc. The contents of the reference docx - are ignored, but its stylesheets and document properties (including - margins, page size, header, and footer) are used in the new docx. If no - reference docx is specified on the command line, pandoc will look - for a file `reference.docx` in the user data directory (see - `--data-dir`). If this is not found either, sensible defaults will be - used. + Docx: For best results, the reference docx should be a modified + version of a docx file produced using pandoc. The contents + of the reference docx are ignored, but its stylesheets and + document properties (including margins, page size, header, + and footer) are used in the new docx. If no reference docx + is specified on the command line, pandoc will look for a + file `reference.docx` in the user data directory (see + `--data-dir`). If this is not found either, sensible + defaults will be used. To produce a custom `reference.docx`, first get a copy of the default `reference.docx`: `pandoc @@ -830,15 +815,30 @@ Options affecting specific writers custom-reference.docx`. Then open `custom-reference.docx` in Word, modify the styles as you wish, and save the file. For best results, do not make changes to this file other - than modifying the styles used by pandoc: [paragraph] Normal, - Body Text, First Paragraph, Compact, Title, Subtitle, - Author, Date, Abstract, Bibliography, Heading 1, Heading 2, - Heading 3, Heading 4, Heading 5, Heading 6, Block Text, - Footnote Text, Definition Term, Definition, Caption, Table - Caption, Image Caption, Figure, Figure With Caption, TOC - Heading; [character] Default Paragraph Font, Body Text Char, - Verbatim Char, Footnote Reference, Hyperlink; [table] Normal - Table. + than modifying the styles used by pandoc: [paragraph] + Normal, Body Text, First Paragraph, Compact, Title, + Subtitle, Author, Date, Abstract, Bibliography, Heading 1, + Heading 2, Heading 3, Heading 4, Heading 5, Heading 6, Block + Text, Footnote Text, Definition Term, Definition, Caption, + Table Caption, Image Caption, Figure, Figure With Caption, + TOC Heading; [character] Default Paragraph Font, Body Text + Char, Verbatim Char, Footnote Reference, Hyperlink; [table] + Normal Table. + + ODT: For best results, the reference ODT should be a modified + version of an ODT produced using pandoc. The contents of + the reference ODT are ignored, but its stylesheets are used + in the new ODT. If no reference ODT is specified on the + command line, pandoc will look for a file `reference.odt` in + the user data directory (see `--data-dir`). If this is not + found either, sensible defaults will be used. + + To produce a custom `reference.odt`, first get a copy of + the default `reference.odt`: `pandoc + --print-default-data-file reference.odt > + custom-reference.odt`. Then open `custom-reference.docx` in + LibreOffice, modify the styles as you wish, and save the + file. `--epub-stylesheet=`*FILE* @@ -1099,7 +1099,7 @@ directory (see `--data-dir`, above). *Exceptions:* (or the `default.beamer` template, if you use `-t beamer`, or the `default.context` template, if you use `-t context`). - `docx` has no template (however, you can use - `--reference-docx` to customize the output). + `--reference-doc` to customize the output). Templates contain *variables*, which allow for the inclusion of arbitrary information at any point in the file. Variables may be set diff --git a/pandoc.hs b/pandoc.hs index f1a9aac05..f5d5e0da6 100644 --- a/pandoc.hs +++ b/pandoc.hs @@ -185,8 +185,7 @@ data Opt = Opt , optHighlightStyle :: Style -- ^ Style to use for highlighted code , optTopLevelDivision :: TopLevelDivision -- ^ Type of the top-level divisions , optHTMLMathMethod :: HTMLMathMethod -- ^ Method to print HTML math - , optReferenceODT :: Maybe FilePath -- ^ Path of reference.odt - , optReferenceDocx :: Maybe FilePath -- ^ Path of reference.docx + , optReferenceDoc :: Maybe FilePath -- ^ Path of reference doc , optEpubStylesheet :: Maybe String -- ^ EPUB stylesheet , optEpubMetadata :: String -- ^ EPUB metadata , optEpubFonts :: [FilePath] -- ^ EPUB fonts to embed @@ -252,8 +251,7 @@ defaultOpts = Opt , optHighlightStyle = pygments , optTopLevelDivision = TopLevelDefault , optHTMLMathMethod = PlainMath - , optReferenceODT = Nothing - , optReferenceDocx = Nothing + , optReferenceDoc = Nothing , optEpubStylesheet = Nothing , optEpubMetadata = "" , optEpubFonts = [] @@ -708,19 +706,12 @@ options = "URL") "" -- "Link to CSS style sheet" - , Option "" ["reference-odt"] + , Option "" ["reference-doc"] (ReqArg (\arg opt -> - return opt { optReferenceODT = Just arg }) + return opt { optReferenceDoc = Just arg }) "FILENAME") - "" -- "Path of custom reference.odt" - - , Option "" ["reference-docx"] - (ReqArg - (\arg opt -> - return opt { optReferenceDocx = Just arg }) - "FILENAME") - "" -- "Path of custom reference.docx" + "" -- "Path of custom reference doc" , Option "" ["epub-stylesheet"] (ReqArg @@ -1190,8 +1181,7 @@ convertWithOpts opts args = do , optHighlightStyle = highlightStyle , optTopLevelDivision = topLevelDivision , optHTMLMathMethod = mathMethod' - , optReferenceODT = referenceODT - , optReferenceDocx = referenceDocx + , optReferenceDoc = referenceDoc , optEpubStylesheet = epubStylesheet , optEpubMetadata = epubMetadata , optEpubFonts = epubFonts @@ -1485,8 +1475,7 @@ convertWithOpts opts args = do writerEpubFonts = epubFonts, writerEpubChapterLevel = epubChapterLevel, writerTOCDepth = epubTOCDepth, - writerReferenceODT = referenceODT, - writerReferenceDocx = referenceDocx, + writerReferenceDoc = referenceDoc, writerMediaBag = media, writerVerbose = verbose, writerLaTeXArgs = latexEngineArgs diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index 48bc5f4eb..4fee577e7 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -395,8 +395,7 @@ data WriterOptions = WriterOptions , writerEpubFonts :: [FilePath] -- ^ Paths to fonts to embed , writerEpubChapterLevel :: Int -- ^ Header level for chapters (separate files) , writerTOCDepth :: Int -- ^ Number of levels to include in TOC - , writerReferenceODT :: Maybe FilePath -- ^ Path to reference ODT if specified - , writerReferenceDocx :: Maybe FilePath -- ^ Path to reference DOCX if specified + , writerReferenceDoc :: Maybe FilePath -- ^ Path to reference document if specified , writerMediaBag :: MediaBag -- ^ Media collected by docx or epub reader , writerVerbose :: Bool -- ^ Verbose debugging output , writerLaTeXArgs :: [String] -- ^ Flags to pass to latex-engine @@ -442,8 +441,7 @@ instance Default WriterOptions where , writerEpubFonts = [] , writerEpubChapterLevel = 1 , writerTOCDepth = 3 - , writerReferenceODT = Nothing - , writerReferenceDocx = Nothing + , writerReferenceDoc = Nothing , writerMediaBag = mempty , writerVerbose = False , writerLaTeXArgs = [] diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 0f040d19b..20320907e 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -225,7 +225,7 @@ writeDocx opts doc@(Pandoc meta _) = do username <- P.lookupEnv "USERNAME" utctime <- P.getCurrentTime distArchive <- P.getDefaultReferenceDocx datadir - refArchive <- case writerReferenceDocx opts of + refArchive <- case writerReferenceDoc opts of Just f -> toArchive <$> P.readFileLazy f Nothing -> P.getDefaultReferenceDocx datadir diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index b17b18a21..a1a1c4f62 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -77,7 +77,7 @@ pandocToODT opts doc@(Pandoc meta _) = do let datadir = writerUserDataDir opts let title = docTitle meta refArchive <- - case writerReferenceODT opts of + case writerReferenceDoc opts of Just f -> liftM toArchive $ lift $ P.readFileLazy f Nothing -> lift $ P.getDefaultReferenceODT datadir -- handle formulas and pictures diff --git a/tests/Tests/Writers/Docx.hs b/tests/Tests/Writers/Docx.hs index 548e9ddcf..44095925f 100644 --- a/tests/Tests/Writers/Docx.hs +++ b/tests/Tests/Writers/Docx.hs @@ -141,7 +141,7 @@ tests = [ testGroup "inlines" ] , testGroup "customized styles" [ testCompareWithOpts - ( def{writerReferenceDocx=Just "docx/custom-style-reference.docx"} + ( def{writerReferenceDoc=Just "docx/custom-style-reference.docx"} , def) "simple customized blocks and inlines" "docx/custom-style-roundtrip-start.native" -- cgit v1.2.3 From f91a6b541fa4f05377dc48e9552584743eb9734b Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 10 Dec 2016 10:46:01 +0100 Subject: Removed deprecated toJsonFilter. Use toJSONFilter from Text.Pandoc.JSON. --- src/Text/Pandoc.hs | 6 ------ 1 file changed, 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index 4990a77fe..99ad76cda 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -378,12 +378,6 @@ getWriter s \o -> r o{ writerExtensions = setExts $ getDefaultExtensions writerName } -{-# DEPRECATED toJsonFilter "Use 'toJSONFilter' from 'Text.Pandoc.JSON' instead" #-} --- | Deprecated. Use @toJSONFilter@ from @Text.Pandoc.JSON@ instead. -class ToJSONFilter a => ToJsonFilter a - where toJsonFilter :: a -> IO () - toJsonFilter = toJSONFilter - readJSON :: ReaderOptions -> String -> Either PandocError Pandoc readJSON _ = mapLeft PandocParseError . eitherDecode' . UTF8.fromStringLazy -- cgit v1.2.3 From f1ef0e364578044873d242ba7bc8baaad27e2ee1 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 10 Dec 2016 10:47:37 +0100 Subject: Finished previous commit; removed export of toJsonFilter. --- src/Text/Pandoc.hs | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index 99ad76cda..013a9d9ac 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -124,13 +124,11 @@ module Text.Pandoc , getReader , getWriter , getDefaultExtensions - , ToJsonFilter(..) , pandocVersion ) where import Text.Pandoc.Definition import Text.Pandoc.Generic -import Text.Pandoc.JSON import Text.Pandoc.Readers.Markdown import Text.Pandoc.Readers.CommonMark import Text.Pandoc.Readers.MediaWiki -- cgit v1.2.3 From 2b24c6ff3aa3b6a0b1778be10595b691c21bc70f Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 10 Dec 2016 11:40:14 +0100 Subject: Shared: put err into MonadIO. --- src/Text/Pandoc/Shared.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 4420199f2..3df016996 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -999,8 +999,8 @@ openURL u -- Error reporting -- -err :: Int -> String -> IO a -err exitCode msg = do +err :: MonadIO m => Int -> String -> m a +err exitCode msg = liftIO $ do UTF8.hPutStrLn stderr msg exitWith $ ExitFailure exitCode return undefined -- cgit v1.2.3 From 753c14cb63fb17d6a19f4e20a31c2a1f5474bc43 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 10 Dec 2016 12:21:18 +0100 Subject: PDF: put makePDF in MonadIO. --- src/Text/Pandoc/PDF.hs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index d1d1c803c..348f6a2fe 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -55,6 +55,7 @@ import Text.Pandoc.Writers.Shared (getField, metaToJSON) import Text.Pandoc.Options (WriterOptions(..), HTMLMathMethod(..)) import Text.Pandoc.MIME (extensionFromMimeType, getMimeType) import Text.Pandoc.Process (pipeProcess) +import Control.Monad.Trans (MonadIO(..)) import qualified Data.ByteString.Lazy as BL import qualified Codec.Picture as JP #ifdef _WINDOWS @@ -67,13 +68,14 @@ changePathSeparators :: FilePath -> FilePath changePathSeparators = intercalate "/" . splitDirectories #endif -makePDF :: String -- ^ pdf creator (pdflatex, lualatex, +makePDF :: MonadIO m + => String -- ^ pdf creator (pdflatex, lualatex, -- xelatex, context, wkhtmltopdf) -> (WriterOptions -> Pandoc -> PandocIO String) -- ^ writer -> WriterOptions -- ^ options -> Pandoc -- ^ document - -> IO (Either ByteString ByteString) -makePDF "wkhtmltopdf" writer opts doc@(Pandoc meta _) = do + -> m (Either ByteString ByteString) +makePDF "wkhtmltopdf" writer opts doc@(Pandoc meta _) = liftIO $ do let mathArgs = case writerHTMLMathMethod opts of -- with MathJax, wait til all math is rendered: MathJax _ -> ["--run-script", "MathJax.Hub.Register.StartupHook('End Typeset', function() { window.status = 'mathjax_loaded' });", @@ -96,7 +98,7 @@ makePDF "wkhtmltopdf" writer opts doc@(Pandoc meta _) = do ] source <- runIOorExplode $ writer opts doc html2pdf (writerVerbose opts) args source -makePDF program writer opts doc = withTempDir "tex2pdf." $ \tmpdir -> do +makePDF program writer opts doc = liftIO $ withTempDir "tex2pdf." $ \tmpdir -> do doc' <- handleImages opts tmpdir doc source <- runIOorExplode $ writer opts doc' let args = writerLaTeXArgs opts -- cgit v1.2.3 From cf7d7f533a998576099c6879d0d0c50ecd8cb7dc Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 10 Dec 2016 12:21:32 +0100 Subject: SelfContained: put makeSelfContained in MonadIO. --- src/Text/Pandoc/SelfContained.hs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/SelfContained.hs b/src/Text/Pandoc/SelfContained.hs index d08d636df..6bcdc8728 100644 --- a/src/Text/Pandoc/SelfContained.hs +++ b/src/Text/Pandoc/SelfContained.hs @@ -40,6 +40,7 @@ import System.FilePath (takeExtension, takeDirectory, ()) import Data.Char (toLower, isAscii, isAlphaNum) import Codec.Compression.GZip as Gzip import qualified Data.ByteString.Lazy as L +import Control.Monad.Trans (MonadIO(..)) import Text.Pandoc.Shared (renderTags', err, fetchItem', warn, trim) import Text.Pandoc.MediaBag (MediaBag) import Text.Pandoc.MIME (MimeType) @@ -171,8 +172,8 @@ getDataURI media sourceURL mimetype src = do -- | Convert HTML into self-contained HTML, incorporating images, -- scripts, and CSS using data: URIs. -makeSelfContained :: WriterOptions -> String -> IO String -makeSelfContained opts inp = do +makeSelfContained :: MonadIO m => WriterOptions -> String -> m String +makeSelfContained opts inp = liftIO $ do let tags = parseTags inp out' <- mapM (convertTag (writerMediaBag opts) (writerSourceURL opts)) tags return $ renderTags' out' -- cgit v1.2.3 From dcccf65f3303475d9d5fdd8d49226190b9d11089 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 10 Dec 2016 12:24:04 +0100 Subject: MediaBag: put extractMediaBag into MonadIO. --- src/Text/Pandoc/MediaBag.hs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/MediaBag.hs b/src/Text/Pandoc/MediaBag.hs index eea25fadf..fe99be5fe 100644 --- a/src/Text/Pandoc/MediaBag.hs +++ b/src/Text/Pandoc/MediaBag.hs @@ -42,6 +42,7 @@ import System.Directory (createDirectoryIfMissing) import qualified Data.Map as M import qualified Data.ByteString.Lazy as BL import Control.Monad (when) +import Control.Monad.Trans (MonadIO(..)) import Text.Pandoc.MIME (MimeType, getMimeTypeDef) import qualified Text.Pandoc.UTF8 as UTF8 import Data.Maybe (fromMaybe) @@ -88,11 +89,14 @@ mediaDirectory (MediaBag mediamap) = -- | Extract contents of MediaBag to a given directory. Print informational -- messages if 'verbose' is true. -extractMediaBag :: Bool +-- TODO: eventually we may want to put this into PandocMonad +-- In PandocPure, it could write to the fake file system... +extractMediaBag :: MonadIO m + => Bool -> FilePath -> MediaBag - -> IO () -extractMediaBag verbose dir (MediaBag mediamap) = do + -> m () +extractMediaBag verbose dir (MediaBag mediamap) = liftIO $ do sequence_ $ M.foldWithKey (\fp (_ ,contents) -> ((writeMedia verbose dir (Posix.joinPath fp, contents)):)) [] mediamap -- cgit v1.2.3 From 2e7b0c7edaac9fbba52ac3cbc6380dbfb74805cf Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 10 Dec 2016 16:52:35 +0100 Subject: Added ReaderOptions parameter to readNative. This makes it similar to the other readers -- even though ReaderOptions is essentially ignored, the uniformity is nice. --- src/Text/Pandoc.hs | 2 +- src/Text/Pandoc/Readers/Native.hs | 6 ++++-- tests/Tests/Old.hs | 2 +- tests/Tests/Readers/Docx.hs | 2 +- tests/Tests/Readers/Odt.hs | 2 +- tests/Tests/Writers/Docx.hs | 4 ++-- 6 files changed, 10 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index 013a9d9ac..e5fc665a7 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -238,7 +238,7 @@ data Reader m = StringReader (ReaderOptions -> String -> m Pandoc) -- | Association list of formats and readers. readers :: PandocMonad m => [(String, Reader m)] -readers = [ ("native" , StringReader $ \_ s -> readNative s) +readers = [ ("native" , StringReader readNative) ,("json" , StringReader $ \o s -> case readJSON o s of Right doc -> return doc diff --git a/src/Text/Pandoc/Readers/Native.hs b/src/Text/Pandoc/Readers/Native.hs index 3e934e43f..1953c0c83 100644 --- a/src/Text/Pandoc/Readers/Native.hs +++ b/src/Text/Pandoc/Readers/Native.hs @@ -32,6 +32,7 @@ module Text.Pandoc.Readers.Native ( readNative ) where import Text.Pandoc.Definition import Text.Pandoc.Shared (safeRead) +import Text.Pandoc.Options (ReaderOptions) import Control.Monad.Except (throwError) import Text.Pandoc.Error @@ -48,9 +49,10 @@ import Text.Pandoc.Class -- > Pandoc nullMeta [Plain [Str "hi"]] -- readNative :: PandocMonad m - => String -- ^ String to parse (assuming @'\n'@ line endings) + => ReaderOptions + -> String -- ^ String to parse (assuming @'\n'@ line endings) -> m Pandoc -readNative s = +readNative _ s = case maybe (Pandoc nullMeta <$> readBlocks s) Right (safeRead s) of Right doc -> return doc Left _ -> throwError $ PandocParseError "couldn't read native" diff --git a/tests/Tests/Old.hs b/tests/Tests/Old.hs index 04612d49d..cc35c8aa0 100644 --- a/tests/Tests/Old.hs +++ b/tests/Tests/Old.hs @@ -196,7 +196,7 @@ lhsReaderTest format = testWithNormalize normalizer "lhs" ["-r", format, "-w", "native"] ("lhs-test" <.> format) norm where normalizer = purely $ \nat -> do - d <- readNative nat + d <- readNative def nat writeNative def $ normalize d norm = if format == "markdown+lhs" then "lhs-test-markdown.native" diff --git a/tests/Tests/Readers/Docx.hs b/tests/Tests/Readers/Docx.hs index 22fdf575a..ef060b8ae 100644 --- a/tests/Tests/Readers/Docx.hs +++ b/tests/Tests/Readers/Docx.hs @@ -44,7 +44,7 @@ compareOutput opts docxFile nativeFile = do df <- B.readFile docxFile nf <- Prelude.readFile nativeFile p <- runIOorExplode $ readDocx opts df - df' <- runIOorExplode $ readNative nf + df' <- runIOorExplode $ readNative def nf return $ (noNorm p, noNorm df') testCompareWithOptsIO :: ReaderOptions -> String -> FilePath -> FilePath -> IO Test diff --git a/tests/Tests/Readers/Odt.hs b/tests/Tests/Readers/Odt.hs index c3a44a729..b0e916336 100644 --- a/tests/Tests/Readers/Odt.hs +++ b/tests/Tests/Readers/Odt.hs @@ -63,7 +63,7 @@ compareOdtToNative :: TestCreator compareOdtToNative opts odtPath nativePath = do nativeFile <- Prelude.readFile nativePath odtFile <- B.readFile odtPath - native <- getNoNormVia id "native" <$> runIO (readNative nativeFile) + native <- getNoNormVia id "native" <$> runIO (readNative def nativeFile) odt <- getNoNormVia id "odt" <$> runIO (readOdt opts odtFile) return (odt,native) diff --git a/tests/Tests/Writers/Docx.hs b/tests/Tests/Writers/Docx.hs index 44095925f..fd320d224 100644 --- a/tests/Tests/Writers/Docx.hs +++ b/tests/Tests/Writers/Docx.hs @@ -21,9 +21,9 @@ compareOutput opts nativeFileIn nativeFileOut = do nf' <- Prelude.readFile nativeFileOut let wopts = fst opts df <- runIOorExplode $ do - d <- readNative nf + d <- readNative def nf writeDocx wopts{writerUserDataDir = Just (".." "data")} d - df' <- runIOorExplode (readNative nf') + df' <- runIOorExplode (readNative def nf') p <- runIOorExplode $ readDocx (snd opts) df return (p, df') -- cgit v1.2.3 From a964b1447554d9f0074723db52faf086984d0c28 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 10 Dec 2016 23:14:43 +0100 Subject: Text.Pandoc: limit exports from Text.Pandoc.Class. --- src/Text/Pandoc.hs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index e5fc665a7..b94d05718 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -59,7 +59,10 @@ module Text.Pandoc -- * Options , module Text.Pandoc.Options -- * Typeclass - , module Text.Pandoc.Class + , PandocMonad + , runIO + , runPure + , runIOorExplode -- * Error handling , module Text.Pandoc.Error -- * Lists of readers and writers @@ -177,7 +180,7 @@ import Text.Pandoc.Templates import Text.Pandoc.Options import Text.Pandoc.Shared (safeRead, mapLeft, pandocVersion) import Text.Pandoc.Error -import Text.Pandoc.Class (PandocMonad, runIOorExplode) +import Text.Pandoc.Class import Data.Aeson import qualified Data.ByteString.Lazy as BL import Data.List (intercalate) -- cgit v1.2.3 From a66c1bf88e511b73c6237bf6e85b57c5756321b7 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 10 Dec 2016 23:30:42 +0100 Subject: Generic instance for PandocError. --- src/Text/Pandoc/Error.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs index f76749a80..b624f4cb0 100644 --- a/src/Text/Pandoc/Error.hs +++ b/src/Text/Pandoc/Error.hs @@ -34,6 +34,7 @@ module Text.Pandoc.Error (PandocError(..), handleError) where import Text.Parsec.Error import Text.Parsec.Pos hiding (Line) import Data.Generics (Typeable) +import GHC.Generics (Generic) import Control.Exception (Exception) import Text.Pandoc.Shared (err) @@ -44,7 +45,7 @@ data PandocError = PandocFileReadError FilePath | PandocSomeError String | PandocParseError String | PandocParsecError Input ParseError - deriving (Show, Typeable) + deriving (Show, Typeable, Generic) -- data PandocError = -- | Generic parse failure -- cgit v1.2.3 From b5d15670223ada11a357161f3b057fae6f852554 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 10 Dec 2016 23:41:37 +0100 Subject: Class: removed 'fail' from PandocMonad. Do we need this? I don't see why. There's a name clash which would better be avoided. --- src/Text/Pandoc/Class.hs | 24 ++++++++++++------------ src/Text/Pandoc/Writers/Docx.hs | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 7f86e27b1..8b94d64a9 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -58,8 +58,7 @@ module Text.Pandoc.Class ( PandocMonad(..) , withWarningsToStderr ) where -import Prelude hiding (readFile, fail) -import qualified Control.Monad as M (fail) +import Prelude hiding (readFile) import System.Random (StdGen, next, mkStdGen) import qualified System.Random as IO (newStdGen) import Codec.Archive.Zip (Archive, fromArchive, emptyArchive) @@ -90,11 +89,12 @@ import System.FilePath.Glob (match, compile) import System.FilePath (()) import qualified System.FilePath.Glob as IO (glob) import qualified System.Directory as IO (getModificationTime) -import Control.Monad.State hiding (fail) +import Control.Monad as M (fail) import Control.Monad.Reader (ReaderT) +import Control.Monad.State +import Control.Monad.Except import Control.Monad.Writer (WriterT) import Control.Monad.RWS (RWST) -import Control.Monad.Except hiding (fail) import Data.Word (Word8) import Data.Default import System.IO.Error @@ -121,7 +121,7 @@ class (Functor m, Applicative m, Monad m, MonadError PandocError m) -> Maybe String -> String -> m (Either E.SomeException (B.ByteString, Maybe MimeType)) - fail :: String -> m b + -- fail :: String -> m b glob :: String -> m [FilePath] getModificationTime :: FilePath -> m UTCTime getCommonState :: m CommonState @@ -231,7 +231,7 @@ instance PandocMonad PandocIO where case eitherBS of Right bs -> return bs Left _ -> throwError $ PandocFileReadError fname - fail = M.fail + -- fail = M.fail fetchItem ms s = liftIO $ IO.fetchItem ms s fetchItem' mb ms s = liftIO $ IO.fetchItem' mb ms s glob = liftIO . IO.glob @@ -361,7 +361,7 @@ instance PandocMonad PandocPure where case infoFileContents <$> (getFileInfo (userDir fname) userDirFiles) of Just bs -> return bs Nothing -> readDataFile Nothing fname - fail = M.fail + -- fail = M.fail fetchItem _ fp = do fps <- getsPureState stFiles case infoFileContents <$> (getFileInfo fp fps) of @@ -396,7 +396,7 @@ instance PandocMonad m => PandocMonad (ParserT s st m) where newUniqueHash = lift newUniqueHash readFileLazy = lift . readFileLazy readDataFile mbuserdir = lift . readDataFile mbuserdir - fail = lift . fail + -- fail = lift . fail fetchItem media = lift . fetchItem media fetchItem' media sourceUrl = lift . fetchItem' media sourceUrl glob = lift . glob @@ -414,7 +414,7 @@ instance PandocMonad m => PandocMonad (ReaderT r m) where newUniqueHash = lift newUniqueHash readFileLazy = lift . readFileLazy readDataFile mbuserdir = lift . readDataFile mbuserdir - fail = lift . fail + -- fail = lift . fail fetchItem media = lift . fetchItem media fetchItem' media sourceUrl = lift . fetchItem' media sourceUrl glob = lift . glob @@ -432,7 +432,7 @@ instance (PandocMonad m, Monoid w) => PandocMonad (WriterT w m) where newUniqueHash = lift newUniqueHash readFileLazy = lift . readFileLazy readDataFile mbuserdir = lift . readDataFile mbuserdir - fail = lift . fail + -- fail = lift . fail fetchItem media = lift . fetchItem media fetchItem' media sourceUrl = lift . fetchItem' media sourceUrl glob = lift . glob @@ -450,7 +450,7 @@ instance (PandocMonad m, Monoid w) => PandocMonad (RWST r w st m) where newUniqueHash = lift newUniqueHash readFileLazy = lift . readFileLazy readDataFile mbuserdir = lift . readDataFile mbuserdir - fail = lift . fail + -- fail = lift . fail fetchItem media = lift . fetchItem media fetchItem' media sourceUrl = lift . fetchItem' media sourceUrl glob = lift . glob @@ -468,7 +468,7 @@ instance PandocMonad m => PandocMonad (StateT st m) where newUniqueHash = lift newUniqueHash readFileLazy = lift . readFileLazy readDataFile mbuserdir = lift . readDataFile mbuserdir - fail = lift . fail + -- fail = lift . fail fetchItem media = lift . fetchItem media fetchItem' media sourceUrl = lift . fetchItem' media sourceUrl glob = lift . glob diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 20320907e..662b4d3bb 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -1276,7 +1276,7 @@ parseXml refArchive distArchive relpath = findEntryByPath relpath distArchive of Nothing -> fail $ relpath ++ " missing in reference docx" Just e -> case parseXMLDoc . UTF8.toStringLazy . fromEntry $ e of - Nothing -> P.fail $ relpath ++ " corrupt in reference docx" + Nothing -> fail $ relpath ++ " corrupt in reference docx" Just d -> return d -- | Scales the image to fit the page -- cgit v1.2.3 From 143d1a21139cc7b32777e4154daf6c732037b625 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 11 Dec 2016 16:00:36 +0100 Subject: Removed commented-out vestigaes of fail in Class. --- src/Text/Pandoc/Class.hs | 8 -------- 1 file changed, 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 8b94d64a9..402fe9dcf 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -121,7 +121,6 @@ class (Functor m, Applicative m, Monad m, MonadError PandocError m) -> Maybe String -> String -> m (Either E.SomeException (B.ByteString, Maybe MimeType)) - -- fail :: String -> m b glob :: String -> m [FilePath] getModificationTime :: FilePath -> m UTCTime getCommonState :: m CommonState @@ -231,7 +230,6 @@ instance PandocMonad PandocIO where case eitherBS of Right bs -> return bs Left _ -> throwError $ PandocFileReadError fname - -- fail = M.fail fetchItem ms s = liftIO $ IO.fetchItem ms s fetchItem' mb ms s = liftIO $ IO.fetchItem' mb ms s glob = liftIO . IO.glob @@ -361,7 +359,6 @@ instance PandocMonad PandocPure where case infoFileContents <$> (getFileInfo (userDir fname) userDirFiles) of Just bs -> return bs Nothing -> readDataFile Nothing fname - -- fail = M.fail fetchItem _ fp = do fps <- getsPureState stFiles case infoFileContents <$> (getFileInfo fp fps) of @@ -396,7 +393,6 @@ instance PandocMonad m => PandocMonad (ParserT s st m) where newUniqueHash = lift newUniqueHash readFileLazy = lift . readFileLazy readDataFile mbuserdir = lift . readDataFile mbuserdir - -- fail = lift . fail fetchItem media = lift . fetchItem media fetchItem' media sourceUrl = lift . fetchItem' media sourceUrl glob = lift . glob @@ -414,7 +410,6 @@ instance PandocMonad m => PandocMonad (ReaderT r m) where newUniqueHash = lift newUniqueHash readFileLazy = lift . readFileLazy readDataFile mbuserdir = lift . readDataFile mbuserdir - -- fail = lift . fail fetchItem media = lift . fetchItem media fetchItem' media sourceUrl = lift . fetchItem' media sourceUrl glob = lift . glob @@ -432,7 +427,6 @@ instance (PandocMonad m, Monoid w) => PandocMonad (WriterT w m) where newUniqueHash = lift newUniqueHash readFileLazy = lift . readFileLazy readDataFile mbuserdir = lift . readDataFile mbuserdir - -- fail = lift . fail fetchItem media = lift . fetchItem media fetchItem' media sourceUrl = lift . fetchItem' media sourceUrl glob = lift . glob @@ -450,7 +444,6 @@ instance (PandocMonad m, Monoid w) => PandocMonad (RWST r w st m) where newUniqueHash = lift newUniqueHash readFileLazy = lift . readFileLazy readDataFile mbuserdir = lift . readDataFile mbuserdir - -- fail = lift . fail fetchItem media = lift . fetchItem media fetchItem' media sourceUrl = lift . fetchItem' media sourceUrl glob = lift . glob @@ -468,7 +461,6 @@ instance PandocMonad m => PandocMonad (StateT st m) where newUniqueHash = lift newUniqueHash readFileLazy = lift . readFileLazy readDataFile mbuserdir = lift . readDataFile mbuserdir - -- fail = lift . fail fetchItem media = lift . fetchItem media fetchItem' media sourceUrl = lift . fetchItem' media sourceUrl glob = lift . glob -- cgit v1.2.3 From 08110c371484cb74206a150fe9c2e06eeb32e475 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 11 Dec 2016 16:21:08 +0100 Subject: Class: Removed getDefaultReferenceDocx/ODT from PandocMonad. We don't need these, since the default docx and odt can be retrieved using `readDataFile datadir "reference.docx"` (or odt). --- src/Text/Pandoc/Class.hs | 24 ++---------------------- src/Text/Pandoc/Writers/Docx.hs | 5 +++-- src/Text/Pandoc/Writers/ODT.hs | 3 ++- 3 files changed, 7 insertions(+), 25 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 402fe9dcf..7af9b8bdd 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -66,8 +66,6 @@ import Data.Unique (hashUnique) import qualified Data.Unique as IO (newUnique) import qualified Text.Pandoc.Shared as IO ( fetchItem , fetchItem' - , getDefaultReferenceDocx - , getDefaultReferenceODT , readDataFile , warn) import Text.Pandoc.Compat.Time (UTCTime) @@ -106,8 +104,6 @@ class (Functor m, Applicative m, Monad m, MonadError PandocError m) lookupEnv :: String -> m (Maybe String) getCurrentTime :: m UTCTime getCurrentTimeZone :: m TimeZone - getDefaultReferenceDocx :: Maybe FilePath -> m Archive - getDefaultReferenceODT :: Maybe FilePath -> m Archive newStdGen :: m StdGen newUniqueHash :: m Int readFileLazy :: FilePath -> m BL.ByteString @@ -215,8 +211,6 @@ instance PandocMonad PandocIO where lookupEnv = liftIO . IO.lookupEnv getCurrentTime = liftIO IO.getCurrentTime getCurrentTimeZone = liftIO IO.getCurrentTimeZone - getDefaultReferenceDocx = liftIO . IO.getDefaultReferenceDocx - getDefaultReferenceODT = liftIO . IO.getDefaultReferenceODT newStdGen = liftIO IO.newStdGen newUniqueHash = hashUnique <$> (liftIO IO.newUnique) readFileLazy s = do @@ -325,10 +319,6 @@ instance PandocMonad PandocPure where getCurrentTimeZone = getsPureState stTimeZone - getDefaultReferenceDocx _ = getsPureState stReferenceDocx - - getDefaultReferenceODT _ = getsPureState stReferenceODT - newStdGen = do g <- getsPureState stStdGen let (_, nxtGen) = next g @@ -348,9 +338,9 @@ instance PandocMonad PandocPure where Just bs -> return (BL.fromStrict bs) Nothing -> throwError $ PandocFileReadError fp readDataFile Nothing "reference.docx" = do - (B.concat . BL.toChunks . fromArchive) <$> (getDefaultReferenceDocx Nothing) + (B.concat . BL.toChunks . fromArchive) <$> getsPureState stReferenceDocx readDataFile Nothing "reference.odt" = do - (B.concat . BL.toChunks . fromArchive) <$> (getDefaultReferenceODT Nothing) + (B.concat . BL.toChunks . fromArchive) <$> getsPureState stReferenceODT readDataFile Nothing fname = do let fname' = if fname == "MANUAL.txt" then fname else "data" fname BL.toStrict <$> (readFileLazy fname') @@ -387,8 +377,6 @@ instance PandocMonad m => PandocMonad (ParserT s st m) where lookupEnv = lift . lookupEnv getCurrentTime = lift getCurrentTime getCurrentTimeZone = lift getCurrentTimeZone - getDefaultReferenceDocx = lift . getDefaultReferenceDocx - getDefaultReferenceODT = lift . getDefaultReferenceODT newStdGen = lift newStdGen newUniqueHash = lift newUniqueHash readFileLazy = lift . readFileLazy @@ -404,8 +392,6 @@ instance PandocMonad m => PandocMonad (ReaderT r m) where lookupEnv = lift . lookupEnv getCurrentTime = lift getCurrentTime getCurrentTimeZone = lift getCurrentTimeZone - getDefaultReferenceDocx = lift . getDefaultReferenceDocx - getDefaultReferenceODT = lift . getDefaultReferenceODT newStdGen = lift newStdGen newUniqueHash = lift newUniqueHash readFileLazy = lift . readFileLazy @@ -421,8 +407,6 @@ instance (PandocMonad m, Monoid w) => PandocMonad (WriterT w m) where lookupEnv = lift . lookupEnv getCurrentTime = lift getCurrentTime getCurrentTimeZone = lift getCurrentTimeZone - getDefaultReferenceDocx = lift . getDefaultReferenceDocx - getDefaultReferenceODT = lift . getDefaultReferenceODT newStdGen = lift newStdGen newUniqueHash = lift newUniqueHash readFileLazy = lift . readFileLazy @@ -438,8 +422,6 @@ instance (PandocMonad m, Monoid w) => PandocMonad (RWST r w st m) where lookupEnv = lift . lookupEnv getCurrentTime = lift getCurrentTime getCurrentTimeZone = lift getCurrentTimeZone - getDefaultReferenceDocx = lift . getDefaultReferenceDocx - getDefaultReferenceODT = lift . getDefaultReferenceODT newStdGen = lift newStdGen newUniqueHash = lift newUniqueHash readFileLazy = lift . readFileLazy @@ -455,8 +437,6 @@ instance PandocMonad m => PandocMonad (StateT st m) where lookupEnv = lift . lookupEnv getCurrentTime = lift getCurrentTime getCurrentTimeZone = lift getCurrentTimeZone - getDefaultReferenceDocx = lift . getDefaultReferenceDocx - getDefaultReferenceODT = lift . getDefaultReferenceODT newStdGen = lift newStdGen newUniqueHash = lift newUniqueHash readFileLazy = lift . readFileLazy diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 662b4d3bb..07aed0c9b 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -224,10 +224,11 @@ writeDocx opts doc@(Pandoc meta _) = do let doc' = walk fixDisplayMath $ doc username <- P.lookupEnv "USERNAME" utctime <- P.getCurrentTime - distArchive <- P.getDefaultReferenceDocx datadir + distArchive <- (toArchive . BL.fromStrict) <$> + P.readDataFile datadir "reference.docx" refArchive <- case writerReferenceDoc opts of Just f -> toArchive <$> P.readFileLazy f - Nothing -> P.getDefaultReferenceDocx datadir + Nothing -> return distArchive parsedDoc <- parseXml refArchive distArchive "word/document.xml" let wname f qn = qPrefix qn == Just "w" && f (qName qn) diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index a1a1c4f62..0e4999712 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -79,7 +79,8 @@ pandocToODT opts doc@(Pandoc meta _) = do refArchive <- case writerReferenceDoc opts of Just f -> liftM toArchive $ lift $ P.readFileLazy f - Nothing -> lift $ P.getDefaultReferenceODT datadir + Nothing -> lift $ (toArchive . B.fromStrict) <$> + P.readDataFile datadir "reference.odt" -- handle formulas and pictures -- picEntriesRef <- P.newIORef ([] :: [Entry]) doc' <- walkM (transformPicMath opts) $ walk fixDisplayMath doc -- cgit v1.2.3 From 8165014df679338d5bf228d84efc74b2c5ac39d2 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 11 Dec 2016 22:09:33 +0100 Subject: Removed `--normalize` option and normalization functions from Shared. * Removed normalize, normalizeInlines, normalizeBlocks from Text.Pandoc.Shared. These shouldn't now be necessary, since normalization is handled automatically by the Builder monoid instance. * Remove `--normalize` command-line option. * Don't use normalize in tests. * A few revisions to readers so they work well without normalize. --- MANUAL.txt | 5 -- pandoc.hs | 8 +- src/Text/Pandoc/Readers/RST.hs | 2 +- src/Text/Pandoc/Readers/Txt2Tags.hs | 8 +- src/Text/Pandoc/Shared.hs | 150 ------------------------------------ src/Text/Pandoc/Writers/Docx.hs | 4 +- src/Text/Pandoc/Writers/DokuWiki.hs | 15 +++- tests/Tests/Helpers.hs | 8 +- tests/Tests/Old.hs | 12 +-- tests/Tests/Readers/Org.hs | 2 +- tests/Tests/Readers/Txt2Tags.hs | 2 +- tests/Tests/Shared.hs | 22 +----- tests/rst-reader.native | 6 -- 13 files changed, 30 insertions(+), 214 deletions(-) (limited to 'src/Text') diff --git a/MANUAL.txt b/MANUAL.txt index 9cfd6026a..9ac79da2e 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -478,11 +478,6 @@ Reader options underlying document (which is accessible from filters and may be printed in some output formats). -`--normalize` - -: Normalize the document after reading: merge adjacent - `Str` or `Emph` elements, for example, and remove repeated `Space`s. - `-p`, `--preserve-tabs` : Preserve tabs instead of converting them to spaces (the default). diff --git a/pandoc.hs b/pandoc.hs index dd58e79ab..b758aaa97 100644 --- a/pandoc.hs +++ b/pandoc.hs @@ -35,7 +35,7 @@ import Text.Pandoc.Builder (setMeta) import Text.Pandoc.PDF (makePDF) import Text.Pandoc.Walk (walk) import Text.Pandoc.Shared ( tabFilter, readDataFileUTF8, readDataFile, - safeRead, headerShift, normalize, err, warn, + safeRead, headerShift, err, warn, openURL ) import Text.Pandoc.MediaBag ( mediaDirectory, extractMediaBag, MediaBag ) import Text.Pandoc.XML ( toEntities ) @@ -731,12 +731,6 @@ options = "PROGRAM") "" -- "External JSON filter" - , Option "" ["normalize"] - (NoArg - (\opt -> return opt { optTransforms = - normalize : optTransforms opt } )) - "" -- "Normalize the Pandoc AST" - , Option "p" ["preserve-tabs"] (NoArg (\opt -> return opt { optPreserveTabs = True })) diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 75cd03d30..57b6c6f6c 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -744,7 +744,7 @@ addNewRole roleString fields = do M.insert role (baseRole, fmt, attr) customRoles } - return $ B.singleton Null + return mempty where countKeys k = length . filter (== k) . map fst $ fields inheritedRole = diff --git a/src/Text/Pandoc/Readers/Txt2Tags.hs b/src/Text/Pandoc/Readers/Txt2Tags.hs index 4abe13827..d2459ba47 100644 --- a/src/Text/Pandoc/Readers/Txt2Tags.hs +++ b/src/Text/Pandoc/Readers/Txt2Tags.hs @@ -447,9 +447,13 @@ inlineMarkup p f c special = try $ do lastChar <- anyChar end <- many1 (char c) let parser inp = parseFromString (mconcat <$> many p) inp - let start' = special (drop 2 start) + let start' = case drop 2 start of + "" -> mempty + xs -> special xs body' <- parser (middle ++ [lastChar]) - let end' = special (drop 2 end) + let end' = case drop 2 end of + "" -> mempty + xs -> special xs return $ f (start' <> body' <> end') Nothing -> do -- Either bad or case such as ***** guard (l >= 5) diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 3df016996..6f52a8290 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -55,9 +55,6 @@ module Text.Pandoc.Shared ( orderedListMarkers, normalizeSpaces, extractSpaces, - normalize, - normalizeInlines, - normalizeBlocks, removeFormatting, stringify, capitalize, @@ -398,153 +395,6 @@ extractSpaces f is = _ -> mempty in (left <> f (B.trimInlines . B.Many $ contents) <> right) --- | Normalize @Pandoc@ document, consolidating doubled 'Space's, --- combining adjacent 'Str's and 'Emph's, remove 'Null's and --- empty elements, etc. -normalize :: Pandoc -> Pandoc -normalize (Pandoc (Meta meta) blocks) = - Pandoc (Meta $ M.map go meta) (normalizeBlocks blocks) - where go (MetaInlines xs) = MetaInlines $ normalizeInlines xs - go (MetaBlocks xs) = MetaBlocks $ normalizeBlocks xs - go (MetaList ms) = MetaList $ map go ms - go (MetaMap m) = MetaMap $ M.map go m - go x = x - -normalizeBlocks :: [Block] -> [Block] -normalizeBlocks (Null : xs) = normalizeBlocks xs -normalizeBlocks (Div attr bs : xs) = - Div attr (normalizeBlocks bs) : normalizeBlocks xs -normalizeBlocks (BlockQuote bs : xs) = - case normalizeBlocks bs of - [] -> normalizeBlocks xs - bs' -> BlockQuote bs' : normalizeBlocks xs -normalizeBlocks (BulletList [] : xs) = normalizeBlocks xs -normalizeBlocks (BulletList items : xs) = - BulletList (map normalizeBlocks items) : normalizeBlocks xs -normalizeBlocks (OrderedList _ [] : xs) = normalizeBlocks xs -normalizeBlocks (OrderedList attr items : xs) = - OrderedList attr (map normalizeBlocks items) : normalizeBlocks xs -normalizeBlocks (DefinitionList [] : xs) = normalizeBlocks xs -normalizeBlocks (DefinitionList items : xs) = - DefinitionList (map go items) : normalizeBlocks xs - where go (ils, bs) = (normalizeInlines ils, map normalizeBlocks bs) -normalizeBlocks (RawBlock _ "" : xs) = normalizeBlocks xs -normalizeBlocks (RawBlock f x : xs) = - case normalizeBlocks xs of - (RawBlock f' x' : rest) | f' == f -> - RawBlock f (x ++ ('\n':x')) : rest - rest -> RawBlock f x : rest -normalizeBlocks (Para ils : xs) = - case normalizeInlines ils of - [] -> normalizeBlocks xs - ils' -> Para ils' : normalizeBlocks xs -normalizeBlocks (Plain ils : xs) = - case normalizeInlines ils of - [] -> normalizeBlocks xs - ils' -> Plain ils' : normalizeBlocks xs -normalizeBlocks (Header lev attr ils : xs) = - Header lev attr (normalizeInlines ils) : normalizeBlocks xs -normalizeBlocks (Table capt aligns widths hdrs rows : xs) = - Table (normalizeInlines capt) aligns widths - (map normalizeBlocks hdrs) (map (map normalizeBlocks) rows) - : normalizeBlocks xs -normalizeBlocks (x:xs) = x : normalizeBlocks xs -normalizeBlocks [] = [] - -normalizeInlines :: [Inline] -> [Inline] -normalizeInlines (Str x : ys) = - case concat (x : map fromStr strs) of - "" -> rest - n -> Str n : rest - where - (strs, rest) = span isStr $ normalizeInlines ys - isStr (Str _) = True - isStr _ = False - fromStr (Str z) = z - fromStr _ = error "normalizeInlines - fromStr - not a Str" -normalizeInlines (Space : SoftBreak : ys) = - SoftBreak : normalizeInlines ys -normalizeInlines (Space : ys) = - if null rest - then [] - else Space : rest - where isSp Space = True - isSp _ = False - rest = dropWhile isSp $ normalizeInlines ys -normalizeInlines (Emph xs : zs) = - case normalizeInlines zs of - (Emph ys : rest) -> normalizeInlines $ - Emph (normalizeInlines $ xs ++ ys) : rest - rest -> case normalizeInlines xs of - [] -> rest - xs' -> Emph xs' : rest -normalizeInlines (Strong xs : zs) = - case normalizeInlines zs of - (Strong ys : rest) -> normalizeInlines $ - Strong (normalizeInlines $ xs ++ ys) : rest - rest -> case normalizeInlines xs of - [] -> rest - xs' -> Strong xs' : rest -normalizeInlines (Subscript xs : zs) = - case normalizeInlines zs of - (Subscript ys : rest) -> normalizeInlines $ - Subscript (normalizeInlines $ xs ++ ys) : rest - rest -> case normalizeInlines xs of - [] -> rest - xs' -> Subscript xs' : rest -normalizeInlines (Superscript xs : zs) = - case normalizeInlines zs of - (Superscript ys : rest) -> normalizeInlines $ - Superscript (normalizeInlines $ xs ++ ys) : rest - rest -> case normalizeInlines xs of - [] -> rest - xs' -> Superscript xs' : rest -normalizeInlines (SmallCaps xs : zs) = - case normalizeInlines zs of - (SmallCaps ys : rest) -> normalizeInlines $ - SmallCaps (normalizeInlines $ xs ++ ys) : rest - rest -> case normalizeInlines xs of - [] -> rest - xs' -> SmallCaps xs' : rest -normalizeInlines (Strikeout xs : zs) = - case normalizeInlines zs of - (Strikeout ys : rest) -> normalizeInlines $ - Strikeout (normalizeInlines $ xs ++ ys) : rest - rest -> case normalizeInlines xs of - [] -> rest - xs' -> Strikeout xs' : rest -normalizeInlines (RawInline _ [] : ys) = normalizeInlines ys -normalizeInlines (RawInline f xs : zs) = - case normalizeInlines zs of - (RawInline f' ys : rest) | f == f' -> normalizeInlines $ - RawInline f (xs ++ ys) : rest - rest -> RawInline f xs : rest -normalizeInlines (Code _ "" : ys) = normalizeInlines ys -normalizeInlines (Code attr xs : zs) = - case normalizeInlines zs of - (Code attr' ys : rest) | attr == attr' -> normalizeInlines $ - Code attr (xs ++ ys) : rest - rest -> Code attr xs : rest --- allow empty spans, they may carry identifiers etc. --- normalizeInlines (Span _ [] : ys) = normalizeInlines ys -normalizeInlines (Span attr xs : zs) = - case normalizeInlines zs of - (Span attr' ys : rest) | attr == attr' -> normalizeInlines $ - Span attr (normalizeInlines $ xs ++ ys) : rest - rest -> Span attr (normalizeInlines xs) : rest -normalizeInlines (Note bs : ys) = Note (normalizeBlocks bs) : - normalizeInlines ys -normalizeInlines (Quoted qt ils : ys) = - Quoted qt (normalizeInlines ils) : normalizeInlines ys -normalizeInlines (Link attr ils t : ys) = - Link attr (normalizeInlines ils) t : normalizeInlines ys -normalizeInlines (Image attr ils t : ys) = - Image attr (normalizeInlines ils) t : normalizeInlines ys -normalizeInlines (Cite cs ils : ys) = - Cite cs (normalizeInlines ils) : normalizeInlines ys -normalizeInlines (x : xs) = x : normalizeInlines xs -normalizeInlines [] = [] - -- | Extract inlines, removing formatting. removeFormatting :: Walkable Inline a => a -> [Inline] removeFormatting = query go . walk deNote diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 07aed0c9b..163b2f3af 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -141,7 +141,7 @@ defaultWriterState = WriterState{ , stDelId = 1 , stStyleMaps = defaultStyleMaps , stFirstPara = False - , stTocTitle = normalizeInlines [Str "Table of Contents"] + , stTocTitle = [Str "Table of Contents"] , stDynamicParaProps = [] , stDynamicTextProps = [] } @@ -207,7 +207,7 @@ isValidChar (ord -> c) | otherwise = False metaValueToInlines :: MetaValue -> [Inline] -metaValueToInlines (MetaString s) = normalizeInlines [Str s] +metaValueToInlines (MetaString s) = [Str s] metaValueToInlines (MetaInlines ils) = ils metaValueToInlines (MetaBlocks bs) = query return bs metaValueToInlines (MetaBool b) = [Str $ show b] diff --git a/src/Text/Pandoc/Writers/DokuWiki.hs b/src/Text/Pandoc/Writers/DokuWiki.hs index c7a09fe50..42cddcef8 100644 --- a/src/Text/Pandoc/Writers/DokuWiki.hs +++ b/src/Text/Pandoc/Writers/DokuWiki.hs @@ -45,7 +45,7 @@ import Text.Pandoc.Options ( WriterOptions( , writerTemplate , writerWrapText), WrapOption(..) ) import Text.Pandoc.Shared ( escapeURI, linesToPara, removeFormatting - , camelCaseToHyphenated, trimr, normalize, substitute ) + , camelCaseToHyphenated, trimr, substitute ) import Text.Pandoc.Writers.Shared ( defField, metaToJSON ) import Text.Pandoc.ImageSize import Text.Pandoc.Templates ( renderTemplate' ) @@ -80,7 +80,7 @@ type DokuWiki = ReaderT WriterEnvironment (State WriterState) -- | Convert Pandoc to DokuWiki. writeDokuWiki :: PandocMonad m => WriterOptions -> Pandoc -> m String writeDokuWiki opts document = return $ - runDokuWiki (pandocToDokuWiki opts $ normalize document) + runDokuWiki (pandocToDokuWiki opts document) runDokuWiki :: DokuWiki a -> a runDokuWiki = flip evalState def . flip runReaderT def @@ -394,9 +394,16 @@ blockListToDokuWiki :: WriterOptions -- ^ Options -> DokuWiki String blockListToDokuWiki opts blocks = do backSlash <- stBackSlashLB <$> ask + let blocks' = consolidateRawBlocks blocks if backSlash - then (backSlashLineBreaks . vcat) <$> mapM (blockToDokuWiki opts) blocks - else vcat <$> mapM (blockToDokuWiki opts) blocks + then (backSlashLineBreaks . vcat) <$> mapM (blockToDokuWiki opts) blocks' + else vcat <$> mapM (blockToDokuWiki opts) blocks' + +consolidateRawBlocks :: [Block] -> [Block] +consolidateRawBlocks [] = [] +consolidateRawBlocks (RawBlock f1 b1 : RawBlock f2 b2 : xs) + | f1 == f2 = consolidateRawBlocks (RawBlock f1 (b1 ++ "\n" ++ b2) : xs) +consolidateRawBlocks (x:xs) = x : consolidateRawBlocks xs -- | Convert list of Pandoc inline elements to DokuWiki. inlineListToDokuWiki :: WriterOptions -> [Inline] -> DokuWiki String diff --git a/tests/Tests/Helpers.hs b/tests/Tests/Helpers.hs index 28a11266b..84c2394bc 100644 --- a/tests/Tests/Helpers.hs +++ b/tests/Tests/Helpers.hs @@ -17,7 +17,7 @@ import Test.Framework import Test.Framework.Providers.HUnit import Test.Framework.Providers.QuickCheck2 import Test.HUnit (assertBool) -import Text.Pandoc.Shared (normalize, trimr) +import Text.Pandoc.Shared (trimr) import Text.Pandoc.Options import Text.Pandoc.Writers.Native (writeNative) import qualified Test.QuickCheck.Property as QP @@ -81,10 +81,10 @@ class ToPandoc a where toPandoc :: a -> Pandoc instance ToPandoc Pandoc where - toPandoc = normalize + toPandoc = id instance ToPandoc Blocks where - toPandoc = normalize . doc + toPandoc = doc instance ToPandoc Inlines where - toPandoc = normalize . doc . plain + toPandoc = doc . plain diff --git a/tests/Tests/Old.hs b/tests/Tests/Old.hs index cc35c8aa0..c52a368e2 100644 --- a/tests/Tests/Old.hs +++ b/tests/Tests/Old.hs @@ -11,15 +11,10 @@ import System.FilePath ( (), (<.>), takeDirectory, splitDirectories, import System.Directory import System.Exit import Data.Algorithm.Diff -import Text.Pandoc.Shared ( normalize ) -import Text.Pandoc.Options -import Text.Pandoc.Writers.Native ( writeNative ) -import Text.Pandoc.Readers.Native ( readNative ) import Prelude hiding ( readFile ) import qualified Data.ByteString.Lazy as B import Text.Pandoc.UTF8 (toStringLazy) import Text.Printf -import Tests.Helpers (purely) readFileUTF8 :: FilePath -> IO String readFileUTF8 f = B.readFile f >>= return . toStringLazy @@ -193,12 +188,9 @@ lhsWriterTests format lhsReaderTest :: String -> Test lhsReaderTest format = - testWithNormalize normalizer "lhs" ["-r", format, "-w", "native"] + test "lhs" ["-r", format, "-w", "native"] ("lhs-test" <.> format) norm - where normalizer = purely $ \nat -> do - d <- readNative def nat - writeNative def $ normalize d - norm = if format == "markdown+lhs" + where norm = if format == "markdown+lhs" then "lhs-test-markdown.native" else "lhs-test.native" diff --git a/tests/Tests/Readers/Org.hs b/tests/Tests/Readers/Org.hs index b1db75b83..96a783045 100644 --- a/tests/Tests/Readers/Org.hs +++ b/tests/Tests/Readers/Org.hs @@ -1525,7 +1525,7 @@ tests = , "" , "#+RESULTS:" , ": 65" ] =?> - rawBlock "html" "" + (mempty :: Blocks) , "Source block with toggling header arguments" =: unlines [ "#+BEGIN_SRC sh :noeval" diff --git a/tests/Tests/Readers/Txt2Tags.hs b/tests/Tests/Readers/Txt2Tags.hs index 77430601b..46831d86f 100644 --- a/tests/Tests/Readers/Txt2Tags.hs +++ b/tests/Tests/Readers/Txt2Tags.hs @@ -88,7 +88,7 @@ tests = para "1970-01-01" , "Macros: Mod Time" =: "%%mtime" =?> - para "" + para (str "") , "Macros: Infile" =: "%%infile" =?> para "in" diff --git a/tests/Tests/Shared.hs b/tests/Tests/Shared.hs index 55f520433..4ff1dc837 100644 --- a/tests/Tests/Shared.hs +++ b/tests/Tests/Shared.hs @@ -1,9 +1,7 @@ module Tests.Shared (tests) where -import Text.Pandoc.Definition import Text.Pandoc.Shared import Test.Framework -import Tests.Helpers import Text.Pandoc.Arbitrary() import Test.Framework.Providers.HUnit import Test.HUnit ( assertBool, (@?=) ) @@ -11,13 +9,7 @@ import Text.Pandoc.Builder import System.FilePath.Posix (joinPath) tests :: [Test] -tests = [ testGroup "normalize" - [ property "p_normalize_blocks_rt" p_normalize_blocks_rt - , property "p_normalize_inlines_rt" p_normalize_inlines_rt - , property "p_normalize_no_trailing_spaces" - p_normalize_no_trailing_spaces - ] - , testGroup "compactify'DL" +tests = [ testGroup "compactify'DL" [ testCase "compactify'DL with empty def" $ assertBool "compactify'DL" (let x = [(str "word", [para (str "def"), mempty])] @@ -26,18 +18,6 @@ tests = [ testGroup "normalize" , testGroup "collapseFilePath" testCollapse ] -p_normalize_blocks_rt :: [Block] -> Bool -p_normalize_blocks_rt bs = - normalizeBlocks bs == normalizeBlocks (normalizeBlocks bs) - -p_normalize_inlines_rt :: [Inline] -> Bool -p_normalize_inlines_rt ils = - normalizeInlines ils == normalizeInlines (normalizeInlines ils) - -p_normalize_no_trailing_spaces :: [Inline] -> Bool -p_normalize_no_trailing_spaces ils = null ils' || last ils' /= Space - where ils' = normalizeInlines $ ils ++ [Space] - testCollapse :: [Test] testCollapse = map (testCase "collapse") [ (collapseFilePath (joinPath [ ""]) @?= (joinPath [ ""])) diff --git a/tests/rst-reader.native b/tests/rst-reader.native index 768a05c24..bc4641a3f 100644 --- a/tests/rst-reader.native +++ b/tests/rst-reader.native @@ -327,15 +327,9 @@ Pandoc (Meta {unMeta = fromList [("author",MetaList [MetaInlines [Str "John",Spa ,Para [Str "Some",Space,Superscript [Str "of"],Space,Str "these",Space,Superscript [Str "words"],Space,Str "are",Space,Str "in",Space,Superscript [Str "superscript"],Str "."] ,Para [Str "Reset",Space,Str "default-role",Space,Str "to",Space,Str "the",Space,Str "default",Space,Str "default."] ,Para [Str "And",Space,Str "now",Space,Str "some-invalid-string-3231231",Space,Str "is",Space,Str "nonsense."] -,Null ,Para [Str "And",Space,Str "now",Space,Str "with",Space,RawInline (Format "html") "inline HTML",Str "."] -,Null ,Para [Str "And",Space,Str "some",Space,Str "inline",Space,Str "haskell",Space,Code ("",["haskell","sourceCode"],[]) "fmap id [1,2..10]",Str "."] -,Null -,Null ,Para [Str "Indirect",Space,Str "python",Space,Str "role",Space,Code ("",["py","python","indirect","sourceCode"],[]) "[x*x for x in [1,2,3,4,5]]",Str "."] -,Null -,Null ,Para [Str "Different",Space,Str "indirect",Space,Str "C",Space,Code ("",["c","different-indirect","sourceCode"],[]) "int x = 15;",Str "."] ,Header 2 ("literal-symbols",[],[]) [Str "Literal",Space,Str "symbols"] ,Para [Str "2*2",Space,Str "=",Space,Str "4*1"]] -- cgit v1.2.3 From 00240ca7ed84c377880817c85c25dce8cfaff3cd Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 11 Dec 2016 22:17:10 +0100 Subject: Removed hush from Text.Pandoc.Shared. Not used anywhere. --- src/Text/Pandoc/ImageSize.hs | 6 +++--- src/Text/Pandoc/Shared.hs | 5 ----- 2 files changed, 3 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/ImageSize.hs b/src/Text/Pandoc/ImageSize.hs index e46c91eda..cc22c06ca 100644 --- a/src/Text/Pandoc/ImageSize.hs +++ b/src/Text/Pandoc/ImageSize.hs @@ -53,7 +53,7 @@ import Control.Monad import Data.Bits import Data.Binary import Data.Binary.Get -import Text.Pandoc.Shared (safeRead, hush) +import Text.Pandoc.Shared (safeRead) import Data.Default (Default) import Numeric (showFFloat) import Text.Pandoc.Definition @@ -240,7 +240,7 @@ pngSize img = do ([w1,w2,w3,w4,h1,h2,h3,h4] :: [Integer]) -> return ((shift w1 24) + (shift w2 16) + (shift w3 8) + w4, (shift h1 24) + (shift h2 16) + (shift h3 8) + h4) - _ -> (hush . Left) "PNG parse error" + _ -> Nothing -- "PNG parse error" let (dpix, dpiy) = findpHYs rest'' return $ ImageSize { pxX = x, pxY = y, dpiX = dpix, dpiY = dpiy } @@ -269,7 +269,7 @@ gifSize img = do dpiX = 72, dpiY = 72 } - _ -> (hush . Left) "GIF parse error" + _ -> Nothing -- "GIF parse error" jpegSize :: ByteString -> Either String ImageSize jpegSize img = diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 6f52a8290..0ff30dcce 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -88,7 +88,6 @@ module Text.Pandoc.Shared ( err, warn, mapLeft, - hush, -- * for squashing blocks blocksToInlines, -- * Safe read @@ -863,10 +862,6 @@ mapLeft :: (a -> b) -> Either a c -> Either b c mapLeft f (Left x) = Left (f x) mapLeft _ (Right x) = Right x -hush :: Either a b -> Maybe b -hush (Left _) = Nothing -hush (Right x) = Just x - -- | Remove intermediate "." and ".." directories from a path. -- -- > collapseFilePath "./foo" == "foo" -- cgit v1.2.3 From 4cb124d147790814cf2055afdfd17e500cece559 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 11 Dec 2016 23:10:46 +0100 Subject: Add openURL and readFileStrict to PandocMonad. Removed fetchItem and fetchItem'. Provide fetchItem in PandocMonad (it uses openURL and readFileStrict). TODO: - PandocPure instance for openURL. - Fix places where fetchItem is used so that we trap the exception instead of checking for a Left value. (At least in the places where we want a warning rather than a failure.) --- src/Text/Pandoc/Class.hs | 136 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 98 insertions(+), 38 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 7af9b8bdd..9604d7c18 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -46,6 +46,7 @@ module Text.Pandoc.Class ( PandocMonad(..) , getMediaBag , setMediaBag , insertMedia + , fetchItem , getInputFiles , getOutputFile , PandocIO(..) @@ -64,27 +65,28 @@ import qualified System.Random as IO (newStdGen) import Codec.Archive.Zip (Archive, fromArchive, emptyArchive) import Data.Unique (hashUnique) import qualified Data.Unique as IO (newUnique) -import qualified Text.Pandoc.Shared as IO ( fetchItem - , fetchItem' - , readDataFile - , warn) +import qualified Text.Pandoc.Shared as IO ( readDataFile + , warn + , openURL ) import Text.Pandoc.Compat.Time (UTCTime) import Text.Pandoc.Parsing (ParserT, SourcePos) import qualified Text.Pandoc.Compat.Time as IO (getCurrentTime) +import Text.Pandoc.MIME (MimeType, getMimeType) import Data.Time.Clock.POSIX ( utcTimeToPOSIXSeconds , posixSecondsToUTCTime , POSIXTime ) import Data.Time.LocalTime (TimeZone, ZonedTime, utcToZonedTime, utc) +import Network.URI ( escapeURIString, nonStrictRelativeTo, + unEscapeString, parseURIReference, isAllowedInURI, + parseURI, URI(..) ) import qualified Data.Time.LocalTime as IO (getCurrentTimeZone) -import Text.Pandoc.MIME (MimeType, getMimeType) -import Text.Pandoc.MediaBag (MediaBag) +import Text.Pandoc.MediaBag (MediaBag, lookupMedia) import qualified Text.Pandoc.MediaBag as MB import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL -import qualified Control.Exception as E import qualified System.Environment as IO (lookupEnv) import System.FilePath.Glob (match, compile) -import System.FilePath (()) +import System.FilePath ((), takeExtension, dropExtension) import qualified System.FilePath.Glob as IO (glob) import qualified System.Directory as IO (getModificationTime) import Control.Monad as M (fail) @@ -106,17 +108,12 @@ class (Functor m, Applicative m, Monad m, MonadError PandocError m) getCurrentTimeZone :: m TimeZone newStdGen :: m StdGen newUniqueHash :: m Int + openURL :: String -> m (B.ByteString, Maybe MimeType) readFileLazy :: FilePath -> m BL.ByteString + readFileStrict :: FilePath -> m B.ByteString readDataFile :: Maybe FilePath -> FilePath -> m B.ByteString - fetchItem :: Maybe String - -> String - -> m (Either E.SomeException (B.ByteString, Maybe MimeType)) - fetchItem' :: MediaBag - -> Maybe String - -> String - -> m (Either E.SomeException (B.ByteString, Maybe MimeType)) glob :: String -> m [FilePath] getModificationTime :: FilePath -> m UTCTime getCommonState :: m CommonState @@ -213,19 +210,28 @@ instance PandocMonad PandocIO where getCurrentTimeZone = liftIO IO.getCurrentTimeZone newStdGen = liftIO IO.newStdGen newUniqueHash = hashUnique <$> (liftIO IO.newUnique) + openURL u = do + eitherRes <- liftIO $ (tryIOError $ IO.openURL u) + case eitherRes of + Right (Right res) -> return res + Right (Left _) -> throwError $ PandocFileReadError u + Left _ -> throwError $ PandocFileReadError u readFileLazy s = do eitherBS <- liftIO (tryIOError $ BL.readFile s) case eitherBS of Right bs -> return bs Left _ -> throwError $ PandocFileReadError s + readFileStrict s = do + eitherBS <- liftIO (tryIOError $ B.readFile s) + case eitherBS of + Right bs -> return bs + Left _ -> throwError $ PandocFileReadError s -- TODO: Make this more sensitive to the different sorts of failure readDataFile mfp fname = do eitherBS <- liftIO (tryIOError $ IO.readDataFile mfp fname) case eitherBS of Right bs -> return bs Left _ -> throwError $ PandocFileReadError fname - fetchItem ms s = liftIO $ IO.fetchItem ms s - fetchItem' mb ms s = liftIO $ IO.fetchItem' mb ms s glob = liftIO . IO.glob getModificationTime fp = do eitherMtime <- liftIO (tryIOError $ IO.getModificationTime fp) @@ -235,6 +241,64 @@ instance PandocMonad PandocIO where getCommonState = PandocIO $ lift get putCommonState x = PandocIO $ lift $ put x + +-- | Specialized version of parseURIReference that disallows +-- single-letter schemes. Reason: these are usually windows absolute +-- paths. +parseURIReference' :: String -> Maybe URI +parseURIReference' s = + case parseURIReference s of + Just u + | length (uriScheme u) > 2 -> Just u + | null (uriScheme u) -> Just u -- protocol-relative + _ -> Nothing + +-- | Fetch an image or other item from the local filesystem or the net. +-- Returns raw content and maybe mime type. +fetchItem :: PandocMonad m + => Maybe String + -> String + -> m (B.ByteString, Maybe MimeType) +fetchItem sourceURL s = do + mediabag <- getMediaBag + case lookupMedia s mediabag of + Just (mime, bs) -> return $ (BL.toStrict bs, Just mime) + Nothing -> + case (sourceURL >>= parseURIReference' . + ensureEscaped, ensureEscaped s) of + (Just u, s') -> -- try fetching from relative path at source + case parseURIReference' s' of + Just u' -> openURL $ show $ u' `nonStrictRelativeTo` u + Nothing -> openURL s' -- will throw error + (Nothing, s'@('/':'/':_)) -> -- protocol-relative URI + case parseURIReference' s' of + Just u' -> openURL $ show $ u' `nonStrictRelativeTo` httpcolon + Nothing -> openURL s' -- will throw error + (Nothing, s') -> + case parseURI s' of -- requires absolute URI + -- We don't want to treat C:/ as a scheme: + Just u' | length (uriScheme u') > 2 -> openURL (show u') + Just u' | uriScheme u' == "file:" -> + readLocalFile $ dropWhile (=='/') (uriPath u') + _ -> readLocalFile fp -- get from local file system + where readLocalFile f = do + cont <- readFileStrict f + return (cont, mime) + httpcolon = URI{ uriScheme = "http:", + uriAuthority = Nothing, + uriPath = "", + uriQuery = "", + uriFragment = "" } + dropFragmentAndQuery = takeWhile (\c -> c /= '?' && c /= '#') + fp = unEscapeString $ dropFragmentAndQuery s + mime = case takeExtension fp of + ".gz" -> getMimeType $ dropExtension fp + ".svgz" -> getMimeType $ dropExtension fp ++ ".svg" + x -> getMimeType x + ensureEscaped = escapeURIString isAllowedInURI . map convertSlash + convertSlash '\\' = '/' + convertSlash x = x + data PureState = PureState { stStdGen :: StdGen , stWord8Store :: [Word8] -- should be -- inifinite, @@ -332,33 +396,29 @@ instance PandocMonad PandocPure where modifyPureState $ \st -> st { stUniqStore = us } return u _ -> M.fail "uniq store ran out of elements" + openURL _ = undefined -- TODO readFileLazy fp = do fps <- getsPureState stFiles case infoFileContents <$> getFileInfo fp fps of Just bs -> return (BL.fromStrict bs) Nothing -> throwError $ PandocFileReadError fp + readFileStrict fp = do + fps <- getsPureState stFiles + case infoFileContents <$> getFileInfo fp fps of + Just bs -> return bs + Nothing -> throwError $ PandocFileReadError fp readDataFile Nothing "reference.docx" = do (B.concat . BL.toChunks . fromArchive) <$> getsPureState stReferenceDocx readDataFile Nothing "reference.odt" = do (B.concat . BL.toChunks . fromArchive) <$> getsPureState stReferenceODT readDataFile Nothing fname = do let fname' = if fname == "MANUAL.txt" then fname else "data" fname - BL.toStrict <$> (readFileLazy fname') + readFileStrict fname' readDataFile (Just userDir) fname = do userDirFiles <- getsPureState stUserDataDir case infoFileContents <$> (getFileInfo (userDir fname) userDirFiles) of Just bs -> return bs Nothing -> readDataFile Nothing fname - fetchItem _ fp = do - fps <- getsPureState stFiles - case infoFileContents <$> (getFileInfo fp fps) of - Just bs -> return (Right (bs, getMimeType fp)) - Nothing -> return (Left $ E.toException $ PandocFileReadError fp) - - fetchItem' media sourceUrl nm = do - case MB.lookupMedia nm media of - Nothing -> fetchItem sourceUrl nm - Just (mime, bs) -> return (Right (B.concat $ BL.toChunks bs, Just mime)) glob s = do fontFiles <- getsPureState stFontFiles @@ -379,10 +439,10 @@ instance PandocMonad m => PandocMonad (ParserT s st m) where getCurrentTimeZone = lift getCurrentTimeZone newStdGen = lift newStdGen newUniqueHash = lift newUniqueHash + openURL = lift . openURL readFileLazy = lift . readFileLazy + readFileStrict = lift . readFileStrict readDataFile mbuserdir = lift . readDataFile mbuserdir - fetchItem media = lift . fetchItem media - fetchItem' media sourceUrl = lift . fetchItem' media sourceUrl glob = lift . glob getModificationTime = lift . getModificationTime getCommonState = lift getCommonState @@ -394,10 +454,10 @@ instance PandocMonad m => PandocMonad (ReaderT r m) where getCurrentTimeZone = lift getCurrentTimeZone newStdGen = lift newStdGen newUniqueHash = lift newUniqueHash + openURL = lift . openURL readFileLazy = lift . readFileLazy + readFileStrict = lift . readFileStrict readDataFile mbuserdir = lift . readDataFile mbuserdir - fetchItem media = lift . fetchItem media - fetchItem' media sourceUrl = lift . fetchItem' media sourceUrl glob = lift . glob getModificationTime = lift . getModificationTime getCommonState = lift getCommonState @@ -409,10 +469,10 @@ instance (PandocMonad m, Monoid w) => PandocMonad (WriterT w m) where getCurrentTimeZone = lift getCurrentTimeZone newStdGen = lift newStdGen newUniqueHash = lift newUniqueHash + openURL = lift . openURL readFileLazy = lift . readFileLazy + readFileStrict = lift . readFileStrict readDataFile mbuserdir = lift . readDataFile mbuserdir - fetchItem media = lift . fetchItem media - fetchItem' media sourceUrl = lift . fetchItem' media sourceUrl glob = lift . glob getModificationTime = lift . getModificationTime getCommonState = lift getCommonState @@ -424,10 +484,10 @@ instance (PandocMonad m, Monoid w) => PandocMonad (RWST r w st m) where getCurrentTimeZone = lift getCurrentTimeZone newStdGen = lift newStdGen newUniqueHash = lift newUniqueHash + openURL = lift . openURL readFileLazy = lift . readFileLazy + readFileStrict = lift . readFileStrict readDataFile mbuserdir = lift . readDataFile mbuserdir - fetchItem media = lift . fetchItem media - fetchItem' media sourceUrl = lift . fetchItem' media sourceUrl glob = lift . glob getModificationTime = lift . getModificationTime getCommonState = lift getCommonState @@ -439,10 +499,10 @@ instance PandocMonad m => PandocMonad (StateT st m) where getCurrentTimeZone = lift getCurrentTimeZone newStdGen = lift newStdGen newUniqueHash = lift newUniqueHash + openURL = lift . openURL readFileLazy = lift . readFileLazy + readFileStrict = lift . readFileStrict readDataFile mbuserdir = lift . readDataFile mbuserdir - fetchItem media = lift . fetchItem media - fetchItem' media sourceUrl = lift . fetchItem' media sourceUrl glob = lift . glob getModificationTime = lift . getModificationTime getCommonState = lift getCommonState -- cgit v1.2.3 From 6aff97e4e16b3829151a5e84b63a0aee26ea8511 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 12 Dec 2016 13:51:20 +0100 Subject: Text.Pandoc.Shared: Removed fetchItem, fetchItem'. Made changes where these are used, so that the version of fetchItem from PandocMonad can be used instead. --- src/Text/Pandoc/PDF.hs | 9 +++--- src/Text/Pandoc/SelfContained.hs | 6 ++-- src/Text/Pandoc/Shared.hs | 69 ++-------------------------------------- src/Text/Pandoc/Writers/Docx.hs | 9 +++--- src/Text/Pandoc/Writers/EPUB.hs | 33 +++++++++---------- src/Text/Pandoc/Writers/FB2.hs | 18 ++++++++--- src/Text/Pandoc/Writers/ICML.hs | 8 +++-- src/Text/Pandoc/Writers/ODT.hs | 7 ++-- src/Text/Pandoc/Writers/RTF.hs | 7 ++-- 9 files changed, 58 insertions(+), 108 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index 348f6a2fe..68151f569 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -49,8 +49,7 @@ import Data.Maybe (fromMaybe) import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Definition import Text.Pandoc.Walk (walkM) -import Text.Pandoc.Shared (fetchItem', warn, withTempDir, inDirectory, - stringify) +import Text.Pandoc.Shared (warn, withTempDir, inDirectory, stringify) import Text.Pandoc.Writers.Shared (getField, metaToJSON) import Text.Pandoc.Options (WriterOptions(..), HTMLMathMethod(..)) import Text.Pandoc.MIME (extensionFromMimeType, getMimeType) @@ -61,7 +60,7 @@ import qualified Codec.Picture as JP #ifdef _WINDOWS import Data.List (intercalate) #endif -import Text.Pandoc.Class (PandocIO, runIOorExplode) +import Text.Pandoc.Class (PandocIO, runIOorExplode, fetchItem, setMediaBag, runIO) #ifdef _WINDOWS changePathSeparators :: FilePath -> FilePath @@ -123,7 +122,9 @@ handleImage' opts tmpdir (Image attr ils (src,tit)) = do if exists then return $ Image attr ils (src,tit) else do - res <- fetchItem' (writerMediaBag opts) (writerSourceURL opts) src + res <- runIO $ do + setMediaBag $ writerMediaBag opts + fetchItem (writerSourceURL opts) src case res of Right (contents, Just mime) -> do let ext = fromMaybe (takeExtension src) $ diff --git a/src/Text/Pandoc/SelfContained.hs b/src/Text/Pandoc/SelfContained.hs index 6bcdc8728..176de99be 100644 --- a/src/Text/Pandoc/SelfContained.hs +++ b/src/Text/Pandoc/SelfContained.hs @@ -41,7 +41,7 @@ import Data.Char (toLower, isAscii, isAlphaNum) import Codec.Compression.GZip as Gzip import qualified Data.ByteString.Lazy as L import Control.Monad.Trans (MonadIO(..)) -import Text.Pandoc.Shared (renderTags', err, fetchItem', warn, trim) +import Text.Pandoc.Shared (renderTags', err, warn, trim) import Text.Pandoc.MediaBag (MediaBag) import Text.Pandoc.MIME (MimeType) import Text.Pandoc.UTF8 (toString) @@ -51,6 +51,7 @@ import Control.Applicative ((<|>)) import Text.Parsec (runParserT, ParsecT) import qualified Text.Parsec as P import Control.Monad.Trans (lift) +import Text.Pandoc.Class (fetchItem, runIO, setMediaBag) isOk :: Char -> Bool isOk c = isAscii c && isAlphaNum c @@ -144,7 +145,8 @@ getDataURI :: MediaBag -> Maybe String -> MimeType -> String getDataURI _ _ _ src@('d':'a':'t':'a':':':_) = return src -- already data: uri getDataURI media sourceURL mimetype src = do let ext = map toLower $ takeExtension src - fetchResult <- fetchItem' media sourceURL src + fetchResult <- runIO $ do setMediaBag media + fetchItem sourceURL src (raw, respMime) <- case fetchResult of Left msg -> err 67 $ "Could not fetch " ++ src ++ "\n" ++ show msg diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 0ff30dcce..fabda42ed 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -79,8 +79,6 @@ module Text.Pandoc.Shared ( getDefaultReferenceODT, readDataFile, readDataFileUTF8, - fetchItem, - fetchItem', openURL, collapseFilePath, filteredFilesFromArchive, @@ -100,7 +98,6 @@ module Text.Pandoc.Shared ( import Text.Pandoc.Definition import Text.Pandoc.Walk -import Text.Pandoc.MediaBag (MediaBag, lookupMedia) import Text.Pandoc.Builder (Inlines, Blocks, ToMetaValue(..)) import qualified Text.Pandoc.Builder as B import qualified Text.Pandoc.UTF8 as UTF8 @@ -111,15 +108,13 @@ import Data.List ( find, stripPrefix, intercalate ) import Data.Maybe (mapMaybe) import Data.Version ( showVersion ) import qualified Data.Map as M -import Network.URI ( escapeURIString, nonStrictRelativeTo, - unEscapeString, parseURIReference, isAllowedInURI, - parseURI, URI(..) ) +import Network.URI ( escapeURIString, unEscapeString ) import qualified Data.Set as Set import System.Directory import System.FilePath (splitDirectories, isPathSeparator) import qualified System.FilePath.Posix as Posix -import Text.Pandoc.MIME (MimeType, getMimeType) -import System.FilePath ( (), takeExtension, dropExtension) +import Text.Pandoc.MIME (MimeType) +import System.FilePath ( () ) import Data.Generics (Typeable, Data) import qualified Control.Monad.State as S import Control.Monad.Trans (MonadIO (..)) @@ -752,64 +747,6 @@ readDataFileUTF8 :: Maybe FilePath -> FilePath -> IO String readDataFileUTF8 userDir fname = UTF8.toString `fmap` readDataFile userDir fname --- | Specialized version of parseURIReference that disallows --- single-letter schemes. Reason: these are usually windows absolute --- paths. -parseURIReference' :: String -> Maybe URI -parseURIReference' s = - case parseURIReference s of - Just u - | length (uriScheme u) > 2 -> Just u - | null (uriScheme u) -> Just u -- protocol-relative - _ -> Nothing - --- | Fetch an image or other item from the local filesystem or the net. --- Returns raw content and maybe mime type. -fetchItem :: Maybe String -> String - -> IO (Either E.SomeException (BS.ByteString, Maybe MimeType)) -fetchItem sourceURL s = - case (sourceURL >>= parseURIReference' . ensureEscaped, ensureEscaped s) of - (Just u, s') -> -- try fetching from relative path at source - case parseURIReference' s' of - Just u' -> openURL $ show $ u' `nonStrictRelativeTo` u - Nothing -> openURL s' -- will throw error - (Nothing, s'@('/':'/':_)) -> -- protocol-relative URI - case parseURIReference' s' of - Just u' -> openURL $ show $ u' `nonStrictRelativeTo` httpcolon - Nothing -> openURL s' -- will throw error - (Nothing, s') -> - case parseURI s' of -- requires absolute URI - -- We don't want to treat C:/ as a scheme: - Just u' | length (uriScheme u') > 2 -> openURL (show u') - Just u' | uriScheme u' == "file:" -> - E.try $ readLocalFile $ dropWhile (=='/') (uriPath u') - _ -> E.try $ readLocalFile fp -- get from local file system - where readLocalFile f = do - cont <- BS.readFile f - return (cont, mime) - httpcolon = URI{ uriScheme = "http:", - uriAuthority = Nothing, - uriPath = "", - uriQuery = "", - uriFragment = "" } - dropFragmentAndQuery = takeWhile (\c -> c /= '?' && c /= '#') - fp = unEscapeString $ dropFragmentAndQuery s - mime = case takeExtension fp of - ".gz" -> getMimeType $ dropExtension fp - ".svgz" -> getMimeType $ dropExtension fp ++ ".svg" - x -> getMimeType x - ensureEscaped = escapeURIString isAllowedInURI . map convertSlash - convertSlash '\\' = '/' - convertSlash x = x - --- | Like 'fetchItem', but also looks for items in a 'MediaBag'. -fetchItem' :: MediaBag -> Maybe String -> String - -> IO (Either E.SomeException (BS.ByteString, Maybe MimeType)) -fetchItem' media sourceURL s = do - case lookupMedia s media of - Nothing -> fetchItem sourceURL s - Just (mime, bs) -> return $ Right (BS.concat $ toChunks bs, Just mime) - -- | Read from a URL and return raw data and maybe mime type. openURL :: String -> IO (Either E.SomeException (BS.ByteString, Maybe MimeType)) openURL u diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 163b2f3af..25e224a7a 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -48,6 +48,7 @@ import Text.Pandoc.Options import Text.Pandoc.Writers.Math import Text.Pandoc.Highlighting ( highlight ) import Text.Pandoc.Walk +import Text.Pandoc.Error (PandocError) import Text.XML.Light as XML import Text.TeXMath import Text.Pandoc.Readers.Docx.StyleMap @@ -55,9 +56,9 @@ import Text.Pandoc.Readers.Docx.Util (elemName) import Control.Monad.Reader import Control.Monad.State import Skylighting +import Control.Monad.Except (runExceptT) import System.Random (randomR) import Text.Printf (printf) -import qualified Control.Exception as E import Data.Monoid ((<>)) import qualified Data.Text as T import Text.Pandoc.MIME (MimeType, getMimeType, getMimeTypeDef, @@ -1180,10 +1181,10 @@ inlineToOpenXML' opts (Image attr alt (src, title)) = do case M.lookup src imgs of Just (_,_,_,elt,_) -> return [elt] Nothing -> do - res <- (lift . lift) $ P.fetchItem' (writerMediaBag opts) (writerSourceURL opts) src + res <- runExceptT $ lift (P.fetchItem (writerSourceURL opts) src) case res of - Left (_ :: E.SomeException) -> do - (lift . lift) $ P.warning ("Could not find image `" ++ src ++ "', skipping...") + Left (_ :: PandocError) -> do + P.warning ("Could not find image `" ++ src ++ "', skipping...") -- emit alt text inlinesToOpenXML opts alt Right (img, mt) -> do diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index 1c3a44207..d6c3ff533 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -64,7 +64,7 @@ import Data.Char ( toLower, isDigit, isAlphaNum ) import Text.Pandoc.MIME (MimeType, getMimeType, extensionFromMimeType) import Text.Blaze.Html.Renderer.Utf8 (renderHtml) import Text.HTML.TagSoup (Tag(TagOpen), fromAttrib, parseTags) -import Control.Monad.Except (throwError) +import Control.Monad.Except (throwError, catchError) import Text.Pandoc.Error import Text.Pandoc.Class (PandocMonad) import qualified Text.Pandoc.Class as P @@ -858,23 +858,20 @@ modifyMediaRef opts oldsrc = do media <- gets stMediaPaths case lookup oldsrc media of Just (n,_) -> return n - Nothing -> do - res <- lift $ P.fetchItem' (writerMediaBag opts) - (writerSourceURL opts) oldsrc - (new, mbEntry) <- - case res of - Left _ -> do - lift $ P.warning $ "Could not find media `" ++ oldsrc ++ "', skipping..." - return (oldsrc, Nothing) - Right (img,mbMime) -> do - let new = "media/file" ++ show (length media) ++ - fromMaybe (takeExtension (takeWhile (/='?') oldsrc)) - (('.':) <$> (mbMime >>= extensionFromMimeType)) - epochtime <- floor `fmap` lift P.getPOSIXTime - let entry = toEntry new epochtime $ B.fromChunks . (:[]) $ img - return (new, Just entry) - modify $ \st -> st{ stMediaPaths = (oldsrc, (new, mbEntry)):media} - return new + Nothing -> catchError + (do (img, mbMime) <- P.fetchItem (writerSourceURL opts) oldsrc + let new = "media/file" ++ show (length media) ++ + fromMaybe (takeExtension (takeWhile (/='?') oldsrc)) + (('.':) <$> (mbMime >>= extensionFromMimeType)) + epochtime <- floor `fmap` lift P.getPOSIXTime + let entry = toEntry new epochtime $ B.fromChunks . (:[]) $ img + modify $ \st -> st{ stMediaPaths = + (oldsrc, (new, Just entry)):media} + return new) + (\e -> do + P.warning $ "Could not find media `" ++ oldsrc ++ + "', skipping...\n" ++ show e + return oldsrc) transformBlock :: PandocMonad m => WriterOptions diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index 20af67b62..7baac4f9e 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -39,7 +39,7 @@ import Text.XML.Light import qualified Text.XML.Light as X import qualified Text.XML.Light.Cursor as XC import qualified Data.ByteString.Char8 as B8 -import Control.Monad.Except (throwError) +import Control.Monad.Except (throwError, catchError) import Text.Pandoc.Definition @@ -241,10 +241,18 @@ fetchImage href link = do else return Nothing (True, Just _) -> return Nothing -- not base64-encoded _ -> do - response <- P.fetchItem Nothing link - case response of - Right (bs, Just mime) -> return $ Just (mime, B8.unpack $ encode bs) - _ -> return $ Nothing + catchError (do (bs, mbmime) <- P.fetchItem Nothing link + case mbmime of + Nothing -> do + P.warning ("Could not determine mime type for " + ++ link) + return Nothing + Just mime -> return $ Just (mime, + B8.unpack $ encode bs)) + (\e -> + do P.warning ("Could not fetch " ++ link ++ + ":\n" ++ show e) + return Nothing) case mbimg of Just (imgtype, imgdata) -> do return . Right $ el "binary" diff --git a/src/Text/Pandoc/Writers/ICML.hs b/src/Text/Pandoc/Writers/ICML.hs index 6bc7436d8..b68b9067a 100644 --- a/src/Text/Pandoc/Writers/ICML.hs +++ b/src/Text/Pandoc/Writers/ICML.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE OverloadedStrings, FlexibleContexts #-} +{-# LANGUAGE OverloadedStrings, FlexibleContexts, ScopedTypeVariables #-} {- | Module : Text.Pandoc.Writers.ICML @@ -15,6 +15,7 @@ into InDesign with File -> Place. -} module Text.Pandoc.Writers.ICML (writeICML) where import Text.Pandoc.Definition +import Text.Pandoc.Error (PandocError) import Text.Pandoc.XML import Text.Pandoc.Writers.Math (texMathToInlines) import Text.Pandoc.Writers.Shared @@ -26,6 +27,7 @@ import Text.Pandoc.ImageSize import Data.List (isPrefixOf, isInfixOf, stripPrefix, intersperse) import Data.Text as Text (breakOnAll, pack) import Control.Monad.State +import Control.Monad.Except (runExceptT) import Network.URI (isURI) import qualified Data.Set as Set import Text.Pandoc.Class (PandocMonad) @@ -534,9 +536,9 @@ styleToStrAttr style = -- | Assemble an ICML Image. imageICML :: PandocMonad m => WriterOptions -> Style -> Attr -> Target -> WS m Doc imageICML opts style attr (src, _) = do - res <- lift $ P.fetchItem (writerSourceURL opts) src + res <- runExceptT $ lift $ P.fetchItem (writerSourceURL opts) src imgS <- case res of - Left (_) -> do + Left (_ :: PandocError) -> do lift $ P.warning $ "Could not find image `" ++ src ++ "', skipping..." return def Right (img, _) -> do diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index 0e4999712..5672719f9 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -45,9 +45,10 @@ import Text.Pandoc.Walk import Text.Pandoc.Writers.Shared ( fixDisplayMath ) import Text.Pandoc.Writers.OpenDocument ( writeOpenDocument ) import Control.Monad.State +import Control.Monad.Except (runExceptT) +import Text.Pandoc.Error (PandocError) import Text.Pandoc.XML import Text.Pandoc.Pretty -import qualified Control.Exception as E import System.FilePath ( takeExtension, takeDirectory, (<.>)) import Text.Pandoc.Class ( PandocMonad ) import qualified Text.Pandoc.Class as P @@ -145,9 +146,9 @@ pandocToODT opts doc@(Pandoc meta _) = do -- | transform both Image and Math elements transformPicMath :: PandocMonad m => WriterOptions ->Inline -> O m Inline transformPicMath opts (Image attr@(id', cls, _) lab (src,t)) = do - res <- lift $ P.fetchItem' (writerMediaBag opts) (writerSourceURL opts) src + res <- runExceptT $ lift $ P.fetchItem (writerSourceURL opts) src case res of - Left (_ :: E.SomeException) -> do + Left (_ :: PandocError) -> do lift $ P.warning $ "Could not find image `" ++ src ++ "', skipping..." return $ Emph lab Right (img, mbMimeType) -> do diff --git a/src/Text/Pandoc/Writers/RTF.hs b/src/Text/Pandoc/Writers/RTF.hs index a3351a705..bd3461a03 100644 --- a/src/Text/Pandoc/Writers/RTF.hs +++ b/src/Text/Pandoc/Writers/RTF.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE ScopedTypeVariables #-} {- Copyright (C) 2006-2015 John MacFarlane @@ -43,7 +44,7 @@ import qualified Data.ByteString as B import qualified Data.Map as M import Text.Printf ( printf ) import Text.Pandoc.ImageSize -import Control.Monad.Except (throwError) +import Control.Monad.Except (throwError, runExceptT, lift) import Text.Pandoc.Error import Text.Pandoc.Class (PandocMonad) import qualified Text.Pandoc.Class as P @@ -53,7 +54,7 @@ import qualified Text.Pandoc.Class as P -- If file not found or filetype not jpeg or png, leave the inline unchanged. rtfEmbedImage :: PandocMonad m => WriterOptions -> Inline -> m Inline rtfEmbedImage opts x@(Image attr _ (src,_)) = do - result <- P.fetchItem' (writerMediaBag opts) (writerSourceURL opts) src + result <- runExceptT $ lift $ P.fetchItem (writerSourceURL opts) src case result of Right (imgdata, Just mime) | mime == "image/jpeg" || mime == "image/png" -> do @@ -87,7 +88,7 @@ rtfEmbedImage opts x@(Image attr _ (src,_)) = do Right (_, Nothing) -> do warning $ "Could not determine image type for " ++ src ++ ", skipping." return x - Left e -> do + Left ( e :: PandocError ) -> do warning $ "Could not fetch image " ++ src ++ "\n" ++ show e return x rtfEmbedImage _ x = return x -- cgit v1.2.3 From 994d43117231af8f9825d4df3dd3f2f6af74f8af Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 12 Dec 2016 14:15:49 +0100 Subject: Class: have pure instance of openURL throw an error, for now. Later we may want to include a map of URLs and mime type, bytestring pairs in pure state to serve as a fake internet. --- src/Text/Pandoc/Class.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 9604d7c18..9c11256c8 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -396,7 +396,7 @@ instance PandocMonad PandocPure where modifyPureState $ \st -> st { stUniqStore = us } return u _ -> M.fail "uniq store ran out of elements" - openURL _ = undefined -- TODO + openURL _ = throwError $ PandocSomeError "Cannot open URL in PandocPure" readFileLazy fp = do fps <- getsPureState stFiles case infoFileContents <$> getFileInfo fp fps of -- cgit v1.2.3 From 5814096d79770edabc2822ff66747e3559c61e76 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Tue, 13 Dec 2016 19:35:07 -0500 Subject: Introduce DeferredMediaBag. This is a lazy MediaBag, that will only be evaluated (downloaded/read in) upon demand. Note that we use fetchItem in getDefferedMedia at the moment to read in/download. This means that we don't need to distinguish between URIs and FilePaths. But there is an inefficiency here: `fetchItem` will pull an item out of the mediaBag if it's already there, and then we'll reinsert it. We could separate out `fetchItem` into the function that checks the MediaBag and the underlying downloader/read-inner. --- src/Text/Pandoc/Class.hs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 9c11256c8..4412e8d76 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -100,6 +100,7 @@ import Data.Default import System.IO.Error import qualified Data.Map as M import Text.Pandoc.Error +import Data.Monoid class (Functor m, Applicative m, Monad m, MonadError PandocError m) => PandocMonad m where @@ -167,6 +168,29 @@ warningWithPos pos msg = lift $ warning $ msg ++ " " ++ show pos -- +newtype DeferredMediaPath = DeferredMediaPath {unDefer :: String} + deriving (Show, Eq) + +data DeferredMediaBag = DeferredMediaBag MediaBag [DeferredMediaPath] + deriving (Show) + +instance Monoid DeferredMediaBag where + mempty = DeferredMediaBag mempty mempty + mappend (DeferredMediaBag mb lst) (DeferredMediaBag mb' lst') = + DeferredMediaBag (mb <> mb') (lst <> lst') + +getDeferredMedia :: PandocMonad m => DeferredMediaBag -> m MediaBag +getDeferredMedia (DeferredMediaBag mb defMedia) = do + fetchedMedia <- mapM (\dfp -> fetchItem Nothing (unDefer dfp)) defMedia + return $ foldr + (\(dfp, (bs, mbMime)) mb' -> + MB.insertMedia (unDefer dfp) mbMime (BL.fromStrict bs) mb') + mb + (zip defMedia fetchedMedia) + +dropDeferredMedia :: DeferredMediaBag -> MediaBag +dropDeferredMedia (DeferredMediaBag mb _) = mb + data CommonState = CommonState { stWarnings :: [String] , stMediaBag :: MediaBag , stInputFiles :: Maybe [FilePath] -- cgit v1.2.3 From 55dbc00d55a4136271cd4c4d7ff4ab73d186f4b6 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Tue, 13 Dec 2016 20:22:50 -0500 Subject: Integrate DeferredMediaBag into CommonState The DeferredMediaBag is now the object that is held in state. It should not be visible to users, who will still deal with MediaBag through exported getters and setters. We now have a function `fetchDeferredMedia` which returns () but downloads/reads in all of the deferred media. Note that getMediaBag first fetches all deferred media. --- src/Text/Pandoc/Class.hs | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 4412e8d76..836c57b2e 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -135,15 +135,17 @@ getWarnings :: PandocMonad m => m [String] getWarnings = getsCommonState stWarnings setMediaBag :: PandocMonad m => MediaBag -> m () -setMediaBag mb = modifyCommonState $ \st -> st{stMediaBag = mb} +setMediaBag mb = modifyCommonState $ + \st -> st{stDeferredMediaBag = DeferredMediaBag mb mempty} getMediaBag :: PandocMonad m => m MediaBag -getMediaBag = getsCommonState stMediaBag +getMediaBag = fetchDeferredMedia >> (dropDeferredMedia <$> getsCommonState stDeferredMediaBag) insertMedia :: PandocMonad m => FilePath -> Maybe MimeType -> BL.ByteString -> m () -insertMedia fp mime bs = - modifyCommonState $ \st -> - st{stMediaBag = MB.insertMedia fp mime bs (stMediaBag st) } +insertMedia fp mime bs = do + (DeferredMediaBag mb dm) <- getsCommonState stDeferredMediaBag + let mb' = MB.insertMedia fp mime bs mb + modifyCommonState $ \st -> st{stDeferredMediaBag =DeferredMediaBag mb' dm } getInputFiles :: PandocMonad m => m (Maybe [FilePath]) getInputFiles = getsCommonState stInputFiles @@ -179,8 +181,9 @@ instance Monoid DeferredMediaBag where mappend (DeferredMediaBag mb lst) (DeferredMediaBag mb' lst') = DeferredMediaBag (mb <> mb') (lst <> lst') -getDeferredMedia :: PandocMonad m => DeferredMediaBag -> m MediaBag -getDeferredMedia (DeferredMediaBag mb defMedia) = do +fetchDeferredMedia' :: PandocMonad m => m MediaBag +fetchDeferredMedia' = do + (DeferredMediaBag mb defMedia) <- getsCommonState stDeferredMediaBag fetchedMedia <- mapM (\dfp -> fetchItem Nothing (unDefer dfp)) defMedia return $ foldr (\(dfp, (bs, mbMime)) mb' -> @@ -188,18 +191,22 @@ getDeferredMedia (DeferredMediaBag mb defMedia) = do mb (zip defMedia fetchedMedia) +fetchDeferredMedia :: PandocMonad m => m () +fetchDeferredMedia = fetchDeferredMedia' >>= setMediaBag + dropDeferredMedia :: DeferredMediaBag -> MediaBag dropDeferredMedia (DeferredMediaBag mb _) = mb + data CommonState = CommonState { stWarnings :: [String] - , stMediaBag :: MediaBag + , stDeferredMediaBag :: DeferredMediaBag , stInputFiles :: Maybe [FilePath] , stOutputFile :: Maybe FilePath } instance Default CommonState where def = CommonState { stWarnings = [] - , stMediaBag = mempty + , stDeferredMediaBag = mempty , stInputFiles = Nothing , stOutputFile = Nothing } @@ -284,7 +291,7 @@ fetchItem :: PandocMonad m -> String -> m (B.ByteString, Maybe MimeType) fetchItem sourceURL s = do - mediabag <- getMediaBag + mediabag <- dropDeferredMedia <$> getsCommonState stDeferredMediaBag case lookupMedia s mediabag of Just (mime, bs) -> return $ (BL.toStrict bs, Just mime) Nothing -> -- cgit v1.2.3 From 4b953720c84cec5fb219376a22bb6bc5a0cc0a25 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Tue, 13 Dec 2016 21:02:57 -0500 Subject: Class: Add insertDeferredMedia function. --- src/Text/Pandoc/Class.hs | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 836c57b2e..43721a1f1 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -46,6 +46,7 @@ module Text.Pandoc.Class ( PandocMonad(..) , getMediaBag , setMediaBag , insertMedia + , insertDeferredMedia , fetchItem , getInputFiles , getOutputFile @@ -147,6 +148,12 @@ insertMedia fp mime bs = do let mb' = MB.insertMedia fp mime bs mb modifyCommonState $ \st -> st{stDeferredMediaBag =DeferredMediaBag mb' dm } +insertDeferredMedia :: PandocMonad m => FilePath -> m () +insertDeferredMedia fp = do + (DeferredMediaBag mb dm) <- getsCommonState stDeferredMediaBag + modifyCommonState $ + \st -> st{stDeferredMediaBag = DeferredMediaBag mb ((DeferredMediaPath fp) : dm)} + getInputFiles :: PandocMonad m => m (Maybe [FilePath]) getInputFiles = getsCommonState stInputFiles -- cgit v1.2.3 From 613588a0dcc21c9ebdcea246a6113f0122785eeb Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Tue, 13 Dec 2016 21:44:02 -0500 Subject: Class: Refactor fetchItem. Move the downloading/reading-in logic out of fetchItem, so we can use it to fill the MediaBag. Now when other modules use `fetchItem` it will fill the MediaBag as expected. --- src/Text/Pandoc/Class.hs | 82 ++++++++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 38 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 43721a1f1..11b827aba 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -191,7 +191,7 @@ instance Monoid DeferredMediaBag where fetchDeferredMedia' :: PandocMonad m => m MediaBag fetchDeferredMedia' = do (DeferredMediaBag mb defMedia) <- getsCommonState stDeferredMediaBag - fetchedMedia <- mapM (\dfp -> fetchItem Nothing (unDefer dfp)) defMedia + fetchedMedia <- mapM (\dfp -> downloadOrRead Nothing (unDefer dfp)) defMedia return $ foldr (\(dfp, (bs, mbMime)) mb' -> MB.insertMedia (unDefer dfp) mbMime (BL.fromStrict bs) mb') @@ -298,44 +298,50 @@ fetchItem :: PandocMonad m -> String -> m (B.ByteString, Maybe MimeType) fetchItem sourceURL s = do - mediabag <- dropDeferredMedia <$> getsCommonState stDeferredMediaBag + mediabag <- getMediaBag case lookupMedia s mediabag of - Just (mime, bs) -> return $ (BL.toStrict bs, Just mime) - Nothing -> - case (sourceURL >>= parseURIReference' . - ensureEscaped, ensureEscaped s) of - (Just u, s') -> -- try fetching from relative path at source - case parseURIReference' s' of - Just u' -> openURL $ show $ u' `nonStrictRelativeTo` u - Nothing -> openURL s' -- will throw error - (Nothing, s'@('/':'/':_)) -> -- protocol-relative URI - case parseURIReference' s' of - Just u' -> openURL $ show $ u' `nonStrictRelativeTo` httpcolon - Nothing -> openURL s' -- will throw error - (Nothing, s') -> - case parseURI s' of -- requires absolute URI - -- We don't want to treat C:/ as a scheme: - Just u' | length (uriScheme u') > 2 -> openURL (show u') - Just u' | uriScheme u' == "file:" -> - readLocalFile $ dropWhile (=='/') (uriPath u') - _ -> readLocalFile fp -- get from local file system - where readLocalFile f = do - cont <- readFileStrict f - return (cont, mime) - httpcolon = URI{ uriScheme = "http:", - uriAuthority = Nothing, - uriPath = "", - uriQuery = "", - uriFragment = "" } - dropFragmentAndQuery = takeWhile (\c -> c /= '?' && c /= '#') - fp = unEscapeString $ dropFragmentAndQuery s - mime = case takeExtension fp of - ".gz" -> getMimeType $ dropExtension fp - ".svgz" -> getMimeType $ dropExtension fp ++ ".svg" - x -> getMimeType x - ensureEscaped = escapeURIString isAllowedInURI . map convertSlash - convertSlash '\\' = '/' - convertSlash x = x + Just (mime, bs) -> return $ (BL.toStrict bs, Just mime) + Nothing -> downloadOrRead sourceURL s + +downloadOrRead :: PandocMonad m + => Maybe String + -> String + -> m (B.ByteString, Maybe MimeType) +downloadOrRead sourceURL s = do + case (sourceURL >>= parseURIReference' . + ensureEscaped, ensureEscaped s) of + (Just u, s') -> -- try fetching from relative path at source + case parseURIReference' s' of + Just u' -> openURL $ show $ u' `nonStrictRelativeTo` u + Nothing -> openURL s' -- will throw error + (Nothing, s'@('/':'/':_)) -> -- protocol-relative URI + case parseURIReference' s' of + Just u' -> openURL $ show $ u' `nonStrictRelativeTo` httpcolon + Nothing -> openURL s' -- will throw error + (Nothing, s') -> + case parseURI s' of -- requires absolute URI + -- We don't want to treat C:/ as a scheme: + Just u' | length (uriScheme u') > 2 -> openURL (show u') + Just u' | uriScheme u' == "file:" -> + readLocalFile $ dropWhile (=='/') (uriPath u') + _ -> readLocalFile fp -- get from local file system + where readLocalFile f = do + cont <- readFileStrict f + return (cont, mime) + httpcolon = URI{ uriScheme = "http:", + uriAuthority = Nothing, + uriPath = "", + uriQuery = "", + uriFragment = "" } + dropFragmentAndQuery = takeWhile (\c -> c /= '?' && c /= '#') + fp = unEscapeString $ dropFragmentAndQuery s + mime = case takeExtension fp of + ".gz" -> getMimeType $ dropExtension fp + ".svgz" -> getMimeType $ dropExtension fp ++ ".svg" + x -> getMimeType x + ensureEscaped = escapeURIString isAllowedInURI . map convertSlash + convertSlash '\\' = '/' + convertSlash x = x data PureState = PureState { stStdGen :: StdGen , stWord8Store :: [Word8] -- should be -- cgit v1.2.3 From 5b3bfa28f4a093a1096f628b84180165bc4cff29 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal Date: Wed, 14 Dec 2016 06:34:28 -0500 Subject: Class: Warn instead or erroring if we can't fetch media If deferred media can't be fetched, we catch the error and warn instead. We add an internal function for fetching which returns a Maybe value, and then run catMaybes to only keep the Just's. --- src/Text/Pandoc/Class.hs | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 11b827aba..da9b837f7 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -102,6 +102,7 @@ import System.IO.Error import qualified Data.Map as M import Text.Pandoc.Error import Data.Monoid +import Data.Maybe (catMaybes) class (Functor m, Applicative m, Monad m, MonadError PandocError m) => PandocMonad m where @@ -188,15 +189,27 @@ instance Monoid DeferredMediaBag where mappend (DeferredMediaBag mb lst) (DeferredMediaBag mb' lst') = DeferredMediaBag (mb <> mb') (lst <> lst') + +-- the internal function for downloading individual items. We want to +-- catch errors and return a Nothing with a warning, so we can +-- continue without erroring out. +fetchMediaItem :: PandocMonad m + => DeferredMediaPath + -> m (Maybe (FilePath, B.ByteString, Maybe MimeType)) +fetchMediaItem dfp = + (do (bs, mbmime) <- downloadOrRead Nothing (unDefer dfp) + return $ Just $ (unDefer dfp, bs, mbmime)) + `catchError` + (const $ do warning ("Couldn't access media at " ++ unDefer dfp) + return Nothing) + fetchDeferredMedia' :: PandocMonad m => m MediaBag fetchDeferredMedia' = do (DeferredMediaBag mb defMedia) <- getsCommonState stDeferredMediaBag - fetchedMedia <- mapM (\dfp -> downloadOrRead Nothing (unDefer dfp)) defMedia + fetchedMedia <- catMaybes <$> mapM fetchMediaItem defMedia return $ foldr - (\(dfp, (bs, mbMime)) mb' -> - MB.insertMedia (unDefer dfp) mbMime (BL.fromStrict bs) mb') - mb - (zip defMedia fetchedMedia) + (\(fp, bs, mbMime) mb' -> MB.insertMedia fp mbMime (BL.fromStrict bs) mb') + mb fetchedMedia fetchDeferredMedia :: PandocMonad m => m () fetchDeferredMedia = fetchDeferredMedia' >>= setMediaBag -- cgit v1.2.3 From 93e4cd9f8ca30253d3bf31bbf6e13a762c4c78a0 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 24 Dec 2016 15:57:23 -0700 Subject: Fixed something small that broke in rebase. --- src/Text/Pandoc/Writers/Markdown.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 7f4d37b1f..6a5a1130e 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -1000,7 +1000,7 @@ inlineToMarkdown opts (Math DisplayMath str) = | isEnabled Ext_tex_math_double_backslash opts -> return $ "\\\\[" <> text str <> "\\\\]" | otherwise -> (\x -> cr <> x <> cr) `fmap` - texMathToInlines DisplayMath str >>= inlineListToMarkdown opts + (texMathToInlines DisplayMath str >>= inlineListToMarkdown opts) inlineToMarkdown opts (RawInline f str) = do plain <- asks envPlain if not plain && -- cgit v1.2.3 From 14272521600f9a616c07333261fa258b3dc5c487 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 14 Jan 2017 10:29:12 +0100 Subject: Split extensions code from Options into separate Text.Pandoc.Extensions. API change. However, Extensions exports Options, so this shouldn't have much impact. --- pandoc.cabal | 2 + src/Text/Pandoc/Extensions.hs | 245 ++++++++++++++++++++++++++++++++++++++++++ src/Text/Pandoc/Options.hs | 208 +---------------------------------- 3 files changed, 250 insertions(+), 205 deletions(-) create mode 100644 src/Text/Pandoc/Extensions.hs (limited to 'src/Text') diff --git a/pandoc.cabal b/pandoc.cabal index 2d67421a4..80a35378f 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -257,6 +257,7 @@ Library containers >= 0.1 && < 0.6, unordered-containers >= 0.2 && < 0.3, array >= 0.3 && < 0.6, + largeword >= 1.2 && < 1.3, parsec >= 3.1 && < 3.2, mtl >= 2.2 && < 2.3, filepath >= 1.1 && < 1.5, @@ -328,6 +329,7 @@ Library Exposed-Modules: Text.Pandoc, Text.Pandoc.Options, + Text.Pandoc.Extensions, Text.Pandoc.Pretty, Text.Pandoc.Shared, Text.Pandoc.MediaBag, diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs new file mode 100644 index 000000000..91cd045de --- /dev/null +++ b/src/Text/Pandoc/Extensions.hs @@ -0,0 +1,245 @@ +{-# LANGUAGE DeriveDataTypeable, DeriveGeneric #-} +{- +Copyright (C) 2012-2016 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.Extensions + Copyright : Copyright (C) 2012-2016 John MacFarlane + License : GNU GPL, version 2 or above + + Maintainer : John MacFarlane + Stability : alpha + Portability : portable + +Data structures and functions for representing markup extensions. +-} +module Text.Pandoc.Extensions ( Extension(..) + , pandocExtensions + , plainExtensions + , strictExtensions + , phpMarkdownExtraExtensions + , githubMarkdownExtensions + , multimarkdownExtensions ) +where +import Data.LargeWord (Word256) +import Data.Bits () +import Data.Set (Set) +import qualified Data.Set as Set +import Data.Data (Data) +import Data.Typeable (Typeable) +import GHC.Generics (Generic) + +newtype Extensions = Extensions { unExtensions :: Word256 } + +-- | 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_markdown_in_html_blocks -- ^ Interpret as markdown inside HTML blocks + | Ext_native_divs -- ^ Use Div blocks for contents of
tags + | Ext_native_spans -- ^ Use Span inlines for contents of + | 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_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_all_symbols_escapable -- ^ Make all non-alphanumerics escapable + | Ext_angle_brackets_escapable -- ^ Make < and > escapable + | Ext_intraword_underscores -- ^ Treat underscore inside word as literal + | Ext_blank_before_blockquote -- ^ Require blank line before a blockquote + | Ext_blank_before_header -- ^ Require blank line before a header + | 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_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 + | Ext_emoji -- ^ Support emoji like :smile: + | Ext_auto_identifiers -- ^ Automatic identifiers for headers + | Ext_ascii_identifiers -- ^ ascii-only identifiers for headers + | Ext_header_attributes -- ^ Explicit header attributes {#id .class k=v} + | Ext_mmd_header_identifiers -- ^ Multimarkdown style header identifiers [myid] + | Ext_implicit_header_references -- ^ Implicit reference links for headers + | Ext_line_blocks -- ^ RST style line blocks + | Ext_epub_html_exts -- ^ Recognise the EPUB extended version of HTML + | Ext_shortcut_reference_links -- ^ Shortcut reference links + deriving (Show, Read, Enum, Eq, Ord, Bounded, Data, Typeable, Generic) + +pandocExtensions :: Set Extension +pandocExtensions = Set.fromList + [ Ext_footnotes + , Ext_inline_notes + , Ext_pandoc_title_block + , Ext_yaml_metadata_block + , Ext_table_captions + , Ext_implicit_figures + , Ext_simple_tables + , Ext_multiline_tables + , Ext_grid_tables + , Ext_pipe_tables + , Ext_citations + , Ext_raw_tex + , Ext_raw_html + , Ext_tex_math_dollars + , Ext_latex_macros + , Ext_fenced_code_blocks + , Ext_fenced_code_attributes + , Ext_backtick_code_blocks + , Ext_inline_code_attributes + , Ext_markdown_in_html_blocks + , Ext_native_divs + , Ext_native_spans + , Ext_bracketed_spans + , Ext_escaped_line_breaks + , Ext_fancy_lists + , Ext_startnum + , Ext_definition_lists + , Ext_example_lists + , Ext_all_symbols_escapable + , Ext_intraword_underscores + , Ext_blank_before_blockquote + , Ext_blank_before_header + , Ext_strikeout + , Ext_superscript + , Ext_subscript + , Ext_auto_identifiers + , Ext_header_attributes + , Ext_link_attributes + , Ext_implicit_header_references + , Ext_line_blocks + , Ext_shortcut_reference_links + ] + +plainExtensions :: Set Extension +plainExtensions = Set.fromList + [ Ext_table_captions + , Ext_implicit_figures + , Ext_simple_tables + , Ext_multiline_tables + , Ext_grid_tables + , Ext_latex_macros + , Ext_fancy_lists + , Ext_startnum + , Ext_definition_lists + , Ext_example_lists + , Ext_intraword_underscores + , Ext_blank_before_blockquote + , Ext_blank_before_header + , Ext_strikeout + ] + +phpMarkdownExtraExtensions :: Set Extension +phpMarkdownExtraExtensions = Set.fromList + [ Ext_footnotes + , Ext_pipe_tables + , Ext_raw_html + , Ext_markdown_attribute + , Ext_fenced_code_blocks + , Ext_definition_lists + , Ext_intraword_underscores + , Ext_header_attributes + , Ext_link_attributes + , Ext_abbreviations + , Ext_shortcut_reference_links + ] + +githubMarkdownExtensions :: Set Extension +githubMarkdownExtensions = Set.fromList + [ Ext_angle_brackets_escapable + , Ext_pipe_tables + , Ext_raw_html + , Ext_fenced_code_blocks + , Ext_auto_identifiers + , Ext_ascii_identifiers + , Ext_backtick_code_blocks + , Ext_autolink_bare_uris + , Ext_intraword_underscores + , Ext_strikeout + , Ext_hard_line_breaks + , Ext_emoji + , Ext_lists_without_preceding_blankline + , Ext_shortcut_reference_links + ] + +multimarkdownExtensions :: Set Extension +multimarkdownExtensions = Set.fromList + [ Ext_pipe_tables + , Ext_raw_html + , Ext_markdown_attribute + , Ext_mmd_link_attributes + -- , Ext_raw_tex + -- Note: MMD's raw TeX syntax requires raw TeX to be + -- enclosed in HTML comment + , Ext_tex_math_double_backslash + , Ext_intraword_underscores + , Ext_mmd_title_block + , Ext_footnotes + , Ext_definition_lists + , Ext_all_symbols_escapable + , Ext_implicit_header_references + , Ext_auto_identifiers + , Ext_mmd_header_identifiers + , Ext_implicit_figures + -- Note: MMD's syntax for superscripts and subscripts + -- is a bit more permissive than pandoc's, allowing + -- e^2 and a~1 instead of e^2^ and a~1~, so even with + -- these options we don't have full support for MMD + -- superscripts and subscripts, but there's no reason + -- not to include these: + , Ext_superscript + , Ext_subscript + ] + +strictExtensions :: Set Extension +strictExtensions = Set.fromList + [ Ext_raw_html + , Ext_shortcut_reference_links + ] + diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index 4fee577e7..56681f4b2 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -29,13 +29,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Data structures and functions for representing parser and writer options. -} -module Text.Pandoc.Options ( Extension(..) - , pandocExtensions - , plainExtensions - , strictExtensions - , phpMarkdownExtraExtensions - , githubMarkdownExtensions - , multimarkdownExtensions +module Text.Pandoc.Options ( module Text.Pandoc.Extensions , ReaderOptions(..) , HTMLMathMethod (..) , CiteMethod (..) @@ -50,212 +44,16 @@ module Text.Pandoc.Options ( Extension(..) , def , isEnabled ) where -import Data.Set (Set) +import Text.Pandoc.Extensions import qualified Data.Set as Set import Data.Default import Text.Pandoc.Highlighting (Style, pygments) import Text.Pandoc.MediaBag (MediaBag) import Data.Data (Data) +import Data.Set (Set) import Data.Typeable (Typeable) import GHC.Generics (Generic) --- | 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_markdown_in_html_blocks -- ^ Interpret as markdown inside HTML blocks - | Ext_native_divs -- ^ Use Div blocks for contents of
tags - | Ext_native_spans -- ^ Use Span inlines for contents of - | 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_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_all_symbols_escapable -- ^ Make all non-alphanumerics escapable - | Ext_angle_brackets_escapable -- ^ Make < and > escapable - | Ext_intraword_underscores -- ^ Treat underscore inside word as literal - | Ext_blank_before_blockquote -- ^ Require blank line before a blockquote - | Ext_blank_before_header -- ^ Require blank line before a header - | 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_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 - | Ext_emoji -- ^ Support emoji like :smile: - | Ext_auto_identifiers -- ^ Automatic identifiers for headers - | Ext_ascii_identifiers -- ^ ascii-only identifiers for headers - | Ext_header_attributes -- ^ Explicit header attributes {#id .class k=v} - | Ext_mmd_header_identifiers -- ^ Multimarkdown style header identifiers [myid] - | Ext_implicit_header_references -- ^ Implicit reference links for headers - | Ext_line_blocks -- ^ RST style line blocks - | Ext_epub_html_exts -- ^ Recognise the EPUB extended version of HTML - | Ext_shortcut_reference_links -- ^ Shortcut reference links - deriving (Show, Read, Enum, Eq, Ord, Bounded, Data, Typeable, Generic) - -pandocExtensions :: Set Extension -pandocExtensions = Set.fromList - [ Ext_footnotes - , Ext_inline_notes - , Ext_pandoc_title_block - , Ext_yaml_metadata_block - , Ext_table_captions - , Ext_implicit_figures - , Ext_simple_tables - , Ext_multiline_tables - , Ext_grid_tables - , Ext_pipe_tables - , Ext_citations - , Ext_raw_tex - , Ext_raw_html - , Ext_tex_math_dollars - , Ext_latex_macros - , Ext_fenced_code_blocks - , Ext_fenced_code_attributes - , Ext_backtick_code_blocks - , Ext_inline_code_attributes - , Ext_markdown_in_html_blocks - , Ext_native_divs - , Ext_native_spans - , Ext_bracketed_spans - , Ext_escaped_line_breaks - , Ext_fancy_lists - , Ext_startnum - , Ext_definition_lists - , Ext_example_lists - , Ext_all_symbols_escapable - , Ext_intraword_underscores - , Ext_blank_before_blockquote - , Ext_blank_before_header - , Ext_strikeout - , Ext_superscript - , Ext_subscript - , Ext_auto_identifiers - , Ext_header_attributes - , Ext_link_attributes - , Ext_implicit_header_references - , Ext_line_blocks - , Ext_shortcut_reference_links - ] - -plainExtensions :: Set Extension -plainExtensions = Set.fromList - [ Ext_table_captions - , Ext_implicit_figures - , Ext_simple_tables - , Ext_multiline_tables - , Ext_grid_tables - , Ext_latex_macros - , Ext_fancy_lists - , Ext_startnum - , Ext_definition_lists - , Ext_example_lists - , Ext_intraword_underscores - , Ext_blank_before_blockquote - , Ext_blank_before_header - , Ext_strikeout - ] - -phpMarkdownExtraExtensions :: Set Extension -phpMarkdownExtraExtensions = Set.fromList - [ Ext_footnotes - , Ext_pipe_tables - , Ext_raw_html - , Ext_markdown_attribute - , Ext_fenced_code_blocks - , Ext_definition_lists - , Ext_intraword_underscores - , Ext_header_attributes - , Ext_link_attributes - , Ext_abbreviations - , Ext_shortcut_reference_links - ] - -githubMarkdownExtensions :: Set Extension -githubMarkdownExtensions = Set.fromList - [ Ext_angle_brackets_escapable - , Ext_pipe_tables - , Ext_raw_html - , Ext_fenced_code_blocks - , Ext_auto_identifiers - , Ext_ascii_identifiers - , Ext_backtick_code_blocks - , Ext_autolink_bare_uris - , Ext_intraword_underscores - , Ext_strikeout - , Ext_hard_line_breaks - , Ext_emoji - , Ext_lists_without_preceding_blankline - , Ext_shortcut_reference_links - ] - -multimarkdownExtensions :: Set Extension -multimarkdownExtensions = Set.fromList - [ Ext_pipe_tables - , Ext_raw_html - , Ext_markdown_attribute - , Ext_mmd_link_attributes - -- , Ext_raw_tex - -- Note: MMD's raw TeX syntax requires raw TeX to be - -- enclosed in HTML comment - , Ext_tex_math_double_backslash - , Ext_intraword_underscores - , Ext_mmd_title_block - , Ext_footnotes - , Ext_definition_lists - , Ext_all_symbols_escapable - , Ext_implicit_header_references - , Ext_auto_identifiers - , Ext_mmd_header_identifiers - , Ext_implicit_figures - -- Note: MMD's syntax for superscripts and subscripts - -- is a bit more permissive than pandoc's, allowing - -- e^2 and a~1 instead of e^2^ and a~1~, so even with - -- these options we don't have full support for MMD - -- superscripts and subscripts, but there's no reason - -- not to include these: - , Ext_superscript - , Ext_subscript - ] - -strictExtensions :: Set Extension -strictExtensions = Set.fromList - [ Ext_raw_html - , Ext_shortcut_reference_links - ] - data ReaderOptions = ReaderOptions{ readerExtensions :: Set Extension -- ^ Syntax extensions , readerSmart :: Bool -- ^ Smart punctuation -- cgit v1.2.3 From 3876b91448ad8d279f5d5a9d217e00cf4583e14b Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 14 Jan 2017 13:06:27 +0100 Subject: Make Extensions a custom type instead of a Set Extension. The type is implemented in terms of an underlying bitset which should be more efficient. API change: from Text.Pandoc.Extensions export Extensions, emptyExtensions, extensionsFromList, enableExtension, disableExtension, extensionEnabled. --- pandoc.cabal | 1 - pandoc.hs | 3 +-- src/Text/Pandoc.hs | 36 +++++++++++++------------ src/Text/Pandoc/Extensions.hs | 54 +++++++++++++++++++++++++------------ src/Text/Pandoc/Options.hs | 8 +++--- src/Text/Pandoc/Parsing.hs | 8 +++--- src/Text/Pandoc/Readers/Markdown.hs | 18 ++++++------- tests/Tests/Readers/Markdown.hs | 7 +++-- 8 files changed, 76 insertions(+), 59 deletions(-) (limited to 'src/Text') diff --git a/pandoc.cabal b/pandoc.cabal index 80a35378f..3e87fec97 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -257,7 +257,6 @@ Library containers >= 0.1 && < 0.6, unordered-containers >= 0.2 && < 0.3, array >= 0.3 && < 0.6, - largeword >= 1.2 && < 1.3, parsec >= 3.1 && < 3.2, mtl >= 2.2 && < 2.3, filepath >= 1.1 && < 1.5, diff --git a/pandoc.hs b/pandoc.hs index b758aaa97..cd29428ec 100644 --- a/pandoc.hs +++ b/pandoc.hs @@ -47,7 +47,6 @@ import System.Environment ( getArgs, getProgName, getEnvironment ) import System.Exit ( ExitCode (..), exitSuccess ) import System.FilePath import System.Console.GetOpt -import qualified Data.Set as Set import Data.Char ( toLower, toUpper ) import Data.List ( intercalate, isPrefixOf, isSuffixOf, sort ) import System.Directory ( getAppUserDataDirectory, findExecutable, @@ -1295,7 +1294,7 @@ options = (NoArg (\_ -> do let showExt x = drop 4 (show x) ++ - if x `Set.member` pandocExtensions + if extensionEnabled x pandocExtensions then " +" else " -" mapM_ (UTF8.hPutStrLn stdout . showExt) diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index b94d05718..86f70b293 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -184,15 +184,13 @@ import Text.Pandoc.Class import Data.Aeson import qualified Data.ByteString.Lazy as BL import Data.List (intercalate) -import Data.Set (Set) -import qualified Data.Set as Set import Text.Parsec import Text.Parsec.Error import qualified Text.Pandoc.UTF8 as UTF8 import Control.Monad.Except (throwError) parseFormatSpec :: String - -> Either ParseError (String, Set Extension -> Set Extension) + -> Either ParseError (String, Extensions -> Extensions) parseFormatSpec = parse formatSpec "" where formatSpec = do name <- formatName @@ -208,8 +206,8 @@ parseFormatSpec = parse formatSpec "" | name == "lhs" -> return Ext_literate_haskell | otherwise -> fail $ "Unknown extension: " ++ name return $ case polarity of - '-' -> Set.delete ext - _ -> Set.insert ext + '-' -> disableExtension ext + _ -> enableExtension ext -- TODO: when we get the PandocMonad stuff all sorted out, -- we can simply these types considerably. Errors/MediaBag can be @@ -330,25 +328,29 @@ writers = [ ,("tei" , StringWriter writeTEI) ] -getDefaultExtensions :: String -> Set Extension +getDefaultExtensions :: String -> Extensions getDefaultExtensions "markdown_strict" = strictExtensions getDefaultExtensions "markdown_phpextra" = phpMarkdownExtraExtensions getDefaultExtensions "markdown_mmd" = multimarkdownExtensions getDefaultExtensions "markdown_github" = githubMarkdownExtensions getDefaultExtensions "markdown" = pandocExtensions getDefaultExtensions "plain" = plainExtensions -getDefaultExtensions "org" = Set.fromList [Ext_citations, - Ext_auto_identifiers] -getDefaultExtensions "textile" = Set.fromList [Ext_auto_identifiers] -getDefaultExtensions "html" = Set.fromList [Ext_auto_identifiers, - Ext_native_divs, - Ext_native_spans] +getDefaultExtensions "org" = extensionsFromList + [Ext_citations, Ext_auto_identifiers] +getDefaultExtensions "textile" = extensionsFromList + [Ext_auto_identifiers] +getDefaultExtensions "html" = extensionsFromList + [Ext_auto_identifiers, + Ext_native_divs, + Ext_native_spans] getDefaultExtensions "html5" = getDefaultExtensions "html" -getDefaultExtensions "epub" = Set.fromList [Ext_raw_html, - Ext_native_divs, - Ext_native_spans, - Ext_epub_html_exts] -getDefaultExtensions _ = Set.fromList [Ext_auto_identifiers] +getDefaultExtensions "epub" = extensionsFromList + [Ext_raw_html, + Ext_native_divs, + Ext_native_spans, + Ext_epub_html_exts] +getDefaultExtensions _ = extensionsFromList + [Ext_auto_identifiers] -- | Retrieve reader based on formatSpec (format+extensions). getReader :: PandocMonad m => String -> Either String (Reader m) diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 91cd045de..68d76792c 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -29,6 +29,12 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Data structures and functions for representing markup extensions. -} module Text.Pandoc.Extensions ( Extension(..) + , Extensions + , emptyExtensions + , extensionsFromList + , extensionEnabled + , enableExtension + , disableExtension , pandocExtensions , plainExtensions , strictExtensions @@ -36,15 +42,29 @@ module Text.Pandoc.Extensions ( Extension(..) , githubMarkdownExtensions , multimarkdownExtensions ) where -import Data.LargeWord (Word256) -import Data.Bits () -import Data.Set (Set) -import qualified Data.Set as Set +import Data.Word (Word64) +import Data.Bits (testBit, setBit, clearBit) import Data.Data (Data) import Data.Typeable (Typeable) import GHC.Generics (Generic) -newtype Extensions = Extensions { unExtensions :: Word256 } +newtype Extensions = Extensions Word64 + deriving (Show, Read, Eq, Ord, Bounded, Data, Typeable, Generic) + +extensionsFromList :: [Extension] -> Extensions +extensionsFromList = foldr enableExtension emptyExtensions + +emptyExtensions :: Extensions +emptyExtensions = Extensions 0 + +extensionEnabled :: Extension -> Extensions -> Bool +extensionEnabled x (Extensions exts) = testBit exts (fromEnum x) + +enableExtension :: Extension -> Extensions -> Extensions +enableExtension x (Extensions exts) = Extensions (setBit exts (fromEnum x)) + +disableExtension :: Extension -> Extensions -> Extensions +disableExtension x (Extensions exts) = Extensions (clearBit exts (fromEnum x)) -- | Individually selectable syntax extensions. data Extension = @@ -112,8 +132,8 @@ data Extension = | Ext_shortcut_reference_links -- ^ Shortcut reference links deriving (Show, Read, Enum, Eq, Ord, Bounded, Data, Typeable, Generic) -pandocExtensions :: Set Extension -pandocExtensions = Set.fromList +pandocExtensions :: Extensions +pandocExtensions = extensionsFromList [ Ext_footnotes , Ext_inline_notes , Ext_pandoc_title_block @@ -157,8 +177,8 @@ pandocExtensions = Set.fromList , Ext_shortcut_reference_links ] -plainExtensions :: Set Extension -plainExtensions = Set.fromList +plainExtensions :: Extensions +plainExtensions = extensionsFromList [ Ext_table_captions , Ext_implicit_figures , Ext_simple_tables @@ -175,8 +195,8 @@ plainExtensions = Set.fromList , Ext_strikeout ] -phpMarkdownExtraExtensions :: Set Extension -phpMarkdownExtraExtensions = Set.fromList +phpMarkdownExtraExtensions :: Extensions +phpMarkdownExtraExtensions = extensionsFromList [ Ext_footnotes , Ext_pipe_tables , Ext_raw_html @@ -190,8 +210,8 @@ phpMarkdownExtraExtensions = Set.fromList , Ext_shortcut_reference_links ] -githubMarkdownExtensions :: Set Extension -githubMarkdownExtensions = Set.fromList +githubMarkdownExtensions :: Extensions +githubMarkdownExtensions = extensionsFromList [ Ext_angle_brackets_escapable , Ext_pipe_tables , Ext_raw_html @@ -208,8 +228,8 @@ githubMarkdownExtensions = Set.fromList , Ext_shortcut_reference_links ] -multimarkdownExtensions :: Set Extension -multimarkdownExtensions = Set.fromList +multimarkdownExtensions :: Extensions +multimarkdownExtensions = extensionsFromList [ Ext_pipe_tables , Ext_raw_html , Ext_markdown_attribute @@ -237,8 +257,8 @@ multimarkdownExtensions = Set.fromList , Ext_subscript ] -strictExtensions :: Set Extension -strictExtensions = Set.fromList +strictExtensions :: Extensions +strictExtensions = extensionsFromList [ Ext_raw_html , Ext_shortcut_reference_links ] diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index 56681f4b2..e18ee7d5f 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -45,17 +45,15 @@ module Text.Pandoc.Options ( module Text.Pandoc.Extensions , isEnabled ) where import Text.Pandoc.Extensions -import qualified Data.Set as Set import Data.Default import Text.Pandoc.Highlighting (Style, pygments) import Text.Pandoc.MediaBag (MediaBag) import Data.Data (Data) -import Data.Set (Set) import Data.Typeable (Typeable) import GHC.Generics (Generic) data ReaderOptions = ReaderOptions{ - readerExtensions :: Set Extension -- ^ Syntax extensions + readerExtensions :: Extensions -- ^ Syntax extensions , readerSmart :: Bool -- ^ Smart punctuation , readerStandalone :: Bool -- ^ Standalone document with header , readerParseRaw :: Bool -- ^ Parse raw HTML, LaTeX @@ -165,7 +163,7 @@ data WriterOptions = WriterOptions , writerNumberSections :: Bool -- ^ Number sections in LaTeX , writerNumberOffset :: [Int] -- ^ Starting number for section, subsection, ... , writerSectionDivs :: Bool -- ^ Put sections in div tags in HTML - , writerExtensions :: Set Extension -- ^ Markdown extensions that can be used + , writerExtensions :: Extensions -- ^ Markdown extensions that can be used , writerReferenceLinks :: Bool -- ^ Use reference links in writing markdown, rst , writerDpi :: Int -- ^ Dpi for pixel to/from inch/cm conversions , writerWrapText :: WrapOption -- ^ Option for wrapping text @@ -248,4 +246,4 @@ instance Default WriterOptions where -- | Returns True if the given extension is enabled. isEnabled :: Extension -> WriterOptions -> Bool -isEnabled ext opts = ext `Set.member` (writerExtensions opts) +isEnabled ext opts = ext `extensionEnabled` (writerExtensions opts) diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index f53db1cbc..cd85fe58e 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -1031,11 +1031,11 @@ defaultParserState = -- | Succeed only if the extension is enabled. guardEnabled :: (Stream s m a, HasReaderOptions st) => Extension -> ParserT s st m () -guardEnabled ext = getOption readerExtensions >>= guard . Set.member ext +guardEnabled ext = getOption readerExtensions >>= guard . extensionEnabled ext -- | Succeed only if the extension is disabled. guardDisabled :: (Stream s m a, HasReaderOptions st) => Extension -> ParserT s st m () -guardDisabled ext = getOption readerExtensions >>= guard . not . Set.member ext +guardDisabled ext = getOption readerExtensions >>= guard . not . extensionEnabled ext -- | Update the position on which the last string ended. updateLastStrPos :: (Stream s m a, HasLastStrPosition st) => ParserT s st m () @@ -1090,10 +1090,10 @@ registerHeader (ident,classes,kvs) header' = do ids <- extractIdentifierList <$> getState exts <- getOption readerExtensions let insert' = M.insertWith (\_new old -> old) - if null ident && Ext_auto_identifiers `Set.member` exts + if null ident && Ext_auto_identifiers `extensionEnabled` exts then do let id' = uniqueIdent (B.toList header') ids - let id'' = if Ext_ascii_identifiers `Set.member` exts + let id'' = if Ext_ascii_identifiers `extensionEnabled` exts then catMaybes $ map toAsciiChar id' else id' updateState $ updateIdentifierList $ Set.insert id' diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index a6156e497..e0694f38a 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -61,7 +61,6 @@ import Text.Pandoc.Readers.HTML ( htmlTag, htmlInBalanced, isInlineTag, isBlockT import Control.Monad import System.FilePath (takeExtension, addExtension) import Text.HTML.TagSoup -import qualified Data.Set as Set import Text.Printf (printf) import Debug.Trace (trace) import Data.Monoid ((<>)) @@ -310,11 +309,11 @@ toMetaValue opts x = toMeta <$> readMarkdown opts' (T.unpack x) | otherwise -> MetaInlines xs Pandoc _ bs -> MetaBlocks bs endsWithNewline t = T.pack "\n" `T.isSuffixOf` t - opts' = opts{readerExtensions=readerExtensions opts `Set.difference` meta_exts} - meta_exts = Set.fromList [ Ext_pandoc_title_block - , Ext_mmd_title_block - , Ext_yaml_metadata_block - ] + opts' = opts{readerExtensions = + disableExtension Ext_pandoc_title_block $ + disableExtension Ext_mmd_title_block $ + disableExtension Ext_yaml_metadata_block $ + readerExtensions opts } yamlToMeta :: PandocMonad m => ReaderOptions -> Yaml.Value -> m MetaValue yamlToMeta opts (Yaml.String t) = toMetaValue opts t @@ -534,8 +533,9 @@ header = setextHeader <|> atxHeader "header" atxChar :: PandocMonad m => MarkdownParser m Char atxChar = do exts <- getOption readerExtensions - return $ if Set.member Ext_literate_haskell exts - then '=' else '#' + return $ if extensionEnabled Ext_literate_haskell exts + then '=' + else '#' atxHeader :: PandocMonad m => MarkdownParser m (F Blocks) atxHeader = try $ do @@ -1013,7 +1013,7 @@ para = try $ do result' <- result case B.toList result' of [Image attr alt (src,tit)] - | Ext_implicit_figures `Set.member` exts -> + | Ext_implicit_figures `extensionEnabled` exts -> -- the fig: at beginning of title indicates a figure return $ B.para $ B.singleton $ Image attr alt (src,'f':'i':'g':':':tit) diff --git a/tests/Tests/Readers/Markdown.hs b/tests/Tests/Readers/Markdown.hs index b43a0ec49..3bc8f34ae 100644 --- a/tests/Tests/Readers/Markdown.hs +++ b/tests/Tests/Readers/Markdown.hs @@ -6,7 +6,6 @@ import Test.Framework import Tests.Helpers import Text.Pandoc.Arbitrary() import Text.Pandoc.Builder -import qualified Data.Set as Set import Text.Pandoc markdown :: String -> Pandoc @@ -16,7 +15,7 @@ markdownSmart :: String -> Pandoc markdownSmart = purely $ readMarkdown def { readerSmart = True } markdownCDL :: String -> Pandoc -markdownCDL = purely $ readMarkdown def { readerExtensions = Set.insert +markdownCDL = purely $ readMarkdown def { readerExtensions = enableExtension Ext_compact_definition_lists $ readerExtensions def } markdownGH :: String -> Pandoc @@ -30,7 +29,7 @@ infix 4 =: testBareLink :: (String, Inlines) -> Test testBareLink (inp, ils) = test (purely $ readMarkdown def{ readerExtensions = - Set.fromList [Ext_autolink_bare_uris, Ext_raw_html] }) + extensionsFromList [Ext_autolink_bare_uris, Ext_raw_html] }) inp (inp, doc $ para ils) autolink :: String -> Inlines @@ -303,7 +302,7 @@ tests = [ testGroup "inline code" =?> para (note (para "See [^1]")) ] , testGroup "lhs" - [ test (purely $ readMarkdown def{ readerExtensions = Set.insert + [ test (purely $ readMarkdown def{ readerExtensions = enableExtension Ext_literate_haskell $ readerExtensions def }) "inverse bird tracks and html" $ "> a\n\n< b\n\n
\n" -- cgit v1.2.3 From 6f8b967d98ea4270aa2492688fbcdfe8bad150b2 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 14 Jan 2017 18:27:06 +0100 Subject: Removed readerSmart and the --smart option; added Ext_smart extension. Now you will need to do -f markdown+smart instead of -f markdown --smart This change opens the way for writers, in addition to readers, to be sensitive to +smart, but this change hasn't yet been made. API change. Command-line option change. Updated manual. --- MANUAL.txt | 45 ++++++++++++++-------------------- pandoc.hs | 29 ++++++++-------------- src/Text/Pandoc.hs | 9 ++++++- src/Text/Pandoc/Extensions.hs | 1 + src/Text/Pandoc/Options.hs | 2 -- src/Text/Pandoc/Parsing.hs | 6 +---- src/Text/Pandoc/Readers/CommonMark.hs | 2 +- src/Text/Pandoc/Readers/LaTeX.hs | 4 +-- src/Text/Pandoc/Readers/Markdown.hs | 6 ++--- src/Text/Pandoc/Readers/Org/Inlines.hs | 2 +- src/Text/Pandoc/Readers/RST.hs | 2 +- src/Text/Pandoc/Readers/TWiki.hs | 2 +- tests/Tests/Old.hs | 8 +++--- tests/Tests/Readers/Markdown.hs | 3 ++- tests/Tests/Readers/Org.hs | 3 ++- 15 files changed, 55 insertions(+), 69 deletions(-) (limited to 'src/Text') diff --git a/MANUAL.txt b/MANUAL.txt index 9ac79da2e..239367480 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -394,15 +394,6 @@ Reader options HTML codes and LaTeX environments. (The LaTeX reader does pass through untranslatable LaTeX *commands*, even if `-R` is not specified.) -`-S`, `--smart` - -: Produce typographically correct output, converting straight quotes - to curly quotes, `---` to em-dashes, `--` to en-dashes, and - `...` to ellipses. Nonbreaking spaces are inserted after certain - abbreviations, such as "Mr." (Note: This option is selected automatically - when the output format is `latex` or `context`, unless `--no-tex-ligatures` - is used. It has no effect for `latex` input.) - `--old-dashes` : Selects the pandoc <= 1.8.2.1 behavior for parsing smart dashes: `-` before @@ -721,12 +712,12 @@ Options affecting specific writers than parsing ligatures for quotation marks and dashes. In writing LaTeX or ConTeXt, print unicode quotation mark and dash characters literally, rather than converting them to - the standard ASCII TeX ligatures. Note: normally `--smart` - is selected automatically for LaTeX and ConTeXt output, but - it must be specified explicitly if `--no-tex-ligatures` is + the standard ASCII TeX ligatures. Note: normally the `smart` + extension is selected automatically for LaTeX and ConTeXt output, + but it must be specified explicitly if `--no-tex-ligatures` is selected. If you use literal curly quotes, dashes, and ellipses in your source, then you may want to use - `--no-tex-ligatures` without `--smart`. + `--no-tex-ligatures` without `+smart`. `--listings` @@ -2639,20 +2630,6 @@ two trailing spaces on a line. Backslash escapes do not work in verbatim contexts. -Smart punctuation ------------------ - -#### Extension #### - -If the `--smart` option is specified, pandoc will produce typographically -correct output, converting straight quotes to curly quotes, `---` to -em-dashes, `--` to en-dashes, and `...` to ellipses. Nonbreaking spaces -are inserted after certain abbreviations, such as "Mr." - -Note: if your LaTeX template or any included header file call for the -[`csquotes`] package, pandoc will detect this automatically and use -`\enquote{...}` for quoted text. - Inline formatting ----------------- @@ -3410,6 +3387,20 @@ in pandoc, but may be enabled by adding `+EXTENSION` to the format name, where `EXTENSION` is the name of the extension. Thus, for example, `markdown+hard_line_breaks` is Markdown with hard line breaks. +#### Extension: `smart` #### + +Produce typographically correct output, converting straight +quotes to curly quotes, `---` to em-dashes, `--` to en-dashes, +and `...` to ellipses. Nonbreaking spaces are inserted after +certain abbreviations, such as "Mr." (Note: This option is +selected automatically when the output format is `latex` or +`context`, unless `--no-tex-ligatures` is used. It has no +effect for `latex` input.) + +Note: if your LaTeX template or any included header file call +for the [`csquotes`] package, pandoc will detect this +automatically and use `\enquote{...}` for quoted text. + #### Extension: `angle_brackets_escapable` #### Allow `<` and `>` to be backslash-escaped, as they can be in diff --git a/pandoc.hs b/pandoc.hs index cd29428ec..371ad16e0 100644 --- a/pandoc.hs +++ b/pandoc.hs @@ -112,7 +112,6 @@ convertWithOpts opts args = do , optSectionDivs = sectionDivs , optIncremental = incremental , optSelfContained = selfContained - , optSmart = smart , optOldDashes = oldDashes , optHtml5 = html5 , optHtmlQTags = htmlQTags @@ -212,10 +211,6 @@ convertWithOpts opts args = do let conTeXtOutput = format == "context" let html5Output = format == "html5" - 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 @@ -295,11 +290,15 @@ convertWithOpts opts args = do uriFragment = "" } _ -> Nothing - let readerOpts = def{ readerSmart = if laTeXInput - then texLigatures - else smart || (texLigatures && - (laTeXOutput || conTeXtOutput)) - , readerStandalone = standalone' + {- TODO - smart is now an extension, but we should prob make + - texligatures one too... + let smartExt = if laTeXInput + then texLigatures + else smart || (texLigatures && + (laTeXOutput || conTeXtOutput)) + -} + + let readerOpts = def{ readerStandalone = standalone' , readerParseRaw = parseRaw , readerColumns = columns , readerTabStop = tabStop @@ -547,7 +546,6 @@ data Opt = Opt , optSectionDivs :: Bool -- ^ Put sections in div tags in HTML , optIncremental :: Bool -- ^ Use incremental lists in Slidy/Slideous/S5 , optSelfContained :: Bool -- ^ Make HTML accessible offline - , optSmart :: Bool -- ^ Use smart typography , optOldDashes :: Bool -- ^ Parse dashes like pandoc <=1.8.2.1 , optHtml5 :: Bool -- ^ Produce HTML5 in HTML , optHtmlQTags :: Bool -- ^ Use tags in HTML @@ -613,7 +611,6 @@ defaultOpts = Opt , optSectionDivs = False , optIncremental = False , optSelfContained = False - , optSmart = False , optOldDashes = False , optHtml5 = False , optHtmlQTags = False @@ -692,15 +689,9 @@ options = (\opt -> return opt { optParseRaw = True })) "" -- "Parse untranslatable HTML codes and LaTeX environments as raw" - , Option "S" ["smart"] - (NoArg - (\opt -> return opt { optSmart = True })) - "" -- "Use smart quotes, dashes, and ellipses" - , Option "" ["old-dashes"] (NoArg - (\opt -> return opt { optSmart = True - , optOldDashes = True })) + (\opt -> return opt { optOldDashes = True })) "" -- "Use smart quotes, dashes, and ellipses" , Option "" ["base-header-level"] diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index 86f70b293..3671b08ad 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -336,7 +336,8 @@ getDefaultExtensions "markdown_github" = githubMarkdownExtensions getDefaultExtensions "markdown" = pandocExtensions getDefaultExtensions "plain" = plainExtensions getDefaultExtensions "org" = extensionsFromList - [Ext_citations, Ext_auto_identifiers] + [Ext_citations, + Ext_auto_identifiers] getDefaultExtensions "textile" = extensionsFromList [Ext_auto_identifiers] getDefaultExtensions "html" = extensionsFromList @@ -349,6 +350,12 @@ getDefaultExtensions "epub" = extensionsFromList Ext_native_divs, Ext_native_spans, Ext_epub_html_exts] +getDefaultExtensions "latex" = extensionsFromList + [Ext_smart, + Ext_auto_identifiers] +getDefaultExtensions "context" = extensionsFromList + [Ext_smart, + Ext_auto_identifiers] getDefaultExtensions _ = extensionsFromList [Ext_auto_identifiers] diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 68d76792c..584aa18e2 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -130,6 +130,7 @@ data Extension = | Ext_line_blocks -- ^ RST style line blocks | Ext_epub_html_exts -- ^ Recognise the EPUB extended version of HTML | Ext_shortcut_reference_links -- ^ Shortcut reference links + | Ext_smart -- ^ "Smart" quotes, apostrophes, ellipses, dashes deriving (Show, Read, Enum, Eq, Ord, Bounded, Data, Typeable, Generic) pandocExtensions :: Extensions diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index e18ee7d5f..f325e9905 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -54,7 +54,6 @@ import GHC.Generics (Generic) data ReaderOptions = ReaderOptions{ readerExtensions :: Extensions -- ^ Syntax extensions - , readerSmart :: Bool -- ^ Smart punctuation , readerStandalone :: Bool -- ^ Standalone document with header , readerParseRaw :: Bool -- ^ Parse raw HTML, LaTeX , readerColumns :: Int -- ^ Number of columns in terminal @@ -74,7 +73,6 @@ data ReaderOptions = ReaderOptions{ instance Default ReaderOptions where def = ReaderOptions{ readerExtensions = pandocExtensions - , readerSmart = False , readerStandalone = False , readerParseRaw = False , readerColumns = 80 diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index cd85fe58e..b92894dd7 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -1105,15 +1105,11 @@ registerHeader (ident,classes,kvs) header' = do updateState $ updateHeaderMap $ insert' header' ident return (ident,classes,kvs) --- | Fail unless we're in "smart typography" mode. -failUnlessSmart :: (Stream s m a, HasReaderOptions st) => ParserT s st m () -failUnlessSmart = getOption readerSmart >>= guard - smartPunctuation :: (HasReaderOptions st, HasLastStrPosition st, HasQuoteContext st m, Stream s m Char) => ParserT s st m Inlines -> ParserT s st m Inlines smartPunctuation inlineParser = do - failUnlessSmart + guardEnabled Ext_smart choice [ quoted inlineParser, apostrophe, dash, ellipses ] apostrophe :: Stream s m Char => ParserT s st m Inlines diff --git a/src/Text/Pandoc/Readers/CommonMark.hs b/src/Text/Pandoc/Readers/CommonMark.hs index 38c54c8dc..b0bcbd580 100644 --- a/src/Text/Pandoc/Readers/CommonMark.hs +++ b/src/Text/Pandoc/Readers/CommonMark.hs @@ -43,7 +43,7 @@ import Text.Pandoc.Class (PandocMonad) readCommonMark :: PandocMonad m => ReaderOptions -> String -> m Pandoc readCommonMark opts s = return $ nodeToPandoc $ commonmarkToNode opts' $ pack s - where opts' = if readerSmart opts + where opts' = if extensionEnabled Ext_smart (readerExtensions opts) then [optNormalize, optSmart] else [optNormalize] diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 1c8536924..86ff2b83a 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -187,7 +187,7 @@ mathChars = quoted' :: PandocMonad m => (Inlines -> Inlines) -> LP m String -> LP m () -> LP m Inlines quoted' f starter ender = do startchs <- starter - smart <- getOption readerSmart + smart <- extensionEnabled Ext_smart <$> getOption readerExtensions if smart then do ils <- many (notFollowedBy ender >> inline) @@ -209,7 +209,7 @@ doubleQuote = do singleQuote :: PandocMonad m => LP m Inlines singleQuote = do - smart <- getOption readerSmart + smart <- extensionEnabled Ext_smart <$> getOption readerExtensions if smart then quoted' singleQuoted (string "`") (try $ char '\'' >> notFollowedBy letter) <|> quoted' singleQuoted (string "‘") (try $ char '’' >> notFollowedBy letter) diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index e0694f38a..9137ae4b6 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -1590,7 +1590,7 @@ code = try $ do math :: PandocMonad m => MarkdownParser m (F Inlines) math = (return . B.displayMath <$> (mathDisplay >>= applyMacros')) <|> (return . B.math <$> (mathInline >>= applyMacros')) <+?> - ((getOption readerSmart >>= guard) *> (return <$> apostrophe) + (guardEnabled Ext_smart *> (return <$> apostrophe) <* notFollowedBy (space <|> satisfy isPunctuation)) -- Parses material enclosed in *s, **s, _s, or __s. @@ -1697,7 +1697,7 @@ str = do result <- many1 alphaNum updateLastStrPos let spacesToNbr = map (\c -> if c == ' ' then '\160' else c) - isSmart <- getOption readerSmart + isSmart <- extensionEnabled Ext_smart <$> getOption readerExtensions if isSmart then case likelyAbbrev result of [] -> return $ return $ B.str result @@ -2104,7 +2104,7 @@ citation = try $ do smart :: PandocMonad m => MarkdownParser m (F Inlines) smart = do - getOption readerSmart >>= guard + guardEnabled Ext_smart doubleQuoted <|> singleQuoted <|> choice (map (return <$>) [apostrophe, dash, ellipses]) diff --git a/src/Text/Pandoc/Readers/Org/Inlines.hs b/src/Text/Pandoc/Readers/Org/Inlines.hs index 5a02eb8eb..bcf8f6df9 100644 --- a/src/Text/Pandoc/Readers/Org/Inlines.hs +++ b/src/Text/Pandoc/Readers/Org/Inlines.hs @@ -841,7 +841,7 @@ exportSnippet = try $ do smart :: PandocMonad m => OrgParser m (F Inlines) smart = do - getOption readerSmart >>= guard + guardEnabled Ext_smart doubleQuoted <|> singleQuoted <|> choice (map (return <$>) [orgApostrophe, orgDash, orgEllipses]) where diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 57b6c6f6c..42a1a22e6 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -1309,7 +1309,7 @@ note = try $ do smart :: PandocMonad m => RSTParser m Inlines smart = do - getOption readerSmart >>= guard + guardEnabled Ext_smart doubleQuoted <|> singleQuoted <|> choice [apostrophe, dash, ellipses] diff --git a/src/Text/Pandoc/Readers/TWiki.hs b/src/Text/Pandoc/Readers/TWiki.hs index da908a58c..3e547e5f4 100644 --- a/src/Text/Pandoc/Readers/TWiki.hs +++ b/src/Text/Pandoc/Readers/TWiki.hs @@ -476,7 +476,7 @@ symbol = count 1 nonspaceChar >>= return . B.str smart :: TWParser B.Inlines smart = do - getOption readerSmart >>= guard + guardEnabled Ext_smart doubleQuoted <|> singleQuoted <|> choice [ apostrophe , dash diff --git a/tests/Tests/Old.hs b/tests/Tests/Old.hs index c52a368e2..f9a8a71d5 100644 --- a/tests/Tests/Old.hs +++ b/tests/Tests/Old.hs @@ -47,13 +47,13 @@ tests = [ testGroup "markdown" [ testGroup "writer" $ writerTests "markdown" ++ lhsWriterTests "markdown" , testGroup "reader" - [ test "basic" ["-r", "markdown", "-w", "native", "-s", "-S"] + [ test "basic" ["-r", "markdown+smart", "-w", "native", "-s"] "testsuite.txt" "testsuite.native" , test "tables" ["-r", "markdown", "-w", "native", "--columns=80"] "tables.txt" "tables.native" , test "pipe tables" ["-r", "markdown", "-w", "native", "--columns=80"] "pipe-tables.txt" "pipe-tables.native" - , test "more" ["-r", "markdown", "-w", "native", "-s", "-S"] + , test "more" ["-r", "markdown+smart", "-w", "native", "-s"] "markdown-reader-more.txt" "markdown-reader-more.native" , lhsReaderTest "markdown+lhs" ] @@ -65,8 +65,8 @@ tests = [ testGroup "markdown" , testGroup "rst" [ testGroup "writer" (writerTests "rst" ++ lhsWriterTests "rst") , testGroup "reader" - [ test "basic" ["-r", "rst", "-w", "native", - "-s", "-S", "--columns=80"] "rst-reader.rst" "rst-reader.native" + [ test "basic" ["-r", "rst+smart", "-w", "native", + "-s", "--columns=80"] "rst-reader.rst" "rst-reader.native" , test "tables" ["-r", "rst", "-w", "native", "--columns=80"] "tables.rst" "tables-rstsubset.native" , lhsReaderTest "rst+lhs" diff --git a/tests/Tests/Readers/Markdown.hs b/tests/Tests/Readers/Markdown.hs index 3bc8f34ae..ff68b4d3f 100644 --- a/tests/Tests/Readers/Markdown.hs +++ b/tests/Tests/Readers/Markdown.hs @@ -12,7 +12,8 @@ markdown :: String -> Pandoc markdown = purely $ readMarkdown def markdownSmart :: String -> Pandoc -markdownSmart = purely $ readMarkdown def { readerSmart = True } +markdownSmart = purely $ readMarkdown def { readerExtensions = + enableExtension Ext_smart $ readerExtensions def } markdownCDL :: String -> Pandoc markdownCDL = purely $ readMarkdown def { readerExtensions = enableExtension diff --git a/tests/Tests/Readers/Org.hs b/tests/Tests/Readers/Org.hs index 96a783045..ed29f1377 100644 --- a/tests/Tests/Readers/Org.hs +++ b/tests/Tests/Readers/Org.hs @@ -12,7 +12,8 @@ org :: String -> Pandoc org = purely $ readOrg def orgSmart :: String -> Pandoc -orgSmart = purely $ readOrg def { readerSmart = True } +orgSmart = purely $ readOrg def { readerExtensions = + enableExtension Ext_smart $ readerExtensions def } infix 4 =: (=:) :: ToString c -- cgit v1.2.3 From 5bf912577092fd1fd8874ccc89370396f22b5388 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 14 Jan 2017 21:00:22 +0100 Subject: Removed readerOldDashes and --old-dashes option, added old_dashes extension. API change. CLI option change. --- MANUAL.txt | 13 +++++++------ pandoc.hs | 9 --------- src/Text/Pandoc.hs | 6 ++++-- src/Text/Pandoc/Extensions.hs | 1 + src/Text/Pandoc/Options.hs | 4 ---- src/Text/Pandoc/Parsing.hs | 2 +- src/Text/Pandoc/Readers/Textile.hs | 4 +--- tests/textile-reader.native | 16 ++++++++-------- 8 files changed, 22 insertions(+), 33 deletions(-) (limited to 'src/Text') diff --git a/MANUAL.txt b/MANUAL.txt index 239367480..236deeed4 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -394,12 +394,6 @@ Reader options HTML codes and LaTeX environments. (The LaTeX reader does pass through untranslatable LaTeX *commands*, even if `-R` is not specified.) -`--old-dashes` - -: Selects the pandoc <= 1.8.2.1 behavior for parsing smart dashes: `-` before - a numeral is an en-dash, and `--` is an em-dash. This option is selected - automatically for `textile` input. - `--base-header-level=`*NUMBER* : Specify the base level for headers (defaults to 1). @@ -3401,6 +3395,13 @@ Note: if your LaTeX template or any included header file call for the [`csquotes`] package, pandoc will detect this automatically and use `\enquote{...}` for quoted text. +#### Extension: `old_dashes` #### + +Selects the pandoc <= 1.8.2.1 behavior for parsing smart dashes: +`-` before a numeral is an en-dash, and `--` is an em-dash. +This option only has an effect if `smart` is enabled. It is +selected automatically for `textile` input. + #### Extension: `angle_brackets_escapable` #### Allow `<` and `>` to be backslash-escaped, as they can be in diff --git a/pandoc.hs b/pandoc.hs index 371ad16e0..5d3b85f6e 100644 --- a/pandoc.hs +++ b/pandoc.hs @@ -112,7 +112,6 @@ convertWithOpts opts args = do , optSectionDivs = sectionDivs , optIncremental = incremental , optSelfContained = selfContained - , optOldDashes = oldDashes , optHtml5 = html5 , optHtmlQTags = htmlQTags , optHighlight = highlight @@ -302,7 +301,6 @@ convertWithOpts opts args = do , readerParseRaw = parseRaw , readerColumns = columns , readerTabStop = tabStop - , readerOldDashes = oldDashes , readerIndentedCodeClasses = codeBlockClasses , readerApplyMacros = not laTeXOutput , readerDefaultImageExtension = defaultImageExtension @@ -546,7 +544,6 @@ data Opt = Opt , optSectionDivs :: Bool -- ^ Put sections in div tags in HTML , optIncremental :: Bool -- ^ Use incremental lists in Slidy/Slideous/S5 , optSelfContained :: Bool -- ^ Make HTML accessible offline - , optOldDashes :: Bool -- ^ Parse dashes like pandoc <=1.8.2.1 , optHtml5 :: Bool -- ^ Produce HTML5 in HTML , optHtmlQTags :: Bool -- ^ Use tags in HTML , optHighlight :: Bool -- ^ Highlight source code @@ -611,7 +608,6 @@ defaultOpts = Opt , optSectionDivs = False , optIncremental = False , optSelfContained = False - , optOldDashes = False , optHtml5 = False , optHtmlQTags = False , optHighlight = True @@ -689,11 +685,6 @@ options = (\opt -> return opt { optParseRaw = True })) "" -- "Parse untranslatable HTML codes and LaTeX environments as raw" - , Option "" ["old-dashes"] - (NoArg - (\opt -> return opt { optOldDashes = True })) - "" -- "Use smart quotes, dashes, and ellipses" - , Option "" ["base-header-level"] (ReqArg (\arg opt -> diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index 3671b08ad..f9e032f4f 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -338,8 +338,6 @@ getDefaultExtensions "plain" = plainExtensions getDefaultExtensions "org" = extensionsFromList [Ext_citations, Ext_auto_identifiers] -getDefaultExtensions "textile" = extensionsFromList - [Ext_auto_identifiers] getDefaultExtensions "html" = extensionsFromList [Ext_auto_identifiers, Ext_native_divs, @@ -356,6 +354,10 @@ getDefaultExtensions "latex" = extensionsFromList getDefaultExtensions "context" = extensionsFromList [Ext_smart, Ext_auto_identifiers] +getDefaultExtensions "textile" = extensionsFromList + [Ext_old_dashes, + Ext_smart, + Ext_auto_identifiers] getDefaultExtensions _ = extensionsFromList [Ext_auto_identifiers] diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 584aa18e2..7278ece61 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -131,6 +131,7 @@ data Extension = | Ext_epub_html_exts -- ^ Recognise the EPUB extended version of HTML | Ext_shortcut_reference_links -- ^ Shortcut reference links | Ext_smart -- ^ "Smart" quotes, apostrophes, ellipses, dashes + | Ext_old_dashes -- ^ -- = em, - before number = en deriving (Show, Read, Enum, Eq, Ord, Bounded, Data, Typeable, Generic) pandocExtensions :: Extensions diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index f325e9905..61cb7b9ec 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -58,9 +58,6 @@ data ReaderOptions = ReaderOptions{ , readerParseRaw :: Bool -- ^ Parse raw HTML, LaTeX , readerColumns :: Int -- ^ Number of columns in terminal , readerTabStop :: Int -- ^ Tab stop - , readerOldDashes :: Bool -- ^ Use pandoc <= 1.8.2.1 behavior - -- in parsing dashes; -- is em-dash; - -- - before numerial is en-dash , readerApplyMacros :: Bool -- ^ Apply macros to TeX math , readerIndentedCodeClasses :: [String] -- ^ Default classes for -- indented code blocks @@ -77,7 +74,6 @@ instance Default ReaderOptions , readerParseRaw = False , readerColumns = 80 , readerTabStop = 4 - , readerOldDashes = False , readerApplyMacros = True , readerIndentedCodeClasses = [] , readerDefaultImageExtension = "" diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index b92894dd7..e8f4c776c 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -1183,7 +1183,7 @@ ellipses = try (string "..." >> return (B.str "\8230")) dash :: (HasReaderOptions st, Stream s m Char) => ParserT s st m Inlines dash = try $ do - oldDashes <- getOption readerOldDashes + oldDashes <- extensionEnabled Ext_old_dashes <$> getOption readerExtensions if oldDashes then do char '-' diff --git a/src/Text/Pandoc/Readers/Textile.hs b/src/Text/Pandoc/Readers/Textile.hs index 721b57f46..404913926 100644 --- a/src/Text/Pandoc/Readers/Textile.hs +++ b/src/Text/Pandoc/Readers/Textile.hs @@ -91,9 +91,7 @@ parseTextile = do -- asked for, for better conversion to other light markup formats oldOpts <- stateOptions `fmap` getState updateState $ \state -> state{ stateOptions = - oldOpts{ readerParseRaw = True - , readerOldDashes = True - } } + oldOpts{ readerParseRaw = True } } many blankline startPos <- getPosition -- go through once just to get list of reference keys and notes diff --git a/tests/textile-reader.native b/tests/textile-reader.native index c617a53f5..8b3100ffa 100644 --- a/tests/textile-reader.native +++ b/tests/textile-reader.native @@ -1,5 +1,5 @@ Pandoc (Meta {unMeta = fromList []}) -[Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "set",Space,Str "of",Space,Str "tests",Space,Str "for",Space,Str "pandoc",Space,Str "Textile",Space,Str "Reader.",Space,Str "Part",Space,Str "of",Space,Str "it",Space,Str "comes",LineBreak,Str "from",Space,Str "John",Space,Str "Gruber's",Space,Str "markdown",Space,Str "test",Space,Str "suite."] +[Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "set",Space,Str "of",Space,Str "tests",Space,Str "for",Space,Str "pandoc",Space,Str "Textile",Space,Str "Reader.",Space,Str "Part",Space,Str "of",Space,Str "it",Space,Str "comes",LineBreak,Str "from",Space,Str "John",Space,Str "Gruber\8217s",Space,Str "markdown",Space,Str "test",Space,Str "suite."] ,HorizontalRule ,Header 1 ("headers",[],[]) [Str "Headers"] ,Header 2 ("level-2-with-an-embeded-link",[],[]) [Str "Level",Space,Str "2",Space,Str "with",Space,Str "an",Space,Link ("",[],[]) [Str "embeded",Space,Str "link"] ("http://www.example.com","")] @@ -8,9 +8,9 @@ Pandoc (Meta {unMeta = fromList []}) ,Header 5 ("level-5",[],[]) [Str "Level",Space,Str "5"] ,Header 6 ("level-6",[],[]) [Str "Level",Space,Str "6"] ,Header 1 ("paragraphs",[],[]) [Str "Paragraphs"] -,Para [Str "Here's",Space,Str "a",Space,Str "regular",Space,Str "paragraph."] +,Para [Str "Here\8217s",Space,Str "a",Space,Str "regular",Space,Str "paragraph."] ,Para [Str "Line",Space,Str "breaks",Space,Str "are",Space,Str "preserved",Space,Str "in",Space,Str "textile,",Space,Str "so",Space,Str "you",Space,Str "can",Space,Str "not",Space,Str "wrap",Space,Str "your",Space,Str "very",LineBreak,Str "long",Space,Str "paragraph",Space,Str "with",Space,Str "your",Space,Str "favourite",Space,Str "text",Space,Str "editor",Space,Str "and",Space,Str "have",Space,Str "it",Space,Str "rendered",LineBreak,Str "with",Space,Str "no",Space,Str "break."] -,Para [Str "Here's",Space,Str "one",Space,Str "with",Space,Str "a",Space,Str "bullet."] +,Para [Str "Here\8217s",Space,Str "one",Space,Str "with",Space,Str "a",Space,Str "bullet."] ,BulletList [[Plain [Str "criminey."]]] ,Para [Str "There",Space,Str "should",Space,Str "be",Space,Str "a",Space,Str "paragraph",Space,Str "break",Space,Str "between",Space,Str "here"] @@ -89,14 +89,14 @@ Pandoc (Meta {unMeta = fromList []}) ,Para [Str "This",Space,Str "is",Space,Emph [Str "emphasized"],Str ",",Space,Str "and",Space,Str "so",Space,Emph [Str "is",Space,Str "this"],Str ".",LineBreak,Str "This",Space,Str "is",Space,Strong [Str "strong"],Str ",",Space,Str "and",Space,Str "so",Space,Strong [Str "is",Space,Str "this"],Str ".",LineBreak,Str "Hyphenated-words-are-ok,",Space,Str "as",Space,Str "well",Space,Str "as",Space,Str "strange_underscore_notation.",LineBreak,Str "A",Space,Link ("",[],[]) [Strong [Str "strong",Space,Str "link"]] ("http://www.foobar.com",""),Str "."] ,Para [Emph [Strong [Str "This",Space,Str "is",Space,Str "strong",Space,Str "and",Space,Str "em."]],LineBreak,Str "So",Space,Str "is",Space,Strong [Emph [Str "this"]],Space,Str "word",Space,Str "and",Space,Emph [Strong [Str "that",Space,Str "one"]],Str ".",LineBreak,Strikeout [Str "This",Space,Str "is",Space,Str "strikeout",Space,Str "and",Space,Strong [Str "strong"]]] ,Para [Str "Superscripts:",Space,Str "a",Superscript [Str "bc"],Str "d",Space,Str "a",Space,Superscript [Strong [Str "hello"]],Space,Str "a",Superscript [Str "hello",Space,Str "there"],Str ".",LineBreak,Str "Subscripts:",Space,Subscript [Str "here"],Space,Str "H",Space,Subscript [Str "2"],Str "O,",Space,Str "H",Space,Subscript [Str "23"],Str "O,",Space,Str "H",Space,Subscript [Str "many",Space,Str "of",Space,Str "them"],Str "O."] -,Para [Str "Dashes",Space,Str ":",Space,Str "How",Space,Str "cool",Space,Str "--",Space,Str "automatic",Space,Str "dashes."] -,Para [Str "Elipses",Space,Str ":",Space,Str "He",Space,Str "thought",Space,Str "and",Space,Str "thought",Space,Str "...",Space,Str "and",Space,Str "then",Space,Str "thought",Space,Str "some",Space,Str "more."] -,Para [Str "Quotes",Space,Str "and",Space,Str "apostrophes",Space,Str ":",Space,Str "\"I'd",Space,Str "like",Space,Str "to",Space,Str "thank",Space,Str "you\"",Space,Str "for",Space,Str "example."] +,Para [Str "Dashes",Space,Str ":",Space,Str "How",Space,Str "cool",Space,Str "\8212",Space,Str "automatic",Space,Str "dashes."] +,Para [Str "Elipses",Space,Str ":",Space,Str "He",Space,Str "thought",Space,Str "and",Space,Str "thought",Space,Str "\8230",Space,Str "and",Space,Str "then",Space,Str "thought",Space,Str "some",Space,Str "more."] +,Para [Str "Quotes",Space,Str "and",Space,Str "apostrophes",Space,Str ":",Space,Quoted DoubleQuote [Str "I\8217d",Space,Str "like",Space,Str "to",Space,Str "thank",Space,Str "you"],Space,Str "for",Space,Str "example."] ,Header 1 ("links",[],[]) [Str "Links"] ,Header 2 ("explicit",[],[]) [Str "Explicit"] ,Para [Str "Just",Space,Str "a",Space,Link ("",[],[]) [Str "url"] ("http://www.url.com","")] ,Para [Link ("",[],[]) [Str "Email",Space,Str "link"] ("mailto:nobody@nowhere.net","")] -,Para [Str "\"not",Space,Str "a",Space,Str "link\":",Space,Str "foo"] +,Para [Quoted DoubleQuote [Str "not",Space,Str "a",Space,Str "link"],Str ":",Space,Str "foo"] ,Para [Str "Automatic",Space,Str "linking",Space,Str "to",Space,Link ("",[],[]) [Str "http://www.example.com"] ("http://www.example.com",""),Str "."] ,Para [Link ("",[],[]) [Str "Example"] ("http://www.example.com/",""),Str ":",Space,Str "Example",Space,Str "of",Space,Str "a",Space,Str "link",Space,Str "followed",Space,Str "by",Space,Str "a",Space,Str "colon."] ,Para [Str "A",Space,Str "link",Link ("",[],[]) [Str "with",Space,Str "brackets"] ("http://www.example.com",""),Str "and",Space,Str "no",Space,Str "spaces."] @@ -117,7 +117,7 @@ Pandoc (Meta {unMeta = fromList []}) ,[[Plain [Str "bella"]] ,[Plain [Str "45"]] ,[Plain [Str "f"]]]] -,Para [Str "and",Space,Str "some",Space,Str "text",Space,Str "following",Space,Str "..."] +,Para [Str "and",Space,Str "some",Space,Str "text",Space,Str "following",Space,Str "\8230"] ,Header 2 ("with-headers",[],[]) [Str "With",Space,Str "headers"] ,Table [] [AlignDefault,AlignDefault,AlignDefault] [0.0,0.0,0.0] [[Plain [Str "name"]] -- cgit v1.2.3 From 412ed3f1321a49d3c3b2119ebd28705376bbd551 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 15 Jan 2017 11:56:26 +0100 Subject: Make the `smart` extension affect the Markdown writer. Thus, to "unsmartify" something that has been parsed as smart by pandoc, you can use `-t markdown+smart`, and straight quotes will be produced instead of curly quotes, etc. Example: % pandoc -f latex -t markdown+smart ``hi''---ok ^D "hi"---ok --- MANUAL.txt | 23 ++++++++++++----------- src/Text/Pandoc/Writers/Markdown.hs | 35 +++++++++++++++++++++++++++++------ 2 files changed, 41 insertions(+), 17 deletions(-) (limited to 'src/Text') diff --git a/MANUAL.txt b/MANUAL.txt index 236deeed4..ec3499513 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -3383,17 +3383,18 @@ example, `markdown+hard_line_breaks` is Markdown with hard line breaks. #### Extension: `smart` #### -Produce typographically correct output, converting straight -quotes to curly quotes, `---` to em-dashes, `--` to en-dashes, -and `...` to ellipses. Nonbreaking spaces are inserted after -certain abbreviations, such as "Mr." (Note: This option is -selected automatically when the output format is `latex` or -`context`, unless `--no-tex-ligatures` is used. It has no -effect for `latex` input.) - -Note: if your LaTeX template or any included header file call -for the [`csquotes`] package, pandoc will detect this -automatically and use `\enquote{...}` for quoted text. +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." + +Notes: + + * This extension option is selected automatically when the + output format is `latex` or `context`, unless + `--no-tex-ligatures` is used. It has no effect for `latex` input. + * If your LaTeX template or any included header file call + for the [`csquotes`] package, pandoc will detect this + automatically and use `\enquote{...}` for quoted text. #### Extension: `old_dashes` #### diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 6a5a1130e..9ef968fc6 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -280,7 +280,10 @@ escapeString opts = escapeStringUsing markdownEscapes (if isEnabled Ext_tex_math_dollars opts then ('$':) else id) $ - "\\`*_[]#" + "\\`*_[]#" ++ + if isEnabled Ext_smart opts + then "\"'" + else "" -- | Construct table of contents from list of header blocks. tableOfContents :: PandocMonad m => WriterOptions -> [Block] -> m Doc @@ -949,10 +952,14 @@ inlineToMarkdown opts (SmallCaps lst) = do else inlineListToMarkdown opts $ capitalize lst inlineToMarkdown opts (Quoted SingleQuote lst) = do contents <- inlineListToMarkdown opts lst - return $ "‘" <> contents <> "’" + return $ if isEnabled Ext_smart opts + then "'" <> contents <> "'" + else "‘" <> contents <> "’" inlineToMarkdown opts (Quoted DoubleQuote lst) = do contents <- inlineListToMarkdown opts lst - return $ "“" <> contents <> "”" + return $ if isEnabled Ext_smart opts + then "\"" <> contents <> "\"" + else "“" <> contents <> "”" inlineToMarkdown opts (Code attr str) = do let tickGroups = filter (\s -> '`' `elem` s) $ group str let longest = if null tickGroups @@ -969,9 +976,13 @@ inlineToMarkdown opts (Code attr str) = do else return $ text (marker ++ spacer ++ str ++ spacer ++ marker) <> attrs inlineToMarkdown opts (Str str) = do isPlain <- asks envPlain - if isPlain - then return $ text str - else return $ text $ escapeString opts str + let str' = (if isEnabled Ext_smart opts + then unsmartify opts + else id) $ + if isPlain + then str + else escapeString opts str + return $ text str' inlineToMarkdown opts (Math InlineMath str) = case writerHTMLMathMethod opts of WebTeX url -> @@ -1126,3 +1137,15 @@ makeMathPlainer = walk go go (Emph xs) = Span nullAttr xs go x = x +unsmartify :: WriterOptions -> String -> String +unsmartify opts ('\8217':xs) = '\'' : unsmartify opts xs +unsmartify opts ('\8230':xs) = "..." ++ unsmartify opts xs +unsmartify opts ('\8211':xs) + | isEnabled Ext_old_dashes opts = '-' : unsmartify opts xs + | otherwise = "--" ++ unsmartify opts xs +unsmartify opts ('\8212':xs) + | isEnabled Ext_old_dashes opts = "--" ++ unsmartify opts xs + | otherwise = "---" ++ unsmartify opts xs +unsmartify opts (x:xs) = x : unsmartify opts xs +unsmartify _ [] = [] + -- cgit v1.2.3 From a58369a7e650758004975084f984efb2bf3d7d68 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 15 Jan 2017 12:57:15 +0100 Subject: Options: changed default reader/writerExtensions to emptyExtensions. Previously they were pandocExtensions. This didn't make sense for many formats. --- src/Text/Pandoc/Options.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index 61cb7b9ec..5e4c51abf 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -69,7 +69,7 @@ data ReaderOptions = ReaderOptions{ instance Default ReaderOptions where def = ReaderOptions{ - readerExtensions = pandocExtensions + readerExtensions = emptyExtensions , readerStandalone = False , readerParseRaw = False , readerColumns = 80 @@ -204,7 +204,7 @@ instance Default WriterOptions where , writerNumberSections = False , writerNumberOffset = [0,0,0,0,0,0] , writerSectionDivs = False - , writerExtensions = pandocExtensions + , writerExtensions = emptyExtensions , writerReferenceLinks = False , writerDpi = 96 , writerWrapText = WrapAuto -- cgit v1.2.3 From 4f6e6247f9a672770a6d7a55a3aa2709a860ff38 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 15 Jan 2017 20:42:00 +0100 Subject: Made `smart` extension default for pandoc markdown. Updated tests. --- MANUAL.txt | 41 +++++++++++++++---------------- pandoc.hs | 8 ------ src/Text/Pandoc/Extensions.hs | 1 + tests/Tests/Old.hs | 4 +-- tests/Tests/Readers/Docx.hs | 12 +++++---- tests/Tests/Readers/LaTeX.hs | 3 ++- tests/Tests/Readers/Markdown.hs | 12 +++++---- tests/Tests/Readers/Odt.hs | 16 ++++++------ tests/Tests/Readers/Org.hs | 6 ++--- tests/Tests/Writers/Markdown.hs | 15 +++++++----- tests/fb2/basic.fb2 | 3 ++- tests/fb2/titles.fb2 | 3 ++- tests/markdown-citations.native | 16 ++++++------ tests/tables-rstsubset.native | 8 +++--- tests/tables.asciidoc | 8 +++--- tests/tables.docbook | 8 +++--- tests/tables.docbook5 | 8 +++--- tests/tables.dokuwiki | 8 +++--- tests/tables.fb2 | 3 ++- tests/tables.haddock | 8 +++--- tests/tables.html | 8 +++--- tests/tables.icml | 8 +++--- tests/tables.man | 8 +++--- tests/tables.mediawiki | 8 +++--- tests/tables.native | 8 +++--- tests/tables.opendocument | 8 +++--- tests/tables.plain | 8 +++--- tests/tables.rst | 8 +++--- tests/tables.rtf | 8 +++--- tests/tables.tei | 6 ++--- tests/tables.zimwiki | 8 +++--- tests/writer.markdown | 54 ++++++++++++++++++++--------------------- tests/writer.opml | 14 +++++------ 33 files changed, 174 insertions(+), 171 deletions(-) (limited to 'src/Text') diff --git a/MANUAL.txt b/MANUAL.txt index ec3499513..2b55b8239 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -706,12 +706,10 @@ Options affecting specific writers than parsing ligatures for quotation marks and dashes. In writing LaTeX or ConTeXt, print unicode quotation mark and dash characters literally, rather than converting them to - the standard ASCII TeX ligatures. Note: normally the `smart` - extension is selected automatically for LaTeX and ConTeXt output, - but it must be specified explicitly if `--no-tex-ligatures` is - selected. If you use literal curly quotes, dashes, and - ellipses in your source, then you may want to use - `--no-tex-ligatures` without `+smart`. + the standard ASCII TeX ligatures. Note: If you use literal + curly quotes, dashes, and ellipses in your source, then you + may want to use disable the `smart` extension in your + source format. `--listings` @@ -3180,6 +3178,22 @@ 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." + +Note: If you are *writing* Markdown, then the `smart` extension +has the reverse effect: what would have been curly quotes comes +out straight. + +If your LaTeX template or any included header file call +for the [`csquotes`] package, pandoc will detect this +automatically and use `\enquote{...}` for quoted text. Citations --------- @@ -3381,21 +3395,6 @@ in pandoc, but may be enabled by adding `+EXTENSION` to the format name, where `EXTENSION` is the name of the extension. Thus, for example, `markdown+hard_line_breaks` is Markdown with hard line breaks. -#### 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." - -Notes: - - * This extension option is selected automatically when the - output format is `latex` or `context`, unless - `--no-tex-ligatures` is used. It has no effect for `latex` input. - * If your LaTeX template or any included header file call - for the [`csquotes`] package, pandoc will detect this - automatically and use `\enquote{...}` for quoted text. - #### Extension: `old_dashes` #### Selects the pandoc <= 1.8.2.1 behavior for parsing smart dashes: diff --git a/pandoc.hs b/pandoc.hs index 5d3b85f6e..0baf555de 100644 --- a/pandoc.hs +++ b/pandoc.hs @@ -289,14 +289,6 @@ convertWithOpts opts args = do uriFragment = "" } _ -> Nothing - {- TODO - smart is now an extension, but we should prob make - - texligatures one too... - let smartExt = if laTeXInput - then texLigatures - else smart || (texLigatures && - (laTeXOutput || conTeXtOutput)) - -} - let readerOpts = def{ readerStandalone = standalone' , readerParseRaw = parseRaw , readerColumns = columns diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 7278ece61..14422ce39 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -177,6 +177,7 @@ pandocExtensions = extensionsFromList , Ext_implicit_header_references , Ext_line_blocks , Ext_shortcut_reference_links + , Ext_smart ] plainExtensions :: Extensions diff --git a/tests/Tests/Old.hs b/tests/Tests/Old.hs index f9a8a71d5..21e00b033 100644 --- a/tests/Tests/Old.hs +++ b/tests/Tests/Old.hs @@ -47,13 +47,13 @@ tests = [ testGroup "markdown" [ testGroup "writer" $ writerTests "markdown" ++ lhsWriterTests "markdown" , testGroup "reader" - [ test "basic" ["-r", "markdown+smart", "-w", "native", "-s"] + [ test "basic" ["-r", "markdown", "-w", "native", "-s"] "testsuite.txt" "testsuite.native" , test "tables" ["-r", "markdown", "-w", "native", "--columns=80"] "tables.txt" "tables.native" , test "pipe tables" ["-r", "markdown", "-w", "native", "--columns=80"] "pipe-tables.txt" "pipe-tables.native" - , test "more" ["-r", "markdown+smart", "-w", "native", "-s"] + , test "more" ["-r", "markdown", "-w", "native", "-s"] "markdown-reader-more.txt" "markdown-reader-more.native" , lhsReaderTest "markdown+lhs" ] diff --git a/tests/Tests/Readers/Docx.hs b/tests/Tests/Readers/Docx.hs index ef060b8ae..1fdb29f2e 100644 --- a/tests/Tests/Readers/Docx.hs +++ b/tests/Tests/Readers/Docx.hs @@ -1,7 +1,6 @@ module Tests.Readers.Docx (tests) where -import Text.Pandoc.Options -import Text.Pandoc.Readers.Native +import Text.Pandoc import Text.Pandoc.Definition import Tests.Helpers import Test.Framework @@ -26,6 +25,9 @@ data NoNormPandoc = NoNormPandoc {unNoNorm :: Pandoc} noNorm :: Pandoc -> NoNormPandoc noNorm = NoNormPandoc +defopts :: ReaderOptions +defopts = def{ readerExtensions = getDefaultExtensions "docx" } + instance ToString NoNormPandoc where toString d = purely (writeNative def{ writerTemplate = s }) $ toPandoc d where s = case d of @@ -57,7 +59,7 @@ testCompareWithOpts opts name docxFile nativeFile = buildTest $ testCompareWithOptsIO opts name docxFile nativeFile testCompare :: String -> FilePath -> FilePath -> Test -testCompare = testCompareWithOpts def +testCompare = testCompareWithOpts defopts testForWarningsWithOptsIO :: ReaderOptions -> String -> FilePath -> [String] -> IO Test testForWarningsWithOptsIO opts name docxFile expected = do @@ -70,7 +72,7 @@ testForWarningsWithOpts opts name docxFile expected = buildTest $ testForWarningsWithOptsIO opts name docxFile expected -- testForWarnings :: String -> FilePath -> [String] -> Test --- testForWarnings = testForWarningsWithOpts def +-- testForWarnings = testForWarningsWithOpts defopts getMedia :: FilePath -> FilePath -> IO (Maybe B.ByteString) getMedia archivePath mediaPath = do @@ -95,7 +97,7 @@ compareMediaPathIO mediaPath mediaBag docxPath = do compareMediaBagIO :: FilePath -> IO Bool compareMediaBagIO docxFile = do df <- B.readFile docxFile - mb <- runIOorExplode (readDocx def df >> P.getMediaBag) + mb <- runIOorExplode (readDocx defopts df >> P.getMediaBag) bools <- mapM (\(fp, _, _) -> compareMediaPathIO fp mb docxFile) (mediaDirectory mb) diff --git a/tests/Tests/Readers/LaTeX.hs b/tests/Tests/Readers/LaTeX.hs index 45e88d90e..d8572b15b 100644 --- a/tests/Tests/Readers/LaTeX.hs +++ b/tests/Tests/Readers/LaTeX.hs @@ -9,7 +9,8 @@ import Text.Pandoc.Builder import Text.Pandoc latex :: String -> Pandoc -latex = purely $ readLaTeX def +latex = purely $ readLaTeX def{ + readerExtensions = getDefaultExtensions "latex" } infix 4 =: (=:) :: ToString c diff --git a/tests/Tests/Readers/Markdown.hs b/tests/Tests/Readers/Markdown.hs index ff68b4d3f..65edf7c38 100644 --- a/tests/Tests/Readers/Markdown.hs +++ b/tests/Tests/Readers/Markdown.hs @@ -9,18 +9,20 @@ import Text.Pandoc.Builder import Text.Pandoc markdown :: String -> Pandoc -markdown = purely $ readMarkdown def +markdown = purely $ readMarkdown def { readerExtensions = + disableExtension Ext_smart pandocExtensions } markdownSmart :: String -> Pandoc markdownSmart = purely $ readMarkdown def { readerExtensions = - enableExtension Ext_smart $ readerExtensions def } + enableExtension Ext_smart pandocExtensions } markdownCDL :: String -> Pandoc markdownCDL = purely $ readMarkdown def { readerExtensions = enableExtension - Ext_compact_definition_lists $ readerExtensions def } + Ext_compact_definition_lists pandocExtensions } markdownGH :: String -> Pandoc -markdownGH = purely $ readMarkdown def { readerExtensions = githubMarkdownExtensions } +markdownGH = purely $ readMarkdown def { + readerExtensions = githubMarkdownExtensions } infix 4 =: (=:) :: ToString c @@ -304,7 +306,7 @@ tests = [ testGroup "inline code" ] , testGroup "lhs" [ test (purely $ readMarkdown def{ readerExtensions = enableExtension - Ext_literate_haskell $ readerExtensions def }) + Ext_literate_haskell pandocExtensions }) "inverse bird tracks and html" $ "> a\n\n< b\n\n
\n" =?> codeBlockWith ("",["sourceCode","literate","haskell"],[]) "a" diff --git a/tests/Tests/Readers/Odt.hs b/tests/Tests/Readers/Odt.hs index b0e916336..63283497b 100644 --- a/tests/Tests/Readers/Odt.hs +++ b/tests/Tests/Readers/Odt.hs @@ -1,18 +1,16 @@ module Tests.Readers.Odt (tests) where import Control.Monad ( liftM ) -import Text.Pandoc.Options -import Text.Pandoc.Readers.Native -import Text.Pandoc.Readers.Markdown -import Text.Pandoc.Definition +import Text.Pandoc import Text.Pandoc.Class (runIO) import Tests.Helpers import Test.Framework import qualified Data.ByteString.Lazy as B -import Text.Pandoc.Readers.Odt import Text.Pandoc.Writers.Native (writeNative) import qualified Data.Map as M -import Text.Pandoc.Error + +defopts :: ReaderOptions +defopts = def{ readerExtensions = getDefaultExtensions "odt" } tests :: [Test] tests = testsComparingToMarkdown ++ testsComparingToNative @@ -71,7 +69,9 @@ compareOdtToMarkdown :: TestCreator compareOdtToMarkdown opts odtPath markdownPath = do markdownFile <- Prelude.readFile markdownPath odtFile <- B.readFile odtPath - markdown <- getNoNormVia id "markdown" <$> runIO (readMarkdown opts markdownFile) + markdown <- getNoNormVia id "markdown" <$> + runIO (readMarkdown def{ readerExtensions = pandocExtensions } + markdownFile) odt <- getNoNormVia id "odt" <$> runIO (readOdt opts odtFile) return (odt,markdown) @@ -81,7 +81,7 @@ createTest :: TestCreator -> FilePath -> FilePath -> Test createTest creator name path1 path2 = - buildTest $ liftM (test id name) (creator def path1 path2) + buildTest $ liftM (test id name) (creator defopts path1 path2) {- -- diff --git a/tests/Tests/Readers/Org.hs b/tests/Tests/Readers/Org.hs index ed29f1377..ef0530b37 100644 --- a/tests/Tests/Readers/Org.hs +++ b/tests/Tests/Readers/Org.hs @@ -9,11 +9,11 @@ import Text.Pandoc import Data.List (intersperse) org :: String -> Pandoc -org = purely $ readOrg def - +org = purely $ readOrg def{ readerExtensions = getDefaultExtensions "org" } + orgSmart :: String -> Pandoc orgSmart = purely $ readOrg def { readerExtensions = - enableExtension Ext_smart $ readerExtensions def } + enableExtension Ext_smart $ getDefaultExtensions "org" } infix 4 =: (=:) :: ToString c diff --git a/tests/Tests/Writers/Markdown.hs b/tests/Tests/Writers/Markdown.hs index aa8a732f1..abefe27d5 100644 --- a/tests/Tests/Writers/Markdown.hs +++ b/tests/Tests/Writers/Markdown.hs @@ -8,8 +8,11 @@ import Text.Pandoc import Tests.Helpers import Text.Pandoc.Arbitrary() +defopts :: WriterOptions +defopts = def{ writerExtensions = pandocExtensions } + markdown :: (ToPandoc a) => a -> String -markdown = purely (writeMarkdown def) . toPandoc +markdown = purely (writeMarkdown defopts) . toPandoc markdownWithOpts :: (ToPandoc a) => WriterOptions -> a -> String markdownWithOpts opts x = purely (writeMarkdown opts) $ toPandoc x @@ -84,7 +87,7 @@ noteTestDoc = noteTests :: Test noteTests = testGroup "note and reference location" - [ test (markdownWithOpts def) + [ test (markdownWithOpts defopts) "footnotes at the end of a document" $ noteTestDoc =?> (unlines $ [ "First Header" @@ -105,7 +108,7 @@ noteTests = testGroup "note and reference location" , "" , "[^2]: The second note." ]) - , test (markdownWithOpts def{writerReferenceLocation=EndOfBlock}) + , test (markdownWithOpts defopts{writerReferenceLocation=EndOfBlock}) "footnotes at the end of blocks" $ noteTestDoc =?> (unlines $ [ "First Header" @@ -126,7 +129,7 @@ noteTests = testGroup "note and reference location" , "" , "Some more text." ]) - , test (markdownWithOpts def{writerReferenceLocation=EndOfBlock, writerReferenceLinks=True}) + , test (markdownWithOpts defopts{writerReferenceLocation=EndOfBlock, writerReferenceLinks=True}) "footnotes and reference links at the end of blocks" $ noteTestDoc =?> (unlines $ [ "First Header" @@ -149,7 +152,7 @@ noteTests = testGroup "note and reference location" , "" , "Some more text." ]) - , test (markdownWithOpts def{writerReferenceLocation=EndOfSection}) + , test (markdownWithOpts defopts{writerReferenceLocation=EndOfSection}) "footnotes at the end of section" $ noteTestDoc =?> (unlines $ [ "First Header" @@ -179,7 +182,7 @@ shortcutLinkRefsTests = (=:) :: (ToString a, ToPandoc a) => String -> (a, String) -> Test - (=:) = test (purely (writeMarkdown def{writerReferenceLinks = True}) . toPandoc) + (=:) = test (purely (writeMarkdown defopts{writerReferenceLinks = True}) . toPandoc) in testGroup "Shortcut reference links" [ "Simple link (shortcutable)" =: (para (link "/url" "title" "foo")) diff --git a/tests/fb2/basic.fb2 b/tests/fb2/basic.fb2 index 14b03fbea..ffb2bfbdf 100644 --- a/tests/fb2/basic.fb2 +++ b/tests/fb2/basic.fb2 @@ -1,2 +1,3 @@ -pandoc<p />

<p>Top-level title</p>
<p>Section</p>
<p>Subsection</p>

This emphasized strong verbatim markdown. See this link[1].

Ordered list:

 1. one

 2. two

 3. three

Blockquote is for citatons.

Code

block

is

for

code.

Strikeout is Pandoc's extension. Superscript and subscripts too: H2O is a liquid[2]. 210 is 1024.

Math is another Pandoc extension: E = m c^2.

<p>1</p>

http://example.com/

<p>2</p>

Sometimes.

\ No newline at end of file +pandoc<p />

<p>Top-level title</p>
<p>Section</p>
<p>Subsection</p>

This emphasized strong verbatim markdown. See this link[1].

Ordered list:

 1. one

 2. two

 3. three

Blockquote is for citatons.

Code

block

is

for

code.

Strikeout is Pandoc’s extension. Superscript and subscripts too: H2O is a liquid[2]. 210 is 1024.

Math is another Pandoc extension: E = m c^2.

<p>1</p>

http://example.com/

<p>2</p>

Sometimes.

+ diff --git a/tests/fb2/titles.fb2 b/tests/fb2/titles.fb2 index d8fc1e424..9e8d47e36 100644 --- a/tests/fb2/titles.fb2 +++ b/tests/fb2/titles.fb2 @@ -1,2 +1,3 @@ -pandoc<p />

<p>Simple title</p>

This example tests if Pandoc doesn't insert forbidden elements in FictionBook titles.

<p>Emphasized Strong Title</p>
<p>Title with</p><empty-line /><p>line break</p>
\ No newline at end of file +pandoc<p />

<p>Simple title</p>

This example tests if Pandoc doesn’t insert forbidden elements in FictionBook titles.

<p>Emphasized Strong Title</p>
<p>Title with</p><empty-line /><p>line break</p>
+ diff --git a/tests/markdown-citations.native b/tests/markdown-citations.native index d9738fb4f..c77ccbbfc 100644 --- a/tests/markdown-citations.native +++ b/tests/markdown-citations.native @@ -3,15 +3,15 @@ [[Para [Cite [Citation {citationId = "nonexistent", citationPrefix = [], citationSuffix = [], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@nonexistent]"]]] ,[Para [Cite [Citation {citationId = "nonexistent", citationPrefix = [], citationSuffix = [], citationMode = AuthorInText, citationNoteNum = 0, citationHash = 0}] [Str "@nonexistent"]]] ,[Para [Cite [Citation {citationId = "item1", citationPrefix = [], citationSuffix = [], citationMode = AuthorInText, citationNoteNum = 0, citationHash = 0}] [Str "@item1"],Space,Str "says",Space,Str "blah."]] - ,[Para [Cite [Citation {citationId = "item1", citationPrefix = [], citationSuffix = [Str "p.",Space,Str "30"], citationMode = AuthorInText, citationNoteNum = 0, citationHash = 0}] [Str "@item1",Space,Str "[p.",Space,Str "30]"],Space,Str "says",Space,Str "blah."]] - ,[Para [Cite [Citation {citationId = "item1", citationPrefix = [], citationSuffix = [Str "p.",Space,Str "30,",Space,Str "with",Space,Str "suffix"], citationMode = AuthorInText, citationNoteNum = 0, citationHash = 0}] [Str "@item1",Space,Str "[p.",Space,Str "30,",Space,Str "with",Space,Str "suffix]"],Space,Str "says",Space,Str "blah."]] - ,[Para [Cite [Citation {citationId = "item1", citationPrefix = [], citationSuffix = [], citationMode = AuthorInText, citationNoteNum = 0, citationHash = 0},Citation {citationId = "item2", citationPrefix = [], citationSuffix = [Space,Str "p.",Space,Str "30"], citationMode = SuppressAuthor, citationNoteNum = 0, citationHash = 0},Citation {citationId = "\1087\1091\1085\1082\1090\&3", citationPrefix = [Str "see",Space,Str "also"], citationSuffix = [], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "@item1",Space,Str "[-@item2",Space,Str "p.",Space,Str "30;",Space,Str "see",Space,Str "also",Space,Str "@\1087\1091\1085\1082\1090\&3]"],Space,Str "says",Space,Str "blah."]] - ,[Para [Str "In",Space,Str "a",Space,Str "note.",Note [Para [Cite [Citation {citationId = "\1087\1091\1085\1082\1090\&3", citationPrefix = [], citationSuffix = [Str "p.",Space,Str "12"], citationMode = AuthorInText, citationNoteNum = 0, citationHash = 0}] [Str "@\1087\1091\1085\1082\1090\&3",Space,Str "[p.",Space,Str "12]"],Space,Str "and",Space,Str "a",Space,Str "citation",Space,Str "without",Space,Str "locators",Space,Cite [Citation {citationId = "\1087\1091\1085\1082\1090\&3", citationPrefix = [], citationSuffix = [], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@\1087\1091\1085\1082\1090\&3]"],Str "."]]]] - ,[Para [Str "A",Space,Str "citation",Space,Str "group",Space,Cite [Citation {citationId = "item1", citationPrefix = [Str "see"], citationSuffix = [Space,Str "chap.",Space,Str "3"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0},Citation {citationId = "\1087\1091\1085\1082\1090\&3", citationPrefix = [Str "also"], citationSuffix = [Space,Str "p.",Space,Str "34-35"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[see",Space,Str "@item1",Space,Str "chap.",Space,Str "3;",Space,Str "also",Space,Str "@\1087\1091\1085\1082\1090\&3",Space,Str "p.",Space,Str "34-35]"],Str "."]] - ,[Para [Str "Another",Space,Str "one",Space,Cite [Citation {citationId = "item1", citationPrefix = [Str "see"], citationSuffix = [Space,Str "p.",Space,Str "34-35"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[see",Space,Str "@item1",Space,Str "p.",Space,Str "34-35]"],Str "."]] + ,[Para [Cite [Citation {citationId = "item1", citationPrefix = [], citationSuffix = [Str "p.\160\&30"], citationMode = AuthorInText, citationNoteNum = 0, citationHash = 0}] [Str "@item1",Space,Str "[p.",Space,Str "30]"],Space,Str "says",Space,Str "blah."]] + ,[Para [Cite [Citation {citationId = "item1", citationPrefix = [], citationSuffix = [Str "p.\160\&30,",Space,Str "with",Space,Str "suffix"], citationMode = AuthorInText, citationNoteNum = 0, citationHash = 0}] [Str "@item1",Space,Str "[p.",Space,Str "30,",Space,Str "with",Space,Str "suffix]"],Space,Str "says",Space,Str "blah."]] + ,[Para [Cite [Citation {citationId = "item1", citationPrefix = [], citationSuffix = [], citationMode = AuthorInText, citationNoteNum = 0, citationHash = 0},Citation {citationId = "item2", citationPrefix = [], citationSuffix = [Space,Str "p.\160\&30"], citationMode = SuppressAuthor, citationNoteNum = 0, citationHash = 0},Citation {citationId = "\1087\1091\1085\1082\1090\&3", citationPrefix = [Str "see",Space,Str "also"], citationSuffix = [], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "@item1",Space,Str "[-@item2",Space,Str "p.",Space,Str "30;",Space,Str "see",Space,Str "also",Space,Str "@\1087\1091\1085\1082\1090\&3]"],Space,Str "says",Space,Str "blah."]] + ,[Para [Str "In",Space,Str "a",Space,Str "note.",Note [Para [Cite [Citation {citationId = "\1087\1091\1085\1082\1090\&3", citationPrefix = [], citationSuffix = [Str "p.\160\&12"], citationMode = AuthorInText, citationNoteNum = 0, citationHash = 0}] [Str "@\1087\1091\1085\1082\1090\&3",Space,Str "[p.",Space,Str "12]"],Space,Str "and",Space,Str "a",Space,Str "citation",Space,Str "without",Space,Str "locators",Space,Cite [Citation {citationId = "\1087\1091\1085\1082\1090\&3", citationPrefix = [], citationSuffix = [], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@\1087\1091\1085\1082\1090\&3]"],Str "."]]]] + ,[Para [Str "A",Space,Str "citation",Space,Str "group",Space,Cite [Citation {citationId = "item1", citationPrefix = [Str "see"], citationSuffix = [Space,Str "chap.",Space,Str "3"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0},Citation {citationId = "\1087\1091\1085\1082\1090\&3", citationPrefix = [Str "also"], citationSuffix = [Space,Str "p.\160\&34-35"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[see",Space,Str "@item1",Space,Str "chap.",Space,Str "3;",Space,Str "also",Space,Str "@\1087\1091\1085\1082\1090\&3",Space,Str "p.",Space,Str "34-35]"],Str "."]] + ,[Para [Str "Another",Space,Str "one",Space,Cite [Citation {citationId = "item1", citationPrefix = [Str "see"], citationSuffix = [Space,Str "p.\160\&34-35"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[see",Space,Str "@item1",Space,Str "p.",Space,Str "34-35]"],Str "."]] ,[Para [Str "And",Space,Str "another",Space,Str "one",Space,Str "in",Space,Str "a",Space,Str "note.",Note [Para [Str "Some",Space,Str "citations",Space,Cite [Citation {citationId = "item1", citationPrefix = [Str "see"], citationSuffix = [Space,Str "chap.",Space,Str "3"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0},Citation {citationId = "\1087\1091\1085\1082\1090\&3", citationPrefix = [], citationSuffix = [], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0},Citation {citationId = "item2", citationPrefix = [], citationSuffix = [], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[see",Space,Str "@item1",Space,Str "chap.",Space,Str "3;",Space,Str "@\1087\1091\1085\1082\1090\&3;",Space,Str "@item2]"],Str "."]]]] - ,[Para [Str "Citation",Space,Str "with",Space,Str "a",Space,Str "suffix",Space,Str "and",Space,Str "locator",Space,Cite [Citation {citationId = "item1", citationPrefix = [], citationSuffix = [Space,Str "pp.",Space,Str "33,",Space,Str "35-37,",Space,Str "and",Space,Str "nowhere",Space,Str "else"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@item1",Space,Str "pp.",Space,Str "33,",Space,Str "35-37,",Space,Str "and",Space,Str "nowhere",Space,Str "else]"],Str "."]] + ,[Para [Str "Citation",Space,Str "with",Space,Str "a",Space,Str "suffix",Space,Str "and",Space,Str "locator",Space,Cite [Citation {citationId = "item1", citationPrefix = [], citationSuffix = [Space,Str "pp.\160\&33,",Space,Str "35-37,",Space,Str "and",Space,Str "nowhere",Space,Str "else"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@item1",Space,Str "pp.",Space,Str "33,",Space,Str "35-37,",Space,Str "and",Space,Str "nowhere",Space,Str "else]"],Str "."]] ,[Para [Str "Citation",Space,Str "with",Space,Str "suffix",Space,Str "only",Space,Cite [Citation {citationId = "item1", citationPrefix = [], citationSuffix = [Space,Str "and",Space,Str "nowhere",Space,Str "else"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[@item1",Space,Str "and",Space,Str "nowhere",Space,Str "else]"],Str "."]] - ,[Para [Str "Now",Space,Str "some",Space,Str "modifiers.",Note [Para [Str "Like",Space,Str "a",Space,Str "citation",Space,Str "without",Space,Str "author:",Space,Cite [Citation {citationId = "item1", citationPrefix = [], citationSuffix = [], citationMode = SuppressAuthor, citationNoteNum = 0, citationHash = 0}] [Str "[-@item1]"],Str ",",Space,Str "and",Space,Str "now",Space,Str "Doe",Space,Str "with",Space,Str "a",Space,Str "locator",Space,Cite [Citation {citationId = "item2", citationPrefix = [], citationSuffix = [Space,Str "p.",Space,Str "44"], citationMode = SuppressAuthor, citationNoteNum = 0, citationHash = 0}] [Str "[-@item2",Space,Str "p.",Space,Str "44]"],Str "."]]]] + ,[Para [Str "Now",Space,Str "some",Space,Str "modifiers.",Note [Para [Str "Like",Space,Str "a",Space,Str "citation",Space,Str "without",Space,Str "author:",Space,Cite [Citation {citationId = "item1", citationPrefix = [], citationSuffix = [], citationMode = SuppressAuthor, citationNoteNum = 0, citationHash = 0}] [Str "[-@item1]"],Str ",",Space,Str "and",Space,Str "now",Space,Str "Doe",Space,Str "with",Space,Str "a",Space,Str "locator",Space,Cite [Citation {citationId = "item2", citationPrefix = [], citationSuffix = [Space,Str "p.\160\&44"], citationMode = SuppressAuthor, citationNoteNum = 0, citationHash = 0}] [Str "[-@item2",Space,Str "p.",Space,Str "44]"],Str "."]]]] ,[Para [Str "With",Space,Str "some",Space,Str "markup",Space,Cite [Citation {citationId = "item1", citationPrefix = [Emph [Str "see"]], citationSuffix = [Space,Str "p.",Space,Strong [Str "32"]], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [Str "[*see*",Space,Str "@item1",Space,Str "p.",Space,Str "**32**]"],Str "."]]] ,Header 1 ("references",[],[]) [Str "References"]] diff --git a/tests/tables-rstsubset.native b/tests/tables-rstsubset.native index c98a95541..ecf6911dc 100644 --- a/tests/tables-rstsubset.native +++ b/tests/tables-rstsubset.native @@ -67,8 +67,8 @@ ,[[Plain [Str "Second"]] ,[Plain [Str "row"]] ,[Plain [Str "5.0"]] - ,[Plain [Str "Here's",Space,Str "another",Space,Str "one.",Space,Str "Note",SoftBreak,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",SoftBreak,Str "rows."]]]] -,Para [Str "Table:",Space,Str "Here's",Space,Str "the",Space,Str "caption.",Space,Str "It",Space,Str "may",Space,Str "span",Space,Str "multiple",Space,Str "lines."] + ,[Plain [Str "Here\8217s",Space,Str "another",Space,Str "one.",Space,Str "Note",SoftBreak,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",SoftBreak,Str "rows."]]]] +,Para [Str "Table:",Space,Str "Here\8217s",Space,Str "the",Space,Str "caption.",Space,Str "It",Space,Str "may",Space,Str "span",Space,Str "multiple",Space,Str "lines."] ,Para [Str "Multiline",Space,Str "table",Space,Str "without",Space,Str "caption:"] ,Table [] [AlignDefault,AlignDefault,AlignDefault,AlignDefault] [0.175,0.1625,0.1875,0.3625] [[Plain [Str "Centered",Space,Str "Header"]] @@ -82,7 +82,7 @@ ,[[Plain [Str "Second"]] ,[Plain [Str "row"]] ,[Plain [Str "5.0"]] - ,[Plain [Str "Here's",Space,Str "another",Space,Str "one.",Space,Str "Note",SoftBreak,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",SoftBreak,Str "rows."]]]] + ,[Plain [Str "Here\8217s",Space,Str "another",Space,Str "one.",Space,Str "Note",SoftBreak,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",SoftBreak,Str "rows."]]]] ,Para [Str "Table",Space,Str "without",Space,Str "column",Space,Str "headers:"] ,Table [] [AlignDefault,AlignDefault,AlignDefault,AlignDefault] [0.1,0.1,0.1,0.1] [[] @@ -114,4 +114,4 @@ ,[[Plain [Str "Second"]] ,[Plain [Str "row"]] ,[Plain [Str "5.0"]] - ,[Plain [Str "Here's",Space,Str "another",Space,Str "one.",Space,Str "Note",SoftBreak,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",SoftBreak,Str "rows."]]]]] + ,[Plain [Str "Here\8217s",Space,Str "another",Space,Str "one.",Space,Str "Note",SoftBreak,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",SoftBreak,Str "rows."]]]]] diff --git a/tests/tables.asciidoc b/tests/tables.asciidoc index 2a24544a3..91490a27a 100644 --- a/tests/tables.asciidoc +++ b/tests/tables.asciidoc @@ -32,12 +32,12 @@ Simple table indented two spaces: Multiline table with caption: -.Here's the caption. It may span multiple lines. +.Here’s the caption. It may span multiple lines. [width="78%",cols="^21%,<17%,>20%,<42%",options="header",] |======================================================================= |Centered Header |Left Aligned |Right Aligned |Default aligned |First |row |12.0 |Example of a row that spans multiple lines. -|Second |row |5.0 |Here's another one. Note the blank line between rows. +|Second |row |5.0 |Here’s another one. Note the blank line between rows. |======================================================================= Multiline table without caption: @@ -46,7 +46,7 @@ Multiline table without caption: |======================================================================= |Centered Header |Left Aligned |Right Aligned |Default aligned |First |row |12.0 |Example of a row that spans multiple lines. -|Second |row |5.0 |Here's another one. Note the blank line between rows. +|Second |row |5.0 |Here’s another one. Note the blank line between rows. |======================================================================= Table without column headers: @@ -63,5 +63,5 @@ Multiline table without column headers: [width="78%",cols="^21%,<17%,>20%,42%",] |======================================================================= |First |row |12.0 |Example of a row that spans multiple lines. -|Second |row |5.0 |Here's another one. Note the blank line between rows. +|Second |row |5.0 |Here’s another one. Note the blank line between rows. |======================================================================= diff --git a/tests/tables.docbook b/tests/tables.docbook index 6224cf222..f86b1c390 100644 --- a/tests/tables.docbook +++ b/tests/tables.docbook @@ -222,7 +222,7 @@ - Here's the caption. It may span multiple lines. + Here’s the caption. It may span multiple lines. @@ -271,7 +271,7 @@ 5.0 - Here's another one. Note the blank line between rows. + Here’s another one. Note the blank line between rows. @@ -328,7 +328,7 @@ 5.0 - Here's another one. Note the blank line between rows. + Here’s another one. Note the blank line between rows. @@ -424,7 +424,7 @@ 5.0 - Here's another one. Note the blank line between rows. + Here’s another one. Note the blank line between rows. diff --git a/tests/tables.docbook5 b/tests/tables.docbook5 index 6224cf222..f86b1c390 100644 --- a/tests/tables.docbook5 +++ b/tests/tables.docbook5 @@ -222,7 +222,7 @@
- Here's the caption. It may span multiple lines. + Here’s the caption. It may span multiple lines. @@ -271,7 +271,7 @@ 5.0 - Here's another one. Note the blank line between rows. + Here’s another one. Note the blank line between rows. @@ -328,7 +328,7 @@ 5.0 - Here's another one. Note the blank line between rows. + Here’s another one. Note the blank line between rows. @@ -424,7 +424,7 @@ 5.0 - Here's another one. Note the blank line between rows. + Here’s another one. Note the blank line between rows. diff --git a/tests/tables.dokuwiki b/tests/tables.dokuwiki index 21e61f656..23c0d22cb 100644 --- a/tests/tables.dokuwiki +++ b/tests/tables.dokuwiki @@ -23,16 +23,16 @@ Demonstration of simple table syntax. Multiline table with caption: -Here's the caption. It may span multiple lines. +Here’s the caption. It may span multiple lines. ^ Centered Header ^Left Aligned ^ Right Aligned^Default aligned ^ | First |row | 12.0|Example of a row that spans multiple lines. | -| Second |row | 5.0|Here's another one. Note the blank line between rows. | +| Second |row | 5.0|Here’s another one. Note the blank line between rows. | Multiline table without caption: ^ Centered Header ^Left Aligned ^ Right Aligned^Default aligned ^ | First |row | 12.0|Example of a row that spans multiple lines. | -| Second |row | 5.0|Here's another one. Note the blank line between rows. | +| Second |row | 5.0|Here’s another one. Note the blank line between rows. | Table without column headers: @@ -43,5 +43,5 @@ Table without column headers: Multiline table without column headers: | First |row | 12.0|Example of a row that spans multiple lines. | -| Second |row | 5.0|Here's another one. Note the blank line between rows.| +| Second |row | 5.0|Here’s another one. Note the blank line between rows.| diff --git a/tests/tables.fb2 b/tests/tables.fb2 index f636e9fd4..df285888e 100644 --- a/tests/tables.fb2 +++ b/tests/tables.fb2 @@ -1,2 +1,3 @@ -pandoc<p />

Simple table with caption:

RightLeftCenterDefault
12121212
123123123123
1111

Demonstration of simple table syntax.

Simple table without caption:

RightLeftCenterDefault
12121212
123123123123
1111

Simple table indented two spaces:

RightLeftCenterDefault
12121212
123123123123
1111

Demonstration of simple table syntax.

Multiline table with caption:

Centered HeaderLeft AlignedRight AlignedDefault aligned
Firstrow12.0Example of a row that spans multiple lines.
Secondrow5.0Here's another one. Note the blank line between rows.

Here's the caption. It may span multiple lines.

Multiline table without caption:

Centered HeaderLeft AlignedRight AlignedDefault aligned
Firstrow12.0Example of a row that spans multiple lines.
Secondrow5.0Here's another one. Note the blank line between rows.

Table without column headers:

12121212
123123123123
1111

Multiline table without column headers:

Firstrow12.0Example of a row that spans multiple lines.
Secondrow5.0Here's another one. Note the blank line between rows.

\ No newline at end of file +pandoc<p />

Simple table with caption:

RightLeftCenterDefault
12121212
123123123123
1111

Demonstration of simple table syntax.

Simple table without caption:

RightLeftCenterDefault
12121212
123123123123
1111

Simple table indented two spaces:

RightLeftCenterDefault
12121212
123123123123
1111

Demonstration of simple table syntax.

Multiline table with caption:

Centered HeaderLeft AlignedRight AlignedDefault aligned
Firstrow12.0Example of a row that spans multiple lines.
Secondrow5.0Here’s another one. Note the blank line between rows.

Here’s the caption. It may span multiple lines.

Multiline table without caption:

Centered HeaderLeft AlignedRight AlignedDefault aligned
Firstrow12.0Example of a row that spans multiple lines.
Secondrow5.0Here’s another one. Note the blank line between rows.

Table without column headers:

12121212
123123123123
1111

Multiline table without column headers:

Firstrow12.0Example of a row that spans multiple lines.
Secondrow5.0Here’s another one. Note the blank line between rows.

+ diff --git a/tests/tables.haddock b/tests/tables.haddock index f9efdc0de..84a15cce8 100644 --- a/tests/tables.haddock +++ b/tests/tables.haddock @@ -35,12 +35,12 @@ Multiline table with caption: > First row 12.0 Example of a row that > spans multiple lines. > -> Second row 5.0 Here\'s another one. Note +> Second row 5.0 Here’s another one. Note > the blank line between > rows. > -------------------------------------------------------------- > -> Here\'s the caption. It may span multiple lines. +> Here’s the caption. It may span multiple lines. Multiline table without caption: @@ -51,7 +51,7 @@ Multiline table without caption: > First row 12.0 Example of a row that > spans multiple lines. > -> Second row 5.0 Here\'s another one. Note +> Second row 5.0 Here’s another one. Note > the blank line between > rows. > -------------------------------------------------------------- @@ -70,7 +70,7 @@ Multiline table without column headers: > First row 12.0 Example of a row that > spans multiple lines. > -> Second row 5.0 Here\'s another one. Note +> Second row 5.0 Here’s another one. Note > the blank line between > rows. > ----------- ---------- ------------ -------------------------- diff --git a/tests/tables.html b/tests/tables.html index 0a9ea413c..5bb7a7de2 100644 --- a/tests/tables.html +++ b/tests/tables.html @@ -95,7 +95,7 @@

Multiline table with caption:

- +@@ -121,7 +121,7 @@ - +
Here's the caption. It may span multiple lines.Here’s the caption. It may span multiple lines.
Second row 5.0Here's another one. Note the blank line between rows.Here’s another one. Note the blank line between rows.
@@ -152,7 +152,7 @@ Second row 5.0 -Here's another one. Note the blank line between rows. +Here’s another one. Note the blank line between rows. @@ -198,7 +198,7 @@ Second row 5.0 -Here's another one. Note the blank line between rows. +Here’s another one. Note the blank line between rows. diff --git a/tests/tables.icml b/tests/tables.icml index 678f4b7a9..0280cafed 100644 --- a/tests/tables.icml +++ b/tests/tables.icml @@ -476,14 +476,14 @@ - Here's another one. Note the blank line between rows. + Here’s another one. Note the blank line between rows. - Here's the caption. It may span multiple lines. + Here’s the caption. It may span multiple lines.
@@ -578,7 +578,7 @@ - Here's another one. Note the blank line between rows. + Here’s another one. Note the blank line between rows. @@ -748,7 +748,7 @@ - Here's another one. Note the blank line between rows. + Here’s another one. Note the blank line between rows. diff --git a/tests/tables.man b/tests/tables.man index 788b2199d..dd6a3cce9 100644 --- a/tests/tables.man +++ b/tests/tables.man @@ -135,7 +135,7 @@ T} .PP Multiline table with caption: .PP -Here\[aq]s the caption. It may span multiple lines. +Here's the caption. It may span multiple lines. .TS tab(@); cw(10.5n) lw(9.6n) rw(11.4n) lw(23.6n). @@ -165,7 +165,7 @@ row T}@T{ 5.0 T}@T{ -Here\[aq]s another one. +Here's another one. Note the blank line between rows. T} .TE @@ -201,7 +201,7 @@ row T}@T{ 5.0 T}@T{ -Here\[aq]s another one. +Here's another one. Note the blank line between rows. T} .TE @@ -261,7 +261,7 @@ row T}@T{ 5.0 T}@T{ -Here\[aq]s another one. +Here's another one. Note the blank line between rows. T} .TE diff --git a/tests/tables.mediawiki b/tests/tables.mediawiki index 614c3eea1..ce7c17887 100644 --- a/tests/tables.mediawiki +++ b/tests/tables.mediawiki @@ -75,7 +75,7 @@ Simple table indented two spaces: Multiline table with caption: {| -|+ Here's the caption. It may span multiple lines. +|+ Here’s the caption. It may span multiple lines. !align="center" width="15%"| Centered Header !width="13%"| Left Aligned !align="right" width="16%"| Right Aligned @@ -89,7 +89,7 @@ Multiline table with caption: |align="center"| Second | row |align="right"| 5.0 -| Here's another one. Note the blank line between rows. +| Here’s another one. Note the blank line between rows. |} Multiline table without caption: @@ -108,7 +108,7 @@ Multiline table without caption: |align="center"| Second | row |align="right"| 5.0 -| Here's another one. Note the blank line between rows. +| Here’s another one. Note the blank line between rows. |} Table without column headers: @@ -141,6 +141,6 @@ Multiline table without column headers: |align="center"| Second | row |align="right"| 5.0 -| Here's another one. Note the blank line between rows. +| Here’s another one. Note the blank line between rows. |} diff --git a/tests/tables.native b/tests/tables.native index a7f4fdcf1..a60f9b586 100644 --- a/tests/tables.native +++ b/tests/tables.native @@ -53,7 +53,7 @@ ,[Plain [Str "1"]] ,[Plain [Str "1"]]]] ,Para [Str "Multiline",Space,Str "table",Space,Str "with",Space,Str "caption:"] -,Table [Str "Here's",Space,Str "the",Space,Str "caption.",SoftBreak,Str "It",Space,Str "may",Space,Str "span",Space,Str "multiple",Space,Str "lines."] [AlignCenter,AlignLeft,AlignRight,AlignLeft] [0.15,0.1375,0.1625,0.3375] +,Table [Str "Here\8217s",Space,Str "the",Space,Str "caption.",SoftBreak,Str "It",Space,Str "may",Space,Str "span",Space,Str "multiple",Space,Str "lines."] [AlignCenter,AlignLeft,AlignRight,AlignLeft] [0.15,0.1375,0.1625,0.3375] [[Plain [Str "Centered",SoftBreak,Str "Header"]] ,[Plain [Str "Left",SoftBreak,Str "Aligned"]] ,[Plain [Str "Right",SoftBreak,Str "Aligned"]] @@ -65,7 +65,7 @@ ,[[Plain [Str "Second"]] ,[Plain [Str "row"]] ,[Plain [Str "5.0"]] - ,[Plain [Str "Here's",Space,Str "another",Space,Str "one.",Space,Str "Note",SoftBreak,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",Space,Str "rows."]]]] + ,[Plain [Str "Here\8217s",Space,Str "another",Space,Str "one.",Space,Str "Note",SoftBreak,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",Space,Str "rows."]]]] ,Para [Str "Multiline",Space,Str "table",Space,Str "without",Space,Str "caption:"] ,Table [] [AlignCenter,AlignLeft,AlignRight,AlignLeft] [0.15,0.1375,0.1625,0.3375] [[Plain [Str "Centered",SoftBreak,Str "Header"]] @@ -79,7 +79,7 @@ ,[[Plain [Str "Second"]] ,[Plain [Str "row"]] ,[Plain [Str "5.0"]] - ,[Plain [Str "Here's",Space,Str "another",Space,Str "one.",Space,Str "Note",SoftBreak,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",Space,Str "rows."]]]] + ,[Plain [Str "Here\8217s",Space,Str "another",Space,Str "one.",Space,Str "Note",SoftBreak,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",Space,Str "rows."]]]] ,Para [Str "Table",Space,Str "without",Space,Str "column",Space,Str "headers:"] ,Table [] [AlignRight,AlignLeft,AlignCenter,AlignRight] [0.0,0.0,0.0,0.0] [[] @@ -111,4 +111,4 @@ ,[[Plain [Str "Second"]] ,[Plain [Str "row"]] ,[Plain [Str "5.0"]] - ,[Plain [Str "Here's",Space,Str "another",Space,Str "one.",Space,Str "Note",SoftBreak,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",Space,Str "rows."]]]]] + ,[Plain [Str "Here\8217s",Space,Str "another",Space,Str "one.",Space,Str "Note",SoftBreak,Str "the",Space,Str "blank",Space,Str "line",Space,Str "between",Space,Str "rows."]]]]] diff --git a/tests/tables.opendocument b/tests/tables.opendocument index 0765bb783..c331ecc43 100644 --- a/tests/tables.opendocument +++ b/tests/tables.opendocument @@ -246,12 +246,12 @@ caption: 5.0 - Here's another one. Note the + Here’s another one. Note the blank line between rows. -Here's the caption. It may span multiple +Here’s the caption. It may span multiple lines. Multiline table without caption: @@ -302,7 +302,7 @@ caption: 5.0 - Here's another one. Note the + Here’s another one. Note the blank line between rows. @@ -390,7 +390,7 @@ headers: 5.0 - Here's another one. Note the + Here’s another one. Note the blank line between rows. diff --git a/tests/tables.plain b/tests/tables.plain index 4b5754cf9..4c7ebbf82 100644 --- a/tests/tables.plain +++ b/tests/tables.plain @@ -35,12 +35,12 @@ Multiline table with caption: First row 12.0 Example of a row that spans multiple lines. - Second row 5.0 Here's another one. Note + Second row 5.0 Here’s another one. Note the blank line between rows. -------------------------------------------------------------- - : Here's the caption. It may span multiple lines. + : Here’s the caption. It may span multiple lines. Multiline table without caption: @@ -51,7 +51,7 @@ Multiline table without caption: First row 12.0 Example of a row that spans multiple lines. - Second row 5.0 Here's another one. Note + Second row 5.0 Here’s another one. Note the blank line between rows. -------------------------------------------------------------- @@ -70,7 +70,7 @@ Multiline table without column headers: First row 12.0 Example of a row that spans multiple lines. - Second row 5.0 Here's another one. Note + Second row 5.0 Here’s another one. Note the blank line between rows. ----------- ---------- ------------ -------------------------- diff --git a/tests/tables.rst b/tests/tables.rst index 25d5932ea..fc7f0b475 100644 --- a/tests/tables.rst +++ b/tests/tables.rst @@ -47,12 +47,12 @@ Multiline table with caption: | First | row | 12.0 | Example of a row that | | | | | spans multiple lines. | +-------------+------------+--------------+----------------------------+ -| Second | row | 5.0 | Here's another one. Note | +| Second | row | 5.0 | Here’s another one. Note | | | | | the blank line between | | | | | rows. | +-------------+------------+--------------+----------------------------+ -Table: Here's the caption. It may span multiple lines. +Table: Here’s the caption. It may span multiple lines. Multiline table without caption: @@ -63,7 +63,7 @@ Multiline table without caption: | First | row | 12.0 | Example of a row that | | | | | spans multiple lines. | +-------------+------------+--------------+----------------------------+ -| Second | row | 5.0 | Here's another one. Note | +| Second | row | 5.0 | Here’s another one. Note | | | | | the blank line between | | | | | rows. | +-------------+------------+--------------+----------------------------+ @@ -84,7 +84,7 @@ Multiline table without column headers: | First | row | 12.0 | Example of a row that | | | | | spans multiple lines. | +-------------+------------+--------------+----------------------------+ -| Second | row | 5.0 | Here's another one. Note | +| Second | row | 5.0 | Here’s another one. Note | | | | | the blank line between | | | | | rows. | +-------------+------------+--------------+----------------------------+ diff --git a/tests/tables.rtf b/tests/tables.rtf index 60b088082..57030b114 100644 --- a/tests/tables.rtf +++ b/tests/tables.rtf @@ -226,11 +226,11 @@ \cell} {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 5.0\par} \cell} -{{\pard\intbl \ql \f0 \sa0 \li0 \fi0 Here's another one. Note the blank line between rows.\par} +{{\pard\intbl \ql \f0 \sa0 \li0 \fi0 Here\u8217's another one. Note the blank line between rows.\par} \cell} } \intbl\row} -{\pard \ql \f0 \sa180 \li0 \fi0 Here's the caption. It may span multiple lines.\par} +{\pard \ql \f0 \sa180 \li0 \fi0 Here\u8217's the caption. It may span multiple lines.\par} {\pard \ql \f0 \sa180 \li0 \fi0 Multiline table without caption:\par} { \trowd \trgaph120 @@ -273,7 +273,7 @@ \cell} {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 5.0\par} \cell} -{{\pard\intbl \ql \f0 \sa0 \li0 \fi0 Here's another one. Note the blank line between rows.\par} +{{\pard\intbl \ql \f0 \sa0 \li0 \fi0 Here\u8217's another one. Note the blank line between rows.\par} \cell} } \intbl\row} @@ -352,7 +352,7 @@ \cell} {{\pard\intbl \qr \f0 \sa0 \li0 \fi0 5.0\par} \cell} -{{\pard\intbl \ql \f0 \sa0 \li0 \fi0 Here's another one. Note the blank line between rows.\par} +{{\pard\intbl \ql \f0 \sa0 \li0 \fi0 Here\u8217's another one. Note the blank line between rows.\par} \cell} } \intbl\row} diff --git a/tests/tables.tei b/tests/tables.tei index 45b88b1cb..64438e520 100644 --- a/tests/tables.tei +++ b/tests/tables.tei @@ -97,7 +97,7 @@

Second

row

5.0

-

Here's another one. Note the blank line between rows.

+

Here’s another one. Note the blank line between rows.

Multiline table without caption:

@@ -118,7 +118,7 @@

Second

row

5.0

-

Here's another one. Note the blank line between rows.

+

Here’s another one. Note the blank line between rows.

Table without column headers:

@@ -166,6 +166,6 @@

Second

row

5.0

-

Here's another one. Note the blank line between rows.

+

Here’s another one. Note the blank line between rows.

diff --git a/tests/tables.zimwiki b/tests/tables.zimwiki index 1f02c9908..6da1f7f2c 100644 --- a/tests/tables.zimwiki +++ b/tests/tables.zimwiki @@ -26,18 +26,18 @@ Demonstration of simple table syntax. Multiline table with caption: -Here's the caption. It may span multiple lines. +Here’s the caption. It may span multiple lines. | Centered Header |Left Aligned | Right Aligned|Default aligned | |:-----------------:|:-------------|--------------:|:------------------------------------------------------| | First |row | 12.0|Example of a row that spans multiple lines. | -| Second |row | 5.0|Here's another one. Note the blank line between rows. | +| Second |row | 5.0|Here’s another one. Note the blank line between rows. | Multiline table without caption: | Centered Header |Left Aligned | Right Aligned|Default aligned | |:-----------------:|:-------------|--------------:|:------------------------------------------------------| | First |row | 12.0|Example of a row that spans multiple lines. | -| Second |row | 5.0|Here's another one. Note the blank line between rows. | +| Second |row | 5.0|Here’s another one. Note the blank line between rows. | Table without column headers: @@ -52,5 +52,5 @@ Multiline table without column headers: | First |row | 12.0|Example of a row that spans multiple lines. | |:--------:|:----|-----:|-----------------------------------------------------| | First |row | 12.0|Example of a row that spans multiple lines. | -| Second |row | 5.0|Here's another one. Note the blank line between rows.| +| Second |row | 5.0|Here’s another one. Note the blank line between rows.| diff --git a/tests/writer.markdown b/tests/writer.markdown index 4f91a803b..3fe0f4b3e 100644 --- a/tests/writer.markdown +++ b/tests/writer.markdown @@ -6,7 +6,7 @@ date: 'July 17, 2006' title: Pandoc Test Suite --- -This is a set of tests for pandoc. Most of them are adapted from John Gruber’s +This is a set of tests for pandoc. Most of them are adapted from John Gruber's markdown test suite. ------------------------------------------------------------------------------ @@ -43,13 +43,13 @@ with no blank line Paragraphs ========== -Here’s a regular paragraph. +Here's a regular paragraph. 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. -Here’s one with a bullet. \* criminey. +Here's one with a bullet. \* criminey. There should be a hard line break\ here. @@ -190,7 +190,7 @@ Multiple paragraphs: 1. Item 1, graf one. - Item 1. graf two. The quick brown fox jumped over the lazy dog’s back. + Item 1. graf two. The quick brown fox jumped over the lazy dog's back. 2. Item 2. @@ -203,7 +203,7 @@ Nested - Tab - Tab -Here’s another: +Here's another: 1. First 2. Second: @@ -409,7 +409,7 @@ And this is **strong** -Here’s a simple block: +Here's a simple block:
@@ -466,7 +466,7 @@ Code:
-Hr’s: +Hr's:

@@ -513,22 +513,22 @@ spaces: a\^b c\^d, a\~b c\~d. Smart quotes, ellipses, dashes ============================== -“Hello,” said the spider. “‘Shelob’ is my name.” +"Hello," said the spider. "'Shelob' is my name." -‘A’, ‘B’, and ‘C’ are letters. +'A', 'B', and 'C' are letters. -‘Oak,’ ‘elm,’ and ‘beech’ are names of trees. So is ‘pine.’ +'Oak,' 'elm,' and 'beech' are names of trees. So is 'pine.' -‘He said, “I want to go.”’ Were you alive in the 70’s? +'He said, "I want to go."' Were you alive in the 70's? -Here is some quoted ‘`code`’ and a “[quoted -link](http://example.com/?foo=1&bar=2)”. +Here is some quoted '`code`' and a "[quoted +link](http://example.com/?foo=1&bar=2)". -Some dashes: one—two — three—four — five. +Some dashes: one---two --- three---four --- five. -Dashes between numbers: 5–7, 255–66, 1987–1999. +Dashes between numbers: 5--7, 255--66, 1987--1999. -Ellipses…and…and…. +Ellipses...and...and.... ------------------------------------------------------------------------------ @@ -541,19 +541,19 @@ LaTeX - $\alpha \wedge \omega$ - $223$ - $p$-Tree -- Here’s some display math: +- Here's some display math: $$\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$$ -- Here’s one that has a line break in it: $\alpha + \omega \times x^2$. +- Here's one that has a line break in it: $\alpha + \omega \times x^2$. -These shouldn’t be math: +These shouldn't be math: - To get the famous equation, write `$e = mc^2$`. -- \$22,000 is a *lot* of money. So is \$34,000. (It worked if “lot” is +- \$22,000 is a *lot* of money. So is \$34,000. (It worked if "lot" is emphasized.) - Shoes (\$20) and socks (\$5). - Escaped `$`: \$73 *this should be emphasized* 23\$. -Here’s a LaTeX table: +Here's a LaTeX table: \begin{tabular}{|l|l|}\hline Animal & Number \\ \hline @@ -672,14 +672,14 @@ Foo [biz](/url/ "Title with "quote" inside"). With ampersands --------------- -Here’s a [link with an ampersand in the URL](http://example.com/?foo=1&bar=2). +Here's a [link with an ampersand in the URL](http://example.com/?foo=1&bar=2). -Here’s a link with an amersand in the link text: +Here's a link with an amersand in the link text: [AT&T](http://att.com/ "AT&T"). -Here’s an [inline link](/script?foo=1&bar=2). +Here's an [inline link](/script?foo=1&bar=2). -Here’s an [inline link in pointy braces](/script?foo=1&bar=2). +Here's an [inline link in pointy braces](/script?foo=1&bar=2). Autolinks --------- @@ -703,7 +703,7 @@ Auto-links should not occur here: `` Images ====== -From “Voyage dans la Lune” by Georges Melies (1902): +From "Voyage dans la Lune" by Georges Melies (1902): ![lalune](lalune.jpg "Voyage dans la Lune") @@ -727,7 +727,7 @@ This paragraph should not be part of the note, as it is not indented. [^1]: Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document. -[^2]: Here’s the long note. This one contains multiple blocks. +[^2]: Here's the long note. This one contains multiple blocks. Subsequent blocks are indented to show that they belong to the footnote (as with list items). diff --git a/tests/writer.opml b/tests/writer.opml index c94a88f77..261f83426 100644 --- a/tests/writer.opml +++ b/tests/writer.opml @@ -24,7 +24,7 @@ - + @@ -39,18 +39,18 @@ - + - + - + - + - + @@ -66,7 +66,7 @@ - + -- cgit v1.2.3 From 0bcc81c0b149f1ae3eda7ce72f28199e48744a76 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 15 Jan 2017 21:00:22 +0100 Subject: Removed writerTeXLigatures. Make `smart` extension work in LaTeX/ConTeXt writers instead. Instead of `-t latex --no-tex-ligatures`, do `-t latex-smart`. --- MANUAL.txt | 30 ++++++++++++------------------ pandoc.hs | 9 --------- src/Text/Pandoc/Options.hs | 2 -- src/Text/Pandoc/Writers/ConTeXt.hs | 2 +- src/Text/Pandoc/Writers/LaTeX.hs | 6 +++--- 5 files changed, 16 insertions(+), 33 deletions(-) (limited to 'src/Text') diff --git a/MANUAL.txt b/MANUAL.txt index 2b55b8239..171f55955 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -697,20 +697,6 @@ Options affecting specific writers be numbered "1.5", specify `--number-offset=1,4`. Offsets are 0 by default. Implies `--number-sections`. -`--no-tex-ligatures` - -: Do not use the TeX ligatures for quotation marks, apostrophes, - and dashes (`` `...' ``, ` ``..'' `, `--`, `---`) when - writing or reading LaTeX or ConTeXt. In reading LaTeX, - parse the characters `` ` ``, `'`, and `-` literally, rather - than parsing ligatures for quotation marks and dashes. In - writing LaTeX or ConTeXt, print unicode quotation mark and - dash characters literally, rather than converting them to - the standard ASCII TeX ligatures. Note: If you use literal - curly quotes, dashes, and ellipses in your source, then you - may want to use disable the `smart` extension in your - source format. - `--listings` : Use the [`listings`] package for LaTeX code blocks @@ -3185,15 +3171,23 @@ Typography 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." +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`. Note: If you are *writing* Markdown, then the `smart` extension has the reverse effect: what would have been curly quotes comes out straight. -If your LaTeX template or any included header file call -for the [`csquotes`] package, pandoc will detect this -automatically and use `\enquote{...}` for quoted text. +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 --------- diff --git a/pandoc.hs b/pandoc.hs index 0baf555de..fe9cdba00 100644 --- a/pandoc.hs +++ b/pandoc.hs @@ -146,7 +146,6 @@ convertWithOpts opts args = do , optSlideLevel = slideLevel , optSetextHeaders = setextHeaders , optAscii = ascii - , optTeXLigatures = texLigatures , optDefaultImageExtension = defaultImageExtension , optExtractMedia = mbExtractMedia , optTrace = trace @@ -330,7 +329,6 @@ convertWithOpts opts args = do writerHighlight = highlight, writerHighlightStyle = highlightStyle, writerSetextHeaders = setextHeaders, - writerTeXLigatures = texLigatures, writerEpubMetadata = epubMetadata, writerEpubStylesheet = epubStylesheet, writerEpubFonts = epubFonts, @@ -570,7 +568,6 @@ data Opt = Opt , optSlideLevel :: Maybe Int -- ^ Header level that creates slides , optSetextHeaders :: Bool -- ^ Use atx headers for markdown level 1-2 , optAscii :: Bool -- ^ Use ascii characters only in html - , optTeXLigatures :: Bool -- ^ Use TeX ligatures for quotes/dashes , optDefaultImageExtension :: String -- ^ Default image extension , optExtractMedia :: Maybe FilePath -- ^ Path to extract embedded media , optTrace :: Bool -- ^ Print debug information @@ -634,7 +631,6 @@ defaultOpts = Opt , optSlideLevel = Nothing , optSetextHeaders = True , optAscii = False - , optTeXLigatures = True , optDefaultImageExtension = "" , optExtractMedia = Nothing , optTrace = False @@ -955,11 +951,6 @@ options = "NUMBERS") "" -- "Starting number for sections, subsections, etc." - , Option "" ["no-tex-ligatures"] - (NoArg - (\opt -> return opt { optTeXLigatures = False })) - "" -- "Don't use tex ligatures for quotes, dashes" - , Option "" ["listings"] (NoArg (\opt -> return opt { optListings = True })) diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index 5e4c51abf..4fe92dbbf 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -178,7 +178,6 @@ data WriterOptions = WriterOptions , writerHighlight :: Bool -- ^ Highlight source code , writerHighlightStyle :: Style -- ^ Style to use for highlighting , writerSetextHeaders :: Bool -- ^ Use setext headers for levels 1-2 in markdown - , writerTeXLigatures :: Bool -- ^ Use tex ligatures quotes, dashes in latex , writerEpubVersion :: Maybe EPUBVersion -- ^ Nothing or EPUB version , writerEpubMetadata :: String -- ^ Metadata to include in EPUB , writerEpubStylesheet :: Maybe String -- ^ EPUB stylesheet specified at command line @@ -224,7 +223,6 @@ instance Default WriterOptions where , writerHighlight = False , writerHighlightStyle = pygments , writerSetextHeaders = True - , writerTeXLigatures = True , writerEpubVersion = Nothing , writerEpubMetadata = "" , writerEpubStylesheet = Nothing diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index c8a4abfd5..b997c306a 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -111,7 +111,7 @@ toContextDir _ = "" -- | escape things as needed for ConTeXt escapeCharForConTeXt :: WriterOptions -> Char -> String escapeCharForConTeXt opts ch = - let ligatures = writerTeXLigatures opts in + let ligatures = isEnabled Ext_smart opts in case ch of '{' -> "\\{" '}' -> "\\}" diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index dbb8e4326..d9a31751e 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -272,7 +272,7 @@ stringToLaTeX _ [] = return "" stringToLaTeX ctx (x:xs) = do opts <- gets stOptions rest <- stringToLaTeX ctx xs - let ligatures = writerTeXLigatures opts && ctx == TextString + let ligatures = isEnabled Ext_smart opts && ctx == TextString let isUrl = ctx == URLString when (x == '€') $ modify $ \st -> st{ stUsesEuro = True } @@ -938,11 +938,11 @@ inlineToLaTeX (Quoted qt lst) = do let inner = s1 <> contents <> s2 return $ case qt of DoubleQuote -> - if writerTeXLigatures opts + if isEnabled Ext_smart opts then text "``" <> inner <> text "''" else char '\x201C' <> inner <> char '\x201D' SingleQuote -> - if writerTeXLigatures opts + if isEnabled Ext_smart opts then char '`' <> inner <> char '\'' else char '\x2018' <> inner <> char '\x2019' inlineToLaTeX (Str str) = liftM text $ stringToLaTeX TextString str -- cgit v1.2.3 From a3c3694024c1cb58748a31983bccdc4a58af567e Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 15 Jan 2017 21:30:20 +0100 Subject: Removed writerMediaBag from WriterOpts. ...since this is now handled through PandocMonad. Added an explicit MediaBag parameter to makePDF and makeSelfContained. --- pandoc.hs | 10 ++++------ src/Text/Pandoc/PDF.hs | 34 ++++++++++++++++++++-------------- src/Text/Pandoc/SelfContained.hs | 6 +++--- 3 files changed, 27 insertions(+), 23 deletions(-) (limited to 'src/Text') diff --git a/pandoc.hs b/pandoc.hs index fe9cdba00..3f09660e5 100644 --- a/pandoc.hs +++ b/pandoc.hs @@ -335,7 +335,6 @@ convertWithOpts opts args = do writerEpubChapterLevel = epubChapterLevel, writerTOCDepth = epubTOCDepth, writerReferenceDoc = referenceDoc, - writerMediaBag = mempty, writerVerbose = verbose, writerLaTeXArgs = latexEngineArgs } @@ -394,10 +393,9 @@ convertWithOpts opts args = do applyTransforms transforms >=> applyFilters datadir filters' [format]) doc - let writerOptions' = writerOptions{ writerMediaBag = media } case writer of -- StringWriter f -> f writerOptions doc' >>= writerFn outputFile - ByteStringWriter f -> f writerOptions' doc' >>= writeFnBinary outputFile + ByteStringWriter f -> f writerOptions doc' >>= writeFnBinary outputFile StringWriter f | pdfOutput -> do -- make sure writer is latex or beamer or context or html5 @@ -415,7 +413,7 @@ convertWithOpts opts args = do err 41 $ pdfprog ++ " not found. " ++ pdfprog ++ " is needed for pdf output." - res <- makePDF pdfprog f writerOptions' doc' + res <- makePDF pdfprog f writerOptions media doc' case res of Right pdf -> writeFnBinary outputFile pdf Left err' -> liftIO $ do @@ -426,12 +424,12 @@ convertWithOpts opts args = do let htmlFormat = format `elem` ["html","html5","s5","slidy","slideous","dzslides","revealjs"] selfcontain = if selfContained && htmlFormat - then makeSelfContained writerOptions' + then makeSelfContained writerOptions media else return handleEntities = if htmlFormat && ascii then toEntities else id - output <- f writerOptions' doc' + output <- f writerOptions doc' selfcontain (output ++ ['\n' | not standalone']) >>= writerFn outputFile . handleEntities diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index 68151f569..be889c052 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -48,6 +48,7 @@ import Data.List (isInfixOf) import Data.Maybe (fromMaybe) import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Definition +import Text.Pandoc.MediaBag import Text.Pandoc.Walk (walkM) import Text.Pandoc.Shared (warn, withTempDir, inDirectory, stringify) import Text.Pandoc.Writers.Shared (getField, metaToJSON) @@ -72,9 +73,10 @@ makePDF :: MonadIO m -- xelatex, context, wkhtmltopdf) -> (WriterOptions -> Pandoc -> PandocIO String) -- ^ writer -> WriterOptions -- ^ options + -> MediaBag -- ^ media -> Pandoc -- ^ document -> m (Either ByteString ByteString) -makePDF "wkhtmltopdf" writer opts doc@(Pandoc meta _) = liftIO $ do +makePDF "wkhtmltopdf" writer opts _mediabag doc@(Pandoc meta _) = liftIO $ do let mathArgs = case writerHTMLMathMethod opts of -- with MathJax, wait til all math is rendered: MathJax _ -> ["--run-script", "MathJax.Hub.Register.StartupHook('End Typeset', function() { window.status = 'mathjax_loaded' });", @@ -97,33 +99,37 @@ makePDF "wkhtmltopdf" writer opts doc@(Pandoc meta _) = liftIO $ do ] source <- runIOorExplode $ writer opts doc html2pdf (writerVerbose opts) args source -makePDF program writer opts doc = liftIO $ withTempDir "tex2pdf." $ \tmpdir -> do - doc' <- handleImages opts tmpdir doc - 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"] - -> tex2pdf' (writerVerbose opts) args tmpdir program source - _ -> return $ Left $ UTF8.fromStringLazy $ "Unknown program " ++ program +makePDF program writer opts mediabag doc = + liftIO $ withTempDir "tex2pdf." $ \tmpdir -> do + doc' <- handleImages opts mediabag tmpdir doc + 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"] + -> tex2pdf' (writerVerbose opts) args tmpdir program source + _ -> return $ Left $ UTF8.fromStringLazy $ "Unknown program " ++ program handleImages :: WriterOptions + -> MediaBag -> FilePath -- ^ temp dir to store images -> Pandoc -- ^ document -> IO Pandoc -handleImages opts tmpdir = walkM (convertImages tmpdir) <=< walkM (handleImage' opts tmpdir) +handleImages opts mediabag tmpdir = + walkM (convertImages tmpdir) <=< walkM (handleImage' opts mediabag tmpdir) handleImage' :: WriterOptions + -> MediaBag -> FilePath -> Inline -> IO Inline -handleImage' opts tmpdir (Image attr ils (src,tit)) = do +handleImage' opts mediabag tmpdir (Image attr ils (src,tit)) = do exists <- doesFileExist src if exists then return $ Image attr ils (src,tit) else do res <- runIO $ do - setMediaBag $ writerMediaBag opts + setMediaBag mediabag fetchItem (writerSourceURL opts) src case res of Right (contents, Just mime) -> do @@ -137,7 +143,7 @@ handleImage' opts tmpdir (Image attr ils (src,tit)) = do warn $ "Could not find image `" ++ src ++ "', skipping..." -- return alt text return $ Emph ils -handleImage' _ _ x = return x +handleImage' _ _ _ x = return x convertImages :: FilePath -> Inline -> IO Inline convertImages tmpdir (Image attr ils (src, tit)) = do diff --git a/src/Text/Pandoc/SelfContained.hs b/src/Text/Pandoc/SelfContained.hs index 176de99be..85b298a85 100644 --- a/src/Text/Pandoc/SelfContained.hs +++ b/src/Text/Pandoc/SelfContained.hs @@ -174,8 +174,8 @@ getDataURI media sourceURL mimetype src = do -- | Convert HTML into self-contained HTML, incorporating images, -- scripts, and CSS using data: URIs. -makeSelfContained :: MonadIO m => WriterOptions -> String -> m String -makeSelfContained opts inp = liftIO $ do +makeSelfContained :: MonadIO m => WriterOptions -> MediaBag -> String -> m String +makeSelfContained opts mediabag inp = liftIO $ do let tags = parseTags inp - out' <- mapM (convertTag (writerMediaBag opts) (writerSourceURL opts)) tags + out' <- mapM (convertTag mediabag (writerSourceURL opts)) tags return $ renderTags' out' -- cgit v1.2.3 From 00c6c371f2ad4660009f60800539569a5f4a556c Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 15 Jan 2017 21:45:38 +0100 Subject: Removed unused readerFileScope. API change. --- pandoc.hs | 1 - src/Text/Pandoc/Options.hs | 2 -- 2 files changed, 3 deletions(-) (limited to 'src/Text') diff --git a/pandoc.hs b/pandoc.hs index 3f09660e5..ee97e54a0 100644 --- a/pandoc.hs +++ b/pandoc.hs @@ -297,7 +297,6 @@ convertWithOpts opts args = do , readerDefaultImageExtension = defaultImageExtension , readerTrace = trace , readerTrackChanges = trackChanges - , readerFileScope = fileScope } let writerOptions = def { writerTemplate = templ, diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index 4fe92dbbf..d81f4da88 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -64,7 +64,6 @@ data ReaderOptions = ReaderOptions{ , readerDefaultImageExtension :: String -- ^ Default extension for images , readerTrace :: Bool -- ^ Print debugging info , readerTrackChanges :: TrackChanges - , readerFileScope :: Bool -- ^ Parse before combining } deriving (Show, Read, Data, Typeable, Generic) instance Default ReaderOptions @@ -79,7 +78,6 @@ instance Default ReaderOptions , readerDefaultImageExtension = "" , readerTrace = False , readerTrackChanges = AcceptChanges - , readerFileScope = False } -- -- cgit v1.2.3 From 2d04922cd0f2213f371db41729f4348f968c8b30 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 15 Jan 2017 22:15:35 +0100 Subject: Factored out deNote in Shared. --- src/Text/Pandoc/Shared.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index fabda42ed..18b4d3eac 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -400,8 +400,10 @@ removeFormatting = query go . walk deNote go (Math _ x) = [Str x] go LineBreak = [Space] go _ = [] - deNote (Note _) = Str "" - deNote x = x + +deNote :: Inline -> Inline +deNote (Note _) = Str "" +deNote x = x -- | Convert pandoc structure to a string with formatting removed. -- Footnotes are skipped (since we don't want their contents in link @@ -417,8 +419,6 @@ stringify = query go . walk deNote go (RawInline (Format "html") ('<':'b':'r':_)) = " " -- see #2105 go LineBreak = " " go _ = "" - deNote (Note _) = Str "" - deNote x = x -- | Bring all regular text in a pandoc structure to uppercase. -- -- cgit v1.2.3 From 4007d6a89749ff6576e65bb08631ff14a6d0ee20 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 15 Jan 2017 22:34:14 +0100 Subject: Removed writerIgnoreNotes. Instead, just temporarily remove notes when generating TOC lists in HTML and Markdown (as we already did in LaTeX). Also export deNote from Text.Pandoc.Shared. API change in Shared and Options.WriterOptions. --- pandoc.hs | 1 - src/Text/Pandoc/Options.hs | 2 -- src/Text/Pandoc/Shared.hs | 1 + src/Text/Pandoc/Writers/HTML.hs | 10 ++++------ src/Text/Pandoc/Writers/LaTeX.hs | 4 ---- src/Text/Pandoc/Writers/Markdown.hs | 10 +++++----- 6 files changed, 10 insertions(+), 18 deletions(-) (limited to 'src/Text') diff --git a/pandoc.hs b/pandoc.hs index ee97e54a0..e1c2c9097 100644 --- a/pandoc.hs +++ b/pandoc.hs @@ -306,7 +306,6 @@ convertWithOpts opts args = do writerHTMLMathMethod = mathMethod, writerIncremental = incremental, writerCiteMethod = citeMethod, - writerIgnoreNotes = False, writerNumberSections = numberSections, writerNumberOffset = numberFrom, writerSectionDivs = sectionDivs, diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index d81f4da88..cd10abeff 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -151,7 +151,6 @@ data WriterOptions = WriterOptions , writerSlideVariant :: HTMLSlideVariant -- ^ Are we writing S5, Slidy or Slideous? , writerIncremental :: Bool -- ^ True if lists should be incremental , writerHTMLMathMethod :: HTMLMathMethod -- ^ How to print math in HTML - , writerIgnoreNotes :: Bool -- ^ Ignore footnotes (used in making toc) , writerNumberSections :: Bool -- ^ Number sections in LaTeX , writerNumberOffset :: [Int] -- ^ Starting number for section, subsection, ... , writerSectionDivs :: Bool -- ^ Put sections in div tags in HTML @@ -197,7 +196,6 @@ instance Default WriterOptions where , writerSlideVariant = NoSlides , writerIncremental = False , writerHTMLMathMethod = PlainMath - , writerIgnoreNotes = False , writerNumberSections = False , writerNumberOffset = [0,0,0,0,0,0] , writerSectionDivs = False diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 18b4d3eac..f2a80fccf 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -56,6 +56,7 @@ module Text.Pandoc.Shared ( normalizeSpaces, extractSpaces, removeFormatting, + deNote, stringify, capitalize, compactify, diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 40658eaa8..a63047866 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -30,6 +30,7 @@ Conversion of 'Pandoc' documents to HTML. -} module Text.Pandoc.Writers.HTML ( writeHtml , writeHtmlString ) where import Text.Pandoc.Definition +import Text.Pandoc.Walk import Data.Monoid ((<>)) import Text.Pandoc.Shared import Text.Pandoc.Writers.Shared @@ -228,8 +229,7 @@ defList opts items = toList H.dl opts (items ++ [nl opts]) tableOfContents :: PandocMonad m => WriterOptions -> [Element] -> StateT WriterState m (Maybe Html) tableOfContents _ [] = return Nothing tableOfContents opts sects = do - let opts' = opts { writerIgnoreNotes = True } - contents <- mapM (elementToListItem opts') sects + contents <- mapM (elementToListItem opts) sects let tocList = catMaybes contents return $ if null tocList then Nothing @@ -253,7 +253,7 @@ elementToListItem opts (Sec lev num (id',classes,_) headerText subsecs) then (H.span ! A.class_ "toc-section-number" $ toHtml $ showSecNum num') >> preEscapedString " " else mempty - txt <- liftM (sectnum >>) $ inlineListToHtml opts headerText + txt <- liftM (sectnum >>) $ inlineListToHtml opts $ walk deNote headerText subHeads <- mapM (elementToListItem opts) subsecs >>= return . catMaybes let subList = if null subHeads then mempty @@ -852,9 +852,7 @@ inlineToHtml opts inline = imgAttrsToHtml opts attr return $ foldl (!) H5.embed attributes -- note: null title included, as in Markdown.pl - (Note contents) - | writerIgnoreNotes opts -> return mempty - | otherwise -> do + (Note contents) -> do notes <- gets stNotes let number = (length notes) + 1 let ref = show number diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index d9a31751e..655ea7dac 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -1318,10 +1318,6 @@ commonFromBcp47 x = fromIso $ head x fromIso "vi" = "vietnamese" fromIso _ = "" -deNote :: Inline -> Inline -deNote (Note _) = RawInline (Format "latex") "" -deNote x = x - pDocumentOptions :: P.Parsec String () [String] pDocumentOptions = do P.char '[' diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 9ef968fc6..8ae550fe1 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -288,9 +288,8 @@ escapeString opts = escapeStringUsing markdownEscapes -- | Construct table of contents from list of header blocks. tableOfContents :: PandocMonad m => WriterOptions -> [Block] -> m Doc tableOfContents opts headers = - let opts' = opts { writerIgnoreNotes = True } - contents = BulletList $ map (elementToListItem opts) $ hierarchicalize headers - in evalMD (blockToMarkdown opts' contents) def def + let contents = BulletList $ map (elementToListItem opts) $ hierarchicalize headers + in evalMD (blockToMarkdown opts contents) def def -- | Converts an Element to a list item for a table of contents, elementToListItem :: WriterOptions -> Element -> [Block] @@ -299,8 +298,9 @@ elementToListItem opts (Sec lev _nums (ident,_,_) headerText subsecs) [ BulletList (map (elementToListItem opts) subsecs) | not (null subsecs) && lev < writerTOCDepth opts ] where headerLink = if null ident - then headerText - else [Link nullAttr headerText ('#':ident, "")] + then walk deNote headerText + else [Link nullAttr (walk deNote headerText) + ('#':ident, "")] elementToListItem _ (Blk _) = [] attrsToMarkdown :: Attr -> Doc -- cgit v1.2.3 From 17916f478bc949e53c49720d0d930e03b0367176 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 15 Jan 2017 22:52:14 +0100 Subject: Put an Integer rather than Word64 behind Extensions. This allows us to expand indefinitely. No measurable performance penalty. --- src/Text/Pandoc/Extensions.hs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 14422ce39..d5e59e8e1 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -42,14 +42,13 @@ module Text.Pandoc.Extensions ( Extension(..) , githubMarkdownExtensions , multimarkdownExtensions ) where -import Data.Word (Word64) import Data.Bits (testBit, setBit, clearBit) import Data.Data (Data) import Data.Typeable (Typeable) import GHC.Generics (Generic) -newtype Extensions = Extensions Word64 - deriving (Show, Read, Eq, Ord, Bounded, Data, Typeable, Generic) +newtype Extensions = Extensions Integer + deriving (Show, Read, Eq, Ord, Data, Typeable, Generic) extensionsFromList :: [Extension] -> Extensions extensionsFromList = foldr enableExtension emptyExtensions -- cgit v1.2.3 From 73f57daf69d4f1dbeb4b2574eb4e85280293ed67 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 16 Jan 2017 21:10:34 +0100 Subject: Fixed shadowing warnings. --- src/Text/Pandoc/Writers/OpenDocument.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index 1a758193a..71e599e09 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -35,7 +35,7 @@ import Text.Pandoc.Options import Text.Pandoc.XML import Text.Pandoc.Shared (linesToPara) import Text.Pandoc.Templates (renderTemplate') -import Text.Pandoc.Readers.Odt.StyleReader +import Text.Pandoc.Readers.Odt.StyleReader hiding (listStyle) import Text.Pandoc.Writers.Math import Text.Pandoc.Pretty import Text.Printf ( printf ) @@ -392,9 +392,9 @@ blockToOpenDocument o bs paragraph inlines = inParagraphTags =<< inlinesToOpenDocument o inlines paraWithBreak :: PandocMonad m => ParaBreak -> [Inline] -> OD m Doc - paraWithBreak breakKind bs = do + paraWithBreak breakKind bs' = do pn <- paraBreakStyle breakKind - withParagraphStyle o ("P" ++ show pn) [Para bs] + withParagraphStyle o ("P" ++ show pn) [Para bs'] colHeadsToOpenDocument :: PandocMonad m => WriterOptions -> String -> [String] -> [[Block]] -- cgit v1.2.3 From 01483f91bd152ad806a8110d75353edfc9551ec8 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 22 Jan 2017 10:37:19 +0100 Subject: Revert "Added page breaks into Pandoc." This reverts commit f02a12aff638fa2339192231b8f601bffdfe3e14. --- pandoc.cabal | 6 +++--- src/Text/Pandoc/Readers/Odt/ContentReader.hs | 19 ++++--------------- src/Text/Pandoc/Readers/Odt/StyleReader.hs | 27 ++++++--------------------- src/Text/Pandoc/Writers/AsciiDoc.hs | 1 - src/Text/Pandoc/Writers/CommonMark.hs | 1 - src/Text/Pandoc/Writers/ConTeXt.hs | 1 - src/Text/Pandoc/Writers/Custom.hs | 2 -- src/Text/Pandoc/Writers/Docbook.hs | 1 - src/Text/Pandoc/Writers/Docx.hs | 1 - src/Text/Pandoc/Writers/DokuWiki.hs | 2 -- src/Text/Pandoc/Writers/FB2.hs | 2 -- src/Text/Pandoc/Writers/HTML.hs | 1 - src/Text/Pandoc/Writers/Haddock.hs | 1 - src/Text/Pandoc/Writers/ICML.hs | 1 - src/Text/Pandoc/Writers/LaTeX.hs | 1 - src/Text/Pandoc/Writers/Man.hs | 1 - src/Text/Pandoc/Writers/Markdown.hs | 1 - src/Text/Pandoc/Writers/MediaWiki.hs | 2 -- src/Text/Pandoc/Writers/OpenDocument.hs | 27 +++------------------------ src/Text/Pandoc/Writers/Org.hs | 1 - src/Text/Pandoc/Writers/RST.hs | 1 - src/Text/Pandoc/Writers/RTF.hs | 1 - src/Text/Pandoc/Writers/TEI.hs | 1 - src/Text/Pandoc/Writers/Texinfo.hs | 2 -- src/Text/Pandoc/Writers/Textile.hs | 2 -- src/Text/Pandoc/Writers/ZimWiki.hs | 2 -- stack.yaml | 5 +---- 27 files changed, 17 insertions(+), 96 deletions(-) (limited to 'src/Text') diff --git a/pandoc.cabal b/pandoc.cabal index b62fe551d..7cb292b05 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -270,7 +270,7 @@ Library xml >= 1.3.12 && < 1.4, random >= 1 && < 1.2, extensible-exceptions >= 0.1 && < 0.2, - pandoc-types >= 1.18 && < 1.19, + pandoc-types >= 1.17 && < 1.18, aeson >= 0.7 && < 1.2, tagsoup >= 0.13.7 && < 0.15, base64-bytestring >= 0.1 && < 1.1, @@ -426,7 +426,7 @@ Library Executable pandoc Build-Depends: pandoc, - pandoc-types >= 1.18 && < 1.19, + pandoc-types >= 1.17 && < 1.18, base >= 4.2 && <5, directory >= 1.2 && < 1.4, filepath >= 1.1 && < 1.5, @@ -504,7 +504,7 @@ Test-Suite test-pandoc Build-Depends: base >= 4.2 && < 5, syb >= 0.1 && < 0.7, pandoc, - pandoc-types >= 1.18 && < 1.19, + pandoc-types >= 1.17 && < 1.18, bytestring >= 0.9 && < 0.11, text >= 0.11 && < 1.3, directory >= 1 && < 1.4, diff --git a/src/Text/Pandoc/Readers/Odt/ContentReader.hs b/src/Text/Pandoc/Readers/Odt/ContentReader.hs index 0df86e2a5..2672b01ef 100644 --- a/src/Text/Pandoc/Readers/Odt/ContentReader.hs +++ b/src/Text/Pandoc/Readers/Odt/ContentReader.hs @@ -381,9 +381,9 @@ getParaModifier :: Style -> ParaModifier getParaModifier Style{..} | Just props <- paraProperties styleProperties , isBlockQuote (indentation props) (margin_left props) - = pageBreakMaybe (paraProperties styleProperties) blockQuote + = blockQuote | otherwise - = pageBreakMaybe (paraProperties styleProperties) id + = id where isBlockQuote mIndent mMargin | LengthValueMM indent <- mIndent @@ -408,19 +408,7 @@ getParaModifier Style{..} | Just props <- paraProperties styleProperties | otherwise = False - pageBreakMaybe :: Maybe ParaProperties -> ParaModifier -> ParaModifier - pageBreakMaybe (Just props) modifier = insertPageBreak (page_break props) modifier - pageBreakMaybe Nothing modifier = modifier - - insertPageBreak :: ParaBreak -> ParaModifier -> ParaModifier - insertPageBreak PageAfter modifier = - \x -> (fromList (toList (modifier x) ++ [Para (toList pageBreak)])) - insertPageBreak PageBefore modifier = - \x -> (fromList (Para (toList pageBreak) : toList (modifier x))) - insertPageBreak PageBoth modifier = - \x -> (fromList ((Para (toList pageBreak) : toList (modifier x)) ++ [Para (toList pageBreak)])) - insertPageBreak _ modifier = - modifier + -- constructPara :: OdtReaderSafe Blocks Blocks -> OdtReaderSafe Blocks Blocks constructPara reader = proc blocks -> do @@ -906,6 +894,7 @@ read_reference_ref = matchingElement NsText "reference-ref" $ maybeInAnchorRef <<< matchChildContent [] read_plain_text + ---------------------- -- Entry point ---------------------- diff --git a/src/Text/Pandoc/Readers/Odt/StyleReader.hs b/src/Text/Pandoc/Readers/Odt/StyleReader.hs index cd31f50a8..26ba6df82 100644 --- a/src/Text/Pandoc/Readers/Odt/StyleReader.hs +++ b/src/Text/Pandoc/Readers/Odt/StyleReader.hs @@ -43,7 +43,6 @@ module Text.Pandoc.Readers.Odt.StyleReader , TextProperties (..) , ParaProperties (..) , VerticalTextPosition (..) -, ParaBreak (..) , ListItemNumberFormat (..) , ListLevel , ListStyle (..) @@ -274,7 +273,6 @@ instance Default TextProperties where data ParaProperties = PropP { paraNumbering :: ParaNumbering , indentation :: LengthOrPercent , margin_left :: LengthOrPercent - , page_break :: ParaBreak } deriving ( Eq, Show ) @@ -282,7 +280,6 @@ instance Default ParaProperties where def = PropP { paraNumbering = NumberingNone , indentation = def , margin_left = def - , page_break = AutoNone } ---- @@ -317,9 +314,6 @@ instance Lookupable UnderlineMode where data ParaNumbering = NumberingNone | NumberingKeep | NumberingRestart Int deriving ( Eq, Show ) -data ParaBreak = AutoNone | PageBefore | PageAfter | PageBoth - deriving ( Eq, Show ) - data LengthOrPercent = LengthValueMM Int | PercentValue Int deriving ( Eq, Show ) @@ -539,20 +533,16 @@ readLineMode modeAttr styleAttr = proc x -> do readParaProperties :: StyleReader _x ParaProperties readParaProperties = executeIn NsStyle "paragraph-properties" $ liftAsSuccess - ( liftA4 PropP + ( liftA3 PropP ( liftA2 readNumbering - ( isSet' NsText "number-lines" ) - ( readAttr' NsText "line-number" ) + ( isSet' NsText "number-lines" ) + ( readAttr' NsText "line-number" ) ) ( liftA2 readIndentation - ( isSetWithDefault NsStyle "auto-text-indent" False ) - ( getAttr NsXSL_FO "text-indent" ) - ) - ( getAttr NsXSL_FO "margin-left" ) - ( liftA2 readPageBreak - ( findAttrWithDefault NsXSL_FO "break-before" "auto" ) - ( findAttrWithDefault NsXSL_FO "break-after" "auto" ) + ( isSetWithDefault NsStyle "auto-text-indent" False ) + ( getAttr NsXSL_FO "text-indent" ) ) + ( getAttr NsXSL_FO "margin-left" ) ) where readNumbering (Just True) (Just n) = NumberingRestart n readNumbering (Just True) _ = NumberingKeep @@ -561,11 +551,6 @@ readParaProperties = readIndentation False indent = indent readIndentation True _ = def - readPageBreak "page" "page" = PageBoth - readPageBreak "page" _ = PageBefore - readPageBreak _ "page" = PageAfter - readPageBreak _ _ = AutoNone - ---- -- List styles ---- diff --git a/src/Text/Pandoc/Writers/AsciiDoc.hs b/src/Text/Pandoc/Writers/AsciiDoc.hs index eed6183b4..356b29504 100644 --- a/src/Text/Pandoc/Writers/AsciiDoc.hs +++ b/src/Text/Pandoc/Writers/AsciiDoc.hs @@ -413,7 +413,6 @@ inlineToAsciiDoc _ (RawInline f s) | f == "asciidoc" = return $ text s | otherwise = return empty inlineToAsciiDoc _ LineBreak = return $ " +" <> cr -inlineToAsciiDoc _ PageBreak = return empty inlineToAsciiDoc _ Space = return space inlineToAsciiDoc opts SoftBreak = case writerWrapText opts of diff --git a/src/Text/Pandoc/Writers/CommonMark.hs b/src/Text/Pandoc/Writers/CommonMark.hs index c1963a9a8..c58e83f19 100644 --- a/src/Text/Pandoc/Writers/CommonMark.hs +++ b/src/Text/Pandoc/Writers/CommonMark.hs @@ -149,7 +149,6 @@ inlineToNodes :: Inline -> [Node] -> [Node] inlineToNodes (Str s) = (node (TEXT (T.pack s)) [] :) inlineToNodes Space = (node (TEXT (T.pack " ")) [] :) inlineToNodes LineBreak = (node LINEBREAK [] :) -inlineToNodes PageBreak = id inlineToNodes SoftBreak = (node SOFTBREAK [] :) inlineToNodes (Emph xs) = (node EMPH (inlinesToNodes xs) :) inlineToNodes (Strong xs) = (node STRONG (inlinesToNodes xs) :) diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index b997c306a..ea8b90db3 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -347,7 +347,6 @@ inlineToConTeXt SoftBreak = do WrapAuto -> space WrapNone -> space WrapPreserve -> cr -inlineToConTeXt PageBreak = return empty inlineToConTeXt Space = return space -- Handle HTML-like internal document references to sections inlineToConTeXt (Link _ txt (('#' : ref), _)) = do diff --git a/src/Text/Pandoc/Writers/Custom.hs b/src/Text/Pandoc/Writers/Custom.hs index 371dd21c3..cf641dcd6 100644 --- a/src/Text/Pandoc/Writers/Custom.hs +++ b/src/Text/Pandoc/Writers/Custom.hs @@ -310,8 +310,6 @@ inlineToCustom lua (RawInline format str) = inlineToCustom lua (LineBreak) = callfunc lua "LineBreak" -inlineToCustom lua (PageBreak) = callfunc lua "PageBreak" - inlineToCustom lua (Link attr txt (src,tit)) = callfunc lua "Link" txt src tit (attrToMap attr) diff --git a/src/Text/Pandoc/Writers/Docbook.hs b/src/Text/Pandoc/Writers/Docbook.hs index 0ec7445be..32695e128 100644 --- a/src/Text/Pandoc/Writers/Docbook.hs +++ b/src/Text/Pandoc/Writers/Docbook.hs @@ -373,7 +373,6 @@ inlineToDocbook _ (RawInline f x) inlineToDocbook _ LineBreak = return $ text "\n" -- currently ignore, would require the option to add custom -- styles to the document -inlineToDocbook _ PageBreak = return empty inlineToDocbook _ Space = return space -- because we use \n for LineBreak, we can't do soft breaks: inlineToDocbook _ SoftBreak = return space diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 25e224a7a..b7fd3e2a3 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -1106,7 +1106,6 @@ inlineToOpenXML' opts (Strikeout lst) = withTextProp (mknode "w:strike" [] ()) $ inlinesToOpenXML opts lst inlineToOpenXML' _ LineBreak = return [br] -inlineToOpenXML' _ PageBreak = return [pageBreak] inlineToOpenXML' _ (RawInline f str) | f == Format "openxml" = return [ x | Elem x <- parseXML str ] | otherwise = return [] diff --git a/src/Text/Pandoc/Writers/DokuWiki.hs b/src/Text/Pandoc/Writers/DokuWiki.hs index 42cddcef8..79a371d4d 100644 --- a/src/Text/Pandoc/Writers/DokuWiki.hs +++ b/src/Text/Pandoc/Writers/DokuWiki.hs @@ -475,8 +475,6 @@ inlineToDokuWiki _ (RawInline f str) inlineToDokuWiki _ LineBreak = return "\\\\\n" -inlineToDokuWiki _ PageBreak = return mempty - inlineToDokuWiki opts SoftBreak = case writerWrapText opts of WrapNone -> return " " diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index 7baac4f9e..600d34499 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -437,7 +437,6 @@ toXml (Code _ s) = return [el "code" s] toXml Space = return [txt " "] toXml SoftBreak = return [txt " "] toXml LineBreak = return [el "empty-line" ()] -toXml PageBreak = return [] toXml (Math _ formula) = insertMath InlineImage formula toXml (RawInline _ _) = return [] -- raw TeX and raw HTML are suppressed toXml (Link _ text (url,ttl)) = do @@ -569,7 +568,6 @@ plain (Code _ s) = s plain Space = " " plain SoftBreak = " " plain LineBreak = "\n" -plain PageBreak = "\n" plain (Math _ s) = s plain (RawInline _ s) = s plain (Link _ text (url,_)) = concat (map plain text ++ [" <", url, ">"]) diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index a63047866..e144d0d63 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -713,7 +713,6 @@ inlineToHtml opts inline = WrapPreserve -> preEscapedString "\n" (LineBreak) -> return $ (if writerHtml5 opts then H5.br else H.br) <> strToHtml "\n" - (PageBreak) -> return mempty (Span (id',classes,kvs) ils) -> inlineListToHtml opts ils >>= return . addAttrs opts attr' . H.span diff --git a/src/Text/Pandoc/Writers/Haddock.hs b/src/Text/Pandoc/Writers/Haddock.hs index 115d5d8d8..1c160ea1c 100644 --- a/src/Text/Pandoc/Writers/Haddock.hs +++ b/src/Text/Pandoc/Writers/Haddock.hs @@ -339,7 +339,6 @@ inlineToHaddock _ (RawInline f str) | otherwise = return empty -- no line break in haddock (see above on CodeBlock) inlineToHaddock _ LineBreak = return cr -inlineToHaddock _ PageBreak = return empty inlineToHaddock opts SoftBreak = case writerWrapText opts of WrapAuto -> return space diff --git a/src/Text/Pandoc/Writers/ICML.hs b/src/Text/Pandoc/Writers/ICML.hs index b68b9067a..41bca11b2 100644 --- a/src/Text/Pandoc/Writers/ICML.hs +++ b/src/Text/Pandoc/Writers/ICML.hs @@ -435,7 +435,6 @@ inlineToICML opts style SoftBreak = WrapNone -> charStyle style space WrapPreserve -> charStyle style cr inlineToICML _ style LineBreak = charStyle style $ text lineSeparator -inlineToICML _ _ PageBreak = return empty inlineToICML opts style (Math mt str) = lift (texMathToInlines mt str) >>= (fmap cat . mapM (inlineToICML opts style)) diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 655ea7dac..031cd584e 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -961,7 +961,6 @@ inlineToLaTeX SoftBreak = do WrapAuto -> return space WrapNone -> return space WrapPreserve -> return cr -inlineToLaTeX PageBreak = return $ "\\clearpage{}" inlineToLaTeX Space = return space inlineToLaTeX (Link _ txt ('#':ident, _)) = do contents <- inlineListToLaTeX txt diff --git a/src/Text/Pandoc/Writers/Man.hs b/src/Text/Pandoc/Writers/Man.hs index a9a30fd45..36ed5fab0 100644 --- a/src/Text/Pandoc/Writers/Man.hs +++ b/src/Text/Pandoc/Writers/Man.hs @@ -351,7 +351,6 @@ inlineToMan _ (RawInline f str) | otherwise = return empty inlineToMan _ LineBreak = return $ cr <> text ".PD 0" $$ text ".P" $$ text ".PD" <> cr -inlineToMan _ PageBreak = return empty inlineToMan _ SoftBreak = return space inlineToMan _ Space = return space inlineToMan opts (Link _ txt (src, _)) = do diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 8ae550fe1..8de09864a 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -1038,7 +1038,6 @@ inlineToMarkdown opts SoftBreak = do WrapNone -> space' WrapAuto -> space' WrapPreserve -> cr -inlineToMarkdown _ PageBreak = return empty inlineToMarkdown opts (Cite [] lst) = inlineListToMarkdown opts lst inlineToMarkdown opts (Cite (c:cs) lst) | not (isEnabled Ext_citations opts) = inlineListToMarkdown opts lst diff --git a/src/Text/Pandoc/Writers/MediaWiki.hs b/src/Text/Pandoc/Writers/MediaWiki.hs index 774139c43..dc6206e6c 100644 --- a/src/Text/Pandoc/Writers/MediaWiki.hs +++ b/src/Text/Pandoc/Writers/MediaWiki.hs @@ -405,8 +405,6 @@ inlineToMediaWiki (RawInline f str) inlineToMediaWiki LineBreak = return "
\n" -inlineToMediaWiki PageBreak = return mempty - inlineToMediaWiki SoftBreak = do wrapText <- gets (writerWrapText . stOptions) case wrapText of diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index 71e599e09..f50b240a4 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -35,7 +35,6 @@ import Text.Pandoc.Options import Text.Pandoc.XML import Text.Pandoc.Shared (linesToPara) import Text.Pandoc.Templates (renderTemplate') -import Text.Pandoc.Readers.Odt.StyleReader hiding (listStyle) import Text.Pandoc.Writers.Math import Text.Pandoc.Pretty import Text.Printf ( printf ) @@ -319,7 +318,9 @@ blockToOpenDocument o bs else inParagraphTags =<< inlinesToOpenDocument o b | Para [Image attr c (s,'f':'i':'g':':':t)] <- bs = figure attr c s t - | Para b <- bs = paragraph b + | Para b <- bs = if null b + then return empty + else inParagraphTags =<< inlinesToOpenDocument o b | LineBlock b <- bs = blockToOpenDocument o $ linesToPara b | Div _ xs <- bs = blocksToOpenDocument o xs | Header i _ b <- bs = setFirstPara >> @@ -380,22 +381,6 @@ blockToOpenDocument o bs captionDoc <- withParagraphStyle o "FigureCaption" [Para caption] return $ imageDoc $$ captionDoc - endsWithPageBreak [] = False - endsWithPageBreak [PageBreak] = True - endsWithPageBreak (_ : xs) = endsWithPageBreak xs - - paragraph :: PandocMonad m => [Inline] -> OD m Doc - paragraph [] = return empty - paragraph (PageBreak : rest) | endsWithPageBreak rest = paraWithBreak PageBoth rest - paragraph (PageBreak : rest) = paraWithBreak PageBefore rest - paragraph inlines | endsWithPageBreak inlines = paraWithBreak PageAfter inlines - paragraph inlines = inParagraphTags =<< inlinesToOpenDocument o inlines - - paraWithBreak :: PandocMonad m => ParaBreak -> [Inline] -> OD m Doc - paraWithBreak breakKind bs' = do - pn <- paraBreakStyle breakKind - withParagraphStyle o ("P" ++ show pn) [Para bs'] - colHeadsToOpenDocument :: PandocMonad m => WriterOptions -> String -> [String] -> [[Block]] -> OD m Doc @@ -595,12 +580,6 @@ paraStyle attrs = do addParaStyle $ inTags True "style:style" (styleAttr ++ attrs) paraProps return pn -paraBreakStyle :: PandocMonad m => ParaBreak -> OD m Int -paraBreakStyle PageBefore = paraStyle "Text_20_body" [("fo:break-before", "page")] -paraBreakStyle PageAfter = paraStyle "Text_20_body" [("fo:break-after", "page")] -paraBreakStyle PageBoth = paraStyle "Text_20_body" [("fo:break-before", "page"), ("fo:break-after", "page")] -paraBreakStyle AutoNone = paraStyle "Text_20_body" [] - paraListStyle :: PandocMonad m => Int -> OD m Int paraListStyle l = paraStyle [("style:parent-style-name","Text_20_body") diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index febb2e98f..09c924397 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -351,7 +351,6 @@ inlineToOrg (RawInline f@(Format f') str) = then text str else "@@" <> text f' <> ":" <> text str <> "@@" inlineToOrg LineBreak = return (text "\\\\" <> cr) -inlineToOrg PageBreak = return empty inlineToOrg Space = return space inlineToOrg SoftBreak = do wrapText <- gets (writerWrapText . stOptions) diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index 438407cce..ee3ecd9cd 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -462,7 +462,6 @@ inlineToRST SoftBreak = do WrapPreserve -> return cr WrapAuto -> return space WrapNone -> return space -inlineToRST PageBreak = return $ ".. pagebreak::" -- autolink inlineToRST (Link _ [Str str] (src, _)) | isURI src && diff --git a/src/Text/Pandoc/Writers/RTF.hs b/src/Text/Pandoc/Writers/RTF.hs index bd3461a03..77f01e4a1 100644 --- a/src/Text/Pandoc/Writers/RTF.hs +++ b/src/Text/Pandoc/Writers/RTF.hs @@ -396,7 +396,6 @@ inlineToRTF (RawInline f str) | otherwise = return "" inlineToRTF (LineBreak) = return "\\line " inlineToRTF SoftBreak = return " " -inlineToRTF PageBreak = return "\\page " inlineToRTF Space = return " " inlineToRTF (Link _ text (src, _)) = do contents <- inlinesToRTF text diff --git a/src/Text/Pandoc/Writers/TEI.hs b/src/Text/Pandoc/Writers/TEI.hs index 0a22ae085..c589c0c36 100644 --- a/src/Text/Pandoc/Writers/TEI.hs +++ b/src/Text/Pandoc/Writers/TEI.hs @@ -285,7 +285,6 @@ inlineToTEI _ (Math t str) = inlineToTEI _ (RawInline f x) | f == "tei" = text x | otherwise = empty inlineToTEI _ LineBreak = selfClosingTag "lb" [] -inlineToTEI _ PageBreak = selfClosingTag "pb" [] inlineToTEI _ Space = space -- because we use \n for LineBreak, we can't do soft breaks: inlineToTEI _ SoftBreak = space diff --git a/src/Text/Pandoc/Writers/Texinfo.hs b/src/Text/Pandoc/Writers/Texinfo.hs index 783a01063..a66ffe88b 100644 --- a/src/Text/Pandoc/Writers/Texinfo.hs +++ b/src/Text/Pandoc/Writers/Texinfo.hs @@ -458,8 +458,6 @@ inlineToTexinfo SoftBreak = do WrapPreserve -> return cr inlineToTexinfo Space = return space -inlineToTexinfo PageBreak = return $ text "@page" - inlineToTexinfo (Link _ txt (src@('#':_), _)) = do contents <- escapeCommas $ inlineListToTexinfo txt return $ text "@ref" <> diff --git a/src/Text/Pandoc/Writers/Textile.hs b/src/Text/Pandoc/Writers/Textile.hs index 9691b7705..45f1780cf 100644 --- a/src/Text/Pandoc/Writers/Textile.hs +++ b/src/Text/Pandoc/Writers/Textile.hs @@ -438,8 +438,6 @@ inlineToTextile opts (RawInline f str) inlineToTextile _ LineBreak = return "\n" -inlineToTextile _ PageBreak = return mempty - inlineToTextile _ SoftBreak = return " " inlineToTextile _ Space = return " " diff --git a/src/Text/Pandoc/Writers/ZimWiki.hs b/src/Text/Pandoc/Writers/ZimWiki.hs index f15b290e4..42b168418 100644 --- a/src/Text/Pandoc/Writers/ZimWiki.hs +++ b/src/Text/Pandoc/Writers/ZimWiki.hs @@ -320,8 +320,6 @@ inlineToZimWiki opts (RawInline f str) inlineToZimWiki _ LineBreak = return "\n" -- was \\\\ -inlineToZimWiki _ PageBreak = return mempty - inlineToZimWiki opts SoftBreak = case writerWrapText opts of WrapNone -> return " " diff --git a/stack.yaml b/stack.yaml index 3d4d4797d..66cab58a5 100644 --- a/stack.yaml +++ b/stack.yaml @@ -7,10 +7,6 @@ flags: network-uri: true packages: - '.' -- location: - git: https://github.com/jgm/pandoc-types.git - commit: 973394685aad945ccd92a86bf76e5c644e72e127 - extra-dep: true - location: git: https://github.com/jgm/texmath.git commit: 31273683a376e97848028e4619f28ab8c03c88af @@ -19,4 +15,5 @@ extra-deps: - doctemplates-0.1.0.2 - pandoc-types-1.17.0.4 - skylighting-0.1.1.1 +- texmath-0.9 resolver: lts-7.14 -- cgit v1.2.3 From 6f9df9b4f1d3d22c53b9d6f3c333efc23a84ffe7 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 22 Jan 2017 10:49:52 +0100 Subject: Removed vestigial writerMediaBag from WriterOptions. API change. --- src/Text/Pandoc/Options.hs | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index cd10abeff..3a787a733 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -47,7 +47,6 @@ module Text.Pandoc.Options ( module Text.Pandoc.Extensions import Text.Pandoc.Extensions import Data.Default import Text.Pandoc.Highlighting (Style, pygments) -import Text.Pandoc.MediaBag (MediaBag) import Data.Data (Data) import Data.Typeable (Typeable) import GHC.Generics (Generic) @@ -182,7 +181,6 @@ data WriterOptions = WriterOptions , writerEpubChapterLevel :: Int -- ^ Header level for chapters (separate files) , writerTOCDepth :: Int -- ^ Number of levels to include in TOC , writerReferenceDoc :: Maybe FilePath -- ^ Path to reference document if specified - , writerMediaBag :: MediaBag -- ^ Media collected by docx or epub reader , writerVerbose :: Bool -- ^ Verbose debugging output , writerLaTeXArgs :: [String] -- ^ Flags to pass to latex-engine , writerReferenceLocation :: ReferenceLocation -- ^ Location of footnotes and references for writing markdown @@ -226,7 +224,6 @@ instance Default WriterOptions where , writerEpubChapterLevel = 1 , writerTOCDepth = 3 , writerReferenceDoc = Nothing - , writerMediaBag = mempty , writerVerbose = False , writerLaTeXArgs = [] , writerReferenceLocation = EndOfDocument -- cgit v1.2.3 From d1efc839f129d23fe8a6523e33a01b0b463ee409 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 22 Jan 2017 11:36:30 +0100 Subject: Removed writerHighlight; made writerHighlightStyle a Maybe. API change. For no highlighting, set writerHighlightStyle to Nothing. --- pandoc.hs | 12 ++++-------- src/Text/Pandoc/Options.hs | 7 +++---- src/Text/Pandoc/Writers/Docx.hs | 29 ++++++++--------------------- src/Text/Pandoc/Writers/HTML.hs | 19 ++++++++++++------- src/Text/Pandoc/Writers/LaTeX.hs | 19 ++++++++++++------- tests/Tests/Writers/HTML.hs | 2 +- tests/Tests/Writers/LaTeX.hs | 2 +- tests/Tests/Writers/RST.hs | 2 +- 8 files changed, 42 insertions(+), 50 deletions(-) (limited to 'src/Text') diff --git a/pandoc.hs b/pandoc.hs index e1c2c9097..d13686cee 100644 --- a/pandoc.hs +++ b/pandoc.hs @@ -114,7 +114,6 @@ convertWithOpts opts args = do , optSelfContained = selfContained , optHtml5 = html5 , optHtmlQTags = htmlQTags - , optHighlight = highlight , optHighlightStyle = highlightStyle , optTopLevelDivision = topLevelDivision , optHTMLMathMethod = mathMethod' @@ -324,7 +323,6 @@ convertWithOpts opts args = do writerListings = listings, writerBeamer = False, writerSlideLevel = slideLevel, - writerHighlight = highlight, writerHighlightStyle = highlightStyle, writerSetextHeaders = setextHeaders, writerEpubMetadata = epubMetadata, @@ -532,8 +530,7 @@ data Opt = Opt , optSelfContained :: Bool -- ^ Make HTML accessible offline , optHtml5 :: Bool -- ^ Produce HTML5 in HTML , optHtmlQTags :: Bool -- ^ Use tags in HTML - , optHighlight :: Bool -- ^ Highlight source code - , optHighlightStyle :: Style -- ^ Style to use for highlighted code + , optHighlightStyle :: Maybe Style -- ^ Style to use for highlighted code , optTopLevelDivision :: TopLevelDivision -- ^ Type of the top-level divisions , optHTMLMathMethod :: HTMLMathMethod -- ^ Method to print HTML math , optReferenceDoc :: Maybe FilePath -- ^ Path of reference doc @@ -595,8 +592,7 @@ defaultOpts = Opt , optSelfContained = False , optHtml5 = False , optHtmlQTags = False - , optHighlight = True - , optHighlightStyle = pygments + , optHighlightStyle = Just pygments , optTopLevelDivision = TopLevelDefault , optHTMLMathMethod = PlainMath , optReferenceDoc = Nothing @@ -836,14 +832,14 @@ options = , Option "" ["no-highlight"] (NoArg - (\opt -> return opt { optHighlight = False })) + (\opt -> return opt { optHighlightStyle = Nothing })) "" -- "Don't highlight source code" , Option "" ["highlight-style"] (ReqArg (\arg opt -> do case lookup (map toLower arg) highlightingStyles of - Just s -> return opt{ optHighlightStyle = s } + Just s -> return opt{ optHighlightStyle = Just s } Nothing -> err 39 $ "Unknown style: " ++ arg) "STYLE") "" -- "Style for highlighted code" diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index 3a787a733..e7dec6492 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -171,8 +171,8 @@ data WriterOptions = WriterOptions , writerSlideLevel :: Maybe Int -- ^ Force header level of slides , writerTopLevelDivision :: TopLevelDivision -- ^ Type of top-level divisions , writerListings :: Bool -- ^ Use listings package for code - , writerHighlight :: Bool -- ^ Highlight source code - , writerHighlightStyle :: Style -- ^ Style to use for highlighting + , writerHighlightStyle :: Maybe Style -- ^ Style to use for highlighting + -- (Nothing = no highlighting) , writerSetextHeaders :: Bool -- ^ Use setext headers for levels 1-2 in markdown , writerEpubVersion :: Maybe EPUBVersion -- ^ Nothing or EPUB version , writerEpubMetadata :: String -- ^ Metadata to include in EPUB @@ -214,8 +214,7 @@ instance Default WriterOptions where , writerSlideLevel = Nothing , writerTopLevelDivision = TopLevelDefault , writerListings = False - , writerHighlight = False - , writerHighlightStyle = pygments + , writerHighlightStyle = Just pygments , writerSetextHeaders = True , writerEpubVersion = Nothing , writerEpubMetadata = "" diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index b7fd3e2a3..6a53485c4 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -52,7 +52,6 @@ import Text.Pandoc.Error (PandocError) import Text.XML.Light as XML import Text.TeXMath import Text.Pandoc.Readers.Docx.StyleMap -import Text.Pandoc.Readers.Docx.Util (elemName) import Control.Monad.Reader import Control.Monad.State import Skylighting @@ -450,18 +449,11 @@ writeDocx opts doc@(Pandoc meta _) = do let newstyles = map newParaPropToOpenXml newDynamicParaProps ++ map newTextPropToOpenXml newDynamicTextProps ++ - (styleToOpenXml styleMaps $ writerHighlightStyle opts) - let styledoc' = styledoc{ elContent = modifyContent (elContent styledoc) } - where - modifyContent - | writerHighlight opts = (++ map Elem newstyles) - | otherwise = filter notTokStyle - notTokStyle (Elem el) = notStyle el || notTokId el - notTokStyle _ = True - notStyle = (/= elemName' "style") . elName - notTokId = maybe True (`notElem` tokStys) . findAttr (elemName' "styleId") - tokStys = "SourceCode" : map show (enumFromTo KeywordTok NormalTok) - elemName' = elemName (sNameSpaces styleMaps) "w" + (case writerHighlightStyle opts of + Nothing -> [] + Just sty -> (styleToOpenXml styleMaps sty)) + let styledoc' = styledoc{ elContent = elContent styledoc ++ + map Elem newstyles } let styleEntry = toEntry stylepath epochtime $ renderXml styledoc' -- construct word/numbering.xml @@ -1130,11 +1122,9 @@ inlineToOpenXML' opts (Code attrs str) = do [ rCustomStyle (show toktype) ] , mknode "w:t" [("xml:space","preserve")] (T.unpack tok) ] withTextProp (rCustomStyle "VerbatimChar") - $ if writerHighlight opts - then case highlight formatOpenXML attrs str of - Nothing -> unhighlighted - Just h -> return h - else unhighlighted + $ case writerHighlightStyle opts >> highlight formatOpenXML attrs str of + Just h -> return h + Nothing -> unhighlighted inlineToOpenXML' opts (Note bs) = do notes <- gets stFootnotes notenum <- (lift . lift) getUniqueId @@ -1249,9 +1239,6 @@ inlineToOpenXML' opts (Image attr alt (src, title)) = do br :: Element br = breakElement "textWrapping" -pageBreak :: Element -pageBreak = breakElement "page" - breakElement :: String -> Element breakElement kind = mknode "w:r" [] [mknode "w:br" [("w:type",kind)] () ] diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index e144d0d63..c6d7b7f6a 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -179,8 +179,10 @@ pandocToHtml opts (Pandoc meta blocks) = do | otherwise -> mempty Nothing -> mempty let context = (if stHighlighting st - then defField "highlighting-css" - (styleToCss $ writerHighlightStyle opts) + then case writerHighlightStyle opts of + Just sty -> defField "highlighting-css" + (styleToCss sty) + Nothing -> id else id) $ (if stMath st then defField "math" (renderHtml math) @@ -509,8 +511,9 @@ blockToHtml opts (CodeBlock (id',classes,keyvals) rawCode) = do adjCode = if tolhs then unlines . map ("> " ++) . lines $ rawCode else rawCode - hlCode = if writerHighlight opts -- check highlighting options - then highlight formatHtmlBlock (id',classes',keyvals) adjCode + hlCode = if isJust (writerHighlightStyle opts) + then highlight formatHtmlBlock + (id',classes',keyvals) adjCode else Nothing case hlCode of Nothing -> return $ addAttrs opts (id',classes,keyvals) @@ -702,7 +705,8 @@ annotateMML e tex = math (unode "semantics" [cs, unode "annotation" (annotAttrs, -- | Convert Pandoc inline element to HTML. -inlineToHtml :: PandocMonad m => WriterOptions -> Inline -> StateT WriterState m Html +inlineToHtml :: PandocMonad m + => WriterOptions -> Inline -> StateT WriterState m Html inlineToHtml opts inline = case inline of (Str str) -> return $ strToHtml str @@ -739,8 +743,9 @@ inlineToHtml opts inline = modify $ \st -> st{ stHighlighting = True } return $ addAttrs opts (id',[],keyvals) h where (id',_,keyvals) = attr - hlCode = if writerHighlight opts - then highlight formatHtmlInline attr str + hlCode = if isJust (writerHighlightStyle opts) + then highlight formatHtmlInline + attr str else Nothing (Strikeout lst) -> inlineListToHtml opts lst >>= return . H.del diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 031cd584e..953e4250f 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -188,8 +188,11 @@ pandocToLaTeX options (Pandoc meta blocks) = do defField "listings" (writerListings options || stLHS st) $ defField "beamer" (writerBeamer options) $ (if stHighlighting st - then defField "highlighting-macros" (styleToLaTeX - $ writerHighlightStyle options ) + then case writerHighlightStyle options of + Just sty -> + defField "highlighting-macros" + (styleToLaTeX sty) + Nothing -> id else id) $ (case writerCiteMethod options of Natbib -> defField "biblio-title" biblioTitle . @@ -512,10 +515,11 @@ blockToLaTeX (CodeBlock (identifier,classes,keyvalAttr) str) = do return (flush $ linkAnchor $$ text (T.unpack h)) case () of _ | isEnabled Ext_literate_haskell opts && "haskell" `elem` classes && - "literate" `elem` classes -> lhsCodeBlock - | writerListings opts -> listingsCodeBlock - | writerHighlight opts && not (null classes) -> highlightedCodeBlock - | otherwise -> rawCodeBlock + "literate" `elem` classes -> lhsCodeBlock + | writerListings opts -> listingsCodeBlock + | not (null classes) && isJust (writerHighlightStyle opts) + -> highlightedCodeBlock + | otherwise -> rawCodeBlock blockToLaTeX (RawBlock f x) | f == Format "latex" || f == Format "tex" = return $ text x @@ -904,7 +908,8 @@ inlineToLaTeX (Code (_,classes,_) str) = do inHeading <- gets stInHeading case () of _ | writerListings opts && not inHeading -> listingsCode - | writerHighlight opts && not (null classes) -> highlightCode + | isJust (writerHighlightStyle opts) && not (null classes) + -> highlightCode | otherwise -> rawCode where listingsCode = do inNote <- gets stInNote diff --git a/tests/Tests/Writers/HTML.hs b/tests/Tests/Writers/HTML.hs index 0ce9aecb3..d99698c21 100644 --- a/tests/Tests/Writers/HTML.hs +++ b/tests/Tests/Writers/HTML.hs @@ -31,7 +31,7 @@ tests :: [Test] tests = [ testGroup "inline code" [ "basic" =: code "@&" =?> "@&" , "haskell" =: codeWith ("",["haskell"],[]) ">>=" - =?> ">>=" + =?> ">>=" , "nolanguage" =: codeWith ("",["nolanguage"],[]) ">>=" =?> ">>=" ] diff --git a/tests/Tests/Writers/LaTeX.hs b/tests/Tests/Writers/LaTeX.hs index 9eee1f58b..00c590370 100644 --- a/tests/Tests/Writers/LaTeX.hs +++ b/tests/Tests/Writers/LaTeX.hs @@ -8,7 +8,7 @@ import Text.Pandoc.Arbitrary () import Text.Pandoc.Builder latex :: (ToPandoc a) => a -> String -latex = latexWithOpts def{ writerHighlight = True } +latex = latexWithOpts def latexListing :: (ToPandoc a) => a -> String latexListing = latexWithOpts def{ writerListings = True } diff --git a/tests/Tests/Writers/RST.hs b/tests/Tests/Writers/RST.hs index 68a890ca8..dd55580c9 100644 --- a/tests/Tests/Writers/RST.hs +++ b/tests/Tests/Writers/RST.hs @@ -10,7 +10,7 @@ import Text.Pandoc.Arbitrary() infix 4 =: (=:) :: (ToString a, ToPandoc a) => String -> (a, String) -> Test -(=:) = test (purely (writeRST def{ writerHighlight = True }) . toPandoc) +(=:) = test (purely (writeRST def . toPandoc)) tests :: [Test] tests = [ testGroup "rubrics" -- cgit v1.2.3 From 8280d6a48958ef305e3dd29e2bb189fb1ea96b14 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 22 Jan 2017 12:19:46 +0100 Subject: Changes to verbosity in writer and reader options. API changes: Text.Pandoc.Options: * Added Verbosity. * Added writerVerbosity. * Added readerVerbosity. * Removed writerVerbose. * Removed readerTrace. pandoc CLI: The `--trace` option sets verbosity to DEBUG; the `--quiet` option sets it to ERROR, and the `--verbose` option sets it to INFO. The default is WARNING. --- pandoc.hs | 28 +++++++++-------------- src/Text/Pandoc/Options.hs | 13 +++++++---- src/Text/Pandoc/PDF.hs | 43 ++++++++++++++++++------------------ src/Text/Pandoc/Readers/EPUB.hs | 4 ++-- src/Text/Pandoc/Readers/HTML.hs | 6 ++--- src/Text/Pandoc/Readers/Haddock.hs | 2 +- src/Text/Pandoc/Readers/Markdown.hs | 2 +- src/Text/Pandoc/Readers/MediaWiki.hs | 2 +- src/Text/Pandoc/Readers/TWiki.hs | 2 +- src/Text/Pandoc/Readers/Textile.hs | 2 +- 10 files changed, 52 insertions(+), 52 deletions(-) (limited to 'src/Text') diff --git a/pandoc.hs b/pandoc.hs index d13686cee..5d1c7d068 100644 --- a/pandoc.hs +++ b/pandoc.hs @@ -125,8 +125,7 @@ convertWithOpts opts args = do , optTOCDepth = epubTOCDepth , optDumpArgs = dumpArgs , optIgnoreArgs = ignoreArgs - , optVerbose = verbose - , optQuiet = quiet + , optVerbosity = verbosity , optFailIfWarnings = failIfWarnings , optReferenceLinks = referenceLinks , optReferenceLocation = referenceLocation @@ -147,7 +146,6 @@ convertWithOpts opts args = do , optAscii = ascii , optDefaultImageExtension = defaultImageExtension , optExtractMedia = mbExtractMedia - , optTrace = trace , optTrackChanges = trackChanges , optFileScope = fileScope , optKaTeXStylesheet = katexStylesheet @@ -294,7 +292,7 @@ convertWithOpts opts args = do , readerIndentedCodeClasses = codeBlockClasses , readerApplyMacros = not laTeXOutput , readerDefaultImageExtension = defaultImageExtension - , readerTrace = trace + , readerVerbosity = verbosity , readerTrackChanges = trackChanges } @@ -331,7 +329,7 @@ convertWithOpts opts args = do writerEpubChapterLevel = epubChapterLevel, writerTOCDepth = epubTOCDepth, writerReferenceDoc = referenceDoc, - writerVerbose = verbose, + writerVerbosity = verbosity, writerLaTeXArgs = latexEngineArgs } @@ -361,8 +359,8 @@ convertWithOpts opts args = do ws <- getWarnings return (x, ws) when (not (null warnings)) $ do - unless quiet $ - mapM_ warn warnings + -- TODO make these streaming + when (verbosity >= WARNING) $ mapM_ warn warnings when failIfWarnings $ err 3 "Failing because there were warnings." return res @@ -541,8 +539,7 @@ data Opt = Opt , optTOCDepth :: Int -- ^ Number of levels to include in TOC , optDumpArgs :: Bool -- ^ Output command-line arguments , optIgnoreArgs :: Bool -- ^ Ignore command-line arguments - , optVerbose :: Bool -- ^ Verbose diagnostic output - , optQuiet :: Bool -- ^ Suppress warnings + , optVerbosity :: Verbosity -- ^ Verbosity of diagnostic output , optFailIfWarnings :: Bool -- ^ Fail on warnings , optReferenceLinks :: Bool -- ^ Use reference links in writing markdown, rst , optReferenceLocation :: ReferenceLocation -- ^ location for footnotes and link references in markdown output @@ -563,7 +560,6 @@ data Opt = Opt , optAscii :: Bool -- ^ Use ascii characters only in html , optDefaultImageExtension :: String -- ^ Default image extension , optExtractMedia :: Maybe FilePath -- ^ Path to extract embedded media - , optTrace :: Bool -- ^ Print debug information , optTrackChanges :: TrackChanges -- ^ Accept or reject MS Word track-changes. , optFileScope :: Bool -- ^ Parse input files before combining , optKaTeXStylesheet :: Maybe String -- ^ Path to stylesheet for KaTeX @@ -603,8 +599,7 @@ defaultOpts = Opt , optTOCDepth = 3 , optDumpArgs = False , optIgnoreArgs = False - , optVerbose = False - , optQuiet = False + , optVerbosity = WARNING , optFailIfWarnings = False , optReferenceLinks = False , optReferenceLocation = EndOfDocument @@ -625,9 +620,8 @@ defaultOpts = Opt , optAscii = False , optDefaultImageExtension = "" , optExtractMedia = Nothing - , optTrace = False , optTrackChanges = AcceptChanges - , optFileScope = False + , optFileScope = False , optKaTeXStylesheet = Nothing , optKaTeXJS = Nothing } @@ -1186,7 +1180,7 @@ options = , Option "" ["trace"] (NoArg - (\opt -> return opt { optTrace = True })) + (\opt -> return opt { optVerbosity = DEBUG })) "" -- "Turn on diagnostic tracing in readers." , Option "" ["dump-args"] @@ -1201,12 +1195,12 @@ options = , Option "" ["verbose"] (NoArg - (\opt -> return opt { optVerbose = True })) + (\opt -> return opt { optVerbosity = INFO })) "" -- "Verbose diagnostic output." , Option "" ["quiet"] (NoArg - (\opt -> return opt { optQuiet = True })) + (\opt -> return opt { optVerbosity = ERROR })) "" -- "Suppress warnings." , Option "" ["fail-if-warnings"] diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index e7dec6492..262a0392d 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -37,6 +37,7 @@ module Text.Pandoc.Options ( module Text.Pandoc.Extensions , HTMLSlideVariant (..) , EPUBVersion (..) , WrapOption (..) + , Verbosity (..) , TopLevelDivision (..) , WriterOptions (..) , TrackChanges (..) @@ -61,7 +62,7 @@ data ReaderOptions = ReaderOptions{ , readerIndentedCodeClasses :: [String] -- ^ Default classes for -- indented code blocks , readerDefaultImageExtension :: String -- ^ Default extension for images - , readerTrace :: Bool -- ^ Print debugging info + , readerVerbosity :: Verbosity -- ^ Verbosity level , readerTrackChanges :: TrackChanges } deriving (Show, Read, Data, Typeable, Generic) @@ -75,7 +76,7 @@ instance Default ReaderOptions , readerApplyMacros = True , readerIndentedCodeClasses = [] , readerDefaultImageExtension = "" - , readerTrace = False + , readerVerbosity = ERROR , readerTrackChanges = AcceptChanges } @@ -141,6 +142,10 @@ data ReferenceLocation = EndOfBlock -- ^ End of block | EndOfDocument -- ^ at end of document deriving (Show, Read, Eq, Data, Typeable, Generic) +-- | Verbosity level. +data Verbosity = ERROR | WARNING | INFO | DEBUG + deriving (Show, Read, Eq, Data, Enum, Ord, Bounded, Typeable, Generic) + -- | Options for writers data WriterOptions = WriterOptions { writerTemplate :: Maybe String -- ^ Template to use @@ -181,7 +186,7 @@ data WriterOptions = WriterOptions , writerEpubChapterLevel :: Int -- ^ Header level for chapters (separate files) , writerTOCDepth :: Int -- ^ Number of levels to include in TOC , writerReferenceDoc :: Maybe FilePath -- ^ Path to reference document if specified - , writerVerbose :: Bool -- ^ Verbose debugging output + , writerVerbosity :: Verbosity -- ^ Verbose debugging output , writerLaTeXArgs :: [String] -- ^ Flags to pass to latex-engine , writerReferenceLocation :: ReferenceLocation -- ^ Location of footnotes and references for writing markdown } deriving (Show, Data, Typeable, Generic) @@ -223,7 +228,7 @@ instance Default WriterOptions where , writerEpubChapterLevel = 1 , writerTOCDepth = 3 , writerReferenceDoc = Nothing - , writerVerbose = False + , writerVerbosity = WARNING , writerLaTeXArgs = [] , writerReferenceLocation = EndOfDocument } diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index be889c052..cc523febf 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -52,7 +52,8 @@ import Text.Pandoc.MediaBag import Text.Pandoc.Walk (walkM) import Text.Pandoc.Shared (warn, withTempDir, inDirectory, stringify) import Text.Pandoc.Writers.Shared (getField, metaToJSON) -import Text.Pandoc.Options (WriterOptions(..), HTMLMathMethod(..)) +import Text.Pandoc.Options (WriterOptions(..), HTMLMathMethod(..), + Verbosity(..)) import Text.Pandoc.MIME (extensionFromMimeType, getMimeType) import Text.Pandoc.Process (pipeProcess) import Control.Monad.Trans (MonadIO(..)) @@ -98,16 +99,16 @@ makePDF "wkhtmltopdf" writer opts _mediabag doc@(Pandoc meta _) = liftIO $ do (getField "margin-left" meta')) ] source <- runIOorExplode $ writer opts doc - html2pdf (writerVerbose opts) args source + html2pdf (writerVerbosity opts) args source makePDF program writer opts mediabag doc = liftIO $ withTempDir "tex2pdf." $ \tmpdir -> do doc' <- handleImages opts mediabag tmpdir doc source <- runIOorExplode $ writer opts doc' let args = writerLaTeXArgs opts case takeBaseName program of - "context" -> context2pdf (writerVerbose opts) tmpdir source + "context" -> context2pdf (writerVerbosity opts) tmpdir source prog | prog `elem` ["pdflatex", "lualatex", "xelatex"] - -> tex2pdf' (writerVerbose opts) args tmpdir program source + -> tex2pdf' (writerVerbosity opts) args tmpdir program source _ -> return $ Left $ UTF8.fromStringLazy $ "Unknown program " ++ program handleImages :: WriterOptions @@ -174,17 +175,17 @@ convertImage tmpdir fname = mime = getMimeType fname doNothing = return (Right fname) -tex2pdf' :: Bool -- ^ Verbose output +tex2pdf' :: Verbosity -- ^ Verbosity level -> [String] -- ^ Arguments to the latex-engine -> FilePath -- ^ temp directory for output -> String -- ^ tex program -> String -- ^ tex source -> IO (Either ByteString ByteString) -tex2pdf' verbose args tmpDir program source = do +tex2pdf' verbosity args tmpDir program source = do let numruns = if "\\tableofcontents" `isInfixOf` source then 3 -- to get page numbers else 2 -- 1 run won't give you PDF bookmarks - (exit, log', mbPdf) <- runTeXProgram verbose program args 1 numruns tmpDir source + (exit, log', mbPdf) <- runTeXProgram verbosity program args 1 numruns tmpDir source case (exit, mbPdf) of (ExitFailure _, _) -> do let logmsg = extractMsg log' @@ -222,9 +223,9 @@ extractConTeXtMsg log' = do -- Run a TeX program on an input bytestring and return (exit code, -- contents of stdout, contents of produced PDF if any). Rerun -- a fixed number of times to resolve references. -runTeXProgram :: Bool -> String -> [String] -> Int -> Int -> FilePath -> String - -> IO (ExitCode, ByteString, Maybe ByteString) -runTeXProgram verbose program args runNumber numRuns tmpDir source = do +runTeXProgram :: Verbosity -> String -> [String] -> Int -> Int -> FilePath + -> String -> IO (ExitCode, ByteString, Maybe ByteString) +runTeXProgram verbosity program args runNumber numRuns tmpDir source = do let file = tmpDir "input.tex" exists <- doesFileExist file unless exists $ UTF8.writeFile file source @@ -244,7 +245,7 @@ runTeXProgram verbose program args runNumber numRuns tmpDir source = do $ lookup "TEXINPUTS" env' let env'' = ("TEXINPUTS", texinputs) : [(k,v) | (k,v) <- env', k /= "TEXINPUTS"] - when (verbose && runNumber == 1) $ do + when (verbosity >= INFO && runNumber == 1) $ do putStrLn "[makePDF] temp dir:" putStrLn tmpDir' putStrLn "[makePDF] Command line:" @@ -257,12 +258,12 @@ runTeXProgram verbose program args runNumber numRuns tmpDir source = do B.readFile file' >>= B.putStr putStr "\n" (exit, out) <- pipeProcess (Just env'') program programArgs BL.empty - when verbose $ do + when (verbosity >= INFO) $ do putStrLn $ "[makePDF] Run #" ++ show runNumber B.hPutStr stdout out putStr "\n" if runNumber <= numRuns - then runTeXProgram verbose program args (runNumber + 1) numRuns tmpDir source + then runTeXProgram verbosity program args (runNumber + 1) numRuns tmpDir source else do let pdfFile = replaceDirectory (replaceExtension file ".pdf") tmpDir pdfExists <- doesFileExist pdfFile @@ -274,17 +275,17 @@ runTeXProgram verbose program args runNumber numRuns tmpDir source = do else return Nothing return (exit, out, pdf) -html2pdf :: Bool -- ^ Verbose output +html2pdf :: Verbosity -- ^ Verbosity level -> [String] -- ^ Args to wkhtmltopdf -> String -- ^ HTML5 source -> IO (Either ByteString ByteString) -html2pdf verbose args source = do +html2pdf verbosity args source = do file <- withTempFile "." "html2pdf.html" $ \fp _ -> return fp pdfFile <- withTempFile "." "html2pdf.pdf" $ \fp _ -> return fp UTF8.writeFile file source let programArgs = args ++ [file, pdfFile] env' <- getEnvironment - when verbose $ do + when (verbosity >= INFO) $ do putStrLn "[makePDF] Command line:" putStrLn $ "wkhtmltopdf" ++ " " ++ unwords (map show programArgs) putStr "\n" @@ -296,7 +297,7 @@ html2pdf verbose args source = do putStr "\n" (exit, out) <- pipeProcess (Just env') "wkhtmltopdf" programArgs BL.empty removeFile file - when verbose $ do + when (verbosity >= INFO) $ do B.hPutStr stdout out putStr "\n" pdfExists <- doesFileExist pdfFile @@ -314,11 +315,11 @@ html2pdf verbose args source = do (ExitSuccess, Nothing) -> Left "" (ExitSuccess, Just pdf) -> Right pdf -context2pdf :: Bool -- ^ Verbose output +context2pdf :: Verbosity -- ^ Verbosity level -> FilePath -- ^ temp directory for output -> String -- ^ ConTeXt source -> IO (Either ByteString ByteString) -context2pdf verbose tmpDir source = inDirectory tmpDir $ do +context2pdf verbosity tmpDir source = inDirectory tmpDir $ do let file = "input.tex" UTF8.writeFile file source #ifdef _WINDOWS @@ -334,7 +335,7 @@ context2pdf verbose tmpDir source = inDirectory tmpDir $ do $ lookup "TEXINPUTS" env' let env'' = ("TEXINPUTS", texinputs) : [(k,v) | (k,v) <- env', k /= "TEXINPUTS"] - when verbose $ do + when (verbosity >= INFO) $ do putStrLn "[makePDF] temp dir:" putStrLn tmpDir' putStrLn "[makePDF] Command line:" @@ -347,7 +348,7 @@ context2pdf verbose tmpDir source = inDirectory tmpDir $ do B.readFile file >>= B.putStr putStr "\n" (exit, out) <- pipeProcess (Just env'') "context" programArgs BL.empty - when verbose $ do + when (verbosity >= INFO) $ do B.hPutStr stdout out putStr "\n" let pdfFile = replaceExtension file ".pdf" diff --git a/src/Text/Pandoc/Readers/EPUB.hs b/src/Text/Pandoc/Readers/EPUB.hs index a76ed04ba..71a527f13 100644 --- a/src/Text/Pandoc/Readers/EPUB.hs +++ b/src/Text/Pandoc/Readers/EPUB.hs @@ -12,7 +12,7 @@ import Text.XML.Light import Text.Pandoc.Definition hiding (Attr) import Text.Pandoc.Readers.HTML (readHtml) import Text.Pandoc.Walk (walk, query) -import Text.Pandoc.Options ( ReaderOptions(..), readerTrace) +import Text.Pandoc.Options ( ReaderOptions(..), readerVerbosity, Verbosity(..)) import Text.Pandoc.Shared (escapeURI, collapseFilePath, addMetaField) import Network.URI (unEscapeString) import Text.Pandoc.MediaBag (MediaBag, insertMedia) @@ -71,7 +71,7 @@ archiveToEPUB os archive = do os' = os {readerParseRaw = True} parseSpineElem :: PandocMonad m => FilePath -> (FilePath, MimeType) -> m Pandoc parseSpineElem (normalise -> r) (normalise -> path, mime) = do - when (readerTrace os) (traceM path) + when (readerVerbosity os == DEBUG) (traceM path) doc <- mimeToReader mime r path let docSpan = B.doc $ B.para $ B.spanWith (takeFileName path, [], []) mempty return $ docSpan <> doc diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index b66a712e0..d602f7303 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -45,8 +45,8 @@ import qualified Text.Pandoc.Builder as B import Text.Pandoc.Builder (Blocks, Inlines, trimInlines, HasMeta(..)) import Text.Pandoc.Shared ( extractSpaces, renderTags', addMetaField , escapeURI, safeRead ) -import Text.Pandoc.Options (ReaderOptions(readerParseRaw, readerTrace) - , Extension (Ext_epub_html_exts, +import Text.Pandoc.Options (ReaderOptions(readerParseRaw, readerVerbosity), + Verbosity(..), Extension (Ext_epub_html_exts, Ext_native_divs, Ext_native_spans)) import Text.Pandoc.Parsing hiding ((<|>)) import Text.Pandoc.Walk @@ -160,7 +160,7 @@ pHead = pInTags "head" $ pTitle <|> pMetaTag <|> pBaseTag <|> (mempty <$ pAnyTag block :: PandocMonad m => TagParser m Blocks block = do - tr <- getOption readerTrace + tr <- (== DEBUG) <$> getOption readerVerbosity pos <- getPosition res <- choice [ eSection diff --git a/src/Text/Pandoc/Readers/Haddock.hs b/src/Text/Pandoc/Readers/Haddock.hs index 987342bf7..575d99c77 100644 --- a/src/Text/Pandoc/Readers/Haddock.hs +++ b/src/Text/Pandoc/Readers/Haddock.hs @@ -48,7 +48,7 @@ readHaddockEither opts = #else Right . B.doc . docHToBlocks . trace' . parseParas #endif - where trace' x = if readerTrace opts + where trace' x = if readerVerbosity opts == DEBUG then trace (show x) x else x diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 9137ae4b6..e0036f708 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -490,7 +490,7 @@ parseBlocks = mconcat <$> manyTill block eof block :: PandocMonad m => MarkdownParser m (F Blocks) block = do - tr <- getOption readerTrace + tr <- (== DEBUG) <$> getOption readerVerbosity pos <- getPosition res <- choice [ mempty <$ blanklines , codeBlockFenced diff --git a/src/Text/Pandoc/Readers/MediaWiki.hs b/src/Text/Pandoc/Readers/MediaWiki.hs index 5bdf0ca4e..38a9e3f4f 100644 --- a/src/Text/Pandoc/Readers/MediaWiki.hs +++ b/src/Text/Pandoc/Readers/MediaWiki.hs @@ -194,7 +194,7 @@ parseMediaWiki = do block :: PandocMonad m => MWParser m Blocks block = do - tr <- getOption readerTrace + tr <- (== DEBUG) <$> getOption readerVerbosity pos <- getPosition res <- mempty <$ skipMany1 blankline <|> table diff --git a/src/Text/Pandoc/Readers/TWiki.hs b/src/Text/Pandoc/Readers/TWiki.hs index 3e547e5f4..b54eec735 100644 --- a/src/Text/Pandoc/Readers/TWiki.hs +++ b/src/Text/Pandoc/Readers/TWiki.hs @@ -127,7 +127,7 @@ parseTWiki = do block :: TWParser B.Blocks block = do - tr <- getOption readerTrace + tr <- (== DEBUG) <$> getOption readerVerbosity pos <- getPosition res <- mempty <$ skipMany1 blankline <|> blockElements diff --git a/src/Text/Pandoc/Readers/Textile.hs b/src/Text/Pandoc/Readers/Textile.hs index 404913926..b3a1a208f 100644 --- a/src/Text/Pandoc/Readers/Textile.hs +++ b/src/Text/Pandoc/Readers/Textile.hs @@ -147,7 +147,7 @@ block :: PandocMonad m => ParserT [Char] ParserState m Blocks block = do res <- choice blockParsers "block" pos <- getPosition - tr <- getOption readerTrace + tr <- (== DEBUG) <$> getOption readerVerbosity when tr $ trace (printf "line %d: %s" (sourceLine pos) (take 60 $ show $ B.toList res)) (return ()) -- cgit v1.2.3 From 4e97efe857aa574d14566ef33e7402840c9ef684 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 22 Jan 2017 22:10:11 +0100 Subject: Class: Changes around logging. * Export getLog, setVerbosity * Add report to PandocMonad methods. * Redefine warning and getWarnings in terms of getLog and report. * Remove stWarnings from CommonState, add stLog and stVerbosity. --- src/Text/Pandoc/Class.hs | 72 ++++++++++++++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 27 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index da9b837f7..1c21c7b7b 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -43,6 +43,8 @@ module Text.Pandoc.Class ( PandocMonad(..) , warning , warningWithPos , getWarnings + , getLog + , setVerbosity , getMediaBag , setMediaBag , insertMedia @@ -70,6 +72,7 @@ import qualified Text.Pandoc.Shared as IO ( readDataFile , warn , openURL ) import Text.Pandoc.Compat.Time (UTCTime) +import Text.Pandoc.Options (Verbosity(..)) import Text.Pandoc.Parsing (ParserT, SourcePos) import qualified Text.Pandoc.Compat.Time as IO (getCurrentTime) import Text.Pandoc.MIME (MimeType, getMimeType) @@ -128,20 +131,55 @@ class (Functor m, Applicative m, Monad m, MonadError PandocError m) modifyCommonState :: (CommonState -> CommonState) -> m () modifyCommonState f = getCommonState >>= putCommonState . f + -- Can be overridden when you want log to be written to + -- stderr in a streaming fashion + report :: Verbosity -> String -> m () + report level msg = do + verbosity <- getsCommonState stVerbosity + when (level >= verbosity) $ + modifyCommonState $ \st -> st{ stLog = (level, msg) : stLog st } + -- Functions defined for all PandocMonad instances +setVerbosity :: PandocMonad m => Verbosity -> m () +setVerbosity verbosity = + modifyCommonState $ \st -> st{ stVerbosity = verbosity } + +getLog :: PandocMonad m => m [(Verbosity, String)] +getLog = getsCommonState stLog + warning :: PandocMonad m => String -> m () -warning msg = modifyCommonState $ \st -> st{stWarnings = msg : stWarnings st} +warning msg = report WARNING msg +warningWithPos :: PandocMonad m + => SourcePos + -> String + -> ParserT s st m () +warningWithPos pos msg = lift $ warning $ msg ++ " " ++ show pos + +-- TODO get rid of this? getWarnings :: PandocMonad m => m [String] -getWarnings = getsCommonState stWarnings +getWarnings = do + logs <- getLog + return [s | (WARNING, s) <- logs] setMediaBag :: PandocMonad m => MediaBag -> m () setMediaBag mb = modifyCommonState $ \st -> st{stDeferredMediaBag = DeferredMediaBag mb mempty} getMediaBag :: PandocMonad m => m MediaBag -getMediaBag = fetchDeferredMedia >> (dropDeferredMedia <$> getsCommonState stDeferredMediaBag) +getMediaBag = do + fetchDeferredMedia + DeferredMediaBag mb' _ <- getsCommonState stDeferredMediaBag + return mb' + +fetchDeferredMedia :: PandocMonad m => m () +fetchDeferredMedia = do + (DeferredMediaBag mb defMedia) <- getsCommonState stDeferredMediaBag + fetchedMedia <- catMaybes <$> mapM fetchMediaItem defMedia + setMediaBag $ foldr + (\(fp, bs, mbMime) mb' -> MB.insertMedia fp mbMime (BL.fromStrict bs) mb') + mb fetchedMedia insertMedia :: PandocMonad m => FilePath -> Maybe MimeType -> BL.ByteString -> m () insertMedia fp mime bs = do @@ -170,12 +208,6 @@ getZonedTime = do tz <- getCurrentTimeZone return $ utcToZonedTime tz t -warningWithPos :: PandocMonad m - => SourcePos - -> String - -> ParserT s st m () -warningWithPos pos msg = lift $ warning $ msg ++ " " ++ show pos - -- newtype DeferredMediaPath = DeferredMediaPath {unDefer :: String} @@ -189,7 +221,6 @@ instance Monoid DeferredMediaBag where mappend (DeferredMediaBag mb lst) (DeferredMediaBag mb' lst') = DeferredMediaBag (mb <> mb') (lst <> lst') - -- the internal function for downloading individual items. We want to -- catch errors and return a Nothing with a warning, so we can -- continue without erroring out. @@ -203,32 +234,19 @@ fetchMediaItem dfp = (const $ do warning ("Couldn't access media at " ++ unDefer dfp) return Nothing) -fetchDeferredMedia' :: PandocMonad m => m MediaBag -fetchDeferredMedia' = do - (DeferredMediaBag mb defMedia) <- getsCommonState stDeferredMediaBag - fetchedMedia <- catMaybes <$> mapM fetchMediaItem defMedia - return $ foldr - (\(fp, bs, mbMime) mb' -> MB.insertMedia fp mbMime (BL.fromStrict bs) mb') - mb fetchedMedia - -fetchDeferredMedia :: PandocMonad m => m () -fetchDeferredMedia = fetchDeferredMedia' >>= setMediaBag - -dropDeferredMedia :: DeferredMediaBag -> MediaBag -dropDeferredMedia (DeferredMediaBag mb _) = mb - - -data CommonState = CommonState { stWarnings :: [String] +data CommonState = CommonState { stLog :: [(Verbosity, String)] , stDeferredMediaBag :: DeferredMediaBag , stInputFiles :: Maybe [FilePath] , stOutputFile :: Maybe FilePath + , stVerbosity :: Verbosity } instance Default CommonState where - def = CommonState { stWarnings = [] + def = CommonState { stLog = [] , stDeferredMediaBag = mempty , stInputFiles = Nothing , stOutputFile = Nothing + , stVerbosity = WARNING } runIO :: PandocIO a -> IO (Either PandocError a) -- cgit v1.2.3 From bc7e846da61bdcd3ce6ef49e9d3e6bf4a0bd1a5d Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 22 Jan 2017 23:49:05 +0100 Subject: More logging-related changes. Class: * Removed getWarnings, withWarningsToStderr * Added report * Added logOutput to PandocMonad * Make logOutput streaming in PandocIO monad * Properly reverse getLog output Readers: * Replaced use of trace with report DEBUG. TWiki Reader: Put everything inside PandocMonad m. API changes. --- pandoc.hs | 19 +++-- src/Text/Pandoc/Class.hs | 45 ++++++----- src/Text/Pandoc/Readers/HTML.hs | 14 ++-- src/Text/Pandoc/Readers/Markdown.hs | 10 +-- src/Text/Pandoc/Readers/MediaWiki.hs | 9 +-- src/Text/Pandoc/Readers/TWiki.hs | 149 ++++++++++++++++++----------------- src/Text/Pandoc/Readers/Textile.hs | 11 +-- 7 files changed, 124 insertions(+), 133 deletions(-) (limited to 'src/Text') diff --git a/pandoc.hs b/pandoc.hs index 5d1c7d068..c863f324f 100644 --- a/pandoc.hs +++ b/pandoc.hs @@ -35,8 +35,7 @@ import Text.Pandoc.Builder (setMeta) import Text.Pandoc.PDF (makePDF) import Text.Pandoc.Walk (walk) import Text.Pandoc.Shared ( tabFilter, readDataFileUTF8, readDataFile, - safeRead, headerShift, err, warn, - openURL ) + safeRead, headerShift, err, openURL ) import Text.Pandoc.MediaBag ( mediaDirectory, extractMediaBag, MediaBag ) import Text.Pandoc.XML ( toEntities ) import Text.Pandoc.SelfContained ( makeSelfContained ) @@ -75,7 +74,7 @@ import System.Posix.Terminal (queryTerminal) import System.Posix.IO (stdOutput) #endif import Control.Monad.Trans -import Text.Pandoc.Class (withMediaBag, PandocIO, getWarnings) +import Text.Pandoc.Class (withMediaBag, PandocIO, getLog, setVerbosity) main :: IO () main = do @@ -354,14 +353,14 @@ convertWithOpts opts args = do let runIO' :: PandocIO a -> IO a runIO' f = do - (res, warnings) <- runIOorExplode $ do + (res, reports) <- runIOorExplode $ do + setVerbosity verbosity x <- f - ws <- getWarnings - return (x, ws) - when (not (null warnings)) $ do - -- TODO make these streaming - when (verbosity >= WARNING) $ mapM_ warn warnings - when failIfWarnings $ + rs <- getLog + return (x, rs) + let isWarning (WARNING, _) = True + isWarning _ = False + when (failIfWarnings && any isWarning reports) $ err 3 "Failing because there were warnings." return res diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 1c21c7b7b..79c7316f1 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -42,7 +42,7 @@ module Text.Pandoc.Class ( PandocMonad(..) , getZonedTime , warning , warningWithPos - , getWarnings + , report , getLog , setVerbosity , getMediaBag @@ -59,7 +59,6 @@ module Text.Pandoc.Class ( PandocMonad(..) , runIOorExplode , runPure , withMediaBag - , withWarningsToStderr ) where import Prelude hiding (readFile) @@ -69,8 +68,8 @@ import Codec.Archive.Zip (Archive, fromArchive, emptyArchive) import Data.Unique (hashUnique) import qualified Data.Unique as IO (newUnique) import qualified Text.Pandoc.Shared as IO ( readDataFile - , warn , openURL ) +import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Compat.Time (UTCTime) import Text.Pandoc.Options (Verbosity(..)) import Text.Pandoc.Parsing (ParserT, SourcePos) @@ -102,10 +101,12 @@ import Control.Monad.RWS (RWST) import Data.Word (Word8) import Data.Default import System.IO.Error +import System.IO (stderr) import qualified Data.Map as M import Text.Pandoc.Error import Data.Monoid import Data.Maybe (catMaybes) +import Text.Printf (printf) class (Functor m, Applicative m, Monad m, MonadError PandocError m) => PandocMonad m where @@ -131,13 +132,7 @@ class (Functor m, Applicative m, Monad m, MonadError PandocError m) modifyCommonState :: (CommonState -> CommonState) -> m () modifyCommonState f = getCommonState >>= putCommonState . f - -- Can be overridden when you want log to be written to - -- stderr in a streaming fashion - report :: Verbosity -> String -> m () - report level msg = do - verbosity <- getsCommonState stVerbosity - when (level >= verbosity) $ - modifyCommonState $ \st -> st{ stLog = (level, msg) : stLog st } + logOutput :: Verbosity -> String -> m () -- Functions defined for all PandocMonad instances @@ -146,7 +141,7 @@ setVerbosity verbosity = modifyCommonState $ \st -> st{ stVerbosity = verbosity } getLog :: PandocMonad m => m [(Verbosity, String)] -getLog = getsCommonState stLog +getLog = reverse <$> getsCommonState stLog warning :: PandocMonad m => String -> m () warning msg = report WARNING msg @@ -157,11 +152,13 @@ warningWithPos :: PandocMonad m -> ParserT s st m () warningWithPos pos msg = lift $ warning $ msg ++ " " ++ show pos --- TODO get rid of this? -getWarnings :: PandocMonad m => m [String] -getWarnings = do - logs <- getLog - return [s | (WARNING, s) <- logs] +report :: PandocMonad m => Verbosity -> String -> m () +report level msg = do + verbosity <- getsCommonState stVerbosity + when (level <= verbosity) $ do + logOutput verbosity msg + unless (level == DEBUG) $ + modifyCommonState $ \st -> st{ stLog = (level, msg) : stLog st } setMediaBag :: PandocMonad m => MediaBag -> m () setMediaBag mb = modifyCommonState $ @@ -255,12 +252,6 @@ runIO ma = flip evalStateT def $ runExceptT $ unPandocIO ma withMediaBag :: PandocMonad m => m a -> m (a, MediaBag) withMediaBag ma = ((,)) <$> ma <*> getMediaBag -withWarningsToStderr :: PandocIO a -> PandocIO a -withWarningsToStderr f = do - x <- f - getWarnings >>= mapM_ IO.warn - return x - runIOorExplode :: PandocIO a -> IO a runIOorExplode ma = runIO ma >>= handleError @@ -309,7 +300,8 @@ instance PandocMonad PandocIO where Left _ -> throwError $ PandocFileReadError fp getCommonState = PandocIO $ lift get putCommonState x = PandocIO $ lift $ put x - + logOutput level msg = + liftIO $ UTF8.hPutStrLn stderr $ printf "%-7s %s" (show level) msg -- | Specialized version of parseURIReference that disallows -- single-letter schemes. Reason: these are usually windows absolute @@ -508,6 +500,8 @@ instance PandocMonad PandocPure where getCommonState = PandocPure $ lift $ get putCommonState x = PandocPure $ lift $ put x + logOutput _level _msg = return () + instance PandocMonad m => PandocMonad (ParserT s st m) where lookupEnv = lift . lookupEnv getCurrentTime = lift getCurrentTime @@ -522,6 +516,7 @@ instance PandocMonad m => PandocMonad (ParserT s st m) where getModificationTime = lift . getModificationTime getCommonState = lift getCommonState putCommonState = lift . putCommonState + logOutput lvl = lift . logOutput lvl instance PandocMonad m => PandocMonad (ReaderT r m) where lookupEnv = lift . lookupEnv @@ -537,6 +532,7 @@ instance PandocMonad m => PandocMonad (ReaderT r m) where getModificationTime = lift . getModificationTime getCommonState = lift getCommonState putCommonState = lift . putCommonState + logOutput lvl = lift . logOutput lvl instance (PandocMonad m, Monoid w) => PandocMonad (WriterT w m) where lookupEnv = lift . lookupEnv @@ -552,6 +548,7 @@ instance (PandocMonad m, Monoid w) => PandocMonad (WriterT w m) where getModificationTime = lift . getModificationTime getCommonState = lift getCommonState putCommonState = lift . putCommonState + logOutput lvl = lift . report lvl instance (PandocMonad m, Monoid w) => PandocMonad (RWST r w st m) where lookupEnv = lift . lookupEnv @@ -567,6 +564,7 @@ instance (PandocMonad m, Monoid w) => PandocMonad (RWST r w st m) where getModificationTime = lift . getModificationTime getCommonState = lift getCommonState putCommonState = lift . putCommonState + logOutput lvl = lift . logOutput lvl instance PandocMonad m => PandocMonad (StateT st m) where lookupEnv = lift . lookupEnv @@ -582,4 +580,5 @@ instance PandocMonad m => PandocMonad (StateT st m) where getModificationTime = lift . getModificationTime getCommonState = lift getCommonState putCommonState = lift . putCommonState + logOutput lvl = lift . logOutput lvl diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index d602f7303..0bb837ba9 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -45,7 +45,7 @@ import qualified Text.Pandoc.Builder as B import Text.Pandoc.Builder (Blocks, Inlines, trimInlines, HasMeta(..)) import Text.Pandoc.Shared ( extractSpaces, renderTags', addMetaField , escapeURI, safeRead ) -import Text.Pandoc.Options (ReaderOptions(readerParseRaw, readerVerbosity), +import Text.Pandoc.Options (ReaderOptions(readerParseRaw), Verbosity(..), Extension (Ext_epub_html_exts, Ext_native_divs, Ext_native_spans)) import Text.Pandoc.Parsing hiding ((<|>)) @@ -54,12 +54,11 @@ import qualified Data.Map as M import Data.Maybe ( fromMaybe, isJust) import Data.List ( intercalate, isInfixOf, isPrefixOf ) import Data.Char ( isDigit ) -import Control.Monad ( guard, when, mzero, void, unless ) +import Control.Monad ( guard, mzero, void, unless ) import Control.Arrow ((***)) import Control.Applicative ( (<|>) ) import Data.Monoid (First (..)) import Text.Printf (printf) -import Debug.Trace (trace) import Text.TeXMath (readMathML, writeTeX) import Data.Default (Default (..), def) import Control.Monad.Reader (ask, asks, local, ReaderT, runReaderT, lift) @@ -69,7 +68,7 @@ import Data.Monoid ((<>)) import Text.Parsec.Error import qualified Data.Set as Set import Text.Pandoc.Error -import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Class (PandocMonad, report) import Control.Monad.Except (throwError) @@ -96,8 +95,6 @@ readHtml opts inp = do case result of Right doc -> return doc Left err -> throwError $ PandocParseError $ getError err - - where replaceNotes :: PandocMonad m => [Block] -> TagParser m [Block] replaceNotes = walkM replaceNotes' @@ -160,7 +157,6 @@ pHead = pInTags "head" $ pTitle <|> pMetaTag <|> pBaseTag <|> (mempty <$ pAnyTag block :: PandocMonad m => TagParser m Blocks block = do - tr <- (== DEBUG) <$> getOption readerVerbosity pos <- getPosition res <- choice [ eSection @@ -181,8 +177,8 @@ block = do , pPlain , pRawHtmlBlock ] - when tr $ trace (printf "line %d: %s" (sourceLine pos) - (take 60 $ show $ B.toList res)) (return ()) + report DEBUG $ printf "line %d: %s" + (sourceLine pos) (take 60 $ show $ B.toList res) return res namespaces :: PandocMonad m => [(String, TagParser m Inlines)] diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index e0036f708..5052f52bf 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -62,11 +62,10 @@ import Control.Monad import System.FilePath (takeExtension, addExtension) import Text.HTML.TagSoup import Text.Printf (printf) -import Debug.Trace (trace) import Data.Monoid ((<>)) import Control.Monad.Trans (lift) import Control.Monad.Except (throwError, catchError) -import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Class (PandocMonad, report) import qualified Text.Pandoc.Class as P type MarkdownParser m = ParserT [Char] ParserState m @@ -490,7 +489,6 @@ parseBlocks = mconcat <$> manyTill block eof block :: PandocMonad m => MarkdownParser m (F Blocks) block = do - tr <- (== DEBUG) <$> getOption readerVerbosity pos <- getPosition res <- choice [ mempty <$ blanklines , codeBlockFenced @@ -517,10 +515,8 @@ block = do , para , plain ] "block" - when tr $ do - st <- getState - trace (printf "line %d: %s" (sourceLine pos) - (take 60 $ show $ B.toList $ runF res st)) (return ()) + report DEBUG $ printf "line %d: %s" (sourceLine pos) + (take 60 $ show $ B.toList $ runF res defaultParserState) return res -- diff --git a/src/Text/Pandoc/Readers/MediaWiki.hs b/src/Text/Pandoc/Readers/MediaWiki.hs index 38a9e3f4f..b81d0f3e4 100644 --- a/src/Text/Pandoc/Readers/MediaWiki.hs +++ b/src/Text/Pandoc/Readers/MediaWiki.hs @@ -56,9 +56,8 @@ import qualified Data.Set as Set import Data.Char (isDigit, isSpace) import Data.Maybe (fromMaybe) import Text.Printf (printf) -import Debug.Trace (trace) import Control.Monad.Except (throwError) -import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Class (PandocMonad, report) -- | Read mediawiki from an input string and return a Pandoc document. readMediaWiki :: PandocMonad m @@ -194,7 +193,6 @@ parseMediaWiki = do block :: PandocMonad m => MWParser m Blocks block = do - tr <- (== DEBUG) <$> getOption readerVerbosity pos <- getPosition res <- mempty <$ skipMany1 blankline <|> table @@ -208,9 +206,8 @@ block = do <|> blockTag <|> (B.rawBlock "mediawiki" <$> template) <|> para - when tr $ - trace (printf "line %d: %s" (sourceLine pos) - (take 60 $ show $ B.toList res)) (return ()) + report DEBUG $ printf "line %d: %s" (sourceLine pos) + (take 60 $ show $ B.toList res) return res para :: PandocMonad m => MWParser m Blocks diff --git a/src/Text/Pandoc/Readers/TWiki.hs b/src/Text/Pandoc/Readers/TWiki.hs index b54eec735..1a827bcd9 100644 --- a/src/Text/Pandoc/Readers/TWiki.hs +++ b/src/Text/Pandoc/Readers/TWiki.hs @@ -39,39 +39,38 @@ import Text.Pandoc.Parsing hiding (enclosed, macro, nested) import Text.Pandoc.Readers.HTML (htmlTag, isCommentTag) import Control.Monad import Text.Printf (printf) -import Debug.Trace (trace) import Text.Pandoc.XML (fromEntities) import Data.Maybe (fromMaybe) import Text.HTML.TagSoup import Data.Char (isAlphaNum) import qualified Data.Foldable as F import Control.Monad.Except (throwError) -import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Class (PandocMonad, report) -- | Read twiki from an input string and return a Pandoc document. readTWiki :: PandocMonad m => ReaderOptions -> String -> m Pandoc -readTWiki opts s = - case (readWith parseTWiki) def{ stateOptions = opts } (s ++ "\n\n") of +readTWiki opts s = do + res <- readWithM parseTWiki def{ stateOptions = opts } (s ++ "\n\n") + case res of Left e -> throwError e Right d -> return d - -type TWParser = Parser [Char] ParserState +type TWParser = ParserT [Char] ParserState -- -- utility functions -- -tryMsg :: String -> TWParser a -> TWParser a +tryMsg :: String -> TWParser m a -> TWParser m a tryMsg msg p = try p msg -skip :: TWParser a -> TWParser () +skip :: TWParser m a -> TWParser m () skip parser = parser >> return () -nested :: TWParser a -> TWParser a +nested :: PandocMonad m => TWParser m a -> TWParser m a nested p = do nestlevel <- stateMaxNestingLevel <$> getState guard $ nestlevel > 0 @@ -80,7 +79,7 @@ nested p = do updateState $ \st -> st{ stateMaxNestingLevel = nestlevel } return res -htmlElement :: String -> TWParser (Attr, String) +htmlElement :: PandocMonad m => String -> TWParser m (Attr, String) htmlElement tag = tryMsg tag $ do (TagOpen _ attr, _) <- htmlTag (~== TagOpen tag []) content <- manyTill anyChar (endtag <|> endofinput) @@ -97,7 +96,8 @@ htmlAttrToPandoc attrs = (ident, classes, keyvals) classes = maybe [] words $ lookup "class" attrs keyvals = [(k,v) | (k,v) <- attrs, k /= "id" && k /= "class"] -parseHtmlContentWithAttrs :: String -> TWParser a -> TWParser (Attr, [a]) +parseHtmlContentWithAttrs :: PandocMonad m + => String -> TWParser m a -> TWParser m (Attr, [a]) parseHtmlContentWithAttrs tag parser = do (attr, content) <- htmlElement tag parsedContent <- try $ parseContent content @@ -106,14 +106,14 @@ parseHtmlContentWithAttrs tag parser = do parseContent = parseFromString $ nested $ manyTill parser endOfContent endOfContent = try $ skipMany blankline >> skipSpaces >> eof -parseHtmlContent :: String -> TWParser a -> TWParser [a] +parseHtmlContent :: PandocMonad m => String -> TWParser m a -> TWParser m [a] parseHtmlContent tag p = parseHtmlContentWithAttrs tag p >>= return . snd -- -- main parser -- -parseTWiki :: TWParser Pandoc +parseTWiki :: PandocMonad m => TWParser m Pandoc parseTWiki = do bs <- mconcat <$> many block spaces @@ -125,20 +125,18 @@ parseTWiki = do -- block parsers -- -block :: TWParser B.Blocks +block :: PandocMonad m => TWParser m B.Blocks block = do - tr <- (== DEBUG) <$> getOption readerVerbosity pos <- getPosition res <- mempty <$ skipMany1 blankline <|> blockElements <|> para skipMany blankline - when tr $ - trace (printf "line %d: %s" (sourceLine pos) - (take 60 $ show $ B.toList res)) (return ()) + report DEBUG $ printf "line %d: %s" (sourceLine pos) + (take 60 $ show $ B.toList res) return res -blockElements :: TWParser B.Blocks +blockElements :: PandocMonad m => TWParser m B.Blocks blockElements = choice [ separator , header , verbatim @@ -149,10 +147,10 @@ blockElements = choice [ separator , noautolink ] -separator :: TWParser B.Blocks +separator :: PandocMonad m => TWParser m B.Blocks separator = tryMsg "separator" $ string "---" >> newline >> return B.horizontalRule -header :: TWParser B.Blocks +header :: PandocMonad m => TWParser m B.Blocks header = tryMsg "header" $ do string "---" level <- many1 (char '+') >>= return . length @@ -163,43 +161,45 @@ header = tryMsg "header" $ do attr <- registerHeader ("", classes, []) content return $ B.headerWith attr level $ content -verbatim :: TWParser B.Blocks +verbatim :: PandocMonad m => TWParser m B.Blocks verbatim = (htmlElement "verbatim" <|> htmlElement "pre") >>= return . (uncurry B.codeBlockWith) -literal :: TWParser B.Blocks +literal :: PandocMonad m => TWParser m B.Blocks literal = htmlElement "literal" >>= return . rawBlock where format (_, _, kvs) = fromMaybe "html" $ lookup "format" kvs rawBlock (attrs, content) = B.rawBlock (format attrs) content -list :: String -> TWParser B.Blocks +list :: PandocMonad m => String -> TWParser m B.Blocks list prefix = choice [ bulletList prefix , orderedList prefix , definitionList prefix] -definitionList :: String -> TWParser B.Blocks +definitionList :: PandocMonad m => String -> TWParser m B.Blocks definitionList prefix = tryMsg "definitionList" $ do indent <- lookAhead $ string prefix *> (many1 $ string " ") <* string "$ " elements <- many $ parseDefinitionListItem (prefix ++ concat indent) return $ B.definitionList elements where - parseDefinitionListItem :: String -> TWParser (B.Inlines, [B.Blocks]) + parseDefinitionListItem :: PandocMonad m + => String -> TWParser m (B.Inlines, [B.Blocks]) parseDefinitionListItem indent = do string (indent ++ "$ ") >> skipSpaces term <- many1Till inline $ string ": " line <- listItemLine indent $ string "$ " return $ (mconcat term, [line]) -bulletList :: String -> TWParser B.Blocks +bulletList :: PandocMonad m => String -> TWParser m B.Blocks bulletList prefix = tryMsg "bulletList" $ parseList prefix (char '*') (char ' ') -orderedList :: String -> TWParser B.Blocks +orderedList :: PandocMonad m => String -> TWParser m B.Blocks orderedList prefix = tryMsg "orderedList" $ parseList prefix (oneOf "1iIaA") (string ". ") -parseList :: String -> TWParser Char -> TWParser a -> TWParser B.Blocks +parseList :: PandocMonad m + => String -> TWParser m Char -> TWParser m a -> TWParser m B.Blocks parseList prefix marker delim = do (indent, style) <- lookAhead $ string prefix *> listStyle <* delim blocks <- many $ parseListItem (prefix ++ indent) (char style <* delim) @@ -216,10 +216,12 @@ parseList prefix marker delim = do style <- marker return (concat indent, style) -parseListItem :: Show a => String -> TWParser a -> TWParser B.Blocks +parseListItem :: (PandocMonad m, Show a) + => String -> TWParser m a -> TWParser m B.Blocks parseListItem prefix marker = string prefix >> marker >> listItemLine prefix marker -listItemLine :: Show a => String -> TWParser a -> TWParser B.Blocks +listItemLine :: (PandocMonad m, Show a) + => String -> TWParser m a -> TWParser m B.Blocks listItemLine prefix marker = lineContent >>= parseContent >>= return . mconcat where lineContent = do @@ -236,7 +238,7 @@ listItemLine prefix marker = lineContent >>= parseContent >>= return . mconcat lastNewline = try $ char '\n' <* eof newlineBeforeNestedList = try $ char '\n' <* lookAhead nestedList -table :: TWParser B.Blocks +table :: PandocMonad m => TWParser m B.Blocks table = try $ do tableHead <- optionMaybe $ many1Till tableParseHeader newline >>= return . unzip rows <- many1 tableParseRow @@ -248,7 +250,7 @@ table = try $ do columns rows = replicate (columCount rows) mempty columCount rows = length $ head rows -tableParseHeader :: TWParser ((Alignment, Double), B.Blocks) +tableParseHeader :: PandocMonad m => TWParser m ((Alignment, Double), B.Blocks) tableParseHeader = try $ do char '|' leftSpaces <- many spaceChar >>= return . length @@ -264,27 +266,27 @@ tableParseHeader = try $ do | left > right = (AlignRight, 0) | otherwise = (AlignLeft, 0) -tableParseRow :: TWParser [B.Blocks] +tableParseRow :: PandocMonad m => TWParser m [B.Blocks] tableParseRow = many1Till tableParseColumn newline -tableParseColumn :: TWParser B.Blocks +tableParseColumn :: PandocMonad m => TWParser m B.Blocks tableParseColumn = char '|' *> skipSpaces *> tableColumnContent (skipSpaces >> char '|') <* skipSpaces <* optional tableEndOfRow -tableEndOfRow :: TWParser Char +tableEndOfRow :: PandocMonad m => TWParser m Char tableEndOfRow = lookAhead (try $ char '|' >> char '\n') >> char '|' -tableColumnContent :: TWParser a -> TWParser B.Blocks +tableColumnContent :: PandocMonad m => TWParser m a -> TWParser m B.Blocks tableColumnContent end = manyTill content (lookAhead $ try end) >>= return . B.plain . mconcat where content = continuation <|> inline continuation = try $ char '\\' >> newline >> return mempty -blockQuote :: TWParser B.Blocks +blockQuote :: PandocMonad m => TWParser m B.Blocks blockQuote = parseHtmlContent "blockquote" block >>= return . B.blockQuote . mconcat -noautolink :: TWParser B.Blocks +noautolink :: PandocMonad m => TWParser m B.Blocks noautolink = do (_, content) <- htmlElement "noautolink" st <- getState @@ -295,7 +297,7 @@ noautolink = do where parseContent = parseFromString $ many $ block -para :: TWParser B.Blocks +para :: PandocMonad m => TWParser m B.Blocks para = many1Till inline endOfParaElement >>= return . result . mconcat where endOfParaElement = lookAhead $ endOfInput <|> endOfPara <|> newBlockElement @@ -311,7 +313,7 @@ para = many1Till inline endOfParaElement >>= return . result . mconcat -- inline parsers -- -inline :: TWParser B.Inlines +inline :: PandocMonad m => TWParser m B.Inlines inline = choice [ whitespace , br , macro @@ -332,36 +334,39 @@ inline = choice [ whitespace , symbol ] "inline" -whitespace :: TWParser B.Inlines +whitespace :: PandocMonad m => TWParser m B.Inlines whitespace = (lb <|> regsp) >>= return where lb = try $ skipMany spaceChar >> linebreak >> return B.space regsp = try $ skipMany1 spaceChar >> return B.space -br :: TWParser B.Inlines +br :: PandocMonad m => TWParser m B.Inlines br = try $ string "%BR%" >> return B.linebreak -linebreak :: TWParser B.Inlines +linebreak :: PandocMonad m => TWParser m B.Inlines linebreak = newline >> notFollowedBy newline >> (lastNewline <|> innerNewline) where lastNewline = eof >> return mempty innerNewline = return B.space -between :: (Monoid c) => TWParser a -> TWParser b -> (TWParser b -> TWParser c) -> TWParser c +between :: (Monoid c, PandocMonad m) + => TWParser m a -> TWParser m b -> (TWParser m b -> TWParser m c) + -> TWParser m c between start end p = mconcat <$> try (start >> notFollowedBy whitespace >> many1Till (p end) end) -enclosed :: (Monoid b) => TWParser a -> (TWParser a -> TWParser b) -> TWParser b +enclosed :: (Monoid b, PandocMonad m) + => TWParser m a -> (TWParser m a -> TWParser m b) -> TWParser m b enclosed sep p = between sep (try $ sep <* endMarker) p where endMarker = lookAhead $ skip endSpace <|> skip (oneOf ".,!?:)|") <|> eof endSpace = (spaceChar <|> newline) >> return B.space -macro :: TWParser B.Inlines +macro :: PandocMonad m => TWParser m B.Inlines macro = macroWithParameters <|> withoutParameters where withoutParameters = enclosed (char '%') (\_ -> macroName) >>= return . emptySpan emptySpan name = buildSpan name [] mempty -macroWithParameters :: TWParser B.Inlines +macroWithParameters :: PandocMonad m => TWParser m B.Inlines macroWithParameters = try $ do char '%' name <- macroName @@ -376,13 +381,13 @@ buildSpan className kvs = B.spanWith attrs additionalClasses = maybe [] words $ lookup "class" kvs kvsWithoutClasses = [(k,v) | (k,v) <- kvs, k /= "class"] -macroName :: TWParser String +macroName :: PandocMonad m => TWParser m String macroName = do first <- letter rest <- many $ alphaNum <|> char '_' return (first:rest) -attributes :: TWParser (String, [(String, String)]) +attributes :: PandocMonad m => TWParser m (String, [(String, String)]) attributes = char '{' *> spnl *> many (attribute <* spnl) <* char '}' >>= return . foldr (either mkContent mkKvs) ([], []) where @@ -391,7 +396,7 @@ attributes = char '{' *> spnl *> many (attribute <* spnl) <* char '}' >>= mkContent c (rest, kvs) = (c ++ " " ++ rest, kvs) mkKvs kv (cont, rest) = (cont, (kv : rest)) -attribute :: TWParser (Either String (String, String)) +attribute :: PandocMonad m => TWParser m (Either String (String, String)) attribute = withKey <|> withoutKey where withKey = try $ do @@ -405,49 +410,51 @@ attribute = withKey <|> withoutKey | allowSpaces == True = many1 $ noneOf "}" | otherwise = many1 $ noneOf " }" -nestedInlines :: Show a => TWParser a -> TWParser B.Inlines +nestedInlines :: (Show a, PandocMonad m) + => TWParser m a -> TWParser m B.Inlines nestedInlines end = innerSpace <|> nestedInline where innerSpace = try $ whitespace <* (notFollowedBy end) nestedInline = notFollowedBy whitespace >> nested inline -strong :: TWParser B.Inlines +strong :: PandocMonad m => TWParser m B.Inlines strong = try $ enclosed (char '*') nestedInlines >>= return . B.strong -strongHtml :: TWParser B.Inlines +strongHtml :: PandocMonad m => TWParser m B.Inlines strongHtml = (parseHtmlContent "strong" inline <|> parseHtmlContent "b" inline) >>= return . B.strong . mconcat -strongAndEmph :: TWParser B.Inlines +strongAndEmph :: PandocMonad m => TWParser m B.Inlines strongAndEmph = try $ enclosed (string "__") nestedInlines >>= return . B.emph . B.strong -emph :: TWParser B.Inlines +emph :: PandocMonad m => TWParser m B.Inlines emph = try $ enclosed (char '_') nestedInlines >>= return . B.emph -emphHtml :: TWParser B.Inlines +emphHtml :: PandocMonad m => TWParser m B.Inlines emphHtml = (parseHtmlContent "em" inline <|> parseHtmlContent "i" inline) >>= return . B.emph . mconcat -nestedString :: Show a => TWParser a -> TWParser String +nestedString :: (Show a, PandocMonad m) + => TWParser m a -> TWParser m String nestedString end = innerSpace <|> (count 1 nonspaceChar) where innerSpace = try $ many1 spaceChar <* notFollowedBy end -boldCode :: TWParser B.Inlines +boldCode :: PandocMonad m => TWParser m B.Inlines boldCode = try $ enclosed (string "==") nestedString >>= return . B.strong . B.code . fromEntities -htmlComment :: TWParser B.Inlines +htmlComment :: PandocMonad m => TWParser m B.Inlines htmlComment = htmlTag isCommentTag >> return mempty -code :: TWParser B.Inlines +code :: PandocMonad m => TWParser m B.Inlines code = try $ enclosed (char '=') nestedString >>= return . B.code . fromEntities -codeHtml :: TWParser B.Inlines +codeHtml :: PandocMonad m => TWParser m B.Inlines codeHtml = do (attrs, content) <- parseHtmlContentWithAttrs "code" anyChar return $ B.codeWith attrs $ fromEntities content -autoLink :: TWParser B.Inlines +autoLink :: PandocMonad m => TWParser m B.Inlines autoLink = try $ do state <- getState guard $ stateAllowLinks state @@ -461,20 +468,20 @@ autoLink = try $ do | c == '/' = True | otherwise = isAlphaNum c -str :: TWParser B.Inlines +str :: PandocMonad m => TWParser m B.Inlines str = (many1 alphaNum <|> count 1 characterReference) >>= return . B.str -nop :: TWParser B.Inlines +nop :: PandocMonad m => TWParser m B.Inlines nop = try $ (skip exclamation <|> skip nopTag) >> followContent where exclamation = char '!' nopTag = stringAnyCase "" followContent = many1 nonspaceChar >>= return . B.str . fromEntities -symbol :: TWParser B.Inlines +symbol :: PandocMonad m => TWParser m B.Inlines symbol = count 1 nonspaceChar >>= return . B.str -smart :: TWParser B.Inlines +smart :: PandocMonad m => TWParser m B.Inlines smart = do guardEnabled Ext_smart doubleQuoted <|> singleQuoted <|> @@ -483,14 +490,14 @@ smart = do , ellipses ] -singleQuoted :: TWParser B.Inlines +singleQuoted :: PandocMonad m => TWParser m B.Inlines singleQuoted = try $ do singleQuoteStart withQuoteContext InSingleQuote $ many1Till inline singleQuoteEnd >>= (return . B.singleQuoted . B.trimInlines . mconcat) -doubleQuoted :: TWParser B.Inlines +doubleQuoted :: PandocMonad m => TWParser m B.Inlines doubleQuoted = try $ do doubleQuoteStart contents <- mconcat <$> many (try $ notFollowedBy doubleQuoteEnd >> inline) @@ -498,7 +505,7 @@ doubleQuoted = try $ do return (B.doubleQuoted $ B.trimInlines contents)) <|> (return $ (B.str "\8220") B.<> contents) -link :: TWParser B.Inlines +link :: PandocMonad m => TWParser m B.Inlines link = try $ do st <- getState guard $ stateAllowLinks st @@ -507,7 +514,7 @@ link = try $ do setState $ st{ stateAllowLinks = True } return $ B.link url title content -linkText :: TWParser (String, String, B.Inlines) +linkText :: PandocMonad m => TWParser m (String, String, B.Inlines) linkText = do string "[[" url <- many1Till anyChar (char ']') diff --git a/src/Text/Pandoc/Readers/Textile.hs b/src/Text/Pandoc/Readers/Textile.hs index b3a1a208f..804ee39aa 100644 --- a/src/Text/Pandoc/Readers/Textile.hs +++ b/src/Text/Pandoc/Readers/Textile.hs @@ -64,11 +64,10 @@ import Text.HTML.TagSoup (fromAttrib, Tag(..)) import Text.HTML.TagSoup.Match import Data.List ( intercalate, transpose, intersperse ) import Data.Char ( digitToInt, isUpper ) -import Control.Monad ( guard, liftM, when ) +import Control.Monad ( guard, liftM ) import Data.Monoid ((<>)) import Text.Printf -import Debug.Trace (trace) -import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Class (PandocMonad, report) import Control.Monad.Except (throwError) -- | Parse a Textile text and return a Pandoc document. @@ -147,10 +146,8 @@ block :: PandocMonad m => ParserT [Char] ParserState m Blocks block = do res <- choice blockParsers "block" pos <- getPosition - tr <- (== DEBUG) <$> getOption readerVerbosity - when tr $ - trace (printf "line %d: %s" (sourceLine pos) - (take 60 $ show $ B.toList res)) (return ()) + report DEBUG $ printf "line %d: %s" (sourceLine pos) + (take 60 $ show $ B.toList res) return res commentBlock :: PandocMonad m => ParserT [Char] ParserState m Blocks -- cgit v1.2.3 From 70b86f48e1cd11b2c861951ec0a121fa5a54f889 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 23 Jan 2017 00:06:04 +0100 Subject: Removed readerVerbosity and writerVerbosity. API change. Also added a verbosity parameter to makePDF. --- pandoc.hs | 4 +--- src/Text/Pandoc/Options.hs | 4 ---- src/Text/Pandoc/PDF.hs | 11 ++++++----- src/Text/Pandoc/Readers/EPUB.hs | 13 ++++--------- src/Text/Pandoc/Readers/Haddock.hs | 10 +++------- tests/Tests/Readers/Docx.hs | 3 ++- 6 files changed, 16 insertions(+), 29 deletions(-) (limited to 'src/Text') diff --git a/pandoc.hs b/pandoc.hs index c863f324f..c6faa9edf 100644 --- a/pandoc.hs +++ b/pandoc.hs @@ -291,7 +291,6 @@ convertWithOpts opts args = do , readerIndentedCodeClasses = codeBlockClasses , readerApplyMacros = not laTeXOutput , readerDefaultImageExtension = defaultImageExtension - , readerVerbosity = verbosity , readerTrackChanges = trackChanges } @@ -328,7 +327,6 @@ convertWithOpts opts args = do writerEpubChapterLevel = epubChapterLevel, writerTOCDepth = epubTOCDepth, writerReferenceDoc = referenceDoc, - writerVerbosity = verbosity, writerLaTeXArgs = latexEngineArgs } @@ -406,7 +404,7 @@ convertWithOpts opts args = do err 41 $ pdfprog ++ " not found. " ++ pdfprog ++ " is needed for pdf output." - res <- makePDF pdfprog f writerOptions media doc' + res <- makePDF pdfprog f writerOptions verbosity media doc' case res of Right pdf -> writeFnBinary outputFile pdf Left err' -> liftIO $ do diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index 262a0392d..cd525a3c1 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -62,7 +62,6 @@ data ReaderOptions = ReaderOptions{ , readerIndentedCodeClasses :: [String] -- ^ Default classes for -- indented code blocks , readerDefaultImageExtension :: String -- ^ Default extension for images - , readerVerbosity :: Verbosity -- ^ Verbosity level , readerTrackChanges :: TrackChanges } deriving (Show, Read, Data, Typeable, Generic) @@ -76,7 +75,6 @@ instance Default ReaderOptions , readerApplyMacros = True , readerIndentedCodeClasses = [] , readerDefaultImageExtension = "" - , readerVerbosity = ERROR , readerTrackChanges = AcceptChanges } @@ -186,7 +184,6 @@ data WriterOptions = WriterOptions , writerEpubChapterLevel :: Int -- ^ Header level for chapters (separate files) , writerTOCDepth :: Int -- ^ Number of levels to include in TOC , writerReferenceDoc :: Maybe FilePath -- ^ Path to reference document if specified - , writerVerbosity :: Verbosity -- ^ Verbose debugging output , writerLaTeXArgs :: [String] -- ^ Flags to pass to latex-engine , writerReferenceLocation :: ReferenceLocation -- ^ Location of footnotes and references for writing markdown } deriving (Show, Data, Typeable, Generic) @@ -228,7 +225,6 @@ instance Default WriterOptions where , writerEpubChapterLevel = 1 , writerTOCDepth = 3 , writerReferenceDoc = Nothing - , writerVerbosity = WARNING , writerLaTeXArgs = [] , writerReferenceLocation = EndOfDocument } diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index cc523febf..b3bbcb4f5 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -74,10 +74,11 @@ makePDF :: MonadIO m -- xelatex, context, wkhtmltopdf) -> (WriterOptions -> Pandoc -> PandocIO String) -- ^ writer -> WriterOptions -- ^ options + -> Verbosity -- ^ verbosity level -> MediaBag -- ^ media -> Pandoc -- ^ document -> m (Either ByteString ByteString) -makePDF "wkhtmltopdf" writer opts _mediabag doc@(Pandoc meta _) = liftIO $ do +makePDF "wkhtmltopdf" writer opts verbosity _ doc@(Pandoc meta _) = liftIO $ do let mathArgs = case writerHTMLMathMethod opts of -- with MathJax, wait til all math is rendered: MathJax _ -> ["--run-script", "MathJax.Hub.Register.StartupHook('End Typeset', function() { window.status = 'mathjax_loaded' });", @@ -99,16 +100,16 @@ makePDF "wkhtmltopdf" writer opts _mediabag doc@(Pandoc meta _) = liftIO $ do (getField "margin-left" meta')) ] source <- runIOorExplode $ writer opts doc - html2pdf (writerVerbosity opts) args source -makePDF program writer opts mediabag doc = + html2pdf verbosity args source +makePDF program writer opts verbosity mediabag doc = liftIO $ withTempDir "tex2pdf." $ \tmpdir -> do doc' <- handleImages opts mediabag tmpdir doc source <- runIOorExplode $ writer opts doc' let args = writerLaTeXArgs opts case takeBaseName program of - "context" -> context2pdf (writerVerbosity opts) tmpdir source + "context" -> context2pdf verbosity tmpdir source prog | prog `elem` ["pdflatex", "lualatex", "xelatex"] - -> tex2pdf' (writerVerbosity opts) args tmpdir program source + -> tex2pdf' verbosity args tmpdir program source _ -> return $ Left $ UTF8.fromStringLazy $ "Unknown program " ++ program handleImages :: WriterOptions diff --git a/src/Text/Pandoc/Readers/EPUB.hs b/src/Text/Pandoc/Readers/EPUB.hs index 71a527f13..f24adb5b1 100644 --- a/src/Text/Pandoc/Readers/EPUB.hs +++ b/src/Text/Pandoc/Readers/EPUB.hs @@ -12,7 +12,7 @@ import Text.XML.Light import Text.Pandoc.Definition hiding (Attr) import Text.Pandoc.Readers.HTML (readHtml) import Text.Pandoc.Walk (walk, query) -import Text.Pandoc.Options ( ReaderOptions(..), readerVerbosity, Verbosity(..)) +import Text.Pandoc.Options ( ReaderOptions(..), Verbosity(..)) import Text.Pandoc.Shared (escapeURI, collapseFilePath, addMetaField) import Network.URI (unEscapeString) import Text.Pandoc.MediaBag (MediaBag, insertMedia) @@ -26,18 +26,16 @@ import System.FilePath ( takeFileName, (), dropFileName, normalise , dropFileName , splitFileName ) import qualified Text.Pandoc.UTF8 as UTF8 (toStringLazy) -import Control.Monad (guard, liftM, when) +import Control.Monad (guard, liftM) import Data.List (isPrefixOf, isInfixOf) import Data.Maybe (mapMaybe, fromMaybe) import qualified Data.Map as M (Map, lookup, fromList, elems) import Data.Monoid ((<>)) import Control.DeepSeq (deepseq, NFData) import Text.Pandoc.Error -import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Class (PandocMonad, report) import qualified Text.Pandoc.Class as P -import Debug.Trace (trace) - type Items = M.Map String (FilePath, MimeType) readEPUB :: PandocMonad m => ReaderOptions -> BL.ByteString -> m Pandoc @@ -71,7 +69,7 @@ archiveToEPUB os archive = do os' = os {readerParseRaw = True} parseSpineElem :: PandocMonad m => FilePath -> (FilePath, MimeType) -> m Pandoc parseSpineElem (normalise -> r) (normalise -> path, mime) = do - when (readerVerbosity os == DEBUG) (traceM path) + report DEBUG ("parseSpineElem called with path " ++ show path) doc <- mimeToReader mime r path let docSpan = B.doc $ B.para $ B.spanWith (takeFileName path, [], []) mempty return $ docSpan <> doc @@ -241,9 +239,6 @@ foldM' f z (x:xs) = do uncurry3 :: (a -> b -> c -> d) -> (a, b, c) -> d uncurry3 f (a, b, c) = f a b c -traceM :: Monad m => String -> m () -traceM = flip trace (return ()) - -- Utility stripNamespace :: QName -> String diff --git a/src/Text/Pandoc/Readers/Haddock.hs b/src/Text/Pandoc/Readers/Haddock.hs index 575d99c77..310a04574 100644 --- a/src/Text/Pandoc/Readers/Haddock.hs +++ b/src/Text/Pandoc/Readers/Haddock.hs @@ -24,7 +24,6 @@ import Text.Pandoc.Definition import Text.Pandoc.Options import Documentation.Haddock.Parser import Documentation.Haddock.Types -import Debug.Trace (trace) import Text.Pandoc.Error import Control.Monad.Except (throwError) import Text.Pandoc.Class (PandocMonad) @@ -42,15 +41,12 @@ readHaddock opts s = case readHaddockEither opts s of readHaddockEither :: ReaderOptions -- ^ Reader options -> String -- ^ String to parse -> Either PandocError Pandoc -readHaddockEither opts = +readHaddockEither _opts = #if MIN_VERSION_haddock_library(1,2,0) - Right . B.doc . docHToBlocks . trace' . _doc . parseParas + Right . B.doc . docHToBlocks . _doc . parseParas #else - Right . B.doc . docHToBlocks . trace' . parseParas + Right . B.doc . docHToBlocks . parseParas #endif - where trace' x = if readerVerbosity opts == DEBUG - then trace (show x) x - else x docHToBlocks :: DocH String Identifier -> Blocks docHToBlocks d' = diff --git a/tests/Tests/Readers/Docx.hs b/tests/Tests/Readers/Docx.hs index 14bae19b0..8ced43907 100644 --- a/tests/Tests/Readers/Docx.hs +++ b/tests/Tests/Readers/Docx.hs @@ -62,7 +62,8 @@ testCompare = testCompareWithOpts defopts testForWarningsWithOptsIO :: ReaderOptions -> String -> FilePath -> [String] -> IO Test testForWarningsWithOptsIO opts name docxFile expected = do df <- B.readFile docxFile - warns <- runIOorExplode (readDocx opts df >> P.getWarnings) + logs <- runIOorExplode (readDocx opts df >> P.getLog) + let warns = [s | (WARNING, s) <- logs] return $ test id name (unlines warns, unlines expected) testForWarningsWithOpts :: ReaderOptions -> String -> FilePath -> [String] -> Test -- cgit v1.2.3 From 73e343cfcd1bbdc46cd1bcfc37737678a3cebc20 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 23 Jan 2017 21:11:35 +0100 Subject: Fixed small mistake in instance for logOutput. --- src/Text/Pandoc/Class.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 79c7316f1..b8befd5b8 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -548,7 +548,7 @@ instance (PandocMonad m, Monoid w) => PandocMonad (WriterT w m) where getModificationTime = lift . getModificationTime getCommonState = lift getCommonState putCommonState = lift . putCommonState - logOutput lvl = lift . report lvl + logOutput lvl = lift . logOutput lvl instance (PandocMonad m, Monoid w) => PandocMonad (RWST r w st m) where lookupEnv = lift . lookupEnv -- cgit v1.2.3 From 65b8570e0e0b2c7e570e051859c9e0db0b7442f6 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 24 Jan 2017 15:28:02 +0100 Subject: Cleanups for rebase. --- src/Text/Pandoc/Readers/Org/Blocks.hs | 2 +- src/Text/Pandoc/Writers/OpenDocument.hs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index 5176e0f6c..8ffc0bb19 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -682,7 +682,7 @@ exampleCode = B.codeBlockWith ("", ["example"], []) specialLine :: PandocMonad m => OrgParser m (F Blocks) specialLine = fmap return . try $ rawExportLine <|> metaLine <|> commentLine -rawExportLine :: PnadocMonad m => OrgParser m Blocks +rawExportLine :: PandocMonad m => OrgParser m Blocks rawExportLine = try $ do metaLineStart key <- metaKey diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index f50b240a4..59470c2f9 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -206,7 +206,7 @@ writeOpenDocument opts (Pandoc meta blocks) = do meta b <- render' `fmap` blocksToOpenDocument opts blocks return (b, m) - styles = stTableStyles s ++ stParaStyles s ++ + let styles = stTableStyles s ++ stParaStyles s ++ map snd (reverse $ sortBy (comparing fst) $ Map.elems (stTextStyles s)) listStyle (n,l) = inTags True "text:list-style" @@ -559,7 +559,7 @@ tableStyle num wcs = paraStyle :: PandocMonad m => [(String,String)] -> OD m Int paraStyle attrs = do pn <- (+) 1 . length <$> gets stParaStyles - i <- (*) 0.5 . fromIntegral <$> gets stIndentPara :: State WriterState Double + i <- (*) (0.5 :: Double) . fromIntegral <$> gets stIndentPara b <- gets stInDefinition t <- gets stTight let styleAttr = [ ("style:name" , "P" ++ show pn) -- cgit v1.2.3 From 2985e0ea4fe24fbe62cb757af1bf6afb514bc9b7 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 25 Jan 2017 21:10:49 +0100 Subject: Removed unneeded exports. --- src/Text/Pandoc/Highlighting.hs | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Highlighting.hs b/src/Text/Pandoc/Highlighting.hs index 8722bb463..896682389 100644 --- a/src/Text/Pandoc/Highlighting.hs +++ b/src/Text/Pandoc/Highlighting.hs @@ -51,11 +51,9 @@ module Text.Pandoc.Highlighting ( languages import Text.Pandoc.Definition import Text.Pandoc.Shared (safeRead) import Skylighting -import qualified Data.Set as Set import Data.Maybe (fromMaybe) import Data.Char (toLower) import qualified Data.Map as M -import Control.Applicative ((<|>)) import Control.Monad import qualified Data.Text as T -- cgit v1.2.3 From fce0a60f0a85d6c3a9e7633074ecd781af08c75b Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 25 Jan 2017 21:51:26 +0100 Subject: Provide explicit separate functions for HTML 4 and 5. * Text.Pandoc.Writers.HTML: removed writeHtml, writeHtmlString, added writeHtml4, writeHtml4String, writeHtml5, writeHtml5String. * Removed writerHtml5 from WriterOptions. * Renamed default.html template to default.html4. * "html" now aliases to "html5"; to get the old HTML4 behavior, you must now specify "-t html4". --- pandoc.cabal | 8 +- pandoc.hs | 10 +- src/Text/Pandoc.hs | 27 +- src/Text/Pandoc/Options.hs | 2 - src/Text/Pandoc/Templates.hs | 1 + src/Text/Pandoc/Writers/CommonMark.hs | 4 +- src/Text/Pandoc/Writers/EPUB.hs | 11 +- src/Text/Pandoc/Writers/HTML.hs | 117 +++++--- src/Text/Pandoc/Writers/Markdown.hs | 8 +- src/Text/Pandoc/Writers/OPML.hs | 4 +- tests/Tests/Old.hs | 7 +- tests/Tests/Writers/HTML.hs | 2 +- tests/lhs-test.html | 13 +- tests/lhs-test.html+lhs | 13 +- tests/tables.html | 204 ------------- tests/tables.html4 | 204 +++++++++++++ tests/tables.html5 | 204 +++++++++++++ tests/writer.html | 546 --------------------------------- tests/writer.html4 | 546 +++++++++++++++++++++++++++++++++ tests/writer.html5 | 548 ++++++++++++++++++++++++++++++++++ 20 files changed, 1633 insertions(+), 846 deletions(-) delete mode 100644 tests/tables.html create mode 100644 tests/tables.html4 create mode 100644 tests/tables.html5 delete mode 100644 tests/writer.html create mode 100644 tests/writer.html4 create mode 100644 tests/writer.html5 (limited to 'src/Text') diff --git a/pandoc.cabal b/pandoc.cabal index 7cb292b05..97e70c830 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -36,7 +36,7 @@ Description: Pandoc is a Haskell library for converting from one markup only adding a reader or writer. Data-Files: -- templates - data/templates/default.html + data/templates/default.html4 data/templates/default.html5 data/templates/default.docbook data/templates/default.docbook5 @@ -150,7 +150,8 @@ Extra-Source-Files: tests/tables.dokuwiki tests/tables.zimwiki tests/tables.icml - tests/tables.html + tests/tables.html4 + tests/tables.html5 tests/tables.latex tests/tables.man tests/tables.plain @@ -172,7 +173,8 @@ Extra-Source-Files: tests/writer.context tests/writer.docbook tests/writer.docbook5 - tests/writer.html + tests/writer.html4 + tests/writer.html5 tests/writer.man tests/writer.markdown tests/writer.plain diff --git a/pandoc.hs b/pandoc.hs index c6faa9edf..9ee6e376b 100644 --- a/pandoc.hs +++ b/pandoc.hs @@ -111,7 +111,6 @@ convertWithOpts opts args = do , optSectionDivs = sectionDivs , optIncremental = incremental , optSelfContained = selfContained - , optHtml5 = html5 , optHtmlQTags = htmlQTags , optHighlightStyle = highlightStyle , optTopLevelDivision = topLevelDivision @@ -188,13 +187,11 @@ convertWithOpts opts args = do (if any isURI sources then "html" else "markdown") sources - "html4" -> "html" x -> x let writerName' = case map toLower writerName of [] -> defaultWriterName outputFile "epub2" -> "epub" - "html4" -> "html" x -> x let format = takeWhile (`notElem` ['+','-']) $ takeFileName writerName' -- in case path to lua script @@ -203,7 +200,7 @@ convertWithOpts opts args = do let laTeXOutput = format `elem` ["latex", "beamer"] let conTeXtOutput = format == "context" - let html5Output = format == "html5" + let html5Output = format == "html5" || format == "html" -- disabling the custom writer for now writer <- if ".lua" `isSuffixOf` format @@ -313,7 +310,6 @@ convertWithOpts opts args = do writerIdentifierPrefix = idPrefix, writerSourceURL = sourceURL, writerUserDataDir = datadir, - writerHtml5 = html5, writerHtmlQTags = htmlQTags, writerTopLevelDivision = topLevelDivision, writerListings = listings, @@ -413,7 +409,7 @@ convertWithOpts opts args = do err 43 "Error producing PDF" | otherwise -> do let htmlFormat = format `elem` - ["html","html5","s5","slidy","slideous","dzslides","revealjs"] + ["html","html4","html5","s5","slidy","slideous","dzslides","revealjs"] selfcontain = if selfContained && htmlFormat then makeSelfContained writerOptions media else return @@ -523,7 +519,6 @@ data Opt = Opt , optSectionDivs :: Bool -- ^ Put sections in div tags in HTML , optIncremental :: Bool -- ^ Use incremental lists in Slidy/Slideous/S5 , optSelfContained :: Bool -- ^ Make HTML accessible offline - , optHtml5 :: Bool -- ^ Produce HTML5 in HTML , optHtmlQTags :: Bool -- ^ Use tags in HTML , optHighlightStyle :: Maybe Style -- ^ Style to use for highlighted code , optTopLevelDivision :: TopLevelDivision -- ^ Type of the top-level divisions @@ -583,7 +578,6 @@ defaultOpts = Opt , optSectionDivs = False , optIncremental = False , optSelfContained = False - , optHtml5 = False , optHtmlQTags = False , optHighlightStyle = Just pygments , optTopLevelDivision = TopLevelDefault diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index f9e032f4f..aa4cab840 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -99,8 +99,10 @@ module Text.Pandoc , writeLaTeX , writeConTeXt , writeTexinfo - , writeHtml - , writeHtmlString + , writeHtml4 + , writeHtml4String + , writeHtml5 + , writeHtml5String , writeICML , writeDocbook , writeOPML @@ -281,23 +283,21 @@ writers = [ ,("epub3" , ByteStringWriter $ \o -> writeEPUB o{ writerEpubVersion = Just EPUB3 }) ,("fb2" , StringWriter writeFB2) - ,("html" , StringWriter writeHtmlString) - ,("html5" , StringWriter $ \o -> - writeHtmlString o{ writerHtml5 = True }) + ,("html" , StringWriter writeHtml5String) + ,("html4" , StringWriter writeHtml4String) + ,("html5" , StringWriter writeHtml5String) ,("icml" , StringWriter writeICML) ,("s5" , StringWriter $ \o -> - writeHtmlString o{ writerSlideVariant = S5Slides - , writerTableOfContents = False }) + writeHtml4String o{ writerSlideVariant = S5Slides + , writerTableOfContents = False }) ,("slidy" , StringWriter $ \o -> - writeHtmlString o{ writerSlideVariant = SlidySlides }) + writeHtml4String o{ writerSlideVariant = SlidySlides }) ,("slideous" , StringWriter $ \o -> - writeHtmlString o{ writerSlideVariant = SlideousSlides }) + writeHtml4String o{ writerSlideVariant = SlideousSlides }) ,("dzslides" , StringWriter $ \o -> - writeHtmlString o{ writerSlideVariant = DZSlides - , writerHtml5 = True }) + writeHtml5String o{ writerSlideVariant = DZSlides }) ,("revealjs" , StringWriter $ \o -> - writeHtmlString o{ writerSlideVariant = RevealJsSlides - , writerHtml5 = True }) + writeHtml5String o{ writerSlideVariant = RevealJsSlides }) ,("docbook" , StringWriter writeDocbook) ,("docbook5" , StringWriter $ \o -> writeDocbook o{ writerDocbook5 = True }) @@ -342,6 +342,7 @@ getDefaultExtensions "html" = extensionsFromList [Ext_auto_identifiers, Ext_native_divs, Ext_native_spans] +getDefaultExtensions "html4" = getDefaultExtensions "html" getDefaultExtensions "html5" = getDefaultExtensions "html" getDefaultExtensions "epub" = extensionsFromList [Ext_raw_html, diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index cd525a3c1..6cb2d883a 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -168,7 +168,6 @@ data WriterOptions = WriterOptions , writerUserDataDir :: Maybe FilePath -- ^ Path of user data directory , writerCiteMethod :: CiteMethod -- ^ How to print cites , writerDocbook5 :: Bool -- ^ Produce DocBook5 - , writerHtml5 :: Bool -- ^ Produce HTML5 , writerHtmlQTags :: Bool -- ^ Use @@ tags for quotes in HTML , writerBeamer :: Bool -- ^ Produce beamer LaTeX slide show , writerSlideLevel :: Maybe Int -- ^ Force header level of slides @@ -210,7 +209,6 @@ instance Default WriterOptions where , writerUserDataDir = Nothing , writerCiteMethod = Citeproc , writerDocbook5 = False - , writerHtml5 = False , writerHtmlQTags = False , writerBeamer = False , writerSlideLevel = Nothing diff --git a/src/Text/Pandoc/Templates.hs b/src/Text/Pandoc/Templates.hs index d15d27438..03dc917e6 100644 --- a/src/Text/Pandoc/Templates.hs +++ b/src/Text/Pandoc/Templates.hs @@ -60,6 +60,7 @@ getDefaultTemplate user writer = do "docx" -> return $ Right "" "fb2" -> return $ Right "" "odt" -> getDefaultTemplate user "opendocument" + "html" -> getDefaultTemplate user "html5" "markdown_strict" -> getDefaultTemplate user "markdown" "multimarkdown" -> getDefaultTemplate user "markdown" "markdown_github" -> getDefaultTemplate user "markdown" diff --git a/src/Text/Pandoc/Writers/CommonMark.hs b/src/Text/Pandoc/Writers/CommonMark.hs index c58e83f19..b83f6785d 100644 --- a/src/Text/Pandoc/Writers/CommonMark.hs +++ b/src/Text/Pandoc/Writers/CommonMark.hs @@ -31,7 +31,7 @@ CommonMark: -} module Text.Pandoc.Writers.CommonMark (writeCommonMark) where -import Text.Pandoc.Writers.HTML (writeHtmlString) +import Text.Pandoc.Writers.HTML (writeHtml5String) import Text.Pandoc.Definition import Text.Pandoc.Shared (isTightList, linesToPara) import Text.Pandoc.Templates (renderTemplate') @@ -138,7 +138,7 @@ blockToNodes (DefinitionList items) ns = blockToNodes (BulletList items') ns dlToBullet (term, xs) = Para term : concat xs blockToNodes t@(Table _ _ _ _ _) ns = do - s <- writeHtmlString def $! Pandoc nullMeta [t] + s <- writeHtml5String def $! Pandoc nullMeta [t] return (node (HTML_BLOCK (T.pack $! s)) [] : ns) blockToNodes Null ns = return ns diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index d6c3ff533..bd95c170e 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -59,7 +59,7 @@ import Control.Monad (mplus, when, zipWithM) import Text.XML.Light ( unode, Element(..), unqual, Attr(..), add_attrs , strContent, lookupAttr, Node(..), QName(..), parseXML , onlyElems, node, ppElement) -import Text.Pandoc.Writers.HTML ( writeHtml ) +import Text.Pandoc.Writers.HTML ( writeHtml4, writeHtml5 ) import Data.Char ( toLower, isDigit, isAlphaNum ) import Text.Pandoc.MIME (MimeType, getMimeType, extensionFromMimeType) import Text.Blaze.Html.Renderer.Utf8 (renderHtml) @@ -361,13 +361,15 @@ pandocToEPUB opts doc@(Pandoc meta _) = do : writerVariables opts let opts' = opts{ writerEmailObfuscation = NoObfuscation , writerSectionDivs = True - , writerHtml5 = epub3 , writerVariables = vars , writerHTMLMathMethod = if epub3 then MathML Nothing else writerHTMLMathMethod opts , writerWrapText = WrapAuto } + let writeHtml = if epub3 + then writeHtml5 + else writeHtml4 metadata <- getEPUBMetadata opts' meta -- cover page @@ -376,7 +378,7 @@ pandocToEPUB opts doc@(Pandoc meta _) = do Nothing -> return ([],[]) Just img -> do let coverImage = "media/" ++ takeFileName img - cpContent <- renderHtml <$> (lift $ writeHtml + cpContent <- renderHtml <$> (lift $ writeHtml opts'{ writerVariables = ("coverpage","true"):vars } (Pandoc meta [RawBlock (Format "html") $ "
\n\"cover\n
"])) imgContent <- lift $ P.readFileLazy img @@ -484,8 +486,7 @@ pandocToEPUB opts doc@(Pandoc meta _) = do Chapter mbnum $ walk fixInternalReferences bs) chapters' - let chapToEntry :: PandocMonad m => Int -> Chapter -> m Entry - chapToEntry num (Chapter mbnum bs) = + let chapToEntry num (Chapter mbnum bs) = (mkEntry (showChapter num) . renderHtml) <$> (writeHtml opts'{ writerNumberOffset = fromMaybe [] mbnum } diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index c6d7b7f6a..ee1f260b6 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -28,7 +28,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Conversion of 'Pandoc' documents to HTML. -} -module Text.Pandoc.Writers.HTML ( writeHtml , writeHtmlString ) where +module Text.Pandoc.Writers.HTML ( + writeHtml4, writeHtml4String, + writeHtml5, writeHtml5String ) where import Text.Pandoc.Definition import Text.Pandoc.Walk import Data.Monoid ((<>)) @@ -80,12 +82,13 @@ data WriterState = WriterState , stHighlighting :: Bool -- ^ Syntax highlighting is used , stSecNum :: [Int] -- ^ Number of current section , stElement :: Bool -- ^ Processing an Element + , stHtml5 :: Bool -- ^ Use HTML5 } defaultWriterState :: WriterState defaultWriterState = WriterState {stNotes= [], stMath = False, stQuotes = False, stHighlighting = False, stSecNum = [], - stElement = False} + stElement = False, stHtml5 = False} -- Helpers to render HTML with the appropriate function. @@ -102,19 +105,35 @@ nl opts = if writerWrapText opts == WrapNone then mempty else preEscapedString "\n" --- | Convert Pandoc document to Html string. -writeHtmlString :: PandocMonad m => WriterOptions -> Pandoc -> m String -writeHtmlString opts d = do - (body, context) <- evalStateT (pandocToHtml opts d) defaultWriterState +-- | Convert Pandoc document to Html 5 string. +writeHtml5String :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeHtml5String = writeHtmlString' True + +-- | Convert Pandoc document to Html 5 structure. +writeHtml5 :: PandocMonad m => WriterOptions -> Pandoc -> m Html +writeHtml5 = writeHtml' True + +-- | Convert Pandoc document to Html 4 string. +writeHtml4String :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeHtml4String = writeHtmlString' False + +-- | Convert Pandoc document to Html 4 structure. +writeHtml4 :: PandocMonad m => WriterOptions -> Pandoc -> m Html +writeHtml4 = writeHtml' False + +writeHtmlString' :: PandocMonad m => Bool -> WriterOptions -> Pandoc -> m String +writeHtmlString' html5 opts d = do + (body, context) <- evalStateT (pandocToHtml opts d) + defaultWriterState{ stHtml5 = html5 } return $ case writerTemplate opts of Nothing -> renderHtml body Just tpl -> renderTemplate' tpl $ defField "body" (renderHtml body) context --- | Convert Pandoc document to Html structure. -writeHtml :: PandocMonad m => WriterOptions -> Pandoc -> m Html -writeHtml opts d = do - (body, context) <- evalStateT (pandocToHtml opts d) defaultWriterState +writeHtml' :: PandocMonad m => Bool -> WriterOptions -> Pandoc -> m Html +writeHtml' html5 opts d = do + (body, context) <- evalStateT (pandocToHtml opts d) + defaultWriterState{ stHtml5 = html5 } return $ case writerTemplate opts of Nothing -> body Just tpl -> renderTemplate' tpl $ @@ -144,8 +163,8 @@ pandocToHtml opts (Pandoc meta blocks) = do blocks' <- liftM (mconcat . intersperse (nl opts)) $ mapM (elementToHtml slideLevel opts) sects st <- get - let notes = reverse (stNotes st) - let thebody = blocks' >> footnoteSection opts notes + notes <- footnoteSection opts (reverse (stNotes st)) + let thebody = blocks' >> notes let math = case writerHTMLMathMethod opts of LaTeXMathML (Just url) -> H.script ! A.src (toValue url) @@ -172,7 +191,7 @@ pandocToHtml opts (Pandoc meta blocks) = do (H.link ! A.rel "stylesheet" ! A.href (toValue css)) <> (H.script ! A.type_ "text/javascript" $ toHtml renderKaTeX) _ -> case lookup "mathml-script" (writerVariables opts) of - Just s | not (writerHtml5 opts) -> + Just s | not (stHtml5 st) -> H.script ! A.type_ "text/javascript" $ preEscapedString ("/**/\n") @@ -199,7 +218,7 @@ pandocToHtml opts (Pandoc meta blocks) = do defField "slideous-url" ("slideous" :: String) $ defField "revealjs-url" ("reveal.js" :: String) $ defField "s5-url" ("s5/default" :: String) $ - defField "html5" (writerHtml5 opts) $ + defField "html5" (stHtml5 st) $ metadata return (thebody, context) @@ -277,6 +296,7 @@ elementToHtml slideLevel opts (Sec level num (id',classes,keyvals) title' elemen let slide = writerSlideVariant opts /= NoSlides && level <= slideLevel let num' = zipWith (+) num (writerNumberOffset opts ++ repeat 0) modify $ \st -> st{stSecNum = num'} -- update section number + html5 <- gets stHtml5 let titleSlide = slide && level < slideLevel header' <- if title' == [Str "\0"] -- marker for hrule then return mempty @@ -307,10 +327,10 @@ elementToHtml slideLevel opts (Sec level num (id',classes,keyvals) title' elemen let inNl x = mconcat $ nl opts : intersperse (nl opts) x ++ [nl opts] let classes' = ["titleslide" | titleSlide] ++ ["slide" | slide] ++ ["section" | (slide || writerSectionDivs opts) && - not (writerHtml5 opts) ] ++ + not html5 ] ++ ["level" ++ show level | slide || writerSectionDivs opts ] ++ classes - let secttag = if writerHtml5 opts + let secttag = if html5 then H5.section else H.div let attr = (id',classes',keyvals) @@ -327,19 +347,22 @@ elementToHtml slideLevel opts (Sec level num (id',classes,keyvals) title' elemen -- | Convert list of Note blocks to a footnote
. -- Assumes notes are sorted. -footnoteSection :: WriterOptions -> [Html] -> Html -footnoteSection opts notes = - if null notes - then mempty - else nl opts >> (container - $ nl opts >> hrtag >> nl opts >> - H.ol (mconcat notes >> nl opts) >> nl opts) - where container x = if writerHtml5 opts - then H5.section ! A.class_ "footnotes" $ x - else if writerSlideVariant opts /= NoSlides - then H.div ! A.class_ "footnotes slide" $ x - else H.div ! A.class_ "footnotes" $ x - hrtag = if writerHtml5 opts then H5.hr else H.hr +footnoteSection :: PandocMonad m + => WriterOptions -> [Html] -> StateT WriterState m Html +footnoteSection opts notes = do + html5 <- gets stHtml5 + let hrtag = if html5 then H5.hr else H.hr + let container x = if html5 + then H5.section ! A.class_ "footnotes" $ x + else if writerSlideVariant opts /= NoSlides + then H.div ! A.class_ "footnotes slide" $ x + else H.div ! A.class_ "footnotes" $ x + return $ + if null notes + then mempty + else nl opts >> (container + $ nl opts >> hrtag >> nl opts >> + H.ol (mconcat notes >> nl opts) >> nl opts) -- | Parse a mailto link; return Just (name, domain) or Nothing. parseMailto :: String -> Maybe (String, String) @@ -448,13 +471,14 @@ blockToHtml opts (Plain lst) = inlineListToHtml opts lst -- title beginning with fig: indicates that the image is a figure blockToHtml opts (Para [Image attr txt (s,'f':'i':'g':':':tit)]) = do img <- inlineToHtml opts (Image attr txt (s,tit)) - let tocapt = if writerHtml5 opts + html5 <- gets stHtml5 + let tocapt = if html5 then H5.figcaption else H.p ! A.class_ "caption" capt <- if null txt then return mempty else tocapt `fmap` inlineListToHtml opts txt - return $ if writerHtml5 opts + return $ if html5 then H5.figure $ mconcat [nl opts, img, capt, nl opts] else H.div ! A.class_ "figure" $ mconcat @@ -475,12 +499,13 @@ blockToHtml opts (LineBlock lns) = htmlLines <- mconcat . intersperse lf <$> mapM (inlineListToHtml opts) lns return $ H.div ! A.style "white-space: pre-line;" $ htmlLines blockToHtml opts (Div attr@(ident, classes, kvs) bs) = do + html5 <- gets stHtml5 let speakerNotes = "notes" `elem` classes -- we don't want incremental output inside speaker notes, see #1394 let opts' = if speakerNotes then opts{ writerIncremental = False } else opts contents <- blockListToHtml opts' bs let contents' = nl opts >> contents >> nl opts - let (divtag, classes') = if writerHtml5 opts && "section" `elem` classes + let (divtag, classes') = if html5 && "section" `elem` classes then (H5.section, filter (/= "section") classes) else (H.div, classes) return $ @@ -498,7 +523,9 @@ blockToHtml opts (RawBlock f str) allowsMathEnvironments (writerHTMLMathMethod opts) && isMathEnvironment str = blockToHtml opts $ Plain [Math DisplayMath str] | otherwise = return mempty -blockToHtml opts (HorizontalRule) = return $ if writerHtml5 opts then H5.hr else H.hr +blockToHtml _ (HorizontalRule) = do + html5 <- gets stHtml5 + return $ if html5 then H5.hr else H.hr blockToHtml opts (CodeBlock (id',classes,keyvals) rawCode) = do let tolhs = isEnabled Ext_literate_haskell opts && any (\c -> map toLower c == "haskell") classes && @@ -564,6 +591,7 @@ blockToHtml opts (BulletList lst) = do return $ unordList opts contents blockToHtml opts (OrderedList (startnum, numstyle, _) lst) = do contents <- mapM (blockListToHtml opts) lst + html5 <- gets stHtml5 let numstyle' = case numstyle of Example -> "decimal" _ -> camelCaseToHyphenated $ show numstyle @@ -574,7 +602,7 @@ blockToHtml opts (OrderedList (startnum, numstyle, _) lst) = do then [A.class_ "example"] else []) ++ (if numstyle /= DefaultStyle - then if writerHtml5 opts + then if html5 then [A.type_ $ case numstyle of Decimal -> "1" @@ -603,6 +631,7 @@ blockToHtml opts (Table capt aligns widths headers rows') = do else do cs <- inlineListToHtml opts capt return $ H.caption cs >> nl opts + html5 <- gets stHtml5 let percent w = show (truncate (100*w) :: Integer) ++ "%" let coltags = if all (== 0.0) widths then mempty @@ -610,7 +639,7 @@ blockToHtml opts (Table capt aligns widths headers rows') = do H.colgroup $ do nl opts mapM_ (\w -> do - if writerHtml5 opts + if html5 then H.col ! A.style (toValue $ "width: " ++ percent w) else H.col ! A.width (toValue $ percent w) @@ -666,8 +695,9 @@ tableItemToHtml :: PandocMonad m -> StateT WriterState m Html tableItemToHtml opts tag' align' item = do contents <- blockListToHtml opts item + html5 <- gets stHtml5 let alignStr = alignmentToString align' - let attribs = if writerHtml5 opts + let attribs = if html5 then A.style (toValue $ "text-align: " ++ alignStr ++ ";") else A.align (toValue alignStr) let tag'' = if null alignStr @@ -707,7 +737,8 @@ annotateMML e tex = math (unode "semantics" [cs, unode "annotation" (annotAttrs, -- | Convert Pandoc inline element to HTML. inlineToHtml :: PandocMonad m => WriterOptions -> Inline -> StateT WriterState m Html -inlineToHtml opts inline = +inlineToHtml opts inline = do + html5 <- gets stHtml5 case inline of (Str str) -> return $ strToHtml str (Space) -> return $ strToHtml " " @@ -715,7 +746,7 @@ inlineToHtml opts inline = WrapNone -> preEscapedString " " WrapAuto -> preEscapedString " " WrapPreserve -> preEscapedString "\n" - (LineBreak) -> return $ (if writerHtml5 opts then H5.br else H.br) + (LineBreak) -> return $ (if html5 then H5.br else H.br) <> strToHtml "\n" (Span (id',classes,kvs) ils) -> inlineListToHtml opts ils >>= @@ -784,12 +815,12 @@ inlineToHtml opts inline = InlineMath -> H.span ! A.class_ mathClass $ m DisplayMath -> H.div ! A.class_ mathClass $ m WebTeX url -> do - let imtag = if writerHtml5 opts then H5.img else H.img + let imtag = if html5 then H5.img else H.img let m = imtag ! A.style "vertical-align:middle" ! A.src (toValue $ url ++ urlEncode str) ! A.alt (toValue str) ! A.title (toValue str) - let brtag = if writerHtml5 opts then H5.br else H.br + let brtag = if html5 then H5.br else H.br return $ case t of InlineMath -> m DisplayMath -> brtag >> m >> brtag @@ -817,7 +848,7 @@ inlineToHtml opts inline = PlainMath -> do x <- lift (texMathToInlines t str) >>= inlineListToHtml opts let m = H.span ! A.class_ mathClass $ x - let brtag = if writerHtml5 opts then H5.br else H.br + let brtag = if html5 then H5.br else H.br return $ case t of InlineMath -> m DisplayMath -> brtag >> m >> brtag @@ -847,7 +878,7 @@ inlineToHtml opts inline = [A.title $ toValue tit | not (null tit)] ++ [A.alt $ toValue alternate' | not (null txt)] ++ imgAttrsToHtml opts attr - let tag = if writerHtml5 opts then H5.img else H.img + let tag = if html5 then H5.img else H.img return $ foldl (!) tag attributes -- note: null title included, as in Markdown.pl (Image attr _ (s,tit)) -> do @@ -880,7 +911,7 @@ inlineToHtml opts inline = (Cite cits il)-> do contents <- inlineListToHtml opts il let citationIds = unwords $ map citationId cits let result = H.span ! A.class_ "citation" $ contents - return $ if writerHtml5 opts + return $ if html5 then result ! customAttribute "data-cites" (toValue citationIds) else result diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 8de09864a..e965528cc 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -47,7 +47,7 @@ import Text.Pandoc.Pretty import Control.Monad.Reader import Control.Monad.State import Control.Monad.Except (throwError) -import Text.Pandoc.Writers.HTML (writeHtmlString) +import Text.Pandoc.Writers.HTML (writeHtml5String) import Text.Pandoc.Writers.Math (texMathToInlines) import Text.HTML.TagSoup (parseTags, isTagText, Tag(..)) import Network.URI (isURI) @@ -536,7 +536,7 @@ blockToMarkdown' opts t@(Table caption aligns widths headers rows) = do rawHeaders rawRows | isEnabled Ext_raw_html opts -> fmap (id,) $ text <$> - (writeHtmlString def $ Pandoc nullMeta [t]) + (writeHtml5String def $ Pandoc nullMeta [t]) | otherwise -> return $ (id, text "[TABLE]") return $ nst $ tbl $$ blankline $$ caption'' $$ blankline blockToMarkdown' opts (BulletList items) = do @@ -1072,7 +1072,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 - (text . trim) <$> writeHtmlString def (Pandoc nullMeta [Plain [lnk]]) + (text . trim) <$> writeHtml5String def (Pandoc nullMeta [Plain [lnk]]) | otherwise = do plain <- asks envPlain linktext <- inlineListToMarkdown opts txt @@ -1111,7 +1111,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 - (text . trim) <$> writeHtmlString def (Pandoc nullMeta [Plain [img]]) + (text . trim) <$> writeHtml5String 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/OPML.hs b/src/Text/Pandoc/Writers/OPML.hs index 38c96589a..bc0cfc300 100644 --- a/src/Text/Pandoc/Writers/OPML.hs +++ b/src/Text/Pandoc/Writers/OPML.hs @@ -35,7 +35,7 @@ import Text.Pandoc.Writers.Shared import Text.Pandoc.Shared import Text.Pandoc.Options import Text.Pandoc.Templates (renderTemplate') -import Text.Pandoc.Writers.HTML (writeHtmlString) +import Text.Pandoc.Writers.HTML (writeHtml5String) import Text.Pandoc.Writers.Markdown (writeMarkdown) import Text.Pandoc.Pretty import Text.Pandoc.Compat.Time @@ -65,7 +65,7 @@ writeOPML opts (Pandoc meta blocks) = do writeHtmlInlines :: PandocMonad m => [Inline] -> m String writeHtmlInlines ils = - trim <$> (writeHtmlString def $ Pandoc nullMeta [Plain ils]) + trim <$> (writeHtml5String def $ Pandoc nullMeta [Plain ils]) -- date format: RFC 822: Thu, 14 Jul 2005 23:41:05 GMT showDateTimeRFC822 :: UTCTime -> String diff --git a/tests/Tests/Old.hs b/tests/Tests/Old.hs index 21e00b033..a46ac2260 100644 --- a/tests/Tests/Old.hs +++ b/tests/Tests/Old.hs @@ -81,16 +81,17 @@ tests = [ testGroup "markdown" ] ] , testGroup "html" - [ testGroup "writer" (writerTests "html" ++ lhsWriterTests "html") + [ testGroup "writer" (writerTests "html4" ++ writerTests "html5" ++ + lhsWriterTests "html") , test "reader" ["-r", "html", "-w", "native", "-s"] "html-reader.html" "html-reader.native" ] , testGroup "s5" [ s5WriterTest "basic" ["-s"] "s5" , s5WriterTest "fancy" ["-s","-m","-i"] "s5" - , s5WriterTest "fragment" [] "html" + , s5WriterTest "fragment" [] "html4" , s5WriterTest "inserts" ["-s", "-H", "insert", - "-B", "insert", "-A", "insert", "-c", "main.css"] "html" + "-B", "insert", "-A", "insert", "-c", "main.css"] "html4" ] , testGroup "textile" [ testGroup "writer" $ writerTests "textile" diff --git a/tests/Tests/Writers/HTML.hs b/tests/Tests/Writers/HTML.hs index d99698c21..45de2b042 100644 --- a/tests/Tests/Writers/HTML.hs +++ b/tests/Tests/Writers/HTML.hs @@ -8,7 +8,7 @@ import Tests.Helpers import Text.Pandoc.Arbitrary() html :: (ToPandoc a) => a -> String -html = purely (writeHtmlString def{ writerWrapText = WrapNone }) . toPandoc +html = purely (writeHtml4String def{ writerWrapText = WrapNone }) . toPandoc {- "my test" =: X =?> Y diff --git a/tests/lhs-test.html b/tests/lhs-test.html index e4a5b3868..2c3b6b0f8 100644 --- a/tests/lhs-test.html +++ b/tests/lhs-test.html @@ -1,9 +1,9 @@ - - + + - - - + + + +

lhs test

diff --git a/tests/lhs-test.html+lhs b/tests/lhs-test.html+lhs index 41e9ca283..443b0642f 100644 --- a/tests/lhs-test.html+lhs +++ b/tests/lhs-test.html+lhs @@ -1,9 +1,9 @@ - - + + - - - + + + +

lhs test

diff --git a/tests/tables.html b/tests/tables.html deleted file mode 100644 index 5bb7a7de2..000000000 --- a/tests/tables.html +++ /dev/null @@ -1,204 +0,0 @@ -

Simple table with caption:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Demonstration of simple table syntax.
RightLeftCenterDefault
12121212
123123123123
1111
-

Simple table without caption:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RightLeftCenterDefault
12121212
123123123123
1111
-

Simple table indented two spaces:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Demonstration of simple table syntax.
RightLeftCenterDefault
12121212
123123123123
1111
-

Multiline table with caption:

- - ------ - - - - - - - - - - - - - - - - - - - - - - -
Here’s the caption. It may span multiple lines.
Centered HeaderLeft AlignedRight AlignedDefault aligned
Firstrow12.0Example of a row that spans multiple lines.
Secondrow5.0Here’s another one. Note the blank line between rows.
-

Multiline table without caption:

- ------ - - - - - - - - - - - - - - - - - - - - - - -
Centered HeaderLeft AlignedRight AlignedDefault aligned
Firstrow12.0Example of a row that spans multiple lines.
Secondrow5.0Here’s another one. Note the blank line between rows.
-

Table without column headers:

- - - - - - - - - - - - - - - - - - - - - -
12121212
123123123123
1111
-

Multiline table without column headers:

- ------ - - - - - - - - - - - - - - -
Firstrow12.0Example of a row that spans multiple lines.
Secondrow5.0Here’s another one. Note the blank line between rows.
diff --git a/tests/tables.html4 b/tests/tables.html4 new file mode 100644 index 000000000..5bb7a7de2 --- /dev/null +++ b/tests/tables.html4 @@ -0,0 +1,204 @@ +

Simple table with caption:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Demonstration of simple table syntax.
RightLeftCenterDefault
12121212
123123123123
1111
+

Simple table without caption:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
RightLeftCenterDefault
12121212
123123123123
1111
+

Simple table indented two spaces:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Demonstration of simple table syntax.
RightLeftCenterDefault
12121212
123123123123
1111
+

Multiline table with caption:

+ + ++++++ + + + + + + + + + + + + + + + + + + + + + + +
Here’s the caption. It may span multiple lines.
Centered HeaderLeft AlignedRight AlignedDefault aligned
Firstrow12.0Example of a row that spans multiple lines.
Secondrow5.0Here’s another one. Note the blank line between rows.
+

Multiline table without caption:

+ ++++++ + + + + + + + + + + + + + + + + + + + + + + +
Centered HeaderLeft AlignedRight AlignedDefault aligned
Firstrow12.0Example of a row that spans multiple lines.
Secondrow5.0Here’s another one. Note the blank line between rows.
+

Table without column headers:

+ + + + + + + + + + + + + + + + + + + + + +
12121212
123123123123
1111
+

Multiline table without column headers:

+ ++++++ + + + + + + + + + + + + + + +
Firstrow12.0Example of a row that spans multiple lines.
Secondrow5.0Here’s another one. Note the blank line between rows.
diff --git a/tests/tables.html5 b/tests/tables.html5 new file mode 100644 index 000000000..17a82110f --- /dev/null +++ b/tests/tables.html5 @@ -0,0 +1,204 @@ +

Simple table with caption:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Demonstration of simple table syntax.
RightLeftCenterDefault
12121212
123123123123
1111
+

Simple table without caption:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
RightLeftCenterDefault
12121212
123123123123
1111
+

Simple table indented two spaces:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Demonstration of simple table syntax.
RightLeftCenterDefault
12121212
123123123123
1111
+

Multiline table with caption:

+ + ++++++ + + + + + + + + + + + + + + + + + + + + + + +
Here’s the caption. It may span multiple lines.
Centered HeaderLeft AlignedRight AlignedDefault aligned
Firstrow12.0Example of a row that spans multiple lines.
Secondrow5.0Here’s another one. Note the blank line between rows.
+

Multiline table without caption:

+ ++++++ + + + + + + + + + + + + + + + + + + + + + + +
Centered HeaderLeft AlignedRight AlignedDefault aligned
Firstrow12.0Example of a row that spans multiple lines.
Secondrow5.0Here’s another one. Note the blank line between rows.
+

Table without column headers:

+ + + + + + + + + + + + + + + + + + + + + +
12121212
123123123123
1111
+

Multiline table without column headers:

+ ++++++ + + + + + + + + + + + + + + +
Firstrow12.0Example of a row that spans multiple lines.
Secondrow5.0Here’s another one. Note the blank line between rows.
diff --git a/tests/writer.html b/tests/writer.html deleted file mode 100644 index 3b63f4e16..000000000 --- a/tests/writer.html +++ /dev/null @@ -1,546 +0,0 @@ - - - - - - - - - - Pandoc Test Suite - - - - -

This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite.

-
-

Headers

- -

Level 3 with emphasis

-

Level 4

-
Level 5
-

Level 1

-

Level 2 with emphasis

-

Level 3

-

with no blank line

-

Level 2

-

with no blank line

-
-

Paragraphs

-

Here’s a regular paragraph.

-

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.

-

Here’s one with a bullet. * criminey.

-

There should be a hard line break
-here.

-
-

Block Quotes

-

E-mail style:

-
-

This is a block quote. It is pretty short.

-
-
-

Code in a block quote:

-
sub status {
-    print "working";
-}
-

A list:

-
    -
  1. item one
  2. -
  3. item two
  4. -
-

Nested block quotes:

-
-

nested

-
-
-

nested

-
-
-

This should not be a block quote: 2 > 1.

-

And a following paragraph.

-
-

Code Blocks

-

Code:

-
---- (should be four hyphens)
-
-sub status {
-    print "working";
-}
-
-this code block is indented by one tab
-

And:

-
    this code block is indented by two tabs
-
-These should not be escaped:  \$ \\ \> \[ \{
-
-

Lists

-

Unordered

-

Asterisks tight:

-
    -
  • asterisk 1
  • -
  • asterisk 2
  • -
  • asterisk 3
  • -
-

Asterisks loose:

-
    -
  • asterisk 1

  • -
  • asterisk 2

  • -
  • asterisk 3

  • -
-

Pluses tight:

-
    -
  • Plus 1
  • -
  • Plus 2
  • -
  • Plus 3
  • -
-

Pluses loose:

-
    -
  • Plus 1

  • -
  • Plus 2

  • -
  • Plus 3

  • -
-

Minuses tight:

-
    -
  • Minus 1
  • -
  • Minus 2
  • -
  • Minus 3
  • -
-

Minuses loose:

-
    -
  • Minus 1

  • -
  • Minus 2

  • -
  • Minus 3

  • -
-

Ordered

-

Tight:

-
    -
  1. First
  2. -
  3. Second
  4. -
  5. Third
  6. -
-

and:

-
    -
  1. One
  2. -
  3. Two
  4. -
  5. Three
  6. -
-

Loose using tabs:

-
    -
  1. First

  2. -
  3. Second

  4. -
  5. Third

  6. -
-

and using spaces:

-
    -
  1. One

  2. -
  3. Two

  4. -
  5. Three

  6. -
-

Multiple paragraphs:

-
    -
  1. Item 1, graf one.

    -

    Item 1. graf two. The quick brown fox jumped over the lazy dog’s back.

  2. -
  3. Item 2.

  4. -
  5. Item 3.

  6. -
-

Nested

-
    -
  • Tab -
      -
    • Tab -
        -
      • Tab
      • -
    • -
  • -
-

Here’s another:

-
    -
  1. First
  2. -
  3. Second: -
      -
    • Fee
    • -
    • Fie
    • -
    • Foe
    • -
  4. -
  5. Third
  6. -
-

Same thing but with paragraphs:

-
    -
  1. First

  2. -
  3. Second:

    -
      -
    • Fee
    • -
    • Fie
    • -
    • Foe
    • -
  4. -
  5. Third

  6. -
-

Tabs and spaces

-
    -
  • this is a list item indented with tabs

  • -
  • this is a list item indented with spaces

    -
      -
    • this is an example list item indented with tabs

    • -
    • this is an example list item indented with spaces

    • -
  • -
-

Fancy list markers

-
    -
  1. begins with 2
  2. -
  3. and now 3

    -

    with a continuation

    -
      -
    1. sublist with roman numerals, starting with 4
    2. -
    3. more items -
        -
      1. a subsublist
      2. -
      3. a subsublist
      4. -
    4. -
  4. -
-

Nesting:

-
    -
  1. Upper Alpha -
      -
    1. Upper Roman. -
        -
      1. Decimal start with 6 -
          -
        1. Lower alpha with paren
        2. -
      2. -
    2. -
  2. -
-

Autonumbering:

-
    -
  1. Autonumber.
  2. -
  3. More. -
      -
    1. Nested.
    2. -
  4. -
-

Should not be a list item:

-

M.A. 2007

-

B. Williams

-
-

Definition Lists

-

Tight using spaces:

-
-
apple
-
red fruit -
-
orange
-
orange fruit -
-
banana
-
yellow fruit -
-
-

Tight using tabs:

-
-
apple
-
red fruit -
-
orange
-
orange fruit -
-
banana
-
yellow fruit -
-
-

Loose:

-
-
apple
-

red fruit

-
-
orange
-

orange fruit

-
-
banana
-

yellow fruit

-
-
-

Multiple blocks with italics:

-
-
apple
-

red fruit

-

contains seeds, crisp, pleasant to taste

-
-
orange
-

orange fruit

-
{ orange code block }
-
-

orange block quote

-
-
-
-

Multiple definitions, tight:

-
-
apple
-
red fruit -
-
computer -
-
orange
-
orange fruit -
-
bank -
-
-

Multiple definitions, loose:

-
-
apple
-

red fruit

-
-

computer

-
-
orange
-

orange fruit

-
-

bank

-
-
-

Blank line after term, indented marker, alternate markers:

-
-
apple
-

red fruit

-
-

computer

-
-
orange
-

orange fruit

-
    -
  1. sublist
  2. -
  3. sublist
  4. -
-
-
-

HTML Blocks

-

Simple block on one line:

-
-foo -
-

And nested without indentation:

-
-
-
-

foo

-
-
-
-bar -
-
-

Interpreted markdown in a table:

- - - - - -
-This is emphasized - -And this is strong -
- -

Here’s a simple block:

-
-

foo

-
-

This should be a code block, though:

-
<div>
-    foo
-</div>
-

As should this:

-
<div>foo</div>
-

Now, nested:

-
-
-
-foo -
-
-
-

This should just be an HTML comment:

- -

Multiline:

- - -

Code block:

-
<!-- Comment -->
-

Just plain comment, with trailing spaces on the line:

- -

Code:

-
<hr />
-

Hr’s:

-
-
-
-
-
-
-
-
-
-
-

Inline Markup

-

This is emphasized, and so is this.

-

This is strong, and so is this.

-

An emphasized link.

-

This is strong and em.

-

So is this word.

-

This is strong and em.

-

So is this word.

-

This is code: >, $, \, \$, <html>.

-

This is strikeout.

-

Superscripts: abcd ahello ahello there.

-

Subscripts: H2O, H23O, Hmany of themO.

-

These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d.

-
-

Smart quotes, ellipses, dashes

-

“Hello,” said the spider. “‘Shelob’ is my name.”

-

‘A’, ‘B’, and ‘C’ are letters.

-

‘Oak,’ ‘elm,’ and ‘beech’ are names of trees. So is ‘pine.’

-

‘He said, “I want to go.”’ Were you alive in the 70’s?

-

Here is some quoted ‘code’ and a “quoted link”.

-

Some dashes: one—two — three—four — five.

-

Dashes between numbers: 5–7, 255–66, 1987–1999.

-

Ellipses…and…and….

-
-

LaTeX

-
    -
  • -
  • 2 + 2 = 4
  • -
  • x ∈ y
  • -
  • α ∧ ω
  • -
  • 223
  • -
  • p-Tree
  • -
  • Here’s some display math:
    $$\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$$
  • -
  • Here’s one that has a line break in it: α + ω × x2.
  • -
-

These shouldn’t be math:

-
    -
  • To get the famous equation, write $e = mc^2$.
  • -
  • $22,000 is a lot of money. So is $34,000. (It worked if “lot” is emphasized.)
  • -
  • Shoes ($20) and socks ($5).
  • -
  • Escaped $: $73 this should be emphasized 23$.
  • -
-

Here’s a LaTeX table:

- -
-

Special Characters

-

Here is some unicode:

-
    -
  • I hat: Î
  • -
  • o umlaut: ö
  • -
  • section: §
  • -
  • set membership: ∈
  • -
  • copyright: ©
  • -
-

AT&T has an ampersand in their name.

-

AT&T is another way to write it.

-

This & that.

-

4 < 5.

-

6 > 5.

-

Backslash: \

-

Backtick: `

-

Asterisk: *

-

Underscore: _

-

Left brace: {

-

Right brace: }

-

Left bracket: [

-

Right bracket: ]

-

Left paren: (

-

Right paren: )

-

Greater-than: >

-

Hash: #

-

Period: .

-

Bang: !

-

Plus: +

-

Minus: -

-
-

Links

-

Explicit

-

Just a URL.

-

URL and title.

-

URL and title.

-

URL and title.

-

URL and title

-

URL and title

-

with_underscore

-

Email link

-

Empty.

-

Reference

-

Foo bar.

-

Foo bar.

-

Foo bar.

-

With embedded [brackets].

-

b by itself should be a link.

-

Indented once.

-

Indented twice.

-

Indented thrice.

-

This should [not][] be a link.

-
[not]: /url
-

Foo bar.

-

Foo biz.

-

With ampersands

-

Here’s a link with an ampersand in the URL.

-

Here’s a link with an amersand in the link text: AT&T.

-

Here’s an inline link.

-

Here’s an inline link in pointy braces.

- -

With an ampersand: http://example.com/?foo=1&bar=2

- -

An e-mail address: nobody@nowhere.net

-
-

Blockquoted: http://example.com/

-
-

Auto-links should not occur here: <http://example.com/>

-
or here: <http://example.com/>
-
-

Images

-

From “Voyage dans la Lune” by Georges Melies (1902):

-
-lalune -

lalune

-
-

Here is a movie movie icon.

-
-

Footnotes

-

Here is a footnote reference,1 and another.2 This should not be a footnote reference, because it contains a space.[^my note] Here is an inline note.3

-
-

Notes can go in quotes.4

-
-
    -
  1. And in list items.5
  2. -
-

This paragraph should not be part of the note, as it is not indented.

-
-
-
    -
  1. Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document.

  2. -
  3. Here’s the long note. This one contains multiple blocks.

    -

    Subsequent blocks are indented to show that they belong to the footnote (as with list items).

    -
      { <code> }
    -

    If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.

  4. -
  5. This is easier to type. Inline notes may contain links and ] verbatim characters, as well as [bracketed text].

  6. -
  7. In quote.

  8. -
  9. In list.

  10. -
-
- - diff --git a/tests/writer.html4 b/tests/writer.html4 new file mode 100644 index 000000000..3b63f4e16 --- /dev/null +++ b/tests/writer.html4 @@ -0,0 +1,546 @@ + + + + + + + + + + Pandoc Test Suite + + + + +

This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite.

+
+

Headers

+ +

Level 3 with emphasis

+

Level 4

+
Level 5
+

Level 1

+

Level 2 with emphasis

+

Level 3

+

with no blank line

+

Level 2

+

with no blank line

+
+

Paragraphs

+

Here’s a regular paragraph.

+

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.

+

Here’s one with a bullet. * criminey.

+

There should be a hard line break
+here.

+
+

Block Quotes

+

E-mail style:

+
+

This is a block quote. It is pretty short.

+
+
+

Code in a block quote:

+
sub status {
+    print "working";
+}
+

A list:

+
    +
  1. item one
  2. +
  3. item two
  4. +
+

Nested block quotes:

+
+

nested

+
+
+

nested

+
+
+

This should not be a block quote: 2 > 1.

+

And a following paragraph.

+
+

Code Blocks

+

Code:

+
---- (should be four hyphens)
+
+sub status {
+    print "working";
+}
+
+this code block is indented by one tab
+

And:

+
    this code block is indented by two tabs
+
+These should not be escaped:  \$ \\ \> \[ \{
+
+

Lists

+

Unordered

+

Asterisks tight:

+
    +
  • asterisk 1
  • +
  • asterisk 2
  • +
  • asterisk 3
  • +
+

Asterisks loose:

+
    +
  • asterisk 1

  • +
  • asterisk 2

  • +
  • asterisk 3

  • +
+

Pluses tight:

+
    +
  • Plus 1
  • +
  • Plus 2
  • +
  • Plus 3
  • +
+

Pluses loose:

+
    +
  • Plus 1

  • +
  • Plus 2

  • +
  • Plus 3

  • +
+

Minuses tight:

+
    +
  • Minus 1
  • +
  • Minus 2
  • +
  • Minus 3
  • +
+

Minuses loose:

+
    +
  • Minus 1

  • +
  • Minus 2

  • +
  • Minus 3

  • +
+

Ordered

+

Tight:

+
    +
  1. First
  2. +
  3. Second
  4. +
  5. Third
  6. +
+

and:

+
    +
  1. One
  2. +
  3. Two
  4. +
  5. Three
  6. +
+

Loose using tabs:

+
    +
  1. First

  2. +
  3. Second

  4. +
  5. Third

  6. +
+

and using spaces:

+
    +
  1. One

  2. +
  3. Two

  4. +
  5. Three

  6. +
+

Multiple paragraphs:

+
    +
  1. Item 1, graf one.

    +

    Item 1. graf two. The quick brown fox jumped over the lazy dog’s back.

  2. +
  3. Item 2.

  4. +
  5. Item 3.

  6. +
+

Nested

+
    +
  • Tab +
      +
    • Tab +
        +
      • Tab
      • +
    • +
  • +
+

Here’s another:

+
    +
  1. First
  2. +
  3. Second: +
      +
    • Fee
    • +
    • Fie
    • +
    • Foe
    • +
  4. +
  5. Third
  6. +
+

Same thing but with paragraphs:

+
    +
  1. First

  2. +
  3. Second:

    +
      +
    • Fee
    • +
    • Fie
    • +
    • Foe
    • +
  4. +
  5. Third

  6. +
+

Tabs and spaces

+
    +
  • this is a list item indented with tabs

  • +
  • this is a list item indented with spaces

    +
      +
    • this is an example list item indented with tabs

    • +
    • this is an example list item indented with spaces

    • +
  • +
+

Fancy list markers

+
    +
  1. begins with 2
  2. +
  3. and now 3

    +

    with a continuation

    +
      +
    1. sublist with roman numerals, starting with 4
    2. +
    3. more items +
        +
      1. a subsublist
      2. +
      3. a subsublist
      4. +
    4. +
  4. +
+

Nesting:

+
    +
  1. Upper Alpha +
      +
    1. Upper Roman. +
        +
      1. Decimal start with 6 +
          +
        1. Lower alpha with paren
        2. +
      2. +
    2. +
  2. +
+

Autonumbering:

+
    +
  1. Autonumber.
  2. +
  3. More. +
      +
    1. Nested.
    2. +
  4. +
+

Should not be a list item:

+

M.A. 2007

+

B. Williams

+
+

Definition Lists

+

Tight using spaces:

+
+
apple
+
red fruit +
+
orange
+
orange fruit +
+
banana
+
yellow fruit +
+
+

Tight using tabs:

+
+
apple
+
red fruit +
+
orange
+
orange fruit +
+
banana
+
yellow fruit +
+
+

Loose:

+
+
apple
+

red fruit

+
+
orange
+

orange fruit

+
+
banana
+

yellow fruit

+
+
+

Multiple blocks with italics:

+
+
apple
+

red fruit

+

contains seeds, crisp, pleasant to taste

+
+
orange
+

orange fruit

+
{ orange code block }
+
+

orange block quote

+
+
+
+

Multiple definitions, tight:

+
+
apple
+
red fruit +
+
computer +
+
orange
+
orange fruit +
+
bank +
+
+

Multiple definitions, loose:

+
+
apple
+

red fruit

+
+

computer

+
+
orange
+

orange fruit

+
+

bank

+
+
+

Blank line after term, indented marker, alternate markers:

+
+
apple
+

red fruit

+
+

computer

+
+
orange
+

orange fruit

+
    +
  1. sublist
  2. +
  3. sublist
  4. +
+
+
+

HTML Blocks

+

Simple block on one line:

+
+foo +
+

And nested without indentation:

+
+
+
+

foo

+
+
+
+bar +
+
+

Interpreted markdown in a table:

+ + + + + +
+This is emphasized + +And this is strong +
+ +

Here’s a simple block:

+
+

foo

+
+

This should be a code block, though:

+
<div>
+    foo
+</div>
+

As should this:

+
<div>foo</div>
+

Now, nested:

+
+
+
+foo +
+
+
+

This should just be an HTML comment:

+ +

Multiline:

+ + +

Code block:

+
<!-- Comment -->
+

Just plain comment, with trailing spaces on the line:

+ +

Code:

+
<hr />
+

Hr’s:

+
+
+
+
+
+
+
+
+
+
+

Inline Markup

+

This is emphasized, and so is this.

+

This is strong, and so is this.

+

An emphasized link.

+

This is strong and em.

+

So is this word.

+

This is strong and em.

+

So is this word.

+

This is code: >, $, \, \$, <html>.

+

This is strikeout.

+

Superscripts: abcd ahello ahello there.

+

Subscripts: H2O, H23O, Hmany of themO.

+

These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d.

+
+

Smart quotes, ellipses, dashes

+

“Hello,” said the spider. “‘Shelob’ is my name.”

+

‘A’, ‘B’, and ‘C’ are letters.

+

‘Oak,’ ‘elm,’ and ‘beech’ are names of trees. So is ‘pine.’

+

‘He said, “I want to go.”’ Were you alive in the 70’s?

+

Here is some quoted ‘code’ and a “quoted link”.

+

Some dashes: one—two — three—four — five.

+

Dashes between numbers: 5–7, 255–66, 1987–1999.

+

Ellipses…and…and….

+
+

LaTeX

+
    +
  • +
  • 2 + 2 = 4
  • +
  • x ∈ y
  • +
  • α ∧ ω
  • +
  • 223
  • +
  • p-Tree
  • +
  • Here’s some display math:
    $$\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$$
  • +
  • Here’s one that has a line break in it: α + ω × x2.
  • +
+

These shouldn’t be math:

+
    +
  • To get the famous equation, write $e = mc^2$.
  • +
  • $22,000 is a lot of money. So is $34,000. (It worked if “lot” is emphasized.)
  • +
  • Shoes ($20) and socks ($5).
  • +
  • Escaped $: $73 this should be emphasized 23$.
  • +
+

Here’s a LaTeX table:

+ +
+

Special Characters

+

Here is some unicode:

+
    +
  • I hat: Î
  • +
  • o umlaut: ö
  • +
  • section: §
  • +
  • set membership: ∈
  • +
  • copyright: ©
  • +
+

AT&T has an ampersand in their name.

+

AT&T is another way to write it.

+

This & that.

+

4 < 5.

+

6 > 5.

+

Backslash: \

+

Backtick: `

+

Asterisk: *

+

Underscore: _

+

Left brace: {

+

Right brace: }

+

Left bracket: [

+

Right bracket: ]

+

Left paren: (

+

Right paren: )

+

Greater-than: >

+

Hash: #

+

Period: .

+

Bang: !

+

Plus: +

+

Minus: -

+
+

Links

+

Explicit

+

Just a URL.

+

URL and title.

+

URL and title.

+

URL and title.

+

URL and title

+

URL and title

+

with_underscore

+

Email link

+

Empty.

+

Reference

+

Foo bar.

+

Foo bar.

+

Foo bar.

+

With embedded [brackets].

+

b by itself should be a link.

+

Indented once.

+

Indented twice.

+

Indented thrice.

+

This should [not][] be a link.

+
[not]: /url
+

Foo bar.

+

Foo biz.

+

With ampersands

+

Here’s a link with an ampersand in the URL.

+

Here’s a link with an amersand in the link text: AT&T.

+

Here’s an inline link.

+

Here’s an inline link in pointy braces.

+ +

With an ampersand: http://example.com/?foo=1&bar=2

+ +

An e-mail address: nobody@nowhere.net

+
+

Blockquoted: http://example.com/

+
+

Auto-links should not occur here: <http://example.com/>

+
or here: <http://example.com/>
+
+

Images

+

From “Voyage dans la Lune” by Georges Melies (1902):

+
+lalune +

lalune

+
+

Here is a movie movie icon.

+
+

Footnotes

+

Here is a footnote reference,1 and another.2 This should not be a footnote reference, because it contains a space.[^my note] Here is an inline note.3

+
+

Notes can go in quotes.4

+
+
    +
  1. And in list items.5
  2. +
+

This paragraph should not be part of the note, as it is not indented.

+
+
+
    +
  1. Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document.

  2. +
  3. Here’s the long note. This one contains multiple blocks.

    +

    Subsequent blocks are indented to show that they belong to the footnote (as with list items).

    +
      { <code> }
    +

    If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.

  4. +
  5. This is easier to type. Inline notes may contain links and ] verbatim characters, as well as [bracketed text].

  6. +
  7. In quote.

  8. +
  9. In list.

  10. +
+
+ + diff --git a/tests/writer.html5 b/tests/writer.html5 new file mode 100644 index 000000000..8e0dff764 --- /dev/null +++ b/tests/writer.html5 @@ -0,0 +1,548 @@ + + + + + + + + + + Pandoc Test Suite + + + + +
+

Pandoc Test Suite

+

John MacFarlane

+

Anonymous

+

July 17, 2006

+
+

This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite.

+
+

Headers

+ +

Level 3 with emphasis

+

Level 4

+
Level 5
+

Level 1

+

Level 2 with emphasis

+

Level 3

+

with no blank line

+

Level 2

+

with no blank line

+
+

Paragraphs

+

Here’s a regular paragraph.

+

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.

+

Here’s one with a bullet. * criminey.

+

There should be a hard line break
+here.

+
+

Block Quotes

+

E-mail style:

+
+

This is a block quote. It is pretty short.

+
+
+

Code in a block quote:

+
sub status {
+    print "working";
+}
+

A list:

+
    +
  1. item one
  2. +
  3. item two
  4. +
+

Nested block quotes:

+
+

nested

+
+
+

nested

+
+
+

This should not be a block quote: 2 > 1.

+

And a following paragraph.

+
+

Code Blocks

+

Code:

+
---- (should be four hyphens)
+
+sub status {
+    print "working";
+}
+
+this code block is indented by one tab
+

And:

+
    this code block is indented by two tabs
+
+These should not be escaped:  \$ \\ \> \[ \{
+
+

Lists

+

Unordered

+

Asterisks tight:

+
    +
  • asterisk 1
  • +
  • asterisk 2
  • +
  • asterisk 3
  • +
+

Asterisks loose:

+
    +
  • asterisk 1

  • +
  • asterisk 2

  • +
  • asterisk 3

  • +
+

Pluses tight:

+
    +
  • Plus 1
  • +
  • Plus 2
  • +
  • Plus 3
  • +
+

Pluses loose:

+
    +
  • Plus 1

  • +
  • Plus 2

  • +
  • Plus 3

  • +
+

Minuses tight:

+
    +
  • Minus 1
  • +
  • Minus 2
  • +
  • Minus 3
  • +
+

Minuses loose:

+
    +
  • Minus 1

  • +
  • Minus 2

  • +
  • Minus 3

  • +
+

Ordered

+

Tight:

+
    +
  1. First
  2. +
  3. Second
  4. +
  5. Third
  6. +
+

and:

+
    +
  1. One
  2. +
  3. Two
  4. +
  5. Three
  6. +
+

Loose using tabs:

+
    +
  1. First

  2. +
  3. Second

  4. +
  5. Third

  6. +
+

and using spaces:

+
    +
  1. One

  2. +
  3. Two

  4. +
  5. Three

  6. +
+

Multiple paragraphs:

+
    +
  1. Item 1, graf one.

    +

    Item 1. graf two. The quick brown fox jumped over the lazy dog’s back.

  2. +
  3. Item 2.

  4. +
  5. Item 3.

  6. +
+

Nested

+
    +
  • Tab +
      +
    • Tab +
        +
      • Tab
      • +
    • +
  • +
+

Here’s another:

+
    +
  1. First
  2. +
  3. Second: +
      +
    • Fee
    • +
    • Fie
    • +
    • Foe
    • +
  4. +
  5. Third
  6. +
+

Same thing but with paragraphs:

+
    +
  1. First

  2. +
  3. Second:

    +
      +
    • Fee
    • +
    • Fie
    • +
    • Foe
    • +
  4. +
  5. Third

  6. +
+

Tabs and spaces

+
    +
  • this is a list item indented with tabs

  • +
  • this is a list item indented with spaces

    +
      +
    • this is an example list item indented with tabs

    • +
    • this is an example list item indented with spaces

    • +
  • +
+

Fancy list markers

+
    +
  1. begins with 2
  2. +
  3. and now 3

    +

    with a continuation

    +
      +
    1. sublist with roman numerals, starting with 4
    2. +
    3. more items +
        +
      1. a subsublist
      2. +
      3. a subsublist
      4. +
    4. +
  4. +
+

Nesting:

+
    +
  1. Upper Alpha +
      +
    1. Upper Roman. +
        +
      1. Decimal start with 6 +
          +
        1. Lower alpha with paren
        2. +
      2. +
    2. +
  2. +
+

Autonumbering:

+
    +
  1. Autonumber.
  2. +
  3. More. +
      +
    1. Nested.
    2. +
  4. +
+

Should not be a list item:

+

M.A. 2007

+

B. Williams

+
+

Definition Lists

+

Tight using spaces:

+
+
apple
+
red fruit +
+
orange
+
orange fruit +
+
banana
+
yellow fruit +
+
+

Tight using tabs:

+
+
apple
+
red fruit +
+
orange
+
orange fruit +
+
banana
+
yellow fruit +
+
+

Loose:

+
+
apple
+

red fruit

+
+
orange
+

orange fruit

+
+
banana
+

yellow fruit

+
+
+

Multiple blocks with italics:

+
+
apple
+

red fruit

+

contains seeds, crisp, pleasant to taste

+
+
orange
+

orange fruit

+
{ orange code block }
+
+

orange block quote

+
+
+
+

Multiple definitions, tight:

+
+
apple
+
red fruit +
+
computer +
+
orange
+
orange fruit +
+
bank +
+
+

Multiple definitions, loose:

+
+
apple
+

red fruit

+
+

computer

+
+
orange
+

orange fruit

+
+

bank

+
+
+

Blank line after term, indented marker, alternate markers:

+
+
apple
+

red fruit

+
+

computer

+
+
orange
+

orange fruit

+
    +
  1. sublist
  2. +
  3. sublist
  4. +
+
+
+

HTML Blocks

+

Simple block on one line:

+
+foo +
+

And nested without indentation:

+
+
+
+

foo

+
+
+
+bar +
+
+

Interpreted markdown in a table:

+ + + + + +
+This is emphasized + +And this is strong +
+ +

Here’s a simple block:

+
+

foo

+
+

This should be a code block, though:

+
<div>
+    foo
+</div>
+

As should this:

+
<div>foo</div>
+

Now, nested:

+
+
+
+foo +
+
+
+

This should just be an HTML comment:

+ +

Multiline:

+ + +

Code block:

+
<!-- Comment -->
+

Just plain comment, with trailing spaces on the line:

+ +

Code:

+
<hr />
+

Hr’s:

+
+
+
+
+
+
+
+
+
+
+

Inline Markup

+

This is emphasized, and so is this.

+

This is strong, and so is this.

+

An emphasized link.

+

This is strong and em.

+

So is this word.

+

This is strong and em.

+

So is this word.

+

This is code: >, $, \, \$, <html>.

+

This is strikeout.

+

Superscripts: abcd ahello ahello there.

+

Subscripts: H2O, H23O, Hmany of themO.

+

These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d.

+
+

Smart quotes, ellipses, dashes

+

“Hello,” said the spider. “‘Shelob’ is my name.”

+

‘A’, ‘B’, and ‘C’ are letters.

+

‘Oak,’ ‘elm,’ and ‘beech’ are names of trees. So is ‘pine.’

+

‘He said, “I want to go.”’ Were you alive in the 70’s?

+

Here is some quoted ‘code’ and a “quoted link”.

+

Some dashes: one—two — three—four — five.

+

Dashes between numbers: 5–7, 255–66, 1987–1999.

+

Ellipses…and…and….

+
+

LaTeX

+
    +
  • +
  • 2 + 2 = 4
  • +
  • x ∈ y
  • +
  • α ∧ ω
  • +
  • 223
  • +
  • p-Tree
  • +
  • Here’s some display math:
    $$\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$$
  • +
  • Here’s one that has a line break in it: α + ω × x2.
  • +
+

These shouldn’t be math:

+
    +
  • To get the famous equation, write $e = mc^2$.
  • +
  • $22,000 is a lot of money. So is $34,000. (It worked if “lot” is emphasized.)
  • +
  • Shoes ($20) and socks ($5).
  • +
  • Escaped $: $73 this should be emphasized 23$.
  • +
+

Here’s a LaTeX table:

+ +
+

Special Characters

+

Here is some unicode:

+
    +
  • I hat: Î
  • +
  • o umlaut: ö
  • +
  • section: §
  • +
  • set membership: ∈
  • +
  • copyright: ©
  • +
+

AT&T has an ampersand in their name.

+

AT&T is another way to write it.

+

This & that.

+

4 < 5.

+

6 > 5.

+

Backslash: \

+

Backtick: `

+

Asterisk: *

+

Underscore: _

+

Left brace: {

+

Right brace: }

+

Left bracket: [

+

Right bracket: ]

+

Left paren: (

+

Right paren: )

+

Greater-than: >

+

Hash: #

+

Period: .

+

Bang: !

+

Plus: +

+

Minus: -

+
+

Links

+

Explicit

+

Just a URL.

+

URL and title.

+

URL and title.

+

URL and title.

+

URL and title

+

URL and title

+

with_underscore

+

Email link

+

Empty.

+

Reference

+

Foo bar.

+

Foo bar.

+

Foo bar.

+

With embedded [brackets].

+

b by itself should be a link.

+

Indented once.

+

Indented twice.

+

Indented thrice.

+

This should [not][] be a link.

+
[not]: /url
+

Foo bar.

+

Foo biz.

+

With ampersands

+

Here’s a link with an ampersand in the URL.

+

Here’s a link with an amersand in the link text: AT&T.

+

Here’s an inline link.

+

Here’s an inline link in pointy braces.

+ +

With an ampersand: http://example.com/?foo=1&bar=2

+ +

An e-mail address: nobody@nowhere.net

+
+

Blockquoted: http://example.com/

+
+

Auto-links should not occur here: <http://example.com/>

+
or here: <http://example.com/>
+
+

Images

+

From “Voyage dans la Lune” by Georges Melies (1902):

+
+lalune
lalune
+
+

Here is a movie movie icon.

+
+

Footnotes

+

Here is a footnote reference,1 and another.2 This should not be a footnote reference, because it contains a space.[^my note] Here is an inline note.3

+
+

Notes can go in quotes.4

+
+
    +
  1. And in list items.5
  2. +
+

This paragraph should not be part of the note, as it is not indented.

+
+
+
    +
  1. Here is the footnote. It can go anywhere after the footnote reference. It need not be placed at the end of the document.

  2. +
  3. Here’s the long note. This one contains multiple blocks.

    +

    Subsequent blocks are indented to show that they belong to the footnote (as with list items).

    +
      { <code> }
    +

    If you want, you can indent every line, but you can also be lazy and just indent the first line of each block.

  4. +
  5. This is easier to type. Inline notes may contain links and ] verbatim characters, as well as [bracketed text].

  6. +
  7. In quote.

  8. +
  9. In list.

  10. +
+
+ + -- cgit v1.2.3 From 190943e1fd75b7fa30689387e4416dd81b584f5e Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 26 Jan 2017 20:39:32 +0100 Subject: EPUB writer: split writeEPUB into writeEPUB2, writeEPUB3. Also include explicit epub2 output format in CLI tool. --- MANUAL.txt | 23 ++++++++++++----------- data/templates | 2 +- pandoc.cabal | 2 +- src/Text/Pandoc.hs | 12 +++++++----- src/Text/Pandoc/Templates.hs | 1 + src/Text/Pandoc/Writers/EPUB.hs | 31 ++++++++++++++++++++++++------- 6 files changed, 46 insertions(+), 25 deletions(-) (limited to 'src/Text') diff --git a/MANUAL.txt b/MANUAL.txt index 3b8ac2b85..91f4bacc0 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -101,7 +101,7 @@ If no *input-file* is specified, input is read from *stdin*. Otherwise, the *input-files* are concatenated (with a blank line between each) and used as input. Output goes to *stdout* by default (though output to *stdout* is disabled for the `odt`, `docx`, -`epub`, and `epub3` output formats). For output to a file, use the +`epub2`, and `epub3` output formats). For output to a file, use the `-o` option: pandoc -o output.html input.txt @@ -273,7 +273,7 @@ General options (original unextended Markdown), `markdown_phpextra` (PHP Markdown Extra), `markdown_github` (GitHub-Flavored Markdown), `markdown_mmd` (MultiMarkdown), `commonmark` (CommonMark Markdown), `rst` - (reStructuredText), `html` (XHTML), `html5` (HTML5), `latex` + (reStructuredText), `html4` (XHTML4), `html` or `html5` (HTML5), `latex` (LaTeX), `beamer` (LaTeX beamer slide show), `context` (ConTeXt), `man` (groff man), `mediawiki` (MediaWiki markup), `dokuwiki` (DokuWiki markup), `zimwiki` (ZimWiki markup), @@ -281,7 +281,7 @@ General options `texinfo` (GNU Texinfo), `opml` (OPML), `docbook` (DocBook 4), `docbook5` (DocBook 5), `opendocument` (OpenDocument), `odt` (OpenOffice text document), `docx` (Word docx), `haddock` - (Haddock markup), `rtf` (rich text format), `epub` (EPUB v2 + (Haddock markup), `rtf` (rich text format), `epub` or `epub2` (EPUB v2 book), `epub3` (EPUB v3), `fb2` (FictionBook2 e-book), `asciidoc` (AsciiDoc), `icml` (InDesign ICML), `tei` (TEI Simple), `slidy` (Slidy HTML and JavaScript slide show), @@ -293,7 +293,7 @@ General options `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`, `html`, or `html5`, the output will be rendered as + `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 @@ -626,7 +626,7 @@ Options affecting specific writers images, and videos. The resulting file should be "self-contained," in the sense that it needs no external files and no net access to be displayed properly by a browser. This option works only with HTML output - formats, including `html`, `html5`, `html+lhs`, `html5+lhs`, `s5`, + formats, including `html4`, `html5`, `html+lhs`, `html5+lhs`, `s5`, `slidy`, `slideous`, `dzslides`, and `revealjs`. Scripts, images, and stylesheets at absolute URLs will be downloaded; those at relative URLs will be sought relative to the working directory (if the first source @@ -947,10 +947,11 @@ Math rendering in HTML `--mathml`[`=`*URL*] -: Convert TeX math to [MathML] (in `docbook`, `docbook5`, `html` and `html5`). - In standalone `html` output, a small JavaScript (or a link to such a - script if a *URL* is supplied) will be inserted that allows the MathML to - be viewed on some browsers. +: Convert TeX math to [MathML] (in `docbook`, `docbook5`, + `html4` and `html5`). In standalone HTML output, a small + JavaScript (or a link to such a script if a *URL* is + supplied) will be inserted that allows the MathML to be + viewed on some browsers. `--jsmath`[`=`*URL*] @@ -1647,7 +1648,7 @@ 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), +be wrapped in a `div` (or a `section`, if `html5` was specified), and the identifier will be attached to the enclosing `
` (or `
`) tag rather than the header itself. This allows entire sections to be manipulated using JavaScript or treated differently in @@ -3891,7 +3892,7 @@ 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`, `html` or `html5` for output only), pandoc will treat the document as +`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 diff --git a/data/templates b/data/templates index 67d601119..335360e40 160000 --- a/data/templates +++ b/data/templates @@ -1 +1 @@ -Subproject commit 67d601119928f95c525dfb2c518ec61661f1e770 +Subproject commit 335360e40c5cd395b33954906144c834783b41fd diff --git a/pandoc.cabal b/pandoc.cabal index 97e70c830..341ab5a12 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -66,7 +66,7 @@ Data-Files: data/templates/default.haddock data/templates/default.textile data/templates/default.org - data/templates/default.epub + data/templates/default.epub2 data/templates/default.epub3 -- source files for reference.docx data/docx/[Content_Types].xml diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index aa4cab840..449cab120 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -115,7 +115,8 @@ module Text.Pandoc , writeRTF , writeODT , writeDocx - , writeEPUB + , writeEPUB2 + , writeEPUB3 , writeFB2 , writeOrg , writeAsciiDoc @@ -278,10 +279,9 @@ writers = [ ,("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 }) + ,("epub" , ByteStringWriter writeEPUB2) + ,("epub2" , ByteStringWriter writeEPUB2) + ,("epub3" , ByteStringWriter writeEPUB3) ,("fb2" , StringWriter writeFB2) ,("html" , StringWriter writeHtml5String) ,("html4" , StringWriter writeHtml4String) @@ -349,6 +349,8 @@ getDefaultExtensions "epub" = extensionsFromList Ext_native_divs, Ext_native_spans, Ext_epub_html_exts] +getDefaultExtensions "epub2" = getDefaultExtensions "epub" +getDefaultExtensions "epub3" = getDefaultExtensions "epub" getDefaultExtensions "latex" = extensionsFromList [Ext_smart, Ext_auto_identifiers] diff --git a/src/Text/Pandoc/Templates.hs b/src/Text/Pandoc/Templates.hs index 03dc917e6..38d956f1f 100644 --- a/src/Text/Pandoc/Templates.hs +++ b/src/Text/Pandoc/Templates.hs @@ -61,6 +61,7 @@ getDefaultTemplate user writer = do "fb2" -> return $ Right "" "odt" -> getDefaultTemplate user "opendocument" "html" -> getDefaultTemplate user "html5" + "epub" -> getDefaultTemplate user "epub2" "markdown_strict" -> getDefaultTemplate user "markdown" "multimarkdown" -> getDefaultTemplate user "markdown" "markdown_github" -> getDefaultTemplate user "markdown" diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index bd95c170e..c2fc4422e 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -28,7 +28,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Conversion of 'Pandoc' documents to EPUB. -} -module Text.Pandoc.Writers.EPUB ( writeEPUB ) where +module Text.Pandoc.Writers.EPUB ( writeEPUB2, writeEPUB3 ) where import qualified Data.Map as M import qualified Data.Set as Set import Data.Maybe ( fromMaybe, catMaybes ) @@ -75,8 +75,9 @@ import qualified Text.Pandoc.Class as P -- in filenames, chapter0003.xhtml. data Chapter = Chapter (Maybe [Int]) [Block] -data EPUBState = EPUBState { stMediaPaths :: [(FilePath, (FilePath, Maybe Entry))] - } +data EPUBState = EPUBState { + stMediaPaths :: [(FilePath, (FilePath, Maybe Entry))] + } type E m = StateT EPUBState m @@ -336,16 +337,32 @@ metadataFromMeta opts meta = EPUBMetadata{ Just "rtl" -> Just RTL _ -> Nothing +-- | Produce an EPUB2 file from a Pandoc document. +writeEPUB2 :: PandocMonad m + => WriterOptions -- ^ Writer options + -> Pandoc -- ^ Document to convert + -> m B.ByteString +writeEPUB2 = writeEPUB EPUB2 + +-- | Produce an EPUB3 file from a Pandoc document. +writeEPUB3 :: PandocMonad m + => WriterOptions -- ^ Writer options + -> Pandoc -- ^ Document to convert + -> m B.ByteString +writeEPUB3 = writeEPUB EPUB3 + -- | Produce an EPUB file from a Pandoc document. writeEPUB :: PandocMonad m - => WriterOptions -- ^ Writer options + => EPUBVersion + -> WriterOptions -- ^ Writer options -> Pandoc -- ^ Document to convert -> m B.ByteString -writeEPUB opts doc = +writeEPUB epubVersion opts doc = let initState = EPUBState { stMediaPaths = [] } in - evalStateT (pandocToEPUB opts doc) initState + evalStateT (pandocToEPUB opts{ writerEpubVersion = Just epubVersion } doc) + initState pandocToEPUB :: PandocMonad m => WriterOptions @@ -353,7 +370,7 @@ pandocToEPUB :: PandocMonad m -> E m B.ByteString pandocToEPUB opts doc@(Pandoc meta _) = do let version = fromMaybe EPUB2 (writerEpubVersion opts) - let epub3 = version == EPUB3 + let epub3 = writerEpubVersion opts == Just EPUB3 epochtime <- floor <$> lift P.getPOSIXTime let mkEntry path content = toEntry path epochtime content let vars = ("epub3", if epub3 then "true" else "false") -- cgit v1.2.3 From b6c1d491f5379f1924657f525540766dbbc1ae0f Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 26 Jan 2017 22:40:57 +0100 Subject: Split writeDocbook into writeDocbook4, writeDocbook5. Removed writerDocbookVersion in WriterOptions. Renamed default.docbook template to default.docbook4. Allow docbook4 as an output format. But alias docbook = docbook4. --- MANUAL.txt | 10 +- data/templates | 2 +- pandoc.cabal | 6 +- src/Text/Pandoc.hs | 9 +- src/Text/Pandoc/Options.hs | 2 - src/Text/Pandoc/Templates.hs | 15 +- src/Text/Pandoc/Writers/Docbook.hs | 62 +- tests/Tests/Old.hs | 2 +- tests/Tests/Writers/Docbook.hs | 2 +- tests/tables.docbook | 432 ----------- tests/tables.docbook4 | 432 +++++++++++ tests/writer.docbook | 1422 ------------------------------------ tests/writer.docbook4 | 1422 ++++++++++++++++++++++++++++++++++++ 13 files changed, 1918 insertions(+), 1900 deletions(-) delete mode 100644 tests/tables.docbook create mode 100644 tests/tables.docbook4 delete mode 100644 tests/writer.docbook create mode 100644 tests/writer.docbook4 (limited to 'src/Text') diff --git a/MANUAL.txt b/MANUAL.txt index 91f4bacc0..5d14773a5 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -278,9 +278,9 @@ General options `man` (groff man), `mediawiki` (MediaWiki markup), `dokuwiki` (DokuWiki markup), `zimwiki` (ZimWiki markup), `textile` (Textile), `org` (Emacs Org mode), - `texinfo` (GNU Texinfo), `opml` (OPML), `docbook` (DocBook 4), - `docbook5` (DocBook 5), `opendocument` (OpenDocument), `odt` - (OpenOffice text document), `docx` (Word docx), `haddock` + `texinfo` (GNU Texinfo), `opml` (OPML), `docbook` or `docbook4` + (DocBook 4), `docbook5` (DocBook 5), `opendocument` (OpenDocument), + `odt` (OpenOffice text document), `docx` (Word docx), `haddock` (Haddock markup), `rtf` (rich text format), `epub` or `epub2` (EPUB v2 book), `epub3` (EPUB v3), `fb2` (FictionBook2 e-book), `asciidoc` (AsciiDoc), `icml` (InDesign ICML), `tei` (TEI @@ -569,7 +569,7 @@ General writer options : Include an automatically generated table of contents (or, in the case of `latex`, `context`, `docx`, and `rst`, an instruction to create one) in the output document. This option has no effect on `man`, - `docbook`, `docbook5`, `slidy`, `slideous`, `s5`, or `odt` output. + `docbook4`, `docbook5`, `slidy`, `slideous`, `s5`, or `odt` output. `--toc-depth=`*NUMBER* @@ -947,7 +947,7 @@ Math rendering in HTML `--mathml`[`=`*URL*] -: Convert TeX math to [MathML] (in `docbook`, `docbook5`, +: Convert TeX math to [MathML] (in `docbook4`, `docbook5`, `html4` and `html5`). In standalone HTML output, a small JavaScript (or a link to such a script if a *URL* is supplied) will be inserted that allows the MathML to be diff --git a/data/templates b/data/templates index 335360e40..c4ba8bab6 160000 --- a/data/templates +++ b/data/templates @@ -1 +1 @@ -Subproject commit 335360e40c5cd395b33954906144c834783b41fd +Subproject commit c4ba8bab6248f8999e520547f1c45f10de85db9d diff --git a/pandoc.cabal b/pandoc.cabal index 341ab5a12..194332619 100644 --- a/pandoc.cabal +++ b/pandoc.cabal @@ -38,7 +38,7 @@ Data-Files: -- templates data/templates/default.html4 data/templates/default.html5 - data/templates/default.docbook + data/templates/default.docbook4 data/templates/default.docbook5 data/templates/default.tei data/templates/default.beamer @@ -145,7 +145,7 @@ Extra-Source-Files: tests/s5-fragment.html tests/s5-inserts.html tests/tables.context - tests/tables.docbook + tests/tables.docbook4 tests/tables.docbook5 tests/tables.dokuwiki tests/tables.zimwiki @@ -171,7 +171,7 @@ Extra-Source-Files: tests/testsuite.txt tests/writer.latex tests/writer.context - tests/writer.docbook + tests/writer.docbook4 tests/writer.docbook5 tests/writer.html4 tests/writer.html5 diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index 449cab120..ea625ffa1 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -104,7 +104,8 @@ module Text.Pandoc , writeHtml5 , writeHtml5String , writeICML - , writeDocbook + , writeDocbook4 + , writeDocbook5 , writeOPML , writeOpenDocument , writeMan @@ -298,9 +299,9 @@ writers = [ writeHtml5String o{ writerSlideVariant = DZSlides }) ,("revealjs" , StringWriter $ \o -> writeHtml5String o{ writerSlideVariant = RevealJsSlides }) - ,("docbook" , StringWriter writeDocbook) - ,("docbook5" , StringWriter $ \o -> - writeDocbook o{ writerDocbook5 = True }) + ,("docbook" , StringWriter writeDocbook5) + ,("docbook4" , StringWriter writeDocbook4) + ,("docbook5" , StringWriter writeDocbook5) ,("opml" , StringWriter writeOPML) ,("opendocument" , StringWriter writeOpenDocument) ,("latex" , StringWriter writeLaTeX) diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index 6cb2d883a..39fee298d 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -167,7 +167,6 @@ data WriterOptions = WriterOptions , writerSourceURL :: Maybe String -- ^ Absolute URL + directory of 1st source file , writerUserDataDir :: Maybe FilePath -- ^ Path of user data directory , writerCiteMethod :: CiteMethod -- ^ How to print cites - , writerDocbook5 :: Bool -- ^ Produce DocBook5 , writerHtmlQTags :: Bool -- ^ Use @@ tags for quotes in HTML , writerBeamer :: Bool -- ^ Produce beamer LaTeX slide show , writerSlideLevel :: Maybe Int -- ^ Force header level of slides @@ -208,7 +207,6 @@ instance Default WriterOptions where , writerSourceURL = Nothing , writerUserDataDir = Nothing , writerCiteMethod = Citeproc - , writerDocbook5 = False , writerHtmlQTags = False , writerBeamer = False , writerSlideLevel = Nothing diff --git a/src/Text/Pandoc/Templates.hs b/src/Text/Pandoc/Templates.hs index 38d956f1f..ddb073409 100644 --- a/src/Text/Pandoc/Templates.hs +++ b/src/Text/Pandoc/Templates.hs @@ -55,13 +55,14 @@ getDefaultTemplate :: (Maybe FilePath) -- ^ User data directory to search first getDefaultTemplate user writer = do let format = takeWhile (`notElem` ("+-" :: String)) writer -- strip off extensions case format of - "native" -> return $ Right "" - "json" -> return $ Right "" - "docx" -> return $ Right "" - "fb2" -> return $ Right "" - "odt" -> getDefaultTemplate user "opendocument" - "html" -> getDefaultTemplate user "html5" - "epub" -> getDefaultTemplate user "epub2" + "native" -> return $ Right "" + "json" -> return $ Right "" + "docx" -> return $ Right "" + "fb2" -> return $ Right "" + "odt" -> getDefaultTemplate user "opendocument" + "html" -> getDefaultTemplate user "html5" + "docbook" -> getDefaultTemplate user "docbook5" + "epub" -> getDefaultTemplate user "epub2" "markdown_strict" -> getDefaultTemplate user "markdown" "multimarkdown" -> getDefaultTemplate user "markdown" "markdown_github" -> getDefaultTemplate user "markdown" diff --git a/src/Text/Pandoc/Writers/Docbook.hs b/src/Text/Pandoc/Writers/Docbook.hs index 32695e128..53618d173 100644 --- a/src/Text/Pandoc/Writers/Docbook.hs +++ b/src/Text/Pandoc/Writers/Docbook.hs @@ -28,7 +28,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Conversion of 'Pandoc' documents to Docbook XML. -} -module Text.Pandoc.Writers.Docbook ( writeDocbook) where +module Text.Pandoc.Writers.Docbook ( writeDocbook4, writeDocbook5 ) where import Text.Pandoc.Definition import Text.Pandoc.XML import Text.Pandoc.Shared @@ -48,9 +48,15 @@ import Text.TeXMath import qualified Text.XML.Light as Xml import Data.Generics (everywhere, mkT) import Text.Pandoc.Class (PandocMonad) +import Control.Monad.Reader + +data DocBookVersion = DocBook4 | DocBook5 + deriving (Eq, Show) + +type DB = ReaderT DocBookVersion -- | Convert list of authors to a docbook section -authorToDocbook :: PandocMonad m => WriterOptions -> [Inline] -> m B.Inlines +authorToDocbook :: PandocMonad m => WriterOptions -> [Inline] -> DB m B.Inlines authorToDocbook opts name' = do name <- render Nothing <$> inlinesToDocbook opts name' let colwidth = if writerWrapText opts == WrapAuto @@ -73,8 +79,16 @@ authorToDocbook opts name' = do in inTagsSimple "firstname" (text $ escapeStringForXML firstname) $$ inTagsSimple "surname" (text $ escapeStringForXML lastname) +writeDocbook4 :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeDocbook4 opts d = + runReaderT (writeDocbook opts d) DocBook4 + +writeDocbook5 :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeDocbook5 opts d = + runReaderT (writeDocbook opts d) DocBook5 + -- | Convert Pandoc document to string in Docbook format. -writeDocbook :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeDocbook :: PandocMonad m => WriterOptions -> Pandoc -> DB m String writeDocbook opts (Pandoc meta blocks) = do let elements = hierarchicalize blocks let colwidth = if writerWrapText opts == WrapAuto @@ -100,7 +114,7 @@ writeDocbook opts (Pandoc meta blocks) = do hierarchicalize)) (fmap (render colwidth) . inlinesToDocbook opts') meta' - main <- (render' . vcat) <$> mapM (elementToDocbook opts' startLvl) elements + main <- (render' . vcat) <$> (mapM (elementToDocbook opts' startLvl) elements) let context = defField "body" main $ defField "mathml" (case writerHTMLMathMethod opts of MathML _ -> True @@ -111,9 +125,10 @@ writeDocbook opts (Pandoc meta blocks) = do Just tpl -> renderTemplate' tpl context -- | Convert an Element to Docbook. -elementToDocbook :: PandocMonad m => WriterOptions -> Int -> Element -> m Doc +elementToDocbook :: PandocMonad m => WriterOptions -> Int -> Element -> DB m Doc elementToDocbook opts _ (Blk block) = blockToDocbook opts block elementToDocbook opts lvl (Sec _ _num (id',_,_) title elements) = do + version <- ask -- Docbook doesn't allow sections with no content, so insert some if needed let elements' = if null elements then [Blk (Para [])] @@ -121,15 +136,15 @@ elementToDocbook opts lvl (Sec _ _num (id',_,_) title elements) = do tag = case lvl of -1 -> "part" 0 -> "chapter" - n | n >= 1 && n <= 5 -> if writerDocbook5 opts + n | n >= 1 && n <= 5 -> if version == DocBook5 then "section" else "sect" ++ show n _ -> "simplesect" - idName = if writerDocbook5 opts + idName = if version == DocBook5 then "xml:id" else "id" idAttr = [(idName, writerIdentifierPrefix opts ++ id') | not (null id')] - nsAttr = if writerDocbook5 opts && lvl == 0 then [("xmlns", "http://docbook.org/ns/docbook"),("xmlns:xlink", "http://www.w3.org/1999/xlink")] + nsAttr = if version == DocBook5 && lvl == 0 then [("xmlns", "http://docbook.org/ns/docbook"),("xmlns:xlink", "http://www.w3.org/1999/xlink")] else [] attribs = nsAttr ++ idAttr contents <- mapM (elementToDocbook opts (lvl + 1)) elements' @@ -138,7 +153,7 @@ elementToDocbook opts lvl (Sec _ _num (id',_,_) title elements) = do inTagsSimple "title" title' $$ vcat contents -- | Convert a list of Pandoc blocks to Docbook. -blocksToDocbook :: PandocMonad m => WriterOptions -> [Block] -> m Doc +blocksToDocbook :: PandocMonad m => WriterOptions -> [Block] -> DB m Doc blocksToDocbook opts = fmap vcat . mapM (blockToDocbook opts) -- | Auxiliary function to convert Plain block to Para. @@ -149,13 +164,13 @@ plainToPara x = x -- | Convert a list of pairs of terms and definitions into a list of -- Docbook varlistentrys. deflistItemsToDocbook :: PandocMonad m - => WriterOptions -> [([Inline],[[Block]])] -> m Doc + => WriterOptions -> [([Inline],[[Block]])] -> DB m Doc deflistItemsToDocbook opts items = vcat <$> mapM (\(term, defs) -> deflistItemToDocbook opts term defs) items -- | Convert a term and a list of blocks into a Docbook varlistentry. deflistItemToDocbook :: PandocMonad m - => WriterOptions -> [Inline] -> [[Block]] -> m Doc + => WriterOptions -> [Inline] -> [[Block]] -> DB m Doc deflistItemToDocbook opts term defs = do term' <- inlinesToDocbook opts term def' <- blocksToDocbook opts $ concatMap (map plainToPara) defs @@ -164,11 +179,11 @@ deflistItemToDocbook opts term defs = do inTagsIndented "listitem" def' -- | Convert a list of lists of blocks to a list of Docbook list items. -listItemsToDocbook :: PandocMonad m => WriterOptions -> [[Block]] -> m Doc +listItemsToDocbook :: PandocMonad m => WriterOptions -> [[Block]] -> DB m Doc listItemsToDocbook opts items = vcat <$> mapM (listItemToDocbook opts) items -- | Convert a list of blocks into a Docbook list item. -listItemToDocbook :: PandocMonad m => WriterOptions -> [Block] -> m Doc +listItemToDocbook :: PandocMonad m => WriterOptions -> [Block] -> DB m Doc listItemToDocbook opts item = inTagsIndented "listitem" <$> blocksToDocbook opts (map plainToPara item) @@ -182,7 +197,7 @@ imageToDocbook _ attr src = selfClosingTag "imagedata" $ Nothing -> [] -- | Convert a Pandoc block element to Docbook. -blockToDocbook :: PandocMonad m => WriterOptions -> Block -> m Doc +blockToDocbook :: PandocMonad m => WriterOptions -> Block -> DB m Doc blockToDocbook _ Null = return empty -- Add ids to paragraphs in divs with ids - this is needed for -- pandoc-citeproc to get link anchors in bibliographies: @@ -260,9 +275,11 @@ blockToDocbook opts (OrderedList (start, numstyle, _) (first:rest)) = do blockToDocbook opts (DefinitionList lst) = do let attribs = [("spacing", "compact") | isTightList $ concatMap snd lst] inTags True "variablelist" attribs <$> deflistItemsToDocbook opts lst -blockToDocbook opts (RawBlock f str) +blockToDocbook _ (RawBlock f str) | f == "docbook" = return $ text str -- raw XML block - | f == "html" = if writerDocbook5 opts + | f == "html" = do + version <- ask + if version == DocBook5 then return empty -- No html in Docbook5 else return $ text str -- allow html for backwards compatibility | otherwise = return empty @@ -306,23 +323,23 @@ alignmentToString alignment = case alignment of tableRowToDocbook :: PandocMonad m => WriterOptions -> [[Block]] - -> m Doc + -> DB m Doc tableRowToDocbook opts cols = (inTagsIndented "row" . vcat) <$> mapM (tableItemToDocbook opts) cols tableItemToDocbook :: PandocMonad m => WriterOptions -> [Block] - -> m Doc + -> DB m Doc tableItemToDocbook opts item = (inTags True "entry" [] . vcat) <$> mapM (blockToDocbook opts) item -- | Convert a list of inline elements to Docbook. -inlinesToDocbook :: PandocMonad m => WriterOptions -> [Inline] -> m Doc +inlinesToDocbook :: PandocMonad m => WriterOptions -> [Inline] -> DB m Doc inlinesToDocbook opts lst = hcat <$> mapM (inlineToDocbook opts) lst -- | Convert an inline element to Docbook. -inlineToDocbook :: PandocMonad m => WriterOptions -> Inline -> m Doc +inlineToDocbook :: PandocMonad m => WriterOptions -> Inline -> DB m Doc inlineToDocbook _ (Str str) = return $ text $ escapeStringForXML str inlineToDocbook opts (Emph lst) = inTagsSimple "emphasis" <$> inlinesToDocbook opts lst @@ -385,10 +402,11 @@ inlineToDocbook opts (Link attr txt (src, _)) _ -> do contents <- inlinesToDocbook opts txt return $ contents <+> char '(' <> emailLink <> char ')' - | otherwise = + | otherwise = do + version <- ask (if isPrefixOf "#" src then inTags False "link" $ ("linkend", drop 1 src) : idAndRole attr - else if writerDocbook5 opts + else if version == DocBook5 then inTags False "link" $ ("xlink:href", src) : idAndRole attr else inTags False "ulink" $ ("url", src) : idAndRole attr ) <$> inlinesToDocbook opts txt diff --git a/tests/Tests/Old.hs b/tests/Tests/Old.hs index a46ac2260..f22636747 100644 --- a/tests/Tests/Old.hs +++ b/tests/Tests/Old.hs @@ -99,7 +99,7 @@ tests = [ testGroup "markdown" "textile-reader.textile" "textile-reader.native" ] , testGroup "docbook" - [ testGroup "writer" $ writerTests "docbook" + [ testGroup "writer" $ writerTests "docbook4" , test "reader" ["-r", "docbook", "-w", "native", "-s"] "docbook-reader.docbook" "docbook-reader.native" , test "reader" ["-r", "docbook", "-w", "native", "-s"] diff --git a/tests/Tests/Writers/Docbook.hs b/tests/Tests/Writers/Docbook.hs index adf6e9050..f34f2495c 100644 --- a/tests/Tests/Writers/Docbook.hs +++ b/tests/Tests/Writers/Docbook.hs @@ -11,7 +11,7 @@ docbook :: (ToPandoc a) => a -> String docbook = docbookWithOpts def{ writerWrapText = WrapNone } docbookWithOpts :: ToPandoc a => WriterOptions -> a -> String -docbookWithOpts opts = purely (writeDocbook opts) . toPandoc +docbookWithOpts opts = purely (writeDocbook4 opts) . toPandoc {- "my test" =: X =?> Y diff --git a/tests/tables.docbook b/tests/tables.docbook deleted file mode 100644 index f86b1c390..000000000 --- a/tests/tables.docbook +++ /dev/null @@ -1,432 +0,0 @@ - - Simple table with caption: - - - - Demonstration of simple table syntax. - - - - - - - - - - Right - - - Left - - - Center - - - Default - - - - - - - 12 - - - 12 - - - 12 - - - 12 - - - - - 123 - - - 123 - - - 123 - - - 123 - - - - - 1 - - - 1 - - - 1 - - - 1 - - - - -
- - Simple table without caption: - - - - - - - - - - - Right - - - Left - - - Center - - - Default - - - - - - - 12 - - - 12 - - - 12 - - - 12 - - - - - 123 - - - 123 - - - 123 - - - 123 - - - - - 1 - - - 1 - - - 1 - - - 1 - - - - - - - Simple table indented two spaces: - - - - Demonstration of simple table syntax. - - - - - - - - - - Right - - - Left - - - Center - - - Default - - - - - - - 12 - - - 12 - - - 12 - - - 12 - - - - - 123 - - - 123 - - - 123 - - - 123 - - - - - 1 - - - 1 - - - 1 - - - 1 - - - - -
- - Multiline table with caption: - - - - Here’s the caption. It may span multiple lines. - - - - - - - - - - Centered Header - - - Left Aligned - - - Right Aligned - - - Default aligned - - - - - - - First - - - row - - - 12.0 - - - Example of a row that spans multiple lines. - - - - - Second - - - row - - - 5.0 - - - Here’s another one. Note the blank line between rows. - - - - -
- - Multiline table without caption: - - - - - - - - - - - Centered Header - - - Left Aligned - - - Right Aligned - - - Default aligned - - - - - - - First - - - row - - - 12.0 - - - Example of a row that spans multiple lines. - - - - - Second - - - row - - - 5.0 - - - Here’s another one. Note the blank line between rows. - - - - - - - Table without column headers: - - - - - - - - - - - 12 - - - 12 - - - 12 - - - 12 - - - - - 123 - - - 123 - - - 123 - - - 123 - - - - - 1 - - - 1 - - - 1 - - - 1 - - - - - - - Multiline table without column headers: - - - - - - - - - - - First - - - row - - - 12.0 - - - Example of a row that spans multiple lines. - - - - - Second - - - row - - - 5.0 - - - Here’s another one. Note the blank line between rows. - - - - - diff --git a/tests/tables.docbook4 b/tests/tables.docbook4 new file mode 100644 index 000000000..f86b1c390 --- /dev/null +++ b/tests/tables.docbook4 @@ -0,0 +1,432 @@ + + Simple table with caption: + + + + Demonstration of simple table syntax. + + + + + + + + + + Right + + + Left + + + Center + + + Default + + + + + + + 12 + + + 12 + + + 12 + + + 12 + + + + + 123 + + + 123 + + + 123 + + + 123 + + + + + 1 + + + 1 + + + 1 + + + 1 + + + + +
+ + Simple table without caption: + + + + + + + + + + + Right + + + Left + + + Center + + + Default + + + + + + + 12 + + + 12 + + + 12 + + + 12 + + + + + 123 + + + 123 + + + 123 + + + 123 + + + + + 1 + + + 1 + + + 1 + + + 1 + + + + + + + Simple table indented two spaces: + + + + Demonstration of simple table syntax. + + + + + + + + + + Right + + + Left + + + Center + + + Default + + + + + + + 12 + + + 12 + + + 12 + + + 12 + + + + + 123 + + + 123 + + + 123 + + + 123 + + + + + 1 + + + 1 + + + 1 + + + 1 + + + + +
+ + Multiline table with caption: + + + + Here’s the caption. It may span multiple lines. + + + + + + + + + + Centered Header + + + Left Aligned + + + Right Aligned + + + Default aligned + + + + + + + First + + + row + + + 12.0 + + + Example of a row that spans multiple lines. + + + + + Second + + + row + + + 5.0 + + + Here’s another one. Note the blank line between rows. + + + + +
+ + Multiline table without caption: + + + + + + + + + + + Centered Header + + + Left Aligned + + + Right Aligned + + + Default aligned + + + + + + + First + + + row + + + 12.0 + + + Example of a row that spans multiple lines. + + + + + Second + + + row + + + 5.0 + + + Here’s another one. Note the blank line between rows. + + + + + + + Table without column headers: + + + + + + + + + + + 12 + + + 12 + + + 12 + + + 12 + + + + + 123 + + + 123 + + + 123 + + + 123 + + + + + 1 + + + 1 + + + 1 + + + 1 + + + + + + + Multiline table without column headers: + + + + + + + + + + + First + + + row + + + 12.0 + + + Example of a row that spans multiple lines. + + + + + Second + + + row + + + 5.0 + + + Here’s another one. Note the blank line between rows. + + + + + diff --git a/tests/writer.docbook b/tests/writer.docbook deleted file mode 100644 index eee19cdd9..000000000 --- a/tests/writer.docbook +++ /dev/null @@ -1,1422 +0,0 @@ - - -
- - Pandoc Test Suite - - - John - MacFarlane - - - - Anonymous - - - July 17, 2006 - - - This is a set of tests for pandoc. Most of them are adapted from John - Gruber’s markdown test suite. - - - Headers - - Level 2 with an <ulink url="/url">embedded link</ulink> - - Level 3 with <emphasis>emphasis</emphasis> - - Level 4 - - Level 5 - - - - - - - - - Level 1 - - Level 2 with <emphasis>emphasis</emphasis> - - Level 3 - - with no blank line - - - - - Level 2 - - with no blank line - - - - - Paragraphs - - Here’s a regular paragraph. - - - 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. - - - Here’s one with a bullet. * criminey. - -There should be a hard line break -here. - - - Block Quotes - - E-mail style: - -
- - This is a block quote. It is pretty short. - -
-
- - Code in a block quote: - - -sub status { - print "working"; -} - - - A list: - - - - - item one - - - - - item two - - - - - Nested block quotes: - -
- - nested - -
-
- - nested - -
-
- - This should not be a block quote: 2 > 1. - - - And a following paragraph. - -
- - Code Blocks - - Code: - - ----- (should be four hyphens) - -sub status { - print "working"; -} - -this code block is indented by one tab - - - And: - - - this code block is indented by two tabs - -These should not be escaped: \$ \\ \> \[ \{ - - - - Lists - - Unordered - - Asterisks tight: - - - - - asterisk 1 - - - - - asterisk 2 - - - - - asterisk 3 - - - - - Asterisks loose: - - - - - asterisk 1 - - - - - asterisk 2 - - - - - asterisk 3 - - - - - Pluses tight: - - - - - Plus 1 - - - - - Plus 2 - - - - - Plus 3 - - - - - Pluses loose: - - - - - Plus 1 - - - - - Plus 2 - - - - - Plus 3 - - - - - Minuses tight: - - - - - Minus 1 - - - - - Minus 2 - - - - - Minus 3 - - - - - Minuses loose: - - - - - Minus 1 - - - - - Minus 2 - - - - - Minus 3 - - - - - - Ordered - - Tight: - - - - - First - - - - - Second - - - - - Third - - - - - and: - - - - - One - - - - - Two - - - - - Three - - - - - Loose using tabs: - - - - - First - - - - - Second - - - - - Third - - - - - and using spaces: - - - - - One - - - - - Two - - - - - Three - - - - - Multiple paragraphs: - - - - - Item 1, graf one. - - - Item 1. graf two. The quick brown fox jumped over the lazy dog’s - back. - - - - - Item 2. - - - - - Item 3. - - - - - - Nested - - - - Tab - - - - - Tab - - - - - Tab - - - - - - - - - Here’s another: - - - - - First - - - - - Second: - - - - - Fee - - - - - Fie - - - - - Foe - - - - - - - Third - - - - - Same thing but with paragraphs: - - - - - First - - - - - Second: - - - - - Fee - - - - - Fie - - - - - Foe - - - - - - - Third - - - - - - Tabs and spaces - - - - this is a list item indented with tabs - - - - - this is a list item indented with spaces - - - - - this is an example list item indented with tabs - - - - - this is an example list item indented with spaces - - - - - - - - Fancy list markers - - - - begins with 2 - - - - - and now 3 - - - with a continuation - - - - - sublist with roman numerals, starting with 4 - - - - - more items - - - - - a subsublist - - - - - a subsublist - - - - - - - - - Nesting: - - - - - Upper Alpha - - - - - Upper Roman. - - - - - Decimal start with 6 - - - - - Lower alpha with paren - - - - - - - - - - - Autonumbering: - - - - - Autonumber. - - - - - More. - - - - - Nested. - - - - - - - Should not be a list item: - - - M.A. 2007 - - - B. Williams - - - - - Definition Lists - - Tight using spaces: - - - - - apple - - - - red fruit - - - - - - orange - - - - orange fruit - - - - - - banana - - - - yellow fruit - - - - - - Tight using tabs: - - - - - apple - - - - red fruit - - - - - - orange - - - - orange fruit - - - - - - banana - - - - yellow fruit - - - - - - Loose: - - - - - apple - - - - red fruit - - - - - - orange - - - - orange fruit - - - - - - banana - - - - yellow fruit - - - - - - Multiple blocks with italics: - - - - - apple - - - - red fruit - - - contains seeds, crisp, pleasant to taste - - - - - - orange - - - - orange fruit - - -{ orange code block } - -
- - orange block quote - -
-
-
-
- - Multiple definitions, tight: - - - - - apple - - - - red fruit - - - computer - - - - - - orange - - - - orange fruit - - - bank - - - - - - Multiple definitions, loose: - - - - - apple - - - - red fruit - - - computer - - - - - - orange - - - - orange fruit - - - bank - - - - - - Blank line after term, indented marker, alternate markers: - - - - - apple - - - - red fruit - - - computer - - - - - - orange - - - - orange fruit - - - - - sublist - - - - - sublist - - - - - - -
- - HTML Blocks - - Simple block on one line: - - - foo - - - And nested without indentation: - - - foo - - - bar - - - Interpreted markdown in a table: - - - - - - -
- This is emphasized - - And this is strong -
- - - Here’s a simple block: - - - foo - - - This should be a code block, though: - - -<div> - foo -</div> - - - As should this: - - -<div>foo</div> - - - Now, nested: - - - foo - - - This should just be an HTML comment: - - - - Multiline: - - - - - Code block: - - -<!-- Comment --> - - - Just plain comment, with trailing spaces on the line: - - - - Code: - - -<hr /> - - - Hr’s: - -
-
-
-
-
-
-
-
-
-
- - Inline Markup - - This is emphasized, and so is - this. - - - This is strong, and so - is this. - - - An emphasized link. - - - This is strong and - em. - - - So is this word. - - - This is strong and - em. - - - So is this word. - - - This is code: >, $, - \, \$, - <html>. - - - This is - strikeout. - - - Superscripts: abcd - ahello - ahello there. - - - Subscripts: H2O, H23O, - Hmany of themO. - - - These should not be superscripts or subscripts, because of the unescaped - spaces: a^b c^d, a~b c~d. - - - - Smart quotes, ellipses, dashes - - Hello, said the spider. Shelob is my - name. - - - A, B, and C are letters. - - - Oak, elm, and beech are names - of trees. So is pine. - - - He said, I want to go. Were you alive in the - 70’s? - - - Here is some quoted code and a - quoted - link. - - - Some dashes: one—two — three—four — five. - - - Dashes between numbers: 5–7, 255–66, 1987–1999. - - - Ellipses…and…and…. - - - - LaTeX - - - - - - - - 2 + 2 = 4 - - - - - x ∈ y - - - - - α ∧ ω - - - - - 223 - - - - - p-Tree - - - - - Here’s some display math: - $$\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$$ - - - - - Here’s one that has a line break in it: - α + ω × x2. - - - - - These shouldn’t be math: - - - - - To get the famous equation, write $e = mc^2$. - - - - - $22,000 is a lot of money. So is $34,000. (It - worked if lot is emphasized.) - - - - - Shoes ($20) and socks ($5). - - - - - Escaped $: $73 this should be - emphasized 23$. - - - - - Here’s a LaTeX table: - - - - Special Characters - - Here is some unicode: - - - - - I hat: Î - - - - - o umlaut: ö - - - - - section: § - - - - - set membership: ∈ - - - - - copyright: © - - - - - AT&T has an ampersand in their name. - - - AT&T is another way to write it. - - - This & that. - - - 4 < 5. - - - 6 > 5. - - - Backslash: \ - - - Backtick: ` - - - Asterisk: * - - - Underscore: _ - - - Left brace: { - - - Right brace: } - - - Left bracket: [ - - - Right bracket: ] - - - Left paren: ( - - - Right paren: ) - - - Greater-than: > - - - Hash: # - - - Period: . - - - Bang: ! - - - Plus: + - - - Minus: - - - - - Links - - Explicit - - Just a URL. - - - URL and title. - - - URL and title. - - - URL and title. - - - URL and title - - - URL and title - - - with_underscore - - - Email link (nobody@nowhere.net) - - - Empty. - - - - Reference - - Foo bar. - - - Foo bar. - - - Foo bar. - - - With embedded [brackets]. - - - b by itself should be a link. - - - Indented once. - - - Indented twice. - - - Indented thrice. - - - This should [not][] be a link. - - -[not]: /url - - - Foo bar. - - - Foo biz. - - - - With ampersands - - Here’s a link with an - ampersand in the URL. - - - Here’s a link with an amersand in the link text: - AT&T. - - - Here’s an inline link. - - - Here’s an inline link in pointy - braces. - - - - Autolinks - - With an ampersand: - http://example.com/?foo=1&bar=2 - - - - - In a list? - - - - - http://example.com/ - - - - - It should. - - - - - An e-mail address: nobody@nowhere.net - -
- - Blockquoted: - http://example.com/ - -
- - Auto-links should not occur here: - <http://example.com/> - - -or here: <http://example.com/> - -
-
- - Images - - From Voyage dans la Lune by Georges Melies (1902): - -
- lalune - - - - - lalune - -
- - Here is a movie - - - - icon. - -
- - Footnotes - - Here is a footnote reference, - - Here is the footnote. It can go anywhere after the footnote reference. - It need not be placed at the end of the document. - - and another. - - Here’s the long note. This one contains multiple blocks. - - - Subsequent blocks are indented to show that they belong to the - footnote (as with list items). - - - { <code> } - - - If you want, you can indent every line, but you can also be lazy and - just indent the first line of each block. - - This should not be a footnote reference, - because it contains a space.[^my note] Here is an inline note. - - This is easier to type. Inline notes may contain - links and ] - verbatim characters, as well as [bracketed text]. - - - -
- - Notes can go in quotes. - - In quote. - - - -
- - - - And in list items. - - In list. - - - - - - - This paragraph should not be part of the note, as it is not indented. - -
-
diff --git a/tests/writer.docbook4 b/tests/writer.docbook4 new file mode 100644 index 000000000..eee19cdd9 --- /dev/null +++ b/tests/writer.docbook4 @@ -0,0 +1,1422 @@ + + +
+ + Pandoc Test Suite + + + John + MacFarlane + + + + Anonymous + + + July 17, 2006 + + + This is a set of tests for pandoc. Most of them are adapted from John + Gruber’s markdown test suite. + + + Headers + + Level 2 with an <ulink url="/url">embedded link</ulink> + + Level 3 with <emphasis>emphasis</emphasis> + + Level 4 + + Level 5 + + + + + + + + + Level 1 + + Level 2 with <emphasis>emphasis</emphasis> + + Level 3 + + with no blank line + + + + + Level 2 + + with no blank line + + + + + Paragraphs + + Here’s a regular paragraph. + + + 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. + + + Here’s one with a bullet. * criminey. + +There should be a hard line break +here. + + + Block Quotes + + E-mail style: + +
+ + This is a block quote. It is pretty short. + +
+
+ + Code in a block quote: + + +sub status { + print "working"; +} + + + A list: + + + + + item one + + + + + item two + + + + + Nested block quotes: + +
+ + nested + +
+
+ + nested + +
+
+ + This should not be a block quote: 2 > 1. + + + And a following paragraph. + +
+ + Code Blocks + + Code: + + +---- (should be four hyphens) + +sub status { + print "working"; +} + +this code block is indented by one tab + + + And: + + + this code block is indented by two tabs + +These should not be escaped: \$ \\ \> \[ \{ + + + + Lists + + Unordered + + Asterisks tight: + + + + + asterisk 1 + + + + + asterisk 2 + + + + + asterisk 3 + + + + + Asterisks loose: + + + + + asterisk 1 + + + + + asterisk 2 + + + + + asterisk 3 + + + + + Pluses tight: + + + + + Plus 1 + + + + + Plus 2 + + + + + Plus 3 + + + + + Pluses loose: + + + + + Plus 1 + + + + + Plus 2 + + + + + Plus 3 + + + + + Minuses tight: + + + + + Minus 1 + + + + + Minus 2 + + + + + Minus 3 + + + + + Minuses loose: + + + + + Minus 1 + + + + + Minus 2 + + + + + Minus 3 + + + + + + Ordered + + Tight: + + + + + First + + + + + Second + + + + + Third + + + + + and: + + + + + One + + + + + Two + + + + + Three + + + + + Loose using tabs: + + + + + First + + + + + Second + + + + + Third + + + + + and using spaces: + + + + + One + + + + + Two + + + + + Three + + + + + Multiple paragraphs: + + + + + Item 1, graf one. + + + Item 1. graf two. The quick brown fox jumped over the lazy dog’s + back. + + + + + Item 2. + + + + + Item 3. + + + + + + Nested + + + + Tab + + + + + Tab + + + + + Tab + + + + + + + + + Here’s another: + + + + + First + + + + + Second: + + + + + Fee + + + + + Fie + + + + + Foe + + + + + + + Third + + + + + Same thing but with paragraphs: + + + + + First + + + + + Second: + + + + + Fee + + + + + Fie + + + + + Foe + + + + + + + Third + + + + + + Tabs and spaces + + + + this is a list item indented with tabs + + + + + this is a list item indented with spaces + + + + + this is an example list item indented with tabs + + + + + this is an example list item indented with spaces + + + + + + + + Fancy list markers + + + + begins with 2 + + + + + and now 3 + + + with a continuation + + + + + sublist with roman numerals, starting with 4 + + + + + more items + + + + + a subsublist + + + + + a subsublist + + + + + + + + + Nesting: + + + + + Upper Alpha + + + + + Upper Roman. + + + + + Decimal start with 6 + + + + + Lower alpha with paren + + + + + + + + + + + Autonumbering: + + + + + Autonumber. + + + + + More. + + + + + Nested. + + + + + + + Should not be a list item: + + + M.A. 2007 + + + B. Williams + + + + + Definition Lists + + Tight using spaces: + + + + + apple + + + + red fruit + + + + + + orange + + + + orange fruit + + + + + + banana + + + + yellow fruit + + + + + + Tight using tabs: + + + + + apple + + + + red fruit + + + + + + orange + + + + orange fruit + + + + + + banana + + + + yellow fruit + + + + + + Loose: + + + + + apple + + + + red fruit + + + + + + orange + + + + orange fruit + + + + + + banana + + + + yellow fruit + + + + + + Multiple blocks with italics: + + + + + apple + + + + red fruit + + + contains seeds, crisp, pleasant to taste + + + + + + orange + + + + orange fruit + + +{ orange code block } + +
+ + orange block quote + +
+
+
+
+ + Multiple definitions, tight: + + + + + apple + + + + red fruit + + + computer + + + + + + orange + + + + orange fruit + + + bank + + + + + + Multiple definitions, loose: + + + + + apple + + + + red fruit + + + computer + + + + + + orange + + + + orange fruit + + + bank + + + + + + Blank line after term, indented marker, alternate markers: + + + + + apple + + + + red fruit + + + computer + + + + + + orange + + + + orange fruit + + + + + sublist + + + + + sublist + + + + + + +
+ + HTML Blocks + + Simple block on one line: + + + foo + + + And nested without indentation: + + + foo + + + bar + + + Interpreted markdown in a table: + + + + + + +
+ This is emphasized + + And this is strong +
+ + + Here’s a simple block: + + + foo + + + This should be a code block, though: + + +<div> + foo +</div> + + + As should this: + + +<div>foo</div> + + + Now, nested: + + + foo + + + This should just be an HTML comment: + + + + Multiline: + + + + + Code block: + + +<!-- Comment --> + + + Just plain comment, with trailing spaces on the line: + + + + Code: + + +<hr /> + + + Hr’s: + +
+
+
+
+
+
+
+
+
+
+ + Inline Markup + + This is emphasized, and so is + this. + + + This is strong, and so + is this. + + + An emphasized link. + + + This is strong and + em. + + + So is this word. + + + This is strong and + em. + + + So is this word. + + + This is code: >, $, + \, \$, + <html>. + + + This is + strikeout. + + + Superscripts: abcd + ahello + ahello there. + + + Subscripts: H2O, H23O, + Hmany of themO. + + + These should not be superscripts or subscripts, because of the unescaped + spaces: a^b c^d, a~b c~d. + + + + Smart quotes, ellipses, dashes + + Hello, said the spider. Shelob is my + name. + + + A, B, and C are letters. + + + Oak, elm, and beech are names + of trees. So is pine. + + + He said, I want to go. Were you alive in the + 70’s? + + + Here is some quoted code and a + quoted + link. + + + Some dashes: one—two — three—four — five. + + + Dashes between numbers: 5–7, 255–66, 1987–1999. + + + Ellipses…and…and…. + + + + LaTeX + + + + + + + + 2 + 2 = 4 + + + + + x ∈ y + + + + + α ∧ ω + + + + + 223 + + + + + p-Tree + + + + + Here’s some display math: + $$\frac{d}{dx}f(x)=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}$$ + + + + + Here’s one that has a line break in it: + α + ω × x2. + + + + + These shouldn’t be math: + + + + + To get the famous equation, write $e = mc^2$. + + + + + $22,000 is a lot of money. So is $34,000. (It + worked if lot is emphasized.) + + + + + Shoes ($20) and socks ($5). + + + + + Escaped $: $73 this should be + emphasized 23$. + + + + + Here’s a LaTeX table: + + + + Special Characters + + Here is some unicode: + + + + + I hat: Î + + + + + o umlaut: ö + + + + + section: § + + + + + set membership: ∈ + + + + + copyright: © + + + + + AT&T has an ampersand in their name. + + + AT&T is another way to write it. + + + This & that. + + + 4 < 5. + + + 6 > 5. + + + Backslash: \ + + + Backtick: ` + + + Asterisk: * + + + Underscore: _ + + + Left brace: { + + + Right brace: } + + + Left bracket: [ + + + Right bracket: ] + + + Left paren: ( + + + Right paren: ) + + + Greater-than: > + + + Hash: # + + + Period: . + + + Bang: ! + + + Plus: + + + + Minus: - + + + + Links + + Explicit + + Just a URL. + + + URL and title. + + + URL and title. + + + URL and title. + + + URL and title + + + URL and title + + + with_underscore + + + Email link (nobody@nowhere.net) + + + Empty. + + + + Reference + + Foo bar. + + + Foo bar. + + + Foo bar. + + + With embedded [brackets]. + + + b by itself should be a link. + + + Indented once. + + + Indented twice. + + + Indented thrice. + + + This should [not][] be a link. + + +[not]: /url + + + Foo bar. + + + Foo biz. + + + + With ampersands + + Here’s a link with an + ampersand in the URL. + + + Here’s a link with an amersand in the link text: + AT&T. + + + Here’s an inline link. + + + Here’s an inline link in pointy + braces. + + + + Autolinks + + With an ampersand: + http://example.com/?foo=1&bar=2 + + + + + In a list? + + + + + http://example.com/ + + + + + It should. + + + + + An e-mail address: nobody@nowhere.net + +
+ + Blockquoted: + http://example.com/ + +
+ + Auto-links should not occur here: + <http://example.com/> + + +or here: <http://example.com/> + +
+
+ + Images + + From Voyage dans la Lune by Georges Melies (1902): + +
+ lalune + + + + + lalune + +
+ + Here is a movie + + + + icon. + +
+ + Footnotes + + Here is a footnote reference, + + Here is the footnote. It can go anywhere after the footnote reference. + It need not be placed at the end of the document. + + and another. + + Here’s the long note. This one contains multiple blocks. + + + Subsequent blocks are indented to show that they belong to the + footnote (as with list items). + + + { <code> } + + + If you want, you can indent every line, but you can also be lazy and + just indent the first line of each block. + + This should not be a footnote reference, + because it contains a space.[^my note] Here is an inline note. + + This is easier to type. Inline notes may contain + links and ] + verbatim characters, as well as [bracketed text]. + + + +
+ + Notes can go in quotes. + + In quote. + + + +
+ + + + And in list items. + + In list. + + + + + + + This paragraph should not be part of the note, as it is not indented. + +
+
-- cgit v1.2.3 From f5dd1238198450c4917707214f19e2f0da8c3cb4 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 27 Jan 2017 10:27:34 +0100 Subject: HTML writer: export writeHtmlStringForEPUB. Options: Remove writerEPUBVersion. --- src/Text/Pandoc/Options.hs | 2 -- src/Text/Pandoc/Writers/EPUB.hs | 36 ++++++++++++++++-------------------- src/Text/Pandoc/Writers/HTML.hs | 32 ++++++++++++++++++++++++++------ 3 files changed, 42 insertions(+), 28 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index 39fee298d..755ab9add 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -175,7 +175,6 @@ data WriterOptions = WriterOptions , writerHighlightStyle :: Maybe Style -- ^ Style to use for highlighting -- (Nothing = no highlighting) , writerSetextHeaders :: Bool -- ^ Use setext headers for levels 1-2 in markdown - , writerEpubVersion :: Maybe EPUBVersion -- ^ Nothing or EPUB version , writerEpubMetadata :: String -- ^ Metadata to include in EPUB , writerEpubStylesheet :: Maybe String -- ^ EPUB stylesheet specified at command line , writerEpubFonts :: [FilePath] -- ^ Paths to fonts to embed @@ -214,7 +213,6 @@ instance Default WriterOptions where , writerListings = False , writerHighlightStyle = Just pygments , writerSetextHeaders = True - , writerEpubVersion = Nothing , writerEpubMetadata = "" , writerEpubStylesheet = Nothing , writerEpubFonts = [] diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index c2fc4422e..ae77c10a2 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -59,10 +59,9 @@ import Control.Monad (mplus, when, zipWithM) import Text.XML.Light ( unode, Element(..), unqual, Attr(..), add_attrs , strContent, lookupAttr, Node(..), QName(..), parseXML , onlyElems, node, ppElement) -import Text.Pandoc.Writers.HTML ( writeHtml4, writeHtml5 ) +import Text.Pandoc.Writers.HTML ( writeHtmlStringForEPUB ) import Data.Char ( toLower, isDigit, isAlphaNum ) import Text.Pandoc.MIME (MimeType, getMimeType, extensionFromMimeType) -import Text.Blaze.Html.Renderer.Utf8 (renderHtml) import Text.HTML.TagSoup (Tag(TagOpen), fromAttrib, parseTags) import Control.Monad.Except (throwError, catchError) import Text.Pandoc.Error @@ -361,16 +360,18 @@ writeEPUB epubVersion opts doc = let initState = EPUBState { stMediaPaths = [] } in - evalStateT (pandocToEPUB opts{ writerEpubVersion = Just epubVersion } doc) + evalStateT (pandocToEPUB epubVersion opts doc) initState pandocToEPUB :: PandocMonad m - => WriterOptions + => EPUBVersion + -> WriterOptions -> Pandoc -> E m B.ByteString -pandocToEPUB opts doc@(Pandoc meta _) = do - let version = fromMaybe EPUB2 (writerEpubVersion opts) - let epub3 = writerEpubVersion opts == Just EPUB3 +pandocToEPUB version opts doc@(Pandoc meta _) = do + let epub3 = version == EPUB3 + let writeHtml o = fmap UTF8.fromStringLazy . + writeHtmlStringForEPUB version o epochtime <- floor <$> lift P.getPOSIXTime let mkEntry path content = toEntry path epochtime content let vars = ("epub3", if epub3 then "true" else "false") @@ -384,9 +385,6 @@ pandocToEPUB opts doc@(Pandoc meta _) = do then MathML Nothing else writerHTMLMathMethod opts , writerWrapText = WrapAuto } - let writeHtml = if epub3 - then writeHtml5 - else writeHtml4 metadata <- getEPUBMetadata opts' meta -- cover page @@ -395,17 +393,17 @@ pandocToEPUB opts doc@(Pandoc meta _) = do Nothing -> return ([],[]) Just img -> do let coverImage = "media/" ++ takeFileName img - cpContent <- renderHtml <$> (lift $ writeHtml + cpContent <- lift $ writeHtml opts'{ writerVariables = ("coverpage","true"):vars } - (Pandoc meta [RawBlock (Format "html") $ "
\n\"cover\n
"])) + (Pandoc meta [RawBlock (Format "html") $ "
\n\"cover\n
"]) imgContent <- lift $ P.readFileLazy img return ( [mkEntry "cover.xhtml" cpContent] , [mkEntry coverImage imgContent] ) -- title page - tpContent <- renderHtml <$> (lift $ writeHtml opts'{ + tpContent <- lift $ writeHtml opts'{ writerVariables = ("titlepage","true"):vars } - (Pandoc meta [])) + (Pandoc meta []) let tpEntry = mkEntry "title_page.xhtml" tpContent -- handle pictures @@ -504,9 +502,8 @@ pandocToEPUB opts doc@(Pandoc meta _) = do chapters' let chapToEntry num (Chapter mbnum bs) = - (mkEntry (showChapter num) . renderHtml) <$> - (writeHtml opts'{ writerNumberOffset = - fromMaybe [] mbnum } + mkEntry (showChapter num) <$> + (writeHtml opts'{ writerNumberOffset = fromMaybe [] mbnum } $ case bs of (Header _ _ xs : _) -> -- remove notes or we get doubled footnotes @@ -702,11 +699,10 @@ pandocToEPUB opts doc@(Pandoc meta _) = do ] ] else [] - navData <- renderHtml <$> (lift $ writeHtml - opts'{ writerVariables = ("navpage","true"):vars } + navData <- 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 ee1f260b6..518848139 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -29,8 +29,12 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Conversion of 'Pandoc' documents to HTML. -} module Text.Pandoc.Writers.HTML ( - writeHtml4, writeHtml4String, - writeHtml5, writeHtml5String ) where + writeHtml4, + writeHtml4String, + writeHtml5, + writeHtml5String, + writeHtmlStringForEPUB + ) where import Text.Pandoc.Definition import Text.Pandoc.Walk import Data.Monoid ((<>)) @@ -83,12 +87,14 @@ data WriterState = WriterState , stSecNum :: [Int] -- ^ Number of current section , stElement :: Bool -- ^ Processing an Element , stHtml5 :: Bool -- ^ Use HTML5 + , stEPUBVersion :: Maybe EPUBVersion -- ^ EPUB version if for epub } defaultWriterState :: WriterState defaultWriterState = WriterState {stNotes= [], stMath = False, stQuotes = False, stHighlighting = False, stSecNum = [], - stElement = False, stHtml5 = False} + stElement = False, stHtml5 = False, + stEPUBVersion = Nothing} -- Helpers to render HTML with the appropriate function. @@ -121,6 +127,18 @@ writeHtml4String = writeHtmlString' False writeHtml4 :: PandocMonad m => WriterOptions -> Pandoc -> m Html writeHtml4 = writeHtml' False +-- | Convert Pandoc document to Html appropriate for an epub version. +writeHtmlStringForEPUB :: PandocMonad m + => EPUBVersion -> WriterOptions -> Pandoc -> m String +writeHtmlStringForEPUB version opts d = do + (body, context) <- evalStateT (pandocToHtml opts d) + defaultWriterState{ stHtml5 = version == EPUB3, + stEPUBVersion = Just version } + return $ case writerTemplate opts of + Nothing -> renderHtml body + Just tpl -> renderTemplate' tpl $ + defField "body" (renderHtml body) context + writeHtmlString' :: PandocMonad m => Bool -> WriterOptions -> Pandoc -> m String writeHtmlString' html5 opts d = do (body, context) <- evalStateT (pandocToHtml opts d) @@ -892,6 +910,7 @@ inlineToHtml opts inline = do let number = (length notes) + 1 let ref = show number htmlContents <- blockListToNote opts ref contents + epubVersion <- gets stEPUBVersion -- push contents onto front of notes modify $ \st -> st {stNotes = (htmlContents:notes)} let revealSlash = ['/' | writerSlideVariant opts @@ -901,11 +920,11 @@ inlineToHtml opts inline = do writerIdentifierPrefix opts ++ "fn" ++ ref) ! A.class_ "footnoteRef" ! prefixedId opts ("fnref" ++ ref) - $ (if isJust (writerEpubVersion opts) + $ (if isJust epubVersion then id else H.sup) $ toHtml ref - return $ case writerEpubVersion opts of + return $ case epubVersion of Just EPUB3 -> link ! customAttribute "epub:type" "noteref" _ -> link (Cite cits il)-> do contents <- inlineListToHtml opts il @@ -933,7 +952,8 @@ blockListToNote opts ref blocks = Plain backlink] in do contents <- blockListToHtml opts blocks' let noteItem = H.li ! (prefixedId opts ("fn" ++ ref)) $ contents - let noteItem' = case writerEpubVersion opts of + epubVersion <- gets stEPUBVersion + let noteItem' = case epubVersion of Just EPUB3 -> noteItem ! customAttribute "epub:type" "footnote" _ -> noteItem return $ nl opts >> noteItem' -- cgit v1.2.3 From 4ccbdf4e8dabee046106bda8826f7211d8d23546 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 27 Jan 2017 11:15:42 +0100 Subject: Expose FileTree in Class --- src/Text/Pandoc/Class.hs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index b8befd5b8..348da71ba 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -54,6 +54,7 @@ module Text.Pandoc.Class ( PandocMonad(..) , getOutputFile , PandocIO(..) , PandocPure(..) + , FileTree(..) , FileInfo(..) , runIO , runIOorExplode -- cgit v1.2.3 From 56f74cb0abdcf991f26b7456ed69d99e1993d0ab Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 27 Jan 2017 21:30:35 +0100 Subject: Removed Shared.compactify. Changed signatures on Parsing.tableWith and Parsing.gridTableWith. --- src/Text/Pandoc/Parsing.hs | 24 ++++++++++++------------ src/Text/Pandoc/Readers/RST.hs | 21 +++++++++++++-------- src/Text/Pandoc/Shared.hs | 17 ----------------- 3 files changed, 25 insertions(+), 37 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index e8f4c776c..b1cc8cc48 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -740,11 +740,11 @@ lineBlockLines = try $ do -- | Parse a table using 'headerParser', 'rowParser', -- 'lineParser', and 'footerParser'. tableWith :: Stream s m Char - => ParserT s ParserState m ([[Block]], [Alignment], [Int]) - -> ([Int] -> ParserT s ParserState m [[Block]]) + => ParserT s ParserState m ([Blocks], [Alignment], [Int]) + -> ([Int] -> ParserT s ParserState m [Blocks]) -> ParserT s ParserState m sep -> ParserT s ParserState m end - -> ParserT s ParserState m Block + -> ParserT s ParserState m Blocks tableWith headerParser rowParser lineParser footerParser = try $ do (heads, aligns, indices) <- headerParser lines' <- rowParser indices `sepEndBy1` lineParser @@ -753,7 +753,7 @@ tableWith headerParser rowParser lineParser footerParser = try $ do let widths = if (indices == []) then replicate (length aligns) 0.0 else widthsFromIndices numColumns indices - return $ Table [] aligns widths heads lines' + return $ B.table mempty (zip aligns widths) heads lines' -- Calculate relative widths of table columns, based on indices widthsFromIndices :: Int -- Number of columns on terminal @@ -787,9 +787,9 @@ widthsFromIndices numColumns' indices = -- which may be grid, separated by blank lines, and -- ending with a footer (dashed line followed by blank line). gridTableWith :: Stream [Char] m Char - => ParserT [Char] ParserState m [Block] -- ^ Block list parser + => ParserT [Char] ParserState m Blocks -- ^ Block list parser -> Bool -- ^ Headerless table - -> ParserT [Char] ParserState m Block + -> ParserT [Char] ParserState m Blocks gridTableWith blocks headless = tableWith (gridTableHeader headless blocks) (gridTableRow blocks) (gridTableSep '-') gridTableFooter @@ -818,8 +818,8 @@ gridTableSep ch = try $ gridDashedLines ch >> return '\n' -- | Parse header for a grid table. gridTableHeader :: Stream [Char] m Char => Bool -- ^ Headerless table - -> ParserT [Char] ParserState m [Block] - -> ParserT [Char] ParserState m ([[Block]], [Alignment], [Int]) + -> ParserT [Char] ParserState m Blocks + -> ParserT [Char] ParserState m ([Blocks], [Alignment], [Int]) gridTableHeader headless blocks = try $ do optional blanklines dashes <- gridDashedLines '-' @@ -850,9 +850,9 @@ gridTableRawLine indices = do -- | Parse row of grid table. gridTableRow :: Stream [Char] m Char - => ParserT [Char] ParserState m [Block] + => ParserT [Char] ParserState m Blocks -> [Int] - -> ParserT [Char] ParserState m [[Block]] + -> ParserT [Char] ParserState m [Blocks] gridTableRow blocks indices = do colLines <- many1 (gridTableRawLine indices) let cols = map ((++ "\n") . unlines . removeOneLeadingSpace) $ @@ -867,8 +867,8 @@ removeOneLeadingSpace xs = where startsWithSpace "" = True startsWithSpace (y:_) = y == ' ' -compactifyCell :: [Block] -> [Block] -compactifyCell bs = head $ compactify [bs] +compactifyCell :: Blocks -> Blocks +compactifyCell bs = head $ compactify' [bs] -- | Parse footer for a grid table. gridTableFooter :: Stream s m Char => ParserT s ParserState m [Char] diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 42a1a22e6..96b5c4a9d 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -973,13 +973,13 @@ simpleTableRawLine indices = do return (simpleTableSplitLine indices line) -- Parse a table row and return a list of blocks (columns). -simpleTableRow :: PandocMonad m => [Int] -> RSTParser m [[Block]] +simpleTableRow :: PandocMonad m => [Int] -> RSTParser m [Blocks] simpleTableRow indices = do notFollowedBy' simpleTableFooter firstLine <- simpleTableRawLine indices colLines <- return [] -- TODO let cols = map unlines . transpose $ firstLine : colLines - mapM (parseFromString (B.toList . mconcat <$> many plain)) cols + mapM (parseFromString (mconcat <$> many plain)) cols simpleTableSplitLine :: [Int] -> String -> [String] simpleTableSplitLine indices line = @@ -988,7 +988,7 @@ simpleTableSplitLine indices line = simpleTableHeader :: PandocMonad m => Bool -- ^ Headerless table - -> RSTParser m ([[Block]], [Alignment], [Int]) + -> RSTParser m ([Blocks], [Alignment], [Int]) simpleTableHeader headless = try $ do optional blanklines rawContent <- if headless @@ -1002,7 +1002,7 @@ simpleTableHeader headless = try $ do let rawHeads = if headless then replicate (length dashes) "" else simpleTableSplitLine indices rawContent - heads <- mapM (parseFromString (B.toList . mconcat <$> many plain)) $ + heads <- mapM (parseFromString (mconcat <$> many plain)) $ map trim rawHeads return (heads, aligns, indices) @@ -1011,17 +1011,22 @@ simpleTable :: PandocMonad m => Bool -- ^ Headerless table -> RSTParser m Blocks simpleTable headless = do - Table c a _w h l <- tableWith (simpleTableHeader headless) simpleTableRow sep simpleTableFooter + tbl <- tableWith (simpleTableHeader headless) simpleTableRow + sep simpleTableFooter -- Simple tables get 0s for relative column widths (i.e., use default) - return $ B.singleton $ Table c a (replicate (length a) 0) h l + case B.toList tbl of + [Table c a _w h l] -> return $ B.singleton $ + Table c a (replicate (length a) 0) h l + _ -> do + warning "tableWith returned something unexpected" + return tbl -- TODO error? where sep = return () -- optional (simpleTableSep '-') gridTable :: PandocMonad m => Bool -- ^ Headerless table -> RSTParser m Blocks -gridTable headerless = B.singleton - <$> gridTableWith (B.toList <$> parseBlocks) headerless +gridTable headerless = gridTableWith parseBlocks headerless table :: PandocMonad m => RSTParser m Blocks table = gridTable False <|> simpleTable False <|> diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index f2a80fccf..5f49c2723 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -59,7 +59,6 @@ module Text.Pandoc.Shared ( deNote, stringify, capitalize, - compactify, compactify', compactify'DL, linesToPara, @@ -432,22 +431,6 @@ capitalize = walk go go (Str s) = Str (T.unpack $ T.toUpper $ T.pack s) go x = x --- | Change final list item from @Para@ to @Plain@ if the list contains --- no other @Para@ blocks. -compactify :: [[Block]] -- ^ List of list items (each a list of blocks) - -> [[Block]] -compactify [] = [] -compactify items = - case (init items, last items) of - (_,[]) -> items - (others, final) -> - case last final of - Para a -> case (filter isPara $ concat items) of - -- if this is only Para, change to Plain - [_] -> others ++ [init final ++ [Plain a]] - _ -> items - _ -> items - -- | Change final list item from @Para@ to @Plain@ if the list contains -- no other @Para@ blocks. Like compactify, but operates on @Blocks@ rather -- than @[Block]@. -- cgit v1.2.3 From 5156a4fe3c2438eeb0caa4a85e8adfdbea94e59d Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 27 Jan 2017 21:36:45 +0100 Subject: Shared: rename compactify', compactify'DL -> compactify, compactifyDL. --- src/Text/Pandoc/Parsing.hs | 2 +- src/Text/Pandoc/Readers/Markdown.hs | 8 ++++---- src/Text/Pandoc/Readers/Odt/ContentReader.hs | 4 ++-- src/Text/Pandoc/Readers/Org/Blocks.hs | 8 ++++---- src/Text/Pandoc/Readers/RST.hs | 4 ++-- src/Text/Pandoc/Readers/Txt2Tags.hs | 8 ++++---- src/Text/Pandoc/Shared.hs | 16 ++++++++-------- tests/Tests/Shared.hs | 8 ++++---- 8 files changed, 29 insertions(+), 29 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index b1cc8cc48..5e9ff7fd1 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -868,7 +868,7 @@ removeOneLeadingSpace xs = startsWithSpace (y:_) = y == ' ' compactifyCell :: Blocks -> Blocks -compactifyCell bs = head $ compactify' [bs] +compactifyCell bs = head $ compactify [bs] -- | Parse footer for a grid table. gridTableFooter :: Stream s m Char => ParserT s ParserState m [Char] diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 5052f52bf..1d8f7c78e 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -909,12 +909,12 @@ orderedList = try $ do atMostSpaces (tabStop - (endpos - startpos)) return res ) start' <- option 1 $ guardEnabled Ext_startnum >> return start - return $ B.orderedListWith (start', style, delim) <$> fmap compactify' items + return $ B.orderedListWith (start', style, delim) <$> fmap compactify items bulletList :: PandocMonad m => MarkdownParser m (F Blocks) bulletList = do items <- fmap sequence $ many1 $ listItem bulletListStart - return $ B.bulletList <$> fmap compactify' items + return $ B.bulletList <$> fmap compactify items -- definition lists @@ -972,7 +972,7 @@ compactDefinitionList :: PandocMonad m => MarkdownParser m (F Blocks) compactDefinitionList = do guardEnabled Ext_compact_definition_lists items <- fmap sequence $ many1 $ definitionListItem True - return $ B.definitionList <$> fmap compactify'DL items + return $ B.definitionList <$> fmap compactifyDL items normalDefinitionList :: PandocMonad m => MarkdownParser m (F Blocks) normalDefinitionList = do @@ -1349,7 +1349,7 @@ gridTableRow indices = do colLines <- many1 (gridTableRawLine indices) let cols = map ((++ "\n") . unlines . removeOneLeadingSpace) $ transpose colLines - fmap compactify' <$> fmap sequence (mapM (parseFromString parseBlocks) cols) + fmap compactify <$> fmap sequence (mapM (parseFromString parseBlocks) cols) removeOneLeadingSpace :: [String] -> [String] removeOneLeadingSpace xs = diff --git a/src/Text/Pandoc/Readers/Odt/ContentReader.hs b/src/Text/Pandoc/Readers/Odt/ContentReader.hs index 2672b01ef..a1bd8cb59 100644 --- a/src/Text/Pandoc/Readers/Odt/ContentReader.hs +++ b/src/Text/Pandoc/Readers/Odt/ContentReader.hs @@ -663,7 +663,7 @@ read_list = matchingElement NsText "list" -- read_list_item :: ElementMatcher [Blocks] read_list_item = matchingElement NsText "list-item" - $ liftA (compactify'.(:[])) + $ liftA (compactify.(:[])) ( matchChildContent' [ read_paragraph , read_header , read_list @@ -749,7 +749,7 @@ read_table_row = matchingElement NsTable "table-row" -- read_table_cell :: ElementMatcher [Blocks] read_table_cell = matchingElement NsTable "table-cell" - $ liftA (compactify'.(:[])) + $ liftA (compactify.(:[])) $ matchChildContent' [ read_paragraph ] diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index 8ffc0bb19..78ac8d0d1 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -47,7 +47,7 @@ import Text.Pandoc.Builder ( Inlines, Blocks ) import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Options -import Text.Pandoc.Shared ( compactify', compactify'DL, safeRead ) +import Text.Pandoc.Shared ( compactify, compactifyDL, safeRead ) import Control.Monad ( foldM, guard, mzero, void ) import Data.Char ( isSpace, toLower, toUpper) @@ -898,16 +898,16 @@ list = choice [ definitionList, bulletList, orderedList ] "list" definitionList :: PandocMonad m => OrgParser m (F Blocks) definitionList = try $ do n <- lookAhead (bulletListStart' Nothing) - fmap B.definitionList . fmap compactify'DL . sequence + fmap B.definitionList . fmap compactifyDL . sequence <$> many1 (definitionListItem $ bulletListStart' (Just n)) bulletList :: PandocMonad m => OrgParser m (F Blocks) bulletList = try $ do n <- lookAhead (bulletListStart' Nothing) - fmap B.bulletList . fmap compactify' . sequence + fmap B.bulletList . fmap compactify . sequence <$> many1 (listItem (bulletListStart' $ Just n)) orderedList :: PandocMonad m => OrgParser m (F Blocks) -orderedList = fmap B.orderedList . fmap compactify' . sequence +orderedList = fmap B.orderedList . fmap compactify . sequence <$> many1 (listItem orderedListStart) bulletListStart' :: Monad m => Maybe Int -> OrgParser m Int diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 96b5c4a9d..c9868c11f 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -574,11 +574,11 @@ orderedList :: PandocMonad m => RSTParser m Blocks orderedList = try $ do (start, style, delim) <- lookAhead (anyOrderedListMarker <* spaceChar) items <- many1 (listItem (orderedListStart style delim)) - let items' = compactify' items + let items' = compactify items return $ B.orderedListWith (start, style, delim) items' bulletList :: PandocMonad m => RSTParser m Blocks -bulletList = B.bulletList . compactify' <$> many1 (listItem bulletListStart) +bulletList = B.bulletList . compactify <$> many1 (listItem bulletListStart) -- -- directive (e.g. comment, container, compound-paragraph) diff --git a/src/Text/Pandoc/Readers/Txt2Tags.hs b/src/Text/Pandoc/Readers/Txt2Tags.hs index d2459ba47..9e2b6963d 100644 --- a/src/Text/Pandoc/Readers/Txt2Tags.hs +++ b/src/Text/Pandoc/Readers/Txt2Tags.hs @@ -37,7 +37,7 @@ import Text.Pandoc.Builder ( Inlines, Blocks, trimInlines ) import Data.Monoid ((<>)) import Text.Pandoc.Definition import Text.Pandoc.Options -import Text.Pandoc.Shared (escapeURI,compactify', compactify'DL) +import Text.Pandoc.Shared (escapeURI,compactify, compactifyDL) import Text.Pandoc.Parsing hiding (space, spaces, uri, macro) import Data.Char (toLower) import Data.List (transpose, intersperse, intercalate) @@ -225,16 +225,16 @@ list :: T2T Blocks list = choice [bulletList, orderedList, definitionList] bulletList :: T2T Blocks -bulletList = B.bulletList . compactify' +bulletList = B.bulletList . compactify <$> many1 (listItem bulletListStart parseBlocks) orderedList :: T2T Blocks -orderedList = B.orderedList . compactify' +orderedList = B.orderedList . compactify <$> many1 (listItem orderedListStart parseBlocks) definitionList :: T2T Blocks definitionList = try $ do - B.definitionList . compactify'DL <$> + B.definitionList . compactifyDL <$> many1 (listItem definitionListStart definitionListEnd) definitionListEnd :: T2T (Inlines, [Blocks]) diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 5f49c2723..22847931f 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -59,8 +59,8 @@ module Text.Pandoc.Shared ( deNote, stringify, capitalize, - compactify', - compactify'DL, + compactify, + compactifyDL, linesToPara, Element (..), hierarchicalize, @@ -434,10 +434,10 @@ capitalize = walk go -- | Change final list item from @Para@ to @Plain@ if the list contains -- no other @Para@ blocks. Like compactify, but operates on @Blocks@ rather -- than @[Block]@. -compactify' :: [Blocks] -- ^ List of list items (each a list of blocks) +compactify :: [Blocks] -- ^ List of list items (each a list of blocks) -> [Blocks] -compactify' [] = [] -compactify' items = +compactify [] = [] +compactify items = let (others, final) = (init items, last items) in case reverse (B.toList final) of (Para a:xs) -> case [Para x | Para x <- concatMap B.toList items] of @@ -446,9 +446,9 @@ compactify' items = _ -> items _ -> items --- | Like @compactify'@, but acts on items of definition lists. -compactify'DL :: [(Inlines, [Blocks])] -> [(Inlines, [Blocks])] -compactify'DL items = +-- | Like @compactify@, but acts on items of definition lists. +compactifyDL :: [(Inlines, [Blocks])] -> [(Inlines, [Blocks])] +compactifyDL items = let defs = concatMap snd items in case reverse (concatMap B.toList defs) of (Para x:xs) diff --git a/tests/Tests/Shared.hs b/tests/Tests/Shared.hs index 4ff1dc837..9b9aeb6a3 100644 --- a/tests/Tests/Shared.hs +++ b/tests/Tests/Shared.hs @@ -9,11 +9,11 @@ import Text.Pandoc.Builder import System.FilePath.Posix (joinPath) tests :: [Test] -tests = [ testGroup "compactify'DL" - [ testCase "compactify'DL with empty def" $ - assertBool "compactify'DL" +tests = [ testGroup "compactifyDL" + [ testCase "compactifyDL with empty def" $ + assertBool "compactifyDL" (let x = [(str "word", [para (str "def"), mempty])] - in compactify'DL x == x) + in compactifyDL x == x) ] , testGroup "collapseFilePath" testCollapse ] -- cgit v1.2.3 From 91cdcc796df3db290d1930b159eb3ee2f74d4c03 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 27 Jan 2017 22:39:36 +0100 Subject: HTML: export separate functions for slide formats. writeS5, writeSlideous, writeRevealJs, writeDZSlides, writeSlidy. Removed writerSlideVariant from WriterOptions. --- src/Text/Pandoc.hs | 21 +++-- src/Text/Pandoc/Options.hs | 2 - src/Text/Pandoc/Writers/HTML.hs | 164 +++++++++++++++++++++++++++------------- 3 files changed, 121 insertions(+), 66 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index ea625ffa1..4d0dde96c 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -103,6 +103,11 @@ module Text.Pandoc , writeHtml4String , writeHtml5 , writeHtml5String + , writeRevealJs + , writeS5 + , writeSlidy + , writeSlideous + , writeDZSlides , writeICML , writeDocbook4 , writeDocbook5 @@ -288,17 +293,11 @@ writers = [ ,("html4" , StringWriter writeHtml4String) ,("html5" , StringWriter writeHtml5String) ,("icml" , StringWriter writeICML) - ,("s5" , StringWriter $ \o -> - writeHtml4String o{ writerSlideVariant = S5Slides - , writerTableOfContents = False }) - ,("slidy" , StringWriter $ \o -> - writeHtml4String o{ writerSlideVariant = SlidySlides }) - ,("slideous" , StringWriter $ \o -> - writeHtml4String o{ writerSlideVariant = SlideousSlides }) - ,("dzslides" , StringWriter $ \o -> - writeHtml5String o{ writerSlideVariant = DZSlides }) - ,("revealjs" , StringWriter $ \o -> - writeHtml5String o{ writerSlideVariant = RevealJsSlides }) + ,("s5" , StringWriter writeS5) + ,("slidy" , StringWriter writeSlidy) + ,("slideous" , StringWriter writeSlideous) + ,("dzslides" , StringWriter writeDZSlides) + ,("revealjs" , StringWriter writeRevealJs) ,("docbook" , StringWriter writeDocbook5) ,("docbook4" , StringWriter writeDocbook4) ,("docbook5" , StringWriter writeDocbook5) diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index 755ab9add..ddd81ec51 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -150,7 +150,6 @@ data WriterOptions = WriterOptions , writerVariables :: [(String, String)] -- ^ Variables to set in template , writerTabStop :: Int -- ^ Tabstop for conversion btw spaces and tabs , writerTableOfContents :: Bool -- ^ Include table of contents - , writerSlideVariant :: HTMLSlideVariant -- ^ Are we writing S5, Slidy or Slideous? , writerIncremental :: Bool -- ^ True if lists should be incremental , writerHTMLMathMethod :: HTMLMathMethod -- ^ How to print math in HTML , writerNumberSections :: Bool -- ^ Number sections in LaTeX @@ -190,7 +189,6 @@ instance Default WriterOptions where , writerVariables = [] , writerTabStop = 4 , writerTableOfContents = False - , writerSlideVariant = NoSlides , writerIncremental = False , writerHTMLMathMethod = PlainMath , writerNumberSections = False diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 518848139..9037bfbec 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -33,7 +33,12 @@ module Text.Pandoc.Writers.HTML ( writeHtml4String, writeHtml5, writeHtml5String, - writeHtmlStringForEPUB + writeHtmlStringForEPUB, + writeS5, + writeSlidy, + writeSlideous, + writeDZSlides, + writeRevealJs ) where import Text.Pandoc.Definition import Text.Pandoc.Walk @@ -88,13 +93,15 @@ data WriterState = WriterState , stElement :: Bool -- ^ Processing an Element , stHtml5 :: Bool -- ^ Use HTML5 , stEPUBVersion :: Maybe EPUBVersion -- ^ EPUB version if for epub + , stSlideVariant :: HTMLSlideVariant } defaultWriterState :: WriterState defaultWriterState = WriterState {stNotes= [], stMath = False, stQuotes = False, stHighlighting = False, stSecNum = [], stElement = False, stHtml5 = False, - stEPUBVersion = Nothing} + stEPUBVersion = Nothing, + stSlideVariant = NoSlides} -- Helpers to render HTML with the appropriate function. @@ -113,45 +120,79 @@ nl opts = if writerWrapText opts == WrapNone -- | Convert Pandoc document to Html 5 string. writeHtml5String :: PandocMonad m => WriterOptions -> Pandoc -> m String -writeHtml5String = writeHtmlString' True +writeHtml5String = writeHtmlString' + defaultWriterState{ stHtml5 = True } -- | Convert Pandoc document to Html 5 structure. writeHtml5 :: PandocMonad m => WriterOptions -> Pandoc -> m Html -writeHtml5 = writeHtml' True +writeHtml5 = writeHtml' defaultWriterState{ stHtml5 = True } -- | Convert Pandoc document to Html 4 string. writeHtml4String :: PandocMonad m => WriterOptions -> Pandoc -> m String -writeHtml4String = writeHtmlString' False +writeHtml4String = writeHtmlString' + defaultWriterState{ stHtml5 = False } -- | Convert Pandoc document to Html 4 structure. writeHtml4 :: PandocMonad m => WriterOptions -> Pandoc -> m Html -writeHtml4 = writeHtml' False +writeHtml4 = writeHtml' defaultWriterState{ stHtml5 = False } -- | Convert Pandoc document to Html appropriate for an epub version. writeHtmlStringForEPUB :: PandocMonad m => EPUBVersion -> WriterOptions -> Pandoc -> m String -writeHtmlStringForEPUB version opts d = do - (body, context) <- evalStateT (pandocToHtml opts d) +writeHtmlStringForEPUB version = writeHtmlString' defaultWriterState{ stHtml5 = version == EPUB3, stEPUBVersion = Just version } - return $ case writerTemplate opts of - Nothing -> renderHtml body - Just tpl -> renderTemplate' tpl $ - defField "body" (renderHtml body) context -writeHtmlString' :: PandocMonad m => Bool -> WriterOptions -> Pandoc -> m String -writeHtmlString' html5 opts d = do - (body, context) <- evalStateT (pandocToHtml opts d) - defaultWriterState{ stHtml5 = html5 } +-- | Convert Pandoc document to Reveal JS HTML slide show. +writeRevealJs :: PandocMonad m + => WriterOptions -> Pandoc -> m String +writeRevealJs = writeHtmlSlideShow' RevealJsSlides + +-- | Convert Pandoc document to S5 HTML slide show. +writeS5 :: PandocMonad m + => WriterOptions -> Pandoc -> m String +writeS5 = writeHtmlSlideShow' S5Slides + +-- | Convert Pandoc document to Slidy HTML slide show. +writeSlidy :: PandocMonad m + => WriterOptions -> Pandoc -> m String +writeSlidy = writeHtmlSlideShow' SlidySlides + +-- | Convert Pandoc document to Slideous HTML slide show. +writeSlideous :: PandocMonad m + => WriterOptions -> Pandoc -> m String +writeSlideous = writeHtmlSlideShow' SlideousSlides + +-- | Convert Pandoc document to DZSlides HTML slide show. +writeDZSlides :: PandocMonad m + => WriterOptions -> Pandoc -> m String +writeDZSlides = writeHtmlSlideShow' DZSlides + +writeHtmlSlideShow' :: PandocMonad m + => HTMLSlideVariant -> WriterOptions -> Pandoc -> m String +writeHtmlSlideShow' variant = writeHtmlString' + defaultWriterState{ stSlideVariant = variant + , stHtml5 = case variant of + RevealJsSlides -> True + S5Slides -> False + SlidySlides -> False + DZSlides -> True + SlideousSlides -> False + NoSlides -> False + } + +writeHtmlString' :: PandocMonad m + => WriterState -> WriterOptions -> Pandoc -> m String +writeHtmlString' st opts d = do + (body, context) <- evalStateT (pandocToHtml opts d) st return $ case writerTemplate opts of Nothing -> renderHtml body Just tpl -> renderTemplate' tpl $ defField "body" (renderHtml body) context -writeHtml' :: PandocMonad m => Bool -> WriterOptions -> Pandoc -> m Html -writeHtml' html5 opts d = do - (body, context) <- evalStateT (pandocToHtml opts d) - defaultWriterState{ stHtml5 = html5 } +writeHtml' :: PandocMonad m => WriterState -> WriterOptions -> Pandoc -> m Html +writeHtml' st opts d = do + (body, context) <- evalStateT (pandocToHtml opts d) st return $ case writerTemplate opts of Nothing -> body Just tpl -> renderTemplate' tpl $ @@ -171,11 +212,12 @@ pandocToHtml opts (Pandoc meta blocks) = do let authsMeta = map stringifyHTML $ docAuthors meta let dateMeta = stringifyHTML $ docDate meta let slideLevel = fromMaybe (getSlideLevel blocks) $ writerSlideLevel opts + slideVariant <- gets stSlideVariant let sects = hierarchicalize $ - if writerSlideVariant opts == NoSlides + if slideVariant == NoSlides then blocks else prepSlides slideLevel blocks - toc <- if writerTableOfContents opts + toc <- if writerTableOfContents opts && slideVariant /= S5Slides then tableOfContents opts sects else return Nothing blocks' <- liftM (mconcat . intersperse (nl opts)) $ @@ -195,7 +237,7 @@ pandocToHtml opts (Pandoc meta blocks) = do MathJax url -> H.script ! A.src (toValue url) ! A.type_ "text/javascript" - $ case writerSlideVariant opts of + $ case slideVariant of SlideousSlides -> preEscapedString "MathJax.Hub.Queue([\"Typeset\",MathJax.Hub]);" @@ -247,21 +289,30 @@ prefixedId opts s = "" -> mempty _ -> A.id $ toValue $ writerIdentifierPrefix opts ++ s -toList :: (Html -> Html) -> WriterOptions -> ([Html] -> Html) +toList :: PandocMonad m + => (Html -> Html) + -> WriterOptions + -> [Html] + -> StateT WriterState m Html toList listop opts items = do - if (writerIncremental opts) - then if (writerSlideVariant opts /= RevealJsSlides) - then (listop $ mconcat items) ! A.class_ "incremental" - else listop $ mconcat $ map (! A.class_ "fragment") items - else listop $ mconcat items + slideVariant <- gets stSlideVariant + return $ + if (writerIncremental opts) + then if (slideVariant /= RevealJsSlides) + then (listop $ mconcat items) ! A.class_ "incremental" + else listop $ mconcat $ map (! A.class_ "fragment") items + else listop $ mconcat items -unordList :: WriterOptions -> [Html] -> Html +unordList :: PandocMonad m + => WriterOptions -> [Html] -> StateT WriterState m Html unordList opts = toList H.ul opts . toListItems opts -ordList :: WriterOptions -> [Html] -> Html +ordList :: PandocMonad m + => WriterOptions -> [Html] -> StateT WriterState m Html ordList opts = toList H.ol opts . toListItems opts -defList :: WriterOptions -> [Html] -> Html +defList :: PandocMonad m + => WriterOptions -> [Html] -> StateT WriterState m Html defList opts items = toList H.dl opts (items ++ [nl opts]) -- | Construct table of contents from list of elements. @@ -270,9 +321,9 @@ tableOfContents _ [] = return Nothing tableOfContents opts sects = do contents <- mapM (elementToListItem opts) sects let tocList = catMaybes contents - return $ if null tocList - then Nothing - else Just $ unordList opts tocList + if null tocList + then return Nothing + else Just <$> unordList opts tocList -- | Convert section number to string showSecNum :: [Int] -> String @@ -294,11 +345,12 @@ elementToListItem opts (Sec lev num (id',classes,_) headerText subsecs) else mempty txt <- liftM (sectnum >>) $ inlineListToHtml opts $ walk deNote headerText subHeads <- mapM (elementToListItem opts) subsecs >>= return . catMaybes - let subList = if null subHeads - then mempty - else unordList opts subHeads + subList <- if null subHeads + then return mempty + else unordList opts subHeads -- in reveal.js, we need #/apples, not #apples: - let revealSlash = ['/' | writerSlideVariant opts == RevealJsSlides] + slideVariant <- gets stSlideVariant + let revealSlash = ['/' | slideVariant== RevealJsSlides] return $ Just $ if null id' then (H.a $ toHtml txt) >> subList @@ -311,7 +363,8 @@ elementToListItem _ _ = return Nothing elementToHtml :: PandocMonad m => Int -> WriterOptions -> Element -> StateT WriterState m Html elementToHtml _slideLevel opts (Blk block) = blockToHtml opts block elementToHtml slideLevel opts (Sec level num (id',classes,keyvals) title' elements) = do - let slide = writerSlideVariant opts /= NoSlides && level <= slideLevel + slideVariant <- gets stSlideVariant + let slide = slideVariant /= NoSlides && level <= slideLevel let num' = zipWith (+) num (writerNumberOffset opts ++ repeat 0) modify $ \st -> st{stSecNum = num'} -- update section number html5 <- gets stHtml5 @@ -329,7 +382,7 @@ elementToHtml slideLevel opts (Sec level num (id',classes,keyvals) title' elemen isSec (Blk _) = False let isPause (Blk x) = x == Para [Str ".",Space,Str ".",Space,Str "."] isPause _ = False - let fragmentClass = case writerSlideVariant opts of + let fragmentClass = case slideVariant of RevealJsSlides -> "fragment" _ -> "incremental" let inDiv xs = Blk (RawBlock (Format "html") ("
WriterOptions -> [Html] -> StateT WriterState m Html footnoteSection opts notes = do html5 <- gets stHtml5 + slideVariant <- gets stSlideVariant let hrtag = if html5 then H5.hr else H.hr let container x = if html5 then H5.section ! A.class_ "footnotes" $ x - else if writerSlideVariant opts /= NoSlides + else if slideVariant /= NoSlides then H.div ! A.class_ "footnotes slide" $ x else H.div ! A.class_ "footnotes" $ x return $ @@ -526,9 +580,10 @@ blockToHtml opts (Div attr@(ident, classes, kvs) bs) = do let (divtag, classes') = if html5 && "section" `elem` classes then (H5.section, filter (/= "section") classes) else (H.div, classes) + slideVariant <- gets stSlideVariant return $ if speakerNotes - then case writerSlideVariant opts of + then case slideVariant of RevealJsSlides -> addAttrs opts' attr $ H5.aside $ contents' DZSlides -> (addAttrs opts' attr $ H5.div $ contents') ! (H5.customAttribute "role" "note") @@ -565,11 +620,12 @@ blockToHtml opts (CodeBlock (id',classes,keyvals) rawCode) = do $ H.pre $ H.code $ toHtml adjCode Just h -> modify (\st -> st{ stHighlighting = True }) >> return (addAttrs opts (id',[],keyvals) h) -blockToHtml opts (BlockQuote blocks) = +blockToHtml opts (BlockQuote blocks) = do -- in S5, treat list in blockquote specially -- if default is incremental, make it nonincremental; -- otherwise incremental - if writerSlideVariant opts /= NoSlides + slideVariant <- gets stSlideVariant + if slideVariant /= NoSlides then let inc = not (writerIncremental opts) in case blocks of [BulletList lst] -> blockToHtml (opts {writerIncremental = inc}) @@ -606,7 +662,7 @@ blockToHtml opts (Header level attr@(_,classes,_) lst) = do _ -> H.p contents' blockToHtml opts (BulletList lst) = do contents <- mapM (blockListToHtml opts) lst - return $ unordList opts contents + unordList opts contents blockToHtml opts (OrderedList (startnum, numstyle, _) lst) = do contents <- mapM (blockListToHtml opts) lst html5 <- gets stHtml5 @@ -632,7 +688,8 @@ blockToHtml opts (OrderedList (startnum, numstyle, _) lst) = do else [A.style $ toValue $ "list-style-type: " ++ numstyle'] else []) - return $ foldl (!) (ordList opts contents) attribs + l <- ordList opts contents + return $ foldl (!) l attribs blockToHtml opts (DefinitionList lst) = do contents <- mapM (\(term, defs) -> do term' <- if null term @@ -642,7 +699,7 @@ blockToHtml opts (DefinitionList lst) = do blockListToHtml opts) defs return $ mconcat $ nl opts : term' : nl opts : intersperse (nl opts) defs') lst - return $ defList opts contents + defList opts contents blockToHtml opts (Table capt aligns widths headers rows') = do captionDoc <- if null capt then return mempty @@ -878,9 +935,10 @@ inlineToHtml opts inline = do lift $ obfuscateLink opts attr linkText s (Link attr txt (s,tit)) -> do linkText <- inlineListToHtml opts txt + slideVariant <- gets stSlideVariant let s' = case s of - '#':xs | writerSlideVariant opts == - RevealJsSlides -> '#':'/':xs + '#':xs | slideVariant == RevealJsSlides + -> '#':'/':xs _ -> s let link = H.a ! A.href (toValue s') $ linkText let link' = if txt == [Str (unEscapeString s)] @@ -913,8 +971,8 @@ inlineToHtml opts inline = do epubVersion <- gets stEPUBVersion -- push contents onto front of notes modify $ \st -> st {stNotes = (htmlContents:notes)} - let revealSlash = ['/' | writerSlideVariant opts - == RevealJsSlides] + slideVariant <- gets stSlideVariant + let revealSlash = ['/' | slideVariant == RevealJsSlides] let link = H.a ! A.href (toValue $ "#" ++ revealSlash ++ writerIdentifierPrefix opts ++ "fn" ++ ref) -- cgit v1.2.3 From d2e0592e0174d4890ef0971bd4d47bbb45a98c3a Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 28 Jan 2017 09:52:45 +0100 Subject: LaTeX writer: export writeBeamer. Removed writerBeamer from WriterOptions. --- pandoc.hs | 1 - src/Text/Pandoc.hs | 4 +-- src/Text/Pandoc/Options.hs | 2 -- src/Text/Pandoc/Writers/LaTeX.hs | 75 ++++++++++++++++++++++++++-------------- tests/Tests/Writers/LaTeX.hs | 6 ++-- 5 files changed, 56 insertions(+), 32 deletions(-) (limited to 'src/Text') diff --git a/pandoc.hs b/pandoc.hs index 9ee6e376b..a5163dae5 100644 --- a/pandoc.hs +++ b/pandoc.hs @@ -313,7 +313,6 @@ convertWithOpts opts args = do writerHtmlQTags = htmlQTags, writerTopLevelDivision = topLevelDivision, writerListings = listings, - writerBeamer = False, writerSlideLevel = slideLevel, writerHighlightStyle = highlightStyle, writerSetextHeaders = setextHeaders, diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index 4d0dde96c..a1c3f8486 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -97,6 +97,7 @@ module Text.Pandoc , writePlain , writeRST , writeLaTeX + , writeBeamer , writeConTeXt , writeTexinfo , writeHtml4 @@ -304,8 +305,7 @@ writers = [ ,("opml" , StringWriter writeOPML) ,("opendocument" , StringWriter writeOpenDocument) ,("latex" , StringWriter writeLaTeX) - ,("beamer" , StringWriter $ \o -> - writeLaTeX o{ writerBeamer = True }) + ,("beamer" , StringWriter writeBeamer) ,("context" , StringWriter writeConTeXt) ,("texinfo" , StringWriter writeTexinfo) ,("man" , StringWriter writeMan) diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index ddd81ec51..02ae9f771 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -167,7 +167,6 @@ data WriterOptions = WriterOptions , writerUserDataDir :: Maybe FilePath -- ^ Path of user data directory , writerCiteMethod :: CiteMethod -- ^ How to print cites , writerHtmlQTags :: Bool -- ^ Use @@ tags for quotes in HTML - , writerBeamer :: Bool -- ^ Produce beamer LaTeX slide show , writerSlideLevel :: Maybe Int -- ^ Force header level of slides , writerTopLevelDivision :: TopLevelDivision -- ^ Type of top-level divisions , writerListings :: Bool -- ^ Use listings package for code @@ -205,7 +204,6 @@ instance Default WriterOptions where , writerUserDataDir = Nothing , writerCiteMethod = Citeproc , writerHtmlQTags = False - , writerBeamer = False , writerSlideLevel = Nothing , writerTopLevelDivision = TopLevelDefault , writerListings = False diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 953e4250f..67318a549 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -29,7 +29,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Conversion of 'Pandoc' format into LaTeX. -} -module Text.Pandoc.Writers.LaTeX ( writeLaTeX ) where +module Text.Pandoc.Writers.LaTeX ( + writeLaTeX + , writeBeamer + ) where import Text.Pandoc.Definition import Text.Pandoc.Walk import Text.Pandoc.Shared @@ -76,26 +79,46 @@ data WriterState = , stIncremental :: Bool -- true if beamer lists should be displayed bit by bit , stInternalLinks :: [String] -- list of internal link targets , stUsesEuro :: Bool -- true if euro symbol used + , stBeamer :: Bool -- produce beamer } +startingState :: WriterOptions -> WriterState +startingState options = WriterState { + stInNote = False + , stInQuote = False + , stInMinipage = False + , stInHeading = False + , stNotes = [] + , stOLLevel = 1 + , stOptions = options + , stVerbInNote = False + , stTable = False + , stStrikeout = False + , stUrl = False + , stGraphics = False + , stLHS = False + , stBook = (case writerTopLevelDivision options of + TopLevelPart -> True + TopLevelChapter -> True + _ -> False) + , stCsquotes = False + , stHighlighting = False + , stIncremental = writerIncremental options + , stInternalLinks = [] + , stUsesEuro = False + , stBeamer = False } + -- | Convert Pandoc to LaTeX. writeLaTeX :: PandocMonad m => WriterOptions -> Pandoc -> m String writeLaTeX options document = return $ evalState (pandocToLaTeX options document) $ - WriterState { stInNote = False, stInQuote = False, - stInMinipage = False, stInHeading = False, - stNotes = [], stOLLevel = 1, - stOptions = options, stVerbInNote = False, - stTable = False, stStrikeout = False, - stUrl = False, stGraphics = False, - stLHS = False, - stBook = (case writerTopLevelDivision options of - TopLevelPart -> True - TopLevelChapter -> True - _ -> False), - stCsquotes = False, stHighlighting = False, - stIncremental = writerIncremental options, - stInternalLinks = [], stUsesEuro = False } + startingState options + +-- | Convert Pandoc to LaTeX Beamer. +writeBeamer :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeBeamer options document = return $ + evalState (pandocToLaTeX options document) $ + (startingState options){ stBeamer = True } pandocToLaTeX :: WriterOptions -> Pandoc -> State WriterState String pandocToLaTeX options (Pandoc meta blocks) = do @@ -144,7 +167,8 @@ pandocToLaTeX options (Pandoc meta blocks) = do else case last blocks' of Header 1 _ il -> (init blocks', il) _ -> (blocks', []) - blocks''' <- if writerBeamer options + beamer <- gets stBeamer + blocks''' <- if beamer then toSlides blocks'' else return blocks'' body <- mapM (elementToLaTeX options) $ hierarchicalize blocks''' @@ -171,7 +195,7 @@ pandocToLaTeX options (Pandoc meta blocks) = do defField "body" main $ defField "title-meta" titleMeta $ defField "author-meta" (intercalate "; " authorsMeta) $ - defField "documentclass" (if writerBeamer options + defField "documentclass" (if beamer then ("beamer" :: String) else if stBook st then "book" @@ -186,7 +210,7 @@ pandocToLaTeX options (Pandoc meta blocks) = do defField "book-class" (stBook st) $ defField "euro" (stUsesEuro st) $ defField "listings" (writerListings options || stLHS st) $ - defField "beamer" (writerBeamer options) $ + defField "beamer" beamer $ (if stHighlighting st then case writerHighlightStyle options of Just sty -> @@ -388,7 +412,7 @@ blockToLaTeX :: Block -- ^ Block to convert -> State WriterState Doc blockToLaTeX Null = return empty blockToLaTeX (Div (identifier,classes,kvs) bs) = do - beamer <- writerBeamer `fmap` gets stOptions + beamer <- gets stBeamer ref <- toLabel identifier let linkAnchor = if null identifier then empty @@ -439,7 +463,7 @@ blockToLaTeX (Para [Image attr@(ident, _, _) txt (src,'f':'i':'g':':':tit)]) = d else figure $$ footnotes -- . . . indicates pause in beamer slides blockToLaTeX (Para [Str ".",Space,Str ".",Space,Str "."]) = do - beamer <- writerBeamer `fmap` gets stOptions + beamer <- gets stBeamer if beamer then blockToLaTeX (RawBlock "latex" "\\pause") else inlineListToLaTeX [Str ".",Space,Str ".",Space,Str "."] @@ -448,7 +472,7 @@ blockToLaTeX (Para lst) = blockToLaTeX (LineBlock lns) = do blockToLaTeX $ linesToPara lns blockToLaTeX (BlockQuote lst) = do - beamer <- writerBeamer `fmap` gets stOptions + beamer <- gets stBeamer case lst of [b] | beamer && isListBlock b -> do oldIncremental <- gets stIncremental @@ -527,7 +551,7 @@ blockToLaTeX (RawBlock f x) blockToLaTeX (BulletList []) = return empty -- otherwise latex error blockToLaTeX (BulletList lst) = do incremental <- gets stIncremental - beamer <- writerBeamer `fmap` gets stOptions + beamer <- gets stBeamer let inc = if beamer && incremental then "[<+->]" else "" items <- mapM listItemToLaTeX lst let spacing = if isTightList lst @@ -772,7 +796,8 @@ sectionHeader unnumbered ident level lst = do let topLevelDivision = if book && writerTopLevelDivision opts == TopLevelDefault then TopLevelChapter else writerTopLevelDivision opts - let level' = if writerBeamer opts && + beamer <- gets stBeamer + let level' = if beamer && topLevelDivision `elem` [TopLevelPart, TopLevelChapter] -- beamer has parts but no chapters then if level == 1 then -1 else level - 1 @@ -1022,9 +1047,9 @@ inlineToLaTeX (Note contents) = do (CodeBlock _ _ : _) -> cr _ -> empty let noteContents = nest 2 contents' <> optnl - opts <- gets stOptions + beamer <- gets stBeamer -- in beamer slides, display footnote from current overlay forward - let beamerMark = if writerBeamer opts + let beamerMark = if beamer then text "<.->" else empty modify $ \st -> st{ stNotes = noteContents : stNotes st } diff --git a/tests/Tests/Writers/LaTeX.hs b/tests/Tests/Writers/LaTeX.hs index 00c590370..f54aef4dc 100644 --- a/tests/Tests/Writers/LaTeX.hs +++ b/tests/Tests/Writers/LaTeX.hs @@ -16,6 +16,9 @@ latexListing = latexWithOpts def{ writerListings = True } latexWithOpts :: (ToPandoc a) => WriterOptions -> a -> String latexWithOpts opts = purely (writeLaTeX opts) . toPandoc +beamerWithOpts :: (ToPandoc a) => WriterOptions -> a -> String +beamerWithOpts opts = purely (writeBeamer opts) . toPandoc + {- "my test" =: X =?> Y @@ -95,8 +98,7 @@ tests = [ testGroup "code blocks" beamerTopLevelDiv :: (ToPandoc a) => TopLevelDivision -> a -> String beamerTopLevelDiv division = - latexWithOpts def { writerTopLevelDivision = division - , writerBeamer = True } + beamerWithOpts def { writerTopLevelDivision = division } in [ test (latexTopLevelDiv TopLevelSection) "sections as top-level" $ headers =?> -- cgit v1.2.3 From 411434bf130a3116290c7ca54358678e03d0d92b Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 28 Jan 2017 23:34:29 +0100 Subject: Removed some old commented-out code. --- src/Text/Pandoc.hs | 25 ------------------------- 1 file changed, 25 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index a1c3f8486..3d28dbfb9 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -219,34 +219,9 @@ parseFormatSpec = parse formatSpec "" '-' -> disableExtension ext _ -> enableExtension ext --- TODO: when we get the PandocMonad stuff all sorted out, --- we can simply these types considerably. Errors/MediaBag can be --- part of the monad's internal state. data Reader m = StringReader (ReaderOptions -> String -> m Pandoc) | ByteStringReader (ReaderOptions -> BL.ByteString -> m Pandoc) --- mkStringReader :: (ReaderOptions -> String -> Either PandocError Pandoc) -> Reader IO --- mkStringReader r = StringReader (\o s -> return $ r o s) - --- mkStringReaderWithWarnings :: (ReaderOptions -> String -> Either PandocError (Pandoc, [String])) -> Reader IO --- mkStringReaderWithWarnings r = StringReader $ \o s -> --- case r o s of --- Left err -> return $ Left err --- Right (doc, warnings) -> do --- mapM_ warn warnings --- return (Right doc) - --- mkBSReader :: (ReaderOptions -> BL.ByteString -> Either PandocError (Pandoc, MediaBag)) -> Reader IO --- mkBSReader r = ByteStringReader (\o s -> return $ r o s) - --- mkBSReaderWithWarnings :: (ReaderOptions -> BL.ByteString -> Either PandocError (Pandoc, MediaBag, [String])) -> Reader IO --- mkBSReaderWithWarnings r = ByteStringReader $ \o s -> --- case r o s of --- Left err -> return $ Left err --- Right (doc, mediaBag, warnings) -> do --- mapM_ warn warnings --- return $ Right (doc, mediaBag) - -- | Association list of formats and readers. readers :: PandocMonad m => [(String, Reader m)] readers = [ ("native" , StringReader readNative) -- cgit v1.2.3