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. --- src/Text/Pandoc/Free.hs | 209 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 src/Text/Pandoc/Free.hs (limited to 'src/Text') 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. --- 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 +- 6 files changed, 176 insertions(+), 84 deletions(-) (limited to 'src/Text') 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. --- src/Text/Pandoc/Class.hs | 202 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 src/Text/Pandoc/Class.hs (limited to 'src/Text') 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 --- src/Text/Pandoc/Free.hs | 269 ------------------------------------------------ 1 file changed, 269 deletions(-) delete mode 100644 src/Text/Pandoc/Free.hs (limited to 'src/Text') 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. --- 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 +- 24 files changed, 375 insertions(+), 258 deletions(-) (limited to 'src/Text') 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. --- src/Text/Pandoc.hs | 126 ++++++++++++++++++++++------------------------------- 1 file changed, 53 insertions(+), 73 deletions(-) (limited to 'src/Text') 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. --- 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 +- 8 files changed, 10 insertions(+), 10 deletions(-) (limited to 'src/Text') 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 ++++++--- 2 files changed, 8 insertions(+), 5 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) -- 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. --- src/Text/Pandoc/Class.hs | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src/Text') 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. --- 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 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 -- 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. --- 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 ++++++++++++++++------------- 11 files changed, 488 insertions(+), 416 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/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. --- src/Text/Pandoc/Class.hs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'src/Text') 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. --- src/Text/Pandoc/Parsing.hs | 2 + src/Text/Pandoc/Readers/LaTeX.hs | 144 +++++++++------------------------------ 2 files changed, 35 insertions(+), 111 deletions(-) (limited to 'src/Text') 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 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 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 + -- 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. --- src/Text/Pandoc/PDF.hs | 20 +++++++------------- src/Text/Pandoc/Process.hs | 22 ++++++++-------------- 2 files changed, 15 insertions(+), 27 deletions(-) (limited to 'src/Text') 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. --- src/Text/Pandoc/Options.hs | 6 ++---- src/Text/Pandoc/Writers/Docx.hs | 2 +- src/Text/Pandoc/Writers/ODT.hs | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) (limited to 'src/Text') 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 -- 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 ++++-- 2 files changed, 5 insertions(+), 3 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" -- 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. --- 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 +++- 5 files changed, 20 insertions(+), 159 deletions(-) (limited to 'src/Text') 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 -- 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. --- src/Text/Pandoc/Extensions.hs | 245 ++++++++++++++++++++++++++++++++++++++++++ src/Text/Pandoc/Options.hs | 208 +---------------------------------- 2 files changed, 248 insertions(+), 205 deletions(-) create mode 100644 src/Text/Pandoc/Extensions.hs (limited to 'src/Text') 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. --- 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 ++++++------- 5 files changed, 72 insertions(+), 52 deletions(-) (limited to 'src/Text') 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) -- 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. --- 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 +- 10 files changed, 19 insertions(+), 17 deletions(-) (limited to 'src/Text') 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 -- 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. --- 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 +--- 5 files changed, 7 insertions(+), 10 deletions(-) (limited to 'src/Text') 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 -- 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 --- src/Text/Pandoc/Writers/Markdown.hs | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) (limited to 'src/Text') 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. --- src/Text/Pandoc/Extensions.hs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/Text') 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 -- 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`. --- src/Text/Pandoc/Options.hs | 2 -- src/Text/Pandoc/Writers/ConTeXt.hs | 2 +- src/Text/Pandoc/Writers/LaTeX.hs | 6 +++--- 3 files changed, 4 insertions(+), 6 deletions(-) (limited to 'src/Text') 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. --- src/Text/Pandoc/PDF.hs | 34 ++++++++++++++++++++-------------- src/Text/Pandoc/SelfContained.hs | 6 +++--- 2 files changed, 23 insertions(+), 17 deletions(-) (limited to 'src/Text') 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. --- src/Text/Pandoc/Options.hs | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/Text') 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. --- 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 +++++----- 5 files changed, 10 insertions(+), 17 deletions(-) (limited to 'src/Text') 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. --- 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 -- 25 files changed, 13 insertions(+), 89 deletions(-) (limited to 'src/Text') 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 " " -- 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. --- 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 ++++++++++++------- 4 files changed, 35 insertions(+), 39 deletions(-) (limited to 'src/Text') 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 -- 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. --- 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 +- 9 files changed, 41 insertions(+), 35 deletions(-) (limited to 'src/Text') 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. --- 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 +-- 6 files changed, 115 insertions(+), 123 deletions(-) (limited to 'src/Text') 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. --- 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 +++------- 4 files changed, 13 insertions(+), 25 deletions(-) (limited to 'src/Text') 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' = -- 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". --- 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 +- 8 files changed, 103 insertions(+), 71 deletions(-) (limited to 'src/Text') 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 -- 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. --- src/Text/Pandoc.hs | 12 +++++++----- src/Text/Pandoc/Templates.hs | 1 + src/Text/Pandoc/Writers/EPUB.hs | 31 ++++++++++++++++++++++++------- 3 files changed, 32 insertions(+), 12 deletions(-) (limited to 'src/Text') 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. --- src/Text/Pandoc.hs | 9 +++--- src/Text/Pandoc/Options.hs | 2 -- src/Text/Pandoc/Templates.hs | 15 ++++----- src/Text/Pandoc/Writers/Docbook.hs | 62 ++++++++++++++++++++++++-------------- 4 files changed, 53 insertions(+), 35 deletions(-) (limited to 'src/Text') 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 -- 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 ++++++++-------- 7 files changed, 25 insertions(+), 25 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) -- 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. --- src/Text/Pandoc.hs | 4 +-- src/Text/Pandoc/Options.hs | 2 -- src/Text/Pandoc/Writers/LaTeX.hs | 75 ++++++++++++++++++++++++++-------------- 3 files changed, 52 insertions(+), 29 deletions(-) (limited to 'src/Text') 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 } -- 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 From 5e2754f515c954e6c2d2aaa387ea800ed55a9775 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 30 Jan 2017 10:19:30 +0100 Subject: Make epub an alias for epub3, not epub2. --- src/Text/Pandoc.hs | 2 +- src/Text/Pandoc/Templates.hs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index 3d28dbfb9..3b7c8f94d 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -261,7 +261,7 @@ writers = [ ,("json" , StringWriter $ \o d -> return $ writeJSON o d) ,("docx" , ByteStringWriter writeDocx) ,("odt" , ByteStringWriter writeODT) - ,("epub" , ByteStringWriter writeEPUB2) + ,("epub" , ByteStringWriter writeEPUB3) ,("epub2" , ByteStringWriter writeEPUB2) ,("epub3" , ByteStringWriter writeEPUB3) ,("fb2" , StringWriter writeFB2) diff --git a/src/Text/Pandoc/Templates.hs b/src/Text/Pandoc/Templates.hs index ddb073409..705ac54c9 100644 --- a/src/Text/Pandoc/Templates.hs +++ b/src/Text/Pandoc/Templates.hs @@ -62,7 +62,7 @@ getDefaultTemplate user writer = do "odt" -> getDefaultTemplate user "opendocument" "html" -> getDefaultTemplate user "html5" "docbook" -> getDefaultTemplate user "docbook5" - "epub" -> getDefaultTemplate user "epub2" + "epub" -> getDefaultTemplate user "epub3" "markdown_strict" -> getDefaultTemplate user "markdown" "multimarkdown" -> getDefaultTemplate user "markdown" "markdown_github" -> getDefaultTemplate user "markdown" -- cgit v1.2.3 From 7018003811f2b606808ddecb5c1ce12e27ad7d51 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Mon, 30 Jan 2017 11:31:50 +0100 Subject: `--mathml` and MathML in HTMLMathMethod longer take an argument. The argument was for a bridge javascript that used to be necessary in 2004. We have removed the script already. --- src/Text/Pandoc/Options.hs | 2 +- src/Text/Pandoc/Writers/Docbook.hs | 8 ++++---- src/Text/Pandoc/Writers/EPUB.hs | 2 +- src/Text/Pandoc/Writers/HTML.hs | 8 ++------ src/Text/Pandoc/Writers/TEI.hs | 4 ++-- 5 files changed, 10 insertions(+), 14 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index 02ae9f771..c00981d5d 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -89,7 +89,7 @@ data HTMLMathMethod = PlainMath | JsMath (Maybe String) -- url of jsMath load script | GladTeX | WebTeX String -- url of TeX->image script. - | MathML (Maybe String) -- url of MathMLinHTML.js + | MathML | MathJax String -- url of MathJax.js | KaTeX String String -- url of stylesheet and katex.js deriving (Show, Read, Eq, Data, Typeable, Generic) diff --git a/src/Text/Pandoc/Writers/Docbook.hs b/src/Text/Pandoc/Writers/Docbook.hs index 53618d173..4c5b255d8 100644 --- a/src/Text/Pandoc/Writers/Docbook.hs +++ b/src/Text/Pandoc/Writers/Docbook.hs @@ -117,8 +117,8 @@ writeDocbook opts (Pandoc meta blocks) = do main <- (render' . vcat) <$> (mapM (elementToDocbook opts' startLvl) elements) let context = defField "body" main $ defField "mathml" (case writerHTMLMathMethod opts of - MathML _ -> True - _ -> False) + MathML -> True + _ -> False) $ metadata return $ case writerTemplate opts of Nothing -> main @@ -421,8 +421,8 @@ inlineToDocbook opts (Note contents) = inTagsIndented "footnote" <$> blocksToDocbook opts contents isMathML :: HTMLMathMethod -> Bool -isMathML (MathML _) = True -isMathML _ = False +isMathML MathML = True +isMathML _ = False idAndRole :: Attr -> [(String, String)] idAndRole (id',cls,_) = ident ++ role diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index ae77c10a2..d21f7bea1 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -382,7 +382,7 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do , writerVariables = vars , writerHTMLMathMethod = if epub3 - then MathML Nothing + then MathML else writerHTMLMathMethod opts , writerWrapText = WrapAuto } metadata <- getEPUBMetadata opts' meta diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 9037bfbec..d1fb3dda7 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -230,10 +230,6 @@ pandocToHtml opts (Pandoc meta blocks) = do H.script ! A.src (toValue url) ! A.type_ "text/javascript" $ mempty - MathML (Just url) -> - H.script ! A.src (toValue url) - ! A.type_ "text/javascript" - $ mempty MathJax url -> H.script ! A.src (toValue url) ! A.type_ "text/javascript" @@ -903,7 +899,7 @@ inlineToHtml opts inline = do return $ case t of InlineMath -> preEscapedString $ "" ++ str ++ "" DisplayMath -> preEscapedString $ "" ++ str ++ "" - MathML _ -> do + MathML -> do let conf = useShortEmptyTags (const False) defaultConfigPP res <- lift $ convertMath writeMathML t str @@ -1061,6 +1057,6 @@ isMathEnvironment s = "\\begin{" `isPrefixOf` s && allowsMathEnvironments :: HTMLMathMethod -> Bool allowsMathEnvironments (MathJax _) = True -allowsMathEnvironments (MathML _) = True +allowsMathEnvironments (MathML) = True allowsMathEnvironments (WebTeX _) = True allowsMathEnvironments _ = False diff --git a/src/Text/Pandoc/Writers/TEI.hs b/src/Text/Pandoc/Writers/TEI.hs index c589c0c36..a54d42c53 100644 --- a/src/Text/Pandoc/Writers/TEI.hs +++ b/src/Text/Pandoc/Writers/TEI.hs @@ -76,8 +76,8 @@ writeTEI opts (Pandoc meta blocks) = return $ main = render' $ vcat (map (elementToTEI opts startLvl) elements) context = defField "body" main $ defField "mathml" (case writerHTMLMathMethod opts of - MathML _ -> True - _ -> False) + MathML -> True + _ -> False) $ metadata in case writerTemplate opts of Nothing -> main -- cgit v1.2.3 From 59a2e5575a8349821a3d1108771527e47bb260e9 Mon Sep 17 00:00:00 2001 From: Alexander Krotov Date: Wed, 1 Feb 2017 15:00:40 +0400 Subject: Reduce state in Org writer (#3404) --- src/Text/Pandoc/Writers/Org.hs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index 09c924397..316cc61cf 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -46,8 +46,6 @@ import Text.Pandoc.Class (PandocMonad) data WriterState = WriterState { stNotes :: [[Block]] - , stLinks :: Bool - , stImages :: Bool , stHasMath :: Bool , stOptions :: WriterOptions } @@ -55,8 +53,8 @@ data WriterState = -- | Convert Pandoc to Org. writeOrg :: PandocMonad m => WriterOptions -> Pandoc -> m String writeOrg opts document = return $ - let st = WriterState { stNotes = [], stLinks = False, - stImages = False, stHasMath = False, + let st = WriterState { stNotes = [], + stHasMath = False, stOptions = opts } in evalState (pandocToOrg document) st @@ -361,13 +359,10 @@ inlineToOrg SoftBreak = do inlineToOrg (Link _ txt (src, _)) = do case txt of [Str x] | escapeURI x == src -> -- autolink - do modify $ \s -> s{ stLinks = True } - return $ "[[" <> text (orgPath x) <> "]]" + do return $ "[[" <> text (orgPath x) <> "]]" _ -> do contents <- inlineListToOrg txt - modify $ \s -> s{ stLinks = True } return $ "[[" <> text (orgPath src) <> "][" <> contents <> "]]" inlineToOrg (Image _ _ (source, _)) = do - modify $ \s -> s{ stImages = True } return $ "[[" <> text (orgPath source) <> "]]" inlineToOrg (Note contents) = do -- add to notes in state -- cgit v1.2.3 From 9327e70c1081f83f60bbc473f60bb25d7cee314a Mon Sep 17 00:00:00 2001 From: Alexander Krotov Date: Thu, 2 Feb 2017 04:41:22 +0400 Subject: Org.hs: remove misleading comment This comment is likely copied from RST.hs where 'refs' variable indeed exists, but makes no sense here. --- src/Text/Pandoc/Writers/Org.hs | 1 - 1 file changed, 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index 316cc61cf..ae1c92a97 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -71,7 +71,6 @@ pandocToOrg (Pandoc meta blocks) = do meta body <- blockListToOrg blocks notes <- liftM (reverse . stNotes) get >>= notesToOrg - -- note that the notes may contain refs, so we do them first hasMath <- liftM stHasMath get let main = render colwidth $ foldl ($+$) empty $ [body, notes] let context = defField "body" main -- cgit v1.2.3 From 5cd475be7057487ba4f63e2257b6f65b975acd58 Mon Sep 17 00:00:00 2001 From: Mauro Bieg Date: Fri, 3 Feb 2017 09:53:43 +0100 Subject: HTML and DocBook writers: fix internal links with writerIdentifierPrefix opt (#3398) closes #3397 --- src/Text/Pandoc/Writers/Docbook.hs | 2 +- src/Text/Pandoc/Writers/HTML.hs | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docbook.hs b/src/Text/Pandoc/Writers/Docbook.hs index 4c5b255d8..482cae3db 100644 --- a/src/Text/Pandoc/Writers/Docbook.hs +++ b/src/Text/Pandoc/Writers/Docbook.hs @@ -405,7 +405,7 @@ inlineToDocbook opts (Link attr txt (src, _)) | otherwise = do version <- ask (if isPrefixOf "#" src - then inTags False "link" $ ("linkend", drop 1 src) : idAndRole attr + then inTags False "link" $ ("linkend", writerIdentifierPrefix opts ++ drop 1 src) : idAndRole attr else if version == DocBook5 then inTags False "link" $ ("xlink:href", src) : idAndRole attr else inTags False "ulink" $ ("url", src) : idAndRole attr ) diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index d1fb3dda7..64eccd35e 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -933,9 +933,11 @@ inlineToHtml opts inline = do linkText <- inlineListToHtml opts txt slideVariant <- gets stSlideVariant let s' = case s of - '#':xs | slideVariant == RevealJsSlides - -> '#':'/':xs - _ -> s + '#':xs -> let prefix = if slideVariant == RevealJsSlides + then "/" + else writerIdentifierPrefix opts + in '#' : prefix ++ xs + _ -> s let link = H.a ! A.href (toValue s') $ linkText let link' = if txt == [Str (unEscapeString s)] then link ! A.class_ "uri" -- cgit v1.2.3 From 23e6495624682f0c8d130a3f6db5dc60056015fa Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 3 Feb 2017 11:57:07 +0100 Subject: Docx reader: Don't drop smartTag contents. This just parses inside smartTags and yields their contents, ignoring the attributes of the smartTag. @jkr, you may want to adjust this, but I wanted to get a fix in as fast as possible for the dropped content. Closes #2242; see also #3412. --- src/Text/Pandoc/Readers/Docx.hs | 3 +++ src/Text/Pandoc/Readers/Docx/Parse.hs | 5 +++++ 2 files changed, 8 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index 490fdf878..2b92cceee 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -411,6 +411,9 @@ parPartToInlines (ExternalHyperLink target runs) = do return $ link target "" ils parPartToInlines (PlainOMath exps) = do return $ math $ writeTeX exps +parPartToInlines (SmartTag runs) = do + ils <- smushInlines <$> mapM runToInlines runs + return ils isAnchorSpan :: Inline -> Bool isAnchorSpan (Span (_, classes, kvs) _) = diff --git a/src/Text/Pandoc/Readers/Docx/Parse.hs b/src/Text/Pandoc/Readers/Docx/Parse.hs index 6cd3a49b6..0532b5497 100644 --- a/src/Text/Pandoc/Readers/Docx/Parse.hs +++ b/src/Text/Pandoc/Readers/Docx/Parse.hs @@ -218,6 +218,7 @@ data ParPart = PlainRun Run | Drawing FilePath String String B.ByteString Extent -- title, alt | Chart -- placeholder for now | PlainOMath [Exp] + | SmartTag [Run] deriving Show data Run = Run RunStyle [RunElem] @@ -708,6 +709,10 @@ elemToParPart ns element , Just cDate <- findAttr (elemName ns "w" "date") element = do runs <- mapD (elemToRun ns) (elChildren element) return $ Deletion cId cAuthor cDate runs +elemToParPart ns element + | isElem ns "w" "smartTag" element = do + runs <- mapD (elemToRun ns) (elChildren element) + return $ SmartTag runs elemToParPart ns element | isElem ns "w" "bookmarkStart" element , Just bmId <- findAttr (elemName ns "w" "id") element -- cgit v1.2.3 From 76aa43c579cf9662143d2f145cc44f3a094d139a Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 3 Feb 2017 22:23:07 +0100 Subject: Docx reader: handle local namespace declarations. Previously we didn't recognize math, for example, when the xmlns declaration occured on the element and not the root. Now we recognize either. Closes #3365. This patch defines findChildByName, findChildrenByName, and findAttrByName in Util, and uses these in Parse. --- src/Text/Pandoc/Readers/Docx/Parse.hs | 210 +++++++++++++++++----------------- src/Text/Pandoc/Readers/Docx/Util.hs | 24 +++- 2 files changed, 127 insertions(+), 107 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx/Parse.hs b/src/Text/Pandoc/Readers/Docx/Parse.hs index 0532b5497..221a1d10a 100644 --- a/src/Text/Pandoc/Readers/Docx/Parse.hs +++ b/src/Text/Pandoc/Readers/Docx/Parse.hs @@ -295,7 +295,7 @@ archiveToDocument zf = do entry <- maybeToD $ findEntryByPath "word/document.xml" zf docElem <- maybeToD $ (parseXMLDoc . UTF8.toStringLazy . fromEntry) entry let namespaces = elemToNameSpaces docElem - bodyElem <- maybeToD $ findChild (elemName namespaces "w" "body") docElem + bodyElem <- maybeToD $ findChildByName namespaces "w" "body" docElem body <- elemToBody namespaces bodyElem return $ Document namespaces body @@ -323,15 +323,15 @@ archiveToStyles zf = isBasedOnStyle :: (ElemToStyle a) => NameSpaces -> Element -> Maybe a -> Bool isBasedOnStyle ns element parentStyle | isElem ns "w" "style" element - , Just styleType <- findAttr (elemName ns "w" "type") element + , Just styleType <- findAttrByName ns "w" "type" element , styleType == cStyleType parentStyle - , Just basedOnVal <- findChild (elemName ns "w" "basedOn") element >>= - findAttr (elemName ns "w" "val") + , Just basedOnVal <- findChildByName ns "w" "basedOn" element >>= + findAttrByName ns "w" "val" , Just ps <- parentStyle = (basedOnVal == getStyleId ps) | isElem ns "w" "style" element - , Just styleType <- findAttr (elemName ns "w" "type") element + , Just styleType <- findAttrByName ns "w" "type" element , styleType == cStyleType parentStyle - , Nothing <- findChild (elemName ns "w" "basedOn") element + , Nothing <- findChildByName ns "w" "basedOn" element , Nothing <- parentStyle = True | otherwise = False @@ -344,8 +344,8 @@ instance ElemToStyle CharStyle where cStyleType _ = "character" elemToStyle ns element parentStyle | isElem ns "w" "style" element - , Just "character" <- findAttr (elemName ns "w" "type") element - , Just styleId <- findAttr (elemName ns "w" "styleId") element = + , Just "character" <- findAttrByName ns "w" "type" element + , Just styleId <- findAttrByName ns "w" "styleId" element = Just (styleId, elemToRunStyle ns element parentStyle) | otherwise = Nothing getStyleId s = fst s @@ -354,8 +354,8 @@ instance ElemToStyle ParStyle where cStyleType _ = "paragraph" elemToStyle ns element parentStyle | isElem ns "w" "style" element - , Just "paragraph" <- findAttr (elemName ns "w" "type") element - , Just styleId <- findAttr (elemName ns "w" "styleId") element = + , Just "paragraph" <- findAttrByName ns "w" "type" element + , Just styleId <- findAttrByName ns "w" "styleId" element = Just (styleId, elemToParStyleData ns element parentStyle) | otherwise = Nothing getStyleId s = fst s @@ -447,17 +447,17 @@ lookupLevel numId ilvl (Numbering _ numbs absNumbs) = do numElemToNum :: NameSpaces -> Element -> Maybe Numb numElemToNum ns element | isElem ns "w" "num" element = do - numId <- findAttr (elemName ns "w" "numId") element - absNumId <- findChild (elemName ns "w" "abstractNumId") element - >>= findAttr (elemName ns "w" "val") + numId <- findAttrByName ns "w" "numId" element + absNumId <- findChildByName ns "w" "abstractNumId" element + >>= findAttrByName ns "w" "val" return $ Numb numId absNumId numElemToNum _ _ = Nothing absNumElemToAbsNum :: NameSpaces -> Element -> Maybe AbstractNumb absNumElemToAbsNum ns element | isElem ns "w" "abstractNum" element = do - absNumId <- findAttr (elemName ns "w" "abstractNumId") element - let levelElems = findChildren (elemName ns "w" "lvl") element + absNumId <- findAttrByName ns "w" "abstractNumId" element + let levelElems = findChildrenByName ns "w" "lvl" element levels = mapMaybe (levelElemToLevel ns) levelElems return $ AbstractNumb absNumId levels absNumElemToAbsNum _ _ = Nothing @@ -465,13 +465,13 @@ absNumElemToAbsNum _ _ = Nothing levelElemToLevel :: NameSpaces -> Element -> Maybe Level levelElemToLevel ns element | isElem ns "w" "lvl" element = do - ilvl <- findAttr (elemName ns "w" "ilvl") element - fmt <- findChild (elemName ns "w" "numFmt") element - >>= findAttr (elemName ns "w" "val") - txt <- findChild (elemName ns "w" "lvlText") element - >>= findAttr (elemName ns "w" "val") - let start = findChild (elemName ns "w" "start") element - >>= findAttr (elemName ns "w" "val") + ilvl <- findAttrByName ns "w" "ilvl" element + fmt <- findChildByName ns "w" "numFmt" element + >>= findAttrByName ns "w" "val" + txt <- findChildByName ns "w" "lvlText" element + >>= findAttrByName ns "w" "val" + let start = findChildByName ns "w" "start" element + >>= findAttrByName ns "w" "val" >>= (\s -> listToMaybe (map fst (reads s :: [(Integer, String)]))) return (ilvl, fmt, txt, start) levelElemToLevel _ _ = Nothing @@ -483,8 +483,8 @@ archiveToNumbering' zf = do Just entry -> do numberingElem <- (parseXMLDoc . UTF8.toStringLazy . fromEntry) entry let namespaces = elemToNameSpaces numberingElem - numElems = findChildren (elemName namespaces "w" "num") numberingElem - absNumElems = findChildren (elemName namespaces "w" "abstractNum") numberingElem + numElems = findChildrenByName namespaces "w" "num" numberingElem + absNumElems = findChildrenByName namespaces "w" "abstractNum" numberingElem nums = mapMaybe (numElemToNum namespaces) numElems absNums = mapMaybe (absNumElemToAbsNum namespaces) absNumElems return $ Numbering namespaces nums absNums @@ -497,9 +497,9 @@ elemToNotes :: NameSpaces -> String -> Element -> Maybe (M.Map String Element) elemToNotes ns notetype element | isElem ns "w" (notetype ++ "s") element = let pairs = mapMaybe - (\e -> findAttr (elemName ns "w" "id") e >>= + (\e -> findAttrByName ns "w" "id" e >>= (\a -> Just (a, e))) - (findChildren (elemName ns "w" notetype) element) + (findChildrenByName ns "w" notetype element) in Just $ M.fromList $ pairs elemToNotes _ _ _ = Nothing @@ -508,9 +508,9 @@ elemToComments :: NameSpaces -> Element -> M.Map String Element elemToComments ns element | isElem ns "w" "comments" element = let pairs = mapMaybe - (\e -> findAttr (elemName ns "w" "id") e >>= + (\e -> findAttrByName ns "w" "id" e >>= (\a -> Just (a, e))) - (findChildren (elemName ns "w" "comment") element) + (findChildrenByName ns "w" "comment" element) in M.fromList $ pairs elemToComments _ _ = M.empty @@ -521,16 +521,16 @@ elemToComments _ _ = M.empty elemToTblGrid :: NameSpaces -> Element -> D TblGrid elemToTblGrid ns element | isElem ns "w" "tblGrid" element = - let cols = findChildren (elemName ns "w" "gridCol") element + let cols = findChildrenByName ns "w" "gridCol" element in - mapD (\e -> maybeToD (findAttr (elemName ns "w" "val") e >>= stringToInteger)) + mapD (\e -> maybeToD (findAttrByName ns "w" "val" e >>= stringToInteger)) cols elemToTblGrid _ _ = throwError WrongElem elemToTblLook :: NameSpaces -> Element -> D TblLook elemToTblLook ns element | isElem ns "w" "tblLook" element = - let firstRow = findAttr (elemName ns "w" "firstRow") element - val = findAttr (elemName ns "w" "val") element + let firstRow = findAttrByName ns "w" "firstRow" element + val = findAttrByName ns "w" "val" element firstRowFmt = case firstRow of Just "1" -> True @@ -545,7 +545,7 @@ elemToTblLook _ _ = throwError WrongElem elemToRow :: NameSpaces -> Element -> D Row elemToRow ns element | isElem ns "w" "tr" element = do - let cellElems = findChildren (elemName ns "w" "tc") element + let cellElems = findChildrenByName ns "w" "tc" element cells <- mapD (elemToCell ns) cellElems return $ Row cells elemToRow _ _ = throwError WrongElem @@ -561,13 +561,13 @@ elemToParIndentation :: NameSpaces -> Element -> Maybe ParIndentation elemToParIndentation ns element | isElem ns "w" "ind" element = Just $ ParIndentation { leftParIndent = - findAttr (elemName ns "w" "left") element >>= + findAttrByName ns "w" "left" element >>= stringToInteger , rightParIndent = - findAttr (elemName ns "w" "right") element >>= + findAttrByName ns "w" "right" element >>= stringToInteger , hangingParIndent = - findAttr (elemName ns "w" "hanging") element >>= + findAttrByName ns "w" "hanging" element >>= stringToInteger} elemToParIndentation _ _ = Nothing @@ -583,7 +583,7 @@ stringToInteger s = listToMaybe $ map fst (reads s :: [(Integer, String)]) elemToBodyPart :: NameSpaces -> Element -> D BodyPart elemToBodyPart ns element | isElem ns "w" "p" element - , (c:_) <- findChildren (elemName ns "m" "oMathPara") element = + , (c:_) <- findChildrenByName ns "m" "oMathPara" element = do expsLst <- eitherToD $ readOMML $ showElement c return $ OMathPara expsLst @@ -611,15 +611,15 @@ elemToBodyPart ns element _ -> return $ Paragraph parstyle parparts elemToBodyPart ns element | isElem ns "w" "tbl" element = do - let caption' = findChild (elemName ns "w" "tblPr") element - >>= findChild (elemName ns "w" "tblCaption") - >>= findAttr (elemName ns "w" "val") + let caption' = findChildByName ns "w" "tblPr" element + >>= findChildByName ns "w" "tblCaption" + >>= findAttrByName ns "w" "val" caption = (fromMaybe "" caption') - grid' = case findChild (elemName ns "w" "tblGrid") element of + grid' = case findChildByName ns "w" "tblGrid" element of Just g -> elemToTblGrid ns g Nothing -> return [] - tblLook' = case findChild (elemName ns "w" "tblPr") element >>= - findChild (elemName ns "w" "tblLook") + tblLook' = case findChildByName ns "w" "tblPr" element >>= + findChildByName ns "w" "tblLook" of Just l -> elemToTblLook ns l Nothing -> return defaultTblLook @@ -650,12 +650,12 @@ expandDrawingId s = do getTitleAndAlt :: NameSpaces -> Element -> (String, String) getTitleAndAlt ns element = - let mbDocPr = findChild (elemName ns "wp" "inline") element >>= - findChild (elemName ns "wp" "docPr") - title = case mbDocPr >>= findAttr (elemName ns "" "title") of + let mbDocPr = findChildByName ns "wp" "inline" element >>= + findChildByName ns "wp" "docPr" + title = case mbDocPr >>= findAttrByName ns "" "title" of Just title' -> title' Nothing -> "" - alt = case mbDocPr >>= findAttr (elemName ns "" "descr") of + alt = case mbDocPr >>= findAttrByName ns "" "descr" of Just alt' -> alt' Nothing -> "" in (title, alt) @@ -663,13 +663,13 @@ getTitleAndAlt ns element = elemToParPart :: NameSpaces -> Element -> D ParPart elemToParPart ns element | isElem ns "w" "r" element - , Just drawingElem <- findChild (elemName ns "w" "drawing") element + , Just drawingElem <- findChildByName ns "w" "drawing" element , pic_ns <- "http://schemas.openxmlformats.org/drawingml/2006/picture" , Just picElem <- findElement (QName "pic" (Just pic_ns) (Just "pic")) drawingElem = let (title, alt) = getTitleAndAlt ns drawingElem a_ns = "http://schemas.openxmlformats.org/drawingml/2006/main" drawing = findElement (QName "blip" (Just a_ns) (Just "a")) picElem - >>= findAttr (elemName ns "r" "embed") + >>= findAttrByName ns "r" "embed" in case drawing of Just s -> expandDrawingId s >>= (\(fp, bs) -> return $ Drawing fp title alt bs $ elemToExtent drawingElem) @@ -677,9 +677,9 @@ elemToParPart ns element -- The below is an attempt to deal with images in deprecated vml format. elemToParPart ns element | isElem ns "w" "r" element - , Just _ <- findChild (elemName ns "w" "pict") element = + , Just _ <- findChildByName ns "w" "pict" element = let drawing = findElement (elemName ns "v" "imagedata") element - >>= findAttr (elemName ns "r" "id") + >>= findAttrByName ns "r" "id" in case drawing of -- Todo: check out title and attr for deprecated format. @@ -688,7 +688,7 @@ elemToParPart ns element -- Chart elemToParPart ns element | isElem ns "w" "r" element - , Just drawingElem <- findChild (elemName ns "w" "drawing") element + , Just drawingElem <- findChildByName 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 @@ -697,16 +697,16 @@ elemToParPart ns element elemToRun ns element >>= (\r -> return $ PlainRun r) elemToParPart ns element | isElem ns "w" "ins" element || isElem ns "w" "moveTo" element - , Just cId <- findAttr (elemName ns "w" "id") element - , Just cAuthor <- findAttr (elemName ns "w" "author") element - , Just cDate <- findAttr (elemName ns "w" "date") element = do + , Just cId <- findAttrByName ns "w" "id" element + , Just cAuthor <- findAttrByName ns "w" "author" element + , Just cDate <- findAttrByName ns "w" "date" element = do runs <- mapD (elemToRun ns) (elChildren element) return $ Insertion cId cAuthor cDate runs elemToParPart ns element | isElem ns "w" "del" element || isElem ns "w" "moveFrom" element - , Just cId <- findAttr (elemName ns "w" "id") element - , Just cAuthor <- findAttr (elemName ns "w" "author") element - , Just cDate <- findAttr (elemName ns "w" "date") element = do + , Just cId <- findAttrByName ns "w" "id" element + , Just cAuthor <- findAttrByName ns "w" "author" element + , Just cDate <- findAttrByName ns "w" "date" element = do runs <- mapD (elemToRun ns) (elChildren element) return $ Deletion cId cAuthor cDate runs elemToParPart ns element @@ -715,36 +715,36 @@ elemToParPart ns element return $ SmartTag runs elemToParPart ns element | isElem ns "w" "bookmarkStart" element - , Just bmId <- findAttr (elemName ns "w" "id") element - , Just bmName <- findAttr (elemName ns "w" "name") element = + , Just bmId <- findAttrByName ns "w" "id" element + , Just bmName <- findAttrByName ns "w" "name" element = return $ BookMark bmId bmName elemToParPart ns element | isElem ns "w" "hyperlink" element - , Just relId <- findAttr (elemName ns "r" "id") element = do + , Just relId <- findAttrByName ns "r" "id" element = do location <- asks envLocation runs <- mapD (elemToRun ns) (elChildren element) rels <- asks envRelationships case lookupRelationship location relId rels of Just target -> do - case findAttr (elemName ns "w" "anchor") element of + case findAttrByName ns "w" "anchor" element of Just anchor -> return $ ExternalHyperLink (target ++ '#':anchor) runs Nothing -> return $ ExternalHyperLink target runs Nothing -> return $ ExternalHyperLink "" runs elemToParPart ns element | isElem ns "w" "hyperlink" element - , Just anchor <- findAttr (elemName ns "w" "anchor") element = do + , Just anchor <- findAttrByName ns "w" "anchor" element = do runs <- mapD (elemToRun ns) (elChildren element) return $ InternalHyperLink anchor runs elemToParPart ns element | isElem ns "w" "commentRangeStart" element - , Just cmtId <- findAttr (elemName ns "w" "id") element = do + , Just cmtId <- findAttrByName ns "w" "id" element = do (Comments _ commentMap) <- asks envComments case M.lookup cmtId commentMap of Just cmtElem -> elemToCommentStart ns cmtElem Nothing -> throwError WrongElem elemToParPart ns element | isElem ns "w" "commentRangeEnd" element - , Just cmtId <- findAttr (elemName ns "w" "id") element = + , Just cmtId <- findAttrByName ns "w" "id" element = return $ CommentEnd cmtId elemToParPart ns element | isElem ns "m" "oMath" element = @@ -754,9 +754,9 @@ elemToParPart _ _ = throwError WrongElem elemToCommentStart :: NameSpaces -> Element -> D ParPart elemToCommentStart ns element | isElem ns "w" "comment" element - , Just cmtId <- findAttr (elemName ns "w" "id") element - , Just cmtAuthor <- findAttr (elemName ns "w" "author") element - , Just cmtDate <- findAttr (elemName ns "w" "date") element = do + , Just cmtId <- findAttrByName ns "w" "id" element + , Just cmtAuthor <- findAttrByName ns "w" "author" element + , Just cmtDate <- findAttrByName ns "w" "date" element = do bps <- mapD (elemToBodyPart ns) (elChildren element) return $ CommentStart cmtId cmtAuthor cmtDate bps elemToCommentStart _ _ = throwError WrongElem @@ -799,7 +799,7 @@ childElemToRun ns element = return InlineChart childElemToRun ns element | isElem ns "w" "footnoteReference" element - , Just fnId <- findAttr (elemName ns "w" "id") element = do + , Just fnId <- findAttrByName ns "w" "id" element = do notes <- asks envNotes case lookupFootnote fnId notes of Just e -> do bps <- local (\r -> r {envLocation=InFootnote}) $ mapD (elemToBodyPart ns) (elChildren e) @@ -807,7 +807,7 @@ childElemToRun ns element Nothing -> return $ Footnote [] childElemToRun ns element | isElem ns "w" "endnoteReference" element - , Just enId <- findAttr (elemName ns "w" "id") element = do + , Just enId <- findAttrByName ns "w" "id" element = do notes <- asks envNotes case lookupEndnote enId notes of Just e -> do bps <- local (\r -> r {envLocation=InEndnote}) $ mapD (elemToBodyPart ns) (elChildren e) @@ -818,8 +818,8 @@ childElemToRun _ _ = throwError WrongElem elemToRun :: NameSpaces -> Element -> D Run elemToRun ns element | isElem ns "w" "r" element - , Just altCont <- findChild (elemName ns "mc" "AlternateContent") element = - do let choices = findChildren (elemName ns "mc" "Choice") altCont + , Just altCont <- findChildByName ns "mc" "AlternateContent" element = + do let choices = findChildrenByName ns "mc" "Choice" altCont choiceChildren = map head $ filter (not . null) $ map elChildren choices outputs <- mapD (childElemToRun ns) choiceChildren case outputs of @@ -827,15 +827,15 @@ elemToRun ns element [] -> throwError WrongElem elemToRun ns element | isElem ns "w" "r" element - , Just drawingElem <- findChild (elemName ns "w" "drawing") element = + , Just drawingElem <- findChildByName ns "w" "drawing" element = childElemToRun ns drawingElem elemToRun ns element | isElem ns "w" "r" element - , Just ref <- findChild (elemName ns "w" "footnoteReference") element = + , Just ref <- findChildByName ns "w" "footnoteReference" element = childElemToRun ns ref elemToRun ns element | isElem ns "w" "r" element - , Just ref <- findChild (elemName ns "w" "endnoteReference") element = + , Just ref <- findChildByName ns "w" "endnoteReference" element = childElemToRun ns ref elemToRun ns element | isElem ns "w" "r" element = do @@ -861,20 +861,20 @@ getParStyleField _ _ _ = Nothing elemToParagraphStyle :: NameSpaces -> Element -> ParStyleMap -> ParagraphStyle elemToParagraphStyle ns element sty - | Just pPr <- findChild (elemName ns "w" "pPr") element = + | Just pPr <- findChildByName ns "w" "pPr" element = let style = mapMaybe - (findAttr (elemName ns "w" "val")) - (findChildren (elemName ns "w" "pStyle") pPr) + (findAttrByName ns "w" "val") + (findChildrenByName ns "w" "pStyle" pPr) in ParagraphStyle {pStyle = style , indentation = - findChild (elemName ns "w" "ind") pPr >>= + findChildByName ns "w" "ind" pPr >>= elemToParIndentation ns , dropCap = case - findChild (elemName ns "w" "framePr") pPr >>= - findAttr (elemName ns "w" "dropCap") + findChildByName ns "w" "framePr" pPr >>= + findAttrByName ns "w" "dropCap" of Just "none" -> False Just _ -> True @@ -888,7 +888,7 @@ elemToParagraphStyle _ _ _ = defaultParagraphStyle checkOnOff :: NameSpaces -> Element -> QName -> Maybe Bool checkOnOff ns rPr tag | Just t <- findChild tag rPr - , Just val <- findAttr (elemName ns "w" "val") t = + , Just val <- findAttrByName ns "w" "val" t = Just $ case val of "true" -> True "false" -> False @@ -902,11 +902,11 @@ checkOnOff _ _ _ = Nothing elemToRunStyleD :: NameSpaces -> Element -> D RunStyle elemToRunStyleD ns element - | Just rPr <- findChild (elemName ns "w" "rPr") element = do + | Just rPr <- findChildByName ns "w" "rPr" element = do charStyles <- asks envCharStyles let parentSty = case - findChild (elemName ns "w" "rStyle") rPr >>= - findAttr (elemName ns "w" "val") + findChildByName ns "w" "rStyle" rPr >>= + findAttrByName ns "w" "val" of Just styName | Just style <- M.lookup styName charStyles -> Just (styName, style) @@ -916,7 +916,7 @@ elemToRunStyleD _ _ = return defaultRunStyle elemToRunStyle :: NameSpaces -> Element -> Maybe CharStyle -> RunStyle elemToRunStyle ns element parentStyle - | Just rPr <- findChild (elemName ns "w" "rPr") element = + | Just rPr <- findChildByName ns "w" "rPr" element = RunStyle { isBold = checkOnOff ns rPr (elemName ns "w" "b") @@ -924,15 +924,15 @@ elemToRunStyle ns element parentStyle , isSmallCaps = checkOnOff ns rPr (elemName ns "w" "smallCaps") , isStrike = checkOnOff ns rPr (elemName ns "w" "strike") , rVertAlign = - findChild (elemName ns "w" "vertAlign") rPr >>= - findAttr (elemName ns "w" "val") >>= + findChildByName ns "w" "vertAlign" rPr >>= + findAttrByName ns "w" "val" >>= \v -> Just $ case v of "superscript" -> SupScrpt "subscript" -> SubScrpt _ -> BaseLn , rUnderline = - findChild (elemName ns "w" "u") rPr >>= - findAttr (elemName ns "w" "val") + findChildByName ns "w" "u" rPr >>= + findAttrByName ns "w" "val" , rStyle = parentStyle } elemToRunStyle _ _ _ = defaultRunStyle @@ -942,12 +942,12 @@ isNumericNotNull str = (str /= []) && (all isDigit str) getHeaderLevel :: NameSpaces -> Element -> Maybe (String,Int) getHeaderLevel ns element - | Just styleId <- findAttr (elemName ns "w" "styleId") element + | Just styleId <- findAttrByName ns "w" "styleId" element , Just index <- stripPrefix "Heading" styleId , isNumericNotNull index = Just (styleId, read index) - | Just styleId <- findAttr (elemName ns "w" "styleId") element - , Just index <- findChild (elemName ns "w" "name") element >>= - findAttr (elemName ns "w" "val") >>= + | Just styleId <- findAttrByName ns "w" "styleId" element + , Just index <- findChildByName ns "w" "name" element >>= + findAttrByName ns "w" "val" >>= stripPrefix "heading " , isNumericNotNull index = Just (styleId, read index) getHeaderLevel _ _ = Nothing @@ -960,23 +960,23 @@ blockQuoteStyleNames = ["Quote", "Block Text"] getBlockQuote :: NameSpaces -> Element -> Maybe Bool getBlockQuote ns element - | Just styleId <- findAttr (elemName ns "w" "styleId") element + | Just styleId <- findAttrByName ns "w" "styleId" element , styleId `elem` blockQuoteStyleIds = Just True - | Just styleName <- findChild (elemName ns "w" "name") element >>= - findAttr (elemName ns "w" "val") + | Just styleName <- findChildByName ns "w" "name" element >>= + findAttrByName ns "w" "val" , styleName `elem` blockQuoteStyleNames = Just True getBlockQuote _ _ = Nothing getNumInfo :: NameSpaces -> Element -> Maybe (String, String) getNumInfo ns element = do - let numPr = findChild (elemName ns "w" "pPr") element >>= - findChild (elemName ns "w" "numPr") + let numPr = findChildByName ns "w" "pPr" element >>= + findChildByName ns "w" "numPr" lvl = fromMaybe "0" (numPr >>= - findChild (elemName ns "w" "ilvl") >>= - findAttr (elemName ns "w" "val")) + findChildByName ns "w" "ilvl" >>= + findAttrByName ns "w" "val") numId <- numPr >>= - findChild (elemName ns "w" "numId") >>= - findAttr (elemName ns "w" "val") + findChildByName ns "w" "numId" >>= + findAttrByName ns "w" "val" return (numId, lvl) @@ -1020,8 +1020,8 @@ getSymChar ns element let [(char, _)] = readLitChar ("\\x" ++ s) in TextRun . maybe "" (:[]) $ getUnicode font char where - getCodepoint = findAttr (elemName ns "w" "char") element - getFont = stringToFont =<< findAttr (elemName ns "w" "font") element + getCodepoint = findAttrByName ns "w" "char" element + getFont = stringToFont =<< findAttrByName ns "w" "font" element lowerFromPrivate ('F':xs) = '0':xs lowerFromPrivate xs = xs getSymChar _ _ = TextRun "" diff --git a/src/Text/Pandoc/Readers/Docx/Util.hs b/src/Text/Pandoc/Readers/Docx/Util.hs index 33d69ccf3..6646e5b7f 100644 --- a/src/Text/Pandoc/Readers/Docx/Util.hs +++ b/src/Text/Pandoc/Readers/Docx/Util.hs @@ -3,6 +3,9 @@ module Text.Pandoc.Readers.Docx.Util ( , elemName , isElem , elemToNameSpaces + , findChildByName + , findChildrenByName + , findAttrByName ) where import Text.XML.Light @@ -23,5 +26,22 @@ elemName ns prefix name = isElem :: NameSpaces -> String -> String -> Element -> Bool isElem ns prefix name element = - qName (elName element) == name && - qURI (elName element) == lookup prefix ns + let ns' = ns ++ elemToNameSpaces element + in qName (elName element) == name && + qURI (elName element) == lookup prefix ns' + +findChildByName :: NameSpaces -> String -> String -> Element -> Maybe Element +findChildByName ns pref name el = + let ns' = ns ++ elemToNameSpaces el + in findChild (elemName ns' pref name) el + +findChildrenByName :: NameSpaces -> String -> String -> Element -> [Element] +findChildrenByName ns pref name el = + let ns' = ns ++ elemToNameSpaces el + in findChildren (elemName ns' pref name) el + +findAttrByName :: NameSpaces -> String -> String -> Element -> Maybe String +findAttrByName ns pref name el = + let ns' = ns ++ elemToNameSpaces el + in findAttr (elemName ns' pref name) el + -- cgit v1.2.3 From 8418c1a7d7e5312dfddbc011adb257552b2a864b Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 4 Feb 2017 12:52:08 +0100 Subject: Implemented +/-smart in rst writer. Moved unsmartify to Writers.Shared. --- src/Text/Pandoc/Writers/Markdown.hs | 12 ------------ src/Text/Pandoc/Writers/RST.hs | 17 ++++++++++++++--- src/Text/Pandoc/Writers/Shared.hs | 15 +++++++++++++++ 3 files changed, 29 insertions(+), 15 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index e965528cc..7826c4bdd 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -1136,15 +1136,3 @@ 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 _ [] = [] - diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index ee3ecd9cd..4e0fe1011 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -432,14 +432,25 @@ inlineToRST (Subscript lst) = do inlineToRST (SmallCaps lst) = inlineListToRST lst inlineToRST (Quoted SingleQuote lst) = do contents <- inlineListToRST lst - return $ "‘" <> contents <> "’" + opts <- gets stOptions + if isEnabled Ext_smart opts + then return $ "'" <> contents <> "'" + else return $ "‘" <> contents <> "’" inlineToRST (Quoted DoubleQuote lst) = do contents <- inlineListToRST lst - return $ "“" <> contents <> "”" + opts <- gets stOptions + if isEnabled Ext_smart opts + then return $ "\"" <> contents <> "\"" + else return $ "“" <> contents <> "”" inlineToRST (Cite _ lst) = inlineListToRST lst inlineToRST (Code _ str) = return $ "``" <> text str <> "``" -inlineToRST (Str str) = return $ text $ escapeString str +inlineToRST (Str str) = do + opts <- gets stOptions + let str' = if isEnabled Ext_smart opts + then unsmartify opts str + else str + return $ text $ escapeString str' inlineToRST (Math t str) = do modify $ \st -> st{ stHasMath = True } return $ if t == InlineMath diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index 845d22077..3d50d3312 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -35,10 +35,12 @@ module Text.Pandoc.Writers.Shared ( , defField , tagWithAttrs , fixDisplayMath + , unsmartify ) where import Text.Pandoc.Definition import Text.Pandoc.Pretty +import Text.Pandoc.Options import Text.Pandoc.XML (escapeStringForXML) import Control.Monad (liftM) import Text.Pandoc.Options (WriterOptions(..)) @@ -167,3 +169,16 @@ fixDisplayMath (Para lst) groupBy (\x y -> (isDisplayMath x && isDisplayMath y) || not (isDisplayMath x || isDisplayMath y)) lst fixDisplayMath 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 cb1b0bcba7963a3d5becf57af0dcc72e82c82aed Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 4 Feb 2017 21:06:36 +0100 Subject: Expose setVerbosity in Text.Pandoc --- src/Text/Pandoc.hs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index 3b7c8f94d..920bc9655 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -63,6 +63,7 @@ module Text.Pandoc , runIO , runPure , runIOorExplode + , setVerbosity -- * Error handling , module Text.Pandoc.Error -- * Lists of readers and writers -- cgit v1.2.3 From e0abe18bb92b4d57cf0364486010de9acd8b8d71 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 4 Feb 2017 21:06:42 +0100 Subject: Markdown writer: Better escaping when +smart. --- src/Text/Pandoc/Writers/Markdown.hs | 38 ++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 7826c4bdd..8327ea9bc 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -267,23 +267,27 @@ noteToMarkdown opts num blocks = do -- | Escape special characters for Markdown. escapeString :: WriterOptions -> String -> String -escapeString opts = escapeStringUsing markdownEscapes - where markdownEscapes = ('<', "<") : ('>', ">") : - backslashEscapes specialChars - specialChars = - (if isEnabled Ext_superscript opts - then ('^':) - else id) . - (if isEnabled Ext_subscript opts - then ('~':) - else id) . - (if isEnabled Ext_tex_math_dollars opts - then ('$':) - else id) $ - "\\`*_[]#" ++ - if isEnabled Ext_smart opts - then "\"'" - else "" +escapeString _ [] = [] +escapeString opts (c:cs) = + case c of + '<' -> "<" ++ escapeString opts cs + '>' -> ">" ++ escapeString opts cs + _ | c `elem` ['\\','`','*','_','[',']','#'] -> + '\\':c:escapeString opts cs + '^' | isEnabled Ext_superscript opts -> '\\':'^':escapeString opts cs + '~' | isEnabled Ext_subscript opts -> '\\':'~':escapeString opts cs + '$' | isEnabled Ext_tex_math_dollars opts -> '\\':'$':escapeString opts cs + '\'' | isEnabled Ext_smart opts -> '\\':'\'':escapeString opts cs + '"' | isEnabled Ext_smart opts -> '\\':'"':escapeString opts cs + '-' | isEnabled Ext_smart opts -> + case cs of + '-':_ -> '\\':'-':escapeString opts cs + _ -> '-':escapeString opts cs + '.' | isEnabled Ext_smart opts -> + case cs of + '.':'.':rest -> '\\':'.':'.':'.':escapeString opts rest + _ -> '.':escapeString opts cs + _ -> c : escapeString opts cs -- | Construct table of contents from list of header blocks. tableOfContents :: PandocMonad m => WriterOptions -> [Block] -> m Doc -- cgit v1.2.3 From 623d860be6578c51f7937933ca2ce7ec2ddc91b5 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Sat, 4 Feb 2017 21:07:52 +0100 Subject: Org writer: reduce to two spaces after bullets The org writer was inserting two spaces after list bullets. Emacs Org-mode defaults to a single space, so behavior is changed to reflect this. Closes: #3417 --- src/Text/Pandoc/Writers/Org.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index ae1c92a97..fd4c16c64 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -255,7 +255,7 @@ blockToOrg (DefinitionList items) = do bulletListItemToOrg :: [Block] -> State WriterState Doc bulletListItemToOrg items = do contents <- blockListToOrg items - return $ hang 3 "- " (contents <> cr) + return $ hang 2 "- " (contents <> cr) -- | Convert ordered list item (a list of blocks) to Org. orderedListItemToOrg :: String -- ^ marker for list item @@ -270,7 +270,7 @@ definitionListItemToOrg :: ([Inline], [[Block]]) -> State WriterState Doc definitionListItemToOrg (label, defs) = do label' <- inlineListToOrg label contents <- liftM vcat $ mapM blockListToOrg defs - return $ hang 3 "- " $ label' <> " :: " <> (contents <> cr) + return $ hang 2 "- " $ label' <> " :: " <> (contents <> cr) -- | Convert list of key/value pairs to Org :PROPERTIES: drawer. propertiesDrawer :: Attr -> Doc -- cgit v1.2.3 From 7404c83fb3338e791b8ff0dc7a21346d67f3e322 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 4 Feb 2017 22:09:06 +0100 Subject: Improved escaping in RST writer with smart option. --- src/Text/Pandoc/Writers/RST.hs | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index 4e0fe1011..6093c668b 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -161,8 +161,22 @@ pictToRST (label, (attr, src, _, mbtarget)) = do Just t -> " :target: " <> text t -- | Escape special characters for RST. -escapeString :: String -> String -escapeString = escapeStringUsing (backslashEscapes "`\\|*_") +escapeString :: WriterOptions -> String -> String +escapeString _ [] = [] +escapeString opts (c:cs) = + case c of + _ | c `elem` ['\\','`','*','_','|'] -> '\\':c:escapeString opts cs + '\'' | isEnabled Ext_smart opts -> '\\':'\'':escapeString opts cs + '"' | isEnabled Ext_smart opts -> '\\':'"':escapeString opts cs + '-' | isEnabled Ext_smart opts -> + case cs of + '-':_ -> '\\':'-':escapeString opts cs + _ -> '-':escapeString opts cs + '.' | isEnabled Ext_smart opts -> + case cs of + '.':'.':rest -> '\\':'.':'.':'.':escapeString opts rest + _ -> '.':escapeString opts cs + _ -> c : escapeString opts cs titleToRST :: [Inline] -> [Inline] -> State WriterState Doc titleToRST [] _ = return empty @@ -447,10 +461,10 @@ inlineToRST (Cite _ lst) = inlineToRST (Code _ str) = return $ "``" <> text str <> "``" inlineToRST (Str str) = do opts <- gets stOptions - let str' = if isEnabled Ext_smart opts - then unsmartify opts str - else str - return $ text $ escapeString str' + return $ text $ + (if isEnabled Ext_smart opts + then unsmartify opts + else id) $ escapeString opts str inlineToRST (Math t str) = do modify $ \st -> st{ stHasMath = True } return $ if t == InlineMath -- cgit v1.2.3 From 63b568f445513a570121f93ef7ca0ed1cfd924d6 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 4 Feb 2017 22:51:51 +0100 Subject: Changed writerEpubMetadata to a Maybe String. API change. --- src/Text/Pandoc/Options.hs | 4 ++-- src/Text/Pandoc/Writers/EPUB.hs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index c00981d5d..38c083dfd 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -173,7 +173,7 @@ 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 - , writerEpubMetadata :: String -- ^ Metadata to include in EPUB + , writerEpubMetadata :: Maybe String -- ^ Metadata to include in EPUB , writerEpubStylesheet :: Maybe String -- ^ EPUB stylesheet specified at command line , writerEpubFonts :: [FilePath] -- ^ Paths to fonts to embed , writerEpubChapterLevel :: Int -- ^ Header level for chapters (separate files) @@ -209,7 +209,7 @@ instance Default WriterOptions where , writerListings = False , writerHighlightStyle = Just pygments , writerSetextHeaders = True - , writerEpubMetadata = "" + , writerEpubMetadata = Nothing , writerEpubStylesheet = Nothing , writerEpubFonts = [] , writerEpubChapterLevel = 1 diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index d21f7bea1..7e9a20a0c 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -150,7 +150,7 @@ removeNote x = x getEPUBMetadata :: PandocMonad m => WriterOptions -> Meta -> E m EPUBMetadata getEPUBMetadata opts meta = do let md = metadataFromMeta opts meta - let elts = onlyElems $ parseXML $ writerEpubMetadata opts + let elts = maybe [] (onlyElems . parseXML) $ writerEpubMetadata opts let md' = foldr addMetadataFromXML md elts let addIdentifier m = if null (epubIdentifier m) -- cgit v1.2.3 From 942189056d76cd8dedfe11436fb9a7b6a7b8724c Mon Sep 17 00:00:00 2001 From: Thenaesh Elango Date: Sun, 5 Feb 2017 18:28:39 +0800 Subject: Allow user to specify User-Agent (#3421) This commit enables users to specify the User-Agent header used when pandoc requests a document from a URL. This is done by setting an environment variable. For instance, one can do: USER_AGENT="..." ./pandoc -f html -t markdown http://example.com Signed-off-by: Thenaesh Elango --- src/Text/Pandoc/Shared.hs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 22847931f..86e9a5525 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -146,13 +146,13 @@ import Paths_pandoc (getDataFileName) #endif #ifdef HTTP_CLIENT import Network.HTTP.Client (httpLbs, responseBody, responseHeaders, - Request(port,host)) + Request(port,host,requestHeaders)) import Network.HTTP.Client (parseRequest) import Network.HTTP.Client (newManager) import Network.HTTP.Client.Internal (addProxy) import Network.HTTP.Client.TLS (tlsManagerSettings) import System.Environment (getEnv) -import Network.HTTP.Types.Header ( hContentType) +import Network.HTTP.Types.Header ( hContentType, hUserAgent) import Network (withSocketsDo) #else import Network.URI (parseURI) @@ -742,13 +742,21 @@ openURL u | otherwise = withSocketsDo $ E.try $ do let parseReq = parseRequest (proxy :: Either E.SomeException String) <- E.try $ getEnv "http_proxy" + (useragent :: Either E.SomeException String) <- E.try $ getEnv "USER_AGENT" req <- parseReq u req' <- case proxy of Left _ -> return req Right pr -> (parseReq pr >>= \r -> return $ addProxy (host r) (port r) req) `mplus` return req - resp <- newManager tlsManagerSettings >>= httpLbs req' + req'' <- case useragent of + Left _ -> return req' + Right ua -> do + let headers = requestHeaders req' + let useragentheader = (hUserAgent, B8.pack ua) + let headers' = useragentheader:headers + return $ req' {requestHeaders = headers'} + resp <- newManager tlsManagerSettings >>= httpLbs req'' return (BS.concat $ toChunks $ responseBody resp, UTF8.toString `fmap` lookup hContentType (responseHeaders resp)) #else -- cgit v1.2.3 From 5f2f2efe3225c64f7c822dab035286b2913b5c5c Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 5 Feb 2017 11:54:05 +0100 Subject: Removed redundant import. --- src/Text/Pandoc/Writers/Shared.hs | 1 - 1 file changed, 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index 3d50d3312..89a826269 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -43,7 +43,6 @@ import Text.Pandoc.Pretty import Text.Pandoc.Options import Text.Pandoc.XML (escapeStringForXML) import Control.Monad (liftM) -import Text.Pandoc.Options (WriterOptions(..)) import qualified Data.HashMap.Strict as H import qualified Data.Map as M import qualified Data.Text as T -- cgit v1.2.3 From 0bbea0cc7650af61870b310ebbf6fb8a9fec09a7 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 5 Feb 2017 21:58:45 +0100 Subject: Split pandoc.hs into a module, Text.Pandoc.App, and a small program. The App module provides a function that does a pandoc conversion, based on option settings. The program (pandoc.hs) now does nothing more than parse options and pass them to this function, which can easily be used by other applications (e.g. a GUI wrapper). The Opt structure has been further simplified. API changes: * New exposed module Text.Pandoc.App * Text.Pandoc.Highlighting has been exposed. * highlightingStyles has been moved to Text.Pandoc.Highlighting. --- src/Text/Pandoc/App.hs | 720 ++++++++++++++++++++++++++++++++++++++++ src/Text/Pandoc/Highlighting.hs | 14 +- 2 files changed, 733 insertions(+), 1 deletion(-) create mode 100644 src/Text/Pandoc/App.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs new file mode 100644 index 000000000..e51a45395 --- /dev/null +++ b/src/Text/Pandoc/App.hs @@ -0,0 +1,720 @@ +{-# LANGUAGE CPP, TupleSections, ScopedTypeVariables, PatternGuards #-} +{- +Copyright (C) 2006-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.App + Copyright : Copyright (C) 2006-2016 John MacFarlane + License : GNU GPL, version 2 or above + + Maintainer : John MacFarlane + Stability : alpha + Portability : portable + +Does a pandoc conversion based on command-line options. +-} +module Text.Pandoc.App ( + convertWithOpts + , Opt(..) + , defaultOpts + ) where +import Text.Pandoc +import Text.Pandoc.Builder (setMeta) +import Text.Pandoc.PDF (makePDF) +import Text.Pandoc.Walk (walk) +import Text.Pandoc.Shared ( tabFilter, readDataFileUTF8, + headerShift, err, openURL ) +import Text.Pandoc.MediaBag ( mediaDirectory, extractMediaBag, MediaBag ) +import Text.Pandoc.XML ( toEntities ) +import Text.Pandoc.Highlighting (highlightingStyles) +import Text.Pandoc.SelfContained ( makeSelfContained ) +import Text.Pandoc.Process (pipeProcess) +import Skylighting ( Style ) +import System.Environment ( getEnvironment ) +import System.Exit ( ExitCode (..), exitSuccess ) +import System.FilePath +import Data.Char ( toLower ) +import Data.List ( intercalate, isPrefixOf, isSuffixOf ) +import System.Directory ( getAppUserDataDirectory, findExecutable, + doesFileExist, Permissions(..), getPermissions ) +import System.IO ( stdout, stderr ) +import System.IO.Error ( isDoesNotExistError ) +import qualified Control.Exception as E +import Control.Exception.Extensible ( throwIO ) +import qualified Text.Pandoc.UTF8 as UTF8 +import Control.Monad (when, unless, (>=>)) +import Data.Maybe (fromMaybe, isNothing, isJust) +import Data.Foldable (foldrM) +import Network.URI (parseURI, isURI, URI(..)) +import qualified Data.ByteString.Lazy as B +import Data.Aeson (eitherDecode', encode) +import Data.Yaml (decode) +import qualified Data.Yaml as Yaml +import qualified Data.Text as T +#ifndef _WINDOWS +import System.Posix.Terminal (queryTerminal) +import System.Posix.IO (stdOutput) +#endif +import Control.Monad.Trans +import Text.Pandoc.Class (withMediaBag, PandocIO, getLog, setVerbosity) + +convertWithOpts :: Opt -> [FilePath] -> IO () +convertWithOpts opts args = do + let outputFile = optOutputFile opts + let filters = optFilters opts + let verbosity = optVerbosity opts + + when (optDumpArgs opts) $ + do UTF8.hPutStrLn stdout outputFile + mapM_ (UTF8.hPutStrLn stdout) args + exitSuccess + + epubStylesheet <- case optEpubStylesheet opts of + Nothing -> return Nothing + Just fp -> Just <$> UTF8.readFile fp + + epubMetadata <- case optEpubMetadata opts of + Nothing -> return Nothing + Just fp -> Just <$> UTF8.readFile fp + + let csscdn = "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.css" + let mathMethod = + case (optKaTeXJS opts, optKaTeXStylesheet opts) of + (Nothing, _) -> optHTMLMathMethod opts + (Just js, ss) -> KaTeX js (fromMaybe csscdn ss) + + + -- --bibliography implies -F pandoc-citeproc for backwards compatibility: + let needsCiteproc = isJust (lookup "bibliography" (optMetadata opts)) && + optCiteMethod opts `notElem` [Natbib, Biblatex] && + "pandoc-citeproc" `notElem` map takeBaseName filters + let filters' = if needsCiteproc then "pandoc-citeproc" : filters + else filters + + let sources = case args of + [] -> ["-"] + xs | optIgnoreArgs opts -> ["-"] + | otherwise -> xs + + datadir <- case optDataDir opts of + Nothing -> E.catch + (Just <$> getAppUserDataDirectory "pandoc") + (\e -> let _ = (e :: E.SomeException) + in return Nothing) + Just _ -> return $ optDataDir opts + + -- assign reader and writer based on options and filenames + let readerName = case optReader opts of + Nothing -> defaultReaderName + (if any isURI sources + then "html" + else "markdown") sources + Just x -> map toLower x + + let writerName = case optWriter opts of + Nothing -> defaultWriterName outputFile + Just x -> map toLower x + let format = takeWhile (`notElem` ['+','-']) + $ takeFileName writerName -- in case path to lua script + + let pdfOutput = map toLower (takeExtension outputFile) == ".pdf" + + let laTeXOutput = format `elem` ["latex", "beamer"] + let conTeXtOutput = format == "context" + let html5Output = format == "html5" || format == "html" + + -- disabling the custom writer for now + writer <- if ".lua" `isSuffixOf` format + -- note: use non-lowercased version writerName + then error "custom writers disabled for now" + else case getWriter writerName of + Left e -> err 9 $ + if format == "pdf" + then e ++ + "\nTo create a pdf with pandoc, use " ++ + "the latex or beamer writer and specify\n" ++ + "an output file with .pdf extension " ++ + "(pandoc -t latex -o filename.pdf)." + else e + Right w -> return (w :: Writer PandocIO) + + -- TODO: we have to get the input and the output into the state for + -- the sake of the text2tags reader. + reader <- case getReader readerName of + Right r -> return (r :: Reader PandocIO) + Left e -> err 7 e' + where e' = case readerName of + "pdf" -> e ++ + "\nPandoc can convert to PDF, but not from PDF." + "doc" -> e ++ + "\nPandoc can convert from DOCX, but not from DOC.\nTry using Word to save your DOC file as DOCX, and convert that with pandoc." + _ -> e + + let standalone = optStandalone opts || not (isTextFormat format) || pdfOutput + + templ <- case optTemplate opts of + _ | not standalone -> return Nothing + Nothing -> do + deftemp <- getDefaultTemplate datadir format + case deftemp of + Left e -> throwIO e + Right t -> return (Just t) + Just tp -> do + -- strip off extensions + let tp' = case takeExtension tp of + "" -> tp <.> format + _ -> tp + Just <$> E.catch (UTF8.readFile tp') + (\e -> if isDoesNotExistError e + then E.catch + (readDataFileUTF8 datadir + ("templates" tp')) + (\e' -> let _ = (e' :: E.SomeException) + in throwIO e') + else throwIO e) + + let addStringAsVariable varname s vars = return $ (varname, s) : vars + + let addContentsAsVariable varname fp vars = do + s <- UTF8.readFile fp + return $ (varname, s) : vars + + -- note: this reverses the list constructed in option parsing, + -- which in turn was reversed from the command-line order, + -- so we end up with the correct order in the variable list: + let withList _ [] vars = return vars + withList f (x:xs) vars = f x vars >>= withList f xs + + variables <- return (optVariables opts) + >>= + withList (addContentsAsVariable "include-before") + (optIncludeBeforeBody opts) + >>= + withList (addContentsAsVariable "include-after") + (optIncludeAfterBody opts) + >>= + withList (addContentsAsVariable "header-includes") + (optIncludeInHeader opts) + >>= + withList (addStringAsVariable "css") (optCss opts) + >>= + maybe return (addStringAsVariable "title-prefix") (optTitlePrefix opts) + >>= + maybe return (addStringAsVariable "epub-cover-image") + (optEpubCoverImage opts) + >>= + (\vars -> case mathMethod of + LaTeXMathML Nothing -> do + s <- readDataFileUTF8 datadir "LaTeXMathML.js" + return $ ("mathml-script", s) : vars + _ -> return vars) + >>= + (\vars -> if format == "dzslides" + then do + dztempl <- readDataFileUTF8 datadir + ("dzslides" "template.html") + let dzline = "\n" + else RawBlock "markdown" " " mapM (blockToMarkdown opts) (fixBlocks blocks) >>= return . cat - -- insert comment between list and indented code block, or the - -- code block will be treated as a list continuation paragraph - where fixBlocks (b : CodeBlock attr x : rest) - | (not (isEnabled Ext_fenced_code_blocks opts) || attr == nullAttr) - && isListBlock b = b : commentSep : CodeBlock attr x : - fixBlocks rest - fixBlocks (b1@(BulletList _) : b2@(BulletList _) : bs) = - b1 : commentSep : fixBlocks (b2:bs) - fixBlocks (b1@(OrderedList _ _) : b2@(OrderedList _ _) : bs) = - b1 : commentSep : fixBlocks (b2:bs) - fixBlocks (b1@(DefinitionList _) : b2@(DefinitionList _) : bs) = - b1 : commentSep : fixBlocks (b2:bs) - fixBlocks (x : xs) = x : fixBlocks xs - fixBlocks [] = [] - isListBlock (BulletList _) = True - isListBlock (OrderedList _ _) = True - isListBlock (DefinitionList _) = True - isListBlock _ = False - commentSep = if isEnabled Ext_raw_html opts - then RawBlock "html" "\n" - else RawBlock "markdown" " " -- | Get reference for target; if none exists, create unique one and return. -- Prefer label if possible; otherwise, generate a unique key. -- cgit v1.2.3 From bcfb77e2ab832d97b66e0bd06c35a9a13be437da Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 8 Mar 2017 12:47:39 +0100 Subject: Markdown writer: Avoid spurious blanklines at end of document... after tables and list, for example. --- 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 782d0d085..ab986208d 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -214,7 +214,7 @@ pandocToMarkdown opts (Pandoc meta blocks) = do body <- blockListToMarkdown opts blocks' notesAndRefs' <- notesAndRefs opts let render' :: Doc -> String - render' = render colwidth + render' = render colwidth . chomp let main = render' $ body <> notesAndRefs' let context = defField "toc" (render' toc) $ defField "body" main -- cgit v1.2.3 From c91f168fc93f22b8c281fb2933052ff6da63d47b Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Wed, 8 Mar 2017 15:36:48 +0100 Subject: Org reader: disallow tables on list marker lines Fixes: #3499 --- src/Text/Pandoc/Readers/Org/Blocks.hs | 7 ++++--- 1 file changed, 4 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 72d1f31dc..75019f74f 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -721,10 +721,11 @@ data OrgTable = OrgTable table :: PandocMonad m => OrgParser m (F Blocks) table = try $ do - -- don't allow a table inside a list item; org requires that + -- don't allow a table on the first line of a list item; org requires that -- tables start at first non-space character on the line - ctx <- orgStateParserContext <$> getState - guard (ctx == NullState) + let isFirstInListItem st = (orgStateParserContext st == ListItemState) && + (orgStateLastPreCharPos st == Nothing) + guard =<< not . isFirstInListItem <$> getState blockAttrs <- blockAttributes lookAhead tableStart do -- cgit v1.2.3 From fd35661646b87c4960f6c610de06c891c78e9aab Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 8 Mar 2017 19:06:32 +0100 Subject: Remove space at beginning/end of RST code span. Otherwise we get invalid RST. There seems to be no way to escape the space. Closes #3496. --- src/Text/Pandoc/Writers/RST.hs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index f1de2ab0e..2657afa2a 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -459,7 +459,10 @@ inlineToRST (Quoted DoubleQuote lst) = do else return $ "“" <> contents <> "”" inlineToRST (Cite _ lst) = inlineListToRST lst -inlineToRST (Code _ str) = return $ "``" <> text str <> "``" +inlineToRST (Code _ str) = + -- we trim the string because the delimiters must adjoin a + -- non-space character; see #3496 + return $ "``" <> text (trim str) <> "``" inlineToRST (Str str) = do opts <- gets stOptions return $ text $ -- cgit v1.2.3 From 239a17a9863db9eea87e142931e6647b0e0064e1 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 9 Mar 2017 09:41:22 +0100 Subject: HTML writer: fallback to basename rather than Untitled. --- src/Text/Pandoc/Writers/HTML.hs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index e904b5fb7..ab2713748 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -76,7 +76,7 @@ import qualified Text.Blaze.Html5 as H5 #endif import Control.Monad.Except (throwError) import Data.Aeson (Value) -import System.FilePath (takeExtension) +import System.FilePath (takeExtension, takeBaseName) import Text.Blaze.Html.Renderer.String (renderHtml) import qualified Text.Blaze.XHtml1.Transitional as H import qualified Text.Blaze.XHtml1.Transitional.Attributes as A @@ -197,9 +197,10 @@ writeHtmlString' st opts d = do case getField "pagetitle" context of Just (s :: String) | not (null s) -> return context _ -> do - report $ NoTitleElement "Untitled" - return $ resetField "pagetitle" ("Untitled" :: String) - context + let fallback = fromMaybe "Untitled" $ takeBaseName <$> + lookup "sourcefile" (writerVariables opts) + report $ NoTitleElement fallback + return $ resetField "pagetitle" fallback context return $ renderTemplate' tpl $ defField "body" (renderHtml body) context' -- cgit v1.2.3 From 11e57c4d18f3adc1742ed6b45642a2ac17ce68d8 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 9 Mar 2017 10:20:30 +0100 Subject: Logging: Added NoLangSpecified, use toConstr to avoid boilerplate. --- src/Text/Pandoc/Logging.hs | 72 +++++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 42 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs index e7d81d292..ba836b91a 100644 --- a/src/Text/Pandoc/Logging.hs +++ b/src/Text/Pandoc/Logging.hs @@ -43,7 +43,7 @@ import Data.Aeson import Data.Aeson.Encode.Pretty (Config (..), defConfig, encodePretty', keyOrder) import qualified Data.ByteString.Lazy as BL -import Data.Data (Data) +import Data.Data (Data, toConstr) import Data.Generics (Typeable) import qualified Data.Text as Text import GHC.Generics (Generic) @@ -78,102 +78,86 @@ data LogMessage = | CouldNotParseCSS String | Fetching String | NoTitleElement String + | NoLangSpecified deriving (Show, Eq, Data, Ord, Typeable, Generic) instance ToJSON LogMessage where - toJSON x = object $ "verbosity" .= toJSON (messageVerbosity x) : + toJSON x = object $ + "verbosity" .= toJSON (messageVerbosity x) : + "type" .= toJSON (show $ toConstr x) : case x of SkippedContent s pos -> - ["type" .= String "SkippedContent", - "contents" .= Text.pack s, + ["contents" .= Text.pack s, "source" .= Text.pack (sourceName pos), "line" .= sourceLine pos, "column" .= sourceColumn pos] CouldNotParseYamlMetadata s pos -> - ["type" .= String "YamlSectionNotAnObject", - "message" .= Text.pack s, + ["message" .= Text.pack s, "source" .= Text.pack (sourceName pos), "line" .= toJSON (sourceLine pos), "column" .= toJSON (sourceColumn pos)] DuplicateLinkReference s pos -> - ["type" .= String "DuplicateLinkReference", - "contents" .= Text.pack s, + ["contents" .= Text.pack s, "source" .= Text.pack (sourceName pos), "line" .= toJSON (sourceLine pos), "column" .= toJSON (sourceColumn pos)] DuplicateNoteReference s pos -> - ["type" .= String "DuplicateNoteReference", - "contents" .= Text.pack s, + ["contents" .= Text.pack s, "source" .= Text.pack (sourceName pos), "line" .= toJSON (sourceLine pos), "column" .= toJSON (sourceColumn pos)] ReferenceNotFound s pos -> - ["type" .= String "ReferenceNotFound", - "contents" .= Text.pack s, + ["contents" .= Text.pack s, "source" .= Text.pack (sourceName pos), "line" .= toJSON (sourceLine pos), "column" .= toJSON (sourceColumn pos)] CircularReference s pos -> - ["type" .= String "CircularReference", - "contents" .= Text.pack s, + ["contents" .= Text.pack s, "source" .= Text.pack (sourceName pos), "line" .= toJSON (sourceLine pos), "column" .= toJSON (sourceColumn pos)] ParsingUnescaped s pos -> - ["type" .= String "ParsingUnescaped", - "contents" .= Text.pack s, + ["contents" .= Text.pack s, "source" .= Text.pack (sourceName pos), "line" .= toJSON (sourceLine pos), "column" .= toJSON (sourceColumn pos)] CouldNotLoadIncludeFile fp pos -> - ["type" .= String "CouldNotLoadIncludeFile", - "path" .= Text.pack fp, + ["path" .= Text.pack fp, "source" .= Text.pack (sourceName pos), "line" .= toJSON (sourceLine pos), "column" .= toJSON (sourceColumn pos)] ParsingTrace s pos -> - ["type" .= String "ParsingTrace", - "contents" .= Text.pack s, + ["contents" .= Text.pack s, "source" .= Text.pack (sourceName pos), "line" .= sourceLine pos, "column" .= sourceColumn pos] InlineNotRendered il -> - ["type" .= String "InlineNotRendered", - "contents" .= toJSON il] + ["contents" .= toJSON il] BlockNotRendered bl -> - ["type" .= String "BlockNotRendered", - "contents" .= toJSON bl] + ["contents" .= toJSON bl] DocxParserWarning s -> - ["type" .= String "DocxParserWarning", - "contents" .= Text.pack s] + ["contents" .= Text.pack s] CouldNotFetchResource fp s -> - ["type" .= String "CouldNotFetchResource", - "path" .= Text.pack fp, + ["path" .= Text.pack fp, "message" .= Text.pack s] CouldNotDetermineImageSize fp s -> - ["type" .= String "CouldNotDetermineImageSize", - "path" .= Text.pack fp, + ["path" .= Text.pack fp, "message" .= Text.pack s] CouldNotConvertImage fp s -> - ["type" .= String "CouldNotConvertImage", - "path" .= Text.pack fp, + ["path" .= Text.pack fp, "message" .= Text.pack s] CouldNotDetermineMimeType fp -> - ["type" .= String "CouldNotDetermineMimeType", - "path" .= Text.pack fp] + ["path" .= Text.pack fp] CouldNotConvertTeXMath s msg -> - ["type" .= String "CouldNotConvertTeXMath", - "contents" .= Text.pack s, + ["contents" .= Text.pack s, "message" .= Text.pack msg] CouldNotParseCSS msg -> - ["type" .= String "CouldNotParseCSS", - "message" .= Text.pack msg] + ["message" .= Text.pack msg] Fetching fp -> - ["type" .= String "CouldNotParseCSS", - "path" .= Text.pack fp] + ["path" .= Text.pack fp] NoTitleElement fallback -> - ["type" .= String "NoTitleElement", - "fallback" .= Text.pack fallback] + ["fallback" .= Text.pack fallback] + NoLangSpecified -> [] showPos :: SourcePos -> String showPos pos = sn ++ "line " ++ @@ -238,6 +222,9 @@ showLogMessage msg = "This document format requires a nonempty element.\n" ++ "Please specify either 'title' or 'pagetitle' in the metadata.\n" ++ "Falling back to '" ++ fallback ++ "'" + NoLangSpecified -> + "No value for 'lang' was specified in the metadata.\n" ++ + "It is recommended that lang be specified for this format." messageVerbosity:: LogMessage -> Verbosity messageVerbosity msg = @@ -262,3 +249,4 @@ messageVerbosity msg = CouldNotParseCSS{} -> WARNING Fetching{} -> INFO NoTitleElement{} -> WARNING + NoLangSpecified -> INFO \ No newline at end of file -- cgit v1.2.3 From 1e78aec88e5cd93b96ae0aaec47f7495d95a86e7 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 9 Mar 2017 10:21:11 +0100 Subject: HTML writer: info message if 'lang' is unspecified. Closes #3486. --- src/Text/Pandoc/Writers/HTML.hs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index ab2713748..fdf62dd56 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -46,7 +46,7 @@ module Text.Pandoc.Writers.HTML ( import Control.Monad.State import Data.Char (ord, toLower) import Data.List (intersperse, isPrefixOf) -import Data.Maybe (catMaybes, fromMaybe, isJust) +import Data.Maybe (catMaybes, fromMaybe, isJust, isNothing) import Data.Monoid ((<>)) import Data.String (fromString) import Network.HTTP (urlEncode) @@ -192,6 +192,9 @@ writeHtmlString' st opts d = do case writerTemplate opts of Nothing -> return $ renderHtml body Just tpl -> do + -- warn if empty lang + when (isNothing (getField "lang" context :: Maybe String)) $ + report NoLangSpecified -- check for empty pagetitle context' <- case getField "pagetitle" context of -- cgit v1.2.3 From 1ec6a19223a2466075fd4dadd7428d01475f5d77 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 9 Mar 2017 10:30:57 +0100 Subject: Changed display format for messages. --- src/Text/Pandoc/Class.hs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index c45249257..1afa64c10 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -77,6 +77,7 @@ import Text.Pandoc.Logging import Text.Parsec (ParsecT) import qualified Text.Pandoc.Compat.Time as IO (getCurrentTime) import Text.Pandoc.MIME (MimeType, getMimeType) +import Data.Char (toLower) import Data.Time.Clock.POSIX ( utcTimeToPOSIXSeconds , posixSecondsToUTCTime , POSIXTime ) @@ -106,7 +107,6 @@ import System.IO.Error import System.IO (stderr) import qualified Data.Map as M import Text.Pandoc.Error -import Text.Printf (printf) class (Functor m, Applicative m, Monad m, MonadError PandocError m) => PandocMonad m where @@ -251,17 +251,17 @@ instance PandocMonad PandocIO where getCommonState = PandocIO $ lift get putCommonState x = PandocIO $ lift $ put x logOutput msg = liftIO $ do - UTF8.hPutStr stderr $ printf "%-7s " (show (messageVerbosity msg)) - hangingIndent 2 $ lines $ showLogMessage msg + UTF8.hPutStr stderr $ "[" ++ + (map toLower $ show (messageVerbosity msg)) ++ "] " + alertIndent $ lines $ showLogMessage msg -hangingIndent :: Int -> [String] -> IO () -hangingIndent _level [] = return () -hangingIndent level (l:ls) = do +alertIndent :: [String] -> IO () +alertIndent [] = return () +alertIndent (l:ls) = do UTF8.hPutStrLn stderr l mapM_ go ls - where go l' = do UTF8.hPutStr stderr ind + where go l' = do UTF8.hPutStr stderr "! " UTF8.hPutStrLn stderr l' - ind = replicate level ' ' -- | Specialized version of parseURIReference that disallows -- single-letter schemes. Reason: these are usually windows absolute -- cgit v1.2.3 From a088d67f0d7212c96f5b59c568f0fc61a1106be4 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 9 Mar 2017 21:03:54 +0100 Subject: LaTeX reader: Treat `{{xxx}}` the same as `{xxx}`. Closes #2115. --- src/Text/Pandoc/Readers/LaTeX.hs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 60113173d..48266f894 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -147,17 +147,22 @@ egroup = () <$ char '}' <|> () <$ controlSeq "endgroup" grouped :: PandocMonad m => Monoid a => LP m a -> LP m a -grouped parser = try $ bgroup *> (mconcat <$> manyTill parser egroup) +grouped parser = try $ do + bgroup + -- first we check for an inner 'grouped', because + -- {{a,b}} should be parsed the same as {a,b} + try (grouped parser <* egroup) + <|> (mconcat <$> manyTill parser egroup) braced :: PandocMonad m => LP m String -braced = bgroup *> (concat <$> manyTill - ( many1 (satisfy (\c -> c /= '\\' && c /= '}' && c /= '{')) - <|> try (string "\\}") - <|> try (string "\\{") - <|> try (string "\\\\") - <|> ((\x -> "{" ++ x ++ "}") <$> braced) - <|> count 1 anyChar - ) egroup) +braced = grouped chunk + where chunk = + many1 (satisfy (\c -> c /= '\\' && c /= '}' && c /= '{')) + <|> try (string "\\}") + <|> try (string "\\{") + <|> try (string "\\\\") + <|> ((\x -> "{" ++ x ++ "}") <$> braced) + <|> count 1 anyChar bracketed :: PandocMonad m => Monoid a => LP m a -> LP m a bracketed parser = try $ char '[' *> (mconcat <$> manyTill parser (char ']')) -- cgit v1.2.3 From c46febaaeef1c203f5bbb88d845ad5554622f609 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 10 Mar 2017 09:46:32 +0100 Subject: Expand \newenvironment macros. Closes #987. Depends on still unreleased texmath 0.9.3. --- src/Text/Pandoc/Readers/LaTeX.hs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 48266f894..7018d2ce3 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -971,17 +971,19 @@ rawEnv name = do let parseRaw = extensionEnabled Ext_raw_tex exts rawOptions <- mconcat <$> many rawopt let beginCommand = "\\begin{" ++ name ++ "}" ++ rawOptions - unless parseRaw $ do - pos1 <- getPosition - report $ SkippedContent beginCommand pos1 + pos1 <- getPosition (bs, raw) <- withRaw $ env name blocks - raw' <- applyMacros' raw - if parseRaw - then return $ rawBlock "latex" $ beginCommand ++ raw' - else do - pos2 <- getPosition - report $ SkippedContent ("\\end{" ++ name ++ "}") pos2 - return bs + raw' <- applyMacros' $ beginCommand ++ raw + if raw' /= beginCommand ++ raw + then parseFromString blocks raw' + else if parseRaw + then return $ rawBlock "latex" $ beginCommand ++ raw' + else do + unless parseRaw $ do + report $ SkippedContent beginCommand pos1 + pos2 <- getPosition + report $ SkippedContent ("\\end{" ++ name ++ "}") pos2 + return bs ---- -- cgit v1.2.3 From 21ae5db20cd78d814d375f6e2be48e506e4a24da Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 10 Mar 2017 10:12:51 +0100 Subject: Use pMacroDefinition in macro (for more direct parsing). This is newly exported in texmath 0.9.3. Note that this means that `macro` will now parse one macro at a time, rather than parsing a whole group together. --- src/Text/Pandoc/Parsing.hs | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 46dc22112..b207e79e0 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -186,8 +186,7 @@ import Data.Char ( toLower, toUpper, ord, chr, isAscii, isAlphaNum, import Data.List ( intercalate, transpose, isSuffixOf ) import Text.Pandoc.Shared import qualified Data.Map as M -import Text.TeXMath.Readers.TeX.Macros (applyMacros, Macro, - parseMacroDefinitions) +import Text.TeXMath.Readers.TeX.Macros (applyMacros, Macro, pMacroDefinition) import Text.HTML.TagSoup.Entity ( lookupEntity ) import Text.Pandoc.Asciify (toAsciiChar) import Data.Monoid ((<>)) @@ -1263,21 +1262,17 @@ token pp pos match = tokenPrim pp (\_ t _ -> pos t) match -- Macros -- --- | Parse a \newcommand or \renewcommand macro definition. +-- | Parse a \newcommand or \newenviroment macro definition. macro :: (Stream [Char] m Char, HasMacros st, HasReaderOptions st) => ParserT [Char] st m Blocks macro = do apply <- getOption readerApplyMacros - inp <- getInput - case parseMacroDefinitions inp of - ([], _) -> mzero - (ms, rest) -> do def' <- count (length inp - length rest) anyChar - if apply - then do - updateState $ \st -> - updateMacros (ms ++) st - return mempty - else return $ rawBlock "latex" def' + (m, def') <- withRaw pMacroDefinition + if apply + then do + updateState $ \st -> updateMacros (m:) st + return mempty + else return $ rawBlock "latex" def' -- | Apply current macros to string. applyMacros' :: (HasReaderOptions st, HasMacros st, Stream [Char] m Char) -- cgit v1.2.3 From d037c5019d51b9fc64690f5e73158c1dd683012b Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 10 Mar 2017 13:16:27 +0400 Subject: Add Muse writer (#3489) * Add Muse writer * Advertise new Muse writer * Muse writer: add regressions tests --- src/Text/Pandoc.hs | 3 + src/Text/Pandoc/Writers/Muse.hs | 336 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 339 insertions(+) create mode 100644 src/Text/Pandoc/Writers/Muse.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index 5561c719d..1577491df 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -136,6 +136,7 @@ module Text.Pandoc , writeCommonMark , writeCustom , writeTEI + , writeMuse -- * Rendering templates and default templates , module Text.Pandoc.Templates -- * Miscellaneous @@ -191,6 +192,7 @@ import Text.Pandoc.Writers.LaTeX import Text.Pandoc.Writers.Man import Text.Pandoc.Writers.Markdown import Text.Pandoc.Writers.MediaWiki +import Text.Pandoc.Writers.Muse import Text.Pandoc.Writers.Native import Text.Pandoc.Writers.ODT import Text.Pandoc.Writers.OpenDocument @@ -307,6 +309,7 @@ writers = [ ,("haddock" , StringWriter writeHaddock) ,("commonmark" , StringWriter writeCommonMark) ,("tei" , StringWriter writeTEI) + ,("muse" , StringWriter writeMuse) ] getDefaultExtensions :: String -> Extensions diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs new file mode 100644 index 000000000..cc88eb762 --- /dev/null +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -0,0 +1,336 @@ +{-# LANGUAGE OverloadedStrings #-} +{- +Copyright (C) 2017 Alexander Krotov <ilabdsf@gmail.com> + +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.Writers.Muse + Copyright : Copyright (C) 2017 Alexander Krotov + License : GNU GPL, version 2 or above + + Maintainer : Alexander Krotov <ilabdsf@gmail.com> + Stability : stable + Portability : portable + +Conversion of 'Pandoc' documents to Muse. + +This module is mostly intended for <https://amusewiki.org/ Amusewiki> markup support, +as described by <https://amusewiki.org/library/manual Text::Amuse markup manual>. +Original <https://www.gnu.org/software/emacs-muse/ Emacs Muse> markup support +is a secondary goal. + +Where Text::Amuse markup +<https://metacpan.org/pod/Text::Amuse#DIFFERENCES-WITH-THE-ORIGINAL-EMACS-MUSE-MARKUP differs> +from <https://www.gnu.org/software/emacs-muse/manual/ Emacs Muse markup>, +Text::Amuse markup is supported. +For example, native tables are always used instead of Org Mode tables. +However, @\<literal style="html">@ tag is used for HTML raw blocks +even though it is supported only in Emacs Muse. +-} +module Text.Pandoc.Writers.Muse (writeMuse) where +import Control.Monad.State +import Data.List (intersperse, transpose, isInfixOf) +import System.FilePath (takeExtension) +import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Definition +import Text.Pandoc.Options +import Text.Pandoc.Pretty +import Text.Pandoc.Shared +import Text.Pandoc.Templates (renderTemplate') +import Text.Pandoc.Writers.Math +import Text.Pandoc.Writers.Shared + +type Notes = [[Block]] +data WriterState = + WriterState { stNotes :: Notes + , stOptions :: WriterOptions + , stTopLevel :: Bool + , stInsideBlock :: Bool + } + +-- | Convert Pandoc to Muse. +writeMuse :: PandocMonad m + => WriterOptions + -> Pandoc + -> m String +writeMuse opts document = + let st = WriterState { stNotes = [] + , stOptions = opts + , stTopLevel = True + , stInsideBlock = False + } + in evalStateT (pandocToMuse document) st + +-- | Return Muse representation of document. +pandocToMuse :: PandocMonad m + => Pandoc + -> StateT WriterState m String +pandocToMuse (Pandoc meta blocks) = do + opts <- gets stOptions + let colwidth = if writerWrapText opts == WrapAuto + then Just $ writerColumns opts + else Nothing + metadata <- metaToJSON opts + (fmap (render colwidth) . blockListToMuse) + (fmap (render colwidth) . inlineListToMuse) + meta + body <- blockListToMuse blocks + notes <- liftM (reverse . stNotes) get >>= notesToMuse + let main = render colwidth $ body $+$ notes + let context = defField "body" main + $ metadata + case writerTemplate opts of + Nothing -> return main + Just tpl -> return $ renderTemplate' tpl context + +-- | Convert list of Pandoc block elements to Muse. +blockListToMuse :: PandocMonad m + => [Block] -- ^ List of block elements + -> StateT WriterState m Doc +blockListToMuse blocks = do + oldState <- get + modify $ \s -> s { stTopLevel = not $ stInsideBlock s + , stInsideBlock = True + } + contents <- mapM blockToMuse blocks + modify $ \s -> s { stTopLevel = stTopLevel oldState + , stInsideBlock = stInsideBlock oldState + } + return $ cat contents + +-- | Convert Pandoc block element to Muse. +blockToMuse :: PandocMonad m + => Block -- ^ Block element + -> StateT WriterState m Doc +blockToMuse (Plain inlines) = inlineListToMuse inlines +blockToMuse (Para inlines) = do + contents <- inlineListToMuse inlines + return $ contents <> blankline +blockToMuse (LineBlock lns) = do + let splitStanza [] = [] + splitStanza xs = case break (== mempty) xs of + (l, []) -> l : [] + (l, _:r) -> l : splitStanza r + let joinWithLinefeeds = nowrap . mconcat . intersperse cr + let joinWithBlankLines = mconcat . intersperse blankline + let prettyfyStanza ls = joinWithLinefeeds <$> mapM inlineListToMuse ls + contents <- joinWithBlankLines <$> mapM prettyfyStanza (splitStanza lns) + return $ blankline $$ "<verse>" $$ contents $$ "</verse>" <> blankline +blockToMuse (CodeBlock (_,_,_) str) = do + return $ "<example>" $$ text str $$ "</example>" $$ blankline +blockToMuse (RawBlock (Format format) str) = + return $ blankline $$ "<literal style=\"" <> text format <> "\">" $$ + text str $$ "</literal>" $$ blankline +blockToMuse (BlockQuote blocks) = do + contents <- blockListToMuse blocks + return $ blankline + <> "<quote>" + $$ flush contents -- flush to drop blanklines + $$ "</quote>" + <> blankline +blockToMuse (OrderedList (start, style, _) items) = do + let markers = take (length items) $ orderedListMarkers + (start, style, Period) + let maxMarkerLength = maximum $ map length markers + let markers' = map (\m -> let s = maxMarkerLength - length m + in m ++ replicate s ' ') markers + contents <- mapM (\(item, num) -> orderedListItemToMuse item num) $ + zip markers' items + -- ensure that sublists have preceding blank line + topLevel <- gets stTopLevel + return $ cr $$ ((if topLevel then nest 1 else id) $ vcat contents) $$ blankline + where orderedListItemToMuse :: PandocMonad m + => String -- ^ marker for list item + -> [Block] -- ^ list item (list of blocks) + -> StateT WriterState m Doc + orderedListItemToMuse marker item = do + contents <- blockListToMuse item + return $ hang (length marker + 1) (text marker <> space) contents +blockToMuse (BulletList items) = do + contents <- mapM bulletListItemToMuse items + -- ensure that sublists have preceding blank line + topLevel <- gets stTopLevel + return $ cr $$ ((if topLevel then nest 1 else id) $ vcat contents) $$ blankline + where bulletListItemToMuse :: PandocMonad m + => [Block] + -> StateT WriterState m Doc + bulletListItemToMuse item = do + contents <- blockListToMuse item + return $ hang 2 "- " contents +blockToMuse (DefinitionList items) = do + contents <- mapM definitionListItemToMuse items + return $ cr $$ (nest 1 $ vcat $ contents) $$ blankline + where definitionListItemToMuse :: PandocMonad m + => ([Inline], [[Block]]) + -> StateT WriterState m Doc + definitionListItemToMuse (label, defs) = do + label' <- inlineListToMuse label + contents <- liftM vcat $ mapM blockListToMuse defs + let label'' = label' <> " :: " + let ind = offset label'' + return $ hang ind label'' contents +blockToMuse (Header level (ident,_,_) inlines) = do + contents <- inlineListToMuse inlines + let attr' = if null ident + then empty + else "#" <> text ident <> cr + let header' = text $ replicate level '*' + return $ blankline <> nowrap (header' <> space <> contents) + <> blankline <> attr' +-- https://www.gnu.org/software/emacs-muse/manual/muse.html#Horizontal-Rules-and-Anchors +blockToMuse HorizontalRule = return $ blankline $$ "----" $$ blankline +blockToMuse (Table caption _ _ headers rows) = do + caption' <- inlineListToMuse caption + headers' <- mapM blockListToMuse headers + rows' <- mapM (mapM blockListToMuse) rows + let noHeaders = all null headers + + let numChars = maximum . map offset + -- FIXME: width is not being used. + let widthsInChars = + map numChars $ transpose (headers' : rows') + -- FIXME: Muse doesn't allow blocks with height more than 1. + let hpipeBlocks sep blocks = hcat $ intersperse sep' blocks + where h = maximum (1 : map height blocks) + sep' = lblock (length sep) $ vcat (map text $ replicate h sep) + let makeRow sep = hpipeBlocks sep . zipWith lblock widthsInChars + let head' = makeRow " || " headers' + let rowSeparator = if noHeaders then " | " else " | " + rows'' <- mapM (\row -> do cols <- mapM blockListToMuse row + return $ makeRow rowSeparator cols) rows + let body = vcat rows'' + return $ (if noHeaders then empty else head') + $$ body + $$ (if null caption then empty else "|+ " <> caption' <> " +|") + $$ blankline +blockToMuse (Div _ bs) = blockListToMuse bs +blockToMuse Null = return empty + +-- | Return Muse representation of notes. +notesToMuse :: PandocMonad m + => Notes + -> StateT WriterState m Doc +notesToMuse notes = + mapM (\(num, note) -> noteToMuse num note) (zip [1..] notes) >>= + return . vsep + +-- | Return Muse representation of a note. +noteToMuse :: PandocMonad m + => Int + -> [Block] + -> StateT WriterState m Doc +noteToMuse num note = do + contents <- blockListToMuse note + let marker = "[" ++ show num ++ "] " + return $ hang (length marker) (text marker) contents + +-- | Escape special characters for Muse. +escapeString :: String -> String +escapeString s = + "<verbatim>" ++ + substitute "</verbatim>" "<</verbatim><verbatim>/verbatim>" s ++ + "</verbatim>" + +-- | Escape special characters for Muse if needed. +conditionalEscapeString :: String -> String +conditionalEscapeString s + | any (`elem` ("*<=>[]|" :: String)) s || + "::" `isInfixOf` s = escapeString s + | otherwise = s + +-- | Convert list of Pandoc inline elements to Muse. +inlineListToMuse :: PandocMonad m + => [Inline] + -> StateT WriterState m Doc +inlineListToMuse lst = mapM inlineToMuse lst >>= return . hcat + +-- | Convert Pandoc inline element to Muse. +inlineToMuse :: PandocMonad m + => Inline + -> StateT WriterState m Doc +inlineToMuse (Str str) = return $ text $ conditionalEscapeString str +inlineToMuse (Emph lst) = do + contents <- inlineListToMuse lst + return $ "<em>" <> contents <> "</em>" +inlineToMuse (Strong lst) = do + contents <- inlineListToMuse lst + return $ "<strong>" <> contents <> "</strong>" +inlineToMuse (Strikeout lst) = do + contents <- inlineListToMuse lst + return $ "<del>" <> contents <> "</del>" +inlineToMuse (Superscript lst) = do + contents <- inlineListToMuse lst + return $ "<sup>" <> contents <> "</sup>" +inlineToMuse (Subscript lst) = do + contents <- inlineListToMuse lst + return $ "<sub>" <> contents <> "</sub>" +inlineToMuse (SmallCaps lst) = inlineListToMuse lst +inlineToMuse (Quoted SingleQuote lst) = do + contents <- inlineListToMuse lst + return $ "'" <> contents <> "'" +inlineToMuse (Quoted DoubleQuote lst) = do + contents <- inlineListToMuse lst + return $ "\"" <> contents <> "\"" +-- Amusewiki does not support <cite> tag, +-- and Emacs Muse citation support is limited +-- (https://www.gnu.org/software/emacs-muse/manual/html_node/Citations.html#Citation) +-- so just fallback to expanding inlines. +inlineToMuse (Cite _ lst) = inlineListToMuse lst +inlineToMuse (Code _ str) = return $ + "<code>" <> text (conditionalEscapeString str) <> "</code>" +inlineToMuse (Math InlineMath str) = + lift (texMathToInlines InlineMath str) >>= inlineListToMuse +inlineToMuse (Math DisplayMath str) = do + contents <- lift (texMathToInlines DisplayMath str) >>= inlineListToMuse + return $ "<verse>" <> contents <> "</verse>" <> blankline +inlineToMuse (RawInline (Format f) str) = + return $ "<literal style=\"" <> text f <> "\">" <> text str <> "</literal>" +inlineToMuse LineBreak = return $ "<br>" <> cr +inlineToMuse Space = return space +inlineToMuse SoftBreak = do + wrapText <- gets $ writerWrapText . stOptions + return $ if wrapText == WrapPreserve then cr else space +inlineToMuse (Link _ txt (src, _)) = do + case txt of + [Str x] | escapeURI x == src -> + return $ "[[" <> text (escapeLink x) <> "]]" + _ -> do contents <- inlineListToMuse txt + return $ "[[" <> text (escapeLink src) <> "][" <> contents <> "]]" + where escapeLink lnk = escapeURI (if isImageUrl lnk then "URL:" ++ lnk else lnk) + -- Taken from muse-image-regexp defined in Emacs Muse file lisp/muse-regexps.el + imageExtensions = [".eps", ".gif", ".jpg", ".jpeg", ".pbm", ".png", ".tiff", ".xbm", ".xpm"] + isImageUrl = (`elem` imageExtensions) . takeExtension +inlineToMuse (Image attr alt (source,'f':'i':'g':':':title)) = + inlineToMuse (Image attr alt (source,title)) +inlineToMuse (Image _ inlines (source, title)) = do + alt <- inlineListToMuse inlines + let title' = if null title + then if null inlines + then "" + else "[" <> alt <> "]" + else "[" <> text title <> "]" + return $ "[[" <> text source <> "]" <> title' <> "]" +inlineToMuse (Note contents) = do + -- add to notes in state + notes <- gets stNotes + modify $ \st -> st { stNotes = contents:notes } + let ref = show $ (length notes) + 1 + return $ "[" <> text ref <> "]" +inlineToMuse (Span (_,name:_,_) inlines) = do + contents <- inlineListToMuse inlines + return $ "<class name=\"" <> text name <> "\">" <> contents <> "</class>" +inlineToMuse (Span _ lst) = inlineListToMuse lst -- cgit v1.2.3 From 9862d7c359ce4f9e5f89c4ee5131e0f051c134d2 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 10 Mar 2017 20:21:17 +0100 Subject: Shared.normalizeSpaces: strip off leading/trailing line breaks... ...not just spaces. --- src/Text/Pandoc/Shared.hs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 19d915b11..2eacbcc1c 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -355,8 +355,8 @@ orderedListMarkers (start, numstyle, numdelim) = in map inDelim nums -- | Normalize a list of inline elements: remove leading and trailing --- @Space@ elements, collapse double @Space@s into singles, and --- remove empty Str elements. +-- @Space@, @LineBreak@, and @SoftBreak@ elements, collapse double +-- @Space@s into singles, and remove empty @Str@ elements. normalizeSpaces :: [Inline] -> [Inline] normalizeSpaces = cleanup . dropWhile isSpaceOrEmpty where cleanup [] = [] @@ -368,6 +368,8 @@ normalizeSpaces = cleanup . dropWhile isSpaceOrEmpty isSpaceOrEmpty :: Inline -> Bool isSpaceOrEmpty Space = True +isSpaceOrEmpty SoftBreak = True +isSpaceOrEmpty LineBreak = True isSpaceOrEmpty (Str "") = True isSpaceOrEmpty _ = False -- cgit v1.2.3 From a197dc9d3fa578d223ab07884906351cced646c9 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 10 Mar 2017 20:33:30 +0100 Subject: Docx reader: more efficient trimSps. Replacing trimLineBreaks. This does the work of normalizeSpaces as well, so we avoid the need for that function here. See #1530. --- src/Text/Pandoc/Readers/Docx.hs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index 108055b42..a5efdae57 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -83,7 +83,7 @@ import Data.Default (Default) import Data.List (delete, intersect) import qualified Data.Map as M import Data.Sequence (ViewL (..), viewl) -import qualified Data.Sequence as Seq (null) +import qualified Data.Sequence as Seq import qualified Data.Set as Set import Text.Pandoc.Builder -- import Text.Pandoc.Definition @@ -476,12 +476,13 @@ rowToBlocksList (Row cells) = do blksList <- mapM cellToBlocks cells return $ map singleParaToPlain blksList -trimLineBreaks :: [Inline] -> [Inline] -trimLineBreaks [] = [] -trimLineBreaks (LineBreak : ils) = trimLineBreaks ils -trimLineBreaks ils - | (LineBreak : ils') <- reverse ils = trimLineBreaks (reverse ils') -trimLineBreaks ils = ils +-- like trimInlines, but also take out linebreaks +trimSps :: Inlines -> Inlines +trimSps (Many ils) = Many $ Seq.dropWhileL isSp $ Seq.dropWhileR isSp $ ils + where isSp Space = True + isSp SoftBreak = True + isSp LineBreak = True + isSp _ = False parStyleToTransform :: ParagraphStyle -> (Blocks -> Blocks) parStyleToTransform pPr @@ -534,8 +535,7 @@ bodyPartToBlocks (Paragraph pPr parparts) makeHeaderAnchor $ headerWith ("", delete style (pStyle pPr), []) n ils | otherwise = do - ils <- smushInlines <$> mapM parPartToInlines parparts >>= - (return . fromList . trimLineBreaks . normalizeSpaces . toList) + ils <- (trimSps . smushInlines) <$> mapM parPartToInlines parparts dropIls <- gets docxDropCap let ils' = dropIls <> ils if dropCap pPr -- cgit v1.2.3 From ba78b75146d9d02de64b400a78402aac884f0644 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 10 Mar 2017 20:44:49 +0100 Subject: Removed normalizeSpaces from Text.Pandoc.Shared. Rewrote functions in RST reader and writer to avoid the need for it. Closes #1530. --- src/Text/Pandoc/Readers/RST.hs | 6 ++++++ src/Text/Pandoc/Shared.hs | 20 -------------------- src/Text/Pandoc/Writers/RST.hs | 6 +++--- 3 files changed, 9 insertions(+), 23 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 0adc190c3..f27b02f25 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -141,6 +141,12 @@ metaFromDefList ds meta = adjustAuthors $ foldr f meta ds splitAuthors x = x splitAuthors' = map normalizeSpaces . splitOnSemi . concatMap factorSemi + normalizeSpaces = reverse . dropWhile isSp . reverse . + dropWhile isSp + isSp Space = True + isSp SoftBreak = True + isSp LineBreak = True + isSp _ = False splitOnSemi = splitBy (==Str ";") factorSemi (Str []) = [] factorSemi (Str s) = case break (==';') s of diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 2eacbcc1c..3b9ae7501 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -53,7 +53,6 @@ module Text.Pandoc.Shared ( normalizeDate, -- * Pandoc block and inline list processing orderedListMarkers, - normalizeSpaces, extractSpaces, removeFormatting, deNote, @@ -354,25 +353,6 @@ orderedListMarkers (start, numstyle, numdelim) = TwoParens -> "(" ++ str ++ ")" in map inDelim nums --- | Normalize a list of inline elements: remove leading and trailing --- @Space@, @LineBreak@, and @SoftBreak@ elements, collapse double --- @Space@s into singles, and remove empty @Str@ elements. -normalizeSpaces :: [Inline] -> [Inline] -normalizeSpaces = cleanup . dropWhile isSpaceOrEmpty - where cleanup [] = [] - cleanup (Space:rest) = case dropWhile isSpaceOrEmpty rest of - [] -> [] - (x:xs) -> Space : x : cleanup xs - cleanup ((Str ""):rest) = cleanup rest - cleanup (x:rest) = x : cleanup rest - -isSpaceOrEmpty :: Inline -> Bool -isSpaceOrEmpty Space = True -isSpaceOrEmpty SoftBreak = True -isSpaceOrEmpty LineBreak = True -isSpaceOrEmpty (Str "") = True -isSpaceOrEmpty _ = False - -- | Extract the leading and trailing spaces from inside an inline element -- and place them outside the element. SoftBreaks count as Spaces for -- these purposes. diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index 2657afa2a..496350024 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -36,7 +36,7 @@ import Data.Char (isSpace, toLower) import Data.List (intersperse, isPrefixOf, stripPrefix, transpose) import Data.Maybe (fromMaybe) import Network.URI (isURI) -import Text.Pandoc.Builder (deleteMeta) +import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.ImageSize @@ -81,7 +81,7 @@ pandocToRST (Pandoc meta blocks) = do metadata <- metaToJSON opts (fmap (render colwidth) . blockListToRST) (fmap (trimr . render colwidth) . inlineListToRST) - $ deleteMeta "title" $ deleteMeta "subtitle" meta + $ B.deleteMeta "title" $ B.deleteMeta "subtitle" meta body <- blockListToRST' True $ case writerTemplate opts of Just _ -> normalizeHeadings 1 blocks Nothing -> blocks @@ -504,7 +504,7 @@ inlineToRST (Link _ [Image attr alt (imgsrc,imgtit)] (src, _tit)) = do return $ "|" <> label <> "|" inlineToRST (Link _ txt (src, tit)) = do useReferenceLinks <- gets $ writerReferenceLinks . stOptions - linktext <- inlineListToRST $ normalizeSpaces txt + linktext <- inlineListToRST $ B.toList . B.trimInlines . B.fromList $ txt if useReferenceLinks then do refs <- gets stLinks case lookup txt refs of -- cgit v1.2.3 From ac15b0443f942771003508ccbe891954242fd07f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 10 Mar 2017 20:52:34 +0100 Subject: ConTeXt writer: converted to use PandocMonad. --- src/Text/Pandoc/Writers/ConTeXt.hs | 47 +++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 19 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index 353901fa5..57f920259 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -34,7 +34,8 @@ import Data.Char (ord) import Data.List (intercalate, intersperse) import Data.Maybe (catMaybes) import Network.URI (isURI, unEscapeString) -import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Class (PandocMonad, report) +import Text.Pandoc.Logging import Text.Pandoc.Definition import Text.Pandoc.ImageSize import Text.Pandoc.Options @@ -56,14 +57,16 @@ orderedListStyles = cycle "narg" -- | Convert Pandoc to ConTeXt. writeConTeXt :: PandocMonad m => WriterOptions -> Pandoc -> m String -writeConTeXt options document = return $ +writeConTeXt options document = let defaultWriterState = WriterState { stNextRef = 1 , stOrderedListLevel = 0 , stOptions = options } - in evalState (pandocToConTeXt options document) defaultWriterState + in evalStateT (pandocToConTeXt options document) defaultWriterState -pandocToConTeXt :: WriterOptions -> Pandoc -> State WriterState String +type WM = StateT WriterState + +pandocToConTeXt :: PandocMonad m => WriterOptions -> Pandoc -> WM m String pandocToConTeXt options (Pandoc meta blocks) = do let colwidth = if writerWrapText options == WrapAuto then Just $ writerColumns options @@ -142,7 +145,7 @@ toLabel z = concatMap go z | otherwise = [x] -- | Convert Elements to ConTeXt -elementToConTeXt :: WriterOptions -> Element -> State WriterState Doc +elementToConTeXt :: PandocMonad m => WriterOptions -> Element -> WM m Doc elementToConTeXt _ (Blk block) = blockToConTeXt block elementToConTeXt opts (Sec level _ attr title' elements) = do header' <- sectionHeader attr level title' @@ -150,8 +153,7 @@ elementToConTeXt opts (Sec level _ attr title' elements) = do return $ vcat (header' : innerContents) -- | Convert Pandoc block element to ConTeXt. -blockToConTeXt :: Block - -> State WriterState Doc +blockToConTeXt :: PandocMonad m => Block -> WM m Doc blockToConTeXt Null = return empty blockToConTeXt (Plain lst) = inlineListToConTeXt lst -- title beginning with fig: indicates that the image is a figure @@ -176,7 +178,9 @@ blockToConTeXt (CodeBlock _ str) = return $ flush ("\\starttyping" <> cr <> text str <> cr <> "\\stoptyping") $$ blankline -- blankline because \stoptyping can't have anything after it, inc. '}' blockToConTeXt (RawBlock "context" str) = return $ text str <> blankline -blockToConTeXt (RawBlock _ _ ) = return empty +blockToConTeXt b@(RawBlock _ _ ) = do + report $ BlockNotRendered b + return empty blockToConTeXt (Div (ident,_,kvs) bs) = do let align dir txt = "\\startalignment[" <> dir <> "]" $$ txt $$ "\\stopalignment" let wrapRef txt = if null ident @@ -262,16 +266,16 @@ blockToConTeXt (Table caption aligns widths heads rows) = do "\\HL" $$ headers $$ vcat rows' $$ "\\HL" $$ "\\stoptable" <> blankline -tableRowToConTeXt :: [[Block]] -> State WriterState Doc +tableRowToConTeXt :: PandocMonad m => [[Block]] -> WM m Doc tableRowToConTeXt cols = do cols' <- mapM blockListToConTeXt cols return $ (vcat (map ("\\NC " <>) cols')) $$ "\\NC\\AR" -listItemToConTeXt :: [Block] -> State WriterState Doc +listItemToConTeXt :: PandocMonad m => [Block] -> WM m Doc listItemToConTeXt list = blockListToConTeXt list >>= return . ("\\item" $$) . (nest 2) -defListItemToConTeXt :: ([Inline], [[Block]]) -> State WriterState Doc +defListItemToConTeXt :: PandocMonad m => ([Inline], [[Block]]) -> WM m Doc defListItemToConTeXt (term, defs) = do term' <- inlineListToConTeXt term def' <- liftM vsep $ mapM blockListToConTeXt defs @@ -279,12 +283,13 @@ defListItemToConTeXt (term, defs) = do "\\stopdescription" <> blankline -- | Convert list of block elements to ConTeXt. -blockListToConTeXt :: [Block] -> State WriterState Doc +blockListToConTeXt :: PandocMonad m => [Block] -> WM m Doc blockListToConTeXt lst = liftM vcat $ mapM blockToConTeXt lst -- | Convert list of inline elements to ConTeXt. -inlineListToConTeXt :: [Inline] -- ^ Inlines to convert - -> State WriterState Doc +inlineListToConTeXt :: PandocMonad m + => [Inline] -- ^ Inlines to convert + -> WM m Doc inlineListToConTeXt lst = liftM hcat $ mapM inlineToConTeXt $ addStruts lst -- We add a \strut after a line break that precedes a space, -- or the space gets swallowed @@ -298,8 +303,9 @@ inlineListToConTeXt lst = liftM hcat $ mapM inlineToConTeXt $ addStruts lst isSpacey _ = False -- | Convert inline element to ConTeXt -inlineToConTeXt :: Inline -- ^ Inline to convert - -> State WriterState Doc +inlineToConTeXt :: PandocMonad m + => Inline -- ^ Inline to convert + -> WM m Doc inlineToConTeXt (Emph lst) = do contents <- inlineListToConTeXt lst return $ braces $ "\\em " <> contents @@ -339,7 +345,9 @@ inlineToConTeXt (Math DisplayMath str) = return $ text "\\startformula " <> text str <> text " \\stopformula" <> space inlineToConTeXt (RawInline "context" str) = return $ text str inlineToConTeXt (RawInline "tex" str) = return $ text str -inlineToConTeXt (RawInline _ _) = return empty +inlineToConTeXt il@(RawInline _ _) = do + report $ InlineNotRendered il + return empty inlineToConTeXt (LineBreak) = return $ text "\\crlf" <> cr inlineToConTeXt SoftBreak = do wrapText <- gets (writerWrapText . stOptions) @@ -416,10 +424,11 @@ inlineToConTeXt (Span (_,_,kvs) ils) = do fmap (wrapLang . wrapDir) $ inlineListToConTeXt ils -- | Craft the section header, inserting the section reference, if supplied. -sectionHeader :: Attr +sectionHeader :: PandocMonad m + => Attr -> Int -> [Inline] - -> State WriterState Doc + -> WM m Doc sectionHeader (ident,classes,_) hdrLevel lst = do contents <- inlineListToConTeXt lst st <- get -- cgit v1.2.3 From be733385c9457a006da2b5d92fed0077401c3d1c Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 11 Mar 2017 18:42:39 +0100 Subject: Markdown reader: optimized nonindentSpaces. Makes the benchmark go from 40 to 36 ms. --- src/Text/Pandoc/Readers/Markdown.hs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 2cd1c0d83..169872391 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -133,17 +133,14 @@ indentSpaces = try $ do nonindentSpaces :: PandocMonad m => MarkdownParser m String nonindentSpaces = do - tabStop <- getOption readerTabStop - sps <- many (char ' ') - if length sps < tabStop - then return sps - else unexpected "indented line" + n <- skipNonindentSpaces + return $ replicate n ' ' -- returns number of spaces parsed skipNonindentSpaces :: PandocMonad m => MarkdownParser m Int skipNonindentSpaces = do tabStop <- getOption readerTabStop - atMostSpaces (tabStop - 1) <* notFollowedBy (char ' ') + atMostSpaces (tabStop - 1) <* notFollowedBy spaceChar atMostSpaces :: PandocMonad m => Int -> MarkdownParser m Int atMostSpaces n -- cgit v1.2.3 From d66b046c8a961dcb6ad08fa7ef5eea1864db4f9c Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 11 Mar 2017 23:24:14 +0100 Subject: Markdown writer: fixed bugs in simple/multiline list output. * Previously we got overlong lists with `--wrap=none`. This is fixed. * Previously a multiline list could become a simple list (and would always become one with `--wrap=none`). Closes #3384. --- src/Text/Pandoc/Writers/Markdown.hs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index ab986208d..8b58d5beb 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -673,15 +673,14 @@ pandocTable opts headless aligns widths rawHeaders rawRows = do let minNumChars = (+ 2) . maximum . map minOffset let columns = transpose (rawHeaders : rawRows) -- minimal column width without wrapping a single word - let noWordWrapWidth - | writerWrapText opts == WrapAuto - = fromIntegral $ maximum (map minNumChars columns) - | otherwise = fromIntegral $ maximum (map numChars columns) - let relWidth w = floor $ max (fromIntegral (writerColumns opts) * w) - (noWordWrapWidth * w / minimum widths) + let relWidth w col = + max (floor $ fromIntegral (writerColumns opts) * w) + (if writerWrapText opts == WrapAuto + then minNumChars col + else numChars col) let widthsInChars | isSimple = map numChars columns - | otherwise = map relWidth widths + | otherwise = zipWith relWidth widths columns let makeRow = hcat . intersperse (lblock 1 (text " ")) . (zipWith3 alignHeader aligns widthsInChars) let rows' = map makeRow rawRows @@ -698,9 +697,9 @@ pandocTable opts headless aligns widths rawHeaders rawRows = do let head'' = if headless then empty else border <> cr <> head' - let body = if maxRowHeight > 1 - then vsep rows' - else vcat rows' + let body = if isSimple + then vcat rows' + else vsep rows' let bottom = if headless then underline else border -- cgit v1.2.3 From 0ef1e51211cd35b1ddb54a5fe8bee4247f1e1801 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 12 Mar 2017 12:42:03 +0100 Subject: Shared: export extractIds. This will be used to help with #1745. --- src/Text/Pandoc/Shared.hs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 3b9ae7501..95a53be72 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -64,6 +64,7 @@ module Text.Pandoc.Shared ( Element (..), hierarchicalize, uniqueIdent, + extractIds, inlineListToIdentifier, isHeaderBlock, headerShift, @@ -529,6 +530,25 @@ headerLtEq level (Header l _ _) = l <= level headerLtEq level (Div ("",["references"],[]) (Header l _ _ : _)) = l <= level headerLtEq _ _ = False +-- | Extract the identifiers from a block element. +extractBlockIds :: Block -> Set.Set String +extractBlockIds (Header _ (ident,_,_) _) | not (null ident) = + Set.singleton ident +extractBlockIds (Div (ident,_,_) _) | not (null ident) = + Set.singleton ident +extractBlockIds _ = Set.empty + +-- | Extract the identifiers from an inline element. +extractInlineIds :: Inline -> Set.Set String +extractInlineIds (Span (ident,_,_) _) | not (null ident) = + Set.singleton ident +extractInlineIds _ = Set.empty + +-- | Extract the identifiers from a pandoc document. +extractIds :: Pandoc -> Set.Set String +extractIds doc = + query extractBlockIds doc `Set.union` query extractInlineIds doc + -- | Generate a unique identifier from a list of inlines. -- Second argument is a list of already used identifiers. uniqueIdent :: [Inline] -> Set.Set String -> String -- cgit v1.2.3 From 3765f08304a642cd85691864c1fd988b6bdb1c27 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 12 Mar 2017 21:18:19 +0100 Subject: Revert "Shared: export extractIds." This reverts commit 0ef1e51211cd35b1ddb54a5fe8bee4247f1e1801. --- src/Text/Pandoc/Shared.hs | 20 -------------------- 1 file changed, 20 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 95a53be72..3b9ae7501 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -64,7 +64,6 @@ module Text.Pandoc.Shared ( Element (..), hierarchicalize, uniqueIdent, - extractIds, inlineListToIdentifier, isHeaderBlock, headerShift, @@ -530,25 +529,6 @@ headerLtEq level (Header l _ _) = l <= level headerLtEq level (Div ("",["references"],[]) (Header l _ _ : _)) = l <= level headerLtEq _ _ = False --- | Extract the identifiers from a block element. -extractBlockIds :: Block -> Set.Set String -extractBlockIds (Header _ (ident,_,_) _) | not (null ident) = - Set.singleton ident -extractBlockIds (Div (ident,_,_) _) | not (null ident) = - Set.singleton ident -extractBlockIds _ = Set.empty - --- | Extract the identifiers from an inline element. -extractInlineIds :: Inline -> Set.Set String -extractInlineIds (Span (ident,_,_) _) | not (null ident) = - Set.singleton ident -extractInlineIds _ = Set.empty - --- | Extract the identifiers from a pandoc document. -extractIds :: Pandoc -> Set.Set String -extractIds doc = - query extractBlockIds doc `Set.union` query extractInlineIds doc - -- | Generate a unique identifier from a list of inlines. -- Second argument is a list of already used identifiers. uniqueIdent :: [Inline] -> Set.Set String -> String -- cgit v1.2.3 From c8b906256dccc7b0572c1707dee9b211c86e996d Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 12 Mar 2017 21:30:04 +0100 Subject: Improved behavior of `auto_identifiers` when there are explicit ids. Previously only autogenerated ids were added to the list of header identifiers in state, so explicit ids weren't taken into account when generating unique identifiers. Duplicated identifiers could result. This simple fix ensures that explicitly given identifiers are also taken into account. Fixes #1745. Note some limitations, however. An autogenerated identifier may still coincide with an explicit identifier that is given for a header later in the document, or with an identifier on a div, span, link, or image. Fixing this would be much more difficult, because we need to run `registerHeader` before we have the complete parse tree (so we can't get a complete list of identifiers from the document by walking the tree). However, it might be worth issuing warnings for duplicate header identifiers; I think we can do that. It is not common for headers to have the same text, and the issue can always be worked around by adding explicit identifiers, if the user is aware of it. --- src/Text/Pandoc/Parsing.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index b207e79e0..a616058bb 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -1130,7 +1130,8 @@ registerHeader (ident,classes,kvs) header' = do updateState $ updateHeaderMap $ insert' header' id' return (id'',classes,kvs) else do - unless (null ident) $ + unless (null ident) $ do + updateState $ updateIdentifierList $ Set.insert ident updateState $ updateHeaderMap $ insert' header' ident return (ident,classes,kvs) -- cgit v1.2.3 From 2f8f8f0da64388fa01d83fccf3cf1f2899c64269 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 12 Mar 2017 22:03:10 +0100 Subject: Issue warning for duplicate header identifiers. As noted in the previous commit, an autogenerated identifier may still coincide with an explicit identifier that is given for a header later in the document, or with an identifier on a div, span, link, or image. This commit adds a warning in this case, so users can supply an explicit identifier. * Added `DuplicateIdentifier` to LogMessage. * Modified HTML, Org, MediaWiki readers so their custom state type is an instance of HasLogMessages. This is necessary for `registerHeader` to issue warnings. See #1745. --- src/Text/Pandoc/Logging.hs | 11 ++++++++++- src/Text/Pandoc/Parsing.hs | 10 ++++++++-- src/Text/Pandoc/Readers/HTML.hs | 16 +++++++++++----- src/Text/Pandoc/Readers/MediaWiki.hs | 7 +++++++ src/Text/Pandoc/Readers/Org.hs | 2 ++ src/Text/Pandoc/Readers/Org/ParserState.hs | 8 ++++++++ 6 files changed, 46 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs index ba836b91a..3d2cc2287 100644 --- a/src/Text/Pandoc/Logging.hs +++ b/src/Text/Pandoc/Logging.hs @@ -62,6 +62,7 @@ data LogMessage = | CouldNotParseYamlMetadata String SourcePos | DuplicateLinkReference String SourcePos | DuplicateNoteReference String SourcePos + | DuplicateIdentifier String SourcePos | ReferenceNotFound String SourcePos | CircularReference String SourcePos | ParsingUnescaped String SourcePos @@ -106,6 +107,11 @@ instance ToJSON LogMessage where "source" .= Text.pack (sourceName pos), "line" .= toJSON (sourceLine pos), "column" .= toJSON (sourceColumn pos)] + DuplicateIdentifier s pos -> + ["contents" .= Text.pack s, + "source" .= Text.pack (sourceName pos), + "line" .= toJSON (sourceLine pos), + "column" .= toJSON (sourceColumn pos)] ReferenceNotFound s pos -> ["contents" .= Text.pack s, "source" .= Text.pack (sourceName pos), @@ -184,6 +190,8 @@ showLogMessage msg = "Duplicate link reference '" ++ s ++ "' at " ++ showPos pos DuplicateNoteReference s pos -> "Duplicate note reference '" ++ s ++ "' at " ++ showPos pos + DuplicateIdentifier s pos -> + "Duplicate identifier '" ++ s ++ "' at " ++ showPos pos ReferenceNotFound s pos -> "Reference not found for '" ++ s ++ "' at " ++ showPos pos CircularReference s pos -> @@ -233,6 +241,7 @@ messageVerbosity msg = CouldNotParseYamlMetadata{} -> WARNING DuplicateLinkReference{} -> WARNING DuplicateNoteReference{} -> WARNING + DuplicateIdentifier{} -> WARNING ReferenceNotFound{} -> WARNING CircularReference{} -> WARNING CouldNotLoadIncludeFile{} -> WARNING @@ -249,4 +258,4 @@ messageVerbosity msg = CouldNotParseCSS{} -> WARNING Fetching{} -> INFO NoTitleElement{} -> WARNING - NoLangSpecified -> INFO \ No newline at end of file + NoLangSpecified -> INFO diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index a616058bb..3058185da 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -1112,8 +1112,11 @@ type SubstTable = M.Map Key Inlines -- with its associated identifier. If the identifier is null -- and the auto_identifers extension is set, generate a new -- unique identifier, and update the list of identifiers --- in state. -registerHeader :: (Stream s m a, HasReaderOptions st, HasHeaderMap st, HasIdentifierList st) +-- in state. Issue a warning if an explicit identifier +-- is encountered that duplicates an earlier identifier +-- (explict or automatically generated). +registerHeader :: (Stream s m a, HasReaderOptions st, + HasHeaderMap st, HasLogMessages st, HasIdentifierList st) => Attr -> Inlines -> ParserT s st m Attr registerHeader (ident,classes,kvs) header' = do ids <- extractIdentifierList <$> getState @@ -1131,6 +1134,9 @@ registerHeader (ident,classes,kvs) header' = do return (id'',classes,kvs) else do unless (null ident) $ do + when (ident `Set.member` ids) $ do + pos <- getPosition + logMessage $ DuplicateIdentifier ident pos updateState $ updateIdentifierList $ Set.insert ident updateState $ updateHeaderMap $ insert' header' ident return (ident,classes,kvs) diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 7e7d505ac..0af369469 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -83,14 +83,15 @@ readHtml opts inp = do blocks <- (fixPlains False) . mconcat <$> manyTill block eof meta <- stateMeta . parserState <$> getState bs' <- replaceNotes (B.toList blocks) + reportLogMessages 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 + 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 @@ -110,7 +111,8 @@ data HTMLState = noteTable :: [(String, Blocks)], baseHref :: Maybe URI, identifiers :: Set.Set String, - headerMap :: M.Map Inlines String + headerMap :: M.Map Inlines String, + logMessages :: [LogMessage] } data HTMLLocal = HTMLLocal { quoteContext :: QuoteContext @@ -376,7 +378,7 @@ ignore raw = do -- raw can be null for tags like <!DOCTYPE>; see paRawTag -- in this case we don't want a warning: unless (null raw) $ - report $ SkippedContent raw pos + logMessage $ SkippedContent raw pos return mempty pHtmlBlock :: PandocMonad m => String -> TagParser m String @@ -1092,6 +1094,10 @@ instance HasHeaderMap HTMLState where extractHeaderMap = headerMap updateHeaderMap f s = s{ headerMap = f (headerMap s) } +instance HasLogMessages HTMLState where + addLogMessage m s = s{ logMessages = m : logMessages s } + getLogMessages = reverse . logMessages + -- This signature should be more general -- MonadReader HTMLLocal m => HasQuoteContext st m instance PandocMonad m => HasQuoteContext HTMLState (ReaderT HTMLLocal m) where diff --git a/src/Text/Pandoc/Readers/MediaWiki.hs b/src/Text/Pandoc/Readers/MediaWiki.hs index fa20ade07..b35f39aad 100644 --- a/src/Text/Pandoc/Readers/MediaWiki.hs +++ b/src/Text/Pandoc/Readers/MediaWiki.hs @@ -73,6 +73,7 @@ readMediaWiki opts s = do , mwCategoryLinks = [] , mwHeaderMap = M.empty , mwIdentifierList = Set.empty + , mwLogMessages = [] } (s ++ "\n") case parsed of @@ -85,6 +86,7 @@ data MWState = MWState { mwOptions :: ReaderOptions , mwCategoryLinks :: [Inlines] , mwHeaderMap :: M.Map Inlines String , mwIdentifierList :: Set.Set String + , mwLogMessages :: [LogMessage] } type MWParser m = ParserT [Char] MWState m @@ -100,6 +102,10 @@ instance HasIdentifierList MWState where extractIdentifierList = mwIdentifierList updateIdentifierList f st = st{ mwIdentifierList = f $ mwIdentifierList st } +instance HasLogMessages MWState where + addLogMessage m s = s{ mwLogMessages = m : mwLogMessages s } + getLogMessages = reverse . mwLogMessages + -- -- auxiliary functions -- @@ -187,6 +193,7 @@ parseMediaWiki = do let categories = if null categoryLinks then mempty else B.para $ mconcat $ intersperse B.space categoryLinks + reportLogMessages return $ B.doc $ bs <> categories -- diff --git a/src/Text/Pandoc/Readers/Org.hs b/src/Text/Pandoc/Readers/Org.hs index cc3ed6003..5e509178d 100644 --- a/src/Text/Pandoc/Readers/Org.hs +++ b/src/Text/Pandoc/Readers/Org.hs @@ -35,6 +35,7 @@ import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Error import Text.Pandoc.Options +import Text.Pandoc.Parsing (reportLogMessages) import Control.Monad.Except (throwError) import Control.Monad.Reader (runReaderT) @@ -59,4 +60,5 @@ parseOrg :: PandocMonad m => OrgParser m Pandoc parseOrg = do blocks' <- blockList meta' <- meta + reportLogMessages return $ Pandoc meta' blocks' diff --git a/src/Text/Pandoc/Readers/Org/ParserState.hs b/src/Text/Pandoc/Readers/Org/ParserState.hs index 0bbe27991..6bed2a547 100644 --- a/src/Text/Pandoc/Readers/Org/ParserState.hs +++ b/src/Text/Pandoc/Readers/Org/ParserState.hs @@ -60,7 +60,9 @@ import qualified Data.Set as Set import Text.Pandoc.Builder (Blocks, Inlines, trimInlines) import Text.Pandoc.Definition (Meta (..), nullMeta) import Text.Pandoc.Options (ReaderOptions (..)) +import Text.Pandoc.Logging import Text.Pandoc.Parsing (HasHeaderMap (..), HasIdentifierList (..), + HasLogMessages (..), HasLastStrPosition (..), HasQuoteContext (..), HasReaderOptions (..), ParserContext (..), QuoteContext (..), SourcePos) @@ -104,6 +106,7 @@ data OrgParserState = OrgParserState , orgStateOptions :: ReaderOptions , orgStateParserContext :: ParserContext , orgStateTodoSequences :: [TodoSequence] + , orgLogMessages :: [LogMessage] } data OrgParserLocal = OrgParserLocal { orgLocalQuoteContext :: QuoteContext } @@ -130,6 +133,10 @@ instance HasHeaderMap OrgParserState where extractHeaderMap = orgStateHeaderMap updateHeaderMap f s = s{ orgStateHeaderMap = f (orgStateHeaderMap s) } +instance HasLogMessages OrgParserState where + addLogMessage msg st = st{ orgLogMessages = msg : orgLogMessages st } + getLogMessages st = reverse $ orgLogMessages st + instance Default OrgParserState where def = defaultOrgParserState @@ -150,6 +157,7 @@ defaultOrgParserState = OrgParserState , orgStateOptions = def , orgStateParserContext = NullState , orgStateTodoSequences = [] + , orgLogMessages = [] } optionsToParserState :: ReaderOptions -> OrgParserState -- cgit v1.2.3 From df7a443f672bf92d2821c494d20bf510f238d7f8 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 12 Mar 2017 23:09:00 +0100 Subject: MediaWiki writer: use PandocMonad. --- src/Text/Pandoc/Writers/MediaWiki.hs | 52 ++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 23 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/MediaWiki.hs b/src/Text/Pandoc/Writers/MediaWiki.hs index cb36df5f5..594e31e95 100644 --- a/src/Text/Pandoc/Writers/MediaWiki.hs +++ b/src/Text/Pandoc/Writers/MediaWiki.hs @@ -35,7 +35,8 @@ import Control.Monad.State import Data.List (intercalate) import qualified Data.Set as Set import Network.URI (isURI) -import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Class (PandocMonad, report) +import Text.Pandoc.Logging import Text.Pandoc.Definition import Text.Pandoc.ImageSize import Text.Pandoc.Options @@ -56,17 +57,17 @@ data WriterReader = WriterReader { , useTags :: Bool -- True if we should use HTML tags because we're in a complex list } -type MediaWikiWriter = ReaderT WriterReader (State WriterState) +type MediaWikiWriter m = ReaderT WriterReader (StateT WriterState m) -- | Convert Pandoc to MediaWiki. writeMediaWiki :: PandocMonad m => WriterOptions -> Pandoc -> m String -writeMediaWiki opts document = return $ +writeMediaWiki opts document = let initialState = WriterState { stNotes = False, stOptions = opts } env = WriterReader { options = opts, listLevel = [], useTags = False } - in evalState (runReaderT (pandocToMediaWiki document) env) initialState + in evalStateT (runReaderT (pandocToMediaWiki document) env) initialState -- | Return MediaWiki representation of document. -pandocToMediaWiki :: Pandoc -> MediaWikiWriter String +pandocToMediaWiki :: PandocMonad m => Pandoc -> MediaWikiWriter m String pandocToMediaWiki (Pandoc meta blocks) = do opts <- asks options metadata <- metaToJSON opts @@ -90,8 +91,9 @@ escapeString :: String -> String escapeString = escapeStringForXML -- | Convert Pandoc block element to MediaWiki. -blockToMediaWiki :: Block -- ^ Block element - -> MediaWikiWriter String +blockToMediaWiki :: PandocMonad m + => Block -- ^ Block element + -> MediaWikiWriter m String blockToMediaWiki Null = return "" @@ -125,10 +127,10 @@ blockToMediaWiki (Para inlines) = do blockToMediaWiki (LineBlock lns) = blockToMediaWiki $ linesToPara lns -blockToMediaWiki (RawBlock f str) +blockToMediaWiki b@(RawBlock f str) | f == Format "mediawiki" = return str | f == Format "html" = return str - | otherwise = return "" + | otherwise = "" <$ report (BlockNotRendered b) blockToMediaWiki HorizontalRule = return "\n-----\n" @@ -212,7 +214,7 @@ listAttribsToString (startnum, numstyle, _) = else "") -- | Convert bullet or ordered list item (list of blocks) to MediaWiki. -listItemToMediaWiki :: [Block] -> MediaWikiWriter String +listItemToMediaWiki :: PandocMonad m => [Block] -> MediaWikiWriter m String listItemToMediaWiki items = do contents <- blockListToMediaWiki items tags <- asks useTags @@ -223,8 +225,9 @@ listItemToMediaWiki items = do return $ marker ++ " " ++ contents -- | Convert definition list item (label, list of blocks) to MediaWiki. -definitionListItemToMediaWiki :: ([Inline],[[Block]]) - -> MediaWikiWriter String +definitionListItemToMediaWiki :: PandocMonad m + => ([Inline],[[Block]]) + -> MediaWikiWriter m String definitionListItemToMediaWiki (label, items) = do labelText <- inlineListToMediaWiki label contents <- mapM blockListToMediaWiki items @@ -278,20 +281,22 @@ vcat = intercalate "\n" -- Auxiliary functions for tables: -tableRowToMediaWiki :: Bool +tableRowToMediaWiki :: PandocMonad m + => Bool -> [Alignment] -> [Double] -> (Int, [[Block]]) - -> MediaWikiWriter String + -> MediaWikiWriter m String tableRowToMediaWiki headless alignments widths (rownum, cells) = do cells' <- mapM (tableCellToMediaWiki headless rownum) $ zip3 alignments widths cells return $ unlines cells' -tableCellToMediaWiki :: Bool +tableCellToMediaWiki :: PandocMonad m + => Bool -> Int -> (Alignment, Double, [Block]) - -> MediaWikiWriter String + -> MediaWikiWriter m String tableCellToMediaWiki headless rownum (alignment, width, bs) = do contents <- blockListToMediaWiki bs let marker = if rownum == 1 && not headless then "!" else "|" @@ -316,7 +321,7 @@ alignmentToString alignment = case alignment of AlignCenter -> "center" AlignDefault -> "left" -imageToMediaWiki :: Attr -> MediaWikiWriter String +imageToMediaWiki :: PandocMonad m => Attr -> MediaWikiWriter m String imageToMediaWiki attr = do opts <- gets stOptions let (_, cls, _) = attr @@ -334,18 +339,19 @@ imageToMediaWiki attr = do return $ dims ++ classes -- | Convert list of Pandoc block elements to MediaWiki. -blockListToMediaWiki :: [Block] -- ^ List of block elements - -> MediaWikiWriter String +blockListToMediaWiki :: PandocMonad m + => [Block] -- ^ List of block elements + -> MediaWikiWriter m String blockListToMediaWiki blocks = fmap vcat $ mapM blockToMediaWiki blocks -- | Convert list of Pandoc inline elements to MediaWiki. -inlineListToMediaWiki :: [Inline] -> MediaWikiWriter String +inlineListToMediaWiki :: PandocMonad m => [Inline] -> MediaWikiWriter m String inlineListToMediaWiki lst = fmap concat $ mapM inlineToMediaWiki lst -- | Convert Pandoc inline element to MediaWiki. -inlineToMediaWiki :: Inline -> MediaWikiWriter String +inlineToMediaWiki :: PandocMonad m => Inline -> MediaWikiWriter m String inlineToMediaWiki (Span attrs ils) = do contents <- inlineListToMediaWiki ils @@ -394,10 +400,10 @@ inlineToMediaWiki (Math mt str) = return $ "\">" ++ str ++ "</math>" -- note: str should NOT be escaped -inlineToMediaWiki (RawInline f str) +inlineToMediaWiki il@(RawInline f str) | f == Format "mediawiki" = return str | f == Format "html" = return str - | otherwise = return "" + | otherwise = "" <$ report (InlineNotRendered il) inlineToMediaWiki LineBreak = return "<br />\n" -- cgit v1.2.3 From 1012e668cfd57c919e930a7919a9bd69a7c3a486 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 12 Mar 2017 23:15:22 +0100 Subject: Converted DokuWiki writer to use PandocMonad. --- src/Text/Pandoc/Writers/DokuWiki.hs | 59 +++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 25 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/DokuWiki.hs b/src/Text/Pandoc/Writers/DokuWiki.hs index 215d0b2fb..5e29acbaf 100644 --- a/src/Text/Pandoc/Writers/DokuWiki.hs +++ b/src/Text/Pandoc/Writers/DokuWiki.hs @@ -41,11 +41,12 @@ DokuWiki: <https://www.dokuwiki.org/dokuwiki> module Text.Pandoc.Writers.DokuWiki ( writeDokuWiki ) where import Control.Monad (zipWithM) import Control.Monad.Reader (ReaderT, ask, local, runReaderT) -import Control.Monad.State (State, evalState, gets, modify) +import Control.Monad.State (StateT, evalStateT, gets, modify) import Data.Default (Default (..)) import Data.List (intercalate, intersect, isPrefixOf, transpose) import Network.URI (isURI) -import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Class (PandocMonad, report) +import Text.Pandoc.Logging import Text.Pandoc.Definition import Text.Pandoc.ImageSize import Text.Pandoc.Options (WrapOption (..), WriterOptions (writerTableOfContents, writerTemplate, writerWrapText)) @@ -72,18 +73,19 @@ instance Default WriterEnvironment where , stUseTags = False , stBackSlashLB = False } -type DokuWiki = ReaderT WriterEnvironment (State WriterState) +type DokuWiki m = ReaderT WriterEnvironment (StateT WriterState m) -- | Convert Pandoc to DokuWiki. writeDokuWiki :: PandocMonad m => WriterOptions -> Pandoc -> m String -writeDokuWiki opts document = return $ +writeDokuWiki opts document = runDokuWiki (pandocToDokuWiki opts document) -runDokuWiki :: DokuWiki a -> a -runDokuWiki = flip evalState def . flip runReaderT def +runDokuWiki :: PandocMonad m => DokuWiki m a -> m a +runDokuWiki = flip evalStateT def . flip runReaderT def -- | Return DokuWiki representation of document. -pandocToDokuWiki :: WriterOptions -> Pandoc -> DokuWiki String +pandocToDokuWiki :: PandocMonad m + => WriterOptions -> Pandoc -> DokuWiki m String pandocToDokuWiki opts (Pandoc meta blocks) = do metadata <- metaToJSON opts (fmap trimr . blockListToDokuWiki opts) @@ -110,9 +112,10 @@ escapeString = substitute "__" "%%__%%" . substitute "//" "%%//%%" -- | Convert Pandoc block element to DokuWiki. -blockToDokuWiki :: WriterOptions -- ^ Options +blockToDokuWiki :: PandocMonad m + => WriterOptions -- ^ Options -> Block -- ^ Block element - -> DokuWiki String + -> DokuWiki m String blockToDokuWiki _ Null = return "" @@ -147,12 +150,12 @@ blockToDokuWiki opts (Para inlines) = do blockToDokuWiki opts (LineBlock lns) = blockToDokuWiki opts $ linesToPara lns -blockToDokuWiki _ (RawBlock f str) +blockToDokuWiki _ b@(RawBlock f str) | f == Format "dokuwiki" = return str -- See https://www.dokuwiki.org/wiki:syntax -- use uppercase HTML tag for block-level content: | f == Format "html" = return $ "<HTML>\n" ++ str ++ "\n</HTML>" - | otherwise = return "" + | otherwise = "" <$ (report $ BlockNotRendered b) blockToDokuWiki _ HorizontalRule = return "\n----\n" @@ -276,7 +279,8 @@ listAttribsToString (startnum, numstyle, _) = else "") -- | Convert bullet list item (list of blocks) to DokuWiki. -listItemToDokuWiki :: WriterOptions -> [Block] -> DokuWiki String +listItemToDokuWiki :: PandocMonad m + => WriterOptions -> [Block] -> DokuWiki m String listItemToDokuWiki opts items = do contents <- blockListToDokuWiki opts items useTags <- stUseTags <$> ask @@ -290,7 +294,7 @@ listItemToDokuWiki opts items = do -- | Convert ordered list item (list of blocks) to DokuWiki. -- | TODO Emiminate dreadful duplication of text from listItemToDokuWiki -orderedListItemToDokuWiki :: WriterOptions -> [Block] -> DokuWiki String +orderedListItemToDokuWiki :: PandocMonad m => WriterOptions -> [Block] -> DokuWiki m String orderedListItemToDokuWiki opts items = do contents <- blockListToDokuWiki opts items useTags <- stUseTags <$> ask @@ -303,9 +307,10 @@ orderedListItemToDokuWiki opts items = do return $ indent' ++ "- " ++ contents -- | Convert definition list item (label, list of blocks) to DokuWiki. -definitionListItemToDokuWiki :: WriterOptions +definitionListItemToDokuWiki :: PandocMonad m + => WriterOptions -> ([Inline],[[Block]]) - -> DokuWiki String + -> DokuWiki m String definitionListItemToDokuWiki opts (label, items) = do labelText <- inlineListToDokuWiki opts label contents <- mapM (blockListToDokuWiki opts) items @@ -370,10 +375,11 @@ backSlashLineBreaks cs = reverse $ g $ reverse $ concatMap f cs -- Auxiliary functions for tables: -tableItemToDokuWiki :: WriterOptions - -> Alignment - -> [Block] - -> DokuWiki String +tableItemToDokuWiki :: PandocMonad m + => WriterOptions + -> Alignment + -> [Block] + -> DokuWiki m String tableItemToDokuWiki opts align' item = do let mkcell x = (if align' == AlignRight || align' == AlignCenter then " " @@ -386,9 +392,10 @@ tableItemToDokuWiki opts align' item = do return $ mkcell contents -- | Convert list of Pandoc block elements to DokuWiki. -blockListToDokuWiki :: WriterOptions -- ^ Options +blockListToDokuWiki :: PandocMonad m + => WriterOptions -- ^ Options -> [Block] -- ^ List of block elements - -> DokuWiki String + -> DokuWiki m String blockListToDokuWiki opts blocks = do backSlash <- stBackSlashLB <$> ask let blocks' = consolidateRawBlocks blocks @@ -403,12 +410,14 @@ consolidateRawBlocks (RawBlock f1 b1 : RawBlock f2 b2 : xs) consolidateRawBlocks (x:xs) = x : consolidateRawBlocks xs -- | Convert list of Pandoc inline elements to DokuWiki. -inlineListToDokuWiki :: WriterOptions -> [Inline] -> DokuWiki String +inlineListToDokuWiki :: PandocMonad m + => WriterOptions -> [Inline] -> DokuWiki m String inlineListToDokuWiki opts lst = concat <$> (mapM (inlineToDokuWiki opts) lst) -- | Convert Pandoc inline element to DokuWiki. -inlineToDokuWiki :: WriterOptions -> Inline -> DokuWiki String +inlineToDokuWiki :: PandocMonad m + => WriterOptions -> Inline -> DokuWiki m String inlineToDokuWiki opts (Span _attrs ils) = inlineListToDokuWiki opts ils @@ -465,10 +474,10 @@ inlineToDokuWiki _ (Math mathType str) = return $ delim ++ str ++ delim DisplayMath -> "$$" InlineMath -> "$" -inlineToDokuWiki _ (RawInline f str) +inlineToDokuWiki _ il@(RawInline f str) | f == Format "dokuwiki" = return str | f == Format "html" = return $ "<html>" ++ str ++ "</html>" - | otherwise = return "" + | otherwise = "" <$ report (InlineNotRendered il) inlineToDokuWiki _ LineBreak = return "\\\\\n" -- cgit v1.2.3 From 0196ca893d5cbdf3d31ee5f5e9bcf76240f6698b Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sun, 12 Mar 2017 23:29:39 +0100 Subject: Org reader: interpret more meta value as inlines The values of the following meta variables are now interpreted using org-markup instead of treating them as pure strings: - *keywords*: comma-separated list of inlines - *subtitle*: inline values - *nocite*: inline values; using it multiple times accumulates the values. --- src/Text/Pandoc/Readers/Org/Meta.hs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/Meta.hs b/src/Text/Pandoc/Readers/Org/Meta.hs index c22f441d4..69ca00b23 100644 --- a/src/Text/Pandoc/Readers/Org/Meta.hs +++ b/src/Text/Pandoc/Readers/Org/Meta.hs @@ -90,8 +90,11 @@ metaValue key = let inclKey = "header-includes" in case key of "author" -> (key,) <$> metaInlinesCommaSeparated + "keywords" -> (key,) <$> metaInlinesCommaSeparated "title" -> (key,) <$> metaInlines + "subtitle" -> (key,) <$> metaInlines "date" -> (key,) <$> metaInlines + "nocite" -> (key,) <$> accumulatingList key metaInlines "header-includes" -> (key,) <$> accumulatingList key metaInlines "latex_header" -> (inclKey,) <$> accumulatingList inclKey (metaExportSnippet "latex") @@ -109,11 +112,11 @@ metaInlines = fmap (MetaInlines . B.toList) <$> inlinesTillNewline metaInlinesCommaSeparated :: PandocMonad m => OrgParser m (F MetaValue) metaInlinesCommaSeparated = do - authStrs <- (many1 (noneOf ",\n")) `sepBy1` (char ',') + itemStrs <- (many1 (noneOf ",\n")) `sepBy1` (char ',') newline - authors <- mapM (parseFromString inlinesTillNewline . (++ "\n")) authStrs + items <- mapM (parseFromString inlinesTillNewline . (++ "\n")) itemStrs let toMetaInlines = MetaInlines . B.toList - return $ MetaList . map toMetaInlines <$> sequence authors + return $ MetaList . map toMetaInlines <$> sequence items metaString :: Monad m => OrgParser m (F MetaValue) metaString = metaModifiedString id @@ -183,7 +186,9 @@ parseFormat = try $ do tillSpecifier c = manyTill (noneOf "\n\r") (try $ string ('%':c:"")) inlinesTillNewline :: PandocMonad m => OrgParser m (F Inlines) -inlinesTillNewline = trimInlinesF . mconcat <$> manyTill inline newline +inlinesTillNewline = do + updateLastPreCharPos + trimInlinesF . mconcat <$> manyTill inline newline -- -- ToDo Sequences and Keywords -- cgit v1.2.3 From efcb51bcb01b5f07d0957fdac7f73e366d9f8c85 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 13 Mar 2017 11:08:17 +0100 Subject: Put TEI writer inside PandocMonad. Added warnings for omitted raw elements. Also added identifiers on `<div>` elements. These were commented out before, not sure why? --- src/Text/Pandoc/Writers/TEI.hs | 253 ++++++++++++++++++++++------------------- 1 file changed, 135 insertions(+), 118 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/TEI.hs b/src/Text/Pandoc/Writers/TEI.hs index 0ef283ad3..d6d8d60b7 100644 --- a/src/Text/Pandoc/Writers/TEI.hs +++ b/src/Text/Pandoc/Writers/TEI.hs @@ -33,7 +33,8 @@ module Text.Pandoc.Writers.TEI (writeTEI) where import Data.Char (toLower) import Data.List (isPrefixOf, stripPrefix) import qualified Text.Pandoc.Builder as B -import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Class (PandocMonad, report) +import Text.Pandoc.Logging import Text.Pandoc.Definition import Text.Pandoc.Highlighting (languages, languagesByExtension) import Text.Pandoc.ImageSize @@ -45,18 +46,18 @@ import Text.Pandoc.Writers.Shared import Text.Pandoc.XML -- | Convert list of authors to a docbook <author> section -authorToTEI :: WriterOptions -> [Inline] -> B.Inlines -authorToTEI opts name' = - let name = render Nothing $ inlinesToTEI opts name' - colwidth = if writerWrapText opts == WrapAuto +authorToTEI :: PandocMonad m => WriterOptions -> [Inline] -> m B.Inlines +authorToTEI opts name' = do + name <- render Nothing <$> inlinesToTEI opts name' + let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing - in B.rawInline "tei" $ render colwidth $ + return $ B.rawInline "tei" $ render colwidth $ inTagsSimple "author" (text $ escapeStringForXML name) -- | Convert Pandoc document to string in Docbook format. writeTEI :: PandocMonad m => WriterOptions -> Pandoc -> m String -writeTEI opts (Pandoc meta blocks) = return $ +writeTEI opts (Pandoc meta blocks) = do let elements = hierarchicalize blocks colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts @@ -67,27 +68,27 @@ writeTEI opts (Pandoc meta blocks) = return $ TopLevelChapter -> 0 TopLevelSection -> 1 TopLevelDefault -> 1 - auths' = map (authorToTEI opts) $ docAuthors meta - meta' = B.setMeta "author" auths' meta - Just metadata = metaToJSON opts - (Just . render colwidth . (vcat . - (map (elementToTEI opts startLvl)) . hierarchicalize)) - (Just . render colwidth . inlinesToTEI opts) + auths' <- mapM (authorToTEI opts) $ docAuthors meta + let meta' = B.setMeta "author" auths' meta + metadata <- metaToJSON opts + (fmap (render colwidth . vcat) . + (mapM (elementToTEI opts startLvl)) . hierarchicalize) + (fmap (render colwidth) . inlinesToTEI opts) meta' - main = render' $ vcat (map (elementToTEI opts startLvl) elements) - context = defField "body" main + main <- (render' . vcat) <$> mapM (elementToTEI opts startLvl) elements + let context = defField "body" main $ defField "mathml" (case writerHTMLMathMethod opts of MathML -> True _ -> False) $ metadata - in case writerTemplate opts of - Nothing -> main - Just tpl -> renderTemplate' tpl context + case writerTemplate opts of + Nothing -> return main + Just tpl -> return $ renderTemplate' tpl context -- | Convert an Element to TEI. -elementToTEI :: WriterOptions -> Int -> Element -> Doc +elementToTEI :: PandocMonad m => WriterOptions -> Int -> Element -> m Doc elementToTEI opts _ (Blk block) = blockToTEI opts block -elementToTEI opts lvl (Sec _ _num (id',_,_) title elements) = +elementToTEI opts lvl (Sec _ _num (id',_,_) title elements) = do -- TEI doesn't allow sections with no content, so insert some if needed let elements' = if null elements then [Blk (Para [])] @@ -98,14 +99,15 @@ elementToTEI opts lvl (Sec _ _num (id',_,_) title elements) = | n == 0 -> "chapter" | n >= 1 && n <= 5 -> "level" ++ show n | otherwise -> "section" - in inTags True "div" [("type", divType) | not (null id')] $ --- ("id", writerIdentifierPrefix opts ++ id') | not (null id')] $ - inTagsSimple "head" (inlinesToTEI opts title) $$ - vcat (map (elementToTEI opts (lvl + 1)) elements') + contents <- vcat <$> mapM (elementToTEI opts (lvl + 1)) elements' + titleContents <- inlinesToTEI opts title + return $ inTags True "div" (("type", divType) : + [("id", writerIdentifierPrefix opts ++ id') | not (null id')]) $ + inTagsSimple "head" titleContents $$ contents -- | Convert a list of Pandoc blocks to TEI. -blocksToTEI :: WriterOptions -> [Block] -> Doc -blocksToTEI opts = vcat . map (blockToTEI opts) +blocksToTEI :: PandocMonad m => WriterOptions -> [Block] -> m Doc +blocksToTEI opts bs = vcat <$> mapM (blockToTEI opts) bs -- | Auxiliary function to convert Plain block to Para. plainToPara :: Block -> Block @@ -114,28 +116,32 @@ plainToPara x = x -- | Convert a list of pairs of terms and definitions into a TEI -- list with labels and items. -deflistItemsToTEI :: WriterOptions -> [([Inline],[[Block]])] -> Doc +deflistItemsToTEI :: PandocMonad m + => WriterOptions -> [([Inline],[[Block]])] -> m Doc deflistItemsToTEI opts items = - vcat $ map (\(term, defs) -> deflistItemToTEI opts term defs) items + vcat <$> mapM (\(term, defs) -> deflistItemToTEI opts term defs) items -- | Convert a term and a list of blocks into a TEI varlistentry. -deflistItemToTEI :: WriterOptions -> [Inline] -> [[Block]] -> Doc -deflistItemToTEI opts term defs = +deflistItemToTEI :: PandocMonad m + => WriterOptions -> [Inline] -> [[Block]] -> m Doc +deflistItemToTEI opts term defs = do let def' = concatMap (map plainToPara) defs - in inTagsIndented "label" (inlinesToTEI opts term) $$ - inTagsIndented "item" (blocksToTEI opts def') + term' <- inlinesToTEI opts term + defs' <- blocksToTEI opts $ concatMap (map plainToPara) defs + return $ inTagsIndented "label" term' $$ + inTagsIndented "item" defs' -- | Convert a list of lists of blocks to a list of TEI list items. -listItemsToTEI :: WriterOptions -> [[Block]] -> Doc -listItemsToTEI opts items = vcat $ map (listItemToTEI opts) items +listItemsToTEI :: PandocMonad m => WriterOptions -> [[Block]] -> m Doc +listItemsToTEI opts items = vcat <$> mapM (listItemToTEI opts) items -- | Convert a list of blocks into a TEI list item. -listItemToTEI :: WriterOptions -> [Block] -> Doc +listItemToTEI :: PandocMonad m => WriterOptions -> [Block] -> m Doc listItemToTEI opts item = - inTagsIndented "item" $ blocksToTEI opts $ map plainToPara item + inTagsIndented "item" <$> blocksToTEI opts (map plainToPara item) -imageToTEI :: WriterOptions -> Attr -> String -> Doc -imageToTEI _ attr src = selfClosingTag "graphic" $ +imageToTEI :: PandocMonad m => WriterOptions -> Attr -> String -> m Doc +imageToTEI _ attr src = return $ selfClosingTag "graphic" $ ("url", src) : idAndRole attr ++ dims where dims = go Width "width" ++ go Height "depth" @@ -144,15 +150,16 @@ imageToTEI _ attr src = selfClosingTag "graphic" $ Nothing -> [] -- | Convert a Pandoc block element to TEI. -blockToTEI :: WriterOptions -> Block -> Doc -blockToTEI _ Null = empty +blockToTEI :: PandocMonad m => WriterOptions -> Block -> m Doc +blockToTEI _ Null = return empty -- Add ids to paragraphs in divs with ids - this is needed for -- pandoc-citeproc to get link anchors in bibliographies: -blockToTEI opts (Div (ident,_,_) [Para lst]) = - let attribs = [("id", ident) | not (null ident)] in - inTags False "p" attribs $ inlinesToTEI opts lst +blockToTEI opts (Div (ident,_,_) [Para lst]) = do + let attribs = [("id", ident) | not (null ident)] + inTags False "p" attribs <$> inlinesToTEI opts lst blockToTEI opts (Div _ bs) = blocksToTEI opts $ map plainToPara bs -blockToTEI _ (Header _ _ _) = empty -- should not occur after hierarchicalize +blockToTEI _ (Header _ _ _) = return empty +-- should not occur after hierarchicalize -- For TEI simple, text must be within containing block element, so -- we use plainToPara to ensure that Plain text ends up contained by -- something. @@ -170,13 +177,13 @@ blockToTEI opts (Plain lst) = blockToTEI opts $ Para lst -- (imageToTEI opts attr src)) $$ -- inTagsSimple "textobject" (inTagsSimple "phrase" alt)) blockToTEI opts (Para lst) = - inTags False "p" [] $ inlinesToTEI opts lst + inTags False "p" [] <$> inlinesToTEI opts lst blockToTEI opts (LineBlock lns) = blockToTEI opts $ linesToPara lns blockToTEI opts (BlockQuote blocks) = - inTagsIndented "quote" $ blocksToTEI opts blocks + inTagsIndented "quote" <$> blocksToTEI opts blocks blockToTEI _ (CodeBlock (_,classes,_) str) = - text ("<ab type='codeblock " ++ lang ++ "'>") <> cr <> + return $ text ("<ab type='codeblock " ++ lang ++ "'>") <> cr <> flush (text (escapeStringForXML str) <> cr <> text "</ab>") where lang = if null langs then "" @@ -186,11 +193,11 @@ blockToTEI _ (CodeBlock (_,classes,_) str) = then [s] else languagesByExtension . map toLower $ s langs = concatMap langsFrom classes -blockToTEI opts (BulletList lst) = +blockToTEI opts (BulletList lst) = do let attribs = [("type", "unordered")] - in inTags True "list" attribs $ listItemsToTEI opts lst -blockToTEI _ (OrderedList _ []) = empty -blockToTEI opts (OrderedList (start, numstyle, _) (first:rest)) = + inTags True "list" attribs <$> listItemsToTEI opts lst +blockToTEI _ (OrderedList _ []) = return empty +blockToTEI opts (OrderedList (start, numstyle, _) (first:rest)) = do let attribs = case numstyle of DefaultStyle -> [] Decimal -> [("type", "ordered:arabic")] @@ -199,120 +206,130 @@ blockToTEI opts (OrderedList (start, numstyle, _) (first:rest)) = LowerAlpha -> [("type", "ordered:loweralpha")] UpperRoman -> [("type", "ordered:upperroman")] LowerRoman -> [("type", "ordered:lowerroman")] - items = if start == 1 - then listItemsToTEI opts (first:rest) - else (inTags True "item" [("n",show start)] - (blocksToTEI opts $ map plainToPara first)) $$ - listItemsToTEI opts rest - in inTags True "list" attribs items -blockToTEI opts (DefinitionList lst) = + items <- if start == 1 + then listItemsToTEI opts (first:rest) + else do + fi <- blocksToTEI opts $ map plainToPara first + re <- listItemsToTEI opts rest + return $ (inTags True "item" [("n",show start)] fi) $$ re + return $ inTags True "list" attribs items +blockToTEI opts (DefinitionList lst) = do let attribs = [("type", "definition")] - in inTags True "list" attribs $ deflistItemsToTEI opts lst -blockToTEI _ (RawBlock f str) - | f == "tei" = text str -- raw TEI block (should such a thing exist). --- | f == "html" = text str -- allow html for backwards compatibility - | otherwise = empty -blockToTEI _ HorizontalRule = - selfClosingTag "milestone" [("unit","undefined"), ("type","separator"),("rendition","line")] + inTags True "list" attribs <$> deflistItemsToTEI opts lst +blockToTEI _ b@(RawBlock f str) + | f == "tei" = return $ text str + -- raw TEI block (should such a thing exist). + | otherwise = do + report $ BlockNotRendered b + return empty +blockToTEI _ HorizontalRule = return $ + selfClosingTag "milestone" [("unit","undefined") + ,("type","separator") + ,("rendition","line")] -- | TEI Tables -- TEI Simple's tables are composed of cells and rows; other -- table info in the AST is here lossily discard. -blockToTEI opts (Table _ _ _ headers rows) = - let - headers' = tableHeadersToTEI opts headers --- headers' = if all null headers --- then return empty --- else tableRowToTEI opts headers - in - inTags True "table" [] $ - vcat $ [headers'] <> map (tableRowToTEI opts) rows +blockToTEI opts (Table _ _ _ headers rows) = do + headers' <- tableHeadersToTEI opts headers + rows' <- mapM (tableRowToTEI opts) rows + return $ inTags True "table" [] $ headers' $$ vcat rows' -tableRowToTEI :: WriterOptions - -> [[Block]] - -> Doc +tableRowToTEI :: PandocMonad m + => WriterOptions + -> [[Block]] + -> m Doc tableRowToTEI opts cols = - inTagsIndented "row" $ vcat $ map (tableItemToTEI opts) cols + (inTagsIndented "row" . vcat) <$> mapM (tableItemToTEI opts) cols -tableHeadersToTEI :: WriterOptions +tableHeadersToTEI :: PandocMonad m + => WriterOptions -> [[Block]] - -> Doc + -> m Doc tableHeadersToTEI opts cols = - inTags True "row" [("role","label")] $ vcat $ map (tableItemToTEI opts) cols + (inTags True "row" [("role","label")] . vcat) <$> + mapM (tableItemToTEI opts) cols -tableItemToTEI :: WriterOptions - -> [Block] - -> Doc +tableItemToTEI :: PandocMonad m + => WriterOptions + -> [Block] + -> m Doc tableItemToTEI opts item = - inTags False "cell" [] $ vcat $ map (blockToTEI opts) item + (inTags False "cell" [] . vcat) <$> mapM (blockToTEI opts) item -- | Convert a list of inline elements to TEI. -inlinesToTEI :: WriterOptions -> [Inline] -> Doc -inlinesToTEI opts lst = hcat $ map (inlineToTEI opts) lst +inlinesToTEI :: PandocMonad m => WriterOptions -> [Inline] -> m Doc +inlinesToTEI opts lst = hcat <$> mapM (inlineToTEI opts) lst -- | Convert an inline element to TEI. -inlineToTEI :: WriterOptions -> Inline -> Doc -inlineToTEI _ (Str str) = text $ escapeStringForXML str +inlineToTEI :: PandocMonad m => WriterOptions -> Inline -> m Doc +inlineToTEI _ (Str str) = return $ text $ escapeStringForXML str inlineToTEI opts (Emph lst) = - inTags False "hi" [("rendition","simple:italic")] $ inlinesToTEI opts lst + inTags False "hi" [("rendition","simple:italic")] <$> inlinesToTEI opts lst inlineToTEI opts (Strong lst) = - inTags False "hi" [("rendition", "simple:bold")] $ inlinesToTEI opts lst + inTags False "hi" [("rendition", "simple:bold")] <$> inlinesToTEI opts lst inlineToTEI opts (Strikeout lst) = - inTags False "hi" [("rendition", "simple:strikethrough")] $ + inTags False "hi" [("rendition", "simple:strikethrough")] <$> inlinesToTEI opts lst inlineToTEI opts (Superscript lst) = - inTags False "hi" [("rendition", "simple:superscript")] $ inlinesToTEI opts lst + inTags False "hi" [("rendition", "simple:superscript")] <$> + inlinesToTEI opts lst inlineToTEI opts (Subscript lst) = - inTags False "hi" [("rendition", "simple:subscript")] $ inlinesToTEI opts lst + inTags False "hi" [("rendition", "simple:subscript")] <$> + inlinesToTEI opts lst inlineToTEI opts (SmallCaps lst) = - inTags False "hi" [("rendition", "simple:smallcaps")] $ - inlinesToTEI opts lst + inTags False "hi" [("rendition", "simple:smallcaps")] <$> + inlinesToTEI opts lst inlineToTEI opts (Quoted _ lst) = - inTagsSimple "quote" $ inlinesToTEI opts lst + inTagsSimple "quote" <$> inlinesToTEI opts lst inlineToTEI opts (Cite _ lst) = inlinesToTEI opts lst inlineToTEI opts (Span _ ils) = inlinesToTEI opts ils -inlineToTEI _ (Code _ str) = +inlineToTEI _ (Code _ str) = return $ inTags False "seg" [("type","code")] $ text (escapeStringForXML str) -- Distinguish display from inline math by wrapping the former in a "figure." -inlineToTEI _ (Math t str) = +inlineToTEI _ (Math t str) = return $ case t of InlineMath -> inTags False "formula" [("notation","TeX")] $ text (str) DisplayMath -> inTags True "figure" [("type","math")] $ inTags False "formula" [("notation","TeX")] $ text (str) -inlineToTEI _ (RawInline f x) | f == "tei" = text x - | otherwise = empty -inlineToTEI _ LineBreak = selfClosingTag "lb" [] -inlineToTEI _ Space = space +inlineToTEI _ il@(RawInline f x) | f == "tei" = return $ text x + | otherwise = empty <$ + report (InlineNotRendered il) +inlineToTEI _ LineBreak = return $ selfClosingTag "lb" [] +inlineToTEI _ Space = return $ space -- because we use \n for LineBreak, we can't do soft breaks: -inlineToTEI _ SoftBreak = space +inlineToTEI _ SoftBreak = return $ space inlineToTEI opts (Link attr txt (src, _)) - | Just email <- stripPrefix "mailto:" src = + | Just email <- stripPrefix "mailto:" src = do let emailLink = text $ escapeStringForXML $ email - in case txt of - [Str s] | escapeURI s == email -> emailLink - _ -> inlinesToTEI opts txt <+> - char '(' <> emailLink <> char ')' + case txt of + [Str s] | escapeURI s == email -> return $ emailLink + _ -> do + linktext <- inlinesToTEI opts txt + return $ linktext <+> char '(' <> emailLink <> char ')' | otherwise = (if isPrefixOf "#" src then inTags False "ref" $ ("target", drop 1 src) : idAndRole attr - else inTags False "ref" $ ("target", src) : idAndRole attr ) $ + else inTags False "ref" $ ("target", src) : idAndRole attr ) <$> inlinesToTEI opts txt -inlineToTEI opts (Image attr description (src, tit)) = +inlineToTEI opts (Image attr description (src, tit)) = do let titleDoc = if null tit then empty - else inTags False "figDesc" [] (text $ escapeStringForXML tit) - imageDesc = if null description - then empty - else inTags False "head" [] (inlinesToTEI opts description) - in inTagsIndented "figure" $ imageDesc $$ - imageToTEI opts attr src $$ titleDoc + else inTags False "figDesc" [] + (text $ escapeStringForXML tit) + imageDesc <- if null description + then return empty + else inTags False "head" [] + <$> inlinesToTEI opts description + img <- imageToTEI opts attr src + return $ inTagsIndented "figure" $ imageDesc $$ img $$ titleDoc inlineToTEI opts (Note contents) = - inTagsIndented "note" $ blocksToTEI opts contents + inTagsIndented "note" <$> blocksToTEI opts contents idAndRole :: Attr -> [(String, String)] idAndRole (id',cls,_) = ident ++ role -- cgit v1.2.3 From db37b71d9a12bbd9370d68a472a0553f07661aec Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 13 Mar 2017 11:38:19 +0100 Subject: Highlighting: highlighting now returns an Either rather than Maybe. This allows us to display error information returned by the skylighting library. Display a warning if the highlighting library throws an error. --- src/Text/Pandoc/Highlighting.hs | 19 +++++++++---------- src/Text/Pandoc/Logging.hs | 6 ++++++ src/Text/Pandoc/Writers/Docx.hs | 10 +++++++--- src/Text/Pandoc/Writers/HTML.hs | 21 +++++++++++++-------- src/Text/Pandoc/Writers/LaTeX.hs | 13 +++++++++---- 5 files changed, 44 insertions(+), 25 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Highlighting.hs b/src/Text/Pandoc/Highlighting.hs index 80e6581b7..a4732cd02 100644 --- a/src/Text/Pandoc/Highlighting.hs +++ b/src/Text/Pandoc/Highlighting.hs @@ -79,7 +79,7 @@ languagesByExtension ext = highlight :: (FormatOptions -> [SourceLine] -> a) -- ^ Formatter -> Attr -- ^ Attributes of the CodeBlock -> String -- ^ Raw contents of the CodeBlock - -> Maybe a -- ^ Maybe the formatted result + -> Either String a highlight formatter (_, classes, keyvals) rawCode = let firstNum = fromMaybe 1 (safeRead (fromMaybe "1" $ lookup "startFrom" keyvals)) fmtOpts = defaultFormatOpts{ @@ -92,18 +92,17 @@ highlight formatter (_, classes, keyvals) rawCode = rawCode' = T.pack rawCode in case msum (map (\l -> lookupSyntax l defaultSyntaxMap) classes') of Nothing - | numberLines fmtOpts -> Just + | numberLines fmtOpts -> Right $ formatter fmtOpts{ codeClasses = [], containerClasses = classes' } - $ map (\ln -> [(NormalTok, ln)]) $ T.lines rawCode' - | otherwise -> Nothing + $ map (\ln -> [(NormalTok, ln)]) + $ T.lines rawCode' + | otherwise -> Left "" Just syntax -> - case tokenize tokenizeOpts syntax rawCode' of - Right slines -> Just $ - formatter fmtOpts{ codeClasses = - [T.toLower (sShortname syntax)], - containerClasses = classes' } slines - Left _ -> Nothing + (formatter fmtOpts{ codeClasses = + [T.toLower (sShortname syntax)], + containerClasses = classes' }) <$> + tokenize tokenizeOpts syntax rawCode' -- Functions for correlating latex listings package's language names -- with skylighting language names: diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs index 3d2cc2287..052f5d364 100644 --- a/src/Text/Pandoc/Logging.hs +++ b/src/Text/Pandoc/Logging.hs @@ -80,6 +80,7 @@ data LogMessage = | Fetching String | NoTitleElement String | NoLangSpecified + | CouldNotHighlight String deriving (Show, Eq, Data, Ord, Typeable, Generic) instance ToJSON LogMessage where @@ -164,6 +165,8 @@ instance ToJSON LogMessage where NoTitleElement fallback -> ["fallback" .= Text.pack fallback] NoLangSpecified -> [] + CouldNotHighlight msg -> + ["message" .= Text.pack msg] showPos :: SourcePos -> String showPos pos = sn ++ "line " ++ @@ -233,6 +236,8 @@ showLogMessage msg = NoLangSpecified -> "No value for 'lang' was specified in the metadata.\n" ++ "It is recommended that lang be specified for this format." + CouldNotHighlight msg -> + "Could not highlight code block:\n" ++ msg messageVerbosity:: LogMessage -> Verbosity messageVerbosity msg = @@ -259,3 +264,4 @@ messageVerbosity msg = Fetching{} -> INFO NoTitleElement{} -> WARNING NoLangSpecified -> INFO + CouldNotHighlight{} -> WARNING diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index c182d42a3..04daf3b4b 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -1156,9 +1156,13 @@ inlineToOpenXML' opts (Code attrs str) = do [ rCustomStyle (show toktype) ] , mknode "w:t" [("xml:space","preserve")] (T.unpack tok) ] withTextProp (rCustomStyle "VerbatimChar") - $ case writerHighlightStyle opts >> highlight formatOpenXML attrs str of - Just h -> return h - Nothing -> unhighlighted + $ if isNothing (writerHighlightStyle opts) + then unhighlighted + else case highlight formatOpenXML attrs str of + Right h -> return h + Left msg -> do + unless (null msg) $ report $ CouldNotHighlight msg + unhighlighted inlineToOpenXML' opts (Note bs) = do notes <- gets stFootnotes notenum <- (lift . lift) getUniqueId diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index fdf62dd56..10b782de7 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -644,11 +644,14 @@ blockToHtml opts (CodeBlock (id',classes,keyvals) rawCode) = do hlCode = if isJust (writerHighlightStyle opts) then highlight formatHtmlBlock (id',classes',keyvals) adjCode - else Nothing + else Left "" case hlCode of - Nothing -> return $ addAttrs opts (id',classes,keyvals) + Left msg -> do + unless (null msg) $ + report $ CouldNotHighlight msg + return $ addAttrs opts (id',classes,keyvals) $ H.pre $ H.code $ toHtml adjCode - Just h -> modify (\st -> st{ stHighlighting = True }) >> + Right h -> modify (\st -> st{ stHighlighting = True }) >> return (addAttrs opts (id',[],keyvals) h) blockToHtml opts (BlockQuote blocks) = do -- in S5, treat list in blockquote specially @@ -872,17 +875,19 @@ inlineToHtml opts inline = do (Emph lst) -> inlineListToHtml opts lst >>= return . H.em (Strong lst) -> inlineListToHtml opts lst >>= return . H.strong (Code attr str) -> case hlCode of - Nothing -> return - $ addAttrs opts attr - $ H.code $ strToHtml str - Just h -> do + Left msg -> do + unless (null msg) $ + report $ CouldNotHighlight msg + return $ addAttrs opts attr + $ H.code $ strToHtml str + Right h -> do modify $ \st -> st{ stHighlighting = True } return $ addAttrs opts (id',[],keyvals) h where (id',_,keyvals) = attr hlCode = if isJust (writerHighlightStyle opts) then highlight formatHtmlInline attr str - else Nothing + else Left "" (Strikeout lst) -> inlineListToHtml opts lst >>= return . H.del (SmallCaps lst) -> inlineListToHtml opts lst >>= diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 578c7017f..7e1970d01 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -540,8 +540,11 @@ blockToLaTeX (CodeBlock (identifier,classes,keyvalAttr) str) = do "\\end{lstlisting}") $$ cr let highlightedCodeBlock = case highlight formatLaTeXBlock ("",classes,keyvalAttr) str of - Nothing -> rawCodeBlock - Just h -> modify (\st -> st{ stHighlighting = True }) >> + Left msg -> do + unless (null msg) $ + report $ CouldNotHighlight msg + rawCodeBlock + Right h -> modify (\st -> st{ stHighlighting = True }) >> return (flush $ linkAnchor $$ text (T.unpack h)) case () of _ | isEnabled Ext_literate_haskell opts && "haskell" `elem` classes && @@ -958,8 +961,10 @@ inlineToLaTeX (Code (_,classes,_) str) = do return $ text $ "\\lstinline" ++ listingsopt ++ [chr] ++ str ++ [chr] highlightCode = do case highlight formatLaTeXInline ("",classes,[]) str of - Nothing -> rawCode - Just h -> modify (\st -> st{ stHighlighting = True }) >> + Left msg -> do + unless (null msg) $ report $ CouldNotHighlight msg + rawCode + Right h -> modify (\st -> st{ stHighlighting = True }) >> return (text (T.unpack h)) rawCode = liftM (text . (\s -> "\\texttt{" ++ escapeSpaces s ++ "}")) $ stringToLaTeX CodeString str -- cgit v1.2.3 From 4de4816b99f29ee3cb5c1f71e44e3fc1995efbd8 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 13 Mar 2017 21:32:58 +0100 Subject: RST writer: convert to PandocMonad, report on unrendered raw items. --- src/Text/Pandoc/Logging.hs | 4 +-- src/Text/Pandoc/Writers/RST.hs | 70 +++++++++++++++++++++++------------------- 2 files changed, 41 insertions(+), 33 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs index 052f5d364..59b010034 100644 --- a/src/Text/Pandoc/Logging.hs +++ b/src/Text/Pandoc/Logging.hs @@ -236,8 +236,8 @@ showLogMessage msg = NoLangSpecified -> "No value for 'lang' was specified in the metadata.\n" ++ "It is recommended that lang be specified for this format." - CouldNotHighlight msg -> - "Could not highlight code block:\n" ++ msg + CouldNotHighlight m -> + "Could not highlight code block:\n" ++ m messageVerbosity:: LogMessage -> Verbosity messageVerbosity msg = diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index 496350024..d4a537d72 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -37,7 +37,8 @@ import Data.List (intersperse, isPrefixOf, stripPrefix, transpose) import Data.Maybe (fromMaybe) import Network.URI (isURI) import qualified Text.Pandoc.Builder as B -import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Class (PandocMonad, report) +import Text.Pandoc.Logging import Text.Pandoc.Definition import Text.Pandoc.ImageSize import Text.Pandoc.Options @@ -58,17 +59,19 @@ data WriterState = , stTopLevel :: Bool } +type RST = StateT WriterState + -- | Convert Pandoc to RST. writeRST :: PandocMonad m => WriterOptions -> Pandoc -> m String -writeRST opts document = return $ +writeRST opts document = do let st = WriterState { stNotes = [], stLinks = [], stImages = [], stHasMath = False, stHasRawTeX = False, stOptions = opts, stTopLevel = True} - in evalState (pandocToRST document) st + evalStateT (pandocToRST document) st -- | Return RST representation of document. -pandocToRST :: Pandoc -> State WriterState String +pandocToRST :: PandocMonad m => Pandoc -> RST m String pandocToRST (Pandoc meta blocks) = do opts <- gets stOptions let colwidth = if writerWrapText opts == WrapAuto @@ -113,12 +116,11 @@ pandocToRST (Pandoc meta blocks) = do normalizeHeadings _ [] = [] -- | Return RST representation of reference key table. -refsToRST :: Refs -> State WriterState Doc +refsToRST :: PandocMonad m => Refs -> RST m Doc refsToRST refs = mapM keyToRST refs >>= return . vcat -- | Return RST representation of a reference key. -keyToRST :: ([Inline], (String, String)) - -> State WriterState Doc +keyToRST :: PandocMonad m => ([Inline], (String, String)) -> RST m Doc keyToRST (label, (src, _)) = do label' <- inlineListToRST label let label'' = if ':' `elem` ((render Nothing label') :: String) @@ -127,26 +129,28 @@ keyToRST (label, (src, _)) = do return $ nowrap $ ".. _" <> label'' <> ": " <> text src -- | Return RST representation of notes. -notesToRST :: [[Block]] -> State WriterState Doc +notesToRST :: PandocMonad m => [[Block]] -> RST m Doc notesToRST notes = mapM (\(num, note) -> noteToRST num note) (zip [1..] notes) >>= return . vsep -- | Return RST representation of a note. -noteToRST :: Int -> [Block] -> State WriterState Doc +noteToRST :: PandocMonad m => Int -> [Block] -> RST m Doc noteToRST num note = do contents <- blockListToRST note let marker = ".. [" <> text (show num) <> "]" return $ nowrap $ marker $$ nest 3 contents -- | Return RST representation of picture reference table. -pictRefsToRST :: [([Inline], (Attr, String, String, Maybe String))] - -> State WriterState Doc +pictRefsToRST :: PandocMonad m + => [([Inline], (Attr, String, String, Maybe String))] + -> RST m Doc pictRefsToRST refs = mapM pictToRST refs >>= return . vcat -- | Return RST representation of a picture substitution reference. -pictToRST :: ([Inline], (Attr, String, String, Maybe String)) - -> State WriterState Doc +pictToRST :: PandocMonad m + => ([Inline], (Attr, String, String, Maybe String)) + -> RST m Doc pictToRST (label, (attr, src, _, mbtarget)) = do label' <- inlineListToRST label dims <- imageDimsToRST attr @@ -178,7 +182,7 @@ escapeString opts (c:cs) = _ -> '.':escapeString opts cs _ -> c : escapeString opts cs -titleToRST :: [Inline] -> [Inline] -> State WriterState Doc +titleToRST :: PandocMonad m => [Inline] -> [Inline] -> RST m Doc titleToRST [] _ = return empty titleToRST tit subtit = do title <- inlineListToRST tit @@ -194,8 +198,9 @@ bordered contents c = border = text (replicate len c) -- | Convert Pandoc block element to RST. -blockToRST :: Block -- ^ Block element - -> State WriterState Doc +blockToRST :: PandocMonad m + => Block -- ^ Block element + -> RST m Doc blockToRST Null = return empty blockToRST (Div attr bs) = do contents <- blockListToRST bs @@ -323,22 +328,23 @@ blockToRST (DefinitionList items) = do return $ blankline $$ chomp (vcat contents) $$ blankline -- | Convert bullet list item (list of blocks) to RST. -bulletListItemToRST :: [Block] -> State WriterState Doc +bulletListItemToRST :: PandocMonad m => [Block] -> RST m Doc bulletListItemToRST items = do contents <- blockListToRST items return $ hang 3 "- " $ contents <> cr -- | Convert ordered list item (a list of blocks) to RST. -orderedListItemToRST :: String -- ^ marker for list item +orderedListItemToRST :: PandocMonad m + => String -- ^ marker for list item -> [Block] -- ^ list item (list of blocks) - -> State WriterState Doc + -> RST m Doc orderedListItemToRST marker items = do contents <- blockListToRST items let marker' = marker ++ " " return $ hang (length marker') (text marker') $ contents <> cr -- | Convert defintion list item (label, list of blocks) to RST. -definitionListItemToRST :: ([Inline], [[Block]]) -> State WriterState Doc +definitionListItemToRST :: PandocMonad m => ([Inline], [[Block]]) -> RST m Doc definitionListItemToRST (label, defs) = do label' <- inlineListToRST label contents <- liftM vcat $ mapM blockListToRST defs @@ -346,15 +352,16 @@ definitionListItemToRST (label, defs) = do return $ label' $$ nest tabstop (nestle contents <> cr) -- | Format a list of lines as line block. -linesToLineBlock :: [[Inline]] -> State WriterState Doc +linesToLineBlock :: PandocMonad m => [[Inline]] -> RST m Doc linesToLineBlock inlineLines = do lns <- mapM inlineListToRST inlineLines return $ (vcat $ map (hang 2 (text "| ")) lns) <> blankline -- | Convert list of Pandoc block elements to RST. -blockListToRST' :: Bool +blockListToRST' :: PandocMonad m + => Bool -> [Block] -- ^ List of block elements - -> State WriterState Doc + -> RST m Doc blockListToRST' topLevel blocks = do tl <- gets stTopLevel modify (\s->s{stTopLevel=topLevel}) @@ -362,12 +369,13 @@ blockListToRST' topLevel blocks = do modify (\s->s{stTopLevel=tl}) return res -blockListToRST :: [Block] -- ^ List of block elements - -> State WriterState Doc +blockListToRST :: PandocMonad m + => [Block] -- ^ List of block elements + -> RST m Doc blockListToRST = blockListToRST' False -- | Convert list of Pandoc inline elements to RST. -inlineListToRST :: [Inline] -> State WriterState Doc +inlineListToRST :: PandocMonad m => [Inline] -> RST m Doc inlineListToRST lst = mapM inlineToRST (removeSpaceAfterDisplayMath $ insertBS lst) >>= return . hcat @@ -427,7 +435,7 @@ inlineListToRST lst = isComplex _ = False -- | Convert Pandoc inline element to RST. -inlineToRST :: Inline -> State WriterState Doc +inlineToRST :: PandocMonad m => Inline -> RST m Doc inlineToRST (Span _ ils) = inlineListToRST ils inlineToRST (Emph lst) = do contents <- inlineListToRST lst @@ -477,12 +485,12 @@ inlineToRST (Math t str) = do then blankline $$ ".. math::" $$ blankline $$ nest 3 (text str) $$ blankline else blankline $$ (".. math:: " <> text str) $$ blankline -inlineToRST (RawInline f x) +inlineToRST il@(RawInline f x) | f == "rst" = return $ text x | f == "latex" || f == "tex" = do modify $ \st -> st{ stHasRawTeX = True } return $ ":raw-latex:`" <> text x <> "`" - | otherwise = return empty + | otherwise = empty <$ report (InlineNotRendered il) inlineToRST (LineBreak) = return cr -- there's no line break in RST (see Para) inlineToRST Space = return space inlineToRST SoftBreak = do @@ -527,7 +535,7 @@ inlineToRST (Note contents) = do let ref = show $ (length notes) + 1 return $ " [" <> text ref <> "]_" -registerImage :: Attr -> [Inline] -> Target -> Maybe String -> State WriterState Doc +registerImage :: PandocMonad m => Attr -> [Inline] -> Target -> Maybe String -> RST m Doc registerImage attr alt (src,tit) mbtarget = do pics <- gets stImages txt <- case lookup alt pics of @@ -542,7 +550,7 @@ registerImage attr alt (src,tit) mbtarget = do return alt' inlineListToRST txt -imageDimsToRST :: Attr -> State WriterState Doc +imageDimsToRST :: PandocMonad m => Attr -> RST m Doc imageDimsToRST attr = do let (ident, _, _) = attr name = if null ident -- cgit v1.2.3 From 6bf3f89d69f96f043f63600e199e53c8cfac680c Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 13 Mar 2017 22:11:10 +0100 Subject: Better handling of \part in LaTeX. Closes #1905. Removed stateChapters from ParserState. Now we parse chapters as level 0 headers, and parts as level -1 headers. After parsing, we check for the lowest header level, and if it's less than 1 we bump everything up so that 1 is the lowest header level. So `\part` will always produce a header; no command-line options are needed. --- src/Text/Pandoc/Parsing.hs | 2 -- src/Text/Pandoc/Readers/LaTeX.hs | 25 +++++++++++++++++-------- 2 files changed, 17 insertions(+), 10 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 3058185da..a84535875 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -928,7 +928,6 @@ data ParserState = ParserState stateIdentifiers :: Set.Set String, -- ^ Header identifiers used stateNextExample :: Int, -- ^ Number of next example stateExamples :: M.Map String Int, -- ^ Map from example labels to numbers - stateHasChapters :: Bool, -- ^ True if \chapter encountered stateMacros :: [Macro], -- ^ List of macros defined so far stateRstDefaultRole :: String, -- ^ Current rST default interpreted text role stateRstCustomRoles :: M.Map String (String, Maybe String, Attr), -- ^ Current rST custom text roles @@ -1036,7 +1035,6 @@ defaultParserState = stateIdentifiers = Set.empty, stateNextExample = 1, stateExamples = M.empty, - stateHasChapters = False, stateMacros = [], stateRstDefaultRole = "title-reference", stateRstCustomRoles = M.empty, diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 7018d2ce3..ae441a387 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -42,6 +42,7 @@ import Data.Char (chr, isAlphaNum, isLetter, ord) import Data.List (intercalate) import qualified Data.Map as M import Data.Maybe (fromMaybe, maybeToList) +import Safe (minimumDef) import System.FilePath (addExtension, replaceExtension, takeExtension) import Text.Pandoc.Builder import Text.Pandoc.Class (PandocMonad, PandocPure, lookupEnv, readFileFromDirs, @@ -72,7 +73,17 @@ parseLaTeX = do eof st <- getState let meta = stateMeta st - let (Pandoc _ bs') = doc bs + let doc' = doc bs + let headerLevel (Header n _ _) = [n] + headerLevel _ = [] + let bottomLevel = minimumDef 1 $ query headerLevel doc' + let adjustHeaders m (Header n attr ils) = Header (n+m) attr ils + adjustHeaders _ x = x + let (Pandoc _ bs') = + -- handle the case where you have \part or \chapter + (if bottomLevel < 1 + then walk (adjustHeaders (1 - bottomLevel)) + else id) doc' return $ Pandoc meta bs' type LP m = ParserT String ParserState m @@ -345,10 +356,10 @@ blockCommands = M.fromList $ -- Koma-script metadata commands , ("dedication", mempty <$ (skipopts *> tok >>= addMeta "dedication")) -- sectioning - , ("chapter", updateState (\s -> s{ stateHasChapters = True }) - *> section nullAttr 0) - , ("chapter*", updateState (\s -> s{ stateHasChapters = True }) - *> section ("",["unnumbered"],[]) 0) + , ("part", section nullAttr (-1)) + , ("part*", section nullAttr (-1)) + , ("chapter", section nullAttr 0) + , ("chapter*", section ("",["unnumbered"],[]) 0) , ("section", section nullAttr 1) , ("section*", section ("",["unnumbered"],[]) 1) , ("subsection", section nullAttr 2) @@ -444,13 +455,11 @@ authors = try $ do 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 skipopts contents <- grouped inline lab <- option ident $ try (spaces' >> controlSeq "label" >> spaces' >> braced) attr' <- registerHeader (lab, classes, kvs) contents - return $ headerWith attr' lvl' contents + return $ headerWith attr' lvl contents inlineCommand :: PandocMonad m => LP m Inlines inlineCommand = try $ do -- cgit v1.2.3 From 0b4ae3af662587a69e6893b7f6c347d90912c48f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 13 Mar 2017 22:21:50 +0100 Subject: Removed unused symbol. --- src/Text/Pandoc/Writers/TEI.hs | 1 - 1 file changed, 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/TEI.hs b/src/Text/Pandoc/Writers/TEI.hs index d6d8d60b7..0e1a0526d 100644 --- a/src/Text/Pandoc/Writers/TEI.hs +++ b/src/Text/Pandoc/Writers/TEI.hs @@ -125,7 +125,6 @@ deflistItemsToTEI opts items = deflistItemToTEI :: PandocMonad m => WriterOptions -> [Inline] -> [[Block]] -> m Doc deflistItemToTEI opts term defs = do - let def' = concatMap (map plainToPara) defs term' <- inlinesToTEI opts term defs' <- blocksToTEI opts $ concatMap (map plainToPara) defs return $ inTagsIndented "label" term' $$ -- cgit v1.2.3 From 482e5b78a05a02df512a495a2a67657879a2d436 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 15 Mar 2017 17:19:28 +0100 Subject: OpenDocument writer: use more widely available bullet characters. The old characters weren't available in some font sets. These seem to work well on Windows and Linux versions of LibreOffice. Closes #1400. --- src/Text/Pandoc/Writers/OpenDocument.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index 961bb981a..3432d258a 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -503,7 +503,7 @@ bulletListStyle l = do , ("style:num-suffix", "." ) , ("text:bullet-char", [bulletList !! i] ) ] (listLevelStyle (1 + i)) - bulletList = map chr $ cycle [8226,8227,8259] + bulletList = map chr $ cycle [8226,9702,9642] listElStyle = map doStyles [0..9] pn <- paraListStyle l return (pn, (l, listElStyle)) -- cgit v1.2.3 From 38c3a683468428fc26485f3346bdbbb4eb7d6ef2 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 16 Mar 2017 10:15:14 +0100 Subject: LaTeX/Beamer writer: allow hyperlinks to frames. Previously you could link to a header above or below slide level but not TO slide level. This commit changes that. Hypertargets are inserted inside frame titles; technically the reference is to just after the title, but in normal use (where slides are viewed full screen in a slide show), this does not matter. Closes #3220. --- src/Text/Pandoc/Writers/LaTeX.hs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 7e1970d01..44c00df24 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -390,10 +390,19 @@ elementToBeamer slideLevel (Sec lvl _num (ident,classes,kvs) tit elts) let options = if null optionslist then "" else "[" ++ intercalate "," optionslist ++ "]" - let slideStart = Para $ RawInline "latex" ("\\begin{frame}" ++ options) : - if tit == [Str "\0"] -- marker for hrule - then [] - else (RawInline "latex" "{") : tit ++ [RawInline "latex" "}"] + let latex = RawInline (Format "latex") + slideTitle <- + if tit == [Str "\0"] -- marker for hrule + then return [] + else + if null ident + then return $ latex "{" : tit ++ [latex "}"] + else do + ref <- toLabel ident + return $ latex ("{%\n\\protect\\hypertarget{" ++ + ref ++ "}{%\n") : tit ++ [latex "}}"] + let slideStart = Para $ + RawInline "latex" ("\\begin{frame}" ++ options) : slideTitle let slideEnd = RawBlock "latex" "\\end{frame}" -- now carve up slide into blocks if there are sections inside bs <- concat `fmap` mapM (elementToBeamer slideLevel) elts -- cgit v1.2.3 From 2fe806e9ac54a6733222df9369c890b92df92ff4 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 16 Mar 2017 21:45:50 +0100 Subject: Added --abbreviations=FILE option for custom abbreviations file. --- src/Text/Pandoc/App.hs | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index cdc0c52fc..9c2e076c5 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -46,6 +46,7 @@ import Data.Aeson (eitherDecode', encode) import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as B import Data.Char (toLower, toUpper) +import qualified Data.Set as Set import Data.Foldable (foldrM) import Data.List (intercalate, isPrefixOf, isSuffixOf, sort) import qualified Data.Map as M @@ -279,6 +280,11 @@ convertWithOpts opts = do uriFragment = "" } _ -> Nothing + abbrevs <- case optAbbreviations opts of + Nothing -> return $ readerAbbreviations def + Just f -> (Set.fromList . filter (not . null) . lines) + <$> UTF8.readFile f + let readerOpts = def{ readerStandalone = standalone , readerColumns = optColumns opts , readerTabStop = optTabStop opts @@ -287,6 +293,7 @@ convertWithOpts opts = do , readerDefaultImageExtension = optDefaultImageExtension opts , readerTrackChanges = optTrackChanges opts + , readerAbbreviations = abbrevs } highlightStyle <- lookupHighlightStyle $ optHighlightStyle opts @@ -489,6 +496,7 @@ data Opt = Opt , optHighlightStyle :: Maybe String -- ^ Style to use for highlighted code , optTopLevelDivision :: TopLevelDivision -- ^ Type of the top-level divisions , optHTMLMathMethod :: HTMLMathMethod -- ^ Method to print HTML math + , optAbbreviations :: Maybe FilePath -- ^ Path to abbrevs file , optReferenceDoc :: Maybe FilePath -- ^ Path of reference doc , optEpubMetadata :: Maybe FilePath -- ^ EPUB metadata , optEpubFonts :: [FilePath] -- ^ EPUB fonts to embed @@ -554,6 +562,7 @@ defaultOpts = Opt , optHighlightStyle = Just "pygments" , optTopLevelDivision = TopLevelDefault , optHTMLMathMethod = PlainMath + , optAbbreviations = Nothing , optReferenceDoc = Nothing , optEpubMetadata = Nothing , optEpubFonts = [] @@ -772,7 +781,7 @@ options = , Option "o" ["output"] (ReqArg (\arg opt -> return opt { optOutputFile = arg }) - "FILENAME") + "FILE") "" -- "Name of output file" , Option "" ["data-dir"] @@ -855,7 +864,7 @@ options = (\arg opt -> return opt{ optTemplate = Just arg, optStandalone = True }) - "FILENAME") + "FILE") "" -- "Use custom template" , Option "M" ["metadata"] @@ -954,7 +963,7 @@ options = (\arg opt -> return opt{ optIncludeInHeader = arg : optIncludeInHeader opt, optStandalone = True }) - "FILENAME") + "FILE") "" -- "File to include at end of header (implies -s)" , Option "B" ["include-before-body"] @@ -962,7 +971,7 @@ options = (\arg opt -> return opt{ optIncludeBeforeBody = arg : optIncludeBeforeBody opt, optStandalone = True }) - "FILENAME") + "FILE") "" -- "File to include before document body" , Option "A" ["include-after-body"] @@ -970,7 +979,7 @@ options = (\arg opt -> return opt{ optIncludeAfterBody = arg : optIncludeAfterBody opt, optStandalone = True }) - "FILENAME") + "FILE") "" -- "File to include after document body" , Option "" ["self-contained"] @@ -1110,7 +1119,7 @@ options = (ReqArg (\arg opt -> return opt { optReferenceDoc = Just arg }) - "FILENAME") + "FILE") "" -- "Path of custom reference doc" , Option "" ["epub-cover-image"] @@ -1118,13 +1127,13 @@ options = (\arg opt -> return opt { optVariables = ("epub-cover-image", arg) : optVariables opt }) - "FILENAME") + "FILE") "" -- "Path of epub cover image" , Option "" ["epub-metadata"] (ReqArg (\arg opt -> return opt { optEpubMetadata = Just arg }) - "FILENAME") + "FILE") "" -- "Path of epub metadata file" , Option "" ["epub-embed-font"] @@ -1261,6 +1270,12 @@ options = (\opt -> return opt { optHTMLMathMethod = GladTeX })) "" -- "Use gladtex for HTML math" + , Option "" ["abbreviations"] + (ReqArg + (\arg opt -> return opt { optAbbreviations = Just arg }) + "FILE") + "" -- "Specify file for custom abbreviations" + , Option "" ["trace"] (NoArg (\opt -> return opt { optVerbosity = DEBUG })) -- cgit v1.2.3 From c93d069d49fcf724fc59405b82587d608724c2c6 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 16 Mar 2017 22:16:41 +0100 Subject: Add default abbreviations file (data/abbreviations). This contains a list of strings that will be recognized by pandoc's Markdown parser as abbreviations. (A nonbreaking space will be inserted after the period, preventing a sentence space in formats like LaTeX.) Users can override the default by putting a file abbreviations in their user data directory (`~/.pandoc` on *nix). --- src/Text/Pandoc/App.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 9c2e076c5..34eadb6e0 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -280,10 +280,10 @@ convertWithOpts opts = do uriFragment = "" } _ -> Nothing - abbrevs <- case optAbbreviations opts of - Nothing -> return $ readerAbbreviations def - Just f -> (Set.fromList . filter (not . null) . lines) - <$> UTF8.readFile f + abbrevs <- (Set.fromList . filter (not . null) . lines) <$> + case optAbbreviations opts of + Nothing -> readDataFileUTF8 datadir "abbreviations" + Just f -> UTF8.readFile f let readerOpts = def{ readerStandalone = standalone , readerColumns = optColumns opts -- cgit v1.2.3 From 87f99f3fdf0c372c2e5ded0112ff432aa0d7571f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 18 Mar 2017 22:43:57 +0100 Subject: HTML reader: Better sanity checks on raw HTML. This also affects the Markdown reader. Closes #3257. --- src/Text/Pandoc/Readers/HTML.hs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 0af369469..5251962f2 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -54,7 +54,7 @@ import Text.Pandoc.Walk import qualified Data.Map as M import Data.Maybe ( fromMaybe, isJust) import Data.List ( intercalate, isInfixOf, isPrefixOf ) -import Data.Char ( isDigit ) +import Data.Char ( isDigit, isLetter, isAlphaNum ) import Control.Monad ( guard, mzero, void, unless ) import Control.Arrow ((***)) import Control.Applicative ( (<|>) ) @@ -1032,13 +1032,22 @@ htmlTag f = try $ do let (next : _) = canonicalizeTags $ parseTagsOptions parseOptions{ optTagWarning = False } inp guard $ f next + + -- <www.boe.es/buscar/act.php?id=BOE-A-1996-8930#a66> + -- should NOT be parsed as an HTML tag, see #2277, + -- so we exclude . even though it's a valid character + -- in XML elemnet names + let isNameChar c = isAlphaNum c || c == ':' || c == '-' || c == '_' + let isName s = case s of + [] -> False + (c:cs) -> isLetter c && all isNameChar cs + let handleTag tagname = do - -- <www.boe.es/buscar/act.php?id=BOE-A-1996-8930#a66> - -- should NOT be parsed as an HTML tag, see #2277 - guard $ not ('.' `elem` tagname) + -- basic sanity check, since the parser is very forgiving + -- and finds tags in stuff like x<y) + guard $ isName tagname -- <https://example.org> should NOT be a tag either. -- tagsoup will parse it as TagOpen "https:" [("example.org","")] - guard $ not (null tagname) guard $ last tagname /= ':' rendered <- manyTill anyChar (char '>') return (next, rendered ++ ">") @@ -1050,7 +1059,9 @@ htmlTag f = try $ do char '>' return (next, "<!--" ++ s ++ "-->") | otherwise -> fail "bogus comment mode, HTML5 parse error" - TagOpen tagname _attr -> handleTag tagname + TagOpen tagname attr -> do + guard $ all (isName . fst) attr + handleTag tagname TagClose tagname -> handleTag tagname _ -> mzero -- cgit v1.2.3 From 34412cf57c904f5729f96553ed9481869dde7358 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 19 Mar 2017 21:55:38 +0100 Subject: RST reader: recurse into bodies of unknown directives. In most cases it's better to preserve the content than to emit it. This isn't guaranteed to have good results; it will fail spectacularly for unknown raw or verbatim directives. See #3432. --- src/Text/Pandoc/Readers/RST.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index f27b02f25..7564998ff 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -755,7 +755,8 @@ directive' = do other -> do pos <- getPosition logMessage $ SkippedContent (".. " ++ other) pos - return mempty + bod <- parseFromString parseBlocks $ top ++ "\n\n" ++ body' + return $ B.divWith ("",[other],[]) bod tableDirective :: PandocMonad m => String -> [(String, String)] -> String -> RSTParser m Blocks -- cgit v1.2.3 From b010a8c5e7ba4969100fe078f0f9a1a6cdaf7c5c Mon Sep 17 00:00:00 2001 From: Mauro Bieg <mb21@users.noreply.github.com> Date: Mon, 20 Mar 2017 10:06:24 +0100 Subject: docx writer: lang meta, see #1667 (#3515) --- src/Text/Pandoc/Writers/Docx.hs | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 04daf3b4b..5e4fe7731 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -72,6 +72,7 @@ import Text.Pandoc.Writers.Shared (fixDisplayMath) import Text.Printf (printf) import Text.TeXMath import Text.XML.Light as XML +import Text.XML.Light.Cursor as XMLC data ListMarker = NoMarker | BulletMarker @@ -256,8 +257,30 @@ writeDocx opts doc@(Pandoc meta _) = do ) -- styles + let lang = case lookupMeta "lang" meta of + Just (MetaInlines [Str s]) -> Just s + Just (MetaString s) -> Just s + _ -> Nothing + let addLang :: Element -> Element + addLang e = case lang >>= \l -> (return . XMLC.toTree . go l . XMLC.fromElement) e of + Just (Elem e') -> e' + _ -> e -- return original + where go :: String -> Cursor -> Cursor + go l cursor = case XMLC.findRec (isLangElt . current) cursor of + Nothing -> cursor + Just t -> XMLC.modifyContent (setval l) t + setval :: String -> Content -> Content + setval l (Elem e') = Elem $ e'{ elAttribs = map (setvalattr l) $ + elAttribs e' } + setval _ x = x + setvalattr :: String -> XML.Attr -> XML.Attr + setvalattr l (XML.Attr qn@(QName "val" _ _) _) = XML.Attr qn l + setvalattr _ x = x + isLangElt (Elem e') = qName (elName e') == "lang" + isLangElt _ = False + let stylepath = "word/styles.xml" - styledoc <- parseXml refArchive distArchive stylepath + styledoc <- addLang <$> parseXml refArchive distArchive stylepath -- parse styledoc for heading styles let styleMaps = getStyleMaps styledoc -- cgit v1.2.3 From f2f6851713674545e2f303b95589cbaff8e6a6b9 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert+github@zeitkraut.de> Date: Mon, 20 Mar 2017 15:17:03 +0100 Subject: Lua filters (#3514) * Add `--lua-filter` option. This works like `--filter` but takes pathnames of special lua filters and uses the lua interpreter baked into pandoc, so that no external interpreter is needed. Note that lua filters are all applied after regular filters, regardless of their position on the command line. * Add Text.Pandoc.Lua, exporting `runLuaFilter`. Add `pandoc.lua` to data files. * Add private module Text.Pandoc.Lua.PandocModule to supply the default lua module. * Add Tests.Lua to tests. * Add data/pandoc.lua, the lua module pandoc imports when processing its lua filters. * Document in MANUAL.txt. --- src/Text/Pandoc/App.hs | 16 +++ src/Text/Pandoc/Lua.hs | 226 ++++++++++++++++++++++++++++++++++++ src/Text/Pandoc/Lua/PandocModule.hs | 47 ++++++++ 3 files changed, 289 insertions(+) create mode 100644 src/Text/Pandoc/Lua.hs create mode 100644 src/Text/Pandoc/Lua/PandocModule.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 34eadb6e0..d555f6f5f 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -69,6 +69,7 @@ import Text.Pandoc import Text.Pandoc.Builder (setMeta) import Text.Pandoc.Class (PandocIO, getLog, withMediaBag) import Text.Pandoc.Highlighting (highlightingStyles) +import Text.Pandoc.Lua ( runLuaFilter ) import Text.Pandoc.MediaBag (MediaBag, extractMediaBag, mediaDirectory) import Text.Pandoc.PDF (makePDF) import Text.Pandoc.Process (pipeProcess) @@ -389,6 +390,7 @@ convertWithOpts opts = do doc' <- (maybe return (extractMedia media) (optExtractMedia opts) >=> return . flip (foldr addMetadata) (optMetadata opts) >=> applyTransforms transforms >=> + applyLuaFilters datadir (optLuaFilters opts) [format] >=> applyFilters datadir filters' [format]) doc case writer of @@ -514,6 +516,7 @@ data Opt = Opt , optWrapText :: WrapOption -- ^ Options for wrapping text , optColumns :: Int -- ^ Line length in characters , optFilters :: [FilePath] -- ^ Filters to apply + , optLuaFilters :: [FilePath] -- ^ Lua filters to apply , optEmailObfuscation :: ObfuscationMethod , optIdentifierPrefix :: String , optIndentedCodeClasses :: [String] -- ^ Default classes for indented code blocks @@ -580,6 +583,7 @@ defaultOpts = Opt , optWrapText = WrapAuto , optColumns = 72 , optFilters = [] + , optLuaFilters = [] , optEmailObfuscation = NoObfuscation , optIdentifierPrefix = "" , optIndentedCodeClasses = [] @@ -725,6 +729,12 @@ expandFilterPath mbDatadir fp = liftIO $ do else return fp _ -> return fp +applyLuaFilters :: MonadIO m + => Maybe FilePath -> [FilePath] -> [String] -> Pandoc -> m Pandoc +applyLuaFilters mbDatadir filters args d = do + expandedFilters <- mapM (expandFilterPath mbDatadir) filters + foldrM ($) d $ map (flip runLuaFilter args) expandedFilters + applyFilters :: MonadIO m => Maybe FilePath -> [FilePath] -> [String] -> Pandoc -> m Pandoc applyFilters mbDatadir filters args d = do @@ -814,6 +824,12 @@ options = "PROGRAM") "" -- "External JSON filter" + , Option "" ["lua-filter"] + (ReqArg + (\arg opt -> return opt { optLuaFilters = arg : optLuaFilters opt }) + "SCRIPTPATH") + "" -- "Lua filter" + , Option "p" ["preserve-tabs"] (NoArg (\opt -> return opt { optPreserveTabs = True })) diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs new file mode 100644 index 000000000..6fa6b2020 --- /dev/null +++ b/src/Text/Pandoc/Lua.hs @@ -0,0 +1,226 @@ +{- +Copyright © 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> + +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 +-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} +{- | + Module : Text.Pandoc.Lua + Copyright : Copyright © 2017 Albert Krewinkel + License : GNU GPL, version 2 or above + + Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> + Stability : alpha + +Pandoc lua utils. +-} +module Text.Pandoc.Lua ( runLuaFilter ) where + +import Control.Monad ( (>=>), when ) +import Control.Monad.Trans ( MonadIO(..) ) +import Data.Aeson ( FromJSON(..), ToJSON(..), Result(..), Value, fromJSON ) +import Data.HashMap.Lazy ( HashMap ) +import Data.Text ( Text, pack, unpack ) +import Data.Text.Encoding ( decodeUtf8 ) +import Scripting.Lua ( LuaState, StackValue(..) ) +import Scripting.Lua.Aeson () +import Text.Pandoc.Definition ( Block(..), Inline(..), Pandoc(..) ) +import Text.Pandoc.Lua.PandocModule +import Text.Pandoc.Walk + +import qualified Data.HashMap.Lazy as HashMap +import qualified Scripting.Lua as Lua +import qualified Scripting.Lua as LuaAeson + +runLuaFilter :: (MonadIO m) + => FilePath -> [String] -> Pandoc -> m Pandoc +runLuaFilter filterPath args pd = liftIO $ do + lua <- LuaAeson.newstate + Lua.openlibs lua + Lua.newtable lua + Lua.setglobal lua "PANDOC_FILTER_FUNCTIONS" -- hack, store functions here + pushPandocModule lua + Lua.setglobal lua "pandoc" + status <- Lua.loadfile lua filterPath + if (status /= 0) + then do + luaErrMsg <- unpack . decodeUtf8 <$> Lua.tostring lua 1 + error luaErrMsg + else do + Lua.call lua 0 1 + Just luaFilters <- Lua.peek lua (-1) + Lua.push lua (map pack args) + Lua.setglobal lua "PandocParameters" + doc <- runAll luaFilters >=> luaFilter lua "filter_doc" $ pd + Lua.close lua + return doc + +runAll :: [LuaFilter] -> Pandoc -> IO Pandoc +runAll [] = return +runAll (x:xs) = walkMWithLuaFilter x >=> runAll xs + +luaFilter :: Lua.LuaState -> String -> Pandoc -> IO Pandoc +luaFilter lua luaFn x = do + fnExists <- isLuaFunction lua luaFn + if fnExists + then walkM (Lua.callfunc lua luaFn :: Pandoc -> IO Pandoc) x + else return x + +walkMWithLuaFilter :: LuaFilter -> Pandoc -> IO Pandoc +walkMWithLuaFilter (LuaFilter lua inlineFnMap blockFnMap docFnMap) = + walkM (execInlineLuaFilter lua inlineFnMap) >=> + walkM (execBlockLuaFilter lua blockFnMap) >=> + walkM (execDocLuaFilter lua docFnMap) + +type InlineFunctionMap = HashMap Text (LuaFilterFunction Inline) +type BlockFunctionMap = HashMap Text (LuaFilterFunction Block) +type DocFunctionMap = HashMap Text (LuaFilterFunction Pandoc) +data LuaFilter = + LuaFilter LuaState InlineFunctionMap BlockFunctionMap DocFunctionMap + +newtype LuaFilterFunction a = LuaFilterFunction { functionIndex :: Int } + +execDocLuaFilter :: LuaState + -> HashMap Text (LuaFilterFunction Pandoc) + -> Pandoc -> IO Pandoc +execDocLuaFilter lua fnMap x = do + let docFnName = "Doc" + case HashMap.lookup docFnName fnMap of + Nothing -> return x + Just fn -> runLuaFilterFunction lua fn x + +execBlockLuaFilter :: LuaState + -> HashMap Text (LuaFilterFunction Block) + -> Block -> IO Block +execBlockLuaFilter lua fnMap x = do + let filterOrId constr = case HashMap.lookup constr fnMap of + Nothing -> return x + Just fn -> runLuaFilterFunction lua fn x + case x of + Plain _ -> filterOrId "Plain" + Para _ -> filterOrId "Para" + LineBlock _ -> filterOrId "LineBlock" + CodeBlock _ _ -> filterOrId "CodeBlock" + RawBlock _ _ -> filterOrId "RawBlock" + BlockQuote _ -> filterOrId "BlockQuote" + OrderedList _ _ -> filterOrId "OrderedList" + BulletList _ -> filterOrId "BulletList" + DefinitionList _ -> filterOrId "DefinitionList" + Header _ _ _ -> filterOrId "Header" + HorizontalRule -> filterOrId "HorizontalRule" + Table _ _ _ _ _ -> filterOrId "Table" + Div _ _ -> filterOrId "Div" + Null -> filterOrId "Null" + +execInlineLuaFilter :: LuaState + -> HashMap Text (LuaFilterFunction Inline) + -> Inline -> IO Inline +execInlineLuaFilter lua fnMap x = do + let filterOrId constr = case HashMap.lookup constr fnMap of + Nothing -> return x + Just fn -> runLuaFilterFunction lua fn x + case x of + Cite _ _ -> filterOrId "Cite" + Code _ _ -> filterOrId "Code" + Emph _ -> filterOrId "Emph" + Image _ _ _ -> filterOrId "Image" + LineBreak -> filterOrId "LineBreak" + Link _ _ _ -> filterOrId "Link" + Math _ _ -> filterOrId "Math" + Note _ -> filterOrId "Note" + Quoted _ _ -> filterOrId "Quoted" + RawInline _ _ -> filterOrId "RawInline" + SmallCaps _ -> filterOrId "SmallCaps" + SoftBreak -> filterOrId "SoftBreak" + Space -> filterOrId "Space" + Span _ _ -> filterOrId "Span" + Str _ -> filterOrId "Str" + Strikeout _ -> filterOrId "Strikeout" + Strong _ -> filterOrId "Strong" + Subscript _ -> filterOrId "Subscript" + Superscript _ -> filterOrId "Superscript" + +instance StackValue LuaFilter where + valuetype _ = Lua.TTABLE + push = undefined + peek lua i = do + -- TODO: find a more efficient way of doing this in a typesafe manner. + inlineFnMap <- Lua.peek lua i + blockFnMap <- Lua.peek lua i + docFnMap <- Lua.peek lua i + return $ LuaFilter lua <$> inlineFnMap <*> blockFnMap <*> docFnMap + +runLuaFilterFunction :: (StackValue a) + => LuaState -> LuaFilterFunction a -> a -> IO a +runLuaFilterFunction lua lf inline = do + pushFilterFunction lua lf + Lua.push lua inline + Lua.call lua 1 1 + Just res <- Lua.peek lua (-1) + Lua.pop lua 1 + return res + +-- FIXME: use registry +pushFilterFunction :: Lua.LuaState -> LuaFilterFunction a -> IO () +pushFilterFunction lua lf = do + Lua.getglobal lua "PANDOC_FILTER_FUNCTIONS" + Lua.rawgeti lua (-1) (functionIndex lf) + Lua.remove lua (-2) -- remove global from stack + +instance StackValue (LuaFilterFunction a) where + valuetype _ = Lua.TFUNCTION + push lua v = pushFilterFunction lua v + peek lua i = do + isFn <- Lua.isfunction lua i + when (not isFn) (error $ "Not a function at index " ++ (show i)) + Lua.pushvalue lua i + Lua.getglobal lua "PANDOC_FILTER_FUNCTIONS" + len <- Lua.objlen lua (-1) + Lua.insert lua (-2) + Lua.rawseti lua (-2) (len + 1) + Lua.pop lua 1 + return . Just $ LuaFilterFunction (len + 1) + + +isLuaFunction :: Lua.LuaState -> String -> IO Bool +isLuaFunction lua fnName = do + Lua.getglobal lua fnName + res <- Lua.isfunction lua (-1) + Lua.pop lua (-1) + return res + +maybeFromJson :: (FromJSON a) => Maybe Value -> Maybe a +maybeFromJson mv = fromJSON <$> mv >>= \case + Success x -> Just x + _ -> Nothing + +instance StackValue Pandoc where + push lua = Lua.push lua . toJSON + peek lua i = maybeFromJson <$> peek lua i + valuetype _ = Lua.TTABLE + +instance StackValue Block where + push lua = Lua.push lua . toJSON + peek lua i = maybeFromJson <$> peek lua i + valuetype _ = Lua.TTABLE + +instance StackValue Inline where + push lua = Lua.push lua . toJSON + peek lua i = maybeFromJson <$> peek lua i + valuetype _ = Lua.TTABLE diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs new file mode 100644 index 000000000..5b2e82103 --- /dev/null +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -0,0 +1,47 @@ +{- +Copyright © 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> + +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.Lua.PandocModule + Copyright : Copyright © 2017 Albert Krewinkel + License : GNU GPL, version 2 or above + + Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> + Stability : alpha + +Pandoc module for lua. +-} +module Text.Pandoc.Lua.PandocModule ( pushPandocModule ) where + +import Data.ByteString.Char8 ( unpack ) +import Scripting.Lua ( LuaState, loadstring, call) +import Text.Pandoc.Shared ( readDataFile ) + + +-- | Push the "pandoc" on the lua stack. +pushPandocModule :: LuaState -> IO () +pushPandocModule lua = do + script <- pandocModuleScript + status <- loadstring lua script "cn" + if (status /= 0) + then return () + else do + call lua 0 1 + +-- | Get the string representation of the pandoc module +pandocModuleScript :: IO String +pandocModuleScript = unpack <$> readDataFile Nothing "pandoc.lua" -- cgit v1.2.3 From 48c88d566d19683a7d5b63f88c8b4487234e3712 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 20 Mar 2017 21:51:29 +0100 Subject: Add `space_in_atx_header` extension. This is enabled by default in pandoc and GitHub markdown but not the other flavors. This requirse a space between the opening #'s and the header text in ATX headers (as CommonMark does but many other implementations do not). This is desirable to avoid falsely capturing things ilke #hashtag or #5 Closes #3512. --- src/Text/Pandoc/Extensions.hs | 3 +++ src/Text/Pandoc/Readers/Markdown.hs | 1 + 2 files changed, 4 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index b543d489f..54f38f4a0 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -112,6 +112,7 @@ data Extension = | 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_space_in_atx_header -- ^ Require space between # and header text | Ext_strikeout -- ^ Strikeout using ~~this~~ syntax | Ext_superscript -- ^ Superscript using ^this^ syntax | Ext_subscript -- ^ Subscript using ~this~ syntax @@ -168,6 +169,7 @@ pandocExtensions = extensionsFromList , Ext_intraword_underscores , Ext_blank_before_blockquote , Ext_blank_before_header + , Ext_space_in_atx_header , Ext_strikeout , Ext_superscript , Ext_subscript @@ -223,6 +225,7 @@ githubMarkdownExtensions = extensionsFromList , Ext_ascii_identifiers , Ext_backtick_code_blocks , Ext_autolink_bare_uris + , Ext_space_in_atx_header , Ext_intraword_underscores , Ext_strikeout , Ext_hard_line_breaks diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 169872391..0cc10c1d4 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -534,6 +534,7 @@ atxHeader = try $ do level <- atxChar >>= many1 . char >>= return . length notFollowedBy $ guardEnabled Ext_fancy_lists >> (char '.' <|> char ')') -- this would be a list + guardDisabled Ext_space_in_atx_header <|> notFollowedBy nonspaceChar skipSpaces (text, raw) <- withRaw $ trimInlinesF . mconcat <$> many (notFollowedBy atxClosing >> inline) -- cgit v1.2.3 From e7336b1feb4c5282b15b0e369539a34984362b40 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 21 Mar 2017 10:02:30 +0100 Subject: Moved gridTable from Markdown writer to Writers.Shared. --- src/Text/Pandoc/Writers/Haddock.hs | 2 +- src/Text/Pandoc/Writers/Markdown.hs | 41 ----------------------------------- src/Text/Pandoc/Writers/Shared.hs | 43 ++++++++++++++++++++++++++++++++++++- 3 files changed, 43 insertions(+), 43 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Haddock.hs b/src/Text/Pandoc/Writers/Haddock.hs index 7f7d89a43..e573704e7 100644 --- a/src/Text/Pandoc/Writers/Haddock.hs +++ b/src/Text/Pandoc/Writers/Haddock.hs @@ -45,7 +45,7 @@ import Text.Pandoc.Pretty import Text.Pandoc.Shared import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Writers.Math (texMathToInlines) -import Text.Pandoc.Writers.Shared +import Text.Pandoc.Writers.Shared hiding (gridTable) 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 8b58d5beb..d3d7abfd0 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -705,47 +705,6 @@ pandocTable opts headless aligns widths rawHeaders rawRows = do else border return $ head'' $$ underline $$ body $$ bottom -gridTable :: PandocMonad m => Bool -> [Alignment] -> [Int] - -> [Doc] -> [[Doc]] -> MD m Doc -gridTable headless aligns widthsInChars headers' rawRows = do - let hpipeBlocks blocks = hcat [beg, middle, end] - where h = maximum (1 : map height blocks) - sep' = lblock 3 $ vcat (map text $ replicate h " | ") - beg = lblock 2 $ vcat (map text $ replicate h "| ") - end = lblock 2 $ vcat (map text $ replicate h " |") - middle = chomp $ hcat $ intersperse sep' blocks - let makeRow = hpipeBlocks . zipWith lblock widthsInChars - let head' = makeRow headers' - let rows' = map (makeRow . map chomp) rawRows - let borderpart ch align widthInChars = - let widthInChars' = if widthInChars < 1 then 1 else widthInChars - in (if (align == AlignLeft || align == AlignCenter) - then char ':' - else char ch) <> - text (replicate widthInChars' ch) <> - (if (align == AlignRight || align == AlignCenter) - then char ':' - else char ch) - let border ch aligns' widthsInChars' = - char '+' <> - hcat (intersperse (char '+') (zipWith (borderpart ch) - aligns' widthsInChars')) <> char '+' - let body = vcat $ intersperse (border '-' (repeat AlignDefault) widthsInChars) - rows' - let head'' = if headless - then empty - else head' $$ border '=' aligns widthsInChars - if headless - then return $ - border '-' aligns widthsInChars $$ - body $$ - border '-' (repeat AlignDefault) widthsInChars - else return $ - border '-' (repeat AlignDefault) widthsInChars $$ - head'' $$ - body $$ - border '-' (repeat AlignDefault) widthsInChars - itemEndsWithTightList :: [Block] -> Bool itemEndsWithTightList bs = case bs of diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index 34bfa0b64..e2853a9cb 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -39,13 +39,14 @@ module Text.Pandoc.Writers.Shared ( , tagWithAttrs , fixDisplayMath , unsmartify + , gridTable ) where import Control.Monad (liftM) import Data.Aeson (FromJSON (..), Result (..), ToJSON (..), Value (Object), encode, fromJSON) import qualified Data.HashMap.Strict as H -import Data.List (groupBy) +import Data.List (groupBy, intersperse) import qualified Data.Map as M import Data.Maybe (isJust) import qualified Data.Text as T @@ -216,3 +217,43 @@ unsmartify opts ('\8212':xs) unsmartify opts (x:xs) = x : unsmartify opts xs unsmartify _ [] = [] +gridTable :: Monad m => Bool -> [Alignment] -> [Int] + -> [Doc] -> [[Doc]] -> m Doc +gridTable headless aligns widthsInChars headers' rawRows = do + let hpipeBlocks blocks = hcat [beg, middle, end] + where h = maximum (1 : map height blocks) + sep' = lblock 3 $ vcat (map text $ replicate h " | ") + beg = lblock 2 $ vcat (map text $ replicate h "| ") + end = lblock 2 $ vcat (map text $ replicate h " |") + middle = chomp $ hcat $ intersperse sep' blocks + let makeRow = hpipeBlocks . zipWith lblock widthsInChars + let head' = makeRow headers' + let rows' = map (makeRow . map chomp) rawRows + let borderpart ch align widthInChars = + let widthInChars' = if widthInChars < 1 then 1 else widthInChars + in (if (align == AlignLeft || align == AlignCenter) + then char ':' + else char ch) <> + text (replicate widthInChars' ch) <> + (if (align == AlignRight || align == AlignCenter) + then char ':' + else char ch) + let border ch aligns' widthsInChars' = + char '+' <> + hcat (intersperse (char '+') (zipWith (borderpart ch) + aligns' widthsInChars')) <> char '+' + let body = vcat $ intersperse (border '-' (repeat AlignDefault) widthsInChars) + rows' + let head'' = if headless + then empty + else head' $$ border '=' aligns widthsInChars + if headless + then return $ + border '-' aligns widthsInChars $$ + body $$ + border '-' (repeat AlignDefault) widthsInChars + else return $ + border '-' (repeat AlignDefault) widthsInChars $$ + head'' $$ + body $$ + border '-' (repeat AlignDefault) widthsInChars -- cgit v1.2.3 From e6cdf21fa5d68409f362bd89cc56090d34983cb3 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 21 Mar 2017 10:16:11 +0100 Subject: Moved more gridTable calculations to Writers.Shared. --- src/Text/Pandoc/Writers/Markdown.hs | 27 +++------------------------ src/Text/Pandoc/Writers/Shared.hs | 35 ++++++++++++++++++++++++++++++----- 2 files changed, 33 insertions(+), 29 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index d3d7abfd0..3a431fb02 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -565,30 +565,9 @@ blockToMarkdown' opts t@(Table caption aligns widths headers rows) = do pandocTable opts (all null headers) aligns' widths' rawHeaders rawRows | isEnabled Ext_grid_tables opts && - writerColumns opts >= 8 * numcols -> do - let widths'' = if all (==0) widths' - then replicate numcols - (1.0 / fromIntegral numcols) - else widths' - let widthsInChars = map ((\x -> x - 3) . floor . - (fromIntegral (writerColumns opts) *)) widths'' - rawHeaders' <- zipWithM - blockListToMarkdown - (map (\w -> opts{writerColumns = - min (w - 2) (writerColumns opts)}) - widthsInChars) - headers - rawRows' <- mapM - (\cs -> zipWithM - blockListToMarkdown - (map (\w -> opts{writerColumns = - min (w - 2) (writerColumns opts)}) - widthsInChars) - cs) - rows - fmap (id,) $ - gridTable (all null headers) aligns' widthsInChars - rawHeaders' rawRows' + writerColumns opts >= 8 * numcols -> (id,) <$> + gridTable opts blockListToMarkdown + (all null headers) aligns' widths' headers rows | isEnabled Ext_raw_html opts -> fmap (id,) $ text <$> (writeHtml5String def $ Pandoc nullMeta [t]) diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index e2853a9cb..520df1037 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -42,7 +42,7 @@ module Text.Pandoc.Writers.Shared ( , gridTable ) where -import Control.Monad (liftM) +import Control.Monad (liftM, zipWithM) import Data.Aeson (FromJSON (..), Result (..), ToJSON (..), Value (Object), encode, fromJSON) import qualified Data.HashMap.Strict as H @@ -217,9 +217,34 @@ unsmartify opts ('\8212':xs) unsmartify opts (x:xs) = x : unsmartify opts xs unsmartify _ [] = [] -gridTable :: Monad m => Bool -> [Alignment] -> [Int] - -> [Doc] -> [[Doc]] -> m Doc -gridTable headless aligns widthsInChars headers' rawRows = do +gridTable :: Monad m + => WriterOptions + -> (WriterOptions -> [Block] -> m Doc) + -> Bool -- ^ headless + -> [Alignment] + -> [Double] + -> [[Block]] + -> [[[Block]]] + -> m Doc +gridTable opts blocksToDoc headless aligns widths headers rows = do + let numcols = maximum (length aligns : length widths : + map length (headers:rows)) + let widths' = if all (==0) widths + then replicate numcols + (1.0 / fromIntegral numcols) + else widths + let widthsInChars = map ((\x -> x - 3) . floor . + (fromIntegral (writerColumns opts) *)) widths' + rawHeaders <- zipWithM blocksToDoc + (map (\w -> opts{writerColumns = + min (w - 2) (writerColumns opts)}) widthsInChars) + headers + rawRows <- mapM + (\cs -> zipWithM blocksToDoc + (map (\w -> opts{writerColumns = + min (w - 2) (writerColumns opts)}) widthsInChars) + cs) + rows let hpipeBlocks blocks = hcat [beg, middle, end] where h = maximum (1 : map height blocks) sep' = lblock 3 $ vcat (map text $ replicate h " | ") @@ -227,7 +252,7 @@ gridTable headless aligns widthsInChars headers' rawRows = do end = lblock 2 $ vcat (map text $ replicate h " |") middle = chomp $ hcat $ intersperse sep' blocks let makeRow = hpipeBlocks . zipWith lblock widthsInChars - let head' = makeRow headers' + let head' = makeRow rawHeaders let rows' = map (makeRow . map chomp) rawRows let borderpart ch align widthInChars = let widthInChars' = if widthInChars < 1 then 1 else widthInChars -- cgit v1.2.3 From d3798a044db281d2217c2d64ab1c5380d1df7a70 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 21 Mar 2017 10:20:18 +0100 Subject: Reuse Writers.Shared.gridTable in Haddock writer. --- src/Text/Pandoc/Writers/Haddock.hs | 34 +++------------------------------- 1 file changed, 3 insertions(+), 31 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Haddock.hs b/src/Text/Pandoc/Writers/Haddock.hs index e573704e7..eae1377cd 100644 --- a/src/Text/Pandoc/Writers/Haddock.hs +++ b/src/Text/Pandoc/Writers/Haddock.hs @@ -45,7 +45,7 @@ import Text.Pandoc.Pretty import Text.Pandoc.Shared import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Writers.Math (texMathToInlines) -import Text.Pandoc.Writers.Shared hiding (gridTable) +import Text.Pandoc.Writers.Shared type Notes = [[Block]] data WriterState = WriterState { stNotes :: Notes } @@ -157,8 +157,8 @@ blockToHaddock opts (Table caption aligns widths headers rows) = do pandocTable opts (all null headers) aligns widths rawHeaders rawRows | otherwise -> fmap (id,) $ - gridTable opts (all null headers) aligns widths - rawHeaders rawRows + gridTable opts blockListToHaddock + (all null headers) aligns widths headers rows return $ (prefixed "> " $ nst $ tbl $$ blankline $$ caption'') $$ blankline blockToHaddock opts (BulletList items) = do contents <- mapM (bulletListItemToHaddock opts) items @@ -217,34 +217,6 @@ pandocTable opts headless aligns widths rawHeaders rawRows = do else border return $ head'' $$ underline $$ body $$ bottom -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 - then replicate numcols (1.0 / fromIntegral numcols) - else widths - let widthsInChars = map (floor . (fromIntegral (writerColumns opts) *)) widths' - let hpipeBlocks blocks = hcat [beg, middle, end] - where h = maximum (map height blocks) - sep' = lblock 3 $ vcat (map text $ replicate h " | ") - beg = lblock 2 $ vcat (map text $ replicate h "| ") - end = lblock 2 $ vcat (map text $ replicate h " |") - middle = chomp $ hcat $ intersperse sep' blocks - let makeRow = hpipeBlocks . zipWith lblock widthsInChars - let head' = makeRow headers' - let rows' = map (makeRow . map chomp) rawRows - let border ch = char '+' <> char ch <> - (hcat $ intersperse (char ch <> char '+' <> char ch) $ - map (\l -> text $ replicate l ch) widthsInChars) <> - char ch <> char '+' - let body = vcat $ intersperse (border '-') rows' - let head'' = if headless - then empty - else head' $$ border '=' - return $ border '-' $$ head'' $$ body $$ border '-' - -- | Convert bullet list item (list of blocks) to haddock bulletListItemToHaddock :: PandocMonad m => WriterOptions -> [Block] -> StateT WriterState m Doc -- cgit v1.2.3 From daf8d1db18efcfbac31afd6a2323411b93ce1b62 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 21 Mar 2017 14:16:46 +0100 Subject: RST writer: improve grid table output, fix bug with empty rows. Uses the new gridTable in Writers.Shared, which is here improved to better handle 0-width cells. Closes #3516. --- src/Text/Pandoc/Writers/RST.hs | 43 ++++++++++-------------------------- src/Text/Pandoc/Writers/Shared.hs | 46 ++++++++++++++++++++++++--------------- 2 files changed, 40 insertions(+), 49 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index d4a537d72..24898d62e 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -33,7 +33,7 @@ reStructuredText: <http://docutils.sourceforge.net/rst.html> module Text.Pandoc.Writers.RST ( writeRST ) where import Control.Monad.State import Data.Char (isSpace, toLower) -import Data.List (intersperse, isPrefixOf, stripPrefix, transpose) +import Data.List (isPrefixOf, stripPrefix) import Data.Maybe (fromMaybe) import Network.URI (isURI) import qualified Text.Pandoc.Builder as B @@ -269,39 +269,18 @@ blockToRST (BlockQuote blocks) = do tabstop <- gets $ writerTabStop . stOptions contents <- blockListToRST blocks return $ (nest tabstop contents) <> blankline -blockToRST (Table caption _ widths headers rows) = do +blockToRST (Table caption aligns widths headers rows) = do caption' <- inlineListToRST caption - headers' <- mapM blockListToRST headers - rawRows <- mapM (mapM blockListToRST) rows - -- let isSimpleCell [Plain _] = True - -- isSimpleCell [Para _] = True - -- isSimpleCell [] = True - -- isSimpleCell _ = False - -- let isSimple = all (==0) widths && all (all isSimpleCell) rows - let numChars = maximum . map offset + let blocksToDoc opts bs = do + oldOpts <- gets stOptions + modify $ \st -> st{ stOptions = opts } + result <- blockListToRST bs + modify $ \st -> st{ stOptions = oldOpts } + return result opts <- gets stOptions - let widthsInChars = - if all (== 0) widths - then map ((+2) . numChars) $ transpose (headers' : rawRows) - else map (floor . (fromIntegral (writerColumns opts) *)) widths - let hpipeBlocks blocks = hcat [beg, middle, end] - where h = height (hcat blocks) - sep' = lblock 3 $ vcat (map text $ replicate h " | ") - beg = lblock 2 $ vcat (map text $ replicate h "| ") - end = lblock 2 $ vcat (map text $ replicate h " |") - middle = hcat $ intersperse sep' blocks - let makeRow = hpipeBlocks . zipWith lblock widthsInChars - let head' = makeRow headers' - let rows' = map makeRow rawRows - let border ch = char '+' <> char ch <> - (hcat $ intersperse (char ch <> char '+' <> char ch) $ - map (\l -> text $ replicate l ch) widthsInChars) <> - char ch <> char '+' - let body = vcat $ intersperse (border '-') rows' - let head'' = if all null headers - then empty - else head' $$ border '=' - let tbl = border '-' $$ head'' $$ body $$ border '-' + tbl <- gridTable opts blocksToDoc (all null headers) + (map (const AlignDefault) aligns) widths + headers rows return $ if null caption then tbl $$ blankline else (".. table:: " <> caption') $$ blankline $$ nest 3 tbl $$ diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index 520df1037..3b28c58c8 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -46,7 +46,7 @@ import Control.Monad (liftM, zipWithM) import Data.Aeson (FromJSON (..), Result (..), ToJSON (..), Value (Object), encode, fromJSON) import qualified Data.HashMap.Strict as H -import Data.List (groupBy, intersperse) +import Data.List (groupBy, intersperse, transpose) import qualified Data.Map as M import Data.Maybe (isJust) import qualified Data.Text as T @@ -229,22 +229,34 @@ gridTable :: Monad m gridTable opts blocksToDoc headless aligns widths headers rows = do let numcols = maximum (length aligns : length widths : map length (headers:rows)) - let widths' = if all (==0) widths - then replicate numcols - (1.0 / fromIntegral numcols) - else widths - let widthsInChars = map ((\x -> x - 3) . floor . - (fromIntegral (writerColumns opts) *)) widths' - rawHeaders <- zipWithM blocksToDoc - (map (\w -> opts{writerColumns = - min (w - 2) (writerColumns opts)}) widthsInChars) - headers - rawRows <- mapM - (\cs -> zipWithM blocksToDoc - (map (\w -> opts{writerColumns = - min (w - 2) (writerColumns opts)}) widthsInChars) - cs) - rows + let handleGivenWidths widths' = do + let widthsInChars' = map ((\x -> x - 3) . floor . + (fromIntegral (writerColumns opts) *)) widths' + rawHeaders' <- zipWithM blocksToDoc + (map (\w -> opts{writerColumns = + min (w - 2) (writerColumns opts)}) widthsInChars') + headers + rawRows' <- mapM + (\cs -> zipWithM blocksToDoc + (map (\w -> opts{writerColumns = + min (w - 2) (writerColumns opts)}) widthsInChars') + cs) + rows + return (widthsInChars', rawHeaders', rawRows') + let handleZeroWidths = do + rawHeaders' <- mapM (blocksToDoc opts) headers + rawRows' <- mapM (mapM (blocksToDoc opts)) rows + let numChars = maximum . map offset + let widthsInChars' = + map ((+2) . numChars) $ transpose (rawHeaders' : rawRows') + if sum widthsInChars' > writerColumns opts + then -- use even widths + handleGivenWidths + (replicate numcols (1.0 / fromIntegral numcols) :: [Double]) + else return (widthsInChars', rawHeaders', rawRows') + (widthsInChars, rawHeaders, rawRows) <- if all (== 0) widths + then handleZeroWidths + else handleGivenWidths widths let hpipeBlocks blocks = hcat [beg, middle, end] where h = maximum (1 : map height blocks) sep' = lblock 3 $ vcat (map text $ replicate h " | ") -- cgit v1.2.3 From 430e2db9baa222dbf87ea664ec2d995640817b70 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 21 Mar 2017 14:39:21 +0100 Subject: Improve rendering of superscript in plain output. We now handle a few non digit characters (+, -, =, parentheses) for which there are superscripted unicode characters. Closes #3518. --- src/Text/Pandoc/Writers/Markdown.hs | 42 +++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 13 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 3a431fb02..c1a02e609 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -915,14 +915,8 @@ inlineToMarkdown opts (Superscript lst) = then "^" <> contents <> "^" else if isEnabled Ext_raw_html opts then "<sup>" <> contents <> "</sup>" - else case (render Nothing contents) of - ds | all (\d -> d >= '0' && d <= '9') ds - -> text (map toSuperscript ds) - _ -> contents - where toSuperscript '1' = '\x00B9' - toSuperscript '2' = '\x00B2' - toSuperscript '3' = '\x00B3' - toSuperscript c = chr (0x2070 + (ord c - 48)) + else text $ map toSuperscript + $ render Nothing contents inlineToMarkdown opts (Subscript lst) = local (\env -> env {envEscapeSpaces = True}) $ do contents <- inlineListToMarkdown opts lst @@ -930,11 +924,8 @@ inlineToMarkdown opts (Subscript lst) = then "~" <> contents <> "~" else if isEnabled Ext_raw_html opts then "<sub>" <> contents <> "</sub>" - else case (render Nothing contents) of - ds | all (\d -> d >= '0' && d <= '9') ds - -> text (map toSubscript ds) - _ -> contents - where toSubscript c = chr (0x2080 + (ord c - 48)) + else text $ map toSubscript + $ render Nothing contents inlineToMarkdown opts (SmallCaps lst) = do plain <- asks envPlain if not plain && @@ -1129,3 +1120,28 @@ makeMathPlainer = walk go go (Emph xs) = Span nullAttr xs go x = x +toSuperscript :: Char -> Char +toSuperscript '1' = '\x00B9' +toSuperscript '2' = '\x00B2' +toSuperscript '3' = '\x00B3' +toSuperscript '+' = '\x207A' +toSuperscript '-' = '\x207B' +toSuperscript '=' = '\x207C' +toSuperscript '(' = '\x207D' +toSuperscript ')' = '\x207E' +toSuperscript c + | c >= '0' && c <= '9' = + chr (0x2070 + (ord c - 48)) + | otherwise = c + +toSubscript :: Char -> Char +toSubscript '+' = '\x208A' +toSubscript '-' = '\x208B' +toSubscript '=' = '\x208C' +toSubscript '(' = '\x208D' +toSubscript ')' = '\x208E' +toSubscript c + | c >= '0' && c <= '9' = + chr (0x2080 + (ord c - 48)) + | otherwise = c + -- cgit v1.2.3 From cf306f34e5e23a1416b598766ca73d63d7367283 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 21 Mar 2017 15:41:58 +0100 Subject: Plain writer: use _(..) or ^(..) for super/subscript... ...unless unicode super/subscripted characters are available. --- src/Text/Pandoc/Writers/Markdown.hs | 55 +++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 24 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index c1a02e609..88dd53808 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -915,8 +915,11 @@ inlineToMarkdown opts (Superscript lst) = then "^" <> contents <> "^" else if isEnabled Ext_raw_html opts then "<sup>" <> contents <> "</sup>" - else text $ map toSuperscript - $ render Nothing contents + else + let rendered = render Nothing contents + in case mapM toSuperscript rendered of + Just r -> text r + Nothing -> text $ "^(" ++ rendered ++ ")" inlineToMarkdown opts (Subscript lst) = local (\env -> env {envEscapeSpaces = True}) $ do contents <- inlineListToMarkdown opts lst @@ -924,8 +927,11 @@ inlineToMarkdown opts (Subscript lst) = then "~" <> contents <> "~" else if isEnabled Ext_raw_html opts then "<sub>" <> contents <> "</sub>" - else text $ map toSubscript - $ render Nothing contents + else + let rendered = render Nothing contents + in case mapM toSubscript rendered of + Just r -> text r + Nothing -> text $ "_(" ++ rendered ++ ")" inlineToMarkdown opts (SmallCaps lst) = do plain <- asks envPlain if not plain && @@ -1120,28 +1126,29 @@ makeMathPlainer = walk go go (Emph xs) = Span nullAttr xs go x = x -toSuperscript :: Char -> Char -toSuperscript '1' = '\x00B9' -toSuperscript '2' = '\x00B2' -toSuperscript '3' = '\x00B3' -toSuperscript '+' = '\x207A' -toSuperscript '-' = '\x207B' -toSuperscript '=' = '\x207C' -toSuperscript '(' = '\x207D' -toSuperscript ')' = '\x207E' +toSuperscript :: Char -> Maybe Char +toSuperscript '1' = Just '\x00B9' +toSuperscript '2' = Just '\x00B2' +toSuperscript '3' = Just '\x00B3' +toSuperscript '+' = Just '\x207A' +toSuperscript '-' = Just '\x207B' +toSuperscript '=' = Just '\x207C' +toSuperscript '(' = Just '\x207D' +toSuperscript ')' = Just '\x207E' toSuperscript c | c >= '0' && c <= '9' = - chr (0x2070 + (ord c - 48)) - | otherwise = c + Just $ chr (0x2070 + (ord c - 48)) + | isSpace c = Just c + | otherwise = Nothing -toSubscript :: Char -> Char -toSubscript '+' = '\x208A' -toSubscript '-' = '\x208B' -toSubscript '=' = '\x208C' -toSubscript '(' = '\x208D' -toSubscript ')' = '\x208E' +toSubscript :: Char -> Maybe Char +toSubscript '+' = Just '\x208A' +toSubscript '-' = Just '\x208B' +toSubscript '=' = Just '\x208C' +toSubscript '(' = Just '\x208D' +toSubscript ')' = Just '\x208E' toSubscript c | c >= '0' && c <= '9' = - chr (0x2080 + (ord c - 48)) - | otherwise = c - + Just $ chr (0x2080 + (ord c - 48)) + | isSpace c = Just c + | otherwise = Nothing -- cgit v1.2.3 From c59e31722471ce3a67a41413de4e6a5ecfd00ba1 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 22 Mar 2017 15:37:08 +0100 Subject: Experimental change to charWidth in Pretty. Hunch that this might help with #3526. --- src/Text/Pandoc/Pretty.hs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Pretty.hs b/src/Text/Pandoc/Pretty.hs index 32e60843c..73d698001 100644 --- a/src/Text/Pandoc/Pretty.hs +++ b/src/Text/Pandoc/Pretty.hs @@ -511,6 +511,7 @@ doubleQuotes = inside (char '"') (char '"') -- | Returns width of a character in a monospace font: 0 for a combining -- character, 1 for a regular character, 2 for an East Asian wide character. charWidth :: Char -> Int +charWidth '\r' = 0 charWidth c = case c of _ | c < '\x0300' -> 1 -- cgit v1.2.3 From 9437ab449c8f8bf50f4c8a7f08156d2c1aea604a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 22 Mar 2017 16:08:09 +0100 Subject: Revert "Experimental change to charWidth in Pretty." This reverts commit c59e31722471ce3a67a41413de4e6a5ecfd00ba1. --- src/Text/Pandoc/Pretty.hs | 1 - 1 file changed, 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Pretty.hs b/src/Text/Pandoc/Pretty.hs index 73d698001..32e60843c 100644 --- a/src/Text/Pandoc/Pretty.hs +++ b/src/Text/Pandoc/Pretty.hs @@ -511,7 +511,6 @@ doubleQuotes = inside (char '"') (char '"') -- | Returns width of a character in a monospace font: 0 for a combining -- character, 1 for a regular character, 2 for an East Asian wide character. charWidth :: Char -> Int -charWidth '\r' = 0 charWidth c = case c of _ | c < '\x0300' -> 1 -- cgit v1.2.3 From f4ac0edf2a5087f3f988147726813a6240288945 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 22 Mar 2017 21:18:55 +0100 Subject: Markdown reader: allow latex macro definitions indented 1-3 spaces. Previously they only worked if nonindented. --- src/Text/Pandoc/Readers/Markdown.hs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 0cc10c1d4..5f08afe08 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -498,7 +498,7 @@ block = do , htmlBlock , table , codeBlockIndented - , guardEnabled Ext_latex_macros *> (macro >>= return . return) + , latexMacro , rawTeXBlock , lineBlock , blockQuote @@ -1071,6 +1071,13 @@ rawVerbatimBlock = htmlInBalanced isVerbTag isVerbTag (TagOpen "script" _) = True isVerbTag _ = False +latexMacro :: PandocMonad m => MarkdownParser m (F Blocks) +latexMacro = try $ do + guardEnabled Ext_latex_macros + skipNonindentSpaces + res <- macro + return $ return res + rawTeXBlock :: PandocMonad m => MarkdownParser m (F Blocks) rawTeXBlock = do guardEnabled Ext_raw_tex -- cgit v1.2.3 From 6c204ea2bd9b9526af3b60485d3787da6b7bf8ac Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 15 Mar 2017 21:04:14 +0100 Subject: Initial addition of groff ms writer. * New module: Text.Pandoc.Writers.Ms. * New template: default.ms. * The writer uses texmath's new eqn writer to convert math to eqn format, so a ms file produced with this writer should be processed with `groff -ms -e` if it contains math. --- src/Text/Pandoc.hs | 3 + src/Text/Pandoc/App.hs | 1 + src/Text/Pandoc/Data.hsb | 1 + src/Text/Pandoc/Writers/Ms.hs | 426 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 431 insertions(+) create mode 100644 src/Text/Pandoc/Writers/Ms.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index 1577491df..e77bc6d45 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -120,6 +120,7 @@ module Text.Pandoc , writeOPML , writeOpenDocument , writeMan + , writeMs , writeMediaWiki , writeDokuWiki , writeZimWiki @@ -190,6 +191,7 @@ import Text.Pandoc.Writers.HTML import Text.Pandoc.Writers.ICML import Text.Pandoc.Writers.LaTeX import Text.Pandoc.Writers.Man +import Text.Pandoc.Writers.Ms import Text.Pandoc.Writers.Markdown import Text.Pandoc.Writers.MediaWiki import Text.Pandoc.Writers.Muse @@ -292,6 +294,7 @@ writers = [ ,("context" , StringWriter writeConTeXt) ,("texinfo" , StringWriter writeTexinfo) ,("man" , StringWriter writeMan) + ,("ms" , StringWriter writeMs) ,("markdown" , StringWriter writeMarkdown) ,("markdown_strict" , StringWriter writeMarkdown) ,("markdown_phpextra" , StringWriter writeMarkdown) diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index d555f6f5f..b9cd04631 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -691,6 +691,7 @@ defaultWriterName x = ".icml" -> "icml" ".tei.xml" -> "tei" ".tei" -> "tei" + ".ms" -> "ms" ['.',y] | y `elem` ['1'..'9'] -> "man" _ -> "html" diff --git a/src/Text/Pandoc/Data.hsb b/src/Text/Pandoc/Data.hsb index 8786647c5..02c109816 100644 --- a/src/Text/Pandoc/Data.hsb +++ b/src/Text/Pandoc/Data.hsb @@ -13,3 +13,4 @@ dataFiles = map (\(fp, contents) -> dataFiles' :: [(FilePath, B.ByteString)] dataFiles' = ("MANUAL.txt", %blob "MANUAL.txt") : %blobs "data" + diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs new file mode 100644 index 000000000..194475304 --- /dev/null +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -0,0 +1,426 @@ +{- +Copyright (C) 2007-2015 John MacFarlane <jgm@berkeley.edu> + +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.Writers.Ms + Copyright : Copyright (C) 2007-2015 John MacFarlane + License : GNU GPL, version 2 or above + + Maintainer : John MacFarlane <jgm@berkeley.edu> + Stability : alpha + Portability : portable + +Conversion of 'Pandoc' documents to groff man page format. + +TODO: + +[ ] warning for non-rendered raw content +[ ] is there a better way to do strikeout? +[ ] strong + em doesn't seem to work +[ ] super + subscript don't seem to work +[ ] options for hyperlink rendering (currently footnote) +[ ] avoid note-in-note (which we currently get easily with + links in footnotes) +[ ] can we get prettier output using .B, etc. instead of + the inline forms? +[ ] tight/loose list distinction +[ ] internal hyperlinks (this seems to be possible since + they exist in the groff manual PDF version) +[ ] use a custom macro for defn lists so they're configurable +[ ] better handling of accented characters and other non-ascii + characters (e.g. curly quotes) -- we shouldn't rely on a + utf8 compatible groff +[ ] avoid blank line after footnote marker when footnote has a + paragraph +[ ] add via groff option to PDF module +[ ] better handling of autolinks? +[ ] better handling of images, perhaps converting to eps when + going to PDF? +[ ] better template, with configurable page number, table of contents, + columns, etc. +-} + +module Text.Pandoc.Writers.Ms ( writeMs ) where +import Text.Pandoc.Definition +import Text.Pandoc.Templates +import Text.Pandoc.Shared +import Text.Pandoc.Writers.Shared +import Text.Pandoc.Options +import Text.Pandoc.Writers.Math +import Text.Printf ( printf ) +import Data.List ( stripPrefix, intersperse, intercalate ) +import Data.Maybe (fromMaybe) +import Text.Pandoc.Pretty +import Text.Pandoc.Builder (deleteMeta) +import Text.Pandoc.Class (PandocMonad) +import Control.Monad.State +import Data.Char ( isDigit ) +import Text.TeXMath (writeEqn) + +data WriterState = WriterState { stHasInlineMath :: Bool + , stNotes :: [Note] } +type Note = [Block] + +type MS = StateT WriterState + +-- | Convert Pandoc to Ms. +writeMs :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeMs opts document = + evalStateT (pandocToMs opts document) (WriterState False []) + +-- | Return groff man representation of document. +pandocToMs :: PandocMonad m => WriterOptions -> Pandoc -> MS m String +pandocToMs opts (Pandoc meta blocks) = do + let colwidth = if writerWrapText opts == WrapAuto + then Just $ writerColumns opts + else Nothing + let render' = render colwidth + titleText <- inlineListToMs' opts $ docTitle meta + let title' = render' titleText + let setFieldsFromTitle = + case break (== ' ') title' of + (cmdName, rest) -> case reverse cmdName of + (')':d:'(':xs) | isDigit d -> + defField "title" (reverse xs) . + defField "section" [d] . + case splitBy (=='|') rest of + (ft:hds) -> + defField "footer" (trim ft) . + defField "header" + (trim $ concat hds) + [] -> id + _ -> defField "title" title' + metadata <- metaToJSON opts + (fmap (render colwidth) . blockListToMs opts) + (fmap (render colwidth) . inlineListToMs' opts) + $ deleteMeta "title" meta + body <- blockListToMs opts blocks + let main = render' body + hasInlineMath <- gets stHasInlineMath + let context = defField "body" main + $ setFieldsFromTitle + $ defField "has-inline-math" hasInlineMath + $ defField "hyphenate" True + $ defField "pandoc-version" pandocVersion + $ metadata + case writerTemplate opts of + Nothing -> return main + Just tpl -> return $ renderTemplate' tpl context + +-- | Association list of characters to escape. +manEscapes :: [(Char, String)] +manEscapes = [ ('\160', "\\ ") + , ('\'', "\\[aq]") + , ('’', "'") + , ('\x2014', "\\[em]") + , ('\x2013', "\\[en]") + , ('\x2026', "\\&...") + , ('|', "\\[u007C]") -- because we use | for inline math + ] ++ backslashEscapes "-@\\" + +-- | Escape | character, used to mark inline math, inside math. +escapeBar :: String -> String +escapeBar = concatMap go + where go '|' = "\\[u007C]" + go c = [c] + +-- | Escape special characters for Ms. +escapeString :: String -> String +escapeString = escapeStringUsing manEscapes + +-- | Escape a literal (code) section for Ms. +escapeCode :: String -> String +escapeCode = concat . intersperse "\n" . map escapeLine . lines where + escapeLine codeline = + case escapeStringUsing (manEscapes ++ backslashEscapes "\t ") codeline of + a@('.':_) -> "\\&" ++ a + b -> b + +-- We split inline lists into sentences, and print one sentence per +-- line. groff/troff treats the line-ending period differently. +-- See http://code.google.com/p/pandoc/issues/detail?id=148. + +-- | Returns the first sentence in a list of inlines, and the rest. +breakSentence :: [Inline] -> ([Inline], [Inline]) +breakSentence [] = ([],[]) +breakSentence xs = + let isSentenceEndInline (Str ys@(_:_)) | last ys == '.' = True + isSentenceEndInline (Str ys@(_:_)) | last ys == '?' = True + isSentenceEndInline (LineBreak) = True + isSentenceEndInline _ = False + (as, bs) = break isSentenceEndInline xs + in case bs of + [] -> (as, []) + [c] -> (as ++ [c], []) + (c:Space:cs) -> (as ++ [c], cs) + (c:SoftBreak:cs) -> (as ++ [c], cs) + (Str ".":Str (')':ys):cs) -> (as ++ [Str ".", Str (')':ys)], cs) + (x@(Str ('.':')':_)):cs) -> (as ++ [x], cs) + (LineBreak:x@(Str ('.':_)):cs) -> (as ++[LineBreak], x:cs) + (c:cs) -> (as ++ [c] ++ ds, es) + where (ds, es) = breakSentence cs + +-- | Split a list of inlines into sentences. +splitSentences :: [Inline] -> [[Inline]] +splitSentences xs = + let (sent, rest) = breakSentence xs + in if null rest then [sent] else sent : splitSentences rest + +blockToMs :: PandocMonad m + => WriterOptions -- ^ Options + -> Block -- ^ Block element + -> MS m Doc +blockToMs _ Null = return empty +blockToMs opts (Div _ bs) = blockListToMs opts bs +blockToMs opts (Plain inlines) = + liftM vcat $ mapM (inlineListToMs' opts) $ splitSentences inlines +blockToMs opts (Para inlines) = do + contents <- liftM vcat $ mapM (inlineListToMs' opts) $ + splitSentences inlines + return $ text ".LP" $$ contents +blockToMs _ (RawBlock f str) + | f == Format "man" = return $ text str + | otherwise = return empty +blockToMs _ HorizontalRule = return $ text ".PP" $$ text " * * * * *" +blockToMs opts (Header level _ inlines) = do + contents <- inlineListToMs' opts inlines + let heading = if writerNumberSections opts + then ".NH" + else ".SH" + return $ text heading <> space <> text (show level) $$ contents +blockToMs _ (CodeBlock _ str) = return $ + text ".IP" $$ + text ".nf" $$ + text "\\f[C]" $$ + text (escapeCode str) $$ + text "\\f[]" $$ + text ".fi" +blockToMs opts (LineBlock ls) = do + blockToMs opts $ Para $ intercalate [LineBreak] ls +blockToMs opts (BlockQuote blocks) = do + contents <- blockListToMs opts blocks + return $ text ".RS" $$ contents $$ text ".RE" +blockToMs opts (Table caption alignments widths headers rows) = + let aligncode AlignLeft = "l" + aligncode AlignRight = "r" + aligncode AlignCenter = "c" + aligncode AlignDefault = "l" + in do + caption' <- inlineListToMs' opts caption + let iwidths = if all (== 0) widths + then repeat "" + else map (printf "w(%0.1fn)" . (70 *)) widths + -- 78n default width - 8n indent = 70n + let coldescriptions = text $ intercalate " " + (zipWith (\align width -> aligncode align ++ width) + alignments iwidths) ++ "." + colheadings <- mapM (blockListToMs opts) headers + let makeRow cols = text "T{" $$ + (vcat $ intersperse (text "T}@T{") cols) $$ + text "T}" + let colheadings' = if all null headers + then empty + else makeRow colheadings $$ char '_' + body <- mapM (\row -> do + cols <- mapM (blockListToMs opts) row + return $ makeRow cols) rows + return $ text ".PP" $$ caption' $$ + text ".TS" $$ text "tab(@);" $$ coldescriptions $$ + colheadings' $$ vcat body $$ text ".TE" + +blockToMs opts (BulletList items) = do + contents <- mapM (bulletListItemToMs opts) items + return (vcat contents) +blockToMs opts (OrderedList attribs items) = do + let markers = take (length items) $ orderedListMarkers attribs + let indent = 1 + (maximum $ map length markers) + contents <- mapM (\(num, item) -> orderedListItemToMs opts num indent item) $ + zip markers items + return (vcat contents) +blockToMs opts (DefinitionList items) = do + contents <- mapM (definitionListItemToMs opts) items + return (vcat contents) + +-- | Convert bullet list item (list of blocks) to man. +bulletListItemToMs :: PandocMonad m => WriterOptions -> [Block] -> MS m Doc +bulletListItemToMs _ [] = return empty +bulletListItemToMs opts ((Para first):rest) = + bulletListItemToMs opts ((Plain first):rest) +bulletListItemToMs opts ((Plain first):rest) = do + first' <- blockToMs opts (Plain first) + rest' <- blockListToMs opts rest + let first'' = text ".IP \\[bu] 2" $$ first' + let rest'' = if null rest + then empty + else text ".RS 2" $$ rest' $$ text ".RE" + return (first'' $$ rest'') +bulletListItemToMs opts (first:rest) = do + first' <- blockToMs opts first + rest' <- blockListToMs opts rest + return $ text "\\[bu] .RS 2" $$ first' $$ rest' $$ text ".RE" + +-- | Convert ordered list item (a list of blocks) to man. +orderedListItemToMs :: PandocMonad m + => WriterOptions -- ^ options + -> String -- ^ order marker for list item + -> Int -- ^ number of spaces to indent + -> [Block] -- ^ list item (list of blocks) + -> MS m Doc +orderedListItemToMs _ _ _ [] = return empty +orderedListItemToMs opts num indent ((Para first):rest) = + orderedListItemToMs opts num indent ((Plain first):rest) +orderedListItemToMs opts num indent (first:rest) = do + first' <- blockToMs opts first + rest' <- blockListToMs opts rest + let num' = printf ("%" ++ show (indent - 1) ++ "s") num + let first'' = text (".IP \"" ++ num' ++ "\" " ++ show indent) $$ first' + let rest'' = if null rest + then empty + else text ".RS 4" $$ rest' $$ text ".RE" + return $ first'' $$ rest'' + +-- | Convert definition list item (label, list of blocks) to man. +definitionListItemToMs :: PandocMonad m + => WriterOptions + -> ([Inline],[[Block]]) + -> MS m Doc +definitionListItemToMs opts (label, defs) = do + labelText <- inlineListToMs' 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" + rest' <- liftM vcat $ + mapM (\item -> blockToMs opts item) rest + first' <- blockToMs opts first + return $ first' $$ text ".RS" $$ rest' $$ text ".RE" + return $ text ".XP" $$ nowrap (text ".B \"" <> labelText <> text "\"") + $$ text "\\~\\~" <> contents + +-- | Convert list of Pandoc block elements to man. +blockListToMs :: PandocMonad m + => WriterOptions -- ^ Options + -> [Block] -- ^ List of block elements + -> MS m Doc +blockListToMs opts blocks = + mapM (blockToMs opts) blocks >>= (return . vcat) + +-- | Convert list of Pandoc inline elements to ms. +inlineListToMs :: PandocMonad m => WriterOptions -> [Inline] -> MS m Doc +-- if list starts with ., insert a zero-width character \& so it +-- won't be interpreted as markup if it falls at the beginning of a line. +inlineListToMs opts lst@(Str ('.':_) : _) = mapM (inlineToMs opts) lst >>= + (return . (text "\\&" <>) . hcat) +inlineListToMs opts lst = hcat <$> mapM (inlineToMs opts) lst + +-- This version to be used when there is no further inline content; +-- forces a note at the end. +inlineListToMs' :: PandocMonad m => WriterOptions -> [Inline] -> MS m Doc +inlineListToMs' opts lst = do + x <- hcat <$> mapM (inlineToMs opts) lst + y <- handleNotes opts empty + return $ x <> y + +-- | Convert Pandoc inline element to man. +inlineToMs :: PandocMonad m => WriterOptions -> Inline -> MS m Doc +inlineToMs opts (Span _ ils) = inlineListToMs opts ils +inlineToMs opts (Emph lst) = do + contents <- inlineListToMs opts lst + return $ text "\\f[I]" <> contents <> text "\\f[]" +inlineToMs opts (Strong lst) = do + contents <- inlineListToMs opts lst + return $ text "\\f[B]" <> contents <> text "\\f[]" +inlineToMs opts (Strikeout lst) = do + contents <- inlineListToMs opts lst + return $ text "[STRIKEOUT:" <> contents <> char ']' +inlineToMs opts (Superscript lst) = do + contents <- inlineListToMs opts lst + return $ char '^' <> contents <> char '^' +inlineToMs opts (Subscript lst) = do + contents <- inlineListToMs opts lst + return $ char '~' <> contents <> char '~' +inlineToMs opts (SmallCaps lst) = inlineListToMs opts lst -- not supported +inlineToMs opts (Quoted SingleQuote lst) = do + contents <- inlineListToMs opts lst + return $ char '`' <> contents <> char '\'' +inlineToMs opts (Quoted DoubleQuote lst) = do + contents <- inlineListToMs opts lst + return $ text "\\[lq]" <> contents <> text "\\[rq]" +inlineToMs opts (Cite _ lst) = + inlineListToMs opts lst +inlineToMs _ (Code _ str) = + return $ text $ "\\f[C]" ++ escapeCode str ++ "\\f[]" +inlineToMs _ (Str str) = return $ text $ escapeString str +inlineToMs opts (Math InlineMath str) = do + modify $ \st -> st{ stHasInlineMath = True } + res <- convertMath writeEqn InlineMath str + case res of + Left il -> inlineToMs opts il + Right r -> return $ text "|" <> text (escapeBar r) <> text "|" +inlineToMs opts (Math DisplayMath str) = do + res <- convertMath writeEqn InlineMath str + case res of + Left il -> do + contents <- inlineToMs opts il + return $ cr <> text ".RS" $$ contents $$ text ".RE" + Right r -> return $ + cr <> text ".EQ" $$ text (escapeBar r) $$ text ".EN" +inlineToMs _ (RawInline f str) + | f == Format "man" = return $ text str + | otherwise = return empty +inlineToMs _ (LineBreak) = return $ + cr <> text ".PD 0" $$ text ".P" $$ text ".PD" <> cr +inlineToMs opts SoftBreak = handleNotes opts cr +inlineToMs opts Space = handleNotes opts space +inlineToMs opts (Link _ txt (src, _)) = do + let srcSuffix = fromMaybe src (stripPrefix "mailto:" src) + case txt of + [Str s] + | escapeURI s == srcSuffix -> + return $ char '<' <> text srcSuffix <> char '>' + _ -> do + let linknote = [Plain [Str src]] + inlineListToMs opts (txt ++ [Note linknote]) +inlineToMs opts (Image attr alternate (source, tit)) = do + let txt = if (null alternate) || (alternate == [Str ""]) || + (alternate == [Str source]) -- to prevent autolinks + then [Str "image"] + else alternate + linkPart <- inlineToMs opts (Link attr txt (source, tit)) + return $ char '[' <> text "IMAGE: " <> linkPart <> char ']' +inlineToMs _ (Note contents) = do + modify $ \st -> st{ stNotes = contents : stNotes st } + return $ text "\\**" + +handleNotes :: PandocMonad m => WriterOptions -> Doc -> MS m Doc +handleNotes opts fallback = do + notes <- gets stNotes + if null notes + then return fallback + else do + modify $ \st -> st{ stNotes = [] } + vcat <$> mapM (handleNote opts) notes + +handleNote :: PandocMonad m => WriterOptions -> Note -> MS m Doc +handleNote opts bs = do + contents <- blockListToMs opts bs + return $ cr <> text ".FS" $$ contents $$ text ".FE" <> cr + -- cgit v1.2.3 From da0aae9c8fb7cf03e9577b8ae8dce1f2d23c25e9 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 23 Mar 2017 11:48:01 +0100 Subject: Ms writer: fixed strong/emph combination. Perhaps something similar is needed in the man writer. --- src/Text/Pandoc/Writers/Ms.hs | 67 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 13 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 194475304..0c3586aa6 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -31,7 +31,7 @@ TODO: [ ] warning for non-rendered raw content [ ] is there a better way to do strikeout? -[ ] strong + em doesn't seem to work +[x] strong + em doesn't seem to work [ ] super + subscript don't seem to work [ ] options for hyperlink rendering (currently footnote) [ ] avoid note-in-note (which we currently get easily with @@ -43,10 +43,23 @@ TODO: they exist in the groff manual PDF version) [ ] use a custom macro for defn lists so they're configurable [ ] better handling of accented characters and other non-ascii - characters (e.g. curly quotes) -- we shouldn't rely on a - utf8 compatible groff + characters (e.g. curly quotes). + Note: recent versions of groff (more recent than standard + on many systems) include a -k option which runs preconv. + preconv basically converts non-ascii characters + to \[uXXXX] entities. Since we can't assume that the local + groff has the -k option, we could have any invocation of + groff in Text.Pandoc.PDF filter the input through a Haskell + function that does what preconv does. + On the other hand: only recent groffs have -Tpdf. so + if we want compatibility with older groffs, we need to to + -Tps and pipe through ps2pdf (can we assume it's available?). + A big advantage of gropdf: it supports the tag + \X'pdf: pdfpic file alignment width height line-length' + and also seems to support bookmarks. [ ] avoid blank line after footnote marker when footnote has a paragraph +[ ] better smallcaps support, see below... [ ] add via groff option to PDF module [ ] better handling of autolinks? [ ] better handling of images, perhaps converting to eps when @@ -63,7 +76,8 @@ import Text.Pandoc.Writers.Shared import Text.Pandoc.Options import Text.Pandoc.Writers.Math import Text.Printf ( printf ) -import Data.List ( stripPrefix, intersperse, intercalate ) +import qualified Data.Map as Map +import Data.List ( stripPrefix, intersperse, intercalate, sort ) import Data.Maybe (fromMaybe) import Text.Pandoc.Pretty import Text.Pandoc.Builder (deleteMeta) @@ -73,7 +87,20 @@ import Data.Char ( isDigit ) import Text.TeXMath (writeEqn) data WriterState = WriterState { stHasInlineMath :: Bool - , stNotes :: [Note] } + , stNotes :: [Note] + , stFontFeatures :: Map.Map Char Bool + } + +defaultWriterState :: WriterState +defaultWriterState = WriterState{ stHasInlineMath = False + , stNotes = [] + , stFontFeatures = Map.fromList [ + ('I',False) + , ('B',False) + , ('C',False) + ] + } + type Note = [Block] type MS = StateT WriterState @@ -81,7 +108,7 @@ type MS = StateT WriterState -- | Convert Pandoc to Ms. writeMs :: PandocMonad m => WriterOptions -> Pandoc -> m String writeMs opts document = - evalStateT (pandocToMs opts document) (WriterState False []) + evalStateT (pandocToMs opts document) defaultWriterState -- | Return groff man representation of document. pandocToMs :: PandocMonad m => WriterOptions -> Pandoc -> MS m String @@ -342,12 +369,10 @@ inlineListToMs' opts lst = do -- | Convert Pandoc inline element to man. inlineToMs :: PandocMonad m => WriterOptions -> Inline -> MS m Doc inlineToMs opts (Span _ ils) = inlineListToMs opts ils -inlineToMs opts (Emph lst) = do - contents <- inlineListToMs opts lst - return $ text "\\f[I]" <> contents <> text "\\f[]" -inlineToMs opts (Strong lst) = do - contents <- inlineListToMs opts lst - return $ text "\\f[B]" <> contents <> text "\\f[]" +inlineToMs opts (Emph lst) = + withFontFeature 'I' (inlineListToMs opts lst) +inlineToMs opts (Strong lst) = + withFontFeature 'B' (inlineListToMs opts lst) inlineToMs opts (Strikeout lst) = do contents <- inlineListToMs opts lst return $ text "[STRIKEOUT:" <> contents <> char ']' @@ -358,6 +383,8 @@ inlineToMs opts (Subscript lst) = do contents <- inlineListToMs opts lst return $ char '~' <> contents <> char '~' inlineToMs opts (SmallCaps lst) = inlineListToMs opts lst -- not supported +-- but see https://lists.gnu.org/archive/html/groff/2015-01/msg00016.html +-- for a way to fake them inlineToMs opts (Quoted SingleQuote lst) = do contents <- inlineListToMs opts lst return $ char '`' <> contents <> char '\'' @@ -367,7 +394,7 @@ inlineToMs opts (Quoted DoubleQuote lst) = do inlineToMs opts (Cite _ lst) = inlineListToMs opts lst inlineToMs _ (Code _ str) = - return $ text $ "\\f[C]" ++ escapeCode str ++ "\\f[]" + withFontFeature 'C' (return $ text $ escapeCode str) inlineToMs _ (Str str) = return $ text $ escapeString str inlineToMs opts (Math InlineMath str) = do modify $ \st -> st{ stHasInlineMath = True } @@ -424,3 +451,17 @@ handleNote opts bs = do contents <- blockListToMs opts bs return $ cr <> text ".FS" $$ contents $$ text ".FE" <> cr +fontChange :: PandocMonad m => MS m Doc +fontChange = do + features <- gets stFontFeatures + let filling = sort [c | (c,True) <- Map.toList features] + return $ text $ "\\f[" ++ filling ++ "]" + +withFontFeature :: PandocMonad m => Char -> MS m Doc -> MS m Doc +withFontFeature c action = do + modify $ \st -> st{ stFontFeatures = Map.adjust not c $ stFontFeatures st } + begin <- fontChange + d <- action + modify $ \st -> st{ stFontFeatures = Map.adjust not c $ stFontFeatures st } + end <- fontChange + return $ begin <> d <> end -- cgit v1.2.3 From 1809f64a4cacdfe4bb7463b52181ceb74502ccd9 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 23 Mar 2017 11:56:13 +0100 Subject: Ms writer: Improved footnotes. --- src/Text/Pandoc/Writers/Ms.hs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 0c3586aa6..3315548e2 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -57,7 +57,7 @@ TODO: A big advantage of gropdf: it supports the tag \X'pdf: pdfpic file alignment width height line-length' and also seems to support bookmarks. -[ ] avoid blank line after footnote marker when footnote has a +[x] avoid blank line after footnote marker when footnote has a paragraph [ ] better smallcaps support, see below... [ ] add via groff option to PDF module @@ -448,7 +448,12 @@ handleNotes opts fallback = do handleNote :: PandocMonad m => WriterOptions -> Note -> MS m Doc handleNote opts bs = do - contents <- blockListToMs opts bs + -- don't start with Paragraph or we'll get a spurious blank + -- line after the note ref: + let bs' = case bs of + (Para ils : rest) -> Plain ils : rest + _ -> bs + contents <- blockListToMs opts bs' return $ cr <> text ".FS" $$ contents $$ text ".FE" <> cr fontChange :: PandocMonad m => MS m Doc -- cgit v1.2.3 From 8cf5c55e5e5a9d17cc8bbf57dec64e70e4ba810d Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 23 Mar 2017 12:05:41 +0100 Subject: Ms. writer: don't render links in footnotes as footnotes. --- src/Text/Pandoc/Writers/Ms.hs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 3315548e2..f45ddede8 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -34,7 +34,7 @@ TODO: [x] strong + em doesn't seem to work [ ] super + subscript don't seem to work [ ] options for hyperlink rendering (currently footnote) -[ ] avoid note-in-note (which we currently get easily with +[x] avoid note-in-note (which we currently get easily with links in footnotes) [ ] can we get prettier output using .B, etc. instead of the inline forms? @@ -88,12 +88,14 @@ import Text.TeXMath (writeEqn) data WriterState = WriterState { stHasInlineMath :: Bool , stNotes :: [Note] + , stInNote :: Bool , stFontFeatures :: Map.Map Char Bool } defaultWriterState :: WriterState defaultWriterState = WriterState{ stHasInlineMath = False , stNotes = [] + , stInNote = False , stFontFeatures = Map.fromList [ ('I',False) , ('B',False) @@ -419,11 +421,17 @@ inlineToMs opts SoftBreak = handleNotes opts cr inlineToMs opts Space = handleNotes opts space inlineToMs opts (Link _ txt (src, _)) = do let srcSuffix = fromMaybe src (stripPrefix "mailto:" src) + inNote <- gets stInNote case txt of [Str s] | escapeURI s == srcSuffix -> - return $ char '<' <> text srcSuffix <> char '>' - _ -> do + return $ char '<' <> text (escapeString srcSuffix) <> char '>' + _ | inNote -> do + -- avoid a note in a note! + contents <- inlineListToMs opts txt + return $ contents <> space <> char '(' <> + text (escapeString src) <> char ')' + | otherwise -> do let linknote = [Plain [Str src]] inlineListToMs opts (txt ++ [Note linknote]) inlineToMs opts (Image attr alternate (source, tit)) = do @@ -443,8 +451,10 @@ handleNotes opts fallback = do if null notes then return fallback else do - modify $ \st -> st{ stNotes = [] } - vcat <$> mapM (handleNote opts) notes + modify $ \st -> st{ stNotes = [], stInNote = True } + res <- vcat <$> mapM (handleNote opts) notes + modify $ \st -> st{ stInNote = False } + return res handleNote :: PandocMonad m => WriterOptions -> Note -> MS m Doc handleNote opts bs = do -- cgit v1.2.3 From 1af6faf2d4fce2cfa30cd4ebc15c1613c8f2abd8 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 23 Mar 2017 12:45:23 +0100 Subject: Ms writer: support for fake smallcaps. --- src/Text/Pandoc/Writers/Ms.hs | 80 ++++++++++++++++++++++++++++--------------- 1 file changed, 52 insertions(+), 28 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index f45ddede8..d228235f1 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -31,11 +31,8 @@ TODO: [ ] warning for non-rendered raw content [ ] is there a better way to do strikeout? -[x] strong + em doesn't seem to work [ ] super + subscript don't seem to work [ ] options for hyperlink rendering (currently footnote) -[x] avoid note-in-note (which we currently get easily with - links in footnotes) [ ] can we get prettier output using .B, etc. instead of the inline forms? [ ] tight/loose list distinction @@ -57,11 +54,7 @@ TODO: A big advantage of gropdf: it supports the tag \X'pdf: pdfpic file alignment width height line-length' and also seems to support bookmarks. -[x] avoid blank line after footnote marker when footnote has a - paragraph -[ ] better smallcaps support, see below... [ ] add via groff option to PDF module -[ ] better handling of autolinks? [ ] better handling of images, perhaps converting to eps when going to PDF? [ ] better template, with configurable page number, table of contents, @@ -83,12 +76,13 @@ import Text.Pandoc.Pretty import Text.Pandoc.Builder (deleteMeta) import Text.Pandoc.Class (PandocMonad) import Control.Monad.State -import Data.Char ( isDigit ) +import Data.Char ( isDigit, isLower, isUpper, toUpper ) import Text.TeXMath (writeEqn) data WriterState = WriterState { stHasInlineMath :: Bool , stNotes :: [Note] , stInNote :: Bool + , stSmallCaps :: Bool , stFontFeatures :: Map.Map Char Bool } @@ -96,6 +90,7 @@ defaultWriterState :: WriterState defaultWriterState = WriterState{ stHasInlineMath = False , stNotes = [] , stInNote = False + , stSmallCaps = False , stFontFeatures = Map.fromList [ ('I',False) , ('B',False) @@ -152,15 +147,24 @@ pandocToMs opts (Pandoc meta blocks) = do Just tpl -> return $ renderTemplate' tpl context -- | Association list of characters to escape. -manEscapes :: [(Char, String)] -manEscapes = [ ('\160', "\\ ") - , ('\'', "\\[aq]") - , ('’', "'") - , ('\x2014', "\\[em]") - , ('\x2013', "\\[en]") - , ('\x2026', "\\&...") - , ('|', "\\[u007C]") -- because we use | for inline math - ] ++ backslashEscapes "-@\\" +manEscapes :: Map.Map Char String +manEscapes = Map.fromList $ + [ ('\160', "\\ ") + , ('\'', "\\[aq]") + , ('’', "'") + , ('\x2014', "\\[em]") + , ('\x2013', "\\[en]") + , ('\x2026', "\\&...") + , ('|', "\\[u007C]") -- because we use | for inline math + , ('-', "\\-") + , ('@', "\\@") + , ('\\', "\\\\") + ] + +escapeChar :: Char -> String +escapeChar c = case Map.lookup c manEscapes of + Just s -> s + Nothing -> [c] -- | Escape | character, used to mark inline math, inside math. escapeBar :: String -> String @@ -170,15 +174,28 @@ escapeBar = concatMap go -- | Escape special characters for Ms. escapeString :: String -> String -escapeString = escapeStringUsing manEscapes +escapeString = concatMap escapeChar + +toSmallCaps :: String -> String +toSmallCaps [] = [] +toSmallCaps (c:cs) + | isLower c = let (lowers,rest) = span isLower (c:cs) + in "\\s-2" ++ escapeString (map toUpper lowers) ++ + "\\s0" ++ toSmallCaps rest + | isUpper c = let (uppers,rest) = span isUpper (c:cs) + in escapeString uppers ++ toSmallCaps rest + | otherwise = escapeChar c ++ toSmallCaps cs -- | Escape a literal (code) section for Ms. escapeCode :: String -> String -escapeCode = concat . intersperse "\n" . map escapeLine . lines where - escapeLine codeline = - case escapeStringUsing (manEscapes ++ backslashEscapes "\t ") codeline of - a@('.':_) -> "\\&" ++ a - b -> b +escapeCode = concat . intersperse "\n" . map escapeLine . lines + where escapeCodeChar ' ' = "\\ " + escapeCodeChar '\t' = "\\\t" + escapeCodeChar c = escapeChar c + escapeLine codeline = + case concatMap escapeCodeChar codeline of + a@('.':_) -> "\\&" ++ a + b -> b -- We split inline lists into sentences, and print one sentence per -- line. groff/troff treats the line-ending period differently. @@ -384,9 +401,12 @@ inlineToMs opts (Superscript lst) = do inlineToMs opts (Subscript lst) = do contents <- inlineListToMs opts lst return $ char '~' <> contents <> char '~' -inlineToMs opts (SmallCaps lst) = inlineListToMs opts lst -- not supported --- but see https://lists.gnu.org/archive/html/groff/2015-01/msg00016.html --- for a way to fake them +inlineToMs opts (SmallCaps lst) = do + -- see https://lists.gnu.org/archive/html/groff/2015-01/msg00016.html + modify $ \st -> st{ stSmallCaps = not (stSmallCaps st) } + res <- inlineListToMs opts lst + modify $ \st -> st{ stSmallCaps = not (stSmallCaps st) } + return res inlineToMs opts (Quoted SingleQuote lst) = do contents <- inlineListToMs opts lst return $ char '`' <> contents <> char '\'' @@ -397,7 +417,11 @@ inlineToMs opts (Cite _ lst) = inlineListToMs opts lst inlineToMs _ (Code _ str) = withFontFeature 'C' (return $ text $ escapeCode str) -inlineToMs _ (Str str) = return $ text $ escapeString str +inlineToMs _ (Str str) = do + smallcaps <- gets stSmallCaps + if smallcaps + then return $ text $ toSmallCaps str + else return $ text $ escapeString str inlineToMs opts (Math InlineMath str) = do modify $ \st -> st{ stHasInlineMath = True } res <- convertMath writeEqn InlineMath str @@ -425,7 +449,7 @@ inlineToMs opts (Link _ txt (src, _)) = do case txt of [Str s] | escapeURI s == srcSuffix -> - return $ char '<' <> text (escapeString srcSuffix) <> char '>' + return $ text (escapeString srcSuffix) _ | inNote -> do -- avoid a note in a note! contents <- inlineListToMs opts txt -- cgit v1.2.3 From d20d3a5dbbb1bb1e6b6de5fc3d9c65bf844dcb56 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 23 Mar 2017 12:59:19 +0100 Subject: Ms writer: Super/subscript support. Also added some macro definitions to default template to support subscripts + better superscripts. --- src/Text/Pandoc/Writers/Ms.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index d228235f1..5994545e5 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -397,10 +397,10 @@ inlineToMs opts (Strikeout lst) = do return $ text "[STRIKEOUT:" <> contents <> char ']' inlineToMs opts (Superscript lst) = do contents <- inlineListToMs opts lst - return $ char '^' <> contents <> char '^' + return $ text "\\*{" <> contents <> text "\\*}" inlineToMs opts (Subscript lst) = do contents <- inlineListToMs opts lst - return $ char '~' <> contents <> char '~' + return $ text "\\*<" <> contents <> text "\\*>" inlineToMs opts (SmallCaps lst) = do -- see https://lists.gnu.org/archive/html/groff/2015-01/msg00016.html modify $ \st -> st{ stSmallCaps = not (stSmallCaps st) } -- cgit v1.2.3 From 1ae38fde4dbc542976e70b9337cf1342e205f25b Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 23 Mar 2017 13:00:14 +0100 Subject: MS writer: updated TODO comment. --- src/Text/Pandoc/Writers/Ms.hs | 1 - 1 file changed, 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 5994545e5..523167349 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -31,7 +31,6 @@ TODO: [ ] warning for non-rendered raw content [ ] is there a better way to do strikeout? -[ ] super + subscript don't seem to work [ ] options for hyperlink rendering (currently footnote) [ ] can we get prettier output using .B, etc. instead of the inline forms? -- cgit v1.2.3 From dd1ac8f48fdb4cf29c924690ecbed4557519b444 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 23 Mar 2017 14:33:39 +0100 Subject: Writers.Shared.gridTable: defensive coding around 'maximum'. --- src/Text/Pandoc/Writers/Shared.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index 3b28c58c8..299c6faaf 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -246,7 +246,8 @@ gridTable opts blocksToDoc headless aligns widths headers rows = do let handleZeroWidths = do rawHeaders' <- mapM (blocksToDoc opts) headers rawRows' <- mapM (mapM (blocksToDoc opts)) rows - let numChars = maximum . map offset + let numChars [] = 0 + numChars xs = maximum . map offset $ xs let widthsInChars' = map ((+2) . numChars) $ transpose (rawHeaders' : rawRows') if sum widthsInChars' > writerColumns opts -- cgit v1.2.3 From e180a2efa6477dd08b7c6c41816d6cfdf9f24afd Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 23 Mar 2017 14:48:09 +0100 Subject: Ms writer: fixed hard line breaks. --- src/Text/Pandoc/Writers/Ms.hs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 523167349..e716b147d 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -438,8 +438,7 @@ inlineToMs opts (Math DisplayMath str) = do inlineToMs _ (RawInline f str) | f == Format "man" = return $ text str | otherwise = return empty -inlineToMs _ (LineBreak) = return $ - cr <> text ".PD 0" $$ text ".P" $$ text ".PD" <> cr +inlineToMs _ (LineBreak) = return $ cr <> text ".br" <> cr inlineToMs opts SoftBreak = handleNotes opts cr inlineToMs opts Space = handleNotes opts space inlineToMs opts (Link _ txt (src, _)) = do -- cgit v1.2.3 From e92941a9ca75560d6de99b86061492ec49ef1525 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 23 Mar 2017 14:59:51 +0100 Subject: Grid tables: remove unnecessary extra space in cells. --- src/Text/Pandoc/Writers/Shared.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index 299c6faaf..7e08724d8 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -249,7 +249,7 @@ gridTable opts blocksToDoc headless aligns widths headers rows = do let numChars [] = 0 numChars xs = maximum . map offset $ xs let widthsInChars' = - map ((+2) . numChars) $ transpose (rawHeaders' : rawRows') + map numChars $ transpose (rawHeaders' : rawRows') if sum widthsInChars' > writerColumns opts then -- use even widths handleGivenWidths -- cgit v1.2.3 From a939cfe769408ec41aee7ebe5ce4d36f5160d7d1 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 23 Mar 2017 16:54:24 +0100 Subject: Pipe tables: impose minimum cell size. This might help with #3526. At any rate, it fixes another bug (see test/command/3526.md). --- src/Text/Pandoc/Writers/Shared.hs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index 7e08724d8..615733a78 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -230,8 +230,11 @@ gridTable opts blocksToDoc headless aligns widths headers rows = do let numcols = maximum (length aligns : length widths : map length (headers:rows)) let handleGivenWidths widths' = do - let widthsInChars' = map ((\x -> x - 3) . floor . - (fromIntegral (writerColumns opts) *)) widths' + let widthsInChars' = map ( + (\x -> if x < 1 then 1 else x) . + (\x -> x - 3) . floor . + (fromIntegral (writerColumns opts) *) + ) widths' rawHeaders' <- zipWithM blocksToDoc (map (\w -> opts{writerColumns = min (w - 2) (writerColumns opts)}) widthsInChars') @@ -268,11 +271,10 @@ gridTable opts blocksToDoc headless aligns widths headers rows = do let head' = makeRow rawHeaders let rows' = map (makeRow . map chomp) rawRows let borderpart ch align widthInChars = - let widthInChars' = if widthInChars < 1 then 1 else widthInChars - in (if (align == AlignLeft || align == AlignCenter) + (if (align == AlignLeft || align == AlignCenter) then char ':' else char ch) <> - text (replicate widthInChars' ch) <> + text (replicate widthInChars ch) <> (if (align == AlignRight || align == AlignCenter) then char ':' else char ch) -- cgit v1.2.3 From 6c07e431129c27bf6e541329dc140aaa23e7ac79 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 23 Mar 2017 17:20:06 +0100 Subject: Ms writer: use .IP for more standard definition lists. --- src/Text/Pandoc/Writers/Ms.hs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index e716b147d..0ca3ddea9 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -357,8 +357,7 @@ definitionListItemToMs opts (label, defs) = do mapM (\item -> blockToMs opts item) rest first' <- blockToMs opts first return $ first' $$ text ".RS" $$ rest' $$ text ".RE" - return $ text ".XP" $$ nowrap (text ".B \"" <> labelText <> text "\"") - $$ text "\\~\\~" <> contents + return $ nowrap (text ".IP \"" <> labelText <> text "\"") $$ contents -- | Convert list of Pandoc block elements to man. blockListToMs :: PandocMonad m -- cgit v1.2.3 From ffd699385a9ea040e6859b7b882b4190597a7f0c Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 23 Mar 2017 17:29:26 +0100 Subject: Ms writer: improved definition lists. Use standard .IP macro. Also properly escape ". --- src/Text/Pandoc/Writers/Ms.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 0ca3ddea9..e326f19ab 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -37,7 +37,6 @@ TODO: [ ] tight/loose list distinction [ ] internal hyperlinks (this seems to be possible since they exist in the groff manual PDF version) -[ ] use a custom macro for defn lists so they're configurable [ ] better handling of accented characters and other non-ascii characters (e.g. curly quotes). Note: recent versions of groff (more recent than standard @@ -53,6 +52,8 @@ TODO: A big advantage of gropdf: it supports the tag \X'pdf: pdfpic file alignment width height line-length' and also seems to support bookmarks. + See also the pdfroff shell script that comes with more + recent versions of groff. [ ] add via groff option to PDF module [ ] better handling of images, perhaps converting to eps when going to PDF? @@ -151,6 +152,7 @@ manEscapes = Map.fromList $ [ ('\160', "\\ ") , ('\'', "\\[aq]") , ('’', "'") + , ('"', "\\\"") , ('\x2014', "\\[em]") , ('\x2013', "\\[en]") , ('\x2026', "\\&...") -- cgit v1.2.3 From f0abbe7533db3e2c14066bddbb5d52ade1ef0685 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 23 Mar 2017 21:24:01 +0100 Subject: Allow creation of pdf via groff ms and pdfroff. pandoc -t ms -o output.pdf input.txt --- src/Text/Pandoc/App.hs | 12 ++++++++---- src/Text/Pandoc/PDF.hs | 33 ++++++++++++++++++++++++++++++++- src/Text/Pandoc/Writers/Ms.hs | 23 +++-------------------- 3 files changed, 43 insertions(+), 25 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index b9cd04631..29a8add3d 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -164,6 +164,7 @@ convertWithOpts opts = do let laTeXOutput = format `elem` ["latex", "beamer"] let conTeXtOutput = format == "context" let html5Output = format == "html5" || format == "html" + let msOutput = format == "ms" -- disabling the custom writer for now writer <- if ".lua" `isSuffixOf` format @@ -398,15 +399,18 @@ convertWithOpts opts = do ByteStringWriter f -> f writerOptions doc' >>= writeFnBinary outputFile StringWriter f | pdfOutput -> do - -- make sure writer is latex or beamer or context or html5 - unless (laTeXOutput || conTeXtOutput || html5Output) $ + -- make sure writer is latex, beamer, context, html5 or ms + unless (laTeXOutput || conTeXtOutput || html5Output || + msOutput) $ err 47 $ "cannot produce pdf output with " ++ format ++ " writer" let pdfprog = case () of _ | conTeXtOutput -> "context" - _ | html5Output -> "wkhtmltopdf" - _ -> optLaTeXEngine opts + | html5Output -> "wkhtmltopdf" + | html5Output -> "wkhtmltopdf" + | msOutput -> "pdfroff" + | otherwise -> optLaTeXEngine opts -- check for pdf creating program mbPdfProg <- liftIO $ findExecutable pdfprog when (isNothing mbPdfProg) $ diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index 43110abf1..f1274686d 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -74,7 +74,7 @@ changePathSeparators = intercalate "/" . splitDirectories makePDF :: MonadIO m => String -- ^ pdf creator (pdflatex, lualatex, - -- xelatex, context, wkhtmltopdf) + -- xelatex, context, wkhtmltopdf, pdfroff) -> (WriterOptions -> Pandoc -> PandocIO String) -- ^ writer -> WriterOptions -- ^ options -> Verbosity -- ^ verbosity level @@ -106,6 +106,12 @@ makePDF "wkhtmltopdf" writer opts verbosity _ doc@(Pandoc meta _) = liftIO $ do setVerbosity verbosity writer opts doc html2pdf verbosity args source +makePDF "pdfroff" writer opts verbosity _mediabag doc = liftIO $ do + source <- runIOorExplode $ do + setVerbosity verbosity + writer opts doc + let args = ["-ms", "-e", "-k", "-i"] + ms2pdf verbosity args source makePDF program writer opts verbosity mediabag doc = do let withTemp = if takeBaseName program == "context" then withTempDirectory "." @@ -295,6 +301,31 @@ runTeXProgram verbosity program args runNumber numRuns tmpDir source = do else return Nothing return (exit, out, pdf) +ms2pdf :: Verbosity + -> [String] + -> String + -> IO (Either ByteString ByteString) +ms2pdf verbosity args source = do + env' <- getEnvironment + when (verbosity >= INFO) $ do + putStrLn "[makePDF] Command line:" + putStrLn $ "pdfroff " ++ " " ++ unwords (map show args) + putStr "\n" + putStrLn "[makePDF] Environment:" + mapM_ print env' + putStr "\n" + putStrLn $ "[makePDF] Contents:\n" + putStr source + putStr "\n" + (exit, out) <- pipeProcess (Just env') "pdfroff" args + (UTF8.fromStringLazy source) + when (verbosity >= INFO) $ do + B.hPutStr stdout out + putStr "\n" + return $ case exit of + ExitFailure _ -> Left out + ExitSuccess -> Right out + html2pdf :: Verbosity -- ^ Verbosity level -> [String] -- ^ Args to wkhtmltopdf -> String -- ^ HTML5 source diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index e326f19ab..4e6ae0951 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -37,28 +37,11 @@ TODO: [ ] tight/loose list distinction [ ] internal hyperlinks (this seems to be possible since they exist in the groff manual PDF version) -[ ] better handling of accented characters and other non-ascii - characters (e.g. curly quotes). - Note: recent versions of groff (more recent than standard - on many systems) include a -k option which runs preconv. - preconv basically converts non-ascii characters - to \[uXXXX] entities. Since we can't assume that the local - groff has the -k option, we could have any invocation of - groff in Text.Pandoc.PDF filter the input through a Haskell - function that does what preconv does. - On the other hand: only recent groffs have -Tpdf. so - if we want compatibility with older groffs, we need to to - -Tps and pipe through ps2pdf (can we assume it's available?). - A big advantage of gropdf: it supports the tag - \X'pdf: pdfpic file alignment width height line-length' - and also seems to support bookmarks. - See also the pdfroff shell script that comes with more - recent versions of groff. -[ ] add via groff option to PDF module -[ ] better handling of images, perhaps converting to eps when - going to PDF? [ ] better template, with configurable page number, table of contents, columns, etc. +[ ] support for images? gropdf (and maybe pdfroff) supports the tag + \X'pdf: pdfpic file alignment width height line-length' + and also seems to support bookmarks. -} module Text.Pandoc.Writers.Ms ( writeMs ) where -- cgit v1.2.3 From c964898899688f495197f300f05d5ee15beff411 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 24 Mar 2017 08:52:35 +0100 Subject: Ms writer: warning for non-rendered raw content. --- src/Text/Pandoc/Writers/Ms.hs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 4e6ae0951..ceec05ae7 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -29,7 +29,6 @@ Conversion of 'Pandoc' documents to groff man page format. TODO: -[ ] warning for non-rendered raw content [ ] is there a better way to do strikeout? [ ] options for hyperlink rendering (currently footnote) [ ] can we get prettier output using .B, etc. instead of @@ -57,7 +56,8 @@ import Data.List ( stripPrefix, intersperse, intercalate, sort ) import Data.Maybe (fromMaybe) import Text.Pandoc.Pretty import Text.Pandoc.Builder (deleteMeta) -import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Class (PandocMonad, report) +import Text.Pandoc.Logging import Control.Monad.State import Data.Char ( isDigit, isLower, isUpper, toUpper ) import Text.TeXMath (writeEqn) @@ -223,9 +223,11 @@ blockToMs opts (Para inlines) = do contents <- liftM vcat $ mapM (inlineListToMs' opts) $ splitSentences inlines return $ text ".LP" $$ contents -blockToMs _ (RawBlock f str) +blockToMs _ b@(RawBlock f str) | f == Format "man" = return $ text str - | otherwise = return empty + | otherwise = do + report $ BlockNotRendered b + return empty blockToMs _ HorizontalRule = return $ text ".PP" $$ text " * * * * *" blockToMs opts (Header level _ inlines) = do contents <- inlineListToMs' opts inlines @@ -419,9 +421,11 @@ inlineToMs opts (Math DisplayMath str) = do return $ cr <> text ".RS" $$ contents $$ text ".RE" Right r -> return $ cr <> text ".EQ" $$ text (escapeBar r) $$ text ".EN" -inlineToMs _ (RawInline f str) +inlineToMs _ il@(RawInline f str) | f == Format "man" = return $ text str - | otherwise = return empty + | otherwise = do + report $ InlineNotRendered il + return empty inlineToMs _ (LineBreak) = return $ cr <> text ".br" <> cr inlineToMs opts SoftBreak = handleNotes opts cr inlineToMs opts Space = handleNotes opts space -- cgit v1.2.3 From 2251d9cb737dee44b7aad587972c8098057f99d1 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 24 Mar 2017 09:18:56 +0100 Subject: Ms writer: Use custom .HRULE macro for horizontal rule. --- src/Text/Pandoc/Writers/Ms.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index ceec05ae7..eb56611ba 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -228,7 +228,8 @@ blockToMs _ b@(RawBlock f str) | otherwise = do report $ BlockNotRendered b return empty -blockToMs _ HorizontalRule = return $ text ".PP" $$ text " * * * * *" +blockToMs _ HorizontalRule = + return $ text ".HLINE" blockToMs opts (Header level _ inlines) = do contents <- inlineListToMs' opts inlines let heading = if writerNumberSections opts -- cgit v1.2.3 From 7de9a6ef5fd89c0aded82acc7f037d9a98e243be Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 24 Mar 2017 09:35:30 +0100 Subject: PDF via groff ms: use -t and -KUTF-8 options to pdfroff. --- src/Text/Pandoc/PDF.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index f1274686d..164ab0244 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -110,7 +110,7 @@ makePDF "pdfroff" writer opts verbosity _mediabag doc = liftIO $ do source <- runIOorExplode $ do setVerbosity verbosity writer opts doc - let args = ["-ms", "-e", "-k", "-i"] + let args = ["-ms", "-e", "-t", "-k", "-KUTF-8", "-i"] ms2pdf verbosity args source makePDF program writer opts verbosity mediabag doc = do let withTemp = if takeBaseName program == "context" -- cgit v1.2.3 From 718254340fb73d75cf8f6fc322631c1c72db0e47 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 24 Mar 2017 09:40:02 +0100 Subject: Recognize .roff extension as ms format. --- src/Text/Pandoc/App.hs | 1 + src/Text/Pandoc/Writers/Ms.hs | 9 +++++++++ 2 files changed, 10 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 29a8add3d..0fc883792 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -696,6 +696,7 @@ defaultWriterName x = ".tei.xml" -> "tei" ".tei" -> "tei" ".ms" -> "ms" + ".roff" -> "ms" ['.',y] | y `elem` ['1'..'9'] -> "man" _ -> "html" diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index eb56611ba..84b911e35 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -29,6 +29,10 @@ Conversion of 'Pandoc' documents to groff man page format. TODO: +[ ] for links, emails, consider using macros from www: man groff_www + alo has a raw html macro and support for images. +[ ] consider using a custom macro package for pandoc (perhaps if + a variable is set?) [ ] is there a better way to do strikeout? [ ] options for hyperlink rendering (currently footnote) [ ] can we get prettier output using .B, etc. instead of @@ -41,6 +45,11 @@ TODO: [ ] support for images? gropdf (and maybe pdfroff) supports the tag \X'pdf: pdfpic file alignment width height line-length' and also seems to support bookmarks. + note that in the groff_www macros, .PIMG allows a png to + be specified and converts it automatically to eps for + ps output + NB. -U (unsafe mode) is needed for groff invocations if this + functionality is used -} module Text.Pandoc.Writers.Ms ( writeMs ) where -- cgit v1.2.3 From 4248231a7e43f9a0cbd42aec5448c4d3fc392d22 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 24 Mar 2017 09:56:40 +0100 Subject: Ms writer: fixed rawblock/inline to accept "ms" format. --- src/Text/Pandoc/Writers/Ms.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 84b911e35..f69448dbf 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -233,8 +233,8 @@ blockToMs opts (Para inlines) = do splitSentences inlines return $ text ".LP" $$ contents blockToMs _ b@(RawBlock f str) - | f == Format "man" = return $ text str - | otherwise = do + | f == Format "ms" = return $ text str + | otherwise = do report $ BlockNotRendered b return empty blockToMs _ HorizontalRule = @@ -432,8 +432,8 @@ inlineToMs opts (Math DisplayMath str) = do Right r -> return $ cr <> text ".EQ" $$ text (escapeBar r) $$ text ".EN" inlineToMs _ il@(RawInline f str) - | f == Format "man" = return $ text str - | otherwise = do + | f == Format "ms" = return $ text str + | otherwise = do report $ InlineNotRendered il return empty inlineToMs _ (LineBreak) = return $ cr <> text ".br" <> cr -- cgit v1.2.3 From 9945f9129fc46be38b3362fe70950f538191e194 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 24 Mar 2017 10:13:33 +0100 Subject: Ms writer: changed some names and comments, man -> ms. --- src/Text/Pandoc/Writers/Ms.hs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index f69448dbf..a9b73b1ec 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -25,7 +25,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Stability : alpha Portability : portable -Conversion of 'Pandoc' documents to groff man page format. +Conversion of 'Pandoc' documents to groff ms format. TODO: @@ -99,7 +99,7 @@ writeMs :: PandocMonad m => WriterOptions -> Pandoc -> m String writeMs opts document = evalStateT (pandocToMs opts document) defaultWriterState --- | Return groff man representation of document. +-- | Return groff ms representation of document. pandocToMs :: PandocMonad m => WriterOptions -> Pandoc -> MS m String pandocToMs opts (Pandoc meta blocks) = do let colwidth = if writerWrapText opts == WrapAuto @@ -139,8 +139,8 @@ pandocToMs opts (Pandoc meta blocks) = do Just tpl -> return $ renderTemplate' tpl context -- | Association list of characters to escape. -manEscapes :: Map.Map Char String -manEscapes = Map.fromList $ +msEscapes :: Map.Map Char String +msEscapes = Map.fromList $ [ ('\160', "\\ ") , ('\'', "\\[aq]") , ('’', "'") @@ -155,7 +155,7 @@ manEscapes = Map.fromList $ ] escapeChar :: Char -> String -escapeChar c = case Map.lookup c manEscapes of +escapeChar c = case Map.lookup c msEscapes of Just s -> s Nothing -> [c] @@ -298,7 +298,7 @@ blockToMs opts (DefinitionList items) = do contents <- mapM (definitionListItemToMs opts) items return (vcat contents) --- | Convert bullet list item (list of blocks) to man. +-- | Convert bullet list item (list of blocks) to ms. bulletListItemToMs :: PandocMonad m => WriterOptions -> [Block] -> MS m Doc bulletListItemToMs _ [] = return empty bulletListItemToMs opts ((Para first):rest) = @@ -316,7 +316,7 @@ bulletListItemToMs opts (first:rest) = do rest' <- blockListToMs opts rest return $ text "\\[bu] .RS 2" $$ first' $$ rest' $$ text ".RE" --- | Convert ordered list item (a list of blocks) to man. +-- | Convert ordered list item (a list of blocks) to ms. orderedListItemToMs :: PandocMonad m => WriterOptions -- ^ options -> String -- ^ order marker for list item @@ -336,7 +336,7 @@ orderedListItemToMs opts num indent (first:rest) = do else text ".RS 4" $$ rest' $$ text ".RE" return $ first'' $$ rest'' --- | Convert definition list item (label, list of blocks) to man. +-- | Convert definition list item (label, list of blocks) to ms. definitionListItemToMs :: PandocMonad m => WriterOptions -> ([Inline],[[Block]]) @@ -356,7 +356,7 @@ definitionListItemToMs opts (label, defs) = do return $ first' $$ text ".RS" $$ rest' $$ text ".RE" return $ nowrap (text ".IP \"" <> labelText <> text "\"") $$ contents --- | Convert list of Pandoc block elements to man. +-- | Convert list of Pandoc block elements to ms. blockListToMs :: PandocMonad m => WriterOptions -- ^ Options -> [Block] -- ^ List of block elements @@ -380,7 +380,7 @@ inlineListToMs' opts lst = do y <- handleNotes opts empty return $ x <> y --- | Convert Pandoc inline element to man. +-- | Convert Pandoc inline element to ms. inlineToMs :: PandocMonad m => WriterOptions -> Inline -> MS m Doc inlineToMs opts (Span _ ils) = inlineListToMs opts ils inlineToMs opts (Emph lst) = -- cgit v1.2.3 From 438e8686cf707cc0fe338678111a63fdc1fc5bf2 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 24 Mar 2017 16:11:56 +0100 Subject: Markdown writer: don't emit a simple table if `simple_tables` disabled. Closes #3529. --- src/Text/Pandoc/Writers/Markdown.hs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 88dd53808..69a3fd8b4 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -555,14 +555,14 @@ blockToMarkdown' opts t@(Table caption aligns widths headers rows) = do case True of _ | isSimple && isEnabled Ext_simple_tables opts -> fmap (nest 2,) $ - pandocTable opts (all null headers) aligns' widths' + pandocTable opts False (all null headers) aligns' widths' rawHeaders rawRows | isSimple && isEnabled Ext_pipe_tables opts -> fmap (id,) $ pipeTable (all null headers) aligns' rawHeaders rawRows | not hasBlocks && isEnabled Ext_multiline_tables opts -> fmap (nest 2,) $ - pandocTable opts (all null headers) aligns' widths' + pandocTable opts True (all null headers) aligns' widths' rawHeaders rawRows | isEnabled Ext_grid_tables opts && writerColumns opts >= 8 * numcols -> (id,) <$> @@ -633,9 +633,10 @@ pipeTable headless aligns rawHeaders rawRows = do let body = vcat $ map torow rawRows return $ header $$ border $$ body -pandocTable :: PandocMonad m => WriterOptions -> Bool -> [Alignment] -> [Double] +pandocTable :: PandocMonad m + => WriterOptions -> Bool -> Bool -> [Alignment] -> [Double] -> [Doc] -> [[Doc]] -> MD m Doc -pandocTable opts headless aligns widths rawHeaders rawRows = do +pandocTable opts multiline headless aligns widths rawHeaders rawRows = do let isSimple = all (==0) widths let alignHeader alignment = case alignment of AlignLeft -> lblock @@ -664,10 +665,9 @@ pandocTable opts headless aligns widths rawHeaders rawRows = do (zipWith3 alignHeader aligns widthsInChars) let rows' = map makeRow rawRows let head' = makeRow rawHeaders - let maxRowHeight = maximum $ map height (head':rows') let underline = cat $ intersperse (text " ") $ map (\width -> text (replicate width '-')) widthsInChars - let border = if maxRowHeight > 1 + let border = if multiline then text (replicate (sum widthsInChars + length widthsInChars - 1) '-') else if headless @@ -676,9 +676,9 @@ pandocTable opts headless aligns widths rawHeaders rawRows = do let head'' = if headless then empty else border <> cr <> head' - let body = if isSimple - then vcat rows' - else vsep rows' + let body = if multiline + then vsep rows' + else vcat rows' let bottom = if headless then underline else border -- cgit v1.2.3 From b98a05d604ab4353c2e684beecf669b182d88906 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 24 Mar 2017 16:23:49 +0100 Subject: LaTeX reader: be more picky about beamer angle arguments. We now only allow them if they contain only numbers, spaces, `-`, and `,`. --- 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 ae441a387..0b34be2a6 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -948,7 +948,7 @@ skipopts = skipMany rawopt rawangle :: PandocMonad m => LP m () rawangle = try $ do char '<' - skipMany (noneOf ">") + skipMany (oneOf "0123456789,- \t") char '>' return () -- cgit v1.2.3 From 6dd7be72500326a14701efae6a63b4982a93350a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 24 Mar 2017 16:31:34 +0100 Subject: Revert "LaTeX reader: be more picky about beamer angle arguments." This reverts commit b98a05d604ab4353c2e684beecf669b182d88906. --- 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 0b34be2a6..ae441a387 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -948,7 +948,7 @@ skipopts = skipMany rawopt rawangle :: PandocMonad m => LP m () rawangle = try $ do char '<' - skipMany (oneOf "0123456789,- \t") + skipMany (noneOf ">") char '>' return () -- cgit v1.2.3 From a7ae4b1ee2a164c916d1ded7d393d18f71e0fc86 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 24 Mar 2017 17:24:25 +0100 Subject: Ms writer: support --toc, date, abstract. --- src/Text/Pandoc/Writers/Ms.hs | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index a9b73b1ec..40a33b423 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -40,7 +40,7 @@ TODO: [ ] tight/loose list distinction [ ] internal hyperlinks (this seems to be possible since they exist in the groff manual PDF version) -[ ] better template, with configurable page number, table of contents, +[ ] better template, with configurable page number, columns, etc. [ ] support for images? gropdf (and maybe pdfroff) supports the tag \X'pdf: pdfpic file alignment width height line-length' @@ -64,11 +64,10 @@ import qualified Data.Map as Map import Data.List ( stripPrefix, intersperse, intercalate, sort ) import Data.Maybe (fromMaybe) import Text.Pandoc.Pretty -import Text.Pandoc.Builder (deleteMeta) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Logging import Control.Monad.State -import Data.Char ( isDigit, isLower, isUpper, toUpper ) +import Data.Char ( isLower, isUpper, toUpper ) import Text.TeXMath (writeEqn) data WriterState = WriterState { stHasInlineMath :: Bool @@ -106,33 +105,18 @@ pandocToMs opts (Pandoc meta blocks) = do then Just $ writerColumns opts else Nothing let render' = render colwidth - titleText <- inlineListToMs' opts $ docTitle meta - let title' = render' titleText - let setFieldsFromTitle = - case break (== ' ') title' of - (cmdName, rest) -> case reverse cmdName of - (')':d:'(':xs) | isDigit d -> - defField "title" (reverse xs) . - defField "section" [d] . - case splitBy (=='|') rest of - (ft:hds) -> - defField "footer" (trim ft) . - defField "header" - (trim $ concat hds) - [] -> id - _ -> defField "title" title' metadata <- metaToJSON opts (fmap (render colwidth) . blockListToMs opts) (fmap (render colwidth) . inlineListToMs' opts) - $ deleteMeta "title" meta + meta body <- blockListToMs opts blocks let main = render' body hasInlineMath <- gets stHasInlineMath let context = defField "body" main - $ setFieldsFromTitle $ defField "has-inline-math" hasInlineMath $ defField "hyphenate" True $ defField "pandoc-version" pandocVersion + $ defField "toc" (writerTableOfContents opts) $ metadata case writerTemplate opts of Nothing -> return main @@ -241,10 +225,16 @@ blockToMs _ HorizontalRule = return $ text ".HLINE" blockToMs opts (Header level _ inlines) = do contents <- inlineListToMs' opts inlines + let tocEntry = if writerTableOfContents opts && + level <= writerTOCDepth opts + then text ".XS" $$ + (text (replicate level '\t') <> contents) $$ + text ".XE" + else empty let heading = if writerNumberSections opts then ".NH" else ".SH" - return $ text heading <> space <> text (show level) $$ contents + return $ text heading <> space <> text (show level) $$ contents $$ tocEntry blockToMs _ (CodeBlock _ str) = return $ text ".IP" $$ text ".nf" $$ -- cgit v1.2.3 From 1e13e98ecfba2d78a88ba42bb54c8cdc15929e2d Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Fri, 24 Mar 2017 21:19:55 +0100 Subject: Ensure compatibility with hslua 0.5.* The 0.5.0 release of hslua fixes problems with lua C modules on linux. The signature of the `loadstring` function changed, so a compatibility wrapper is introduced to allow both 0.4.* and 0.5.* versions to be used. --- src/Text/Pandoc/Lua/Compat.hs | 40 +++++++++++++++++++++++++++++++++++++ src/Text/Pandoc/Lua/PandocModule.hs | 5 +++-- src/Text/Pandoc/Writers/Custom.hs | 3 ++- 3 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 src/Text/Pandoc/Lua/Compat.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/Compat.hs b/src/Text/Pandoc/Lua/Compat.hs new file mode 100644 index 000000000..998d8d032 --- /dev/null +++ b/src/Text/Pandoc/Lua/Compat.hs @@ -0,0 +1,40 @@ +{- +Copyright © 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> + +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 +-} +{-# LANGUAGE CPP #-} +{- | + Module : Text.Pandoc.Lua.Compat + Copyright : Copyright © 2017 Albert Krewinkel + License : GNU GPL, version 2 or above + + Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> + Stability : alpha + +Compatibility helpers for hslua +-} +module Text.Pandoc.Lua.Compat ( loadstring ) where + +import Scripting.Lua ( LuaState ) +import qualified Scripting.Lua as Lua + +-- | Interpret string as lua code and load into the lua environment. +loadstring :: LuaState -> String -> String -> IO Int +#if MIN_VERSION_hslua(0,5,0) +loadstring lua script _ = Lua.loadstring lua script +#else +loadstring lua script cn = Lua.loadstring lua script cn +#endif diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index 5b2e82103..87d1fa6b9 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -28,7 +28,8 @@ Pandoc module for lua. module Text.Pandoc.Lua.PandocModule ( pushPandocModule ) where import Data.ByteString.Char8 ( unpack ) -import Scripting.Lua ( LuaState, loadstring, call) +import Scripting.Lua ( LuaState, call) +import Text.Pandoc.Lua.Compat ( loadstring ) import Text.Pandoc.Shared ( readDataFile ) @@ -36,7 +37,7 @@ import Text.Pandoc.Shared ( readDataFile ) pushPandocModule :: LuaState -> IO () pushPandocModule lua = do script <- pandocModuleScript - status <- loadstring lua script "cn" + status <- loadstring lua script "pandoc.lua" if (status /= 0) then return () else do diff --git a/src/Text/Pandoc/Writers/Custom.hs b/src/Text/Pandoc/Writers/Custom.hs index d7374b68b..9bed1dcd3 100644 --- a/src/Text/Pandoc/Writers/Custom.hs +++ b/src/Text/Pandoc/Writers/Custom.hs @@ -47,6 +47,7 @@ import Data.Typeable import GHC.IO.Encoding (getForeignEncoding, setForeignEncoding, utf8) import Scripting.Lua (LuaState, StackValue, callfunc) import qualified Scripting.Lua as Lua +import Text.Pandoc.Lua.Compat ( loadstring ) import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Templates @@ -186,7 +187,7 @@ writeCustom luaFile opts doc@(Pandoc meta _) = do setForeignEncoding utf8 lua <- Lua.newstate Lua.openlibs lua - status <- Lua.loadstring lua luaScript luaFile + status <- loadstring lua luaScript luaFile -- check for error in lua script (later we'll change the return type -- to handle this more gracefully): when (status /= 0) $ -- cgit v1.2.3 From c13cfe8f5d52c2381a323c620b5ba447544e9df9 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 24 Mar 2017 21:57:41 +0100 Subject: Ms writer: Use indented paragraphs after first in section. Note that the current indentation setting is 0; see the settings in the template. --- src/Text/Pandoc/Writers/Ms.hs | 48 +++++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 11 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 40a33b423..af31014c5 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -71,6 +71,7 @@ import Data.Char ( isLower, isUpper, toUpper ) import Text.TeXMath (writeEqn) data WriterState = WriterState { stHasInlineMath :: Bool + , stFirstPara :: Bool , stNotes :: [Note] , stInNote :: Bool , stSmallCaps :: Bool @@ -79,6 +80,7 @@ data WriterState = WriterState { stHasInlineMath :: Bool defaultWriterState :: WriterState defaultWriterState = WriterState{ stHasInlineMath = False + , stFirstPara = True , stNotes = [] , stInNote = False , stSmallCaps = False @@ -209,21 +211,29 @@ blockToMs :: PandocMonad m -> Block -- ^ Block element -> MS m Doc blockToMs _ Null = return empty -blockToMs opts (Div _ bs) = blockListToMs opts bs +blockToMs opts (Div _ bs) = do + setFirstPara + res <- blockListToMs opts bs + setFirstPara + return res blockToMs opts (Plain inlines) = liftM vcat $ mapM (inlineListToMs' opts) $ splitSentences inlines blockToMs opts (Para inlines) = do + firstPara <- gets stFirstPara + resetFirstPara contents <- liftM vcat $ mapM (inlineListToMs' opts) $ splitSentences inlines - return $ text ".LP" $$ contents + return $ text (if firstPara then ".LP" else ".PP") $$ contents blockToMs _ b@(RawBlock f str) | f == Format "ms" = return $ text str | otherwise = do report $ BlockNotRendered b return empty -blockToMs _ HorizontalRule = +blockToMs _ HorizontalRule = do + resetFirstPara return $ text ".HLINE" blockToMs opts (Header level _ inlines) = do + setFirstPara contents <- inlineListToMs' opts inlines let tocEntry = if writerTableOfContents opts && level <= writerTOCDepth opts @@ -234,18 +244,24 @@ blockToMs opts (Header level _ inlines) = do let heading = if writerNumberSections opts then ".NH" else ".SH" + modify $ \st -> st{ stFirstPara = True } return $ text heading <> space <> text (show level) $$ contents $$ tocEntry -blockToMs _ (CodeBlock _ str) = return $ - text ".IP" $$ - text ".nf" $$ - text "\\f[C]" $$ - text (escapeCode str) $$ - text "\\f[]" $$ - text ".fi" +blockToMs _ (CodeBlock _ str) = do + setFirstPara + return $ + text ".IP" $$ + text ".nf" $$ + text "\\f[C]" $$ + text (escapeCode str) $$ + text "\\f[]" $$ + text ".fi" blockToMs opts (LineBlock ls) = do + resetFirstPara blockToMs opts $ Para $ intercalate [LineBreak] ls blockToMs opts (BlockQuote blocks) = do + setFirstPara contents <- blockListToMs opts blocks + setFirstPara return $ text ".RS" $$ contents $$ text ".RE" blockToMs opts (Table caption alignments widths headers rows) = let aligncode AlignLeft = "l" @@ -271,21 +287,25 @@ blockToMs opts (Table caption alignments widths headers rows) = body <- mapM (\row -> do cols <- mapM (blockListToMs opts) row return $ makeRow cols) rows + setFirstPara return $ text ".PP" $$ caption' $$ text ".TS" $$ text "tab(@);" $$ coldescriptions $$ colheadings' $$ vcat body $$ text ".TE" blockToMs opts (BulletList items) = do contents <- mapM (bulletListItemToMs opts) items + setFirstPara return (vcat contents) blockToMs opts (OrderedList attribs items) = do let markers = take (length items) $ orderedListMarkers attribs let indent = 1 + (maximum $ map length markers) contents <- mapM (\(num, item) -> orderedListItemToMs opts num indent item) $ zip markers items + setFirstPara return (vcat contents) blockToMs opts (DefinitionList items) = do contents <- mapM (definitionListItemToMs opts) items + setFirstPara return (vcat contents) -- | Convert bullet list item (list of blocks) to ms. @@ -344,7 +364,7 @@ definitionListItemToMs opts (label, defs) = do mapM (\item -> blockToMs opts item) rest first' <- blockToMs opts first return $ first' $$ text ".RS" $$ rest' $$ text ".RE" - return $ nowrap (text ".IP \"" <> labelText <> text "\"") $$ contents + return $ nowrap (text ".IP " <> doubleQuotes labelText) $$ contents -- | Convert list of Pandoc block elements to ms. blockListToMs :: PandocMonad m @@ -490,3 +510,9 @@ withFontFeature c action = do modify $ \st -> st{ stFontFeatures = Map.adjust not c $ stFontFeatures st } end <- fontChange return $ begin <> d <> end + +setFirstPara :: PandocMonad m => MS m () +setFirstPara = modify $ \st -> st{ stFirstPara = True } + +resetFirstPara :: PandocMonad m => MS m () +resetFirstPara = modify $ \st -> st{ stFirstPara = False } -- cgit v1.2.3 From 980cc50aff06c3181cc7ddc53cbe726470a692f4 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 25 Mar 2017 10:12:10 +0100 Subject: Ms writer: Got figures with ps and eps images working. --- src/Text/Pandoc/Writers/Ms.hs | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index af31014c5..438282437 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -65,10 +65,12 @@ import Data.List ( stripPrefix, intersperse, intercalate, sort ) import Data.Maybe (fromMaybe) import Text.Pandoc.Pretty import Text.Pandoc.Class (PandocMonad, report) +import Text.Pandoc.ImageSize import Text.Pandoc.Logging import Control.Monad.State import Data.Char ( isLower, isUpper, toUpper ) import Text.TeXMath (writeEqn) +import System.FilePath (takeExtension) data WriterState = WriterState { stHasInlineMath :: Bool , stFirstPara :: Bool @@ -218,6 +220,25 @@ blockToMs opts (Div _ bs) = do return res blockToMs opts (Plain inlines) = liftM vcat $ mapM (inlineListToMs' opts) $ splitSentences inlines +blockToMs opts (Para [Image attr alt (src,_tit)]) + | let ext = takeExtension src in (ext == ".ps" || ext == ".eps") = do + let (mbW,mbH) = (inPoints opts <$> dimension Width attr, + inPoints opts <$> dimension Height attr) + let sizeAttrs = case (mbW, mbH) of + (Just wp, Nothing) -> space <> doubleQuotes + (text (show (floor wp :: Int) ++ "p")) + (Just wp, Just hp) -> space <> doubleQuotes + (text (show (floor wp :: Int) ++ "p")) <> + space <> + doubleQuotes (text (show (floor hp :: Int))) + _ -> empty + capt <- inlineListToMs' opts alt + return $ nowrap (text ".PSPIC -C " <> + doubleQuotes (text (escapeString src)) <> + sizeAttrs) $$ + text ".ce 1000" $$ + capt $$ + text ".ce 0" blockToMs opts (Para inlines) = do firstPara <- gets stFirstPara resetFirstPara @@ -465,11 +486,8 @@ inlineToMs opts (Link _ txt (src, _)) = do let linknote = [Plain [Str src]] inlineListToMs opts (txt ++ [Note linknote]) inlineToMs opts (Image attr alternate (source, tit)) = do - let txt = if (null alternate) || (alternate == [Str ""]) || - (alternate == [Str source]) -- to prevent autolinks - then [Str "image"] - else alternate - linkPart <- inlineToMs opts (Link attr txt (source, tit)) + let alt = if null alternate then [Str "image"] else alternate + linkPart <- inlineToMs opts (Link attr alt (source, tit)) return $ char '[' <> text "IMAGE: " <> linkPart <> char ']' inlineToMs _ (Note contents) = do modify $ \st -> st{ stNotes = contents : stNotes st } -- cgit v1.2.3 From 2e20129903d9420d677a6304212f09a1f99a080b Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 25 Mar 2017 10:35:05 +0100 Subject: Ms. writer: links: use footnote only for absolute URIs. --- src/Text/Pandoc/Writers/Ms.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 438282437..6e8c1bd63 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -71,6 +71,7 @@ import Control.Monad.State import Data.Char ( isLower, isUpper, toUpper ) import Text.TeXMath (writeEqn) import System.FilePath (takeExtension) +import Network.URI (isURI) data WriterState = WriterState { stHasInlineMath :: Bool , stFirstPara :: Bool @@ -477,7 +478,8 @@ inlineToMs opts (Link _ txt (src, _)) = do [Str s] | escapeURI s == srcSuffix -> return $ text (escapeString srcSuffix) - _ | inNote -> do + _ | not (isURI src) -> inlineListToMs opts txt + | inNote -> do -- avoid a note in a note! contents <- inlineListToMs opts txt return $ contents <> space <> char '(' <> -- cgit v1.2.3 From c941a00cac0655349b6c03618bc096ac07237779 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 25 Mar 2017 19:30:24 +0100 Subject: Ms writer: improved pdf metadata. --- src/Text/Pandoc/Writers/Ms.hs | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 6e8c1bd63..7d0c278a6 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -117,11 +117,15 @@ pandocToMs opts (Pandoc meta blocks) = do body <- blockListToMs opts blocks let main = render' body hasInlineMath <- gets stHasInlineMath + let titleMeta = (escapeString . stringify) $ docTitle meta + let authorsMeta = map (escapeString . stringify) $ docAuthors meta let context = defField "body" main $ defField "has-inline-math" hasInlineMath $ defField "hyphenate" True $ defField "pandoc-version" pandocVersion $ defField "toc" (writerTableOfContents opts) + $ defField "title-meta" titleMeta + $ defField "author-meta" (intercalate "; " authorsMeta) $ metadata case writerTemplate opts of Nothing -> return main -- cgit v1.2.3 From ed6249bd0b36613c293f9a7c5cb6df869d3b52e8 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 25 Mar 2017 20:53:32 +0100 Subject: Ms writer: use light gray for strikeout. Pending groff definitions for striking out an arbitrary section of text (not just a few words). --- src/Text/Pandoc/Writers/Ms.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 7d0c278a6..d5f323b5e 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -425,7 +425,9 @@ inlineToMs opts (Strong lst) = withFontFeature 'B' (inlineListToMs opts lst) inlineToMs opts (Strikeout lst) = do contents <- inlineListToMs opts lst - return $ text "[STRIKEOUT:" <> contents <> char ']' + -- we use grey color instead of strikeout, which seems quite + -- hard to do in groff for arbitrary bits of text + return $ text "\\m[strikecolor]" <> contents <> text "\\m[]" inlineToMs opts (Superscript lst) = do contents <- inlineListToMs opts lst return $ text "\\*{" <> contents <> text "\\*}" -- cgit v1.2.3 From ce4bb68967eee557f49e78f276a277d3c4db0a13 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 25 Mar 2017 20:57:45 +0100 Subject: Ms writer: revise TODO comments. --- src/Text/Pandoc/Writers/Ms.hs | 16 ---------------- 1 file changed, 16 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index d5f323b5e..b95878c30 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -29,27 +29,11 @@ Conversion of 'Pandoc' documents to groff ms format. TODO: -[ ] for links, emails, consider using macros from www: man groff_www - alo has a raw html macro and support for images. -[ ] consider using a custom macro package for pandoc (perhaps if - a variable is set?) [ ] is there a better way to do strikeout? [ ] options for hyperlink rendering (currently footnote) -[ ] can we get prettier output using .B, etc. instead of - the inline forms? [ ] tight/loose list distinction [ ] internal hyperlinks (this seems to be possible since they exist in the groff manual PDF version) -[ ] better template, with configurable page number, - columns, etc. -[ ] support for images? gropdf (and maybe pdfroff) supports the tag - \X'pdf: pdfpic file alignment width height line-length' - and also seems to support bookmarks. - note that in the groff_www macros, .PIMG allows a png to - be specified and converts it automatically to eps for - ps output - NB. -U (unsafe mode) is needed for groff invocations if this - functionality is used -} module Text.Pandoc.Writers.Ms ( writeMs ) where -- cgit v1.2.3 From 1d659bec015774d0ec8bdd54d4975bdcacf657a2 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 25 Mar 2017 22:16:31 +0100 Subject: Ms writer: Implement header identifiers and internal links. --- src/Text/Pandoc/PDF.hs | 2 +- src/Text/Pandoc/Writers/Ms.hs | 36 +++++++++++++++++++++++++++--------- 2 files changed, 28 insertions(+), 10 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index 164ab0244..7e44adeda 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -110,7 +110,7 @@ makePDF "pdfroff" writer opts verbosity _mediabag doc = liftIO $ do source <- runIOorExplode $ do setVerbosity verbosity writer opts doc - let args = ["-ms", "-e", "-t", "-k", "-KUTF-8", "-i"] + let args = ["-ms", "-mpdfmark", "-e", "-t", "-k", "-KUTF-8", "-i"] ms2pdf verbosity args source makePDF program writer opts verbosity mediabag doc = do let withTemp = if takeBaseName program == "context" diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index b95878c30..2ff83ed96 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -29,11 +29,14 @@ Conversion of 'Pandoc' documents to groff ms format. TODO: +[ ] is there a way to avoid the extra space between internal links + and following punctuation? +[ ] manually create TOC including internal links and pdf outline + bookmarks? See + http://pipeline.lbl.gov/code/3rd_party/licenses.win/groff/1.19.2/pdf/pdfmark.pdf [ ] is there a better way to do strikeout? [ ] options for hyperlink rendering (currently footnote) [ ] tight/loose list distinction -[ ] internal hyperlinks (this seems to be possible since - they exist in the groff manual PDF version) -} module Text.Pandoc.Writers.Ms ( writeMs ) where @@ -242,20 +245,28 @@ blockToMs _ b@(RawBlock f str) blockToMs _ HorizontalRule = do resetFirstPara return $ text ".HLINE" -blockToMs opts (Header level _ inlines) = do +blockToMs opts (Header level (ident,classes,_) inlines) = do setFirstPara contents <- inlineListToMs' opts inlines + let anchor = if null ident + then empty + else nowrap $ + text ".pdfhref M " <> doubleQuotes (text ident) let tocEntry = if writerTableOfContents opts && level <= writerTOCDepth opts then text ".XS" $$ (text (replicate level '\t') <> contents) $$ text ".XE" else empty - let heading = if writerNumberSections opts + let heading = if writerNumberSections opts && + "unnumbered" `notElem` classes then ".NH" else ".SH" modify $ \st -> st{ stFirstPara = True } - return $ text heading <> space <> text (show level) $$ contents $$ tocEntry + return $ anchor $$ + (text heading <> space <> text (show level)) $$ + contents $$ + tocEntry blockToMs _ (CodeBlock _ str) = do setFirstPara return $ @@ -388,8 +399,6 @@ blockListToMs opts blocks = inlineListToMs :: PandocMonad m => WriterOptions -> [Inline] -> MS m Doc -- if list starts with ., insert a zero-width character \& so it -- won't be interpreted as markup if it falls at the beginning of a line. -inlineListToMs opts lst@(Str ('.':_) : _) = mapM (inlineToMs opts) lst >>= - (return . (text "\\&" <>) . hcat) inlineListToMs opts lst = hcat <$> mapM (inlineToMs opts) lst -- This version to be used when there is no further inline content; @@ -435,10 +444,13 @@ inlineToMs opts (Cite _ lst) = inlineToMs _ (Code _ str) = withFontFeature 'C' (return $ text $ escapeCode str) inlineToMs _ (Str str) = do + let shim = case str of + '.':_ -> afterBreak "\\&" + _ -> empty smallcaps <- gets stSmallCaps if smallcaps - then return $ text $ toSmallCaps str - else return $ text $ escapeString str + then return $ shim <> text (toSmallCaps str) + else return $ shim <> text (escapeString str) inlineToMs opts (Math InlineMath str) = do modify $ \st -> st{ stHasInlineMath = True } res <- convertMath writeEqn InlineMath str @@ -461,6 +473,12 @@ inlineToMs _ il@(RawInline f str) inlineToMs _ (LineBreak) = return $ cr <> text ".br" <> cr inlineToMs opts SoftBreak = handleNotes opts cr inlineToMs opts Space = handleNotes opts space +inlineToMs opts (Link _ txt ('#':ident, _)) = do + -- internal link + contents <- inlineListToMs' opts{ writerWrapText = WrapNone } txt + return $ text "\\c" <> cr <> nowrap (text ".pdfhref L -D " <> + doubleQuotes (text ident) <> space <> + doubleQuotes contents) <> cr inlineToMs opts (Link _ txt (src, _)) = do let srcSuffix = fromMaybe src (stripPrefix "mailto:" src) inNote <- gets stInNote -- cgit v1.2.3 From c44ad0710a6aecc064be39d07a0dda69eac2f1d8 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 25 Mar 2017 22:23:15 +0100 Subject: Ms writer: added TODO comment. --- src/Text/Pandoc/Writers/Ms.hs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 2ff83ed96..d70968d08 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -31,6 +31,9 @@ TODO: [ ] is there a way to avoid the extra space between internal links and following punctuation? + internal links followed by a space also cause bad formatting + (a line break) + but adding \c at the end of the link text doesn't seem to work [ ] manually create TOC including internal links and pdf outline bookmarks? See http://pipeline.lbl.gov/code/3rd_party/licenses.win/groff/1.19.2/pdf/pdfmark.pdf -- cgit v1.2.3 From a5ec298989df1f04e06000db6074b4a7c4db74f4 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 26 Mar 2017 10:09:41 +0200 Subject: Ms writer: improved internal links. --- src/Text/Pandoc/Writers/Ms.hs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index d70968d08..9b1119ed4 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -480,8 +480,9 @@ inlineToMs opts (Link _ txt ('#':ident, _)) = do -- internal link contents <- inlineListToMs' opts{ writerWrapText = WrapNone } txt return $ text "\\c" <> cr <> nowrap (text ".pdfhref L -D " <> - doubleQuotes (text ident) <> space <> - doubleQuotes contents) <> cr + doubleQuotes (text ident) <> text " -A " <> + doubleQuotes (text "\\c") <> text " -- " <> contents) <> + cr <> text "\\&" inlineToMs opts (Link _ txt (src, _)) = do let srcSuffix = fromMaybe src (stripPrefix "mailto:" src) inNote <- gets stInNote -- cgit v1.2.3 From e8d8d8721a7abfcf6660ab1981130129bdb9950f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 26 Mar 2017 10:39:20 +0200 Subject: Ms writer: more spacing fixes for internal links. --- src/Text/Pandoc/Writers/Ms.hs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 9b1119ed4..4c6cc5a34 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -376,7 +376,7 @@ definitionListItemToMs :: PandocMonad m -> ([Inline],[[Block]]) -> MS m Doc definitionListItemToMs opts (label, defs) = do - labelText <- inlineListToMs' opts label + labelText <- inlineListToMs' opts $ map breakToSpace label contents <- if null defs then return empty else liftM vcat $ forM defs $ \blocks -> do @@ -478,11 +478,11 @@ inlineToMs opts SoftBreak = handleNotes opts cr inlineToMs opts Space = handleNotes opts space inlineToMs opts (Link _ txt ('#':ident, _)) = do -- internal link - contents <- inlineListToMs' opts{ writerWrapText = WrapNone } txt + contents <- inlineListToMs' opts $ map breakToSpace txt return $ text "\\c" <> cr <> nowrap (text ".pdfhref L -D " <> doubleQuotes (text ident) <> text " -A " <> - doubleQuotes (text "\\c") <> text " -- " <> contents) <> - cr <> text "\\&" + doubleQuotes (text "\\c") <> space <> text "\\") <> cr <> + text " -- " <> doubleQuotes (nowrap contents) <> cr <> text "\\&" inlineToMs opts (Link _ txt (src, _)) = do let srcSuffix = fromMaybe src (stripPrefix "mailto:" src) inNote <- gets stInNote @@ -548,3 +548,8 @@ setFirstPara = modify $ \st -> st{ stFirstPara = True } resetFirstPara :: PandocMonad m => MS m () resetFirstPara = modify $ \st -> st{ stFirstPara = False } + +breakToSpace :: Inline -> Inline +breakToSpace SoftBreak = Space +breakToSpace LineBreak = Space +breakToSpace x = x -- cgit v1.2.3 From d9e8e84be0fb873b90abc8cc43e66f83f17e0d83 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 26 Mar 2017 10:44:34 +0200 Subject: Ms writer: better placement of header anchors. --- src/Text/Pandoc/Writers/Ms.hs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 4c6cc5a34..5fbc54543 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -29,16 +29,12 @@ Conversion of 'Pandoc' documents to groff ms format. TODO: -[ ] is there a way to avoid the extra space between internal links - and following punctuation? - internal links followed by a space also cause bad formatting - (a line break) - but adding \c at the end of the link text doesn't seem to work +[ ] external links + http://pipeline.lbl.gov/code/3rd_party/licenses.win/groff/1.19.2/pdf/pdfmark.pdf [ ] manually create TOC including internal links and pdf outline bookmarks? See http://pipeline.lbl.gov/code/3rd_party/licenses.win/groff/1.19.2/pdf/pdfmark.pdf [ ] is there a better way to do strikeout? -[ ] options for hyperlink rendering (currently footnote) [ ] tight/loose list distinction -} @@ -250,7 +246,7 @@ blockToMs _ HorizontalRule = do return $ text ".HLINE" blockToMs opts (Header level (ident,classes,_) inlines) = do setFirstPara - contents <- inlineListToMs' opts inlines + contents <- inlineListToMs' opts $ map breakToSpace inlines let anchor = if null ident then empty else nowrap $ @@ -266,9 +262,9 @@ blockToMs opts (Header level (ident,classes,_) inlines) = do then ".NH" else ".SH" modify $ \st -> st{ stFirstPara = True } - return $ anchor $$ - (text heading <> space <> text (show level)) $$ + return $ (text heading <> space <> text (show level)) $$ contents $$ + anchor $$ tocEntry blockToMs _ (CodeBlock _ str) = do setFirstPara -- cgit v1.2.3 From 267e1a13eadec5ae7ca6e951fd9ab650487f0f2a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 26 Mar 2017 11:04:33 +0200 Subject: Ms writer: Support external links. Also add config options for link color. --- src/Text/Pandoc/Writers/Ms.hs | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 5fbc54543..1978fc429 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -29,8 +29,6 @@ Conversion of 'Pandoc' documents to groff ms format. TODO: -[ ] external links - http://pipeline.lbl.gov/code/3rd_party/licenses.win/groff/1.19.2/pdf/pdfmark.pdf [ ] manually create TOC including internal links and pdf outline bookmarks? See http://pipeline.lbl.gov/code/3rd_party/licenses.win/groff/1.19.2/pdf/pdfmark.pdf @@ -62,7 +60,6 @@ import Network.URI (isURI) data WriterState = WriterState { stHasInlineMath :: Bool , stFirstPara :: Bool , stNotes :: [Note] - , stInNote :: Bool , stSmallCaps :: Bool , stFontFeatures :: Map.Map Char Bool } @@ -71,7 +68,6 @@ defaultWriterState :: WriterState defaultWriterState = WriterState{ stHasInlineMath = False , stFirstPara = True , stNotes = [] - , stInNote = False , stSmallCaps = False , stFontFeatures = Map.fromList [ ('I',False) @@ -480,21 +476,12 @@ inlineToMs opts (Link _ txt ('#':ident, _)) = do doubleQuotes (text "\\c") <> space <> text "\\") <> cr <> text " -- " <> doubleQuotes (nowrap contents) <> cr <> text "\\&" inlineToMs opts (Link _ txt (src, _)) = do - let srcSuffix = fromMaybe src (stripPrefix "mailto:" src) - inNote <- gets stInNote - case txt of - [Str s] - | escapeURI s == srcSuffix -> - return $ text (escapeString srcSuffix) - _ | not (isURI src) -> inlineListToMs opts txt - | inNote -> do - -- avoid a note in a note! - contents <- inlineListToMs opts txt - return $ contents <> space <> char '(' <> - text (escapeString src) <> char ')' - | otherwise -> do - let linknote = [Plain [Str src]] - inlineListToMs opts (txt ++ [Note linknote]) + -- external link + contents <- inlineListToMs' opts $ map breakToSpace txt + return $ text "\\c" <> cr <> nowrap (text ".pdfhref W -D " <> + doubleQuotes (text src) <> text " -A " <> + doubleQuotes (text "\\c") <> space <> text "\\") <> cr <> + text " -- " <> doubleQuotes (nowrap contents) <> cr <> text "\\&" inlineToMs opts (Image attr alternate (source, tit)) = do let alt = if null alternate then [Str "image"] else alternate linkPart <- inlineToMs opts (Link attr alt (source, tit)) @@ -509,9 +496,8 @@ handleNotes opts fallback = do if null notes then return fallback else do - modify $ \st -> st{ stNotes = [], stInNote = True } + modify $ \st -> st{ stNotes = [] } res <- vcat <$> mapM (handleNote opts) notes - modify $ \st -> st{ stInNote = False } return res handleNote :: PandocMonad m => WriterOptions -> Note -> MS m Doc -- cgit v1.2.3 From b085b9f4a712d902ca9e50e4d4b32b403c56e0ae Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 26 Mar 2017 12:05:27 +0200 Subject: Removed unused imports. --- src/Text/Pandoc/Writers/Ms.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 1978fc429..80b56193c 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -32,6 +32,8 @@ TODO: [ ] manually create TOC including internal links and pdf outline bookmarks? See http://pipeline.lbl.gov/code/3rd_party/licenses.win/groff/1.19.2/pdf/pdfmark.pdf +[ ] use base URL to construct absolute URLs from relative ones for external + links [ ] is there a better way to do strikeout? [ ] tight/loose list distinction -} @@ -45,8 +47,7 @@ import Text.Pandoc.Options import Text.Pandoc.Writers.Math import Text.Printf ( printf ) import qualified Data.Map as Map -import Data.List ( stripPrefix, intersperse, intercalate, sort ) -import Data.Maybe (fromMaybe) +import Data.List ( intersperse, intercalate, sort ) import Text.Pandoc.Pretty import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.ImageSize @@ -55,7 +56,6 @@ import Control.Monad.State import Data.Char ( isLower, isUpper, toUpper ) import Text.TeXMath (writeEqn) import System.FilePath (takeExtension) -import Network.URI (isURI) data WriterState = WriterState { stHasInlineMath :: Bool , stFirstPara :: Bool -- cgit v1.2.3 From 1b967d90365090a8d55e3c400262c8af51a60bf7 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 26 Mar 2017 16:59:29 +0200 Subject: Ms writre: Added some escapes. --- src/Text/Pandoc/Writers/Ms.hs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 80b56193c..ce3907bbe 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -118,12 +118,14 @@ msEscapes :: Map.Map Char String msEscapes = Map.fromList $ [ ('\160', "\\ ") , ('\'', "\\[aq]") - , ('’', "'") + , ('`', "\\`") + , ('\8217', "'") , ('"', "\\\"") , ('\x2014', "\\[em]") , ('\x2013', "\\[en]") , ('\x2026', "\\&...") - , ('|', "\\[u007C]") -- because we use | for inline math + , ('~', "\\[ti]") + , ('^', "\\[ha]") , ('-', "\\-") , ('@', "\\@") , ('\\', "\\\\") -- cgit v1.2.3 From e30d2c700a94177edf223b40999c05f95bc85742 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 26 Mar 2017 16:59:38 +0200 Subject: Ms writer: Use @ instead of | for inline math delimiter. The `|` delimiter had a bad interaction with tbl. See discussion in #1839. --- src/Text/Pandoc/Writers/Ms.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index ce3907bbe..60e1f6041 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -297,7 +297,7 @@ blockToMs opts (Table caption alignments widths headers rows) = alignments iwidths) ++ "." colheadings <- mapM (blockListToMs opts) headers let makeRow cols = text "T{" $$ - (vcat $ intersperse (text "T}@T{") cols) $$ + (vcat $ intersperse (text "T}\tT{") cols) $$ text "T}" let colheadings' = if all null headers then empty @@ -307,7 +307,7 @@ blockToMs opts (Table caption alignments widths headers rows) = return $ makeRow cols) rows setFirstPara return $ text ".PP" $$ caption' $$ - text ".TS" $$ text "tab(@);" $$ coldescriptions $$ + text ".TS" $$ text "delim(@@) tab(\t);" $$ coldescriptions $$ colheadings' $$ vcat body $$ text ".TE" blockToMs opts (BulletList items) = do @@ -453,7 +453,7 @@ inlineToMs opts (Math InlineMath str) = do res <- convertMath writeEqn InlineMath str case res of Left il -> inlineToMs opts il - Right r -> return $ text "|" <> text (escapeBar r) <> text "|" + Right r -> return $ text "@" <> text (escapeBar r) <> text "@" inlineToMs opts (Math DisplayMath str) = do res <- convertMath writeEqn InlineMath str case res of -- cgit v1.2.3 From 10d91c147968d2e4d63b99b5b0342624827f416f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 26 Mar 2017 17:22:00 +0200 Subject: Use file-embed instead of hsb2hs to embed data files. I think template haskell is robust enough now across platforms that this will work. Motivation: file-embed gives us better dependency tracking: if a data file changes, ghc/stack/cabal know to recompile the Data module. This also removes hsb2hs as a build dependency. --- src/Text/Pandoc/Data.hs | 17 +++++++++++++++++ src/Text/Pandoc/Data.hsb | 16 ---------------- 2 files changed, 17 insertions(+), 16 deletions(-) create mode 100644 src/Text/Pandoc/Data.hs delete mode 100644 src/Text/Pandoc/Data.hsb (limited to 'src/Text') diff --git a/src/Text/Pandoc/Data.hs b/src/Text/Pandoc/Data.hs new file mode 100644 index 000000000..df26f5412 --- /dev/null +++ b/src/Text/Pandoc/Data.hs @@ -0,0 +1,17 @@ +{-# LANGUAGE TemplateHaskell #-} + +module Text.Pandoc.Data (dataFiles) where + +import Data.FileEmbed +import qualified Data.ByteString as B +import System.FilePath (splitDirectories) +import qualified System.FilePath.Posix as Posix + +-- We ensure that the data files are stored using Posix +-- path separators (/), even on Windows. +dataFiles :: [(FilePath, B.ByteString)] +dataFiles = map (\(fp, contents) -> + (Posix.joinPath (splitDirectories fp), contents)) dataFiles' + +dataFiles' :: [(FilePath, B.ByteString)] +dataFiles' = ("MANUAL.txt", $(embedFile "MANUAL.txt")) : $(embedDir "data") diff --git a/src/Text/Pandoc/Data.hsb b/src/Text/Pandoc/Data.hsb deleted file mode 100644 index 02c109816..000000000 --- a/src/Text/Pandoc/Data.hsb +++ /dev/null @@ -1,16 +0,0 @@ -{-# LANGUAGE OverloadedStrings #-} --- to be processed using hsb2hs -module Text.Pandoc.Data (dataFiles) where -import qualified Data.ByteString as B -import System.FilePath (splitDirectories) -import qualified System.FilePath.Posix as Posix - --- We ensure that the data files are stored using Posix --- path separators (/), even on Windows. -dataFiles :: [(FilePath, B.ByteString)] -dataFiles = map (\(fp, contents) -> - (Posix.joinPath (splitDirectories fp), contents)) dataFiles' - -dataFiles' :: [(FilePath, B.ByteString)] -dataFiles' = ("MANUAL.txt", %blob "MANUAL.txt") : %blobs "data" - -- cgit v1.2.3 From bd99d9f6affedd476d5fe14bef267197cb3e5f55 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 26 Mar 2017 18:06:15 +0200 Subject: Ms writer: Add PDF outline bookmarks. --- src/Text/Pandoc/Writers/Ms.hs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 60e1f6041..0536e0cfb 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -249,6 +249,8 @@ blockToMs opts (Header level (ident,classes,_) inlines) = do then empty else nowrap $ text ".pdfhref M " <> doubleQuotes (text ident) + let bookmark = text ".pdfhref O " <> text (show level ++ " ") <> + doubleQuotes (text (escapeString (stringify inlines))) let tocEntry = if writerTableOfContents opts && level <= writerTOCDepth opts then text ".XS" $$ @@ -262,6 +264,7 @@ blockToMs opts (Header level (ident,classes,_) inlines) = do modify $ \st -> st{ stFirstPara = True } return $ (text heading <> space <> text (show level)) $$ contents $$ + bookmark $$ anchor $$ tocEntry blockToMs _ (CodeBlock _ str) = do -- cgit v1.2.3 From 453970c6b34ed7cc2f52181e5e77b9182a3639e9 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 26 Mar 2017 19:19:54 +0200 Subject: Text.Pandoc.Data: ensure it compiles even without embed_data_files. In this case we don't depend on file-embed or use TH. --- src/Text/Pandoc/Data.hs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Data.hs b/src/Text/Pandoc/Data.hs index df26f5412..41ff5a0d6 100644 --- a/src/Text/Pandoc/Data.hs +++ b/src/Text/Pandoc/Data.hs @@ -1,11 +1,15 @@ +{-# LANGUAGE CPP #-} +#ifdef EMBED_DATA_FILES {-# LANGUAGE TemplateHaskell #-} - +#endif module Text.Pandoc.Data (dataFiles) where -import Data.FileEmbed +import System.FilePath (FilePath, splitDirectories) import qualified Data.ByteString as B -import System.FilePath (splitDirectories) import qualified System.FilePath.Posix as Posix +#ifdef EMBED_DATA_FILES +import Data.FileEmbed +#endif -- We ensure that the data files are stored using Posix -- path separators (/), even on Windows. @@ -14,4 +18,8 @@ dataFiles = map (\(fp, contents) -> (Posix.joinPath (splitDirectories fp), contents)) dataFiles' dataFiles' :: [(FilePath, B.ByteString)] +#ifdef EMBED_DATA_FILES dataFiles' = ("MANUAL.txt", $(embedFile "MANUAL.txt")) : $(embedDir "data") +#else +dataFiles' = error "dataFiles is only defined when embed_data_files flag set" +#endif -- cgit v1.2.3 From 0ae448e63810b5599c21cac7a83e90dd82f07031 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 26 Mar 2017 20:00:40 +0200 Subject: PDF: when running pdfroff, don't do second pass to relocate toc. --- src/Text/Pandoc/PDF.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index 7e44adeda..696dbacf0 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -110,7 +110,8 @@ makePDF "pdfroff" writer opts verbosity _mediabag doc = liftIO $ do source <- runIOorExplode $ do setVerbosity verbosity writer opts doc - let args = ["-ms", "-mpdfmark", "-e", "-t", "-k", "-KUTF-8", "-i"] + let args = ["-ms", "-mpdfmark", "-e", "-t", "-k", "-KUTF-8", "-i", + "--no-toc-relocation"] ms2pdf verbosity args source makePDF program writer opts verbosity mediabag doc = do let withTemp = if takeBaseName program == "context" -- cgit v1.2.3 From 0eb62f03fe5cc65534a30bb1f66362e85992c55a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 26 Mar 2017 20:01:37 +0200 Subject: Ms writer: Hyperlink table of contents and other improvements. --- src/Text/Pandoc/Writers/Ms.hs | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 0536e0cfb..1fd8bb344 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -116,7 +116,7 @@ pandocToMs opts (Pandoc meta blocks) = do -- | Association list of characters to escape. msEscapes :: Map.Map Char String msEscapes = Map.fromList $ - [ ('\160', "\\ ") + [ ('\160', "\\~") , ('\'', "\\[aq]") , ('`', "\\`") , ('\8217', "'") @@ -245,22 +245,34 @@ blockToMs _ HorizontalRule = do blockToMs opts (Header level (ident,classes,_) inlines) = do setFirstPara contents <- inlineListToMs' opts $ map breakToSpace inlines + let (heading, secnum) = if writerNumberSections opts && + "unnumbered" `notElem` classes + then (".NH", "\\*[SN]") + else (".SH", "") let anchor = if null ident then empty else nowrap $ text ".pdfhref M " <> doubleQuotes (text ident) let bookmark = text ".pdfhref O " <> text (show level ++ " ") <> - doubleQuotes (text (escapeString (stringify inlines))) + doubleQuotes (text $ secnum ++ + (if null secnum + then "" + else " ") ++ + escapeString (stringify inlines)) + let backlink = nowrap (text ".pdfhref L -D " <> + doubleQuotes (text ident) <> space <> text "\\") <> cr <> + text " -- " let tocEntry = if writerTableOfContents opts && level <= writerTOCDepth opts - then text ".XS" $$ - (text (replicate level '\t') <> contents) $$ - text ".XE" + then text ".XS" + $$ backlink <> doubleQuotes ( + nowrap ((text (replicate level '\t') <> + (if null secnum + then empty + else text secnum <> text "\\~\\~") + <> contents))) + $$ text ".XE" else empty - let heading = if writerNumberSections opts && - "unnumbered" `notElem` classes - then ".NH" - else ".SH" modify $ \st -> st{ stFirstPara = True } return $ (text heading <> space <> text (show level)) $$ contents $$ -- cgit v1.2.3 From 51ab1bf2700f23e881aa06c638da4d3606fa22a4 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 26 Mar 2017 20:04:46 +0200 Subject: Ms writer: update TODO comments. --- src/Text/Pandoc/Writers/Ms.hs | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 1fd8bb344..be191c7da 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -29,9 +29,6 @@ Conversion of 'Pandoc' documents to groff ms format. TODO: -[ ] manually create TOC including internal links and pdf outline - bookmarks? See - http://pipeline.lbl.gov/code/3rd_party/licenses.win/groff/1.19.2/pdf/pdfmark.pdf [ ] use base URL to construct absolute URLs from relative ones for external links [ ] is there a better way to do strikeout? -- cgit v1.2.3 From 358dfba8f4eba45fdfdfad2c117de99df8b45bc4 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 26 Mar 2017 20:41:09 +0200 Subject: MediaWiki writer: don't softbreak lines inside list items. Closes #3531. --- src/Text/Pandoc/Writers/MediaWiki.hs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/MediaWiki.hs b/src/Text/Pandoc/Writers/MediaWiki.hs index 594e31e95..def245e38 100644 --- a/src/Text/Pandoc/Writers/MediaWiki.hs +++ b/src/Text/Pandoc/Writers/MediaWiki.hs @@ -409,10 +409,13 @@ inlineToMediaWiki LineBreak = return "<br />\n" inlineToMediaWiki SoftBreak = do wrapText <- gets (writerWrapText . stOptions) + listlevel <- asks listLevel case wrapText of WrapAuto -> return " " WrapNone -> return " " - WrapPreserve -> return "\n" + WrapPreserve -> if null listlevel + then return "\n" + else return " " inlineToMediaWiki Space = return " " -- cgit v1.2.3 From b7782cf8d32d22d8e9bc8bc125be2b2ff1fdd751 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 26 Mar 2017 20:41:35 +0200 Subject: Revert "Text.Pandoc.Data: ensure it compiles even without embed_data_files." This reverts commit 453970c6b34ed7cc2f52181e5e77b9182a3639e9. --- src/Text/Pandoc/Data.hs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Data.hs b/src/Text/Pandoc/Data.hs index 41ff5a0d6..df26f5412 100644 --- a/src/Text/Pandoc/Data.hs +++ b/src/Text/Pandoc/Data.hs @@ -1,15 +1,11 @@ -{-# LANGUAGE CPP #-} -#ifdef EMBED_DATA_FILES {-# LANGUAGE TemplateHaskell #-} -#endif + module Text.Pandoc.Data (dataFiles) where -import System.FilePath (FilePath, splitDirectories) +import Data.FileEmbed import qualified Data.ByteString as B +import System.FilePath (splitDirectories) import qualified System.FilePath.Posix as Posix -#ifdef EMBED_DATA_FILES -import Data.FileEmbed -#endif -- We ensure that the data files are stored using Posix -- path separators (/), even on Windows. @@ -18,8 +14,4 @@ dataFiles = map (\(fp, contents) -> (Posix.joinPath (splitDirectories fp), contents)) dataFiles' dataFiles' :: [(FilePath, B.ByteString)] -#ifdef EMBED_DATA_FILES dataFiles' = ("MANUAL.txt", $(embedFile "MANUAL.txt")) : $(embedDir "data") -#else -dataFiles' = error "dataFiles is only defined when embed_data_files flag set" -#endif -- cgit v1.2.3 From 1fa15c225b515e1fa1c6566f90f1be363a4d770f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 26 Mar 2017 20:48:17 +0200 Subject: Revert "Use file-embed instead of hsb2hs to embed data files." This reverts commit 10d91c147968d2e4d63b99b5b0342624827f416f. --- src/Text/Pandoc/Data.hs | 17 ----------------- src/Text/Pandoc/Data.hsb | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 17 deletions(-) delete mode 100644 src/Text/Pandoc/Data.hs create mode 100644 src/Text/Pandoc/Data.hsb (limited to 'src/Text') diff --git a/src/Text/Pandoc/Data.hs b/src/Text/Pandoc/Data.hs deleted file mode 100644 index df26f5412..000000000 --- a/src/Text/Pandoc/Data.hs +++ /dev/null @@ -1,17 +0,0 @@ -{-# LANGUAGE TemplateHaskell #-} - -module Text.Pandoc.Data (dataFiles) where - -import Data.FileEmbed -import qualified Data.ByteString as B -import System.FilePath (splitDirectories) -import qualified System.FilePath.Posix as Posix - --- We ensure that the data files are stored using Posix --- path separators (/), even on Windows. -dataFiles :: [(FilePath, B.ByteString)] -dataFiles = map (\(fp, contents) -> - (Posix.joinPath (splitDirectories fp), contents)) dataFiles' - -dataFiles' :: [(FilePath, B.ByteString)] -dataFiles' = ("MANUAL.txt", $(embedFile "MANUAL.txt")) : $(embedDir "data") diff --git a/src/Text/Pandoc/Data.hsb b/src/Text/Pandoc/Data.hsb new file mode 100644 index 000000000..02c109816 --- /dev/null +++ b/src/Text/Pandoc/Data.hsb @@ -0,0 +1,16 @@ +{-# LANGUAGE OverloadedStrings #-} +-- to be processed using hsb2hs +module Text.Pandoc.Data (dataFiles) where +import qualified Data.ByteString as B +import System.FilePath (splitDirectories) +import qualified System.FilePath.Posix as Posix + +-- We ensure that the data files are stored using Posix +-- path separators (/), even on Windows. +dataFiles :: [(FilePath, B.ByteString)] +dataFiles = map (\(fp, contents) -> + (Posix.joinPath (splitDirectories fp), contents)) dataFiles' + +dataFiles' :: [(FilePath, B.ByteString)] +dataFiles' = ("MANUAL.txt", %blob "MANUAL.txt") : %blobs "data" + -- cgit v1.2.3 From 49d72444d7223afe8730e1d7fe2ad881cc132b9c Mon Sep 17 00:00:00 2001 From: schrieveslaach <schrieveslaach@online.de> Date: Mon, 27 Mar 2017 21:20:27 +0200 Subject: LaTeX reader: add support for LaTeX subfiles package. Closes #3530. --- 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 ae441a387..9c028faa4 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1008,6 +1008,7 @@ include = do char '\\' name <- try (string "include") <|> try (string "input") + <|> try (string "subfile") <|> string "usepackage" -- skip options skipMany $ try $ char '[' *> manyTill anyChar (char ']') -- cgit v1.2.3 From 5cb18e5dc2707f1ae005e13765882971f5c16016 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Tue, 28 Mar 2017 19:49:49 +0200 Subject: Custom writer: remove old preprocesesor conditionals The minimum required hslua version is 0.4.0, the conditionals inserted to support hslua-0.3.* can hence be removed. --- src/Text/Pandoc/Writers/Custom.hs | 36 ------------------------------------ 1 file changed, 36 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Custom.hs b/src/Text/Pandoc/Writers/Custom.hs index 9bed1dcd3..58b222997 100644 --- a/src/Text/Pandoc/Writers/Custom.hs +++ b/src/Text/Pandoc/Writers/Custom.hs @@ -60,7 +60,6 @@ attrToMap (id',classes,keyvals) = M.fromList : ("class", unwords classes) : keyvals -#if MIN_VERSION_hslua(0,4,0) #if MIN_VERSION_base(4,8,0) instance {-# OVERLAPS #-} StackValue [Char] where #else @@ -71,37 +70,6 @@ instance StackValue [Char] where res <- Lua.peek lua i return $ UTF8.toString `fmap` res valuetype _ = Lua.TSTRING -#else -#if MIN_VERSION_base(4,8,0) -instance {-# OVERLAPS #-} StackValue a => StackValue [a] where -#else -instance StackValue a => StackValue [a] where -#endif - push lua xs = do - Lua.createtable lua (length xs + 1) 0 - let addValue (i, x) = Lua.push lua x >> Lua.rawseti lua (-2) i - mapM_ addValue $ zip [1..] xs - peek lua i = do - top <- Lua.gettop lua - let i' = if i < 0 then top + i + 1 else i - Lua.pushnil lua - lst <- getList lua i' - Lua.pop lua 1 - return (Just lst) - valuetype _ = Lua.TTABLE - -getList :: StackValue a => LuaState -> Int -> IO [a] -getList lua i' = do - continue <- Lua.next lua i' - if continue - then do - next <- Lua.peek lua (-1) - Lua.pop lua 1 - x <- maybe (fail "peek returned Nothing") return next - rest <- getList lua i' - return (x : rest) - else return [] -#endif instance StackValue Format where push lua (Format f) = Lua.push lua (map toLower f) @@ -191,11 +159,7 @@ writeCustom luaFile opts doc@(Pandoc meta _) = do -- check for error in lua script (later we'll change the return type -- to handle this more gracefully): when (status /= 0) $ -#if MIN_VERSION_hslua(0,4,0) Lua.tostring lua 1 >>= throw . PandocLuaException . UTF8.toString -#else - Lua.tostring lua 1 >>= throw . PandocLuaException -#endif Lua.call lua 0 0 -- TODO - call hierarchicalize, so we have that info rendered <- docToCustom lua opts doc -- cgit v1.2.3 From 5fe734d452976ff66ede965984954c6d3755d5c2 Mon Sep 17 00:00:00 2001 From: schrieveslaach <schrieveslaach@online.de> Date: Wed, 29 Mar 2017 14:49:46 +0200 Subject: lstinline with braces can be used (verb cannot be used with braces) (#3535) * Fix lstinline handling: lstinline with braces can be used (verb cannot be used with braces) * Use codeWith and determine the language from lstinline * Improve code * Add another test: convert lstinline without language option --- src/Text/Pandoc/Readers/LaTeX.hs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 9c028faa4..e85002ba3 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -601,7 +601,7 @@ inlineCommands = M.fromList $ , ("thanks", note <$> grouped block) , ("footnote", note <$> grouped block) , ("verb", doverb) - , ("lstinline", skipopts *> doverb) + , ("lstinline", dolstinline) , ("Verb", doverb) , ("url", (unescapeURL <$> braced) >>= \url -> pure (link url "" (str url))) @@ -716,6 +716,13 @@ doverb = do marker <- anyChar code <$> manyTill (satisfy (/='\n')) (char marker) +dolstinline :: PandocMonad m => LP m Inlines +dolstinline = do + options <- option [] keyvals + let classes = maybeToList $ lookup "language" options >>= fromListingsLanguage + marker <- char '{' <|> anyChar + codeWith ("",classes,[]) <$> manyTill (satisfy (/='\n')) (char '}' <|> char marker) + doLHSverb :: PandocMonad m => LP m Inlines doLHSverb = codeWith ("",["haskell"],[]) <$> manyTill (satisfy (/='\n')) (char '|') -- cgit v1.2.3 From 831e1c5edd4703b6ab0953a79980e37ea1bee5dc Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 28 Mar 2017 09:51:30 +0200 Subject: Added JATS writer. * New module Text.Pandoc.Writer.JATS exporting writeJATS. * New output format `jats`. * Added tests. * Revised manual. --- src/Text/Pandoc.hs | 3 + src/Text/Pandoc/Writers/JATS.hs | 429 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 432 insertions(+) create mode 100644 src/Text/Pandoc/Writers/JATS.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index e77bc6d45..977ad1ab4 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -117,6 +117,7 @@ module Text.Pandoc , writeICML , writeDocbook4 , writeDocbook5 + , writeJATS , writeOPML , writeOpenDocument , writeMan @@ -182,6 +183,7 @@ import Text.Pandoc.Writers.CommonMark import Text.Pandoc.Writers.ConTeXt import Text.Pandoc.Writers.Custom import Text.Pandoc.Writers.Docbook +import Text.Pandoc.Writers.JATS import Text.Pandoc.Writers.Docx import Text.Pandoc.Writers.DokuWiki import Text.Pandoc.Writers.EPUB @@ -287,6 +289,7 @@ writers = [ ,("docbook" , StringWriter writeDocbook5) ,("docbook4" , StringWriter writeDocbook4) ,("docbook5" , StringWriter writeDocbook5) + ,("jats" , StringWriter writeJATS) ,("opml" , StringWriter writeOPML) ,("opendocument" , StringWriter writeOpenDocument) ,("latex" , StringWriter writeLaTeX) diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs new file mode 100644 index 000000000..9aaba78e0 --- /dev/null +++ b/src/Text/Pandoc/Writers/JATS.hs @@ -0,0 +1,429 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE PatternGuards #-} +{- +Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu> + +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.Writers.JATS + Copyright : Copyright (C) 2017 John MacFarlane + License : GNU GPL, version 2 or above + + Maintainer : John MacFarlane <jgm@berkeley.edu> + Stability : alpha + Portability : portable + +Conversion of 'Pandoc' documents to JATS XML. +Reference: +https://jats.nlm.nih.gov/publishing/tag-library/1.1d3/element/mml-math.html +-} +module Text.Pandoc.Writers.JATS ( writeJATS ) where +import Control.Monad.Reader +import Data.Char (toLower) +import Data.Generics (everywhere, mkT) +import Data.List (intercalate, isSuffixOf) +import Data.Maybe (fromMaybe) +import qualified Text.Pandoc.Builder as B +import Text.Pandoc.Class (PandocMonad, report) +import Text.Pandoc.Definition +import Text.Pandoc.Highlighting (languages, languagesByExtension) +import Text.Pandoc.Logging +import Text.Pandoc.Options +import Text.Pandoc.Pretty +import Text.Pandoc.Shared +import Text.Pandoc.Templates (renderTemplate') +import Text.Pandoc.Writers.Math +import Text.Pandoc.Writers.Shared +import Text.Pandoc.XML +import Text.Pandoc.MIME (getMimeType) +import Text.TeXMath +import qualified Text.XML.Light as Xml + +data JATSVersion = JATS1_1 + deriving (Eq, Show) + +type DB = ReaderT JATSVersion + +-- | Convert list of authors to a docbook <author> section +authorToJATS :: PandocMonad m => WriterOptions -> [Inline] -> DB m B.Inlines +authorToJATS opts name' = do + name <- render Nothing <$> inlinesToJATS opts name' + let colwidth = if writerWrapText opts == WrapAuto + then Just $ writerColumns opts + else Nothing + return $ B.rawInline "docbook" $ render colwidth $ + if ',' `elem` name + then -- last name first + let (lastname, rest) = break (==',') name + firstname = triml rest in + inTagsSimple "firstname" (text $ escapeStringForXML firstname) <> + inTagsSimple "surname" (text $ escapeStringForXML lastname) + else -- last name last + let namewords = words name + lengthname = length namewords + (firstname, lastname) = case lengthname of + 0 -> ("","") + 1 -> ("", name) + n -> (intercalate " " (take (n-1) namewords), last namewords) + in inTagsSimple "firstname" (text $ escapeStringForXML firstname) $$ + inTagsSimple "surname" (text $ escapeStringForXML lastname) + +writeJATS :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeJATS opts d = + runReaderT (docToJATS opts d) JATS1_1 + +-- | Convert Pandoc document to string in JATS format. +docToJATS :: PandocMonad m => WriterOptions -> Pandoc -> DB m String +docToJATS opts (Pandoc meta blocks) = do + let elements = hierarchicalize blocks + let colwidth = if writerWrapText opts == WrapAuto + then Just $ writerColumns opts + else Nothing + 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 + let startLvl = case writerTopLevelDivision opts' of + TopLevelPart -> -1 + TopLevelChapter -> 0 + TopLevelSection -> 1 + TopLevelDefault -> 1 + auths' <- mapM (authorToJATS opts) $ docAuthors meta + let meta' = B.setMeta "author" auths' meta + metadata <- metaToJSON opts + (fmap (render colwidth . vcat) . + (mapM (elementToJATS opts' startLvl) . + hierarchicalize)) + (fmap (render colwidth) . inlinesToJATS opts') + meta' + main <- (render' . inTagsIndented "body" . vcat) <$> + (mapM (elementToJATS opts' startLvl) elements) + let context = defField "body" main + $ defField "mathml" (case writerHTMLMathMethod opts of + MathML -> True + _ -> False) + $ metadata + return $ case writerTemplate opts of + Nothing -> main + Just tpl -> renderTemplate' tpl context + +-- | Convert an Element to JATS. +elementToJATS :: PandocMonad m => WriterOptions -> Int -> Element -> DB m Doc +elementToJATS opts _ (Blk block) = blockToJATS opts block +elementToJATS opts lvl (Sec _ _num (id',_,kvs) title elements) = do + let idAttr = [("id", writerIdentifierPrefix opts ++ id') | not (null id')] + let otherAttrs = ["sec-type", "specific-use"] + let attribs = idAttr ++ [(k,v) | (k,v) <- kvs, k `elem` otherAttrs] + contents <- mapM (elementToJATS opts (lvl + 1)) elements + title' <- inlinesToJATS opts title + return $ inTags True "sec" attribs $ + inTagsSimple "title" title' $$ vcat contents + +-- | Convert a list of Pandoc blocks to JATS. +blocksToJATS :: PandocMonad m => WriterOptions -> [Block] -> DB m Doc +blocksToJATS opts = fmap vcat . mapM (blockToJATS opts) + +-- | Auxiliary function to convert Plain block to Para. +plainToPara :: Block -> Block +plainToPara (Plain x) = Para x +plainToPara x = x + +-- | Convert a list of pairs of terms and definitions into a list of +-- JATS varlistentrys. +deflistItemsToJATS :: PandocMonad m + => WriterOptions -> [([Inline],[[Block]])] -> DB m Doc +deflistItemsToJATS opts items = + vcat <$> mapM (\(term, defs) -> deflistItemToJATS opts term defs) items + +-- | Convert a term and a list of blocks into a JATS varlistentry. +deflistItemToJATS :: PandocMonad m + => WriterOptions -> [Inline] -> [[Block]] -> DB m Doc +deflistItemToJATS opts term defs = do + term' <- inlinesToJATS opts term + def' <- blocksToJATS opts $ concatMap (map plainToPara) defs + return $ inTagsIndented "def-item" $ + inTagsIndented "term" term' $$ + inTagsIndented "def" def' + +-- | Convert a list of lists of blocks to a list of JATS list items. +listItemsToJATS :: PandocMonad m + => WriterOptions -> (Maybe [String]) -> [[Block]] -> DB m Doc +listItemsToJATS opts markers items = + case markers of + Nothing -> vcat <$> mapM (listItemToJATS opts Nothing) items + Just ms -> vcat <$> zipWithM (listItemToJATS opts) (map Just ms) items + +-- | Convert a list of blocks into a JATS list item. +listItemToJATS :: PandocMonad m + => WriterOptions -> (Maybe String) -> [Block] -> DB m Doc +listItemToJATS opts mbmarker item = do + contents <- blocksToJATS opts item + return $ inTagsIndented "list-item" $ + maybe empty (\lbl -> inTagsIndented "label" (text lbl)) mbmarker + $$ contents + +-- | Convert a Pandoc block element to JATS. +blockToJATS :: PandocMonad m => WriterOptions -> Block -> DB m Doc +blockToJATS _ Null = return empty +-- Add ids to paragraphs in divs with ids - this is needed for +-- pandoc-citeproc to get link anchors in bibliographies: +blockToJATS opts (Div (ident,_,_) [Para lst]) = + let attribs = [("id", ident) | not (null ident)] in + inTags True "p" attribs <$> inlinesToJATS opts lst +blockToJATS opts (Div (ident,_,kvs) bs) = do + contents <- blocksToJATS opts bs + let attr = [("id", ident) | not (null ident)] ++ + [("xml:lang",l) | ("lang",l) <- kvs] ++ + [(k,v) | (k,v) <- kvs, k `elem` ["specific-use", + "content-type", "orientation", "position"]] + return $ inTags True "boxed-text" attr contents +blockToJATS _ (Header _ _ _) = + return empty -- should not occur after hierarchicalize +-- No Plain, everything needs to be in a block-level tag +blockToJATS opts (Plain lst) = blockToJATS opts (Para lst) +-- title beginning with fig: indicates that the image is a figure +blockToJATS opts (Para [Image (ident,_,kvs) txt + (src,'f':'i':'g':':':tit)]) = do + alt <- inlinesToJATS opts txt + let capt = if null txt + then empty + else inTagsSimple "caption" alt + let attr = [("id", ident) | not (null ident)] ++ + [(k,v) | (k,v) <- kvs, k `elem` ["fig-type", "orientation", + "position", "specific-use"]] + let mbMT = getMimeType src + let maintype = fromMaybe "image" $ + lookup "mimetype" kvs `mplus` + (takeWhile (/='/') <$> mbMT) + let subtype = fromMaybe "" $ + lookup "mime-subtype" kvs `mplus` + ((drop 1 . dropWhile (/='/')) <$> mbMT) + let graphicattr = [("mimetype",maintype), + ("mime-subtype",drop 1 subtype), + ("xlink:href",src), -- do we need to URL escape this? + ("xlink:title",tit)] + return $ inTags True "fig" attr $ + capt $$ selfClosingTag "graphic" graphicattr +blockToJATS opts (Para lst) = + inTagsIndented "p" <$> inlinesToJATS opts lst +blockToJATS opts (LineBlock lns) = + blockToJATS opts $ linesToPara lns +blockToJATS opts (BlockQuote blocks) = + inTagsIndented "disp-quote" <$> blocksToJATS opts blocks +blockToJATS _ (CodeBlock (ident,classes,kvs) str) = return $ + inTags False tag attr (flush (text (escapeStringForXML str))) + where attr = [("id",ident) | not (null ident)] ++ + [("language",lang) | not (null lang)] ++ + [(k,v) | (k,v) <- kvs, k `elem` ["code-type", + "code-version", "executable", + "language-version", "orientation", + "platforms", "position", "specific-use"]] + tag = if null lang then "preformat" else "code" + lang = case langs of + (l:_) -> escapeStringForXML l + [] -> "" + isLang l = map toLower l `elem` map (map toLower) languages + langsFrom s = if isLang s + then [s] + else languagesByExtension . map toLower $ s + langs = concatMap langsFrom classes +blockToJATS _ (BulletList []) = return empty +blockToJATS opts (BulletList lst) = do + inTags True "list" [("list-type", "bullet")] <$> + listItemsToJATS opts Nothing lst +blockToJATS _ (OrderedList _ []) = return empty +blockToJATS opts (OrderedList (start, numstyle, delimstyle) items) = do + let listType = case numstyle of + DefaultStyle -> "order" + Decimal -> "order" + Example -> "order" + UpperAlpha -> "alpha-upper" + LowerAlpha -> "alpha-lower" + UpperRoman -> "roman-upper" + LowerRoman -> "roman-lower" + let simpleList = start == 1 && (delimstyle == DefaultDelim || + delimstyle == Period) + let markers = if simpleList + then Nothing + else Just $ + orderedListMarkers (start, numstyle, delimstyle) + inTags True "list" [("list-type", listType)] <$> + listItemsToJATS opts markers items +blockToJATS opts (DefinitionList lst) = do + inTags True "def-list" [] <$> deflistItemsToJATS opts lst +blockToJATS _ b@(RawBlock f str) + | f == "jats" = return $ text str -- raw XML block + | otherwise = do + report $ BlockNotRendered b + return empty +blockToJATS _ HorizontalRule = return empty -- not semantic +blockToJATS opts (Table [] aligns widths headers rows) = do + let percent w = show (truncate (100*w) :: Integer) ++ "*" + let coltags = vcat $ zipWith (\w al -> selfClosingTag "col" + ([("width", percent w) | w > 0] ++ + [("align", alignmentToString al)])) widths aligns + thead <- if all null headers + then return empty + else inTagsIndented "thead" <$> tableRowToJATS opts True headers + tbody <- (inTagsIndented "tbody" . vcat) <$> + mapM (tableRowToJATS opts False) rows + return $ inTags True "table" [] $ coltags $$ thead $$ tbody +blockToJATS opts (Table caption aligns widths headers rows) = do + captionDoc <- inTagsIndented "caption" <$> blockToJATS opts (Para caption) + tbl <- blockToJATS opts (Table [] aligns widths headers rows) + return $ inTags True "table-wrap" [] $ captionDoc $$ tbl + +alignmentToString :: Alignment -> [Char] +alignmentToString alignment = case alignment of + AlignLeft -> "left" + AlignRight -> "right" + AlignCenter -> "center" + AlignDefault -> "left" + +tableRowToJATS :: PandocMonad m + => WriterOptions + -> Bool + -> [[Block]] + -> DB m Doc +tableRowToJATS opts isHeader cols = + (inTagsIndented "tr" . vcat) <$> mapM (tableItemToJATS opts isHeader) cols + +tableItemToJATS :: PandocMonad m + => WriterOptions + -> Bool + -> [Block] + -> DB m Doc +tableItemToJATS opts isHeader item = + (inTags True (if isHeader then "th" else "td") [] . vcat) <$> + mapM (blockToJATS opts) item + +-- | Convert a list of inline elements to JATS. +inlinesToJATS :: PandocMonad m => WriterOptions -> [Inline] -> DB m Doc +inlinesToJATS opts lst = hcat <$> mapM (inlineToJATS opts) lst + +-- | Convert an inline element to JATS. +inlineToJATS :: PandocMonad m => WriterOptions -> Inline -> DB m Doc +inlineToJATS _ (Str str) = return $ text $ escapeStringForXML str +inlineToJATS opts (Emph lst) = + inTagsSimple "italic" <$> inlinesToJATS opts lst +inlineToJATS opts (Strong lst) = + inTags False "bold" [("role", "strong")] <$> inlinesToJATS opts lst +inlineToJATS opts (Strikeout lst) = + inTagsSimple "strike" <$> inlinesToJATS opts lst +inlineToJATS opts (Superscript lst) = + inTagsSimple "sup" <$> inlinesToJATS opts lst +inlineToJATS opts (Subscript lst) = + inTagsSimple "sub" <$> inlinesToJATS opts lst +inlineToJATS opts (SmallCaps lst) = + inTags False "sc" [("role", "smallcaps")] <$> + inlinesToJATS opts lst +inlineToJATS opts (Quoted SingleQuote lst) = do + contents <- inlinesToJATS opts lst + return $ char '‘' <> contents <> char '’' +inlineToJATS opts (Quoted DoubleQuote lst) = do + contents <- inlinesToJATS opts lst + return $ char '“' <> contents <> char '”' +inlineToJATS _ (Code _ str) = + return $ inTagsSimple "monospace" $ text (escapeStringForXML str) +inlineToJATS _ il@(RawInline f x) + | f == "jats" = return $ text x + | otherwise = do + report $ InlineNotRendered il + return empty +inlineToJATS _ LineBreak = return $ selfClosingTag "break" [] +inlineToJATS _ Space = return space +inlineToJATS opts SoftBreak + | writerWrapText opts == WrapPreserve = return cr + | otherwise = return space +inlineToJATS opts (Note contents) = + -- TODO technically only <p> tags are allowed inside + inTagsIndented "fn" <$> blocksToJATS opts contents +inlineToJATS opts (Cite _ lst) = + -- TODO revisit this after examining the jats.csl pipeline + inlinesToJATS opts lst +inlineToJATS opts (Span ("",_,[]) ils) = inlinesToJATS opts ils +inlineToJATS opts (Span (ident,_,kvs) ils) = do + contents <- inlinesToJATS opts ils + let attr = [("id",ident) | not (null ident)] ++ + [("xml:lang",l) | ("lang",l) <- kvs] ++ + [(k,v) | (k,v) <- kvs + , k `elem` ["content-type", "rationale", + "rid", "specific-use"]] + return $ selfClosingTag "milestone-start" attr <> contents <> + selfClosingTag "milestone-end" [] +inlineToJATS _ (Math t str) = do + let addPref (Xml.Attr q v) + | Xml.qName q == "xmlns" = Xml.Attr q{ Xml.qName = "xmlns:mml" } v + | otherwise = Xml.Attr q v + let fixNS' e = e{ Xml.elName = + (Xml.elName e){ Xml.qPrefix = Just "mml" } } + let fixNS = everywhere (mkT fixNS') . + (\e -> e{ Xml.elAttribs = map addPref (Xml.elAttribs e) }) + let conf = Xml.useShortEmptyTags (const False) Xml.defaultConfigPP + res <- convertMath writeMathML t str + let tagtype = case t of + DisplayMath -> "disp-formula" + InlineMath -> "inline-formula" + return $ inTagsSimple tagtype $ + case res of + Right r -> text $ Xml.ppcElement conf + $ fixNS r + Left _ -> inTagsSimple "tex-math" + $ text "<![CDATA[" <> + text str <> + text "]]>" +inlineToJATS _ (Link _attr [Str t] ('m':'a':'i':'l':'t':'o':':':email, _)) + | escapeURI t == email = + return $ inTagsSimple "email" $ text (escapeStringForXML email) +inlineToJATS opts (Link (ident,_,kvs) txt ('#':src, _)) = do + let attr = [("id", ident) | not (null ident)] ++ + [("alt", stringify txt), + ("rid", src)] ++ + [(k,v) | (k,v) <- kvs, k `elem` ["ref-type", "specific-use"]] + contents <- inlinesToJATS opts txt + return $ inTags False "xref" attr contents +inlineToJATS opts (Link (ident,_,kvs) txt (src, tit)) = do + let attr = [("id", ident) | not (null ident)] ++ + [("ext-link-type", "uri"), + ("xlink:href", src)] ++ + [("xlink:title", tit) | not (null tit)] ++ + [(k,v) | (k,v) <- kvs, k `elem` ["assigning-authority", + "specific-use", "xlink:actuate", + "xlink:role", "xlink:show", + "xlink:type"]] + contents <- inlinesToJATS opts txt + return $ inTags False "ext-link" attr contents +inlineToJATS _ (Image (ident,_,kvs) _ (src, tit)) = do + let mbMT = getMimeType src + let maintype = fromMaybe "image" $ + lookup "mimetype" kvs `mplus` + (takeWhile (/='/') <$> mbMT) + let subtype = fromMaybe "" $ + lookup "mime-subtype" kvs `mplus` + ((drop 1 . dropWhile (/='/')) <$> mbMT) + let attr = [("id", ident) | not (null ident)] ++ + [("mimetype", maintype), + ("mime-subtype", subtype), + ("xlink:href", src)] ++ + [("xlink:title", tit) | not (null tit)] ++ + [(k,v) | (k,v) <- kvs, k `elem` ["baseline-shift", + "content-type", "specific-use", "xlink:actuate", + "xlink:href", "xlink:role", "xlink:show", + "xlink:type"]] + return $ selfClosingTag "inline-graphic" attr -- cgit v1.2.3 From 0d06c632b10299d4955fc85c04c73c5796056891 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 30 Mar 2017 15:39:21 +0200 Subject: JATS writer: Fixed bibliography handling. --- src/Text/Pandoc/Writers/JATS.hs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs index 9aaba78e0..5e1b3164b 100644 --- a/src/Text/Pandoc/Writers/JATS.hs +++ b/src/Text/Pandoc/Writers/JATS.hs @@ -181,11 +181,12 @@ listItemToJATS opts mbmarker item = do -- | Convert a Pandoc block element to JATS. blockToJATS :: PandocMonad m => WriterOptions -> Block -> DB m Doc blockToJATS _ Null = return empty --- Add ids to paragraphs in divs with ids - this is needed for --- pandoc-citeproc to get link anchors in bibliographies: -blockToJATS opts (Div (ident,_,_) [Para lst]) = - let attribs = [("id", ident) | not (null ident)] in - inTags True "p" attribs <$> inlinesToJATS opts lst +-- Bibliography reference: +blockToJATS opts (Div ('r':'e':'f':'-':_,_,_) [Para lst]) = + inlinesToJATS opts lst +blockToJATS opts (Div ("refs",_,_) xs) = do + contents <- blocksToJATS opts xs + return $ inTagsIndented "ref-list" contents blockToJATS opts (Div (ident,_,kvs) bs) = do contents <- blocksToJATS opts bs let attr = [("id", ident) | not (null ident)] ++ -- cgit v1.2.3 From e5e2a6e0a5c1ef10375e4c94985b3247289d77e9 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 30 Mar 2017 16:22:54 +0200 Subject: JATS writer: use both tex and mml alternatives for math when possible. --- src/Text/Pandoc/Writers/JATS.hs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs index 5e1b3164b..71a45bf77 100644 --- a/src/Text/Pandoc/Writers/JATS.hs +++ b/src/Text/Pandoc/Writers/JATS.hs @@ -381,14 +381,16 @@ inlineToJATS _ (Math t str) = do let tagtype = case t of DisplayMath -> "disp-formula" InlineMath -> "inline-formula" - return $ inTagsSimple tagtype $ - case res of - Right r -> text $ Xml.ppcElement conf - $ fixNS r - Left _ -> inTagsSimple "tex-math" + let rawtex = inTagsSimple "tex-math" $ text "<![CDATA[" <> text str <> text "]]>" + return $ inTagsSimple tagtype $ + case res of + Right r -> inTagsSimple "alternatives" $ + cr <> rawtex $$ + (text $ Xml.ppcElement conf $ fixNS r) + Left _ -> rawtex inlineToJATS _ (Link _attr [Str t] ('m':'a':'i':'l':'t':'o':':':email, _)) | escapeURI t == email = return $ inTagsSimple "email" $ text (escapeStringForXML email) -- cgit v1.2.3 From 2f19b5daac4da2b849b5165f9eb1386ab41ccc1b Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 30 Mar 2017 16:43:12 +0200 Subject: SelfContained: export makeDataURI --- src/Text/Pandoc/SelfContained.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/SelfContained.hs b/src/Text/Pandoc/SelfContained.hs index 524378146..53cb4a4b5 100644 --- a/src/Text/Pandoc/SelfContained.hs +++ b/src/Text/Pandoc/SelfContained.hs @@ -30,7 +30,7 @@ Functions for converting an HTML file into one that can be viewed offline, by incorporating linked images, CSS, and scripts into the HTML using data URIs. -} -module Text.Pandoc.SelfContained ( makeSelfContained ) where +module Text.Pandoc.SelfContained ( makeDataURI, makeSelfContained ) where import Codec.Compression.GZip as Gzip import Control.Applicative ((<|>)) import Control.Monad.Except (throwError) -- cgit v1.2.3 From 6ad486c3c3922e4ac7dd5710f69c3ccf623e92b4 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 30 Mar 2017 16:43:17 +0200 Subject: Automatically include URI-encoded jats.csl for jats output. This way people can do pandoc -s -t jats --filter pandoc-citeproc and it will just work. If they want to specify a stylesheet, they still can. --- src/Text/Pandoc/App.hs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 0fc883792..e45d10254 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -73,7 +73,7 @@ import Text.Pandoc.Lua ( runLuaFilter ) import Text.Pandoc.MediaBag (MediaBag, extractMediaBag, mediaDirectory) import Text.Pandoc.PDF (makePDF) import Text.Pandoc.Process (pipeProcess) -import Text.Pandoc.SelfContained (makeSelfContained) +import Text.Pandoc.SelfContained (makeSelfContained, makeDataURI) import Text.Pandoc.Shared (err, headerShift, openURL, readDataFile, readDataFileUTF8, safeRead, tabFilter) import qualified Text.Pandoc.UTF8 as UTF8 @@ -386,10 +386,16 @@ convertWithOpts opts = do withMediaBag . r readerOpts) sources return (mconcat (map fst pairs), mconcat (map snd pairs)) + jatsCSL <- readDataFile datadir "jats.csl" + let jatsEncoded = makeDataURI ("application/xml", jatsCSL) + let metadata = if format == "jats" + then ("csl", jatsEncoded) : optMetadata opts + else optMetadata opts + runIO' $ do (doc, media) <- sourceToDoc sources doc' <- (maybe return (extractMedia media) (optExtractMedia opts) >=> - return . flip (foldr addMetadata) (optMetadata opts) >=> + return . flip (foldr addMetadata) metadata >=> applyTransforms transforms >=> applyLuaFilters datadir (optLuaFilters opts) [format] >=> applyFilters datadir filters' [format]) doc -- cgit v1.2.3 From 8d50f37d533b48fedcbaa953964771a759a1421d Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 30 Mar 2017 17:23:16 +0200 Subject: Don't read jats.csl unless we actually need it. --- src/Text/Pandoc/App.hs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index e45d10254..ce4c87ec1 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -386,11 +386,12 @@ convertWithOpts opts = do withMediaBag . r readerOpts) sources return (mconcat (map fst pairs), mconcat (map snd pairs)) - jatsCSL <- readDataFile datadir "jats.csl" - let jatsEncoded = makeDataURI ("application/xml", jatsCSL) - let metadata = if format == "jats" - then ("csl", jatsEncoded) : optMetadata opts - else optMetadata opts + metadata <- if format == "jats" + then do + jatsCSL <- readDataFile datadir "jats.csl" + let jatsEncoded = makeDataURI ("application/xml", jatsCSL) + return $ ("csl", jatsEncoded) : optMetadata opts + else return $ optMetadata opts runIO' $ do (doc, media) <- sourceToDoc sources -- cgit v1.2.3 From b27836666f98c19b2d86d5b63ce2ddf2658bb343 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 30 Mar 2017 21:31:43 +0200 Subject: Org writer: move everything into PandocMonad. --- src/Text/Pandoc/Writers/Org.hs | 58 +++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 23 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index 50eeec09a..28f4e8220 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -36,11 +36,12 @@ module Text.Pandoc.Writers.Org ( writeOrg) where import Control.Monad.State import Data.Char (isAlphaNum, toLower) import Data.List (intersect, intersperse, isPrefixOf, partition, transpose) -import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Pretty import Text.Pandoc.Shared +import Text.Pandoc.Logging import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Writers.Shared @@ -50,16 +51,18 @@ data WriterState = , stOptions :: WriterOptions } +type Org = StateT WriterState + -- | Convert Pandoc to Org. writeOrg :: PandocMonad m => WriterOptions -> Pandoc -> m String -writeOrg opts document = return $ +writeOrg opts document = do let st = WriterState { stNotes = [], stHasMath = False, stOptions = opts } - in evalState (pandocToOrg document) st + evalStateT (pandocToOrg document) st -- | Return Org representation of document. -pandocToOrg :: Pandoc -> State WriterState String +pandocToOrg :: PandocMonad m => Pandoc -> Org m String pandocToOrg (Pandoc meta blocks) = do opts <- gets stOptions let colwidth = if writerWrapText opts == WrapAuto @@ -81,13 +84,13 @@ pandocToOrg (Pandoc meta blocks) = do Just tpl -> return $ renderTemplate' tpl context -- | Return Org representation of notes. -notesToOrg :: [[Block]] -> State WriterState Doc +notesToOrg :: PandocMonad m => [[Block]] -> Org m Doc notesToOrg notes = mapM (\(num, note) -> noteToOrg num note) (zip [1..] notes) >>= return . vsep -- | Return Org representation of a note. -noteToOrg :: Int -> [Block] -> State WriterState Doc +noteToOrg :: PandocMonad m => Int -> [Block] -> Org m Doc noteToOrg num note = do contents <- blockListToOrg note let marker = "[fn:" ++ show num ++ "] " @@ -107,8 +110,9 @@ isRawFormat f = f == Format "latex" || f == Format "tex" || f == Format "org" -- | Convert Pandoc block element to Org. -blockToOrg :: Block -- ^ Block element - -> State WriterState Doc +blockToOrg :: PandocMonad m + => Block -- ^ Block element + -> Org m Doc blockToOrg Null = return empty blockToOrg (Div (_,classes@(cls:_),kvs) bs) | "drawer" `elem` classes = do contents <- blockListToOrg bs @@ -176,9 +180,11 @@ blockToOrg (LineBlock lns) = do blockToOrg (RawBlock "html" str) = return $ blankline $$ "#+BEGIN_HTML" $$ nest 2 (text str) $$ "#+END_HTML" $$ blankline -blockToOrg (RawBlock f str) | isRawFormat f = - return $ text str -blockToOrg (RawBlock _ _) = return empty +blockToOrg b@(RawBlock f str) + | isRawFormat f = return $ text str + | otherwise = do + report $ BlockNotRendered b + return empty blockToOrg HorizontalRule = return $ blankline $$ "--------------" $$ blankline blockToOrg (Header level attr inlines) = do contents <- inlineListToOrg inlines @@ -252,21 +258,23 @@ blockToOrg (DefinitionList items) = do return $ vcat contents $$ blankline -- | Convert bullet list item (list of blocks) to Org. -bulletListItemToOrg :: [Block] -> State WriterState Doc +bulletListItemToOrg :: PandocMonad m => [Block] -> Org m Doc bulletListItemToOrg items = do contents <- blockListToOrg items return $ hang 2 "- " (contents <> cr) -- | Convert ordered list item (a list of blocks) to Org. -orderedListItemToOrg :: String -- ^ marker for list item +orderedListItemToOrg :: PandocMonad m + => String -- ^ marker for list item -> [Block] -- ^ list item (list of blocks) - -> State WriterState Doc + -> Org m Doc orderedListItemToOrg marker items = do contents <- blockListToOrg items return $ hang (length marker + 1) (text marker <> space) (contents <> cr) -- | Convert defintion list item (label, list of blocks) to Org. -definitionListItemToOrg :: ([Inline], [[Block]]) -> State WriterState Doc +definitionListItemToOrg :: PandocMonad m + => ([Inline], [[Block]]) -> Org m Doc definitionListItemToOrg (label, defs) = do label' <- inlineListToOrg label contents <- liftM vcat $ mapM blockListToOrg defs @@ -299,16 +307,19 @@ attrHtml (ident, classes, kvs) = in name <> keyword <> ": " <> text (unwords kvStrings) <> cr -- | Convert list of Pandoc block elements to Org. -blockListToOrg :: [Block] -- ^ List of block elements - -> State WriterState Doc +blockListToOrg :: PandocMonad m + => [Block] -- ^ List of block elements + -> Org m Doc blockListToOrg blocks = mapM blockToOrg blocks >>= return . vcat -- | Convert list of Pandoc inline elements to Org. -inlineListToOrg :: [Inline] -> State WriterState Doc +inlineListToOrg :: PandocMonad m + => [Inline] + -> Org m Doc inlineListToOrg lst = mapM inlineToOrg lst >>= return . hcat -- | Convert Pandoc inline element to Org. -inlineToOrg :: Inline -> State WriterState Doc +inlineToOrg :: PandocMonad m => Inline -> Org m Doc inlineToOrg (Span (uid, [], []) []) = return $ "<<" <> text uid <> ">>" inlineToOrg (Span _ lst) = @@ -343,10 +354,11 @@ inlineToOrg (Math t str) = do return $ if t == InlineMath then "$" <> text str <> "$" else "$$" <> text str <> "$$" -inlineToOrg (RawInline f@(Format f') str) = - return $ if isRawFormat f - then text str - else "@@" <> text f' <> ":" <> text str <> "@@" +inlineToOrg il@(RawInline f@(Format f') str) + | isRawFormat f = return $ text str + | otherwise = do + report $ InlineNotRendered il + return empty inlineToOrg LineBreak = return (text "\\\\" <> cr) inlineToOrg Space = return space inlineToOrg SoftBreak = do -- cgit v1.2.3 From d8a322861742355663a0ffea4550200cddcbd002 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 30 Mar 2017 21:37:13 +0200 Subject: Textile writer: moved into PandocMonad. Warnings for omitted raw content. --- src/Text/Pandoc/Writers/Textile.hs | 79 +++++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 31 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Textile.hs b/src/Text/Pandoc/Writers/Textile.hs index 625e8031b..0ecb746c3 100644 --- a/src/Text/Pandoc/Writers/Textile.hs +++ b/src/Text/Pandoc/Writers/Textile.hs @@ -33,7 +33,8 @@ module Text.Pandoc.Writers.Textile ( writeTextile ) where import Control.Monad.State import Data.Char (isSpace) import Data.List (intercalate) -import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Class (PandocMonad, report) +import Text.Pandoc.Logging import Text.Pandoc.Definition import Text.Pandoc.ImageSize import Text.Pandoc.Options @@ -50,15 +51,20 @@ data WriterState = WriterState { , stUseTags :: Bool -- True if we should use HTML tags because we're in a complex list } +type TW = StateT WriterState + -- | Convert Pandoc to Textile. writeTextile :: PandocMonad m => WriterOptions -> Pandoc -> m String -writeTextile opts document = return $ - evalState (pandocToTextile opts document) - WriterState { stNotes = [], stListLevel = [], stStartNum = Nothing, +writeTextile opts document = + evalStateT (pandocToTextile opts document) + WriterState { stNotes = [], + stListLevel = [], + stStartNum = Nothing, stUseTags = False } -- | Return Textile representation of document. -pandocToTextile :: WriterOptions -> Pandoc -> State WriterState String +pandocToTextile :: PandocMonad m + => WriterOptions -> Pandoc -> TW m String pandocToTextile opts (Pandoc meta blocks) = do metadata <- metaToJSON opts (blockListToTextile opts) (inlineListToTextile opts) meta @@ -70,7 +76,7 @@ pandocToTextile opts (Pandoc meta blocks) = do Nothing -> return main Just tpl -> return $ renderTemplate' tpl context -withUseTags :: State WriterState a -> State WriterState a +withUseTags :: PandocMonad m => TW m a -> TW m a withUseTags action = do oldUseTags <- gets stUseTags modify $ \s -> s { stUseTags = True } @@ -102,9 +108,10 @@ escapeStringForTextile :: String -> String escapeStringForTextile = concatMap escapeCharForTextile -- | Convert Pandoc block element to Textile. -blockToTextile :: WriterOptions -- ^ Options - -> Block -- ^ Block element - -> State WriterState String +blockToTextile :: PandocMonad m + => WriterOptions -- ^ Options + -> Block -- ^ Block element + -> TW m String blockToTextile _ Null = return "" @@ -134,9 +141,11 @@ blockToTextile opts (Para inlines) = do blockToTextile opts (LineBlock lns) = blockToTextile opts $ linesToPara lns -blockToTextile _ (RawBlock f str) +blockToTextile _ b@(RawBlock f str) | f == Format "html" || f == Format "textile" = return str - | otherwise = return "" + | otherwise = do + report $ BlockNotRendered b + return "" blockToTextile _ HorizontalRule = return "<hr />\n" @@ -262,7 +271,8 @@ listAttribsToString (startnum, numstyle, _) = else "") -- | Convert bullet or ordered list item (list of blocks) to Textile. -listItemToTextile :: WriterOptions -> [Block] -> State WriterState String +listItemToTextile :: PandocMonad m + => WriterOptions -> [Block] -> TW m String listItemToTextile opts items = do contents <- blockListToTextile opts items useTags <- gets stUseTags @@ -278,9 +288,10 @@ listItemToTextile opts items = do Nothing -> return $ marker ++ " " ++ contents -- | Convert definition list item (label, list of blocks) to Textile. -definitionListItemToTextile :: WriterOptions +definitionListItemToTextile :: PandocMonad m + => WriterOptions -> ([Inline],[[Block]]) - -> State WriterState String + -> TW m String definitionListItemToTextile opts (label, items) = do labelText <- inlineListToTextile opts label contents <- mapM (blockListToTextile opts) items @@ -326,11 +337,12 @@ vcat = intercalate "\n" -- Auxiliary functions for tables. (TODO: these are common to HTML, MediaWiki, -- and Textile writers, and should be abstracted out.) -tableRowToTextile :: WriterOptions - -> [String] - -> Int - -> [[Block]] - -> State WriterState String +tableRowToTextile :: PandocMonad m + => WriterOptions + -> [String] + -> Int + -> [[Block]] + -> TW m String tableRowToTextile opts alignStrings rownum cols' = do let celltype = if rownum == 0 then "th" else "td" let rowclass = case rownum of @@ -349,11 +361,12 @@ alignmentToString alignment = case alignment of AlignCenter -> "center" AlignDefault -> "left" -tableItemToTextile :: WriterOptions - -> String - -> String - -> [Block] - -> State WriterState String +tableItemToTextile :: PandocMonad m + => WriterOptions + -> String + -> String + -> [Block] + -> TW m String tableItemToTextile opts celltype align' item = do let mkcell x = "<" ++ celltype ++ " align=\"" ++ align' ++ "\">" ++ x ++ "</" ++ celltype ++ ">" @@ -361,19 +374,21 @@ tableItemToTextile opts celltype align' item = do return $ mkcell contents -- | Convert list of Pandoc block elements to Textile. -blockListToTextile :: WriterOptions -- ^ Options - -> [Block] -- ^ List of block elements - -> State WriterState String +blockListToTextile :: PandocMonad m + => WriterOptions -- ^ Options + -> [Block] -- ^ List of block elements + -> TW m String blockListToTextile opts blocks = mapM (blockToTextile opts) blocks >>= return . vcat -- | Convert list of Pandoc inline elements to Textile. -inlineListToTextile :: WriterOptions -> [Inline] -> State WriterState String +inlineListToTextile :: PandocMonad m + => WriterOptions -> [Inline] -> TW m String inlineListToTextile opts lst = mapM (inlineToTextile opts) lst >>= return . concat -- | Convert Pandoc inline element to Textile. -inlineToTextile :: WriterOptions -> Inline -> State WriterState String +inlineToTextile :: PandocMonad m => WriterOptions -> Inline -> TW m String inlineToTextile opts (Span _ lst) = inlineListToTextile opts lst @@ -430,11 +445,13 @@ inlineToTextile _ (Str str) = return $ escapeStringForTextile str inlineToTextile _ (Math _ str) = return $ "<span class=\"math\">" ++ escapeStringForXML str ++ "</math>" -inlineToTextile opts (RawInline f str) +inlineToTextile opts il@(RawInline f str) | f == Format "html" || f == Format "textile" = return str | (f == Format "latex" || f == Format "tex") && isEnabled Ext_raw_tex opts = return str - | otherwise = return "" + | otherwise = do + report $ InlineNotRendered il + return "" inlineToTextile _ LineBreak = return "\n" -- cgit v1.2.3 From ea84cd0842ede0ff9835bc4aae47c949ee1d1dd2 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 30 Mar 2017 21:43:22 +0200 Subject: ZimWiki writer: put in PandocMonad, added warnings for raw. --- src/Text/Pandoc/Writers/ZimWiki.hs | 49 +++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 17 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/ZimWiki.hs b/src/Text/Pandoc/Writers/ZimWiki.hs index 19f476a17..da8b08de1 100644 --- a/src/Text/Pandoc/Writers/ZimWiki.hs +++ b/src/Text/Pandoc/Writers/ZimWiki.hs @@ -32,13 +32,14 @@ http://zim-wiki.org/manual/Help/Wiki_Syntax.html module Text.Pandoc.Writers.ZimWiki ( writeZimWiki ) where import Control.Monad (zipWithM) -import Control.Monad.State (State, evalState, gets, modify) +import Control.Monad.State (StateT, evalStateT, gets, modify) import Data.Default (Default (..)) import Data.List (intercalate, isInfixOf, isPrefixOf, transpose) import qualified Data.Map as Map import Data.Text (breakOnAll, pack) import Network.URI (isURI) -import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Class (PandocMonad, report) +import Text.Pandoc.Logging import Text.Pandoc.Definition import Text.Pandoc.ImageSize import Text.Pandoc.Options (WrapOption (..), WriterOptions (writerTableOfContents, writerTemplate, writerWrapText)) @@ -57,12 +58,14 @@ data WriterState = WriterState { instance Default WriterState where def = WriterState { stItemNum = 1, stIndent = "", stInTable = False, stInLink = False } +type ZW = StateT WriterState + -- | Convert Pandoc to ZimWiki. writeZimWiki :: PandocMonad m => WriterOptions -> Pandoc -> m String -writeZimWiki opts document = return $ evalState (pandocToZimWiki opts document) def +writeZimWiki opts document = evalStateT (pandocToZimWiki opts document) def -- | Return ZimWiki representation of document. -pandocToZimWiki :: WriterOptions -> Pandoc -> State WriterState String +pandocToZimWiki :: PandocMonad m => WriterOptions -> Pandoc -> ZW m String pandocToZimWiki opts (Pandoc meta blocks) = do metadata <- metaToJSON opts (fmap trimr . blockListToZimWiki opts) @@ -86,7 +89,7 @@ escapeString = substitute "__" "''__''" . substitute "//" "''//''" -- | Convert Pandoc block element to ZimWiki. -blockToZimWiki :: WriterOptions -> Block -> State WriterState String +blockToZimWiki :: PandocMonad m => WriterOptions -> Block -> ZW m String blockToZimWiki _ Null = return "" @@ -118,10 +121,12 @@ blockToZimWiki opts (Para inlines) = do blockToZimWiki opts (LineBlock lns) = do blockToZimWiki opts $ linesToPara lns -blockToZimWiki opts (RawBlock f str) +blockToZimWiki opts b@(RawBlock f str) | f == Format "zimwiki" = return str | f == Format "html" = do cont <- indentFromHTML opts str; return cont - | otherwise = return "" + | otherwise = do + report $ BlockNotRendered b + return "" blockToZimWiki _ HorizontalRule = return "\n----\n" @@ -198,7 +203,10 @@ blockToZimWiki opts (DefinitionList items) = do contents <- (mapM (definitionListItemToZimWiki opts) items) return $ vcat contents -definitionListItemToZimWiki :: WriterOptions -> ([Inline],[[Block]]) -> State WriterState String +definitionListItemToZimWiki :: PandocMonad m + => WriterOptions + -> ([Inline],[[Block]]) + -> ZW m String definitionListItemToZimWiki opts (label, items) = do labelText <- inlineListToZimWiki opts label contents <- mapM (blockListToZimWiki opts) items @@ -206,7 +214,7 @@ definitionListItemToZimWiki opts (label, items) = do return $ indent ++ "* **" ++ labelText ++ "** " ++ concat contents -- Auxiliary functions for lists: -indentFromHTML :: WriterOptions -> String -> State WriterState String +indentFromHTML :: PandocMonad m => WriterOptions -> String -> ZW m String indentFromHTML _ str = do indent <- gets stIndent itemnum <- gets stItemNum @@ -239,14 +247,15 @@ vcat :: [String] -> String vcat = intercalate "\n" -- | Convert bullet list item (list of blocks) to ZimWiki. -listItemToZimWiki :: WriterOptions -> [Block] -> State WriterState String +listItemToZimWiki :: PandocMonad m => WriterOptions -> [Block] -> ZW m String listItemToZimWiki opts items = do contents <- blockListToZimWiki opts items indent <- gets stIndent return $ indent ++ "* " ++ contents -- | Convert ordered list item (list of blocks) to ZimWiki. -orderedListItemToZimWiki :: WriterOptions -> [Block] -> State WriterState String +orderedListItemToZimWiki :: PandocMonad m + => WriterOptions -> [Block] -> ZW m String orderedListItemToZimWiki opts items = do contents <- blockListToZimWiki opts items indent <- gets stIndent @@ -255,7 +264,8 @@ orderedListItemToZimWiki opts items = do return $ indent ++ show itemnum ++ ". " ++ contents -- Auxiliary functions for tables: -tableItemToZimWiki :: WriterOptions -> Alignment -> [Block] -> State WriterState String +tableItemToZimWiki :: PandocMonad m + => WriterOptions -> Alignment -> [Block] -> ZW m String tableItemToZimWiki opts align' item = do let mkcell x = (if align' == AlignRight || align' == AlignCenter then " " @@ -269,15 +279,18 @@ tableItemToZimWiki opts align' item = do return $ mkcell contents -- | Convert list of Pandoc block elements to ZimWiki. -blockListToZimWiki :: WriterOptions -> [Block] -> State WriterState String +blockListToZimWiki :: PandocMonad m + => WriterOptions -> [Block] -> ZW m String blockListToZimWiki opts blocks = vcat <$> mapM (blockToZimWiki opts) blocks -- | Convert list of Pandoc inline elements to ZimWiki. -inlineListToZimWiki :: WriterOptions -> [Inline] -> State WriterState String +inlineListToZimWiki :: PandocMonad m + => WriterOptions -> [Inline] -> ZW m String inlineListToZimWiki opts lst = concat <$> (mapM (inlineToZimWiki opts) lst) -- | Convert Pandoc inline element to ZimWiki. -inlineToZimWiki :: WriterOptions -> Inline -> State WriterState String +inlineToZimWiki :: PandocMonad m + => WriterOptions -> Inline -> ZW m String inlineToZimWiki opts (Emph lst) = do contents <- inlineListToZimWiki opts lst @@ -331,10 +344,12 @@ inlineToZimWiki _ (Math mathType str) = return $ delim ++ str ++ delim -- note InlineMath -> "$" -- | f == Format "html" = return $ "<html>" ++ str ++ "</html>" -inlineToZimWiki opts (RawInline f str) +inlineToZimWiki opts il@(RawInline f str) | f == Format "zimwiki" = return str | f == Format "html" = do cont <- indentFromHTML opts str; return cont - | otherwise = return "" + | otherwise = do + report $ InlineNotRendered il + return "" inlineToZimWiki _ LineBreak = do inTable <- gets stInTable -- cgit v1.2.3 From 80d093843b4f782dda73054d4fc4ba9ef2a82843 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 30 Mar 2017 21:51:11 +0200 Subject: Allow dynamic loading of syntax definitions. See #3334. * Add writerSyntaxMap to WriterOptions. * Highlighting: added parameter for SyntaxMap to highlight. * Implemented --syntax-definition option. TODO: [ ] Figure out whether we want to have the xml parsing depend on the dtd (it currently does, and fails unless the language.dtd is found in the same directory). [ ] Add an option to read a KDE syntax highlighting theme as a custom style. [ ] Add tests. --- src/Text/Pandoc/App.hs | 29 ++++++++++++++++++++++++- src/Text/Pandoc/Highlighting.hs | 9 ++++---- src/Text/Pandoc/Options.hs | 3 +++ src/Text/Pandoc/Writers/Docx.hs | 3 ++- src/Text/Pandoc/Writers/HTML.hs | 7 +++--- src/Text/Pandoc/Writers/LaTeX.hs | 46 +++++++++++++++++++++------------------- 6 files changed, 66 insertions(+), 31 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index ce4c87ec1..5391f0fa6 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -57,6 +57,8 @@ import qualified Data.Yaml as Yaml import Network.URI (URI (..), isURI, parseURI) import Paths_pandoc (getDataDir) import Skylighting (Style, Syntax (..), defaultSyntaxMap) +import Skylighting.Parser (missingIncludes, parseSyntaxDefinition, + addSyntaxDefinition) import System.Console.GetOpt import System.Directory (Permissions (..), doesFileExist, findExecutable, getAppUserDataDirectory, getPermissions) @@ -299,6 +301,21 @@ convertWithOpts opts = do } highlightStyle <- lookupHighlightStyle $ optHighlightStyle opts + let addSyntaxMap existingmap f = do + res <- parseSyntaxDefinition f + case res of + Left errstr -> err 67 errstr + Right syn -> return $ addSyntaxDefinition syn existingmap + + syntaxMap <- foldM addSyntaxMap defaultSyntaxMap + (optSyntaxDefinitions opts) + + case missingIncludes (M.elems syntaxMap) of + [] -> return () + xs -> err 73 $ "Missing syntax definitions:\n" ++ + unlines (map + (\(syn,dep) -> (T.unpack syn ++ " requires " ++ + T.unpack dep ++ " through IncludeRules.")) xs) let writerOptions = def { writerTemplate = templ, writerVariables = variables, @@ -330,7 +347,8 @@ convertWithOpts opts = do writerEpubChapterLevel = optEpubChapterLevel opts, writerTOCDepth = optTOCDepth opts, writerReferenceDoc = optReferenceDoc opts, - writerLaTeXArgs = optLaTeXEngineArgs opts + writerLaTeXArgs = optLaTeXEngineArgs opts, + writerSyntaxMap = syntaxMap } @@ -507,6 +525,7 @@ data Opt = Opt , optSelfContained :: Bool -- ^ Make HTML accessible offline , optHtmlQTags :: Bool -- ^ Use <q> tags in HTML , optHighlightStyle :: Maybe String -- ^ Style to use for highlighted code + , optSyntaxDefinitions :: [FilePath] -- ^ xml syntax defs to load , optTopLevelDivision :: TopLevelDivision -- ^ Type of the top-level divisions , optHTMLMathMethod :: HTMLMathMethod -- ^ Method to print HTML math , optAbbreviations :: Maybe FilePath -- ^ Path to abbrevs file @@ -574,6 +593,7 @@ defaultOpts = Opt , optSelfContained = False , optHtmlQTags = False , optHighlightStyle = Just "pygments" + , optSyntaxDefinitions = [] , optTopLevelDivision = TopLevelDefault , optHTMLMathMethod = PlainMath , optAbbreviations = Nothing @@ -987,6 +1007,13 @@ options = "STYLE") "" -- "Style for highlighted code" + , Option "" ["syntax-definition"] + (ReqArg + (\arg opt -> return opt{ optSyntaxDefinitions = arg : + optSyntaxDefinitions opt }) + "FILE") + "" -- "Syntax definition (xml) file" + , Option "H" ["include-in-header"] (ReqArg (\arg opt -> return opt{ optIncludeInHeader = diff --git a/src/Text/Pandoc/Highlighting.hs b/src/Text/Pandoc/Highlighting.hs index a4732cd02..f249f96ad 100644 --- a/src/Text/Pandoc/Highlighting.hs +++ b/src/Text/Pandoc/Highlighting.hs @@ -76,21 +76,22 @@ languagesByExtension :: String -> [String] languagesByExtension ext = [T.unpack (T.toLower (sName s)) | s <- syntaxesByExtension defaultSyntaxMap ext] -highlight :: (FormatOptions -> [SourceLine] -> a) -- ^ Formatter +highlight :: SyntaxMap + -> (FormatOptions -> [SourceLine] -> a) -- ^ Formatter -> Attr -- ^ Attributes of the CodeBlock -> String -- ^ Raw contents of the CodeBlock -> Either String a -highlight formatter (_, classes, keyvals) rawCode = +highlight syntaxmap formatter (_, classes, keyvals) rawCode = let firstNum = fromMaybe 1 (safeRead (fromMaybe "1" $ lookup "startFrom" keyvals)) fmtOpts = defaultFormatOpts{ startNumber = firstNum, numberLines = any (`elem` ["number","numberLines", "number-lines"]) classes } - tokenizeOpts = TokenizerConfig{ syntaxMap = defaultSyntaxMap + tokenizeOpts = TokenizerConfig{ syntaxMap = syntaxmap , traceOutput = False } classes' = map T.pack classes rawCode' = T.pack rawCode - in case msum (map (\l -> lookupSyntax l defaultSyntaxMap) classes') of + in case msum (map (\l -> lookupSyntax l syntaxmap) classes') of Nothing | numberLines fmtOpts -> Right $ formatter fmtOpts{ codeClasses = [], diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index 0379b0ddf..0b09f0497 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -50,6 +50,7 @@ import Data.Default import qualified Data.Set as Set import Data.Typeable (Typeable) import GHC.Generics (Generic) +import Skylighting (SyntaxMap, defaultSyntaxMap) import Text.Pandoc.Extensions import Text.Pandoc.Highlighting (Style, pygments) @@ -185,6 +186,7 @@ data WriterOptions = WriterOptions , writerReferenceDoc :: Maybe FilePath -- ^ Path to reference document if specified , writerLaTeXArgs :: [String] -- ^ Flags to pass to latex-engine , writerReferenceLocation :: ReferenceLocation -- ^ Location of footnotes and references for writing markdown + , writerSyntaxMap :: SyntaxMap } deriving (Show, Data, Typeable, Generic) instance Default WriterOptions where @@ -220,6 +222,7 @@ instance Default WriterOptions where , writerReferenceDoc = Nothing , writerLaTeXArgs = [] , writerReferenceLocation = EndOfDocument + , writerSyntaxMap = defaultSyntaxMap } -- | Returns True if the given extension is enabled. diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 5e4fe7731..fcc8551a4 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -1181,7 +1181,8 @@ inlineToOpenXML' opts (Code attrs str) = do withTextProp (rCustomStyle "VerbatimChar") $ if isNothing (writerHighlightStyle opts) then unhighlighted - else case highlight formatOpenXML attrs str of + else case highlight (writerSyntaxMap opts) + formatOpenXML attrs str of Right h -> return h Left msg -> do unless (null msg) $ report $ CouldNotHighlight msg diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 10b782de7..42726bc61 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -642,7 +642,7 @@ blockToHtml opts (CodeBlock (id',classes,keyvals) rawCode) = do then unlines . map ("> " ++) . lines $ rawCode else rawCode hlCode = if isJust (writerHighlightStyle opts) - then highlight formatHtmlBlock + then highlight (writerSyntaxMap opts) formatHtmlBlock (id',classes',keyvals) adjCode else Left "" case hlCode of @@ -885,8 +885,9 @@ inlineToHtml opts inline = do return $ addAttrs opts (id',[],keyvals) h where (id',_,keyvals) = attr hlCode = if isJust (writerHighlightStyle opts) - then highlight formatHtmlInline - attr str + then highlight + (writerSyntaxMap opts) + formatHtmlInline attr str else Left "" (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 44c00df24..eb38485de 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -548,7 +548,8 @@ blockToLaTeX (CodeBlock (identifier,classes,keyvalAttr) str) = do return $ flush ("\\begin{lstlisting}" <> printParams $$ text str $$ "\\end{lstlisting}") $$ cr let highlightedCodeBlock = - case highlight formatLaTeXBlock ("",classes,keyvalAttr) str of + case highlight (writerSyntaxMap opts) + formatLaTeXBlock ("",classes,keyvalAttr) str of Left msg -> do unless (null msg) $ report $ CouldNotHighlight msg @@ -953,32 +954,33 @@ inlineToLaTeX (Cite cits lst) = do inlineToLaTeX (Code (_,classes,_) str) = do opts <- gets stOptions inHeading <- gets stInHeading + let listingsCode = do + let listingsopt = case getListingsLanguage classes of + Just l -> "[language=" ++ mbBraced l ++ "]" + Nothing -> "" + inNote <- gets stInNote + when inNote $ modify $ \s -> s{ stVerbInNote = True } + let chr = case "!\"&'()*,-./:;?@_" \\ str of + (c:_) -> c + [] -> '!' + return $ text $ "\\lstinline" ++ listingsopt ++ [chr] ++ str ++ [chr] + let rawCode = liftM (text . (\s -> "\\texttt{" ++ escapeSpaces s ++ "}")) + $ stringToLaTeX CodeString str + where escapeSpaces = concatMap + (\c -> if c == ' ' then "\\ " else [c]) + let highlightCode = do + case highlight (writerSyntaxMap opts) + formatLaTeXInline ("",classes,[]) str of + Left msg -> do + unless (null msg) $ report $ CouldNotHighlight msg + rawCode + Right h -> modify (\st -> st{ stHighlighting = True }) >> + return (text (T.unpack h)) case () of _ | writerListings opts && not inHeading -> listingsCode | isJust (writerHighlightStyle opts) && not (null classes) -> highlightCode | otherwise -> rawCode - where listingsCode = do - let listingsopt = case getListingsLanguage classes of - Just l -> "[language=" ++ mbBraced l ++ "]" - Nothing -> "" - inNote <- gets stInNote - when inNote $ modify $ \s -> s{ stVerbInNote = True } - let chr = case "!\"&'()*,-./:;?@_" \\ str of - (c:_) -> c - [] -> '!' - return $ text $ "\\lstinline" ++ listingsopt ++ [chr] ++ str ++ [chr] - highlightCode = do - case highlight formatLaTeXInline ("",classes,[]) str of - Left msg -> do - unless (null msg) $ report $ CouldNotHighlight msg - rawCode - Right h -> modify (\st -> st{ stHighlighting = True }) >> - return (text (T.unpack h)) - rawCode = liftM (text . (\s -> "\\texttt{" ++ escapeSpaces s ++ "}")) - $ stringToLaTeX CodeString str - where - escapeSpaces = concatMap (\c -> if c == ' ' then "\\ " else [c]) inlineToLaTeX (Quoted qt lst) = do contents <- inlineListToLaTeX lst csquotes <- liftM stCsquotes get -- cgit v1.2.3 From 136a53edc88513d0d300a50b9f77ff003baa512f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 30 Mar 2017 23:03:57 +0200 Subject: Fix compiler warning. --- src/Text/Pandoc/Writers/Org.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index 28f4e8220..fc6608450 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -354,7 +354,7 @@ inlineToOrg (Math t str) = do return $ if t == InlineMath then "$" <> text str <> "$" else "$$" <> text str <> "$$" -inlineToOrg il@(RawInline f@(Format f') str) +inlineToOrg il@(RawInline f str) | isRawFormat f = return $ text str | otherwise = do report $ InlineNotRendered il -- cgit v1.2.3 From 3217bc192ec90f251ad54c098e5ada35f9aa863a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 31 Mar 2017 11:07:09 +0200 Subject: JATS writer: put references in `<back>`. Modified template to include a `<back>` and `<body>` section. This should give authors more flexibility, e.g. to put acknowledgements metadata in `<back>`. References are automatically extracted and put into `<back>`. --- src/Text/Pandoc/Writers/JATS.hs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs index 71a45bf77..aca7dc969 100644 --- a/src/Text/Pandoc/Writers/JATS.hs +++ b/src/Text/Pandoc/Writers/JATS.hs @@ -34,7 +34,7 @@ module Text.Pandoc.Writers.JATS ( writeJATS ) where import Control.Monad.Reader import Data.Char (toLower) import Data.Generics (everywhere, mkT) -import Data.List (intercalate, isSuffixOf) +import Data.List (intercalate, isSuffixOf, partition) import Data.Maybe (fromMaybe) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad, report) @@ -88,7 +88,11 @@ writeJATS opts d = -- | Convert Pandoc document to string in JATS format. docToJATS :: PandocMonad m => WriterOptions -> Pandoc -> DB m String docToJATS opts (Pandoc meta blocks) = do - let elements = hierarchicalize blocks + let isBackBlock (Div ("refs",_,_) _) = True + isBackBlock _ = False + let (backblocks, bodyblocks) = partition isBackBlock blocks + let elements = hierarchicalize bodyblocks + let backElements = hierarchicalize backblocks let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing @@ -112,9 +116,12 @@ docToJATS opts (Pandoc meta blocks) = do hierarchicalize)) (fmap (render colwidth) . inlinesToJATS opts') meta' - main <- (render' . inTagsIndented "body" . vcat) <$> + main <- (render' . vcat) <$> (mapM (elementToJATS opts' startLvl) elements) + back <- (render' . vcat) <$> + (mapM (elementToJATS opts' startLvl) backElements) let context = defField "body" main + $ defField "back" back $ defField "mathml" (case writerHTMLMathMethod opts of MathML -> True _ -> False) -- cgit v1.2.3 From 80c3c93273fee0eceee2ebd996c79e2151aee4d1 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 31 Mar 2017 15:15:49 +0200 Subject: JATS writer: don't include jats.csl in metadata if csl already specified. --- src/Text/Pandoc/App.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 5391f0fa6..d484a77e6 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -404,7 +404,9 @@ convertWithOpts opts = do withMediaBag . r readerOpts) sources return (mconcat (map fst pairs), mconcat (map snd pairs)) - metadata <- if format == "jats" + metadata <- if format == "jats" && + lookup "csl" (optMetadata opts) == Nothing && + lookup "citation-style" (optMetadata opts) == Nothing then do jatsCSL <- readDataFile datadir "jats.csl" let jatsEncoded = makeDataURI ("application/xml", jatsCSL) -- cgit v1.2.3 From 8761d5775075b8a0eb12be2ded8c931156b09a18 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 1 Apr 2017 11:05:12 +0200 Subject: Change MathJax CDN default since old one is shutting down. New URL: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js Announcement: https://www.mathjax.org/cdn-shutting-down/ NOTE: The new URL requires a version number, which we'll have to update manually in subsequent pandoc releases in order to take advantage of mathjax improvements. Closes #3544. --- src/Text/Pandoc/App.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index d484a77e6..dfc8e3559 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -1303,7 +1303,7 @@ options = , Option "" ["mathjax"] (OptArg (\arg opt -> do - let url' = fromMaybe "https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_CHTML-full" arg + let url' = fromMaybe "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-AMS_CHTML-full" arg return opt { optHTMLMathMethod = MathJax url'}) "URL") "" -- "Use MathJax for HTML math" -- cgit v1.2.3 From 34b9bee5a4eff0d4b76a2d9ac153105a4da363ed Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 1 Apr 2017 12:26:08 +0200 Subject: OpenDocument writer: wider labels for lists. This avoids overly narrow labels for ordered lists with () delimiters. However, arguably it creates overly wide labels for bullets. Also, lists now start flush with the margin, rather than indented. Fixes #2421. --- src/Text/Pandoc/Writers/OpenDocument.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index 3432d258a..491069343 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -531,10 +531,10 @@ orderedListLevelStyle (s,n, d) (l,ls) = listLevelStyle :: Int -> Doc listLevelStyle i = - let indent = show (0.25 * fromIntegral i :: Double) in + let indent = show (0.4 * fromIntegral (i - 1) :: Double) in selfClosingTag "style:list-level-properties" [ ("text:space-before" , indent ++ "in") - , ("text:min-label-width", "0.25in")] + , ("text:min-label-width", "0.4in")] tableStyle :: Int -> [(Char,Double)] -> Doc tableStyle num wcs = -- cgit v1.2.3 From 1c84a035098ab868db718b56f3f135603144d65a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 1 Apr 2017 17:07:39 +0200 Subject: Ms writer: added syntax highlighting. Closes #3547. Macro definitions are inserted in the template when there is highlighted code. Limitations: background colors and underline currently not supported. --- src/Text/Pandoc/Writers/Ms.hs | 89 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index be191c7da..00be502b3 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -43,7 +43,9 @@ import Text.Pandoc.Writers.Shared import Text.Pandoc.Options import Text.Pandoc.Writers.Math import Text.Printf ( printf ) +import qualified Data.Text as T import qualified Data.Map as Map +import Data.Maybe ( catMaybes, fromMaybe ) import Data.List ( intersperse, intercalate, sort ) import Text.Pandoc.Pretty import Text.Pandoc.Class (PandocMonad, report) @@ -53,11 +55,14 @@ import Control.Monad.State import Data.Char ( isLower, isUpper, toUpper ) import Text.TeXMath (writeEqn) import System.FilePath (takeExtension) +import Skylighting +import Text.Pandoc.Highlighting data WriterState = WriterState { stHasInlineMath :: Bool , stFirstPara :: Bool , stNotes :: [Note] , stSmallCaps :: Bool + , stHighlighting :: Bool , stFontFeatures :: Map.Map Char Bool } @@ -66,6 +71,7 @@ defaultWriterState = WriterState{ stHasInlineMath = False , stFirstPara = True , stNotes = [] , stSmallCaps = False + , stHighlighting = False , stFontFeatures = Map.fromList [ ('I',False) , ('B',False) @@ -98,6 +104,13 @@ pandocToMs opts (Pandoc meta blocks) = do hasInlineMath <- gets stHasInlineMath let titleMeta = (escapeString . stringify) $ docTitle meta let authorsMeta = map (escapeString . stringify) $ docAuthors meta + hasHighlighting <- gets stHighlighting + let highlightingMacros = if hasHighlighting + then case writerHighlightStyle opts of + Nothing -> "" + Just sty -> render' $ styleToMs sty + else "" + let context = defField "body" main $ defField "has-inline-math" hasInlineMath $ defField "hyphenate" True @@ -105,6 +118,7 @@ pandocToMs opts (Pandoc meta blocks) = do $ defField "toc" (writerTableOfContents opts) $ defField "title-meta" titleMeta $ defField "author-meta" (intercalate "; " authorsMeta) + $ defField "highlighting-macros" highlightingMacros $ metadata case writerTemplate opts of Nothing -> return main @@ -117,7 +131,7 @@ msEscapes = Map.fromList $ , ('\'', "\\[aq]") , ('`', "\\`") , ('\8217', "'") - , ('"', "\\\"") + , ('"', "\\[dq]") , ('\x2014', "\\[em]") , ('\x2013', "\\[en]") , ('\x2026', "\\&...") @@ -276,13 +290,14 @@ blockToMs opts (Header level (ident,classes,_) inlines) = do bookmark $$ anchor $$ tocEntry -blockToMs _ (CodeBlock _ str) = do +blockToMs opts (CodeBlock attr str) = do + hlCode <- highlightCode opts attr str setFirstPara return $ text ".IP" $$ text ".nf" $$ text "\\f[C]" $$ - text (escapeCode str) $$ + hlCode $$ text "\\f[]" $$ text ".fi" blockToMs opts (LineBlock ls) = do @@ -450,8 +465,9 @@ inlineToMs opts (Quoted DoubleQuote lst) = do return $ text "\\[lq]" <> contents <> text "\\[rq]" inlineToMs opts (Cite _ lst) = inlineListToMs opts lst -inlineToMs _ (Code _ str) = - withFontFeature 'C' (return $ text $ escapeCode str) +inlineToMs opts (Code attr str) = do + hlCode <- highlightCode opts attr str + withFontFeature 'C' (return hlCode) inlineToMs _ (Str str) = do let shim = case str of '.':_ -> afterBreak "\\&" @@ -549,3 +565,66 @@ breakToSpace :: Inline -> Inline breakToSpace SoftBreak = Space breakToSpace LineBreak = Space breakToSpace x = x + +-- Highlighting + +styleToMs :: Style -> Doc +styleToMs sty = vcat $ colordefs ++ map (toMacro sty) alltoktypes + where alltoktypes = enumFromTo KeywordTok NormalTok + colordefs = map toColorDef allcolors + toColorDef c = text (".defcolor " ++ + hexColor c ++ " rgb #" ++ hexColor c) + allcolors = catMaybes $ ordNub $ + [defaultColor sty, backgroundColor sty, + lineNumberColor sty, lineNumberBackgroundColor sty] ++ + concatMap colorsForToken (map snd (tokenStyles sty)) + colorsForToken ts = [tokenColor ts, tokenBackground ts] + +hexColor :: Color -> String +hexColor (RGB r g b) = printf "%02x%02x%02x" r g b + +toMacro :: Style -> TokenType -> Doc +toMacro sty toktype = + nowrap (text ".ds " <> text (show toktype) <> text " " <> + setbg <> setcolor <> setfont <> + text "\\\\$1" <> + resetfont <> resetcolor <> resetbg) + where setcolor = maybe empty fgcol tokCol + resetcolor = maybe empty (const $ text "\\\\m[]") tokCol + setbg = empty -- maybe empty bgcol tokBg + resetbg = empty -- maybe empty (const $ text "\\\\M[]") tokBg + fgcol c = text $ "\\\\m[" ++ hexColor c ++ "]" + -- bgcol c = text $ "\\\\M[" ++ hexColor c ++ "]" + setfont = if tokBold || tokItalic + then text $ "\\\\f[C" ++ ['B' | tokBold] ++ + ['I' | tokItalic] ++ "]" + else empty + resetfont = if tokBold || tokItalic + then text "\\\\f[C]" + else empty + tokSty = lookup toktype (tokenStyles sty) + tokCol = (tokSty >>= tokenColor) `mplus` defaultColor sty + -- tokBg = (tokSty >>= tokenBackground) `mplus` backgroundColor sty + tokBold = fromMaybe False (tokenBold <$> tokSty) + tokItalic = fromMaybe False (tokenItalic <$> tokSty) + -- tokUnderline = fromMaybe False (tokSty >>= tokUnderline) + -- lnColor = lineNumberColor sty + -- lnBkgColor = lineNumberBackgroundColor sty + +msFormatter :: FormatOptions -> [SourceLine] -> Doc +msFormatter _fmtopts = + vcat . map fmtLine + where fmtLine = hcat . map fmtToken + fmtToken (toktype, tok) = text "\\*" <> + brackets (text (show toktype) <> text " \"" + <> text (escapeCode (T.unpack tok)) <> text "\"") + +highlightCode :: PandocMonad m => WriterOptions -> Attr -> String -> MS m Doc +highlightCode opts attr str = + case highlight (writerSyntaxMap opts) msFormatter attr str of + Left msg -> do + unless (null msg) $ report $ CouldNotHighlight msg + return $ text (escapeCode str) + Right h -> do + modify (\st -> st{ stHighlighting = True }) + return h -- cgit v1.2.3 From 420e3eb26e6ac8b9854d41ede1bbd78e1c23fa66 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 1 Apr 2017 22:27:00 +0200 Subject: Allow a theme file as argument to `--highlight-style`. Also include a sample, `default.theme`, in `data/`. --- src/Text/Pandoc/App.hs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index dfc8e3559..8f0410f12 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -56,7 +56,7 @@ import Data.Yaml (decode) import qualified Data.Yaml as Yaml import Network.URI (URI (..), isURI, parseURI) import Paths_pandoc (getDataDir) -import Skylighting (Style, Syntax (..), defaultSyntaxMap) +import Skylighting (Style, Syntax (..), defaultSyntaxMap, parseTheme) import Skylighting.Parser (missingIncludes, parseSyntaxDefinition, addSyntaxDefinition) import System.Console.GetOpt @@ -802,7 +802,13 @@ writerFn f = liftIO . UTF8.writeFile f lookupHighlightStyle :: Maybe String -> IO (Maybe Style) lookupHighlightStyle Nothing = return Nothing -lookupHighlightStyle (Just s) = +lookupHighlightStyle (Just s) + | takeExtension s == ".theme" = -- attempt to load KDE theme + do contents <- B.readFile s + case parseTheme contents of + Left _ -> err 69 $ "Could not read highlighting theme " ++ s + Right sty -> return (Just sty) + | otherwise = case lookup (map toLower s) highlightingStyles of Just sty -> return (Just sty) Nothing -> err 68 $ "Unknown highlight-style " ++ s -- cgit v1.2.3 From e7eb21ecca46daaf240e33584c55b9d5101eebc7 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sun, 2 Apr 2017 17:21:22 +0200 Subject: Lua module: add readers submodule Plain text readers are exposed to lua scripts via the `pandoc.reader` submodule, which is further subdivided by format. Converting e.g. a markdown string into a pandoc document is possible from within lua: doc = pandoc.reader.markdown.read_doc("Hello, World!") A `read_block` convenience function is provided for all formats, although it will still parse the whole string but return only the first block as the result. Custom reader options are not supported yet, default options are used for all parsing operations. --- src/Text/Pandoc/Lua.hs | 31 ++------------- src/Text/Pandoc/Lua/PandocModule.hs | 74 +++++++++++++++++++++++++++++++++- src/Text/Pandoc/Lua/StackInstances.hs | 75 +++++++++++++++++++++++++++++++++++ 3 files changed, 150 insertions(+), 30 deletions(-) create mode 100644 src/Text/Pandoc/Lua/StackInstances.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 6fa6b2020..d754b43b8 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -15,11 +15,7 @@ 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 -} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# OPTIONS_GHC -fno-warn-orphans #-} {- | Module : Text.Pandoc.Lua Copyright : Copyright © 2017 Albert Krewinkel @@ -34,24 +30,23 @@ module Text.Pandoc.Lua ( runLuaFilter ) where import Control.Monad ( (>=>), when ) import Control.Monad.Trans ( MonadIO(..) ) -import Data.Aeson ( FromJSON(..), ToJSON(..), Result(..), Value, fromJSON ) import Data.HashMap.Lazy ( HashMap ) import Data.Text ( Text, pack, unpack ) import Data.Text.Encoding ( decodeUtf8 ) import Scripting.Lua ( LuaState, StackValue(..) ) -import Scripting.Lua.Aeson () +import Scripting.Lua.Aeson ( newstate ) import Text.Pandoc.Definition ( Block(..), Inline(..), Pandoc(..) ) import Text.Pandoc.Lua.PandocModule +import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Walk import qualified Data.HashMap.Lazy as HashMap import qualified Scripting.Lua as Lua -import qualified Scripting.Lua as LuaAeson runLuaFilter :: (MonadIO m) => FilePath -> [String] -> Pandoc -> m Pandoc runLuaFilter filterPath args pd = liftIO $ do - lua <- LuaAeson.newstate + lua <- newstate Lua.openlibs lua Lua.newtable lua Lua.setglobal lua "PANDOC_FILTER_FUNCTIONS" -- hack, store functions here @@ -204,23 +199,3 @@ isLuaFunction lua fnName = do res <- Lua.isfunction lua (-1) Lua.pop lua (-1) return res - -maybeFromJson :: (FromJSON a) => Maybe Value -> Maybe a -maybeFromJson mv = fromJSON <$> mv >>= \case - Success x -> Just x - _ -> Nothing - -instance StackValue Pandoc where - push lua = Lua.push lua . toJSON - peek lua i = maybeFromJson <$> peek lua i - valuetype _ = Lua.TTABLE - -instance StackValue Block where - push lua = Lua.push lua . toJSON - peek lua i = maybeFromJson <$> peek lua i - valuetype _ = Lua.TTABLE - -instance StackValue Inline where - push lua = Lua.push lua . toJSON - peek lua i = maybeFromJson <$> peek lua i - valuetype _ = Lua.TTABLE diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index 87d1fa6b9..d0c78f562 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -28,11 +28,25 @@ Pandoc module for lua. module Text.Pandoc.Lua.PandocModule ( pushPandocModule ) where import Data.ByteString.Char8 ( unpack ) -import Scripting.Lua ( LuaState, call) +import Data.Default ( Default(..) ) +import Scripting.Lua ( LuaState, call, newtable, push, pushhsfunction, rawset) +import Text.Pandoc.Class hiding ( readDataFile ) +import Text.Pandoc.Definition ( Pandoc(..), Block(..) ) import Text.Pandoc.Lua.Compat ( loadstring ) +import Text.Pandoc.Lua.StackInstances () +import Text.Pandoc.Readers.DocBook ( readDocBook ) +import Text.Pandoc.Readers.HTML ( readHtml ) +import Text.Pandoc.Readers.LaTeX ( readLaTeX ) +import Text.Pandoc.Readers.Native ( readNative ) +import Text.Pandoc.Readers.Markdown ( readMarkdown ) +import Text.Pandoc.Readers.MediaWiki ( readMediaWiki ) +import Text.Pandoc.Readers.Org ( readOrg ) +import Text.Pandoc.Readers.RST ( readRST ) +import Text.Pandoc.Readers.Textile ( readTextile ) +import Text.Pandoc.Readers.TWiki ( readTWiki ) +import Text.Pandoc.Readers.Txt2Tags ( readTxt2Tags ) import Text.Pandoc.Shared ( readDataFile ) - -- | Push the "pandoc" on the lua stack. pushPandocModule :: LuaState -> IO () pushPandocModule lua = do @@ -42,7 +56,63 @@ pushPandocModule lua = do then return () else do call lua 0 1 + push lua "reader" + pushReadersModule lua readers + rawset lua (-3) + +readers :: [(String, String -> PandocIO Pandoc)] +readers = + [ ("docbook", readDocBook def) + , ("html", readHtml def) + , ("latex", readLaTeX def) + , ("native", readNative def) + , ("markdown", readMarkdown def) + , ("mediawiki", readMediaWiki def) + , ("org", readOrg def) + , ("rst", readRST def) + , ("textile", readTextile def) + , ("twiki", readTWiki def) + , ("txt2tags", readTxt2Tags def) + ] -- | Get the string representation of the pandoc module pandocModuleScript :: IO String pandocModuleScript = unpack <$> readDataFile Nothing "pandoc.lua" + +-- | Push a lua table containing readers of the given formats. +pushReadersModule :: LuaState + -> [(String, String -> PandocIO Pandoc)] + -> IO () +pushReadersModule lua readerFns = do + newtable lua + mapM_ (uncurry $ addReaderTable) readerFns + where + addReaderTable :: String + -> (String -> PandocIO Pandoc) + -> IO () + addReaderTable formatName readerFn = do + let readDoc :: String -> IO Pandoc + readDoc s = do + res <- runIO $ readerFn s + case res of + (Left x) -> error (show x) + (Right x) -> return x + let readBlock :: String -> IO Block + readBlock s = do + Pandoc _ blks <- readDoc s + return $ case blks of + x:_ -> x + _ -> Null + -- Push table containing all functions for this format + push lua formatName + newtable lua + -- set document-reading function + push lua "read_doc" + pushhsfunction lua readDoc + rawset lua (-3) + -- set block-reading function + push lua "read_block" + pushhsfunction lua readBlock + rawset lua (-3) + -- store table in readers module + rawset lua (-3) diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs new file mode 100644 index 000000000..0c9addc23 --- /dev/null +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -0,0 +1,75 @@ +{- +Copyright © 2012-2015 John MacFarlane <jgm@berkeley.edu> + 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> + +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 +-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE LambdaCase #-} +#if !MIN_VERSION_base(4,8,0) +{-# LANGUAGE OverlappingInstances #-} +#endif +{-# OPTIONS_GHC -fno-warn-orphans #-} +{- | + Module : Text.Pandoc.Lua.StackInstances + Copyright : Copyright © 2017 Albert Krewinkel + License : GNU GPL, version 2 or above + + Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> + Stability : alpha + +StackValue instances for pandoc types. +-} +module Text.Pandoc.Lua.StackInstances () where + +import Data.Aeson ( FromJSON(..), ToJSON(..), Result(..), Value, fromJSON ) +import Scripting.Lua ( StackValue(..) ) +import Scripting.Lua.Aeson () +import Text.Pandoc.Definition ( Block(..), Inline(..), Pandoc(..) ) + +import qualified Scripting.Lua as Lua +import qualified Text.Pandoc.UTF8 as UTF8 + +maybeFromJson :: (FromJSON a) => Maybe Value -> Maybe a +maybeFromJson mv = fromJSON <$> mv >>= \case + Success x -> Just x + _ -> Nothing + +instance StackValue Pandoc where + push lua = Lua.push lua . toJSON + peek lua i = maybeFromJson <$> peek lua i + valuetype _ = Lua.TTABLE + +instance StackValue Block where + push lua = Lua.push lua . toJSON + peek lua i = maybeFromJson <$> peek lua i + valuetype _ = Lua.TTABLE + +instance StackValue Inline where + push lua = Lua.push lua . toJSON + peek lua i = maybeFromJson <$> peek lua i + valuetype _ = Lua.TTABLE + +#if MIN_VERSION_base(4,8,0) +instance {-# OVERLAPS #-} StackValue [Char] where +#else +instance StackValue [Char] where +#endif + push lua cs = Lua.push lua (UTF8.fromString cs) + peek lua i = do + res <- Lua.peek lua i + return $ UTF8.toString `fmap` res + valuetype _ = Lua.TSTRING -- cgit v1.2.3 From 913db947a9cb43b6f449db2cd4c85fd74aa1ac8f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 2 Apr 2017 23:02:55 +0200 Subject: Text.Pandoc.App: Throw errors rather than exiting. These are caught (and lead to exit) in pandoc.hs, but other uses of Text.Pandoc.App may want to recover in another way. Added PandocAppError to PandocError (API change). This is a stopgap: later we should have a separate constructor for each type of error. Also fixed uses of 'exit' in Shared.readDataFile, and removed 'err' from Shared (API change). Finally, removed the dependency on extensible-exceptions. See #3548. --- src/Text/Pandoc/App.hs | 94 +++++++++++++++++++++++++------------------- src/Text/Pandoc/Error.hs | 11 +++++- src/Text/Pandoc/Shared.hs | 16 ++------ src/Text/Pandoc/Templates.hs | 2 +- 4 files changed, 68 insertions(+), 55 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 8f0410f12..b7ac4fd75 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -39,7 +39,6 @@ module Text.Pandoc.App ( ) where import Control.Applicative ((<|>)) import qualified Control.Exception as E -import Control.Exception.Extensible (throwIO) import Control.Monad import Control.Monad.Trans import Data.Aeson (eitherDecode', encode) @@ -68,6 +67,7 @@ import System.FilePath import System.IO (stderr, stdout) import System.IO.Error (isDoesNotExistError) import Text.Pandoc +import Text.Pandoc.Error (PandocError(..)) import Text.Pandoc.Builder (setMeta) import Text.Pandoc.Class (PandocIO, getLog, withMediaBag) import Text.Pandoc.Highlighting (highlightingStyles) @@ -76,7 +76,7 @@ import Text.Pandoc.MediaBag (MediaBag, extractMediaBag, mediaDirectory) import Text.Pandoc.PDF (makePDF) import Text.Pandoc.Process (pipeProcess) import Text.Pandoc.SelfContained (makeSelfContained, makeDataURI) -import Text.Pandoc.Shared (err, headerShift, openURL, readDataFile, +import Text.Pandoc.Shared (headerShift, openURL, readDataFile, readDataFileUTF8, safeRead, tabFilter) import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Walk (walk) @@ -98,7 +98,8 @@ parseOptions options' defaults = do let unknownOptionErrors = foldr handleUnrecognizedOption [] unrecognizedOpts unless (null errors && null unknownOptionErrors) $ - err 2 $ concat errors ++ unlines unknownOptionErrors ++ + E.throwIO $ PandocAppError 2 $ + concat errors ++ unlines unknownOptionErrors ++ ("Try " ++ prg ++ " --help for more information.") -- thread option data structure through all supplied option actions @@ -175,7 +176,7 @@ convertWithOpts opts = do (\o d -> liftIO $ writeCustom writerName o d) :: Writer PandocIO) else case getWriter writerName of - Left e -> err 9 $ + Left e -> E.throwIO $ PandocAppError 9 $ if format == "pdf" then e ++ "\nTo create a pdf with pandoc, use " ++ @@ -189,7 +190,7 @@ convertWithOpts opts = do -- the sake of the text2tags reader. reader <- case getReader readerName of Right r -> return (r :: Reader PandocIO) - Left e -> err 7 e' + Left e -> E.throwIO $ PandocAppError 7 e' where e' = case readerName of "pdf" -> e ++ "\nPandoc can convert to PDF, but not from PDF." @@ -204,7 +205,7 @@ convertWithOpts opts = do Nothing -> do deftemp <- getDefaultTemplate datadir format case deftemp of - Left e -> throwIO e + Left e -> E.throwIO e Right t -> return (Just t) Just tp -> do -- strip off extensions @@ -217,8 +218,8 @@ convertWithOpts opts = do (readDataFileUTF8 datadir ("templates" </> tp')) (\e' -> let _ = (e' :: E.SomeException) - in throwIO e') - else throwIO e) + in E.throwIO e') + else E.throwIO e) let addStringAsVariable varname s vars = return $ (varname, s) : vars @@ -304,7 +305,7 @@ convertWithOpts opts = do let addSyntaxMap existingmap f = do res <- parseSyntaxDefinition f case res of - Left errstr -> err 67 errstr + Left errstr -> E.throwIO $ PandocAppError 67 errstr Right syn -> return $ addSyntaxDefinition syn existingmap syntaxMap <- foldM addSyntaxMap defaultSyntaxMap @@ -312,7 +313,8 @@ convertWithOpts opts = do case missingIncludes (M.elems syntaxMap) of [] -> return () - xs -> err 73 $ "Missing syntax definitions:\n" ++ + xs -> E.throwIO $ PandocAppError 73 $ + "Missing syntax definitions:\n" ++ unlines (map (\(syn,dep) -> (T.unpack syn ++ " requires " ++ T.unpack dep ++ " through IncludeRules.")) xs) @@ -358,7 +360,8 @@ convertWithOpts opts = do istty <- queryTerminal stdOutput #endif when (istty && not (isTextFormat format) && outputFile == "-") $ - err 5 $ "Cannot write " ++ format ++ " output to stdout.\n" ++ + E.throwIO $ PandocAppError 5 $ + "Cannot write " ++ format ++ " output to stdout.\n" ++ "Specify an output file using the -o option." @@ -386,7 +389,8 @@ convertWithOpts opts = do Just logfile -> B.writeFile logfile (encodeLogMessages reports) let isWarning msg = messageVerbosity msg == WARNING when (optFailIfWarnings opts && any isWarning reports) $ - err 3 "Failing because there were warnings." + E.throwIO $ + PandocAppError 3 "Failing because there were warnings." return res let sourceToDoc :: [FilePath] -> PandocIO (Pandoc, MediaBag) @@ -429,8 +433,8 @@ convertWithOpts opts = do -- make sure writer is latex, beamer, context, html5 or ms unless (laTeXOutput || conTeXtOutput || html5Output || msOutput) $ - err 47 $ "cannot produce pdf output with " ++ format ++ - " writer" + liftIO $ E.throwIO $ PandocAppError 47 $ + "cannot produce pdf output with " ++ format ++ " writer" let pdfprog = case () of _ | conTeXtOutput -> "context" @@ -441,7 +445,8 @@ convertWithOpts opts = do -- check for pdf creating program mbPdfProg <- liftIO $ findExecutable pdfprog when (isNothing mbPdfProg) $ - err 41 $ pdfprog ++ " not found. " ++ + liftIO $ E.throwIO $ PandocAppError 41 $ + pdfprog ++ " not found. " ++ pdfprog ++ " is needed for pdf output." res <- makePDF pdfprog f writerOptions verbosity media doc' @@ -450,7 +455,7 @@ convertWithOpts opts = do Left err' -> liftIO $ do B.hPutStr stderr err' B.hPut stderr $ B.pack [10] - err 43 "Error producing PDF" + E.throwIO $ PandocAppError 43 "Error producing PDF" | otherwise -> do let htmlFormat = format `elem` ["html","html4","html5","s5","slidy","slideous","dzslides","revealjs"] @@ -492,19 +497,21 @@ externalFilter f args' d = liftIO $ do unless (exists && isExecutable) $ do mbExe <- findExecutable f' when (isNothing mbExe) $ - err 83 $ "Error running filter " ++ f ++ ":\n" ++ - "Could not find executable '" ++ f' ++ "'." + E.throwIO $ PandocAppError 83 $ + "Error running filter " ++ f ++ ":\n" ++ + "Could not find executable '" ++ f' ++ "'." env <- getEnvironment let env' = Just $ ("PANDOC_VERSION", pandocVersion) : env (exitcode, outbs) <- E.handle filterException $ pipeProcess env' f' args'' $ encode d case exitcode of ExitSuccess -> return $ either error id $ eitherDecode' outbs - ExitFailure ec -> err 83 $ "Error running filter " ++ f ++ "\n" ++ - "Filter returned error status " ++ show ec + ExitFailure ec -> E.throwIO $ PandocAppError 83 $ + "Error running filter " ++ f ++ "\n" ++ + "Filter returned error status " ++ show ec where filterException :: E.SomeException -> IO a - filterException e = err 83 $ "Error running filter " ++ f ++ "\n" ++ - show e + filterException e = E.throwIO $ PandocAppError 83 $ + "Error running filter " ++ f ++ "\n" ++ show e -- | Data structure for command line options. data Opt = Opt @@ -806,12 +813,14 @@ lookupHighlightStyle (Just s) | takeExtension s == ".theme" = -- attempt to load KDE theme do contents <- B.readFile s case parseTheme contents of - Left _ -> err 69 $ "Could not read highlighting theme " ++ s + Left _ -> E.throwIO $ PandocAppError 69 $ + "Could not read highlighting theme " ++ s Right sty -> return (Just sty) | otherwise = case lookup (map toLower s) highlightingStyles of Just sty -> return (Just sty) - Nothing -> err 68 $ "Unknown highlight-style " ++ s + Nothing -> E.throwIO $ PandocAppError 68 $ + "Unknown highlight-style " ++ s -- | A list of functions, each transforming the options data structure -- in response to a command-line option. @@ -847,8 +856,8 @@ options = case safeRead arg of Just t | t > 0 && t < 6 -> return opt{ optBaseHeaderLevel = t } - _ -> err 19 - "base-header-level must be 1-5") + _ -> E.throwIO $ PandocAppError 19 + "base-header-level must be 1-5") "NUMBER") "" -- "Headers base level" @@ -881,8 +890,8 @@ options = (\arg opt -> case safeRead arg of Just t | t > 0 -> return opt { optTabStop = t } - _ -> err 31 - "tab-stop must be a number greater than 0") + _ -> E.throwIO $ PandocAppError 31 + "tab-stop must be a number greater than 0") "NUMBER") "" -- "Tab stop (default 4)" @@ -893,7 +902,7 @@ options = "accept" -> return AcceptChanges "reject" -> return RejectChanges "all" -> return AllChanges - _ -> err 6 + _ -> E.throwIO $ PandocAppError 6 ("Unknown option for track-changes: " ++ arg) return opt { optTrackChanges = action }) "accept|reject|all") @@ -964,7 +973,7 @@ options = (\arg opt -> case safeRead arg of Just t | t > 0 -> return opt { optDpi = t } - _ -> err 31 + _ -> E.throwIO $ PandocAppError 31 "dpi must be a number greater than 0") "NUMBER") "" -- "Dpi (default 96)" @@ -974,7 +983,8 @@ options = (\arg opt -> case safeRead ("Wrap" ++ uppercaseFirstLetter arg) of Just o -> return opt { optWrapText = o } - Nothing -> err 77 "--wrap must be auto, none, or preserve") + Nothing -> E.throwIO $ PandocAppError 77 + "--wrap must be auto, none, or preserve") "auto|none|preserve") "" -- "Option for wrapping text in output" @@ -983,7 +993,7 @@ options = (\arg opt -> case safeRead arg of Just t | t > 0 -> return opt { optColumns = t } - _ -> err 33 + _ -> E.throwIO $ PandocAppError 33 "columns must be a number greater than 0") "NUMBER") "" -- "Length of line in characters" @@ -999,7 +1009,7 @@ options = case safeRead arg of Just t | t >= 1 && t <= 6 -> return opt { optTOCDepth = t } - _ -> err 57 + _ -> E.throwIO $ PandocAppError 57 "TOC level must be a number between 1 and 6") "NUMBER") "" -- "Number of levels to include in TOC" @@ -1075,7 +1085,7 @@ options = "block" -> return EndOfBlock "section" -> return EndOfSection "document" -> return EndOfDocument - _ -> err 6 + _ -> E.throwIO $ PandocAppError 6 ("Unknown option for reference-location: " ++ arg) return opt { optReferenceLocation = action }) "block|section|document") @@ -1092,8 +1102,9 @@ options = let tldName = "TopLevel" ++ uppercaseFirstLetter arg case safeRead tldName of Just tlDiv -> return opt { optTopLevelDivision = tlDiv } - _ -> err 76 ("Top-level division must be " ++ - "section, chapter, part, or default")) + _ -> E.throwIO $ PandocAppError 76 + ("Top-level division must be " ++ + "section, chapter, part, or default")) "section|chapter|part") "" -- "Use top-level division type in LaTeX, ConTeXt, DocBook" @@ -1108,7 +1119,8 @@ options = case safeRead ('[':arg ++ "]") of Just ns -> return opt { optNumberOffset = ns, optNumberSections = True } - _ -> err 57 "could not parse number-offset") + _ -> E.throwIO $ PandocAppError 57 + "could not parse number-offset") "NUMBERS") "" -- "Starting number for sections, subsections, etc." @@ -1128,7 +1140,7 @@ options = case safeRead arg of Just t | t >= 1 && t <= 6 -> return opt { optSlideLevel = Just t } - _ -> err 39 + _ -> E.throwIO $ PandocAppError 39 "slide level must be a number between 1 and 6") "NUMBER") "" -- "Force header level for slides" @@ -1151,7 +1163,7 @@ options = "references" -> return ReferenceObfuscation "javascript" -> return JavascriptObfuscation "none" -> return NoObfuscation - _ -> err 6 + _ -> E.throwIO $ PandocAppError 6 ("Unknown obfuscation method: " ++ arg) return opt { optEmailObfuscation = method }) "none|javascript|references") @@ -1213,7 +1225,7 @@ options = case safeRead arg of Just t | t >= 1 && t <= 6 -> return opt { optEpubChapterLevel = t } - _ -> err 59 + _ -> E.throwIO $ PandocAppError 59 "chapter level must be a number between 1 and 6") "NUMBER") "" -- "Header level at which to split chapters in EPUB" @@ -1224,7 +1236,7 @@ options = let b = takeBaseName arg if b `elem` ["pdflatex", "lualatex", "xelatex"] then return opt { optLaTeXEngine = arg } - else err 45 "latex-engine must be pdflatex, lualatex, or xelatex.") + else E.throwIO $ PandocAppError 45 "latex-engine must be pdflatex, lualatex, or xelatex.") "PROGRAM") "" -- "Name of latex program to use in generating PDF" diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs index 4b38348ac..252c469b1 100644 --- a/src/Text/Pandoc/Error.hs +++ b/src/Text/Pandoc/Error.hs @@ -37,9 +37,11 @@ module Text.Pandoc.Error ( import Control.Exception (Exception) import Data.Generics (Typeable) import GHC.Generics (Generic) -import Text.Pandoc.Shared (err) import Text.Parsec.Error import Text.Parsec.Pos hiding (Line) +import qualified Text.Pandoc.UTF8 as UTF8 +import System.Exit (exitWith, ExitCode(..)) +import System.IO (stderr) type Input = String @@ -49,6 +51,7 @@ data PandocError = PandocIOError String IOError | PandocParseError String | PandocParsecError Input ParseError | PandocMakePDFError String + | PandocAppError Int String deriving (Show, Typeable, Generic) instance Exception PandocError @@ -74,4 +77,10 @@ handleError (Left e) = else "" in err 65 $ "\nError at " ++ show err' ++ errorInFile PandocMakePDFError s -> err 65 s + PandocAppError ec s -> err ec s +err :: Int -> String -> IO a +err exitCode msg = do + UTF8.hPutStrLn stderr msg + exitWith $ ExitFailure exitCode + return undefined diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 3b9ae7501..dfdbaf428 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -82,7 +82,6 @@ module Text.Pandoc.Shared ( collapseFilePath, filteredFilesFromArchive, -- * Error handling - err, mapLeft, -- * for squashing blocks blocksToInlines, @@ -99,7 +98,6 @@ import Text.Pandoc.Walk import Text.Pandoc.Builder (Inlines, Blocks, ToMetaValue(..)) import qualified Text.Pandoc.Builder as B import qualified Text.Pandoc.UTF8 as UTF8 -import System.Exit (exitWith, ExitCode(..)) import Data.Char ( toLower, isLower, isUpper, isAlpha, isLetter, isDigit, isSpace ) import Data.List ( find, stripPrefix, intercalate ) @@ -112,16 +110,15 @@ import System.Directory import System.FilePath (splitDirectories, isPathSeparator) import qualified System.FilePath.Posix as Posix import Text.Pandoc.MIME (MimeType) +import Text.Pandoc.Error (PandocError(..)) import System.FilePath ( (</>) ) import Data.Generics (Typeable, Data) import qualified Control.Monad.State as S -import Control.Monad.Trans (MonadIO (..)) import qualified Control.Exception as E import Control.Monad (msum, unless, MonadPlus(..)) import Text.Pandoc.Pretty (charWidth) import Text.Pandoc.Compat.Time import Data.Time.Clock.POSIX -import System.IO (stderr) import System.IO.Error import System.IO.Temp import Text.HTML.TagSoup (renderTagsOptions, RenderOptions(..), Tag(..), @@ -677,7 +674,8 @@ readDefaultDataFile "reference.odt" = readDefaultDataFile fname = #ifdef EMBED_DATA_FILES case lookup (makeCanonical fname) dataFiles of - Nothing -> err 97 $ "Could not find data file " ++ fname + Nothing -> E.throwIO $ PandocAppError 97 $ + "Could not find data file " ++ fname Just contents -> return contents where makeCanonical = Posix.joinPath . transformPathParts . splitDirectories transformPathParts = reverse . foldl go [] @@ -693,7 +691,7 @@ checkExistence fn = do exists <- doesFileExist fn if exists then return fn - else err 97 ("Could not find data file " ++ fn) + else E.throwIO $ PandocAppError 97 ("Could not find data file " ++ fn) #endif -- | Read file from specified user data directory or, if not found there, from @@ -759,12 +757,6 @@ openURL u -- Error reporting -- -err :: MonadIO m => Int -> String -> m a -err exitCode msg = liftIO $ do - UTF8.hPutStrLn stderr msg - exitWith $ ExitFailure exitCode - return undefined - mapLeft :: (a -> b) -> Either a c -> Either b c mapLeft f (Left x) = Left (f x) mapLeft _ (Right x) = Right x diff --git a/src/Text/Pandoc/Templates.hs b/src/Text/Pandoc/Templates.hs index 4ae2e80d7..26aeb9a73 100644 --- a/src/Text/Pandoc/Templates.hs +++ b/src/Text/Pandoc/Templates.hs @@ -41,7 +41,7 @@ module Text.Pandoc.Templates ( renderTemplate , Template , getDefaultTemplate ) where -import qualified Control.Exception.Extensible as E (IOException, try) +import qualified Control.Exception as E (IOException, try) import Data.Aeson (ToJSON (..)) import qualified Data.Text as T import System.FilePath ((<.>), (</>)) -- cgit v1.2.3 From ff991d1e2151479ff7dc15aba9c17251ff060408 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 2 Apr 2017 23:10:10 +0200 Subject: Revert "Revert "Use file-embed instead of hsb2hs to embed data files."" This reverts commit 1fa15c225b515e1fa1c6566f90f1be363a4d770f. --- src/Text/Pandoc/Data.hs | 17 +++++++++++++++++ src/Text/Pandoc/Data.hsb | 16 ---------------- 2 files changed, 17 insertions(+), 16 deletions(-) create mode 100644 src/Text/Pandoc/Data.hs delete mode 100644 src/Text/Pandoc/Data.hsb (limited to 'src/Text') diff --git a/src/Text/Pandoc/Data.hs b/src/Text/Pandoc/Data.hs new file mode 100644 index 000000000..df26f5412 --- /dev/null +++ b/src/Text/Pandoc/Data.hs @@ -0,0 +1,17 @@ +{-# LANGUAGE TemplateHaskell #-} + +module Text.Pandoc.Data (dataFiles) where + +import Data.FileEmbed +import qualified Data.ByteString as B +import System.FilePath (splitDirectories) +import qualified System.FilePath.Posix as Posix + +-- We ensure that the data files are stored using Posix +-- path separators (/), even on Windows. +dataFiles :: [(FilePath, B.ByteString)] +dataFiles = map (\(fp, contents) -> + (Posix.joinPath (splitDirectories fp), contents)) dataFiles' + +dataFiles' :: [(FilePath, B.ByteString)] +dataFiles' = ("MANUAL.txt", $(embedFile "MANUAL.txt")) : $(embedDir "data") diff --git a/src/Text/Pandoc/Data.hsb b/src/Text/Pandoc/Data.hsb deleted file mode 100644 index 02c109816..000000000 --- a/src/Text/Pandoc/Data.hsb +++ /dev/null @@ -1,16 +0,0 @@ -{-# LANGUAGE OverloadedStrings #-} --- to be processed using hsb2hs -module Text.Pandoc.Data (dataFiles) where -import qualified Data.ByteString as B -import System.FilePath (splitDirectories) -import qualified System.FilePath.Posix as Posix - --- We ensure that the data files are stored using Posix --- path separators (/), even on Windows. -dataFiles :: [(FilePath, B.ByteString)] -dataFiles = map (\(fp, contents) -> - (Posix.joinPath (splitDirectories fp), contents)) dataFiles' - -dataFiles' :: [(FilePath, B.ByteString)] -dataFiles' = ("MANUAL.txt", %blob "MANUAL.txt") : %blobs "data" - -- cgit v1.2.3 From e281a7cda0b7901995705138f10a3748004abff4 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 2 Apr 2017 23:29:58 +0200 Subject: Make sure docx/_rels/.rels gets into data files. embedDir in file-embed excludes hidden files, so we need to add this manually. --- src/Text/Pandoc/Data.hs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Data.hs b/src/Text/Pandoc/Data.hs index df26f5412..b8e189440 100644 --- a/src/Text/Pandoc/Data.hs +++ b/src/Text/Pandoc/Data.hs @@ -14,4 +14,8 @@ dataFiles = map (\(fp, contents) -> (Posix.joinPath (splitDirectories fp), contents)) dataFiles' dataFiles' :: [(FilePath, B.ByteString)] -dataFiles' = ("MANUAL.txt", $(embedFile "MANUAL.txt")) : $(embedDir "data") +dataFiles' = ("MANUAL.txt", $(embedFile "MANUAL.txt")) : + -- handle the hidden file separately, since embedDir doesn't + -- include it: + ("docx/_rels/.rels", $(embedFile "data/docx/_rels/.rels")) : + $(embedDir "data") -- cgit v1.2.3 From e3e52aa4c17aee452658e9252210d3621255964c Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 3 Apr 2017 10:20:51 +0200 Subject: Removed unused import. --- src/Text/Pandoc/App.hs | 1 - 1 file changed, 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index b7ac4fd75..886055849 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -67,7 +67,6 @@ import System.FilePath import System.IO (stderr, stdout) import System.IO.Error (isDoesNotExistError) import Text.Pandoc -import Text.Pandoc.Error (PandocError(..)) import Text.Pandoc.Builder (setMeta) import Text.Pandoc.Class (PandocIO, getLog, withMediaBag) import Text.Pandoc.Highlighting (highlightingStyles) -- cgit v1.2.3 From 6e2019d58812df59d1cb74723c71305c9b233bae Mon Sep 17 00:00:00 2001 From: Timm Albers <timm.albers@open-xchange.com> Date: Mon, 3 Apr 2017 14:29:32 +0200 Subject: Include \VerbatimFootnotes for highlighted code blocks Updated the LaTeX writer to also include \VerbatimFootnotes in the preamble for highlighted code blocks. Previously this was only done for raw code blocks. --- src/Text/Pandoc/Writers/LaTeX.hs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index eb38485de..70539a4a6 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -554,8 +554,11 @@ blockToLaTeX (CodeBlock (identifier,classes,keyvalAttr) str) = do unless (null msg) $ report $ CouldNotHighlight msg rawCodeBlock - Right h -> modify (\st -> st{ stHighlighting = True }) >> - return (flush $ linkAnchor $$ text (T.unpack h)) + Right h -> do + st <- get + when (stInNote st) $ modify (\s -> s{ stVerbInNote = True }) + modify (\s -> s{ stHighlighting = True }) + return (flush $ linkAnchor $$ text (T.unpack h)) case () of _ | isEnabled Ext_literate_haskell opts && "haskell" `elem` classes && "literate" `elem` classes -> lhsCodeBlock -- cgit v1.2.3 From 3e817124fe58bf4dbe23fbc93062eb5d34382148 Mon Sep 17 00:00:00 2001 From: Timm Albers <timmm.albers@gmail.com> Date: Mon, 3 Apr 2017 16:59:53 +0200 Subject: Add class to footnote back references The HTML writer now also adds the class footnoteBack to back references of footnotes. This allows for easier CSS styling. --- src/Text/Pandoc/Writers/HTML.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 42726bc61..ef5e6b416 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -1047,7 +1047,7 @@ blockListToNote :: PandocMonad m => WriterOptions -> String -> [Block] -> StateT 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. - let backlink = [Link nullAttr [Str "↩"] ("#" ++ writerIdentifierPrefix opts ++ "fnref" ++ ref,[])] + let backlink = [Link ("",["footnoteBack"],[]) [Str "↩"] ("#" ++ writerIdentifierPrefix opts ++ "fnref" ++ ref,[])] blocks' = if null blocks then [] else let lastBlock = last blocks -- cgit v1.2.3 From f1eb3b316904a20ccde4b60aa62977ad73e2d6a2 Mon Sep 17 00:00:00 2001 From: Timm Albers <timmm.albers@gmail.com> Date: Tue, 4 Apr 2017 10:36:00 +0200 Subject: Add original classes to JS obfuscated links (#3554) HTML links containing classes originally now preserve them when using javascript email obfuscation. Fixes #2989 --- src/Text/Pandoc/Writers/HTML.hs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index ef5e6b416..d56a6e4ae 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -475,6 +475,8 @@ obfuscateLink opts attr (renderHtml -> txt) s = then ("e", name' ++ " at " ++ domain') else ("'" ++ obfuscateString txt ++ "'", txt ++ " (" ++ name' ++ " at " ++ domain' ++ ")") + (_, classNames, _) = attr + classNamesStr = concatMap (' ':) classNames in case meth of ReferenceObfuscation -> -- need to use preEscapedString or &'s are escaped to & in URL @@ -487,7 +489,8 @@ obfuscateLink opts attr (renderHtml -> txt) s = preEscapedString ("\n<!--\nh='" ++ obfuscateString domain ++ "';a='" ++ at' ++ "';n='" ++ obfuscateString name' ++ "';e=n+a+h;\n" ++ - "document.write('<a h'+'ref'+'=\"ma'+'ilto'+':'+e+'\" clas'+'s=\"em' + 'ail\">'+" ++ + "document.write('<a h'+'ref'+'=\"ma'+'ilto'+':'+e+'\" clas'+'s=\"em' + 'ail" ++ + classNamesStr ++ "\">'+" ++ linkText ++ "+'<\\/'+'a'+'>');\n// -->\n")) >> H.noscript (preEscapedString $ obfuscateString altText) _ -> throwError $ PandocSomeError $ "Unknown obfuscation method: " ++ show meth -- cgit v1.2.3 From e650d1fbfd2e2486dac939638bde5c0d325da8a9 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 4 Apr 2017 14:34:39 +0200 Subject: Error: Added PandocOptionError. --- src/Text/Pandoc/App.hs | 2 +- src/Text/Pandoc/Error.hs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 886055849..107ca435f 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -97,7 +97,7 @@ parseOptions options' defaults = do let unknownOptionErrors = foldr handleUnrecognizedOption [] unrecognizedOpts unless (null errors && null unknownOptionErrors) $ - E.throwIO $ PandocAppError 2 $ + E.throwIO $ PandocOptionError $ concat errors ++ unlines unknownOptionErrors ++ ("Try " ++ prg ++ " --help for more information.") diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs index 252c469b1..454ad9982 100644 --- a/src/Text/Pandoc/Error.hs +++ b/src/Text/Pandoc/Error.hs @@ -51,6 +51,7 @@ data PandocError = PandocIOError String IOError | PandocParseError String | PandocParsecError Input ParseError | PandocMakePDFError String + | PandocOptionError String | PandocAppError Int String deriving (Show, Typeable, Generic) @@ -77,6 +78,7 @@ handleError (Left e) = else "" in err 65 $ "\nError at " ++ show err' ++ errorInFile PandocMakePDFError s -> err 65 s + PandocOptionError s -> err 2 s PandocAppError ec s -> err ec s err :: Int -> String -> IO a -- cgit v1.2.3 From 1ebb766aff22590874b9660322f1788c3698649a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 4 Apr 2017 16:13:24 +0200 Subject: Ms writer: ensure that @ is escaped in URIs. Otherwise we may get unescaped @s that give eqn fits, with @ as the delimiter character. --- src/Text/Pandoc/Writers/Ms.hs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 00be502b3..aadbd208e 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -57,6 +57,7 @@ import Text.TeXMath (writeEqn) import System.FilePath (takeExtension) import Skylighting import Text.Pandoc.Highlighting +import Network.URI (escapeURIString, isAllowedInURI) data WriterState = WriterState { stHasInlineMath :: Bool , stFirstPara :: Bool @@ -157,6 +158,9 @@ escapeBar = concatMap go escapeString :: String -> String escapeString = concatMap escapeChar +escapeUri :: String -> String +escapeUri = escapeURIString (\c -> c /= '@' && isAllowedInURI c) + toSmallCaps :: String -> String toSmallCaps [] = [] toSmallCaps (c:cs) @@ -509,7 +513,7 @@ inlineToMs opts (Link _ txt (src, _)) = do -- external link contents <- inlineListToMs' opts $ map breakToSpace txt return $ text "\\c" <> cr <> nowrap (text ".pdfhref W -D " <> - doubleQuotes (text src) <> text " -A " <> + doubleQuotes (text (escapeUri src)) <> text " -A " <> doubleQuotes (text "\\c") <> space <> text "\\") <> cr <> text " -- " <> doubleQuotes (nowrap contents) <> cr <> text "\\&" inlineToMs opts (Image attr alternate (source, tit)) = do -- cgit v1.2.3 From 48729f9715f4deec6d621c363fd4d16d089497b2 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 4 Apr 2017 17:21:02 +0200 Subject: Ms writer improvements: - added some variables to the default template. - cleaner output for images (stringify alt text). --- src/Text/Pandoc/Writers/Ms.hs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index aadbd208e..4118a7bfb 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -516,10 +516,9 @@ inlineToMs opts (Link _ txt (src, _)) = do doubleQuotes (text (escapeUri src)) <> text " -A " <> doubleQuotes (text "\\c") <> space <> text "\\") <> cr <> text " -- " <> doubleQuotes (nowrap contents) <> cr <> text "\\&" -inlineToMs opts (Image attr alternate (source, tit)) = do - let alt = if null alternate then [Str "image"] else alternate - linkPart <- inlineToMs opts (Link attr alt (source, tit)) - return $ char '[' <> text "IMAGE: " <> linkPart <> char ']' +inlineToMs _ (Image _ alternate (_, _)) = + return $ char '[' <> text "IMAGE: " <> + text (escapeString (stringify alternate)) <> char ']' inlineToMs _ (Note contents) = do modify $ \st -> st{ stNotes = contents : stNotes st } return $ text "\\**" -- cgit v1.2.3 From 12a3481632bfc3d477759095fa01fa92e169b292 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 5 Apr 2017 15:17:35 +0200 Subject: Ms writer: respect text wrapping options. --- src/Text/Pandoc/Writers/Ms.hs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 4118a7bfb..f162c4213 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -500,7 +500,12 @@ inlineToMs _ il@(RawInline f str) report $ InlineNotRendered il return empty inlineToMs _ (LineBreak) = return $ cr <> text ".br" <> cr -inlineToMs opts SoftBreak = handleNotes opts cr +inlineToMs opts SoftBreak = + handleNotes opts $ + case writerWrapText opts of + WrapAuto -> space + WrapNone -> space + WrapPreserve -> cr inlineToMs opts Space = handleNotes opts space inlineToMs opts (Link _ txt ('#':ident, _)) = do -- internal link -- cgit v1.2.3 From 12ae1df5bfa447f94d8a3db24dd890e54bcbcf55 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 6 Apr 2017 11:30:03 +0200 Subject: Allow raw latex commands starting with `\start` in Markdown. Previously these weren't allowed because they were interpreted as starting ConTeXt environments, even without a corresponding \stop... Closes #3558. --- src/Text/Pandoc/Readers/Markdown.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 5f08afe08..9eb242d74 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -1912,7 +1912,8 @@ inlineNote = try $ do rawLaTeXInline' :: PandocMonad m => MarkdownParser m (F Inlines) rawLaTeXInline' = try $ do guardEnabled Ext_raw_tex - lookAhead $ char '\\' >> notFollowedBy' (string "start") -- context env + lookAhead (char '\\') + notFollowedBy' rawConTeXtEnvironment RawInline _ s <- rawLaTeXInline return $ return $ B.rawInline "tex" s -- "tex" because it might be context or latex -- cgit v1.2.3 From 6b0d3d1582bd764a8496b4074608da11ae9349d4 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 6 Apr 2017 12:45:23 +0200 Subject: Ms writer: wider indents for lists. Previously some indents weren't wide enough, leading the list item to start on a line after the marker. --- src/Text/Pandoc/Writers/Ms.hs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index f162c4213..e4daa1be0 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -347,7 +347,7 @@ blockToMs opts (BulletList items) = do return (vcat contents) blockToMs opts (OrderedList attribs items) = do let markers = take (length items) $ orderedListMarkers attribs - let indent = 1 + (maximum $ map length markers) + let indent = 2 + (maximum $ map length markers) contents <- mapM (\(num, item) -> orderedListItemToMs opts num indent item) $ zip markers items setFirstPara @@ -365,15 +365,15 @@ bulletListItemToMs opts ((Para first):rest) = bulletListItemToMs opts ((Plain first):rest) = do first' <- blockToMs opts (Plain first) rest' <- blockListToMs opts rest - let first'' = text ".IP \\[bu] 2" $$ first' + let first'' = text ".IP \\[bu] 3" $$ first' let rest'' = if null rest then empty - else text ".RS 2" $$ rest' $$ text ".RE" + else text ".RS 3" $$ rest' $$ text ".RE" return (first'' $$ rest'') bulletListItemToMs opts (first:rest) = do first' <- blockToMs opts first rest' <- blockListToMs opts rest - return $ text "\\[bu] .RS 2" $$ first' $$ rest' $$ text ".RE" + return $ text "\\[bu] .RS 3" $$ first' $$ rest' $$ text ".RE" -- | Convert ordered list item (a list of blocks) to ms. orderedListItemToMs :: PandocMonad m @@ -392,7 +392,8 @@ orderedListItemToMs opts num indent (first:rest) = do let first'' = text (".IP \"" ++ num' ++ "\" " ++ show indent) $$ first' let rest'' = if null rest then empty - else text ".RS 4" $$ rest' $$ text ".RE" + else text ".RS " <> text (show indent) $$ + rest' $$ text ".RE" return $ first'' $$ rest'' -- | Convert definition list item (label, list of blocks) to ms. -- cgit v1.2.3 From fca93efb624af48a212a4597a116bfcde8b7192f Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Tue, 4 Apr 2017 21:51:51 +0200 Subject: Use lua registry instead of named globals This is slightly cleaner while keeping performance approximately the same. --- src/Text/Pandoc/Lua.hs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index d754b43b8..a68810bd7 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -48,8 +48,11 @@ runLuaFilter :: (MonadIO m) runLuaFilter filterPath args pd = liftIO $ do lua <- newstate Lua.openlibs lua + -- create table in registry to store filter functions + Lua.push lua ("PANDOC_FILTER_FUNCTIONS"::String) Lua.newtable lua - Lua.setglobal lua "PANDOC_FILTER_FUNCTIONS" -- hack, store functions here + Lua.rawset lua Lua.registryindex + -- store module in global "pandoc" pushPandocModule lua Lua.setglobal lua "pandoc" status <- Lua.loadfile lua filterPath @@ -171,12 +174,14 @@ runLuaFilterFunction lua lf inline = do Lua.pop lua 1 return res --- FIXME: use registry +-- | Push the filter function to the top of the stack. pushFilterFunction :: Lua.LuaState -> LuaFilterFunction a -> IO () pushFilterFunction lua lf = do - Lua.getglobal lua "PANDOC_FILTER_FUNCTIONS" + -- The function is stored in a lua registry table, retrieve it from there. + push lua ("PANDOC_FILTER_FUNCTIONS"::String) + Lua.rawget lua Lua.registryindex Lua.rawgeti lua (-1) (functionIndex lf) - Lua.remove lua (-2) -- remove global from stack + Lua.remove lua (-2) -- remove registry table from stack instance StackValue (LuaFilterFunction a) where valuetype _ = Lua.TFUNCTION @@ -185,7 +190,8 @@ instance StackValue (LuaFilterFunction a) where isFn <- Lua.isfunction lua i when (not isFn) (error $ "Not a function at index " ++ (show i)) Lua.pushvalue lua i - Lua.getglobal lua "PANDOC_FILTER_FUNCTIONS" + push lua ("PANDOC_FILTER_FUNCTIONS"::String) + Lua.rawget lua Lua.registryindex len <- Lua.objlen lua (-1) Lua.insert lua (-2) Lua.rawseti lua (-2) (len + 1) -- cgit v1.2.3 From dd00163a35a9c1aa9ddc58b720919a6219c87a17 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Thu, 6 Apr 2017 00:02:33 +0200 Subject: Lua filter: Improve inline filter performance Getting inline instances from the lua stack is handled manually for some simple inline constructors, including the `Str` constructor. This avoids the indirect route through aeson's Value type and improves performance considerably (approx. 30% speedup for some filters). --- src/Text/Pandoc/Lua/StackInstances.hs | 80 ++++++++++++++++++++++++++++++++--- 1 file changed, 73 insertions(+), 7 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index 0c9addc23..59c5ec6b5 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -36,7 +36,7 @@ StackValue instances for pandoc types. module Text.Pandoc.Lua.StackInstances () where import Data.Aeson ( FromJSON(..), ToJSON(..), Result(..), Value, fromJSON ) -import Scripting.Lua ( StackValue(..) ) +import Scripting.Lua ( LuaState, StackValue(..), newtable, pop, rawget, rawset ) import Scripting.Lua.Aeson () import Text.Pandoc.Definition ( Block(..), Inline(..), Pandoc(..) ) @@ -59,8 +59,20 @@ instance StackValue Block where valuetype _ = Lua.TTABLE instance StackValue Inline where - push lua = Lua.push lua . toJSON - peek lua i = maybeFromJson <$> peek lua i + push lua = \case + Emph inlns -> pushTagged lua "Emph" inlns + LineBreak -> pushTagged' lua "LineBreak" + Note blcks -> pushTagged lua "Note" blcks + SmallCaps inlns -> pushTagged lua "SmallCaps" inlns + SoftBreak -> pushTagged' lua "SoftBreak" + Space -> pushTagged' lua "Space" + Str s -> pushTagged lua "Str" s + Strikeout inlns -> pushTagged lua "Strikeout" inlns + Strong inlns -> pushTagged lua "Strong" inlns + Subscript inlns -> pushTagged lua "Subscript" inlns + Superscript inlns -> pushTagged lua "Superscript" inlns + x -> push lua (toJSON x) + peek = peekInline valuetype _ = Lua.TTABLE #if MIN_VERSION_base(4,8,0) @@ -68,8 +80,62 @@ instance {-# OVERLAPS #-} StackValue [Char] where #else instance StackValue [Char] where #endif - push lua cs = Lua.push lua (UTF8.fromString cs) - peek lua i = do - res <- Lua.peek lua i - return $ UTF8.toString `fmap` res + push lua cs = push lua (UTF8.fromString cs) + peek lua i = fmap UTF8.toString <$> peek lua i valuetype _ = Lua.TSTRING + +-- | Push a value to the lua stack, tagged with a given string. This currently +-- creates a structure equivalent to what the JSONified value would look like +-- when pushed to lua. +pushTagged :: StackValue a => LuaState -> String -> a -> IO () +pushTagged lua tag value = do + newtable lua + push lua "t" + push lua tag + rawset lua (-3) + push lua "c" + push lua value + rawset lua (-3) + +pushTagged' :: LuaState -> String -> IO () +pushTagged' lua tag = do + newtable lua + push lua "t" + push lua tag + rawset lua (-3) + +-- | Return the value at the given index as inline if possible. +peekInline :: LuaState -> Int -> IO (Maybe Inline) +peekInline lua idx = do + push lua "t" + rawget lua (idx `adjustIndexBy` 1) + tag <- peek lua (-1) <* pop lua 1 + case tag of + Nothing -> return Nothing + Just t -> case t of + "Emph" -> fmap Emph <$> elementContent + "LineBreak" -> return (Just LineBreak) + "Note" -> fmap Note <$> elementContent + "SmallCaps" -> fmap SmallCaps <$> elementContent + "SoftBreak" -> return (Just SoftBreak) + "Space" -> return (Just Space) + "Str" -> fmap Str <$> elementContent + "Strikeout" -> fmap Strikeout <$> elementContent + "Strong" -> fmap Strong <$> elementContent + "Subscript" -> fmap Subscript <$> elementContent + "Superscript"-> fmap Superscript <$> elementContent + _ -> maybeFromJson <$> peek lua idx + where + elementContent :: StackValue a => IO (Maybe a) + elementContent = do + push lua "c" + rawget lua (idx `adjustIndexBy` 1) + peek lua (-1) <* pop lua 1 + +-- | Adjust the stack index, assuming that @n@ new elements have been pushed on +-- the stack. +adjustIndexBy :: Int -> Int -> Int +adjustIndexBy idx n = + if idx < 0 + then idx - n + else idx -- cgit v1.2.3 From 9278a6325d01f2b8442103c98ad00b05e65c2b3e Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Thu, 6 Apr 2017 13:55:27 +0200 Subject: Lua filter: Improve block filter performance Reading of simple block values from the lua stack is handled manually, but most block constructors are still handled via instances of aeson's Value type. --- src/Text/Pandoc/Lua/StackInstances.hs | 51 ++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 7 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index 59c5ec6b5..601868095 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -54,8 +54,17 @@ instance StackValue Pandoc where valuetype _ = Lua.TTABLE instance StackValue Block where - push lua = Lua.push lua . toJSON - peek lua i = maybeFromJson <$> peek lua i + push lua = \case + BlockQuote blcks -> pushTagged lua "BlockQuote" blcks + BulletList items -> pushTagged lua "BulletList" items + HorizontalRule -> pushTagged' lua "HorizontalRule" + LineBlock blcks -> pushTagged lua "LineBlock" blcks + Null -> pushTagged' lua "Null" + Para blcks -> pushTagged lua "Para" blcks + Plain blcks -> pushTagged lua "Plain" blcks + -- fall back to conversion via aeson's Value + x -> push lua (toJSON x) + peek lua i = peekBlock lua i valuetype _ = Lua.TTABLE instance StackValue Inline where @@ -124,13 +133,41 @@ peekInline lua idx = do "Strong" -> fmap Strong <$> elementContent "Subscript" -> fmap Subscript <$> elementContent "Superscript"-> fmap Superscript <$> elementContent + -- fall back to construction via aeson's Value + _ -> maybeFromJson <$> peek lua idx + where + -- Get the contents of an AST element. + elementContent :: StackValue a => IO (Maybe a) + elementContent = do + push lua "c" + rawget lua (idx `adjustIndexBy` 1) + peek lua (-1) <* pop lua 1 + +-- | Return the value at the given index as block if possible. +peekBlock :: LuaState -> Int -> IO (Maybe Block) +peekBlock lua idx = do + push lua "t" + rawget lua (idx `adjustIndexBy` 1) + tag <- peek lua (-1) <* pop lua 1 + case tag of + Nothing -> return Nothing + Just t -> case t of + "BlockQuote" -> fmap BlockQuote <$> elementContent + "BulletList" -> fmap BulletList <$> elementContent + "HorizontalRule" -> return (Just HorizontalRule) + "LineBlock" -> fmap LineBlock <$> elementContent + "Null" -> return (Just Null) + "Para" -> fmap Para <$> elementContent + "Plain" -> fmap Plain <$> elementContent + -- fall back to construction via aeson's Value _ -> maybeFromJson <$> peek lua idx where - elementContent :: StackValue a => IO (Maybe a) - elementContent = do - push lua "c" - rawget lua (idx `adjustIndexBy` 1) - peek lua (-1) <* pop lua 1 + -- Get the contents of an AST element. + elementContent :: StackValue a => IO (Maybe a) + elementContent = do + push lua "c" + rawget lua (idx `adjustIndexBy` 1) + peek lua (-1) <* pop lua 1 -- | Adjust the stack index, assuming that @n@ new elements have been pushed on -- the stack. -- cgit v1.2.3 From 41ebdee5df4b322ce49ee955824047a7e4d888f9 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Fri, 7 Apr 2017 21:04:22 +0200 Subject: Lua filter: improve doc filter performance Pandoc elements are pushed and pulled from the lua stack via custom instances. --- src/Text/Pandoc/Lua/StackInstances.hs | 83 ++++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 36 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index 601868095..07ca06798 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -38,7 +38,7 @@ module Text.Pandoc.Lua.StackInstances () where import Data.Aeson ( FromJSON(..), ToJSON(..), Result(..), Value, fromJSON ) import Scripting.Lua ( LuaState, StackValue(..), newtable, pop, rawget, rawset ) import Scripting.Lua.Aeson () -import Text.Pandoc.Definition ( Block(..), Inline(..), Pandoc(..) ) +import Text.Pandoc.Definition ( Block(..), Inline(..), Meta(..), Pandoc(..) ) import qualified Scripting.Lua as Lua import qualified Text.Pandoc.UTF8 as UTF8 @@ -49,19 +49,30 @@ maybeFromJson mv = fromJSON <$> mv >>= \case _ -> Nothing instance StackValue Pandoc where - push lua = Lua.push lua . toJSON - peek lua i = maybeFromJson <$> peek lua i + push lua (Pandoc meta blocks) = do + newtable lua + setField lua (-1) "blocks" blocks + setField lua (-1) "meta" meta + peek lua idx = do + blocks <- getField lua idx "blocks" + meta <- getField lua idx "meta" + return $ Pandoc <$> meta <*> blocks + valuetype _ = Lua.TTABLE + +instance StackValue Meta where + push lua = push lua . toJSON + peek lua = fmap maybeFromJson . peek lua valuetype _ = Lua.TTABLE instance StackValue Block where push lua = \case - BlockQuote blcks -> pushTagged lua "BlockQuote" blcks - BulletList items -> pushTagged lua "BulletList" items + BlockQuote blcks -> pushTagged lua "BlockQuote" blcks + BulletList items -> pushTagged lua "BulletList" items HorizontalRule -> pushTagged' lua "HorizontalRule" - LineBlock blcks -> pushTagged lua "LineBlock" blcks + LineBlock blcks -> pushTagged lua "LineBlock" blcks Null -> pushTagged' lua "Null" - Para blcks -> pushTagged lua "Para" blcks - Plain blcks -> pushTagged lua "Plain" blcks + Para blcks -> pushTagged lua "Para" blcks + Plain blcks -> pushTagged lua "Plain" blcks -- fall back to conversion via aeson's Value x -> push lua (toJSON x) peek lua i = peekBlock lua i @@ -69,17 +80,17 @@ instance StackValue Block where instance StackValue Inline where push lua = \case - Emph inlns -> pushTagged lua "Emph" inlns + Emph inlns -> pushTagged lua "Emph" inlns LineBreak -> pushTagged' lua "LineBreak" - Note blcks -> pushTagged lua "Note" blcks - SmallCaps inlns -> pushTagged lua "SmallCaps" inlns + Note blcks -> pushTagged lua "Note" blcks + SmallCaps inlns -> pushTagged lua "SmallCaps" inlns SoftBreak -> pushTagged' lua "SoftBreak" Space -> pushTagged' lua "Space" - Str s -> pushTagged lua "Str" s - Strikeout inlns -> pushTagged lua "Strikeout" inlns - Strong inlns -> pushTagged lua "Strong" inlns - Subscript inlns -> pushTagged lua "Subscript" inlns - Superscript inlns -> pushTagged lua "Superscript" inlns + Str s -> pushTagged lua "Str" s + Strikeout inlns -> pushTagged lua "Strikeout" inlns + Strong inlns -> pushTagged lua "Strong" inlns + Subscript inlns -> pushTagged lua "Subscript" inlns + Superscript inlns -> pushTagged lua "Superscript" inlns x -> push lua (toJSON x) peek = peekInline valuetype _ = Lua.TTABLE @@ -99,12 +110,8 @@ instance StackValue [Char] where pushTagged :: StackValue a => LuaState -> String -> a -> IO () pushTagged lua tag value = do newtable lua - push lua "t" - push lua tag - rawset lua (-3) - push lua "c" - push lua value - rawset lua (-3) + setField lua (-1) "t" tag + setField lua (-1) "c" value pushTagged' :: LuaState -> String -> IO () pushTagged' lua tag = do @@ -116,9 +123,7 @@ pushTagged' lua tag = do -- | Return the value at the given index as inline if possible. peekInline :: LuaState -> Int -> IO (Maybe Inline) peekInline lua idx = do - push lua "t" - rawget lua (idx `adjustIndexBy` 1) - tag <- peek lua (-1) <* pop lua 1 + tag <- getField lua idx "t" case tag of Nothing -> return Nothing Just t -> case t of @@ -138,17 +143,12 @@ peekInline lua idx = do where -- Get the contents of an AST element. elementContent :: StackValue a => IO (Maybe a) - elementContent = do - push lua "c" - rawget lua (idx `adjustIndexBy` 1) - peek lua (-1) <* pop lua 1 + elementContent = getField lua idx "c" -- | Return the value at the given index as block if possible. peekBlock :: LuaState -> Int -> IO (Maybe Block) peekBlock lua idx = do - push lua "t" - rawget lua (idx `adjustIndexBy` 1) - tag <- peek lua (-1) <* pop lua 1 + tag <- getField lua idx "t" case tag of Nothing -> return Nothing Just t -> case t of @@ -164,10 +164,7 @@ peekBlock lua idx = do where -- Get the contents of an AST element. elementContent :: StackValue a => IO (Maybe a) - elementContent = do - push lua "c" - rawget lua (idx `adjustIndexBy` 1) - peek lua (-1) <* pop lua 1 + elementContent = getField lua idx "c" -- | Adjust the stack index, assuming that @n@ new elements have been pushed on -- the stack. @@ -176,3 +173,17 @@ adjustIndexBy idx n = if idx < 0 then idx - n else idx + +-- | Get value behind key from table at given index. +getField :: (StackValue a, StackValue b) => LuaState -> Int -> a -> IO (Maybe b) +getField lua idx key = do + push lua key + rawget lua (idx `adjustIndexBy` 1) + peek lua (-1) <* pop lua 1 + +-- | Set value for key for table at the given index +setField :: (StackValue a, StackValue b) => LuaState -> Int -> a -> b -> IO () +setField lua idx key value = do + push lua key + push lua value + rawset lua (idx `adjustIndexBy` 2) -- cgit v1.2.3 From d4e5fe02b0adddbe82ed00c3aabe46b2915a1ed7 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 10 Apr 2017 22:21:11 +0200 Subject: Docx writer: don't take "distArchive" from datadir. The docx writer takes components from the distribution's version of reference.docx when it can't find them in a user's custom reference.docx. (This sometimes happens because Word will sometimes omit components needed for larger documents when saving a simple one.) Previously, we allowed a reference.docx in the data directory (e.g. `~/.pandoc`) to be used as the distribution's reference.docx. This led to a bizarre situation where pandoc would produce a good docx using `--template ~/.pandoc/ref.docx`, but if `ref.docx` were moved to `~/.pandoc/reference.docx`, it would then produce a corrupted docx. Closes #3322 (I think). --- src/Text/Pandoc/Writers/Docx.hs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index fcc8551a4..fddec91cc 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -231,10 +231,11 @@ writeDocx opts doc@(Pandoc meta _) = do username <- P.lookupEnv "USERNAME" utctime <- P.getCurrentTime distArchive <- (toArchive . BL.fromStrict) <$> - P.readDataFile datadir "reference.docx" + P.readDataFile Nothing "reference.docx" refArchive <- case writerReferenceDoc opts of Just f -> toArchive <$> P.readFileLazy f - Nothing -> return distArchive + Nothing -> (toArchive . BL.fromStrict) <$> + P.readDataFile datadir "reference.docx" parsedDoc <- parseXml refArchive distArchive "word/document.xml" let wname f qn = qPrefix qn == Just "w" && f (qName qn) -- cgit v1.2.3 From 7e3705c1c4a7b63ce6818c1e3cb3496ff618ac0f Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Tue, 11 Apr 2017 23:31:55 +0200 Subject: Lua filter: use custom StackValue Inline instance Inline elements are no longer pushed and pulled via aeson's Value. --- src/Text/Pandoc/Lua.hs | 8 +- src/Text/Pandoc/Lua/StackInstances.hs | 175 +++++++++++++++++++++++++++++----- 2 files changed, 156 insertions(+), 27 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index a68810bd7..d7c54b6af 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -170,9 +170,11 @@ runLuaFilterFunction lua lf inline = do pushFilterFunction lua lf Lua.push lua inline Lua.call lua 1 1 - Just res <- Lua.peek lua (-1) - Lua.pop lua 1 - return res + mbres <- Lua.peek lua (-1) + case mbres of + Nothing -> error $ "Error while trying to get a filter's return " + ++ "value from lua stack." + Just res -> res <$ Lua.pop lua 1 -- | Push the filter function to the top of the stack. pushFilterFunction :: Lua.LuaState -> LuaFilterFunction a -> IO () diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index 07ca06798..690557788 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -36,11 +36,15 @@ StackValue instances for pandoc types. module Text.Pandoc.Lua.StackInstances () where import Data.Aeson ( FromJSON(..), ToJSON(..), Result(..), Value, fromJSON ) -import Scripting.Lua ( LuaState, StackValue(..), newtable, pop, rawget, rawset ) +import Scripting.Lua + ( LTYPE(..), LuaState, StackValue(..) + , gettable, newtable, pop, rawgeti, rawset, rawseti, settable + ) import Scripting.Lua.Aeson () -import Text.Pandoc.Definition ( Block(..), Inline(..), Meta(..), Pandoc(..) ) +import Text.Pandoc.Definition + ( Block(..), Inline(..), Meta(..), Pandoc(..) + , Citation(..), CitationMode(..), Format(..), MathType(..), QuoteType(..) ) -import qualified Scripting.Lua as Lua import qualified Text.Pandoc.UTF8 as UTF8 maybeFromJson :: (FromJSON a) => Maybe Value -> Maybe a @@ -57,12 +61,12 @@ instance StackValue Pandoc where blocks <- getField lua idx "blocks" meta <- getField lua idx "meta" return $ Pandoc <$> meta <*> blocks - valuetype _ = Lua.TTABLE + valuetype _ = TTABLE instance StackValue Meta where push lua = push lua . toJSON peek lua = fmap maybeFromJson . peek lua - valuetype _ = Lua.TTABLE + valuetype _ = TTABLE instance StackValue Block where push lua = \case @@ -76,24 +80,99 @@ instance StackValue Block where -- fall back to conversion via aeson's Value x -> push lua (toJSON x) peek lua i = peekBlock lua i - valuetype _ = Lua.TTABLE + valuetype _ = TTABLE instance StackValue Inline where push lua = \case - Emph inlns -> pushTagged lua "Emph" inlns - LineBreak -> pushTagged' lua "LineBreak" - Note blcks -> pushTagged lua "Note" blcks - SmallCaps inlns -> pushTagged lua "SmallCaps" inlns - SoftBreak -> pushTagged' lua "SoftBreak" - Space -> pushTagged' lua "Space" - Str s -> pushTagged lua "Str" s - Strikeout inlns -> pushTagged lua "Strikeout" inlns - Strong inlns -> pushTagged lua "Strong" inlns - Subscript inlns -> pushTagged lua "Subscript" inlns - Superscript inlns -> pushTagged lua "Superscript" inlns - x -> push lua (toJSON x) + Cite citations lst -> pushTagged lua "Cite" (citations, lst) + Code attr lst -> pushTagged lua "Code" (attr, lst) + Emph inlns -> pushTagged lua "Emph" inlns + Image attr lst tgt -> pushTagged lua "Image" (attr, lst, tgt) + LineBreak -> pushTagged' lua "LineBreak" + Link attr lst tgt -> pushTagged lua "Link" (attr, lst, tgt) + Note blcks -> pushTagged lua "Note" blcks + Math mty str -> pushTagged lua "Math" (mty, str) + Quoted qt inlns -> pushTagged lua "Quoted" (qt, inlns) + RawInline f cs -> pushTagged lua "RawInline" (f, cs) + SmallCaps inlns -> pushTagged lua "SmallCaps" inlns + SoftBreak -> pushTagged' lua "SoftBreak" + Space -> pushTagged' lua "Space" + Span attr inlns -> pushTagged lua "Span" (attr, inlns) + Str str -> pushTagged lua "Str" str + Strikeout inlns -> pushTagged lua "Strikeout" inlns + Strong inlns -> pushTagged lua "Strong" inlns + Subscript inlns -> pushTagged lua "Subscript" inlns + Superscript inlns -> pushTagged lua "Superscript" inlns peek = peekInline - valuetype _ = Lua.TTABLE + valuetype _ = TTABLE + +instance StackValue Citation where + push lua c = do + newtable lua + setField lua (-1) "citationId" (citationId c) + setField lua (-1) "citationPrefix" (citationPrefix c) + setField lua (-1) "citationSuffix" (citationSuffix c) + setField lua (-1) "citationMode" (citationMode c) + setField lua (-1) "citationNoteNum" (citationNoteNum c) + setField lua (-1) "citationHash" (citationHash c) + peek lua idx = do + id' <- getField lua idx "citationId" + prefix <- getField lua idx "citationPrefix" + suffix <- getField lua idx "citationSuffix" + mode <- getField lua idx "citationMode" + num <- getField lua idx "citationNoteNum" + hash <- getField lua idx "citationHash" + return $ Citation + <$> id' + <*> prefix + <*> suffix + <*> mode + <*> num + <*> hash + valuetype _ = TTABLE + +instance StackValue CitationMode where + push lua = \case + AuthorInText -> pushTagged' lua "AuthorInText" + NormalCitation -> pushTagged' lua "NormalCitation" + SuppressAuthor -> pushTagged' lua "SuppressAuthor" + peek lua idx = do + tag <- getField lua idx "t" + case tag of + Just "AuthorInText" -> return $ Just AuthorInText + Just "NormalCitation" -> return $ Just NormalCitation + Just "SuppressAuthor" -> return $ Just SuppressAuthor + _ -> return Nothing + valuetype _ = TSTRING + +instance StackValue Format where + push lua (Format f) = push lua f + peek lua idx = fmap Format <$> peek lua idx + valuetype _ = TSTRING + +instance StackValue MathType where + push lua = \case + InlineMath -> pushTagged' lua "InlineMath" + DisplayMath -> pushTagged' lua "DisplayMath" + peek lua idx = do + res <- getField lua idx "t" + case res of + Just "InlineMath" -> return $ Just InlineMath + Just "DisplayMath" -> return $ Just DisplayMath + _ -> return Nothing + valuetype _ = TTABLE + +instance StackValue QuoteType where + push lua = \case + SingleQuote -> pushTagged' lua "SingleQuote" + DoubleQuote -> pushTagged' lua "DoubleQuote" + peek lua idx = do + res <- getField lua idx "t" + case res of + Just "SingleQuote" -> return $ Just SingleQuote + Just "DoubleQuote" -> return $ Just DoubleQuote + _ -> return Nothing + valuetype _ = TTABLE #if MIN_VERSION_base(4,8,0) instance {-# OVERLAPS #-} StackValue [Char] where @@ -102,7 +181,33 @@ instance StackValue [Char] where #endif push lua cs = push lua (UTF8.fromString cs) peek lua i = fmap UTF8.toString <$> peek lua i - valuetype _ = Lua.TSTRING + valuetype _ = TSTRING + +instance (StackValue a, StackValue b) => StackValue (a, b) where + push lua (a, b) = do + newtable lua + setIntField lua (-1) 1 a + setIntField lua (-1) 2 b + peek lua idx = do + a <- getIntField lua idx 1 + b <- getIntField lua idx 2 + return $ (,) <$> a <*> b + valuetype _ = TTABLE + +instance (StackValue a, StackValue b, StackValue c) => + StackValue (a, b, c) + where + push lua (a, b, c) = do + newtable lua + setIntField lua (-1) 1 a + setIntField lua (-1) 2 b + setIntField lua (-1) 3 c + peek lua idx = do + a <- getIntField lua idx 1 + b <- getIntField lua idx 2 + c <- getIntField lua idx 3 + return $ (,,) <$> a <*> b <*> c + valuetype _ = TTABLE -- | Push a value to the lua stack, tagged with a given string. This currently -- creates a structure equivalent to what the JSONified value would look like @@ -127,19 +232,28 @@ peekInline lua idx = do case tag of Nothing -> return Nothing Just t -> case t of + "Cite" -> fmap (uncurry Cite) <$> elementContent + "Code" -> fmap (uncurry Code) <$> elementContent "Emph" -> fmap Emph <$> elementContent + "Image" -> fmap (\(attr, lst, tgt) -> Image attr lst tgt) + <$> elementContent + "Link" -> fmap (\(attr, lst, tgt) -> Link attr lst tgt) + <$> elementContent "LineBreak" -> return (Just LineBreak) "Note" -> fmap Note <$> elementContent + "Math" -> fmap (uncurry Math) <$> elementContent + "Quoted" -> fmap (uncurry Quoted) <$> elementContent + "RawInline" -> fmap (uncurry RawInline) <$> elementContent "SmallCaps" -> fmap SmallCaps <$> elementContent "SoftBreak" -> return (Just SoftBreak) "Space" -> return (Just Space) + "Span" -> fmap (uncurry Span) <$> elementContent "Str" -> fmap Str <$> elementContent "Strikeout" -> fmap Strikeout <$> elementContent "Strong" -> fmap Strong <$> elementContent "Subscript" -> fmap Subscript <$> elementContent "Superscript"-> fmap Superscript <$> elementContent - -- fall back to construction via aeson's Value - _ -> maybeFromJson <$> peek lua idx + _ -> return Nothing where -- Get the contents of an AST element. elementContent :: StackValue a => IO (Maybe a) @@ -178,7 +292,7 @@ adjustIndexBy idx n = getField :: (StackValue a, StackValue b) => LuaState -> Int -> a -> IO (Maybe b) getField lua idx key = do push lua key - rawget lua (idx `adjustIndexBy` 1) + gettable lua (idx `adjustIndexBy` 1) peek lua (-1) <* pop lua 1 -- | Set value for key for table at the given index @@ -186,4 +300,17 @@ setField :: (StackValue a, StackValue b) => LuaState -> Int -> a -> b -> IO () setField lua idx key value = do push lua key push lua value - rawset lua (idx `adjustIndexBy` 2) + settable lua (idx `adjustIndexBy` 2) + +-- | Get value behind key from table at given index. +getIntField :: StackValue a => LuaState -> Int -> Int -> IO (Maybe a) +getIntField lua idx key = + rawgeti lua idx key + *> peek lua (-1) + <* pop lua 1 + +-- | Set numeric key/value in table at the given index +setIntField :: StackValue a => LuaState -> Int -> Int -> a -> IO () +setIntField lua idx key value = do + push lua value + rawseti lua (idx `adjustIndexBy` 1) key -- cgit v1.2.3 From 31a36cf186353dd7c18533b42a88424145b12dcc Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 12 Apr 2017 12:22:25 +0200 Subject: Man writer: Fix handling of nested font commands. Previously pandoc emitted incorrect markup for bold + italic, for example, or bold + code. Closes #3568. --- src/Text/Pandoc/Writers/Man.hs | 46 +++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Man.hs b/src/Text/Pandoc/Writers/Man.hs index 6d7a4f84b..1f3e17c16 100644 --- a/src/Text/Pandoc/Writers/Man.hs +++ b/src/Text/Pandoc/Writers/Man.hs @@ -31,7 +31,8 @@ Conversion of 'Pandoc' documents to groff man page format. module Text.Pandoc.Writers.Man ( writeMan) where import Control.Monad.Except (throwError) import Control.Monad.State -import Data.List (intercalate, intersperse, stripPrefix) +import Data.List (intercalate, intersperse, stripPrefix, sort) +import qualified Data.Map as Map import Data.Maybe (fromMaybe) import Text.Pandoc.Builder (deleteMeta) import Text.Pandoc.Class (PandocMonad, report) @@ -47,12 +48,23 @@ import Text.Pandoc.Writers.Shared import Text.Printf (printf) type Notes = [[Block]] -data WriterState = WriterState { stNotes :: Notes - , stHasTables :: Bool } +data WriterState = WriterState { stNotes :: Notes + , stFontFeatures :: Map.Map Char Bool + , stHasTables :: Bool } + +defaultWriterState :: WriterState +defaultWriterState = WriterState { stNotes = [] + , stFontFeatures = Map.fromList [ + ('I',False) + , ('B',False) + , ('C',False) + ] + , stHasTables = False } -- | Convert Pandoc to Man. writeMan :: PandocMonad m => WriterOptions -> Pandoc -> m String -writeMan opts document = evalStateT (pandocToMan opts document) (WriterState [] False) +writeMan opts document = + evalStateT (pandocToMan opts document) defaultWriterState -- | Return groff man representation of document. pandocToMan :: PandocMonad m => WriterOptions -> Pandoc -> StateT WriterState m String @@ -316,11 +328,9 @@ inlineListToMan opts lst = mapM (inlineToMan opts) lst >>= (return . hcat) 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 - return $ text "\\f[I]" <> contents <> text "\\f[]" + withFontFeature 'I' (inlineListToMan opts lst) inlineToMan opts (Strong lst) = do - contents <- inlineListToMan opts lst - return $ text "\\f[B]" <> contents <> text "\\f[]" + withFontFeature 'B' (inlineListToMan opts lst) inlineToMan opts (Strikeout lst) = do contents <- inlineListToMan opts lst return $ text "[STRIKEOUT:" <> contents <> char ']' @@ -340,7 +350,7 @@ inlineToMan opts (Quoted DoubleQuote lst) = do inlineToMan opts (Cite _ lst) = inlineListToMan opts lst inlineToMan _ (Code _ str) = - return $ text $ "\\f[C]" ++ escapeCode str ++ "\\f[]" + withFontFeature 'C' (return (text $ escapeCode str)) inlineToMan _ (Str str@('.':_)) = return $ afterBreak "\\&" <> text (escapeString str) inlineToMan _ (Str str) = return $ text $ escapeString str @@ -379,3 +389,21 @@ inlineToMan _ (Note contents) = do notes <- gets stNotes let ref = show $ (length notes) return $ char '[' <> text ref <> char ']' + +fontChange :: PandocMonad m => StateT WriterState m Doc +fontChange = do + features <- gets stFontFeatures + let filling = sort [c | (c,True) <- Map.toList features] + return $ text $ "\\f[" ++ filling ++ "]" + +withFontFeature :: PandocMonad m + => Char + -> StateT WriterState m Doc + -> StateT WriterState m Doc +withFontFeature c action = do + modify $ \st -> st{ stFontFeatures = Map.adjust not c $ stFontFeatures st } + begin <- fontChange + d <- action + modify $ \st -> st{ stFontFeatures = Map.adjust not c $ stFontFeatures st } + end <- fontChange + return $ begin <> d <> end -- cgit v1.2.3 From 624ccbd45e24b1862e32252b3a03af7ee652bd16 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 14 Apr 2017 03:47:01 +0300 Subject: s/safed/saved/ --- src/Text/Pandoc/Readers/Org/Blocks.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index 75019f74f..9ef714da7 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -259,7 +259,7 @@ blockList = do headlineBlocks <- fmap mconcat . sequence . map headlineToBlocks $ runF headlines st return . B.toList $ (runF initialBlocks st) <> headlineBlocks --- | Get the meta information safed in the state. +-- | Get the meta information saved in the state. meta :: Monad m => OrgParser m Meta meta = do meta' <- metaExport -- cgit v1.2.3 From 2761a38e530735499f7ac0075b768feb101190a5 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Wed, 12 Apr 2017 20:48:44 +0200 Subject: Lua filter: use destructured functions for inline filters Instead of taking the whole inline element, forcing users to destructure it themselves, the components of the elements are passed to the filtering functions. --- src/Text/Pandoc/Lua.hs | 91 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 33 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index d7c54b6af..ccd820682 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -15,6 +15,9 @@ 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 -} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua @@ -130,29 +133,35 @@ execInlineLuaFilter :: LuaState -> HashMap Text (LuaFilterFunction Inline) -> Inline -> IO Inline execInlineLuaFilter lua fnMap x = do - let filterOrId constr = case HashMap.lookup constr fnMap of - Nothing -> return x - Just fn -> runLuaFilterFunction lua fn x + let runFn :: PushViaFilterFunction Inline a => LuaFilterFunction Inline -> a + runFn fn = runLuaFilterFunction lua fn + let tryFilter :: Text -> (LuaFilterFunction Inline -> IO Inline) -> IO Inline + tryFilter fnName callFilterFn = + case HashMap.lookup fnName fnMap of + Nothing -> return x + Just fn -> callFilterFn fn case x of - Cite _ _ -> filterOrId "Cite" - Code _ _ -> filterOrId "Code" - Emph _ -> filterOrId "Emph" - Image _ _ _ -> filterOrId "Image" - LineBreak -> filterOrId "LineBreak" - Link _ _ _ -> filterOrId "Link" - Math _ _ -> filterOrId "Math" - Note _ -> filterOrId "Note" - Quoted _ _ -> filterOrId "Quoted" - RawInline _ _ -> filterOrId "RawInline" - SmallCaps _ -> filterOrId "SmallCaps" - SoftBreak -> filterOrId "SoftBreak" - Space -> filterOrId "Space" - Span _ _ -> filterOrId "Span" - Str _ -> filterOrId "Str" - Strikeout _ -> filterOrId "Strikeout" - Strong _ -> filterOrId "Strong" - Subscript _ -> filterOrId "Subscript" - Superscript _ -> filterOrId "Superscript" + LineBreak -> tryFilter "LineBreak" runFn + SoftBreak -> tryFilter "SoftBreak" runFn + Space -> tryFilter "Space" runFn + Cite cs lst -> tryFilter "Cite" $ \fn -> runFn fn lst cs + Code attr str -> tryFilter "Code" $ \fn -> runFn fn str attr + Emph lst -> tryFilter "Emph" $ \fn -> runFn fn lst + Math mt lst -> tryFilter "Math" $ \fn -> runFn fn lst mt + Note blks -> tryFilter "Note" $ \fn -> runFn fn blks + Quoted qt lst -> tryFilter "Quoted" $ \fn -> runFn fn qt lst + RawInline f str -> tryFilter "RawInline" $ \fn -> runFn fn f str + SmallCaps lst -> tryFilter "SmallCaps" $ \fn -> runFn fn lst + Span attr lst -> tryFilter "Span" $ \fn -> runFn fn lst attr + Str str -> tryFilter "Str" $ \fn -> runFn fn str + Strikeout lst -> tryFilter "Strikeout" $ \fn -> runFn fn lst + Strong lst -> tryFilter "Strong" $ \fn -> runFn fn lst + Subscript lst -> tryFilter "Subscript" $ \fn -> runFn fn lst + Superscript lst -> tryFilter "Superscript" $ \fn -> runFn fn lst + Link attr txt (src, tit) -> tryFilter "Link" $ + \fn -> runFn fn txt src tit attr + Image attr alt (src, tit) -> tryFilter "Image" $ + \fn -> runFn fn alt src tit attr instance StackValue LuaFilter where valuetype _ = Lua.TTABLE @@ -164,17 +173,33 @@ instance StackValue LuaFilter where docFnMap <- Lua.peek lua i return $ LuaFilter lua <$> inlineFnMap <*> blockFnMap <*> docFnMap -runLuaFilterFunction :: (StackValue a) - => LuaState -> LuaFilterFunction a -> a -> IO a -runLuaFilterFunction lua lf inline = do - pushFilterFunction lua lf - Lua.push lua inline - Lua.call lua 1 1 - mbres <- Lua.peek lua (-1) - case mbres of - Nothing -> error $ "Error while trying to get a filter's return " - ++ "value from lua stack." - Just res -> res <$ Lua.pop lua 1 +-- | Helper class for pushing a single value to the stack via a lua function. +-- See @pushViaCall@. +class PushViaFilterFunction a b where + pushViaFilterFunction' :: LuaState -> LuaFilterFunction a -> IO () -> Int -> b + +instance (StackValue a) => PushViaFilterFunction a (IO a) where + pushViaFilterFunction' lua lf pushArgs num = do + pushFilterFunction lua lf + pushArgs + Lua.call lua num 1 + mbres <- Lua.peek lua (-1) + case mbres of + Nothing -> error $ "Error while trying to get a filter's return " + ++ "value from lua stack." + Just res -> res <$ Lua.pop lua 1 + +instance (PushViaFilterFunction a c, StackValue b) => + PushViaFilterFunction a (b -> c) where + pushViaFilterFunction' lua lf pushArgs num x = + pushViaFilterFunction' lua lf (pushArgs *> push lua x) (num + 1) + +-- | Push an value to the stack via a lua filter function. The function is +-- called with all arguments that are passed to this function and is expected to +-- return a single value. +runLuaFilterFunction :: (StackValue a, PushViaFilterFunction a b) + => LuaState -> LuaFilterFunction a -> b +runLuaFilterFunction lua lf = pushViaFilterFunction' lua lf (return ()) 0 -- | Push the filter function to the top of the stack. pushFilterFunction :: Lua.LuaState -> LuaFilterFunction a -> IO () -- cgit v1.2.3 From 425df8fff435c105590986e1b85efbcca8986931 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Thu, 13 Apr 2017 22:57:50 +0200 Subject: Use lua constructors to push meta values --- src/Text/Pandoc/Lua.hs | 4 +- src/Text/Pandoc/Lua/StackInstances.hs | 178 ++++++++++++++++++++++++++++------ 2 files changed, 150 insertions(+), 32 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index ccd820682..95bc1ef35 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -29,7 +29,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Pandoc lua utils. -} -module Text.Pandoc.Lua ( runLuaFilter ) where +module Text.Pandoc.Lua ( runLuaFilter, pushPandocModule ) where import Control.Monad ( (>=>), when ) import Control.Monad.Trans ( MonadIO(..) ) @@ -39,7 +39,7 @@ import Data.Text.Encoding ( decodeUtf8 ) import Scripting.Lua ( LuaState, StackValue(..) ) import Scripting.Lua.Aeson ( newstate ) import Text.Pandoc.Definition ( Block(..), Inline(..), Pandoc(..) ) -import Text.Pandoc.Lua.PandocModule +import Text.Pandoc.Lua.PandocModule ( pushPandocModule ) import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Walk diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index 690557788..5387f94e5 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -35,16 +35,19 @@ StackValue instances for pandoc types. -} module Text.Pandoc.Lua.StackInstances () where +import Control.Applicative ( (<|>) ) import Data.Aeson ( FromJSON(..), ToJSON(..), Result(..), Value, fromJSON ) import Scripting.Lua ( LTYPE(..), LuaState, StackValue(..) - , gettable, newtable, pop, rawgeti, rawset, rawseti, settable + , call, getglobal2, gettable, ltype, newtable, next, objlen + , pop, pushnil, rawgeti, rawset, rawseti, settable ) import Scripting.Lua.Aeson () import Text.Pandoc.Definition - ( Block(..), Inline(..), Meta(..), Pandoc(..) + ( Block(..), Inline(..), Meta(..), MetaValue(..), Pandoc(..) , Citation(..), CitationMode(..), Format(..), MathType(..), QuoteType(..) ) +import qualified Data.Map as M import qualified Text.Pandoc.UTF8 as UTF8 maybeFromJson :: (FromJSON a) => Maybe Value -> Maybe a @@ -55,8 +58,8 @@ maybeFromJson mv = fromJSON <$> mv >>= \case instance StackValue Pandoc where push lua (Pandoc meta blocks) = do newtable lua - setField lua (-1) "blocks" blocks - setField lua (-1) "meta" meta + addKeyValue lua "blocks" blocks + addKeyValue lua "meta" meta peek lua idx = do blocks <- getField lua idx "blocks" meta <- getField lua idx "meta" @@ -64,10 +67,58 @@ instance StackValue Pandoc where valuetype _ = TTABLE instance StackValue Meta where - push lua = push lua . toJSON - peek lua = fmap maybeFromJson . peek lua + push lua (Meta mmap) = push lua mmap + peek lua idx = fmap Meta <$> peek lua idx valuetype _ = TTABLE +instance StackValue MetaValue where + push lua = \case + MetaBlocks blcks -> pushViaConstructor lua "MetaBlocks" blcks + MetaBool b -> pushViaConstructor lua "MetaBool" b + MetaInlines inlns -> pushViaConstructor lua "MetaInlines" inlns + MetaList metalist -> pushViaConstructor lua "MetaList" metalist + MetaMap metamap -> pushViaConstructor lua "MetaMap" metamap + MetaString cs -> pushViaConstructor lua "MetaString" cs + peek lua idx = do + luatype <- ltype lua idx + case luatype of + TBOOLEAN -> fmap MetaBool <$> peek lua idx + TSTRING -> fmap MetaString <$> peek lua idx + TTABLE -> do + tag <- push lua "t" + *> gettable lua (idx `adjustIndexBy` 1) + *> peek lua (-1) + <* pop lua 1 + case tag of + Just "MetaBlocks" -> fmap MetaBlocks <$> peekContent lua idx + Just "MetaBool" -> fmap MetaBool <$> peekContent lua idx + Just "MetaMap" -> fmap MetaMap <$> peekContent lua idx + Just "MetaInlines" -> fmap MetaInlines <$> peekContent lua idx + Just "MetaList" -> fmap MetaList <$> peekContent lua idx + Just "MetaString" -> fmap MetaString <$> peekContent lua idx + Nothing -> do + len <- objlen lua idx + if len <= 0 + then fmap MetaMap <$> peek lua idx + else (fmap MetaInlines <$> peek lua idx) + <|> (fmap MetaBlocks <$> peek lua idx) + <|> (fmap MetaList <$> peek lua idx) + _ -> return Nothing + _ -> return Nothing + valuetype = \case + MetaBlocks _ -> TTABLE + MetaBool _ -> TBOOLEAN + MetaInlines _ -> TTABLE + MetaList _ -> TTABLE + MetaMap _ -> TTABLE + MetaString _ -> TSTRING + +peekContent :: StackValue a => LuaState -> Int -> IO (Maybe a) +peekContent lua idx = do + push lua "c" + gettable lua (idx `adjustIndexBy` 1) + peek lua (-1) <* pop lua 1 + instance StackValue Block where push lua = \case BlockQuote blcks -> pushTagged lua "BlockQuote" blcks @@ -77,6 +128,7 @@ instance StackValue Block where Null -> pushTagged' lua "Null" Para blcks -> pushTagged lua "Para" blcks Plain blcks -> pushTagged lua "Plain" blcks + RawBlock f cs -> pushTagged lua "RawBlock" (f, cs) -- fall back to conversion via aeson's Value x -> push lua (toJSON x) peek lua i = peekBlock lua i @@ -109,12 +161,12 @@ instance StackValue Inline where instance StackValue Citation where push lua c = do newtable lua - setField lua (-1) "citationId" (citationId c) - setField lua (-1) "citationPrefix" (citationPrefix c) - setField lua (-1) "citationSuffix" (citationSuffix c) - setField lua (-1) "citationMode" (citationMode c) - setField lua (-1) "citationNoteNum" (citationNoteNum c) - setField lua (-1) "citationHash" (citationHash c) + addKeyValue lua "citationId" (citationId c) + addKeyValue lua "citationPrefix" (citationPrefix c) + addKeyValue lua "citationSuffix" (citationSuffix c) + addKeyValue lua "citationMode" (citationMode c) + addKeyValue lua "citationNoteNum" (citationNoteNum c) + addKeyValue lua "citationHash" (citationHash c) peek lua idx = do id' <- getField lua idx "citationId" prefix <- getField lua idx "citationPrefix" @@ -186,11 +238,11 @@ instance StackValue [Char] where instance (StackValue a, StackValue b) => StackValue (a, b) where push lua (a, b) = do newtable lua - setIntField lua (-1) 1 a - setIntField lua (-1) 2 b + addIndexedValue lua 1 a + addIndexedValue lua 2 b peek lua idx = do - a <- getIntField lua idx 1 - b <- getIntField lua idx 2 + a <- getIndexedValue lua idx 1 + b <- getIndexedValue lua idx 2 return $ (,) <$> a <*> b valuetype _ = TTABLE @@ -199,24 +251,82 @@ instance (StackValue a, StackValue b, StackValue c) => where push lua (a, b, c) = do newtable lua - setIntField lua (-1) 1 a - setIntField lua (-1) 2 b - setIntField lua (-1) 3 c + addIndexedValue lua 1 a + addIndexedValue lua 2 b + addIndexedValue lua 3 c peek lua idx = do - a <- getIntField lua idx 1 - b <- getIntField lua idx 2 - c <- getIntField lua idx 3 + a <- getIndexedValue lua idx 1 + b <- getIndexedValue lua idx 2 + c <- getIndexedValue lua idx 3 return $ (,,) <$> a <*> b <*> c valuetype _ = TTABLE +instance (Ord a, StackValue a, StackValue b) => + StackValue (M.Map a b) where + push lua m = do + newtable lua + mapM_ (uncurry $ addKeyValue lua) $ M.toList m + peek lua idx = fmap M.fromList <$> keyValuePairs lua idx + valuetype _ = TTABLE + +-- | Try reading the value under the given index as a list of key-value pairs. +keyValuePairs :: (StackValue a, StackValue b) + => LuaState -> Int -> IO (Maybe [(a, b)]) +keyValuePairs lua idx = do + pushnil lua + sequence <$> remainingPairs + where + remainingPairs = do + res <- nextPair + case res of + Nothing -> return [] + Just a -> (a:) <$> remainingPairs + nextPair :: (StackValue a, StackValue b) => IO (Maybe (Maybe (a,b))) + nextPair = do + hasNext <- next lua (idx `adjustIndexBy` 1) + if hasNext + then do + val <- peek lua (-1) + key <- peek lua (-2) + pop lua 1 -- removes the value, keeps the key + return $ Just <$> ((,) <$> key <*> val) + else do + return Nothing + + +-- | Helper class for pushing a single value to the stack via a lua function. +-- See @pushViaCall@. +class PushViaCall a where + pushViaCall' :: LuaState -> String -> IO () -> Int -> a + +instance PushViaCall (IO ()) where + pushViaCall' lua fn pushArgs num = do + getglobal2 lua fn + pushArgs + call lua num 1 + +instance (StackValue a, PushViaCall b) => PushViaCall (a -> b) where + pushViaCall' lua fn pushArgs num x = + pushViaCall' lua fn (pushArgs *> push lua x) (num + 1) + +-- | Push an value to the stack via a lua function. The lua function is called +-- with all arguments that are passed to this function and is expected to return +-- a single value. +pushViaCall :: PushViaCall a => LuaState -> String -> a +pushViaCall lua fn = pushViaCall' lua fn (return ()) 0 + +-- | Call a pandoc element constructor within lua, passing all given arguments. +pushViaConstructor :: PushViaCall a => LuaState -> String -> a +pushViaConstructor lua pandocFn = pushViaCall lua ("pandoc." ++ pandocFn) + -- | Push a value to the lua stack, tagged with a given string. This currently -- creates a structure equivalent to what the JSONified value would look like -- when pushed to lua. pushTagged :: StackValue a => LuaState -> String -> a -> IO () pushTagged lua tag value = do newtable lua - setField lua (-1) "t" tag - setField lua (-1) "c" value + addKeyValue lua "t" tag + addKeyValue lua "c" value pushTagged' :: LuaState -> String -> IO () pushTagged' lua tag = do @@ -296,21 +406,29 @@ getField lua idx key = do peek lua (-1) <* pop lua 1 -- | Set value for key for table at the given index -setField :: (StackValue a, StackValue b) => LuaState -> Int -> a -> b -> IO () -setField lua idx key value = do +setKeyValue :: (StackValue a, StackValue b) => LuaState -> Int -> a -> b -> IO () +setKeyValue lua idx key value = do push lua key push lua value settable lua (idx `adjustIndexBy` 2) +-- | Add a key-value pair to the table at the top of the stack +addKeyValue :: (StackValue a, StackValue b) => LuaState -> a -> b -> IO () +addKeyValue lua = setKeyValue lua (-1) + -- | Get value behind key from table at given index. -getIntField :: StackValue a => LuaState -> Int -> Int -> IO (Maybe a) -getIntField lua idx key = +getIndexedValue :: StackValue a => LuaState -> Int -> Int -> IO (Maybe a) +getIndexedValue lua idx key = rawgeti lua idx key *> peek lua (-1) <* pop lua 1 -- | Set numeric key/value in table at the given index -setIntField :: StackValue a => LuaState -> Int -> Int -> a -> IO () -setIntField lua idx key value = do +setIndexedValue :: StackValue a => LuaState -> Int -> Int -> a -> IO () +setIndexedValue lua idx key value = do push lua value rawseti lua (idx `adjustIndexBy` 1) key + +-- | Set numeric key/value in table at the top of the stack. +addIndexedValue :: StackValue a => LuaState -> Int -> a -> IO () +addIndexedValue lua = setIndexedValue lua (-1) -- cgit v1.2.3 From 0085251ec7ca2f2beb836eff0c954c80aa3bfcdc Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Fri, 14 Apr 2017 00:35:38 +0200 Subject: Push inlines via lua constructors and constants All element creation tasks are handled in the lua module. --- src/Text/Pandoc/Lua/StackInstances.hs | 70 +++++++++++++++-------------------- 1 file changed, 29 insertions(+), 41 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index 5387f94e5..6f89bbee1 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -136,37 +136,31 @@ instance StackValue Block where instance StackValue Inline where push lua = \case - Cite citations lst -> pushTagged lua "Cite" (citations, lst) - Code attr lst -> pushTagged lua "Code" (attr, lst) - Emph inlns -> pushTagged lua "Emph" inlns - Image attr lst tgt -> pushTagged lua "Image" (attr, lst, tgt) - LineBreak -> pushTagged' lua "LineBreak" - Link attr lst tgt -> pushTagged lua "Link" (attr, lst, tgt) - Note blcks -> pushTagged lua "Note" blcks - Math mty str -> pushTagged lua "Math" (mty, str) - Quoted qt inlns -> pushTagged lua "Quoted" (qt, inlns) - RawInline f cs -> pushTagged lua "RawInline" (f, cs) - SmallCaps inlns -> pushTagged lua "SmallCaps" inlns - SoftBreak -> pushTagged' lua "SoftBreak" - Space -> pushTagged' lua "Space" - Span attr inlns -> pushTagged lua "Span" (attr, inlns) - Str str -> pushTagged lua "Str" str - Strikeout inlns -> pushTagged lua "Strikeout" inlns - Strong inlns -> pushTagged lua "Strong" inlns - Subscript inlns -> pushTagged lua "Subscript" inlns - Superscript inlns -> pushTagged lua "Superscript" inlns + Cite citations lst -> pushViaConstructor lua "Cite" lst citations + Code attr lst -> pushViaConstructor lua "Code" lst attr + Emph inlns -> pushViaConstructor lua "Emph" inlns + Image attr alt (src,tit) -> pushViaConstructor lua "Image" alt src tit attr + LineBreak -> pushViaConstructor lua "LineBreak" + Link attr lst (src,tit) -> pushViaConstructor lua "Link" lst src tit attr + Note blcks -> pushViaConstructor lua "Note" blcks + Math mty str -> pushViaConstructor lua "Math" mty str + Quoted qt inlns -> pushViaConstructor lua "Quoted" qt inlns + RawInline f cs -> pushViaConstructor lua "RawInline" f cs + SmallCaps inlns -> pushViaConstructor lua "SmallCaps" inlns + SoftBreak -> pushViaConstructor lua "SoftBreak" + Space -> pushViaConstructor lua "Space" + Span attr inlns -> pushViaConstructor lua "Span" inlns attr + Str str -> pushViaConstructor lua "Str" str + Strikeout inlns -> pushViaConstructor lua "Strikeout" inlns + Strong inlns -> pushViaConstructor lua "Strong" inlns + Subscript inlns -> pushViaConstructor lua "Subscript" inlns + Superscript inlns -> pushViaConstructor lua "Superscript" inlns peek = peekInline valuetype _ = TTABLE instance StackValue Citation where - push lua c = do - newtable lua - addKeyValue lua "citationId" (citationId c) - addKeyValue lua "citationPrefix" (citationPrefix c) - addKeyValue lua "citationSuffix" (citationSuffix c) - addKeyValue lua "citationMode" (citationMode c) - addKeyValue lua "citationNoteNum" (citationNoteNum c) - addKeyValue lua "citationHash" (citationHash c) + push lua (Citation cid prefix suffix mode noteNum hash) = + pushViaConstructor lua "Citation" cid mode prefix suffix noteNum hash peek lua idx = do id' <- getField lua idx "citationId" prefix <- getField lua idx "citationPrefix" @@ -174,20 +168,14 @@ instance StackValue Citation where mode <- getField lua idx "citationMode" num <- getField lua idx "citationNoteNum" hash <- getField lua idx "citationHash" - return $ Citation - <$> id' - <*> prefix - <*> suffix - <*> mode - <*> num - <*> hash + return $ Citation <$> id' <*> prefix <*> suffix <*> mode <*> num <*> hash valuetype _ = TTABLE instance StackValue CitationMode where push lua = \case - AuthorInText -> pushTagged' lua "AuthorInText" - NormalCitation -> pushTagged' lua "NormalCitation" - SuppressAuthor -> pushTagged' lua "SuppressAuthor" + AuthorInText -> getglobal2 lua "pandoc.AuthorInText" + NormalCitation -> getglobal2 lua "pandoc.NormalCitation" + SuppressAuthor -> getglobal2 lua "pandoc.SuppressAuthor" peek lua idx = do tag <- getField lua idx "t" case tag of @@ -204,8 +192,8 @@ instance StackValue Format where instance StackValue MathType where push lua = \case - InlineMath -> pushTagged' lua "InlineMath" - DisplayMath -> pushTagged' lua "DisplayMath" + InlineMath -> getglobal2 lua "pandoc.InlineMath" + DisplayMath -> getglobal2 lua "pandoc.DisplayMath" peek lua idx = do res <- getField lua idx "t" case res of @@ -216,8 +204,8 @@ instance StackValue MathType where instance StackValue QuoteType where push lua = \case - SingleQuote -> pushTagged' lua "SingleQuote" - DoubleQuote -> pushTagged' lua "DoubleQuote" + SingleQuote -> getglobal2 lua "pandoc.SingleQuote" + DoubleQuote -> getglobal2 lua "pandoc.DoubleQuote" peek lua idx = do res <- getField lua idx "t" case res of -- cgit v1.2.3 From 540a3e80ad33cb43d23532515757dff7ee68a17f Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Fri, 14 Apr 2017 10:33:38 +0200 Subject: Push blocks via lua constructors and constants All element creation tasks are handled by lua functions defined in the pandoc module. --- src/Text/Pandoc/Lua/StackInstances.hs | 139 +++++++++++++++++++++++++--------- 1 file changed, 102 insertions(+), 37 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index 6f89bbee1..bafe24201 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -36,25 +36,17 @@ StackValue instances for pandoc types. module Text.Pandoc.Lua.StackInstances () where import Control.Applicative ( (<|>) ) -import Data.Aeson ( FromJSON(..), ToJSON(..), Result(..), Value, fromJSON ) import Scripting.Lua ( LTYPE(..), LuaState, StackValue(..) , call, getglobal2, gettable, ltype, newtable, next, objlen - , pop, pushnil, rawgeti, rawset, rawseti, settable + , pop, pushnil, rawgeti, rawseti, settable ) import Scripting.Lua.Aeson () import Text.Pandoc.Definition - ( Block(..), Inline(..), Meta(..), MetaValue(..), Pandoc(..) - , Citation(..), CitationMode(..), Format(..), MathType(..), QuoteType(..) ) import qualified Data.Map as M import qualified Text.Pandoc.UTF8 as UTF8 -maybeFromJson :: (FromJSON a) => Maybe Value -> Maybe a -maybeFromJson mv = fromJSON <$> mv >>= \case - Success x -> Just x - _ -> Nothing - instance StackValue Pandoc where push lua (Pandoc meta blocks) = do newtable lua @@ -121,16 +113,22 @@ peekContent lua idx = do instance StackValue Block where push lua = \case - BlockQuote blcks -> pushTagged lua "BlockQuote" blcks - BulletList items -> pushTagged lua "BulletList" items - HorizontalRule -> pushTagged' lua "HorizontalRule" - LineBlock blcks -> pushTagged lua "LineBlock" blcks - Null -> pushTagged' lua "Null" - Para blcks -> pushTagged lua "Para" blcks - Plain blcks -> pushTagged lua "Plain" blcks - RawBlock f cs -> pushTagged lua "RawBlock" (f, cs) + BlockQuote blcks -> pushViaConstructor lua "BlockQuote" blcks + BulletList items -> pushViaConstructor lua "BulletList" items + CodeBlock attr code -> pushViaConstructor lua "CodeBlock" code attr + DefinitionList items -> pushViaConstructor lua "DefinitionList" items + Div attr blcks -> pushViaConstructor lua "Div" blcks attr + Header lvl attr inlns -> pushViaConstructor lua "Header" lvl attr inlns + HorizontalRule -> pushViaConstructor lua "HorizontalRule" + LineBlock blcks -> pushViaConstructor lua "LineBlock" blcks + OrderedList lstAttr list -> pushViaConstructor lua "OrderedList" list lstAttr + Null -> pushViaConstructor lua "Null" + Para blcks -> pushViaConstructor lua "Para" blcks + Plain blcks -> pushViaConstructor lua "Plain" blcks + RawBlock f cs -> pushViaConstructor lua "RawBlock" f cs + Table capt aligns widths headers rows -> + pushViaConstructor lua "Table" capt aligns widths headers rows -- fall back to conversion via aeson's Value - x -> push lua (toJSON x) peek lua i = peekBlock lua i valuetype _ = TTABLE @@ -158,6 +156,22 @@ instance StackValue Inline where peek = peekInline valuetype _ = TTABLE +instance StackValue Alignment where + push lua = \case + AlignLeft -> getglobal2 lua "pandoc.AlignLeft" + AlignRight -> getglobal2 lua "pandoc.AlignRight" + AlignCenter -> getglobal2 lua "pandoc.AlignCenter" + AlignDefault -> getglobal2 lua "pandoc.AlignDefault" + peek lua idx = do + tag <- getField lua idx "t" + case tag of + Just "AlignLeft" -> return $ Just AlignLeft + Just "AlignRight" -> return $ Just AlignRight + Just "AlignCenter" -> return $ Just AlignCenter + Just "AlignDefault" -> return $ Just AlignDefault + _ -> return Nothing + valuetype _ = TSTRING + instance StackValue Citation where push lua (Citation cid prefix suffix mode noteNum hash) = pushViaConstructor lua "Citation" cid mode prefix suffix noteNum hash @@ -183,13 +197,51 @@ instance StackValue CitationMode where Just "NormalCitation" -> return $ Just NormalCitation Just "SuppressAuthor" -> return $ Just SuppressAuthor _ -> return Nothing - valuetype _ = TSTRING + valuetype _ = TTABLE instance StackValue Format where push lua (Format f) = push lua f peek lua idx = fmap Format <$> peek lua idx valuetype _ = TSTRING +instance StackValue ListNumberDelim where + push lua = \case + DefaultDelim -> getglobal2 lua "pandoc.DefaultDelim" + Period -> getglobal2 lua "pandoc.Period" + OneParen -> getglobal2 lua "pandoc.OneParen" + TwoParens -> getglobal2 lua "pandoc.TwoParens" + peek lua idx = do + tag <- getField lua idx "t" + case tag of + Just "DefaultDelim" -> return $ Just DefaultDelim + Just "Period" -> return $ Just Period + Just "OneParen" -> return $ Just OneParen + Just "TwoParens" -> return $ Just TwoParens + _ -> return Nothing + valuetype _ = TTABLE + +instance StackValue ListNumberStyle where + push lua = \case + DefaultStyle -> getglobal2 lua "pandoc.DefaultStyle" + LowerRoman -> getglobal2 lua "pandoc.LowerRoman" + UpperRoman -> getglobal2 lua "pandoc.UpperRoman" + LowerAlpha -> getglobal2 lua "pandoc.LowerAlpha" + UpperAlpha -> getglobal2 lua "pandoc.UpperAlpha" + Decimal -> getglobal2 lua "pandoc.Decimal" + Example -> getglobal2 lua "pandoc.Example" + peek lua idx = do + tag <- getField lua idx "t" + case tag of + Just "DefaultStyle" -> return $ Just DefaultStyle + Just "LowerRoman" -> return $ Just LowerRoman + Just "UpperRoman" -> return $ Just UpperRoman + Just "LowerAlpha" -> return $ Just LowerAlpha + Just "UpperAlpha" -> return $ Just UpperAlpha + Just "Decimal" -> return $ Just Decimal + Just "Example" -> return $ Just Example + _ -> return Nothing + valuetype _ = TTABLE + instance StackValue MathType where push lua = \case InlineMath -> getglobal2 lua "pandoc.InlineMath" @@ -249,6 +301,26 @@ instance (StackValue a, StackValue b, StackValue c) => return $ (,,) <$> a <*> b <*> c valuetype _ = TTABLE +instance (StackValue a, StackValue b, StackValue c, + StackValue d, StackValue e) => + StackValue (a, b, c, d, e) + where + push lua (a, b, c, d, e) = do + newtable lua + addIndexedValue lua 1 a + addIndexedValue lua 2 b + addIndexedValue lua 3 c + addIndexedValue lua 4 d + addIndexedValue lua 5 e + peek lua idx = do + a <- getIndexedValue lua idx 1 + b <- getIndexedValue lua idx 2 + c <- getIndexedValue lua idx 3 + d <- getIndexedValue lua idx 4 + e <- getIndexedValue lua idx 5 + return $ (,,,,) <$> a <*> b <*> c <*> d <*> e + valuetype _ = TTABLE + instance (Ord a, StackValue a, StackValue b) => StackValue (M.Map a b) where push lua m = do @@ -307,22 +379,6 @@ pushViaCall lua fn = pushViaCall' lua fn (return ()) 0 pushViaConstructor :: PushViaCall a => LuaState -> String -> a pushViaConstructor lua pandocFn = pushViaCall lua ("pandoc." ++ pandocFn) --- | Push a value to the lua stack, tagged with a given string. This currently --- creates a structure equivalent to what the JSONified value would look like --- when pushed to lua. -pushTagged :: StackValue a => LuaState -> String -> a -> IO () -pushTagged lua tag value = do - newtable lua - addKeyValue lua "t" tag - addKeyValue lua "c" value - -pushTagged' :: LuaState -> String -> IO () -pushTagged' lua tag = do - newtable lua - push lua "t" - push lua tag - rawset lua (-3) - -- | Return the value at the given index as inline if possible. peekInline :: LuaState -> Int -> IO (Maybe Inline) peekInline lua idx = do @@ -366,13 +422,22 @@ peekBlock lua idx = do Just t -> case t of "BlockQuote" -> fmap BlockQuote <$> elementContent "BulletList" -> fmap BulletList <$> elementContent + "CodeBlock" -> fmap (uncurry CodeBlock) <$> elementContent + "DefinitionList" -> fmap DefinitionList <$> elementContent + "Div" -> fmap (uncurry Div) <$> elementContent + "Header" -> fmap (\(lvl, attr, lst) -> Header lvl attr lst) + <$> elementContent "HorizontalRule" -> return (Just HorizontalRule) "LineBlock" -> fmap LineBlock <$> elementContent + "OrderedList" -> fmap (uncurry OrderedList) <$> elementContent "Null" -> return (Just Null) "Para" -> fmap Para <$> elementContent "Plain" -> fmap Plain <$> elementContent - -- fall back to construction via aeson's Value - _ -> maybeFromJson <$> peek lua idx + "RawBlock" -> fmap (uncurry RawBlock) <$> elementContent + "Table" -> fmap (\(capt, aligns, widths, headers, body) -> + Table capt aligns widths headers body) + <$> elementContent + _ -> return Nothing where -- Get the contents of an AST element. elementContent :: StackValue a => IO (Maybe a) -- cgit v1.2.3 From 0516b5127c3674786f92c61f4131428ed3b8bd4b Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Fri, 14 Apr 2017 10:43:44 +0200 Subject: Drop dependency on hslua-aeson Pushing values to the lua stack via custom functions is faster and more flexible. --- src/Text/Pandoc/Lua.hs | 33 +++++++++++++++------------------ src/Text/Pandoc/Lua/StackInstances.hs | 1 - 2 files changed, 15 insertions(+), 19 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 95bc1ef35..bca6a2589 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -33,23 +33,20 @@ module Text.Pandoc.Lua ( runLuaFilter, pushPandocModule ) where import Control.Monad ( (>=>), when ) import Control.Monad.Trans ( MonadIO(..) ) -import Data.HashMap.Lazy ( HashMap ) -import Data.Text ( Text, pack, unpack ) -import Data.Text.Encoding ( decodeUtf8 ) +import Data.Map ( Map ) import Scripting.Lua ( LuaState, StackValue(..) ) -import Scripting.Lua.Aeson ( newstate ) import Text.Pandoc.Definition ( Block(..), Inline(..), Pandoc(..) ) import Text.Pandoc.Lua.PandocModule ( pushPandocModule ) import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Walk -import qualified Data.HashMap.Lazy as HashMap +import qualified Data.Map as Map import qualified Scripting.Lua as Lua runLuaFilter :: (MonadIO m) => FilePath -> [String] -> Pandoc -> m Pandoc runLuaFilter filterPath args pd = liftIO $ do - lua <- newstate + lua <- Lua.newstate Lua.openlibs lua -- create table in registry to store filter functions Lua.push lua ("PANDOC_FILTER_FUNCTIONS"::String) @@ -61,12 +58,12 @@ runLuaFilter filterPath args pd = liftIO $ do status <- Lua.loadfile lua filterPath if (status /= 0) then do - luaErrMsg <- unpack . decodeUtf8 <$> Lua.tostring lua 1 + Just luaErrMsg <- Lua.peek lua 1 error luaErrMsg else do Lua.call lua 0 1 Just luaFilters <- Lua.peek lua (-1) - Lua.push lua (map pack args) + Lua.push lua args Lua.setglobal lua "PandocParameters" doc <- runAll luaFilters >=> luaFilter lua "filter_doc" $ pd Lua.close lua @@ -89,28 +86,28 @@ walkMWithLuaFilter (LuaFilter lua inlineFnMap blockFnMap docFnMap) = walkM (execBlockLuaFilter lua blockFnMap) >=> walkM (execDocLuaFilter lua docFnMap) -type InlineFunctionMap = HashMap Text (LuaFilterFunction Inline) -type BlockFunctionMap = HashMap Text (LuaFilterFunction Block) -type DocFunctionMap = HashMap Text (LuaFilterFunction Pandoc) +type InlineFunctionMap = Map String (LuaFilterFunction Inline) +type BlockFunctionMap = Map String (LuaFilterFunction Block) +type DocFunctionMap = Map String (LuaFilterFunction Pandoc) data LuaFilter = LuaFilter LuaState InlineFunctionMap BlockFunctionMap DocFunctionMap newtype LuaFilterFunction a = LuaFilterFunction { functionIndex :: Int } execDocLuaFilter :: LuaState - -> HashMap Text (LuaFilterFunction Pandoc) + -> Map String (LuaFilterFunction Pandoc) -> Pandoc -> IO Pandoc execDocLuaFilter lua fnMap x = do let docFnName = "Doc" - case HashMap.lookup docFnName fnMap of + case Map.lookup docFnName fnMap of Nothing -> return x Just fn -> runLuaFilterFunction lua fn x execBlockLuaFilter :: LuaState - -> HashMap Text (LuaFilterFunction Block) + -> Map String (LuaFilterFunction Block) -> Block -> IO Block execBlockLuaFilter lua fnMap x = do - let filterOrId constr = case HashMap.lookup constr fnMap of + let filterOrId constr = case Map.lookup constr fnMap of Nothing -> return x Just fn -> runLuaFilterFunction lua fn x case x of @@ -130,14 +127,14 @@ execBlockLuaFilter lua fnMap x = do Null -> filterOrId "Null" execInlineLuaFilter :: LuaState - -> HashMap Text (LuaFilterFunction Inline) + -> Map String (LuaFilterFunction Inline) -> Inline -> IO Inline execInlineLuaFilter lua fnMap x = do let runFn :: PushViaFilterFunction Inline a => LuaFilterFunction Inline -> a runFn fn = runLuaFilterFunction lua fn - let tryFilter :: Text -> (LuaFilterFunction Inline -> IO Inline) -> IO Inline + let tryFilter :: String -> (LuaFilterFunction Inline -> IO Inline) -> IO Inline tryFilter fnName callFilterFn = - case HashMap.lookup fnName fnMap of + case Map.lookup fnName fnMap of Nothing -> return x Just fn -> callFilterFn fn case x of diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index bafe24201..38f392527 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -41,7 +41,6 @@ import Scripting.Lua , call, getglobal2, gettable, ltype, newtable, next, objlen , pop, pushnil, rawgeti, rawseti, settable ) -import Scripting.Lua.Aeson () import Text.Pandoc.Definition import qualified Data.Map as M -- cgit v1.2.3 From 07f41a5515c0d753c8b3fa074132ba219db8360c Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Fri, 14 Apr 2017 11:21:58 +0200 Subject: Lua filter: use destructured functions for block filters Filtering functions take element components as arguments instead of the whole block elements. This resembles the way elements are handled in custom writers. --- src/Text/Pandoc/Lua.hs | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index bca6a2589..9903d4df6 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -107,24 +107,30 @@ execBlockLuaFilter :: LuaState -> Map String (LuaFilterFunction Block) -> Block -> IO Block execBlockLuaFilter lua fnMap x = do - let filterOrId constr = case Map.lookup constr fnMap of - Nothing -> return x - Just fn -> runLuaFilterFunction lua fn x + let runFn :: PushViaFilterFunction Block a => LuaFilterFunction Block -> a + runFn fn = runLuaFilterFunction lua fn + let tryFilter :: String -> (LuaFilterFunction Block -> IO Block) -> IO Block + tryFilter fnName callFilterFn = + case Map.lookup fnName fnMap of + Nothing -> return x + Just fn -> callFilterFn fn case x of - Plain _ -> filterOrId "Plain" - Para _ -> filterOrId "Para" - LineBlock _ -> filterOrId "LineBlock" - CodeBlock _ _ -> filterOrId "CodeBlock" - RawBlock _ _ -> filterOrId "RawBlock" - BlockQuote _ -> filterOrId "BlockQuote" - OrderedList _ _ -> filterOrId "OrderedList" - BulletList _ -> filterOrId "BulletList" - DefinitionList _ -> filterOrId "DefinitionList" - Header _ _ _ -> filterOrId "Header" - HorizontalRule -> filterOrId "HorizontalRule" - Table _ _ _ _ _ -> filterOrId "Table" - Div _ _ -> filterOrId "Div" - Null -> filterOrId "Null" + HorizontalRule -> tryFilter "HorizontalRule" runFn + Null -> tryFilter "Null" runFn + BlockQuote blcks -> tryFilter "BlockQuote" $ \fn -> runFn fn blcks + BulletList items -> tryFilter "BulletList" $ \fn -> runFn fn items + CodeBlock attr code -> tryFilter "CodeBlock" $ \fn -> runFn fn attr code + DefinitionList lst -> tryFilter "DefinitionList" $ \fn -> runFn fn lst + Div attr content -> tryFilter "Div" $ \fn -> runFn fn content attr + Header lvl attr inlns -> tryFilter "Header" $ \fn -> runFn fn lvl inlns attr + LineBlock inlns -> tryFilter "LineBlock" $ \fn -> runFn fn inlns + Para inlns -> tryFilter "Para" $ \fn -> runFn fn inlns + Plain inlns -> tryFilter "Plain" $ \fn -> runFn fn inlns + RawBlock format str -> tryFilter "RawBlock" $ \fn -> runFn fn format str + OrderedList (num,sty,delim) items -> + tryFilter "OrderedList" $ \fn -> runFn fn items (num,sty,delim) + Table capt aligns widths headers rows -> + tryFilter "Table" $ \fn -> runFn fn capt aligns widths headers rows execInlineLuaFilter :: LuaState -> Map String (LuaFilterFunction Inline) -- cgit v1.2.3 From 1d9742bb5dd976d478db877c48d9ba005ce98098 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Fri, 14 Apr 2017 14:59:17 +0200 Subject: Use lua bools and strings for MetaBool, MetaString Native lua booleans and strings are used to represent MetaBool and MetaString values. This is more natural than the previous table-based representation. The old lua representation can still be read back to haskell, ensuring compatibility with the `pandoc.MetaBool` and `pandoc.MetaString` lua constructors. --- src/Text/Pandoc/Lua/StackInstances.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index 38f392527..62beedabc 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -65,11 +65,11 @@ instance StackValue Meta where instance StackValue MetaValue where push lua = \case MetaBlocks blcks -> pushViaConstructor lua "MetaBlocks" blcks - MetaBool b -> pushViaConstructor lua "MetaBool" b + MetaBool bool -> push lua bool MetaInlines inlns -> pushViaConstructor lua "MetaInlines" inlns MetaList metalist -> pushViaConstructor lua "MetaList" metalist MetaMap metamap -> pushViaConstructor lua "MetaMap" metamap - MetaString cs -> pushViaConstructor lua "MetaString" cs + MetaString str -> push lua str peek lua idx = do luatype <- ltype lua idx case luatype of -- cgit v1.2.3 From feb1c1a9301667cc3b6c36c5fda65c7014cfcdcf Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Fri, 14 Apr 2017 18:26:42 +0200 Subject: Extract lua helper functions into Lua.Util module --- src/Text/Pandoc/Lua.hs | 20 +---- src/Text/Pandoc/Lua/StackInstances.hs | 159 ++++++++++++---------------------- src/Text/Pandoc/Lua/Util.hs | 86 ++++++++++++++++++ src/Text/Pandoc/Writers/Custom.hs | 17 ++-- 4 files changed, 151 insertions(+), 131 deletions(-) create mode 100644 src/Text/Pandoc/Lua/Util.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 9903d4df6..d8b9f62f0 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -18,7 +18,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua Copyright : Copyright © 2017 Albert Krewinkel @@ -49,7 +48,7 @@ runLuaFilter filterPath args pd = liftIO $ do lua <- Lua.newstate Lua.openlibs lua -- create table in registry to store filter functions - Lua.push lua ("PANDOC_FILTER_FUNCTIONS"::String) + Lua.push lua "PANDOC_FILTER_FUNCTIONS" Lua.newtable lua Lua.rawset lua Lua.registryindex -- store module in global "pandoc" @@ -65,7 +64,7 @@ runLuaFilter filterPath args pd = liftIO $ do Just luaFilters <- Lua.peek lua (-1) Lua.push lua args Lua.setglobal lua "PandocParameters" - doc <- runAll luaFilters >=> luaFilter lua "filter_doc" $ pd + doc <- runAll luaFilters pd Lua.close lua return doc @@ -73,13 +72,6 @@ runAll :: [LuaFilter] -> Pandoc -> IO Pandoc runAll [] = return runAll (x:xs) = walkMWithLuaFilter x >=> runAll xs -luaFilter :: Lua.LuaState -> String -> Pandoc -> IO Pandoc -luaFilter lua luaFn x = do - fnExists <- isLuaFunction lua luaFn - if fnExists - then walkM (Lua.callfunc lua luaFn :: Pandoc -> IO Pandoc) x - else return x - walkMWithLuaFilter :: LuaFilter -> Pandoc -> IO Pandoc walkMWithLuaFilter (LuaFilter lua inlineFnMap blockFnMap docFnMap) = walkM (execInlineLuaFilter lua inlineFnMap) >=> @@ -227,11 +219,3 @@ instance StackValue (LuaFilterFunction a) where Lua.rawseti lua (-2) (len + 1) Lua.pop lua 1 return . Just $ LuaFilterFunction (len + 1) - - -isLuaFunction :: Lua.LuaState -> String -> IO Bool -isLuaFunction lua fnName = do - Lua.getglobal lua fnName - res <- Lua.isfunction lua (-1) - Lua.pop lua (-1) - return res diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index 62beedabc..8e26ece55 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -25,7 +25,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {-# OPTIONS_GHC -fno-warn-orphans #-} {- | Module : Text.Pandoc.Lua.StackInstances - Copyright : Copyright © 2017 Albert Krewinkel + Copyright : © 2012-2016 John MacFarlane + © 2017 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> @@ -38,10 +39,11 @@ module Text.Pandoc.Lua.StackInstances () where import Control.Applicative ( (<|>) ) import Scripting.Lua ( LTYPE(..), LuaState, StackValue(..) - , call, getglobal2, gettable, ltype, newtable, next, objlen - , pop, pushnil, rawgeti, rawseti, settable + , call, getglobal2, ltype, newtable, next, objlen, pop, pushnil ) import Text.Pandoc.Definition +import Text.Pandoc.Lua.Util + ( adjustIndexBy, addValue, getTable, addRawInt, getRawInt ) import qualified Data.Map as M import qualified Text.Pandoc.UTF8 as UTF8 @@ -49,11 +51,11 @@ import qualified Text.Pandoc.UTF8 as UTF8 instance StackValue Pandoc where push lua (Pandoc meta blocks) = do newtable lua - addKeyValue lua "blocks" blocks - addKeyValue lua "meta" meta + addValue lua "blocks" blocks + addValue lua "meta" meta peek lua idx = do - blocks <- getField lua idx "blocks" - meta <- getField lua idx "meta" + blocks <- getTable lua idx "blocks" + meta <- getTable lua idx "meta" return $ Pandoc <$> meta <*> blocks valuetype _ = TTABLE @@ -71,22 +73,22 @@ instance StackValue MetaValue where MetaMap metamap -> pushViaConstructor lua "MetaMap" metamap MetaString str -> push lua str peek lua idx = do + -- Get the contents of an AST element. + let elementContent :: StackValue a => IO (Maybe a) + elementContent = getTable lua idx "c" luatype <- ltype lua idx case luatype of TBOOLEAN -> fmap MetaBool <$> peek lua idx TSTRING -> fmap MetaString <$> peek lua idx TTABLE -> do - tag <- push lua "t" - *> gettable lua (idx `adjustIndexBy` 1) - *> peek lua (-1) - <* pop lua 1 + tag <- getTable lua idx "t" case tag of - Just "MetaBlocks" -> fmap MetaBlocks <$> peekContent lua idx - Just "MetaBool" -> fmap MetaBool <$> peekContent lua idx - Just "MetaMap" -> fmap MetaMap <$> peekContent lua idx - Just "MetaInlines" -> fmap MetaInlines <$> peekContent lua idx - Just "MetaList" -> fmap MetaList <$> peekContent lua idx - Just "MetaString" -> fmap MetaString <$> peekContent lua idx + Just "MetaBlocks" -> fmap MetaBlocks <$> elementContent + Just "MetaBool" -> fmap MetaBool <$> elementContent + Just "MetaMap" -> fmap MetaMap <$> elementContent + Just "MetaInlines" -> fmap MetaInlines <$> elementContent + Just "MetaList" -> fmap MetaList <$> elementContent + Just "MetaString" -> fmap MetaString <$> elementContent Nothing -> do len <- objlen lua idx if len <= 0 @@ -104,12 +106,6 @@ instance StackValue MetaValue where MetaMap _ -> TTABLE MetaString _ -> TSTRING -peekContent :: StackValue a => LuaState -> Int -> IO (Maybe a) -peekContent lua idx = do - push lua "c" - gettable lua (idx `adjustIndexBy` 1) - peek lua (-1) <* pop lua 1 - instance StackValue Block where push lua = \case BlockQuote blcks -> pushViaConstructor lua "BlockQuote" blcks @@ -162,7 +158,7 @@ instance StackValue Alignment where AlignCenter -> getglobal2 lua "pandoc.AlignCenter" AlignDefault -> getglobal2 lua "pandoc.AlignDefault" peek lua idx = do - tag <- getField lua idx "t" + tag <- getTable lua idx "t" case tag of Just "AlignLeft" -> return $ Just AlignLeft Just "AlignRight" -> return $ Just AlignRight @@ -175,12 +171,12 @@ instance StackValue Citation where push lua (Citation cid prefix suffix mode noteNum hash) = pushViaConstructor lua "Citation" cid mode prefix suffix noteNum hash peek lua idx = do - id' <- getField lua idx "citationId" - prefix <- getField lua idx "citationPrefix" - suffix <- getField lua idx "citationSuffix" - mode <- getField lua idx "citationMode" - num <- getField lua idx "citationNoteNum" - hash <- getField lua idx "citationHash" + id' <- getTable lua idx "citationId" + prefix <- getTable lua idx "citationPrefix" + suffix <- getTable lua idx "citationSuffix" + mode <- getTable lua idx "citationMode" + num <- getTable lua idx "citationNoteNum" + hash <- getTable lua idx "citationHash" return $ Citation <$> id' <*> prefix <*> suffix <*> mode <*> num <*> hash valuetype _ = TTABLE @@ -190,7 +186,7 @@ instance StackValue CitationMode where NormalCitation -> getglobal2 lua "pandoc.NormalCitation" SuppressAuthor -> getglobal2 lua "pandoc.SuppressAuthor" peek lua idx = do - tag <- getField lua idx "t" + tag <- getTable lua idx "t" case tag of Just "AuthorInText" -> return $ Just AuthorInText Just "NormalCitation" -> return $ Just NormalCitation @@ -210,7 +206,7 @@ instance StackValue ListNumberDelim where OneParen -> getglobal2 lua "pandoc.OneParen" TwoParens -> getglobal2 lua "pandoc.TwoParens" peek lua idx = do - tag <- getField lua idx "t" + tag <- getTable lua idx "t" case tag of Just "DefaultDelim" -> return $ Just DefaultDelim Just "Period" -> return $ Just Period @@ -229,7 +225,7 @@ instance StackValue ListNumberStyle where Decimal -> getglobal2 lua "pandoc.Decimal" Example -> getglobal2 lua "pandoc.Example" peek lua idx = do - tag <- getField lua idx "t" + tag <- getTable lua idx "t" case tag of Just "DefaultStyle" -> return $ Just DefaultStyle Just "LowerRoman" -> return $ Just LowerRoman @@ -246,7 +242,7 @@ instance StackValue MathType where InlineMath -> getglobal2 lua "pandoc.InlineMath" DisplayMath -> getglobal2 lua "pandoc.DisplayMath" peek lua idx = do - res <- getField lua idx "t" + res <- getTable lua idx "t" case res of Just "InlineMath" -> return $ Just InlineMath Just "DisplayMath" -> return $ Just DisplayMath @@ -258,7 +254,7 @@ instance StackValue QuoteType where SingleQuote -> getglobal2 lua "pandoc.SingleQuote" DoubleQuote -> getglobal2 lua "pandoc.DoubleQuote" peek lua idx = do - res <- getField lua idx "t" + res <- getTable lua idx "t" case res of Just "SingleQuote" -> return $ Just SingleQuote Just "DoubleQuote" -> return $ Just DoubleQuote @@ -277,11 +273,11 @@ instance StackValue [Char] where instance (StackValue a, StackValue b) => StackValue (a, b) where push lua (a, b) = do newtable lua - addIndexedValue lua 1 a - addIndexedValue lua 2 b + addRawInt lua 1 a + addRawInt lua 2 b peek lua idx = do - a <- getIndexedValue lua idx 1 - b <- getIndexedValue lua idx 2 + a <- getRawInt lua idx 1 + b <- getRawInt lua idx 2 return $ (,) <$> a <*> b valuetype _ = TTABLE @@ -290,13 +286,13 @@ instance (StackValue a, StackValue b, StackValue c) => where push lua (a, b, c) = do newtable lua - addIndexedValue lua 1 a - addIndexedValue lua 2 b - addIndexedValue lua 3 c + addRawInt lua 1 a + addRawInt lua 2 b + addRawInt lua 3 c peek lua idx = do - a <- getIndexedValue lua idx 1 - b <- getIndexedValue lua idx 2 - c <- getIndexedValue lua idx 3 + a <- getRawInt lua idx 1 + b <- getRawInt lua idx 2 + c <- getRawInt lua idx 3 return $ (,,) <$> a <*> b <*> c valuetype _ = TTABLE @@ -306,17 +302,17 @@ instance (StackValue a, StackValue b, StackValue c, where push lua (a, b, c, d, e) = do newtable lua - addIndexedValue lua 1 a - addIndexedValue lua 2 b - addIndexedValue lua 3 c - addIndexedValue lua 4 d - addIndexedValue lua 5 e + addRawInt lua 1 a + addRawInt lua 2 b + addRawInt lua 3 c + addRawInt lua 4 d + addRawInt lua 5 e peek lua idx = do - a <- getIndexedValue lua idx 1 - b <- getIndexedValue lua idx 2 - c <- getIndexedValue lua idx 3 - d <- getIndexedValue lua idx 4 - e <- getIndexedValue lua idx 5 + a <- getRawInt lua idx 1 + b <- getRawInt lua idx 2 + c <- getRawInt lua idx 3 + d <- getRawInt lua idx 4 + e <- getRawInt lua idx 5 return $ (,,,,) <$> a <*> b <*> c <*> d <*> e valuetype _ = TTABLE @@ -324,7 +320,7 @@ instance (Ord a, StackValue a, StackValue b) => StackValue (M.Map a b) where push lua m = do newtable lua - mapM_ (uncurry $ addKeyValue lua) $ M.toList m + mapM_ (uncurry $ addValue lua) $ M.toList m peek lua idx = fmap M.fromList <$> keyValuePairs lua idx valuetype _ = TTABLE @@ -381,7 +377,7 @@ pushViaConstructor lua pandocFn = pushViaCall lua ("pandoc." ++ pandocFn) -- | Return the value at the given index as inline if possible. peekInline :: LuaState -> Int -> IO (Maybe Inline) peekInline lua idx = do - tag <- getField lua idx "t" + tag <- getTable lua idx "t" case tag of Nothing -> return Nothing Just t -> case t of @@ -410,12 +406,12 @@ peekInline lua idx = do where -- Get the contents of an AST element. elementContent :: StackValue a => IO (Maybe a) - elementContent = getField lua idx "c" + elementContent = getTable lua idx "c" -- | Return the value at the given index as block if possible. peekBlock :: LuaState -> Int -> IO (Maybe Block) peekBlock lua idx = do - tag <- getField lua idx "t" + tag <- getTable lua idx "t" case tag of Nothing -> return Nothing Just t -> case t of @@ -440,47 +436,4 @@ peekBlock lua idx = do where -- Get the contents of an AST element. elementContent :: StackValue a => IO (Maybe a) - elementContent = getField lua idx "c" - --- | Adjust the stack index, assuming that @n@ new elements have been pushed on --- the stack. -adjustIndexBy :: Int -> Int -> Int -adjustIndexBy idx n = - if idx < 0 - then idx - n - else idx - --- | Get value behind key from table at given index. -getField :: (StackValue a, StackValue b) => LuaState -> Int -> a -> IO (Maybe b) -getField lua idx key = do - push lua key - gettable lua (idx `adjustIndexBy` 1) - peek lua (-1) <* pop lua 1 - --- | Set value for key for table at the given index -setKeyValue :: (StackValue a, StackValue b) => LuaState -> Int -> a -> b -> IO () -setKeyValue lua idx key value = do - push lua key - push lua value - settable lua (idx `adjustIndexBy` 2) - --- | Add a key-value pair to the table at the top of the stack -addKeyValue :: (StackValue a, StackValue b) => LuaState -> a -> b -> IO () -addKeyValue lua = setKeyValue lua (-1) - --- | Get value behind key from table at given index. -getIndexedValue :: StackValue a => LuaState -> Int -> Int -> IO (Maybe a) -getIndexedValue lua idx key = - rawgeti lua idx key - *> peek lua (-1) - <* pop lua 1 - --- | Set numeric key/value in table at the given index -setIndexedValue :: StackValue a => LuaState -> Int -> Int -> a -> IO () -setIndexedValue lua idx key value = do - push lua value - rawseti lua (idx `adjustIndexBy` 1) key - --- | Set numeric key/value in table at the top of the stack. -addIndexedValue :: StackValue a => LuaState -> Int -> a -> IO () -addIndexedValue lua = setIndexedValue lua (-1) + elementContent = getTable lua idx "c" diff --git a/src/Text/Pandoc/Lua/Util.hs b/src/Text/Pandoc/Lua/Util.hs new file mode 100644 index 000000000..9c5625c3c --- /dev/null +++ b/src/Text/Pandoc/Lua/Util.hs @@ -0,0 +1,86 @@ +{- +Copyright © 2012-2016 John MacFarlane <jgm@berkeley.edu> + 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> + +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.Lua.Util + Copyright : © 2012–2016 John MacFarlane, + © 2017 Albert Krewinkel + License : GNU GPL, version 2 or above + + Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> + Stability : alpha + +Lua utility functions. +-} +module Text.Pandoc.Lua.Util + ( adjustIndexBy + , getTable + , setTable + , addValue + , getRawInt + , setRawInt + , addRawInt + ) where + +import Scripting.Lua + ( LuaState, StackValue(..) + , gettable, pop, rawgeti, rawseti, settable + ) + +-- | Adjust the stack index, assuming that @n@ new elements have been pushed on +-- the stack. +adjustIndexBy :: Int -> Int -> Int +adjustIndexBy idx n = + if idx < 0 + then idx - n + else idx + +-- | Get value behind key from table at given index. +getTable :: (StackValue a, StackValue b) => LuaState -> Int -> a -> IO (Maybe b) +getTable lua idx key = do + push lua key + gettable lua (idx `adjustIndexBy` 1) + peek lua (-1) <* pop lua 1 + +-- | Set value for key for table at the given index +setTable :: (StackValue a, StackValue b) => LuaState -> Int -> a -> b -> IO () +setTable lua idx key value = do + push lua key + push lua value + settable lua (idx `adjustIndexBy` 2) + +-- | Add a key-value pair to the table at the top of the stack +addValue :: (StackValue a, StackValue b) => LuaState -> a -> b -> IO () +addValue lua = setTable lua (-1) + +-- | Get value behind key from table at given index. +getRawInt :: StackValue a => LuaState -> Int -> Int -> IO (Maybe a) +getRawInt lua idx key = + rawgeti lua idx key + *> peek lua (-1) + <* pop lua 1 + +-- | Set numeric key/value in table at the given index +setRawInt :: StackValue a => LuaState -> Int -> Int -> a -> IO () +setRawInt lua idx key value = do + push lua value + rawseti lua (idx `adjustIndexBy` 1) key + +-- | Set numeric key/value in table at the top of the stack. +addRawInt :: StackValue a => LuaState -> Int -> a -> IO () +addRawInt lua = setRawInt lua (-1) diff --git a/src/Text/Pandoc/Writers/Custom.hs b/src/Text/Pandoc/Writers/Custom.hs index 58b222997..b06dd0c8a 100644 --- a/src/Text/Pandoc/Writers/Custom.hs +++ b/src/Text/Pandoc/Writers/Custom.hs @@ -2,8 +2,6 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ScopedTypeVariables #-} #if MIN_VERSION_base(4,8,0) #else {-# LANGUAGE OverlappingInstances #-} @@ -48,6 +46,7 @@ import GHC.IO.Encoding (getForeignEncoding, setForeignEncoding, utf8) import Scripting.Lua (LuaState, StackValue, callfunc) import qualified Scripting.Lua as Lua import Text.Pandoc.Lua.Compat ( loadstring ) +import Text.Pandoc.Lua.Util ( addValue ) import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Templates @@ -131,14 +130,12 @@ instance StackValue MetaValue where instance StackValue Citation where push lua cit = do Lua.createtable lua 6 0 - let addValue (k :: String, v) = Lua.push lua k >> Lua.push lua v >> - Lua.rawset lua (-3) - addValue ("citationId", citationId cit) - addValue ("citationPrefix", citationPrefix cit) - addValue ("citationSuffix", citationSuffix cit) - addValue ("citationMode", show (citationMode cit)) - addValue ("citationNoteNum", citationNoteNum cit) - addValue ("citationHash", citationHash cit) + addValue lua "citationId" $ citationId cit + addValue lua "citationPrefix" $ citationPrefix cit + addValue lua "citationSuffix" $ citationSuffix cit + addValue lua "citationMode" $ show (citationMode cit) + addValue lua "citationNoteNum" $ citationNoteNum cit + addValue lua "citationHash" $ citationHash cit peek = undefined valuetype _ = Lua.TTABLE -- cgit v1.2.3 From 0add4253e6dc5c3cdca894c5bb312428fe3d31b3 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Fri, 14 Apr 2017 19:07:55 +0200 Subject: Avoid repeating StackValue instances definitions The lua filters and custom lua writer system defined very similar StackValue instances for strings and tuples. These instance definitions are extracted to a separate module to enable sharing. --- src/Text/Pandoc/Lua/SharedInstances.hs | 106 +++++++++++++++++++++++++++ src/Text/Pandoc/Lua/StackInstances.hs | 128 +-------------------------------- src/Text/Pandoc/Lua/Util.hs | 56 ++++++++++++++- src/Text/Pandoc/Writers/Custom.hs | 31 +------- 4 files changed, 165 insertions(+), 156 deletions(-) create mode 100644 src/Text/Pandoc/Lua/SharedInstances.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/SharedInstances.hs b/src/Text/Pandoc/Lua/SharedInstances.hs new file mode 100644 index 000000000..02438b93b --- /dev/null +++ b/src/Text/Pandoc/Lua/SharedInstances.hs @@ -0,0 +1,106 @@ +{- +Copyright © 2012-2016 John MacFarlane <jgm@berkeley.edu> + 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> + +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 +-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE LambdaCase #-} +#if !MIN_VERSION_base(4,8,0) +{-# LANGUAGE OverlappingInstances #-} +#endif +{-# OPTIONS_GHC -fno-warn-orphans #-} +{- | + Module : Text.Pandoc.Lua.SharedInstances + Copyright : © 2012–2016 John MacFarlane, + © 2017 Albert Krewinkel + License : GNU GPL, version 2 or above + + Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> + Stability : alpha + +Shared StackValue instances for pandoc and generic types. +-} +module Text.Pandoc.Lua.SharedInstances () where + +import Scripting.Lua ( LTYPE(..), StackValue(..), newtable ) +import Text.Pandoc.Lua.Util ( addRawInt, addValue, getRawInt, keyValuePairs ) + +import qualified Data.Map as M +import qualified Text.Pandoc.UTF8 as UTF8 + +#if MIN_VERSION_base(4,8,0) +instance {-# OVERLAPS #-} StackValue [Char] where +#else +instance StackValue [Char] where +#endif + push lua cs = push lua (UTF8.fromString cs) + peek lua i = fmap UTF8.toString <$> peek lua i + valuetype _ = TSTRING + +instance (StackValue a, StackValue b) => StackValue (a, b) where + push lua (a, b) = do + newtable lua + addRawInt lua 1 a + addRawInt lua 2 b + peek lua idx = do + a <- getRawInt lua idx 1 + b <- getRawInt lua idx 2 + return $ (,) <$> a <*> b + valuetype _ = TTABLE + +instance (StackValue a, StackValue b, StackValue c) => + StackValue (a, b, c) + where + push lua (a, b, c) = do + newtable lua + addRawInt lua 1 a + addRawInt lua 2 b + addRawInt lua 3 c + peek lua idx = do + a <- getRawInt lua idx 1 + b <- getRawInt lua idx 2 + c <- getRawInt lua idx 3 + return $ (,,) <$> a <*> b <*> c + valuetype _ = TTABLE + +instance (StackValue a, StackValue b, StackValue c, + StackValue d, StackValue e) => + StackValue (a, b, c, d, e) + where + push lua (a, b, c, d, e) = do + newtable lua + addRawInt lua 1 a + addRawInt lua 2 b + addRawInt lua 3 c + addRawInt lua 4 d + addRawInt lua 5 e + peek lua idx = do + a <- getRawInt lua idx 1 + b <- getRawInt lua idx 2 + c <- getRawInt lua idx 3 + d <- getRawInt lua idx 4 + e <- getRawInt lua idx 5 + return $ (,,,,) <$> a <*> b <*> c <*> d <*> e + valuetype _ = TTABLE + +instance (Ord a, StackValue a, StackValue b) => + StackValue (M.Map a b) where + push lua m = do + newtable lua + mapM_ (uncurry $ addValue lua) $ M.toList m + peek lua idx = fmap M.fromList <$> keyValuePairs lua idx + valuetype _ = TTABLE diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index 8e26ece55..8af7f78c0 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -16,12 +16,8 @@ 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 -} -{-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE LambdaCase #-} -#if !MIN_VERSION_base(4,8,0) -{-# LANGUAGE OverlappingInstances #-} -#endif {-# OPTIONS_GHC -fno-warn-orphans #-} {- | Module : Text.Pandoc.Lua.StackInstances @@ -38,15 +34,10 @@ module Text.Pandoc.Lua.StackInstances () where import Control.Applicative ( (<|>) ) import Scripting.Lua - ( LTYPE(..), LuaState, StackValue(..) - , call, getglobal2, ltype, newtable, next, objlen, pop, pushnil - ) + ( LTYPE(..), LuaState, StackValue(..), getglobal2, ltype, newtable, objlen ) import Text.Pandoc.Definition -import Text.Pandoc.Lua.Util - ( adjustIndexBy, addValue, getTable, addRawInt, getRawInt ) - -import qualified Data.Map as M -import qualified Text.Pandoc.UTF8 as UTF8 +import Text.Pandoc.Lua.SharedInstances () +import Text.Pandoc.Lua.Util ( addValue, getTable, pushViaConstructor ) instance StackValue Pandoc where push lua (Pandoc meta blocks) = do @@ -261,119 +252,6 @@ instance StackValue QuoteType where _ -> return Nothing valuetype _ = TTABLE -#if MIN_VERSION_base(4,8,0) -instance {-# OVERLAPS #-} StackValue [Char] where -#else -instance StackValue [Char] where -#endif - push lua cs = push lua (UTF8.fromString cs) - peek lua i = fmap UTF8.toString <$> peek lua i - valuetype _ = TSTRING - -instance (StackValue a, StackValue b) => StackValue (a, b) where - push lua (a, b) = do - newtable lua - addRawInt lua 1 a - addRawInt lua 2 b - peek lua idx = do - a <- getRawInt lua idx 1 - b <- getRawInt lua idx 2 - return $ (,) <$> a <*> b - valuetype _ = TTABLE - -instance (StackValue a, StackValue b, StackValue c) => - StackValue (a, b, c) - where - push lua (a, b, c) = do - newtable lua - addRawInt lua 1 a - addRawInt lua 2 b - addRawInt lua 3 c - peek lua idx = do - a <- getRawInt lua idx 1 - b <- getRawInt lua idx 2 - c <- getRawInt lua idx 3 - return $ (,,) <$> a <*> b <*> c - valuetype _ = TTABLE - -instance (StackValue a, StackValue b, StackValue c, - StackValue d, StackValue e) => - StackValue (a, b, c, d, e) - where - push lua (a, b, c, d, e) = do - newtable lua - addRawInt lua 1 a - addRawInt lua 2 b - addRawInt lua 3 c - addRawInt lua 4 d - addRawInt lua 5 e - peek lua idx = do - a <- getRawInt lua idx 1 - b <- getRawInt lua idx 2 - c <- getRawInt lua idx 3 - d <- getRawInt lua idx 4 - e <- getRawInt lua idx 5 - return $ (,,,,) <$> a <*> b <*> c <*> d <*> e - valuetype _ = TTABLE - -instance (Ord a, StackValue a, StackValue b) => - StackValue (M.Map a b) where - push lua m = do - newtable lua - mapM_ (uncurry $ addValue lua) $ M.toList m - peek lua idx = fmap M.fromList <$> keyValuePairs lua idx - valuetype _ = TTABLE - --- | Try reading the value under the given index as a list of key-value pairs. -keyValuePairs :: (StackValue a, StackValue b) - => LuaState -> Int -> IO (Maybe [(a, b)]) -keyValuePairs lua idx = do - pushnil lua - sequence <$> remainingPairs - where - remainingPairs = do - res <- nextPair - case res of - Nothing -> return [] - Just a -> (a:) <$> remainingPairs - nextPair :: (StackValue a, StackValue b) => IO (Maybe (Maybe (a,b))) - nextPair = do - hasNext <- next lua (idx `adjustIndexBy` 1) - if hasNext - then do - val <- peek lua (-1) - key <- peek lua (-2) - pop lua 1 -- removes the value, keeps the key - return $ Just <$> ((,) <$> key <*> val) - else do - return Nothing - - --- | Helper class for pushing a single value to the stack via a lua function. --- See @pushViaCall@. -class PushViaCall a where - pushViaCall' :: LuaState -> String -> IO () -> Int -> a - -instance PushViaCall (IO ()) where - pushViaCall' lua fn pushArgs num = do - getglobal2 lua fn - pushArgs - call lua num 1 - -instance (StackValue a, PushViaCall b) => PushViaCall (a -> b) where - pushViaCall' lua fn pushArgs num x = - pushViaCall' lua fn (pushArgs *> push lua x) (num + 1) - --- | Push an value to the stack via a lua function. The lua function is called --- with all arguments that are passed to this function and is expected to return --- a single value. -pushViaCall :: PushViaCall a => LuaState -> String -> a -pushViaCall lua fn = pushViaCall' lua fn (return ()) 0 - --- | Call a pandoc element constructor within lua, passing all given arguments. -pushViaConstructor :: PushViaCall a => LuaState -> String -> a -pushViaConstructor lua pandocFn = pushViaCall lua ("pandoc." ++ pandocFn) - -- | Return the value at the given index as inline if possible. peekInline :: LuaState -> Int -> IO (Maybe Inline) peekInline lua idx = do diff --git a/src/Text/Pandoc/Lua/Util.hs b/src/Text/Pandoc/Lua/Util.hs index 9c5625c3c..f0b87c231 100644 --- a/src/Text/Pandoc/Lua/Util.hs +++ b/src/Text/Pandoc/Lua/Util.hs @@ -16,6 +16,7 @@ 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 -} +{-# LANGUAGE FlexibleInstances #-} {- | Module : Text.Pandoc.Lua.Util Copyright : © 2012–2016 John MacFarlane, @@ -35,11 +36,15 @@ module Text.Pandoc.Lua.Util , getRawInt , setRawInt , addRawInt + , keyValuePairs + , PushViaCall + , pushViaCall + , pushViaConstructor ) where import Scripting.Lua ( LuaState, StackValue(..) - , gettable, pop, rawgeti, rawseti, settable + , call, getglobal2, gettable, next, pop, pushnil, rawgeti, rawseti, settable ) -- | Adjust the stack index, assuming that @n@ new elements have been pushed on @@ -84,3 +89,52 @@ setRawInt lua idx key value = do -- | Set numeric key/value in table at the top of the stack. addRawInt :: StackValue a => LuaState -> Int -> a -> IO () addRawInt lua = setRawInt lua (-1) + +-- | Try reading the table under the given index as a list of key-value pairs. +keyValuePairs :: (StackValue a, StackValue b) + => LuaState -> Int -> IO (Maybe [(a, b)]) +keyValuePairs lua idx = do + pushnil lua + sequence <$> remainingPairs + where + remainingPairs = do + res <- nextPair + case res of + Nothing -> return [] + Just a -> (a:) <$> remainingPairs + nextPair :: (StackValue a, StackValue b) => IO (Maybe (Maybe (a,b))) + nextPair = do + hasNext <- next lua (idx `adjustIndexBy` 1) + if hasNext + then do + val <- peek lua (-1) + key <- peek lua (-2) + pop lua 1 -- removes the value, keeps the key + return $ Just <$> ((,) <$> key <*> val) + else do + return Nothing + +-- | Helper class for pushing a single value to the stack via a lua function. +-- See @pushViaCall@. +class PushViaCall a where + pushViaCall' :: LuaState -> String -> IO () -> Int -> a + +instance PushViaCall (IO ()) where + pushViaCall' lua fn pushArgs num = do + getglobal2 lua fn + pushArgs + call lua num 1 + +instance (StackValue a, PushViaCall b) => PushViaCall (a -> b) where + pushViaCall' lua fn pushArgs num x = + pushViaCall' lua fn (pushArgs *> push lua x) (num + 1) + +-- | Push an value to the stack via a lua function. The lua function is called +-- with all arguments that are passed to this function and is expected to return +-- a single value. +pushViaCall :: PushViaCall a => LuaState -> String -> a +pushViaCall lua fn = pushViaCall' lua fn (return ()) 0 + +-- | Call a pandoc element constructor within lua, passing all given arguments. +pushViaConstructor :: PushViaCall a => LuaState -> String -> a +pushViaConstructor lua pandocFn = pushViaCall lua ("pandoc." ++ pandocFn) diff --git a/src/Text/Pandoc/Writers/Custom.hs b/src/Text/Pandoc/Writers/Custom.hs index b06dd0c8a..ce90e4834 100644 --- a/src/Text/Pandoc/Writers/Custom.hs +++ b/src/Text/Pandoc/Writers/Custom.hs @@ -47,6 +47,7 @@ import Scripting.Lua (LuaState, StackValue, callfunc) import qualified Scripting.Lua as Lua import Text.Pandoc.Lua.Compat ( loadstring ) import Text.Pandoc.Lua.Util ( addValue ) +import Text.Pandoc.Lua.SharedInstances () import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Templates @@ -59,41 +60,11 @@ attrToMap (id',classes,keyvals) = M.fromList : ("class", unwords classes) : keyvals -#if MIN_VERSION_base(4,8,0) -instance {-# OVERLAPS #-} StackValue [Char] where -#else -instance StackValue [Char] where -#endif - push lua cs = Lua.push lua (UTF8.fromString cs) - peek lua i = do - res <- Lua.peek lua i - return $ UTF8.toString `fmap` res - valuetype _ = Lua.TSTRING - instance StackValue Format where push lua (Format f) = Lua.push lua (map toLower f) peek l n = fmap Format `fmap` Lua.peek l n valuetype _ = Lua.TSTRING -instance (StackValue a, StackValue b) => StackValue (M.Map a b) where - push lua m = do - let xs = M.toList m - Lua.createtable lua (length xs + 1) 0 - let addValue (k, v) = Lua.push lua k >> Lua.push lua v >> - Lua.rawset lua (-3) - mapM_ addValue xs - peek _ _ = undefined -- not needed for our purposes - valuetype _ = Lua.TTABLE - -instance (StackValue a, StackValue b) => StackValue (a,b) where - push lua (k,v) = do - Lua.createtable lua 2 0 - Lua.push lua k - Lua.push lua v - Lua.rawset lua (-3) - peek _ _ = undefined -- not needed for our purposes - valuetype _ = Lua.TTABLE - #if MIN_VERSION_base(4,8,0) instance {-# OVERLAPS #-} StackValue [Inline] where #else -- cgit v1.2.3 From d671b69b8790a0c73019257ec2d8cd21859e1c06 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Fri, 14 Apr 2017 22:32:08 +0200 Subject: Lua filter: use lua strings for nullary constructors Lua string are used to represent nullary data constructors. The previous table-based representation was based on the JSON serialization, but can be simplified. This also matches the way those arguments are passed to custom writers. --- src/Text/Pandoc/Lua/StackInstances.hs | 99 +++++++---------------------------- 1 file changed, 19 insertions(+), 80 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index 8af7f78c0..796095512 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -34,10 +34,11 @@ module Text.Pandoc.Lua.StackInstances () where import Control.Applicative ( (<|>) ) import Scripting.Lua - ( LTYPE(..), LuaState, StackValue(..), getglobal2, ltype, newtable, objlen ) + ( LTYPE(..), LuaState, StackValue(..), ltype, newtable, objlen ) import Text.Pandoc.Definition import Text.Pandoc.Lua.SharedInstances () import Text.Pandoc.Lua.Util ( addValue, getTable, pushViaConstructor ) +import Text.Pandoc.Shared ( safeRead ) instance StackValue Pandoc where push lua (Pandoc meta blocks) = do @@ -143,19 +144,8 @@ instance StackValue Inline where valuetype _ = TTABLE instance StackValue Alignment where - push lua = \case - AlignLeft -> getglobal2 lua "pandoc.AlignLeft" - AlignRight -> getglobal2 lua "pandoc.AlignRight" - AlignCenter -> getglobal2 lua "pandoc.AlignCenter" - AlignDefault -> getglobal2 lua "pandoc.AlignDefault" - peek lua idx = do - tag <- getTable lua idx "t" - case tag of - Just "AlignLeft" -> return $ Just AlignLeft - Just "AlignRight" -> return $ Just AlignRight - Just "AlignCenter" -> return $ Just AlignCenter - Just "AlignDefault" -> return $ Just AlignDefault - _ -> return Nothing + push lua = push lua . show + peek lua idx = (>>= safeRead) <$> peek lua idx valuetype _ = TSTRING instance StackValue Citation where @@ -172,18 +162,9 @@ instance StackValue Citation where valuetype _ = TTABLE instance StackValue CitationMode where - push lua = \case - AuthorInText -> getglobal2 lua "pandoc.AuthorInText" - NormalCitation -> getglobal2 lua "pandoc.NormalCitation" - SuppressAuthor -> getglobal2 lua "pandoc.SuppressAuthor" - peek lua idx = do - tag <- getTable lua idx "t" - case tag of - Just "AuthorInText" -> return $ Just AuthorInText - Just "NormalCitation" -> return $ Just NormalCitation - Just "SuppressAuthor" -> return $ Just SuppressAuthor - _ -> return Nothing - valuetype _ = TTABLE + push lua = push lua . show + peek lua idx = (>>= safeRead) <$> peek lua idx + valuetype _ = TSTRING instance StackValue Format where push lua (Format f) = push lua f @@ -191,66 +172,24 @@ instance StackValue Format where valuetype _ = TSTRING instance StackValue ListNumberDelim where - push lua = \case - DefaultDelim -> getglobal2 lua "pandoc.DefaultDelim" - Period -> getglobal2 lua "pandoc.Period" - OneParen -> getglobal2 lua "pandoc.OneParen" - TwoParens -> getglobal2 lua "pandoc.TwoParens" - peek lua idx = do - tag <- getTable lua idx "t" - case tag of - Just "DefaultDelim" -> return $ Just DefaultDelim - Just "Period" -> return $ Just Period - Just "OneParen" -> return $ Just OneParen - Just "TwoParens" -> return $ Just TwoParens - _ -> return Nothing - valuetype _ = TTABLE + push lua = push lua . show + peek lua idx = (>>= safeRead) <$> peek lua idx + valuetype _ = TSTRING instance StackValue ListNumberStyle where - push lua = \case - DefaultStyle -> getglobal2 lua "pandoc.DefaultStyle" - LowerRoman -> getglobal2 lua "pandoc.LowerRoman" - UpperRoman -> getglobal2 lua "pandoc.UpperRoman" - LowerAlpha -> getglobal2 lua "pandoc.LowerAlpha" - UpperAlpha -> getglobal2 lua "pandoc.UpperAlpha" - Decimal -> getglobal2 lua "pandoc.Decimal" - Example -> getglobal2 lua "pandoc.Example" - peek lua idx = do - tag <- getTable lua idx "t" - case tag of - Just "DefaultStyle" -> return $ Just DefaultStyle - Just "LowerRoman" -> return $ Just LowerRoman - Just "UpperRoman" -> return $ Just UpperRoman - Just "LowerAlpha" -> return $ Just LowerAlpha - Just "UpperAlpha" -> return $ Just UpperAlpha - Just "Decimal" -> return $ Just Decimal - Just "Example" -> return $ Just Example - _ -> return Nothing - valuetype _ = TTABLE + push lua = push lua . show + peek lua idx = (>>= safeRead) <$> peek lua idx + valuetype _ = TSTRING instance StackValue MathType where - push lua = \case - InlineMath -> getglobal2 lua "pandoc.InlineMath" - DisplayMath -> getglobal2 lua "pandoc.DisplayMath" - peek lua idx = do - res <- getTable lua idx "t" - case res of - Just "InlineMath" -> return $ Just InlineMath - Just "DisplayMath" -> return $ Just DisplayMath - _ -> return Nothing - valuetype _ = TTABLE + push lua = push lua . show + peek lua idx = (>>= safeRead) <$> peek lua idx + valuetype _ = TSTRING instance StackValue QuoteType where - push lua = \case - SingleQuote -> getglobal2 lua "pandoc.SingleQuote" - DoubleQuote -> getglobal2 lua "pandoc.DoubleQuote" - peek lua idx = do - res <- getTable lua idx "t" - case res of - Just "SingleQuote" -> return $ Just SingleQuote - Just "DoubleQuote" -> return $ Just DoubleQuote - _ -> return Nothing - valuetype _ = TTABLE + push lua = push lua . show + peek lua idx = (>>= safeRead) <$> peek lua idx + valuetype _ = TSTRING -- | Return the value at the given index as inline if possible. peekInline :: LuaState -> Int -> IO (Maybe Inline) -- cgit v1.2.3 From eb8de6514b1ed44087a1d98a2cb8745b2903d98b Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Fri, 14 Apr 2017 22:58:00 +0200 Subject: Lua filter: Re-order code of stack value instances --- src/Text/Pandoc/Lua/StackInstances.hs | 228 ++++++++++++++++++---------------- 1 file changed, 122 insertions(+), 106 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index 796095512..d57144513 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -57,39 +57,8 @@ instance StackValue Meta where valuetype _ = TTABLE instance StackValue MetaValue where - push lua = \case - MetaBlocks blcks -> pushViaConstructor lua "MetaBlocks" blcks - MetaBool bool -> push lua bool - MetaInlines inlns -> pushViaConstructor lua "MetaInlines" inlns - MetaList metalist -> pushViaConstructor lua "MetaList" metalist - MetaMap metamap -> pushViaConstructor lua "MetaMap" metamap - MetaString str -> push lua str - peek lua idx = do - -- Get the contents of an AST element. - let elementContent :: StackValue a => IO (Maybe a) - elementContent = getTable lua idx "c" - luatype <- ltype lua idx - case luatype of - TBOOLEAN -> fmap MetaBool <$> peek lua idx - TSTRING -> fmap MetaString <$> peek lua idx - TTABLE -> do - tag <- getTable lua idx "t" - case tag of - Just "MetaBlocks" -> fmap MetaBlocks <$> elementContent - Just "MetaBool" -> fmap MetaBool <$> elementContent - Just "MetaMap" -> fmap MetaMap <$> elementContent - Just "MetaInlines" -> fmap MetaInlines <$> elementContent - Just "MetaList" -> fmap MetaList <$> elementContent - Just "MetaString" -> fmap MetaString <$> elementContent - Nothing -> do - len <- objlen lua idx - if len <= 0 - then fmap MetaMap <$> peek lua idx - else (fmap MetaInlines <$> peek lua idx) - <|> (fmap MetaBlocks <$> peek lua idx) - <|> (fmap MetaList <$> peek lua idx) - _ -> return Nothing - _ -> return Nothing + push = pushMetaValue + peek = peekMetaValue valuetype = \case MetaBlocks _ -> TTABLE MetaBool _ -> TBOOLEAN @@ -99,55 +68,15 @@ instance StackValue MetaValue where MetaString _ -> TSTRING instance StackValue Block where - push lua = \case - BlockQuote blcks -> pushViaConstructor lua "BlockQuote" blcks - BulletList items -> pushViaConstructor lua "BulletList" items - CodeBlock attr code -> pushViaConstructor lua "CodeBlock" code attr - DefinitionList items -> pushViaConstructor lua "DefinitionList" items - Div attr blcks -> pushViaConstructor lua "Div" blcks attr - Header lvl attr inlns -> pushViaConstructor lua "Header" lvl attr inlns - HorizontalRule -> pushViaConstructor lua "HorizontalRule" - LineBlock blcks -> pushViaConstructor lua "LineBlock" blcks - OrderedList lstAttr list -> pushViaConstructor lua "OrderedList" list lstAttr - Null -> pushViaConstructor lua "Null" - Para blcks -> pushViaConstructor lua "Para" blcks - Plain blcks -> pushViaConstructor lua "Plain" blcks - RawBlock f cs -> pushViaConstructor lua "RawBlock" f cs - Table capt aligns widths headers rows -> - pushViaConstructor lua "Table" capt aligns widths headers rows - -- fall back to conversion via aeson's Value - peek lua i = peekBlock lua i + push = pushBlock + peek = peekBlock valuetype _ = TTABLE instance StackValue Inline where - push lua = \case - Cite citations lst -> pushViaConstructor lua "Cite" lst citations - Code attr lst -> pushViaConstructor lua "Code" lst attr - Emph inlns -> pushViaConstructor lua "Emph" inlns - Image attr alt (src,tit) -> pushViaConstructor lua "Image" alt src tit attr - LineBreak -> pushViaConstructor lua "LineBreak" - Link attr lst (src,tit) -> pushViaConstructor lua "Link" lst src tit attr - Note blcks -> pushViaConstructor lua "Note" blcks - Math mty str -> pushViaConstructor lua "Math" mty str - Quoted qt inlns -> pushViaConstructor lua "Quoted" qt inlns - RawInline f cs -> pushViaConstructor lua "RawInline" f cs - SmallCaps inlns -> pushViaConstructor lua "SmallCaps" inlns - SoftBreak -> pushViaConstructor lua "SoftBreak" - Space -> pushViaConstructor lua "Space" - Span attr inlns -> pushViaConstructor lua "Span" inlns attr - Str str -> pushViaConstructor lua "Str" str - Strikeout inlns -> pushViaConstructor lua "Strikeout" inlns - Strong inlns -> pushViaConstructor lua "Strong" inlns - Subscript inlns -> pushViaConstructor lua "Subscript" inlns - Superscript inlns -> pushViaConstructor lua "Superscript" inlns + push = pushInline peek = peekInline valuetype _ = TTABLE -instance StackValue Alignment where - push lua = push lua . show - peek lua idx = (>>= safeRead) <$> peek lua idx - valuetype _ = TSTRING - instance StackValue Citation where push lua (Citation cid prefix suffix mode noteNum hash) = pushViaConstructor lua "Citation" cid mode prefix suffix noteNum hash @@ -161,6 +90,11 @@ instance StackValue Citation where return $ Citation <$> id' <*> prefix <*> suffix <*> mode <*> num <*> hash valuetype _ = TTABLE +instance StackValue Alignment where + push lua = push lua . show + peek lua idx = (>>= safeRead) <$> peek lua idx + valuetype _ = TSTRING + instance StackValue CitationMode where push lua = push lua . show peek lua idx = (>>= safeRead) <$> peek lua idx @@ -191,6 +125,118 @@ instance StackValue QuoteType where peek lua idx = (>>= safeRead) <$> peek lua idx valuetype _ = TSTRING +-- | Push an meta value element to the top of the lua stack. +pushMetaValue :: LuaState -> MetaValue -> IO () +pushMetaValue lua = \case + MetaBlocks blcks -> pushViaConstructor lua "MetaBlocks" blcks + MetaBool bool -> push lua bool + MetaInlines inlns -> pushViaConstructor lua "MetaInlines" inlns + MetaList metalist -> pushViaConstructor lua "MetaList" metalist + MetaMap metamap -> pushViaConstructor lua "MetaMap" metamap + MetaString str -> push lua str + +-- | Interpret the value at the given stack index as meta value. +peekMetaValue :: LuaState -> Int -> IO (Maybe MetaValue) +peekMetaValue lua idx = do + -- Get the contents of an AST element. + let elementContent :: StackValue a => IO (Maybe a) + elementContent = getTable lua idx "c" + luatype <- ltype lua idx + case luatype of + TBOOLEAN -> fmap MetaBool <$> peek lua idx + TSTRING -> fmap MetaString <$> peek lua idx + TTABLE -> do + tag <- getTable lua idx "t" + case tag of + Just "MetaBlocks" -> fmap MetaBlocks <$> elementContent + Just "MetaBool" -> fmap MetaBool <$> elementContent + Just "MetaMap" -> fmap MetaMap <$> elementContent + Just "MetaInlines" -> fmap MetaInlines <$> elementContent + Just "MetaList" -> fmap MetaList <$> elementContent + Just "MetaString" -> fmap MetaString <$> elementContent + Nothing -> do + -- no meta value tag given, try to guess. + len <- objlen lua idx + if len <= 0 + then fmap MetaMap <$> peek lua idx + else (fmap MetaInlines <$> peek lua idx) + <|> (fmap MetaBlocks <$> peek lua idx) + <|> (fmap MetaList <$> peek lua idx) + _ -> return Nothing + _ -> return Nothing + +-- | Push an block element to the top of the lua stack. +pushBlock :: LuaState -> Block -> IO () +pushBlock lua = \case + BlockQuote blcks -> pushViaConstructor lua "BlockQuote" blcks + BulletList items -> pushViaConstructor lua "BulletList" items + CodeBlock attr code -> pushViaConstructor lua "CodeBlock" code attr + DefinitionList items -> pushViaConstructor lua "DefinitionList" items + Div attr blcks -> pushViaConstructor lua "Div" blcks attr + Header lvl attr inlns -> pushViaConstructor lua "Header" lvl attr inlns + HorizontalRule -> pushViaConstructor lua "HorizontalRule" + LineBlock blcks -> pushViaConstructor lua "LineBlock" blcks + OrderedList lstAttr list -> pushViaConstructor lua "OrderedList" list lstAttr + Null -> pushViaConstructor lua "Null" + Para blcks -> pushViaConstructor lua "Para" blcks + Plain blcks -> pushViaConstructor lua "Plain" blcks + RawBlock f cs -> pushViaConstructor lua "RawBlock" f cs + Table capt aligns widths headers rows -> + pushViaConstructor lua "Table" capt aligns widths headers rows + +-- | Return the value at the given index as block if possible. +peekBlock :: LuaState -> Int -> IO (Maybe Block) +peekBlock lua idx = do + tag <- getTable lua idx "t" + case tag of + Nothing -> return Nothing + Just t -> case t of + "BlockQuote" -> fmap BlockQuote <$> elementContent + "BulletList" -> fmap BulletList <$> elementContent + "CodeBlock" -> fmap (uncurry CodeBlock) <$> elementContent + "DefinitionList" -> fmap DefinitionList <$> elementContent + "Div" -> fmap (uncurry Div) <$> elementContent + "Header" -> fmap (\(lvl, attr, lst) -> Header lvl attr lst) + <$> elementContent + "HorizontalRule" -> return (Just HorizontalRule) + "LineBlock" -> fmap LineBlock <$> elementContent + "OrderedList" -> fmap (uncurry OrderedList) <$> elementContent + "Null" -> return (Just Null) + "Para" -> fmap Para <$> elementContent + "Plain" -> fmap Plain <$> elementContent + "RawBlock" -> fmap (uncurry RawBlock) <$> elementContent + "Table" -> fmap (\(capt, aligns, widths, headers, body) -> + Table capt aligns widths headers body) + <$> elementContent + _ -> return Nothing + where + -- Get the contents of an AST element. + elementContent :: StackValue a => IO (Maybe a) + elementContent = getTable lua idx "c" + +-- | Push an inline element to the top of the lua stack. +pushInline :: LuaState -> Inline -> IO () +pushInline lua = \case + Cite citations lst -> pushViaConstructor lua "Cite" lst citations + Code attr lst -> pushViaConstructor lua "Code" lst attr + Emph inlns -> pushViaConstructor lua "Emph" inlns + Image attr alt (src,tit) -> pushViaConstructor lua "Image" alt src tit attr + LineBreak -> pushViaConstructor lua "LineBreak" + Link attr lst (src,tit) -> pushViaConstructor lua "Link" lst src tit attr + Note blcks -> pushViaConstructor lua "Note" blcks + Math mty str -> pushViaConstructor lua "Math" mty str + Quoted qt inlns -> pushViaConstructor lua "Quoted" qt inlns + RawInline f cs -> pushViaConstructor lua "RawInline" f cs + SmallCaps inlns -> pushViaConstructor lua "SmallCaps" inlns + SoftBreak -> pushViaConstructor lua "SoftBreak" + Space -> pushViaConstructor lua "Space" + Span attr inlns -> pushViaConstructor lua "Span" inlns attr + Str str -> pushViaConstructor lua "Str" str + Strikeout inlns -> pushViaConstructor lua "Strikeout" inlns + Strong inlns -> pushViaConstructor lua "Strong" inlns + Subscript inlns -> pushViaConstructor lua "Subscript" inlns + Superscript inlns -> pushViaConstructor lua "Superscript" inlns + -- | Return the value at the given index as inline if possible. peekInline :: LuaState -> Int -> IO (Maybe Inline) peekInline lua idx = do @@ -224,33 +270,3 @@ peekInline lua idx = do -- Get the contents of an AST element. elementContent :: StackValue a => IO (Maybe a) elementContent = getTable lua idx "c" - --- | Return the value at the given index as block if possible. -peekBlock :: LuaState -> Int -> IO (Maybe Block) -peekBlock lua idx = do - tag <- getTable lua idx "t" - case tag of - Nothing -> return Nothing - Just t -> case t of - "BlockQuote" -> fmap BlockQuote <$> elementContent - "BulletList" -> fmap BulletList <$> elementContent - "CodeBlock" -> fmap (uncurry CodeBlock) <$> elementContent - "DefinitionList" -> fmap DefinitionList <$> elementContent - "Div" -> fmap (uncurry Div) <$> elementContent - "Header" -> fmap (\(lvl, attr, lst) -> Header lvl attr lst) - <$> elementContent - "HorizontalRule" -> return (Just HorizontalRule) - "LineBlock" -> fmap LineBlock <$> elementContent - "OrderedList" -> fmap (uncurry OrderedList) <$> elementContent - "Null" -> return (Just Null) - "Para" -> fmap Para <$> elementContent - "Plain" -> fmap Plain <$> elementContent - "RawBlock" -> fmap (uncurry RawBlock) <$> elementContent - "Table" -> fmap (\(capt, aligns, widths, headers, body) -> - Table capt aligns widths headers body) - <$> elementContent - _ -> return Nothing - where - -- Get the contents of an AST element. - elementContent :: StackValue a => IO (Maybe a) - elementContent = getTable lua idx "c" -- cgit v1.2.3 From 3aeed816e163b1ad3925caff0496fa05a63d1369 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Fri, 14 Apr 2017 23:24:52 +0200 Subject: Lua filter: allow shorthand functions for math and quoted Allow to use functions named `SingleQuoted`, `DoubleQuoted`, `DisplayMath`, and `InlineMath` in filters. --- src/Text/Pandoc/Lua.hs | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index d8b9f62f0..a89da52bc 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -34,7 +34,7 @@ import Control.Monad ( (>=>), when ) import Control.Monad.Trans ( MonadIO(..) ) import Data.Map ( Map ) import Scripting.Lua ( LuaState, StackValue(..) ) -import Text.Pandoc.Definition ( Block(..), Inline(..), Pandoc(..) ) +import Text.Pandoc.Definition import Text.Pandoc.Lua.PandocModule ( pushPandocModule ) import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Walk @@ -135,6 +135,12 @@ execInlineLuaFilter lua fnMap x = do case Map.lookup fnName fnMap of Nothing -> return x Just fn -> callFilterFn fn + let tryFilterAlternatives :: [(String, LuaFilterFunction Inline -> IO Inline)] -> IO Inline + tryFilterAlternatives [] = return x + tryFilterAlternatives ((fnName, callFilterFn) : alternatives) = + case Map.lookup fnName fnMap of + Nothing -> tryFilterAlternatives alternatives + Just fn -> callFilterFn fn case x of LineBreak -> tryFilter "LineBreak" runFn SoftBreak -> tryFilter "SoftBreak" runFn @@ -142,9 +148,7 @@ execInlineLuaFilter lua fnMap x = do Cite cs lst -> tryFilter "Cite" $ \fn -> runFn fn lst cs Code attr str -> tryFilter "Code" $ \fn -> runFn fn str attr Emph lst -> tryFilter "Emph" $ \fn -> runFn fn lst - Math mt lst -> tryFilter "Math" $ \fn -> runFn fn lst mt Note blks -> tryFilter "Note" $ \fn -> runFn fn blks - Quoted qt lst -> tryFilter "Quoted" $ \fn -> runFn fn qt lst RawInline f str -> tryFilter "RawInline" $ \fn -> runFn fn f str SmallCaps lst -> tryFilter "SmallCaps" $ \fn -> runFn fn lst Span attr lst -> tryFilter "Span" $ \fn -> runFn fn lst attr @@ -153,6 +157,22 @@ execInlineLuaFilter lua fnMap x = do Strong lst -> tryFilter "Strong" $ \fn -> runFn fn lst Subscript lst -> tryFilter "Subscript" $ \fn -> runFn fn lst Superscript lst -> tryFilter "Superscript" $ \fn -> runFn fn lst + Math DisplayMath lst -> tryFilterAlternatives + [ ("DisplayMath", \fn -> runFn fn lst) + , ("Math", \fn -> runFn fn DisplayMath lst) + ] + Math InlineMath lst -> tryFilterAlternatives + [ ("InlineMath", \fn -> runFn fn lst) + , ("Math", \fn -> runFn fn InlineMath lst) + ] + Quoted SingleQuote lst -> tryFilterAlternatives + [ ("SingleQuoted", \fn -> runFn fn lst) + , ("Quoted", \fn -> runFn fn SingleQuote lst) + ] + Quoted DoubleQuote lst -> tryFilterAlternatives + [ ("DoubleQuoted", \fn -> runFn fn lst) + , ("Quoted", \fn -> runFn fn DoubleQuote lst) + ] Link attr txt (src, tit) -> tryFilter "Link" $ \fn -> runFn fn txt src tit attr Image attr alt (src, tit) -> tryFilter "Image" $ -- cgit v1.2.3 From 3d6edbd9e39dfccdd76ec32f9aa34977b7b56fe0 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sat, 15 Apr 2017 09:31:09 +0200 Subject: Lua filter: use Attributes constructor for Attrs Element attributes are pushed to the stack via the `Attributes` function. `Attributes` creates an Attr like triple, but the triple also allows table-like access to key-value pairs. --- src/Text/Pandoc/Lua/StackInstances.hs | 40 +++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 14 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index d57144513..9ba28b58e 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -170,10 +170,10 @@ pushBlock :: LuaState -> Block -> IO () pushBlock lua = \case BlockQuote blcks -> pushViaConstructor lua "BlockQuote" blcks BulletList items -> pushViaConstructor lua "BulletList" items - CodeBlock attr code -> pushViaConstructor lua "CodeBlock" code attr + CodeBlock attr code -> pushViaConstructor lua "CodeBlock" code (LuaAttr attr) DefinitionList items -> pushViaConstructor lua "DefinitionList" items - Div attr blcks -> pushViaConstructor lua "Div" blcks attr - Header lvl attr inlns -> pushViaConstructor lua "Header" lvl attr inlns + Div attr blcks -> pushViaConstructor lua "Div" blcks (LuaAttr attr) + Header lvl attr inlns -> pushViaConstructor lua "Header" lvl (LuaAttr attr) inlns HorizontalRule -> pushViaConstructor lua "HorizontalRule" LineBlock blcks -> pushViaConstructor lua "LineBlock" blcks OrderedList lstAttr list -> pushViaConstructor lua "OrderedList" list lstAttr @@ -193,10 +193,10 @@ peekBlock lua idx = do Just t -> case t of "BlockQuote" -> fmap BlockQuote <$> elementContent "BulletList" -> fmap BulletList <$> elementContent - "CodeBlock" -> fmap (uncurry CodeBlock) <$> elementContent + "CodeBlock" -> fmap (withAttr CodeBlock) <$> elementContent "DefinitionList" -> fmap DefinitionList <$> elementContent - "Div" -> fmap (uncurry Div) <$> elementContent - "Header" -> fmap (\(lvl, attr, lst) -> Header lvl attr lst) + "Div" -> fmap (withAttr Div) <$> elementContent + "Header" -> fmap (\(lvl, LuaAttr attr, lst) -> Header lvl attr lst) <$> elementContent "HorizontalRule" -> return (Just HorizontalRule) "LineBlock" -> fmap LineBlock <$> elementContent @@ -218,11 +218,11 @@ peekBlock lua idx = do pushInline :: LuaState -> Inline -> IO () pushInline lua = \case Cite citations lst -> pushViaConstructor lua "Cite" lst citations - Code attr lst -> pushViaConstructor lua "Code" lst attr + Code attr lst -> pushViaConstructor lua "Code" lst (LuaAttr attr) Emph inlns -> pushViaConstructor lua "Emph" inlns - Image attr alt (src,tit) -> pushViaConstructor lua "Image" alt src tit attr + Image attr alt (src,tit) -> pushViaConstructor lua "Image" alt src tit (LuaAttr attr) LineBreak -> pushViaConstructor lua "LineBreak" - Link attr lst (src,tit) -> pushViaConstructor lua "Link" lst src tit attr + Link attr lst (src,tit) -> pushViaConstructor lua "Link" lst src tit (LuaAttr attr) Note blcks -> pushViaConstructor lua "Note" blcks Math mty str -> pushViaConstructor lua "Math" mty str Quoted qt inlns -> pushViaConstructor lua "Quoted" qt inlns @@ -230,7 +230,7 @@ pushInline lua = \case SmallCaps inlns -> pushViaConstructor lua "SmallCaps" inlns SoftBreak -> pushViaConstructor lua "SoftBreak" Space -> pushViaConstructor lua "Space" - Span attr inlns -> pushViaConstructor lua "Span" inlns attr + Span attr inlns -> pushViaConstructor lua "Span" inlns (LuaAttr attr) Str str -> pushViaConstructor lua "Str" str Strikeout inlns -> pushViaConstructor lua "Strikeout" inlns Strong inlns -> pushViaConstructor lua "Strong" inlns @@ -245,11 +245,11 @@ peekInline lua idx = do Nothing -> return Nothing Just t -> case t of "Cite" -> fmap (uncurry Cite) <$> elementContent - "Code" -> fmap (uncurry Code) <$> elementContent + "Code" -> fmap (withAttr Code) <$> elementContent "Emph" -> fmap Emph <$> elementContent - "Image" -> fmap (\(attr, lst, tgt) -> Image attr lst tgt) + "Image" -> fmap (\(LuaAttr attr, lst, tgt) -> Image attr lst tgt) <$> elementContent - "Link" -> fmap (\(attr, lst, tgt) -> Link attr lst tgt) + "Link" -> fmap (\(LuaAttr attr, lst, tgt) -> Link attr lst tgt) <$> elementContent "LineBreak" -> return (Just LineBreak) "Note" -> fmap Note <$> elementContent @@ -259,7 +259,7 @@ peekInline lua idx = do "SmallCaps" -> fmap SmallCaps <$> elementContent "SoftBreak" -> return (Just SoftBreak) "Space" -> return (Just Space) - "Span" -> fmap (uncurry Span) <$> elementContent + "Span" -> fmap (withAttr Span) <$> elementContent "Str" -> fmap Str <$> elementContent "Strikeout" -> fmap Strikeout <$> elementContent "Strong" -> fmap Strong <$> elementContent @@ -270,3 +270,15 @@ peekInline lua idx = do -- Get the contents of an AST element. elementContent :: StackValue a => IO (Maybe a) elementContent = getTable lua idx "c" + +withAttr :: (Attr -> a -> b) -> (LuaAttr, a) -> b +withAttr f (attributes, x) = f (fromLuaAttr attributes) x + +-- | Wrapper for Attr +newtype LuaAttr = LuaAttr { fromLuaAttr :: Attr } + +instance StackValue LuaAttr where + push lua (LuaAttr (id', classes, kv)) = + pushViaConstructor lua "Attributes" kv id' classes + peek lua idx = fmap LuaAttr <$> peek lua idx + valuetype _ = TTABLE -- cgit v1.2.3 From dd4110fb09aa676cb03ed9cec52b21f9e7e46a3f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 13 Apr 2017 16:49:44 +0200 Subject: Revised error message for pandoc -t pdf. --- src/Text/Pandoc/App.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 107ca435f..619c692a0 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -178,10 +178,10 @@ convertWithOpts opts = do Left e -> E.throwIO $ PandocAppError 9 $ if format == "pdf" then e ++ - "\nTo create a pdf with pandoc, use " ++ - "the latex or beamer writer and specify\n" ++ - "an output file with .pdf extension " ++ - "(pandoc -t latex -o filename.pdf)." + "\nTo create a pdf using pandoc, use " ++ + "-t latex|beamer|context|ms|html5" ++ + "\nand specify an output file with " ++ + ".pdf extension (-o filename.pdf)." ++ else e Right w -> return (w :: Writer PandocIO) -- cgit v1.2.3 From d5701e625ceff494f4a54aa82490fe8e82dd1672 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 13 Apr 2017 17:02:30 +0200 Subject: Text.Pandoc.Error: added new constructors. - PandocSyntaxMapError String - PandocFailOnWarningError - PandocPDFProgramNotFoundError String --- src/Text/Pandoc/App.hs | 15 ++++++--------- src/Text/Pandoc/Error.hs | 7 +++++++ 2 files changed, 13 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 619c692a0..e44c6ebfb 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -181,7 +181,7 @@ convertWithOpts opts = do "\nTo create a pdf using pandoc, use " ++ "-t latex|beamer|context|ms|html5" ++ "\nand specify an output file with " ++ - ".pdf extension (-o filename.pdf)." ++ + ".pdf extension (-o filename.pdf)." else e Right w -> return (w :: Writer PandocIO) @@ -304,7 +304,7 @@ convertWithOpts opts = do let addSyntaxMap existingmap f = do res <- parseSyntaxDefinition f case res of - Left errstr -> E.throwIO $ PandocAppError 67 errstr + Left errstr -> E.throwIO $ PandocSyntaxMapError errstr Right syn -> return $ addSyntaxDefinition syn existingmap syntaxMap <- foldM addSyntaxMap defaultSyntaxMap @@ -312,7 +312,7 @@ convertWithOpts opts = do case missingIncludes (M.elems syntaxMap) of [] -> return () - xs -> E.throwIO $ PandocAppError 73 $ + xs -> E.throwIO $ PandocSyntaxMapError $ "Missing syntax definitions:\n" ++ unlines (map (\(syn,dep) -> (T.unpack syn ++ " requires " ++ @@ -388,8 +388,7 @@ convertWithOpts opts = do Just logfile -> B.writeFile logfile (encodeLogMessages reports) let isWarning msg = messageVerbosity msg == WARNING when (optFailIfWarnings opts && any isWarning reports) $ - E.throwIO $ - PandocAppError 3 "Failing because there were warnings." + E.throwIO PandocFailOnWarningError return res let sourceToDoc :: [FilePath] -> PandocIO (Pandoc, MediaBag) @@ -443,10 +442,8 @@ convertWithOpts opts = do | otherwise -> optLaTeXEngine opts -- check for pdf creating program mbPdfProg <- liftIO $ findExecutable pdfprog - when (isNothing mbPdfProg) $ - liftIO $ E.throwIO $ PandocAppError 41 $ - pdfprog ++ " not found. " ++ - pdfprog ++ " is needed for pdf output." + when (isNothing mbPdfProg) $ liftIO $ E.throwIO $ + PandocPDFProgramNotFoundError pdfprog res <- makePDF pdfprog f writerOptions verbosity media doc' case res of diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs index 454ad9982..36e9cca63 100644 --- a/src/Text/Pandoc/Error.hs +++ b/src/Text/Pandoc/Error.hs @@ -52,6 +52,9 @@ data PandocError = PandocIOError String IOError | PandocParsecError Input ParseError | PandocMakePDFError String | PandocOptionError String + | PandocSyntaxMapError String + | PandocFailOnWarningError + | PandocPDFProgramNotFoundError String | PandocAppError Int String deriving (Show, Typeable, Generic) @@ -79,6 +82,10 @@ handleError (Left e) = in err 65 $ "\nError at " ++ show err' ++ errorInFile PandocMakePDFError s -> err 65 s PandocOptionError s -> err 2 s + PandocSyntaxMapError s -> err 67 s + PandocFailOnWarningError -> err 3 "Failing because there were warnings." + PandocPDFProgramNotFoundError pdfprog -> err 47 $ + pdfprog ++ " not found. " ++ pdfprog ++ " is needed for pdf output." PandocAppError ec s -> err ec s err :: Int -> String -> IO a -- cgit v1.2.3 From 306dc624d982663e07d91bef6d2f84d311b978af Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 13 Apr 2017 17:38:42 +0200 Subject: Error: Added PandocPDFError --- src/Text/Pandoc/App.hs | 2 +- src/Text/Pandoc/Error.hs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index e44c6ebfb..b34980c71 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -451,7 +451,7 @@ convertWithOpts opts = do Left err' -> liftIO $ do B.hPutStr stderr err' B.hPut stderr $ B.pack [10] - E.throwIO $ PandocAppError 43 "Error producing PDF" + E.throwIO $ PandocPDFError (UTF8.toStringLazy err') | otherwise -> do let htmlFormat = format `elem` ["html","html4","html5","s5","slidy","slideous","dzslides","revealjs"] diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs index 36e9cca63..b6782036f 100644 --- a/src/Text/Pandoc/Error.hs +++ b/src/Text/Pandoc/Error.hs @@ -55,6 +55,7 @@ data PandocError = PandocIOError String IOError | PandocSyntaxMapError String | PandocFailOnWarningError | PandocPDFProgramNotFoundError String + | PandocPDFError String | PandocAppError Int String deriving (Show, Typeable, Generic) @@ -86,6 +87,7 @@ handleError (Left e) = PandocFailOnWarningError -> err 3 "Failing because there were warnings." PandocPDFProgramNotFoundError pdfprog -> err 47 $ pdfprog ++ " not found. " ++ pdfprog ++ " is needed for pdf output." + PandocPDFError log -> err 43 $ "Error producing PDF.\n" ++ log PandocAppError ec s -> err ec s err :: Int -> String -> IO a -- cgit v1.2.3 From d722d93b6118bfed4e0a176a66f859b9636ae689 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 13 Apr 2017 19:24:50 +0200 Subject: Error: Added PandocFilterError. --- src/Text/Pandoc/App.hs | 12 ++++-------- src/Text/Pandoc/Error.hs | 3 +++ 2 files changed, 7 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index b34980c71..77e13a297 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -493,21 +493,17 @@ externalFilter f args' d = liftIO $ do unless (exists && isExecutable) $ do mbExe <- findExecutable f' when (isNothing mbExe) $ - E.throwIO $ PandocAppError 83 $ - "Error running filter " ++ f ++ ":\n" ++ - "Could not find executable '" ++ f' ++ "'." + E.throwIO $ PandocFilterError f ("Could not find executable " ++ f') env <- getEnvironment let env' = Just $ ("PANDOC_VERSION", pandocVersion) : env (exitcode, outbs) <- E.handle filterException $ pipeProcess env' f' args'' $ encode d case exitcode of ExitSuccess -> return $ either error id $ eitherDecode' outbs - ExitFailure ec -> E.throwIO $ PandocAppError 83 $ - "Error running filter " ++ f ++ "\n" ++ - "Filter returned error status " ++ show ec + ExitFailure ec -> E.throwIO $ PandocFilterError f + ("Filter returned error status " ++ show ec) where filterException :: E.SomeException -> IO a - filterException e = E.throwIO $ PandocAppError 83 $ - "Error running filter " ++ f ++ "\n" ++ show e + filterException e = E.throwIO $ PandocFilterError f (show e) -- | Data structure for command line options. data Opt = Opt diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs index b6782036f..4ead6aba8 100644 --- a/src/Text/Pandoc/Error.hs +++ b/src/Text/Pandoc/Error.hs @@ -56,6 +56,7 @@ data PandocError = PandocIOError String IOError | PandocFailOnWarningError | PandocPDFProgramNotFoundError String | PandocPDFError String + | PandocFilterError String String | PandocAppError Int String deriving (Show, Typeable, Generic) @@ -88,6 +89,8 @@ handleError (Left e) = PandocPDFProgramNotFoundError pdfprog -> err 47 $ pdfprog ++ " not found. " ++ pdfprog ++ " is needed for pdf output." PandocPDFError log -> err 43 $ "Error producing PDF.\n" ++ log + PandocFilterError filter msg -> err 83 $ "Error running filter " ++ + filter ++ ":\n" ++ msg PandocAppError ec s -> err ec s err :: Int -> String -> IO a -- cgit v1.2.3 From 35e9da28b8698890def63de74e34230afe442211 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 15 Apr 2017 11:52:51 +0200 Subject: App: use PandocOptionError instead of PandocAppError where appropriate. --- src/Text/Pandoc/App.hs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 77e13a297..f5194b42d 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -805,13 +805,13 @@ lookupHighlightStyle (Just s) | takeExtension s == ".theme" = -- attempt to load KDE theme do contents <- B.readFile s case parseTheme contents of - Left _ -> E.throwIO $ PandocAppError 69 $ + Left _ -> E.throwIO $ PandocOptionError $ "Could not read highlighting theme " ++ s Right sty -> return (Just sty) | otherwise = case lookup (map toLower s) highlightingStyles of Just sty -> return (Just sty) - Nothing -> E.throwIO $ PandocAppError 68 $ + Nothing -> E.throwIO $ PandocOptionError $ "Unknown highlight-style " ++ s -- | A list of functions, each transforming the options data structure @@ -848,7 +848,7 @@ options = case safeRead arg of Just t | t > 0 && t < 6 -> return opt{ optBaseHeaderLevel = t } - _ -> E.throwIO $ PandocAppError 19 + _ -> E.throwIO $ PandocOptionError "base-header-level must be 1-5") "NUMBER") "" -- "Headers base level" @@ -882,7 +882,7 @@ options = (\arg opt -> case safeRead arg of Just t | t > 0 -> return opt { optTabStop = t } - _ -> E.throwIO $ PandocAppError 31 + _ -> E.throwIO $ PandocOptionError "tab-stop must be a number greater than 0") "NUMBER") "" -- "Tab stop (default 4)" @@ -894,7 +894,7 @@ options = "accept" -> return AcceptChanges "reject" -> return RejectChanges "all" -> return AllChanges - _ -> E.throwIO $ PandocAppError 6 + _ -> E.throwIO $ PandocOptionError ("Unknown option for track-changes: " ++ arg) return opt { optTrackChanges = action }) "accept|reject|all") @@ -965,7 +965,7 @@ options = (\arg opt -> case safeRead arg of Just t | t > 0 -> return opt { optDpi = t } - _ -> E.throwIO $ PandocAppError 31 + _ -> E.throwIO $ PandocOptionError "dpi must be a number greater than 0") "NUMBER") "" -- "Dpi (default 96)" @@ -975,7 +975,7 @@ options = (\arg opt -> case safeRead ("Wrap" ++ uppercaseFirstLetter arg) of Just o -> return opt { optWrapText = o } - Nothing -> E.throwIO $ PandocAppError 77 + Nothing -> E.throwIO $ PandocOptionError "--wrap must be auto, none, or preserve") "auto|none|preserve") "" -- "Option for wrapping text in output" @@ -985,7 +985,7 @@ options = (\arg opt -> case safeRead arg of Just t | t > 0 -> return opt { optColumns = t } - _ -> E.throwIO $ PandocAppError 33 + _ -> E.throwIO $ PandocOptionError "columns must be a number greater than 0") "NUMBER") "" -- "Length of line in characters" @@ -1001,7 +1001,7 @@ options = case safeRead arg of Just t | t >= 1 && t <= 6 -> return opt { optTOCDepth = t } - _ -> E.throwIO $ PandocAppError 57 + _ -> E.throwIO $ PandocOptionError "TOC level must be a number between 1 and 6") "NUMBER") "" -- "Number of levels to include in TOC" @@ -1077,7 +1077,7 @@ options = "block" -> return EndOfBlock "section" -> return EndOfSection "document" -> return EndOfDocument - _ -> E.throwIO $ PandocAppError 6 + _ -> E.throwIO $ PandocOptionError ("Unknown option for reference-location: " ++ arg) return opt { optReferenceLocation = action }) "block|section|document") @@ -1094,7 +1094,7 @@ options = let tldName = "TopLevel" ++ uppercaseFirstLetter arg case safeRead tldName of Just tlDiv -> return opt { optTopLevelDivision = tlDiv } - _ -> E.throwIO $ PandocAppError 76 + _ -> E.throwIO $ PandocOptionError ("Top-level division must be " ++ "section, chapter, part, or default")) "section|chapter|part") @@ -1111,7 +1111,7 @@ options = case safeRead ('[':arg ++ "]") of Just ns -> return opt { optNumberOffset = ns, optNumberSections = True } - _ -> E.throwIO $ PandocAppError 57 + _ -> E.throwIO $ PandocOptionError "could not parse number-offset") "NUMBERS") "" -- "Starting number for sections, subsections, etc." @@ -1132,7 +1132,7 @@ options = case safeRead arg of Just t | t >= 1 && t <= 6 -> return opt { optSlideLevel = Just t } - _ -> E.throwIO $ PandocAppError 39 + _ -> E.throwIO $ PandocOptionError "slide level must be a number between 1 and 6") "NUMBER") "" -- "Force header level for slides" @@ -1155,7 +1155,7 @@ options = "references" -> return ReferenceObfuscation "javascript" -> return JavascriptObfuscation "none" -> return NoObfuscation - _ -> E.throwIO $ PandocAppError 6 + _ -> E.throwIO $ PandocOptionError ("Unknown obfuscation method: " ++ arg) return opt { optEmailObfuscation = method }) "none|javascript|references") @@ -1217,7 +1217,7 @@ options = case safeRead arg of Just t | t >= 1 && t <= 6 -> return opt { optEpubChapterLevel = t } - _ -> E.throwIO $ PandocAppError 59 + _ -> E.throwIO $ PandocOptionError "chapter level must be a number between 1 and 6") "NUMBER") "" -- "Header level at which to split chapters in EPUB" @@ -1228,7 +1228,7 @@ options = let b = takeBaseName arg if b `elem` ["pdflatex", "lualatex", "xelatex"] then return opt { optLaTeXEngine = arg } - else E.throwIO $ PandocAppError 45 "latex-engine must be pdflatex, lualatex, or xelatex.") + else E.throwIO $ PandocOptionError "latex-engine must be pdflatex, lualatex, or xelatex.") "PROGRAM") "" -- "Name of latex program to use in generating PDF" -- cgit v1.2.3 From 8e0032940fc385844674bc482efde5e24aeed28f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 15 Apr 2017 11:53:14 +0200 Subject: Docx reader: removed readDocxWithWarnings (API change). No longer necessary with pandoc 2.0 framework. --- src/Text/Pandoc/Readers/Docx.hs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index a5efdae57..683277993 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -71,8 +71,7 @@ implemented, [-] means partially implemented): -} module Text.Pandoc.Readers.Docx - ( readDocxWithWarnings - , readDocx + ( readDocx ) where import Codec.Archive.Zip @@ -117,13 +116,6 @@ readDocx opts bytes readDocx _ _ = throwError $ PandocSomeError "couldn't parse docx file" --- TODO remove this for 2.0: -readDocxWithWarnings :: PandocMonad m - => ReaderOptions - -> B.ByteString - -> m Pandoc -readDocxWithWarnings = readDocx - data DState = DState { docxAnchorMap :: M.Map String String , docxMediaBag :: MediaBag , docxDropCap :: Inlines -- cgit v1.2.3 From 76b051f7b44f0df850aafe2f57a44f0486b8b282 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 15 Apr 2017 11:59:48 +0200 Subject: Fixed shadowing warnings --- src/Text/Pandoc/Error.hs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs index 4ead6aba8..637db6187 100644 --- a/src/Text/Pandoc/Error.hs +++ b/src/Text/Pandoc/Error.hs @@ -57,7 +57,7 @@ data PandocError = PandocIOError String IOError | PandocPDFProgramNotFoundError String | PandocPDFError String | PandocFilterError String String - | PandocAppError Int String + | PandocAppError String deriving (Show, Typeable, Generic) instance Exception PandocError @@ -88,10 +88,10 @@ handleError (Left e) = PandocFailOnWarningError -> err 3 "Failing because there were warnings." PandocPDFProgramNotFoundError pdfprog -> err 47 $ pdfprog ++ " not found. " ++ pdfprog ++ " is needed for pdf output." - PandocPDFError log -> err 43 $ "Error producing PDF.\n" ++ log - PandocFilterError filter msg -> err 83 $ "Error running filter " ++ - filter ++ ":\n" ++ msg - PandocAppError ec s -> err ec s + PandocPDFError logmsg -> err 43 $ "Error producing PDF.\n" ++ logmsg + PandocFilterError filtername msg -> err 83 $ "Error running filter " ++ + filtername ++ ":\n" ++ msg + PandocAppError s -> err 1 s err :: Int -> String -> IO a err exitCode msg = do -- cgit v1.2.3 From 1fe1c162ac1891fc688e1aef207dd60e18672c06 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 15 Apr 2017 12:05:58 +0200 Subject: Error: Added PandocCouldNotFindDataFileError. Use this instead of PandocAppError when appropriate. Removed exit code from PandocAppError, use 1 for all. --- src/Text/Pandoc/App.hs | 8 ++++---- src/Text/Pandoc/Error.hs | 3 +++ src/Text/Pandoc/Shared.hs | 5 ++--- 3 files changed, 9 insertions(+), 7 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index f5194b42d..4b14a9d73 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -175,7 +175,7 @@ convertWithOpts opts = do (\o d -> liftIO $ writeCustom writerName o d) :: Writer PandocIO) else case getWriter writerName of - Left e -> E.throwIO $ PandocAppError 9 $ + Left e -> E.throwIO $ PandocAppError $ if format == "pdf" then e ++ "\nTo create a pdf using pandoc, use " ++ @@ -189,7 +189,7 @@ convertWithOpts opts = do -- the sake of the text2tags reader. reader <- case getReader readerName of Right r -> return (r :: Reader PandocIO) - Left e -> E.throwIO $ PandocAppError 7 e' + Left e -> E.throwIO $ PandocAppError e' where e' = case readerName of "pdf" -> e ++ "\nPandoc can convert to PDF, but not from PDF." @@ -359,7 +359,7 @@ convertWithOpts opts = do istty <- queryTerminal stdOutput #endif when (istty && not (isTextFormat format) && outputFile == "-") $ - E.throwIO $ PandocAppError 5 $ + E.throwIO $ PandocAppError $ "Cannot write " ++ format ++ " output to stdout.\n" ++ "Specify an output file using the -o option." @@ -431,7 +431,7 @@ convertWithOpts opts = do -- make sure writer is latex, beamer, context, html5 or ms unless (laTeXOutput || conTeXtOutput || html5Output || msOutput) $ - liftIO $ E.throwIO $ PandocAppError 47 $ + liftIO $ E.throwIO $ PandocAppError $ "cannot produce pdf output with " ++ format ++ " writer" let pdfprog = case () of diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs index 637db6187..135cb3945 100644 --- a/src/Text/Pandoc/Error.hs +++ b/src/Text/Pandoc/Error.hs @@ -57,6 +57,7 @@ data PandocError = PandocIOError String IOError | PandocPDFProgramNotFoundError String | PandocPDFError String | PandocFilterError String String + | PandocCouldNotFindDataFileError String | PandocAppError String deriving (Show, Typeable, Generic) @@ -91,6 +92,8 @@ handleError (Left e) = PandocPDFError logmsg -> err 43 $ "Error producing PDF.\n" ++ logmsg PandocFilterError filtername msg -> err 83 $ "Error running filter " ++ filtername ++ ":\n" ++ msg + PandocCouldNotFindDataFileError fn -> err 97 $ + "Could not find data file " ++ fn PandocAppError s -> err 1 s err :: Int -> String -> IO a diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index dfdbaf428..8256d14c0 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -674,8 +674,7 @@ readDefaultDataFile "reference.odt" = readDefaultDataFile fname = #ifdef EMBED_DATA_FILES case lookup (makeCanonical fname) dataFiles of - Nothing -> E.throwIO $ PandocAppError 97 $ - "Could not find data file " ++ fname + Nothing -> E.throwIO $ PandocCouldNotFindDataFileError fname Just contents -> return contents where makeCanonical = Posix.joinPath . transformPathParts . splitDirectories transformPathParts = reverse . foldl go [] @@ -691,7 +690,7 @@ checkExistence fn = do exists <- doesFileExist fn if exists then return fn - else E.throwIO $ PandocAppError 97 ("Could not find data file " ++ fn) + else E.throwIO $ PandocCouldNotFindDataFileError fn #endif -- | Read file from specified user data directory or, if not found there, from -- cgit v1.2.3 From bcc848d773f9b0f968e7ecb69739adea0432045e Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 15 Apr 2017 13:32:28 +0200 Subject: Avoid parsing "Notes:**" as a bare URI. This avoids parsing bare URIs that start with a scheme + colon + `*`, `_`, or `]`. Closes #3570. --- src/Text/Pandoc/Parsing.hs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index a84535875..e985f3d32 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -463,6 +463,8 @@ uri :: Stream [Char] m Char => ParserT [Char] st m (String, String) uri = try $ do scheme <- uriScheme char ':' + -- Avoid parsing e.g. "**Notes:**" as a raw URI: + notFollowedBy (oneOf "*_]") -- We allow sentence punctuation except at the end, since -- we don't want the trailing '.' in 'http://google.com.' We want to allow -- http://en.wikipedia.org/wiki/State_of_emergency_(disambiguation) -- cgit v1.2.3 From 629c6494a599a6a2923798e8a83c7f5ab8de086e Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 15 Apr 2017 13:46:46 +0200 Subject: Small fix to error reporting in App. Closes #3548. --- src/Text/Pandoc/App.hs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 4b14a9d73..c38ebdd84 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -64,7 +64,7 @@ import System.Directory (Permissions (..), doesFileExist, findExecutable, import System.Environment (getArgs, getEnvironment, getProgName) import System.Exit (ExitCode (..), exitSuccess) import System.FilePath -import System.IO (stderr, stdout) +import System.IO (stdout) import System.IO.Error (isDoesNotExistError) import Text.Pandoc import Text.Pandoc.Builder (setMeta) @@ -448,9 +448,7 @@ convertWithOpts opts = do res <- makePDF pdfprog f writerOptions verbosity media doc' case res of Right pdf -> writeFnBinary outputFile pdf - Left err' -> liftIO $ do - B.hPutStr stderr err' - B.hPut stderr $ B.pack [10] + Left err' -> liftIO $ E.throwIO $ PandocPDFError (UTF8.toStringLazy err') | otherwise -> do let htmlFormat = format `elem` -- cgit v1.2.3 From e6a536befcfd433aba66a3085e62792383867695 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sat, 15 Apr 2017 21:40:48 +0200 Subject: Lua filter: revert to non-destructuring filters We want to provide an interface familiar to users of other filtering libraries. --- src/Text/Pandoc/Lua.hs | 112 ++++++++++++++++++++----------------------------- 1 file changed, 46 insertions(+), 66 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index a89da52bc..f4cd4e0d6 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -99,84 +99,64 @@ execBlockLuaFilter :: LuaState -> Map String (LuaFilterFunction Block) -> Block -> IO Block execBlockLuaFilter lua fnMap x = do - let runFn :: PushViaFilterFunction Block a => LuaFilterFunction Block -> a - runFn fn = runLuaFilterFunction lua fn - let tryFilter :: String -> (LuaFilterFunction Block -> IO Block) -> IO Block - tryFilter fnName callFilterFn = - case Map.lookup fnName fnMap of + let tryFilter :: String -> IO Block + tryFilter filterFnName = + case Map.lookup filterFnName fnMap of Nothing -> return x - Just fn -> callFilterFn fn + Just fn -> runLuaFilterFunction lua fn x case x of - HorizontalRule -> tryFilter "HorizontalRule" runFn - Null -> tryFilter "Null" runFn - BlockQuote blcks -> tryFilter "BlockQuote" $ \fn -> runFn fn blcks - BulletList items -> tryFilter "BulletList" $ \fn -> runFn fn items - CodeBlock attr code -> tryFilter "CodeBlock" $ \fn -> runFn fn attr code - DefinitionList lst -> tryFilter "DefinitionList" $ \fn -> runFn fn lst - Div attr content -> tryFilter "Div" $ \fn -> runFn fn content attr - Header lvl attr inlns -> tryFilter "Header" $ \fn -> runFn fn lvl inlns attr - LineBlock inlns -> tryFilter "LineBlock" $ \fn -> runFn fn inlns - Para inlns -> tryFilter "Para" $ \fn -> runFn fn inlns - Plain inlns -> tryFilter "Plain" $ \fn -> runFn fn inlns - RawBlock format str -> tryFilter "RawBlock" $ \fn -> runFn fn format str - OrderedList (num,sty,delim) items -> - tryFilter "OrderedList" $ \fn -> runFn fn items (num,sty,delim) - Table capt aligns widths headers rows -> - tryFilter "Table" $ \fn -> runFn fn capt aligns widths headers rows + BlockQuote _ -> tryFilter "BlockQuote" + BulletList _ -> tryFilter "BulletList" + CodeBlock _ _ -> tryFilter "CodeBlock" + DefinitionList _ -> tryFilter "DefinitionList" + Div _ _ -> tryFilter "Div" + Header _ _ _ -> tryFilter "Header" + HorizontalRule -> tryFilter "HorizontalRule" + LineBlock _ -> tryFilter "LineBlock" + Null -> tryFilter "Null" + Para _ -> tryFilter "Para" + Plain _ -> tryFilter "Plain" + RawBlock _ _ -> tryFilter "RawBlock" + OrderedList _ _ -> tryFilter "OrderedList" + Table _ _ _ _ _ -> tryFilter "Table" execInlineLuaFilter :: LuaState -> Map String (LuaFilterFunction Inline) -> Inline -> IO Inline execInlineLuaFilter lua fnMap x = do - let runFn :: PushViaFilterFunction Inline a => LuaFilterFunction Inline -> a - runFn fn = runLuaFilterFunction lua fn - let tryFilter :: String -> (LuaFilterFunction Inline -> IO Inline) -> IO Inline - tryFilter fnName callFilterFn = - case Map.lookup fnName fnMap of + let tryFilter :: String -> IO Inline + tryFilter filterFnName = + case Map.lookup filterFnName fnMap of Nothing -> return x - Just fn -> callFilterFn fn - let tryFilterAlternatives :: [(String, LuaFilterFunction Inline -> IO Inline)] -> IO Inline + Just fn -> runLuaFilterFunction lua fn x + let tryFilterAlternatives :: [String] -> IO Inline tryFilterAlternatives [] = return x - tryFilterAlternatives ((fnName, callFilterFn) : alternatives) = + tryFilterAlternatives (fnName : alternatives) = case Map.lookup fnName fnMap of Nothing -> tryFilterAlternatives alternatives - Just fn -> callFilterFn fn + Just fn -> runLuaFilterFunction lua fn x case x of - LineBreak -> tryFilter "LineBreak" runFn - SoftBreak -> tryFilter "SoftBreak" runFn - Space -> tryFilter "Space" runFn - Cite cs lst -> tryFilter "Cite" $ \fn -> runFn fn lst cs - Code attr str -> tryFilter "Code" $ \fn -> runFn fn str attr - Emph lst -> tryFilter "Emph" $ \fn -> runFn fn lst - Note blks -> tryFilter "Note" $ \fn -> runFn fn blks - RawInline f str -> tryFilter "RawInline" $ \fn -> runFn fn f str - SmallCaps lst -> tryFilter "SmallCaps" $ \fn -> runFn fn lst - Span attr lst -> tryFilter "Span" $ \fn -> runFn fn lst attr - Str str -> tryFilter "Str" $ \fn -> runFn fn str - Strikeout lst -> tryFilter "Strikeout" $ \fn -> runFn fn lst - Strong lst -> tryFilter "Strong" $ \fn -> runFn fn lst - Subscript lst -> tryFilter "Subscript" $ \fn -> runFn fn lst - Superscript lst -> tryFilter "Superscript" $ \fn -> runFn fn lst - Math DisplayMath lst -> tryFilterAlternatives - [ ("DisplayMath", \fn -> runFn fn lst) - , ("Math", \fn -> runFn fn DisplayMath lst) - ] - Math InlineMath lst -> tryFilterAlternatives - [ ("InlineMath", \fn -> runFn fn lst) - , ("Math", \fn -> runFn fn InlineMath lst) - ] - Quoted SingleQuote lst -> tryFilterAlternatives - [ ("SingleQuoted", \fn -> runFn fn lst) - , ("Quoted", \fn -> runFn fn SingleQuote lst) - ] - Quoted DoubleQuote lst -> tryFilterAlternatives - [ ("DoubleQuoted", \fn -> runFn fn lst) - , ("Quoted", \fn -> runFn fn DoubleQuote lst) - ] - Link attr txt (src, tit) -> tryFilter "Link" $ - \fn -> runFn fn txt src tit attr - Image attr alt (src, tit) -> tryFilter "Image" $ - \fn -> runFn fn alt src tit attr + Cite _ _ -> tryFilter "Cite" + Code _ _ -> tryFilter "Code" + Emph _ -> tryFilter "Emph" + Image _ _ _ -> tryFilter "Image" + LineBreak -> tryFilter "LineBreak" + Link _ _ _ -> tryFilter "Link" + Math DisplayMath _ -> tryFilterAlternatives ["DisplayMath", "Math"] + Math InlineMath _ -> tryFilterAlternatives ["InlineMath", "Math"] + Note _ -> tryFilter "Note" + Quoted DoubleQuote _ -> tryFilterAlternatives ["DoubleQuoted", "Quoted"] + Quoted SingleQuote _ -> tryFilterAlternatives ["SingleQuoted", "Quoted"] + RawInline _ _ -> tryFilter "RawInline" + SmallCaps _ -> tryFilter "SmallCaps" + SoftBreak -> tryFilter "SoftBreak" + Space -> tryFilter "Space" + Span _ _ -> tryFilter "Span" + Str _ -> tryFilter "Str" + Strikeout _ -> tryFilter "Strikeout" + Strong _ -> tryFilter "Strong" + Subscript _ -> tryFilter "Subscript" + Superscript _ -> tryFilter "Superscript" instance StackValue LuaFilter where valuetype _ = Lua.TTABLE -- cgit v1.2.3 From 56dddcc3f556217a3354aff2e887b0ba4714f369 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sun, 16 Apr 2017 17:22:47 +0200 Subject: Org reader: convert markup at beginning of footnotes Closes: #3576 --- src/Text/Pandoc/Readers/Org/Blocks.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index 9ef714da7..0753b5deb 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -869,7 +869,7 @@ latexEnd envName = try $ -- noteBlock :: PandocMonad m => OrgParser m (F Blocks) noteBlock = try $ do - ref <- noteMarker <* skipSpaces + ref <- noteMarker <* skipSpaces <* updateLastPreCharPos content <- mconcat <$> blocksTillHeaderOrNote addToNotesTable (ref, content) return mempty -- cgit v1.2.3 From 1c8683f205c6e0b028dd62a13936d9a9f6124270 Mon Sep 17 00:00:00 2001 From: schrieveslaach <schrieveslaach@online.de> Date: Sun, 16 Apr 2017 19:39:24 +0200 Subject: Read image size of Inkscape SVGs (#3580) --- src/Text/Pandoc/ImageSize.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/ImageSize.hs b/src/Text/Pandoc/ImageSize.hs index 5cede7083..8b2d577a9 100644 --- a/src/Text/Pandoc/ImageSize.hs +++ b/src/Text/Pandoc/ImageSize.hs @@ -108,15 +108,15 @@ imageType img = case B.take 4 img of "%PDF" -> return Pdf "<svg" -> return Svg "<?xm" - | "<svg " == (B.take 5 $ last $ B.groupBy openingTag $ B.drop 7 img) + | findSvgTag img -> return Svg "%!PS" | (B.take 4 $ B.drop 1 $ B.dropWhile (/=' ') img) == "EPSF" -> return Eps _ -> mzero - where - -- B.groupBy openingTag matches first "<svg" or "<html" but not "<!--" - openingTag x y = x == '<' && y /= '!' + +findSvgTag :: ByteString -> Bool +findSvgTag img = B.null $ snd (B.breakSubstring img "<svg") imageSize :: WriterOptions -> ByteString -> Either String ImageSize imageSize opts img = -- cgit v1.2.3 From 464db59394e5ff0e366e1dab40c3edbf7ea32dac Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sun, 16 Apr 2017 21:19:35 +0200 Subject: Org reader: allow emphasized text to be followed by `[` Closes: #3577 --- src/Text/Pandoc/Readers/Org/Inlines.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/Inlines.hs b/src/Text/Pandoc/Readers/Org/Inlines.hs index 4e61bc695..31bfe4478 100644 --- a/src/Text/Pandoc/Readers/Org/Inlines.hs +++ b/src/Text/Pandoc/Readers/Org/Inlines.hs @@ -731,7 +731,7 @@ emphasisPreChars = "\t \"'({" -- | Chars allowed at after emphasis emphasisPostChars :: [Char] -emphasisPostChars = "\t\n !\"'),-.:;?\\}" +emphasisPostChars = "\t\n !\"'),-.:;?\\}[" -- | Chars not allowed at the (inner) border of emphasis emphasisForbiddenBorderChars :: [Char] -- cgit v1.2.3 From 57a0759def058df0322152823bca003664b961c5 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Tue, 18 Apr 2017 19:05:52 +0200 Subject: Lua: drop useless filter function type parameter The return-type parameter for lua filter functions is removed. It only complicated the code without introducing any additional type safety. --- src/Text/Pandoc/Lua.hs | 48 ++++++++++++++++++++---------------------------- 1 file changed, 20 insertions(+), 28 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index f4cd4e0d6..0d1c6cf45 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -73,21 +73,18 @@ runAll [] = return runAll (x:xs) = walkMWithLuaFilter x >=> runAll xs walkMWithLuaFilter :: LuaFilter -> Pandoc -> IO Pandoc -walkMWithLuaFilter (LuaFilter lua inlineFnMap blockFnMap docFnMap) = - walkM (execInlineLuaFilter lua inlineFnMap) >=> - walkM (execBlockLuaFilter lua blockFnMap) >=> - walkM (execDocLuaFilter lua docFnMap) +walkMWithLuaFilter (LuaFilter lua fnMap) = + walkM (execInlineLuaFilter lua fnMap) >=> + walkM (execBlockLuaFilter lua fnMap) >=> + walkM (execDocLuaFilter lua fnMap) -type InlineFunctionMap = Map String (LuaFilterFunction Inline) -type BlockFunctionMap = Map String (LuaFilterFunction Block) -type DocFunctionMap = Map String (LuaFilterFunction Pandoc) -data LuaFilter = - LuaFilter LuaState InlineFunctionMap BlockFunctionMap DocFunctionMap +type FunctionMap = Map String LuaFilterFunction +data LuaFilter = LuaFilter LuaState FunctionMap -newtype LuaFilterFunction a = LuaFilterFunction { functionIndex :: Int } +newtype LuaFilterFunction = LuaFilterFunction { functionIndex :: Int } execDocLuaFilter :: LuaState - -> Map String (LuaFilterFunction Pandoc) + -> FunctionMap -> Pandoc -> IO Pandoc execDocLuaFilter lua fnMap x = do let docFnName = "Doc" @@ -96,7 +93,7 @@ execDocLuaFilter lua fnMap x = do Just fn -> runLuaFilterFunction lua fn x execBlockLuaFilter :: LuaState - -> Map String (LuaFilterFunction Block) + -> FunctionMap -> Block -> IO Block execBlockLuaFilter lua fnMap x = do let tryFilter :: String -> IO Block @@ -121,7 +118,7 @@ execBlockLuaFilter lua fnMap x = do Table _ _ _ _ _ -> tryFilter "Table" execInlineLuaFilter :: LuaState - -> Map String (LuaFilterFunction Inline) + -> FunctionMap -> Inline -> IO Inline execInlineLuaFilter lua fnMap x = do let tryFilter :: String -> IO Inline @@ -161,19 +158,14 @@ execInlineLuaFilter lua fnMap x = do instance StackValue LuaFilter where valuetype _ = Lua.TTABLE push = undefined - peek lua i = do - -- TODO: find a more efficient way of doing this in a typesafe manner. - inlineFnMap <- Lua.peek lua i - blockFnMap <- Lua.peek lua i - docFnMap <- Lua.peek lua i - return $ LuaFilter lua <$> inlineFnMap <*> blockFnMap <*> docFnMap + peek lua idx = fmap (LuaFilter lua) <$> Lua.peek lua idx -- | Helper class for pushing a single value to the stack via a lua function. -- See @pushViaCall@. -class PushViaFilterFunction a b where - pushViaFilterFunction' :: LuaState -> LuaFilterFunction a -> IO () -> Int -> b +class PushViaFilterFunction a where + pushViaFilterFunction' :: LuaState -> LuaFilterFunction -> IO () -> Int -> a -instance (StackValue a) => PushViaFilterFunction a (IO a) where +instance StackValue a => PushViaFilterFunction (IO a) where pushViaFilterFunction' lua lf pushArgs num = do pushFilterFunction lua lf pushArgs @@ -184,20 +176,20 @@ instance (StackValue a) => PushViaFilterFunction a (IO a) where ++ "value from lua stack." Just res -> res <$ Lua.pop lua 1 -instance (PushViaFilterFunction a c, StackValue b) => - PushViaFilterFunction a (b -> c) where +instance (StackValue a, PushViaFilterFunction b) => + PushViaFilterFunction (a -> b) where pushViaFilterFunction' lua lf pushArgs num x = pushViaFilterFunction' lua lf (pushArgs *> push lua x) (num + 1) -- | Push an value to the stack via a lua filter function. The function is -- called with all arguments that are passed to this function and is expected to -- return a single value. -runLuaFilterFunction :: (StackValue a, PushViaFilterFunction a b) - => LuaState -> LuaFilterFunction a -> b +runLuaFilterFunction :: PushViaFilterFunction a + => LuaState -> LuaFilterFunction -> a runLuaFilterFunction lua lf = pushViaFilterFunction' lua lf (return ()) 0 -- | Push the filter function to the top of the stack. -pushFilterFunction :: Lua.LuaState -> LuaFilterFunction a -> IO () +pushFilterFunction :: Lua.LuaState -> LuaFilterFunction -> IO () pushFilterFunction lua lf = do -- The function is stored in a lua registry table, retrieve it from there. push lua ("PANDOC_FILTER_FUNCTIONS"::String) @@ -205,7 +197,7 @@ pushFilterFunction lua lf = do Lua.rawgeti lua (-1) (functionIndex lf) Lua.remove lua (-2) -- remove registry table from stack -instance StackValue (LuaFilterFunction a) where +instance StackValue LuaFilterFunction where valuetype _ = Lua.TFUNCTION push lua v = pushFilterFunction lua v peek lua i = do -- cgit v1.2.3 From 020dc63e23226789cb2e3b7957bc70d6e938fab1 Mon Sep 17 00:00:00 2001 From: schrieveslaach <schrieveslaach@online.de> Date: Sat, 22 Apr 2017 21:57:21 +0200 Subject: Add siunitx Support (#3588) For example: ```latex \SI[round-precision=2]{1}{m} is equal to \SI{1000}{mm}. \SI[round-precision=2]{1}[\$]{} is equal to \SI{0.938094}{\euro} ``` --- src/Text/Pandoc/Readers/LaTeX.hs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index e85002ba3..f3c94dacb 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -668,6 +668,8 @@ inlineCommands = M.fromList $ , ("nocite", mempty <$ (citation "nocite" NormalCitation False >>= addMeta "nocite")) , ("hypertarget", braced >> tok) + -- siuntix + , ("SI", dosiunitx) ] ++ map ignoreInlines -- these commands will be ignored unless --parse-raw is specified, -- in which case they will appear as raw latex blocks: @@ -726,6 +728,21 @@ dolstinline = do doLHSverb :: PandocMonad m => LP m Inlines doLHSverb = codeWith ("",["haskell"],[]) <$> manyTill (satisfy (/='\n')) (char '|') +-- converts e.g. \SI{1}[\$]{} to "$ 1" or \SI{1}{\euro} to "1 €" +dosiunitx :: PandocMonad m => LP m Inlines +dosiunitx = do + skipopts + value <- tok + valueprefix <- option "" $ char '[' >> (mconcat <$> manyTill tok (char ']')) + unit <- tok + let emptyOr160 "" = "" + emptyOr160 _ = "\160" + return . mconcat $ [valueprefix, + emptyOr160 valueprefix, + value, + emptyOr160 unit, + unit] + lit :: String -> LP m Inlines lit = pure . str -- cgit v1.2.3 From 51a46b7e31c92c717ca4778df96535b0e492babe Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 23 Apr 2017 10:55:16 +0200 Subject: HTML reader: Revise treatment of li with id attribute. Previously we always added an empty div before the list item, but this created problems with spacing in tight lists. Now we do this: If the list item contents begin with a Plain block, we modify the Plain block by adding a Span around its contents. Otherwise, we add a Div around the contents of the list item (instead of adding an empty Div to the beginning, as before). Closes #3596. --- src/Text/Pandoc/Readers/HTML.hs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 5251962f2..14b051539 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -260,8 +260,12 @@ pBulletList = try $ do 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) - (liDiv <>) <$> pInTags "li" block <* skipMany nonItem + let addId ident bs = case B.toList bs of + (Plain ils:xs) -> B.fromList (Plain + [Span (ident, [], []) ils] : xs) + _ -> B.divWith (ident, [], []) bs + (maybe id addId (lookup "id" attr)) <$> + pInTags "li" block <* skipMany nonItem parseListStyleType :: String -> ListNumberStyle parseListStyleType "lower-roman" = LowerRoman -- cgit v1.2.3 From 04658c491b94ed851c201f0d298e8dd398f81363 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sun, 23 Apr 2017 11:54:36 +0200 Subject: Org reader: handle line numbering switch for src blocks The line-numbering switch that can be given to source blocks (`-n` with an start number as an optional parameter) is parsed and translated to a class/key-value combination used by highlighting and other readers and writers. --- src/Text/Pandoc/Readers/Org/Blocks.hs | 70 ++++++++++++++++++++++++++++------- 1 file changed, 57 insertions(+), 13 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index 0753b5deb..6fc12d84b 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -284,6 +284,11 @@ block = choice [ mempty <$ blanklines ] <?> "block" +-- | Parse a horizontal rule into a block element +horizontalRule :: Monad m => OrgParser m (F Blocks) +horizontalRule = return B.horizontalRule <$ try hline + + -- -- Block Attributes -- @@ -522,28 +527,70 @@ trailingResultsBlock = optionMaybe . try $ do block -- | Parse code block arguments --- TODO: We currently don't handle switches. codeHeaderArgs :: Monad m => OrgParser m ([String], [(String, String)]) codeHeaderArgs = try $ do language <- skipSpaces *> orgArgWord - _ <- skipSpaces *> (try $ switch `sepBy` (many1 spaceChar)) + (switchClasses, switchKv) <- switchesAsAttributes parameters <- manyTill blockOption newline let pandocLang = translateLang language + let classes = pandocLang : switchClasses return $ if hasRundocParameters parameters - then ( [ pandocLang, rundocBlockClass ] - , map toRundocAttrib (("language", language) : parameters) + then ( classes <> [ rundocBlockClass ] + , switchKv <> map toRundocAttrib (("language", language) : parameters) ) - else ([ pandocLang ], parameters) + else (classes, switchKv <> parameters) where hasRundocParameters = not . null -switch :: Monad m => OrgParser m (Char, Maybe String) -switch = try $ simpleSwitch <|> lineNumbersSwitch +switchesAsAttributes :: Monad m => OrgParser m ([String], [(String, String)]) +switchesAsAttributes = try $ do + switches <- skipSpaces *> (try $ switch `sepBy` (many1 spaceChar)) + return $ foldr addToAttr ([], []) switches where - simpleSwitch = (\c -> (c, Nothing)) <$> (oneOf "-+" *> letter) - lineNumbersSwitch = (\ls -> ('l', Just ls)) <$> - (string "-l \"" *> many1Till nonspaceChar (char '"')) + addToAttr :: (Char, Maybe String, SwitchPolarity) + -> ([String], [(String, String)]) + -> ([String], [(String, String)]) + addToAttr ('n', lineNum, pol) (cls, kv) = + let kv' = case lineNum of + Just num -> (("startFrom", num):kv) + Nothing -> kv + cls' = case pol of + SwitchPlus -> "continuedSourceBlock":cls + SwitchMinus -> cls + in ("numberLines":cls', kv') + addToAttr _ x = x + +-- | Whether a switch flag is specified with @+@ or @-@. +data SwitchPolarity = SwitchPlus | SwitchMinus + deriving (Show, Eq) + +-- | Parses a switch's polarity. +switchPolarity :: Monad m => OrgParser m SwitchPolarity +switchPolarity = (SwitchMinus <$ char '-') <|> (SwitchPlus <$ char '+') + +-- | Parses a source block switch option. +switch :: Monad m => OrgParser m (Char, Maybe String, SwitchPolarity) +switch = try $ lineNumberSwitch <|> labelSwitch <|> simpleSwitch + where + simpleSwitch = (\pol c -> (c, Nothing, pol)) <$> switchPolarity <*> letter + labelSwitch = genericSwitch 'l' $ + char '"' *> many1Till nonspaceChar (char '"') + +-- | Generic source block switch-option parser. +genericSwitch :: Monad m + => Char + -> OrgParser m String + -> OrgParser m (Char, Maybe String, SwitchPolarity) +genericSwitch c p = try $ do + polarity <- switchPolarity <* char c <* skipSpaces + arg <- optionMaybe p + return $ (c, arg, polarity) + +-- | Reads a line number switch option. The line number switch can be used with +-- example and source blocks. +lineNumberSwitch :: Monad m => OrgParser m (Char, Maybe String, SwitchPolarity) +lineNumberSwitch = genericSwitch 'n' (many digit) blockOption :: Monad m => OrgParser m (String, String) blockOption = try $ do @@ -558,9 +605,6 @@ orgParamValue = try $ *> many1 nonspaceChar <* skipSpaces -horizontalRule :: Monad m => OrgParser m (F Blocks) -horizontalRule = return B.horizontalRule <$ try hline - -- -- Drawers -- cgit v1.2.3 From 2e43e27e5c6374c0cbc3ad690f04ec95bbac1f91 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sun, 23 Apr 2017 12:56:11 +0200 Subject: Org reader: stop adding rundoc prefix to src params Source block parameter names are no longer prefixed with *rundoc*. This was intended to simplify working with the rundoc project, a babel runner. However, the rundoc project is unmaintained, and adding those markers is not the reader's job anyway. The original language that is specified for a source element is now retained as the `data-org-language` attribute and only added if it differs from the translated language. --- src/Text/Pandoc/Readers/Org/Blocks.hs | 32 ++++++++++---------------------- src/Text/Pandoc/Readers/Org/Inlines.hs | 7 +++---- src/Text/Pandoc/Readers/Org/Shared.hs | 24 +++++++++++------------- 3 files changed, 24 insertions(+), 39 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index 6fc12d84b..3cb9c7ed8 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -39,8 +39,7 @@ import Text.Pandoc.Readers.Org.Meta (metaExport, metaKey, metaLine) import Text.Pandoc.Readers.Org.ParserState import Text.Pandoc.Readers.Org.Parsing import Text.Pandoc.Readers.Org.Shared (cleanLinkString, isImageFilename, - rundocBlockClass, toRundocAttrib, - translateLang) + originalLang, translateLang) import Text.Pandoc.Builder (Blocks, Inlines) import qualified Text.Pandoc.Builder as B @@ -493,16 +492,14 @@ codeBlock blockAttrs blockType = do content <- rawBlockContent blockType resultsContent <- trailingResultsBlock let id' = fromMaybe mempty $ blockAttrName blockAttrs - let includeCode = exportsCode kv - let includeResults = exportsResults kv let codeBlck = B.codeBlockWith ( id', classes, kv ) content let labelledBlck = maybe (pure codeBlck) (labelDiv codeBlck) (blockAttrCaption blockAttrs) let resultBlck = fromMaybe mempty resultsContent return $ - (if includeCode then labelledBlck else mempty) <> - (if includeResults then resultBlck else mempty) + (if exportsCode kv then labelledBlck else mempty) <> + (if exportsResults kv then resultBlck else mempty) where labelDiv :: Blocks -> F Inlines -> F Blocks labelDiv blk value = @@ -511,13 +508,11 @@ codeBlock blockAttrs blockType = do labelledBlock :: F Inlines -> F Blocks labelledBlock = fmap (B.plain . B.spanWith ("", ["label"], [])) -exportsCode :: [(String, String)] -> Bool -exportsCode attrs = not (("rundoc-exports", "none") `elem` attrs - || ("rundoc-exports", "results") `elem` attrs) + exportsCode :: [(String, String)] -> Bool + exportsCode = maybe True (`elem` ["code", "both"]) . lookup "exports" -exportsResults :: [(String, String)] -> Bool -exportsResults attrs = ("rundoc-exports", "results") `elem` attrs - || ("rundoc-exports", "both") `elem` attrs + exportsResults :: [(String, String)] -> Bool + exportsResults = maybe False (`elem` ["results", "both"]) . lookup "exports" trailingResultsBlock :: PandocMonad m => OrgParser m (Maybe (F Blocks)) trailingResultsBlock = optionMaybe . try $ do @@ -532,16 +527,9 @@ codeHeaderArgs = try $ do language <- skipSpaces *> orgArgWord (switchClasses, switchKv) <- switchesAsAttributes parameters <- manyTill blockOption newline - let pandocLang = translateLang language - let classes = pandocLang : switchClasses - return $ - if hasRundocParameters parameters - then ( classes <> [ rundocBlockClass ] - , switchKv <> map toRundocAttrib (("language", language) : parameters) - ) - else (classes, switchKv <> parameters) - where - hasRundocParameters = not . null + return $ ( translateLang language : switchClasses + , originalLang language <> switchKv <> parameters + ) switchesAsAttributes :: Monad m => OrgParser m ([String], [(String, String)]) switchesAsAttributes = try $ do diff --git a/src/Text/Pandoc/Readers/Org/Inlines.hs b/src/Text/Pandoc/Readers/Org/Inlines.hs index 31bfe4478..d227eb66a 100644 --- a/src/Text/Pandoc/Readers/Org/Inlines.hs +++ b/src/Text/Pandoc/Readers/Org/Inlines.hs @@ -37,8 +37,7 @@ import Text.Pandoc.Readers.Org.BlockStarts (endOfBlock, noteMarker) import Text.Pandoc.Readers.Org.ParserState import Text.Pandoc.Readers.Org.Parsing import Text.Pandoc.Readers.Org.Shared (cleanLinkString, isImageFilename, - rundocBlockClass, toRundocAttrib, - translateLang) + originalLang, translateLang) import Text.Pandoc.Builder (Inlines) import qualified Text.Pandoc.Builder as B @@ -518,8 +517,8 @@ inlineCodeBlock = try $ do lang <- many1 orgArgWordChar opts <- option [] $ enclosedByPair '[' ']' inlineBlockOption inlineCode <- enclosedByPair '{' '}' (noneOf "\n\r") - let attrClasses = [translateLang lang, rundocBlockClass] - let attrKeyVal = map toRundocAttrib (("language", lang) : opts) + let attrClasses = [translateLang lang] + let attrKeyVal = originalLang lang <> opts returnF $ B.codeWith ("", attrClasses, attrKeyVal) inlineCode where inlineBlockOption :: PandocMonad m => OrgParser m (String, String) diff --git a/src/Text/Pandoc/Readers/Org/Shared.hs b/src/Text/Pandoc/Readers/Org/Shared.hs index a5b285f30..f89ce6732 100644 --- a/src/Text/Pandoc/Readers/Org/Shared.hs +++ b/src/Text/Pandoc/Readers/Org/Shared.hs @@ -29,12 +29,10 @@ Utility functions used in other Pandoc Org modules. module Text.Pandoc.Readers.Org.Shared ( cleanLinkString , isImageFilename - , rundocBlockClass - , toRundocAttrib + , originalLang , translateLang ) where -import Control.Arrow (first) import Data.Char (isAlphaNum) import Data.List (isPrefixOf, isSuffixOf) @@ -68,17 +66,17 @@ cleanLinkString s = in all (\c -> isAlphaNum c || c `elem` (".-"::String)) scheme && not (null path) --- | Prefix used for Rundoc classes and arguments. -rundocPrefix :: String -rundocPrefix = "rundoc-" +-- | Creates an key-value pair marking the original language name specified for +-- a piece of source code. --- | The class-name used to mark rundoc blocks. -rundocBlockClass :: String -rundocBlockClass = rundocPrefix ++ "block" - --- | Prefix the name of a attribute, marking it as a code execution parameter. -toRundocAttrib :: (String, String) -> (String, String) -toRundocAttrib = first (rundocPrefix ++) +-- | Creates an key-value attributes marking the original language name +-- specified for a piece of source code. +originalLang :: String -> [(String, String)] +originalLang lang = + let transLang = translateLang lang + in if transLang == lang + then [] + else [("data-org-language", lang)] -- | Translate from Org-mode's programming language identifiers to those used -- by Pandoc. This is useful to allow for proper syntax highlighting in -- cgit v1.2.3 From 04cb602d79df5ee386107c27107cb9c8d09b1665 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sun, 23 Apr 2017 13:58:16 +0200 Subject: Org reader: allow multi-word arguments to src block params The reader now correctly parses src block parameter list even if parameter arguments contain multiple words. Closes: #3477 --- src/Text/Pandoc/Readers/Org/Blocks.hs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index 3cb9c7ed8..fb942608d 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -589,9 +589,12 @@ blockOption = try $ do orgParamValue :: Monad m => OrgParser m String orgParamValue = try $ skipSpaces - *> notFollowedBy (char ':' ) - *> many1 nonspaceChar + *> notFollowedBy orgArgKey + *> noneOf "\n\r" `many1Till` endOfValue <* skipSpaces + where + endOfValue = lookAhead $ (try $ skipSpaces <* oneOf "\n\r") + <|> (try $ skipSpaces1 <* orgArgKey) -- -- cgit v1.2.3 From 624e5b2f9283a5b18ceeb178f9ad4e7fa2520e7e Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 24 Apr 2017 11:17:53 +0300 Subject: TWiki reader: remove unnecessary $ (#3597) --- src/Text/Pandoc/Readers/TWiki.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/TWiki.hs b/src/Text/Pandoc/Readers/TWiki.hs index 397179dd1..0d8ff383e 100644 --- a/src/Text/Pandoc/Readers/TWiki.hs +++ b/src/Text/Pandoc/Readers/TWiki.hs @@ -161,7 +161,7 @@ header = tryMsg "header" $ do skipSpaces content <- B.trimInlines . mconcat <$> manyTill inline newline attr <- registerHeader ("", classes, []) content - return $ B.headerWith attr level $ content + return $ B.headerWith attr level content verbatim :: PandocMonad m => TWParser m B.Blocks verbatim = (htmlElement "verbatim" <|> htmlElement "pre") -- cgit v1.2.3 From d17f0dab841fdda322853c175563792bde50fca2 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 24 Apr 2017 23:39:14 +0200 Subject: LaTeX reader: better support for subfigure package. A figure with two subfigures turns into two pandoc figures; the subcaptions are used and the main caption ignored, unless there are no subcaptions. Closes #3577. --- src/Text/Pandoc/Readers/LaTeX.hs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index f3c94dacb..3e5fae8fb 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -39,7 +39,7 @@ import Control.Applicative (many, optional, (<|>)) import Control.Monad import Control.Monad.Except (throwError) import Data.Char (chr, isAlphaNum, isLetter, ord) -import Data.List (intercalate) +import Data.List (intercalate, isPrefixOf) import qualified Data.Map as M import Data.Maybe (fromMaybe, maybeToList) import Safe (minimumDef) @@ -1111,10 +1111,11 @@ rawLaTeXInline = do addImageCaption :: PandocMonad m => Blocks -> LP m Blocks addImageCaption = walkM go - where go (Image attr alt (src,tit)) = do + where go (Image attr alt (src,tit)) + | not ("fig:" `isPrefixOf` tit) = do mbcapt <- stateCaption <$> getState return $ case mbcapt of - Just ils -> Image attr (toList ils) (src, "fig:") + Just ils -> Image attr (toList ils) (src, "fig:" ++ tit) Nothing -> Image attr alt (src,tit) go x = return x @@ -1134,8 +1135,8 @@ environments = M.fromList , ("letter", env "letter" letterContents) , ("minipage", env "minipage" $ skipopts *> spaces' *> optional braced *> spaces' *> blocks) - , ("figure", env "figure" $ - resetCaption *> skipopts *> blocks >>= addImageCaption) + , ("figure", env "figure" $ skipopts *> figure) + , ("subfigure", env "subfigure" $ skipopts *> tok *> figure) , ("center", env "center" blocks) , ("longtable", env "longtable" $ resetCaption *> simpTable False >>= addTableCaption) @@ -1187,6 +1188,11 @@ environments = M.fromList , ("alignat*", mathEnv para (Just "aligned") "alignat*") ] +figure :: PandocMonad m => LP m Blocks +figure = try $ do + resetCaption + blocks >>= addImageCaption + letterContents :: PandocMonad m => LP m Blocks letterContents = do bs <- blocks -- cgit v1.2.3 From ee160d7c4cc912554fe0a1c7ff9fb802e9e72b64 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 25 Apr 2017 15:00:27 +0200 Subject: LaTeX writer: fix error with line breaks after empty content. LaTeX requires something before a line break, so we insert a `~` if no printable content has yet been emitted. Closes #2874. --- src/Text/Pandoc/Writers/LaTeX.hs | 47 ++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 19 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 70539a4a6..59d6030cf 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -81,6 +81,7 @@ data WriterState = , stInternalLinks :: [String] -- list of internal link targets , stUsesEuro :: Bool -- true if euro symbol used , stBeamer :: Bool -- produce beamer + , stEmptyLine :: Bool -- true if no content on line } startingState :: WriterOptions -> WriterState @@ -107,7 +108,8 @@ startingState options = WriterState { , stIncremental = writerIncremental options , stInternalLinks = [] , stUsesEuro = False - , stBeamer = False } + , stBeamer = False + , stEmptyLine = True } -- | Convert Pandoc to LaTeX. writeLaTeX :: PandocMonad m => WriterOptions -> Pandoc -> m String @@ -680,7 +682,8 @@ toColDescriptor align = AlignDefault -> "l" blockListToLaTeX :: PandocMonad m => [Block] -> LW m Doc -blockListToLaTeX lst = vsep `fmap` mapM blockToLaTeX lst +blockListToLaTeX lst = + vsep `fmap` mapM (\b -> setEmptyLine True >> blockToLaTeX b) lst tableRowToLaTeX :: PandocMonad m => Bool @@ -882,7 +885,7 @@ inlineListToLaTeX :: PandocMonad m => [Inline] -- ^ Inlines to convert -> LW m Doc inlineListToLaTeX lst = - mapM inlineToLaTeX (fixBreaks $ fixLineInitialSpaces lst) + mapM inlineToLaTeX (fixLineInitialSpaces lst) >>= return . hcat -- nonbreaking spaces (~) in LaTeX don't work after line breaks, -- so we turn nbsps after hard breaks to \hspace commands. @@ -894,14 +897,6 @@ inlineListToLaTeX lst = fixNbsps s = let (ys,zs) = span (=='\160') s in replicate (length ys) hspace ++ [Str zs] hspace = RawInline "latex" "\\hspace*{0.333em}" - -- linebreaks after blank lines cause problems: - fixBreaks [] = [] - fixBreaks ys@(LineBreak : LineBreak : _) = - case span (== LineBreak) ys of - (lbs, rest) -> RawInline "latex" - ("\\\\[" ++ show (length lbs) ++ - "\\baselineskip]") : fixBreaks rest - fixBreaks (y:ys) = y : fixBreaks ys isQuoted :: Inline -> Bool isQuoted (Quoted _ _) = True @@ -927,9 +922,9 @@ inlineToLaTeX (Span (id',classes,kvs) ils) = do return $ (if null id' then empty else "\\protect" <> linkAnchor) <> - if null cmds - then braces contents - else foldr inCmd contents cmds + (if null cmds + then braces contents + else foldr inCmd contents cmds) inlineToLaTeX (Emph lst) = inlineListToLaTeX lst >>= return . inCmd "emph" inlineToLaTeX (Strong lst) = @@ -1007,18 +1002,27 @@ inlineToLaTeX (Quoted qt lst) = do if isEnabled Ext_smart opts then char '`' <> inner <> char '\'' else char '\x2018' <> inner <> char '\x2019' -inlineToLaTeX (Str str) = liftM text $ stringToLaTeX TextString str -inlineToLaTeX (Math InlineMath str) = +inlineToLaTeX (Str str) = do + setEmptyLine False + liftM text $ stringToLaTeX TextString str +inlineToLaTeX (Math InlineMath str) = do + setEmptyLine False return $ "\\(" <> text str <> "\\)" -inlineToLaTeX (Math DisplayMath str) = +inlineToLaTeX (Math DisplayMath str) = do + setEmptyLine False return $ "\\[" <> text str <> "\\]" inlineToLaTeX il@(RawInline f str) | f == Format "latex" || f == Format "tex" - = return $ text str + = do + setEmptyLine False + return $ text str | otherwise = do report $ InlineNotRendered il return empty -inlineToLaTeX (LineBreak) = return $ "\\\\" <> cr +inlineToLaTeX (LineBreak) = do + emptyLine <- gets stEmptyLine + setEmptyLine True + return $ (if emptyLine then "~" else "") <> "\\\\" <> cr inlineToLaTeX SoftBreak = do wrapText <- gets (writerWrapText . stOptions) case wrapText of @@ -1048,6 +1052,7 @@ inlineToLaTeX (Link _ txt (src, _)) = return $ text ("\\href{" ++ src' ++ "}{") <> contents <> char '}' inlineToLaTeX (Image attr _ (source, _)) = do + setEmptyLine False modify $ \s -> s{ stGraphics = True } opts <- gets stOptions let showDim dir = let d = text (show dir) <> "=" @@ -1073,6 +1078,7 @@ inlineToLaTeX (Image attr _ (source, _)) = do (if inHeading then "\\protect\\includegraphics" else "\\includegraphics") <> dims <> braces (text source'') inlineToLaTeX (Note contents) = do + setEmptyLine False inMinipage <- gets stInMinipage modify (\s -> s{stInNote = True}) contents' <- blockListToLaTeX contents @@ -1100,6 +1106,9 @@ protectCode (x@(Code _ _) : xs) = ltx "\\mbox{" : x : ltx "}" : xs where ltx = RawInline (Format "latex") protectCode (x : xs) = x : protectCode xs +setEmptyLine :: PandocMonad m => Bool -> LW m () +setEmptyLine b = modify $ \st -> st{ stEmptyLine = b } + citationsToNatbib :: PandocMonad m => [Citation] -> LW m Doc citationsToNatbib (one:[]) = citeCommand c p s k -- cgit v1.2.3 From 66b08391b38b0812112ff03d7610d4592c3a1017 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 25 Apr 2017 23:07:30 +0200 Subject: HTML line block: Use class instead of style attribute. We now issue `<div class="line-block">` and include a default definition for `line-block` in the default templates, instead of hard-coding a `style` on the div. Closes #1623. --- src/Text/Pandoc/Writers/HTML.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index d56a6e4ae..9f41f77d1 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -600,7 +600,7 @@ blockToHtml opts (LineBlock lns) = else do let lf = preEscapedString "\n" htmlLines <- mconcat . intersperse lf <$> mapM (inlineListToHtml opts) lns - return $ H.div ! A.style "white-space: pre-line;" $ htmlLines + return $ H.div ! A.class_ "line-block" $ htmlLines blockToHtml opts (Div attr@(ident, classes, kvs) bs) = do html5 <- gets stHtml5 let speakerNotes = "notes" `elem` classes -- cgit v1.2.3 From 81548960d52d6c3ca34aa5fa041e5aa8bae9481b Mon Sep 17 00:00:00 2001 From: schrieveslaach <schrieveslaach@online.de> Date: Wed, 26 Apr 2017 12:03:07 +0200 Subject: LaTeX reader: Add support for \vdots (#3607) --- 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 3e5fae8fb..564d4e417 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -528,6 +528,7 @@ inlineCommands = M.fromList $ , ("textbf", extractSpaces strong <$> tok) , ("textnormal", extractSpaces (spanWith ("",["nodecor"],[])) <$> tok) , ("ldots", lit "…") + , ("vdots", lit "\8942") , ("dots", lit "…") , ("mdots", lit "…") , ("sim", lit "~") -- cgit v1.2.3 From 256e3a6a5d3f91262e8b3304fc2fa8cd1a854038 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Wed, 26 Apr 2017 13:04:28 +0300 Subject: Simplify linkText in Text.Pandoc.Readers.TWiki (#3605) Avoid constructing list of one element ([B.str url]) just to flatten it back with mconcat. --- src/Text/Pandoc/Readers/TWiki.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/TWiki.hs b/src/Text/Pandoc/Readers/TWiki.hs index 0d8ff383e..ecb609ae9 100644 --- a/src/Text/Pandoc/Readers/TWiki.hs +++ b/src/Text/Pandoc/Readers/TWiki.hs @@ -520,9 +520,9 @@ linkText :: PandocMonad m => TWParser m (String, String, B.Inlines) linkText = do string "[[" url <- many1Till anyChar (char ']') - content <- option [B.str url] linkContent + content <- option (B.str url) (mconcat <$> linkContent) char ']' - return (url, "", mconcat content) + return (url, "", content) where linkContent = (char '[') >> many1Till anyChar (char ']') >>= parseLinkContent parseLinkContent = parseFromString $ many1 inline -- cgit v1.2.3 From a29fa15a7b214ff83efe7b32fe51a55711984ef7 Mon Sep 17 00:00:00 2001 From: schrieveslaach <schrieveslaach@online.de> Date: Wed, 26 Apr 2017 12:05:13 +0200 Subject: LaTeX reader: Add basic support for hyphenat package (#3603) --- src/Text/Pandoc/Readers/LaTeX.hs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 564d4e417..a54238206 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -518,7 +518,7 @@ inlineCommands = M.fromList $ , ("textmd", extractSpaces (spanWith ("",["medium"],[])) <$> tok) , ("textrm", extractSpaces (spanWith ("",["roman"],[])) <$> tok) , ("textup", extractSpaces (spanWith ("",["upright"],[])) <$> tok) - , ("texttt", (code . stringify . toList) <$> tok) + , ("texttt", ttfamily) , ("sout", extractSpaces strikeout <$> tok) , ("textsuperscript", extractSpaces superscript <$> tok) , ("textsubscript", extractSpaces subscript <$> tok) @@ -671,6 +671,15 @@ inlineCommands = M.fromList $ , ("hypertarget", braced >> tok) -- siuntix , ("SI", dosiunitx) + -- hyphenat + , ("bshyp", lit "\\\173") + , ("fshyp", lit "/\173") + , ("dothyp", lit ".\173") + , ("colonhyp", lit ":\173") + , ("hyp", lit "-") + , ("nohyphens", tok) + , ("textnhtt", ttfamily) + , ("nhttfamily", ttfamily) ] ++ map ignoreInlines -- these commands will be ignored unless --parse-raw is specified, -- in which case they will appear as raw latex blocks: @@ -682,6 +691,9 @@ inlineCommands = M.fromList $ , "pagebreak" ] +ttfamily :: PandocMonad m => LP m Inlines +ttfamily = (code . stringify . toList) <$> tok + mkImage :: PandocMonad m => [(String, String)] -> String -> LP m Inlines mkImage options src = do let replaceTextwidth (k,v) = case numUnit v of -- cgit v1.2.3 From 5416fb14aba9804ce4a227a4ebd8228d82aa658e Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sun, 16 Apr 2017 21:00:01 +0200 Subject: Lua filter: allow natural access to meta elements Meta elements that are treated as lua tables (i.e. MetaList, MetaInlines, MetaBlocks, and MetaMap), are no longer wrapped in an additional table but simply marked via a metatable. This allows treating those meta values just like normal tables, while still making empty elements of those values distinguishable. --- src/Text/Pandoc/Lua/StackInstances.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index 9ba28b58e..52151ce64 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -140,7 +140,7 @@ peekMetaValue :: LuaState -> Int -> IO (Maybe MetaValue) peekMetaValue lua idx = do -- Get the contents of an AST element. let elementContent :: StackValue a => IO (Maybe a) - elementContent = getTable lua idx "c" + elementContent = peek lua idx luatype <- ltype lua idx case luatype of TBOOLEAN -> fmap MetaBool <$> peek lua idx -- cgit v1.2.3 From 9cd20c9b8b1fa3bd4581399327d61551558cf899 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sun, 16 Apr 2017 21:06:50 +0200 Subject: Lua filter: allow filtering of meta data only --- src/Text/Pandoc/Lua.hs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 0d1c6cf45..ffc57c9c2 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -75,7 +75,8 @@ runAll (x:xs) = walkMWithLuaFilter x >=> runAll xs walkMWithLuaFilter :: LuaFilter -> Pandoc -> IO Pandoc walkMWithLuaFilter (LuaFilter lua fnMap) = walkM (execInlineLuaFilter lua fnMap) >=> - walkM (execBlockLuaFilter lua fnMap) >=> + walkM (execBlockLuaFilter lua fnMap) >=> + walkM (execMetaLuaFilter lua fnMap) >=> walkM (execDocLuaFilter lua fnMap) type FunctionMap = Map String LuaFilterFunction @@ -92,6 +93,17 @@ execDocLuaFilter lua fnMap x = do Nothing -> return x Just fn -> runLuaFilterFunction lua fn x +execMetaLuaFilter :: LuaState + -> FunctionMap + -> Pandoc -> IO Pandoc +execMetaLuaFilter lua fnMap pd@(Pandoc meta blks) = do + let metaFnName = "Meta" + case Map.lookup metaFnName fnMap of + Nothing -> return pd + Just fn -> do + meta' <- runLuaFilterFunction lua fn meta + return $ Pandoc meta' blks + execBlockLuaFilter :: LuaState -> FunctionMap -> Block -> IO Block -- cgit v1.2.3 From 3ac23ab615e67c5d08941fcebeb1d19d37ff1a46 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Mon, 17 Apr 2017 10:04:20 +0200 Subject: API change: move extension handling to Text.Pandoc.Extensions Extension parsing and processing functions were defined in the top-level Text.Pandoc module. These functions are moved to the Extensions submodule as to enable reuse in other submodules. --- src/Text/Pandoc.hs | 61 +-------------------------------- src/Text/Pandoc/Extensions.hs | 78 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 75 insertions(+), 64 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index 977ad1ab4..5d495299f 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -144,7 +144,6 @@ module Text.Pandoc -- * Miscellaneous , getReader , getWriter - , getDefaultExtensions , pandocVersion ) where @@ -175,7 +174,7 @@ import Text.Pandoc.Readers.RST import Text.Pandoc.Readers.Textile import Text.Pandoc.Readers.TWiki import Text.Pandoc.Readers.Txt2Tags -import Text.Pandoc.Shared (mapLeft, pandocVersion, safeRead) +import Text.Pandoc.Shared (mapLeft, pandocVersion) import Text.Pandoc.Templates import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Writers.AsciiDoc @@ -208,29 +207,8 @@ import Text.Pandoc.Writers.TEI import Text.Pandoc.Writers.Texinfo import Text.Pandoc.Writers.Textile import Text.Pandoc.Writers.ZimWiki -import Text.Parsec import Text.Parsec.Error -parseFormatSpec :: String - -> Either ParseError (String, Extensions -> Extensions) -parseFormatSpec = parse formatSpec "" - where formatSpec = do - name <- formatName - extMods <- many extMod - return (name, \x -> foldl (flip ($)) x extMods) - formatName = many1 $ noneOf "-+" - extMod = do - polarity <- oneOf "-+" - name <- many $ noneOf "-+" - ext <- case safeRead ("Ext_" ++ name) of - Just n -> return n - Nothing - | name == "lhs" -> return Ext_literate_haskell - | otherwise -> fail $ "Unknown extension: " ++ name - return $ case polarity of - '-' -> disableExtension ext - _ -> enableExtension ext - data Reader m = StringReader (ReaderOptions -> String -> m Pandoc) | ByteStringReader (ReaderOptions -> BL.ByteString -> m Pandoc) @@ -318,43 +296,6 @@ writers = [ ,("muse" , StringWriter writeMuse) ] -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" = extensionsFromList - [Ext_citations, - Ext_auto_identifiers] -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, - 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] -getDefaultExtensions "context" = extensionsFromList - [Ext_smart, - Ext_auto_identifiers] -getDefaultExtensions "textile" = extensionsFromList - [Ext_old_dashes, - Ext_smart, - Ext_raw_html, - Ext_auto_identifiers] -getDefaultExtensions _ = extensionsFromList - [Ext_auto_identifiers] - -- | Retrieve reader based on formatSpec (format+extensions). getReader :: PandocMonad m => String -> Either String (Reader m) getReader s = diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 54f38f4a0..24f7d56ec 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -1,7 +1,5 @@ -{-# LANGUAGE DeriveDataTypeable #-} -{-# LANGUAGE DeriveGeneric #-} {- -Copyright (C) 2012-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2012-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -17,10 +15,12 @@ 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 -} +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE DeriveGeneric #-} {- | Module : Text.Pandoc.Extensions - Copyright : Copyright (C) 2012-2016 John MacFarlane + Copyright : Copyright (C) 2012-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> @@ -33,9 +33,11 @@ module Text.Pandoc.Extensions ( Extension(..) , Extensions , emptyExtensions , extensionsFromList + , parseFormatSpec , extensionEnabled , enableExtension , disableExtension + , getDefaultExtensions , pandocExtensions , plainExtensions , strictExtensions @@ -47,6 +49,8 @@ import Data.Bits (clearBit, setBit, testBit) import Data.Data (Data) import Data.Typeable (Typeable) import GHC.Generics (Generic) +import Text.Pandoc.Shared (safeRead) +import Text.Parsec newtype Extensions = Extensions Integer deriving (Show, Read, Eq, Ord, Data, Typeable, Generic) @@ -135,6 +139,7 @@ data Extension = | Ext_old_dashes -- ^ -- = em, - before number = en deriving (Show, Read, Enum, Eq, Ord, Bounded, Data, Typeable, Generic) +-- | Extensions to be used with pandoc-flavored markdown. pandocExtensions :: Extensions pandocExtensions = extensionsFromList [ Ext_footnotes @@ -182,6 +187,7 @@ pandocExtensions = extensionsFromList , Ext_smart ] +-- | Extensions to be used with github-flavored markdown. plainExtensions :: Extensions plainExtensions = extensionsFromList [ Ext_table_captions @@ -200,6 +206,7 @@ plainExtensions = extensionsFromList , Ext_strikeout ] +-- | Extensions to be used with github-flavored markdown. phpMarkdownExtraExtensions :: Extensions phpMarkdownExtraExtensions = extensionsFromList [ Ext_footnotes @@ -215,6 +222,7 @@ phpMarkdownExtraExtensions = extensionsFromList , Ext_shortcut_reference_links ] +-- | Extensions to be used with github-flavored markdown. githubMarkdownExtensions :: Extensions githubMarkdownExtensions = extensionsFromList [ Ext_angle_brackets_escapable @@ -234,6 +242,7 @@ githubMarkdownExtensions = extensionsFromList , Ext_shortcut_reference_links ] +-- | Extensions to be used with multimarkdown. multimarkdownExtensions :: Extensions multimarkdownExtensions = extensionsFromList [ Ext_pipe_tables @@ -264,9 +273,70 @@ multimarkdownExtensions = extensionsFromList , Ext_subscript ] +-- | Language extensions to be used with strict markdown. strictExtensions :: Extensions strictExtensions = extensionsFromList [ Ext_raw_html , Ext_shortcut_reference_links ] +-- | Default extensions from format-describing string. +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" = extensionsFromList + [Ext_citations, + Ext_auto_identifiers] +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, + 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] +getDefaultExtensions "context" = extensionsFromList + [Ext_smart, + Ext_auto_identifiers] +getDefaultExtensions "textile" = extensionsFromList + [Ext_old_dashes, + Ext_smart, + Ext_raw_html, + Ext_auto_identifiers] +getDefaultExtensions _ = extensionsFromList + [Ext_auto_identifiers] + +-- | Parse a format-specifying string into a markup format and a function that +-- takes Extensions and enables and disables extensions as defined in the format +-- spec. +parseFormatSpec :: String + -> Either ParseError (String, Extensions -> Extensions) +parseFormatSpec = parse formatSpec "" + where formatSpec = do + name <- formatName + extMods <- many extMod + return (name, \x -> foldl (flip ($)) x extMods) + formatName = many1 $ noneOf "-+" + extMod = do + polarity <- oneOf "-+" + name <- many $ noneOf "-+" + ext <- case safeRead ("Ext_" ++ name) of + Just n -> return n + Nothing + | name == "lhs" -> return Ext_literate_haskell + | otherwise -> fail $ "Unknown extension: " ++ name + return $ case polarity of + '-' -> disableExtension ext + _ -> enableExtension ext -- cgit v1.2.3 From 0e107a305f048cbe4cc45c6d2d0c8df14f00375a Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Mon, 17 Apr 2017 10:51:37 +0200 Subject: API change: move reader functions to Text.Pandoc.Readers Reader helper functions were defined in the top-level Text.Pandoc module. These functions are moved to the Readers submodule as to enable reuse in other submodules. --- src/Text/Pandoc.hs | 96 ++--------------------------- src/Text/Pandoc/Readers.hs | 146 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+), 92 deletions(-) create mode 100644 src/Text/Pandoc/Readers.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index 5d495299f..afe4bc8c7 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -70,30 +70,10 @@ module Text.Pandoc , setVerbosity -- * Error handling , module Text.Pandoc.Error - -- * Lists of readers and writers - , readers - -- , writers + -- * Lists of writers , writers -- * Readers: converting /to/ Pandoc format - , Reader (..) - , readDocx - , readOdt - , readMarkdown - , readCommonMark - , readMediaWiki - , readRST - , readOrg - , readLaTeX - , readHtml - , readTextile - , readDocBook - , readOPML - , readHaddock - , readNative - , readJSON - , readTWiki - , readTxt2Tags - , readEPUB + , module Text.Pandoc.Readers -- * Writers: converting /from/ Pandoc format , Writer(..) , writeNative @@ -142,12 +122,10 @@ module Text.Pandoc -- * Rendering templates and default templates , module Text.Pandoc.Templates -- * Miscellaneous - , getReader , getWriter , pandocVersion ) where -import Control.Monad.Except (throwError) import Data.Aeson import qualified Data.ByteString.Lazy as BL import Data.List (intercalate) @@ -157,24 +135,8 @@ import Text.Pandoc.Error import Text.Pandoc.Generic import Text.Pandoc.Logging import Text.Pandoc.Options -import Text.Pandoc.Readers.CommonMark -import Text.Pandoc.Readers.DocBook -import Text.Pandoc.Readers.Docx -import Text.Pandoc.Readers.EPUB -import Text.Pandoc.Readers.Haddock -import Text.Pandoc.Readers.HTML -import Text.Pandoc.Readers.LaTeX -import Text.Pandoc.Readers.Markdown -import Text.Pandoc.Readers.MediaWiki -import Text.Pandoc.Readers.Native -import Text.Pandoc.Readers.Odt -import Text.Pandoc.Readers.OPML -import Text.Pandoc.Readers.Org -import Text.Pandoc.Readers.RST -import Text.Pandoc.Readers.Textile -import Text.Pandoc.Readers.TWiki -import Text.Pandoc.Readers.Txt2Tags -import Text.Pandoc.Shared (mapLeft, pandocVersion) +import Text.Pandoc.Readers +import Text.Pandoc.Shared (pandocVersion) import Text.Pandoc.Templates import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Writers.AsciiDoc @@ -209,38 +171,6 @@ import Text.Pandoc.Writers.Textile import Text.Pandoc.Writers.ZimWiki import Text.Parsec.Error -data Reader m = StringReader (ReaderOptions -> String -> m Pandoc) - | ByteStringReader (ReaderOptions -> BL.ByteString -> m Pandoc) - --- | Association list of formats and readers. -readers :: PandocMonad m => [(String, Reader m)] -readers = [ ("native" , StringReader readNative) - ,("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 readRST) - ,("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" , StringReader readTxt2Tags) - ,("epub" , ByteStringReader readEPUB) - ] - data Writer m = StringWriter (WriterOptions -> Pandoc -> m String) | ByteStringWriter (WriterOptions -> Pandoc -> m BL.ByteString) @@ -296,21 +226,6 @@ writers = [ ,("muse" , StringWriter writeMuse) ] --- | Retrieve reader based on formatSpec (format+extensions). -getReader :: PandocMonad m => String -> Either String (Reader m) -getReader s = - case parseFormatSpec s of - Left e -> Left $ intercalate "\n" [m | Message m <- errorMessages e] - Right (readerName, setExts) -> - case lookup readerName readers of - Nothing -> Left $ "Unknown reader: " ++ readerName - Just (StringReader r) -> Right $ StringReader $ \o -> - r o{ readerExtensions = setExts $ - getDefaultExtensions readerName } - Just (ByteStringReader r) -> Right $ ByteStringReader $ \o -> - r o{ readerExtensions = setExts $ - getDefaultExtensions readerName } - getWriter :: PandocMonad m => String -> Either String (Writer m) getWriter s = case parseFormatSpec s of @@ -325,8 +240,5 @@ getWriter s \o -> r o{ writerExtensions = setExts $ getDefaultExtensions writerName } -readJSON :: ReaderOptions -> String -> Either PandocError Pandoc -readJSON _ = mapLeft PandocParseError . eitherDecode' . UTF8.fromStringLazy - writeJSON :: WriterOptions -> Pandoc -> String writeJSON _ = UTF8.toStringLazy . encode diff --git a/src/Text/Pandoc/Readers.hs b/src/Text/Pandoc/Readers.hs new file mode 100644 index 000000000..e2d40336c --- /dev/null +++ b/src/Text/Pandoc/Readers.hs @@ -0,0 +1,146 @@ +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE ScopedTypeVariables #-} +{- +Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> + +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 + Copyright : Copyright (C) 2006-2017 John MacFarlane + License : GNU GPL, version 2 or above + + Maintainer : John MacFarlane <jgm@berkeley.edu> + Stability : alpha + Portability : portable + +This helper module exports the readers. + +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, +you should remove @'\r'@ characters using @filter (/='\r')@. + +-} + +module Text.Pandoc.Readers + ( + -- * Readers: converting /to/ Pandoc format + Reader (..) + , readers + , readDocx + , readOdt + , readMarkdown + , readCommonMark + , readMediaWiki + , readRST + , readOrg + , readLaTeX + , readHtml + , readTextile + , readDocBook + , readOPML + , readHaddock + , readNative + , readJSON + , readTWiki + , readTxt2Tags + , readEPUB + -- * Miscellaneous + , getReader + , getDefaultExtensions + ) where + +import Control.Monad.Except (throwError) +import Data.Aeson +import Data.List (intercalate) +import Text.Pandoc.Class +import Text.Pandoc.Definition +import Text.Pandoc.Error +import Text.Pandoc.Extensions +import Text.Pandoc.Options +import Text.Pandoc.Readers.CommonMark +import Text.Pandoc.Readers.DocBook +import Text.Pandoc.Readers.Docx +import Text.Pandoc.Readers.EPUB +import Text.Pandoc.Readers.Haddock +import Text.Pandoc.Readers.HTML +import Text.Pandoc.Readers.LaTeX +import Text.Pandoc.Readers.Markdown +import Text.Pandoc.Readers.MediaWiki +import Text.Pandoc.Readers.Native +import Text.Pandoc.Readers.Odt +import Text.Pandoc.Readers.OPML +import Text.Pandoc.Readers.Org +import Text.Pandoc.Readers.RST +import Text.Pandoc.Readers.Textile +import Text.Pandoc.Readers.TWiki +import Text.Pandoc.Readers.Txt2Tags +import Text.Pandoc.Shared (mapLeft) +import Text.Parsec.Error +import qualified Text.Pandoc.UTF8 as UTF8 +import qualified Data.ByteString.Lazy as BL + +data Reader m = StringReader (ReaderOptions -> String -> m Pandoc) + | ByteStringReader (ReaderOptions -> BL.ByteString -> m Pandoc) + +-- | Association list of formats and readers. +readers :: PandocMonad m => [(String, Reader m)] +readers = [ ("native" , StringReader readNative) + ,("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 readRST) + ,("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" , StringReader readTxt2Tags) + ,("epub" , ByteStringReader readEPUB) + ] + +-- | Retrieve reader based on formatSpec (format+extensions). +getReader :: PandocMonad m => String -> Either String (Reader m) +getReader s = + case parseFormatSpec s of + Left e -> Left $ intercalate "\n" [m | Message m <- errorMessages e] + Right (readerName, setExts) -> + case lookup readerName readers of + Nothing -> Left $ "Unknown reader: " ++ readerName + Just (StringReader r) -> Right $ StringReader $ \o -> + r o{ readerExtensions = setExts $ + getDefaultExtensions readerName } + Just (ByteStringReader r) -> Right $ ByteStringReader $ \o -> + r o{ readerExtensions = setExts $ + getDefaultExtensions readerName } + +-- | Read pandoc document from JSON format. +readJSON :: ReaderOptions -> String -> Either PandocError Pandoc +readJSON _ = mapLeft PandocParseError . eitherDecode' . UTF8.fromStringLazy -- cgit v1.2.3 From c2567b2bd03f6dadd971cf8bc81cd93a0dde623e Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Mon, 17 Apr 2017 11:02:38 +0200 Subject: API change: move writer functions to Text.Pandoc.Writers Writer helper functions were defined in the top-level Text.Pandoc module. These functions are moved to the Writer submodule as to enable reuse in other submodules. --- src/Text/Pandoc.hs | 156 +----------------------------------- src/Text/Pandoc/Writers.hs | 193 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+), 154 deletions(-) create mode 100644 src/Text/Pandoc/Writers.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index afe4bc8c7..345ef3b18 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -70,65 +70,16 @@ module Text.Pandoc , setVerbosity -- * Error handling , module Text.Pandoc.Error - -- * Lists of writers - , writers -- * Readers: converting /to/ Pandoc format , module Text.Pandoc.Readers -- * Writers: converting /from/ Pandoc format - , Writer(..) - , writeNative - , writeJSON - , writeMarkdown - , writePlain - , writeRST - , writeLaTeX - , writeBeamer - , writeConTeXt - , writeTexinfo - , writeHtml4 - , writeHtml4String - , writeHtml5 - , writeHtml5String - , writeRevealJs - , writeS5 - , writeSlidy - , writeSlideous - , writeDZSlides - , writeICML - , writeDocbook4 - , writeDocbook5 - , writeJATS - , writeOPML - , writeOpenDocument - , writeMan - , writeMs - , writeMediaWiki - , writeDokuWiki - , writeZimWiki - , writeTextile - , writeRTF - , writeODT - , writeDocx - , writeEPUB2 - , writeEPUB3 - , writeFB2 - , writeOrg - , writeAsciiDoc - , writeHaddock - , writeCommonMark - , writeCustom - , writeTEI - , writeMuse + , module Text.Pandoc.Writers -- * Rendering templates and default templates , module Text.Pandoc.Templates -- * Miscellaneous - , getWriter , pandocVersion ) where -import Data.Aeson -import qualified Data.ByteString.Lazy as BL -import Data.List (intercalate) import Text.Pandoc.Class import Text.Pandoc.Definition import Text.Pandoc.Error @@ -138,107 +89,4 @@ import Text.Pandoc.Options import Text.Pandoc.Readers import Text.Pandoc.Shared (pandocVersion) import Text.Pandoc.Templates -import qualified Text.Pandoc.UTF8 as UTF8 -import Text.Pandoc.Writers.AsciiDoc -import Text.Pandoc.Writers.CommonMark -import Text.Pandoc.Writers.ConTeXt -import Text.Pandoc.Writers.Custom -import Text.Pandoc.Writers.Docbook -import Text.Pandoc.Writers.JATS -import Text.Pandoc.Writers.Docx -import Text.Pandoc.Writers.DokuWiki -import Text.Pandoc.Writers.EPUB -import Text.Pandoc.Writers.FB2 -import Text.Pandoc.Writers.Haddock -import Text.Pandoc.Writers.HTML -import Text.Pandoc.Writers.ICML -import Text.Pandoc.Writers.LaTeX -import Text.Pandoc.Writers.Man -import Text.Pandoc.Writers.Ms -import Text.Pandoc.Writers.Markdown -import Text.Pandoc.Writers.MediaWiki -import Text.Pandoc.Writers.Muse -import Text.Pandoc.Writers.Native -import Text.Pandoc.Writers.ODT -import Text.Pandoc.Writers.OpenDocument -import Text.Pandoc.Writers.OPML -import Text.Pandoc.Writers.Org -import Text.Pandoc.Writers.RST -import Text.Pandoc.Writers.RTF -import Text.Pandoc.Writers.TEI -import Text.Pandoc.Writers.Texinfo -import Text.Pandoc.Writers.Textile -import Text.Pandoc.Writers.ZimWiki -import Text.Parsec.Error - -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 writeEPUB3) - ,("epub2" , ByteStringWriter writeEPUB2) - ,("epub3" , ByteStringWriter writeEPUB3) - ,("fb2" , StringWriter writeFB2) - ,("html" , StringWriter writeHtml5String) - ,("html4" , StringWriter writeHtml4String) - ,("html5" , StringWriter writeHtml5String) - ,("icml" , StringWriter writeICML) - ,("s5" , StringWriter writeS5) - ,("slidy" , StringWriter writeSlidy) - ,("slideous" , StringWriter writeSlideous) - ,("dzslides" , StringWriter writeDZSlides) - ,("revealjs" , StringWriter writeRevealJs) - ,("docbook" , StringWriter writeDocbook5) - ,("docbook4" , StringWriter writeDocbook4) - ,("docbook5" , StringWriter writeDocbook5) - ,("jats" , StringWriter writeJATS) - ,("opml" , StringWriter writeOPML) - ,("opendocument" , StringWriter writeOpenDocument) - ,("latex" , StringWriter writeLaTeX) - ,("beamer" , StringWriter writeBeamer) - ,("context" , StringWriter writeConTeXt) - ,("texinfo" , StringWriter writeTexinfo) - ,("man" , StringWriter writeMan) - ,("ms" , StringWriter writeMs) - ,("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 writeRTF) - ,("org" , StringWriter writeOrg) - ,("asciidoc" , StringWriter writeAsciiDoc) - ,("haddock" , StringWriter writeHaddock) - ,("commonmark" , StringWriter writeCommonMark) - ,("tei" , StringWriter writeTEI) - ,("muse" , StringWriter writeMuse) - ] - -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 - Nothing -> Left $ "Unknown writer: " ++ writerName - Just (StringWriter r) -> Right $ StringWriter $ - \o -> r o{ writerExtensions = setExts $ - getDefaultExtensions writerName } - Just (ByteStringWriter r) -> Right $ ByteStringWriter $ - \o -> r o{ writerExtensions = setExts $ - getDefaultExtensions writerName } - -writeJSON :: WriterOptions -> Pandoc -> String -writeJSON _ = UTF8.toStringLazy . encode +import Text.Pandoc.Writers diff --git a/src/Text/Pandoc/Writers.hs b/src/Text/Pandoc/Writers.hs new file mode 100644 index 000000000..0181f41c9 --- /dev/null +++ b/src/Text/Pandoc/Writers.hs @@ -0,0 +1,193 @@ +{- +Copyright (C) 2006-2016 John MacFarlane <jgm@berkeley.edu> + +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 +-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE ScopedTypeVariables #-} + +{- | + Module : Text.Pandoc + Copyright : Copyright (C) 2006-2016 John MacFarlane + License : GNU GPL, version 2 or above + + Maintainer : John MacFarlane <jgm@berkeley.edu> + Stability : alpha + Portability : portable + +This helper module exports all writers functions. +-} +module Text.Pandoc.Writers + ( + -- * Writers: converting /from/ Pandoc format + Writer(..) + , writers + , writeAsciiDoc + , writeBeamer + , writeCommonMark + , writeConTeXt + , writeCustom + , writeDZSlides + , writeDocbook4 + , writeDocbook5 + , writeDocx + , writeDokuWiki + , writeEPUB2 + , writeEPUB3 + , writeFB2 + , writeHaddock + , writeHtml4 + , writeHtml4String + , writeHtml5 + , writeHtml5String + , writeICML + , writeJATS + , writeJSON + , writeLaTeX + , writeMan + , writeMarkdown + , writeMediaWiki + , writeMs + , writeMuse + , writeNative + , writeODT + , writeOPML + , writeOpenDocument + , writeOrg + , writePlain + , writeRST + , writeRTF + , writeRevealJs + , writeS5 + , writeSlideous + , writeSlidy + , writeTEI + , writeTexinfo + , writeTextile + , writeZimWiki + , getWriter + ) where + +import Data.Aeson +import Data.List (intercalate) +import Text.Pandoc.Class +import Text.Pandoc.Definition +import Text.Pandoc.Options +import Text.Pandoc.Writers.AsciiDoc +import Text.Pandoc.Writers.CommonMark +import Text.Pandoc.Writers.ConTeXt +import Text.Pandoc.Writers.Custom +import Text.Pandoc.Writers.Docbook +import Text.Pandoc.Writers.Docx +import Text.Pandoc.Writers.DokuWiki +import Text.Pandoc.Writers.EPUB +import Text.Pandoc.Writers.FB2 +import Text.Pandoc.Writers.HTML +import Text.Pandoc.Writers.Haddock +import Text.Pandoc.Writers.ICML +import Text.Pandoc.Writers.JATS +import Text.Pandoc.Writers.LaTeX +import Text.Pandoc.Writers.Man +import Text.Pandoc.Writers.Markdown +import Text.Pandoc.Writers.MediaWiki +import Text.Pandoc.Writers.Ms +import Text.Pandoc.Writers.Muse +import Text.Pandoc.Writers.Native +import Text.Pandoc.Writers.ODT +import Text.Pandoc.Writers.OPML +import Text.Pandoc.Writers.OpenDocument +import Text.Pandoc.Writers.Org +import Text.Pandoc.Writers.RST +import Text.Pandoc.Writers.RTF +import Text.Pandoc.Writers.TEI +import Text.Pandoc.Writers.Texinfo +import Text.Pandoc.Writers.Textile +import Text.Pandoc.Writers.ZimWiki +import Text.Parsec.Error +import qualified Text.Pandoc.UTF8 as UTF8 +import qualified Data.ByteString.Lazy as BL + +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 writeEPUB3) + ,("epub2" , ByteStringWriter writeEPUB2) + ,("epub3" , ByteStringWriter writeEPUB3) + ,("fb2" , StringWriter writeFB2) + ,("html" , StringWriter writeHtml5String) + ,("html4" , StringWriter writeHtml4String) + ,("html5" , StringWriter writeHtml5String) + ,("icml" , StringWriter writeICML) + ,("s5" , StringWriter writeS5) + ,("slidy" , StringWriter writeSlidy) + ,("slideous" , StringWriter writeSlideous) + ,("dzslides" , StringWriter writeDZSlides) + ,("revealjs" , StringWriter writeRevealJs) + ,("docbook" , StringWriter writeDocbook5) + ,("docbook4" , StringWriter writeDocbook4) + ,("docbook5" , StringWriter writeDocbook5) + ,("jats" , StringWriter writeJATS) + ,("opml" , StringWriter writeOPML) + ,("opendocument" , StringWriter writeOpenDocument) + ,("latex" , StringWriter writeLaTeX) + ,("beamer" , StringWriter writeBeamer) + ,("context" , StringWriter writeConTeXt) + ,("texinfo" , StringWriter writeTexinfo) + ,("man" , StringWriter writeMan) + ,("ms" , StringWriter writeMs) + ,("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 writeRTF) + ,("org" , StringWriter writeOrg) + ,("asciidoc" , StringWriter writeAsciiDoc) + ,("haddock" , StringWriter writeHaddock) + ,("commonmark" , StringWriter writeCommonMark) + ,("tei" , StringWriter writeTEI) + ,("muse" , StringWriter writeMuse) + ] + +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 + Nothing -> Left $ "Unknown writer: " ++ writerName + Just (StringWriter r) -> Right $ StringWriter $ + \o -> r o{ writerExtensions = setExts $ + getDefaultExtensions writerName } + Just (ByteStringWriter r) -> Right $ ByteStringWriter $ + \o -> r o{ writerExtensions = setExts $ + getDefaultExtensions writerName } + +writeJSON :: WriterOptions -> Pandoc -> String +writeJSON _ = UTF8.toStringLazy . encode -- cgit v1.2.3 From 24ef67213213b621a6c0f340e7bfdf68d2d40ac1 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Mon, 17 Apr 2017 11:54:42 +0200 Subject: Lua module: provide simple `read` format parser A single `read` function parsing pandoc-supported formats is added to the module. This is simpler and more convenient than the previous method of exposing all reader functions individually. --- src/Text/Pandoc/Lua/PandocModule.hs | 85 +++++++--------------------------- src/Text/Pandoc/Lua/SharedInstances.hs | 10 ++++ 2 files changed, 28 insertions(+), 67 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index d0c78f562..15f19f024 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -29,22 +29,12 @@ module Text.Pandoc.Lua.PandocModule ( pushPandocModule ) where import Data.ByteString.Char8 ( unpack ) import Data.Default ( Default(..) ) -import Scripting.Lua ( LuaState, call, newtable, push, pushhsfunction, rawset) +import Scripting.Lua ( LuaState, call, push, pushhsfunction, rawset) import Text.Pandoc.Class hiding ( readDataFile ) -import Text.Pandoc.Definition ( Pandoc(..), Block(..) ) +import Text.Pandoc.Definition ( Pandoc ) import Text.Pandoc.Lua.Compat ( loadstring ) import Text.Pandoc.Lua.StackInstances () -import Text.Pandoc.Readers.DocBook ( readDocBook ) -import Text.Pandoc.Readers.HTML ( readHtml ) -import Text.Pandoc.Readers.LaTeX ( readLaTeX ) -import Text.Pandoc.Readers.Native ( readNative ) -import Text.Pandoc.Readers.Markdown ( readMarkdown ) -import Text.Pandoc.Readers.MediaWiki ( readMediaWiki ) -import Text.Pandoc.Readers.Org ( readOrg ) -import Text.Pandoc.Readers.RST ( readRST ) -import Text.Pandoc.Readers.Textile ( readTextile ) -import Text.Pandoc.Readers.TWiki ( readTWiki ) -import Text.Pandoc.Readers.Txt2Tags ( readTxt2Tags ) +import Text.Pandoc.Readers ( Reader(..), getReader ) import Text.Pandoc.Shared ( readDataFile ) -- | Push the "pandoc" on the lua stack. @@ -56,63 +46,24 @@ pushPandocModule lua = do then return () else do call lua 0 1 - push lua "reader" - pushReadersModule lua readers + push lua "__read" + pushhsfunction lua read_doc rawset lua (-3) -readers :: [(String, String -> PandocIO Pandoc)] -readers = - [ ("docbook", readDocBook def) - , ("html", readHtml def) - , ("latex", readLaTeX def) - , ("native", readNative def) - , ("markdown", readMarkdown def) - , ("mediawiki", readMediaWiki def) - , ("org", readOrg def) - , ("rst", readRST def) - , ("textile", readTextile def) - , ("twiki", readTWiki def) - , ("txt2tags", readTxt2Tags def) - ] - -- | Get the string representation of the pandoc module pandocModuleScript :: IO String pandocModuleScript = unpack <$> readDataFile Nothing "pandoc.lua" --- | Push a lua table containing readers of the given formats. -pushReadersModule :: LuaState - -> [(String, String -> PandocIO Pandoc)] - -> IO () -pushReadersModule lua readerFns = do - newtable lua - mapM_ (uncurry $ addReaderTable) readerFns - where - addReaderTable :: String - -> (String -> PandocIO Pandoc) - -> IO () - addReaderTable formatName readerFn = do - let readDoc :: String -> IO Pandoc - readDoc s = do - res <- runIO $ readerFn s - case res of - (Left x) -> error (show x) - (Right x) -> return x - let readBlock :: String -> IO Block - readBlock s = do - Pandoc _ blks <- readDoc s - return $ case blks of - x:_ -> x - _ -> Null - -- Push table containing all functions for this format - push lua formatName - newtable lua - -- set document-reading function - push lua "read_doc" - pushhsfunction lua readDoc - rawset lua (-3) - -- set block-reading function - push lua "read_block" - pushhsfunction lua readBlock - rawset lua (-3) - -- store table in readers module - rawset lua (-3) +read_doc :: String -> String -> IO (Either String Pandoc) +read_doc formatSpec content = do + case getReader formatSpec of + Left s -> return $ Left s + Right reader -> + case reader of + StringReader r -> do + res <- runIO $ r def content + case res of + Left s -> return . Left $ show s + Right pd -> return $ Right pd + _ -> return $ Left "Only string formats are supported at the moment." + diff --git a/src/Text/Pandoc/Lua/SharedInstances.hs b/src/Text/Pandoc/Lua/SharedInstances.hs index 02438b93b..3d2d29ebf 100644 --- a/src/Text/Pandoc/Lua/SharedInstances.hs +++ b/src/Text/Pandoc/Lua/SharedInstances.hs @@ -104,3 +104,13 @@ instance (Ord a, StackValue a, StackValue b) => mapM_ (uncurry $ addValue lua) $ M.toList m peek lua idx = fmap M.fromList <$> keyValuePairs lua idx valuetype _ = TTABLE + +instance (StackValue a, StackValue b) => StackValue (Either a b) where + push lua = \case + Left x -> push lua x + Right x -> push lua x + peek lua idx = peek lua idx >>= \case + Just left -> return . Just $ Left left + Nothing -> fmap Right <$> peek lua idx + valuetype (Left x) = valuetype x + valuetype (Right x) = valuetype x -- cgit v1.2.3 From e97e9cd6a3d804678fc1554ef0f39443e1d2aeaa Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 28 Apr 2017 11:03:39 +0300 Subject: Muse writer: Do not reflow directives (#3614) Directives at the beginning of documents cannot span multiple lines so they must not be reflown. --- src/Text/Pandoc/Writers/Muse.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index cc88eb762..8f6493975 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -85,8 +85,8 @@ pandocToMuse (Pandoc meta blocks) = do then Just $ writerColumns opts else Nothing metadata <- metaToJSON opts - (fmap (render colwidth) . blockListToMuse) - (fmap (render colwidth) . inlineListToMuse) + (fmap (render Nothing) . blockListToMuse) + (fmap (render Nothing) . inlineListToMuse) meta body <- blockListToMuse blocks notes <- liftM (reverse . stNotes) get >>= notesToMuse -- cgit v1.2.3 From c346a8a7bef5a59c779073c9299bd968ecad44c9 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 28 Apr 2017 11:04:09 +0300 Subject: Ms writer: make use of already defined render' (#3613) --- src/Text/Pandoc/Writers/Ms.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index e4daa1be0..534f26a5a 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -97,8 +97,8 @@ pandocToMs opts (Pandoc meta blocks) = do else Nothing let render' = render colwidth metadata <- metaToJSON opts - (fmap (render colwidth) . blockListToMs opts) - (fmap (render colwidth) . inlineListToMs' opts) + (fmap render' . blockListToMs opts) + (fmap render' . inlineListToMs' opts) meta body <- blockListToMs opts blocks let main = render' body -- cgit v1.2.3 From e76b6724144032c62c183f850fe05271aa245fb5 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 28 Apr 2017 12:03:59 +0200 Subject: LaTeX writer: don't use lstinline it \item[..]. If you do, the contents of item disappear or are misplaced. Use `\texttt` instead. Closes #645. --- src/Text/Pandoc/Writers/LaTeX.hs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 59d6030cf..f0767c17c 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -65,6 +65,7 @@ data WriterState = , stInQuote :: Bool -- true if in a blockquote , stInMinipage :: Bool -- true if in minipage , stInHeading :: Bool -- true if in a section heading + , stInItem :: Bool -- true if in \item[..] , stNotes :: [Doc] -- notes in a minipage , stOLLevel :: Int -- level of ordered list nesting , stOptions :: WriterOptions -- writer options, so they don't have to be parameter @@ -90,6 +91,7 @@ startingState options = WriterState { , stInQuote = False , stInMinipage = False , stInHeading = False + , stInItem = False , stNotes = [] , stOLLevel = 1 , stOptions = options @@ -777,7 +779,10 @@ listItemToLaTeX lst defListItemToLaTeX :: PandocMonad m => ([Inline], [[Block]]) -> LW m Doc defListItemToLaTeX (term, defs) = do + -- needed to turn off 'listings' because it breaks inside \item[...]: + modify $ \s -> s{stInItem = True} term' <- inlineListToLaTeX term + modify $ \s -> s{stInItem = False} -- put braces around term if it contains an internal link, -- since otherwise we get bad bracket interactions: \item[\hyperref[..] let isInternalLink (Link _ _ ('#':_,_)) = True @@ -952,6 +957,7 @@ inlineToLaTeX (Cite cits lst) = do inlineToLaTeX (Code (_,classes,_) str) = do opts <- gets stOptions inHeading <- gets stInHeading + inItem <- gets stInItem let listingsCode = do let listingsopt = case getListingsLanguage classes of Just l -> "[language=" ++ mbBraced l ++ "]" @@ -975,7 +981,7 @@ inlineToLaTeX (Code (_,classes,_) str) = do Right h -> modify (\st -> st{ stHighlighting = True }) >> return (text (T.unpack h)) case () of - _ | writerListings opts && not inHeading -> listingsCode + _ | writerListings opts && not (inHeading || inItem) -> listingsCode | isJust (writerHighlightStyle opts) && not (null classes) -> highlightCode | otherwise -> rawCode -- cgit v1.2.3 From 730796ee314d42477fab216621b8e44539c94656 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 29 Apr 2017 11:05:44 +0200 Subject: LaTeX writer: Fix problem with escaping in lstinline. Previously the LaTeX writer created invalid LaTeX when `--listings` was specified and a code span occured inside emphasis or another construction. This is because the characters `%{}\` must be escaped in lstinline when the listinline occurs in another command, otherwise they must not be escaped. To deal with this, adoping Michael Kofler's suggestion, we always wrap lstinline in a dummy command `\passthrough`, now defined in the default template if `--listings` is specified. This way we can consistently escape the special characters. Closes #1629. --- src/Text/Pandoc/Writers/LaTeX.hs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index f0767c17c..000f4f8fb 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -967,7 +967,12 @@ inlineToLaTeX (Code (_,classes,_) str) = do let chr = case "!\"&'()*,-./:;?@_" \\ str of (c:_) -> c [] -> '!' - return $ text $ "\\lstinline" ++ listingsopt ++ [chr] ++ str ++ [chr] + let str' = escapeStringUsing (backslashEscapes "\\{}%") str + -- we always put lstinline in a dummy 'passthrough' command + -- (defined in the default template) so that we don't have + -- to change the way we escape characters depending on whether + -- the lstinline is inside another command. See #1629: + return $ text $ "\\passthrough{\\lstinline" ++ listingsopt ++ [chr] ++ str' ++ [chr] ++ "}" let rawCode = liftM (text . (\s -> "\\texttt{" ++ escapeSpaces s ++ "}")) $ stringToLaTeX CodeString str where escapeSpaces = concatMap -- cgit v1.2.3 From 540f3d49efa35fc48585e8feb845a01c14e3020a Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sat, 29 Apr 2017 23:58:26 +0300 Subject: Org reader: Avoid creating nullMeta by applying setMeta directly --- src/Text/Pandoc/Readers/Org/Meta.hs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/Meta.hs b/src/Text/Pandoc/Readers/Org/Meta.hs index 69ca00b23..7938fc6c6 100644 --- a/src/Text/Pandoc/Readers/Org/Meta.hs +++ b/src/Text/Pandoc/Readers/Org/Meta.hs @@ -48,7 +48,6 @@ import Control.Monad (mzero, void) import Data.Char (toLower) import Data.List (intersperse) import qualified Data.Map as M -import Data.Monoid ((<>)) import Network.HTTP (urlEncode) -- | Returns the current meta, respecting export options. @@ -76,9 +75,7 @@ declarationLine :: PandocMonad m => OrgParser m () declarationLine = try $ do key <- map toLower <$> metaKey (key', value) <- metaValue key - updateState $ \st -> - let meta' = B.setMeta key' <$> value <*> pure nullMeta - in st { orgStateMeta = meta' <> orgStateMeta st } + updateState $ \st -> st { orgStateMeta = B.setMeta key' <$> value <*> orgStateMeta st } metaKey :: Monad m => OrgParser m String metaKey = map toLower <$> many1 (noneOf ": \n\r") -- cgit v1.2.3 From 97addc2a17266b7d1c6cc712244f675bc0263595 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Tue, 25 Apr 2017 19:06:27 +0300 Subject: Add returnF to Text.Pandoc.Parsing --- src/Text/Pandoc/Readers/Org/Blocks.hs | 4 ++-- src/Text/Pandoc/Readers/Org/Inlines.hs | 2 +- 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 fb942608d..b0a19b833 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -701,7 +701,7 @@ endOfParagraph = try $ skipSpaces *> newline *> endOfBlock -- | Example code marked up by a leading colon. example :: Monad m => OrgParser m (F Blocks) example = try $ do - return . return . exampleCode =<< unlines <$> many1 exampleLine + returnF . exampleCode =<< unlines <$> many1 exampleLine where exampleLine :: Monad m => OrgParser m String exampleLine = try $ exampleLineStart *> anyLine @@ -885,7 +885,7 @@ latexFragment :: Monad m => OrgParser m (F Blocks) latexFragment = try $ do envName <- latexEnvStart content <- mconcat <$> manyTill anyLineNewline (latexEnd envName) - return . return $ B.rawBlock "latex" (content `inLatexEnv` envName) + returnF $ B.rawBlock "latex" (content `inLatexEnv` envName) where c `inLatexEnv` e = mconcat [ "\\begin{", e, "}\n" , c diff --git a/src/Text/Pandoc/Readers/Org/Inlines.hs b/src/Text/Pandoc/Readers/Org/Inlines.hs index d227eb66a..64ffb8ef5 100644 --- a/src/Text/Pandoc/Readers/Org/Inlines.hs +++ b/src/Text/Pandoc/Readers/Org/Inlines.hs @@ -157,7 +157,7 @@ endline = try $ do decEmphasisNewlinesCount guard =<< newlinesCountWithinLimits updateLastPreCharPos - return . return $ B.softbreak + returnF B.softbreak -- -- cgit v1.2.3 From 31caa616a9353e073eb86be7889b7087e14a48ac Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Thu, 27 Apr 2017 21:48:32 +0200 Subject: Provide shared F monad functions for Markdown and Org readers The `F` monads used for delayed evaluation of certain values in the Markdown and Org readers are based on a shared data type capturing the common pattern of both `F` types. --- src/Text/Pandoc/Parsing.hs | 35 ++++++++++++++++------- src/Text/Pandoc/Readers/Markdown.hs | 5 +--- src/Text/Pandoc/Readers/Org/ParserState.hs | 46 ++++++------------------------ src/Text/Pandoc/Readers/Org/Parsing.hs | 2 +- 4 files changed, 36 insertions(+), 52 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index e985f3d32..a6d3cd46a 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -108,10 +108,13 @@ module Text.Pandoc.Parsing ( anyLine, applyMacros', Parser, ParserT, - F(..), + F, + Future(..), runF, askF, asksF, + returnF, + trimInlinesF, token, (<+?>), extractIdClass, @@ -175,7 +178,7 @@ where import Text.Pandoc.Definition import Text.Pandoc.Options -import Text.Pandoc.Builder (Blocks, Inlines, rawBlock, HasMeta(..)) +import Text.Pandoc.Builder (Blocks, Inlines, rawBlock, HasMeta(..), trimInlines) import qualified Text.Pandoc.Builder as B import Text.Pandoc.XML (fromEntities) import qualified Text.Pandoc.UTF8 as UTF8 (putStrLn) @@ -205,18 +208,30 @@ type Parser t s = Parsec t s type ParserT = ParsecT -newtype F a = F { unF :: Reader ParserState a } deriving (Monad, Applicative, Functor) +-- | Reader monad wrapping the parser state. This is used to possibly delay +-- evaluation until all relevant information has been parsed and made available +-- in the parser state. +newtype Future s a = Future { runDelayed :: Reader s a } + deriving (Monad, Applicative, Functor) -runF :: F a -> ParserState -> a -runF = runReader . unF +type F = Future ParserState -askF :: F ParserState -askF = F ask +runF :: Future s a -> s -> a +runF = runReader . runDelayed -asksF :: (ParserState -> a) -> F a -asksF f = F $ asks f +askF :: Future s s +askF = Future ask -instance Monoid a => Monoid (F a) where +asksF :: (s -> a) -> Future s a +asksF f = Future $ asks f + +returnF :: Monad m => a -> m (Future s a) +returnF = return . return + +trimInlinesF :: Future s Inlines -> Future s Inlines +trimInlinesF = liftM trimInlines + +instance Monoid a => Monoid (Future s a) where mempty = return mempty mappend = liftM2 mappend mconcat = liftM mconcat . sequence diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 9eb242d74..5515c735b 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -50,7 +50,7 @@ import Data.Yaml (ParseException (..), YamlException (..), YamlMark (..)) import qualified Data.Yaml as Yaml import System.FilePath (addExtension, takeExtension) import Text.HTML.TagSoup -import Text.Pandoc.Builder (Blocks, Inlines, trimInlines) +import Text.Pandoc.Builder (Blocks, Inlines) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Definition @@ -80,9 +80,6 @@ readMarkdown opts s = do Right result -> return result Left e -> throwError e -trimInlinesF :: F Inlines -> F Inlines -trimInlinesF = liftM trimInlines - -- -- Constants and data structure definitions -- diff --git a/src/Text/Pandoc/Readers/Org/ParserState.hs b/src/Text/Pandoc/Readers/Org/ParserState.hs index 6bed2a547..bdd1dc951 100644 --- a/src/Text/Pandoc/Readers/Org/ParserState.hs +++ b/src/Text/Pandoc/Readers/Org/ParserState.hs @@ -39,7 +39,7 @@ module Text.Pandoc.Readers.Org.ParserState , TodoState (..) , activeTodoMarkers , registerTodoSequence - , F(..) + , F , askF , asksF , trimInlinesF @@ -50,14 +50,13 @@ module Text.Pandoc.Readers.Org.ParserState , optionsToParserState ) where -import Control.Monad (liftM, liftM2) -import Control.Monad.Reader (Reader, ReaderT, ask, asks, local, runReader) +import Control.Monad.Reader (ReaderT, asks, local) import Data.Default (Default (..)) import qualified Data.Map as M import qualified Data.Set as Set -import Text.Pandoc.Builder (Blocks, Inlines, trimInlines) +import Text.Pandoc.Builder (Blocks, Inlines) import Text.Pandoc.Definition (Meta (..), nullMeta) import Text.Pandoc.Options (ReaderOptions (..)) import Text.Pandoc.Logging @@ -65,7 +64,12 @@ import Text.Pandoc.Parsing (HasHeaderMap (..), HasIdentifierList (..), HasLogMessages (..), HasLastStrPosition (..), HasQuoteContext (..), HasReaderOptions (..), ParserContext (..), - QuoteContext (..), SourcePos) + QuoteContext (..), SourcePos, Future, + askF, asksF, returnF, runF, trimInlinesF) + +-- | This is used to delay evaluation until all relevant information has been +-- parsed and made available in the parser state. +type F = Future OrgParserState -- | An inline note / footnote containing the note key and its (inline) value. type OrgNoteRecord = (String, F Blocks) @@ -229,35 +233,3 @@ defaultExportSettings = ExportSettings , exportWithEmail = True , exportWithTodoKeywords = True } - - --- --- Parser state reader --- - --- | Reader monad wrapping the parser state. This is used to delay evaluation --- until all relevant information has been parsed and made available in the --- parser state. See also the newtype of the same name in --- Text.Pandoc.Parsing. -newtype F a = F { unF :: Reader OrgParserState a - } deriving (Functor, Applicative, Monad) - -instance Monoid a => Monoid (F a) where - mempty = return mempty - mappend = liftM2 mappend - mconcat = fmap mconcat . sequence - -runF :: F a -> OrgParserState -> a -runF = runReader . unF - -askF :: F OrgParserState -askF = F ask - -asksF :: (OrgParserState -> a) -> F a -asksF f = F $ asks f - -trimInlinesF :: F Inlines -> F Inlines -trimInlinesF = liftM trimInlines - -returnF :: Monad m => a -> m (F a) -returnF = return . return diff --git a/src/Text/Pandoc/Readers/Org/Parsing.hs b/src/Text/Pandoc/Readers/Org/Parsing.hs index 03c9b1981..464ef9ca6 100644 --- a/src/Text/Pandoc/Readers/Org/Parsing.hs +++ b/src/Text/Pandoc/Readers/Org/Parsing.hs @@ -110,7 +110,7 @@ module Text.Pandoc.Readers.Org.Parsing import Text.Pandoc.Readers.Org.ParserState -import Text.Pandoc.Parsing hiding (anyLine, blanklines, newline, +import Text.Pandoc.Parsing hiding (F, anyLine, blanklines, newline, parseFromString) import qualified Text.Pandoc.Parsing as P -- cgit v1.2.3 From 3362cb89d9034b84ec9504ff55ed0f79aa02f7e4 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sun, 30 Apr 2017 11:28:03 +0200 Subject: Lua module: make Header argument order consistent Attributes are always passed as the last element, making it possible to omit this argument. Argument order for `Header` was wrong and is fixed. --- src/Text/Pandoc/Lua/StackInstances.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index 52151ce64..acf2b7eb1 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -173,7 +173,7 @@ pushBlock lua = \case CodeBlock attr code -> pushViaConstructor lua "CodeBlock" code (LuaAttr attr) DefinitionList items -> pushViaConstructor lua "DefinitionList" items Div attr blcks -> pushViaConstructor lua "Div" blcks (LuaAttr attr) - Header lvl attr inlns -> pushViaConstructor lua "Header" lvl (LuaAttr attr) inlns + Header lvl attr inlns -> pushViaConstructor lua "Header" lvl inlns (LuaAttr attr) HorizontalRule -> pushViaConstructor lua "HorizontalRule" LineBlock blcks -> pushViaConstructor lua "LineBlock" blcks OrderedList lstAttr list -> pushViaConstructor lua "OrderedList" list lstAttr -- cgit v1.2.3 From 2e916b3abfefc0a57964e8f33aec2d37877f9ced Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sun, 30 Apr 2017 11:50:09 +0200 Subject: Lua module: simplify Attributes, rename to Attr Attributes was written to behave much like a normal table, in order to simplify working with it. However, all Attr containing elements were changed to provide panflute-like accessors to Attr components, rendering the previous approach unnecessary. --- src/Text/Pandoc/Lua/StackInstances.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index acf2b7eb1..03f6e06e2 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -279,6 +279,6 @@ newtype LuaAttr = LuaAttr { fromLuaAttr :: Attr } instance StackValue LuaAttr where push lua (LuaAttr (id', classes, kv)) = - pushViaConstructor lua "Attributes" kv id' classes + pushViaConstructor lua "Attr" id' classes kv peek lua idx = fmap LuaAttr <$> peek lua idx valuetype _ = TTABLE -- cgit v1.2.3 From ae21a8bb2a9d892491424f257ed0146c1b2affa2 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sun, 30 Apr 2017 16:14:33 +0200 Subject: Lua filter: fall-back to global filters when none is returned The implicitly defined global filter (i.e. all element filtering functions defined in the global lua environment) is used if no filter is returned from a lua script. This allows to just write top-level functions in order to define a lua filter. E.g function Emph(elem) return pandoc.Strong(elem.content) end --- src/Text/Pandoc/Lua.hs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index ffc57c9c2..f4a22b92a 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -54,13 +54,17 @@ runLuaFilter filterPath args pd = liftIO $ do -- store module in global "pandoc" pushPandocModule lua Lua.setglobal lua "pandoc" + top <- Lua.gettop lua status <- Lua.loadfile lua filterPath if (status /= 0) then do Just luaErrMsg <- Lua.peek lua 1 error luaErrMsg else do - Lua.call lua 0 1 + Lua.call lua 0 Lua.multret + newtop <- Lua.gettop lua + -- Use the implicitly defined global filter if nothing was returned + when (newtop - top < 1) $ pushGlobalFilter lua Just luaFilters <- Lua.peek lua (-1) Lua.push lua args Lua.setglobal lua "PandocParameters" @@ -68,6 +72,13 @@ runLuaFilter filterPath args pd = liftIO $ do Lua.close lua return doc +pushGlobalFilter :: LuaState -> IO () +pushGlobalFilter lua = + Lua.newtable lua + *> Lua.getglobal2 lua "pandoc.global_filter" + *> Lua.call lua 0 1 + *> Lua.rawseti lua (-2) 1 + runAll :: [LuaFilter] -> Pandoc -> IO Pandoc runAll [] = return runAll (x:xs) = walkMWithLuaFilter x >=> runAll xs -- cgit v1.2.3 From 5d529e30c7690146e7f082e0baa616b68da3e594 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Tue, 2 May 2017 09:57:08 +0300 Subject: FB2 writer: Add support for "lang" metadata (#3625) --- src/Text/Pandoc/Writers/FB2.hs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index 238bd397b..fb232e278 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -117,8 +117,13 @@ description meta' = do bt <- booktitle meta' let as = authors meta' dd <- docdate meta' - return $ el "description" - [ el "title-info" (bt ++ as ++ dd) + let lang = case lookupMeta "lang" meta' of + Just (MetaInlines [Str s]) -> [el "lang" $ iso639 s] + Just (MetaString s) -> [el "lang" $ iso639 s] + _ -> [] + where iso639 = takeWhile (/= '-') -- Convert BCP 47 to ISO 639 + return $ el "description" $ + [ el "title-info" (bt ++ as ++ dd ++ lang) , el "document-info" [ el "program-used" "pandoc" ] -- FIXME: +version ] -- cgit v1.2.3 From c0192132cfbe2bc7ee22519b556cf9dbf52bac47 Mon Sep 17 00:00:00 2001 From: David A Roberts <d@vidr.cc> Date: Tue, 2 May 2017 17:00:37 +1000 Subject: Markdown writer: Case-insensitive reference links. (#3616) Ensure that we do not generate reference links whose labels differ only by case. Also allow implicit reference links when the link text and label are identical up to case. Closes #3615. --- src/Text/Pandoc/Writers/Markdown.hs | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 69a3fd8b4..8e3ac3665 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -66,7 +66,7 @@ import Text.Pandoc.Writers.Math (texMathToInlines) import Text.Pandoc.Writers.Shared type Notes = [[Block]] -type Ref = ([Inline], Target, Attr) +type Ref = (Doc, Target, Attr) type Refs = [Ref] type MD m = ReaderT WriterEnv (StateT WriterState m) @@ -235,8 +235,7 @@ keyToMarkdown :: PandocMonad m => WriterOptions -> Ref -> MD m Doc -keyToMarkdown opts (label, (src, tit), attr) = do - label' <- inlineListToMarkdown opts label +keyToMarkdown opts (label', (src, tit), attr) = do let tit' = if null tit then empty else space <> "\"" <> text tit <> "\"" @@ -792,22 +791,25 @@ blockListToMarkdown opts blocks = do else RawBlock "markdown" " " mapM (blockToMarkdown opts) (fixBlocks blocks) >>= return . cat +getKey :: Doc -> Key +getKey = toKey . render Nothing + -- | Get reference for target; if none exists, create unique one and return. -- Prefer label if possible; otherwise, generate a unique key. -getReference :: PandocMonad m => Attr -> [Inline] -> Target -> MD m [Inline] +getReference :: PandocMonad m => Attr -> Doc -> Target -> MD m Doc getReference attr label target = do st <- get + let keys = map (\(l,_,_) -> getKey l) (stRefs st) case find (\(_,t,a) -> t == target && a == attr) (stRefs st) of Just (ref, _, _) -> return ref Nothing -> do - 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)] + label' <- case getKey label `elem` keys of + True -> -- label is used; generate numerical label + case find (\n -> Key n `notElem` keys) $ + map show [1..(10000 :: Integer)] of + Just x -> return $ text x Nothing -> throwError $ PandocSomeError "no unique label" - Nothing -> return label + False -> return label modify (\s -> s{ stRefs = (label', target, attr) : stRefs st }) return label' @@ -1078,15 +1080,15 @@ inlineToMarkdown opts lnk@(Link attr txt (src, tit)) shortcutable <- asks envRefShortcutable let useShortcutRefLinks = shortcutable && isEnabled Ext_shortcut_reference_links opts - ref <- if useRefLinks then getReference attr txt (src, tit) else return [] - reftext <- inlineListToMarkdown opts ref + reftext <- if useRefLinks then getReference attr linktext (src, tit) + else return empty return $ if useAuto then if plain then text srcSuffix else "<" <> text srcSuffix <> ">" else if useRefLinks then let first = "[" <> linktext <> "]" - second = if txt == ref + second = if getKey linktext == getKey reftext then if useShortcutRefLinks then "" else "[]" -- cgit v1.2.3 From 49336ee6eeecc352e248d1262ea1b46070e00243 Mon Sep 17 00:00:00 2001 From: Marc Schreiber <marc.schreiber@fh-aachen.de> Date: Tue, 2 May 2017 10:48:57 +0200 Subject: Add basic \textcolor support to LaTeX reader --- src/Text/Pandoc/Readers/LaTeX.hs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index a54238206..6252293d7 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -680,6 +680,8 @@ inlineCommands = M.fromList $ , ("nohyphens", tok) , ("textnhtt", ttfamily) , ("nhttfamily", ttfamily) + -- textcolor + , ("textcolor", textcolor) ] ++ map ignoreInlines -- these commands will be ignored unless --parse-raw is specified, -- in which case they will appear as raw latex blocks: @@ -756,6 +758,12 @@ dosiunitx = do emptyOr160 unit, unit] +textcolor :: PandocMonad m => LP m Inlines +textcolor = do + skipopts + color <- braced + spanWith ("",[],[("style","color: " ++ color)]) <$> tok + lit :: String -> LP m Inlines lit = pure . str -- cgit v1.2.3 From cd2551c16c1da0404b8de182f17160aebb69219d Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 2 May 2017 16:00:04 +0200 Subject: Added PandocResourceNotFound error. Use this instead of PandocIOError when a resource is not found in path. This improves the error message in this case, see #3629. --- src/Text/Pandoc/Class.hs | 12 ++++-------- src/Text/Pandoc/Error.hs | 3 +++ src/Text/Pandoc/Writers/Docx.hs | 11 ++++------- src/Text/Pandoc/Writers/ICML.hs | 6 +----- src/Text/Pandoc/Writers/ODT.hs | 6 +----- src/Text/Pandoc/Writers/RTF.hs | 5 +---- 6 files changed, 14 insertions(+), 29 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 1afa64c10..ad9901125 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -330,8 +330,7 @@ downloadOrRead sourceURL s = do convertSlash x = x withPaths :: PandocMonad m => [FilePath] -> (FilePath -> m a) -> FilePath -> m a -withPaths [] _ fp = throwError $ PandocIOError fp - (userError "file not found in resource path") +withPaths [] _ fp = throwError $ PandocResourceNotFound fp withPaths (p:ps) action fp = catchError (action (p </> fp)) (\_ -> withPaths ps action fp) @@ -433,20 +432,17 @@ instance PandocMonad PandocPure where modifyPureState $ \st -> st { stUniqStore = us } return u _ -> M.fail "uniq store ran out of elements" - openURL u = throwError $ PandocIOError u $ - userError "Cannot open URL in PandocPure" + openURL u = throwError $ PandocResourceNotFound u readFileLazy fp = do fps <- getsPureState stFiles case infoFileContents <$> getFileInfo fp fps of Just bs -> return (BL.fromStrict bs) - Nothing -> throwError $ PandocIOError fp - (userError "File not found in PureState") + Nothing -> throwError $ PandocResourceNotFound fp readFileStrict fp = do fps <- getsPureState stFiles case infoFileContents <$> getFileInfo fp fps of Just bs -> return bs - Nothing -> throwError $ PandocIOError fp - (userError "File not found in PureState") + Nothing -> throwError $ PandocResourceNotFound fp readDataFile Nothing "reference.docx" = do (B.concat . BL.toChunks . fromArchive) <$> getsPureState stReferenceDocx readDataFile Nothing "reference.odt" = do diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs index 135cb3945..a6db5e047 100644 --- a/src/Text/Pandoc/Error.hs +++ b/src/Text/Pandoc/Error.hs @@ -58,6 +58,7 @@ data PandocError = PandocIOError String IOError | PandocPDFError String | PandocFilterError String String | PandocCouldNotFindDataFileError String + | PandocResourceNotFound String | PandocAppError String deriving (Show, Typeable, Generic) @@ -94,6 +95,8 @@ handleError (Left e) = filtername ++ ":\n" ++ msg PandocCouldNotFindDataFileError fn -> err 97 $ "Could not find data file " ++ fn + PandocResourceNotFound fn -> err 99 $ + "File " ++ fn ++ " not found in resource path" PandocAppError s -> err 1 s err :: Int -> String -> IO a diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index fddec91cc..620f9060e 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -55,7 +55,6 @@ import Text.Pandoc.Class (PandocMonad, report) import qualified Text.Pandoc.Class as P import Text.Pandoc.Compat.Time import Text.Pandoc.Definition -import Text.Pandoc.Error import Text.Pandoc.Generic import Text.Pandoc.Highlighting (highlight) import Text.Pandoc.ImageSize @@ -1303,12 +1302,10 @@ inlineToOpenXML' opts (Image attr alt (src, title)) = do M.insert src (ident, imgpath, mbMimeType, imgElt, img) $ stImages st } return [imgElt]) - (\e -> do case e of - PandocIOError _ e' -> - report $ CouldNotFetchResource src (show e') - e' -> report $ CouldNotFetchResource src (show e') - -- emit alt text - inlinesToOpenXML opts alt) + (\e -> do + report $ CouldNotFetchResource src (show e) + -- emit alt text + inlinesToOpenXML opts alt) br :: Element br = breakElement "textWrapping" diff --git a/src/Text/Pandoc/Writers/ICML.hs b/src/Text/Pandoc/Writers/ICML.hs index cd3cac5a7..4d9998665 100644 --- a/src/Text/Pandoc/Writers/ICML.hs +++ b/src/Text/Pandoc/Writers/ICML.hs @@ -25,7 +25,6 @@ import Network.URI (isURI) import Text.Pandoc.Class (PandocMonad, report) import qualified Text.Pandoc.Class as P import Text.Pandoc.Definition -import Text.Pandoc.Error (PandocError (..)) import Text.Pandoc.ImageSize import Text.Pandoc.Logging import Text.Pandoc.Options @@ -550,10 +549,7 @@ imageICML opts style attr (src, _) = do report $ CouldNotDetermineImageSize src msg return def) (\e -> do - case e of - PandocIOError _ e' -> - report $ CouldNotFetchResource src (show e') - e' -> report $ CouldNotFetchResource src (show e') + report $ CouldNotFetchResource src (show e) return def) let (ow, oh) = sizeInPoints imgS (imgWidth, imgHeight) = desiredSizeInPoints opts attr imgS diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index 395ef0a96..6c6f38dbe 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -39,7 +39,6 @@ import System.FilePath (takeDirectory, takeExtension, (<.>)) import Text.Pandoc.Class (PandocMonad, report) import qualified Text.Pandoc.Class as P import Text.Pandoc.Definition -import Text.Pandoc.Error (PandocError (..)) import Text.Pandoc.ImageSize import Text.Pandoc.Logging import Text.Pandoc.MIME (extensionFromMimeType, getMimeType) @@ -178,10 +177,7 @@ transformPicMath opts (Image attr@(id', cls, _) lab (src,t)) = catchError modify $ \st -> st{ stEntries = entry : entries } return $ Image newattr lab (newsrc, t)) (\e -> do - case e of - PandocIOError _ e' -> - report $ CouldNotFetchResource src (show e') - e' -> report $ CouldNotFetchResource src (show e') + report $ CouldNotFetchResource src (show e) return $ Emph lab) transformPicMath _ (Math t math) = do diff --git a/src/Text/Pandoc/Writers/RTF.hs b/src/Text/Pandoc/Writers/RTF.hs index 67f0fc2e0..7aa2280dd 100644 --- a/src/Text/Pandoc/Writers/RTF.hs +++ b/src/Text/Pandoc/Writers/RTF.hs @@ -92,10 +92,7 @@ rtfEmbedImage opts x@(Image attr _ (src,_)) = catchError report $ CouldNotDetermineMimeType src return x) (\e -> do - case e of - PandocIOError _ e' -> - report $ CouldNotFetchResource src (show e') - e' -> report $ CouldNotFetchResource src (show e') + report $ CouldNotFetchResource src (show e) return x) rtfEmbedImage _ x = return x -- cgit v1.2.3 From e02cfcdeaccf588399579283998a7fb93a5c08f6 Mon Sep 17 00:00:00 2001 From: Mauro Bieg <mb21@users.noreply.github.com> Date: Wed, 3 May 2017 12:13:25 +0200 Subject: Markdown Writer: put space before reference link definitions Fixes #3630 (#3631). Previously the attributes in link reference definitions did not have a space preceding. --- 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 8e3ac3665..655fd8780 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -241,7 +241,7 @@ keyToMarkdown opts (label', (src, tit), attr) = do else space <> "\"" <> text tit <> "\"" return $ nest 2 $ hang 2 ("[" <> label' <> "]:" <> space) (text src <> tit') - <> linkAttributes opts attr + <+> linkAttributes opts attr -- | Return markdown representation of notes. notesToMarkdown :: PandocMonad m => WriterOptions -> [[Block]] -> MD m Doc -- cgit v1.2.3 From 6e55e6837a38b83d0ed4329ab366c699d6c2551f Mon Sep 17 00:00:00 2001 From: schrieveslaach <schrieveslaach@online.de> Date: Wed, 3 May 2017 12:16:48 +0200 Subject: LaTeX reader: Add support for tabularx environment (#3632) --- src/Text/Pandoc/Readers/LaTeX.hs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index a54238206..b88b6eae4 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1156,6 +1156,7 @@ environments = M.fromList , ("table", env "table" $ resetCaption *> skipopts *> blocks >>= addTableCaption) , ("tabular*", env "tabular" $ simpTable True) + , ("tabularx", env "tabularx" $ simpTable True) , ("tabular", env "tabular" $ simpTable False) , ("quote", blockQuote <$> env "quote" blocks) , ("quotation", blockQuote <$> env "quotation" blocks) @@ -1414,7 +1415,11 @@ parseAligns = try $ do let lAlign = AlignLeft <$ char 'l' let rAlign = AlignRight <$ char 'r' let parAlign = AlignLeft <$ (char 'p' >> braced) - let alignChar = cAlign <|> lAlign <|> rAlign <|> parAlign + -- algins from tabularx + let xAlign = AlignLeft <$ char 'X' + let mAlign = AlignLeft <$ (char 'm' >> braced) + let bAlign = AlignLeft <$ (char 'b' >> braced) + let alignChar = cAlign <|> lAlign <|> rAlign <|> parAlign <|> xAlign <|> mAlign <|> bAlign let alignPrefix = char '>' >> braced let alignSuffix = char '<' >> braced let alignSpec = do -- cgit v1.2.3 From 79855ef934175c9a8890653375e05735d8b05a8d Mon Sep 17 00:00:00 2001 From: David A Roberts <d@vidr.cc> Date: Wed, 3 May 2017 20:19:45 +1000 Subject: Markdown writer: better escaping for links (#3628) Previously the Markdown writer would sometimes create links where there were none in the source. This is now avoided by selectively escaping bracket characters when they occur in a place where a link might be created. Closes #3619. --- src/Text/Pandoc/Writers/Markdown.hs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 655fd8780..7c0874278 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -821,7 +821,8 @@ inlineListToMarkdown opts lst = do where go [] = return empty go (i:is) = case i of (Link _ _ _) -> case is of - -- If a link is followed by another link or '[' we don't shortcut + -- If a link is followed by another link, or '[', '(' or ':' + -- then we don't shortcut (Link _ _ _):_ -> unshortcutable Space:(Link _ _ _):_ -> unshortcutable Space:(Str('[':_)):_ -> unshortcutable @@ -831,9 +832,17 @@ inlineListToMarkdown opts lst = do SoftBreak:(Str('[':_)):_ -> unshortcutable SoftBreak:(RawInline _ ('[':_)):_ -> unshortcutable SoftBreak:(Cite _ _):_ -> unshortcutable + LineBreak:(Link _ _ _):_ -> unshortcutable + LineBreak:(Str('[':_)):_ -> unshortcutable + LineBreak:(RawInline _ ('[':_)):_ -> unshortcutable + LineBreak:(Cite _ _):_ -> unshortcutable (Cite _ _):_ -> unshortcutable Str ('[':_):_ -> unshortcutable + Str ('(':_):_ -> unshortcutable + Str (':':_):_ -> unshortcutable (RawInline _ ('[':_)):_ -> unshortcutable + (RawInline _ ('(':_)):_ -> unshortcutable + (RawInline _ (':':_)):_ -> unshortcutable (RawInline _ (' ':'[':_)):_ -> unshortcutable _ -> shortcutable _ -> shortcutable -- cgit v1.2.3 From df23d96c8991b215ead8ceb11607c5bebfb1f6db Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Tue, 2 May 2017 23:41:45 +0200 Subject: Generalize tableWith, gridTableWith The parsing functions `tableWith` and `gridTableWith` are generalized to work with more parsers. The parser state only has to be an instance of the `HasOptions` class instead of requiring a concrete type. Block parsers are required to return blocks wrapped into a monad, as this makes it possible to use parsers returning results wrapped in `Future`s. --- src/Text/Pandoc/Parsing.hs | 49 ++++++++++++++++++++++-------------------- src/Text/Pandoc/Readers/RST.hs | 12 ++++++++--- 2 files changed, 35 insertions(+), 26 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index a6d3cd46a..e0c0e36d6 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -762,21 +762,22 @@ lineBlockLines = try $ do -- | Parse a table using 'headerParser', 'rowParser', -- 'lineParser', and 'footerParser'. -tableWith :: Stream s m Char - => 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 Blocks +tableWith :: (Stream s m Char, HasReaderOptions st, + Functor mf, Applicative mf, Monad mf) + => ParserT s st m (mf [Blocks], [Alignment], [Int]) + -> ([Int] -> ParserT s st m (mf [Blocks])) + -> ParserT s st m sep + -> ParserT s st m end + -> ParserT s st m (mf Blocks) tableWith headerParser rowParser lineParser footerParser = try $ do (heads, aligns, indices) <- headerParser - lines' <- rowParser indices `sepEndBy1` lineParser + lines' <- sequence <$> rowParser indices `sepEndBy1` lineParser footerParser numColumns <- getOption readerColumns let widths = if (indices == []) then replicate (length aligns) 0.0 else widthsFromIndices numColumns indices - return $ B.table mempty (zip 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 @@ -809,10 +810,11 @@ widthsFromIndices numColumns' indices = -- (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). -gridTableWith :: Stream [Char] m Char - => ParserT [Char] ParserState m Blocks -- ^ Block list parser - -> Bool -- ^ Headerless table - -> ParserT [Char] ParserState m Blocks +gridTableWith :: (Stream [Char] m Char, HasReaderOptions st, + Functor mf, Applicative mf, Monad mf) + => ParserT [Char] st m (mf Blocks) -- ^ Block list parser + -> Bool -- ^ Headerless table + -> ParserT [Char] st m (mf Blocks) gridTableWith blocks headless = tableWith (gridTableHeader headless blocks) (gridTableRow blocks) (gridTableSep '-') gridTableFooter @@ -835,14 +837,14 @@ removeFinalBar = reverse . dropWhile (`elem` " \t") . dropWhile (=='|') . reverse -- | Separator between rows of grid table. -gridTableSep :: Stream s m Char => Char -> ParserT s ParserState m Char +gridTableSep :: Stream s m Char => Char -> ParserT s st m Char gridTableSep ch = try $ gridDashedLines ch >> return '\n' -- | Parse header for a grid table. -gridTableHeader :: Stream [Char] m Char +gridTableHeader :: (Stream [Char] m Char, Functor mf, Applicative mf, Monad mf) => Bool -- ^ Headerless table - -> ParserT [Char] ParserState m Blocks - -> ParserT [Char] ParserState m ([Blocks], [Alignment], [Int]) + -> ParserT [Char] st m (mf Blocks) + -> ParserT [Char] st m (mf [Blocks], [Alignment], [Int]) gridTableHeader headless blocks = try $ do optional blanklines dashes <- gridDashedLines '-' @@ -862,25 +864,26 @@ gridTableHeader headless blocks = try $ do then replicate (length dashes) "" else map (intercalate " ") $ transpose $ map (gridTableSplitLine indices) rawContent - heads <- mapM (parseFromString blocks) $ map trim rawHeads + heads <- fmap sequence . mapM (parseFromString blocks) $ map trim rawHeads return (heads, aligns, indices) -gridTableRawLine :: Stream s m Char => [Int] -> ParserT s ParserState m [String] +gridTableRawLine :: Stream s m Char => [Int] -> ParserT s st m [String] gridTableRawLine indices = do char '|' line <- many1Till anyChar newline return (gridTableSplitLine indices line) -- | Parse row of grid table. -gridTableRow :: Stream [Char] m Char - => ParserT [Char] ParserState m Blocks +gridTableRow :: (Stream [Char] m Char, Functor mf, Applicative mf, Monad mf) + => ParserT [Char] st m (mf Blocks) -> [Int] - -> ParserT [Char] ParserState m [Blocks] + -> ParserT [Char] st m (mf [Blocks]) gridTableRow blocks indices = do colLines <- many1 (gridTableRawLine indices) let cols = map ((++ "\n") . unlines . removeOneLeadingSpace) $ transpose colLines - mapM (liftM compactifyCell . parseFromString blocks) cols + cells <- sequence <$> mapM (parseFromString blocks) cols + return $ fmap (map compactifyCell) cells removeOneLeadingSpace :: [String] -> [String] removeOneLeadingSpace xs = @@ -894,7 +897,7 @@ compactifyCell :: Blocks -> Blocks compactifyCell bs = head $ compactify [bs] -- | Parse footer for a grid table. -gridTableFooter :: Stream s m Char => ParserT s ParserState m [Char] +gridTableFooter :: Stream s m Char => ParserT s st m [Char] gridTableFooter = blanklines --- diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 7564998ff..628351f36 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -32,6 +32,7 @@ Conversion from reStructuredText to 'Pandoc' document. -} module Text.Pandoc.Readers.RST ( readRST ) where import Control.Monad (guard, liftM, mzero, when) +import Control.Monad.Identity (Identity(..)) import Control.Monad.Except (throwError) import Data.Char (isHexDigit, isSpace, toLower, toUpper) import Data.List (deleteFirstsBy, findIndex, intercalate, isInfixOf, isSuffixOf, @@ -1119,8 +1120,12 @@ simpleTable :: PandocMonad m => Bool -- ^ Headerless table -> RSTParser m Blocks simpleTable headless = do - tbl <- tableWith (simpleTableHeader headless) simpleTableRow - sep simpleTableFooter + let wrapIdFst (a, b, c) = (Identity a, b, c) + wrapId = fmap Identity + tbl <- runIdentity <$> tableWith + (wrapIdFst <$> simpleTableHeader headless) + (wrapId <$> simpleTableRow) + sep simpleTableFooter -- Simple tables get 0s for relative column widths (i.e., use default) case B.toList tbl of [Table c a _w h l] -> return $ B.singleton $ @@ -1134,7 +1139,8 @@ simpleTable headless = do gridTable :: PandocMonad m => Bool -- ^ Headerless table -> RSTParser m Blocks -gridTable headerless = gridTableWith parseBlocks headerless +gridTable headerless = runIdentity <$> + gridTableWith (Identity <$> parseBlocks) headerless table :: PandocMonad m => RSTParser m Blocks table = gridTable False <|> simpleTable False <|> -- cgit v1.2.3 From d9439808f2fe226aad027c8c9d0a38217a1e8c34 Mon Sep 17 00:00:00 2001 From: Marc Schreiber <marc.schreiber@fh-aachen.de> Date: Wed, 3 May 2017 12:00:30 +0200 Subject: Add block version of \textcolor --- src/Text/Pandoc/Readers/LaTeX.hs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 6252293d7..1c1aa4c62 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -393,6 +393,8 @@ blockCommands = M.fromList $ , ("graphicspath", graphicsPath) -- hyperlink , ("hypertarget", braced >> grouped block) + -- textcolor + , ("textcolor", blockTextcolor) ] ++ map ignoreBlocks -- these commands will be ignored unless --parse-raw is specified, -- in which case they will appear as raw latex blocks @@ -414,6 +416,12 @@ blockCommands = M.fromList $ , "pagebreak" ] +blockTextcolor :: PandocMonad m => LP m Blocks +blockTextcolor = do + skipopts + color <- braced + return <$> divWith ("",[],[("style","color: " ++ color)]) $ block + graphicsPath :: PandocMonad m => LP m Blocks graphicsPath = do ps <- bgroup *> (manyTill braced egroup) @@ -681,7 +689,7 @@ inlineCommands = M.fromList $ , ("textnhtt", ttfamily) , ("nhttfamily", ttfamily) -- textcolor - , ("textcolor", textcolor) + , ("textcolor", inlineTextcolor) ] ++ map ignoreInlines -- these commands will be ignored unless --parse-raw is specified, -- in which case they will appear as raw latex blocks: @@ -693,6 +701,12 @@ inlineCommands = M.fromList $ , "pagebreak" ] +inlineTextcolor :: PandocMonad m => LP m Inlines +inlineTextcolor = do + skipopts + color <- braced + spanWith ("",[],[("style","color: " ++ color)]) <$> tok + ttfamily :: PandocMonad m => LP m Inlines ttfamily = (code . stringify . toList) <$> tok @@ -758,12 +772,6 @@ dosiunitx = do emptyOr160 unit, unit] -textcolor :: PandocMonad m => LP m Inlines -textcolor = do - skipopts - color <- braced - spanWith ("",[],[("style","color: " ++ color)]) <$> tok - lit :: String -> LP m Inlines lit = pure . str -- cgit v1.2.3 From 1728d4e60983e050be4bfb5b8c2e6065b4dd198a Mon Sep 17 00:00:00 2001 From: Marc Schreiber <marc.schreiber@fh-aachen.de> Date: Wed, 3 May 2017 13:39:38 +0200 Subject: \textcolor works as inline and block command --- 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 1c1aa4c62..58ed97b04 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -420,7 +420,7 @@ blockTextcolor :: PandocMonad m => LP m Blocks blockTextcolor = do skipopts color <- braced - return <$> divWith ("",[],[("style","color: " ++ color)]) $ block + divWith ("",[],[("style","color: " ++ color)]) <$> grouped block graphicsPath :: PandocMonad m => LP m Blocks graphicsPath = do -- cgit v1.2.3 From 57cba3f1d5aa682df4ca8aafc3bc1d2ed4ead911 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Wed, 3 May 2017 22:43:34 +0200 Subject: Org reader: support table.el tables Closes #3314 --- src/Text/Pandoc/Readers/Org/BlockStarts.hs | 5 +++++ src/Text/Pandoc/Readers/Org/Blocks.hs | 20 ++++++++++++-------- src/Text/Pandoc/Readers/Org/Parsing.hs | 1 + 3 files changed, 18 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/BlockStarts.hs b/src/Text/Pandoc/Readers/Org/BlockStarts.hs index cc2e82d5b..f05725f16 100644 --- a/src/Text/Pandoc/Readers/Org/BlockStarts.hs +++ b/src/Text/Pandoc/Readers/Org/BlockStarts.hs @@ -61,6 +61,10 @@ headerStart = try $ tableStart :: Monad m => OrgParser m Char tableStart = try $ skipSpaces *> char '|' +gridTableStart :: Monad m => OrgParser m () +gridTableStart = try $ skipSpaces <* char '+' <* char '-' + + latexEnvStart :: Monad m => OrgParser m String latexEnvStart = try $ do skipSpaces *> string "\\begin{" @@ -126,6 +130,7 @@ endOfBlock = lookAhead . try $ do , hline , metaLineStart , commentLineStart + , gridTableStart , void noteMarker , void tableStart , void drawerStart diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index b0a19b833..89c076869 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -755,7 +755,11 @@ data OrgTable = OrgTable } table :: PandocMonad m => OrgParser m (F Blocks) -table = try $ do +table = gridTableWith blocks True <|> orgTable + +-- | A normal org table +orgTable :: PandocMonad m => OrgParser m (F Blocks) +orgTable = try $ do -- don't allow a table on the first line of a list item; org requires that -- tables start at first non-space character on the line let isFirstInListItem st = (orgStateParserContext st == ListItemState) && @@ -854,28 +858,28 @@ normalizeTable (OrgTable colProps heads rows) = rowToContent :: OrgTable -> OrgTableRow -> F OrgTable -rowToContent orgTable row = +rowToContent tbl row = case row of OrgHlineRow -> return singleRowPromotedToHeader OrgAlignRow props -> return . setProperties $ props OrgContentRow cs -> appendToBody cs where singleRowPromotedToHeader :: OrgTable - singleRowPromotedToHeader = case orgTable of + singleRowPromotedToHeader = case tbl of OrgTable{ orgTableHeader = [], orgTableRows = b:[] } -> - orgTable{ orgTableHeader = b , orgTableRows = [] } - _ -> orgTable + tbl{ orgTableHeader = b , orgTableRows = [] } + _ -> tbl setProperties :: [ColumnProperty] -> OrgTable - setProperties ps = orgTable{ orgTableColumnProperties = ps } + setProperties ps = tbl{ orgTableColumnProperties = ps } appendToBody :: F [Blocks] -> F OrgTable appendToBody frow = do newRow <- frow - let oldRows = orgTableRows orgTable + let oldRows = orgTableRows tbl -- NOTE: This is an inefficient O(n) operation. This should be changed -- if performance ever becomes a problem. - return orgTable{ orgTableRows = oldRows ++ [newRow] } + return tbl{ orgTableRows = oldRows ++ [newRow] } -- diff --git a/src/Text/Pandoc/Readers/Org/Parsing.hs b/src/Text/Pandoc/Readers/Org/Parsing.hs index 464ef9ca6..aa3a08279 100644 --- a/src/Text/Pandoc/Readers/Org/Parsing.hs +++ b/src/Text/Pandoc/Readers/Org/Parsing.hs @@ -70,6 +70,7 @@ module Text.Pandoc.Readers.Org.Parsing , dash , ellipses , citeKey + , gridTableWith -- * Re-exports from Text.Pandoc.Parsec , runParser , runParserT -- cgit v1.2.3 From 430e6be1f41358bed21b2edf02bcdb41dbee88cc Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Thu, 4 May 2017 12:36:52 +0300 Subject: Muse writer: omit automatic header identifiers (#3633) --- src/Text/Pandoc/Writers/Muse.hs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index 8f6493975..8b083e2c6 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -53,6 +53,7 @@ import Text.Pandoc.Shared import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Writers.Math import Text.Pandoc.Writers.Shared +import qualified Data.Set as Set type Notes = [[Block]] data WriterState = @@ -60,6 +61,7 @@ data WriterState = , stOptions :: WriterOptions , stTopLevel :: Bool , stInsideBlock :: Bool + , stIds :: Set.Set String } -- | Convert Pandoc to Muse. @@ -72,6 +74,7 @@ writeMuse opts document = , stOptions = opts , stTopLevel = True , stInsideBlock = False + , stIds = Set.empty } in evalStateT (pandocToMuse document) st @@ -184,8 +187,14 @@ blockToMuse (DefinitionList items) = do let ind = offset label'' return $ hang ind label'' contents blockToMuse (Header level (ident,_,_) inlines) = do + opts <- gets stOptions contents <- inlineListToMuse inlines - let attr' = if null ident + + ids <- gets stIds + let autoId = uniqueIdent inlines ids + modify $ \st -> st{ stIds = Set.insert autoId ids } + + let attr' = if null ident || (isEnabled Ext_auto_identifiers opts && ident == autoId) then empty else "#" <> text ident <> cr let header' = text $ replicate level '*' -- cgit v1.2.3 From 1668998c460be69fb5b26c3ba727c878394be331 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 4 May 2017 16:36:35 +0200 Subject: Include `backtick_code_blocks` extension in `mardkown_mmd`. Closes #3637. --- src/Text/Pandoc/Extensions.hs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 24f7d56ec..374fae2c1 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -271,6 +271,7 @@ multimarkdownExtensions = extensionsFromList -- not to include these: , Ext_superscript , Ext_subscript + , Ext_backtick_code_blocks ] -- | Language extensions to be used with strict markdown. -- cgit v1.2.3 From 4ed6d9165672917cb9450578c8f7d84121ecfc24 Mon Sep 17 00:00:00 2001 From: Marc Schreiber <schrieveslaach@online.de> Date: Thu, 4 May 2017 16:48:27 +0200 Subject: \textcolor will be parse as span at the beginning of a paragraph --- 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 58ed97b04..a34be46e2 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -420,7 +420,7 @@ blockTextcolor :: PandocMonad m => LP m Blocks blockTextcolor = do skipopts color <- braced - divWith ("",[],[("style","color: " ++ color)]) <$> grouped block + divWith ("",[],[("style","color: " ++ color)]) <$> grouped block <* notFollowedBy inline graphicsPath :: PandocMonad m => LP m Blocks graphicsPath = do -- cgit v1.2.3 From c1b45adda09bae3c44e9e05832d54682696296c4 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 5 May 2017 17:03:27 +0200 Subject: SelfContained: Handle url() inside material retrieved from url(). This can happen e.g. with an @import of a google web font. (What is imported is some CSS which contains an url reference to the font itself.) Also, allow unescaped pipe (|) in URL. This is intended to help with #3629, but it doesn't seem to work. --- src/Text/Pandoc/SelfContained.hs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/SelfContained.hs b/src/Text/Pandoc/SelfContained.hs index 53cb4a4b5..6391ef0e0 100644 --- a/src/Text/Pandoc/SelfContained.hs +++ b/src/Text/Pandoc/SelfContained.hs @@ -178,12 +178,21 @@ pCSSUrl sourceURL d = P.try $ do P.char ')' let fallback = B.pack ("url(" ++ maybe "" (:[]) quote ++ trim url ++ maybe "" (:[]) quote ++ ")") - case trim url of + -- pipes are used in URLs provided by Google Code fonts + -- but parseURI doesn't like them, so we escape them: + case escapeURIString (/='|') (trim url) of '#':_ -> return fallback 'd':'a':'t':'a':':':_ -> return fallback u -> do let url' = if isURI u then u else d </> u - enc <- lift $ getDataURI sourceURL "" url' - return (B.pack $ "url(" ++ enc ++ ")") + res <- lift $ getData sourceURL "" url' + case res of + Left uri -> return (B.pack $ "url(" ++ uri ++ ")") + Right (mt, raw) -> do + -- note that the downloaded content may + -- itself contain url(...). + raw' <- cssURLs sourceURL d raw + let enc = makeDataURI (mt, raw') + return (B.pack $ "url(" ++ enc ++ ")") getDataURI :: PandocMonad m => Maybe String -> MimeType -> String -> m String getDataURI sourceURL mimetype src = do -- cgit v1.2.3 From 89b3fcc8e050def3779fed716d70bfd4e7120a6b Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 5 May 2017 23:03:31 +0200 Subject: SelfContained: special handling for css @import. We now avoid creating a data URI for the url under an @import. --- src/Text/Pandoc/SelfContained.hs | 41 +++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/SelfContained.hs b/src/Text/Pandoc/SelfContained.hs index 6391ef0e0..a5ae0a929 100644 --- a/src/Text/Pandoc/SelfContained.hs +++ b/src/Text/Pandoc/SelfContained.hs @@ -31,6 +31,7 @@ offline, by incorporating linked images, CSS, and scripts into the HTML using data URIs. -} module Text.Pandoc.SelfContained ( makeDataURI, makeSelfContained ) where +import Data.Monoid ((<>)) import Codec.Compression.GZip as Gzip import Control.Applicative ((<|>)) import Control.Monad.Except (throwError) @@ -149,7 +150,32 @@ cssURLs sourceURL d orig = do parseCSSUrls :: PandocMonad m => Maybe String -> FilePath -> ParsecT ByteString () m ByteString parseCSSUrls sourceURL d = B.concat <$> P.many - (pCSSWhite <|> pCSSComment <|> pCSSUrl sourceURL d <|> pCSSOther) + ( pCSSWhite + <|> pCSSComment + <|> pCSSImport sourceURL d + <|> (pCSSUrl >>= processCSSUrl sourceURL d) + <|> pCSSOther + ) + +pCSSImport :: PandocMonad m + => Maybe String -> FilePath -> ParsecT ByteString () m ByteString +pCSSImport sourceURL d = P.try $ do + P.string "@import" + P.spaces + url <- pCSSUrl + P.spaces + media <- P.manyTill P.anyChar (P.char ';') + let u = escapeURIString (/='|') (trim url) + let url' = if isURI u then u else d </> u + res <- lift $ getData sourceURL "" url' + case res of + Left uri -> return (B.pack $ "url(" ++ uri ++ ")") + Right (_, raw) -> do + raw' <- cssURLs sourceURL d raw + if null media + then return raw' + else return $ B.pack ("@media " ++ media ++ "{\n") <> raw' <> + B.pack "}" -- Note: some whitespace in CSS is significant, so we can't collapse it! pCSSWhite :: PandocMonad m => ParsecT ByteString () m ByteString @@ -168,16 +194,21 @@ pCSSOther = do (B.singleton <$> P.char '/') pCSSUrl :: PandocMonad m - => Maybe String -> FilePath -> ParsecT ByteString () m ByteString -pCSSUrl sourceURL d = P.try $ do + => ParsecT ByteString () m String +pCSSUrl = P.try $ do P.string "url(" P.spaces quote <- P.option Nothing (Just <$> P.oneOf "\"'") url <- P.manyTill P.anyChar (maybe (P.lookAhead (P.char ')')) P.char quote) P.spaces P.char ')' - let fallback = B.pack ("url(" ++ maybe "" (:[]) quote ++ trim url ++ - maybe "" (:[]) quote ++ ")") + return url + +processCSSUrl :: PandocMonad m + => Maybe String -> FilePath -> String + -> ParsecT ByteString () m ByteString +processCSSUrl sourceURL d url = do + let fallback = B.pack ("url('" ++ trim url ++ "')") -- pipes are used in URLs provided by Google Code fonts -- but parseURI doesn't like them, so we escape them: case escapeURIString (/='|') (trim url) of -- cgit v1.2.3 From 9f0a80457fb0ab343af651af8c7bc6f9dc467f55 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 5 May 2017 23:23:49 +0200 Subject: Revert "SelfContained: special handling for css @import." This reverts commit 89b3fcc8e050def3779fed716d70bfd4e7120a6b. --- src/Text/Pandoc/SelfContained.hs | 41 +++++----------------------------------- 1 file changed, 5 insertions(+), 36 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/SelfContained.hs b/src/Text/Pandoc/SelfContained.hs index a5ae0a929..6391ef0e0 100644 --- a/src/Text/Pandoc/SelfContained.hs +++ b/src/Text/Pandoc/SelfContained.hs @@ -31,7 +31,6 @@ offline, by incorporating linked images, CSS, and scripts into the HTML using data URIs. -} module Text.Pandoc.SelfContained ( makeDataURI, makeSelfContained ) where -import Data.Monoid ((<>)) import Codec.Compression.GZip as Gzip import Control.Applicative ((<|>)) import Control.Monad.Except (throwError) @@ -150,32 +149,7 @@ cssURLs sourceURL d orig = do parseCSSUrls :: PandocMonad m => Maybe String -> FilePath -> ParsecT ByteString () m ByteString parseCSSUrls sourceURL d = B.concat <$> P.many - ( pCSSWhite - <|> pCSSComment - <|> pCSSImport sourceURL d - <|> (pCSSUrl >>= processCSSUrl sourceURL d) - <|> pCSSOther - ) - -pCSSImport :: PandocMonad m - => Maybe String -> FilePath -> ParsecT ByteString () m ByteString -pCSSImport sourceURL d = P.try $ do - P.string "@import" - P.spaces - url <- pCSSUrl - P.spaces - media <- P.manyTill P.anyChar (P.char ';') - let u = escapeURIString (/='|') (trim url) - let url' = if isURI u then u else d </> u - res <- lift $ getData sourceURL "" url' - case res of - Left uri -> return (B.pack $ "url(" ++ uri ++ ")") - Right (_, raw) -> do - raw' <- cssURLs sourceURL d raw - if null media - then return raw' - else return $ B.pack ("@media " ++ media ++ "{\n") <> raw' <> - B.pack "}" + (pCSSWhite <|> pCSSComment <|> pCSSUrl sourceURL d <|> pCSSOther) -- Note: some whitespace in CSS is significant, so we can't collapse it! pCSSWhite :: PandocMonad m => ParsecT ByteString () m ByteString @@ -194,21 +168,16 @@ pCSSOther = do (B.singleton <$> P.char '/') pCSSUrl :: PandocMonad m - => ParsecT ByteString () m String -pCSSUrl = P.try $ do + => Maybe String -> FilePath -> ParsecT ByteString () m ByteString +pCSSUrl sourceURL d = P.try $ do P.string "url(" P.spaces quote <- P.option Nothing (Just <$> P.oneOf "\"'") url <- P.manyTill P.anyChar (maybe (P.lookAhead (P.char ')')) P.char quote) P.spaces P.char ')' - return url - -processCSSUrl :: PandocMonad m - => Maybe String -> FilePath -> String - -> ParsecT ByteString () m ByteString -processCSSUrl sourceURL d url = do - let fallback = B.pack ("url('" ++ trim url ++ "')") + let fallback = B.pack ("url(" ++ maybe "" (:[]) quote ++ trim url ++ + maybe "" (:[]) quote ++ ")") -- pipes are used in URLs provided by Google Code fonts -- but parseURI doesn't like them, so we escape them: case escapeURIString (/='|') (trim url) of -- cgit v1.2.3 From da8c153a6872a040440f8853a37f559bb3b26b02 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sat, 6 May 2017 10:59:40 +0200 Subject: Org reader: support macros Closes: #3401 --- src/Text/Pandoc/Readers/Org/Inlines.hs | 21 +++++++++++++++++++++ src/Text/Pandoc/Readers/Org/Meta.hs | 27 ++++++++++++++++++++++++++- src/Text/Pandoc/Readers/Org/ParserState.hs | 18 ++++++++++++++++++ 3 files changed, 65 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/Inlines.hs b/src/Text/Pandoc/Readers/Org/Inlines.hs index 64ffb8ef5..5772e4157 100644 --- a/src/Text/Pandoc/Readers/Org/Inlines.hs +++ b/src/Text/Pandoc/Readers/Org/Inlines.hs @@ -120,6 +120,7 @@ inline = , superscript , inlineLaTeX , exportSnippet + , macro , smart , symbol ] <* (guard =<< newlinesCountWithinLimits) @@ -839,6 +840,26 @@ exportSnippet = try $ do snippet <- manyTill anyChar (try $ string "@@") returnF $ B.rawInline format snippet +macro :: PandocMonad m => OrgParser m (F Inlines) +macro = try $ do + recursionDepth <- orgStateMacroDepth <$> getState + guard $ recursionDepth < 15 + string "{{{" + name <- many alphaNum + args <- ([] <$ string "}}}") + <|> char '(' *> argument `sepBy` char ',' <* eoa + expander <- lookupMacro name <$> getState + case expander of + Nothing -> mzero + Just fn -> do + updateState $ \s -> s { orgStateMacroDepth = recursionDepth + 1 } + res <- parseFromString (mconcat <$> many inline) $ fn args + updateState $ \s -> s { orgStateMacroDepth = recursionDepth } + return res + where + argument = many $ notFollowedBy eoa *> noneOf "," + eoa = string ")}}}" + smart :: PandocMonad m => OrgParser m (F Inlines) smart = do guardEnabled Ext_smart diff --git a/src/Text/Pandoc/Readers/Org/Meta.hs b/src/Text/Pandoc/Readers/Org/Meta.hs index 7938fc6c6..8c362f209 100644 --- a/src/Text/Pandoc/Readers/Org/Meta.hs +++ b/src/Text/Pandoc/Readers/Org/Meta.hs @@ -46,7 +46,7 @@ import Text.Pandoc.Definition import Control.Monad (mzero, void) import Data.Char (toLower) -import Data.List (intersperse) +import Data.List (intersperse, sort) import qualified Data.Map as M import Network.HTTP (urlEncode) @@ -151,6 +151,7 @@ optionLine = try $ do "todo" -> todoSequence >>= updateState . registerTodoSequence "seq_todo" -> todoSequence >>= updateState . registerTodoSequence "typ_todo" -> todoSequence >>= updateState . registerTodoSequence + "macro" -> macroDefinition >>= updateState . registerMacro _ -> mzero addLinkFormat :: Monad m => String @@ -218,3 +219,27 @@ todoSequence = try $ do let todoMarkers = map (TodoMarker Todo) todo doneMarkers = map (TodoMarker Done) done in todoMarkers ++ doneMarkers + +macroDefinition :: Monad m => OrgParser m (String, [String] -> String) +macroDefinition = try $ do + macroName <- many1 nonspaceChar <* skipSpaces + firstPart <- expansionPart + (elemOrder, parts) <- unzip <$> many ((,) <$> placeholder <*> expansionPart) + let expander = mconcat . alternate (firstPart:parts) . reorder elemOrder + return (macroName, expander) + where + placeholder :: Monad m => OrgParser m Int + placeholder = try . fmap read $ char '$' *> many1 digit + + expansionPart :: Monad m => OrgParser m String + expansionPart = try $ many (notFollowedBy placeholder *> noneOf "\n\r") + + alternate :: [a] -> [a] -> [a] + alternate [] ys = ys + alternate xs [] = xs + alternate (x:xs) (y:ys) = x : y : alternate xs ys + + reorder :: [Int] -> [String] -> [String] + reorder perm xs = + let element n = take 1 $ drop (n - 1) xs + in concatMap element perm diff --git a/src/Text/Pandoc/Readers/Org/ParserState.hs b/src/Text/Pandoc/Readers/Org/ParserState.hs index bdd1dc951..e47565814 100644 --- a/src/Text/Pandoc/Readers/Org/ParserState.hs +++ b/src/Text/Pandoc/Readers/Org/ParserState.hs @@ -39,6 +39,9 @@ module Text.Pandoc.Readers.Org.ParserState , TodoState (..) , activeTodoMarkers , registerTodoSequence + , MacroExpander + , lookupMacro + , registerMacro , F , askF , asksF @@ -78,6 +81,8 @@ type OrgNoteTable = [OrgNoteRecord] -- | Map of functions for link transformations. The map key is refers to the -- link-type, the corresponding function transforms the given link string. type OrgLinkFormatters = M.Map String (String -> String) +-- | Macro expander function +type MacroExpander = [String] -> String -- | The states in which a todo item can be data TodoState = Todo | Done @@ -105,6 +110,8 @@ data OrgParserState = OrgParserState , orgStateLastPreCharPos :: Maybe SourcePos , orgStateLastStrPos :: Maybe SourcePos , orgStateLinkFormatters :: OrgLinkFormatters + , orgStateMacros :: M.Map String MacroExpander + , orgStateMacroDepth :: Int , orgStateMeta :: F Meta , orgStateNotes' :: OrgNoteTable , orgStateOptions :: ReaderOptions @@ -156,6 +163,8 @@ defaultOrgParserState = OrgParserState , orgStateLastPreCharPos = Nothing , orgStateLastStrPos = Nothing , orgStateLinkFormatters = M.empty + , orgStateMacros = M.empty + , orgStateMacroDepth = 0 , orgStateMeta = return nullMeta , orgStateNotes' = [] , orgStateOptions = def @@ -185,6 +194,15 @@ activeTodoSequences st = activeTodoMarkers :: OrgParserState -> TodoSequence activeTodoMarkers = concat . activeTodoSequences +lookupMacro :: String -> OrgParserState -> Maybe MacroExpander +lookupMacro macroName = M.lookup macroName . orgStateMacros + +registerMacro :: (String, MacroExpander) -> OrgParserState -> OrgParserState +registerMacro (name, expander) st = + let curMacros = orgStateMacros st + in st{ orgStateMacros = M.insert name expander curMacros } + + -- -- Export Settings -- cgit v1.2.3 From bf44b885228ae2352777372c7f06b800560d3914 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sat, 6 May 2017 11:32:38 +0200 Subject: Drop redundant import of sort This was left in accidentally. --- src/Text/Pandoc/Readers/Org/Meta.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/Meta.hs b/src/Text/Pandoc/Readers/Org/Meta.hs index 8c362f209..5dc742403 100644 --- a/src/Text/Pandoc/Readers/Org/Meta.hs +++ b/src/Text/Pandoc/Readers/Org/Meta.hs @@ -46,7 +46,7 @@ import Text.Pandoc.Definition import Control.Monad (mzero, void) import Data.Char (toLower) -import Data.List (intersperse, sort) +import Data.List (intersperse) import qualified Data.Map as M import Network.HTTP (urlEncode) -- cgit v1.2.3 From ddf2524477e2a59b36fd37f7e5957ebb3b37c265 Mon Sep 17 00:00:00 2001 From: schrieveslaach <schrieveslaach@online.de> Date: Sat, 6 May 2017 15:09:29 +0200 Subject: =?UTF-8?q?Fix=20keyval=20funtion:=20pandoc=20did=20not=20parse=20?= =?UTF-8?q?options=20in=20braces=20correctly.=E2=80=A6=20(#3642)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix keyval funtion: pandoc did not parse options in braces correctly. Additionally, dot, dash, and colon were no valid characters * Add | as possible option value * Improved code --- 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 b88b6eae4..1ce92a4a2 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1095,7 +1095,7 @@ parseListingsOptions options = keyval :: PandocMonad m => LP m (String, String) keyval = try $ do key <- many1 alphaNum - val <- option "" $ char '=' >> many1 (alphaNum <|> char '.' <|> char '\\') + val <- option "" $ char '=' >> braced <|> (many1 (alphaNum <|> oneOf ".:-|\\")) skipMany spaceChar optional (char ',') skipMany spaceChar -- cgit v1.2.3 From f20c89e24380007a47f3e28889706a6f584bc6e0 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 6 May 2017 22:15:51 +0200 Subject: LaTeX reader: Better handling of comments inside math environments. This solves a problem with commented out `\end{eqnarray}` inside an eqnarray (among other things). Closes #3113. --- src/Text/Pandoc/Readers/LaTeX.hs | 73 +++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 30 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 1ce92a4a2..b13fc215b 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -492,20 +492,20 @@ isBlockCommand s = s `M.member` (blockCommands :: M.Map String (LP PandocPure Bl inlineEnvironments :: PandocMonad m => M.Map String (LP m Inlines) inlineEnvironments = M.fromList - [ ("displaymath", mathEnv id Nothing "displaymath") - , ("math", math <$> verbEnv "math") - , ("equation", mathEnv id Nothing "equation") - , ("equation*", mathEnv id Nothing "equation*") - , ("gather", mathEnv id (Just "gathered") "gather") - , ("gather*", mathEnv id (Just "gathered") "gather*") - , ("multline", mathEnv id (Just "gathered") "multline") - , ("multline*", mathEnv id (Just "gathered") "multline*") - , ("eqnarray", mathEnv id (Just "aligned") "eqnarray") - , ("eqnarray*", mathEnv id (Just "aligned") "eqnarray*") - , ("align", mathEnv id (Just "aligned") "align") - , ("align*", mathEnv id (Just "aligned") "align*") - , ("alignat", mathEnv id (Just "aligned") "alignat") - , ("alignat*", mathEnv id (Just "aligned") "alignat*") + [ ("displaymath", mathEnvWith id Nothing "displaymath") + , ("math", math <$> mathEnv "math") + , ("equation", mathEnvWith id Nothing "equation") + , ("equation*", mathEnvWith id Nothing "equation*") + , ("gather", mathEnvWith id (Just "gathered") "gather") + , ("gather*", mathEnvWith id (Just "gathered") "gather*") + , ("multline", mathEnvWith id (Just "gathered") "multline") + , ("multline*", mathEnvWith id (Just "gathered") "multline*") + , ("eqnarray", mathEnvWith id (Just "aligned") "eqnarray") + , ("eqnarray*", mathEnvWith id (Just "aligned") "eqnarray*") + , ("align", mathEnvWith id (Just "aligned") "align") + , ("align*", mathEnvWith id (Just "aligned") "align*") + , ("alignat", mathEnvWith id (Just "aligned") "alignat") + , ("alignat*", mathEnvWith id (Just "aligned") "alignat*") ] inlineCommands :: PandocMonad m => M.Map String (LP m Inlines) @@ -1187,19 +1187,19 @@ environments = M.fromList , ("obeylines", parseFromString (para . trimInlines . mconcat <$> many inline) =<< intercalate "\\\\\n" . lines <$> verbEnv "obeylines") - , ("displaymath", mathEnv para Nothing "displaymath") - , ("equation", mathEnv para Nothing "equation") - , ("equation*", mathEnv para Nothing "equation*") - , ("gather", mathEnv para (Just "gathered") "gather") - , ("gather*", mathEnv para (Just "gathered") "gather*") - , ("multline", mathEnv para (Just "gathered") "multline") - , ("multline*", mathEnv para (Just "gathered") "multline*") - , ("eqnarray", mathEnv para (Just "aligned") "eqnarray") - , ("eqnarray*", mathEnv para (Just "aligned") "eqnarray*") - , ("align", mathEnv para (Just "aligned") "align") - , ("align*", mathEnv para (Just "aligned") "align*") - , ("alignat", mathEnv para (Just "aligned") "alignat") - , ("alignat*", mathEnv para (Just "aligned") "alignat*") + , ("displaymath", mathEnvWith para Nothing "displaymath") + , ("equation", mathEnvWith para Nothing "equation") + , ("equation*", mathEnvWith para Nothing "equation*") + , ("gather", mathEnvWith para (Just "gathered") "gather") + , ("gather*", mathEnvWith para (Just "gathered") "gather*") + , ("multline", mathEnvWith para (Just "gathered") "multline") + , ("multline*", mathEnvWith para (Just "gathered") "multline*") + , ("eqnarray", mathEnvWith para (Just "aligned") "eqnarray") + , ("eqnarray*", mathEnvWith para (Just "aligned") "eqnarray*") + , ("align", mathEnvWith para (Just "aligned") "align") + , ("align*", mathEnvWith para (Just "aligned") "align*") + , ("alignat", mathEnvWith para (Just "aligned") "alignat") + , ("alignat*", mathEnvWith para (Just "aligned") "alignat*") ] figure :: PandocMonad m => LP m Blocks @@ -1264,19 +1264,32 @@ listenv name p = try $ do updateState $ \st -> st{ stateParserContext = oldCtx } return res -mathEnv :: PandocMonad m => (Inlines -> a) -> Maybe String -> String -> LP m a -mathEnv f innerEnv name = f <$> mathDisplay (inner <$> verbEnv name) +mathEnvWith :: PandocMonad m + => (Inlines -> a) -> Maybe String -> String -> LP m a +mathEnvWith f innerEnv name = f <$> mathDisplay (inner <$> mathEnv name) where inner x = case innerEnv of Nothing -> x Just y -> "\\begin{" ++ y ++ "}\n" ++ x ++ "\\end{" ++ y ++ "}" +mathEnv :: PandocMonad m => String -> LP m String +mathEnv name = do + skipopts + optional blankline + let endEnv = try $ controlSeq "end" *> braced >>= guard . (== name) + charMuncher = skipMany comment *> + (many1 (noneOf "\\%") <|> try (string "\\%") + <|> try (string "\\\\") <|> count 1 anyChar) + res <- concat <$> manyTill charMuncher endEnv + return $ stripTrailingNewlines res + verbEnv :: PandocMonad m => String -> LP m String verbEnv name = do skipopts optional blankline let endEnv = try $ controlSeq "end" *> braced >>= guard . (== name) - res <- manyTill anyChar endEnv + charMuncher = anyChar + res <- manyTill charMuncher endEnv return $ stripTrailingNewlines res fancyverbEnv :: PandocMonad m => String -> LP m Blocks -- cgit v1.2.3 From 82cc7fb0d462401b54bfe5172e7e49ab7b7302d9 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 6 May 2017 22:56:16 +0200 Subject: Markdown reader: improved parsing of indented raw HTML blocks. Previously we inadvertently interpreted indented HTML as code blocks. This was a regression. We now seek to determine the indentation level of the contents of an HTML block, and (optionally) skip that much indentation. As a side effect, indentation may be stripped off of raw HTML blocks, if `markdown_in_html_blocks` is used. This is better than having things interpreted as indented code blocks. Closes #1841. --- src/Text/Pandoc/Readers/Markdown.hs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 5515c735b..691d4d5cf 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -1088,13 +1088,19 @@ rawTeXBlock = do rawHtmlBlocks :: PandocMonad m => MarkdownParser m (F Blocks) rawHtmlBlocks = do (TagOpen tagtype _, raw) <- htmlTag isBlockTag + -- we don't want '<td> text' to be a code block: + skipMany spaceChar + indentlevel <- (blankline >> length <$> many (char ' ')) <|> return 0 -- try to find closing tag -- we set stateInHtmlBlock so that closing tags that can be either block or -- inline will not be parsed as inline tags oldInHtmlBlock <- stateInHtmlBlock <$> getState updateState $ \st -> st{ stateInHtmlBlock = Just tagtype } let closer = htmlTag (\x -> x ~== TagClose tagtype) - contents <- mconcat <$> many (notFollowedBy' closer >> block) + let block' = do notFollowedBy' closer + atMostSpaces indentlevel + block + contents <- mconcat <$> many block' result <- (closer >>= \(_, rawcloser) -> return ( return (B.rawBlock "html" $ stripMarkdownAttribute raw) <> -- cgit v1.2.3 From e15a4badff82a62afd2356c1e1e3211ef4c6eb71 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 7 May 2017 10:34:04 +0200 Subject: Simplify plumbing for document transformation. --- src/Text/Pandoc/App.hs | 46 +++++++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 25 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index c38ebdd84..b8a3c6613 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -68,10 +68,10 @@ import System.IO (stdout) import System.IO.Error (isDoesNotExistError) import Text.Pandoc import Text.Pandoc.Builder (setMeta) -import Text.Pandoc.Class (PandocIO, getLog, withMediaBag) +import Text.Pandoc.Class (PandocIO, getLog, withMediaBag, getMediaBag) import Text.Pandoc.Highlighting (highlightingStyles) import Text.Pandoc.Lua ( runLuaFilter ) -import Text.Pandoc.MediaBag (MediaBag, extractMediaBag, mediaDirectory) +import Text.Pandoc.MediaBag (extractMediaBag, mediaDirectory) import Text.Pandoc.PDF (makePDF) import Text.Pandoc.Process (pipeProcess) import Text.Pandoc.SelfContained (makeSelfContained, makeDataURI) @@ -391,20 +391,16 @@ convertWithOpts opts = do E.throwIO PandocFailOnWarningError return res - let sourceToDoc :: [FilePath] -> PandocIO (Pandoc, MediaBag) + let sourceToDoc :: [FilePath] -> PandocIO Pandoc sourceToDoc sources' = case reader of StringReader r - | optFileScope opts || readerName == "json" -> do - pairs <- mapM - (readSource >=> withMediaBag . r readerOpts) sources - return (mconcat (map fst pairs), mconcat (map snd pairs)) + | optFileScope opts || readerName == "json" -> + mconcat <$> mapM (readSource >=> r readerOpts) sources | otherwise -> - readSources sources' >>= withMediaBag . r readerOpts - ByteStringReader r -> do - pairs <- mapM (readFile' >=> - withMediaBag . r readerOpts) sources - return (mconcat (map fst pairs), mconcat (map snd pairs)) + readSources sources' >>= r readerOpts + ByteStringReader r -> + mconcat <$> mapM (readFile' >=> r readerOpts) sources metadata <- if format == "jats" && lookup "csl" (optMetadata opts) == Nothing && @@ -416,16 +412,15 @@ convertWithOpts opts = do else return $ optMetadata opts runIO' $ do - (doc, media) <- sourceToDoc sources - doc' <- (maybe return (extractMedia media) (optExtractMedia opts) >=> - return . flip (foldr addMetadata) metadata >=> - applyTransforms transforms >=> - applyLuaFilters datadir (optLuaFilters opts) [format] >=> - applyFilters datadir filters' [format]) doc + (doc, media) <- withMediaBag $ sourceToDoc sources >>= + (maybe return extractMedia (optExtractMedia opts) + >=> return . flip (foldr addMetadata) metadata + >=> applyTransforms transforms + >=> applyLuaFilters datadir (optLuaFilters opts) [format] + >=> applyFilters datadir filters' [format]) 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, beamer, context, html5 or ms @@ -445,7 +440,7 @@ convertWithOpts opts = do when (isNothing mbPdfProg) $ liftIO $ E.throwIO $ PandocPDFProgramNotFoundError pdfprog - res <- makePDF pdfprog f writerOptions verbosity media doc' + res <- makePDF pdfprog f writerOptions verbosity media doc case res of Right pdf -> writeFnBinary outputFile pdf Left err' -> liftIO $ @@ -462,7 +457,7 @@ convertWithOpts opts = do format == "docbook") && optAscii opts then toEntities else id - output <- f writerOptions doc' + output <- f writerOptions doc selfcontain (output ++ ['\n' | not standalone]) >>= writerFn outputFile . handleEntities @@ -728,12 +723,13 @@ defaultWriterName x = -- Transformations of a Pandoc document post-parsing: -extractMedia :: MonadIO m => MediaBag -> FilePath -> Pandoc -> m Pandoc -extractMedia media dir d = +extractMedia :: FilePath -> Pandoc -> PandocIO Pandoc +extractMedia dir d = do + media <- getMediaBag case [fp | (fp, _, _) <- mediaDirectory media] of [] -> return d fps -> do - extractMediaBag True dir media + liftIO $ extractMediaBag True dir media return $ walk (adjustImagePath dir fps) d adjustImagePath :: FilePath -> [FilePath] -> Inline -> Inline -- cgit v1.2.3 From 400fe3188e3f5a3e48700ae114a0da05ae6e599a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 7 May 2017 11:45:33 +0200 Subject: Allow `--extract-media` to work with non-binary input formats. If `--extract-media` is supplied with a non-binary input format, pandoc will attempt to extract the contents of all linked images, whether in local files, data: uris, or external uris. They will be named based on the sha1 hash of the contents. Closes #1583, #2289. Notes: - One thing that is slightly subideal with this commit is that identical resources will be downloaded multiple times. To improve this we could have mediabag store an original filename/url + a new name. - We might think about reusing some of this code, since more or less the same thing is done in the Docx, EPUB, PDF writers (with slight variations). --- src/Text/Pandoc/App.hs | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index b8a3c6613..212ae7fe2 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -45,6 +45,7 @@ import Data.Aeson (eitherDecode', encode) import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as B import Data.Char (toLower, toUpper) +import Data.Digest.Pure.SHA (sha1, showDigest) import qualified Data.Set as Set import Data.Foldable (foldrM) import Data.List (intercalate, isPrefixOf, isSuffixOf, sort) @@ -68,17 +69,19 @@ import System.IO (stdout) import System.IO.Error (isDoesNotExistError) import Text.Pandoc import Text.Pandoc.Builder (setMeta) -import Text.Pandoc.Class (PandocIO, getLog, withMediaBag, getMediaBag) +import Text.Pandoc.Class (PandocIO, getLog, withMediaBag, getMediaBag, + fetchItem, insertMedia) import Text.Pandoc.Highlighting (highlightingStyles) import Text.Pandoc.Lua ( runLuaFilter ) import Text.Pandoc.MediaBag (extractMediaBag, mediaDirectory) +import Text.Pandoc.MIME (extensionFromMimeType) import Text.Pandoc.PDF (makePDF) import Text.Pandoc.Process (pipeProcess) import Text.Pandoc.SelfContained (makeSelfContained, makeDataURI) import Text.Pandoc.Shared (headerShift, openURL, readDataFile, readDataFileUTF8, safeRead, tabFilter) import qualified Text.Pandoc.UTF8 as UTF8 -import Text.Pandoc.Walk (walk) +import Text.Pandoc.Walk (walkM, walk) import Text.Pandoc.XML (toEntities) import Text.Printf #ifndef _WINDOWS @@ -413,11 +416,15 @@ convertWithOpts opts = do runIO' $ do (doc, media) <- withMediaBag $ sourceToDoc sources >>= - (maybe return extractMedia (optExtractMedia opts) + ( (if isJust (optExtractMedia opts) + then fillMedia (writerSourceURL writerOptions) + else return) + >=> maybe return extractMedia (optExtractMedia opts) >=> return . flip (foldr addMetadata) metadata >=> applyTransforms transforms >=> applyLuaFilters datadir (optLuaFilters opts) [format] - >=> applyFilters datadir filters' [format]) + >=> applyFilters datadir filters' [format] + ) case writer of ByteStringWriter f -> f writerOptions doc >>= writeFnBinary outputFile @@ -723,6 +730,21 @@ defaultWriterName x = -- Transformations of a Pandoc document post-parsing: +-- | Traverse tree, filling media bag. +fillMedia :: Maybe String -> Pandoc -> PandocIO Pandoc +fillMedia sourceURL d = walkM handleImage d + where handleImage :: Inline -> PandocIO Inline + handleImage (Image attr lab (src, tit)) = do + (bs, mt) <- fetchItem sourceURL src + let ext = fromMaybe (takeExtension src) + (mt >>= extensionFromMimeType) + let bs' = B.fromChunks [bs] + let basename = showDigest $ sha1 bs' + let fname = basename <.> ext + insertMedia fname mt bs' + return $ Image attr lab (fname, tit) + handleImage x = return x + extractMedia :: FilePath -> Pandoc -> PandocIO Pandoc extractMedia dir d = do media <- getMediaBag -- cgit v1.2.3 From f8e125f42d8568b9f2926c2d1a3eb37acba2b3d1 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 7 May 2017 12:16:14 +0200 Subject: fillMediaBag: don't cause fatal error if resource not found. Report warning instead and change image to its alt text. --- src/Text/Pandoc/App.hs | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 212ae7fe2..2efa69944 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -39,6 +39,7 @@ module Text.Pandoc.App ( ) where import Control.Applicative ((<|>)) import qualified Control.Exception as E +import Control.Monad.Except (catchError, throwError) import Control.Monad import Control.Monad.Trans import Data.Aeson (eitherDecode', encode) @@ -70,7 +71,7 @@ import System.IO.Error (isDoesNotExistError) import Text.Pandoc import Text.Pandoc.Builder (setMeta) import Text.Pandoc.Class (PandocIO, getLog, withMediaBag, getMediaBag, - fetchItem, insertMedia) + fetchItem, insertMedia, report) import Text.Pandoc.Highlighting (highlightingStyles) import Text.Pandoc.Lua ( runLuaFilter ) import Text.Pandoc.MediaBag (extractMediaBag, mediaDirectory) @@ -734,15 +735,23 @@ defaultWriterName x = fillMedia :: Maybe String -> Pandoc -> PandocIO Pandoc fillMedia sourceURL d = walkM handleImage d where handleImage :: Inline -> PandocIO Inline - handleImage (Image attr lab (src, tit)) = do - (bs, mt) <- fetchItem sourceURL src - let ext = fromMaybe (takeExtension src) - (mt >>= extensionFromMimeType) - let bs' = B.fromChunks [bs] - let basename = showDigest $ sha1 bs' - let fname = basename <.> ext - insertMedia fname mt bs' - return $ Image attr lab (fname, tit) + handleImage (Image attr lab (src, tit)) = catchError + (do (bs, mt) <- fetchItem sourceURL src + let ext = fromMaybe (takeExtension src) + (mt >>= extensionFromMimeType) + let bs' = B.fromChunks [bs] + let basename = showDigest $ sha1 bs' + let fname = basename <.> ext + insertMedia fname mt bs' + return $ Image attr lab (fname, tit)) + (\e -> do + case e of + PandocResourceNotFound _ -> do + report $ CouldNotFetchResource src + "replacing image with description" + -- emit alt text + return $ Span ("",["image"],[]) lab + _ -> throwError e) handleImage x = return x extractMedia :: FilePath -> Pandoc -> PandocIO Pandoc -- cgit v1.2.3 From d414b2543a1686007e84c54bc711dff969dfb569 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 7 May 2017 12:49:25 +0200 Subject: Remove https flag. Supporting two completely different libraries for fetching from URLs makes it difficult to trap errors, because of different error types expected from the libraries. There's no clear reason not to build with these https-capable libraires. --- src/Text/Pandoc/Shared.hs | 21 --------------------- 1 file changed, 21 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 8256d14c0..44a26509b 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -140,7 +140,6 @@ import Text.Pandoc.Data (dataFiles) #else import Paths_pandoc (getDataFileName) #endif -#ifdef HTTP_CLIENT import Network.HTTP.Client (httpLbs, responseBody, responseHeaders, Request(port,host,requestHeaders)) import Network.HTTP.Client (parseRequest) @@ -150,12 +149,6 @@ import Network.HTTP.Client.TLS (tlsManagerSettings) import System.Environment (getEnv) import Network.HTTP.Types.Header ( hContentType, hUserAgent) import Network (withSocketsDo) -#else -import Network.URI (parseURI) -import Network.HTTP (findHeader, rspBody, - RequestMethod(..), HeaderName(..), mkRequest) -import Network.Browser (browse, setAllowRedirects, setOutHandler, request) -#endif -- | Version number of pandoc library. pandocVersion :: String @@ -715,7 +708,6 @@ openURL u let mime = takeWhile (/=',') u'' contents = B8.pack $ unEscapeString $ drop 1 $ dropWhile (/=',') u'' in return (decodeLenient contents, Just mime) -#ifdef HTTP_CLIENT | otherwise = withSocketsDo $ do let parseReq = parseRequest (proxy :: Either IOError String) <- @@ -738,19 +730,6 @@ openURL u resp <- newManager tlsManagerSettings >>= httpLbs req'' return (BS.concat $ toChunks $ responseBody resp, UTF8.toString `fmap` lookup hContentType (responseHeaders resp)) -#else - | otherwise = getBodyAndMimeType `fmap` browse - (do liftIO $ UTF8.hPutStrLn stderr $ "Fetching " ++ u ++ "..." - setOutHandler $ const (return ()) - setAllowRedirects True - request (getRequest' u')) - where getBodyAndMimeType (_, r) = (rspBody r, findHeader HdrContentType r) - getRequest' uriString = case parseURI uriString of - Nothing -> error ("Not a valid URL: " ++ - uriString) - Just v -> mkRequest GET v - u' = escapeURIString (/= '|') u -- pipes are rejected by Network.URI -#endif -- -- Error reporting -- cgit v1.2.3 From 99be906101f7852e84e5da9c3b66dd6d99f649da Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 7 May 2017 13:11:04 +0200 Subject: Added PandocHttpException, trap exceptions in fetching from URLs. Closes #3646. --- src/Text/Pandoc/App.hs | 17 +++++++++++++---- src/Text/Pandoc/Class.hs | 5 ++++- src/Text/Pandoc/Error.hs | 4 ++++ src/Text/Pandoc/Shared.hs | 9 +++++---- 4 files changed, 26 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 2efa69944..a1691c5e2 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -376,7 +376,7 @@ convertWithOpts opts = do then 0 else optTabStop opts) - readSources :: (Functor m, MonadIO m) => [FilePath] -> m String + readSources :: [FilePath] -> PandocIO String readSources srcs = convertTabs . intercalate "\n" <$> mapM readSource srcs @@ -751,6 +751,11 @@ fillMedia sourceURL d = walkM handleImage d "replacing image with description" -- emit alt text return $ Span ("",["image"],[]) lab + PandocHttpError u er -> do + report $ CouldNotFetchResource u + (show er ++ "\rReplacing image with description.") + -- emit alt text + return $ Span ("",["image"],[]) lab _ -> throwError e) handleImage x = return x @@ -800,7 +805,7 @@ applyFilters mbDatadir filters args d = do expandedFilters <- mapM (expandFilterPath mbDatadir) filters foldrM ($) d $ map (flip externalFilter args) expandedFilters -readSource :: MonadIO m => FilePath -> m String +readSource :: FilePath -> PandocIO String readSource "-" = liftIO UTF8.getContents readSource src = case parseURI src of Just u | uriScheme u `elem` ["http:","https:"] -> @@ -809,8 +814,12 @@ readSource src = case parseURI src of liftIO $ UTF8.readFile (uriPath u) _ -> liftIO $ UTF8.readFile src -readURI :: MonadIO m => FilePath -> m String -readURI src = liftIO $ (UTF8.toString . fst) <$> openURL src +readURI :: FilePath -> PandocIO String +readURI src = do + res <- liftIO $ openURL src + case res of + Left e -> throwError $ PandocHttpError src e + Right (contents, _) -> return $ UTF8.toString contents readFile' :: MonadIO m => FilePath -> m B.ByteString readFile' "-" = liftIO B.getContents diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index ad9901125..939e0bd18 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -242,7 +242,10 @@ instance PandocMonad PandocIO where newUniqueHash = hashUnique <$> (liftIO IO.newUnique) openURL u = do report $ Fetching u - liftIOError IO.openURL u + res <- liftIO (IO.openURL u) + case res of + Right r -> return r + Left e -> throwError $ PandocHttpError u e readFileLazy s = liftIOError BL.readFile s readFileStrict s = liftIOError B.readFile s readDataFile mfp fname = liftIOError (IO.readDataFile mfp) fname diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs index a6db5e047..9b3f1b902 100644 --- a/src/Text/Pandoc/Error.hs +++ b/src/Text/Pandoc/Error.hs @@ -42,10 +42,12 @@ import Text.Parsec.Pos hiding (Line) import qualified Text.Pandoc.UTF8 as UTF8 import System.Exit (exitWith, ExitCode(..)) import System.IO (stderr) +import Network.HTTP.Client (HttpException) type Input = String data PandocError = PandocIOError String IOError + | PandocHttpError String HttpException | PandocShouldNeverHappenError String | PandocSomeError String | PandocParseError String @@ -70,6 +72,8 @@ handleError (Right r) = return r handleError (Left e) = case e of PandocIOError _ err' -> ioError err' + PandocHttpError u err' -> err 61 $ + "Could not fetch " ++ u ++ "\n" ++ show err' PandocShouldNeverHappenError s -> err 62 s PandocSomeError s -> err 63 s PandocParseError s -> err 64 s diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 44a26509b..0ebaf0f89 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -141,7 +141,8 @@ import Text.Pandoc.Data (dataFiles) import Paths_pandoc (getDataFileName) #endif import Network.HTTP.Client (httpLbs, responseBody, responseHeaders, - Request(port,host,requestHeaders)) + Request(port,host,requestHeaders), + HttpException) import Network.HTTP.Client (parseRequest) import Network.HTTP.Client (newManager) import Network.HTTP.Client.Internal (addProxy) @@ -702,13 +703,13 @@ readDataFileUTF8 userDir fname = UTF8.toString `fmap` readDataFile userDir fname -- | Read from a URL and return raw data and maybe mime type. -openURL :: String -> IO (BS.ByteString, Maybe MimeType) +openURL :: String -> IO (Either HttpException (BS.ByteString, Maybe MimeType)) openURL u | Just u'' <- stripPrefix "data:" u = let mime = takeWhile (/=',') u'' contents = B8.pack $ unEscapeString $ drop 1 $ dropWhile (/=',') u'' - in return (decodeLenient contents, Just mime) - | otherwise = withSocketsDo $ do + in return $ Right (decodeLenient contents, Just mime) + | otherwise = E.try $ withSocketsDo $ do let parseReq = parseRequest (proxy :: Either IOError String) <- tryIOError $ getEnv "http_proxy" -- cgit v1.2.3 From af7215a048a490a7c69eb6ea906bf4ca5d09c1b1 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 7 May 2017 20:42:32 +0200 Subject: Moved fillMedia, extractMedia from App to Class. Also generalized type of fillMedia to any instance of PandocMonad. --- src/Text/Pandoc/App.hs | 52 +++----------------------------------------- src/Text/Pandoc/Class.hs | 56 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 56 insertions(+), 52 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index a1691c5e2..6bc345d73 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -39,14 +39,13 @@ module Text.Pandoc.App ( ) where import Control.Applicative ((<|>)) import qualified Control.Exception as E -import Control.Monad.Except (catchError, throwError) +import Control.Monad.Except (throwError) import Control.Monad import Control.Monad.Trans import Data.Aeson (eitherDecode', encode) import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as B import Data.Char (toLower, toUpper) -import Data.Digest.Pure.SHA (sha1, showDigest) import qualified Data.Set as Set import Data.Foldable (foldrM) import Data.List (intercalate, isPrefixOf, isSuffixOf, sort) @@ -70,19 +69,16 @@ import System.IO (stdout) import System.IO.Error (isDoesNotExistError) import Text.Pandoc import Text.Pandoc.Builder (setMeta) -import Text.Pandoc.Class (PandocIO, getLog, withMediaBag, getMediaBag, - fetchItem, insertMedia, report) +import Text.Pandoc.Class (PandocIO, getLog, withMediaBag, + extractMedia, fillMedia) import Text.Pandoc.Highlighting (highlightingStyles) import Text.Pandoc.Lua ( runLuaFilter ) -import Text.Pandoc.MediaBag (extractMediaBag, mediaDirectory) -import Text.Pandoc.MIME (extensionFromMimeType) import Text.Pandoc.PDF (makePDF) import Text.Pandoc.Process (pipeProcess) import Text.Pandoc.SelfContained (makeSelfContained, makeDataURI) import Text.Pandoc.Shared (headerShift, openURL, readDataFile, readDataFileUTF8, safeRead, tabFilter) import qualified Text.Pandoc.UTF8 as UTF8 -import Text.Pandoc.Walk (walkM, walk) import Text.Pandoc.XML (toEntities) import Text.Printf #ifndef _WINDOWS @@ -731,48 +727,6 @@ defaultWriterName x = -- Transformations of a Pandoc document post-parsing: --- | Traverse tree, filling media bag. -fillMedia :: Maybe String -> Pandoc -> PandocIO Pandoc -fillMedia sourceURL d = walkM handleImage d - where handleImage :: Inline -> PandocIO Inline - handleImage (Image attr lab (src, tit)) = catchError - (do (bs, mt) <- fetchItem sourceURL src - let ext = fromMaybe (takeExtension src) - (mt >>= extensionFromMimeType) - let bs' = B.fromChunks [bs] - let basename = showDigest $ sha1 bs' - let fname = basename <.> ext - insertMedia fname mt bs' - return $ Image attr lab (fname, tit)) - (\e -> do - case e of - PandocResourceNotFound _ -> do - report $ CouldNotFetchResource src - "replacing image with description" - -- emit alt text - return $ Span ("",["image"],[]) lab - PandocHttpError u er -> do - report $ CouldNotFetchResource u - (show er ++ "\rReplacing image with description.") - -- emit alt text - return $ Span ("",["image"],[]) lab - _ -> throwError e) - handleImage x = return x - -extractMedia :: FilePath -> Pandoc -> PandocIO Pandoc -extractMedia dir d = do - media <- getMediaBag - case [fp | (fp, _, _) <- mediaDirectory media] of - [] -> return d - fps -> do - liftIO $ extractMediaBag True dir media - return $ walk (adjustImagePath dir fps) d - -adjustImagePath :: FilePath -> [FilePath] -> Inline -> Inline -adjustImagePath dir paths (Image attr lab (src, tit)) - | src `elem` paths = Image attr lab (dir ++ "/" ++ src, tit) -adjustImagePath _ _ x = x - applyTransforms :: Monad m => [Transform] -> Pandoc -> m Pandoc applyTransforms transforms d = return $ foldr ($) d transforms diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 939e0bd18..7407d0799 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -61,6 +61,8 @@ module Text.Pandoc.Class ( PandocMonad(..) , runIOorExplode , runPure , withMediaBag + , fillMedia + , extractMedia ) where import Prelude hiding (readFile) @@ -76,8 +78,11 @@ import Text.Pandoc.Compat.Time (UTCTime) import Text.Pandoc.Logging import Text.Parsec (ParsecT) import qualified Text.Pandoc.Compat.Time as IO (getCurrentTime) -import Text.Pandoc.MIME (MimeType, getMimeType) +import Text.Pandoc.MIME (MimeType, getMimeType, extensionFromMimeType) +import Text.Pandoc.Definition import Data.Char (toLower) +import Data.Digest.Pure.SHA (sha1, showDigest) +import Data.Maybe (fromMaybe) import Data.Time.Clock.POSIX ( utcTimeToPOSIXSeconds , posixSecondsToUTCTime , POSIXTime ) @@ -86,13 +91,15 @@ import Network.URI ( escapeURIString, nonStrictRelativeTo, unEscapeString, parseURIReference, isAllowedInURI, parseURI, URI(..) ) import qualified Data.Time.LocalTime as IO (getCurrentTimeZone) -import Text.Pandoc.MediaBag (MediaBag, lookupMedia) +import Text.Pandoc.MediaBag (MediaBag, lookupMedia, extractMediaBag, + mediaDirectory) +import Text.Pandoc.Walk (walkM, walk) import qualified Text.Pandoc.MediaBag as MB import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL import qualified System.Environment as IO (lookupEnv) import System.FilePath.Glob (match, compile) -import System.FilePath ((</>), takeExtension, dropExtension, isRelative) +import System.FilePath ((</>), (<.>), takeExtension, dropExtension, isRelative) import qualified System.FilePath.Glob as IO (glob) import qualified System.Directory as IO (getModificationTime) import Control.Monad as M (fail) @@ -338,6 +345,49 @@ withPaths (p:ps) action fp = catchError (action (p </> fp)) (\_ -> withPaths ps action fp) +-- | Traverse tree, filling media bag. +fillMedia :: PandocMonad m => Maybe String -> Pandoc -> m Pandoc +fillMedia sourceURL d = walkM handleImage d + where handleImage :: PandocMonad m => Inline -> m Inline + handleImage (Image attr lab (src, tit)) = catchError + (do (bs, mt) <- fetchItem sourceURL src + let ext = fromMaybe (takeExtension src) + (mt >>= extensionFromMimeType) + let bs' = BL.fromChunks [bs] + let basename = showDigest $ sha1 bs' + let fname = basename <.> ext + insertMedia fname mt bs' + return $ Image attr lab (fname, tit)) + (\e -> do + case e of + PandocResourceNotFound _ -> do + report $ CouldNotFetchResource src + "replacing image with description" + -- emit alt text + return $ Span ("",["image"],[]) lab + PandocHttpError u er -> do + report $ CouldNotFetchResource u + (show er ++ "\rReplacing image with description.") + -- emit alt text + return $ Span ("",["image"],[]) lab + _ -> throwError e) + handleImage x = return x + +-- | Extract media from the mediabag into a directory. +extractMedia :: FilePath -> Pandoc -> PandocIO Pandoc +extractMedia dir d = do + media <- getMediaBag + case [fp | (fp, _, _) <- mediaDirectory media] of + [] -> return d + fps -> do + liftIO $ extractMediaBag True dir media + return $ walk (adjustImagePath dir fps) d + +adjustImagePath :: FilePath -> [FilePath] -> Inline -> Inline +adjustImagePath dir paths (Image attr lab (src, tit)) + | src `elem` paths = Image attr lab (dir ++ "/" ++ src, tit) +adjustImagePath _ _ x = x + data PureState = PureState { stStdGen :: StdGen , stWord8Store :: [Word8] -- should be -- inifinite, -- cgit v1.2.3 From a902109c6d56f5249a0521c89ab90ca105b7b023 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 7 May 2017 20:57:16 +0200 Subject: PDF: use fillMedia and extractMedia to extract media to tmp dir. This reduces code duplication. We should be able to do something similar in ODT, Docx, EPUB writers. --- src/Text/Pandoc/PDF.hs | 50 +++++++++++--------------------------------------- 1 file changed, 11 insertions(+), 39 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index 696dbacf0..240da3ef0 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -34,14 +34,13 @@ module Text.Pandoc.PDF ( makePDF ) where import qualified Codec.Picture as JP import qualified Control.Exception as E -import Control.Monad (unless, when, (<=<)) +import Control.Monad (unless, when) import Control.Monad.Trans (MonadIO (..)) import qualified Data.ByteString as BS import Data.ByteString.Lazy (ByteString) import qualified Data.ByteString.Lazy as B import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Lazy.Char8 as BC -import Data.Digest.Pure.SHA (sha1, showDigest) import Data.List (isInfixOf) import Data.Maybe (fromMaybe) import Data.Monoid ((<>)) @@ -53,7 +52,7 @@ import System.IO (stdout) import System.IO.Temp (withTempDirectory, withTempFile) import Text.Pandoc.Definition import Text.Pandoc.MediaBag -import Text.Pandoc.MIME (extensionFromMimeType, getMimeType) +import Text.Pandoc.MIME (getMimeType) import Text.Pandoc.Options (HTMLMathMethod (..), WriterOptions (..)) import Text.Pandoc.Process (pipeProcess) import Text.Pandoc.Shared (inDirectory, stringify, withTempDir) @@ -63,8 +62,8 @@ import Text.Pandoc.Writers.Shared (getField, metaToJSON) #ifdef _WINDOWS import Data.List (intercalate) #endif -import Text.Pandoc.Class (PandocIO, fetchItem, report, runIO, runIOorExplode, - setMediaBag, setVerbosity) +import Text.Pandoc.Class (PandocIO, report, runIO, runIOorExplode, + setMediaBag, setVerbosity, fillMedia, extractMedia) import Text.Pandoc.Logging #ifdef _WINDOWS @@ -135,40 +134,13 @@ handleImages :: Verbosity -> FilePath -- ^ temp dir to store images -> Pandoc -- ^ document -> IO Pandoc -handleImages verbosity opts mediabag tmpdir = - walkM (convertImages verbosity tmpdir) <=< - walkM (handleImage' verbosity opts mediabag tmpdir) - -handleImage' :: Verbosity - -> WriterOptions - -> MediaBag - -> FilePath - -> Inline - -> IO Inline -handleImage' verbosity 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 - setVerbosity verbosity - setMediaBag mediabag - fetchItem (writerSourceURL opts) src - case res of - Right (contents, Just mime) -> do - let ext = fromMaybe (takeExtension src) $ - extensionFromMimeType mime - let basename = showDigest $ sha1 $ BL.fromChunks [contents] - let fname = tmpdir </> basename <.> ext - BS.writeFile fname contents - return $ Image attr ils (fname,tit) - _ -> do - runIO $ do - setVerbosity verbosity - report $ CouldNotFetchResource src "skipping..." - -- return alt text - return $ Emph ils -handleImage' _ _ _ _ x = return x +handleImages verbosity opts mediabag tmpdir doc = do + doc' <- runIOorExplode $ do + setVerbosity verbosity + setMediaBag mediabag + fillMedia (writerSourceURL opts) doc >>= + extractMedia tmpdir + walkM (convertImages verbosity tmpdir) doc' convertImages :: Verbosity -> FilePath -> Inline -> IO Inline convertImages verbosity tmpdir (Image attr ils (src, tit)) = do -- cgit v1.2.3 From 6b086acae8f20ad46ca92139e47e516302280e94 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 7 May 2017 21:03:18 +0200 Subject: Rename fillMedia -> fillMediaBag. --- src/Text/Pandoc/App.hs | 4 ++-- src/Text/Pandoc/Class.hs | 6 +++--- src/Text/Pandoc/PDF.hs | 5 +++-- 3 files changed, 8 insertions(+), 7 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 6bc345d73..f340259f3 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -70,7 +70,7 @@ import System.IO.Error (isDoesNotExistError) import Text.Pandoc import Text.Pandoc.Builder (setMeta) import Text.Pandoc.Class (PandocIO, getLog, withMediaBag, - extractMedia, fillMedia) + extractMedia, fillMediaBag) import Text.Pandoc.Highlighting (highlightingStyles) import Text.Pandoc.Lua ( runLuaFilter ) import Text.Pandoc.PDF (makePDF) @@ -414,7 +414,7 @@ convertWithOpts opts = do runIO' $ do (doc, media) <- withMediaBag $ sourceToDoc sources >>= ( (if isJust (optExtractMedia opts) - then fillMedia (writerSourceURL writerOptions) + then fillMediaBag (writerSourceURL writerOptions) else return) >=> maybe return extractMedia (optExtractMedia opts) >=> return . flip (foldr addMetadata) metadata diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 7407d0799..4ef56ec33 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -61,7 +61,7 @@ module Text.Pandoc.Class ( PandocMonad(..) , runIOorExplode , runPure , withMediaBag - , fillMedia + , fillMediaBag , extractMedia ) where @@ -346,8 +346,8 @@ withPaths (p:ps) action fp = (\_ -> withPaths ps action fp) -- | Traverse tree, filling media bag. -fillMedia :: PandocMonad m => Maybe String -> Pandoc -> m Pandoc -fillMedia sourceURL d = walkM handleImage d +fillMediaBag :: PandocMonad m => Maybe String -> Pandoc -> m Pandoc +fillMediaBag sourceURL d = walkM handleImage d where handleImage :: PandocMonad m => Inline -> m Inline handleImage (Image attr lab (src, tit)) = catchError (do (bs, mt) <- fetchItem sourceURL src diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index 240da3ef0..7097337e2 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -63,7 +63,8 @@ import Text.Pandoc.Writers.Shared (getField, metaToJSON) import Data.List (intercalate) #endif import Text.Pandoc.Class (PandocIO, report, runIO, runIOorExplode, - setMediaBag, setVerbosity, fillMedia, extractMedia) + setMediaBag, setVerbosity, + fillMediaBag, extractMedia) import Text.Pandoc.Logging #ifdef _WINDOWS @@ -138,7 +139,7 @@ handleImages verbosity opts mediabag tmpdir doc = do doc' <- runIOorExplode $ do setVerbosity verbosity setMediaBag mediabag - fillMedia (writerSourceURL opts) doc >>= + fillMediaBag (writerSourceURL opts) doc >>= extractMedia tmpdir walkM (convertImages verbosity tmpdir) doc' -- cgit v1.2.3 From 69110cde81a7bad260cdca579b4dcca306d4be2b Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sun, 7 May 2017 22:41:38 +0300 Subject: Muse writer: Indent tables with one space (#3649) It is required to trigger Muse table rendering. --- src/Text/Pandoc/Writers/Muse.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index 8b083e2c6..ccc6e9aef 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -216,7 +216,7 @@ blockToMuse (Table caption _ _ headers rows) = do let hpipeBlocks sep blocks = hcat $ intersperse sep' blocks where h = maximum (1 : map height blocks) sep' = lblock (length sep) $ vcat (map text $ replicate h sep) - let makeRow sep = hpipeBlocks sep . zipWith lblock widthsInChars + let makeRow sep = (" " <>) . (hpipeBlocks sep . zipWith lblock widthsInChars) let head' = makeRow " || " headers' let rowSeparator = if noHeaders then " | " else " | " rows'' <- mapM (\row -> do cols <- mapM blockListToMuse row @@ -224,7 +224,7 @@ blockToMuse (Table caption _ _ headers rows) = do let body = vcat rows'' return $ (if noHeaders then empty else head') $$ body - $$ (if null caption then empty else "|+ " <> caption' <> " +|") + $$ (if null caption then empty else " |+ " <> caption' <> " +|") $$ blankline blockToMuse (Div _ bs) = blockListToMuse bs blockToMuse Null = return empty -- cgit v1.2.3 From cff6d2dd73492d4c24ead814fc3564503f4d5b01 Mon Sep 17 00:00:00 2001 From: David A Roberts <d@vidr.cc> Date: Mon, 8 May 2017 07:11:57 +1000 Subject: Markdown writer: missing \n (#3647) --- 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 7c0874278..e67dcef6c 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -788,7 +788,7 @@ blockListToMarkdown opts blocks = do isListBlock _ = False commentSep = if isEnabled Ext_raw_html opts then RawBlock "html" "<!-- -->\n" - else RawBlock "markdown" " " + else RawBlock "markdown" " \n" mapM (blockToMarkdown opts) (fixBlocks blocks) >>= return . cat getKey :: Doc -> Key -- cgit v1.2.3 From 4b9fb7a1280f1d923a6bcecbf42a496480020359 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Wed, 10 May 2017 23:35:45 +0200 Subject: Combine grid table parsers The grid table parsers for markdown and rst was combined into one single parser, slightly changing parsing behavior of both parsers: - The markdown parser now compactifies block content cell-wise: pure text blocks in cells are now treated as paragraphs only if the cell contains multiple paragraphs, and as plain blocks otherwise. Before, this was true only for single-column tables. - The rst parser now accepts newlines and multiple blocks in header cells. Closes: #3638 --- src/Text/Pandoc/Parsing.hs | 69 ++++++++++++++++++++++-------- src/Text/Pandoc/Readers/Markdown.hs | 84 +------------------------------------ 2 files changed, 52 insertions(+), 101 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index e0c0e36d6..fa3ff898e 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -66,6 +66,7 @@ module Text.Pandoc.Parsing ( anyLine, tableWith, widthsFromIndices, gridTableWith, + gridTableWith', readWith, readWithM, testStringWith, @@ -770,6 +771,20 @@ tableWith :: (Stream s m Char, HasReaderOptions st, -> ParserT s st m end -> ParserT s st m (mf Blocks) tableWith headerParser rowParser lineParser footerParser = try $ do + (aligns, widths, heads, rows) <- tableWith' headerParser rowParser + lineParser footerParser + return $ B.table mempty (zip aligns widths) <$> heads <*> rows + +type TableComponents mf = ([Alignment], [Double], mf [Blocks], mf [[Blocks]]) + +tableWith' :: (Stream s m Char, HasReaderOptions st, + Functor mf, Applicative mf, Monad mf) + => ParserT s st m (mf [Blocks], [Alignment], [Int]) + -> ([Int] -> ParserT s st m (mf [Blocks])) + -> ParserT s st m sep + -> ParserT s st m end + -> ParserT s st m (TableComponents mf) +tableWith' headerParser rowParser lineParser footerParser = try $ do (heads, aligns, indices) <- headerParser lines' <- sequence <$> rowParser indices `sepEndBy1` lineParser footerParser @@ -777,7 +792,7 @@ tableWith headerParser rowParser lineParser footerParser = try $ do let widths = if (indices == []) then replicate (length aligns) 0.0 else widthsFromIndices numColumns indices - return $ B.table mempty (zip aligns widths) <$> heads <*> lines' + return $ (aligns, widths, heads, lines') -- Calculate relative widths of table columns, based on indices widthsFromIndices :: Int -- Number of columns on terminal @@ -812,24 +827,42 @@ widthsFromIndices numColumns' indices = -- ending with a footer (dashed line followed by blank line). gridTableWith :: (Stream [Char] m Char, HasReaderOptions st, Functor mf, Applicative mf, Monad mf) - => ParserT [Char] st m (mf Blocks) -- ^ Block list parser + => ParserT [Char] st m (mf Blocks) -- ^ Block list parser -> Bool -- ^ Headerless table -> ParserT [Char] st m (mf Blocks) gridTableWith blocks headless = tableWith (gridTableHeader headless blocks) (gridTableRow blocks) (gridTableSep '-') gridTableFooter +gridTableWith' :: (Stream [Char] m Char, HasReaderOptions st, + Functor mf, Applicative mf, Monad mf) + => ParserT [Char] st m (mf Blocks) -- ^ Block list parser + -> Bool -- ^ Headerless table + -> ParserT [Char] st m (TableComponents mf) +gridTableWith' blocks headless = + tableWith' (gridTableHeader headless blocks) (gridTableRow blocks) + (gridTableSep '-') gridTableFooter + gridTableSplitLine :: [Int] -> String -> [String] gridTableSplitLine indices line = map removeFinalBar $ tail $ splitStringByIndices (init indices) $ trimr line -gridPart :: Stream s m Char => Char -> ParserT s st m (Int, Int) +gridPart :: Stream s m Char => Char -> ParserT s st m ((Int, Int), Alignment) gridPart ch = do + leftColon <- option False (True <$ char ':') dashes <- many1 (char ch) + rightColon <- option False (True <$ char ':') char '+' - return (length dashes, length dashes + 1) - -gridDashedLines :: Stream s m Char => Char -> ParserT s st m [(Int,Int)] + let lengthDashes = length dashes + (if leftColon then 1 else 0) + + (if rightColon then 1 else 0) + let alignment = case (leftColon, rightColon) of + (True, True) -> AlignCenter + (True, False) -> AlignLeft + (False, True) -> AlignRight + (False, False) -> AlignDefault + return ((lengthDashes, lengthDashes + 1), alignment) + +gridDashedLines :: Stream s m Char => Char -> ParserT s st m [((Int, Int), Alignment)] gridDashedLines ch = try $ char '+' >> many1 (gridPart ch) <* blankline removeFinalBar :: String -> String @@ -853,18 +886,18 @@ gridTableHeader headless blocks = try $ do else many1 (notFollowedBy (gridTableSep '=') >> char '|' >> many1Till anyChar newline) - if headless - then return () - else gridTableSep '=' >> return () - let lines' = map snd dashes + underDashes <- if headless + then return dashes + else gridDashedLines '=' + guard $ length dashes == length underDashes + let lines' = map (snd . fst) underDashes let indices = scanl (+) 0 lines' - let aligns = replicate (length lines') AlignDefault - -- RST does not have a notion of alignments + let aligns = map snd underDashes let rawHeads = if headless - then replicate (length dashes) "" - else map (intercalate " ") $ transpose + then replicate (length underDashes) "" + else map (unlines . map trim) $ transpose $ map (gridTableSplitLine indices) rawContent - heads <- fmap sequence . mapM (parseFromString blocks) $ map trim rawHeads + heads <- fmap sequence $ mapM (parseFromString blocks . trim) rawHeads return (heads, aligns, indices) gridTableRawLine :: Stream s m Char => [Int] -> ParserT s st m [String] @@ -882,6 +915,9 @@ gridTableRow blocks indices = do colLines <- many1 (gridTableRawLine indices) let cols = map ((++ "\n") . unlines . removeOneLeadingSpace) $ transpose colLines + compactifyCell bs = case compactify [bs] of + [] -> mempty + x:_ -> x cells <- sequence <$> mapM (parseFromString blocks) cols return $ fmap (map compactifyCell) cells @@ -893,9 +929,6 @@ removeOneLeadingSpace xs = where startsWithSpace "" = True startsWithSpace (y:_) = y == ' ' -compactifyCell :: Blocks -> Blocks -compactifyCell bs = head $ compactify [bs] - -- | Parse footer for a grid table. gridTableFooter :: Stream s m Char => ParserT s st m [Char] gridTableFooter = blanklines diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 691d4d5cf..4ff5a1845 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -1291,89 +1291,7 @@ multilineTableHeader headless = try $ do -- ending with a footer (dashed line followed by blank line). gridTable :: PandocMonad m => Bool -- ^ Headerless table -> MarkdownParser m ([Alignment], [Double], F [Blocks], F [[Blocks]]) -gridTable headless = - tableWith (gridTableHeader headless) gridTableRow - (gridTableSep '-') gridTableFooter - -gridTableSplitLine :: [Int] -> String -> [String] -gridTableSplitLine indices line = map removeFinalBar $ tail $ - splitStringByIndices (init indices) $ trimr line - -gridPart :: PandocMonad m => Char -> ParserT [Char] st m ((Int, Int), Alignment) -gridPart ch = do - leftColon <- option False (True <$ char ':') - dashes <- many1 (char ch) - rightColon <- option False (True <$ char ':') - char '+' - let lengthDashes = length dashes + (if leftColon then 1 else 0) + - (if rightColon then 1 else 0) - let alignment = case (leftColon, rightColon) of - (True, True) -> AlignCenter - (True, False) -> AlignLeft - (False, True) -> AlignRight - (False, False) -> AlignDefault - return ((lengthDashes, lengthDashes + 1), alignment) - -gridDashedLines :: PandocMonad m => Char -> ParserT [Char] st m [((Int, Int), Alignment)] -gridDashedLines ch = try $ char '+' >> many1 (gridPart ch) <* blankline - -removeFinalBar :: String -> String -removeFinalBar = - reverse . dropWhile (`elem` " \t") . dropWhile (=='|') . reverse - --- | Separator between rows of grid table. -gridTableSep :: PandocMonad m => Char -> MarkdownParser m Char -gridTableSep ch = try $ gridDashedLines ch >> return '\n' - --- | Parse header for a grid table. -gridTableHeader :: PandocMonad m => Bool -- ^ Headerless table - -> MarkdownParser m (F [Blocks], [Alignment], [Int]) -gridTableHeader headless = try $ do - optional blanklines - dashes <- gridDashedLines '-' - rawContent <- if headless - then return [] - else many1 (try (char '|' >> anyLine)) - underDashes <- if headless - then return dashes - else gridDashedLines '=' - guard $ length dashes == length underDashes - let lines' = map (snd . fst) underDashes - let indices = scanl (+) 0 lines' - let aligns = map snd underDashes - let rawHeads = if headless - then replicate (length underDashes) "" - else map (unlines . map trim) $ transpose - $ map (gridTableSplitLine indices) rawContent - heads <- fmap sequence $ mapM (parseFromString parseBlocks . trim) rawHeads - return (heads, aligns, indices) - -gridTableRawLine :: PandocMonad m => [Int] -> MarkdownParser m [String] -gridTableRawLine indices = do - char '|' - line <- anyLine - return (gridTableSplitLine indices line) - --- | Parse row of grid table. -gridTableRow :: PandocMonad m => [Int] - -> MarkdownParser m (F [Blocks]) -gridTableRow indices = do - colLines <- many1 (gridTableRawLine indices) - let cols = map ((++ "\n") . unlines . removeOneLeadingSpace) $ - transpose colLines - fmap compactify <$> fmap sequence (mapM (parseFromString parseBlocks) cols) - -removeOneLeadingSpace :: [String] -> [String] -removeOneLeadingSpace xs = - if all startsWithSpace xs - then map (drop 1) xs - else xs - where startsWithSpace "" = True - startsWithSpace (y:_) = y == ' ' - --- | Parse footer for a grid table. -gridTableFooter :: PandocMonad m => MarkdownParser m [Char] -gridTableFooter = blanklines +gridTable headless = gridTableWith' parseBlocks headless pipeBreak :: PandocMonad m => MarkdownParser m ([Alignment], [Int]) pipeBreak = try $ do -- cgit v1.2.3 From 2a291e437a18073e0005447245809833ce46ae5c Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 12 May 2017 11:55:45 +0300 Subject: Replace `repeat' and `take' with `replicate' once more --- src/Text/Pandoc/Readers/Org/Blocks.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index 89c076869..788ec26dc 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -442,7 +442,7 @@ rawBlockContent blockType = try $ do tabsToSpaces tabLen cs'@(c:cs) = case c of ' ' -> ' ':tabsToSpaces tabLen cs - '\t' -> (take tabLen $ repeat ' ') ++ tabsToSpaces tabLen cs + '\t' -> replicate tabLen ' ' ++ tabsToSpaces tabLen cs _ -> cs' commaEscaped :: String -> String -- cgit v1.2.3 From 62d34c79b9b00cc37ea395f70abd2c25eccf4cf8 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sat, 13 May 2017 22:14:29 +0200 Subject: Change maintainer line in Org writer module --- src/Text/Pandoc/Writers/Org.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index fc6608450..46752c7ce 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -24,7 +24,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Copyright : Copyright (C) 2010-2015 Puneeth Chaganti and John MacFarlane License : GNU GPL, version 2 or above - Maintainer : Puneeth Chaganti <punchagan@gmail.com> + Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> Stability : alpha Portability : portable -- cgit v1.2.3 From 1cbb3bad2b9d7c609959186668e3edbd496bea0b Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sat, 13 May 2017 22:18:12 +0200 Subject: Add haddock module description to FB2 writer Copyright, maintainer etc. were missing in haddock docs for this module. --- src/Text/Pandoc/Writers/FB2.hs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index fb232e278..b8806a261 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -19,7 +19,17 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} -{- | Conversion of 'Pandoc' documents to FB2 (FictionBook2) format. +{- | +Module : Text.Pandoc.Writers.FB2 +Copyright : Copyright (C) 2011-2012 Sergey Astanin + 2012-2017 John MacFarlane +License : GNU GPL, version 2 or above + +Maintainer : John MacFarlane +Stability : alpha +Portability : portable + +Conversion of 'Pandoc' documents to FB2 (FictionBook2) format. FictionBook is an XML-based e-book format. For more information see: <http://www.fictionbook.org/index.php/Eng:XML_Schema_Fictionbook_2.1> -- cgit v1.2.3 From 965f1ddd4a9d1317455094b8c41016624d92f8ce Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sat, 13 May 2017 23:30:13 +0200 Subject: Update dates in copyright notices This follows the suggestions given by the FSF for GPL licensed software. <https://www.gnu.org/prep/maintain/html_node/Copyright-Notices.html> --- src/Text/Pandoc.hs | 4 ++-- src/Text/Pandoc/App.hs | 4 ++-- src/Text/Pandoc/Asciify.hs | 4 ++-- src/Text/Pandoc/Error.hs | 4 ++-- src/Text/Pandoc/Highlighting.hs | 4 ++-- src/Text/Pandoc/ImageSize.hs | 4 ++-- src/Text/Pandoc/Logging.hs | 2 +- src/Text/Pandoc/Lua/SharedInstances.hs | 2 +- src/Text/Pandoc/Lua/StackInstances.hs | 4 ++-- src/Text/Pandoc/Lua/Util.hs | 2 +- src/Text/Pandoc/MIME.hs | 4 ++-- src/Text/Pandoc/MediaBag.hs | 4 ++-- src/Text/Pandoc/Options.hs | 4 ++-- src/Text/Pandoc/PDF.hs | 4 ++-- src/Text/Pandoc/Parsing.hs | 4 ++-- src/Text/Pandoc/Pretty.hs | 4 ++-- src/Text/Pandoc/Process.hs | 4 ++-- src/Text/Pandoc/Readers/Docx.hs | 4 ++-- src/Text/Pandoc/Readers/Docx/Lists.hs | 4 ++-- src/Text/Pandoc/Readers/Docx/Parse.hs | 4 ++-- src/Text/Pandoc/Readers/HTML.hs | 4 ++-- src/Text/Pandoc/Readers/LaTeX.hs | 4 ++-- src/Text/Pandoc/Readers/Markdown.hs | 4 ++-- src/Text/Pandoc/Readers/MediaWiki.hs | 4 ++-- src/Text/Pandoc/Readers/Native.hs | 4 ++-- src/Text/Pandoc/Readers/Org.hs | 4 ++-- src/Text/Pandoc/Readers/Org/BlockStarts.hs | 4 ++-- src/Text/Pandoc/Readers/Org/ExportSettings.hs | 2 +- src/Text/Pandoc/Readers/Org/Inlines.hs | 4 ++-- src/Text/Pandoc/Readers/Org/ParserState.hs | 4 ++-- src/Text/Pandoc/Readers/Org/Parsing.hs | 4 ++-- src/Text/Pandoc/Readers/Org/Shared.hs | 4 ++-- src/Text/Pandoc/Readers/RST.hs | 4 ++-- src/Text/Pandoc/Readers/Textile.hs | 7 ++++--- src/Text/Pandoc/SelfContained.hs | 4 ++-- src/Text/Pandoc/Shared.hs | 4 ++-- src/Text/Pandoc/Slides.hs | 4 ++-- src/Text/Pandoc/Templates.hs | 4 ++-- src/Text/Pandoc/UTF8.hs | 4 ++-- src/Text/Pandoc/UUID.hs | 4 ++-- src/Text/Pandoc/Writers.hs | 4 ++-- src/Text/Pandoc/Writers/AsciiDoc.hs | 4 ++-- src/Text/Pandoc/Writers/ConTeXt.hs | 4 ++-- src/Text/Pandoc/Writers/Custom.hs | 4 ++-- src/Text/Pandoc/Writers/Docbook.hs | 4 ++-- src/Text/Pandoc/Writers/Docx.hs | 4 ++-- src/Text/Pandoc/Writers/DokuWiki.hs | 4 ++-- src/Text/Pandoc/Writers/EPUB.hs | 4 ++-- src/Text/Pandoc/Writers/FB2.hs | 4 ++-- src/Text/Pandoc/Writers/HTML.hs | 4 ++-- src/Text/Pandoc/Writers/Haddock.hs | 4 ++-- src/Text/Pandoc/Writers/ICML.hs | 2 +- src/Text/Pandoc/Writers/JATS.hs | 2 +- src/Text/Pandoc/Writers/LaTeX.hs | 4 ++-- src/Text/Pandoc/Writers/Man.hs | 4 ++-- src/Text/Pandoc/Writers/Markdown.hs | 4 ++-- src/Text/Pandoc/Writers/MediaWiki.hs | 4 ++-- src/Text/Pandoc/Writers/Ms.hs | 4 ++-- src/Text/Pandoc/Writers/Native.hs | 4 ++-- src/Text/Pandoc/Writers/ODT.hs | 4 ++-- src/Text/Pandoc/Writers/OPML.hs | 4 ++-- src/Text/Pandoc/Writers/OpenDocument.hs | 4 ++-- src/Text/Pandoc/Writers/Org.hs | 8 +++++--- src/Text/Pandoc/Writers/RST.hs | 4 ++-- src/Text/Pandoc/Writers/RTF.hs | 4 ++-- src/Text/Pandoc/Writers/Shared.hs | 4 ++-- src/Text/Pandoc/Writers/TEI.hs | 4 ++-- src/Text/Pandoc/Writers/Texinfo.hs | 6 ++++-- src/Text/Pandoc/Writers/Textile.hs | 4 ++-- src/Text/Pandoc/Writers/ZimWiki.hs | 5 +++-- src/Text/Pandoc/XML.hs | 4 ++-- 71 files changed, 144 insertions(+), 138 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index 345ef3b18..8ee1adf13 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -2,7 +2,7 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE ScopedTypeVariables #-} {- -Copyright (C) 2006-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc - Copyright : Copyright (C) 2006-2016 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index f340259f3..157100507 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -2,7 +2,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} {- -Copyright (C) 2006-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.App - Copyright : Copyright (C) 2006-2016 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley@edu> diff --git a/src/Text/Pandoc/Asciify.hs b/src/Text/Pandoc/Asciify.hs index 411a112b2..7125e5bcd 100644 --- a/src/Text/Pandoc/Asciify.hs +++ b/src/Text/Pandoc/Asciify.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2013-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2013-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Asciify - Copyright : Copyright (C) 2013-2016 John MacFarlane + Copyright : Copyright (C) 2013-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs index 9b3f1b902..077413056 100644 --- a/src/Text/Pandoc/Error.hs +++ b/src/Text/Pandoc/Error.hs @@ -1,7 +1,7 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-} {- -Copyright (C) 2006-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | Module : Text.Pandoc.Error - Copyright : Copyright (C) 2006-2016 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Highlighting.hs b/src/Text/Pandoc/Highlighting.hs index f249f96ad..183155d5b 100644 --- a/src/Text/Pandoc/Highlighting.hs +++ b/src/Text/Pandoc/Highlighting.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2008-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2008-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Highlighting - Copyright : Copyright (C) 2008-2016 John MacFarlane + Copyright : Copyright (C) 2008-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/ImageSize.hs b/src/Text/Pandoc/ImageSize.hs index 8b2d577a9..a0800e499 100644 --- a/src/Text/Pandoc/ImageSize.hs +++ b/src/Text/Pandoc/ImageSize.hs @@ -1,7 +1,7 @@ {-# LANGUAGE OverloadedStrings, ScopedTypeVariables, CPP #-} {-# OPTIONS_GHC -fno-warn-type-defaults #-} {- - Copyright (C) 2011-2016 John MacFarlane <jgm@berkeley.edu> + Copyright (C) 2011-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -20,7 +20,7 @@ {- | Module : Text.Pandoc.ImageSize -Copyright : Copyright (C) 2011-2016 John MacFarlane +Copyright : Copyright (C) 2011-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs index 59b010034..2cca4b7d3 100644 --- a/src/Text/Pandoc/Logging.hs +++ b/src/Text/Pandoc/Logging.hs @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | Module : Text.Pandoc.Logging - Copyright : Copyright (C) 2006-2016 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Lua/SharedInstances.hs b/src/Text/Pandoc/Lua/SharedInstances.hs index 3d2d29ebf..019a82446 100644 --- a/src/Text/Pandoc/Lua/SharedInstances.hs +++ b/src/Text/Pandoc/Lua/SharedInstances.hs @@ -1,5 +1,5 @@ {- -Copyright © 2012-2016 John MacFarlane <jgm@berkeley.edu> +Copyright © 2012-2017 John MacFarlane <jgm@berkeley.edu> 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> This program is free software; you can redistribute it and/or modify diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index 03f6e06e2..cfc4389c2 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -1,5 +1,5 @@ {- -Copyright © 2012-2015 John MacFarlane <jgm@berkeley.edu> +Copyright © 2012-2017 John MacFarlane <jgm@berkeley.edu> 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> This program is free software; you can redistribute it and/or modify @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {-# OPTIONS_GHC -fno-warn-orphans #-} {- | Module : Text.Pandoc.Lua.StackInstances - Copyright : © 2012-2016 John MacFarlane + Copyright : © 2012-2017 John MacFarlane © 2017 Albert Krewinkel License : GNU GPL, version 2 or above diff --git a/src/Text/Pandoc/Lua/Util.hs b/src/Text/Pandoc/Lua/Util.hs index f0b87c231..ff07ba7d7 100644 --- a/src/Text/Pandoc/Lua/Util.hs +++ b/src/Text/Pandoc/Lua/Util.hs @@ -1,5 +1,5 @@ {- -Copyright © 2012-2016 John MacFarlane <jgm@berkeley.edu> +Copyright © 2012-2017 John MacFarlane <jgm@berkeley.edu> 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> This program is free software; you can redistribute it and/or modify diff --git a/src/Text/Pandoc/MIME.hs b/src/Text/Pandoc/MIME.hs index 2e4a97b71..162112634 100644 --- a/src/Text/Pandoc/MIME.hs +++ b/src/Text/Pandoc/MIME.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2011-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2011-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.MIME - Copyright : Copyright (C) 2011-2016 John MacFarlane + Copyright : Copyright (C) 2011-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/MediaBag.hs b/src/Text/Pandoc/MediaBag.hs index b865f97c2..980511acc 100644 --- a/src/Text/Pandoc/MediaBag.hs +++ b/src/Text/Pandoc/MediaBag.hs @@ -1,7 +1,7 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {- -Copyright (C) 2014 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2014-2015, 2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.MediaBag - Copyright : Copyright (C) 2014 John MacFarlane + Copyright : Copyright (C) 2014-2015, 2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index 0b09f0497..6757c6782 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -1,7 +1,7 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-} {- -Copyright (C) 2012-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2012-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Options - Copyright : Copyright (C) 2012-2016 John MacFarlane + Copyright : Copyright (C) 2012-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index 7097337e2..cc9b38f7f 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -2,7 +2,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {- -Copyright (C) 2012-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2012-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.PDF - Copyright : Copyright (C) 2012-2016 John MacFarlane + Copyright : Copyright (C) 2012-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index fa3ff898e..e90f64c5b 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -7,7 +7,7 @@ , IncoherentInstances #-} {- -Copyright (C) 2006-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -26,7 +26,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Parsing - Copyright : Copyright (C) 2006-2016 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Pretty.hs b/src/Text/Pandoc/Pretty.hs index 32e60843c..a432949c8 100644 --- a/src/Text/Pandoc/Pretty.hs +++ b/src/Text/Pandoc/Pretty.hs @@ -1,7 +1,7 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {- -Copyright (C) 2010-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2010-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111(-1)307 USA {- | Module : Text.Pandoc.Pretty - Copyright : Copyright (C) 2010-2016 John MacFarlane + Copyright : Copyright (C) 2010-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Process.hs b/src/Text/Pandoc/Process.hs index 1014f37dd..b2a0c17f1 100644 --- a/src/Text/Pandoc/Process.hs +++ b/src/Text/Pandoc/Process.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2013-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2013-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Process - Copyright : Copyright (C) 2013-2016 John MacFarlane + Copyright : Copyright (C) 2013-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index 683277993..2757314ab 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -3,7 +3,7 @@ {-# LANGUAGE PatternGuards #-} {- -Copyright (C) 2014-2016 Jesse Rosenthal <jrosenthal@jhu.edu> +Copyright (C) 2014-2017 Jesse Rosenthal <jrosenthal@jhu.edu> 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 @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Docx - Copyright : Copyright (C) 2014-2016 Jesse Rosenthal + Copyright : Copyright (C) 2014-2017 Jesse Rosenthal License : GNU GPL, version 2 or above Maintainer : Jesse Rosenthal <jrosenthal@jhu.edu> diff --git a/src/Text/Pandoc/Readers/Docx/Lists.hs b/src/Text/Pandoc/Readers/Docx/Lists.hs index 94b4d919a..8be2e1894 100644 --- a/src/Text/Pandoc/Readers/Docx/Lists.hs +++ b/src/Text/Pandoc/Readers/Docx/Lists.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2014-2016 Jesse Rosenthal <jrosenthal@jhu.edu> +Copyright (C) 2014-2017 Jesse Rosenthal <jrosenthal@jhu.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Docx.Lists - Copyright : Copyright (C) 2014-2016 Jesse Rosenthal + Copyright : Copyright (C) 2014-2017 Jesse Rosenthal License : GNU GPL, version 2 or above Maintainer : Jesse Rosenthal <jrosenthal@jhu.edu> diff --git a/src/Text/Pandoc/Readers/Docx/Parse.hs b/src/Text/Pandoc/Readers/Docx/Parse.hs index 0f23555f4..e6736100f 100644 --- a/src/Text/Pandoc/Readers/Docx/Parse.hs +++ b/src/Text/Pandoc/Readers/Docx/Parse.hs @@ -3,7 +3,7 @@ {-# LANGUAGE ViewPatterns #-} {- -Copyright (C) 2014-2016 Jesse Rosenthal <jrosenthal@jhu.edu> +Copyright (C) 2014-2017 Jesse Rosenthal <jrosenthal@jhu.edu> 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 @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Docx.Parse - Copyright : Copyright (C) 2014-2016 Jesse Rosenthal + Copyright : Copyright (C) 2014-2017 Jesse Rosenthal License : GNU GPL, version 2 or above Maintainer : Jesse Rosenthal <jrosenthal@jhu.edu> diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 14b051539..650454ae6 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -1,7 +1,7 @@ {-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, ViewPatterns#-} {- -Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.HTML - Copyright : Copyright (C) 2006-2015 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index b13fc215b..9a887c40c 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1,7 +1,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {- -Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.LaTeX - Copyright : Copyright (C) 2006-2015 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 4ff5a1845..0c0d07140 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -2,7 +2,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {- -Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Markdown - Copyright : Copyright (C) 2006-2015 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Readers/MediaWiki.hs b/src/Text/Pandoc/Readers/MediaWiki.hs index b35f39aad..c860a0cdf 100644 --- a/src/Text/Pandoc/Readers/MediaWiki.hs +++ b/src/Text/Pandoc/Readers/MediaWiki.hs @@ -3,7 +3,7 @@ {-# LANGUAGE TypeSynonymInstances #-} -- RelaxedPolyRec needed for inlinesBetween on GHC < 7 {- - Copyright (C) 2012-2015 John MacFarlane <jgm@berkeley.edu> + Copyright (C) 2012-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.MediaWiki - Copyright : Copyright (C) 2012-2015 John MacFarlane + Copyright : Copyright (C) 2012-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Readers/Native.hs b/src/Text/Pandoc/Readers/Native.hs index 2e307fa4f..8f42a45de 100644 --- a/src/Text/Pandoc/Readers/Native.hs +++ b/src/Text/Pandoc/Readers/Native.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2011-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2011-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Native - Copyright : Copyright (C) 2011-2015 John MacFarlane + Copyright : Copyright (C) 2011-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Readers/Org.hs b/src/Text/Pandoc/Readers/Org.hs index 5e509178d..2b29bcfda 100644 --- a/src/Text/Pandoc/Readers/Org.hs +++ b/src/Text/Pandoc/Readers/Org.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2014-2016 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright (C) 2014-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Org - Copyright : Copyright (C) 2014-2016 Albert Krewinkel + Copyright : Copyright (C) 2014-2017 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> diff --git a/src/Text/Pandoc/Readers/Org/BlockStarts.hs b/src/Text/Pandoc/Readers/Org/BlockStarts.hs index f05725f16..066bde9e0 100644 --- a/src/Text/Pandoc/Readers/Org/BlockStarts.hs +++ b/src/Text/Pandoc/Readers/Org/BlockStarts.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2014-2016 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright (C) 2014-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Org.Options - Copyright : Copyright (C) 2014-2016 Albert Krewinkel + Copyright : Copyright (C) 2014-2017 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> diff --git a/src/Text/Pandoc/Readers/Org/ExportSettings.hs b/src/Text/Pandoc/Readers/Org/ExportSettings.hs index 1d6fdd7e1..934191e71 100644 --- a/src/Text/Pandoc/Readers/Org/ExportSettings.hs +++ b/src/Text/Pandoc/Readers/Org/ExportSettings.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2014-2016 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright (C) 2014-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 diff --git a/src/Text/Pandoc/Readers/Org/Inlines.hs b/src/Text/Pandoc/Readers/Org/Inlines.hs index 5772e4157..800264db0 100644 --- a/src/Text/Pandoc/Readers/Org/Inlines.hs +++ b/src/Text/Pandoc/Readers/Org/Inlines.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2014-2016 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright (C) 2014-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Org.Options - Copyright : Copyright (C) 2014-2016 Albert Krewinkel + Copyright : Copyright (C) 2014-2017 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> diff --git a/src/Text/Pandoc/Readers/Org/ParserState.hs b/src/Text/Pandoc/Readers/Org/ParserState.hs index e47565814..f530d1d03 100644 --- a/src/Text/Pandoc/Readers/Org/ParserState.hs +++ b/src/Text/Pandoc/Readers/Org/ParserState.hs @@ -2,7 +2,7 @@ {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} {- -Copyright (C) 2014-2016 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright (C) 2014-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Org.Options - Copyright : Copyright (C) 2014-2016 Albert Krewinkel + Copyright : Copyright (C) 2014-2017 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> diff --git a/src/Text/Pandoc/Readers/Org/Parsing.hs b/src/Text/Pandoc/Readers/Org/Parsing.hs index aa3a08279..50f5ebae5 100644 --- a/src/Text/Pandoc/Readers/Org/Parsing.hs +++ b/src/Text/Pandoc/Readers/Org/Parsing.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2014-2016 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright (C) 2014-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Org.Options - Copyright : Copyright (C) 2014-2016 Albert Krewinkel + Copyright : Copyright (C) 2014-2017 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> diff --git a/src/Text/Pandoc/Readers/Org/Shared.hs b/src/Text/Pandoc/Readers/Org/Shared.hs index f89ce6732..95424319f 100644 --- a/src/Text/Pandoc/Readers/Org/Shared.hs +++ b/src/Text/Pandoc/Readers/Org/Shared.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2014-2016 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright (C) 2014-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Org.Options - Copyright : Copyright (C) 2014-2016 Albert Krewinkel + Copyright : Copyright (C) 2014-2017 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 628351f36..868bfafa4 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -2,7 +2,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {- -Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.RST - Copyright : Copyright (C) 2006-2015 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Readers/Textile.hs b/src/Text/Pandoc/Readers/Textile.hs index 047aa061c..df057837f 100644 --- a/src/Text/Pandoc/Readers/Textile.hs +++ b/src/Text/Pandoc/Readers/Textile.hs @@ -1,6 +1,6 @@ {- -Copyright (C) 2010-2015 Paul Rivier <paul*rivier#demotera*com> | tr '*#' '.@' - and John MacFarlane +Copyright (C) 2010-2012 Paul Rivier <paul*rivier#demotera*com> | tr '*#' '.@' + 2010-2017 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 @@ -19,7 +19,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Textile - Copyright : Copyright (C) 2010-2015 Paul Rivier and John MacFarlane + Copyright : Copyright (C) 2010-2012 Paul Rivier + 2010-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : Paul Rivier <paul*rivier#demotera*com> diff --git a/src/Text/Pandoc/SelfContained.hs b/src/Text/Pandoc/SelfContained.hs index 6391ef0e0..c0a12adf2 100644 --- a/src/Text/Pandoc/SelfContained.hs +++ b/src/Text/Pandoc/SelfContained.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2011-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2011-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.SelfContained - Copyright : Copyright (C) 2011-2016 John MacFarlane + Copyright : Copyright (C) 2011-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 0ebaf0f89..3a61656e5 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -2,7 +2,7 @@ FlexibleContexts, ScopedTypeVariables, PatternGuards, ViewPatterns #-} {- -Copyright (C) 2006-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Shared - Copyright : Copyright (C) 2006-2016 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Slides.hs b/src/Text/Pandoc/Slides.hs index b53e0eb6d..cd7695dbe 100644 --- a/src/Text/Pandoc/Slides.hs +++ b/src/Text/Pandoc/Slides.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2012-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2012-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Slides - Copyright : Copyright (C) 2012-2016 John MacFarlane + Copyright : Copyright (C) 2012-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Templates.hs b/src/Text/Pandoc/Templates.hs index 26aeb9a73..9b635a97b 100644 --- a/src/Text/Pandoc/Templates.hs +++ b/src/Text/Pandoc/Templates.hs @@ -3,7 +3,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeSynonymInstances #-} {- -Copyright (C) 2009-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2009-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Templates - Copyright : Copyright (C) 2009-2016 John MacFarlane + Copyright : Copyright (C) 2009-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/UTF8.hs b/src/Text/Pandoc/UTF8.hs index d88a44948..e27a24e63 100644 --- a/src/Text/Pandoc/UTF8.hs +++ b/src/Text/Pandoc/UTF8.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2010-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2010-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.UTF8 - Copyright : Copyright (C) 2010-2016 John MacFarlane + Copyright : Copyright (C) 2010-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/UUID.hs b/src/Text/Pandoc/UUID.hs index 9446c4692..989dd20c6 100644 --- a/src/Text/Pandoc/UUID.hs +++ b/src/Text/Pandoc/UUID.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2010-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2010-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.UUID - Copyright : Copyright (C) 2010-2016 John MacFarlane + Copyright : Copyright (C) 2010-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers.hs b/src/Text/Pandoc/Writers.hs index 0181f41c9..62445c072 100644 --- a/src/Text/Pandoc/Writers.hs +++ b/src/Text/Pandoc/Writers.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2006-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc - Copyright : Copyright (C) 2006-2016 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/AsciiDoc.hs b/src/Text/Pandoc/Writers/AsciiDoc.hs index 20fa7c209..e0085fb1a 100644 --- a/src/Text/Pandoc/Writers/AsciiDoc.hs +++ b/src/Text/Pandoc/Writers/AsciiDoc.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.AsciiDoc - Copyright : Copyright (C) 2006-2015 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index 57f920259..eef16d3da 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2007-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2007-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.ConTeXt - Copyright : Copyright (C) 2007-2015 John MacFarlane + Copyright : Copyright (C) 2007-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/Custom.hs b/src/Text/Pandoc/Writers/Custom.hs index ce90e4834..b33acb17c 100644 --- a/src/Text/Pandoc/Writers/Custom.hs +++ b/src/Text/Pandoc/Writers/Custom.hs @@ -6,7 +6,7 @@ #else {-# LANGUAGE OverlappingInstances #-} #endif -{- Copyright (C) 2012-2015 John MacFarlane <jgm@berkeley.edu> +{- Copyright (C) 2012-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -25,7 +25,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Custom - Copyright : Copyright (C) 2012-2015 John MacFarlane + Copyright : Copyright (C) 2012-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/Docbook.hs b/src/Text/Pandoc/Writers/Docbook.hs index dce2cbd3e..1afdfc457 100644 --- a/src/Text/Pandoc/Writers/Docbook.hs +++ b/src/Text/Pandoc/Writers/Docbook.hs @@ -1,7 +1,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternGuards #-} {- -Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Docbook - Copyright : Copyright (C) 2006-2015 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 620f9060e..b58c983a1 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -4,7 +4,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ViewPatterns #-} {- -Copyright (C) 2012-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2012-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Docx - Copyright : Copyright (C) 2012-2015 John MacFarlane + Copyright : Copyright (C) 2012-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/DokuWiki.hs b/src/Text/Pandoc/Writers/DokuWiki.hs index 5e29acbaf..81987dc44 100644 --- a/src/Text/Pandoc/Writers/DokuWiki.hs +++ b/src/Text/Pandoc/Writers/DokuWiki.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2008-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2008-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.DokuWiki - Copyright : Copyright (C) 2008-2015 John MacFarlane + Copyright : Copyright (C) 2008-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : Clare Macrae <clare.macrae@googlemail.com> diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index 5b64564ce..c8d64cf0b 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -3,7 +3,7 @@ {-# LANGUAGE PatternGuards #-} {-# LANGUAGE ScopedTypeVariables #-} {- -Copyright (C) 2010-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2010-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.EPUB - Copyright : Copyright (C) 2010-2015 John MacFarlane + Copyright : Copyright (C) 2010-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index b8806a261..0926cc331 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -1,8 +1,8 @@ {-# LANGUAGE PatternGuards #-} {- -Copyright (c) 2011-2012, Sergey Astanin -All rights reserved. +Copyright (c) 2011-2012 Sergey Astanin + 2012-2017 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 diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 9f41f77d1..63e839684 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -3,7 +3,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ViewPatterns #-} {- -Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.HTML - Copyright : Copyright (C) 2006-2015 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/Haddock.hs b/src/Text/Pandoc/Writers/Haddock.hs index eae1377cd..812b46c30 100644 --- a/src/Text/Pandoc/Writers/Haddock.hs +++ b/src/Text/Pandoc/Writers/Haddock.hs @@ -2,7 +2,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} {- -Copyright (C) 2014 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2014-2015, 2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Haddock - Copyright : Copyright (C) 2014 John MacFarlane + Copyright : Copyright (C) 2014-2015,2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/ICML.hs b/src/Text/Pandoc/Writers/ICML.hs index 4d9998665..2f7a4889f 100644 --- a/src/Text/Pandoc/Writers/ICML.hs +++ b/src/Text/Pandoc/Writers/ICML.hs @@ -4,7 +4,7 @@ {- | Module : Text.Pandoc.Writers.ICML - Copyright : Copyright (C) 2013-2016 github.com/mb21 + Copyright : Copyright (C) 2013-2017 github.com/mb21 License : GNU GPL, version 2 or above Stability : alpha diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs index aca7dc969..0b5108a79 100644 --- a/src/Text/Pandoc/Writers/JATS.hs +++ b/src/Text/Pandoc/Writers/JATS.hs @@ -1,7 +1,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternGuards #-} {- -Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> 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 diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 000f4f8fb..26508b7c3 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -2,7 +2,7 @@ {-# LANGUAGE PatternGuards #-} {-# LANGUAGE ScopedTypeVariables #-} {- -Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.LaTeX - Copyright : Copyright (C) 2006-2015 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/Man.hs b/src/Text/Pandoc/Writers/Man.hs index 1f3e17c16..f3d356de7 100644 --- a/src/Text/Pandoc/Writers/Man.hs +++ b/src/Text/Pandoc/Writers/Man.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2007-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2007-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Man - Copyright : Copyright (C) 2007-2015 John MacFarlane + Copyright : Copyright (C) 2007-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index e67dcef6c..37bb98f5f 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -3,7 +3,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} {- -Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Markdown - Copyright : Copyright (C) 2006-2015 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/MediaWiki.hs b/src/Text/Pandoc/Writers/MediaWiki.hs index def245e38..439bbb2f9 100644 --- a/src/Text/Pandoc/Writers/MediaWiki.hs +++ b/src/Text/Pandoc/Writers/MediaWiki.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2008-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2008-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.MediaWiki - Copyright : Copyright (C) 2008-2015 John MacFarlane + Copyright : Copyright (C) 2008-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 534f26a5a..5dd225e19 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2007-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2007-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Ms - Copyright : Copyright (C) 2007-2015 John MacFarlane + Copyright : Copyright (C) 2007-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/Native.hs b/src/Text/Pandoc/Writers/Native.hs index b031a0231..653efb3ce 100644 --- a/src/Text/Pandoc/Writers/Native.hs +++ b/src/Text/Pandoc/Writers/Native.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Native - Copyright : Copyright (C) 2006-2015 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index 6c6f38dbe..68e68c659 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -1,6 +1,6 @@ {-# LANGUAGE ScopedTypeVariables #-} {- -Copyright (C) 2008-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2008-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.ODT - Copyright : Copyright (C) 2008-2015 John MacFarlane + Copyright : Copyright (C) 2008-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/OPML.hs b/src/Text/Pandoc/Writers/OPML.hs index 98510c40f..cdb6ab0d1 100644 --- a/src/Text/Pandoc/Writers/OPML.hs +++ b/src/Text/Pandoc/Writers/OPML.hs @@ -1,6 +1,6 @@ {-# LANGUAGE CPP #-} {- -Copyright (C) 2013-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2013-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.OPML - Copyright : Copyright (C) 2013-2015 John MacFarlane + Copyright : Copyright (C) 2013-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index 491069343..53c1d0c59 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -2,7 +2,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternGuards #-} {- -Copyright (C) 2008-2015 Andrea Rossato <andrea.rossato@ing.unitn.it> +Copyright (C) 2008-2017 Andrea Rossato <andrea.rossato@ing.unitn.it> and John MacFarlane. This program is free software; you can redistribute it and/or modify @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.OpenDocument - Copyright : Copyright (C) 2008-2015 Andrea Rossato and John MacFarlane + Copyright : Copyright (C) 2008-2017 Andrea Rossato and John MacFarlane License : GNU GPL, version 2 or above Maintainer : Andrea Rossato <andrea.rossato@ing.unitn.it> diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index 46752c7ce..ef60e2f6c 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -1,8 +1,8 @@ {-# LANGUAGE OverloadedStrings #-} {- Copyright (C) 2010-2015 Puneeth Chaganti <punchagan@gmail.com> - Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>, - and John MacFarlane <jgm@berkeley.edu> + 2010-2017 John MacFarlane <jgm@berkeley.edu> + 2016-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -21,7 +21,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Org - Copyright : Copyright (C) 2010-2015 Puneeth Chaganti and John MacFarlane + Copyright : © 2010-2015 Puneeth Chaganti <punchagan@gmail.com> + 2010-2017 John MacFarlane <jgm@berkeley.edu> + 2016-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index 24898d62e..d16f013c0 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.RST - Copyright : Copyright (C) 2006-2015 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/RTF.hs b/src/Text/Pandoc/Writers/RTF.hs index 7aa2280dd..e9b29f97d 100644 --- a/src/Text/Pandoc/Writers/RTF.hs +++ b/src/Text/Pandoc/Writers/RTF.hs @@ -1,6 +1,6 @@ {-# LANGUAGE ScopedTypeVariables #-} {- -Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.RTF - Copyright : Copyright (C) 2006-2015 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index 615733a78..c33655522 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2013-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2013-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Shared - Copyright : Copyright (C) 2013-2015 John MacFarlane + Copyright : Copyright (C) 2013-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/TEI.hs b/src/Text/Pandoc/Writers/TEI.hs index 0e1a0526d..7da792c9e 100644 --- a/src/Text/Pandoc/Writers/TEI.hs +++ b/src/Text/Pandoc/Writers/TEI.hs @@ -1,7 +1,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternGuards #-} {- -Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Docbook - Copyright : Copyright (C) 2006-2015 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/Texinfo.hs b/src/Text/Pandoc/Writers/Texinfo.hs index da4f43ee5..9926daea1 100644 --- a/src/Text/Pandoc/Writers/Texinfo.hs +++ b/src/Text/Pandoc/Writers/Texinfo.hs @@ -1,6 +1,7 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2008-2015 John MacFarlane and Peter Wang +Copyright (C) 2008-2017 John MacFarlane + 2012 Peter Wang 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 @@ -19,7 +20,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Texinfo - Copyright : Copyright (C) 2008-2015 John MacFarlane and Peter Wang + Copyright : Copyright (C) 2008-2017 John MacFarlane + 2012 Peter Wang License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/Textile.hs b/src/Text/Pandoc/Writers/Textile.hs index 0ecb746c3..d532f3ed3 100644 --- a/src/Text/Pandoc/Writers/Textile.hs +++ b/src/Text/Pandoc/Writers/Textile.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2010-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2010-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Textile - Copyright : Copyright (C) 2010-2015 John MacFarlane + Copyright : Copyright (C) 2010-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/ZimWiki.hs b/src/Text/Pandoc/Writers/ZimWiki.hs index da8b08de1..bc2cf8f3c 100644 --- a/src/Text/Pandoc/Writers/ZimWiki.hs +++ b/src/Text/Pandoc/Writers/ZimWiki.hs @@ -1,5 +1,6 @@ {- -Copyright (C) 2008-2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2008-2017 John MacFarlane <jgm@berkeley.edu> + 2017 Alex Ivkin 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 @@ -18,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.ZimWiki - Copyright : Copyright (C) 2008-2015 John MacFarlane, 2017 Alex Ivkin + Copyright : Copyright (C) 2008-2017 John MacFarlane, 2017 Alex Ivkin License : GNU GPL, version 2 or above Maintainer : Alex Ivkin <alex@ivkin.net> diff --git a/src/Text/Pandoc/XML.hs b/src/Text/Pandoc/XML.hs index d7fdc4278..b6edd6be5 100644 --- a/src/Text/Pandoc/XML.hs +++ b/src/Text/Pandoc/XML.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2006-2016 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.XML - Copyright : Copyright (C) 2006-2016 John MacFarlane + Copyright : Copyright (C) 2006-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> -- cgit v1.2.3 From 7a17c3eb9f5b7037764e9dfad854cc7d59b47abc Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sun, 14 May 2017 09:28:08 +0200 Subject: Parsing: replace partial with total function Calling `tail` on an empty list raises an exception, while calling the otherwise equivalent `drop 1` will return the empty list again. --- src/Text/Pandoc/Parsing.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index e90f64c5b..e4113f31f 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -1380,5 +1380,5 @@ insertIncludedFile blocks dirs f = do bs <- blocks setInput oldInput setPosition oldPos - updateState $ \s -> s{ stateContainers = tail $ stateContainers s } + updateState $ \s -> s{ stateContainers = drop 1 $ stateContainers s } return bs -- cgit v1.2.3 From 5ff6108b4cd18ad2efdf34a79f576b2b09969123 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sun, 14 May 2017 10:00:58 +0200 Subject: Parsing: introduce `HasIncludeFiles` type class The `insertIncludeFile` function is generalized to work with all parser states which are instances of that class. --- src/Text/Pandoc/Parsing.hs | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index e4113f31f..a6a1a83dd 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -83,6 +83,7 @@ module Text.Pandoc.Parsing ( anyLine, HasMacros (..), HasLogMessages (..), HasLastStrPosition (..), + HasIncludeFiles (..), defaultParserState, HeaderType (..), ParserContext (..), @@ -1008,6 +1009,9 @@ class HasReaderOptions st where -- default getOption f = (f . extractReaderOptions) <$> getState +instance HasReaderOptions ParserState where + extractReaderOptions = stateOptions + class HasQuoteContext st m where getQuoteContext :: (Stream s m t) => ParsecT s st m QuoteContext withQuoteContext :: QuoteContext -> ParsecT s st m a -> ParsecT s st m a @@ -1023,9 +1027,6 @@ instance Monad m => HasQuoteContext ParserState m where setState newState { stateQuoteContext = oldQuoteContext } return result -instance HasReaderOptions ParserState where - extractReaderOptions = stateOptions - class HasHeaderMap st where extractHeaderMap :: st -> M.Map Inlines String updateHeaderMap :: (M.Map Inlines String -> M.Map Inlines String) -> @@ -1067,6 +1068,16 @@ instance HasLogMessages ParserState where addLogMessage msg st = st{ stateLogMessages = msg : stateLogMessages st } getLogMessages st = reverse $ stateLogMessages st +class HasIncludeFiles st where + getIncludeFiles :: st -> [String] + addIncludeFile :: String -> st -> st + dropLatestIncludeFile :: st -> st + +instance HasIncludeFiles ParserState where + getIncludeFiles = stateContainers + addIncludeFile f s = s{ stateContainers = f : stateContainers s } + dropLatestIncludeFile s = s { stateContainers = drop 1 $ stateContainers s } + defaultParserState :: ParserState defaultParserState = ParserState { stateOptions = def, @@ -1358,17 +1369,19 @@ extractIdClass (ident, cls, kvs) = (ident', cls', kvs') Nothing -> cls kvs' = filter (\(k,_) -> k /= "id" || k /= "class") kvs -insertIncludedFile :: PandocMonad m - => ParserT String ParserState m Blocks +-- | Parse content of include file as blocks. Circular includes result in an +-- @PandocParseError@. +insertIncludedFile :: (PandocMonad m, HasIncludeFiles st) + => ParserT String st m Blocks -> [FilePath] -> FilePath - -> ParserT String ParserState m Blocks + -> ParserT String st m Blocks insertIncludedFile blocks dirs f = do oldPos <- getPosition oldInput <- getInput - containers <- stateContainers <$> getState + containers <- getIncludeFiles <$> getState when (f `elem` containers) $ throwError $ PandocParseError $ "Include file loop at " ++ show oldPos - updateState $ \s -> s{ stateContainers = f : stateContainers s } + updateState $ addIncludeFile f mbcontents <- readFileFromDirs dirs f contents <- case mbcontents of Just s -> return s @@ -1380,5 +1393,5 @@ insertIncludedFile blocks dirs f = do bs <- blocks setInput oldInput setPosition oldPos - updateState $ \s -> s{ stateContainers = drop 1 $ stateContainers s } + updateState dropLatestIncludeFile return bs -- cgit v1.2.3 From 9d295f4527f894493c61c5e8129b9f8616a7e2b4 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sun, 14 May 2017 12:40:16 +0200 Subject: Parsing: add `insertIncludedFilesF` which returns F blocks The `insertIncludeFiles` function was generalized and renamed to `insertIncludedFiles'`; the specialized versions are based on that. --- src/Text/Pandoc/Parsing.hs | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index a6a1a83dd..bde13f07e 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -121,6 +121,7 @@ module Text.Pandoc.Parsing ( anyLine, (<+?>), extractIdClass, insertIncludedFile, + insertIncludedFileF, -- * Re-exports from Text.Pandoc.Parsec Stream, runParser, @@ -1369,13 +1370,12 @@ extractIdClass (ident, cls, kvs) = (ident', cls', kvs') Nothing -> cls kvs' = filter (\(k,_) -> k /= "id" || k /= "class") kvs --- | Parse content of include file as blocks. Circular includes result in an --- @PandocParseError@. -insertIncludedFile :: (PandocMonad m, HasIncludeFiles st) - => ParserT String st m Blocks - -> [FilePath] -> FilePath - -> ParserT String st m Blocks -insertIncludedFile blocks dirs f = do +insertIncludedFile' :: (PandocMonad m, HasIncludeFiles st, + Functor mf, Applicative mf, Monad mf) + => ParserT String st m (mf Blocks) + -> [FilePath] -> FilePath + -> ParserT String st m (mf Blocks) +insertIncludedFile' blocks dirs f = do oldPos <- getPosition oldInput <- getInput containers <- getIncludeFiles <$> getState @@ -1395,3 +1395,20 @@ insertIncludedFile blocks dirs f = do setPosition oldPos updateState dropLatestIncludeFile return bs + +-- | Parse content of include file as blocks. Circular includes result in an +-- @PandocParseError@. +insertIncludedFile :: (PandocMonad m, HasIncludeFiles st) + => ParserT String st m Blocks + -> [FilePath] -> FilePath + -> ParserT String st m Blocks +insertIncludedFile blocks dirs f = + runIdentity <$> insertIncludedFile' (Identity <$> blocks) dirs f + +-- | Parse content of include file as future blocks. Circular includes result in +-- an @PandocParseError@. +insertIncludedFileF :: (PandocMonad m, HasIncludeFiles st) + => ParserT String st m (Future st Blocks) + -> [FilePath] -> FilePath + -> ParserT String st m (Future st Blocks) +insertIncludedFileF = insertIncludedFile' -- cgit v1.2.3 From af4bf91c5925b5c6a7431cef8a7997c16d4c7b2b Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sun, 14 May 2017 12:45:31 +0200 Subject: Org reader: add basic file inclusion mechanism Support for the `#+INCLUDE:` file inclusion mechanism was added. Recognized include types are *example*, *export*, *src*, and normal org file inclusion. Advanced features like line numbers and level selection are not implemented yet. Closes: #3510 --- src/Text/Pandoc/Readers/Org/Blocks.hs | 36 ++++++++++++++++++++++++++---- src/Text/Pandoc/Readers/Org/ParserState.hs | 11 ++++++++- src/Text/Pandoc/Readers/Org/Parsing.hs | 1 + 3 files changed, 43 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index 788ec26dc..e77a64efe 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -1,6 +1,3 @@ -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ViewPatterns #-} {- Copyright (C) 2014-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> @@ -18,7 +15,9 @@ 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 -} - +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ViewPatterns #-} {- | Module : Text.Pandoc.Readers.Org.Options Copyright : Copyright (C) 2014-2017 Albert Krewinkel @@ -274,6 +273,7 @@ block = choice [ mempty <$ blanklines , figure , example , genericDrawer + , include , specialLine , horizontalRule , list @@ -717,6 +717,34 @@ exampleCode = B.codeBlockWith ("", ["example"], []) specialLine :: PandocMonad m => OrgParser m (F Blocks) specialLine = fmap return . try $ rawExportLine <|> metaLine <|> commentLine +-- | Include the content of a file. +include :: PandocMonad m => OrgParser m (F Blocks) +include = try $ do + metaLineStart <* stringAnyCase "include:" <* skipSpaces + filename <- includeTarget + blockType <- optionMaybe $ skipSpaces *> many1 alphaNum + blocksParser <- case blockType of + Just "example" -> do + return $ pure . B.codeBlock <$> parseRaw + Just "export" -> do + format <- skipSpaces *> many (noneOf "\n\r\t ") + return $ pure . B.rawBlock format <$> parseRaw + Just "src" -> do + language <- skipSpaces *> many (noneOf "\n\r\t ") + let attr = (mempty, [language], mempty) + return $ pure . B.codeBlockWith attr <$> parseRaw + _ -> return $ pure . B.fromList <$> blockList + anyLine + insertIncludedFileF blocksParser ["."] filename + where + includeTarget :: PandocMonad m => OrgParser m FilePath + includeTarget = do + char '"' + manyTill (noneOf "\n\r\t") (char '"') + + parseRaw :: PandocMonad m => OrgParser m String + parseRaw = many anyChar + rawExportLine :: PandocMonad m => OrgParser m Blocks rawExportLine = try $ do metaLineStart diff --git a/src/Text/Pandoc/Readers/Org/ParserState.hs b/src/Text/Pandoc/Readers/Org/ParserState.hs index f530d1d03..51666fc64 100644 --- a/src/Text/Pandoc/Readers/Org/ParserState.hs +++ b/src/Text/Pandoc/Readers/Org/ParserState.hs @@ -66,7 +66,8 @@ import Text.Pandoc.Logging import Text.Pandoc.Parsing (HasHeaderMap (..), HasIdentifierList (..), HasLogMessages (..), HasLastStrPosition (..), HasQuoteContext (..), - HasReaderOptions (..), ParserContext (..), + HasReaderOptions (..), HasIncludeFiles (..), + ParserContext (..), QuoteContext (..), SourcePos, Future, askF, asksF, returnF, runF, trimInlinesF) @@ -106,6 +107,7 @@ data OrgParserState = OrgParserState , orgStateExportSettings :: ExportSettings , orgStateHeaderMap :: M.Map Inlines String , orgStateIdentifiers :: Set.Set String + , orgStateIncludeFiles :: [String] , orgStateLastForbiddenCharPos :: Maybe SourcePos , orgStateLastPreCharPos :: Maybe SourcePos , orgStateLastStrPos :: Maybe SourcePos @@ -148,6 +150,12 @@ instance HasLogMessages OrgParserState where addLogMessage msg st = st{ orgLogMessages = msg : orgLogMessages st } getLogMessages st = reverse $ orgLogMessages st +instance HasIncludeFiles OrgParserState where + getIncludeFiles = orgStateIncludeFiles + addIncludeFile f st = st { orgStateIncludeFiles = f : orgStateIncludeFiles st } + dropLatestIncludeFile st = + st { orgStateIncludeFiles = drop 1 $ orgStateIncludeFiles st } + instance Default OrgParserState where def = defaultOrgParserState @@ -159,6 +167,7 @@ defaultOrgParserState = OrgParserState , orgStateExportSettings = def , orgStateHeaderMap = M.empty , orgStateIdentifiers = Set.empty + , orgStateIncludeFiles = [] , orgStateLastForbiddenCharPos = Nothing , orgStateLastPreCharPos = Nothing , orgStateLastStrPos = Nothing diff --git a/src/Text/Pandoc/Readers/Org/Parsing.hs b/src/Text/Pandoc/Readers/Org/Parsing.hs index 50f5ebae5..c25b215df 100644 --- a/src/Text/Pandoc/Readers/Org/Parsing.hs +++ b/src/Text/Pandoc/Readers/Org/Parsing.hs @@ -71,6 +71,7 @@ module Text.Pandoc.Readers.Org.Parsing , ellipses , citeKey , gridTableWith + , insertIncludedFileF -- * Re-exports from Text.Pandoc.Parsec , runParser , runParserT -- cgit v1.2.3 From 2de5208311472d4fe951acf69d36156a6465dfc1 Mon Sep 17 00:00:00 2001 From: Henri Werth <henri.werth@alumni.fh-aachen.de> Date: Mon, 15 May 2017 16:37:08 +0200 Subject: Added support for horizontal spacing in LaTeX: parse \, to \8198 (six-per-em space) --- 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 b13fc215b..279cdd138 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -590,7 +590,7 @@ inlineCommands = M.fromList $ , ("u", option (str "u") $ try $ tok >>= accent breve) , ("i", lit "i") , ("\\", linebreak <$ (optional (bracketed inline) *> spaces')) - , (",", pure mempty) + , (",", lit "\8198") , ("@", pure mempty) , (" ", lit "\160") , ("ps", pure $ str "PS." <> space) -- cgit v1.2.3 From 37189667cc2bc86d308ad771318528bd77876912 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 15 May 2017 20:36:11 +0200 Subject: Textile reader: fix bug for certain links in table cells. Closes #3667. --- src/Text/Pandoc/Readers/Textile.hs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Textile.hs b/src/Text/Pandoc/Readers/Textile.hs index df057837f..abf8be452 100644 --- a/src/Text/Pandoc/Readers/Textile.hs +++ b/src/Text/Pandoc/Readers/Textile.hs @@ -586,8 +586,9 @@ link = try $ do char ':' let stop = if bracketed then char ']' - else lookAhead $ space <|> - try (oneOf "!.,;:" *> (space <|> newline)) + else lookAhead $ space <|> eof' <|> + try (oneOf "!.,;:" *> + (space <|> newline <|> eof')) url <- many1Till nonspaceChar stop let name' = if B.toList name == [Str "$"] then B.str url else name return $ if attr == nullAttr @@ -728,3 +729,5 @@ groupedInlineMarkup = try $ do singleton :: a -> [a] singleton x = [x] +eof' :: Monad m => ParserT [Char] s m Char +eof' = '\n' <$ eof -- cgit v1.2.3 From a27e2e8a4e6b4f8a28fe540511f48afccc503ef6 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Tue, 16 May 2017 22:42:34 +0200 Subject: Org reader: put tree parsing code into dedicated module --- src/Text/Pandoc/Readers/Org/Blocks.hs | 212 +---------------------- src/Text/Pandoc/Readers/Org/DocumentTree.hs | 260 ++++++++++++++++++++++++++++ 2 files changed, 262 insertions(+), 210 deletions(-) create mode 100644 src/Text/Pandoc/Readers/Org/DocumentTree.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index e77a64efe..acede0c77 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -33,6 +33,7 @@ module Text.Pandoc.Readers.Org.Blocks ) where import Text.Pandoc.Readers.Org.BlockStarts +import Text.Pandoc.Readers.Org.DocumentTree (headline, headlineToBlocks) import Text.Pandoc.Readers.Org.Inlines import Text.Pandoc.Readers.Org.Meta (metaExport, metaKey, metaLine) import Text.Pandoc.Readers.Org.ParserState @@ -54,196 +55,6 @@ import Data.List (foldl', isPrefixOf) import Data.Maybe (fromMaybe, isNothing) import Data.Monoid ((<>)) --- --- Org headers --- -newtype Tag = Tag { fromTag :: String } - deriving (Show, Eq) - --- | Create a tag containing the given string. -toTag :: String -> Tag -toTag = Tag - --- | The key (also called name or type) of a property. -newtype PropertyKey = PropertyKey { fromKey :: String } - deriving (Show, Eq, Ord) - --- | Create a property key containing the given string. Org mode keys are --- case insensitive and are hence converted to lower case. -toPropertyKey :: String -> PropertyKey -toPropertyKey = PropertyKey . map toLower - --- | The value assigned to a property. -newtype PropertyValue = PropertyValue { fromValue :: String } - --- | Create a property value containing the given string. -toPropertyValue :: String -> PropertyValue -toPropertyValue = PropertyValue - --- | Check whether the property value is non-nil (i.e. truish). -isNonNil :: PropertyValue -> Bool -isNonNil p = map toLower (fromValue p) `notElem` ["()", "{}", "nil"] - --- | Key/value pairs from a PROPERTIES drawer -type Properties = [(PropertyKey, PropertyValue)] - --- | Org mode headline (i.e. a document subtree). -data Headline = Headline - { headlineLevel :: Int - , headlineTodoMarker :: Maybe TodoMarker - , headlineText :: Inlines - , headlineTags :: [Tag] - , headlineProperties :: Properties - , headlineContents :: Blocks - , headlineChildren :: [Headline] - } - --- --- Parsing headlines and subtrees --- - --- | Read an Org mode headline and its contents (i.e. a document subtree). --- @lvl@ gives the minimum acceptable level of the tree. -headline :: PandocMonad m => Int -> OrgParser m (F Headline) -headline lvl = try $ do - level <- headerStart - guard (lvl <= level) - todoKw <- optionMaybe todoKeyword - title <- trimInlinesF . mconcat <$> manyTill inline endOfTitle - tags <- option [] headerTags - newline - properties <- option mempty propertiesDrawer - contents <- blocks - children <- many (headline (level + 1)) - return $ do - title' <- title - contents' <- contents - children' <- sequence children - return $ Headline - { headlineLevel = level - , headlineTodoMarker = todoKw - , headlineText = title' - , headlineTags = tags - , headlineProperties = properties - , headlineContents = contents' - , headlineChildren = children' - } - where - endOfTitle :: Monad m => OrgParser m () - endOfTitle = void . lookAhead $ optional headerTags *> newline - - 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 :: Monad m => Headline -> OrgParser m Blocks -headlineToBlocks hdln@(Headline {..}) = do - maxHeadlineLevels <- getExportSetting exportHeadlineLevels - case () of - _ | any isNoExportTag headlineTags -> return mempty - _ | any isArchiveTag headlineTags -> archivedHeadlineToBlocks hdln - _ | isCommentTitle headlineText -> return mempty - _ | headlineLevel >= maxHeadlineLevels -> headlineToHeaderWithList hdln - _ | otherwise -> headlineToHeaderWithContents hdln - -isNoExportTag :: Tag -> Bool -isNoExportTag = (== toTag "noexport") - -isArchiveTag :: Tag -> Bool -isArchiveTag = (== toTag "ARCHIVE") - --- | Check if the title starts with COMMENT. --- FIXME: This accesses builder internals not intended for use in situations --- like these. Replace once keyword parsing is supported. -isCommentTitle :: Inlines -> Bool -isCommentTitle (B.toList -> (Str "COMMENT":_)) = True -isCommentTitle _ = False - -archivedHeadlineToBlocks :: Monad m => Headline -> OrgParser m Blocks -archivedHeadlineToBlocks hdln = do - archivedTreesOption <- getExportSetting exportArchivedTrees - case archivedTreesOption of - ArchivedTreesNoExport -> return mempty - ArchivedTreesExport -> headlineToHeaderWithContents hdln - ArchivedTreesHeadlineOnly -> headlineToHeader hdln - -headlineToHeaderWithList :: Monad m => Headline -> OrgParser m Blocks -headlineToHeaderWithList hdln@(Headline {..}) = do - maxHeadlineLevels <- getExportSetting exportHeadlineLevels - header <- headlineToHeader hdln - listElements <- sequence (map headlineToBlocks headlineChildren) - let listBlock = if null listElements - then mempty - else B.orderedList listElements - let headerText = if maxHeadlineLevels == headlineLevel - then header - else flattenHeader header - return $ headerText <> headlineContents <> listBlock - where - flattenHeader :: Blocks -> Blocks - flattenHeader blks = - case B.toList blks of - (Header _ _ inlns:_) -> B.para (B.fromList inlns) - _ -> mempty - -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 :: Monad m => Headline -> OrgParser m Blocks -headlineToHeader (Headline {..}) = do - exportTodoKeyword <- getExportSetting exportWithTodoKeywords - let todoText = if exportTodoKeyword - then case headlineTodoMarker of - Just kw -> todoKeywordToInlines kw <> B.space - Nothing -> mempty - else mempty - let text = tagTitle (todoText <> headlineText) headlineTags - let propAttr = propertiesToAttr headlineProperties - attr <- registerHeader propAttr headlineText - return $ B.headerWith attr headlineLevel text - -todoKeyword :: Monad m => OrgParser m TodoMarker -todoKeyword = try $ do - taskStates <- activeTodoMarkers <$> getState - let kwParser tdm = try $ (tdm <$ string (todoMarkerName tdm) <* spaceChar) - choice (map kwParser taskStates) - -todoKeywordToInlines :: TodoMarker -> Inlines -todoKeywordToInlines tdm = - let todoText = todoMarkerName tdm - todoState = map toLower . show $ todoMarkerState tdm - classes = [todoState, todoText] - in B.spanWith (mempty, classes, mempty) (B.str todoText) - -propertiesToAttr :: Properties -> Attr -propertiesToAttr properties = - let - toStringPair prop = (fromKey (fst prop), fromValue (snd prop)) - customIdKey = toPropertyKey "custom_id" - classKey = toPropertyKey "class" - unnumberedKey = toPropertyKey "unnumbered" - specialProperties = [customIdKey, classKey, unnumberedKey] - id' = fromMaybe mempty . fmap fromValue . lookup customIdKey $ properties - cls = fromMaybe mempty . fmap fromValue . lookup classKey $ properties - kvs' = map toStringPair . filter ((`notElem` specialProperties) . fst) - $ properties - isUnnumbered = - fromMaybe False . fmap isNonNil . lookup unnumberedKey $ properties - in - (id', words cls ++ (if isUnnumbered then ["unnumbered"] else []), kvs') - -tagTitle :: Inlines -> [Tag] -> Inlines -tagTitle title tags = title <> (mconcat $ map tagToInline tags) - -tagToInline :: Tag -> Inlines -tagToInline t = B.spanWith ("", ["tag"], [("data-tag-name", fromTag t)]) mempty - - -- -- parsing blocks -- @@ -252,7 +63,7 @@ tagToInline t = B.spanWith ("", ["tag"], [("data-tag-name", fromTag t)]) mempty blockList :: PandocMonad m => OrgParser m [Block] blockList = do initialBlocks <- blocks - headlines <- sequence <$> manyTill (headline 1) eof + headlines <- sequence <$> manyTill (headline blocks inline 1) eof st <- getState headlineBlocks <- fmap mconcat . sequence . map headlineToBlocks $ runF headlines st return . B.toList $ (runF initialBlocks st) <> headlineBlocks @@ -631,25 +442,6 @@ 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 :: Monad m => OrgParser m Properties -propertiesDrawer = try $ do - drawerType <- drawerStart - guard $ map toUpper drawerType == "PROPERTIES" - manyTill property (try drawerEnd) - where - property :: Monad m => OrgParser m (PropertyKey, PropertyValue) - property = try $ (,) <$> key <*> value - - key :: Monad m => OrgParser m PropertyKey - key = fmap toPropertyKey . try $ - skipSpaces *> char ':' *> many1Till nonspaceChar (char ':') - - value :: Monad m => OrgParser m PropertyValue - value = fmap toPropertyValue . try $ - skipSpaces *> manyTill anyChar (try $ skipSpaces *> newline) - -- -- Figures diff --git a/src/Text/Pandoc/Readers/Org/DocumentTree.hs b/src/Text/Pandoc/Readers/Org/DocumentTree.hs new file mode 100644 index 000000000..3e2a046d4 --- /dev/null +++ b/src/Text/Pandoc/Readers/Org/DocumentTree.hs @@ -0,0 +1,260 @@ +{- +Copyright (C) 2014-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> + +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 +-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ViewPatterns #-} +{- | + Module : Text.Pandoc.Readers.Org.DocumentTree + Copyright : Copyright (C) 2014-2017 Albert Krewinkel + License : GNU GPL, version 2 or above + + Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> + +Parsers for org-mode headlines and document subtrees +-} +module Text.Pandoc.Readers.Org.DocumentTree + ( headline + , headlineToBlocks + ) where + +import Control.Monad (guard, void) +import Data.Char (toLower, toUpper) +import Data.Maybe (fromMaybe) +import Data.Monoid ((<>)) +import Text.Pandoc.Builder (Blocks, Inlines) +import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Definition +import Text.Pandoc.Readers.Org.BlockStarts +import Text.Pandoc.Readers.Org.Parsing +import Text.Pandoc.Readers.Org.ParserState + +import qualified Text.Pandoc.Builder as B + +-- +-- Org headers +-- +newtype Tag = Tag { fromTag :: String } + deriving (Show, Eq) + +-- | Create a tag containing the given string. +toTag :: String -> Tag +toTag = Tag + +-- | The key (also called name or type) of a property. +newtype PropertyKey = PropertyKey { fromKey :: String } + deriving (Show, Eq, Ord) + +-- | Create a property key containing the given string. Org mode keys are +-- case insensitive and are hence converted to lower case. +toPropertyKey :: String -> PropertyKey +toPropertyKey = PropertyKey . map toLower + +-- | The value assigned to a property. +newtype PropertyValue = PropertyValue { fromValue :: String } + +-- | Create a property value containing the given string. +toPropertyValue :: String -> PropertyValue +toPropertyValue = PropertyValue + +-- | Check whether the property value is non-nil (i.e. truish). +isNonNil :: PropertyValue -> Bool +isNonNil p = map toLower (fromValue p) `notElem` ["()", "{}", "nil"] + +-- | Key/value pairs from a PROPERTIES drawer +type Properties = [(PropertyKey, PropertyValue)] + +-- | Org mode headline (i.e. a document subtree). +data Headline = Headline + { headlineLevel :: Int + , headlineTodoMarker :: Maybe TodoMarker + , headlineText :: Inlines + , headlineTags :: [Tag] + , headlineProperties :: Properties + , headlineContents :: Blocks + , headlineChildren :: [Headline] + } + +-- | Read an Org mode headline and its contents (i.e. a document subtree). +-- @lvl@ gives the minimum acceptable level of the tree. +headline :: PandocMonad m + => OrgParser m (F Blocks) + -> OrgParser m (F Inlines) + -> Int + -> OrgParser m (F Headline) +headline blocks inline lvl = try $ do + level <- headerStart + guard (lvl <= level) + todoKw <- optionMaybe todoKeyword + title <- trimInlinesF . mconcat <$> manyTill inline endOfTitle + tags <- option [] headerTags + newline + properties <- option mempty propertiesDrawer + contents <- blocks + children <- many (headline blocks inline (level + 1)) + return $ do + title' <- title + contents' <- contents + children' <- sequence children + return $ Headline + { headlineLevel = level + , headlineTodoMarker = todoKw + , headlineText = title' + , headlineTags = tags + , headlineProperties = properties + , headlineContents = contents' + , headlineChildren = children' + } + where + endOfTitle :: Monad m => OrgParser m () + endOfTitle = void . lookAhead $ optional headerTags *> newline + + 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 :: Monad m => Headline -> OrgParser m Blocks +headlineToBlocks hdln@(Headline {..}) = do + maxHeadlineLevels <- getExportSetting exportHeadlineLevels + case () of + _ | any isNoExportTag headlineTags -> return mempty + _ | any isArchiveTag headlineTags -> archivedHeadlineToBlocks hdln + _ | isCommentTitle headlineText -> return mempty + _ | headlineLevel >= maxHeadlineLevels -> headlineToHeaderWithList hdln + _ | otherwise -> headlineToHeaderWithContents hdln + +isNoExportTag :: Tag -> Bool +isNoExportTag = (== toTag "noexport") + +isArchiveTag :: Tag -> Bool +isArchiveTag = (== toTag "ARCHIVE") + +-- | Check if the title starts with COMMENT. +-- FIXME: This accesses builder internals not intended for use in situations +-- like these. Replace once keyword parsing is supported. +isCommentTitle :: Inlines -> Bool +isCommentTitle (B.toList -> (Str "COMMENT":_)) = True +isCommentTitle _ = False + +archivedHeadlineToBlocks :: Monad m => Headline -> OrgParser m Blocks +archivedHeadlineToBlocks hdln = do + archivedTreesOption <- getExportSetting exportArchivedTrees + case archivedTreesOption of + ArchivedTreesNoExport -> return mempty + ArchivedTreesExport -> headlineToHeaderWithContents hdln + ArchivedTreesHeadlineOnly -> headlineToHeader hdln + +headlineToHeaderWithList :: Monad m => Headline -> OrgParser m Blocks +headlineToHeaderWithList hdln@(Headline {..}) = do + maxHeadlineLevels <- getExportSetting exportHeadlineLevels + header <- headlineToHeader hdln + listElements <- sequence (map headlineToBlocks headlineChildren) + let listBlock = if null listElements + then mempty + else B.orderedList listElements + let headerText = if maxHeadlineLevels == headlineLevel + then header + else flattenHeader header + return $ headerText <> headlineContents <> listBlock + where + flattenHeader :: Blocks -> Blocks + flattenHeader blks = + case B.toList blks of + (Header _ _ inlns:_) -> B.para (B.fromList inlns) + _ -> mempty + +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 :: Monad m => Headline -> OrgParser m Blocks +headlineToHeader (Headline {..}) = do + exportTodoKeyword <- getExportSetting exportWithTodoKeywords + let todoText = if exportTodoKeyword + then case headlineTodoMarker of + Just kw -> todoKeywordToInlines kw <> B.space + Nothing -> mempty + else mempty + let text = tagTitle (todoText <> headlineText) headlineTags + let propAttr = propertiesToAttr headlineProperties + attr <- registerHeader propAttr headlineText + return $ B.headerWith attr headlineLevel text + +todoKeyword :: Monad m => OrgParser m TodoMarker +todoKeyword = try $ do + taskStates <- activeTodoMarkers <$> getState + let kwParser tdm = try $ (tdm <$ string (todoMarkerName tdm) <* spaceChar) + choice (map kwParser taskStates) + +todoKeywordToInlines :: TodoMarker -> Inlines +todoKeywordToInlines tdm = + let todoText = todoMarkerName tdm + todoState = map toLower . show $ todoMarkerState tdm + classes = [todoState, todoText] + in B.spanWith (mempty, classes, mempty) (B.str todoText) + +propertiesToAttr :: Properties -> Attr +propertiesToAttr properties = + let + toStringPair prop = (fromKey (fst prop), fromValue (snd prop)) + customIdKey = toPropertyKey "custom_id" + classKey = toPropertyKey "class" + unnumberedKey = toPropertyKey "unnumbered" + specialProperties = [customIdKey, classKey, unnumberedKey] + id' = fromMaybe mempty . fmap fromValue . lookup customIdKey $ properties + cls = fromMaybe mempty . fmap fromValue . lookup classKey $ properties + kvs' = map toStringPair . filter ((`notElem` specialProperties) . fst) + $ properties + isUnnumbered = + fromMaybe False . fmap isNonNil . lookup unnumberedKey $ properties + in + (id', words cls ++ (if isUnnumbered then ["unnumbered"] else []), kvs') + +tagTitle :: Inlines -> [Tag] -> Inlines +tagTitle title tags = title <> (mconcat $ map tagToInline tags) + +-- | Convert +tagToInline :: Tag -> Inlines +tagToInline t = B.spanWith ("", ["tag"], [("data-tag-name", fromTag t)]) mempty + +-- | Read a :PROPERTIES: drawer and return the key/value pairs contained +-- within. +propertiesDrawer :: Monad m => OrgParser m Properties +propertiesDrawer = try $ do + drawerType <- drawerStart + guard $ map toUpper drawerType == "PROPERTIES" + manyTill property (try endOfDrawer) + where + property :: Monad m => OrgParser m (PropertyKey, PropertyValue) + property = try $ (,) <$> key <*> value + + key :: Monad m => OrgParser m PropertyKey + key = fmap toPropertyKey . try $ + skipSpaces *> char ':' *> many1Till nonspaceChar (char ':') + + value :: Monad m => OrgParser m PropertyValue + value = fmap toPropertyValue . try $ + skipSpaces *> manyTill anyChar (try $ skipSpaces *> newline) + + endOfDrawer :: Monad m => OrgParser m String + endOfDrawer = try $ + skipSpaces *> stringAnyCase ":END:" <* skipSpaces <* newline + -- cgit v1.2.3 From 602cd6a327ad41e68e47689d3842f349cf33444d Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Tue, 16 May 2017 22:49:52 +0200 Subject: Org reader: replace `sequence . map` with `mapM` --- src/Text/Pandoc/Readers/Org/Blocks.hs | 2 +- src/Text/Pandoc/Readers/Org/DocumentTree.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 acede0c77..8c78e5157 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -65,7 +65,7 @@ blockList = do initialBlocks <- blocks headlines <- sequence <$> manyTill (headline blocks inline 1) eof st <- getState - headlineBlocks <- fmap mconcat . sequence . map headlineToBlocks $ runF headlines st + headlineBlocks <- fmap mconcat . mapM headlineToBlocks $ runF headlines st return . B.toList $ (runF initialBlocks st) <> headlineBlocks -- | Get the meta information saved in the state. diff --git a/src/Text/Pandoc/Readers/Org/DocumentTree.hs b/src/Text/Pandoc/Readers/Org/DocumentTree.hs index 3e2a046d4..53ec2ef57 100644 --- a/src/Text/Pandoc/Readers/Org/DocumentTree.hs +++ b/src/Text/Pandoc/Readers/Org/DocumentTree.hs @@ -164,7 +164,7 @@ headlineToHeaderWithList :: Monad m => Headline -> OrgParser m Blocks headlineToHeaderWithList hdln@(Headline {..}) = do maxHeadlineLevels <- getExportSetting exportHeadlineLevels header <- headlineToHeader hdln - listElements <- sequence (map headlineToBlocks headlineChildren) + listElements <- mapM headlineToBlocks headlineChildren let listBlock = if null listElements then mempty else B.orderedList listElements @@ -182,7 +182,7 @@ headlineToHeaderWithList hdln@(Headline {..}) = do headlineToHeaderWithContents :: Monad m => Headline -> OrgParser m Blocks headlineToHeaderWithContents hdln@(Headline {..}) = do header <- headlineToHeader hdln - childrenBlocks <- mconcat <$> sequence (map headlineToBlocks headlineChildren) + childrenBlocks <- mconcat <$> mapM headlineToBlocks headlineChildren return $ header <> headlineContents <> childrenBlocks headlineToHeader :: Monad m => Headline -> OrgParser m Blocks -- cgit v1.2.3 From e74bd06cc8b05c0820601cb764ddb679fc9fca77 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Wed, 17 May 2017 02:12:24 +0300 Subject: Txt2Tags parser: newline is not indentation space parses '\n', while spaceChar parses only ' ' and '\t' --- src/Text/Pandoc/Readers/Txt2Tags.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Txt2Tags.hs b/src/Text/Pandoc/Readers/Txt2Tags.hs index 33f785109..012ab7cb1 100644 --- a/src/Text/Pandoc/Readers/Txt2Tags.hs +++ b/src/Text/Pandoc/Readers/Txt2Tags.hs @@ -281,7 +281,7 @@ anyLineNewline :: T2T String anyLineNewline = (++ "\n") <$> anyLine indentWith :: Int -> T2T String -indentWith n = count n space +indentWith n = count n spaceChar -- Table -- cgit v1.2.3 From 55ce47d050fd6e1a38db765c7632e1989d60854d Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Wed, 17 May 2017 01:52:48 +0300 Subject: Move anyLineNewline to Parsing.hs --- src/Text/Pandoc/Parsing.hs | 5 +++++ src/Text/Pandoc/Readers/Markdown.hs | 2 +- src/Text/Pandoc/Readers/Org/Blocks.hs | 4 ---- src/Text/Pandoc/Readers/Org/Parsing.hs | 1 + src/Text/Pandoc/Readers/Txt2Tags.hs | 3 --- 5 files changed, 7 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index e90f64c5b..766d0fd49 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -36,6 +36,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA A utility library with parsers used in pandoc readers. -} module Text.Pandoc.Parsing ( anyLine, + anyLineNewline, many1Till, notFollowedBy', oneOfStrings, @@ -253,6 +254,10 @@ anyLine = do return this _ -> mzero +-- | Parse any line, include the final newline in the output +anyLineNewline :: Stream [Char] m Char => ParserT [Char] st m [Char] +anyLineNewline = (++ "\n") <$> anyLine + -- | Like @manyTill@, but reads at least one item. many1Till :: Stream s m t => ParserT s st m a diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 0c0d07140..7434ef1f6 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -614,7 +614,7 @@ hrule = try $ do -- indentedLine :: PandocMonad m => MarkdownParser m String -indentedLine = indentSpaces >> anyLine >>= return . (++ "\n") +indentedLine = indentSpaces >> anyLineNewline blockDelimiter :: PandocMonad m => (Char -> Bool) diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index 788ec26dc..f0740ede4 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -1017,7 +1017,3 @@ listContinuation markerLength = try $ then count num (char ' ') else choice [ try (count num (char ' ')) , try (char '\t' >> count (num - tabStop) (char ' ')) ] - --- | Parse any line, include the final newline in the output. -anyLineNewline :: Monad m => OrgParser m String -anyLineNewline = (++ "\n") <$> anyLine diff --git a/src/Text/Pandoc/Readers/Org/Parsing.hs b/src/Text/Pandoc/Readers/Org/Parsing.hs index 50f5ebae5..c62718346 100644 --- a/src/Text/Pandoc/Readers/Org/Parsing.hs +++ b/src/Text/Pandoc/Readers/Org/Parsing.hs @@ -31,6 +31,7 @@ functions are adapted to Org-mode specific functionality. module Text.Pandoc.Readers.Org.Parsing ( OrgParser , anyLine + , anyLineNewline , blanklines , newline , parseFromString diff --git a/src/Text/Pandoc/Readers/Txt2Tags.hs b/src/Text/Pandoc/Readers/Txt2Tags.hs index 33f785109..d8b6c016c 100644 --- a/src/Text/Pandoc/Readers/Txt2Tags.hs +++ b/src/Text/Pandoc/Readers/Txt2Tags.hs @@ -277,9 +277,6 @@ listContinuation markerLength = try $ <*> many blankline) where listLine = try $ indentWith markerLength *> anyLineNewline -anyLineNewline :: T2T String -anyLineNewline = (++ "\n") <$> anyLine - indentWith :: Int -> T2T String indentWith n = count n space -- cgit v1.2.3 From 7b3aaee15ab69cdf3125a214c2124b91622af759 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 17 May 2017 16:23:33 +0200 Subject: Markdown writer: Fixed duplicated reference links with `--reference-links` and `--reference-location=section`. Also ensure that there are no empty link references `[]`. Closes #3674. --- src/Text/Pandoc/Writers/Markdown.hs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 37bb98f5f..b70716181 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -91,6 +91,7 @@ instance Default WriterEnv data WriterState = WriterState { stNotes :: Notes , stRefs :: Refs + , stKeys :: Set.Set Key , stIds :: Set.Set String , stNoteNum :: Int } @@ -98,6 +99,7 @@ data WriterState = WriterState { stNotes :: Notes instance Default WriterState where def = WriterState{ stNotes = [] , stRefs = [] + , stKeys = Set.empty , stIds = Set.empty , stNoteNum = 1 } @@ -798,19 +800,21 @@ getKey = toKey . render Nothing -- Prefer label if possible; otherwise, generate a unique key. getReference :: PandocMonad m => Attr -> Doc -> Target -> MD m Doc getReference attr label target = do - st <- get - let keys = map (\(l,_,_) -> getKey l) (stRefs st) - case find (\(_,t,a) -> t == target && a == attr) (stRefs st) of + refs <- gets stRefs + case find (\(_,t,a) -> t == target && a == attr) refs of Just (ref, _, _) -> return ref Nothing -> do - label' <- case getKey label `elem` keys of - True -> -- label is used; generate numerical label - case find (\n -> Key n `notElem` keys) $ - map show [1..(10000 :: Integer)] of - Just x -> return $ text x - Nothing -> throwError $ PandocSomeError "no unique label" - False -> return label - modify (\s -> s{ stRefs = (label', target, attr) : stRefs st }) + keys <- gets stKeys + label' <- if isEmpty label || getKey label `Set.member` keys + then case find (\n -> not (Key n `Set.member` keys)) $ + map show [1..(10000 :: Integer)] of + Just x -> return $ text x + Nothing -> + throwError $ PandocSomeError "no unique label" + else return label + modify (\s -> s{ stRefs = (label', target, attr) : stRefs s, + stKeys = Set.insert (getKey label') (stKeys s) + }) return label' -- | Convert list of Pandoc inline elements to markdown. -- cgit v1.2.3 From 6b8240fc2f45ced4f16403316cab76df15ceaf7a Mon Sep 17 00:00:00 2001 From: Stefan Dresselhaus <sdressel@techfak.uni-bielefeld.de> Date: Wed, 17 May 2017 15:13:35 +0200 Subject: Add `--eol` flag and writer option to control line endings. * Add `--eol=crlf|lf` CLI option. * Add `optEol` to `WriterOptions` [API change] * In `Text.Pandoc.UTF8`, add new functions parameterized on `Newline`: `writeFileWith`, `putStrWith`, `putStrLnWith`, `hPutStrWith`, `hPutStrLnWith`. [API change] * Document option in MANUAL.txt. Closes #3663. Closes #2097. --- src/Text/Pandoc/App.hs | 26 +++++++++++++++++++++----- src/Text/Pandoc/UTF8.hs | 41 +++++++++++++++++++++++++++++++++-------- 2 files changed, 54 insertions(+), 13 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 157100507..9c8e1bde4 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -65,7 +65,7 @@ import System.Directory (Permissions (..), doesFileExist, findExecutable, import System.Environment (getArgs, getEnvironment, getProgName) import System.Exit (ExitCode (..), exitSuccess) import System.FilePath -import System.IO (stdout) +import System.IO (stdout, nativeNewline, Newline(..)) import System.IO.Error (isDoesNotExistError) import Text.Pandoc import Text.Pandoc.Builder (setMeta) @@ -411,6 +411,8 @@ convertWithOpts opts = do return $ ("csl", jatsEncoded) : optMetadata opts else return $ optMetadata opts + let eol = fromMaybe nativeNewline $ optEol opts + runIO' $ do (doc, media) <- withMediaBag $ sourceToDoc sources >>= ( (if isJust (optExtractMedia opts) @@ -463,7 +465,7 @@ convertWithOpts opts = do else id output <- f writerOptions doc selfcontain (output ++ ['\n' | not standalone]) >>= - writerFn outputFile . handleEntities + writerFn eol outputFile . handleEntities type Transform = Pandoc -> Pandoc @@ -567,6 +569,7 @@ data Opt = Opt , optIncludeBeforeBody :: [FilePath] -- ^ Files to include before , optIncludeAfterBody :: [FilePath] -- ^ Files to include after body , optIncludeInHeader :: [FilePath] -- ^ Files to include in header + , optEol :: Maybe Newline -- ^ Enforce line-endings } -- | Defaults for command-line options. @@ -635,6 +638,7 @@ defaultOpts = Opt , optIncludeBeforeBody = [] , optIncludeAfterBody = [] , optIncludeInHeader = [] + , optEol = Nothing } addMetadata :: (String, String) -> Pandoc -> Pandoc @@ -783,9 +787,9 @@ writeFnBinary :: MonadIO m => FilePath -> B.ByteString -> m () writeFnBinary "-" = liftIO . B.putStr writeFnBinary f = liftIO . B.writeFile (UTF8.encodePath f) -writerFn :: MonadIO m => FilePath -> String -> m () -writerFn "-" = liftIO . UTF8.putStr -writerFn f = liftIO . UTF8.writeFile f +writerFn :: MonadIO m => Newline -> FilePath -> String -> m () +writerFn eol "-" = liftIO . UTF8.putStrWith eol +writerFn eol f = liftIO . UTF8.writeFileWith eol f lookupHighlightStyle :: Maybe String -> IO (Maybe Style) lookupHighlightStyle Nothing = return Nothing @@ -958,6 +962,18 @@ options = "NUMBER") "" -- "Dpi (default 96)" + , Option "" ["eol"] + (ReqArg + (\arg opt -> + case toLower <$> arg of + "crlf" -> return opt { optEol = Just CRLF } + "lf" -> return opt { optEol = Just LF } + -- mac-syntax (cr) is not supported in ghc-base. + _ -> E.throwIO $ PandocOptionError + "--eol must be one of crlf (Windows), lf (Unix)") + "crlf|lf") + "" -- "EOL (default OS-dependent)" + , Option "" ["wrap"] (ReqArg (\arg opt -> diff --git a/src/Text/Pandoc/UTF8.hs b/src/Text/Pandoc/UTF8.hs index e27a24e63..84043d4cb 100644 --- a/src/Text/Pandoc/UTF8.hs +++ b/src/Text/Pandoc/UTF8.hs @@ -28,11 +28,16 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA UTF-8 aware string IO functions that will work with GHC 6.10, 6.12, or 7. -} module Text.Pandoc.UTF8 ( readFile - , writeFile , getContents + , writeFileWith + , writeFile + , putStrWith , putStr + , putStrLnWith , putStrLn + , hPutStrWith , hPutStr + , hPutStrLnWith , hPutStrLn , hGetContents , toString @@ -61,23 +66,43 @@ readFile f = do h <- openFile (encodePath f) ReadMode hGetContents h -writeFile :: FilePath -> String -> IO () -writeFile f s = withFile (encodePath f) WriteMode $ \h -> hPutStr h s - getContents :: IO String getContents = hGetContents stdin +writeFileWith :: Newline -> FilePath -> String -> IO () +writeFileWith eol f s = + withFile (encodePath f) WriteMode $ \h -> hPutStrWith eol h s + +writeFile :: FilePath -> String -> IO () +writeFile = writeFileWith nativeNewline + +putStrWith :: Newline -> String -> IO () +putStrWith eol s = hPutStrWith eol stdout s + putStr :: String -> IO () -putStr s = hPutStr stdout s +putStr = putStrWith nativeNewline + +putStrLnWith :: Newline -> String -> IO () +putStrLnWith eol s = hPutStrLnWith eol stdout s putStrLn :: String -> IO () -putStrLn s = hPutStrLn stdout s +putStrLn = putStrLnWith nativeNewline + +hPutStrWith :: Newline -> Handle -> String -> IO () +hPutStrWith eol h s = + hSetNewlineMode h (NewlineMode eol eol) >> + hSetEncoding h utf8 >> IO.hPutStr h s hPutStr :: Handle -> String -> IO () -hPutStr h s = hSetEncoding h utf8 >> IO.hPutStr h s +hPutStr = hPutStrWith nativeNewline + +hPutStrLnWith :: Newline -> Handle -> String -> IO () +hPutStrLnWith eol h s = + hSetNewlineMode h (NewlineMode eol eol) >> + hSetEncoding h utf8 >> IO.hPutStrLn h s hPutStrLn :: Handle -> String -> IO () -hPutStrLn h s = hSetEncoding h utf8 >> IO.hPutStrLn h s +hPutStrLn = hPutStrLnWith nativeNewline hGetContents :: Handle -> IO String hGetContents = fmap toString . B.hGetContents -- cgit v1.2.3 From 818d5c2f354cd4896659493452722c030ae7c766 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 18 May 2017 13:20:32 +0200 Subject: Markdown: allow attributes in reference links to start on next line. This addresses a subsidiary issue in #3674. --- src/Text/Pandoc/Readers/Markdown.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 7434ef1f6..af7588562 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -392,7 +392,9 @@ referenceKey = try $ do src <- try betweenAngles <|> sourceURL tit <- option "" referenceTitle attr <- option nullAttr $ try $ - guardEnabled Ext_link_attributes >> skipSpaces >> attributes + do guardEnabled Ext_link_attributes + skipSpaces >> optional newline >> skipSpaces + attributes addKvs <- option [] $ guardEnabled Ext_mmd_link_attributes >> many (try $ spnl >> keyValAttr) blanklines -- cgit v1.2.3 From 0f6458c0c13380969ccac82d54a0e68a3ec76200 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 18 May 2017 13:38:19 +0200 Subject: Don't double extract images from docx. This fixes a regression that was introduced when `--extract-media` was generalized to work with any input format. We were getting two versions of each image extracted from a docx, one with a hash, one with the original filename, though only the hash one was used. This patch restores the original behavior (using the original filename). Pointed out in comments on #3674. Thanks to @laperouse. --- src/Text/Pandoc/Class.hs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 4ef56ec33..8b2adc507 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -345,19 +345,24 @@ withPaths (p:ps) action fp = catchError (action (p </> fp)) (\_ -> withPaths ps action fp) --- | Traverse tree, filling media bag. +-- | Traverse tree, filling media bag for any images that +-- aren't already in the media bag. fillMediaBag :: PandocMonad m => Maybe String -> Pandoc -> m Pandoc fillMediaBag sourceURL d = walkM handleImage d where handleImage :: PandocMonad m => Inline -> m Inline handleImage (Image attr lab (src, tit)) = catchError - (do (bs, mt) <- fetchItem sourceURL src - let ext = fromMaybe (takeExtension src) - (mt >>= extensionFromMimeType) - let bs' = BL.fromChunks [bs] - let basename = showDigest $ sha1 bs' - let fname = basename <.> ext - insertMedia fname mt bs' - return $ Image attr lab (fname, tit)) + (do mediabag <- getMediaBag + case lookupMedia src mediabag of + Just (_, _) -> return $ Image attr lab (src, tit) + Nothing -> do + (bs, mt) <- downloadOrRead sourceURL src + let ext = fromMaybe (takeExtension src) + (mt >>= extensionFromMimeType) + let bs' = BL.fromChunks [bs] + let basename = showDigest $ sha1 bs' + let fname = basename <.> ext + insertMedia fname mt bs' + return $ Image attr lab (fname, tit)) (\e -> do case e of PandocResourceNotFound _ -> do -- cgit v1.2.3 From b9185b02162ea56ee685594e1c5cfb816e796754 Mon Sep 17 00:00:00 2001 From: Ian <iandol@users.noreply.github.com> Date: Fri, 19 May 2017 04:34:13 +0800 Subject: Docx writer: Change FigureWithCaption to CaptionedFigure (#3658) Edit styles.xml as part of the fix for #3656 --- src/Text/Pandoc/Writers/Docx.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index b58c983a1..2282a5c58 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -875,7 +875,7 @@ blockToOpenXML' opts (Para [Image attr alt (src,'f':'i':'g':':':tit)]) = do let prop = pCustomStyle $ if null alt then "Figure" - else "FigureWithCaption" + else "CaptionedFigure" paraProps <- local (\env -> env { envParaProperties = prop : envParaProperties env }) (getParaProps False) contents <- inlinesToOpenXML opts [Image attr alt (src,tit)] captionNode <- withParaProp (pCustomStyle "ImageCaption") -- cgit v1.2.3 From f870a2d8ea8cca5c8cf9ca30d87e0ff758618f18 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 18 May 2017 22:50:07 +0200 Subject: Don't render LaTeX images with data: URIs. LaTeX can't handle these. Note that --extract-media can be used when the input contains data: URIs. Closes #3636. --- src/Text/Pandoc/Writers/LaTeX.hs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 26508b7c3..31c70e99d 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -1062,6 +1062,9 @@ inlineToLaTeX (Link _ txt (src, _)) = src' <- stringToLaTeX URLString (escapeURI src) return $ text ("\\href{" ++ src' ++ "}{") <> contents <> char '}' +inlineToLaTeX il@(Image _ _ ('d':'a':'t':'a':':':_, _)) = do + report $ InlineNotRendered il + return empty inlineToLaTeX (Image attr _ (source, _)) = do setEmptyLine False modify $ \s -> s{ stGraphics = True } -- cgit v1.2.3 From 7a09b7b21dbbee34332047d07eae88fe152340b8 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Thu, 18 May 2017 23:12:17 +0200 Subject: Org reader: fix smart parsing behavior Parsing of smart quotes and special characters can either be enabled via the `smart` language extension or the `'` and `-` export options. Smart parsing is active if either the extension or export option is enabled. Only smart parsing of special characters (like ellipses and en and em dashes) is enabled by default, while smart quotes are disabled. This means that all smart parsing features will be enabled by adding the `smart` language extension. Fine-grained control is possible by leaving the language extension disabled. In that case, smart parsing is controlled via the aforementioned export OPTIONS only. Previously, all smart parsing was disabled unless the language extension was enabled. --- src/Text/Pandoc/Readers/Org/Inlines.hs | 23 ++++++++++++++--------- src/Text/Pandoc/Readers/Org/ParserState.hs | 2 +- 2 files changed, 15 insertions(+), 10 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/Inlines.hs b/src/Text/Pandoc/Readers/Org/Inlines.hs index 800264db0..aa376fe25 100644 --- a/src/Text/Pandoc/Readers/Org/Inlines.hs +++ b/src/Text/Pandoc/Readers/Org/Inlines.hs @@ -862,24 +862,29 @@ macro = try $ do smart :: PandocMonad m => OrgParser m (F Inlines) smart = do - guardEnabled Ext_smart doubleQuoted <|> singleQuoted <|> choice (map (return <$>) [orgApostrophe, orgDash, orgEllipses]) where orgDash = do - guard =<< getExportSetting exportSpecialStrings + guardOrSmartEnabled =<< getExportSetting exportSpecialStrings dash <* updatePositions '-' orgEllipses = do - guard =<< getExportSetting exportSpecialStrings + guardOrSmartEnabled =<< getExportSetting exportSpecialStrings ellipses <* updatePositions '.' - orgApostrophe = - (char '\'' <|> char '\8217') <* updateLastPreCharPos - <* updateLastForbiddenCharPos - *> return (B.str "\x2019") + orgApostrophe = do + guardEnabled Ext_smart + (char '\'' <|> char '\8217') <* updateLastPreCharPos + <* updateLastForbiddenCharPos + return (B.str "\x2019") + +guardOrSmartEnabled :: PandocMonad m => Bool -> OrgParser m () +guardOrSmartEnabled b = do + smartExtension <- extensionEnabled Ext_smart <$> getOption readerExtensions + guard (b || smartExtension) singleQuoted :: PandocMonad m => OrgParser m (F Inlines) singleQuoted = try $ do - guard =<< getExportSetting exportSmartQuotes + guardOrSmartEnabled =<< getExportSetting exportSmartQuotes singleQuoteStart updatePositions '\'' withQuoteContext InSingleQuote $ @@ -891,7 +896,7 @@ singleQuoted = try $ do -- in the same paragraph. doubleQuoted :: PandocMonad m => OrgParser m (F Inlines) doubleQuoted = try $ do - guard =<< getExportSetting exportSmartQuotes + guardOrSmartEnabled =<< getExportSetting exportSmartQuotes doubleQuoteStart updatePositions '"' contents <- mconcat <$> many (try $ notFollowedBy doubleQuoteEnd >> inline) diff --git a/src/Text/Pandoc/Readers/Org/ParserState.hs b/src/Text/Pandoc/Readers/Org/ParserState.hs index 51666fc64..1736cd881 100644 --- a/src/Text/Pandoc/Readers/Org/ParserState.hs +++ b/src/Text/Pandoc/Readers/Org/ParserState.hs @@ -252,7 +252,7 @@ defaultExportSettings = ExportSettings , exportDrawers = Left ["LOGBOOK"] , exportEmphasizedText = True , exportHeadlineLevels = 3 - , exportSmartQuotes = True + , exportSmartQuotes = False , exportSpecialStrings = True , exportSubSuperscripts = True , exportWithAuthor = True -- cgit v1.2.3 From ca77f0a95e03cace027a235ebbc1effa99ea030a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 19 May 2017 21:01:45 +0200 Subject: RST writer: add empty comments when needed... to avoid including a blocquote in the indented content of a preceding block. Closes #3675. --- src/Text/Pandoc/Writers/RST.hs | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index d16f013c0..5dc2ba31a 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -57,6 +57,7 @@ data WriterState = , stHasRawTeX :: Bool , stOptions :: WriterOptions , stTopLevel :: Bool + , stLastNested :: Bool } type RST = StateT WriterState @@ -67,7 +68,7 @@ writeRST opts document = do let st = WriterState { stNotes = [], stLinks = [], stImages = [], stHasMath = False, stHasRawTeX = False, stOptions = opts, - stTopLevel = True} + stTopLevel = True, stLastNested = False} evalStateT (pandocToRST document) st -- | Return RST representation of document. @@ -343,11 +344,32 @@ blockListToRST' :: PandocMonad m -> RST m Doc blockListToRST' topLevel blocks = do tl <- gets stTopLevel - modify (\s->s{stTopLevel=topLevel}) - res <- vcat `fmap` mapM blockToRST blocks + modify (\s->s{stTopLevel=topLevel, stLastNested=False}) + res <- vcat `fmap` mapM blockToRST' blocks modify (\s->s{stTopLevel=tl}) return res +blockToRST' :: PandocMonad m => Block -> RST m Doc +blockToRST' (x@BlockQuote{}) = do + lastNested <- gets stLastNested + res <- blockToRST x + modify (\s -> s{stLastNested = True}) + return $ if lastNested + then ".." $+$ res + else res +blockToRST' x = do + modify (\s -> s{stLastNested = + case x of + Para [Image _ _ (_,'f':'i':'g':':':_)] -> True + Para{} -> False + Plain{} -> False + Header{} -> False + LineBlock{} -> False + HorizontalRule -> False + _ -> True + }) + blockToRST x + blockListToRST :: PandocMonad m => [Block] -- ^ List of block elements -> RST m Doc -- cgit v1.2.3 From 8d4fbe6a2a50d93bff0e9c7ada73774ff1bc17c6 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 20 May 2017 17:09:47 +0200 Subject: SelfContained: fixed problem with embedded fonts. Closes #3629. However, there is still room for improvement. `@import` with following media declaration is not handled. Also `@import` with a simple filename (rather than `url(...)` is not handled. --- src/Text/Pandoc/SelfContained.hs | 54 ++++++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 13 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/SelfContained.hs b/src/Text/Pandoc/SelfContained.hs index c0a12adf2..e9a91b690 100644 --- a/src/Text/Pandoc/SelfContained.hs +++ b/src/Text/Pandoc/SelfContained.hs @@ -35,6 +35,7 @@ import Codec.Compression.GZip as Gzip import Control.Applicative ((<|>)) import Control.Monad.Except (throwError) import Control.Monad.Trans (lift) +import Data.Monoid ((<>)) import Data.ByteString (ByteString) import Data.ByteString.Base64 import qualified Data.ByteString.Char8 as B @@ -95,9 +96,9 @@ convertTags sourceURL (t@(TagOpen "script" as):TagClose "script":ts) = (("src",dataUri) : [(x,y) | (x,y) <- as, x /= "src"]) : TagClose "script" : rest Right (mime, bs) - | (mime == "text/javascript" || - mime == "application/javascript" || - mime == "application/x-javascript") && + | ("text/javascript" `isPrefixOf` mime || + "application/javascript" `isPrefixOf` mime || + "application/x-javascript" `isPrefixOf` mime) && not ("</script" `B.isInfixOf` bs) -> return $ TagOpen "script" [("type", typeAttr)|not (null typeAttr)] @@ -121,11 +122,12 @@ convertTags sourceURL (t@(TagOpen "link" as):ts) = (("href",dataUri) : [(x,y) | (x,y) <- as, x /= "href"]) : rest Right (mime, bs) - | mime == "text/css" && not ("</" `B.isInfixOf` bs) -> do + | "text/css" `isPrefixOf` mime + && not ("</" `B.isInfixOf` bs) -> do rest <- convertTags sourceURL $ dropWhile (==TagClose "link") ts return $ - TagOpen "style" [("type", "text/css")] + TagOpen "style" [("type", mime)] : TagText (toString bs) : TagClose "style" : rest @@ -149,7 +151,20 @@ cssURLs sourceURL d orig = do parseCSSUrls :: PandocMonad m => Maybe String -> FilePath -> ParsecT ByteString () m ByteString parseCSSUrls sourceURL d = B.concat <$> P.many - (pCSSWhite <|> pCSSComment <|> pCSSUrl sourceURL d <|> pCSSOther) + (pCSSWhite <|> pCSSComment <|> pCSSImport sourceURL d <|> + pCSSUrl sourceURL d <|> pCSSOther) + +pCSSImport :: PandocMonad m => Maybe String -> FilePath + -> ParsecT ByteString () m ByteString +pCSSImport sourceURL d = P.try $ do + P.string "@import" + P.spaces + res <- pCSSUrl' sourceURL d + P.spaces + P.optional $ P.char ';' >> P.spaces + case res of + Left b -> return $ B.pack "@import " <> b + Right (_, b) -> return b -- Note: some whitespace in CSS is significant, so we can't collapse it! pCSSWhite :: PandocMonad m => ParsecT ByteString () m ByteString @@ -170,13 +185,25 @@ pCSSOther = do pCSSUrl :: PandocMonad m => Maybe String -> FilePath -> ParsecT ByteString () m ByteString pCSSUrl sourceURL d = P.try $ do + res <- pCSSUrl' sourceURL d + case res of + Left b -> return b + Right (mt,b) -> do + let enc = makeDataURI (mt, b) + return (B.pack $ "url(" ++ enc ++ ")") + +pCSSUrl' :: PandocMonad m + => Maybe String -> FilePath + -> ParsecT ByteString () m (Either ByteString (MimeType, ByteString)) +pCSSUrl' sourceURL d = P.try $ do P.string "url(" P.spaces quote <- P.option Nothing (Just <$> P.oneOf "\"'") url <- P.manyTill P.anyChar (maybe (P.lookAhead (P.char ')')) P.char quote) P.spaces P.char ')' - let fallback = B.pack ("url(" ++ maybe "" (:[]) quote ++ trim url ++ + let fallback = Left $ + B.pack ("url(" ++ maybe "" (:[]) quote ++ trim url ++ maybe "" (:[]) quote ++ ")") -- pipes are used in URLs provided by Google Code fonts -- but parseURI doesn't like them, so we escape them: @@ -186,13 +213,14 @@ pCSSUrl sourceURL d = P.try $ do u -> do let url' = if isURI u then u else d </> u res <- lift $ getData sourceURL "" url' case res of - Left uri -> return (B.pack $ "url(" ++ uri ++ ")") + Left uri -> return $ Left (B.pack $ "url(" ++ uri ++ ")") Right (mt, raw) -> do - -- note that the downloaded content may + -- note that the downloaded CSS may -- itself contain url(...). - raw' <- cssURLs sourceURL d raw - let enc = makeDataURI (mt, raw') - return (B.pack $ "url(" ++ enc ++ ")") + b <- if "text/css" `isPrefixOf` mt + then cssURLs sourceURL d raw + else return raw + return $ Right (mt, b) getDataURI :: PandocMonad m => Maybe String -> MimeType -> String -> m String getDataURI sourceURL mimetype src = do @@ -224,7 +252,7 @@ getData sourceURL mimetype src = do uriQuery = "", uriFragment = "" } _ -> Nothing - result <- if mime == "text/css" + result <- if "text/css" `isPrefixOf` mime then cssURLs cssSourceURL (takeDirectory src) raw' else return raw' return $ Right (mime, result) -- cgit v1.2.3 From 93eaf33e6e7fbb364c83e6bde66f253a8b14297b Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 20 May 2017 17:27:07 +0200 Subject: SelfContained: handle @import with quoted string. --- src/Text/Pandoc/SelfContained.hs | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/SelfContained.hs b/src/Text/Pandoc/SelfContained.hs index e9a91b690..f8ad43b1e 100644 --- a/src/Text/Pandoc/SelfContained.hs +++ b/src/Text/Pandoc/SelfContained.hs @@ -159,9 +159,10 @@ pCSSImport :: PandocMonad m => Maybe String -> FilePath pCSSImport sourceURL d = P.try $ do P.string "@import" P.spaces - res <- pCSSUrl' sourceURL d + res <- (pQuoted <|> pUrl) >>= handleCSSUrl sourceURL d + P.spaces + P.char ';' P.spaces - P.optional $ P.char ';' >> P.spaces case res of Left b -> return $ B.pack "@import " <> b Right (_, b) -> return b @@ -185,31 +186,44 @@ pCSSOther = do pCSSUrl :: PandocMonad m => Maybe String -> FilePath -> ParsecT ByteString () m ByteString pCSSUrl sourceURL d = P.try $ do - res <- pCSSUrl' sourceURL d + res <- pUrl >>= handleCSSUrl sourceURL d case res of Left b -> return b Right (mt,b) -> do let enc = makeDataURI (mt, b) return (B.pack $ "url(" ++ enc ++ ")") -pCSSUrl' :: PandocMonad m - => Maybe String -> FilePath - -> ParsecT ByteString () m (Either ByteString (MimeType, ByteString)) -pCSSUrl' sourceURL d = P.try $ do +pQuoted :: PandocMonad m + => ParsecT ByteString () m (String, ByteString) +pQuoted = P.try $ do + quote <- P.oneOf "\"'" + url <- P.manyTill P.anyChar (P.char quote) + let fallback = B.pack ([quote] ++ trim url ++ [quote]) + return (url, fallback) + +pUrl :: PandocMonad m + => ParsecT ByteString () m (String, ByteString) +pUrl = P.try $ do P.string "url(" P.spaces quote <- P.option Nothing (Just <$> P.oneOf "\"'") url <- P.manyTill P.anyChar (maybe (P.lookAhead (P.char ')')) P.char quote) P.spaces P.char ')' - let fallback = Left $ - B.pack ("url(" ++ maybe "" (:[]) quote ++ trim url ++ + let fallback = B.pack ("url(" ++ maybe "" (:[]) quote ++ trim url ++ maybe "" (:[]) quote ++ ")") + return (url, fallback) + +handleCSSUrl :: PandocMonad m + => Maybe String -> FilePath -> (String, ByteString) + -> ParsecT ByteString () m + (Either ByteString (MimeType, ByteString)) +handleCSSUrl sourceURL d (url, fallback) = do -- pipes are used in URLs provided by Google Code fonts -- but parseURI doesn't like them, so we escape them: case escapeURIString (/='|') (trim url) of - '#':_ -> return fallback - 'd':'a':'t':'a':':':_ -> return fallback + '#':_ -> return $ Left fallback + 'd':'a':'t':'a':':':_ -> return $ Left fallback u -> do let url' = if isURI u then u else d </> u res <- lift $ getData sourceURL "" url' case res of -- cgit v1.2.3 From fd6e65b00ffc628488c27171f7dd9ab833c436c6 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 20 May 2017 21:43:53 +0200 Subject: Added `--resource-path=SEARCHPATH` command line option. SEARCHPATH is separated by the usual character, depending on OS (: on unix, ; on windows). Note: This does not yet work for PDF output, because the routine that creates PDFs runs outside PandocMonad. (This has to do with its use of inTemporaryDirectory and its interaction with our exceptions.) The best solution would be to figure out how to move the PDF creation routines into PandocMonad. Second-best, just pass an extra parameter in? See #852. --- src/Text/Pandoc/App.hs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 9c8e1bde4..a4967e5d1 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -70,7 +70,7 @@ import System.IO.Error (isDoesNotExistError) import Text.Pandoc import Text.Pandoc.Builder (setMeta) import Text.Pandoc.Class (PandocIO, getLog, withMediaBag, - extractMedia, fillMediaBag) + extractMedia, fillMediaBag, setResourcePath) import Text.Pandoc.Highlighting (highlightingStyles) import Text.Pandoc.Lua ( runLuaFilter ) import Text.Pandoc.PDF (makePDF) @@ -414,6 +414,7 @@ convertWithOpts opts = do let eol = fromMaybe nativeNewline $ optEol opts runIO' $ do + setResourcePath $ "." : (optResourcePath opts) (doc, media) <- withMediaBag $ sourceToDoc sources >>= ( (if isJust (optExtractMedia opts) then fillMediaBag (writerSourceURL writerOptions) @@ -569,6 +570,7 @@ data Opt = Opt , optIncludeBeforeBody :: [FilePath] -- ^ Files to include before , optIncludeAfterBody :: [FilePath] -- ^ Files to include after body , optIncludeInHeader :: [FilePath] -- ^ Files to include in header + , optResourcePath :: [FilePath] -- ^ Path to search for images etc , optEol :: Maybe Newline -- ^ Enforce line-endings } @@ -638,6 +640,7 @@ defaultOpts = Opt , optIncludeBeforeBody = [] , optIncludeAfterBody = [] , optIncludeInHeader = [] + , optResourcePath = [] , optEol = Nothing } @@ -1052,6 +1055,14 @@ options = "FILE") "" -- "File to include after document body" + , Option "" ["resource-path"] + (ReqArg + (\arg opt -> return opt { optResourcePath = + splitSearchPath arg }) + "SEARCHPATH") + "" -- "Paths to search for images and other resources" + + , Option "" ["self-contained"] (NoArg (\opt -> return opt { optSelfContained = True, -- cgit v1.2.3 From 5c44fd554fbebc2e01a0aa9f569468789f353bf4 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 20 May 2017 22:42:30 +0200 Subject: PDF: Refactoring, makePDF is now in PandocIO [API change]. --- src/Text/Pandoc/PDF.hs | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index cc9b38f7f..822067e78 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -72,16 +72,15 @@ changePathSeparators :: FilePath -> FilePath changePathSeparators = intercalate "/" . splitDirectories #endif -makePDF :: MonadIO m - => String -- ^ pdf creator (pdflatex, lualatex, +makePDF :: String -- ^ pdf creator (pdflatex, lualatex, -- xelatex, context, wkhtmltopdf, pdfroff) -> (WriterOptions -> Pandoc -> PandocIO String) -- ^ writer -> WriterOptions -- ^ options -> Verbosity -- ^ verbosity level -> MediaBag -- ^ media -> Pandoc -- ^ document - -> m (Either ByteString ByteString) -makePDF "wkhtmltopdf" writer opts verbosity _ doc@(Pandoc meta _) = liftIO $ do + -> PandocIO (Either ByteString ByteString) +makePDF "wkhtmltopdf" writer opts verbosity _ doc@(Pandoc meta _) = 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' });", @@ -102,17 +101,13 @@ makePDF "wkhtmltopdf" writer opts verbosity _ doc@(Pandoc meta _) = liftIO $ do ,("margin-left", fromMaybe (Just "1.25in") (getField "margin-left" meta')) ] - source <- runIOorExplode $ do - setVerbosity verbosity - writer opts doc - html2pdf verbosity args source -makePDF "pdfroff" writer opts verbosity _mediabag doc = liftIO $ do - source <- runIOorExplode $ do - setVerbosity verbosity - writer opts doc + source <- writer opts doc + liftIO $ html2pdf verbosity args source +makePDF "pdfroff" writer opts verbosity _mediabag doc = do + source <- writer opts doc let args = ["-ms", "-mpdfmark", "-e", "-t", "-k", "-KUTF-8", "-i", "--no-toc-relocation"] - ms2pdf verbosity args source + liftIO $ ms2pdf verbosity args source makePDF program writer opts verbosity mediabag doc = do let withTemp = if takeBaseName program == "context" then withTempDirectory "." -- cgit v1.2.3 From 03cb05f4c614f08600bcd8e90a7fd1ca13ae33a2 Mon Sep 17 00:00:00 2001 From: Marc Schreiber <marc.schreiber@fh-aachen.de> Date: Thu, 20 Apr 2017 11:11:01 +0200 Subject: Improve SVG image size code. The old code made some unwise assumptions about how the svg file would look. See #3580. --- src/Text/Pandoc/ImageSize.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/ImageSize.hs b/src/Text/Pandoc/ImageSize.hs index a0800e499..4d914a10c 100644 --- a/src/Text/Pandoc/ImageSize.hs +++ b/src/Text/Pandoc/ImageSize.hs @@ -116,7 +116,7 @@ imageType img = case B.take 4 img of _ -> mzero findSvgTag :: ByteString -> Bool -findSvgTag img = B.null $ snd (B.breakSubstring img "<svg") +findSvgTag img = "<svg" `B.isInfixOf` img || "<SVG" `B.isInfixOf` img imageSize :: WriterOptions -> ByteString -> Either String ImageSize imageSize opts img = -- cgit v1.2.3 From 753d5811e2d08ac27dd77659e43a6968b7ebd72a Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sun, 21 May 2017 00:14:08 +0300 Subject: RST reader: make use of anyLineNewline (#3686) --- src/Text/Pandoc/Readers/RST.hs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 868bfafa4..e85ebade1 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -559,8 +559,7 @@ listLine :: Monad m => Int -> RSTParser m [Char] listLine markerLength = try $ do notFollowedBy blankline indentWith markerLength - line <- anyLine - return $ line ++ "\n" + anyLineNewline -- indent by specified number of spaces (or equiv. tabs) indentWith :: Monad m => Int -> RSTParser m [Char] -- cgit v1.2.3 From d109c8be8fe97631fa29affed0de6c4d50f56a95 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 20 May 2017 23:23:52 +0200 Subject: PDF: better error message for non-converted svg images. --- src/Text/Pandoc/PDF.hs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index 822067e78..090bcbc6d 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -159,6 +159,7 @@ convertImage tmpdir fname = Just "image/png" -> doNothing Just "image/jpeg" -> doNothing Just "application/pdf" -> doNothing + Just "image/svg+xml" -> return $ Left "conversion from svg not supported" _ -> JP.readImage fname >>= \res -> case res of Left e -> return $ Left e -- cgit v1.2.3 From 6a7f980247bd2e3fcb7b977edbbcd1fc17758074 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 20 May 2017 23:46:31 +0200 Subject: PDF: Got --resource-path working with pdf output. See #852. --- src/Text/Pandoc/PDF.hs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index 090bcbc6d..e8a826e4c 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -63,8 +63,8 @@ import Text.Pandoc.Writers.Shared (getField, metaToJSON) import Data.List (intercalate) #endif import Text.Pandoc.Class (PandocIO, report, runIO, runIOorExplode, - setMediaBag, setVerbosity, - fillMediaBag, extractMedia) + setMediaBag, setVerbosity, getResourcePath, + setResourcePath, fillMediaBag, extractMedia) import Text.Pandoc.Logging #ifdef _WINDOWS @@ -112,8 +112,9 @@ makePDF program writer opts verbosity mediabag doc = do let withTemp = if takeBaseName program == "context" then withTempDirectory "." else withTempDir + resourcePath <- getResourcePath liftIO $ withTemp "tex2pdf." $ \tmpdir -> do - doc' <- handleImages verbosity opts mediabag tmpdir doc + doc' <- handleImages verbosity opts resourcePath mediabag tmpdir doc source <- runIOorExplode $ do setVerbosity verbosity writer opts doc' @@ -126,13 +127,15 @@ makePDF program writer opts verbosity mediabag doc = do handleImages :: Verbosity -> WriterOptions + -> [FilePath] -> MediaBag -> FilePath -- ^ temp dir to store images -> Pandoc -- ^ document -> IO Pandoc -handleImages verbosity opts mediabag tmpdir doc = do +handleImages verbosity opts resourcePath mediabag tmpdir doc = do doc' <- runIOorExplode $ do setVerbosity verbosity + setResourcePath resourcePath setMediaBag mediabag fillMediaBag (writerSourceURL opts) doc >>= extractMedia tmpdir -- cgit v1.2.3 From 8c1b81bbef7125a9a2fde9d6894578f06bf4cedd Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 21 May 2017 08:59:06 +0200 Subject: Finished implemtation of `--resource-path`. * Default is just working directory. * Working directory must be explicitly specifide if `--resource-path` option is used. --- src/Text/Pandoc/App.hs | 4 ++-- src/Text/Pandoc/Readers/LaTeX.hs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index a4967e5d1..c874a2cde 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -414,7 +414,7 @@ convertWithOpts opts = do let eol = fromMaybe nativeNewline $ optEol opts runIO' $ do - setResourcePath $ "." : (optResourcePath opts) + setResourcePath (optResourcePath opts) (doc, media) <- withMediaBag $ sourceToDoc sources >>= ( (if isJust (optExtractMedia opts) then fillMediaBag (writerSourceURL writerOptions) @@ -640,7 +640,7 @@ defaultOpts = Opt , optIncludeBeforeBody = [] , optIncludeAfterBody = [] , optIncludeInHeader = [] - , optResourcePath = [] + , optResourcePath = ["."] , optEol = Nothing } diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 30c788666..84758d309 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -46,7 +46,7 @@ import Safe (minimumDef) import System.FilePath (addExtension, replaceExtension, takeExtension) import Text.Pandoc.Builder import Text.Pandoc.Class (PandocMonad, PandocPure, lookupEnv, readFileFromDirs, - report, setResourcePath) + report, setResourcePath, getResourcePath) import Text.Pandoc.Highlighting (fromListingsLanguage, languagesByExtension) import Text.Pandoc.ImageSize (numUnit, showFl) import Text.Pandoc.Logging @@ -417,7 +417,7 @@ blockCommands = M.fromList $ graphicsPath :: PandocMonad m => LP m Blocks graphicsPath = do ps <- bgroup *> (manyTill braced egroup) - setResourcePath (".":ps) + getResourcePath >>= setResourcePath . (++ ps) return mempty addMeta :: PandocMonad m => ToMetaValue a => String -> a -> LP m () -- cgit v1.2.3 From aa1e39858dd0ad25fd5e0cf0e2e19182bd4f157b Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 21 May 2017 11:42:50 +0200 Subject: Text.Pandoc.App: ToJSON and FromJSON instances for Opts. This can be used e.g. to pass options via web interface, such as trypandoc. --- src/Text/Pandoc/App.hs | 27 ++++++++++++++++++++++----- src/Text/Pandoc/Logging.hs | 10 ++++++++++ src/Text/Pandoc/Options.hs | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index c874a2cde..eee72fd3c 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -1,6 +1,7 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} +{-# LANGUAGE DeriveGeneric #-} {- Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> @@ -42,12 +43,14 @@ import qualified Control.Exception as E import Control.Monad.Except (throwError) import Control.Monad import Control.Monad.Trans -import Data.Aeson (eitherDecode', encode) +import Data.Aeson (eitherDecode', encode, ToJSON(..), FromJSON(..), + genericToEncoding, defaultOptions) import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as B import Data.Char (toLower, toUpper) import qualified Data.Set as Set import Data.Foldable (foldrM) +import GHC.Generics import Data.List (intercalate, isPrefixOf, isSuffixOf, sort) import qualified Data.Map as M import Data.Maybe (fromMaybe, isJust, isNothing) @@ -65,7 +68,8 @@ import System.Directory (Permissions (..), doesFileExist, findExecutable, import System.Environment (getArgs, getEnvironment, getProgName) import System.Exit (ExitCode (..), exitSuccess) import System.FilePath -import System.IO (stdout, nativeNewline, Newline(..)) +import System.IO (stdout, nativeNewline) +import qualified System.IO as IO (Newline(..)) import System.IO.Error (isDoesNotExistError) import Text.Pandoc import Text.Pandoc.Builder (setMeta) @@ -86,6 +90,12 @@ import System.Posix.IO (stdOutput) import System.Posix.Terminal (queryTerminal) #endif +data Newline = LF | CRLF deriving (Show, Generic) + +instance ToJSON Newline where + toEncoding = genericToEncoding defaultOptions +instance FromJSON Newline + parseOptions :: [OptDescr (Opt -> IO Opt)] -> Opt -> IO Opt parseOptions options' defaults = do rawArgs <- map UTF8.decodeArg <$> getArgs @@ -411,7 +421,10 @@ convertWithOpts opts = do return $ ("csl", jatsEncoded) : optMetadata opts else return $ optMetadata opts - let eol = fromMaybe nativeNewline $ optEol opts + let eol = case optEol opts of + Just CRLF -> IO.CRLF + Just LF -> IO.LF + Nothing -> nativeNewline runIO' $ do setResourcePath (optResourcePath opts) @@ -572,7 +585,11 @@ data Opt = Opt , optIncludeInHeader :: [FilePath] -- ^ Files to include in header , optResourcePath :: [FilePath] -- ^ Path to search for images etc , optEol :: Maybe Newline -- ^ Enforce line-endings - } + } deriving (Generic, Show) + +instance ToJSON Opt where + toEncoding = genericToEncoding defaultOptions +instance FromJSON Opt -- | Defaults for command-line options. defaultOpts :: Opt @@ -790,7 +807,7 @@ writeFnBinary :: MonadIO m => FilePath -> B.ByteString -> m () writeFnBinary "-" = liftIO . B.putStr writeFnBinary f = liftIO . B.writeFile (UTF8.encodePath f) -writerFn :: MonadIO m => Newline -> FilePath -> String -> m () +writerFn :: MonadIO m => IO.Newline -> FilePath -> String -> m () writerFn eol "-" = liftIO . UTF8.putStrWith eol writerFn eol f = liftIO . UTF8.writeFileWith eol f diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs index 2cca4b7d3..bf7f33d29 100644 --- a/src/Text/Pandoc/Logging.hs +++ b/src/Text/Pandoc/Logging.hs @@ -39,6 +39,7 @@ module Text.Pandoc.Logging ( , messageVerbosity ) where +import Control.Monad (mzero) import Data.Aeson import Data.Aeson.Encode.Pretty (Config (..), defConfig, encodePretty', keyOrder) @@ -56,6 +57,15 @@ data Verbosity = ERROR | WARNING | INFO | DEBUG instance ToJSON Verbosity where toJSON x = toJSON (show x) +instance FromJSON Verbosity where + parseJSON (String t) = + case t of + "ERROR" -> return ERROR + "WARNING" -> return WARNING + "INFO" -> return INFO + "DEBUG" -> return DEBUG + _ -> mzero + parseJSON _ = mzero data LogMessage = SkippedContent String SourcePos diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index 6757c6782..c7211c86e 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -45,6 +45,8 @@ module Text.Pandoc.Options ( module Text.Pandoc.Extensions , def , isEnabled ) where +import Data.Aeson (ToJSON(..), FromJSON(..), + genericToEncoding, defaultOptions) import Data.Data (Data) import Data.Default import qualified Data.Set as Set @@ -104,17 +106,29 @@ data HTMLMathMethod = PlainMath | KaTeX String String -- url of stylesheet and katex.js deriving (Show, Read, Eq, Data, Typeable, Generic) +instance ToJSON HTMLMathMethod where + toEncoding = genericToEncoding defaultOptions +instance FromJSON HTMLMathMethod + data CiteMethod = Citeproc -- use citeproc to render them | Natbib -- output natbib cite commands | Biblatex -- output biblatex cite commands deriving (Show, Read, Eq, Data, Typeable, Generic) +instance ToJSON CiteMethod where + toEncoding = genericToEncoding defaultOptions +instance FromJSON CiteMethod + -- | Methods for obfuscating email addresses in HTML. data ObfuscationMethod = NoObfuscation | ReferenceObfuscation | JavascriptObfuscation deriving (Show, Read, Eq, Data, Typeable, Generic) +instance ToJSON ObfuscationMethod where + toEncoding = genericToEncoding defaultOptions +instance FromJSON ObfuscationMethod + -- | Varieties of HTML slide shows. data HTMLSlideVariant = S5Slides | SlidySlides @@ -124,18 +138,30 @@ data HTMLSlideVariant = S5Slides | NoSlides deriving (Show, Read, Eq, Data, Typeable, Generic) +instance ToJSON HTMLSlideVariant where + toEncoding = genericToEncoding defaultOptions +instance FromJSON HTMLSlideVariant + -- | Options for accepting or rejecting MS Word track-changes. data TrackChanges = AcceptChanges | RejectChanges | AllChanges deriving (Show, Read, Eq, Data, Typeable, Generic) +instance ToJSON TrackChanges where + toEncoding = genericToEncoding defaultOptions +instance FromJSON TrackChanges + -- | Options for wrapping text in the output. data WrapOption = WrapAuto -- ^ Automatically wrap to width | WrapNone -- ^ No non-semantic newlines | WrapPreserve -- ^ Preserve wrapping of input source deriving (Show, Read, Eq, Data, Typeable, Generic) +instance ToJSON WrapOption where + toEncoding = genericToEncoding defaultOptions +instance FromJSON WrapOption + -- | Options defining the type of top-level headers. data TopLevelDivision = TopLevelPart -- ^ Top-level headers become parts | TopLevelChapter -- ^ Top-level headers become chapters @@ -144,12 +170,20 @@ data TopLevelDivision = TopLevelPart -- ^ Top-level headers become parts -- heuristics deriving (Show, Read, Eq, Data, Typeable, Generic) +instance ToJSON TopLevelDivision where + toEncoding = genericToEncoding defaultOptions +instance FromJSON TopLevelDivision + -- | Locations for footnotes and references in markdown output data ReferenceLocation = EndOfBlock -- ^ End of block | EndOfSection -- ^ prior to next section header (or end of document) | EndOfDocument -- ^ at end of document deriving (Show, Read, Eq, Data, Typeable, Generic) +instance ToJSON ReferenceLocation where + toEncoding = genericToEncoding defaultOptions +instance FromJSON ReferenceLocation + -- | Options for writers data WriterOptions = WriterOptions { writerTemplate :: Maybe String -- ^ Template to use -- cgit v1.2.3 From 30a3deadcce18dd53a79a4915d915beb815702cf Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 22 May 2017 11:10:15 +0300 Subject: Move indentWith to Text.Pandoc.Parsing (#3687) --- src/Text/Pandoc/Parsing.hs | 12 ++++++++++++ src/Text/Pandoc/Readers/Org/Blocks.hs | 9 --------- src/Text/Pandoc/Readers/Org/Parsing.hs | 1 + src/Text/Pandoc/Readers/RST.hs | 9 --------- src/Text/Pandoc/Readers/Txt2Tags.hs | 3 --- 5 files changed, 13 insertions(+), 21 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index ce2523d12..e430c7cb5 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -37,6 +37,7 @@ A utility library with parsers used in pandoc readers. -} module Text.Pandoc.Parsing ( anyLine, anyLineNewline, + indentWith, many1Till, notFollowedBy', oneOfStrings, @@ -260,6 +261,17 @@ anyLine = do anyLineNewline :: Stream [Char] m Char => ParserT [Char] st m [Char] anyLineNewline = (++ "\n") <$> anyLine +-- | Parse indent by specified number of spaces (or equiv. tabs) +indentWith :: Stream [Char] m Char + => HasReaderOptions st + => Int -> ParserT [Char] st m [Char] +indentWith num = do + tabStop <- getOption readerTabStop + if (num < tabStop) + then count num (char ' ') + else choice [ try (count num (char ' ')) + , try (char '\t' >> indentWith (num - tabStop)) ] + -- | Like @manyTill@, but reads at least one item. many1Till :: Stream s m t => ParserT s st m a diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index f5823c7aa..fa2f7fac5 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -828,12 +828,3 @@ listContinuation markerLength = try $ <*> many blankline) where listLine = try $ indentWith markerLength *> anyLineNewline - - -- indent by specified number of spaces (or equiv. tabs) - indentWith :: Monad m => Int -> OrgParser m String - indentWith num = do - tabStop <- getOption readerTabStop - if num < tabStop - then count num (char ' ') - else choice [ try (count num (char ' ')) - , try (char '\t' >> count (num - tabStop) (char ' ')) ] diff --git a/src/Text/Pandoc/Readers/Org/Parsing.hs b/src/Text/Pandoc/Readers/Org/Parsing.hs index 5c93a7eca..1d3e8c257 100644 --- a/src/Text/Pandoc/Readers/Org/Parsing.hs +++ b/src/Text/Pandoc/Readers/Org/Parsing.hs @@ -32,6 +32,7 @@ module Text.Pandoc.Readers.Org.Parsing ( OrgParser , anyLine , anyLineNewline + , indentWith , blanklines , newline , parseFromString diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index e85ebade1..ac1f4f834 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -561,15 +561,6 @@ listLine markerLength = try $ do indentWith markerLength anyLineNewline --- indent by specified number of spaces (or equiv. tabs) -indentWith :: Monad m => Int -> RSTParser m [Char] -indentWith num = do - tabStop <- getOption readerTabStop - if (num < tabStop) - then count num (char ' ') - else choice [ try (count num (char ' ')), - (try (char '\t' >> count (num - tabStop) (char ' '))) ] - -- parse raw text for one list item, excluding start marker and continuations rawListItem :: Monad m => RSTParser m Int -> RSTParser m (Int, [Char]) diff --git a/src/Text/Pandoc/Readers/Txt2Tags.hs b/src/Text/Pandoc/Readers/Txt2Tags.hs index aa7774b4c..ba2b20083 100644 --- a/src/Text/Pandoc/Readers/Txt2Tags.hs +++ b/src/Text/Pandoc/Readers/Txt2Tags.hs @@ -277,9 +277,6 @@ listContinuation markerLength = try $ <*> many blankline) where listLine = try $ indentWith markerLength *> anyLineNewline -indentWith :: Int -> T2T String -indentWith n = count n spaceChar - -- Table table :: T2T Blocks -- cgit v1.2.3 From 4d1e9b8e4198990e515185fd3a0d6047f7999a61 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 22 May 2017 10:10:04 +0200 Subject: Let `--eol` take `native` as an argument. Add `Native` to the `LineEnding` type. Make `optEol` a `Native` rather than `Maybe Native`. --- src/Text/Pandoc/App.hs | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index eee72fd3c..97954764a 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -90,11 +90,11 @@ import System.Posix.IO (stdOutput) import System.Posix.Terminal (queryTerminal) #endif -data Newline = LF | CRLF deriving (Show, Generic) +data LineEnding = LF | CRLF | Native deriving (Show, Generic) -instance ToJSON Newline where +instance ToJSON LineEnding where toEncoding = genericToEncoding defaultOptions -instance FromJSON Newline +instance FromJSON LineEnding parseOptions :: [OptDescr (Opt -> IO Opt)] -> Opt -> IO Opt parseOptions options' defaults = do @@ -422,9 +422,9 @@ convertWithOpts opts = do else return $ optMetadata opts let eol = case optEol opts of - Just CRLF -> IO.CRLF - Just LF -> IO.LF - Nothing -> nativeNewline + CRLF -> IO.CRLF + LF -> IO.LF + Native -> nativeNewline runIO' $ do setResourcePath (optResourcePath opts) @@ -584,7 +584,7 @@ data Opt = Opt , optIncludeAfterBody :: [FilePath] -- ^ Files to include after body , optIncludeInHeader :: [FilePath] -- ^ Files to include in header , optResourcePath :: [FilePath] -- ^ Path to search for images etc - , optEol :: Maybe Newline -- ^ Enforce line-endings + , optEol :: LineEnding -- ^ Style of line-endings to use } deriving (Generic, Show) instance ToJSON Opt where @@ -658,7 +658,7 @@ defaultOpts = Opt , optIncludeAfterBody = [] , optIncludeInHeader = [] , optResourcePath = ["."] - , optEol = Nothing + , optEol = Native } addMetadata :: (String, String) -> Pandoc -> Pandoc @@ -986,12 +986,13 @@ options = (ReqArg (\arg opt -> case toLower <$> arg of - "crlf" -> return opt { optEol = Just CRLF } - "lf" -> return opt { optEol = Just LF } + "crlf" -> return opt { optEol = CRLF } + "lf" -> return opt { optEol = LF } + "native" -> return opt { optEol = Native } -- mac-syntax (cr) is not supported in ghc-base. _ -> E.throwIO $ PandocOptionError - "--eol must be one of crlf (Windows), lf (Unix)") - "crlf|lf") + "--eol must be crlf, lf, or native") + "crlf|lf|native") "" -- "EOL (default OS-dependent)" , Option "" ["wrap"] -- cgit v1.2.3 From 5debb0da0f94d1454d51cacede7c4844f01cc2f5 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert+github@zeitkraut.de> Date: Tue, 23 May 2017 09:48:11 +0200 Subject: Shared: Provide custom isURI that rejects unknown schemes [isURI] We also export the set of known `schemes`. The new function replaces the function of the same name from `Network.URI`, as the latter did not check whether a scheme is well-known. E.g. MediaWiki wikis frequently feature pages with names like `User:John`. These links were interpreted as URIs, thus turning internal links into global links. This is prevented by also checking whether the scheme of a URI is frequently used (i.e. is IANA registered or an otherwise well-known scheme). Fixes: #2713 Update set of well-known URIs from IANA list All official IANA schemes (as of 2017-05-22) are included in the set of known schemes. The four non-official schemes doi, isbn, javascript, and pmid are kept. --- src/Text/Pandoc/App.hs | 4 +-- src/Text/Pandoc/Parsing.hs | 27 +------------- src/Text/Pandoc/Readers/Txt2Tags.hs | 1 - src/Text/Pandoc/SelfContained.hs | 4 +-- src/Text/Pandoc/Shared.hs | 69 +++++++++++++++++++++++++++++++++++- src/Text/Pandoc/Writers/ConTeXt.hs | 2 +- src/Text/Pandoc/Writers/DokuWiki.hs | 3 +- src/Text/Pandoc/Writers/FB2.hs | 3 +- src/Text/Pandoc/Writers/Haddock.hs | 1 - src/Text/Pandoc/Writers/ICML.hs | 3 +- src/Text/Pandoc/Writers/LaTeX.hs | 2 +- src/Text/Pandoc/Writers/Markdown.hs | 1 - src/Text/Pandoc/Writers/MediaWiki.hs | 1 - src/Text/Pandoc/Writers/RST.hs | 1 - src/Text/Pandoc/Writers/Texinfo.hs | 2 +- src/Text/Pandoc/Writers/ZimWiki.hs | 5 ++- 16 files changed, 81 insertions(+), 48 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 97954764a..845146f34 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -57,7 +57,7 @@ import Data.Maybe (fromMaybe, isJust, isNothing) import qualified Data.Text as T import Data.Yaml (decode) import qualified Data.Yaml as Yaml -import Network.URI (URI (..), isURI, parseURI) +import Network.URI (URI (..), parseURI) import Paths_pandoc (getDataDir) import Skylighting (Style, Syntax (..), defaultSyntaxMap, parseTheme) import Skylighting.Parser (missingIncludes, parseSyntaxDefinition, @@ -80,7 +80,7 @@ import Text.Pandoc.Lua ( runLuaFilter ) import Text.Pandoc.PDF (makePDF) import Text.Pandoc.Process (pipeProcess) import Text.Pandoc.SelfContained (makeSelfContained, makeDataURI) -import Text.Pandoc.Shared (headerShift, openURL, readDataFile, +import Text.Pandoc.Shared (isURI, headerShift, openURL, readDataFile, readDataFileUTF8, safeRead, tabFilter) import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.XML (toEntities) diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index e430c7cb5..c6be48d19 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -465,33 +465,8 @@ emailAddress = try $ toResult <$> mailbox <*> (char '@' *> domain) sepby1 p sep = (:) <$> p <*> (many (try $ sep >> p)) --- Schemes from http://www.iana.org/assignments/uri-schemes.html plus --- the unofficial schemes coap, doi, javascript, isbn, pmid -schemes :: [String] -schemes = ["coap","doi","javascript","aaa","aaas","about","acap","cap","cid", - "crid","data","dav","dict","dns","file","ftp","geo","go","gopher", - "h323","http","https","iax","icap","im","imap","info","ipp","iris", - "iris.beep","iris.xpc","iris.xpcs","iris.lwz","ldap","mailto","mid", - "msrp","msrps","mtqp","mupdate","news","nfs","ni","nih","nntp", - "opaquelocktoken","pop","pres","rtsp","service","session","shttp","sieve", - "sip","sips","sms","snmp","soap.beep","soap.beeps","tag","tel","telnet", - "tftp","thismessage","tn3270","tip","tv","urn","vemmi","ws","wss","xcon", - "xcon-userid","xmlrpc.beep","xmlrpc.beeps","xmpp","z39.50r","z39.50s", - "adiumxtra","afp","afs","aim","apt","attachment","aw","beshare","bitcoin", - "bolo","callto","chrome","chrome-extension","com-eventbrite-attendee", - "content", "cvs","dlna-playsingle","dlna-playcontainer","dtn","dvb", - "ed2k","facetime","feed","finger","fish","gg","git","gizmoproject", - "gtalk","hcp","icon","ipn","irc","irc6","ircs","itms","jar","jms", - "keyparc","lastfm","ldaps","magnet","maps","market","message","mms", - "ms-help","msnim","mumble","mvn","notes","oid","palm","paparazzi", - "platform","proxy","psyc","query","res","resource","rmi","rsync", - "rtmp","secondlife","sftp","sgn","skype","smb","soldat","spotify", - "ssh","steam","svn","teamspeak","things","udp","unreal","ut2004", - "ventrilo","view-source","webcal","wtai","wyciwyg","xfire","xri", - "ymsgr", "isbn", "pmid"] - uriScheme :: Stream s m Char => ParserT s st m String -uriScheme = oneOfStringsCI schemes +uriScheme = oneOfStringsCI (Set.toList schemes) -- | Parses a URI. Returns pair of original and URI-escaped version. uri :: Stream [Char] m Char => ParserT [Char] st m (String, String) diff --git a/src/Text/Pandoc/Readers/Txt2Tags.hs b/src/Text/Pandoc/Readers/Txt2Tags.hs index ba2b20083..05c6c9a69 100644 --- a/src/Text/Pandoc/Readers/Txt2Tags.hs +++ b/src/Text/Pandoc/Readers/Txt2Tags.hs @@ -42,7 +42,6 @@ import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (macro, space, spaces, uri) import Text.Pandoc.Shared (compactify, compactifyDL, escapeURI) ---import Network.URI (isURI) -- Not sure whether to use this function import Control.Monad (guard, void, when) import Control.Monad.Reader (Reader, asks, runReader) import Data.Default diff --git a/src/Text/Pandoc/SelfContained.hs b/src/Text/Pandoc/SelfContained.hs index f8ad43b1e..55df147b6 100644 --- a/src/Text/Pandoc/SelfContained.hs +++ b/src/Text/Pandoc/SelfContained.hs @@ -42,7 +42,7 @@ import qualified Data.ByteString.Char8 as B import qualified Data.ByteString.Lazy as L import Data.Char (isAlphaNum, isAscii, toLower) import Data.List (isPrefixOf) -import Network.URI (URI (..), escapeURIString, isURI, parseURI) +import Network.URI (URI (..), escapeURIString, parseURI) import System.FilePath (takeDirectory, takeExtension, (</>)) import Text.HTML.TagSoup import Text.Pandoc.Class (PandocMonad (..), fetchItem, report) @@ -50,7 +50,7 @@ import Text.Pandoc.Error import Text.Pandoc.Logging import Text.Pandoc.MIME (MimeType) import Text.Pandoc.Options (WriterOptions (..)) -import Text.Pandoc.Shared (renderTags', trim) +import Text.Pandoc.Shared (isURI, renderTags', trim) import Text.Pandoc.UTF8 (toString) import Text.Parsec (ParsecT, runParserT) import qualified Text.Parsec as P diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 3a61656e5..7a1e6f3e3 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -81,6 +81,9 @@ module Text.Pandoc.Shared ( openURL, collapseFilePath, filteredFilesFromArchive, + -- * URI handling + schemes, + isURI, -- * Error handling mapLeft, -- * for squashing blocks @@ -104,7 +107,7 @@ import Data.List ( find, stripPrefix, intercalate ) import Data.Maybe (mapMaybe) import Data.Version ( showVersion ) import qualified Data.Map as M -import Network.URI ( escapeURIString, unEscapeString ) +import Network.URI ( URI(uriScheme), escapeURIString, unEscapeString, parseURI ) import qualified Data.Set as Set import System.Directory import System.FilePath (splitDirectories, isPathSeparator) @@ -774,6 +777,70 @@ filteredFilesFromArchive zf f = fileAndBinary :: Archive -> FilePath -> Maybe (FilePath, BL.ByteString) fileAndBinary a fp = findEntryByPath fp a >>= \e -> Just (fp, fromEntry e) + +-- +-- IANA URIs +-- + +-- | Schemes from http://www.iana.org/assignments/uri-schemes.html plus +-- the unofficial schemes doi, javascript, isbn, pmid. +schemes :: Set.Set String +schemes = Set.fromList + -- Official IANA schemes + [ "aaa", "aaas", "about", "acap", "acct", "acr", "adiumxtra", "afp", "afs" + , "aim", "appdata", "apt", "attachment", "aw", "barion", "beshare", "bitcoin" + , "blob", "bolo", "browserext", "callto", "cap", "chrome", "chrome-extension" + , "cid", "coap", "coaps", "com-eventbrite-attendee", "content", "crid", "cvs" + , "data", "dav", "dict", "dis", "dlna-playcontainer", "dlna-playsingle" + , "dns", "dntp", "dtn", "dvb", "ed2k", "example", "facetime", "fax", "feed" + , "feedready", "file", "filesystem", "finger", "fish", "ftp", "geo", "gg" + , "git", "gizmoproject", "go", "gopher", "graph", "gtalk", "h323", "ham" + , "hcp", "http", "https", "hxxp", "hxxps", "hydrazone", "iax", "icap", "icon" + , "im", "imap", "info", "iotdisco", "ipn", "ipp", "ipps", "irc", "irc6" + , "ircs", "iris", "iris.beep", "iris.lwz", "iris.xpc", "iris.xpcs" + , "isostore", "itms", "jabber", "jar", "jms", "keyparc", "lastfm", "ldap" + , "ldaps", "lvlt", "magnet", "mailserver", "mailto", "maps", "market" + , "message", "mid", "mms", "modem", "mongodb", "moz", "ms-access" + , "ms-browser-extension", "ms-drive-to", "ms-enrollment", "ms-excel" + , "ms-gamebarservices", "ms-getoffice", "ms-help", "ms-infopath" + , "ms-media-stream-id", "ms-officeapp", "ms-project", "ms-powerpoint" + , "ms-publisher", "ms-search-repair", "ms-secondary-screen-controller" + , "ms-secondary-screen-setup", "ms-settings", "ms-settings-airplanemode" + , "ms-settings-bluetooth", "ms-settings-camera", "ms-settings-cellular" + , "ms-settings-cloudstorage", "ms-settings-connectabledevices" + , "ms-settings-displays-topology", "ms-settings-emailandaccounts" + , "ms-settings-language", "ms-settings-location", "ms-settings-lock" + , "ms-settings-nfctransactions", "ms-settings-notifications" + , "ms-settings-power", "ms-settings-privacy", "ms-settings-proximity" + , "ms-settings-screenrotation", "ms-settings-wifi", "ms-settings-workplace" + , "ms-spd", "ms-sttoverlay", "ms-transit-to", "ms-virtualtouchpad" + , "ms-visio", "ms-walk-to", "ms-whiteboard", "ms-whiteboard-cmd", "ms-word" + , "msnim", "msrp", "msrps", "mtqp", "mumble", "mupdate", "mvn", "news", "nfs" + , "ni", "nih", "nntp", "notes", "ocf", "oid", "onenote", "onenote-cmd" + , "opaquelocktoken", "pack", "palm", "paparazzi", "pkcs11", "platform", "pop" + , "pres", "prospero", "proxy", "pwid", "psyc", "qb", "query", "redis" + , "rediss", "reload", "res", "resource", "rmi", "rsync", "rtmfp", "rtmp" + , "rtsp", "rtsps", "rtspu", "secondlife", "service", "session", "sftp", "sgn" + , "shttp", "sieve", "sip", "sips", "skype", "smb", "sms", "smtp", "snews" + , "snmp", "soap.beep", "soap.beeps", "soldat", "spotify", "ssh", "steam" + , "stun", "stuns", "submit", "svn", "tag", "teamspeak", "tel", "teliaeid" + , "telnet", "tftp", "things", "thismessage", "tip", "tn3270", "tool", "turn" + , "turns", "tv", "udp", "unreal", "urn", "ut2004", "v-event", "vemmi" + , "ventrilo", "videotex", "vnc", "view-source", "wais", "webcal", "wpid" + , "ws", "wss", "wtai", "wyciwyg", "xcon", "xcon-userid", "xfire" + , "xmlrpc.beep", "xmlrpc.beeps", "xmpp", "xri", "ymsgr", "z39.50", "z39.50r" + , "z39.50s" + -- Inofficial schemes + , "doi", "isbn", "javascript", "pmid" + ] + +-- | Check if the string is a valid URL with a IANA or frequently used but +-- unofficial scheme (see @schemes@). +isURI :: String -> Bool +isURI = maybe False hasKnownScheme . parseURI + where + hasKnownScheme = (`Set.member` schemes) . filter (/= ':') . uriScheme + --- --- Squash blocks into inlines --- diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index eef16d3da..2d4502153 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -33,7 +33,7 @@ import Control.Monad.State import Data.Char (ord) import Data.List (intercalate, intersperse) import Data.Maybe (catMaybes) -import Network.URI (isURI, unEscapeString) +import Network.URI (unEscapeString) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Logging import Text.Pandoc.Definition diff --git a/src/Text/Pandoc/Writers/DokuWiki.hs b/src/Text/Pandoc/Writers/DokuWiki.hs index 81987dc44..1d02a9c40 100644 --- a/src/Text/Pandoc/Writers/DokuWiki.hs +++ b/src/Text/Pandoc/Writers/DokuWiki.hs @@ -44,13 +44,12 @@ import Control.Monad.Reader (ReaderT, ask, local, runReaderT) import Control.Monad.State (StateT, evalStateT, gets, modify) import Data.Default (Default (..)) import Data.List (intercalate, intersect, isPrefixOf, transpose) -import Network.URI (isURI) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Logging import Text.Pandoc.Definition import Text.Pandoc.ImageSize import Text.Pandoc.Options (WrapOption (..), WriterOptions (writerTableOfContents, writerTemplate, writerWrapText)) -import Text.Pandoc.Shared (camelCaseToHyphenated, escapeURI, linesToPara, +import Text.Pandoc.Shared (camelCaseToHyphenated, escapeURI, isURI, linesToPara, removeFormatting, substitute, trimr) import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Writers.Shared (defField, metaToJSON) diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index 0926cc331..d450513bc 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -46,7 +46,6 @@ import Data.Char (isAscii, isControl, isSpace, toLower) import Data.Either (lefts, rights) import Data.List (intercalate, intersperse, isPrefixOf, stripPrefix) import Network.HTTP (urlEncode) -import Network.URI (isURI) import Text.XML.Light import qualified Text.XML.Light as X import qualified Text.XML.Light.Cursor as XC @@ -57,7 +56,7 @@ import Text.Pandoc.Definition import Text.Pandoc.Error import Text.Pandoc.Logging import Text.Pandoc.Options (HTMLMathMethod (..), WriterOptions (..), def) -import Text.Pandoc.Shared (capitalize, isHeaderBlock, linesToPara, +import Text.Pandoc.Shared (capitalize, isHeaderBlock, isURI, linesToPara, orderedListMarkers) -- | Data to be written at the end of the document: diff --git a/src/Text/Pandoc/Writers/Haddock.hs b/src/Text/Pandoc/Writers/Haddock.hs index 812b46c30..cbbe5bdb4 100644 --- a/src/Text/Pandoc/Writers/Haddock.hs +++ b/src/Text/Pandoc/Writers/Haddock.hs @@ -36,7 +36,6 @@ module Text.Pandoc.Writers.Haddock (writeHaddock) where import Control.Monad.State import Data.Default import Data.List (intersperse, transpose) -import Network.URI (isURI) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.Logging diff --git a/src/Text/Pandoc/Writers/ICML.hs b/src/Text/Pandoc/Writers/ICML.hs index 2f7a4889f..f36a32015 100644 --- a/src/Text/Pandoc/Writers/ICML.hs +++ b/src/Text/Pandoc/Writers/ICML.hs @@ -21,7 +21,6 @@ import Control.Monad.State import Data.List (intersperse, isInfixOf, isPrefixOf, stripPrefix) import qualified Data.Set as Set import Data.Text as Text (breakOnAll, pack) -import Network.URI (isURI) import Text.Pandoc.Class (PandocMonad, report) import qualified Text.Pandoc.Class as P import Text.Pandoc.Definition @@ -29,7 +28,7 @@ import Text.Pandoc.ImageSize import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Pretty -import Text.Pandoc.Shared (linesToPara, splitBy) +import Text.Pandoc.Shared (isURI, linesToPara, splitBy) import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Writers.Math (texMathToInlines) import Text.Pandoc.Writers.Shared diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 31c70e99d..2b3d7c878 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -43,7 +43,7 @@ import Data.List (foldl', intercalate, intersperse, isInfixOf, nub, nubBy, stripPrefix, (\\)) import Data.Maybe (catMaybes, fromMaybe, isJust) import qualified Data.Text as T -import Network.URI (isURI, unEscapeString) +import Network.URI (unEscapeString) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.Highlighting (formatLaTeXBlock, formatLaTeXInline, highlight, diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index b70716181..e858bc43f 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -49,7 +49,6 @@ import qualified Data.Text as T import qualified Data.Vector as V import Data.Yaml (Value (Array, Bool, Number, Object, String)) import Network.HTTP (urlEncode) -import Network.URI (isURI) import Text.HTML.TagSoup (Tag (..), isTagText, parseTags) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Definition diff --git a/src/Text/Pandoc/Writers/MediaWiki.hs b/src/Text/Pandoc/Writers/MediaWiki.hs index 439bbb2f9..aa5c3bc4f 100644 --- a/src/Text/Pandoc/Writers/MediaWiki.hs +++ b/src/Text/Pandoc/Writers/MediaWiki.hs @@ -34,7 +34,6 @@ import Control.Monad.Reader import Control.Monad.State import Data.List (intercalate) import qualified Data.Set as Set -import Network.URI (isURI) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Logging import Text.Pandoc.Definition diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index 5dc2ba31a..b88fc2245 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -35,7 +35,6 @@ import Control.Monad.State import Data.Char (isSpace, toLower) import Data.List (isPrefixOf, stripPrefix) import Data.Maybe (fromMaybe) -import Network.URI (isURI) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Logging diff --git a/src/Text/Pandoc/Writers/Texinfo.hs b/src/Text/Pandoc/Writers/Texinfo.hs index 9926daea1..710e1dea0 100644 --- a/src/Text/Pandoc/Writers/Texinfo.hs +++ b/src/Text/Pandoc/Writers/Texinfo.hs @@ -37,7 +37,7 @@ import Data.Char (chr, ord) import Data.List (maximumBy, transpose) import Data.Ord (comparing) import qualified Data.Set as Set -import Network.URI (isURI, unEscapeString) +import Network.URI (unEscapeString) import System.FilePath import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Definition diff --git a/src/Text/Pandoc/Writers/ZimWiki.hs b/src/Text/Pandoc/Writers/ZimWiki.hs index bc2cf8f3c..4ab8bde30 100644 --- a/src/Text/Pandoc/Writers/ZimWiki.hs +++ b/src/Text/Pandoc/Writers/ZimWiki.hs @@ -38,14 +38,13 @@ import Data.Default (Default (..)) import Data.List (intercalate, isInfixOf, isPrefixOf, transpose) import qualified Data.Map as Map import Data.Text (breakOnAll, pack) -import Network.URI (isURI) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Logging import Text.Pandoc.Definition import Text.Pandoc.ImageSize import Text.Pandoc.Options (WrapOption (..), WriterOptions (writerTableOfContents, writerTemplate, writerWrapText)) -import Text.Pandoc.Shared (escapeURI, linesToPara, removeFormatting, substitute, - trimr) +import Text.Pandoc.Shared (isURI, escapeURI, linesToPara, removeFormatting, + substitute, trimr) import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Writers.Shared (defField, metaToJSON) -- cgit v1.2.3 From 66fa38ed1c27935fc57677d9c63ac9263958e3fd Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 23 May 2017 09:49:56 +0200 Subject: Shared.isURI: allow uppercase versions of known schemes. --- src/Text/Pandoc/Shared.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 7a1e6f3e3..a6c6fb95f 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -839,7 +839,8 @@ schemes = Set.fromList isURI :: String -> Bool isURI = maybe False hasKnownScheme . parseURI where - hasKnownScheme = (`Set.member` schemes) . filter (/= ':') . uriScheme + hasKnownScheme = (`Set.member` schemes) . map toLower . + filter (/= ':') . uriScheme --- --- Squash blocks into inlines -- cgit v1.2.3 From 8edeaa9349474e3c87b9515664e597db2d32df8f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 23 May 2017 16:58:24 +0200 Subject: Fixed handling of soft hyphen (0173) in docx writer. Closes #3691. --- src/Text/Pandoc/Writers/Docx.hs | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 2282a5c58..a10840033 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -1059,13 +1059,24 @@ withParaPropM :: PandocMonad m => WS m Element -> WS m a -> WS m a withParaPropM = (. flip withParaProp) . (>>=) formattedString :: PandocMonad m => String -> WS m [Element] -formattedString str = do - props <- getTextProps +formattedString str = + -- properly handle soft hyphens + case splitBy (=='\173') str of + [w] -> formattedString' w + ws -> do + sh <- formattedRun [mknode "w:softHyphen" [] ()] + (intercalate sh) <$> mapM formattedString' ws + +formattedString' :: PandocMonad m => String -> WS m [Element] +formattedString' str = do inDel <- asks envInDel - return [ mknode "w:r" [] $ - props ++ - [ mknode (if inDel then "w:delText" else "w:t") - [("xml:space","preserve")] (stripInvalidChars str) ] ] + formattedRun [ mknode (if inDel then "w:delText" else "w:t") + [("xml:space","preserve")] (stripInvalidChars str) ] + +formattedRun :: PandocMonad m => [Element] -> WS m [Element] +formattedRun els = do + props <- getTextProps + return [ mknode "w:r" [] $ props ++ els ] setFirstPara :: PandocMonad m => WS m () setFirstPara = modify $ \s -> s { stFirstPara = True } @@ -1075,7 +1086,8 @@ inlineToOpenXML :: PandocMonad m => WriterOptions -> Inline -> WS m [Element] inlineToOpenXML opts il = withDirection $ inlineToOpenXML' opts il inlineToOpenXML' :: PandocMonad m => WriterOptions -> Inline -> WS m [Element] -inlineToOpenXML' _ (Str str) = formattedString str +inlineToOpenXML' _ (Str str) = + formattedString str inlineToOpenXML' opts Space = inlineToOpenXML opts (Str " ") inlineToOpenXML' opts SoftBreak = inlineToOpenXML opts (Str " ") inlineToOpenXML' opts (Span (ident,classes,kvs) ils) = do -- cgit v1.2.3 From c0c54b79063379dc8534a88b4a9cccbe7c9a3b80 Mon Sep 17 00:00:00 2001 From: keiichiro shikano <k16.shikano@gmail.com> Date: Wed, 24 May 2017 03:53:04 +0900 Subject: RST Reader: parse list table directive (#3688) Closes #3432. --- src/Text/Pandoc/Readers/RST.hs | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index ac1f4f834..c835ecf52 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -58,7 +58,6 @@ import Text.Printf (printf) -- [ ] .. parsed-literal -- [ ] :widths: attribute in .. table -- [ ] .. csv-table --- [ ] .. list-table -- | Parse reStructuredText string and return Pandoc document. readRST :: PandocMonad m @@ -676,6 +675,7 @@ directive' = do (lengthToDim . filter (not . isSpace)) case label of "table" -> tableDirective top fields body' + "list-table" -> listTableDirective top fields body' "line-block" -> lineBlockDirective body' "raw" -> return $ B.rawBlock (trim top) (stripTrailingNewlines body) "role" -> addNewRole top $ map (\(k,v) -> (k, trim v)) fields @@ -762,6 +762,33 @@ tableDirective top _fields body = do aligns' widths' header' rows' _ -> return mempty + +-- TODO: :stub-columns:. +-- Only the first row becomes the header even if header-rows: > 1, since Pandoc doesn't support a table with multiple header rows. +-- We don't need to parse :align: as it represents the whole table align. +listTableDirective :: PandocMonad m => String -> [(String, String)] -> String -> RSTParser m Blocks +listTableDirective top fields body = do + bs <- parseFromString parseBlocks body + title <- parseFromString (trimInlines . mconcat <$> many inline) top + let rows = takeRows $ B.toList bs + headerRowsNum = fromMaybe (0 :: Int) $ lookup "header-rows" fields >>= safeRead + (headerRow,bodyRows,numOfCols) = case rows of + x:xs -> if headerRowsNum > 0 then (x, xs, length x) else ([], rows, length x) + _ -> ([],[],0) + widths = case trim <$> lookup "widths" fields of + Just "auto" -> replicate numOfCols 0 + Just specs -> normWidths $ map (fromMaybe (0 :: Double) . safeRead) $ splitBy (`elem` (" ," :: String)) specs + _ -> replicate numOfCols 0 + return $ B.table title + (zip (replicate numOfCols AlignDefault) widths) + headerRow + bodyRows + where takeRows [BulletList rows] = map takeCells rows + takeRows _ = [] + takeCells [BulletList cells] = map B.fromList cells + takeCells _ = [] + normWidths ws = map (/ max 1 (sum ws)) ws + -- TODO: -- - Only supports :format: fields with a single format for :raw: roles, -- change Text.Pandoc.Definition.Format to fix -- cgit v1.2.3 From 5844af67b41606d6da15e14c9b7dd6cedb17321e Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 23 May 2017 21:00:51 +0200 Subject: RST reader: reformatting (code line length). --- src/Text/Pandoc/Readers/RST.hs | 70 ++++++++++++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 23 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index c835ecf52..e3780f89b 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -35,8 +35,8 @@ import Control.Monad (guard, liftM, mzero, when) import Control.Monad.Identity (Identity(..)) import Control.Monad.Except (throwError) import Data.Char (isHexDigit, isSpace, toLower, toUpper) -import Data.List (deleteFirstsBy, findIndex, intercalate, isInfixOf, isSuffixOf, - nub, sort, transpose, union) +import Data.List (deleteFirstsBy, findIndex, intercalate, isInfixOf, + isSuffixOf, nub, sort, transpose, union) import qualified Data.Map as M import Data.Maybe (fromMaybe, isJust) import Data.Monoid ((<>)) @@ -131,7 +131,10 @@ metaFromDefList ds meta = adjustAuthors $ foldr f meta ds adjustAuthors (Meta metamap) = Meta $ M.adjust splitAuthors "author" $ M.adjust toPlain "date" $ M.adjust toPlain "title" - $ M.mapKeys (\k -> if k == "authors" then "author" else k) + $ M.mapKeys (\k -> + if k == "authors" + then "author" + else k) $ metamap toPlain (MetaBlocks [Para xs]) = MetaInlines xs toPlain x = x @@ -595,10 +598,14 @@ listItem start = try $ do parsed <- parseFromString parseBlocks $ concat (first:rest) ++ "\n" updateState (\st -> st {stateParserContext = oldContext}) return $ case B.toList parsed of - [Para xs] -> B.singleton $ Plain xs - [Para xs, BulletList ys] -> B.fromList [Plain xs, BulletList ys] - [Para xs, OrderedList s ys] -> B.fromList [Plain xs, OrderedList s ys] - [Para xs, DefinitionList ys] -> B.fromList [Plain xs, DefinitionList ys] + [Para xs] -> + B.singleton $ Plain xs + [Para xs, BulletList ys] -> + B.fromList [Plain xs, BulletList ys] + [Para xs, OrderedList s ys] -> + B.fromList [Plain xs, OrderedList s ys] + [Para xs, DefinitionList ys] -> + B.fromList [Plain xs, DefinitionList ys] _ -> parsed orderedList :: PandocMonad m => RSTParser m Blocks @@ -726,7 +733,8 @@ directive' = do "figure" -> do (caption, legend) <- parseFromString extractCaption body' let src = escapeURI $ trim top - return $ B.para (B.imageWith (imgAttr "figclass") src "fig:" caption) <> legend + return $ B.para (B.imageWith (imgAttr "figclass") src "fig:" + caption) <> legend "image" -> do let src = escapeURI $ trim top let alt = B.str $ maybe "image" trim $ lookup "alt" fields @@ -737,7 +745,8 @@ directive' = do $ B.imageWith attr src "" alt Nothing -> B.imageWith attr src "" alt "class" -> do - let attrs = ("", (splitBy isSpace $ trim top), map (\(k,v) -> (k, trimr v)) fields) + let attrs = ("", (splitBy isSpace $ trim top), + map (\(k,v) -> (k, trimr v)) fields) -- directive content or the first immediately following element children <- case body of "" -> block @@ -764,20 +773,27 @@ tableDirective top _fields body = do -- TODO: :stub-columns:. --- Only the first row becomes the header even if header-rows: > 1, since Pandoc doesn't support a table with multiple header rows. --- We don't need to parse :align: as it represents the whole table align. -listTableDirective :: PandocMonad m => String -> [(String, String)] -> String -> RSTParser m Blocks +-- Only the first row becomes the header even if header-rows: > 1, +-- since Pandoc doesn't support a table with multiple header rows. +-- We don't need to parse :align: as it represents the whole table align. +listTableDirective :: PandocMonad m + => String -> [(String, String)] -> String + -> RSTParser m Blocks listTableDirective top fields body = do bs <- parseFromString parseBlocks body title <- parseFromString (trimInlines . mconcat <$> many inline) top let rows = takeRows $ B.toList bs - headerRowsNum = fromMaybe (0 :: Int) $ lookup "header-rows" fields >>= safeRead + headerRowsNum = fromMaybe (0 :: Int) $ + lookup "header-rows" fields >>= safeRead (headerRow,bodyRows,numOfCols) = case rows of - x:xs -> if headerRowsNum > 0 then (x, xs, length x) else ([], rows, length x) + x:xs -> if headerRowsNum > 0 + then (x, xs, length x) + else ([], rows, length x) _ -> ([],[],0) widths = case trim <$> lookup "widths" fields of Just "auto" -> replicate numOfCols 0 - Just specs -> normWidths $ map (fromMaybe (0 :: Double) . safeRead) $ splitBy (`elem` (" ," :: String)) specs + Just specs -> normWidths $ map (fromMaybe (0 :: Double) . safeRead) $ + splitBy (`elem` (" ," :: String)) specs _ -> replicate numOfCols 0 return $ B.table title (zip (replicate numOfCols AlignDefault) widths) @@ -792,7 +808,8 @@ listTableDirective top fields body = do -- TODO: -- - Only supports :format: fields with a single format for :raw: roles, -- change Text.Pandoc.Definition.Format to fix -addNewRole :: PandocMonad m => String -> [(String, String)] -> RSTParser m Blocks +addNewRole :: PandocMonad m + => String -> [(String, String)] -> RSTParser m Blocks addNewRole roleString fields = do pos <- getPosition (role, parentRole) <- parseFromString inheritedRole roleString @@ -822,7 +839,8 @@ addNewRole roleString fields = do SkippedContent ":format: [because parent of role is not :raw:]" pos _ -> logMessage $ SkippedContent (":" ++ key ++ ":") pos when (parentRole == "raw" && countKeys "format" > 1) $ - logMessage $ SkippedContent ":format: [after first in definition of role]" + logMessage $ SkippedContent + ":format: [after first in definition of role]" pos when (parentRole == "code" && countKeys "language" > 1) $ logMessage $ SkippedContent @@ -837,7 +855,8 @@ addNewRole roleString fields = do where countKeys k = length . filter (== k) . map fst $ fields inheritedRole = - (,) <$> roleName <*> ((char '(' *> roleName <* char ')') <|> pure "span") + (,) <$> roleName <*> ((char '(' *> roleName <* char ')') + <|> pure "span") -- Can contain character codes as decimal numbers or @@ -1014,7 +1033,8 @@ substKey = try $ do [Para ils] -> return $ B.fromList ils _ -> mzero let key = toKey $ stripFirstAndLast ref - updateState $ \s -> s{ stateSubstitutions = M.insert key il $ stateSubstitutions s } + updateState $ \s -> s{ stateSubstitutions = + M.insert key il $ stateSubstitutions s } anonymousKey :: Monad m => RSTParser m () anonymousKey = try $ do @@ -1023,7 +1043,8 @@ anonymousKey = try $ do pos <- getPosition let key = toKey $ "_" ++ printf "%09d" (sourceLine pos) --TODO: parse width, height, class and name attributes - updateState $ \s -> s { stateKeys = M.insert key ((src,""), nullAttr) $ stateKeys s } + updateState $ \s -> s { stateKeys = M.insert key ((src,""), nullAttr) $ + stateKeys s } stripTicks :: String -> String stripTicks = reverse . stripTick . reverse . stripTick @@ -1038,7 +1059,8 @@ regularKey = try $ do src <- targetURI let key = toKey $ stripTicks ref --TODO: parse width, height, class and name attributes - updateState $ \s -> s { stateKeys = M.insert key ((src,""), nullAttr) $ stateKeys s } + updateState $ \s -> s { stateKeys = M.insert key ((src,""), nullAttr) $ + stateKeys s } headerBlock :: PandocMonad m => RSTParser m [Char] headerBlock = do @@ -1243,7 +1265,8 @@ interpretedRole = try $ do (role, contents) <- roleBefore <|> roleAfter renderRole contents Nothing role nullAttr -renderRole :: PandocMonad m => String -> Maybe String -> String -> Attr -> RSTParser m 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 @@ -1376,7 +1399,8 @@ referenceLink = try $ do (k:_) -> return k ((src,tit), attr) <- lookupKey [] key -- if anonymous link, remove key so it won't be used again - when (isAnonKey key) $ updateState $ \s -> s{ stateKeys = M.delete key keyTable } + when (isAnonKey key) $ updateState $ \s -> + s{ stateKeys = M.delete key keyTable } return $ B.linkWith attr src tit label' -- We keep a list of oldkeys so we can detect lookup loops. -- cgit v1.2.3 From 29a4bdc68131d3925a55e0428b35c6a4f75f86e0 Mon Sep 17 00:00:00 2001 From: Marc Schreiber <schrieveslaach@online.de> Date: Tue, 23 May 2017 17:31:42 -0300 Subject: Add suggestions of @jgm: parse bracketed stuff as inlines --- src/Text/Pandoc/Readers/LaTeX.hs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index a34be46e2..6b44df468 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -418,9 +418,15 @@ blockCommands = M.fromList $ blockTextcolor :: PandocMonad m => LP m Blocks blockTextcolor = do - skipopts - color <- braced - divWith ("",[],[("style","color: " ++ color)]) <$> grouped block <* notFollowedBy inline + skipopts + color <- braced + let constructor = divWith ("",[],[("style","color: " ++ color)]) + inlineContents <|> constructor <$> blockContents + where inlineContents = do + ils <- grouped inline + rest <- inlines + return (para (ils <> rest)) + blockContents = grouped block graphicsPath :: PandocMonad m => LP m Blocks graphicsPath = do -- cgit v1.2.3 From 7174776c19476701933df83ce4f2689a967a1a0a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 24 May 2017 12:11:12 +0200 Subject: HTML reader: Add `details` tag to list of block tags. Closes #3694. --- src/Text/Pandoc/Readers/HTML.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 650454ae6..23af6171e 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -887,7 +887,8 @@ inlineHtmlTags = ["a", "abbr", "acronym", "b", "basefont", "bdo", "big", blockHtmlTags :: [String] blockHtmlTags = ["?xml", "!DOCTYPE", "address", "article", "aside", "blockquote", "body", "button", "canvas", - "caption", "center", "col", "colgroup", "dd", "dir", "div", + "caption", "center", "col", "colgroup", "dd", "details", + "dir", "div", "dl", "dt", "fieldset", "figcaption", "figure", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html", -- cgit v1.2.3 From 19d3a2bbe5291dcba0bdba9f6faf0103f5f47245 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 24 May 2017 21:46:43 +0200 Subject: Logging: Made SkippedContent WARNING not INFO. --- src/Text/Pandoc/Logging.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs index bf7f33d29..70384f936 100644 --- a/src/Text/Pandoc/Logging.hs +++ b/src/Text/Pandoc/Logging.hs @@ -252,7 +252,7 @@ showLogMessage msg = messageVerbosity:: LogMessage -> Verbosity messageVerbosity msg = case msg of - SkippedContent{} -> INFO + SkippedContent{} -> WARNING CouldNotParseYamlMetadata{} -> WARNING DuplicateLinkReference{} -> WARNING DuplicateNoteReference{} -> WARNING -- cgit v1.2.3 From 1288a50380e01ac50818033a16cc9146f373bdde Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 24 May 2017 21:46:53 +0200 Subject: LaTeX reader: parse tikzpicture as raw verbatim environment... if `raw_tex` extension is selected. Otherwise skip with a warning. This is better than trying to parse it as text! Closes #3692. --- src/Text/Pandoc/Readers/LaTeX.hs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 84758d309..af7c1d9b7 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1031,6 +1031,19 @@ rawEnv name = do report $ SkippedContent ("\\end{" ++ name ++ "}") pos2 return bs +rawVerbEnv :: PandocMonad m => String -> LP m Blocks +rawVerbEnv name = do + pos <- getPosition + (_, raw) <- withRaw $ verbEnv name + let raw' = "\\begin{tikzpicture}" ++ raw + exts <- getOption readerExtensions + let parseRaw = extensionEnabled Ext_raw_tex exts + if parseRaw + then return $ rawBlock "latex" raw' + else do + report $ SkippedContent raw' pos + return mempty + ---- maybeAddExtension :: String -> FilePath -> FilePath @@ -1200,6 +1213,7 @@ environments = M.fromList , ("align*", mathEnvWith para (Just "aligned") "align*") , ("alignat", mathEnvWith para (Just "aligned") "alignat") , ("alignat*", mathEnvWith para (Just "aligned") "alignat*") + , ("tikzpicture", rawVerbEnv "tikzpicture") ] figure :: PandocMonad m => LP m Blocks -- cgit v1.2.3 From bc6aac7b474495c4433c31bcd4a3570057edb850 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 24 May 2017 22:41:47 +0200 Subject: Parsing: Provide parseFromString'. This is a verison of parseFromString specialied to ParserState, which resets stateLastStrPos at the end. This is almost always what we want. This fixes a bug where `_hi_` wasn't treated as emphasis in the following, because pandoc got confused about the position of the last word: - [o] _hi_ Closes #3690. --- src/Text/Pandoc/Parsing.hs | 18 ++++++++++++- src/Text/Pandoc/Readers/LaTeX.hs | 12 ++++----- src/Text/Pandoc/Readers/Markdown.hs | 40 +++++++++++++++-------------- src/Text/Pandoc/Readers/RST.hs | 50 ++++++++++++++++++------------------- src/Text/Pandoc/Readers/TWiki.hs | 8 +++--- src/Text/Pandoc/Readers/Textile.hs | 8 +++--- src/Text/Pandoc/Readers/Txt2Tags.hs | 6 ++--- 7 files changed, 80 insertions(+), 62 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index c6be48d19..e6157dde3 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -50,6 +50,7 @@ module Text.Pandoc.Parsing ( anyLine, enclosed, stringAnyCase, parseFromString, + parseFromString', lineClump, charsInBalanced, romanNumeral, @@ -358,7 +359,10 @@ stringAnyCase (x:xs) = do return (firstChar:rest) -- | Parse contents of 'str' using 'parser' and return result. -parseFromString :: Monad m => ParserT String st m a -> String -> ParserT String st m a +parseFromString :: Monad m + => ParserT String st m a + -> String + -> ParserT String st m a parseFromString parser str = do oldPos <- getPosition oldInput <- getInput @@ -370,6 +374,18 @@ parseFromString parser str = do setPosition oldPos return result +-- | Like 'parseFromString' but specialized for 'ParserState'. +-- This resets 'stateLastStrPos', which is almost always what we want. +parseFromString' :: Monad m + => ParserT String ParserState m a + -> String + -> ParserT String ParserState m a +parseFromString' parser str = do + oldStrPos <- stateLastStrPos <$> getState + res <- parseFromString parser str + updateState $ \st -> st{ stateLastStrPos = oldStrPos } + return res + -- | Parse raw line block up to and including blank lines. lineClump :: Stream [Char] m Char => ParserT [Char] st m String lineClump = blanklines diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index af7c1d9b7..88be40e3e 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -304,8 +304,8 @@ blockCommand = try $ do rawcommand <- getRawCommand name' transformed <- applyMacros' rawcommand guard $ transformed /= rawcommand - notFollowedBy $ parseFromString inlines transformed - parseFromString blocks transformed + notFollowedBy $ parseFromString' inlines transformed + parseFromString' blocks transformed lookupListDefault raw [name',name] blockCommands inBrackets :: Inlines -> Inlines @@ -475,7 +475,7 @@ inlineCommand = try $ do transformed <- applyMacros' rawcommand exts <- getOption readerExtensions if transformed /= rawcommand - then parseFromString inlines transformed + then parseFromString' inlines transformed else if extensionEnabled Ext_raw_tex exts then return $ rawInline "latex" rawcommand else ignore rawcommand @@ -1021,7 +1021,7 @@ rawEnv name = do (bs, raw) <- withRaw $ env name blocks raw' <- applyMacros' $ beginCommand ++ raw if raw' /= beginCommand ++ raw - then parseFromString blocks raw' + then parseFromString' blocks raw' else if parseRaw then return $ rawBlock "latex" $ beginCommand ++ raw' else do @@ -1119,7 +1119,7 @@ keyvals :: PandocMonad m => LP m [(String, String)] keyvals = try $ char '[' *> manyTill keyval (char ']') alltt :: PandocMonad m => String -> LP m Blocks -alltt t = walk strToCode <$> parseFromString blocks +alltt t = walk strToCode <$> parseFromString' blocks (substitute " " "\\ " $ substitute "%" "\\%" $ intercalate "\\\\\n" $ lines t) where strToCode (Str s) = Code nullAttr s @@ -1503,7 +1503,7 @@ parseTableRow cols prefixes suffixes = try $ do guard $ length rawcells == cols let rawcells' = zipWith3 (\c p s -> p ++ trim c ++ s) rawcells prefixes suffixes - cells' <- mapM (parseFromString tableCell) rawcells' + cells' <- mapM (parseFromString' tableCell) rawcells' let numcells = length cells' guard $ numcells <= cols && numcells >= 1 guard $ cells' /= [mempty] diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index af7588562..17a7184c0 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -155,9 +155,11 @@ litChar = escapedChar' inlinesInBalancedBrackets :: PandocMonad m => MarkdownParser m (F Inlines) inlinesInBalancedBrackets = do char '[' + pos <- getPosition (_, raw) <- withRaw $ charsInBalancedBrackets 1 guard $ not $ null raw - parseFromString (trimInlinesF . mconcat <$> many inline) (init raw) + parseFromString' (setPosition pos >> + trimInlinesF . mconcat <$> many inline) (init raw) charsInBalancedBrackets :: PandocMonad m => Int -> MarkdownParser m () charsInBalancedBrackets 0 = return () @@ -189,7 +191,7 @@ rawTitleBlockLine = do titleLine :: PandocMonad m => MarkdownParser m (F Inlines) titleLine = try $ do raw <- rawTitleBlockLine - res <- parseFromString (many inline) raw + res <- parseFromString' (many inline) raw return $ trimInlinesF $ mconcat res authorsLine :: PandocMonad m => MarkdownParser m (F [Inlines]) @@ -200,12 +202,12 @@ authorsLine = try $ do (trimInlinesF . mconcat <$> many (try $ notFollowedBy sep >> inline)) sep - sequence <$> parseFromString pAuthors raw + sequence <$> parseFromString' pAuthors raw dateLine :: PandocMonad m => MarkdownParser m (F Inlines) dateLine = try $ do raw <- rawTitleBlockLine - res <- parseFromString (many inline) raw + res <- parseFromString' (many inline) raw return $ trimInlinesF $ mconcat res titleBlock :: PandocMonad m => MarkdownParser m () @@ -290,7 +292,7 @@ ignorable t = (T.pack "_") `T.isSuffixOf` t toMetaValue :: PandocMonad m => Text -> MarkdownParser m (F MetaValue) -toMetaValue x = toMeta <$> parseFromString parseBlocks (T.unpack x) +toMetaValue x = toMeta <$> parseFromString' parseBlocks (T.unpack x) where toMeta p = do p' <- p @@ -466,7 +468,7 @@ noteBlock = try $ do rest <- many $ try $ blanklines >> indentSpaces >> rawLines let raw = unlines (first:rest) ++ "\n" optional blanklines - parsed <- parseFromString parseBlocks raw + parsed <- parseFromString' parseBlocks raw let newnote = (ref, parsed) oldnotes <- stateNotes' <$> getState case lookup ref oldnotes of @@ -774,7 +776,7 @@ blockQuote :: PandocMonad m => MarkdownParser m (F Blocks) blockQuote = do raw <- emailBlockQuote -- parse the extracted block, which may contain various block elements: - contents <- parseFromString parseBlocks $ (intercalate "\n" raw) ++ "\n\n" + contents <- parseFromString' parseBlocks $ (intercalate "\n" raw) ++ "\n\n" return $ B.blockQuote <$> contents -- @@ -887,7 +889,7 @@ listItem start = try $ do setState $ state {stateParserContext = ListItemState} -- parse the extracted block, which may contain various block elements: let raw = concat (first:continuations) - contents <- parseFromString parseBlocks raw + contents <- parseFromString' parseBlocks raw updateState (\st -> st {stateParserContext = oldContext}) return contents @@ -934,8 +936,8 @@ definitionListItem :: PandocMonad m => Bool -> MarkdownParser m (F (Inlines, [Bl definitionListItem compact = try $ do rawLine' <- anyLine raw <- many1 $ defRawBlock compact - term <- parseFromString (trimInlinesF . mconcat <$> many inline) rawLine' - contents <- mapM (parseFromString parseBlocks . (++"\n")) raw + term <- parseFromString' (trimInlinesF . mconcat <$> many inline) rawLine' + contents <- mapM (parseFromString' parseBlocks . (++"\n")) raw optional blanklines return $ liftM2 (,) term (sequence contents) @@ -1127,7 +1129,7 @@ lineBlock :: PandocMonad m => MarkdownParser m (F Blocks) lineBlock = try $ do guardEnabled Ext_line_blocks lines' <- lineBlockLines >>= - mapM (parseFromString (trimInlinesF . mconcat <$> many inline)) + mapM (parseFromString' (trimInlinesF . mconcat <$> many inline)) return $ B.lineBlock <$> sequence lines' -- @@ -1170,7 +1172,7 @@ simpleTableHeader headless = try $ do then replicate (length dashes) "" else rawHeads heads <- fmap sequence - $ mapM (parseFromString (mconcat <$> many plain)) + $ mapM (parseFromString' (mconcat <$> many plain)) $ map trim rawHeads' return (heads, aligns, indices) @@ -1216,7 +1218,7 @@ tableLine :: PandocMonad m => [Int] -> MarkdownParser m (F [Blocks]) tableLine indices = rawTableLine indices >>= - fmap sequence . mapM (parseFromString (mconcat <$> many plain)) + fmap sequence . mapM (parseFromString' (mconcat <$> many plain)) -- Parse a multiline table row and return a list of blocks (columns). multilineRow :: PandocMonad m @@ -1225,7 +1227,7 @@ multilineRow :: PandocMonad m multilineRow indices = do colLines <- many1 (rawTableLine indices) let cols = map unlines $ transpose colLines - fmap sequence $ mapM (parseFromString (mconcat <$> many plain)) cols + fmap sequence $ mapM (parseFromString' (mconcat <$> many plain)) cols -- Parses a table caption: inlines beginning with 'Table:' -- and followed by blank lines. @@ -1283,7 +1285,7 @@ multilineTableHeader headless = try $ do then replicate (length dashes) "" else map (unlines . map trim) rawHeadsList heads <- fmap sequence $ - mapM (parseFromString (mconcat <$> many plain)) $ + mapM (parseFromString' (mconcat <$> many plain)) $ map trim rawHeads return (heads, aligns, indices) @@ -1340,7 +1342,7 @@ pipeTableRow = try $ do let chunk = void (code <|> math <|> rawHtmlInline <|> escapedChar <|> rawLaTeXInline') <|> void (noneOf "|\n\r") let cellContents = ((trim . snd) <$> withRaw (many chunk)) >>= - parseFromString pipeTableCell + parseFromString' pipeTableCell cells <- cellContents `sepEndBy1` (char '|') -- surrounding pipes needed for a one-column table: guard $ not (length cells == 1 && not openPipe) @@ -1747,8 +1749,8 @@ referenceLink constructor (lab, raw) = do when (raw' == "") $ guardEnabled Ext_shortcut_reference_links let labIsRef = raw' == "" || raw' == "[]" let key = toKey $ if labIsRef then raw else raw' - parsedRaw <- parseFromString (mconcat <$> many inline) raw' - fallback <- parseFromString (mconcat <$> many inline) $ dropBrackets raw + parsedRaw <- parseFromString' (mconcat <$> many inline) raw' + fallback <- parseFromString' (mconcat <$> many inline) $ dropBrackets raw implicitHeaderRefs <- option False $ True <$ guardEnabled Ext_implicit_header_references let makeFallback = do @@ -1954,7 +1956,7 @@ textualCite = try $ do let (spaces',raw') = span isSpace raw spc | null spaces' = mempty | otherwise = B.space - lab <- parseFromString (mconcat <$> many inline) $ dropBrackets raw' + lab <- parseFromString' (mconcat <$> many inline) $ dropBrackets raw' fallback <- referenceLink B.linkWith (lab,raw') return $ do fallback' <- fallback diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index e3780f89b..1ea142112 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -196,7 +196,7 @@ parseRST = do parseCitation :: PandocMonad m => (String, String) -> RSTParser m (Inlines, [Blocks]) parseCitation (ref, raw) = do - contents <- parseFromString parseBlocks raw + contents <- parseFromString' parseBlocks raw return $ (B.spanWith (ref, ["citation-label"], []) (B.str ref), [contents]) @@ -246,7 +246,7 @@ fieldListItem :: PandocMonad m => Int -> RSTParser m (Inlines, [Blocks]) fieldListItem minIndent = try $ do (name, raw) <- rawFieldListItem minIndent term <- parseInlineFromString name - contents <- parseFromString parseBlocks raw + contents <- parseFromString' parseBlocks raw optional blanklines return (term, [contents]) @@ -445,7 +445,7 @@ blockQuote :: PandocMonad m => RSTParser m Blocks blockQuote = do raw <- indentedBlock -- parse the extracted block, which may contain various block elements: - contents <- parseFromString parseBlocks $ raw ++ "\n\n" + contents <- parseFromString' parseBlocks $ raw ++ "\n\n" return $ B.blockQuote contents {- @@ -533,7 +533,7 @@ definitionListItem = try $ do term <- trimInlines . mconcat <$> many1Till inline endline raw <- indentedBlock -- parse the extracted block, which may contain various block elements: - contents <- parseFromString parseBlocks $ raw ++ "\n" + contents <- parseFromString' parseBlocks $ raw ++ "\n" return (term, [contents]) definitionList :: PandocMonad m => RSTParser m Blocks @@ -595,7 +595,7 @@ listItem start = try $ do let oldContext = stateParserContext state setState $ state {stateParserContext = ListItemState} -- parse the extracted block, which may itself contain block elements - parsed <- parseFromString parseBlocks $ concat (first:rest) ++ "\n" + parsed <- parseFromString' parseBlocks $ concat (first:rest) ++ "\n" updateState (\st -> st {stateParserContext = oldContext}) return $ case B.toList parsed of [Para xs] -> @@ -686,19 +686,19 @@ directive' = do "line-block" -> lineBlockDirective body' "raw" -> return $ B.rawBlock (trim top) (stripTrailingNewlines body) "role" -> addNewRole top $ map (\(k,v) -> (k, trim v)) fields - "container" -> parseFromString parseBlocks body' + "container" -> parseFromString' parseBlocks body' "replace" -> B.para <$> -- consumed by substKey parseInlineFromString (trim top) "unicode" -> B.para <$> -- consumed by substKey parseInlineFromString (trim $ unicodeTransform top) - "compound" -> parseFromString parseBlocks body' - "pull-quote" -> B.blockQuote <$> parseFromString parseBlocks body' - "epigraph" -> B.blockQuote <$> parseFromString parseBlocks body' - "highlights" -> B.blockQuote <$> parseFromString parseBlocks body' + "compound" -> parseFromString' parseBlocks body' + "pull-quote" -> B.blockQuote <$> parseFromString' parseBlocks body' + "epigraph" -> B.blockQuote <$> parseFromString' parseBlocks body' + "highlights" -> B.blockQuote <$> parseFromString' parseBlocks body' "rubric" -> B.para . B.strong <$> parseInlineFromString top _ | label `elem` ["attention","caution","danger","error","hint", "important","note","tip","warning","admonition"] -> - do bod <- parseFromString parseBlocks $ top ++ "\n\n" ++ body' + do bod <- parseFromString' parseBlocks $ top ++ "\n\n" ++ body' let lab = case label of "admonition" -> mempty (l:ls) -> B.divWith ("",["admonition-title"],[]) @@ -711,11 +711,11 @@ directive' = do (trim top ++ if null subtit then "" else (": " ++ subtit)) - bod <- parseFromString parseBlocks body' + bod <- parseFromString' parseBlocks body' return $ B.divWith ("",["sidebar"],[]) $ tit <> bod "topic" -> do tit <- B.para . B.strong <$> parseInlineFromString top - bod <- parseFromString parseBlocks body' + bod <- parseFromString' parseBlocks body' return $ B.divWith ("",["topic"],[]) $ tit <> bod "default-role" -> mempty <$ updateState (\s -> s { stateRstDefaultRole = @@ -731,7 +731,7 @@ directive' = do "math" -> return $ B.para $ mconcat $ map B.displayMath $ toChunks $ top ++ "\n\n" ++ body "figure" -> do - (caption, legend) <- parseFromString extractCaption body' + (caption, legend) <- parseFromString' extractCaption body' let src = escapeURI $ trim top return $ B.para (B.imageWith (imgAttr "figclass") src "fig:" caption) <> legend @@ -750,21 +750,21 @@ directive' = do -- directive content or the first immediately following element children <- case body of "" -> block - _ -> parseFromString parseBlocks body' + _ -> parseFromString' parseBlocks body' return $ B.divWith attrs children other -> do pos <- getPosition logMessage $ SkippedContent (".. " ++ other) pos - bod <- parseFromString parseBlocks $ top ++ "\n\n" ++ body' + bod <- parseFromString' parseBlocks $ top ++ "\n\n" ++ body' return $ B.divWith ("",[other],[]) bod tableDirective :: PandocMonad m => String -> [(String, String)] -> String -> RSTParser m Blocks tableDirective top _fields body = do - bs <- parseFromString parseBlocks body + bs <- parseFromString' parseBlocks body case B.toList bs of [Table _ aligns' widths' header' rows'] -> do - title <- parseFromString (trimInlines . mconcat <$> many inline) top + title <- parseFromString' (trimInlines . mconcat <$> many inline) top -- TODO widths -- align is not applicable since we can't represent whole table align return $ B.singleton $ Table (B.toList title) @@ -780,8 +780,8 @@ listTableDirective :: PandocMonad m => String -> [(String, String)] -> String -> RSTParser m Blocks listTableDirective top fields body = do - bs <- parseFromString parseBlocks body - title <- parseFromString (trimInlines . mconcat <$> many inline) top + bs <- parseFromString' parseBlocks body + title <- parseFromString' (trimInlines . mconcat <$> many inline) top let rows = takeRows $ B.toList bs headerRowsNum = fromMaybe (0 :: Int) $ lookup "header-rows" fields >>= safeRead @@ -812,7 +812,7 @@ addNewRole :: PandocMonad m => String -> [(String, String)] -> RSTParser m Blocks addNewRole roleString fields = do pos <- getPosition - (role, parentRole) <- parseFromString inheritedRole roleString + (role, parentRole) <- parseFromString' inheritedRole roleString customRoles <- stateRstCustomRoles <$> getState let getBaseRole (r, f, a) roles = case M.lookup r roles of @@ -1127,7 +1127,7 @@ simpleTableRow indices = do let cols = map unlines . transpose $ firstLine : conLines ++ [replicate (length indices) "" | not (null conLines)] - mapM (parseFromString parseBlocks) cols + mapM (parseFromString' parseBlocks) cols simpleTableSplitLine :: [Int] -> String -> [String] simpleTableSplitLine indices line = @@ -1150,7 +1150,7 @@ simpleTableHeader headless = try $ do let rawHeads = if headless then replicate (length dashes) "" else simpleTableSplitLine indices rawContent - heads <- mapM (parseFromString (mconcat <$> many plain)) $ + heads <- mapM (parseFromString' (mconcat <$> many plain)) $ map trim rawHeads return (heads, aligns, indices) @@ -1206,7 +1206,7 @@ inline = choice [ note -- can start with whitespace, so try before ws , symbol ] <?> "inline" parseInlineFromString :: PandocMonad m => String -> RSTParser m Inlines -parseInlineFromString = parseFromString (trimInlines . mconcat <$> many inline) +parseInlineFromString = parseFromString' (trimInlines . mconcat <$> many inline) hyphens :: Monad m => RSTParser m Inlines hyphens = do @@ -1470,7 +1470,7 @@ note = try $ do -- Note references inside other notes are allowed in reST, but -- not yet in this implementation. updateState $ \st -> st{ stateNotes = [] } - contents <- parseFromString parseBlocks raw + contents <- parseFromString' parseBlocks raw let newnotes = if (ref == "*" || ref == "#") -- auto-numbered -- delete the note so the next auto-numbered note -- doesn't get the same contents: diff --git a/src/Text/Pandoc/Readers/TWiki.hs b/src/Text/Pandoc/Readers/TWiki.hs index ecb609ae9..aea55b7a9 100644 --- a/src/Text/Pandoc/Readers/TWiki.hs +++ b/src/Text/Pandoc/Readers/TWiki.hs @@ -106,7 +106,7 @@ parseHtmlContentWithAttrs tag parser = do parsedContent <- try $ parseContent content return (attr, parsedContent) where - parseContent = parseFromString $ nested $ manyTill parser endOfContent + parseContent = parseFromString' $ nested $ manyTill parser endOfContent endOfContent = try $ skipMany blankline >> skipSpaces >> eof parseHtmlContent :: PandocMonad m => String -> TWParser m a -> TWParser m [a] @@ -233,7 +233,7 @@ listItemLine prefix marker = lineContent >>= parseContent >>= return . mconcat filterSpaces = reverse . dropWhile (== ' ') . reverse listContinuation = notFollowedBy (string prefix >> marker) >> string " " >> lineContent - parseContent = parseFromString $ many1 $ nestedList <|> parseInline + parseContent = parseFromString' $ many1 $ nestedList <|> parseInline parseInline = many1Till inline (lastNewline <|> newlineBeforeNestedList) >>= return . B.plain . mconcat nestedList = list prefix @@ -297,7 +297,7 @@ noautolink = do setState $ st{ stateAllowLinks = True } return $ mconcat blocks where - parseContent = parseFromString $ many $ block + parseContent = parseFromString' $ many $ block para :: PandocMonad m => TWParser m B.Blocks para = many1Till inline endOfParaElement >>= return . result . mconcat @@ -525,4 +525,4 @@ linkText = do return (url, "", content) where linkContent = (char '[') >> many1Till anyChar (char ']') >>= parseLinkContent - parseLinkContent = parseFromString $ many1 inline + parseLinkContent = parseFromString' $ many1 inline diff --git a/src/Text/Pandoc/Readers/Textile.hs b/src/Text/Pandoc/Readers/Textile.hs index abf8be452..52f4f2493 100644 --- a/src/Text/Pandoc/Readers/Textile.hs +++ b/src/Text/Pandoc/Readers/Textile.hs @@ -315,7 +315,7 @@ definitionListItem = try $ do optional whitespace >> newline s <- many1Till anyChar (try (string "=:" >> newline)) -- this ++ "\n\n" does not look very good - ds <- parseFromString parseBlocks (s ++ "\n\n") + ds <- parseFromString' parseBlocks (s ++ "\n\n") return [ds] -- raw content @@ -367,7 +367,7 @@ tableCell = try $ do notFollowedBy blankline raw <- trim <$> many (noneOf "|\n" <|> try (char '\n' <* notFollowedBy blankline)) - content <- mconcat <$> parseFromString (many inline) raw + content <- mconcat <$> parseFromString' (many inline) raw return ((isHeader, alignment), B.plain content) -- | A table row is made of many table cells @@ -389,7 +389,7 @@ table = try $ do _ <- attributes char '.' rawcapt <- trim <$> anyLine - parseFromString (mconcat <$> many inline) rawcapt + parseFromString' (mconcat <$> many inline) rawcapt rawrows <- many1 $ (skipMany ignorableRow) >> tableRow skipMany ignorableRow blanklines @@ -507,7 +507,7 @@ note = try $ do notes <- stateNotes <$> getState case lookup ref notes of Nothing -> fail "note not found" - Just raw -> B.note <$> parseFromString parseBlocks raw + Just raw -> B.note <$> parseFromString' parseBlocks raw -- | Special chars markupChars :: [Char] diff --git a/src/Text/Pandoc/Readers/Txt2Tags.hs b/src/Text/Pandoc/Readers/Txt2Tags.hs index 05c6c9a69..d8791869d 100644 --- a/src/Text/Pandoc/Readers/Txt2Tags.hs +++ b/src/Text/Pandoc/Readers/Txt2Tags.hs @@ -212,7 +212,7 @@ quote :: T2T Blocks quote = try $ do lookAhead tab rawQuote <- many1 (tab *> optional spaces *> anyLine) - contents <- parseFromString parseBlocks (intercalate "\n" rawQuote ++ "\n\n") + contents <- parseFromString' parseBlocks (intercalate "\n" rawQuote ++ "\n\n") return $ B.blockQuote contents commentLine :: T2T Inlines @@ -264,7 +264,7 @@ listItem start end = try $ do firstLine <- anyLineNewline blank <- option "" ("\n" <$ blankline) rest <- concat <$> many (listContinuation markerLength) - parseFromString end $ firstLine ++ blank ++ rest + parseFromString' end $ firstLine ++ blank ++ rest -- continuation of a list item - indented and separated by blankline or endline. -- Note: nested lists are parsed as continuations. @@ -439,7 +439,7 @@ inlineMarkup p f c special = try $ do Just middle -> do lastChar <- anyChar end <- many1 (char c) - let parser inp = parseFromString (mconcat <$> many p) inp + let parser inp = parseFromString' (mconcat <$> many p) inp let start' = case drop 2 start of "" -> mempty xs -> special xs -- cgit v1.2.3 From 8f718b08834e496e98790e1b5b8a3cb9e1b932a6 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 24 May 2017 23:04:49 +0200 Subject: LaTeX reader: Fixed failures on \ref{}, \label{} with `+raw_tex`. Now these commands are parsed as raw if `+raw_tex`; otherwise, their argument is parsed as a bracketed string. --- src/Text/Pandoc/Readers/LaTeX.hs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 88be40e3e..7bcd120ce 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -482,9 +482,12 @@ inlineCommand = try $ do (lookupListDefault raw [name',name] inlineCommands <* optional (try (string "{}"))) -unlessParseRaw :: PandocMonad m => LP m () -unlessParseRaw = getOption readerExtensions >>= - guard . not . extensionEnabled Ext_raw_tex +rawInlineOr :: PandocMonad m => String -> LP m Inlines -> LP m Inlines +rawInlineOr name' fallback = do + parseRaw <- extensionEnabled Ext_raw_tex <$> getOption readerExtensions + if parseRaw + then rawInline "latex" <$> getRawCommand name' + else fallback isBlockCommand :: String -> Bool isBlockCommand s = s `M.member` (blockCommands :: M.Map String (LP PandocPure Blocks)) @@ -532,11 +535,11 @@ inlineCommands = M.fromList $ , ("dots", lit "…") , ("mdots", lit "…") , ("sim", lit "~") - , ("label", unlessParseRaw >> (inBrackets <$> tok)) - , ("ref", unlessParseRaw >> (inBrackets <$> tok)) + , ("label", rawInlineOr "label" (inBrackets <$> tok)) + , ("ref", rawInlineOr "ref" (inBrackets <$> tok)) , ("textgreek", tok) , ("sep", lit ",") - , ("cref", unlessParseRaw >> (inBrackets <$> tok)) -- from cleveref.sty + , ("cref", rawInlineOr "cref" (inBrackets <$> tok)) -- from cleveref.sty , ("(", mathInline $ manyTill anyChar (try $ string "\\)")) , ("[", mathDisplay $ manyTill anyChar (try $ string "\\]")) , ("ensuremath", mathInline braced) -- cgit v1.2.3 From b9a30ef9596b8d19554e03cd1ef8f0dc0695a486 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 24 May 2017 23:23:08 +0200 Subject: Markdown reader: fixed smart quotes after emphasis. E.g. in *foo*'s 'foo' Closes #2228. --- src/Text/Pandoc/Readers/Markdown.hs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 17a7184c0..3e3de0d9d 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -1557,9 +1557,9 @@ ender c n = try $ do 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)) - <|> (ender c 2 >> one c (B.strong <$> contents)) - <|> (ender c 1 >> two c (B.emph <$> contents)) + (ender c 3 >> updateLastStrPos >> return ((B.strong . B.emph) <$> contents)) + <|> (ender c 2 >> updateLastStrPos >> one c (B.strong <$> contents)) + <|> (ender c 1 >> updateLastStrPos >> two c (B.emph <$> contents)) <|> return (return (B.str [c,c,c]) <> contents) -- Parse inlines til you hit two c's, and emit strong. @@ -1567,7 +1567,8 @@ three c = do 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))) + (ender c 2 >> updateLastStrPos >> + return (B.strong <$> (prefix' <> contents))) <|> return (return (B.str [c,c]) <> (prefix' <> contents)) -- Parse inlines til you hit a c, and emit emph. @@ -1578,7 +1579,7 @@ one c prefix' = do <|> try (string [c,c] >> notFollowedBy (ender c 1) >> two c mempty) ) - (ender c 1 >> return (B.emph <$> (prefix' <> contents))) + (ender c 1 >> updateLastStrPos >> return (B.emph <$> (prefix' <> contents))) <|> return (return (B.str [c]) <> (prefix' <> contents)) strongOrEmph :: PandocMonad m => MarkdownParser m (F Inlines) -- cgit v1.2.3 From e6f4636a2cc6a3fa5ae834528fe21280d8f0a56a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 25 May 2017 09:19:34 +0200 Subject: MediaWiki reader: Make smart double quotes depend on `smart` extension. Closes #3585. --- src/Text/Pandoc/Readers/MediaWiki.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/MediaWiki.hs b/src/Text/Pandoc/Readers/MediaWiki.hs index c860a0cdf..b261021e0 100644 --- a/src/Text/Pandoc/Readers/MediaWiki.hs +++ b/src/Text/Pandoc/Readers/MediaWiki.hs @@ -688,6 +688,8 @@ strong = B.strong <$> nested (inlinesBetween start end) end = try $ sym "'''" doubleQuotes :: PandocMonad m => MWParser m Inlines -doubleQuotes = B.doubleQuoted <$> nested (inlinesBetween openDoubleQuote closeDoubleQuote) +doubleQuotes = do + guardEnabled Ext_smart + B.doubleQuoted <$> nested (inlinesBetween openDoubleQuote closeDoubleQuote) where openDoubleQuote = sym "\"" >> lookAhead nonspaceChar closeDoubleQuote = try $ sym "\"" -- cgit v1.2.3 From 41db9e826e5be45d087b1959d6d5dbeb8389e2a7 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 25 May 2017 09:35:25 +0200 Subject: MediaWiki reader: don't do curly quotes inside `<tt>` contexts. Even if `+smart`. See #3585. --- src/Text/Pandoc/Readers/MediaWiki.hs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/MediaWiki.hs b/src/Text/Pandoc/Readers/MediaWiki.hs index b261021e0..3f6142f00 100644 --- a/src/Text/Pandoc/Readers/MediaWiki.hs +++ b/src/Text/Pandoc/Readers/MediaWiki.hs @@ -74,6 +74,7 @@ readMediaWiki opts s = do , mwHeaderMap = M.empty , mwIdentifierList = Set.empty , mwLogMessages = [] + , mwInTT = False } (s ++ "\n") case parsed of @@ -87,6 +88,7 @@ data MWState = MWState { mwOptions :: ReaderOptions , mwHeaderMap :: M.Map Inlines String , mwIdentifierList :: Set.Set String , mwLogMessages :: [LogMessage] + , mwInTT :: Bool } type MWParser m = ParserT [Char] MWState m @@ -569,7 +571,12 @@ inlineTag = do TagOpen "sub" _ -> B.subscript <$> inlinesInTags "sub" TagOpen "sup" _ -> B.superscript <$> inlinesInTags "sup" TagOpen "code" _ -> encode <$> inlinesInTags "code" - TagOpen "tt" _ -> encode <$> inlinesInTags "tt" + TagOpen "tt" _ -> do + inTT <- mwInTT <$> getState + updateState $ \st -> st{ mwInTT = True } + result <- encode <$> inlinesInTags "tt" + updateState $ \st -> st{ mwInTT = inTT } + return result TagOpen "hask" _ -> B.codeWith ("",["haskell"],[]) <$> charsInTags "hask" _ -> B.rawInline "html" . snd <$> htmlTag (~== tag) @@ -690,6 +697,8 @@ strong = B.strong <$> nested (inlinesBetween start end) doubleQuotes :: PandocMonad m => MWParser m Inlines doubleQuotes = do guardEnabled Ext_smart + inTT <- mwInTT <$> getState + guard (not inTT) B.doubleQuoted <$> nested (inlinesBetween openDoubleQuote closeDoubleQuote) where openDoubleQuote = sym "\"" >> lookAhead nonspaceChar closeDoubleQuote = try $ sym "\"" -- cgit v1.2.3 From 8f2c803f973d53da340c876edbbcb2b1223f35cd Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 25 May 2017 11:15:52 +0200 Subject: Markdown reader: warn for notes defined but not used. Closes #1718. Parsing.ParserState: Make stateNotes' a Map, add stateNoteRefs. --- src/Text/Pandoc/Logging.hs | 10 ++++++++++ src/Text/Pandoc/Parsing.hs | 7 +++++-- src/Text/Pandoc/Readers/Markdown.hs | 20 ++++++++++++++------ 3 files changed, 29 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs index 70384f936..7afce9f5f 100644 --- a/src/Text/Pandoc/Logging.hs +++ b/src/Text/Pandoc/Logging.hs @@ -72,6 +72,7 @@ data LogMessage = | CouldNotParseYamlMetadata String SourcePos | DuplicateLinkReference String SourcePos | DuplicateNoteReference String SourcePos + | NoteDefinedButNotUsed String SourcePos | DuplicateIdentifier String SourcePos | ReferenceNotFound String SourcePos | CircularReference String SourcePos @@ -113,6 +114,11 @@ instance ToJSON LogMessage where "source" .= Text.pack (sourceName pos), "line" .= toJSON (sourceLine pos), "column" .= toJSON (sourceColumn pos)] + NoteDefinedButNotUsed s pos -> + ["key" .= Text.pack s, + "source" .= Text.pack (sourceName pos), + "line" .= toJSON (sourceLine pos), + "column" .= toJSON (sourceColumn pos)] DuplicateNoteReference s pos -> ["contents" .= Text.pack s, "source" .= Text.pack (sourceName pos), @@ -203,6 +209,9 @@ showLogMessage msg = "Duplicate link reference '" ++ s ++ "' at " ++ showPos pos DuplicateNoteReference s pos -> "Duplicate note reference '" ++ s ++ "' at " ++ showPos pos + NoteDefinedButNotUsed s pos -> + "Note with key '" ++ s ++ "' defined at " ++ showPos pos ++ + " but not used." DuplicateIdentifier s pos -> "Duplicate identifier '" ++ s ++ "' at " ++ showPos pos ReferenceNotFound s pos -> @@ -256,6 +265,7 @@ messageVerbosity msg = CouldNotParseYamlMetadata{} -> WARNING DuplicateLinkReference{} -> WARNING DuplicateNoteReference{} -> WARNING + NoteDefinedButNotUsed{} -> WARNING DuplicateIdentifier{} -> WARNING ReferenceNotFound{} -> WARNING CircularReference{} -> WARNING diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index e6157dde3..225796272 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -983,6 +983,7 @@ data ParserState = ParserState stateSubstitutions :: SubstTable, -- ^ List of substitution references stateNotes :: NoteTable, -- ^ List of notes (raw bodies) stateNotes' :: NoteTable', -- ^ List of notes (parsed bodies) + stateNoteRefs :: Set.Set String, -- ^ List of note references used stateMeta :: Meta, -- ^ Document metadata stateMeta' :: F Meta, -- ^ Document metadata stateCitations :: M.Map String String, -- ^ RST-style citations @@ -1099,7 +1100,8 @@ defaultParserState = stateHeaderKeys = M.empty, stateSubstitutions = M.empty, stateNotes = [], - stateNotes' = [], + stateNotes' = M.empty, + stateNoteRefs = Set.empty, stateMeta = nullMeta, stateMeta' = return nullMeta, stateCitations = M.empty, @@ -1166,7 +1168,8 @@ data QuoteContext type NoteTable = [(String, String)] -type NoteTable' = [(String, F Blocks)] -- used in markdown reader +type NoteTable' = M.Map String (SourcePos, F Blocks) +-- used in markdown reader newtype Key = Key String deriving (Show, Read, Eq, Ord) diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 3e3de0d9d..11f35deb2 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -362,6 +362,14 @@ parseMarkdown = do optional titleBlock blocks <- parseBlocks st <- getState + -- check for notes with no corresponding note references + let notesUsed = stateNoteRefs st + let notesDefined = M.keys (stateNotes' st) + mapM_ (\n -> unless (n `Set.member` notesUsed) $ do + -- lookup to get sourcepos + case M.lookup n (stateNotes' st) of + Just (pos, _) -> report (NoteDefinedButNotUsed n pos) + Nothing -> error "The impossible happened.") notesDefined let doc = runF (do Pandoc _ bs <- B.doc <$> blocks meta <- stateMeta' st return $ Pandoc meta bs) st @@ -469,12 +477,11 @@ noteBlock = try $ do let raw = unlines (first:rest) ++ "\n" optional blanklines parsed <- parseFromString' parseBlocks raw - let newnote = (ref, parsed) oldnotes <- stateNotes' <$> getState - case lookup ref oldnotes of + case M.lookup ref oldnotes of Just _ -> logMessage $ DuplicateNoteReference ref pos Nothing -> return () - updateState $ \s -> s { stateNotes' = newnote : oldnotes } + updateState $ \s -> s { stateNotes' = M.insert ref (pos, parsed) oldnotes } return mempty -- @@ -1816,16 +1823,17 @@ note :: PandocMonad m => MarkdownParser m (F Inlines) note = try $ do guardEnabled Ext_footnotes ref <- noteMarker + updateState $ \st -> st{ stateNoteRefs = Set.insert ref (stateNoteRefs st) } return $ do notes <- asksF stateNotes' - case lookup ref notes of + case M.lookup ref notes of Nothing -> return $ B.str $ "[^" ++ ref ++ "]" - Just contents -> do + Just (_pos, contents) -> do st <- askF -- process the note in a context that doesn't resolve -- notes, to avoid infinite looping with notes inside -- notes: - let contents' = runF contents st{ stateNotes' = [] } + let contents' = runF contents st{ stateNotes' = M.empty } return $ B.note contents' inlineNote :: PandocMonad m => MarkdownParser m (F Inlines) -- cgit v1.2.3 From 650e1ac1fdbbb172c58c1898607a7f82806cf55e Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 25 May 2017 12:11:46 +0200 Subject: Docx writer: Use Table rather than "Table Normal" for table style. "Table Normal" is the default table style and can't be modified. Closes #3275, further testing welcome. --- src/Text/Pandoc/Writers/Docx.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index a10840033..5b714ba41 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -953,7 +953,7 @@ blockToOpenXML' opts (Table caption aligns widths headers rows) = do caption' ++ [mknode "w:tbl" [] ( mknode "w:tblPr" [] - ( mknode "w:tblStyle" [("w:val","TableNormal")] () : + ( mknode "w:tblStyle" [("w:val","Table")] () : mknode "w:tblW" [("w:type", "pct"), ("w:w", show rowwidth)] () : mknode "w:tblLook" [("w:firstRow","1") | hasHeader ] () : [ mknode "w:tblCaption" [("w:val", captionStr)] () -- cgit v1.2.3 From 708973a33a0ce425bb21a5ffa06fbdab465d3fb8 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 25 May 2017 12:50:43 +0200 Subject: Added `spaced_reference_links` extension. This is now the default for pandoc's Markdown. It allows whitespace between the two parts of a reference link: e.g. [a] [b] [b]: url This is now forbidden by default. Closes #2602. --- src/Text/Pandoc/Extensions.hs | 6 +++++- src/Text/Pandoc/Readers/Markdown.hs | 8 +++++--- 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 374fae2c1..58e8c414d 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -137,6 +137,7 @@ data Extension = | Ext_shortcut_reference_links -- ^ Shortcut reference links | Ext_smart -- ^ "Smart" quotes, apostrophes, ellipses, dashes | Ext_old_dashes -- ^ -- = em, - before number = en + | Ext_spaced_reference_links -- ^ Allow space between two parts of ref link deriving (Show, Read, Enum, Eq, Ord, Bounded, Data, Typeable, Generic) -- | Extensions to be used with pandoc-flavored markdown. @@ -187,7 +188,7 @@ pandocExtensions = extensionsFromList , Ext_smart ] --- | Extensions to be used with github-flavored markdown. +-- | Extensions to be used with plain text output. plainExtensions :: Extensions plainExtensions = extensionsFromList [ Ext_table_captions @@ -220,6 +221,7 @@ phpMarkdownExtraExtensions = extensionsFromList , Ext_link_attributes , Ext_abbreviations , Ext_shortcut_reference_links + , Ext_spaced_reference_links ] -- | Extensions to be used with github-flavored markdown. @@ -272,6 +274,7 @@ multimarkdownExtensions = extensionsFromList , Ext_superscript , Ext_subscript , Ext_backtick_code_blocks + , Ext_spaced_reference_links ] -- | Language extensions to be used with strict markdown. @@ -279,6 +282,7 @@ strictExtensions :: Extensions strictExtensions = extensionsFromList [ Ext_raw_html , Ext_shortcut_reference_links + , Ext_spaced_reference_links ] -- | Default extensions from format-describing string. diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 11f35deb2..4fb75b344 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -1750,10 +1750,12 @@ referenceLink :: PandocMonad m referenceLink constructor (lab, raw) = do sp <- (True <$ lookAhead (char ' ')) <|> return False (_,raw') <- option (mempty, "") $ - lookAhead (try (guardEnabled Ext_citations >> - spnl >> normalCite >> return (mempty, ""))) + lookAhead (try (do guardEnabled Ext_citations + guardDisabled Ext_spaced_reference_links <|> spnl + normalCite + return (mempty, ""))) <|> - try (spnl >> reference) + try ((guardDisabled Ext_spaced_reference_links <|> spnl) >> reference) when (raw' == "") $ guardEnabled Ext_shortcut_reference_links let labIsRef = raw' == "" || raw' == "[]" let key = toKey $ if labIsRef then raw else raw' -- cgit v1.2.3 From cb7b0a69859cbf838519c5ad5f35d40ffd4f4246 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 25 May 2017 22:48:27 +0200 Subject: Allow em for image height/width in HTML, LaTeX. - Export `inEm` from ImageSize [API change]. - Change `showFl` and `show` instance for `Dimension` so extra decimal places are omitted. - Added `Em` as a constructor of `Dimension` [API change]. - Allow `em`, `cm`, `in` to pass through without conversion in HTML, LaTeX. Closes #3450. --- src/Text/Pandoc/ImageSize.hs | 20 ++++++++++++++++++-- src/Text/Pandoc/Writers/HTML.hs | 7 +++---- 2 files changed, 21 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/ImageSize.hs b/src/Text/Pandoc/ImageSize.hs index 4d914a10c..eec8658c5 100644 --- a/src/Text/Pandoc/ImageSize.hs +++ b/src/Text/Pandoc/ImageSize.hs @@ -43,6 +43,7 @@ module Text.Pandoc.ImageSize ( ImageType(..) , inInch , inPixel , inPoints + , inEm , numUnit , showInInch , showInPixel @@ -80,12 +81,14 @@ data Dimension = Pixel Integer | Centimeter Double | Inch Double | Percent Double + | Em Double instance Show Dimension where show (Pixel a) = show a ++ "px" show (Centimeter a) = showFl a ++ "cm" show (Inch a) = showFl a ++ "in" show (Percent a) = show a ++ "%" + show (Em a) = showFl a ++ "em" data ImageSize = ImageSize{ pxX :: Integer @@ -97,7 +100,13 @@ instance Default ImageSize where def = ImageSize 300 200 72 72 showFl :: (RealFloat a) => a -> String -showFl a = showFFloat (Just 5) a "" +showFl a = removeExtra0s $ showFFloat (Just 5) a "" + +removeExtra0s :: String -> String +removeExtra0s s = + case dropWhile (=='0') $ reverse s of + '.':xs -> reverse xs + xs -> reverse xs imageType :: ByteString -> Maybe ImageType imageType img = case B.take 4 img of @@ -167,6 +176,9 @@ desiredSizeInPoints opts attr s = inPoints :: WriterOptions -> Dimension -> Double inPoints opts dim = 72 * inInch opts dim +inEm :: WriterOptions -> Dimension -> Double +inEm opts dim = (64/11) * inInch opts dim + inInch :: WriterOptions -> Dimension -> Double inInch opts dim = case dim of @@ -174,6 +186,7 @@ inInch opts dim = (Centimeter a) -> a * 0.3937007874 (Inch a) -> a (Percent _) -> 0 + (Em a) -> a * (11/64) inPixel :: WriterOptions -> Dimension -> Integer inPixel opts dim = @@ -181,7 +194,8 @@ inPixel opts dim = (Pixel a) -> a (Centimeter a) -> floor $ dpi * a * 0.3937007874 :: Integer (Inch a) -> floor $ dpi * a :: Integer - _ -> 0 + (Percent _) -> 0 + (Em a) -> floor $ dpi * a * (11/64) :: Integer where dpi = fromIntegral $ writerDpi opts @@ -213,6 +227,7 @@ scaleDimension factor dim = Centimeter x -> Centimeter (factor * x) Inch x -> Inch (factor * x) Percent x -> Percent (factor * x) + Em x -> Em (factor * x) -- | Read a Dimension from an Attr attribute. -- `dimension Width attr` might return `Just (Pixel 3)` or for example `Just (Centimeter 2.0)`, etc. @@ -236,6 +251,7 @@ lengthToDim s = numUnit s >>= uncurry toDim toDim a "" = Just $ Pixel (floor a::Integer) toDim a "pt" = Just $ Inch (a / 72) toDim a "pc" = Just $ Inch (a / 6) + toDim a "em" = Just $ Em a toDim _ _ = Nothing epsSize :: ByteString -> Maybe ImageSize diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 63e839684..2a72f6f3d 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -533,10 +533,9 @@ dimensionsToAttrList :: WriterOptions -> Attr -> [(String, String)] dimensionsToAttrList opts attr = (go Width) ++ (go Height) where go dir = case (dimension dir attr) of - (Just (Percent a)) -> [("style", show dir ++ ":" ++ show (Percent a))] - (Just dim) -> [(show dir, showInPixel opts dim)] - _ -> [] - + (Just (Pixel a)) -> [(show dir, show a)] + (Just x) -> [("style", show dir ++ ":" ++ show x)] + Nothing -> [] imageExts :: [String] imageExts = [ "art", "bmp", "cdr", "cdt", "cpt", "cr2", "crw", "djvu", "erf", -- cgit v1.2.3 From 8ec03cfc872d7ff58a2585740cecb4215313251a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 26 May 2017 10:21:55 +0200 Subject: HTML writer: Removed unused parameter in dimensionsToAttributeList. --- src/Text/Pandoc/Writers/HTML.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 2a72f6f3d..030f332ca 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -521,7 +521,7 @@ attrsToHtml opts (id',classes',keyvals) = imgAttrsToHtml :: WriterOptions -> Attr -> [Attribute] imgAttrsToHtml opts attr = attrsToHtml opts (ident,cls,kvs') ++ - toAttrs (dimensionsToAttrList opts attr) + toAttrs (dimensionsToAttrList attr) where (ident,cls,kvs) = attr kvs' = filter isNotDim kvs @@ -529,8 +529,8 @@ imgAttrsToHtml opts attr = isNotDim ("height", _) = False isNotDim _ = True -dimensionsToAttrList :: WriterOptions -> Attr -> [(String, String)] -dimensionsToAttrList opts attr = (go Width) ++ (go Height) +dimensionsToAttrList :: Attr -> [(String, String)] +dimensionsToAttrList attr = (go Width) ++ (go Height) where go dir = case (dimension dir attr) of (Just (Pixel a)) -> [(show dir, show a)] -- cgit v1.2.3 From bf93c07267bf138f4f4cab7625ff273fa2ac67cd Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sat, 27 May 2017 15:24:01 +0200 Subject: Org reader: subject full doc tree to headline transformations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Emacs parses org documents into a tree structure, which is then post-processed during exporting. The reader is changed to do the same, turning the document into a single tree of headlines starting at level 0. Fixes: #3695 --- src/Text/Pandoc/Readers/Org/Blocks.hs | 10 ++++----- src/Text/Pandoc/Readers/Org/DocumentTree.hs | 33 ++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index fa2f7fac5..52e990584 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -33,7 +33,7 @@ module Text.Pandoc.Readers.Org.Blocks ) where import Text.Pandoc.Readers.Org.BlockStarts -import Text.Pandoc.Readers.Org.DocumentTree (headline, headlineToBlocks) +import Text.Pandoc.Readers.Org.DocumentTree (documentTree, headlineToBlocks) import Text.Pandoc.Readers.Org.Inlines import Text.Pandoc.Readers.Org.Meta (metaExport, metaKey, metaLine) import Text.Pandoc.Readers.Org.ParserState @@ -62,11 +62,11 @@ import Data.Monoid ((<>)) -- | Get a list of blocks. blockList :: PandocMonad m => OrgParser m [Block] blockList = do - initialBlocks <- blocks - headlines <- sequence <$> manyTill (headline blocks inline 1) eof + headlines <- documentTree blocks inline st <- getState - headlineBlocks <- fmap mconcat . mapM headlineToBlocks $ runF headlines st - return . B.toList $ (runF initialBlocks st) <> headlineBlocks + headlineBlocks <- headlineToBlocks $ runF headlines st + -- ignore first headline, it's the document's title + return . drop 1 . B.toList $ headlineBlocks -- | Get the meta information saved in the state. meta :: Monad m => OrgParser m Meta diff --git a/src/Text/Pandoc/Readers/Org/DocumentTree.hs b/src/Text/Pandoc/Readers/Org/DocumentTree.hs index 53ec2ef57..8c2a8482a 100644 --- a/src/Text/Pandoc/Readers/Org/DocumentTree.hs +++ b/src/Text/Pandoc/Readers/Org/DocumentTree.hs @@ -28,7 +28,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Parsers for org-mode headlines and document subtrees -} module Text.Pandoc.Readers.Org.DocumentTree - ( headline + ( documentTree , headlineToBlocks ) where @@ -43,11 +43,42 @@ import Text.Pandoc.Readers.Org.BlockStarts import Text.Pandoc.Readers.Org.Parsing import Text.Pandoc.Readers.Org.ParserState +import qualified Data.Map as Map import qualified Text.Pandoc.Builder as B -- -- Org headers -- + +-- | Parse input as org document tree. +documentTree :: PandocMonad m + => OrgParser m (F Blocks) + -> OrgParser m (F Inlines) + -> OrgParser m (F Headline) +documentTree blocks inline = do + initialBlocks <- blocks + headlines <- sequence <$> manyTill (headline blocks inline 1) eof + title <- fmap (getTitle . unMeta) . orgStateMeta <$> getState + return $ do + headlines' <- headlines + initialBlocks' <- initialBlocks + title' <- title + return Headline + { headlineLevel = 0 + , headlineTodoMarker = Nothing + , headlineText = B.fromList title' + , headlineTags = mempty + , headlineProperties = mempty + , headlineContents = initialBlocks' + , headlineChildren = headlines' + } + where + getTitle :: Map.Map String MetaValue -> [Inline] + getTitle metamap = + case Map.lookup "title" metamap of + Just (MetaInlines inlns) -> inlns + _ -> [] + newtype Tag = Tag { fromTag :: String } deriving (Show, Eq) -- cgit v1.2.3 From 4dabcc27f69f6d2ec0b5ed7829927cc58b39f8c9 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 27 May 2017 23:16:07 +0200 Subject: Pretty: Eq instance for Doc. --- src/Text/Pandoc/Pretty.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Pretty.hs b/src/Text/Pandoc/Pretty.hs index a432949c8..d78a2f1d9 100644 --- a/src/Text/Pandoc/Pretty.hs +++ b/src/Text/Pandoc/Pretty.hs @@ -108,10 +108,10 @@ data D = Text Int String | CarriageReturn | NewLine | BlankLines Int -- number of blank lines - deriving (Show) + deriving (Show, Eq) newtype Doc = Doc { unDoc :: Seq D } - deriving (Monoid, Show) + deriving (Monoid, Show, Eq) instance IsString Doc where fromString = text -- cgit v1.2.3 From 8614902234902c02f6493b651e585527d49e058b Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 27 May 2017 21:41:47 +0200 Subject: Markdown writer: changes to `--reference-links`. With `--reference-location` of `section` or `block`, pandoc will now repeat references that have been used in earlier sections. The Markdown reader has also been modified, so that *exactly* repeated references do not generate a warning, only references with the same label but different targets. The idea is that, with references after every block, one might want to repeat references sometimes. Closes #3701. --- src/Text/Pandoc/Readers/Markdown.hs | 8 +++-- src/Text/Pandoc/Writers/Markdown.hs | 59 +++++++++++++++++++++++++++---------- 2 files changed, 50 insertions(+), 17 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 4fb75b344..95310346c 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -414,8 +414,12 @@ referenceKey = try $ do let oldkeys = stateKeys st let key = toKey raw case M.lookup key oldkeys of - Just _ -> logMessage $ DuplicateLinkReference raw pos - Nothing -> return () + Just (t,a) | not (t == target && a == attr') -> + -- We don't warn on two duplicate keys if the targets are also + -- the same. This can happen naturally with --reference-location=block + -- or section. See #3701. + logMessage $ DuplicateLinkReference raw pos + _ -> return () updateState $ \s -> s { stateKeys = M.insert key (target, attr') oldkeys } return $ return mempty diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index e858bc43f..efdf3852b 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -34,12 +34,12 @@ Conversion of 'Pandoc' documents to markdown-formatted plain text. Markdown: <http://daringfireball.net/projects/markdown/> -} module Text.Pandoc.Writers.Markdown (writeMarkdown, writePlain) where -import Control.Monad.Except (throwError) import Control.Monad.Reader import Control.Monad.State import Data.Char (chr, isPunctuation, isSpace, ord) import Data.Default import qualified Data.HashMap.Strict as H +import qualified Data.Map as M import Data.List (find, group, intersperse, sortBy, stripPrefix, transpose) import Data.Maybe (fromMaybe) import Data.Monoid (Any (..)) @@ -52,7 +52,6 @@ import Network.HTTP (urlEncode) import Text.HTML.TagSoup (Tag (..), isTagText, parseTags) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Definition -import Text.Pandoc.Error import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (blankline, blanklines, char, space) @@ -90,7 +89,9 @@ instance Default WriterEnv data WriterState = WriterState { stNotes :: Notes , stRefs :: Refs - , stKeys :: Set.Set Key + , stKeys :: M.Map Key + (M.Map (Target, Attr) Int) + , stLastIdx :: Int , stIds :: Set.Set String , stNoteNum :: Int } @@ -98,7 +99,8 @@ data WriterState = WriterState { stNotes :: Notes instance Default WriterState where def = WriterState{ stNotes = [] , stRefs = [] - , stKeys = Set.empty + , stKeys = M.empty + , stLastIdx = 0 , stIds = Set.empty , stNoteNum = 1 } @@ -804,17 +806,44 @@ getReference attr label target = do Just (ref, _, _) -> return ref Nothing -> do keys <- gets stKeys - label' <- if isEmpty label || getKey label `Set.member` keys - then case find (\n -> not (Key n `Set.member` keys)) $ - map show [1..(10000 :: Integer)] of - Just x -> return $ text x - Nothing -> - throwError $ PandocSomeError "no unique label" - else return label - modify (\s -> s{ stRefs = (label', target, attr) : stRefs s, - stKeys = Set.insert (getKey label') (stKeys s) - }) - return label' + case M.lookup (getKey label) keys of + Nothing -> do -- no other refs with this label + (lab', idx) <- if isEmpty label + then do + i <- (+ 1) <$> gets stLastIdx + modify $ \s -> s{ stLastIdx = i } + return (text (show i), i) + else return (label, 0) + modify (\s -> s{ + stRefs = (lab', target, attr) : refs, + stKeys = M.insert (getKey label) + (M.insert (target, attr) idx mempty) + (stKeys s) }) + return lab' + + Just km -> do -- we have refs with this label + case M.lookup (target, attr) km of + Just i -> do + let lab' = label <> if i == 0 + then mempty + else text (show i) + -- make sure it's in stRefs; it may be + -- a duplicate that was printed in a previous + -- block: + when ((lab', target, attr) `notElem` refs) $ + modify (\s -> s{ + stRefs = (lab', target, attr) : refs }) + return lab' + Nothing -> do -- but this one is to a new target + i <- (+ 1) <$> gets stLastIdx + modify $ \s -> s{ stLastIdx = i } + let lab' = text (show i) + modify (\s -> s{ + stRefs = (lab', target, attr) : refs, + stKeys = M.insert (getKey label) + (M.insert (target, attr) i km) + (stKeys s) }) + return lab' -- | Convert list of Pandoc inline elements to markdown. inlineListToMarkdown :: PandocMonad m => WriterOptions -> [Inline] -> MD m Doc -- cgit v1.2.3 From c38d5966edd9954d4ec1eb30b2eff140cb63d93e Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sun, 28 May 2017 10:29:37 +0300 Subject: RST reader: use anyLineNewline in rawListItem (#3702) --- src/Text/Pandoc/Readers/RST.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 1ea142112..b242d6428 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -568,9 +568,9 @@ rawListItem :: Monad m => RSTParser m Int -> RSTParser m (Int, [Char]) rawListItem start = try $ do markerLength <- start - firstLine <- anyLine + firstLine <- anyLineNewline restLines <- many (listLine markerLength) - return (markerLength, (firstLine ++ "\n" ++ (concat restLines))) + return (markerLength, firstLine ++ concat restLines) -- continuation of a list item - indented and separated by blankline or -- (in compact lists) endline. -- cgit v1.2.3 From 5a71632d11f86cb6ed700fe3a4d717a56bad1b9e Mon Sep 17 00:00:00 2001 From: Herwig Stuetz <herwig@herwigstuetz.com> Date: Tue, 23 May 2017 23:21:51 +0200 Subject: Parsing: `many1Till`: Check for the end condition before parsing By not checking for the end condition before the first parse, the parser was applied too often, consuming too much of the input. This fixes the behaviour of `testStringWith (many1Till (oneOf "ab") (string "aa")) "aaa"` which before incorrectly returned `Right "a"`. With this change, it instead correctly fails with `Left (PandocParsecError ...)` because it is not able to parse at least one occurence of `oneOf "ab"` that is not `"aa"`. Note that this only affects `many1Till p end` where `p` matches on a prefix of `end`. --- src/Text/Pandoc/Parsing.hs | 5 +++-- src/Text/Pandoc/Readers/Org/Inlines.hs | 4 ++-- src/Text/Pandoc/Readers/TWiki.hs | 4 ++-- src/Text/Pandoc/Readers/Textile.hs | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 225796272..cd51bff69 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -274,11 +274,12 @@ indentWith num = do , try (char '\t' >> indentWith (num - tabStop)) ] -- | Like @manyTill@, but reads at least one item. -many1Till :: Stream s m t +many1Till :: (Show end, Stream s m t) => ParserT s st m a -> ParserT s st m end -> ParserT s st m [a] many1Till p end = do + notFollowedBy' end first <- p rest <- manyTill p end return (first:rest) @@ -343,7 +344,7 @@ blanklines :: Stream s m Char => ParserT s st m [Char] blanklines = many1 blankline -- | Parses material enclosed between start and end parsers. -enclosed :: Stream s m Char => ParserT s st m t -- ^ start parser +enclosed :: (Show end, Stream s m Char) => ParserT s st m t -- ^ start parser -> ParserT s st m end -- ^ end parser -> ParserT s st m a -- ^ content parser (to be used repeatedly) -> ParserT s st m [a] diff --git a/src/Text/Pandoc/Readers/Org/Inlines.hs b/src/Text/Pandoc/Readers/Org/Inlines.hs index aa376fe25..6946e8379 100644 --- a/src/Text/Pandoc/Readers/Org/Inlines.hs +++ b/src/Text/Pandoc/Readers/Org/Inlines.hs @@ -687,13 +687,13 @@ mathEnd c = try $ do return res -enclosedInlines :: PandocMonad m => OrgParser m a +enclosedInlines :: (PandocMonad m, Show b) => OrgParser m a -> OrgParser m b -> OrgParser m (F Inlines) enclosedInlines start end = try $ trimInlinesF . mconcat <$> enclosed start end inline -enclosedRaw :: PandocMonad m => OrgParser m a +enclosedRaw :: (PandocMonad m, Show b) => OrgParser m a -> OrgParser m b -> OrgParser m String enclosedRaw start end = try $ diff --git a/src/Text/Pandoc/Readers/TWiki.hs b/src/Text/Pandoc/Readers/TWiki.hs index aea55b7a9..fcb95fc35 100644 --- a/src/Text/Pandoc/Readers/TWiki.hs +++ b/src/Text/Pandoc/Readers/TWiki.hs @@ -349,13 +349,13 @@ linebreak = newline >> notFollowedBy newline >> (lastNewline <|> innerNewline) where lastNewline = eof >> return mempty innerNewline = return B.space -between :: (Monoid c, PandocMonad m) +between :: (Monoid c, PandocMonad m, Show b) => 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, PandocMonad m) +enclosed :: (Monoid b, PandocMonad m, Show a) => TWParser m a -> (TWParser m a -> TWParser m b) -> TWParser m b enclosed sep p = between sep (try $ sep <* endMarker) p where diff --git a/src/Text/Pandoc/Readers/Textile.hs b/src/Text/Pandoc/Readers/Textile.hs index 52f4f2493..0b964dd63 100644 --- a/src/Text/Pandoc/Readers/Textile.hs +++ b/src/Text/Pandoc/Readers/Textile.hs @@ -692,7 +692,7 @@ langAttr = do return $ \(id',classes,keyvals) -> (id',classes,("lang",lang):keyvals) -- | Parses material surrounded by a parser. -surrounded :: PandocMonad m +surrounded :: (PandocMonad m, Show t) => ParserT [Char] st m t -- ^ surrounding parser -> ParserT [Char] st m a -- ^ content parser (to be used repeatedly) -> ParserT [Char] st m [a] -- cgit v1.2.3 From bfd5c6b172b7b4cc471b1ed80673bac545604f62 Mon Sep 17 00:00:00 2001 From: Herwig Stuetz <herwig@herwigstuetz.com> Date: Tue, 23 May 2017 21:30:31 +0200 Subject: Org reader: Fix cite parsing behaviour Until now, org-ref cite keys included special characters also at the end. This caused problems when citations occur right before colons or at the end of a sentence. With this change, all non alphanumeric characters at the end of a cite key are ignored. This also adds `,` to the list of special characters that are legal in cite keys to better mirror the behaviour of org-export. --- src/Text/Pandoc/Readers/Org/Inlines.hs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/Inlines.hs b/src/Text/Pandoc/Readers/Org/Inlines.hs index 6946e8379..dcea61222 100644 --- a/src/Text/Pandoc/Readers/Org/Inlines.hs +++ b/src/Text/Pandoc/Readers/Org/Inlines.hs @@ -339,8 +339,16 @@ 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 :: PandocMonad m => OrgParser m String -orgRefCiteKey = try . many1 . satisfy $ \c -> - isAlphaNum c || c `elem` ("-_:\\./"::String) +orgRefCiteKey = + let citeKeySpecialChars = "-_:\\./," :: String + isCiteKeySpecialChar c = c `elem` citeKeySpecialChars + isCiteKeyChar c = isAlphaNum c || isCiteKeySpecialChar c + + in try $ many1Till (satisfy $ isCiteKeyChar) + $ try . lookAhead $ do + many . satisfy $ isCiteKeySpecialChar + satisfy $ not . isCiteKeyChar + -- | Supported citation types. Only a small subset of org-ref types is -- supported for now. TODO: rewrite this, use LaTeX reader as template. -- cgit v1.2.3 From efc069de5d6714119eac6d70338cac514d07139c Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sun, 28 May 2017 22:52:35 +0300 Subject: Markdown reader: use anyLineNewline --- src/Text/Pandoc/Readers/Markdown.hs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 95310346c..7e2bd5a4d 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -883,8 +883,7 @@ listContinuationLine = try $ do notFollowedBy' listStart notFollowedByHtmlCloser optional indentSpaces - result <- anyLine - return $ result ++ "\n" + anyLineNewline listItem :: PandocMonad m => MarkdownParser m a @@ -956,7 +955,7 @@ defRawBlock :: PandocMonad m => Bool -> MarkdownParser m String defRawBlock compact = try $ do hasBlank <- option False $ blankline >> return True defListMarker - firstline <- anyLine + firstline <- anyLineNewline let dline = try ( do notFollowedBy blankline notFollowedByHtmlCloser @@ -971,7 +970,7 @@ defRawBlock compact = try $ do ln <- indentSpaces >> notFollowedBy blankline >> anyLine lns <- many dline return $ trailing ++ unlines (ln:lns) - return $ trimr (firstline ++ "\n" ++ unlines rawlines ++ cont) ++ + return $ trimr (firstline ++ unlines rawlines ++ cont) ++ if hasBlank || not (null cont) then "\n\n" else "" definitionList :: PandocMonad m => MarkdownParser m (F Blocks) -- cgit v1.2.3 From 230a1b89e8fd761a67599aad190a6b9462686abc Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 29 May 2017 15:09:24 +0200 Subject: LaTeX reader: don't crash on empty enumerate environment. Closes #3707. --- 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 7bcd120ce..4179f4550 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1321,7 +1321,7 @@ fancyverbEnv name = do codeBlockWith attr <$> verbEnv name orderedList' :: PandocMonad m => LP m Blocks -orderedList' = do +orderedList' = try $ do optional sp (_, style, delim) <- option (1, DefaultStyle, DefaultDelim) $ try $ char '[' *> anyOrderedListMarker <* char ']' -- cgit v1.2.3 From 5ec384eb60a4d32a83e94eec041020004dff96ce Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 29 May 2017 22:45:49 +0200 Subject: LaTeX reader: handle escaped & inside table cell. Closes #3708. --- src/Text/Pandoc/Readers/LaTeX.hs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 4179f4550..d1262867c 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1494,9 +1494,11 @@ parseTableRow :: PandocMonad m -> [String] -- ^ suffixes -> LP m [Blocks] parseTableRow cols prefixes suffixes = try $ do - let tableCellRaw = many (notFollowedBy - (amp <|> lbreak <|> - (() <$ try (string "\\end"))) >> anyChar) + let tableCellRaw = concat <$> many + (do notFollowedBy (amp <|> lbreak <|> (() <$ try (string "\\end"))) + many1 (noneOf "&%\n\r\\") + <|> try (string "\\&") + <|> count 1 anyChar) let minipage = try $ controlSeq "begin" *> string "{minipage}" *> env "minipage" (skipopts *> spaces' *> optional braced *> spaces' *> blocks) -- cgit v1.2.3 From 774075c3e22ab2ad35e2306a5b98e30da512b310 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 30 May 2017 10:22:48 +0200 Subject: Added eastAsianLineBreakFilter to Shared. This used to live in the Markdown reader. --- src/Text/Pandoc/Readers/Markdown.hs | 12 +----------- src/Text/Pandoc/Shared.hs | 12 ++++++++++++ 2 files changed, 13 insertions(+), 11 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 7e2bd5a4d..5694c4354 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -55,11 +55,9 @@ import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.Emoji (emojis) -import Text.Pandoc.Generic (bottomUp) import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (tableWith) -import Text.Pandoc.Pretty (charWidth) import Text.Pandoc.Readers.HTML (htmlInBalanced, htmlTag, isBlockTag, isCommentTag, isInlineTag, isTextTag) import Text.Pandoc.Readers.LaTeX (rawLaTeXBlock, rawLaTeXInline) @@ -375,15 +373,7 @@ parseMarkdown = do return $ Pandoc meta bs) st reportLogMessages (do guardEnabled Ext_east_asian_line_breaks - return $ bottomUp softBreakFilter doc) <|> return doc - -softBreakFilter :: [Inline] -> [Inline] -softBreakFilter (x:SoftBreak:y:zs) = - case (stringify x, stringify y) of - (xs@(_:_), (c:_)) - | charWidth (last xs) == 2 && charWidth c == 2 -> x:y:zs - _ -> x:SoftBreak:y:zs -softBreakFilter xs = xs + return $ eastAsianLineBreakFilter doc) <|> return doc referenceKey :: PandocMonad m => MarkdownParser m (F Blocks) referenceKey = try $ do diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index a6c6fb95f..ce2c4888a 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -70,6 +70,7 @@ module Text.Pandoc.Shared ( isTightList, addMetaField, makeMeta, + eastAsianLineBreakFilter, -- * TagSoup HTML handling renderTags', -- * File handling @@ -120,6 +121,7 @@ import qualified Control.Monad.State as S import qualified Control.Exception as E import Control.Monad (msum, unless, MonadPlus(..)) import Text.Pandoc.Pretty (charWidth) +import Text.Pandoc.Generic (bottomUp) import Text.Pandoc.Compat.Time import Data.Time.Clock.POSIX import System.IO.Error @@ -578,6 +580,16 @@ makeMeta title authors date = $ addMetaField "date" (B.fromList date) $ nullMeta +-- | Remove soft breaks between East Asian characters. +eastAsianLineBreakFilter :: Pandoc -> Pandoc +eastAsianLineBreakFilter = bottomUp go + where go (x:SoftBreak:y:zs) = + case (stringify x, stringify y) of + (xs@(_:_), (c:_)) + | charWidth (last xs) == 2 && charWidth c == 2 -> x:y:zs + _ -> x:SoftBreak:y:zs + go xs = xs + -- -- TagSoup HTML handling -- -- cgit v1.2.3 From f955af58e6a668179ec678c13d2c5a6c0c3d7b63 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Wed, 31 May 2017 19:59:34 +0200 Subject: Odt reader: remove dead code The ODT reader contained a lot of general code useful for working with arrows. However, many of these utils weren't used and are hence removed. --- src/Text/Pandoc/Readers/Odt/Arrows/State.hs | 90 ------ src/Text/Pandoc/Readers/Odt/Arrows/Utils.hs | 272 +----------------- src/Text/Pandoc/Readers/Odt/ContentReader.hs | 5 - src/Text/Pandoc/Readers/Odt/Generic/Fallible.hs | 129 --------- src/Text/Pandoc/Readers/Odt/Generic/Utils.hs | 7 - .../Pandoc/Readers/Odt/Generic/XMLConverter.hs | 320 --------------------- src/Text/Pandoc/Readers/Odt/StyleReader.hs | 83 ------ 7 files changed, 4 insertions(+), 902 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Odt/Arrows/State.hs b/src/Text/Pandoc/Readers/Odt/Arrows/State.hs index b056f1ecc..3d716ba19 100644 --- a/src/Text/Pandoc/Readers/Odt/Arrows/State.hs +++ b/src/Text/Pandoc/Readers/Odt/Arrows/State.hs @@ -58,10 +58,6 @@ newtype ArrowState state a b = ArrowState withState :: (state -> a -> (state, b)) -> ArrowState state a b withState = ArrowState . uncurry --- | Constructor -withState' :: ((state, a) -> (state, b)) -> ArrowState state a b -withState' = ArrowState - -- | Constructor modifyState :: (state -> state ) -> ArrowState state a a modifyState = ArrowState . first @@ -78,10 +74,6 @@ fromState = ArrowState . (.fst) extractFromState :: (state -> b ) -> ArrowState state x b extractFromState f = ArrowState $ \(state,_) -> (state, f state) --- | Constructor -withUnchangedState :: (state -> a -> b ) -> ArrowState state a b -withUnchangedState f = ArrowState $ \(state,a) -> (state, f state a) - -- | Constructor tryModifyState :: (state -> Either f state) -> ArrowState state a (Either f a) @@ -107,43 +99,9 @@ instance ArrowChoice (ArrowState state) where Left l -> (s, Left l) Right r -> second Right $ runArrowState a (s,r) -instance ArrowLoop (ArrowState state) where - loop a = ArrowState $ \(s, x) - -> let (s', (x', _d)) = runArrowState a (s, (x, _d)) - in (s', x') - instance ArrowApply (ArrowState state) where app = ArrowState $ \(s, (f,b)) -> runArrowState f (s,b) - --- | Embedding of a state arrow in a state arrow with a different state type. -switchState :: (s -> s') -> (s' -> s) -> ArrowState s' x y -> ArrowState s x y -switchState there back a = ArrowState $ first there - >>> runArrowState a - >>> first back - --- | Lift a state arrow to modify the state of an arrow --- with a different state type. -liftToState :: (s -> s') -> ArrowState s' s s -> ArrowState s x x -liftToState unlift a = modifyState $ unlift &&& id - >>> runArrowState a - >>> snd - --- | Switches the type of the state temporarily. --- Drops the intermediate result state, behaving like the identity arrow, --- save for side effects in the state. -withSubState :: ArrowState s x s2 -> ArrowState s2 s s -> ArrowState s x x -withSubState unlift a = keepingTheValue (withSubState unlift a) >>^ fst - --- | Switches the type of the state temporarily. --- Returns the resulting sub-state. -withSubState' :: ArrowState s x s' -> ArrowState s' s s -> ArrowState s x s' -withSubState' unlift a = ArrowState $ runArrowState unlift - >>> switch - >>> runArrowState a - >>> switch - where switch (x,y) = (y,x) - -- | Switches the type of the state temporarily. -- Drops the intermediate result state, behaving like a fallible -- identity arrow, save for side effects in the state. @@ -175,42 +133,6 @@ foldS :: (Foldable f, Monoid m) => ArrowState s x m -> ArrowState s (f x) m foldS a = ArrowState $ \(s,f) -> foldr a' (s,mempty) f where a' x (s',m) = second (m <>) $ runArrowState a (s',x) --- | Fold a state arrow through something 'Foldable'. Collect the results --- in a 'Monoid'. --- Intermediate form of a fold between one with "only" a 'Monoid' --- and one with any function. -foldSL :: (Foldable f, Monoid m) => ArrowState s x m -> ArrowState s (f x) m -foldSL a = ArrowState $ \(s,f) -> foldl a' (s,mempty) f - where a' (s',m) x = second (m <>) $ runArrowState a (s',x) - --- | Fold a fallible state arrow through something 'Foldable'. Collect the --- results in a 'Monoid'. --- Intermediate form of a fold between one with "only" a 'Monoid' --- and one with any function. --- If the iteration fails, the state will be reset to the initial one. -foldS' :: (Foldable f, Monoid m) - => ArrowState s x (Either e m) - -> ArrowState s (f x) (Either e m) -foldS' a = ArrowState $ \(s,f) -> foldr (a' s) (s,Right mempty) f - where a' s x (s',Right m) = case runArrowState a (s',x) of - (s'',Right m') -> (s'', Right (m <> m')) - (_ ,Left e ) -> (s , Left e) - a' _ _ e = e - --- | Fold a fallible state arrow through something 'Foldable'. Collect the --- results in a 'Monoid'. --- Intermediate form of a fold between one with "only" a 'Monoid' --- and one with any function. --- If the iteration fails, the state will be reset to the initial one. -foldSL' :: (Foldable f, Monoid m) - => ArrowState s x (Either e m) - -> ArrowState s (f x) (Either e m) -foldSL' a = ArrowState $ \(s,f) -> foldl (a' s) (s,Right mempty) f - where a' s (s',Right m) x = case runArrowState a (s',x) of - (s'',Right m') -> (s'', Right (m <> m')) - (_ ,Left e ) -> (s , Left e) - a' _ e _ = e - -- | Fold a state arrow through something 'Foldable'. Collect the results in a -- 'MonadPlus'. iterateS :: (Foldable f, MonadPlus m) @@ -239,15 +161,3 @@ iterateS' a = ArrowState $ \(s,f) -> foldr (a' s) (s,Right mzero) f (s'',Right m') -> (s'',Right $ mplus m $ return m') (_ ,Left e ) -> (s ,Left e ) a' _ _ e = e - --- | Fold a fallible state arrow through something 'Foldable'. --- Collect the results in a 'MonadPlus'. --- If the iteration fails, the state will be reset to the initial one. -iterateSL' :: (Foldable f, MonadPlus m) - => ArrowState s x (Either e y ) - -> ArrowState s (f x) (Either e (m y)) -iterateSL' a = ArrowState $ \(s,f) -> foldl (a' s) (s,Right mzero) f - where a' s (s',Right m) x = case runArrowState a (s',x) of - (s'',Right m') -> (s'',Right $ mplus m $ return m') - (_ ,Left e ) -> (s ,Left e ) - a' _ e _ = e diff --git a/src/Text/Pandoc/Readers/Odt/Arrows/Utils.hs b/src/Text/Pandoc/Readers/Odt/Arrows/Utils.hs index 218a85661..ecef8b6e3 100644 --- a/src/Text/Pandoc/Readers/Odt/Arrows/Utils.hs +++ b/src/Text/Pandoc/Readers/Odt/Arrows/Utils.hs @@ -40,10 +40,7 @@ with an equivalent return value. module Text.Pandoc.Readers.Odt.Arrows.Utils where import Control.Arrow -import Control.Monad ( join, MonadPlus(..) ) - -import qualified Data.Foldable as F -import Data.Monoid +import Control.Monad ( join ) import Text.Pandoc.Readers.Odt.Generic.Fallible import Text.Pandoc.Readers.Odt.Generic.Utils @@ -63,12 +60,6 @@ and5 :: (Arrow a) and6 :: (Arrow a) => a b c0->a b c1->a b c2->a b c3->a b c4->a b c5 -> a b (c0,c1,c2,c3,c4,c5 ) -and7 :: (Arrow a) - => a b c0->a b c1->a b c2->a b c3->a b c4->a b c5->a b c6 - -> a b (c0,c1,c2,c3,c4,c5,c6 ) -and8 :: (Arrow a) - => a b c0->a b c1->a b c2->a b c3->a b c4->a b c5->a b c6->a b c7 - -> a b (c0,c1,c2,c3,c4,c5,c6,c7) and3 a b c = (and2 a b ) &&& c >>^ \((z,y ) , x) -> (z,y,x ) @@ -78,10 +69,6 @@ and5 a b c d e = (and4 a b c d ) &&& e >>^ \((z,y,x,w ) , v) -> (z,y,x,w,v ) and6 a b c d e f = (and5 a b c d e ) &&& f >>^ \((z,y,x,w,v ) , u) -> (z,y,x,w,v,u ) -and7 a b c d e f g = (and6 a b c d e f ) &&& g - >>^ \((z,y,x,w,v,u ) , t) -> (z,y,x,w,v,u,t ) -and8 a b c d e f g h = (and7 a b c d e f g) &&& h - >>^ \((z,y,x,w,v,u,t) , s) -> (z,y,x,w,v,u,t,s) liftA2 :: (Arrow a) => (x -> y -> z) -> a b x -> a b y -> a b z liftA2 f a b = a &&& b >>^ uncurry f @@ -98,19 +85,11 @@ liftA5 :: (Arrow a) => (z->y->x->w->v -> r) liftA6 :: (Arrow a) => (z->y->x->w->v->u -> r) -> a b z->a b y->a b x->a b w->a b v->a b u -> a b r -liftA7 :: (Arrow a) => (z->y->x->w->v->u->t -> r) - -> a b z->a b y->a b x->a b w->a b v->a b u->a b t - -> a b r -liftA8 :: (Arrow a) => (z->y->x->w->v->u->t->s -> r) - -> a b z->a b y->a b x->a b w->a b v->a b u->a b t->a b s - -> a b r liftA3 fun a b c = and3 a b c >>^ uncurry3 fun liftA4 fun a b c d = and4 a b c d >>^ uncurry4 fun liftA5 fun a b c d e = and5 a b c d e >>^ uncurry5 fun liftA6 fun a b c d e f = and6 a b c d e f >>^ uncurry6 fun -liftA7 fun a b c d e f g = and7 a b c d e f g >>^ uncurry7 fun -liftA8 fun a b c d e f g h = and8 a b c d e f g h >>^ uncurry8 fun liftA :: (Arrow a) => (y -> z) -> a b y -> a b z liftA fun a = a >>^ fun @@ -124,28 +103,12 @@ liftA fun a = a >>^ fun duplicate :: (Arrow a) => a b (b,b) duplicate = arr $ join (,) --- | Lifts the combination of two values into an arrow. -joinOn :: (Arrow a) => (x -> y -> z) -> a (x,y) z -joinOn = arr.uncurry - -- | Applies a function to the uncurried result-pair of an arrow-application. -- (The %-symbol was chosen to evoke an association with pairs.) (>>%) :: (Arrow a) => a x (b,c) -> (b -> c -> d) -> a x d a >>% f = a >>^ uncurry f --- | '(>>%)' with its arguments flipped -(%<<) :: (Arrow a) => (b -> c -> d) -> a x (b,c) -> a x d -(%<<) = flip (>>%) - --- | Precomposition with an uncurried function -(%>>) :: (Arrow a) => (b -> c -> d) -> a d r -> a (b,c) r -f %>> a = uncurry f ^>> a - --- | Precomposition with an uncurried function (right to left variant) -(<<%) :: (Arrow a) => a d r -> (b -> c -> d) -> a (b,c) r -(<<%) = flip (%>>) - -infixr 2 >>%, %<<, %>>, <<% +infixr 2 >>% -- | Duplicate a value and apply an arrow to the second instance. @@ -156,56 +119,6 @@ infixr 2 >>%, %<<, %>>, <<% keepingTheValue :: (Arrow a) => a b c -> a b (b,c) keepingTheValue a = returnA &&& a --- | Duplicate a value and apply an arrow to the first instance. --- Aequivalent to --- > \a -> duplicate >>> first a --- or --- > \a -> a &&& returnA -keepingTheValue' :: (Arrow a) => a b c -> a b (c,b) -keepingTheValue' a = a &&& returnA - --- | 'bind' from the "Maybe"-Monad lifted into an 'ArrowChoice'. --- Actually, it's the more complex '(>=>)', because 'bind' alone does not --- combine as nicely in arrow form. --- The current implementation is not the most efficient one, because it can --- not return directly if a 'Nothing' is encountered. That in turn follows --- from the type system, as 'Nothing' has an "invisible" type parameter that --- can not be dropped early. --- --- Also, there probably is a way to generalize this to other monads --- or applicatives, but I'm leaving that as an exercise to the reader. --- I have a feeling there is a new Arrow-typeclass to be found that is less --- restrictive than 'ArrowApply'. If it is already out there, --- I have not seen it yet. ('ArrowPlus' for example is not general enough.) -(>>>=) :: (ArrowChoice a) => a x (Maybe b) -> a b (Maybe c) -> a x (Maybe c) -a1 >>>= a2 = a1 >>> maybeToChoice >>> right a2 >>> choiceToMaybe >>^ join - -infixr 2 >>>= - --- | 'mplus' Lifted into an arrow. No 'ArrowPlus' required. --- (But still different from a true bind) -(>++<) :: (Arrow a, MonadPlus m) => a x (m b) -> a x (m b) -> a x (m b) -(>++<) = liftA2 mplus - --- | Left-compose with a pure function -leftLift :: (ArrowChoice a) => (l -> l') -> a (Either l r) (Either l' r) -leftLift = left.arr - --- | Right-compose with a pure function -rightLift :: (ArrowChoice a) => (r -> r') -> a (Either l r) (Either l r') -rightLift = right.arr - - -( ^+++ ) :: (ArrowChoice a) => (b -> c) -> a b' c' -> a (Either b b') (Either c c') -( +++^ ) :: (ArrowChoice a) => a b c -> (b' -> c') -> a (Either b b') (Either c c') -( ^+++^ ) :: (ArrowChoice a) => (b -> c) -> (b' -> c') -> a (Either b b') (Either c c') - -l ^+++ r = leftLift l >>> right r -l +++^ r = left l >>> rightLift r -l ^+++^ r = leftLift l >>> rightLift r - -infixr 2 ^+++, +++^, ^+++^ - ( ^||| ) :: (ArrowChoice a) => (b -> d) -> a c d -> a (Either b c) d ( |||^ ) :: (ArrowChoice a) => a b d -> (c -> d) -> a (Either b c) d ( ^|||^ ) :: (ArrowChoice a) => (b -> d) -> (c -> d) -> a (Either b c) d @@ -218,33 +131,12 @@ infixr 2 ^||| , |||^, ^|||^ ( ^&&& ) :: (Arrow a) => (b -> c) -> a b c' -> a b (c,c') ( &&&^ ) :: (Arrow a) => a b c -> (b -> c') -> a b (c,c') -( ^&&&^ ) :: (Arrow a) => (b -> c) -> (b -> c') -> a b (c,c') l ^&&& r = arr l &&& r l &&&^ r = l &&& arr r -l ^&&&^ r = arr l &&& arr r - -infixr 3 ^&&&, &&&^, ^&&&^ -( ^*** ) :: (Arrow a) => (b -> c) -> a b' c' -> a (b,b') (c,c') -( ***^ ) :: (Arrow a) => a b c -> (b' -> c') -> a (b,b') (c,c') -( ^***^ ) :: (Arrow a) => (b -> c) -> (b' -> c') -> a (b,b') (c,c') +infixr 3 ^&&&, &&&^ -l ^*** r = arr l *** r -l ***^ r = l *** arr r -l ^***^ r = arr l *** arr r - -infixr 3 ^***, ***^, ^***^ - --- | A version of --- --- >>> \p -> arr (\x -> if p x the Right x else Left x) --- --- but with p being an arrow -choose :: (ArrowChoice a) => a b Bool -> a b (Either b b) -choose checkValue = keepingTheValue checkValue >>^ select - where select (x,True ) = Right x - select (x,False ) = Left x -- | Converts @Right a@ into @Just a@ and @Left _@ into @Nothing@. choiceToMaybe :: (ArrowChoice a) => a (Either l r) (Maybe r) @@ -258,130 +150,15 @@ maybeToChoice = arr maybeToEither returnV :: (Arrow a) => c -> a x c returnV = arr.const --- | 'returnA' dropping everything -returnA_ :: (Arrow a) => a _b () -returnA_ = returnV () - --- | Wrapper for an arrow that can be evaluated im parallel. All --- Arrows can be evaluated in parallel, as long as they return a --- monoid. -newtype ParallelArrow a b c = CoEval { evalParallelArrow :: a b c } - deriving (Eq, Ord, Show) - -instance (Arrow a, Monoid m) => Monoid (ParallelArrow a b m) where - mempty = CoEval $ returnV mempty - (CoEval a) `mappend` (CoEval ~b) = CoEval $ a &&& b >>% mappend - --- | Evaluates a collection of arrows in a parallel fashion. --- --- This is in essence a fold of '(&&&)' over the collection, --- so the actual execution order and parallelity depends on the --- implementation of '(&&&)' in the arrow in question. --- The default implementation of '(&&&)' for example keeps the --- order as given in the collection. --- --- This function can be seen as a generalization of --- 'Control.Applicative.sequenceA' to arrows or as an alternative to --- a fold with 'Control.Applicative.WrappedArrow', which --- substitutes the monoid with function application. --- -coEval :: (Arrow a, F.Foldable f, Monoid m) => f (a b m) -> a b m -coEval = evalParallelArrow . (F.foldMap CoEval) - -- | Defines Left as failure, Right as success type FallibleArrow a input failure success = a input (Either failure success) -type ReFallibleArrow a failure success success' - = FallibleArrow a (Either failure success) failure success' - --- | Wrapper for fallible arrows. Fallible arrows are all arrows that return --- an Either value where left is a faliure and right is a success value. -newtype AlternativeArrow a input failure success - = TryArrow { evalAlternativeArrow :: FallibleArrow a input failure success } - - -instance (ArrowChoice a, Monoid failure) - => Monoid (AlternativeArrow a input failure success) where - mempty = TryArrow $ returnV $ Left mempty - (TryArrow a) `mappend` (TryArrow b) - = TryArrow $ a &&& b - >>^ \(a',~b') - -> ( (\a'' -> left (mappend a'') b') ||| Right ) - a' - --- | Evaluates a collection of fallible arrows, trying each one in succession. --- Left values are interpreted as failures, right values as successes. --- --- The evaluation is stopped once an arrow succeeds. --- Up to that point, all failures are collected in the failure-monoid. --- Note that '()' is a monoid, and thus can serve as a failure-collector if --- you are uninterested in the exact failures. --- --- This is in essence a fold of '(&&&)' over the collection, enhanced with a --- little bit of repackaging, so the actual execution order depends on the --- implementation of '(&&&)' in the arrow in question. --- The default implementation of '(&&&)' for example keeps the --- order as given in the collection. --- -tryArrows :: (ArrowChoice a, F.Foldable f, Monoid failure) - => f (FallibleArrow a b failure success) - -> FallibleArrow a b failure success -tryArrows = evalAlternativeArrow . (F.foldMap TryArrow) - --- -liftSuccess :: (ArrowChoice a) - => (success -> success') - -> ReFallibleArrow a failure success success' -liftSuccess = rightLift - -- liftAsSuccess :: (ArrowChoice a) => a x success -> FallibleArrow a x failure success liftAsSuccess a = a >>^ Right --- -asFallibleArrow :: (ArrowChoice a) - => a x success - -> FallibleArrow a x failure success -asFallibleArrow a = a >>^ Right - --- | Raises an error into a 'ReFallibleArrow' if the arrow is already in --- "error mode" -liftError :: (ArrowChoice a, Monoid failure) - => failure - -> ReFallibleArrow a failure success success -liftError e = leftLift (e <>) - --- | Raises an error into a 'FallibleArrow', droping both the arrow input --- and any previously stored error value. -_raiseA :: (ArrowChoice a) - => failure - -> FallibleArrow a x failure success -_raiseA e = returnV (Left e) - --- | Raises an empty error into a 'FallibleArrow', droping both the arrow input --- and any previously stored error value. -_raiseAEmpty :: (ArrowChoice a, Monoid failure) - => FallibleArrow a x failure success -_raiseAEmpty = _raiseA mempty - --- | Raises an error into a 'ReFallibleArrow', possibly appending the new error --- to an existing one -raiseA :: (ArrowChoice a, Monoid failure) - => failure - -> ReFallibleArrow a failure success success -raiseA e = arr $ Left.(either (<> e) (const e)) - --- | Raises an empty error into a 'ReFallibleArrow'. If there already is an --- error, nothing changes. --- (Note that this function is only aequivalent to @raiseA mempty@ iff the --- failure monoid follows the monoid laws.) -raiseAEmpty :: (ArrowChoice a, Monoid failure) - => ReFallibleArrow a failure success success -raiseAEmpty = arr (fromRight (const mempty) >>> Left) - - -- | Execute the second arrow if the first succeeds (>>?) :: (ArrowChoice a) => FallibleArrow a x failure success @@ -410,20 +187,6 @@ a >>?^? b = a >>> Left ^|||^ b -> FallibleArrow a x failure success' a ^>>? b = a ^>> Left ^||| b --- | Execute the lifted second arrow if the lifted first arrow succeeds -(^>>?^) :: (ArrowChoice a) - => (x -> Either failure success) - -> (success -> success') - -> FallibleArrow a x failure success' -a ^>>?^ f = arr $ a >>> right f - --- | Execute the lifted second arrow if the lifted first arrow succeeds -(^>>?^?) :: (ArrowChoice a) - => (x -> Either failure success) - -> (success -> Either failure success') - -> FallibleArrow a x failure success' -a ^>>?^? f = a ^>> Left ^|||^ f - -- | Execute the second, non-fallible arrow if the first arrow succeeds (>>?!) :: (ArrowChoice a) => FallibleArrow a x failure success @@ -453,33 +216,9 @@ a ^>>?% f = arr a >>?^ (uncurry f) a >>?%? f = a >>?^? (uncurry f) infixr 1 >>?, >>?^, >>?^? -infixr 1 ^>>?, ^>>?^, ^>>?^?, >>?! +infixr 1 ^>>?, >>?! infixr 1 >>?%, ^>>?%, >>?%? --- | Keep values that are Right, replace Left values by a constant. -ifFailedUse :: (ArrowChoice a) => v -> a (Either f v) v -ifFailedUse v = arr $ either (const v) id - --- | '(&&)' lifted into an arrow -(<&&>) :: (Arrow a) => a x Bool -> a x Bool -> a x Bool -(<&&>) = liftA2 (&&) - --- | '(||)' lifted into an arrow -(<||>) :: (Arrow a) => a x Bool -> a x Bool -> a x Bool -(<||>) = liftA2 (||) - --- | An equivalent of '(&&)' in a fallible arrow -(>&&<) :: (ArrowChoice a, Monoid f) => FallibleArrow a x f s - -> FallibleArrow a x f s' - -> FallibleArrow a x f (s,s') -(>&&<) = liftA2 chooseMin - --- | An equivalent of '(||)' in some forms of fallible arrows -(>||<) :: (ArrowChoice a, Monoid f, Monoid s) => FallibleArrow a x f s - -> FallibleArrow a x f s - -> FallibleArrow a x f s -(>||<) = liftA2 chooseMax - -- | An arrow version of a short-circuit (<|>) ifFailedDo :: (ArrowChoice a) => FallibleArrow a x f y @@ -489,7 +228,4 @@ ifFailedDo a b = keepingTheValue a >>> repackage ^>> (b |||^ Right) where repackage (x , Left _) = Left x repackage (_ , Right y) = Right y -infixr 4 <&&>, <||>, >&&<, >||< infixr 1 `ifFailedDo` - - diff --git a/src/Text/Pandoc/Readers/Odt/ContentReader.hs b/src/Text/Pandoc/Readers/Odt/ContentReader.hs index a1bd8cb59..777c10df5 100644 --- a/src/Text/Pandoc/Readers/Odt/ContentReader.hs +++ b/src/Text/Pandoc/Readers/Odt/ContentReader.hs @@ -94,8 +94,6 @@ data ReaderState , envMedia :: Media -- | Hold binary resources used in the document , odtMediaBag :: MediaBag --- , sequences --- , trackedChangeIDs } deriving ( Show ) @@ -899,9 +897,6 @@ read_reference_ref = matchingElement NsText "reference-ref" -- Entry point ---------------------- ---read_plain_content :: OdtReaderSafe _x Inlines ---read_plain_content = strContent >>^ text - read_text :: OdtReaderSafe _x Pandoc read_text = matchChildContent' [ read_header , read_paragraph diff --git a/src/Text/Pandoc/Readers/Odt/Generic/Fallible.hs b/src/Text/Pandoc/Readers/Odt/Generic/Fallible.hs index 877443543..4d6a67b8e 100644 --- a/src/Text/Pandoc/Readers/Odt/Generic/Fallible.hs +++ b/src/Text/Pandoc/Readers/Odt/Generic/Fallible.hs @@ -39,10 +39,6 @@ compatible instances of "ArrowChoice". -- We export everything module Text.Pandoc.Readers.Odt.Generic.Fallible where -import Control.Applicative -import Control.Monad - -import qualified Data.Foldable as F import Data.Monoid ((<>)) -- | Default for now. Will probably become a class at some point. @@ -51,16 +47,6 @@ type Failure = () type Fallible a = Either Failure a --- | False -> Left (), True -> Right () -boolToEither :: Bool -> Fallible () -boolToEither False = Left () -boolToEither True = Right () - --- | False -> Left (), True -> Right () -boolToChoice :: Bool -> Fallible () -boolToChoice False = Left () -boolToChoice True = Right () - -- maybeToEither :: Maybe a -> Fallible a maybeToEither (Just a) = Right a @@ -71,21 +57,11 @@ eitherToMaybe :: Either _l a -> Maybe a eitherToMaybe (Left _) = Nothing eitherToMaybe (Right a) = Just a --- | > untagEither === either id id -untagEither :: Either a a -> a -untagEither (Left a) = a -untagEither (Right a) = a - -- | > fromLeft f === either f id fromLeft :: (a -> b) -> Either a b -> b fromLeft f (Left a) = f a fromLeft _ (Right b) = b --- | > fromRight f === either id f -fromRight :: (a -> b) -> Either b a -> b -fromRight _ (Left b) = b -fromRight f (Right a) = f a - -- | > recover a === fromLeft (const a) === either (const a) id recover :: a -> Either _f a -> a recover a (Left _) = a @@ -110,24 +86,6 @@ collapseEither (Left f ) = Left f collapseEither (Right (Left f)) = Left f collapseEither (Right (Right x)) = Right x --- | If either of the values represents an error, the result is a --- (possibly combined) error. If both values represent a success, --- both are returned. -chooseMin :: (Monoid a) => Either a b -> Either a b' -> Either a (b,b') -chooseMin = chooseMinWith (,) - --- | If either of the values represents an error, the result is a --- (possibly combined) error. If both values represent a success, --- a combination is returned. -chooseMinWith :: (Monoid a) => (b -> b' -> c) - -> Either a b - -> Either a b' - -> Either a c -chooseMinWith (><) (Right a) (Right b) = Right $ a >< b -chooseMinWith _ (Left a) (Left b) = Left $ a <> b -chooseMinWith _ (Left a) _ = Left a -chooseMinWith _ _ (Left b) = Left b - -- | If either of the values represents a non-error, the result is a -- (possibly combined) non-error. If both values represent an error, an error -- is returned. @@ -152,87 +110,11 @@ chooseMaxWith _ _ (Right b) = Right b class ChoiceVector v where spreadChoice :: v (Either f a) -> Either f (v a) --- Let's do a few examples first - -instance ChoiceVector Maybe where - spreadChoice (Just (Left f)) = Left f - spreadChoice (Just (Right x)) = Right (Just x) - spreadChoice Nothing = Right Nothing - -instance ChoiceVector (Either l) where - spreadChoice (Right (Left f)) = Left f - spreadChoice (Right (Right x)) = Right (Right x) - spreadChoice (Left x ) = Right (Left x) - instance ChoiceVector ((,) a) where spreadChoice (_, Left f) = Left f spreadChoice (x, Right y) = Right (x,y) -- Wasn't there a newtype somewhere with the elements flipped? --- --- More instances later, first some discussion. --- --- I'll have to freshen up on type system details to see how (or if) to do --- something like --- --- > instance (ChoiceVector a, ChoiceVector b) => ChoiceVector (a b) where --- > : --- --- But maybe it would be even better to use something like --- --- > class ChoiceVector v v' f | v -> v' f where --- > spreadChoice :: v -> Either f v' --- --- That way, more places in @v@ could spread the cheer, e.g.: --- --- As before: --- -- ( a , Either f b) (a , b) f --- > instance ChoiceVector ((,) a (Either f b)) ((,) a b) f where --- > spreadChoice (_, Left f) = Left f --- > spreadChoice (a, Right b) = Right (a,b) --- --- But also: --- -- ( Either f a , b) (a , b) f --- > instance ChoiceVector ((,) (Either f a) b) ((,) a b) f where --- > spreadChoice (Right a,b) = Right (a,b) --- > spreadChoice (Left f,_) = Left f --- --- And maybe even: --- -- ( Either f a , Either f b) (a , b) f --- > instance ChoiceVector ((,) (Either f a) (Either f b)) ((,) a b) f where --- > spreadChoice (Right a , Right b) = Right (a,b) --- > spreadChoice (Left f , _ ) = Left f --- > spreadChoice ( _ , Left f) = Left f --- --- Of course that would lead to a lot of overlapping instances... --- But I can't think of a different way. A selector function might help, --- but not even a "Data.Traversable" is powerful enough for that. --- But maybe someone has already solved all this with a lens library. --- --- Well, it's an interesting academic question. But for practical purposes, --- I have more than enough right now. - -instance ChoiceVector ((,,) a b) where - spreadChoice (_,_, Left f) = Left f - spreadChoice (a,b, Right x) = Right (a,b,x) - -instance ChoiceVector ((,,,) a b c) where - spreadChoice (_,_,_, Left f) = Left f - spreadChoice (a,b,c, Right x) = Right (a,b,c,x) - -instance ChoiceVector ((,,,,) a b c d) where - spreadChoice (_,_,_,_, Left f) = Left f - spreadChoice (a,b,c,d, Right x) = Right (a,b,c,d,x) - -instance ChoiceVector (Const a) where - spreadChoice (Const c) = Right (Const c) -- need to repackage because of implicit types - --- | Fails on the first error -instance ChoiceVector [] where - spreadChoice = sequence -- using the monad instance of Either. - -- Could be generalized to "Data.Traversable" - but why play - -- with UndecidableInstances unless this is really needed. - -- | Wrapper for a list. While the normal list instance of 'ChoiceVector' -- fails whenever it can, this type will never fail. newtype SuccessList a = SuccessList { collectNonFailing :: [a] } @@ -247,14 +129,3 @@ instance ChoiceVector SuccessList where collectRights :: [Either _l r] -> [r] collectRights = collectNonFailing . untag . spreadChoice . SuccessList where untag = fromLeft (error "Unexpected Left") - --- | A version of 'collectRights' generalized to other containers. The --- container must be both "reducible" and "buildable". Most general containers --- should fullfill these requirements, but there is no single typeclass --- (that I know of) for that. --- Therefore, they are split between 'Foldable' and 'MonadPlus'. --- (Note that 'Data.Traversable.Traversable' alone would not be enough, either.) -collectRightsF :: (F.Foldable c, MonadPlus c) => c (Either _l r) -> c r -collectRightsF = F.foldr unTagRight mzero - where unTagRight (Right x) = mplus $ return x - unTagRight _ = id diff --git a/src/Text/Pandoc/Readers/Odt/Generic/Utils.hs b/src/Text/Pandoc/Readers/Odt/Generic/Utils.hs index 6c10ed61d..4af4242b6 100644 --- a/src/Text/Pandoc/Readers/Odt/Generic/Utils.hs +++ b/src/Text/Pandoc/Readers/Odt/Generic/Utils.hs @@ -38,8 +38,6 @@ module Text.Pandoc.Readers.Odt.Generic.Utils , uncurry4 , uncurry5 , uncurry6 -, uncurry7 -, uncurry8 , swap , reverseComposition , bool @@ -148,15 +146,11 @@ uncurry3 :: (a->b->c -> z) -> (a,b,c ) -> z uncurry4 :: (a->b->c->d -> z) -> (a,b,c,d ) -> z uncurry5 :: (a->b->c->d->e -> z) -> (a,b,c,d,e ) -> z uncurry6 :: (a->b->c->d->e->f -> z) -> (a,b,c,d,e,f ) -> z -uncurry7 :: (a->b->c->d->e->f->g -> z) -> (a,b,c,d,e,f,g ) -> z -uncurry8 :: (a->b->c->d->e->f->g->h -> z) -> (a,b,c,d,e,f,g,h) -> z uncurry3 fun (a,b,c ) = fun a b c uncurry4 fun (a,b,c,d ) = fun a b c d uncurry5 fun (a,b,c,d,e ) = fun a b c d e uncurry6 fun (a,b,c,d,e,f ) = fun a b c d e f -uncurry7 fun (a,b,c,d,e,f,g ) = fun a b c d e f g -uncurry8 fun (a,b,c,d,e,f,g,h) = fun a b c d e f g h swap :: (a,b) -> (b,a) swap (a,b) = (b,a) @@ -168,4 +162,3 @@ findBy :: (a -> Maybe b) -> [a] -> Maybe b findBy _ [] = Nothing findBy f ((f -> Just x):_ ) = Just x findBy f ( _:xs) = findBy f xs - diff --git a/src/Text/Pandoc/Readers/Odt/Generic/XMLConverter.hs b/src/Text/Pandoc/Readers/Odt/Generic/XMLConverter.hs index 8c03d1a09..1c3e08a7f 100644 --- a/src/Text/Pandoc/Readers/Odt/Generic/XMLConverter.hs +++ b/src/Text/Pandoc/Readers/Odt/Generic/XMLConverter.hs @@ -41,50 +41,17 @@ module Text.Pandoc.Readers.Odt.Generic.XMLConverter , XMLConverterState , XMLConverter , FallibleXMLConverter -, swapPosition -, runConverter -, runConverter'' , runConverter' -, runConverterF' -, runConverterF -, getCurrentElement , getExtraState , setExtraState , modifyExtraState -, convertingExtraState , producingExtraState -, lookupNSiri -, lookupNSprefix -, readNSattributes -, elemName -, elemNameIs -, strContent -, elContent -, currentElem -, currentElemIs -, expectElement -, elChildren -, findChildren -, filterChildren -, filterChildrenName , findChild' -, findChild -, filterChild' -, filterChild -, filterChildName' -, filterChildName -, isSet , isSet' , isSetWithDefault -, hasAttrValueOf' -, failIfNotAttrValueOf -, isThatTheAttrValue -, searchAttrIn -, searchAttrWith , searchAttr , lookupAttr , lookupAttr' -, lookupAttrWithDefault , lookupDefaultingAttr , findAttr' , findAttr @@ -93,25 +60,9 @@ module Text.Pandoc.Readers.Odt.Generic.XMLConverter , readAttr' , readAttrWithDefault , getAttr --- , (>/<) --- , (?>/<) , executeIn -, collectEvery , withEveryL -, withEvery , tryAll -, tryAll' -, IdXMLConverter -, MaybeEConverter -, ElementMatchConverter -, MaybeCConverter -, ContentMatchConverter -, makeMatcherE -, makeMatcherC -, prepareMatchersE -, prepareMatchersC -, matchChildren -, matchContent'' , matchContent' , matchContent ) where @@ -121,7 +72,6 @@ import Control.Monad ( MonadPlus ) import Control.Arrow import qualified Data.Map as M -import qualified Data.Foldable as F import Data.Default import Data.Maybe @@ -208,17 +158,6 @@ currentElement :: XMLConverterState nsID extraState -> XML.Element currentElement state = head (parentElements state) --- | Replace the current position by another, modifying the extra state --- in the process -swapPosition :: (extraState -> extraState') - -> [XML.Element] - -> XMLConverterState nsID extraState - -> XMLConverterState nsID extraState' -swapPosition f stack state - = state { parentElements = stack - , moreState = f (moreState state) - } - -- | Replace the current position by another, modifying the extra state -- in the process swapStack' :: XMLConverterState nsID extraState @@ -264,14 +203,6 @@ runConverter :: XMLConverter nsID extraState input output -> output runConverter converter state input = snd $ runArrowState converter (state,input) --- -runConverter'' :: (NameSpaceID nsID) - => XMLConverter nsID extraState (Fallible ()) output - -> extraState - -> XML.Element - -> output -runConverter'' converter extraState element = runConverter (readNSattributes >>> converter) (createStartState element extraState) () - runConverter' :: (NameSpaceID nsID) => FallibleXMLConverter nsID extraState () success -> extraState @@ -279,20 +210,6 @@ runConverter' :: (NameSpaceID nsID) -> Fallible success runConverter' converter extraState element = runConverter (readNSattributes >>? converter) (createStartState element extraState) () --- -runConverterF' :: FallibleXMLConverter nsID extraState x y - -> XMLConverterState nsID extraState - -> Fallible x -> Fallible y -runConverterF' a s e = runConverter (returnV e >>? a) s e - --- -runConverterF :: (NameSpaceID nsID) - => FallibleXMLConverter nsID extraState XML.Element x - -> extraState - -> Fallible XML.Element -> Fallible x -runConverterF a s = either failWith - (\e -> runConverter a (createStartState e s) e) - -- getCurrentElement :: XMLConverter nsID extraState x XML.Element getCurrentElement = extractFromState currentElement @@ -429,58 +346,16 @@ elemNameIs nsID name = keepingTheValue (lookupNSiri nsID) >>% hasThatName -- General content -------------------------------------------------------------------------------- --- -strContent :: XMLConverter nsID extraState x String -strContent = getCurrentElement - >>^ XML.strContent - -- elContent :: XMLConverter nsID extraState x [XML.Content] elContent = getCurrentElement >>^ XML.elContent --------------------------------------------------------------------------------- --- Current element --------------------------------------------------------------------------------- - --- -currentElem :: XMLConverter nsID extraState x (XML.QName) -currentElem = getCurrentElement - >>^ XML.elName - -currentElemIs :: (NameSpaceID nsID) - => nsID -> ElementName - -> XMLConverter nsID extraState x Bool -currentElemIs nsID name = getCurrentElement - >>> elemNameIs nsID name - - - -{- -currentElemIs'' nsID name = ( (getCurrentElement >>^ XML.elName >>> - (XML.qName >>^ (&&).(== name) ) - ^&&&^ - (XML.qIRI >>^ (==) ) - ) >>% (.) - ) &&& lookupNSiri nsID >>% ($) --} - --- -expectElement :: (NameSpaceID nsID) - => nsID -> ElementName - -> FallibleXMLConverter nsID extraState x () -expectElement nsID name = currentElemIs nsID name - >>^ boolToChoice - -------------------------------------------------------------------------------- -- Chilren -------------------------------------------------------------------------------- -- -elChildren :: XMLConverter nsID extraState x [XML.Element] -elChildren = getCurrentElement - >>^ XML.elChildren - -- findChildren :: (NameSpaceID nsID) => nsID -> ElementName @@ -489,18 +364,6 @@ findChildren nsID name = elemName nsID name &&& getCurrentElement >>% XML.findChildren --- -filterChildren :: (XML.Element -> Bool) - -> XMLConverter nsID extraState x [XML.Element] -filterChildren p = getCurrentElement - >>^ XML.filterChildren p - --- -filterChildrenName :: (XML.QName -> Bool) - -> XMLConverter nsID extraState x [XML.Element] -filterChildrenName p = getCurrentElement - >>^ XML.filterChildrenName p - -- findChild' :: (NameSpaceID nsID) => nsID @@ -517,44 +380,11 @@ findChild :: (NameSpaceID nsID) findChild nsID name = findChild' nsID name >>> maybeToChoice --- -filterChild' :: (XML.Element -> Bool) - -> XMLConverter nsID extraState x (Maybe XML.Element) -filterChild' p = getCurrentElement - >>^ XML.filterChild p - --- -filterChild :: (XML.Element -> Bool) - -> FallibleXMLConverter nsID extraState x XML.Element -filterChild p = filterChild' p - >>> maybeToChoice - --- -filterChildName' :: (XML.QName -> Bool) - -> XMLConverter nsID extraState x (Maybe XML.Element) -filterChildName' p = getCurrentElement - >>^ XML.filterChildName p - --- -filterChildName :: (XML.QName -> Bool) - -> FallibleXMLConverter nsID extraState x XML.Element -filterChildName p = filterChildName' p - >>> maybeToChoice - -------------------------------------------------------------------------------- -- Attributes -------------------------------------------------------------------------------- --- -isSet :: (NameSpaceID nsID) - => nsID -> AttributeName - -> (Either Failure Bool) - -> FallibleXMLConverter nsID extraState x Bool -isSet nsID attrName deflt - = findAttr' nsID attrName - >>^ maybe deflt stringToBool - -- isSet' :: (NameSpaceID nsID) => nsID -> AttributeName @@ -570,34 +400,6 @@ isSetWithDefault nsID attrName def' = isSet' nsID attrName >>^ fromMaybe def' --- -hasAttrValueOf' :: (NameSpaceID nsID) - => nsID -> AttributeName - -> AttributeValue - -> XMLConverter nsID extraState x Bool -hasAttrValueOf' nsID attrName attrValue - = findAttr nsID attrName - >>> ( const False ^|||^ (==attrValue)) - --- -failIfNotAttrValueOf :: (NameSpaceID nsID) - => nsID -> AttributeName - -> AttributeValue - -> FallibleXMLConverter nsID extraState x () -failIfNotAttrValueOf nsID attrName attrValue - = hasAttrValueOf' nsID attrName attrValue - >>^ boolToChoice - --- | Is the value that is currently transported in the arrow the value of --- the specified attribute? -isThatTheAttrValue :: (NameSpaceID nsID) - => nsID -> AttributeName - -> FallibleXMLConverter nsID extraState AttributeValue Bool -isThatTheAttrValue nsID attrName - = keepingTheValue - (findAttr nsID attrName) - >>% right.(==) - -- | Lookup value in a dictionary, fail if no attribute found or value -- not in dictionary searchAttrIn :: (NameSpaceID nsID) @@ -608,18 +410,6 @@ searchAttrIn nsID attrName dict = findAttr nsID attrName >>?^? maybeToChoice.(`lookup` dict ) - --- | Lookup value in a dictionary. Fail if no attribute found. If value not in --- dictionary, return default value -searchAttrWith :: (NameSpaceID nsID) - => nsID -> AttributeName - -> a - -> [(AttributeValue,a)] - -> FallibleXMLConverter nsID extraState x a -searchAttrWith nsID attrName defV dict - = findAttr nsID attrName - >>?^ (fromMaybe defV).(`lookup` dict ) - -- | Lookup value in a dictionary. If attribute or value not found, -- return default value searchAttr :: (NameSpaceID nsID) @@ -789,16 +579,6 @@ prepareIteration nsID name = keepingTheValue (findChildren nsID name) >>% distributeValue --- | Applies a converter to every child element of a specific type. --- Collects results in a 'Monoid'. --- Fails completely if any conversion fails. -collectEvery :: (NameSpaceID nsID, Monoid m) - => nsID -> ElementName - -> FallibleXMLConverter nsID extraState a m - -> FallibleXMLConverter nsID extraState a m -collectEvery nsID name a = prepareIteration nsID name - >>> foldS' (switchingTheStack a) - -- withEveryL :: (NameSpaceID nsID) => nsID -> ElementName @@ -826,16 +606,6 @@ tryAll nsID name a = prepareIteration nsID name >>> iterateS (switchingTheStack a) >>^ collectRights --- | Applies a converter to every child element of a specific type. --- Collects all successful results. -tryAll' :: (NameSpaceID nsID, F.Foldable c, MonadPlus c) - => nsID -> ElementName - -> FallibleXMLConverter nsID extraState b a - -> XMLConverter nsID extraState b (c a) -tryAll' nsID name a = prepareIteration nsID name - >>> iterateS (switchingTheStack a) - >>^ collectRightsF - -------------------------------------------------------------------------------- -- Matching children -------------------------------------------------------------------------------- @@ -843,15 +613,6 @@ tryAll' nsID name a = prepareIteration nsID name type IdXMLConverter nsID moreState x = XMLConverter nsID moreState x x -type MaybeEConverter nsID moreState x - = Maybe (IdXMLConverter nsID moreState (x, XML.Element)) - --- Chainable converter that helps deciding which converter to actually use. -type ElementMatchConverter nsID extraState x - = IdXMLConverter nsID - extraState - (MaybeEConverter nsID extraState x, XML.Element) - type MaybeCConverter nsID moreState x = Maybe (IdXMLConverter nsID moreState (x, XML.Content)) @@ -861,26 +622,6 @@ type ContentMatchConverter nsID extraState x extraState (MaybeCConverter nsID extraState x, XML.Content) --- Helper function: The @c@ is actually a converter that is to be selected by --- matching XML elements to the first two parameters. --- The fold used to match elements however is very simple, so to use it, --- this function wraps the converter in another converter that unifies --- the accumulator. Think of a lot of converters with the resulting type --- chained together. The accumulator not only transports the element --- unchanged to the next matcher, it also does the actual selecting by --- combining the intermediate results with '(<|>)'. -makeMatcherE :: (NameSpaceID nsID) - => nsID -> ElementName - -> FallibleXMLConverter nsID extraState a a - -> ElementMatchConverter nsID extraState a -makeMatcherE nsID name c = ( second ( - elemNameIs nsID name - >>^ bool Nothing (Just tryC) - ) - >>% (<|>) - ) &&&^ snd - where tryC = (fst ^&&& executeThere c >>% recover) &&&^ snd - -- Helper function: The @c@ is actually a converter that is to be selected by -- matching XML content to the first two parameters. -- The fold used to match elements however is very simple, so to use it, @@ -913,13 +654,6 @@ makeMatcherC nsID name c = ( second ( contentToElem XML.Elem e' -> succeedWith e' _ -> failEmpty --- Creates and chains a bunch of matchers -prepareMatchersE :: (NameSpaceID nsID) - => [(nsID, ElementName, FallibleXMLConverter nsID extraState x x)] - -> ElementMatchConverter nsID extraState x ---prepareMatchersE = foldSs . (map $ uncurry3 makeMatcherE) -prepareMatchersE = reverseComposition . (map $ uncurry3 makeMatcherE) - -- Creates and chains a bunch of matchers prepareMatchersC :: (NameSpaceID nsID) => [(nsID, ElementName, FallibleXMLConverter nsID extraState x x)] @@ -927,52 +661,6 @@ prepareMatchersC :: (NameSpaceID nsID) --prepareMatchersC = foldSs . (map $ uncurry3 makeMatcherC) prepareMatchersC = reverseComposition . (map $ uncurry3 makeMatcherC) --- | Takes a list of element-data - converter groups and --- * Finds all children of the current element --- * Matches each group to each child in order (at most one group per child) --- * Filters non-matched children --- * Chains all found converters in child-order --- * Applies the chain to the input element -matchChildren :: (NameSpaceID nsID) - => [(nsID, ElementName, FallibleXMLConverter nsID extraState a a)] - -> XMLConverter nsID extraState a a -matchChildren lookups = let matcher = prepareMatchersE lookups - in keepingTheValue ( - elChildren - >>> map (Nothing,) - ^>> iterateSL matcher - >>^ catMaybes.map (\(m,e) -> fmap (swallowElem e) m) - -- >>> foldSs - >>> reverseComposition - ) - >>> swap - ^>> app - where - -- let the converter swallow the element and drop the element - -- in the return value - swallowElem element converter = (,element) ^>> converter >>^ fst - --- -matchContent'' :: (NameSpaceID nsID) - => [(nsID, ElementName, FallibleXMLConverter nsID extraState a a)] - -> XMLConverter nsID extraState a a -matchContent'' lookups = let matcher = prepareMatchersC lookups - in keepingTheValue ( - elContent - >>> map (Nothing,) - ^>> iterateSL matcher - >>^ catMaybes.map (\(m,c) -> fmap (swallowContent c) m) - -- >>> foldSs - >>> reverseComposition - ) - >>> swap - ^>> app - where - -- let the converter swallow the content and drop the content - -- in the return value - swallowContent content converter = (,content) ^>> converter >>^ fst - - -- | Takes a list of element-data - converter groups and -- * Finds all content of the current element -- * Matches each group to each piece of content in order @@ -1018,14 +706,6 @@ matchContent lookups fallback -- Internals -------------------------------------------------------------------------------- -stringToBool :: (Monoid failure) => String -> Either failure Bool -stringToBool val -- stringToBool' val >>> maybeToChoice - | val `elem` trueValues = succeedWith True - | val `elem` falseValues = succeedWith False - | otherwise = failEmpty - where trueValues = ["true" ,"on" ,"1"] - falseValues = ["false","off","0"] - stringToBool' :: String -> Maybe Bool stringToBool' val | val `elem` trueValues = Just True | val `elem` falseValues = Just False diff --git a/src/Text/Pandoc/Readers/Odt/StyleReader.hs b/src/Text/Pandoc/Readers/Odt/StyleReader.hs index 26ba6df82..87a6dc91c 100644 --- a/src/Text/Pandoc/Readers/Odt/StyleReader.hs +++ b/src/Text/Pandoc/Readers/Odt/StyleReader.hs @@ -50,23 +50,11 @@ module Text.Pandoc.Readers.Odt.StyleReader , ListLevelType (..) , LengthOrPercent (..) , lookupStyle -, getTextProperty -, getTextProperty' -, getParaProperty -, getListStyle , getListLevelStyle , getStyleFamily -, lookupDefaultStyle , lookupDefaultStyle' , lookupListStyleByName -, getPropertyChain -, textPropertyChain -, stylePropertyChain -, stylePropertyChain' -, getStylePropertyChain , extendedStylePropertyChain -, extendedStylePropertyChain' -, liftStyles , readStylesAt ) where @@ -83,7 +71,6 @@ import Data.Maybe import qualified Text.XML.Light as XML -import Text.Pandoc.Readers.Odt.Arrows.State import Text.Pandoc.Readers.Odt.Arrows.Utils import Text.Pandoc.Readers.Odt.Generic.Utils @@ -623,20 +610,11 @@ chooseMostSpecificListLevelStyle ls | ls == mempty = Nothing lookupStyle :: StyleName -> Styles -> Maybe Style lookupStyle name Styles{..} = M.lookup name stylesByName --- -lookupDefaultStyle :: StyleFamily -> Styles -> StyleProperties -lookupDefaultStyle family Styles{..} = fromMaybe def - (M.lookup family defaultStyleMap) - -- lookupDefaultStyle' :: Styles -> StyleFamily -> StyleProperties lookupDefaultStyle' Styles{..} family = fromMaybe def (M.lookup family defaultStyleMap) --- -getListStyle :: Style -> Styles -> Maybe ListStyle -getListStyle Style{..} styles = listStyle >>= (`lookupListStyleByName` styles) - -- lookupListStyleByName :: StyleName -> Styles -> Maybe ListStyle lookupListStyleByName name Styles{..} = M.lookup name listStylesByName @@ -681,64 +659,3 @@ extendedStylePropertyChain [style] styles = (stylePropertyChain style s ++ (maybeToList (fmap (lookupDefaultStyle' styles) (getStyleFamily style styles))) extendedStylePropertyChain (style:trace) styles = (stylePropertyChain style styles) ++ (extendedStylePropertyChain trace styles) --- Optimizable with Data.Sequence - --- -extendedStylePropertyChain' :: [Style] -> Styles -> Maybe [StyleProperties] -extendedStylePropertyChain' [] _ = Nothing -extendedStylePropertyChain' [style] styles = Just ( - (stylePropertyChain style styles) - ++ (maybeToList (fmap (lookupDefaultStyle' styles) (getStyleFamily style styles))) - ) -extendedStylePropertyChain' (style:trace) styles = fmap ((stylePropertyChain style styles) ++) - (extendedStylePropertyChain' trace styles) - --- -stylePropertyChain' :: Styles -> Style -> [StyleProperties] -stylePropertyChain' = flip stylePropertyChain - --- -getStylePropertyChain :: StyleName -> Styles -> [StyleProperties] -getStylePropertyChain name styles = maybe [] - (`stylePropertyChain` styles) - (lookupStyle name styles) - --- -getPropertyChain :: (StyleProperties -> Maybe a) -> Style -> Styles -> [a] -getPropertyChain extract style styles = catMaybes - $ map extract - $ stylePropertyChain style styles - --- -textPropertyChain :: Style -> Styles -> [TextProperties] -textPropertyChain = getPropertyChain textProperties - --- -paraPropertyChain :: Style -> Styles -> [ParaProperties] -paraPropertyChain = getPropertyChain paraProperties - --- -getTextProperty :: (TextProperties -> a) -> Style -> Styles -> Maybe a -getTextProperty extract style styles = fmap extract - $ listToMaybe - $ textPropertyChain style styles - --- -getTextProperty' :: (TextProperties -> Maybe a) -> Style -> Styles -> Maybe a -getTextProperty' extract style styles = F.asum - $ map extract - $ textPropertyChain style styles - --- -getParaProperty :: (ParaProperties -> a) -> Style -> Styles -> Maybe a -getParaProperty extract style styles = fmap extract - $ listToMaybe - $ paraPropertyChain style styles - --- | Lifts the reader into another readers' state. -liftStyles :: (OdtConverterState s -> OdtConverterState Styles) - -> (OdtConverterState Styles -> OdtConverterState s ) - -> XMLReader s x x -liftStyles extract inject = switchState extract inject - $ convertingExtraState M.empty readAllStyles - -- cgit v1.2.3 From 4b98d0459a8f3486ee4c63149746476e1e6dde80 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Wed, 31 May 2017 20:01:04 +0200 Subject: Org reader: fix module names in haddock comments Copy-pasting had lead to haddock module descriptions containing the wrong module names. --- src/Text/Pandoc/Readers/Org/BlockStarts.hs | 3 +-- src/Text/Pandoc/Readers/Org/Blocks.hs | 2 +- src/Text/Pandoc/Readers/Org/ExportSettings.hs | 4 ++-- src/Text/Pandoc/Readers/Org/Inlines.hs | 2 +- src/Text/Pandoc/Readers/Org/ParserState.hs | 2 +- src/Text/Pandoc/Readers/Org/Parsing.hs | 2 +- src/Text/Pandoc/Readers/Org/Shared.hs | 2 +- 7 files changed, 8 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/BlockStarts.hs b/src/Text/Pandoc/Readers/Org/BlockStarts.hs index 066bde9e0..fb2b52654 100644 --- a/src/Text/Pandoc/Readers/Org/BlockStarts.hs +++ b/src/Text/Pandoc/Readers/Org/BlockStarts.hs @@ -17,7 +17,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | - Module : Text.Pandoc.Readers.Org.Options + Module : Text.Pandoc.Readers.Org.BlockStarts Copyright : Copyright (C) 2014-2017 Albert Krewinkel License : GNU GPL, version 2 or above @@ -139,4 +139,3 @@ endOfBlock = lookAhead . try $ do , void bulletListStart , void orderedListStart ] - diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index 52e990584..88ecbacd3 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ViewPatterns #-} {- | - Module : Text.Pandoc.Readers.Org.Options + Module : Text.Pandoc.Readers.Org.Blocks Copyright : Copyright (C) 2014-2017 Albert Krewinkel License : GNU GPL, version 2 or above diff --git a/src/Text/Pandoc/Readers/Org/ExportSettings.hs b/src/Text/Pandoc/Readers/Org/ExportSettings.hs index 934191e71..c49b5ec07 100644 --- a/src/Text/Pandoc/Readers/Org/ExportSettings.hs +++ b/src/Text/Pandoc/Readers/Org/ExportSettings.hs @@ -17,8 +17,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | - Module : Text.Pandoc.Readers.Org.Options - Copyright : Copyright (C) 2016 Albert Krewinkel + Module : Text.Pandoc.Readers.Org.ExportSettings + Copyright : © 2016–2017 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> diff --git a/src/Text/Pandoc/Readers/Org/Inlines.hs b/src/Text/Pandoc/Readers/Org/Inlines.hs index dcea61222..ad5a1e4de 100644 --- a/src/Text/Pandoc/Readers/Org/Inlines.hs +++ b/src/Text/Pandoc/Readers/Org/Inlines.hs @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | - Module : Text.Pandoc.Readers.Org.Options + Module : Text.Pandoc.Readers.Org.Inlines Copyright : Copyright (C) 2014-2017 Albert Krewinkel License : GNU GPL, version 2 or above diff --git a/src/Text/Pandoc/Readers/Org/ParserState.hs b/src/Text/Pandoc/Readers/Org/ParserState.hs index 1736cd881..adc3b313e 100644 --- a/src/Text/Pandoc/Readers/Org/ParserState.hs +++ b/src/Text/Pandoc/Readers/Org/ParserState.hs @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | - Module : Text.Pandoc.Readers.Org.Options + Module : Text.Pandoc.Readers.Org.ParserState Copyright : Copyright (C) 2014-2017 Albert Krewinkel License : GNU GPL, version 2 or above diff --git a/src/Text/Pandoc/Readers/Org/Parsing.hs b/src/Text/Pandoc/Readers/Org/Parsing.hs index 1d3e8c257..3273c92e4 100644 --- a/src/Text/Pandoc/Readers/Org/Parsing.hs +++ b/src/Text/Pandoc/Readers/Org/Parsing.hs @@ -17,7 +17,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | - Module : Text.Pandoc.Readers.Org.Options + Module : Text.Pandoc.Readers.Org.Parsing Copyright : Copyright (C) 2014-2017 Albert Krewinkel License : GNU GPL, version 2 or above diff --git a/src/Text/Pandoc/Readers/Org/Shared.hs b/src/Text/Pandoc/Readers/Org/Shared.hs index 95424319f..d9414319a 100644 --- a/src/Text/Pandoc/Readers/Org/Shared.hs +++ b/src/Text/Pandoc/Readers/Org/Shared.hs @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | - Module : Text.Pandoc.Readers.Org.Options + Module : Text.Pandoc.Readers.Org.Shared Copyright : Copyright (C) 2014-2017 Albert Krewinkel License : GNU GPL, version 2 or above -- cgit v1.2.3 From 7852cd560398f0da22783b51fe21db4dc3eb0a54 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Wed, 31 May 2017 20:01:04 +0200 Subject: Org reader: recognize babel result blocks with attributes Babel result blocks can have block attributes like captions and names. Result blocks with attributes were not recognized and were parsed as normal blocks without attributes. Fixes: #3706 --- src/Text/Pandoc/Readers/Org/Blocks.hs | 38 +++++++++++++++++------------------ src/Text/Pandoc/Readers/Org/Meta.hs | 6 ++++-- 2 files changed, 22 insertions(+), 22 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index 88ecbacd3..b650721b3 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -122,18 +122,18 @@ attrFromBlockAttributes (BlockAttributes{..}) = kv = filter ((`notElem` ["id", "class"]) . fst) blockAttrKeyValues in (ident, classes, kv) -stringyMetaAttribute :: Monad m => (String -> Bool) -> OrgParser m (String, String) -stringyMetaAttribute attrCheck = try $ do +stringyMetaAttribute :: Monad m => OrgParser m (String, String) +stringyMetaAttribute = try $ do metaLineStart attrName <- map toUpper <$> many1Till nonspaceChar (char ':') - guard $ attrCheck attrName skipSpaces - attrValue <- anyLine + attrValue <- anyLine <|> ("" <$ newline) return (attrName, attrValue) blockAttributes :: PandocMonad m => OrgParser m BlockAttributes blockAttributes = try $ do - kv <- many (stringyMetaAttribute attrCheck) + kv <- many stringyMetaAttribute + guard $ all (attrCheck . fst) kv let caption = foldl' (appendValues "CAPTION") Nothing kv let kvAttrs = foldl' (appendValues "ATTR_HTML") Nothing kv let name = lookup "NAME" kv @@ -150,13 +150,7 @@ blockAttributes = try $ do } where attrCheck :: String -> Bool - attrCheck attr = - case attr of - "NAME" -> True - "LABEL" -> True - "CAPTION" -> True - "ATTR_HTML" -> True - _ -> False + attrCheck x = x `elem` ["NAME", "LABEL", "CAPTION", "ATTR_HTML", "RESULTS"] appendValues :: String -> Maybe String -> (String, String) -> Maybe String appendValues attrName accValue (key, value) = @@ -166,6 +160,7 @@ blockAttributes = try $ do Just acc -> Just $ acc ++ ' ':value Nothing -> Just value +-- | Parse key-value pairs for HTML attributes keyValues :: Monad m => OrgParser m [(String, String)] keyValues = try $ manyTill ((,) <$> key <*> value) newline @@ -301,16 +296,15 @@ codeBlock blockAttrs blockType = do skipSpaces (classes, kv) <- codeHeaderArgs <|> (mempty <$ ignHeaders) content <- rawBlockContent blockType - resultsContent <- trailingResultsBlock + resultsContent <- option mempty babelResultsBlock let id' = fromMaybe mempty $ blockAttrName blockAttrs let codeBlck = B.codeBlockWith ( id', classes, kv ) content let labelledBlck = maybe (pure codeBlck) (labelDiv codeBlck) (blockAttrCaption blockAttrs) - let resultBlck = fromMaybe mempty resultsContent return $ - (if exportsCode kv then labelledBlck else mempty) <> - (if exportsResults kv then resultBlck else mempty) + (if exportsCode kv then labelledBlck else mempty) <> + (if exportsResults kv then resultsContent else mempty) where labelDiv :: Blocks -> F Inlines -> F Blocks labelDiv blk value = @@ -325,12 +319,16 @@ codeBlock blockAttrs blockType = do exportsResults :: [(String, String)] -> Bool exportsResults = maybe False (`elem` ["results", "both"]) . lookup "exports" -trailingResultsBlock :: PandocMonad m => OrgParser m (Maybe (F Blocks)) -trailingResultsBlock = optionMaybe . try $ do +-- | Parse the result of an evaluated babel code block. +babelResultsBlock :: PandocMonad m => OrgParser m (F Blocks) +babelResultsBlock = try $ do blanklines - stringAnyCase "#+RESULTS:" - blankline + resultsMarker <|> + (lookAhead . void . try $ + manyTill (metaLineStart *> anyLineNewline) resultsMarker) block + where + resultsMarker = try . void $ stringAnyCase "#+RESULTS:" *> blankline -- | Parse code block arguments codeHeaderArgs :: Monad m => OrgParser m ([String], [(String, String)]) diff --git a/src/Text/Pandoc/Readers/Org/Meta.hs b/src/Text/Pandoc/Readers/Org/Meta.hs index 5dc742403..33c212bca 100644 --- a/src/Text/Pandoc/Readers/Org/Meta.hs +++ b/src/Text/Pandoc/Readers/Org/Meta.hs @@ -44,7 +44,7 @@ import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Definition -import Control.Monad (mzero, void) +import Control.Monad (mzero, void, when) import Data.Char (toLower) import Data.List (intersperse) import qualified Data.Map as M @@ -75,7 +75,9 @@ declarationLine :: PandocMonad m => OrgParser m () declarationLine = try $ do key <- map toLower <$> metaKey (key', value) <- metaValue key - updateState $ \st -> st { orgStateMeta = B.setMeta key' <$> value <*> orgStateMeta st } + when (key' /= "results") $ + updateState $ \st -> + st { orgStateMeta = B.setMeta key' <$> value <*> orgStateMeta st } metaKey :: Monad m => OrgParser m String metaKey = map toLower <$> many1 (noneOf ": \n\r") -- cgit v1.2.3 From 33a1e4ae1af769eb45b671794da4984bcba25340 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Wed, 31 May 2017 20:43:30 +0200 Subject: Org reader: include tags in headlines The Emacs default is to include tags in the headline when exporting. Instead of just empty spans, which contain the tag name as attribute, tags are rendered as small caps and wrapped in those spans. Non-breaking spaces serve as separators for multiple tags. --- src/Text/Pandoc/Readers/Org/DocumentTree.hs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/DocumentTree.hs b/src/Text/Pandoc/Readers/Org/DocumentTree.hs index 8c2a8482a..66ccd4655 100644 --- a/src/Text/Pandoc/Readers/Org/DocumentTree.hs +++ b/src/Text/Pandoc/Readers/Org/DocumentTree.hs @@ -34,6 +34,7 @@ module Text.Pandoc.Readers.Org.DocumentTree import Control.Monad (guard, void) import Data.Char (toLower, toUpper) +import Data.List ( intersperse ) import Data.Maybe (fromMaybe) import Data.Monoid ((<>)) import Text.Pandoc.Builder (Blocks, Inlines) @@ -224,7 +225,7 @@ headlineToHeader (Headline {..}) = do Just kw -> todoKeywordToInlines kw <> B.space Nothing -> mempty else mempty - let text = tagTitle (todoText <> headlineText) headlineTags + let text = todoText <> headlineText <> tagsToInlines headlineTags let propAttr = propertiesToAttr headlineProperties attr <- registerHeader propAttr headlineText return $ B.headerWith attr headlineLevel text @@ -259,12 +260,21 @@ propertiesToAttr properties = in (id', words cls ++ (if isUnnumbered then ["unnumbered"] else []), kvs') -tagTitle :: Inlines -> [Tag] -> Inlines -tagTitle title tags = title <> (mconcat $ map tagToInline tags) +tagsToInlines :: [Tag] -> Inlines +tagsToInlines [] = mempty +tagsToInlines tags = + (B.space <>) . mconcat . intersperse (B.str "\160") . map tagToInline $ tags + where + tagToInline :: Tag -> Inlines + tagToInline t = tagSpan t . B.smallcaps . B.str $ fromTag t + +-- | Wrap the given inline in a span, marking it as a tag. +tagSpan :: Tag -> Inlines -> Inlines +tagSpan t = B.spanWith ("", ["tag"], [("data-tag-name", fromTag t)]) + + + --- | Convert -tagToInline :: Tag -> Inlines -tagToInline t = B.spanWith ("", ["tag"], [("data-tag-name", fromTag t)]) mempty -- | Read a :PROPERTIES: drawer and return the key/value pairs contained -- within. -- cgit v1.2.3 From e1a066668921e60dd3ca1e1154a5741650294463 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Wed, 31 May 2017 21:26:07 +0200 Subject: Org reader: respect export option for tags Tags are appended to headlines by default, but will be omitted when the `tags` export option is set to nil. Closes: #3713 --- src/Text/Pandoc/Readers/Org/DocumentTree.hs | 6 +++++- src/Text/Pandoc/Readers/Org/ExportSettings.hs | 2 +- src/Text/Pandoc/Readers/Org/ParserState.hs | 2 ++ 3 files changed, 8 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/DocumentTree.hs b/src/Text/Pandoc/Readers/Org/DocumentTree.hs index 66ccd4655..4abbe7be8 100644 --- a/src/Text/Pandoc/Readers/Org/DocumentTree.hs +++ b/src/Text/Pandoc/Readers/Org/DocumentTree.hs @@ -220,12 +220,16 @@ headlineToHeaderWithContents hdln@(Headline {..}) = do headlineToHeader :: Monad m => Headline -> OrgParser m Blocks headlineToHeader (Headline {..}) = do exportTodoKeyword <- getExportSetting exportWithTodoKeywords + exportTags <- getExportSetting exportWithTags let todoText = if exportTodoKeyword then case headlineTodoMarker of Just kw -> todoKeywordToInlines kw <> B.space Nothing -> mempty else mempty - let text = todoText <> headlineText <> tagsToInlines headlineTags + let text = todoText <> headlineText <> + if exportTags + then tagsToInlines headlineTags + else mempty let propAttr = propertiesToAttr headlineProperties attr <- registerHeader propAttr headlineText return $ B.headerWith attr headlineLevel text diff --git a/src/Text/Pandoc/Readers/Org/ExportSettings.hs b/src/Text/Pandoc/Readers/Org/ExportSettings.hs index c49b5ec07..11f0972d5 100644 --- a/src/Text/Pandoc/Readers/Org/ExportSettings.hs +++ b/src/Text/Pandoc/Readers/Org/ExportSettings.hs @@ -71,7 +71,7 @@ exportSetting = choice , ignoredSetting "pri" , ignoredSetting "prop" , ignoredSetting "stat" - , ignoredSetting "tags" + , booleanSetting "tags" (\val es -> es { exportWithTags = val }) , ignoredSetting "tasks" , ignoredSetting "tex" , ignoredSetting "timestamp" diff --git a/src/Text/Pandoc/Readers/Org/ParserState.hs b/src/Text/Pandoc/Readers/Org/ParserState.hs index adc3b313e..4520a5552 100644 --- a/src/Text/Pandoc/Readers/Org/ParserState.hs +++ b/src/Text/Pandoc/Readers/Org/ParserState.hs @@ -240,6 +240,7 @@ data ExportSettings = ExportSettings , exportWithAuthor :: Bool -- ^ Include author in final meta-data , exportWithCreator :: Bool -- ^ Include creator in final meta-data , exportWithEmail :: Bool -- ^ Include email in final meta-data + , exportWithTags :: Bool -- ^ Keep tags as part of headlines , exportWithTodoKeywords :: Bool -- ^ Keep TODO keywords in headers } @@ -258,5 +259,6 @@ defaultExportSettings = ExportSettings , exportWithAuthor = True , exportWithCreator = True , exportWithEmail = True + , exportWithTags = True , exportWithTodoKeywords = True } -- cgit v1.2.3 From 181c56d4003aa83abed23b95a452c4890aa3797c Mon Sep 17 00:00:00 2001 From: Marc Schreiber <marc.schreiber@fh-aachen.de> Date: Thu, 1 Jun 2017 09:09:27 +0200 Subject: Add \colorbox support --- src/Text/Pandoc/Readers/LaTeX.hs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 6b44df468..1d13f7107 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -393,8 +393,9 @@ blockCommands = M.fromList $ , ("graphicspath", graphicsPath) -- hyperlink , ("hypertarget", braced >> grouped block) - -- textcolor - , ("textcolor", blockTextcolor) + -- LaTeX colors + , ("textcolor", coloredBlock "color") + , ("colorbox", coloredBlock "background-color") ] ++ map ignoreBlocks -- these commands will be ignored unless --parse-raw is specified, -- in which case they will appear as raw latex blocks @@ -416,11 +417,11 @@ blockCommands = M.fromList $ , "pagebreak" ] -blockTextcolor :: PandocMonad m => LP m Blocks -blockTextcolor = do +coloredBlock :: PandocMonad m => String -> LP m Blocks +coloredBlock stylename = do skipopts color <- braced - let constructor = divWith ("",[],[("style","color: " ++ color)]) + let constructor = divWith ("",[],[("style",stylename ++ ": " ++ color)]) inlineContents <|> constructor <$> blockContents where inlineContents = do ils <- grouped inline @@ -694,8 +695,9 @@ inlineCommands = M.fromList $ , ("nohyphens", tok) , ("textnhtt", ttfamily) , ("nhttfamily", ttfamily) - -- textcolor - , ("textcolor", inlineTextcolor) + -- LaTeX colors + , ("textcolor", coloredInline "color") + , ("colorbox", coloredInline "background-color") ] ++ map ignoreInlines -- these commands will be ignored unless --parse-raw is specified, -- in which case they will appear as raw latex blocks: @@ -707,11 +709,11 @@ inlineCommands = M.fromList $ , "pagebreak" ] -inlineTextcolor :: PandocMonad m => LP m Inlines -inlineTextcolor = do +coloredInline :: PandocMonad m => String -> LP m Inlines +coloredInline stylename = do skipopts color <- braced - spanWith ("",[],[("style","color: " ++ color)]) <$> tok + spanWith ("",[],[("style",stylename ++ ": " ++ color)]) <$> tok ttfamily :: PandocMonad m => LP m Inlines ttfamily = (code . stringify . toList) <$> tok -- cgit v1.2.3 From 1e7ba5ccd7febff6cd43736109cf62b8ff54eecc Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 1 Jun 2017 11:16:28 +0200 Subject: LaTeX reader: Handle block structure inside table cells. minipage is no longer required. Closes #3709. --- src/Text/Pandoc/Readers/LaTeX.hs | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index d1262867c..bbf9ae9fe 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -276,8 +276,6 @@ block = (mempty <$ comment) <|> blockCommand <|> paragraph <|> grouped block - <|> (mempty <$ char '&') -- loose & in table environment - blocks :: PandocMonad m => LP m Blocks blocks = mconcat <$> many block @@ -1168,12 +1166,12 @@ environments = M.fromList , ("subfigure", env "subfigure" $ skipopts *> tok *> figure) , ("center", env "center" blocks) , ("longtable", env "longtable" $ - resetCaption *> simpTable False >>= addTableCaption) + resetCaption *> simpTable "longtable" False >>= addTableCaption) , ("table", env "table" $ resetCaption *> skipopts *> blocks >>= addTableCaption) - , ("tabular*", env "tabular" $ simpTable True) - , ("tabularx", env "tabularx" $ simpTable True) - , ("tabular", env "tabular" $ simpTable False) + , ("tabular*", env "tabular" $ simpTable "tabular*" True) + , ("tabularx", env "tabularx" $ simpTable "tabularx" True) + , ("tabular", env "tabular" $ simpTable "tabular" False) , ("quote", blockQuote <$> env "quote" blocks) , ("quotation", blockQuote <$> env "quotation" blocks) , ("verse", blockQuote <$> env "verse" blocks) @@ -1489,25 +1487,27 @@ amp :: PandocMonad m => LP m () amp = () <$ try (spaces' *> char '&' <* spaces') parseTableRow :: PandocMonad m - => Int -- ^ number of columns + => String -- ^ table environment name + -> Int -- ^ number of columns -> [String] -- ^ prefixes -> [String] -- ^ suffixes -> LP m [Blocks] -parseTableRow cols prefixes suffixes = try $ do +parseTableRow envname cols prefixes suffixes = try $ do let tableCellRaw = concat <$> many - (do notFollowedBy (amp <|> lbreak <|> (() <$ try (string "\\end"))) + (do notFollowedBy amp + notFollowedBy lbreak + notFollowedBy $ () <$ try (string ("\\end{" ++ envname ++ "}")) many1 (noneOf "&%\n\r\\") <|> try (string "\\&") <|> count 1 anyChar) - let minipage = try $ controlSeq "begin" *> string "{minipage}" *> - env "minipage" - (skipopts *> spaces' *> optional braced *> spaces' *> blocks) - let tableCell = minipage <|> - ((plain . trimInlines . mconcat) <$> many inline) + let plainify bs = case toList bs of + [Para ils] -> plain (fromList ils) + _ -> bs rawcells <- sepBy1 tableCellRaw amp guard $ length rawcells == cols let rawcells' = zipWith3 (\c p s -> p ++ trim c ++ s) rawcells prefixes suffixes + let tableCell = plainify <$> blocks cells' <- mapM (parseFromString' tableCell) rawcells' let numcells = length cells' guard $ numcells <= cols && numcells >= 1 @@ -1520,8 +1520,8 @@ parseTableRow cols prefixes suffixes = try $ do spaces' :: PandocMonad m => LP m () spaces' = spaces *> skipMany (comment *> spaces) -simpTable :: PandocMonad m => Bool -> LP m Blocks -simpTable hasWidthParameter = try $ do +simpTable :: PandocMonad m => String -> Bool -> LP m Blocks +simpTable envname hasWidthParameter = try $ do when hasWidthParameter $ () <$ (spaces' >> tok) skipopts (prefixes, aligns, suffixes) <- unzip3 <$> parseAligns @@ -1531,10 +1531,10 @@ simpTable hasWidthParameter = try $ do spaces' skipMany hline spaces' - header' <- option [] $ try (parseTableRow cols prefixes suffixes <* + header' <- option [] $ try (parseTableRow envname cols prefixes suffixes <* lbreak <* many1 hline) spaces' - rows <- sepEndBy (parseTableRow cols prefixes suffixes) + rows <- sepEndBy (parseTableRow envname cols prefixes suffixes) (lbreak <* optional (skipMany hline)) spaces' optional $ controlSeq "caption" *> skipopts *> setCaption -- cgit v1.2.3 From 58cfac84f01d86d45e31a02bc40ade8c88f5f7b2 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 1 Jun 2017 11:34:17 +0200 Subject: LaTeX reader: Small refactoring of table parsing code. This makes room for doing something with widths. --- src/Text/Pandoc/Readers/LaTeX.hs | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index bbf9ae9fe..087a26f51 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1434,7 +1434,7 @@ complexNatbibCitation mode = try $ do -- tables -parseAligns :: PandocMonad m => LP m [(String, Alignment, String)] +parseAligns :: PandocMonad m => LP m [(Alignment, (String, String))] parseAligns = try $ do bgroup let maybeBar = skipMany $ sp <|> () <$ char '|' <|> () <$ (char '@' >> braced) @@ -1442,12 +1442,13 @@ parseAligns = try $ do let cAlign = AlignCenter <$ char 'c' let lAlign = AlignLeft <$ char 'l' let rAlign = AlignRight <$ char 'r' - let parAlign = AlignLeft <$ (char 'p' >> braced) + let parAlign = AlignLeft <$ char 'p' -- algins from tabularx let xAlign = AlignLeft <$ char 'X' - let mAlign = AlignLeft <$ (char 'm' >> braced) - let bAlign = AlignLeft <$ (char 'b' >> braced) - let alignChar = cAlign <|> lAlign <|> rAlign <|> parAlign <|> xAlign <|> mAlign <|> bAlign + let mAlign = AlignLeft <$ char 'm' + let bAlign = AlignLeft <$ char 'b' + let alignChar = cAlign <|> lAlign <|> rAlign <|> parAlign + <|> xAlign <|> mAlign <|> bAlign let alignPrefix = char '>' >> braced let alignSuffix = char '<' >> braced let alignSpec = do @@ -1455,9 +1456,10 @@ parseAligns = try $ do pref <- option "" alignPrefix spaces ch <- alignChar + _width <- option "" braced -- TODO parse this spaces suff <- option "" alignSuffix - return (pref, ch, suff) + return (ch, (pref, suff)) aligns' <- sepEndBy alignSpec maybeBar spaces egroup @@ -1488,11 +1490,11 @@ amp = () <$ try (spaces' *> char '&' <* spaces') parseTableRow :: PandocMonad m => String -- ^ table environment name - -> Int -- ^ number of columns - -> [String] -- ^ prefixes - -> [String] -- ^ suffixes + -> [(Alignment, (String, String))] -- ^ colspecs -> LP m [Blocks] -parseTableRow envname cols prefixes suffixes = try $ do +parseTableRow envname colspecs = try $ do + let prefsufs = map snd colspecs + let cols = length colspecs let tableCellRaw = concat <$> many (do notFollowedBy amp notFollowedBy lbreak @@ -1505,8 +1507,7 @@ parseTableRow envname cols prefixes suffixes = try $ do _ -> bs rawcells <- sepBy1 tableCellRaw amp guard $ length rawcells == cols - let rawcells' = zipWith3 (\c p s -> p ++ trim c ++ s) - rawcells prefixes suffixes + let rawcells' = zipWith (\c (p, s) -> p ++ trim c ++ s) rawcells prefsufs let tableCell = plainify <$> blocks cells' <- mapM (parseFromString' tableCell) rawcells' let numcells = length cells' @@ -1524,17 +1525,17 @@ simpTable :: PandocMonad m => String -> Bool -> LP m Blocks simpTable envname hasWidthParameter = try $ do when hasWidthParameter $ () <$ (spaces' >> tok) skipopts - (prefixes, aligns, suffixes) <- unzip3 <$> parseAligns - let cols = length aligns + colspecs <- parseAligns + let cols = length colspecs optional $ controlSeq "caption" *> skipopts *> setCaption optional lbreak spaces' skipMany hline spaces' - header' <- option [] $ try (parseTableRow envname cols prefixes suffixes <* + header' <- option [] $ try (parseTableRow envname colspecs <* lbreak <* many1 hline) spaces' - rows <- sepEndBy (parseTableRow envname cols prefixes suffixes) + rows <- sepEndBy (parseTableRow envname colspecs) (lbreak <* optional (skipMany hline)) spaces' optional $ controlSeq "caption" *> skipopts *> setCaption @@ -1544,6 +1545,7 @@ simpTable envname hasWidthParameter = try $ do then replicate cols mempty else header' lookAhead $ controlSeq "end" -- make sure we're at end + let (aligns, _) = unzip colspecs return $ table mempty (zip aligns (repeat 0)) header'' rows removeDoubleQuotes :: String -> String -- cgit v1.2.3 From af6e8414c7d39d80831720d35a6d0d5f6e09bbd5 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 1 Jun 2017 11:56:59 +0200 Subject: LaTeX reader: more table refactoring. --- src/Text/Pandoc/Readers/LaTeX.hs | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 087a26f51..a69c17ebc 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1434,7 +1434,7 @@ complexNatbibCitation mode = try $ do -- tables -parseAligns :: PandocMonad m => LP m [(Alignment, (String, String))] +parseAligns :: PandocMonad m => LP m [(Alignment, Double, (String, String))] parseAligns = try $ do bgroup let maybeBar = skipMany $ sp <|> () <$ char '|' <|> () <$ (char '@' >> braced) @@ -1455,11 +1455,13 @@ parseAligns = try $ do spaces pref <- option "" alignPrefix spaces - ch <- alignChar - _width <- option "" braced -- TODO parse this + al <- alignChar + let parseWidth :: String -> Double + parseWidth _ = 0.00 -- TODO actually parse the width + width <- parseWidth <$> option "" braced spaces suff <- option "" alignSuffix - return (ch, (pref, suff)) + return (al, width, (pref, suff)) aligns' <- sepEndBy alignSpec maybeBar spaces egroup @@ -1490,11 +1492,10 @@ amp = () <$ try (spaces' *> char '&' <* spaces') parseTableRow :: PandocMonad m => String -- ^ table environment name - -> [(Alignment, (String, String))] -- ^ colspecs + -> [(String, String)] -- ^ pref/suffixes -> LP m [Blocks] -parseTableRow envname colspecs = try $ do - let prefsufs = map snd colspecs - let cols = length colspecs +parseTableRow envname prefsufs = try $ do + let cols = length prefsufs let tableCellRaw = concat <$> many (do notFollowedBy amp notFollowedBy lbreak @@ -1526,16 +1527,17 @@ simpTable envname hasWidthParameter = try $ do when hasWidthParameter $ () <$ (spaces' >> tok) skipopts colspecs <- parseAligns + let (aligns, widths, prefsufs) = unzip3 colspecs let cols = length colspecs optional $ controlSeq "caption" *> skipopts *> setCaption optional lbreak spaces' skipMany hline spaces' - header' <- option [] $ try (parseTableRow envname colspecs <* + header' <- option [] $ try (parseTableRow envname prefsufs <* lbreak <* many1 hline) spaces' - rows <- sepEndBy (parseTableRow envname colspecs) + rows <- sepEndBy (parseTableRow envname prefsufs) (lbreak <* optional (skipMany hline)) spaces' optional $ controlSeq "caption" *> skipopts *> setCaption @@ -1545,8 +1547,7 @@ simpTable envname hasWidthParameter = try $ do then replicate cols mempty else header' lookAhead $ controlSeq "end" -- make sure we're at end - let (aligns, _) = unzip colspecs - return $ table mempty (zip aligns (repeat 0)) header'' rows + return $ table mempty (zip aligns widths) header'' rows removeDoubleQuotes :: String -> String removeDoubleQuotes ('"':xs) = -- cgit v1.2.3 From 9396f1fb6766cc4b08fb7b7c97ef2c02e9f0f700 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 1 Jun 2017 12:08:28 +0200 Subject: LaTeX reader: handle some width specifiers on table columns. Currently we only handle the form `0.9\linewidth`. Anything else would have to be converted to a percentage, using some kind arbitrary assumptions about line widths. See #3709. --- src/Text/Pandoc/Readers/LaTeX.hs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index a69c17ebc..b65ae15ad 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1451,14 +1451,24 @@ parseAligns = try $ do <|> xAlign <|> mAlign <|> bAlign let alignPrefix = char '>' >> braced let alignSuffix = char '<' >> braced + let colWidth = try $ do + char '{' + ds <- many1 (oneOf "0123456789.") + spaces + string "\\linewidth" + char '}' + case safeRead ds of + Just w -> return w + Nothing -> return 0.0 let alignSpec = do spaces pref <- option "" alignPrefix spaces al <- alignChar - let parseWidth :: String -> Double - parseWidth _ = 0.00 -- TODO actually parse the width - width <- parseWidth <$> option "" braced + width <- colWidth <|> option 0.0 (do s <- braced + pos <- getPosition + report $ SkippedContent s pos + return 0.0) spaces suff <- option "" alignSuffix return (al, width, (pref, suff)) -- cgit v1.2.3 From c366fab2cba3238a4d262fefdfe03d8acf813cf1 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 1 Jun 2017 12:30:58 +0200 Subject: Markdown writer: Avoid inline surround-marking with empty content. E.g. we don't want `<strong></strong>` to become `****`. Similarly for emphasis, super/subscript, strikeout. Closes #3715. --- src/Text/Pandoc/Writers/Markdown.hs | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index efdf3852b..989d5af9d 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -931,12 +931,14 @@ inlineToMarkdown opts (Span attrs ils) = do isEnabled Ext_native_spans opts -> tagWithAttrs "span" attrs <> contents <> text "</span>" | otherwise -> contents +inlineToMarkdown _ (Emph []) = return empty inlineToMarkdown opts (Emph lst) = do plain <- asks envPlain contents <- inlineListToMarkdown opts lst return $ if plain then "_" <> contents <> "_" else "*" <> contents <> "*" +inlineToMarkdown _ (Strong []) = return empty inlineToMarkdown opts (Strong lst) = do plain <- asks envPlain if plain @@ -944,6 +946,7 @@ inlineToMarkdown opts (Strong lst) = do else do contents <- inlineListToMarkdown opts lst return $ "**" <> contents <> "**" +inlineToMarkdown _ (Strikeout []) = return empty inlineToMarkdown opts (Strikeout lst) = do contents <- inlineListToMarkdown opts lst return $ if isEnabled Ext_strikeout opts @@ -951,6 +954,7 @@ inlineToMarkdown opts (Strikeout lst) = do else if isEnabled Ext_raw_html opts then "<s>" <> contents <> "</s>" else contents +inlineToMarkdown _ (Superscript []) = return empty inlineToMarkdown opts (Superscript lst) = local (\env -> env {envEscapeSpaces = True}) $ do contents <- inlineListToMarkdown opts lst @@ -963,6 +967,7 @@ inlineToMarkdown opts (Superscript lst) = in case mapM toSuperscript rendered of Just r -> text r Nothing -> text $ "^(" ++ rendered ++ ")" +inlineToMarkdown _ (Subscript []) = return empty inlineToMarkdown opts (Subscript lst) = local (\env -> env {envEscapeSpaces = True}) $ do contents <- inlineListToMarkdown opts lst -- cgit v1.2.3 From 00d8585d8f6cfafea536c59912f3de9d53ef3193 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 1 Jun 2017 14:14:42 +0200 Subject: Trivial renaming. --- src/Text/Pandoc/App.hs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 845146f34..d8409a00f 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -1423,8 +1423,8 @@ options = map ("--" ++) longs let allopts = unwords (concatMap optnames options) UTF8.hPutStrLn stdout $ printf tpl allopts - (unwords readers'names) - (unwords writers'names) + (unwords readersNames) + (unwords writersNames) (unwords $ map fst highlightingStyles) ddir exitSuccess )) @@ -1433,14 +1433,14 @@ options = , Option "" ["list-input-formats"] (NoArg (\_ -> do - mapM_ (UTF8.hPutStrLn stdout) readers'names + mapM_ (UTF8.hPutStrLn stdout) readersNames exitSuccess )) "" , Option "" ["list-output-formats"] (NoArg (\_ -> do - mapM_ (UTF8.hPutStrLn stdout) writers'names + mapM_ (UTF8.hPutStrLn stdout) writersNames exitSuccess )) "" @@ -1544,11 +1544,11 @@ uppercaseFirstLetter :: String -> String uppercaseFirstLetter (c:cs) = toUpper c : cs uppercaseFirstLetter [] = [] -readers'names :: [String] -readers'names = sort (map fst (readers :: [(String, Reader PandocIO)])) +readersNames :: [String] +readersNames = sort (map fst (readers :: [(String, Reader PandocIO)])) -writers'names :: [String] -writers'names = sort (map fst (writers :: [(String, Writer PandocIO)])) +writersNames :: [String] +writersNames = sort (map fst (writers :: [(String, Writer PandocIO)])) splitField :: String -> (String, String) splitField s = -- cgit v1.2.3 From c2eb7d085743b8a78d4580d5a07baa899fa9b64e Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 1 Jun 2017 14:16:17 +0200 Subject: Use isNothing. --- src/Text/Pandoc/App.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index d8409a00f..bc1d4ce18 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -413,8 +413,8 @@ convertWithOpts opts = do mconcat <$> mapM (readFile' >=> r readerOpts) sources metadata <- if format == "jats" && - lookup "csl" (optMetadata opts) == Nothing && - lookup "citation-style" (optMetadata opts) == Nothing + isNothing (lookup "csl" (optMetadata opts)) && + isNothing (lookup "citation-style" (optMetadata opts)) then do jatsCSL <- readDataFile datadir "jats.csl" let jatsEncoded = makeDataURI ("application/xml", jatsCSL) -- cgit v1.2.3 From b1a9b567aac2b725669dacbc0fe524d0724dec35 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 1 Jun 2017 14:19:43 +0200 Subject: Trivial reformatting. --- src/Text/Pandoc/Class.hs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 8b2adc507..f47efb2aa 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -1,6 +1,10 @@ -{-# LANGUAGE DeriveFunctor, DeriveDataTypeable, TypeSynonymInstances, -FlexibleInstances, GeneralizedNewtypeDeriving, FlexibleContexts, -StandaloneDeriving #-} +{-# LANGUAGE DeriveFunctor #-} +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE TypeSynonymInstances #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE StandaloneDeriving #-} {- Copyright (C) 2016 Jesse Rosenthal <jrosenthal@jhu.edu> -- cgit v1.2.3 From 0cf6511f16388fc2bb71cffc733a704d20cfe3e3 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 1 Jun 2017 15:09:38 +0200 Subject: Some hlint refactoring. --- src/Text/Pandoc/App.hs | 37 ++++++++++++++++++------------------- src/Text/Pandoc/Class.hs | 29 +++++++++++++---------------- 2 files changed, 31 insertions(+), 35 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index bc1d4ce18..58044860b 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -1,7 +1,7 @@ {-# LANGUAGE CPP #-} +{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} -{-# LANGUAGE DeriveGeneric #-} {- Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> @@ -40,47 +40,47 @@ module Text.Pandoc.App ( ) where import Control.Applicative ((<|>)) import qualified Control.Exception as E -import Control.Monad.Except (throwError) import Control.Monad +import Control.Monad.Except (throwError) import Control.Monad.Trans -import Data.Aeson (eitherDecode', encode, ToJSON(..), FromJSON(..), - genericToEncoding, defaultOptions) +import Data.Aeson (FromJSON (..), ToJSON (..), defaultOptions, eitherDecode', + encode, genericToEncoding) import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as B import Data.Char (toLower, toUpper) -import qualified Data.Set as Set import Data.Foldable (foldrM) -import GHC.Generics import Data.List (intercalate, isPrefixOf, isSuffixOf, sort) import qualified Data.Map as M import Data.Maybe (fromMaybe, isJust, isNothing) +import qualified Data.Set as Set import qualified Data.Text as T import Data.Yaml (decode) import qualified Data.Yaml as Yaml +import GHC.Generics import Network.URI (URI (..), parseURI) import Paths_pandoc (getDataDir) import Skylighting (Style, Syntax (..), defaultSyntaxMap, parseTheme) -import Skylighting.Parser (missingIncludes, parseSyntaxDefinition, - addSyntaxDefinition) +import Skylighting.Parser (addSyntaxDefinition, missingIncludes, + parseSyntaxDefinition) import System.Console.GetOpt import System.Directory (Permissions (..), doesFileExist, findExecutable, getAppUserDataDirectory, getPermissions) import System.Environment (getArgs, getEnvironment, getProgName) import System.Exit (ExitCode (..), exitSuccess) import System.FilePath -import System.IO (stdout, nativeNewline) -import qualified System.IO as IO (Newline(..)) +import System.IO (nativeNewline, stdout) +import qualified System.IO as IO (Newline (..)) import System.IO.Error (isDoesNotExistError) import Text.Pandoc import Text.Pandoc.Builder (setMeta) -import Text.Pandoc.Class (PandocIO, getLog, withMediaBag, - extractMedia, fillMediaBag, setResourcePath) +import Text.Pandoc.Class (PandocIO, extractMedia, fillMediaBag, getLog, + setResourcePath, withMediaBag) import Text.Pandoc.Highlighting (highlightingStyles) -import Text.Pandoc.Lua ( runLuaFilter ) +import Text.Pandoc.Lua (runLuaFilter) import Text.Pandoc.PDF (makePDF) import Text.Pandoc.Process (pipeProcess) -import Text.Pandoc.SelfContained (makeSelfContained, makeDataURI) -import Text.Pandoc.Shared (isURI, headerShift, openURL, readDataFile, +import Text.Pandoc.SelfContained (makeDataURI, makeSelfContained) +import Text.Pandoc.Shared (headerShift, isURI, openURL, readDataFile, readDataFileUTF8, safeRead, tabFilter) import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.XML (toEntities) @@ -243,10 +243,9 @@ convertWithOpts opts = do withList f (x:xs) vars = f x vars >>= withList f xs variables <- - return (("outputfile", optOutputFile opts) : optVariables opts) - >>= + withList (addStringAsVariable "sourcefile") - (reverse $ optInputFiles opts) + (reverse $ optInputFiles opts) (("outputfile", optOutputFile opts) : optVariables opts) -- we reverse this list because, unlike -- the other option lists here, it is -- not reversed when parsed from CLI arguments. @@ -796,7 +795,7 @@ readURI :: FilePath -> PandocIO String readURI src = do res <- liftIO $ openURL src case res of - Left e -> throwError $ PandocHttpError src e + Left e -> throwError $ PandocHttpError src e Right (contents, _) -> return $ UTF8.toString contents readFile' :: MonadIO m => FilePath -> m B.ByteString diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index f47efb2aa..49b20bd30 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -1,10 +1,8 @@ {-# LANGUAGE DeriveFunctor #-} -{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE StandaloneDeriving #-} {- Copyright (C) 2016 Jesse Rosenthal <jrosenthal@jhu.edu> @@ -156,7 +154,7 @@ report :: PandocMonad m => LogMessage -> m () report msg = do verbosity <- getsCommonState stVerbosity let level = messageVerbosity msg - when (level <= verbosity) $ do + when (level <= verbosity) $ logOutput msg unless (level == DEBUG) $ modifyCommonState $ \st -> st{ stLog = msg : stLog st } @@ -224,7 +222,7 @@ runIO :: PandocIO a -> IO (Either PandocError a) runIO ma = flip evalStateT def $ runExceptT $ unPandocIO ma withMediaBag :: PandocMonad m => m a -> m (a, MediaBag) -withMediaBag ma = ((,)) <$> ma <*> getMediaBag +withMediaBag ma = (,) <$> ma <*> getMediaBag runIOorExplode :: PandocIO a -> IO a runIOorExplode ma = runIO ma >>= handleError @@ -250,7 +248,7 @@ instance PandocMonad PandocIO where getCurrentTime = liftIO IO.getCurrentTime getCurrentTimeZone = liftIO IO.getCurrentTimeZone newStdGen = liftIO IO.newStdGen - newUniqueHash = hashUnique <$> (liftIO IO.newUnique) + newUniqueHash = hashUnique <$> liftIO IO.newUnique openURL u = do report $ Fetching u res <- liftIO (IO.openURL u) @@ -266,7 +264,7 @@ instance PandocMonad PandocIO where putCommonState x = PandocIO $ lift $ put x logOutput msg = liftIO $ do UTF8.hPutStr stderr $ "[" ++ - (map toLower $ show (messageVerbosity msg)) ++ "] " + map toLower (show (messageVerbosity msg)) ++ "] " alertIndent $ lines $ showLogMessage msg alertIndent :: [String] -> IO () @@ -297,14 +295,14 @@ fetchItem :: PandocMonad m fetchItem sourceURL s = do mediabag <- getMediaBag case lookupMedia s mediabag of - Just (mime, bs) -> return $ (BL.toStrict bs, Just mime) + 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 +downloadOrRead sourceURL s = case (sourceURL >>= parseURIReference' . ensureEscaped, ensureEscaped s) of (Just u, s') -> -- try fetching from relative path at source @@ -367,7 +365,7 @@ fillMediaBag sourceURL d = walkM handleImage d let fname = basename <.> ext insertMedia fname mt bs' return $ Image attr lab (fname, tit)) - (\e -> do + (\e -> case e of PandocResourceNotFound _ -> do report $ CouldNotFetchResource src @@ -434,7 +432,7 @@ instance Default PureState where getPureState :: PandocPure PureState -getPureState = PandocPure $ lift $ lift $ get +getPureState = PandocPure $ lift $ lift get getsPureState :: (PureState -> a) -> PandocPure a getsPureState f = f <$> getPureState @@ -505,16 +503,16 @@ instance PandocMonad PandocPure where case infoFileContents <$> getFileInfo fp fps of Just bs -> return bs Nothing -> throwError $ PandocResourceNotFound fp - readDataFile Nothing "reference.docx" = do + readDataFile Nothing "reference.docx" = (B.concat . BL.toChunks . fromArchive) <$> getsPureState stReferenceDocx - readDataFile Nothing "reference.odt" = do + readDataFile Nothing "reference.odt" = (B.concat . BL.toChunks . fromArchive) <$> getsPureState stReferenceODT readDataFile Nothing fname = do let fname' = if fname == "MANUAL.txt" then fname else "data" </> fname readFileStrict fname' readDataFile (Just userDir) fname = do userDirFiles <- getsPureState stUserDataDir - case infoFileContents <$> (getFileInfo (userDir </> fname) userDirFiles) of + case infoFileContents <$> getFileInfo (userDir </> fname) userDirFiles of Just bs -> return bs Nothing -> readDataFile Nothing fname @@ -524,12 +522,12 @@ instance PandocMonad PandocPure where getModificationTime fp = do fps <- getsPureState stFiles - case infoFileMTime <$> (getFileInfo fp fps) of + case infoFileMTime <$> getFileInfo fp fps of Just tm -> return tm Nothing -> throwError $ PandocIOError fp (userError "Can't get modification time") - getCommonState = PandocPure $ lift $ get + getCommonState = PandocPure $ lift get putCommonState x = PandocPure $ lift $ put x logOutput _msg = return () @@ -613,4 +611,3 @@ instance PandocMonad m => PandocMonad (StateT st m) where getCommonState = lift getCommonState putCommonState = lift . putCommonState logOutput = lift . logOutput - -- cgit v1.2.3 From 8218bdb95c070b2c09f51ae29c280260fc47dffb Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 1 Jun 2017 18:41:54 +0200 Subject: HTML writer: Avoid two class attributes when adding 'uri' class. Closes #3716. --- src/Text/Pandoc/Writers/HTML.hs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 030f332ca..2605a29aa 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -973,7 +973,7 @@ inlineToHtml opts inline = do (Link attr txt (s,_)) | "mailto:" `isPrefixOf` s -> do linkText <- inlineListToHtml opts txt lift $ obfuscateLink opts attr linkText s - (Link attr txt (s,tit)) -> do + (Link (ident,classes,kvs) txt (s,tit)) -> do linkText <- inlineListToHtml opts txt slideVariant <- gets stSlideVariant let s' = case s of @@ -983,13 +983,13 @@ inlineToHtml opts inline = do in '#' : prefix ++ xs _ -> s let link = H.a ! A.href (toValue s') $ linkText - let link' = if txt == [Str (unEscapeString s)] - then link ! A.class_ "uri" - else link - let link'' = addAttrs opts attr link' + let attr = if txt == [Str (unEscapeString s)] + then (ident, "uri" : classes, kvs) + else (ident, classes, kvs) + let link' = addAttrs opts attr link return $ if null tit - then link'' - else link'' ! A.title (toValue tit) + then link' + else link' ! A.title (toValue tit) (Image attr txt (s,tit)) | treatAsImage s -> do let alternate' = stringify txt slideVariant <- gets stSlideVariant -- cgit v1.2.3 From 0f07404daf08156fac655f0ff907a6126c639450 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 1 Jun 2017 18:59:21 +0200 Subject: HTML reader: Removed "button" from block tag list. It is already in the eitherBlockOrInlineTag list, and should be both places. Closes #3717. Note: the result of this change is that there will be p tags around the whole paragraph. That is the right result, because the `button` tags are treated as inline HTML here, and the whole chunk of text is a Markdown paragraph. --- src/Text/Pandoc/Readers/HTML.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 23af6171e..095382ae0 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -886,7 +886,7 @@ inlineHtmlTags = ["a", "abbr", "acronym", "b", "basefont", "bdo", "big", blockHtmlTags :: [String] blockHtmlTags = ["?xml", "!DOCTYPE", "address", "article", "aside", - "blockquote", "body", "button", "canvas", + "blockquote", "body", "canvas", "caption", "center", "col", "colgroup", "dd", "details", "dir", "div", "dl", "dt", "fieldset", "figcaption", "figure", -- cgit v1.2.3 From eb6fb62e55656de796618203a15c87c43458e923 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 1 Jun 2017 19:15:00 +0200 Subject: HTML reader: Use sets instead of lists for block tag lookup. --- src/Text/Pandoc/Readers/HTML.hs | 93 +++++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 50 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 095382ae0..218ce3f5a 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -869,62 +869,54 @@ pSpace = many1 (satisfy isSpace) >>= \xs -> -- Constants -- -eitherBlockOrInline :: [String] -eitherBlockOrInline = ["audio", "applet", "button", "iframe", "embed", - "del", "ins", - "progress", "map", "area", "noscript", "script", - "object", "svg", "video", "source"] - -{- -inlineHtmlTags :: [[Char]] -inlineHtmlTags = ["a", "abbr", "acronym", "b", "basefont", "bdo", "big", - "br", "cite", "code", "dfn", "em", "font", "i", "img", - "input", "kbd", "label", "q", "s", "samp", "select", - "small", "span", "strike", "strong", "sub", "sup", - "textarea", "tt", "u", "var"] --} - -blockHtmlTags :: [String] -blockHtmlTags = ["?xml", "!DOCTYPE", "address", "article", "aside", - "blockquote", "body", "canvas", - "caption", "center", "col", "colgroup", "dd", "details", - "dir", "div", - "dl", "dt", "fieldset", "figcaption", "figure", - "footer", "form", "h1", "h2", "h3", "h4", - "h5", "h6", "head", "header", "hgroup", "hr", "html", - "isindex", "menu", "noframes", "ol", "output", "p", "pre", - "section", "table", "tbody", "textarea", - "thead", "tfoot", "ul", "dd", - "dt", "frameset", "li", "tbody", "td", "tfoot", - "th", "thead", "tr", "script", "style"] +eitherBlockOrInline :: Set.Set String +eitherBlockOrInline = Set.fromList + ["audio", "applet", "button", "iframe", "embed", + "del", "ins", "progress", "map", "area", "noscript", "script", + "object", "svg", "video", "source"] + +blockHtmlTags :: Set.Set String +blockHtmlTags = Set.fromList + ["?xml", "!DOCTYPE", "address", "article", "aside", + "blockquote", "body", "canvas", + "caption", "center", "col", "colgroup", "dd", "details", + "dir", "div", "dl", "dt", "fieldset", "figcaption", "figure", + "footer", "form", "h1", "h2", "h3", "h4", + "h5", "h6", "head", "header", "hgroup", "hr", "html", + "isindex", "menu", "noframes", "ol", "output", "p", "pre", + "section", "table", "tbody", "textarea", + "thead", "tfoot", "ul", "dd", + "dt", "frameset", "li", "tbody", "td", "tfoot", + "th", "thead", "tr", "script", "style"] -- We want to allow raw docbook in markdown documents, so we -- include docbook block tags here too. -blockDocBookTags :: [String] -blockDocBookTags = ["calloutlist", "bibliolist", "glosslist", "itemizedlist", - "orderedlist", "segmentedlist", "simplelist", - "variablelist", "caution", "important", "note", "tip", - "warning", "address", "literallayout", "programlisting", - "programlistingco", "screen", "screenco", "screenshot", - "synopsis", "example", "informalexample", "figure", - "informalfigure", "table", "informaltable", "para", - "simpara", "formalpara", "equation", "informalequation", - "figure", "screenshot", "mediaobject", "qandaset", - "procedure", "task", "cmdsynopsis", "funcsynopsis", - "classsynopsis", "blockquote", "epigraph", "msgset", - "sidebar", "title"] - -epubTags :: [String] -epubTags = ["case", "switch", "default"] - -blockTags :: [String] -blockTags = blockHtmlTags ++ blockDocBookTags ++ epubTags +blockDocBookTags :: Set.Set String +blockDocBookTags = Set.fromList + ["calloutlist", "bibliolist", "glosslist", "itemizedlist", + "orderedlist", "segmentedlist", "simplelist", + "variablelist", "caution", "important", "note", "tip", + "warning", "address", "literallayout", "programlisting", + "programlistingco", "screen", "screenco", "screenshot", + "synopsis", "example", "informalexample", "figure", + "informalfigure", "table", "informaltable", "para", + "simpara", "formalpara", "equation", "informalequation", + "figure", "screenshot", "mediaobject", "qandaset", + "procedure", "task", "cmdsynopsis", "funcsynopsis", + "classsynopsis", "blockquote", "epigraph", "msgset", + "sidebar", "title"] + +epubTags :: Set.Set String +epubTags = Set.fromList ["case", "switch", "default"] + +blockTags :: Set.Set String +blockTags = Set.unions [blockHtmlTags, blockDocBookTags, epubTags] isInlineTag :: Tag String -> Bool isInlineTag t = tagOpen isInlineTagName (const True) t || tagClose isInlineTagName t || tagComment (const True) t - where isInlineTagName x = x `notElem` blockTags + where isInlineTagName x = x `Set.notMember` blockTags isBlockTag :: Tag String -> Bool isBlockTag t = tagOpen isBlockTagName (const True) t || @@ -976,8 +968,9 @@ t `closes` t2 | t `elem` ["h1","h2","h3","h4","h5","h6","dl","ol","ul","table","div","p"] && t2 `elem` ["h1","h2","h3","h4","h5","h6","p" ] = True -- not "div" t1 `closes` t2 | - t1 `elem` blockTags && - t2 `notElem` (blockTags ++ eitherBlockOrInline) = True + t1 `Set.member` blockTags && + t2 `Set.notMember` blockTags && + t2 `Set.notMember` eitherBlockOrInline = True _ `closes` _ = False --- parsers for use in markdown, textile readers -- cgit v1.2.3 From b2fe1015d9d92cf001018781f2e047e637ed9a54 Mon Sep 17 00:00:00 2001 From: Ian <iandol@users.noreply.github.com> Date: Fri, 2 Jun 2017 10:47:30 +0800 Subject: Add keywords metadata to docx document properties Hi, I don't know haskell so possibly this is wrong, but DOCX stores keywords in cp:keywords in core.xml, and this should be easy to add from the pandoc metadata (I copy and paste the author code). As far as I can tell (no clear documentation, just a few refs), keywords should be separated with a comma. --- src/Text/Pandoc/Writers/Docx.hs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 5b714ba41..c68e90049 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -505,6 +505,7 @@ writeDocx opts doc@(Pandoc meta _) = do ,("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance")] $ mknode "dc:title" [] (stringify $ docTitle meta) : mknode "dc:creator" [] (intercalate "; " (map stringify $ docAuthors meta)) + : mknode "cp:keywords" [] (intercalate ", " (map stringify $ docKeywords meta)) : (\x -> [ mknode "dcterms:created" [("xsi:type","dcterms:W3CDTF")] x , mknode "dcterms:modified" [("xsi:type","dcterms:W3CDTF")] x ]) (formatTime defaultTimeLocale "%FT%XZ" utctime) -- cgit v1.2.3 From e43ea03410db11276eff9f8112625415f045eab6 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 2 Jun 2017 10:10:31 +0200 Subject: Fixed HTML reader. --- src/Text/Pandoc/Readers/HTML.hs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 218ce3f5a..c1bdb4d09 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -924,8 +924,9 @@ isBlockTag t = tagOpen isBlockTagName (const True) t || tagComment (const True) t where isBlockTagName ('?':_) = True isBlockTagName ('!':_) = True - isBlockTagName x = x `elem` blockTags - || x `elem` eitherBlockOrInline + isBlockTagName x = x `Set.member` blockTags + || x `Set.member` + eitherBlockOrInline isTextTag :: Tag String -> Bool isTextTag = tagText (const True) -- cgit v1.2.3 From 18f86a0c02da887ad0a61dfe449e58aa7fda294c Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 2 Jun 2017 10:17:19 +0200 Subject: Fixed keywords in docx writer. (See #3719) --- src/Text/Pandoc/Writers/Docx.hs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index c68e90049..63bb8a5ae 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -496,6 +496,11 @@ writeDocx opts doc@(Pandoc meta _) = do , qName (elName e) == "abstractNum" ] ++ [Elem e | e <- allElts , qName (elName e) == "num" ] } + + let keywords = case lookupMeta "keywords" meta of + Just (MetaList xs) -> map stringify xs + _ -> [] + let docPropsPath = "docProps/core.xml" let docProps = mknode "cp:coreProperties" [("xmlns:cp","http://schemas.openxmlformats.org/package/2006/metadata/core-properties") @@ -505,7 +510,7 @@ writeDocx opts doc@(Pandoc meta _) = do ,("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance")] $ mknode "dc:title" [] (stringify $ docTitle meta) : mknode "dc:creator" [] (intercalate "; " (map stringify $ docAuthors meta)) - : mknode "cp:keywords" [] (intercalate ", " (map stringify $ docKeywords meta)) + : mknode "cp:keywords" [] (intercalate ", " keywords) : (\x -> [ mknode "dcterms:created" [("xsi:type","dcterms:W3CDTF")] x , mknode "dcterms:modified" [("xsi:type","dcterms:W3CDTF")] x ]) (formatTime defaultTimeLocale "%FT%XZ" utctime) -- cgit v1.2.3 From b61a51ee1551c62558369d9bcdaff32de7f3e2eb Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 2 Jun 2017 15:06:14 +0200 Subject: hlint suggestions. --- src/Text/Pandoc/App.hs | 1 + src/Text/Pandoc/CSS.hs | 2 +- src/Text/Pandoc/Class.hs | 2 +- src/Text/Pandoc/Compat/Time.hs | 2 +- src/Text/Pandoc/Error.hs | 2 +- src/Text/Pandoc/Highlighting.hs | 6 ++--- src/Text/Pandoc/ImageSize.hs | 57 ++++++++++++++++++++--------------------- 7 files changed, 36 insertions(+), 36 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 58044860b..4d42b2f2b 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -1554,3 +1554,4 @@ splitField s = case break (`elem` ":=") s of (k,_:v) -> (k,v) (k,[]) -> (k,"true") + diff --git a/src/Text/Pandoc/CSS.hs b/src/Text/Pandoc/CSS.hs index 3e2fd6309..41be1ea13 100644 --- a/src/Text/Pandoc/CSS.hs +++ b/src/Text/Pandoc/CSS.hs @@ -11,7 +11,7 @@ import Text.Parsec.String ruleParser :: Parser (String, String) ruleParser = do p <- many1 (noneOf ":") <* char ':' - v <- many1 (noneOf ":;") <* (optional $ char ';') <* spaces + v <- many1 (noneOf ":;") <* optional (char ';') <* spaces return (trim p, trim v) styleAttrParser :: Parser [(String, String)] diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 49b20bd30..91731d396 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -365,7 +365,7 @@ fillMediaBag sourceURL d = walkM handleImage d let fname = basename <.> ext insertMedia fname mt bs' return $ Image attr lab (fname, tit)) - (\e -> + (\e -> case e of PandocResourceNotFound _ -> do report $ CouldNotFetchResource src diff --git a/src/Text/Pandoc/Compat/Time.hs b/src/Text/Pandoc/Compat/Time.hs index b1cde82a4..1de197801 100644 --- a/src/Text/Pandoc/Compat/Time.hs +++ b/src/Text/Pandoc/Compat/Time.hs @@ -27,4 +27,4 @@ where import Data.Time import System.Locale ( defaultTimeLocale ) -#endif +#endif \ No newline at end of file diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs index 077413056..3cf381168 100644 --- a/src/Text/Pandoc/Error.hs +++ b/src/Text/Pandoc/Error.hs @@ -83,7 +83,7 @@ handleError (Left e) = errColumn = sourceColumn errPos ls = lines input ++ [""] errorInFile = if length ls > errLine - 1 - then concat ["\n", (ls !! (errLine - 1)) + then concat ["\n", ls !! (errLine - 1) ,"\n", replicate (errColumn - 1) ' ' ,"^"] else "" diff --git a/src/Text/Pandoc/Highlighting.hs b/src/Text/Pandoc/Highlighting.hs index 183155d5b..0754aae4c 100644 --- a/src/Text/Pandoc/Highlighting.hs +++ b/src/Text/Pandoc/Highlighting.hs @@ -91,7 +91,7 @@ highlight syntaxmap formatter (_, classes, keyvals) rawCode = , traceOutput = False } classes' = map T.pack classes rawCode' = T.pack rawCode - in case msum (map (\l -> lookupSyntax l syntaxmap) classes') of + in case msum (map ((`lookupSyntax` syntaxmap)) classes') of Nothing | numberLines fmtOpts -> Right $ formatter fmtOpts{ codeClasses = [], @@ -100,9 +100,9 @@ highlight syntaxmap formatter (_, classes, keyvals) rawCode = $ T.lines rawCode' | otherwise -> Left "" Just syntax -> - (formatter fmtOpts{ codeClasses = + formatter fmtOpts{ codeClasses = [T.toLower (sShortname syntax)], - containerClasses = classes' }) <$> + containerClasses = classes' } <$> tokenize tokenizeOpts syntax rawCode' -- Functions for correlating latex listings package's language names diff --git a/src/Text/Pandoc/ImageSize.hs b/src/Text/Pandoc/ImageSize.hs index eec8658c5..61ff006cf 100644 --- a/src/Text/Pandoc/ImageSize.hs +++ b/src/Text/Pandoc/ImageSize.hs @@ -120,7 +120,7 @@ imageType img = case B.take 4 img of | findSvgTag img -> return Svg "%!PS" - | (B.take 4 $ B.drop 1 $ B.dropWhile (/=' ') img) == "EPSF" + | B.take 4 (B.drop 1 $ B.dropWhile (/=' ') img) == "EPSF" -> return Eps _ -> mzero @@ -168,7 +168,7 @@ desiredSizeInPoints opts attr s = (Nothing, Nothing) -> sizeInPoints s where ratio = fromIntegral (pxX s) / fromIntegral (pxY s) - getDim dir = case (dimension dir attr) of + getDim dir = case dimension dir attr of Just (Percent _) -> Nothing Just dim -> Just $ inPoints opts dim Nothing -> Nothing @@ -182,7 +182,7 @@ inEm opts dim = (64/11) * inInch opts dim inInch :: WriterOptions -> Dimension -> Double inInch opts dim = case dim of - (Pixel a) -> fromIntegral a / (fromIntegral $ writerDpi opts) + (Pixel a) -> fromIntegral a / fromIntegral (writerDpi opts) (Centimeter a) -> a * 0.3937007874 (Inch a) -> a (Percent _) -> 0 @@ -261,7 +261,7 @@ epsSize img = do case ls' of [] -> mzero (x:_) -> case B.words x of - (_:_:_:ux:uy:[]) -> do + [_, _, _, ux, uy] -> do ux' <- safeRead $ B.unpack ux uy' <- safeRead $ B.unpack uy return ImageSize{ @@ -279,27 +279,26 @@ pngSize img = do let (i, rest') = B.splitAt 4 $ B.drop 4 rest guard $ i == "MHDR" || i == "IHDR" let (sizes, rest'') = B.splitAt 8 rest' - (x,y) <- case map fromIntegral $ unpack $ sizes of + (x,y) <- case map fromIntegral $unpack sizes of ([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) + (shift w1 24 + shift w2 16 + shift w3 8 + w4, + shift h1 24 + shift h2 16 + shift h3 8 + h4) _ -> Nothing -- "PNG parse error" let (dpix, dpiy) = findpHYs rest'' - return $ ImageSize { pxX = x, pxY = y, dpiX = dpix, dpiY = dpiy } + return ImageSize { pxX = x, pxY = y, dpiX = dpix, dpiY = dpiy } findpHYs :: ByteString -> (Integer, Integer) -findpHYs x = - if B.null x || "IDAT" `B.isPrefixOf` x - then (72,72) -- default, no pHYs - else if "pHYs" `B.isPrefixOf` x - then let [x1,x2,x3,x4,y1,y2,y3,y4,u] = map fromIntegral - $ unpack $ B.take 9 $ B.drop 4 x - factor = if u == 1 -- dots per meter - then \z -> z * 254 `div` 10000 - else const 72 - in ( factor $ (shift x1 24) + (shift x2 16) + (shift x3 8) + x4, - factor $ (shift y1 24) + (shift y2 16) + (shift y3 8) + y4 ) - else findpHYs $ B.drop 1 x -- read another byte +findpHYs x + | B.null x || "IDAT" `B.isPrefixOf` x = (72,72) + | "pHYs" `B.isPrefixOf` x = + let [x1,x2,x3,x4,y1,y2,y3,y4,u] = + map fromIntegral $ unpack $ B.take 9 $ B.drop 4 x + factor = if u == 1 -- dots per meter + then \z -> z * 254 `div` 10000 + else const 72 + in ( factor $ (shift x1 24) + (shift x2 16) + (shift x3 8) + x4, + factor $ (shift y1 24) + (shift y2 16) + (shift y3 8) + y4 ) + | otherwise = findpHYs $ B.drop 1 x -- read another byte gifSize :: ByteString -> Maybe ImageSize gifSize img = do @@ -343,16 +342,16 @@ jpegSize img = jfifSize :: ByteString -> Either String ImageSize jfifSize rest = let [dpiDensity,dpix1,dpix2,dpiy1,dpiy2] = map fromIntegral - $ unpack $ B.take 5 $ B.drop 9 $ rest + $ unpack $ B.take 5 $B.drop 9 rest factor = case dpiDensity of 1 -> id - 2 -> \x -> (x * 254 `div` 10) + 2 -> \x -> x * 254 `div` 10 _ -> const 72 dpix = factor (shift dpix1 8 + dpix2) dpiy = factor (shift dpiy1 8 + dpiy2) in case findJfifSize rest of Left msg -> Left msg - Right (w,h) -> Right $ ImageSize { pxX = w + Right (w,h) ->Right ImageSize { pxX = w , pxY = h , dpiX = dpix , dpiY = dpiy } @@ -386,7 +385,7 @@ runGet' p bl = exifSize :: ByteString -> Either String ImageSize -exifSize bs = runGet' header $ bl +exifSize bs =runGet' header bl where bl = BL.fromChunks [bs] header = runExceptT $ exifHeader bl -- NOTE: It would be nicer to do @@ -456,14 +455,13 @@ exifHeader hdr = do Left msg -> throwError msg Right x -> return x return (tag, payload) - entries <- sequence $ replicate (fromIntegral numentries) ifdEntry + entries <- replicateM (fromIntegral numentries) ifdEntry subentries <- case lookup ExifOffset entries of Just (UnsignedLong offset') -> do pos <- lift bytesRead lift $ skip (fromIntegral offset' - (fromIntegral pos - 8)) numsubentries <- lift getWord16 - sequence $ - replicate (fromIntegral numsubentries) ifdEntry + replicateM (fromIntegral numsubentries) ifdEntry _ -> return [] let allentries = entries ++ subentries (wdth, hght) <- case (lookup ExifImageWidth allentries, @@ -474,13 +472,13 @@ exifHeader hdr = do -- we return a default width and height when -- the exif header doesn't contain these let resfactor = case lookup ResolutionUnit allentries of - Just (UnsignedShort 1) -> (100 / 254) + Just (UnsignedShort 1) -> 100 / 254 _ -> 1 let xres = maybe 72 (\(UnsignedRational x) -> floor $ x * resfactor) $ lookup XResolution allentries let yres = maybe 72 (\(UnsignedRational x) -> floor $ x * resfactor) $ lookup YResolution allentries - return $ ImageSize{ + return ImageSize{ pxX = wdth , pxY = hght , dpiX = xres @@ -604,3 +602,4 @@ tagTypeTable = M.fromList , (0xa300, FileSource) , (0xa301, SceneType) ] + -- cgit v1.2.3 From d55f01c65f0a149b0951d4350293622385cceae9 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Fri, 2 Jun 2017 23:54:15 +0200 Subject: Org reader: apply hlint suggestions --- src/Text/Pandoc/Readers/Org/BlockStarts.hs | 9 ++-- src/Text/Pandoc/Readers/Org/Blocks.hs | 63 ++++++++++++++-------------- src/Text/Pandoc/Readers/Org/DocumentTree.hs | 25 ++++++----- src/Text/Pandoc/Readers/Org/Inlines.hs | 64 +++++++++++++++-------------- src/Text/Pandoc/Readers/Org/Meta.hs | 9 ++-- src/Text/Pandoc/Readers/Org/ParserState.hs | 1 - src/Text/Pandoc/Readers/Org/Shared.hs | 2 +- 7 files changed, 84 insertions(+), 89 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/BlockStarts.hs b/src/Text/Pandoc/Readers/Org/BlockStarts.hs index fb2b52654..9c6614c99 100644 --- a/src/Text/Pandoc/Readers/Org/BlockStarts.hs +++ b/src/Text/Pandoc/Readers/Org/BlockStarts.hs @@ -66,7 +66,7 @@ gridTableStart = try $ skipSpaces <* char '+' <* char '-' latexEnvStart :: Monad m => OrgParser m String -latexEnvStart = try $ do +latexEnvStart = try $ skipSpaces *> string "\\begin{" *> latexEnvName <* string "}" @@ -97,8 +97,7 @@ orderedListStart = genericListStart orderedListMarker where orderedListMarker = mappend <$> many1 digit <*> (pure <$> oneOf ".)") drawerStart :: Monad m => OrgParser m String -drawerStart = try $ - skipSpaces *> drawerName <* skipSpaces <* newline +drawerStart = try $ skipSpaces *> drawerName <* skipSpaces <* newline where drawerName = char ':' *> manyTill nonspaceChar (char ':') metaLineStart :: Monad m => OrgParser m () @@ -120,8 +119,8 @@ noteMarker = try $ do -- | Succeeds if the parser is at the end of a block. endOfBlock :: Monad m => OrgParser m () -endOfBlock = lookAhead . try $ do - void blankline <|> anyBlockStart +endOfBlock = lookAhead . try $ + void blankline <|> anyBlockStart where -- Succeeds if there is a new block starting at this position. anyBlockStart :: Monad m => OrgParser m () diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index b650721b3..f669abc27 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -17,7 +17,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE ViewPatterns #-} {- | Module : Text.Pandoc.Readers.Org.Blocks Copyright : Copyright (C) 2014-2017 Albert Krewinkel @@ -52,7 +51,7 @@ import Control.Monad (foldM, guard, mzero, void) import Data.Char (isSpace, toLower, toUpper) import Data.Default (Default) import Data.List (foldl', isPrefixOf) -import Data.Maybe (fromMaybe, isNothing) +import Data.Maybe (fromMaybe, isJust, isNothing) import Data.Monoid ((<>)) -- @@ -113,7 +112,7 @@ data BlockAttributes = BlockAttributes -- | Convert BlockAttributes into pandoc Attr attrFromBlockAttributes :: BlockAttributes -> Attr -attrFromBlockAttributes (BlockAttributes{..}) = +attrFromBlockAttributes BlockAttributes{..} = let ident = fromMaybe mempty $ lookup "id" blockAttrKeyValues classes = case lookup "class" blockAttrKeyValues of @@ -142,7 +141,7 @@ blockAttributes = try $ do Nothing -> return Nothing Just s -> Just <$> parseFromString inlines (s ++ "\n") kvAttrs' <- parseFromString keyValues . (++ "\n") $ fromMaybe mempty kvAttrs - return $ BlockAttributes + return BlockAttributes { blockAttrName = name , blockAttrLabel = label , blockAttrCaption = caption' @@ -187,7 +186,7 @@ orgBlock = try $ do blockAttrs <- blockAttributes blkType <- blockHeaderStart ($ blkType) $ - case (map toLower blkType) of + case map toLower blkType of "export" -> exportBlock "comment" -> rawBlockLines (const mempty) "html" -> rawBlockLines (return . B.rawBlock (lowercase blkType)) @@ -208,10 +207,10 @@ orgBlock = try $ do lowercase = map toLower rawBlockLines :: Monad m => (String -> F Blocks) -> String -> OrgParser m (F Blocks) -rawBlockLines f blockType = (ignHeaders *> (f <$> rawBlockContent blockType)) +rawBlockLines f blockType = ignHeaders *> (f <$> rawBlockContent blockType) parseBlockLines :: PandocMonad m => (F Blocks -> F Blocks) -> String -> OrgParser m (F Blocks) -parseBlockLines f blockType = (ignHeaders *> (f <$> parsedBlockContent)) +parseBlockLines f blockType = ignHeaders *> (f <$> parsedBlockContent) where parsedBlockContent :: PandocMonad m => OrgParser m (F Blocks) parsedBlockContent = try $ do @@ -239,8 +238,7 @@ rawBlockContent blockType = try $ do stripIndent strs = map (drop (shortestIndent strs)) strs shortestIndent :: [String] -> Int - shortestIndent = foldr min maxBound - . map (length . takeWhile isSpace) + shortestIndent = foldr (min . length . takeWhile isSpace) maxBound . filter (not . null) tabsToSpaces :: Int -> String -> String @@ -336,13 +334,13 @@ codeHeaderArgs = try $ do language <- skipSpaces *> orgArgWord (switchClasses, switchKv) <- switchesAsAttributes parameters <- manyTill blockOption newline - return $ ( translateLang language : switchClasses - , originalLang language <> switchKv <> parameters - ) + return ( translateLang language : switchClasses + , originalLang language <> switchKv <> parameters + ) switchesAsAttributes :: Monad m => OrgParser m ([String], [(String, String)]) switchesAsAttributes = try $ do - switches <- skipSpaces *> (try $ switch `sepBy` (many1 spaceChar)) + switches <- skipSpaces *> try (switch `sepBy` many1 spaceChar) return $ foldr addToAttr ([], []) switches where addToAttr :: (Char, Maybe String, SwitchPolarity) @@ -350,7 +348,7 @@ switchesAsAttributes = try $ do -> ([String], [(String, String)]) addToAttr ('n', lineNum, pol) (cls, kv) = let kv' = case lineNum of - Just num -> (("startFrom", num):kv) + Just num -> ("startFrom", num):kv Nothing -> kv cls' = case pol of SwitchPlus -> "continuedSourceBlock":cls @@ -382,7 +380,7 @@ genericSwitch :: Monad m genericSwitch c p = try $ do polarity <- switchPolarity <* char c <* skipSpaces arg <- optionMaybe p - return $ (c, arg, polarity) + return (c, arg, polarity) -- | Reads a line number switch option. The line number switch can be used with -- example and source blocks. @@ -402,8 +400,8 @@ orgParamValue = try $ *> noneOf "\n\r" `many1Till` endOfValue <* skipSpaces where - endOfValue = lookAhead $ (try $ skipSpaces <* oneOf "\n\r") - <|> (try $ skipSpaces1 <* orgArgKey) + endOfValue = lookAhead $ try (skipSpaces <* oneOf "\n\r") + <|> try (skipSpaces1 <* orgArgKey) -- @@ -421,7 +419,7 @@ genericDrawer = try $ do -- Include drawer if it is explicitly included in or not explicitly excluded -- from the list of drawers that should be exported. PROPERTIES drawers are -- never exported. - case (exportDrawers . orgStateExportSettings $ state) of + case exportDrawers . orgStateExportSettings $ state of _ | name == "PROPERTIES" -> return mempty Left names | name `elem` names -> return mempty Right names | name `notElem` names -> return mempty @@ -455,7 +453,7 @@ figure = try $ do Nothing -> mzero Just imgSrc -> do guard (isImageFilename imgSrc) - let isFigure = not . isNothing $ blockAttrCaption figAttrs + let isFigure = isJust $ blockAttrCaption figAttrs return $ imageBlock isFigure figAttrs imgSrc where selfTarget :: PandocMonad m => OrgParser m String @@ -490,8 +488,7 @@ endOfParagraph = try $ skipSpaces *> newline *> endOfBlock -- | Example code marked up by a leading colon. example :: Monad m => OrgParser m (F Blocks) -example = try $ do - returnF . exampleCode =<< unlines <$> many1 exampleLine +example = try $ returnF . exampleCode =<< unlines <$> many1 exampleLine where exampleLine :: Monad m => OrgParser m String exampleLine = try $ exampleLineStart *> anyLine @@ -514,7 +511,7 @@ include = try $ do filename <- includeTarget blockType <- optionMaybe $ skipSpaces *> many1 alphaNum blocksParser <- case blockType of - Just "example" -> do + Just "example" -> return $ pure . B.codeBlock <$> parseRaw Just "export" -> do format <- skipSpaces *> many (noneOf "\n\r\t ") @@ -580,8 +577,8 @@ orgTable :: PandocMonad m => OrgParser m (F Blocks) orgTable = try $ do -- don't allow a table on the first line of a list item; org requires that -- tables start at first non-space character on the line - let isFirstInListItem st = (orgStateParserContext st == ListItemState) && - (orgStateLastPreCharPos st == Nothing) + let isFirstInListItem st = orgStateParserContext st == ListItemState && + isNothing (orgStateLastPreCharPos st) guard =<< not . isFirstInListItem <$> getState blockAttrs <- blockAttributes lookAhead tableStart @@ -594,7 +591,7 @@ orgToPandocTable :: OrgTable -> Inlines -> Blocks orgToPandocTable (OrgTable colProps heads lns) caption = - let totalWidth = if any (not . isNothing) (map columnRelWidth colProps) + let totalWidth = if any isJust (map columnRelWidth colProps) then Just . sum $ map (fromMaybe 1 . columnRelWidth) colProps else Nothing in B.table caption (map (convertColProp totalWidth) colProps) heads lns @@ -604,7 +601,7 @@ orgToPandocTable (OrgTable colProps heads lns) caption = let align' = fromMaybe AlignDefault $ columnAlignment colProp width' = fromMaybe 0 $ (\w t -> (fromIntegral w / fromIntegral t)) - <$> (columnRelWidth colProp) + <$> columnRelWidth colProp <*> totalWidth in (align', width') @@ -630,7 +627,7 @@ tableAlignRow = try $ do columnPropertyCell :: Monad m => OrgParser m ColumnProperty columnPropertyCell = emptyCell <|> propCell <?> "alignment info" where - emptyCell = ColumnProperty Nothing Nothing <$ (try $ skipSpaces *> endOfCell) + emptyCell = ColumnProperty Nothing Nothing <$ try (skipSpaces *> endOfCell) propCell = try $ ColumnProperty <$> (skipSpaces *> char '<' @@ -684,7 +681,7 @@ rowToContent tbl row = where singleRowPromotedToHeader :: OrgTable singleRowPromotedToHeader = case tbl of - OrgTable{ orgTableHeader = [], orgTableRows = b:[] } -> + OrgTable{ orgTableHeader = [], orgTableRows = [b] } -> tbl{ orgTableHeader = b , orgTableRows = [] } _ -> tbl @@ -739,7 +736,7 @@ noteBlock = try $ do paraOrPlain :: PandocMonad m => OrgParser m (F Blocks) paraOrPlain = try $ do -- Make sure we are not looking at a headline - notFollowedBy' (char '*' *> (oneOf " *")) + notFollowedBy' (char '*' *> oneOf " *") ils <- inlines nl <- option False (newline *> return True) -- Read block as paragraph, except if we are in a list context and the block @@ -748,7 +745,7 @@ paraOrPlain = try $ do try (guard nl *> notFollowedBy (inList *> (() <$ orderedListStart <|> bulletListStart)) *> return (B.para <$> ils)) - <|> (return (B.plain <$> ils)) + <|> return (B.plain <$> ils) -- @@ -760,16 +757,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 compactifyDL . sequence + fmap (B.definitionList . 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 . 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 . compactify) . sequence <$> many1 (listItem orderedListStart) bulletListStart' :: Monad m => Maybe Int -> OrgParser m Int diff --git a/src/Text/Pandoc/Readers/Org/DocumentTree.hs b/src/Text/Pandoc/Readers/Org/DocumentTree.hs index 4abbe7be8..cee740e30 100644 --- a/src/Text/Pandoc/Readers/Org/DocumentTree.hs +++ b/src/Text/Pandoc/Readers/Org/DocumentTree.hs @@ -32,10 +32,10 @@ module Text.Pandoc.Readers.Org.DocumentTree , headlineToBlocks ) where +import Control.Arrow ((***)) import Control.Monad (guard, void) import Data.Char (toLower, toUpper) import Data.List ( intersperse ) -import Data.Maybe (fromMaybe) import Data.Monoid ((<>)) import Text.Pandoc.Builder (Blocks, Inlines) import Text.Pandoc.Class (PandocMonad) @@ -142,7 +142,7 @@ headline blocks inline lvl = try $ do title' <- title contents' <- contents children' <- sequence children - return $ Headline + return Headline { headlineLevel = level , headlineTodoMarker = todoKw , headlineText = title' @@ -162,7 +162,7 @@ headline blocks inline lvl = try $ do -- | Convert an Org mode headline (i.e. a document tree) into pandoc's Blocks headlineToBlocks :: Monad m => Headline -> OrgParser m Blocks -headlineToBlocks hdln@(Headline {..}) = do +headlineToBlocks hdln@Headline {..} = do maxHeadlineLevels <- getExportSetting exportHeadlineLevels case () of _ | any isNoExportTag headlineTags -> return mempty @@ -193,7 +193,7 @@ archivedHeadlineToBlocks hdln = do ArchivedTreesHeadlineOnly -> headlineToHeader hdln headlineToHeaderWithList :: Monad m => Headline -> OrgParser m Blocks -headlineToHeaderWithList hdln@(Headline {..}) = do +headlineToHeaderWithList hdln@Headline {..} = do maxHeadlineLevels <- getExportSetting exportHeadlineLevels header <- headlineToHeader hdln listElements <- mapM headlineToBlocks headlineChildren @@ -212,13 +212,13 @@ headlineToHeaderWithList hdln@(Headline {..}) = do _ -> mempty headlineToHeaderWithContents :: Monad m => Headline -> OrgParser m Blocks -headlineToHeaderWithContents hdln@(Headline {..}) = do +headlineToHeaderWithContents hdln@Headline {..} = do header <- headlineToHeader hdln childrenBlocks <- mconcat <$> mapM headlineToBlocks headlineChildren return $ header <> headlineContents <> childrenBlocks headlineToHeader :: Monad m => Headline -> OrgParser m Blocks -headlineToHeader (Headline {..}) = do +headlineToHeader Headline {..} = do exportTodoKeyword <- getExportSetting exportWithTodoKeywords exportTags <- getExportSetting exportWithTags let todoText = if exportTodoKeyword @@ -237,7 +237,7 @@ headlineToHeader (Headline {..}) = do todoKeyword :: Monad m => OrgParser m TodoMarker todoKeyword = try $ do taskStates <- activeTodoMarkers <$> getState - let kwParser tdm = try $ (tdm <$ string (todoMarkerName tdm) <* spaceChar) + let kwParser tdm = try (tdm <$ string (todoMarkerName tdm) <* spaceChar) choice (map kwParser taskStates) todoKeywordToInlines :: TodoMarker -> Inlines @@ -250,19 +250,19 @@ todoKeywordToInlines tdm = propertiesToAttr :: Properties -> Attr propertiesToAttr properties = let - toStringPair prop = (fromKey (fst prop), fromValue (snd prop)) + toStringPair = fromKey *** fromValue customIdKey = toPropertyKey "custom_id" classKey = toPropertyKey "class" unnumberedKey = toPropertyKey "unnumbered" specialProperties = [customIdKey, classKey, unnumberedKey] - id' = fromMaybe mempty . fmap fromValue . lookup customIdKey $ properties - cls = fromMaybe mempty . fmap fromValue . lookup classKey $ properties + id' = maybe mempty fromValue . lookup customIdKey $ properties + cls = maybe mempty fromValue . lookup classKey $ properties kvs' = map toStringPair . filter ((`notElem` specialProperties) . fst) $ properties isUnnumbered = - fromMaybe False . fmap isNonNil . lookup unnumberedKey $ properties + maybe False isNonNil . lookup unnumberedKey $ properties in - (id', words cls ++ (if isUnnumbered then ["unnumbered"] else []), kvs') + (id', words cls ++ ["unnumbered" | isUnnumbered], kvs') tagsToInlines :: [Tag] -> Inlines tagsToInlines [] = mempty @@ -302,4 +302,3 @@ propertiesDrawer = try $ do endOfDrawer :: Monad m => OrgParser m String endOfDrawer = try $ skipSpaces *> stringAnyCase ":END:" <* skipSpaces <* newline - diff --git a/src/Text/Pandoc/Readers/Org/Inlines.hs b/src/Text/Pandoc/Readers/Org/Inlines.hs index ad5a1e4de..af28701d7 100644 --- a/src/Text/Pandoc/Readers/Org/Inlines.hs +++ b/src/Text/Pandoc/Readers/Org/Inlines.hs @@ -48,7 +48,7 @@ import Text.Pandoc.Readers.LaTeX (inlineCommand, rawLaTeXInline) import Text.TeXMath (DisplayType (..), readTeX, writePandoc) import qualified Text.TeXMath.Readers.MathML.EntityMap as MathMLEntityMap -import Control.Monad (guard, mplus, mzero, void, when) +import Control.Monad (guard, mplus, mzero, unless, void, when) import Control.Monad.Trans (lift) import Data.Char (isAlphaNum, isSpace) import Data.List (intersperse) @@ -63,7 +63,7 @@ import Prelude hiding (sequence) -- recordAnchorId :: PandocMonad m => String -> OrgParser m () recordAnchorId i = updateState $ \s -> - s{ orgStateAnchorIds = i : (orgStateAnchorIds s) } + s{ orgStateAnchorIds = i : orgStateAnchorIds s } pushToInlineCharStack :: PandocMonad m => Char -> OrgParser m () pushToInlineCharStack c = updateState $ \s -> @@ -184,7 +184,7 @@ cite = try $ berkeleyCite <|> do , orgRefCite , berkeleyTextualCite ] - return $ (flip B.cite (B.text raw)) <$> cs + return $ flip B.cite (B.text raw) <$> cs -- | A citation in Pandoc Org-mode style (@[prefix \@citekey suffix]@). pandocOrgCite :: PandocMonad m => OrgParser m (F [Citation]) @@ -209,7 +209,7 @@ normalOrgRefCite = try $ do orgRefCiteList :: PandocMonad m => CitationMode -> OrgParser m (F Citation) orgRefCiteList citeMode = try $ do key <- orgRefCiteKey - returnF $ Citation + returnF Citation { citationId = key , citationPrefix = mempty , citationSuffix = mempty @@ -232,11 +232,11 @@ berkeleyCite = try $ do return $ if parens then toCite - . maybe id (\p -> alterFirst (prependPrefix p)) prefix - . maybe id (\s -> alterLast (appendSuffix s)) suffix + . maybe id (alterFirst . prependPrefix) prefix + . maybe id (alterLast . appendSuffix) suffix $ citationList else maybe mempty (<> " ") prefix - <> (toListOfCites $ map toInTextMode citationList) + <> toListOfCites (map toInTextMode citationList) <> maybe mempty (", " <>) suffix where toCite :: [Citation] -> Inlines @@ -250,7 +250,7 @@ berkeleyCite = try $ do alterFirst, alterLast :: (a -> a) -> [a] -> [a] alterFirst _ [] = [] - alterFirst f (c:cs) = (f c):cs + alterFirst f (c:cs) = f c : cs alterLast f = reverse . alterFirst f . reverse prependPrefix, appendSuffix :: Inlines -> Citation -> Citation @@ -271,7 +271,7 @@ berkeleyCitationList = try $ do skipSpaces commonPrefix <- optionMaybe (try $ citationListPart <* char ';') citations <- citeList - commonSuffix <- optionMaybe (try $ citationListPart) + commonSuffix <- optionMaybe (try citationListPart) char ']' return (BerkeleyCitationList parens <$> sequence commonPrefix @@ -344,7 +344,7 @@ orgRefCiteKey = isCiteKeySpecialChar c = c `elem` citeKeySpecialChars isCiteKeyChar c = isAlphaNum c || isCiteKeySpecialChar c - in try $ many1Till (satisfy $ isCiteKeyChar) + in try $ many1Till (satisfy isCiteKeyChar) $ try . lookAhead $ do many . satisfy $ isCiteKeySpecialChar satisfy $ not . isCiteKeyChar @@ -374,15 +374,16 @@ citation = try $ do return $ do x <- pref y <- suff - return $ Citation{ citationId = key - , citationPrefix = B.toList x - , citationSuffix = B.toList y - , citationMode = if suppress_author - then SuppressAuthor - else NormalCitation - , citationNoteNum = 0 - , citationHash = 0 - } + return Citation + { citationId = key + , citationPrefix = B.toList x + , citationSuffix = B.toList y + , citationMode = if suppress_author + then SuppressAuthor + else NormalCitation + , citationNoteNum = 0 + , citationHash = 0 + } where prefix = trimInlinesF . mconcat <$> manyTill inline (char ']' <|> (']' <$ lookAhead citeKey)) @@ -404,7 +405,7 @@ inlineNote = try $ do ref <- many alphaNum char ':' note <- fmap B.para . trimInlinesF . mconcat <$> many1Till inline (char ']') - when (not $ null ref) $ + unless (null ref) $ addToNotesTable ("fn:" ++ ref, note) return $ B.note <$> note @@ -780,7 +781,7 @@ notAfterForbiddenBorderChar = do -- | Read a sub- or superscript expression subOrSuperExpr :: PandocMonad m => OrgParser m (F Inlines) subOrSuperExpr = try $ - choice [ id <$> charsInBalanced '{' '}' (noneOf "\n\r") + choice [ charsInBalanced '{' '}' (noneOf "\n\r") , enclosing ('(', ')') <$> charsInBalanced '(' ')' (noneOf "\n\r") , simpleSubOrSuperString ] >>= parseFromString (mconcat <$> many inline) @@ -818,7 +819,7 @@ inlineLaTeX = try $ do enableExtension Ext_raw_tex (readerExtensions def) } } texMathToPandoc :: String -> Maybe [Inline] - texMathToPandoc cs = (maybeRight $ readTeX cs) >>= writePandoc DisplayInline + texMathToPandoc cs = maybeRight (readTeX cs) >>= writePandoc DisplayInline maybeRight :: Either a b -> Maybe b maybeRight = either (const Nothing) Just @@ -869,21 +870,19 @@ macro = try $ do eoa = string ")}}}" smart :: PandocMonad m => OrgParser m (F Inlines) -smart = do - doubleQuoted <|> singleQuoted <|> - choice (map (return <$>) [orgApostrophe, orgDash, orgEllipses]) +smart = choice [doubleQuoted, singleQuoted, orgApostrophe, orgDash, orgEllipses] where orgDash = do guardOrSmartEnabled =<< getExportSetting exportSpecialStrings - dash <* updatePositions '-' + pure <$> dash <* updatePositions '-' orgEllipses = do guardOrSmartEnabled =<< getExportSetting exportSpecialStrings - ellipses <* updatePositions '.' + pure <$> ellipses <* updatePositions '.' orgApostrophe = do guardEnabled Ext_smart (char '\'' <|> char '\8217') <* updateLastPreCharPos <* updateLastForbiddenCharPos - return (B.str "\x2019") + returnF (B.str "\x2019") guardOrSmartEnabled :: PandocMonad m => Bool -> OrgParser m () guardOrSmartEnabled b = do @@ -908,6 +907,9 @@ doubleQuoted = try $ do doubleQuoteStart updatePositions '"' contents <- mconcat <$> many (try $ notFollowedBy doubleQuoteEnd >> inline) - (withQuoteContext InDoubleQuote $ (doubleQuoteEnd <* updateLastForbiddenCharPos) >> return - (fmap B.doubleQuoted . trimInlinesF $ contents)) - <|> (return $ return (B.str "\8220") <> contents) + let doubleQuotedContent = withQuoteContext InDoubleQuote $ do + doubleQuoteEnd + updateLastForbiddenCharPos + return . fmap B.doubleQuoted . trimInlinesF $ contents + let leftQuoteAndContent = return $ pure (B.str "\8220") <> contents + doubleQuotedContent <|> leftQuoteAndContent diff --git a/src/Text/Pandoc/Readers/Org/Meta.hs b/src/Text/Pandoc/Readers/Org/Meta.hs index 33c212bca..a87042871 100644 --- a/src/Text/Pandoc/Readers/Org/Meta.hs +++ b/src/Text/Pandoc/Readers/Org/Meta.hs @@ -84,7 +84,7 @@ metaKey = map toLower <$> many1 (noneOf ": \n\r") <* char ':' <* skipSpaces -metaValue :: PandocMonad m => String -> OrgParser m (String, (F MetaValue)) +metaValue :: PandocMonad m => String -> OrgParser m (String, F MetaValue) metaValue key = let inclKey = "header-includes" in case key of @@ -111,7 +111,7 @@ metaInlines = fmap (MetaInlines . B.toList) <$> inlinesTillNewline metaInlinesCommaSeparated :: PandocMonad m => OrgParser m (F MetaValue) metaInlinesCommaSeparated = do - itemStrs <- (many1 (noneOf ",\n")) `sepBy1` (char ',') + itemStrs <- many1 (noneOf ",\n") `sepBy1` char ',' newline items <- mapM (parseFromString inlinesTillNewline . (++ "\n")) itemStrs let toMetaInlines = MetaInlines . B.toList @@ -163,7 +163,7 @@ addLinkFormat key formatter = updateState $ \s -> let fs = orgStateLinkFormatters s in s{ orgStateLinkFormatters = M.insert key formatter fs } -parseLinkFormat :: Monad m => OrgParser m ((String, String -> String)) +parseLinkFormat :: Monad m => OrgParser m (String, String -> String) parseLinkFormat = try $ do linkType <- (:) <$> letter <*> many (alphaNum <|> oneOf "-_") <* skipSpaces linkSubst <- parseFormat @@ -172,8 +172,7 @@ parseLinkFormat = try $ do -- | An ad-hoc, single-argument-only implementation of a printf-style format -- parser. parseFormat :: Monad m => OrgParser m (String -> String) -parseFormat = try $ do - replacePlain <|> replaceUrl <|> justAppend +parseFormat = try $ replacePlain <|> replaceUrl <|> justAppend where -- inefficient, but who cares replacePlain = try $ (\x -> concat . flip intersperse x) diff --git a/src/Text/Pandoc/Readers/Org/ParserState.hs b/src/Text/Pandoc/Readers/Org/ParserState.hs index 4520a5552..6a78ce276 100644 --- a/src/Text/Pandoc/Readers/Org/ParserState.hs +++ b/src/Text/Pandoc/Readers/Org/ParserState.hs @@ -1,5 +1,4 @@ {-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} {- Copyright (C) 2014-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> diff --git a/src/Text/Pandoc/Readers/Org/Shared.hs b/src/Text/Pandoc/Readers/Org/Shared.hs index d9414319a..952082ec1 100644 --- a/src/Text/Pandoc/Readers/Org/Shared.hs +++ b/src/Text/Pandoc/Readers/Org/Shared.hs @@ -56,7 +56,7 @@ cleanLinkString s = '.':'/':_ -> Just s -- relative path '.':'.':'/':_ -> Just s -- relative path -- Relative path or URL (file schema) - 'f':'i':'l':'e':':':s' -> Just $ if ("//" `isPrefixOf` s') then s else s' + 'f':'i':'l':'e':':':s' -> Just $ if "//" `isPrefixOf` s' then s else s' _ | isUrl s -> Just s -- URL _ -> Nothing where -- cgit v1.2.3 From 55d679e382954dd458acd6233609851748522d99 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sat, 3 Jun 2017 12:28:52 +0200 Subject: Improve code style in lua and org modules --- src/Text/Pandoc/Lua.hs | 18 ++++++++--------- src/Text/Pandoc/Lua/Compat.hs | 4 ++-- src/Text/Pandoc/Lua/PandocModule.hs | 26 ++++++++++++------------- src/Text/Pandoc/Lua/SharedInstances.hs | 12 ++++++------ src/Text/Pandoc/Lua/StackInstances.hs | 12 ++++++------ src/Text/Pandoc/Lua/Util.hs | 6 ++---- src/Text/Pandoc/Readers/Org/Blocks.hs | 2 +- src/Text/Pandoc/Readers/Org/DocumentTree.hs | 6 +++--- src/Text/Pandoc/Readers/Org/Inlines.hs | 13 ++++++------- src/Text/Pandoc/Readers/Org/Meta.hs | 10 +++++----- src/Text/Pandoc/Readers/Org/ParserState.hs | 19 +++++++++--------- src/Text/Pandoc/Writers/Org.hs | 30 ++++++++++++++--------------- 12 files changed, 75 insertions(+), 83 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index f4a22b92a..f74c0e425 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -15,8 +15,8 @@ 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 -} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {- | Module : Text.Pandoc.Lua @@ -30,12 +30,12 @@ Pandoc lua utils. -} module Text.Pandoc.Lua ( runLuaFilter, pushPandocModule ) where -import Control.Monad ( (>=>), when ) -import Control.Monad.Trans ( MonadIO(..) ) -import Data.Map ( Map ) -import Scripting.Lua ( LuaState, StackValue(..) ) +import Control.Monad (unless, when, (>=>)) +import Control.Monad.Trans (MonadIO (..)) +import Data.Map (Map) +import Scripting.Lua (LuaState, StackValue (..)) import Text.Pandoc.Definition -import Text.Pandoc.Lua.PandocModule ( pushPandocModule ) +import Text.Pandoc.Lua.PandocModule (pushPandocModule) import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Walk @@ -80,7 +80,7 @@ pushGlobalFilter lua = *> Lua.rawseti lua (-2) 1 runAll :: [LuaFilter] -> Pandoc -> IO Pandoc -runAll [] = return +runAll [] = return runAll (x:xs) = walkMWithLuaFilter x >=> runAll xs walkMWithLuaFilter :: LuaFilter -> Pandoc -> IO Pandoc @@ -225,7 +225,7 @@ instance StackValue LuaFilterFunction where push lua v = pushFilterFunction lua v peek lua i = do isFn <- Lua.isfunction lua i - when (not isFn) (error $ "Not a function at index " ++ (show i)) + unless isFn (error $ "Not a function at index " ++ (show i)) Lua.pushvalue lua i push lua ("PANDOC_FILTER_FUNCTIONS"::String) Lua.rawget lua Lua.registryindex diff --git a/src/Text/Pandoc/Lua/Compat.hs b/src/Text/Pandoc/Lua/Compat.hs index 998d8d032..3fc81a15c 100644 --- a/src/Text/Pandoc/Lua/Compat.hs +++ b/src/Text/Pandoc/Lua/Compat.hs @@ -28,13 +28,13 @@ Compatibility helpers for hslua -} module Text.Pandoc.Lua.Compat ( loadstring ) where -import Scripting.Lua ( LuaState ) +import Scripting.Lua (LuaState) import qualified Scripting.Lua as Lua -- | Interpret string as lua code and load into the lua environment. loadstring :: LuaState -> String -> String -> IO Int #if MIN_VERSION_hslua(0,5,0) -loadstring lua script _ = Lua.loadstring lua script +loadstring lua script _ = Lua.loadstring lua script #else loadstring lua script cn = Lua.loadstring lua script cn #endif diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index 15f19f024..8e0f3a5b4 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -27,25 +27,23 @@ Pandoc module for lua. -} module Text.Pandoc.Lua.PandocModule ( pushPandocModule ) where -import Data.ByteString.Char8 ( unpack ) -import Data.Default ( Default(..) ) -import Scripting.Lua ( LuaState, call, push, pushhsfunction, rawset) -import Text.Pandoc.Class hiding ( readDataFile ) -import Text.Pandoc.Definition ( Pandoc ) -import Text.Pandoc.Lua.Compat ( loadstring ) +import Control.Monad (unless) +import Data.ByteString.Char8 (unpack) +import Data.Default (Default (..)) +import Scripting.Lua (LuaState, call, push, pushhsfunction, rawset) +import Text.Pandoc.Class hiding (readDataFile) +import Text.Pandoc.Definition (Pandoc) +import Text.Pandoc.Lua.Compat (loadstring) import Text.Pandoc.Lua.StackInstances () -import Text.Pandoc.Readers ( Reader(..), getReader ) -import Text.Pandoc.Shared ( readDataFile ) +import Text.Pandoc.Readers (Reader (..), getReader) +import Text.Pandoc.Shared (readDataFile) -- | Push the "pandoc" on the lua stack. pushPandocModule :: LuaState -> IO () pushPandocModule lua = do script <- pandocModuleScript status <- loadstring lua script "pandoc.lua" - if (status /= 0) - then return () - else do - call lua 0 1 + unless (status /= 0) $ call lua 0 1 push lua "__read" pushhsfunction lua read_doc rawset lua (-3) @@ -57,13 +55,13 @@ pandocModuleScript = unpack <$> readDataFile Nothing "pandoc.lua" read_doc :: String -> String -> IO (Either String Pandoc) read_doc formatSpec content = do case getReader formatSpec of - Left s -> return $ Left s + Left s -> return $ Left s Right reader -> case reader of StringReader r -> do res <- runIO $ r def content case res of - Left s -> return . Left $ show s + Left s -> return . Left $ show s Right pd -> return $ Right pd _ -> return $ Left "Only string formats are supported at the moment." diff --git a/src/Text/Pandoc/Lua/SharedInstances.hs b/src/Text/Pandoc/Lua/SharedInstances.hs index 019a82446..a5d4ba1e9 100644 --- a/src/Text/Pandoc/Lua/SharedInstances.hs +++ b/src/Text/Pandoc/Lua/SharedInstances.hs @@ -16,9 +16,9 @@ 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 -} -{-# LANGUAGE CPP #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE LambdaCase #-} #if !MIN_VERSION_base(4,8,0) {-# LANGUAGE OverlappingInstances #-} #endif @@ -36,8 +36,8 @@ Shared StackValue instances for pandoc and generic types. -} module Text.Pandoc.Lua.SharedInstances () where -import Scripting.Lua ( LTYPE(..), StackValue(..), newtable ) -import Text.Pandoc.Lua.Util ( addRawInt, addValue, getRawInt, keyValuePairs ) +import Scripting.Lua (LTYPE (..), StackValue (..), newtable) +import Text.Pandoc.Lua.Util (addRawInt, addValue, getRawInt, keyValuePairs) import qualified Data.Map as M import qualified Text.Pandoc.UTF8 as UTF8 @@ -112,5 +112,5 @@ instance (StackValue a, StackValue b) => StackValue (Either a b) where peek lua idx = peek lua idx >>= \case Just left -> return . Just $ Left left Nothing -> fmap Right <$> peek lua idx - valuetype (Left x) = valuetype x + valuetype (Left x) = valuetype x valuetype (Right x) = valuetype x diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index cfc4389c2..d2e3f630a 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -17,7 +17,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE LambdaCase #-} {-# OPTIONS_GHC -fno-warn-orphans #-} {- | Module : Text.Pandoc.Lua.StackInstances @@ -32,13 +32,13 @@ StackValue instances for pandoc types. -} module Text.Pandoc.Lua.StackInstances () where -import Control.Applicative ( (<|>) ) -import Scripting.Lua - ( LTYPE(..), LuaState, StackValue(..), ltype, newtable, objlen ) +import Control.Applicative ((<|>)) +import Scripting.Lua (LTYPE (..), LuaState, StackValue (..), ltype, newtable, + objlen) import Text.Pandoc.Definition import Text.Pandoc.Lua.SharedInstances () -import Text.Pandoc.Lua.Util ( addValue, getTable, pushViaConstructor ) -import Text.Pandoc.Shared ( safeRead ) +import Text.Pandoc.Lua.Util (addValue, getTable, pushViaConstructor) +import Text.Pandoc.Shared (safeRead) instance StackValue Pandoc where push lua (Pandoc meta blocks) = do diff --git a/src/Text/Pandoc/Lua/Util.hs b/src/Text/Pandoc/Lua/Util.hs index ff07ba7d7..0a704d027 100644 --- a/src/Text/Pandoc/Lua/Util.hs +++ b/src/Text/Pandoc/Lua/Util.hs @@ -42,10 +42,8 @@ module Text.Pandoc.Lua.Util , pushViaConstructor ) where -import Scripting.Lua - ( LuaState, StackValue(..) - , call, getglobal2, gettable, next, pop, pushnil, rawgeti, rawseti, settable - ) +import Scripting.Lua (LuaState, StackValue (..), call, getglobal2, gettable, + next, pop, pushnil, rawgeti, rawseti, settable) -- | Adjust the stack index, assuming that @n@ new elements have been pushed on -- the stack. diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index f669abc27..3e0ab0127 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -351,7 +351,7 @@ switchesAsAttributes = try $ do Just num -> ("startFrom", num):kv Nothing -> kv cls' = case pol of - SwitchPlus -> "continuedSourceBlock":cls + SwitchPlus -> "continuedSourceBlock":cls SwitchMinus -> cls in ("numberLines":cls', kv') addToAttr _ x = x diff --git a/src/Text/Pandoc/Readers/Org/DocumentTree.hs b/src/Text/Pandoc/Readers/Org/DocumentTree.hs index cee740e30..743f6cc0e 100644 --- a/src/Text/Pandoc/Readers/Org/DocumentTree.hs +++ b/src/Text/Pandoc/Readers/Org/DocumentTree.hs @@ -35,14 +35,14 @@ module Text.Pandoc.Readers.Org.DocumentTree import Control.Arrow ((***)) import Control.Monad (guard, void) import Data.Char (toLower, toUpper) -import Data.List ( intersperse ) +import Data.List (intersperse) import Data.Monoid ((<>)) import Text.Pandoc.Builder (Blocks, Inlines) import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Readers.Org.BlockStarts -import Text.Pandoc.Readers.Org.Parsing import Text.Pandoc.Readers.Org.ParserState +import Text.Pandoc.Readers.Org.Parsing import qualified Data.Map as Map import qualified Text.Pandoc.Builder as B @@ -78,7 +78,7 @@ documentTree blocks inline = do getTitle metamap = case Map.lookup "title" metamap of Just (MetaInlines inlns) -> inlns - _ -> [] + _ -> [] newtype Tag = Tag { fromTag :: String } deriving (Show, Eq) diff --git a/src/Text/Pandoc/Readers/Org/Inlines.hs b/src/Text/Pandoc/Readers/Org/Inlines.hs index af28701d7..66273e05d 100644 --- a/src/Text/Pandoc/Readers/Org/Inlines.hs +++ b/src/Text/Pandoc/Readers/Org/Inlines.hs @@ -343,11 +343,10 @@ orgRefCiteKey = let citeKeySpecialChars = "-_:\\./," :: String isCiteKeySpecialChar c = c `elem` citeKeySpecialChars isCiteKeyChar c = isAlphaNum c || isCiteKeySpecialChar c - - in try $ many1Till (satisfy isCiteKeyChar) - $ try . lookAhead $ do - many . satisfy $ isCiteKeySpecialChar - satisfy $ not . isCiteKeyChar + endOfCitation = try $ do + many $ satisfy isCiteKeySpecialChar + satisfy $ not . isCiteKeyChar + in try $ satisfy isCiteKeyChar `many1Till` lookAhead endOfCitation -- | Supported citation types. Only a small subset of org-ref types is @@ -415,7 +414,7 @@ referencedNote = try $ do return $ do notes <- asksF orgStateNotes' case lookup ref notes of - Nothing -> return $ B.str $ "[" ++ ref ++ "]" + Nothing -> return . B.str $ "[" ++ ref ++ "]" Just contents -> do st <- askF let contents' = runF contents st{ orgStateNotes' = [] } @@ -439,7 +438,7 @@ explicitOrImageLink = try $ do src <- srcF case cleanLinkString title of Just imgSrc | isImageFilename imgSrc -> - pure $ B.link src "" $ B.image imgSrc mempty mempty + pure . B.link src "" $ B.image imgSrc mempty mempty _ -> linkToInlinesF src =<< title' diff --git a/src/Text/Pandoc/Readers/Org/Meta.hs b/src/Text/Pandoc/Readers/Org/Meta.hs index a87042871..d22902eae 100644 --- a/src/Text/Pandoc/Readers/Org/Meta.hs +++ b/src/Text/Pandoc/Readers/Org/Meta.hs @@ -75,9 +75,9 @@ declarationLine :: PandocMonad m => OrgParser m () declarationLine = try $ do key <- map toLower <$> metaKey (key', value) <- metaValue key - when (key' /= "results") $ - updateState $ \st -> - st { orgStateMeta = B.setMeta key' <$> value <*> orgStateMeta st } + let addMetaValue st = + st { orgStateMeta = B.setMeta key' <$> value <*> orgStateMeta st } + when (key' /= "results") $ updateState addMetaValue metaKey :: Monad m => OrgParser m String metaKey = map toLower <$> many1 (noneOf ": \n\r") @@ -236,8 +236,8 @@ macroDefinition = try $ do expansionPart = try $ many (notFollowedBy placeholder *> noneOf "\n\r") alternate :: [a] -> [a] -> [a] - alternate [] ys = ys - alternate xs [] = xs + alternate [] ys = ys + alternate xs [] = xs alternate (x:xs) (y:ys) = x : y : alternate xs ys reorder :: [Int] -> [String] -> [String] diff --git a/src/Text/Pandoc/Readers/Org/ParserState.hs b/src/Text/Pandoc/Readers/Org/ParserState.hs index 6a78ce276..92f868516 100644 --- a/src/Text/Pandoc/Readers/Org/ParserState.hs +++ b/src/Text/Pandoc/Readers/Org/ParserState.hs @@ -1,5 +1,5 @@ -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE MultiParamTypeClasses #-} {- Copyright (C) 2014-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> @@ -60,15 +60,14 @@ import qualified Data.Set as Set import Text.Pandoc.Builder (Blocks, Inlines) import Text.Pandoc.Definition (Meta (..), nullMeta) -import Text.Pandoc.Options (ReaderOptions (..)) import Text.Pandoc.Logging -import Text.Pandoc.Parsing (HasHeaderMap (..), HasIdentifierList (..), - HasLogMessages (..), - HasLastStrPosition (..), HasQuoteContext (..), - HasReaderOptions (..), HasIncludeFiles (..), - ParserContext (..), - QuoteContext (..), SourcePos, Future, - askF, asksF, returnF, runF, trimInlinesF) +import Text.Pandoc.Options (ReaderOptions (..)) +import Text.Pandoc.Parsing (Future, HasHeaderMap (..), HasIdentifierList (..), + HasIncludeFiles (..), HasLastStrPosition (..), + HasLogMessages (..), HasQuoteContext (..), + HasReaderOptions (..), ParserContext (..), + QuoteContext (..), SourcePos, askF, asksF, returnF, + runF, trimInlinesF) -- | This is used to delay evaluation until all relevant information has been -- parsed and made available in the parser state. diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index ef60e2f6c..78c102db6 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -34,7 +34,7 @@ Conversion of 'Pandoc' documents to Emacs Org-Mode. Org-Mode: <http://orgmode.org> -} -module Text.Pandoc.Writers.Org ( writeOrg) where +module Text.Pandoc.Writers.Org (writeOrg) where import Control.Monad.State import Data.Char (isAlphaNum, toLower) import Data.List (intersect, intersperse, isPrefixOf, partition, transpose) @@ -77,9 +77,9 @@ pandocToOrg (Pandoc meta blocks) = do body <- blockListToOrg blocks notes <- gets (reverse . stNotes) >>= notesToOrg hasMath <- gets stHasMath - let main = render colwidth $ foldl ($+$) empty $ [body, notes] + let main = render colwidth . foldl ($+$) empty $ [body, notes] let context = defField "body" main - $ defField "math" hasMath + . defField "math" hasMath $ metadata case writerTemplate opts of Nothing -> return main @@ -88,8 +88,7 @@ pandocToOrg (Pandoc meta blocks) = do -- | Return Org representation of notes. notesToOrg :: PandocMonad m => [[Block]] -> Org m Doc notesToOrg notes = - mapM (\(num, note) -> noteToOrg num note) (zip [1..] notes) >>= - return . vsep + vsep <$> zipWithM noteToOrg [1..] notes -- | Return Org representation of a note. noteToOrg :: PandocMonad m => Int -> [Block] -> Org m Doc @@ -221,16 +220,16 @@ blockToOrg (Table caption' _ _ headers rows) = do -- FIXME: Org doesn't allow blocks with height more than 1. let hpipeBlocks blocks = hcat [beg, middle, end] where h = maximum (1 : map height blocks) - sep' = lblock 3 $ vcat (map text $ replicate h " | ") - beg = lblock 2 $ vcat (map text $ replicate h "| ") - end = lblock 2 $ vcat (map text $ replicate h " |") + sep' = lblock 3 $ vcat (replicate h (text " | ")) + beg = lblock 2 $ vcat (replicate h (text "| ")) + end = lblock 2 $ vcat (replicate h (text " |")) middle = hcat $ intersperse sep' blocks let makeRow = hpipeBlocks . zipWith lblock widthsInChars let head' = makeRow headers' rows' <- mapM (\row -> do cols <- mapM blockListToOrg row return $ makeRow cols) rows let border ch = char '|' <> char ch <> - (hcat $ intersperse (char ch <> char '+' <> char ch) $ + (hcat . intersperse (char ch <> char '+' <> char ch) $ map (\l -> text $ replicate l ch) widthsInChars) <> char ch <> char '|' let body = vcat rows' @@ -251,8 +250,7 @@ blockToOrg (OrderedList (start, _, delim) items) = do let maxMarkerLength = maximum $ map length markers let markers' = map (\m -> let s = maxMarkerLength - length m in m ++ replicate s ' ') markers - contents <- mapM (\(item, num) -> orderedListItemToOrg item num) $ - zip markers' items + contents <- zipWithM orderedListItemToOrg markers' items -- ensure that sublists have preceding blank line return $ blankline $$ vcat contents $$ blankline blockToOrg (DefinitionList items) = do @@ -279,8 +277,8 @@ definitionListItemToOrg :: PandocMonad m => ([Inline], [[Block]]) -> Org m Doc definitionListItemToOrg (label, defs) = do label' <- inlineListToOrg label - contents <- liftM vcat $ mapM blockListToOrg defs - return $ hang 2 "- " $ label' <> " :: " <> (contents <> cr) + contents <- vcat <$> mapM blockListToOrg defs + return . hang 2 "- " $ label' <> " :: " <> (contents <> cr) -- | Convert list of key/value pairs to Org :PROPERTIES: drawer. propertiesDrawer :: Attr -> Doc @@ -312,13 +310,13 @@ attrHtml (ident, classes, kvs) = blockListToOrg :: PandocMonad m => [Block] -- ^ List of block elements -> Org m Doc -blockListToOrg blocks = mapM blockToOrg blocks >>= return . vcat +blockListToOrg blocks = vcat <$> mapM blockToOrg blocks -- | Convert list of Pandoc inline elements to Org. inlineListToOrg :: PandocMonad m => [Inline] -> Org m Doc -inlineListToOrg lst = mapM inlineToOrg lst >>= return . hcat +inlineListToOrg lst = hcat <$> mapM inlineToOrg lst -- | Convert Pandoc inline element to Org. inlineToOrg :: PandocMonad m => Inline -> Org m Doc @@ -350,7 +348,7 @@ inlineToOrg (Quoted DoubleQuote lst) = do return $ "\"" <> contents <> "\"" inlineToOrg (Cite _ lst) = inlineListToOrg lst inlineToOrg (Code _ str) = return $ "=" <> text str <> "=" -inlineToOrg (Str str) = return $ text $ escapeString str +inlineToOrg (Str str) = return . text $ escapeString str inlineToOrg (Math t str) = do modify $ \st -> st{ stHasMath = True } return $ if t == InlineMath -- cgit v1.2.3 From 72b45f05ed361d9fd21c0b8625263cf69494fe7a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 10 Jun 2017 15:22:25 +0200 Subject: Rewrote convertTabs to use Text not String. --- src/Text/Pandoc/Shared.hs | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index ce2c4888a..9ee80827f 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -133,7 +133,7 @@ import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as B8 import Data.ByteString.Base64 (decodeLenient) import Data.Sequence (ViewR(..), ViewL(..), viewl, viewr) -import qualified Data.Text as T (toUpper, pack, unpack) +import qualified Data.Text as T import Data.ByteString.Lazy (toChunks, fromChunks) import qualified Data.ByteString.Lazy as BL import Paths_pandoc (version) @@ -279,26 +279,20 @@ escapeURI = escapeURIString (not . needsEscaping) where needsEscaping c = isSpace c || c `elem` ['<','>','|','"','{','}','[',']','^', '`'] - -- | Convert tabs to spaces and filter out DOS line endings. -- Tabs will be preserved if tab stop is set to 0. tabFilter :: Int -- ^ Tab stop - -> String -- ^ Input - -> String + -> T.Text -- ^ Input + -> T.Text tabFilter tabStop = - let go _ [] = "" - go _ ('\n':xs) = '\n' : go tabStop xs - go _ ('\r':'\n':xs) = '\n' : go tabStop xs - go _ ('\r':xs) = '\n' : go tabStop xs - go spsToNextStop ('\t':xs) = - if tabStop == 0 - then '\t' : go tabStop xs - else replicate spsToNextStop ' ' ++ go tabStop xs - go 1 (x:xs) = - x : go tabStop xs - go spsToNextStop (x:xs) = - x : go (spsToNextStop - 1) xs - in go tabStop + T.unlines . (if tabStop == 0 then id else map go) . T.lines + where go s = + let (s1, s2) = T.break (== '\t') s + in if T.null s2 + then s1 + else s1 <> T.replicate + (tabStop - (T.length s1 `mod` tabStop)) (T.pack " ") + <> go (T.drop 1 s2) -- -- Date/time -- cgit v1.2.3 From c691b975061e3674a80474968fab604cafe776af Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 10 Jun 2017 15:54:35 +0200 Subject: UTF8: export toText, toTextLazy. Define toString, toStringLazy in terms of them. --- src/Text/Pandoc/UTF8.hs | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/UTF8.hs b/src/Text/Pandoc/UTF8.hs index 84043d4cb..3f1b28e54 100644 --- a/src/Text/Pandoc/UTF8.hs +++ b/src/Text/Pandoc/UTF8.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE OverloadedStrings #-} {- Copyright (C) 2010-2017 John MacFarlane <jgm@berkeley.edu> @@ -41,8 +42,10 @@ module Text.Pandoc.UTF8 ( readFile , hPutStrLn , hGetContents , toString + , toText , fromString , toStringLazy + , toTextLazy , fromStringLazy , encodePath , decodeArg @@ -51,7 +54,7 @@ module Text.Pandoc.UTF8 ( readFile where import qualified Data.ByteString.Char8 as B -import qualified Data.ByteString.Lazy as BL +import qualified Data.ByteString.Lazy.Char8 as BL import qualified Data.Text as T import qualified Data.Text.Encoding as T import qualified Data.Text.Lazy as TL @@ -110,31 +113,38 @@ hGetContents = fmap toString . B.hGetContents -- >> hSetNewlineMode h universalNewlineMode -- >> IO.hGetContents h --- | Drop BOM (byte order marker) if present at beginning of string. --- Note that Data.Text converts the BOM to code point FEFF, zero-width --- no-break space, so if the string begins with this we strip it off. -dropBOM :: String -> String -dropBOM ('\xFEFF':xs) = xs -dropBOM xs = xs - -filterCRs :: String -> String -filterCRs ('\r':'\n':xs) = '\n': filterCRs xs -filterCRs ('\r':xs) = '\n' : filterCRs xs -filterCRs (x:xs) = x : filterCRs xs -filterCRs [] = [] +-- | Convert UTF8-encoded ByteString to Text, also +-- removing '\r' characters. +toText :: B.ByteString -> T.Text +toText = T.decodeUtf8 . filterCRs . dropBOM + where dropBOM bs = + if "\xEF\xBB\xBF" `B.isPrefixOf` bs + then B.drop 3 bs + else bs + filterCRs = B.filter (/='\r') -- | Convert UTF8-encoded ByteString to String, also -- removing '\r' characters. toString :: B.ByteString -> String -toString = filterCRs . dropBOM . T.unpack . T.decodeUtf8 +toString = T.unpack . toText -fromString :: String -> B.ByteString -fromString = T.encodeUtf8 . T.pack +-- | Convert UTF8-encoded ByteString to Text, also +-- removing '\r' characters. +toTextLazy :: BL.ByteString -> TL.Text +toTextLazy = TL.decodeUtf8 . filterCRs . dropBOM + where dropBOM bs = + if "\xEF\xBB\xBF" `BL.isPrefixOf` bs + then BL.drop 3 bs + else bs + filterCRs = BL.filter (/='\r') -- | Convert UTF8-encoded ByteString to String, also -- removing '\r' characters. toStringLazy :: BL.ByteString -> String -toStringLazy = filterCRs . dropBOM . TL.unpack . TL.decodeUtf8 +toStringLazy = TL.unpack . toTextLazy + +fromString :: String -> B.ByteString +fromString = T.encodeUtf8 . T.pack fromStringLazy :: String -> BL.ByteString fromStringLazy = TL.encodeUtf8 . TL.pack -- cgit v1.2.3 From 627e27fc1e3800e71cac0d0b0ae7f1e687772aea Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 10 Jun 2017 15:55:18 +0200 Subject: App: change readSource(s) to use Text instead of String. --- src/Text/Pandoc/App.hs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 4d42b2f2b..1d42e4854 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -53,7 +53,9 @@ import Data.List (intercalate, isPrefixOf, isSuffixOf, sort) import qualified Data.Map as M import Data.Maybe (fromMaybe, isJust, isNothing) import qualified Data.Set as Set +import Data.Text (Text) import qualified Data.Text as T +import qualified Data.Text.IO as T import Data.Yaml (decode) import qualified Data.Yaml as Yaml import GHC.Generics @@ -381,8 +383,8 @@ convertWithOpts opts = do then 0 else optTabStop opts) - readSources :: [FilePath] -> PandocIO String - readSources srcs = convertTabs . intercalate "\n" <$> + readSources :: [FilePath] -> PandocIO Text + readSources srcs = convertTabs . T.intercalate (T.pack "\n") <$> mapM readSource srcs let runIO' :: PandocIO a -> IO a @@ -405,9 +407,9 @@ convertWithOpts opts = do case reader of StringReader r | optFileScope opts || readerName == "json" -> - mconcat <$> mapM (readSource >=> r readerOpts) sources + mconcat <$> mapM (readSource >=> r readerOpts . T.unpack) sources | otherwise -> - readSources sources' >>= r readerOpts + readSources sources' >>= r readerOpts . T.unpack ByteStringReader r -> mconcat <$> mapM (readFile' >=> r readerOpts) sources @@ -782,21 +784,23 @@ applyFilters mbDatadir filters args d = do expandedFilters <- mapM (expandFilterPath mbDatadir) filters foldrM ($) d $ map (flip externalFilter args) expandedFilters -readSource :: FilePath -> PandocIO String -readSource "-" = liftIO UTF8.getContents +readSource :: FilePath -> PandocIO Text +readSource "-" = liftIO T.getContents readSource src = case parseURI src of Just u | uriScheme u `elem` ["http:","https:"] -> readURI src | uriScheme u == "file:" -> - liftIO $ UTF8.readFile (uriPath u) - _ -> liftIO $ UTF8.readFile src + liftIO $ UTF8.toText <$> + BS.readFile (uriPath u) + _ -> liftIO $ UTF8.toText <$> + BS.readFile src -readURI :: FilePath -> PandocIO String +readURI :: FilePath -> PandocIO Text readURI src = do res <- liftIO $ openURL src case res of Left e -> throwError $ PandocHttpError src e - Right (contents, _) -> return $ UTF8.toString contents + Right (contents, _) -> return $ UTF8.toText contents readFile' :: MonadIO m => FilePath -> m B.ByteString readFile' "-" = liftIO B.getContents -- cgit v1.2.3 From d1e78d96b6ad9a4afe4b319f9c06668e0aa4ca1c Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 10 Jun 2017 16:05:56 +0200 Subject: UTF8: export fromText, fromTextLazy. --- src/Text/Pandoc/UTF8.hs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/UTF8.hs b/src/Text/Pandoc/UTF8.hs index 3f1b28e54..663f30d92 100644 --- a/src/Text/Pandoc/UTF8.hs +++ b/src/Text/Pandoc/UTF8.hs @@ -44,7 +44,9 @@ module Text.Pandoc.UTF8 ( readFile , toString , toText , fromString + , fromText , toStringLazy + , fromTextLazy , toTextLazy , fromStringLazy , encodePath @@ -143,11 +145,17 @@ toTextLazy = TL.decodeUtf8 . filterCRs . dropBOM toStringLazy :: BL.ByteString -> String toStringLazy = TL.unpack . toTextLazy +fromText :: T.Text -> B.ByteString +fromText = T.encodeUtf8 + +fromTextLazy :: TL.Text -> BL.ByteString +fromTextLazy = TL.encodeUtf8 + fromString :: String -> B.ByteString -fromString = T.encodeUtf8 . T.pack +fromString = fromText . T.pack fromStringLazy :: String -> BL.ByteString -fromStringLazy = TL.encodeUtf8 . TL.pack +fromStringLazy = fromTextLazy . TL.pack encodePath :: FilePath -> FilePath encodePath = id -- cgit v1.2.3 From d6822157e75432e09210350e3b58eec3998dca76 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 10 Jun 2017 16:06:15 +0200 Subject: Readers: Changed StringReader -> TextReader. --- src/Text/Pandoc/Readers.hs | 49 ++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 23 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers.hs b/src/Text/Pandoc/Readers.hs index e2d40336c..5cc37cd72 100644 --- a/src/Text/Pandoc/Readers.hs +++ b/src/Text/Pandoc/Readers.hs @@ -93,36 +93,38 @@ import Text.Pandoc.Shared (mapLeft) import Text.Parsec.Error import qualified Text.Pandoc.UTF8 as UTF8 import qualified Data.ByteString.Lazy as BL +import Data.Text (Text) +import qualified Data.Text.Lazy as TL -data Reader m = StringReader (ReaderOptions -> String -> m Pandoc) +data Reader m = TextReader (ReaderOptions -> Text -> m Pandoc) | ByteStringReader (ReaderOptions -> BL.ByteString -> m Pandoc) -- | Association list of formats and readers. readers :: PandocMonad m => [(String, Reader m)] -readers = [ ("native" , StringReader readNative) - ,("json" , StringReader $ \o s -> +readers = [ ("native" , TextReader readNative) + ,("json" , TextReader $ \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 readRST) - ,("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) + ,("markdown" , TextReader readMarkdown) + ,("markdown_strict" , TextReader readMarkdown) + ,("markdown_phpextra" , TextReader readMarkdown) + ,("markdown_github" , TextReader readMarkdown) + ,("markdown_mmd", TextReader readMarkdown) + ,("commonmark" , TextReader readCommonMark) + ,("rst" , TextReader readRST) + ,("mediawiki" , TextReader readMediaWiki) + ,("docbook" , TextReader readDocBook) + ,("opml" , TextReader readOPML) + ,("org" , TextReader readOrg) + ,("textile" , TextReader readTextile) -- TODO : textile+lhs + ,("html" , TextReader readHtml) + ,("latex" , TextReader readLaTeX) + ,("haddock" , TextReader readHaddock) + ,("twiki" , TextReader readTWiki) ,("docx" , ByteStringReader readDocx) ,("odt" , ByteStringReader readOdt) - ,("t2t" , StringReader readTxt2Tags) + ,("t2t" , TextReader readTxt2Tags) ,("epub" , ByteStringReader readEPUB) ] @@ -134,7 +136,7 @@ getReader s = Right (readerName, setExts) -> case lookup readerName readers of Nothing -> Left $ "Unknown reader: " ++ readerName - Just (StringReader r) -> Right $ StringReader $ \o -> + Just (TextReader r) -> Right $ TextReader $ \o -> r o{ readerExtensions = setExts $ getDefaultExtensions readerName } Just (ByteStringReader r) -> Right $ ByteStringReader $ \o -> @@ -142,5 +144,6 @@ getReader s = getDefaultExtensions readerName } -- | Read pandoc document from JSON format. -readJSON :: ReaderOptions -> String -> Either PandocError Pandoc -readJSON _ = mapLeft PandocParseError . eitherDecode' . UTF8.fromStringLazy +readJSON :: ReaderOptions -> Text -> Either PandocError Pandoc +readJSON _ = + mapLeft PandocParseError . eitherDecode' . BL.fromStrict . UTF8.fromText -- cgit v1.2.3 From 94b3dacb4ea7e5e99ab62286b13877b92f9391b3 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 10 Jun 2017 18:26:44 +0200 Subject: Changed all readers to take Text instead of String. Readers: Renamed StringReader -> TextReader. Updated tests. API change. --- src/Text/Pandoc/App.hs | 6 +++--- src/Text/Pandoc/Lua/PandocModule.hs | 5 +++-- src/Text/Pandoc/Readers.hs | 1 - src/Text/Pandoc/Readers/CommonMark.hs | 6 +++--- src/Text/Pandoc/Readers/DocBook.hs | 8 +++++--- src/Text/Pandoc/Readers/EPUB.hs | 4 +++- src/Text/Pandoc/Readers/HTML.hs | 6 ++++-- src/Text/Pandoc/Readers/Haddock.hs | 5 +++-- src/Text/Pandoc/Readers/LaTeX.hs | 5 +++-- src/Text/Pandoc/Readers/Markdown.hs | 5 +++-- src/Text/Pandoc/Readers/MediaWiki.hs | 5 +++-- src/Text/Pandoc/Readers/Native.hs | 21 +++++++++++---------- src/Text/Pandoc/Readers/OPML.hs | 10 ++++++---- src/Text/Pandoc/Readers/Org.hs | 7 +++++-- src/Text/Pandoc/Readers/RST.hs | 7 +++++-- src/Text/Pandoc/Readers/TWiki.hs | 7 +++++-- src/Text/Pandoc/Readers/Textile.hs | 7 +++++-- src/Text/Pandoc/Readers/Txt2Tags.hs | 7 ++++--- 18 files changed, 74 insertions(+), 48 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 1d42e4854..c39bda859 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -405,11 +405,11 @@ convertWithOpts opts = do let sourceToDoc :: [FilePath] -> PandocIO Pandoc sourceToDoc sources' = case reader of - StringReader r + TextReader r | optFileScope opts || readerName == "json" -> - mconcat <$> mapM (readSource >=> r readerOpts . T.unpack) sources + mconcat <$> mapM (readSource >=> r readerOpts) sources | otherwise -> - readSources sources' >>= r readerOpts . T.unpack + readSources sources' >>= r readerOpts ByteStringReader r -> mconcat <$> mapM (readFile' >=> r readerOpts) sources diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index 8e0f3a5b4..27c19d4f0 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -30,6 +30,7 @@ module Text.Pandoc.Lua.PandocModule ( pushPandocModule ) where import Control.Monad (unless) import Data.ByteString.Char8 (unpack) import Data.Default (Default (..)) +import Data.Text (pack) import Scripting.Lua (LuaState, call, push, pushhsfunction, rawset) import Text.Pandoc.Class hiding (readDataFile) import Text.Pandoc.Definition (Pandoc) @@ -58,8 +59,8 @@ read_doc formatSpec content = do Left s -> return $ Left s Right reader -> case reader of - StringReader r -> do - res <- runIO $ r def content + TextReader r -> do + res <- runIO $ r def (pack content) case res of Left s -> return . Left $ show s Right pd -> return $ Right pd diff --git a/src/Text/Pandoc/Readers.hs b/src/Text/Pandoc/Readers.hs index 5cc37cd72..004fefe25 100644 --- a/src/Text/Pandoc/Readers.hs +++ b/src/Text/Pandoc/Readers.hs @@ -94,7 +94,6 @@ import Text.Parsec.Error import qualified Text.Pandoc.UTF8 as UTF8 import qualified Data.ByteString.Lazy as BL import Data.Text (Text) -import qualified Data.Text.Lazy as TL data Reader m = TextReader (ReaderOptions -> Text -> m Pandoc) | ByteStringReader (ReaderOptions -> BL.ByteString -> m Pandoc) diff --git a/src/Text/Pandoc/Readers/CommonMark.hs b/src/Text/Pandoc/Readers/CommonMark.hs index e98ee066e..3c62f8db5 100644 --- a/src/Text/Pandoc/Readers/CommonMark.hs +++ b/src/Text/Pandoc/Readers/CommonMark.hs @@ -34,15 +34,15 @@ where import CMark import Data.List (groupBy) -import Data.Text (pack, unpack) +import Data.Text (Text, unpack) import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Options -- | Parse a CommonMark formatted string into a 'Pandoc' structure. -readCommonMark :: PandocMonad m => ReaderOptions -> String -> m Pandoc +readCommonMark :: PandocMonad m => ReaderOptions -> Text -> m Pandoc readCommonMark opts s = return $ - nodeToPandoc $ commonmarkToNode opts' $ pack s + nodeToPandoc $ commonmarkToNode opts' s where opts' = if extensionEnabled Ext_smart (readerExtensions opts) then [optNormalize, optSmart] else [optNormalize] diff --git a/src/Text/Pandoc/Readers/DocBook.hs b/src/Text/Pandoc/Readers/DocBook.hs index bef256a93..bd3c7c356 100644 --- a/src/Text/Pandoc/Readers/DocBook.hs +++ b/src/Text/Pandoc/Readers/DocBook.hs @@ -16,6 +16,8 @@ import Text.TeXMath (readMathML, writeTeX) import Data.Default import Data.Foldable (asum) import Text.Pandoc.Class (PandocMonad) +import Data.Text (Text) +import qualified Data.Text as T {- @@ -522,11 +524,11 @@ instance Default DBState where , dbContent = [] } -readDocBook :: PandocMonad m => ReaderOptions -> String -> m Pandoc +readDocBook :: PandocMonad m => ReaderOptions -> Text -> m Pandoc readDocBook _ inp = do - let tree = normalizeTree . parseXML . handleInstructions $ inp + let tree = normalizeTree . parseXML . handleInstructions $ T.unpack inp (bs, st') <- flip runStateT (def{ dbContent = tree }) $ mapM parseBlock $ tree - return $ Pandoc (dbMeta st') (toList . mconcat $ bs) + return $ Pandoc (dbMeta st') (toList . mconcat $ bs) -- We treat <?asciidoc-br?> specially (issue #1236), converting it -- to <br/>, since xml-light doesn't parse the instruction correctly. diff --git a/src/Text/Pandoc/Readers/EPUB.hs b/src/Text/Pandoc/Readers/EPUB.hs index db58e9654..c0d8029dc 100644 --- a/src/Text/Pandoc/Readers/EPUB.hs +++ b/src/Text/Pandoc/Readers/EPUB.hs @@ -13,6 +13,8 @@ import Control.DeepSeq (NFData, deepseq) import Control.Monad (guard, liftM) import Control.Monad.Except (throwError) import qualified Data.ByteString.Lazy as BL (ByteString) +import qualified Data.Text.Lazy.Encoding as TL +import qualified Data.Text.Lazy as TL import Data.List (isInfixOf, isPrefixOf) import qualified Data.Map as M (Map, elems, fromList, lookup) import Data.Maybe (fromMaybe, mapMaybe) @@ -73,7 +75,7 @@ archiveToEPUB os archive = do mimeToReader "application/xhtml+xml" (unEscapeString -> root) (unEscapeString -> path) = do fname <- findEntryByPathE (root </> path) archive - html <- readHtml os' . UTF8.toStringLazy $ fromEntry fname + html <- readHtml os' . TL.toStrict . TL.decodeUtf8 $ fromEntry fname return $ fixInternalReferences path html mimeToReader s _ (unEscapeString -> path) | s `elem` imageMimes = return $ imageToPandoc path diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index c1bdb4d09..3bccf89fb 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -59,6 +59,7 @@ import Control.Monad ( guard, mzero, void, unless ) import Control.Arrow ((***)) import Control.Applicative ( (<|>) ) import Data.Monoid (First (..)) +import Data.Text (Text, unpack) import Text.TeXMath (readMathML, writeTeX) import Data.Default (Default (..), def) import Control.Monad.Reader (ask, asks, local, ReaderT, runReaderT, lift) @@ -74,11 +75,12 @@ import Control.Monad.Except (throwError) -- | Convert HTML-formatted string to 'Pandoc' document. readHtml :: PandocMonad m => ReaderOptions -- ^ Reader options - -> String -- ^ String to parse (assumes @'\n'@ line endings) + -> Text -- ^ String to parse (assumes @'\n'@ line endings) -> m Pandoc readHtml opts inp = do let tags = stripPrefixes . canonicalizeTags $ - parseTagsOptions parseOptions{ optTagPosition = True } inp + parseTagsOptions parseOptions{ optTagPosition = True } + (unpack inp) parseDoc = do blocks <- (fixPlains False) . mconcat <$> manyTill block eof meta <- stateMeta . parserState <$> getState diff --git a/src/Text/Pandoc/Readers/Haddock.hs b/src/Text/Pandoc/Readers/Haddock.hs index 28caa528e..b22b71b96 100644 --- a/src/Text/Pandoc/Readers/Haddock.hs +++ b/src/Text/Pandoc/Readers/Haddock.hs @@ -16,6 +16,7 @@ module Text.Pandoc.Readers.Haddock import Control.Monad.Except (throwError) import Data.List (intersperse, stripPrefix) +import Data.Text (Text, unpack) import Data.Maybe (fromMaybe) import Data.Monoid ((<>)) import Documentation.Haddock.Parser @@ -32,9 +33,9 @@ import Text.Pandoc.Shared (splitBy, trim) -- | Parse Haddock markup and return a 'Pandoc' document. readHaddock :: PandocMonad m => ReaderOptions - -> String + -> Text -> m Pandoc -readHaddock opts s = case readHaddockEither opts s of +readHaddock opts s = case readHaddockEither opts (unpack s) of Right result -> return result Left e -> throwError e diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index b65ae15ad..796d2789e 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -39,6 +39,7 @@ import Control.Applicative (many, optional, (<|>)) import Control.Monad import Control.Monad.Except (throwError) import Data.Char (chr, isAlphaNum, isLetter, ord) +import Data.Text (Text, unpack) import Data.List (intercalate, isPrefixOf) import qualified Data.Map as M import Data.Maybe (fromMaybe, maybeToList) @@ -59,10 +60,10 @@ import Text.Pandoc.Walk -- | Parse LaTeX from string and return 'Pandoc' document. readLaTeX :: PandocMonad m => ReaderOptions -- ^ Reader options - -> String -- ^ String to parse (assumes @'\n'@ line endings) + -> Text -- ^ String to parse (assumes @'\n'@ line endings) -> m Pandoc readLaTeX opts ltx = do - parsed <- readWithM parseLaTeX def{ stateOptions = opts } ltx + parsed <- readWithM parseLaTeX def{ stateOptions = opts } (unpack ltx) case parsed of Right result -> return result Left e -> throwError e diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 5694c4354..5e966a17e 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -70,10 +70,11 @@ type MarkdownParser m = ParserT [Char] ParserState m -- | Read markdown from an input string and return a Pandoc document. readMarkdown :: PandocMonad m => ReaderOptions -- ^ Reader options - -> String -- ^ String to parse (assuming @'\n'@ line endings) + -> Text -- ^ String to parse (assuming @'\n'@ line endings) -> m Pandoc readMarkdown opts s = do - parsed <- (readWithM parseMarkdown) def{ stateOptions = opts } (s ++ "\n\n") + parsed <- (readWithM parseMarkdown) def{ stateOptions = opts } + (T.unpack s ++ "\n\n") case parsed of Right result -> return result Left e -> throwError e diff --git a/src/Text/Pandoc/Readers/MediaWiki.hs b/src/Text/Pandoc/Readers/MediaWiki.hs index 3f6142f00..a3ff60c14 100644 --- a/src/Text/Pandoc/Readers/MediaWiki.hs +++ b/src/Text/Pandoc/Readers/MediaWiki.hs @@ -41,6 +41,7 @@ module Text.Pandoc.Readers.MediaWiki ( readMediaWiki ) where import Control.Monad import Control.Monad.Except (throwError) import Data.Char (isDigit, isSpace) +import Data.Text (Text, unpack) import qualified Data.Foldable as F import Data.List (intercalate, intersperse, isPrefixOf) import qualified Data.Map as M @@ -64,7 +65,7 @@ import Text.Pandoc.XML (fromEntities) -- | Read mediawiki from an input string and return a Pandoc document. readMediaWiki :: PandocMonad m => ReaderOptions -- ^ Reader options - -> String -- ^ String to parse (assuming @'\n'@ line endings) + -> Text -- ^ String to parse (assuming @'\n'@ line endings) -> m Pandoc readMediaWiki opts s = do parsed <- readWithM parseMediaWiki MWState{ mwOptions = opts @@ -76,7 +77,7 @@ readMediaWiki opts s = do , mwLogMessages = [] , mwInTT = False } - (s ++ "\n") + (unpack s ++ "\n") case parsed of Right result -> return result Left e -> throwError e diff --git a/src/Text/Pandoc/Readers/Native.hs b/src/Text/Pandoc/Readers/Native.hs index 8f42a45de..abc2ed38a 100644 --- a/src/Text/Pandoc/Readers/Native.hs +++ b/src/Text/Pandoc/Readers/Native.hs @@ -37,6 +37,7 @@ import Text.Pandoc.Shared (safeRead) import Control.Monad.Except (throwError) import Text.Pandoc.Class import Text.Pandoc.Error +import Data.Text (Text, unpack) -- | Read native formatted text and return a Pandoc document. -- The input may be a full pandoc document, a block list, a block, @@ -50,22 +51,22 @@ import Text.Pandoc.Error -- readNative :: PandocMonad m => ReaderOptions - -> String -- ^ String to parse (assuming @'\n'@ line endings) + -> Text -- ^ String to parse (assuming @'\n'@ line endings) -> m Pandoc readNative _ s = - case maybe (Pandoc nullMeta <$> readBlocks s) Right (safeRead s) of + case maybe (Pandoc nullMeta <$> readBlocks s) Right (safeRead (unpack 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) +readBlocks :: Text -> Either PandocError [Block] +readBlocks s = maybe ((:[]) <$> readBlock s) Right (safeRead (unpack s)) -readBlock :: String -> Either PandocError Block -readBlock s = maybe (Plain <$> readInlines s) Right (safeRead s) +readBlock :: Text -> Either PandocError Block +readBlock s = maybe (Plain <$> readInlines s) Right (safeRead (unpack s)) -readInlines :: String -> Either PandocError [Inline] -readInlines s = maybe ((:[]) <$> readInline s) Right (safeRead s) +readInlines :: Text -> Either PandocError [Inline] +readInlines s = maybe ((:[]) <$> readInline s) Right (safeRead (unpack s)) -readInline :: String -> Either PandocError Inline -readInline s = maybe (Left . PandocParseError $ "Could not read: " ++ s) Right (safeRead s) +readInline :: Text -> Either PandocError Inline +readInline s = maybe (Left . PandocParseError $ "Could not read: " ++ unpack s) Right (safeRead (unpack s)) diff --git a/src/Text/Pandoc/Readers/OPML.hs b/src/Text/Pandoc/Readers/OPML.hs index cf1c8f479..591d7590e 100644 --- a/src/Text/Pandoc/Readers/OPML.hs +++ b/src/Text/Pandoc/Readers/OPML.hs @@ -2,6 +2,7 @@ module Text.Pandoc.Readers.OPML ( readOPML ) where import Control.Monad.State import Data.Char (toUpper) +import Data.Text (Text, unpack, pack) import Data.Default import Data.Generics import Text.HTML.TagSoup.Entity (lookupEntity) @@ -28,9 +29,10 @@ instance Default OPMLState where , opmlDocDate = mempty } -readOPML :: PandocMonad m => ReaderOptions -> String -> m Pandoc +readOPML :: PandocMonad m => ReaderOptions -> Text -> m Pandoc readOPML _ inp = do - (bs, st') <- flip runStateT def (mapM parseBlock $ normalizeTree $ parseXML inp) + (bs, st') <- flip runStateT def + (mapM parseBlock $ normalizeTree $ parseXML (unpack inp)) return $ setTitle (opmlDocTitle st') $ setAuthors (opmlDocAuthors st') $ @@ -69,10 +71,10 @@ asHtml :: PandocMonad m => String -> OPML m Inlines asHtml s = (\(Pandoc _ bs) -> case bs of [Plain ils] -> fromList ils - _ -> mempty) <$> (lift $ readHtml def s) + _ -> mempty) <$> (lift $ readHtml def (pack s)) asMarkdown :: PandocMonad m => String -> OPML m Blocks -asMarkdown s = (\(Pandoc _ bs) -> fromList bs) <$> (lift $ readMarkdown def s) +asMarkdown s = (\(Pandoc _ bs) -> fromList bs) <$> (lift $ readMarkdown def (pack s)) getBlocks :: PandocMonad m => Element -> OPML m Blocks getBlocks e = mconcat <$> (mapM parseBlock $ elContent e) diff --git a/src/Text/Pandoc/Readers/Org.hs b/src/Text/Pandoc/Readers/Org.hs index 2b29bcfda..5e0d67d10 100644 --- a/src/Text/Pandoc/Readers/Org.hs +++ b/src/Text/Pandoc/Readers/Org.hs @@ -40,15 +40,18 @@ import Text.Pandoc.Parsing (reportLogMessages) import Control.Monad.Except (throwError) import Control.Monad.Reader (runReaderT) +import Data.Text (Text) +import qualified Data.Text as T -- | Parse org-mode string and return a Pandoc document. readOrg :: PandocMonad m => ReaderOptions -- ^ Reader options - -> String -- ^ String to parse (assuming @'\n'@ line endings) + -> Text -- ^ String to parse (assuming @'\n'@ line endings) -> m Pandoc readOrg opts s = do parsed <- flip runReaderT def $ - readWithM parseOrg (optionsToParserState opts) (s ++ "\n\n") + readWithM parseOrg (optionsToParserState opts) + (T.unpack s ++ "\n\n") case parsed of Right result -> return result Left _ -> throwError $ PandocParseError "problem parsing org" diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index b242d6428..fb5f6f2d4 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -53,6 +53,8 @@ import Text.Pandoc.Options import Text.Pandoc.Parsing import Text.Pandoc.Shared import Text.Printf (printf) +import Data.Text (Text) +import qualified Data.Text as T -- TODO: -- [ ] .. parsed-literal @@ -62,10 +64,11 @@ import Text.Printf (printf) -- | Parse reStructuredText string and return Pandoc document. readRST :: PandocMonad m => ReaderOptions -- ^ Reader options - -> String -- ^ String to parse (assuming @'\n'@ line endings) + -> Text -- ^ String to parse (assuming @'\n'@ line endings) -> m Pandoc readRST opts s = do - parsed <- (readWithM parseRST) def{ stateOptions = opts } (s ++ "\n\n") + parsed <- (readWithM parseRST) def{ stateOptions = opts } + (T.unpack s ++ "\n\n") case parsed of Right result -> return result Left e -> throwError e diff --git a/src/Text/Pandoc/Readers/TWiki.hs b/src/Text/Pandoc/Readers/TWiki.hs index fcb95fc35..9e544c4ac 100644 --- a/src/Text/Pandoc/Readers/TWiki.hs +++ b/src/Text/Pandoc/Readers/TWiki.hs @@ -49,14 +49,17 @@ import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (enclosed, macro, nested) import Text.Pandoc.Readers.HTML (htmlTag, isCommentTag) import Text.Pandoc.XML (fromEntities) +import Data.Text (Text) +import qualified Data.Text as T -- | Read twiki from an input string and return a Pandoc document. readTWiki :: PandocMonad m => ReaderOptions - -> String + -> Text -> m Pandoc readTWiki opts s = do - res <- readWithM parseTWiki def{ stateOptions = opts } (s ++ "\n\n") + res <- readWithM parseTWiki def{ stateOptions = opts } + (T.unpack s ++ "\n\n") case res of Left e -> throwError e Right d -> return d diff --git a/src/Text/Pandoc/Readers/Textile.hs b/src/Text/Pandoc/Readers/Textile.hs index 0b964dd63..1669e3e51 100644 --- a/src/Text/Pandoc/Readers/Textile.hs +++ b/src/Text/Pandoc/Readers/Textile.hs @@ -70,14 +70,17 @@ import Text.Pandoc.Parsing import Text.Pandoc.Readers.HTML (htmlTag, isBlockTag, isInlineTag) import Text.Pandoc.Readers.LaTeX (rawLaTeXBlock, rawLaTeXInline) import Text.Pandoc.Shared (trim) +import Data.Text (Text) +import qualified Data.Text as T -- | Parse a Textile text and return a Pandoc document. readTextile :: PandocMonad m => ReaderOptions -- ^ Reader options - -> String -- ^ String to parse (assuming @'\n'@ line endings) + -> Text -- ^ String to parse (assuming @'\n'@ line endings) -> m Pandoc readTextile opts s = do - parsed <- readWithM parseTextile def{ stateOptions = opts } (s ++ "\n\n") + parsed <- readWithM parseTextile def{ stateOptions = opts } + (T.unpack s ++ "\n\n") case parsed of Right result -> return result Left e -> throwError e diff --git a/src/Text/Pandoc/Readers/Txt2Tags.hs b/src/Text/Pandoc/Readers/Txt2Tags.hs index d8791869d..260bb7fff 100644 --- a/src/Text/Pandoc/Readers/Txt2Tags.hs +++ b/src/Text/Pandoc/Readers/Txt2Tags.hs @@ -45,7 +45,8 @@ import Text.Pandoc.Shared (compactify, compactifyDL, escapeURI) import Control.Monad (guard, void, when) import Control.Monad.Reader (Reader, asks, runReader) import Data.Default - +import Data.Text (Text) +import qualified Data.Text as T import Control.Monad.Except (catchError, throwError) import Data.Time.Format (formatTime) import Text.Pandoc.Class (PandocMonad) @@ -90,11 +91,11 @@ getT2TMeta = do -- | Read Txt2Tags from an input string returning a Pandoc document readTxt2Tags :: PandocMonad m => ReaderOptions - -> String + -> Text -> m Pandoc readTxt2Tags opts s = do meta <- getT2TMeta - let parsed = flip runReader meta $ readWithM parseT2T (def {stateOptions = opts}) (s ++ "\n\n") + let parsed = flip runReader meta $ readWithM parseT2T (def {stateOptions = opts}) (T.unpack s ++ "\n\n") case parsed of Right result -> return $ result Left e -> throwError e -- cgit v1.2.3 From e8cc9faa4180a4fe2193e6e25b3e19a9c592c2e2 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 10 Jun 2017 21:54:26 +0200 Subject: Writers: changed StringWriter -> TextWriter. --- src/Text/Pandoc/Writers.hs | 93 +++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 46 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers.hs b/src/Text/Pandoc/Writers.hs index 62445c072..dbe55449f 100644 --- a/src/Text/Pandoc/Writers.hs +++ b/src/Text/Pandoc/Writers.hs @@ -83,6 +83,7 @@ module Text.Pandoc.Writers import Data.Aeson import Data.List (intercalate) +import Data.Text (Text) import Text.Pandoc.Class import Text.Pandoc.Definition import Text.Pandoc.Options @@ -120,59 +121,59 @@ import Text.Parsec.Error import qualified Text.Pandoc.UTF8 as UTF8 import qualified Data.ByteString.Lazy as BL -data Writer m = StringWriter (WriterOptions -> Pandoc -> m String) +data Writer m = TextWriter (WriterOptions -> Pandoc -> m Text) | 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) + ("native" , TextWriter writeNative) + ,("json" , TextWriter $ \o d -> return $ writeJSON o d) ,("docx" , ByteStringWriter writeDocx) ,("odt" , ByteStringWriter writeODT) ,("epub" , ByteStringWriter writeEPUB3) ,("epub2" , ByteStringWriter writeEPUB2) ,("epub3" , ByteStringWriter writeEPUB3) - ,("fb2" , StringWriter writeFB2) - ,("html" , StringWriter writeHtml5String) - ,("html4" , StringWriter writeHtml4String) - ,("html5" , StringWriter writeHtml5String) - ,("icml" , StringWriter writeICML) - ,("s5" , StringWriter writeS5) - ,("slidy" , StringWriter writeSlidy) - ,("slideous" , StringWriter writeSlideous) - ,("dzslides" , StringWriter writeDZSlides) - ,("revealjs" , StringWriter writeRevealJs) - ,("docbook" , StringWriter writeDocbook5) - ,("docbook4" , StringWriter writeDocbook4) - ,("docbook5" , StringWriter writeDocbook5) - ,("jats" , StringWriter writeJATS) - ,("opml" , StringWriter writeOPML) - ,("opendocument" , StringWriter writeOpenDocument) - ,("latex" , StringWriter writeLaTeX) - ,("beamer" , StringWriter writeBeamer) - ,("context" , StringWriter writeConTeXt) - ,("texinfo" , StringWriter writeTexinfo) - ,("man" , StringWriter writeMan) - ,("ms" , StringWriter writeMs) - ,("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 writeRTF) - ,("org" , StringWriter writeOrg) - ,("asciidoc" , StringWriter writeAsciiDoc) - ,("haddock" , StringWriter writeHaddock) - ,("commonmark" , StringWriter writeCommonMark) - ,("tei" , StringWriter writeTEI) - ,("muse" , StringWriter writeMuse) + ,("fb2" , TextWriter writeFB2) + ,("html" , TextWriter writeHtml5String) + ,("html4" , TextWriter writeHtml4String) + ,("html5" , TextWriter writeHtml5String) + ,("icml" , TextWriter writeICML) + ,("s5" , TextWriter writeS5) + ,("slidy" , TextWriter writeSlidy) + ,("slideous" , TextWriter writeSlideous) + ,("dzslides" , TextWriter writeDZSlides) + ,("revealjs" , TextWriter writeRevealJs) + ,("docbook" , TextWriter writeDocbook5) + ,("docbook4" , TextWriter writeDocbook4) + ,("docbook5" , TextWriter writeDocbook5) + ,("jats" , TextWriter writeJATS) + ,("opml" , TextWriter writeOPML) + ,("opendocument" , TextWriter writeOpenDocument) + ,("latex" , TextWriter writeLaTeX) + ,("beamer" , TextWriter writeBeamer) + ,("context" , TextWriter writeConTeXt) + ,("texinfo" , TextWriter writeTexinfo) + ,("man" , TextWriter writeMan) + ,("ms" , TextWriter writeMs) + ,("markdown" , TextWriter writeMarkdown) + ,("markdown_strict" , TextWriter writeMarkdown) + ,("markdown_phpextra" , TextWriter writeMarkdown) + ,("markdown_github" , TextWriter writeMarkdown) + ,("markdown_mmd" , TextWriter writeMarkdown) + ,("plain" , TextWriter writePlain) + ,("rst" , TextWriter writeRST) + ,("mediawiki" , TextWriter writeMediaWiki) + ,("dokuwiki" , TextWriter writeDokuWiki) + ,("zimwiki" , TextWriter writeZimWiki) + ,("textile" , TextWriter writeTextile) + ,("rtf" , TextWriter writeRTF) + ,("org" , TextWriter writeOrg) + ,("asciidoc" , TextWriter writeAsciiDoc) + ,("haddock" , TextWriter writeHaddock) + ,("commonmark" , TextWriter writeCommonMark) + ,("tei" , TextWriter writeTEI) + ,("muse" , TextWriter writeMuse) ] getWriter :: PandocMonad m => String -> Either String (Writer m) @@ -182,12 +183,12 @@ getWriter s Right (writerName, setExts) -> case lookup writerName writers of Nothing -> Left $ "Unknown writer: " ++ writerName - Just (StringWriter r) -> Right $ StringWriter $ + Just (TextWriter r) -> Right $ TextWriter $ \o -> r o{ writerExtensions = setExts $ getDefaultExtensions writerName } Just (ByteStringWriter r) -> Right $ ByteStringWriter $ \o -> r o{ writerExtensions = setExts $ getDefaultExtensions writerName } -writeJSON :: WriterOptions -> Pandoc -> String -writeJSON _ = UTF8.toStringLazy . encode +writeJSON :: WriterOptions -> Pandoc -> Text +writeJSON _ = UTF8.toText . BL.toStrict . encode -- cgit v1.2.3 From 0c2a509dfb3bd9f7ba8a0fdec02a165ed7cf49da Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 10 Jun 2017 23:09:53 +0200 Subject: Writers.Shared: metaToJSON, generalized type so it can take a Text. Previously a String was needed as argument; now any ToJSON instance will do. API change. --- src/Text/Pandoc/Writers/Shared.hs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index c33655522..2047285eb 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -62,10 +62,10 @@ import Text.Pandoc.XML (escapeStringForXML) -- Variables overwrite metadata fields with the same names. -- If multiple variables are set with the same name, a list is -- assigned. Does nothing if 'writerTemplate' is Nothing. -metaToJSON :: (Functor m, Monad m) +metaToJSON :: (Functor m, Monad m, ToJSON a) => WriterOptions - -> ([Block] -> m String) - -> ([Inline] -> m String) + -> ([Block] -> m a) + -> ([Inline] -> m a) -> Meta -> m Value metaToJSON opts blockWriter inlineWriter meta @@ -75,9 +75,9 @@ metaToJSON opts blockWriter inlineWriter meta -- | Like 'metaToJSON', but does not include variables and is -- not sensitive to 'writerTemplate'. -metaToJSON' :: Monad m - => ([Block] -> m String) - -> ([Inline] -> m String) +metaToJSON' :: (Monad m, ToJSON a) + => ([Block] -> m a) + -> ([Inline] -> m a) -> Meta -> m Value metaToJSON' blockWriter inlineWriter (Meta metamap) = do @@ -98,9 +98,9 @@ addVariablesToJSON opts metadata = where combineMetadata (Object o1) (Object o2) = Object $ H.union o1 o2 combineMetadata x _ = x -metaValueToJSON :: Monad m - => ([Block] -> m String) - -> ([Inline] -> m String) +metaValueToJSON :: (Monad m, ToJSON a) + => ([Block] -> m a) + -> ([Inline] -> m a) -> MetaValue -> m Value metaValueToJSON blockWriter inlineWriter (MetaMap metamap) = liftM toJSON $ -- cgit v1.2.3 From fa719d026464619dd51714620470998ab5d18e17 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 10 Jun 2017 23:39:49 +0200 Subject: Switched Writer types to use Text. * XML.toEntities: changed type to Text -> Text. * Shared.tabFilter -- fixed so it strips out CRs as before. * Modified writers to take Text. * Updated tests, benchmarks, trypandoc. [API change] Closes #3731. --- src/Text/Pandoc/App.hs | 29 +++++++++++-------- src/Text/Pandoc/PDF.hs | 27 +++++++++--------- src/Text/Pandoc/Shared.hs | 4 +-- src/Text/Pandoc/Writers/AsciiDoc.hs | 11 +++++--- src/Text/Pandoc/Writers/CommonMark.hs | 13 ++++----- src/Text/Pandoc/Writers/ConTeXt.hs | 13 +++++---- src/Text/Pandoc/Writers/Custom.hs | 8 ++++-- src/Text/Pandoc/Writers/Docbook.hs | 14 +++++---- src/Text/Pandoc/Writers/DokuWiki.hs | 7 +++-- src/Text/Pandoc/Writers/EPUB.hs | 5 ++-- src/Text/Pandoc/Writers/FB2.hs | 7 +++-- src/Text/Pandoc/Writers/HTML.hs | 50 ++++++++++++++++++--------------- src/Text/Pandoc/Writers/Haddock.hs | 11 ++++---- src/Text/Pandoc/Writers/ICML.hs | 4 ++- src/Text/Pandoc/Writers/JATS.hs | 12 ++++---- src/Text/Pandoc/Writers/LaTeX.hs | 17 ++++++----- src/Text/Pandoc/Writers/Man.hs | 30 +++++++++++--------- src/Text/Pandoc/Writers/Markdown.hs | 23 ++++++++------- src/Text/Pandoc/Writers/MediaWiki.hs | 8 ++++-- src/Text/Pandoc/Writers/Ms.hs | 12 ++++---- src/Text/Pandoc/Writers/Muse.hs | 11 +++++--- src/Text/Pandoc/Writers/Native.hs | 3 +- src/Text/Pandoc/Writers/ODT.hs | 5 ++-- src/Text/Pandoc/Writers/OPML.hs | 15 ++++++---- src/Text/Pandoc/Writers/OpenDocument.hs | 10 ++++--- src/Text/Pandoc/Writers/Org.hs | 11 +++++--- src/Text/Pandoc/Writers/RST.hs | 13 +++++---- src/Text/Pandoc/Writers/RTF.hs | 7 +++-- src/Text/Pandoc/Writers/TEI.hs | 10 ++++--- src/Text/Pandoc/Writers/Texinfo.hs | 11 +++++--- src/Text/Pandoc/Writers/Textile.hs | 11 ++++---- src/Text/Pandoc/Writers/ZimWiki.hs | 8 +++--- src/Text/Pandoc/XML.hs | 11 ++++---- 33 files changed, 251 insertions(+), 180 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index c39bda859..658266046 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -43,6 +43,7 @@ import qualified Control.Exception as E import Control.Monad import Control.Monad.Except (throwError) import Control.Monad.Trans +import Data.Monoid import Data.Aeson (FromJSON (..), ToJSON (..), defaultOptions, eitherDecode', encode, genericToEncoding) import qualified Data.ByteString as BS @@ -183,7 +184,7 @@ convertWithOpts opts = do -- disabling the custom writer for now writer <- if ".lua" `isSuffixOf` format -- note: use non-lowercased version writerName - then return (StringWriter + then return (TextWriter (\o d -> liftIO $ writeCustom writerName o d) :: Writer PandocIO) else case getWriter writerName of @@ -442,7 +443,7 @@ convertWithOpts opts = do case writer of ByteStringWriter f -> f writerOptions doc >>= writeFnBinary outputFile - StringWriter f + TextWriter f | pdfOutput -> do -- make sure writer is latex, beamer, context, html5 or ms unless (laTeXOutput || conTeXtOutput || html5Output || @@ -469,18 +470,23 @@ convertWithOpts opts = do | otherwise -> do let htmlFormat = format `elem` ["html","html4","html5","s5","slidy","slideous","dzslides","revealjs"] - selfcontain = if optSelfContained opts && htmlFormat - then makeSelfContained writerOptions - else return handleEntities = if (htmlFormat || format == "docbook4" || format == "docbook5" || format == "docbook") && optAscii opts then toEntities else id - output <- f writerOptions doc - selfcontain (output ++ ['\n' | not standalone]) >>= - writerFn eol outputFile . handleEntities + addNl = if standalone + then id + else (<> T.singleton '\n') + output <- (addNl . handleEntities) <$> f writerOptions doc + writerFn eol outputFile =<< + if optSelfContained opts && htmlFormat + -- TODO not maximally efficient; change type + -- of makeSelfContained so it works w/ Text + then T.pack <$> makeSelfContained writerOptions + (T.unpack output) + else return output type Transform = Pandoc -> Pandoc @@ -810,9 +816,10 @@ writeFnBinary :: MonadIO m => FilePath -> B.ByteString -> m () writeFnBinary "-" = liftIO . B.putStr writeFnBinary f = liftIO . B.writeFile (UTF8.encodePath f) -writerFn :: MonadIO m => IO.Newline -> FilePath -> String -> m () -writerFn eol "-" = liftIO . UTF8.putStrWith eol -writerFn eol f = liftIO . UTF8.writeFileWith eol f +writerFn :: MonadIO m => IO.Newline -> FilePath -> Text -> m () +-- TODO this implementation isn't maximally efficient: +writerFn eol "-" = liftIO . UTF8.putStrWith eol . T.unpack +writerFn eol f = liftIO . UTF8.writeFileWith eol f . T.unpack lookupHighlightStyle :: Maybe String -> IO (Maybe Style) lookupHighlightStyle Nothing = return Nothing diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index e8a826e4c..cd75d869d 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -36,12 +36,13 @@ import qualified Codec.Picture as JP import qualified Control.Exception as E import Control.Monad (unless, when) import Control.Monad.Trans (MonadIO (..)) +import qualified Data.Text as T +import Data.Text (Text) import qualified Data.ByteString as BS import Data.ByteString.Lazy (ByteString) import qualified Data.ByteString.Lazy as B import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Lazy.Char8 as BC -import Data.List (isInfixOf) import Data.Maybe (fromMaybe) import Data.Monoid ((<>)) import System.Directory @@ -74,7 +75,7 @@ changePathSeparators = intercalate "/" . splitDirectories makePDF :: String -- ^ pdf creator (pdflatex, lualatex, -- xelatex, context, wkhtmltopdf, pdfroff) - -> (WriterOptions -> Pandoc -> PandocIO String) -- ^ writer + -> (WriterOptions -> Pandoc -> PandocIO Text) -- ^ writer -> WriterOptions -- ^ options -> Verbosity -- ^ verbosity level -> MediaBag -- ^ media @@ -178,10 +179,10 @@ tex2pdf' :: Verbosity -- ^ Verbosity level -> [String] -- ^ Arguments to the latex-engine -> FilePath -- ^ temp directory for output -> String -- ^ tex program - -> String -- ^ tex source + -> Text -- ^ tex source -> IO (Either ByteString ByteString) tex2pdf' verbosity args tmpDir program source = do - let numruns = if "\\tableofcontents" `isInfixOf` source + let numruns = if "\\tableofcontents" `T.isInfixOf` source then 3 -- to get page numbers else 2 -- 1 run won't give you PDF bookmarks (exit, log', mbPdf) <- runTeXProgram verbosity program args 1 numruns tmpDir source @@ -223,11 +224,11 @@ extractConTeXtMsg log' = do -- contents of stdout, contents of produced PDF if any). Rerun -- a fixed number of times to resolve references. runTeXProgram :: Verbosity -> String -> [String] -> Int -> Int -> FilePath - -> String -> IO (ExitCode, ByteString, Maybe ByteString) + -> Text -> 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 + unless exists $ BS.writeFile file $ UTF8.fromText source #ifdef _WINDOWS -- note: we want / even on Windows, for TexLive let tmpDir' = changePathSeparators tmpDir @@ -276,7 +277,7 @@ runTeXProgram verbosity program args runNumber numRuns tmpDir source = do ms2pdf :: Verbosity -> [String] - -> String + -> Text -> IO (Either ByteString ByteString) ms2pdf verbosity args source = do env' <- getEnvironment @@ -288,10 +289,10 @@ ms2pdf verbosity args source = do mapM_ print env' putStr "\n" putStrLn $ "[makePDF] Contents:\n" - putStr source + putStr $ T.unpack source putStr "\n" (exit, out) <- pipeProcess (Just env') "pdfroff" args - (UTF8.fromStringLazy source) + (BL.fromStrict $ UTF8.fromText source) when (verbosity >= INFO) $ do B.hPutStr stdout out putStr "\n" @@ -301,12 +302,12 @@ ms2pdf verbosity args source = do html2pdf :: Verbosity -- ^ Verbosity level -> [String] -- ^ Args to wkhtmltopdf - -> String -- ^ HTML5 source + -> Text -- ^ HTML5 source -> IO (Either ByteString ByteString) html2pdf verbosity args source = do file <- withTempFile "." "html2pdf.html" $ \fp _ -> return fp pdfFile <- withTempFile "." "html2pdf.pdf" $ \fp _ -> return fp - UTF8.writeFile file source + BS.writeFile file $ UTF8.fromText source let programArgs = args ++ [file, pdfFile] env' <- getEnvironment when (verbosity >= INFO) $ do @@ -341,11 +342,11 @@ html2pdf verbosity args source = do context2pdf :: Verbosity -- ^ Verbosity level -> FilePath -- ^ temp directory for output - -> String -- ^ ConTeXt source + -> Text -- ^ ConTeXt source -> IO (Either ByteString ByteString) context2pdf verbosity tmpDir source = inDirectory tmpDir $ do let file = "input.tex" - UTF8.writeFile file source + BS.writeFile file $ UTF8.fromText source #ifdef _WINDOWS -- note: we want / even on Windows, for TexLive let tmpDir' = changePathSeparators tmpDir diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 9ee80827f..745e809d0 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -284,8 +284,8 @@ escapeURI = escapeURIString (not . needsEscaping) tabFilter :: Int -- ^ Tab stop -> T.Text -- ^ Input -> T.Text -tabFilter tabStop = - T.unlines . (if tabStop == 0 then id else map go) . T.lines +tabFilter tabStop = T.filter (/= '\r') . T.unlines . + (if tabStop == 0 then id else map go) . T.lines where go s = let (s1, s2) = T.break (== '\t') s in if T.null s2 diff --git a/src/Text/Pandoc/Writers/AsciiDoc.hs b/src/Text/Pandoc/Writers/AsciiDoc.hs index e0085fb1a..46dbe6eaf 100644 --- a/src/Text/Pandoc/Writers/AsciiDoc.hs +++ b/src/Text/Pandoc/Writers/AsciiDoc.hs @@ -43,6 +43,7 @@ import Data.Char (isPunctuation, isSpace) import Data.List (intercalate, intersperse, stripPrefix) import qualified Data.Map as M import Data.Maybe (fromMaybe) +import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Definition @@ -62,7 +63,7 @@ data WriterState = WriterState { defListMarker :: String } -- | Convert Pandoc to AsciiDoc. -writeAsciiDoc :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeAsciiDoc :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeAsciiDoc opts document = evalStateT (pandocToAsciiDoc opts document) WriterState{ defListMarker = "::" @@ -74,16 +75,18 @@ writeAsciiDoc opts document = type ADW = StateT WriterState -- | Return asciidoc representation of document. -pandocToAsciiDoc :: PandocMonad m => WriterOptions -> Pandoc -> ADW m String +pandocToAsciiDoc :: PandocMonad m => WriterOptions -> Pandoc -> ADW m Text pandocToAsciiDoc opts (Pandoc meta blocks) = do let titleblock = not $ null (docTitle meta) && null (docAuthors meta) && null (docDate meta) let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing + let render' :: Doc -> Text + render' = render colwidth metadata <- metaToJSON opts - (fmap (render colwidth) . blockListToAsciiDoc opts) - (fmap (render colwidth) . inlineListToAsciiDoc opts) + (fmap render' . blockListToAsciiDoc opts) + (fmap render' . inlineListToAsciiDoc opts) meta let addTitleLine (String t) = String $ t <> "\n" <> T.replicate (T.length t) "=" diff --git a/src/Text/Pandoc/Writers/CommonMark.hs b/src/Text/Pandoc/Writers/CommonMark.hs index 5e0a06bf0..ed316ced9 100644 --- a/src/Text/Pandoc/Writers/CommonMark.hs +++ b/src/Text/Pandoc/Writers/CommonMark.hs @@ -34,6 +34,7 @@ module Text.Pandoc.Writers.CommonMark (writeCommonMark) where import CMark import Control.Monad.State (State, get, modify, runState) import Data.Foldable (foldrM) +import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Definition @@ -45,7 +46,7 @@ import Text.Pandoc.Writers.HTML (writeHtml5String) import Text.Pandoc.Writers.Shared -- | Convert Pandoc to CommonMark. -writeCommonMark :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeCommonMark :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeCommonMark opts (Pandoc meta blocks) = do let (blocks', notes) = runState (walkM processNotes blocks) [] notes' = if null notes @@ -71,7 +72,7 @@ processNotes x = return x node :: NodeType -> [Node] -> Node node = Node Nothing -blocksToCommonMark :: PandocMonad m => WriterOptions -> [Block] -> m String +blocksToCommonMark :: PandocMonad m => WriterOptions -> [Block] -> m Text blocksToCommonMark opts bs = do let cmarkOpts = [optHardBreaks | isEnabled Ext_hard_line_breaks opts] colwidth = if writerWrapText opts == WrapAuto @@ -79,14 +80,12 @@ blocksToCommonMark opts bs = do else Nothing nodes <- blocksToNodes bs return $ - T.unpack $ nodeToCommonmark cmarkOpts colwidth $ node DOCUMENT nodes -inlinesToCommonMark :: PandocMonad m => WriterOptions -> [Inline] -> m String +inlinesToCommonMark :: PandocMonad m => WriterOptions -> [Inline] -> m Text inlinesToCommonMark opts ils = return $ - T.unpack $ nodeToCommonmark cmarkOpts colwidth - $ node PARAGRAPH (inlinesToNodes ils) + nodeToCommonmark cmarkOpts colwidth $ node PARAGRAPH (inlinesToNodes ils) where cmarkOpts = [optHardBreaks | isEnabled Ext_hard_line_breaks opts] colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts @@ -139,7 +138,7 @@ blockToNodes (DefinitionList items) ns = blockToNodes (BulletList items') ns Para term : concat xs blockToNodes t@(Table _ _ _ _ _) ns = do s <- writeHtml5String def $! Pandoc nullMeta [t] - return (node (HTML_BLOCK (T.pack $! s)) [] : ns) + return (node (HTML_BLOCK s) [] : ns) blockToNodes Null ns = return ns inlinesToNodes :: [Inline] -> [Node] diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index 2d4502153..2da6a7f9a 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -33,6 +33,7 @@ import Control.Monad.State import Data.Char (ord) import Data.List (intercalate, intersperse) import Data.Maybe (catMaybes) +import Data.Text (Text) import Network.URI (unEscapeString) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Logging @@ -56,7 +57,7 @@ orderedListStyles :: [Char] orderedListStyles = cycle "narg" -- | Convert Pandoc to ConTeXt. -writeConTeXt :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeConTeXt :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeConTeXt options document = let defaultWriterState = WriterState { stNextRef = 1 , stOrderedListLevel = 0 @@ -66,17 +67,19 @@ writeConTeXt options document = type WM = StateT WriterState -pandocToConTeXt :: PandocMonad m => WriterOptions -> Pandoc -> WM m String +pandocToConTeXt :: PandocMonad m => WriterOptions -> Pandoc -> WM m Text pandocToConTeXt options (Pandoc meta blocks) = do let colwidth = if writerWrapText options == WrapAuto then Just $ writerColumns options else Nothing + let render' :: Doc -> Text + render' = render colwidth metadata <- metaToJSON options - (fmap (render colwidth) . blockListToConTeXt) - (fmap (render colwidth) . inlineListToConTeXt) + (fmap render' . blockListToConTeXt) + (fmap render' . inlineListToConTeXt) meta body <- mapM (elementToConTeXt options) $ hierarchicalize blocks - let main = (render colwidth . vcat) body + let main = (render' . vcat) body let layoutFromMargins = intercalate [','] $ catMaybes $ map (\(x,y) -> ((x ++ "=") ++) <$> getField y metadata) diff --git a/src/Text/Pandoc/Writers/Custom.hs b/src/Text/Pandoc/Writers/Custom.hs index b33acb17c..1314ef844 100644 --- a/src/Text/Pandoc/Writers/Custom.hs +++ b/src/Text/Pandoc/Writers/Custom.hs @@ -41,6 +41,7 @@ import Control.Monad (when) import Data.Char (toLower) import Data.List (intersperse) import qualified Data.Map as M +import Data.Text (Text, pack) import Data.Typeable import GHC.IO.Encoding (getForeignEncoding, setForeignEncoding, utf8) import Scripting.Lua (LuaState, StackValue, callfunc) @@ -116,7 +117,7 @@ data PandocLuaException = PandocLuaException String instance Exception PandocLuaException -- | Convert Pandoc to custom markup. -writeCustom :: FilePath -> WriterOptions -> Pandoc -> IO String +writeCustom :: FilePath -> WriterOptions -> Pandoc -> IO Text writeCustom luaFile opts doc@(Pandoc meta _) = do luaScript <- UTF8.readFile luaFile enc <- getForeignEncoding @@ -139,8 +140,9 @@ writeCustom luaFile opts doc@(Pandoc meta _) = do setForeignEncoding enc let body = rendered case writerTemplate opts of - Nothing -> return body - Just tpl -> return $ renderTemplate' tpl $ setField "body" body context + Nothing -> return $ pack body + Just tpl -> return $ pack $ + renderTemplate' tpl $ setField "body" body context docToCustom :: LuaState -> WriterOptions -> Pandoc -> IO String docToCustom lua opts (Pandoc (Meta metamap) blocks) = do diff --git a/src/Text/Pandoc/Writers/Docbook.hs b/src/Text/Pandoc/Writers/Docbook.hs index 1afdfc457..02ffbf831 100644 --- a/src/Text/Pandoc/Writers/Docbook.hs +++ b/src/Text/Pandoc/Writers/Docbook.hs @@ -32,6 +32,7 @@ Conversion of 'Pandoc' documents to Docbook XML. module Text.Pandoc.Writers.Docbook ( writeDocbook4, writeDocbook5 ) where import Control.Monad.Reader import Data.Char (toLower) +import Data.Text (Text) import Data.Generics (everywhere, mkT) import Data.List (intercalate, isPrefixOf, isSuffixOf, stripPrefix) import Data.Monoid (Any (..)) @@ -81,22 +82,23 @@ authorToDocbook opts name' = do in inTagsSimple "firstname" (text $ escapeStringForXML firstname) $$ inTagsSimple "surname" (text $ escapeStringForXML lastname) -writeDocbook4 :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeDocbook4 :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeDocbook4 opts d = runReaderT (writeDocbook opts d) DocBook4 -writeDocbook5 :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeDocbook5 :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeDocbook5 opts d = runReaderT (writeDocbook opts d) DocBook5 -- | Convert Pandoc document to string in Docbook format. -writeDocbook :: PandocMonad m => WriterOptions -> Pandoc -> DB m String +writeDocbook :: PandocMonad m => WriterOptions -> Pandoc -> DB m Text writeDocbook opts (Pandoc meta blocks) = do let elements = hierarchicalize blocks let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing - let render' = render colwidth + let render' :: Doc -> Text + render' = render colwidth let opts' = if (maybe False (("/book>" `isSuffixOf`) . trimr) (writerTemplate opts) && TopLevelDefault == writerTopLevelDivision opts) @@ -111,10 +113,10 @@ writeDocbook opts (Pandoc meta blocks) = do auths' <- mapM (authorToDocbook opts) $ docAuthors meta let meta' = B.setMeta "author" auths' meta metadata <- metaToJSON opts - (fmap (render colwidth . vcat) . + (fmap (render' . vcat) . (mapM (elementToDocbook opts' startLvl) . hierarchicalize)) - (fmap (render colwidth) . inlinesToDocbook opts') + (fmap render' . inlinesToDocbook opts') meta' main <- (render' . vcat) <$> (mapM (elementToDocbook opts' startLvl) elements) let context = defField "body" main diff --git a/src/Text/Pandoc/Writers/DokuWiki.hs b/src/Text/Pandoc/Writers/DokuWiki.hs index 1d02a9c40..551a1b0b5 100644 --- a/src/Text/Pandoc/Writers/DokuWiki.hs +++ b/src/Text/Pandoc/Writers/DokuWiki.hs @@ -44,6 +44,7 @@ import Control.Monad.Reader (ReaderT, ask, local, runReaderT) import Control.Monad.State (StateT, evalStateT, gets, modify) import Data.Default (Default (..)) import Data.List (intercalate, intersect, isPrefixOf, transpose) +import Data.Text (Text, pack) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Logging import Text.Pandoc.Definition @@ -75,7 +76,7 @@ instance Default WriterEnvironment where type DokuWiki m = ReaderT WriterEnvironment (StateT WriterState m) -- | Convert Pandoc to DokuWiki. -writeDokuWiki :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeDokuWiki :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeDokuWiki opts document = runDokuWiki (pandocToDokuWiki opts document) @@ -84,7 +85,7 @@ runDokuWiki = flip evalStateT def . flip runReaderT def -- | Return DokuWiki representation of document. pandocToDokuWiki :: PandocMonad m - => WriterOptions -> Pandoc -> DokuWiki m String + => WriterOptions -> Pandoc -> DokuWiki m Text pandocToDokuWiki opts (Pandoc meta blocks) = do metadata <- metaToJSON opts (fmap trimr . blockListToDokuWiki opts) @@ -96,7 +97,7 @@ pandocToDokuWiki opts (Pandoc meta blocks) = do then "" -- TODO Was "\n<references />" Check whether I can really remove this: -- if it is definitely to do with footnotes, can remove this whole bit else "" - let main = body ++ notes + let main = pack $ body ++ notes let context = defField "body" main $ defField "toc" (writerTableOfContents opts) $ metadata diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index c8d64cf0b..d68283007 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -40,6 +40,7 @@ import Control.Monad.State (State, StateT, evalState, evalStateT, get, gets, lift, modify, put) import qualified Data.ByteString.Lazy as B import qualified Data.ByteString.Lazy.Char8 as B8 +import qualified Data.Text.Lazy as TL import Data.Char (isAlphaNum, isDigit, toLower) import Data.List (intercalate, isInfixOf, isPrefixOf) import qualified Data.Map as M @@ -373,8 +374,8 @@ pandocToEPUB :: PandocMonad m -> E m B.ByteString pandocToEPUB version opts doc@(Pandoc meta _) = do let epub3 = version == EPUB3 - let writeHtml o = fmap UTF8.fromStringLazy . - writeHtmlStringForEPUB version o + let writeHtml o = fmap (UTF8.fromTextLazy . TL.fromStrict) . + writeHtmlStringForEPUB version o epochtime <- floor <$> lift P.getPOSIXTime metadata <- getEPUBMetadata opts meta let mkEntry path content = toEntry path epochtime content diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index d450513bc..213756330 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -44,6 +44,7 @@ import Data.ByteString.Base64 (encode) import qualified Data.ByteString.Char8 as B8 import Data.Char (isAscii, isControl, isSpace, toLower) import Data.Either (lefts, rights) +import Data.Text (Text, pack) import Data.List (intercalate, intersperse, isPrefixOf, stripPrefix) import Network.HTTP (urlEncode) import Text.XML.Light @@ -86,13 +87,13 @@ instance Show ImageMode where writeFB2 :: PandocMonad m => WriterOptions -- ^ conversion options -> Pandoc -- ^ document to convert - -> m String -- ^ FictionBook2 document (not encoded yet) + -> m Text -- ^ FictionBook2 document (not encoded yet) writeFB2 opts doc = flip evalStateT newFB $ pandocToFB2 opts doc pandocToFB2 :: PandocMonad m => WriterOptions -> Pandoc - -> FBM m String + -> FBM m Text pandocToFB2 opts (Pandoc meta blocks) = do modify (\s -> s { writerOptions = opts }) desc <- description meta @@ -103,7 +104,7 @@ pandocToFB2 opts (Pandoc meta blocks) = do (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" + return $ pack $ xml_head ++ (showContent fb2_xml) ++ "\n" where xml_head = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" fb2_attrs = diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 2605a29aa..5ee8ab4ce 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -45,6 +45,8 @@ module Text.Pandoc.Writers.HTML ( ) where import Control.Monad.State import Data.Char (ord, toLower) +import Data.Text (Text) +import qualified Data.Text.Lazy as TL import Data.List (intersperse, isPrefixOf) import Data.Maybe (catMaybes, fromMaybe, isJust, isNothing) import Data.Monoid ((<>)) @@ -67,7 +69,7 @@ import Text.Pandoc.Writers.Shared import Text.Pandoc.XML (escapeStringForXML, fromEntities) #if MIN_VERSION_blaze_markup(0,6,3) #else -import Text.Blaze.Internal (preEscapedString) +import Text.Blaze.Internal (preEscapedString, preEscapedText) #endif #if MIN_VERSION_blaze_html(0,5,1) import qualified Text.Blaze.XHtml5 as H5 @@ -77,7 +79,7 @@ import qualified Text.Blaze.Html5 as H5 import Control.Monad.Except (throwError) import Data.Aeson (Value) import System.FilePath (takeExtension, takeBaseName) -import Text.Blaze.Html.Renderer.String (renderHtml) +import Text.Blaze.Html.Renderer.Text (renderHtml) import qualified Text.Blaze.XHtml1.Transitional as H import qualified Text.Blaze.XHtml1.Transitional.Attributes as A import Text.Pandoc.Class (PandocMonad, report) @@ -123,7 +125,7 @@ nl opts = if writerWrapText opts == WrapNone else preEscapedString "\n" -- | Convert Pandoc document to Html 5 string. -writeHtml5String :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeHtml5String :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeHtml5String = writeHtmlString' defaultWriterState{ stHtml5 = True } @@ -132,7 +134,7 @@ writeHtml5 :: PandocMonad m => WriterOptions -> Pandoc -> m Html writeHtml5 = writeHtml' defaultWriterState{ stHtml5 = True } -- | Convert Pandoc document to Html 4 string. -writeHtml4String :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeHtml4String :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeHtml4String = writeHtmlString' defaultWriterState{ stHtml5 = False } @@ -142,38 +144,39 @@ 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 = writeHtmlString' + => EPUBVersion -> WriterOptions -> Pandoc + -> m Text +writeHtmlStringForEPUB version o = writeHtmlString' defaultWriterState{ stHtml5 = version == EPUB3, - stEPUBVersion = Just version } + stEPUBVersion = Just version } o -- | Convert Pandoc document to Reveal JS HTML slide show. writeRevealJs :: PandocMonad m - => WriterOptions -> Pandoc -> m String + => WriterOptions -> Pandoc -> m Text writeRevealJs = writeHtmlSlideShow' RevealJsSlides -- | Convert Pandoc document to S5 HTML slide show. writeS5 :: PandocMonad m - => WriterOptions -> Pandoc -> m String + => WriterOptions -> Pandoc -> m Text writeS5 = writeHtmlSlideShow' S5Slides -- | Convert Pandoc document to Slidy HTML slide show. writeSlidy :: PandocMonad m - => WriterOptions -> Pandoc -> m String + => WriterOptions -> Pandoc -> m Text writeSlidy = writeHtmlSlideShow' SlidySlides -- | Convert Pandoc document to Slideous HTML slide show. writeSlideous :: PandocMonad m - => WriterOptions -> Pandoc -> m String + => WriterOptions -> Pandoc -> m Text writeSlideous = writeHtmlSlideShow' SlideousSlides -- | Convert Pandoc document to DZSlides HTML slide show. writeDZSlides :: PandocMonad m - => WriterOptions -> Pandoc -> m String + => WriterOptions -> Pandoc -> m Text writeDZSlides = writeHtmlSlideShow' DZSlides writeHtmlSlideShow' :: PandocMonad m - => HTMLSlideVariant -> WriterOptions -> Pandoc -> m String + => HTMLSlideVariant -> WriterOptions -> Pandoc -> m Text writeHtmlSlideShow' variant = writeHtmlString' defaultWriterState{ stSlideVariant = variant , stHtml5 = case variant of @@ -185,12 +188,15 @@ writeHtmlSlideShow' variant = writeHtmlString' NoSlides -> False } +renderHtml' :: Html -> Text +renderHtml' = TL.toStrict . renderHtml + writeHtmlString' :: PandocMonad m - => WriterState -> WriterOptions -> Pandoc -> m String + => WriterState -> WriterOptions -> Pandoc -> m Text writeHtmlString' st opts d = do (body, context) <- evalStateT (pandocToHtml opts d) st case writerTemplate opts of - Nothing -> return $ renderHtml body + Nothing -> return $ renderHtml' body Just tpl -> do -- warn if empty lang when (isNothing (getField "lang" context :: Maybe String)) $ @@ -205,12 +211,12 @@ writeHtmlString' st opts d = do report $ NoTitleElement fallback return $ resetField "pagetitle" fallback context return $ renderTemplate' tpl $ - defField "body" (renderHtml body) context' + defField "body" (renderHtml' body) context' writeHtml' :: PandocMonad m => WriterState -> WriterOptions -> Pandoc -> m Html writeHtml' st opts d = do case writerTemplate opts of - Just _ -> preEscapedString <$> writeHtmlString' st opts d + Just _ -> preEscapedText <$> writeHtmlString' st opts d Nothing -> do (body, _) <- evalStateT (pandocToHtml opts d) st return body @@ -222,8 +228,8 @@ pandocToHtml :: PandocMonad m -> StateT WriterState m (Html, Value) pandocToHtml opts (Pandoc meta blocks) = do metadata <- metaToJSON opts - (fmap renderHtml . blockListToHtml opts) - (fmap renderHtml . inlineListToHtml opts) + (fmap renderHtml' . blockListToHtml opts) + (fmap renderHtml' . inlineListToHtml opts) meta let stringifyHTML = escapeStringForXML . stringify let authsMeta = map stringifyHTML $ docAuthors meta @@ -277,10 +283,10 @@ pandocToHtml opts (Pandoc meta blocks) = do Nothing -> id else id) $ (if stMath st - then defField "math" (renderHtml math) + then defField "math" (renderHtml' math) else id) $ defField "quotes" (stQuotes st) $ - maybe id (defField "toc" . renderHtml) toc $ + maybe id (defField "toc" . renderHtml') toc $ defField "author-meta" authsMeta $ maybe id (defField "date-meta") (normalizeDate dateMeta) $ defField "pagetitle" (stringifyHTML (docTitle meta)) $ @@ -463,7 +469,7 @@ parseMailto s = do obfuscateLink :: PandocMonad m => WriterOptions -> Attr -> Html -> String -> m Html obfuscateLink opts attr txt s | writerEmailObfuscation opts == NoObfuscation = return $ addAttrs opts attr $ H.a ! A.href (toValue s) $ txt -obfuscateLink opts attr (renderHtml -> txt) s = +obfuscateLink opts attr (TL.unpack . renderHtml -> txt) s = let meth = writerEmailObfuscation opts s' = map toLower (take 7 s) ++ drop 7 s in case parseMailto s' of diff --git a/src/Text/Pandoc/Writers/Haddock.hs b/src/Text/Pandoc/Writers/Haddock.hs index cbbe5bdb4..1ad9acd40 100644 --- a/src/Text/Pandoc/Writers/Haddock.hs +++ b/src/Text/Pandoc/Writers/Haddock.hs @@ -35,6 +35,7 @@ Haddock: <http://www.haskell.org/haddock/doc/html/> module Text.Pandoc.Writers.Haddock (writeHaddock) where import Control.Monad.State import Data.Default +import Data.Text (Text) import Data.List (intersperse, transpose) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Definition @@ -52,14 +53,14 @@ instance Default WriterState where def = WriterState{ stNotes = [] } -- | Convert Pandoc to Haddock. -writeHaddock :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeHaddock :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeHaddock opts document = evalStateT (pandocToHaddock opts{ writerWrapText = writerWrapText opts } document) def -- | Return haddock representation of document. pandocToHaddock :: PandocMonad m - => WriterOptions -> Pandoc -> StateT WriterState m String + => WriterOptions -> Pandoc -> StateT WriterState m Text pandocToHaddock opts (Pandoc meta blocks) = do let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts @@ -67,13 +68,13 @@ pandocToHaddock opts (Pandoc meta blocks) = do body <- blockListToHaddock opts blocks st <- get notes' <- notesToHaddock opts (reverse $ stNotes st) - let render' :: Doc -> String + let render' :: Doc -> Text render' = render colwidth let main = render' $ body <> (if isEmpty notes' then empty else blankline <> notes') metadata <- metaToJSON opts - (fmap (render colwidth) . blockListToHaddock opts) - (fmap (render colwidth) . inlineListToHaddock opts) + (fmap render' . blockListToHaddock opts) + (fmap render' . inlineListToHaddock opts) meta let context = defField "body" main $ metadata diff --git a/src/Text/Pandoc/Writers/ICML.hs b/src/Text/Pandoc/Writers/ICML.hs index f36a32015..2884bc532 100644 --- a/src/Text/Pandoc/Writers/ICML.hs +++ b/src/Text/Pandoc/Writers/ICML.hs @@ -21,6 +21,7 @@ import Control.Monad.State import Data.List (intersperse, isInfixOf, isPrefixOf, stripPrefix) import qualified Data.Set as Set import Data.Text as Text (breakOnAll, pack) +import Data.Text (Text) import Text.Pandoc.Class (PandocMonad, report) import qualified Text.Pandoc.Class as P import Text.Pandoc.Definition @@ -127,11 +128,12 @@ footnoteName = "Footnote" citeName = "Cite" -- | Convert Pandoc document to string in ICML format. -writeICML :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeICML :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeICML opts (Pandoc meta blocks) = do let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing + render' :: Doc -> Text render' = render colwidth renderMeta f s = liftM (render' . fst) $ runStateT (f opts [] s) defaultWriterState metadata <- metaToJSON opts diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs index 0b5108a79..1a8d80747 100644 --- a/src/Text/Pandoc/Writers/JATS.hs +++ b/src/Text/Pandoc/Writers/JATS.hs @@ -33,6 +33,7 @@ https://jats.nlm.nih.gov/publishing/tag-library/1.1d3/element/mml-math.html module Text.Pandoc.Writers.JATS ( writeJATS ) where import Control.Monad.Reader import Data.Char (toLower) +import Data.Text (Text) import Data.Generics (everywhere, mkT) import Data.List (intercalate, isSuffixOf, partition) import Data.Maybe (fromMaybe) @@ -81,12 +82,12 @@ authorToJATS opts name' = do in inTagsSimple "firstname" (text $ escapeStringForXML firstname) $$ inTagsSimple "surname" (text $ escapeStringForXML lastname) -writeJATS :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeJATS :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeJATS opts d = runReaderT (docToJATS opts d) JATS1_1 -- | Convert Pandoc document to string in JATS format. -docToJATS :: PandocMonad m => WriterOptions -> Pandoc -> DB m String +docToJATS :: PandocMonad m => WriterOptions -> Pandoc -> DB m Text docToJATS opts (Pandoc meta blocks) = do let isBackBlock (Div ("refs",_,_) _) = True isBackBlock _ = False @@ -96,7 +97,8 @@ docToJATS opts (Pandoc meta blocks) = do let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing - let render' = render colwidth + let render' :: Doc -> Text + render' = render colwidth let opts' = if (maybe False (("/book>" `isSuffixOf`) . trimr) (writerTemplate opts) && TopLevelDefault == writerTopLevelDivision opts) @@ -111,10 +113,10 @@ docToJATS opts (Pandoc meta blocks) = do auths' <- mapM (authorToJATS opts) $ docAuthors meta let meta' = B.setMeta "author" auths' meta metadata <- metaToJSON opts - (fmap (render colwidth . vcat) . + (fmap (render' . vcat) . (mapM (elementToJATS opts' startLvl) . hierarchicalize)) - (fmap (render colwidth) . inlinesToJATS opts') + (fmap render' . inlinesToJATS opts') meta' main <- (render' . vcat) <$> (mapM (elementToJATS opts' startLvl) elements) diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 2b3d7c878..80606d510 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -42,6 +42,7 @@ import Data.Char (isAlphaNum, isAscii, isDigit, isLetter, isPunctuation, ord, import Data.List (foldl', intercalate, intersperse, isInfixOf, nub, nubBy, stripPrefix, (\\)) import Data.Maybe (catMaybes, fromMaybe, isJust) +import Data.Text (Text) import qualified Data.Text as T import Network.URI (unEscapeString) import Text.Pandoc.Class (PandocMonad, report) @@ -114,13 +115,13 @@ startingState options = WriterState { , stEmptyLine = True } -- | Convert Pandoc to LaTeX. -writeLaTeX :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeLaTeX :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeLaTeX options document = evalStateT (pandocToLaTeX options document) $ startingState options -- | Convert Pandoc to LaTeX Beamer. -writeBeamer :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeBeamer :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeBeamer options document = evalStateT (pandocToLaTeX options document) $ (startingState options){ stBeamer = True } @@ -128,7 +129,7 @@ writeBeamer options document = type LW m = StateT WriterState m pandocToLaTeX :: PandocMonad m - => WriterOptions -> Pandoc -> LW m String + => WriterOptions -> Pandoc -> LW m Text pandocToLaTeX options (Pandoc meta blocks) = do -- Strip off final 'references' header if --natbib or --biblatex let method = writerCiteMethod options @@ -146,9 +147,11 @@ pandocToLaTeX options (Pandoc meta blocks) = do let colwidth = if writerWrapText options == WrapAuto then Just $ writerColumns options else Nothing + let render' :: Doc -> Text + render' = render colwidth metadata <- metaToJSON options - (fmap (render colwidth) . blockListToLaTeX) - (fmap (render colwidth) . inlineListToLaTeX) + (fmap render' . blockListToLaTeX) + (fmap render' . inlineListToLaTeX) meta let bookClasses = ["memoir","book","report","scrreprt","scrbook"] let documentClass = case P.parse pDocumentClass "template" template of @@ -180,8 +183,8 @@ pandocToLaTeX options (Pandoc meta blocks) = do then toSlides blocks'' else return blocks'' body <- mapM (elementToLaTeX options) $ hierarchicalize blocks''' - (biblioTitle :: String) <- liftM (render colwidth) $ inlineListToLaTeX lastHeader - let main = render colwidth $ vsep body + (biblioTitle :: Text) <- render' <$> inlineListToLaTeX lastHeader + let main = render' $ vsep body st <- get titleMeta <- stringToLaTeX TextString $ stringify $ docTitle meta authorsMeta <- mapM (stringToLaTeX TextString . stringify) $ docAuthors meta diff --git a/src/Text/Pandoc/Writers/Man.hs b/src/Text/Pandoc/Writers/Man.hs index f3d356de7..0fc6afbdc 100644 --- a/src/Text/Pandoc/Writers/Man.hs +++ b/src/Text/Pandoc/Writers/Man.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE OverloadedStrings #-} {- Copyright (C) 2007-2017 John MacFarlane <jgm@berkeley.edu> @@ -34,6 +35,8 @@ import Control.Monad.State import Data.List (intercalate, intersperse, stripPrefix, sort) import qualified Data.Map as Map import Data.Maybe (fromMaybe) +import Data.Text (Text) +import qualified Data.Text as T import Text.Pandoc.Builder (deleteMeta) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Definition @@ -62,36 +65,37 @@ defaultWriterState = WriterState { stNotes = [] , stHasTables = False } -- | Convert Pandoc to Man. -writeMan :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeMan :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeMan opts document = evalStateT (pandocToMan opts document) defaultWriterState -- | Return groff man representation of document. -pandocToMan :: PandocMonad m => WriterOptions -> Pandoc -> StateT WriterState m String +pandocToMan :: PandocMonad m => WriterOptions -> Pandoc -> StateT WriterState m Text pandocToMan opts (Pandoc meta blocks) = do let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing - let render' = render colwidth + let render' :: Doc -> Text + render' = render colwidth titleText <- inlineListToMan opts $ docTitle meta let title' = render' titleText let setFieldsFromTitle = - case break (== ' ') title' of - (cmdName, rest) -> case break (=='(') cmdName of - (xs, '(':ys) | not (null ys) && - last ys == ')' -> + case T.break (== ' ') title' of + (cmdName, rest) -> case T.break (=='(') cmdName of + (xs, ys) | "(" `T.isPrefixOf` ys + && ")" `T.isSuffixOf` ys -> defField "title" xs . - defField "section" (init ys) . - case splitBy (=='|') rest of + defField "section" (T.init $ T.drop 1 ys) . + case T.splitOn "|" rest of (ft:hds) -> - defField "footer" (trim ft) . + defField "footer" (T.strip ft) . defField "header" - (trim $ concat hds) + (T.strip $ mconcat hds) [] -> id _ -> defField "title" title' metadata <- metaToJSON opts - (fmap (render colwidth) . blockListToMan opts) - (fmap (render colwidth) . inlineListToMan opts) + (fmap render' . blockListToMan opts) + (fmap render' . inlineListToMan opts) $ deleteMeta "title" meta body <- blockListToMan opts blocks notes <- gets stNotes diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 989d5af9d..69243a214 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -45,6 +45,7 @@ import Data.Maybe (fromMaybe) import Data.Monoid (Any (..)) import Data.Ord (comparing) import qualified Data.Set as Set +import Data.Text (Text) import qualified Data.Text as T import qualified Data.Vector as V import Data.Yaml (Value (Array, Bool, Number, Object, String)) @@ -106,7 +107,7 @@ instance Default WriterState } -- | Convert Pandoc to Markdown. -writeMarkdown :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeMarkdown :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeMarkdown opts document = evalMD (pandocToMarkdown opts{ writerWrapText = if isEnabled Ext_hard_line_breaks opts @@ -116,7 +117,7 @@ writeMarkdown opts document = -- | Convert Pandoc to plain text (like markdown, but without links, -- pictures, or inline formatting). -writePlain :: PandocMonad m => WriterOptions -> Pandoc -> m String +writePlain :: PandocMonad m => WriterOptions -> Pandoc -> m Text writePlain opts document = evalMD (pandocToMarkdown opts document) def{ envPlain = True } def @@ -180,15 +181,17 @@ jsonToYaml (Number n) = text $ show n jsonToYaml _ = empty -- | Return markdown representation of document. -pandocToMarkdown :: PandocMonad m => WriterOptions -> Pandoc -> MD m String +pandocToMarkdown :: PandocMonad m => WriterOptions -> Pandoc -> MD m Text pandocToMarkdown opts (Pandoc meta blocks) = do let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing isPlain <- asks envPlain + let render' :: Doc -> Text + render' = render colwidth . chomp metadata <- metaToJSON' - (fmap (render colwidth) . blockListToMarkdown opts) - (fmap (render colwidth) . blockToMarkdown opts . Plain) + (fmap render' . blockListToMarkdown opts) + (fmap render' . blockToMarkdown opts . Plain) meta let title' = maybe empty text $ getField "title" metadata let authors' = maybe [] (map text) $ getField "author" metadata @@ -216,8 +219,6 @@ pandocToMarkdown opts (Pandoc meta blocks) = do else blocks body <- blockListToMarkdown opts blocks' notesAndRefs' <- notesAndRefs opts - let render' :: Doc -> String - render' = render colwidth . chomp let main = render' $ body <> notesAndRefs' let context = defField "toc" (render' toc) $ defField "body" main @@ -571,7 +572,7 @@ blockToMarkdown' opts t@(Table caption aligns widths headers rows) = do gridTable opts blockListToMarkdown (all null headers) aligns' widths' headers rows | isEnabled Ext_raw_html opts -> fmap (id,) $ - text <$> + (text . T.unpack) <$> (writeHtml5String def $ Pandoc nullMeta [t]) | otherwise -> return $ (id, text "[TABLE]") return $ nst $ tbl $$ caption'' $$ blankline @@ -1110,7 +1111,8 @@ 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) <$> writeHtml5String def (Pandoc nullMeta [Plain [lnk]]) + (text . T.unpack . T.strip) <$> + writeHtml5String def (Pandoc nullMeta [Plain [lnk]]) | otherwise = do plain <- asks envPlain linktext <- inlineListToMarkdown opts txt @@ -1149,7 +1151,8 @@ 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) <$> writeHtml5String def (Pandoc nullMeta [Plain [img]]) + (text . T.unpack . T.strip) <$> + 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/MediaWiki.hs b/src/Text/Pandoc/Writers/MediaWiki.hs index aa5c3bc4f..c70e5b786 100644 --- a/src/Text/Pandoc/Writers/MediaWiki.hs +++ b/src/Text/Pandoc/Writers/MediaWiki.hs @@ -34,6 +34,7 @@ import Control.Monad.Reader import Control.Monad.State import Data.List (intercalate) import qualified Data.Set as Set +import Data.Text (Text, pack) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Logging import Text.Pandoc.Definition @@ -59,14 +60,14 @@ data WriterReader = WriterReader { type MediaWikiWriter m = ReaderT WriterReader (StateT WriterState m) -- | Convert Pandoc to MediaWiki. -writeMediaWiki :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeMediaWiki :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeMediaWiki opts document = let initialState = WriterState { stNotes = False, stOptions = opts } env = WriterReader { options = opts, listLevel = [], useTags = False } in evalStateT (runReaderT (pandocToMediaWiki document) env) initialState -- | Return MediaWiki representation of document. -pandocToMediaWiki :: PandocMonad m => Pandoc -> MediaWikiWriter m String +pandocToMediaWiki :: PandocMonad m => Pandoc -> MediaWikiWriter m Text pandocToMediaWiki (Pandoc meta blocks) = do opts <- asks options metadata <- metaToJSON opts @@ -81,7 +82,8 @@ pandocToMediaWiki (Pandoc meta blocks) = do let main = body ++ notes let context = defField "body" main $ defField "toc" (writerTableOfContents opts) metadata - return $ case writerTemplate opts of + return $ pack + $ case writerTemplate opts of Nothing -> main Just tpl -> renderTemplate' tpl context diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 5dd225e19..c5c3d9f5b 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -44,6 +44,7 @@ import Text.Pandoc.Options import Text.Pandoc.Writers.Math import Text.Printf ( printf ) import qualified Data.Text as T +import Data.Text (Text) import qualified Data.Map as Map import Data.Maybe ( catMaybes, fromMaybe ) import Data.List ( intersperse, intercalate, sort ) @@ -85,17 +86,18 @@ type Note = [Block] type MS = StateT WriterState -- | Convert Pandoc to Ms. -writeMs :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeMs :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeMs opts document = evalStateT (pandocToMs opts document) defaultWriterState -- | Return groff ms representation of document. -pandocToMs :: PandocMonad m => WriterOptions -> Pandoc -> MS m String +pandocToMs :: PandocMonad m => WriterOptions -> Pandoc -> MS m Text pandocToMs opts (Pandoc meta blocks) = do let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing - let render' = render colwidth + let render' :: Doc -> Text + render' = render colwidth metadata <- metaToJSON opts (fmap render' . blockListToMs opts) (fmap render' . inlineListToMs' opts) @@ -108,9 +110,9 @@ pandocToMs opts (Pandoc meta blocks) = do hasHighlighting <- gets stHighlighting let highlightingMacros = if hasHighlighting then case writerHighlightStyle opts of - Nothing -> "" + Nothing -> mempty Just sty -> render' $ styleToMs sty - else "" + else mempty let context = defField "body" main $ defField "has-inline-math" hasInlineMath diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index ccc6e9aef..85e0b5467 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -43,6 +43,7 @@ even though it is supported only in Emacs Muse. -} module Text.Pandoc.Writers.Muse (writeMuse) where import Control.Monad.State +import Data.Text (Text) import Data.List (intersperse, transpose, isInfixOf) import System.FilePath (takeExtension) import Text.Pandoc.Class (PandocMonad) @@ -68,7 +69,7 @@ data WriterState = writeMuse :: PandocMonad m => WriterOptions -> Pandoc - -> m String + -> m Text writeMuse opts document = let st = WriterState { stNotes = [] , stOptions = opts @@ -81,15 +82,17 @@ writeMuse opts document = -- | Return Muse representation of document. pandocToMuse :: PandocMonad m => Pandoc - -> StateT WriterState m String + -> StateT WriterState m Text pandocToMuse (Pandoc meta blocks) = do opts <- gets stOptions let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing + let render' :: Doc -> Text + render' = render Nothing metadata <- metaToJSON opts - (fmap (render Nothing) . blockListToMuse) - (fmap (render Nothing) . inlineListToMuse) + (fmap render' . blockListToMuse) + (fmap render' . inlineListToMuse) meta body <- blockListToMuse blocks notes <- liftM (reverse . stNotes) get >>= notesToMuse diff --git a/src/Text/Pandoc/Writers/Native.hs b/src/Text/Pandoc/Writers/Native.hs index 653efb3ce..3ef33f05c 100644 --- a/src/Text/Pandoc/Writers/Native.hs +++ b/src/Text/Pandoc/Writers/Native.hs @@ -30,6 +30,7 @@ Conversion of a 'Pandoc' document to a string representation. -} module Text.Pandoc.Writers.Native ( writeNative ) where +import Data.Text (Text) import Data.List (intersperse) import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Definition @@ -67,7 +68,7 @@ prettyBlock (Div attr blocks) = prettyBlock block = text $ show block -- | Prettyprint Pandoc document. -writeNative :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeNative :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeNative opts (Pandoc meta blocks) = return $ 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 68e68c659..1da051380 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -35,6 +35,7 @@ import Control.Monad.State import qualified Data.ByteString.Lazy as B import Data.List (isPrefixOf) import Data.Maybe (fromMaybe) +import qualified Data.Text.Lazy as TL import System.FilePath (takeDirectory, takeExtension, (<.>)) import Text.Pandoc.Class (PandocMonad, report) import qualified Text.Pandoc.Class as P @@ -45,7 +46,7 @@ import Text.Pandoc.MIME (extensionFromMimeType, getMimeType) import Text.Pandoc.Options (WrapOption (..), WriterOptions (..)) import Text.Pandoc.Pretty import Text.Pandoc.Shared (stringify) -import Text.Pandoc.UTF8 (fromStringLazy) +import Text.Pandoc.UTF8 (fromStringLazy, fromTextLazy) import Text.Pandoc.Walk import Text.Pandoc.Writers.OpenDocument (writeOpenDocument) import Text.Pandoc.Writers.Shared (fixDisplayMath) @@ -88,7 +89,7 @@ pandocToODT opts doc@(Pandoc meta _) = do newContents <- lift $ writeOpenDocument opts{writerWrapText = WrapNone} doc' epochtime <- floor `fmap` (lift P.getPOSIXTime) let contentEntry = toEntry "content.xml" epochtime - $ fromStringLazy newContents + $ fromTextLazy $ TL.fromStrict newContents picEntries <- gets stEntries let archive = foldr addEntryToArchive refArchive $ contentEntry : picEntries diff --git a/src/Text/Pandoc/Writers/OPML.hs b/src/Text/Pandoc/Writers/OPML.hs index cdb6ab0d1..4a0a317fa 100644 --- a/src/Text/Pandoc/Writers/OPML.hs +++ b/src/Text/Pandoc/Writers/OPML.hs @@ -30,6 +30,8 @@ Conversion of 'Pandoc' documents to OPML XML. -} module Text.Pandoc.Writers.OPML ( writeOPML) where import Control.Monad.Except (throwError) +import Data.Text (Text, unpack) +import qualified Data.Text as T import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Compat.Time @@ -45,7 +47,7 @@ import Text.Pandoc.Writers.Shared import Text.Pandoc.XML -- | Convert Pandoc document to string in OPML format. -writeOPML :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeOPML :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeOPML opts (Pandoc meta blocks) = do let elements = hierarchicalize blocks colwidth = if writerWrapText opts == WrapAuto @@ -54,7 +56,7 @@ writeOPML opts (Pandoc meta blocks) = do meta' = B.setMeta "date" (B.str $ convertDate $ docDate meta) meta metadata <- metaToJSON opts (writeMarkdown def . Pandoc nullMeta) - (\ils -> trimr <$> (writeMarkdown def $ Pandoc nullMeta [Plain ils])) + (\ils -> T.stripEnd <$> (writeMarkdown def $ Pandoc nullMeta [Plain ils])) meta' main <- (render colwidth . vcat) <$> (mapM (elementToOPML opts) elements) let context = defField "body" main metadata @@ -63,9 +65,9 @@ writeOPML opts (Pandoc meta blocks) = do Just tpl -> renderTemplate' tpl context -writeHtmlInlines :: PandocMonad m => [Inline] -> m String +writeHtmlInlines :: PandocMonad m => [Inline] -> m Text writeHtmlInlines ils = - trim <$> (writeHtml5String def $ Pandoc nullMeta [Plain ils]) + T.strip <$> (writeHtml5String def $ Pandoc nullMeta [Plain ils]) -- date format: RFC 822: Thu, 14 Jul 2005 23:41:05 GMT showDateTimeRFC822 :: UTCTime -> String @@ -95,9 +97,10 @@ elementToOPML opts (Sec _ _num _ title elements) = do (blocks, rest) = span isBlk elements htmlIls <- writeHtmlInlines title md <- if null blocks - then return [] + then return mempty else do blks <- mapM fromBlk blocks writeMarkdown def $ Pandoc nullMeta blks - let attrs = [("text", htmlIls)] ++ [("_note", md) | not (null blocks)] + let attrs = [("text", unpack htmlIls)] ++ + [("_note", unpack md) | not (null blocks)] 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 53c1d0c59..58295684e 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -36,6 +36,7 @@ import Control.Arrow ((***), (>>>)) import Control.Monad.State hiding (when) import Data.Char (chr) import Data.List (sortBy) +import Data.Text (Text) import qualified Data.Map as Map import Data.Ord (comparing) import qualified Data.Set as Set @@ -195,17 +196,18 @@ handleSpaces s rm [] = empty -- | Convert Pandoc document to string in OpenDocument format. -writeOpenDocument :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeOpenDocument :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeOpenDocument opts (Pandoc meta blocks) = do let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing - let render' = render colwidth + let render' :: Doc -> Text + render' = render colwidth ((body, metadata),s) <- flip runStateT defaultWriterState $ do m <- metaToJSON opts - (fmap (render colwidth) . blocksToOpenDocument opts) - (fmap (render colwidth) . inlinesToOpenDocument opts) + (fmap render' . blocksToOpenDocument opts) + (fmap render' . inlinesToOpenDocument opts) meta b <- render' `fmap` blocksToOpenDocument opts blocks return (b, m) diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index 78c102db6..e8f48da00 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -37,6 +37,7 @@ Org-Mode: <http://orgmode.org> module Text.Pandoc.Writers.Org (writeOrg) where import Control.Monad.State import Data.Char (isAlphaNum, toLower) +import Data.Text (Text) import Data.List (intersect, intersperse, isPrefixOf, partition, transpose) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Definition @@ -56,7 +57,7 @@ data WriterState = type Org = StateT WriterState -- | Convert Pandoc to Org. -writeOrg :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeOrg :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeOrg opts document = do let st = WriterState { stNotes = [], stHasMath = False, @@ -64,15 +65,17 @@ writeOrg opts document = do evalStateT (pandocToOrg document) st -- | Return Org representation of document. -pandocToOrg :: PandocMonad m => Pandoc -> Org m String +pandocToOrg :: PandocMonad m => Pandoc -> Org m Text pandocToOrg (Pandoc meta blocks) = do opts <- gets stOptions let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing + let render' :: Doc -> Text + render' = render colwidth metadata <- metaToJSON opts - (fmap (render colwidth) . blockListToOrg) - (fmap (render colwidth) . inlineListToOrg) + (fmap render' . blockListToOrg) + (fmap render' . inlineListToOrg) meta body <- blockListToOrg blocks notes <- gets (reverse . stNotes) >>= notesToOrg diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index b88fc2245..59f6553e2 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -35,6 +35,7 @@ import Control.Monad.State import Data.Char (isSpace, toLower) import Data.List (isPrefixOf, stripPrefix) import Data.Maybe (fromMaybe) +import Data.Text (Text, stripEnd) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Logging @@ -62,7 +63,7 @@ data WriterState = type RST = StateT WriterState -- | Convert Pandoc to RST. -writeRST :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeRST :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeRST opts document = do let st = WriterState { stNotes = [], stLinks = [], stImages = [], stHasMath = False, @@ -71,19 +72,21 @@ writeRST opts document = do evalStateT (pandocToRST document) st -- | Return RST representation of document. -pandocToRST :: PandocMonad m => Pandoc -> RST m String +pandocToRST :: PandocMonad m => Pandoc -> RST m Text pandocToRST (Pandoc meta blocks) = do opts <- gets stOptions let colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing + let render' :: Doc -> Text + render' = render colwidth let subtit = case lookupMeta "subtitle" meta of Just (MetaBlocks [Plain xs]) -> xs _ -> [] title <- titleToRST (docTitle meta) subtit metadata <- metaToJSON opts - (fmap (render colwidth) . blockListToRST) - (fmap (trimr . render colwidth) . inlineListToRST) + (fmap render' . blockListToRST) + (fmap (stripEnd . render') . inlineListToRST) $ B.deleteMeta "title" $ B.deleteMeta "subtitle" meta body <- blockListToRST' True $ case writerTemplate opts of Just _ -> normalizeHeadings 1 blocks @@ -94,7 +97,7 @@ pandocToRST (Pandoc meta blocks) = do pics <- gets (reverse . stImages) >>= pictRefsToRST hasMath <- gets stHasMath rawTeX <- gets stHasRawTeX - let main = render colwidth $ foldl ($+$) empty $ [body, notes, refs, pics] + let main = render' $ foldl ($+$) empty $ [body, notes, refs, pics] let context = defField "body" main $ defField "toc" (writerTableOfContents opts) $ defField "toc-depth" (show $ writerTOCDepth opts) diff --git a/src/Text/Pandoc/Writers/RTF.hs b/src/Text/Pandoc/Writers/RTF.hs index e9b29f97d..5c990f324 100644 --- a/src/Text/Pandoc/Writers/RTF.hs +++ b/src/Text/Pandoc/Writers/RTF.hs @@ -34,6 +34,8 @@ import Control.Monad.Except (catchError, throwError) import qualified Data.ByteString as B import Data.Char (chr, isDigit, ord) import Data.List (intercalate, isSuffixOf) +import Data.Text (Text) +import qualified Data.Text as T import qualified Data.Map as M import Text.Pandoc.Class (PandocMonad, report) import qualified Text.Pandoc.Class as P @@ -97,7 +99,7 @@ rtfEmbedImage opts x@(Image attr _ (src,_)) = catchError rtfEmbedImage _ x = return x -- | Convert Pandoc to a string in rich text format. -writeRTF :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeRTF :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeRTF options doc = do -- handle images Pandoc meta@(Meta metamap) blocks <- walkM (rtfEmbedImage options) doc @@ -123,7 +125,8 @@ writeRTF options doc = do then defField "toc" toc else id) $ metadata - return $ case writerTemplate options of + return $ T.pack + $ case writerTemplate options of Just tpl -> renderTemplate' tpl context Nothing -> case reverse body of ('\n':_) -> body diff --git a/src/Text/Pandoc/Writers/TEI.hs b/src/Text/Pandoc/Writers/TEI.hs index 7da792c9e..27d26c7d9 100644 --- a/src/Text/Pandoc/Writers/TEI.hs +++ b/src/Text/Pandoc/Writers/TEI.hs @@ -31,6 +31,7 @@ Conversion of 'Pandoc' documents to Docbook XML. -} module Text.Pandoc.Writers.TEI (writeTEI) where import Data.Char (toLower) +import Data.Text (Text) import Data.List (isPrefixOf, stripPrefix) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad, report) @@ -56,12 +57,13 @@ authorToTEI opts name' = do inTagsSimple "author" (text $ escapeStringForXML name) -- | Convert Pandoc document to string in Docbook format. -writeTEI :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeTEI :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeTEI opts (Pandoc meta blocks) = do let elements = hierarchicalize blocks colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing + render' :: Doc -> Text render' = render colwidth startLvl = case writerTopLevelDivision opts of TopLevelPart -> -1 @@ -71,9 +73,9 @@ writeTEI opts (Pandoc meta blocks) = do auths' <- mapM (authorToTEI opts) $ docAuthors meta let meta' = B.setMeta "author" auths' meta metadata <- metaToJSON opts - (fmap (render colwidth . vcat) . - (mapM (elementToTEI opts startLvl)) . hierarchicalize) - (fmap (render colwidth) . inlinesToTEI opts) + (fmap (render' . vcat) . + mapM (elementToTEI opts startLvl) . hierarchicalize) + (fmap render' . inlinesToTEI opts) meta' main <- (render' . vcat) <$> mapM (elementToTEI opts startLvl) elements let context = defField "body" main diff --git a/src/Text/Pandoc/Writers/Texinfo.hs b/src/Text/Pandoc/Writers/Texinfo.hs index 710e1dea0..387e55290 100644 --- a/src/Text/Pandoc/Writers/Texinfo.hs +++ b/src/Text/Pandoc/Writers/Texinfo.hs @@ -37,6 +37,7 @@ import Data.Char (chr, ord) import Data.List (maximumBy, transpose) import Data.Ord (comparing) import qualified Data.Set as Set +import Data.Text (Text) import Network.URI (unEscapeString) import System.FilePath import Text.Pandoc.Class (PandocMonad, report) @@ -68,7 +69,7 @@ data WriterState = type TI m = StateT WriterState m -- | Convert Pandoc to Texinfo. -writeTexinfo :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeTexinfo :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeTexinfo options document = evalStateT (pandocToTexinfo options $ wrapTop document) $ WriterState { stStrikeout = False, stSuperscript = False, @@ -80,16 +81,18 @@ wrapTop :: Pandoc -> Pandoc wrapTop (Pandoc meta blocks) = Pandoc meta (Header 0 nullAttr (docTitle meta) : blocks) -pandocToTexinfo :: PandocMonad m => WriterOptions -> Pandoc -> TI m String +pandocToTexinfo :: PandocMonad m => WriterOptions -> Pandoc -> TI m Text pandocToTexinfo options (Pandoc meta blocks) = do let titlePage = not $ all null $ docTitle meta : docDate meta : docAuthors meta let colwidth = if writerWrapText options == WrapAuto then Just $ writerColumns options else Nothing + let render' :: Doc -> Text + render' = render colwidth metadata <- metaToJSON options - (fmap (render colwidth) . blockListToTexinfo) - (fmap (render colwidth) . inlineListToTexinfo) + (fmap render' . blockListToTexinfo) + (fmap render' . inlineListToTexinfo) meta main <- blockListToTexinfo blocks st <- get diff --git a/src/Text/Pandoc/Writers/Textile.hs b/src/Text/Pandoc/Writers/Textile.hs index d532f3ed3..091a5baca 100644 --- a/src/Text/Pandoc/Writers/Textile.hs +++ b/src/Text/Pandoc/Writers/Textile.hs @@ -33,6 +33,7 @@ module Text.Pandoc.Writers.Textile ( writeTextile ) where import Control.Monad.State import Data.Char (isSpace) import Data.List (intercalate) +import Data.Text (Text, pack) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Logging import Text.Pandoc.Definition @@ -54,7 +55,7 @@ data WriterState = WriterState { type TW = StateT WriterState -- | Convert Pandoc to Textile. -writeTextile :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeTextile :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeTextile opts document = evalStateT (pandocToTextile opts document) WriterState { stNotes = [], @@ -64,17 +65,17 @@ writeTextile opts document = -- | Return Textile representation of document. pandocToTextile :: PandocMonad m - => WriterOptions -> Pandoc -> TW m String + => WriterOptions -> Pandoc -> TW m Text pandocToTextile opts (Pandoc meta blocks) = do metadata <- metaToJSON opts (blockListToTextile opts) (inlineListToTextile opts) meta body <- blockListToTextile opts blocks notes <- gets $ unlines . reverse . stNotes - let main = body ++ if null notes then "" else ("\n\n" ++ notes) + let main = pack $ body ++ if null notes then "" else ("\n\n" ++ notes) let context = defField "body" main metadata case writerTemplate opts of - Nothing -> return main - Just tpl -> return $ renderTemplate' tpl context + Nothing -> return main + Just tpl -> return $ renderTemplate' tpl context withUseTags :: PandocMonad m => TW m a -> TW m a withUseTags action = do diff --git a/src/Text/Pandoc/Writers/ZimWiki.hs b/src/Text/Pandoc/Writers/ZimWiki.hs index 4ab8bde30..5ee239e59 100644 --- a/src/Text/Pandoc/Writers/ZimWiki.hs +++ b/src/Text/Pandoc/Writers/ZimWiki.hs @@ -37,7 +37,7 @@ import Control.Monad.State (StateT, evalStateT, gets, modify) import Data.Default (Default (..)) import Data.List (intercalate, isInfixOf, isPrefixOf, transpose) import qualified Data.Map as Map -import Data.Text (breakOnAll, pack) +import Data.Text (breakOnAll, pack, Text) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Logging import Text.Pandoc.Definition @@ -61,17 +61,17 @@ instance Default WriterState where type ZW = StateT WriterState -- | Convert Pandoc to ZimWiki. -writeZimWiki :: PandocMonad m => WriterOptions -> Pandoc -> m String +writeZimWiki :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeZimWiki opts document = evalStateT (pandocToZimWiki opts document) def -- | Return ZimWiki representation of document. -pandocToZimWiki :: PandocMonad m => WriterOptions -> Pandoc -> ZW m String +pandocToZimWiki :: PandocMonad m => WriterOptions -> Pandoc -> ZW m Text pandocToZimWiki opts (Pandoc meta blocks) = do metadata <- metaToJSON opts (fmap trimr . blockListToZimWiki opts) (inlineListToZimWiki opts) meta - body <- blockListToZimWiki opts blocks + body <- pack <$> blockListToZimWiki opts blocks --let header = "Content-Type: text/x-zim-wiki\nWiki-Format: zim 0.4\n" let main = body let context = defField "body" main diff --git a/src/Text/Pandoc/XML.hs b/src/Text/Pandoc/XML.hs index b6edd6be5..67608fb43 100644 --- a/src/Text/Pandoc/XML.hs +++ b/src/Text/Pandoc/XML.hs @@ -37,6 +37,8 @@ module Text.Pandoc.XML ( escapeCharForXML, fromEntities ) where import Data.Char (isAscii, isSpace, ord) +import Data.Text (Text) +import qualified Data.Text as T import Text.HTML.TagSoup.Entity (lookupEntity) import Text.Pandoc.Pretty @@ -91,11 +93,10 @@ inTagsIndented :: String -> Doc -> Doc inTagsIndented tagType = inTags True tagType [] -- | Escape all non-ascii characters using numerical entities. -toEntities :: String -> String -toEntities [] = "" -toEntities (c:cs) - | isAscii c = c : toEntities cs - | otherwise = "&#" ++ show (ord c) ++ ";" ++ toEntities cs +toEntities :: Text -> Text +toEntities = T.concatMap go + where go c | isAscii c = T.singleton c + | otherwise = T.pack ("&#" ++ show (ord c) ++ ";") -- Unescapes XML entities fromEntities :: String -> String -- cgit v1.2.3 From f36de77a2586d314d8de25c3c2a48fb5f95cafc9 Mon Sep 17 00:00:00 2001 From: schrieveslaach <schrieveslaach@online.de> Date: Sun, 11 Jun 2017 07:47:42 +0200 Subject: Support for \faCheck and \faClose (#3727) --- src/Text/Pandoc/Readers/LaTeX.hs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 796d2789e..1ac872933 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -682,6 +682,9 @@ inlineCommands = M.fromList $ , ("nohyphens", tok) , ("textnhtt", ttfamily) , ("nhttfamily", ttfamily) + -- fontawesome + , ("faCheck", lit "\10003") + , ("faClose", lit "\10007") ] ++ map ignoreInlines -- these commands will be ignored unless --parse-raw is specified, -- in which case they will appear as raw latex blocks: -- cgit v1.2.3 From 49b738de4e9babe727a8e48f22a90eacbdd63847 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 11 Jun 2017 11:55:21 +0200 Subject: Rewrote HTML reader to use Text throughout. - Export new NamedTag class from HTML reader. - Effect on memory usage is modest (< 10%). --- src/Text/Pandoc/Readers/HTML.hs | 331 +++++++++++++++++++++++----------------- 1 file changed, 194 insertions(+), 137 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 3bccf89fb..94f933c4d 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -1,5 +1,5 @@ {-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, -ViewPatterns#-} +ViewPatterns, OverloadedStrings #-} {- Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> @@ -34,6 +34,7 @@ module Text.Pandoc.Readers.HTML ( readHtml , htmlInBalanced , isInlineTag , isBlockTag + , NamedTag(..) , isTextTag , isCommentTag ) where @@ -43,7 +44,7 @@ import Text.HTML.TagSoup.Match 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 +import Text.Pandoc.Shared ( extractSpaces, addMetaField , escapeURI, safeRead ) import Text.Pandoc.Options (ReaderOptions(readerExtensions), extensionEnabled, Extension (Ext_epub_html_exts, @@ -53,13 +54,14 @@ import Text.Pandoc.Parsing hiding ((<|>)) import Text.Pandoc.Walk import qualified Data.Map as M import Data.Maybe ( fromMaybe, isJust) -import Data.List ( intercalate, isInfixOf, isPrefixOf ) +import Data.List ( intercalate, isPrefixOf ) import Data.Char ( isDigit, isLetter, isAlphaNum ) import Control.Monad ( guard, mzero, void, unless ) import Control.Arrow ((***)) import Control.Applicative ( (<|>) ) import Data.Monoid (First (..)) -import Data.Text (Text, unpack) +import Data.Text (Text) +import qualified Data.Text as T import Text.TeXMath (readMathML, writeTeX) import Data.Default (Default (..), def) import Control.Monad.Reader (ask, asks, local, ReaderT, runReaderT, lift) @@ -80,7 +82,7 @@ readHtml :: PandocMonad m readHtml opts inp = do let tags = stripPrefixes . canonicalizeTags $ parseTagsOptions parseOptions{ optTagPosition = True } - (unpack inp) + inp parseDoc = do blocks <- (fixPlains False) . mconcat <$> manyTill block eof meta <- stateMeta . parserState <$> getState @@ -130,7 +132,7 @@ setInPlain = local (\s -> s {inPlain = True}) type HTMLParser m s = ParserT s HTMLState (ReaderT HTMLLocal m) -type TagParser m = HTMLParser m [Tag String] +type TagParser m = HTMLParser m [Tag Text] pBody :: PandocMonad m => TagParser m Blocks pBody = pInTags "body" block @@ -140,12 +142,12 @@ pHead = pInTags "head" $ pTitle <|> pMetaTag <|> pBaseTag <|> (mempty <$ pAnyTag where pTitle = pInTags "title" inline >>= setTitle . trimInlines setTitle t = mempty <$ (updateState $ B.setMeta "title" t) pMetaTag = do - mt <- pSatisfy (~== TagOpen "meta" []) - let name = fromAttrib "name" mt + mt <- pSatisfy (matchTagOpen "meta" []) + let name = T.unpack $ fromAttrib "name" mt if null name then return mempty else do - let content = fromAttrib "content" mt + let content = T.unpack $ fromAttrib "content" mt updateState $ \s -> let ps = parserState s in s{ parserState = ps{ @@ -153,9 +155,9 @@ pHead = pInTags "head" $ pTitle <|> pMetaTag <|> pBaseTag <|> (mempty <$ pAnyTag (stateMeta ps) } } return mempty pBaseTag = do - bt <- pSatisfy (~== TagOpen "base" []) + bt <- pSatisfy (matchTagOpen "base" []) updateState $ \st -> st{ baseHref = - parseURIReference $ fromAttrib "href" bt } + parseURIReference $ T.unpack $ fromAttrib "href" bt } return mempty block :: PandocMonad m => TagParser m Blocks @@ -195,29 +197,31 @@ eSwitch :: (PandocMonad m, Monoid a) -> TagParser m a eSwitch constructor parser = try $ do guardEnabled Ext_epub_html_exts - pSatisfy (~== TagOpen "switch" []) + pSatisfy (matchTagOpen "switch" []) cases <- getFirst . mconcat <$> manyTill (First <$> (eCase <* skipMany pBlank) ) - (lookAhead $ try $ pSatisfy (~== TagOpen "default" [])) + (lookAhead $ try $ pSatisfy (matchTagOpen "default" [])) skipMany pBlank fallback <- pInTags "default" (skipMany pBlank *> parser <* skipMany pBlank) skipMany pBlank - pSatisfy (~== TagClose "switch") + pSatisfy (matchTagClose "switch") return $ maybe fallback constructor cases eCase :: PandocMonad m => TagParser m (Maybe Inlines) eCase = do skipMany pBlank - TagOpen _ attr <- lookAhead $ pSatisfy $ (~== TagOpen "case" []) + TagOpen _ attr' <- lookAhead $ pSatisfy $ (matchTagOpen "case" []) + let attr = toStringAttr attr' case (flip lookup namespaces) =<< lookup "required-namespace" attr of Just p -> Just <$> (pInTags "case" (skipMany pBlank *> p <* skipMany pBlank)) - Nothing -> Nothing <$ manyTill pAnyTag (pSatisfy (~== TagClose "case")) + Nothing -> Nothing <$ manyTill pAnyTag (pSatisfy (matchTagClose "case")) eFootnote :: PandocMonad m => TagParser m () eFootnote = try $ do let notes = ["footnote", "rearnote"] guardEnabled Ext_epub_html_exts - (TagOpen tag attr) <- lookAhead $ pAnyTag + (TagOpen tag attr') <- lookAhead $ pAnyTag + let attr = toStringAttr attr' guard (maybe False (flip elem notes) (lookup "type" attr)) let ident = fromMaybe "" (lookup "id" attr) content <- pInTags tag block @@ -229,7 +233,8 @@ addNote uid cont = updateState (\s -> s {noteTable = (uid, cont) : (noteTable s) eNoteref :: PandocMonad m => TagParser m Inlines eNoteref = try $ do guardEnabled Ext_epub_html_exts - TagOpen tag attr <- lookAhead $ pAnyTag + TagOpen tag attr' <- lookAhead $ pAnyTag + let attr = toStringAttr attr' guard (maybe False (== "noteref") (lookup "type" attr)) let ident = maybe "" (dropWhile (== '#')) (lookup "href" attr) guard (not (null ident)) @@ -249,10 +254,10 @@ pList = pBulletList <|> pOrderedList <|> pDefinitionList pBulletList :: PandocMonad m => TagParser m Blocks pBulletList = try $ do - pSatisfy (~== TagOpen "ul" []) + pSatisfy (matchTagOpen "ul" []) let nonItem = pSatisfy (\t -> not (tagOpen (`elem` ["li","ol","ul","dl"]) (const True) t) && - not (t ~== TagClose "ul")) + not (matchTagClose "ul" t)) -- note: if they have an <ol> or <ul> not in scope of a <li>, -- treat it as a list item, though it's not valid xhtml... skipMany nonItem @@ -261,7 +266,8 @@ pBulletList = try $ do pListItem :: PandocMonad m => TagParser m a -> TagParser m Blocks pListItem nonItem = do - TagOpen _ attr <- lookAhead $ pSatisfy (~== TagOpen "li" []) + TagOpen _ attr' <- lookAhead $ pSatisfy (matchTagOpen "li" []) + let attr = toStringAttr attr' let addId ident bs = case B.toList bs of (Plain ils:xs) -> B.fromList (Plain [Span (ident, [], []) ils] : xs) @@ -287,7 +293,8 @@ parseTypeAttr _ = DefaultStyle pOrderedList :: PandocMonad m => TagParser m Blocks pOrderedList = try $ do - TagOpen _ attribs <- pSatisfy (~== TagOpen "ol" []) + TagOpen _ attribs' <- pSatisfy (matchTagOpen "ol" []) + let attribs = toStringAttr attribs' let (start, style) = (sta', sty') where sta = fromMaybe "1" $ lookup "start" attribs @@ -309,7 +316,7 @@ pOrderedList = try $ do ] let nonItem = pSatisfy (\t -> not (tagOpen (`elem` ["li","ol","ul","dl"]) (const True) t) && - not (t ~== TagClose "ol")) + not (matchTagClose "ol" t)) -- note: if they have an <ol> or <ul> not in scope of a <li>, -- treat it as a list item, though it's not valid xhtml... skipMany nonItem @@ -318,14 +325,14 @@ pOrderedList = try $ do pDefinitionList :: PandocMonad m => TagParser m Blocks pDefinitionList = try $ do - pSatisfy (~== TagOpen "dl" []) + pSatisfy (matchTagOpen "dl" []) items <- manyTill pDefListItem (pCloses "dl") return $ B.definitionList items 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")) + let nonItem = pSatisfy (\t -> not (matchTagOpen "dt" [] t) && + not (matchTagOpen "dd" [] t) && not (matchTagClose "dl" t)) terms <- many1 (try $ skipMany nonItem >> pInTags "dt" inline) defs <- many1 (try $ skipMany nonItem >> pInTags "dd" block) skipMany nonItem @@ -348,12 +355,12 @@ fixPlains inList bs = if any isParaish bs' plainToPara x = x bs' = B.toList bs -pRawTag :: PandocMonad m => TagParser m String +pRawTag :: PandocMonad m => TagParser m Text pRawTag = do tag <- pAnyTag let ignorable x = x `elem` ["html","head","body","!DOCTYPE","?xml"] if tagOpen ignorable (const True) tag || tagClose ignorable tag - then return [] + then return mempty else return $ renderTags' [tag] pDiv :: PandocMonad m => TagParser m Blocks @@ -362,7 +369,8 @@ pDiv = try $ do let isDivLike "div" = True isDivLike "section" = True isDivLike _ = False - TagOpen tag attr <- lookAhead $ pSatisfy $ tagOpen isDivLike (const True) + TagOpen tag attr' <- lookAhead $ pSatisfy $ tagOpen isDivLike (const True) + let attr = toStringAttr attr' contents <- pInTags tag block let (ident, classes, kvs) = mkAttr attr let classes' = if tag == "section" @@ -372,7 +380,7 @@ pDiv = try $ do pRawHtmlBlock :: PandocMonad m => TagParser m Blocks pRawHtmlBlock = do - raw <- pHtmlBlock "script" <|> pHtmlBlock "style" <|> pRawTag + raw <- T.unpack <$> (pHtmlBlock "script" <|> pHtmlBlock "style" <|> pRawTag) exts <- getOption readerExtensions if extensionEnabled Ext_raw_html exts && not (null raw) then return $ B.rawBlock "html" raw @@ -387,33 +395,35 @@ ignore raw = do logMessage $ SkippedContent raw pos return mempty -pHtmlBlock :: PandocMonad m => String -> TagParser m String +pHtmlBlock :: PandocMonad m => Text -> TagParser m Text pHtmlBlock t = try $ do - open <- pSatisfy (~== TagOpen t []) - contents <- manyTill pAnyTag (pSatisfy (~== TagClose t)) - return $ renderTags' $ [open] ++ contents ++ [TagClose t] + open <- pSatisfy (matchTagOpen t []) + contents <- manyTill pAnyTag (pSatisfy (matchTagClose t)) + return $ renderTags' $ [open] <> contents <> [TagClose t] -- Sets chapter context eSection :: PandocMonad m => TagParser m Blocks eSection = try $ do - let matchChapter as = maybe False (isInfixOf "chapter") (lookup "type" as) + let matchChapter as = maybe False (T.isInfixOf "chapter") (lookup "type" as) let sectTag = tagOpen (`elem` sectioningContent) matchChapter TagOpen tag _ <- lookAhead $ pSatisfy sectTag setInChapter (pInTags tag block) -headerLevel :: PandocMonad m => String -> TagParser m Int +headerLevel :: PandocMonad m => Text -> TagParser m Int headerLevel tagtype = do - let level = read (drop 1 tagtype) - (try $ do - guardEnabled Ext_epub_html_exts - asks inChapter >>= guard - return (level - 1)) - <|> - return level + case safeRead (T.unpack (T.drop 1 tagtype)) of + Just level -> + (try $ do + guardEnabled Ext_epub_html_exts + asks inChapter >>= guard + return (level - 1)) + <|> + return level + Nothing -> fail "Could not retrieve header level" eTitlePage :: PandocMonad m => TagParser m () eTitlePage = try $ do - let isTitlePage as = maybe False (isInfixOf "titlepage") (lookup "type" as) + let isTitlePage as = maybe False (T.isInfixOf "titlepage") (lookup "type" as) let groupTag = tagOpen (\x -> x `elem` groupingContent || x == "section") isTitlePage TagOpen tag _ <- lookAhead $ pSatisfy groupTag @@ -421,19 +431,21 @@ eTitlePage = try $ do pHeader :: PandocMonad m => TagParser m Blocks pHeader = try $ do - TagOpen tagtype attr <- pSatisfy $ + TagOpen tagtype attr' <- pSatisfy $ tagOpen (`elem` ["h1","h2","h3","h4","h5","h6"]) (const True) - let bodyTitle = TagOpen tagtype attr ~== TagOpen "h1" [("class","title")] + let attr = toStringAttr attr' + let bodyTitle = TagOpen tagtype attr' ~== TagOpen ("h1" :: Text) + [("class","title")] level <- headerLevel tagtype contents <- trimInlines . mconcat <$> manyTill inline (pCloses tagtype <|> eof) let ident = fromMaybe "" $ lookup "id" attr let classes = maybe [] words $ lookup "class" attr let keyvals = [(k,v) | (k,v) <- attr, k /= "class", k /= "id"] - attr' <- registerHeader (ident, classes, keyvals) contents + attr'' <- registerHeader (ident, classes, keyvals) contents return $ if bodyTitle then mempty -- skip a representation of the title in the body - else B.headerWith attr' level contents + else B.headerWith attr'' level contents pHrule :: PandocMonad m => TagParser m Blocks pHrule = do @@ -442,7 +454,7 @@ pHrule = do pTable :: PandocMonad m => TagParser m Blocks pTable = try $ do - TagOpen _ _ <- pSatisfy (~== TagOpen "table" []) + TagOpen _ _ <- pSatisfy (matchTagOpen "table" []) skipMany pBlank caption <- option mempty $ pInTags "caption" inline <* skipMany pBlank widths' <- (mconcat <$> many1 pColgroup) <|> many pCol @@ -456,8 +468,8 @@ pTable = try $ do else return head'' rowsLs <- many pTBody rows' <- pOptInTag "tfoot" $ many pTr - TagClose _ <- pSatisfy (~== TagClose "table") - let rows'' = (concat rowsLs) ++ rows' + TagClose _ <- pSatisfy (matchTagClose "table") + let rows'' = (concat rowsLs) <> rows' -- fail on empty table guard $ not $ null head' && null rows'' let isSinglePlain x = case B.toList x of @@ -468,7 +480,7 @@ pTable = try $ do let cols = length $ if null head' then head rows'' else head' -- add empty cells to short rows let addEmpties r = case cols - length r of - n | n > 0 -> r ++ replicate n mempty + n | n > 0 -> r <> replicate n mempty | otherwise -> r let rows = map addEmpties rows'' let aligns = replicate cols AlignDefault @@ -481,15 +493,16 @@ pTable = try $ do pCol :: PandocMonad m => TagParser m Double pCol = try $ do - TagOpen _ attribs <- pSatisfy (~== TagOpen "col" []) + TagOpen _ attribs' <- pSatisfy (matchTagOpen "col" []) + let attribs = toStringAttr attribs' skipMany pBlank - optional $ pSatisfy (~== TagClose "col") + optional $ pSatisfy (matchTagClose "col") skipMany pBlank return $ case lookup "width" attribs of Nothing -> case lookup "style" attribs of Just ('w':'i':'d':'t':'h':':':xs) | '%' `elem` xs -> fromMaybe 0.0 $ safeRead ('0':'.':filter - (`notElem` " \t\r\n%'\";") xs) + (`notElem` (" \t\r\n%'\";" :: [Char])) xs) _ -> 0.0 Just x | not (null x) && last x == '%' -> fromMaybe 0.0 $ safeRead ('0':'.':init x) @@ -497,18 +510,18 @@ pCol = try $ do pColgroup :: PandocMonad m => TagParser m [Double] pColgroup = try $ do - pSatisfy (~== TagOpen "colgroup" []) + pSatisfy (matchTagOpen "colgroup" []) skipMany pBlank manyTill pCol (pCloses "colgroup" <|> eof) <* skipMany pBlank -noColOrRowSpans :: Tag String -> Bool +noColOrRowSpans :: Tag Text -> Bool noColOrRowSpans t = isNullOrOne "colspan" && isNullOrOne "rowspan" where isNullOrOne x = case fromAttrib x t of "" -> True "1" -> True _ -> False -pCell :: PandocMonad m => String -> TagParser m [Blocks] +pCell :: PandocMonad m => Text -> TagParser m [Blocks] pCell celltype = try $ do skipMany pBlank res <- pInTags' celltype noColOrRowSpans block @@ -534,7 +547,8 @@ pPara = do pCodeBlock :: PandocMonad m => TagParser m Blocks pCodeBlock = try $ do - TagOpen _ attr <- pSatisfy (~== TagOpen "pre" []) + TagOpen _ attr' <- pSatisfy (matchTagOpen "pre" []) + let attr = toStringAttr attr' contents <- manyTill pAnyTag (pCloses "pre" <|> eof) let rawText = concatMap tagToString contents -- drop leading newline if any @@ -547,8 +561,8 @@ pCodeBlock = try $ do _ -> result' return $ B.codeBlockWith (mkAttr attr) result -tagToString :: Tag String -> String -tagToString (TagText s) = s +tagToString :: Tag Text -> String +tagToString (TagText s) = T.unpack s tagToString (TagOpen "br" _) = "\n" tagToString _ = "" @@ -577,20 +591,20 @@ pLocation = do (TagPosition r c) <- pSat isTagPosition setPosition $ newPos "input" r c -pSat :: PandocMonad m => (Tag String -> Bool) -> TagParser m (Tag String) +pSat :: PandocMonad m => (Tag Text -> Bool) -> TagParser m (Tag Text) pSat f = do pos <- getPosition token show (const pos) (\x -> if f x then Just x else Nothing) -pSatisfy :: PandocMonad m => (Tag String -> Bool) -> TagParser m (Tag String) +pSatisfy :: PandocMonad m => (Tag Text -> Bool) -> TagParser m (Tag Text) pSatisfy f = try $ optional pLocation >> pSat f -pAnyTag :: PandocMonad m => TagParser m (Tag String) +pAnyTag :: PandocMonad m => TagParser m (Tag Text) pAnyTag = pSatisfy (const True) pSelfClosing :: PandocMonad m - => (String -> Bool) -> ([Attribute String] -> Bool) - -> TagParser m (Tag String) + => (Text -> Bool) -> ([Attribute Text] -> Bool) + -> TagParser m (Tag Text) pSelfClosing f g = do open <- pSatisfy (tagOpen f g) optional $ pSatisfy (tagClose f) @@ -628,7 +642,7 @@ pStrikeout = do pInlinesInTags "s" B.strikeout <|> pInlinesInTags "strike" B.strikeout <|> pInlinesInTags "del" B.strikeout <|> - try (do pSatisfy (~== TagOpen "span" [("class","strikeout")]) + try (do pSatisfy (matchTagOpen "span" [("class","strikeout")]) contents <- mconcat <$> manyTill inline (pCloses "span") return $ B.strikeout contents) @@ -639,17 +653,19 @@ pLineBreak = do -- Unlike fromAttrib from tagsoup, this distinguishes -- between a missing attribute and an attribute with empty content. -maybeFromAttrib :: String -> Tag String -> Maybe String -maybeFromAttrib name (TagOpen _ attrs) = lookup name attrs +maybeFromAttrib :: String -> Tag Text -> Maybe String +maybeFromAttrib name (TagOpen _ attrs) = + T.unpack <$> lookup (T.pack name) attrs maybeFromAttrib _ _ = Nothing pLink :: PandocMonad m => TagParser m Inlines pLink = try $ do tag <- pSatisfy $ tagOpenLit "a" (const True) - let title = fromAttrib "title" tag + let title = T.unpack $ fromAttrib "title" tag -- take id from id attribute if present, otherwise name - let uid = maybe (fromAttrib "name" tag) id $ maybeFromAttrib "id" tag - let cls = words $ fromAttrib "class" tag + let uid = maybe (T.unpack $ fromAttrib "name" tag) id $ + maybeFromAttrib "id" tag + let cls = words $ T.unpack $ fromAttrib "class" tag lab <- trimInlines . mconcat <$> manyTill inline (pCloses "a") -- check for href; if href, then a link, otherwise a span case maybeFromAttrib "href" tag of @@ -667,30 +683,33 @@ pImage :: PandocMonad m => TagParser m Inlines pImage = do tag <- pSelfClosing (=="img") (isJust . lookup "src") mbBaseHref <- baseHref <$> getState - let url' = fromAttrib "src" tag + let url' = T.unpack $ fromAttrib "src" tag let url = case (parseURIReference url', mbBaseHref) of (Just rel, Just bs) -> show (rel `nonStrictRelativeTo` bs) _ -> url' - let title = fromAttrib "title" tag - let alt = fromAttrib "alt" tag - let uid = fromAttrib "id" tag - let cls = words $ fromAttrib "class" tag + let title = T.unpack $ fromAttrib "title" tag + let alt = T.unpack $ fromAttrib "alt" tag + let uid = T.unpack $ fromAttrib "id" tag + let cls = words $ T.unpack $ fromAttrib "class" tag let getAtt k = case fromAttrib k tag of "" -> [] - v -> [(k, v)] + v -> [(T.unpack k, T.unpack v)] let kvs = concat $ map getAtt ["width", "height", "sizes", "srcset"] return $ B.imageWith (uid, cls, kvs) (escapeURI url) title (B.text alt) pCode :: PandocMonad m => TagParser m Inlines pCode = try $ do - (TagOpen open attr) <- pSatisfy $ tagOpen (`elem` ["code","tt"]) (const True) + (TagOpen open attr') <- pSatisfy $ tagOpen (`elem` ["code","tt"]) (const True) + let attr = toStringAttr attr' result <- manyTill pAnyTag (pCloses open) - return $ B.codeWith (mkAttr attr) $ intercalate " " $ lines $ innerText result + return $ B.codeWith (mkAttr attr) $ intercalate " " $ lines $ T.unpack $ + innerText result pSpan :: PandocMonad m => TagParser m Inlines pSpan = try $ do guardEnabled Ext_native_spans - TagOpen _ attr <- lookAhead $ pSatisfy $ tagOpen (=="span") (const True) + TagOpen _ attr' <- lookAhead $ pSatisfy $ tagOpen (=="span") (const True) + let attr = toStringAttr attr' contents <- pInTags "span" inline let isSmallCaps = fontVariant == "small-caps" || "smallcaps" `elem` classes where styleAttr = fromMaybe "" $ lookup "style" attr @@ -708,7 +727,7 @@ pRawHtmlInline = do then pSatisfy (not . isBlockTag) else pSatisfy isInlineTag exts <- getOption readerExtensions - let raw = renderTags' [result] + let raw = T.unpack $ renderTags' [result] if extensionEnabled Ext_raw_html exts then return $ B.rawInline "html" raw else ignore raw @@ -716,32 +735,38 @@ pRawHtmlInline = do mathMLToTeXMath :: String -> Either String String mathMLToTeXMath s = writeTeX <$> readMathML s +toStringAttr :: [(Text, Text)] -> [(String, String)] +toStringAttr = map go + where go (x,y) = (T.unpack x, T.unpack y) + pMath :: PandocMonad m => Bool -> TagParser m Inlines pMath inCase = try $ do - open@(TagOpen _ attr) <- pSatisfy $ tagOpen (=="math") (const True) + open@(TagOpen _ attr') <- pSatisfy $ tagOpen (=="math") (const True) -- we'll assume math tags are MathML unless specially marked -- otherwise... + let attr = toStringAttr attr' unless inCase $ guard (maybe True (== mathMLNamespace) (lookup "xmlns" attr)) - contents <- manyTill pAnyTag (pSatisfy (~== TagClose "math")) - case mathMLToTeXMath (renderTags $ [open] ++ contents ++ [TagClose "math"]) of + contents <- manyTill pAnyTag (pSatisfy (matchTagClose "math")) + case mathMLToTeXMath (T.unpack $ renderTags $ + [open] <> contents <> [TagClose "math"]) of Left _ -> return $ B.spanWith ("",["math"],attr) $ B.text $ - innerText contents + T.unpack $ innerText contents Right [] -> return mempty Right x -> return $ case lookup "display" attr of Just "block" -> B.displayMath x _ -> B.math x -pInlinesInTags :: PandocMonad m => String -> (Inlines -> Inlines) +pInlinesInTags :: PandocMonad m => Text -> (Inlines -> Inlines) -> TagParser m Inlines pInlinesInTags tagtype f = extractSpaces f <$> pInTags tagtype inline -pInTags :: (PandocMonad m, Monoid a) => String -> TagParser m a -> TagParser m a +pInTags :: (PandocMonad m, Monoid a) => Text -> TagParser m a -> TagParser m a pInTags tagtype parser = pInTags' tagtype (const True) parser pInTags' :: (PandocMonad m, Monoid a) - => String - -> (Tag String -> Bool) + => Text + -> (Tag Text -> Bool) -> TagParser m a -> TagParser m a pInTags' tagtype tagtest parser = try $ do @@ -750,18 +775,18 @@ pInTags' tagtype tagtest parser = try $ do -- parses p, preceeded by an optional opening tag -- and followed by an optional closing tags -pOptInTag :: PandocMonad m => String -> TagParser m a -> TagParser m a +pOptInTag :: PandocMonad m => Text -> TagParser m a -> TagParser m a pOptInTag tagtype p = try $ do skipMany pBlank - optional $ pSatisfy (~== TagOpen tagtype []) + optional $ pSatisfy (matchTagOpen tagtype []) skipMany pBlank x <- p skipMany pBlank - optional $ pSatisfy (~== TagClose tagtype) + optional $ pSatisfy (matchTagClose tagtype) skipMany pBlank return x -pCloses :: PandocMonad m => String -> TagParser m () +pCloses :: PandocMonad m => Text -> TagParser m () pCloses tagtype = try $ do t <- lookAhead $ pSatisfy $ \tag -> isTagClose tag || isTagOpen tag case t of @@ -782,15 +807,15 @@ pTagText = try $ do parsed <- lift $ lift $ flip runReaderT qu $ runParserT (many pTagContents) st "text" str case parsed of - Left _ -> throwError $ PandocParseError $ "Could not parse `" ++ str ++ "'" + Left _ -> throwError $ PandocParseError $ "Could not parse `" <> T.unpack str <> "'" Right result -> return $ mconcat result pBlank :: PandocMonad m => TagParser m () pBlank = try $ do (TagText str) <- pSatisfy isTagText - guard $ all isSpace str + guard $ T.all isSpace str -type InlinesParser m = HTMLParser m String +type InlinesParser m = HTMLParser m Text pTagContents :: PandocMonad m => InlinesParser m Inlines pTagContents = @@ -871,13 +896,13 @@ pSpace = many1 (satisfy isSpace) >>= \xs -> -- Constants -- -eitherBlockOrInline :: Set.Set String +eitherBlockOrInline :: Set.Set Text eitherBlockOrInline = Set.fromList ["audio", "applet", "button", "iframe", "embed", "del", "ins", "progress", "map", "area", "noscript", "script", "object", "svg", "video", "source"] -blockHtmlTags :: Set.Set String +blockHtmlTags :: Set.Set Text blockHtmlTags = Set.fromList ["?xml", "!DOCTYPE", "address", "article", "aside", "blockquote", "body", "canvas", @@ -893,7 +918,7 @@ blockHtmlTags = Set.fromList -- We want to allow raw docbook in markdown documents, so we -- include docbook block tags here too. -blockDocBookTags :: Set.Set String +blockDocBookTags :: Set.Set Text blockDocBookTags = Set.fromList ["calloutlist", "bibliolist", "glosslist", "itemizedlist", "orderedlist", "segmentedlist", "simplelist", @@ -908,37 +933,52 @@ blockDocBookTags = Set.fromList "classsynopsis", "blockquote", "epigraph", "msgset", "sidebar", "title"] -epubTags :: Set.Set String +epubTags :: Set.Set Text epubTags = Set.fromList ["case", "switch", "default"] -blockTags :: Set.Set String +blockTags :: Set.Set Text blockTags = Set.unions [blockHtmlTags, blockDocBookTags, epubTags] -isInlineTag :: Tag String -> Bool -isInlineTag t = tagOpen isInlineTagName (const True) t || - tagClose isInlineTagName t || - tagComment (const True) t - where isInlineTagName x = x `Set.notMember` blockTags - -isBlockTag :: Tag String -> Bool -isBlockTag t = tagOpen isBlockTagName (const True) t || - tagClose isBlockTagName t || - tagComment (const True) t - where isBlockTagName ('?':_) = True - isBlockTagName ('!':_) = True - isBlockTagName x = x `Set.member` blockTags - || x `Set.member` - eitherBlockOrInline - -isTextTag :: Tag String -> Bool +class NamedTag a where + getTagName :: a -> Maybe Text + +instance NamedTag (Tag Text) where + getTagName (TagOpen t _) = Just t + getTagName (TagClose t) = Just t + getTagName _ = Nothing + +instance NamedTag (Tag String) where + getTagName (TagOpen t _) = Just (T.pack t) + getTagName (TagClose t) = Just (T.pack t) + getTagName _ = Nothing + +isInlineTag :: NamedTag (Tag a) => Tag a -> Bool +isInlineTag t = isInlineTagName || isCommentTag t + where isInlineTagName = case getTagName t of + Just x -> x + `Set.notMember` blockTags + Nothing -> False + +isBlockTag :: NamedTag (Tag a) => Tag a -> Bool +isBlockTag t = isBlockTagName || isTagComment t + where isBlockTagName = + case getTagName t of + Just x + | "?" `T.isPrefixOf` x -> True + | "!" `T.isPrefixOf` x -> True + | otherwise -> x `Set.member` blockTags + || x `Set.member` eitherBlockOrInline + Nothing -> False + +isTextTag :: Tag a -> Bool isTextTag = tagText (const True) -isCommentTag :: Tag String -> Bool +isCommentTag :: Tag a -> Bool isCommentTag = tagComment (const True) -- taken from HXT and extended -- See http://www.w3.org/TR/html5/syntax.html sec 8.1.2.4 optional tags -closes :: String -> String -> Bool +closes :: Text -> Text -> Bool _ `closes` "body" = False _ `closes` "html" = False "body" `closes` "head" = True @@ -1000,8 +1040,11 @@ htmlInBalanced f = try $ do let cs = ec - sc lscontents <- unlines <$> count ls anyLine cscontents <- count cs anyChar - (_,closetag) <- htmlTag (~== TagClose tn) - return (lscontents ++ cscontents ++ closetag) + closetag <- do + x <- many (satisfy (/='>')) + char '>' + return (x <> ">") + return (lscontents <> cscontents <> closetag) _ -> mzero _ -> mzero @@ -1019,7 +1062,7 @@ htmlInBalanced' tagname ts = fromMaybe [] $ go 0 ts go n (t:ts') = (t :) <$> go n ts' go _ [] = mzero -hasTagWarning :: [Tag String] -> Bool +hasTagWarning :: [Tag a] -> Bool hasTagWarning (TagWarning _:_) = True hasTagWarning _ = False @@ -1047,47 +1090,48 @@ htmlTag f = try $ do -- basic sanity check, since the parser is very forgiving -- and finds tags in stuff like x<y) guard $ isName tagname + guard $ not $ null tagname -- <https://example.org> should NOT be a tag either. -- tagsoup will parse it as TagOpen "https:" [("example.org","")] guard $ last tagname /= ':' rendered <- manyTill anyChar (char '>') - return (next, rendered ++ ">") + return (next, rendered <> ">") case next of TagComment s | "<!--" `isPrefixOf` inp -> do count (length s + 4) anyChar skipMany (satisfy (/='>')) char '>' - return (next, "<!--" ++ s ++ "-->") + return (next, "<!--" <> s <> "-->") | otherwise -> fail "bogus comment mode, HTML5 parse error" TagOpen tagname attr -> do guard $ all (isName . fst) attr handleTag tagname - TagClose tagname -> handleTag tagname + TagClose tagname -> + handleTag tagname _ -> mzero mkAttr :: [(String, String)] -> Attr mkAttr attr = (attribsId, attribsClasses, attribsKV) where attribsId = fromMaybe "" $ lookup "id" attr - attribsClasses = (words $ fromMaybe "" $ lookup "class" attr) ++ epubTypes + attribsClasses = (words $ fromMaybe "" $ lookup "class" attr) <> epubTypes attribsKV = filter (\(k,_) -> k /= "class" && k /= "id") attr epubTypes = words $ fromMaybe "" $ lookup "epub:type" attr -- Strip namespace prefixes -stripPrefixes :: [Tag String] -> [Tag String] +stripPrefixes :: [Tag Text] -> [Tag Text] stripPrefixes = map stripPrefix -stripPrefix :: Tag String -> Tag String +stripPrefix :: Tag Text -> Tag Text stripPrefix (TagOpen s as) = TagOpen (stripPrefix' s) (map (stripPrefix' *** id) as) stripPrefix (TagClose s) = TagClose (stripPrefix' s) stripPrefix x = x -stripPrefix' :: String -> String +stripPrefix' :: Text -> Text stripPrefix' s = - case span (/= ':') s of - (_, "") -> s - (_, (_:ts)) -> ts + if T.null t then s else T.drop 1 t + where (_, t) = T.span (/= ':') s isSpace :: Char -> Bool isSpace ' ' = True @@ -1130,19 +1174,32 @@ instance HasLastStrPosition HTMLState where setLastStrPos s st = st {parserState = setLastStrPos s (parserState st)} getLastStrPos = getLastStrPos . parserState +-- For now we need a special verison here; the one in Shared has String type +renderTags' :: [Tag Text] -> Text +renderTags' = renderTagsOptions + renderOptions{ optMinimize = matchTags ["hr", "br", "img", + "meta", "link"] + , optRawTag = matchTags ["script", "style"] } + where matchTags = \tags -> flip elem tags . T.toLower + -- EPUB Specific -- -- -sectioningContent :: [String] +sectioningContent :: [Text] sectioningContent = ["article", "aside", "nav", "section"] -groupingContent :: [String] +groupingContent :: [Text] groupingContent = ["p", "hr", "pre", "blockquote", "ol" , "ul", "li", "dl", "dt", "dt", "dd" , "figure", "figcaption", "div", "main"] +matchTagClose :: Text -> (Tag Text -> Bool) +matchTagClose t = (~== TagClose t) + +matchTagOpen :: Text -> [(Text, Text)] -> (Tag Text -> Bool) +matchTagOpen t as = (~== TagOpen t as) {- @@ -1150,7 +1207,7 @@ types :: [(String, ([String], Int))] types = -- Document divisions map (\s -> (s, (["section", "body"], 0))) ["volume", "part", "chapter", "division"] - ++ -- Document section and components + <> -- Document section and components [ ("abstract", ([], 0))] -} -- cgit v1.2.3 From d1da54a4c3138df6781dfe1d67a4d83d2f8adc61 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 11 Jun 2017 21:22:44 +0200 Subject: Properly decode source from stdin. This should fix the appveyor failures. --- src/Text/Pandoc/App.hs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 658266046..19066e8b7 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -56,7 +56,6 @@ import Data.Maybe (fromMaybe, isJust, isNothing) import qualified Data.Set as Set import Data.Text (Text) import qualified Data.Text as T -import qualified Data.Text.IO as T import Data.Yaml (decode) import qualified Data.Yaml as Yaml import GHC.Generics @@ -791,7 +790,7 @@ applyFilters mbDatadir filters args d = do foldrM ($) d $ map (flip externalFilter args) expandedFilters readSource :: FilePath -> PandocIO Text -readSource "-" = liftIO T.getContents +readSource "-" = liftIO (UTF8.toText <$> BS.getContents) readSource src = case parseURI src of Just u | uriScheme u `elem` ["http:","https:"] -> readURI src -- cgit v1.2.3 From b466152d6185750438f7355701ae68186692d65b Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 11 Jun 2017 22:24:20 +0200 Subject: Don't allow backslash + newline to affect block structure. Note that as a result of this change, the following, which formerly produced a header with two lines separated by a line break, will now produce a header followed by a paragraph: # Hi\ there This may affect some existing documents that relied on this undocumented and unintended behavior. This change makes pandoc more consistent with other Markdown implementations, and with itself (since the two-space version of a line break doesn't work inside ATX headers, and neither version works inside Setext headers). Closes #3730. --- src/Text/Pandoc/Readers/Markdown.hs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 5e966a17e..e1c481311 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -1451,6 +1451,7 @@ inline = choice [ whitespace , autoLink , spanHtml , rawHtmlInline + , escapedNewline , escapedChar , rawLaTeXInline' , exampleRef @@ -1467,16 +1468,20 @@ escapedChar' = try $ do (guardEnabled Ext_all_symbols_escapable >> satisfy (not . isAlphaNum)) <|> (guardEnabled Ext_angle_brackets_escapable >> oneOf "\\`*_{}[]()>#+-.!~\"<>") - <|> (guardEnabled Ext_escaped_line_breaks >> char '\n') <|> oneOf "\\`*_{}[]()>#+-.!~\"" +escapedNewline :: PandocMonad m => MarkdownParser m (F Inlines) +escapedNewline = try $ do + guardEnabled Ext_escaped_line_breaks + char '\\' + lookAhead (char '\n') -- don't consume the newline (see #3730) + return $ return B.linebreak + escapedChar :: PandocMonad m => MarkdownParser m (F Inlines) escapedChar = do result <- escapedChar' case result of ' ' -> return $ return $ B.str "\160" -- "\ " is a nonbreaking space - '\n' -> guardEnabled Ext_escaped_line_breaks >> - return (return B.linebreak) -- "\[newline]" is a linebreak _ -> return $ return $ B.str [result] ltSign :: PandocMonad m => MarkdownParser m (F Inlines) -- cgit v1.2.3 From 8a000e3ecc330ff8a4953ebe8c7da9a54eca5c58 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 12 Jun 2017 09:16:05 +0200 Subject: Markdown writer: don't allow soft break in header. Closes #3736. --- src/Text/Pandoc/Writers/Markdown.hs | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 69243a214..3ac677943 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -475,6 +475,8 @@ blockToMarkdown' opts (Header level attr inlines) = do space <> attrsToMarkdown attr | otherwise -> empty contents <- inlineListToMarkdown opts $ + -- ensure no newlines; see #3736 + walk lineBreakToSpace $ if level == 1 && plain then capitalize inlines else inlines @@ -1203,3 +1205,8 @@ toSubscript c Just $ chr (0x2080 + (ord c - 48)) | isSpace c = Just c | otherwise = Nothing + +lineBreakToSpace :: Inline -> Inline +lineBreakToSpace LineBreak = Space +lineBreakToSpace SoftBreak = Space +lineBreakToSpace x = x -- cgit v1.2.3 From 23f3c2d7b4796d1af742a74999ce67924bf2abb3 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 12 Jun 2017 15:28:39 +0200 Subject: Changed "extracting..." warning to a regular log message. This makes it sensitive to proper verbosity settings. (It is now treated as INFO rather than WARNING, so one doesn't get these messages for creation of tmp images while making a pdf.) API changes: * Removed extractMediaBag from Text.Pandoc.MediaBag. * Added Extracting as constructor for LogMessage. --- src/Text/Pandoc/Class.hs | 23 +++++++++++++++++++---- src/Text/Pandoc/Logging.hs | 6 ++++++ src/Text/Pandoc/MediaBag.hs | 31 ------------------------------- 3 files changed, 25 insertions(+), 35 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 91731d396..14a0b8044 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -93,15 +93,16 @@ import Network.URI ( escapeURIString, nonStrictRelativeTo, unEscapeString, parseURIReference, isAllowedInURI, parseURI, URI(..) ) import qualified Data.Time.LocalTime as IO (getCurrentTimeZone) -import Text.Pandoc.MediaBag (MediaBag, lookupMedia, extractMediaBag, - mediaDirectory) +import Text.Pandoc.MediaBag (MediaBag, lookupMedia, mediaDirectory) import Text.Pandoc.Walk (walkM, walk) import qualified Text.Pandoc.MediaBag as MB import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL import qualified System.Environment as IO (lookupEnv) import System.FilePath.Glob (match, compile) -import System.FilePath ((</>), (<.>), takeExtension, dropExtension, isRelative) +import System.Directory (createDirectoryIfMissing) +import System.FilePath ((</>), (<.>), takeDirectory, + takeExtension, dropExtension, isRelative, normalise) import qualified System.FilePath.Glob as IO (glob) import qualified System.Directory as IO (getModificationTime) import Control.Monad as M (fail) @@ -387,9 +388,23 @@ extractMedia dir d = do case [fp | (fp, _, _) <- mediaDirectory media] of [] -> return d fps -> do - liftIO $ extractMediaBag True dir media + mapM_ (writeMedia dir media) fps return $ walk (adjustImagePath dir fps) d +writeMedia :: FilePath -> MediaBag -> FilePath -> PandocIO () +writeMedia dir mediabag subpath = do + -- we join and split to convert a/b/c to a\b\c on Windows; + -- in zip containers all paths use / + let fullpath = dir </> normalise subpath + let mbcontents = lookupMedia subpath mediabag + case mbcontents of + Nothing -> throwError $ PandocResourceNotFound subpath + Just (_, bs) -> do + report $ Extracting fullpath + liftIO $ do + createDirectoryIfMissing True $ takeDirectory fullpath + BL.writeFile fullpath bs + adjustImagePath :: FilePath -> [FilePath] -> Inline -> Inline adjustImagePath dir paths (Image attr lab (src, tit)) | src `elem` paths = Image attr lab (dir ++ "/" ++ src, tit) diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs index 7afce9f5f..da8c775f6 100644 --- a/src/Text/Pandoc/Logging.hs +++ b/src/Text/Pandoc/Logging.hs @@ -89,6 +89,7 @@ data LogMessage = | CouldNotConvertTeXMath String String | CouldNotParseCSS String | Fetching String + | Extracting String | NoTitleElement String | NoLangSpecified | CouldNotHighlight String @@ -178,6 +179,8 @@ instance ToJSON LogMessage where ["message" .= Text.pack msg] Fetching fp -> ["path" .= Text.pack fp] + Extracting fp -> + ["path" .= Text.pack fp] NoTitleElement fallback -> ["fallback" .= Text.pack fallback] NoLangSpecified -> [] @@ -248,6 +251,8 @@ showLogMessage msg = "Could not parse CSS" ++ if null m then "" else (':':'\n':m) Fetching fp -> "Fetching " ++ fp ++ "..." + Extracting fp -> + "Extracting " ++ fp ++ "..." NoTitleElement fallback -> "This document format requires a nonempty <title> element.\n" ++ "Please specify either 'title' or 'pagetitle' in the metadata.\n" ++ @@ -282,6 +287,7 @@ messageVerbosity msg = CouldNotConvertTeXMath{} -> WARNING CouldNotParseCSS{} -> WARNING Fetching{} -> INFO + Extracting{} -> INFO NoTitleElement{} -> WARNING NoLangSpecified -> INFO CouldNotHighlight{} -> WARNING diff --git a/src/Text/Pandoc/MediaBag.hs b/src/Text/Pandoc/MediaBag.hs index 980511acc..d8d6da345 100644 --- a/src/Text/Pandoc/MediaBag.hs +++ b/src/Text/Pandoc/MediaBag.hs @@ -35,21 +35,15 @@ module Text.Pandoc.MediaBag ( lookupMedia, insertMedia, mediaDirectory, - extractMediaBag ) where -import Control.Monad (when) -import Control.Monad.Trans (MonadIO (..)) import qualified Data.ByteString.Lazy as BL import Data.Data (Data) import qualified Data.Map as M import Data.Maybe (fromMaybe) import Data.Typeable (Typeable) -import System.Directory (createDirectoryIfMissing) import System.FilePath import qualified System.FilePath.Posix as Posix -import System.IO (stderr) import Text.Pandoc.MIME (MimeType, getMimeTypeDef) -import qualified Text.Pandoc.UTF8 as UTF8 -- | A container for a collection of binary resources, with names and -- mime types. Note that a 'MediaBag' is a Monoid, so 'mempty' @@ -87,28 +81,3 @@ mediaDirectory :: MediaBag -> [(String, MimeType, Int)] mediaDirectory (MediaBag mediamap) = M.foldWithKey (\fp (mime,contents) -> (((Posix.joinPath fp), mime, fromIntegral $ BL.length contents):)) [] mediamap - --- | Extract contents of MediaBag to a given directory. Print informational --- messages if 'verbose' is true. --- 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 - -> m () -extractMediaBag verbose dir (MediaBag mediamap) = liftIO $ do - sequence_ $ M.foldWithKey - (\fp (_ ,contents) -> - ((writeMedia verbose dir (Posix.joinPath fp, contents)):)) [] mediamap - -writeMedia :: Bool -> FilePath -> (FilePath, BL.ByteString) -> IO () -writeMedia verbose dir (subpath, bs) = do - -- we join and split to convert a/b/c to a\b\c on Windows; - -- in zip containers all paths use / - let fullpath = dir </> normalise subpath - createDirectoryIfMissing True $ takeDirectory fullpath - when verbose $ UTF8.hPutStrLn stderr $ "pandoc: extracting " ++ fullpath - BL.writeFile fullpath bs - - -- cgit v1.2.3 From 9849ba7fd744f529f063e0802a18fa18c8433eeb Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 16 Jun 2017 23:29:37 +0200 Subject: Use Control.Monad.State.Strict throughout. This gives 20-30% speedup and reduction of memory usage in most of the writers. --- src/Text/Pandoc/Class.hs | 2 +- src/Text/Pandoc/Compat/Time.hs | 2 +- src/Text/Pandoc/Pretty.hs | 2 +- src/Text/Pandoc/Readers/DocBook.hs | 2 +- src/Text/Pandoc/Readers/Docx.hs | 2 +- src/Text/Pandoc/Readers/Docx/Parse.hs | 2 +- src/Text/Pandoc/Readers/Docx/StyleMap.hs | 2 +- src/Text/Pandoc/Readers/OPML.hs | 2 +- src/Text/Pandoc/Shared.hs | 2 +- src/Text/Pandoc/Writers/AsciiDoc.hs | 2 +- src/Text/Pandoc/Writers/CommonMark.hs | 2 +- src/Text/Pandoc/Writers/ConTeXt.hs | 2 +- src/Text/Pandoc/Writers/Docx.hs | 2 +- src/Text/Pandoc/Writers/DokuWiki.hs | 2 +- src/Text/Pandoc/Writers/EPUB.hs | 2 +- src/Text/Pandoc/Writers/FB2.hs | 4 ++-- src/Text/Pandoc/Writers/HTML.hs | 2 +- src/Text/Pandoc/Writers/Haddock.hs | 2 +- src/Text/Pandoc/Writers/ICML.hs | 2 +- src/Text/Pandoc/Writers/LaTeX.hs | 2 +- src/Text/Pandoc/Writers/Man.hs | 2 +- src/Text/Pandoc/Writers/Markdown.hs | 2 +- src/Text/Pandoc/Writers/MediaWiki.hs | 2 +- src/Text/Pandoc/Writers/Ms.hs | 2 +- src/Text/Pandoc/Writers/Muse.hs | 2 +- src/Text/Pandoc/Writers/ODT.hs | 2 +- src/Text/Pandoc/Writers/OpenDocument.hs | 2 +- src/Text/Pandoc/Writers/Org.hs | 2 +- src/Text/Pandoc/Writers/RST.hs | 2 +- src/Text/Pandoc/Writers/Texinfo.hs | 2 +- src/Text/Pandoc/Writers/Textile.hs | 2 +- src/Text/Pandoc/Writers/ZimWiki.hs | 2 +- 32 files changed, 33 insertions(+), 33 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 14a0b8044..8db2e214e 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -107,7 +107,7 @@ import qualified System.FilePath.Glob as IO (glob) import qualified System.Directory as IO (getModificationTime) import Control.Monad as M (fail) import Control.Monad.Reader (ReaderT) -import Control.Monad.State +import Control.Monad.State.Strict import Control.Monad.Except import Control.Monad.Writer (WriterT) import Control.Monad.RWS (RWST) diff --git a/src/Text/Pandoc/Compat/Time.hs b/src/Text/Pandoc/Compat/Time.hs index 1de197801..b1cde82a4 100644 --- a/src/Text/Pandoc/Compat/Time.hs +++ b/src/Text/Pandoc/Compat/Time.hs @@ -27,4 +27,4 @@ where import Data.Time import System.Locale ( defaultTimeLocale ) -#endif \ No newline at end of file +#endif diff --git a/src/Text/Pandoc/Pretty.hs b/src/Text/Pandoc/Pretty.hs index d78a2f1d9..1b3c647a1 100644 --- a/src/Text/Pandoc/Pretty.hs +++ b/src/Text/Pandoc/Pretty.hs @@ -77,7 +77,7 @@ module Text.Pandoc.Pretty ( ) where -import Control.Monad.State +import Control.Monad.State.Strict import Data.Char (isSpace) import Data.Foldable (toList) import Data.List (intersperse) diff --git a/src/Text/Pandoc/Readers/DocBook.hs b/src/Text/Pandoc/Readers/DocBook.hs index bd3c7c356..6108aae7f 100644 --- a/src/Text/Pandoc/Readers/DocBook.hs +++ b/src/Text/Pandoc/Readers/DocBook.hs @@ -9,7 +9,7 @@ import Text.HTML.TagSoup.Entity (lookupEntity) import Data.Either (rights) import Data.Generics import Data.Char (isSpace) -import Control.Monad.State +import Control.Monad.State.Strict import Data.List (intersperse) import Data.Maybe (fromMaybe) import Text.TeXMath (readMathML, writeTeX) diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index 2757314ab..21aa358f2 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -76,7 +76,7 @@ module Text.Pandoc.Readers.Docx import Codec.Archive.Zip import Control.Monad.Reader -import Control.Monad.State +import Control.Monad.State.Strict import qualified Data.ByteString.Lazy as B import Data.Default (Default) import Data.List (delete, intersect) diff --git a/src/Text/Pandoc/Readers/Docx/Parse.hs b/src/Text/Pandoc/Readers/Docx/Parse.hs index e6736100f..24615ba94 100644 --- a/src/Text/Pandoc/Readers/Docx/Parse.hs +++ b/src/Text/Pandoc/Readers/Docx/Parse.hs @@ -58,7 +58,7 @@ import Codec.Archive.Zip import Control.Applicative ((<|>)) import Control.Monad.Except import Control.Monad.Reader -import Control.Monad.State +import Control.Monad.State.Strict import Data.Bits ((.|.)) import qualified Data.ByteString.Lazy as B import Data.Char (chr, isDigit, ord, readLitChar) diff --git a/src/Text/Pandoc/Readers/Docx/StyleMap.hs b/src/Text/Pandoc/Readers/Docx/StyleMap.hs index 38f976fd8..b32a73770 100644 --- a/src/Text/Pandoc/Readers/Docx/StyleMap.hs +++ b/src/Text/Pandoc/Readers/Docx/StyleMap.hs @@ -7,7 +7,7 @@ module Text.Pandoc.Readers.Docx.StyleMap ( StyleMaps(..) , hasStyleName ) where -import Control.Monad.State +import Control.Monad.State.Strict import Data.Char (toLower) import qualified Data.Map as M import Text.Pandoc.Readers.Docx.Util diff --git a/src/Text/Pandoc/Readers/OPML.hs b/src/Text/Pandoc/Readers/OPML.hs index 591d7590e..e9f876525 100644 --- a/src/Text/Pandoc/Readers/OPML.hs +++ b/src/Text/Pandoc/Readers/OPML.hs @@ -1,6 +1,6 @@ {-# LANGUAGE FlexibleContexts #-} module Text.Pandoc.Readers.OPML ( readOPML ) where -import Control.Monad.State +import Control.Monad.State.Strict import Data.Char (toUpper) import Data.Text (Text, unpack, pack) import Data.Default diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 745e809d0..7b299c56b 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -117,7 +117,7 @@ import Text.Pandoc.MIME (MimeType) import Text.Pandoc.Error (PandocError(..)) import System.FilePath ( (</>) ) import Data.Generics (Typeable, Data) -import qualified Control.Monad.State as S +import qualified Control.Monad.State.Strict as S import qualified Control.Exception as E import Control.Monad (msum, unless, MonadPlus(..)) import Text.Pandoc.Pretty (charWidth) diff --git a/src/Text/Pandoc/Writers/AsciiDoc.hs b/src/Text/Pandoc/Writers/AsciiDoc.hs index 46dbe6eaf..ee977f90b 100644 --- a/src/Text/Pandoc/Writers/AsciiDoc.hs +++ b/src/Text/Pandoc/Writers/AsciiDoc.hs @@ -37,7 +37,7 @@ that it has omitted the construct. AsciiDoc: <http://www.methods.co.nz/asciidoc/> -} module Text.Pandoc.Writers.AsciiDoc (writeAsciiDoc) where -import Control.Monad.State +import Control.Monad.State.Strict import Data.Aeson (Result (..), Value (String), fromJSON, toJSON) import Data.Char (isPunctuation, isSpace) import Data.List (intercalate, intersperse, stripPrefix) diff --git a/src/Text/Pandoc/Writers/CommonMark.hs b/src/Text/Pandoc/Writers/CommonMark.hs index ed316ced9..93cc0b53a 100644 --- a/src/Text/Pandoc/Writers/CommonMark.hs +++ b/src/Text/Pandoc/Writers/CommonMark.hs @@ -32,7 +32,7 @@ CommonMark: <http://commonmark.org> module Text.Pandoc.Writers.CommonMark (writeCommonMark) where import CMark -import Control.Monad.State (State, get, modify, runState) +import Control.Monad.State.Strict (State, get, modify, runState) import Data.Foldable (foldrM) import Data.Text (Text) import qualified Data.Text as T diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index 2da6a7f9a..571c55b19 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -29,7 +29,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Conversion of 'Pandoc' format into ConTeXt. -} module Text.Pandoc.Writers.ConTeXt ( writeConTeXt ) where -import Control.Monad.State +import Control.Monad.State.Strict import Data.Char (ord) import Data.List (intercalate, intersperse) import Data.Maybe (catMaybes) diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 63bb8a5ae..b488f2479 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -37,7 +37,7 @@ import Codec.Archive.Zip import Control.Applicative ((<|>)) import Control.Monad.Except (catchError) import Control.Monad.Reader -import Control.Monad.State +import Control.Monad.State.Strict import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Lazy.Char8 as BL8 diff --git a/src/Text/Pandoc/Writers/DokuWiki.hs b/src/Text/Pandoc/Writers/DokuWiki.hs index 551a1b0b5..dc227cfa9 100644 --- a/src/Text/Pandoc/Writers/DokuWiki.hs +++ b/src/Text/Pandoc/Writers/DokuWiki.hs @@ -41,7 +41,7 @@ DokuWiki: <https://www.dokuwiki.org/dokuwiki> module Text.Pandoc.Writers.DokuWiki ( writeDokuWiki ) where import Control.Monad (zipWithM) import Control.Monad.Reader (ReaderT, ask, local, runReaderT) -import Control.Monad.State (StateT, evalStateT, gets, modify) +import Control.Monad.State.Strict (StateT, evalStateT, gets, modify) import Data.Default (Default (..)) import Data.List (intercalate, intersect, isPrefixOf, transpose) import Data.Text (Text, pack) diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index d68283007..bd9a4c800 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -36,7 +36,7 @@ import Codec.Archive.Zip (Entry, addEntryToArchive, eRelativePath, emptyArchive, fromArchive, fromEntry, toEntry) import Control.Monad (mplus, when, zipWithM) import Control.Monad.Except (catchError, throwError) -import Control.Monad.State (State, StateT, evalState, evalStateT, get, gets, +import Control.Monad.State.Strict (State, StateT, evalState, evalStateT, get, gets, lift, modify, put) import qualified Data.ByteString.Lazy as B import qualified Data.ByteString.Lazy.Char8 as B8 diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index 213756330..20f94c185 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -38,8 +38,8 @@ FictionBook is an XML-based e-book format. For more information see: module Text.Pandoc.Writers.FB2 (writeFB2) where import Control.Monad.Except (catchError, throwError) -import Control.Monad.State (StateT, evalStateT, get, lift, modify) -import Control.Monad.State (liftM) +import Control.Monad.State.Strict (StateT, evalStateT, get, lift, modify) +import Control.Monad.State.Strict (liftM) import Data.ByteString.Base64 (encode) import qualified Data.ByteString.Char8 as B8 import Data.Char (isAscii, isControl, isSpace, toLower) diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 5ee8ab4ce..7de38f49a 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -43,7 +43,7 @@ module Text.Pandoc.Writers.HTML ( writeDZSlides, writeRevealJs ) where -import Control.Monad.State +import Control.Monad.State.Strict import Data.Char (ord, toLower) import Data.Text (Text) import qualified Data.Text.Lazy as TL diff --git a/src/Text/Pandoc/Writers/Haddock.hs b/src/Text/Pandoc/Writers/Haddock.hs index 1ad9acd40..7965ebfae 100644 --- a/src/Text/Pandoc/Writers/Haddock.hs +++ b/src/Text/Pandoc/Writers/Haddock.hs @@ -33,7 +33,7 @@ Conversion of 'Pandoc' documents to haddock markup. Haddock: <http://www.haskell.org/haddock/doc/html/> -} module Text.Pandoc.Writers.Haddock (writeHaddock) where -import Control.Monad.State +import Control.Monad.State.Strict import Data.Default import Data.Text (Text) import Data.List (intersperse, transpose) diff --git a/src/Text/Pandoc/Writers/ICML.hs b/src/Text/Pandoc/Writers/ICML.hs index 2884bc532..e564f94fe 100644 --- a/src/Text/Pandoc/Writers/ICML.hs +++ b/src/Text/Pandoc/Writers/ICML.hs @@ -17,7 +17,7 @@ into InDesign with File -> Place. -} module Text.Pandoc.Writers.ICML (writeICML) where import Control.Monad.Except (catchError) -import Control.Monad.State +import Control.Monad.State.Strict import Data.List (intersperse, isInfixOf, isPrefixOf, stripPrefix) import qualified Data.Set as Set import Data.Text as Text (breakOnAll, pack) diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 80606d510..e0ea9acfe 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -35,7 +35,7 @@ module Text.Pandoc.Writers.LaTeX ( , writeBeamer ) where import Control.Applicative ((<|>)) -import Control.Monad.State +import Control.Monad.State.Strict import Data.Aeson (FromJSON, object, (.=)) import Data.Char (isAlphaNum, isAscii, isDigit, isLetter, isPunctuation, ord, toLower) diff --git a/src/Text/Pandoc/Writers/Man.hs b/src/Text/Pandoc/Writers/Man.hs index 0fc6afbdc..d96342fb5 100644 --- a/src/Text/Pandoc/Writers/Man.hs +++ b/src/Text/Pandoc/Writers/Man.hs @@ -31,7 +31,7 @@ Conversion of 'Pandoc' documents to groff man page format. -} module Text.Pandoc.Writers.Man ( writeMan) where import Control.Monad.Except (throwError) -import Control.Monad.State +import Control.Monad.State.Strict import Data.List (intercalate, intersperse, stripPrefix, sort) import qualified Data.Map as Map import Data.Maybe (fromMaybe) diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 3ac677943..4449bb5ce 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -35,7 +35,7 @@ Markdown: <http://daringfireball.net/projects/markdown/> -} module Text.Pandoc.Writers.Markdown (writeMarkdown, writePlain) where import Control.Monad.Reader -import Control.Monad.State +import Control.Monad.State.Strict import Data.Char (chr, isPunctuation, isSpace, ord) import Data.Default import qualified Data.HashMap.Strict as H diff --git a/src/Text/Pandoc/Writers/MediaWiki.hs b/src/Text/Pandoc/Writers/MediaWiki.hs index c70e5b786..3825a4e73 100644 --- a/src/Text/Pandoc/Writers/MediaWiki.hs +++ b/src/Text/Pandoc/Writers/MediaWiki.hs @@ -31,7 +31,7 @@ MediaWiki: <http://www.mediawiki.org/wiki/MediaWiki> -} module Text.Pandoc.Writers.MediaWiki ( writeMediaWiki ) where import Control.Monad.Reader -import Control.Monad.State +import Control.Monad.State.Strict import Data.List (intercalate) import qualified Data.Set as Set import Data.Text (Text, pack) diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index c5c3d9f5b..0999d13db 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -52,7 +52,7 @@ import Text.Pandoc.Pretty import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.ImageSize import Text.Pandoc.Logging -import Control.Monad.State +import Control.Monad.State.Strict import Data.Char ( isLower, isUpper, toUpper ) import Text.TeXMath (writeEqn) import System.FilePath (takeExtension) diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index 85e0b5467..286bd1431 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -42,7 +42,7 @@ However, @\<literal style="html">@ tag is used for HTML raw blocks even though it is supported only in Emacs Muse. -} module Text.Pandoc.Writers.Muse (writeMuse) where -import Control.Monad.State +import Control.Monad.State.Strict import Data.Text (Text) import Data.List (intersperse, transpose, isInfixOf) import System.FilePath (takeExtension) diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index 1da051380..c9a7de642 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -31,7 +31,7 @@ Conversion of 'Pandoc' documents to ODT. module Text.Pandoc.Writers.ODT ( writeODT ) where import Codec.Archive.Zip import Control.Monad.Except (catchError) -import Control.Monad.State +import Control.Monad.State.Strict import qualified Data.ByteString.Lazy as B import Data.List (isPrefixOf) import Data.Maybe (fromMaybe) diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index 58295684e..fd9a13f3e 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -33,7 +33,7 @@ Conversion of 'Pandoc' documents to OpenDocument XML. -} module Text.Pandoc.Writers.OpenDocument ( writeOpenDocument ) where import Control.Arrow ((***), (>>>)) -import Control.Monad.State hiding (when) +import Control.Monad.State.Strict hiding (when) import Data.Char (chr) import Data.List (sortBy) import Data.Text (Text) diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index e8f48da00..8524c441d 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -35,7 +35,7 @@ Conversion of 'Pandoc' documents to Emacs Org-Mode. Org-Mode: <http://orgmode.org> -} module Text.Pandoc.Writers.Org (writeOrg) where -import Control.Monad.State +import Control.Monad.State.Strict import Data.Char (isAlphaNum, toLower) import Data.Text (Text) import Data.List (intersect, intersperse, isPrefixOf, partition, transpose) diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index 59f6553e2..9c0693b0f 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -31,7 +31,7 @@ Conversion of 'Pandoc' documents to reStructuredText. reStructuredText: <http://docutils.sourceforge.net/rst.html> -} module Text.Pandoc.Writers.RST ( writeRST ) where -import Control.Monad.State +import Control.Monad.State.Strict import Data.Char (isSpace, toLower) import Data.List (isPrefixOf, stripPrefix) import Data.Maybe (fromMaybe) diff --git a/src/Text/Pandoc/Writers/Texinfo.hs b/src/Text/Pandoc/Writers/Texinfo.hs index 387e55290..fd489786d 100644 --- a/src/Text/Pandoc/Writers/Texinfo.hs +++ b/src/Text/Pandoc/Writers/Texinfo.hs @@ -32,7 +32,7 @@ Conversion of 'Pandoc' format into Texinfo. -} module Text.Pandoc.Writers.Texinfo ( writeTexinfo ) where import Control.Monad.Except (throwError) -import Control.Monad.State +import Control.Monad.State.Strict import Data.Char (chr, ord) import Data.List (maximumBy, transpose) import Data.Ord (comparing) diff --git a/src/Text/Pandoc/Writers/Textile.hs b/src/Text/Pandoc/Writers/Textile.hs index 091a5baca..432c055b8 100644 --- a/src/Text/Pandoc/Writers/Textile.hs +++ b/src/Text/Pandoc/Writers/Textile.hs @@ -30,7 +30,7 @@ Conversion of 'Pandoc' documents to Textile markup. Textile: <http://thresholdstate.com/articles/4312/the-textile-reference-manual> -} module Text.Pandoc.Writers.Textile ( writeTextile ) where -import Control.Monad.State +import Control.Monad.State.Strict import Data.Char (isSpace) import Data.List (intercalate) import Data.Text (Text, pack) diff --git a/src/Text/Pandoc/Writers/ZimWiki.hs b/src/Text/Pandoc/Writers/ZimWiki.hs index 5ee239e59..ba51acfce 100644 --- a/src/Text/Pandoc/Writers/ZimWiki.hs +++ b/src/Text/Pandoc/Writers/ZimWiki.hs @@ -33,7 +33,7 @@ http://zim-wiki.org/manual/Help/Wiki_Syntax.html module Text.Pandoc.Writers.ZimWiki ( writeZimWiki ) where import Control.Monad (zipWithM) -import Control.Monad.State (StateT, evalStateT, gets, modify) +import Control.Monad.State.Strict (StateT, evalStateT, gets, modify) import Data.Default (Default (..)) import Data.List (intercalate, isInfixOf, isPrefixOf, transpose) import qualified Data.Map as Map -- cgit v1.2.3 From abd2e94f5a8c1238eebeef9b6edb91b8031507e7 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 18 Jun 2017 11:17:00 +0200 Subject: In producing PDFs, warn if the font is missing some characters. * Added `MissingCharacter` to `LogMessage` in Text.Pandoc.Logging. * Parse the (xe)latex log for missing character warnings and issue the warning. Closes #3742. --- src/Text/Pandoc/Logging.hs | 6 ++++++ src/Text/Pandoc/PDF.hs | 47 +++++++++++++++++++++++++++++++++------------- 2 files changed, 40 insertions(+), 13 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs index da8c775f6..b31c33d4e 100644 --- a/src/Text/Pandoc/Logging.hs +++ b/src/Text/Pandoc/Logging.hs @@ -93,6 +93,7 @@ data LogMessage = | NoTitleElement String | NoLangSpecified | CouldNotHighlight String + | MissingCharacter String deriving (Show, Eq, Data, Ord, Typeable, Generic) instance ToJSON LogMessage where @@ -186,6 +187,8 @@ instance ToJSON LogMessage where NoLangSpecified -> [] CouldNotHighlight msg -> ["message" .= Text.pack msg] + MissingCharacter msg -> + ["message" .= Text.pack msg] showPos :: SourcePos -> String showPos pos = sn ++ "line " ++ @@ -262,6 +265,8 @@ showLogMessage msg = "It is recommended that lang be specified for this format." CouldNotHighlight m -> "Could not highlight code block:\n" ++ m + MissingCharacter m -> + "Missing character: " ++ m messageVerbosity:: LogMessage -> Verbosity messageVerbosity msg = @@ -291,3 +296,4 @@ messageVerbosity msg = NoTitleElement{} -> WARNING NoLangSpecified -> INFO CouldNotHighlight{} -> WARNING + MissingCharacter{} -> WARNING diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index cd75d869d..25a94972a 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -40,7 +40,6 @@ import qualified Data.Text as T import Data.Text (Text) import qualified Data.ByteString as BS import Data.ByteString.Lazy (ByteString) -import qualified Data.ByteString.Lazy as B import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Lazy.Char8 as BC import Data.Maybe (fromMaybe) @@ -197,7 +196,22 @@ tex2pdf' verbosity args tmpDir program source = do _ -> "" return $ Left $ logmsg <> extramsg (ExitSuccess, Nothing) -> return $ Left "" - (ExitSuccess, Just pdf) -> return $ Right pdf + (ExitSuccess, Just pdf) -> do + missingCharacterWarnings verbosity log' + return $ Right pdf + +missingCharacterWarnings :: Verbosity -> ByteString -> IO () +missingCharacterWarnings verbosity log' = do + let ls = BC.lines log' + let isMissingCharacterWarning = BC.isPrefixOf "Missing character: " + let warnings = [ UTF8.toStringLazy (BC.drop 19 l) + | l <- ls + , isMissingCharacterWarning l + ] + runIO $ do + setVerbosity verbosity + mapM_ (report . MissingCharacter) warnings + return () -- parsing output @@ -255,12 +269,12 @@ runTeXProgram verbosity program args runNumber numRuns tmpDir source = do mapM_ print env'' putStr "\n" putStrLn $ "[makePDF] Contents of " ++ file' ++ ":" - B.readFile file' >>= B.putStr + BL.readFile file' >>= BL.putStr putStr "\n" (exit, out) <- pipeProcess (Just env'') program programArgs BL.empty when (verbosity >= INFO) $ do putStrLn $ "[makePDF] Run #" ++ show runNumber - B.hPutStr stdout out + BL.hPutStr stdout out putStr "\n" if runNumber <= numRuns then runTeXProgram verbosity program args (runNumber + 1) numRuns tmpDir source @@ -271,9 +285,16 @@ runTeXProgram verbosity program args runNumber numRuns tmpDir source = do -- We read PDF as a strict bytestring to make sure that the -- temp directory is removed on Windows. -- See https://github.com/jgm/pandoc/issues/1192. - then (Just . B.fromChunks . (:[])) `fmap` BS.readFile pdfFile + then (Just . BL.fromChunks . (:[])) `fmap` BS.readFile pdfFile else return Nothing - return (exit, out, pdf) + -- Note that some things like Missing character warnings + -- appear in the log but not on stderr, so we prefer the log: + let logFile = replaceExtension file ".log" + logExists <- doesFileExist logFile + log' <- if logExists + then BL.readFile logFile + else return out + return (exit, log', pdf) ms2pdf :: Verbosity -> [String] @@ -294,7 +315,7 @@ ms2pdf verbosity args source = do (exit, out) <- pipeProcess (Just env') "pdfroff" args (BL.fromStrict $ UTF8.fromText source) when (verbosity >= INFO) $ do - B.hPutStr stdout out + BL.hPutStr stdout out putStr "\n" return $ case exit of ExitFailure _ -> Left out @@ -318,12 +339,12 @@ html2pdf verbosity args source = do mapM_ print env' putStr "\n" putStrLn $ "[makePDF] Contents of " ++ file ++ ":" - B.readFile file >>= B.putStr + BL.readFile file >>= BL.putStr putStr "\n" (exit, out) <- pipeProcess (Just env') "wkhtmltopdf" programArgs BL.empty removeFile file when (verbosity >= INFO) $ do - B.hPutStr stdout out + BL.hPutStr stdout out putStr "\n" pdfExists <- doesFileExist pdfFile mbPdf <- if pdfExists @@ -331,7 +352,7 @@ html2pdf verbosity args source = do -- temp directory is removed on Windows. -- See https://github.com/jgm/pandoc/issues/1192. then do - res <- (Just . B.fromChunks . (:[])) `fmap` BS.readFile pdfFile + res <- (Just . BL.fromChunks . (:[])) `fmap` BS.readFile pdfFile removeFile pdfFile return res else return Nothing @@ -365,11 +386,11 @@ context2pdf verbosity tmpDir source = inDirectory tmpDir $ do mapM_ print env' putStr "\n" putStrLn $ "[makePDF] Contents of " ++ file ++ ":" - B.readFile file >>= B.putStr + BL.readFile file >>= BL.putStr putStr "\n" (exit, out) <- pipeProcess (Just env') "context" programArgs BL.empty when (verbosity >= INFO) $ do - B.hPutStr stdout out + BL.hPutStr stdout out putStr "\n" let pdfFile = replaceExtension file ".pdf" pdfExists <- doesFileExist pdfFile @@ -377,7 +398,7 @@ context2pdf verbosity tmpDir source = inDirectory tmpDir $ do -- We read PDF as a strict bytestring to make sure that the -- temp directory is removed on Windows. -- See https://github.com/jgm/pandoc/issues/1192. - then (Just . B.fromChunks . (:[])) `fmap` BS.readFile pdfFile + then (Just . BL.fromChunks . (:[])) `fmap` BS.readFile pdfFile else return Nothing case (exit, mbPdf) of (ExitFailure _, _) -> do -- cgit v1.2.3 From ec3992b2f0aef0eefb85bdb693adfd0969126f7d Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 18 Jun 2017 11:41:40 +0200 Subject: Use revealjs's math plugin for mathjax. This is a thin wrapper around mathjax that makes math look better on revealjs. See https://github.com/hakimel/reveal.js/#mathjax We do this by setting the 'mathjax' boolean variable and using it in the revealjs template. Also, for revealjs and mathjax, we don't assign the usual thing to the 'math' variable, since it's handled by mathjax config. Closes #3743. --- src/Text/Pandoc/Writers/HTML.hs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 7de38f49a..43c098866 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -253,7 +253,9 @@ pandocToHtml opts (Pandoc meta blocks) = do H.script ! A.src (toValue url) ! A.type_ "text/javascript" $ mempty - MathJax url -> + MathJax url + | slideVariant /= RevealJsSlides -> + -- mathjax is handled via a special plugin in revealjs H.script ! A.src (toValue url) ! A.type_ "text/javascript" $ case slideVariant of @@ -285,6 +287,10 @@ pandocToHtml opts (Pandoc meta blocks) = do (if stMath st then defField "math" (renderHtml' math) else id) $ + defField "mathjax" + (case writerHTMLMathMethod opts of + MathJax _ -> True + _ -> False) $ defField "quotes" (stQuotes st) $ maybe id (defField "toc" . renderHtml') toc $ defField "author-meta" authsMeta $ -- cgit v1.2.3 From a91b9b2a1d768cd8a4dfff3c7e72a3cc96153d83 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 19 Jun 2017 11:46:02 +0300 Subject: Add Muse reader (#3620) --- src/Text/Pandoc/Readers.hs | 3 + src/Text/Pandoc/Readers/Muse.hs | 577 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 580 insertions(+) create mode 100644 src/Text/Pandoc/Readers/Muse.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers.hs b/src/Text/Pandoc/Readers.hs index 004fefe25..4c95d5d28 100644 --- a/src/Text/Pandoc/Readers.hs +++ b/src/Text/Pandoc/Readers.hs @@ -59,6 +59,7 @@ module Text.Pandoc.Readers , readTWiki , readTxt2Tags , readEPUB + , readMuse -- * Miscellaneous , getReader , getDefaultExtensions @@ -81,6 +82,7 @@ import Text.Pandoc.Readers.HTML import Text.Pandoc.Readers.LaTeX import Text.Pandoc.Readers.Markdown import Text.Pandoc.Readers.MediaWiki +import Text.Pandoc.Readers.Muse import Text.Pandoc.Readers.Native import Text.Pandoc.Readers.Odt import Text.Pandoc.Readers.OPML @@ -125,6 +127,7 @@ readers = [ ("native" , TextReader readNative) ,("odt" , ByteStringReader readOdt) ,("t2t" , TextReader readTxt2Tags) ,("epub" , ByteStringReader readEPUB) + ,("muse" , TextReader readMuse) ] -- | Retrieve reader based on formatSpec (format+extensions). diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs new file mode 100644 index 000000000..bc9da26cb --- /dev/null +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -0,0 +1,577 @@ +{- + Copyright (C) 2017 Alexander Krotov <ilabdsf@gmail.com> + +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.Muse + Copyright : Copyright (C) 2017 Alexander Krotov + License : GNU GPL, version 2 or above + + Maintainer : Alexander Krotov <ilabdsf@gmail.com> + Stability : alpha + Portability : portable + +Conversion of Muse text to 'Pandoc' document. +-} +{- +TODO: +- {{{ }}} syntax for <example> +- Page breaks (five "*") +- Headings with anchors (make it round trip with Muse writer) +- <verse> and ">" +- Definition lists +- Org tables +- table.el tables +- Images with attributes (floating and width) +- Anchors +- Citations and <biblio> +- <play> environment +- <verbatim> tag +-} +module Text.Pandoc.Readers.Muse (readMuse) where + +import Control.Monad +import Control.Monad.Except (throwError) +import qualified Data.Map as M +import Data.Text (Text, unpack) +import Data.List (stripPrefix) +import Data.Maybe (fromMaybe) +import Text.HTML.TagSoup +import Text.Pandoc.Builder (Blocks, Inlines) +import qualified Text.Pandoc.Builder as B +import Text.Pandoc.Class (PandocMonad, report) +import Text.Pandoc.Definition +import Text.Pandoc.Logging +import Text.Pandoc.Options +import Text.Pandoc.Parsing hiding (macro, nested) +import Text.Pandoc.Readers.HTML (htmlTag) +import Text.Pandoc.XML (fromEntities) +import System.FilePath (takeExtension) + +-- | Read Muse from an input string and return a Pandoc document. +readMuse :: PandocMonad m + => ReaderOptions + -> Text + -> m Pandoc +readMuse opts s = do + res <- readWithM parseMuse def{ stateOptions = opts } (unpack s) + case res of + Left e -> throwError e + Right d -> return d + +type MuseParser = ParserT String ParserState + +-- +-- main parser +-- + +parseMuse :: PandocMonad m => MuseParser m Pandoc +parseMuse = do + many directive + blocks <- parseBlocks + st <- getState + let doc = runF (do Pandoc _ bs <- B.doc <$> blocks + meta <- stateMeta' st + return $ Pandoc meta bs) st + reportLogMessages + return doc + +parseBlocks :: PandocMonad m => MuseParser m (F Blocks) +parseBlocks = do + res <- mconcat <$> many block + spaces + eof + return res + +-- +-- utility functions +-- + +nested :: PandocMonad m => MuseParser m a -> MuseParser m a +nested p = do + nestlevel <- stateMaxNestingLevel <$> getState + guard $ nestlevel > 0 + updateState $ \st -> st{ stateMaxNestingLevel = stateMaxNestingLevel st - 1 } + res <- p + updateState $ \st -> st{ stateMaxNestingLevel = nestlevel } + return res + +htmlElement :: PandocMonad m => String -> MuseParser m (Attr, String) +htmlElement tag = try $ do + (TagOpen _ attr, _) <- htmlTag (~== TagOpen tag []) + content <- manyTill anyChar (endtag <|> endofinput) + return (htmlAttrToPandoc attr, trim content) + where + endtag = void $ htmlTag (~== TagClose tag) + endofinput = lookAhead $ try $ skipMany blankline >> skipSpaces >> eof + trim = dropWhile (=='\n') . reverse . dropWhile (=='\n') . reverse + +htmlAttrToPandoc :: [Attribute String] -> Attr +htmlAttrToPandoc attrs = (ident, classes, keyvals) + where + ident = fromMaybe "" $ lookup "id" attrs + classes = maybe [] words $ lookup "class" attrs + keyvals = [(k,v) | (k,v) <- attrs, k /= "id" && k /= "class"] + +parseHtmlContentWithAttrs :: PandocMonad m + => String -> MuseParser m a -> MuseParser m (Attr, [a]) +parseHtmlContentWithAttrs tag parser = do + (attr, content) <- htmlElement tag + parsedContent <- try $ parseContent content + return (attr, parsedContent) + where + parseContent = parseFromString $ nested $ manyTill parser endOfContent + endOfContent = try $ skipMany blankline >> skipSpaces >> eof + +parseHtmlContent :: PandocMonad m => String -> MuseParser m a -> MuseParser m [a] +parseHtmlContent tag p = liftM snd (parseHtmlContentWithAttrs tag p) + +-- +-- directive parsers +-- + +parseDirective :: PandocMonad m => MuseParser m (String, F Inlines) +parseDirective = do + char '#' + key <- many letter + space + spaces + raw <- many $ noneOf "\n" + newline + value <- parseFromString (trimInlinesF . mconcat <$> many inline) raw + return (key, value) + +directive :: PandocMonad m => MuseParser m () +directive = do + (key, value) <- parseDirective + updateState $ \st -> st { stateMeta' = B.setMeta key <$> value <*> stateMeta' st } + +-- +-- block parsers +-- + +block :: PandocMonad m => MuseParser m (F Blocks) +block = do + pos <- getPosition + res <- mempty <$ skipMany1 blankline + <|> blockElements + <|> para + skipMany blankline + report $ ParsingTrace (take 60 $ show $ B.toList $ runF res defaultParserState) pos + return res + +blockElements :: PandocMonad m => MuseParser m (F Blocks) +blockElements = choice [ comment + , separator + , header + , exampleTag + , literal + , centerTag + , rightTag + , quoteTag + , bulletList + , orderedList + , table + , commentTag + , noteBlock + ] + +comment :: PandocMonad m => MuseParser m (F Blocks) +comment = try $ do + char ';' + space + many $ noneOf "\n" + void newline <|> eof + return mempty + +separator :: PandocMonad m => MuseParser m (F Blocks) +separator = try $ do + string "---" + newline + return $ return B.horizontalRule + +header :: PandocMonad m => MuseParser m (F Blocks) +header = try $ do + level <- liftM length $ many1 $ char '*' + guard $ level <= 5 + skipSpaces + content <- trimInlinesF . mconcat <$> manyTill inline newline + attr <- registerHeader ("", [], []) (runF content defaultParserState) + return $ B.headerWith attr level <$> content + +exampleTag :: PandocMonad m => MuseParser m (F Blocks) +exampleTag = liftM (return . uncurry B.codeBlockWith) $ htmlElement "example" + +literal :: PandocMonad m => MuseParser m (F Blocks) +literal = liftM (return . rawBlock) $ htmlElement "literal" + where + format (_, _, kvs) = fromMaybe "html" $ lookup "format" kvs + rawBlock (attrs, content) = B.rawBlock (format attrs) content + +blockTag :: PandocMonad m + => (Blocks -> Blocks) + -> String + -> MuseParser m (F Blocks) +blockTag f s = do + res <- parseHtmlContent s block + return $ f <$> mconcat res + +-- <center> tag is ignored +centerTag :: PandocMonad m => MuseParser m (F Blocks) +centerTag = blockTag id "center" + +-- <right> tag is ignored +rightTag :: PandocMonad m => MuseParser m (F Blocks) +rightTag = blockTag id "right" + +quoteTag :: PandocMonad m => MuseParser m (F Blocks) +quoteTag = blockTag B.blockQuote "quote" + +commentTag :: PandocMonad m => MuseParser m (F Blocks) +commentTag = parseHtmlContent "comment" block >> return mempty + +para :: PandocMonad m => MuseParser m (F Blocks) +para = do + res <- trimInlinesF . mconcat <$> many1Till inline endOfParaElement + return $ B.para <$> res + where + endOfParaElement = lookAhead $ endOfInput <|> endOfPara <|> newBlockElement + endOfInput = try $ skipMany blankline >> skipSpaces >> eof + endOfPara = try $ blankline >> skipMany1 blankline + newBlockElement = try $ blankline >> void blockElements + +noteMarker :: PandocMonad m => MuseParser m String +noteMarker = try $ do + char '[' + many1Till digit $ char ']' + +noteBlock :: PandocMonad m => MuseParser m (F Blocks) +noteBlock = try $ do + pos <- getPosition + ref <- noteMarker <* skipSpaces + content <- mconcat <$> blocksTillNote + oldnotes <- stateNotes' <$> getState + case M.lookup ref oldnotes of + Just _ -> logMessage $ DuplicateNoteReference ref pos + Nothing -> return () + updateState $ \s -> s{ stateNotes' = M.insert ref (pos, content) oldnotes } + return mempty + where + blocksTillNote = + many1Till block (eof <|> () <$ lookAhead noteMarker) + +-- +-- lists +-- + +listLine :: PandocMonad m => Int -> MuseParser m String +listLine markerLength = try $ do + notFollowedBy blankline + indentWith markerLength + anyLineNewline + +withListContext :: PandocMonad m => MuseParser m a -> MuseParser m a +withListContext p = do + state <- getState + let oldContext = stateParserContext state + setState $ state { stateParserContext = ListItemState } + parsed <- p + updateState (\st -> st {stateParserContext = oldContext}) + return parsed + +listContinuation :: PandocMonad m => Int -> MuseParser m String +listContinuation markerLength = try $ do + result <- many1 $ listLine markerLength + blanks <- many1 blankline + return $ concat result ++ blanks + +listStart :: PandocMonad m => MuseParser m Int -> MuseParser m Int +listStart marker = try $ do + preWhitespace <- length <$> many spaceChar + st <- stateParserContext <$> getState + getPosition >>= \pos -> guard (st == ListItemState || sourceColumn pos /= 1) + markerLength <- marker + postWhitespace <- length <$> many1 spaceChar + return $ preWhitespace + markerLength + postWhitespace + +listItem :: PandocMonad m => MuseParser m Int -> MuseParser m (F Blocks) +listItem start = try $ do + markerLength <- start + firstLine <- anyLineNewline + blank <- option "" ("\n" <$ blankline) + restLines <- many $ listLine markerLength + let first = firstLine ++ blank ++ concat restLines + rest <- many $ listContinuation markerLength + parseFromString (withListContext parseBlocks) $ concat (first:rest) ++ "\n" + +bulletListItems :: PandocMonad m => MuseParser m (F [Blocks]) +bulletListItems = sequence <$> many1 (listItem bulletListStart) + +bulletListStart :: PandocMonad m => MuseParser m Int +bulletListStart = listStart (char '-' >> return 1) + +bulletList :: PandocMonad m => MuseParser m (F Blocks) +bulletList = do + listItems <- bulletListItems + return $ B.bulletList <$> listItems + +orderedListStart :: PandocMonad m + => ListNumberStyle + -> ListNumberDelim + -> MuseParser m Int +orderedListStart style delim = listStart (snd <$> withHorizDisplacement (orderedListMarker style delim)) + +orderedList :: PandocMonad m => MuseParser m (F Blocks) +orderedList = try $ do + p@(_, style, delim) <- lookAhead (many spaceChar *> anyOrderedListMarker <* spaceChar) + guard $ style `elem` [Decimal, LowerAlpha, UpperAlpha, LowerRoman, UpperRoman] + guard $ delim == Period + items <- sequence <$> many1 (listItem $ orderedListStart style delim) + return $ B.orderedListWith p <$> items + +-- +-- tables +-- + +data MuseTable = MuseTable + { museTableCaption :: Inlines + , museTableHeaders :: [[Blocks]] + , museTableRows :: [[Blocks]] + , museTableFooters :: [[Blocks]] + } + +data MuseTableElement = MuseHeaderRow (F [Blocks]) + | MuseBodyRow (F [Blocks]) + | MuseFooterRow (F [Blocks]) + | MuseCaption (F Inlines) + +museToPandocTable :: MuseTable -> Blocks +museToPandocTable (MuseTable caption headers body footers) = + B.table caption attrs headRow rows + where ncol = maximum (0 : map length (headers ++ body ++ footers)) + attrs = replicate ncol (AlignDefault, 0.0) + headRow = if null headers then [] else head headers + rows = (if null headers then [] else tail headers) ++ body ++ footers + +museAppendElement :: MuseTable + -> MuseTableElement + -> F MuseTable +museAppendElement tbl element = + case element of + MuseHeaderRow row -> do + row' <- row + return tbl{ museTableHeaders = museTableHeaders tbl ++ [row'] } + MuseBodyRow row -> do + row' <- row + return tbl{ museTableRows = museTableRows tbl ++ [row'] } + MuseFooterRow row-> do + row' <- row + return tbl{ museTableFooters = museTableFooters tbl ++ [row'] } + MuseCaption inlines -> do + inlines' <- inlines + return tbl{ museTableCaption = inlines' } + +tableCell :: PandocMonad m => MuseParser m (F Blocks) +tableCell = try $ do + content <- trimInlinesF . mconcat <$> manyTill inline (lookAhead cellEnd) + return $ B.plain <$> content + where cellEnd = try $ void (many1 spaceChar >> char '|') <|> void newline <|> eof + +tableElements :: PandocMonad m => MuseParser m [MuseTableElement] +tableElements = tableParseElement `sepEndBy1` (void newline <|> eof) + +elementsToTable :: [MuseTableElement] -> F MuseTable +elementsToTable = foldM museAppendElement emptyTable + where emptyTable = MuseTable mempty mempty mempty mempty + +table :: PandocMonad m => MuseParser m (F Blocks) +table = try $ do + rows <- tableElements + let tbl = elementsToTable rows + let pandocTbl = museToPandocTable <$> tbl :: F Blocks + return pandocTbl + +tableParseElement :: PandocMonad m => MuseParser m MuseTableElement +tableParseElement = tableParseHeader + <|> tableParseBody + <|> tableParseFooter + <|> tableParseCaption + +tableParseRow :: PandocMonad m => Int -> MuseParser m (F [Blocks]) +tableParseRow n = try $ do + fields <- tableCell `sepBy2` fieldSep + return $ sequence fields + where p `sepBy2` sep = (:) <$> p <*> many1 (sep >> p) + fieldSep = many1 spaceChar >> count n (char '|') >> (void (many1 spaceChar) <|> void (lookAhead newline)) + +tableParseHeader :: PandocMonad m => MuseParser m MuseTableElement +tableParseHeader = MuseHeaderRow <$> tableParseRow 2 + +tableParseBody :: PandocMonad m => MuseParser m MuseTableElement +tableParseBody = MuseBodyRow <$> tableParseRow 1 + +tableParseFooter :: PandocMonad m => MuseParser m MuseTableElement +tableParseFooter = MuseFooterRow <$> tableParseRow 3 + +tableParseCaption :: PandocMonad m => MuseParser m MuseTableElement +tableParseCaption = try $ do + many spaceChar + string "|+" + contents <- trimInlinesF . mconcat <$> many1Till inline (lookAhead $ string "+|") + string "+|" + return $ MuseCaption contents + +-- +-- inline parsers +-- + +inline :: PandocMonad m => MuseParser m (F Inlines) +inline = choice [ whitespace + , br + , footnote + , strong + , strongTag + , emph + , emphTag + , superscriptTag + , subscriptTag + , strikeoutTag + , link + , code + , codeTag + , str + , symbol + ] <?> "inline" + +footnote :: PandocMonad m => MuseParser m (F Inlines) +footnote = try $ do + ref <- noteMarker + return $ do + notes <- asksF stateNotes' + case M.lookup ref notes of + Nothing -> return $ B.str $ "[" ++ ref ++ "]" + Just (_pos, contents) -> do + st <- askF + let contents' = runF contents st { stateNotes' = M.empty } + return $ B.note contents' + +whitespace :: PandocMonad m => MuseParser m (F Inlines) +whitespace = liftM return (lb <|> regsp) + where lb = try $ skipMany spaceChar >> linebreak >> return B.space + regsp = try $ skipMany1 spaceChar >> return B.space + +br :: PandocMonad m => MuseParser m (F Inlines) +br = try $ do + string "<br>" + return $ return B.linebreak + +linebreak :: PandocMonad m => MuseParser m (F Inlines) +linebreak = newline >> notFollowedBy newline >> (lastNewline <|> innerNewline) + where lastNewline = do + eof + return $ return mempty + innerNewline = return $ return B.space + +emphasisBetween :: (PandocMonad m, Show a) => MuseParser m a -> MuseParser m (F Inlines) +emphasisBetween c = try $ enclosedInlines c c + +enclosedInlines :: (PandocMonad m, Show a, Show b) + => MuseParser m a + -> MuseParser m b + -> MuseParser m (F Inlines) +enclosedInlines start end = try $ + trimInlinesF . mconcat <$> enclosed start end inline + +verbatimBetween :: PandocMonad m + => Char + -> MuseParser m String +verbatimBetween c = try $ do + char c + many1Till anyChar $ char c + +inlineTag :: PandocMonad m + => (Inlines -> Inlines) + -> String + -> MuseParser m (F Inlines) +inlineTag f s = do + res <- parseHtmlContent s inline + return $ f <$> mconcat res + +strongTag :: PandocMonad m => MuseParser m (F Inlines) +strongTag = inlineTag B.strong "strong" + +strong :: PandocMonad m => MuseParser m (F Inlines) +strong = fmap B.strong <$> emphasisBetween (string "**") + +emph :: PandocMonad m => MuseParser m (F Inlines) +emph = fmap B.emph <$> emphasisBetween (char '*') + +emphTag :: PandocMonad m => MuseParser m (F Inlines) +emphTag = inlineTag B.emph "em" + +superscriptTag :: PandocMonad m => MuseParser m (F Inlines) +superscriptTag = inlineTag B.superscript "sup" + +subscriptTag :: PandocMonad m => MuseParser m (F Inlines) +subscriptTag = inlineTag B.subscript "sub" + +strikeoutTag :: PandocMonad m => MuseParser m (F Inlines) +strikeoutTag = inlineTag B.strikeout "del" + +code :: PandocMonad m => MuseParser m (F Inlines) +code = return . B.code <$> verbatimBetween '=' + +codeTag :: PandocMonad m => MuseParser m (F Inlines) +codeTag = do + (attrs, content) <- parseHtmlContentWithAttrs "code" anyChar + return $ return $ B.codeWith attrs $ fromEntities content + +str :: PandocMonad m => MuseParser m (F Inlines) +str = liftM (return . B.str) (many1 alphaNum <|> count 1 characterReference) + +symbol :: PandocMonad m => MuseParser m (F Inlines) +symbol = liftM (return . B.str) $ count 1 nonspaceChar + +link :: PandocMonad m => MuseParser m (F Inlines) +link = try $ do + st <- getState + guard $ stateAllowLinks st + setState $ st{ stateAllowLinks = False } + (url, title, content) <- linkText + setState $ st{ stateAllowLinks = True } + return $ case stripPrefix "URL:" url of + Nothing -> if isImageUrl url + then B.image url title <$> fromMaybe (return mempty) content + else B.link url title <$> fromMaybe (return $ B.str url) content + Just url' -> B.link url' title <$> fromMaybe (return $ B.str url') content + where -- Taken from muse-image-regexp defined in Emacs Muse file lisp/muse-regexps.el + imageExtensions = [".eps", ".gif", ".jpg", ".jpeg", ".pbm", ".png", ".tiff", ".xbm", ".xpm"] + isImageUrl = (`elem` imageExtensions) . takeExtension + +linkContent :: PandocMonad m => MuseParser m (F Inlines) +linkContent = do + char '[' + res <- many1Till anyChar $ char ']' + parseFromString (mconcat <$> many1 inline) res + +linkText :: PandocMonad m => MuseParser m (String, String, Maybe (F Inlines)) +linkText = do + string "[[" + url <- many1Till anyChar $ char ']' + content <- optionMaybe linkContent + char ']' + return (url, "", content) -- cgit v1.2.3 From b3041de2fc05b26421c5be4df374ec84aafa11ee Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 19 Jun 2017 11:10:29 +0200 Subject: Text.Pandoc.Writers.Math: export defaultMathJaxURL, defaultKaTeXURL. This will ensure that we only need to update these in one place. (Currently, for example, the mathjax URL is used in both App and trypandoc.) Closes #3685. --- src/Text/Pandoc/App.hs | 10 ++++++---- src/Text/Pandoc/Writers/Math.hs | 7 +++++++ 2 files changed, 13 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 19066e8b7..033614752 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -79,6 +79,7 @@ import Text.Pandoc.Class (PandocIO, extractMedia, fillMediaBag, getLog, setResourcePath, withMediaBag) import Text.Pandoc.Highlighting (highlightingStyles) import Text.Pandoc.Lua (runLuaFilter) +import Text.Pandoc.Writers.Math (defaultMathJaxURL, defaultKaTeXURL) import Text.Pandoc.PDF (makePDF) import Text.Pandoc.Process (pipeProcess) import Text.Pandoc.SelfContained (makeDataURI, makeSelfContained) @@ -133,11 +134,11 @@ convertWithOpts opts = do Nothing -> return Nothing Just fp -> Just <$> UTF8.readFile fp - let csscdn = "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.css" let mathMethod = case (optKaTeXJS opts, optKaTeXStylesheet opts) of (Nothing, _) -> optHTMLMathMethod opts - (Just js, ss) -> KaTeX js (fromMaybe csscdn ss) + (Just js, ss) -> KaTeX js (fromMaybe + (defaultKaTeXURL ++ "katex.min.css") ss) -- --bibliography implies -F pandoc-citeproc for backwards compatibility: @@ -1355,7 +1356,8 @@ options = , Option "" ["mathjax"] (OptArg (\arg opt -> do - let url' = fromMaybe "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-AMS_CHTML-full" arg + let url' = fromMaybe (defaultMathJaxURL ++ + "MathJax.js?config=TeX-AMS_CHTML-full") arg return opt { optHTMLMathMethod = MathJax url'}) "URL") "" -- "Use MathJax for HTML math" @@ -1364,7 +1366,7 @@ options = (\arg opt -> return opt { optKaTeXJS = - arg <|> Just "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.js"}) + arg <|> Just (defaultKaTeXURL ++ "katex.min.js")}) "URL") "" -- Use KaTeX for HTML Math diff --git a/src/Text/Pandoc/Writers/Math.hs b/src/Text/Pandoc/Writers/Math.hs index 104d3c20b..58252d60f 100644 --- a/src/Text/Pandoc/Writers/Math.hs +++ b/src/Text/Pandoc/Writers/Math.hs @@ -1,6 +1,8 @@ module Text.Pandoc.Writers.Math ( texMathToInlines , convertMath + , defaultMathJaxURL + , defaultKaTeXURL ) where @@ -47,3 +49,8 @@ convertMath writer mt str = do DisplayMath -> DisplayBlock InlineMath -> DisplayInline +defaultMathJaxURL :: String +defaultMathJaxURL = "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/" + +defaultKaTeXURL :: String +defaultKaTeXURL = "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/" -- cgit v1.2.3 From 564c77964ddbbdc5541086726b9109091119e140 Mon Sep 17 00:00:00 2001 From: Yuchen Pei <ycpei@users.noreply.github.com> Date: Mon, 19 Jun 2017 16:15:12 -0400 Subject: Added Vimwiki reader (#3705). * New module Text.Pandoc.Readers.Vimwiki, exporting readVimwiki [API change]. * New input format `vimwiki`. * New data file, `data/vimwiki.css`, for displaying the HTML produced by this reader and pandoc's HTML writer in the style of vimwiki's own HTML export. --- src/Text/Pandoc/Readers.hs | 3 + src/Text/Pandoc/Readers/Vimwiki.hs | 655 +++++++++++++++++++++++++++++++++++++ 2 files changed, 658 insertions(+) create mode 100644 src/Text/Pandoc/Readers/Vimwiki.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers.hs b/src/Text/Pandoc/Readers.hs index 4c95d5d28..20e503a7e 100644 --- a/src/Text/Pandoc/Readers.hs +++ b/src/Text/Pandoc/Readers.hs @@ -46,6 +46,7 @@ module Text.Pandoc.Readers , readMarkdown , readCommonMark , readMediaWiki + , readVimwiki , readRST , readOrg , readLaTeX @@ -82,6 +83,7 @@ import Text.Pandoc.Readers.HTML import Text.Pandoc.Readers.LaTeX import Text.Pandoc.Readers.Markdown import Text.Pandoc.Readers.MediaWiki +import Text.Pandoc.Readers.Vimwiki import Text.Pandoc.Readers.Muse import Text.Pandoc.Readers.Native import Text.Pandoc.Readers.Odt @@ -115,6 +117,7 @@ readers = [ ("native" , TextReader readNative) ,("commonmark" , TextReader readCommonMark) ,("rst" , TextReader readRST) ,("mediawiki" , TextReader readMediaWiki) + ,("vimwiki" , TextReader readVimwiki) ,("docbook" , TextReader readDocBook) ,("opml" , TextReader readOPML) ,("org" , TextReader readOrg) diff --git a/src/Text/Pandoc/Readers/Vimwiki.hs b/src/Text/Pandoc/Readers/Vimwiki.hs new file mode 100644 index 000000000..07e23fa1e --- /dev/null +++ b/src/Text/Pandoc/Readers/Vimwiki.hs @@ -0,0 +1,655 @@ +{- + Copyright (C) 2017 Yuchen Pei <me@ypei.me> + +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.Vimwiki + Copyright : Copyright (C) 2017 Yuchen Pei + License : GNU GPL, version 2 or above + + Maintainer : Yuchen Pei <me@ypei.me> + Stability : alpha + Portability : portable + +Conversion of vimwiki text to 'Pandoc' document. +-} +{-- + progress: +* block parsers: + * [X] header + * [X] hrule + * [X] comment + * [X] blockquote + * [X] preformatted + * [X] displaymath + * [X] bulletlist / orderedlist + * [X] orderedlist with 1., i., a) etc identification. + * [X] todo lists -- not list builder with attributes? using span. + * [X] table + * [X] centered table -- using div + * [O] colspan and rowspan -- pandoc limitation, see issue #1024 + * [X] paragraph + * [X] definition list +* inline parsers: + * [X] bareURL + * [X] strong + * [X] emph + * [X] strikeout + * [X] code + * [X] link + * [X] image + * [X] inline math + * [X] tag + * [X] sub- and super-scripts +* misc: + * [X] `TODO:` mark + * [X] metadata placeholders: %title and %date + * [O] control placeholders: %template and %nohtml -- %template added to + meta, %nohtml ignored +--} + +module Text.Pandoc.Readers.Vimwiki ( readVimwiki + ) where +import Control.Monad.Except (throwError) +import Control.Monad (guard) +import Data.Default +import Data.Maybe +import Data.Monoid ((<>)) +import Data.List (isInfixOf, isPrefixOf) +import Data.Text (Text, unpack) +import Text.Pandoc.Builder (Blocks, Inlines, trimInlines, fromList, toList) +import qualified Text.Pandoc.Builder + as B (headerWith, str, space, strong, emph, strikeout, code, link, image, + spanWith, para, horizontalRule, blockQuote, bulletList, plain, + orderedList, simpleTable, softbreak, codeBlockWith, imageWith, divWith, + setMeta, definitionList, superscript, subscript) +import Text.Pandoc.Class (PandocMonad, report) +import Text.Pandoc.Definition (Pandoc(..), Inline(Space), + Block(BulletList, OrderedList), Attr, nullMeta, Meta, ListNumberStyle(..), + ListNumberDelim(..)) +import Text.Pandoc.Logging (LogMessage(ParsingTrace)) +import Text.Pandoc.Options (ReaderOptions) +import Text.Pandoc.Parsing (readWithM, ParserT, stateOptions, ParserState, + stateMeta', blanklines, registerHeader, spaceChar, emailAddress, uri, F, runF, + orderedListMarker, many1Till) +import Text.Pandoc.Shared (splitBy, stripFirstAndLast, stringify) +import Text.Parsec.Char (spaces, char, anyChar, newline, string, noneOf, + alphaNum) +import Text.Parsec.Combinator (eof, choice, many1, manyTill, count, skipMany1, + notFollowedBy, option) +import Text.Parsec.Prim (many, getPosition, try, updateState, getState) +import Text.Parsec.Char (oneOf, space) +import Text.Parsec.Combinator (lookAhead, between) +import Text.Parsec.Prim ((<|>)) + +readVimwiki :: PandocMonad m => ReaderOptions -> Text -> m Pandoc +readVimwiki opts s = do + res <- readWithM parseVimwiki def{ stateOptions = opts } (unpack s) + case res of + Left e -> throwError e + Right result -> return result + +type VwParser = ParserT [Char] ParserState + + +-- constants + +specialChars :: [Char] +specialChars = "=*-#[]_~{}`$|:%^," + +spaceChars :: [Char] +spaceChars = " \t\n" + +-- main parser + +parseVimwiki :: PandocMonad m => VwParser m Pandoc +parseVimwiki = do + bs <- mconcat <$> many block + spaces + eof + st <- getState + let meta = runF (stateMeta' st) st + return $ Pandoc meta (toList bs) + +-- block parser + +block :: PandocMonad m => VwParser m Blocks +block = do + pos <- getPosition + res <- choice [ mempty <$ blanklines + , header + , hrule + , mempty <$ comment + , mixedList + , preformatted + , displayMath + , table + , mempty <$ placeholder + , blockQuote + , definitionList + , para + ] + report $ ParsingTrace (take 60 $ show $ toList res) pos + return res + +blockML :: PandocMonad m => VwParser m Blocks +blockML = choice [preformatted, displayMath, table] + +header :: PandocMonad m => VwParser m Blocks +header = try $ do + sp <- many spaceChar + eqs <- many1 (char '=') + spaceChar + let lev = length eqs + guard $ lev <= 6 + contents <- trimInlines . mconcat <$> manyTill inline (try $ spaceChar + >> (string eqs) >> many spaceChar >> newline) + attr <- registerHeader (makeId contents, + (if sp == "" then [] else ["justcenter"]), []) contents + return $ B.headerWith attr lev contents + +para :: PandocMonad m => VwParser m Blocks +para = try $ do + contents <- trimInlines . mconcat <$> many1 inline + if all (==Space) (toList contents) + then return mempty + else return $ B.para contents + +hrule :: PandocMonad m => VwParser m Blocks +hrule = try $ B.horizontalRule <$ (string "----" >> many (char '-') >> newline) + +comment :: PandocMonad m => VwParser m () +comment = try $ do + many spaceChar >> string "%%" >> many (noneOf "\n") + return () + +blockQuote :: PandocMonad m => VwParser m Blocks +blockQuote = try $ do + string " " + contents <- trimInlines . mconcat <$> many1 inlineBQ + if all (==Space) (toList contents) + then return mempty + else return $ B.blockQuote $ B.plain contents + +definitionList :: PandocMonad m => VwParser m Blocks +definitionList = try $ + B.definitionList <$> (many1 (dlItemWithDT <|> dlItemWithoutDT)) + +dlItemWithDT :: PandocMonad m => VwParser m (Inlines, [Blocks]) +dlItemWithDT = do + dt <- definitionTerm + dds <- many definitionDef + return $ (dt, dds) + +dlItemWithoutDT :: PandocMonad m => VwParser m (Inlines, [Blocks]) +dlItemWithoutDT = do + dds <- many1 definitionDef + return $ (mempty, dds) + +definitionDef :: PandocMonad m => VwParser m Blocks +definitionDef = try $ + (notFollowedBy definitionTerm) >> many spaceChar + >> (definitionDef1 <|> definitionDef2) + +definitionDef1 :: PandocMonad m => VwParser m Blocks +definitionDef1 = try $ mempty <$ defMarkerE + +definitionDef2 :: PandocMonad m => VwParser m Blocks +definitionDef2 = try $ B.plain <$> + (defMarkerM >> (trimInlines . mconcat <$> many inline') <* newline) + + +definitionTerm :: PandocMonad m => VwParser m Inlines +definitionTerm = try $ do + x <- definitionTerm1 <|> definitionTerm2 + guard $ (stringify x /= "") + return x + +definitionTerm1 :: PandocMonad m => VwParser m Inlines +definitionTerm1 = try $ + trimInlines . mconcat <$> manyTill inline' (try $ defMarkerE) + +definitionTerm2 :: PandocMonad m => VwParser m Inlines +definitionTerm2 = try $ trimInlines . mconcat <$> manyTill inline' + (try $ lookAhead $ (defMarkerM >> notFollowedBy hasDefMarkerM)) + +defMarkerM :: PandocMonad m => VwParser m Char +defMarkerM = string "::" >> spaceChar + +defMarkerE :: PandocMonad m => VwParser m Char +defMarkerE = string "::" >> newline + +hasDefMarkerM :: PandocMonad m => VwParser m String +hasDefMarkerM = manyTill (noneOf "\n") (try defMarkerM) + +preformatted :: PandocMonad m => VwParser m Blocks +preformatted = try $ do + many spaceChar >> string "{{{" + attrText <- many (noneOf "\n") + lookAhead newline + contents <- manyTill anyChar (try (char '\n' >> many spaceChar >> string "}}}" + >> many spaceChar >> newline)) + if (not $ contents == "") && (head contents == '\n') + then return $ B.codeBlockWith (makeAttr attrText) (tail contents) + else return $ B.codeBlockWith (makeAttr attrText) contents + +makeAttr :: String -> Attr +makeAttr s = + let xs = splitBy (`elem` " \t") s in + ("", [], catMaybes $ map nameValue xs) + +nameValue :: String -> Maybe (String, String) +nameValue s = + let t = splitBy (== '=') s in + if length t /= 2 + then Nothing + else let (a, b) = (head t, last t) in + if ((length b) < 2) || ((head b, last b) /= ('"', '"')) + then Nothing + else Just (a, stripFirstAndLast b) + + +displayMath :: PandocMonad m => VwParser m Blocks +displayMath = try $ do + many spaceChar >> string "{{$" + mathTag <- option "" mathTagParser + contents <- manyTill anyChar (try (char '\n' >> many spaceChar >> string "}}$" + >> many spaceChar >> newline)) + let contentsWithTags + | mathTag == "" = "\\[" ++ contents ++ "\n\\]" + | otherwise = "\\begin{" ++ mathTag ++ "}" ++ contents + ++ "\n\\end{" ++ mathTag ++ "}" + return $ B.plain $ B.str contentsWithTags + +mixedList :: PandocMonad m => VwParser m Blocks +mixedList = try $ do + (bl, _) <- mixedList' (-1) + return $ head bl + +mixedList' :: PandocMonad m => Int -> VwParser m ([Blocks], Int) +mixedList' prevInd = do + (curInd, builder) <- option (-1, "na") (lookAhead listStart) + if curInd < prevInd + then return ([], curInd) + else do + listStart + curLine <- listItemContent + let listBuilder = + if builder == "ul" then B.bulletList else B.orderedList + (subList, lowInd) <- (mixedList' curInd) + if lowInd >= curInd + then do + (sameIndList, endInd) <- (mixedList' lowInd) + let curList = (combineList curLine subList) ++ sameIndList + if curInd > prevInd + then return ([listBuilder curList], endInd) + else return (curList, endInd) + else do + let (curList, endInd) = ((combineList curLine subList), + lowInd) + if curInd > prevInd + then return ([listBuilder curList], endInd) + else return (curList, endInd) + +plainInlineML' :: PandocMonad m => Inlines -> VwParser m Blocks +plainInlineML' w = do + xs <- many inlineML + newline + return $ B.plain $ trimInlines $ mconcat $ w:xs + +plainInlineML :: PandocMonad m => VwParser m Blocks +plainInlineML = (notFollowedBy listStart) >> spaceChar >> plainInlineML' mempty + + +listItemContent :: PandocMonad m => VwParser m Blocks +listItemContent = try $ do + w <- option mempty listTodoMarker + x <- plainInlineML' w + y <- many blocksThenInline + z <- many blockML + return $ mconcat $ x:y ++ z + +blocksThenInline :: PandocMonad m => VwParser m Blocks +blocksThenInline = try $ do + y <- many1 blockML + x <- plainInlineML + return $ mconcat $ y ++ [x] + +listTodoMarker :: PandocMonad m => VwParser m Inlines +listTodoMarker = try $ do + x <- between (many spaceChar >> char '[') (char ']' >> spaceChar) + (oneOf " .oOX") + return $ makeListMarkerSpan x + +makeListMarkerSpan :: Char -> Inlines +makeListMarkerSpan x = + let cl = case x of + ' ' -> "done0" + '.' -> "done1" + 'o' -> "done2" + 'O' -> "done3" + 'X' -> "done4" + _ -> "" + in + B.spanWith ("", [cl], []) mempty + +combineList :: Blocks -> [Blocks] -> [Blocks] +combineList x [y] = case toList y of + [BulletList z] -> [fromList $ (toList x) + ++ [BulletList z]] + [OrderedList attr z] -> [fromList $ (toList x) + ++ [OrderedList attr z]] + _ -> x:[y] +combineList x xs = x:xs + +listStart :: PandocMonad m => VwParser m (Int, String) +listStart = try $ do + s <- many spaceChar + listType <- bulletListMarkers <|> orderedListMarkers + spaceChar + return (length s, listType) + +bulletListMarkers :: PandocMonad m => VwParser m String +bulletListMarkers = "ul" <$ (char '*' <|> char '-') + +orderedListMarkers :: PandocMonad m => VwParser m String +orderedListMarkers = + ("ol" <$ (choice $ (orderedListMarker Decimal Period):(($OneParen) + <$> orderedListMarker + <$> [Decimal, LowerRoman, UpperRoman, LowerAlpha, UpperAlpha]))) + <|> ("ol" <$ char '#') + +--many need trimInlines +table :: PandocMonad m => VwParser m Blocks +table = try $ do + indent <- lookAhead (many spaceChar) + (th, trs) <- table1 <|> table2 + let tab = B.simpleTable th trs + if indent == "" + then return tab + else return $ B.divWith ("", ["center"], []) tab + +-- table with header +table1 :: PandocMonad m => VwParser m ([Blocks], [[Blocks]]) +table1 = try $ do + th <- tableRow + many1 tableHeaderSeparator + trs <- many tableRow + return (th, trs) + +-- headerless table +table2 :: PandocMonad m => VwParser m ([Blocks], [[Blocks]]) +table2 = try $ do + trs <- many1 tableRow + return (take (length $ head trs) $ repeat mempty, trs) + +tableHeaderSeparator :: PandocMonad m => VwParser m () +tableHeaderSeparator = try $ do + many spaceChar >> char '|' >> many1 ((many1 $ char '-') >> char '|') + >> many spaceChar >> newline + return () + +tableRow :: PandocMonad m => VwParser m [Blocks] +tableRow = try $ do + many spaceChar >> char '|' + s <- lookAhead $ manyTill anyChar (try (char '|' >> many spaceChar + >> newline)) + guard $ not $ "||" `isInfixOf` ("|" ++ s ++ "|") + tr <- many tableCell + many spaceChar >> char '\n' + return tr + +tableCell :: PandocMonad m => VwParser m Blocks +tableCell = try $ + B.plain <$> trimInlines . mconcat <$> (manyTill inline' (char '|')) + +placeholder :: PandocMonad m => VwParser m () +placeholder = try $ + (choice (ph <$> ["title", "date"])) <|> noHtmlPh <|> templatePh + +ph :: PandocMonad m => String -> VwParser m () +ph s = try $ do + many spaceChar >> (string $ '%':s) >> spaceChar + contents <- (trimInlines . mconcat <$> (manyTill inline (lookAhead newline))) + --use lookAhead because of placeholder in the whitespace parser + let meta' = return $ B.setMeta s contents nullMeta :: F Meta + updateState $ \st -> st { stateMeta' = stateMeta' st <> meta' } + +noHtmlPh :: PandocMonad m => VwParser m () +noHtmlPh = try $ + () <$ (many spaceChar >> string "%nohtml" >> many spaceChar + >> (lookAhead newline)) + +templatePh :: PandocMonad m => VwParser m () +templatePh = try $ + () <$ (many spaceChar >> string "%template" >> (many $ noneOf "\n") + >> (lookAhead newline)) + +-- inline parser + +inline :: PandocMonad m => VwParser m Inlines +inline = choice $ (whitespace endlineP):inlineList + +inlineList :: PandocMonad m => [VwParser m Inlines] +inlineList = [ bareURL + , todoMark + , str + , strong + , emph + , strikeout + , code + , link + , image + , inlineMath + , tag + , superscript + , subscript + , special + ] + +-- inline parser without softbreaks or comment breaks +inline' :: PandocMonad m => VwParser m Inlines +inline' = choice $ whitespace':inlineList + +-- inline parser for blockquotes +inlineBQ :: PandocMonad m => VwParser m Inlines +inlineBQ = choice $ (whitespace endlineBQ):inlineList + +-- inline parser for mixedlists +inlineML :: PandocMonad m => VwParser m Inlines +inlineML = choice $ (whitespace endlineML):inlineList + +str :: PandocMonad m => VwParser m Inlines +str = B.str <$> (many1 $ noneOf $ spaceChars ++ specialChars) + +whitespace :: PandocMonad m => VwParser m () -> VwParser m Inlines +whitespace endline = B.space <$ (skipMany1 spaceChar <|> + (try (newline >> (comment <|> placeholder)))) + <|> B.softbreak <$ endline + +whitespace' :: PandocMonad m => VwParser m Inlines +whitespace' = B.space <$ skipMany1 spaceChar + +special :: PandocMonad m => VwParser m Inlines +special = B.str <$> count 1 (oneOf specialChars) + +bareURL :: PandocMonad m => VwParser m Inlines +bareURL = try $ do + (orig, src) <- uri <|> emailAddress + return $ B.link src "" (B.str orig) + +strong :: PandocMonad m => VwParser m Inlines +strong = try $ do + s <- lookAhead $ between (char '*') (char '*') (many1 $ noneOf "*") + guard $ (not $ (head s) `elem` spaceChars) + && (not $ (last s) `elem` spaceChars) + char '*' + contents <- mconcat <$> (manyTill inline' $ char '*' + >> notFollowedBy alphaNum) + return $ (B.spanWith ((makeId contents), [], []) mempty) + <> (B.strong contents) + +makeId :: Inlines -> String +makeId i = concat (stringify <$> (toList i)) + +emph :: PandocMonad m => VwParser m Inlines +emph = try $ do + s <- lookAhead $ between (char '_') (char '_') (many1 $ noneOf "_") + guard $ (not $ (head s) `elem` spaceChars) + && (not $ (last s) `elem` spaceChars) + char '_' + contents <- mconcat <$> (manyTill inline' $ char '_' + >> notFollowedBy alphaNum) + return $ B.emph contents + +strikeout :: PandocMonad m => VwParser m Inlines +strikeout = try $ do + string "~~" + contents <- mconcat <$> (many1Till inline' $ string $ "~~") + return $ B.strikeout contents + +code :: PandocMonad m => VwParser m Inlines +code = try $ do + char '`' + contents <- many1Till (noneOf "\n") (char '`') + return $ B.code contents + +superscript :: PandocMonad m => VwParser m Inlines +superscript = try $ + B.superscript <$> mconcat <$> (char '^' >> many1Till inline' (char '^')) + +subscript :: PandocMonad m => VwParser m Inlines +subscript = try $ + B.subscript <$> mconcat <$> (string ",," + >> many1Till inline' (try $ string ",,")) + +link :: PandocMonad m => VwParser m Inlines +link = try $ do + string "[[" + contents <- lookAhead $ manyTill anyChar (string "]]") + case '|' `elem` contents of + False -> do + manyTill anyChar (string "]]") +-- not using try here because [[hell]o]] is not rendered as a link in vimwiki + return $ B.link (procLink contents) "" (B.str contents) + True -> do + url <- manyTill anyChar $ char '|' + lab <- mconcat <$> (manyTill inline $ string "]]") + return $ B.link (procLink url) "" lab + +image :: PandocMonad m => VwParser m Inlines +image = try $ do + string "{{" + contentText <- lookAhead $ manyTill (noneOf "\n") (try $ string "}}") + images $ length $ filter (== '|') contentText + +images :: PandocMonad m => Int -> VwParser m Inlines +images k + | k == 0 = do + imgurl <- manyTill anyChar (try $ string "}}") + return $ B.image (procImgurl imgurl) "" (B.str "") + | k == 1 = do + imgurl <- manyTill anyChar (char '|') + alt <- mconcat <$> (manyTill inline $ (try $ string "}}")) + return $ B.image (procImgurl imgurl) "" alt + | k == 2 = do + imgurl <- manyTill anyChar (char '|') + alt <- mconcat <$> (manyTill inline $ char '|') + attrText <- manyTill anyChar (try $ string "}}") + return $ B.imageWith (makeAttr attrText) (procImgurl imgurl) "" alt + | otherwise = do + imgurl <- manyTill anyChar (char '|') + alt <- mconcat <$> (manyTill inline $ char '|') + attrText <- manyTill anyChar (char '|') + manyTill anyChar (try $ string "}}") + return $ B.imageWith (makeAttr attrText) (procImgurl imgurl) "" alt + +procLink' :: String -> String +procLink' s + | ((take 6 s) == "local:") = "file" ++ (drop 5 s) + | ((take 6 s) == "diary:") = "diary/" ++ (drop 6 s) ++ ".html" + | or ((`isPrefixOf` s) <$> [ "http:", "https:", "ftp:", "file:", "mailto:", + "news:", "telnet:" ]) + = s + | s == "" = "" + | (last s) == '/' = s + | otherwise = s ++ ".html" + +procLink :: String -> String +procLink s = procLink' x ++ y + where (x, y) = break (=='#') s + +procImgurl :: String -> String +procImgurl s = if ((take 6 s) == "local:") then "file" ++ (drop 5 s) else s + +inlineMath :: PandocMonad m => VwParser m Inlines +inlineMath = try $ do + char '$' + contents <- many1Till (noneOf "\n") (char '$') + return $ B.str $ "\\(" ++ contents ++ "\\)" + +tag :: PandocMonad m => VwParser m Inlines +tag = try $ do + char ':' + s <- manyTill (noneOf spaceChars) (try (char ':' >> (lookAhead space))) + guard $ not $ "::" `isInfixOf` (":" ++ s ++ ":") + let ss = splitBy (==':') s + return $ mconcat $ (makeTagSpan' $ head ss):(makeTagSpan <$> (tail ss)) + +todoMark :: PandocMonad m => VwParser m Inlines +todoMark = try $ do + string "TODO:" + return $ B.spanWith ("", ["todo"], []) (B.str "TODO:") + +-- helper functions and parsers +endlineP :: PandocMonad m => VwParser m () +endlineP = () <$ try (newline <* nFBTTBSB <* notFollowedBy blockQuote) + +endlineBQ :: PandocMonad m => VwParser m () +endlineBQ = () <$ try (newline <* nFBTTBSB <* string " ") + +endlineML :: PandocMonad m => VwParser m () +endlineML = () <$ try (newline <* nFBTTBSB <* many1 spaceChar) + +--- nFBTTBSB is short for notFollowedByThingsThatBreakSoftBreaks +nFBTTBSB :: PandocMonad m => VwParser m () +nFBTTBSB = + notFollowedBy newline <* + notFollowedBy hrule <* + notFollowedBy tableRow <* + notFollowedBy header <* + notFollowedBy listStart <* + notFollowedBy preformatted <* + notFollowedBy displayMath <* + notFollowedBy hasDefMarker + +hasDefMarker :: PandocMonad m => VwParser m () +hasDefMarker = () <$ (manyTill (noneOf "\n") (string "::" >> oneOf spaceChars)) + +makeTagSpan' :: String -> Inlines +makeTagSpan' s = B.spanWith ('-' : s, [], []) (B.str "") <> + B.spanWith (s, ["tag"], []) (B.str s) + +makeTagSpan :: String -> Inlines +makeTagSpan s = (B.space) <> (makeTagSpan' s) + +mathTagParser :: PandocMonad m => VwParser m String +mathTagParser = do + s <- try $ lookAhead (char '%' >> (manyTill (noneOf spaceChars) + (try $ char '%' >> many (noneOf $ '%':spaceChars) >> space))) + char '%' >> string s >> char '%' + return s -- cgit v1.2.3 From 4929d027dc57151dc7f009347478b35b90d2373b Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 19 Jun 2017 23:16:21 +0300 Subject: Muse reader: fix list item continuation parsing (#3747) --- src/Text/Pandoc/Readers/Muse.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index bc9da26cb..c1ea1354b 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -295,9 +295,9 @@ withListContext p = do listContinuation :: PandocMonad m => Int -> MuseParser m String listContinuation markerLength = try $ do - result <- many1 $ listLine markerLength blanks <- many1 blankline - return $ concat result ++ blanks + result <- many1 $ listLine markerLength + return $ blanks ++ concat result listStart :: PandocMonad m => MuseParser m Int -> MuseParser m Int listStart marker = try $ do -- cgit v1.2.3 From 814ac51d3228eeb3bbcbf78a8a88a43cd11d23dd Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 19 Jun 2017 22:04:01 +0200 Subject: Separated tracing from logging. Formerly tracing was just log messages with a DEBUG log level. We now make these things independent. Tracing can be turned on or off in PandocMonad using `setTrace`; it is independent of logging. * Removed `DEBUG` from `Verbosity`. * Removed `ParserTrace` from `LogMessage`. * Added `trace`, `setTrace` to `PandocMonad`. --- src/Text/Pandoc/App.hs | 7 +++++-- src/Text/Pandoc/Class.hs | 27 ++++++++++++++++++++++----- src/Text/Pandoc/Logging.hs | 12 +----------- src/Text/Pandoc/Readers/HTML.hs | 5 ++--- src/Text/Pandoc/Readers/Markdown.hs | 6 ++---- src/Text/Pandoc/Readers/MediaWiki.hs | 5 ++--- src/Text/Pandoc/Readers/Muse.hs | 5 ++--- src/Text/Pandoc/Readers/TWiki.hs | 6 ++---- src/Text/Pandoc/Readers/Textile.hs | 6 ++---- 9 files changed, 40 insertions(+), 39 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 033614752..2c5e1de6b 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -76,7 +76,7 @@ import System.IO.Error (isDoesNotExistError) import Text.Pandoc import Text.Pandoc.Builder (setMeta) import Text.Pandoc.Class (PandocIO, extractMedia, fillMediaBag, getLog, - setResourcePath, withMediaBag) + setResourcePath, withMediaBag, setTrace) import Text.Pandoc.Highlighting (highlightingStyles) import Text.Pandoc.Lua (runLuaFilter) import Text.Pandoc.Writers.Math (defaultMathJaxURL, defaultKaTeXURL) @@ -391,6 +391,7 @@ convertWithOpts opts = do let runIO' :: PandocIO a -> IO a runIO' f = do (res, reports) <- runIOorExplode $ do + setTrace (optTrace opts) setVerbosity verbosity x <- f rs <- getLog @@ -559,6 +560,7 @@ data Opt = Opt , optDumpArgs :: Bool -- ^ Output command-line arguments , optIgnoreArgs :: Bool -- ^ Ignore command-line arguments , optVerbosity :: Verbosity -- ^ Verbosity of diagnostic output + , optTrace :: Bool -- ^ Enable tracing , optLogFile :: Maybe FilePath -- ^ File to write JSON log output , optFailIfWarnings :: Bool -- ^ Fail on warnings , optReferenceLinks :: Bool -- ^ Use reference links in writing markdown, rst @@ -633,6 +635,7 @@ defaultOpts = Opt , optDumpArgs = False , optIgnoreArgs = False , optVerbosity = WARNING + , optTrace = False , optLogFile = Nothing , optFailIfWarnings = False , optReferenceLinks = False @@ -1390,7 +1393,7 @@ options = , Option "" ["trace"] (NoArg - (\opt -> return opt { optVerbosity = DEBUG })) + (\opt -> return opt { optTrace = True })) "" -- "Turn on diagnostic tracing in readers." , Option "" ["dump-args"] diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 8db2e214e..a7194f8d5 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -45,6 +45,7 @@ module Text.Pandoc.Class ( PandocMonad(..) , getZonedTime , readFileFromDirs , report + , setTrace , getLog , setVerbosity , getMediaBag @@ -78,7 +79,7 @@ import qualified Text.Pandoc.Shared as IO ( readDataFile import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Compat.Time (UTCTime) import Text.Pandoc.Logging -import Text.Parsec (ParsecT) +import Text.Parsec (ParsecT, getPosition) import qualified Text.Pandoc.Compat.Time as IO (getCurrentTime) import Text.Pandoc.MIME (MimeType, getMimeType, extensionFromMimeType) import Text.Pandoc.Definition @@ -117,6 +118,7 @@ import System.IO.Error import System.IO (stderr) import qualified Data.Map as M import Text.Pandoc.Error +import qualified Debug.Trace class (Functor m, Applicative m, Monad m, MonadError PandocError m) => PandocMonad m where @@ -140,6 +142,11 @@ class (Functor m, Applicative m, Monad m, MonadError PandocError m) modifyCommonState :: (CommonState -> CommonState) -> m () modifyCommonState f = getCommonState >>= putCommonState . f + trace :: String -> m () + trace msg = do + tracing <- getsCommonState stTrace + when tracing $ Debug.Trace.trace ("[trace] " ++ msg) (return ()) + logOutput :: LogMessage -> m () -- Functions defined for all PandocMonad instances @@ -155,10 +162,11 @@ report :: PandocMonad m => LogMessage -> m () report msg = do verbosity <- getsCommonState stVerbosity let level = messageVerbosity msg - when (level <= verbosity) $ - logOutput msg - unless (level == DEBUG) $ - modifyCommonState $ \st -> st{ stLog = msg : stLog st } + when (level <= verbosity) $ logOutput msg + modifyCommonState $ \st -> st{ stLog = msg : stLog st } + +setTrace :: PandocMonad m => Bool -> m () +setTrace useTracing = modifyCommonState $ \st -> st{stTrace = useTracing} setMediaBag :: PandocMonad m => MediaBag -> m () setMediaBag mb = modifyCommonState $ \st -> st{stMediaBag = mb} @@ -208,6 +216,7 @@ data CommonState = CommonState { stLog :: [LogMessage] , stOutputFile :: Maybe FilePath , stResourcePath :: [FilePath] , stVerbosity :: Verbosity + , stTrace :: Bool } instance Default CommonState where @@ -217,6 +226,7 @@ instance Default CommonState where , stOutputFile = Nothing , stResourcePath = ["."] , stVerbosity = WARNING + , stTrace = False } runIO :: PandocIO a -> IO (Either PandocError a) @@ -561,8 +571,15 @@ instance PandocMonad m => PandocMonad (ParsecT s st m) where getModificationTime = lift . getModificationTime getCommonState = lift getCommonState putCommonState = lift . putCommonState + trace msg = do + tracing <- getsCommonState stTrace + when tracing $ do + pos <- getPosition + Debug.Trace.trace + ("[trace] Parsed " ++ msg ++ " at " ++ show pos) (return ()) logOutput = lift . logOutput + instance PandocMonad m => PandocMonad (ReaderT r m) where lookupEnv = lift . lookupEnv getCurrentTime = lift getCurrentTime diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs index b31c33d4e..4090243ea 100644 --- a/src/Text/Pandoc/Logging.hs +++ b/src/Text/Pandoc/Logging.hs @@ -52,7 +52,7 @@ import Text.Pandoc.Definition import Text.Parsec.Pos -- | Verbosity level. -data Verbosity = ERROR | WARNING | INFO | DEBUG +data Verbosity = ERROR | WARNING | INFO deriving (Show, Read, Eq, Data, Enum, Ord, Bounded, Typeable, Generic) instance ToJSON Verbosity where @@ -63,7 +63,6 @@ instance FromJSON Verbosity where "ERROR" -> return ERROR "WARNING" -> return WARNING "INFO" -> return INFO - "DEBUG" -> return DEBUG _ -> mzero parseJSON _ = mzero @@ -78,7 +77,6 @@ data LogMessage = | CircularReference String SourcePos | ParsingUnescaped String SourcePos | CouldNotLoadIncludeFile String SourcePos - | ParsingTrace String SourcePos | InlineNotRendered Inline | BlockNotRendered Block | DocxParserWarning String @@ -151,11 +149,6 @@ instance ToJSON LogMessage where "source" .= Text.pack (sourceName pos), "line" .= toJSON (sourceLine pos), "column" .= toJSON (sourceColumn pos)] - ParsingTrace s pos -> - ["contents" .= Text.pack s, - "source" .= Text.pack (sourceName pos), - "line" .= sourceLine pos, - "column" .= sourceColumn pos] InlineNotRendered il -> ["contents" .= toJSON il] BlockNotRendered bl -> @@ -228,8 +221,6 @@ showLogMessage msg = "Parsing unescaped '" ++ s ++ "' at " ++ showPos pos CouldNotLoadIncludeFile fp pos -> "Could not load include file '" ++ fp ++ "' at " ++ showPos pos - ParsingTrace s pos -> - "Parsing trace at " ++ showPos pos ++ ": " ++ s InlineNotRendered il -> "Not rendering " ++ show il BlockNotRendered bl -> @@ -281,7 +272,6 @@ messageVerbosity msg = CircularReference{} -> WARNING CouldNotLoadIncludeFile{} -> WARNING ParsingUnescaped{} -> INFO - ParsingTrace{} -> DEBUG InlineNotRendered{} -> INFO BlockNotRendered{} -> INFO DocxParserWarning{} -> WARNING diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 94f933c4d..e203298b8 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -71,7 +71,7 @@ import Data.Monoid ((<>)) import Text.Parsec.Error import qualified Data.Set as Set import Text.Pandoc.Error -import Text.Pandoc.Class (PandocMonad, report) +import Text.Pandoc.Class (PandocMonad(..)) import Control.Monad.Except (throwError) -- | Convert HTML-formatted string to 'Pandoc' document. @@ -162,7 +162,6 @@ pHead = pInTags "head" $ pTitle <|> pMetaTag <|> pBaseTag <|> (mempty <$ pAnyTag block :: PandocMonad m => TagParser m Blocks block = do - pos <- getPosition res <- choice [ eSection , eSwitch B.para block @@ -182,7 +181,7 @@ block = do , pPlain , pRawHtmlBlock ] - report $ ParsingTrace (take 60 $ show $ B.toList res) pos + trace (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 e1c481311..edb356b39 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -52,7 +52,7 @@ import System.FilePath (addExtension, takeExtension) import Text.HTML.TagSoup import Text.Pandoc.Builder (Blocks, Inlines) import qualified Text.Pandoc.Builder as B -import Text.Pandoc.Class (PandocMonad, report) +import Text.Pandoc.Class (PandocMonad(..), report) import Text.Pandoc.Definition import Text.Pandoc.Emoji (emojis) import Text.Pandoc.Logging @@ -488,7 +488,6 @@ parseBlocks = mconcat <$> manyTill block eof block :: PandocMonad m => MarkdownParser m (F Blocks) block = do - pos <- getPosition res <- choice [ mempty <$ blanklines , codeBlockFenced , yamlMetaBlock @@ -514,8 +513,7 @@ block = do , para , plain ] <?> "block" - report $ ParsingTrace - (take 60 $ show $ B.toList $ runF res defaultParserState) pos + trace (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 a3ff60c14..e371ff152 100644 --- a/src/Text/Pandoc/Readers/MediaWiki.hs +++ b/src/Text/Pandoc/Readers/MediaWiki.hs @@ -52,7 +52,7 @@ import qualified Data.Set as Set import Text.HTML.TagSoup import Text.Pandoc.Builder (Blocks, Inlines, trimInlines) import qualified Text.Pandoc.Builder as B -import Text.Pandoc.Class (PandocMonad, report) +import Text.Pandoc.Class (PandocMonad(..)) import Text.Pandoc.Definition import Text.Pandoc.Logging import Text.Pandoc.Options @@ -205,7 +205,6 @@ parseMediaWiki = do block :: PandocMonad m => MWParser m Blocks block = do - pos <- getPosition res <- mempty <$ skipMany1 blankline <|> table <|> header @@ -218,7 +217,7 @@ block = do <|> blockTag <|> (B.rawBlock "mediawiki" <$> template) <|> para - report $ ParsingTrace (take 60 $ show $ B.toList res) pos + trace (take 60 $ show $ B.toList res) return res para :: PandocMonad m => MWParser m Blocks diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index c1ea1354b..ac19a2382 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -53,7 +53,7 @@ import Data.Maybe (fromMaybe) import Text.HTML.TagSoup import Text.Pandoc.Builder (Blocks, Inlines) import qualified Text.Pandoc.Builder as B -import Text.Pandoc.Class (PandocMonad, report) +import Text.Pandoc.Class (PandocMonad(..)) import Text.Pandoc.Definition import Text.Pandoc.Logging import Text.Pandoc.Options @@ -166,12 +166,11 @@ directive = do block :: PandocMonad m => MuseParser m (F Blocks) block = do - pos <- getPosition res <- mempty <$ skipMany1 blankline <|> blockElements <|> para skipMany blankline - report $ ParsingTrace (take 60 $ show $ B.toList $ runF res defaultParserState) pos + trace (take 60 $ show $ B.toList $ runF res defaultParserState) return res blockElements :: PandocMonad m => MuseParser m (F Blocks) diff --git a/src/Text/Pandoc/Readers/TWiki.hs b/src/Text/Pandoc/Readers/TWiki.hs index 9e544c4ac..91ee8d1f1 100644 --- a/src/Text/Pandoc/Readers/TWiki.hs +++ b/src/Text/Pandoc/Readers/TWiki.hs @@ -42,9 +42,8 @@ import qualified Data.Foldable as F import Data.Maybe (fromMaybe) import Text.HTML.TagSoup import qualified Text.Pandoc.Builder as B -import Text.Pandoc.Class (PandocMonad, report) +import Text.Pandoc.Class (PandocMonad(..)) import Text.Pandoc.Definition -import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (enclosed, macro, nested) import Text.Pandoc.Readers.HTML (htmlTag, isCommentTag) @@ -133,12 +132,11 @@ parseTWiki = do block :: PandocMonad m => TWParser m B.Blocks block = do - pos <- getPosition res <- mempty <$ skipMany1 blankline <|> blockElements <|> para skipMany blankline - report $ ParsingTrace (take 60 $ show $ B.toList res) pos + trace (take 60 $ show $ B.toList res) return res blockElements :: PandocMonad m => TWParser m B.Blocks diff --git a/src/Text/Pandoc/Readers/Textile.hs b/src/Text/Pandoc/Readers/Textile.hs index 1669e3e51..96b51feef 100644 --- a/src/Text/Pandoc/Readers/Textile.hs +++ b/src/Text/Pandoc/Readers/Textile.hs @@ -61,10 +61,9 @@ import Text.HTML.TagSoup (Tag (..), fromAttrib) import Text.HTML.TagSoup.Match import Text.Pandoc.Builder (Blocks, Inlines, trimInlines) import qualified Text.Pandoc.Builder as B -import Text.Pandoc.Class (PandocMonad, report) +import Text.Pandoc.Class (PandocMonad(..)) import Text.Pandoc.CSS import Text.Pandoc.Definition -import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Parsing import Text.Pandoc.Readers.HTML (htmlTag, isBlockTag, isInlineTag) @@ -143,8 +142,7 @@ blockParsers = [ codeBlock block :: PandocMonad m => ParserT [Char] ParserState m Blocks block = do res <- choice blockParsers <?> "block" - pos <- getPosition - report $ ParsingTrace (take 60 $ show $ B.toList res) pos + trace (take 60 $ show $ B.toList res) return res commentBlock :: PandocMonad m => ParserT [Char] ParserState m Blocks -- cgit v1.2.3 From b6a38ed1114eae604706694aaca920b86ed28385 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 19 Jun 2017 22:29:01 +0200 Subject: Vimwiki reader: adjusted for changes in trace. --- src/Text/Pandoc/Readers/Vimwiki.hs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Vimwiki.hs b/src/Text/Pandoc/Readers/Vimwiki.hs index 07e23fa1e..0cfbec34d 100644 --- a/src/Text/Pandoc/Readers/Vimwiki.hs +++ b/src/Text/Pandoc/Readers/Vimwiki.hs @@ -77,11 +77,10 @@ import qualified Text.Pandoc.Builder spanWith, para, horizontalRule, blockQuote, bulletList, plain, orderedList, simpleTable, softbreak, codeBlockWith, imageWith, divWith, setMeta, definitionList, superscript, subscript) -import Text.Pandoc.Class (PandocMonad, report) +import Text.Pandoc.Class (PandocMonad(..)) import Text.Pandoc.Definition (Pandoc(..), Inline(Space), Block(BulletList, OrderedList), Attr, nullMeta, Meta, ListNumberStyle(..), ListNumberDelim(..)) -import Text.Pandoc.Logging (LogMessage(ParsingTrace)) import Text.Pandoc.Options (ReaderOptions) import Text.Pandoc.Parsing (readWithM, ParserT, stateOptions, ParserState, stateMeta', blanklines, registerHeader, spaceChar, emailAddress, uri, F, runF, @@ -91,7 +90,7 @@ import Text.Parsec.Char (spaces, char, anyChar, newline, string, noneOf, alphaNum) import Text.Parsec.Combinator (eof, choice, many1, manyTill, count, skipMany1, notFollowedBy, option) -import Text.Parsec.Prim (many, getPosition, try, updateState, getState) +import Text.Parsec.Prim (many, try, updateState, getState) import Text.Parsec.Char (oneOf, space) import Text.Parsec.Combinator (lookAhead, between) import Text.Parsec.Prim ((<|>)) @@ -129,7 +128,6 @@ parseVimwiki = do block :: PandocMonad m => VwParser m Blocks block = do - pos <- getPosition res <- choice [ mempty <$ blanklines , header , hrule @@ -143,7 +141,7 @@ block = do , definitionList , para ] - report $ ParsingTrace (take 60 $ show $ toList res) pos + trace (take 60 $ show $ toList res) return res blockML :: PandocMonad m => VwParser m Blocks -- cgit v1.2.3 From 328655e8636bc524829ae56ffd5ef15ad21f3917 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 19 Jun 2017 22:41:09 +0200 Subject: Tracing: give less misleading line information with parseWithString. Previously positions would be reported past the end of the chunk. We now reset the source position within the chunk and report positions "in chunk." --- src/Text/Pandoc/Class.hs | 9 +++++++-- src/Text/Pandoc/Parsing.hs | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index a7194f8d5..120ba8fee 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -79,7 +79,7 @@ import qualified Text.Pandoc.Shared as IO ( readDataFile import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Compat.Time (UTCTime) import Text.Pandoc.Logging -import Text.Parsec (ParsecT, getPosition) +import Text.Parsec (ParsecT, getPosition, sourceLine, sourceName) import qualified Text.Pandoc.Compat.Time as IO (getCurrentTime) import Text.Pandoc.MIME (MimeType, getMimeType, extensionFromMimeType) import Text.Pandoc.Definition @@ -576,7 +576,12 @@ instance PandocMonad m => PandocMonad (ParsecT s st m) where when tracing $ do pos <- getPosition Debug.Trace.trace - ("[trace] Parsed " ++ msg ++ " at " ++ show pos) (return ()) + ("[trace] Parsed " ++ msg ++ " at line " ++ + show (sourceLine pos) ++ + if sourceName pos == "chunk" + then " of chunk" + else "") + (return ()) logOutput = lift . logOutput diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index cd51bff69..eb5b37f40 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -189,7 +189,7 @@ import qualified Text.Pandoc.Builder as B import Text.Pandoc.XML (fromEntities) import qualified Text.Pandoc.UTF8 as UTF8 (putStrLn) import Text.Parsec hiding (token) -import Text.Parsec.Pos (newPos) +import Text.Parsec.Pos (newPos, initialPos) import Data.Char ( toLower, toUpper, ord, chr, isAscii, isAlphaNum, isHexDigit, isSpace, isPunctuation ) import Data.List ( intercalate, transpose, isSuffixOf ) @@ -366,6 +366,7 @@ parseFromString :: Monad m -> ParserT String st m a parseFromString parser str = do oldPos <- getPosition + setPosition $ initialPos "chunk" oldInput <- getInput setInput str result <- parser -- cgit v1.2.3 From 6a077ac9c79ac16d6af5409976e48ad80f42fd01 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 20 Jun 2017 11:21:32 +0200 Subject: Fixed footnotes in table captions. Note that if the table has a first page header and a continuation page header, the notes will appear only on the first occurrence of the header. Closes #2378. --- src/Text/Pandoc/Writers/LaTeX.hs | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index e0ea9acfe..88ff454ce 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -647,23 +647,25 @@ blockToLaTeX (Header level (id',classes,_) lst) = do modify $ \s -> s{stInHeading = False} return hdr blockToLaTeX (Table caption aligns widths heads rows) = do - headers <- if all null heads - then return empty - else do - contents <- (tableRowToLaTeX True aligns widths) heads - return ("\\toprule" $$ contents $$ "\\midrule") - let endhead = if all null heads - then empty - else text "\\endhead" - let endfirsthead = if all null heads - then empty - else text "\\endfirsthead" + let toHeaders hs = do contents <- (tableRowToLaTeX True aligns widths) hs + return ("\\toprule" $$ contents $$ "\\midrule") + let removeNote (Note _) = Span ("", [], []) [] + removeNote x = x captionText <- inlineListToLaTeX caption + firsthead <- if isEmpty captionText || all null heads + then return empty + else ($$ text "\\endfirsthead") <$> toHeaders heads + head' <- if all null heads + then return empty + -- avoid duplicate notes in head and firsthead: + else ($$ text "\\endhead") <$> + toHeaders (if isEmpty firsthead + then heads + else walk removeNote heads) let capt = if isEmpty captionText then empty - else text "\\caption" <> braces captionText <> "\\tabularnewline" - $$ headers - $$ endfirsthead + else text "\\caption" <> + braces captionText <> "\\tabularnewline" rows' <- mapM (tableRowToLaTeX False aligns widths) rows let colDescriptors = text $ concat $ map toColDescriptor aligns modify $ \s -> s{ stTable = True } @@ -671,9 +673,9 @@ blockToLaTeX (Table caption aligns widths heads rows) = do braces ("@{}" <> colDescriptors <> "@{}") -- the @{} removes extra space at beginning and end $$ capt + $$ firsthead $$ (if all null heads then "\\toprule" else empty) - $$ headers - $$ endhead + $$ head' $$ vcat rows' $$ "\\bottomrule" $$ "\\end{longtable}" -- cgit v1.2.3 From b26d3c45225184a882baad5cfb287f030d966104 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 20 Jun 2017 14:21:43 +0200 Subject: FB2 writer: don't fail with an error on interior headers (e.g. in list). Instead, omit them with an INFO message. Closes #3750. --- src/Text/Pandoc/Writers/FB2.hs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index 20f94c185..f561133fb 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -37,7 +37,7 @@ FictionBook is an XML-based e-book format. For more information see: -} module Text.Pandoc.Writers.FB2 (writeFB2) where -import Control.Monad.Except (catchError, throwError) +import Control.Monad.Except (catchError) import Control.Monad.State.Strict (StateT, evalStateT, get, lift, modify) import Control.Monad.State.Strict (liftM) import Data.ByteString.Base64 (encode) @@ -371,8 +371,10 @@ blockToXml (DefinitionList defs) = needsBreak (Para _) = False needsBreak (Plain ins) = LineBreak `notElem` ins needsBreak _ = True -blockToXml (Header _ _ _) = -- should never happen, see renderSections - throwError $ PandocShouldNeverHappenError "unexpected header in section text" +blockToXml h@(Header _ _ _) = do + -- should not occur after hierarchicalize, except inside lists/blockquotes + report $ BlockNotRendered h + return [] blockToXml HorizontalRule = return [ el "empty-line" () , el "p" (txt (replicate 10 '—')) -- cgit v1.2.3 From ea1724e35e5ac008ef1293cb7b2b49595392c38e Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 20 Jun 2017 14:22:19 +0200 Subject: Docbook, JATS, TEI writers: print INFO message when omitting interior header. This only applies to section headers inside list items, e.g., which were otherwise silently omitted. See #3750. --- src/Text/Pandoc/Writers/Docbook.hs | 6 ++++-- src/Text/Pandoc/Writers/JATS.hs | 6 ++++-- src/Text/Pandoc/Writers/TEI.hs | 10 ++++++---- 3 files changed, 14 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docbook.hs b/src/Text/Pandoc/Writers/Docbook.hs index 02ffbf831..a0e69ffb4 100644 --- a/src/Text/Pandoc/Writers/Docbook.hs +++ b/src/Text/Pandoc/Writers/Docbook.hs @@ -217,8 +217,10 @@ blockToDocbook opts (Div (ident,_,_) bs) = do (if null ident then mempty else selfClosingTag "anchor" [("id", ident)]) $$ contents -blockToDocbook _ (Header _ _ _) = - return empty -- should not occur after hierarchicalize +blockToDocbook _ h@(Header _ _ _) = do + -- should not occur after hierarchicalize, except inside lists/blockquotes + report $ BlockNotRendered h + return empty 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':':':_)]) = do diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs index 1a8d80747..11f3b0c22 100644 --- a/src/Text/Pandoc/Writers/JATS.hs +++ b/src/Text/Pandoc/Writers/JATS.hs @@ -203,8 +203,10 @@ blockToJATS opts (Div (ident,_,kvs) bs) = do [(k,v) | (k,v) <- kvs, k `elem` ["specific-use", "content-type", "orientation", "position"]] return $ inTags True "boxed-text" attr contents -blockToJATS _ (Header _ _ _) = - return empty -- should not occur after hierarchicalize +blockToJATS _ h@(Header _ _ _) = do + -- should not occur after hierarchicalize, except inside lists/blockquotes + report $ BlockNotRendered h + return empty -- No Plain, everything needs to be in a block-level tag blockToJATS opts (Plain lst) = blockToJATS opts (Para lst) -- title beginning with fig: indicates that the image is a figure diff --git a/src/Text/Pandoc/Writers/TEI.hs b/src/Text/Pandoc/Writers/TEI.hs index 27d26c7d9..86a7415cf 100644 --- a/src/Text/Pandoc/Writers/TEI.hs +++ b/src/Text/Pandoc/Writers/TEI.hs @@ -159,11 +159,13 @@ blockToTEI opts (Div (ident,_,_) [Para lst]) = do let attribs = [("id", ident) | not (null ident)] inTags False "p" attribs <$> inlinesToTEI opts lst blockToTEI opts (Div _ bs) = blocksToTEI opts $ map plainToPara bs -blockToTEI _ (Header _ _ _) = return empty --- should not occur after hierarchicalize +blockToTEI _ h@(Header _ _ _) = do + -- should not occur after hierarchicalize, except inside lists/blockquotes + report $ BlockNotRendered h + return empty -- For TEI simple, text must be within containing block element, so --- we use plainToPara to ensure that Plain text ends up contained by --- something. +-- we use treat as Para to ensure that Plain text ends up contained by +-- something: blockToTEI opts (Plain lst) = blockToTEI opts $ Para lst -- title beginning with fig: indicates that the image is a figure --blockToTEI opts (Para [Image attr txt (src,'f':'i':'g':':':_)]) = -- cgit v1.2.3 From 21925284244bb88f927c287c21b48df35234b260 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Tue, 20 Jun 2017 15:48:00 +0300 Subject: Muse reader: check that headers start at the first column (#3749) --- src/Text/Pandoc/Readers/Muse.hs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index ac19a2382..84121cabe 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -205,6 +205,8 @@ separator = try $ do header :: PandocMonad m => MuseParser m (F Blocks) header = try $ do + st <- stateParserContext <$> getState + getPosition >>= \pos -> guard (st == NullState && sourceColumn pos == 1) level <- liftM length $ many1 $ char '*' guard $ level <= 5 skipSpaces -- cgit v1.2.3 From b78afbd9803c75fcf2db32b4ce4ded1b8fa0224a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 20 Jun 2017 16:09:33 +0200 Subject: Text.Pandoc.Lua: throw LuaException instead of using 'error'. Text.Pandoc.App: trap LuaException and issue a PandocFilterError. --- src/Text/Pandoc/App.hs | 12 +++++++++--- src/Text/Pandoc/Lua.hs | 21 ++++++++++++++++----- 2 files changed, 25 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 2c5e1de6b..9778911ba 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -78,7 +78,7 @@ import Text.Pandoc.Builder (setMeta) import Text.Pandoc.Class (PandocIO, extractMedia, fillMediaBag, getLog, setResourcePath, withMediaBag, setTrace) import Text.Pandoc.Highlighting (highlightingStyles) -import Text.Pandoc.Lua (runLuaFilter) +import Text.Pandoc.Lua (runLuaFilter, LuaException(..)) import Text.Pandoc.Writers.Math (defaultMathJaxURL, defaultKaTeXURL) import Text.Pandoc.PDF (makePDF) import Text.Pandoc.Process (pipeProcess) @@ -782,10 +782,16 @@ expandFilterPath mbDatadir fp = liftIO $ do _ -> return fp applyLuaFilters :: MonadIO m - => Maybe FilePath -> [FilePath] -> [String] -> Pandoc -> m Pandoc + => Maybe FilePath -> [FilePath] -> [String] -> Pandoc + -> m Pandoc applyLuaFilters mbDatadir filters args d = do expandedFilters <- mapM (expandFilterPath mbDatadir) filters - foldrM ($) d $ map (flip runLuaFilter args) expandedFilters + let go f d' = liftIO $ do + res <- E.try (runLuaFilter f args d') + case res of + Right x -> return x + Left (LuaException s) -> E.throw (PandocFilterError f s) + foldrM ($) d $ map go expandedFilters applyFilters :: MonadIO m => Maybe FilePath -> [FilePath] -> [String] -> Pandoc -> m Pandoc diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index f74c0e425..e9184c7ce 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -28,11 +28,15 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Pandoc lua utils. -} -module Text.Pandoc.Lua ( runLuaFilter, pushPandocModule ) where +module Text.Pandoc.Lua ( LuaException(..), + runLuaFilter, + pushPandocModule ) where +import Control.Exception import Control.Monad (unless, when, (>=>)) import Control.Monad.Trans (MonadIO (..)) import Data.Map (Map) +import Data.Typeable (Typeable) import Scripting.Lua (LuaState, StackValue (..)) import Text.Pandoc.Definition import Text.Pandoc.Lua.PandocModule (pushPandocModule) @@ -42,6 +46,11 @@ import Text.Pandoc.Walk import qualified Data.Map as Map import qualified Scripting.Lua as Lua +data LuaException = LuaException String + deriving (Show, Typeable) + +instance Exception LuaException + runLuaFilter :: (MonadIO m) => FilePath -> [String] -> Pandoc -> m Pandoc runLuaFilter filterPath args pd = liftIO $ do @@ -59,7 +68,7 @@ runLuaFilter filterPath args pd = liftIO $ do if (status /= 0) then do Just luaErrMsg <- Lua.peek lua 1 - error luaErrMsg + throwIO (LuaException luaErrMsg) else do Lua.call lua 0 Lua.multret newtop <- Lua.gettop lua @@ -195,8 +204,9 @@ instance StackValue a => PushViaFilterFunction (IO a) where Lua.call lua num 1 mbres <- Lua.peek lua (-1) case mbres of - Nothing -> error $ "Error while trying to get a filter's return " - ++ "value from lua stack." + Nothing -> throwIO $ LuaException + ("Error while trying to get a filter's return " + ++ "value from lua stack.") Just res -> res <$ Lua.pop lua 1 instance (StackValue a, PushViaFilterFunction b) => @@ -225,7 +235,8 @@ instance StackValue LuaFilterFunction where push lua v = pushFilterFunction lua v peek lua i = do isFn <- Lua.isfunction lua i - unless isFn (error $ "Not a function at index " ++ (show i)) + unless isFn (throwIO $ LuaException $ + "Not a function at index " ++ (show i)) Lua.pushvalue lua i push lua ("PANDOC_FILTER_FUNCTIONS"::String) Lua.rawget lua Lua.registryindex -- cgit v1.2.3 From 32f86067ecea5c8e63952dfec4fb30999d2a1f77 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 20 Jun 2017 16:16:32 +0200 Subject: App: issue proper errors instead of using 'error'. --- src/Text/Pandoc/App.hs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 9778911ba..12429b51d 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -520,7 +520,8 @@ externalFilter f args' d = liftIO $ do (exitcode, outbs) <- E.handle filterException $ pipeProcess env' f' args'' $ encode d case exitcode of - ExitSuccess -> return $ either error id $ eitherDecode' outbs + ExitSuccess -> either (E.throwIO . PandocFilterError f) + return $ eitherDecode' outbs ExitFailure ec -> E.throwIO $ PandocFilterError f ("Filter returned error status " ++ show ec) where filterException :: E.SomeException -> IO a @@ -978,7 +979,7 @@ options = templ <- getDefaultTemplate Nothing arg case templ of Right t -> UTF8.hPutStr stdout t - Left e -> error $ show e + Left e -> E.throwIO $ PandocAppError (show e) exitSuccess) "FORMAT") "" -- "Print default template for FORMAT" -- cgit v1.2.3 From 429c4620dfe6455bb2d40c7388da384692f85031 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 20 Jun 2017 16:44:05 +0200 Subject: Removed redundant import. --- src/Text/Pandoc/Writers/FB2.hs | 1 - 1 file changed, 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index f561133fb..4c764d987 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -54,7 +54,6 @@ import qualified Text.XML.Light.Cursor as XC import Text.Pandoc.Class (PandocMonad, report) import qualified Text.Pandoc.Class as P import Text.Pandoc.Definition -import Text.Pandoc.Error import Text.Pandoc.Logging import Text.Pandoc.Options (HTMLMathMethod (..), WriterOptions (..), def) import Text.Pandoc.Shared (capitalize, isHeaderBlock, isURI, linesToPara, -- cgit v1.2.3 From bd5a7e525800b41752e422dc9fb6e47ed8bf4479 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Tue, 20 Jun 2017 19:20:50 +0200 Subject: Lua: apply hslint suggestions --- src/Text/Pandoc/Lua.hs | 65 +++++++++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 33 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index e9184c7ce..7cdcfd3d3 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -46,7 +46,7 @@ import Text.Pandoc.Walk import qualified Data.Map as Map import qualified Scripting.Lua as Lua -data LuaException = LuaException String +newtype LuaException = LuaException String deriving (Show, Typeable) instance Exception LuaException @@ -65,7 +65,7 @@ runLuaFilter filterPath args pd = liftIO $ do Lua.setglobal lua "pandoc" top <- Lua.gettop lua status <- Lua.loadfile lua filterPath - if (status /= 0) + if status /= 0 then do Just luaErrMsg <- Lua.peek lua 1 throwIO (LuaException luaErrMsg) @@ -89,8 +89,7 @@ pushGlobalFilter lua = *> Lua.rawseti lua (-2) 1 runAll :: [LuaFilter] -> Pandoc -> IO Pandoc -runAll [] = return -runAll (x:xs) = walkMWithLuaFilter x >=> runAll xs +runAll = foldr ((>=>) . walkMWithLuaFilter) return walkMWithLuaFilter :: LuaFilter -> Pandoc -> IO Pandoc walkMWithLuaFilter (LuaFilter lua fnMap) = @@ -134,20 +133,20 @@ execBlockLuaFilter lua fnMap x = do Nothing -> return x Just fn -> runLuaFilterFunction lua fn x case x of - BlockQuote _ -> tryFilter "BlockQuote" - BulletList _ -> tryFilter "BulletList" - CodeBlock _ _ -> tryFilter "CodeBlock" - DefinitionList _ -> tryFilter "DefinitionList" - Div _ _ -> tryFilter "Div" - Header _ _ _ -> tryFilter "Header" + BlockQuote{} -> tryFilter "BlockQuote" + BulletList{} -> tryFilter "BulletList" + CodeBlock{} -> tryFilter "CodeBlock" + DefinitionList{} -> tryFilter "DefinitionList" + Div{} -> tryFilter "Div" + Header{} -> tryFilter "Header" HorizontalRule -> tryFilter "HorizontalRule" - LineBlock _ -> tryFilter "LineBlock" + LineBlock{} -> tryFilter "LineBlock" Null -> tryFilter "Null" - Para _ -> tryFilter "Para" - Plain _ -> tryFilter "Plain" - RawBlock _ _ -> tryFilter "RawBlock" - OrderedList _ _ -> tryFilter "OrderedList" - Table _ _ _ _ _ -> tryFilter "Table" + Para{} -> tryFilter "Para" + Plain{} -> tryFilter "Plain" + RawBlock{} -> tryFilter "RawBlock" + OrderedList{} -> tryFilter "OrderedList" + Table{} -> tryFilter "Table" execInlineLuaFilter :: LuaState -> FunctionMap @@ -165,27 +164,27 @@ execInlineLuaFilter lua fnMap x = do Nothing -> tryFilterAlternatives alternatives Just fn -> runLuaFilterFunction lua fn x case x of - Cite _ _ -> tryFilter "Cite" - Code _ _ -> tryFilter "Code" - Emph _ -> tryFilter "Emph" - Image _ _ _ -> tryFilter "Image" + Cite{} -> tryFilter "Cite" + Code{} -> tryFilter "Code" + Emph{} -> tryFilter "Emph" + Image{} -> tryFilter "Image" LineBreak -> tryFilter "LineBreak" - Link _ _ _ -> tryFilter "Link" + Link{} -> tryFilter "Link" Math DisplayMath _ -> tryFilterAlternatives ["DisplayMath", "Math"] Math InlineMath _ -> tryFilterAlternatives ["InlineMath", "Math"] - Note _ -> tryFilter "Note" + Note{} -> tryFilter "Note" Quoted DoubleQuote _ -> tryFilterAlternatives ["DoubleQuoted", "Quoted"] Quoted SingleQuote _ -> tryFilterAlternatives ["SingleQuoted", "Quoted"] - RawInline _ _ -> tryFilter "RawInline" - SmallCaps _ -> tryFilter "SmallCaps" + RawInline{} -> tryFilter "RawInline" + SmallCaps{} -> tryFilter "SmallCaps" SoftBreak -> tryFilter "SoftBreak" Space -> tryFilter "Space" - Span _ _ -> tryFilter "Span" - Str _ -> tryFilter "Str" - Strikeout _ -> tryFilter "Strikeout" - Strong _ -> tryFilter "Strong" - Subscript _ -> tryFilter "Subscript" - Superscript _ -> tryFilter "Superscript" + Span{} -> tryFilter "Span" + Str{} -> tryFilter "Str" + Strikeout{} -> tryFilter "Strikeout" + Strong{} -> tryFilter "Strong" + Subscript{} -> tryFilter "Subscript" + Superscript{} -> tryFilter "Superscript" instance StackValue LuaFilter where valuetype _ = Lua.TTABLE @@ -232,11 +231,11 @@ pushFilterFunction lua lf = do instance StackValue LuaFilterFunction where valuetype _ = Lua.TFUNCTION - push lua v = pushFilterFunction lua v + push = pushFilterFunction peek lua i = do isFn <- Lua.isfunction lua i - unless isFn (throwIO $ LuaException $ - "Not a function at index " ++ (show i)) + unless isFn . + throwIO . LuaException $ "Not a function at index " ++ show i Lua.pushvalue lua i push lua ("PANDOC_FILTER_FUNCTIONS"::String) Lua.rawget lua Lua.registryindex -- cgit v1.2.3 From f4c12606e170ffaf558d07c21514ef5dd44d1b40 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Tue, 20 Jun 2017 20:51:10 +0200 Subject: Lua: use registry to store function references Using the registry directly instead of a custom table is cleaner and more efficient. The performance improvement is especially noticable when filtering on frequent elements like Str. --- src/Text/Pandoc/Lua.hs | 48 +++++++++++++++++++----------------------------- 1 file changed, 19 insertions(+), 29 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 7cdcfd3d3..f965bd95d 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -56,10 +56,6 @@ runLuaFilter :: (MonadIO m) runLuaFilter filterPath args pd = liftIO $ do lua <- Lua.newstate Lua.openlibs lua - -- create table in registry to store filter functions - Lua.push lua "PANDOC_FILTER_FUNCTIONS" - Lua.newtable lua - Lua.rawset lua Lua.registryindex -- store module in global "pandoc" pushPandocModule lua Lua.setglobal lua "pandoc" @@ -110,7 +106,7 @@ execDocLuaFilter lua fnMap x = do let docFnName = "Doc" case Map.lookup docFnName fnMap of Nothing -> return x - Just fn -> runLuaFilterFunction lua fn x + Just fn -> runFilterFunction lua fn x execMetaLuaFilter :: LuaState -> FunctionMap @@ -120,7 +116,7 @@ execMetaLuaFilter lua fnMap pd@(Pandoc meta blks) = do case Map.lookup metaFnName fnMap of Nothing -> return pd Just fn -> do - meta' <- runLuaFilterFunction lua fn meta + meta' <- runFilterFunction lua fn meta return $ Pandoc meta' blks execBlockLuaFilter :: LuaState @@ -131,7 +127,7 @@ execBlockLuaFilter lua fnMap x = do tryFilter filterFnName = case Map.lookup filterFnName fnMap of Nothing -> return x - Just fn -> runLuaFilterFunction lua fn x + Just fn -> runFilterFunction lua fn x case x of BlockQuote{} -> tryFilter "BlockQuote" BulletList{} -> tryFilter "BulletList" @@ -156,13 +152,13 @@ execInlineLuaFilter lua fnMap x = do tryFilter filterFnName = case Map.lookup filterFnName fnMap of Nothing -> return x - Just fn -> runLuaFilterFunction lua fn x + Just fn -> runFilterFunction lua fn x let tryFilterAlternatives :: [String] -> IO Inline tryFilterAlternatives [] = return x tryFilterAlternatives (fnName : alternatives) = case Map.lookup fnName fnMap of Nothing -> tryFilterAlternatives alternatives - Just fn -> runLuaFilterFunction lua fn x + Just fn -> runFilterFunction lua fn x case x of Cite{} -> tryFilter "Cite" Code{} -> tryFilter "Code" @@ -213,34 +209,28 @@ instance (StackValue a, PushViaFilterFunction b) => pushViaFilterFunction' lua lf pushArgs num x = pushViaFilterFunction' lua lf (pushArgs *> push lua x) (num + 1) --- | Push an value to the stack via a lua filter function. The function is +-- | Push a value to the stack via a lua filter function. The filter function is -- called with all arguments that are passed to this function and is expected to -- return a single value. -runLuaFilterFunction :: PushViaFilterFunction a +runFilterFunction :: PushViaFilterFunction a => LuaState -> LuaFilterFunction -> a -runLuaFilterFunction lua lf = pushViaFilterFunction' lua lf (return ()) 0 +runFilterFunction lua lf = pushViaFilterFunction' lua lf (return ()) 0 -- | Push the filter function to the top of the stack. pushFilterFunction :: Lua.LuaState -> LuaFilterFunction -> IO () -pushFilterFunction lua lf = do +pushFilterFunction lua lf = -- The function is stored in a lua registry table, retrieve it from there. - push lua ("PANDOC_FILTER_FUNCTIONS"::String) - Lua.rawget lua Lua.registryindex - Lua.rawgeti lua (-1) (functionIndex lf) - Lua.remove lua (-2) -- remove registry table from stack + Lua.rawgeti lua Lua.registryindex (functionIndex lf) + +registerFilterFunction :: LuaState -> Int -> IO LuaFilterFunction +registerFilterFunction lua idx = do + isFn <- Lua.isfunction lua idx + unless isFn . throwIO . LuaException $ "Not a function at index " ++ show idx + Lua.pushvalue lua idx + refIdx <- Lua.ref lua Lua.registryindex + return $ LuaFilterFunction refIdx instance StackValue LuaFilterFunction where valuetype _ = Lua.TFUNCTION push = pushFilterFunction - peek lua i = do - isFn <- Lua.isfunction lua i - unless isFn . - throwIO . LuaException $ "Not a function at index " ++ show i - Lua.pushvalue lua i - push lua ("PANDOC_FILTER_FUNCTIONS"::String) - Lua.rawget lua Lua.registryindex - len <- Lua.objlen lua (-1) - Lua.insert lua (-2) - Lua.rawseti lua (-2) (len + 1) - Lua.pop lua 1 - return . Just $ LuaFilterFunction (len + 1) + peek = fmap (fmap Just) . registerFilterFunction -- cgit v1.2.3 From 5ec84bfeb42e73acb4e309ccde34905b3254fb5c Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 20 Jun 2017 21:11:01 +0200 Subject: Text.Pandoc.Lua - added DeriveDataTypeable for ghc 7.8. --- src/Text/Pandoc/Lua.hs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index f965bd95d..90f72d685 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -1,3 +1,7 @@ +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE DeriveDataTypeable #-} {- Copyright © 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> @@ -15,9 +19,6 @@ 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 -} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE MultiParamTypeClasses #-} {- | Module : Text.Pandoc.Lua Copyright : Copyright © 2017 Albert Krewinkel -- cgit v1.2.3 From 4ba5ef46aeaf979bd74d8f4a5f6cea116527ddd3 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 20 Jun 2017 21:25:39 +0200 Subject: Updated code example. --- src/Text/Pandoc.hs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index 8ee1adf13..b8dba860a 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -39,12 +39,18 @@ inline links: > module Main where > import Text.Pandoc +> import Data.Text (Text) +> import qualified Data.Text.IO as T > -> markdownToRST :: String -> Either PandocError String -> markdownToRST = -> writeRST def {writerReferenceLinks = True} . readMarkdown def +> mdToRST :: Text -> IO Text +> mdToRST txt = runIOorExplode $ +> readMarkdown def txt +> >>= writeRST def{ writerReferenceLinks = True } + > -> main = getContents >>= either error return markdownToRST >>= putStrLn +> main :: IO () +> main = do +> T.getContents >>= mdToRST >>= T.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, -- cgit v1.2.3 From 2363e6a15bdde1c206d65461bd2e21f773dbc808 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 20 Jun 2017 21:52:13 +0200 Subject: Move CR filtering from tabFilter to the readers. The readers previously assumed that CRs had been filtered from the input. Now we strip the CRs in the readers themselves, before parsing. (The point of this is just to simplify the parsers.) Shared now exports a new function `crFilter`. [API change] And `tabFilter` no longer filters CRs. --- src/Text/Pandoc.hs | 4 - src/Text/Pandoc/App.hs | 4 +- src/Text/Pandoc/Readers/DocBook.hs | 5 +- src/Text/Pandoc/Readers/HTML.hs | 4 +- src/Text/Pandoc/Readers/Haddock.hs | 4 +- src/Text/Pandoc/Readers/LaTeX.hs | 3 +- src/Text/Pandoc/Readers/Markdown.hs | 2 +- src/Text/Pandoc/Readers/MediaWiki.hs | 5 +- src/Text/Pandoc/Readers/Muse.hs | 3 +- src/Text/Pandoc/Readers/OPML.hs | 4 +- src/Text/Pandoc/Readers/Org.hs | 3 +- src/Text/Pandoc/Readers/RST.hs | 2 +- src/Text/Pandoc/Readers/TWiki.hs | 3 +- src/Text/Pandoc/Readers/Textile.hs | 4 +- src/Text/Pandoc/Readers/Txt2Tags.hs | 6 +- src/Text/Pandoc/Readers/Vimwiki.hs | 151 ++++++++++++++++++----------------- src/Text/Pandoc/Shared.hs | 12 ++- 17 files changed, 115 insertions(+), 104 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index b8dba860a..9fa5f098d 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -52,10 +52,6 @@ inline links: > main = do > T.getContents >>= mdToRST >>= T.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, -you should remove @'\r'@ characters using @filter (/='\r')@. - -} module Text.Pandoc diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 12429b51d..7e9cfdd95 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -381,8 +381,8 @@ convertWithOpts opts = do | otherwise -> [] let convertTabs = tabFilter (if optPreserveTabs opts || readerName == "t2t" - then 0 - else optTabStop opts) + then 0 + else optTabStop opts) readSources :: [FilePath] -> PandocIO Text readSources srcs = convertTabs . T.intercalate (T.pack "\n") <$> diff --git a/src/Text/Pandoc/Readers/DocBook.hs b/src/Text/Pandoc/Readers/DocBook.hs index 6108aae7f..c1e4d742c 100644 --- a/src/Text/Pandoc/Readers/DocBook.hs +++ b/src/Text/Pandoc/Readers/DocBook.hs @@ -1,6 +1,6 @@ module Text.Pandoc.Readers.DocBook ( readDocBook ) where import Data.Char (toUpper) -import Text.Pandoc.Shared (safeRead) +import Text.Pandoc.Shared (safeRead, crFilter) import Text.Pandoc.Options import Text.Pandoc.Definition import Text.Pandoc.Builder @@ -526,7 +526,8 @@ instance Default DBState where readDocBook :: PandocMonad m => ReaderOptions -> Text -> m Pandoc readDocBook _ inp = do - let tree = normalizeTree . parseXML . handleInstructions $ T.unpack inp + let tree = normalizeTree . parseXML . handleInstructions + $ T.unpack $ crFilter inp (bs, st') <- flip runStateT (def{ dbContent = tree }) $ mapM parseBlock $ tree return $ Pandoc (dbMeta st') (toList . mconcat $ bs) diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index e203298b8..301afa207 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -45,7 +45,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, addMetaField - , escapeURI, safeRead ) + , escapeURI, safeRead, crFilter ) import Text.Pandoc.Options (ReaderOptions(readerExtensions), extensionEnabled, Extension (Ext_epub_html_exts, Ext_raw_html, Ext_native_divs, Ext_native_spans)) @@ -82,7 +82,7 @@ readHtml :: PandocMonad m readHtml opts inp = do let tags = stripPrefixes . canonicalizeTags $ parseTagsOptions parseOptions{ optTagPosition = True } - inp + (crFilter inp) parseDoc = do blocks <- (fixPlains False) . mconcat <$> manyTill block eof meta <- stateMeta . parserState <$> getState diff --git a/src/Text/Pandoc/Readers/Haddock.hs b/src/Text/Pandoc/Readers/Haddock.hs index b22b71b96..a09ed8be9 100644 --- a/src/Text/Pandoc/Readers/Haddock.hs +++ b/src/Text/Pandoc/Readers/Haddock.hs @@ -27,7 +27,7 @@ import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Error import Text.Pandoc.Options -import Text.Pandoc.Shared (splitBy, trim) +import Text.Pandoc.Shared (splitBy, trim, crFilter) -- | Parse Haddock markup and return a 'Pandoc' document. @@ -35,7 +35,7 @@ readHaddock :: PandocMonad m => ReaderOptions -> Text -> m Pandoc -readHaddock opts s = case readHaddockEither opts (unpack s) of +readHaddock opts s = case readHaddockEither opts (unpack (crFilter s)) of Right result -> return result Left e -> throwError e diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 1ac872933..090dc5fdb 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -63,7 +63,8 @@ readLaTeX :: PandocMonad m -> Text -- ^ String to parse (assumes @'\n'@ line endings) -> m Pandoc readLaTeX opts ltx = do - parsed <- readWithM parseLaTeX def{ stateOptions = opts } (unpack ltx) + parsed <- readWithM parseLaTeX def{ stateOptions = opts } + (unpack (crFilter ltx)) case parsed of Right result -> return result Left e -> throwError e diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index edb356b39..96885c9b1 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -74,7 +74,7 @@ readMarkdown :: PandocMonad m -> m Pandoc readMarkdown opts s = do parsed <- (readWithM parseMarkdown) def{ stateOptions = opts } - (T.unpack s ++ "\n\n") + (T.unpack (crFilter s) ++ "\n\n") case parsed of Right result -> return result Left e -> throwError e diff --git a/src/Text/Pandoc/Readers/MediaWiki.hs b/src/Text/Pandoc/Readers/MediaWiki.hs index e371ff152..a7f073d50 100644 --- a/src/Text/Pandoc/Readers/MediaWiki.hs +++ b/src/Text/Pandoc/Readers/MediaWiki.hs @@ -58,7 +58,8 @@ import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (nested) import Text.Pandoc.Readers.HTML (htmlTag, isBlockTag, isCommentTag) -import Text.Pandoc.Shared (safeRead, stringify, stripTrailingNewlines, trim) +import Text.Pandoc.Shared (safeRead, stringify, stripTrailingNewlines, trim, + crFilter) import Text.Pandoc.Walk (walk) import Text.Pandoc.XML (fromEntities) @@ -77,7 +78,7 @@ readMediaWiki opts s = do , mwLogMessages = [] , mwInTT = False } - (unpack s ++ "\n") + (unpack (crFilter s) ++ "\n") case parsed of Right result -> return result Left e -> throwError e diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 84121cabe..7eee064a7 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -57,6 +57,7 @@ import Text.Pandoc.Class (PandocMonad(..)) import Text.Pandoc.Definition import Text.Pandoc.Logging import Text.Pandoc.Options +import Text.Pandoc.Shared (crFilter) import Text.Pandoc.Parsing hiding (macro, nested) import Text.Pandoc.Readers.HTML (htmlTag) import Text.Pandoc.XML (fromEntities) @@ -68,7 +69,7 @@ readMuse :: PandocMonad m -> Text -> m Pandoc readMuse opts s = do - res <- readWithM parseMuse def{ stateOptions = opts } (unpack s) + res <- readWithM parseMuse def{ stateOptions = opts } (unpack (crFilter s)) case res of Left e -> throwError e Right d -> return d diff --git a/src/Text/Pandoc/Readers/OPML.hs b/src/Text/Pandoc/Readers/OPML.hs index e9f876525..c25ace800 100644 --- a/src/Text/Pandoc/Readers/OPML.hs +++ b/src/Text/Pandoc/Readers/OPML.hs @@ -9,6 +9,7 @@ import Text.HTML.TagSoup.Entity (lookupEntity) import Text.Pandoc.Builder import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Options +import Text.Pandoc.Shared (crFilter) import Text.Pandoc.Readers.HTML (readHtml) import Text.Pandoc.Readers.Markdown (readMarkdown) import Text.XML.Light @@ -32,7 +33,8 @@ instance Default OPMLState where readOPML :: PandocMonad m => ReaderOptions -> Text -> m Pandoc readOPML _ inp = do (bs, st') <- flip runStateT def - (mapM parseBlock $ normalizeTree $ parseXML (unpack inp)) + (mapM parseBlock $ normalizeTree $ + parseXML (unpack (crFilter inp))) return $ setTitle (opmlDocTitle st') $ setAuthors (opmlDocAuthors st') $ diff --git a/src/Text/Pandoc/Readers/Org.hs b/src/Text/Pandoc/Readers/Org.hs index 5e0d67d10..eaccc251c 100644 --- a/src/Text/Pandoc/Readers/Org.hs +++ b/src/Text/Pandoc/Readers/Org.hs @@ -36,6 +36,7 @@ import Text.Pandoc.Definition import Text.Pandoc.Error import Text.Pandoc.Options import Text.Pandoc.Parsing (reportLogMessages) +import Text.Pandoc.Shared (crFilter) import Control.Monad.Except (throwError) import Control.Monad.Reader (runReaderT) @@ -51,7 +52,7 @@ readOrg :: PandocMonad m readOrg opts s = do parsed <- flip runReaderT def $ readWithM parseOrg (optionsToParserState opts) - (T.unpack s ++ "\n\n") + (T.unpack (crFilter s) ++ "\n\n") case parsed of Right result -> return result Left _ -> throwError $ PandocParseError "problem parsing org" diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index fb5f6f2d4..d13f697b7 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -68,7 +68,7 @@ readRST :: PandocMonad m -> m Pandoc readRST opts s = do parsed <- (readWithM parseRST) def{ stateOptions = opts } - (T.unpack s ++ "\n\n") + (T.unpack (crFilter s) ++ "\n\n") case parsed of Right result -> return result Left e -> throwError e diff --git a/src/Text/Pandoc/Readers/TWiki.hs b/src/Text/Pandoc/Readers/TWiki.hs index 91ee8d1f1..210d3e5aa 100644 --- a/src/Text/Pandoc/Readers/TWiki.hs +++ b/src/Text/Pandoc/Readers/TWiki.hs @@ -48,6 +48,7 @@ import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (enclosed, macro, nested) import Text.Pandoc.Readers.HTML (htmlTag, isCommentTag) import Text.Pandoc.XML (fromEntities) +import Text.Pandoc.Shared (crFilter) import Data.Text (Text) import qualified Data.Text as T @@ -58,7 +59,7 @@ readTWiki :: PandocMonad m -> m Pandoc readTWiki opts s = do res <- readWithM parseTWiki def{ stateOptions = opts } - (T.unpack s ++ "\n\n") + (T.unpack (crFilter s) ++ "\n\n") case res of Left e -> throwError e Right d -> return d diff --git a/src/Text/Pandoc/Readers/Textile.hs b/src/Text/Pandoc/Readers/Textile.hs index 96b51feef..a80d75340 100644 --- a/src/Text/Pandoc/Readers/Textile.hs +++ b/src/Text/Pandoc/Readers/Textile.hs @@ -68,7 +68,7 @@ import Text.Pandoc.Options import Text.Pandoc.Parsing import Text.Pandoc.Readers.HTML (htmlTag, isBlockTag, isInlineTag) import Text.Pandoc.Readers.LaTeX (rawLaTeXBlock, rawLaTeXInline) -import Text.Pandoc.Shared (trim) +import Text.Pandoc.Shared (trim, crFilter) import Data.Text (Text) import qualified Data.Text as T @@ -79,7 +79,7 @@ readTextile :: PandocMonad m -> m Pandoc readTextile opts s = do parsed <- readWithM parseTextile def{ stateOptions = opts } - (T.unpack s ++ "\n\n") + (T.unpack (crFilter s) ++ "\n\n") case parsed of Right result -> return result Left e -> throwError e diff --git a/src/Text/Pandoc/Readers/Txt2Tags.hs b/src/Text/Pandoc/Readers/Txt2Tags.hs index 260bb7fff..5708358f6 100644 --- a/src/Text/Pandoc/Readers/Txt2Tags.hs +++ b/src/Text/Pandoc/Readers/Txt2Tags.hs @@ -41,7 +41,7 @@ import qualified Text.Pandoc.Builder as B import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (macro, space, spaces, uri) -import Text.Pandoc.Shared (compactify, compactifyDL, escapeURI) +import Text.Pandoc.Shared (compactify, compactifyDL, escapeURI, crFilter) import Control.Monad (guard, void, when) import Control.Monad.Reader (Reader, asks, runReader) import Data.Default @@ -95,7 +95,9 @@ readTxt2Tags :: PandocMonad m -> m Pandoc readTxt2Tags opts s = do meta <- getT2TMeta - let parsed = flip runReader meta $ readWithM parseT2T (def {stateOptions = opts}) (T.unpack s ++ "\n\n") + let parsed = flip runReader meta $ + readWithM parseT2T (def {stateOptions = opts}) $ + T.unpack (crFilter s) ++ "\n\n" case parsed of Right result -> return $ result Left e -> throwError e diff --git a/src/Text/Pandoc/Readers/Vimwiki.hs b/src/Text/Pandoc/Readers/Vimwiki.hs index 0cfbec34d..98f04eda9 100644 --- a/src/Text/Pandoc/Readers/Vimwiki.hs +++ b/src/Text/Pandoc/Readers/Vimwiki.hs @@ -33,10 +33,10 @@ Conversion of vimwiki text to 'Pandoc' document. * [X] header * [X] hrule * [X] comment - * [X] blockquote - * [X] preformatted - * [X] displaymath - * [X] bulletlist / orderedlist + * [X] blockquote + * [X] preformatted + * [X] displaymath + * [X] bulletlist / orderedlist * [X] orderedlist with 1., i., a) etc identification. * [X] todo lists -- not list builder with attributes? using span. * [X] table @@ -57,8 +57,8 @@ Conversion of vimwiki text to 'Pandoc' document. * [X] sub- and super-scripts * misc: * [X] `TODO:` mark - * [X] metadata placeholders: %title and %date - * [O] control placeholders: %template and %nohtml -- %template added to + * [X] metadata placeholders: %title and %date + * [O] control placeholders: %template and %nohtml -- %template added to meta, %nohtml ignored --} @@ -66,29 +66,29 @@ module Text.Pandoc.Readers.Vimwiki ( readVimwiki ) where import Control.Monad.Except (throwError) import Control.Monad (guard) -import Data.Default +import Data.Default import Data.Maybe import Data.Monoid ((<>)) import Data.List (isInfixOf, isPrefixOf) import Data.Text (Text, unpack) import Text.Pandoc.Builder (Blocks, Inlines, trimInlines, fromList, toList) -import qualified Text.Pandoc.Builder - as B (headerWith, str, space, strong, emph, strikeout, code, link, image, - spanWith, para, horizontalRule, blockQuote, bulletList, plain, - orderedList, simpleTable, softbreak, codeBlockWith, imageWith, divWith, +import qualified Text.Pandoc.Builder + as B (headerWith, str, space, strong, emph, strikeout, code, link, image, + spanWith, para, horizontalRule, blockQuote, bulletList, plain, + orderedList, simpleTable, softbreak, codeBlockWith, imageWith, divWith, setMeta, definitionList, superscript, subscript) import Text.Pandoc.Class (PandocMonad(..)) -import Text.Pandoc.Definition (Pandoc(..), Inline(Space), - Block(BulletList, OrderedList), Attr, nullMeta, Meta, ListNumberStyle(..), +import Text.Pandoc.Definition (Pandoc(..), Inline(Space), + Block(BulletList, OrderedList), Attr, nullMeta, Meta, ListNumberStyle(..), ListNumberDelim(..)) import Text.Pandoc.Options (ReaderOptions) -import Text.Pandoc.Parsing (readWithM, ParserT, stateOptions, ParserState, +import Text.Pandoc.Parsing (readWithM, ParserT, stateOptions, ParserState, stateMeta', blanklines, registerHeader, spaceChar, emailAddress, uri, F, runF, orderedListMarker, many1Till) -import Text.Pandoc.Shared (splitBy, stripFirstAndLast, stringify) -import Text.Parsec.Char (spaces, char, anyChar, newline, string, noneOf, +import Text.Pandoc.Shared (splitBy, stripFirstAndLast, stringify, crFilter) +import Text.Parsec.Char (spaces, char, anyChar, newline, string, noneOf, alphaNum) -import Text.Parsec.Combinator (eof, choice, many1, manyTill, count, skipMany1, +import Text.Parsec.Combinator (eof, choice, many1, manyTill, count, skipMany1, notFollowedBy, option) import Text.Parsec.Prim (many, try, updateState, getState) import Text.Parsec.Char (oneOf, space) @@ -97,7 +97,8 @@ import Text.Parsec.Prim ((<|>)) readVimwiki :: PandocMonad m => ReaderOptions -> Text -> m Pandoc readVimwiki opts s = do - res <- readWithM parseVimwiki def{ stateOptions = opts } (unpack s) + res <- readWithM parseVimwiki def{ stateOptions = opts } + (unpack (crFilter s)) case res of Left e -> throwError e Right result -> return result @@ -110,7 +111,7 @@ type VwParser = ParserT [Char] ParserState specialChars :: [Char] specialChars = "=*-#[]_~{}`$|:%^," -spaceChars :: [Char] +spaceChars :: [Char] spaceChars = " \t\n" -- main parser @@ -134,7 +135,7 @@ block = do , mempty <$ comment , mixedList , preformatted - , displayMath + , displayMath , table , mempty <$ placeholder , blockQuote @@ -149,14 +150,14 @@ blockML = choice [preformatted, displayMath, table] header :: PandocMonad m => VwParser m Blocks header = try $ do - sp <- many spaceChar + sp <- many spaceChar eqs <- many1 (char '=') spaceChar let lev = length eqs guard $ lev <= 6 - contents <- trimInlines . mconcat <$> manyTill inline (try $ spaceChar - >> (string eqs) >> many spaceChar >> newline) - attr <- registerHeader (makeId contents, + contents <- trimInlines . mconcat <$> manyTill inline (try $ spaceChar + >> (string eqs) >> many spaceChar >> newline) + attr <- registerHeader (makeId contents, (if sp == "" then [] else ["justcenter"]), []) contents return $ B.headerWith attr lev contents @@ -184,7 +185,7 @@ blockQuote = try $ do else return $ B.blockQuote $ B.plain contents definitionList :: PandocMonad m => VwParser m Blocks -definitionList = try $ +definitionList = try $ B.definitionList <$> (many1 (dlItemWithDT <|> dlItemWithoutDT)) dlItemWithDT :: PandocMonad m => VwParser m (Inlines, [Blocks]) @@ -199,15 +200,15 @@ dlItemWithoutDT = do return $ (mempty, dds) definitionDef :: PandocMonad m => VwParser m Blocks -definitionDef = try $ - (notFollowedBy definitionTerm) >> many spaceChar +definitionDef = try $ + (notFollowedBy definitionTerm) >> many spaceChar >> (definitionDef1 <|> definitionDef2) definitionDef1 :: PandocMonad m => VwParser m Blocks definitionDef1 = try $ mempty <$ defMarkerE definitionDef2 :: PandocMonad m => VwParser m Blocks -definitionDef2 = try $ B.plain <$> +definitionDef2 = try $ B.plain <$> (defMarkerM >> (trimInlines . mconcat <$> many inline') <* newline) @@ -218,11 +219,11 @@ definitionTerm = try $ do return x definitionTerm1 :: PandocMonad m => VwParser m Inlines -definitionTerm1 = try $ +definitionTerm1 = try $ trimInlines . mconcat <$> manyTill inline' (try $ defMarkerE) definitionTerm2 :: PandocMonad m => VwParser m Inlines -definitionTerm2 = try $ trimInlines . mconcat <$> manyTill inline' +definitionTerm2 = try $ trimInlines . mconcat <$> manyTill inline' (try $ lookAhead $ (defMarkerM >> notFollowedBy hasDefMarkerM)) defMarkerM :: PandocMonad m => VwParser m Char @@ -236,8 +237,8 @@ hasDefMarkerM = manyTill (noneOf "\n") (try defMarkerM) preformatted :: PandocMonad m => VwParser m Blocks preformatted = try $ do - many spaceChar >> string "{{{" - attrText <- many (noneOf "\n") + many spaceChar >> string "{{{" + attrText <- many (noneOf "\n") lookAhead newline contents <- manyTill anyChar (try (char '\n' >> many spaceChar >> string "}}}" >> many spaceChar >> newline)) @@ -246,14 +247,14 @@ preformatted = try $ do else return $ B.codeBlockWith (makeAttr attrText) contents makeAttr :: String -> Attr -makeAttr s = +makeAttr s = let xs = splitBy (`elem` " \t") s in ("", [], catMaybes $ map nameValue xs) nameValue :: String -> Maybe (String, String) -nameValue s = +nameValue s = let t = splitBy (== '=') s in - if length t /= 2 + if length t /= 2 then Nothing else let (a, b) = (head t, last t) in if ((length b) < 2) || ((head b, last b) /= ('"', '"')) @@ -269,7 +270,7 @@ displayMath = try $ do >> many spaceChar >> newline)) let contentsWithTags | mathTag == "" = "\\[" ++ contents ++ "\n\\]" - | otherwise = "\\begin{" ++ mathTag ++ "}" ++ contents + | otherwise = "\\begin{" ++ mathTag ++ "}" ++ contents ++ "\n\\end{" ++ mathTag ++ "}" return $ B.plain $ B.str contentsWithTags @@ -286,7 +287,7 @@ mixedList' prevInd = do else do listStart curLine <- listItemContent - let listBuilder = + let listBuilder = if builder == "ul" then B.bulletList else B.orderedList (subList, lowInd) <- (mixedList' curInd) if lowInd >= curInd @@ -297,7 +298,7 @@ mixedList' prevInd = do then return ([listBuilder curList], endInd) else return (curList, endInd) else do - let (curList, endInd) = ((combineList curLine subList), + let (curList, endInd) = ((combineList curLine subList), lowInd) if curInd > prevInd then return ([listBuilder curList], endInd) @@ -328,13 +329,13 @@ blocksThenInline = try $ do return $ mconcat $ y ++ [x] listTodoMarker :: PandocMonad m => VwParser m Inlines -listTodoMarker = try $ do - x <- between (many spaceChar >> char '[') (char ']' >> spaceChar) +listTodoMarker = try $ do + x <- between (many spaceChar >> char '[') (char ']' >> spaceChar) (oneOf " .oOX") return $ makeListMarkerSpan x makeListMarkerSpan :: Char -> Inlines -makeListMarkerSpan x = +makeListMarkerSpan x = let cl = case x of ' ' -> "done0" '.' -> "done1" @@ -347,9 +348,9 @@ makeListMarkerSpan x = combineList :: Blocks -> [Blocks] -> [Blocks] combineList x [y] = case toList y of - [BulletList z] -> [fromList $ (toList x) + [BulletList z] -> [fromList $ (toList x) ++ [BulletList z]] - [OrderedList attr z] -> [fromList $ (toList x) + [OrderedList attr z] -> [fromList $ (toList x) ++ [OrderedList attr z]] _ -> x:[y] combineList x xs = x:xs @@ -365,9 +366,9 @@ bulletListMarkers :: PandocMonad m => VwParser m String bulletListMarkers = "ul" <$ (char '*' <|> char '-') orderedListMarkers :: PandocMonad m => VwParser m String -orderedListMarkers = - ("ol" <$ (choice $ (orderedListMarker Decimal Period):(($OneParen) - <$> orderedListMarker +orderedListMarkers = + ("ol" <$ (choice $ (orderedListMarker Decimal Period):(($OneParen) + <$> orderedListMarker <$> [Decimal, LowerRoman, UpperRoman, LowerAlpha, UpperAlpha]))) <|> ("ol" <$ char '#') @@ -397,14 +398,14 @@ table2 = try $ do tableHeaderSeparator :: PandocMonad m => VwParser m () tableHeaderSeparator = try $ do - many spaceChar >> char '|' >> many1 ((many1 $ char '-') >> char '|') + many spaceChar >> char '|' >> many1 ((many1 $ char '-') >> char '|') >> many spaceChar >> newline return () - + tableRow :: PandocMonad m => VwParser m [Blocks] tableRow = try $ do many spaceChar >> char '|' - s <- lookAhead $ manyTill anyChar (try (char '|' >> many spaceChar + s <- lookAhead $ manyTill anyChar (try (char '|' >> many spaceChar >> newline)) guard $ not $ "||" `isInfixOf` ("|" ++ s ++ "|") tr <- many tableCell @@ -416,25 +417,25 @@ tableCell = try $ B.plain <$> trimInlines . mconcat <$> (manyTill inline' (char '|')) placeholder :: PandocMonad m => VwParser m () -placeholder = try $ +placeholder = try $ (choice (ph <$> ["title", "date"])) <|> noHtmlPh <|> templatePh ph :: PandocMonad m => String -> VwParser m () ph s = try $ do many spaceChar >> (string $ '%':s) >> spaceChar - contents <- (trimInlines . mconcat <$> (manyTill inline (lookAhead newline))) + contents <- (trimInlines . mconcat <$> (manyTill inline (lookAhead newline))) --use lookAhead because of placeholder in the whitespace parser let meta' = return $ B.setMeta s contents nullMeta :: F Meta updateState $ \st -> st { stateMeta' = stateMeta' st <> meta' } noHtmlPh :: PandocMonad m => VwParser m () noHtmlPh = try $ - () <$ (many spaceChar >> string "%nohtml" >> many spaceChar + () <$ (many spaceChar >> string "%nohtml" >> many spaceChar >> (lookAhead newline)) templatePh :: PandocMonad m => VwParser m () templatePh = try $ - () <$ (many spaceChar >> string "%template" >> (many $ noneOf "\n") + () <$ (many spaceChar >> string "%template" >> (many $ noneOf "\n") >> (lookAhead newline)) -- inline parser @@ -475,7 +476,7 @@ str :: PandocMonad m => VwParser m Inlines str = B.str <$> (many1 $ noneOf $ spaceChars ++ specialChars) whitespace :: PandocMonad m => VwParser m () -> VwParser m Inlines -whitespace endline = B.space <$ (skipMany1 spaceChar <|> +whitespace endline = B.space <$ (skipMany1 spaceChar <|> (try (newline >> (comment <|> placeholder)))) <|> B.softbreak <$ endline @@ -493,24 +494,24 @@ bareURL = try $ do strong :: PandocMonad m => VwParser m Inlines strong = try $ do s <- lookAhead $ between (char '*') (char '*') (many1 $ noneOf "*") - guard $ (not $ (head s) `elem` spaceChars) + guard $ (not $ (head s) `elem` spaceChars) && (not $ (last s) `elem` spaceChars) char '*' - contents <- mconcat <$> (manyTill inline' $ char '*' + contents <- mconcat <$> (manyTill inline' $ char '*' >> notFollowedBy alphaNum) - return $ (B.spanWith ((makeId contents), [], []) mempty) + return $ (B.spanWith ((makeId contents), [], []) mempty) <> (B.strong contents) -makeId :: Inlines -> String +makeId :: Inlines -> String makeId i = concat (stringify <$> (toList i)) emph :: PandocMonad m => VwParser m Inlines emph = try $ do s <- lookAhead $ between (char '_') (char '_') (many1 $ noneOf "_") - guard $ (not $ (head s) `elem` spaceChars) + guard $ (not $ (head s) `elem` spaceChars) && (not $ (last s) `elem` spaceChars) char '_' - contents <- mconcat <$> (manyTill inline' $ char '_' + contents <- mconcat <$> (manyTill inline' $ char '_' >> notFollowedBy alphaNum) return $ B.emph contents @@ -532,32 +533,32 @@ superscript = try $ subscript :: PandocMonad m => VwParser m Inlines subscript = try $ - B.subscript <$> mconcat <$> (string ",," + B.subscript <$> mconcat <$> (string ",," >> many1Till inline' (try $ string ",,")) link :: PandocMonad m => VwParser m Inlines -link = try $ do +link = try $ do string "[[" contents <- lookAhead $ manyTill anyChar (string "]]") - case '|' `elem` contents of + case '|' `elem` contents of False -> do - manyTill anyChar (string "]]") + manyTill anyChar (string "]]") -- not using try here because [[hell]o]] is not rendered as a link in vimwiki return $ B.link (procLink contents) "" (B.str contents) - True -> do + True -> do url <- manyTill anyChar $ char '|' lab <- mconcat <$> (manyTill inline $ string "]]") return $ B.link (procLink url) "" lab image :: PandocMonad m => VwParser m Inlines -image = try $ do +image = try $ do string "{{" contentText <- lookAhead $ manyTill (noneOf "\n") (try $ string "}}") images $ length $ filter (== '|') contentText images :: PandocMonad m => Int -> VwParser m Inlines images k - | k == 0 = do + | k == 0 = do imgurl <- manyTill anyChar (try $ string "}}") return $ B.image (procImgurl imgurl) "" (B.str "") | k == 1 = do @@ -578,15 +579,15 @@ images k procLink' :: String -> String procLink' s - | ((take 6 s) == "local:") = "file" ++ (drop 5 s) + | ((take 6 s) == "local:") = "file" ++ (drop 5 s) | ((take 6 s) == "diary:") = "diary/" ++ (drop 6 s) ++ ".html" - | or ((`isPrefixOf` s) <$> [ "http:", "https:", "ftp:", "file:", "mailto:", - "news:", "telnet:" ]) + | or ((`isPrefixOf` s) <$> [ "http:", "https:", "ftp:", "file:", "mailto:", + "news:", "telnet:" ]) = s | s == "" = "" | (last s) == '/' = s | otherwise = s ++ ".html" - + procLink :: String -> String procLink s = procLink' x ++ y where (x, y) = break (=='#') s @@ -606,7 +607,7 @@ tag = try $ do s <- manyTill (noneOf spaceChars) (try (char ':' >> (lookAhead space))) guard $ not $ "::" `isInfixOf` (":" ++ s ++ ":") let ss = splitBy (==':') s - return $ mconcat $ (makeTagSpan' $ head ss):(makeTagSpan <$> (tail ss)) + return $ mconcat $ (makeTagSpan' $ head ss):(makeTagSpan <$> (tail ss)) todoMark :: PandocMonad m => VwParser m Inlines todoMark = try $ do @@ -623,7 +624,7 @@ endlineBQ = () <$ try (newline <* nFBTTBSB <* string " ") endlineML :: PandocMonad m => VwParser m () endlineML = () <$ try (newline <* nFBTTBSB <* many1 spaceChar) ---- nFBTTBSB is short for notFollowedByThingsThatBreakSoftBreaks +--- nFBTTBSB is short for notFollowedByThingsThatBreakSoftBreaks nFBTTBSB :: PandocMonad m => VwParser m () nFBTTBSB = notFollowedBy newline <* @@ -639,7 +640,7 @@ hasDefMarker :: PandocMonad m => VwParser m () hasDefMarker = () <$ (manyTill (noneOf "\n") (string "::" >> oneOf spaceChars)) makeTagSpan' :: String -> Inlines -makeTagSpan' s = B.spanWith ('-' : s, [], []) (B.str "") <> +makeTagSpan' s = B.spanWith ('-' : s, [], []) (B.str "") <> B.spanWith (s, ["tag"], []) (B.str s) makeTagSpan :: String -> Inlines @@ -647,7 +648,7 @@ makeTagSpan s = (B.space) <> (makeTagSpan' s) mathTagParser :: PandocMonad m => VwParser m String mathTagParser = do - s <- try $ lookAhead (char '%' >> (manyTill (noneOf spaceChars) + s <- try $ lookAhead (char '%' >> (manyTill (noneOf spaceChars) (try $ char '%' >> many (noneOf $ '%':spaceChars) >> space))) char '%' >> string s >> char '%' return s diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 7b299c56b..53fd38ffd 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -49,6 +49,7 @@ module Text.Pandoc.Shared ( toRomanNumeral, escapeURI, tabFilter, + crFilter, -- * Date/time normalizeDate, -- * Pandoc block and inline list processing @@ -279,13 +280,12 @@ escapeURI = escapeURIString (not . needsEscaping) where needsEscaping c = isSpace c || c `elem` ['<','>','|','"','{','}','[',']','^', '`'] --- | Convert tabs to spaces and filter out DOS line endings. --- Tabs will be preserved if tab stop is set to 0. +-- | Convert tabs to spaces. Tabs will be preserved if tab stop is set to 0. tabFilter :: Int -- ^ Tab stop -> T.Text -- ^ Input -> T.Text -tabFilter tabStop = T.filter (/= '\r') . T.unlines . - (if tabStop == 0 then id else map go) . T.lines +tabFilter 0 = id +tabFilter tabStop = T.unlines . map go . T.lines where go s = let (s1, s2) = T.break (== '\t') s in if T.null s2 @@ -294,6 +294,10 @@ tabFilter tabStop = T.filter (/= '\r') . T.unlines . (tabStop - (T.length s1 `mod` tabStop)) (T.pack " ") <> go (T.drop 1 s2) +-- | Strip out DOS line endings. +crFilter :: T.Text -> T.Text +crFilter = T.filter (/= '\r') + -- -- Date/time -- -- cgit v1.2.3 From 8f8f505fd4db9a4903dc616dc179901d2492c6dd Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 20 Jun 2017 22:41:34 +0200 Subject: Text.Pandoc.Error: added PandocTemplateError. --- src/Text/Pandoc/Error.hs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs index 3cf381168..0056a1591 100644 --- a/src/Text/Pandoc/Error.hs +++ b/src/Text/Pandoc/Error.hs @@ -61,6 +61,7 @@ data PandocError = PandocIOError String IOError | PandocFilterError String String | PandocCouldNotFindDataFileError String | PandocResourceNotFound String + | PandocTemplateError String | PandocAppError String deriving (Show, Typeable, Generic) @@ -101,6 +102,7 @@ handleError (Left e) = "Could not find data file " ++ fn PandocResourceNotFound fn -> err 99 $ "File " ++ fn ++ " not found in resource path" + PandocTemplateError s -> err 5 s PandocAppError s -> err 1 s err :: Int -> String -> IO a -- cgit v1.2.3 From c0a12860253c2ddf67d6e9bcb0d6b67f4be18c18 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 20 Jun 2017 22:41:56 +0200 Subject: Text.Pandoc.Templates: change type of renderTemplate'. Now it runs in PandocMonad and raises a proper PandocTemplateError if there are problems, rather than failing with uncatchable 'error'. --- src/Text/Pandoc/Templates.hs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Templates.hs b/src/Text/Pandoc/Templates.hs index 9b635a97b..1a26b7168 100644 --- a/src/Text/Pandoc/Templates.hs +++ b/src/Text/Pandoc/Templates.hs @@ -33,20 +33,20 @@ A simple templating system with variable substitution and conditionals. -} -module Text.Pandoc.Templates ( renderTemplate +module Text.Pandoc.Templates ( module Text.DocTemplates , renderTemplate' - , TemplateTarget - , varListToJSON - , compileTemplate - , Template - , getDefaultTemplate ) where + , getDefaultTemplate + ) where import qualified Control.Exception as E (IOException, try) +import Control.Monad.Except (throwError) import Data.Aeson (ToJSON (..)) import qualified Data.Text as T import System.FilePath ((<.>), (</>)) import Text.DocTemplates (Template, TemplateTarget, applyTemplate, compileTemplate, renderTemplate, varListToJSON) +import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Error import Text.Pandoc.Shared (readDataFileUTF8) -- | Get default template for the specified writer. @@ -72,7 +72,11 @@ getDefaultTemplate user writer = do _ -> let fname = "templates" </> "default" <.> format in E.try $ readDataFileUTF8 user fname --- | Like 'applyTemplate', but raising an error if compilation fails. -renderTemplate' :: (ToJSON a, TemplateTarget b) => String -> a -> b -renderTemplate' template = either error id . applyTemplate (T.pack template) - +-- | Like 'applyTemplate', but runs in PandocMonad and +-- raises an error if compilation fails. +renderTemplate' :: (PandocMonad m, ToJSON a, TemplateTarget b) + => String -> a -> m b +renderTemplate' template context = do + case applyTemplate (T.pack template) context of + Left e -> throwError (PandocTemplateError e) + Right r -> return r -- cgit v1.2.3 From 21c4281b13bf2d72012139ecc5c16cf7cae21de1 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 20 Jun 2017 22:43:06 +0200 Subject: Odt reader: replaced collectRights with rights from Data.Either. --- src/Text/Pandoc/Readers/Odt/Generic/Fallible.hs | 5 ----- src/Text/Pandoc/Readers/Odt/Generic/XMLConverter.hs | 3 ++- 2 files changed, 2 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Odt/Generic/Fallible.hs b/src/Text/Pandoc/Readers/Odt/Generic/Fallible.hs index 4d6a67b8e..8c47cdaf5 100644 --- a/src/Text/Pandoc/Readers/Odt/Generic/Fallible.hs +++ b/src/Text/Pandoc/Readers/Odt/Generic/Fallible.hs @@ -124,8 +124,3 @@ instance ChoiceVector SuccessList where spreadChoice = Right . SuccessList . (foldr unTagRight []) . collectNonFailing where unTagRight (Right x) = (x:) unTagRight _ = id - --- | Like 'catMaybes', but for 'Either'. -collectRights :: [Either _l r] -> [r] -collectRights = collectNonFailing . untag . spreadChoice . SuccessList - where untag = fromLeft (error "Unexpected Left") diff --git a/src/Text/Pandoc/Readers/Odt/Generic/XMLConverter.hs b/src/Text/Pandoc/Readers/Odt/Generic/XMLConverter.hs index 1c3e08a7f..428048427 100644 --- a/src/Text/Pandoc/Readers/Odt/Generic/XMLConverter.hs +++ b/src/Text/Pandoc/Readers/Odt/Generic/XMLConverter.hs @@ -71,6 +71,7 @@ import Control.Applicative hiding ( liftA, liftA2 ) import Control.Monad ( MonadPlus ) import Control.Arrow +import Data.Either ( rights ) import qualified Data.Map as M import Data.Default import Data.Maybe @@ -604,7 +605,7 @@ tryAll :: (NameSpaceID nsID) -> XMLConverter nsID extraState b [a] tryAll nsID name a = prepareIteration nsID name >>> iterateS (switchingTheStack a) - >>^ collectRights + >>^ rights -------------------------------------------------------------------------------- -- Matching children -- cgit v1.2.3 From c349f0b0baef5866041b6668fff5bbb16f0002f9 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 20 Jun 2017 22:43:48 +0200 Subject: Writers: adjusted for renderTemplate' changes. Now we raise a proper error on template failure. --- src/Text/Pandoc/Writers/AsciiDoc.hs | 2 +- src/Text/Pandoc/Writers/CommonMark.hs | 6 +++--- src/Text/Pandoc/Writers/ConTeXt.hs | 6 +++--- src/Text/Pandoc/Writers/Custom.hs | 7 +++++-- src/Text/Pandoc/Writers/Docbook.hs | 6 +++--- src/Text/Pandoc/Writers/DokuWiki.hs | 2 +- src/Text/Pandoc/Writers/HTML.hs | 2 +- src/Text/Pandoc/Writers/Haddock.hs | 2 +- src/Text/Pandoc/Writers/ICML.hs | 6 +++--- src/Text/Pandoc/Writers/JATS.hs | 6 +++--- src/Text/Pandoc/Writers/LaTeX.hs | 6 +++--- src/Text/Pandoc/Writers/Man.hs | 2 +- src/Text/Pandoc/Writers/Markdown.hs | 2 +- src/Text/Pandoc/Writers/MediaWiki.hs | 5 ++--- src/Text/Pandoc/Writers/Ms.hs | 5 +++-- src/Text/Pandoc/Writers/Muse.hs | 2 +- src/Text/Pandoc/Writers/OPML.hs | 6 +++--- src/Text/Pandoc/Writers/OpenDocument.hs | 6 +++--- src/Text/Pandoc/Writers/Org.hs | 2 +- src/Text/Pandoc/Writers/RST.hs | 2 +- src/Text/Pandoc/Writers/RTF.hs | 7 ++++--- src/Text/Pandoc/Writers/TEI.hs | 2 +- src/Text/Pandoc/Writers/Texinfo.hs | 2 +- src/Text/Pandoc/Writers/Textile.hs | 2 +- src/Text/Pandoc/Writers/ZimWiki.hs | 2 +- 25 files changed, 51 insertions(+), 47 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/AsciiDoc.hs b/src/Text/Pandoc/Writers/AsciiDoc.hs index ee977f90b..112f8b657 100644 --- a/src/Text/Pandoc/Writers/AsciiDoc.hs +++ b/src/Text/Pandoc/Writers/AsciiDoc.hs @@ -105,7 +105,7 @@ pandocToAsciiDoc opts (Pandoc meta blocks) = do $ metadata' case writerTemplate opts of Nothing -> return main - Just tpl -> return $ renderTemplate' tpl context + Just tpl -> renderTemplate' tpl context -- | Escape special characters for AsciiDoc. escapeString :: String -> String diff --git a/src/Text/Pandoc/Writers/CommonMark.hs b/src/Text/Pandoc/Writers/CommonMark.hs index 93cc0b53a..63249a7ce 100644 --- a/src/Text/Pandoc/Writers/CommonMark.hs +++ b/src/Text/Pandoc/Writers/CommonMark.hs @@ -58,9 +58,9 @@ writeCommonMark opts (Pandoc meta blocks) = do (inlinesToCommonMark opts) meta let context = defField "body" main $ metadata - return $ case writerTemplate opts of - Nothing -> main - Just tpl -> renderTemplate' tpl context + case writerTemplate opts of + Nothing -> return main + Just tpl -> renderTemplate' tpl context processNotes :: Inline -> State [[Block]] Inline processNotes (Note bs) = do diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index 571c55b19..5a81aa8a0 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -105,9 +105,9 @@ pandocToConTeXt options (Pandoc meta blocks) = do getField "lang" context) $ defField "context-dir" (toContextDir $ getField "dir" context) $ context - return $ case writerTemplate options of - Nothing -> main - Just tpl -> renderTemplate' tpl context' + case writerTemplate options of + Nothing -> return main + Just tpl -> renderTemplate' tpl context' toContextDir :: Maybe String -> String toContextDir (Just "rtl") = "r2l" diff --git a/src/Text/Pandoc/Writers/Custom.hs b/src/Text/Pandoc/Writers/Custom.hs index 1314ef844..363bad99b 100644 --- a/src/Text/Pandoc/Writers/Custom.hs +++ b/src/Text/Pandoc/Writers/Custom.hs @@ -46,6 +46,7 @@ import Data.Typeable import GHC.IO.Encoding (getForeignEncoding, setForeignEncoding, utf8) import Scripting.Lua (LuaState, StackValue, callfunc) import qualified Scripting.Lua as Lua +import Text.Pandoc.Error import Text.Pandoc.Lua.Compat ( loadstring ) import Text.Pandoc.Lua.Util ( addValue ) import Text.Pandoc.Lua.SharedInstances () @@ -141,8 +142,10 @@ writeCustom luaFile opts doc@(Pandoc meta _) = do let body = rendered case writerTemplate opts of Nothing -> return $ pack body - Just tpl -> return $ pack $ - renderTemplate' tpl $ setField "body" body context + Just tpl -> + case applyTemplate (pack tpl) $ setField "body" body context of + Left e -> throw (PandocTemplateError e) + Right r -> return (pack r) docToCustom :: LuaState -> WriterOptions -> Pandoc -> IO String docToCustom lua opts (Pandoc (Meta metamap) blocks) = do diff --git a/src/Text/Pandoc/Writers/Docbook.hs b/src/Text/Pandoc/Writers/Docbook.hs index a0e69ffb4..9db9a0102 100644 --- a/src/Text/Pandoc/Writers/Docbook.hs +++ b/src/Text/Pandoc/Writers/Docbook.hs @@ -124,9 +124,9 @@ writeDocbook opts (Pandoc meta blocks) = do MathML -> True _ -> False) $ metadata - return $ case writerTemplate opts of - Nothing -> main - Just tpl -> renderTemplate' tpl context + case writerTemplate opts of + Nothing -> return main + Just tpl -> renderTemplate' tpl context -- | Convert an Element to Docbook. elementToDocbook :: PandocMonad m => WriterOptions -> Int -> Element -> DB m Doc diff --git a/src/Text/Pandoc/Writers/DokuWiki.hs b/src/Text/Pandoc/Writers/DokuWiki.hs index dc227cfa9..ad8689e8c 100644 --- a/src/Text/Pandoc/Writers/DokuWiki.hs +++ b/src/Text/Pandoc/Writers/DokuWiki.hs @@ -103,7 +103,7 @@ pandocToDokuWiki opts (Pandoc meta blocks) = do $ metadata case writerTemplate opts of Nothing -> return main - Just tpl -> return $ renderTemplate' tpl context + Just tpl -> renderTemplate' tpl context -- | Escape special characters for DokuWiki. escapeString :: String -> String diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 43c098866..3687ca53b 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -210,7 +210,7 @@ writeHtmlString' st opts d = do lookup "sourcefile" (writerVariables opts) report $ NoTitleElement fallback return $ resetField "pagetitle" fallback context - return $ renderTemplate' tpl $ + renderTemplate' tpl $ defField "body" (renderHtml' body) context' writeHtml' :: PandocMonad m => WriterState -> WriterOptions -> Pandoc -> m Html diff --git a/src/Text/Pandoc/Writers/Haddock.hs b/src/Text/Pandoc/Writers/Haddock.hs index 7965ebfae..d1146ca73 100644 --- a/src/Text/Pandoc/Writers/Haddock.hs +++ b/src/Text/Pandoc/Writers/Haddock.hs @@ -80,7 +80,7 @@ pandocToHaddock opts (Pandoc meta blocks) = do $ metadata case writerTemplate opts of Nothing -> return main - Just tpl -> return $ renderTemplate' tpl context + Just tpl -> renderTemplate' tpl context -- | Return haddock representation of notes. notesToHaddock :: PandocMonad m diff --git a/src/Text/Pandoc/Writers/ICML.hs b/src/Text/Pandoc/Writers/ICML.hs index e564f94fe..37df58e65 100644 --- a/src/Text/Pandoc/Writers/ICML.hs +++ b/src/Text/Pandoc/Writers/ICML.hs @@ -147,9 +147,9 @@ writeICML opts (Pandoc meta blocks) = do $ defField "parStyles" (render' $ parStylesToDoc st) $ defField "hyperlinks" (render' $ hyperlinksToDoc $ links st) $ metadata - return $ case writerTemplate opts of - Nothing -> main - Just tpl -> renderTemplate' tpl context + case writerTemplate opts of + Nothing -> return main + Just tpl -> renderTemplate' tpl context -- | Auxilary functions for parStylesToDoc and charStylesToDoc. contains :: String -> (String, (String, String)) -> [(String, String)] diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs index 11f3b0c22..012ff8416 100644 --- a/src/Text/Pandoc/Writers/JATS.hs +++ b/src/Text/Pandoc/Writers/JATS.hs @@ -128,9 +128,9 @@ docToJATS opts (Pandoc meta blocks) = do MathML -> True _ -> False) $ metadata - return $ case writerTemplate opts of - Nothing -> main - Just tpl -> renderTemplate' tpl context + case writerTemplate opts of + Nothing -> return main + Just tpl -> renderTemplate' tpl context -- | Convert an Element to JATS. elementToJATS :: PandocMonad m => WriterOptions -> Int -> Element -> DB m Doc diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 88ff454ce..53a67a27a 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -285,9 +285,9 @@ pandocToLaTeX options (Pandoc meta blocks) = do Just "rtl" -> True _ -> False) $ context - return $ case writerTemplate options of - Nothing -> main - Just tpl -> renderTemplate' tpl context' + case writerTemplate options of + Nothing -> return main + Just tpl -> renderTemplate' tpl context' -- | Convert Elements to LaTeX elementToLaTeX :: PandocMonad m => WriterOptions -> Element -> LW m Doc diff --git a/src/Text/Pandoc/Writers/Man.hs b/src/Text/Pandoc/Writers/Man.hs index d96342fb5..4e756c419 100644 --- a/src/Text/Pandoc/Writers/Man.hs +++ b/src/Text/Pandoc/Writers/Man.hs @@ -110,7 +110,7 @@ pandocToMan opts (Pandoc meta blocks) = do $ metadata case writerTemplate opts of Nothing -> return main - Just tpl -> return $ renderTemplate' tpl context + Just tpl -> renderTemplate' tpl context -- | Return man representation of notes. notesToMan :: PandocMonad m => WriterOptions -> [[Block]] -> StateT WriterState m Doc diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 4449bb5ce..8433f648f 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -228,7 +228,7 @@ pandocToMarkdown opts (Pandoc meta blocks) = do $ addVariablesToJSON opts metadata case writerTemplate opts of Nothing -> return main - Just tpl -> return $ renderTemplate' tpl context + Just tpl -> renderTemplate' tpl context -- | Return markdown representation of reference key table. refsToMarkdown :: PandocMonad m => WriterOptions -> Refs -> MD m Doc diff --git a/src/Text/Pandoc/Writers/MediaWiki.hs b/src/Text/Pandoc/Writers/MediaWiki.hs index 3825a4e73..58d1b0707 100644 --- a/src/Text/Pandoc/Writers/MediaWiki.hs +++ b/src/Text/Pandoc/Writers/MediaWiki.hs @@ -82,9 +82,8 @@ pandocToMediaWiki (Pandoc meta blocks) = do let main = body ++ notes let context = defField "body" main $ defField "toc" (writerTableOfContents opts) metadata - return $ pack - $ case writerTemplate opts of - Nothing -> main + pack <$> case writerTemplate opts of + Nothing -> return main Just tpl -> renderTemplate' tpl context -- | Escape special characters for MediaWiki. diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 0999d13db..493da1545 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -125,7 +125,7 @@ pandocToMs opts (Pandoc meta blocks) = do $ metadata case writerTemplate opts of Nothing -> return main - Just tpl -> return $ renderTemplate' tpl context + Just tpl -> renderTemplate' tpl context -- | Association list of characters to escape. msEscapes :: Map.Map Char String @@ -411,7 +411,8 @@ definitionListItemToMs opts (label, defs) = do let (first, rest) = case blocks of ((Para x):y) -> (Plain x,y) (x:y) -> (x,y) - [] -> error "blocks is null" + [] -> (Plain [], []) + -- should not happen rest' <- liftM vcat $ mapM (\item -> blockToMs opts item) rest first' <- blockToMs opts first diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index 286bd1431..3d9e232ae 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -101,7 +101,7 @@ pandocToMuse (Pandoc meta blocks) = do $ metadata case writerTemplate opts of Nothing -> return main - Just tpl -> return $ renderTemplate' tpl context + Just tpl -> renderTemplate' tpl context -- | Convert list of Pandoc block elements to Muse. blockListToMuse :: PandocMonad m diff --git a/src/Text/Pandoc/Writers/OPML.hs b/src/Text/Pandoc/Writers/OPML.hs index 4a0a317fa..52577ac17 100644 --- a/src/Text/Pandoc/Writers/OPML.hs +++ b/src/Text/Pandoc/Writers/OPML.hs @@ -60,9 +60,9 @@ writeOPML opts (Pandoc meta blocks) = do 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 + case writerTemplate opts of + Nothing -> return main + Just tpl -> renderTemplate' tpl context writeHtmlInlines :: PandocMonad m => [Inline] -> m Text diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index fd9a13f3e..95a800c94 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -221,9 +221,9 @@ writeOpenDocument opts (Pandoc meta blocks) = do let context = defField "body" body $ defField "automatic-styles" (render' automaticStyles) $ metadata - return $ case writerTemplate opts of - Nothing -> body - Just tpl -> renderTemplate' tpl context + case writerTemplate opts of + Nothing -> return body + Just tpl -> renderTemplate' tpl context withParagraphStyle :: PandocMonad m => WriterOptions -> String -> [Block] -> OD m Doc diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index 8524c441d..48f17c4fb 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -86,7 +86,7 @@ pandocToOrg (Pandoc meta blocks) = do $ metadata case writerTemplate opts of Nothing -> return main - Just tpl -> return $ renderTemplate' tpl context + Just tpl -> renderTemplate' tpl context -- | Return Org representation of notes. notesToOrg :: PandocMonad m => [[Block]] -> Org m Doc diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index 9c0693b0f..019c8335d 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -108,7 +108,7 @@ pandocToRST (Pandoc meta blocks) = do $ metadata case writerTemplate opts of Nothing -> return main - Just tpl -> return $ renderTemplate' tpl context + Just tpl -> renderTemplate' tpl context where normalizeHeadings lev (Header l a i:bs) = Header lev a i:normalizeHeadings (lev+1) cont ++ normalizeHeadings lev bs' diff --git a/src/Text/Pandoc/Writers/RTF.hs b/src/Text/Pandoc/Writers/RTF.hs index 5c990f324..6666f6549 100644 --- a/src/Text/Pandoc/Writers/RTF.hs +++ b/src/Text/Pandoc/Writers/RTF.hs @@ -125,10 +125,11 @@ writeRTF options doc = do then defField "toc" toc else id) $ metadata - return $ T.pack - $ case writerTemplate options of + T.pack <$> + case writerTemplate options of Just tpl -> renderTemplate' tpl context - Nothing -> case reverse body of + Nothing -> return $ + case reverse body of ('\n':_) -> body _ -> body ++ "\n" diff --git a/src/Text/Pandoc/Writers/TEI.hs b/src/Text/Pandoc/Writers/TEI.hs index 86a7415cf..26070966e 100644 --- a/src/Text/Pandoc/Writers/TEI.hs +++ b/src/Text/Pandoc/Writers/TEI.hs @@ -85,7 +85,7 @@ writeTEI opts (Pandoc meta blocks) = do $ metadata case writerTemplate opts of Nothing -> return main - Just tpl -> return $ renderTemplate' tpl context + Just tpl -> renderTemplate' tpl context -- | Convert an Element to TEI. elementToTEI :: PandocMonad m => WriterOptions -> Int -> Element -> m Doc diff --git a/src/Text/Pandoc/Writers/Texinfo.hs b/src/Text/Pandoc/Writers/Texinfo.hs index fd489786d..549d4f3d9 100644 --- a/src/Text/Pandoc/Writers/Texinfo.hs +++ b/src/Text/Pandoc/Writers/Texinfo.hs @@ -106,7 +106,7 @@ pandocToTexinfo options (Pandoc meta blocks) = do $ metadata case writerTemplate options of Nothing -> return body - Just tpl -> return $ renderTemplate' tpl context + Just tpl -> renderTemplate' tpl context -- | Escape things as needed for Texinfo. stringToTexinfo :: String -> String diff --git a/src/Text/Pandoc/Writers/Textile.hs b/src/Text/Pandoc/Writers/Textile.hs index 432c055b8..acc9eaa0f 100644 --- a/src/Text/Pandoc/Writers/Textile.hs +++ b/src/Text/Pandoc/Writers/Textile.hs @@ -75,7 +75,7 @@ pandocToTextile opts (Pandoc meta blocks) = do let context = defField "body" main metadata case writerTemplate opts of Nothing -> return main - Just tpl -> return $ renderTemplate' tpl context + Just tpl -> renderTemplate' tpl context withUseTags :: PandocMonad m => TW m a -> TW m a withUseTags action = do diff --git a/src/Text/Pandoc/Writers/ZimWiki.hs b/src/Text/Pandoc/Writers/ZimWiki.hs index ba51acfce..ced02d4be 100644 --- a/src/Text/Pandoc/Writers/ZimWiki.hs +++ b/src/Text/Pandoc/Writers/ZimWiki.hs @@ -78,7 +78,7 @@ pandocToZimWiki opts (Pandoc meta blocks) = do $ defField "toc" (writerTableOfContents opts) $ metadata case writerTemplate opts of - Just tpl -> return $ renderTemplate' tpl context + Just tpl -> renderTemplate' tpl context Nothing -> return main -- | Escape special characters for ZimWiki. -- cgit v1.2.3 From 6e6324badee219164bad271f3fcd037889962096 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 20 Jun 2017 22:44:09 +0200 Subject: Removed an 'error' bomb. --- src/Text/Pandoc/Readers/Markdown.hs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 96885c9b1..793ee0996 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -55,6 +55,7 @@ import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad(..), report) import Text.Pandoc.Definition import Text.Pandoc.Emoji (emojis) +import Text.Pandoc.Error import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (tableWith) @@ -368,7 +369,9 @@ parseMarkdown = do -- lookup to get sourcepos case M.lookup n (stateNotes' st) of Just (pos, _) -> report (NoteDefinedButNotUsed n pos) - Nothing -> error "The impossible happened.") notesDefined + Nothing -> throwError $ + PandocShouldNeverHappenError "note not found") + notesDefined let doc = runF (do Pandoc _ bs <- B.doc <$> blocks meta <- stateMeta' st return $ Pandoc meta bs) st -- cgit v1.2.3 From 242e2a064f6a32b22e1599bbfe72e64d7b6203b8 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 21 Jun 2017 23:54:16 +0200 Subject: Change default EPUB directory structure in OCF container. See #3720. We now put all EPUB related content in an EPUB/ subdirectory by default (later this will be configurable). mimetype META-INF/ com.apple.ibooks.display-options.xml container.xml EPUB/ <<--configurable-->> fonts/ <<--static-->> font.otf media/ <<--static-->> cover.jpg fig1.jpg styles/ <<--static-->> stylesheet.css content.opf toc.ncx text/ <<--static-->> ch001.xhtml --- src/Text/Pandoc/Writers/EPUB.hs | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index bd9a4c800..11ca7d168 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -80,6 +80,7 @@ data Chapter = Chapter (Maybe [Int]) [Block] data EPUBState = EPUBState { stMediaPaths :: [(FilePath, (FilePath, Maybe Entry))] + , stEPUBSubdir :: String } type E m = StateT EPUBState m @@ -362,6 +363,7 @@ writeEPUB :: PandocMonad m -> m B.ByteString writeEPUB epubVersion opts doc = let initState = EPUBState { stMediaPaths = [] + , stEPUBSubdir = "EPUB" } in evalStateT (pandocToEPUB epubVersion opts doc) @@ -373,6 +375,7 @@ pandocToEPUB :: PandocMonad m -> Pandoc -> E m B.ByteString pandocToEPUB version opts doc@(Pandoc meta _) = do + epubSubdir <- gets stEPUBSubdir let epub3 = version == EPUB3 let writeHtml o = fmap (UTF8.fromTextLazy . TL.fromStrict) . writeHtmlStringForEPUB version o @@ -383,10 +386,11 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do -- stylesheet stylesheets <- case epubStylesheets metadata of [] -> (\x -> [B.fromChunks [x]]) <$> - P.readDataFile (writerUserDataDir opts) "epub.css" + P.readDataFile (writerUserDataDir opts) + "epub.css" fs -> mapM P.readFileLazy fs let stylesheetEntries = zipWith - (\bs n -> mkEntry ("stylesheet" ++ show n ++ ".css") bs) + (\bs n -> mkEntry ("styles/stylesheet" ++ show n ++ ".css") bs) stylesheets [(1 :: Int)..] let vars = ("epub3", if epub3 then "true" else "false") @@ -431,7 +435,8 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do when (null xs) $ report $ CouldNotFetchResource f "glob did not match any font files" return xs - let mkFontEntry f = mkEntry (takeFileName f) `fmap` (lift $ P.readFileLazy f) + let mkFontEntry f = mkEntry ("fonts/" ++ takeFileName f) <$> + lift (P.readFileLazy f) fontFiles <- concat <$> mapM matchingGlob (writerEpubFonts opts') fontEntries <- mapM mkFontEntry fontFiles @@ -728,7 +733,8 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do unode "container" ! [("version","1.0") ,("xmlns","urn:oasis:names:tc:opendocument:xmlns:container")] $ unode "rootfiles" $ - unode "rootfile" ! [("full-path","content.opf") + unode "rootfile" ! [("full-path", + epubSubdir ++ ['/' | not (null epubSubdir)] ++ "content.opf") ,("media-type","application/oebps-package+xml")] $ () let containerEntry = mkEntry "META-INF/container.xml" containerData @@ -739,10 +745,14 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do unode "option" ! [("name","specified-fonts")] $ "true" let appleEntry = mkEntry "META-INF/com.apple.ibooks.display-options.xml" apple + let addEpubSubdir :: Entry -> Entry + addEpubSubdir e = e{ eRelativePath = + epubSubdir ++ ['/' | not (null epubSubdir)] ++ eRelativePath e } -- construct archive - let archive = foldr addEntryToArchive emptyArchive - (mimetypeEntry : containerEntry : appleEntry : tpEntry : - contentsEntry : tocEntry : navEntry : + let archive = foldr addEntryToArchive emptyArchive $ + [mimetypeEntry, containerEntry, appleEntry] ++ + map addEpubSubdir + (tpEntry : contentsEntry : tocEntry : navEntry : (stylesheetEntries ++ picEntries ++ cpicEntry ++ cpgEntry ++ chapterEntries ++ fontEntries)) return $ fromArchive archive @@ -878,15 +888,16 @@ modifyMediaRef :: PandocMonad m modifyMediaRef _ "" = return "" modifyMediaRef opts oldsrc = do media <- gets stMediaPaths + epubSubdir <- gets stEPUBSubdir case lookup oldsrc media of Just (n,_) -> return n Nothing -> catchError (do (img, mbMime) <- P.fetchItem (writerSourceURL opts) oldsrc let new = "media/file" ++ show (length media) ++ - fromMaybe (takeExtension (takeWhile (/='?') oldsrc)) - (('.':) <$> (mbMime >>= extensionFromMimeType)) + fromMaybe (takeExtension (takeWhile (/='?') oldsrc)) + (('.':) <$> (mbMime >>= extensionFromMimeType)) epochtime <- floor `fmap` lift P.getPOSIXTime - let entry = toEntry new epochtime $ B.fromChunks . (:[]) $ img + let entry = toEntry new epochtime (B.fromChunks . (:[]) $ img) modify $ \st -> st{ stMediaPaths = (oldsrc, (new, Just entry)):media} return new) @@ -952,7 +963,7 @@ mediaTypeOf x = -- Returns filename for chapter number. showChapter :: Int -> String -showChapter = printf "ch%03d.xhtml" +showChapter = printf "text/ch%03d.xhtml" -- Add identifiers to any headers without them. addIdentifiers :: [Block] -> [Block] -- cgit v1.2.3 From 379b99f63abe534c7666c8e16e0bf2e980b63d1c Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 22 Jun 2017 11:43:50 +0200 Subject: Added `writerEpubSubdirectory` to `WriterOptions`. [API change] The EPUB writer now takes its EPUB subdirectory from this option. Also added `PandocEpubSubdirectoryError` to `PandocError`. This is raised if the EPUB subdirectory is not all ASCII alphanumerics. See #3720. --- src/Text/Pandoc/Error.hs | 3 +++ src/Text/Pandoc/Options.hs | 2 ++ src/Text/Pandoc/Writers/EPUB.hs | 16 ++++++++-------- 3 files changed, 13 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs index 0056a1591..60bc699ab 100644 --- a/src/Text/Pandoc/Error.hs +++ b/src/Text/Pandoc/Error.hs @@ -63,6 +63,7 @@ data PandocError = PandocIOError String IOError | PandocResourceNotFound String | PandocTemplateError String | PandocAppError String + | PandocEpubSubdirectoryError String deriving (Show, Typeable, Generic) instance Exception PandocError @@ -104,6 +105,8 @@ handleError (Left e) = "File " ++ fn ++ " not found in resource path" PandocTemplateError s -> err 5 s PandocAppError s -> err 1 s + PandocEpubSubdirectoryError s -> err 31 $ + "EPUB subdirectory name '" ++ s ++ "' contains illegal characters" err :: Int -> String -> IO a err exitCode msg = do diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index c7211c86e..6519f807c 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -213,6 +213,7 @@ 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 + , writerEpubSubdirectory :: String -- ^ Subdir for epub in OCF , writerEpubMetadata :: Maybe String -- ^ Metadata to include in EPUB , writerEpubFonts :: [FilePath] -- ^ Paths to fonts to embed , writerEpubChapterLevel :: Int -- ^ Header level for chapters (separate files) @@ -249,6 +250,7 @@ instance Default WriterOptions where , writerListings = False , writerHighlightStyle = Just pygments , writerSetextHeaders = True + , writerEpubSubdirectory = "EPUB" , writerEpubMetadata = Nothing , writerEpubFonts = [] , writerEpubChapterLevel = 1 diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index 11ca7d168..96c8847df 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -34,14 +34,14 @@ Conversion of 'Pandoc' documents to EPUB. module Text.Pandoc.Writers.EPUB ( writeEPUB2, writeEPUB3 ) where import Codec.Archive.Zip (Entry, addEntryToArchive, eRelativePath, emptyArchive, fromArchive, fromEntry, toEntry) -import Control.Monad (mplus, when, zipWithM) +import Control.Monad (mplus, when, unless, zipWithM) import Control.Monad.Except (catchError, throwError) import Control.Monad.State.Strict (State, StateT, evalState, evalStateT, get, gets, lift, modify, put) import qualified Data.ByteString.Lazy as B import qualified Data.ByteString.Lazy.Char8 as B8 import qualified Data.Text.Lazy as TL -import Data.Char (isAlphaNum, isDigit, toLower) +import Data.Char (isAlphaNum, isDigit, toLower, isAscii) import Data.List (intercalate, isInfixOf, isPrefixOf) import qualified Data.Map as M import Data.Maybe (catMaybes, fromMaybe) @@ -80,7 +80,6 @@ data Chapter = Chapter (Maybe [Int]) [Block] data EPUBState = EPUBState { stMediaPaths :: [(FilePath, (FilePath, Maybe Entry))] - , stEPUBSubdir :: String } type E m = StateT EPUBState m @@ -362,9 +361,7 @@ writeEPUB :: PandocMonad m -> Pandoc -- ^ Document to convert -> m B.ByteString writeEPUB epubVersion opts doc = - let initState = EPUBState { stMediaPaths = [] - , stEPUBSubdir = "EPUB" - } + let initState = EPUBState { stMediaPaths = [] } in evalStateT (pandocToEPUB epubVersion opts doc) initState @@ -375,7 +372,10 @@ pandocToEPUB :: PandocMonad m -> Pandoc -> E m B.ByteString pandocToEPUB version opts doc@(Pandoc meta _) = do - epubSubdir <- gets stEPUBSubdir + let epubSubdir = writerEpubSubdirectory opts + -- sanity check on epubSubdir + unless (all (\c -> isAscii c && isAlphaNum c) epubSubdir) $ + throwError $ PandocEpubSubdirectoryError epubSubdir let epub3 = version == EPUB3 let writeHtml o = fmap (UTF8.fromTextLazy . TL.fromStrict) . writeHtmlStringForEPUB version o @@ -888,7 +888,7 @@ modifyMediaRef :: PandocMonad m modifyMediaRef _ "" = return "" modifyMediaRef opts oldsrc = do media <- gets stMediaPaths - epubSubdir <- gets stEPUBSubdir + let epubSubdir = writerEpubSubdirectory opts case lookup oldsrc media of Just (n,_) -> return n Nothing -> catchError -- cgit v1.2.3 From 24d215acf584a52ad3ea3a9a3f97c751d26e08a4 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 22 Jun 2017 12:01:33 +0200 Subject: Added `--epub-subdirectory` option. This specifies the subdirectory in the OCF container that holds the EPUB specific content. Closes #3720. --- src/Text/Pandoc/App.hs | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 7e9cfdd95..3c259fce7 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -355,6 +355,7 @@ convertWithOpts opts = do writerSlideLevel = optSlideLevel opts, writerHighlightStyle = highlightStyle, writerSetextHeaders = optSetextHeaders opts, + writerEpubSubdirectory = optEpubSubdirectory opts, writerEpubMetadata = epubMetadata, writerEpubFonts = optEpubFonts opts, writerEpubChapterLevel = optEpubChapterLevel opts, @@ -553,6 +554,7 @@ data Opt = Opt , optHTMLMathMethod :: HTMLMathMethod -- ^ Method to print HTML math , optAbbreviations :: Maybe FilePath -- ^ Path to abbrevs file , optReferenceDoc :: Maybe FilePath -- ^ Path of reference doc + , optEpubSubdirectory :: String -- ^ EPUB subdir in OCF container , optEpubMetadata :: Maybe FilePath -- ^ EPUB metadata , optEpubFonts :: [FilePath] -- ^ EPUB fonts to embed , optEpubChapterLevel :: Int -- ^ Header level at which to split chapters @@ -628,6 +630,7 @@ defaultOpts = Opt , optHTMLMathMethod = PlainMath , optAbbreviations = Nothing , optReferenceDoc = Nothing + , optEpubSubdirectory = "EPUB" , optEpubMetadata = Nothing , optEpubFonts = [] , optEpubChapterLevel = 1 @@ -1243,6 +1246,13 @@ options = "FILE") "" -- "Path of custom reference doc" + , Option "" ["epub-subdirectory"] + (ReqArg + (\arg opt -> + return opt { optEpubSubdirectory = arg }) + "DIRNAME") + "" -- "Name of subdirectory for epub content in OCF container" + , Option "" ["epub-cover-image"] (ReqArg (\arg opt -> -- cgit v1.2.3 From 2b3e8cb718d527640c237486d84cefa741221035 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 22 Jun 2017 12:38:08 +0200 Subject: EPUB writer: Fixed various things with new EPUB structure. --- src/Text/Pandoc/Writers/EPUB.hs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index 96c8847df..ab9f873c8 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -394,7 +394,7 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do stylesheets [(1 :: Int)..] let vars = ("epub3", if epub3 then "true" else "false") - : map (\e -> ("css", eRelativePath e)) stylesheetEntries + : map (\e -> ("css", "../" ++ eRelativePath e)) stylesheetEntries ++ [(x,y) | (x,y) <- writerVariables opts, x /= "css"] let opts' = opts{ writerEmailObfuscation = NoObfuscation , writerSectionDivs = True @@ -521,7 +521,7 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do chapters' let chapToEntry num (Chapter mbnum bs) = - mkEntry (showChapter num) <$> + mkEntry ("text/" ++ showChapter num) <$> (writeHtml opts'{ writerNumberOffset = fromMaybe [] mbnum } $ case bs of (Header _ _ xs : _) -> @@ -888,7 +888,6 @@ modifyMediaRef :: PandocMonad m modifyMediaRef _ "" = return "" modifyMediaRef opts oldsrc = do media <- gets stMediaPaths - let epubSubdir = writerEpubSubdirectory opts case lookup oldsrc media of Just (n,_) -> return n Nothing -> catchError @@ -924,12 +923,13 @@ transformInline :: PandocMonad m -> E m Inline transformInline opts (Image attr lab (src,tit)) = do newsrc <- modifyMediaRef opts src - return $ Image attr lab (newsrc, tit) + return $ Image attr lab ("../" ++ newsrc, tit) transformInline opts (x@(Math t m)) | WebTeX url <- writerHTMLMathMethod opts = do newsrc <- modifyMediaRef opts (url ++ urlEncode m) let mathclass = if t == DisplayMath then "display" else "inline" - return $ Span ("",["math",mathclass],[]) [Image nullAttr [x] (newsrc, "")] + return $ Span ("",["math",mathclass],[]) + [Image nullAttr [x] ("../" ++ newsrc, "")] transformInline opts (RawInline fmt raw) | fmt == Format "html" = do let tags = parseTags raw @@ -963,7 +963,7 @@ mediaTypeOf x = -- Returns filename for chapter number. showChapter :: Int -> String -showChapter = printf "text/ch%03d.xhtml" +showChapter = printf "ch%03d.xhtml" -- Add identifiers to any headers without them. addIdentifiers :: [Block] -> [Block] -- cgit v1.2.3 From 4a6868885d961b0df782c19f70dd725148446633 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 22 Jun 2017 12:42:21 +0200 Subject: EPUB writer: put title_page.xhtml in text/. --- src/Text/Pandoc/Writers/EPUB.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index ab9f873c8..d20eb8a2f 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -422,7 +422,7 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do tpContent <- lift $ writeHtml opts'{ writerVariables = ("titlepage","true"):vars } (Pandoc meta []) - let tpEntry = mkEntry "title_page.xhtml" tpContent + let tpEntry = mkEntry "text/title_page.xhtml" tpContent -- handle pictures -- mediaRef <- P.newIORef [] @@ -658,7 +658,7 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do let tpNode = unode "navPoint" ! [("id", "navPoint-0")] $ [ unode "navLabel" $ unode "text" (stringify $ docTitle' meta) - , unode "content" ! [("src","title_page.xhtml")] $ () ] + , unode "content" ! [("src","text/title_page.xhtml")] $ () ] navMap <- lift $ evalStateT (mapM (navPointNode navMapFormatter) secs) 1 let tocData = UTF8.fromStringLazy $ ppTopElement $ -- cgit v1.2.3 From 2b34337a9cf8b025914e8219498b4c0258772be0 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 22 Jun 2017 23:38:42 +0200 Subject: Text.Pandoc.Extensions: Added `Ext_raw_attribute`. Documented in MANUAL.txt. This is enabled by default in pandoc markdown and multimarkdown. --- src/Text/Pandoc/Extensions.hs | 4 ++++ src/Text/Pandoc/Readers/Markdown.hs | 42 +++++++++++++++++++++++++++++-------- 2 files changed, 37 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 58e8c414d..398944d47 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -94,6 +94,7 @@ data Extension = | Ext_fenced_code_attributes -- ^ Allow attributes on fenced code blocks | Ext_backtick_code_blocks -- ^ GitHub style ``` code blocks | Ext_inline_code_attributes -- ^ Allow attributes on inline code + | Ext_raw_attribute -- ^ Allow explicit raw blocks/inlines | Ext_markdown_in_html_blocks -- ^ Interpret as markdown inside HTML blocks | Ext_native_divs -- ^ Use Div blocks for contents of <div> tags | Ext_native_spans -- ^ Use Span inlines for contents of <span> @@ -162,6 +163,7 @@ pandocExtensions = extensionsFromList , Ext_fenced_code_attributes , Ext_backtick_code_blocks , Ext_inline_code_attributes + , Ext_raw_attribute , Ext_markdown_in_html_blocks , Ext_native_divs , Ext_native_spans @@ -275,6 +277,8 @@ multimarkdownExtensions = extensionsFromList , Ext_subscript , Ext_backtick_code_blocks , Ext_spaced_reference_links + -- So far only in dev version of mmd: + , Ext_raw_attribute ] -- | Language extensions to be used with strict markdown. diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 793ee0996..b91efcd8c 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -681,19 +681,36 @@ specialAttr = do char '-' return $ \(id',cs,kvs) -> (id',cs ++ ["unnumbered"],kvs) +rawAttribute :: PandocMonad m => MarkdownParser m String +rawAttribute = do + char '{' + skipMany spaceChar + char '=' + format <- many1 $ satisfy (\c -> isAlphaNum c || c `elem` "-_") + skipMany spaceChar + char '}' + return format + 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 '`')) size <- blockDelimiter (== c) Nothing skipMany spaceChar - attr <- option ([],[],[]) $ - try (guardEnabled Ext_fenced_code_attributes >> attributes) - <|> ((\x -> ("",[toLanguageId x],[])) <$> many1 nonspaceChar) + rawattr <- + (Left <$> try (guardEnabled Ext_raw_attribute >> rawAttribute)) + <|> + (Right <$> option ("",[],[]) + (try (guardEnabled Ext_fenced_code_attributes >> attributes) + <|> ((\x -> ("",[toLanguageId x],[])) <$> many1 nonspaceChar))) blankline - contents <- manyTill anyLine (blockDelimiter (== c) (Just size)) + contents <- intercalate "\n" <$> + manyTill anyLine (blockDelimiter (== c) (Just size)) blanklines - return $ return $ B.codeBlockWith attr $ intercalate "\n" contents + return $ return $ + case rawattr of + Left syn -> B.rawBlock syn contents + Right attr -> B.codeBlockWith attr contents -- correctly handle github language identifiers toLanguageId :: String -> String @@ -1516,13 +1533,20 @@ code :: PandocMonad m => MarkdownParser m (F Inlines) code = try $ do starts <- many1 (char '`') skipSpaces - result <- many1Till (many1 (noneOf "`\n") <|> many1 (char '`') <|> + result <- (trim . concat) <$> + many1Till (many1 (noneOf "`\n") <|> many1 (char '`') <|> (char '\n' >> notFollowedBy' blankline >> return " ")) (try (skipSpaces >> count (length starts) (char '`') >> notFollowedBy (char '`'))) - attr <- option ([],[],[]) (try $ guardEnabled Ext_inline_code_attributes - >> attributes) - return $ return $ B.codeWith attr $ trim $ concat result + rawattr <- + (Left <$> try (guardEnabled Ext_raw_attribute >> rawAttribute)) + <|> + (Right <$> option ("",[],[]) + (try (guardEnabled Ext_inline_code_attributes >> attributes))) + return $ return $ + case rawattr of + Left syn -> B.rawInline syn result + Right attr -> B.codeWith attr result math :: PandocMonad m => MarkdownParser m (F Inlines) math = (return . B.displayMath <$> (mathDisplay >>= applyMacros')) -- cgit v1.2.3 From da7d9ef295de3d51db97c4ff57104ae7d6e57e86 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 23 Jun 2017 11:51:26 +0200 Subject: HTML writer: make sure html4, html5 formats work for raw blocks/inlines. --- src/Text/Pandoc/Writers/HTML.hs | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 3687ca53b..45c878781 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -603,7 +603,8 @@ blockToHtml opts (Para lst) contents <- inlineListToHtml opts lst return $ H.p contents where - isEmptyRaw [RawInline f _] = f /= (Format "html") + isEmptyRaw [RawInline f _] = f `notElem` [Format "html", + Format "html4", Format "html5"] isEmptyRaw _ = False blockToHtml opts (LineBlock lns) = if writerWrapText opts == WrapNone @@ -632,14 +633,17 @@ blockToHtml opts (Div attr@(ident, classes, kvs) bs) = do NoSlides -> addAttrs opts' attr $ H.div $ contents' _ -> mempty else addAttrs opts (ident, classes', kvs) $ divtag $ contents' -blockToHtml opts (RawBlock f str) - | f == Format "html" = return $ preEscapedString str - | (f == Format "latex" || f == Format "tex") && - allowsMathEnvironments (writerHTMLMathMethod opts) && - isMathEnvironment str = blockToHtml opts $ Plain [Math DisplayMath str] - | otherwise = do - report $ BlockNotRendered (RawBlock f str) - return mempty +blockToHtml opts (RawBlock f str) = do + ishtml <- isRawHtml f + if ishtml + then return $ preEscapedString str + else if (f == Format "latex" || f == Format "tex") && + allowsMathEnvironments (writerHTMLMathMethod opts) && + isMathEnvironment str + then blockToHtml opts $ Plain [Math DisplayMath str] + else do + report $ BlockNotRendered (RawBlock f str) + return mempty blockToHtml _ (HorizontalRule) = do html5 <- gets stHtml5 return $ if html5 then H5.hr else H.hr @@ -977,11 +981,13 @@ inlineToHtml opts inline = do return $ case t of InlineMath -> m DisplayMath -> brtag >> m >> brtag - (RawInline f str) - | f == Format "html" -> return $ preEscapedString str - | otherwise -> do - report $ InlineNotRendered inline - return mempty + (RawInline f str) -> do + ishtml <- isRawHtml f + if ishtml + then return $ preEscapedString str + else do + report $ InlineNotRendered inline + return mempty (Link attr txt (s,_)) | "mailto:" `isPrefixOf` s -> do linkText <- inlineListToHtml opts txt lift $ obfuscateLink opts attr linkText s @@ -1129,3 +1135,9 @@ allowsMathEnvironments (MathJax _) = True allowsMathEnvironments (MathML) = True allowsMathEnvironments (WebTeX _) = True allowsMathEnvironments _ = False + +isRawHtml :: PandocMonad m => Format -> StateT WriterState m Bool +isRawHtml f = do + html5 <- gets stHtml5 + return $ f == Format "html" || + ((html5 && f == Format "html5") || f == Format "html4") -- cgit v1.2.3 From 57cc9a391c18977f229d7a5e15d0e9bcb861b684 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 23 Jun 2017 11:51:44 +0200 Subject: Markdown writer: make sure `plain`, `markdown_github`, etc. work for raw. Previously only `markdown` worked. Note: currently a raw block labeled `markdown_github` will be printed for any `markdown` format. --- src/Text/Pandoc/Writers/Markdown.hs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 8433f648f..6c7e662bf 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -433,8 +433,10 @@ blockToMarkdown' opts (LineBlock lns) = return $ (vcat $ map (hang 2 (text "| ")) mdLines) <> blankline else blockToMarkdown opts $ linesToPara lns blockToMarkdown' opts b@(RawBlock f str) - | f == "markdown" = return $ text str <> text "\n" - | f == "html" && isEnabled Ext_raw_html opts = do + | f `elem` ["markdown", "markdown_github", "markdown_phpextra", + "markdown_mmd", "markdown_strict"] + = return $ text str <> text "\n" + | f `elem` ["html", "html5", "html4"] && isEnabled Ext_raw_html opts = do plain <- asks envPlain return $ if plain then empty @@ -1053,10 +1055,12 @@ inlineToMarkdown opts (Math DisplayMath str) = (texMathToInlines DisplayMath str >>= inlineListToMarkdown opts) inlineToMarkdown opts il@(RawInline f str) = do plain <- asks envPlain - if not plain && - ( f == "markdown" || + if (plain && f == "plain") || (not plain && + ( f `elem` ["markdown", "markdown_github", "markdown_phpextra", + "markdown_mmd", "markdown_strict"] || (isEnabled Ext_raw_tex opts && (f == "latex" || f == "tex")) || - (isEnabled Ext_raw_html opts && f == "html") ) + (isEnabled Ext_raw_html opts && f `elem` ["html", "html4", "html5"]) + )) then return $ text str else do report $ InlineNotRendered il -- cgit v1.2.3 From 5812ac03902169e5ce8593c26fb2d8cffafbd828 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 23 Jun 2017 22:31:08 +0200 Subject: Markdown reader: interpret YAML metadata as Inlines when possible. If the metadata field is all on one line, we try to interpret it as Inlines, and only try parsing as Blocks if that fails. If it extends over one line (including possibly the `|` or `>` character signaling an indented block), then we parse as Blocks. This was motivated by some German users finding that date: '22. Juin 2017' got parsed as an ordered list. Closes #3755. --- src/Text/Pandoc/Readers/Markdown.hs | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index b91efcd8c..b3b275674 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -292,18 +292,19 @@ ignorable t = (T.pack "_") `T.isSuffixOf` t toMetaValue :: PandocMonad m => Text -> MarkdownParser m (F MetaValue) -toMetaValue x = toMeta <$> parseFromString' parseBlocks (T.unpack x) - where - toMeta p = do - p' <- p - return $ - case B.toList p' of - [Plain xs] -> MetaInlines xs - [Para xs] - | endsWithNewline x -> MetaBlocks [Para xs] - | otherwise -> MetaInlines xs - bs -> MetaBlocks bs - endsWithNewline t = T.pack "\n" `T.isSuffixOf` t +toMetaValue x = + parseFromString' parser' (T.unpack x) + where parser' = (asInlines <$> ((trimInlinesF . mconcat) + <$> (guard (not endsWithNewline) + *> manyTill inline eof))) + <|> (asBlocks <$> parseBlocks) + asBlocks p = do + p' <- p + return $ MetaBlocks (B.toList p') + asInlines p = do + p' <- p + return $ MetaInlines (B.toList p') + endsWithNewline = T.pack "\n" `T.isSuffixOf` x yamlToMeta :: PandocMonad m => Yaml.Value -> MarkdownParser m (F MetaValue) -- cgit v1.2.3 From a20302d9cfc3651a217bd76e9f9cce3e285433d4 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 23 Jun 2017 22:36:43 +0200 Subject: Added comment in source. --- src/Text/Pandoc/Readers/Markdown.hs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index b3b275674..31b51f237 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -305,6 +305,9 @@ toMetaValue x = p' <- p return $ MetaInlines (B.toList p') endsWithNewline = T.pack "\n" `T.isSuffixOf` x + -- Note: a standard quoted or unquoted YAML value will + -- not end in a newline, but a "block" set off with + -- `|` or `>` will. yamlToMeta :: PandocMonad m => Yaml.Value -> MarkdownParser m (F MetaValue) -- cgit v1.2.3 From a9259c1501cdfd0d0b5c0f95f36497da82befb50 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 24 Jun 2017 13:20:30 +0200 Subject: Extensions: Monoid instance for Extensions. [API change] --- src/Text/Pandoc/Extensions.hs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 398944d47..79e3529e9 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -45,7 +45,7 @@ module Text.Pandoc.Extensions ( Extension(..) , githubMarkdownExtensions , multimarkdownExtensions ) where -import Data.Bits (clearBit, setBit, testBit) +import Data.Bits (clearBit, setBit, testBit, (.|.)) import Data.Data (Data) import Data.Typeable (Typeable) import GHC.Generics (Generic) @@ -55,6 +55,10 @@ import Text.Parsec newtype Extensions = Extensions Integer deriving (Show, Read, Eq, Ord, Data, Typeable, Generic) +instance Monoid Extensions where + mempty = Extensions 0 + mappend (Extensions a) (Extensions b) = Extensions (a .|. b) + extensionsFromList :: [Extension] -> Extensions extensionsFromList = foldr enableExtension emptyExtensions -- cgit v1.2.3 From 743419af5c0872d8e4b5fdf53d47080e8648b4a7 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 24 Jun 2017 13:47:10 +0200 Subject: Readers.getReader, Writers.getWriter API change. Now these functions return a pair of a reader/writer and an Extensions, instead of building the extensions into the reader/writer. The calling code must explicitly set readerExtensions or writerExtensions using the Extensions returned. The point of the change is to make it possible for the calling code to determine what extensions are being used. See #3659. --- src/Text/Pandoc/App.hs | 14 +++++++++----- src/Text/Pandoc/Lua/PandocModule.hs | 5 +++-- src/Text/Pandoc/Readers.hs | 12 ++++-------- src/Text/Pandoc/Writers.hs | 11 ++++------- 4 files changed, 20 insertions(+), 22 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 3c259fce7..ee74d39c0 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -182,11 +182,12 @@ convertWithOpts opts = do let msOutput = format == "ms" -- disabling the custom writer for now - writer <- if ".lua" `isSuffixOf` format + (writer, writerExts) <- + if ".lua" `isSuffixOf` format -- note: use non-lowercased version writerName then return (TextWriter (\o d -> liftIO $ writeCustom writerName o d) - :: Writer PandocIO) + :: Writer PandocIO, mempty) else case getWriter writerName of Left e -> E.throwIO $ PandocAppError $ if format == "pdf" @@ -196,12 +197,13 @@ convertWithOpts opts = do "\nand specify an output file with " ++ ".pdf extension (-o filename.pdf)." else e - Right w -> return (w :: Writer PandocIO) + Right (w, es) -> return (w :: Writer PandocIO, es) -- TODO: we have to get the input and the output into the state for -- the sake of the text2tags reader. - reader <- case getReader readerName of - Right r -> return (r :: Reader PandocIO) + (reader, readerExts) <- + case getReader readerName of + Right (r, es) -> return (r :: Reader PandocIO, es) Left e -> E.throwIO $ PandocAppError e' where e' = case readerName of "pdf" -> e ++ @@ -310,6 +312,7 @@ convertWithOpts opts = do optDefaultImageExtension opts , readerTrackChanges = optTrackChanges opts , readerAbbreviations = abbrevs + , readerExtensions = readerExts } highlightStyle <- lookupHighlightStyle $ optHighlightStyle opts @@ -340,6 +343,7 @@ convertWithOpts opts = do writerNumberSections = optNumberSections opts, writerNumberOffset = optNumberOffset opts, writerSectionDivs = optSectionDivs opts, + writerExtensions = writerExts, writerReferenceLinks = optReferenceLinks opts, writerReferenceLocation = optReferenceLocation opts, writerDpi = optDpi opts, diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index 27c19d4f0..fccfbebf3 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -34,6 +34,7 @@ import Data.Text (pack) import Scripting.Lua (LuaState, call, push, pushhsfunction, rawset) import Text.Pandoc.Class hiding (readDataFile) import Text.Pandoc.Definition (Pandoc) +import Text.Pandoc.Options (ReaderOptions(readerExtensions)) import Text.Pandoc.Lua.Compat (loadstring) import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Readers (Reader (..), getReader) @@ -57,10 +58,10 @@ read_doc :: String -> String -> IO (Either String Pandoc) read_doc formatSpec content = do case getReader formatSpec of Left s -> return $ Left s - Right reader -> + Right (reader, es) -> case reader of TextReader r -> do - res <- runIO $ r def (pack content) + res <- runIO $ r def{ readerExtensions = es } (pack content) case res of Left s -> return . Left $ show s Right pd -> return $ Right pd diff --git a/src/Text/Pandoc/Readers.hs b/src/Text/Pandoc/Readers.hs index 20e503a7e..0374d27d5 100644 --- a/src/Text/Pandoc/Readers.hs +++ b/src/Text/Pandoc/Readers.hs @@ -133,20 +133,16 @@ readers = [ ("native" , TextReader readNative) ,("muse" , TextReader readMuse) ] --- | Retrieve reader based on formatSpec (format+extensions). -getReader :: PandocMonad m => String -> Either String (Reader m) +-- | Retrieve reader, extensions based on formatSpec (format+extensions). +getReader :: PandocMonad m => String -> Either String (Reader m, Extensions) getReader s = case parseFormatSpec s of Left e -> Left $ intercalate "\n" [m | Message m <- errorMessages e] Right (readerName, setExts) -> case lookup readerName readers of Nothing -> Left $ "Unknown reader: " ++ readerName - Just (TextReader r) -> Right $ TextReader $ \o -> - r o{ readerExtensions = setExts $ - getDefaultExtensions readerName } - Just (ByteStringReader r) -> Right $ ByteStringReader $ \o -> - r o{ readerExtensions = setExts $ - getDefaultExtensions readerName } + Just r -> Right (r, setExts $ + getDefaultExtensions readerName) -- | Read pandoc document from JSON format. readJSON :: ReaderOptions -> Text -> Either PandocError Pandoc diff --git a/src/Text/Pandoc/Writers.hs b/src/Text/Pandoc/Writers.hs index dbe55449f..6dfc1a7b3 100644 --- a/src/Text/Pandoc/Writers.hs +++ b/src/Text/Pandoc/Writers.hs @@ -176,19 +176,16 @@ writers = [ ,("muse" , TextWriter writeMuse) ] -getWriter :: PandocMonad m => String -> Either String (Writer m) +-- | Retrieve writer, extensions based on formatSpec (format+extensions). +getWriter :: PandocMonad m => String -> Either String (Writer m, Extensions) 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 (TextWriter r) -> Right $ TextWriter $ - \o -> r o{ writerExtensions = setExts $ - getDefaultExtensions writerName } - Just (ByteStringWriter r) -> Right $ ByteStringWriter $ - \o -> r o{ writerExtensions = setExts $ - getDefaultExtensions writerName } + Just r -> Right (r, setExts $ + getDefaultExtensions writerName) writeJSON :: WriterOptions -> Pandoc -> Text writeJSON _ = UTF8.toText . BL.toStrict . encode -- cgit v1.2.3 From f8877516e03ec678aeb735cdafe56e20ca52b235 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sun, 25 Jun 2017 11:01:43 +0300 Subject: Muse reader: Require space before and after '=' for code (#3758) --- src/Text/Pandoc/Readers/Muse.hs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 7eee064a7..fe8a55f5c 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -442,8 +442,7 @@ tableParseCaption = try $ do -- inline :: PandocMonad m => MuseParser m (F Inlines) -inline = choice [ whitespace - , br +inline = choice [ br , footnote , strong , strongTag @@ -455,6 +454,7 @@ inline = choice [ whitespace , link , code , codeTag + , whitespace , str , symbol ] <?> "inline" @@ -535,7 +535,14 @@ strikeoutTag :: PandocMonad m => MuseParser m (F Inlines) strikeoutTag = inlineTag B.strikeout "del" code :: PandocMonad m => MuseParser m (F Inlines) -code = return . B.code <$> verbatimBetween '=' +code = try $ do + pos <- getPosition + sp <- if sourceColumn pos == 1 + then pure mempty + else skipMany1 spaceChar >> pure B.space + cd <- verbatimBetween '=' + notFollowedBy nonspaceChar + return $ return (sp B.<> B.code cd) codeTag :: PandocMonad m => MuseParser m (F Inlines) codeTag = do -- cgit v1.2.3 From 87e6235fe73fef3fd6bc6e374c9372b2254d2764 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 25 Jun 2017 10:37:49 +0200 Subject: Text.Pandoc.Writers.Shared: added getLang. --- src/Text/Pandoc/Writers/Shared.hs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index 2047285eb..6f7c9f75c 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -29,7 +29,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Shared utility functions for pandoc writers. -} module Text.Pandoc.Writers.Shared ( - metaToJSON + getLang + , metaToJSON , metaToJSON' , addVariablesToJSON , getField @@ -42,7 +43,7 @@ module Text.Pandoc.Writers.Shared ( , gridTable ) where -import Control.Monad (liftM, zipWithM) +import Control.Monad (liftM, zipWithM, mplus) import Data.Aeson (FromJSON (..), Result (..), ToJSON (..), Value (Object), encode, fromJSON) import qualified Data.HashMap.Strict as H @@ -57,6 +58,16 @@ import Text.Pandoc.Pretty import Text.Pandoc.UTF8 (toStringLazy) import Text.Pandoc.XML (escapeStringForXML) +-- | Get the contents of the `lang` metadata field or variable. +getLang :: WriterOptions -> Meta -> Maybe String +getLang opts meta = + lookup "lang" (writerVariables opts) + `mplus` + case lookupMeta "lang" meta of + Just (MetaInlines [Str s]) -> Just s + Just (MetaString s) -> Just s + _ -> Nothing + -- | Create JSON value for template from a 'Meta' and an association list -- of variables, specified at the command line or in the writer. -- Variables overwrite metadata fields with the same names. -- cgit v1.2.3 From 0c993a6c7b73bfd10c795f45abfed32772790999 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 25 Jun 2017 12:45:25 +0200 Subject: Text.Pandoc.Writers.Shared: export splitLang. --- src/Text/Pandoc/Writers/Shared.hs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index 6f7c9f75c..0b35d27f6 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -30,6 +30,7 @@ Shared utility functions for pandoc writers. -} module Text.Pandoc.Writers.Shared ( getLang + , splitLang , metaToJSON , metaToJSON' , addVariablesToJSON @@ -53,8 +54,11 @@ import Data.Maybe (isJust) import qualified Data.Text as T import qualified Data.Traversable as Traversable import Text.Pandoc.Definition +import Text.Pandoc.Class (PandocMonad, report) +import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Pretty +import Text.Pandoc.Shared (splitBy) import Text.Pandoc.UTF8 (toStringLazy) import Text.Pandoc.XML (escapeStringForXML) @@ -68,6 +72,21 @@ getLang opts meta = Just (MetaString s) -> Just s _ -> Nothing +-- | Split `lang` field into lang and country, issuing warning +-- if it doesn't look valid. +splitLang :: PandocMonad m => String -> m (Maybe String, Maybe String) +splitLang lang = + case splitBy (== '-') lang of + [la,co] + | length la == 2 && length co == 2 + -> return (Just la, Just co) + [la] + | length la == 2 + -> return (Just la, Nothing) + _ -> do + report $ InvalidLang lang + return (Nothing, Nothing) + -- | Create JSON value for template from a 'Meta' and an association list -- of variables, specified at the command line or in the writer. -- Variables overwrite metadata fields with the same names. -- cgit v1.2.3 From a02f08c9fc608727da0ac3b65b39f627e8bb2033 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 25 Jun 2017 12:45:42 +0200 Subject: Added InvalidLang to LogMessage. --- src/Text/Pandoc/Logging.hs | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs index 4090243ea..ad0fcdd2d 100644 --- a/src/Text/Pandoc/Logging.hs +++ b/src/Text/Pandoc/Logging.hs @@ -90,6 +90,7 @@ data LogMessage = | Extracting String | NoTitleElement String | NoLangSpecified + | InvalidLang String | CouldNotHighlight String | MissingCharacter String deriving (Show, Eq, Data, Ord, Typeable, Generic) @@ -178,6 +179,8 @@ instance ToJSON LogMessage where NoTitleElement fallback -> ["fallback" .= Text.pack fallback] NoLangSpecified -> [] + InvalidLang s -> + ["lang" .= Text.pack s] CouldNotHighlight msg -> ["message" .= Text.pack msg] MissingCharacter msg -> @@ -254,6 +257,9 @@ showLogMessage msg = NoLangSpecified -> "No value for 'lang' was specified in the metadata.\n" ++ "It is recommended that lang be specified for this format." + InvalidLang s -> + "Invalid 'lang' value '" ++ s ++ "'.\n" ++ + "Use ISO 8601 format like 'en-US'." CouldNotHighlight m -> "Could not highlight code block:\n" ++ m MissingCharacter m -> @@ -285,5 +291,6 @@ messageVerbosity msg = Extracting{} -> INFO NoTitleElement{} -> WARNING NoLangSpecified -> INFO + InvalidLang{} -> WARNING CouldNotHighlight{} -> WARNING MissingCharacter{} -> WARNING -- cgit v1.2.3 From 083a224d1e3d791c459a998d18aa9975b8816c15 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 25 Jun 2017 10:38:11 +0200 Subject: Support `lang` attribute in OpenDocument and ODT writers. This adds the required attributes to the temporary styles, and also replaces existing language attributes in styles.xml. Support for lang attributes on Div and Span has also been added. Closes #1667. --- src/Text/Pandoc/Writers/Docx.hs | 7 ++--- src/Text/Pandoc/Writers/ODT.hs | 48 ++++++++++++++++++++++++++++----- src/Text/Pandoc/Writers/OpenDocument.hs | 35 +++++++++++++++++++----- 3 files changed, 72 insertions(+), 18 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index b488f2479..d93b99486 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -67,7 +67,7 @@ import Text.Pandoc.Shared hiding (Element) import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Walk import Text.Pandoc.Writers.Math -import Text.Pandoc.Writers.Shared (fixDisplayMath) +import Text.Pandoc.Writers.Shared (fixDisplayMath, getLang) import Text.Printf (printf) import Text.TeXMath import Text.XML.Light as XML @@ -257,10 +257,7 @@ writeDocx opts doc@(Pandoc meta _) = do ) -- styles - let lang = case lookupMeta "lang" meta of - Just (MetaInlines [Str s]) -> Just s - Just (MetaString s) -> Just s - _ -> Nothing + let lang = getLang opts meta let addLang :: Element -> Element addLang e = case lang >>= \l -> (return . XMLC.toTree . go l . XMLC.fromElement) e of Just (Elem e') -> e' diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index c9a7de642..dff4f8fcf 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -33,6 +33,7 @@ import Codec.Archive.Zip import Control.Monad.Except (catchError) import Control.Monad.State.Strict import qualified Data.ByteString.Lazy as B +import Data.Generics (everywhere', mkT) import Data.List (isPrefixOf) import Data.Maybe (fromMaybe) import qualified Data.Text.Lazy as TL @@ -46,13 +47,13 @@ import Text.Pandoc.MIME (extensionFromMimeType, getMimeType) import Text.Pandoc.Options (WrapOption (..), WriterOptions (..)) import Text.Pandoc.Pretty import Text.Pandoc.Shared (stringify) -import Text.Pandoc.UTF8 (fromStringLazy, fromTextLazy) +import Text.Pandoc.UTF8 (fromStringLazy, fromTextLazy, toStringLazy) import Text.Pandoc.Walk import Text.Pandoc.Writers.OpenDocument (writeOpenDocument) -import Text.Pandoc.Writers.Shared (fixDisplayMath) +import Text.Pandoc.Writers.Shared (fixDisplayMath, getLang, splitLang) import Text.Pandoc.XML import Text.TeXMath -import Text.XML.Light.Output +import Text.XML.Light data ODTState = ODTState { stEntries :: [Entry] } @@ -78,6 +79,7 @@ pandocToODT :: PandocMonad m pandocToODT opts doc@(Pandoc meta _) = do let datadir = writerUserDataDir opts let title = docTitle meta + let lang = getLang opts meta refArchive <- case writerReferenceDoc opts of Just f -> liftM toArchive $ lift $ P.readFileLazy f @@ -132,18 +134,50 @@ pandocToODT opts doc@(Pandoc meta _) = do ,("xmlns:ooo","http://openoffice.org/2004/office") ,("xmlns:grddl","http://www.w3.org/2003/g/data-view#") ,("office:version","1.2")] - $ ( inTagsSimple "office:meta" - $ ( inTagsSimple "dc:title" (text $ escapeStringForXML (stringify title)) - ) + $ ( inTagsSimple "office:meta" $ + ( inTagsSimple "dc:title" + (text $ escapeStringForXML (stringify title)) + $$ + case lang of + Just l -> inTagsSimple "dc:language" + (text (escapeStringForXML l)) + Nothing -> empty + ) ) ) -- make sure mimetype is first let mimetypeEntry = toEntry "mimetype" epochtime $ fromStringLazy "application/vnd.oasis.opendocument.text" - let archive'' = addEntryToArchive mimetypeEntry + archive'' <- updateStyleWithLang lang + $ addEntryToArchive mimetypeEntry $ addEntryToArchive metaEntry archive' return $ fromArchive archive'' +updateStyleWithLang :: PandocMonad m => Maybe String -> Archive -> O m Archive +updateStyleWithLang Nothing arch = return arch +updateStyleWithLang (Just l) arch = do + (mblang, mbcountry) <- splitLang l + epochtime <- floor `fmap` (lift P.getPOSIXTime) + return arch{ zEntries = [if eRelativePath e == "styles.xml" + then case parseXMLDoc + (toStringLazy (fromEntry e)) of + Nothing -> e + Just d -> + toEntry "styles.xml" epochtime + ( fromStringLazy + . ppTopElement + . addLang mblang mbcountry $ d ) + else e + | e <- zEntries arch] } + +addLang :: Maybe String -> Maybe String -> Element -> Element +addLang mblang mbcountry = everywhere' (mkT updateLangAttr) + where updateLangAttr (Attr n@(QName "language" _ (Just "fo")) l) + = Attr n (maybe l id mblang) + updateLangAttr (Attr n@(QName "country" _ (Just "fo")) c) + = Attr n (maybe c id mbcountry) + updateLangAttr x = x + -- | transform both Image and Math elements transformPicMath :: PandocMonad m => WriterOptions ->Inline -> O m Inline transformPicMath opts (Image attr@(id', cls, _) lab (src,t)) = catchError diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index 95a800c94..a4c9e0ef2 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -45,7 +45,7 @@ import Text.Pandoc.Definition import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Pretty -import Text.Pandoc.Shared (linesToPara) +import Text.Pandoc.Shared (linesToPara, splitBy) import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Writers.Math import Text.Pandoc.Writers.Shared @@ -75,6 +75,8 @@ data WriterState = , stTight :: Bool , stFirstPara :: Bool , stImageId :: Int + , stLang :: Maybe String + , stCountry :: Maybe String } defaultWriterState :: WriterState @@ -90,6 +92,8 @@ defaultWriterState = , stTight = False , stFirstPara = False , stImageId = 1 + , stLang = Nothing + , stCountry = Nothing } when :: Bool -> Doc -> Doc @@ -155,6 +159,10 @@ withTextStyle s f = do inTextStyle :: PandocMonad m => Doc -> OD m Doc inTextStyle d = do at <- gets stTextStyleAttr + mblang <- gets stLang + mbcountry <- gets stCountry + let langat = maybe [] (\la -> [("fo:language", la)]) mblang + let countryat = maybe [] (\co -> [("fo:country", co)]) mbcountry if Set.null at then return d else do @@ -168,8 +176,9 @@ inTextStyle d = do inTags False "style:style" [("style:name", styleName) ,("style:family", "text")] - $ selfClosingTag "style:text-properties" - (concatMap textStyleAttr (Set.toList at))) + $ selfClosingTag "style:text-properties" + (langat ++ countryat ++ + concatMap textStyleAttr (Set.toList at))) return $ inTags False "text:span" [("text:style-name",styleName)] d @@ -203,8 +212,10 @@ writeOpenDocument opts (Pandoc meta blocks) = do else Nothing let render' :: Doc -> Text render' = render colwidth + let lang = getLang opts meta + (mblang, mbcountry) <- maybe (return (Nothing, Nothing)) splitLang lang ((body, metadata),s) <- flip runStateT - defaultWriterState $ do + defaultWriterState{ stLang = mblang, stCountry = mbcountry } $ do m <- metaToJSON opts (fmap render' . blocksToOpenDocument opts) (fmap render' . inlinesToOpenDocument opts) @@ -326,7 +337,8 @@ blockToOpenDocument o bs then return empty else inParagraphTags =<< inlinesToOpenDocument o b | LineBlock b <- bs = blockToOpenDocument o $ linesToPara b - | Div _ xs <- bs = blocksToOpenDocument o xs + | Div attr xs <- bs = withLangFromAttr attr + (blocksToOpenDocument o xs) | Header i _ b <- bs = setFirstPara >> (inHeaderTags i =<< inlinesToOpenDocument o b) | BlockQuote b <- bs = setFirstPara >> mkBlockQuote b @@ -444,7 +456,7 @@ inlineToOpenDocument o ils | writerWrapText o == WrapPreserve -> return $ preformatted "\n" | otherwise -> return $ space - Span _ xs -> inlinesToOpenDocument o xs + Span attr xs -> withLangFromAttr attr (inlinesToOpenDocument o xs) LineBreak -> return $ selfClosingTag "text:line-break" [] Str s -> return $ handleSpaces $ escapeStringForXML s Emph l -> withTextStyle Italic $ inlinesToOpenDocument o l @@ -625,3 +637,14 @@ textStyleAttr s ,("style:font-name-asian" ,"Courier New") ,("style:font-name-complex" ,"Courier New")] | otherwise = [] + +withLangFromAttr :: PandocMonad m => Attr -> OD m a -> OD m a +withLangFromAttr (_,_,kvs) action = do + oldlang <- gets stLang + case lookup "lang" kvs of + Nothing -> action + Just l -> do + modify (\st -> st{ stLang = Just l}) + result <- action + modify (\st -> st{ stLang = oldlang}) + return result -- cgit v1.2.3 From 3ae4105d143dbec44afa713f6c3fa28f7a8c1d1f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 25 Jun 2017 10:38:11 +0200 Subject: Fixed support for `lang` attribute in OpenDocument and ODT writers. This improves on the last commit, which didn't work in some important ways. See #1667. --- src/Text/Pandoc/Writers/OpenDocument.hs | 35 ++++++++++++++------------------- 1 file changed, 15 insertions(+), 20 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index a4c9e0ef2..3a720acdc 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -36,6 +36,7 @@ import Control.Arrow ((***), (>>>)) import Control.Monad.State.Strict hiding (when) import Data.Char (chr) import Data.List (sortBy) +import Data.Maybe (fromMaybe) import Data.Text (Text) import qualified Data.Map as Map import Data.Ord (comparing) @@ -45,7 +46,7 @@ import Text.Pandoc.Definition import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Pretty -import Text.Pandoc.Shared (linesToPara, splitBy) +import Text.Pandoc.Shared (linesToPara) import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Writers.Math import Text.Pandoc.Writers.Shared @@ -75,8 +76,6 @@ data WriterState = , stTight :: Bool , stFirstPara :: Bool , stImageId :: Int - , stLang :: Maybe String - , stCountry :: Maybe String } defaultWriterState :: WriterState @@ -92,8 +91,6 @@ defaultWriterState = , stTight = False , stFirstPara = False , stImageId = 1 - , stLang = Nothing - , stCountry = Nothing } when :: Bool -> Doc -> Doc @@ -159,10 +156,6 @@ withTextStyle s f = do inTextStyle :: PandocMonad m => Doc -> OD m Doc inTextStyle d = do at <- gets stTextStyleAttr - mblang <- gets stLang - mbcountry <- gets stCountry - let langat = maybe [] (\la -> [("fo:language", la)]) mblang - let countryat = maybe [] (\co -> [("fo:country", co)]) mbcountry if Set.null at then return d else do @@ -177,8 +170,7 @@ inTextStyle d = do [("style:name", styleName) ,("style:family", "text")] $ selfClosingTag "style:text-properties" - (langat ++ countryat ++ - concatMap textStyleAttr (Set.toList at))) + (concatMap textStyleAttr (Set.toList at))) return $ inTags False "text:span" [("text:style-name",styleName)] d @@ -212,10 +204,8 @@ writeOpenDocument opts (Pandoc meta blocks) = do else Nothing let render' :: Doc -> Text render' = render colwidth - let lang = getLang opts meta - (mblang, mbcountry) <- maybe (return (Nothing, Nothing)) splitLang lang ((body, metadata),s) <- flip runStateT - defaultWriterState{ stLang = mblang, stCountry = mbcountry } $ do + defaultWriterState $ do m <- metaToJSON opts (fmap render' . blocksToOpenDocument opts) (fmap render' . inlinesToOpenDocument opts) @@ -619,6 +609,7 @@ paraTableStyles t s (a:xs) , ("style:justify-single-word", "false")] data TextStyle = Italic | Bold | Strike | Sub | Sup | SmallC | Pre + | Lang String String deriving ( Eq,Ord ) textStyleAttr :: TextStyle -> [(String,String)] @@ -636,15 +627,19 @@ textStyleAttr s | Pre <- s = [("style:font-name" ,"Courier New") ,("style:font-name-asian" ,"Courier New") ,("style:font-name-complex" ,"Courier New")] + | Lang lang country <- s + = [("fo:language" ,lang) + ,("fo:country" ,country)] | otherwise = [] withLangFromAttr :: PandocMonad m => Attr -> OD m a -> OD m a -withLangFromAttr (_,_,kvs) action = do - oldlang <- gets stLang +withLangFromAttr (_,_,kvs) action = case lookup "lang" kvs of Nothing -> action Just l -> do - modify (\st -> st{ stLang = Just l}) - result <- action - modify (\st -> st{ stLang = oldlang}) - return result + (mblang, mbcountry) <- splitLang l + case (mblang, mbcountry) of + (Just lang, _) -> withTextStyle + (Lang lang (fromMaybe "" mbcountry)) + action + _ -> action -- cgit v1.2.3 From e7cd3cb4668b119b61eb69eed857b0254a614ad9 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 25 Jun 2017 15:36:30 +0200 Subject: Writers.Shared: refactored getLang, splitLang... into `Lang(..)`, `getLang`, `parceBCP47`. --- src/Text/Pandoc/Writers/Docx.hs | 8 +++++--- src/Text/Pandoc/Writers/ODT.hs | 26 ++++++++++++------------ src/Text/Pandoc/Writers/OpenDocument.hs | 22 ++++++++++++--------- src/Text/Pandoc/Writers/Shared.hs | 35 ++++++++++++++++++++++----------- 4 files changed, 55 insertions(+), 36 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index d93b99486..52ababb14 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -67,7 +67,7 @@ import Text.Pandoc.Shared hiding (Element) import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Walk import Text.Pandoc.Writers.Math -import Text.Pandoc.Writers.Shared (fixDisplayMath, getLang) +import Text.Pandoc.Writers.Shared (fixDisplayMath, getLang, renderLang) import Text.Printf (printf) import Text.TeXMath import Text.XML.Light as XML @@ -257,9 +257,11 @@ writeDocx opts doc@(Pandoc meta _) = do ) -- styles - let lang = getLang opts meta + lang <- getLang opts meta let addLang :: Element -> Element - addLang e = case lang >>= \l -> (return . XMLC.toTree . go l . XMLC.fromElement) e of + addLang e = case lang >>= \l -> + (return . XMLC.toTree . go (renderLang l) + . XMLC.fromElement) e of Just (Elem e') -> e' _ -> e -- return original where go :: String -> Cursor -> Cursor diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index dff4f8fcf..8573f5719 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -50,7 +50,8 @@ import Text.Pandoc.Shared (stringify) import Text.Pandoc.UTF8 (fromStringLazy, fromTextLazy, toStringLazy) import Text.Pandoc.Walk import Text.Pandoc.Writers.OpenDocument (writeOpenDocument) -import Text.Pandoc.Writers.Shared (fixDisplayMath, getLang, splitLang) +import Text.Pandoc.Writers.Shared (fixDisplayMath, getLang, Lang(..), + renderLang) import Text.Pandoc.XML import Text.TeXMath import Text.XML.Light @@ -79,7 +80,7 @@ pandocToODT :: PandocMonad m pandocToODT opts doc@(Pandoc meta _) = do let datadir = writerUserDataDir opts let title = docTitle meta - let lang = getLang opts meta + lang <- getLang opts meta refArchive <- case writerReferenceDoc opts of Just f -> liftM toArchive $ lift $ P.readFileLazy f @@ -140,7 +141,7 @@ pandocToODT opts doc@(Pandoc meta _) = do $$ case lang of Just l -> inTagsSimple "dc:language" - (text (escapeStringForXML l)) + (text (escapeStringForXML (renderLang l))) Nothing -> empty ) ) @@ -153,10 +154,9 @@ pandocToODT opts doc@(Pandoc meta _) = do $ addEntryToArchive metaEntry archive' return $ fromArchive archive'' -updateStyleWithLang :: PandocMonad m => Maybe String -> Archive -> O m Archive +updateStyleWithLang :: PandocMonad m => Maybe Lang -> Archive -> O m Archive updateStyleWithLang Nothing arch = return arch -updateStyleWithLang (Just l) arch = do - (mblang, mbcountry) <- splitLang l +updateStyleWithLang (Just lang) arch = do epochtime <- floor `fmap` (lift P.getPOSIXTime) return arch{ zEntries = [if eRelativePath e == "styles.xml" then case parseXMLDoc @@ -166,16 +166,16 @@ updateStyleWithLang (Just l) arch = do toEntry "styles.xml" epochtime ( fromStringLazy . ppTopElement - . addLang mblang mbcountry $ d ) + . addLang lang $ d ) else e | e <- zEntries arch] } -addLang :: Maybe String -> Maybe String -> Element -> Element -addLang mblang mbcountry = everywhere' (mkT updateLangAttr) - where updateLangAttr (Attr n@(QName "language" _ (Just "fo")) l) - = Attr n (maybe l id mblang) - updateLangAttr (Attr n@(QName "country" _ (Just "fo")) c) - = Attr n (maybe c id mbcountry) +addLang :: Lang -> Element -> Element +addLang (Lang lang country) = everywhere' (mkT updateLangAttr) + where updateLangAttr (Attr n@(QName "language" _ (Just "fo")) _) + = Attr n lang + updateLangAttr (Attr n@(QName "country" _ (Just "fo")) _) + = Attr n country updateLangAttr x = x -- | transform both Image and Math elements diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index 3a720acdc..57f3c1194 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -36,7 +36,6 @@ import Control.Arrow ((***), (>>>)) import Control.Monad.State.Strict hiding (when) import Data.Char (chr) import Data.List (sortBy) -import Data.Maybe (fromMaybe) import Data.Text (Text) import qualified Data.Map as Map import Data.Ord (comparing) @@ -608,8 +607,14 @@ paraTableStyles t s (a:xs) [ ("fo:text-align", x) , ("style:justify-single-word", "false")] -data TextStyle = Italic | Bold | Strike | Sub | Sup | SmallC | Pre - | Lang String String +data TextStyle = Italic + | Bold + | Strike + | Sub + | Sup + | SmallC + | Pre + | Language String String deriving ( Eq,Ord ) textStyleAttr :: TextStyle -> [(String,String)] @@ -627,7 +632,7 @@ textStyleAttr s | Pre <- s = [("style:font-name" ,"Courier New") ,("style:font-name-asian" ,"Courier New") ,("style:font-name-complex" ,"Courier New")] - | Lang lang country <- s + | Language lang country <- s = [("fo:language" ,lang) ,("fo:country" ,country)] | otherwise = [] @@ -637,9 +642,8 @@ withLangFromAttr (_,_,kvs) action = case lookup "lang" kvs of Nothing -> action Just l -> do - (mblang, mbcountry) <- splitLang l - case (mblang, mbcountry) of - (Just lang, _) -> withTextStyle - (Lang lang (fromMaybe "" mbcountry)) - action + mblang <- parseBCP47 l + case mblang of + Just (Lang lang country) -> withTextStyle + (Language lang country) action _ -> action diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index 0b35d27f6..efb553ac2 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -30,7 +30,9 @@ Shared utility functions for pandoc writers. -} module Text.Pandoc.Writers.Shared ( getLang - , splitLang + , parseBCP47 + , Lang(..) + , renderLang , metaToJSON , metaToJSON' , addVariablesToJSON @@ -62,30 +64,41 @@ import Text.Pandoc.Shared (splitBy) import Text.Pandoc.UTF8 (toStringLazy) import Text.Pandoc.XML (escapeStringForXML) +-- | Represents BCP 47 language/country code. +data Lang = Lang String String + +-- | Render a Lang as BCP 47. +renderLang :: Lang -> String +renderLang (Lang la co) = la ++ if null co + then "" + else '-':co + -- | Get the contents of the `lang` metadata field or variable. -getLang :: WriterOptions -> Meta -> Maybe String -getLang opts meta = - lookup "lang" (writerVariables opts) +getLang :: PandocMonad m => WriterOptions -> Meta -> m (Maybe Lang) +getLang opts meta = maybe (return Nothing) parseBCP47 $ + case lookup "lang" (writerVariables opts) of + Just s -> Just s + _ -> Nothing `mplus` case lookupMeta "lang" meta of Just (MetaInlines [Str s]) -> Just s Just (MetaString s) -> Just s _ -> Nothing --- | Split `lang` field into lang and country, issuing warning --- if it doesn't look valid. -splitLang :: PandocMonad m => String -> m (Maybe String, Maybe String) -splitLang lang = +-- | Parse a BCP 47 string as a Lang, issuing a warning if there +-- are issues. +parseBCP47 :: PandocMonad m => String -> m (Maybe Lang) +parseBCP47 lang = case splitBy (== '-') lang of [la,co] | length la == 2 && length co == 2 - -> return (Just la, Just co) + -> return $ Just $ Lang la co [la] | length la == 2 - -> return (Just la, Nothing) + -> return $ Just $ Lang la "" _ -> do report $ InvalidLang lang - return (Nothing, Nothing) + return Nothing -- | Create JSON value for template from a 'Meta' and an association list -- of variables, specified at the command line or in the writer. -- cgit v1.2.3 From a85d8335767b8acad7de36a16be1c6ae4bca9aff Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 25 Jun 2017 15:52:30 +0200 Subject: Fixed log message for InvalidLang. --- src/Text/Pandoc/Logging.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs index ad0fcdd2d..e31fb1521 100644 --- a/src/Text/Pandoc/Logging.hs +++ b/src/Text/Pandoc/Logging.hs @@ -259,7 +259,7 @@ showLogMessage msg = "It is recommended that lang be specified for this format." InvalidLang s -> "Invalid 'lang' value '" ++ s ++ "'.\n" ++ - "Use ISO 8601 format like 'en-US'." + "Use an IETF language tag like 'en-US'." CouldNotHighlight m -> "Could not highlight code block:\n" ++ m MissingCharacter m -> -- cgit v1.2.3 From 643cbdf1044623475cb6ade9c35de85148d0dff6 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 25 Jun 2017 18:31:59 +0200 Subject: Writers.Shared: improve type of Lang and bcp47 parser. Use a real parsec parser for BCP47, include variants. --- src/Text/Pandoc/Writers/ODT.hs | 6 +-- src/Text/Pandoc/Writers/OpenDocument.hs | 18 +++---- src/Text/Pandoc/Writers/Shared.hs | 96 +++++++++++++++++++++++---------- 3 files changed, 79 insertions(+), 41 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index 8573f5719..54873efb2 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -171,11 +171,11 @@ updateStyleWithLang (Just lang) arch = do | e <- zEntries arch] } addLang :: Lang -> Element -> Element -addLang (Lang lang country) = everywhere' (mkT updateLangAttr) +addLang lang = everywhere' (mkT updateLangAttr) where updateLangAttr (Attr n@(QName "language" _ (Just "fo")) _) - = Attr n lang + = Attr n (langLanguage lang) updateLangAttr (Attr n@(QName "country" _ (Just "fo")) _) - = Attr n country + = Attr n (langRegion lang) updateLangAttr x = x -- | transform both Image and Math elements diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index 57f3c1194..763cea5ad 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -614,7 +614,7 @@ data TextStyle = Italic | Sup | SmallC | Pre - | Language String String + | Language Lang deriving ( Eq,Ord ) textStyleAttr :: TextStyle -> [(String,String)] @@ -632,9 +632,9 @@ textStyleAttr s | Pre <- s = [("style:font-name" ,"Courier New") ,("style:font-name-asian" ,"Courier New") ,("style:font-name-complex" ,"Courier New")] - | Language lang country <- s - = [("fo:language" ,lang) - ,("fo:country" ,country)] + | Language lang <- s + = [("fo:language" ,langLanguage lang) + ,("fo:country" ,langRegion lang)] | otherwise = [] withLangFromAttr :: PandocMonad m => Attr -> OD m a -> OD m a @@ -642,8 +642,8 @@ withLangFromAttr (_,_,kvs) action = case lookup "lang" kvs of Nothing -> action Just l -> do - mblang <- parseBCP47 l - case mblang of - Just (Lang lang country) -> withTextStyle - (Language lang country) action - _ -> action + case parseBCP47 l of + Right lang -> withTextStyle (Language lang) action + Left _ -> do + report $ InvalidLang l + action diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index efb553ac2..b56f2d468 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -46,11 +46,12 @@ module Text.Pandoc.Writers.Shared ( , gridTable ) where -import Control.Monad (liftM, zipWithM, mplus) +import Control.Monad (liftM, zipWithM, guard) import Data.Aeson (FromJSON (..), Result (..), ToJSON (..), Value (Object), encode, fromJSON) +import Data.Char (isAscii, isLetter, isUpper, isLower) import qualified Data.HashMap.Strict as H -import Data.List (groupBy, intersperse, transpose) +import Data.List (groupBy, intersperse, transpose, intercalate) import qualified Data.Map as M import Data.Maybe (isJust) import qualified Data.Text as T @@ -60,45 +61,82 @@ import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Pretty -import Text.Pandoc.Shared (splitBy) import Text.Pandoc.UTF8 (toStringLazy) import Text.Pandoc.XML (escapeStringForXML) +import qualified Text.Parsec as P -- | Represents BCP 47 language/country code. -data Lang = Lang String String +data Lang = Lang{ langLanguage :: String + , langScript :: String + , langRegion :: String + , langVariants :: [String] } + deriving (Eq, Ord, Show) -- | Render a Lang as BCP 47. renderLang :: Lang -> String -renderLang (Lang la co) = la ++ if null co - then "" - else '-':co +renderLang lang = intercalate "-" (langLanguage lang : filter (not . null) + ([langScript lang, langRegion lang] ++ langVariants lang)) -- | Get the contents of the `lang` metadata field or variable. getLang :: PandocMonad m => WriterOptions -> Meta -> m (Maybe Lang) -getLang opts meta = maybe (return Nothing) parseBCP47 $ - case lookup "lang" (writerVariables opts) of - Just s -> Just s - _ -> Nothing - `mplus` - case lookupMeta "lang" meta of - Just (MetaInlines [Str s]) -> Just s - Just (MetaString s) -> Just s - _ -> Nothing +getLang opts meta = case + (case lookup "lang" (writerVariables opts) of + Just s -> Just s + _ -> + case lookupMeta "lang" meta of + Just (MetaInlines [Str s]) -> Just s + Just (MetaString s) -> Just s + _ -> Nothing) of + Nothing -> return Nothing + Just s -> case parseBCP47 s of + Left _ -> do + report $ InvalidLang s + return Nothing + Right l -> return (Just l) --- | Parse a BCP 47 string as a Lang, issuing a warning if there --- are issues. -parseBCP47 :: PandocMonad m => String -> m (Maybe Lang) +-- | Parse a BCP 47 string as a Lang. +parseBCP47 :: String -> Either String Lang parseBCP47 lang = - case splitBy (== '-') lang of - [la,co] - | length la == 2 && length co == 2 - -> return $ Just $ Lang la co - [la] - | length la == 2 - -> return $ Just $ Lang la "" - _ -> do - report $ InvalidLang lang - return Nothing + case P.parse bcp47 "lang" lang of + Right r -> Right r + Left e -> Left $ show e + where bcp47 = do + language <- pLanguage + script <- P.option "" pScript + region <- P.option "" pRegion + variants <- P.many pVariant + () <$ P.char '-' P.<|> P.eof + return $ Lang{ langLanguage = language + , langScript = script + , langRegion = region + , langVariants = variants } + asciiLetter = P.satisfy (\c -> isAscii c && isLetter c) + pLanguage = do + cs <- P.many1 asciiLetter + let lcs = length cs + guard $ lcs == 2 || lcs == 3 + return cs + pScript = P.try $ do + P.char '-' + x <- P.satisfy (\c -> isAscii c && isLetter c && isUpper c) + xs <- P.count 3 + (P.satisfy (\c -> isAscii c && isLetter c && isLower c)) + return (x:xs) + pRegion = P.try $ do + P.char '-' + cs <- P.many1 asciiLetter + let lcs = length cs + guard $ lcs == 2 || lcs == 3 + return cs + pVariant = P.try $ do + P.char '-' + ds <- P.option "" (P.count 1 P.digit) + cs <- P.many1 asciiLetter + let var = ds ++ cs + guard $ if null ds + then length var >= 5 && length var <= 8 + else length var == 4 + return var -- | Create JSON value for template from a 'Meta' and an association list -- of variables, specified at the command line or in the writer. -- cgit v1.2.3 From ac9423eccc76005f996a10a545594247ac753e02 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 25 Jun 2017 21:00:35 +0200 Subject: Moved BCP47 specific functions from Writers.Shared to new module. Text.Pandoc.BCP47 (unexported, internal module). `getLang`, `Lang(..)`, `parseBCP47`. --- src/Text/Pandoc/BCP47.hs | 117 ++++++++++++++++++++++++++++++++ src/Text/Pandoc/Writers/Docx.hs | 3 +- src/Text/Pandoc/Writers/ODT.hs | 4 +- src/Text/Pandoc/Writers/OpenDocument.hs | 1 + src/Text/Pandoc/Writers/Shared.hs | 87 +----------------------- 5 files changed, 125 insertions(+), 87 deletions(-) create mode 100644 src/Text/Pandoc/BCP47.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/BCP47.hs b/src/Text/Pandoc/BCP47.hs new file mode 100644 index 000000000..ae7f54473 --- /dev/null +++ b/src/Text/Pandoc/BCP47.hs @@ -0,0 +1,117 @@ +{- +Copyright (C) 2017 John MacFarlane <jgm@berkeley.edu> + +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.BCP47 + Copyright : Copyright (C) 2017 John MacFarlane + License : GNU GPL, version 2 or above + + Maintainer : John MacFarlane <jgm@berkeley.edu> + Stability : alpha + Portability : portable + +Functions for parsing and rendering BCP47 language identifiers. +-} +module Text.Pandoc.BCP47 ( + getLang + , parseBCP47 + , Lang(..) + , renderLang + ) +where +import Control.Monad (guard) +import Data.Char (isAscii, isLetter, isUpper, isLower) +import Data.List (intercalate) +import Text.Pandoc.Definition +import Text.Pandoc.Class (PandocMonad, report) +import Text.Pandoc.Logging +import Text.Pandoc.Options +import qualified Text.Parsec as P + +-- | Represents BCP 47 language/country code. +data Lang = Lang{ langLanguage :: String + , langScript :: String + , langRegion :: String + , langVariants :: [String] } + deriving (Eq, Ord, Show) + +-- | Render a Lang as BCP 47. +renderLang :: Lang -> String +renderLang lang = intercalate "-" (langLanguage lang : filter (not . null) + ([langScript lang, langRegion lang] ++ langVariants lang)) + +-- | Get the contents of the `lang` metadata field or variable. +getLang :: PandocMonad m => WriterOptions -> Meta -> m (Maybe Lang) +getLang opts meta = case + (case lookup "lang" (writerVariables opts) of + Just s -> Just s + _ -> + case lookupMeta "lang" meta of + Just (MetaInlines [Str s]) -> Just s + Just (MetaString s) -> Just s + _ -> Nothing) of + Nothing -> return Nothing + Just s -> case parseBCP47 s of + Left _ -> do + report $ InvalidLang s + return Nothing + Right l -> return (Just l) + +-- | Parse a BCP 47 string as a Lang. +parseBCP47 :: String -> Either String Lang +parseBCP47 lang = + case P.parse bcp47 "lang" lang of + Right r -> Right r + Left e -> Left $ show e + where bcp47 = do + language <- pLanguage + script <- P.option "" pScript + region <- P.option "" pRegion + variants <- P.many pVariant + () <$ P.char '-' P.<|> P.eof + return $ Lang{ langLanguage = language + , langScript = script + , langRegion = region + , langVariants = variants } + asciiLetter = P.satisfy (\c -> isAscii c && isLetter c) + pLanguage = do + cs <- P.many1 asciiLetter + let lcs = length cs + guard $ lcs == 2 || lcs == 3 + return cs + pScript = P.try $ do + P.char '-' + x <- P.satisfy (\c -> isAscii c && isLetter c && isUpper c) + xs <- P.count 3 + (P.satisfy (\c -> isAscii c && isLetter c && isLower c)) + return (x:xs) + pRegion = P.try $ do + P.char '-' + cs <- P.many1 asciiLetter + let lcs = length cs + guard $ lcs == 2 || lcs == 3 + return cs + pVariant = P.try $ do + P.char '-' + ds <- P.option "" (P.count 1 P.digit) + cs <- P.many1 asciiLetter + let var = ds ++ cs + guard $ if null ds + then length var >= 5 && length var <= 8 + else length var == 4 + return var diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 52ababb14..bc8568cd1 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -67,7 +67,8 @@ import Text.Pandoc.Shared hiding (Element) import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Walk import Text.Pandoc.Writers.Math -import Text.Pandoc.Writers.Shared (fixDisplayMath, getLang, renderLang) +import Text.Pandoc.Writers.Shared (fixDisplayMath) +import Text.Pandoc.BCP47 (getLang, renderLang) import Text.Printf (printf) import Text.TeXMath import Text.XML.Light as XML diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index 54873efb2..98aa3b30b 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -50,8 +50,8 @@ import Text.Pandoc.Shared (stringify) import Text.Pandoc.UTF8 (fromStringLazy, fromTextLazy, toStringLazy) import Text.Pandoc.Walk import Text.Pandoc.Writers.OpenDocument (writeOpenDocument) -import Text.Pandoc.Writers.Shared (fixDisplayMath, getLang, Lang(..), - renderLang) +import Text.Pandoc.Writers.Shared (fixDisplayMath) +import Text.Pandoc.BCP47 (getLang, Lang(..), renderLang) import Text.Pandoc.XML import Text.TeXMath import Text.XML.Light diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index 763cea5ad..6c53ab4ab 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -50,6 +50,7 @@ import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Writers.Math import Text.Pandoc.Writers.Shared import Text.Pandoc.XML +import Text.Pandoc.BCP47 (parseBCP47, Lang(..)) import Text.Printf (printf) -- | Auxiliary function to convert Plain block to Para. diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index b56f2d468..2047285eb 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -29,11 +29,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Shared utility functions for pandoc writers. -} module Text.Pandoc.Writers.Shared ( - getLang - , parseBCP47 - , Lang(..) - , renderLang - , metaToJSON + metaToJSON , metaToJSON' , addVariablesToJSON , getField @@ -46,97 +42,20 @@ module Text.Pandoc.Writers.Shared ( , gridTable ) where -import Control.Monad (liftM, zipWithM, guard) +import Control.Monad (liftM, zipWithM) import Data.Aeson (FromJSON (..), Result (..), ToJSON (..), Value (Object), encode, fromJSON) -import Data.Char (isAscii, isLetter, isUpper, isLower) import qualified Data.HashMap.Strict as H -import Data.List (groupBy, intersperse, transpose, intercalate) +import Data.List (groupBy, intersperse, transpose) import qualified Data.Map as M import Data.Maybe (isJust) import qualified Data.Text as T import qualified Data.Traversable as Traversable import Text.Pandoc.Definition -import Text.Pandoc.Class (PandocMonad, report) -import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Pretty import Text.Pandoc.UTF8 (toStringLazy) import Text.Pandoc.XML (escapeStringForXML) -import qualified Text.Parsec as P - --- | Represents BCP 47 language/country code. -data Lang = Lang{ langLanguage :: String - , langScript :: String - , langRegion :: String - , langVariants :: [String] } - deriving (Eq, Ord, Show) - --- | Render a Lang as BCP 47. -renderLang :: Lang -> String -renderLang lang = intercalate "-" (langLanguage lang : filter (not . null) - ([langScript lang, langRegion lang] ++ langVariants lang)) - --- | Get the contents of the `lang` metadata field or variable. -getLang :: PandocMonad m => WriterOptions -> Meta -> m (Maybe Lang) -getLang opts meta = case - (case lookup "lang" (writerVariables opts) of - Just s -> Just s - _ -> - case lookupMeta "lang" meta of - Just (MetaInlines [Str s]) -> Just s - Just (MetaString s) -> Just s - _ -> Nothing) of - Nothing -> return Nothing - Just s -> case parseBCP47 s of - Left _ -> do - report $ InvalidLang s - return Nothing - Right l -> return (Just l) - --- | Parse a BCP 47 string as a Lang. -parseBCP47 :: String -> Either String Lang -parseBCP47 lang = - case P.parse bcp47 "lang" lang of - Right r -> Right r - Left e -> Left $ show e - where bcp47 = do - language <- pLanguage - script <- P.option "" pScript - region <- P.option "" pRegion - variants <- P.many pVariant - () <$ P.char '-' P.<|> P.eof - return $ Lang{ langLanguage = language - , langScript = script - , langRegion = region - , langVariants = variants } - asciiLetter = P.satisfy (\c -> isAscii c && isLetter c) - pLanguage = do - cs <- P.many1 asciiLetter - let lcs = length cs - guard $ lcs == 2 || lcs == 3 - return cs - pScript = P.try $ do - P.char '-' - x <- P.satisfy (\c -> isAscii c && isLetter c && isUpper c) - xs <- P.count 3 - (P.satisfy (\c -> isAscii c && isLetter c && isLower c)) - return (x:xs) - pRegion = P.try $ do - P.char '-' - cs <- P.many1 asciiLetter - let lcs = length cs - guard $ lcs == 2 || lcs == 3 - return cs - pVariant = P.try $ do - P.char '-' - ds <- P.option "" (P.count 1 P.digit) - cs <- P.many1 asciiLetter - let var = ds ++ cs - guard $ if null ds - then length var >= 5 && length var <= 8 - else length var == 4 - return var -- | Create JSON value for template from a 'Meta' and an association list -- of variables, specified at the command line or in the writer. -- cgit v1.2.3 From d0d2443f2e069c9aa4510579f10ed8fe0b5f20ab Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 25 Jun 2017 21:56:29 +0200 Subject: Refactored ConTeXt writer to use BCP47. BCP47 - consistent case for BCP47 fields (e.g. uppercase for region). --- src/Text/Pandoc/BCP47.hs | 10 +++--- src/Text/Pandoc/Writers/ConTeXt.hs | 68 +++++++++++++++++++------------------- 2 files changed, 39 insertions(+), 39 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/BCP47.hs b/src/Text/Pandoc/BCP47.hs index ae7f54473..956130fb7 100644 --- a/src/Text/Pandoc/BCP47.hs +++ b/src/Text/Pandoc/BCP47.hs @@ -35,7 +35,7 @@ module Text.Pandoc.BCP47 ( ) where import Control.Monad (guard) -import Data.Char (isAscii, isLetter, isUpper, isLower) +import Data.Char (isAscii, isLetter, isUpper, isLower, toUpper, toLower) import Data.List (intercalate) import Text.Pandoc.Definition import Text.Pandoc.Class (PandocMonad, report) @@ -93,19 +93,19 @@ parseBCP47 lang = cs <- P.many1 asciiLetter let lcs = length cs guard $ lcs == 2 || lcs == 3 - return cs + return $ map toLower cs pScript = P.try $ do P.char '-' x <- P.satisfy (\c -> isAscii c && isLetter c && isUpper c) xs <- P.count 3 (P.satisfy (\c -> isAscii c && isLetter c && isLower c)) - return (x:xs) + return $ map toLower (x:xs) pRegion = P.try $ do P.char '-' cs <- P.many1 asciiLetter let lcs = length cs guard $ lcs == 2 || lcs == 3 - return cs + return $ map toUpper cs pVariant = P.try $ do P.char '-' ds <- P.option "" (P.count 1 P.digit) @@ -114,4 +114,4 @@ parseBCP47 lang = guard $ if null ds then length var >= 5 && length var <= 8 else length var == 4 - return var + return $ map toLower var diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index 5a81aa8a0..ae6cb482f 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -35,6 +35,7 @@ import Data.List (intercalate, intersperse) import Data.Maybe (catMaybes) import Data.Text (Text) import Network.URI (unEscapeString) +import Text.Pandoc.BCP47 import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Logging import Text.Pandoc.Definition @@ -88,6 +89,7 @@ pandocToConTeXt options (Pandoc meta blocks) = do ,("top","margin-top") ,("bottom","margin-bottom") ] + lang <- maybe "" fromBCP47 <$> getLang options meta let context = defField "toc" (writerTableOfContents options) $ defField "placelist" (intercalate ("," :: String) $ take (writerTOCDepth options + @@ -100,11 +102,10 @@ pandocToConTeXt options (Pandoc meta blocks) = do $ defField "body" main $ defField "layout" layoutFromMargins $ defField "number-sections" (writerNumberSections options) + $ defField "context-lang" lang $ metadata - let context' = defField "context-lang" (maybe "" (fromBcp47 . splitBy (=='-')) $ - getField "lang" context) - $ defField "context-dir" (toContextDir $ getField "dir" context) - $ context + let context' = defField "context-dir" (toContextDir + $ getField "dir" context) context case writerTemplate options of Nothing -> return main Just tpl -> renderTemplate' tpl context' @@ -196,7 +197,7 @@ blockToConTeXt (Div (ident,_,kvs) bs) = do _ -> id wrapLang txt = case lookup "lang" kvs of Just lng -> "\\start\\language[" - <> text (fromBcp47' lng) <> "]" $$ txt $$ "\\stop" + <> text (fromBCP47' lng) <> "]" $$ txt $$ "\\stop" Nothing -> txt wrapBlank txt = blankline <> txt <> blankline fmap (wrapBlank . wrapLang . wrapDir . wrapRef) $ blockListToConTeXt bs @@ -421,7 +422,7 @@ inlineToConTeXt (Span (_,_,kvs) ils) = do Just "ltr" -> braces $ "\\lefttoright " <> txt _ -> txt wrapLang txt = case lookup "lang" kvs of - Just lng -> "\\start\\language[" <> text (fromBcp47' lng) + Just lng -> "\\start\\language[" <> text (fromBCP47' lng) <> "]" <> txt <> "\\stop " Nothing -> txt fmap (wrapLang . wrapDir) $ inlineListToConTeXt ils @@ -458,36 +459,35 @@ sectionHeader (ident,classes,_) hdrLevel lst = do <> blankline _ -> contents <> blankline -fromBcp47' :: String -> String -fromBcp47' = fromBcp47 . splitBy (=='-') +fromBCP47' :: String -> String +fromBCP47' s = case parseBCP47 s of + Right r -> fromBCP47 r + Left _ -> "" -- Takes a list of the constituents of a BCP 47 language code -- and irons out ConTeXt's exceptions -- https://tools.ietf.org/html/bcp47#section-2.1 -- http://wiki.contextgarden.net/Language_Codes -fromBcp47 :: [String] -> String -fromBcp47 [] = "" -fromBcp47 ("ar":"SY":_) = "ar-sy" -fromBcp47 ("ar":"IQ":_) = "ar-iq" -fromBcp47 ("ar":"JO":_) = "ar-jo" -fromBcp47 ("ar":"LB":_) = "ar-lb" -fromBcp47 ("ar":"DZ":_) = "ar-dz" -fromBcp47 ("ar":"MA":_) = "ar-ma" -fromBcp47 ("de":"1901":_) = "deo" -fromBcp47 ("de":"DE":_) = "de-de" -fromBcp47 ("de":"AT":_) = "de-at" -fromBcp47 ("de":"CH":_) = "de-ch" -fromBcp47 ("el":"poly":_) = "agr" -fromBcp47 ("en":"US":_) = "en-us" -fromBcp47 ("en":"GB":_) = "en-gb" -fromBcp47 ("grc":_) = "agr" -fromBcp47 x = fromIso $ head x - where - fromIso "el" = "gr" - fromIso "eu" = "ba" - fromIso "he" = "il" - fromIso "jp" = "ja" - fromIso "uk" = "ua" - fromIso "vi" = "vn" - fromIso "zh" = "cn" - fromIso l = l +fromBCP47 :: Lang -> String +fromBCP47 (Lang "ar" _ "SY" _) = "ar-sy" +fromBCP47 (Lang "ar" _ "IQ" _) = "ar-iq" +fromBCP47 (Lang "ar" _ "JO" _) = "ar-jo" +fromBCP47 (Lang "ar" _ "LB" _) = "ar-lb" +fromBCP47 (Lang "ar" _ "DZ" _) = "ar-dz" +fromBCP47 (Lang "ar" _ "MA" _) = "ar-ma" +fromBCP47 (Lang "de" _ _ ["1901"]) = "deo" +fromBCP47 (Lang "de" _ "DE" _) = "de-de" +fromBCP47 (Lang "de" _ "AT" _) = "de-at" +fromBCP47 (Lang "de" _ "CH" _) = "de-ch" +fromBCP47 (Lang "el" _ _ ["poly"]) = "agr" +fromBCP47 (Lang "en" _ "US" _) = "en-us" +fromBCP47 (Lang "en" _ "GB" _) = "en-gb" +fromBCP47 (Lang "grc"_ _ _) = "agr" +fromBCP47 (Lang "el" _ _ _) = "gr" +fromBCP47 (Lang "eu" _ _ _) = "ba" +fromBCP47 (Lang "he" _ _ _) = "il" +fromBCP47 (Lang "jp" _ _ _) = "ja" +fromBCP47 (Lang "uk" _ _ _) = "ua" +fromBCP47 (Lang "vi" _ _ _) = "vn" +fromBCP47 (Lang "zh" _ _ _) = "cn" +fromBCP47 (Lang l _ _ _) = l -- cgit v1.2.3 From 4cbbc9dd587d73d576b4c891f3f37a19f12cf10c Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 25 Jun 2017 23:16:55 +0200 Subject: BCP47: split toLang from getLang, rearranged types. --- src/Text/Pandoc/BCP47.hs | 26 +++++++++------ src/Text/Pandoc/Writers/ConTeXt.hs | 67 +++++++++++++++++++------------------- src/Text/Pandoc/Writers/Docx.hs | 6 ++-- src/Text/Pandoc/Writers/ODT.hs | 4 +-- 4 files changed, 55 insertions(+), 48 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/BCP47.hs b/src/Text/Pandoc/BCP47.hs index 956130fb7..16dd3a032 100644 --- a/src/Text/Pandoc/BCP47.hs +++ b/src/Text/Pandoc/BCP47.hs @@ -29,6 +29,7 @@ Functions for parsing and rendering BCP47 language identifiers. -} module Text.Pandoc.BCP47 ( getLang + , toLang , parseBCP47 , Lang(..) , renderLang @@ -56,21 +57,26 @@ renderLang lang = intercalate "-" (langLanguage lang : filter (not . null) ([langScript lang, langRegion lang] ++ langVariants lang)) -- | Get the contents of the `lang` metadata field or variable. -getLang :: PandocMonad m => WriterOptions -> Meta -> m (Maybe Lang) -getLang opts meta = case - (case lookup "lang" (writerVariables opts) of +getLang :: WriterOptions -> Meta -> Maybe String +getLang opts meta = + case lookup "lang" (writerVariables opts) of Just s -> Just s _ -> case lookupMeta "lang" meta of Just (MetaInlines [Str s]) -> Just s Just (MetaString s) -> Just s - _ -> Nothing) of - Nothing -> return Nothing - Just s -> case parseBCP47 s of - Left _ -> do - report $ InvalidLang s - return Nothing - Right l -> return (Just l) + _ -> Nothing + +-- | Convert BCP47 string to a Lang, issuing warning +-- if there are problems. +toLang :: PandocMonad m => Maybe String -> m (Maybe Lang) +toLang Nothing = return Nothing +toLang (Just s) = + case parseBCP47 s of + Left _ -> do + report $ InvalidLang s + return Nothing + Right l -> return (Just l) -- | Parse a BCP 47 string as a Lang. parseBCP47 :: String -> Either String Lang diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index ae6cb482f..7886bc052 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -89,7 +89,7 @@ pandocToConTeXt options (Pandoc meta blocks) = do ,("top","margin-top") ,("bottom","margin-bottom") ] - lang <- maybe "" fromBCP47 <$> getLang options meta + mblang <- fromBCP47 (getLang options meta) let context = defField "toc" (writerTableOfContents options) $ defField "placelist" (intercalate ("," :: String) $ take (writerTOCDepth options + @@ -102,7 +102,7 @@ pandocToConTeXt options (Pandoc meta blocks) = do $ defField "body" main $ defField "layout" layoutFromMargins $ defField "number-sections" (writerNumberSections options) - $ defField "context-lang" lang + $ maybe id (defField "context-lang") mblang $ metadata let context' = defField "context-dir" (toContextDir $ getField "dir" context) context @@ -187,6 +187,7 @@ blockToConTeXt b@(RawBlock _ _ ) = do return empty blockToConTeXt (Div (ident,_,kvs) bs) = do let align dir txt = "\\startalignment[" <> dir <> "]" $$ txt $$ "\\stopalignment" + mblang <- fromBCP47 (lookup "lang" kvs) let wrapRef txt = if null ident then txt else ("\\reference" <> brackets (text $ toLabel ident) <> @@ -195,9 +196,9 @@ blockToConTeXt (Div (ident,_,kvs) bs) = do Just "rtl" -> align "righttoleft" Just "ltr" -> align "lefttoright" _ -> id - wrapLang txt = case lookup "lang" kvs of + wrapLang txt = case mblang of Just lng -> "\\start\\language[" - <> text (fromBCP47' lng) <> "]" $$ txt $$ "\\stop" + <> text lng <> "]" $$ txt $$ "\\stop" Nothing -> txt wrapBlank txt = blankline <> txt <> blankline fmap (wrapBlank . wrapLang . wrapDir . wrapRef) $ blockListToConTeXt bs @@ -417,12 +418,13 @@ inlineToConTeXt (Note contents) = do else text "\\startbuffer " <> nest 2 contents' <> text "\\stopbuffer\\footnote{\\getbuffer}" inlineToConTeXt (Span (_,_,kvs) ils) = do + mblang <- fromBCP47 (lookup "lang" kvs) let wrapDir txt = case lookup "dir" kvs of Just "rtl" -> braces $ "\\righttoleft " <> txt Just "ltr" -> braces $ "\\lefttoright " <> txt _ -> txt - wrapLang txt = case lookup "lang" kvs of - Just lng -> "\\start\\language[" <> text (fromBCP47' lng) + wrapLang txt = case mblang of + Just lng -> "\\start\\language[" <> text lng <> "]" <> txt <> "\\stop " Nothing -> txt fmap (wrapLang . wrapDir) $ inlineListToConTeXt ils @@ -459,35 +461,34 @@ sectionHeader (ident,classes,_) hdrLevel lst = do <> blankline _ -> contents <> blankline -fromBCP47' :: String -> String -fromBCP47' s = case parseBCP47 s of - Right r -> fromBCP47 r - Left _ -> "" +fromBCP47 :: PandocMonad m => Maybe String -> WM m (Maybe String) +fromBCP47 mbs = fromBCP47' <$> toLang mbs -- Takes a list of the constituents of a BCP 47 language code -- and irons out ConTeXt's exceptions -- https://tools.ietf.org/html/bcp47#section-2.1 -- http://wiki.contextgarden.net/Language_Codes -fromBCP47 :: Lang -> String -fromBCP47 (Lang "ar" _ "SY" _) = "ar-sy" -fromBCP47 (Lang "ar" _ "IQ" _) = "ar-iq" -fromBCP47 (Lang "ar" _ "JO" _) = "ar-jo" -fromBCP47 (Lang "ar" _ "LB" _) = "ar-lb" -fromBCP47 (Lang "ar" _ "DZ" _) = "ar-dz" -fromBCP47 (Lang "ar" _ "MA" _) = "ar-ma" -fromBCP47 (Lang "de" _ _ ["1901"]) = "deo" -fromBCP47 (Lang "de" _ "DE" _) = "de-de" -fromBCP47 (Lang "de" _ "AT" _) = "de-at" -fromBCP47 (Lang "de" _ "CH" _) = "de-ch" -fromBCP47 (Lang "el" _ _ ["poly"]) = "agr" -fromBCP47 (Lang "en" _ "US" _) = "en-us" -fromBCP47 (Lang "en" _ "GB" _) = "en-gb" -fromBCP47 (Lang "grc"_ _ _) = "agr" -fromBCP47 (Lang "el" _ _ _) = "gr" -fromBCP47 (Lang "eu" _ _ _) = "ba" -fromBCP47 (Lang "he" _ _ _) = "il" -fromBCP47 (Lang "jp" _ _ _) = "ja" -fromBCP47 (Lang "uk" _ _ _) = "ua" -fromBCP47 (Lang "vi" _ _ _) = "vn" -fromBCP47 (Lang "zh" _ _ _) = "cn" -fromBCP47 (Lang l _ _ _) = l +fromBCP47' :: Maybe Lang -> Maybe String +fromBCP47' (Just (Lang "ar" _ "SY" _) ) = Just "ar-sy" +fromBCP47' (Just (Lang "ar" _ "IQ" _) ) = Just "ar-iq" +fromBCP47' (Just (Lang "ar" _ "JO" _) ) = Just "ar-jo" +fromBCP47' (Just (Lang "ar" _ "LB" _) ) = Just "ar-lb" +fromBCP47' (Just (Lang "ar" _ "DZ" _) ) = Just "ar-dz" +fromBCP47' (Just (Lang "ar" _ "MA" _) ) = Just "ar-ma" +fromBCP47' (Just (Lang "de" _ _ ["1901"]) ) = Just "deo" +fromBCP47' (Just (Lang "de" _ "DE" _) ) = Just "de-de" +fromBCP47' (Just (Lang "de" _ "AT" _) ) = Just "de-at" +fromBCP47' (Just (Lang "de" _ "CH" _) ) = Just "de-ch" +fromBCP47' (Just (Lang "el" _ _ ["poly"]) ) = Just "agr" +fromBCP47' (Just (Lang "en" _ "US" _) ) = Just "en-us" +fromBCP47' (Just (Lang "en" _ "GB" _) ) = Just "en-gb" +fromBCP47' (Just (Lang "grc"_ _ _) ) = Just "agr" +fromBCP47' (Just (Lang "el" _ _ _) ) = Just "gr" +fromBCP47' (Just (Lang "eu" _ _ _) ) = Just "ba" +fromBCP47' (Just (Lang "he" _ _ _) ) = Just "il" +fromBCP47' (Just (Lang "jp" _ _ _) ) = Just "ja" +fromBCP47' (Just (Lang "uk" _ _ _) ) = Just "ua" +fromBCP47' (Just (Lang "vi" _ _ _) ) = Just "vn" +fromBCP47' (Just (Lang "zh" _ _ _) ) = Just "cn" +fromBCP47' (Just (Lang l _ _ _) ) = Just l +fromBCP47' Nothing = Nothing diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index bc8568cd1..06318b20c 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -68,7 +68,7 @@ import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Walk import Text.Pandoc.Writers.Math import Text.Pandoc.Writers.Shared (fixDisplayMath) -import Text.Pandoc.BCP47 (getLang, renderLang) +import Text.Pandoc.BCP47 (getLang, renderLang, toLang) import Text.Printf (printf) import Text.TeXMath import Text.XML.Light as XML @@ -258,9 +258,9 @@ writeDocx opts doc@(Pandoc meta _) = do ) -- styles - lang <- getLang opts meta + mblang <- toLang $ getLang opts meta let addLang :: Element -> Element - addLang e = case lang >>= \l -> + addLang e = case mblang >>= \l -> (return . XMLC.toTree . go (renderLang l) . XMLC.fromElement) e of Just (Elem e') -> e' diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index 98aa3b30b..785891a9f 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -51,7 +51,7 @@ import Text.Pandoc.UTF8 (fromStringLazy, fromTextLazy, toStringLazy) import Text.Pandoc.Walk import Text.Pandoc.Writers.OpenDocument (writeOpenDocument) import Text.Pandoc.Writers.Shared (fixDisplayMath) -import Text.Pandoc.BCP47 (getLang, Lang(..), renderLang) +import Text.Pandoc.BCP47 (getLang, toLang, Lang(..), renderLang) import Text.Pandoc.XML import Text.TeXMath import Text.XML.Light @@ -80,7 +80,7 @@ pandocToODT :: PandocMonad m pandocToODT opts doc@(Pandoc meta _) = do let datadir = writerUserDataDir opts let title = docTitle meta - lang <- getLang opts meta + lang <- toLang (getLang opts meta) refArchive <- case writerReferenceDoc opts of Just f -> liftM toArchive $ lift $ P.readFileLazy f -- cgit v1.2.3 From b95f391bebdd6d79b11db4469d97640e80285ccc Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 26 Jun 2017 09:40:53 +0300 Subject: Muse reader: simplify para implementation (#3761) --- src/Text/Pandoc/Readers/Muse.hs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index fe8a55f5c..06d385222 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -247,9 +247,7 @@ commentTag :: PandocMonad m => MuseParser m (F Blocks) commentTag = parseHtmlContent "comment" block >> return mempty para :: PandocMonad m => MuseParser m (F Blocks) -para = do - res <- trimInlinesF . mconcat <$> many1Till inline endOfParaElement - return $ B.para <$> res +para = liftM B.para . trimInlinesF . mconcat <$> many1Till inline endOfParaElement where endOfParaElement = lookAhead $ endOfInput <|> endOfPara <|> newBlockElement endOfInput = try $ skipMany blankline >> skipSpaces >> eof -- cgit v1.2.3 From 492b3b129190be9742981493812894f888bb5f2d Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 26 Jun 2017 09:41:17 +0300 Subject: Muse reader: fix horizontal rule parsing (#3762) Do not parse 3 dashes as horizontal rule and allow whitespace after rule --- src/Text/Pandoc/Readers/Muse.hs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 06d385222..eb0769e0b 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -200,8 +200,10 @@ comment = try $ do separator :: PandocMonad m => MuseParser m (F Blocks) separator = try $ do - string "---" - newline + string "----" + many $ char '-' + many spaceChar + void newline <|> eof return $ return B.horizontalRule header :: PandocMonad m => MuseParser m (F Blocks) -- cgit v1.2.3 From f09473eab70f3d540fe1586c0256336ab9679049 Mon Sep 17 00:00:00 2001 From: Yuchen Pei <ycpei@users.noreply.github.com> Date: Mon, 26 Jun 2017 02:41:51 -0400 Subject: minor updates to vimwiki reader. (#3759) - updated comments in Vimwiki.hs to reflect current status of implementation - added vimwiki to trypandoc --- src/Text/Pandoc/Readers/Vimwiki.hs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Vimwiki.hs b/src/Text/Pandoc/Readers/Vimwiki.hs index 98f04eda9..11faedb24 100644 --- a/src/Text/Pandoc/Readers/Vimwiki.hs +++ b/src/Text/Pandoc/Readers/Vimwiki.hs @@ -28,20 +28,20 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Conversion of vimwiki text to 'Pandoc' document. -} {-- - progress: +[X]: implemented +[O]: not implemented * block parsers: * [X] header * [X] hrule * [X] comment * [X] blockquote - * [X] preformatted + * [X] preformatted -- using codeblock * [X] displaymath * [X] bulletlist / orderedlist - * [X] orderedlist with 1., i., a) etc identification. - * [X] todo lists -- not list builder with attributes? using span. + * [X] todo lists -- using span. * [X] table * [X] centered table -- using div - * [O] colspan and rowspan -- pandoc limitation, see issue #1024 + * [O] colspan and rowspan -- see issue #1024 * [X] paragraph * [X] definition list * inline parsers: @@ -58,8 +58,7 @@ Conversion of vimwiki text to 'Pandoc' document. * misc: * [X] `TODO:` mark * [X] metadata placeholders: %title and %date - * [O] control placeholders: %template and %nohtml -- %template added to - meta, %nohtml ignored + * [O] control placeholders: %template and %nohtml -- ignored --} module Text.Pandoc.Readers.Vimwiki ( readVimwiki -- cgit v1.2.3 From 700a0843b2310c6b319bf34d2aebd8470cc76b40 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 26 Jun 2017 15:03:51 +0200 Subject: parseBCP47: Parse extensions and private-use as variants. Even though officially they aren't. This suffices for our purposes. --- src/Text/Pandoc/BCP47.hs | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/BCP47.hs b/src/Text/Pandoc/BCP47.hs index 16dd3a032..b4b55c5d4 100644 --- a/src/Text/Pandoc/BCP47.hs +++ b/src/Text/Pandoc/BCP47.hs @@ -36,7 +36,8 @@ module Text.Pandoc.BCP47 ( ) where import Control.Monad (guard) -import Data.Char (isAscii, isLetter, isUpper, isLower, toUpper, toLower) +import Data.Char (isAscii, isLetter, isUpper, isLower, toUpper, toLower, + isAlphaNum) import Data.List (intercalate) import Text.Pandoc.Definition import Text.Pandoc.Class (PandocMonad, report) @@ -78,7 +79,9 @@ toLang (Just s) = return Nothing Right l -> return (Just l) --- | Parse a BCP 47 string as a Lang. +-- | Parse a BCP 47 string as a Lang. Currently we parse +-- extensions and private-use fields as "variants," even +-- though officially they aren't. parseBCP47 :: String -> Either String Lang parseBCP47 lang = case P.parse bcp47 "lang" lang of @@ -88,8 +91,8 @@ parseBCP47 lang = language <- pLanguage script <- P.option "" pScript region <- P.option "" pRegion - variants <- P.many pVariant - () <$ P.char '-' P.<|> P.eof + variants <- P.many (pVariant P.<|> pExtension P.<|> pPrivateUse) + P.eof return $ Lang{ langLanguage = language , langScript = script , langRegion = region @@ -121,3 +124,16 @@ parseBCP47 lang = then length var >= 5 && length var <= 8 else length var == 4 return $ map toLower var + pExtension = P.try $ do + P.char '-' + cs <- P.many1 $ P.satisfy (\c -> isAscii c && isAlphaNum c) + guard $ length cs >= 2 && length cs <= 8 + return $ map toLower cs + pPrivateUse = P.try $ do + P.char '-' + P.char 'x' + P.char '-' + cs <- P.many1 $ P.satisfy (\c -> isAscii c && isAlphaNum c) + guard $ length cs >= 1 && length cs <= 8 + let var = "x-" ++ cs + return $ map toLower var -- cgit v1.2.3 From b2fe009d8fee618cbcd837976b6f2dea7c0a9837 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 26 Jun 2017 15:04:22 +0200 Subject: LaTeX writer: use BCP47 parser. --- src/Text/Pandoc/Writers/LaTeX.hs | 194 +++++++++++++++++++++------------------ 1 file changed, 105 insertions(+), 89 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 53a67a27a..5d505ed15 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -39,12 +39,13 @@ import Control.Monad.State.Strict import Data.Aeson (FromJSON, object, (.=)) import Data.Char (isAlphaNum, isAscii, isDigit, isLetter, isPunctuation, ord, toLower) -import Data.List (foldl', intercalate, intersperse, isInfixOf, nub, nubBy, +import Data.List (foldl', intercalate, intersperse, isInfixOf, nubBy, stripPrefix, (\\)) import Data.Maybe (catMaybes, fromMaybe, isJust) import Data.Text (Text) import qualified Data.Text as T import Network.URI (unEscapeString) +import Text.Pandoc.BCP47 (Lang(..), toLang, getLang, renderLang) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.Highlighting (formatLaTeXBlock, formatLaTeXInline, highlight, @@ -188,7 +189,8 @@ pandocToLaTeX options (Pandoc meta blocks) = do st <- get titleMeta <- stringToLaTeX TextString $ stringify $ docTitle meta authorsMeta <- mapM (stringToLaTeX TextString . stringify) $ docAuthors meta - let docLangs = nub $ query (extract "lang") blocks + docLangs <- catMaybes <$> + mapM (toLang . Just) (ordNub (query (extract "lang") blocks)) let hasStringValue x = isJust (getField x metadata :: Maybe String) let geometryFromMargins = intercalate [','] $ catMaybes $ map (\(x,y) -> @@ -198,6 +200,18 @@ pandocToLaTeX options (Pandoc meta blocks) = do ,("tmargin","margin-top") ,("bmargin","margin-bottom") ] + let toPolyObj lang = object [ "name" .= T.pack name + , "options" .= T.pack opts ] + where + (name, opts) = toPolyglossia lang + mblang <- toLang $ case getLang options meta of + Just l -> Just l + Nothing | null docLangs -> Nothing + | otherwise -> Just "en" + -- we need a default here since lang is used in template conditionals + + let dirs = query (extract "dir") blocks + let context = defField "toc" (writerTableOfContents options) $ defField "toc-depth" (show (writerTOCDepth options - if stBook st @@ -235,26 +249,20 @@ pandocToLaTeX options (Pandoc meta blocks) = do Biblatex -> defField "biblio-title" biblioTitle . defField "biblatex" True _ -> id) $ - -- set lang to something so polyglossia/babel is included - defField "lang" (if null docLangs then ""::String else "en") $ - defField "otherlangs" docLangs $ defField "colorlinks" (any hasStringValue ["citecolor", "urlcolor", "linkcolor", "toccolor"]) $ - defField "dir" (if (null $ query (extract "dir") blocks) - then ""::String - else "ltr") $ + (if null dirs + then id + else defField "dir" ("ltr" :: String)) $ defField "section-titles" True $ defField "geometry" geometryFromMargins $ metadata - let toPolyObj lang = object [ "name" .= T.pack name - , "options" .= T.pack opts ] - where - (name, opts) = toPolyglossia lang - let lang = maybe [] (splitBy (=='-')) $ getField "lang" context - otherlangs = maybe [] (map $ splitBy (=='-')) $ getField "otherlangs" context let context' = - defField "babel-lang" (toBabel lang) - $ defField "babel-otherlangs" (map toBabel otherlangs) + -- note: lang is used in some conditionals in the template, + -- so we need to set it if we have any babel/polyglossia: + maybe id (defField "lang" . renderLang) mblang + $ maybe id (defField "babel-lang" . toBabel) mblang + $ defField "babel-otherlangs" (map toBabel docLangs) $ defField "babel-newcommands" (concatMap (\(poly, babel) -> -- \textspanish and \textgalician are already used by babel -- save them as \oritext... and let babel use that @@ -274,16 +282,12 @@ pandocToLaTeX options (Pandoc meta blocks) = do -- eliminate duplicates that have same polyglossia name $ nubBy (\a b -> fst a == fst b) -- find polyglossia and babel names of languages used in the document - $ map (\l -> - let lng = splitBy (=='-') l - in (fst $ toPolyglossia lng, toBabel lng) - ) - docLangs ) - $ defField "polyglossia-lang" (toPolyObj lang) - $ defField "polyglossia-otherlangs" (map toPolyObj otherlangs) - $ defField "latex-dir-rtl" (case (getField "dir" context)::Maybe String of - Just "rtl" -> True - _ -> False) + $ map (\l -> (fst $ toPolyglossia l, toBabel l)) docLangs + ) + $ maybe id (defField "polyglossia-lang" . toPolyObj) mblang + $ defField "polyglossia-otherlangs" (map toPolyObj docLangs) + $ defField "latex-dir-rtl" + (getField "dir" context == Just ("rtl" :: String)) $ context case writerTemplate options of Nothing -> return main @@ -443,11 +447,12 @@ blockToLaTeX (Div (identifier,classes,kvs) bs) = do -> "\\leavevmode" <> linkAnchor' <> "%" _ -> linkAnchor' let align dir txt = inCmd "begin" dir $$ txt $$ inCmd "end" dir + lang <- toLang $ lookup "lang" kvs let wrapDir = case lookup "dir" kvs of Just "rtl" -> align "RTL" Just "ltr" -> align "LTR" _ -> id - wrapLang txt = case lookup "lang" kvs of + wrapLang txt = case lang of Just lng -> let (l, o) = toPolyglossiaEnv lng ops = if null o then "" @@ -918,13 +923,14 @@ inlineToLaTeX :: PandocMonad m -> LW m Doc inlineToLaTeX (Span (id',classes,kvs) ils) = do linkAnchor <- hypertarget False id' empty + lang <- toLang $ lookup "lang" kvs let cmds = ["textup" | "csl-no-emph" `elem` classes] ++ ["textnormal" | "csl-no-strong" `elem` classes || "csl-no-smallcaps" `elem` classes] ++ ["RL" | ("dir", "rtl") `elem` kvs] ++ ["LR" | ("dir", "ltr") `elem` kvs] ++ - (case lookup "lang" kvs of - Just lng -> let (l, o) = toPolyglossia $ splitBy (=='-') lng + (case lang of + Just lng -> let (l, o) = toPolyglossia lng ops = if null o then "" else ("[" ++ o ++ "]") in ["text" ++ l ++ ops] Nothing -> []) @@ -1254,85 +1260,95 @@ lookKey :: String -> Attr -> [String] lookKey key (_,_,kvs) = maybe [] words $ lookup key kvs -- In environments \Arabic instead of \arabic is used -toPolyglossiaEnv :: String -> (String, String) +toPolyglossiaEnv :: Lang -> (String, String) toPolyglossiaEnv l = - case toPolyglossia $ (splitBy (=='-')) l of + case toPolyglossia l of ("arabic", o) -> ("Arabic", o) x -> x -- Takes a list of the constituents of a BCP 47 language code and -- converts it to a Polyglossia (language, options) tuple -- http://mirrors.ctan.org/macros/latex/contrib/polyglossia/polyglossia.pdf -toPolyglossia :: [String] -> (String, String) -toPolyglossia ("ar":"DZ":_) = ("arabic", "locale=algeria") -toPolyglossia ("ar":"IQ":_) = ("arabic", "locale=mashriq") -toPolyglossia ("ar":"JO":_) = ("arabic", "locale=mashriq") -toPolyglossia ("ar":"LB":_) = ("arabic", "locale=mashriq") -toPolyglossia ("ar":"LY":_) = ("arabic", "locale=libya") -toPolyglossia ("ar":"MA":_) = ("arabic", "locale=morocco") -toPolyglossia ("ar":"MR":_) = ("arabic", "locale=mauritania") -toPolyglossia ("ar":"PS":_) = ("arabic", "locale=mashriq") -toPolyglossia ("ar":"SY":_) = ("arabic", "locale=mashriq") -toPolyglossia ("ar":"TN":_) = ("arabic", "locale=tunisia") -toPolyglossia ("de":"1901":_) = ("german", "spelling=old") -toPolyglossia ("de":"AT":"1901":_) = ("german", "variant=austrian, spelling=old") -toPolyglossia ("de":"AT":_) = ("german", "variant=austrian") -toPolyglossia ("de":"CH":"1901":_) = ("german", "variant=swiss, spelling=old") -toPolyglossia ("de":"CH":_) = ("german", "variant=swiss") -toPolyglossia ("de":_) = ("german", "") -toPolyglossia ("dsb":_) = ("lsorbian", "") -toPolyglossia ("el":"polyton":_) = ("greek", "variant=poly") -toPolyglossia ("en":"AU":_) = ("english", "variant=australian") -toPolyglossia ("en":"CA":_) = ("english", "variant=canadian") -toPolyglossia ("en":"GB":_) = ("english", "variant=british") -toPolyglossia ("en":"NZ":_) = ("english", "variant=newzealand") -toPolyglossia ("en":"UK":_) = ("english", "variant=british") -toPolyglossia ("en":"US":_) = ("english", "variant=american") -toPolyglossia ("grc":_) = ("greek", "variant=ancient") -toPolyglossia ("hsb":_) = ("usorbian", "") -toPolyglossia ("la":"x":"classic":_) = ("latin", "variant=classic") -toPolyglossia ("sl":_) = ("slovenian", "") -toPolyglossia x = (commonFromBcp47 x, "") +toPolyglossia :: Lang -> (String, String) +toPolyglossia (Lang "ar" _ "DZ" _) = ("arabic", "locale=algeria") +toPolyglossia (Lang "ar" _ "IQ" _) = ("arabic", "locale=mashriq") +toPolyglossia (Lang "ar" _ "JO" _) = ("arabic", "locale=mashriq") +toPolyglossia (Lang "ar" _ "LB" _) = ("arabic", "locale=mashriq") +toPolyglossia (Lang "ar" _ "LY" _) = ("arabic", "locale=libya") +toPolyglossia (Lang "ar" _ "MA" _) = ("arabic", "locale=morocco") +toPolyglossia (Lang "ar" _ "MR" _) = ("arabic", "locale=mauritania") +toPolyglossia (Lang "ar" _ "PS" _) = ("arabic", "locale=mashriq") +toPolyglossia (Lang "ar" _ "SY" _) = ("arabic", "locale=mashriq") +toPolyglossia (Lang "ar" _ "TN" _) = ("arabic", "locale=tunisia") +toPolyglossia (Lang "de" _ _ vars) + | "1901" `elem` vars = ("german", "spelling=old") +toPolyglossia (Lang "de" _ "AT" vars) + | "1901" `elem` vars = ("german", "variant=austrian, spelling=old") +toPolyglossia (Lang "de" _ "AT" _) = ("german", "variant=austrian") +toPolyglossia (Lang "de" _ "CH" vars) + | "1901" `elem` vars = ("german", "variant=swiss, spelling=old") +toPolyglossia (Lang "de" _ "CH" _) = ("german", "variant=swiss") +toPolyglossia (Lang "de" _ _ _) = ("german", "") +toPolyglossia (Lang "dsb" _ _ _) = ("lsorbian", "") +toPolyglossia (Lang "el" _ "polyton" _) = ("greek", "variant=poly") +toPolyglossia (Lang "en" _ "AU" _) = ("english", "variant=australian") +toPolyglossia (Lang "en" _ "CA" _) = ("english", "variant=canadian") +toPolyglossia (Lang "en" _ "GB" _) = ("english", "variant=british") +toPolyglossia (Lang "en" _ "NZ" _) = ("english", "variant=newzealand") +toPolyglossia (Lang "en" _ "UK" _) = ("english", "variant=british") +toPolyglossia (Lang "en" _ "US" _) = ("english", "variant=american") +toPolyglossia (Lang "grc" _ _ _) = ("greek", "variant=ancient") +toPolyglossia (Lang "hsb" _ _ _) = ("usorbian", "") +toPolyglossia (Lang "la" _ _ vars) + | "x-classic" `elem` vars = ("latin", "variant=classic") +toPolyglossia (Lang "sl" _ _ _) = ("slovenian", "") +toPolyglossia x = (commonFromBcp47 x, "") -- Takes a list of the constituents of a BCP 47 language code and -- converts it to a Babel language string. -- http://mirrors.ctan.org/macros/latex/required/babel/base/babel.pdf -- List of supported languages (slightly outdated): -- http://tug.ctan.org/language/hyph-utf8/doc/generic/hyph-utf8/hyphenation.pdf -toBabel :: [String] -> String -toBabel ("de":"1901":_) = "german" -toBabel ("de":"AT":"1901":_) = "austrian" -toBabel ("de":"AT":_) = "naustrian" -toBabel ("de":"CH":"1901":_) = "swissgerman" -toBabel ("de":"CH":_) = "nswissgerman" -toBabel ("de":_) = "ngerman" -toBabel ("dsb":_) = "lowersorbian" -toBabel ("el":"polyton":_) = "polutonikogreek" -toBabel ("en":"AU":_) = "australian" -toBabel ("en":"CA":_) = "canadian" -toBabel ("en":"GB":_) = "british" -toBabel ("en":"NZ":_) = "newzealand" -toBabel ("en":"UK":_) = "british" -toBabel ("en":"US":_) = "american" -toBabel ("fr":"CA":_) = "canadien" -toBabel ("fra":"aca":_) = "acadian" -toBabel ("grc":_) = "polutonikogreek" -toBabel ("hsb":_) = "uppersorbian" -toBabel ("la":"x":"classic":_) = "classiclatin" -toBabel ("sl":_) = "slovene" -toBabel x = commonFromBcp47 x +toBabel :: Lang -> String +toBabel (Lang "de" _ "AT" vars) + | "1901" `elem` vars = "austrian" + | otherwise = "naustrian" +toBabel (Lang "de" _ "CH" vars) + | "1901" `elem` vars = "swissgerman" + | otherwise = "nswissgerman" +toBabel (Lang "de" _ _ vars) + | "1901" `elem` vars = "german" + | otherwise = "ngerman" +toBabel (Lang "dsb" _ _ _) = "lowersorbian" +toBabel (Lang "el" _ _ vars) + | "polyton" `elem` vars = "polutonikogreek" +toBabel (Lang "en" _ "AU" _) = "australian" +toBabel (Lang "en" _ "CA" _) = "canadian" +toBabel (Lang "en" _ "GB" _) = "british" +toBabel (Lang "en" _ "NZ" _) = "newzealand" +toBabel (Lang "en" _ "UK" _) = "british" +toBabel (Lang "en" _ "US" _) = "american" +toBabel (Lang "fr" _ "CA" _) = "canadien" +toBabel (Lang "fra" _ _ vars) + | "aca" `elem` vars = "acadian" +toBabel (Lang "grc" _ _ _) = "polutonikogreek" +toBabel (Lang "hsb" _ _ _) = "uppersorbian" +toBabel (Lang "la" _ _ vars) + | "x-classic" `elem` vars = "classiclatin" +toBabel (Lang "sl" _ _ _) = "slovene" +toBabel x = commonFromBcp47 x -- Takes a list of the constituents of a BCP 47 language code -- and converts it to a string shared by Babel and Polyglossia. -- https://tools.ietf.org/html/bcp47#section-2.1 -commonFromBcp47 :: [String] -> String -commonFromBcp47 [] = "" -commonFromBcp47 ("pt":"BR":_) = "brazil" +commonFromBcp47 :: Lang -> String +commonFromBcp47 (Lang "pt" _ "BR" _) = "brazil" -- Note: documentation says "brazilian" works too, but it doesn't seem to work -- on some systems. See #2953. -commonFromBcp47 ("sr":"Cyrl":_) = "serbianc" -commonFromBcp47 ("zh":"Latn":"pinyin":_) = "pinyin" -commonFromBcp47 x = fromIso $ head x +commonFromBcp47 (Lang "sr" "Cyrl" _ _) = "serbianc" +commonFromBcp47 (Lang "zh" "Latn" _ vars) + | "pinyin" `elem` vars = "pinyin" +commonFromBcp47 (Lang l _ _ _) = fromIso l where fromIso "af" = "afrikaans" fromIso "am" = "amharic" -- cgit v1.2.3 From fa515e46f36fa3e73b26b89b721a2de1738cf4e3 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 26 Jun 2017 16:07:45 +0300 Subject: Muse writer: fix hlint errors (#3764) --- src/Text/Pandoc/Writers/Muse.hs | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index 3d9e232ae..b386a85b9 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -97,8 +97,7 @@ pandocToMuse (Pandoc meta blocks) = do body <- blockListToMuse blocks notes <- liftM (reverse . stNotes) get >>= notesToMuse let main = render colwidth $ body $+$ notes - let context = defField "body" main - $ metadata + let context = defField "body" main metadata case writerTemplate opts of Nothing -> return main Just tpl -> renderTemplate' tpl context @@ -129,14 +128,14 @@ blockToMuse (Para inlines) = do blockToMuse (LineBlock lns) = do let splitStanza [] = [] splitStanza xs = case break (== mempty) xs of - (l, []) -> l : [] + (l, []) -> [l] (l, _:r) -> l : splitStanza r let joinWithLinefeeds = nowrap . mconcat . intersperse cr let joinWithBlankLines = mconcat . intersperse blankline let prettyfyStanza ls = joinWithLinefeeds <$> mapM inlineListToMuse ls contents <- joinWithBlankLines <$> mapM prettyfyStanza (splitStanza lns) return $ blankline $$ "<verse>" $$ contents $$ "</verse>" <> blankline -blockToMuse (CodeBlock (_,_,_) str) = do +blockToMuse (CodeBlock (_,_,_) str) = return $ "<example>" $$ text str $$ "</example>" $$ blankline blockToMuse (RawBlock (Format format) str) = return $ blankline $$ "<literal style=\"" <> text format <> "\">" $$ @@ -154,11 +153,10 @@ blockToMuse (OrderedList (start, style, _) items) = do let maxMarkerLength = maximum $ map length markers let markers' = map (\m -> let s = maxMarkerLength - length m in m ++ replicate s ' ') markers - contents <- mapM (\(item, num) -> orderedListItemToMuse item num) $ - zip markers' items + contents <- zipWithM orderedListItemToMuse markers' items -- ensure that sublists have preceding blank line topLevel <- gets stTopLevel - return $ cr $$ ((if topLevel then nest 1 else id) $ vcat contents) $$ blankline + return $ cr $$ (if topLevel then nest 1 else id) (vcat contents) $$ blankline where orderedListItemToMuse :: PandocMonad m => String -- ^ marker for list item -> [Block] -- ^ list item (list of blocks) @@ -170,7 +168,7 @@ blockToMuse (BulletList items) = do contents <- mapM bulletListItemToMuse items -- ensure that sublists have preceding blank line topLevel <- gets stTopLevel - return $ cr $$ ((if topLevel then nest 1 else id) $ vcat contents) $$ blankline + return $ cr $$ (if topLevel then nest 1 else id) (vcat contents) $$ blankline where bulletListItemToMuse :: PandocMonad m => [Block] -> StateT WriterState m Doc @@ -179,7 +177,7 @@ blockToMuse (BulletList items) = do return $ hang 2 "- " contents blockToMuse (DefinitionList items) = do contents <- mapM definitionListItemToMuse items - return $ cr $$ (nest 1 $ vcat $ contents) $$ blankline + return $ cr $$ nest 1 (vcat contents) $$ blankline where definitionListItemToMuse :: PandocMonad m => ([Inline], [[Block]]) -> StateT WriterState m Doc @@ -218,8 +216,8 @@ blockToMuse (Table caption _ _ headers rows) = do -- FIXME: Muse doesn't allow blocks with height more than 1. let hpipeBlocks sep blocks = hcat $ intersperse sep' blocks where h = maximum (1 : map height blocks) - sep' = lblock (length sep) $ vcat (map text $ replicate h sep) - let makeRow sep = (" " <>) . (hpipeBlocks sep . zipWith lblock widthsInChars) + sep' = lblock (length sep) $ vcat (replicate h (text sep)) + let makeRow sep = (" " <>) . hpipeBlocks sep . zipWith lblock widthsInChars let head' = makeRow " || " headers' let rowSeparator = if noHeaders then " | " else " | " rows'' <- mapM (\row -> do cols <- mapM blockListToMuse row @@ -236,9 +234,7 @@ blockToMuse Null = return empty notesToMuse :: PandocMonad m => Notes -> StateT WriterState m Doc -notesToMuse notes = - mapM (\(num, note) -> noteToMuse num note) (zip [1..] notes) >>= - return . vsep +notesToMuse notes = liftM vsep (zipWithM noteToMuse [1 ..] notes) -- | Return Muse representation of a note. noteToMuse :: PandocMonad m @@ -268,7 +264,7 @@ conditionalEscapeString s inlineListToMuse :: PandocMonad m => [Inline] -> StateT WriterState m Doc -inlineListToMuse lst = mapM inlineToMuse lst >>= return . hcat +inlineListToMuse lst = liftM hcat (mapM inlineToMuse lst) -- | Convert Pandoc inline element to Muse. inlineToMuse :: PandocMonad m @@ -316,7 +312,7 @@ inlineToMuse Space = return space inlineToMuse SoftBreak = do wrapText <- gets $ writerWrapText . stOptions return $ if wrapText == WrapPreserve then cr else space -inlineToMuse (Link _ txt (src, _)) = do +inlineToMuse (Link _ txt (src, _)) = case txt of [Str x] | escapeURI x == src -> return $ "[[" <> text (escapeLink x) <> "]]" @@ -340,7 +336,7 @@ inlineToMuse (Note contents) = do -- add to notes in state notes <- gets stNotes modify $ \st -> st { stNotes = contents:notes } - let ref = show $ (length notes) + 1 + let ref = show $ length notes + 1 return $ "[" <> text ref <> "]" inlineToMuse (Span (_,name:_,_) inlines) = do contents <- inlineListToMuse inlines -- cgit v1.2.3 From 75f4e41d7d292e011a83d06efebc356060ea812b Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 26 Jun 2017 16:07:59 +0200 Subject: Use `table-of-contents` for contents of toc, make `toc` a boolean. Changed markdown, rtf, and HTML-based templates accordingly. This allows you to set `toc: true` in the metadata; this previously produced strange results in some output formats. Closes #2872. For backwards compatibility, `toc` is still set to the toc contents. But it is recommended that you update templates to use `table-of-contents` for the toc contents and `toc` for a boolean flag. --- src/Text/Pandoc/Writers/HTML.hs | 8 ++++++-- src/Text/Pandoc/Writers/Markdown.hs | 10 +++++++--- src/Text/Pandoc/Writers/RTF.hs | 6 +++++- 3 files changed, 18 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 45c878781..451123a6d 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -241,7 +241,7 @@ pandocToHtml opts (Pandoc meta blocks) = do then blocks else prepSlides slideLevel blocks toc <- if writerTableOfContents opts && slideVariant /= S5Slides - then tableOfContents opts sects + then fmap renderHtml' <$> tableOfContents opts sects else return Nothing blocks' <- liftM (mconcat . intersperse (nl opts)) $ mapM (elementToHtml slideLevel opts) sects @@ -292,7 +292,11 @@ pandocToHtml opts (Pandoc meta blocks) = do MathJax _ -> True _ -> False) $ defField "quotes" (stQuotes st) $ - maybe id (defField "toc" . renderHtml') toc $ + -- for backwards compatibility we populate toc + -- with the contents of the toc, rather than a + -- boolean: + maybe id (defField "toc") toc $ + maybe id (defField "table-of-contents") toc $ defField "author-meta" authsMeta $ maybe id (defField "date-meta") (normalizeDate dateMeta) $ defField "pagetitle" (stringifyHTML (docTitle meta)) $ diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 6c7e662bf..b951288bc 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -209,8 +209,8 @@ pandocToMarkdown opts (Pandoc meta blocks) = do Nothing -> empty let headerBlocks = filter isHeaderBlock blocks toc <- if writerTableOfContents opts - then tableOfContents opts headerBlocks - else return empty + then render' <$> tableOfContents opts headerBlocks + else return "" -- Strip off final 'references' header if markdown citations enabled let blocks' = if isEnabled Ext_citations opts then case reverse blocks of @@ -220,7 +220,11 @@ pandocToMarkdown opts (Pandoc meta blocks) = do body <- blockListToMarkdown opts blocks' notesAndRefs' <- notesAndRefs opts let main = render' $ body <> notesAndRefs' - let context = defField "toc" (render' toc) + let context = -- for backwards compatibility we populate toc + -- with the contents of the toc, rather than a + -- boolean: + defField "toc" toc + $ defField "table-of-contents" toc $ defField "body" main $ (if isNullMeta meta then id diff --git a/src/Text/Pandoc/Writers/RTF.hs b/src/Text/Pandoc/Writers/RTF.hs index 6666f6549..48d31c7bf 100644 --- a/src/Text/Pandoc/Writers/RTF.hs +++ b/src/Text/Pandoc/Writers/RTF.hs @@ -122,7 +122,11 @@ writeRTF options doc = do let context = defField "body" body $ defField "spacer" spacer $ (if writerTableOfContents options - then defField "toc" toc + then defField "table-of-contents" toc + -- for backwards compatibility, + -- we populate toc with the contents + -- of the toc rather than a boolean: + . defField "toc" toc else id) $ metadata T.pack <$> -- cgit v1.2.3 From 19d9482fc400cf486547b6a670c946d3634401cf Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 26 Jun 2017 16:46:56 +0200 Subject: OpenDocument/ODT writer: Added support for table of contents. Closes #2836. Thanks to @anayrat. --- src/Text/Pandoc/Writers/OpenDocument.hs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index 6c53ab4ab..ed3dabb87 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -220,6 +220,7 @@ writeOpenDocument opts (Pandoc meta blocks) = do let listStyles = map listStyle (stListStyles s) let automaticStyles = vcat $ reverse $ styles ++ listStyles let context = defField "body" body + $ defField "toc" (writerTableOfContents opts) $ defField "automatic-styles" (render' automaticStyles) $ metadata case writerTemplate opts of -- cgit v1.2.3 From 460b6c470bae26f31d7c0d72b85aef8eb254b7f7 Mon Sep 17 00:00:00 2001 From: bucklereed <horridimpfoobarbaz@chammy.info> Date: Tue, 27 Jun 2017 09:19:37 +0100 Subject: HTML reader: Use the lang value of <html> to set the lang meta value. (#3765) * HTML reader: Use the lang value of <html> to set the lang meta value. * Fix for pre-AMP environments. --- src/Text/Pandoc/Readers/HTML.hs | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 301afa207..b07b65019 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -53,6 +53,7 @@ import Text.Pandoc.Logging import Text.Pandoc.Parsing hiding ((<|>)) import Text.Pandoc.Walk import qualified Data.Map as M +import Data.Foldable ( for_ ) import Data.Maybe ( fromMaybe, isJust) import Data.List ( intercalate, isPrefixOf ) import Data.Char ( isDigit, isLetter, isAlphaNum ) @@ -134,6 +135,13 @@ type HTMLParser m s = ParserT s HTMLState (ReaderT HTMLLocal m) type TagParser m = HTMLParser m [Tag Text] +pHtml :: PandocMonad m => TagParser m Blocks +pHtml = try $ do + (TagOpen "html" attr) <- lookAhead $ pAnyTag + for_ (lookup "lang" attr) $ + updateState . B.setMeta "lang" . B.text . T.unpack + pInTags "html" block + pBody :: PandocMonad m => TagParser m Blocks pBody = pInTags "body" block @@ -175,6 +183,7 @@ block = do , pList , pHrule , pTable + , pHtml , pHead , pBody , pDiv -- cgit v1.2.3 From a868b238f253423281b2648896f184e7cdc05014 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 27 Jun 2017 12:42:56 +0200 Subject: Docx writer: Allow 9 list levels. Closes #3519. --- src/Text/Pandoc/Writers/Docx.hs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 06318b20c..fb6b2013a 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -657,6 +657,9 @@ mkNumbering lists = do elts <- mapM mkAbstractNum (ordNub lists) return $ elts ++ zipWith mkNum lists [baseListId..(baseListId + length lists - 1)] +maxListLevel :: Int +maxListLevel = 8 + mkNum :: ListMarker -> Int -> Element mkNum marker numid = mknode "w:num" [("w:numId",show numid)] @@ -666,7 +669,8 @@ mkNum marker numid = BulletMarker -> [] NumberMarker _ _ start -> map (\lvl -> mknode "w:lvlOverride" [("w:ilvl",show (lvl :: Int))] - $ mknode "w:startOverride" [("w:val",show start)] ()) [0..6] + $ mknode "w:startOverride" [("w:val",show start)] ()) + [0..maxListLevel] mkAbstractNum :: (PandocMonad m) => ListMarker -> m Element mkAbstractNum marker = do @@ -675,7 +679,8 @@ mkAbstractNum marker = do return $ mknode "w:abstractNum" [("w:abstractNumId",listMarkerToId marker)] $ mknode "w:nsid" [("w:val", printf "%8x" nsid)] () : mknode "w:multiLevelType" [("w:val","multilevel")] () - : map (mkLvl marker) [0..6] + : map (mkLvl marker) + [0..maxListLevel] mkLvl :: ListMarker -> Int -> Element mkLvl marker lvl = @@ -706,7 +711,7 @@ mkLvl marker lvl = bulletFor 3 = "\x2013" bulletFor 4 = "\x2022" bulletFor 5 = "\x2013" - bulletFor _ = "\x2022" + bulletFor x = bulletFor (x `mod` 6) styleFor UpperAlpha _ = "upperLetter" styleFor LowerAlpha _ = "lowerLetter" styleFor UpperRoman _ = "upperRoman" @@ -718,6 +723,7 @@ mkLvl marker lvl = styleFor DefaultStyle 4 = "decimal" styleFor DefaultStyle 5 = "lowerLetter" styleFor DefaultStyle 6 = "lowerRoman" + styleFor DefaultStyle x = styleFor DefaultStyle (x `mod` 7) styleFor _ _ = "decimal" patternFor OneParen s = s ++ ")" patternFor TwoParens s = "(" ++ s ++ ")" -- cgit v1.2.3 From 563c9c8687a62acc7361fb49126a1d2030f3a11e Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 27 Jun 2017 14:35:03 +0200 Subject: RST reader: Handle chained link definitions. For example, .. _hello: .. _goodbye: example.com Here both `hello` and `goodbye` should link to `example.com`. Fixes the first part of #262. --- src/Text/Pandoc/Readers/RST.hs | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index d13f697b7..c790d5188 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -31,7 +31,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 ) where -import Control.Monad (guard, liftM, mzero, when) +import Control.Monad (guard, liftM, mzero, when, forM_) import Control.Monad.Identity (Identity(..)) import Control.Monad.Except (throwError) import Data.Char (isHexDigit, isSpace, toLower, toUpper) @@ -1054,16 +1054,29 @@ stripTicks = reverse . stripTick . reverse . stripTick where stripTick ('`':xs) = xs stripTick xs = xs +referenceNames :: PandocMonad m => RSTParser m [String] +referenceNames = do + let rn = try $ do + string ".. _" + (_, ref) <- withRaw referenceName + char ':' + return ref + first <- rn + rest <- many (try (blanklines *> rn)) + return (first:rest) + regularKey :: PandocMonad m => RSTParser m () regularKey = try $ do - string ".. _" - (_,ref) <- withRaw referenceName - char ':' + -- we allow several references to the same URL, e.g. + -- .. _hello: + -- .. _goodbye: url.com + refs <- referenceNames src <- targetURI - let key = toKey $ stripTicks ref --TODO: parse width, height, class and name attributes - updateState $ \s -> s { stateKeys = M.insert key ((src,""), nullAttr) $ - stateKeys s } + let keys = map (toKey . stripTicks) refs + forM_ keys $ \key -> + updateState $ \s -> s { stateKeys = M.insert key ((src,""), nullAttr) $ + stateKeys s } headerBlock :: PandocMonad m => RSTParser m [Char] headerBlock = do -- cgit v1.2.3 From 33a29fbf8720c0d7eec40b7014e3f819b05474ef Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 27 Jun 2017 15:03:16 +0200 Subject: RST reader: support anchors. E.g. `hello` .. _hello: paragraph This is supported by putting "paragraph" in a Div with id `hello`. Closes #262. --- src/Text/Pandoc/Readers/RST.hs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index c790d5188..2daf60a89 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -170,7 +170,8 @@ parseRST = do -- go through once just to get list of reference keys and notes -- docMinusKeys is the raw document with blanks where the keys were... docMinusKeys <- concat <$> - manyTill (referenceKey <|> noteBlock <|> citationBlock <|> + manyTill (referenceKey <|> anchorDef <|> + noteBlock <|> citationBlock <|> headerBlock <|> lineClump) eof setInput docMinusKeys setPosition startPos @@ -217,6 +218,7 @@ block = choice [ codeBlock , fieldList , include , directive + , anchor , comment , header , hrule @@ -1072,12 +1074,32 @@ regularKey = try $ do -- .. _goodbye: url.com refs <- referenceNames src <- targetURI + guard $ not (null src) --TODO: parse width, height, class and name attributes let keys = map (toKey . stripTicks) refs forM_ keys $ \key -> updateState $ \s -> s { stateKeys = M.insert key ((src,""), nullAttr) $ stateKeys s } +anchorDef :: PandocMonad m => RSTParser m [Char] +anchorDef = try $ do + (refs, raw) <- withRaw (try (referenceNames <* blanklines)) + let keys = map stripTicks refs + forM_ keys $ \rawkey -> + updateState $ \s -> s { stateKeys = + M.insert (toKey rawkey) (('#':rawkey,""), nullAttr) $ stateKeys s } + -- keep this for 2nd round of parsing, where we'll add the divs (anchor) + return raw + +anchor :: PandocMonad m => RSTParser m Blocks +anchor = try $ do + refs <- referenceNames + blanklines + b <- block + -- put identifier on next block: + let addDiv ref = B.divWith (ref, [], []) + return $ foldr addDiv b refs + headerBlock :: PandocMonad m => RSTParser m [Char] headerBlock = do ((txt, _), raw) <- withRaw (doubleHeader' <|> singleHeader') -- cgit v1.2.3 From 7d9d77ca44afa0c69abfefe07d7b027f81c8f1a0 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 27 Jun 2017 15:25:37 +0200 Subject: Require nonempty alt text for `implicit_figures`. A figure with an empty caption doesn't make sense. Closes #2844. --- src/Text/Pandoc/Readers/Markdown.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 31b51f237..49007ad35 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -1035,7 +1035,8 @@ para = try $ do result' <- result case B.toList result' of [Image attr alt (src,tit)] - | Ext_implicit_figures `extensionEnabled` exts -> + | not (null alt) && + 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) -- cgit v1.2.3 From beb78a552cb3480d55b8eca8c0c77bccd5804506 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert.krewinkel@tourstream.eu> Date: Tue, 27 Jun 2017 17:11:42 +0200 Subject: Text.Pandoc.Lua: simplify filter function runner The code still allowed to pass an arbitrary number of arguments to the filter function, as element properties were passed as function arguments at some point. Now we only pass the element as the single arg, so the code to handle multiple arguments is no longer necessary. --- src/Text/Pandoc/Lua.hs | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 90f72d685..858212df1 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -188,34 +188,20 @@ instance StackValue LuaFilter where push = undefined peek lua idx = fmap (LuaFilter lua) <$> Lua.peek lua idx --- | Helper class for pushing a single value to the stack via a lua function. --- See @pushViaCall@. -class PushViaFilterFunction a where - pushViaFilterFunction' :: LuaState -> LuaFilterFunction -> IO () -> Int -> a - -instance StackValue a => PushViaFilterFunction (IO a) where - pushViaFilterFunction' lua lf pushArgs num = do - pushFilterFunction lua lf - pushArgs - Lua.call lua num 1 - mbres <- Lua.peek lua (-1) - case mbres of - Nothing -> throwIO $ LuaException - ("Error while trying to get a filter's return " - ++ "value from lua stack.") - Just res -> res <$ Lua.pop lua 1 - -instance (StackValue a, PushViaFilterFunction b) => - PushViaFilterFunction (a -> b) where - pushViaFilterFunction' lua lf pushArgs num x = - pushViaFilterFunction' lua lf (pushArgs *> push lua x) (num + 1) - -- | Push a value to the stack via a lua filter function. The filter function is -- called with all arguments that are passed to this function and is expected to -- return a single value. -runFilterFunction :: PushViaFilterFunction a - => LuaState -> LuaFilterFunction -> a -runFilterFunction lua lf = pushViaFilterFunction' lua lf (return ()) 0 +runFilterFunction :: StackValue a => LuaState -> LuaFilterFunction -> a -> IO a +runFilterFunction lua lf x = do + pushFilterFunction lua lf + Lua.push lua x + Lua.call lua 1 1 + mbres <- Lua.peek lua (-1) + case mbres of + Nothing -> throwIO $ LuaException + ("Error while trying to get a filter's return " + ++ "value from lua stack.") + Just res -> res <$ Lua.pop lua 1 -- | Push the filter function to the top of the stack. pushFilterFunction :: Lua.LuaState -> LuaFilterFunction -> IO () -- cgit v1.2.3 From 4282abbd0781cf5e6731a9b43dc8cfeb1dca58fa Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert.krewinkel@tourstream.eu> Date: Tue, 27 Jun 2017 17:11:42 +0200 Subject: Text.Pandoc.Lua: keep element unchanged if filter returns nil This was suggested by jgm and is consistent with the behavior of other filtering libraries. --- src/Text/Pandoc/Lua.hs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 858212df1..3770880f3 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -189,19 +189,24 @@ instance StackValue LuaFilter where peek lua idx = fmap (LuaFilter lua) <$> Lua.peek lua idx -- | Push a value to the stack via a lua filter function. The filter function is --- called with all arguments that are passed to this function and is expected to --- return a single value. +-- called with given element as argument and is expected to return an element. +-- Alternatively, the function can return nothing or nil, in which case the +-- element is left unchanged. runFilterFunction :: StackValue a => LuaState -> LuaFilterFunction -> a -> IO a runFilterFunction lua lf x = do pushFilterFunction lua lf Lua.push lua x Lua.call lua 1 1 - mbres <- Lua.peek lua (-1) - case mbres of - Nothing -> throwIO $ LuaException - ("Error while trying to get a filter's return " - ++ "value from lua stack.") - Just res -> res <$ Lua.pop lua 1 + resType <- Lua.ltype lua (-1) + case resType of + Lua.TNIL -> Lua.pop lua 1 *> return x + _ -> do + mbres <- Lua.peek lua (-1) + case mbres of + Nothing -> throwIO $ LuaException + ("Error while trying to get a filter's return " + ++ "value from lua stack.") + Just res -> res <$ Lua.pop lua 1 -- | Push the filter function to the top of the stack. pushFilterFunction :: Lua.LuaState -> LuaFilterFunction -> IO () -- cgit v1.2.3 From f5f84859230568ddafb2e7e23b5d9b3e98fdbba5 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert.krewinkel@tourstream.eu> Date: Tue, 27 Jun 2017 17:55:47 +0200 Subject: Text.Pandoc.Lua: catch lua errors in filter functions Replace lua errors with `LuaException`s. --- src/Text/Pandoc/Lua.hs | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 3770880f3..2ee8d0847 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -196,17 +196,26 @@ runFilterFunction :: StackValue a => LuaState -> LuaFilterFunction -> a -> IO a runFilterFunction lua lf x = do pushFilterFunction lua lf Lua.push lua x - Lua.call lua 1 1 - resType <- Lua.ltype lua (-1) - case resType of - Lua.TNIL -> Lua.pop lua 1 *> return x - _ -> do - mbres <- Lua.peek lua (-1) - case mbres of - Nothing -> throwIO $ LuaException - ("Error while trying to get a filter's return " - ++ "value from lua stack.") - Just res -> res <$ Lua.pop lua 1 + z <- Lua.pcall lua 1 1 0 + if (z /= 0) + then do + msg <- Lua.peek lua (-1) + let prefix = "Error while running filter function: " + throwIO . LuaException $ + case msg of + Nothing -> prefix ++ "could not read error message" + Just msg' -> prefix ++ msg' + else do + resType <- Lua.ltype lua (-1) + case resType of + Lua.TNIL -> Lua.pop lua 1 *> return x + _ -> do + mbres <- Lua.peek lua (-1) + case mbres of + Nothing -> throwIO $ LuaException + ("Error while trying to get a filter's return " + ++ "value from lua stack.") + Just res -> res <$ Lua.pop lua 1 -- | Push the filter function to the top of the stack. pushFilterFunction :: Lua.LuaState -> LuaFilterFunction -> IO () -- cgit v1.2.3 From cd690d04015431e89feefa7f68e9609efab1f16b Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 28 Jun 2017 14:20:53 +0200 Subject: LaTeX writer: fixed detection of otherlangs. We weren't recursing into inline contexts. Closes #3770. --- src/Text/Pandoc/Writers/LaTeX.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 5d505ed15..07ddddcb0 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -1245,9 +1245,9 @@ mbBraced x = if not (all isAlphaNum x) -- Extract a key from divs and spans extract :: String -> Block -> [String] extract key (Div attr _) = lookKey key attr -extract key (Plain ils) = concatMap (extractInline key) ils -extract key (Para ils) = concatMap (extractInline key) ils -extract key (Header _ _ ils) = concatMap (extractInline key) ils +extract key (Plain ils) = query (extractInline key) ils +extract key (Para ils) = query (extractInline key) ils +extract key (Header _ _ ils) = query (extractInline key) ils extract _ _ = [] -- Extract a key from spans -- cgit v1.2.3 From 79cc56726c7e876314c7c21f5bb5f65084e7d8b7 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Wed, 28 Jun 2017 15:32:53 +0300 Subject: Muse reader: parse indented blockquotes (#3769) --- src/Text/Pandoc/Readers/Muse.hs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index eb0769e0b..a51306347 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -187,6 +187,7 @@ blockElements = choice [ comment , orderedList , table , commentTag + , indentedBlock , noteBlock ] @@ -209,7 +210,8 @@ separator = try $ do header :: PandocMonad m => MuseParser m (F Blocks) header = try $ do st <- stateParserContext <$> getState - getPosition >>= \pos -> guard (st == NullState && sourceColumn pos == 1) + q <- stateQuoteContext <$> getState + getPosition >>= \pos -> guard (st == NullState && q == NoQuote && sourceColumn pos == 1) level <- liftM length $ many1 $ char '*' guard $ level <= 5 skipSpaces @@ -248,6 +250,25 @@ quoteTag = blockTag B.blockQuote "quote" commentTag :: PandocMonad m => MuseParser m (F Blocks) commentTag = parseHtmlContent "comment" block >> return mempty +-- Indented block is either center, right or quote +indentedLine :: PandocMonad m => MuseParser m (Int, String) +indentedLine = try $ do + indent <- length <$> many1 spaceChar + line <- anyLine + return (indent, line) + +rawIndentedBlock :: PandocMonad m => MuseParser m (Int, String) +rawIndentedBlock = try $ do + lns <- many1 indentedLine + let indent = minimum $ map fst lns + return (indent, unlines $ map snd lns) + +indentedBlock :: PandocMonad m => MuseParser m (F Blocks) +indentedBlock = try $ do + (indent, raw) <- rawIndentedBlock + contents <- withQuoteContext InDoubleQuote $ parseFromString parseBlocks raw + return $ (if indent >= 2 && indent < 6 then B.blockQuote else id) <$> contents + para :: PandocMonad m => MuseParser m (F Blocks) para = liftM B.para . trimInlinesF . mconcat <$> many1Till inline endOfParaElement where -- cgit v1.2.3 From 2902260b636b36134c0157e32291900603e1011d Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 28 Jun 2017 15:07:35 +0200 Subject: Make `papersize: a4` work regardless of the case of `a4`. It is converted to `a4` in LaTeX and `A4` in ConTeXt. --- src/Text/Pandoc/Writers/ConTeXt.hs | 5 +++++ src/Text/Pandoc/Writers/LaTeX.hs | 4 ++++ 2 files changed, 9 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index 7886bc052..3c901cab6 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} {- Copyright (C) 2007-2017 John MacFarlane <jgm@berkeley.edu> @@ -103,6 +104,10 @@ pandocToConTeXt options (Pandoc meta blocks) = do $ defField "layout" layoutFromMargins $ defField "number-sections" (writerNumberSections options) $ maybe id (defField "context-lang") mblang + $ (case getField "papersize" metadata of + Just ("a4" :: String) -> resetField "papersize" + ("A4" :: String) + _ -> id) $ metadata let context' = defField "context-dir" (toContextDir $ getField "dir" context) context diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 07ddddcb0..55ecda819 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -256,6 +256,10 @@ pandocToLaTeX options (Pandoc meta blocks) = do else defField "dir" ("ltr" :: String)) $ defField "section-titles" True $ defField "geometry" geometryFromMargins $ + (case getField "papersize" metadata of + Just ("A4" :: String) -> resetField "papersize" + ("a4" :: String) + _ -> id) $ metadata let context' = -- note: lang is used in some conditionals in the template, -- cgit v1.2.3 From 6ad74815f66cb36ec4039c597b38473db853eb6c Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 29 Jun 2017 14:00:26 +0200 Subject: Text.Pandoc.Lua: use generics to reduce boilerplate. I tested this with the str.lua filter on MANUAL.txt, and I could see no significant performance degradation. Doing things this way will ease maintenance, as we won't have to manually modify this module when types change. @tarleb, do we really need special cases for things like DoubleQuoted and InlineMath? --- src/Text/Pandoc/Lua.hs | 35 +++-------------------------------- 1 file changed, 3 insertions(+), 32 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 2ee8d0847..85a080277 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -36,6 +36,7 @@ module Text.Pandoc.Lua ( LuaException(..), import Control.Exception import Control.Monad (unless, when, (>=>)) import Control.Monad.Trans (MonadIO (..)) +import Data.Data (toConstr) import Data.Map (Map) import Data.Typeable (Typeable) import Scripting.Lua (LuaState, StackValue (..)) @@ -129,21 +130,7 @@ execBlockLuaFilter lua fnMap x = do case Map.lookup filterFnName fnMap of Nothing -> return x Just fn -> runFilterFunction lua fn x - case x of - BlockQuote{} -> tryFilter "BlockQuote" - BulletList{} -> tryFilter "BulletList" - CodeBlock{} -> tryFilter "CodeBlock" - DefinitionList{} -> tryFilter "DefinitionList" - Div{} -> tryFilter "Div" - Header{} -> tryFilter "Header" - HorizontalRule -> tryFilter "HorizontalRule" - LineBlock{} -> tryFilter "LineBlock" - Null -> tryFilter "Null" - Para{} -> tryFilter "Para" - Plain{} -> tryFilter "Plain" - RawBlock{} -> tryFilter "RawBlock" - OrderedList{} -> tryFilter "OrderedList" - Table{} -> tryFilter "Table" + tryFilter (show (toConstr x)) execInlineLuaFilter :: LuaState -> FunctionMap @@ -161,27 +148,11 @@ execInlineLuaFilter lua fnMap x = do Nothing -> tryFilterAlternatives alternatives Just fn -> runFilterFunction lua fn x case x of - Cite{} -> tryFilter "Cite" - Code{} -> tryFilter "Code" - Emph{} -> tryFilter "Emph" - Image{} -> tryFilter "Image" - LineBreak -> tryFilter "LineBreak" - Link{} -> tryFilter "Link" Math DisplayMath _ -> tryFilterAlternatives ["DisplayMath", "Math"] Math InlineMath _ -> tryFilterAlternatives ["InlineMath", "Math"] - Note{} -> tryFilter "Note" Quoted DoubleQuote _ -> tryFilterAlternatives ["DoubleQuoted", "Quoted"] Quoted SingleQuote _ -> tryFilterAlternatives ["SingleQuoted", "Quoted"] - RawInline{} -> tryFilter "RawInline" - SmallCaps{} -> tryFilter "SmallCaps" - SoftBreak -> tryFilter "SoftBreak" - Space -> tryFilter "Space" - Span{} -> tryFilter "Span" - Str{} -> tryFilter "Str" - Strikeout{} -> tryFilter "Strikeout" - Strong{} -> tryFilter "Strong" - Subscript{} -> tryFilter "Subscript" - Superscript{} -> tryFilter "Superscript" + _ -> tryFilter (show (toConstr x)) instance StackValue LuaFilter where valuetype _ = Lua.TTABLE -- cgit v1.2.3 From 5c80aca0e20492eaa31b9280fb5524d76f5e8098 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 29 Jun 2017 14:31:02 +0200 Subject: Text.Pandoc.Lua: refactored to remove duplicated code. --- src/Text/Pandoc/Lua.hs | 59 +++++++++++++++++++++----------------------------- 1 file changed, 25 insertions(+), 34 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 85a080277..3bb11b705 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -104,55 +104,46 @@ newtype LuaFilterFunction = LuaFilterFunction { functionIndex :: Int } execDocLuaFilter :: LuaState -> FunctionMap -> Pandoc -> IO Pandoc -execDocLuaFilter lua fnMap x = do - let docFnName = "Doc" - case Map.lookup docFnName fnMap of - Nothing -> return x - Just fn -> runFilterFunction lua fn x +execDocLuaFilter lua fnMap = tryFilter lua fnMap "Doc" execMetaLuaFilter :: LuaState -> FunctionMap -> Pandoc -> IO Pandoc -execMetaLuaFilter lua fnMap pd@(Pandoc meta blks) = do - let metaFnName = "Meta" - case Map.lookup metaFnName fnMap of - Nothing -> return pd - Just fn -> do - meta' <- runFilterFunction lua fn meta - return $ Pandoc meta' blks +execMetaLuaFilter lua fnMap (Pandoc meta blks) = do + meta' <- tryFilter lua fnMap "Meta" meta + return $ Pandoc meta' blks execBlockLuaFilter :: LuaState -> FunctionMap -> Block -> IO Block execBlockLuaFilter lua fnMap x = do - let tryFilter :: String -> IO Block - tryFilter filterFnName = - case Map.lookup filterFnName fnMap of - Nothing -> return x - Just fn -> runFilterFunction lua fn x - tryFilter (show (toConstr x)) + tryFilter lua fnMap (show (toConstr x)) x + +tryFilter :: StackValue a => LuaState -> FunctionMap -> String -> a -> IO a +tryFilter lua fnMap filterFnName x = + case Map.lookup filterFnName fnMap of + Nothing -> return x + Just fn -> runFilterFunction lua fn x + +tryFilterAlternatives :: StackValue a + => LuaState -> FunctionMap -> [String] -> a -> IO a +tryFilterAlternatives _ _ [] x = return x +tryFilterAlternatives lua fnMap (fnName : alternatives) x = + case Map.lookup fnName fnMap of + Nothing -> tryFilterAlternatives lua fnMap alternatives x + Just fn -> runFilterFunction lua fn x execInlineLuaFilter :: LuaState -> FunctionMap -> Inline -> IO Inline execInlineLuaFilter lua fnMap x = do - let tryFilter :: String -> IO Inline - tryFilter filterFnName = - case Map.lookup filterFnName fnMap of - Nothing -> return x - Just fn -> runFilterFunction lua fn x - let tryFilterAlternatives :: [String] -> IO Inline - tryFilterAlternatives [] = return x - tryFilterAlternatives (fnName : alternatives) = - case Map.lookup fnName fnMap of - Nothing -> tryFilterAlternatives alternatives - Just fn -> runFilterFunction lua fn x + let tryAlt = tryFilterAlternatives lua fnMap case x of - Math DisplayMath _ -> tryFilterAlternatives ["DisplayMath", "Math"] - Math InlineMath _ -> tryFilterAlternatives ["InlineMath", "Math"] - Quoted DoubleQuote _ -> tryFilterAlternatives ["DoubleQuoted", "Quoted"] - Quoted SingleQuote _ -> tryFilterAlternatives ["SingleQuoted", "Quoted"] - _ -> tryFilter (show (toConstr x)) + Math DisplayMath _ -> tryAlt ["DisplayMath", "Math"] x + Math InlineMath _ -> tryAlt ["InlineMath", "Math"] x + Quoted DoubleQuote _ -> tryAlt ["DoubleQuoted", "Quoted"] x + Quoted SingleQuote _ -> tryAlt ["SingleQuoted", "Quoted"] x + _ -> tryFilter lua fnMap (show (toConstr x)) x instance StackValue LuaFilter where valuetype _ = Lua.TTABLE -- cgit v1.2.3 From 780a65f8a87b40d1a9ee269cd7a51699c42d497e Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 29 Jun 2017 15:47:27 +0200 Subject: Lua filters: Remove special treatment of Quoted, Math. No more SingleQuoted, DoubleQuoted, InlineMath, DisplayMath. This makes everything uniform and predictable, though it does open up a difference btw lua filters and custom writers. --- src/Text/Pandoc/Lua.hs | 32 ++++++++------------------------ 1 file changed, 8 insertions(+), 24 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 3bb11b705..fd7bba0ac 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -101,6 +101,12 @@ data LuaFilter = LuaFilter LuaState FunctionMap newtype LuaFilterFunction = LuaFilterFunction { functionIndex :: Int } +tryFilter :: StackValue a => LuaState -> FunctionMap -> String -> a -> IO a +tryFilter lua fnMap filterFnName x = + case Map.lookup filterFnName fnMap of + Nothing -> return x + Just fn -> runFilterFunction lua fn x + execDocLuaFilter :: LuaState -> FunctionMap -> Pandoc -> IO Pandoc @@ -116,34 +122,12 @@ execMetaLuaFilter lua fnMap (Pandoc meta blks) = do execBlockLuaFilter :: LuaState -> FunctionMap -> Block -> IO Block -execBlockLuaFilter lua fnMap x = do - tryFilter lua fnMap (show (toConstr x)) x - -tryFilter :: StackValue a => LuaState -> FunctionMap -> String -> a -> IO a -tryFilter lua fnMap filterFnName x = - case Map.lookup filterFnName fnMap of - Nothing -> return x - Just fn -> runFilterFunction lua fn x - -tryFilterAlternatives :: StackValue a - => LuaState -> FunctionMap -> [String] -> a -> IO a -tryFilterAlternatives _ _ [] x = return x -tryFilterAlternatives lua fnMap (fnName : alternatives) x = - case Map.lookup fnName fnMap of - Nothing -> tryFilterAlternatives lua fnMap alternatives x - Just fn -> runFilterFunction lua fn x +execBlockLuaFilter lua fnMap x = tryFilter lua fnMap (show (toConstr x)) x execInlineLuaFilter :: LuaState -> FunctionMap -> Inline -> IO Inline -execInlineLuaFilter lua fnMap x = do - let tryAlt = tryFilterAlternatives lua fnMap - case x of - Math DisplayMath _ -> tryAlt ["DisplayMath", "Math"] x - Math InlineMath _ -> tryAlt ["InlineMath", "Math"] x - Quoted DoubleQuote _ -> tryAlt ["DoubleQuoted", "Quoted"] x - Quoted SingleQuote _ -> tryAlt ["SingleQuoted", "Quoted"] x - _ -> tryFilter lua fnMap (show (toConstr x)) x +execInlineLuaFilter lua fnMap x = tryFilter lua fnMap (show (toConstr x)) x instance StackValue LuaFilter where valuetype _ = Lua.TTABLE -- cgit v1.2.3 From cb25326fa313690c3c67caa2a8b44642409fd24c Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 29 Jun 2017 17:07:30 +0200 Subject: Text.Pandoc.Lua: more code simplification. Also, now we check before running walkM that the function table actually does contain something relevant. E.g. if your filter just defines Str, there's no need to run walkM for blocks, meta, or the whole document. This should help performance a bit (and it does, in my tests). --- src/Text/Pandoc/Lua.hs | 56 +++++++++++++++++++++++--------------------------- 1 file changed, 26 insertions(+), 30 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index fd7bba0ac..87fb8fd6b 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -2,6 +2,7 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE ScopedTypeVariables #-} {- Copyright © 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> @@ -34,10 +35,11 @@ module Text.Pandoc.Lua ( LuaException(..), pushPandocModule ) where import Control.Exception -import Control.Monad (unless, when, (>=>)) +import Control.Monad (unless, when, (>=>), mplus) import Control.Monad.Trans (MonadIO (..)) -import Data.Data (toConstr) +import Data.Data (toConstr, showConstr, dataTypeOf, dataTypeConstrs, Data) import Data.Map (Map) +import Data.Maybe (isJust) import Data.Typeable (Typeable) import Scripting.Lua (LuaState, StackValue (..)) import Text.Pandoc.Definition @@ -91,44 +93,38 @@ runAll = foldr ((>=>) . walkMWithLuaFilter) return walkMWithLuaFilter :: LuaFilter -> Pandoc -> IO Pandoc walkMWithLuaFilter (LuaFilter lua fnMap) = - walkM (execInlineLuaFilter lua fnMap) >=> - walkM (execBlockLuaFilter lua fnMap) >=> - walkM (execMetaLuaFilter lua fnMap) >=> - walkM (execDocLuaFilter lua fnMap) + (if hasOneOf (constructorsFor (dataTypeOf (Str []))) + then walkM (tryFilter lua fnMap :: Inline -> IO Inline) + else return) + >=> + (if hasOneOf (constructorsFor (dataTypeOf (Para []))) + then walkM (tryFilter lua fnMap :: Block -> IO Block) + else return) + >=> + (case Map.lookup "Meta" fnMap of + Just fn -> walkM (\(Pandoc meta blocks) -> do + meta' <- runFilterFunction lua fn meta + return $ Pandoc meta' blocks) + Nothing -> return) + >=> + (case Map.lookup "Pandoc" fnMap `mplus` Map.lookup "Doc" fnMap of + Just fn -> (runFilterFunction lua fn) :: Pandoc -> IO Pandoc + Nothing -> return) + where hasOneOf = any (\k -> isJust (Map.lookup k fnMap)) + constructorsFor x = map show (dataTypeConstrs x) type FunctionMap = Map String LuaFilterFunction data LuaFilter = LuaFilter LuaState FunctionMap newtype LuaFilterFunction = LuaFilterFunction { functionIndex :: Int } -tryFilter :: StackValue a => LuaState -> FunctionMap -> String -> a -> IO a -tryFilter lua fnMap filterFnName x = +tryFilter :: (Data a, StackValue a) => LuaState -> FunctionMap -> a -> IO a +tryFilter lua fnMap x = + let filterFnName = showConstr (toConstr x) in case Map.lookup filterFnName fnMap of Nothing -> return x Just fn -> runFilterFunction lua fn x -execDocLuaFilter :: LuaState - -> FunctionMap - -> Pandoc -> IO Pandoc -execDocLuaFilter lua fnMap = tryFilter lua fnMap "Doc" - -execMetaLuaFilter :: LuaState - -> FunctionMap - -> Pandoc -> IO Pandoc -execMetaLuaFilter lua fnMap (Pandoc meta blks) = do - meta' <- tryFilter lua fnMap "Meta" meta - return $ Pandoc meta' blks - -execBlockLuaFilter :: LuaState - -> FunctionMap - -> Block -> IO Block -execBlockLuaFilter lua fnMap x = tryFilter lua fnMap (show (toConstr x)) x - -execInlineLuaFilter :: LuaState - -> FunctionMap - -> Inline -> IO Inline -execInlineLuaFilter lua fnMap x = tryFilter lua fnMap (show (toConstr x)) x - instance StackValue LuaFilter where valuetype _ = Lua.TTABLE push = undefined -- cgit v1.2.3 From 5e00cf8086e0960e81c31f7cd981ace646623f09 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 29 Jun 2017 17:13:19 +0200 Subject: Added parameter for user data directory to runLuaFilter. in Text.Pandoc.Lua. Also to pushPandocModule. This change allows users to override pandoc.lua with a file in their local data directory, adding custom functions, etc. @tarleb, if you think this is a bad idea, you can revert this. But in general our data files are all overridable. --- src/Text/Pandoc/App.hs | 2 +- src/Text/Pandoc/Lua.hs | 6 +++--- src/Text/Pandoc/Lua/PandocModule.hs | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index ee74d39c0..c119fa255 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -795,7 +795,7 @@ applyLuaFilters :: MonadIO m applyLuaFilters mbDatadir filters args d = do expandedFilters <- mapM (expandFilterPath mbDatadir) filters let go f d' = liftIO $ do - res <- E.try (runLuaFilter f args d') + res <- E.try (runLuaFilter mbDatadir f args d') case res of Right x -> return x Left (LuaException s) -> E.throw (PandocFilterError f s) diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 87fb8fd6b..22b68d5e0 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -56,12 +56,12 @@ newtype LuaException = LuaException String instance Exception LuaException runLuaFilter :: (MonadIO m) - => FilePath -> [String] -> Pandoc -> m Pandoc -runLuaFilter filterPath args pd = liftIO $ do + => Maybe FilePath -> FilePath -> [String] -> Pandoc -> m Pandoc +runLuaFilter datadir filterPath args pd = liftIO $ do lua <- Lua.newstate Lua.openlibs lua -- store module in global "pandoc" - pushPandocModule lua + pushPandocModule datadir lua Lua.setglobal lua "pandoc" top <- Lua.gettop lua status <- Lua.loadfile lua filterPath diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index fccfbebf3..2d0baf4f8 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -41,9 +41,9 @@ import Text.Pandoc.Readers (Reader (..), getReader) import Text.Pandoc.Shared (readDataFile) -- | Push the "pandoc" on the lua stack. -pushPandocModule :: LuaState -> IO () -pushPandocModule lua = do - script <- pandocModuleScript +pushPandocModule :: Maybe FilePath -> LuaState -> IO () +pushPandocModule datadir lua = do + script <- pandocModuleScript datadir status <- loadstring lua script "pandoc.lua" unless (status /= 0) $ call lua 0 1 push lua "__read" @@ -51,8 +51,8 @@ pushPandocModule lua = do rawset lua (-3) -- | Get the string representation of the pandoc module -pandocModuleScript :: IO String -pandocModuleScript = unpack <$> readDataFile Nothing "pandoc.lua" +pandocModuleScript :: Maybe FilePath -> IO String +pandocModuleScript datadir = unpack <$> readDataFile datadir "pandoc.lua" read_doc :: String -> String -> IO (Either String Pandoc) read_doc formatSpec content = do -- cgit v1.2.3 From e574d50b1cec1a8aea58db70a2c88ad10f1c4cb2 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 30 Jun 2017 17:41:25 +0200 Subject: Markdown writer: Ensure that `+` and `-` are escaped properly... so they don't cause spurious lists. Previously they were only if succeeded by a space, not if they were at end of line. Closes #3773. --- src/Text/Pandoc/Writers/Markdown.hs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index b951288bc..1e0d8bde2 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -416,6 +416,9 @@ blockToMarkdown' opts (Plain inlines) = do '+':s:_ | not isPlain && isSpace s -> "\\" <> contents '*':s:_ | not isPlain && isSpace s -> "\\" <> contents '-':s:_ | not isPlain && isSpace s -> "\\" <> contents + '+':[] | not isPlain -> "\\" <> contents + '*':[] | not isPlain -> "\\" <> contents + '-':[] | not isPlain -> "\\" <> contents '|':_ | (isEnabled Ext_line_blocks opts || isEnabled Ext_pipe_tables opts) && isEnabled Ext_all_symbols_escapable opts -- cgit v1.2.3 From 69b2cb38a867cd8b761e4c6ec65020bedbafcda1 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 30 Jun 2017 22:23:15 +0200 Subject: Make `east_asian_line_breaks` affect all readers/writers. Closes #3703. --- src/Text/Pandoc/App.hs | 17 +++++++++++++---- src/Text/Pandoc/Readers/Markdown.hs | 3 +-- 2 files changed, 14 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index c119fa255..6fdd2a44c 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -84,7 +84,8 @@ import Text.Pandoc.PDF (makePDF) import Text.Pandoc.Process (pipeProcess) import Text.Pandoc.SelfContained (makeDataURI, makeSelfContained) import Text.Pandoc.Shared (headerShift, isURI, openURL, readDataFile, - readDataFileUTF8, safeRead, tabFilter) + readDataFileUTF8, safeRead, tabFilter, + eastAsianLineBreakFilter) import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.XML (toEntities) import Text.Printf @@ -381,9 +382,17 @@ convertWithOpts opts = do "Specify an output file using the -o option." - let transforms = case optBaseHeaderLevel opts of - x | x > 1 -> [headerShift (x - 1)] - | otherwise -> [] + let transforms = (case optBaseHeaderLevel opts of + x | x > 1 -> (headerShift (x - 1) :) + | otherwise -> id) $ + (if extensionEnabled Ext_east_asian_line_breaks + readerExts && + not (extensionEnabled Ext_east_asian_line_breaks + writerExts && + writerWrapText writerOptions == WrapPreserve) + then (eastAsianLineBreakFilter :) + else id) + [] let convertTabs = tabFilter (if optPreserveTabs opts || readerName == "t2t" then 0 diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 49007ad35..c2342b9f3 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -380,8 +380,7 @@ parseMarkdown = do meta <- stateMeta' st return $ Pandoc meta bs) st reportLogMessages - (do guardEnabled Ext_east_asian_line_breaks - return $ eastAsianLineBreakFilter doc) <|> return doc + return doc referenceKey :: PandocMonad m => MarkdownParser m (F Blocks) referenceKey = try $ do -- cgit v1.2.3 From d3dae1200adf8318fc033f6eed987507be85b71e Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 30 Jun 2017 22:26:42 +0200 Subject: Removed `hard_line_breaks` extension from `markdown_github`. GitHub has two Markdown modes, one for long-form documents like READMEs and one for short things like issue coments. In issue comments, a line break is treated as a hard line break. In README, wikis, etc., it is treated as a space as in regular Markdown. Since pandoc is more likely to be used to convert long-form documents from GitHub Markdown, `-hard_line_breaks` is a better default. Closes #3594. --- src/Text/Pandoc/Extensions.hs | 1 - 1 file changed, 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 79e3529e9..bd164635c 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -244,7 +244,6 @@ githubMarkdownExtensions = extensionsFromList , Ext_space_in_atx_header , Ext_intraword_underscores , Ext_strikeout - , Ext_hard_line_breaks , Ext_emoji , Ext_lists_without_preceding_blankline , Ext_shortcut_reference_links -- cgit v1.2.3 From 20103ac2bcf547fa201eb5d1e79989d3466a7563 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 30 Jun 2017 23:30:18 +0200 Subject: Allow ibooks-specific metadata in epubs. Closes #2693. You can now have the following fields in your YAML metadata, and it will be treated appropriately in the generated EPUB. ``` ibooks: version: 1.3.4 specified-fonts: false ipad-orientation-lock: portrait-only iphone-orientation-lock: landscape-only binding: true scroll-axis: vertical ``` This commit also fixes a regression in stylesheet paths. --- src/Text/Pandoc/Writers/EPUB.hs | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index d20eb8a2f..a48fcf415 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -103,6 +103,7 @@ data EPUBMetadata = EPUBMetadata{ , epubCoverImage :: Maybe String , epubStylesheets :: [FilePath] , epubPageDirection :: Maybe ProgressionDirection + , epubIbooksFields :: [(String, String)] } deriving Show data Date = Date{ @@ -312,6 +313,7 @@ metadataFromMeta opts meta = EPUBMetadata{ , epubCoverImage = coverImage , epubStylesheets = stylesheets , epubPageDirection = pageDirection + , epubIbooksFields = ibooksFields } where identifiers = getIdentifier meta titles = getTitle meta @@ -339,6 +341,10 @@ metadataFromMeta opts meta = EPUBMetadata{ Just "ltr" -> Just LTR Just "rtl" -> Just RTL _ -> Nothing + ibooksFields = case lookupMeta "ibooks" meta of + Just (MetaMap mp) + -> M.toList $ M.map metaValueToString mp + _ -> [] -- | Produce an EPUB2 file from a Pandoc document. writeEPUB2 :: PandocMonad m @@ -577,7 +583,8 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do EPUB2 -> "2.0" EPUB3 -> "3.0") ,("xmlns","http://www.idpf.org/2007/opf") - ,("unique-identifier","epub-id-1")] $ + ,("unique-identifier","epub-id-1") + ,("prefix","ibooks: http://vocabulary.itunes.apple.com/rdf/ibooks/vocabulary-extensions-1.0/")] $ [ metadataElement version metadata currentTime , unode "manifest" $ [ unode "item" ! [("id","ncx"), ("href","toc.ncx") @@ -653,7 +660,7 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do navMapFormatter n tit src subs = unode "navPoint" ! [("id", "navPoint-" ++ show n)] $ [ unode "navLabel" $ unode "text" tit - , unode "content" ! [("src", src)] $ () + , unode "content" ! [("src", "text/" ++ src)] $ () ] ++ subs let tpNode = unode "navPoint" ! [("id", "navPoint-0")] $ @@ -686,7 +693,8 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do let navXhtmlFormatter :: Int -> String -> String -> [Element] -> Element navXhtmlFormatter n tit src subs = unode "li" ! [("id", "toc-li-" ++ show n)] $ - (unode "a" ! [("href",src)] + (unode "a" ! [("href", "text/" ++ + src)] $ tit) : case subs of [] -> [] @@ -719,7 +727,11 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do ] ] else [] - navData <- lift $ writeHtml opts'{ writerVariables = ("navpage","true"):vars } + navData <- lift $ writeHtml opts'{ writerVariables = ("navpage","true"): + -- remove the leading ../ from stylesheet paths: + map (\(k,v) -> if k == "css" + then (k, drop 3 v) + else (k, v)) vars } (Pandoc (setMeta "title" (walk removeNote $ fromList $ docTitle' meta) nullMeta) (navBlocks ++ landmarks)) @@ -761,7 +773,8 @@ metadataElement :: EPUBVersion -> EPUBMetadata -> UTCTime -> Element metadataElement version md currentTime = unode "metadata" ! [("xmlns:dc","http://purl.org/dc/elements/1.1/") ,("xmlns:opf","http://www.idpf.org/2007/opf")] $ mdNodes - where mdNodes = identifierNodes ++ titleNodes ++ dateNodes ++ languageNodes + where mdNodes = identifierNodes ++ titleNodes ++ dateNodes + ++ languageNodes ++ ibooksNodes ++ creatorNodes ++ contributorNodes ++ subjectNodes ++ descriptionNodes ++ typeNodes ++ formatNodes ++ publisherNodes ++ sourceNodes ++ relationNodes @@ -780,6 +793,8 @@ metadataElement version md currentTime = [] -> [] (x:_) -> [dcNode "date" ! [("id","epub-date")] $ dateText x] + ibooksNodes = map ibooksNode (epubIbooksFields md) + ibooksNode (k, v) = unode "meta" ! [("property", "ibooks:" ++ k)] $ v languageNodes = [dcTag "language" $ epubLanguage md] creatorNodes = withIds "epub-creator" (toCreatorNode "creator") $ epubCreator md -- cgit v1.2.3 From 1dd769e55897757812a1d8188b80c5df7fcb2971 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 3 Jul 2017 12:36:12 +0200 Subject: Logging: added MacroAlreadyDefined. --- src/Text/Pandoc/Logging.hs | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs index e31fb1521..1dcff7470 100644 --- a/src/Text/Pandoc/Logging.hs +++ b/src/Text/Pandoc/Logging.hs @@ -77,6 +77,7 @@ data LogMessage = | CircularReference String SourcePos | ParsingUnescaped String SourcePos | CouldNotLoadIncludeFile String SourcePos + | MacroAlreadyDefined String SourcePos | InlineNotRendered Inline | BlockNotRendered Block | DocxParserWarning String @@ -150,6 +151,11 @@ instance ToJSON LogMessage where "source" .= Text.pack (sourceName pos), "line" .= toJSON (sourceLine pos), "column" .= toJSON (sourceColumn pos)] + MacroAlreadyDefined name pos -> + ["name" .= Text.pack name, + "source" .= Text.pack (sourceName pos), + "line" .= toJSON (sourceLine pos), + "column" .= toJSON (sourceColumn pos)] InlineNotRendered il -> ["contents" .= toJSON il] BlockNotRendered bl -> @@ -224,6 +230,8 @@ showLogMessage msg = "Parsing unescaped '" ++ s ++ "' at " ++ showPos pos CouldNotLoadIncludeFile fp pos -> "Could not load include file '" ++ fp ++ "' at " ++ showPos pos + MacroAlreadyDefined name pos -> + "Macro '" ++ name ++ "' already defined, ignoring at " ++ showPos pos InlineNotRendered il -> "Not rendering " ++ show il BlockNotRendered bl -> @@ -277,6 +285,7 @@ messageVerbosity msg = ReferenceNotFound{} -> WARNING CircularReference{} -> WARNING CouldNotLoadIncludeFile{} -> WARNING + MacroAlreadyDefined{} -> WARNING ParsingUnescaped{} -> INFO InlineNotRendered{} -> INFO BlockNotRendered{} -> INFO -- cgit v1.2.3 From 0feb7504b1c68cef76b30ea9987e2eae3101714c Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 1 Jul 2017 19:31:43 +0200 Subject: Rewrote LaTeX reader with proper tokenization. This rewrite is primarily motivated by the need to get macros working properly. A side benefit is that the reader is significantly faster (27s -> 19s in one benchmark, and there is a lot of room for further optimization). We now tokenize the input text, then parse the token stream. Macros modify the token stream, so they should now be effective in any context, including math. Thus, we no longer need the clunky macro processing capacities of texmath. A custom state LaTeXState is used instead of ParserState. This, plus the tokenization, will require some rewriting of the exported functions rawLaTeXInline, inlineCommand, rawLaTeXBlock. * Added Text.Pandoc.Readers.LaTeX.Types (new exported module). Exports Macro, Tok, TokType, Line, Column. [API change] * Text.Pandoc.Parsing: adjusted type of `insertIncludedFile` so it can be used with token parser. * Removed old texmath macro stuff from Parsing. Use Macro from Text.Pandoc.Readers.LaTeX.Types instead. * Removed texmath macro material from Markdown reader. * Changed types for Text.Pandoc.Readers.LaTeX's rawLaTeXInline and rawLaTeXBlock. (Both now return a String, and they are polymorphic in state.) * Added orgMacros field to OrgState. [API change] * Removed readerApplyMacros from ReaderOptions. Now we just check the `latex_macros` reader extension. * Allow `\newcommand\foo{blah}` without braces. Fixes #1390. Fixes #2118. Fixes #3236. Fixes #3779. Fixes #934. Fixes #982. --- src/Text/Pandoc/App.hs | 1 - src/Text/Pandoc/Error.hs | 3 + src/Text/Pandoc/Extensions.hs | 1 + src/Text/Pandoc/Options.hs | 2 - src/Text/Pandoc/Parsing.hs | 67 +- src/Text/Pandoc/Readers/LaTeX.hs | 2777 +++++++++++++++++----------- src/Text/Pandoc/Readers/LaTeX/Types.hs | 48 + src/Text/Pandoc/Readers/Markdown.hs | 19 +- src/Text/Pandoc/Readers/Muse.hs | 2 +- src/Text/Pandoc/Readers/Org/Inlines.hs | 5 +- src/Text/Pandoc/Readers/Org/ParserState.hs | 10 + src/Text/Pandoc/Readers/TWiki.hs | 2 +- src/Text/Pandoc/Readers/Textile.hs | 2 +- src/Text/Pandoc/Readers/Txt2Tags.hs | 2 +- 14 files changed, 1772 insertions(+), 1169 deletions(-) create mode 100644 src/Text/Pandoc/Readers/LaTeX/Types.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 6fdd2a44c..689c0a784 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -308,7 +308,6 @@ convertWithOpts opts = do , readerColumns = optColumns opts , readerTabStop = optTabStop opts , readerIndentedCodeClasses = optIndentedCodeClasses opts - , readerApplyMacros = not laTeXOutput , readerDefaultImageExtension = optDefaultImageExtension opts , readerTrackChanges = optTrackChanges opts diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs index 60bc699ab..24186720c 100644 --- a/src/Text/Pandoc/Error.hs +++ b/src/Text/Pandoc/Error.hs @@ -64,6 +64,7 @@ data PandocError = PandocIOError String IOError | PandocTemplateError String | PandocAppError String | PandocEpubSubdirectoryError String + | PandocMacroLoop String deriving (Show, Typeable, Generic) instance Exception PandocError @@ -107,6 +108,8 @@ handleError (Left e) = PandocAppError s -> err 1 s PandocEpubSubdirectoryError s -> err 31 $ "EPUB subdirectory name '" ++ s ++ "' contains illegal characters" + PandocMacroLoop s -> err 91 $ + "Loop encountered in expanding macro " ++ s err :: Int -> String -> IO a err exitCode msg = do diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index bd164635c..28459d4e6 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -318,6 +318,7 @@ getDefaultExtensions "epub2" = getDefaultExtensions "epub" getDefaultExtensions "epub3" = getDefaultExtensions "epub" getDefaultExtensions "latex" = extensionsFromList [Ext_smart, + Ext_latex_macros, Ext_auto_identifiers] getDefaultExtensions "context" = extensionsFromList [Ext_smart, diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index 6519f807c..d7e77010e 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -61,7 +61,6 @@ data ReaderOptions = ReaderOptions{ , readerStandalone :: Bool -- ^ Standalone document with header , readerColumns :: Int -- ^ Number of columns in terminal , readerTabStop :: Int -- ^ Tab stop - , readerApplyMacros :: Bool -- ^ Apply macros to TeX math , readerIndentedCodeClasses :: [String] -- ^ Default classes for -- indented code blocks , readerAbbreviations :: Set.Set String -- ^ Strings to treat as abbreviations @@ -75,7 +74,6 @@ instance Default ReaderOptions , readerStandalone = False , readerColumns = 80 , readerTabStop = 4 - , readerApplyMacros = True , readerIndentedCodeClasses = [] , readerAbbreviations = defaultAbbrevs , readerDefaultImageExtension = "" diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index eb5b37f40..f6263c782 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -109,8 +109,6 @@ module Text.Pandoc.Parsing ( anyLine, dash, nested, citeKey, - macro, - applyMacros', Parser, ParserT, F, @@ -130,6 +128,7 @@ module Text.Pandoc.Parsing ( anyLine, runParser, runParserT, parse, + tokenPrim, anyToken, getInput, setInput, @@ -178,13 +177,16 @@ module Text.Pandoc.Parsing ( anyLine, sourceLine, setSourceColumn, setSourceLine, - newPos + newPos, + Line, + Column ) where +import Data.Text (Text) import Text.Pandoc.Definition import Text.Pandoc.Options -import Text.Pandoc.Builder (Blocks, Inlines, rawBlock, HasMeta(..), trimInlines) +import Text.Pandoc.Builder (Blocks, Inlines, HasMeta(..), trimInlines) import qualified Text.Pandoc.Builder as B import Text.Pandoc.XML (fromEntities) import qualified Text.Pandoc.UTF8 as UTF8 (putStrLn) @@ -195,7 +197,7 @@ import Data.Char ( toLower, toUpper, ord, chr, isAscii, isAlphaNum, import Data.List ( intercalate, transpose, isSuffixOf ) import Text.Pandoc.Shared import qualified Data.Map as M -import Text.TeXMath.Readers.TeX.Macros (applyMacros, Macro, pMacroDefinition) +import Text.Pandoc.Readers.LaTeX.Types (Macro) import Text.HTML.TagSoup.Entity ( lookupEntity ) import Text.Pandoc.Asciify (toAsciiChar) import Data.Monoid ((<>)) @@ -994,7 +996,7 @@ data ParserState = ParserState stateIdentifiers :: Set.Set String, -- ^ Header identifiers used stateNextExample :: Int, -- ^ Number of next example stateExamples :: M.Map String Int, -- ^ Map from example labels to numbers - stateMacros :: [Macro], -- ^ List of macros defined so far + stateMacros :: M.Map Text Macro, -- ^ Table of macros defined so far stateRstDefaultRole :: String, -- ^ Current rST default interpreted text role stateRstCustomRoles :: M.Map String (String, Maybe String, Attr), -- ^ Current rST custom text roles -- Triple represents: 1) Base role, 2) Optional format (only for :raw: @@ -1057,8 +1059,8 @@ instance HasIdentifierList ParserState where updateIdentifierList f st = st{ stateIdentifiers = f $ stateIdentifiers st } class HasMacros st where - extractMacros :: st -> [Macro] - updateMacros :: ([Macro] -> [Macro]) -> st -> st + extractMacros :: st -> M.Map Text Macro + updateMacros :: (M.Map Text Macro -> M.Map Text Macro) -> st -> st instance HasMacros ParserState where extractMacros = stateMacros @@ -1112,7 +1114,7 @@ defaultParserState = stateIdentifiers = Set.empty, stateNextExample = 1, stateExamples = M.empty, - stateMacros = [], + stateMacros = M.empty, stateRstDefaultRole = "title-reference", stateRstCustomRoles = M.empty, stateCaption = Nothing, @@ -1341,33 +1343,6 @@ token :: (Stream s m t) -> ParsecT s st m a token pp pos match = tokenPrim pp (\_ t _ -> pos t) match --- --- Macros --- - --- | Parse a \newcommand or \newenviroment macro definition. -macro :: (Stream [Char] m Char, HasMacros st, HasReaderOptions st) - => ParserT [Char] st m Blocks -macro = do - apply <- getOption readerApplyMacros - (m, def') <- withRaw pMacroDefinition - if apply - then do - updateState $ \st -> updateMacros (m:) st - return mempty - else return $ rawBlock "latex" def' - --- | Apply current macros to string. -applyMacros' :: (HasReaderOptions st, HasMacros st, Stream [Char] m Char) - => String - -> ParserT [Char] st m String -applyMacros' target = do - apply <- getOption readerApplyMacros - if apply - then do macros <- extractMacros <$> getState - return $ applyMacros macros target - else return target - 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) . (<>) @@ -1385,10 +1360,11 @@ extractIdClass (ident, cls, kvs) = (ident', cls', kvs') insertIncludedFile' :: (PandocMonad m, HasIncludeFiles st, Functor mf, Applicative mf, Monad mf) - => ParserT String st m (mf Blocks) + => ParserT [a] st m (mf Blocks) + -> (String -> [a]) -> [FilePath] -> FilePath - -> ParserT String st m (mf Blocks) -insertIncludedFile' blocks dirs f = do + -> ParserT [a] st m (mf Blocks) +insertIncludedFile' blocks totoks dirs f = do oldPos <- getPosition oldInput <- getInput containers <- getIncludeFiles <$> getState @@ -1402,7 +1378,7 @@ insertIncludedFile' blocks dirs f = do report $ CouldNotLoadIncludeFile f oldPos return "" setPosition $ newPos f 1 1 - setInput contents + setInput $ totoks contents bs <- blocks setInput oldInput setPosition oldPos @@ -1412,11 +1388,12 @@ insertIncludedFile' blocks dirs f = do -- | Parse content of include file as blocks. Circular includes result in an -- @PandocParseError@. insertIncludedFile :: (PandocMonad m, HasIncludeFiles st) - => ParserT String st m Blocks + => ParserT [a] st m Blocks + -> (String -> [a]) -> [FilePath] -> FilePath - -> ParserT String st m Blocks -insertIncludedFile blocks dirs f = - runIdentity <$> insertIncludedFile' (Identity <$> blocks) dirs f + -> ParserT [a] st m Blocks +insertIncludedFile blocks totoks dirs f = + runIdentity <$> insertIncludedFile' (Identity <$> blocks) totoks dirs f -- | Parse content of include file as future blocks. Circular includes result in -- an @PandocParseError@. @@ -1424,4 +1401,4 @@ insertIncludedFileF :: (PandocMonad m, HasIncludeFiles st) => ParserT String st m (Future st Blocks) -> [FilePath] -> FilePath -> ParserT String st m (Future st Blocks) -insertIncludedFileF = insertIncludedFile' +insertIncludedFileF p = insertIncludedFile' p id diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 090dc5fdb..d82e6a5dc 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1,4 +1,7 @@ {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE PatternGuards #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE ScopedTypeVariables #-} {- Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> @@ -28,20 +31,26 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Portability : portable Conversion of LaTeX to 'Pandoc' document. + -} module Text.Pandoc.Readers.LaTeX ( readLaTeX, + applyMacros, rawLaTeXInline, rawLaTeXBlock, - inlineCommand, + macro, + inlineCommand ) where import Control.Applicative (many, optional, (<|>)) import Control.Monad import Control.Monad.Except (throwError) -import Data.Char (chr, isAlphaNum, isLetter, ord) -import Data.Text (Text, unpack) +import Data.Char (chr, isAlphaNum, isLetter, ord, isDigit) +import Data.Default +import Data.Text (Text) +import qualified Data.Text as T import Data.List (intercalate, isPrefixOf) import qualified Data.Map as M +import qualified Data.Set as Set import Data.Maybe (fromMaybe, maybeToList) import Safe (minimumDef) import System.FilePath (addExtension, replaceExtension, takeExtension) @@ -52,10 +61,19 @@ import Text.Pandoc.Highlighting (fromListingsLanguage, languagesByExtension) import Text.Pandoc.ImageSize (numUnit, showFl) import Text.Pandoc.Logging import Text.Pandoc.Options -import Text.Pandoc.Parsing hiding (many, mathDisplay, mathInline, optional, - space, (<|>)) +import Text.Pandoc.Parsing hiding (many, optional, withRaw, + mathInline, mathDisplay, + space, (<|>), spaces, blankline) import Text.Pandoc.Shared +import Text.Pandoc.Readers.LaTeX.Types (Macro(..), Tok(..), + TokType(..), Line, Column) import Text.Pandoc.Walk +import Text.Pandoc.Error (PandocError(PandocParsecError, PandocMacroLoop)) + +-- for debugging: +-- import Text.Pandoc.Extensions (getDefaultExtensions) +-- import Text.Pandoc.Class (runIOorExplode, PandocIO) +-- import Debug.Trace (traceShowId) -- | Parse LaTeX from string and return 'Pandoc' document. readLaTeX :: PandocMonad m @@ -63,18 +81,18 @@ readLaTeX :: PandocMonad m -> Text -- ^ String to parse (assumes @'\n'@ line endings) -> m Pandoc readLaTeX opts ltx = do - parsed <- readWithM parseLaTeX def{ stateOptions = opts } - (unpack (crFilter ltx)) + parsed <- runParserT parseLaTeX def{ sOptions = opts } "source" + (tokenize (crFilter ltx)) case parsed of Right result -> return result - Left e -> throwError e + Left e -> throwError $ PandocParsecError (T.unpack ltx) e parseLaTeX :: PandocMonad m => LP m Pandoc parseLaTeX = do bs <- blocks eof st <- getState - let meta = stateMeta st + let meta = sMeta st let doc' = doc bs let headerLevel (Header n _ _) = [n] headerLevel _ = [] @@ -88,177 +106,476 @@ parseLaTeX = do else id) doc' return $ Pandoc meta bs' -type LP m = ParserT String ParserState m - -anyControlSeq :: PandocMonad m => LP m String -anyControlSeq = do - char '\\' - next <- option '\n' anyChar - case next of - '\n' -> return "" - c | isLetter c -> (c:) <$> (many letter <* optional sp) - | otherwise -> return [c] - -controlSeq :: PandocMonad m => String -> LP m String -controlSeq name = try $ do - char '\\' - case name of - "" -> mzero - [c] | not (isLetter c) -> string [c] - cs -> string cs <* notFollowedBy letter <* optional sp - return name - -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 +-- testParser :: LP PandocIO a -> Text -> IO a +-- testParser p t = do +-- res <- runIOorExplode (runParserT p defaultLaTeXState{ +-- sOptions = def{ readerExtensions = +-- enableExtension Ext_raw_tex $ +-- getDefaultExtensions "latex" }} "source" (tokenize t)) +-- case res of +-- Left e -> error (show e) +-- Right r -> return r + +data LaTeXState = LaTeXState{ sOptions :: ReaderOptions + , sMeta :: Meta + , sQuoteContext :: QuoteContext + , sMacros :: M.Map Text Macro + , sContainers :: [String] + , sHeaders :: M.Map Inlines String + , sLogMessages :: [LogMessage] + , sIdentifiers :: Set.Set String + , sVerbatimMode :: Bool + , sCaption :: Maybe Inlines + , sInListItem :: Bool + , sInTableCell :: Bool + } + deriving Show + +defaultLaTeXState :: LaTeXState +defaultLaTeXState = LaTeXState{ sOptions = def + , sMeta = nullMeta + , sQuoteContext = NoQuote + , sMacros = M.empty + , sContainers = [] + , sHeaders = M.empty + , sLogMessages = [] + , sIdentifiers = Set.empty + , sVerbatimMode = False + , sCaption = Nothing + , sInListItem = False + , sInTableCell = False + } + +instance PandocMonad m => HasQuoteContext LaTeXState m where + getQuoteContext = sQuoteContext <$> getState + withQuoteContext context parser = do + oldState <- getState + let oldQuoteContext = sQuoteContext oldState + setState oldState { sQuoteContext = context } + result <- parser + newState <- getState + setState newState { sQuoteContext = oldQuoteContext } + return result + +instance HasLogMessages LaTeXState where + addLogMessage msg st = st{ sLogMessages = msg : sLogMessages st } + getLogMessages st = reverse $ sLogMessages st + +instance HasIdentifierList LaTeXState where + extractIdentifierList = sIdentifiers + updateIdentifierList f st = st{ sIdentifiers = f $ sIdentifiers st } + +instance HasIncludeFiles LaTeXState where + getIncludeFiles = sContainers + addIncludeFile f s = s{ sContainers = f : sContainers s } + dropLatestIncludeFile s = s { sContainers = drop 1 $ sContainers s } + +instance HasHeaderMap LaTeXState where + extractHeaderMap = sHeaders + updateHeaderMap f st = st{ sHeaders = f $ sHeaders st } + +instance HasMacros LaTeXState where + extractMacros st = sMacros st + updateMacros f st = st{ sMacros = f (sMacros st) } + +instance HasReaderOptions LaTeXState where + extractReaderOptions = sOptions + +instance HasMeta LaTeXState where + setMeta field val st = + st{ sMeta = setMeta field val $ sMeta st } + deleteMeta field st = + st{ sMeta = deleteMeta field $ sMeta st } + +instance Default LaTeXState where + def = defaultLaTeXState + +type LP m = ParserT [Tok] LaTeXState m + +withVerbatimMode :: PandocMonad m => LP m a -> LP m a +withVerbatimMode parser = do + updateState $ \st -> st{ sVerbatimMode = True } + result <- parser + updateState $ \st -> st{ sVerbatimMode = False } + return result + +rawLaTeXBlock :: (PandocMonad m, HasMacros s, HasReaderOptions s) + => ParserT String s m String +rawLaTeXBlock = do + lookAhead (try (char '\\' >> letter)) + inp <- getInput + let toks = tokenize $ T.pack inp + let rawblock = do + (_, raw) <- try $ + withRaw (environment <|> macroDef <|> blockCommand) + return raw + pstate <- getState + let lstate = def{ sOptions = extractReaderOptions pstate } + res <- runParserT rawblock lstate "source" toks + case res of + Left _ -> mzero + Right raw -> count (T.length (untokenize raw)) anyChar + +macro :: (PandocMonad m, HasMacros s, HasReaderOptions s) + => ParserT String s m Blocks +macro = do + guardEnabled Ext_latex_macros + lookAhead (char '\\' *> oneOfStrings ["new", "renew", "provide"] *> + oneOfStrings ["command", "environment"]) + inp <- getInput + let toks = tokenize $ T.pack inp + let rawblock = do + (_, raw) <- withRaw $ try macroDef + st <- getState + return (raw, st) + pstate <- getState + let lstate = def{ sOptions = extractReaderOptions pstate + , sMacros = extractMacros pstate } + res <- runParserT rawblock lstate "source" toks + case res of + Left _ -> mzero + Right (raw, st) -> do + updateState (updateMacros (const $ sMacros st)) + mempty <$ count (T.length (untokenize raw)) anyChar + +applyMacros :: (PandocMonad m, HasMacros s, HasReaderOptions s) + => String -> ParserT String s m String +applyMacros s = do + (guardEnabled Ext_latex_macros >> + do let retokenize = doMacros 0 *> (toksToString <$> getInput) + pstate <- getState + let lstate = def{ sOptions = extractReaderOptions pstate + , sMacros = extractMacros pstate } + res <- runParserT retokenize lstate "math" (tokenize (T.pack s)) + case res of + Left e -> fail (show e) + Right s' -> return s') <|> return s + +rawLaTeXInline :: (PandocMonad m, HasMacros s, HasReaderOptions s) + => ParserT String s m String +rawLaTeXInline = do + lookAhead (try (char '\\' >> letter) <|> char '$') + inp <- getInput + let toks = tokenize $ T.pack inp + let rawinline = do + (_, raw) <- try $ withRaw (inlineEnvironment <|> inlineCommand') + st <- getState + return (raw, st) + pstate <- getState + let lstate = def{ sOptions = extractReaderOptions pstate + , sMacros = extractMacros pstate } + res <- runParserT rawinline lstate "source" toks + case res of + Left _ -> mzero + Right (raw, s) -> do + updateState $ updateMacros (const $ sMacros s) + count (T.length (untokenize raw)) anyChar + +inlineCommand :: PandocMonad m => ParserT String ParserState m Inlines +inlineCommand = do + lookAhead (try (char '\\' >> letter) <|> char '$') + inp <- getInput + let toks = tokenize $ T.pack inp + let rawinline = do + (il, raw) <- try $ withRaw (inlineEnvironment <|> inlineCommand') + st <- getState + return (il, raw, st) + pstate <- getState + let lstate = def{ sOptions = extractReaderOptions pstate + , sMacros = extractMacros pstate } + res <- runParserT rawinline lstate "source" toks + case res of + Left _ -> mzero + Right (il, raw, s) -> do + updateState $ updateMacros (const $ sMacros s) + count (T.length (untokenize raw)) anyChar + return il + +tokenize :: Text -> [Tok] +tokenize = totoks (1, 1) + +totoks :: (Line, Column) -> Text -> [Tok] +totoks (lin,col) t = + case T.uncons t of + Nothing -> [] + Just (c, rest) + | c == '\n' -> + Tok (lin, col) Newline "\n" + : totoks (lin + 1,1) rest + | isSpaceOrTab c -> + let (sps, rest') = T.span isSpaceOrTab t + in Tok (lin, col) Spaces sps + : totoks (lin, col + T.length sps) rest' + | isAlphaNum c -> + let (ws, rest') = T.span isAlphaNum t + in Tok (lin, col) Word ws + : totoks (lin, col + T.length ws) rest' + | c == '%' -> + let (cs, rest') = T.break (== '\n') rest + in Tok (lin, col) Comment ("%" <> cs) + : totoks (lin, col + 1 + T.length cs) rest' + | c == '\\' -> + case T.uncons rest of + Nothing -> [Tok (lin, col) Symbol (T.singleton c)] + Just (d, rest') + | isLetter d -> + let (ws, rest'') = T.span isLetter rest + (ss, rest''') = T.span isSpaceOrTab rest'' + in Tok (lin, col) (CtrlSeq ws) ("\\" <> ws <> ss) + : totoks (lin, + col + 1 + T.length ws + T.length ss) rest''' + | d == '\t' || d == '\n' -> + Tok (lin, col) Symbol ("\\") + : totoks (lin, col + 1) rest + | otherwise -> + Tok (lin, col) (CtrlSeq (T.singleton d)) (T.pack [c,d]) + : totoks (lin, col + 2) rest' + | c == '#' -> + let (t1, t2) = T.span (\d -> d >= '0' && d <= '9') rest + in case safeRead (T.unpack t1) of + Just i -> + Tok (lin, col) (Arg i) ("#" <> t1) + : totoks (lin, col + 1 + T.length t1) t2 + Nothing -> + Tok (lin, col) Symbol ("#") + : totoks (lin, col + 1) t2 + | c == '^' -> + case T.uncons rest of + Just ('^', rest') -> + case T.uncons rest' of + Just (d, rest'') + | isLowerHex d -> + case T.uncons rest'' of + Just (e, rest''') | isLowerHex e -> + Tok (lin, col) Esc2 (T.pack ['^','^',d,e]) + : totoks (lin, col + 4) rest''' + _ -> + Tok (lin, col) Esc1 (T.pack ['^','^',d]) + : totoks (lin, col + 3) rest'' + | d < '\128' -> + Tok (lin, col) Esc1 (T.pack ['^','^',d]) + : totoks (lin, col + 3) rest'' + _ -> [Tok (lin, col) Symbol ("^"), + Tok (lin, col + 1) Symbol ("^")] + _ -> Tok (lin, col) Symbol ("^") + : totoks (lin, col + 1) rest + | otherwise -> + Tok (lin, col) Symbol (T.singleton c) : totoks (lin, col + 1) rest + + where isSpaceOrTab ' ' = True + isSpaceOrTab '\t' = True + isSpaceOrTab _ = False + +isLowerHex :: Char -> Bool +isLowerHex x = x >= '0' && x <= '9' || x >= 'a' && x <= 'f' + +untokenize :: [Tok] -> Text +untokenize = mconcat . map untoken + +untoken :: Tok -> Text +untoken (Tok _ _ t) = t + +satisfyTok :: PandocMonad m => (Tok -> Bool) -> LP m Tok +satisfyTok f = + try $ do + res <- tokenPrim (T.unpack . untoken) updatePos matcher + doMacros 0 -- apply macros on remaining input stream + return res + where matcher t | f t = Just t + | otherwise = Nothing + updatePos :: SourcePos -> Tok -> [Tok] -> SourcePos + updatePos spos _ (Tok (lin,col) _ _ : _) = + setSourceColumn (setSourceLine spos lin) col + updatePos spos _ [] = spos + +doMacros :: PandocMonad m => Int -> LP m () +doMacros n = do + verbatimMode <- sVerbatimMode <$> getState + when (not verbatimMode) $ do + inp <- getInput + case inp of + Tok spos (CtrlSeq "begin") _ : Tok _ Symbol "{" : + Tok _ Word name : Tok _ Symbol "}" : ts + -> handleMacros spos name ts + Tok spos (CtrlSeq "end") _ : Tok _ Symbol "{" : + Tok _ Word name : Tok _ Symbol "}" : ts + -> handleMacros spos ("end" <> name) ts + Tok spos (CtrlSeq name) _ : ts + -> handleMacros spos name ts + _ -> return () + where handleMacros spos name ts = do + macros <- sMacros <$> getState + case M.lookup name macros of + Nothing -> return () + Just (Macro numargs optarg newtoks) -> do + setInput ts + let getarg = spaces >> braced + args <- case optarg of + Nothing -> count numargs getarg + Just o -> + (:) <$> option o bracketedToks + <*> count (numargs - 1) getarg + let addTok (Tok _ (Arg i) _) acc | i > 0 + , i <= numargs = + map (setpos spos) (args !! (i - 1)) ++ acc + addTok t acc = setpos spos t : acc + ts' <- getInput + setInput $ foldr addTok ts' newtoks + if n > 20 -- detect macro expansion loops + then throwError $ PandocMacroLoop (T.unpack name) + else doMacros (n + 1) + +setpos :: (Line, Column) -> Tok -> Tok +setpos spos (Tok _ tt txt) = Tok spos tt txt + +anyControlSeq :: PandocMonad m => LP m Tok +anyControlSeq = satisfyTok isCtrlSeq + where isCtrlSeq (Tok _ (CtrlSeq _) _) = True + isCtrlSeq _ = False + +anySymbol :: PandocMonad m => LP m Tok +anySymbol = satisfyTok isSym + where isSym (Tok _ Symbol _) = True + isSym _ = False + +spaces :: PandocMonad m => LP m () +spaces = skipMany (satisfyTok (tokTypeIn [Comment, Spaces, Newline])) + +spaces1 :: PandocMonad m => LP m () +spaces1 = skipMany1 (satisfyTok (tokTypeIn [Comment, Spaces, Newline])) + +tokTypeIn :: [TokType] -> Tok -> Bool +tokTypeIn toktypes (Tok _ tt _) = tt `elem` toktypes + +controlSeq :: PandocMonad m => Text -> LP m Tok +controlSeq name = satisfyTok isNamed + where isNamed (Tok _ (CtrlSeq n) _) = n == name + isNamed _ = False + +symbol :: PandocMonad m => Char -> LP m Tok +symbol c = satisfyTok isc + where isc (Tok _ Symbol d) = case T.uncons d of + Just (c',_) -> c == c' + _ -> False + isc _ = False + +symbolIn :: PandocMonad m => [Char] -> LP m Tok +symbolIn cs = satisfyTok isInCs + where isInCs (Tok _ Symbol d) = case T.uncons d of + Just (c,_) -> c `elem` cs + _ -> False + isInCs _ = False sp :: PandocMonad m => LP m () sp = whitespace <|> endline whitespace :: PandocMonad m => LP m () -whitespace = skipMany1 $ satisfy (\c -> c == ' ' || c == '\t') +whitespace = () <$ satisfyTok isSpaceTok + where isSpaceTok (Tok _ Spaces _) = True + isSpaceTok _ = False -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' +newlineTok :: PandocMonad m => LP m () +newlineTok = () <$ satisfyTok isNewlineTok -tildeEscape :: PandocMonad m => LP m Char -tildeEscape = try $ do - string "^^" - c <- satisfy (\x -> x >= '\0' && x <= '\128') - d <- if isLowerHex c - then option "" $ count 1 (satisfy isLowerHex) - else return "" - if null d - then case ord c of - x | x >= 64 && x <= 127 -> return $ chr (x - 64) - | otherwise -> return $ chr (x + 64) - else return $ chr $ read ('0':'x':c:d) +isNewlineTok :: Tok -> Bool +isNewlineTok (Tok _ Newline _) = True +isNewlineTok _ = False comment :: PandocMonad m => LP m () -comment = do - char '%' - skipMany (satisfy (/='\n')) - optional newline - return () +comment = () <$ satisfyTok isCommentTok + where isCommentTok (Tok _ Comment _) = True + isCommentTok _ = False + +anyTok :: PandocMonad m => LP m Tok +anyTok = satisfyTok (const True) -bgroup :: PandocMonad m => LP m () +endline :: PandocMonad m => LP m () +endline = try $ do + newlineTok + lookAhead anyTok + notFollowedBy blankline + +blankline :: PandocMonad m => LP m () +blankline = try $ skipMany whitespace *> newlineTok + +primEscape :: PandocMonad m => LP m Char +primEscape = do + Tok _ toktype t <- satisfyTok (tokTypeIn [Esc1, Esc2]) + case toktype of + Esc1 -> case T.uncons (T.drop 2 t) of + Just (c, _) + | c >= '\64' && c <= '\127' -> return (chr (ord c - 64)) + | otherwise -> return (chr (ord c + 64)) + Nothing -> fail "Empty content of Esc1" + Esc2 -> case safeRead ('0':'x':T.unpack (T.drop 2 t)) of + Just x -> return (chr x) + Nothing -> fail $ "Could not read: " ++ T.unpack t + _ -> fail "Expected an Esc1 or Esc2 token" -- should not happen + +bgroup :: PandocMonad m => LP m Tok bgroup = try $ do - skipMany (spaceChar <|> try (newline <* notFollowedBy blankline)) - () <$ char '{' - <|> () <$ controlSeq "bgroup" - <|> () <$ controlSeq "begingroup" + skipMany sp + symbol '{' <|> controlSeq "bgroup" <|> controlSeq "begingroup" -egroup :: PandocMonad m => LP m () -egroup = () <$ char '}' - <|> () <$ controlSeq "egroup" - <|> () <$ controlSeq "endgroup" +egroup :: PandocMonad m => LP m Tok +egroup = (symbol '}' <|> controlSeq "egroup" <|> controlSeq "endgroup") -grouped :: PandocMonad m => Monoid a => LP m a -> LP m a +grouped :: (PandocMonad m, Monoid a) => LP m a -> LP m a grouped parser = try $ do bgroup -- first we check for an inner 'grouped', because -- {{a,b}} should be parsed the same as {a,b} - try (grouped parser <* egroup) - <|> (mconcat <$> manyTill parser egroup) - -braced :: PandocMonad m => LP m String -braced = grouped chunk - where chunk = - many1 (satisfy (\c -> c /= '\\' && c /= '}' && c /= '{')) - <|> try (string "\\}") - <|> try (string "\\{") - <|> try (string "\\\\") - <|> ((\x -> "{" ++ x ++ "}") <$> braced) - <|> count 1 anyChar + try (grouped parser <* egroup) <|> (mconcat <$> manyTill parser egroup) + +braced :: PandocMonad m => LP m [Tok] +braced = bgroup *> braced' 1 + where braced' (n :: Int) = + handleEgroup n <|> handleBgroup n <|> handleOther n + handleEgroup n = do + t <- egroup + if n == 1 + then return [] + else (t:) <$> braced' (n - 1) + handleBgroup n = do + t <- bgroup + (t:) <$> braced' (n + 1) + handleOther n = do + t <- anyTok + (t:) <$> braced' n bracketed :: PandocMonad m => Monoid a => LP m a -> LP m a -bracketed parser = try $ char '[' *> (mconcat <$> manyTill parser (char ']')) - -mathDisplay :: PandocMonad m => LP m String -> LP m Inlines -mathDisplay p = displayMath <$> (try p >>= applyMacros' . trim) - -mathInline :: PandocMonad m => LP m String -> LP m Inlines -mathInline p = math <$> (try p >>= applyMacros') - -mathChars :: PandocMonad m => LP m String -mathChars = - concat <$> many (escapedChar - <|> (snd <$> withRaw braced) - <|> many1 (satisfy isOrdChar)) - where escapedChar = try $ do char '\\' - c <- anyChar - return ['\\',c] - isOrdChar '$' = False - isOrdChar '{' = False - isOrdChar '}' = False - isOrdChar '\\' = False - isOrdChar _ = True - -quoted' :: PandocMonad m => (Inlines -> Inlines) -> LP m String -> LP m () -> LP m Inlines -quoted' f starter ender = do - startchs <- starter - smart <- extensionEnabled Ext_smart <$> getOption readerExtensions - if smart - then do - ils <- many (notFollowedBy ender >> inline) - (ender >> return (f (mconcat ils))) <|> - (<> mconcat ils) <$> - lit (case startchs of - "``" -> "“" - "`" -> "‘" - _ -> startchs) - else lit startchs +bracketed parser = try $ do + symbol '[' + mconcat <$> manyTill parser (symbol ']') -doubleQuote :: PandocMonad m => LP m Inlines -doubleQuote = do - quoted' doubleQuoted (try $ string "``") (void $ try $ string "''") - <|> quoted' doubleQuoted (string "“") (void $ char '”') - -- the following is used by babel for localized quotes: - <|> quoted' doubleQuoted (try $ string "\"`") (void $ try $ string "\"'") - <|> quoted' doubleQuoted (string "\"") (void $ char '"') +dimenarg :: PandocMonad m => LP m Text +dimenarg = try $ do + ch <- option False $ True <$ symbol '=' + Tok _ _ s <- satisfyTok isWordTok + guard $ (T.take 2 (T.reverse s)) `elem` + ["pt","pc","in","bp","cm","mm","dd","cc","sp"] + let num = T.take (T.length s - 2) s + guard $ T.length num > 0 + guard $ T.all isDigit num + return $ T.pack ['=' | ch] <> s -singleQuote :: PandocMonad m => LP m Inlines -singleQuote = do - smart <- extensionEnabled Ext_smart <$> getOption readerExtensions - if smart - then quoted' singleQuoted (string "`") (try $ char '\'' >> notFollowedBy letter) - <|> quoted' singleQuoted (string "‘") (try $ char '’' >> notFollowedBy letter) - else str <$> many1 (oneOf "`\'‘’") +-- inline elements: -inline :: PandocMonad m => LP m Inlines -inline = (mempty <$ comment) - <|> (space <$ whitespace) - <|> (softbreak <$ endline) - <|> inlineText - <|> inlineCommand - <|> inlineEnvironment - <|> inlineGroup - <|> (char '-' *> option (str "-") - (char '-' *> option (str "–") (str "—" <$ char '-'))) - <|> doubleQuote - <|> singleQuote - <|> (str "”" <$ try (string "''")) - <|> (str "”" <$ char '”') - <|> (str "’" <$ char '\'') - <|> (str "’" <$ char '’') - <|> (str "\160" <$ char '~') - <|> mathDisplay (string "$$" *> mathChars <* string "$$") - <|> mathInline (char '$' *> mathChars <* char '$') - <|> (guardEnabled Ext_literate_haskell *> char '|' *> doLHSverb) - <|> (str . (:[]) <$> tildeEscape) - <|> (do res <- oneOf "#&~^'`\"[]" - pos <- getPosition - report $ ParsingUnescaped [res] pos - return $ str [res]) +word :: PandocMonad m => LP m Inlines +word = (str . T.unpack . untoken) <$> satisfyTok isWordTok -inlines :: PandocMonad m => LP m Inlines -inlines = mconcat <$> many (notFollowedBy (char '}') *> inline) +regularSymbol :: PandocMonad m => LP m Inlines +regularSymbol = (str . T.unpack . untoken) <$> satisfyTok isRegularSymbol + where isRegularSymbol (Tok _ Symbol t) = not $ T.any isSpecial t + isRegularSymbol _ = False + isSpecial c = c `Set.member` specialChars + +specialChars :: Set.Set Char +specialChars = Set.fromList "#$%&~_^\\{}" + +isWordTok :: Tok -> Bool +isWordTok (Tok _ Word _) = True +isWordTok _ = False inlineGroup :: PandocMonad m => LP m Inlines inlineGroup = do @@ -269,233 +586,564 @@ inlineGroup = do -- we need the span so we can detitlecase bibtex entries; -- we need to know when something is {C}apitalized -block :: PandocMonad m => LP m Blocks -block = (mempty <$ comment) - <|> (mempty <$ ((spaceChar <|> newline) *> spaces)) - <|> environment - <|> include - <|> macro - <|> blockCommand - <|> paragraph - <|> grouped block - -blocks :: PandocMonad m => LP m Blocks -blocks = mconcat <$> many block +doLHSverb :: PandocMonad m => LP m Inlines +doLHSverb = + (codeWith ("",["haskell"],[]) . T.unpack . untokenize) + <$> manyTill (satisfyTok (not . isNewlineTok)) (symbol '|') -getRawCommand :: PandocMonad m => String -> LP m String -getRawCommand name' = do - rawargs <- withRaw (many (try (optional sp *> opt)) *> - option "" (try (optional sp *> dimenarg)) *> - many braced) - return $ '\\' : name' ++ snd rawargs +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) ++ "%") + _ -> (k, v) + let kvs = map replaceTextwidth + $ filter (\(k,_) -> k `elem` ["width", "height"]) options + let attr = ("",[], kvs) + let alt = str "image" + case takeExtension src of + "" -> do + defaultExt <- getOption readerDefaultImageExtension + return $ imageWith attr (addExtension src defaultExt) "" alt + _ -> return $ imageWith attr src "" alt -lookupListDefault :: (Ord k) => v -> [k] -> M.Map k v -> v -lookupListDefault d = (fromMaybe d .) . lookupList - where - lookupList l m = msum $ map (`M.lookup` m) l +-- converts e.g. \SI{1}[\$]{} to "$ 1" or \SI{1}{\euro} to "1 €" +dosiunitx :: PandocMonad m => LP m Inlines +dosiunitx = do + skipopts + value <- tok + valueprefix <- option "" $ bracketed tok + unit <- tok + let emptyOr160 "" = "" + emptyOr160 _ = "\160" + return . mconcat $ [valueprefix, + emptyOr160 valueprefix, + value, + emptyOr160 unit, + unit] -blockCommand :: PandocMonad m => LP m Blocks -blockCommand = try $ do - name <- anyControlSeq - guard $ name /= "begin" && name /= "end" - star <- option "" (string "*" <* optional sp) - let name' = name ++ star - let raw = do - rawcommand <- getRawCommand name' - transformed <- applyMacros' rawcommand - guard $ transformed /= rawcommand - notFollowedBy $ parseFromString' inlines transformed - parseFromString' blocks transformed - lookupListDefault raw [name',name] blockCommands +lit :: String -> LP m Inlines +lit = pure . str -inBrackets :: Inlines -> Inlines -inBrackets x = str "[" <> x <> str "]" +removeDoubleQuotes :: Text -> Text +removeDoubleQuotes t = + maybe t id $ T.stripPrefix "\"" t >>= T.stripSuffix "\"" --- eat an optional argument and one or more arguments in braces -ignoreInlines :: PandocMonad m => String -> (String, LP m Inlines) -ignoreInlines name = (name, p) - where - p = do oa <- optargs - let rawCommand = '\\':name ++ oa - let doraw = guardRaw >> return (rawInline "latex" rawCommand) - doraw <|> ignore rawCommand +doubleQuote :: PandocMonad m => LP m Inlines +doubleQuote = do + quoted' doubleQuoted (try $ count 2 $ symbol '`') + (void $ try $ count 2 $ symbol '\'') + <|> quoted' doubleQuoted ((:[]) <$> symbol '“') (void $ symbol '”') + -- the following is used by babel for localized quotes: + <|> quoted' doubleQuoted (try $ sequence [symbol '"', symbol '`']) + (void $ try $ sequence [symbol '"', symbol '\'']) + <|> quoted' doubleQuoted ((:[]) <$> symbol '"') + (void $ symbol '"') -guardRaw :: PandocMonad m => LP m () -guardRaw = getOption readerExtensions >>= guard . extensionEnabled Ext_raw_tex +singleQuote :: PandocMonad m => LP m Inlines +singleQuote = do + quoted' singleQuoted ((:[]) <$> symbol '`') + (try $ symbol '\'' >> + notFollowedBy (satisfyTok startsWithLetter)) + <|> quoted' singleQuoted ((:[]) <$> symbol '‘') + (try $ symbol '’' >> + notFollowedBy (satisfyTok startsWithLetter)) + where startsWithLetter (Tok _ Word t) = + case T.uncons t of + Just (c, _) | isLetter c -> True + _ -> False + startsWithLetter _ = False + +quoted' :: PandocMonad m + => (Inlines -> Inlines) + -> LP m [Tok] + -> LP m () + -> LP m Inlines +quoted' f starter ender = do + startchs <- (T.unpack . untokenize) <$> starter + smart <- extensionEnabled Ext_smart <$> getOption readerExtensions + if smart + then do + ils <- many (notFollowedBy ender >> inline) + (ender >> return (f (mconcat ils))) <|> + (<> mconcat ils) <$> + lit (case startchs of + "``" -> "“" + "`" -> "‘" + cs -> cs) + else lit startchs -optargs :: PandocMonad m => LP m String -optargs = snd <$> withRaw (skipopts *> skipMany (try $ optional sp *> braced)) +enquote :: PandocMonad m => LP m Inlines +enquote = do + skipopts + quoteContext <- sQuoteContext <$> getState + if quoteContext == InDoubleQuote + then singleQuoted <$> withQuoteContext InSingleQuote tok + else doubleQuoted <$> withQuoteContext InDoubleQuote tok -ignore :: (Monoid a, PandocMonad m) => String -> ParserT s u m a -ignore raw = do - pos <- getPosition - report $ SkippedContent raw pos - return mempty +doverb :: PandocMonad m => LP m Inlines +doverb = do + Tok _ Symbol t <- anySymbol + marker <- case T.uncons t of + Just (c, ts) | T.null ts -> return c + _ -> mzero + withVerbatimMode $ + (code . T.unpack . untokenize) <$> + manyTill (verbTok marker) (symbol marker) + +verbTok :: PandocMonad m => Char -> LP m Tok +verbTok stopchar = do + t@(Tok (lin, col) toktype txt) <- satisfyTok (not . isNewlineTok) + case T.findIndex (== stopchar) txt of + Nothing -> return t + Just i -> do + let (t1, t2) = T.splitAt i txt + inp <- getInput + setInput $ Tok (lin, col + i) Symbol (T.singleton stopchar) + : (totoks (lin, col + i + 1) (T.drop 1 t2)) ++ inp + return $ Tok (lin, col) toktype t1 -ignoreBlocks :: PandocMonad m => String -> (String, LP m Blocks) -ignoreBlocks name = (name, p) - where - p = do oa <- optargs - let rawCommand = '\\':name ++ oa - let doraw = guardRaw >> return (rawBlock "latex" rawCommand) - doraw <|> ignore rawCommand +dolstinline :: PandocMonad m => LP m Inlines +dolstinline = do + options <- option [] keyvals + let classes = maybeToList $ lookup "language" options >>= fromListingsLanguage + Tok _ Symbol t <- anySymbol + marker <- case T.uncons t of + Just (c, ts) | T.null ts -> return c + _ -> mzero + let stopchar = if marker == '{' then '}' else marker + withVerbatimMode $ + (codeWith ("",classes,[]) . T.unpack . untokenize) <$> + manyTill (verbTok stopchar) (symbol stopchar) -blockCommands :: PandocMonad m => M.Map String (LP m Blocks) -blockCommands = M.fromList $ - [ ("par", mempty <$ skipopts) - , ("parbox", braced >> grouped blocks) - , ("title", mempty <$ (skipopts *> - (grouped inline >>= addMeta "title") - <|> (grouped block >>= addMeta "title"))) - , ("subtitle", mempty <$ (skipopts *> tok >>= addMeta "subtitle")) - , ("author", mempty <$ (skipopts *> authors)) - -- -- in letter class, temp. store address & sig as title, author - , ("address", mempty <$ (skipopts *> tok >>= addMeta "address")) - , ("signature", mempty <$ (skipopts *> authors)) - , ("date", mempty <$ (skipopts *> tok >>= addMeta "date")) - -- Koma-script metadata commands - , ("dedication", mempty <$ (skipopts *> tok >>= addMeta "dedication")) - -- sectioning - , ("part", section nullAttr (-1)) - , ("part*", section nullAttr (-1)) - , ("chapter", section nullAttr 0) - , ("chapter*", section ("",["unnumbered"],[]) 0) - , ("section", section nullAttr 1) - , ("section*", section ("",["unnumbered"],[]) 1) - , ("subsection", section nullAttr 2) - , ("subsection*", section ("",["unnumbered"],[]) 2) - , ("subsubsection", section nullAttr 3) - , ("subsubsection*", section ("",["unnumbered"],[]) 3) - , ("paragraph", section nullAttr 4) - , ("paragraph*", section ("",["unnumbered"],[]) 4) - , ("subparagraph", section nullAttr 5) - , ("subparagraph*", section ("",["unnumbered"],[]) 5) - -- beamer slides - , ("frametitle", section nullAttr 3) - , ("framesubtitle", section nullAttr 4) - -- letters - , ("opening", (para . trimInlines) <$> (skipopts *> tok)) - , ("closing", skipopts *> closing) - -- - , ("hrule", pure horizontalRule) - , ("strut", pure mempty) - , ("rule", skipopts *> tok *> tok *> pure horizontalRule) - , ("item", skipopts *> looseItem) - , ("documentclass", skipopts *> braced *> preamble) - , ("centerline", (para . trimInlines) <$> (skipopts *> tok)) - , ("caption", skipopts *> setCaption) - , ("bibliography", mempty <$ (skipopts *> braced >>= - addMeta "bibliography" . splitBibs)) - , ("addbibresource", mempty <$ (skipopts *> braced >>= - addMeta "bibliography" . splitBibs)) - -- includes - , ("lstinputlisting", inputListing) - , ("graphicspath", graphicsPath) - -- hyperlink - , ("hypertarget", braced >> grouped block) - ] ++ map ignoreBlocks - -- these commands will be ignored unless --parse-raw is specified, - -- in which case they will appear as raw latex blocks - [ "newcommand", "renewcommand", "newenvironment", "renewenvironment" - -- newcommand, etc. should be parsed by macro, but we need this - -- here so these aren't parsed as inline commands to ignore - , "special", "pdfannot", "pdfstringdef" - , "bibliographystyle" - , "maketitle", "makeindex", "makeglossary" - , "addcontentsline", "addtocontents", "addtocounter" - -- \ignore{} is used conventionally in literate haskell for definitions - -- that are to be processed by the compiler but not printed. - , "ignore" - , "hyperdef" - , "markboth", "markright", "markleft" - , "hspace", "vspace" - , "newpage" - , "clearpage" - , "pagebreak" - ] +keyval :: PandocMonad m => LP m (String, String) +keyval = try $ do + Tok _ Word key <- satisfyTok isWordTok + let isSpecSym (Tok _ Symbol t) = t `elem` [".",":","-","|","\\"] + isSpecSym _ = False + val <- option [] $ do + symbol '=' + braced <|> (many1 (satisfyTok isWordTok <|> satisfyTok isSpecSym + <|> anyControlSeq)) + optional sp + optional (symbol ',') + optional sp + return (T.unpack key, T.unpack . untokenize $ val) -graphicsPath :: PandocMonad m => LP m Blocks -graphicsPath = do - ps <- bgroup *> (manyTill braced egroup) - getResourcePath >>= setResourcePath . (++ ps) - return mempty +keyvals :: PandocMonad m => LP m [(String, String)] +keyvals = try $ symbol '[' >> manyTill keyval (symbol ']') -addMeta :: PandocMonad m => ToMetaValue a => String -> a -> LP m () -addMeta field val = updateState $ \st -> - st{ stateMeta = addMetaField field val $ stateMeta st } +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) + [] -> mzero + _ -> return ils -splitBibs :: String -> [Inlines] -splitBibs = map (str . flip replaceExtension "bib" . trim) . splitBy (==',') +grave :: Char -> String +grave 'A' = "À" +grave 'E' = "È" +grave 'I' = "Ì" +grave 'O' = "Ò" +grave 'U' = "Ù" +grave 'a' = "à" +grave 'e' = "è" +grave 'i' = "ì" +grave 'o' = "ò" +grave 'u' = "ù" +grave c = [c] -setCaption :: PandocMonad m => LP m Blocks -setCaption = do - ils <- tok - mblabel <- option Nothing $ - try $ spaces' >> controlSeq "label" >> (Just <$> tok) - let ils' = case mblabel of - Just lab -> ils <> spanWith - ("",[],[("data-label", stringify lab)]) mempty - Nothing -> ils - updateState $ \st -> st{ stateCaption = Just ils' } - return mempty +acute :: Char -> String +acute 'A' = "Á" +acute 'E' = "É" +acute 'I' = "Í" +acute 'O' = "Ó" +acute 'U' = "Ú" +acute 'Y' = "Ý" +acute 'a' = "á" +acute 'e' = "é" +acute 'i' = "í" +acute 'o' = "ó" +acute 'u' = "ú" +acute 'y' = "ý" +acute 'C' = "Ć" +acute 'c' = "ć" +acute 'L' = "Ĺ" +acute 'l' = "ĺ" +acute 'N' = "Ń" +acute 'n' = "ń" +acute 'R' = "Ŕ" +acute 'r' = "ŕ" +acute 'S' = "Ś" +acute 's' = "ś" +acute 'Z' = "Ź" +acute 'z' = "ź" +acute c = [c] -resetCaption :: PandocMonad m => LP m () -resetCaption = updateState $ \st -> st{ stateCaption = Nothing } +circ :: Char -> String +circ 'A' = "Â" +circ 'E' = "Ê" +circ 'I' = "Î" +circ 'O' = "Ô" +circ 'U' = "Û" +circ 'a' = "â" +circ 'e' = "ê" +circ 'i' = "î" +circ 'o' = "ô" +circ 'u' = "û" +circ 'C' = "Ĉ" +circ 'c' = "ĉ" +circ 'G' = "Ĝ" +circ 'g' = "ĝ" +circ 'H' = "Ĥ" +circ 'h' = "ĥ" +circ 'J' = "Ĵ" +circ 'j' = "ĵ" +circ 'S' = "Ŝ" +circ 's' = "ŝ" +circ 'W' = "Ŵ" +circ 'w' = "ŵ" +circ 'Y' = "Ŷ" +circ 'y' = "ŷ" +circ c = [c] -authors :: PandocMonad m => LP m () -authors = try $ do - bgroup - let oneAuthor = mconcat <$> - many1 (notFollowedBy' (controlSeq "and") >> - (inline <|> mempty <$ blockCommand)) - -- skip e.g. \vspace{10pt} - auths <- sepBy oneAuthor (controlSeq "and") - egroup - addMeta "author" (map trimInlines auths) +tilde :: Char -> String +tilde 'A' = "Ã" +tilde 'a' = "ã" +tilde 'O' = "Õ" +tilde 'o' = "õ" +tilde 'I' = "Ĩ" +tilde 'i' = "ĩ" +tilde 'U' = "Ũ" +tilde 'u' = "ũ" +tilde 'N' = "Ñ" +tilde 'n' = "ñ" +tilde c = [c] -section :: PandocMonad m => Attr -> Int -> LP m Blocks -section (ident, classes, kvs) lvl = do - skipopts - contents <- grouped inline - lab <- option ident $ try (spaces' >> controlSeq "label" >> spaces' >> braced) - attr' <- registerHeader (lab, classes, kvs) contents - return $ headerWith attr' lvl contents +umlaut :: Char -> String +umlaut 'A' = "Ä" +umlaut 'E' = "Ë" +umlaut 'I' = "Ï" +umlaut 'O' = "Ö" +umlaut 'U' = "Ü" +umlaut 'a' = "ä" +umlaut 'e' = "ë" +umlaut 'i' = "ï" +umlaut 'o' = "ö" +umlaut 'u' = "ü" +umlaut c = [c] -inlineCommand :: PandocMonad m => LP m Inlines -inlineCommand = try $ do - (name, raw') <- withRaw anyControlSeq - guard $ name /= "begin" && name /= "end" - star <- option "" (string "*") - let name' = name ++ star +hungarumlaut :: Char -> String +hungarumlaut 'A' = "A̋" +hungarumlaut 'E' = "E̋" +hungarumlaut 'I' = "I̋" +hungarumlaut 'O' = "Ő" +hungarumlaut 'U' = "Ű" +hungarumlaut 'Y' = "ӳ" +hungarumlaut 'a' = "a̋" +hungarumlaut 'e' = "e̋" +hungarumlaut 'i' = "i̋" +hungarumlaut 'o' = "ő" +hungarumlaut 'u' = "ű" +hungarumlaut 'y' = "ӳ" +hungarumlaut c = [c] + +dot :: Char -> String +dot 'C' = "Ċ" +dot 'c' = "ċ" +dot 'E' = "Ė" +dot 'e' = "ė" +dot 'G' = "Ġ" +dot 'g' = "ġ" +dot 'I' = "İ" +dot 'Z' = "Ż" +dot 'z' = "ż" +dot c = [c] + +macron :: Char -> String +macron 'A' = "Ā" +macron 'E' = "Ē" +macron 'I' = "Ī" +macron 'O' = "Ō" +macron 'U' = "Ū" +macron 'a' = "ā" +macron 'e' = "ē" +macron 'i' = "ī" +macron 'o' = "ō" +macron 'u' = "ū" +macron c = [c] + +cedilla :: Char -> String +cedilla 'c' = "ç" +cedilla 'C' = "Ç" +cedilla 's' = "ş" +cedilla 'S' = "Ş" +cedilla 't' = "ţ" +cedilla 'T' = "Ţ" +cedilla 'e' = "ȩ" +cedilla 'E' = "Ȩ" +cedilla 'h' = "ḩ" +cedilla 'H' = "Ḩ" +cedilla 'o' = "o̧" +cedilla 'O' = "O̧" +cedilla c = [c] + +hacek :: Char -> String +hacek 'A' = "Ǎ" +hacek 'a' = "ǎ" +hacek 'C' = "Č" +hacek 'c' = "č" +hacek 'D' = "Ď" +hacek 'd' = "ď" +hacek 'E' = "Ě" +hacek 'e' = "ě" +hacek 'G' = "Ǧ" +hacek 'g' = "ǧ" +hacek 'H' = "Ȟ" +hacek 'h' = "ȟ" +hacek 'I' = "Ǐ" +hacek 'i' = "ǐ" +hacek 'j' = "ǰ" +hacek 'K' = "Ǩ" +hacek 'k' = "ǩ" +hacek 'L' = "Ľ" +hacek 'l' = "ľ" +hacek 'N' = "Ň" +hacek 'n' = "ň" +hacek 'O' = "Ǒ" +hacek 'o' = "ǒ" +hacek 'R' = "Ř" +hacek 'r' = "ř" +hacek 'S' = "Š" +hacek 's' = "š" +hacek 'T' = "Ť" +hacek 't' = "ť" +hacek 'U' = "Ǔ" +hacek 'u' = "ǔ" +hacek 'Z' = "Ž" +hacek 'z' = "ž" +hacek c = [c] + +breve :: Char -> String +breve 'A' = "Ă" +breve 'a' = "ă" +breve 'E' = "Ĕ" +breve 'e' = "ĕ" +breve 'G' = "Ğ" +breve 'g' = "ğ" +breve 'I' = "Ĭ" +breve 'i' = "ĭ" +breve 'O' = "Ŏ" +breve 'o' = "ŏ" +breve 'U' = "Ŭ" +breve 'u' = "ŭ" +breve c = [c] + +toksToString :: [Tok] -> String +toksToString = T.unpack . untokenize + +mathDisplay :: String -> Inlines +mathDisplay = displayMath . trim + +mathInline :: String -> Inlines +mathInline = math . trim + +dollarsMath :: PandocMonad m => LP m Inlines +dollarsMath = do + symbol '$' + display <- option False (True <$ symbol '$') + contents <- trim . toksToString <$> + many (notFollowedBy (symbol '$') >> anyTok) + if display + then do + mathDisplay contents <$ try (symbol '$' >> symbol '$') + <|> (guard (null contents) >> return (mathInline "")) + else mathInline contents <$ (symbol '$') + +-- citations + +addPrefix :: [Inline] -> [Citation] -> [Citation] +addPrefix p (k:ks) = k {citationPrefix = p ++ citationPrefix k} : ks +addPrefix _ _ = [] + +addSuffix :: [Inline] -> [Citation] -> [Citation] +addSuffix s ks@(_:_) = + let k = last ks + in init ks ++ [k {citationSuffix = citationSuffix k ++ s}] +addSuffix _ _ = [] + +simpleCiteArgs :: PandocMonad m => LP m [Citation] +simpleCiteArgs = try $ do + first <- optionMaybe $ toList <$> opt + second <- optionMaybe $ toList <$> opt + keys <- try $ bgroup *> (manyTill citationLabel egroup) + let (pre, suf) = case (first , second ) of + (Just s , Nothing) -> (mempty, s ) + (Just s , Just t ) -> (s , t ) + _ -> (mempty, mempty) + conv k = Citation { citationId = k + , citationPrefix = [] + , citationSuffix = [] + , citationMode = NormalCitation + , citationHash = 0 + , citationNoteNum = 0 + } + return $ addPrefix pre $ addSuffix suf $ map conv keys + +citationLabel :: PandocMonad m => LP m String +citationLabel = do + optional sp + toksToString <$> + (many1 (satisfyTok isWordTok <|> symbolIn bibtexKeyChar) + <* optional sp + <* optional (symbol ',') + <* optional sp) + where bibtexKeyChar = ".:;?!`'()/*@_+=-[]" :: [Char] + +cites :: PandocMonad m => CitationMode -> Bool -> LP m [Citation] +cites mode multi = try $ do + cits <- if multi + then many1 simpleCiteArgs + else count 1 simpleCiteArgs + let cs = concat cits + return $ case mode of + AuthorInText -> case cs of + (c:rest) -> c {citationMode = mode} : rest + [] -> [] + _ -> map (\a -> a {citationMode = mode}) cs + +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 ++ (toksToString raw)) + +handleCitationPart :: Inlines -> [Citation] +handleCitationPart ils = + let isCite Cite{} = True + isCite _ = False + (pref, rest) = break isCite (toList ils) + in case rest of + (Cite cs _:suff) -> addPrefix pref $ addSuffix suff cs + _ -> [] + +complexNatbibCitation :: PandocMonad m => CitationMode -> LP m Inlines +complexNatbibCitation mode = try $ do + (cs, raw) <- + withRaw $ concat <$> do + bgroup + items <- mconcat <$> + many1 (notFollowedBy (symbol ';') >> inline) + `sepBy1` (symbol ';') + egroup + return $ map handleCitationPart items + case cs of + [] -> mzero + (c:cits) -> return $ cite (c{ citationMode = mode }:cits) + (rawInline "latex" $ "\\citetext" ++ toksToString raw) + +inNote :: Inlines -> Inlines +inNote ils = + note $ para $ ils <> str "." + +inlineCommand' :: PandocMonad m => LP m Inlines +inlineCommand' = try $ do + Tok _ (CtrlSeq name) cmd <- anyControlSeq + guard $ name /= "begin" && name /= "end" + (star, rawstar) <- withRaw $ option "" ("*" <$ symbol '*' <* optional sp) + let name' = name <> star + let names = ordNub [name', name] -- check non-starred as fallback let raw = do - guard $ not (isBlockCommand name) - rawargs <- withRaw - (skipangles *> skipopts *> option "" dimenarg *> many braced) - let rawcommand = raw' ++ star ++ snd rawargs - transformed <- applyMacros' rawcommand - exts <- getOption readerExtensions - if transformed /= rawcommand - then parseFromString' inlines transformed - else if extensionEnabled Ext_raw_tex exts - then return $ rawInline "latex" rawcommand - else ignore rawcommand - (lookupListDefault raw [name',name] inlineCommands <* - optional (try (string "{}"))) - -rawInlineOr :: PandocMonad m => String -> LP m Inlines -> LP m Inlines -rawInlineOr name' fallback = do - parseRaw <- extensionEnabled Ext_raw_tex <$> getOption readerExtensions - if parseRaw - then rawInline "latex" <$> getRawCommand name' - else fallback + guard $ isInlineCommand name || not (isBlockCommand name) + (_, rawargs) <- withRaw + (skipangles *> skipopts *> option "" dimenarg *> many braced) + let rawcommand = T.unpack $ cmd <> untokenize (rawstar ++ rawargs) + (guardEnabled Ext_raw_tex >> return (rawInline "latex" rawcommand)) + <|> ignore rawcommand + lookupListDefault raw names inlineCommands + +tok :: PandocMonad m => LP m Inlines +tok = grouped inline <|> inlineCommand' <|> singleChar + where singleChar = try $ do + Tok (lin,col) toktype t <- satisfyTok (tokTypeIn [Word, Symbol]) + guard $ not $ toktype == Symbol && + T.any (`Set.member` specialChars) t + if T.length t > 1 + then do + let (t1, t2) = (T.take 1 t, T.drop 1 t) + inp <- getInput + setInput $ (Tok (lin, col + 1) toktype t2) : inp + return $ str (T.unpack t1) + else return $ str (T.unpack t) + +opt :: PandocMonad m => LP m Inlines +opt = bracketed inline + +rawopt :: PandocMonad m => LP m Text +rawopt = do + symbol '[' + inner <- untokenize <$> manyTill anyTok (symbol ']') + optional sp + return $ "[" <> inner <> "]" + +skipopts :: PandocMonad m => LP m () +skipopts = skipMany rawopt + +-- opts in angle brackets are used in beamer +rawangle :: PandocMonad m => LP m () +rawangle = try $ do + symbol '<' + () <$ manyTill anyTok (symbol '>') + +skipangles :: PandocMonad m => LP m () +skipangles = skipMany rawangle + +ignore :: (Monoid a, PandocMonad m) => String -> ParserT s u m a +ignore raw = do + pos <- getPosition + report $ SkippedContent raw pos + return mempty + +withRaw :: PandocMonad m => LP m a -> LP m (a, [Tok]) +withRaw parser = do + inp <- getInput + result <- parser + nxt <- option (Tok (0,0) Word "") (lookAhead anyTok) + let raw = takeWhile (/= nxt) inp + return (result, raw) + +inBrackets :: Inlines -> Inlines +inBrackets x = str "[" <> x <> str "]" + +unescapeURL :: String -> String +unescapeURL ('\\':x:xs) | isEscapable x = x:unescapeURL xs + where isEscapable c = c `elem` ("#$%&~_^\\{}" :: String) +unescapeURL (x:xs) = x:unescapeURL xs +unescapeURL [] = "" + +mathEnvWith :: PandocMonad m + => (Inlines -> a) -> Maybe Text -> Text -> LP m a +mathEnvWith f innerEnv name = f . mathDisplay . inner <$> mathEnv name + where inner x = case innerEnv of + Nothing -> x + Just y -> "\\begin{" ++ T.unpack y ++ "}\n" ++ x ++ + "\\end{" ++ T.unpack y ++ "}" -isBlockCommand :: String -> Bool -isBlockCommand s = s `M.member` (blockCommands :: M.Map String (LP PandocPure Blocks)) +mathEnv :: PandocMonad m => Text -> LP m String +mathEnv name = do + skipopts + optional blankline + res <- manyTill anyTok (end_ name) + return $ stripTrailingNewlines $ T.unpack $ untokenize res +inlineEnvironment :: PandocMonad m => LP m Inlines +inlineEnvironment = try $ do + controlSeq "begin" + name <- untokenize <$> braced + M.findWithDefault mzero name inlineEnvironments -inlineEnvironments :: PandocMonad m => M.Map String (LP m Inlines) -inlineEnvironments = M.fromList - [ ("displaymath", mathEnvWith id Nothing "displaymath") +inlineEnvironments :: PandocMonad m => M.Map Text (LP m Inlines) +inlineEnvironments = M.fromList [ + ("displaymath", mathEnvWith id Nothing "displaymath") , ("math", math <$> mathEnv "math") , ("equation", mathEnvWith id Nothing "equation") , ("equation*", mathEnvWith id Nothing "equation*") @@ -511,7 +1159,7 @@ inlineEnvironments = M.fromList , ("alignat*", mathEnvWith id (Just "aligned") "alignat*") ] -inlineCommands :: PandocMonad m => M.Map String (LP m Inlines) +inlineCommands :: PandocMonad m => M.Map Text (LP m Inlines) inlineCommands = M.fromList $ [ ("emph", extractSpaces emph <$> tok) , ("textit", extractSpaces emph <$> tok) @@ -540,9 +1188,9 @@ inlineCommands = M.fromList $ , ("textgreek", tok) , ("sep", lit ",") , ("cref", rawInlineOr "cref" (inBrackets <$> tok)) -- from cleveref.sty - , ("(", mathInline $ manyTill anyChar (try $ string "\\)")) - , ("[", mathDisplay $ manyTill anyChar (try $ string "\\]")) - , ("ensuremath", mathInline braced) + , ("(", mathInline . toksToString <$> manyTill anyTok (controlSeq ")")) + , ("[", mathDisplay . toksToString <$> manyTill anyTok (controlSeq "]")) + , ("ensuremath", mathInline . toksToString <$> braced) , ("texorpdfstring", (\_ x -> x) <$> tok <*> tok) , ("P", lit "¶") , ("S", lit "§") @@ -592,7 +1240,10 @@ inlineCommands = M.fromList $ , ("v", option (str "v") $ try $ tok >>= accent hacek) , ("u", option (str "u") $ try $ tok >>= accent breve) , ("i", lit "i") - , ("\\", linebreak <$ (optional (bracketed inline) *> spaces')) + , ("\\", linebreak <$ (do inTableCell <- sInTableCell <$> getState + guard $ not inTableCell + optional (bracketed inline) + spaces)) , (",", lit "\8198") , ("@", pure mempty) , (" ", lit "\160") @@ -607,13 +1258,14 @@ inlineCommands = M.fromList $ , ("verb", doverb) , ("lstinline", dolstinline) , ("Verb", doverb) - , ("url", (unescapeURL <$> braced) >>= \url -> - pure (link url "" (str url))) - , ("href", (unescapeURL <$> braced <* optional sp) >>= \url -> - tok >>= \lab -> - pure (link url "" lab)) + , ("url", ((unescapeURL . T.unpack . untokenize) <$> braced) >>= \url -> + pure (link url "" (str url))) + , ("href", (unescapeURL . toksToString <$> + braced <* optional sp) >>= \url -> + tok >>= \lab -> pure (link url "" lab)) , ("includegraphics", do options <- option [] keyvals - src <- unescapeURL . removeDoubleQuotes <$> braced + src <- unescapeURL . T.unpack . + removeDoubleQuotes . untokenize <$> braced mkImage options src) , ("enquote", enquote) , ("cite", citation "cite" NormalCitation False) @@ -686,362 +1338,456 @@ inlineCommands = M.fromList $ -- fontawesome , ("faCheck", lit "\10003") , ("faClose", lit "\10007") - ] ++ map ignoreInlines - -- these commands will be ignored unless --parse-raw is specified, - -- in which case they will appear as raw latex blocks: + ] + +ttfamily :: PandocMonad m => LP m Inlines +ttfamily = (code . stringify . toList) <$> tok + +rawInlineOr :: PandocMonad m => Text -> LP m Inlines -> LP m Inlines +rawInlineOr name' fallback = do + parseRaw <- extensionEnabled Ext_raw_tex <$> getOption readerExtensions + if parseRaw + then rawInline "latex" <$> getRawCommand name' + else fallback + +getRawCommand :: PandocMonad m => Text -> LP m String +getRawCommand txt = do + (_, rawargs) <- withRaw + (many (try (optional sp *> opt)) *> + option "" (try (optional sp *> dimenarg)) *> + many braced) + return $ T.unpack (txt <> untokenize rawargs) + +isBlockCommand :: Text -> Bool +isBlockCommand s = + s `M.member` (blockCommands :: M.Map Text (LP PandocPure Blocks)) + || s `Set.member` treatAsBlock + +treatAsBlock :: Set.Set Text +treatAsBlock = Set.fromList + [ "newcommand", "renewcommand" + , "newenvironment", "renewenvironment" + , "providecommand", "provideenvironment" + -- newcommand, etc. should be parsed by macroDef, but we need this + -- here so these aren't parsed as inline commands to ignore + , "special", "pdfannot", "pdfstringdef" + , "bibliographystyle" + , "maketitle", "makeindex", "makeglossary" + , "addcontentsline", "addtocontents", "addtocounter" + -- \ignore{} is used conventionally in literate haskell for definitions + -- that are to be processed by the compiler but not printed. + , "ignore" + , "hyperdef" + , "markboth", "markright", "markleft" + , "hspace", "vspace" + , "newpage" + , "clearpage" + , "pagebreak" + ] + +isInlineCommand :: Text -> Bool +isInlineCommand s = + s `M.member` (inlineCommands :: M.Map Text (LP PandocPure Inlines)) + || s `Set.member` treatAsInline + +treatAsInline :: Set.Set Text +treatAsInline = Set.fromList [ "index" , "hspace" , "vspace" + , "noindent" , "newpage" , "clearpage" , "pagebreak" ] -ttfamily :: PandocMonad m => LP m Inlines -ttfamily = (code . stringify . toList) <$> tok +lookupListDefault :: (Show k, Ord k) => v -> [k] -> M.Map k v -> v +lookupListDefault d = (fromMaybe d .) . lookupList + where lookupList l m = msum $ map (`M.lookup` m) l -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) ++ "%") - _ -> (k, v) - let kvs = map replaceTextwidth $ filter (\(k,_) -> k `elem` ["width", "height"]) options - let attr = ("",[], kvs) - let alt = str "image" - case takeExtension src of - "" -> do - defaultExt <- getOption readerDefaultImageExtension - return $ imageWith attr (addExtension src defaultExt) "" alt - _ -> return $ imageWith attr src "" alt +inline :: PandocMonad m => LP m Inlines +inline = (mempty <$ comment) + <|> (space <$ whitespace) + <|> (softbreak <$ endline) + <|> word + <|> inlineCommand' + <|> inlineEnvironment + <|> inlineGroup + <|> (symbol '-' *> + option (str "-") (symbol '-' *> + option (str "–") (str "—" <$ symbol '-'))) + <|> doubleQuote + <|> singleQuote + <|> (str "”" <$ try (symbol '\'' >> symbol '\'')) + <|> (str "”" <$ symbol '”') + <|> (str "’" <$ symbol '\'') + <|> (str "’" <$ symbol '’') + <|> (str "\160" <$ symbol '~') + <|> dollarsMath + <|> (guardEnabled Ext_literate_haskell *> symbol '|' *> doLHSverb) + <|> (str . (:[]) <$> primEscape) + <|> regularSymbol + <|> (do res <- symbolIn "#^'`\"[]" + pos <- getPosition + let s = T.unpack (untoken res) + report $ ParsingUnescaped s pos + return $ str s) -inNote :: Inlines -> Inlines -inNote ils = - note $ para $ ils <> str "." +inlines :: PandocMonad m => LP m Inlines +inlines = mconcat <$> many inline -unescapeURL :: String -> String -unescapeURL ('\\':x:xs) | isEscapable x = x:unescapeURL xs - where isEscapable c = c `elem` ("#$%&~_^\\{}" :: String) -unescapeURL (x:xs) = x:unescapeURL xs -unescapeURL [] = "" - -enquote :: PandocMonad m => LP m Inlines -enquote = do - skipopts - context <- stateQuoteContext <$> getState - if context == InDoubleQuote - then singleQuoted <$> withQuoteContext InSingleQuote tok - else doubleQuoted <$> withQuoteContext InDoubleQuote tok - -doverb :: PandocMonad m => LP m Inlines -doverb = do - marker <- anyChar - code <$> manyTill (satisfy (/='\n')) (char marker) - -dolstinline :: PandocMonad m => LP m Inlines -dolstinline = do - options <- option [] keyvals - let classes = maybeToList $ lookup "language" options >>= fromListingsLanguage - marker <- char '{' <|> anyChar - codeWith ("",classes,[]) <$> manyTill (satisfy (/='\n')) (char '}' <|> char marker) - -doLHSverb :: PandocMonad m => LP m Inlines -doLHSverb = codeWith ("",["haskell"],[]) <$> manyTill (satisfy (/='\n')) (char '|') - --- converts e.g. \SI{1}[\$]{} to "$ 1" or \SI{1}{\euro} to "1 €" -dosiunitx :: PandocMonad m => LP m Inlines -dosiunitx = do - skipopts - value <- tok - valueprefix <- option "" $ char '[' >> (mconcat <$> manyTill tok (char ']')) - unit <- tok - let emptyOr160 "" = "" - emptyOr160 _ = "\160" - return . mconcat $ [valueprefix, - emptyOr160 valueprefix, - value, - emptyOr160 unit, - unit] - -lit :: String -> LP m Inlines -lit = pure . str - -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) - [] -> mzero - _ -> return ils - -grave :: Char -> String -grave 'A' = "À" -grave 'E' = "È" -grave 'I' = "Ì" -grave 'O' = "Ò" -grave 'U' = "Ù" -grave 'a' = "à" -grave 'e' = "è" -grave 'i' = "ì" -grave 'o' = "ò" -grave 'u' = "ù" -grave c = [c] - -acute :: Char -> String -acute 'A' = "Á" -acute 'E' = "É" -acute 'I' = "Í" -acute 'O' = "Ó" -acute 'U' = "Ú" -acute 'Y' = "Ý" -acute 'a' = "á" -acute 'e' = "é" -acute 'i' = "í" -acute 'o' = "ó" -acute 'u' = "ú" -acute 'y' = "ý" -acute 'C' = "Ć" -acute 'c' = "ć" -acute 'L' = "Ĺ" -acute 'l' = "ĺ" -acute 'N' = "Ń" -acute 'n' = "ń" -acute 'R' = "Ŕ" -acute 'r' = "ŕ" -acute 'S' = "Ś" -acute 's' = "ś" -acute 'Z' = "Ź" -acute 'z' = "ź" -acute c = [c] - -circ :: Char -> String -circ 'A' = "Â" -circ 'E' = "Ê" -circ 'I' = "Î" -circ 'O' = "Ô" -circ 'U' = "Û" -circ 'a' = "â" -circ 'e' = "ê" -circ 'i' = "î" -circ 'o' = "ô" -circ 'u' = "û" -circ 'C' = "Ĉ" -circ 'c' = "ĉ" -circ 'G' = "Ĝ" -circ 'g' = "ĝ" -circ 'H' = "Ĥ" -circ 'h' = "ĥ" -circ 'J' = "Ĵ" -circ 'j' = "ĵ" -circ 'S' = "Ŝ" -circ 's' = "ŝ" -circ 'W' = "Ŵ" -circ 'w' = "ŵ" -circ 'Y' = "Ŷ" -circ 'y' = "ŷ" -circ c = [c] +-- block elements: -tilde :: Char -> String -tilde 'A' = "Ã" -tilde 'a' = "ã" -tilde 'O' = "Õ" -tilde 'o' = "õ" -tilde 'I' = "Ĩ" -tilde 'i' = "ĩ" -tilde 'U' = "Ũ" -tilde 'u' = "ũ" -tilde 'N' = "Ñ" -tilde 'n' = "ñ" -tilde c = [c] +begin_ :: PandocMonad m => Text -> LP m () +begin_ t = (try $ do + controlSeq "begin" + spaces + symbol '{' + spaces + Tok _ Word txt <- satisfyTok isWordTok + spaces + symbol '}' + guard (t == txt)) <?> ("\\begin{" ++ T.unpack t ++ "}") -umlaut :: Char -> String -umlaut 'A' = "Ä" -umlaut 'E' = "Ë" -umlaut 'I' = "Ï" -umlaut 'O' = "Ö" -umlaut 'U' = "Ü" -umlaut 'a' = "ä" -umlaut 'e' = "ë" -umlaut 'i' = "ï" -umlaut 'o' = "ö" -umlaut 'u' = "ü" -umlaut c = [c] +end_ :: PandocMonad m => Text -> LP m () +end_ t = (try $ do + controlSeq "end" + spaces + symbol '{' + spaces + Tok _ Word txt <- satisfyTok isWordTok + spaces + symbol '}' + guard $ t == txt) <?> ("\\end{" ++ T.unpack t ++ "}") -hungarumlaut :: Char -> String -hungarumlaut 'A' = "A̋" -hungarumlaut 'E' = "E̋" -hungarumlaut 'I' = "I̋" -hungarumlaut 'O' = "Ő" -hungarumlaut 'U' = "Ű" -hungarumlaut 'Y' = "ӳ" -hungarumlaut 'a' = "a̋" -hungarumlaut 'e' = "e̋" -hungarumlaut 'i' = "i̋" -hungarumlaut 'o' = "ő" -hungarumlaut 'u' = "ű" -hungarumlaut 'y' = "ӳ" -hungarumlaut c = [c] +preamble :: PandocMonad m => LP m Blocks +preamble = mempty <$ many preambleBlock + where preambleBlock = spaces1 + <|> void include + <|> void macroDef + <|> void blockCommand + <|> void braced + <|> (notFollowedBy (begin_ "document") >> void anyTok) -dot :: Char -> String -dot 'C' = "Ċ" -dot 'c' = "ċ" -dot 'E' = "Ė" -dot 'e' = "ė" -dot 'G' = "Ġ" -dot 'g' = "ġ" -dot 'I' = "İ" -dot 'Z' = "Ż" -dot 'z' = "ż" -dot c = [c] +paragraph :: PandocMonad m => LP m Blocks +paragraph = do + x <- trimInlines . mconcat <$> many1 inline + if x == mempty + then return mempty + else return $ para x -macron :: Char -> String -macron 'A' = "Ā" -macron 'E' = "Ē" -macron 'I' = "Ī" -macron 'O' = "Ō" -macron 'U' = "Ū" -macron 'a' = "ā" -macron 'e' = "ē" -macron 'i' = "ī" -macron 'o' = "ō" -macron 'u' = "ū" -macron c = [c] +include :: PandocMonad m => LP m Blocks +include = do + (Tok _ (CtrlSeq name) _) <- + controlSeq "include" <|> controlSeq "input" <|> + controlSeq "subfile" <|> controlSeq "usepackage" + skipMany $ bracketed inline -- skip options + fs <- (map trim . splitBy (==',') . T.unpack . untokenize) <$> braced + let fs' = if name == "usepackage" + then map (maybeAddExtension ".sty") fs + else map (maybeAddExtension ".tex") fs + dirs <- (splitBy (==':') . fromMaybe ".") <$> lookupEnv "TEXINPUTS" + mconcat <$> mapM (insertIncludedFile blocks (tokenize . T.pack) dirs) fs' -cedilla :: Char -> String -cedilla 'c' = "ç" -cedilla 'C' = "Ç" -cedilla 's' = "ş" -cedilla 'S' = "Ş" -cedilla 't' = "ţ" -cedilla 'T' = "Ţ" -cedilla 'e' = "ȩ" -cedilla 'E' = "Ȩ" -cedilla 'h' = "ḩ" -cedilla 'H' = "Ḩ" -cedilla 'o' = "o̧" -cedilla 'O' = "O̧" -cedilla c = [c] +maybeAddExtension :: String -> FilePath -> FilePath +maybeAddExtension ext fp = + if null (takeExtension fp) + then addExtension fp ext + else fp -hacek :: Char -> String -hacek 'A' = "Ǎ" -hacek 'a' = "ǎ" -hacek 'C' = "Č" -hacek 'c' = "č" -hacek 'D' = "Ď" -hacek 'd' = "ď" -hacek 'E' = "Ě" -hacek 'e' = "ě" -hacek 'G' = "Ǧ" -hacek 'g' = "ǧ" -hacek 'H' = "Ȟ" -hacek 'h' = "ȟ" -hacek 'I' = "Ǐ" -hacek 'i' = "ǐ" -hacek 'j' = "ǰ" -hacek 'K' = "Ǩ" -hacek 'k' = "ǩ" -hacek 'L' = "Ľ" -hacek 'l' = "ľ" -hacek 'N' = "Ň" -hacek 'n' = "ň" -hacek 'O' = "Ǒ" -hacek 'o' = "ǒ" -hacek 'R' = "Ř" -hacek 'r' = "ř" -hacek 'S' = "Š" -hacek 's' = "š" -hacek 'T' = "Ť" -hacek 't' = "ť" -hacek 'U' = "Ǔ" -hacek 'u' = "ǔ" -hacek 'Z' = "Ž" -hacek 'z' = "ž" -hacek c = [c] +addMeta :: PandocMonad m => ToMetaValue a => String -> a -> LP m () +addMeta field val = updateState $ \st -> + st{ sMeta = addMetaField field val $ sMeta st } -breve :: Char -> String -breve 'A' = "Ă" -breve 'a' = "ă" -breve 'E' = "Ĕ" -breve 'e' = "ĕ" -breve 'G' = "Ğ" -breve 'g' = "ğ" -breve 'I' = "Ĭ" -breve 'i' = "ĭ" -breve 'O' = "Ŏ" -breve 'o' = "ŏ" -breve 'U' = "Ŭ" -breve 'u' = "ŭ" -breve c = [c] +authors :: PandocMonad m => LP m () +authors = try $ do + bgroup + let oneAuthor = mconcat <$> + many1 (notFollowedBy' (controlSeq "and") >> + (inline <|> mempty <$ blockCommand)) + -- skip e.g. \vspace{10pt} + auths <- sepBy oneAuthor (controlSeq "and") + egroup + addMeta "author" (map trimInlines auths) -tok :: PandocMonad m => LP m Inlines -tok = try $ grouped inline <|> inlineCommand <|> str <$> count 1 inlineChar +macroDef :: PandocMonad m => LP m Blocks +macroDef = do + guardEnabled Ext_latex_macros + mempty <$ ((commandDef <|> environmentDef) <* doMacros 0) + where commandDef = do + (name, macro') <- newcommand + updateState $ \s -> s{ sMacros = M.insert name macro' (sMacros s) } + environmentDef = do + (name, macro1, macro2) <- newenvironment + updateState $ \s -> s{ sMacros = + M.insert name macro1 (sMacros s) } + updateState $ \s -> s{ sMacros = + M.insert ("end" <> name) macro2 (sMacros s) } + -- @\newenvironment{envname}[n-args][default]{begin}{end}@ + -- is equivalent to + -- @\newcommand{\envname}[n-args][default]{begin}@ + -- @\newcommand{\endenvname}@ + +newcommand :: PandocMonad m => LP m (Text, Macro) +newcommand = do + pos <- getPosition + Tok _ (CtrlSeq mtype) _ <- controlSeq "newcommand" <|> + controlSeq "renewcommand" <|> + controlSeq "providecommand" + optional $ symbol '*' + Tok _ (CtrlSeq name) txt <- withVerbatimMode $ anyControlSeq <|> + (symbol '{' *> spaces *> anyControlSeq <* spaces <* symbol '}') + spaces + numargs <- option 0 $ try bracketedNum + spaces + optarg <- option Nothing $ Just <$> try bracketedToks + spaces + contents <- braced + when (mtype == "newcommand") $ do + macros <- sMacros <$> getState + case M.lookup name macros of + Just _ -> report $ MacroAlreadyDefined (T.unpack txt) pos + Nothing -> return () + return (name, Macro numargs optarg contents) + +newenvironment :: PandocMonad m => LP m (Text, Macro, Macro) +newenvironment = do + pos <- getPosition + Tok _ (CtrlSeq mtype) _ <- controlSeq "newenvironment" <|> + controlSeq "renewenvironment" <|> + controlSeq "provideenvironment" + optional $ symbol '*' + symbol '{' + spaces + Tok _ Word name <- satisfyTok isWordTok + spaces + symbol '}' + spaces + numargs <- option 0 $ try bracketedNum + spaces + optarg <- option Nothing $ Just <$> try bracketedToks + spaces + startcontents <- braced + spaces + endcontents <- braced + when (mtype == "newenvironment") $ do + macros <- sMacros <$> getState + case M.lookup name macros of + Just _ -> report $ MacroAlreadyDefined (T.unpack name) pos + Nothing -> return () + return (name, Macro numargs optarg startcontents, + Macro 0 Nothing endcontents) + +bracketedToks :: PandocMonad m => LP m [Tok] +bracketedToks = do + symbol '[' + manyTill anyTok (symbol ']') + +bracketedNum :: PandocMonad m => LP m Int +bracketedNum = do + ds <- untokenize <$> bracketedToks + case safeRead (T.unpack ds) of + Just i -> return i + _ -> return 0 -opt :: PandocMonad m => LP m Inlines -opt = bracketed inline +setCaption :: PandocMonad m => LP m Blocks +setCaption = do + ils <- tok + mblabel <- option Nothing $ + try $ spaces >> controlSeq "label" >> (Just <$> tok) + let ils' = case mblabel of + Just lab -> ils <> spanWith + ("",[],[("data-label", stringify lab)]) mempty + Nothing -> ils + updateState $ \st -> st{ sCaption = Just ils' } + return mempty -rawopt :: PandocMonad m => LP m String -rawopt = do - contents <- bracketed (many1 (noneOf "[]") <|> try (string "\\]") <|> - try (string "\\[") <|> rawopt) - optional sp - return $ "[" ++ contents ++ "]" +looseItem :: PandocMonad m => LP m Blocks +looseItem = do + inListItem <- sInListItem <$> getState + guard $ not inListItem + skipopts + return mempty -skipopts :: PandocMonad m => LP m () -skipopts = skipMany rawopt +resetCaption :: PandocMonad m => LP m () +resetCaption = updateState $ \st -> st{ sCaption = Nothing } --- opts in angle brackets are used in beamer -rawangle :: PandocMonad m => LP m () -rawangle = try $ do - char '<' - skipMany (noneOf ">") - char '>' - return () +section :: PandocMonad m => Attr -> Int -> LP m Blocks +section (ident, classes, kvs) lvl = do + skipopts + contents <- grouped inline + lab <- option ident $ + try (spaces >> controlSeq "label" + >> spaces >> toksToString <$> braced) + attr' <- registerHeader (lab, classes, kvs) contents + return $ headerWith attr' lvl contents -skipangles :: PandocMonad m => LP m () -skipangles = skipMany rawangle +blockCommand :: PandocMonad m => LP m Blocks +blockCommand = try $ do + Tok _ (CtrlSeq name) txt <- anyControlSeq + guard $ name /= "begin" && name /= "end" + star <- option "" ("*" <$ symbol '*' <* optional sp) + let name' = name <> star + let names = ordNub [name', name] + let raw = do + guard $ isBlockCommand name || not (isInlineCommand name) + rawBlock "latex" <$> getRawCommand txt + lookupListDefault raw names blockCommands -inlineText :: PandocMonad m => LP m Inlines -inlineText = str <$> many1 inlineChar +closing :: PandocMonad m => LP m Blocks +closing = do + contents <- tok + st <- getState + let extractInlines (MetaBlocks [Plain ys]) = ys + extractInlines (MetaBlocks [Para ys ]) = ys + extractInlines _ = [] + let sigs = case lookupMeta "author" (sMeta st) of + Just (MetaList xs) -> + para $ trimInlines $ fromList $ + intercalate [LineBreak] $ map extractInlines xs + _ -> mempty + return $ para (trimInlines contents) <> sigs -inlineChar :: PandocMonad m => LP m Char -inlineChar = noneOf "\\$%&~#{}^'`\"‘’“”-[] \t\n" +blockCommands :: PandocMonad m => M.Map Text (LP m Blocks) +blockCommands = M.fromList $ + [ ("par", mempty <$ skipopts) + , ("parbox", braced >> grouped blocks) + , ("title", mempty <$ (skipopts *> + (grouped inline >>= addMeta "title") + <|> (grouped block >>= addMeta "title"))) + , ("subtitle", mempty <$ (skipopts *> tok >>= addMeta "subtitle")) + , ("author", mempty <$ (skipopts *> authors)) + -- -- in letter class, temp. store address & sig as title, author + , ("address", mempty <$ (skipopts *> tok >>= addMeta "address")) + , ("signature", mempty <$ (skipopts *> authors)) + , ("date", mempty <$ (skipopts *> tok >>= addMeta "date")) + -- Koma-script metadata commands + , ("dedication", mempty <$ (skipopts *> tok >>= addMeta "dedication")) + -- sectioning + , ("part", section nullAttr (-1)) + , ("part*", section nullAttr (-1)) + , ("chapter", section nullAttr 0) + , ("chapter*", section ("",["unnumbered"],[]) 0) + , ("section", section nullAttr 1) + , ("section*", section ("",["unnumbered"],[]) 1) + , ("subsection", section nullAttr 2) + , ("subsection*", section ("",["unnumbered"],[]) 2) + , ("subsubsection", section nullAttr 3) + , ("subsubsection*", section ("",["unnumbered"],[]) 3) + , ("paragraph", section nullAttr 4) + , ("paragraph*", section ("",["unnumbered"],[]) 4) + , ("subparagraph", section nullAttr 5) + , ("subparagraph*", section ("",["unnumbered"],[]) 5) + -- beamer slides + , ("frametitle", section nullAttr 3) + , ("framesubtitle", section nullAttr 4) + -- letters + , ("opening", (para . trimInlines) <$> (skipopts *> tok)) + , ("closing", skipopts *> closing) + -- + , ("hrule", pure horizontalRule) + , ("strut", pure mempty) + , ("rule", skipopts *> tok *> tok *> pure horizontalRule) + , ("item", looseItem) + , ("documentclass", skipopts *> braced *> preamble) + , ("centerline", (para . trimInlines) <$> (skipopts *> tok)) + , ("caption", skipopts *> setCaption) + , ("bibliography", mempty <$ (skipopts *> braced >>= + addMeta "bibliography" . splitBibs . toksToString)) + , ("addbibresource", mempty <$ (skipopts *> braced >>= + addMeta "bibliography" . splitBibs . toksToString)) + -- includes + , ("lstinputlisting", inputListing) + , ("graphicspath", graphicsPath) + -- hyperlink + , ("hypertarget", try $ braced >> grouped block) + ] + + +environments :: PandocMonad m => M.Map Text (LP m Blocks) +environments = M.fromList + [ ("document", env "document" blocks) + , ("abstract", mempty <$ (env "abstract" blocks >>= addMeta "abstract")) + , ("letter", env "letter" letterContents) + , ("minipage", env "minipage" $ + skipopts *> spaces *> optional braced *> spaces *> blocks) + , ("figure", env "figure" $ skipopts *> figure) + , ("subfigure", env "subfigure" $ skipopts *> tok *> figure) + , ("center", env "center" blocks) + , ("longtable", env "longtable" $ + resetCaption *> simpTable "longtable" False >>= addTableCaption) + , ("table", env "table" $ + resetCaption *> skipopts *> blocks >>= addTableCaption) + , ("tabular*", env "tabular" $ simpTable "tabular*" True) + , ("tabularx", env "tabularx" $ simpTable "tabularx" True) + , ("tabular", env "tabular" $ simpTable "tabular" False) + , ("quote", blockQuote <$> env "quote" blocks) + , ("quotation", blockQuote <$> env "quotation" blocks) + , ("verse", blockQuote <$> env "verse" blocks) + , ("itemize", bulletList <$> listenv "itemize" (many item)) + , ("description", definitionList <$> listenv "description" (many descItem)) + , ("enumerate", orderedList') + , ("alltt", alltt <$> env "alltt" blocks) + , ("code", guardEnabled Ext_literate_haskell *> + (codeBlockWith ("",["sourceCode","literate","haskell"],[]) <$> + verbEnv "code")) + , ("comment", mempty <$ verbEnv "comment") + , ("verbatim", codeBlock <$> verbEnv "verbatim") + , ("Verbatim", fancyverbEnv "Verbatim") + , ("BVerbatim", fancyverbEnv "BVerbatim") + , ("lstlisting", do attr <- parseListingsOptions <$> option [] keyvals + codeBlockWith attr <$> verbEnv "lstlisting") + , ("minted", minted) + , ("obeylines", obeylines) + , ("displaymath", mathEnvWith para Nothing "displaymath") + , ("equation", mathEnvWith para Nothing "equation") + , ("equation*", mathEnvWith para Nothing "equation*") + , ("gather", mathEnvWith para (Just "gathered") "gather") + , ("gather*", mathEnvWith para (Just "gathered") "gather*") + , ("multline", mathEnvWith para (Just "gathered") "multline") + , ("multline*", mathEnvWith para (Just "gathered") "multline*") + , ("eqnarray", mathEnvWith para (Just "aligned") "eqnarray") + , ("eqnarray*", mathEnvWith para (Just "aligned") "eqnarray*") + , ("align", mathEnvWith para (Just "aligned") "align") + , ("align*", mathEnvWith para (Just "aligned") "align*") + , ("alignat", mathEnvWith para (Just "aligned") "alignat") + , ("alignat*", mathEnvWith para (Just "aligned") "alignat*") + , ("tikzpicture", rawVerbEnv "tikzpicture") + ] environment :: PandocMonad m => LP m Blocks environment = do controlSeq "begin" - name <- braced + name <- untokenize <$> braced M.findWithDefault mzero name environments <|> rawEnv name -inlineEnvironment :: PandocMonad m => LP m Inlines -inlineEnvironment = try $ do - controlSeq "begin" - name <- braced - M.findWithDefault mzero name inlineEnvironments +env :: PandocMonad m => Text -> LP m a -> LP m a +env name p = p <* end_ name -rawEnv :: PandocMonad m => String -> LP m Blocks +rawEnv :: PandocMonad m => Text -> LP m Blocks rawEnv name = do exts <- getOption readerExtensions let parseRaw = extensionEnabled Ext_raw_tex exts rawOptions <- mconcat <$> many rawopt - let beginCommand = "\\begin{" ++ name ++ "}" ++ rawOptions + let beginCommand = "\\begin{" <> name <> "}" <> rawOptions pos1 <- getPosition (bs, raw) <- withRaw $ env name blocks - raw' <- applyMacros' $ beginCommand ++ raw - if raw' /= beginCommand ++ raw - then parseFromString' blocks raw' - else if parseRaw - then return $ rawBlock "latex" $ beginCommand ++ raw' - else do - unless parseRaw $ do - report $ SkippedContent beginCommand pos1 - pos2 <- getPosition - report $ SkippedContent ("\\end{" ++ name ++ "}") pos2 - return bs - -rawVerbEnv :: PandocMonad m => String -> LP m Blocks + if parseRaw + then return $ rawBlock "latex" + $ T.unpack $ beginCommand <> untokenize raw + else do + unless parseRaw $ do + report $ SkippedContent (T.unpack beginCommand) pos1 + pos2 <- getPosition + report $ SkippedContent ("\\end{" ++ T.unpack name ++ "}") pos2 + return bs + +rawVerbEnv :: PandocMonad m => Text -> LP m Blocks rawVerbEnv name = do pos <- getPosition (_, raw) <- withRaw $ verbEnv name - let raw' = "\\begin{tikzpicture}" ++ raw + let raw' = "\\begin{tikzpicture}" ++ toksToString raw exts <- getOption readerExtensions let parseRaw = extensionEnabled Ext_raw_tex exts if parseRaw @@ -1050,36 +1796,106 @@ rawVerbEnv name = do report $ SkippedContent raw' pos return mempty ----- +verbEnv :: PandocMonad m => Text -> LP m String +verbEnv name = withVerbatimMode $ do + skipopts + optional blankline + res <- manyTill anyTok (end_ name) + return $ stripTrailingNewlines $ toksToString res + +fancyverbEnv :: PandocMonad m => Text -> LP m Blocks +fancyverbEnv name = do + options <- option [] keyvals + let kvs = [ (if k == "firstnumber" + then "startFrom" + else k, v) | (k,v) <- options ] + let classes = [ "numberLines" | + lookup "numbers" options == Just "left" ] + let attr = ("",classes,kvs) + codeBlockWith attr <$> verbEnv name + +obeylines :: PandocMonad m => LP m Blocks +obeylines = do + para . fromList . removeLeadingTrailingBreaks . + walk softBreakToHard . toList <$> env "obeylines" inlines + where softBreakToHard SoftBreak = LineBreak + softBreakToHard x = x + removeLeadingTrailingBreaks = reverse . dropWhile isLineBreak . + reverse . dropWhile isLineBreak + isLineBreak LineBreak = True + isLineBreak _ = False + +minted :: PandocMonad m => LP m Blocks +minted = do + options <- option [] keyvals + lang <- toksToString <$> braced + let kvs = [ (if k == "firstnumber" + then "startFrom" + else k, v) | (k,v) <- options ] + let classes = [ lang | not (null lang) ] ++ + [ "numberLines" | + lookup "linenos" options == Just "true" ] + let attr = ("",classes,kvs) + codeBlockWith attr <$> verbEnv "minted" + +letterContents :: PandocMonad m => LP m Blocks +letterContents = do + bs <- blocks + st <- getState + -- add signature (author) and address (title) + let addr = case lookupMeta "address" (sMeta st) of + Just (MetaBlocks [Plain xs]) -> + para $ trimInlines $ fromList xs + _ -> mempty + return $ addr <> bs -- sig added by \closing + +figure :: PandocMonad m => LP m Blocks +figure = try $ do + resetCaption + blocks >>= addImageCaption + +addImageCaption :: PandocMonad m => Blocks -> LP m Blocks +addImageCaption = walkM go + where go (Image attr alt (src,tit)) + | not ("fig:" `isPrefixOf` tit) = do + mbcapt <- sCaption <$> getState + return $ case mbcapt of + Just ils -> Image attr (toList ils) (src, "fig:" ++ tit) + Nothing -> Image attr alt (src,tit) + go x = return x -maybeAddExtension :: String -> FilePath -> FilePath -maybeAddExtension ext fp = - if null (takeExtension fp) - then addExtension fp ext - else fp +graphicsPath :: PandocMonad m => LP m Blocks +graphicsPath = do + ps <- map toksToString <$> (bgroup *> manyTill braced egroup) + getResourcePath >>= setResourcePath . (++ ps) + return mempty -include :: PandocMonad m => LP m Blocks -include = do - fs' <- try $ do - char '\\' - name <- try (string "include") - <|> try (string "input") - <|> try (string "subfile") - <|> string "usepackage" - -- skip options - skipMany $ try $ char '[' *> manyTill anyChar (char ']') - fs <- (map trim . splitBy (==',')) <$> braced - return $ if name == "usepackage" - then map (maybeAddExtension ".sty") fs - else map (maybeAddExtension ".tex") fs - dirs <- (splitBy (==':') . fromMaybe ".") <$> lookupEnv "TEXINPUTS" - mconcat <$> mapM (insertIncludedFile blocks dirs) fs' +splitBibs :: String -> [Inlines] +splitBibs = map (str . flip replaceExtension "bib" . trim) . splitBy (==',') + +alltt :: Blocks -> Blocks +alltt = walk strToCode + where strToCode (Str s) = Code nullAttr s + strToCode Space = RawInline (Format "latex") "\\ " + strToCode SoftBreak = LineBreak + strToCode x = x + +parseListingsOptions :: [(String, String)] -> Attr +parseListingsOptions options = + let kvs = [ (if k == "firstnumber" + then "startFrom" + else k, v) | (k,v) <- options ] + classes = [ "numberLines" | + lookup "numbers" options == Just "left" ] + ++ maybeToList (lookup "language" options + >>= fromListingsLanguage) + in (fromMaybe "" (lookup "label" options), classes, kvs) inputListing :: PandocMonad m => LP m Blocks inputListing = do pos <- getPosition options <- option [] keyvals - f <- filter (/='"') <$> braced + f <- filter (/='"') . toksToString <$> braced dirs <- (splitBy (==':') . fromMaybe ".") <$> lookupEnv "TEXINPUTS" mbCode <- readFileFromDirs dirs f codeLines <- case mbCode of @@ -1098,169 +1914,10 @@ inputListing = do drop (firstline - 1) codeLines return $ codeBlockWith (ident,ordNub (classes ++ language),kvs) codeContents -parseListingsOptions :: [(String, String)] -> Attr -parseListingsOptions options = - let kvs = [ (if k == "firstnumber" - then "startFrom" - else k, v) | (k,v) <- options ] - classes = [ "numberLines" | - lookup "numbers" options == Just "left" ] - ++ maybeToList (lookup "language" options - >>= fromListingsLanguage) - in (fromMaybe "" (lookup "label" options), classes, kvs) - ----- - -keyval :: PandocMonad m => LP m (String, String) -keyval = try $ do - key <- many1 alphaNum - val <- option "" $ char '=' >> braced <|> (many1 (alphaNum <|> oneOf ".:-|\\")) - skipMany spaceChar - optional (char ',') - skipMany spaceChar - return (key, val) - - -keyvals :: PandocMonad m => LP m [(String, String)] -keyvals = try $ char '[' *> manyTill keyval (char ']') - -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 :: PandocMonad m => LP m String -rawLaTeXBlock = snd <$> try (withRaw (environment <|> blockCommand)) - -rawLaTeXInline :: PandocMonad m => LP m Inline -rawLaTeXInline = do - raw <- (snd <$> withRaw inlineCommand) - <|> (snd <$> withRaw inlineEnvironment) - <|> (snd <$> withRaw blockCommand) - RawInline "latex" <$> applyMacros' raw - -addImageCaption :: PandocMonad m => Blocks -> LP m Blocks -addImageCaption = walkM go - where go (Image attr alt (src,tit)) - | not ("fig:" `isPrefixOf` tit) = do - mbcapt <- stateCaption <$> getState - return $ case mbcapt of - Just ils -> Image attr (toList ils) (src, "fig:" ++ tit) - Nothing -> Image attr alt (src,tit) - go x = return x - -addTableCaption :: PandocMonad m => Blocks -> LP m Blocks -addTableCaption = walkM go - where go (Table c als ws hs rs) = do - mbcapt <- stateCaption <$> getState - return $ case mbcapt of - Just ils -> Table (toList ils) als ws hs rs - Nothing -> Table c als ws hs rs - go x = return x - -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")) - , ("letter", env "letter" letterContents) - , ("minipage", env "minipage" $ - skipopts *> spaces' *> optional braced *> spaces' *> blocks) - , ("figure", env "figure" $ skipopts *> figure) - , ("subfigure", env "subfigure" $ skipopts *> tok *> figure) - , ("center", env "center" blocks) - , ("longtable", env "longtable" $ - resetCaption *> simpTable "longtable" False >>= addTableCaption) - , ("table", env "table" $ - resetCaption *> skipopts *> blocks >>= addTableCaption) - , ("tabular*", env "tabular" $ simpTable "tabular*" True) - , ("tabularx", env "tabularx" $ simpTable "tabularx" True) - , ("tabular", env "tabular" $ simpTable "tabular" False) - , ("quote", blockQuote <$> env "quote" blocks) - , ("quotation", blockQuote <$> env "quotation" blocks) - , ("verse", blockQuote <$> env "verse" blocks) - , ("itemize", bulletList <$> listenv "itemize" (many item)) - , ("description", definitionList <$> listenv "description" (many descItem)) - , ("enumerate", orderedList') - , ("alltt", alltt =<< verbEnv "alltt") - , ("code", guardEnabled Ext_literate_haskell *> - (codeBlockWith ("",["sourceCode","literate","haskell"],[]) <$> - verbEnv "code")) - , ("comment", mempty <$ verbEnv "comment") - , ("verbatim", codeBlock <$> verbEnv "verbatim") - , ("Verbatim", fancyverbEnv "Verbatim") - , ("BVerbatim", fancyverbEnv "BVerbatim") - , ("lstlisting", do attr <- parseListingsOptions <$> option [] keyvals - codeBlockWith attr <$> verbEnv "lstlisting") - , ("minted", do options <- option [] keyvals - lang <- grouped (many1 $ satisfy (/='}')) - let kvs = [ (if k == "firstnumber" - then "startFrom" - else k, v) | (k,v) <- options ] - let classes = [ lang | not (null lang) ] ++ - [ "numberLines" | - lookup "linenos" options == Just "true" ] - let attr = ("",classes,kvs) - codeBlockWith attr <$> verbEnv "minted") - , ("obeylines", parseFromString - (para . trimInlines . mconcat <$> many inline) =<< - intercalate "\\\\\n" . lines <$> verbEnv "obeylines") - , ("displaymath", mathEnvWith para Nothing "displaymath") - , ("equation", mathEnvWith para Nothing "equation") - , ("equation*", mathEnvWith para Nothing "equation*") - , ("gather", mathEnvWith para (Just "gathered") "gather") - , ("gather*", mathEnvWith para (Just "gathered") "gather*") - , ("multline", mathEnvWith para (Just "gathered") "multline") - , ("multline*", mathEnvWith para (Just "gathered") "multline*") - , ("eqnarray", mathEnvWith para (Just "aligned") "eqnarray") - , ("eqnarray*", mathEnvWith para (Just "aligned") "eqnarray*") - , ("align", mathEnvWith para (Just "aligned") "align") - , ("align*", mathEnvWith para (Just "aligned") "align*") - , ("alignat", mathEnvWith para (Just "aligned") "alignat") - , ("alignat*", mathEnvWith para (Just "aligned") "alignat*") - , ("tikzpicture", rawVerbEnv "tikzpicture") - ] - -figure :: PandocMonad m => LP m Blocks -figure = try $ do - resetCaption - blocks >>= addImageCaption - -letterContents :: PandocMonad m => LP m Blocks -letterContents = do - bs <- blocks - st <- getState - -- add signature (author) and address (title) - let addr = case lookupMeta "address" (stateMeta st) of - Just (MetaBlocks [Plain xs]) -> - para $ trimInlines $ fromList xs - _ -> mempty - return $ addr <> bs -- sig added by \closing - -closing :: PandocMonad m => LP m Blocks -closing = do - contents <- tok - st <- getState - let extractInlines (MetaBlocks [Plain ys]) = ys - extractInlines (MetaBlocks [Para ys ]) = ys - extractInlines _ = [] - let sigs = case lookupMeta "author" (stateMeta st) of - Just (MetaList xs) -> - para $ trimInlines $ fromList $ - intercalate [LineBreak] $ map extractInlines xs - _ -> mempty - return $ para (trimInlines contents) <> sigs +-- lists item :: PandocMonad m => LP m Blocks -item = blocks *> controlSeq "item" *> skipopts *> blocks - -looseItem :: PandocMonad m => LP m Blocks -looseItem = do - ctx <- stateParserContext `fmap` getState - if ctx == ListItemState - then mzero - else return mempty +item = void blocks *> controlSeq "item" *> skipopts *> blocks descItem :: PandocMonad m => LP m (Inlines, [Blocks]) descItem = do @@ -1271,302 +1928,210 @@ descItem = do bs <- blocks return (ils, [bs]) -env :: PandocMonad m => String -> LP m a -> LP m a -env name p = p <* - (try (controlSeq "end" *> braced >>= guard . (== name)) - <?> ("\\end{" ++ name ++ "}")) - -listenv :: PandocMonad m => String -> LP m a -> LP m a +listenv :: PandocMonad m => Text -> LP m a -> LP m a listenv name p = try $ do - oldCtx <- stateParserContext `fmap` getState - updateState $ \st -> st{ stateParserContext = ListItemState } + oldInListItem <- sInListItem `fmap` getState + updateState $ \st -> st{ sInListItem = True } res <- env name p - updateState $ \st -> st{ stateParserContext = oldCtx } + updateState $ \st -> st{ sInListItem = oldInListItem } return res -mathEnvWith :: PandocMonad m - => (Inlines -> a) -> Maybe String -> String -> LP m a -mathEnvWith f innerEnv name = f <$> mathDisplay (inner <$> mathEnv name) - where inner x = case innerEnv of - Nothing -> x - Just y -> "\\begin{" ++ y ++ "}\n" ++ x ++ - "\\end{" ++ y ++ "}" - -mathEnv :: PandocMonad m => String -> LP m String -mathEnv name = do - skipopts - optional blankline - let endEnv = try $ controlSeq "end" *> braced >>= guard . (== name) - charMuncher = skipMany comment *> - (many1 (noneOf "\\%") <|> try (string "\\%") - <|> try (string "\\\\") <|> count 1 anyChar) - res <- concat <$> manyTill charMuncher endEnv - return $ stripTrailingNewlines res - -verbEnv :: PandocMonad m => String -> LP m String -verbEnv name = do - skipopts - optional blankline - let endEnv = try $ controlSeq "end" *> braced >>= guard . (== name) - charMuncher = anyChar - res <- manyTill charMuncher endEnv - return $ stripTrailingNewlines res - -fancyverbEnv :: PandocMonad m => String -> LP m Blocks -fancyverbEnv name = do - options <- option [] keyvals - let kvs = [ (if k == "firstnumber" - then "startFrom" - else k, v) | (k,v) <- options ] - let classes = [ "numberLines" | - lookup "numbers" options == Just "left" ] - let attr = ("",classes,kvs) - codeBlockWith attr <$> verbEnv name - orderedList' :: PandocMonad m => LP m Blocks orderedList' = try $ do - optional sp - (_, style, delim) <- option (1, DefaultStyle, DefaultDelim) $ - try $ char '[' *> anyOrderedListMarker <* char ']' spaces - optional $ try $ controlSeq "setlength" *> grouped (controlSeq "itemindent") *> braced + let markerSpec = do + symbol '[' + ts <- toksToString <$> manyTill anyTok (symbol ']') + case runParser anyOrderedListMarker def "option" ts of + Right r -> return r + Left _ -> do + pos <- getPosition + report $ SkippedContent ("[" ++ ts ++ "]") pos + return (1, DefaultStyle, DefaultDelim) + (_, style, delim) <- option (1, DefaultStyle, DefaultDelim) markerSpec spaces - start <- option 1 $ try $ do controlSeq "setcounter" - grouped (string "enum" *> many1 (oneOf "iv")) + optional $ try $ controlSeq "setlength" + *> grouped (count 1 $ controlSeq "itemindent") + *> braced + spaces + start <- option 1 $ try $ do pos <- getPosition + controlSeq "setcounter" + ctr <- toksToString <$> braced + guard $ "enum" `isPrefixOf` ctr + guard $ all (`elem` ['i','v']) (drop 4 ctr) optional sp - num <- grouped (many1 digit) - spaces - return (read num + 1 :: Int) + num <- toksToString <$> braced + case safeRead num of + Just i -> return (i + 1 :: Int) + Nothing -> do + report $ SkippedContent + ("\\setcounter{" ++ ctr ++ + "}{" ++ num ++ "}") pos + return 1 bs <- listenv "enumerate" (many item) return $ orderedListWith (start, style, delim) bs -paragraph :: PandocMonad m => LP m Blocks -paragraph = do - x <- trimInlines . mconcat <$> many1 inline - if x == mempty - then return mempty - else return $ para x - -preamble :: PandocMonad m => LP m Blocks -preamble = mempty <$> manyTill preambleBlock beginDoc - where beginDoc = lookAhead $ try $ controlSeq "begin" *> string "{document}" - preambleBlock = void comment - <|> void sp - <|> void blanklines - <|> void include - <|> void macro - <|> void blockCommand - <|> void anyControlSeq - <|> void braced - <|> void anyChar - -------- - --- citations - -addPrefix :: [Inline] -> [Citation] -> [Citation] -addPrefix p (k:ks) = k {citationPrefix = p ++ citationPrefix k} : ks -addPrefix _ _ = [] - -addSuffix :: [Inline] -> [Citation] -> [Citation] -addSuffix s ks@(_:_) = - let k = last ks - in init ks ++ [k {citationSuffix = citationSuffix k ++ s}] -addSuffix _ _ = [] - -simpleCiteArgs :: PandocMonad m => LP m [Citation] -simpleCiteArgs = try $ do - first <- optionMaybe $ toList <$> opt - second <- optionMaybe $ toList <$> opt - keys <- try $ bgroup *> (manyTill citationLabel egroup) - let (pre, suf) = case (first , second ) of - (Just s , Nothing) -> (mempty, s ) - (Just s , Just t ) -> (s , t ) - _ -> (mempty, mempty) - conv k = Citation { citationId = k - , citationPrefix = [] - , citationSuffix = [] - , citationMode = NormalCitation - , citationHash = 0 - , citationNoteNum = 0 - } - return $ addPrefix pre $ addSuffix suf $ map conv keys - -citationLabel :: PandocMonad m => LP m String -citationLabel = optional sp *> - (many1 (satisfy isBibtexKeyChar) - <* optional sp - <* optional (char ',') - <* optional sp) - where isBibtexKeyChar c = isAlphaNum c || c `elem` (".:;?!`'()/*@_+=-[]" :: String) +-- tables -cites :: PandocMonad m => CitationMode -> Bool -> LP m [Citation] -cites mode multi = try $ do - cits <- if multi - then many1 simpleCiteArgs - else count 1 simpleCiteArgs - let cs = concat cits - return $ case mode of - AuthorInText -> case cs of - (c:rest) -> c {citationMode = mode} : rest - [] -> [] - _ -> map (\a -> a {citationMode = mode}) cs +hline :: PandocMonad m => LP m () +hline = try $ do + spaces + controlSeq "hline" <|> + -- booktabs rules: + controlSeq "toprule" <|> + controlSeq "bottomrule" <|> + controlSeq "midrule" <|> + controlSeq "endhead" <|> + controlSeq "endfirsthead" + spaces + optional $ bracketed inline + return () -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) +lbreak :: PandocMonad m => LP m Tok +lbreak = (controlSeq "\\" <|> controlSeq "tabularnewline") <* spaces -complexNatbibCitation :: PandocMonad m => CitationMode -> LP m Inlines -complexNatbibCitation mode = try $ do - let ils = (toList . trimInlines . mconcat) <$> - many (notFollowedBy (oneOf "\\};") >> inline) - let parseOne = try $ do - skipSpaces - pref <- ils - cit' <- inline -- expect a citation - let citlist = toList cit' - cits' <- case citlist of - [Cite cs _] -> return cs - _ -> mzero - suff <- ils - skipSpaces - optional $ char ';' - return $ addPrefix pref $ addSuffix suff cits' - (c:cits, raw) <- withRaw $ grouped parseOne - return $ cite (c{ citationMode = mode }:cits) - (rawInline "latex" $ "\\citetext" ++ raw) +amp :: PandocMonad m => LP m Tok +amp = symbol '&' --- tables +-- Split a Word into individual Symbols (for parseAligns) +splitWordTok :: PandocMonad m => LP m () +splitWordTok = do + inp <- getInput + case inp of + (Tok spos Word t : rest) -> do + setInput $ map (Tok spos Symbol . T.singleton) (T.unpack t) ++ rest + _ -> return () -parseAligns :: PandocMonad m => LP m [(Alignment, Double, (String, String))] +parseAligns :: PandocMonad m => LP m [(Alignment, Double, ([Tok], [Tok]))] parseAligns = try $ do - bgroup - let maybeBar = skipMany $ sp <|> () <$ char '|' <|> () <$ (char '@' >> braced) - maybeBar - let cAlign = AlignCenter <$ char 'c' - let lAlign = AlignLeft <$ char 'l' - let rAlign = AlignRight <$ char 'r' - let parAlign = AlignLeft <$ char 'p' - -- algins from tabularx - let xAlign = AlignLeft <$ char 'X' - let mAlign = AlignLeft <$ char 'm' - let bAlign = AlignLeft <$ char 'b' - let alignChar = cAlign <|> lAlign <|> rAlign <|> parAlign - <|> xAlign <|> mAlign <|> bAlign - let alignPrefix = char '>' >> braced - let alignSuffix = char '<' >> braced + let maybeBar = skipMany $ + sp <|> () <$ symbol '|' <|> () <$ (symbol '@' >> braced) + let cAlign = AlignCenter <$ symbol 'c' + let lAlign = AlignLeft <$ symbol 'l' + let rAlign = AlignRight <$ symbol 'r' + let parAlign = AlignLeft <$ symbol 'p' + -- aligns from tabularx + let xAlign = AlignLeft <$ symbol 'X' + let mAlign = AlignLeft <$ symbol 'm' + let bAlign = AlignLeft <$ symbol 'b' + let alignChar = splitWordTok *> ( cAlign <|> lAlign <|> rAlign <|> parAlign + <|> xAlign <|> mAlign <|> bAlign ) + let alignPrefix = symbol '>' >> braced + let alignSuffix = symbol '<' >> braced let colWidth = try $ do - char '{' - ds <- many1 (oneOf "0123456789.") + symbol '{' + ds <- trim . toksToString <$> manyTill anyTok (controlSeq "linewidth") spaces - string "\\linewidth" - char '}' + symbol '}' case safeRead ds of Just w -> return w Nothing -> return 0.0 - let alignSpec = do + let alignSpec = try $ do spaces - pref <- option "" alignPrefix + pref <- option [] alignPrefix spaces al <- alignChar - width <- colWidth <|> option 0.0 (do s <- braced + width <- colWidth <|> option 0.0 (do s <- toksToString <$> braced pos <- getPosition report $ SkippedContent s pos return 0.0) spaces - suff <- option "" alignSuffix + suff <- option [] alignSuffix return (al, width, (pref, suff)) - aligns' <- sepEndBy alignSpec maybeBar + bgroup + spaces + maybeBar + aligns' <- many (alignSpec <* maybeBar) spaces egroup spaces - return $ aligns' - -hline :: PandocMonad m => LP m () -hline = try $ do - spaces' - controlSeq "hline" <|> - -- booktabs rules: - controlSeq "toprule" <|> - controlSeq "bottomrule" <|> - controlSeq "midrule" <|> - controlSeq "endhead" <|> - controlSeq "endfirsthead" - spaces' - optional $ bracketed (many1 (satisfy (/=']'))) - return () - -lbreak :: PandocMonad m => LP m () -lbreak = () <$ try (spaces' *> - (controlSeq "\\" <|> controlSeq "tabularnewline") <* - spaces') - -amp :: PandocMonad m => LP m () -amp = () <$ try (spaces' *> char '&' <* spaces') + return aligns' parseTableRow :: PandocMonad m - => String -- ^ table environment name - -> [(String, String)] -- ^ pref/suffixes + => Text -- ^ table environment name + -> [([Tok], [Tok])] -- ^ pref/suffixes -> LP m [Blocks] -parseTableRow envname prefsufs = try $ do +parseTableRow envname prefsufs = do + notFollowedBy (spaces *> end_ envname) let cols = length prefsufs - let tableCellRaw = concat <$> many - (do notFollowedBy amp - notFollowedBy lbreak - notFollowedBy $ () <$ try (string ("\\end{" ++ envname ++ "}")) - many1 (noneOf "&%\n\r\\") - <|> try (string "\\&") - <|> count 1 anyChar) - let plainify bs = case toList bs of - [Para ils] -> plain (fromList ils) - _ -> bs - rawcells <- sepBy1 tableCellRaw amp - guard $ length rawcells == cols - let rawcells' = zipWith (\c (p, s) -> p ++ trim c ++ s) rawcells prefsufs - let tableCell = plainify <$> blocks - cells' <- mapM (parseFromString' tableCell) rawcells' - let numcells = length cells' + -- add prefixes and suffixes in token stream: + let celltoks (pref, suff) = do + prefpos <- getPosition + contents <- many (notFollowedBy + (() <$ amp <|> () <$ lbreak <|> end_ envname) + >> anyTok) + suffpos <- getPosition + option [] (count 1 amp) + return $ map (setpos (sourceLine prefpos, sourceColumn prefpos)) pref + ++ contents ++ + map (setpos (sourceLine suffpos, sourceColumn suffpos)) suff + rawcells <- sequence (map celltoks prefsufs) + oldInput <- getInput + cells <- sequence $ map (\ts -> setInput ts >> parseTableCell) rawcells + setInput oldInput + spaces + let numcells = length cells guard $ numcells <= cols && numcells >= 1 - guard $ cells' /= [mempty] + guard $ cells /= [mempty] -- note: a & b in a three-column table leaves an empty 3rd cell: - let cells'' = cells' ++ replicate (cols - numcells) mempty - spaces' - return cells'' + return $ cells ++ replicate (cols - numcells) mempty -spaces' :: PandocMonad m => LP m () -spaces' = spaces *> skipMany (comment *> spaces) +parseTableCell :: PandocMonad m => LP m Blocks +parseTableCell = do + let plainify bs = case toList bs of + [Para ils] -> plain (fromList ils) + _ -> bs + updateState $ \st -> st{ sInTableCell = True } + cells <- plainify <$> blocks + updateState $ \st -> st{ sInTableCell = False } + return cells -simpTable :: PandocMonad m => String -> Bool -> LP m Blocks +simpTable :: PandocMonad m => Text -> Bool -> LP m Blocks simpTable envname hasWidthParameter = try $ do - when hasWidthParameter $ () <$ (spaces' >> tok) + when hasWidthParameter $ () <$ (spaces >> tok) skipopts colspecs <- parseAligns let (aligns, widths, prefsufs) = unzip3 colspecs let cols = length colspecs optional $ controlSeq "caption" *> skipopts *> setCaption optional lbreak - spaces' + spaces skipMany hline - spaces' + spaces header' <- option [] $ try (parseTableRow envname prefsufs <* lbreak <* many1 hline) - spaces' + spaces rows <- sepEndBy (parseTableRow envname prefsufs) (lbreak <* optional (skipMany hline)) - spaces' + spaces optional $ controlSeq "caption" *> skipopts *> setCaption optional lbreak - spaces' + spaces let header'' = if null header' then replicate cols mempty else header' lookAhead $ controlSeq "end" -- make sure we're at end return $ table mempty (zip aligns widths) header'' rows -removeDoubleQuotes :: String -> String -removeDoubleQuotes ('"':xs) = - case reverse xs of - '"':ys -> reverse ys - _ -> '"':xs -removeDoubleQuotes xs = xs +addTableCaption :: PandocMonad m => Blocks -> LP m Blocks +addTableCaption = walkM go + where go (Table c als ws hs rs) = do + mbcapt <- sCaption <$> getState + return $ case mbcapt of + Just ils -> Table (toList ils) als ws hs rs + Nothing -> Table c als ws hs rs + go x = return x + + +block :: PandocMonad m => LP m Blocks +block = (mempty <$ spaces1) + <|> environment + <|> include + <|> macroDef + <|> paragraph + <|> blockCommand + <|> grouped block + +blocks :: PandocMonad m => LP m Blocks +blocks = mconcat <$> many block + diff --git a/src/Text/Pandoc/Readers/LaTeX/Types.hs b/src/Text/Pandoc/Readers/LaTeX/Types.hs new file mode 100644 index 000000000..6f84ae1f1 --- /dev/null +++ b/src/Text/Pandoc/Readers/LaTeX/Types.hs @@ -0,0 +1,48 @@ +{- +Copyright (C) 2017 John MacFarlane <jgm@berkeley.edu> + +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.LaTeX.Types + Copyright : Copyright (C) 2017 John MacFarlane + License : GNU GPL, version 2 or above + + Maintainer : John MacFarlane <jgm@berkeley.edu> + Stability : alpha + Portability : portable + +Types for LaTeX tokens and macros. +-} +module Text.Pandoc.Readers.LaTeX.Types ( Tok(..) + , TokType(..) + , Macro(..) + , Line + , Column ) +where +import Data.Text (Text) +import Text.Parsec.Pos (Line, Column) + +data TokType = CtrlSeq Text | Spaces | Newline | Symbol | Word | Comment | + Esc1 | Esc2 | Arg Int + deriving (Eq, Ord, Show) + +data Tok = Tok (Line, Column) TokType Text + deriving (Eq, Ord, Show) + +data Macro = Macro Int (Maybe [Tok]) [Tok] + deriving Show + diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index c2342b9f3..ab6a32b78 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -61,7 +61,8 @@ import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (tableWith) import Text.Pandoc.Readers.HTML (htmlInBalanced, htmlTag, isBlockTag, isCommentTag, isInlineTag, isTextTag) -import Text.Pandoc.Readers.LaTeX (rawLaTeXBlock, rawLaTeXInline) +import Text.Pandoc.Readers.LaTeX (rawLaTeXBlock, rawLaTeXInline, applyMacros, + macro) import Text.Pandoc.Shared import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.XML (fromEntities) @@ -1105,10 +1106,11 @@ latexMacro = try $ do rawTeXBlock :: PandocMonad m => MarkdownParser m (F Blocks) rawTeXBlock = do guardEnabled Ext_raw_tex - result <- (B.rawBlock "latex" . concat <$> - rawLaTeXBlock `sepEndBy1` blankline) - <|> (B.rawBlock "context" . concat <$> + result <- (B.rawBlock "context" . concat <$> rawConTeXtEnvironment `sepEndBy1` blankline) + <|> (B.rawBlock "latex" . concat <$> + rawLaTeXBlock `sepEndBy1` blankline) + spaces return $ return result @@ -1553,8 +1555,8 @@ code = try $ do Right attr -> B.codeWith attr result math :: PandocMonad m => MarkdownParser m (F Inlines) -math = (return . B.displayMath <$> (mathDisplay >>= applyMacros')) - <|> (return . B.math <$> (mathInline >>= applyMacros')) <+?> +math = (return . B.displayMath <$> (mathDisplay >>= applyMacros)) + <|> (return . B.math <$> (mathInline >>= applyMacros)) <+?> (guardEnabled Ext_smart *> (return <$> apostrophe) <* notFollowedBy (space <|> satisfy isPunctuation)) @@ -1878,9 +1880,8 @@ rawLaTeXInline' = try $ do guardEnabled Ext_raw_tex lookAhead (char '\\') notFollowedBy' rawConTeXtEnvironment - RawInline _ s <- rawLaTeXInline - return $ return $ B.rawInline "tex" s - -- "tex" because it might be context or latex + s <- rawLaTeXInline + return $ return $ B.rawInline "tex" s -- "tex" because it might be context rawConTeXtEnvironment :: PandocMonad m => ParserT [Char] st m String rawConTeXtEnvironment = try $ do diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index a51306347..1ae73c148 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -58,7 +58,7 @@ import Text.Pandoc.Definition import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Shared (crFilter) -import Text.Pandoc.Parsing hiding (macro, nested) +import Text.Pandoc.Parsing hiding (nested) import Text.Pandoc.Readers.HTML (htmlTag) import Text.Pandoc.XML (fromEntities) import System.FilePath (takeExtension) diff --git a/src/Text/Pandoc/Readers/Org/Inlines.hs b/src/Text/Pandoc/Readers/Org/Inlines.hs index 66273e05d..42fdfd4dd 100644 --- a/src/Text/Pandoc/Readers/Org/Inlines.hs +++ b/src/Text/Pandoc/Readers/Org/Inlines.hs @@ -826,9 +826,10 @@ maybeRight = either (const Nothing) Just inlineLaTeXCommand :: PandocMonad m => OrgParser m String inlineLaTeXCommand = try $ do rest <- getInput - parsed <- (lift . lift) $ runParserT rawLaTeXInline def "source" rest + st <- getState + parsed <- (lift . lift) $ runParserT rawLaTeXInline st "source" rest case parsed of - Right (RawInline _ cs) -> do + Right cs -> do -- drop any trailing whitespace, those are not be part of the command as -- far as org mode is concerned. let cmdNoSpc = dropWhileEnd isSpace cs diff --git a/src/Text/Pandoc/Readers/Org/ParserState.hs b/src/Text/Pandoc/Readers/Org/ParserState.hs index 92f868516..fc98213fb 100644 --- a/src/Text/Pandoc/Readers/Org/ParserState.hs +++ b/src/Text/Pandoc/Readers/Org/ParserState.hs @@ -33,6 +33,7 @@ module Text.Pandoc.Readers.Org.ParserState , OrgNoteRecord , HasReaderOptions (..) , HasQuoteContext (..) + , HasMacros (..) , TodoMarker (..) , TodoSequence , TodoState (..) @@ -57,14 +58,17 @@ import Control.Monad.Reader (ReaderT, asks, local) import Data.Default (Default (..)) import qualified Data.Map as M import qualified Data.Set as Set +import Data.Text (Text) import Text.Pandoc.Builder (Blocks, Inlines) import Text.Pandoc.Definition (Meta (..), nullMeta) import Text.Pandoc.Logging import Text.Pandoc.Options (ReaderOptions (..)) +import Text.Pandoc.Readers.LaTeX.Types (Macro) import Text.Pandoc.Parsing (Future, HasHeaderMap (..), HasIdentifierList (..), HasIncludeFiles (..), HasLastStrPosition (..), HasLogMessages (..), HasQuoteContext (..), + HasMacros (..), HasReaderOptions (..), ParserContext (..), QuoteContext (..), SourcePos, askF, asksF, returnF, runF, trimInlinesF) @@ -118,6 +122,7 @@ data OrgParserState = OrgParserState , orgStateParserContext :: ParserContext , orgStateTodoSequences :: [TodoSequence] , orgLogMessages :: [LogMessage] + , orgMacros :: M.Map Text Macro } data OrgParserLocal = OrgParserLocal { orgLocalQuoteContext :: QuoteContext } @@ -148,6 +153,10 @@ instance HasLogMessages OrgParserState where addLogMessage msg st = st{ orgLogMessages = msg : orgLogMessages st } getLogMessages st = reverse $ orgLogMessages st +instance HasMacros OrgParserState where + extractMacros st = orgMacros st + updateMacros f st = st{ orgMacros = f (orgMacros st) } + instance HasIncludeFiles OrgParserState where getIncludeFiles = orgStateIncludeFiles addIncludeFile f st = st { orgStateIncludeFiles = f : orgStateIncludeFiles st } @@ -178,6 +187,7 @@ defaultOrgParserState = OrgParserState , orgStateParserContext = NullState , orgStateTodoSequences = [] , orgLogMessages = [] + , orgMacros = M.empty } optionsToParserState :: ReaderOptions -> OrgParserState diff --git a/src/Text/Pandoc/Readers/TWiki.hs b/src/Text/Pandoc/Readers/TWiki.hs index 210d3e5aa..d41152de5 100644 --- a/src/Text/Pandoc/Readers/TWiki.hs +++ b/src/Text/Pandoc/Readers/TWiki.hs @@ -45,7 +45,7 @@ import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad(..)) import Text.Pandoc.Definition import Text.Pandoc.Options -import Text.Pandoc.Parsing hiding (enclosed, macro, nested) +import Text.Pandoc.Parsing hiding (enclosed, nested) import Text.Pandoc.Readers.HTML (htmlTag, isCommentTag) import Text.Pandoc.XML (fromEntities) import Text.Pandoc.Shared (crFilter) diff --git a/src/Text/Pandoc/Readers/Textile.hs b/src/Text/Pandoc/Readers/Textile.hs index a80d75340..853d2768f 100644 --- a/src/Text/Pandoc/Readers/Textile.hs +++ b/src/Text/Pandoc/Readers/Textile.hs @@ -573,7 +573,7 @@ rawHtmlInline = B.rawInline "html" . snd <$> htmlTag isInlineTag rawLaTeXInline' :: PandocMonad m => ParserT [Char] ParserState m Inlines rawLaTeXInline' = try $ do guardEnabled Ext_raw_tex - B.singleton <$> rawLaTeXInline + B.rawInline "latex" <$> rawLaTeXInline -- | Textile standard link syntax is "label":target. But we -- can also have ["label":target]. diff --git a/src/Text/Pandoc/Readers/Txt2Tags.hs b/src/Text/Pandoc/Readers/Txt2Tags.hs index 5708358f6..f000646c2 100644 --- a/src/Text/Pandoc/Readers/Txt2Tags.hs +++ b/src/Text/Pandoc/Readers/Txt2Tags.hs @@ -40,7 +40,7 @@ import Text.Pandoc.Builder (Blocks, Inlines, trimInlines) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Definition import Text.Pandoc.Options -import Text.Pandoc.Parsing hiding (macro, space, spaces, uri) +import Text.Pandoc.Parsing hiding (space, spaces, uri) import Text.Pandoc.Shared (compactify, compactifyDL, escapeURI, crFilter) import Control.Monad (guard, void, when) import Control.Monad.Reader (Reader, asks, runReader) -- cgit v1.2.3 From 6f6e83a06e9793d26cb622024098af39c14cb60a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 7 Jul 2017 11:41:28 +0200 Subject: Parsing: added takeP, takeWhileP for efficient parsing of [Char]. --- src/Text/Pandoc/Parsing.hs | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index f6263c782..549042d14 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -35,7 +35,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA A utility library with parsers used in pandoc readers. -} -module Text.Pandoc.Parsing ( anyLine, +module Text.Pandoc.Parsing ( takeWhileP, + takeP, + anyLine, anyLineNewline, indentWith, many1Till, @@ -191,7 +193,7 @@ import qualified Text.Pandoc.Builder as B import Text.Pandoc.XML (fromEntities) import qualified Text.Pandoc.UTF8 as UTF8 (putStrLn) import Text.Parsec hiding (token) -import Text.Parsec.Pos (newPos, initialPos) +import Text.Parsec.Pos (newPos, initialPos, updatePosString) import Data.Char ( toLower, toUpper, ord, chr, isAscii, isAlphaNum, isHexDigit, isSpace, isPunctuation ) import Data.List ( intercalate, transpose, isSuffixOf ) @@ -244,6 +246,35 @@ instance Monoid a => Monoid (Future s a) where mappend = liftM2 mappend mconcat = liftM mconcat . sequence +-- | Parse characters while a predicate is true. +takeWhileP :: Stream [Char] m Char + => (Char -> Bool) -> ParserT [Char] st m [Char] +takeWhileP f = do + -- faster than 'many (satisfy f)' + inp <- getInput + pos <- getPosition + let (xs, rest) = span f inp + -- needed to persuade parsec that this won't match an empty string: + anyChar + setInput rest + setPosition $ updatePosString pos xs + return xs + +-- Parse n characters of input (or the rest of the input if +-- there aren't n characters). +takeP :: Stream [Char] m Char => Int -> ParserT [Char] st m [Char] +takeP n = do + guard (n > 0) + -- faster than 'count n anyChar' + inp <- getInput + pos <- getPosition + let (xs, rest) = splitAt n inp + -- needed to persuade parsec that this won't match an empty string: + anyChar + setInput rest + setPosition $ updatePosString pos xs + return xs + -- | Parse any line of text anyLine :: Stream [Char] m Char => ParserT [Char] st m [Char] anyLine = do -- cgit v1.2.3 From 770e0cccc1d028415cc9e180b08b396fb0bc379b Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 7 Jul 2017 12:34:42 +0200 Subject: Use takeP in LaTeX reader. --- src/Text/Pandoc/Readers/LaTeX.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index d82e6a5dc..fde177f14 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -214,7 +214,7 @@ rawLaTeXBlock = do res <- runParserT rawblock lstate "source" toks case res of Left _ -> mzero - Right raw -> count (T.length (untokenize raw)) anyChar + Right raw -> takeP (T.length (untokenize raw)) macro :: (PandocMonad m, HasMacros s, HasReaderOptions s) => ParserT String s m Blocks @@ -236,7 +236,7 @@ macro = do Left _ -> mzero Right (raw, st) -> do updateState (updateMacros (const $ sMacros st)) - mempty <$ count (T.length (untokenize raw)) anyChar + mempty <$ takeP (T.length (untokenize raw)) applyMacros :: (PandocMonad m, HasMacros s, HasReaderOptions s) => String -> ParserT String s m String @@ -269,7 +269,7 @@ rawLaTeXInline = do Left _ -> mzero Right (raw, s) -> do updateState $ updateMacros (const $ sMacros s) - count (T.length (untokenize raw)) anyChar + takeP (T.length (untokenize raw)) inlineCommand :: PandocMonad m => ParserT String ParserState m Inlines inlineCommand = do @@ -288,7 +288,7 @@ inlineCommand = do Left _ -> mzero Right (il, raw, s) -> do updateState $ updateMacros (const $ sMacros s) - count (T.length (untokenize raw)) anyChar + takeP (T.length (untokenize raw)) return il tokenize :: Text -> [Tok] -- cgit v1.2.3 From 41209ea6765e9898d7e15c4c945c06275b6c0420 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 11 Jul 2017 15:52:38 +0200 Subject: HTML reader: Ensure that paragraphs are closed properly... when the parent block element closes, even without `</p>`. Closes #3794. --- src/Text/Pandoc/Readers/HTML.hs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index b07b65019..734973e33 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -805,6 +805,8 @@ pCloses tagtype = try $ do (TagClose "dl") | tagtype == "dd" -> return () (TagClose "table") | tagtype == "td" -> return () (TagClose "table") | tagtype == "tr" -> return () + (TagClose t') | tagtype == "p" && t' `Set.member` blockHtmlTags + -> return () -- see #3794 _ -> mzero pTagText :: PandocMonad m => TagParser m Inlines -- cgit v1.2.3 From 013fd1c6b68f2c061202d931f541aa4877ae543f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 12 Jul 2017 13:58:47 +0200 Subject: Make sure \write18 is parsed as raw LaTeX. The change is in the LaTeX reader's treatment of raw commands, but it also affects the Markdown reader. --- src/Text/Pandoc/Readers/LaTeX.hs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index fde177f14..cd2c7c7f8 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1049,14 +1049,12 @@ inlineCommand' :: PandocMonad m => LP m Inlines inlineCommand' = try $ do Tok _ (CtrlSeq name) cmd <- anyControlSeq guard $ name /= "begin" && name /= "end" - (star, rawstar) <- withRaw $ option "" ("*" <$ symbol '*' <* optional sp) + star <- option "" ("*" <$ symbol '*' <* optional sp) let name' = name <> star let names = ordNub [name', name] -- check non-starred as fallback let raw = do guard $ isInlineCommand name || not (isBlockCommand name) - (_, rawargs) <- withRaw - (skipangles *> skipopts *> option "" dimenarg *> many braced) - let rawcommand = T.unpack $ cmd <> untokenize (rawstar ++ rawargs) + rawcommand <- getRawCommand (cmd <> star) (guardEnabled Ext_raw_tex >> return (rawInline "latex" rawcommand)) <|> ignore rawcommand lookupListDefault raw names inlineCommands @@ -1353,7 +1351,11 @@ rawInlineOr name' fallback = do getRawCommand :: PandocMonad m => Text -> LP m String getRawCommand txt = do (_, rawargs) <- withRaw - (many (try (optional sp *> opt)) *> + ((if txt == "\\write" + then () <$ satisfyTok isWordTok -- digits + else return ()) *> + skipangles *> + skipopts *> option "" (try (optional sp *> dimenarg)) *> many braced) return $ T.unpack (txt <> untokenize rawargs) @@ -1631,7 +1633,7 @@ blockCommand = try $ do let names = ordNub [name', name] let raw = do guard $ isBlockCommand name || not (isInlineCommand name) - rawBlock "latex" <$> getRawCommand txt + rawBlock "latex" <$> getRawCommand (txt <> star) lookupListDefault raw names blockCommands closing :: PandocMonad m => LP m Blocks @@ -2128,8 +2130,8 @@ block = (mempty <$ spaces1) <|> environment <|> include <|> macroDef - <|> paragraph <|> blockCommand + <|> paragraph <|> grouped block blocks :: PandocMonad m => LP m Blocks -- cgit v1.2.3 From 050036c036bea4dba65efd033230d552ef637abc Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 12 Jul 2017 16:51:30 +0200 Subject: Print informative message when failing with use of `--normalize`. We may want to think of some kind of graceful fallback, but the present behavior has the advantage of forcing people to update scripts when updating to pandoc 2.0. See #3786. --- src/Text/Pandoc/App.hs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 689c0a784..68bdc1432 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -1564,6 +1564,8 @@ handleUnrecognizedOption :: String -> [String] -> [String] handleUnrecognizedOption "--smart" = (("--smart/-S has been removed. Use +smart or -smart extension instead.\n" ++ "For example: pandoc -f markdown+smart -t markdown-smart.") :) +handleUnrecognizedOption "--normalize" = + ("--normalize has been removed. Normalization is now automatic." :) handleUnrecognizedOption "-S" = handleUnrecognizedOption "--smart" handleUnrecognizedOption "--old-dashes" = ("--old-dashes has been removed. Use +old_dashes extension instead." :) -- cgit v1.2.3 From e0025cf4f18e335917f57814a1854f85ce1b6236 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Wed, 12 Jul 2017 18:14:10 +0300 Subject: Remove redundant imports (#3796) --- 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 cd2c7c7f8..9ec84b3f6 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -66,7 +66,7 @@ import Text.Pandoc.Parsing hiding (many, optional, withRaw, space, (<|>), spaces, blankline) import Text.Pandoc.Shared import Text.Pandoc.Readers.LaTeX.Types (Macro(..), Tok(..), - TokType(..), Line, Column) + TokType(..)) import Text.Pandoc.Walk import Text.Pandoc.Error (PandocError(PandocParsecError, PandocMacroLoop)) -- cgit v1.2.3 From de117fbd9e32e890663eb831b47fd91fcd6419a0 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Wed, 12 Jul 2017 18:16:02 +0300 Subject: Muse writer: indent lists inside <quote> with at least one space (#3795) --- src/Text/Pandoc/Writers/Muse.hs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index b386a85b9..0383d9d86 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -102,6 +102,13 @@ pandocToMuse (Pandoc meta blocks) = do Nothing -> return main Just tpl -> renderTemplate' tpl context +-- | Convert list of Pandoc block elements to Muse +-- | without setting stTopLevel. +flatBlockListToMuse :: PandocMonad m + => [Block] -- ^ List of block elements + -> StateT WriterState m Doc +flatBlockListToMuse blocks = cat <$> mapM blockToMuse blocks + -- | Convert list of Pandoc block elements to Muse. blockListToMuse :: PandocMonad m => [Block] -- ^ List of block elements @@ -111,11 +118,11 @@ blockListToMuse blocks = do modify $ \s -> s { stTopLevel = not $ stInsideBlock s , stInsideBlock = True } - contents <- mapM blockToMuse blocks + result <- flatBlockListToMuse blocks modify $ \s -> s { stTopLevel = stTopLevel oldState , stInsideBlock = stInsideBlock oldState } - return $ cat contents + return result -- | Convert Pandoc block element to Muse. blockToMuse :: PandocMonad m @@ -141,10 +148,10 @@ blockToMuse (RawBlock (Format format) str) = return $ blankline $$ "<literal style=\"" <> text format <> "\">" $$ text str $$ "</literal>" $$ blankline blockToMuse (BlockQuote blocks) = do - contents <- blockListToMuse blocks + contents <- flatBlockListToMuse blocks return $ blankline <> "<quote>" - $$ flush contents -- flush to drop blanklines + $$ nest 0 contents -- nest 0 to remove trailing blank lines $$ "</quote>" <> blankline blockToMuse (OrderedList (start, style, _) items) = do -- cgit v1.2.3 From 8b502dd50ff842bdbbf346a67a607d1a7905bda3 Mon Sep 17 00:00:00 2001 From: Yuchen Pei <ycpei@users.noreply.github.com> Date: Wed, 12 Jul 2017 11:19:49 -0400 Subject: Fixed #3760. (#3784) Using the same solution as in the LaTeX reader: equation -> displaymath align -> displaymath \begin{aligned} ... \end{aligned} etc.. --- src/Text/Pandoc/Readers/Vimwiki.hs | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Vimwiki.hs b/src/Text/Pandoc/Readers/Vimwiki.hs index 11faedb24..52bf37d35 100644 --- a/src/Text/Pandoc/Readers/Vimwiki.hs +++ b/src/Text/Pandoc/Readers/Vimwiki.hs @@ -75,7 +75,8 @@ import qualified Text.Pandoc.Builder as B (headerWith, str, space, strong, emph, strikeout, code, link, image, spanWith, para, horizontalRule, blockQuote, bulletList, plain, orderedList, simpleTable, softbreak, codeBlockWith, imageWith, divWith, - setMeta, definitionList, superscript, subscript) + setMeta, definitionList, superscript, subscript, displayMath, + math) import Text.Pandoc.Class (PandocMonad(..)) import Text.Pandoc.Definition (Pandoc(..), Inline(Space), Block(BulletList, OrderedList), Attr, nullMeta, Meta, ListNumberStyle(..), @@ -265,13 +266,32 @@ displayMath :: PandocMonad m => VwParser m Blocks displayMath = try $ do many spaceChar >> string "{{$" mathTag <- option "" mathTagParser + many space contents <- manyTill anyChar (try (char '\n' >> many spaceChar >> string "}}$" >> many spaceChar >> newline)) let contentsWithTags - | mathTag == "" = "\\[" ++ contents ++ "\n\\]" - | otherwise = "\\begin{" ++ mathTag ++ "}" ++ contents + | mathTag == "" = contents + | otherwise = "\\begin{" ++ mathTag ++ "}\n" ++ contents ++ "\n\\end{" ++ mathTag ++ "}" - return $ B.plain $ B.str contentsWithTags + return $ B.para $ B.displayMath contentsWithTags + + +mathTagLaTeX :: String -> String +mathTagLaTeX s = case s of + "equation" -> "" + "equation*" -> "" + "gather" -> "gathered" + "gather*" -> "gathered" + "multline" -> "gathered" + "multline*" -> "gathered" + "eqnarray" -> "aligned" + "eqnarray*" -> "aligned" + "align" -> "aligned" + "align*" -> "aligned" + "alignat" -> "aligned" + "alignat*" -> "aligned" + _ -> s + mixedList :: PandocMonad m => VwParser m Blocks mixedList = try $ do @@ -598,7 +618,7 @@ inlineMath :: PandocMonad m => VwParser m Inlines inlineMath = try $ do char '$' contents <- many1Till (noneOf "\n") (char '$') - return $ B.str $ "\\(" ++ contents ++ "\\)" + return $ B.math contents tag :: PandocMonad m => VwParser m Inlines tag = try $ do @@ -650,4 +670,4 @@ mathTagParser = do s <- try $ lookAhead (char '%' >> (manyTill (noneOf spaceChars) (try $ char '%' >> many (noneOf $ '%':spaceChars) >> space))) char '%' >> string s >> char '%' - return s + return $ mathTagLaTeX s -- cgit v1.2.3 From 911b63dfc371541700ea16708dc66725b02393f1 Mon Sep 17 00:00:00 2001 From: schrieveslaach <schrieveslaach@online.de> Date: Thu, 13 Jul 2017 20:56:59 +0200 Subject: Add LaTeX xspace support (#3797) --- src/Text/Pandoc/Readers/LaTeX.hs | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 9ec84b3f6..f5e387429 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -607,6 +607,16 @@ mkImage options src = do return $ imageWith attr (addExtension src defaultExt) "" alt _ -> return $ imageWith attr src "" alt +doxspace :: PandocMonad m => LP m Inlines +doxspace = do + (space <$ lookAhead (satisfyTok startsWithLetter)) <|> return mempty + where startsWithLetter (Tok _ Word t) = + case T.uncons t of + Just (c, _) | isLetter c -> True + _ -> False + startsWithLetter _ = False + + -- converts e.g. \SI{1}[\$]{} to "$ 1" or \SI{1}{\euro} to "1 €" dosiunitx :: PandocMonad m => LP m Inlines dosiunitx = do @@ -1336,6 +1346,8 @@ inlineCommands = M.fromList $ -- fontawesome , ("faCheck", lit "\10003") , ("faClose", lit "\10007") + -- xspace + , ("xspace", doxspace) ] ttfamily :: PandocMonad m => LP m Inlines -- cgit v1.2.3 From 3a36441b617b9dff632eda0ac14ca26072e0730b Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 13 Jul 2017 23:37:21 +0200 Subject: Use foldrWithKey instead of deprecated foldWithKey. --- src/Text/Pandoc/MediaBag.hs | 2 +- src/Text/Pandoc/Writers/Shared.hs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/MediaBag.hs b/src/Text/Pandoc/MediaBag.hs index d8d6da345..f89c60c9e 100644 --- a/src/Text/Pandoc/MediaBag.hs +++ b/src/Text/Pandoc/MediaBag.hs @@ -79,5 +79,5 @@ lookupMedia fp (MediaBag mediamap) = M.lookup (splitDirectories fp) mediamap -- their corresponding mime types and the lengths in bytes of the contents. mediaDirectory :: MediaBag -> [(String, MimeType, Int)] mediaDirectory (MediaBag mediamap) = - M.foldWithKey (\fp (mime,contents) -> + M.foldrWithKey (\fp (mime,contents) -> (((Posix.joinPath fp), mime, fromIntegral $ BL.length contents):)) [] mediamap diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index 2047285eb..3f612f40a 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -84,7 +84,7 @@ metaToJSON' blockWriter inlineWriter (Meta metamap) = do renderedMap <- Traversable.mapM (metaValueToJSON blockWriter inlineWriter) metamap - return $ M.foldWithKey defField (Object H.empty) renderedMap + return $ M.foldrWithKey defField (Object H.empty) renderedMap -- | Add variables to JSON object, replacing any existing values. -- Also include @meta-json@, a field containing a string representation -- cgit v1.2.3 From e22dc98a70d030cc6b4056d14ddd6462c7790f97 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 14 Jul 2017 09:57:18 +0200 Subject: Fixed some ghc 8.2 compiler warnings. (Unnecessary type constraints.) --- src/Text/Pandoc/Parsing.hs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 549042d14..c0a3b8dc0 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -247,7 +247,7 @@ instance Monoid a => Monoid (Future s a) where mconcat = liftM mconcat . sequence -- | Parse characters while a predicate is true. -takeWhileP :: Stream [Char] m Char +takeWhileP :: Monad m => (Char -> Bool) -> ParserT [Char] st m [Char] takeWhileP f = do -- faster than 'many (satisfy f)' @@ -262,7 +262,7 @@ takeWhileP f = do -- Parse n characters of input (or the rest of the input if -- there aren't n characters). -takeP :: Stream [Char] m Char => Int -> ParserT [Char] st m [Char] +takeP :: Monad m => Int -> ParserT [Char] st m [Char] takeP n = do guard (n > 0) -- faster than 'count n anyChar' @@ -276,7 +276,7 @@ takeP n = do return xs -- | Parse any line of text -anyLine :: Stream [Char] m Char => ParserT [Char] st m [Char] +anyLine :: Monad m => ParserT [Char] st m [Char] anyLine = do -- This is much faster than: -- manyTill anyChar newline @@ -292,11 +292,11 @@ anyLine = do _ -> mzero -- | Parse any line, include the final newline in the output -anyLineNewline :: Stream [Char] m Char => ParserT [Char] st m [Char] +anyLineNewline :: Monad m => ParserT [Char] st m [Char] anyLineNewline = (++ "\n") <$> anyLine -- | Parse indent by specified number of spaces (or equiv. tabs) -indentWith :: Stream [Char] m Char +indentWith :: Monad m => HasReaderOptions st => Int -> ParserT [Char] st m [Char] indentWith num = do @@ -422,7 +422,7 @@ parseFromString' parser str = do return res -- | Parse raw line block up to and including blank lines. -lineClump :: Stream [Char] m Char => ParserT [Char] st m String +lineClump :: Monad m => ParserT [Char] st m String lineClump = blanklines <|> (many1 (notFollowedBy blankline >> anyLine) >>= return . unlines) @@ -520,7 +520,7 @@ uriScheme :: Stream s m Char => ParserT s st m String uriScheme = oneOfStringsCI (Set.toList schemes) -- | Parses a URI. Returns pair of original and URI-escaped version. -uri :: Stream [Char] m Char => ParserT [Char] st m (String, String) +uri :: Monad m => ParserT [Char] st m (String, String) uri = try $ do scheme <- uriScheme char ':' @@ -625,7 +625,7 @@ withHorizDisplacement parser = do -- | Applies a parser and returns the raw string that was parsed, -- along with the value produced by the parser. -withRaw :: Stream [Char] m Char => ParsecT [Char] st m a -> ParsecT [Char] st m (a, [Char]) +withRaw :: Monad m => ParsecT [Char] st m a -> ParsecT [Char] st m (a, [Char]) withRaw parser = do pos1 <- getPosition inp <- getInput @@ -786,7 +786,7 @@ charRef = do c <- characterReference return $ Str [c] -lineBlockLine :: Stream [Char] m Char => ParserT [Char] st m String +lineBlockLine :: Monad m => ParserT [Char] st m String lineBlockLine = try $ do char '|' char ' ' @@ -796,11 +796,11 @@ lineBlockLine = try $ do continuations <- many (try $ char ' ' >> anyLine) return $ white ++ unwords (line : continuations) -blankLineBlockLine :: Stream [Char] m Char => ParserT [Char] st m Char +blankLineBlockLine :: Monad m => ParserT [Char] st m Char blankLineBlockLine = try (char '|' >> blankline) -- | Parses an RST-style line block and returns a list of strings. -lineBlockLines :: Stream [Char] m Char => ParserT [Char] st m [String] +lineBlockLines :: Monad m => ParserT [Char] st m [String] lineBlockLines = try $ do lines' <- many1 (lineBlockLine <|> ((:[]) <$> blankLineBlockLine)) skipMany1 $ blankline <|> blankLineBlockLine @@ -870,7 +870,7 @@ widthsFromIndices numColumns' indices = -- (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). -gridTableWith :: (Stream [Char] m Char, HasReaderOptions st, +gridTableWith :: (Monad m, HasReaderOptions st, Functor mf, Applicative mf, Monad mf) => ParserT [Char] st m (mf Blocks) -- ^ Block list parser -> Bool -- ^ Headerless table @@ -879,7 +879,7 @@ gridTableWith blocks headless = tableWith (gridTableHeader headless blocks) (gridTableRow blocks) (gridTableSep '-') gridTableFooter -gridTableWith' :: (Stream [Char] m Char, HasReaderOptions st, +gridTableWith' :: (Monad m, HasReaderOptions st, Functor mf, Applicative mf, Monad mf) => ParserT [Char] st m (mf Blocks) -- ^ Block list parser -> Bool -- ^ Headerless table @@ -919,7 +919,7 @@ gridTableSep :: Stream s m Char => Char -> ParserT s st m Char gridTableSep ch = try $ gridDashedLines ch >> return '\n' -- | Parse header for a grid table. -gridTableHeader :: (Stream [Char] m Char, Functor mf, Applicative mf, Monad mf) +gridTableHeader :: (Monad m, Functor mf, Applicative mf, Monad mf) => Bool -- ^ Headerless table -> ParserT [Char] st m (mf Blocks) -> ParserT [Char] st m (mf [Blocks], [Alignment], [Int]) -- cgit v1.2.3 From f6d97bc8cc7d635c6a69f64293a0bf2a04a1db05 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 14 Jul 2017 11:00:42 +0200 Subject: Revert "Fixed some ghc 8.2 compiler warnings." This reverts commit e22dc98a70d030cc6b4056d14ddd6462c7790f97. --- src/Text/Pandoc/Parsing.hs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index c0a3b8dc0..549042d14 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -247,7 +247,7 @@ instance Monoid a => Monoid (Future s a) where mconcat = liftM mconcat . sequence -- | Parse characters while a predicate is true. -takeWhileP :: Monad m +takeWhileP :: Stream [Char] m Char => (Char -> Bool) -> ParserT [Char] st m [Char] takeWhileP f = do -- faster than 'many (satisfy f)' @@ -262,7 +262,7 @@ takeWhileP f = do -- Parse n characters of input (or the rest of the input if -- there aren't n characters). -takeP :: Monad m => Int -> ParserT [Char] st m [Char] +takeP :: Stream [Char] m Char => Int -> ParserT [Char] st m [Char] takeP n = do guard (n > 0) -- faster than 'count n anyChar' @@ -276,7 +276,7 @@ takeP n = do return xs -- | Parse any line of text -anyLine :: Monad m => ParserT [Char] st m [Char] +anyLine :: Stream [Char] m Char => ParserT [Char] st m [Char] anyLine = do -- This is much faster than: -- manyTill anyChar newline @@ -292,11 +292,11 @@ anyLine = do _ -> mzero -- | Parse any line, include the final newline in the output -anyLineNewline :: Monad m => ParserT [Char] st m [Char] +anyLineNewline :: Stream [Char] m Char => ParserT [Char] st m [Char] anyLineNewline = (++ "\n") <$> anyLine -- | Parse indent by specified number of spaces (or equiv. tabs) -indentWith :: Monad m +indentWith :: Stream [Char] m Char => HasReaderOptions st => Int -> ParserT [Char] st m [Char] indentWith num = do @@ -422,7 +422,7 @@ parseFromString' parser str = do return res -- | Parse raw line block up to and including blank lines. -lineClump :: Monad m => ParserT [Char] st m String +lineClump :: Stream [Char] m Char => ParserT [Char] st m String lineClump = blanklines <|> (many1 (notFollowedBy blankline >> anyLine) >>= return . unlines) @@ -520,7 +520,7 @@ uriScheme :: Stream s m Char => ParserT s st m String uriScheme = oneOfStringsCI (Set.toList schemes) -- | Parses a URI. Returns pair of original and URI-escaped version. -uri :: Monad m => ParserT [Char] st m (String, String) +uri :: Stream [Char] m Char => ParserT [Char] st m (String, String) uri = try $ do scheme <- uriScheme char ':' @@ -625,7 +625,7 @@ withHorizDisplacement parser = do -- | Applies a parser and returns the raw string that was parsed, -- along with the value produced by the parser. -withRaw :: Monad m => ParsecT [Char] st m a -> ParsecT [Char] st m (a, [Char]) +withRaw :: Stream [Char] m Char => ParsecT [Char] st m a -> ParsecT [Char] st m (a, [Char]) withRaw parser = do pos1 <- getPosition inp <- getInput @@ -786,7 +786,7 @@ charRef = do c <- characterReference return $ Str [c] -lineBlockLine :: Monad m => ParserT [Char] st m String +lineBlockLine :: Stream [Char] m Char => ParserT [Char] st m String lineBlockLine = try $ do char '|' char ' ' @@ -796,11 +796,11 @@ lineBlockLine = try $ do continuations <- many (try $ char ' ' >> anyLine) return $ white ++ unwords (line : continuations) -blankLineBlockLine :: Monad m => ParserT [Char] st m Char +blankLineBlockLine :: Stream [Char] m Char => ParserT [Char] st m Char blankLineBlockLine = try (char '|' >> blankline) -- | Parses an RST-style line block and returns a list of strings. -lineBlockLines :: Monad m => ParserT [Char] st m [String] +lineBlockLines :: Stream [Char] m Char => ParserT [Char] st m [String] lineBlockLines = try $ do lines' <- many1 (lineBlockLine <|> ((:[]) <$> blankLineBlockLine)) skipMany1 $ blankline <|> blankLineBlockLine @@ -870,7 +870,7 @@ widthsFromIndices numColumns' indices = -- (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). -gridTableWith :: (Monad m, HasReaderOptions st, +gridTableWith :: (Stream [Char] m Char, HasReaderOptions st, Functor mf, Applicative mf, Monad mf) => ParserT [Char] st m (mf Blocks) -- ^ Block list parser -> Bool -- ^ Headerless table @@ -879,7 +879,7 @@ gridTableWith blocks headless = tableWith (gridTableHeader headless blocks) (gridTableRow blocks) (gridTableSep '-') gridTableFooter -gridTableWith' :: (Monad m, HasReaderOptions st, +gridTableWith' :: (Stream [Char] m Char, HasReaderOptions st, Functor mf, Applicative mf, Monad mf) => ParserT [Char] st m (mf Blocks) -- ^ Block list parser -> Bool -- ^ Headerless table @@ -919,7 +919,7 @@ gridTableSep :: Stream s m Char => Char -> ParserT s st m Char gridTableSep ch = try $ gridDashedLines ch >> return '\n' -- | Parse header for a grid table. -gridTableHeader :: (Monad m, Functor mf, Applicative mf, Monad mf) +gridTableHeader :: (Stream [Char] m Char, Functor mf, Applicative mf, Monad mf) => Bool -- ^ Headerless table -> ParserT [Char] st m (mf Blocks) -> ParserT [Char] st m (mf [Blocks], [Alignment], [Int]) -- cgit v1.2.3 From 42fd536a0e66dcfddb4abd7daf6d738bf954b62a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 14 Jul 2017 11:17:46 +0200 Subject: Fix ghc 8.2.1 compiler warnings. --- src/Text/Pandoc/Parsing.hs | 49 ++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 23 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 549042d14..0c97d4060 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -247,8 +247,9 @@ instance Monoid a => Monoid (Future s a) where mconcat = liftM mconcat . sequence -- | Parse characters while a predicate is true. -takeWhileP :: Stream [Char] m Char - => (Char -> Bool) -> ParserT [Char] st m [Char] +takeWhileP :: Monad m + => (Char -> Bool) + -> ParserT [Char] st m [Char] takeWhileP f = do -- faster than 'many (satisfy f)' inp <- getInput @@ -262,7 +263,7 @@ takeWhileP f = do -- Parse n characters of input (or the rest of the input if -- there aren't n characters). -takeP :: Stream [Char] m Char => Int -> ParserT [Char] st m [Char] +takeP :: Monad m => Int -> ParserT [Char] st m [Char] takeP n = do guard (n > 0) -- faster than 'count n anyChar' @@ -276,7 +277,7 @@ takeP n = do return xs -- | Parse any line of text -anyLine :: Stream [Char] m Char => ParserT [Char] st m [Char] +anyLine :: Monad m => ParserT [Char] st m [Char] anyLine = do -- This is much faster than: -- manyTill anyChar newline @@ -292,13 +293,13 @@ anyLine = do _ -> mzero -- | Parse any line, include the final newline in the output -anyLineNewline :: Stream [Char] m Char => ParserT [Char] st m [Char] +anyLineNewline :: Monad m => ParserT [Char] st m [Char] anyLineNewline = (++ "\n") <$> anyLine -- | Parse indent by specified number of spaces (or equiv. tabs) -indentWith :: Stream [Char] m Char +indentWith :: Stream s m Char => HasReaderOptions st - => Int -> ParserT [Char] st m [Char] + => Int -> ParserT s st m [Char] indentWith num = do tabStop <- getOption readerTabStop if (num < tabStop) @@ -394,9 +395,9 @@ stringAnyCase (x:xs) = do -- | Parse contents of 'str' using 'parser' and return result. parseFromString :: Monad m - => ParserT String st m a + => ParserT [Char] st m a -> String - -> ParserT String st m a + -> ParserT [Char] st m a parseFromString parser str = do oldPos <- getPosition setPosition $ initialPos "chunk" @@ -422,9 +423,9 @@ parseFromString' parser str = do return res -- | Parse raw line block up to and including blank lines. -lineClump :: Stream [Char] m Char => ParserT [Char] st m String +lineClump :: Monad m => ParserT [Char] st m String lineClump = blanklines - <|> (many1 (notFollowedBy blankline >> anyLine) >>= return . unlines) + <|> (unlines <$> many1 (notFollowedBy blankline >> anyLine)) -- | Parse a string of characters between an open character -- and a close character, including text between balanced @@ -520,7 +521,7 @@ uriScheme :: Stream s m Char => ParserT s st m String uriScheme = oneOfStringsCI (Set.toList schemes) -- | Parses a URI. Returns pair of original and URI-escaped version. -uri :: Stream [Char] m Char => ParserT [Char] st m (String, String) +uri :: Monad m => ParserT [Char] st m (String, String) uri = try $ do scheme <- uriScheme char ':' @@ -625,7 +626,9 @@ withHorizDisplacement parser = do -- | Applies a parser and returns the raw string that was parsed, -- along with the value produced by the parser. -withRaw :: Stream [Char] m Char => ParsecT [Char] st m a -> ParsecT [Char] st m (a, [Char]) +withRaw :: Monad m + => ParsecT [Char] st m a + -> ParsecT [Char] st m (a, [Char]) withRaw parser = do pos1 <- getPosition inp <- getInput @@ -786,7 +789,7 @@ charRef = do c <- characterReference return $ Str [c] -lineBlockLine :: Stream [Char] m Char => ParserT [Char] st m String +lineBlockLine :: Monad m => ParserT [Char] st m String lineBlockLine = try $ do char '|' char ' ' @@ -796,11 +799,11 @@ lineBlockLine = try $ do continuations <- many (try $ char ' ' >> anyLine) return $ white ++ unwords (line : continuations) -blankLineBlockLine :: Stream [Char] m Char => ParserT [Char] st m Char +blankLineBlockLine :: Stream s m Char => ParserT s st m Char blankLineBlockLine = try (char '|' >> blankline) -- | Parses an RST-style line block and returns a list of strings. -lineBlockLines :: Stream [Char] m Char => ParserT [Char] st m [String] +lineBlockLines :: Monad m => ParserT [Char] st m [String] lineBlockLines = try $ do lines' <- many1 (lineBlockLine <|> ((:[]) <$> blankLineBlockLine)) skipMany1 $ blankline <|> blankLineBlockLine @@ -870,7 +873,7 @@ widthsFromIndices numColumns' indices = -- (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). -gridTableWith :: (Stream [Char] m Char, HasReaderOptions st, +gridTableWith :: (Monad m, HasReaderOptions st, Functor mf, Applicative mf, Monad mf) => ParserT [Char] st m (mf Blocks) -- ^ Block list parser -> Bool -- ^ Headerless table @@ -879,7 +882,7 @@ gridTableWith blocks headless = tableWith (gridTableHeader headless blocks) (gridTableRow blocks) (gridTableSep '-') gridTableFooter -gridTableWith' :: (Stream [Char] m Char, HasReaderOptions st, +gridTableWith' :: (Monad m, HasReaderOptions st, Functor mf, Applicative mf, Monad mf) => ParserT [Char] st m (mf Blocks) -- ^ Block list parser -> Bool -- ^ Headerless table @@ -919,7 +922,7 @@ gridTableSep :: Stream s m Char => Char -> ParserT s st m Char gridTableSep ch = try $ gridDashedLines ch >> return '\n' -- | Parse header for a grid table. -gridTableHeader :: (Stream [Char] m Char, Functor mf, Applicative mf, Monad mf) +gridTableHeader :: (Monad m, Functor mf, Applicative mf, Monad mf) => Bool -- ^ Headerless table -> ParserT [Char] st m (mf Blocks) -> ParserT [Char] st m (mf [Blocks], [Alignment], [Int]) @@ -952,7 +955,7 @@ gridTableRawLine indices = do return (gridTableSplitLine indices line) -- | Parse row of grid table. -gridTableRow :: (Stream [Char] m Char, Functor mf, Applicative mf, Monad mf) +gridTableRow :: (Monad m, Functor mf, Applicative mf, Monad mf) => ParserT [Char] st m (mf Blocks) -> [Int] -> ParserT [Char] st m (mf [Blocks]) @@ -981,8 +984,8 @@ gridTableFooter = blanklines --- -- | Removes the ParsecT layer from the monad transformer stack -readWithM :: (Monad m) - => ParserT [Char] st m a -- ^ parser +readWithM :: Monad m + => ParserT [Char] st m a -- ^ parser -> st -- ^ initial state -> String -- ^ input -> m (Either PandocError a) @@ -998,7 +1001,7 @@ readWith :: Parser [Char] st a readWith p t inp = runIdentity $ readWithM p t inp -- | Parse a string with @parser@ (for testing). -testStringWith :: (Show a) +testStringWith :: Show a => ParserT [Char] ParserState Identity a -> [Char] -> IO () -- cgit v1.2.3 From f1407848708c9d76f0fa3a95e6af7ffc13e158d6 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 14 Jul 2017 16:24:30 +0200 Subject: Class: added addToFileTree --- src/Text/Pandoc/Class.hs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 120ba8fee..d8505e0b1 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -60,6 +60,7 @@ module Text.Pandoc.Class ( PandocMonad(..) , PandocPure(..) , FileTree(..) , FileInfo(..) + , addToFileTree , runIO , runIOorExplode , runPure @@ -479,6 +480,13 @@ newtype FileTree = FileTree {unFileTree :: M.Map FilePath FileInfo} getFileInfo :: FilePath -> FileTree -> Maybe FileInfo getFileInfo fp tree = M.lookup fp $ unFileTree tree +addToFileTree :: FileTree -> FilePath -> IO FileTree +addToFileTree (FileTree treemap) fp = do + contents <- B.readFile fp + mtime <- IO.getModificationTime fp + return $ FileTree $ + M.insert fp FileInfo{ infoFileMTime = mtime + , infoFileContents = contents } treemap newtype PandocPure a = PandocPure { unPandocPure :: ExceptT PandocError -- cgit v1.2.3 From f8fef1ebb053c3e5a66c1a9a91f3e04940a6fdd6 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 14 Jul 2017 16:35:29 +0200 Subject: Class: remove stFontFiles in PureState, 'glob' searches stFiles. --- src/Text/Pandoc/Class.hs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index d8505e0b1..6906873d7 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -438,7 +438,6 @@ data PureState = PureState { stStdGen :: StdGen , stFiles :: FileTree , stUserDataDir :: FileTree , stCabalDataDir :: FileTree - , stFontFiles :: [FilePath] } instance Default PureState where @@ -453,7 +452,6 @@ instance Default PureState where , stFiles = mempty , stUserDataDir = mempty , stCabalDataDir = mempty - , stFontFiles = [] } @@ -550,8 +548,8 @@ instance PandocMonad PandocPure where Nothing -> readDataFile Nothing fname glob s = do - fontFiles <- getsPureState stFontFiles - return (filter (match (compile s)) fontFiles) + FileTree ftmap <- getsPureState stFiles + return $ filter (match (compile s)) $ M.keys ftmap getModificationTime fp = do fps <- getsPureState stFiles -- cgit v1.2.3 From df5a00990e6d7ee410544e1d6daabb0e102348d6 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 14 Jul 2017 17:28:13 +0200 Subject: Class: make addToFileTree handle directories recursively. --- src/Text/Pandoc/Class.hs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 6906873d7..63b7419c4 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -102,7 +102,8 @@ import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL import qualified System.Environment as IO (lookupEnv) import System.FilePath.Glob (match, compile) -import System.Directory (createDirectoryIfMissing) +import System.Directory (createDirectoryIfMissing, listDirectory, + doesDirectoryExist) import System.FilePath ((</>), (<.>), takeDirectory, takeExtension, dropExtension, isRelative, normalise) import qualified System.FilePath.Glob as IO (glob) @@ -478,13 +479,21 @@ newtype FileTree = FileTree {unFileTree :: M.Map FilePath FileInfo} getFileInfo :: FilePath -> FileTree -> Maybe FileInfo getFileInfo fp tree = M.lookup fp $ unFileTree tree +-- | Add the specified file to the FileTree. If file +-- is a directory, add its contents recursively. addToFileTree :: FileTree -> FilePath -> IO FileTree addToFileTree (FileTree treemap) fp = do - contents <- B.readFile fp - mtime <- IO.getModificationTime fp - return $ FileTree $ - M.insert fp FileInfo{ infoFileMTime = mtime - , infoFileContents = contents } treemap + isdir <- doesDirectoryExist fp + if isdir + then do -- recursively add contents of directories + fs <- map (fp </>) <$> listDirectory fp + foldM addToFileTree (FileTree treemap) fs + else do + contents <- B.readFile fp + mtime <- IO.getModificationTime fp + return $ FileTree $ + M.insert fp FileInfo{ infoFileMTime = mtime + , infoFileContents = contents } treemap newtype PandocPure a = PandocPure { unPandocPure :: ExceptT PandocError -- cgit v1.2.3 From dcf79c418801a823f917a97ab1e8461959f0189a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 15 Jul 2017 11:38:43 +0200 Subject: Don't rely on listDirectory, which is only in newer versions... of directory. --- src/Text/Pandoc/Class.hs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 63b7419c4..f83683d4c 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -102,7 +102,7 @@ import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL import qualified System.Environment as IO (lookupEnv) import System.FilePath.Glob (match, compile) -import System.Directory (createDirectoryIfMissing, listDirectory, +import System.Directory (createDirectoryIfMissing, getDirectoryContents, doesDirectoryExist) import System.FilePath ((</>), (<.>), takeDirectory, takeExtension, dropExtension, isRelative, normalise) @@ -486,7 +486,10 @@ addToFileTree (FileTree treemap) fp = do isdir <- doesDirectoryExist fp if isdir then do -- recursively add contents of directories - fs <- map (fp </>) <$> listDirectory fp + let isSpecial ".." = True + isSpecial "." = True + isSpecial _ = False + fs <- (map (fp </>) . filter (not . isSpecial)) <$> getDirectoryContents fp foldM addToFileTree (FileTree treemap) fs else do contents <- B.readFile fp -- cgit v1.2.3 From 56f63af3f6776d8f3d6466f928b0c294032444c6 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 19 Jul 2017 17:30:22 +0200 Subject: LaTeX reader: fixed regression with starred environment names. Closes #3803. --- src/Text/Pandoc/Readers/LaTeX.hs | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index f5e387429..ab3994770 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1456,22 +1456,14 @@ begin_ :: PandocMonad m => Text -> LP m () begin_ t = (try $ do controlSeq "begin" spaces - symbol '{' - spaces - Tok _ Word txt <- satisfyTok isWordTok - spaces - symbol '}' + txt <- untokenize <$> braced guard (t == txt)) <?> ("\\begin{" ++ T.unpack t ++ "}") end_ :: PandocMonad m => Text -> LP m () end_ t = (try $ do controlSeq "end" spaces - symbol '{' - spaces - Tok _ Word txt <- satisfyTok isWordTok - spaces - symbol '}' + txt <- untokenize <$> braced guard $ t == txt) <?> ("\\end{" ++ T.unpack t ++ "}") preamble :: PandocMonad m => LP m Blocks @@ -1571,11 +1563,8 @@ newenvironment = do controlSeq "renewenvironment" <|> controlSeq "provideenvironment" optional $ symbol '*' - symbol '{' - spaces - Tok _ Word name <- satisfyTok isWordTok spaces - symbol '}' + name <- untokenize <$> braced spaces numargs <- option 0 $ try bracketedNum spaces -- cgit v1.2.3 From 2ce6b492e1dce6cddee6dc141bc40bbf67998302 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 19 Jul 2017 21:31:46 +0200 Subject: Class: started adding haddocks. --- src/Text/Pandoc/Class.hs | 49 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 10 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index f83683d4c..46e300953 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -5,7 +5,8 @@ {-# LANGUAGE FlexibleContexts #-} {- -Copyright (C) 2016 Jesse Rosenthal <jrosenthal@jhu.edu> +Copyright (C) 2016-17 Jesse Rosenthal <jrosenthal@jhu.edu> +and 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 @@ -24,7 +25,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Class - Copyright : Copyright (C) 2016 Jesse Rosenthal + Copyright : Copyright (C) 2016-17 Jesse Rosenthal, John MacFarlane License : GNU GPL, version 2 or above Maintainer : Jesse Rosenthal <jrosenthal@jhu.edu> @@ -122,36 +123,64 @@ import qualified Data.Map as M import Text.Pandoc.Error import qualified Debug.Trace +-- | The PandocMonad typeclass contains all the potentially +-- IO-related functions used in pandoc's readers and writers. +-- Instances of this typeclass may implement these functions +-- in IO (as in 'PandocIO') or using an internal state that +-- represents a file system, time, and so on (as in 'PandocPure'). class (Functor m, Applicative m, Monad m, MonadError PandocError m) => PandocMonad m where + -- | Lookup an environment variable. lookupEnv :: String -> m (Maybe String) + -- | Get the current (UTC) time. getCurrentTime :: m UTCTime + -- | Get the locale's time zone. getCurrentTimeZone :: m TimeZone + -- | Return a new generator for random numbers. newStdGen :: m StdGen + -- | Return a new unique integer. newUniqueHash :: m Int + -- | Retrieve contents and mime type from a URL, raising + -- an error on failure. openURL :: String -> m (B.ByteString, Maybe MimeType) + -- | Read the lazy ByteString contents from a file path, + -- raising an error on failure. readFileLazy :: FilePath -> m BL.ByteString + -- | Read the strict ByteString contents from a file path, + -- raising an error on failure. readFileStrict :: FilePath -> m B.ByteString + -- | Read file from specified user data directory or, + -- if not found there, from Cabal data directory. readDataFile :: Maybe FilePath -> FilePath -> m B.ByteString + -- | Return a list of paths that match a glob, relative to + -- the working directory. See 'System.FilePath.Glob' for + -- the glob syntax. glob :: String -> m [FilePath] + -- | Return the modification time of a file. getModificationTime :: FilePath -> m UTCTime + -- | Get the value of the 'CommonState' used by all instances + -- of 'PandocMonad'. getCommonState :: m CommonState + -- | Set the value of the 'CommonState' used by all instances + -- of 'PandocMonad'. + -- | Get the value of a specific field of 'CommonState'. putCommonState :: CommonState -> m () - + -- | Get the value of a specific field of 'CommonState'. getsCommonState :: (CommonState -> a) -> m a getsCommonState f = f <$> getCommonState - + -- | Modify the 'CommonState'. modifyCommonState :: (CommonState -> CommonState) -> m () modifyCommonState f = getCommonState >>= putCommonState . f - + -- Output a log message. + logOutput :: LogMessage -> m () + -- Output a debug message to sterr, using 'Debug.Trace.trace'. + -- Note: this writes to stderr even in pure instances. trace :: String -> m () trace msg = do tracing <- getsCommonState stTrace when tracing $ Debug.Trace.trace ("[trace] " ++ msg) (return ()) - logOutput :: LogMessage -> m () - --- Functions defined for all PandocMonad instances +-- * Functions defined for all PandocMonad instances setVerbosity :: PandocMonad m => Verbosity -> m () setVerbosity verbosity = @@ -194,10 +223,10 @@ setResourcePath ps = modifyCommonState $ \st -> st{stResourcePath = ps} getResourcePath :: PandocMonad m => m [FilePath] getResourcePath = getsCommonState stResourcePath -getPOSIXTime :: (PandocMonad m) => m POSIXTime +getPOSIXTime :: PandocMonad m => m POSIXTime getPOSIXTime = utcTimeToPOSIXSeconds <$> getCurrentTime -getZonedTime :: (PandocMonad m) => m ZonedTime +getZonedTime :: PandocMonad m => m ZonedTime getZonedTime = do t <- getCurrentTime tz <- getCurrentTimeZone -- cgit v1.2.3 From 992943d98e14cc2dd249d6279c8c930dddc5547d Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 19 Jul 2017 21:46:28 +0200 Subject: Templates: change signature of getDefaultTemplate. Now it runs in any instance of PandocMonad, and returns a String rather than an Either value. --- src/Text/Pandoc/App.hs | 7 ++++--- src/Text/Pandoc/Templates.hs | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 13 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 68bdc1432..0d4a82b70 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -218,7 +218,8 @@ convertWithOpts opts = do templ <- case optTemplate opts of _ | not standalone -> return Nothing Nothing -> do - deftemp <- getDefaultTemplate datadir format + deftemp <- runIO $ + getDefaultTemplate datadir format case deftemp of Left e -> E.throwIO e Right t -> return (Just t) @@ -991,10 +992,10 @@ options = , Option "D" ["print-default-template"] (ReqArg (\arg _ -> do - templ <- getDefaultTemplate Nothing arg + templ <- runIO $ getDefaultTemplate Nothing arg case templ of Right t -> UTF8.hPutStr stdout t - Left e -> E.throwIO $ PandocAppError (show e) + Left e -> E.throwIO e exitSuccess) "FORMAT") "" -- "Print default template for FORMAT" diff --git a/src/Text/Pandoc/Templates.hs b/src/Text/Pandoc/Templates.hs index 1a26b7168..516cc4b2f 100644 --- a/src/Text/Pandoc/Templates.hs +++ b/src/Text/Pandoc/Templates.hs @@ -38,28 +38,28 @@ module Text.Pandoc.Templates ( module Text.DocTemplates , getDefaultTemplate ) where -import qualified Control.Exception as E (IOException, try) import Control.Monad.Except (throwError) import Data.Aeson (ToJSON (..)) import qualified Data.Text as T import System.FilePath ((<.>), (</>)) import Text.DocTemplates (Template, TemplateTarget, applyTemplate, compileTemplate, renderTemplate, varListToJSON) -import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Class (PandocMonad(readDataFile)) import Text.Pandoc.Error -import Text.Pandoc.Shared (readDataFileUTF8) +import qualified Text.Pandoc.UTF8 as UTF8 -- | Get default template for the specified writer. -getDefaultTemplate :: (Maybe FilePath) -- ^ User data directory to search first +getDefaultTemplate :: PandocMonad m + => (Maybe FilePath) -- ^ User data directory to search 1st -> String -- ^ Name of writer - -> IO (Either E.IOException String) + -> m String 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 "" + "native" -> return "" + "json" -> return "" + "docx" -> return "" + "fb2" -> return "" "odt" -> getDefaultTemplate user "opendocument" "html" -> getDefaultTemplate user "html5" "docbook" -> getDefaultTemplate user "docbook5" @@ -70,7 +70,7 @@ getDefaultTemplate user writer = do "markdown_mmd" -> getDefaultTemplate user "markdown" "markdown_phpextra" -> getDefaultTemplate user "markdown" _ -> let fname = "templates" </> "default" <.> format - in E.try $ readDataFileUTF8 user fname + in UTF8.toString <$> readDataFile user fname -- | Like 'applyTemplate', but runs in PandocMonad and -- raises an error if compilation fails. -- cgit v1.2.3 From 7191fe1f29f2f8b45f9be7e0f8bc9ed889e431d2 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 21 Jul 2017 09:28:11 +0200 Subject: LaTeX reader: handle optional args in raw `\titleformat`. Closes #3804. --- src/Text/Pandoc/Readers/LaTeX.hs | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index ab3994770..58a48c655 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1362,14 +1362,20 @@ rawInlineOr name' fallback = do getRawCommand :: PandocMonad m => Text -> LP m String getRawCommand txt = do - (_, rawargs) <- withRaw - ((if txt == "\\write" - then () <$ satisfyTok isWordTok -- digits - else return ()) *> - skipangles *> - skipopts *> - option "" (try (optional sp *> dimenarg)) *> - many braced) + (_, rawargs) <- withRaw $ + case txt of + "\\write" -> do + void $ satisfyTok isWordTok -- digits + void braced + "\\titleformat" -> do + void braced + skipopts + void $ count 4 braced + _ -> do + skipangles + skipopts + option "" (try (optional sp *> dimenarg)) + void $ many braced return $ T.unpack (txt <> untokenize rawargs) isBlockCommand :: Text -> Bool @@ -1397,6 +1403,7 @@ treatAsBlock = Set.fromList , "newpage" , "clearpage" , "pagebreak" + , "titleformat" ] isInlineCommand :: Text -> Bool -- cgit v1.2.3 From 335a1c7f4867f7cd3575f07b5efa75712d59d1ac Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 21 Jul 2017 11:04:13 +0300 Subject: Muse reader: fix reading of lists inside tags (#3802) --- src/Text/Pandoc/Readers/Muse.hs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 1ae73c148..9d967a9de 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -115,11 +115,10 @@ htmlElement :: PandocMonad m => String -> MuseParser m (Attr, String) htmlElement tag = try $ do (TagOpen _ attr, _) <- htmlTag (~== TagOpen tag []) content <- manyTill anyChar (endtag <|> endofinput) - return (htmlAttrToPandoc attr, trim content) + return (htmlAttrToPandoc attr, content) where endtag = void $ htmlTag (~== TagClose tag) endofinput = lookAhead $ try $ skipMany blankline >> skipSpaces >> eof - trim = dropWhile (=='\n') . reverse . dropWhile (=='\n') . reverse htmlAttrToPandoc :: [Attribute String] -> Attr htmlAttrToPandoc attrs = (ident, classes, keyvals) @@ -132,7 +131,7 @@ parseHtmlContentWithAttrs :: PandocMonad m => String -> MuseParser m a -> MuseParser m (Attr, [a]) parseHtmlContentWithAttrs tag parser = do (attr, content) <- htmlElement tag - parsedContent <- try $ parseContent content + parsedContent <- try $ parseContent (content ++ "\n") return (attr, parsedContent) where parseContent = parseFromString $ nested $ manyTill parser endOfContent -- cgit v1.2.3 From 2ae75e23dd740e968245269d8765d5b5af3a5f35 Mon Sep 17 00:00:00 2001 From: rlpowell <rlpowell@digitalkingdom.org> Date: Fri, 21 Jul 2017 01:09:54 -0700 Subject: Added TikiWiki reader (#3800) Added TikiWiki reader, including tests and documentation. It's probably not *complete*, but it works pretty well, handles all the basics (and some not-so-basics). --- src/Text/Pandoc/Readers.hs | 3 + src/Text/Pandoc/Readers/TikiWiki.hs | 658 ++++++++++++++++++++++++++++++++++++ 2 files changed, 661 insertions(+) create mode 100644 src/Text/Pandoc/Readers/TikiWiki.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers.hs b/src/Text/Pandoc/Readers.hs index 0374d27d5..78a2038a4 100644 --- a/src/Text/Pandoc/Readers.hs +++ b/src/Text/Pandoc/Readers.hs @@ -58,6 +58,7 @@ module Text.Pandoc.Readers , readNative , readJSON , readTWiki + , readTikiWiki , readTxt2Tags , readEPUB , readMuse @@ -92,6 +93,7 @@ import Text.Pandoc.Readers.Org import Text.Pandoc.Readers.RST import Text.Pandoc.Readers.Textile import Text.Pandoc.Readers.TWiki +import Text.Pandoc.Readers.TikiWiki import Text.Pandoc.Readers.Txt2Tags import Text.Pandoc.Shared (mapLeft) import Text.Parsec.Error @@ -126,6 +128,7 @@ readers = [ ("native" , TextReader readNative) ,("latex" , TextReader readLaTeX) ,("haddock" , TextReader readHaddock) ,("twiki" , TextReader readTWiki) + ,("tikiwiki" , TextReader readTikiWiki) ,("docx" , ByteStringReader readDocx) ,("odt" , ByteStringReader readOdt) ,("t2t" , TextReader readTxt2Tags) diff --git a/src/Text/Pandoc/Readers/TikiWiki.hs b/src/Text/Pandoc/Readers/TikiWiki.hs new file mode 100644 index 000000000..4acbaa30b --- /dev/null +++ b/src/Text/Pandoc/Readers/TikiWiki.hs @@ -0,0 +1,658 @@ +{-# LANGUAGE RelaxedPolyRec, FlexibleInstances, TypeSynonymInstances #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE FlexibleContexts #-} + +{- | + Module : Text.Pandoc.Readers.TikiWiki + Copyright : Copyright (C) 2017 Robin Lee Powell + License : GPLv2 + + Maintainer : Robin Lee Powell <robinleepowell@gmail.com> + Stability : alpha + Portability : portable + +Conversion of TikiWiki text to 'Pandoc' document. +-} + +module Text.Pandoc.Readers.TikiWiki ( readTikiWiki + ) where + +import Control.Monad +import Control.Monad.Except (throwError) +import Text.Pandoc.Definition +import qualified Text.Pandoc.Builder as B +import Text.Pandoc.Options +import Text.Pandoc.Parsing hiding (enclosed, nested) +import Text.Printf (printf) +import Text.Pandoc.XML (fromEntities) +import Text.Pandoc.Class (PandocMonad(..), CommonState(..)) +import Text.Pandoc.Shared (crFilter) +import Text.Pandoc.Logging (Verbosity(..)) +import Data.Maybe (fromMaybe) +import Data.List (intercalate) +import qualified Data.Foldable as F +import Data.Text (Text) +import qualified Data.Text as T + +-- | Read TikiWiki from an input string and return a Pandoc document. +readTikiWiki :: PandocMonad m + => ReaderOptions + -> Text + -> m Pandoc +readTikiWiki opts s = do + res <- readWithM parseTikiWiki def{ stateOptions = opts } + (T.unpack (crFilter s) ++ "\n\n") + case res of + Left e -> throwError e + Right d -> return d + +type TikiWikiParser = ParserT [Char] ParserState + +-- +-- utility functions +-- + +tryMsg :: PandocMonad m => String -> TikiWikiParser m a -> TikiWikiParser m a +tryMsg msg p = try p <?> msg + +skip :: PandocMonad m => TikiWikiParser m a -> TikiWikiParser m () +skip parser = parser >> return () + +nested :: PandocMonad m => TikiWikiParser m a -> TikiWikiParser m a +nested p = do + nestlevel <- stateMaxNestingLevel <$> getState + guard $ nestlevel > 0 + updateState $ \st -> st{ stateMaxNestingLevel = stateMaxNestingLevel st - 1 } + res <- p + updateState $ \st -> st{ stateMaxNestingLevel = nestlevel } + return res + +-- +-- main parser +-- + +parseTikiWiki :: PandocMonad m => TikiWikiParser m Pandoc +parseTikiWiki = do + bs <- mconcat <$> many block + spaces + eof + return $ B.doc bs + +block :: PandocMonad m => TikiWikiParser m B.Blocks +block = do + verbosity <- getsCommonState stVerbosity + pos <- getPosition + res <- mempty <$ skipMany1 blankline + <|> blockElements + <|> para + skipMany blankline + when (verbosity >= INFO) $ do + trace (printf "line %d: %s" (sourceLine pos) (take 60 $ show $ B.toList res)) + return res + +blockElements :: PandocMonad m => TikiWikiParser m B.Blocks +blockElements = choice [ table + , hr + , header + , mixedList + , definitionList + , codeMacro + ] + +-- top +-- ---- +-- bottom +-- +-- ---- +-- +hr :: PandocMonad m => TikiWikiParser m B.Blocks +hr = try $ do + string "----" + many (char '-') + newline + return $ B.horizontalRule + +-- ! header +-- +-- !! header level two +-- +-- !!! header level 3 +-- +header :: PandocMonad m => TikiWikiParser m B.Blocks +header = tryMsg "header" $ do + level <- many1 (char '!') >>= return . length + guard $ level <= 6 + skipSpaces + content <- B.trimInlines . mconcat <$> manyTill inline newline + attr <- registerHeader nullAttr content + return $ B.headerWith attr level $ content + +tableRow :: PandocMonad m => TikiWikiParser m [B.Blocks] +tableRow = try $ do +-- row <- sepBy1 (many1Till inline $ oneOf "\n|") (try $ string "|" <* notFollowedBy (oneOf "|\n")) +-- return $ map (B.plain . mconcat) row + row <- sepBy1 ((many1 $ noneOf "\n|") >>= parseColumn) (try $ string "|" <* notFollowedBy (oneOf "|\n")) + return $ map B.plain row + where + parseColumn x = do + parsed <- parseFromString (many1 inline) x + return $ mconcat parsed + + + +-- Tables: +-- +-- ||foo|| +-- +-- ||row1-column1|row1-column2||row2-column1|row2-column2|| +-- +-- ||row1-column1|row1-column2 +-- row2-column1|row2-column2|| +-- +-- ||row1-column1|row1-column2 +-- row2-column1|row2-column2||row3-column1|row3-column2|| +-- +-- || Orange | Apple | more +-- Bread | Pie | more +-- Butter | Ice cream | and more || +-- +table :: PandocMonad m => TikiWikiParser m B.Blocks +table = try $ do + string "||" + rows <- sepBy1 tableRow (try $ string "\n" <|> (string "||" <* notFollowedBy (string "\n"))) + string "||" + newline + -- return $ B.simpleTable (headers rows) $ trace ("rows: " ++ (show rows)) rows + return $ B.simpleTable (headers rows) $ rows + where + -- The headers are as many empty srings as the number of columns + -- in the first row + headers rows = map (B.plain . B.str) $ take (length $ rows !! 0) $ repeat "" + +para :: PandocMonad m => TikiWikiParser m B.Blocks +para = many1Till inline endOfParaElement >>= return . result . mconcat + where + endOfParaElement = lookAhead $ endOfInput <|> endOfPara <|> newBlockElement + endOfInput = try $ skipMany blankline >> skipSpaces >> eof + endOfPara = try $ blankline >> skipMany1 blankline + newBlockElement = try $ blankline >> skip blockElements + result content = if F.all (==Space) content + then mempty + else B.para $ B.trimInlines content + +-- ;item 1: definition 1 +-- ;item 2: definition 2-1 +-- + definition 2-2 +-- ;item ''3'': definition ''3'' +-- +definitionList :: PandocMonad m => TikiWikiParser m B.Blocks +definitionList = tryMsg "definitionList" $ do + elements <- many1 $ parseDefinitionListItem + return $ B.definitionList elements + where + parseDefinitionListItem :: PandocMonad m => TikiWikiParser m (B.Inlines, [B.Blocks]) + parseDefinitionListItem = do + skipSpaces >> char ';' <* skipSpaces + term <- many1Till inline $ char ':' <* skipSpaces + line <- listItemLine 1 + return $ (mconcat term, [B.plain line]) + +data ListType = None | Numbered | Bullet deriving (Ord, Eq, Show) + +data ListNesting = LN { lntype :: ListType, lnnest :: Int } deriving (Ord, Eq, Show) + +-- The first argument is a stack (most recent == head) of our list +-- nesting status; the list type and the nesting level; if we're in +-- a number list in a bullet list it'd be +-- [LN Numbered 2, LN Bullet 1] +-- +-- Mixed list example: +-- +-- # one +-- # two +-- ** two point one +-- ** two point two +-- # three +-- # four +-- +mixedList :: PandocMonad m => TikiWikiParser m B.Blocks +mixedList = try $ do + items <- try $ many1 listItem + return $ mconcat $ fixListNesting $ spanFoldUpList (LN None 0) items + +-- See the "Handling Lists" section of DESIGN-CODE for why this +-- function exists. It's to post-process the lists and do some +-- mappends. +-- +-- We need to walk the tree two items at a time, so we can see what +-- we're going to join *to* before we get there. +-- +-- Because of that, it seemed easier to do it by hand than to try to +-- figre out a fold or something. +fixListNesting :: [B.Blocks] -> [B.Blocks] +fixListNesting [] = [] +fixListNesting (first:[]) = [recurseOnList first] +-- fixListNesting nestall | trace ("\n\nfixListNesting: " ++ (show nestall)) False = undefined +-- fixListNesting nestall@(first:second:rest) = +fixListNesting (first:second:rest) = + let secondBlock = head $ B.toList second in + case secondBlock of + BulletList _ -> fixListNesting $ [(mappend (recurseOnList first) (recurseOnList second))] ++ rest + OrderedList _ _ -> fixListNesting $ [(mappend (recurseOnList first) (recurseOnList second))] ++ rest + _ -> [recurseOnList first] ++ fixListNesting (second:rest) + +-- This function walks the Block structure for fixListNesting, +-- because it's a bit complicated, what with converting to and from +-- lists and so on. +recurseOnList :: B.Blocks -> B.Blocks +-- recurseOnList item | trace ("rOL: " ++ (show $ length $ B.toList item) ++ ", " ++ (show $ B.toList item)) False = undefined +recurseOnList items + | (length $ B.toList items) == 1 = + let itemBlock = head $ B.toList items in + case itemBlock of + BulletList listItems -> B.bulletList $ fixListNesting $ map B.fromList listItems + OrderedList _ listItems -> B.orderedList $ fixListNesting $ map B.fromList listItems + _ -> items + + -- The otherwise works because we constructed the blocks, and we + -- know for a fact that no mappends have been run on them; each + -- Blocks consists of exactly one Block. + -- + -- Anything that's not like that has already been processed by + -- fixListNesting; don't bother to process it again. + | otherwise = items + + +-- Turn the list if list items into a tree by breaking off the first +-- item, splitting the remainder of the list into items that are in +-- the tree of the first item and those that aren't, wrapping the +-- tree of the first item in its list time, and recursing on both +-- sections. +spanFoldUpList :: ListNesting -> [(ListNesting, B.Blocks)] -> [B.Blocks] +spanFoldUpList _ [] = [] +spanFoldUpList ln (first:[]) = + listWrap ln (fst first) [snd first] +spanFoldUpList ln (first:rest) = + let (span1, span2) = span (splitListNesting (fst first)) rest + newTree1 = listWrap ln (fst first) $ [snd first] ++ spanFoldUpList (fst first) span1 + newTree2 = spanFoldUpList ln span2 + in + newTree1 ++ newTree2 + +-- Decide if the second item should be in the tree of the first +-- item, which is true if the second item is at a deeper nesting +-- level and of the same type. +splitListNesting :: ListNesting -> (ListNesting, B.Blocks) -> Bool +splitListNesting ln1 (ln2, _) = + if (lnnest ln1) < (lnnest ln2) then + True + else + if ln1 == ln2 then + True + else + False + +-- If we've moved to a deeper nesting level, wrap the new level in +-- the appropriate type of list. +listWrap :: ListNesting -> ListNesting -> [B.Blocks] -> [B.Blocks] +listWrap upperLN curLN retTree = + if upperLN == curLN then + retTree + else + case lntype curLN of + None -> [] + Bullet -> [B.bulletList retTree] + Numbered -> [B.orderedList retTree] + +listItem :: PandocMonad m => TikiWikiParser m (ListNesting, B.Blocks) +listItem = choice [ + bulletItem + , numberedItem + ] + + +-- * Start each line +-- * with an asterisk (*). +-- ** More asterisks gives deeper +-- *** and deeper levels. +-- +bulletItem :: PandocMonad m => TikiWikiParser m (ListNesting, B.Blocks) +bulletItem = try $ do + prefix <- many1 $ char '*' + many1 $ char ' ' + content <- listItemLine (length prefix) + return $ (LN Bullet (length prefix), B.plain content) + +-- # Start each line +-- # with a number (1.). +-- ## More number signs gives deeper +-- ### and deeper +-- +numberedItem :: PandocMonad m => TikiWikiParser m (ListNesting, B.Blocks) +numberedItem = try $ do + prefix <- many1 $ char '#' + many1 $ char ' ' + content <- listItemLine (length prefix) + return $ (LN Numbered (length prefix), B.plain content) + +listItemLine :: PandocMonad m => Int -> TikiWikiParser m B.Inlines +listItemLine nest = lineContent >>= parseContent >>= return + where + lineContent = do + content <- anyLine + continuation <- optionMaybe listContinuation + return $ filterSpaces content ++ "\n" ++ (maybe "" id continuation) + filterSpaces = reverse . dropWhile (== ' ') . reverse + listContinuation = string (take nest (repeat '+')) >> lineContent + parseContent x = do + parsed <- parseFromString (many1 inline) x + return $ mconcat parsed + +-- Turn the CODE macro attributes into Pandoc code block attributes. +mungeAttrs :: [(String, String)] -> (String, [String], [(String, String)]) +mungeAttrs rawAttrs = ("", classes, rawAttrs) + where + -- "colors" is TikiWiki CODE macro for "name of language to do + -- highlighting for"; turn the value into a class + color = fromMaybe "" $ lookup "colors" rawAttrs + -- ln = 1 means line numbering. It's also the default. So we + -- emit numberLines as a class unless ln = 0 + lnRaw = fromMaybe "1" $ lookup "ln" rawAttrs + ln = if lnRaw == "0" then + "" + else + "numberLines" + classes = filter (/= "") [color, ln] + +codeMacro :: PandocMonad m => TikiWikiParser m B.Blocks +codeMacro = try $ do + string "{CODE(" + rawAttrs <- macroAttrs + string ")}" + body <- manyTill anyChar (try (string "{CODE}")) + newline + if length rawAttrs > 0 + then + return $ B.codeBlockWith (mungeAttrs rawAttrs) body + else + return $ B.codeBlock body + + +-- +-- inline parsers +-- + +inline :: PandocMonad m => TikiWikiParser m B.Inlines +inline = choice [ whitespace + , noparse + , strong + , emph + , nbsp + , image + , htmlComment + , strikeout + , code + , wikiLink + , notExternalLink + , externalLink + , superTag + , superMacro + , subTag + , subMacro + , escapedChar + , colored + , centered + , underlined + , boxed + , breakChars + , str + , symbol + ] <?> "inline" + +whitespace :: PandocMonad m => TikiWikiParser m B.Inlines +whitespace = (lb <|> regsp) >>= return + where lb = try $ skipMany spaceChar >> linebreak >> return B.space + regsp = try $ skipMany1 spaceChar >> return B.space + +-- UNSUPPORTED, as there doesn't seem to be any facility in calibre +-- for this +nbsp :: PandocMonad m => TikiWikiParser m B.Inlines +nbsp = try $ do + string "~hs~" + return $ B.str " NOT SUPPORTED BEGIN: ~hs~ (non-breaking space) :END " + +-- UNSUPPORTED, as the desired behaviour (that the data be +-- *retained* and stored as a comment) doesn't exist in calibre, and +-- silently throwing data out seemed bad. +htmlComment :: PandocMonad m => TikiWikiParser m B.Inlines +htmlComment = try $ do + string "~hc~" + inner <- many1 $ noneOf "~" + string "~/hc~" + return $ B.str $ " NOT SUPPORTED: ~hc~ (html comment opener) BEGIN: " ++ inner ++ " ~/hc~ :END " + +linebreak :: PandocMonad m => TikiWikiParser m B.Inlines +linebreak = newline >> notFollowedBy newline >> (lastNewline <|> innerNewline) + where lastNewline = eof >> return mempty + innerNewline = return B.space + +between :: (Monoid c, PandocMonad m, Show b) => TikiWikiParser m a -> TikiWikiParser m b -> (TikiWikiParser m b -> TikiWikiParser m c) -> TikiWikiParser m c +between start end p = + mconcat <$> try (start >> notFollowedBy whitespace >> many1Till (p end) end) + +enclosed :: (Monoid b, PandocMonad m, Show a) => TikiWikiParser m a -> (TikiWikiParser m a -> TikiWikiParser m b) -> TikiWikiParser 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 + + +nestedInlines :: (Show a, PandocMonad m) => TikiWikiParser m a -> TikiWikiParser m B.Inlines +nestedInlines end = innerSpace <|> nestedInline + where + innerSpace = try $ whitespace <* (notFollowedBy end) + nestedInline = notFollowedBy whitespace >> nested inline + +-- {img attId="39" imalign="right" link="http://info.tikiwiki.org" alt="Panama Hat"} +-- +-- {img attId="37", thumb="mouseover", styleimage="border", desc="150"} +-- +-- {img src="img/wiki_up/393px-Pears.jpg" thumb="y" imalign="center" stylebox="border" button="y" desc="Pretty pears" max="200" rel="box"} +-- +image :: PandocMonad m => TikiWikiParser m B.Inlines +image = try $ do + string "{img " + rawAttrs <- sepEndBy1 imageAttr spaces + string "}" + let src = fromMaybe "" $ lookup "src" rawAttrs + let title = fromMaybe src $ lookup "desc" rawAttrs + let alt = fromMaybe title $ lookup "alt" rawAttrs + let classes = map fst $ filter (\(_,b) -> b == "" || b == "y") rawAttrs + if length src > 0 + then + return $ B.imageWith ("", classes, rawAttrs) src title (B.str alt) + else + return $ B.str $ " NOT SUPPORTED: image without src attribute BEGIN: {img " ++ (printAttrs rawAttrs) ++ "} :END " + where + printAttrs attrs = intercalate " " $ map (\(a, b) -> a ++ "=\"" ++ b ++ "\"") attrs + +imageAttr :: PandocMonad m => TikiWikiParser m (String, String) +imageAttr = try $ do + key <- many1 (noneOf "=} \t\n") + char '=' + optional $ char '"' + value <- many1 (noneOf "}\"\n") + optional $ char '"' + optional $ char ',' + return (key, value) + + +-- __strong__ +strong :: PandocMonad m => TikiWikiParser m B.Inlines +strong = try $ enclosed (string "__") nestedInlines >>= return . B.strong + +-- ''emph'' +emph :: PandocMonad m => TikiWikiParser m B.Inlines +emph = try $ enclosed (string "''") nestedInlines >>= return . B.emph + +-- ~246~ +escapedChar :: PandocMonad m => TikiWikiParser m B.Inlines +escapedChar = try $ do + string "~" + inner <- many1 $ oneOf "0123456789" + string "~" + return $ B.str $ [(toEnum ((read inner) :: Int)) :: Char] + +-- UNSUPPORTED, as there doesn't seem to be any facility in calibre +-- for this +centered :: PandocMonad m => TikiWikiParser m B.Inlines +centered = try $ do + string "::" + inner <- many1 $ noneOf ":\n" + string "::" + return $ B.str $ " NOT SUPPORTED: :: (centered) BEGIN: ::" ++ inner ++ ":: :END " + +-- UNSUPPORTED, as there doesn't seem to be any facility in calibre +-- for this +colored :: PandocMonad m => TikiWikiParser m B.Inlines +colored = try $ do + string "~~" + inner <- many1 $ noneOf "~\n" + string "~~" + return $ B.str $ " NOT SUPPORTED: ~~ (colored) BEGIN: ~~" ++ inner ++ "~~ :END " + +-- UNSUPPORTED, as there doesn't seem to be any facility in calibre +-- for this +underlined :: PandocMonad m => TikiWikiParser m B.Inlines +underlined = try $ do + string "===" + inner <- many1 $ noneOf "=\n" + string "===" + return $ B.str $ " NOT SUPPORTED: ==== (underlined) BEGIN: ===" ++ inner ++ "=== :END " + +-- UNSUPPORTED, as there doesn't seem to be any facility in calibre +-- for this +boxed :: PandocMonad m => TikiWikiParser m B.Inlines +boxed = try $ do + string "^" + inner <- many1 $ noneOf "^\n" + string "^" + return $ B.str $ " NOT SUPPORTED: ^ (boxed) BEGIN: ^" ++ inner ++ "^ :END " + +-- --text-- +strikeout :: PandocMonad m => TikiWikiParser m B.Inlines +strikeout = try $ enclosed (string "--") nestedInlines >>= return . B.strikeout + +nestedString :: (Show a, PandocMonad m) => TikiWikiParser m a -> TikiWikiParser m String +nestedString end = innerSpace <|> (count 1 nonspaceChar) + where + innerSpace = try $ many1 spaceChar <* notFollowedBy end + +breakChars :: PandocMonad m => TikiWikiParser m B.Inlines +breakChars = try $ string "%%%" >> return B.linebreak + +-- superscript: foo{TAG(tag=>sup)}super{TAG}foo / bar{SUP()}super2{SUP}bar +superTag :: PandocMonad m => TikiWikiParser m B.Inlines +superTag = try $ between (string "{TAG(tag=>sup)}") (string "{TAG}") nestedString >>= return . B.superscript . B.text . fromEntities + +superMacro :: PandocMonad m => TikiWikiParser m B.Inlines +superMacro = try $ do + string "{SUP(" + manyTill anyChar (string ")}") + body <- manyTill anyChar (string "{SUP}") + return $ B.superscript $ B.text body + +-- subscript: baz{TAG(tag=>sub)}sub{TAG}qux / qux{SUB()}sub2{SUB}qux +subTag :: PandocMonad m => TikiWikiParser m B.Inlines +subTag = try $ between (string "{TAG(tag=>sub)}") (string "{TAG}") nestedString >>= return . B.subscript . B.text . fromEntities + +subMacro :: PandocMonad m => TikiWikiParser m B.Inlines +subMacro = try $ do + string "{SUB(" + manyTill anyChar (string ")}") + body <- manyTill anyChar (string "{SUB}") + return $ B.subscript $ B.text body + +-- -+text+- +code :: PandocMonad m => TikiWikiParser m B.Inlines +code = try $ between (string "-+") (string "+-") nestedString >>= return . B.code . fromEntities + +macroAttr :: PandocMonad m => TikiWikiParser m (String, String) +macroAttr = try $ do + key <- many1 (noneOf "=)") + char '=' + optional $ char '"' + value <- many1 (noneOf " )\"") + optional $ char '"' + return (key, value) + +macroAttrs :: PandocMonad m => TikiWikiParser m [(String, String)] +macroAttrs = try $ do + attrs <- sepEndBy macroAttr spaces + return attrs + +-- ~np~ __not bold__ ~/np~ +noparse :: PandocMonad m => TikiWikiParser m B.Inlines +noparse = try $ do + string "~np~" + body <- manyTill anyChar (string "~/np~") + return $ B.str body + +str :: PandocMonad m => TikiWikiParser m B.Inlines +str = (many1 alphaNum <|> count 1 characterReference) >>= return . B.str + +symbol :: PandocMonad m => TikiWikiParser m B.Inlines +symbol = count 1 nonspaceChar >>= return . B.str + +-- [[not a link] +notExternalLink :: PandocMonad m => TikiWikiParser m B.Inlines +notExternalLink = try $ do + start <- string "[[" + body <- many (noneOf "\n[]") + end <- string "]" + return $ B.text (start ++ body ++ end) + +-- [http://www.somesite.org url|Some Site title] +-- ((internal link)) +-- +-- The ((...)) wiki links and [...] external links are handled +-- exactly the same; this abstracts that out +makeLink :: PandocMonad m => String -> String -> String -> TikiWikiParser m B.Inlines +makeLink start middle end = try $ do + st <- getState + guard $ stateAllowLinks st + setState $ st{ stateAllowLinks = False } + (url, title, anchor) <- wikiLinkText start middle end + parsedTitle <- parseFromString (many1 inline) title + setState $ st{ stateAllowLinks = True } + return $ B.link (url++anchor) "" $ mconcat $ parsedTitle + +wikiLinkText :: PandocMonad m => String -> String -> String -> TikiWikiParser m (String, String, String) +wikiLinkText start middle end = do + string start + url <- many1 (noneOf $ middle ++ "\n") + seg1 <- option url linkContent + seg2 <- option "" linkContent + string end + if seg2 /= "" + then + return (url, seg2, seg1) + else + return (url, seg1, "") + where + linkContent = do + (char '|') + mystr <- many (noneOf middle) + return $ mystr + +externalLink :: PandocMonad m => TikiWikiParser m B.Inlines +externalLink = makeLink "[" "]|" "]" + +-- NB: this wiki linking is unlikely to work for anyone besides me +-- (rlpowell); it happens to work for me because my Hakyll code has +-- post-processing that treats pandoc .md titles as valid link +-- targets, so something like +-- [see also this other post](My Other Page) is perfectly valid. +wikiLink :: PandocMonad m => TikiWikiParser m B.Inlines +wikiLink = makeLink "((" ")|" "))" + -- cgit v1.2.3 From f9309bc46e4bf24b3a1d53663297851394a89a3f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 21 Jul 2017 23:27:54 +0200 Subject: LaTeX reader: improved heuristic for raw block/inline. An unknown command at the beginning of the line that could be either block or inline is treated as block if we have a sequence of block commands followed by a newline or a `\startXXX` command (which might start a raw ConTeXt environment). --- src/Text/Pandoc/Readers/LaTeX.hs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 58a48c655..1215187b7 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1639,9 +1639,25 @@ blockCommand = try $ do star <- option "" ("*" <$ symbol '*' <* optional sp) let name' = name <> star let names = ordNub [name', name] - let raw = do - guard $ isBlockCommand name || not (isInlineCommand name) + let rawDefiniteBlock = do + guard $ isBlockCommand name rawBlock "latex" <$> getRawCommand (txt <> star) + -- heuristic: if it could be either block or inline, we + -- treat it if block if we have a sequence of block + -- commands followed by a newline. But we stop if we + -- hit a \startXXX, since this might start a raw ConTeXt + -- environment (this is important because this parser is + -- used by the Markdown reader). + let startCommand = try $ do + Tok _ (CtrlSeq n) _ <- anyControlSeq + guard $ "start" `T.isPrefixOf` n + let rawMaybeBlock = try $ do + guard $ not $ isInlineCommand name + curr <- rawBlock "latex" <$> getRawCommand (txt <> star) + rest <- many $ notFollowedBy startCommand *> blockCommand + lookAhead $ blankline <|> startCommand + return $ curr <> mconcat rest + let raw = rawDefiniteBlock <|> rawMaybeBlock lookupListDefault raw names blockCommands closing :: PandocMonad m => LP m Blocks -- cgit v1.2.3 From 7d9b782f73edfc49fbe6f0c3d6ce61328811cbc7 Mon Sep 17 00:00:00 2001 From: Mauro Bieg <mb21@users.noreply.github.com> Date: Sat, 22 Jul 2017 19:22:56 +0200 Subject: HTML Reader: parse figure and figcaption (#3813) --- src/Text/Pandoc/Readers/HTML.hs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 734973e33..3a0d6eb14 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -188,6 +188,7 @@ block = do , pBody , pDiv , pPlain + , pFigure , pRawHtmlBlock ] trace (take 60 $ show $ B.toList res) @@ -553,6 +554,25 @@ pPara = do contents <- trimInlines <$> pInTags "p" inline return $ B.para contents +pFigure :: PandocMonad m => TagParser m Blocks +pFigure = do + TagOpen _ _ <- pSatisfy (matchTagOpen "figure" []) + skipMany pBlank + let pImg = pOptInTag "p" pImage <* skipMany pBlank + pCapt = option mempty $ pInTags "figcaption" inline <* skipMany pBlank + pImgCapt = do + img <- pImg + cap <- pCapt + return (img, cap) + pCaptImg = do + cap <- pCapt + img <- pImg + return (img, cap) + (imgMany, caption) <- pImgCapt <|> pCaptImg + TagClose _ <- pSatisfy (matchTagClose "figure") + let (Image attr _ (url, tit)):_ = B.toList imgMany + return $ B.para $ B.imageWith attr url ("fig:" ++ tit) caption + pCodeBlock :: PandocMonad m => TagParser m Blocks pCodeBlock = try $ do TagOpen _ attr' <- pSatisfy (matchTagOpen "pre" []) -- cgit v1.2.3 From d453b3319a14ec592438762f3946cd961d893639 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 23 Jul 2017 19:09:50 +0200 Subject: Refactored some common code in LaTeX reader. --- src/Text/Pandoc/Readers/LaTeX.hs | 52 ++++++++++++---------------------------- 1 file changed, 15 insertions(+), 37 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 1215187b7..183eb89a6 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -44,6 +44,7 @@ module Text.Pandoc.Readers.LaTeX ( readLaTeX, import Control.Applicative (many, optional, (<|>)) import Control.Monad import Control.Monad.Except (throwError) +import Control.Monad.Trans (lift) import Data.Char (chr, isAlphaNum, isLetter, ord, isDigit) import Data.Default import Data.Text (Text) @@ -203,18 +204,22 @@ rawLaTeXBlock :: (PandocMonad m, HasMacros s, HasReaderOptions s) => ParserT String s m String rawLaTeXBlock = do lookAhead (try (char '\\' >> letter)) + rawLaTeXParser (environment <|> macroDef <|> blockCommand) + +rawLaTeXParser :: (PandocMonad m, HasMacros s, HasReaderOptions s) + => LP m a -> ParserT String s m String +rawLaTeXParser parser = do inp <- getInput let toks = tokenize $ T.pack inp - let rawblock = do - (_, raw) <- try $ - withRaw (environment <|> macroDef <|> blockCommand) - return raw pstate <- getState let lstate = def{ sOptions = extractReaderOptions pstate } - res <- runParserT rawblock lstate "source" toks + res <- lift $ runParserT ((,) <$> try (snd <$> withRaw parser) <*> getState) + lstate "source" toks case res of Left _ -> mzero - Right raw -> takeP (T.length (untokenize raw)) + Right (raw, st) -> do + updateState (updateMacros (const $ sMacros st)) + takeP (T.length (untokenize raw)) macro :: (PandocMonad m, HasMacros s, HasReaderOptions s) => ParserT String s m Blocks @@ -222,21 +227,8 @@ macro = do guardEnabled Ext_latex_macros lookAhead (char '\\' *> oneOfStrings ["new", "renew", "provide"] *> oneOfStrings ["command", "environment"]) - inp <- getInput - let toks = tokenize $ T.pack inp - let rawblock = do - (_, raw) <- withRaw $ try macroDef - st <- getState - return (raw, st) - pstate <- getState - let lstate = def{ sOptions = extractReaderOptions pstate - , sMacros = extractMacros pstate } - res <- runParserT rawblock lstate "source" toks - case res of - Left _ -> mzero - Right (raw, st) -> do - updateState (updateMacros (const $ sMacros st)) - mempty <$ takeP (T.length (untokenize raw)) + mempty <$ rawLaTeXParser macroDef + -- since we're applying macros, we remove the defns applyMacros :: (PandocMonad m, HasMacros s, HasReaderOptions s) => String -> ParserT String s m String @@ -252,24 +244,10 @@ applyMacros s = do Right s' -> return s') <|> return s rawLaTeXInline :: (PandocMonad m, HasMacros s, HasReaderOptions s) - => ParserT String s m String + => ParserT String s m String rawLaTeXInline = do lookAhead (try (char '\\' >> letter) <|> char '$') - inp <- getInput - let toks = tokenize $ T.pack inp - let rawinline = do - (_, raw) <- try $ withRaw (inlineEnvironment <|> inlineCommand') - st <- getState - return (raw, st) - pstate <- getState - let lstate = def{ sOptions = extractReaderOptions pstate - , sMacros = extractMacros pstate } - res <- runParserT rawinline lstate "source" toks - case res of - Left _ -> mzero - Right (raw, s) -> do - updateState $ updateMacros (const $ sMacros s) - takeP (T.length (untokenize raw)) + rawLaTeXParser (inlineEnvironment <|> inlineCommand') inlineCommand :: PandocMonad m => ParserT String ParserState m Inlines inlineCommand = do -- cgit v1.2.3 From be14e2b5013a442989a338c13baa4d16881f849e Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 23 Jul 2017 20:46:37 +0200 Subject: LaTeX reader: some improvements in macro parsing. Fixed applyMacros so that it operates on the whole string, not just the first token! Don't remove macro definitions from the output, even if Ext_latex_macros is set, so that macros will be applied. Since they're only applied to math in Markdown, removing the macros can have bad effects. Even for math macros, keeping them should be harmless. --- src/Text/Pandoc/Readers/LaTeX.hs | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 183eb89a6..cc567d5bd 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -200,12 +200,6 @@ withVerbatimMode parser = do updateState $ \st -> st{ sVerbatimMode = False } return result -rawLaTeXBlock :: (PandocMonad m, HasMacros s, HasReaderOptions s) - => ParserT String s m String -rawLaTeXBlock = do - lookAhead (try (char '\\' >> letter)) - rawLaTeXParser (environment <|> macroDef <|> blockCommand) - rawLaTeXParser :: (PandocMonad m, HasMacros s, HasReaderOptions s) => LP m a -> ParserT String s m String rawLaTeXParser parser = do @@ -218,7 +212,7 @@ rawLaTeXParser parser = do case res of Left _ -> mzero Right (raw, st) -> do - updateState (updateMacros (const $ sMacros st)) + updateState (updateMacros ((sMacros st) <>)) takeP (T.length (untokenize raw)) macro :: (PandocMonad m, HasMacros s, HasReaderOptions s) @@ -227,21 +221,26 @@ macro = do guardEnabled Ext_latex_macros lookAhead (char '\\' *> oneOfStrings ["new", "renew", "provide"] *> oneOfStrings ["command", "environment"]) - mempty <$ rawLaTeXParser macroDef - -- since we're applying macros, we remove the defns + rawBlock "latex" <$> rawLaTeXParser macroDef applyMacros :: (PandocMonad m, HasMacros s, HasReaderOptions s) => String -> ParserT String s m String -applyMacros s = do - (guardEnabled Ext_latex_macros >> - do let retokenize = doMacros 0 *> (toksToString <$> getInput) +applyMacros s = (guardDisabled Ext_latex_macros >> return s) <|> + do let retokenize = doMacros 0 *> + (toksToString <$> many (satisfyTok (const True))) pstate <- getState let lstate = def{ sOptions = extractReaderOptions pstate , sMacros = extractMacros pstate } res <- runParserT retokenize lstate "math" (tokenize (T.pack s)) case res of Left e -> fail (show e) - Right s' -> return s') <|> return s + Right s' -> return s' + +rawLaTeXBlock :: (PandocMonad m, HasMacros s, HasReaderOptions s) + => ParserT String s m String +rawLaTeXBlock = do + lookAhead (try (char '\\' >> letter)) + rawLaTeXParser (environment <|> macroDef <|> blockCommand) rawLaTeXInline :: (PandocMonad m, HasMacros s, HasReaderOptions s) => ParserT String s m String @@ -1503,17 +1502,18 @@ authors = try $ do macroDef :: PandocMonad m => LP m Blocks macroDef = do - guardEnabled Ext_latex_macros mempty <$ ((commandDef <|> environmentDef) <* doMacros 0) where commandDef = do (name, macro') <- newcommand - updateState $ \s -> s{ sMacros = M.insert name macro' (sMacros s) } + guardDisabled Ext_latex_macros <|> + updateState (\s -> s{ sMacros = M.insert name macro' (sMacros s) }) environmentDef = do (name, macro1, macro2) <- newenvironment - updateState $ \s -> s{ sMacros = - M.insert name macro1 (sMacros s) } - updateState $ \s -> s{ sMacros = - M.insert ("end" <> name) macro2 (sMacros s) } + guardDisabled Ext_latex_macros <|> + do updateState $ \s -> s{ sMacros = + M.insert name macro1 (sMacros s) } + updateState $ \s -> s{ sMacros = + M.insert ("end" <> name) macro2 (sMacros s) } -- @\newenvironment{envname}[n-args][default]{begin}{end}@ -- is equivalent to -- @\newcommand{\envname}[n-args][default]{begin}@ -- cgit v1.2.3 From 159d31e80ff2430e09b3084c5cea85fae46652a9 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 23 Jul 2017 21:02:58 +0200 Subject: LaTeX reader: Removed 'macro'. It is no longer necessary, since the rawLaTeXBlock parser will parse macro definitions. This also avoids the need for a separate latexMacro parser in the Markdown reader. --- src/Text/Pandoc/Readers/LaTeX.hs | 1 - src/Text/Pandoc/Readers/Markdown.hs | 12 ++---------- 2 files changed, 2 insertions(+), 11 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index cc567d5bd..b0f8d536e 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -37,7 +37,6 @@ module Text.Pandoc.Readers.LaTeX ( readLaTeX, applyMacros, rawLaTeXInline, rawLaTeXBlock, - macro, inlineCommand ) where diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index ab6a32b78..d7e59c7fd 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -61,8 +61,8 @@ import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (tableWith) import Text.Pandoc.Readers.HTML (htmlInBalanced, htmlTag, isBlockTag, isCommentTag, isInlineTag, isTextTag) -import Text.Pandoc.Readers.LaTeX (rawLaTeXBlock, rawLaTeXInline, applyMacros, - macro) +import Text.Pandoc.Readers.LaTeX (rawLaTeXBlock, + rawLaTeXInline, applyMacros) import Text.Pandoc.Shared import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.XML (fromEntities) @@ -507,7 +507,6 @@ block = do , htmlBlock , table , codeBlockIndented - , latexMacro , rawTeXBlock , lineBlock , blockQuote @@ -1096,13 +1095,6 @@ rawVerbatimBlock = htmlInBalanced isVerbTag isVerbTag (TagOpen "script" _) = True isVerbTag _ = False -latexMacro :: PandocMonad m => MarkdownParser m (F Blocks) -latexMacro = try $ do - guardEnabled Ext_latex_macros - skipNonindentSpaces - res <- macro - return $ return res - rawTeXBlock :: PandocMonad m => MarkdownParser m (F Blocks) rawTeXBlock = do guardEnabled Ext_raw_tex -- cgit v1.2.3 From e7876d43203a4672e9d5bb10f91d1530272abc7b Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 24 Jul 2017 00:25:35 +0200 Subject: LaTeX reader: removed code for non-exported 'macro'. --- src/Text/Pandoc/Readers/LaTeX.hs | 8 -------- 1 file changed, 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index b0f8d536e..44be8dea3 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -214,14 +214,6 @@ rawLaTeXParser parser = do updateState (updateMacros ((sMacros st) <>)) takeP (T.length (untokenize raw)) -macro :: (PandocMonad m, HasMacros s, HasReaderOptions s) - => ParserT String s m Blocks -macro = do - guardEnabled Ext_latex_macros - lookAhead (char '\\' *> oneOfStrings ["new", "renew", "provide"] *> - oneOfStrings ["command", "environment"]) - rawBlock "latex" <$> rawLaTeXParser macroDef - applyMacros :: (PandocMonad m, HasMacros s, HasReaderOptions s) => String -> ParserT String s m String applyMacros s = (guardDisabled Ext_latex_macros >> return s) <|> -- cgit v1.2.3 From 329b61ff5cbad6297d1ceea5da7f045613e27ec5 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 24 Jul 2017 11:16:43 +0200 Subject: LaTeX reader: support etoolbox's ifstrequal. --- src/Text/Pandoc/Readers/LaTeX.hs | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 44be8dea3..494f532a1 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1316,8 +1316,21 @@ inlineCommands = M.fromList $ , ("faClose", lit "\10007") -- xspace , ("xspace", doxspace) + -- etoolbox + , ("ifstrequal", ifstrequal) ] +ifstrequal :: PandocMonad m => LP m Inlines +ifstrequal = do + str1 <- tok + str2 <- tok + ifequal <- braced + ifnotequal <- braced + if str1 == str2 + then getInput >>= setInput . (ifequal ++) + else getInput >>= setInput . (ifnotequal ++) + return mempty + ttfamily :: PandocMonad m => LP m Inlines ttfamily = (code . stringify . toList) <$> tok -- cgit v1.2.3 From d441e656db576f266c4866e65ff9e4705d376381 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 25 Jul 2017 13:13:24 +0200 Subject: HTML writer: insert data- in front of unsupported attributes. Thus, a span with attribute 'foo' gets written to HTML5 with 'data-foo', so it is valid HTML5. HTML4 is not affected. This will allow us to use custom attributes in pandoc without producing invalid HTML. --- src/Text/Pandoc/Writers/HTML.hs | 357 ++++++++++++++++++++++++++++++++++------ 1 file changed, 309 insertions(+), 48 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 451123a6d..d09158c42 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -50,6 +50,7 @@ import qualified Data.Text.Lazy as TL import Data.List (intersperse, isPrefixOf) import Data.Maybe (catMaybes, fromMaybe, isJust, isNothing) import Data.Monoid ((<>)) +import qualified Data.Set as Set import Data.String (fromString) import Network.HTTP (urlEncode) import Network.URI (URI (..), parseURIReference, unEscapeString) @@ -434,16 +435,19 @@ elementToHtml slideLevel opts (Sec level num (id',classes,keyvals) title' elemen then H5.section else H.div let attr = (id',classes',keyvals) - return $ if titleSlide - then (if slideVariant == RevealJsSlides - then H5.section - else id) $ mconcat $ - (addAttrs opts attr $ secttag $ header') : innerContents - else if writerSectionDivs opts || slide - then addAttrs opts attr - $ secttag $ inNl $ header' : innerContents - else mconcat $ intersperse (nl opts) - $ addAttrs opts attr header' : innerContents + if titleSlide + then do + t <- addAttrs opts attr $ secttag $ header' + return $ + (if slideVariant == RevealJsSlides + then H5.section + else id) $ mconcat $ t : innerContents + else if writerSectionDivs opts || slide + then addAttrs opts attr + $ secttag $ inNl $ header' : innerContents + else do + t <- addAttrs opts attr header' + return $ mconcat $ intersperse (nl opts) (t : innerContents) -- | Convert list of Note blocks to a footnote <div>. -- Assumes notes are sorted. @@ -476,9 +480,11 @@ parseMailto s = do _ -> fail "not a mailto: URL" -- | Obfuscate a "mailto:" link. -obfuscateLink :: PandocMonad m => WriterOptions -> Attr -> Html -> String -> m Html +obfuscateLink :: PandocMonad m + => WriterOptions -> Attr -> Html -> String + -> StateT WriterState m Html obfuscateLink opts attr txt s | writerEmailObfuscation opts == NoObfuscation = - return $ addAttrs opts attr $ H.a ! A.href (toValue s) $ txt + addAttrs opts attr $ H.a ! A.href (toValue s) $ txt obfuscateLink opts attr (TL.unpack . renderHtml -> txt) s = let meth = writerEmailObfuscation opts s' = map toLower (take 7 s) ++ drop 7 s @@ -510,7 +516,7 @@ obfuscateLink opts attr (TL.unpack . renderHtml -> txt) s = linkText ++ "+'<\\/'+'a'+'>');\n// -->\n")) >> H.noscript (preEscapedString $ obfuscateString altText) _ -> throwError $ PandocSomeError $ "Unknown obfuscation method: " ++ show meth - _ -> return $ addAttrs opts attr $ H.a ! A.href (toValue s) $ toHtml txt -- malformed email + _ -> addAttrs opts attr $ H.a ! A.href (toValue s) $ toHtml txt -- malformed email -- | Obfuscate character as entity. obfuscateChar :: Char -> String @@ -523,21 +529,34 @@ obfuscateChar char = obfuscateString :: String -> String obfuscateString = concatMap obfuscateChar . fromEntities -addAttrs :: WriterOptions -> Attr -> Html -> Html -addAttrs opts attr h = foldl (!) h (attrsToHtml opts attr) +addAttrs :: PandocMonad m + => WriterOptions -> Attr -> Html -> StateT WriterState m Html +addAttrs opts attr h = foldl (!) h <$> attrsToHtml opts attr -toAttrs :: [(String, String)] -> [Attribute] -toAttrs kvs = map (\(x,y) -> customAttribute (fromString x) (toValue y)) kvs - -attrsToHtml :: WriterOptions -> Attr -> [Attribute] -attrsToHtml opts (id',classes',keyvals) = - [prefixedId opts id' | not (null id')] ++ - [A.class_ (toValue $ unwords classes') | not (null classes')] ++ toAttrs keyvals - -imgAttrsToHtml :: WriterOptions -> Attr -> [Attribute] -imgAttrsToHtml opts attr = - attrsToHtml opts (ident,cls,kvs') ++ - toAttrs (dimensionsToAttrList attr) +toAttrs :: PandocMonad m + => [(String, String)] -> StateT WriterState m [Attribute] +toAttrs kvs = do + html5 <- gets stHtml5 + return $ map (\(x,y) -> + customAttribute + (fromString (if not html5 || x `Set.member` html5Attributes + then x + else "data-" ++ x)) (toValue y)) kvs + +attrsToHtml :: PandocMonad m + => WriterOptions -> Attr -> StateT WriterState m [Attribute] +attrsToHtml opts (id',classes',keyvals) = do + attrs <- toAttrs keyvals + return $ + [prefixedId opts id' | not (null id')] ++ + [A.class_ (toValue $ unwords classes') | not (null classes')] ++ attrs + +imgAttrsToHtml :: PandocMonad m + => WriterOptions -> Attr -> StateT WriterState m [Attribute] +imgAttrsToHtml opts attr = do + attrs <- attrsToHtml opts (ident,cls,kvs') + dimattrs <- toAttrs (dimensionsToAttrList attr) + return $ attrs ++ dimattrs where (ident,cls,kvs) = attr kvs' = filter isNotDim kvs @@ -628,15 +647,15 @@ blockToHtml opts (Div attr@(ident, classes, kvs) bs) = do then (H5.section, filter (/= "section") classes) else (H.div, classes) slideVariant <- gets stSlideVariant - return $ - if speakerNotes - then case slideVariant of - RevealJsSlides -> addAttrs opts' attr $ H5.aside $ contents' - DZSlides -> (addAttrs opts' attr $ H5.div $ contents') - ! (H5.customAttribute "role" "note") - NoSlides -> addAttrs opts' attr $ H.div $ contents' - _ -> mempty - else addAttrs opts (ident, classes', kvs) $ divtag $ contents' + if speakerNotes + then case slideVariant of + RevealJsSlides -> addAttrs opts' attr $ H5.aside $ contents' + DZSlides -> do + t <- addAttrs opts' attr $ H5.div $ contents' + return $ t ! (H5.customAttribute "role" "note") + NoSlides -> addAttrs opts' attr $ H.div $ contents' + _ -> return mempty + else addAttrs opts (ident, classes', kvs) $ divtag $ contents' blockToHtml opts (RawBlock f str) = do ishtml <- isRawHtml f if ishtml @@ -671,10 +690,10 @@ blockToHtml opts (CodeBlock (id',classes,keyvals) rawCode) = do Left msg -> do unless (null msg) $ report $ CouldNotHighlight msg - return $ addAttrs opts (id',classes,keyvals) - $ H.pre $ H.code $ toHtml adjCode + addAttrs opts (id',classes,keyvals) + $ H.pre $ H.code $ toHtml adjCode Right h -> modify (\st -> st{ stHighlighting = True }) >> - return (addAttrs opts (id',[],keyvals) h) + addAttrs opts (id',[],keyvals) h blockToHtml opts (BlockQuote blocks) = do -- in S5, treat list in blockquote specially -- if default is incremental, make it nonincremental; @@ -706,7 +725,7 @@ blockToHtml opts (Header level attr@(_,classes,_) lst) = do $ showSecNum secnum) >> strToHtml " " >> contents else contents inElement <- gets stElement - return $ (if inElement then id else addAttrs opts attr) + (if inElement then return else addAttrs opts attr) $ case level of 1 -> H.h1 contents' 2 -> H.h2 contents' @@ -880,7 +899,7 @@ inlineToHtml opts inline = do <> strToHtml "\n" (Span (id',classes,kvs) ils) -> inlineListToHtml opts ils >>= - return . addAttrs opts attr' . H.span + addAttrs opts attr' . H.span where attr' = (id',classes',kvs') classes' = filter (`notElem` ["csl-no-emph", "csl-no-strong", @@ -900,11 +919,10 @@ inlineToHtml opts inline = do Left msg -> do unless (null msg) $ report $ CouldNotHighlight msg - return $ addAttrs opts attr - $ H.code $ strToHtml str + addAttrs opts attr $ H.code $ strToHtml str Right h -> do modify $ \st -> st{ stHighlighting = True } - return $ addAttrs opts (id',[],keyvals) h + addAttrs opts (id',[],keyvals) h where (id',_,keyvals) = attr hlCode = if isJust (writerHighlightStyle opts) then highlight @@ -994,7 +1012,7 @@ inlineToHtml opts inline = do return mempty (Link attr txt (s,_)) | "mailto:" `isPrefixOf` s -> do linkText <- inlineListToHtml opts txt - lift $ obfuscateLink opts attr linkText s + obfuscateLink opts attr linkText s (Link (ident,classes,kvs) txt (s,tit)) -> do linkText <- inlineListToHtml opts txt slideVariant <- gets stSlideVariant @@ -1008,7 +1026,7 @@ inlineToHtml opts inline = do let attr = if txt == [Str (unEscapeString s)] then (ident, "uri" : classes, kvs) else (ident, classes, kvs) - let link' = addAttrs opts attr link + link' <- addAttrs opts attr link return $ if null tit then link' else link' ! A.title (toValue tit) @@ -1016,6 +1034,7 @@ inlineToHtml opts inline = do let alternate' = stringify txt slideVariant <- gets stSlideVariant let isReveal = slideVariant == RevealJsSlides + attrs <- imgAttrsToHtml opts attr let attributes = -- reveal.js uses data-src for lazy loading (if isReveal @@ -1023,19 +1042,20 @@ inlineToHtml opts inline = do else A.src $ toValue s) : [A.title $ toValue tit | not (null tit)] ++ [A.alt $ toValue alternate' | not (null txt)] ++ - imgAttrsToHtml opts attr + attrs 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 slideVariant <- gets stSlideVariant let isReveal = slideVariant == RevealJsSlides + attrs <- imgAttrsToHtml opts attr let attributes = (if isReveal then customAttribute "data-src" $ toValue s else A.src $ toValue s) : [A.title $ toValue tit | not (null tit)] ++ - imgAttrsToHtml opts attr + attrs return $ foldl (!) H5.embed attributes -- note: null title included, as in Markdown.pl (Note contents) -> do @@ -1145,3 +1165,244 @@ isRawHtml f = do html5 <- gets stHtml5 return $ f == Format "html" || ((html5 && f == Format "html5") || f == Format "html4") + +html5Attributes :: Set.Set String +html5Attributes = Set.fromList + [ "abbr" + , "accept" + , "accept-charset" + , "accesskey" + , "action" + , "allowfullscreen" + , "allowpaymentrequest" + , "allowusermedia" + , "alt" + , "as" + , "async" + , "autocomplete" + , "autocomplete" + , "autofocus" + , "autoplay" + , "charset" + , "charset" + , "checked" + , "cite" + , "class" + , "color" + , "cols" + , "colspan" + , "content" + , "contenteditable" + , "controls" + , "coords" + , "crossorigin" + , "data" + , "datetime" + , "datetime" + , "default" + , "defer" + , "dir" + , "dir" + , "dirname" + , "disabled" + , "download" + , "draggable" + , "enctype" + , "for" + , "for" + , "form" + , "formaction" + , "formenctype" + , "formmethod" + , "formnovalidate" + , "formtarget" + , "headers" + , "height" + , "hidden" + , "high" + , "href" + , "href" + , "href" + , "hreflang" + , "http-equiv" + , "id" + , "inputmode" + , "integrity" + , "is" + , "ismap" + , "itemid" + , "itemprop" + , "itemref" + , "itemscope" + , "itemtype" + , "kind" + , "label" + , "lang" + , "list" + , "loop" + , "low" + , "manifest" + , "max" + , "max" + , "maxlength" + , "media" + , "method" + , "min" + , "min" + , "minlength" + , "multiple" + , "muted" + , "name" + , "name" + , "name" + , "name" + , "name" + , "name" + , "name" + , "nomodule" + , "nonce" + , "novalidate" + , "open" + , "open" + , "optimum" + , "pattern" + , "ping" + , "placeholder" + , "playsinline" + , "poster" + , "preload" + , "readonly" + , "referrerpolicy" + , "rel" + , "rel" + , "required" + , "reversed" + , "rows" + , "rowspan" + , "sandbox" + , "scope" + , "scope" + , "selected" + , "shape" + , "size" + , "sizes" + , "sizes" + , "slot" + , "span" + , "spellcheck" + , "src" + , "srcdoc" + , "srclang" + , "srcset" + , "start" + , "step" + , "style" + , "tabindex" + , "target" + , "target" + , "target" + , "title" + , "title" + , "title" + , "title" + , "title" + , "translate" + , "type" + , "type" + , "type" + , "type" + , "type" + , "typemustmatch" + , "updateviacache" + , "usemap" + , "value" + , "value" + , "value" + , "value" + , "value" + , "value" + , "width" + , "workertype" + , "wrap" + , "onabort" + , "onauxclick" + , "onafterprint" + , "onbeforeprint" + , "onbeforeunload" + , "onblur" + , "oncancel" + , "oncanplay" + , "oncanplaythrough" + , "onchange" + , "onclick" + , "onclose" + , "oncontextmenu" + , "oncopy" + , "oncuechange" + , "oncut" + , "ondblclick" + , "ondrag" + , "ondragend" + , "ondragenter" + , "ondragexit" + , "ondragleave" + , "ondragover" + , "ondragstart" + , "ondrop" + , "ondurationchange" + , "onemptied" + , "onended" + , "onerror" + , "onfocus" + , "onhashchange" + , "oninput" + , "oninvalid" + , "onkeydown" + , "onkeypress" + , "onkeyup" + , "onlanguagechange" + , "onload" + , "onloadeddata" + , "onloadedmetadata" + , "onloadend" + , "onloadstart" + , "onmessage" + , "onmessageerror" + , "onmousedown" + , "onmouseenter" + , "onmouseleave" + , "onmousemove" + , "onmouseout" + , "onmouseover" + , "onmouseup" + , "onwheel" + , "onoffline" + , "ononline" + , "onpagehide" + , "onpageshow" + , "onpaste" + , "onpause" + , "onplay" + , "onplaying" + , "onpopstate" + , "onprogress" + , "onratechange" + , "onreset" + , "onresize" + , "onrejectionhandled" + , "onscroll" + , "onsecuritypolicyviolation" + , "onseeked" + , "onseeking" + , "onselect" + , "onstalled" + , "onstorage" + , "onsubmit" + , "onsuspend" + , "ontimeupdate" + , "ontoggle" + , "onunhandledrejection" + , "onunload" + , "onvolumechange" + , "onwaiting" + ] -- cgit v1.2.3 From e0ab09611a8ab42c69da81ed3fd3c3df8c0c70de Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 26 Jul 2017 12:50:36 +0200 Subject: HTML writer: render raw inline environments when --mathjax used. We previously did this only with raw blocks, on the assumption that math environments would always be raw blocks. This has changed since we now parse them as inline environments. Closes #3816. --- src/Text/Pandoc/Writers/HTML.hs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index d09158c42..61f2c959a 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -1007,9 +1007,14 @@ inlineToHtml opts inline = do ishtml <- isRawHtml f if ishtml then return $ preEscapedString str - else do - report $ InlineNotRendered inline - return mempty + else if (f == Format "latex" || f == Format "tex") && + "\\begin" `isPrefixOf` str && + allowsMathEnvironments (writerHTMLMathMethod opts) && + isMathEnvironment str + then inlineToHtml opts $ Math DisplayMath str + else do + report $ InlineNotRendered inline + return mempty (Link attr txt (s,_)) | "mailto:" `isPrefixOf` s -> do linkText <- inlineListToHtml opts txt obfuscateLink opts attr linkText s -- cgit v1.2.3 From 200b5fb60ce1372da592dafd0a2f587f0083d72f Mon Sep 17 00:00:00 2001 From: Wandmalfarbe <Wandmalfarbe@users.noreply.github.com> Date: Wed, 26 Jul 2017 18:50:10 +0200 Subject: Sorted the list of supported HTML5 attributes and removed duplicates. (#3817) --- src/Text/Pandoc/Writers/HTML.hs | 126 +++++++++++++++------------------------- 1 file changed, 46 insertions(+), 80 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 61f2c959a..fecb32464 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -1185,11 +1185,9 @@ html5Attributes = Set.fromList , "as" , "async" , "autocomplete" - , "autocomplete" , "autofocus" , "autoplay" , "charset" - , "charset" , "checked" , "cite" , "class" @@ -1203,18 +1201,15 @@ html5Attributes = Set.fromList , "crossorigin" , "data" , "datetime" - , "datetime" , "default" , "defer" , "dir" - , "dir" , "dirname" , "disabled" , "download" , "draggable" , "enctype" , "for" - , "for" , "form" , "formaction" , "formenctype" @@ -1226,8 +1221,6 @@ html5Attributes = Set.fromList , "hidden" , "high" , "href" - , "href" - , "href" , "hreflang" , "http-equiv" , "id" @@ -1248,90 +1241,20 @@ html5Attributes = Set.fromList , "low" , "manifest" , "max" - , "max" , "maxlength" , "media" , "method" , "min" - , "min" , "minlength" , "multiple" , "muted" , "name" - , "name" - , "name" - , "name" - , "name" - , "name" - , "name" , "nomodule" , "nonce" , "novalidate" - , "open" - , "open" - , "optimum" - , "pattern" - , "ping" - , "placeholder" - , "playsinline" - , "poster" - , "preload" - , "readonly" - , "referrerpolicy" - , "rel" - , "rel" - , "required" - , "reversed" - , "rows" - , "rowspan" - , "sandbox" - , "scope" - , "scope" - , "selected" - , "shape" - , "size" - , "sizes" - , "sizes" - , "slot" - , "span" - , "spellcheck" - , "src" - , "srcdoc" - , "srclang" - , "srcset" - , "start" - , "step" - , "style" - , "tabindex" - , "target" - , "target" - , "target" - , "title" - , "title" - , "title" - , "title" - , "title" - , "translate" - , "type" - , "type" - , "type" - , "type" - , "type" - , "typemustmatch" - , "updateviacache" - , "usemap" - , "value" - , "value" - , "value" - , "value" - , "value" - , "value" - , "width" - , "workertype" - , "wrap" , "onabort" - , "onauxclick" , "onafterprint" + , "onauxclick" , "onbeforeprint" , "onbeforeunload" , "onblur" @@ -1380,7 +1303,6 @@ html5Attributes = Set.fromList , "onmouseout" , "onmouseover" , "onmouseup" - , "onwheel" , "onoffline" , "ononline" , "onpagehide" @@ -1392,9 +1314,9 @@ html5Attributes = Set.fromList , "onpopstate" , "onprogress" , "onratechange" + , "onrejectionhandled" , "onreset" , "onresize" - , "onrejectionhandled" , "onscroll" , "onsecuritypolicyviolation" , "onseeked" @@ -1410,4 +1332,48 @@ html5Attributes = Set.fromList , "onunload" , "onvolumechange" , "onwaiting" + , "onwheel" + , "open" + , "optimum" + , "pattern" + , "ping" + , "placeholder" + , "playsinline" + , "poster" + , "preload" + , "readonly" + , "referrerpolicy" + , "rel" + , "required" + , "reversed" + , "rows" + , "rowspan" + , "sandbox" + , "scope" + , "selected" + , "shape" + , "size" + , "sizes" + , "slot" + , "span" + , "spellcheck" + , "src" + , "srcdoc" + , "srclang" + , "srcset" + , "start" + , "step" + , "style" + , "tabindex" + , "target" + , "title" + , "translate" + , "type" + , "typemustmatch" + , "updateviacache" + , "usemap" + , "value" + , "width" + , "workertype" + , "wrap" ] -- cgit v1.2.3 From 7f9e950d8da52dd2333843d7fd85d000c4a1cbe3 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 29 Jul 2017 20:54:25 +0200 Subject: Class: Removed unnecessary withMedia, improved haddocks. --- src/Text/Pandoc/App.hs | 5 +++-- src/Text/Pandoc/Class.hs | 29 ++++++++++++++++++++++------- 2 files changed, 25 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 0d4a82b70..498cfae22 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -76,7 +76,7 @@ import System.IO.Error (isDoesNotExistError) import Text.Pandoc import Text.Pandoc.Builder (setMeta) import Text.Pandoc.Class (PandocIO, extractMedia, fillMediaBag, getLog, - setResourcePath, withMediaBag, setTrace) + setResourcePath, getMediaBag, setTrace) import Text.Pandoc.Highlighting (highlightingStyles) import Text.Pandoc.Lua (runLuaFilter, LuaException(..)) import Text.Pandoc.Writers.Math (defaultMathJaxURL, defaultKaTeXURL) @@ -445,7 +445,7 @@ convertWithOpts opts = do runIO' $ do setResourcePath (optResourcePath opts) - (doc, media) <- withMediaBag $ sourceToDoc sources >>= + doc <- sourceToDoc sources >>= ( (if isJust (optExtractMedia opts) then fillMediaBag (writerSourceURL writerOptions) else return) @@ -455,6 +455,7 @@ convertWithOpts opts = do >=> applyLuaFilters datadir (optLuaFilters opts) [format] >=> applyFilters datadir filters' [format] ) + media <- getMediaBag case writer of ByteStringWriter f -> f writerOptions doc >>= writeFnBinary outputFile diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 46e300953..df6da5a68 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -32,7 +32,11 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Stability : alpha Portability : portable -Typeclass for pandoc readers and writers, allowing both IO and pure instances. +This module defines a type class, 'PandocMonad', for pandoc readers +and writers. A pure instance 'PandocPure' and an impure instance +'PandocIO' are provided. This allows users of the library to choose +whether they want conversions to perform IO operations (such as +reading include files or images). -} module Text.Pandoc.Class ( PandocMonad(..) @@ -65,7 +69,6 @@ module Text.Pandoc.Class ( PandocMonad(..) , runIO , runIOorExplode , runPure - , withMediaBag , fillMediaBag , extractMedia ) where @@ -173,8 +176,9 @@ class (Functor m, Applicative m, Monad m, MonadError PandocError m) modifyCommonState f = getCommonState >>= putCommonState . f -- Output a log message. logOutput :: LogMessage -> m () - -- Output a debug message to sterr, using 'Debug.Trace.trace'. - -- Note: this writes to stderr even in pure instances. + -- Output a debug message to sterr, using 'Debug.Trace.trace', + -- if tracing is enabled. Note: this writes to stderr even in + -- pure instances. trace :: String -> m () trace msg = do tracing <- getsCommonState stTrace @@ -241,13 +245,26 @@ readFileFromDirs (d:ds) f = catchError -- +-- | 'CommonState' represents state that is used by all +-- instances of 'PandocMonad'. Normally users should not +-- need to interact with it directly; instead, auxiliary +-- functions like 'setVerbosity' and 'withMediaBag' should be used. data CommonState = CommonState { stLog :: [LogMessage] + -- ^ A list of log messages in reverse order , stMediaBag :: MediaBag + -- ^ Media parsed from binary containers , stInputFiles :: Maybe [FilePath] + -- ^ List of input files from command line , stOutputFile :: Maybe FilePath + -- ^ Output file from command line , stResourcePath :: [FilePath] + -- ^ Path to search for resources like + -- included images , stVerbosity :: Verbosity + -- ^ Verbosity level , stTrace :: Bool + -- ^ Controls whether tracing messages are + -- issued. } instance Default CommonState where @@ -260,12 +277,10 @@ instance Default CommonState where , stTrace = False } +-- | Evaluate a 'PandocIO' operation. runIO :: PandocIO a -> IO (Either PandocError a) runIO ma = flip evalStateT def $ runExceptT $ unPandocIO ma -withMediaBag :: PandocMonad m => m a -> m (a, MediaBag) -withMediaBag ma = (,) <$> ma <*> getMediaBag - runIOorExplode :: PandocIO a -> IO a runIOorExplode ma = runIO ma >>= handleError -- cgit v1.2.3 From 8cbc28415ef7246e9eb5b74d8958f1ab0dc01d4d Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 29 Jul 2017 21:04:25 +0200 Subject: Class: more haddocks. --- src/Text/Pandoc/Class.hs | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index df6da5a68..a8db05e5b 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -186,13 +186,21 @@ class (Functor m, Applicative m, Monad m, MonadError PandocError m) -- * Functions defined for all PandocMonad instances +-- | Set the verbosity level. setVerbosity :: PandocMonad m => Verbosity -> m () setVerbosity verbosity = modifyCommonState $ \st -> st{ stVerbosity = verbosity } +-- Get the accomulated log messages (in temporal order). getLog :: PandocMonad m => m [LogMessage] getLog = reverse <$> getsCommonState stLog +-- | Log a message using 'logOutput'. Note that +-- 'logOutput' is called only if the verbosity +-- level exceeds the level of the message, but +-- the message is added to the list of log messages +-- that will be retrieved by 'getLog' regardless +-- of its verbosity level. report :: PandocMonad m => LogMessage -> m () report msg = do verbosity <- getsCommonState stVerbosity @@ -200,9 +208,13 @@ report msg = do when (level <= verbosity) $ logOutput msg modifyCommonState $ \st -> st{ stLog = msg : stLog st } +-- | Determine whether tracing is enabled. This affects +-- the behavior of 'trace'. If tracing is not enabled, +-- 'trace' does nothing. setTrace :: PandocMonad m => Bool -> m () setTrace useTracing = modifyCommonState $ \st -> st{stTrace = useTracing} +-- | Initialize the media bag. setMediaBag :: PandocMonad m => MediaBag -> m () setMediaBag mb = modifyCommonState $ \st -> st{stMediaBag = mb} -- cgit v1.2.3 From b8afec05e0ce85ab811bf55d4948be768ad363e2 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 30 Jul 2017 13:45:22 -0700 Subject: Markdown writer: better escaping of `<` and `>`. If `all_symbols_escapable` is set, we backslash escape these. Otherwise we use entities as before. --- src/Text/Pandoc/Writers/Markdown.hs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 1e0d8bde2..837c177f1 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -280,8 +280,12 @@ escapeString :: WriterOptions -> String -> String escapeString _ [] = [] escapeString opts (c:cs) = case c of - '<' -> "<" ++ escapeString opts cs - '>' -> ">" ++ escapeString opts cs + '<' | isEnabled Ext_all_symbols_escapable opts -> + '\\' : '<' : escapeString opts cs + | otherwise -> "<" ++ escapeString opts cs + '>' | isEnabled Ext_all_symbols_escapable opts -> + '\\' : '>' : escapeString opts cs + | otherwise -> ">" ++ escapeString opts cs _ | c `elem` ['\\','`','*','_','[',']','#'] -> '\\':c:escapeString opts cs '^' | isEnabled Ext_superscript opts -> '\\':'^':escapeString opts cs -- cgit v1.2.3 From ced834076d8f4463c60a4f739a3d92a56d3e2183 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 2 Aug 2017 10:33:08 -0700 Subject: DokuWiki reader: better handling for code block in list item. Closes #3824. --- src/Text/Pandoc/Writers/DokuWiki.hs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/DokuWiki.hs b/src/Text/Pandoc/Writers/DokuWiki.hs index ad8689e8c..279475a21 100644 --- a/src/Text/Pandoc/Writers/DokuWiki.hs +++ b/src/Text/Pandoc/Writers/DokuWiki.hs @@ -282,11 +282,16 @@ listAttribsToString (startnum, numstyle, _) = listItemToDokuWiki :: PandocMonad m => WriterOptions -> [Block] -> DokuWiki m String listItemToDokuWiki opts items = do - contents <- blockListToDokuWiki opts items useTags <- stUseTags <$> ask if useTags - then return $ "<HTML><li></HTML>" ++ contents ++ "<HTML></li></HTML>" + then do + contents <- blockListToDokuWiki opts items + return $ "<HTML><li></HTML>" ++ contents ++ "<HTML></li></HTML>" else do + bs <- mapM (blockToDokuWiki opts) items + let contents = case items of + [_, CodeBlock _ _] -> concat bs + _ -> vcat bs indent <- stIndent <$> ask backSlash <- stBackSlashLB <$> ask let indent' = if backSlash then (drop 2 indent) else indent @@ -351,6 +356,7 @@ isSimpleListItem [x, y] | isPlainOrPara x = BulletList _ -> isSimpleList y OrderedList _ _ -> isSimpleList y DefinitionList _ -> isSimpleList y + CodeBlock _ _ -> True _ -> False isSimpleListItem _ = False -- cgit v1.2.3 From 38b6adaac09d63501d2febb034dcd776b6de1654 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Thu, 3 Aug 2017 19:11:00 +0300 Subject: Muse reader: do not parse blocks inside comments (#3828) --- src/Text/Pandoc/Readers/Muse.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 9d967a9de..537596216 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -247,7 +247,7 @@ quoteTag :: PandocMonad m => MuseParser m (F Blocks) quoteTag = blockTag B.blockQuote "quote" commentTag :: PandocMonad m => MuseParser m (F Blocks) -commentTag = parseHtmlContent "comment" block >> return mempty +commentTag = parseHtmlContent "comment" anyChar >> return mempty -- Indented block is either center, right or quote indentedLine :: PandocMonad m => MuseParser m (Int, String) -- cgit v1.2.3 From 7a3a8790de175c9a21d13786214fdea4bb157b3d Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 4 Aug 2017 01:41:45 +0300 Subject: Muse reader: do not allow headers in blockquotes (#3831) --- src/Text/Pandoc/Readers/Muse.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 537596216..ca40cebe3 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -244,7 +244,7 @@ rightTag :: PandocMonad m => MuseParser m (F Blocks) rightTag = blockTag id "right" quoteTag :: PandocMonad m => MuseParser m (F Blocks) -quoteTag = blockTag B.blockQuote "quote" +quoteTag = withQuoteContext InDoubleQuote $ blockTag B.blockQuote "quote" commentTag :: PandocMonad m => MuseParser m (F Blocks) commentTag = parseHtmlContent "comment" anyChar >> return mempty -- cgit v1.2.3 From 685788cd4b13d2162dc011e8f3826da37522e59b Mon Sep 17 00:00:00 2001 From: bucklereed <horridimpfoobarbaz@chammy.info> Date: Sat, 5 Aug 2017 18:03:31 +0100 Subject: LaTeX reader: plainbreak, fancybreak et al from the memoir class (#3833) --- src/Text/Pandoc/Readers/LaTeX.hs | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 5877bbbe1..b06cd7348 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1701,6 +1701,15 @@ blockCommands = M.fromList $ -- letters , ("opening", (para . trimInlines) <$> (skipopts *> tok)) , ("closing", skipopts *> closing) + -- memoir + , ("plainbreak", braced >> pure horizontalRule) + , ("plainbreak*", braced >> pure horizontalRule) + , ("fancybreak", braced >> pure horizontalRule) + , ("fancybreak*", braced >> pure horizontalRule) + , ("plainfancybreak", braced >> braced >> braced >> pure horizontalRule) + , ("plainfancybreak*", braced >> braced >> braced >> pure horizontalRule) + , ("pfbreak", pure horizontalRule) + , ("pfbreak*", pure horizontalRule) -- , ("hrule", pure horizontalRule) , ("strut", pure mempty) -- cgit v1.2.3 From 8164a005c0ac9e36d9d1485fcf38e9073a1bcd68 Mon Sep 17 00:00:00 2001 From: Alexander <ilabdsf@gmail.com> Date: Sun, 6 Aug 2017 23:19:59 +0300 Subject: Muse reader: debug list and list item separation rules (#3837) --- src/Text/Pandoc/Readers/Muse.hs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index ca40cebe3..201a59fc0 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -302,7 +302,6 @@ noteBlock = try $ do listLine :: PandocMonad m => Int -> MuseParser m String listLine markerLength = try $ do - notFollowedBy blankline indentWith markerLength anyLineNewline @@ -317,9 +316,9 @@ withListContext p = do listContinuation :: PandocMonad m => Int -> MuseParser m String listContinuation markerLength = try $ do - blanks <- many1 blankline result <- many1 $ listLine markerLength - return $ blanks ++ concat result + blank <- option "" ("\n" <$ blankline) + return $ concat result ++ blank listStart :: PandocMonad m => MuseParser m Int -> MuseParser m Int listStart marker = try $ do @@ -334,9 +333,9 @@ listItem :: PandocMonad m => MuseParser m Int -> MuseParser m (F Blocks) listItem start = try $ do markerLength <- start firstLine <- anyLineNewline - blank <- option "" ("\n" <$ blankline) restLines <- many $ listLine markerLength - let first = firstLine ++ blank ++ concat restLines + blank <- option "" ("\n" <$ blankline) + let first = firstLine ++ concat restLines ++ blank rest <- many $ listContinuation markerLength parseFromString (withListContext parseBlocks) $ concat (first:rest) ++ "\n" -- cgit v1.2.3 From a36a56b8ac5fea612c1d0614d4e1cb14ffc3a21b Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sun, 6 Aug 2017 19:26:50 -0700 Subject: Docx reader: Avoid 0-level headers. We used to parse paragraphs styled with "HeadingN" as "nth-level header." But if a document has a custom style named "Heading0", this will produce a 0-level header, which shouldn't exist. We only parse this style if N>0. Otherwise we treat it as a normal style name, and follow its dependencies, if any. Closes #3830. --- src/Text/Pandoc/Readers/Docx/Parse.hs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx/Parse.hs b/src/Text/Pandoc/Readers/Docx/Parse.hs index 24615ba94..05ce691a6 100644 --- a/src/Text/Pandoc/Readers/Docx/Parse.hs +++ b/src/Text/Pandoc/Readers/Docx/Parse.hs @@ -61,7 +61,7 @@ import Control.Monad.Reader import Control.Monad.State.Strict import Data.Bits ((.|.)) import qualified Data.ByteString.Lazy as B -import Data.Char (chr, isDigit, ord, readLitChar) +import Data.Char (chr, ord, readLitChar) import Data.List import qualified Data.Map as M import Data.Maybe @@ -939,19 +939,18 @@ elemToRunStyle ns element parentStyle } elemToRunStyle _ _ _ = defaultRunStyle -isNumericNotNull :: String -> Bool -isNumericNotNull str = (str /= []) && (all isDigit str) - getHeaderLevel :: NameSpaces -> Element -> Maybe (String,Int) getHeaderLevel ns element | Just styleId <- findAttrByName ns "w" "styleId" element , Just index <- stripPrefix "Heading" styleId - , isNumericNotNull index = Just (styleId, read index) + , Just n <- stringToInteger index + , n > 0 = Just (styleId, fromInteger n) | Just styleId <- findAttrByName ns "w" "styleId" element , Just index <- findChildByName ns "w" "name" element >>= findAttrByName ns "w" "val" >>= stripPrefix "heading " - , isNumericNotNull index = Just (styleId, read index) + , Just n <- stringToInteger index + , n > 0 = Just (styleId, fromInteger n) getHeaderLevel _ _ = Nothing blockQuoteStyleIds :: [String] -- cgit v1.2.3 From 1b5bfced558d28d45caadc6171f9e7448f3deae1 Mon Sep 17 00:00:00 2001 From: Alexander <ilabdsf@gmail.com> Date: Mon, 7 Aug 2017 07:43:59 +0300 Subject: Muse reader: debug indented paragraph support (#3839) Take only first line indentation into account and do not start new paragraph on indentation change. --- src/Text/Pandoc/Readers/Muse.hs | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 201a59fc0..6e4aed94e 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -186,7 +186,6 @@ blockElements = choice [ comment , orderedList , table , commentTag - , indentedBlock , noteBlock ] @@ -249,27 +248,12 @@ quoteTag = withQuoteContext InDoubleQuote $ blockTag B.blockQuote "quote" commentTag :: PandocMonad m => MuseParser m (F Blocks) commentTag = parseHtmlContent "comment" anyChar >> return mempty --- Indented block is either center, right or quote -indentedLine :: PandocMonad m => MuseParser m (Int, String) -indentedLine = try $ do - indent <- length <$> many1 spaceChar - line <- anyLine - return (indent, line) - -rawIndentedBlock :: PandocMonad m => MuseParser m (Int, String) -rawIndentedBlock = try $ do - lns <- many1 indentedLine - let indent = minimum $ map fst lns - return (indent, unlines $ map snd lns) - -indentedBlock :: PandocMonad m => MuseParser m (F Blocks) -indentedBlock = try $ do - (indent, raw) <- rawIndentedBlock - contents <- withQuoteContext InDoubleQuote $ parseFromString parseBlocks raw - return $ (if indent >= 2 && indent < 6 then B.blockQuote else id) <$> contents - +-- Indented paragraph is either center, right or quote para :: PandocMonad m => MuseParser m (F Blocks) -para = liftM B.para . trimInlinesF . mconcat <$> many1Till inline endOfParaElement +para = do + indent <- length <$> many spaceChar + let f = if indent >= 2 && indent < 6 then B.blockQuote else id + liftM (f . B.para) . trimInlinesF . mconcat <$> many1Till inline endOfParaElement where endOfParaElement = lookAhead $ endOfInput <|> endOfPara <|> newBlockElement endOfInput = try $ skipMany blankline >> skipSpaces >> eof -- cgit v1.2.3 From 3504915e63c68c3a4e91b8dcccb90c93435cf212 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 6 Aug 2017 22:31:15 -0700 Subject: LaTeX writer: Don't use figure inside table cell. Closes #3836. --- src/Text/Pandoc/Writers/LaTeX.hs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 55ecda819..4b7bf0e9b 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -474,6 +474,7 @@ blockToLaTeX (Plain lst) = -- title beginning with fig: indicates that the image is a figure blockToLaTeX (Para [Image attr@(ident, _, _) txt (src,'f':'i':'g':':':tit)]) = do inNote <- gets stInNote + inMinipage <- gets stInMinipage modify $ \st -> st{ stInMinipage = True, stNotes = [] } capt <- inlineListToLaTeX txt notes <- gets stNotes @@ -490,8 +491,9 @@ blockToLaTeX (Para [Image attr@(ident, _, _) txt (src,'f':'i':'g':':':tit)]) = d let figure = cr <> "\\begin{figure}" $$ "\\centering" $$ img $$ caption $$ "\\end{figure}" <> cr figure' <- hypertarget True ident figure - return $ if inNote - -- can't have figures in notes + return $ if inNote || inMinipage + -- can't have figures in notes or minipage (here, table cell) + -- http://www.tex.ac.uk/FAQ-ouparmd.html then "\\begin{center}" $$ img $+$ capt $$ "\\end{center}" else figure' $$ footnotes -- . . . indicates pause in beamer slides -- cgit v1.2.3 From 9e6b9cdc5face62842cfee6a0eacefed82973239 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 7 Aug 2017 13:38:15 -0700 Subject: LaTeX reader: Support `\let`. Also, fix regular macros so they're expanded at the point of use, and NOT also the point of definition. `\let` macros, by contrast, are expanded at the point of definition. Added an `ExpansionPoint` field to `Macro` to track this difference. --- src/Text/Pandoc/Readers/LaTeX.hs | 41 +++++++++++++++++++++++----------- src/Text/Pandoc/Readers/LaTeX/Types.hs | 6 ++++- 2 files changed, 33 insertions(+), 14 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index b06cd7348..7004f2ba5 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -65,7 +65,7 @@ import Text.Pandoc.Parsing hiding (many, optional, withRaw, mathInline, mathDisplay, space, (<|>), spaces, blankline) import Text.Pandoc.Shared -import Text.Pandoc.Readers.LaTeX.Types (Macro(..), Tok(..), +import Text.Pandoc.Readers.LaTeX.Types (Macro(..), ExpansionPoint(..), Tok(..), TokType(..)) import Text.Pandoc.Walk import Text.Pandoc.Error (PandocError(PandocParsecError, PandocMacroLoop)) @@ -375,7 +375,7 @@ doMacros n = do macros <- sMacros <$> getState case M.lookup name macros of Nothing -> return () - Just (Macro numargs optarg newtoks) -> do + Just (Macro expansionPoint numargs optarg newtoks) -> do setInput ts let getarg = spaces >> braced args <- case optarg of @@ -389,9 +389,12 @@ doMacros n = do addTok t acc = setpos spos t : acc ts' <- getInput setInput $ foldr addTok ts' newtoks - if n > 20 -- detect macro expansion loops - then throwError $ PandocMacroLoop (T.unpack name) - else doMacros (n + 1) + case expansionPoint of + ExpandWhenUsed -> + if n > 20 -- detect macro expansion loops + then throwError $ PandocMacroLoop (T.unpack name) + else doMacros (n + 1) + ExpandWhenDefined -> return () setpos :: (Line, Column) -> Tok -> Tok setpos spos (Tok _ tt txt) = Tok spos tt txt @@ -1375,7 +1378,8 @@ isBlockCommand s = treatAsBlock :: Set.Set Text treatAsBlock = Set.fromList - [ "newcommand", "renewcommand" + [ "let" + , "newcommand", "renewcommand" , "newenvironment", "renewenvironment" , "providecommand", "provideenvironment" -- newcommand, etc. should be parsed by macroDef, but we need this @@ -1517,7 +1521,7 @@ macroDef :: PandocMonad m => LP m Blocks macroDef = do mempty <$ ((commandDef <|> environmentDef) <* doMacros 0) where commandDef = do - (name, macro') <- newcommand + (name, macro') <- newcommand <|> letmacro guardDisabled Ext_latex_macros <|> updateState (\s -> s{ sMacros = M.insert name macro' (sMacros s) }) environmentDef = do @@ -1532,6 +1536,15 @@ macroDef = do -- @\newcommand{\envname}[n-args][default]{begin}@ -- @\newcommand{\endenvname}@ +letmacro :: PandocMonad m => LP m (Text, Macro) +letmacro = do + pos <- getPosition + controlSeq "let" + Tok _ (CtrlSeq name) _ <- anyControlSeq + optional $ symbol '=' + contents <- braced <|> ((:[]) <$> anyControlSeq) + return (name, Macro ExpandWhenDefined 0 Nothing contents) + newcommand :: PandocMonad m => LP m (Text, Macro) newcommand = do pos <- getPosition @@ -1546,13 +1559,15 @@ newcommand = do spaces optarg <- option Nothing $ Just <$> try bracketedToks spaces - contents <- braced + contents <- withVerbatimMode braced + -- we use withVerbatimMode, because macros are to be expanded + -- at point of use, not point of definition when (mtype == "newcommand") $ do macros <- sMacros <$> getState case M.lookup name macros of Just _ -> report $ MacroAlreadyDefined (T.unpack txt) pos Nothing -> return () - return (name, Macro numargs optarg contents) + return (name, Macro ExpandWhenUsed numargs optarg contents) newenvironment :: PandocMonad m => LP m (Text, Macro, Macro) newenvironment = do @@ -1568,16 +1583,16 @@ newenvironment = do spaces optarg <- option Nothing $ Just <$> try bracketedToks spaces - startcontents <- braced + startcontents <- withVerbatimMode braced spaces - endcontents <- braced + endcontents <- withVerbatimMode braced when (mtype == "newenvironment") $ do macros <- sMacros <$> getState case M.lookup name macros of Just _ -> report $ MacroAlreadyDefined (T.unpack name) pos Nothing -> return () - return (name, Macro numargs optarg startcontents, - Macro 0 Nothing endcontents) + return (name, Macro ExpandWhenUsed numargs optarg startcontents, + Macro ExpandWhenUsed 0 Nothing endcontents) bracketedToks :: PandocMonad m => LP m [Tok] bracketedToks = do diff --git a/src/Text/Pandoc/Readers/LaTeX/Types.hs b/src/Text/Pandoc/Readers/LaTeX/Types.hs index 6f84ae1f1..2bef3cb1a 100644 --- a/src/Text/Pandoc/Readers/LaTeX/Types.hs +++ b/src/Text/Pandoc/Readers/LaTeX/Types.hs @@ -30,6 +30,7 @@ Types for LaTeX tokens and macros. module Text.Pandoc.Readers.LaTeX.Types ( Tok(..) , TokType(..) , Macro(..) + , ExpansionPoint(..) , Line , Column ) where @@ -43,6 +44,9 @@ data TokType = CtrlSeq Text | Spaces | Newline | Symbol | Word | Comment | data Tok = Tok (Line, Column) TokType Text deriving (Eq, Ord, Show) -data Macro = Macro Int (Maybe [Tok]) [Tok] +data ExpansionPoint = ExpandWhenDefined | ExpandWhenUsed + deriving (Eq, Ord, Show) + +data Macro = Macro ExpansionPoint Int (Maybe [Tok]) [Tok] deriving Show -- cgit v1.2.3 From c806ef1b150147ecaf5a4781e2ac1ce921559ca4 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 7 Aug 2017 16:06:19 -0700 Subject: LaTeX reader: Support simple `\def` macros. Note that we still don't support macros with fancy parameter delimiters, like \def\foo#1..#2{...} --- src/Text/Pandoc/Readers/LaTeX.hs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 7004f2ba5..b9d4de935 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1521,7 +1521,7 @@ macroDef :: PandocMonad m => LP m Blocks macroDef = do mempty <$ ((commandDef <|> environmentDef) <* doMacros 0) where commandDef = do - (name, macro') <- newcommand <|> letmacro + (name, macro') <- newcommand <|> letmacro <|> defmacro guardDisabled Ext_latex_macros <|> updateState (\s -> s{ sMacros = M.insert name macro' (sMacros s) }) environmentDef = do @@ -1538,13 +1538,32 @@ macroDef = do letmacro :: PandocMonad m => LP m (Text, Macro) letmacro = do - pos <- getPosition controlSeq "let" Tok _ (CtrlSeq name) _ <- anyControlSeq optional $ symbol '=' + spaces contents <- braced <|> ((:[]) <$> anyControlSeq) return (name, Macro ExpandWhenDefined 0 Nothing contents) +defmacro :: PandocMonad m => LP m (Text, Macro) +defmacro = try $ do + controlSeq "def" + Tok _ (CtrlSeq name) _ <- anyControlSeq + numargs <- option 0 $ argSeq 1 + contents <- withVerbatimMode braced + return (name, Macro ExpandWhenUsed numargs Nothing contents) + +-- Note: we don't yet support fancy things like #1.#2 +argSeq :: PandocMonad m => Int -> LP m Int +argSeq n = do + Tok _ (Arg i) _ <- satisfyTok isArgTok + guard $ i == n + argSeq (n+1) <|> return n + +isArgTok :: Tok -> Bool +isArgTok (Tok _ (Arg _) _) = True +isArgTok _ = False + newcommand :: PandocMonad m => LP m (Text, Macro) newcommand = do pos <- getPosition -- cgit v1.2.3 From 190f36d2fd018a9d23b1b70c6b7f4404ae7a5c81 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 7 Aug 2017 16:11:13 -0700 Subject: Small tweak to previous commit. --- 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 b9d4de935..e51f797af 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1378,7 +1378,7 @@ isBlockCommand s = treatAsBlock :: Set.Set Text treatAsBlock = Set.fromList - [ "let" + [ "let", "def" , "newcommand", "renewcommand" , "newenvironment", "renewenvironment" , "providecommand", "provideenvironment" -- cgit v1.2.3 From 8995281691b56d711c44fb0c5cae2fc675d12eb9 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 7 Aug 2017 16:53:59 -0700 Subject: Logging: Made SkippedContent an INFO level message... rather than WARNING. --- src/Text/Pandoc/Logging.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs index 1dcff7470..506963619 100644 --- a/src/Text/Pandoc/Logging.hs +++ b/src/Text/Pandoc/Logging.hs @@ -276,7 +276,7 @@ showLogMessage msg = messageVerbosity:: LogMessage -> Verbosity messageVerbosity msg = case msg of - SkippedContent{} -> WARNING + SkippedContent{} -> INFO CouldNotParseYamlMetadata{} -> WARNING DuplicateLinkReference{} -> WARNING DuplicateNoteReference{} -> WARNING -- cgit v1.2.3 From 2c81c4c218be757ff2610e3b47548f0642ce5c10 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 3 Aug 2017 10:50:05 -0700 Subject: Added gfm (GitHub-flavored CommonMark) as an input and output format. This uses bindings to GitHub's fork of cmark, so it should parse gfm exactly as GitHub does (excepting certain postprocessing steps, involving notifications, emojis, etc.). * Added Text.Pandoc.Readers.GFM (exporting readGFM) * Added Text.Pandoc.Writers.GFM (exporting writeGFM) * Added `gfm` as input and output forma Note that tables are currently always rendered as HTML in the writer; this can be improved when CMarkGFM supports tables in output. --- src/Text/Pandoc/Readers.hs | 3 + src/Text/Pandoc/Readers/CommonMark.hs | 4 +- src/Text/Pandoc/Readers/GFM.hs | 185 +++++++++++++++++++++++++++++++++ src/Text/Pandoc/Writers.hs | 3 + src/Text/Pandoc/Writers/GFM.hs | 189 ++++++++++++++++++++++++++++++++++ 5 files changed, 382 insertions(+), 2 deletions(-) create mode 100644 src/Text/Pandoc/Readers/GFM.hs create mode 100644 src/Text/Pandoc/Writers/GFM.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers.hs b/src/Text/Pandoc/Readers.hs index 78a2038a4..996412d48 100644 --- a/src/Text/Pandoc/Readers.hs +++ b/src/Text/Pandoc/Readers.hs @@ -45,6 +45,7 @@ module Text.Pandoc.Readers , readOdt , readMarkdown , readCommonMark + , readGFM , readMediaWiki , readVimwiki , readRST @@ -76,6 +77,7 @@ import Text.Pandoc.Error import Text.Pandoc.Extensions import Text.Pandoc.Options import Text.Pandoc.Readers.CommonMark +import Text.Pandoc.Readers.GFM import Text.Pandoc.Readers.DocBook import Text.Pandoc.Readers.Docx import Text.Pandoc.Readers.EPUB @@ -117,6 +119,7 @@ readers = [ ("native" , TextReader readNative) ,("markdown_github" , TextReader readMarkdown) ,("markdown_mmd", TextReader readMarkdown) ,("commonmark" , TextReader readCommonMark) + ,("gfm" , TextReader readGFM) ,("rst" , TextReader readRST) ,("mediawiki" , TextReader readMediaWiki) ,("vimwiki" , TextReader readVimwiki) diff --git a/src/Text/Pandoc/Readers/CommonMark.hs b/src/Text/Pandoc/Readers/CommonMark.hs index 3c62f8db5..a0ea9f3e8 100644 --- a/src/Text/Pandoc/Readers/CommonMark.hs +++ b/src/Text/Pandoc/Readers/CommonMark.hs @@ -44,8 +44,8 @@ readCommonMark :: PandocMonad m => ReaderOptions -> Text -> m Pandoc readCommonMark opts s = return $ nodeToPandoc $ commonmarkToNode opts' s where opts' = if extensionEnabled Ext_smart (readerExtensions opts) - then [optNormalize, optSmart] - else [optNormalize] + then [optSmart] + else [] nodeToPandoc :: Node -> Pandoc nodeToPandoc (Node _ DOCUMENT nodes) = diff --git a/src/Text/Pandoc/Readers/GFM.hs b/src/Text/Pandoc/Readers/GFM.hs new file mode 100644 index 000000000..1cdc47b98 --- /dev/null +++ b/src/Text/Pandoc/Readers/GFM.hs @@ -0,0 +1,185 @@ +{- +Copyright (C) 2017 John MacFarlane <jgm@berkeley.edu> + +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.GFM + Copyright : Copyright (C) 2017 John MacFarlane + License : GNU GPL, version 2 or above + + Maintainer : John MacFarlane <jgm@berkeley.edu> + Stability : alpha + Portability : portable + +Conversion of GitHub flavored CommonMark text to 'Pandoc' document. + +CommonMark is a strongly specified variant of Markdown: http://commonmark.org. +-} +module Text.Pandoc.Readers.GFM (readGFM) +where + +import CMarkGFM +import Data.List (groupBy) +import Data.Text (Text, unpack) +import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Definition +import Text.Pandoc.Options + +-- | Parse a CommonMark formatted string into a 'Pandoc' structure. +readGFM :: PandocMonad m => ReaderOptions -> Text -> m Pandoc +readGFM opts s = return $ + nodeToPandoc $ commonmarkToNode opts' exts s + where opts' = [optSmart | enabled Ext_smart] + exts = [extStrikethrough, extTable, extAutolink] + enabled x = extensionEnabled x (readerExtensions opts) + +nodeToPandoc :: Node -> Pandoc +nodeToPandoc (Node _ DOCUMENT nodes) = + Pandoc nullMeta $ foldr addBlock [] nodes +nodeToPandoc n = -- shouldn't happen + Pandoc nullMeta $ foldr addBlock [] [n] + +addBlocks :: [Node] -> [Block] +addBlocks = foldr addBlock [] + +addBlock :: Node -> [Block] -> [Block] +addBlock (Node _ PARAGRAPH nodes) = + (Para (addInlines nodes) :) +addBlock (Node _ THEMATIC_BREAK _) = + (HorizontalRule :) +addBlock (Node _ BLOCK_QUOTE nodes) = + (BlockQuote (addBlocks nodes) :) +addBlock (Node _ (HTML_BLOCK t) _) = + (RawBlock (Format "html") (unpack t) :) +-- Note: the cmark parser will never generate CUSTOM_BLOCK, +-- so we don't need to handle it: +addBlock (Node _ (CUSTOM_BLOCK _onEnter _onExit) _nodes) = + id +addBlock (Node _ (CODE_BLOCK info t) _) = + (CodeBlock ("", take 1 (words (unpack info)), []) (unpack t) :) +addBlock (Node _ (HEADING lev) nodes) = + (Header lev ("",[],[]) (addInlines nodes) :) +addBlock (Node _ (LIST listAttrs) nodes) = + (constructor (map (setTightness . addBlocks . children) nodes) :) + where constructor = case listType listAttrs of + BULLET_LIST -> BulletList + ORDERED_LIST -> OrderedList + (start, DefaultStyle, delim) + start = listStart listAttrs + setTightness = if listTight listAttrs + then map paraToPlain + else id + paraToPlain (Para xs) = Plain (xs) + paraToPlain x = x + delim = case listDelim listAttrs of + PERIOD_DELIM -> Period + PAREN_DELIM -> OneParen +addBlock (Node _ (TABLE alignments) nodes) = do + (Table [] aligns widths headers rows :) + where aligns = map fromTableCellAlignment alignments + fromTableCellAlignment NoAlignment = AlignDefault + fromTableCellAlignment LeftAligned = AlignLeft + fromTableCellAlignment RightAligned = AlignRight + fromTableCellAlignment CenterAligned = AlignCenter + widths = replicate numcols 0.0 + numcols = if null rows' + then 0 + else maximum $ map length rows' + rows' = map toRow $ filter isRow nodes + (headers, rows) = case rows' of + (h:rs) -> (h, rs) + [] -> ([], []) + isRow (Node _ TABLE_ROW _) = True + isRow _ = False + isCell (Node _ TABLE_CELL _) = True + isCell _ = False + toRow (Node _ TABLE_ROW ns) = map toCell $ filter isCell ns + toRow (Node _ t _) = error $ "toRow encountered non-row " ++ show t + toCell (Node _ TABLE_CELL []) = [] + toCell (Node _ TABLE_CELL (n:ns)) + | isBlockNode n = addBlocks (n:ns) + | otherwise = [Plain (addInlines (n:ns))] + toCell (Node _ t _) = error $ "toCell encountered non-cell " ++ show t +addBlock (Node _ TABLE_ROW _) = id -- handled in TABLE +addBlock (Node _ TABLE_CELL _) = id -- handled in TABLE +addBlock _ = id + +isBlockNode :: Node -> Bool +isBlockNode (Node _ nodetype _) = + case nodetype of + DOCUMENT -> True + THEMATIC_BREAK -> True + PARAGRAPH -> True + BLOCK_QUOTE -> True + HTML_BLOCK _ -> True + CUSTOM_BLOCK _ _ -> True + CODE_BLOCK _ _ -> True + HEADING _ -> True + LIST _ -> True + ITEM -> True + TEXT _ -> False + SOFTBREAK -> False + LINEBREAK -> False + HTML_INLINE _ -> False + CUSTOM_INLINE _ _ -> False + CODE _ -> False + EMPH -> False + STRONG -> False + LINK _ _ -> False + IMAGE _ _ -> False + STRIKETHROUGH -> False + TABLE _ -> False + TABLE_ROW -> False + TABLE_CELL -> False + +children :: Node -> [Node] +children (Node _ _ ns) = ns + +addInlines :: [Node] -> [Inline] +addInlines = foldr addInline [] + +addInline :: Node -> [Inline] -> [Inline] +addInline (Node _ (TEXT t) _) = (map toinl clumps ++) + where raw = unpack t + clumps = groupBy samekind raw + samekind ' ' ' ' = True + samekind ' ' _ = False + samekind _ ' ' = False + samekind _ _ = True + toinl (' ':_) = Space + toinl xs = Str xs +addInline (Node _ LINEBREAK _) = (LineBreak :) +addInline (Node _ SOFTBREAK _) = (SoftBreak :) +addInline (Node _ (HTML_INLINE t) _) = + (RawInline (Format "html") (unpack t) :) +-- Note: the cmark parser will never generate CUSTOM_BLOCK, +-- so we don't need to handle it: +addInline (Node _ (CUSTOM_INLINE _onEnter _onExit) _nodes) = + id +addInline (Node _ (CODE t) _) = + (Code ("",[],[]) (unpack t) :) +addInline (Node _ EMPH nodes) = + (Emph (addInlines nodes) :) +addInline (Node _ STRONG nodes) = + (Strong (addInlines nodes) :) +addInline (Node _ STRIKETHROUGH nodes) = + (Strikeout (addInlines nodes) :) +addInline (Node _ (LINK url title) nodes) = + (Link nullAttr (addInlines nodes) (unpack url, unpack title) :) +addInline (Node _ (IMAGE url title) nodes) = + (Image nullAttr (addInlines nodes) (unpack url, unpack title) :) +addInline _ = id diff --git a/src/Text/Pandoc/Writers.hs b/src/Text/Pandoc/Writers.hs index 6dfc1a7b3..c10d9149b 100644 --- a/src/Text/Pandoc/Writers.hs +++ b/src/Text/Pandoc/Writers.hs @@ -38,6 +38,7 @@ module Text.Pandoc.Writers , writeAsciiDoc , writeBeamer , writeCommonMark + , writeGFM , writeConTeXt , writeCustom , writeDZSlides @@ -89,6 +90,7 @@ import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Writers.AsciiDoc import Text.Pandoc.Writers.CommonMark +import Text.Pandoc.Writers.GFM import Text.Pandoc.Writers.ConTeXt import Text.Pandoc.Writers.Custom import Text.Pandoc.Writers.Docbook @@ -172,6 +174,7 @@ writers = [ ,("asciidoc" , TextWriter writeAsciiDoc) ,("haddock" , TextWriter writeHaddock) ,("commonmark" , TextWriter writeCommonMark) + ,("gfm" , TextWriter writeGFM) ,("tei" , TextWriter writeTEI) ,("muse" , TextWriter writeMuse) ] diff --git a/src/Text/Pandoc/Writers/GFM.hs b/src/Text/Pandoc/Writers/GFM.hs new file mode 100644 index 000000000..d9806e2fa --- /dev/null +++ b/src/Text/Pandoc/Writers/GFM.hs @@ -0,0 +1,189 @@ +{- +Copyright (C) 2015 John MacFarlane <jgm@berkeley.edu> + +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.Writers.GFM + Copyright : Copyright (C) 2017 John MacFarlane + License : GNU GPL, version 2 or above + + Maintainer : John MacFarlane <jgm@berkeley.edu> + Stability : alpha + Portability : portable + +Conversion of 'Pandoc' documents to CommonMark. + +CommonMark: <http://commonmark.org> +-} +module Text.Pandoc.Writers.GFM (writeGFM) where + +import CMarkGFM +import Control.Monad.State.Strict (State, get, modify, runState) +import Data.Foldable (foldrM) +import Data.Text (Text) +import qualified Data.Text as T +import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Definition +import Text.Pandoc.Options +import Text.Pandoc.Shared (isTightList, linesToPara) +import Text.Pandoc.Templates (renderTemplate') +import Text.Pandoc.Walk (walkM) +import Text.Pandoc.Writers.HTML (writeHtml5String) +import Text.Pandoc.Writers.Shared + +-- | Convert Pandoc to GitHub flavored CommonMark. +writeGFM :: PandocMonad m => WriterOptions -> Pandoc -> m Text +writeGFM 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 + case writerTemplate opts of + Nothing -> return main + Just tpl -> renderTemplate' tpl context + +processNotes :: Inline -> State [[Block]] Inline +processNotes (Note bs) = do + modify (bs :) + notes <- get + return $ Str $ "[" ++ show (length notes) ++ "]" +processNotes x = return x + +node :: NodeType -> [Node] -> Node +node = Node Nothing + +blocksToCommonMark :: PandocMonad m => WriterOptions -> [Block] -> m Text +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 $ + nodeToCommonmark cmarkOpts colwidth $ + node DOCUMENT nodes + +inlinesToCommonMark :: PandocMonad m => WriterOptions -> [Inline] -> m Text +inlinesToCommonMark opts ils = return $ + nodeToCommonmark cmarkOpts colwidth $ node PARAGRAPH (inlinesToNodes ils) + where cmarkOpts = [optHardBreaks | isEnabled Ext_hard_line_breaks opts] + colwidth = if writerWrapText opts == WrapAuto + then Just $ writerColumns opts + else Nothing + +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 + dlToBullet (term, ((Plain xs : ys) : zs)) = + Plain (term ++ [LineBreak] ++ xs) : ys ++ concat zs + dlToBullet (term, xs) = + Para term : concat xs +blockToNodes t@(Table _ _ _ _ _) ns = do + s <- writeHtml5String def $! Pandoc nullMeta [t] + return (node (HTML_BLOCK s) [] : ns) +blockToNodes Null ns = return ns + +inlinesToNodes :: [Inline] -> [Node] +inlinesToNodes = foldr inlineToNodes [] + +inlineToNodes :: Inline -> [Node] -> [Node] +inlineToNodes (Str s) = (node (TEXT (T.pack s)) [] :) +inlineToNodes Space = (node (TEXT (T.pack " ")) [] :) +inlineToNodes LineBreak = (node LINEBREAK [] :) +inlineToNodes SoftBreak = (node SOFTBREAK [] :) +inlineToNodes (Emph xs) = (node EMPH (inlinesToNodes xs) :) +inlineToNodes (Strong xs) = (node STRONG (inlinesToNodes xs) :) +inlineToNodes (Strikeout xs) = + ((node (HTML_INLINE (T.pack "<s>")) [] : inlinesToNodes xs ++ + [node (HTML_INLINE (T.pack "</s>")) []]) ++ ) +inlineToNodes (Superscript xs) = + ((node (HTML_INLINE (T.pack "<sup>")) [] : inlinesToNodes xs ++ + [node (HTML_INLINE (T.pack "</sup>")) []]) ++ ) +inlineToNodes (Subscript xs) = + ((node (HTML_INLINE (T.pack "<sub>")) [] : inlinesToNodes xs ++ + [node (HTML_INLINE (T.pack "</sub>")) []]) ++ ) +inlineToNodes (SmallCaps xs) = + ((node (HTML_INLINE (T.pack "<span class=\"smallcaps\">")) [] + : inlinesToNodes xs ++ + [node (HTML_INLINE (T.pack "</span>")) []]) ++ ) +inlineToNodes (Link _ ils (url,tit)) = + (node (LINK (T.pack url) (T.pack tit)) (inlinesToNodes ils) :) +inlineToNodes (Image _ ils (url,tit)) = + (node (IMAGE (T.pack url) (T.pack tit)) (inlinesToNodes ils) :) +inlineToNodes (RawInline fmt xs) + | fmt == Format "html" = (node (HTML_INLINE (T.pack xs)) [] :) + | otherwise = (node (CUSTOM_INLINE (T.pack xs) (T.empty)) [] :) +inlineToNodes (Quoted qt ils) = + ((node (TEXT start) [] : inlinesToNodes ils ++ [node (TEXT end) []]) ++) + where (start, end) = case qt of + SingleQuote -> (T.pack "‘", T.pack "’") + DoubleQuote -> (T.pack "“", T.pack "”") +inlineToNodes (Code _ str) = (node (CODE (T.pack str)) [] :) +inlineToNodes (Math mt str) = + case mt of + InlineMath -> + (node (HTML_INLINE (T.pack ("\\(" ++ str ++ "\\)"))) [] :) + DisplayMath -> + (node (HTML_INLINE (T.pack ("\\[" ++ str ++ "\\]"))) [] :) +inlineToNodes (Span _ ils) = (inlinesToNodes ils ++) +inlineToNodes (Cite _ ils) = (inlinesToNodes ils ++) +inlineToNodes (Note _) = id -- should not occur +-- we remove Note elements in preprocessing -- cgit v1.2.3 From fd23b6dbce073722931634e04ed6c9c8b851f37c Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 7 Aug 2017 17:07:20 -0700 Subject: Revert "Logging: Made SkippedContent an INFO level message..." This reverts commit 8995281691b56d711c44fb0c5cae2fc675d12eb9. --- src/Text/Pandoc/Logging.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs index 506963619..1dcff7470 100644 --- a/src/Text/Pandoc/Logging.hs +++ b/src/Text/Pandoc/Logging.hs @@ -276,7 +276,7 @@ showLogMessage msg = messageVerbosity:: LogMessage -> Verbosity messageVerbosity msg = case msg of - SkippedContent{} -> INFO + SkippedContent{} -> WARNING CouldNotParseYamlMetadata{} -> WARNING DuplicateLinkReference{} -> WARNING DuplicateNoteReference{} -> WARNING -- cgit v1.2.3 From 2c0e989f9df788b7f168159fd99d5c3e1a78aa85 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 7 Aug 2017 21:00:57 -0700 Subject: Markdown reader: fixed spurious parsing as citation as reference def. We now disallow reference keys starting with `@` if the `citations` extension is enabled. Closes #3840. --- src/Text/Pandoc/Readers/Markdown.hs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index d7e59c7fd..1bcf1cfae 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -1695,8 +1695,10 @@ endline = try $ do -- a reference label for a link reference :: PandocMonad m => MarkdownParser m (F Inlines, String) -reference = do notFollowedBy' (string "[^") -- footnote reference - withRaw $ trimInlinesF <$> inlinesInBalancedBrackets +reference = do + guardDisabled Ext_footnotes <|> notFollowedBy' (string "[^") + guardDisabled Ext_citations <|> notFollowedBy' (string "[@") + withRaw $ trimInlinesF <$> inlinesInBalancedBrackets parenthesizedChars :: PandocMonad m => MarkdownParser m [Char] parenthesizedChars = do -- cgit v1.2.3 From 5064241b24888b325836d4912085e916fe45080b Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 7 Aug 2017 21:18:50 -0700 Subject: Man writer: avoid error for def lists with no definitions. Closes #3832. --- src/Text/Pandoc/Writers/Man.hs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Man.hs b/src/Text/Pandoc/Writers/Man.hs index 4e756c419..8d677600d 100644 --- a/src/Text/Pandoc/Writers/Man.hs +++ b/src/Text/Pandoc/Writers/Man.hs @@ -305,15 +305,18 @@ definitionListItemToMan opts (label, defs) = do labelText <- inlineListToMan opts label contents <- if null defs then return empty - else liftM vcat $ forM defs $ \blocks -> do - (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 - return $ first' $$ text ".RS" $$ rest' $$ text ".RE" + else liftM vcat $ forM defs $ \blocks -> + case blocks of + (x:xs) -> do + first' <- blockToMan opts $ + case x of + Para y -> Plain y + _ -> x + rest' <- liftM vcat $ mapM + (\item -> blockToMan opts item) xs + return $ first' $$ + text ".RS" $$ rest' $$ text ".RE" + [] -> return empty return $ text ".TP" $$ nowrap (text ".B " <> labelText) $$ contents -- | Convert list of Pandoc block elements to man. -- cgit v1.2.3 From ea6be2e4e710b3dcd56d526ec6d98c925b8fd58b Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 7 Aug 2017 21:25:07 -0700 Subject: Added Deprecated warning to LogMessage. --- src/Text/Pandoc/Logging.hs | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs index 1dcff7470..91b225028 100644 --- a/src/Text/Pandoc/Logging.hs +++ b/src/Text/Pandoc/Logging.hs @@ -94,6 +94,7 @@ data LogMessage = | InvalidLang String | CouldNotHighlight String | MissingCharacter String + | Deprecated String deriving (Show, Eq, Data, Ord, Typeable, Generic) instance ToJSON LogMessage where @@ -191,6 +192,9 @@ instance ToJSON LogMessage where ["message" .= Text.pack msg] MissingCharacter msg -> ["message" .= Text.pack msg] + Deprecated msg -> + ["message" .= Text.pack msg] + showPos :: SourcePos -> String showPos pos = sn ++ "line " ++ @@ -272,6 +276,8 @@ showLogMessage msg = "Could not highlight code block:\n" ++ m MissingCharacter m -> "Missing character: " ++ m + Deprecated m -> + "Deprecated: " ++ m messageVerbosity:: LogMessage -> Verbosity messageVerbosity msg = @@ -303,3 +309,4 @@ messageVerbosity msg = InvalidLang{} -> WARNING CouldNotHighlight{} -> WARNING MissingCharacter{} -> WARNING + Deprecated{} -> WARNING -- cgit v1.2.3 From 3bf11cae277aa169f314c76fa8a3eba9ecca382b Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 7 Aug 2017 22:04:08 -0700 Subject: Man writer: removed some unneeded imports. --- src/Text/Pandoc/Writers/Man.hs | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Man.hs b/src/Text/Pandoc/Writers/Man.hs index 8d677600d..8adb3e7eb 100644 --- a/src/Text/Pandoc/Writers/Man.hs +++ b/src/Text/Pandoc/Writers/Man.hs @@ -30,7 +30,6 @@ Conversion of 'Pandoc' documents to groff man page format. -} module Text.Pandoc.Writers.Man ( writeMan) where -import Control.Monad.Except (throwError) import Control.Monad.State.Strict import Data.List (intercalate, intersperse, stripPrefix, sort) import qualified Data.Map as Map @@ -40,7 +39,6 @@ import qualified Data.Text as T import Text.Pandoc.Builder (deleteMeta) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Definition -import Text.Pandoc.Error import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Pretty -- cgit v1.2.3 From 834e9498f9e10ec45f915259d534b917f0c72136 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 7 Aug 2017 22:34:23 -0700 Subject: Logging: Added Deprecated constructor to LogMessage. --- src/Text/Pandoc/Logging.hs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs index 91b225028..ac45b0a66 100644 --- a/src/Text/Pandoc/Logging.hs +++ b/src/Text/Pandoc/Logging.hs @@ -94,7 +94,7 @@ data LogMessage = | InvalidLang String | CouldNotHighlight String | MissingCharacter String - | Deprecated String + | Deprecated String String deriving (Show, Eq, Data, Ord, Typeable, Generic) instance ToJSON LogMessage where @@ -192,8 +192,9 @@ instance ToJSON LogMessage where ["message" .= Text.pack msg] MissingCharacter msg -> ["message" .= Text.pack msg] - Deprecated msg -> - ["message" .= Text.pack msg] + Deprecated thing msg -> + ["thing" .= Text.pack thing, + "message" .= Text.pack msg] showPos :: SourcePos -> String @@ -276,8 +277,11 @@ showLogMessage msg = "Could not highlight code block:\n" ++ m MissingCharacter m -> "Missing character: " ++ m - Deprecated m -> - "Deprecated: " ++ m + Deprecated t m -> + "Deprecated: " ++ t ++ + if null m + then "" + else ". " ++ m messageVerbosity:: LogMessage -> Verbosity messageVerbosity msg = -- cgit v1.2.3 From 6a9db1fde3b592a843b5fa6ce843a46cb3163968 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 7 Aug 2017 22:34:38 -0700 Subject: Issue deprecation warning for `markdown_github`. Advise to use `gfm` instead. --- src/Text/Pandoc/App.hs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 498cfae22..a56ae8149 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -76,8 +76,9 @@ import System.IO.Error (isDoesNotExistError) import Text.Pandoc import Text.Pandoc.Builder (setMeta) import Text.Pandoc.Class (PandocIO, extractMedia, fillMediaBag, getLog, - setResourcePath, getMediaBag, setTrace) + setResourcePath, getMediaBag, setTrace, report) import Text.Pandoc.Highlighting (highlightingStyles) +import Text.Pandoc.Logging (LogMessage(..)) import Text.Pandoc.Lua (runLuaFilter, LuaException(..)) import Text.Pandoc.Writers.Math (defaultMathJaxURL, defaultKaTeXURL) import Text.Pandoc.PDF (makePDF) @@ -444,6 +445,9 @@ convertWithOpts opts = do Native -> nativeNewline runIO' $ do + when (readerName == "markdown_github" || + writerName == "markdown_github") $ + report $ Deprecated "markdown_github" "Use gfm instead." setResourcePath (optResourcePath opts) doc <- sourceToDoc sources >>= ( (if isJust (optExtractMedia opts) -- cgit v1.2.3 From 91c989d6221991a47b1f0a9d180ccf3ce96b3f02 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 7 Aug 2017 23:10:17 -0700 Subject: Remove GFM modules; use CMarkGFM for both gfm and commonmark. We no longer have a separate readGFM and writeGFM; instead, we'll use readCommonMark and writeCommonMark with githubExtensions. It remains to implement these extensions conditionally. Closes #3841. --- src/Text/Pandoc/Extensions.hs | 1 + src/Text/Pandoc/Readers.hs | 4 +- src/Text/Pandoc/Readers/CommonMark.hs | 69 +++++++++++-- src/Text/Pandoc/Readers/GFM.hs | 185 --------------------------------- src/Text/Pandoc/Writers.hs | 4 +- src/Text/Pandoc/Writers/CommonMark.hs | 2 +- src/Text/Pandoc/Writers/GFM.hs | 189 ---------------------------------- 7 files changed, 67 insertions(+), 387 deletions(-) delete mode 100644 src/Text/Pandoc/Readers/GFM.hs delete mode 100644 src/Text/Pandoc/Writers/GFM.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 28459d4e6..9e49c5907 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -300,6 +300,7 @@ getDefaultExtensions "markdown_mmd" = multimarkdownExtensions getDefaultExtensions "markdown_github" = githubMarkdownExtensions getDefaultExtensions "markdown" = pandocExtensions getDefaultExtensions "plain" = plainExtensions +getDefaultExtensions "gfm" = githubMarkdownExtensions getDefaultExtensions "org" = extensionsFromList [Ext_citations, Ext_auto_identifiers] diff --git a/src/Text/Pandoc/Readers.hs b/src/Text/Pandoc/Readers.hs index 996412d48..92a185e0d 100644 --- a/src/Text/Pandoc/Readers.hs +++ b/src/Text/Pandoc/Readers.hs @@ -45,7 +45,6 @@ module Text.Pandoc.Readers , readOdt , readMarkdown , readCommonMark - , readGFM , readMediaWiki , readVimwiki , readRST @@ -77,7 +76,6 @@ import Text.Pandoc.Error import Text.Pandoc.Extensions import Text.Pandoc.Options import Text.Pandoc.Readers.CommonMark -import Text.Pandoc.Readers.GFM import Text.Pandoc.Readers.DocBook import Text.Pandoc.Readers.Docx import Text.Pandoc.Readers.EPUB @@ -119,7 +117,7 @@ readers = [ ("native" , TextReader readNative) ,("markdown_github" , TextReader readMarkdown) ,("markdown_mmd", TextReader readMarkdown) ,("commonmark" , TextReader readCommonMark) - ,("gfm" , TextReader readGFM) + ,("gfm" , TextReader readCommonMark) ,("rst" , TextReader readRST) ,("mediawiki" , TextReader readMediaWiki) ,("vimwiki" , TextReader readVimwiki) diff --git a/src/Text/Pandoc/Readers/CommonMark.hs b/src/Text/Pandoc/Readers/CommonMark.hs index a0ea9f3e8..9a67c8597 100644 --- a/src/Text/Pandoc/Readers/CommonMark.hs +++ b/src/Text/Pandoc/Readers/CommonMark.hs @@ -32,7 +32,7 @@ CommonMark is a strongly specified variant of Markdown: http://commonmark.org. module Text.Pandoc.Readers.CommonMark (readCommonMark) where -import CMark +import CMarkGFM import Data.List (groupBy) import Data.Text (Text, unpack) import Text.Pandoc.Class (PandocMonad) @@ -42,10 +42,10 @@ import Text.Pandoc.Options -- | Parse a CommonMark formatted string into a 'Pandoc' structure. readCommonMark :: PandocMonad m => ReaderOptions -> Text -> m Pandoc readCommonMark opts s = return $ - nodeToPandoc $ commonmarkToNode opts' s - where opts' = if extensionEnabled Ext_smart (readerExtensions opts) - then [optSmart] - else [] + nodeToPandoc $ commonmarkToNode opts' exts s + where opts' = [optSmart | enabled Ext_smart] + exts = [extStrikethrough, extTable, extAutolink] + enabled x = extensionEnabled x (readerExtensions opts) nodeToPandoc :: Node -> Pandoc nodeToPandoc (Node _ DOCUMENT nodes) = @@ -88,9 +88,64 @@ addBlock (Node _ (LIST listAttrs) nodes) = delim = case listDelim listAttrs of PERIOD_DELIM -> Period PAREN_DELIM -> OneParen -addBlock (Node _ ITEM _) = id -- handled in LIST +addBlock (Node _ (TABLE alignments) nodes) = do + (Table [] aligns widths headers rows :) + where aligns = map fromTableCellAlignment alignments + fromTableCellAlignment NoAlignment = AlignDefault + fromTableCellAlignment LeftAligned = AlignLeft + fromTableCellAlignment RightAligned = AlignRight + fromTableCellAlignment CenterAligned = AlignCenter + widths = replicate numcols 0.0 + numcols = if null rows' + then 0 + else maximum $ map length rows' + rows' = map toRow $ filter isRow nodes + (headers, rows) = case rows' of + (h:rs) -> (h, rs) + [] -> ([], []) + isRow (Node _ TABLE_ROW _) = True + isRow _ = False + isCell (Node _ TABLE_CELL _) = True + isCell _ = False + toRow (Node _ TABLE_ROW ns) = map toCell $ filter isCell ns + toRow (Node _ t _) = error $ "toRow encountered non-row " ++ show t + toCell (Node _ TABLE_CELL []) = [] + toCell (Node _ TABLE_CELL (n:ns)) + | isBlockNode n = addBlocks (n:ns) + | otherwise = [Plain (addInlines (n:ns))] + toCell (Node _ t _) = error $ "toCell encountered non-cell " ++ show t +addBlock (Node _ TABLE_ROW _) = id -- handled in TABLE +addBlock (Node _ TABLE_CELL _) = id -- handled in TABLE addBlock _ = id +isBlockNode :: Node -> Bool +isBlockNode (Node _ nodetype _) = + case nodetype of + DOCUMENT -> True + THEMATIC_BREAK -> True + PARAGRAPH -> True + BLOCK_QUOTE -> True + HTML_BLOCK _ -> True + CUSTOM_BLOCK _ _ -> True + CODE_BLOCK _ _ -> True + HEADING _ -> True + LIST _ -> True + ITEM -> True + TEXT _ -> False + SOFTBREAK -> False + LINEBREAK -> False + HTML_INLINE _ -> False + CUSTOM_INLINE _ _ -> False + CODE _ -> False + EMPH -> False + STRONG -> False + LINK _ _ -> False + IMAGE _ _ -> False + STRIKETHROUGH -> False + TABLE _ -> False + TABLE_ROW -> False + TABLE_CELL -> False + children :: Node -> [Node] children (Node _ _ ns) = ns @@ -121,6 +176,8 @@ addInline (Node _ EMPH nodes) = (Emph (addInlines nodes) :) addInline (Node _ STRONG nodes) = (Strong (addInlines nodes) :) +addInline (Node _ STRIKETHROUGH nodes) = + (Strikeout (addInlines nodes) :) addInline (Node _ (LINK url title) nodes) = (Link nullAttr (addInlines nodes) (unpack url, unpack title) :) addInline (Node _ (IMAGE url title) nodes) = diff --git a/src/Text/Pandoc/Readers/GFM.hs b/src/Text/Pandoc/Readers/GFM.hs deleted file mode 100644 index 1cdc47b98..000000000 --- a/src/Text/Pandoc/Readers/GFM.hs +++ /dev/null @@ -1,185 +0,0 @@ -{- -Copyright (C) 2017 John MacFarlane <jgm@berkeley.edu> - -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.GFM - Copyright : Copyright (C) 2017 John MacFarlane - License : GNU GPL, version 2 or above - - Maintainer : John MacFarlane <jgm@berkeley.edu> - Stability : alpha - Portability : portable - -Conversion of GitHub flavored CommonMark text to 'Pandoc' document. - -CommonMark is a strongly specified variant of Markdown: http://commonmark.org. --} -module Text.Pandoc.Readers.GFM (readGFM) -where - -import CMarkGFM -import Data.List (groupBy) -import Data.Text (Text, unpack) -import Text.Pandoc.Class (PandocMonad) -import Text.Pandoc.Definition -import Text.Pandoc.Options - --- | Parse a CommonMark formatted string into a 'Pandoc' structure. -readGFM :: PandocMonad m => ReaderOptions -> Text -> m Pandoc -readGFM opts s = return $ - nodeToPandoc $ commonmarkToNode opts' exts s - where opts' = [optSmart | enabled Ext_smart] - exts = [extStrikethrough, extTable, extAutolink] - enabled x = extensionEnabled x (readerExtensions opts) - -nodeToPandoc :: Node -> Pandoc -nodeToPandoc (Node _ DOCUMENT nodes) = - Pandoc nullMeta $ foldr addBlock [] nodes -nodeToPandoc n = -- shouldn't happen - Pandoc nullMeta $ foldr addBlock [] [n] - -addBlocks :: [Node] -> [Block] -addBlocks = foldr addBlock [] - -addBlock :: Node -> [Block] -> [Block] -addBlock (Node _ PARAGRAPH nodes) = - (Para (addInlines nodes) :) -addBlock (Node _ THEMATIC_BREAK _) = - (HorizontalRule :) -addBlock (Node _ BLOCK_QUOTE nodes) = - (BlockQuote (addBlocks nodes) :) -addBlock (Node _ (HTML_BLOCK t) _) = - (RawBlock (Format "html") (unpack t) :) --- Note: the cmark parser will never generate CUSTOM_BLOCK, --- so we don't need to handle it: -addBlock (Node _ (CUSTOM_BLOCK _onEnter _onExit) _nodes) = - id -addBlock (Node _ (CODE_BLOCK info t) _) = - (CodeBlock ("", take 1 (words (unpack info)), []) (unpack t) :) -addBlock (Node _ (HEADING lev) nodes) = - (Header lev ("",[],[]) (addInlines nodes) :) -addBlock (Node _ (LIST listAttrs) nodes) = - (constructor (map (setTightness . addBlocks . children) nodes) :) - where constructor = case listType listAttrs of - BULLET_LIST -> BulletList - ORDERED_LIST -> OrderedList - (start, DefaultStyle, delim) - start = listStart listAttrs - setTightness = if listTight listAttrs - then map paraToPlain - else id - paraToPlain (Para xs) = Plain (xs) - paraToPlain x = x - delim = case listDelim listAttrs of - PERIOD_DELIM -> Period - PAREN_DELIM -> OneParen -addBlock (Node _ (TABLE alignments) nodes) = do - (Table [] aligns widths headers rows :) - where aligns = map fromTableCellAlignment alignments - fromTableCellAlignment NoAlignment = AlignDefault - fromTableCellAlignment LeftAligned = AlignLeft - fromTableCellAlignment RightAligned = AlignRight - fromTableCellAlignment CenterAligned = AlignCenter - widths = replicate numcols 0.0 - numcols = if null rows' - then 0 - else maximum $ map length rows' - rows' = map toRow $ filter isRow nodes - (headers, rows) = case rows' of - (h:rs) -> (h, rs) - [] -> ([], []) - isRow (Node _ TABLE_ROW _) = True - isRow _ = False - isCell (Node _ TABLE_CELL _) = True - isCell _ = False - toRow (Node _ TABLE_ROW ns) = map toCell $ filter isCell ns - toRow (Node _ t _) = error $ "toRow encountered non-row " ++ show t - toCell (Node _ TABLE_CELL []) = [] - toCell (Node _ TABLE_CELL (n:ns)) - | isBlockNode n = addBlocks (n:ns) - | otherwise = [Plain (addInlines (n:ns))] - toCell (Node _ t _) = error $ "toCell encountered non-cell " ++ show t -addBlock (Node _ TABLE_ROW _) = id -- handled in TABLE -addBlock (Node _ TABLE_CELL _) = id -- handled in TABLE -addBlock _ = id - -isBlockNode :: Node -> Bool -isBlockNode (Node _ nodetype _) = - case nodetype of - DOCUMENT -> True - THEMATIC_BREAK -> True - PARAGRAPH -> True - BLOCK_QUOTE -> True - HTML_BLOCK _ -> True - CUSTOM_BLOCK _ _ -> True - CODE_BLOCK _ _ -> True - HEADING _ -> True - LIST _ -> True - ITEM -> True - TEXT _ -> False - SOFTBREAK -> False - LINEBREAK -> False - HTML_INLINE _ -> False - CUSTOM_INLINE _ _ -> False - CODE _ -> False - EMPH -> False - STRONG -> False - LINK _ _ -> False - IMAGE _ _ -> False - STRIKETHROUGH -> False - TABLE _ -> False - TABLE_ROW -> False - TABLE_CELL -> False - -children :: Node -> [Node] -children (Node _ _ ns) = ns - -addInlines :: [Node] -> [Inline] -addInlines = foldr addInline [] - -addInline :: Node -> [Inline] -> [Inline] -addInline (Node _ (TEXT t) _) = (map toinl clumps ++) - where raw = unpack t - clumps = groupBy samekind raw - samekind ' ' ' ' = True - samekind ' ' _ = False - samekind _ ' ' = False - samekind _ _ = True - toinl (' ':_) = Space - toinl xs = Str xs -addInline (Node _ LINEBREAK _) = (LineBreak :) -addInline (Node _ SOFTBREAK _) = (SoftBreak :) -addInline (Node _ (HTML_INLINE t) _) = - (RawInline (Format "html") (unpack t) :) --- Note: the cmark parser will never generate CUSTOM_BLOCK, --- so we don't need to handle it: -addInline (Node _ (CUSTOM_INLINE _onEnter _onExit) _nodes) = - id -addInline (Node _ (CODE t) _) = - (Code ("",[],[]) (unpack t) :) -addInline (Node _ EMPH nodes) = - (Emph (addInlines nodes) :) -addInline (Node _ STRONG nodes) = - (Strong (addInlines nodes) :) -addInline (Node _ STRIKETHROUGH nodes) = - (Strikeout (addInlines nodes) :) -addInline (Node _ (LINK url title) nodes) = - (Link nullAttr (addInlines nodes) (unpack url, unpack title) :) -addInline (Node _ (IMAGE url title) nodes) = - (Image nullAttr (addInlines nodes) (unpack url, unpack title) :) -addInline _ = id diff --git a/src/Text/Pandoc/Writers.hs b/src/Text/Pandoc/Writers.hs index c10d9149b..3e8729eb9 100644 --- a/src/Text/Pandoc/Writers.hs +++ b/src/Text/Pandoc/Writers.hs @@ -38,7 +38,6 @@ module Text.Pandoc.Writers , writeAsciiDoc , writeBeamer , writeCommonMark - , writeGFM , writeConTeXt , writeCustom , writeDZSlides @@ -90,7 +89,6 @@ import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Writers.AsciiDoc import Text.Pandoc.Writers.CommonMark -import Text.Pandoc.Writers.GFM import Text.Pandoc.Writers.ConTeXt import Text.Pandoc.Writers.Custom import Text.Pandoc.Writers.Docbook @@ -174,7 +172,7 @@ writers = [ ,("asciidoc" , TextWriter writeAsciiDoc) ,("haddock" , TextWriter writeHaddock) ,("commonmark" , TextWriter writeCommonMark) - ,("gfm" , TextWriter writeGFM) + ,("gfm" , TextWriter writeCommonMark) ,("tei" , TextWriter writeTEI) ,("muse" , TextWriter writeMuse) ] diff --git a/src/Text/Pandoc/Writers/CommonMark.hs b/src/Text/Pandoc/Writers/CommonMark.hs index 63249a7ce..75a18dcf4 100644 --- a/src/Text/Pandoc/Writers/CommonMark.hs +++ b/src/Text/Pandoc/Writers/CommonMark.hs @@ -31,7 +31,7 @@ CommonMark: <http://commonmark.org> -} module Text.Pandoc.Writers.CommonMark (writeCommonMark) where -import CMark +import CMarkGFM import Control.Monad.State.Strict (State, get, modify, runState) import Data.Foldable (foldrM) import Data.Text (Text) diff --git a/src/Text/Pandoc/Writers/GFM.hs b/src/Text/Pandoc/Writers/GFM.hs deleted file mode 100644 index d9806e2fa..000000000 --- a/src/Text/Pandoc/Writers/GFM.hs +++ /dev/null @@ -1,189 +0,0 @@ -{- -Copyright (C) 2015 John MacFarlane <jgm@berkeley.edu> - -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.Writers.GFM - Copyright : Copyright (C) 2017 John MacFarlane - License : GNU GPL, version 2 or above - - Maintainer : John MacFarlane <jgm@berkeley.edu> - Stability : alpha - Portability : portable - -Conversion of 'Pandoc' documents to CommonMark. - -CommonMark: <http://commonmark.org> --} -module Text.Pandoc.Writers.GFM (writeGFM) where - -import CMarkGFM -import Control.Monad.State.Strict (State, get, modify, runState) -import Data.Foldable (foldrM) -import Data.Text (Text) -import qualified Data.Text as T -import Text.Pandoc.Class (PandocMonad) -import Text.Pandoc.Definition -import Text.Pandoc.Options -import Text.Pandoc.Shared (isTightList, linesToPara) -import Text.Pandoc.Templates (renderTemplate') -import Text.Pandoc.Walk (walkM) -import Text.Pandoc.Writers.HTML (writeHtml5String) -import Text.Pandoc.Writers.Shared - --- | Convert Pandoc to GitHub flavored CommonMark. -writeGFM :: PandocMonad m => WriterOptions -> Pandoc -> m Text -writeGFM 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 - case writerTemplate opts of - Nothing -> return main - Just tpl -> renderTemplate' tpl context - -processNotes :: Inline -> State [[Block]] Inline -processNotes (Note bs) = do - modify (bs :) - notes <- get - return $ Str $ "[" ++ show (length notes) ++ "]" -processNotes x = return x - -node :: NodeType -> [Node] -> Node -node = Node Nothing - -blocksToCommonMark :: PandocMonad m => WriterOptions -> [Block] -> m Text -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 $ - nodeToCommonmark cmarkOpts colwidth $ - node DOCUMENT nodes - -inlinesToCommonMark :: PandocMonad m => WriterOptions -> [Inline] -> m Text -inlinesToCommonMark opts ils = return $ - nodeToCommonmark cmarkOpts colwidth $ node PARAGRAPH (inlinesToNodes ils) - where cmarkOpts = [optHardBreaks | isEnabled Ext_hard_line_breaks opts] - colwidth = if writerWrapText opts == WrapAuto - then Just $ writerColumns opts - else Nothing - -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 - dlToBullet (term, ((Plain xs : ys) : zs)) = - Plain (term ++ [LineBreak] ++ xs) : ys ++ concat zs - dlToBullet (term, xs) = - Para term : concat xs -blockToNodes t@(Table _ _ _ _ _) ns = do - s <- writeHtml5String def $! Pandoc nullMeta [t] - return (node (HTML_BLOCK s) [] : ns) -blockToNodes Null ns = return ns - -inlinesToNodes :: [Inline] -> [Node] -inlinesToNodes = foldr inlineToNodes [] - -inlineToNodes :: Inline -> [Node] -> [Node] -inlineToNodes (Str s) = (node (TEXT (T.pack s)) [] :) -inlineToNodes Space = (node (TEXT (T.pack " ")) [] :) -inlineToNodes LineBreak = (node LINEBREAK [] :) -inlineToNodes SoftBreak = (node SOFTBREAK [] :) -inlineToNodes (Emph xs) = (node EMPH (inlinesToNodes xs) :) -inlineToNodes (Strong xs) = (node STRONG (inlinesToNodes xs) :) -inlineToNodes (Strikeout xs) = - ((node (HTML_INLINE (T.pack "<s>")) [] : inlinesToNodes xs ++ - [node (HTML_INLINE (T.pack "</s>")) []]) ++ ) -inlineToNodes (Superscript xs) = - ((node (HTML_INLINE (T.pack "<sup>")) [] : inlinesToNodes xs ++ - [node (HTML_INLINE (T.pack "</sup>")) []]) ++ ) -inlineToNodes (Subscript xs) = - ((node (HTML_INLINE (T.pack "<sub>")) [] : inlinesToNodes xs ++ - [node (HTML_INLINE (T.pack "</sub>")) []]) ++ ) -inlineToNodes (SmallCaps xs) = - ((node (HTML_INLINE (T.pack "<span class=\"smallcaps\">")) [] - : inlinesToNodes xs ++ - [node (HTML_INLINE (T.pack "</span>")) []]) ++ ) -inlineToNodes (Link _ ils (url,tit)) = - (node (LINK (T.pack url) (T.pack tit)) (inlinesToNodes ils) :) -inlineToNodes (Image _ ils (url,tit)) = - (node (IMAGE (T.pack url) (T.pack tit)) (inlinesToNodes ils) :) -inlineToNodes (RawInline fmt xs) - | fmt == Format "html" = (node (HTML_INLINE (T.pack xs)) [] :) - | otherwise = (node (CUSTOM_INLINE (T.pack xs) (T.empty)) [] :) -inlineToNodes (Quoted qt ils) = - ((node (TEXT start) [] : inlinesToNodes ils ++ [node (TEXT end) []]) ++) - where (start, end) = case qt of - SingleQuote -> (T.pack "‘", T.pack "’") - DoubleQuote -> (T.pack "“", T.pack "”") -inlineToNodes (Code _ str) = (node (CODE (T.pack str)) [] :) -inlineToNodes (Math mt str) = - case mt of - InlineMath -> - (node (HTML_INLINE (T.pack ("\\(" ++ str ++ "\\)"))) [] :) - DisplayMath -> - (node (HTML_INLINE (T.pack ("\\[" ++ str ++ "\\]"))) [] :) -inlineToNodes (Span _ ils) = (inlinesToNodes ils ++) -inlineToNodes (Cite _ ils) = (inlinesToNodes ils ++) -inlineToNodes (Note _) = id -- should not occur --- we remove Note elements in preprocessing -- cgit v1.2.3 From d752f855827fc6901f851549f8b9029edb1e2177 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 7 Aug 2017 23:20:29 -0700 Subject: CommonMark reader: make exts depend on extensions. --- src/Text/Pandoc/Readers/CommonMark.hs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/CommonMark.hs b/src/Text/Pandoc/Readers/CommonMark.hs index 9a67c8597..4b24ea374 100644 --- a/src/Text/Pandoc/Readers/CommonMark.hs +++ b/src/Text/Pandoc/Readers/CommonMark.hs @@ -43,8 +43,10 @@ import Text.Pandoc.Options readCommonMark :: PandocMonad m => ReaderOptions -> Text -> m Pandoc readCommonMark opts s = return $ nodeToPandoc $ commonmarkToNode opts' exts s - where opts' = [optSmart | enabled Ext_smart] - exts = [extStrikethrough, extTable, extAutolink] + where opts' = [ optSmart | enabled Ext_smart ] + exts = [ extStrikethrough | enabled Ext_strikeout ] ++ + [ extTable | enabled Ext_pipe_tables ] ++ + [ extAutolink | enabled Ext_autolink_bare_uris ] enabled x = extensionEnabled x (readerExtensions opts) nodeToPandoc :: Node -> Pandoc -- cgit v1.2.3 From c95cc813ccfbe507f30ace5e8d6d0f6787e75db0 Mon Sep 17 00:00:00 2001 From: Alexander <ilabdsf@gmail.com> Date: Tue, 8 Aug 2017 18:44:18 +0300 Subject: Fix `blanklines' documentation (#3843) --- src/Text/Pandoc/Pretty.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Pretty.hs b/src/Text/Pandoc/Pretty.hs index 1b3c647a1..b5600ad39 100644 --- a/src/Text/Pandoc/Pretty.hs +++ b/src/Text/Pandoc/Pretty.hs @@ -398,8 +398,8 @@ cr = Doc $ singleton CarriageReturn blankline :: Doc blankline = Doc $ singleton (BlankLines 1) --- | Inserts a blank lines unless they exists already. --- (@blanklines m <> blanklines n@ has the same effect as @blankline (max m n)@. +-- | Inserts blank lines unless they exist already. +-- (@blanklines m <> blanklines n@ has the same effect as @blanklines (max m n)@. blanklines :: Int -> Doc blanklines n = Doc $ singleton (BlankLines n) -- cgit v1.2.3 From 56a680c30583d56cfe847b8067f6bf6a7f764794 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 8 Aug 2017 09:14:13 -0700 Subject: CommonMark writer: support table, strikethrough extensions... when enabled (as with gfm). Note: because of limitations in cmark-gfm, which will hopefully soon be corrected, this currently gives an error on Tables. Also properly support `--wrap=none`. --- src/Text/Pandoc/Writers/CommonMark.hs | 149 ++++++++++++++++++++-------------- 1 file changed, 90 insertions(+), 59 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/CommonMark.hs b/src/Text/Pandoc/Writers/CommonMark.hs index 75a18dcf4..fa838a503 100644 --- a/src/Text/Pandoc/Writers/CommonMark.hs +++ b/src/Text/Pandoc/Writers/CommonMark.hs @@ -34,6 +34,7 @@ module Text.Pandoc.Writers.CommonMark (writeCommonMark) where import CMarkGFM import Control.Monad.State.Strict (State, get, modify, runState) import Data.Foldable (foldrM) +import Data.Monoid (Any (..)) import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Class (PandocMonad) @@ -41,7 +42,7 @@ import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Shared (isTightList, linesToPara) import Text.Pandoc.Templates (renderTemplate') -import Text.Pandoc.Walk (walkM) +import Text.Pandoc.Walk (walkM, walk, query) import Text.Pandoc.Writers.HTML (writeHtml5String) import Text.Pandoc.Writers.Shared @@ -52,7 +53,12 @@ writeCommonMark opts (Pandoc meta blocks) = do notes' = if null notes then [] else [OrderedList (1, Decimal, Period) $ reverse notes] - main <- blocksToCommonMark opts (blocks' ++ notes') + let softBreakToSpace SoftBreak = Space + softBreakToSpace x = x + let blocks'' = if writerWrapText opts == WrapNone + then walk softBreakToSpace blocks' + else blocks' + main <- blocksToCommonMark opts (blocks'' ++ notes') metadata <- metaToJSON opts (blocksToCommonMark opts) (inlinesToCommonMark opts) @@ -78,43 +84,46 @@ blocksToCommonMark opts bs = do colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing - nodes <- blocksToNodes bs + nodes <- blocksToNodes opts bs return $ nodeToCommonmark cmarkOpts colwidth $ node DOCUMENT nodes inlinesToCommonMark :: PandocMonad m => WriterOptions -> [Inline] -> m Text inlinesToCommonMark opts ils = return $ - nodeToCommonmark cmarkOpts colwidth $ node PARAGRAPH (inlinesToNodes ils) + nodeToCommonmark cmarkOpts colwidth $ + node PARAGRAPH (inlinesToNodes opts ils) where cmarkOpts = [optHardBreaks | isEnabled Ext_hard_line_breaks opts] colwidth = if writerWrapText opts == WrapAuto then Just $ writerColumns opts else Nothing -blocksToNodes :: PandocMonad m => [Block] -> m [Node] -blocksToNodes = foldrM blockToNodes [] +blocksToNodes :: PandocMonad m => WriterOptions -> [Block] -> m [Node] +blocksToNodes opts = foldrM (blockToNodes opts) [] -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 $ +blockToNodes :: PandocMonad m => WriterOptions -> Block -> [Node] -> m [Node] +blockToNodes opts (Plain xs) ns = + return (node PARAGRAPH (inlinesToNodes opts xs) : ns) +blockToNodes opts (Para xs) ns = + return (node PARAGRAPH (inlinesToNodes opts xs) : ns) +blockToNodes opts (LineBlock lns) ns = blockToNodes opts (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 +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 +blockToNodes opts (BlockQuote bs) ns = do + nodes <- blocksToNodes opts bs return (node BLOCK_QUOTE nodes : ns) -blockToNodes (BulletList items) ns = do - nodes <- mapM blocksToNodes items +blockToNodes opts (BulletList items) ns = do + nodes <- mapM (blocksToNodes opts) 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 +blockToNodes opts (OrderedList (start, _sty, delim) items) ns = do + nodes <- mapM (blocksToNodes opts) items return (node (LIST ListAttributes{ listType = ORDERED_LIST, listDelim = case delim of @@ -123,12 +132,14 @@ blockToNodes (OrderedList (start, _sty, delim) items) ns = do _ -> 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 +blockToNodes _ HorizontalRule ns = return (node THEMATIC_BREAK [] : ns) +blockToNodes opts (Header lev _ ils) ns = + return (node (HEADING lev) (inlinesToNodes opts ils) : ns) +blockToNodes opts (Div _ bs) ns = do + nodes <- blocksToNodes opts bs return (nodes ++ ns) -blockToNodes (DefinitionList items) ns = blockToNodes (BulletList items') ns +blockToNodes opts (DefinitionList items) ns = + blockToNodes opts (BulletList items') ns where items' = map dlToBullet items dlToBullet (term, ((Para xs : ys) : zs)) = Para (term ++ [LineBreak] ++ xs) : ys ++ concat zs @@ -136,54 +147,74 @@ blockToNodes (DefinitionList items) ns = blockToNodes (BulletList items') ns Plain (term ++ [LineBreak] ++ xs) : ys ++ concat zs dlToBullet (term, xs) = Para term : concat xs -blockToNodes t@(Table _ _ _ _ _) ns = do - s <- writeHtml5String def $! Pandoc nullMeta [t] - return (node (HTML_BLOCK s) [] : ns) -blockToNodes Null ns = return ns - -inlinesToNodes :: [Inline] -> [Node] -inlinesToNodes = foldr inlineToNodes [] - -inlineToNodes :: Inline -> [Node] -> [Node] -inlineToNodes (Str s) = (node (TEXT (T.pack s)) [] :) -inlineToNodes Space = (node (TEXT (T.pack " ")) [] :) -inlineToNodes LineBreak = (node LINEBREAK [] :) -inlineToNodes SoftBreak = (node SOFTBREAK [] :) -inlineToNodes (Emph xs) = (node EMPH (inlinesToNodes xs) :) -inlineToNodes (Strong xs) = (node STRONG (inlinesToNodes xs) :) -inlineToNodes (Strikeout xs) = - ((node (HTML_INLINE (T.pack "<s>")) [] : inlinesToNodes xs ++ - [node (HTML_INLINE (T.pack "</s>")) []]) ++ ) -inlineToNodes (Superscript xs) = - ((node (HTML_INLINE (T.pack "<sup>")) [] : inlinesToNodes xs ++ +blockToNodes opts t@(Table _capt aligns widths headers rows) ns = do + let allrows = headers:rows + let isLineBreak LineBreak = Any True + isLineBreak _ = Any False + let isSimple = all (==0) widths && + not ( getAny (query isLineBreak allrows) ) + if isEnabled Ext_pipe_tables opts && isSimple + then do + let toAlign AlignDefault = NoAlignment + toAlign AlignLeft = LeftAligned + toAlign AlignCenter = CenterAligned + toAlign AlignRight = RightAligned + let aligns' = map toAlign aligns + let toCell bs = node TABLE_CELL <$> blocksToNodes opts bs + let toRow cells = node TABLE_ROW <$> mapM toCell cells + cmrows <- mapM toRow allrows + return (node (TABLE aligns') cmrows : ns) + else do -- fall back to raw HTML + s <- writeHtml5String def $! Pandoc nullMeta [t] + return (node (HTML_BLOCK s) [] : ns) +blockToNodes _ Null ns = return ns + +inlinesToNodes :: WriterOptions -> [Inline] -> [Node] +inlinesToNodes opts = foldr (inlineToNodes opts) [] + +inlineToNodes :: WriterOptions -> Inline -> [Node] -> [Node] +inlineToNodes _ (Str s) = (node (TEXT (T.pack s)) [] :) +inlineToNodes _ Space = (node (TEXT (T.pack " ")) [] :) +inlineToNodes _ LineBreak = (node LINEBREAK [] :) +inlineToNodes _ SoftBreak = (node SOFTBREAK [] :) +inlineToNodes opts (Emph xs) = (node EMPH (inlinesToNodes opts xs) :) +inlineToNodes opts (Strong xs) = (node STRONG (inlinesToNodes opts xs) :) +inlineToNodes opts (Strikeout xs) = + if isEnabled Ext_strikeout opts + then (node STRIKETHROUGH (inlinesToNodes opts xs) :) + else ((node (HTML_INLINE (T.pack "<s>")) [] : inlinesToNodes opts xs ++ + [node (HTML_INLINE (T.pack "</s>")) []]) ++ ) +inlineToNodes opts (Superscript xs) = + ((node (HTML_INLINE (T.pack "<sup>")) [] : inlinesToNodes opts xs ++ [node (HTML_INLINE (T.pack "</sup>")) []]) ++ ) -inlineToNodes (Subscript xs) = - ((node (HTML_INLINE (T.pack "<sub>")) [] : inlinesToNodes xs ++ +inlineToNodes opts (Subscript xs) = + ((node (HTML_INLINE (T.pack "<sub>")) [] : inlinesToNodes opts xs ++ [node (HTML_INLINE (T.pack "</sub>")) []]) ++ ) -inlineToNodes (SmallCaps xs) = +inlineToNodes opts (SmallCaps xs) = ((node (HTML_INLINE (T.pack "<span class=\"smallcaps\">")) [] - : inlinesToNodes xs ++ + : inlinesToNodes opts xs ++ [node (HTML_INLINE (T.pack "</span>")) []]) ++ ) -inlineToNodes (Link _ ils (url,tit)) = - (node (LINK (T.pack url) (T.pack tit)) (inlinesToNodes ils) :) -inlineToNodes (Image _ ils (url,tit)) = - (node (IMAGE (T.pack url) (T.pack tit)) (inlinesToNodes ils) :) -inlineToNodes (RawInline fmt xs) +inlineToNodes opts (Link _ ils (url,tit)) = + (node (LINK (T.pack url) (T.pack tit)) (inlinesToNodes opts ils) :) +inlineToNodes opts (Image _ ils (url,tit)) = + (node (IMAGE (T.pack url) (T.pack tit)) (inlinesToNodes opts ils) :) +inlineToNodes _ (RawInline fmt xs) | fmt == Format "html" = (node (HTML_INLINE (T.pack xs)) [] :) | otherwise = (node (CUSTOM_INLINE (T.pack xs) (T.empty)) [] :) -inlineToNodes (Quoted qt ils) = - ((node (TEXT start) [] : inlinesToNodes ils ++ [node (TEXT end) []]) ++) +inlineToNodes opts (Quoted qt ils) = + ((node (TEXT start) [] : + inlinesToNodes opts ils ++ [node (TEXT end) []]) ++) where (start, end) = case qt of SingleQuote -> (T.pack "‘", T.pack "’") DoubleQuote -> (T.pack "“", T.pack "”") -inlineToNodes (Code _ str) = (node (CODE (T.pack str)) [] :) -inlineToNodes (Math mt str) = +inlineToNodes _ (Code _ str) = (node (CODE (T.pack str)) [] :) +inlineToNodes _ (Math mt str) = case mt of InlineMath -> (node (HTML_INLINE (T.pack ("\\(" ++ str ++ "\\)"))) [] :) DisplayMath -> (node (HTML_INLINE (T.pack ("\\[" ++ str ++ "\\]"))) [] :) -inlineToNodes (Span _ ils) = (inlinesToNodes ils ++) -inlineToNodes (Cite _ ils) = (inlinesToNodes ils ++) -inlineToNodes (Note _) = id -- should not occur +inlineToNodes opts (Span _ ils) = (inlinesToNodes opts ils ++) +inlineToNodes opts (Cite _ ils) = (inlinesToNodes opts ils ++) +inlineToNodes _ (Note _) = id -- should not occur -- we remove Note elements in preprocessing -- cgit v1.2.3 From 312349bbcc698d5e2e3e652eb858a35bedd42a18 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 8 Aug 2017 11:01:05 -0700 Subject: CommonMark writer: Support pipe tables. We bypass the commonmark writer from cmark and construct our own pipe tables, with better results. (Note also that cmark-gfm currently doesn't support rendering table nodes; see kivikakk/cmark-gfm-hs#3.) --- src/Text/Pandoc/Writers/CommonMark.hs | 91 +++++++++++++++++++++++++++++------ 1 file changed, 75 insertions(+), 16 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/CommonMark.hs b/src/Text/Pandoc/Writers/CommonMark.hs index fa838a503..b268f5315 100644 --- a/src/Text/Pandoc/Writers/CommonMark.hs +++ b/src/Text/Pandoc/Writers/CommonMark.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE OverloadedStrings #-} {- Copyright (C) 2015 John MacFarlane <jgm@berkeley.edu> @@ -34,13 +35,14 @@ module Text.Pandoc.Writers.CommonMark (writeCommonMark) where import CMarkGFM import Control.Monad.State.Strict (State, get, modify, runState) import Data.Foldable (foldrM) -import Data.Monoid (Any (..)) +import Data.Monoid (Any (..), (<>)) +import Data.List (transpose) import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Options -import Text.Pandoc.Shared (isTightList, linesToPara) +import Text.Pandoc.Shared (isTightList, linesToPara, substitute) import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Walk (walkM, walk, query) import Text.Pandoc.Writers.HTML (writeHtml5String) @@ -53,8 +55,6 @@ writeCommonMark opts (Pandoc meta blocks) = do notes' = if null notes then [] else [OrderedList (1, Decimal, Period) $ reverse notes] - let softBreakToSpace SoftBreak = Space - softBreakToSpace x = x let blocks'' = if writerWrapText opts == WrapNone then walk softBreakToSpace blocks' else blocks' @@ -68,6 +68,10 @@ writeCommonMark opts (Pandoc meta blocks) = do Nothing -> return main Just tpl -> renderTemplate' tpl context +softBreakToSpace :: Inline -> Inline +softBreakToSpace SoftBreak = Space +softBreakToSpace x = x + processNotes :: Inline -> State [[Block]] Inline processNotes (Note bs) = do modify (bs :) @@ -147,23 +151,78 @@ blockToNodes opts (DefinitionList items) ns = Plain (term ++ [LineBreak] ++ xs) : ys ++ concat zs dlToBullet (term, xs) = Para term : concat xs -blockToNodes opts t@(Table _capt aligns widths headers rows) ns = do - let allrows = headers:rows +blockToNodes opts t@(Table capt aligns widths headers rows) ns = do + let allcells = concat (headers:rows) let isLineBreak LineBreak = Any True isLineBreak _ = Any False + let isPlainOrPara [Para _] = True + isPlainOrPara [Plain _] = True + isPlainOrPara [] = True + isPlainOrPara _ = False let isSimple = all (==0) widths && - not ( getAny (query isLineBreak allrows) ) + all isPlainOrPara allcells && + not ( getAny (query isLineBreak allcells) ) if isEnabled Ext_pipe_tables opts && isSimple then do - let toAlign AlignDefault = NoAlignment - toAlign AlignLeft = LeftAligned - toAlign AlignCenter = CenterAligned - toAlign AlignRight = RightAligned - let aligns' = map toAlign aligns - let toCell bs = node TABLE_CELL <$> blocksToNodes opts bs - let toRow cells = node TABLE_ROW <$> mapM toCell cells - cmrows <- mapM toRow allrows - return (node (TABLE aligns') cmrows : ns) + -- We construct a table manually as a CUSTOM_BLOCK, for + -- two reasons: (1) cmark-gfm currently doesn't support + -- rendering TABLE nodes; (2) we can align the column sides; + -- (3) we can render the caption as a regular paragraph. + let capt' = node PARAGRAPH (inlinesToNodes opts capt) + -- backslash | in code and raw: + let fixPipe (Code attr xs) = + Code attr (substitute "|" "\\|" xs) + fixPipe (RawInline format xs) = + RawInline format (substitute "|" "\\|" xs) + fixPipe x = x + let toCell [Plain ils] = T.strip + $ nodeToCommonmark [] Nothing + $ node (CUSTOM_INLINE mempty mempty) + $ inlinesToNodes opts + $ walk (fixPipe . softBreakToSpace) ils + toCell [Para ils] = T.strip + $ nodeToCommonmark [] Nothing + $ node (CUSTOM_INLINE mempty mempty) + $ inlinesToNodes opts + $ walk (fixPipe . softBreakToSpace) ils + toCell [] = "" + toCell xs = error $ "toCell encountered " ++ show xs + let separator = " | " + let starter = "| " + let ender = " |" + let rawheaders = map toCell headers + let rawrows = map (map toCell) rows + let maximum' [] = 0 + maximum' xs = maximum xs + let colwidths = map (maximum' . map T.length) $ + transpose (rawheaders:rawrows) + let toHeaderLine len AlignDefault = T.replicate len "-" + toHeaderLine len AlignLeft = ":" <> + T.replicate (max (len - 1) 1) "-" + toHeaderLine len AlignRight = + T.replicate (max (len - 1) 1) "-" <> ":" + toHeaderLine len AlignCenter = ":" <> + T.replicate (max (len - 2) 1) (T.pack "-") <> ":" + let rawheaderlines = zipWith toHeaderLine colwidths aligns + let headerlines = starter <> T.intercalate separator rawheaderlines <> + ender + let padContent (align, w) t' = + let padding = w - T.length t' + halfpadding = padding `div` 2 + in case align of + AlignRight -> T.replicate padding " " <> t' + AlignCenter -> T.replicate halfpadding " " <> t' <> + T.replicate (padding - halfpadding) " " + _ -> t' <> T.replicate padding " " + let toRow xs = starter <> T.intercalate separator + (zipWith padContent (zip aligns colwidths) xs) <> + ender + let table' = toRow rawheaders <> "\n" <> headerlines <> "\n" <> + T.intercalate "\n" (map toRow rawrows) + return (node (CUSTOM_BLOCK table' mempty) [] : + if null capt + then ns + else capt' : ns) else do -- fall back to raw HTML s <- writeHtml5String def $! Pandoc nullMeta [t] return (node (HTML_BLOCK s) [] : ns) -- cgit v1.2.3 From 73caf92871a2fb40b6f092e90a86d366eca630cb Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 8 Aug 2017 11:33:13 -0700 Subject: CommonMark reader: support `gfm_auto_identifiers`. Added `Ext_gfm_auto_identifiers`: new constructor for `Extension` in `Text.Pandoc.Extensions` [API change]. Use this in githubExtensions. Closes #2821. --- src/Text/Pandoc/Extensions.hs | 4 +++- src/Text/Pandoc/Readers/CommonMark.hs | 31 +++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 9e49c5907..e6a3ca044 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -133,6 +133,8 @@ data Extension = | Ext_abbreviations -- ^ PHP markdown extra abbreviation definitions | Ext_emoji -- ^ Support emoji like :smile: | Ext_auto_identifiers -- ^ Automatic identifiers for headers + | Ext_gfm_auto_identifiers -- ^ Automatic identifiers for headers, + -- using GitHub's method for generating identifiers | Ext_ascii_identifiers -- ^ ascii-only identifiers for headers | Ext_header_attributes -- ^ Explicit header attributes {#id .class k=v} | Ext_mmd_header_identifiers -- ^ Multimarkdown style header identifiers [myid] @@ -237,7 +239,7 @@ githubMarkdownExtensions = extensionsFromList , Ext_pipe_tables , Ext_raw_html , Ext_fenced_code_blocks - , Ext_auto_identifiers + , Ext_gfm_auto_identifiers , Ext_ascii_identifiers , Ext_backtick_code_blocks , Ext_autolink_bare_uris diff --git a/src/Text/Pandoc/Readers/CommonMark.hs b/src/Text/Pandoc/Readers/CommonMark.hs index 4b24ea374..ea9d696cb 100644 --- a/src/Text/Pandoc/Readers/CommonMark.hs +++ b/src/Text/Pandoc/Readers/CommonMark.hs @@ -33,15 +33,23 @@ module Text.Pandoc.Readers.CommonMark (readCommonMark) where import CMarkGFM +import Control.Monad.State +import Data.Char (isLetter, isAlphaNum, isSpace, toLower) import Data.List (groupBy) import Data.Text (Text, unpack) +import qualified Data.Map as Map import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Options +import Text.Pandoc.Shared (stringify) +import Text.Pandoc.Walk (walkM) -- | Parse a CommonMark formatted string into a 'Pandoc' structure. readCommonMark :: PandocMonad m => ReaderOptions -> Text -> m Pandoc readCommonMark opts s = return $ + (if enabled Ext_gfm_auto_identifiers + then addHeaderIdentifiers + else id) $ nodeToPandoc $ commonmarkToNode opts' exts s where opts' = [ optSmart | enabled Ext_smart ] exts = [ extStrikethrough | enabled Ext_strikeout ] ++ @@ -49,6 +57,29 @@ readCommonMark opts s = return $ [ extAutolink | enabled Ext_autolink_bare_uris ] enabled x = extensionEnabled x (readerExtensions opts) +addHeaderIdentifiers :: Pandoc -> Pandoc +addHeaderIdentifiers doc = evalState (walkM addHeaderId doc) mempty + +addHeaderId :: Block -> State (Map.Map String Int) Block +addHeaderId (Header lev (_,classes,kvs) ils) = do + idmap <- get + let ident = toIdent ils + ident' <- case Map.lookup ident idmap of + Nothing -> do + put (Map.insert ident 1 idmap) + return ident + Just i -> do + put (Map.adjust (+ 1) ident idmap) + return (ident ++ "-" ++ show i) + return $ Header lev (ident',classes,kvs) ils +addHeaderId x = return x + +toIdent :: [Inline] -> String +toIdent = map (\c -> if isSpace c then '-' else c) + . filter (\c -> isLetter c || isAlphaNum c || isSpace c || + c == '_' || c == '-') + . map toLower . stringify + nodeToPandoc :: Node -> Pandoc nodeToPandoc (Node _ DOCUMENT nodes) = Pandoc nullMeta $ foldr addBlock [] nodes -- cgit v1.2.3 From 714d8a6377b83ce9e8b5ef5b721cc1f196341438 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 8 Aug 2017 12:05:20 -0700 Subject: CommonMark reader: support `emoji` extension. --- src/Text/Pandoc/Readers/CommonMark.hs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/CommonMark.hs b/src/Text/Pandoc/Readers/CommonMark.hs index ea9d696cb..4a5081d65 100644 --- a/src/Text/Pandoc/Readers/CommonMark.hs +++ b/src/Text/Pandoc/Readers/CommonMark.hs @@ -40,9 +40,10 @@ import Data.Text (Text, unpack) import qualified Data.Map as Map import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Definition +import Text.Pandoc.Emoji (emojis) import Text.Pandoc.Options import Text.Pandoc.Shared (stringify) -import Text.Pandoc.Walk (walkM) +import Text.Pandoc.Walk (walkM, walk) -- | Parse a CommonMark formatted string into a 'Pandoc' structure. readCommonMark :: PandocMonad m => ReaderOptions -> Text -> m Pandoc @@ -50,6 +51,9 @@ readCommonMark opts s = return $ (if enabled Ext_gfm_auto_identifiers then addHeaderIdentifiers else id) $ + (if enabled Ext_emoji + then addEmojis + else id) $ nodeToPandoc $ commonmarkToNode opts' exts s where opts' = [ optSmart | enabled Ext_smart ] exts = [ extStrikethrough | enabled Ext_strikeout ] ++ @@ -57,6 +61,20 @@ readCommonMark opts s = return $ [ extAutolink | enabled Ext_autolink_bare_uris ] enabled x = extensionEnabled x (readerExtensions opts) +addEmojis :: Pandoc -> Pandoc +addEmojis = walk go + where go (Str xs) = Str (convertEmojis xs) + go x = x + convertEmojis (':':xs) = + case break (==':') xs of + (ys,':':zs) -> + case Map.lookup ys emojis of + Just s -> s ++ convertEmojis zs + Nothing -> ':' : ys ++ convertEmojis (':':zs) + _ -> ':':xs + convertEmojis (x:xs) = x : convertEmojis xs + convertEmojis [] = [] + addHeaderIdentifiers :: Pandoc -> Pandoc addHeaderIdentifiers doc = evalState (walkM addHeaderId doc) mempty -- cgit v1.2.3 From ccd4f13a4accf0366072a93b2843706e60a89a5e Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 8 Aug 2017 13:16:45 -0700 Subject: Writers.Shared.unsmartify: undo literal double curly quotes. Previously we left these. --- src/Text/Pandoc/Writers/Shared.hs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index 3f612f40a..8369bc09c 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -214,6 +214,9 @@ unsmartify opts ('\8211':xs) unsmartify opts ('\8212':xs) | isEnabled Ext_old_dashes opts = "--" ++ unsmartify opts xs | otherwise = "---" ++ unsmartify opts xs +unsmartify opts ('\8220':xs) = '"' : unsmartify opts xs +unsmartify opts ('\8221':xs) = '"' : unsmartify opts xs +unsmartify opts ('\8216':xs) = '\'' : unsmartify opts xs unsmartify opts (x:xs) = x : unsmartify opts xs unsmartify _ [] = [] -- cgit v1.2.3 From b6f7c4930bf13ab9b25b18869f3bde5f87144497 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 8 Aug 2017 13:17:29 -0700 Subject: CommonMark writer: support `hard_line_breaks`, `smart`. Add tests. --- src/Text/Pandoc/Writers/CommonMark.hs | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/CommonMark.hs b/src/Text/Pandoc/Writers/CommonMark.hs index b268f5315..20a22e051 100644 --- a/src/Text/Pandoc/Writers/CommonMark.hs +++ b/src/Text/Pandoc/Writers/CommonMark.hs @@ -55,10 +55,7 @@ writeCommonMark opts (Pandoc meta blocks) = do notes' = if null notes then [] else [OrderedList (1, Decimal, Period) $ reverse notes] - let blocks'' = if writerWrapText opts == WrapNone - then walk softBreakToSpace blocks' - else blocks' - main <- blocksToCommonMark opts (blocks'' ++ notes') + main <- blocksToCommonMark opts (blocks' ++ notes') metadata <- metaToJSON opts (blocksToCommonMark opts) (inlinesToCommonMark opts) @@ -232,10 +229,16 @@ inlinesToNodes :: WriterOptions -> [Inline] -> [Node] inlinesToNodes opts = foldr (inlineToNodes opts) [] inlineToNodes :: WriterOptions -> Inline -> [Node] -> [Node] -inlineToNodes _ (Str s) = (node (TEXT (T.pack s)) [] :) +inlineToNodes opts (Str s) = (node (TEXT (T.pack s')) [] :) + where s' = if isEnabled Ext_smart opts + then unsmartify opts s + else s inlineToNodes _ Space = (node (TEXT (T.pack " ")) [] :) inlineToNodes _ LineBreak = (node LINEBREAK [] :) -inlineToNodes _ SoftBreak = (node SOFTBREAK [] :) +inlineToNodes opts SoftBreak + | isEnabled Ext_hard_line_breaks opts = (node LINEBREAK [] :) + | writerWrapText opts == WrapNone = (node (TEXT " ") [] :) + | otherwise = (node SOFTBREAK [] :) inlineToNodes opts (Emph xs) = (node EMPH (inlinesToNodes opts xs) :) inlineToNodes opts (Strong xs) = (node STRONG (inlinesToNodes opts xs) :) inlineToNodes opts (Strikeout xs) = @@ -264,8 +267,12 @@ inlineToNodes opts (Quoted qt ils) = ((node (TEXT start) [] : inlinesToNodes opts ils ++ [node (TEXT end) []]) ++) where (start, end) = case qt of - SingleQuote -> (T.pack "‘", T.pack "’") - DoubleQuote -> (T.pack "“", T.pack "”") + SingleQuote + | isEnabled Ext_smart opts -> ("'","'") + | otherwise -> ("‘", "’") + DoubleQuote + | isEnabled Ext_smart opts -> ("\"", "\"") + | otherwise -> ("“", "”") inlineToNodes _ (Code _ str) = (node (CODE (T.pack str)) [] :) inlineToNodes _ (Math mt str) = case mt of -- cgit v1.2.3 From 54658b923a6660336e7f6583d5416f786a54473a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 8 Aug 2017 13:30:53 -0700 Subject: Support `hard_line_breaks` in CommonMark reader. --- src/Text/Pandoc/Readers/CommonMark.hs | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/CommonMark.hs b/src/Text/Pandoc/Readers/CommonMark.hs index 4a5081d65..14be946e6 100644 --- a/src/Text/Pandoc/Readers/CommonMark.hs +++ b/src/Text/Pandoc/Readers/CommonMark.hs @@ -54,6 +54,9 @@ readCommonMark opts s = return $ (if enabled Ext_emoji then addEmojis else id) $ + (if enabled Ext_hard_line_breaks + then walk softToHardBreaks + else id) $ nodeToPandoc $ commonmarkToNode opts' exts s where opts' = [ optSmart | enabled Ext_smart ] exts = [ extStrikethrough | enabled Ext_strikeout ] ++ @@ -61,6 +64,10 @@ readCommonMark opts s = return $ [ extAutolink | enabled Ext_autolink_bare_uris ] enabled x = extensionEnabled x (readerExtensions opts) +softToHardBreaks :: Inline -> Inline +softToHardBreaks SoftBreak = LineBreak +softToHardBreaks x = x + addEmojis :: Pandoc -> Pandoc addEmojis = walk go where go (Str xs) = Str (convertEmojis xs) -- cgit v1.2.3 From 3752298d917f101ac0279b7fc057c38d765f1770 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 8 Aug 2017 13:55:19 -0700 Subject: Thread options through CommonMark reader. This is more efficient than doing AST traversals for emojis and hard breaks. Also make behavior sensitive to `raw_html` extension. --- src/Text/Pandoc/Readers/CommonMark.hs | 158 +++++++++++++++++----------------- 1 file changed, 77 insertions(+), 81 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/CommonMark.hs b/src/Text/Pandoc/Readers/CommonMark.hs index 14be946e6..2dba18c9f 100644 --- a/src/Text/Pandoc/Readers/CommonMark.hs +++ b/src/Text/Pandoc/Readers/CommonMark.hs @@ -43,44 +43,34 @@ import Text.Pandoc.Definition import Text.Pandoc.Emoji (emojis) import Text.Pandoc.Options import Text.Pandoc.Shared (stringify) -import Text.Pandoc.Walk (walkM, walk) +import Text.Pandoc.Walk (walkM) -- | Parse a CommonMark formatted string into a 'Pandoc' structure. readCommonMark :: PandocMonad m => ReaderOptions -> Text -> m Pandoc readCommonMark opts s = return $ - (if enabled Ext_gfm_auto_identifiers + (if enabled Ext_gfm_auto_identifiers opts then addHeaderIdentifiers else id) $ - (if enabled Ext_emoji - then addEmojis - else id) $ - (if enabled Ext_hard_line_breaks - then walk softToHardBreaks - else id) $ - nodeToPandoc $ commonmarkToNode opts' exts s - where opts' = [ optSmart | enabled Ext_smart ] - exts = [ extStrikethrough | enabled Ext_strikeout ] ++ - [ extTable | enabled Ext_pipe_tables ] ++ - [ extAutolink | enabled Ext_autolink_bare_uris ] - enabled x = extensionEnabled x (readerExtensions opts) - -softToHardBreaks :: Inline -> Inline -softToHardBreaks SoftBreak = LineBreak -softToHardBreaks x = x - -addEmojis :: Pandoc -> Pandoc -addEmojis = walk go - where go (Str xs) = Str (convertEmojis xs) - go x = x - convertEmojis (':':xs) = - case break (==':') xs of - (ys,':':zs) -> - case Map.lookup ys emojis of - Just s -> s ++ convertEmojis zs - Nothing -> ':' : ys ++ convertEmojis (':':zs) - _ -> ':':xs - convertEmojis (x:xs) = x : convertEmojis xs - convertEmojis [] = [] + nodeToPandoc opts $ commonmarkToNode opts' exts s + where opts' = [ optSmart | enabled Ext_smart opts ] + exts = [ extStrikethrough | enabled Ext_strikeout opts ] ++ + [ extTable | enabled Ext_pipe_tables opts ] ++ + [ extAutolink | enabled Ext_autolink_bare_uris opts ] + +-- | Returns True if the given extension is enabled. +enabled :: Extension -> ReaderOptions -> Bool +enabled ext opts = ext `extensionEnabled` (readerExtensions opts) + +convertEmojis :: String -> String +convertEmojis (':':xs) = + case break (==':') xs of + (ys,':':zs) -> + case Map.lookup ys emojis of + Just s -> s ++ convertEmojis zs + Nothing -> ':' : ys ++ convertEmojis (':':zs) + _ -> ':':xs +convertEmojis (x:xs) = x : convertEmojis xs +convertEmojis [] = [] addHeaderIdentifiers :: Pandoc -> Pandoc addHeaderIdentifiers doc = evalState (walkM addHeaderId doc) mempty @@ -105,34 +95,35 @@ toIdent = map (\c -> if isSpace c then '-' else c) c == '_' || c == '-') . map toLower . stringify -nodeToPandoc :: Node -> Pandoc -nodeToPandoc (Node _ DOCUMENT nodes) = - Pandoc nullMeta $ foldr addBlock [] nodes -nodeToPandoc n = -- shouldn't happen - Pandoc nullMeta $ foldr addBlock [] [n] +nodeToPandoc :: ReaderOptions -> Node -> Pandoc +nodeToPandoc opts (Node _ DOCUMENT nodes) = + Pandoc nullMeta $ foldr (addBlock opts) [] nodes +nodeToPandoc opts n = -- shouldn't happen + Pandoc nullMeta $ foldr (addBlock opts) [] [n] -addBlocks :: [Node] -> [Block] -addBlocks = foldr addBlock [] +addBlocks :: ReaderOptions -> [Node] -> [Block] +addBlocks opts = foldr (addBlock opts) [] -addBlock :: Node -> [Block] -> [Block] -addBlock (Node _ PARAGRAPH nodes) = - (Para (addInlines nodes) :) -addBlock (Node _ THEMATIC_BREAK _) = +addBlock :: ReaderOptions -> Node -> [Block] -> [Block] +addBlock opts (Node _ PARAGRAPH nodes) = + (Para (addInlines opts nodes) :) +addBlock _ (Node _ THEMATIC_BREAK _) = (HorizontalRule :) -addBlock (Node _ BLOCK_QUOTE nodes) = - (BlockQuote (addBlocks nodes) :) -addBlock (Node _ (HTML_BLOCK t) _) = - (RawBlock (Format "html") (unpack t) :) +addBlock opts (Node _ BLOCK_QUOTE nodes) = + (BlockQuote (addBlocks opts nodes) :) +addBlock opts (Node _ (HTML_BLOCK t) _) + | enabled Ext_raw_html opts = (RawBlock (Format "html") (unpack t) :) + | otherwise = id -- Note: the cmark parser will never generate CUSTOM_BLOCK, -- so we don't need to handle it: -addBlock (Node _ (CUSTOM_BLOCK _onEnter _onExit) _nodes) = +addBlock _ (Node _ (CUSTOM_BLOCK _onEnter _onExit) _nodes) = id -addBlock (Node _ (CODE_BLOCK info t) _) = +addBlock _ (Node _ (CODE_BLOCK info t) _) = (CodeBlock ("", take 1 (words (unpack info)), []) (unpack t) :) -addBlock (Node _ (HEADING lev) nodes) = - (Header lev ("",[],[]) (addInlines nodes) :) -addBlock (Node _ (LIST listAttrs) nodes) = - (constructor (map (setTightness . addBlocks . children) nodes) :) +addBlock opts (Node _ (HEADING lev) nodes) = + (Header lev ("",[],[]) (addInlines opts nodes) :) +addBlock opts (Node _ (LIST listAttrs) nodes) = + (constructor (map (setTightness . addBlocks opts . children) nodes) :) where constructor = case listType listAttrs of BULLET_LIST -> BulletList ORDERED_LIST -> OrderedList @@ -146,7 +137,7 @@ addBlock (Node _ (LIST listAttrs) nodes) = delim = case listDelim listAttrs of PERIOD_DELIM -> Period PAREN_DELIM -> OneParen -addBlock (Node _ (TABLE alignments) nodes) = do +addBlock opts (Node _ (TABLE alignments) nodes) = do (Table [] aligns widths headers rows :) where aligns = map fromTableCellAlignment alignments fromTableCellAlignment NoAlignment = AlignDefault @@ -169,12 +160,12 @@ addBlock (Node _ (TABLE alignments) nodes) = do toRow (Node _ t _) = error $ "toRow encountered non-row " ++ show t toCell (Node _ TABLE_CELL []) = [] toCell (Node _ TABLE_CELL (n:ns)) - | isBlockNode n = addBlocks (n:ns) - | otherwise = [Plain (addInlines (n:ns))] + | isBlockNode n = addBlocks opts (n:ns) + | otherwise = [Plain (addInlines opts (n:ns))] toCell (Node _ t _) = error $ "toCell encountered non-cell " ++ show t -addBlock (Node _ TABLE_ROW _) = id -- handled in TABLE -addBlock (Node _ TABLE_CELL _) = id -- handled in TABLE -addBlock _ = id +addBlock _ (Node _ TABLE_ROW _) = id -- handled in TABLE +addBlock _ (Node _ TABLE_CELL _) = id -- handled in TABLE +addBlock _ _ = id isBlockNode :: Node -> Bool isBlockNode (Node _ nodetype _) = @@ -207,11 +198,11 @@ isBlockNode (Node _ nodetype _) = children :: Node -> [Node] children (Node _ _ ns) = ns -addInlines :: [Node] -> [Inline] -addInlines = foldr addInline [] +addInlines :: ReaderOptions -> [Node] -> [Inline] +addInlines opts = foldr (addInline opts) [] -addInline :: Node -> [Inline] -> [Inline] -addInline (Node _ (TEXT t) _) = (map toinl clumps ++) +addInline :: ReaderOptions -> Node -> [Inline] -> [Inline] +addInline opts (Node _ (TEXT t) _) = (map toinl clumps ++) where raw = unpack t clumps = groupBy samekind raw samekind ' ' ' ' = True @@ -219,25 +210,30 @@ addInline (Node _ (TEXT t) _) = (map toinl clumps ++) samekind _ ' ' = False samekind _ _ = True toinl (' ':_) = Space - toinl xs = Str xs -addInline (Node _ LINEBREAK _) = (LineBreak :) -addInline (Node _ SOFTBREAK _) = (SoftBreak :) -addInline (Node _ (HTML_INLINE t) _) = - (RawInline (Format "html") (unpack t) :) + toinl xs = Str $ if enabled Ext_emoji opts + then convertEmojis xs + else xs +addInline _ (Node _ LINEBREAK _) = (LineBreak :) +addInline opts (Node _ SOFTBREAK _) + | enabled Ext_hard_line_breaks opts = (LineBreak :) + | otherwise = (SoftBreak :) +addInline opts (Node _ (HTML_INLINE t) _) + | enabled Ext_raw_html opts = (RawInline (Format "html") (unpack t) :) + | otherwise = id -- Note: the cmark parser will never generate CUSTOM_BLOCK, -- so we don't need to handle it: -addInline (Node _ (CUSTOM_INLINE _onEnter _onExit) _nodes) = +addInline _ (Node _ (CUSTOM_INLINE _onEnter _onExit) _nodes) = id -addInline (Node _ (CODE t) _) = +addInline _ (Node _ (CODE t) _) = (Code ("",[],[]) (unpack t) :) -addInline (Node _ EMPH nodes) = - (Emph (addInlines nodes) :) -addInline (Node _ STRONG nodes) = - (Strong (addInlines nodes) :) -addInline (Node _ STRIKETHROUGH nodes) = - (Strikeout (addInlines nodes) :) -addInline (Node _ (LINK url title) nodes) = - (Link nullAttr (addInlines nodes) (unpack url, unpack title) :) -addInline (Node _ (IMAGE url title) nodes) = - (Image nullAttr (addInlines nodes) (unpack url, unpack title) :) -addInline _ = id +addInline opts (Node _ EMPH nodes) = + (Emph (addInlines opts nodes) :) +addInline opts (Node _ STRONG nodes) = + (Strong (addInlines opts nodes) :) +addInline opts (Node _ STRIKETHROUGH nodes) = + (Strikeout (addInlines opts nodes) :) +addInline opts (Node _ (LINK url title) nodes) = + (Link nullAttr (addInlines opts nodes) (unpack url, unpack title) :) +addInline opts (Node _ (IMAGE url title) nodes) = + (Image nullAttr (addInlines opts nodes) (unpack url, unpack title) :) +addInline _ _ = id -- cgit v1.2.3 From 1ad9679dc9147a778d272003c868d4d4f638fd31 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 8 Aug 2017 14:00:13 -0700 Subject: CommonMark writer: avoid excess blank lines at end of output. --- src/Text/Pandoc/Writers/CommonMark.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/CommonMark.hs b/src/Text/Pandoc/Writers/CommonMark.hs index 20a22e051..77562ed8a 100644 --- a/src/Text/Pandoc/Writers/CommonMark.hs +++ b/src/Text/Pandoc/Writers/CommonMark.hs @@ -86,7 +86,7 @@ blocksToCommonMark opts bs = do then Just $ writerColumns opts else Nothing nodes <- blocksToNodes opts bs - return $ + return $ T.stripEnd $ nodeToCommonmark cmarkOpts colwidth $ node DOCUMENT nodes -- cgit v1.2.3 From a1cd7c3bfd973aca2b7a8f22a2c7f7fae7dc707b Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 8 Aug 2017 14:05:04 -0700 Subject: Templates: Have gfm use commonmark template. --- src/Text/Pandoc/Templates.hs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Templates.hs b/src/Text/Pandoc/Templates.hs index 516cc4b2f..6582e0556 100644 --- a/src/Text/Pandoc/Templates.hs +++ b/src/Text/Pandoc/Templates.hs @@ -69,6 +69,7 @@ getDefaultTemplate user writer = do "markdown_github" -> getDefaultTemplate user "markdown" "markdown_mmd" -> getDefaultTemplate user "markdown" "markdown_phpextra" -> getDefaultTemplate user "markdown" + "gfm" -> getDefaultTemplate user "commonmark" _ -> let fname = "templates" </> "default" <.> format in UTF8.toString <$> readDataFile user fname -- cgit v1.2.3 From b50de96502b3edb27bd06887c0af011fdc76c2c2 Mon Sep 17 00:00:00 2001 From: Alexander <ilabdsf@gmail.com> Date: Wed, 9 Aug 2017 00:05:49 +0300 Subject: Muse writer: insert two blanklines between lists of the same type (#3844) --- src/Text/Pandoc/Writers/Muse.hs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index 0383d9d86..3a5eefc18 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -102,12 +102,34 @@ pandocToMuse (Pandoc meta blocks) = do Nothing -> return main Just tpl -> renderTemplate' tpl context +-- | Helper function for flatBlockListToMuse +-- | Render all blocks and insert blank lines between the first two +catWithBlankLines :: PandocMonad m + => [Block] -- ^ List of block elements + -> Int -- ^ Number of blank lines + -> StateT WriterState m Doc +catWithBlankLines (b : bs) n = do + b' <- blockToMuse b + bs' <- flatBlockListToMuse bs + return $ b' <> blanklines n <> bs' +catWithBlankLines _ _ = error "Expected at least one block" + -- | Convert list of Pandoc block elements to Muse -- | without setting stTopLevel. flatBlockListToMuse :: PandocMonad m => [Block] -- ^ List of block elements -> StateT WriterState m Doc -flatBlockListToMuse blocks = cat <$> mapM blockToMuse blocks +flatBlockListToMuse bs@(BulletList _ : BulletList _ : _) = catWithBlankLines bs 2 +flatBlockListToMuse bs@(OrderedList (_, style1, _) _ : OrderedList (_, style2, _) _ : _) = + catWithBlankLines bs (if style1' == style2' then 2 else 0) + where + style1' = normalizeStyle style1 + style2' = normalizeStyle style2 + normalizeStyle DefaultStyle = Decimal + normalizeStyle s = s +flatBlockListToMuse bs@(DefinitionList _ : DefinitionList _ : _) = catWithBlankLines bs 2 +flatBlockListToMuse bs@(_ : _) = catWithBlankLines bs 0 +flatBlockListToMuse [] = return mempty -- | Convert list of Pandoc block elements to Muse. blockListToMuse :: PandocMonad m -- cgit v1.2.3 From b1c2ada4e4da71578b934ffc7aafb11208f2b552 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 8 Aug 2017 16:46:12 -0700 Subject: Removed redundant import. --- src/Text/Pandoc/App.hs | 1 - 1 file changed, 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index a56ae8149..073b1f3ef 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -78,7 +78,6 @@ import Text.Pandoc.Builder (setMeta) import Text.Pandoc.Class (PandocIO, extractMedia, fillMediaBag, getLog, setResourcePath, getMediaBag, setTrace, report) import Text.Pandoc.Highlighting (highlightingStyles) -import Text.Pandoc.Logging (LogMessage(..)) import Text.Pandoc.Lua (runLuaFilter, LuaException(..)) import Text.Pandoc.Writers.Math (defaultMathJaxURL, defaultKaTeXURL) import Text.Pandoc.PDF (makePDF) -- cgit v1.2.3 From 34d3f25e87240b4641d00b9f206e7301af932e42 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 8 Aug 2017 20:07:06 -0700 Subject: Parsing: added gobbleSpaces. This is a utility function to use in list parsing. --- src/Text/Pandoc/Parsing.hs | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 0c97d4060..37a0b53b4 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -49,6 +49,7 @@ module Text.Pandoc.Parsing ( takeWhileP, skipSpaces, blankline, blanklines, + gobbleSpaces, enclosed, stringAnyCase, parseFromString, @@ -377,6 +378,17 @@ blankline = try $ skipSpaces >> newline blanklines :: Stream s m Char => ParserT s st m [Char] blanklines = many1 blankline +-- | Gobble n spaces; if tabs are encountered, expand them +-- and gobble some or all of their spaces, leaving the rest. +gobbleSpaces :: Monad m => ReaderOptions -> Int -> ParserT [Char] st m () +gobbleSpaces _ 0 = return () +gobbleSpaces opts n = try $ do + char ' ' <|> do char '\t' + inp <- getInput + setInput $ replicate (readerTabStop opts - 1) ' ' ++ inp + return ' ' + gobbleSpaces opts (n - 1) + -- | Parses material enclosed between start and end parsers. enclosed :: (Show end, Stream s m Char) => ParserT s st m t -- ^ start parser -> ParserT s st m end -- ^ end parser -- cgit v1.2.3 From 606a8e2af42df39591df37a1be1a2ef4101d1dcf Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 8 Aug 2017 20:48:30 -0700 Subject: RST reader: support :widths: attribute for table directive. --- src/Text/Pandoc/Readers/RST.hs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 2daf60a89..6cc3b7472 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -765,15 +765,25 @@ directive' = do tableDirective :: PandocMonad m => String -> [(String, String)] -> String -> RSTParser m Blocks -tableDirective top _fields body = do +tableDirective top fields body = do bs <- parseFromString' parseBlocks body case B.toList bs of [Table _ aligns' widths' header' rows'] -> do title <- parseFromString' (trimInlines . mconcat <$> many inline) top - -- TODO widths + columns <- getOption readerColumns + let numOfCols = length header' + let normWidths ws = + map (/ max 1.0 (fromIntegral (columns - numOfCols))) ws + let widths = case trim <$> lookup "widths" fields of + Just "auto" -> replicate numOfCols 0.0 + Just "grid" -> widths' + Just specs -> normWidths + $ map (fromMaybe (0 :: Double) . safeRead) + $ splitBy (`elem` (" ," :: String)) specs + Nothing -> widths' -- align is not applicable since we can't represent whole table align return $ B.singleton $ Table (B.toList title) - aligns' widths' header' rows' + aligns' widths header' rows' _ -> return mempty -- cgit v1.2.3 From cfa597fc2ac2a6dceb0b3387a2ee885ec75bc7d1 Mon Sep 17 00:00:00 2001 From: Alexander <ilabdsf@gmail.com> Date: Wed, 9 Aug 2017 19:09:05 +0300 Subject: Muse reader: simplify tableCell implementation (#3846) --- src/Text/Pandoc/Readers/Muse.hs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 6e4aed94e..5d77dec13 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -391,9 +391,7 @@ museAppendElement tbl element = return tbl{ museTableCaption = inlines' } tableCell :: PandocMonad m => MuseParser m (F Blocks) -tableCell = try $ do - content <- trimInlinesF . mconcat <$> manyTill inline (lookAhead cellEnd) - return $ B.plain <$> content +tableCell = try $ liftM B.plain . trimInlinesF . mconcat <$> manyTill inline (lookAhead cellEnd) where cellEnd = try $ void (many1 spaceChar >> char '|') <|> void newline <|> eof tableElements :: PandocMonad m => MuseParser m [MuseTableElement] -- cgit v1.2.3 From db55f7c1b243cbc82c70276c7dfb9c0403e369b0 Mon Sep 17 00:00:00 2001 From: bucklereed <horridimpfoobarbaz@chammy.info> Date: Wed, 9 Aug 2017 17:10:12 +0100 Subject: HTML reader: parse <main> like <div role=main>. (#3791) * HTML reader: parse <main> like <div role=main>. * <main> closes <p> and behaves like a block element generally --- src/Text/Pandoc/Readers/HTML.hs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 3a0d6eb14..7b9ab38fd 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -54,7 +54,7 @@ import Text.Pandoc.Parsing hiding ((<|>)) import Text.Pandoc.Walk import qualified Data.Map as M import Data.Foldable ( for_ ) -import Data.Maybe ( fromMaybe, isJust) +import Data.Maybe ( fromMaybe, isJust, isNothing ) import Data.List ( intercalate, isPrefixOf ) import Data.Char ( isDigit, isLetter, isAlphaNum ) import Control.Monad ( guard, mzero, void, unless ) @@ -377,6 +377,7 @@ pDiv = try $ do guardEnabled Ext_native_divs let isDivLike "div" = True isDivLike "section" = True + isDivLike "main" = True isDivLike _ = False TagOpen tag attr' <- lookAhead $ pSatisfy $ tagOpen isDivLike (const True) let attr = toStringAttr attr' @@ -385,7 +386,10 @@ pDiv = try $ do let classes' = if tag == "section" then "section":classes else classes - return $ B.divWith (ident, classes', kvs) contents + kvs' = if tag == "main" && isNothing (lookup "role" kvs) + then ("role", "main"):kvs + else kvs + return $ B.divWith (ident, classes', kvs') contents pRawHtmlBlock :: PandocMonad m => TagParser m Blocks pRawHtmlBlock = do @@ -940,7 +944,7 @@ blockHtmlTags = Set.fromList "dir", "div", "dl", "dt", "fieldset", "figcaption", "figure", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html", - "isindex", "menu", "noframes", "ol", "output", "p", "pre", + "isindex", "main", "menu", "noframes", "ol", "output", "p", "pre", "section", "table", "tbody", "textarea", "thead", "tfoot", "ul", "dd", "dt", "frameset", "li", "tbody", "td", "tfoot", @@ -1022,10 +1026,10 @@ _ `closes` "html" = False "optgroup" `closes` "optgroup" = True "optgroup" `closes` "option" = True "option" `closes` "option" = True --- http://www.w3.org/TR/html-markup/p.html +-- https://html.spec.whatwg.org/multipage/syntax.html#optional-tags x `closes` "p" | x `elem` ["address", "article", "aside", "blockquote", "dir", "div", "dl", "fieldset", "footer", "form", "h1", "h2", "h3", "h4", - "h5", "h6", "header", "hr", "menu", "nav", "ol", "p", "pre", "section", + "h5", "h6", "header", "hr", "main", "menu", "nav", "ol", "p", "pre", "section", "table", "ul"] = True "meta" `closes` "meta" = True "form" `closes` "form" = True @@ -1038,8 +1042,8 @@ t `closes` "select" | t /= "option" = True "tfoot" `closes` t | t `elem` ["thead","colgroup"] = True "tbody" `closes` t | t `elem` ["tbody","tfoot","thead","colgroup"] = True t `closes` t2 | - t `elem` ["h1","h2","h3","h4","h5","h6","dl","ol","ul","table","div","p"] && - t2 `elem` ["h1","h2","h3","h4","h5","h6","p" ] = True -- not "div" + t `elem` ["h1","h2","h3","h4","h5","h6","dl","ol","ul","table","div","main","p"] && + t2 `elem` ["h1","h2","h3","h4","h5","h6","p" ] = True -- not "div" or "main" t1 `closes` t2 | t1 `Set.member` blockTags && t2 `Set.notMember` blockTags && -- cgit v1.2.3 From 09b7df472dbc171e6130090dfec0f7b71d1d955e Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 9 Aug 2017 09:14:52 -0700 Subject: LaTeX reader: Use `label` instead of `data-label` for label in caption. See d441e656db576f266c4866e65ff9e4705d376381, #3639. --- 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 e51f797af..0f0e71b93 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1632,7 +1632,7 @@ setCaption = do try $ spaces >> controlSeq "label" >> (Just <$> tok) let ils' = case mblabel of Just lab -> ils <> spanWith - ("",[],[("data-label", stringify lab)]) mempty + ("",[],[("label", stringify lab)]) mempty Nothing -> ils updateState $ \st -> st{ sCaption = Just ils' } return mempty -- cgit v1.2.3 From 96933c60432276fcbe2e24163868fffb138e6a11 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 9 Aug 2017 09:26:57 -0700 Subject: Org reader: use tag-name attribute instead of data-tag-name. --- src/Text/Pandoc/Readers/Org/DocumentTree.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/DocumentTree.hs b/src/Text/Pandoc/Readers/Org/DocumentTree.hs index 743f6cc0e..3b90c9336 100644 --- a/src/Text/Pandoc/Readers/Org/DocumentTree.hs +++ b/src/Text/Pandoc/Readers/Org/DocumentTree.hs @@ -274,7 +274,7 @@ tagsToInlines tags = -- | Wrap the given inline in a span, marking it as a tag. tagSpan :: Tag -> Inlines -> Inlines -tagSpan t = B.spanWith ("", ["tag"], [("data-tag-name", fromTag t)]) +tagSpan t = B.spanWith ("", ["tag"], [("tag-name", fromTag t)]) -- cgit v1.2.3 From ac18ff90b22c2a8f8b829acb9139477ee7b02954 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 9 Aug 2017 09:45:17 -0700 Subject: Org reader: use org-language attribute rather than data-org-language. --- src/Text/Pandoc/Readers/Org/Shared.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/Shared.hs b/src/Text/Pandoc/Readers/Org/Shared.hs index 952082ec1..580e9194f 100644 --- a/src/Text/Pandoc/Readers/Org/Shared.hs +++ b/src/Text/Pandoc/Readers/Org/Shared.hs @@ -76,7 +76,7 @@ originalLang lang = let transLang = translateLang lang in if transLang == lang then [] - else [("data-org-language", lang)] + else [("org-language", lang)] -- | Translate from Org-mode's programming language identifiers to those used -- by Pandoc. This is useful to allow for proper syntax highlighting in -- cgit v1.2.3 From 6b72c5e35ba9b0872704b77b2bd8793657adcaba Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 9 Aug 2017 12:13:49 -0700 Subject: Support svg in PDF output, converting with rsvg2pdf. Closes #1793. --- src/Text/Pandoc/PDF.hs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index 25a94972a..ef6a4099c 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -162,15 +162,24 @@ convertImage tmpdir fname = Just "image/png" -> doNothing Just "image/jpeg" -> doNothing Just "application/pdf" -> doNothing - Just "image/svg+xml" -> return $ Left "conversion from svg not supported" + Just "image/svg+xml" -> E.catch (do + (exit, _) <- pipeProcess Nothing "rsvg-convert" + ["-f","pdf","-a","-o",pdfOut,fname] BL.empty + if exit == ExitSuccess + then return $ Right pdfOut + else return $ Left "conversion from SVG failed") + (\(e :: E.SomeException) -> return $ Left $ + "check that rsvg2pdf is in path.\n" ++ + show e) _ -> JP.readImage fname >>= \res -> case res of Left e -> return $ Left e Right img -> - E.catch (Right fileOut <$ JP.savePngImage fileOut img) $ + E.catch (Right pngOut <$ JP.savePngImage pngOut img) $ \(e :: E.SomeException) -> return (Left (show e)) where - fileOut = replaceDirectory (replaceExtension fname ".png") tmpdir + pngOut = replaceDirectory (replaceExtension fname ".png") tmpdir + pdfOut = replaceDirectory (replaceExtension fname ".pdf") tmpdir mime = getMimeType fname doNothing = return (Right fname) -- cgit v1.2.3 From 2581f97620b36b14ee5560a747f57298a8640c84 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 9 Aug 2017 18:02:38 -0700 Subject: EPUB writer: don't strip formatting in TOC. Closes #1611. --- src/Text/Pandoc/Writers/EPUB.hs | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index a48fcf415..b04a7de51 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -41,6 +41,7 @@ import Control.Monad.State.Strict (State, StateT, evalState, evalStateT, get, ge import qualified Data.ByteString.Lazy as B import qualified Data.ByteString.Lazy.Char8 as B8 import qualified Data.Text.Lazy as TL +import qualified Data.Text as TS import Data.Char (isAlphaNum, isDigit, toLower, isAscii) import Data.List (intercalate, isInfixOf, isPrefixOf) import qualified Data.Map as M @@ -70,7 +71,7 @@ import Text.Pandoc.Writers.HTML (writeHtmlStringForEPUB) import Text.Printf (printf) import Text.XML.Light (Attr (..), Element (..), Node (..), QName (..), add_attrs, lookupAttr, node, onlyElems, parseXML, - ppElement, strContent, unode, unqual) + ppElement, strContent, unode, unqual, showElement) -- A Chapter includes a list of blocks and maybe a section -- number offset. Note, some chapters are unnumbered. The section @@ -635,17 +636,17 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do let tocLevel = writerTOCDepth opts let navPointNode :: PandocMonad m - => (Int -> String -> String -> [Element] -> Element) + => (Int -> [Inline] -> String -> [Element] -> Element) -> S.Element -> StateT Int m Element navPointNode formatter (S.Sec _ nums (ident,_,_) ils children) = do n <- get modify (+1) let showNums :: [Int] -> String showNums = intercalate "." . map show - let tit' = stringify ils let tit = if writerNumberSections opts && not (null nums) - then showNums nums ++ " " ++ tit' - else tit' + then Span ("", ["section-header-number"], []) + [Str (showNums nums)] : Space : ils + else ils src <- case lookup ident reftable of Just x -> return x Nothing -> throwError $ PandocSomeError $ ident ++ " not found in reftable" @@ -656,10 +657,10 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do return $ formatter n tit src subs navPointNode _ (S.Blk _) = throwError $ PandocSomeError "navPointNode encountered Blk" - let navMapFormatter :: Int -> String -> String -> [Element] -> Element + let navMapFormatter :: Int -> [Inline] -> String -> [Element] -> Element navMapFormatter n tit src subs = unode "navPoint" ! [("id", "navPoint-" ++ show n)] $ - [ unode "navLabel" $ unode "text" tit + [ unode "navLabel" $ unode "text" $ stringify tit , unode "content" ! [("src", "text/" ++ src)] $ () ] ++ subs @@ -690,19 +691,31 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do ] let tocEntry = mkEntry "toc.ncx" tocData - let navXhtmlFormatter :: Int -> String -> String -> [Element] -> Element + let navXhtmlFormatter :: Int -> [Inline] -> String -> [Element] -> Element navXhtmlFormatter n tit src subs = unode "li" ! [("id", "toc-li-" ++ show n)] $ (unode "a" ! [("href", "text/" ++ src)] - $ tit) + $ titElements) : case subs of [] -> [] (_:_) -> [unode "ol" ! [("class","toc")] $ subs] + where titElements = parseXML titRendered + titRendered = case P.runPure + (writeHtmlStringForEPUB version + opts{ writerTemplate = Nothing } + (Pandoc nullMeta + [Plain $ walk delink tit])) of + Left _ -> TS.pack $ stringify tit + Right x -> x + -- can't have a element inside a... + delink (Link _ ils _) = Span ("", [], []) ils + delink x = x let navtag = if epub3 then "nav" else "div" tocBlocks <- lift $ evalStateT (mapM (navPointNode navXhtmlFormatter) secs) 1 - let navBlocks = [RawBlock (Format "html") $ ppElement $ + let navBlocks = [RawBlock (Format "html") + $ showElement $ -- prettyprinting introduces bad spaces unode navtag ! ([("epub:type","toc") | epub3] ++ [("id","toc")]) $ [ unode "h1" ! [("id","toc-title")] $ tocTitle -- cgit v1.2.3 From 1dcecffef4bfdef9fa853d6a5b8a7b7a90021555 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 9 Aug 2017 20:53:42 -0700 Subject: Removed spurious comments. --- src/Text/Pandoc/Readers/RST.hs | 4 ---- 1 file changed, 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 6cc3b7472..02812dbd9 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -58,7 +58,6 @@ import qualified Data.Text as T -- TODO: -- [ ] .. parsed-literal --- [ ] :widths: attribute in .. table -- [ ] .. csv-table -- | Parse reStructuredText string and return Pandoc document. @@ -1057,7 +1056,6 @@ anonymousKey = try $ do src <- targetURI pos <- getPosition let key = toKey $ "_" ++ printf "%09d" (sourceLine pos) - --TODO: parse width, height, class and name attributes updateState $ \s -> s { stateKeys = M.insert key ((src,""), nullAttr) $ stateKeys s } @@ -1085,7 +1083,6 @@ regularKey = try $ do refs <- referenceNames src <- targetURI guard $ not (null src) - --TODO: parse width, height, class and name attributes let keys = map (toKey . stripTicks) refs forM_ keys $ \key -> updateState $ \s -> s { stateKeys = M.insert key ((src,""), nullAttr) $ @@ -1115,7 +1112,6 @@ headerBlock = do ((txt, _), raw) <- withRaw (doubleHeader' <|> singleHeader') (ident,_,_) <- registerHeader nullAttr txt let key = toKey (stringify txt) - --TODO: parse width, height, class and name attributes updateState $ \s -> s { stateKeys = M.insert key (('#':ident,""), nullAttr) $ stateKeys s } return raw -- cgit v1.2.3 From 8c1ae7ddafc483d993d86161a93a1bf10d84f045 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 9 Aug 2017 20:59:25 -0700 Subject: RST writer: don't wrap term in definition list. Wrapping is not allowed. --- src/Text/Pandoc/Writers/RST.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index 019c8335d..17f5b3f91 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -331,7 +331,7 @@ definitionListItemToRST (label, defs) = do label' <- inlineListToRST label contents <- liftM vcat $ mapM blockListToRST defs tabstop <- gets $ writerTabStop . stOptions - return $ label' $$ nest tabstop (nestle contents <> cr) + return $ nowrap label' $$ nest tabstop (nestle contents <> cr) -- | Format a list of lines as line block. linesToLineBlock :: PandocMonad m => [[Inline]] -> RST m Doc -- cgit v1.2.3 From f4bff5d3599d0b6874c6b6604fb11016f8e038a9 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 9 Aug 2017 21:15:54 -0700 Subject: RST reader: reorganize block parsers for ~20% faster parsing. --- src/Text/Pandoc/Readers/RST.hs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 02812dbd9..3b1eee010 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -215,10 +215,10 @@ block :: PandocMonad m => RSTParser m Blocks block = choice [ codeBlock , blockQuote , fieldList - , include , directive , anchor , comment + , include , header , hrule , lineBlock -- must go before definitionList @@ -352,7 +352,8 @@ singleHeader = do singleHeader' :: PandocMonad m => RSTParser m (Inlines, Char) singleHeader' = try $ do notFollowedBy' whitespace - txt <- trimInlines . mconcat <$> many1 (do {notFollowedBy blankline; inline}) + lookAhead $ anyLine >> oneOf underlineChars + txt <- trimInlines . mconcat <$> many1 (do {notFollowedBy newline; inline}) pos <- getPosition let len = (sourceColumn pos) - 1 blankline @@ -630,7 +631,7 @@ comment :: Monad m => RSTParser m Blocks comment = try $ do string ".." skipMany1 spaceChar <|> (() <$ lookAhead newline) - notFollowedBy' directiveLabel + -- notFollowedBy' directiveLabel -- comment comes after directive so unnec. manyTill anyChar blanklines optional indentedBlock return mempty -- cgit v1.2.3 From e9eaf8421567b2d54b415b642ec1077d79907a10 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 10 Aug 2017 07:48:28 -0700 Subject: Slidy writer: use h1 for all slides... even if they were originally level 2 headers. Otherwise the built-in table of contents in Slidy breaks. Closes #3566. --- src/Text/Pandoc/Writers/HTML.hs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index fecb32464..b899ce96a 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -403,8 +403,12 @@ elementToHtml slideLevel opts (Sec level num (id',classes,keyvals) title' elemen then return mempty else do modify (\st -> st{ stElement = True}) + let level' = if level <= slideLevel && + slideVariant == SlidySlides + then 1 -- see #3566 + else level res <- blockToHtml opts - (Header level (id',classes,keyvals) title') + (Header level' (id',classes,keyvals) title') modify (\st -> st{ stElement = False}) return res -- cgit v1.2.3 From a5790dd30893cf7143eb64a46fb137caf131a624 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 10 Aug 2017 11:12:41 -0700 Subject: RST reader: Basic support for csv-table directive. * Added Text.Pandoc.CSV, simple CSV parser. * Options still not supported, and we need tests. See #3533. --- src/Text/Pandoc/CSV.hs | 102 +++++++++++++++++++++++++++++++++++++++++ src/Text/Pandoc/Readers/RST.hs | 52 +++++++++++++++++++++ 2 files changed, 154 insertions(+) create mode 100644 src/Text/Pandoc/CSV.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/CSV.hs b/src/Text/Pandoc/CSV.hs new file mode 100644 index 000000000..15492ac52 --- /dev/null +++ b/src/Text/Pandoc/CSV.hs @@ -0,0 +1,102 @@ +{- +Copyright (C) 2017 John MacFarlane <jgm@berkeley.edu> + +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.CSV + Copyright : Copyright (C) 2017 John MacFarlane <jgm@berkeley.edu> + License : GNU GPL, version 2 or above + Maintainer : John MacFarlane <jgm@berkeley.edu> + Stability : alpha + Portability : portable + +Simple CSV parser. +-} + +module Text.Pandoc.CSV ( + CSVOptions, + defaultCSVOptions, + parseCSV, + ParseError +) where + +import Text.Parsec +import Text.Parsec.Text (Parser) +import Text.Parsec.Error (ParseError) +import Data.Text (Text) +import qualified Data.Text as T +import Control.Monad (void) + +data CSVOptions = CSVOptions{ + csvDelim :: Char + , csvQuote :: Char + , csvKeepSpace :: Bool -- treat whitespace following delim as significant + , csvEscape :: Maybe Char -- default is to double up quote +} deriving (Read, Show) + +defaultCSVOptions :: CSVOptions +defaultCSVOptions = CSVOptions{ + csvDelim = ',' + , csvQuote = '"' + , csvKeepSpace = False + , csvEscape = Nothing } + +parseCSV :: CSVOptions -> Text -> Either ParseError [[Text]] +parseCSV opts t = parse (pCSV opts) "csv" t + +pCSV :: CSVOptions -> Parser [[Text]] +pCSV opts = + (pCSVRow opts `sepEndBy` endline) <* (spaces *> eof) + +pCSVRow :: CSVOptions -> Parser [Text] +pCSVRow opts = notFollowedBy blank >> pCSVCell opts `sepBy` pCSVDelim opts + +blank :: Parser () +blank = try $ spaces >> (() <$ endline <|> eof) + +pCSVCell :: CSVOptions -> Parser Text +pCSVCell opts = pCSVQuotedCell opts <|> pCSVUnquotedCell opts + +pCSVQuotedCell :: CSVOptions -> Parser Text +pCSVQuotedCell opts = do + char (csvQuote opts) + res <- many (satisfy (\c -> c /= csvQuote opts) <|> escaped opts) + char (csvQuote opts) + return $ T.pack res + +escaped :: CSVOptions -> Parser Char +escaped opts = do + case csvEscape opts of + Nothing -> try $ char (csvQuote opts) >> char (csvQuote opts) + Just c -> try $ char c >> noneOf "\r\n" + +pCSVUnquotedCell :: CSVOptions -> Parser Text +pCSVUnquotedCell opts = T.pack <$> + many (satisfy $ \c -> c /= csvDelim opts && c /= '\r' && c /= '\n') + +pCSVDelim :: CSVOptions -> Parser () +pCSVDelim opts = do + char (csvDelim opts) + if csvKeepSpace opts + then return () + else skipMany (oneOf " \t") + +endline :: Parser () +endline = do + optional (void $ char '\r') + void $ char '\n' + diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 3b1eee010..6cf8dbae4 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -45,6 +45,7 @@ import Text.Pandoc.Builder (fromList, setMeta) import Text.Pandoc.Builder (Blocks, Inlines, trimInlines) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad, readFileFromDirs) +import Text.Pandoc.CSV (CSVOptions(..), defaultCSVOptions, parseCSV) import Text.Pandoc.Definition import Text.Pandoc.Error import Text.Pandoc.ImageSize (lengthToDim, scaleDimension) @@ -56,6 +57,8 @@ import Text.Printf (printf) import Data.Text (Text) import qualified Data.Text as T +import Debug.Trace + -- TODO: -- [ ] .. parsed-literal -- [ ] .. csv-table @@ -688,6 +691,7 @@ directive' = do case label of "table" -> tableDirective top fields body' "list-table" -> listTableDirective top fields body' + "csv-table" -> csvTableDirective top fields body' "line-block" -> lineBlockDirective body' "raw" -> return $ B.rawBlock (trim top) (stripTrailingNewlines body) "role" -> addNewRole top $ map (\(k,v) -> (k, trim v)) fields @@ -820,6 +824,54 @@ listTableDirective top fields body = do takeCells _ = [] normWidths ws = map (/ max 1 (sum ws)) ws + -- TODO + -- [ ] delim: + -- [ ] quote: + -- [ ] keepspace: + -- [ ] escape: + -- [ ] widths: + -- [ ] header-rows: + -- [ ] header: + -- [ ] url: + -- [ ] file: + -- [ ] encoding: +csvTableDirective :: PandocMonad m + => String -> [(String, String)] -> String + -> RSTParser m Blocks +csvTableDirective top fields rawcsv = do + let res = parseCSV defaultCSVOptions (T.pack rawcsv) + case res of + Left e -> do + throwError $ PandocParsecError "csv table" e + Right rows -> do + return $ B.rawBlock "rst" $ show rows +{- + bs <- parseFromString' parseBlocks body + title <- parseFromString' (trimInlines . mconcat <$> many inline) top + let rows = takeRows $ B.toList bs + headerRowsNum = fromMaybe (0 :: Int) $ + lookup "header-rows" fields >>= safeRead + (headerRow,bodyRows,numOfCols) = case rows of + x:xs -> if headerRowsNum > 0 + then (x, xs, length x) + else ([], rows, length x) + _ -> ([],[],0) + widths = case trim <$> lookup "widths" fields of + Just "auto" -> replicate numOfCols 0 + Just specs -> normWidths $ map (fromMaybe (0 :: Double) . safeRead) $ + splitBy (`elem` (" ," :: String)) specs + _ -> replicate numOfCols 0 + return $ B.table title + (zip (replicate numOfCols AlignDefault) widths) + headerRow + bodyRows + where takeRows [BulletList rows] = map takeCells rows + takeRows _ = [] + takeCells [BulletList cells] = map B.fromList cells + takeCells _ = [] + normWidths ws = map (/ max 1 (sum ws)) ws +-} + -- TODO: -- - Only supports :format: fields with a single format for :raw: roles, -- change Text.Pandoc.Definition.Format to fix -- cgit v1.2.3 From dee4cbc8549d782d9c3f2e9072b2c141ea4f18ad Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 10 Aug 2017 12:04:08 -0700 Subject: RST reader: implement csv-table directive. Most attributes are supported, including `:file:` and `:url:`. A (probably insufficient) test case has been added. Closes #3533. --- src/Text/Pandoc/CSV.hs | 8 ++-- src/Text/Pandoc/Readers/RST.hs | 101 +++++++++++++++++++++++------------------ 2 files changed, 61 insertions(+), 48 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/CSV.hs b/src/Text/Pandoc/CSV.hs index 15492ac52..db9226469 100644 --- a/src/Text/Pandoc/CSV.hs +++ b/src/Text/Pandoc/CSV.hs @@ -28,7 +28,7 @@ Simple CSV parser. -} module Text.Pandoc.CSV ( - CSVOptions, + CSVOptions(..), defaultCSVOptions, parseCSV, ParseError @@ -74,7 +74,8 @@ pCSVCell opts = pCSVQuotedCell opts <|> pCSVUnquotedCell opts pCSVQuotedCell :: CSVOptions -> Parser Text pCSVQuotedCell opts = do char (csvQuote opts) - res <- many (satisfy (\c -> c /= csvQuote opts) <|> escaped opts) + res <- many (satisfy (\c -> c /= csvQuote opts && + Just c /= csvEscape opts) <|> escaped opts) char (csvQuote opts) return $ T.pack res @@ -86,7 +87,8 @@ escaped opts = do pCSVUnquotedCell :: CSVOptions -> Parser Text pCSVUnquotedCell opts = T.pack <$> - many (satisfy $ \c -> c /= csvDelim opts && c /= '\r' && c /= '\n') + many (satisfy (\c -> c /= csvDelim opts && c /= '\r' && c /= '\n' + && c /= csvQuote opts)) pCSVDelim :: CSVOptions -> Parser () pCSVDelim opts = do diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 6cf8dbae4..0f594fe1b 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -31,7 +31,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 ) where -import Control.Monad (guard, liftM, mzero, when, forM_) +import Control.Monad (guard, liftM, mzero, when, forM_, mplus) import Control.Monad.Identity (Identity(..)) import Control.Monad.Except (throwError) import Data.Char (isHexDigit, isSpace, toLower, toUpper) @@ -44,7 +44,7 @@ import Data.Sequence (ViewR (..), viewr) import Text.Pandoc.Builder (fromList, setMeta) import Text.Pandoc.Builder (Blocks, Inlines, trimInlines) import qualified Text.Pandoc.Builder as B -import Text.Pandoc.Class (PandocMonad, readFileFromDirs) +import Text.Pandoc.Class (PandocMonad, readFileFromDirs, fetchItem) import Text.Pandoc.CSV (CSVOptions(..), defaultCSVOptions, parseCSV) import Text.Pandoc.Definition import Text.Pandoc.Error @@ -53,15 +53,13 @@ import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Parsing import Text.Pandoc.Shared +import qualified Text.Pandoc.UTF8 as UTF8 import Text.Printf (printf) import Data.Text (Text) import qualified Data.Text as T -import Debug.Trace - -- TODO: -- [ ] .. parsed-literal --- [ ] .. csv-table -- | Parse reStructuredText string and return Pandoc document. readRST :: PandocMonad m @@ -824,53 +822,66 @@ listTableDirective top fields body = do takeCells _ = [] normWidths ws = map (/ max 1 (sum ws)) ws - -- TODO - -- [ ] delim: - -- [ ] quote: - -- [ ] keepspace: - -- [ ] escape: - -- [ ] widths: - -- [ ] header-rows: - -- [ ] header: - -- [ ] url: - -- [ ] file: - -- [ ] encoding: csvTableDirective :: PandocMonad m => String -> [(String, String)] -> String -> RSTParser m Blocks csvTableDirective top fields rawcsv = do - let res = parseCSV defaultCSVOptions (T.pack rawcsv) + let explicitHeader = trim <$> lookup "header" fields + let opts = defaultCSVOptions{ + csvDelim = case trim <$> lookup "delim" fields of + Just "tab" -> '\t' + Just "space" -> ' ' + Just [c] -> c + _ -> ',' + , csvQuote = case trim <$> lookup "quote" fields of + Just [c] -> c + _ -> '"' + , csvEscape = case trim <$> lookup "escape" fields of + Just [c] -> Just c + _ -> Nothing + , csvKeepSpace = case trim <$> lookup "keepspace" fields of + Just "true" -> True + _ -> False + } + let headerRowsNum = fromMaybe (case explicitHeader of + Just _ -> 1 :: Int + Nothing -> 0 :: Int) $ + lookup "header-rows" fields >>= safeRead + rawcsv' <- case trim <$> + lookup "file" fields `mplus` lookup "url" fields of + Just u -> do + (bs, _) <- fetchItem Nothing u + return $ UTF8.toString bs + Nothing -> return rawcsv + let res = parseCSV opts (T.pack $ case explicitHeader of + Just h -> h ++ "\n" ++ rawcsv' + Nothing -> rawcsv') case res of Left e -> do throwError $ PandocParsecError "csv table" e - Right rows -> do - return $ B.rawBlock "rst" $ show rows -{- - bs <- parseFromString' parseBlocks body - title <- parseFromString' (trimInlines . mconcat <$> many inline) top - let rows = takeRows $ B.toList bs - headerRowsNum = fromMaybe (0 :: Int) $ - lookup "header-rows" fields >>= safeRead - (headerRow,bodyRows,numOfCols) = case rows of - x:xs -> if headerRowsNum > 0 - then (x, xs, length x) - else ([], rows, length x) - _ -> ([],[],0) - widths = case trim <$> lookup "widths" fields of - Just "auto" -> replicate numOfCols 0 - Just specs -> normWidths $ map (fromMaybe (0 :: Double) . safeRead) $ - splitBy (`elem` (" ," :: String)) specs - _ -> replicate numOfCols 0 - return $ B.table title - (zip (replicate numOfCols AlignDefault) widths) - headerRow - bodyRows - where takeRows [BulletList rows] = map takeCells rows - takeRows _ = [] - takeCells [BulletList cells] = map B.fromList cells - takeCells _ = [] - normWidths ws = map (/ max 1 (sum ws)) ws --} + Right rawrows -> do + let parseCell = parseFromString' (plain <|> return mempty) . T.unpack + let parseRow = mapM parseCell + rows <- mapM parseRow rawrows + let (headerRow,bodyRows,numOfCols) = + case rows of + x:xs -> if headerRowsNum > 0 + then (x, xs, length x) + else ([], rows, length x) + _ -> ([],[],0) + title <- parseFromString' (trimInlines . mconcat <$> many inline) top + let normWidths ws = map (/ max 1 (sum ws)) ws + let widths = + case trim <$> lookup "widths" fields of + Just "auto" -> replicate numOfCols 0 + Just specs -> normWidths + $ map (fromMaybe (0 :: Double) . safeRead) + $ splitBy (`elem` (" ," :: String)) specs + _ -> replicate numOfCols 0 + return $ B.table title + (zip (replicate numOfCols AlignDefault) widths) + headerRow + bodyRows -- TODO: -- - Only supports :format: fields with a single format for :raw: roles, -- cgit v1.2.3 From 6aaf8f4770916fcef433d0078502d674a1c97fc7 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 10 Aug 2017 23:04:14 -0700 Subject: Expose getDefaultDataFile in both Shared and Class. --- src/Text/Pandoc/App.hs | 6 +++--- src/Text/Pandoc/Class.hs | 16 +++++++++++++--- src/Text/Pandoc/Shared.hs | 1 + src/Text/Pandoc/Writers/Docx.hs | 2 +- 4 files changed, 18 insertions(+), 7 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 073b1f3ef..8a7947de6 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -84,8 +84,8 @@ import Text.Pandoc.PDF (makePDF) import Text.Pandoc.Process (pipeProcess) import Text.Pandoc.SelfContained (makeDataURI, makeSelfContained) import Text.Pandoc.Shared (headerShift, isURI, openURL, readDataFile, - readDataFileUTF8, safeRead, tabFilter, - eastAsianLineBreakFilter) + readDataFileUTF8, readDefaultDataFile, + safeRead, tabFilter, eastAsianLineBreakFilter) import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.XML (toEntities) import Text.Printf @@ -1007,7 +1007,7 @@ options = , Option "" ["print-default-data-file"] (ReqArg (\arg _ -> do - readDataFile Nothing arg >>= BS.hPutStr stdout + readDefaultDataFile arg >>= BS.hPutStr stdout exitSuccess) "FILE") "" -- "Print default data file" diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index a8db05e5b..cefc8ee2c 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -80,6 +80,7 @@ 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 + , readDefaultDataFile , openURL ) import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Compat.Time (UTCTime) @@ -152,6 +153,8 @@ class (Functor m, Applicative m, Monad m, MonadError PandocError m) -- | Read the strict ByteString contents from a file path, -- raising an error on failure. readFileStrict :: FilePath -> m B.ByteString + -- | Read file from from Cabal data directory. + readDefaultDataFile :: FilePath -> m B.ByteString -- | Read file from specified user data directory or, -- if not found there, from Cabal data directory. readDataFile :: Maybe FilePath -> FilePath -> m B.ByteString @@ -326,6 +329,7 @@ instance PandocMonad PandocIO where Left e -> throwError $ PandocHttpError u e readFileLazy s = liftIOError BL.readFile s readFileStrict s = liftIOError B.readFile s + readDefaultDataFile fname = liftIOError IO.readDefaultDataFile fname readDataFile mfp fname = liftIOError (IO.readDataFile mfp) fname glob = liftIO . IO.glob getModificationTime fp = liftIOError IO.getModificationTime fp @@ -602,11 +606,11 @@ instance PandocMonad PandocPure where case infoFileContents <$> getFileInfo fp fps of Just bs -> return bs Nothing -> throwError $ PandocResourceNotFound fp - readDataFile Nothing "reference.docx" = + readDefaultDataFile "reference.docx" = (B.concat . BL.toChunks . fromArchive) <$> getsPureState stReferenceDocx - readDataFile Nothing "reference.odt" = + readDefaultDataFile "reference.odt" = (B.concat . BL.toChunks . fromArchive) <$> getsPureState stReferenceODT - readDataFile Nothing fname = do + readDefaultDataFile fname = do let fname' = if fname == "MANUAL.txt" then fname else "data" </> fname readFileStrict fname' readDataFile (Just userDir) fname = do @@ -614,6 +618,7 @@ instance PandocMonad PandocPure where case infoFileContents <$> getFileInfo (userDir </> fname) userDirFiles of Just bs -> return bs Nothing -> readDataFile Nothing fname + readDataFile Nothing fname = readDefaultDataFile fname glob s = do FileTree ftmap <- getsPureState stFiles @@ -640,6 +645,7 @@ instance PandocMonad m => PandocMonad (ParsecT s st m) where openURL = lift . openURL readFileLazy = lift . readFileLazy readFileStrict = lift . readFileStrict + readDefaultDataFile = lift . readDefaultDataFile readDataFile mbuserdir = lift . readDataFile mbuserdir glob = lift . glob getModificationTime = lift . getModificationTime @@ -668,6 +674,7 @@ instance PandocMonad m => PandocMonad (ReaderT r m) where openURL = lift . openURL readFileLazy = lift . readFileLazy readFileStrict = lift . readFileStrict + readDefaultDataFile = lift . readDefaultDataFile readDataFile mbuserdir = lift . readDataFile mbuserdir glob = lift . glob getModificationTime = lift . getModificationTime @@ -684,6 +691,7 @@ instance (PandocMonad m, Monoid w) => PandocMonad (WriterT w m) where openURL = lift . openURL readFileLazy = lift . readFileLazy readFileStrict = lift . readFileStrict + readDefaultDataFile = lift . readDefaultDataFile readDataFile mbuserdir = lift . readDataFile mbuserdir glob = lift . glob getModificationTime = lift . getModificationTime @@ -700,6 +708,7 @@ instance (PandocMonad m, Monoid w) => PandocMonad (RWST r w st m) where openURL = lift . openURL readFileLazy = lift . readFileLazy readFileStrict = lift . readFileStrict + readDefaultDataFile = lift . readDefaultDataFile readDataFile mbuserdir = lift . readDataFile mbuserdir glob = lift . glob getModificationTime = lift . getModificationTime @@ -716,6 +725,7 @@ instance PandocMonad m => PandocMonad (StateT st m) where openURL = lift . openURL readFileLazy = lift . readFileLazy readFileStrict = lift . readFileStrict + readDefaultDataFile = lift . readDefaultDataFile readDataFile mbuserdir = lift . readDataFile mbuserdir glob = lift . glob getModificationTime = lift . getModificationTime diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 53fd38ffd..5503c96f1 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -78,6 +78,7 @@ module Text.Pandoc.Shared ( inDirectory, getDefaultReferenceDocx, getDefaultReferenceODT, + readDefaultDataFile, readDataFile, readDataFileUTF8, openURL, diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index fb6b2013a..a60056845 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -231,7 +231,7 @@ writeDocx opts doc@(Pandoc meta _) = do username <- P.lookupEnv "USERNAME" utctime <- P.getCurrentTime distArchive <- (toArchive . BL.fromStrict) <$> - P.readDataFile Nothing "reference.docx" + P.readDefaultDataFile "reference.docx" refArchive <- case writerReferenceDoc opts of Just f -> toArchive <$> P.readFileLazy f Nothing -> (toArchive . BL.fromStrict) <$> -- cgit v1.2.3 From 13abd97ac0dad1162a6779775207a3db06418ce9 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 10 Aug 2017 23:16:10 -0700 Subject: Class: add setUserDataDir and getUserDataDir. * Add stUserDataDir to CommonState. * Rename stUserDataDir/stCabalDataDir in PureState to stUserDataFiles/stCabalDataFiles. --- src/Text/Pandoc/Class.hs | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index cefc8ee2c..8ef7f3c66 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -56,6 +56,8 @@ module Text.Pandoc.Class ( PandocMonad(..) , getMediaBag , setMediaBag , insertMedia + , setUserDataDir + , getUserDataDir , fetchItem , getInputFiles , getOutputFile @@ -266,6 +268,8 @@ readFileFromDirs (d:ds) f = catchError -- functions like 'setVerbosity' and 'withMediaBag' should be used. data CommonState = CommonState { stLog :: [LogMessage] -- ^ A list of log messages in reverse order + , stUserDataDir :: Maybe FilePath + -- ^ Directory to search for data files , stMediaBag :: MediaBag -- ^ Media parsed from binary containers , stInputFiles :: Maybe [FilePath] @@ -284,6 +288,7 @@ data CommonState = CommonState { stLog :: [LogMessage] instance Default CommonState where def = CommonState { stLog = [] + , stUserDataDir = Nothing , stMediaBag = mempty , stInputFiles = Nothing , stOutputFile = Nothing @@ -359,6 +364,17 @@ parseURIReference' s = | null (uriScheme u) -> Just u -- protocol-relative _ -> Nothing +-- | Set the user data directory in common state. +setUserDataDir :: PandocMonad m + => Maybe FilePath + -> m () +setUserDataDir mbfp = modifyCommonState $ \st -> st{ stUserDataDir = mbfp } + +-- | Get the user data directory from common state. +getUserDataDir :: PandocMonad m + => m (Maybe FilePath) +getUserDataDir = getsCommonState stUserDataDir + -- | Fetch an image or other item from the local filesystem or the net. -- Returns raw content and maybe mime type. fetchItem :: PandocMonad m @@ -497,8 +513,8 @@ data PureState = PureState { stStdGen :: StdGen , stReferenceDocx :: Archive , stReferenceODT :: Archive , stFiles :: FileTree - , stUserDataDir :: FileTree - , stCabalDataDir :: FileTree + , stUserDataFiles :: FileTree + , stCabalDataFiles :: FileTree } instance Default PureState where @@ -511,8 +527,8 @@ instance Default PureState where , stReferenceDocx = emptyArchive , stReferenceODT = emptyArchive , stFiles = mempty - , stUserDataDir = mempty - , stCabalDataDir = mempty + , stUserDataFiles = mempty + , stCabalDataFiles = mempty } @@ -614,7 +630,7 @@ instance PandocMonad PandocPure where let fname' = if fname == "MANUAL.txt" then fname else "data" </> fname readFileStrict fname' readDataFile (Just userDir) fname = do - userDirFiles <- getsPureState stUserDataDir + userDirFiles <- getsPureState stUserDataFiles case infoFileContents <$> getFileInfo (userDir </> fname) userDirFiles of Just bs -> return bs Nothing -> readDataFile Nothing fname -- cgit v1.2.3 From 737d09e325a65ad37f97b03371d1999c0360dea0 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 10 Aug 2017 23:29:25 -0700 Subject: Removed datadir param from readDataFile and getDefaultTemplate. In Text.Pandoc.Class and Text.Pandoc.Template, resp. We now get the datadir from CommonState. --- src/Text/Pandoc/App.hs | 13 +++++++++---- src/Text/Pandoc/Class.hs | 34 ++++++++++++++++++++-------------- src/Text/Pandoc/Templates.hs | 27 +++++++++++++-------------- src/Text/Pandoc/Writers/Docx.hs | 2 +- src/Text/Pandoc/Writers/EPUB.hs | 3 +-- src/Text/Pandoc/Writers/ODT.hs | 3 +-- 6 files changed, 45 insertions(+), 37 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 8a7947de6..3174fe738 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -76,7 +76,8 @@ import System.IO.Error (isDoesNotExistError) import Text.Pandoc import Text.Pandoc.Builder (setMeta) import Text.Pandoc.Class (PandocIO, extractMedia, fillMediaBag, getLog, - setResourcePath, getMediaBag, setTrace, report) + setResourcePath, getMediaBag, setTrace, report, + setUserDataDir) import Text.Pandoc.Highlighting (highlightingStyles) import Text.Pandoc.Lua (runLuaFilter, LuaException(..)) import Text.Pandoc.Writers.Math (defaultMathJaxURL, defaultKaTeXURL) @@ -218,8 +219,9 @@ convertWithOpts opts = do templ <- case optTemplate opts of _ | not standalone -> return Nothing Nothing -> do - deftemp <- runIO $ - getDefaultTemplate datadir format + deftemp <- runIO $ do + setUserDataDir datadir + getDefaultTemplate format case deftemp of Left e -> E.throwIO e Right t -> return (Just t) @@ -444,6 +446,7 @@ convertWithOpts opts = do Native -> nativeNewline runIO' $ do + setUserDataDir datadir when (readerName == "markdown_github" || writerName == "markdown_github") $ report $ Deprecated "markdown_github" "Use gfm instead." @@ -996,7 +999,9 @@ options = , Option "D" ["print-default-template"] (ReqArg (\arg _ -> do - templ <- runIO $ getDefaultTemplate Nothing arg + templ <- runIO $ do + setUserDataDir Nothing + getDefaultTemplate arg case templ of Right t -> UTF8.hPutStr stdout t Left e -> E.throwIO e diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 8ef7f3c66..4697177ed 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -157,9 +157,9 @@ class (Functor m, Applicative m, Monad m, MonadError PandocError m) readFileStrict :: FilePath -> m B.ByteString -- | Read file from from Cabal data directory. readDefaultDataFile :: FilePath -> m B.ByteString - -- | Read file from specified user data directory or, + -- | Read file from user data directory or, -- if not found there, from Cabal data directory. - readDataFile :: Maybe FilePath -> FilePath -> m B.ByteString + readDataFile :: FilePath -> m B.ByteString -- | Return a list of paths that match a glob, relative to -- the working directory. See 'System.FilePath.Glob' for -- the glob syntax. @@ -335,7 +335,9 @@ instance PandocMonad PandocIO where readFileLazy s = liftIOError BL.readFile s readFileStrict s = liftIOError B.readFile s readDefaultDataFile fname = liftIOError IO.readDefaultDataFile fname - readDataFile mfp fname = liftIOError (IO.readDataFile mfp) fname + readDataFile fname = do + datadir <- getUserDataDir + liftIOError (IO.readDataFile datadir) fname glob = liftIO . IO.glob getModificationTime fp = liftIOError IO.getModificationTime fp getCommonState = PandocIO $ lift get @@ -629,12 +631,16 @@ instance PandocMonad PandocPure where readDefaultDataFile fname = do let fname' = if fname == "MANUAL.txt" then fname else "data" </> fname readFileStrict fname' - readDataFile (Just userDir) fname = do - userDirFiles <- getsPureState stUserDataFiles - case infoFileContents <$> getFileInfo (userDir </> fname) userDirFiles of - Just bs -> return bs - Nothing -> readDataFile Nothing fname - readDataFile Nothing fname = readDefaultDataFile fname + readDataFile fname = do + datadir <- getUserDataDir + case datadir of + Just userDir -> do + userDirFiles <- getsPureState stUserDataFiles + case infoFileContents <$> getFileInfo (userDir </> fname) + userDirFiles of + Just bs -> return bs + Nothing -> readDefaultDataFile fname + Nothing -> readDefaultDataFile fname glob s = do FileTree ftmap <- getsPureState stFiles @@ -662,7 +668,7 @@ instance PandocMonad m => PandocMonad (ParsecT s st m) where readFileLazy = lift . readFileLazy readFileStrict = lift . readFileStrict readDefaultDataFile = lift . readDefaultDataFile - readDataFile mbuserdir = lift . readDataFile mbuserdir + readDataFile = lift . readDataFile glob = lift . glob getModificationTime = lift . getModificationTime getCommonState = lift getCommonState @@ -691,7 +697,7 @@ instance PandocMonad m => PandocMonad (ReaderT r m) where readFileLazy = lift . readFileLazy readFileStrict = lift . readFileStrict readDefaultDataFile = lift . readDefaultDataFile - readDataFile mbuserdir = lift . readDataFile mbuserdir + readDataFile = lift . readDataFile glob = lift . glob getModificationTime = lift . getModificationTime getCommonState = lift getCommonState @@ -708,7 +714,7 @@ instance (PandocMonad m, Monoid w) => PandocMonad (WriterT w m) where readFileLazy = lift . readFileLazy readFileStrict = lift . readFileStrict readDefaultDataFile = lift . readDefaultDataFile - readDataFile mbuserdir = lift . readDataFile mbuserdir + readDataFile = lift . readDataFile glob = lift . glob getModificationTime = lift . getModificationTime getCommonState = lift getCommonState @@ -725,7 +731,7 @@ instance (PandocMonad m, Monoid w) => PandocMonad (RWST r w st m) where readFileLazy = lift . readFileLazy readFileStrict = lift . readFileStrict readDefaultDataFile = lift . readDefaultDataFile - readDataFile mbuserdir = lift . readDataFile mbuserdir + readDataFile = lift . readDataFile glob = lift . glob getModificationTime = lift . getModificationTime getCommonState = lift getCommonState @@ -742,7 +748,7 @@ instance PandocMonad m => PandocMonad (StateT st m) where readFileLazy = lift . readFileLazy readFileStrict = lift . readFileStrict readDefaultDataFile = lift . readDefaultDataFile - readDataFile mbuserdir = lift . readDataFile mbuserdir + readDataFile = lift . readDataFile glob = lift . glob getModificationTime = lift . getModificationTime getCommonState = lift getCommonState diff --git a/src/Text/Pandoc/Templates.hs b/src/Text/Pandoc/Templates.hs index 6582e0556..43b7dc37b 100644 --- a/src/Text/Pandoc/Templates.hs +++ b/src/Text/Pandoc/Templates.hs @@ -50,28 +50,27 @@ import qualified Text.Pandoc.UTF8 as UTF8 -- | Get default template for the specified writer. getDefaultTemplate :: PandocMonad m - => (Maybe FilePath) -- ^ User data directory to search 1st - -> String -- ^ Name of writer + => String -- ^ Name of writer -> m String -getDefaultTemplate user writer = do +getDefaultTemplate writer = do let format = takeWhile (`notElem` ("+-" :: String)) writer -- strip off extensions case format of "native" -> return "" "json" -> return "" "docx" -> return "" "fb2" -> return "" - "odt" -> getDefaultTemplate user "opendocument" - "html" -> getDefaultTemplate user "html5" - "docbook" -> getDefaultTemplate user "docbook5" - "epub" -> getDefaultTemplate user "epub3" - "markdown_strict" -> getDefaultTemplate user "markdown" - "multimarkdown" -> getDefaultTemplate user "markdown" - "markdown_github" -> getDefaultTemplate user "markdown" - "markdown_mmd" -> getDefaultTemplate user "markdown" - "markdown_phpextra" -> getDefaultTemplate user "markdown" - "gfm" -> getDefaultTemplate user "commonmark" + "odt" -> getDefaultTemplate "opendocument" + "html" -> getDefaultTemplate "html5" + "docbook" -> getDefaultTemplate "docbook5" + "epub" -> getDefaultTemplate "epub3" + "markdown_strict" -> getDefaultTemplate "markdown" + "multimarkdown" -> getDefaultTemplate "markdown" + "markdown_github" -> getDefaultTemplate "markdown" + "markdown_mmd" -> getDefaultTemplate "markdown" + "markdown_phpextra" -> getDefaultTemplate "markdown" + "gfm" -> getDefaultTemplate "commonmark" _ -> let fname = "templates" </> "default" <.> format - in UTF8.toString <$> readDataFile user fname + in UTF8.toString <$> readDataFile fname -- | Like 'applyTemplate', but runs in PandocMonad and -- raises an error if compilation fails. diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index a60056845..f20edbfaa 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -235,7 +235,7 @@ writeDocx opts doc@(Pandoc meta _) = do refArchive <- case writerReferenceDoc opts of Just f -> toArchive <$> P.readFileLazy f Nothing -> (toArchive . BL.fromStrict) <$> - P.readDataFile datadir "reference.docx" + P.readDataFile "reference.docx" 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/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index b04a7de51..04126fbb7 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -393,8 +393,7 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do -- stylesheet stylesheets <- case epubStylesheets metadata of [] -> (\x -> [B.fromChunks [x]]) <$> - P.readDataFile (writerUserDataDir opts) - "epub.css" + P.readDataFile "epub.css" fs -> mapM P.readFileLazy fs let stylesheetEntries = zipWith (\bs n -> mkEntry ("styles/stylesheet" ++ show n ++ ".css") bs) diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index 785891a9f..160141822 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -78,14 +78,13 @@ pandocToODT :: PandocMonad m -> Pandoc -- ^ Document to convert -> O m B.ByteString pandocToODT opts doc@(Pandoc meta _) = do - let datadir = writerUserDataDir opts let title = docTitle meta lang <- toLang (getLang opts meta) refArchive <- case writerReferenceDoc opts of Just f -> liftM toArchive $ lift $ P.readFileLazy f Nothing -> lift $ (toArchive . B.fromStrict) <$> - P.readDataFile datadir "reference.odt" + P.readDataFile "reference.odt" -- handle formulas and pictures -- picEntriesRef <- P.newIORef ([] :: [Entry]) doc' <- walkM (transformPicMath opts) $ walk fixDisplayMath doc -- cgit v1.2.3 From 0283616a4a4e5c9770a9bf2247efad96dcf984f8 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 10 Aug 2017 23:45:28 -0700 Subject: Remove writerUserDataDir from WriterOptions. It is now carried in CommonState in PandocMonad instances. (And thus it can be used by readers too.) --- src/Text/Pandoc/App.hs | 1 - src/Text/Pandoc/Options.hs | 2 -- src/Text/Pandoc/Writers/Docx.hs | 1 - 3 files changed, 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 3174fe738..47976a499 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -355,7 +355,6 @@ convertWithOpts opts = do writerEmailObfuscation = optEmailObfuscation opts, writerIdentifierPrefix = optIdentifierPrefix opts, writerSourceURL = sourceURL, - writerUserDataDir = datadir, writerHtmlQTags = optHtmlQTags opts, writerTopLevelDivision = optTopLevelDivision opts, writerListings = optListings opts, diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index d7e77010e..89b26deb0 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -202,7 +202,6 @@ data WriterOptions = WriterOptions , writerIdentifierPrefix :: String -- ^ Prefix for section & note ids in HTML -- and for footnote marks in markdown , writerSourceURL :: Maybe String -- ^ Absolute URL + directory of 1st source file - , writerUserDataDir :: Maybe FilePath -- ^ Path of user data directory , writerCiteMethod :: CiteMethod -- ^ How to print cites , writerHtmlQTags :: Bool -- ^ Use @<q>@ tags for quotes in HTML , writerSlideLevel :: Maybe Int -- ^ Force header level of slides @@ -240,7 +239,6 @@ instance Default WriterOptions where , writerEmailObfuscation = NoObfuscation , writerIdentifierPrefix = "" , writerSourceURL = Nothing - , writerUserDataDir = Nothing , writerCiteMethod = Citeproc , writerHtmlQTags = False , writerSlideLevel = Nothing diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index f20edbfaa..166a09d4b 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -226,7 +226,6 @@ writeDocx :: (PandocMonad m) -> Pandoc -- ^ Document to convert -> m BL.ByteString writeDocx opts doc@(Pandoc meta _) = do - let datadir = writerUserDataDir opts let doc' = walk fixDisplayMath $ doc username <- P.lookupEnv "USERNAME" utctime <- P.getCurrentTime -- cgit v1.2.3 From 74212eb1b0e1757fc0ac3e5d45b0ee18bac491e5 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 11 Aug 2017 11:56:54 -0700 Subject: Added support for translations (localization) (see #3559). * readDataFile, readDefaultDataFile, getReferenceDocx, getReferenceODT have been removed from Shared and moved into Class. They are now defined in terms of PandocMonad primitives, rather than being primitve methods of the class. * toLang has been moved from BCP47 to Class. * NoTranslation and CouldNotLoudTranslations have been added to LogMessage. * New module, Text.Pandoc.Translations, exporting Term, Translations, readTranslations. * New functions in Class: translateTerm, setTranslations. Note that nothing is loaded from data files until translateTerm is used; setTranslation just sets the language to be used. * Added two translation data files in data/translations. * LaTeX reader: Support `\setmainlanguage` or `\setdefaultlanguage` (polyglossia) and `\figurename`. --- src/Text/Pandoc/App.hs | 341 ++++++++++++++++++------------------ src/Text/Pandoc/BCP47.hs | 14 -- src/Text/Pandoc/Class.hs | 258 ++++++++++++++++++++++----- src/Text/Pandoc/Logging.hs | 14 ++ src/Text/Pandoc/Lua/PandocModule.hs | 6 +- src/Text/Pandoc/Readers/LaTeX.hs | 135 +++++++++++++- src/Text/Pandoc/Shared.hs | 119 +------------ src/Text/Pandoc/Templates.hs | 2 +- src/Text/Pandoc/Translations.hs | 94 ++++++++++ src/Text/Pandoc/Writers/ConTeXt.hs | 2 +- src/Text/Pandoc/Writers/Docx.hs | 4 +- src/Text/Pandoc/Writers/LaTeX.hs | 4 +- src/Text/Pandoc/Writers/ODT.hs | 4 +- 13 files changed, 640 insertions(+), 357 deletions(-) create mode 100644 src/Text/Pandoc/Translations.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 47976a499..99d9aa4cb 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -41,7 +41,7 @@ module Text.Pandoc.App ( import Control.Applicative ((<|>)) import qualified Control.Exception as E import Control.Monad -import Control.Monad.Except (throwError) +import Control.Monad.Except (throwError, catchError) import Control.Monad.Trans import Data.Monoid import Data.Aeson (FromJSON (..), ToJSON (..), defaultOptions, eitherDecode', @@ -71,21 +71,22 @@ import System.Environment (getArgs, getEnvironment, getProgName) import System.Exit (ExitCode (..), exitSuccess) import System.FilePath import System.IO (nativeNewline, stdout) -import qualified System.IO as IO (Newline (..)) import System.IO.Error (isDoesNotExistError) +import qualified System.IO as IO (Newline (..)) import Text.Pandoc import Text.Pandoc.Builder (setMeta) import Text.Pandoc.Class (PandocIO, extractMedia, fillMediaBag, getLog, setResourcePath, getMediaBag, setTrace, report, - setUserDataDir) + setUserDataDir, readFileStrict, readDataFile, + readDefaultDataFile, setTranslations) import Text.Pandoc.Highlighting (highlightingStyles) +import Text.Pandoc.BCP47 (parseBCP47, Lang(..)) import Text.Pandoc.Lua (runLuaFilter, LuaException(..)) import Text.Pandoc.Writers.Math (defaultMathJaxURL, defaultKaTeXURL) import Text.Pandoc.PDF (makePDF) import Text.Pandoc.Process (pipeProcess) import Text.Pandoc.SelfContained (makeDataURI, makeSelfContained) -import Text.Pandoc.Shared (headerShift, isURI, openURL, readDataFile, - readDataFileUTF8, readDefaultDataFile, +import Text.Pandoc.Shared (headerShift, isURI, openURL, safeRead, tabFilter, eastAsianLineBreakFilter) import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.XML (toEntities) @@ -215,84 +216,6 @@ convertWithOpts opts = do _ -> e let standalone = optStandalone opts || not (isTextFormat format) || pdfOutput - - templ <- case optTemplate opts of - _ | not standalone -> return Nothing - Nothing -> do - deftemp <- runIO $ do - setUserDataDir datadir - getDefaultTemplate format - case deftemp of - Left e -> E.throwIO e - Right t -> return (Just t) - Just tp -> do - -- strip off extensions - let tp' = case takeExtension tp of - "" -> tp <.> format - _ -> tp - Just <$> E.catch (UTF8.readFile tp') - (\e -> if isDoesNotExistError e - then E.catch - (readDataFileUTF8 datadir - ("templates" </> tp')) - (\e' -> let _ = (e' :: E.SomeException) - in E.throwIO e') - else E.throwIO e) - - let addStringAsVariable varname s vars = return $ (varname, s) : vars - - let addContentsAsVariable varname fp vars = do - s <- UTF8.readFile fp - return $ (varname, s) : vars - - -- note: this reverses the list constructed in option parsing, - -- which in turn was reversed from the command-line order, - -- so we end up with the correct order in the variable list: - let withList _ [] vars = return vars - withList f (x:xs) vars = f x vars >>= withList f xs - - variables <- - - withList (addStringAsVariable "sourcefile") - (reverse $ optInputFiles opts) (("outputfile", optOutputFile opts) : optVariables opts) - -- we reverse this list because, unlike - -- the other option lists here, it is - -- not reversed when parsed from CLI arguments. - -- See withList, above. - >>= - withList (addContentsAsVariable "include-before") - (optIncludeBeforeBody opts) - >>= - withList (addContentsAsVariable "include-after") - (optIncludeAfterBody opts) - >>= - withList (addContentsAsVariable "header-includes") - (optIncludeInHeader opts) - >>= - withList (addStringAsVariable "css") (optCss opts) - >>= - maybe return (addStringAsVariable "title-prefix") (optTitlePrefix opts) - >>= - maybe return (addStringAsVariable "epub-cover-image") - (optEpubCoverImage opts) - >>= - (\vars -> case mathMethod of - LaTeXMathML Nothing -> do - s <- readDataFileUTF8 datadir "LaTeXMathML.js" - return $ ("mathml-script", s) : vars - _ -> return vars) - >>= - (\vars -> if format == "dzslides" - then do - dztempl <- readDataFileUTF8 datadir - ("dzslides" </> "template.html") - let dzline = "<!-- {{{{ dzslides core" - let dzcore = unlines - $ dropWhile (not . (dzline `isPrefixOf`)) - $ lines dztempl - return $ ("dzslides-core", dzcore) : vars - else return vars) - let sourceURL = case sources of [] -> Nothing (x:_) -> case parseURI x of @@ -302,21 +225,7 @@ convertWithOpts opts = do uriFragment = "" } _ -> Nothing - abbrevs <- (Set.fromList . filter (not . null) . lines) <$> - case optAbbreviations opts of - Nothing -> readDataFileUTF8 datadir "abbreviations" - Just f -> UTF8.readFile f - - let readerOpts = def{ readerStandalone = standalone - , readerColumns = optColumns opts - , readerTabStop = optTabStop opts - , readerIndentedCodeClasses = optIndentedCodeClasses opts - , readerDefaultImageExtension = - optDefaultImageExtension opts - , readerTrackChanges = optTrackChanges opts - , readerAbbreviations = abbrevs - , readerExtensions = readerExts - } + let addStringAsVariable varname s vars = return $ (varname, s) : vars highlightStyle <- lookupHighlightStyle $ optHighlightStyle opts let addSyntaxMap existingmap f = do @@ -336,40 +245,6 @@ convertWithOpts opts = do (\(syn,dep) -> (T.unpack syn ++ " requires " ++ T.unpack dep ++ " through IncludeRules.")) xs) - let writerOptions = def { writerTemplate = templ, - writerVariables = variables, - writerTabStop = optTabStop opts, - writerTableOfContents = optTableOfContents opts, - writerHTMLMathMethod = mathMethod, - writerIncremental = optIncremental opts, - writerCiteMethod = optCiteMethod opts, - writerNumberSections = optNumberSections opts, - writerNumberOffset = optNumberOffset opts, - writerSectionDivs = optSectionDivs opts, - writerExtensions = writerExts, - writerReferenceLinks = optReferenceLinks opts, - writerReferenceLocation = optReferenceLocation opts, - writerDpi = optDpi opts, - writerWrapText = optWrapText opts, - writerColumns = optColumns opts, - writerEmailObfuscation = optEmailObfuscation opts, - writerIdentifierPrefix = optIdentifierPrefix opts, - writerSourceURL = sourceURL, - writerHtmlQTags = optHtmlQTags opts, - writerTopLevelDivision = optTopLevelDivision opts, - writerListings = optListings opts, - writerSlideLevel = optSlideLevel opts, - writerHighlightStyle = highlightStyle, - writerSetextHeaders = optSetextHeaders opts, - writerEpubSubdirectory = optEpubSubdirectory opts, - writerEpubMetadata = epubMetadata, - writerEpubFonts = optEpubFonts opts, - writerEpubChapterLevel = optEpubChapterLevel opts, - writerTOCDepth = optTOCDepth opts, - writerReferenceDoc = optReferenceDoc opts, - writerLaTeXArgs = optLaTeXEngineArgs opts, - writerSyntaxMap = syntaxMap - } #ifdef _WINDOWS @@ -383,18 +258,6 @@ convertWithOpts opts = do "Specify an output file using the -o option." - let transforms = (case optBaseHeaderLevel opts of - x | x > 1 -> (headerShift (x - 1) :) - | otherwise -> id) $ - (if extensionEnabled Ext_east_asian_line_breaks - readerExts && - not (extensionEnabled Ext_east_asian_line_breaks - writerExts && - writerWrapText writerOptions == WrapPreserve) - then (eastAsianLineBreakFilter :) - else id) - [] - let convertTabs = tabFilter (if optPreserveTabs opts || readerName == "t2t" then 0 else optTabStop opts) @@ -419,32 +282,175 @@ convertWithOpts opts = do E.throwIO PandocFailOnWarningError return res - let sourceToDoc :: [FilePath] -> PandocIO Pandoc - sourceToDoc sources' = - case reader of - TextReader r - | optFileScope opts || readerName == "json" -> - mconcat <$> mapM (readSource >=> r readerOpts) sources - | otherwise -> - readSources sources' >>= r readerOpts - ByteStringReader r -> - mconcat <$> mapM (readFile' >=> r readerOpts) sources - - metadata <- if format == "jats" && - isNothing (lookup "csl" (optMetadata opts)) && - isNothing (lookup "citation-style" (optMetadata opts)) - then do - jatsCSL <- readDataFile datadir "jats.csl" - let jatsEncoded = makeDataURI ("application/xml", jatsCSL) - return $ ("csl", jatsEncoded) : optMetadata opts - else return $ optMetadata opts - let eol = case optEol opts of CRLF -> IO.CRLF LF -> IO.LF Native -> nativeNewline + -- note: this reverses the list constructed in option parsing, + -- which in turn was reversed from the command-line order, + -- so we end up with the correct order in the variable list: + let withList _ [] vars = return vars + withList f (x:xs) vars = f x vars >>= withList f xs + + let addContentsAsVariable varname fp vars = do + s <- UTF8.toString <$> readFileStrict fp + return $ (varname, s) : vars + runIO' $ do + variables <- + withList (addStringAsVariable "sourcefile") + (reverse $ optInputFiles opts) + (("outputfile", optOutputFile opts) : optVariables opts) + -- we reverse this list because, unlike + -- the other option lists here, it is + -- not reversed when parsed from CLI arguments. + -- See withList, above. + >>= + withList (addContentsAsVariable "include-before") + (optIncludeBeforeBody opts) + >>= + withList (addContentsAsVariable "include-after") + (optIncludeAfterBody opts) + >>= + withList (addContentsAsVariable "header-includes") + (optIncludeInHeader opts) + >>= + withList (addStringAsVariable "css") (optCss opts) + >>= + maybe return (addStringAsVariable "title-prefix") + (optTitlePrefix opts) + >>= + maybe return (addStringAsVariable "epub-cover-image") + (optEpubCoverImage opts) + >>= + (\vars -> case mathMethod of + LaTeXMathML Nothing -> do + s <- UTF8.toString <$> readDataFile "LaTeXMathML.js" + return $ ("mathml-script", s) : vars + _ -> return vars) + >>= + (\vars -> if format == "dzslides" + then do + dztempl <- UTF8.toString <$> readDataFile + ("dzslides" </> "template.html") + let dzline = "<!-- {{{{ dzslides core" + let dzcore = unlines + $ dropWhile (not . (dzline `isPrefixOf`)) + $ lines dztempl + return $ ("dzslides-core", dzcore) : vars + else return vars) + + abbrevs <- (Set.fromList . filter (not . null) . lines) <$> + case optAbbreviations opts of + Nothing -> UTF8.toString <$> readDataFile "abbreviations" + Just f -> UTF8.toString <$> readFileStrict f + + templ <- case optTemplate opts of + _ | not standalone -> return Nothing + Nothing -> Just <$> getDefaultTemplate format + Just tp -> do + -- strip off extensions + let tp' = case takeExtension tp of + "" -> tp <.> format + _ -> tp + Just . UTF8.toString <$> + (readFileStrict tp' `catchError` + (\e -> + case e of + PandocIOError _ e' | + isDoesNotExistError e' -> + readDataFile ("templates" </> tp') + _ -> throwError e)) + + metadata <- if format == "jats" && + isNothing (lookup "csl" (optMetadata opts)) && + isNothing (lookup "citation-style" (optMetadata opts)) + then do + jatsCSL <- readDataFile "jats.csl" + let jatsEncoded = makeDataURI + ("application/xml", jatsCSL) + return $ ("csl", jatsEncoded) : optMetadata opts + else return $ optMetadata opts + + case lookup "lang" (optMetadata opts) of + Just l -> case parseBCP47 l of + Left _ -> return () + Right l' -> setTranslations l' + Nothing -> setTranslations $ Lang "en" "" "US" [] + + let writerOptions = def { + writerTemplate = templ + , writerVariables = variables + , writerTabStop = optTabStop opts + , writerTableOfContents = optTableOfContents opts + , writerHTMLMathMethod = mathMethod + , writerIncremental = optIncremental opts + , writerCiteMethod = optCiteMethod opts + , writerNumberSections = optNumberSections opts + , writerNumberOffset = optNumberOffset opts + , writerSectionDivs = optSectionDivs opts + , writerExtensions = writerExts + , writerReferenceLinks = optReferenceLinks opts + , writerReferenceLocation = optReferenceLocation opts + , writerDpi = optDpi opts + , writerWrapText = optWrapText opts + , writerColumns = optColumns opts + , writerEmailObfuscation = optEmailObfuscation opts + , writerIdentifierPrefix = optIdentifierPrefix opts + , writerSourceURL = sourceURL + , writerHtmlQTags = optHtmlQTags opts + , writerTopLevelDivision = optTopLevelDivision opts + , writerListings = optListings opts + , writerSlideLevel = optSlideLevel opts + , writerHighlightStyle = highlightStyle + , writerSetextHeaders = optSetextHeaders opts + , writerEpubSubdirectory = optEpubSubdirectory opts + , writerEpubMetadata = epubMetadata + , writerEpubFonts = optEpubFonts opts + , writerEpubChapterLevel = optEpubChapterLevel opts + , writerTOCDepth = optTOCDepth opts + , writerReferenceDoc = optReferenceDoc opts + , writerLaTeXArgs = optLaTeXEngineArgs opts + , writerSyntaxMap = syntaxMap + } + + let readerOpts = def{ + readerStandalone = standalone + , readerColumns = optColumns opts + , readerTabStop = optTabStop opts + , readerIndentedCodeClasses = optIndentedCodeClasses opts + , readerDefaultImageExtension = + optDefaultImageExtension opts + , readerTrackChanges = optTrackChanges opts + , readerAbbreviations = abbrevs + , readerExtensions = readerExts + } + + let transforms = (case optBaseHeaderLevel opts of + x | x > 1 -> (headerShift (x - 1) :) + | otherwise -> id) $ + (if extensionEnabled Ext_east_asian_line_breaks + readerExts && + not (extensionEnabled Ext_east_asian_line_breaks + writerExts && + writerWrapText writerOptions == WrapPreserve) + then (eastAsianLineBreakFilter :) + else id) + [] + + let sourceToDoc :: [FilePath] -> PandocIO Pandoc + sourceToDoc sources' = + case reader of + TextReader r + | optFileScope opts || readerName == "json" -> + mconcat <$> mapM (readSource >=> r readerOpts) sources + | otherwise -> + readSources sources' >>= r readerOpts + ByteStringReader r -> + mconcat <$> mapM (readFile' >=> r readerOpts) sources + + setUserDataDir datadir when (readerName == "markdown_github" || writerName == "markdown_github") $ @@ -1011,7 +1017,8 @@ options = , Option "" ["print-default-data-file"] (ReqArg (\arg _ -> do - readDefaultDataFile arg >>= BS.hPutStr stdout + runIOorExplode $ + readDefaultDataFile arg >>= liftIO . BS.hPutStr stdout exitSuccess) "FILE") "" -- "Print default data file" @@ -1469,7 +1476,9 @@ options = (NoArg (\_ -> do ddir <- getDataDir - tpl <- readDataFileUTF8 Nothing "bash_completion.tpl" + tpl <- runIOorExplode $ + UTF8.toString <$> + readDefaultDataFile "bash_completion.tpl" let optnames (Option shorts longs _ _) = map (\c -> ['-',c]) shorts ++ map ("--" ++) longs diff --git a/src/Text/Pandoc/BCP47.hs b/src/Text/Pandoc/BCP47.hs index b4b55c5d4..1790ccfb7 100644 --- a/src/Text/Pandoc/BCP47.hs +++ b/src/Text/Pandoc/BCP47.hs @@ -29,7 +29,6 @@ Functions for parsing and rendering BCP47 language identifiers. -} module Text.Pandoc.BCP47 ( getLang - , toLang , parseBCP47 , Lang(..) , renderLang @@ -40,8 +39,6 @@ import Data.Char (isAscii, isLetter, isUpper, isLower, toUpper, toLower, isAlphaNum) import Data.List (intercalate) import Text.Pandoc.Definition -import Text.Pandoc.Class (PandocMonad, report) -import Text.Pandoc.Logging import Text.Pandoc.Options import qualified Text.Parsec as P @@ -68,17 +65,6 @@ getLang opts meta = Just (MetaString s) -> Just s _ -> Nothing --- | Convert BCP47 string to a Lang, issuing warning --- if there are problems. -toLang :: PandocMonad m => Maybe String -> m (Maybe Lang) -toLang Nothing = return Nothing -toLang (Just s) = - case parseBCP47 s of - Left _ -> do - report $ InvalidLang s - return Nothing - Right l -> return (Just l) - -- | Parse a BCP 47 string as a Lang. Currently we parse -- extensions and private-use fields as "variants," even -- though officially they aren't. diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 4697177ed..a3dd9ad58 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE CPP #-} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE FlexibleInstances #-} @@ -71,8 +72,15 @@ module Text.Pandoc.Class ( PandocMonad(..) , runIO , runIOorExplode , runPure + , readDefaultDataFile + , readDataFile , fillMediaBag , extractMedia + , toLang + , setTranslations + , translateTerm + , Translations(..) + , Term(..) ) where import Prelude hiding (readFile) @@ -81,10 +89,9 @@ 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 ( readDataFile - , readDefaultDataFile - , openURL ) +import qualified Text.Pandoc.Shared as Shared import qualified Text.Pandoc.UTF8 as UTF8 +import qualified System.Directory as Directory import Text.Pandoc.Compat.Time (UTCTime) import Text.Pandoc.Logging import Text.Parsec (ParsecT, getPosition, sourceLine, sourceName) @@ -98,6 +105,7 @@ import Data.Time.Clock.POSIX ( utcTimeToPOSIXSeconds , posixSecondsToUTCTime , POSIXTime ) import Data.Time.LocalTime (TimeZone, ZonedTime, utcToZonedTime, utc) +import qualified System.FilePath.Posix as Posix import Network.URI ( escapeURIString, nonStrictRelativeTo, unEscapeString, parseURIReference, isAllowedInURI, parseURI, URI(..) ) @@ -111,7 +119,7 @@ import qualified System.Environment as IO (lookupEnv) import System.FilePath.Glob (match, compile) import System.Directory (createDirectoryIfMissing, getDirectoryContents, doesDirectoryExist) -import System.FilePath ((</>), (<.>), takeDirectory, +import System.FilePath ((</>), (<.>), takeDirectory, splitDirectories, takeExtension, dropExtension, isRelative, normalise) import qualified System.FilePath.Glob as IO (glob) import qualified System.Directory as IO (getModificationTime) @@ -121,13 +129,21 @@ import Control.Monad.State.Strict import Control.Monad.Except import Control.Monad.Writer (WriterT) import Control.Monad.RWS (RWST) +import Codec.Archive.Zip 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 Text.Pandoc.BCP47 (Lang(..), parseBCP47, renderLang) +import Text.Pandoc.Translations (Term(..), Translations(..), readTranslations) import qualified Debug.Trace +#ifdef EMBED_DATA_FILES +import Text.Pandoc.Data (dataFiles) +#else +import Paths_pandoc (getDataFileName) +#endif -- | The PandocMonad typeclass contains all the potentially -- IO-related functions used in pandoc's readers and writers. @@ -155,15 +171,12 @@ class (Functor m, Applicative m, Monad m, MonadError PandocError m) -- | Read the strict ByteString contents from a file path, -- raising an error on failure. readFileStrict :: FilePath -> m B.ByteString - -- | Read file from from Cabal data directory. - readDefaultDataFile :: FilePath -> m B.ByteString - -- | Read file from user data directory or, - -- if not found there, from Cabal data directory. - readDataFile :: FilePath -> m B.ByteString -- | Return a list of paths that match a glob, relative to -- the working directory. See 'System.FilePath.Glob' for -- the glob syntax. glob :: String -> m [FilePath] + -- | Returns True if file exists. + fileExists :: FilePath -> m Bool -- | Return the modification time of a file. getModificationTime :: FilePath -> m UTCTime -- | Get the value of the 'CommonState' used by all instances @@ -272,6 +285,9 @@ data CommonState = CommonState { stLog :: [LogMessage] -- ^ Directory to search for data files , stMediaBag :: MediaBag -- ^ Media parsed from binary containers + , stTranslations :: Maybe + (Lang, Maybe Translations) + -- ^ Translations for localization , stInputFiles :: Maybe [FilePath] -- ^ List of input files from command line , stOutputFile :: Maybe FilePath @@ -290,6 +306,7 @@ instance Default CommonState where def = CommonState { stLog = [] , stUserDataDir = Nothing , stMediaBag = mempty + , stTranslations = Nothing , stInputFiles = Nothing , stOutputFile = Nothing , stResourcePath = ["."] @@ -297,6 +314,71 @@ instance Default CommonState where , stTrace = False } +-- | Convert BCP47 string to a Lang, issuing warning +-- if there are problems. +toLang :: PandocMonad m => Maybe String -> m (Maybe Lang) +toLang Nothing = return Nothing +toLang (Just s) = + case parseBCP47 s of + Left _ -> do + report $ InvalidLang s + return Nothing + Right l -> return (Just l) + +-- | Select the language to use with 'translateTerm'. +-- Note that this does not read a translation file; +-- that is only done the first time 'translateTerm' is +-- used. +setTranslations :: PandocMonad m => Lang -> m () +setTranslations lang = + modifyCommonState $ \st -> st{ stTranslations = Just (lang, Nothing) } + +-- | Load term map. +getTranslations :: PandocMonad m => m Translations +getTranslations = do + mbtrans <- getsCommonState stTranslations + case mbtrans of + Nothing -> return mempty -- no language defined + Just (_, Just t) -> return t + Just (lang, Nothing) -> do -- read from file + let translationFile = "translations/" ++ renderLang lang ++ ".trans" + let fallbackFile = "translations/" ++ langLanguage lang ++ ".trans" + let getTrans bs = + case readTranslations (UTF8.toString bs) of + Left e -> do + report $ CouldNotLoadTranslations (renderLang lang) e + -- make sure we don't try again... + modifyCommonState $ \st -> + st{ stTranslations = Nothing } + return mempty + Right t -> do + modifyCommonState $ \st -> + st{ stTranslations = Just (lang, Just t) } + return t + catchError (readDataFile translationFile >>= getTrans) + (\_ -> + catchError (readDataFile fallbackFile >>= getTrans) + (\e -> do + report $ CouldNotLoadTranslations (renderLang lang) + $ case e of + PandocCouldNotFindDataFileError _ -> + ("data file " ++ fallbackFile ++ " not found") + _ -> "" + -- make sure we don't try again... + modifyCommonState $ \st -> st{ stTranslations = Nothing } + return mempty)) + +-- | Get a translation from the current term map. +-- Issue a warning if the term is not defined. +translateTerm :: PandocMonad m => Term -> m String +translateTerm term = do + Translations termMap <- getTranslations + case M.lookup term termMap of + Just s -> return s + Nothing -> do + report $ NoTranslation (show term) + return "" + -- | Evaluate a 'PandocIO' operation. runIO :: PandocIO a -> IO (Either PandocError a) runIO ma = flip evalStateT def $ runExceptT $ unPandocIO ma @@ -317,7 +399,7 @@ liftIOError :: (String -> IO a) -> String -> PandocIO a liftIOError f u = do res <- liftIO $ tryIOError $ f u case res of - Left e -> throwError $ PandocIOError u e + Left e -> throwError $ PandocIOError u e Right r -> return r instance PandocMonad PandocIO where @@ -328,17 +410,15 @@ instance PandocMonad PandocIO where newUniqueHash = hashUnique <$> liftIO IO.newUnique openURL u = do report $ Fetching u - res <- liftIO (IO.openURL u) + res <- liftIOError Shared.openURL u case res of Right r -> return r Left e -> throwError $ PandocHttpError u e readFileLazy s = liftIOError BL.readFile s readFileStrict s = liftIOError B.readFile s - readDefaultDataFile fname = liftIOError IO.readDefaultDataFile fname - readDataFile fname = do - datadir <- getUserDataDir - liftIOError (IO.readDataFile datadir) fname - glob = liftIO . IO.glob + + glob = liftIOError IO.glob + fileExists = liftIOError Directory.doesFileExist getModificationTime fp = liftIOError IO.getModificationTime fp getCommonState = PandocIO $ lift get putCommonState x = PandocIO $ lift $ put x @@ -432,6 +512,109 @@ downloadOrRead sourceURL s = convertSlash '\\' = '/' convertSlash x = x +getDefaultReferenceDocx :: PandocMonad m => m Archive +getDefaultReferenceDocx = do + let paths = ["[Content_Types].xml", + "_rels/.rels", + "docProps/app.xml", + "docProps/core.xml", + "word/document.xml", + "word/fontTable.xml", + "word/footnotes.xml", + "word/numbering.xml", + "word/settings.xml", + "word/webSettings.xml", + "word/styles.xml", + "word/_rels/document.xml.rels", + "word/_rels/footnotes.xml.rels", + "word/theme/theme1.xml"] + let toLazy = BL.fromChunks . (:[]) + let pathToEntry path = do + epochtime <- (floor . utcTimeToPOSIXSeconds) <$> getCurrentTime + contents <- toLazy <$> readDataFile ("docx/" ++ path) + return $ toEntry path epochtime contents + datadir <- getUserDataDir + mbArchive <- case datadir of + Nothing -> return Nothing + Just d -> do + exists <- fileExists (d </> "reference.docx") + if exists + then return (Just (d </> "reference.docx")) + else return Nothing + case mbArchive of + Just arch -> toArchive <$> readFileLazy arch + Nothing -> foldr addEntryToArchive emptyArchive <$> + mapM pathToEntry paths + +getDefaultReferenceODT :: PandocMonad m => m Archive +getDefaultReferenceODT = do + let paths = ["mimetype", + "manifest.rdf", + "styles.xml", + "content.xml", + "meta.xml", + "settings.xml", + "Configurations2/accelerator/current.xml", + "Thumbnails/thumbnail.png", + "META-INF/manifest.xml"] + let pathToEntry path = do epochtime <- floor `fmap` getPOSIXTime + contents <- (BL.fromChunks . (:[])) `fmap` + readDataFile ("odt/" ++ path) + return $ toEntry path epochtime contents + datadir <- getUserDataDir + mbArchive <- case datadir of + Nothing -> return Nothing + Just d -> do + exists <- fileExists (d </> "reference.odt") + if exists + then return (Just (d </> "reference.odt")) + else return Nothing + case mbArchive of + Just arch -> toArchive <$> readFileLazy arch + Nothing -> foldr addEntryToArchive emptyArchive <$> + mapM pathToEntry paths + +-- | Read file from user data directory or, +-- if not found there, from Cabal data directory. +readDataFile :: PandocMonad m => FilePath -> m B.ByteString +readDataFile fname = do + datadir <- getUserDataDir + case datadir of + Nothing -> readDefaultDataFile fname + Just userDir -> do + exists <- fileExists (userDir </> fname) + if exists + then readFileStrict (userDir </> fname) + else readDefaultDataFile fname + +-- | Read file from from Cabal data directory. +readDefaultDataFile :: PandocMonad m => FilePath -> m B.ByteString +readDefaultDataFile "reference.docx" = + (B.concat . BL.toChunks . fromArchive) <$> getDefaultReferenceDocx +readDefaultDataFile "reference.odt" = + (B.concat . BL.toChunks . fromArchive) <$> getDefaultReferenceODT +readDefaultDataFile fname = +#ifdef EMBED_DATA_FILES + case lookup (makeCanonical fname) dataFiles of + Nothing -> throwError $ PandocCouldNotFindDataFileError fname + Just contents -> return contents + where makeCanonical = Posix.joinPath . transformPathParts . splitDirectories + transformPathParts = reverse . foldl go [] + go as "." = as + go (_:as) ".." = as + go as x = x : as +#else + getDataFileName fname' >>= checkExistence >>= readFileStrict + where fname' = if fname == "MANUAL.txt" then fname else "data" </> fname + +checkExistence :: PandocMonad m => FilePath -> m FilePath +checkExistence fn = do + exists <- fileExists fn + if exists + then return fn + else throwError $ PandocCouldNotFindDataFileError fn +#endif + withPaths :: PandocMonad m => [FilePath] -> (FilePath -> m a) -> FilePath -> m a withPaths [] _ fp = throwError $ PandocResourceNotFound fp withPaths (p:ps) action fp = @@ -491,9 +674,8 @@ writeMedia dir mediabag subpath = do Nothing -> throwError $ PandocResourceNotFound subpath Just (_, bs) -> do report $ Extracting fullpath - liftIO $ do - createDirectoryIfMissing True $ takeDirectory fullpath - BL.writeFile fullpath bs + liftIOError (createDirectoryIfMissing True) (takeDirectory fullpath) + liftIOError (\p -> BL.writeFile p bs) fullpath adjustImagePath :: FilePath -> [FilePath] -> Inline -> Inline adjustImagePath dir paths (Image attr lab (src, tit)) @@ -624,28 +806,17 @@ instance PandocMonad PandocPure where case infoFileContents <$> getFileInfo fp fps of Just bs -> return bs Nothing -> throwError $ PandocResourceNotFound fp - readDefaultDataFile "reference.docx" = - (B.concat . BL.toChunks . fromArchive) <$> getsPureState stReferenceDocx - readDefaultDataFile "reference.odt" = - (B.concat . BL.toChunks . fromArchive) <$> getsPureState stReferenceODT - readDefaultDataFile fname = do - let fname' = if fname == "MANUAL.txt" then fname else "data" </> fname - readFileStrict fname' - readDataFile fname = do - datadir <- getUserDataDir - case datadir of - Just userDir -> do - userDirFiles <- getsPureState stUserDataFiles - case infoFileContents <$> getFileInfo (userDir </> fname) - userDirFiles of - Just bs -> return bs - Nothing -> readDefaultDataFile fname - Nothing -> readDefaultDataFile fname glob s = do FileTree ftmap <- getsPureState stFiles return $ filter (match (compile s)) $ M.keys ftmap + fileExists fp = do + fps <- getsPureState stFiles + case getFileInfo fp fps of + Nothing -> return False + Just _ -> return True + getModificationTime fp = do fps <- getsPureState stFiles case infoFileMTime <$> getFileInfo fp fps of @@ -667,9 +838,8 @@ instance PandocMonad m => PandocMonad (ParsecT s st m) where openURL = lift . openURL readFileLazy = lift . readFileLazy readFileStrict = lift . readFileStrict - readDefaultDataFile = lift . readDefaultDataFile - readDataFile = lift . readDataFile glob = lift . glob + fileExists = lift . fileExists getModificationTime = lift . getModificationTime getCommonState = lift getCommonState putCommonState = lift . putCommonState @@ -696,9 +866,8 @@ instance PandocMonad m => PandocMonad (ReaderT r m) where openURL = lift . openURL readFileLazy = lift . readFileLazy readFileStrict = lift . readFileStrict - readDefaultDataFile = lift . readDefaultDataFile - readDataFile = lift . readDataFile glob = lift . glob + fileExists = lift . fileExists getModificationTime = lift . getModificationTime getCommonState = lift getCommonState putCommonState = lift . putCommonState @@ -713,9 +882,8 @@ instance (PandocMonad m, Monoid w) => PandocMonad (WriterT w m) where openURL = lift . openURL readFileLazy = lift . readFileLazy readFileStrict = lift . readFileStrict - readDefaultDataFile = lift . readDefaultDataFile - readDataFile = lift . readDataFile glob = lift . glob + fileExists = lift . fileExists getModificationTime = lift . getModificationTime getCommonState = lift getCommonState putCommonState = lift . putCommonState @@ -730,9 +898,8 @@ instance (PandocMonad m, Monoid w) => PandocMonad (RWST r w st m) where openURL = lift . openURL readFileLazy = lift . readFileLazy readFileStrict = lift . readFileStrict - readDefaultDataFile = lift . readDefaultDataFile - readDataFile = lift . readDataFile glob = lift . glob + fileExists = lift . fileExists getModificationTime = lift . getModificationTime getCommonState = lift getCommonState putCommonState = lift . putCommonState @@ -747,9 +914,8 @@ instance PandocMonad m => PandocMonad (StateT st m) where openURL = lift . openURL readFileLazy = lift . readFileLazy readFileStrict = lift . readFileStrict - readDefaultDataFile = lift . readDefaultDataFile - readDataFile = lift . readDataFile glob = lift . glob + fileExists = lift . fileExists getModificationTime = lift . getModificationTime getCommonState = lift getCommonState putCommonState = lift . putCommonState diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs index ac45b0a66..832a1f4df 100644 --- a/src/Text/Pandoc/Logging.hs +++ b/src/Text/Pandoc/Logging.hs @@ -95,6 +95,8 @@ data LogMessage = | CouldNotHighlight String | MissingCharacter String | Deprecated String String + | NoTranslation String + | CouldNotLoadTranslations String String deriving (Show, Eq, Data, Ord, Typeable, Generic) instance ToJSON LogMessage where @@ -195,6 +197,11 @@ instance ToJSON LogMessage where Deprecated thing msg -> ["thing" .= Text.pack thing, "message" .= Text.pack msg] + NoTranslation term -> + ["term" .= Text.pack term] + CouldNotLoadTranslations lang msg -> + ["lang" .= Text.pack lang, + "message" .= Text.pack msg] showPos :: SourcePos -> String @@ -282,6 +289,11 @@ showLogMessage msg = if null m then "" else ". " ++ m + NoTranslation t -> + "The term " ++ t ++ " has no translation defined." + CouldNotLoadTranslations lang m -> + "Could not load translations for " ++ lang ++ + if null m then "" else ('\n':m) messageVerbosity:: LogMessage -> Verbosity messageVerbosity msg = @@ -314,3 +326,5 @@ messageVerbosity msg = CouldNotHighlight{} -> WARNING MissingCharacter{} -> WARNING Deprecated{} -> WARNING + NoTranslation{} -> WARNING + CouldNotLoadTranslations{} -> WARNING diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index 2d0baf4f8..d46ed3629 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -32,13 +32,12 @@ import Data.ByteString.Char8 (unpack) import Data.Default (Default (..)) import Data.Text (pack) import Scripting.Lua (LuaState, call, push, pushhsfunction, rawset) -import Text.Pandoc.Class hiding (readDataFile) +import Text.Pandoc.Class import Text.Pandoc.Definition (Pandoc) import Text.Pandoc.Options (ReaderOptions(readerExtensions)) import Text.Pandoc.Lua.Compat (loadstring) import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Readers (Reader (..), getReader) -import Text.Pandoc.Shared (readDataFile) -- | Push the "pandoc" on the lua stack. pushPandocModule :: Maybe FilePath -> LuaState -> IO () @@ -52,7 +51,8 @@ pushPandocModule datadir lua = do -- | Get the string representation of the pandoc module pandocModuleScript :: Maybe FilePath -> IO String -pandocModuleScript datadir = unpack <$> readDataFile datadir "pandoc.lua" +pandocModuleScript datadir = unpack <$> + runIOorExplode (setUserDataDir datadir >> readDataFile "pandoc.lua") read_doc :: String -> String -> IO (Either String Pandoc) read_doc formatSpec content = do diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 0f0e71b93..1fe4594ed 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -55,8 +55,11 @@ import Data.Maybe (fromMaybe, maybeToList) import Safe (minimumDef) import System.FilePath (addExtension, replaceExtension, takeExtension) import Text.Pandoc.Builder -import Text.Pandoc.Class (PandocMonad, PandocPure, lookupEnv, readFileFromDirs, - report, setResourcePath, getResourcePath) +import Text.Pandoc.Class (PandocMonad, PandocPure, lookupEnv, + readFileFromDirs, report, setResourcePath, + getResourcePath, setTranslations, translateTerm) +import qualified Text.Pandoc.Translations as Translations +import Text.Pandoc.BCP47 (Lang(..)) import Text.Pandoc.Highlighting (fromListingsLanguage, languagesByExtension) import Text.Pandoc.ImageSize (numUnit, showFl) import Text.Pandoc.Logging @@ -1247,6 +1250,7 @@ inlineCommands = M.fromList $ removeDoubleQuotes . untokenize <$> braced mkImage options src) , ("enquote", enquote) + , ("figurename", doTerm Translations.Figure) , ("cite", citation "cite" NormalCitation False) , ("Cite", citation "Cite" NormalCitation False) , ("citep", citation "citep" NormalCitation False) @@ -1326,6 +1330,12 @@ inlineCommands = M.fromList $ , ("ifstrequal", ifstrequal) ] +doTerm :: PandocMonad m => Translations.Term -> LP m Inlines +doTerm term = do + s <- (symbol '~' >> return (str "\160")) <|> return space + t <- translateTerm term + return (str t <> s) + ifstrequal :: PandocMonad m => LP m Inlines ifstrequal = do str1 <- tok @@ -1759,6 +1769,9 @@ blockCommands = M.fromList $ -- includes , ("lstinputlisting", inputListing) , ("graphicspath", graphicsPath) + -- polyglossia + , ("setdefaultlanguage", setDefaultLanguage) + , ("setmainlanguage", setDefaultLanguage) -- hyperlink , ("hypertarget", try $ braced >> grouped block) -- LaTeX colors @@ -2206,3 +2219,121 @@ block = (mempty <$ spaces1) blocks :: PandocMonad m => LP m Blocks blocks = mconcat <$> many block +setDefaultLanguage :: PandocMonad m => LP m Blocks +setDefaultLanguage = do + o <- option "" $ (T.unpack . T.filter (\c -> c /= '[' && c /= ']')) + <$> rawopt + polylang <- toksToString <$> braced + case polyglossiaLangToBCP47 polylang o of + Nothing -> return () -- TODO mzero? warning? + Just l -> setTranslations l + return mempty + +polyglossiaLangToBCP47 :: String -> String -> Maybe Lang +polyglossiaLangToBCP47 s o = + case (s, filter (/=' ') o) of + ("arabic", "locale=algeria") -> Just $ Lang "ar" "" "DZ" [] + ("arabic", "locale=mashriq") -> Just $ Lang "ar" "" "SY" [] + ("arabic", "locale=libya") -> Just $ Lang "ar" "" "LY" [] + ("arabic", "locale=morocco") -> Just $ Lang "ar" "" "MA" [] + ("arabic", "locale=mauritania") -> Just $ Lang "ar" "" "MR" [] + ("arabic", "locale=tunisia") -> Just $ Lang "ar" "" "TN" [] + ("german", "spelling=old") -> Just $ Lang "de" "" "DE" ["1901"] + ("german", "variant=austrian,spelling=old") + -> Just $ Lang "de" "" "AT" ["1901"] + ("german", "variant=austrian") -> Just $ Lang "de" "" "AT" [] + ("german", "variant=swiss,spelling=old") + -> Just $ Lang "de" "" "CH" ["1901"] + ("german", "variant=swiss") -> Just $ Lang "de" "" "CH" [] + ("german", _) -> Just $ Lang "de" "" "" [] + ("lsorbian", _) -> Just $ Lang "dsb" "" "" [] + ("greek", "variant=poly") -> Just $ Lang "el" "" "polyton" [] + ("english", "variant=australian") -> Just $ Lang "en" "" "AU" [] + ("english", "variant=canadian") -> Just $ Lang "en" "" "CA" [] + ("english", "variant=british") -> Just $ Lang "en" "" "GB" [] + ("english", "variant=newzealand") -> Just $ Lang "en" "" "NZ" [] + ("english", "variant=american") -> Just $ Lang "en" "" "US" [] + ("greek", "variant=ancient") -> Just $ Lang "grc" "" "" [] + ("usorbian", _) -> Just $ Lang "hsb" "" "" [] + ("latin", "variant=classic") -> Just $ Lang "la" "" "" ["x-classic"] + ("slovenian", _) -> Just $ Lang "sl" "" "" [] + ("serbianc", _) -> Just $ Lang "sr" "cyrl" "" [] + ("pinyin", _) -> Just $ Lang "zh" "Latn" "" ["pinyin"] + ("afrikaans", _) -> Just $ Lang "af" "" "" [] + ("amharic", _) -> Just $ Lang "am" "" "" [] + ("arabic", _) -> Just $ Lang "ar" "" "" [] + ("assamese", _) -> Just $ Lang "as" "" "" [] + ("asturian", _) -> Just $ Lang "ast" "" "" [] + ("bulgarian", _) -> Just $ Lang "bg" "" "" [] + ("bengali", _) -> Just $ Lang "bn" "" "" [] + ("tibetan", _) -> Just $ Lang "bo" "" "" [] + ("breton", _) -> Just $ Lang "br" "" "" [] + ("catalan", _) -> Just $ Lang "ca" "" "" [] + ("welsh", _) -> Just $ Lang "cy" "" "" [] + ("czech", _) -> Just $ Lang "cs" "" "" [] + ("coptic", _) -> Just $ Lang "cop" "" "" [] + ("danish", _) -> Just $ Lang "da" "" "" [] + ("divehi", _) -> Just $ Lang "dv" "" "" [] + ("greek", _) -> Just $ Lang "el" "" "" [] + ("english", _) -> Just $ Lang "en" "" "" [] + ("esperanto", _) -> Just $ Lang "eo" "" "" [] + ("spanish", _) -> Just $ Lang "es" "" "" [] + ("estonian", _) -> Just $ Lang "et" "" "" [] + ("basque", _) -> Just $ Lang "eu" "" "" [] + ("farsi", _) -> Just $ Lang "fa" "" "" [] + ("finnish", _) -> Just $ Lang "fi" "" "" [] + ("french", _) -> Just $ Lang "fr" "" "" [] + ("friulan", _) -> Just $ Lang "fur" "" "" [] + ("irish", _) -> Just $ Lang "ga" "" "" [] + ("scottish", _) -> Just $ Lang "gd" "" "" [] + ("ethiopic", _) -> Just $ Lang "gez" "" "" [] + ("galician", _) -> Just $ Lang "gl" "" "" [] + ("hebrew", _) -> Just $ Lang "he" "" "" [] + ("hindi", _) -> Just $ Lang "hi" "" "" [] + ("croatian", _) -> Just $ Lang "hr" "" "" [] + ("magyar", _) -> Just $ Lang "hu" "" "" [] + ("armenian", _) -> Just $ Lang "hy" "" "" [] + ("interlingua", _) -> Just $ Lang "ia" "" "" [] + ("indonesian", _) -> Just $ Lang "id" "" "" [] + ("icelandic", _) -> Just $ Lang "is" "" "" [] + ("italian", _) -> Just $ Lang "it" "" "" [] + ("japanese", _) -> Just $ Lang "jp" "" "" [] + ("khmer", _) -> Just $ Lang "km" "" "" [] + ("kurmanji", _) -> Just $ Lang "kmr" "" "" [] + ("kannada", _) -> Just $ Lang "kn" "" "" [] + ("korean", _) -> Just $ Lang "ko" "" "" [] + ("latin", _) -> Just $ Lang "la" "" "" [] + ("lao", _) -> Just $ Lang "lo" "" "" [] + ("lithuanian", _) -> Just $ Lang "lt" "" "" [] + ("latvian", _) -> Just $ Lang "lv" "" "" [] + ("malayalam", _) -> Just $ Lang "ml" "" "" [] + ("mongolian", _) -> Just $ Lang "mn" "" "" [] + ("marathi", _) -> Just $ Lang "mr" "" "" [] + ("dutch", _) -> Just $ Lang "nl" "" "" [] + ("nynorsk", _) -> Just $ Lang "nn" "" "" [] + ("norsk", _) -> Just $ Lang "no" "" "" [] + ("nko", _) -> Just $ Lang "nqo" "" "" [] + ("occitan", _) -> Just $ Lang "oc" "" "" [] + ("panjabi", _) -> Just $ Lang "pa" "" "" [] + ("polish", _) -> Just $ Lang "pl" "" "" [] + ("piedmontese", _) -> Just $ Lang "pms" "" "" [] + ("portuguese", _) -> Just $ Lang "pt" "" "" [] + ("romansh", _) -> Just $ Lang "rm" "" "" [] + ("romanian", _) -> Just $ Lang "ro" "" "" [] + ("russian", _) -> Just $ Lang "ru" "" "" [] + ("sanskrit", _) -> Just $ Lang "sa" "" "" [] + ("samin", _) -> Just $ Lang "se" "" "" [] + ("slovak", _) -> Just $ Lang "sk" "" "" [] + ("albanian", _) -> Just $ Lang "sq" "" "" [] + ("serbian", _) -> Just $ Lang "sr" "" "" [] + ("swedish", _) -> Just $ Lang "sv" "" "" [] + ("syriac", _) -> Just $ Lang "syr" "" "" [] + ("tamil", _) -> Just $ Lang "ta" "" "" [] + ("telugu", _) -> Just $ Lang "te" "" "" [] + ("thai", _) -> Just $ Lang "th" "" "" [] + ("turkmen", _) -> Just $ Lang "tk" "" "" [] + ("turkish", _) -> Just $ Lang "tr" "" "" [] + ("ukrainian", _) -> Just $ Lang "uk" "" "" [] + ("urdu", _) -> Just $ Lang "ur" "" "" [] + ("vietnamese", _) -> Just $ Lang "vi" "" "" [] + _ -> Nothing diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 5503c96f1..9f88a0ad4 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -76,11 +76,6 @@ module Text.Pandoc.Shared ( renderTags', -- * File handling inDirectory, - getDefaultReferenceDocx, - getDefaultReferenceODT, - readDefaultDataFile, - readDataFile, - readDataFileUTF8, openURL, collapseFilePath, filteredFilesFromArchive, @@ -116,8 +111,6 @@ import System.Directory import System.FilePath (splitDirectories, isPathSeparator) import qualified System.FilePath.Posix as Posix import Text.Pandoc.MIME (MimeType) -import Text.Pandoc.Error (PandocError(..)) -import System.FilePath ( (</>) ) import Data.Generics (Typeable, Data) import qualified Control.Monad.State.Strict as S import qualified Control.Exception as E @@ -125,7 +118,6 @@ import Control.Monad (msum, unless, MonadPlus(..)) import Text.Pandoc.Pretty (charWidth) import Text.Pandoc.Generic (bottomUp) import Text.Pandoc.Compat.Time -import Data.Time.Clock.POSIX import System.IO.Error import System.IO.Temp import Text.HTML.TagSoup (renderTagsOptions, RenderOptions(..), Tag(..), @@ -136,17 +128,12 @@ import qualified Data.ByteString.Char8 as B8 import Data.ByteString.Base64 (decodeLenient) import Data.Sequence (ViewR(..), ViewL(..), viewl, viewr) import qualified Data.Text as T -import Data.ByteString.Lazy (toChunks, fromChunks) +import Data.ByteString.Lazy (toChunks) import qualified Data.ByteString.Lazy as BL import Paths_pandoc (version) import Codec.Archive.Zip -#ifdef EMBED_DATA_FILES -import Text.Pandoc.Data (dataFiles) -#else -import Paths_pandoc (getDataFileName) -#endif import Network.HTTP.Client (httpLbs, responseBody, responseHeaders, Request(port,host,requestHeaders), HttpException) @@ -612,110 +599,6 @@ inDirectory path action = E.bracket setCurrentDirectory (const $ setCurrentDirectory path >> action) -getDefaultReferenceDocx :: Maybe FilePath -> IO Archive -getDefaultReferenceDocx datadir = do - let paths = ["[Content_Types].xml", - "_rels/.rels", - "docProps/app.xml", - "docProps/core.xml", - "word/document.xml", - "word/fontTable.xml", - "word/footnotes.xml", - "word/numbering.xml", - "word/settings.xml", - "word/webSettings.xml", - "word/styles.xml", - "word/_rels/document.xml.rels", - "word/_rels/footnotes.xml.rels", - "word/theme/theme1.xml"] - let toLazy = fromChunks . (:[]) - let pathToEntry path = do epochtime <- (floor . utcTimeToPOSIXSeconds) <$> - getCurrentTime - contents <- toLazy <$> readDataFile datadir - ("docx/" ++ path) - return $ toEntry path epochtime contents - mbArchive <- case datadir of - Nothing -> return Nothing - Just d -> do - exists <- doesFileExist (d </> "reference.docx") - if exists - then return (Just (d </> "reference.docx")) - else return Nothing - case mbArchive of - Just arch -> toArchive <$> BL.readFile arch - Nothing -> foldr addEntryToArchive emptyArchive <$> - mapM pathToEntry paths - -getDefaultReferenceODT :: Maybe FilePath -> IO Archive -getDefaultReferenceODT datadir = do - let paths = ["mimetype", - "manifest.rdf", - "styles.xml", - "content.xml", - "meta.xml", - "settings.xml", - "Configurations2/accelerator/current.xml", - "Thumbnails/thumbnail.png", - "META-INF/manifest.xml"] - let pathToEntry path = do epochtime <- floor `fmap` getPOSIXTime - contents <- (fromChunks . (:[])) `fmap` - readDataFile datadir ("odt/" ++ path) - return $ toEntry path epochtime contents - mbArchive <- case datadir of - Nothing -> return Nothing - Just d -> do - exists <- doesFileExist (d </> "reference.odt") - if exists - then return (Just (d </> "reference.odt")) - else return Nothing - case mbArchive of - Just arch -> toArchive <$> BL.readFile arch - Nothing -> foldr addEntryToArchive emptyArchive <$> - mapM pathToEntry paths - - -readDefaultDataFile :: FilePath -> IO BS.ByteString -readDefaultDataFile "reference.docx" = - (BS.concat . toChunks . fromArchive) <$> getDefaultReferenceDocx Nothing -readDefaultDataFile "reference.odt" = - (BS.concat . toChunks . fromArchive) <$> getDefaultReferenceODT Nothing -readDefaultDataFile fname = -#ifdef EMBED_DATA_FILES - case lookup (makeCanonical fname) dataFiles of - Nothing -> E.throwIO $ PandocCouldNotFindDataFileError fname - Just contents -> return contents - where makeCanonical = Posix.joinPath . transformPathParts . splitDirectories - transformPathParts = reverse . foldl go [] - go as "." = as - go (_:as) ".." = as - go as x = x : as -#else - getDataFileName fname' >>= checkExistence >>= BS.readFile - where fname' = if fname == "MANUAL.txt" then fname else "data" </> fname - -checkExistence :: FilePath -> IO FilePath -checkExistence fn = do - exists <- doesFileExist fn - if exists - then return fn - else E.throwIO $ PandocCouldNotFindDataFileError fn -#endif - --- | Read file from specified user data directory or, if not found there, from --- Cabal data directory. -readDataFile :: Maybe FilePath -> FilePath -> IO BS.ByteString -readDataFile Nothing fname = readDefaultDataFile fname -readDataFile (Just userDir) fname = do - exists <- doesFileExist (userDir </> fname) - if exists - then BS.readFile (userDir </> fname) - else readDefaultDataFile fname - --- | Same as 'readDataFile' but returns a String instead of a ByteString. -readDataFileUTF8 :: Maybe FilePath -> FilePath -> IO String -readDataFileUTF8 userDir fname = - UTF8.toString `fmap` readDataFile userDir fname - -- | Read from a URL and return raw data and maybe mime type. openURL :: String -> IO (Either HttpException (BS.ByteString, Maybe MimeType)) openURL u diff --git a/src/Text/Pandoc/Templates.hs b/src/Text/Pandoc/Templates.hs index 43b7dc37b..d5a4faafa 100644 --- a/src/Text/Pandoc/Templates.hs +++ b/src/Text/Pandoc/Templates.hs @@ -44,7 +44,7 @@ import qualified Data.Text as T import System.FilePath ((<.>), (</>)) import Text.DocTemplates (Template, TemplateTarget, applyTemplate, compileTemplate, renderTemplate, varListToJSON) -import Text.Pandoc.Class (PandocMonad(readDataFile)) +import Text.Pandoc.Class (readDataFile, PandocMonad) import Text.Pandoc.Error import qualified Text.Pandoc.UTF8 as UTF8 diff --git a/src/Text/Pandoc/Translations.hs b/src/Text/Pandoc/Translations.hs new file mode 100644 index 000000000..2185366fd --- /dev/null +++ b/src/Text/Pandoc/Translations.hs @@ -0,0 +1,94 @@ +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{- +Copyright (C) 2017 John MacFarlane <jgm@berkeley.edu> + +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.Translations + Copyright : Copyright (C) 2017 John MacFarlane + License : GNU GPL, version 2 or above + + Maintainer : John MacFarlane <jgm@berkeley.edu> + Stability : alpha + Portability : portable + +Data types for localization. + +Translations are stored in @data/translations/langname.trans@, +where langname can be the full BCP47 language specifier, or +just the language part. File format is: + +> # A comment, ignored +> Figure: Figura +> Index: Indeksi + +-} +module Text.Pandoc.Translations ( + Term(..) + , Translations(..) + , readTranslations + ) +where +import qualified Data.Map as M +import GHC.Generics (Generic) +import Text.Pandoc.Shared (trim, safeRead) + +data Term = + Preface + | References + | Abstract + | Bibliography + | Chapter + | Appendix + | Contents + | ListOfFigures + | ListOfTables + | Index + | Figure + | Table + | Part + | Page + | Proof + | See + | SeeAlso + | Cc + | To + deriving (Show, Eq, Ord, Generic, Read) + +newtype Translations = Translations (M.Map Term String) + deriving (Show, Eq, Ord, Generic, Monoid) + +readTranslations :: String -> Either String Translations +readTranslations = foldr parseLine (Right mempty) . lines + +parseLine :: String + -> Either String Translations + -> Either String Translations +parseLine _ (Left s) = Left s +parseLine ('#':_) x = x +parseLine [] x = x +parseLine t (Right (Translations tm)) = + if null rest + then Left $ "no colon in " ++ term + else + case safeRead term of + Nothing -> Left $ term ++ " is not a recognized term name" + Just term' -> Right (Translations $ (M.insert term' defn) tm) + where (trm, rest) = break (\c -> c == ':') t + defn = trim $ drop 1 rest + term = trim trm diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index 3c901cab6..6f2cb2b9e 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -37,7 +37,7 @@ import Data.Maybe (catMaybes) import Data.Text (Text) import Network.URI (unEscapeString) import Text.Pandoc.BCP47 -import Text.Pandoc.Class (PandocMonad, report) +import Text.Pandoc.Class (PandocMonad, report, toLang) import Text.Pandoc.Logging import Text.Pandoc.Definition import Text.Pandoc.ImageSize diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 166a09d4b..8b19f3740 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -51,7 +51,7 @@ import qualified Data.Text as T import Data.Time.Clock.POSIX import Skylighting import System.Random (randomR) -import Text.Pandoc.Class (PandocMonad, report) +import Text.Pandoc.Class (PandocMonad, report, toLang) import qualified Text.Pandoc.Class as P import Text.Pandoc.Compat.Time import Text.Pandoc.Definition @@ -68,7 +68,7 @@ import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Walk import Text.Pandoc.Writers.Math import Text.Pandoc.Writers.Shared (fixDisplayMath) -import Text.Pandoc.BCP47 (getLang, renderLang, toLang) +import Text.Pandoc.BCP47 (getLang, renderLang) import Text.Printf (printf) import Text.TeXMath import Text.XML.Light as XML diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 4b7bf0e9b..fcc5ad1c6 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -45,8 +45,8 @@ import Data.Maybe (catMaybes, fromMaybe, isJust) import Data.Text (Text) import qualified Data.Text as T import Network.URI (unEscapeString) -import Text.Pandoc.BCP47 (Lang(..), toLang, getLang, renderLang) -import Text.Pandoc.Class (PandocMonad, report) +import Text.Pandoc.BCP47 (Lang(..), getLang, renderLang) +import Text.Pandoc.Class (PandocMonad, report, toLang) import Text.Pandoc.Definition import Text.Pandoc.Highlighting (formatLaTeXBlock, formatLaTeXInline, highlight, styleToLaTeX, toListingsLanguage) diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index 160141822..4c74ef469 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -38,7 +38,7 @@ import Data.List (isPrefixOf) import Data.Maybe (fromMaybe) import qualified Data.Text.Lazy as TL import System.FilePath (takeDirectory, takeExtension, (<.>)) -import Text.Pandoc.Class (PandocMonad, report) +import Text.Pandoc.Class (PandocMonad, report, toLang) import qualified Text.Pandoc.Class as P import Text.Pandoc.Definition import Text.Pandoc.ImageSize @@ -51,7 +51,7 @@ import Text.Pandoc.UTF8 (fromStringLazy, fromTextLazy, toStringLazy) import Text.Pandoc.Walk import Text.Pandoc.Writers.OpenDocument (writeOpenDocument) import Text.Pandoc.Writers.Shared (fixDisplayMath) -import Text.Pandoc.BCP47 (getLang, toLang, Lang(..), renderLang) +import Text.Pandoc.BCP47 (getLang, Lang(..), renderLang) import Text.Pandoc.XML import Text.TeXMath import Text.XML.Light -- cgit v1.2.3 From 2c85c678f27f2e38e0192a418afe91853986d518 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 11 Aug 2017 22:26:23 -0700 Subject: Removed redundant import. --- src/Text/Pandoc/CSV.hs | 1 - 1 file changed, 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/CSV.hs b/src/Text/Pandoc/CSV.hs index db9226469..810c58f92 100644 --- a/src/Text/Pandoc/CSV.hs +++ b/src/Text/Pandoc/CSV.hs @@ -36,7 +36,6 @@ module Text.Pandoc.CSV ( import Text.Parsec import Text.Parsec.Text (Parser) -import Text.Parsec.Error (ParseError) import Data.Text (Text) import qualified Data.Text as T import Control.Monad (void) -- cgit v1.2.3 From 6c5952e746c8d6cbd9bb921b444b6314daacd9ac Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 11 Aug 2017 23:09:51 -0700 Subject: Add getFileName to PandocMonad. We need this for getDefaultDataFile. --- src/Text/Pandoc/Class.hs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index a3dd9ad58..ff86429b5 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -142,7 +142,7 @@ import qualified Debug.Trace #ifdef EMBED_DATA_FILES import Text.Pandoc.Data (dataFiles) #else -import Paths_pandoc (getDataFileName) +import qualified Paths_pandoc as Paths #endif -- | The PandocMonad typeclass contains all the potentially @@ -177,6 +177,8 @@ class (Functor m, Applicative m, Monad m, MonadError PandocError m) glob :: String -> m [FilePath] -- | Returns True if file exists. fileExists :: FilePath -> m Bool + -- | Returns the path of data file. + getDataFileName :: FilePath -> m FilePath -- | Return the modification time of a file. getModificationTime :: FilePath -> m UTCTime -- | Get the value of the 'CommonState' used by all instances @@ -419,7 +421,12 @@ instance PandocMonad PandocIO where glob = liftIOError IO.glob fileExists = liftIOError Directory.doesFileExist - getModificationTime fp = liftIOError IO.getModificationTime fp +#ifdef EMBED_DATA_FILES + getDataFileName = return +#else + getDataFileName = liftIOError Paths.getDataFileName +#endif + getModificationTime = liftIOError IO.getModificationTime getCommonState = PandocIO $ lift get putCommonState x = PandocIO $ lift $ put x logOutput msg = liftIO $ do @@ -817,6 +824,8 @@ instance PandocMonad PandocPure where Nothing -> return False Just _ -> return True + getDataFileName fp = return $ "data/" ++ fp + getModificationTime fp = do fps <- getsPureState stFiles case infoFileMTime <$> getFileInfo fp fps of @@ -840,6 +849,7 @@ instance PandocMonad m => PandocMonad (ParsecT s st m) where readFileStrict = lift . readFileStrict glob = lift . glob fileExists = lift . fileExists + getDataFileName = lift . getDataFileName getModificationTime = lift . getModificationTime getCommonState = lift getCommonState putCommonState = lift . putCommonState @@ -868,6 +878,7 @@ instance PandocMonad m => PandocMonad (ReaderT r m) where readFileStrict = lift . readFileStrict glob = lift . glob fileExists = lift . fileExists + getDataFileName = lift . getDataFileName getModificationTime = lift . getModificationTime getCommonState = lift getCommonState putCommonState = lift . putCommonState @@ -884,6 +895,7 @@ instance (PandocMonad m, Monoid w) => PandocMonad (WriterT w m) where readFileStrict = lift . readFileStrict glob = lift . glob fileExists = lift . fileExists + getDataFileName = lift . getDataFileName getModificationTime = lift . getModificationTime getCommonState = lift getCommonState putCommonState = lift . putCommonState @@ -900,6 +912,7 @@ instance (PandocMonad m, Monoid w) => PandocMonad (RWST r w st m) where readFileStrict = lift . readFileStrict glob = lift . glob fileExists = lift . fileExists + getDataFileName = lift . getDataFileName getModificationTime = lift . getModificationTime getCommonState = lift getCommonState putCommonState = lift . putCommonState @@ -916,6 +929,7 @@ instance PandocMonad m => PandocMonad (StateT st m) where readFileStrict = lift . readFileStrict glob = lift . glob fileExists = lift . fileExists + getDataFileName = lift . getDataFileName getModificationTime = lift . getModificationTime getCommonState = lift getCommonState putCommonState = lift . putCommonState -- cgit v1.2.3 From b3bb9a4384b84500c7c2d0f699e4493ea69bbdb0 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 11 Aug 2017 23:11:37 -0700 Subject: Avoid some warnings when compiled wo/ embed_data_files flag. --- src/Text/Pandoc/Class.hs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index ff86429b5..fa881a745 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -105,7 +105,6 @@ import Data.Time.Clock.POSIX ( utcTimeToPOSIXSeconds , posixSecondsToUTCTime , POSIXTime ) import Data.Time.LocalTime (TimeZone, ZonedTime, utcToZonedTime, utc) -import qualified System.FilePath.Posix as Posix import Network.URI ( escapeURIString, nonStrictRelativeTo, unEscapeString, parseURIReference, isAllowedInURI, parseURI, URI(..) ) @@ -119,7 +118,7 @@ import qualified System.Environment as IO (lookupEnv) import System.FilePath.Glob (match, compile) import System.Directory (createDirectoryIfMissing, getDirectoryContents, doesDirectoryExist) -import System.FilePath ((</>), (<.>), takeDirectory, splitDirectories, +import System.FilePath ((</>), (<.>), takeDirectory, takeExtension, dropExtension, isRelative, normalise) import qualified System.FilePath.Glob as IO (glob) import qualified System.Directory as IO (getModificationTime) @@ -141,6 +140,8 @@ import Text.Pandoc.Translations (Term(..), Translations(..), readTranslations) import qualified Debug.Trace #ifdef EMBED_DATA_FILES import Text.Pandoc.Data (dataFiles) +import qualified System.FilePath.Posix as Posix +import System.Directory (splitDirectories) #else import qualified Paths_pandoc as Paths #endif -- cgit v1.2.3 From 1002178752a9405a15eb3e86476c496cd2f3f69d Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 11 Aug 2017 23:24:55 -0700 Subject: More redundant imports. --- 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 fa881a745..d06c9c301 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -86,7 +86,7 @@ module Text.Pandoc.Class ( PandocMonad(..) import Prelude hiding (readFile) import System.Random (StdGen, next, mkStdGen) import qualified System.Random as IO (newStdGen) -import Codec.Archive.Zip (Archive, fromArchive, emptyArchive) +import Codec.Archive.Zip import Data.Unique (hashUnique) import qualified Data.Unique as IO (newUnique) import qualified Text.Pandoc.Shared as Shared @@ -128,7 +128,6 @@ import Control.Monad.State.Strict import Control.Monad.Except import Control.Monad.Writer (WriterT) import Control.Monad.RWS (RWST) -import Codec.Archive.Zip import Data.Word (Word8) import Data.Default import System.IO.Error -- cgit v1.2.3 From 95f7dc6573465f6feece28aa86509aca45fdeccd Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 11 Aug 2017 23:57:35 -0700 Subject: Fixed import. --- 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 d06c9c301..80ebd58b4 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -140,7 +140,7 @@ import qualified Debug.Trace #ifdef EMBED_DATA_FILES import Text.Pandoc.Data (dataFiles) import qualified System.FilePath.Posix as Posix -import System.Directory (splitDirectories) +import System.FilePath (splitDirectories) #else import qualified Paths_pandoc as Paths #endif -- cgit v1.2.3 From b6e0add76aa2479fde9696f1ab25c1101de4de31 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 12 Aug 2017 12:15:40 -0700 Subject: Set user data dir at beginning, so readDataFile has access to it. --- src/Text/Pandoc/App.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 99d9aa4cb..938bb91e0 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -298,6 +298,8 @@ convertWithOpts opts = do return $ (varname, s) : vars runIO' $ do + setUserDataDir datadir + variables <- withList (addStringAsVariable "sourcefile") (reverse $ optInputFiles opts) @@ -451,7 +453,6 @@ convertWithOpts opts = do mconcat <$> mapM (readFile' >=> r readerOpts) sources - setUserDataDir datadir when (readerName == "markdown_github" || writerName == "markdown_github") $ report $ Deprecated "markdown_github" "Use gfm instead." -- cgit v1.2.3 From 622c3f2fa6c29ecc33502f63ae6f33e59c11c96c Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 12 Aug 2017 12:17:38 -0700 Subject: Change to yaml for translation files. --- src/Text/Pandoc/Class.hs | 13 ++++----- src/Text/Pandoc/Translations.hs | 59 ++++++++++++++++++++++++++--------------- 2 files changed, 44 insertions(+), 28 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 80ebd58b4..074181c92 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -79,7 +79,7 @@ module Text.Pandoc.Class ( PandocMonad(..) , toLang , setTranslations , translateTerm - , Translations(..) + , Translations , Term(..) ) where @@ -135,7 +135,8 @@ import System.IO (stderr) import qualified Data.Map as M import Text.Pandoc.Error import Text.Pandoc.BCP47 (Lang(..), parseBCP47, renderLang) -import Text.Pandoc.Translations (Term(..), Translations(..), readTranslations) +import Text.Pandoc.Translations (Term(..), Translations, lookupTerm, + readTranslations) import qualified Debug.Trace #ifdef EMBED_DATA_FILES import Text.Pandoc.Data (dataFiles) @@ -343,8 +344,8 @@ getTranslations = do Nothing -> return mempty -- no language defined Just (_, Just t) -> return t Just (lang, Nothing) -> do -- read from file - let translationFile = "translations/" ++ renderLang lang ++ ".trans" - let fallbackFile = "translations/" ++ langLanguage lang ++ ".trans" + let translationFile = "translations/" ++ renderLang lang ++ ".yaml" + let fallbackFile = "translations/" ++ langLanguage lang ++ ".yaml" let getTrans bs = case readTranslations (UTF8.toString bs) of Left e -> do @@ -374,8 +375,8 @@ getTranslations = do -- Issue a warning if the term is not defined. translateTerm :: PandocMonad m => Term -> m String translateTerm term = do - Translations termMap <- getTranslations - case M.lookup term termMap of + translations <- getTranslations + case lookupTerm term translations of Just s -> return s Nothing -> do report $ NoTranslation (show term) diff --git a/src/Text/Pandoc/Translations.hs b/src/Text/Pandoc/Translations.hs index 2185366fd..e2091f0a8 100644 --- a/src/Text/Pandoc/Translations.hs +++ b/src/Text/Pandoc/Translations.hs @@ -40,13 +40,19 @@ just the language part. File format is: -} module Text.Pandoc.Translations ( Term(..) - , Translations(..) + , Translations + , lookupTerm , readTranslations ) where +import qualified Data.HashMap.Strict as HM import qualified Data.Map as M import GHC.Generics (Generic) -import Text.Pandoc.Shared (trim, safeRead) +import qualified Text.Pandoc.UTF8 as UTF8 +import Data.Text as T +import Text.Pandoc.Shared (safeRead) +import Data.Yaml as Yaml +import Data.Aeson.Types (typeMismatch) data Term = Preface @@ -68,27 +74,36 @@ data Term = | SeeAlso | Cc | To - deriving (Show, Eq, Ord, Generic, Read) + deriving (Show, Eq, Ord, Generic, Enum, Read) newtype Translations = Translations (M.Map Term String) - deriving (Show, Eq, Ord, Generic, Monoid) + deriving (Show, Generic, Monoid) -readTranslations :: String -> Either String Translations -readTranslations = foldr parseLine (Right mempty) . lines +instance FromJSON Term where + parseJSON (String t) = case safeRead (T.unpack t) of + Just t' -> pure t' + Nothing -> fail $ "Invalid Term name " ++ + show t + parseJSON invalid = typeMismatch "Term" invalid + +instance FromJSON Translations where + parseJSON (Object hm) = do + xs <- mapM addItem (HM.toList hm) + return $ Translations (M.fromList xs) + where addItem (k,v) = + case safeRead (T.unpack k) of + Nothing -> fail $ "Invalid Term name " ++ show k + Just t -> + case v of + (String s) -> return (t, T.unpack $ T.strip s) + inv -> typeMismatch "String" inv + parseJSON invalid = typeMismatch "Translations" invalid -parseLine :: String - -> Either String Translations - -> Either String Translations -parseLine _ (Left s) = Left s -parseLine ('#':_) x = x -parseLine [] x = x -parseLine t (Right (Translations tm)) = - if null rest - then Left $ "no colon in " ++ term - else - case safeRead term of - Nothing -> Left $ term ++ " is not a recognized term name" - Just term' -> Right (Translations $ (M.insert term' defn) tm) - where (trm, rest) = break (\c -> c == ':') t - defn = trim $ drop 1 rest - term = trim trm +lookupTerm :: Term -> Translations -> Maybe String +lookupTerm t (Translations tm) = M.lookup t tm + +readTranslations :: String -> Either String Translations +readTranslations s = + case Yaml.decodeEither' $ UTF8.fromString s of + Left err' -> Left $ prettyPrintParseException err' + Right t -> Right t -- cgit v1.2.3 From 444f8e9569bef7fc0f92923a5acc4970ce9f710f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 12 Aug 2017 12:25:33 -0700 Subject: Improved error report on loading translation file. --- src/Text/Pandoc/Class.hs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 074181c92..25d6d2927 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -346,10 +346,12 @@ getTranslations = do Just (lang, Nothing) -> do -- read from file let translationFile = "translations/" ++ renderLang lang ++ ".yaml" let fallbackFile = "translations/" ++ langLanguage lang ++ ".yaml" - let getTrans bs = + let getTrans fp = do + bs <- readDataFile fp case readTranslations (UTF8.toString bs) of Left e -> do - report $ CouldNotLoadTranslations (renderLang lang) e + report $ CouldNotLoadTranslations (renderLang lang) + (fp ++ ": " ++ e) -- make sure we don't try again... modifyCommonState $ \st -> st{ stTranslations = Nothing } @@ -358,9 +360,9 @@ getTranslations = do modifyCommonState $ \st -> st{ stTranslations = Just (lang, Just t) } return t - catchError (readDataFile translationFile >>= getTrans) + catchError (getTrans translationFile) (\_ -> - catchError (readDataFile fallbackFile >>= getTrans) + catchError (getTrans fallbackFile) (\e -> do report $ CouldNotLoadTranslations (renderLang lang) $ case e of -- cgit v1.2.3 From f035f0ffe3ef70abb6fa3ad0e39ac0f9c1f45c5e Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 12 Aug 2017 12:34:36 -0700 Subject: LaTeX reader: have `\setmainlanguage` set `lang` in metadata. --- src/Text/Pandoc/Readers/LaTeX.hs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 1fe4594ed..c382ce440 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -59,7 +59,7 @@ import Text.Pandoc.Class (PandocMonad, PandocPure, lookupEnv, readFileFromDirs, report, setResourcePath, getResourcePath, setTranslations, translateTerm) import qualified Text.Pandoc.Translations as Translations -import Text.Pandoc.BCP47 (Lang(..)) +import Text.Pandoc.BCP47 (Lang(..), renderLang) import Text.Pandoc.Highlighting (fromListingsLanguage, languagesByExtension) import Text.Pandoc.ImageSize (numUnit, showFl) import Text.Pandoc.Logging @@ -2225,9 +2225,11 @@ setDefaultLanguage = do <$> rawopt polylang <- toksToString <$> braced case polyglossiaLangToBCP47 polylang o of - Nothing -> return () -- TODO mzero? warning? - Just l -> setTranslations l - return mempty + Nothing -> return mempty -- TODO mzero? warning? + Just l -> do + setTranslations l + updateState $ setMeta "lang" $ str (renderLang l) + return mempty polyglossiaLangToBCP47 :: String -> String -> Maybe Lang polyglossiaLangToBCP47 s o = -- cgit v1.2.3 From 78e61cedd0ffd8a3183a1feae1949243534def1d Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 12 Aug 2017 13:14:27 -0700 Subject: Added Encl, Glossary to Term --- src/Text/Pandoc/Translations.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Translations.hs b/src/Text/Pandoc/Translations.hs index e2091f0a8..57fad3386 100644 --- a/src/Text/Pandoc/Translations.hs +++ b/src/Text/Pandoc/Translations.hs @@ -69,11 +69,13 @@ data Term = | Table | Part | Page - | Proof | See | SeeAlso + | Encl | Cc | To + | Proof + | Glossary deriving (Show, Eq, Ord, Generic, Enum, Read) newtype Translations = Translations (M.Map Term String) -- cgit v1.2.3 From f7346bbfc138d0b3d8171ee320fbce84685f82e0 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 12 Aug 2017 13:19:50 -0700 Subject: Added Listing to Term. So far only added to English. --- src/Text/Pandoc/Translations.hs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Translations.hs b/src/Text/Pandoc/Translations.hs index 57fad3386..d9e7e05e2 100644 --- a/src/Text/Pandoc/Translations.hs +++ b/src/Text/Pandoc/Translations.hs @@ -76,6 +76,7 @@ data Term = | To | Proof | Glossary + | Listing deriving (Show, Eq, Ord, Generic, Enum, Read) newtype Translations = Translations (M.Map Term String) -- cgit v1.2.3 From 3897df868ad1e0ca5409e8fffd86c73dbf6f2b31 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 12 Aug 2017 13:28:03 -0700 Subject: LaTeX reader: support \chaptername, \partname, \abstractname, etc. See #3559. Obsoletes #3560. --- src/Text/Pandoc/Readers/LaTeX.hs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index c382ce440..754f379f9 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1251,6 +1251,26 @@ inlineCommands = M.fromList $ mkImage options src) , ("enquote", enquote) , ("figurename", doTerm Translations.Figure) + , ("prefacename", doTerm Translations.Preface) + , ("refname", doTerm Translations.References) + , ("bibname", doTerm Translations.Bibliography) + , ("chaptername", doTerm Translations.Chapter) + , ("partname", doTerm Translations.Part) + , ("contentsname", doTerm Translations.Contents) + , ("listfigurename", doTerm Translations.ListOfFigures) + , ("listtablename", doTerm Translations.ListOfTables) + , ("indexname", doTerm Translations.Index) + , ("abstractname", doTerm Translations.Abstract) + , ("tablename", doTerm Translations.Table) + , ("enclname", doTerm Translations.Encl) + , ("ccname", doTerm Translations.Cc) + , ("headtoname", doTerm Translations.To) + , ("pagename", doTerm Translations.Page) + , ("seename", doTerm Translations.See) + , ("seealsoname", doTerm Translations.SeeAlso) + , ("proofname", doTerm Translations.Proof) + , ("glossaryname", doTerm Translations.Glossary) + , ("lstlistingname", doTerm Translations.Listing) , ("cite", citation "cite" NormalCitation False) , ("Cite", citation "Cite" NormalCitation False) , ("citep", citation "citep" NormalCitation False) -- cgit v1.2.3 From 0ab8670a0eb52b5da2bef64b2cc1917e4d5ddb54 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 12 Aug 2017 13:40:28 -0700 Subject: LaTeX reader: Fixed space after \figurename etc. --- src/Text/Pandoc/Readers/LaTeX.hs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 754f379f9..284dce2bc 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1351,10 +1351,7 @@ inlineCommands = M.fromList $ ] doTerm :: PandocMonad m => Translations.Term -> LP m Inlines -doTerm term = do - s <- (symbol '~' >> return (str "\160")) <|> return space - t <- translateTerm term - return (str t <> s) +doTerm term = str <$> translateTerm term ifstrequal :: PandocMonad m => LP m Inlines ifstrequal = do -- cgit v1.2.3 From be9957bddc1e1edac2375b6b945ab4d07f6198d2 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 12 Aug 2017 20:25:08 -0700 Subject: Escape MetaString values (as added with --metadata flag). Previously they would be transmitted to the template without any escaping. Note that `--M title='*foo*'` yields a different result from --- title: *foo* --- In the latter case, we have emphasis; in the former case, just a string with literal asterisks (which will be escaped in formats, like Markdown, that require it). Closes #3792. --- src/Text/Pandoc/Writers/Shared.hs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index 8369bc09c..c6a5fdaf8 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -42,7 +42,7 @@ module Text.Pandoc.Writers.Shared ( , gridTable ) where -import Control.Monad (liftM, zipWithM) +import Control.Monad (zipWithM) import Data.Aeson (FromJSON (..), Result (..), ToJSON (..), Value (Object), encode, fromJSON) import qualified Data.HashMap.Strict as H @@ -51,6 +51,7 @@ import qualified Data.Map as M import Data.Maybe (isJust) import qualified Data.Text as T import qualified Data.Traversable as Traversable +import qualified Text.Pandoc.Builder as Builder import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Pretty @@ -103,14 +104,15 @@ metaValueToJSON :: (Monad m, ToJSON a) -> ([Inline] -> m a) -> MetaValue -> m Value -metaValueToJSON blockWriter inlineWriter (MetaMap metamap) = liftM toJSON $ +metaValueToJSON blockWriter inlineWriter (MetaMap metamap) = toJSON <$> Traversable.mapM (metaValueToJSON blockWriter inlineWriter) metamap -metaValueToJSON blockWriter inlineWriter (MetaList xs) = liftM toJSON $ +metaValueToJSON blockWriter inlineWriter (MetaList xs) = toJSON <$> Traversable.mapM (metaValueToJSON blockWriter inlineWriter) xs metaValueToJSON _ _ (MetaBool b) = return $ toJSON b -metaValueToJSON _ _ (MetaString s) = return $ toJSON s -metaValueToJSON blockWriter _ (MetaBlocks bs) = liftM toJSON $ blockWriter bs -metaValueToJSON _ inlineWriter (MetaInlines bs) = liftM toJSON $ inlineWriter bs +metaValueToJSON _ inlineWriter (MetaString s) = toJSON <$> + inlineWriter (Builder.toList (Builder.text s)) +metaValueToJSON blockWriter _ (MetaBlocks bs) = toJSON <$> blockWriter bs +metaValueToJSON _ inlineWriter (MetaInlines is) = toJSON <$> inlineWriter is -- | Retrieve a field value from a JSON object. getField :: FromJSON a -- cgit v1.2.3 From 418bda81282c82325c5a296a3c486fdc5ab1dfe0 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 12 Aug 2017 22:42:51 -0700 Subject: Docx writer: pass through comments. We assume that comments are defined as parsed by the docx reader: I want <span class="comment-start" id="0" author="Jesse Rosenthal" date="2016-05-09T16:13:00Z">I left a comment.</span>some text to have a comment <span class="comment-end" id="0"></span>on it. We assume also that the id attributes are unique and properly matched between comment-start and comment-end. Closes #2994. --- src/Text/Pandoc/Writers/Docx.hs | 55 +++++++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 8b19f3740..51e4ffb98 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -123,6 +123,7 @@ defaultWriterEnv = WriterEnv{ envTextProperties = [] data WriterState = WriterState{ stFootnotes :: [Element] + , stComments :: [([(String,String)], [Inline])] , stSectionIds :: Set.Set String , stExternalLinks :: M.Map String String , stImages :: M.Map FilePath (String, String, Maybe MimeType, Element, B.ByteString) @@ -139,6 +140,7 @@ data WriterState = WriterState{ defaultWriterState :: WriterState defaultWriterState = WriterState{ stFootnotes = defaultFootnotes + , stComments = [] , stSectionIds = Set.empty , stExternalLinks = M.empty , stImages = M.empty @@ -305,11 +307,11 @@ writeDocx opts doc@(Pandoc meta _) = do } - ((contents, footnotes), st) <- runStateT - (runReaderT - (writeOpenXML opts{writerWrapText = WrapNone} doc') - env) - initialSt + ((contents, footnotes, comments), st) <- runStateT + (runReaderT + (writeOpenXML opts{writerWrapText = WrapNone} doc') + env) + initialSt let epochtime = floor $ utcTimeToPOSIXSeconds utctime let imgs = M.elems $ stImages st @@ -370,6 +372,8 @@ writeDocx opts doc@(Pandoc meta _) = do "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml") ,("/word/document.xml", "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml") + ,("/word/comments.xml", + "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml") ,("/word/footnotes.xml", "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml") ] ++ @@ -416,6 +420,9 @@ writeDocx opts doc@(Pandoc meta _) = do ,("http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes", "rId7", "footnotes.xml") + ,("http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments", + "rId8", + "comments.xml") ] let idMap = renumIdMap (length baserels' + 1) (headers ++ footers) @@ -461,6 +468,10 @@ writeDocx opts doc@(Pandoc meta _) = do $ renderXml $ mknode "Relationships" [("xmlns","http://schemas.openxmlformats.org/package/2006/relationships")] linkrels + -- comments + let commentsEntry = toEntry "word/comments.xml" epochtime + $ renderXml $ mknode "w:comments" stdAttributes comments + -- styles -- We only want to inject paragraph and text properties that @@ -564,6 +575,7 @@ writeDocx opts doc@(Pandoc meta _) = do let archive = foldr addEntryToArchive emptyArchive $ contentTypesEntry : relsEntry : contentEntry : relEntry : footnoteRelEntry : numEntry : styleEntry : footnotesEntry : + commentsEntry : docPropsEntry : docPropsAppEntry : themeEntry : fontTableEntry : settingsEntry : webSettingsEntry : imageEntries ++ headerFooterEntries ++ @@ -762,7 +774,7 @@ makeTOC _ = return [] -- | Convert Pandoc document to two lists of -- OpenXML elements (the main document and footnotes). -writeOpenXML :: (PandocMonad m) => WriterOptions -> Pandoc -> WS m ([Element], [Element]) +writeOpenXML :: (PandocMonad m) => WriterOptions -> Pandoc -> WS m ([Element], [Element],[Element]) writeOpenXML opts (Pandoc meta blocks) = do let tit = docTitle meta ++ case lookupMeta "subtitle" meta of Just (MetaBlocks [Plain xs]) -> LineBreak : xs @@ -791,10 +803,27 @@ writeOpenXML opts (Pandoc meta blocks) = do convertSpace xs = xs let blocks' = bottomUp convertSpace blocks doc' <- (setFirstPara >> blocksToOpenXML opts blocks') - notes' <- reverse `fmap` gets stFootnotes + notes' <- reverse <$> gets stFootnotes + comments <- reverse <$> gets stComments + let toComment (kvs, ils) = do + annotation <- inlinesToOpenXML opts ils + return $ + mknode "w:comment" [('w':':':k,v) | (k,v) <- kvs] + [ mknode "w:p" [] $ + [ mknode "w:pPr" [] + [ mknode "w:pStyle" [("w:val", "CommentText")] () ] + , mknode "w:r" [] + [ mknode "w:rPr" [] + [ mknode "w:rStyle" [("w:val", "CommentReference")] () + , mknode "w:annotationRef" [] () + ] + ] + ] ++ annotation + ] + comments' <- mapM toComment comments toc <- makeTOC opts let meta' = title ++ subtitle ++ authors ++ date ++ abstract ++ toc - return (meta' ++ doc', notes') + return (meta' ++ doc', notes', comments') -- | Convert a list of Pandoc blocks to OpenXML. blocksToOpenXML :: (PandocMonad m) => WriterOptions -> [Block] -> WS m [Element] @@ -1101,6 +1130,16 @@ inlineToOpenXML' _ (Str str) = formattedString str inlineToOpenXML' opts Space = inlineToOpenXML opts (Str " ") inlineToOpenXML' opts SoftBreak = inlineToOpenXML opts (Str " ") +inlineToOpenXML' _ (Span (ident,["comment-start"],kvs) ils) = do + modify $ \st -> st{ stComments = (("id",ident):kvs, ils) : stComments st } + return [ mknode "w:commentRangeStart" [("w:id", ident)] () ] +inlineToOpenXML' _ (Span (ident,["comment-end"],_) _) = do + return [ mknode "w:commentRangeEnd" [("w:id", ident)] () + , mknode "w:r" [] + [ mknode "w:rPr" [] + [ mknode "w:rStyle" [("w:val", "CommentReference")] () ] + , mknode "w:commentReference" [("w:id", ident)] () ] + ] inlineToOpenXML' opts (Span (ident,classes,kvs) ils) = do stylemod <- case lookup dynamicStyleKey kvs of Just sty -> do -- cgit v1.2.3 From 2dc3dbd68b557cbd8974b9daf84df3d26ab5f843 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sun, 13 Aug 2017 12:37:10 +0200 Subject: Use hslua >= 0.7, update Lua code --- src/Text/Pandoc/Lua.hs | 181 ++++++++------- src/Text/Pandoc/Lua/Compat.hs | 40 ---- src/Text/Pandoc/Lua/PandocModule.hs | 24 +- src/Text/Pandoc/Lua/SharedInstances.hs | 82 +------ src/Text/Pandoc/Lua/StackInstances.hs | 407 +++++++++++++++++---------------- src/Text/Pandoc/Lua/Util.hs | 102 ++++----- src/Text/Pandoc/Writers/Custom.hs | 237 +++++++++---------- 7 files changed, 459 insertions(+), 614 deletions(-) delete mode 100644 src/Text/Pandoc/Lua/Compat.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 22b68d5e0..c5770a18b 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -41,14 +41,16 @@ import Data.Data (toConstr, showConstr, dataTypeOf, dataTypeConstrs, Data) import Data.Map (Map) import Data.Maybe (isJust) import Data.Typeable (Typeable) -import Scripting.Lua (LuaState, StackValue (..)) +import Foreign.Lua (Lua, FromLuaStack (..), ToLuaStack (..), runLua, + peekEither, getglobal', throwLuaError) +import Foreign.Lua.Types.Lua (runLuaWith, liftLua1) +import Foreign.Lua.Api import Text.Pandoc.Definition import Text.Pandoc.Lua.PandocModule (pushPandocModule) import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Walk import qualified Data.Map as Map -import qualified Scripting.Lua as Lua newtype LuaException = LuaException String deriving (Show, Typeable) @@ -57,123 +59,120 @@ instance Exception LuaException runLuaFilter :: (MonadIO m) => Maybe FilePath -> FilePath -> [String] -> Pandoc -> m Pandoc -runLuaFilter datadir filterPath args pd = liftIO $ do - lua <- Lua.newstate - Lua.openlibs lua +runLuaFilter datadir filterPath args pd = liftIO . runLua $ do + openlibs -- store module in global "pandoc" - pushPandocModule datadir lua - Lua.setglobal lua "pandoc" - top <- Lua.gettop lua - status <- Lua.loadfile lua filterPath - if status /= 0 + pushPandocModule datadir + setglobal "pandoc" + top <- gettop + stat<- dofile filterPath + if stat /= OK then do - Just luaErrMsg <- Lua.peek lua 1 - throwIO (LuaException luaErrMsg) + luaErrMsg <- peek (-1) <* pop 1 + throwLuaError luaErrMsg else do - Lua.call lua 0 Lua.multret - newtop <- Lua.gettop lua + newtop <- gettop -- Use the implicitly defined global filter if nothing was returned - when (newtop - top < 1) $ pushGlobalFilter lua - Just luaFilters <- Lua.peek lua (-1) - Lua.push lua args - Lua.setglobal lua "PandocParameters" - doc <- runAll luaFilters pd - Lua.close lua - return doc - -pushGlobalFilter :: LuaState -> IO () -pushGlobalFilter lua = - Lua.newtable lua - *> Lua.getglobal2 lua "pandoc.global_filter" - *> Lua.call lua 0 1 - *> Lua.rawseti lua (-2) 1 - -runAll :: [LuaFilter] -> Pandoc -> IO Pandoc + when (newtop - top < 1) $ pushGlobalFilter + luaFilters <- peek (-1) + push args + setglobal "PandocParameters" + runAll luaFilters pd + +pushGlobalFilter :: Lua () +pushGlobalFilter = do + newtable + getglobal' "pandoc.global_filter" + call 0 1 + rawseti (-2) 1 + +runAll :: [LuaFilter] -> Pandoc -> Lua Pandoc runAll = foldr ((>=>) . walkMWithLuaFilter) return -walkMWithLuaFilter :: LuaFilter -> Pandoc -> IO Pandoc -walkMWithLuaFilter (LuaFilter lua fnMap) = - (if hasOneOf (constructorsFor (dataTypeOf (Str []))) - then walkM (tryFilter lua fnMap :: Inline -> IO Inline) - else return) - >=> - (if hasOneOf (constructorsFor (dataTypeOf (Para []))) - then walkM (tryFilter lua fnMap :: Block -> IO Block) - else return) - >=> - (case Map.lookup "Meta" fnMap of - Just fn -> walkM (\(Pandoc meta blocks) -> do - meta' <- runFilterFunction lua fn meta - return $ Pandoc meta' blocks) - Nothing -> return) - >=> - (case Map.lookup "Pandoc" fnMap `mplus` Map.lookup "Doc" fnMap of - Just fn -> (runFilterFunction lua fn) :: Pandoc -> IO Pandoc - Nothing -> return) - where hasOneOf = any (\k -> isJust (Map.lookup k fnMap)) - constructorsFor x = map show (dataTypeConstrs x) +walkMWithLuaFilter :: LuaFilter -> Pandoc -> Lua Pandoc +walkMWithLuaFilter (LuaFilter fnMap) = liftLua1 walkLua + where + walkLua :: LuaState -> Pandoc -> IO Pandoc + walkLua l = + (if hasOneOf (constructorsFor (dataTypeOf (Str []))) + then walkM (runLuaWith l . (tryFilter fnMap :: Inline -> Lua Inline)) + else return) + >=> + (if hasOneOf (constructorsFor (dataTypeOf (Para []))) + then walkM ((runLuaWith l . (tryFilter fnMap :: Block -> Lua Block))) + else return) + >=> + (case Map.lookup "Meta" fnMap of + Just fn -> walkM ((\(Pandoc meta blocks) -> runLuaWith l $ do + meta' <- runFilterFunction fn meta + return $ Pandoc meta' blocks)) + Nothing -> return) + >=> + (case Map.lookup "Pandoc" fnMap `mplus` Map.lookup "Doc" fnMap of + Just fn -> runLuaWith l . (runFilterFunction fn :: Pandoc -> Lua Pandoc) + Nothing -> return) + hasOneOf = any (\k -> isJust (Map.lookup k fnMap)) + constructorsFor x = map show (dataTypeConstrs x) type FunctionMap = Map String LuaFilterFunction -data LuaFilter = LuaFilter LuaState FunctionMap +data LuaFilter = LuaFilter FunctionMap newtype LuaFilterFunction = LuaFilterFunction { functionIndex :: Int } -tryFilter :: (Data a, StackValue a) => LuaState -> FunctionMap -> a -> IO a -tryFilter lua fnMap x = +-- | Try running a filter for the given element +tryFilter :: (Data a, FromLuaStack a, ToLuaStack a) => FunctionMap -> a -> Lua a +tryFilter fnMap x = let filterFnName = showConstr (toConstr x) in case Map.lookup filterFnName fnMap of Nothing -> return x - Just fn -> runFilterFunction lua fn x + Just fn -> runFilterFunction fn x -instance StackValue LuaFilter where - valuetype _ = Lua.TTABLE - push = undefined - peek lua idx = fmap (LuaFilter lua) <$> Lua.peek lua idx +instance FromLuaStack LuaFilter where + peek idx = LuaFilter <$> peek idx -- | Push a value to the stack via a lua filter function. The filter function is -- called with given element as argument and is expected to return an element. -- Alternatively, the function can return nothing or nil, in which case the -- element is left unchanged. -runFilterFunction :: StackValue a => LuaState -> LuaFilterFunction -> a -> IO a -runFilterFunction lua lf x = do - pushFilterFunction lua lf - Lua.push lua x - z <- Lua.pcall lua 1 1 0 - if (z /= 0) +runFilterFunction :: (FromLuaStack a, ToLuaStack a) + => LuaFilterFunction -> a -> Lua a +runFilterFunction lf x = do + pushFilterFunction lf + push x + z <- pcall 1 1 Nothing + if z /= OK then do - msg <- Lua.peek lua (-1) + msg <- peek (-1) let prefix = "Error while running filter function: " - throwIO . LuaException $ - case msg of - Nothing -> prefix ++ "could not read error message" - Just msg' -> prefix ++ msg' + throwLuaError $ prefix ++ msg else do - resType <- Lua.ltype lua (-1) + resType <- ltype (-1) case resType of - Lua.TNIL -> Lua.pop lua 1 *> return x - _ -> do - mbres <- Lua.peek lua (-1) + TypeNil -> pop 1 *> return x + _ -> do + mbres <- peekEither (-1) case mbres of - Nothing -> throwIO $ LuaException - ("Error while trying to get a filter's return " - ++ "value from lua stack.") - Just res -> res <$ Lua.pop lua 1 + Left err -> throwLuaError + ("Error while trying to get a filter's return " + ++ "value from lua stack.\n" ++ err) + Right res -> res <$ pop 1 -- | Push the filter function to the top of the stack. -pushFilterFunction :: Lua.LuaState -> LuaFilterFunction -> IO () -pushFilterFunction lua lf = +pushFilterFunction :: LuaFilterFunction -> Lua () +pushFilterFunction lf = -- The function is stored in a lua registry table, retrieve it from there. - Lua.rawgeti lua Lua.registryindex (functionIndex lf) - -registerFilterFunction :: LuaState -> Int -> IO LuaFilterFunction -registerFilterFunction lua idx = do - isFn <- Lua.isfunction lua idx - unless isFn . throwIO . LuaException $ "Not a function at index " ++ show idx - Lua.pushvalue lua idx - refIdx <- Lua.ref lua Lua.registryindex + rawgeti registryindex (functionIndex lf) + +registerFilterFunction :: StackIndex -> Lua LuaFilterFunction +registerFilterFunction idx = do + isFn <- isfunction idx + unless isFn . throwLuaError $ "Not a function at index " ++ show idx + pushvalue idx + refIdx <- ref registryindex return $ LuaFilterFunction refIdx -instance StackValue LuaFilterFunction where - valuetype _ = Lua.TFUNCTION +instance ToLuaStack LuaFilterFunction where push = pushFilterFunction - peek = fmap (fmap Just) . registerFilterFunction + +instance FromLuaStack LuaFilterFunction where + peek = registerFilterFunction diff --git a/src/Text/Pandoc/Lua/Compat.hs b/src/Text/Pandoc/Lua/Compat.hs deleted file mode 100644 index 3fc81a15c..000000000 --- a/src/Text/Pandoc/Lua/Compat.hs +++ /dev/null @@ -1,40 +0,0 @@ -{- -Copyright © 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> - -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 --} -{-# LANGUAGE CPP #-} -{- | - Module : Text.Pandoc.Lua.Compat - Copyright : Copyright © 2017 Albert Krewinkel - License : GNU GPL, version 2 or above - - Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> - Stability : alpha - -Compatibility helpers for hslua --} -module Text.Pandoc.Lua.Compat ( loadstring ) where - -import Scripting.Lua (LuaState) -import qualified Scripting.Lua as Lua - --- | Interpret string as lua code and load into the lua environment. -loadstring :: LuaState -> String -> String -> IO Int -#if MIN_VERSION_hslua(0,5,0) -loadstring lua script _ = Lua.loadstring lua script -#else -loadstring lua script cn = Lua.loadstring lua script cn -#endif diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index d46ed3629..c8eaf3da0 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -31,31 +31,31 @@ import Control.Monad (unless) import Data.ByteString.Char8 (unpack) import Data.Default (Default (..)) import Data.Text (pack) -import Scripting.Lua (LuaState, call, push, pushhsfunction, rawset) +import Foreign.Lua (Lua, Status (OK), liftIO, push, pushHaskellFunction) +import Foreign.Lua.Api (call, loadstring, rawset) import Text.Pandoc.Class import Text.Pandoc.Definition (Pandoc) import Text.Pandoc.Options (ReaderOptions(readerExtensions)) -import Text.Pandoc.Lua.Compat (loadstring) import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Readers (Reader (..), getReader) -- | Push the "pandoc" on the lua stack. -pushPandocModule :: Maybe FilePath -> LuaState -> IO () -pushPandocModule datadir lua = do - script <- pandocModuleScript datadir - status <- loadstring lua script "pandoc.lua" - unless (status /= 0) $ call lua 0 1 - push lua "__read" - pushhsfunction lua read_doc - rawset lua (-3) +pushPandocModule :: Maybe FilePath -> Lua () +pushPandocModule datadir = do + script <- liftIO (pandocModuleScript datadir) + status <- loadstring script + unless (status /= OK) $ call 0 1 + push "__read" + pushHaskellFunction readDoc + rawset (-3) -- | Get the string representation of the pandoc module pandocModuleScript :: Maybe FilePath -> IO String pandocModuleScript datadir = unpack <$> runIOorExplode (setUserDataDir datadir >> readDataFile "pandoc.lua") -read_doc :: String -> String -> IO (Either String Pandoc) -read_doc formatSpec content = do +readDoc :: String -> String -> Lua (Either String Pandoc) +readDoc formatSpec content = liftIO $ do case getReader formatSpec of Left s -> return $ Left s Right (reader, es) -> diff --git a/src/Text/Pandoc/Lua/SharedInstances.hs b/src/Text/Pandoc/Lua/SharedInstances.hs index a5d4ba1e9..e9e72c219 100644 --- a/src/Text/Pandoc/Lua/SharedInstances.hs +++ b/src/Text/Pandoc/Lua/SharedInstances.hs @@ -36,81 +36,9 @@ Shared StackValue instances for pandoc and generic types. -} module Text.Pandoc.Lua.SharedInstances () where -import Scripting.Lua (LTYPE (..), StackValue (..), newtable) -import Text.Pandoc.Lua.Util (addRawInt, addValue, getRawInt, keyValuePairs) +import Foreign.Lua (ToLuaStack (push)) -import qualified Data.Map as M -import qualified Text.Pandoc.UTF8 as UTF8 - -#if MIN_VERSION_base(4,8,0) -instance {-# OVERLAPS #-} StackValue [Char] where -#else -instance StackValue [Char] where -#endif - push lua cs = push lua (UTF8.fromString cs) - peek lua i = fmap UTF8.toString <$> peek lua i - valuetype _ = TSTRING - -instance (StackValue a, StackValue b) => StackValue (a, b) where - push lua (a, b) = do - newtable lua - addRawInt lua 1 a - addRawInt lua 2 b - peek lua idx = do - a <- getRawInt lua idx 1 - b <- getRawInt lua idx 2 - return $ (,) <$> a <*> b - valuetype _ = TTABLE - -instance (StackValue a, StackValue b, StackValue c) => - StackValue (a, b, c) - where - push lua (a, b, c) = do - newtable lua - addRawInt lua 1 a - addRawInt lua 2 b - addRawInt lua 3 c - peek lua idx = do - a <- getRawInt lua idx 1 - b <- getRawInt lua idx 2 - c <- getRawInt lua idx 3 - return $ (,,) <$> a <*> b <*> c - valuetype _ = TTABLE - -instance (StackValue a, StackValue b, StackValue c, - StackValue d, StackValue e) => - StackValue (a, b, c, d, e) - where - push lua (a, b, c, d, e) = do - newtable lua - addRawInt lua 1 a - addRawInt lua 2 b - addRawInt lua 3 c - addRawInt lua 4 d - addRawInt lua 5 e - peek lua idx = do - a <- getRawInt lua idx 1 - b <- getRawInt lua idx 2 - c <- getRawInt lua idx 3 - d <- getRawInt lua idx 4 - e <- getRawInt lua idx 5 - return $ (,,,,) <$> a <*> b <*> c <*> d <*> e - valuetype _ = TTABLE - -instance (Ord a, StackValue a, StackValue b) => - StackValue (M.Map a b) where - push lua m = do - newtable lua - mapM_ (uncurry $ addValue lua) $ M.toList m - peek lua idx = fmap M.fromList <$> keyValuePairs lua idx - valuetype _ = TTABLE - -instance (StackValue a, StackValue b) => StackValue (Either a b) where - push lua = \case - Left x -> push lua x - Right x -> push lua x - peek lua idx = peek lua idx >>= \case - Just left -> return . Just $ Left left - Nothing -> fmap Right <$> peek lua idx - valuetype (Left x) = valuetype x - valuetype (Right x) = valuetype x +instance (ToLuaStack a, ToLuaStack b) => ToLuaStack (Either a b) where + push = \case + Left x -> push x + Right x -> push x diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index d2e3f630a..4eea5bc2f 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -33,243 +33,244 @@ StackValue instances for pandoc types. module Text.Pandoc.Lua.StackInstances () where import Control.Applicative ((<|>)) -import Scripting.Lua (LTYPE (..), LuaState, StackValue (..), ltype, newtable, - objlen) +import Foreign.Lua (Lua, Type (..), FromLuaStack (peek), ToLuaStack (push), + StackIndex, peekEither, throwLuaError) +import Foreign.Lua.Api (getfield, ltype, newtable, pop, rawlen) import Text.Pandoc.Definition import Text.Pandoc.Lua.SharedInstances () import Text.Pandoc.Lua.Util (addValue, getTable, pushViaConstructor) import Text.Pandoc.Shared (safeRead) -instance StackValue Pandoc where - push lua (Pandoc meta blocks) = do - newtable lua - addValue lua "blocks" blocks - addValue lua "meta" meta - peek lua idx = do - blocks <- getTable lua idx "blocks" - meta <- getTable lua idx "meta" - return $ Pandoc <$> meta <*> blocks - valuetype _ = TTABLE - -instance StackValue Meta where - push lua (Meta mmap) = push lua mmap - peek lua idx = fmap Meta <$> peek lua idx - valuetype _ = TTABLE - -instance StackValue MetaValue where +instance ToLuaStack Pandoc where + push (Pandoc meta blocks) = do + newtable + addValue "blocks" blocks + addValue "meta" meta +instance FromLuaStack Pandoc where + peek idx = do + blocks <- getTable idx "blocks" + meta <- getTable idx "meta" + return $ Pandoc meta blocks + +instance ToLuaStack Meta where + push (Meta mmap) = push mmap +instance FromLuaStack Meta where + peek idx = Meta <$> peek idx + +instance ToLuaStack MetaValue where push = pushMetaValue +instance FromLuaStack MetaValue where peek = peekMetaValue - valuetype = \case - MetaBlocks _ -> TTABLE - MetaBool _ -> TBOOLEAN - MetaInlines _ -> TTABLE - MetaList _ -> TTABLE - MetaMap _ -> TTABLE - MetaString _ -> TSTRING - -instance StackValue Block where + +instance ToLuaStack Block where push = pushBlock + +instance FromLuaStack Block where peek = peekBlock - valuetype _ = TTABLE -instance StackValue Inline where +-- Inline +instance ToLuaStack Inline where push = pushInline + +instance FromLuaStack Inline where peek = peekInline - valuetype _ = TTABLE - -instance StackValue Citation where - push lua (Citation cid prefix suffix mode noteNum hash) = - pushViaConstructor lua "Citation" cid mode prefix suffix noteNum hash - peek lua idx = do - id' <- getTable lua idx "citationId" - prefix <- getTable lua idx "citationPrefix" - suffix <- getTable lua idx "citationSuffix" - mode <- getTable lua idx "citationMode" - num <- getTable lua idx "citationNoteNum" - hash <- getTable lua idx "citationHash" - return $ Citation <$> id' <*> prefix <*> suffix <*> mode <*> num <*> hash - valuetype _ = TTABLE - -instance StackValue Alignment where - push lua = push lua . show - peek lua idx = (>>= safeRead) <$> peek lua idx - valuetype _ = TSTRING - -instance StackValue CitationMode where - push lua = push lua . show - peek lua idx = (>>= safeRead) <$> peek lua idx - valuetype _ = TSTRING - -instance StackValue Format where - push lua (Format f) = push lua f - peek lua idx = fmap Format <$> peek lua idx - valuetype _ = TSTRING - -instance StackValue ListNumberDelim where - push lua = push lua . show - peek lua idx = (>>= safeRead) <$> peek lua idx - valuetype _ = TSTRING - -instance StackValue ListNumberStyle where - push lua = push lua . show - peek lua idx = (>>= safeRead) <$> peek lua idx - valuetype _ = TSTRING - -instance StackValue MathType where - push lua = push lua . show - peek lua idx = (>>= safeRead) <$> peek lua idx - valuetype _ = TSTRING - -instance StackValue QuoteType where - push lua = push lua . show - peek lua idx = (>>= safeRead) <$> peek lua idx - valuetype _ = TSTRING + +-- Citation +instance ToLuaStack Citation where + push (Citation cid prefix suffix mode noteNum hash) = + pushViaConstructor "Citation" cid mode prefix suffix noteNum hash + +instance FromLuaStack Citation where + peek idx = do + id' <- getTable idx "citationId" + prefix <- getTable idx "citationPrefix" + suffix <- getTable idx "citationSuffix" + mode <- getTable idx "citationMode" + num <- getTable idx "citationNoteNum" + hash <- getTable idx "citationHash" + return $ Citation id' prefix suffix mode num hash + +instance ToLuaStack Alignment where + push = push . show +instance FromLuaStack Alignment where + peek idx = safeRead' =<< peek idx + +instance ToLuaStack CitationMode where + push = push . show +instance FromLuaStack CitationMode where + peek idx = safeRead' =<< peek idx + +instance ToLuaStack Format where + push (Format f) = push f +instance FromLuaStack Format where + peek idx = Format <$> peek idx + +instance ToLuaStack ListNumberDelim where + push = push . show +instance FromLuaStack ListNumberDelim where + peek idx = safeRead' =<< peek idx + +instance ToLuaStack ListNumberStyle where + push = push . show +instance FromLuaStack ListNumberStyle where + peek idx = safeRead' =<< peek idx + +instance ToLuaStack MathType where + push = push . show +instance FromLuaStack MathType where + peek idx = safeRead' =<< peek idx + +instance ToLuaStack QuoteType where + push = push . show +instance FromLuaStack QuoteType where + peek idx = safeRead' =<< peek idx + +safeRead' :: Read a => String -> Lua a +safeRead' s = case safeRead s of + Nothing -> throwLuaError ("Could not read: " ++ s) + Just x -> return x -- | Push an meta value element to the top of the lua stack. -pushMetaValue :: LuaState -> MetaValue -> IO () -pushMetaValue lua = \case - MetaBlocks blcks -> pushViaConstructor lua "MetaBlocks" blcks - MetaBool bool -> push lua bool - MetaInlines inlns -> pushViaConstructor lua "MetaInlines" inlns - MetaList metalist -> pushViaConstructor lua "MetaList" metalist - MetaMap metamap -> pushViaConstructor lua "MetaMap" metamap - MetaString str -> push lua str +pushMetaValue :: MetaValue -> Lua () +pushMetaValue = \case + MetaBlocks blcks -> pushViaConstructor "MetaBlocks" blcks + MetaBool bool -> push bool + MetaInlines inlns -> pushViaConstructor "MetaInlines" inlns + MetaList metalist -> pushViaConstructor "MetaList" metalist + MetaMap metamap -> pushViaConstructor "MetaMap" metamap + MetaString str -> push str -- | Interpret the value at the given stack index as meta value. -peekMetaValue :: LuaState -> Int -> IO (Maybe MetaValue) -peekMetaValue lua idx = do +peekMetaValue :: StackIndex -> Lua MetaValue +peekMetaValue idx = do -- Get the contents of an AST element. - let elementContent :: StackValue a => IO (Maybe a) - elementContent = peek lua idx - luatype <- ltype lua idx + let elementContent :: FromLuaStack a => Lua a + elementContent = peek idx + luatype <- ltype idx case luatype of - TBOOLEAN -> fmap MetaBool <$> peek lua idx - TSTRING -> fmap MetaString <$> peek lua idx - TTABLE -> do - tag <- getTable lua idx "t" + TypeBoolean -> MetaBool <$> peek idx + TypeString -> MetaString <$> peek idx + TypeTable -> do + tag <- getfield idx "t" *> peekEither (-1) <* pop 1 case tag of - Just "MetaBlocks" -> fmap MetaBlocks <$> elementContent - Just "MetaBool" -> fmap MetaBool <$> elementContent - Just "MetaMap" -> fmap MetaMap <$> elementContent - Just "MetaInlines" -> fmap MetaInlines <$> elementContent - Just "MetaList" -> fmap MetaList <$> elementContent - Just "MetaString" -> fmap MetaString <$> elementContent - Nothing -> do + Right "MetaBlocks" -> MetaBlocks <$> elementContent + Right "MetaBool" -> MetaBool <$> elementContent + Right "MetaMap" -> MetaMap <$> elementContent + Right "MetaInlines" -> MetaInlines <$> elementContent + Right "MetaList" -> MetaList <$> elementContent + Right "MetaString" -> MetaString <$> elementContent + Right t -> throwLuaError ("Unknown meta tag: " ++ t) + Left _ -> do -- no meta value tag given, try to guess. - len <- objlen lua idx + len <- rawlen idx if len <= 0 - then fmap MetaMap <$> peek lua idx - else (fmap MetaInlines <$> peek lua idx) - <|> (fmap MetaBlocks <$> peek lua idx) - <|> (fmap MetaList <$> peek lua idx) - _ -> return Nothing - _ -> return Nothing + then MetaMap <$> peek idx + else (MetaInlines <$> peek idx) + <|> (MetaBlocks <$> peek idx) + <|> (MetaList <$> peek idx) + _ -> throwLuaError ("could not get meta value") -- | Push an block element to the top of the lua stack. -pushBlock :: LuaState -> Block -> IO () -pushBlock lua = \case - BlockQuote blcks -> pushViaConstructor lua "BlockQuote" blcks - BulletList items -> pushViaConstructor lua "BulletList" items - CodeBlock attr code -> pushViaConstructor lua "CodeBlock" code (LuaAttr attr) - DefinitionList items -> pushViaConstructor lua "DefinitionList" items - Div attr blcks -> pushViaConstructor lua "Div" blcks (LuaAttr attr) - Header lvl attr inlns -> pushViaConstructor lua "Header" lvl inlns (LuaAttr attr) - HorizontalRule -> pushViaConstructor lua "HorizontalRule" - LineBlock blcks -> pushViaConstructor lua "LineBlock" blcks - OrderedList lstAttr list -> pushViaConstructor lua "OrderedList" list lstAttr - Null -> pushViaConstructor lua "Null" - Para blcks -> pushViaConstructor lua "Para" blcks - Plain blcks -> pushViaConstructor lua "Plain" blcks - RawBlock f cs -> pushViaConstructor lua "RawBlock" f cs +pushBlock :: Block -> Lua () +pushBlock = \case + BlockQuote blcks -> pushViaConstructor "BlockQuote" blcks + BulletList items -> pushViaConstructor "BulletList" items + CodeBlock attr code -> pushViaConstructor "CodeBlock" code (LuaAttr attr) + DefinitionList items -> pushViaConstructor "DefinitionList" items + Div attr blcks -> pushViaConstructor "Div" blcks (LuaAttr attr) + Header lvl attr inlns -> pushViaConstructor "Header" lvl inlns (LuaAttr attr) + HorizontalRule -> pushViaConstructor "HorizontalRule" + LineBlock blcks -> pushViaConstructor "LineBlock" blcks + OrderedList lstAttr list -> pushViaConstructor "OrderedList" list lstAttr + Null -> pushViaConstructor "Null" + Para blcks -> pushViaConstructor "Para" blcks + Plain blcks -> pushViaConstructor "Plain" blcks + RawBlock f cs -> pushViaConstructor "RawBlock" f cs Table capt aligns widths headers rows -> - pushViaConstructor lua "Table" capt aligns widths headers rows + pushViaConstructor "Table" capt aligns widths headers rows -- | Return the value at the given index as block if possible. -peekBlock :: LuaState -> Int -> IO (Maybe Block) -peekBlock lua idx = do - tag <- getTable lua idx "t" +peekBlock :: StackIndex -> Lua Block +peekBlock idx = do + tag <- getTable idx "t" case tag of - Nothing -> return Nothing - Just t -> case t of - "BlockQuote" -> fmap BlockQuote <$> elementContent - "BulletList" -> fmap BulletList <$> elementContent - "CodeBlock" -> fmap (withAttr CodeBlock) <$> elementContent - "DefinitionList" -> fmap DefinitionList <$> elementContent - "Div" -> fmap (withAttr Div) <$> elementContent - "Header" -> fmap (\(lvl, LuaAttr attr, lst) -> Header lvl attr lst) + "BlockQuote" -> BlockQuote <$> elementContent + "BulletList" -> BulletList <$> elementContent + "CodeBlock" -> (withAttr CodeBlock) <$> elementContent + "DefinitionList" -> DefinitionList <$> elementContent + "Div" -> (withAttr Div) <$> elementContent + "Header" -> (\(lvl, LuaAttr attr, lst) -> Header lvl attr lst) <$> elementContent - "HorizontalRule" -> return (Just HorizontalRule) - "LineBlock" -> fmap LineBlock <$> elementContent - "OrderedList" -> fmap (uncurry OrderedList) <$> elementContent - "Null" -> return (Just Null) - "Para" -> fmap Para <$> elementContent - "Plain" -> fmap Plain <$> elementContent - "RawBlock" -> fmap (uncurry RawBlock) <$> elementContent - "Table" -> fmap (\(capt, aligns, widths, headers, body) -> + "HorizontalRule" -> return HorizontalRule + "LineBlock" -> LineBlock <$> elementContent + "OrderedList" -> (uncurry OrderedList) <$> elementContent + "Null" -> return Null + "Para" -> Para <$> elementContent + "Plain" -> Plain <$> elementContent + "RawBlock" -> (uncurry RawBlock) <$> elementContent + "Table" -> (\(capt, aligns, widths, headers, body) -> Table capt aligns widths headers body) <$> elementContent - _ -> return Nothing + _ -> throwLuaError ("Unknown block type: " ++ tag) where -- Get the contents of an AST element. - elementContent :: StackValue a => IO (Maybe a) - elementContent = getTable lua idx "c" + elementContent :: FromLuaStack a => Lua a + elementContent = getTable idx "c" -- | Push an inline element to the top of the lua stack. -pushInline :: LuaState -> Inline -> IO () -pushInline lua = \case - Cite citations lst -> pushViaConstructor lua "Cite" lst citations - Code attr lst -> pushViaConstructor lua "Code" lst (LuaAttr attr) - Emph inlns -> pushViaConstructor lua "Emph" inlns - Image attr alt (src,tit) -> pushViaConstructor lua "Image" alt src tit (LuaAttr attr) - LineBreak -> pushViaConstructor lua "LineBreak" - Link attr lst (src,tit) -> pushViaConstructor lua "Link" lst src tit (LuaAttr attr) - Note blcks -> pushViaConstructor lua "Note" blcks - Math mty str -> pushViaConstructor lua "Math" mty str - Quoted qt inlns -> pushViaConstructor lua "Quoted" qt inlns - RawInline f cs -> pushViaConstructor lua "RawInline" f cs - SmallCaps inlns -> pushViaConstructor lua "SmallCaps" inlns - SoftBreak -> pushViaConstructor lua "SoftBreak" - Space -> pushViaConstructor lua "Space" - Span attr inlns -> pushViaConstructor lua "Span" inlns (LuaAttr attr) - Str str -> pushViaConstructor lua "Str" str - Strikeout inlns -> pushViaConstructor lua "Strikeout" inlns - Strong inlns -> pushViaConstructor lua "Strong" inlns - Subscript inlns -> pushViaConstructor lua "Subscript" inlns - Superscript inlns -> pushViaConstructor lua "Superscript" inlns +pushInline :: Inline -> Lua () +pushInline = \case + Cite citations lst -> pushViaConstructor "Cite" lst citations + Code attr lst -> pushViaConstructor "Code" lst (LuaAttr attr) + Emph inlns -> pushViaConstructor "Emph" inlns + Image attr alt (src,tit) -> pushViaConstructor "Image" alt src tit (LuaAttr attr) + LineBreak -> pushViaConstructor "LineBreak" + Link attr lst (src,tit) -> pushViaConstructor "Link" lst src tit (LuaAttr attr) + Note blcks -> pushViaConstructor "Note" blcks + Math mty str -> pushViaConstructor "Math" mty str + Quoted qt inlns -> pushViaConstructor "Quoted" qt inlns + RawInline f cs -> pushViaConstructor "RawInline" f cs + SmallCaps inlns -> pushViaConstructor "SmallCaps" inlns + SoftBreak -> pushViaConstructor "SoftBreak" + Space -> pushViaConstructor "Space" + Span attr inlns -> pushViaConstructor "Span" inlns (LuaAttr attr) + Str str -> pushViaConstructor "Str" str + Strikeout inlns -> pushViaConstructor "Strikeout" inlns + Strong inlns -> pushViaConstructor "Strong" inlns + Subscript inlns -> pushViaConstructor "Subscript" inlns + Superscript inlns -> pushViaConstructor "Superscript" inlns -- | Return the value at the given index as inline if possible. -peekInline :: LuaState -> Int -> IO (Maybe Inline) -peekInline lua idx = do - tag <- getTable lua idx "t" +peekInline :: StackIndex -> Lua Inline +peekInline idx = do + tag <- getTable idx "t" case tag of - Nothing -> return Nothing - Just t -> case t of - "Cite" -> fmap (uncurry Cite) <$> elementContent - "Code" -> fmap (withAttr Code) <$> elementContent - "Emph" -> fmap Emph <$> elementContent - "Image" -> fmap (\(LuaAttr attr, lst, tgt) -> Image attr lst tgt) - <$> elementContent - "Link" -> fmap (\(LuaAttr attr, lst, tgt) -> Link attr lst tgt) - <$> elementContent - "LineBreak" -> return (Just LineBreak) - "Note" -> fmap Note <$> elementContent - "Math" -> fmap (uncurry Math) <$> elementContent - "Quoted" -> fmap (uncurry Quoted) <$> elementContent - "RawInline" -> fmap (uncurry RawInline) <$> elementContent - "SmallCaps" -> fmap SmallCaps <$> elementContent - "SoftBreak" -> return (Just SoftBreak) - "Space" -> return (Just Space) - "Span" -> fmap (withAttr Span) <$> elementContent - "Str" -> fmap Str <$> elementContent - "Strikeout" -> fmap Strikeout <$> elementContent - "Strong" -> fmap Strong <$> elementContent - "Subscript" -> fmap Subscript <$> elementContent - "Superscript"-> fmap Superscript <$> elementContent - _ -> return Nothing + "Cite" -> (uncurry Cite) <$> elementContent + "Code" -> (withAttr Code) <$> elementContent + "Emph" -> Emph <$> elementContent + "Image" -> (\(LuaAttr attr, lst, tgt) -> Image attr lst tgt) + <$> elementContent + "Link" -> (\(LuaAttr attr, lst, tgt) -> Link attr lst tgt) + <$> elementContent + "LineBreak" -> return LineBreak + "Note" -> Note <$> elementContent + "Math" -> (uncurry Math) <$> elementContent + "Quoted" -> (uncurry Quoted) <$> elementContent + "RawInline" -> (uncurry RawInline) <$> elementContent + "SmallCaps" -> SmallCaps <$> elementContent + "SoftBreak" -> return SoftBreak + "Space" -> return Space + "Span" -> (withAttr Span) <$> elementContent + "Str" -> Str <$> elementContent + "Strikeout" -> Strikeout <$> elementContent + "Strong" -> Strong <$> elementContent + "Subscript" -> Subscript <$> elementContent + "Superscript"-> Superscript <$> elementContent + _ -> throwLuaError ("Unknown inline type: " ++ tag) where -- Get the contents of an AST element. - elementContent :: StackValue a => IO (Maybe a) - elementContent = getTable lua idx "c" + elementContent :: FromLuaStack a => Lua a + elementContent = getTable idx "c" withAttr :: (Attr -> a -> b) -> (LuaAttr, a) -> b withAttr f (attributes, x) = f (fromLuaAttr attributes) x @@ -277,8 +278,8 @@ withAttr f (attributes, x) = f (fromLuaAttr attributes) x -- | Wrapper for Attr newtype LuaAttr = LuaAttr { fromLuaAttr :: Attr } -instance StackValue LuaAttr where - push lua (LuaAttr (id', classes, kv)) = - pushViaConstructor lua "Attr" id' classes kv - peek lua idx = fmap LuaAttr <$> peek lua idx - valuetype _ = TTABLE +instance ToLuaStack LuaAttr where + push (LuaAttr (id', classes, kv)) = + pushViaConstructor "Attr" id' classes kv +instance FromLuaStack LuaAttr where + peek idx = LuaAttr <$> peek idx diff --git a/src/Text/Pandoc/Lua/Util.hs b/src/Text/Pandoc/Lua/Util.hs index 0a704d027..9e72b652c 100644 --- a/src/Text/Pandoc/Lua/Util.hs +++ b/src/Text/Pandoc/Lua/Util.hs @@ -36,103 +36,79 @@ module Text.Pandoc.Lua.Util , getRawInt , setRawInt , addRawInt - , keyValuePairs , PushViaCall , pushViaCall , pushViaConstructor ) where -import Scripting.Lua (LuaState, StackValue (..), call, getglobal2, gettable, - next, pop, pushnil, rawgeti, rawseti, settable) +import Foreign.Lua (Lua, FromLuaStack (..), ToLuaStack (..), NumArgs, + StackIndex, getglobal') +import Foreign.Lua.Api (call, gettable, pop, rawgeti, rawseti, settable) -- | Adjust the stack index, assuming that @n@ new elements have been pushed on -- the stack. -adjustIndexBy :: Int -> Int -> Int +adjustIndexBy :: StackIndex -> StackIndex -> StackIndex adjustIndexBy idx n = if idx < 0 then idx - n else idx -- | Get value behind key from table at given index. -getTable :: (StackValue a, StackValue b) => LuaState -> Int -> a -> IO (Maybe b) -getTable lua idx key = do - push lua key - gettable lua (idx `adjustIndexBy` 1) - peek lua (-1) <* pop lua 1 +getTable :: (ToLuaStack a, FromLuaStack b) => StackIndex -> a -> Lua b +getTable idx key = do + push key + gettable (idx `adjustIndexBy` 1) + peek (-1) <* pop 1 -- | Set value for key for table at the given index -setTable :: (StackValue a, StackValue b) => LuaState -> Int -> a -> b -> IO () -setTable lua idx key value = do - push lua key - push lua value - settable lua (idx `adjustIndexBy` 2) +setTable :: (ToLuaStack a, ToLuaStack b) => StackIndex -> a -> b -> Lua () +setTable idx key value = do + push key + push value + settable (idx `adjustIndexBy` 2) -- | Add a key-value pair to the table at the top of the stack -addValue :: (StackValue a, StackValue b) => LuaState -> a -> b -> IO () -addValue lua = setTable lua (-1) +addValue :: (ToLuaStack a, ToLuaStack b) => a -> b -> Lua () +addValue = setTable (-1) -- | Get value behind key from table at given index. -getRawInt :: StackValue a => LuaState -> Int -> Int -> IO (Maybe a) -getRawInt lua idx key = - rawgeti lua idx key - *> peek lua (-1) - <* pop lua 1 +getRawInt :: FromLuaStack a => StackIndex -> Int -> Lua a +getRawInt idx key = + rawgeti idx key + *> peek (-1) + <* pop 1 -- | Set numeric key/value in table at the given index -setRawInt :: StackValue a => LuaState -> Int -> Int -> a -> IO () -setRawInt lua idx key value = do - push lua value - rawseti lua (idx `adjustIndexBy` 1) key +setRawInt :: ToLuaStack a => StackIndex -> Int -> a -> Lua () +setRawInt idx key value = do + push value + rawseti (idx `adjustIndexBy` 1) key -- | Set numeric key/value in table at the top of the stack. -addRawInt :: StackValue a => LuaState -> Int -> a -> IO () -addRawInt lua = setRawInt lua (-1) - --- | Try reading the table under the given index as a list of key-value pairs. -keyValuePairs :: (StackValue a, StackValue b) - => LuaState -> Int -> IO (Maybe [(a, b)]) -keyValuePairs lua idx = do - pushnil lua - sequence <$> remainingPairs - where - remainingPairs = do - res <- nextPair - case res of - Nothing -> return [] - Just a -> (a:) <$> remainingPairs - nextPair :: (StackValue a, StackValue b) => IO (Maybe (Maybe (a,b))) - nextPair = do - hasNext <- next lua (idx `adjustIndexBy` 1) - if hasNext - then do - val <- peek lua (-1) - key <- peek lua (-2) - pop lua 1 -- removes the value, keeps the key - return $ Just <$> ((,) <$> key <*> val) - else do - return Nothing +addRawInt :: ToLuaStack a => Int -> a -> Lua () +addRawInt = setRawInt (-1) -- | Helper class for pushing a single value to the stack via a lua function. -- See @pushViaCall@. class PushViaCall a where - pushViaCall' :: LuaState -> String -> IO () -> Int -> a + pushViaCall' :: String -> Lua () -> NumArgs -> a -instance PushViaCall (IO ()) where - pushViaCall' lua fn pushArgs num = do - getglobal2 lua fn +instance PushViaCall (Lua ()) where + pushViaCall' fn pushArgs num = do + getglobal' fn pushArgs - call lua num 1 + call num 1 -instance (StackValue a, PushViaCall b) => PushViaCall (a -> b) where - pushViaCall' lua fn pushArgs num x = - pushViaCall' lua fn (pushArgs *> push lua x) (num + 1) +instance (ToLuaStack a, PushViaCall b) => PushViaCall (a -> b) where + pushViaCall' fn pushArgs num x = + pushViaCall' fn (pushArgs *> push x) (num + 1) -- | Push an value to the stack via a lua function. The lua function is called -- with all arguments that are passed to this function and is expected to return -- a single value. -pushViaCall :: PushViaCall a => LuaState -> String -> a -pushViaCall lua fn = pushViaCall' lua fn (return ()) 0 +pushViaCall :: PushViaCall a => String -> a +pushViaCall fn = pushViaCall' fn (return ()) 0 -- | Call a pandoc element constructor within lua, passing all given arguments. -pushViaConstructor :: PushViaCall a => LuaState -> String -> a -pushViaConstructor lua pandocFn = pushViaCall lua ("pandoc." ++ pandocFn) +pushViaConstructor :: PushViaCall a => String -> a +pushViaConstructor pandocFn = pushViaCall ("pandoc." ++ pandocFn) diff --git a/src/Text/Pandoc/Writers/Custom.hs b/src/Text/Pandoc/Writers/Custom.hs index 363bad99b..485394187 100644 --- a/src/Text/Pandoc/Writers/Custom.hs +++ b/src/Text/Pandoc/Writers/Custom.hs @@ -44,10 +44,9 @@ import qualified Data.Map as M import Data.Text (Text, pack) import Data.Typeable import GHC.IO.Encoding (getForeignEncoding, setForeignEncoding, utf8) -import Scripting.Lua (LuaState, StackValue, callfunc) -import qualified Scripting.Lua as Lua +import Foreign.Lua (Lua, ToLuaStack (..), callFunc, runLua) +import Foreign.Lua.Api import Text.Pandoc.Error -import Text.Pandoc.Lua.Compat ( loadstring ) import Text.Pandoc.Lua.Util ( addValue ) import Text.Pandoc.Lua.SharedInstances () import Text.Pandoc.Definition @@ -62,55 +61,40 @@ attrToMap (id',classes,keyvals) = M.fromList : ("class", unwords classes) : keyvals -instance StackValue Format where - push lua (Format f) = Lua.push lua (map toLower f) - peek l n = fmap Format `fmap` Lua.peek l n - valuetype _ = Lua.TSTRING +instance ToLuaStack Format where + push (Format f) = push (map toLower f) #if MIN_VERSION_base(4,8,0) -instance {-# OVERLAPS #-} StackValue [Inline] where +instance {-# OVERLAPS #-} ToLuaStack [Inline] where #else -instance StackValue [Inline] where +instance ToLuaStack [Inline] where #endif - push l ils = Lua.push l =<< inlineListToCustom l ils - peek _ _ = undefined - valuetype _ = Lua.TSTRING + push ils = push =<< inlineListToCustom ils #if MIN_VERSION_base(4,8,0) -instance {-# OVERLAPS #-} StackValue [Block] where +instance {-# OVERLAPS #-} ToLuaStack [Block] where #else -instance StackValue [Block] where +instance ToLuaStack [Block] where #endif - push l ils = Lua.push l =<< blockListToCustom l ils - peek _ _ = undefined - valuetype _ = Lua.TSTRING - -instance StackValue MetaValue where - push l (MetaMap m) = Lua.push l m - push l (MetaList xs) = Lua.push l xs - push l (MetaBool x) = Lua.push l x - push l (MetaString s) = Lua.push l s - push l (MetaInlines ils) = Lua.push l ils - push l (MetaBlocks bs) = Lua.push l bs - peek _ _ = undefined - valuetype (MetaMap _) = Lua.TTABLE - valuetype (MetaList _) = Lua.TTABLE - valuetype (MetaBool _) = Lua.TBOOLEAN - valuetype (MetaString _) = Lua.TSTRING - valuetype (MetaInlines _) = Lua.TSTRING - valuetype (MetaBlocks _) = Lua.TSTRING - -instance StackValue Citation where - push lua cit = do - Lua.createtable lua 6 0 - addValue lua "citationId" $ citationId cit - addValue lua "citationPrefix" $ citationPrefix cit - addValue lua "citationSuffix" $ citationSuffix cit - addValue lua "citationMode" $ show (citationMode cit) - addValue lua "citationNoteNum" $ citationNoteNum cit - addValue lua "citationHash" $ citationHash cit - peek = undefined - valuetype _ = Lua.TTABLE + push ils = push =<< blockListToCustom ils + +instance ToLuaStack MetaValue where + push (MetaMap m) = push m + push (MetaList xs) = push xs + push (MetaBool x) = push x + push (MetaString s) = push s + push (MetaInlines ils) = push ils + push (MetaBlocks bs) = push bs + +instance ToLuaStack Citation where + push cit = do + createtable 6 0 + addValue "citationId" $ citationId cit + addValue "citationPrefix" $ citationPrefix cit + addValue "citationSuffix" $ citationSuffix cit + addValue "citationMode" $ show (citationMode cit) + addValue "citationNoteNum" $ citationNoteNum cit + addValue "citationHash" $ citationHash cit data PandocLuaException = PandocLuaException String deriving (Show, Typeable) @@ -123,23 +107,22 @@ writeCustom luaFile opts doc@(Pandoc meta _) = do luaScript <- UTF8.readFile luaFile enc <- getForeignEncoding setForeignEncoding utf8 - lua <- Lua.newstate - Lua.openlibs lua - status <- loadstring lua luaScript luaFile - -- check for error in lua script (later we'll change the return type - -- to handle this more gracefully): - when (status /= 0) $ - Lua.tostring lua 1 >>= throw . PandocLuaException . UTF8.toString - Lua.call lua 0 0 + (body, context) <- runLua $ do + openlibs + stat <- loadstring luaScript + -- check for error in lua script (later we'll change the return type + -- to handle this more gracefully): + when (stat /= OK) $ + tostring 1 >>= throw . PandocLuaException . UTF8.toString + call 0 0 -- TODO - call hierarchicalize, so we have that info - rendered <- docToCustom lua opts doc - context <- metaToJSON opts - (blockListToCustom lua) - (inlineListToCustom lua) - meta - Lua.close lua + rendered <- docToCustom opts doc + context <- metaToJSON opts + blockListToCustom + inlineListToCustom + meta + return (rendered, context) setForeignEncoding enc - let body = rendered case writerTemplate opts of Nothing -> return $ pack body Just tpl -> @@ -147,117 +130,115 @@ writeCustom luaFile opts doc@(Pandoc meta _) = do Left e -> throw (PandocTemplateError e) Right r -> return (pack r) -docToCustom :: LuaState -> WriterOptions -> Pandoc -> IO String -docToCustom lua opts (Pandoc (Meta metamap) blocks) = do - body <- blockListToCustom lua blocks - callfunc lua "Doc" body metamap (writerVariables opts) +docToCustom :: WriterOptions -> Pandoc -> Lua String +docToCustom opts (Pandoc (Meta metamap) blocks) = do + body <- blockListToCustom blocks + callFunc "Doc" body metamap (writerVariables opts) -- | Convert Pandoc block element to Custom. -blockToCustom :: LuaState -- ^ Lua state - -> Block -- ^ Block element - -> IO String +blockToCustom :: Block -- ^ Block element + -> Lua String -blockToCustom _ Null = return "" +blockToCustom Null = return "" -blockToCustom lua (Plain inlines) = callfunc lua "Plain" inlines +blockToCustom (Plain inlines) = callFunc "Plain" inlines -blockToCustom lua (Para [Image attr txt (src,tit)]) = - callfunc lua "CaptionedImage" src tit txt (attrToMap attr) +blockToCustom (Para [Image attr txt (src,tit)]) = + callFunc "CaptionedImage" src tit txt (attrToMap attr) -blockToCustom lua (Para inlines) = callfunc lua "Para" inlines +blockToCustom (Para inlines) = callFunc "Para" inlines -blockToCustom lua (LineBlock linesList) = callfunc lua "LineBlock" linesList +blockToCustom (LineBlock linesList) = callFunc "LineBlock" linesList -blockToCustom lua (RawBlock format str) = - callfunc lua "RawBlock" format str +blockToCustom (RawBlock format str) = + callFunc "RawBlock" format str -blockToCustom lua HorizontalRule = callfunc lua "HorizontalRule" +blockToCustom HorizontalRule = callFunc "HorizontalRule" -blockToCustom lua (Header level attr inlines) = - callfunc lua "Header" level inlines (attrToMap attr) +blockToCustom (Header level attr inlines) = + callFunc "Header" level inlines (attrToMap attr) -blockToCustom lua (CodeBlock attr str) = - callfunc lua "CodeBlock" str (attrToMap attr) +blockToCustom (CodeBlock attr str) = + callFunc "CodeBlock" str (attrToMap attr) -blockToCustom lua (BlockQuote blocks) = callfunc lua "BlockQuote" blocks +blockToCustom (BlockQuote blocks) = callFunc "BlockQuote" blocks -blockToCustom lua (Table capt aligns widths headers rows') = - callfunc lua "Table" capt (map show aligns) widths headers rows' +blockToCustom (Table capt aligns widths headers rows') = + callFunc "Table" capt (map show aligns) widths headers rows' -blockToCustom lua (BulletList items) = callfunc lua "BulletList" items +blockToCustom (BulletList items) = callFunc "BulletList" items -blockToCustom lua (OrderedList (num,sty,delim) items) = - callfunc lua "OrderedList" items num (show sty) (show delim) +blockToCustom (OrderedList (num,sty,delim) items) = + callFunc "OrderedList" items num (show sty) (show delim) -blockToCustom lua (DefinitionList items) = - callfunc lua "DefinitionList" items +blockToCustom (DefinitionList items) = + callFunc "DefinitionList" items -blockToCustom lua (Div attr items) = - callfunc lua "Div" items (attrToMap attr) +blockToCustom (Div attr items) = + callFunc "Div" items (attrToMap attr) -- | Convert list of Pandoc block elements to Custom. -blockListToCustom :: LuaState -- ^ Options - -> [Block] -- ^ List of block elements - -> IO String -blockListToCustom lua xs = do - blocksep <- callfunc lua "Blocksep" - bs <- mapM (blockToCustom lua) xs +blockListToCustom :: [Block] -- ^ List of block elements + -> Lua String +blockListToCustom xs = do + blocksep <- callFunc "Blocksep" + bs <- mapM blockToCustom xs return $ mconcat $ intersperse blocksep bs -- | Convert list of Pandoc inline elements to Custom. -inlineListToCustom :: LuaState -> [Inline] -> IO String -inlineListToCustom lua lst = do - xs <- mapM (inlineToCustom lua) lst - return $ concat xs +inlineListToCustom :: [Inline] -> Lua String +inlineListToCustom lst = do + xs <- mapM inlineToCustom lst + return $ mconcat xs -- | Convert Pandoc inline element to Custom. -inlineToCustom :: LuaState -> Inline -> IO String +inlineToCustom :: Inline -> Lua String -inlineToCustom lua (Str str) = callfunc lua "Str" str +inlineToCustom (Str str) = callFunc "Str" str -inlineToCustom lua Space = callfunc lua "Space" +inlineToCustom Space = callFunc "Space" -inlineToCustom lua SoftBreak = callfunc lua "SoftBreak" +inlineToCustom SoftBreak = callFunc "SoftBreak" -inlineToCustom lua (Emph lst) = callfunc lua "Emph" lst +inlineToCustom (Emph lst) = callFunc "Emph" lst -inlineToCustom lua (Strong lst) = callfunc lua "Strong" lst +inlineToCustom (Strong lst) = callFunc "Strong" lst -inlineToCustom lua (Strikeout lst) = callfunc lua "Strikeout" lst +inlineToCustom (Strikeout lst) = callFunc "Strikeout" lst -inlineToCustom lua (Superscript lst) = callfunc lua "Superscript" lst +inlineToCustom (Superscript lst) = callFunc "Superscript" lst -inlineToCustom lua (Subscript lst) = callfunc lua "Subscript" lst +inlineToCustom (Subscript lst) = callFunc "Subscript" lst -inlineToCustom lua (SmallCaps lst) = callfunc lua "SmallCaps" lst +inlineToCustom (SmallCaps lst) = callFunc "SmallCaps" lst -inlineToCustom lua (Quoted SingleQuote lst) = callfunc lua "SingleQuoted" lst +inlineToCustom (Quoted SingleQuote lst) = callFunc "SingleQuoted" lst -inlineToCustom lua (Quoted DoubleQuote lst) = callfunc lua "DoubleQuoted" lst +inlineToCustom (Quoted DoubleQuote lst) = callFunc "DoubleQuoted" lst -inlineToCustom lua (Cite cs lst) = callfunc lua "Cite" lst cs +inlineToCustom (Cite cs lst) = callFunc "Cite" lst cs -inlineToCustom lua (Code attr str) = - callfunc lua "Code" str (attrToMap attr) +inlineToCustom (Code attr str) = + callFunc "Code" str (attrToMap attr) -inlineToCustom lua (Math DisplayMath str) = - callfunc lua "DisplayMath" str +inlineToCustom (Math DisplayMath str) = + callFunc "DisplayMath" str -inlineToCustom lua (Math InlineMath str) = - callfunc lua "InlineMath" str +inlineToCustom (Math InlineMath str) = + callFunc "InlineMath" str -inlineToCustom lua (RawInline format str) = - callfunc lua "RawInline" format str +inlineToCustom (RawInline format str) = + callFunc "RawInline" format str -inlineToCustom lua (LineBreak) = callfunc lua "LineBreak" +inlineToCustom (LineBreak) = callFunc "LineBreak" -inlineToCustom lua (Link attr txt (src,tit)) = - callfunc lua "Link" txt src tit (attrToMap attr) +inlineToCustom (Link attr txt (src,tit)) = + callFunc "Link" txt src tit (attrToMap attr) -inlineToCustom lua (Image attr alt (src,tit)) = - callfunc lua "Image" alt src tit (attrToMap attr) +inlineToCustom (Image attr alt (src,tit)) = + callFunc "Image" alt src tit (attrToMap attr) -inlineToCustom lua (Note contents) = callfunc lua "Note" contents +inlineToCustom (Note contents) = callFunc "Note" contents -inlineToCustom lua (Span attr items) = - callfunc lua "Span" items (attrToMap attr) +inlineToCustom (Span attr items) = + callFunc "Span" items (attrToMap attr) -- cgit v1.2.3 From b9c7adf02ee5da08e97746e9638ddcb162ff651d Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sun, 13 Aug 2017 14:23:25 +0200 Subject: Text.Pandoc.Lua: Optimize performance by using raw table access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Raw table accessing functions never call back into haskell, which allows the compiler to use more aggressive optimizations. This improves lua filter performance considerably (⪆5% speedup). --- src/Text/Pandoc/Lua/StackInstances.hs | 27 +++++++++++++++++++++------ src/Text/Pandoc/Lua/Util.hs | 17 ++++++----------- 2 files changed, 27 insertions(+), 17 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index 4eea5bc2f..7d451a16a 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -34,11 +34,11 @@ module Text.Pandoc.Lua.StackInstances () where import Control.Applicative ((<|>)) import Foreign.Lua (Lua, Type (..), FromLuaStack (peek), ToLuaStack (push), - StackIndex, peekEither, throwLuaError) -import Foreign.Lua.Api (getfield, ltype, newtable, pop, rawlen) + StackIndex, throwLuaError, tryLua) +import Foreign.Lua.Api (getmetatable, ltype, newtable, pop, rawget, rawlen) import Text.Pandoc.Definition import Text.Pandoc.Lua.SharedInstances () -import Text.Pandoc.Lua.Util (addValue, getTable, pushViaConstructor) +import Text.Pandoc.Lua.Util (addValue, adjustIndexBy, getTable, pushViaConstructor) import Text.Pandoc.Shared (safeRead) instance ToLuaStack Pandoc where @@ -46,6 +46,7 @@ instance ToLuaStack Pandoc where newtable addValue "blocks" blocks addValue "meta" meta + instance FromLuaStack Pandoc where peek idx = do blocks <- getTable idx "blocks" @@ -151,7 +152,7 @@ peekMetaValue idx = do TypeBoolean -> MetaBool <$> peek idx TypeString -> MetaString <$> peek idx TypeTable -> do - tag <- getfield idx "t" *> peekEither (-1) <* pop 1 + tag <- tryLua $ getTag idx case tag of Right "MetaBlocks" -> MetaBlocks <$> elementContent Right "MetaBool" -> MetaBool <$> elementContent @@ -192,7 +193,7 @@ pushBlock = \case -- | Return the value at the given index as block if possible. peekBlock :: StackIndex -> Lua Block peekBlock idx = do - tag <- getTable idx "t" + tag <- getTag idx case tag of "BlockQuote" -> BlockQuote <$> elementContent "BulletList" -> BulletList <$> elementContent @@ -243,7 +244,7 @@ pushInline = \case -- | Return the value at the given index as inline if possible. peekInline :: StackIndex -> Lua Inline peekInline idx = do - tag <- getTable idx "t" + tag <- getTag idx case tag of "Cite" -> (uncurry Cite) <$> elementContent "Code" -> (withAttr Code) <$> elementContent @@ -272,6 +273,19 @@ peekInline idx = do elementContent :: FromLuaStack a => Lua a elementContent = getTable idx "c" +getTag :: StackIndex -> Lua String +getTag idx = do + hasMT <- getmetatable idx + if hasMT + then do + push "tag" + rawget (-2) + peek (-1) <* pop 2 + else do + push "tag" + rawget (idx `adjustIndexBy` 1) + peek (-1) <* pop 1 + withAttr :: (Attr -> a -> b) -> (LuaAttr, a) -> b withAttr f (attributes, x) = f (fromLuaAttr attributes) x @@ -281,5 +295,6 @@ newtype LuaAttr = LuaAttr { fromLuaAttr :: Attr } instance ToLuaStack LuaAttr where push (LuaAttr (id', classes, kv)) = pushViaConstructor "Attr" id' classes kv + instance FromLuaStack LuaAttr where peek idx = LuaAttr <$> peek idx diff --git a/src/Text/Pandoc/Lua/Util.hs b/src/Text/Pandoc/Lua/Util.hs index 9e72b652c..1b6338e64 100644 --- a/src/Text/Pandoc/Lua/Util.hs +++ b/src/Text/Pandoc/Lua/Util.hs @@ -31,7 +31,6 @@ Lua utility functions. module Text.Pandoc.Lua.Util ( adjustIndexBy , getTable - , setTable , addValue , getRawInt , setRawInt @@ -43,7 +42,7 @@ module Text.Pandoc.Lua.Util import Foreign.Lua (Lua, FromLuaStack (..), ToLuaStack (..), NumArgs, StackIndex, getglobal') -import Foreign.Lua.Api (call, gettable, pop, rawgeti, rawseti, settable) +import Foreign.Lua.Api (call, pop, rawget, rawgeti, rawset, rawseti) -- | Adjust the stack index, assuming that @n@ new elements have been pushed on -- the stack. @@ -57,19 +56,15 @@ adjustIndexBy idx n = getTable :: (ToLuaStack a, FromLuaStack b) => StackIndex -> a -> Lua b getTable idx key = do push key - gettable (idx `adjustIndexBy` 1) + rawget (idx `adjustIndexBy` 1) peek (-1) <* pop 1 --- | Set value for key for table at the given index -setTable :: (ToLuaStack a, ToLuaStack b) => StackIndex -> a -> b -> Lua () -setTable idx key value = do - push key - push value - settable (idx `adjustIndexBy` 2) - -- | Add a key-value pair to the table at the top of the stack addValue :: (ToLuaStack a, ToLuaStack b) => a -> b -> Lua () -addValue = setTable (-1) +addValue key value = do + push key + push value + rawset (-3) -- | Get value behind key from table at given index. getRawInt :: FromLuaStack a => StackIndex -> Int -> Lua a -- cgit v1.2.3 From 6e6cee454eab678b8ad3b15edcee6e07945157ba Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sun, 13 Aug 2017 14:55:33 +0200 Subject: Text.Pandoc.Lua: cleanup element walking code WalkM is general enough to work in any monad, not just IO. Also get rid of the LuaException type, sufficient to use the one defined in hslua. --- src/Text/Pandoc/Lua.hs | 48 ++++++++++++++++++++---------------------------- 1 file changed, 20 insertions(+), 28 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index c5770a18b..264364006 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -30,33 +30,25 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Pandoc lua utils. -} -module Text.Pandoc.Lua ( LuaException(..), - runLuaFilter, - pushPandocModule ) where +module Text.Pandoc.Lua (LuaException (..), pushPandocModule, runLuaFilter) where -import Control.Exception import Control.Monad (unless, when, (>=>), mplus) import Control.Monad.Trans (MonadIO (..)) import Data.Data (toConstr, showConstr, dataTypeOf, dataTypeConstrs, Data) import Data.Map (Map) import Data.Maybe (isJust) -import Data.Typeable (Typeable) -import Foreign.Lua (Lua, FromLuaStack (..), ToLuaStack (..), runLua, - peekEither, getglobal', throwLuaError) -import Foreign.Lua.Types.Lua (runLuaWith, liftLua1) -import Foreign.Lua.Api +import Foreign.Lua (Lua, FromLuaStack (peek), LuaException (..), StackIndex, + Status(OK), ToLuaStack (push), call, isnil, dofile, + getglobal', gettop, isfunction, newtable, openlibs, pcall, + peekEither, pop, pushvalue, rawgeti, rawseti, ref, + registryindex, runLua, setglobal, throwLuaError) import Text.Pandoc.Definition import Text.Pandoc.Lua.PandocModule (pushPandocModule) import Text.Pandoc.Lua.StackInstances () -import Text.Pandoc.Walk +import Text.Pandoc.Walk (Walkable (walkM)) import qualified Data.Map as Map -newtype LuaException = LuaException String - deriving (Show, Typeable) - -instance Exception LuaException - runLuaFilter :: (MonadIO m) => Maybe FilePath -> FilePath -> [String] -> Pandoc -> m Pandoc runLuaFilter datadir filterPath args pd = liftIO . runLua $ do @@ -90,26 +82,26 @@ runAll :: [LuaFilter] -> Pandoc -> Lua Pandoc runAll = foldr ((>=>) . walkMWithLuaFilter) return walkMWithLuaFilter :: LuaFilter -> Pandoc -> Lua Pandoc -walkMWithLuaFilter (LuaFilter fnMap) = liftLua1 walkLua +walkMWithLuaFilter (LuaFilter fnMap) = walkLua where - walkLua :: LuaState -> Pandoc -> IO Pandoc - walkLua l = + walkLua :: Pandoc -> Lua Pandoc + walkLua = (if hasOneOf (constructorsFor (dataTypeOf (Str []))) - then walkM (runLuaWith l . (tryFilter fnMap :: Inline -> Lua Inline)) + then walkM (tryFilter fnMap :: Inline -> Lua Inline) else return) >=> (if hasOneOf (constructorsFor (dataTypeOf (Para []))) - then walkM ((runLuaWith l . (tryFilter fnMap :: Block -> Lua Block))) + then walkM (tryFilter fnMap :: Block -> Lua Block) else return) >=> (case Map.lookup "Meta" fnMap of - Just fn -> walkM ((\(Pandoc meta blocks) -> runLuaWith l $ do - meta' <- runFilterFunction fn meta - return $ Pandoc meta' blocks)) + Just fn -> walkM (\(Pandoc meta blocks) -> do + meta' <- runFilterFunction fn meta + return $ Pandoc meta' blocks) Nothing -> return) >=> (case Map.lookup "Pandoc" fnMap `mplus` Map.lookup "Doc" fnMap of - Just fn -> runLuaWith l . (runFilterFunction fn :: Pandoc -> Lua Pandoc) + Just fn -> runFilterFunction fn :: Pandoc -> Lua Pandoc Nothing -> return) hasOneOf = any (\k -> isJust (Map.lookup k fnMap)) constructorsFor x = map show (dataTypeConstrs x) @@ -146,10 +138,10 @@ runFilterFunction lf x = do let prefix = "Error while running filter function: " throwLuaError $ prefix ++ msg else do - resType <- ltype (-1) - case resType of - TypeNil -> pop 1 *> return x - _ -> do + noExplicitFilter <- isnil (-1) + if noExplicitFilter + then pop 1 *> return x + else do mbres <- peekEither (-1) case mbres of Left err -> throwLuaError -- cgit v1.2.3 From 3d87e2080a27618e70edd1ff2d4160ff959732a6 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sun, 13 Aug 2017 17:48:43 +0200 Subject: Delete Text.Pandoc.Lua.SharedInstances Stack instances for common data types are now provides by hslua. The instance for Either was useful only for a very specific case; the function that was using the `ToLuaStack Either` instance was rewritten to work without it. Closes: #3805 --- src/Text/Pandoc/Lua.hs | 1 - src/Text/Pandoc/Lua/PandocModule.hs | 22 ++++++++--------- src/Text/Pandoc/Lua/SharedInstances.hs | 44 ---------------------------------- src/Text/Pandoc/Lua/StackInstances.hs | 1 - src/Text/Pandoc/Writers/Custom.hs | 1 - 5 files changed, 11 insertions(+), 58 deletions(-) delete mode 100644 src/Text/Pandoc/Lua/SharedInstances.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 264364006..6190a5fcf 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -44,7 +44,6 @@ import Foreign.Lua (Lua, FromLuaStack (peek), LuaException (..), StackIndex, registryindex, runLua, setglobal, throwLuaError) import Text.Pandoc.Definition import Text.Pandoc.Lua.PandocModule (pushPandocModule) -import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Walk (Walkable (walkM)) import qualified Data.Map as Map diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index c8eaf3da0..afb9aeca6 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -31,10 +31,9 @@ import Control.Monad (unless) import Data.ByteString.Char8 (unpack) import Data.Default (Default (..)) import Data.Text (pack) -import Foreign.Lua (Lua, Status (OK), liftIO, push, pushHaskellFunction) -import Foreign.Lua.Api (call, loadstring, rawset) -import Text.Pandoc.Class -import Text.Pandoc.Definition (Pandoc) +import Foreign.Lua (Lua, Status (OK), NumResults, call, loadstring, liftIO, + push, pushHaskellFunction, rawset) +import Text.Pandoc.Class (readDataFile, runIO, runIOorExplode, setUserDataDir) import Text.Pandoc.Options (ReaderOptions(readerExtensions)) import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Readers (Reader (..), getReader) @@ -54,16 +53,17 @@ pandocModuleScript :: Maybe FilePath -> IO String pandocModuleScript datadir = unpack <$> runIOorExplode (setUserDataDir datadir >> readDataFile "pandoc.lua") -readDoc :: String -> String -> Lua (Either String Pandoc) -readDoc formatSpec content = liftIO $ do +readDoc :: String -> String -> Lua NumResults +readDoc formatSpec content = do case getReader formatSpec of - Left s -> return $ Left s + Left s -> push s -- Unknown reader Right (reader, es) -> case reader of TextReader r -> do - res <- runIO $ r def{ readerExtensions = es } (pack content) + res <- liftIO $ runIO $ r def{ readerExtensions = es } (pack content) case res of - Left s -> return . Left $ show s - Right pd -> return $ Right pd - _ -> return $ Left "Only string formats are supported at the moment." + Left s -> push $ show s -- error while reading + Right pd -> push pd -- success, push Pandoc + _ -> push "Only string formats are supported at the moment." + return 1 diff --git a/src/Text/Pandoc/Lua/SharedInstances.hs b/src/Text/Pandoc/Lua/SharedInstances.hs deleted file mode 100644 index e9e72c219..000000000 --- a/src/Text/Pandoc/Lua/SharedInstances.hs +++ /dev/null @@ -1,44 +0,0 @@ -{- -Copyright © 2012-2017 John MacFarlane <jgm@berkeley.edu> - 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> - -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 --} -{-# LANGUAGE CPP #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE LambdaCase #-} -#if !MIN_VERSION_base(4,8,0) -{-# LANGUAGE OverlappingInstances #-} -#endif -{-# OPTIONS_GHC -fno-warn-orphans #-} -{- | - Module : Text.Pandoc.Lua.SharedInstances - Copyright : © 2012–2016 John MacFarlane, - © 2017 Albert Krewinkel - License : GNU GPL, version 2 or above - - Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> - Stability : alpha - -Shared StackValue instances for pandoc and generic types. --} -module Text.Pandoc.Lua.SharedInstances () where - -import Foreign.Lua (ToLuaStack (push)) - -instance (ToLuaStack a, ToLuaStack b) => ToLuaStack (Either a b) where - push = \case - Left x -> push x - Right x -> push x diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index 7d451a16a..da9c33183 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -37,7 +37,6 @@ import Foreign.Lua (Lua, Type (..), FromLuaStack (peek), ToLuaStack (push), StackIndex, throwLuaError, tryLua) import Foreign.Lua.Api (getmetatable, ltype, newtable, pop, rawget, rawlen) import Text.Pandoc.Definition -import Text.Pandoc.Lua.SharedInstances () import Text.Pandoc.Lua.Util (addValue, adjustIndexBy, getTable, pushViaConstructor) import Text.Pandoc.Shared (safeRead) diff --git a/src/Text/Pandoc/Writers/Custom.hs b/src/Text/Pandoc/Writers/Custom.hs index 485394187..63725bb60 100644 --- a/src/Text/Pandoc/Writers/Custom.hs +++ b/src/Text/Pandoc/Writers/Custom.hs @@ -48,7 +48,6 @@ import Foreign.Lua (Lua, ToLuaStack (..), callFunc, runLua) import Foreign.Lua.Api import Text.Pandoc.Error import Text.Pandoc.Lua.Util ( addValue ) -import Text.Pandoc.Lua.SharedInstances () import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Templates -- cgit v1.2.3 From 39066eba1d4068f59c150e0516c9c18d86309eed Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 13 Aug 2017 10:00:46 -0700 Subject: Added some Functor constraints needed for ghc 7.8. --- src/Text/Pandoc/Writers/Shared.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index c6a5fdaf8..8f1a06688 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -76,7 +76,7 @@ metaToJSON opts blockWriter inlineWriter meta -- | Like 'metaToJSON', but does not include variables and is -- not sensitive to 'writerTemplate'. -metaToJSON' :: (Monad m, ToJSON a) +metaToJSON' :: (Functor m, Monad m, ToJSON a) => ([Block] -> m a) -> ([Inline] -> m a) -> Meta @@ -99,7 +99,7 @@ addVariablesToJSON opts metadata = where combineMetadata (Object o1) (Object o2) = Object $ H.union o1 o2 combineMetadata x _ = x -metaValueToJSON :: (Monad m, ToJSON a) +metaValueToJSON :: (Functor m, Monad m, ToJSON a) => ([Block] -> m a) -> ([Inline] -> m a) -> MetaValue -- cgit v1.2.3 From 506866ef7368ed1cae9236bfd8323fde64eeb154 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 13 Aug 2017 09:52:37 -0700 Subject: Markdown writer: Use pipe tables if `raw_html` disabled... and `pipe_tables` enabled, even if the table has relative width information. Closes #3734. --- src/Text/Pandoc/Writers/Markdown.hs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 837c177f1..95977ce17 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -553,8 +553,13 @@ blockToMarkdown' opts t@(Table caption aligns widths headers rows) = do else blankline $$ (": " <> caption') $$ blankline let isLineBreak LineBreak = Any True isLineBreak _ = Any False - let isSimple = all (==0) widths && - not ( getAny (query isLineBreak (headers:rows)) ) + let hasLineBreak = getAny . query isLineBreak + let isSimpleCell [Plain ils] = not (hasLineBreak ils) + isSimpleCell [Para ils ] = not (hasLineBreak ils) + isSimpleCell [] = True + isSimpleCell _ = False + let hasSimpleCells = all isSimpleCell (concat (headers:rows)) + let isSimple = hasSimpleCells && all (==0) widths let isPlainBlock (Plain _) = True isPlainBlock _ = False let hasBlocks = not (all isPlainBlock $ concat . concat $ headers:rows) @@ -589,6 +594,9 @@ blockToMarkdown' opts t@(Table caption aligns widths headers rows) = do | isEnabled Ext_raw_html opts -> fmap (id,) $ (text . T.unpack) <$> (writeHtml5String def $ Pandoc nullMeta [t]) + | hasSimpleCells && + isEnabled Ext_pipe_tables opts -> fmap (id,) $ + pipeTable (all null headers) aligns' rawHeaders rawRows | otherwise -> return $ (id, text "[TABLE]") return $ nst $ tbl $$ caption'' $$ blankline blockToMarkdown' opts (BulletList items) = do -- cgit v1.2.3 From 8f65590ce9fc84452b22a3914190a8d9bc4ceda2 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 13 Aug 2017 10:43:43 -0700 Subject: CommonMark writer: prefer pipe tables to HTML tables... ...even if it means losing relative column width information. See #3734. --- src/Text/Pandoc/Writers/CommonMark.hs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/CommonMark.hs b/src/Text/Pandoc/Writers/CommonMark.hs index 77562ed8a..446578f42 100644 --- a/src/Text/Pandoc/Writers/CommonMark.hs +++ b/src/Text/Pandoc/Writers/CommonMark.hs @@ -148,7 +148,7 @@ blockToNodes opts (DefinitionList items) ns = Plain (term ++ [LineBreak] ++ xs) : ys ++ concat zs dlToBullet (term, xs) = Para term : concat xs -blockToNodes opts t@(Table capt aligns widths headers rows) ns = do +blockToNodes opts t@(Table capt aligns _widths headers rows) ns = do let allcells = concat (headers:rows) let isLineBreak LineBreak = Any True isLineBreak _ = Any False @@ -156,8 +156,7 @@ blockToNodes opts t@(Table capt aligns widths headers rows) ns = do isPlainOrPara [Plain _] = True isPlainOrPara [] = True isPlainOrPara _ = False - let isSimple = all (==0) widths && - all isPlainOrPara allcells && + let isSimple = all isPlainOrPara allcells && not ( getAny (query isLineBreak allcells) ) if isEnabled Ext_pipe_tables opts && isSimple then do -- cgit v1.2.3 From 2845ab59769709cbc250aa4ac116efbdcdf3412b Mon Sep 17 00:00:00 2001 From: schrieveslaach <schrieveslaach@online.de> Date: Sun, 13 Aug 2017 19:58:45 +0200 Subject: =?UTF-8?q?Put=20content=20of=20\ref,=20\label=20commands=20into?= =?UTF-8?q?=20span=E2=80=A6=20(#3639)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Put content of `\ref` and `\label` commands into Span elements so they can be used in filters. * Add support for `\eqref` --- src/Text/Pandoc/Readers/LaTeX.hs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 284dce2bc..498e97b8c 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1165,11 +1165,13 @@ inlineCommands = M.fromList $ , ("dots", lit "…") , ("mdots", lit "…") , ("sim", lit "~") - , ("label", rawInlineOr "label" (inBrackets <$> tok)) - , ("ref", rawInlineOr "ref" (inBrackets <$> tok)) , ("textgreek", tok) , ("sep", lit ",") - , ("cref", rawInlineOr "cref" (inBrackets <$> tok)) -- from cleveref.sty + , ("label", rawInlineOr "label" dolabel) + , ("ref", rawInlineOr "ref" $ doref "ref") + , ("cref", rawInlineOr "cref" $ doref "ref") -- from cleveref.sty + , ("vref", rawInlineOr "vref" $ doref "ref+page") -- from varioref.sty + , ("eqref", rawInlineOr "eqref" $ doref "eqref") -- from amsmath.sty , ("(", mathInline . toksToString <$> manyTill anyTok (controlSeq ")")) , ("[", mathDisplay . toksToString <$> manyTill anyTok (controlSeq "]")) , ("ensuremath", mathInline . toksToString <$> braced) @@ -1443,6 +1445,18 @@ treatAsInline = Set.fromList , "pagebreak" ] +dolabel :: PandocMonad m => LP m Inlines +dolabel = do + v <- braced + return $ spanWith ("",[],[("label", toksToString v)]) + $ inBrackets $ str $ toksToString v + +doref :: PandocMonad m => String -> LP m Inlines +doref cls = do + v <- braced + return $ spanWith ("",[],[("reference-type", cls), ("reference", toksToString v)]) + $ inBrackets $ str $ toksToString v + lookupListDefault :: (Show k, Ord k) => v -> [k] -> M.Map k v -> v lookupListDefault d = (fromMaybe d .) . lookupList where lookupList l m = msum $ map (`M.lookup` m) l -- cgit v1.2.3 From 253a7c620136bcba1a0134898b6a8cf3dcf47eca Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 13 Aug 2017 11:30:17 -0700 Subject: LaTeX reader: track header numbers and correlate with labels. --- src/Text/Pandoc/Readers/LaTeX.hs | 71 +++++++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 22 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 498e97b8c..ffc44ded3 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -119,6 +119,19 @@ parseLaTeX = do -- Left e -> error (show e) -- Right r -> return r +newtype HeaderNum = HeaderNum [Int] + deriving (Show) + +renderHeaderNum :: HeaderNum -> String +renderHeaderNum (HeaderNum xs) = + intercalate "." (map show xs) + +incrementHeaderNum :: Int -> HeaderNum -> HeaderNum +incrementHeaderNum level (HeaderNum ns) = HeaderNum $ + case reverse (take level (ns ++ repeat 0)) of + (x:xs) -> reverse (x+1 : xs) + [] -> [] -- shouldn't happen + data LaTeXState = LaTeXState{ sOptions :: ReaderOptions , sMeta :: Meta , sQuoteContext :: QuoteContext @@ -131,6 +144,8 @@ data LaTeXState = LaTeXState{ sOptions :: ReaderOptions , sCaption :: Maybe Inlines , sInListItem :: Bool , sInTableCell :: Bool + , sLastHeaderNum :: HeaderNum + , sLabels :: M.Map String Inlines } deriving Show @@ -147,6 +162,8 @@ defaultLaTeXState = LaTeXState{ sOptions = def , sCaption = Nothing , sInListItem = False , sInTableCell = False + , sLastHeaderNum = HeaderNum [] + , sLabels = M.empty } instance PandocMonad m => HasQuoteContext LaTeXState m where @@ -1448,14 +1465,16 @@ treatAsInline = Set.fromList dolabel :: PandocMonad m => LP m Inlines dolabel = do v <- braced - return $ spanWith ("",[],[("label", toksToString v)]) + return $ spanWith ("",[],[("label", toksToString v)]) $ inBrackets $ str $ toksToString v doref :: PandocMonad m => String -> LP m Inlines doref cls = do v <- braced - return $ spanWith ("",[],[("reference-type", cls), ("reference", toksToString v)]) - $ inBrackets $ str $ toksToString v + let refstr = toksToString v + return $ spanWith ("",[],[ ("reference-type", cls) + , ("reference", refstr)]) + $ inBrackets $ str refstr lookupListDefault :: (Show k, Ord k) => v -> [k] -> M.Map k v -> v lookupListDefault d = (fromMaybe d .) . lookupList @@ -1688,14 +1707,22 @@ looseItem = do resetCaption :: PandocMonad m => LP m () resetCaption = updateState $ \st -> st{ sCaption = Nothing } -section :: PandocMonad m => Attr -> Int -> LP m Blocks -section (ident, classes, kvs) lvl = do +section :: PandocMonad m => Bool -> Attr -> Int -> LP m Blocks +section starred (ident, classes, kvs) lvl = do skipopts contents <- grouped inline lab <- option ident $ try (spaces >> controlSeq "label" >> spaces >> toksToString <$> braced) - attr' <- registerHeader (lab, classes, kvs) contents + let classes' = if starred then "unnumbered" : classes else classes + unless starred $ do + hn <- sLastHeaderNum <$> getState + let num = incrementHeaderNum lvl hn + updateState $ \st -> st{ sLastHeaderNum = num } + updateState $ \st -> st{ sLabels = M.insert lab + (str (renderHeaderNum num)) + (sLabels st) } + attr' <- registerHeader (lab, classes', kvs) contents return $ headerWith attr' lvl contents blockCommand :: PandocMonad m => LP m Blocks @@ -1756,23 +1783,23 @@ blockCommands = M.fromList $ -- Koma-script metadata commands , ("dedication", mempty <$ (skipopts *> tok >>= addMeta "dedication")) -- sectioning - , ("part", section nullAttr (-1)) - , ("part*", section nullAttr (-1)) - , ("chapter", section nullAttr 0) - , ("chapter*", section ("",["unnumbered"],[]) 0) - , ("section", section nullAttr 1) - , ("section*", section ("",["unnumbered"],[]) 1) - , ("subsection", section nullAttr 2) - , ("subsection*", section ("",["unnumbered"],[]) 2) - , ("subsubsection", section nullAttr 3) - , ("subsubsection*", section ("",["unnumbered"],[]) 3) - , ("paragraph", section nullAttr 4) - , ("paragraph*", section ("",["unnumbered"],[]) 4) - , ("subparagraph", section nullAttr 5) - , ("subparagraph*", section ("",["unnumbered"],[]) 5) + , ("part", section False nullAttr (-1)) + , ("part*", section True nullAttr (-1)) + , ("chapter", section False nullAttr 0) + , ("chapter*", section True ("",["unnumbered"],[]) 0) + , ("section", section False nullAttr 1) + , ("section*", section True ("",["unnumbered"],[]) 1) + , ("subsection", section False nullAttr 2) + , ("subsection*", section True ("",["unnumbered"],[]) 2) + , ("subsubsection", section False nullAttr 3) + , ("subsubsection*", section True ("",["unnumbered"],[]) 3) + , ("paragraph", section False nullAttr 4) + , ("paragraph*", section True ("",["unnumbered"],[]) 4) + , ("subparagraph", section False nullAttr 5) + , ("subparagraph*", section True ("",["unnumbered"],[]) 5) -- beamer slides - , ("frametitle", section nullAttr 3) - , ("framesubtitle", section nullAttr 4) + , ("frametitle", section False nullAttr 3) + , ("framesubtitle", section False nullAttr 4) -- letters , ("opening", (para . trimInlines) <$> (skipopts *> tok)) , ("closing", skipopts *> closing) -- cgit v1.2.3 From f9656ece4ea1e106296bb3e140c46874df09955a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 13 Aug 2017 11:48:44 -0700 Subject: Resolve references to section numbers in LaTeX reader. --- src/Text/Pandoc/Readers/LaTeX.hs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index ffc44ded3..96e5adbc6 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -106,9 +106,23 @@ parseLaTeX = do -- handle the case where you have \part or \chapter (if bottomLevel < 1 then walk (adjustHeaders (1 - bottomLevel)) - else id) doc' + else id) $ + walk (resolveRefs (sLabels st)) $ doc' return $ Pandoc meta bs' +resolveRefs :: M.Map String [Inline] -> Inline -> Inline +resolveRefs labels x@(Span (ident,classes,kvs) _) = + case (lookup "reference-type" kvs, + lookup "reference" kvs) of + (Just "ref", Just lab) -> + case M.lookup lab labels of + Just txt -> Span (ident,classes,kvs) + [Link nullAttr txt ('#':lab, "")] + Nothing -> x + _ -> x +resolveRefs _ x = x + + -- testParser :: LP PandocIO a -> Text -> IO a -- testParser p t = do -- res <- runIOorExplode (runParserT p defaultLaTeXState{ @@ -145,7 +159,7 @@ data LaTeXState = LaTeXState{ sOptions :: ReaderOptions , sInListItem :: Bool , sInTableCell :: Bool , sLastHeaderNum :: HeaderNum - , sLabels :: M.Map String Inlines + , sLabels :: M.Map String [Inline] } deriving Show @@ -1720,7 +1734,7 @@ section starred (ident, classes, kvs) lvl = do let num = incrementHeaderNum lvl hn updateState $ \st -> st{ sLastHeaderNum = num } updateState $ \st -> st{ sLabels = M.insert lab - (str (renderHeaderNum num)) + [Str (renderHeaderNum num)] (sLabels st) } attr' <- registerHeader (lab, classes', kvs) contents return $ headerWith attr' lvl contents -- cgit v1.2.3 From bf9ec6dfd8e832a4d2d550581da482833dfaaa6b Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 13 Aug 2017 12:16:51 -0700 Subject: LaTeX reader: fix `\let\a=0` case, with single character token. --- src/Text/Pandoc/Readers/LaTeX.hs | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 96e5adbc6..4852d02c7 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1075,18 +1075,23 @@ inlineCommand' = try $ do lookupListDefault raw names inlineCommands tok :: PandocMonad m => LP m Inlines -tok = grouped inline <|> inlineCommand' <|> singleChar - where singleChar = try $ do - Tok (lin,col) toktype t <- satisfyTok (tokTypeIn [Word, Symbol]) - guard $ not $ toktype == Symbol && - T.any (`Set.member` specialChars) t - if T.length t > 1 - then do - let (t1, t2) = (T.take 1 t, T.drop 1 t) - inp <- getInput - setInput $ (Tok (lin, col + 1) toktype t2) : inp - return $ str (T.unpack t1) - else return $ str (T.unpack t) +tok = grouped inline <|> inlineCommand' <|> singleChar' + where singleChar' = do + Tok _ _ t <- singleChar + return (str (T.unpack t)) + +singleChar :: PandocMonad m => LP m Tok +singleChar = try $ do + Tok (lin,col) toktype t <- satisfyTok (tokTypeIn [Word, Symbol]) + guard $ not $ toktype == Symbol && + T.any (`Set.member` specialChars) t + if T.length t > 1 + then do + let (t1, t2) = (T.take 1 t, T.drop 1 t) + inp <- getInput + setInput $ (Tok (lin, col + 1) toktype t2) : inp + return $ Tok (lin,col) toktype t1 + else return $ Tok (lin,col) toktype t opt :: PandocMonad m => LP m Inlines opt = bracketed inline @@ -1616,7 +1621,7 @@ letmacro = do Tok _ (CtrlSeq name) _ <- anyControlSeq optional $ symbol '=' spaces - contents <- braced <|> ((:[]) <$> anyControlSeq) + contents <- braced <|> ((:[]) <$> (anyControlSeq <|> singleChar)) return (name, Macro ExpandWhenDefined 0 Nothing contents) defmacro :: PandocMonad m => LP m (Text, Macro) -- cgit v1.2.3 From 425b731050f48739749cd99c29c15aa011dbcabb Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 13 Aug 2017 12:24:06 -0700 Subject: LaTeX reader: Allow @ as a letter in control sequences. @ is commonly used in macros using `\makeatletter`. Ideally we'd make the tokenizer sensitive to `\makeatletter` and `\makeatother`, but until then this seems a good change. --- src/Text/Pandoc/Readers/LaTeX.hs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 4852d02c7..0edfc498d 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -320,8 +320,12 @@ totoks (lin,col) t = case T.uncons rest of Nothing -> [Tok (lin, col) Symbol (T.singleton c)] Just (d, rest') - | isLetter d -> - let (ws, rest'') = T.span isLetter rest + | isLetterOrAt d -> + -- \makeatletter is common in macro defs; + -- ideally we should make tokenization sensitive + -- to \makeatletter and \makeatother, but this is + -- probably best for now + let (ws, rest'') = T.span isLetterOrAt rest (ss, rest''') = T.span isSpaceOrTab rest'' in Tok (lin, col) (CtrlSeq ws) ("\\" <> ws <> ss) : totoks (lin, @@ -367,6 +371,8 @@ totoks (lin,col) t = where isSpaceOrTab ' ' = True isSpaceOrTab '\t' = True isSpaceOrTab _ = False + isLetterOrAt '@' = True + isLetterOrAt c = isLetter c isLowerHex :: Char -> Bool isLowerHex x = x >= '0' && x <= '9' || x >= 'a' && x <= 'f' -- cgit v1.2.3 From 6aef1bd228e7cb3369056bf47151787cc58c11d3 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 13 Aug 2017 12:45:04 -0700 Subject: Better handle complex \def macros as raw latex. --- src/Text/Pandoc/Readers/LaTeX.hs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 0edfc498d..986547f42 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1075,7 +1075,7 @@ inlineCommand' = try $ do let names = ordNub [name', name] -- check non-starred as fallback let raw = do guard $ isInlineCommand name || not (isBlockCommand name) - rawcommand <- getRawCommand (cmd <> star) + rawcommand <- getRawCommand name (cmd <> star) (guardEnabled Ext_raw_tex >> return (rawInline "latex" rawcommand)) <|> ignore rawcommand lookupListDefault raw names inlineCommands @@ -1421,20 +1421,22 @@ rawInlineOr :: PandocMonad m => Text -> LP m Inlines -> LP m Inlines rawInlineOr name' fallback = do parseRaw <- extensionEnabled Ext_raw_tex <$> getOption readerExtensions if parseRaw - then rawInline "latex" <$> getRawCommand name' + then rawInline "latex" <$> getRawCommand name' ("\\" <> name') else fallback -getRawCommand :: PandocMonad m => Text -> LP m String -getRawCommand txt = do +getRawCommand :: PandocMonad m => Text -> Text -> LP m String +getRawCommand name txt = do (_, rawargs) <- withRaw $ - case txt of - "\\write" -> do + case name of + "write" -> do void $ satisfyTok isWordTok -- digits void braced - "\\titleformat" -> do + "titleformat" -> do void braced skipopts void $ count 4 braced + "def" -> do + void $ manyTill anyTok braced _ -> do skipangles skipopts @@ -1759,7 +1761,7 @@ blockCommand = try $ do let names = ordNub [name', name] let rawDefiniteBlock = do guard $ isBlockCommand name - rawBlock "latex" <$> getRawCommand (txt <> star) + rawBlock "latex" <$> getRawCommand name (txt <> star) -- heuristic: if it could be either block or inline, we -- treat it if block if we have a sequence of block -- commands followed by a newline. But we stop if we @@ -1771,7 +1773,7 @@ blockCommand = try $ do guard $ "start" `T.isPrefixOf` n let rawMaybeBlock = try $ do guard $ not $ isInlineCommand name - curr <- rawBlock "latex" <$> getRawCommand (txt <> star) + curr <- rawBlock "latex" <$> getRawCommand name (txt <> star) rest <- many $ notFollowedBy startCommand *> blockCommand lookAhead $ blankline <|> startCommand return $ curr <> mconcat rest -- cgit v1.2.3 From 892a4edeb1c9b9810c8386e639d8e457bbae7e86 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 14 Aug 2017 23:05:09 -0700 Subject: Implement multicolumn support for slide formats. The structure expected is: <div class="columns"> <div class="column" width="40%"> contents... </div> <div class="column" width="60%"> contents... </div> </div> Support has been added for beamer and all HTML slide formats. Closes #1710. Note: later we could add a more elegant way to create this structure in Markdown than to use raw HTML div elements. This would come for free with a "native div syntax" (#168). Or we could devise something specific to slides --- src/Text/Pandoc/Writers/HTML.hs | 8 +++++++- src/Text/Pandoc/Writers/LaTeX.hs | 23 +++++++++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index b899ce96a..c73af4604 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -640,8 +640,14 @@ blockToHtml opts (LineBlock lns) = let lf = preEscapedString "\n" htmlLines <- mconcat . intersperse lf <$> mapM (inlineListToHtml opts) lns return $ H.div ! A.class_ "line-block" $ htmlLines -blockToHtml opts (Div attr@(ident, classes, kvs) bs) = do +blockToHtml opts (Div attr@(ident, classes, kvs') bs) = do html5 <- gets stHtml5 + let kvs = kvs' ++ + if "column" `elem` classes + then let w = fromMaybe "48%" (lookup "width" kvs') + in [("style", "width:" ++ w ++ ";min-width:" ++ w ++ + ";vertical-align:top;")] + else [] 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 diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index fcc5ad1c6..4a81cd245 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -452,7 +452,25 @@ blockToLaTeX (Div (identifier,classes,kvs) bs) = do _ -> linkAnchor' let align dir txt = inCmd "begin" dir $$ txt $$ inCmd "end" dir lang <- toLang $ lookup "lang" kvs - let wrapDir = case lookup "dir" kvs of + let wrapColumns = if "columns" `elem` classes + then \contents -> + inCmd "begin" "columns" <> brackets "T" + $$ contents + $$ inCmd "end" "columns" + else id + wrapColumn = if "column" `elem` classes + then \contents -> + let fromPct xs = + case reverse xs of + '%':ds -> '0':'.': reverse ds + _ -> xs + w = maybe "0.48" fromPct (lookup "width" kvs) + in inCmd "begin" "column" <> + braces (text w <> "\\textwidth") + $$ contents + $$ inCmd "end" "column" + else id + wrapDir = case lookup "dir" kvs of Just "rtl" -> align "RTL" Just "ltr" -> align "LTR" _ -> id @@ -468,7 +486,8 @@ blockToLaTeX (Div (identifier,classes,kvs) bs) = do wrapNotes txt = if beamer && "notes" `elem` classes then "\\note" <> braces txt -- speaker notes else linkAnchor $$ txt - fmap (wrapDir . wrapLang . wrapNotes) $ blockListToLaTeX bs + fmap (wrapColumns . wrapColumn . wrapDir . wrapLang . wrapNotes) + $ blockListToLaTeX bs blockToLaTeX (Plain lst) = inlineListToLaTeX $ dropWhile isLineBreakOrSpace lst -- title beginning with fig: indicates that the image is a figure -- cgit v1.2.3 From 97fe6c35b5c8c5e3e076f712e841f1db13c5a0bd Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 15 Aug 2017 14:01:11 -0700 Subject: Docx writer: fixed a regression (infinite loop on certain lists). Bug was introduced by commit a868b238f253423281b2648896f184e7cdc05014. --- src/Text/Pandoc/Writers/Docx.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 51e4ffb98..3d6eb9fe5 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -733,8 +733,8 @@ mkLvl marker lvl = styleFor DefaultStyle 3 = "lowerRoman" styleFor DefaultStyle 4 = "decimal" styleFor DefaultStyle 5 = "lowerLetter" - styleFor DefaultStyle 6 = "lowerRoman" - styleFor DefaultStyle x = styleFor DefaultStyle (x `mod` 7) + styleFor DefaultStyle 0 = "lowerRoman" + styleFor DefaultStyle x = styleFor DefaultStyle (x `mod` 6) styleFor _ _ = "decimal" patternFor OneParen s = s ++ ")" patternFor TwoParens s = "(" ++ s ++ ")" -- cgit v1.2.3 From f8b6a224aec780785baf3112f24c44f6c424e6ba Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 15 Aug 2017 21:17:20 -0700 Subject: Remove initial check for pdf creating program. Instead, just try running it and raise the exception if it isn't found at that point. This improves things for users of Cygwin on Windows, where the executable won't be found by `findExecutable` unless `.exe` is added. The same exception is raised as before, but at a later point. Closes #3819. --- src/Text/Pandoc/App.hs | 4 ---- src/Text/Pandoc/PDF.hs | 32 +++++++++++++++++++++++++++----- 2 files changed, 27 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 938bb91e0..521f5e275 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -485,10 +485,6 @@ convertWithOpts opts = do | html5Output -> "wkhtmltopdf" | msOutput -> "pdfroff" | otherwise -> optLaTeXEngine opts - -- check for pdf creating program - mbPdfProg <- liftIO $ findExecutable pdfprog - when (isNothing mbPdfProg) $ liftIO $ E.throwIO $ - PandocPDFProgramNotFoundError pdfprog res <- makePDF pdfprog f writerOptions verbosity media doc case res of diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index ef6a4099c..65d546482 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -50,7 +50,9 @@ import System.Exit (ExitCode (..)) import System.FilePath import System.IO (stdout) import System.IO.Temp (withTempDirectory, withTempFile) +import System.IO.Error (IOError, isDoesNotExistError) import Text.Pandoc.Definition +import Text.Pandoc.Error (PandocError(PandocPDFProgramNotFoundError)) import Text.Pandoc.MediaBag import Text.Pandoc.MIME (getMimeType) import Text.Pandoc.Options (HTMLMathMethod (..), WriterOptions (..)) @@ -193,7 +195,12 @@ tex2pdf' verbosity args tmpDir program source = do let numruns = if "\\tableofcontents" `T.isInfixOf` source then 3 -- to get page numbers else 2 -- 1 run won't give you PDF bookmarks - (exit, log', mbPdf) <- runTeXProgram verbosity program args 1 numruns tmpDir source + (exit, log', mbPdf) <- E.catch + (runTeXProgram verbosity program args 1 numruns tmpDir source) + (\(e :: IOError) -> if isDoesNotExistError e + then E.throwIO $ + PandocPDFProgramNotFoundError program + else E.throwIO e) case (exit, mbPdf) of (ExitFailure _, _) -> do let logmsg = extractMsg log' @@ -321,8 +328,13 @@ ms2pdf verbosity args source = do putStrLn $ "[makePDF] Contents:\n" putStr $ T.unpack source putStr "\n" - (exit, out) <- pipeProcess (Just env') "pdfroff" args - (BL.fromStrict $ UTF8.fromText source) + (exit, out) <- E.catch + (pipeProcess (Just env') "pdfroff" args + (BL.fromStrict $ UTF8.fromText source)) + (\(e :: IOError) -> if isDoesNotExistError e + then E.throwIO $ + PandocPDFProgramNotFoundError "pdfroff" + else E.throwIO e) when (verbosity >= INFO) $ do BL.hPutStr stdout out putStr "\n" @@ -350,7 +362,12 @@ html2pdf verbosity args source = do putStrLn $ "[makePDF] Contents of " ++ file ++ ":" BL.readFile file >>= BL.putStr putStr "\n" - (exit, out) <- pipeProcess (Just env') "wkhtmltopdf" programArgs BL.empty + (exit, out) <- E.catch + (pipeProcess (Just env') "wkhtmltopdf" programArgs BL.empty) + (\(e :: IOError) -> if isDoesNotExistError e + then E.throwIO $ + PandocPDFProgramNotFoundError "wkhtml2pdf" + else E.throwIO e) removeFile file when (verbosity >= INFO) $ do BL.hPutStr stdout out @@ -397,7 +414,12 @@ context2pdf verbosity tmpDir source = inDirectory tmpDir $ do putStrLn $ "[makePDF] Contents of " ++ file ++ ":" BL.readFile file >>= BL.putStr putStr "\n" - (exit, out) <- pipeProcess (Just env') "context" programArgs BL.empty + (exit, out) <- E.catch + (pipeProcess (Just env') "context" programArgs BL.empty) + (\(e :: IOError) -> if isDoesNotExistError e + then E.throwIO $ + PandocPDFProgramNotFoundError "context" + else E.throwIO e) when (verbosity >= INFO) $ do BL.hPutStr stdout out putStr "\n" -- cgit v1.2.3 From 9b318355300ca43aadede728c179785f40326d5c Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Wed, 16 Aug 2017 15:47:05 +0200 Subject: Update to hslua-0.8.0 hslua no longer provides lua stack instances for Int and Double, the necessary instances are added to the Custom writer and the lua filtering system. --- src/Text/Pandoc/Lua/StackInstances.hs | 14 ++++++++++++-- src/Text/Pandoc/Writers/Custom.hs | 6 ++++++ 2 files changed, 18 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index da9c33183..15a7cdd84 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -33,8 +33,8 @@ StackValue instances for pandoc types. module Text.Pandoc.Lua.StackInstances () where import Control.Applicative ((<|>)) -import Foreign.Lua (Lua, Type (..), FromLuaStack (peek), ToLuaStack (push), - StackIndex, throwLuaError, tryLua) +import Foreign.Lua (Lua, LuaInteger, LuaNumber, Type (..), FromLuaStack (peek), + ToLuaStack (push), StackIndex, throwLuaError, tryLua) import Foreign.Lua.Api (getmetatable, ltype, newtable, pop, rawget, rawlen) import Text.Pandoc.Definition import Text.Pandoc.Lua.Util (addValue, adjustIndexBy, getTable, pushViaConstructor) @@ -125,6 +125,16 @@ instance ToLuaStack QuoteType where instance FromLuaStack QuoteType where peek idx = safeRead' =<< peek idx +instance ToLuaStack Double where + push = push . (realToFrac :: Double -> LuaNumber) +instance FromLuaStack Double where + peek = fmap (realToFrac :: LuaNumber -> Double) . peek + +instance ToLuaStack Int where + push = push . (fromIntegral :: Int -> LuaInteger) +instance FromLuaStack Int where + peek = fmap (fromIntegral :: LuaInteger-> Int) . peek + safeRead' :: Read a => String -> Lua a safeRead' s = case safeRead s of Nothing -> throwLuaError ("Could not read: " ++ s) diff --git a/src/Text/Pandoc/Writers/Custom.hs b/src/Text/Pandoc/Writers/Custom.hs index 63725bb60..d7dff6d19 100644 --- a/src/Text/Pandoc/Writers/Custom.hs +++ b/src/Text/Pandoc/Writers/Custom.hs @@ -60,6 +60,12 @@ attrToMap (id',classes,keyvals) = M.fromList : ("class", unwords classes) : keyvals +instance ToLuaStack Double where + push = push . (realToFrac :: Double -> LuaNumber) + +instance ToLuaStack Int where + push = push . (fromIntegral :: Int -> LuaInteger) + instance ToLuaStack Format where push (Format f) = push (map toLower f) -- cgit v1.2.3 From 61cf3affa98f8331a2f2d55eedf56fc2a2a529e2 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 16 Aug 2017 09:45:12 -0700 Subject: Change behavior with binary format output to stdout. Previously, for binary formats, output to stdout was disabled unless we could detect that the output was being piped (and not sent to the terminal). Unfortunately, such detection is not possible on Windows, leaving windows users no way to pipe binary output. So we have changed the behavior in the following way: * If the -o option is not used, binary output is never sent to stdout by default; instead, an error is raised. * IF '-o -' is used, binary output is sent to stdout, regardless of whether it is being piped. This works on Windows too. --- src/Text/Pandoc/App.hs | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 521f5e275..c7f8bbb89 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -91,10 +91,6 @@ import Text.Pandoc.Shared (headerShift, isURI, openURL, import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.XML (toEntities) import Text.Printf -#ifndef _WINDOWS -import System.Posix.IO (stdOutput) -import System.Posix.Terminal (queryTerminal) -#endif data LineEnding = LF | CRLF | Native deriving (Show, Generic) @@ -124,7 +120,7 @@ parseOptions options' defaults = do convertWithOpts :: Opt -> IO () convertWithOpts opts = do let args = optInputFiles opts - let outputFile = optOutputFile opts + let outputFile = fromMaybe "-" (optOutputFile opts) let filters = optFilters opts let verbosity = optVerbosity opts @@ -245,18 +241,14 @@ convertWithOpts opts = do (\(syn,dep) -> (T.unpack syn ++ " requires " ++ T.unpack dep ++ " through IncludeRules.")) xs) - - -#ifdef _WINDOWS - let istty = True -#else - istty <- queryTerminal stdOutput -#endif - when (istty && not (isTextFormat format) && outputFile == "-") $ + -- We don't want to send output to the terminal if the user + -- does 'pandoc -t docx input.txt'; though we allow them to + -- force this with '-o -'. + when (not (isTextFormat format) && optOutputFile opts == Nothing) $ E.throwIO $ PandocAppError $ "Cannot write " ++ format ++ " output to stdout.\n" ++ - "Specify an output file using the -o option." - + "Specify an output file using the -o option, or " ++ + "use '-o -' to force output to stdout." let convertTabs = tabFilter (if optPreserveTabs opts || readerName == "t2t" then 0 @@ -303,7 +295,8 @@ convertWithOpts opts = do variables <- withList (addStringAsVariable "sourcefile") (reverse $ optInputFiles opts) - (("outputfile", optOutputFile opts) : optVariables opts) + (("outputfile", fromMaybe "-" (optOutputFile opts)) + : optVariables opts) -- we reverse this list because, unlike -- the other option lists here, it is -- not reversed when parsed from CLI arguments. @@ -562,7 +555,7 @@ data Opt = Opt , optTemplate :: Maybe FilePath -- ^ Custom template , optVariables :: [(String,String)] -- ^ Template variables to set , optMetadata :: [(String, String)] -- ^ Metadata fields to set - , optOutputFile :: FilePath -- ^ Name of output file + , optOutputFile :: Maybe FilePath -- ^ Name of output file , optInputFiles :: [FilePath] -- ^ Names of input files , optNumberSections :: Bool -- ^ Number sections in LaTeX , optNumberOffset :: [Int] -- ^ Starting number for sections @@ -638,7 +631,7 @@ defaultOpts = Opt , optTemplate = Nothing , optVariables = [] , optMetadata = [] - , optOutputFile = "-" -- "-" means stdout + , optOutputFile = Nothing , optInputFiles = [] , optNumberSections = False , optNumberOffset = [0,0,0,0,0,0] @@ -889,7 +882,7 @@ options = , Option "o" ["output"] (ReqArg - (\arg opt -> return opt { optOutputFile = arg }) + (\arg opt -> return opt { optOutputFile = Just arg }) "FILE") "" -- "Name of output file" -- cgit v1.2.3 From 708bb8afe48e82fd35b1951714ecef1304eb38f7 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 16 Aug 2017 10:13:16 -0700 Subject: Fix import in PDF. --- src/Text/Pandoc/PDF.hs | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index 65d546482..4e4c0b2c1 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -50,7 +50,11 @@ import System.Exit (ExitCode (..)) import System.FilePath import System.IO (stdout) import System.IO.Temp (withTempDirectory, withTempFile) +#if MIN_VERSION_base(4,8,3) import System.IO.Error (IOError, isDoesNotExistError) +#else +import System.IO.Error (isDoesNotExistError) +#endif import Text.Pandoc.Definition import Text.Pandoc.Error (PandocError(PandocPDFProgramNotFoundError)) import Text.Pandoc.MediaBag -- cgit v1.2.3 From cf4b40162d10b17cc7e8fade36c6d2ca9903d9dd Mon Sep 17 00:00:00 2001 From: schrieveslaach <schrieveslaach@online.de> Date: Wed, 16 Aug 2017 19:24:46 +0200 Subject: LaTeX reader: add Support for `glossaries` and `acronym` package (#3589) Acronyms are not resolved by the reader, but acronym and glossary information is put into attributes on Spans so that they can be processed in filters. --- src/Text/Pandoc/Readers/LaTeX.hs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 986547f42..5627e4ee1 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -702,6 +702,21 @@ enquote = do then singleQuoted <$> withQuoteContext InSingleQuote tok else doubleQuoted <$> withQuoteContext InDoubleQuote tok +doAcronym :: PandocMonad m => String -> LP m Inlines +doAcronym form = do + acro <- braced + return . mconcat $ [spanWith ("",[],[("acronym-label", toksToString acro), + ("acronym-form", "singular+" ++ form)]) + $ str $ toksToString acro] + +doAcronymPlural :: PandocMonad m => String -> LP m Inlines +doAcronymPlural form = do + acro <- braced + plural <- lit "s" + return . mconcat $ [spanWith ("",[],[("acronym-label", toksToString acro), + ("acronym-form", "plural+" ++ form)]) $ mconcat + $ [str $ toksToString acro, plural]] + doverb :: PandocMonad m => LP m Inlines doverb = do Tok _ Symbol t <- anySymbol @@ -1371,6 +1386,30 @@ inlineCommands = M.fromList $ , ("nocite", mempty <$ (citation "nocite" NormalCitation False >>= addMeta "nocite")) , ("hypertarget", braced >> tok) + -- glossaries package + , ("gls", doAcronym "short") + , ("Gls", doAcronym "short") + , ("glsdesc", doAcronym "long") + , ("Glsdesc", doAcronym "long") + , ("GLSdesc", doAcronym "long") + , ("acrlong", doAcronym "long") + , ("Acrlong", doAcronym "long") + , ("acrfull", doAcronym "full") + , ("Acrfull", doAcronym "full") + , ("acrshort", doAcronym "abbrv") + , ("Acrshort", doAcronym "abbrv") + , ("glspl", doAcronymPlural "short") + , ("Glspl", doAcronymPlural "short") + , ("glsdescplural", doAcronymPlural "long") + , ("Glsdescplural", doAcronymPlural "long") + , ("GLSdescplural", doAcronymPlural "long") + -- acronyms package + , ("ac", doAcronym "short") + , ("acf", doAcronym "full") + , ("acs", doAcronym "abbrv") + , ("acp", doAcronymPlural "short") + , ("acfp", doAcronymPlural "full") + , ("acsp", doAcronymPlural "abbrv") -- siuntix , ("SI", dosiunitx) -- hyphenat -- cgit v1.2.3 From c6ec189a966c100a7992cc633d88efdd176c2a46 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 16 Aug 2017 10:39:34 -0700 Subject: Revision to binary format output to stdout: We now allow default output to stdout when it can be determined that the output is being piped. (On Windows, as mentioned before, this can't be determined.) Using '-o -' forces output to stdout regardless. --- src/Text/Pandoc/App.hs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index c7f8bbb89..367a1f550 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -91,6 +91,10 @@ import Text.Pandoc.Shared (headerShift, isURI, openURL, import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.XML (toEntities) import Text.Printf +#ifndef _WINDOWS +import System.Posix.IO (stdOutput) +import System.Posix.Terminal (queryTerminal) +#endif data LineEnding = LF | CRLF | Native deriving (Show, Generic) @@ -243,10 +247,17 @@ convertWithOpts opts = do -- We don't want to send output to the terminal if the user -- does 'pandoc -t docx input.txt'; though we allow them to - -- force this with '-o -'. - when (not (isTextFormat format) && optOutputFile opts == Nothing) $ + -- force this with '-o -'. On posix systems, we detect + -- when stdout is being piped and allow output to stdout + -- in that case, but on Windows we can't. +#ifdef _WINDOWS + let istty = True +#else + istty <- queryTerminal stdOutput +#endif + when (not (isTextFormat format) && istty && optOutputFile opts == Nothing) $ E.throwIO $ PandocAppError $ - "Cannot write " ++ format ++ " output to stdout.\n" ++ + "Cannot write " ++ format ++ " output to terminal.\n" ++ "Specify an output file using the -o option, or " ++ "use '-o -' to force output to stdout." -- cgit v1.2.3 From db715ca84797c8fc945392b61c1642434e8fb2b1 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 16 Aug 2017 10:46:56 -0700 Subject: LaTeX reader: use Link instead of Span for `\ref`. This makes more sense semantically and avoids unnecessary Span [Link] nestings when references are resolved. --- src/Text/Pandoc/Readers/LaTeX.hs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 5627e4ee1..6b96460e8 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -111,13 +111,12 @@ parseLaTeX = do return $ Pandoc meta bs' resolveRefs :: M.Map String [Inline] -> Inline -> Inline -resolveRefs labels x@(Span (ident,classes,kvs) _) = +resolveRefs labels x@(Link (ident,classes,kvs) _ _) = case (lookup "reference-type" kvs, lookup "reference" kvs) of (Just "ref", Just lab) -> case M.lookup lab labels of - Just txt -> Span (ident,classes,kvs) - [Link nullAttr txt ('#':lab, "")] + Just txt -> Link (ident,classes,kvs) txt ('#':lab, "") Nothing -> x _ -> x resolveRefs _ x = x @@ -1538,9 +1537,11 @@ doref :: PandocMonad m => String -> LP m Inlines doref cls = do v <- braced let refstr = toksToString v - return $ spanWith ("",[],[ ("reference-type", cls) + return $ linkWith ("",[],[ ("reference-type", cls) , ("reference", refstr)]) - $ inBrackets $ str refstr + ('#':refstr) + "" + (inBrackets $ str refstr) lookupListDefault :: (Show k, Ord k) => v -> [k] -> M.Map k v -> v lookupListDefault d = (fromMaybe d .) . lookupList -- cgit v1.2.3 From ae61d5f57dc8094eb40a9e83427db7fb02afcefb Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 16 Aug 2017 10:56:16 -0700 Subject: LaTeX reader: rudimentary support for `\hyperlink`. --- src/Text/Pandoc/Readers/LaTeX.hs | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 6b96460e8..de2b8f913 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1384,7 +1384,11 @@ inlineCommands = M.fromList $ <|> citation "citeauthor" AuthorInText False) , ("nocite", mempty <$ (citation "nocite" NormalCitation False >>= addMeta "nocite")) + -- hyperlink: for now, we just preserve contents. + -- we might add the actual links, but we need to avoid clashes + -- with ids produced by label. , ("hypertarget", braced >> tok) + , ("hyperlink", braced >> tok) -- glossaries package , ("gls", doAcronym "short") , ("Gls", doAcronym "short") -- cgit v1.2.3 From c175317d031985798fddac6ea7b35f44f64fd78c Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 17 Aug 2017 10:09:35 -0700 Subject: LaTeX reader: support \textquoteleft|right, \textquotedblleft|right. Closes #3849. --- src/Text/Pandoc/Readers/LaTeX.hs | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index de2b8f913..bab056c83 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1209,6 +1209,10 @@ inlineCommands = M.fromList $ , ("textup", extractSpaces (spanWith ("",["upright"],[])) <$> tok) , ("texttt", ttfamily) , ("sout", extractSpaces strikeout <$> tok) + , ("textquoteleft", return (str "‘")) + , ("textquoteright", return (str "’")) + , ("textquotedblleft", return (str "“")) + , ("textquotedblright", return (str "”")) , ("textsuperscript", extractSpaces superscript <$> tok) , ("textsubscript", extractSpaces subscript <$> tok) , ("textbackslash", lit "\\") -- cgit v1.2.3 From 2a0e2e6fd675d08a426380b8d85f9fd05d067692 Mon Sep 17 00:00:00 2001 From: ickc <ickc@users.noreply.github.com> Date: Thu, 17 Aug 2017 10:11:49 -0700 Subject: slidy uses https instead of http (#3848) grep -rl 'http://www.w3.org/Talks/Tools/Slidy2' . | xargs sed -i 's/http:\/\/www\.w3\.org\/Talks\/Tools\/Slidy2/https:\/\/www\.w3\.org\/Talks\/Tools\/Slidy2/g' --- src/Text/Pandoc/Writers/HTML.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index c73af4604..9ac37a0ba 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -304,7 +304,7 @@ pandocToHtml opts (Pandoc meta blocks) = do defField "idprefix" (writerIdentifierPrefix opts) $ -- these should maybe be set in pandoc.hs defField "slidy-url" - ("http://www.w3.org/Talks/Tools/Slidy2" :: String) $ + ("https://www.w3.org/Talks/Tools/Slidy2" :: String) $ defField "slideous-url" ("slideous" :: String) $ defField "revealjs-url" ("reveal.js" :: String) $ defField "s5-url" ("s5/default" :: String) $ -- cgit v1.2.3 From b9b35059f612d93d3bd559116dc18d68d3430500 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 17 Aug 2017 10:45:05 -0700 Subject: LaTeX reader: support \lq, \rq. --- src/Text/Pandoc/Readers/LaTeX.hs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index bab056c83..7cf3048e3 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1209,6 +1209,8 @@ inlineCommands = M.fromList $ , ("textup", extractSpaces (spanWith ("",["upright"],[])) <$> tok) , ("texttt", ttfamily) , ("sout", extractSpaces strikeout <$> tok) + , ("lq", return (str "‘")) + , ("rq", return (str "’")) , ("textquoteleft", return (str "‘")) , ("textquoteright", return (str "’")) , ("textquotedblleft", return (str "“")) -- cgit v1.2.3 From b1f6fb4af5e6df40fe72d6224512f60be082a8cd Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 17 Aug 2017 12:07:07 -0700 Subject: HTML reader: support column alignments. These can be set either with a `width` attribute or with `text-width` in a `style` attribute. Closes #1881. --- src/Text/Pandoc/Readers/HTML.hs | 43 ++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 7b9ab38fd..d85488478 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -55,9 +55,10 @@ import Text.Pandoc.Walk import qualified Data.Map as M import Data.Foldable ( for_ ) import Data.Maybe ( fromMaybe, isJust, isNothing ) +import Data.List.Split ( wordsBy ) import Data.List ( intercalate, isPrefixOf ) import Data.Char ( isDigit, isLetter, isAlphaNum ) -import Control.Monad ( guard, mzero, void, unless ) +import Control.Monad ( guard, mzero, void, unless, mplus ) import Control.Arrow ((***)) import Control.Applicative ( (<|>) ) import Data.Monoid (First (..)) @@ -472,31 +473,35 @@ pTable = try $ do caption <- option mempty $ pInTags "caption" inline <* skipMany pBlank widths' <- (mconcat <$> many1 pColgroup) <|> many pCol let pTh = option [] $ pInTags "tr" (pCell "th") - pTr = try $ skipMany pBlank >> pInTags "tr" (pCell "td" <|> pCell "th") + pTr = try $ skipMany pBlank >> + pInTags "tr" (pCell "td" <|> pCell "th") pTBody = do pOptInTag "tbody" $ many1 pTr head'' <- pOptInTag "thead" pTh - head' <- pOptInTag "tbody" $ do - if null head'' - then pTh - else return head'' + head' <- map snd <$> + (pOptInTag "tbody" $ + if null head'' then pTh else return head'') rowsLs <- many pTBody rows' <- pOptInTag "tfoot" $ many pTr TagClose _ <- pSatisfy (matchTagClose "table") let rows'' = (concat rowsLs) <> rows' + let rows''' = map (map snd) rows'' + -- let rows''' = map (map snd) rows'' -- fail on empty table - guard $ not $ null head' && null rows'' + guard $ not $ null head' && null rows''' let isSinglePlain x = case B.toList x of [] -> True [Plain _] -> True _ -> False - let isSimple = all isSinglePlain $ concat (head':rows'') - let cols = length $ if null head' then head rows'' else head' + let isSimple = all isSinglePlain $ concat (head':rows''') + let cols = length $ if null head' then head rows''' else head' -- add empty cells to short rows let addEmpties r = case cols - length r of n | n > 0 -> r <> replicate n mempty | otherwise -> r - let rows = map addEmpties rows'' - let aligns = replicate cols AlignDefault + let rows = map addEmpties rows''' + let aligns = case rows'' of + (cs:_) -> map fst cs + _ -> replicate cols AlignDefault let widths = if null widths' then if isSimple then replicate cols 0 @@ -534,12 +539,24 @@ noColOrRowSpans t = isNullOrOne "colspan" && isNullOrOne "rowspan" "1" -> True _ -> False -pCell :: PandocMonad m => Text -> TagParser m [Blocks] +pCell :: PandocMonad m => Text -> TagParser m [(Alignment, Blocks)] pCell celltype = try $ do skipMany pBlank + tag <- lookAhead $ + pSatisfy (\t -> t ~== TagOpen celltype [] && noColOrRowSpans t) + let extractAlign' [] = "" + extractAlign' ("text-align":x:_) = x + extractAlign' (_:xs) = extractAlign' xs + let extractAlign = extractAlign' . wordsBy (`elem` [' ','\t',';',':']) + let align = case maybeFromAttrib "align" tag `mplus` + (extractAlign <$> maybeFromAttrib "style" tag) of + Just "left" -> AlignLeft + Just "right" -> AlignRight + Just "center" -> AlignCenter + _ -> AlignDefault res <- pInTags' celltype noColOrRowSpans block skipMany pBlank - return [res] + return [(align, res)] pBlockQuote :: PandocMonad m => TagParser m Blocks pBlockQuote = do -- cgit v1.2.3 From d1444b4ecdd7bc2f3b6180ceb2635d51382c4ab8 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 17 Aug 2017 16:01:44 -0700 Subject: RST reader/writer: support unknown interpreted text roles... ...by parsing them as Span with "role" attributes. This way they can be manipulated in the AST. Closes #3407. --- src/Text/Pandoc/Readers/RST.hs | 6 ++---- src/Text/Pandoc/Writers/RST.hs | 7 ++++++- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 0f594fe1b..190b065fb 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -1399,10 +1399,8 @@ renderRole contents fmt role attr = case role of case M.lookup custom customRoles of Just (newRole, newFmt, newAttr) -> renderRole contents newFmt newRole newAttr - Nothing -> do - pos <- getPosition - logMessage $ SkippedContent (":" ++ custom ++ ":") pos - return $ B.str contents -- Undefined role + Nothing -> -- undefined role + return $ B.spanWith ("",[],[("role",role)]) (B.str contents) where titleRef ref = return $ B.str ref -- FIXME: Not a sensible behaviour rfcLink rfcNo = B.link rfcUrl ("RFC " ++ rfcNo) $ B.str ("RFC " ++ rfcNo) diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index 17f5b3f91..8c941f568 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -439,7 +439,12 @@ inlineListToRST lst = -- | Convert Pandoc inline element to RST. inlineToRST :: PandocMonad m => Inline -> RST m Doc -inlineToRST (Span _ ils) = inlineListToRST ils +inlineToRST (Span (_,_,kvs) ils) = do + contents <- inlineListToRST ils + return $ + case lookup "role" kvs of + Just role -> ":" <> text role <> ":`" <> contents <> "`" + Nothing -> contents inlineToRST (Emph lst) = do contents <- inlineListToRST lst return $ "*" <> contents <> "*" -- cgit v1.2.3 From bfbdfa646a48147cc7210679f6b17654eec35c5f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 18 Aug 2017 10:13:41 -0700 Subject: LaTeX reader: implement \newtoggle, \iftoggle, \toggletrue|false from etoolbox. Closes #3853. --- src/Text/Pandoc/Logging.hs | 9 +++++++ src/Text/Pandoc/Readers/LaTeX.hs | 52 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 56 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs index 832a1f4df..ad3247ec9 100644 --- a/src/Text/Pandoc/Logging.hs +++ b/src/Text/Pandoc/Logging.hs @@ -75,6 +75,7 @@ data LogMessage = | DuplicateIdentifier String SourcePos | ReferenceNotFound String SourcePos | CircularReference String SourcePos + | UndefinedToggle String SourcePos | ParsingUnescaped String SourcePos | CouldNotLoadIncludeFile String SourcePos | MacroAlreadyDefined String SourcePos @@ -144,6 +145,11 @@ instance ToJSON LogMessage where "source" .= Text.pack (sourceName pos), "line" .= toJSON (sourceLine pos), "column" .= toJSON (sourceColumn pos)] + UndefinedToggle s pos -> + ["contents" .= Text.pack s, + "source" .= Text.pack (sourceName pos), + "line" .= toJSON (sourceLine pos), + "column" .= toJSON (sourceColumn pos)] ParsingUnescaped s pos -> ["contents" .= Text.pack s, "source" .= Text.pack (sourceName pos), @@ -238,6 +244,8 @@ showLogMessage msg = "Reference not found for '" ++ s ++ "' at " ++ showPos pos CircularReference s pos -> "Circular reference '" ++ s ++ "' at " ++ showPos pos + UndefinedToggle s pos -> + "Undefined toggle '" ++ s ++ "' at " ++ showPos pos ParsingUnescaped s pos -> "Parsing unescaped '" ++ s ++ "' at " ++ showPos pos CouldNotLoadIncludeFile fp pos -> @@ -306,6 +314,7 @@ messageVerbosity msg = DuplicateIdentifier{} -> WARNING ReferenceNotFound{} -> WARNING CircularReference{} -> WARNING + UndefinedToggle{} -> WARNING CouldNotLoadIncludeFile{} -> WARNING MacroAlreadyDefined{} -> WARNING ParsingUnescaped{} -> INFO diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 7cf3048e3..9699fc742 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -159,6 +159,7 @@ data LaTeXState = LaTeXState{ sOptions :: ReaderOptions , sInTableCell :: Bool , sLastHeaderNum :: HeaderNum , sLabels :: M.Map String [Inline] + , sToggles :: M.Map String Bool } deriving Show @@ -177,6 +178,7 @@ defaultLaTeXState = LaTeXState{ sOptions = def , sInTableCell = False , sLastHeaderNum = HeaderNum [] , sLabels = M.empty + , sToggles = M.empty } instance PandocMonad m => HasQuoteContext LaTeXState m where @@ -704,16 +706,16 @@ enquote = do doAcronym :: PandocMonad m => String -> LP m Inlines doAcronym form = do acro <- braced - return . mconcat $ [spanWith ("",[],[("acronym-label", toksToString acro), - ("acronym-form", "singular+" ++ form)]) + return . mconcat $ [spanWith ("",[],[("acronym-label", toksToString acro), + ("acronym-form", "singular+" ++ form)]) $ str $ toksToString acro] doAcronymPlural :: PandocMonad m => String -> LP m Inlines doAcronymPlural form = do acro <- braced plural <- lit "s" - return . mconcat $ [spanWith ("",[],[("acronym-label", toksToString acro), - ("acronym-form", "plural+" ++ form)]) $ mconcat + return . mconcat $ [spanWith ("",[],[("acronym-label", toksToString acro), + ("acronym-form", "plural+" ++ form)]) $ mconcat $ [str $ toksToString acro, plural]] doverb :: PandocMonad m => LP m Inlines @@ -1440,12 +1442,46 @@ inlineCommands = M.fromList $ , ("xspace", doxspace) -- etoolbox , ("ifstrequal", ifstrequal) + , ("newtoggle", braced >>= newToggle) + , ("toggletrue", braced >>= setToggle True) + , ("togglefalse", braced >>= setToggle False) + , ("iftoggle", try $ ifToggle >> inline) ] +newToggle :: (Monoid a, PandocMonad m) => [Tok] -> LP m a +newToggle name = do + updateState $ \st -> + st{ sToggles = M.insert (toksToString name) False (sToggles st) } + return mempty + +setToggle :: (Monoid a, PandocMonad m) => Bool -> [Tok] -> LP m a +setToggle on name = do + updateState $ \st -> + st{ sToggles = M.adjust (const on) (toksToString name) (sToggles st) } + return mempty + +ifToggle :: PandocMonad m => LP m () +ifToggle = do + name <- braced + spaces + yes <- braced + spaces + no <- braced + toggles <- sToggles <$> getState + inp <- getInput + let name' = toksToString name + case M.lookup name' toggles of + Just True -> setInput (yes ++ inp) + Just False -> setInput (no ++ inp) + Nothing -> do + pos <- getPosition + report $ UndefinedToggle name' pos + return () + doTerm :: PandocMonad m => Translations.Term -> LP m Inlines doTerm term = str <$> translateTerm term -ifstrequal :: PandocMonad m => LP m Inlines +ifstrequal :: (PandocMonad m, Monoid a) => LP m a ifstrequal = do str1 <- tok str2 <- tok @@ -1964,6 +2000,12 @@ environments = M.fromList , ("alignat", mathEnvWith para (Just "aligned") "alignat") , ("alignat*", mathEnvWith para (Just "aligned") "alignat*") , ("tikzpicture", rawVerbEnv "tikzpicture") + -- etoolbox + , ("ifstrequal", ifstrequal) + , ("newtoggle", braced >>= newToggle) + , ("toggletrue", braced >>= setToggle True) + , ("togglefalse", braced >>= setToggle False) + , ("iftoggle", try $ ifToggle >> block) ] environment :: PandocMonad m => LP m Blocks -- cgit v1.2.3 From 7cac58f126402fd965a9a027e13a1cc3446512d9 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 18 Aug 2017 11:40:57 -0700 Subject: Markdown reader: parse `-@roe` as suppress-author citation. Previously only `[-@roe]` (with brackets) was recognized as suppress-author, and `-@roe` was treated the same as `@roe`. Closes jgm/pandoc-citeproc#237. --- src/Text/Pandoc/Readers/Markdown.hs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 1bcf1cfae..cc9e9b71f 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -1971,11 +1971,13 @@ cite = do textualCite :: PandocMonad m => MarkdownParser m (F Inlines) textualCite = try $ do - (_, key) <- citeKey + (suppressAuthor, key) <- citeKey let first = Citation{ citationId = key , citationPrefix = [] , citationSuffix = [] - , citationMode = AuthorInText + , citationMode = if suppressAuthor + then SuppressAuthor + else AuthorInText , citationNoteNum = 0 , citationHash = 0 } -- cgit v1.2.3 From 5ab1162def4e6379c84e3363d917252155d9239a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 18 Aug 2017 21:46:55 -0700 Subject: Markdown reader: fixed parsing of fenced code after list... ...when there is no intervening blank line. Closes #3733. --- src/Text/Pandoc/Readers/Markdown.hs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index cc9e9b71f..26263d674 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -868,7 +868,10 @@ rawListItem :: PandocMonad m rawListItem start = try $ do start first <- listLineCommon - rest <- many (notFollowedBy listStart >> notFollowedBy blankline >> listLine) + rest <- many (do notFollowedBy listStart + notFollowedBy (() <$ codeBlockFenced) + notFollowedBy blankline + listLine) blanks <- many blankline return $ unlines (first:rest) ++ blanks -- cgit v1.2.3 From a31241a08bcd3d546528ef7eed4c126fff3cd3bd Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 19 Aug 2017 10:56:15 -0700 Subject: Markdown reader: use CommonMark rules for list item nesting. Closes #3511. Previously pandoc used the four-space rule: continuation paragraphs, sublists, and other block level content had to be indented 4 spaces. Now the indentation required is determined by the first line of the list item: to be included in the list item, blocks must be indented to the level of the first non-space content after the list marker. Exception: if are 5 or more spaces after the list marker, then the content is interpreted as an indented code block, and continuation paragraphs must be indented two spaces beyond the end of the list marker. See the CommonMark spec for more details and examples. Documents that adhere to the four-space rule should, in most cases, be parsed the same way by the new rules. Here are some examples of texts that will be parsed differently: - a - b will be parsed as a list item with a sublist; under the four-space rule, it would be a list with two items. - a code Here we have an indented code block under the list item, even though it is only indented six spaces from the margin, because it is four spaces past the point where a continuation paragraph could begin. With the four-space rule, this would be a regular paragraph rather than a code block. - a code Here the code block will start with two spaces, whereas under the four-space rule, it would start with `code`. With the four-space rule, indented code under a list item always must be indented eight spaces from the margin, while the new rules require only that it be indented four spaces from the beginning of the first non-space text after the list marker (here, `a`). This change was motivated by a slew of bug reports from people who expected lists to work differently (#3125, #2367, #2575, #2210, #1990, #1137, #744, #172, #137, #128) and by the growing prevalance of CommonMark (now used by GitHub, for example). Users who want to use the old rules can select the `four_space_rule` extension. * Added `four_space_rule` extension. * Added `Ext_four_space_rule` to `Extensions`. * `Parsing` now exports `gobbleAtMostSpaces`, and the type of `gobbleSpaces` has been changed so that a `ReaderOptions` parameter is not needed. --- src/Text/Pandoc/Extensions.hs | 1 + src/Text/Pandoc/Parsing.hs | 36 +++++++--- src/Text/Pandoc/Readers/Markdown.hs | 129 +++++++++++++++++------------------- 3 files changed, 90 insertions(+), 76 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index e6a3ca044..95e59063b 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -111,6 +111,7 @@ data Extension = | Ext_autolink_bare_uris -- ^ Make all absolute URIs into links | Ext_fancy_lists -- ^ Enable fancy list numbers and delimiters | Ext_lists_without_preceding_blankline -- ^ Allow lists without preceding blank + | Ext_four_space_rule -- ^ Require 4-space indent for list contents | Ext_startnum -- ^ Make start number of ordered list significant | Ext_definition_lists -- ^ Definition lists as in pandoc, mmd, php | Ext_compact_definition_lists -- ^ Definition lists without diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 37a0b53b4..9ed18d4e0 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -50,6 +50,7 @@ module Text.Pandoc.Parsing ( takeWhileP, blankline, blanklines, gobbleSpaces, + gobbleAtMostSpaces, enclosed, stringAnyCase, parseFromString, @@ -380,14 +381,33 @@ blanklines = many1 blankline -- | Gobble n spaces; if tabs are encountered, expand them -- and gobble some or all of their spaces, leaving the rest. -gobbleSpaces :: Monad m => ReaderOptions -> Int -> ParserT [Char] st m () -gobbleSpaces _ 0 = return () -gobbleSpaces opts n = try $ do - char ' ' <|> do char '\t' - inp <- getInput - setInput $ replicate (readerTabStop opts - 1) ' ' ++ inp - return ' ' - gobbleSpaces opts (n - 1) +gobbleSpaces :: (HasReaderOptions st, Monad m) + => Int -> ParserT [Char] st m () +gobbleSpaces 0 = return () +gobbleSpaces n + | n < 0 = error "gobbleSpaces called with negative number" + | otherwise = try $ do + char ' ' <|> eatOneSpaceOfTab + gobbleSpaces (n - 1) + +eatOneSpaceOfTab :: (HasReaderOptions st, Monad m) => ParserT [Char] st m Char +eatOneSpaceOfTab = do + char '\t' + tabstop <- getOption readerTabStop + inp <- getInput + setInput $ replicate (tabstop - 1) ' ' ++ inp + return ' ' + +-- | Gobble up to n spaces; if tabs are encountered, expand them +-- and gobble some or all of their spaces, leaving the rest. +gobbleAtMostSpaces :: (HasReaderOptions st, Monad m) + => Int -> ParserT [Char] st m Int +gobbleAtMostSpaces 0 = return 0 +gobbleAtMostSpaces n + | n < 0 = error "gobbleAtMostSpaces called with negative number" + | otherwise = option 0 $ do + char ' ' <|> eatOneSpaceOfTab + (+ 1) <$> gobbleAtMostSpaces (n - 1) -- | Parses material enclosed between start and end parsers. enclosed :: (Show end, Stream s m Char) => ParserT s st m t -- ^ start parser diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 26263d674..664691c8c 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -138,12 +138,7 @@ nonindentSpaces = do skipNonindentSpaces :: PandocMonad m => MarkdownParser m Int skipNonindentSpaces = do tabStop <- getOption readerTabStop - atMostSpaces (tabStop - 1) <* notFollowedBy spaceChar - -atMostSpaces :: PandocMonad m => Int -> MarkdownParser m Int -atMostSpaces n - | n > 0 = (char ' ' >> (+1) <$> atMostSpaces (n-1)) <|> return 0 - | otherwise = return 0 + gobbleAtMostSpaces (tabStop - 1) <* notFollowedBy spaceChar litChar :: PandocMonad m => MarkdownParser m Char litChar = escapedChar' @@ -809,49 +804,51 @@ blockQuote = do bulletListStart :: PandocMonad m => MarkdownParser m () bulletListStart = try $ do optional newline -- if preceded by a Plain block in a list context - startpos <- sourceColumn <$> getPosition skipNonindentSpaces notFollowedBy' (() <$ hrule) -- because hrules start out just like lists satisfy isBulletListMarker - endpos <- sourceColumn <$> getPosition - tabStop <- getOption readerTabStop - lookAhead (newline <|> spaceChar) - () <$ atMostSpaces (tabStop - (endpos - startpos)) + gobbleSpaces 1 <|> () <$ lookAhead newline + try (gobbleAtMostSpaces 3 >> notFollowedBy spaceChar) <|> return () -anyOrderedListStart :: PandocMonad m => MarkdownParser m (Int, ListNumberStyle, ListNumberDelim) -anyOrderedListStart = try $ do +orderedListStart :: PandocMonad m + => Maybe (ListNumberStyle, ListNumberDelim) + -> MarkdownParser m (Int, ListNumberStyle, ListNumberDelim) +orderedListStart mbstydelim = try $ do optional newline -- if preceded by a Plain block in a list context - startpos <- sourceColumn <$> getPosition skipNonindentSpaces notFollowedBy $ string "p." >> spaceChar >> digit -- page number - res <- do guardDisabled Ext_fancy_lists - start <- many1 digit >>= safeRead - char '.' - return (start, DefaultStyle, DefaultDelim) - <|> do (num, style, delim) <- anyOrderedListMarker - -- if it could be an abbreviated first name, - -- insist on more than one space - when (delim == Period && (style == UpperAlpha || - (style == UpperRoman && - num `elem` [1, 5, 10, 50, 100, 500, 1000]))) $ - () <$ spaceChar - return (num, style, delim) - endpos <- sourceColumn <$> getPosition - tabStop <- getOption readerTabStop - lookAhead (newline <|> spaceChar) - atMostSpaces (tabStop - (endpos - startpos)) - return res + (do guardDisabled Ext_fancy_lists + start <- many1 digit >>= safeRead + char '.' + gobbleSpaces 1 <|> () <$ lookAhead newline + optional $ try (gobbleAtMostSpaces 3 >> notFollowedBy spaceChar) + return (start, DefaultStyle, DefaultDelim)) + <|> + (do (num, style, delim) <- maybe + anyOrderedListMarker + (\(sty,delim) -> (\start -> (start,sty,delim)) <$> + orderedListMarker sty delim) + mbstydelim + gobbleSpaces 1 <|> () <$ lookAhead newline + -- if it could be an abbreviated first name, + -- insist on more than one space + when (delim == Period && (style == UpperAlpha || + (style == UpperRoman && + num `elem` [1, 5, 10, 50, 100, 500, 1000]))) $ + () <$ lookAhead (newline <|> spaceChar) + optional $ try (gobbleAtMostSpaces 3 >> notFollowedBy spaceChar) + return (num, style, delim)) listStart :: PandocMonad m => MarkdownParser m () -listStart = bulletListStart <|> (anyOrderedListStart >> return ()) +listStart = bulletListStart <|> (orderedListStart Nothing >> return ()) -listLine :: PandocMonad m => MarkdownParser m String -listLine = try $ do - notFollowedBy' (do indentSpaces - many spaceChar +listLine :: PandocMonad m => Int -> MarkdownParser m String +listLine continuationIndent = try $ do + notFollowedBy' (do gobbleSpaces continuationIndent + skipMany spaceChar listStart) notFollowedByHtmlCloser - optional (() <$ indentSpaces) + optional (() <$ gobbleSpaces continuationIndent) listLineCommon listLineCommon :: PandocMonad m => MarkdownParser m String @@ -864,26 +861,39 @@ listLineCommon = concat <$> manyTill -- parse raw text for one list item, excluding start marker and continuations rawListItem :: PandocMonad m => MarkdownParser m a - -> MarkdownParser m String + -> MarkdownParser m (String, Int) rawListItem start = try $ do + pos1 <- getPosition start + pos2 <- getPosition + continuationIndent <- (4 <$ guardEnabled Ext_four_space_rule) + <|> return (sourceColumn pos2 - sourceColumn pos1) first <- listLineCommon rest <- many (do notFollowedBy listStart notFollowedBy (() <$ codeBlockFenced) notFollowedBy blankline - listLine) + listLine continuationIndent) blanks <- many blankline - return $ unlines (first:rest) ++ blanks + let result = unlines (first:rest) ++ blanks + return (result, continuationIndent) -- continuation of a list item - indented and separated by blankline -- or (in compact lists) endline. -- note: nested lists are parsed as continuations -listContinuation :: PandocMonad m => MarkdownParser m String -listContinuation = try $ do - lookAhead indentSpaces - result <- many1 listContinuationLine +listContinuation :: PandocMonad m => Int -> MarkdownParser m String +listContinuation continuationIndent = try $ do + x <- try $ do + notFollowedBy blankline + notFollowedByHtmlCloser + gobbleSpaces continuationIndent + anyLineNewline + xs <- many $ try $ do + notFollowedBy blankline + notFollowedByHtmlCloser + gobbleSpaces continuationIndent <|> notFollowedBy' listStart + anyLineNewline blanks <- many blankline - return $ concat result ++ blanks + return $ concat (x:xs) ++ blanks notFollowedByHtmlCloser :: PandocMonad m => MarkdownParser m () notFollowedByHtmlCloser = do @@ -892,20 +902,12 @@ notFollowedByHtmlCloser = do Just t -> notFollowedBy' $ htmlTag (~== TagClose t) Nothing -> return () -listContinuationLine :: PandocMonad m => MarkdownParser m String -listContinuationLine = try $ do - notFollowedBy blankline - notFollowedBy' listStart - notFollowedByHtmlCloser - optional indentSpaces - anyLineNewline - listItem :: PandocMonad m => MarkdownParser m a -> MarkdownParser m (F Blocks) listItem start = try $ do - first <- rawListItem start - continuations <- many listContinuation + (first, continuationIndent) <- rawListItem start + continuations <- many (listContinuation continuationIndent) -- parsing with ListItemState forces markers at beginning of lines to -- count as list item markers, even if not separated by blank space. -- see definition of "endline" @@ -920,23 +922,14 @@ listItem start = try $ do orderedList :: PandocMonad m => MarkdownParser m (F Blocks) orderedList = try $ do - (start, style, delim) <- lookAhead anyOrderedListStart + (start, style, delim) <- lookAhead (orderedListStart Nothing) unless (style `elem` [DefaultStyle, Decimal, Example] && delim `elem` [DefaultDelim, Period]) $ guardEnabled Ext_fancy_lists when (style == Example) $ guardEnabled Ext_example_lists items <- fmap sequence $ many1 $ listItem - ( try $ do - optional newline -- if preceded by Plain block in a list - startpos <- sourceColumn <$> getPosition - skipNonindentSpaces - res <- orderedListMarker style delim - endpos <- sourceColumn <$> getPosition - tabStop <- getOption readerTabStop - lookAhead (newline <|> spaceChar) - atMostSpaces (tabStop - (endpos - startpos)) - return res ) - start' <- option 1 $ guardEnabled Ext_startnum >> return start + (orderedListStart (Just (style, delim))) + start' <- (start <$ guardEnabled Ext_startnum) <|> return 1 return $ B.orderedListWith (start', style, delim) <$> fmap compactify items bulletList :: PandocMonad m => MarkdownParser m (F Blocks) @@ -1122,7 +1115,7 @@ rawHtmlBlocks = do updateState $ \st -> st{ stateInHtmlBlock = Just tagtype } let closer = htmlTag (\x -> x ~== TagClose tagtype) let block' = do notFollowedBy' closer - atMostSpaces indentlevel + gobbleAtMostSpaces indentlevel block contents <- mconcat <$> many block' result <- -- cgit v1.2.3 From 8b8c94552ffc4d38c7ed0b38af71f9d46026b29b Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 19 Aug 2017 16:39:22 -0700 Subject: Simplify instances in Class by parameterizing on MonadTrans. --- src/Text/Pandoc/Class.hs | 88 ++++++++++++------------------------------------ 1 file changed, 21 insertions(+), 67 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 25d6d2927..aebe617b1 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -4,6 +4,7 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE UndecidableInstances #-} {- Copyright (C) 2016-17 Jesse Rosenthal <jrosenthal@jhu.edu> @@ -123,11 +124,9 @@ import System.FilePath ((</>), (<.>), takeDirectory, import qualified System.FilePath.Glob as IO (glob) import qualified System.Directory as IO (getModificationTime) import Control.Monad as M (fail) -import Control.Monad.Reader (ReaderT) import Control.Monad.State.Strict import Control.Monad.Except -import Control.Monad.Writer (WriterT) -import Control.Monad.RWS (RWST) +import Control.Monad.Trans (MonadTrans) import Data.Word (Word8) import Data.Default import System.IO.Error @@ -841,53 +840,13 @@ instance PandocMonad PandocPure where logOutput _msg = return () -instance PandocMonad m => PandocMonad (ParsecT s st m) where - lookupEnv = lift . lookupEnv - getCurrentTime = lift getCurrentTime - getCurrentTimeZone = lift getCurrentTimeZone - newStdGen = lift newStdGen - newUniqueHash = lift newUniqueHash - openURL = lift . openURL - readFileLazy = lift . readFileLazy - readFileStrict = lift . readFileStrict - glob = lift . glob - fileExists = lift . fileExists - getDataFileName = lift . getDataFileName - getModificationTime = lift . getModificationTime - getCommonState = lift getCommonState - putCommonState = lift . putCommonState - trace msg = do - tracing <- getsCommonState stTrace - when tracing $ do - pos <- getPosition - Debug.Trace.trace - ("[trace] Parsed " ++ msg ++ " at line " ++ - show (sourceLine pos) ++ - if sourceName pos == "chunk" - then " of chunk" - else "") - (return ()) - logOutput = lift . logOutput - - -instance PandocMonad m => PandocMonad (ReaderT r m) where - lookupEnv = lift . lookupEnv - getCurrentTime = lift getCurrentTime - getCurrentTimeZone = lift getCurrentTimeZone - newStdGen = lift newStdGen - newUniqueHash = lift newUniqueHash - openURL = lift . openURL - readFileLazy = lift . readFileLazy - readFileStrict = lift . readFileStrict - glob = lift . glob - fileExists = lift . fileExists - getDataFileName = lift . getDataFileName - getModificationTime = lift . getModificationTime - getCommonState = lift getCommonState - putCommonState = lift . putCommonState - logOutput = lift . logOutput - -instance (PandocMonad m, Monoid w) => PandocMonad (WriterT w m) where +-- This requires UndecidableInstances. We could avoid that +-- by repeating the definitions below for every monad transformer +-- we use: ReaderT, WriterT, StateT, RWST. But this seems to +-- be harmless. +instance (MonadTrans t, PandocMonad m, Functor (t m), + MonadError PandocError (t m), Monad (t m), + Applicative (t m)) => PandocMonad (t m) where lookupEnv = lift . lookupEnv getCurrentTime = lift getCurrentTime getCurrentTimeZone = lift getCurrentTimeZone @@ -904,7 +863,7 @@ instance (PandocMonad m, Monoid w) => PandocMonad (WriterT w m) where putCommonState = lift . putCommonState logOutput = lift . logOutput -instance (PandocMonad m, Monoid w) => PandocMonad (RWST r w st m) where +instance {-# OVERLAPS #-} PandocMonad m => PandocMonad (ParsecT s st m) where lookupEnv = lift . lookupEnv getCurrentTime = lift getCurrentTime getCurrentTimeZone = lift getCurrentTimeZone @@ -919,21 +878,16 @@ instance (PandocMonad m, Monoid w) => PandocMonad (RWST r w st m) where getModificationTime = lift . getModificationTime getCommonState = lift getCommonState putCommonState = lift . putCommonState + trace msg = do + tracing <- getsCommonState stTrace + when tracing $ do + pos <- getPosition + Debug.Trace.trace + ("[trace] Parsed " ++ msg ++ " at line " ++ + show (sourceLine pos) ++ + if sourceName pos == "chunk" + then " of chunk" + else "") + (return ()) logOutput = lift . logOutput -instance PandocMonad m => PandocMonad (StateT st m) where - lookupEnv = lift . lookupEnv - getCurrentTime = lift getCurrentTime - getCurrentTimeZone = lift getCurrentTimeZone - newStdGen = lift newStdGen - newUniqueHash = lift newUniqueHash - openURL = lift . openURL - readFileLazy = lift . readFileLazy - readFileStrict = lift . readFileStrict - glob = lift . glob - fileExists = lift . fileExists - getDataFileName = lift . getDataFileName - getModificationTime = lift . getModificationTime - getCommonState = lift getCommonState - putCommonState = lift . putCommonState - logOutput = lift . logOutput -- cgit v1.2.3 From 40d12466297e51f5d88546798b420ef291b5d48f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 20 Aug 2017 09:37:18 -0700 Subject: Removed redundant import. --- src/Text/Pandoc/Class.hs | 1 - 1 file changed, 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index aebe617b1..6b46cdff5 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -126,7 +126,6 @@ import qualified System.Directory as IO (getModificationTime) import Control.Monad as M (fail) import Control.Monad.State.Strict import Control.Monad.Except -import Control.Monad.Trans (MonadTrans) import Data.Word (Word8) import Data.Default import System.IO.Error -- cgit v1.2.3 From ba3088f0b3e522cb0cc07c9072a0d2b1b5486047 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 20 Aug 2017 10:43:31 -0700 Subject: Use OverlappingInstances instead of OVERLAPS for ghc 7.8.4. --- src/Text/Pandoc/Class.hs | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 6b46cdff5..5ca285ca4 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -5,6 +5,10 @@ {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE UndecidableInstances #-} +#if MIN_VERSION_base(4,8,0) +#else +{-# LANGUAGE OverlappingInstances #-} +#endif {- Copyright (C) 2016-17 Jesse Rosenthal <jrosenthal@jhu.edu> -- cgit v1.2.3 From e334d7dc389203b48b8e5f0d90960d08350b93f8 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 20 Aug 2017 11:13:42 -0700 Subject: Protect OVERLAPS pragma with CPP. --- src/Text/Pandoc/Class.hs | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 5ca285ca4..98c567afc 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -866,7 +866,11 @@ instance (MonadTrans t, PandocMonad m, Functor (t m), putCommonState = lift . putCommonState logOutput = lift . logOutput +#if MIN_VERSION_base(4,8,0) instance {-# OVERLAPS #-} PandocMonad m => PandocMonad (ParsecT s st m) where +#else +instance PandocMonad m => PandocMonad (ParsecT s st m) where +#endif lookupEnv = lift . lookupEnv getCurrentTime = lift getCurrentTime getCurrentTimeZone = lift getCurrentTimeZone -- cgit v1.2.3 From f2fdd275fd44b4992d5ae3736c3f28deec700ba8 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 20 Aug 2017 13:42:43 -0700 Subject: LaTeX reader: allow `]` inside group in option brackets. Closes #3857. --- src/Text/Pandoc/Readers/LaTeX.hs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 9699fc742..56eb85064 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1120,8 +1120,7 @@ opt = bracketed inline rawopt :: PandocMonad m => LP m Text rawopt = do - symbol '[' - inner <- untokenize <$> manyTill anyTok (symbol ']') + inner <- untokenize <$> bracketedToks optional sp return $ "[" <> inner <> "]" @@ -1789,7 +1788,7 @@ newenvironment = do bracketedToks :: PandocMonad m => LP m [Tok] bracketedToks = do symbol '[' - manyTill anyTok (symbol ']') + mconcat <$> manyTill (braced <|> (:[]) <$> anyTok) (symbol ']') bracketedNum :: PandocMonad m => LP m Int bracketedNum = do -- cgit v1.2.3 From 9cc128b5790437bacbda839d8ea49bd7f74ee118 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 20 Aug 2017 16:52:03 -0700 Subject: LaTeX reader: Set identifiers on Spans used for \label. --- src/Text/Pandoc/Readers/LaTeX.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 56eb85064..3292550b2 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1575,7 +1575,8 @@ treatAsInline = Set.fromList dolabel :: PandocMonad m => LP m Inlines dolabel = do v <- braced - return $ spanWith ("",[],[("label", toksToString v)]) + let refstr = toksToString v + return $ spanWith (refstr,[],[("label", refstr)]) $ inBrackets $ str $ toksToString v doref :: PandocMonad m => String -> LP m Inlines -- cgit v1.2.3 From 0a839cbdc982217819c08c918cca75f6f56eabbb Mon Sep 17 00:00:00 2001 From: Alexander <ilabdsf@gmail.com> Date: Tue, 22 Aug 2017 07:08:44 +0300 Subject: Muse reader: add definition list support (#3860) --- src/Text/Pandoc/Readers/Muse.hs | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 5d77dec13..924149294 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -33,7 +33,6 @@ TODO: - Page breaks (five "*") - Headings with anchors (make it round trip with Muse writer) - <verse> and ">" -- Definition lists - Org tables - table.el tables - Images with attributes (floating and width) @@ -184,6 +183,7 @@ blockElements = choice [ comment , quoteTag , bulletList , orderedList + , definitionList , table , commentTag , noteBlock @@ -348,6 +348,33 @@ orderedList = try $ do items <- sequence <$> many1 (listItem $ orderedListStart style delim) return $ B.orderedListWith p <$> items +definitionListItem :: PandocMonad m => MuseParser m (F (Inlines, [Blocks])) +definitionListItem = try $ do + term <- termParser + many1 spaceChar + string "::" + firstLine <- anyLineNewline + restLines <- manyTill anyLineNewline endOfListItemElement + let lns = firstLine : restLines + lineContent <- parseFromString (withListContext parseBlocks) $ concat lns ++ "\n" + pure $ do lineContent' <- lineContent + pure (B.text term, [lineContent']) + where + termParser = (many1 spaceChar) >> -- Initial space as required by Amusewiki, but not Emacs Muse + (many1Till anyChar $ lookAhead (void (try (spaceChar >> string "::")) <|> void newline)) + endOfInput = try $ skipMany blankline >> skipSpaces >> eof + twoBlankLines = try $ blankline >> skipMany1 blankline + newDefinitionListItem = try $ void termParser + endOfListItemElement = lookAhead $ endOfInput <|> newDefinitionListItem <|> twoBlankLines + +definitionListItems :: PandocMonad m => MuseParser m (F [(Inlines, [Blocks])]) +definitionListItems = sequence <$> many1 definitionListItem + +definitionList :: PandocMonad m => MuseParser m (F Blocks) +definitionList = do + listItems <- definitionListItems + return $ B.definitionList <$> listItems + -- -- tables -- -- cgit v1.2.3 From 56fb854ad85dafff2016892bd6d2c5d24423bff0 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Tue, 22 Aug 2017 22:02:30 +0200 Subject: Text.Pandoc.Lua: respect metatable when getting filters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change makes it possible to define a catch-all function using lua's metatable lookup functionality. function catch_all(el) … end return { setmetatable({}, {__index = function(_) return catch_all end}) } A further effect of this change is that the map with filter functions now only contains functions corresponding to AST element constructors. --- src/Text/Pandoc/Lua.hs | 128 +++++++++++++++++++++++++++++-------------------- 1 file changed, 76 insertions(+), 52 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 6190a5fcf..db028d325 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -32,50 +32,50 @@ Pandoc lua utils. -} module Text.Pandoc.Lua (LuaException (..), pushPandocModule, runLuaFilter) where -import Control.Monad (unless, when, (>=>), mplus) +import Control.Monad (mplus, unless, when, (>=>)) import Control.Monad.Trans (MonadIO (..)) -import Data.Data (toConstr, showConstr, dataTypeOf, dataTypeConstrs, Data) +import Data.Data (DataType, Data, toConstr, showConstr, dataTypeOf, + dataTypeConstrs) +import Data.Foldable (foldrM) import Data.Map (Map) import Data.Maybe (isJust) import Foreign.Lua (Lua, FromLuaStack (peek), LuaException (..), StackIndex, - Status(OK), ToLuaStack (push), call, isnil, dofile, - getglobal', gettop, isfunction, newtable, openlibs, pcall, - peekEither, pop, pushvalue, rawgeti, rawseti, ref, - registryindex, runLua, setglobal, throwLuaError) + Status(OK), ToLuaStack (push)) import Text.Pandoc.Definition import Text.Pandoc.Lua.PandocModule (pushPandocModule) import Text.Pandoc.Walk (Walkable (walkM)) import qualified Data.Map as Map +import qualified Foreign.Lua as Lua runLuaFilter :: (MonadIO m) => Maybe FilePath -> FilePath -> [String] -> Pandoc -> m Pandoc -runLuaFilter datadir filterPath args pd = liftIO . runLua $ do - openlibs +runLuaFilter datadir filterPath args pd = liftIO . Lua.runLua $ do + Lua.openlibs -- store module in global "pandoc" pushPandocModule datadir - setglobal "pandoc" - top <- gettop - stat<- dofile filterPath + Lua.setglobal "pandoc" + top <- Lua.gettop + stat<- Lua.dofile filterPath if stat /= OK then do - luaErrMsg <- peek (-1) <* pop 1 - throwLuaError luaErrMsg + luaErrMsg <- peek (-1) <* Lua.pop 1 + Lua.throwLuaError luaErrMsg else do - newtop <- gettop + newtop <- Lua.gettop -- Use the implicitly defined global filter if nothing was returned when (newtop - top < 1) $ pushGlobalFilter luaFilters <- peek (-1) push args - setglobal "PandocParameters" + Lua.setglobal "PandocParameters" runAll luaFilters pd pushGlobalFilter :: Lua () pushGlobalFilter = do - newtable - getglobal' "pandoc.global_filter" - call 0 1 - rawseti (-2) 1 + Lua.newtable + Lua.getglobal' "pandoc.global_filter" + Lua.call 0 1 + Lua.rawseti (-2) 1 runAll :: [LuaFilter] -> Pandoc -> Lua Pandoc runAll = foldr ((>=>) . walkMWithLuaFilter) return @@ -85,29 +85,42 @@ walkMWithLuaFilter (LuaFilter fnMap) = walkLua where walkLua :: Pandoc -> Lua Pandoc walkLua = - (if hasOneOf (constructorsFor (dataTypeOf (Str []))) - then walkM (tryFilter fnMap :: Inline -> Lua Inline) - else return) - >=> - (if hasOneOf (constructorsFor (dataTypeOf (Para []))) - then walkM (tryFilter fnMap :: Block -> Lua Block) - else return) - >=> - (case Map.lookup "Meta" fnMap of - Just fn -> walkM (\(Pandoc meta blocks) -> do - meta' <- runFilterFunction fn meta - return $ Pandoc meta' blocks) - Nothing -> return) - >=> - (case Map.lookup "Pandoc" fnMap `mplus` Map.lookup "Doc" fnMap of - Just fn -> runFilterFunction fn :: Pandoc -> Lua Pandoc - Nothing -> return) + (if hasOneOf inlineFilterNames + then walkM (tryFilter fnMap :: Inline -> Lua Inline) + else return) + >=> + (if hasOneOf blockFilterNames + then walkM (tryFilter fnMap :: Block -> Lua Block) + else return) + >=> + (case Map.lookup "Meta" fnMap of + Just fn -> walkM (\(Pandoc meta blocks) -> do + meta' <- runFilterFunction fn meta + return $ Pandoc meta' blocks) + Nothing -> return) + >=> + (case foldl mplus Nothing (map (`Map.lookup` fnMap) pandocFilterNames) of + Just fn -> runFilterFunction fn :: Pandoc -> Lua Pandoc + Nothing -> return) hasOneOf = any (\k -> isJust (Map.lookup k fnMap)) - constructorsFor x = map show (dataTypeConstrs x) -type FunctionMap = Map String LuaFilterFunction -data LuaFilter = LuaFilter FunctionMap +constructorsFor :: DataType -> [String] +constructorsFor x = map show (dataTypeConstrs x) + +inlineFilterNames :: [String] +inlineFilterNames = constructorsFor (dataTypeOf (Str [])) + +blockFilterNames :: [String] +blockFilterNames = constructorsFor (dataTypeOf (Para [])) +metaFilterName :: String +metaFilterName = "Meta" + +pandocFilterNames :: [String] +pandocFilterNames = ["Pandoc", "Doc"] + +type FunctionMap = Map String LuaFilterFunction +newtype LuaFilter = LuaFilter FunctionMap newtype LuaFilterFunction = LuaFilterFunction { functionIndex :: Int } -- | Try running a filter for the given element @@ -119,7 +132,18 @@ tryFilter fnMap x = Just fn -> runFilterFunction fn x instance FromLuaStack LuaFilter where - peek idx = LuaFilter <$> peek idx + peek idx = + let constrs = metaFilterName : pandocFilterNames + ++ blockFilterNames + ++ inlineFilterNames + fn c acc = do + Lua.getfield idx c + filterFn <- Lua.tryLua (peek (-1)) + Lua.pop 1 + return $ case filterFn of + Left _ -> acc + Right f -> (c, f) : acc + in LuaFilter . Map.fromList <$> foldrM fn [] constrs -- | Push a value to the stack via a lua filter function. The filter function is -- called with given element as argument and is expected to return an element. @@ -130,36 +154,36 @@ runFilterFunction :: (FromLuaStack a, ToLuaStack a) runFilterFunction lf x = do pushFilterFunction lf push x - z <- pcall 1 1 Nothing + z <- Lua.pcall 1 1 Nothing if z /= OK then do msg <- peek (-1) let prefix = "Error while running filter function: " - throwLuaError $ prefix ++ msg + Lua.throwLuaError $ prefix ++ msg else do - noExplicitFilter <- isnil (-1) + noExplicitFilter <- Lua.isnil (-1) if noExplicitFilter - then pop 1 *> return x + then Lua.pop 1 *> return x else do - mbres <- peekEither (-1) + mbres <- Lua.peekEither (-1) case mbres of - Left err -> throwLuaError + Left err -> Lua.throwLuaError ("Error while trying to get a filter's return " ++ "value from lua stack.\n" ++ err) - Right res -> res <$ pop 1 + Right res -> res <$ Lua.pop 1 -- | Push the filter function to the top of the stack. pushFilterFunction :: LuaFilterFunction -> Lua () pushFilterFunction lf = -- The function is stored in a lua registry table, retrieve it from there. - rawgeti registryindex (functionIndex lf) + Lua.rawgeti Lua.registryindex (functionIndex lf) registerFilterFunction :: StackIndex -> Lua LuaFilterFunction registerFilterFunction idx = do - isFn <- isfunction idx - unless isFn . throwLuaError $ "Not a function at index " ++ show idx - pushvalue idx - refIdx <- ref registryindex + isFn <- Lua.isfunction idx + unless isFn . Lua.throwLuaError $ "Not a function at index " ++ show idx + Lua.pushvalue idx + refIdx <- Lua.ref Lua.registryindex return $ LuaFilterFunction refIdx instance ToLuaStack LuaFilterFunction where -- cgit v1.2.3 From 41baaff32737e57dd9ec0a1153416ca24a12dca1 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Tue, 22 Aug 2017 23:12:39 +0200 Subject: Text.Pandoc.Lua: support Inline and Block catch-alls Try function `Inline`/`Block` if no other filter function of the respective type matches an element. Closes: #3859 --- src/Text/Pandoc/Lua.hs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index db028d325..6c6676e4f 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -35,7 +35,7 @@ module Text.Pandoc.Lua (LuaException (..), pushPandocModule, runLuaFilter) where import Control.Monad (mplus, unless, when, (>=>)) import Control.Monad.Trans (MonadIO (..)) import Data.Data (DataType, Data, toConstr, showConstr, dataTypeOf, - dataTypeConstrs) + dataTypeConstrs, dataTypeName) import Data.Foldable (foldrM) import Data.Map (Map) import Data.Maybe (isJust) @@ -108,10 +108,10 @@ constructorsFor :: DataType -> [String] constructorsFor x = map show (dataTypeConstrs x) inlineFilterNames :: [String] -inlineFilterNames = constructorsFor (dataTypeOf (Str [])) +inlineFilterNames = "Inline" : constructorsFor (dataTypeOf (Str [])) blockFilterNames :: [String] -blockFilterNames = constructorsFor (dataTypeOf (Para [])) +blockFilterNames = "Block" : constructorsFor (dataTypeOf (Para [])) metaFilterName :: String metaFilterName = "Meta" @@ -126,10 +126,12 @@ newtype LuaFilterFunction = LuaFilterFunction { functionIndex :: Int } -- | Try running a filter for the given element tryFilter :: (Data a, FromLuaStack a, ToLuaStack a) => FunctionMap -> a -> Lua a tryFilter fnMap x = - let filterFnName = showConstr (toConstr x) in - case Map.lookup filterFnName fnMap of - Nothing -> return x + let filterFnName = showConstr (toConstr x) + catchAllName = dataTypeName (dataTypeOf x) + in + case Map.lookup filterFnName fnMap `mplus` Map.lookup catchAllName fnMap of Just fn -> runFilterFunction fn x + Nothing -> return x instance FromLuaStack LuaFilter where peek idx = -- cgit v1.2.3 From c7d4fd8cf13adb905a8185a7d2fb359e06481184 Mon Sep 17 00:00:00 2001 From: Alexander <ilabdsf@gmail.com> Date: Wed, 23 Aug 2017 02:34:19 +0300 Subject: Muse reader: do not allow closing tags with EOF (#3863) This behavior is compatible to Amusewiki --- src/Text/Pandoc/Readers/Muse.hs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 924149294..f64f9d04f 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -113,11 +113,10 @@ nested p = do htmlElement :: PandocMonad m => String -> MuseParser m (Attr, String) htmlElement tag = try $ do (TagOpen _ attr, _) <- htmlTag (~== TagOpen tag []) - content <- manyTill anyChar (endtag <|> endofinput) + content <- manyTill anyChar endtag return (htmlAttrToPandoc attr, content) where - endtag = void $ htmlTag (~== TagClose tag) - endofinput = lookAhead $ try $ skipMany blankline >> skipSpaces >> eof + endtag = void $ htmlTag (~== TagClose tag) htmlAttrToPandoc :: [Attribute String] -> Attr htmlAttrToPandoc attrs = (ident, classes, keyvals) -- cgit v1.2.3 From 5d74932578ddaa564eae88158bfb4c7f92dc7dd5 Mon Sep 17 00:00:00 2001 From: Alexander <ilabdsf@gmail.com> Date: Wed, 23 Aug 2017 09:12:34 +0300 Subject: Muse reader: avoid crashes on multiparagraph inline tags (#3866) Test checks that behavior is consistent with Amusewiki --- src/Text/Pandoc/Readers/Muse.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index f64f9d04f..74622a639 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -129,7 +129,7 @@ parseHtmlContentWithAttrs :: PandocMonad m => String -> MuseParser m a -> MuseParser m (Attr, [a]) parseHtmlContentWithAttrs tag parser = do (attr, content) <- htmlElement tag - parsedContent <- try $ parseContent (content ++ "\n") + parsedContent <- parseContent (content ++ "\n") return (attr, parsedContent) where parseContent = parseFromString $ nested $ manyTill parser endOfContent @@ -536,7 +536,7 @@ inlineTag :: PandocMonad m => (Inlines -> Inlines) -> String -> MuseParser m (F Inlines) -inlineTag f s = do +inlineTag f s = try $ do res <- parseHtmlContent s inline return $ f <$> mconcat res -- cgit v1.2.3 From f8dce4a9e3a51f77597da20892fad2ca79879005 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Wed, 23 Aug 2017 09:43:49 +0200 Subject: Text.Pandoc.Lua: fix fallback functions with GHC 7.8 --- src/Text/Pandoc/Lua.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 6c6676e4f..d6e5def4a 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -35,7 +35,7 @@ module Text.Pandoc.Lua (LuaException (..), pushPandocModule, runLuaFilter) where import Control.Monad (mplus, unless, when, (>=>)) import Control.Monad.Trans (MonadIO (..)) import Data.Data (DataType, Data, toConstr, showConstr, dataTypeOf, - dataTypeConstrs, dataTypeName) + dataTypeConstrs, dataTypeName, tyconUQname) import Data.Foldable (foldrM) import Data.Map (Map) import Data.Maybe (isJust) @@ -127,7 +127,7 @@ newtype LuaFilterFunction = LuaFilterFunction { functionIndex :: Int } tryFilter :: (Data a, FromLuaStack a, ToLuaStack a) => FunctionMap -> a -> Lua a tryFilter fnMap x = let filterFnName = showConstr (toConstr x) - catchAllName = dataTypeName (dataTypeOf x) + catchAllName = tyconUQname $ dataTypeName (dataTypeOf x) in case Map.lookup filterFnName fnMap `mplus` Map.lookup catchAllName fnMap of Just fn -> runFilterFunction fn x -- cgit v1.2.3 From c80e26f888bcc5bd59192e40f5332da73767762f Mon Sep 17 00:00:00 2001 From: bucklereed <horridimpfoobarbaz@chammy.info> Date: Thu, 24 Aug 2017 17:45:58 +0100 Subject: LaTeX reader: RN and Rn, from biblatex (#3854) --- src/Text/Pandoc/Readers/LaTeX.hs | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 3292550b2..ac471bdb1 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -44,7 +44,7 @@ import Control.Applicative (many, optional, (<|>)) import Control.Monad import Control.Monad.Except (throwError) import Control.Monad.Trans (lift) -import Data.Char (chr, isAlphaNum, isLetter, ord, isDigit) +import Data.Char (chr, isAlphaNum, isLetter, ord, isDigit, toLower) import Data.Default import Data.Text (Text) import qualified Data.Text as T @@ -1445,8 +1445,36 @@ inlineCommands = M.fromList $ , ("toggletrue", braced >>= setToggle True) , ("togglefalse", braced >>= setToggle False) , ("iftoggle", try $ ifToggle >> inline) + -- biblatex misc + , ("RN", romanNumeralUpper) + , ("Rn", romanNumeralLower) ] +romanNumeralUpper :: (PandocMonad m) => LP m Inlines +romanNumeralUpper = + str . toRomanNumeral <$> romanNumeralArg + +romanNumeralLower :: (PandocMonad m) => LP m Inlines +romanNumeralLower = + str . map toLower . toRomanNumeral <$> romanNumeralArg + +romanNumeralArg :: (PandocMonad m) => LP m Int +romanNumeralArg = spaces *> (parser <|> inBraces) + where + inBraces = do + symbol '{' + spaces + res <- parser + spaces + symbol '}' + return res + parser = do + Tok _ Word s <- satisfyTok isWordTok + let (digits, rest) = T.span isDigit s + unless (T.null rest) $ + fail "Non-digits in argument to \\Rn or \\RN" + safeRead $ T.unpack digits + newToggle :: (Monoid a, PandocMonad m) => [Tok] -> LP m a newToggle name = do updateState $ \st -> -- cgit v1.2.3 From e6f767b581742343bd7319bcc4c3632e18c58a70 Mon Sep 17 00:00:00 2001 From: Alexander <ilabdsf@gmail.com> Date: Fri, 25 Aug 2017 17:09:28 +0300 Subject: Muse reader: parse <verse> tag (#3872) --- src/Text/Pandoc/Readers/Muse.hs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 74622a639..77f75c8c6 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -32,7 +32,7 @@ TODO: - {{{ }}} syntax for <example> - Page breaks (five "*") - Headings with anchors (make it round trip with Muse writer) -- <verse> and ">" +- Verse markup (">") - Org tables - table.el tables - Images with attributes (floating and width) @@ -180,6 +180,7 @@ blockElements = choice [ comment , centerTag , rightTag , quoteTag + , verseTag , bulletList , orderedList , definitionList @@ -244,6 +245,25 @@ rightTag = blockTag id "right" quoteTag :: PandocMonad m => MuseParser m (F Blocks) quoteTag = withQuoteContext InDoubleQuote $ blockTag B.blockQuote "quote" +verseLine :: PandocMonad m => MuseParser m String +verseLine = do + line <- anyLine <|> many1Till anyChar eof + let (white, rest) = span (== ' ') line + return $ replicate (length white) '\160' ++ rest + +verseLines :: PandocMonad m => MuseParser m (F Blocks) +verseLines = do + optionMaybe blankline -- Skip blankline after opening tag on separate line + lns <- many verseLine + lns' <- mapM (parseFromString' (trimInlinesF . mconcat <$> many inline)) lns + return $ B.lineBlock <$> sequence lns' + +verseTag :: PandocMonad m => MuseParser m (F Blocks) +verseTag = do + (_, content) <- htmlElement "verse" + parsedContent <- parseFromString verseLines content + return parsedContent + commentTag :: PandocMonad m => MuseParser m (F Blocks) commentTag = parseHtmlContent "comment" anyChar >> return mempty -- cgit v1.2.3 From 1b3431a165309aad3a28a0e8a75755c299561280 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 25 Aug 2017 22:04:57 -0700 Subject: LaTeX reader: improved support for \hyperlink, \hypertarget. Closes #2549. --- src/Text/Pandoc/Readers/LaTeX.hs | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index ac471bdb1..06e112cef 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1391,11 +1391,8 @@ inlineCommands = M.fromList $ <|> citation "citeauthor" AuthorInText False) , ("nocite", mempty <$ (citation "nocite" NormalCitation False >>= addMeta "nocite")) - -- hyperlink: for now, we just preserve contents. - -- we might add the actual links, but we need to avoid clashes - -- with ids produced by label. - , ("hypertarget", braced >> tok) - , ("hyperlink", braced >> tok) + , ("hyperlink", hyperlink) + , ("hypertarget", hypertargetInline) -- glossaries package , ("gls", doAcronym "short") , ("Gls", doAcronym "short") @@ -1450,6 +1447,26 @@ inlineCommands = M.fromList $ , ("Rn", romanNumeralLower) ] +hyperlink :: PandocMonad m => LP m Inlines +hyperlink = try $ do + src <- toksToString <$> braced + lab <- tok + return $ link ('#':src) "" lab + +hypertargetBlock :: PandocMonad m => LP m Blocks +hypertargetBlock = try $ do + ref <- toksToString <$> braced + bs <- grouped block + case toList bs of + [Header 1 (ident,_,_) _] | ident == ref -> return bs + _ -> return $ divWith (ref, [], []) bs + +hypertargetInline :: PandocMonad m => LP m Inlines +hypertargetInline = try $ do + ref <- toksToString <$> braced + ils <- grouped inline + return $ spanWith (ref, [], []) ils + romanNumeralUpper :: (PandocMonad m) => LP m Inlines romanNumeralUpper = str . toRomanNumeral <$> romanNumeralArg @@ -1972,7 +1989,7 @@ blockCommands = M.fromList $ , ("setdefaultlanguage", setDefaultLanguage) , ("setmainlanguage", setDefaultLanguage) -- hyperlink - , ("hypertarget", try $ braced >> grouped block) + , ("hypertarget", hypertargetBlock) -- LaTeX colors , ("textcolor", coloredBlock "color") , ("colorbox", coloredBlock "background-color") -- cgit v1.2.3 From 64376955745cf4fd407947eb8022460bf498176b Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 26 Aug 2017 21:30:00 -0700 Subject: Markdown writer: don't crash on Str "". --- 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 95977ce17..523dfeaed 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -931,7 +931,7 @@ avoidBadWrapsInList (s:Str cs:[]) avoidBadWrapsInList (x:xs) = x : avoidBadWrapsInList xs isOrderedListMarker :: String -> Bool -isOrderedListMarker xs = (last xs `elem` ['.',')']) && +isOrderedListMarker xs = not (null xs) && (last xs `elem` ['.',')']) && isRight (runParser (anyOrderedListMarker >> eof) defaultParserState "" xs) -- cgit v1.2.3 From 8fcf66453cc4f9d1cf9413aa466477e56290d733 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 27 Aug 2017 17:01:24 -0700 Subject: RST reader: Fixed `..include::` directive. Closes #3880. --- src/Text/Pandoc/Readers/RST.hs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 190b065fb..daaeff2f0 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -219,7 +219,6 @@ block = choice [ codeBlock , directive , anchor , comment - , include , header , hrule , lineBlock -- must go before definitionList @@ -460,16 +459,16 @@ tab-width encoding -} -include :: PandocMonad m => RSTParser m Blocks -include = try $ do - string ".. include::" - skipMany spaceChar - f <- trim <$> anyLine - fields <- many $ rawFieldListItem 3 +includeDirective :: PandocMonad m + => String -> [(String, String)] -> String + -> RSTParser m Blocks +includeDirective top fields body = do + let f = trim top + guard $ not (null f) + guard $ null (trim body) -- 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 containers <- stateContainers <$> getState @@ -501,7 +500,7 @@ include = try $ do Just patt -> drop 1 . dropWhile (not . (patt `isInfixOf`)) Nothing -> id) $ contentLines' - let contents' = unlines contentLines'' + let contents' = unlines contentLines'' ++ "\n" case lookup "code" fields of Just lang -> do let numberLines = lookup "number-lines" fields @@ -687,6 +686,7 @@ directive' = do $ lookup "height" fields >>= (lengthToDim . filter (not . isSpace)) case label of + "include" -> includeDirective top fields body' "table" -> tableDirective top fields body' "list-table" -> listTableDirective top fields body' "csv-table" -> csvTableDirective top fields body' -- cgit v1.2.3 From 05bb8ef4aa6faa6a4da3c54a0483d42b846733ca Mon Sep 17 00:00:00 2001 From: Alexander <ilabdsf@gmail.com> Date: Mon, 28 Aug 2017 17:48:46 +0300 Subject: RST reader: handle blank lines correctly in line blocks (#3881) Previously pandoc would sometimes combine two line blocks separated by blanks, and ignore trailing blank lines within the line block. Test is checked to be consisted with http://rst.ninjs.org/ --- src/Text/Pandoc/Parsing.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 9ed18d4e0..2543f11f0 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -838,7 +838,7 @@ blankLineBlockLine = try (char '|' >> blankline) lineBlockLines :: Monad m => ParserT [Char] st m [String] lineBlockLines = try $ do lines' <- many1 (lineBlockLine <|> ((:[]) <$> blankLineBlockLine)) - skipMany1 $ blankline <|> blankLineBlockLine + skipMany $ blankline return lines' -- | Parse a table using 'headerParser', 'rowParser', -- cgit v1.2.3 From 2e26046e1334d85efab9cfc2775cf59a66e8b459 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 28 Aug 2017 23:33:21 -0700 Subject: HTML writer: ensure we don't get two style attributes for width & height. --- src/Text/Pandoc/Writers/HTML.hs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 9ac37a0ba..87f61126b 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -47,7 +47,7 @@ import Control.Monad.State.Strict import Data.Char (ord, toLower) import Data.Text (Text) import qualified Data.Text.Lazy as TL -import Data.List (intersperse, isPrefixOf) +import Data.List (intersperse, isPrefixOf, partition, intercalate) import Data.Maybe (catMaybes, fromMaybe, isJust, isNothing) import Data.Monoid ((<>)) import qualified Data.Set as Set @@ -569,8 +569,14 @@ imgAttrsToHtml opts attr = do isNotDim _ = True dimensionsToAttrList :: Attr -> [(String, String)] -dimensionsToAttrList attr = (go Width) ++ (go Height) +dimensionsToAttrList attr = consolidateStyles $ go Width ++ go Height where + consolidateStyles xs = + case partition isStyle xs of + ([], _) -> xs + (ss, rest) -> ("style", intercalate ";" $ map snd ss) : rest + isStyle ("style", _) = True + isStyle _ = False go dir = case (dimension dir attr) of (Just (Pixel a)) -> [(show dir, show a)] (Just x) -> [("style", show dir ++ ":" ++ show x)] -- cgit v1.2.3 From 22a4adf4ec172545fb1ed72bb85c30dc1186de62 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 29 Aug 2017 09:04:59 -0700 Subject: Add a type sig to satisfy ghc 7.10.3. --- src/Text/Pandoc/Writers/HTML.hs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 87f61126b..1641b991c 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -571,6 +571,7 @@ imgAttrsToHtml opts attr = do dimensionsToAttrList :: Attr -> [(String, String)] dimensionsToAttrList attr = consolidateStyles $ go Width ++ go Height where + consolidateStyles :: [(String, String)] -> [(String, String)] consolidateStyles xs = case partition isStyle xs of ([], _) -> xs -- cgit v1.2.3 From 2d936ff4e08c8b77f2dac0b278b85bb7f66658af Mon Sep 17 00:00:00 2001 From: Alexander <ilabdsf@gmail.com> Date: Tue, 29 Aug 2017 19:15:06 +0300 Subject: hlint Muse reader (#3884) --- src/Text/Pandoc/Readers/Muse.hs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 77f75c8c6..2947c50d6 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -261,8 +261,7 @@ verseLines = do verseTag :: PandocMonad m => MuseParser m (F Blocks) verseTag = do (_, content) <- htmlElement "verse" - parsedContent <- parseFromString verseLines content - return parsedContent + parseFromString verseLines content commentTag :: PandocMonad m => MuseParser m (F Blocks) commentTag = parseHtmlContent "comment" anyChar >> return mempty @@ -379,8 +378,8 @@ definitionListItem = try $ do pure $ do lineContent' <- lineContent pure (B.text term, [lineContent']) where - termParser = (many1 spaceChar) >> -- Initial space as required by Amusewiki, but not Emacs Muse - (many1Till anyChar $ lookAhead (void (try (spaceChar >> string "::")) <|> void newline)) + termParser = many1 spaceChar >> -- Initial space as required by Amusewiki, but not Emacs Muse + many1Till anyChar (lookAhead (void (try (spaceChar >> string "::")) <|> void newline)) endOfInput = try $ skipMany blankline >> skipSpaces >> eof twoBlankLines = try $ blankline >> skipMany1 blankline newDefinitionListItem = try $ void termParser -- cgit v1.2.3 From 14f813c3f294739f3965058e27eb228ab3ed90d5 Mon Sep 17 00:00:00 2001 From: Alexander <ilabdsf@gmail.com> Date: Tue, 29 Aug 2017 22:40:34 +0300 Subject: Muse reader: parse verse markup (#3882) --- src/Text/Pandoc/Readers/Muse.hs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 2947c50d6..a4512cdd7 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -32,7 +32,6 @@ TODO: - {{{ }}} syntax for <example> - Page breaks (five "*") - Headings with anchors (make it round trip with Muse writer) -- Verse markup (">") - Org tables - table.el tables - Images with attributes (floating and width) @@ -181,6 +180,7 @@ blockElements = choice [ comment , rightTag , quoteTag , verseTag + , lineBlock , bulletList , orderedList , definitionList @@ -298,6 +298,26 @@ noteBlock = try $ do blocksTillNote = many1Till block (eof <|> () <$ lookAhead noteMarker) +-- +-- Verse markup +-- + +lineVerseLine :: PandocMonad m => MuseParser m String +lineVerseLine = try $ do + char '>' + white <- many1 (char ' ' >> pure '\160') + rest <- anyLine + return $ tail white ++ rest + +blanklineVerseLine :: PandocMonad m => MuseParser m Char +blanklineVerseLine = try $ char '>' >> blankline + +lineBlock :: PandocMonad m => MuseParser m (F Blocks) +lineBlock = try $ do + lns <- many1 (pure <$> blanklineVerseLine <|> lineVerseLine) + lns' <- mapM (parseFromString' (trimInlinesF . mconcat <$> many inline)) lns + return $ B.lineBlock <$> sequence lns' + -- -- lists -- -- cgit v1.2.3 From 50ec64ffbc56db2c2312feb606df4bc36142b3f0 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 30 Aug 2017 17:05:12 -0700 Subject: HTML reader: improved handling of figure. Previously we had a parse failure if the figure contained anything besides an image and caption. --- src/Text/Pandoc/Readers/HTML.hs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index d85488478..257c16735 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -58,7 +58,7 @@ import Data.Maybe ( fromMaybe, isJust, isNothing ) import Data.List.Split ( wordsBy ) import Data.List ( intercalate, isPrefixOf ) import Data.Char ( isDigit, isLetter, isAlphaNum ) -import Control.Monad ( guard, mzero, void, unless, mplus ) +import Control.Monad ( guard, mzero, void, unless, mplus, msum ) import Control.Arrow ((***)) import Control.Applicative ( (<|>) ) import Data.Monoid (First (..)) @@ -576,23 +576,23 @@ pPara = do return $ B.para contents pFigure :: PandocMonad m => TagParser m Blocks -pFigure = do +pFigure = try $ do TagOpen _ _ <- pSatisfy (matchTagOpen "figure" []) skipMany pBlank - let pImg = pOptInTag "p" pImage <* skipMany pBlank - pCapt = option mempty $ pInTags "figcaption" inline <* skipMany pBlank - pImgCapt = do - img <- pImg - cap <- pCapt - return (img, cap) - pCaptImg = do - cap <- pCapt - img <- pImg - return (img, cap) - (imgMany, caption) <- pImgCapt <|> pCaptImg + let pImg = (\x -> (Just x, Nothing)) <$> + (pOptInTag "p" pImage <* skipMany pBlank) + pCapt = (\x -> (Nothing, Just x)) <$> + (pInTags "figcaption" inline <* skipMany pBlank) + pSkip = (Nothing, Nothing) <$ pSatisfy (not . matchTagClose "figure") + res <- many (pImg <|> pCapt <|> pSkip) + let mbimg = msum $ map fst res + let mbcap = msum $ map snd res TagClose _ <- pSatisfy (matchTagClose "figure") - let (Image attr _ (url, tit)):_ = B.toList imgMany - return $ B.para $ B.imageWith attr url ("fig:" ++ tit) caption + let caption = fromMaybe mempty mbcap + case B.toList <$> mbimg of + Just [Image attr _ (url, tit)] -> + return $ B.para $ B.imageWith attr url ("fig:" ++ tit) caption + Nothing -> mzero pCodeBlock :: PandocMonad m => TagParser m Blocks pCodeBlock = try $ do @@ -961,7 +961,7 @@ blockHtmlTags = Set.fromList "dir", "div", "dl", "dt", "fieldset", "figcaption", "figure", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html", - "isindex", "main", "menu", "noframes", "ol", "output", "p", "pre", + "isindex", "main", "menu", "meta", "noframes", "ol", "output", "p", "pre", "section", "table", "tbody", "textarea", "thead", "tfoot", "ul", "dd", "dt", "frameset", "li", "tbody", "td", "tfoot", @@ -1048,7 +1048,7 @@ x `closes` "p" | x `elem` ["address", "article", "aside", "blockquote", "dir", "div", "dl", "fieldset", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "header", "hr", "main", "menu", "nav", "ol", "p", "pre", "section", "table", "ul"] = True -"meta" `closes` "meta" = True +_ `closes` "meta" = True "form" `closes` "form" = True "label" `closes` "label" = True "map" `closes` "map" = True -- cgit v1.2.3 From 6a6c3858b47671f02f4f50ca2d6ab97d280a0f49 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Thu, 31 Aug 2017 18:02:07 +0200 Subject: Org writer: stop using raw HTML to wrap divs Div's are difficult to translate into org syntax, as there are multiple div-like structures (drawers, special blocks, greater blocks) which all have their advantages and disadvantages. Previously pandoc would use raw HTML to preserve the full div information; this was rarely useful and resulted in visual clutter. Div-rendering was changed to discard the div's classes and key-value pairs if there is no natural way to translate the div into an org structure. Closes: #3771 --- src/Text/Pandoc/Writers/Org.hs | 63 +++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 37 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index 48f17c4fb..88f42acd4 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -129,36 +129,25 @@ blockToOrg (Div (_,classes@(cls:_),kvs) bs) | "drawer" `elem` classes = do blankline $$ contents $$ blankline $$ drawerEndTag $$ blankline -blockToOrg (Div attrs bs) = do +blockToOrg (Div (ident, classes, kv) bs) = do contents <- blockListToOrg bs + -- if one class looks like the name of a greater block then output as such: + -- The ID, if present, is added via the #+NAME keyword; other classes and + -- key-value pairs are kept as #+ATTR_HTML attributes. let isGreaterBlockClass = (`elem` ["center", "quote"]) . map toLower - return $ case attrs of - ("", [], []) -> - -- nullAttr, treat contents as if it wasn't wrapped - blankline $$ contents $$ blankline - (ident, [], []) -> - -- only an id: add id as an anchor, unwrap the rest - blankline $$ "<<" <> text ident <> ">>" $$ contents $$ blankline - (ident, classes, kv) -> - -- if one class looks like the name of a greater block then output as - -- such: The ID, if present, is added via the #+NAME keyword; other - -- classes and key-value pairs are kept as #+ATTR_HTML attributes. - let - (blockTypeCand, classes') = partition isGreaterBlockClass classes - in case blockTypeCand of - (blockType:classes'') -> - blankline $$ attrHtml (ident, classes'' <> classes', kv) $$ - "#+BEGIN_" <> text blockType $$ contents $$ - "#+END_" <> text blockType $$ blankline - _ -> - -- fallback: wrap in div tags - let - startTag = tagWithAttrs "div" attrs - endTag = text "</div>" - in blankline $$ "#+BEGIN_HTML" $$ - nest 2 startTag $$ "#+END_HTML" $$ blankline $$ - contents $$ blankline $$ "#+BEGIN_HTML" $$ - nest 2 endTag $$ "#+END_HTML" $$ blankline + (blockTypeCand, classes') = partition isGreaterBlockClass classes + return $ case blockTypeCand of + (blockType:classes'') -> + blankline $$ attrHtml (ident, classes'' <> classes', kv) $$ + "#+BEGIN_" <> text blockType $$ contents $$ + "#+END_" <> text blockType $$ blankline + _ -> + -- fallback with id: add id as an anchor if present, discard classes and + -- key-value pairs, unwrap the content. + let contents' = if not (null ident) + then "<<" <> text ident <> ">>" $$ contents + else contents + in blankline $$ contents' $$ blankline blockToOrg (Plain inlines) = inlineListToOrg inlines -- title beginning with fig: indicates that the image is a figure blockToOrg (Para [Image attr txt (src,'f':'i':'g':':':tit)]) = do @@ -173,7 +162,7 @@ blockToOrg (Para inlines) = do blockToOrg (LineBlock lns) = do let splitStanza [] = [] splitStanza xs = case break (== mempty) xs of - (l, []) -> l : [] + (l, []) -> [l] (l, _:r) -> l : splitStanza r let joinWithLinefeeds = nowrap . mconcat . intersperse cr let joinWithBlankLines = mconcat . intersperse blankline @@ -213,7 +202,7 @@ blockToOrg (Table caption' _ _ headers rows) = do caption'' <- inlineListToOrg caption' let caption = if null caption' then empty - else ("#+CAPTION: " <> caption'') + else "#+CAPTION: " <> caption'' headers' <- mapM blockListToOrg headers rawRows <- mapM (mapM blockListToOrg) rows let numChars = maximum . map offset @@ -289,8 +278,8 @@ propertiesDrawer (ident, classes, kv) = let drawerStart = text ":PROPERTIES:" drawerEnd = text ":END:" - kv' = if (classes == mempty) then kv else ("CLASS", unwords classes):kv - kv'' = if (ident == mempty) then kv' else ("CUSTOM_ID", ident):kv' + kv' = if classes == mempty then kv else ("CLASS", unwords classes):kv + kv'' = if ident == mempty then kv' else ("CUSTOM_ID", ident):kv' properties = vcat $ map kvToOrgProperty kv'' in drawerStart <> cr <> properties <> cr <> drawerEnd @@ -303,7 +292,7 @@ attrHtml :: Attr -> Doc attrHtml ("" , [] , []) = mempty attrHtml (ident, classes, kvs) = let - name = if (null ident) then mempty else "#+NAME: " <> text ident <> cr + name = if null ident then mempty else "#+NAME: " <> text ident <> cr keyword = "#+ATTR_HTML" classKv = ("class", unwords classes) kvStrings = map (\(k,v) -> ":" <> k <> " " <> v) (classKv:kvs) @@ -370,19 +359,19 @@ inlineToOrg SoftBreak = do WrapPreserve -> return cr WrapAuto -> return space WrapNone -> return space -inlineToOrg (Link _ txt (src, _)) = do +inlineToOrg (Link _ txt (src, _)) = case txt of [Str x] | escapeURI x == src -> -- autolink - do return $ "[[" <> text (orgPath x) <> "]]" + return $ "[[" <> text (orgPath x) <> "]]" _ -> do contents <- inlineListToOrg txt return $ "[[" <> text (orgPath src) <> "][" <> contents <> "]]" -inlineToOrg (Image _ _ (source, _)) = do +inlineToOrg (Image _ _ (source, _)) = return $ "[[" <> text (orgPath source) <> "]]" inlineToOrg (Note contents) = do -- add to notes in state notes <- gets stNotes modify $ \st -> st { stNotes = contents:notes } - let ref = show $ (length notes) + 1 + let ref = show $ length notes + 1 return $ "[fn:" <> text ref <> "]" orgPath :: String -> String -- cgit v1.2.3 From 1d0805ce414398d5ff70c9ed3dbe1288356c7dd9 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 4 Sep 2017 18:11:26 -0700 Subject: HTML reader: Fix pattern match. --- src/Text/Pandoc/Readers/HTML.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 257c16735..2093be19c 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -592,7 +592,7 @@ pFigure = try $ do case B.toList <$> mbimg of Just [Image attr _ (url, tit)] -> return $ B.para $ B.imageWith attr url ("fig:" ++ tit) caption - Nothing -> mzero + _ -> mzero pCodeBlock :: PandocMonad m => TagParser m Blocks pCodeBlock = try $ do -- cgit v1.2.3 From c09b586147d607f645a639a47c7781e8d8655e20 Mon Sep 17 00:00:00 2001 From: Alexander <ilabdsf@gmail.com> Date: Tue, 5 Sep 2017 07:22:40 +0300 Subject: Muse reader: parse <div> tag (#3888) --- src/Text/Pandoc/Readers/Muse.hs | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index a4512cdd7..1951a47af 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -179,6 +179,7 @@ blockElements = choice [ comment , centerTag , rightTag , quoteTag + , divTag , verseTag , lineBlock , bulletList @@ -245,6 +246,12 @@ rightTag = blockTag id "right" quoteTag :: PandocMonad m => MuseParser m (F Blocks) quoteTag = withQuoteContext InDoubleQuote $ blockTag B.blockQuote "quote" +-- <div> tag is supported by Emacs Muse, but not Amusewiki 2.025 +divTag :: PandocMonad m => MuseParser m (F Blocks) +divTag = do + (attrs, content) <- parseHtmlContentWithAttrs "div" block + return $ (B.divWith attrs) <$> mconcat content + verseLine :: PandocMonad m => MuseParser m String verseLine = do line <- anyLine <|> many1Till anyChar eof -- cgit v1.2.3 From 9fdc089cd85e46148720f368644e8badd168f5b8 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 4 Sep 2017 21:56:06 -0700 Subject: Plain writer: don't use   to separate list and indented code. There's no need for it in this context, since this isn't to be interpreted using Markdown rules. --- src/Text/Pandoc/Writers/Markdown.hs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 523dfeaed..9d6064af6 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -787,6 +787,7 @@ blockListToMarkdown :: PandocMonad m -> MD m Doc blockListToMarkdown opts blocks = do inlist <- asks envInList + isPlain <- asks envPlain -- a) insert comment between list and indented code block, or the -- code block will be treated as a list continuation paragraph -- b) change Plain to Para unless it's followed by a RawBlock @@ -813,9 +814,11 @@ blockListToMarkdown opts blocks = do isListBlock (OrderedList _ _) = True isListBlock (DefinitionList _) = True isListBlock _ = False - commentSep = if isEnabled Ext_raw_html opts - then RawBlock "html" "<!-- -->\n" - else RawBlock "markdown" " \n" + commentSep = if isPlain + then Null + else if isEnabled Ext_raw_html opts + then RawBlock "html" "<!-- -->\n" + else RawBlock "markdown" " \n" mapM (blockToMarkdown opts) (fixBlocks blocks) >>= return . cat getKey :: Doc -> Key -- cgit v1.2.3 From 350c282f205f48c6d0f7a96bf349b585a16fbcf4 Mon Sep 17 00:00:00 2001 From: Alexander <ilabdsf@gmail.com> Date: Tue, 5 Sep 2017 19:41:27 +0300 Subject: Muse reader: require at least one space char after * in header (#3895) --- src/Text/Pandoc/Readers/Muse.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 1951a47af..63bdfcba7 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -213,7 +213,7 @@ header = try $ do getPosition >>= \pos -> guard (st == NullState && q == NoQuote && sourceColumn pos == 1) level <- liftM length $ many1 $ char '*' guard $ level <= 5 - skipSpaces + spaceChar content <- trimInlinesF . mconcat <$> manyTill inline newline attr <- registerHeader ("", [], []) (runF content defaultParserState) return $ B.headerWith attr level <$> content -- cgit v1.2.3 From 146a10780e05006f97cd4ba3a0dd02b903533db6 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 5 Sep 2017 09:55:42 -0700 Subject: LaTeX reader: support `\k` ogonek accent. --- src/Text/Pandoc/Readers/LaTeX.hs | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 06e112cef..7b7ac1c01 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -961,6 +961,10 @@ hacek 'Z' = "Ž" hacek 'z' = "ž" hacek c = [c] +ogonek :: Char -> String +ogonek 'a' = "ą" +ogonek c = [c] + breve :: Char -> String breve 'A' = "Ă" breve 'a' = "ă" @@ -1286,6 +1290,7 @@ inlineCommands = M.fromList $ , ("c", option (str "c") $ try $ tok >>= accent cedilla) , ("v", option (str "v") $ try $ tok >>= accent hacek) , ("u", option (str "u") $ try $ tok >>= accent breve) + , ("k", option (str "k") $ try $ tok >>= accent ogonek) , ("i", lit "i") , ("\\", linebreak <$ (do inTableCell <- sInTableCell <$> getState guard $ not inTableCell -- cgit v1.2.3 From d62c4a92470de3b0aa73ddb3fe921a1f9b154b41 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 5 Sep 2017 10:58:34 -0700 Subject: LaTeX reader: Improve handling of accents. Handle ogonek, and fall back correctly with forms like `\"{}`. --- src/Text/Pandoc/Readers/LaTeX.hs | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 7b7ac1c01..b6b53e1fc 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -770,11 +770,13 @@ keyval = try $ do keyvals :: PandocMonad m => LP m [(String, String)] keyvals = try $ symbol '[' >> manyTill keyval (symbol ']') -accent :: (Char -> String) -> Inlines -> LP m Inlines -accent f ils = +accent :: PandocMonad m => Char -> (Char -> String) -> LP m Inlines +accent c f = try $ do + ils <- tok case toList ils of (Str (x:xs) : ys) -> return $ fromList (Str (f x ++ xs) : ys) - [] -> mzero + [Space] -> return $ str [c] + [] -> return $ str [c] _ -> return ils grave :: Char -> String @@ -1279,18 +1281,18 @@ inlineCommands = M.fromList $ , ("copyright", lit "©") , ("textasciicircum", lit "^") , ("textasciitilde", lit "~") - , ("H", try $ tok >>= accent hungarumlaut) - , ("`", option (str "`") $ try $ tok >>= accent grave) - , ("'", option (str "'") $ try $ tok >>= accent acute) - , ("^", option (str "^") $ try $ tok >>= accent circ) - , ("~", option (str "~") $ try $ tok >>= accent tilde) - , ("\"", option (str "\"") $ try $ tok >>= accent umlaut) - , (".", option (str ".") $ try $ tok >>= accent dot) - , ("=", option (str "=") $ try $ tok >>= accent macron) - , ("c", option (str "c") $ try $ tok >>= accent cedilla) - , ("v", option (str "v") $ try $ tok >>= accent hacek) - , ("u", option (str "u") $ try $ tok >>= accent breve) - , ("k", option (str "k") $ try $ tok >>= accent ogonek) + , ("H", accent '\779' hungarumlaut) + , ("`", accent '`' grave) + , ("'", accent '\'' acute) + , ("^", accent '^' circ) + , ("~", accent '~' tilde) + , ("\"", accent '\776' umlaut) + , (".", accent '\775' dot) + , ("=", accent '\772' macron) + , ("c", accent '\807' cedilla) + , ("v", accent 'ˇ' hacek) + , ("u", accent '\774' breve) + , ("k", accent '\808' ogonek) , ("i", lit "i") , ("\\", linebreak <$ (do inTableCell <- sInTableCell <$> getState guard $ not inTableCell -- cgit v1.2.3 From bc5624dac2da7674f42838db8672520e8949c9db Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 5 Sep 2017 13:46:44 -0700 Subject: Markdown writer: make Span with null attribute transparent. That is, we don't use brackets or `<span>` tags to mark spans when there are no attributes; we simply output the contents. --- src/Text/Pandoc/Writers/Markdown.hs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 9d6064af6..3dbfe3f11 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -949,11 +949,10 @@ inlineToMarkdown opts (Span attrs ils) = do contents <- inlineListToMarkdown opts ils return $ case plain of True -> contents - False | isEnabled Ext_bracketed_spans opts -> + False | attrs == nullAttr -> contents + | isEnabled Ext_bracketed_spans opts -> "[" <> contents <> "]" <> - if attrs == nullAttr - then "{}" - else linkAttributes opts attrs + linkAttributes opts attrs | isEnabled Ext_raw_html opts || isEnabled Ext_native_spans opts -> tagWithAttrs "span" attrs <> contents <> text "</span>" -- cgit v1.2.3 From 0b05222a9c915d2062e416d177d36af4b474e0c2 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 5 Sep 2017 13:54:44 -0700 Subject: LaTeX reader: Better support for ogonek accents. --- src/Text/Pandoc/Readers/LaTeX.hs | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index b6b53e1fc..d0e95bd85 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -965,6 +965,15 @@ hacek c = [c] ogonek :: Char -> String ogonek 'a' = "ą" +ogonek 'e' = "ę" +ogonek 'o' = "ǫ" +ogonek 'i' = "į" +ogonek 'u' = "ų" +ogonek 'A' = "Ą" +ogonek 'E' = "Ę" +ogonek 'I' = "Į" +ogonek 'O' = "Ǫ" +ogonek 'U' = "Ų" ogonek c = [c] breve :: Char -> String @@ -1293,6 +1302,7 @@ inlineCommands = M.fromList $ , ("v", accent 'ˇ' hacek) , ("u", accent '\774' breve) , ("k", accent '\808' ogonek) + , ("textogonekcentered", accent '\808' ogonek) , ("i", lit "i") , ("\\", linebreak <$ (do inTableCell <- sInTableCell <$> getState guard $ not inTableCell -- cgit v1.2.3 From 743413a5b506351499fa2fb66d4184d74e125c54 Mon Sep 17 00:00:00 2001 From: Alexander <ilabdsf@gmail.com> Date: Wed, 6 Sep 2017 18:48:06 +0300 Subject: Muse reader: Allow finishing header with EOF (#3897) --- src/Text/Pandoc/Readers/Muse.hs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 63bdfcba7..2454057fa 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE FlexibleContexts #-} {- Copyright (C) 2017 Alexander Krotov <ilabdsf@gmail.com> @@ -100,6 +101,9 @@ parseBlocks = do -- utility functions -- +eol :: Stream s m Char => ParserT s st m () +eol = void newline <|> eof + nested :: PandocMonad m => MuseParser m a -> MuseParser m a nested p = do nestlevel <- stateMaxNestingLevel <$> getState @@ -195,7 +199,7 @@ comment = try $ do char ';' space many $ noneOf "\n" - void newline <|> eof + eol return mempty separator :: PandocMonad m => MuseParser m (F Blocks) @@ -203,7 +207,7 @@ separator = try $ do string "----" many $ char '-' many spaceChar - void newline <|> eof + eol return $ return B.horizontalRule header :: PandocMonad m => MuseParser m (F Blocks) @@ -214,7 +218,7 @@ header = try $ do level <- liftM length $ many1 $ char '*' guard $ level <= 5 spaceChar - content <- trimInlinesF . mconcat <$> manyTill inline newline + content <- trimInlinesF . mconcat <$> manyTill inline eol attr <- registerHeader ("", [], []) (runF content defaultParserState) return $ B.headerWith attr level <$> content @@ -464,10 +468,10 @@ museAppendElement tbl element = tableCell :: PandocMonad m => MuseParser m (F Blocks) tableCell = try $ liftM B.plain . trimInlinesF . mconcat <$> manyTill inline (lookAhead cellEnd) - where cellEnd = try $ void (many1 spaceChar >> char '|') <|> void newline <|> eof + where cellEnd = try $ void (many1 spaceChar >> char '|') <|> eol tableElements :: PandocMonad m => MuseParser m [MuseTableElement] -tableElements = tableParseElement `sepEndBy1` (void newline <|> eof) +tableElements = tableParseElement `sepEndBy1` eol elementsToTable :: [MuseTableElement] -> F MuseTable elementsToTable = foldM museAppendElement emptyTable -- cgit v1.2.3 From a90f131937460f21005a997e0a1f10b930b203b2 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 7 Sep 2017 22:05:22 -0700 Subject: LaTeX writer: use proper code for list enumerators. This should fix problems with lists that don't use arabic numerals. Closes #3891. --- src/Text/Pandoc/Writers/LaTeX.hs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 4a81cd245..2da087077 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -628,6 +628,7 @@ blockToLaTeX (OrderedList (start, numstyle, numdelim) lst) = do put $ st {stOLLevel = oldlevel + 1} items <- mapM listItemToLaTeX lst modify (\s -> s {stOLLevel = oldlevel}) + let beamer = stBeamer st let tostyle x = case numstyle of Decimal -> "\\arabic" <> braces x UpperRoman -> "\\Roman" <> braces x @@ -641,11 +642,21 @@ blockToLaTeX (OrderedList (start, numstyle, numdelim) lst) = do TwoParens -> parens x Period -> x <> "." _ -> x <> "." + let exemplar = case numstyle of + Decimal -> "1" + UpperRoman -> "I" + LowerRoman -> "i" + UpperAlpha -> "A" + LowerAlpha -> "a" + Example -> "1" + DefaultStyle -> "1" let enum = text $ "enum" ++ map toLower (toRomanNumeral oldlevel) let stylecommand = if numstyle == DefaultStyle && numdelim == DefaultDelim then empty - else "\\def" <> "\\label" <> enum <> - braces (todelim $ tostyle enum) + else if beamer + then brackets (todelim exemplar) + else "\\def" <> "\\label" <> enum <> + braces (todelim $ tostyle enum) let resetcounter = if start == 1 || oldlevel > 4 then empty else "\\setcounter" <> braces enum <> -- cgit v1.2.3 From 5fc4980216bf0a6b425827417655991859cba5ec Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 7 Sep 2017 22:10:13 -0700 Subject: Markdown writer: Escape pipe characters when `pipe_tables` enabled. Closes #3887. --- src/Text/Pandoc/Writers/Markdown.hs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 3dbfe3f11..0221ba6ef 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -288,6 +288,7 @@ escapeString opts (c:cs) = | otherwise -> ">" ++ escapeString opts cs _ | c `elem` ['\\','`','*','_','[',']','#'] -> '\\':c:escapeString opts cs + '|' | isEnabled Ext_pipe_tables opts -> '\\':'|':escapeString opts cs '^' | isEnabled Ext_superscript opts -> '\\':'^':escapeString opts cs '~' | isEnabled Ext_subscript opts -> '\\':'~':escapeString opts cs '$' | isEnabled Ext_tex_math_dollars opts -> '\\':'$':escapeString opts cs -- cgit v1.2.3 From 3421f3eac71709566b9072e095048619cf813ace Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 8 Sep 2017 10:48:02 -0700 Subject: Removed old beamer template. We now use the default.latex template for both latex and beamer. It contains conditionals for the beamer-specific things. `pandoc -D beamer` will return this template. --- src/Text/Pandoc/Readers/LaTeX.hs | 1 + src/Text/Pandoc/Templates.hs | 1 + 2 files changed, 2 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index d0e95bd85..4bdf02734 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1224,6 +1224,7 @@ inlineCommands = M.fromList $ , ("textrm", extractSpaces (spanWith ("",["roman"],[])) <$> tok) , ("textup", extractSpaces (spanWith ("",["upright"],[])) <$> tok) , ("texttt", ttfamily) + , ("textenglish", spanWith ("",[],[("lang","en")]) <$> tok) , ("sout", extractSpaces strikeout <$> tok) , ("lq", return (str "‘")) , ("rq", return (str "’")) diff --git a/src/Text/Pandoc/Templates.hs b/src/Text/Pandoc/Templates.hs index d5a4faafa..7914c35f8 100644 --- a/src/Text/Pandoc/Templates.hs +++ b/src/Text/Pandoc/Templates.hs @@ -63,6 +63,7 @@ getDefaultTemplate writer = do "html" -> getDefaultTemplate "html5" "docbook" -> getDefaultTemplate "docbook5" "epub" -> getDefaultTemplate "epub3" + "beamer" -> getDefaultTemplate "latex" "markdown_strict" -> getDefaultTemplate "markdown" "multimarkdown" -> getDefaultTemplate "markdown" "markdown_github" -> getDefaultTemplate "markdown" -- cgit v1.2.3 From 621e43e0ec2d0ee81436c01256d994a514d9824c Mon Sep 17 00:00:00 2001 From: Andrew Dunning <andunning@gmail.com> Date: Fri, 8 Sep 2017 22:17:31 +0100 Subject: Write euro symbol directly in LaTeX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The textcomp package allows pdfLaTeX to parse `€` directly, making the \euro command unneeded. Closes #3801. --- src/Text/Pandoc/Writers/LaTeX.hs | 6 ------ 1 file changed, 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 2da087077..282910ee5 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -82,7 +82,6 @@ data WriterState = , stHighlighting :: Bool -- true if document has highlighted code , 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 , stEmptyLine :: Bool -- true if no content on line } @@ -111,7 +110,6 @@ startingState options = WriterState { , stHighlighting = False , stIncremental = writerIncremental options , stInternalLinks = [] - , stUsesEuro = False , stBeamer = False , stEmptyLine = True } @@ -233,7 +231,6 @@ pandocToLaTeX options (Pandoc meta blocks) = do defField "lhs" (stLHS st) $ defField "graphics" (stGraphics st) $ defField "book-class" (stBook st) $ - defField "euro" (stUsesEuro st) $ defField "listings" (writerListings options || stLHS st) $ defField "beamer" beamer $ (if stHighlighting st @@ -320,11 +317,8 @@ stringToLaTeX ctx (x:xs) = do rest <- stringToLaTeX ctx xs let ligatures = isEnabled Ext_smart opts && ctx == TextString let isUrl = ctx == URLString - when (x == '€') $ - modify $ \st -> st{ stUsesEuro = True } return $ case x of - '€' -> "\\euro{}" ++ rest '{' -> "\\{" ++ rest '}' -> "\\}" ++ rest '`' | ctx == CodeString -> "\\textasciigrave{}" ++ rest -- cgit v1.2.3 From 2230371304f299ca7333af8ad0ee7bcd099a4aa0 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sat, 9 Sep 2017 11:51:36 +0300 Subject: Muse reader: debug inline code markup --- src/Text/Pandoc/Readers/Muse.hs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 2454057fa..3b089772f 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -575,13 +575,6 @@ enclosedInlines :: (PandocMonad m, Show a, Show b) enclosedInlines start end = try $ trimInlinesF . mconcat <$> enclosed start end inline -verbatimBetween :: PandocMonad m - => Char - -> MuseParser m String -verbatimBetween c = try $ do - char c - many1Till anyChar $ char c - inlineTag :: PandocMonad m => (Inlines -> Inlines) -> String @@ -617,9 +610,13 @@ code = try $ do sp <- if sourceColumn pos == 1 then pure mempty else skipMany1 spaceChar >> pure B.space - cd <- verbatimBetween '=' + char '=' + contents <- many1Till (noneOf "\n\r" <|> (newline <* notFollowedBy newline)) $ char '=' + guard $ not $ null contents + guard $ head contents `notElem` " \t\n" + guard $ last contents `notElem` " \t\n" notFollowedBy nonspaceChar - return $ return (sp B.<> B.code cd) + return $ return (sp B.<> B.code contents) codeTag :: PandocMonad m => MuseParser m (F Inlines) codeTag = do -- cgit v1.2.3 From afedb41b170cd9198ab589567f39e99717667a31 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sun, 10 Sep 2017 12:42:24 +0300 Subject: Muse reader: trim newlines from <example>s --- src/Text/Pandoc/Readers/Muse.hs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 3b089772f..f70085c54 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -223,7 +223,16 @@ header = try $ do return $ B.headerWith attr level <$> content exampleTag :: PandocMonad m => MuseParser m (F Blocks) -exampleTag = liftM (return . uncurry B.codeBlockWith) $ htmlElement "example" +exampleTag = do + (attr, contents) <- htmlElement "example" + return $ return $ B.codeBlockWith attr $ chop contents + where lchop s = case s of + '\n':ss -> ss + _ -> s + rchop = reverse . lchop . reverse + -- Trim up to one newline from the beginning and the end, + -- in case opening and/or closing tags are on separate lines. + chop = lchop . rchop literal :: PandocMonad m => MuseParser m (F Blocks) literal = liftM (return . rawBlock) $ htmlElement "literal" -- cgit v1.2.3 From 27cccfac849d644e1f722314cefa1ae212227d18 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 11 Sep 2017 12:13:09 +0300 Subject: Muse reader: parse verbatim tag --- src/Text/Pandoc/Readers/Muse.hs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index f70085c54..ab9a51bad 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -39,7 +39,6 @@ TODO: - Anchors - Citations and <biblio> - <play> environment -- <verbatim> tag -} module Text.Pandoc.Readers.Muse (readMuse) where @@ -537,6 +536,7 @@ inline = choice [ br , superscriptTag , subscriptTag , strikeoutTag + , verbatimTag , link , code , codeTag @@ -613,6 +613,11 @@ subscriptTag = inlineTag B.subscript "sub" strikeoutTag :: PandocMonad m => MuseParser m (F Inlines) strikeoutTag = inlineTag B.strikeout "del" +verbatimTag :: PandocMonad m => MuseParser m (F Inlines) +verbatimTag = do + content <- parseHtmlContent "verbatim" anyChar + return $ return $ B.text $ fromEntities content + code :: PandocMonad m => MuseParser m (F Inlines) code = try $ do pos <- getPosition -- cgit v1.2.3 From 508c3a64d823989dc6613ac7656851989530af65 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sun, 10 Sep 2017 22:35:52 +0300 Subject: Muse reader: parse {{{ }}} example syntax --- src/Text/Pandoc/Readers/Muse.hs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index ab9a51bad..b061d2dfc 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -30,7 +30,6 @@ Conversion of Muse text to 'Pandoc' document. -} {- TODO: -- {{{ }}} syntax for <example> - Page breaks (five "*") - Headings with anchors (make it round trip with Muse writer) - Org tables @@ -177,6 +176,7 @@ blockElements :: PandocMonad m => MuseParser m (F Blocks) blockElements = choice [ comment , separator , header + , example , exampleTag , literal , centerTag @@ -221,6 +221,13 @@ header = try $ do attr <- registerHeader ("", [], []) (runF content defaultParserState) return $ B.headerWith attr level <$> content +example :: PandocMonad m => MuseParser m (F Blocks) +example = try $ do + string "{{{" + optionMaybe blankline + contents <- manyTill anyChar $ try (optionMaybe blankline >> string "}}}") + return $ return $ B.codeBlock contents + exampleTag :: PandocMonad m => MuseParser m (F Blocks) exampleTag = do (attr, contents) <- htmlElement "example" -- cgit v1.2.3 From 8e4ee6656399e897367ea874fe494c35e2715ac9 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 11 Sep 2017 17:30:15 +0300 Subject: Muse reader: allow inline markup to be followed by punctuation Previously code was not allowed to be followed by comma, and emphasis was allowed to be followed by letter. --- src/Text/Pandoc/Readers/Muse.hs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index ab9a51bad..02ac783dd 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -45,6 +45,7 @@ module Text.Pandoc.Readers.Muse (readMuse) where import Control.Monad import Control.Monad.Except (throwError) import qualified Data.Map as M +import Data.Char (isLetter) import Data.Text (Text, unpack) import Data.List (stripPrefix) import Data.Maybe (fromMaybe) @@ -582,7 +583,7 @@ enclosedInlines :: (PandocMonad m, Show a, Show b) -> MuseParser m b -> MuseParser m (F Inlines) enclosedInlines start end = try $ - trimInlinesF . mconcat <$> enclosed start end inline + trimInlinesF . mconcat <$> (enclosed start end inline <* notFollowedBy (satisfy isLetter)) inlineTag :: PandocMonad m => (Inlines -> Inlines) @@ -629,7 +630,7 @@ code = try $ do guard $ not $ null contents guard $ head contents `notElem` " \t\n" guard $ last contents `notElem` " \t\n" - notFollowedBy nonspaceChar + notFollowedBy $ satisfy isLetter return $ return (sp B.<> B.code contents) codeTag :: PandocMonad m => MuseParser m (F Inlines) -- cgit v1.2.3 From c7e3c1ec1797d633c181e5701c94f4169b0e5471 Mon Sep 17 00:00:00 2001 From: Mauro Bieg <mb21@users.noreply.github.com> Date: Tue, 12 Sep 2017 05:18:42 +0200 Subject: Support for PDF generation via `weasyprint` and `prince` (#3909) * Rename --latex-engine to --pdf-engine * In `Text.Pandoc.Options.WriterOptions`, rename `writerLaTeXEngine` to `writerPdfEngine` and `writerLaTeXArgs` to `writerPdfArgs`. * Add support for `weasyprint` and `prince`, in addition to `wkhtmltopdf`, for PDF generation via HTML (closes #3906). * `Text.Pandoc.PDF.html2pdf`: use stdin instead of intermediate HTML file --- src/Text/Pandoc/App.hs | 118 +++++++++++++++++++++++++++++---------------- src/Text/Pandoc/Error.hs | 2 +- src/Text/Pandoc/Options.hs | 4 +- src/Text/Pandoc/PDF.hs | 46 +++++++++++------- 4 files changed, 108 insertions(+), 62 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 367a1f550..93a2a9da6 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -121,6 +121,53 @@ parseOptions options' defaults = do opts <- foldl (>>=) (return defaults) actions return (opts{ optInputFiles = args }) +latexEngines :: [String] +latexEngines = ["pdflatex", "lualatex", "xelatex"] + +htmlEngines :: [String] +htmlEngines = ["wkhtmltopdf", "weasyprint", "prince"] + +pdfEngines :: [String] +pdfEngines = latexEngines ++ htmlEngines ++ ["context", "pdfroff"] + +pdfWriterAndProg :: Maybe String -- ^ user-specified writer name + -> Maybe String -- ^ user-specified pdf-engine + -> IO (String, Maybe String) -- ^ IO (writerName, maybePdfEngineProg) +pdfWriterAndProg mWriter mEngine = do + let panErr msg = liftIO $ E.throwIO $ PandocAppError msg + case go mWriter mEngine of + (Right writ, Right prog) -> return (writ, Just prog) + (Left err, _) -> panErr err + (_, Left err) -> panErr err + where + go Nothing Nothing = (Right "latex", Right $ head latexEngines) + go (Just writer) Nothing = (Right writer, engineForWriter writer) + go Nothing (Just engine) = (writerForEngine engine, Right engine) + go (Just writer) (Just engine) = + let (Right shouldFormat) = writerForEngine engine + userFormat = case map toLower writer of + "html5" -> "html" + x -> x + in if userFormat == shouldFormat + then (Right writer, Right engine) + else (Left $ "pdf-engine " ++ engine ++ " is not compatible with output format " + ++ writer ++ ", please use `-t " ++ shouldFormat ++ "`", Left "") + + writerForEngine "context" = Right "context" + writerForEngine "pdfroff" = Right "ms" + writerForEngine en + | takeBaseName en `elem` latexEngines = Right "latex" + | takeBaseName en `elem` htmlEngines = Right "html" + writerForEngine _ = Left "pdf-engine not known" + + engineForWriter "context" = Right "context" + engineForWriter "ms" = Right "pdfroff" + engineForWriter "latex" = Right $ head latexEngines + engineForWriter format + | format `elem` ["html", "html5"] = Right $ head htmlEngines + | otherwise = Left $ "cannot produce pdf output with output format " ++ format + + convertWithOpts :: Opt -> IO () convertWithOpts opts = do let args = optInputFiles opts @@ -171,18 +218,16 @@ convertWithOpts opts = do else "markdown") sources Just x -> map toLower x - let writerName = case optWriter opts of - Nothing -> defaultWriterName outputFile - Just x -> map toLower x - let format = takeWhile (`notElem` ['+','-']) - $ takeFileName writerName -- in case path to lua script + let nonPdfWriterName Nothing = defaultWriterName outputFile + nonPdfWriterName (Just x) = map toLower x let pdfOutput = map toLower (takeExtension outputFile) == ".pdf" + (writerName, maybePdfProg) <- if pdfOutput + then pdfWriterAndProg (optWriter opts) (optPdfEngine opts) + else return (nonPdfWriterName $ optWriter opts, Nothing) - let laTeXOutput = format `elem` ["latex", "beamer"] - let conTeXtOutput = format == "context" - let html5Output = format == "html5" || format == "html" - let msOutput = format == "ms" + let format = takeWhile (`notElem` ['+','-']) + $ takeFileName writerName -- in case path to lua script -- disabling the custom writer for now (writer, writerExts) <- @@ -417,7 +462,7 @@ convertWithOpts opts = do , writerEpubChapterLevel = optEpubChapterLevel opts , writerTOCDepth = optTOCDepth opts , writerReferenceDoc = optReferenceDoc opts - , writerLaTeXArgs = optLaTeXEngineArgs opts + , writerPdfArgs = optPdfEngineArgs opts , writerSyntaxMap = syntaxMap } @@ -475,27 +520,14 @@ convertWithOpts opts = do case writer of ByteStringWriter f -> f writerOptions doc >>= writeFnBinary outputFile - TextWriter f - | pdfOutput -> do - -- make sure writer is latex, beamer, context, html5 or ms - unless (laTeXOutput || conTeXtOutput || html5Output || - msOutput) $ - liftIO $ E.throwIO $ PandocAppError $ - "cannot produce pdf output with " ++ format ++ " writer" - - let pdfprog = case () of - _ | conTeXtOutput -> "context" - | html5Output -> "wkhtmltopdf" - | html5Output -> "wkhtmltopdf" - | msOutput -> "pdfroff" - | otherwise -> optLaTeXEngine opts - - res <- makePDF pdfprog f writerOptions verbosity media doc + TextWriter f -> case maybePdfProg of + Just pdfProg -> do + res <- makePDF pdfProg f writerOptions verbosity media doc case res of Right pdf -> writeFnBinary outputFile pdf Left err' -> liftIO $ E.throwIO $ PandocPDFError (UTF8.toStringLazy err') - | otherwise -> do + Nothing -> do let htmlFormat = format `elem` ["html","html4","html5","s5","slidy","slideous","dzslides","revealjs"] handleEntities = if (htmlFormat || @@ -605,8 +637,8 @@ data Opt = Opt , optDataDir :: Maybe FilePath , optCiteMethod :: CiteMethod -- ^ Method to output cites , optListings :: Bool -- ^ Use listings package for code blocks - , optLaTeXEngine :: String -- ^ Program to use for latex -> pdf - , optLaTeXEngineArgs :: [String] -- ^ Flags to pass to the latex-engine + , optPdfEngine :: Maybe String -- ^ Program to use for latex/html -> pdf + , optPdfEngineArgs :: [String] -- ^ Flags to pass to the engine , 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 @@ -681,8 +713,8 @@ defaultOpts = Opt , optDataDir = Nothing , optCiteMethod = Citeproc , optListings = False - , optLaTeXEngine = "pdflatex" - , optLaTeXEngineArgs = [] + , optPdfEngine = Nothing + , optPdfEngineArgs = [] , optSlideLevel = Nothing , optSetextHeaders = True , optAscii = False @@ -778,7 +810,6 @@ defaultWriterName x = ".org" -> "org" ".asciidoc" -> "asciidoc" ".adoc" -> "asciidoc" - ".pdf" -> "latex" ".fb2" -> "fb2" ".opml" -> "opml" ".icml" -> "icml" @@ -1314,23 +1345,24 @@ options = "NUMBER") "" -- "Header level at which to split chapters in EPUB" - , Option "" ["latex-engine"] + , Option "" ["pdf-engine"] (ReqArg (\arg opt -> do let b = takeBaseName arg - if b `elem` ["pdflatex", "lualatex", "xelatex"] - then return opt { optLaTeXEngine = arg } - else E.throwIO $ PandocOptionError "latex-engine must be pdflatex, lualatex, or xelatex.") + if b `elem` pdfEngines + then return opt { optPdfEngine = Just arg } + else E.throwIO $ PandocOptionError $ "pdf-engine must be one of " + ++ intercalate ", " pdfEngines) "PROGRAM") - "" -- "Name of latex program to use in generating PDF" + "" -- "Name of program to use in generating PDF" - , Option "" ["latex-engine-opt"] + , Option "" ["pdf-engine-opt"] (ReqArg (\arg opt -> do - let oldArgs = optLaTeXEngineArgs opt - return opt { optLaTeXEngineArgs = arg : oldArgs }) + let oldArgs = optPdfEngineArgs opt + return opt { optPdfEngineArgs = arg : oldArgs }) "STRING") - "" -- "Flags to pass to the LaTeX engine, all instances of this option are accumulated and used" + "" -- "Flags to pass to the PDF-engine, all instances of this option are accumulated and used" , Option "" ["bibliography"] (ReqArg @@ -1590,6 +1622,10 @@ handleUnrecognizedOption "--old-dashes" = ("--old-dashes has been removed. Use +old_dashes extension instead." :) handleUnrecognizedOption "--no-wrap" = ("--no-wrap has been removed. Use --wrap=none instead." :) +handleUnrecognizedOption "--latex-engine" = + ("--latex-engine has been removed. Use --pdf-engine instead." :) +handleUnrecognizedOption "--latex-engine-opt" = + ("--latex-engine-opt has been removed. Use --pdf-engine-opt instead." :) handleUnrecognizedOption "--chapters" = ("--chapters has been removed. Use --top-level-division=chapter instead." :) handleUnrecognizedOption "--reference-docx" = diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs index 24186720c..2bd8bef0a 100644 --- a/src/Text/Pandoc/Error.hs +++ b/src/Text/Pandoc/Error.hs @@ -96,7 +96,7 @@ handleError (Left e) = PandocSyntaxMapError s -> err 67 s PandocFailOnWarningError -> err 3 "Failing because there were warnings." PandocPDFProgramNotFoundError pdfprog -> err 47 $ - pdfprog ++ " not found. " ++ pdfprog ++ " is needed for pdf output." + pdfprog ++ " not found. Please select a different --pdf-engine or install " ++ pdfprog PandocPDFError logmsg -> err 43 $ "Error producing PDF.\n" ++ logmsg PandocFilterError filtername msg -> err 83 $ "Error running filter " ++ filtername ++ ":\n" ++ msg diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index 89b26deb0..0bd66d54d 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -216,7 +216,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 - , writerLaTeXArgs :: [String] -- ^ Flags to pass to latex-engine + , writerPdfArgs :: [String] -- ^ Flags to pass to pdf-engine , writerReferenceLocation :: ReferenceLocation -- ^ Location of footnotes and references for writing markdown , writerSyntaxMap :: SyntaxMap } deriving (Show, Data, Typeable, Generic) @@ -252,7 +252,7 @@ instance Default WriterOptions where , writerEpubChapterLevel = 1 , writerTOCDepth = 3 , writerReferenceDoc = Nothing - , writerLaTeXArgs = [] + , writerPdfArgs = [] , writerReferenceLocation = EndOfDocument , writerSyntaxMap = defaultSyntaxMap } diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index 4e4c0b2c1..b2b7da54f 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -37,6 +37,7 @@ import qualified Control.Exception as E import Control.Monad (unless, when) import Control.Monad.Trans (MonadIO (..)) import qualified Data.Text as T +import qualified Data.Text.IO as TextIO import Data.Text (Text) import qualified Data.ByteString as BS import Data.ByteString.Lazy (ByteString) @@ -78,8 +79,8 @@ changePathSeparators :: FilePath -> FilePath changePathSeparators = intercalate "/" . splitDirectories #endif -makePDF :: String -- ^ pdf creator (pdflatex, lualatex, - -- xelatex, context, wkhtmltopdf, pdfroff) +makePDF :: String -- ^ pdf creator (pdflatex, lualatex, xelatex, + -- wkhtmltopdf, weasyprint, prince, context, pdfroff) -> (WriterOptions -> Pandoc -> PandocIO Text) -- ^ writer -> WriterOptions -- ^ options -> Verbosity -- ^ verbosity level @@ -94,7 +95,7 @@ makePDF "wkhtmltopdf" writer opts verbosity _ doc@(Pandoc meta _) = do _ -> [] meta' <- metaToJSON opts (return . stringify) (return . stringify) meta let toArgs (f, mbd) = maybe [] (\d -> ['-':'-':f, d]) mbd - let args = mathArgs ++ + let args = writerPdfArgs opts ++ mathArgs ++ concatMap toArgs [("page-size", getField "papersize" meta') ,("title", getField "title" meta') @@ -108,11 +109,19 @@ makePDF "wkhtmltopdf" writer opts verbosity _ doc@(Pandoc meta _) = do (getField "margin-left" meta')) ] source <- writer opts doc - liftIO $ html2pdf verbosity args source + liftIO $ html2pdf verbosity "wkhtmltopdf" args source +makePDF "weasyprint" writer opts verbosity _ doc = do + let args = writerPdfArgs opts + source <- writer opts doc + liftIO $ html2pdf verbosity "weasyprint" args source +makePDF "prince" writer opts verbosity _ doc = do + let args = writerPdfArgs opts + source <- writer opts doc + liftIO $ html2pdf verbosity "prince" args source makePDF "pdfroff" writer opts verbosity _mediabag doc = do source <- writer opts doc let args = ["-ms", "-mpdfmark", "-e", "-t", "-k", "-KUTF-8", "-i", - "--no-toc-relocation"] + "--no-toc-relocation"] ++ writerPdfArgs opts liftIO $ ms2pdf verbosity args source makePDF program writer opts verbosity mediabag doc = do let withTemp = if takeBaseName program == "context" @@ -124,7 +133,7 @@ makePDF program writer opts verbosity mediabag doc = do source <- runIOorExplode $ do setVerbosity verbosity writer opts doc' - let args = writerLaTeXArgs opts + let args = writerPdfArgs opts case takeBaseName program of "context" -> context2pdf verbosity tmpdir source prog | prog `elem` ["pdflatex", "lualatex", "xelatex"] @@ -212,7 +221,7 @@ tex2pdf' verbosity args tmpDir program source = do case logmsg of x | "! Package inputenc Error" `BC.isPrefixOf` x && program /= "xelatex" - -> "\nTry running pandoc with --latex-engine=xelatex." + -> "\nTry running pandoc with --pdf-engine=xelatex." _ -> "" return $ Left $ logmsg <> extramsg (ExitSuccess, Nothing) -> return $ Left "" @@ -347,32 +356,33 @@ ms2pdf verbosity args source = do ExitSuccess -> Right out html2pdf :: Verbosity -- ^ Verbosity level - -> [String] -- ^ Args to wkhtmltopdf + -> String -- ^ Program (wkhtmltopdf, weasyprint or prince) + -> [String] -- ^ Args to program -> Text -- ^ HTML5 source -> IO (Either ByteString ByteString) -html2pdf verbosity args source = do - file <- withTempFile "." "html2pdf.html" $ \fp _ -> return fp +html2pdf verbosity program args source = do pdfFile <- withTempFile "." "html2pdf.pdf" $ \fp _ -> return fp - BS.writeFile file $ UTF8.fromText source - let programArgs = args ++ [file, pdfFile] + let pdfFileArgName = if program == "prince" + then ["-o"] + else [] + let programArgs = args ++ ["-"] ++ pdfFileArgName ++ [pdfFile] env' <- getEnvironment when (verbosity >= INFO) $ do putStrLn "[makePDF] Command line:" - putStrLn $ "wkhtmltopdf" ++ " " ++ unwords (map show programArgs) + putStrLn $ program ++ " " ++ unwords (map show programArgs) putStr "\n" putStrLn "[makePDF] Environment:" mapM_ print env' putStr "\n" - putStrLn $ "[makePDF] Contents of " ++ file ++ ":" - BL.readFile file >>= BL.putStr + putStrLn $ "[makePDF] Contents of intermediate HTML:" + TextIO.putStr source putStr "\n" (exit, out) <- E.catch - (pipeProcess (Just env') "wkhtmltopdf" programArgs BL.empty) + (pipeProcess (Just env') program programArgs $ BL.fromStrict $ UTF8.fromText source) (\(e :: IOError) -> if isDoesNotExistError e then E.throwIO $ - PandocPDFProgramNotFoundError "wkhtml2pdf" + PandocPDFProgramNotFoundError program else E.throwIO e) - removeFile file when (verbosity >= INFO) $ do BL.hPutStr stdout out putStr "\n" -- cgit v1.2.3 From 6509501e90442ab4e4c5488b8f4b1aef16352885 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 12 Sep 2017 08:25:41 -0700 Subject: Use defaultLatexEngine instead of `head latexEngines`. Partial functions make me nervous. --- src/Text/Pandoc/App.hs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 93a2a9da6..3e8d767f8 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -124,9 +124,15 @@ parseOptions options' defaults = do latexEngines :: [String] latexEngines = ["pdflatex", "lualatex", "xelatex"] +defaultLatexEngine :: String +defaultLatexEngine = "pdflatex" + htmlEngines :: [String] htmlEngines = ["wkhtmltopdf", "weasyprint", "prince"] +defaultHtmlEngine :: String +defaultHtmlEngine = "wkhtmltopdf" + pdfEngines :: [String] pdfEngines = latexEngines ++ htmlEngines ++ ["context", "pdfroff"] @@ -140,7 +146,7 @@ pdfWriterAndProg mWriter mEngine = do (Left err, _) -> panErr err (_, Left err) -> panErr err where - go Nothing Nothing = (Right "latex", Right $ head latexEngines) + go Nothing Nothing = (Right "latex", Right defaultLatexEngine) go (Just writer) Nothing = (Right writer, engineForWriter writer) go Nothing (Just engine) = (writerForEngine engine, Right engine) go (Just writer) (Just engine) = @@ -162,9 +168,9 @@ pdfWriterAndProg mWriter mEngine = do engineForWriter "context" = Right "context" engineForWriter "ms" = Right "pdfroff" - engineForWriter "latex" = Right $ head latexEngines + engineForWriter "latex" = Right defaultLatexEngine engineForWriter format - | format `elem` ["html", "html5"] = Right $ head htmlEngines + | format `elem` ["html", "html5"] = Right defaultHtmlEngine | otherwise = Left $ "cannot produce pdf output with output format " ++ format -- cgit v1.2.3 From b71c7d97d17d6358a7e797655122ba03ccf193ca Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 12 Sep 2017 08:28:04 -0700 Subject: Add default pdf engine for beamer. --- src/Text/Pandoc/App.hs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 3e8d767f8..3df4953f1 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -169,6 +169,7 @@ pdfWriterAndProg mWriter mEngine = do engineForWriter "context" = Right "context" engineForWriter "ms" = Right "pdfroff" engineForWriter "latex" = Right defaultLatexEngine + engineForWriter "beamer" = Right defaultLatexEngine engineForWriter format | format `elem` ["html", "html5"] = Right defaultHtmlEngine | otherwise = Left $ "cannot produce pdf output with output format " ++ format -- cgit v1.2.3 From 4177ee86261f624232cf6022d28dba573af128fd Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 12 Sep 2017 08:58:47 -0700 Subject: Textile reader: allow 'pre' code in list item. Closes #3916. --- src/Text/Pandoc/Readers/Textile.hs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Textile.hs b/src/Text/Pandoc/Readers/Textile.hs index 853d2768f..9cd3d2c36 100644 --- a/src/Text/Pandoc/Readers/Textile.hs +++ b/src/Text/Pandoc/Readers/Textile.hs @@ -178,7 +178,6 @@ 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"))) - optional blanklines -- drop leading newline if any let result'' = case result' of '\n':xs -> xs @@ -262,10 +261,11 @@ orderedListItemAtDepth = genericListItemAtDepth '#' 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 + contents <- mconcat <$> many ((B.plain . mconcat <$> many1 inline) <|> + try (newline >> codeBlockPre)) newline sublist <- option mempty (anyListAtDepth (depth + 1)) - return $ (B.plain p) <> sublist + return $ contents <> sublist -- | A definition list is a set of consecutive definition items definitionList :: PandocMonad m => ParserT [Char] ParserState m Blocks @@ -295,10 +295,6 @@ definitionListStart = try $ do <|> try (lookAhead (() <$ string ":=")) ) -listInline :: PandocMonad m => ParserT [Char] ParserState m Inlines -listInline = try (notFollowedBy newline >> inline) - <|> try (endline <* notFollowedBy listStart) - -- | A definition list item in textile begins with '- ', followed by -- the term defined, then spaces and ":=". The definition follows, on -- the same single line, or spaned on multiple line, after a line @@ -310,7 +306,7 @@ definitionListItem = try $ do return (term, def') where inlineDef :: PandocMonad m => ParserT [Char] ParserState m [Blocks] inlineDef = liftM (\d -> [B.plain d]) - $ optional whitespace >> (trimInlines . mconcat <$> many listInline) <* newline + $ optional whitespace >> (trimInlines . mconcat <$> many inline) <* newline multilineDef :: PandocMonad m => ParserT [Char] ParserState m [Blocks] multilineDef = try $ do optional whitespace >> newline -- cgit v1.2.3 From 64472a468c9edbe118fa31c618a75a0e5522bbec Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 14 Sep 2017 22:38:23 -0700 Subject: FromJSON/ToJSON instances for Reader, WriterOptions. Depends on skylighting 0.3.5. --- src/Text/Pandoc/Extensions.hs | 10 ++++++++++ src/Text/Pandoc/Options.hs | 8 ++++++++ 2 files changed, 18 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 95e59063b..5d3a4cb29 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -51,10 +51,16 @@ import Data.Typeable (Typeable) import GHC.Generics (Generic) import Text.Pandoc.Shared (safeRead) import Text.Parsec +import Data.Aeson (ToJSON(..), FromJSON(..), + genericToEncoding, defaultOptions) newtype Extensions = Extensions Integer deriving (Show, Read, Eq, Ord, Data, Typeable, Generic) +instance ToJSON Extensions where + toEncoding = genericToEncoding defaultOptions +instance FromJSON Extensions + instance Monoid Extensions where mempty = Extensions 0 mappend (Extensions a) (Extensions b) = Extensions (a .|. b) @@ -148,6 +154,10 @@ data Extension = | Ext_spaced_reference_links -- ^ Allow space between two parts of ref link deriving (Show, Read, Enum, Eq, Ord, Bounded, Data, Typeable, Generic) +instance ToJSON Extension where + toEncoding = genericToEncoding defaultOptions +instance FromJSON Extension + -- | Extensions to be used with pandoc-flavored markdown. pandocExtensions :: Extensions pandocExtensions = extensionsFromList diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index 0bd66d54d..7046e984a 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -68,6 +68,10 @@ data ReaderOptions = ReaderOptions{ , readerTrackChanges :: TrackChanges } deriving (Show, Read, Data, Typeable, Generic) +instance ToJSON ReaderOptions where + toEncoding = genericToEncoding defaultOptions +instance FromJSON ReaderOptions + instance Default ReaderOptions where def = ReaderOptions{ readerExtensions = emptyExtensions @@ -221,6 +225,10 @@ data WriterOptions = WriterOptions , writerSyntaxMap :: SyntaxMap } deriving (Show, Data, Typeable, Generic) +instance ToJSON WriterOptions where + toEncoding = genericToEncoding defaultOptions +instance FromJSON WriterOptions + instance Default WriterOptions where def = WriterOptions { writerTemplate = Nothing , writerVariables = [] -- cgit v1.2.3 From b1aa67f0c925a2ce7cd4f6d53fcf3c7e18709c78 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 15 Sep 2017 09:36:23 -0700 Subject: Remove To/FromJSON instance for WriterOptions. This required the (now removed) instances for Syntax. It was too long, anyway, to be of use. --- src/Text/Pandoc/Options.hs | 4 ---- 1 file changed, 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index 7046e984a..cd353e18e 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -225,10 +225,6 @@ data WriterOptions = WriterOptions , writerSyntaxMap :: SyntaxMap } deriving (Show, Data, Typeable, Generic) -instance ToJSON WriterOptions where - toEncoding = genericToEncoding defaultOptions -instance FromJSON WriterOptions - instance Default WriterOptions where def = WriterOptions { writerTemplate = Nothing , writerVariables = [] -- cgit v1.2.3 From 684f0552489936427d4273b73ea55b6039a59751 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 15 Sep 2017 17:26:14 -0700 Subject: Set PANDOC_READER_OPTIONS in environment where filters are run. This contains a JSON representation of ReaderOptions. --- src/Text/Pandoc/App.hs | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 3df4953f1..f8e23b10c 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -521,7 +521,7 @@ convertWithOpts opts = do >=> return . flip (foldr addMetadata) metadata >=> applyTransforms transforms >=> applyLuaFilters datadir (optLuaFilters opts) [format] - >=> applyFilters datadir filters' [format] + >=> applyFilters readerOpts datadir filters' [format] ) media <- getMediaBag @@ -560,8 +560,9 @@ type Transform = Pandoc -> Pandoc isTextFormat :: String -> Bool isTextFormat s = s `notElem` ["odt","docx","epub","epub3"] -externalFilter :: MonadIO m => FilePath -> [String] -> Pandoc -> m Pandoc -externalFilter f args' d = liftIO $ do +externalFilter :: MonadIO m + => ReaderOptions -> FilePath -> [String] -> Pandoc -> m Pandoc +externalFilter ropts f args' d = liftIO $ do exists <- doesFileExist f isExecutable <- if exists then executable <$> getPermissions f @@ -582,7 +583,10 @@ externalFilter f args' d = liftIO $ do when (isNothing mbExe) $ E.throwIO $ PandocFilterError f ("Could not find executable " ++ f') env <- getEnvironment - let env' = Just $ ("PANDOC_VERSION", pandocVersion) : env + let env' = Just + ( ("PANDOC_VERSION", pandocVersion) + : ("PANDOC_READER_OPTIONS", UTF8.toStringLazy (encode ropts)) + : env ) (exitcode, outbs) <- E.handle filterException $ pipeProcess env' f' args'' $ encode d case exitcode of @@ -862,10 +866,15 @@ applyLuaFilters mbDatadir filters args d = do foldrM ($) d $ map go expandedFilters applyFilters :: MonadIO m - => Maybe FilePath -> [FilePath] -> [String] -> Pandoc -> m Pandoc -applyFilters mbDatadir filters args d = do + => ReaderOptions + -> Maybe FilePath + -> [FilePath] + -> [String] + -> Pandoc + -> m Pandoc +applyFilters ropts mbDatadir filters args d = do expandedFilters <- mapM (expandFilterPath mbDatadir) filters - foldrM ($) d $ map (flip externalFilter args) expandedFilters + foldrM ($) d $ map (flip (externalFilter ropts) args) expandedFilters readSource :: FilePath -> PandocIO Text readSource "-" = liftIO (UTF8.toText <$> BS.getContents) -- cgit v1.2.3 From 86730f49bed89a8068f406266790303f07b0ae71 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 16 Sep 2017 11:10:19 -0700 Subject: Markdown reader: added inlines, inlines1. Eventually we'll add `processEmphasis` and `processBracketed` to this. This will allow us to conform to CommonMark rules and fix #3903 and #1735. --- src/Text/Pandoc/Readers/Markdown.hs | 41 +++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 18 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 664691c8c..c2a73dcc5 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -155,7 +155,7 @@ inlinesInBalancedBrackets = do (_, raw) <- withRaw $ charsInBalancedBrackets 1 guard $ not $ null raw parseFromString' (setPosition pos >> - trimInlinesF . mconcat <$> many inline) (init raw) + trimInlinesF <$> inlines) (init raw) charsInBalancedBrackets :: PandocMonad m => Int -> MarkdownParser m () charsInBalancedBrackets 0 = return () @@ -187,8 +187,8 @@ rawTitleBlockLine = do titleLine :: PandocMonad m => MarkdownParser m (F Inlines) titleLine = try $ do raw <- rawTitleBlockLine - res <- parseFromString' (many inline) raw - return $ trimInlinesF $ mconcat res + res <- parseFromString' inlines raw + return $ trimInlinesF res authorsLine :: PandocMonad m => MarkdownParser m (F [Inlines]) authorsLine = try $ do @@ -203,8 +203,8 @@ authorsLine = try $ do dateLine :: PandocMonad m => MarkdownParser m (F Inlines) dateLine = try $ do raw <- rawTitleBlockLine - res <- parseFromString' (many inline) raw - return $ trimInlinesF $ mconcat res + res <- parseFromString' inlines raw + return $ trimInlinesF res titleBlock :: PandocMonad m => MarkdownParser m () titleBlock = pandocTitleBlock <|> mmdTitleBlock @@ -954,7 +954,7 @@ definitionListItem :: PandocMonad m => Bool -> MarkdownParser m (F (Inlines, [Bl definitionListItem compact = try $ do rawLine' <- anyLine raw <- many1 $ defRawBlock compact - term <- parseFromString' (trimInlinesF . mconcat <$> many inline) rawLine' + term <- parseFromString' (trimInlinesF <$> inlines) rawLine' contents <- mapM (parseFromString' parseBlocks . (++"\n")) raw optional blanklines return $ liftM2 (,) term (sequence contents) @@ -1008,7 +1008,7 @@ normalDefinitionList = do para :: PandocMonad m => MarkdownParser m (F Blocks) para = try $ do exts <- getOption readerExtensions - result <- trimInlinesF . mconcat <$> many1 inline + result <- trimInlinesF <$> inlines1 option (B.plain <$> result) $ try $ do newline @@ -1038,7 +1038,7 @@ para = try $ do _ -> return $ B.para result' plain :: PandocMonad m => MarkdownParser m (F Blocks) -plain = fmap B.plain . trimInlinesF . mconcat <$> many1 inline +plain = fmap B.plain . trimInlinesF <$> inlines1 -- -- raw html @@ -1142,7 +1142,7 @@ lineBlock :: PandocMonad m => MarkdownParser m (F Blocks) lineBlock = try $ do guardEnabled Ext_line_blocks lines' <- lineBlockLines >>= - mapM (parseFromString' (trimInlinesF . mconcat <$> many inline)) + mapM (parseFromString' (trimInlinesF <$> inlines)) return $ B.lineBlock <$> sequence lines' -- @@ -1249,7 +1249,7 @@ tableCaption = try $ do guardEnabled Ext_table_captions skipNonindentSpaces string ":" <|> string "Table:" - trimInlinesF . mconcat <$> many1 inline <* blanklines + trimInlinesF <$> inlines1 <* blanklines -- Parse a simple table with '---' header and one line per row. simpleTable :: PandocMonad m @@ -1363,11 +1363,10 @@ pipeTableRow = try $ do return $ sequence cells 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 +pipeTableCell = + (do result <- inlines1 + return $ B.plain <$> result) + <|> return mempty pipeTableHeaderPart :: PandocMonad m => ParserT [Char] st m (Alignment, Int) pipeTableHeaderPart = try $ do @@ -1443,6 +1442,12 @@ table = try $ do -- inline -- +inlines :: PandocMonad m => MarkdownParser m (F Inlines) +inlines = mconcat <$> many inline + +inlines1 :: PandocMonad m => MarkdownParser m (F Inlines) +inlines1 = mconcat <$> many1 inline + inline :: PandocMonad m => MarkdownParser m (F Inlines) inline = choice [ whitespace , bareURL @@ -1779,8 +1784,8 @@ referenceLink constructor (lab, raw) = do when (raw' == "") $ guardEnabled Ext_shortcut_reference_links let labIsRef = raw' == "" || raw' == "[]" let key = toKey $ if labIsRef then raw else raw' - parsedRaw <- parseFromString' (mconcat <$> many inline) raw' - fallback <- parseFromString' (mconcat <$> many inline) $ dropBrackets raw + parsedRaw <- parseFromString' inlines raw' + fallback <- parseFromString' inlines $ dropBrackets raw implicitHeaderRefs <- option False $ True <$ guardEnabled Ext_implicit_header_references let makeFallback = do @@ -1988,7 +1993,7 @@ textualCite = try $ do let (spaces',raw') = span isSpace raw spc | null spaces' = mempty | otherwise = B.space - lab <- parseFromString' (mconcat <$> many inline) $ dropBrackets raw' + lab <- parseFromString' inlines $ dropBrackets raw' fallback <- referenceLink B.linkWith (lab,raw') return $ do fallback' <- fallback -- cgit v1.2.3 From b1ee747a249e0c1b1840222ef77607218157f099 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 17 Sep 2017 12:49:15 -0700 Subject: Added `--strip-comments` option, `readerStripComments` in `ReaderOptions`. * Options: Added readerStripComments to ReaderOptions. * Added `--strip-comments` command-line option. * Made `htmlTag` from the HTML reader sensitive to this feature. This affects Markdown and Textile input. Closes #2552. --- src/Text/Pandoc/App.hs | 8 ++++++++ src/Text/Pandoc/Options.hs | 4 +++- src/Text/Pandoc/Readers/HTML.hs | 16 ++++++++++------ src/Text/Pandoc/Readers/Markdown.hs | 4 +++- 4 files changed, 24 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index f8e23b10c..e5be7e620 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -483,6 +483,7 @@ convertWithOpts opts = do , readerTrackChanges = optTrackChanges opts , readerAbbreviations = abbrevs , readerExtensions = readerExts + , readerStripComments = optStripComments opts } let transforms = (case optBaseHeaderLevel opts of @@ -666,6 +667,7 @@ data Opt = Opt , optIncludeInHeader :: [FilePath] -- ^ Files to include in header , optResourcePath :: [FilePath] -- ^ Path to search for images etc , optEol :: LineEnding -- ^ Style of line-endings to use + , optStripComments :: Bool -- ^ Skip HTML comments } deriving (Generic, Show) instance ToJSON Opt where @@ -742,6 +744,7 @@ defaultOpts = Opt , optIncludeInHeader = [] , optResourcePath = ["."] , optEol = Native + , optStripComments = False } addMetadata :: (String, String) -> Pandoc -> Pandoc @@ -1114,6 +1117,11 @@ options = "NUMBER") "" -- "Length of line in characters" + , Option "" ["strip-comments"] + (NoArg + (\opt -> return opt { optStripComments = True })) + "" -- "Strip HTML comments" + , Option "" ["toc", "table-of-contents"] (NoArg (\opt -> return opt { optTableOfContents = True })) diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index cd353e18e..345245855 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -65,7 +65,8 @@ data ReaderOptions = ReaderOptions{ -- indented code blocks , readerAbbreviations :: Set.Set String -- ^ Strings to treat as abbreviations , readerDefaultImageExtension :: String -- ^ Default extension for images - , readerTrackChanges :: TrackChanges + , readerTrackChanges :: TrackChanges -- ^ Track changes setting for docx + , readerStripComments :: Bool -- ^ Strip HTML comments instead of parsing as raw HTML } deriving (Show, Read, Data, Typeable, Generic) instance ToJSON ReaderOptions where @@ -82,6 +83,7 @@ instance Default ReaderOptions , readerAbbreviations = defaultAbbrevs , readerDefaultImageExtension = "" , readerTrackChanges = AcceptChanges + , readerStripComments = False } defaultAbbrevs :: Set.Set String diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 2093be19c..4cbc03089 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -46,9 +46,10 @@ import qualified Text.Pandoc.Builder as B import Text.Pandoc.Builder (Blocks, Inlines, trimInlines, HasMeta(..)) import Text.Pandoc.Shared ( extractSpaces, addMetaField , escapeURI, safeRead, crFilter ) -import Text.Pandoc.Options (ReaderOptions(readerExtensions), extensionEnabled, - Extension (Ext_epub_html_exts, - Ext_raw_html, Ext_native_divs, Ext_native_spans)) +import Text.Pandoc.Options ( + ReaderOptions(readerExtensions,readerStripComments), extensionEnabled, + Extension (Ext_epub_html_exts, + Ext_raw_html, Ext_native_divs, Ext_native_spans)) import Text.Pandoc.Logging import Text.Pandoc.Parsing hiding ((<|>)) import Text.Pandoc.Walk @@ -1070,7 +1071,7 @@ _ `closes` _ = False --- parsers for use in markdown, textile readers -- | Matches a stretch of HTML in balanced tags. -htmlInBalanced :: (Monad m) +htmlInBalanced :: (HasReaderOptions st, Monad m) => (Tag String -> Bool) -> ParserT String st m String htmlInBalanced f = try $ do @@ -1118,7 +1119,7 @@ hasTagWarning (TagWarning _:_) = True hasTagWarning _ = False -- | Matches a tag meeting a certain condition. -htmlTag :: Monad m +htmlTag :: (HasReaderOptions st, Monad m) => (Tag String -> Bool) -> ParserT [Char] st m (Tag String, String) htmlTag f = try $ do @@ -1153,7 +1154,10 @@ htmlTag f = try $ do count (length s + 4) anyChar skipMany (satisfy (/='>')) char '>' - return (next, "<!--" <> s <> "-->") + stripComments <- getOption readerStripComments + if stripComments + then return (next, "") + else return (next, "<!--" <> s <> "-->") | otherwise -> fail "bogus comment mode, HTML5 parse error" TagOpen tagname attr -> do guard $ all (isName . fst) attr diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index c2a73dcc5..1364f25cb 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -1079,7 +1079,9 @@ htmlBlock' = try $ do first <- htmlElement skipMany spaceChar optional blanklines - return $ return $ B.rawBlock "html" first + return $ if null first + then mempty + else return $ B.rawBlock "html" first strictHtmlBlock :: PandocMonad m => MarkdownParser m String strictHtmlBlock = htmlInBalanced (not . isInlineTag) -- cgit v1.2.3 From 582cb4b505b774fe8b7424363bcf310e97871c53 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 19 Sep 2017 17:22:32 -0700 Subject: Fix and simply latex engine code in App. Fixes #3931. --- src/Text/Pandoc/App.hs | 91 ++++++++++++++++++++++++-------------------------- 1 file changed, 44 insertions(+), 47 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index e5be7e620..deeac488d 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -50,7 +50,7 @@ import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as B import Data.Char (toLower, toUpper) import Data.Foldable (foldrM) -import Data.List (intercalate, isPrefixOf, isSuffixOf, sort) +import Data.List (intercalate, isPrefixOf, isSuffixOf, sort, find) import qualified Data.Map as M import Data.Maybe (fromMaybe, isJust, isNothing) import qualified Data.Set as Set @@ -86,7 +86,7 @@ import Text.Pandoc.Writers.Math (defaultMathJaxURL, defaultKaTeXURL) import Text.Pandoc.PDF (makePDF) import Text.Pandoc.Process (pipeProcess) import Text.Pandoc.SelfContained (makeDataURI, makeSelfContained) -import Text.Pandoc.Shared (headerShift, isURI, openURL, +import Text.Pandoc.Shared (headerShift, isURI, openURL, ordNub, safeRead, tabFilter, eastAsianLineBreakFilter) import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.XML (toEntities) @@ -124,56 +124,48 @@ parseOptions options' defaults = do latexEngines :: [String] latexEngines = ["pdflatex", "lualatex", "xelatex"] -defaultLatexEngine :: String -defaultLatexEngine = "pdflatex" - htmlEngines :: [String] htmlEngines = ["wkhtmltopdf", "weasyprint", "prince"] -defaultHtmlEngine :: String -defaultHtmlEngine = "wkhtmltopdf" +engines :: [(String, String)] +engines = map ("html",) htmlEngines ++ + map ("html5",) latexEngines ++ + map ("latex",) latexEngines ++ + map ("beamer",) latexEngines ++ + [ ("ms", "pdfroff") + , ("context", "context") + ] pdfEngines :: [String] -pdfEngines = latexEngines ++ htmlEngines ++ ["context", "pdfroff"] +pdfEngines = ordNub $ map snd engines pdfWriterAndProg :: Maybe String -- ^ user-specified writer name -> Maybe String -- ^ user-specified pdf-engine -> IO (String, Maybe String) -- ^ IO (writerName, maybePdfEngineProg) pdfWriterAndProg mWriter mEngine = do let panErr msg = liftIO $ E.throwIO $ PandocAppError msg - case go mWriter mEngine of - (Right writ, Right prog) -> return (writ, Just prog) - (Left err, _) -> panErr err - (_, Left err) -> panErr err + case go (baseWriterName <$> mWriter) mEngine of + Right (writ, prog) -> return (writ, Just prog) + Left err -> panErr err where - go Nothing Nothing = (Right "latex", Right defaultLatexEngine) - go (Just writer) Nothing = (Right writer, engineForWriter writer) - go Nothing (Just engine) = (writerForEngine engine, Right engine) + go Nothing Nothing = Right ("latex", "pdflatex") + go (Just writer) Nothing = (writer,) <$> engineForWriter writer + go Nothing (Just engine) = (,engine) <$> writerForEngine engine go (Just writer) (Just engine) = - let (Right shouldFormat) = writerForEngine engine - userFormat = case map toLower writer of - "html5" -> "html" - x -> x - in if userFormat == shouldFormat - then (Right writer, Right engine) - else (Left $ "pdf-engine " ++ engine ++ " is not compatible with output format " - ++ writer ++ ", please use `-t " ++ shouldFormat ++ "`", Left "") - - writerForEngine "context" = Right "context" - writerForEngine "pdfroff" = Right "ms" - writerForEngine en - | takeBaseName en `elem` latexEngines = Right "latex" - | takeBaseName en `elem` htmlEngines = Right "html" - writerForEngine _ = Left "pdf-engine not known" - - engineForWriter "context" = Right "context" - engineForWriter "ms" = Right "pdfroff" - engineForWriter "latex" = Right defaultLatexEngine - engineForWriter "beamer" = Right defaultLatexEngine - engineForWriter format - | format `elem` ["html", "html5"] = Right defaultHtmlEngine - | otherwise = Left $ "cannot produce pdf output with output format " ++ format + case find (== (writer, engine)) engines of + Just _ -> Right (writer, engine) + Nothing -> Left $ "pdf-engine " ++ engine ++ + " is not compatible with output format " ++ writer + + writerForEngine eng = case [f | (f,e) <- engines, e == eng] of + fmt : _ -> Right fmt + [] -> Left $ + "pdf-engine " ++ eng ++ " not known" + engineForWriter w = case [e | (f,e) <- engines, f == w] of + eng : _ -> Right eng + [] -> Left $ + "cannot produce pdf output from " ++ w convertWithOpts :: Opt -> IO () convertWithOpts opts = do @@ -223,18 +215,19 @@ convertWithOpts opts = do (if any isURI sources then "html" else "markdown") sources - Just x -> map toLower x + Just x -> x let nonPdfWriterName Nothing = defaultWriterName outputFile - nonPdfWriterName (Just x) = map toLower x + nonPdfWriterName (Just x) = x let pdfOutput = map toLower (takeExtension outputFile) == ".pdf" - (writerName, maybePdfProg) <- if pdfOutput - then pdfWriterAndProg (optWriter opts) (optPdfEngine opts) - else return (nonPdfWriterName $ optWriter opts, Nothing) + (writerName, maybePdfProg) <- + if pdfOutput + then pdfWriterAndProg (optWriter opts) (optPdfEngine opts) + else return (nonPdfWriterName $ optWriter opts, Nothing) - let format = takeWhile (`notElem` ['+','-']) - $ takeFileName writerName -- in case path to lua script + let format = baseWriterName + $ takeFileName writerName -- in case path to lua script -- disabling the custom writer for now (writer, writerExts) <- @@ -931,13 +924,15 @@ options :: [OptDescr (Opt -> IO Opt)] options = [ Option "fr" ["from","read"] (ReqArg - (\arg opt -> return opt { optReader = Just arg }) + (\arg opt -> return opt { optReader = + Just (map toLower arg) }) "FORMAT") "" , Option "tw" ["to","write"] (ReqArg - (\arg opt -> return opt { optWriter = Just arg }) + (\arg opt -> return opt { optWriter = + Just (map toLower arg) }) "FORMAT") "" @@ -1680,3 +1675,5 @@ splitField s = (k,_:v) -> (k,v) (k,[]) -> (k,"true") +baseWriterName :: String -> String +baseWriterName = takeWhile (\c -> c /= '+' && c /= '-') -- cgit v1.2.3 From e87db3739e731312c8bd0bf1c207f9dcd1b3ba28 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 20 Sep 2017 11:45:03 -0700 Subject: Fixed bug in determining writer for xelatex engine. This revises the last commit, fixing #3931. --- src/Text/Pandoc/App.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index deeac488d..89a804176 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -129,7 +129,7 @@ htmlEngines = ["wkhtmltopdf", "weasyprint", "prince"] engines :: [(String, String)] engines = map ("html",) htmlEngines ++ - map ("html5",) latexEngines ++ + map ("html5",) htmlEngines ++ map ("latex",) latexEngines ++ map ("beamer",) latexEngines ++ [ ("ms", "pdfroff") -- cgit v1.2.3 From 71f69cd0868f0eecf43ddb606be3074f83a8295c Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Tue, 12 Sep 2017 01:20:49 +0200 Subject: Allow lua filters to return lists of elements Closes: #3918 --- src/Text/Pandoc/Lua.hs | 121 +++++++++++++++++++++------------- src/Text/Pandoc/Lua/StackInstances.hs | 30 ++++----- 2 files changed, 90 insertions(+), 61 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index d6e5def4a..477076191 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -1,8 +1,9 @@ +{-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} {- Copyright © 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> @@ -33,6 +34,7 @@ Pandoc lua utils. module Text.Pandoc.Lua (LuaException (..), pushPandocModule, runLuaFilter) where import Control.Monad (mplus, unless, when, (>=>)) +import Control.Monad.Identity (Identity) import Control.Monad.Trans (MonadIO (..)) import Data.Data (DataType, Data, toConstr, showConstr, dataTypeOf, dataTypeConstrs, dataTypeName, tyconUQname) @@ -40,10 +42,10 @@ import Data.Foldable (foldrM) import Data.Map (Map) import Data.Maybe (isJust) import Foreign.Lua (Lua, FromLuaStack (peek), LuaException (..), StackIndex, - Status(OK), ToLuaStack (push)) + Status (OK), ToLuaStack (push)) import Text.Pandoc.Definition import Text.Pandoc.Lua.PandocModule (pushPandocModule) -import Text.Pandoc.Walk (Walkable (walkM)) +import Text.Pandoc.Walk (walkM) import qualified Data.Map as Map import qualified Foreign.Lua as Lua @@ -56,7 +58,7 @@ runLuaFilter datadir filterPath args pd = liftIO . Lua.runLua $ do pushPandocModule datadir Lua.setglobal "pandoc" top <- Lua.gettop - stat<- Lua.dofile filterPath + stat <- Lua.dofile filterPath if stat /= OK then do luaErrMsg <- peek (-1) <* Lua.pop 1 @@ -64,7 +66,7 @@ runLuaFilter datadir filterPath args pd = liftIO . Lua.runLua $ do else do newtop <- Lua.gettop -- Use the implicitly defined global filter if nothing was returned - when (newtop - top < 1) $ pushGlobalFilter + when (newtop - top < 1) pushGlobalFilter luaFilters <- peek (-1) push args Lua.setglobal "PandocParameters" @@ -81,27 +83,36 @@ runAll :: [LuaFilter] -> Pandoc -> Lua Pandoc runAll = foldr ((>=>) . walkMWithLuaFilter) return walkMWithLuaFilter :: LuaFilter -> Pandoc -> Lua Pandoc -walkMWithLuaFilter (LuaFilter fnMap) = walkLua - where - walkLua :: Pandoc -> Lua Pandoc - walkLua = - (if hasOneOf inlineFilterNames - then walkM (tryFilter fnMap :: Inline -> Lua Inline) - else return) - >=> - (if hasOneOf blockFilterNames - then walkM (tryFilter fnMap :: Block -> Lua Block) - else return) - >=> - (case Map.lookup "Meta" fnMap of - Just fn -> walkM (\(Pandoc meta blocks) -> do - meta' <- runFilterFunction fn meta - return $ Pandoc meta' blocks) - Nothing -> return) - >=> - (case foldl mplus Nothing (map (`Map.lookup` fnMap) pandocFilterNames) of - Just fn -> runFilterFunction fn :: Pandoc -> Lua Pandoc - Nothing -> return) +walkMWithLuaFilter (LuaFilter fnMap) = + walkInlines >=> walkBlocks >=> walkMeta >=> walkPandoc + where + walkInlines :: Pandoc -> Lua Pandoc + walkInlines = + if hasOneOf inlineFilterNames + then walkM (mconcatMapM (tryFilter fnMap :: Inline -> Lua [Inline])) + else return + + walkBlocks :: Pandoc -> Lua Pandoc + walkBlocks = + if hasOneOf blockFilterNames + then walkM (mconcatMapM (tryFilter fnMap :: Block -> Lua [Block])) + else return + + walkMeta :: Pandoc -> Lua Pandoc + walkMeta = + case Map.lookup "Meta" fnMap of + Just fn -> walkM (\(Pandoc meta blocks) -> do + meta' <- runFilterFunction fn meta *> singleElement meta + return $ Pandoc meta' blocks) + Nothing -> return + + walkPandoc :: Pandoc -> Lua Pandoc + walkPandoc = + case foldl mplus Nothing (map (`Map.lookup` fnMap) pandocFilterNames) of + Just fn -> \x -> runFilterFunction fn x *> singleElement x + Nothing -> return + + mconcatMapM f = fmap mconcat . mapM f hasOneOf = any (\k -> isJust (Map.lookup k fnMap)) constructorsFor :: DataType -> [String] @@ -124,14 +135,15 @@ newtype LuaFilter = LuaFilter FunctionMap newtype LuaFilterFunction = LuaFilterFunction { functionIndex :: Int } -- | Try running a filter for the given element -tryFilter :: (Data a, FromLuaStack a, ToLuaStack a) => FunctionMap -> a -> Lua a +tryFilter :: (Data a, FromLuaStack a, ToLuaStack a) + => FunctionMap -> a -> Lua [a] tryFilter fnMap x = let filterFnName = showConstr (toConstr x) catchAllName = tyconUQname $ dataTypeName (dataTypeOf x) in case Map.lookup filterFnName fnMap `mplus` Map.lookup catchAllName fnMap of - Just fn -> runFilterFunction fn x - Nothing -> return x + Just fn -> runFilterFunction fn x *> elementOrList x + Nothing -> return [x] instance FromLuaStack LuaFilter where peek idx = @@ -151,28 +163,42 @@ instance FromLuaStack LuaFilter where -- called with given element as argument and is expected to return an element. -- Alternatively, the function can return nothing or nil, in which case the -- element is left unchanged. -runFilterFunction :: (FromLuaStack a, ToLuaStack a) - => LuaFilterFunction -> a -> Lua a +runFilterFunction :: ToLuaStack a => LuaFilterFunction -> a -> Lua () runFilterFunction lf x = do pushFilterFunction lf push x z <- Lua.pcall 1 1 Nothing - if z /= OK - then do - msg <- peek (-1) - let prefix = "Error while running filter function: " - Lua.throwLuaError $ prefix ++ msg + when (z /= OK) $ do + msg <- Lua.peek (-1) <* Lua.pop 1 + let prefix = "Error while running filter function: " + Lua.throwLuaError $ prefix ++ msg + +elementOrList :: FromLuaStack a => a -> Lua [a] +elementOrList x = do + let topOfStack = Lua.StackIndex (-1) + elementUnchanged <- Lua.isnil topOfStack + if elementUnchanged + then [x] <$ Lua.pop 1 + else do + mbres <- Lua.peekEither topOfStack + case mbres of + Right res -> [res] <$ Lua.pop 1 + Left _ -> Lua.toList topOfStack <* Lua.pop 1 + +singleElement :: FromLuaStack a => a -> Lua a +singleElement x = do + elementUnchanged <- Lua.isnil (-1) + if elementUnchanged + then x <$ Lua.pop 1 else do - noExplicitFilter <- Lua.isnil (-1) - if noExplicitFilter - then Lua.pop 1 *> return x - else do - mbres <- Lua.peekEither (-1) - case mbres of - Left err -> Lua.throwLuaError - ("Error while trying to get a filter's return " - ++ "value from lua stack.\n" ++ err) - Right res -> res <$ Lua.pop 1 + mbres <- Lua.peekEither (-1) + case mbres of + Right res -> res <$ Lua.pop 1 + Left err -> do + Lua.pop 1 + Lua.throwLuaError $ + "Error while trying to get a filter's return " ++ + "value from lua stack.\n" ++ err -- | Push the filter function to the top of the stack. pushFilterFunction :: LuaFilterFunction -> Lua () @@ -188,6 +214,9 @@ registerFilterFunction idx = do refIdx <- Lua.ref Lua.registryindex return $ LuaFilterFunction refIdx +instance (FromLuaStack a) => FromLuaStack (Identity a) where + peek = fmap return . peek + instance ToLuaStack LuaFilterFunction where push = pushFilterFunction diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index 15a7cdd84..73b04e50f 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -35,14 +35,15 @@ module Text.Pandoc.Lua.StackInstances () where import Control.Applicative ((<|>)) import Foreign.Lua (Lua, LuaInteger, LuaNumber, Type (..), FromLuaStack (peek), ToLuaStack (push), StackIndex, throwLuaError, tryLua) -import Foreign.Lua.Api (getmetatable, ltype, newtable, pop, rawget, rawlen) import Text.Pandoc.Definition import Text.Pandoc.Lua.Util (addValue, adjustIndexBy, getTable, pushViaConstructor) import Text.Pandoc.Shared (safeRead) +import qualified Foreign.Lua as Lua + instance ToLuaStack Pandoc where push (Pandoc meta blocks) = do - newtable + Lua.newtable addValue "blocks" blocks addValue "meta" meta @@ -156,7 +157,7 @@ peekMetaValue idx = do -- Get the contents of an AST element. let elementContent :: FromLuaStack a => Lua a elementContent = peek idx - luatype <- ltype idx + luatype <- Lua.ltype idx case luatype of TypeBoolean -> MetaBool <$> peek idx TypeString -> MetaString <$> peek idx @@ -172,13 +173,13 @@ peekMetaValue idx = do Right t -> throwLuaError ("Unknown meta tag: " ++ t) Left _ -> do -- no meta value tag given, try to guess. - len <- rawlen idx + len <- Lua.rawlen idx if len <= 0 then MetaMap <$> peek idx else (MetaInlines <$> peek idx) <|> (MetaBlocks <$> peek idx) <|> (MetaList <$> peek idx) - _ -> throwLuaError ("could not get meta value") + _ -> throwLuaError "could not get meta value" -- | Push an block element to the top of the lua stack. pushBlock :: Block -> Lua () @@ -284,16 +285,15 @@ peekInline idx = do getTag :: StackIndex -> Lua String getTag idx = do - hasMT <- getmetatable idx - if hasMT - then do - push "tag" - rawget (-2) - peek (-1) <* pop 2 - else do - push "tag" - rawget (idx `adjustIndexBy` 1) - peek (-1) <* pop 1 + top <- Lua.gettop + hasMT <- Lua.getmetatable idx + push "tag" + if hasMT then Lua.rawget (-2) else Lua.rawget (idx `adjustIndexBy` 1) + r <- tryLua (peek (-1)) + Lua.settop top + case r of + Left (Lua.LuaException err) -> throwLuaError err + Right res -> return res withAttr :: (Attr -> a -> b) -> (LuaAttr, a) -> b withAttr f (attributes, x) = f (fromLuaAttr attributes) x -- cgit v1.2.3 From 3a7663281a35292b87471da4f5b7a88808546076 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Mon, 25 Sep 2017 09:31:29 +0200 Subject: Org reader: update emphasis border chars The org reader was updated to match current org-mode behavior: the set of characters which are acceptable to occur as the first or last character in an org emphasis have been changed and now allows all non-whitespace chars at the inner border of emphasized text (see `org-emphasis-regexp-components`). Fixes: #3933 --- src/Text/Pandoc/Readers/Org/Inlines.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/Inlines.hs b/src/Text/Pandoc/Readers/Org/Inlines.hs index 42fdfd4dd..c5b1ccc52 100644 --- a/src/Text/Pandoc/Readers/Org/Inlines.hs +++ b/src/Text/Pandoc/Readers/Org/Inlines.hs @@ -735,15 +735,15 @@ many1TillNOrLessNewlines n p end = try $ -- | Chars allowed to occur before emphasis (spaces and newlines are ok, too) emphasisPreChars :: [Char] -emphasisPreChars = "\t \"'({" +emphasisPreChars = "-\t ('\"{" -- | Chars allowed at after emphasis emphasisPostChars :: [Char] -emphasisPostChars = "\t\n !\"'),-.:;?\\}[" +emphasisPostChars = "-\t\n .,:!?;'\")}[" -- | Chars not allowed at the (inner) border of emphasis emphasisForbiddenBorderChars :: [Char] -emphasisForbiddenBorderChars = "\t\n\r \"'," +emphasisForbiddenBorderChars = "\t\n\r " -- | The maximum number of newlines within emphasisAllowedNewlines :: Int -- cgit v1.2.3 From 3fb3af75793686efa114352c3f0c33a4b4b71731 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Tue, 26 Sep 2017 18:28:40 +0300 Subject: Fix a typo: s/collabarators/collaborators/ --- src/Text/Pandoc/Readers/DocBook.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/DocBook.hs b/src/Text/Pandoc/Readers/DocBook.hs index c1e4d742c..f816a9c47 100644 --- a/src/Text/Pandoc/Readers/DocBook.hs +++ b/src/Text/Pandoc/Readers/DocBook.hs @@ -51,7 +51,7 @@ List of all DocBook tags, with [x] indicating implemented, [x] author - The name of an individual author [ ] authorblurb - A short description or note about an author [x] authorgroup - Wrapper for author information when a document has - multiple authors or collabarators + multiple authors or collaborators [x] authorinitials - The initials or other short identifier for an author [o] beginpage - The location of a page break in a print version of the document [ ] bibliocoverage - The spatial or temporal coverage of a document -- cgit v1.2.3 From 2cdb8fe2e65aa13f91c9decd4e6cad97e87cc402 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Tue, 26 Sep 2017 19:31:10 +0300 Subject: Muse reader: test metadata parsing --- src/Text/Pandoc/Readers/Muse.hs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index cf5fc0da2..6e7844d4b 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -150,8 +150,7 @@ parseDirective = do key <- many letter space spaces - raw <- many $ noneOf "\n" - newline + raw <- manyTill anyChar eol value <- parseFromString (trimInlinesF . mconcat <$> many inline) raw return (key, value) -- cgit v1.2.3 From 3fe4aad5a16545a92088510a00d2109a04fd25b8 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 26 Sep 2017 10:05:56 -0700 Subject: Lua: set "arg" instead of "PandocParameters". This is standard for lua scripts, and I see no reason to depart from the standard here. Also, "arg" is now pushed onto the stack before the script is loaded. Previously it was not, and thus "PandocParameters" was not available at the top level. --- src/Text/Pandoc/Lua.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 477076191..2860b84df 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -57,6 +57,8 @@ runLuaFilter datadir filterPath args pd = liftIO . Lua.runLua $ do -- store module in global "pandoc" pushPandocModule datadir Lua.setglobal "pandoc" + push args + Lua.setglobal "arg" top <- Lua.gettop stat <- Lua.dofile filterPath if stat /= OK @@ -68,8 +70,6 @@ runLuaFilter datadir filterPath args pd = liftIO . Lua.runLua $ do -- Use the implicitly defined global filter if nothing was returned when (newtop - top < 1) pushGlobalFilter luaFilters <- peek (-1) - push args - Lua.setglobal "PandocParameters" runAll luaFilters pd pushGlobalFilter :: Lua () -- cgit v1.2.3 From 9a47c7863b7c9d23928e51fd23b8ebc7ac684d16 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 26 Sep 2017 20:20:09 -0700 Subject: Lua filters: set global FORMAT instead of args. This changes the type of runLuaFilter. --- src/Text/Pandoc/App.hs | 8 ++++---- src/Text/Pandoc/Lua.hs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 89a804176..c26b8e768 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -514,7 +514,7 @@ convertWithOpts opts = do >=> maybe return extractMedia (optExtractMedia opts) >=> return . flip (foldr addMetadata) metadata >=> applyTransforms transforms - >=> applyLuaFilters datadir (optLuaFilters opts) [format] + >=> applyLuaFilters datadir (optLuaFilters opts) format >=> applyFilters readerOpts datadir filters' [format] ) media <- getMediaBag @@ -850,12 +850,12 @@ expandFilterPath mbDatadir fp = liftIO $ do _ -> return fp applyLuaFilters :: MonadIO m - => Maybe FilePath -> [FilePath] -> [String] -> Pandoc + => Maybe FilePath -> [FilePath] -> String -> Pandoc -> m Pandoc -applyLuaFilters mbDatadir filters args d = do +applyLuaFilters mbDatadir filters format d = do expandedFilters <- mapM (expandFilterPath mbDatadir) filters let go f d' = liftIO $ do - res <- E.try (runLuaFilter mbDatadir f args d') + res <- E.try (runLuaFilter mbDatadir f format d') case res of Right x -> return x Left (LuaException s) -> E.throw (PandocFilterError f s) diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 2860b84df..ab3b5f4ca 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -51,14 +51,14 @@ import qualified Data.Map as Map import qualified Foreign.Lua as Lua runLuaFilter :: (MonadIO m) - => Maybe FilePath -> FilePath -> [String] -> Pandoc -> m Pandoc -runLuaFilter datadir filterPath args pd = liftIO . Lua.runLua $ do + => Maybe FilePath -> FilePath -> String -> Pandoc -> m Pandoc +runLuaFilter datadir filterPath format pd = liftIO . Lua.runLua $ do Lua.openlibs -- store module in global "pandoc" pushPandocModule datadir Lua.setglobal "pandoc" - push args - Lua.setglobal "arg" + push format + Lua.setglobal "FORMAT" top <- Lua.gettop stat <- Lua.dofile filterPath if stat /= OK -- cgit v1.2.3 From 2314534d4da815fa23f622d43a8d7a2c8902ee8e Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 27 Sep 2017 20:42:04 -0700 Subject: RST writer: add header anchors when header has non-standard id. Closes #3937. --- src/Text/Pandoc/Writers/RST.hs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index 8c941f568..94c135715 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -239,12 +239,17 @@ blockToRST HorizontalRule = return $ blankline $$ "--------------" $$ blankline blockToRST (Header level (name,classes,_) inlines) = do contents <- inlineListToRST inlines + -- we calculate the id that would be used by auto_identifiers + -- so we know whether to print an explicit identifier + let autoId = uniqueIdent inlines mempty isTopLevel <- gets stTopLevel if isTopLevel then do let headerChar = if level > 5 then ' ' else "=-~^'" !! (level - 1) let border = text $ replicate (offset contents) headerChar - return $ nowrap $ contents $$ border $$ blankline + let anchor | null name || name == autoId = empty + | otherwise = ".. " <> text name <> ":" $$ blankline + return $ nowrap $ anchor $$ contents $$ border $$ blankline else do let rub = "rubric:: " <> contents let name' | null name = empty -- cgit v1.2.3 From b5d064e8f08db27f3f9aa7287edd7a777be57594 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Thu, 28 Sep 2017 14:47:07 +0300 Subject: Muse reader: parse anchors --- src/Text/Pandoc/Readers/Muse.hs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 6e7844d4b..6f9b9b3c2 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -35,7 +35,6 @@ TODO: - Org tables - table.el tables - Images with attributes (floating and width) -- Anchors - Citations and <biblio> - <play> environment -} @@ -535,6 +534,7 @@ tableParseCaption = try $ do inline :: PandocMonad m => MuseParser m (F Inlines) inline = choice [ br + , anchor , footnote , strong , strongTag @@ -552,6 +552,16 @@ inline = choice [ br , symbol ] <?> "inline" +anchor :: PandocMonad m => MuseParser m (F Inlines) +anchor = try $ do + getPosition >>= \pos -> guard (sourceColumn pos == 1) + char '#' + first <- letter + rest <- many (letter <|> digit) + skipMany spaceChar <|> void newline + let anchorId = first:rest + return $ return $ B.spanWith (anchorId, [], []) mempty + footnote :: PandocMonad m => MuseParser m (F Inlines) footnote = try $ do ref <- noteMarker -- cgit v1.2.3 From 2f03f389e8df0c52248d0767a8372184d65e4ba5 Mon Sep 17 00:00:00 2001 From: Andrie de Vries <apdevries@gmail.com> Date: Thu, 28 Sep 2017 16:48:42 +0100 Subject: Support R filters #3940 (#3941) --- src/Text/Pandoc/App.hs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index c26b8e768..206c47b30 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -570,6 +570,7 @@ externalFilter ropts f args' d = liftIO $ do ".rb" -> ("ruby", f:args') ".php" -> ("php", f:args') ".js" -> ("node", f:args') + ".r" -> ("Rscript", f:args') _ -> (f, args') else (f, args') unless (exists && isExecutable) $ do -- cgit v1.2.3 From 2f47e04206a3869eadc5c93076e0b50d4362f9df Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Fri, 29 Sep 2017 00:11:52 +0200 Subject: Text.Pandoc.Lua: add mediabag submodule --- src/Text/Pandoc/App.hs | 13 ++-- src/Text/Pandoc/Class.hs | 28 +++++--- src/Text/Pandoc/Lua.hs | 36 +++++++++-- src/Text/Pandoc/Lua/PandocModule.hs | 125 ++++++++++++++++++++++++++++++++---- 4 files changed, 166 insertions(+), 36 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 206c47b30..82c40f5a4 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -850,16 +850,15 @@ expandFilterPath mbDatadir fp = liftIO $ do else return fp _ -> return fp -applyLuaFilters :: MonadIO m - => Maybe FilePath -> [FilePath] -> String -> Pandoc - -> m Pandoc +applyLuaFilters :: Maybe FilePath -> [FilePath] -> String -> Pandoc + -> PandocIO Pandoc applyLuaFilters mbDatadir filters format d = do expandedFilters <- mapM (expandFilterPath mbDatadir) filters - let go f d' = liftIO $ do - res <- E.try (runLuaFilter mbDatadir f format d') + let go f d' = do + res <- runLuaFilter mbDatadir f format d' case res of - Right x -> return x - Left (LuaException s) -> E.throw (PandocFilterError f s) + Right x -> return x + Left (LuaException s) -> E.throw (PandocFilterError f s) foldrM ($) d $ map go expandedFilters applyFilters :: MonadIO m diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 98c567afc..f60062d6c 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -79,6 +79,7 @@ module Text.Pandoc.Class ( PandocMonad(..) , runPure , readDefaultDataFile , readDataFile + , fetchMediaResource , fillMediaBag , extractMedia , toLang @@ -246,9 +247,9 @@ getMediaBag = getsCommonState stMediaBag insertMedia :: PandocMonad m => FilePath -> Maybe MimeType -> BL.ByteString -> m () insertMedia fp mime bs = do - mb <- getsCommonState stMediaBag + mb <- getMediaBag let mb' = MB.insertMedia fp mime bs mb - modifyCommonState $ \st -> st{stMediaBag = mb' } + setMediaBag mb' getInputFiles :: PandocMonad m => m (Maybe [FilePath]) getInputFiles = getsCommonState stInputFiles @@ -633,6 +634,20 @@ withPaths (p:ps) action fp = catchError (action (p </> fp)) (\_ -> withPaths ps action fp) +-- | Fetch local or remote resource (like an image) and provide data suitable +-- for adding it to the MediaBag. +fetchMediaResource :: PandocMonad m + => Maybe String -> String + -> m (FilePath, Maybe MimeType, BL.ByteString) +fetchMediaResource sourceUrl src = do + (bs, mt) <- downloadOrRead sourceUrl src + let ext = fromMaybe (takeExtension src) + (mt >>= extensionFromMimeType) + let bs' = BL.fromChunks [bs] + let basename = showDigest $ sha1 bs' + let fname = basename <.> ext + return (fname, mt, bs') + -- | Traverse tree, filling media bag for any images that -- aren't already in the media bag. fillMediaBag :: PandocMonad m => Maybe String -> Pandoc -> m Pandoc @@ -643,13 +658,8 @@ fillMediaBag sourceURL d = walkM handleImage d case lookupMedia src mediabag of Just (_, _) -> return $ Image attr lab (src, tit) Nothing -> do - (bs, mt) <- downloadOrRead sourceURL src - let ext = fromMaybe (takeExtension src) - (mt >>= extensionFromMimeType) - let bs' = BL.fromChunks [bs] - let basename = showDigest $ sha1 bs' - let fname = basename <.> ext - insertMedia fname mt bs' + (fname, mt, bs) <- fetchMediaResource sourceURL src + insertMedia fname mt bs return $ Image attr lab (fname, tit)) (\e -> case e of diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index ab3b5f4ca..f7e74d0a8 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -39,26 +39,40 @@ import Control.Monad.Trans (MonadIO (..)) import Data.Data (DataType, Data, toConstr, showConstr, dataTypeOf, dataTypeConstrs, dataTypeName, tyconUQname) import Data.Foldable (foldrM) +import Data.IORef (IORef, newIORef, readIORef) import Data.Map (Map) import Data.Maybe (isJust) import Foreign.Lua (Lua, FromLuaStack (peek), LuaException (..), StackIndex, Status (OK), ToLuaStack (push)) +import Text.Pandoc.Class (PandocIO, getMediaBag, setMediaBag) +import Text.Pandoc.MediaBag (MediaBag) import Text.Pandoc.Definition -import Text.Pandoc.Lua.PandocModule (pushPandocModule) +import Text.Pandoc.Lua.PandocModule (pushPandocModule, pushMediaBagModule) import Text.Pandoc.Walk (walkM) import qualified Data.Map as Map import qualified Foreign.Lua as Lua -runLuaFilter :: (MonadIO m) - => Maybe FilePath -> FilePath -> String -> Pandoc -> m Pandoc -runLuaFilter datadir filterPath format pd = liftIO . Lua.runLua $ do +runLuaFilter :: Maybe FilePath -> FilePath -> String + -> Pandoc -> PandocIO (Either LuaException Pandoc) +runLuaFilter datadir filterPath format pd = do + mediaBag <- getMediaBag + mediaBagRef <- liftIO (newIORef mediaBag) + res <- liftIO . Lua.runLuaEither $ + runLuaFilter' datadir filterPath format mediaBagRef pd + newMediaBag <- liftIO (readIORef mediaBagRef) + setMediaBag newMediaBag + return res + +runLuaFilter' :: Maybe FilePath -> FilePath -> String -> IORef MediaBag + -> Pandoc -> Lua Pandoc +runLuaFilter' datadir filterPath format mbRef pd = do Lua.openlibs -- store module in global "pandoc" pushPandocModule datadir Lua.setglobal "pandoc" - push format - Lua.setglobal "FORMAT" + addMediaBagModule + registerFormat top <- Lua.gettop stat <- Lua.dofile filterPath if stat /= OK @@ -71,6 +85,16 @@ runLuaFilter datadir filterPath format pd = liftIO . Lua.runLua $ do when (newtop - top < 1) pushGlobalFilter luaFilters <- peek (-1) runAll luaFilters pd + where + addMediaBagModule = do + Lua.getglobal "pandoc" + push "mediabag" + pushMediaBagModule mbRef + Lua.rawset (-3) + registerFormat = do + push format + Lua.setglobal "FORMAT" + pushGlobalFilter :: Lua () pushGlobalFilter = do diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index afb9aeca6..ffd681d30 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -15,6 +15,10 @@ 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 -} +{-# LANGUAGE CPP #-} +#if !MIN_VERSION_hslua(0,9,0) +{-# OPTIONS_GHC -fno-warn-orphans #-} +#endif {- | Module : Text.Pandoc.Lua.PandocModule Copyright : Copyright © 2017 Albert Krewinkel @@ -25,28 +29,37 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Pandoc module for lua. -} -module Text.Pandoc.Lua.PandocModule ( pushPandocModule ) where +module Text.Pandoc.Lua.PandocModule + ( pushPandocModule + , pushMediaBagModule + ) where -import Control.Monad (unless) +import Control.Monad (unless, zipWithM_) import Data.ByteString.Char8 (unpack) import Data.Default (Default (..)) +import Data.IORef import Data.Text (pack) -import Foreign.Lua (Lua, Status (OK), NumResults, call, loadstring, liftIO, - push, pushHaskellFunction, rawset) -import Text.Pandoc.Class (readDataFile, runIO, runIOorExplode, setUserDataDir) +import Foreign.Lua (Lua, FromLuaStack, ToLuaStack, NumResults, liftIO) +import Text.Pandoc.Class (fetchMediaResource, readDataFile, runIO, + runIOorExplode, setUserDataDir) import Text.Pandoc.Options (ReaderOptions(readerExtensions)) import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Readers (Reader (..), getReader) +import Text.Pandoc.MIME (MimeType) + +import qualified Foreign.Lua as Lua +import qualified Data.ByteString.Lazy as BL +import qualified Text.Pandoc.MediaBag as MB -- | Push the "pandoc" on the lua stack. pushPandocModule :: Maybe FilePath -> Lua () pushPandocModule datadir = do script <- liftIO (pandocModuleScript datadir) - status <- loadstring script - unless (status /= OK) $ call 0 1 - push "__read" - pushHaskellFunction readDoc - rawset (-3) + status <- Lua.loadstring script + unless (status /= Lua.OK) $ Lua.call 0 1 + Lua.push "__read" + Lua.pushHaskellFunction readDoc + Lua.rawset (-3) -- | Get the string representation of the pandoc module pandocModuleScript :: Maybe FilePath -> IO String @@ -56,14 +69,98 @@ pandocModuleScript datadir = unpack <$> readDoc :: String -> String -> Lua NumResults readDoc formatSpec content = do case getReader formatSpec of - Left s -> push s -- Unknown reader + Left s -> Lua.push s -- Unknown reader Right (reader, es) -> case reader of TextReader r -> do res <- liftIO $ runIO $ r def{ readerExtensions = es } (pack content) case res of - Left s -> push $ show s -- error while reading - Right pd -> push pd -- success, push Pandoc - _ -> push "Only string formats are supported at the moment." + Left s -> Lua.push $ show s -- error while reading + Right pd -> Lua.push pd -- success, push Pandoc + _ -> Lua.push "Only string formats are supported at the moment." + return 1 + +-- +-- MediaBag submodule +-- +pushMediaBagModule :: IORef MB.MediaBag -> Lua () +pushMediaBagModule mediaBagRef = do + Lua.newtable + addFunction "insert" (insertMediaFn mediaBagRef) + addFunction "lookup" (lookupMediaFn mediaBagRef) + addFunction "list" (mediaDirectoryFn mediaBagRef) + addFunction "fetch" (insertResource mediaBagRef) + return () + where + addFunction name fn = do + Lua.push name + Lua.pushHaskellFunction fn + Lua.rawset (-3) + +insertMediaFn :: IORef MB.MediaBag + -> FilePath + -> OrNil MimeType + -> BL.ByteString + -> Lua NumResults +insertMediaFn mbRef fp nilOrMime contents = do + liftIO . modifyIORef' mbRef $ MB.insertMedia fp (toMaybe nilOrMime) contents + return 0 + +lookupMediaFn :: IORef MB.MediaBag + -> FilePath + -> Lua NumResults +lookupMediaFn mbRef fp = do + res <- MB.lookupMedia fp <$> liftIO (readIORef mbRef) + case res of + Nothing -> Lua.pushnil *> return 1 + Just (mimeType, contents) -> do + Lua.push mimeType + Lua.push contents + return 2 + +mediaDirectoryFn :: IORef MB.MediaBag + -> Lua NumResults +mediaDirectoryFn mbRef = do + dirContents <- MB.mediaDirectory <$> liftIO (readIORef mbRef) + Lua.newtable + zipWithM_ addEntry [1..] dirContents return 1 + where + addEntry :: Int -> (FilePath, MimeType, Int) -> Lua () + addEntry idx (fp, mimeType, contentLength) = do + Lua.newtable + Lua.push "path" *> Lua.push fp *> Lua.rawset (-3) + Lua.push "type" *> Lua.push mimeType *> Lua.rawset (-3) + Lua.push "length" *> Lua.push contentLength *> Lua.rawset (-3) + Lua.rawseti (-2) idx + +insertResource :: IORef MB.MediaBag + -> String + -> OrNil String + -> Lua NumResults +insertResource mbRef src sourceUrlOrNil = do + (fp, mimeType, bs) <- liftIO . runIOorExplode $ + fetchMediaResource (toMaybe sourceUrlOrNil) src + liftIO $ print (fp, mimeType) + insertMediaFn mbRef fp (OrNil mimeType) bs + +-- +-- Helper types and orphan instances +-- + +newtype OrNil a = OrNil { toMaybe :: Maybe a } + +instance FromLuaStack a => FromLuaStack (OrNil a) where + peek idx = do + noValue <- Lua.isnil idx + if noValue + then return (OrNil Nothing) + else OrNil . Just <$> Lua.peek idx + +#if !MIN_VERSION_hslua(0,9,0) +instance ToLuaStack BL.ByteString where + push = Lua.push . BL.toStrict +instance FromLuaStack BL.ByteString where + peek = fmap BL.fromStrict . Lua.peek +#endif -- cgit v1.2.3 From 358e8c28976276afb1601d308a3cad568494983c Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Fri, 29 Sep 2017 21:05:24 +0200 Subject: Run Lua filters before extracting media This enables users to change the media files being extracted via lua filters. --- src/Text/Pandoc/App.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 82c40f5a4..9b3055b35 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -511,10 +511,10 @@ convertWithOpts opts = do ( (if isJust (optExtractMedia opts) then fillMediaBag (writerSourceURL writerOptions) else return) - >=> maybe return extractMedia (optExtractMedia opts) >=> return . flip (foldr addMetadata) metadata - >=> applyTransforms transforms >=> applyLuaFilters datadir (optLuaFilters opts) format + >=> maybe return extractMedia (optExtractMedia opts) + >=> applyTransforms transforms >=> applyFilters readerOpts datadir filters' [format] ) media <- getMediaBag -- cgit v1.2.3 From f3a80034fff41a8b0c13519fa13bed794db1b8d2 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 30 Sep 2017 16:07:47 -0500 Subject: Removed writerSourceURL, add source URL to common state. Removed `writerSourceURL` from `WriterOptions` (API change). Added `stSourceURL` to `CommonState`. It is set automatically by `setInputFiles`. Text.Pandoc.Class now exports `setInputFiles`, `setOutputFile`. The type of `getInputFiles` has changed; it now returns `[FilePath]` instead of `Maybe [FilePath]`. Functions in Class that formerly took the source URL as a parameter now have one fewer parameter (`fetchItem`, `downloadOrRead`, `setMediaResource`, `fillMediaBag`). Removed `WriterOptions` parameter from `makeSelfContained` in `SelfContained`. --- src/Text/Pandoc/App.hs | 25 +++----- src/Text/Pandoc/Class.hs | 54 ++++++++++++------ src/Text/Pandoc/Lua/PandocModule.hs | 6 +- src/Text/Pandoc/Options.hs | 2 - src/Text/Pandoc/PDF.hs | 7 +-- src/Text/Pandoc/Readers/RST.hs | 2 +- src/Text/Pandoc/Readers/Txt2Tags.hs | 10 +--- src/Text/Pandoc/SelfContained.hs | 110 ++++++++++++++++++------------------ src/Text/Pandoc/Writers/Docx.hs | 2 +- src/Text/Pandoc/Writers/EPUB.hs | 2 +- src/Text/Pandoc/Writers/FB2.hs | 2 +- src/Text/Pandoc/Writers/ICML.hs | 2 +- src/Text/Pandoc/Writers/ODT.hs | 2 +- src/Text/Pandoc/Writers/RTF.hs | 2 +- 14 files changed, 113 insertions(+), 115 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 9b3055b35..503d7b0ac 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -78,7 +78,8 @@ import Text.Pandoc.Builder (setMeta) import Text.Pandoc.Class (PandocIO, extractMedia, fillMediaBag, getLog, setResourcePath, getMediaBag, setTrace, report, setUserDataDir, readFileStrict, readDataFile, - readDefaultDataFile, setTranslations) + readDefaultDataFile, setTranslations, + setInputFiles, setOutputFile) import Text.Pandoc.Highlighting (highlightingStyles) import Text.Pandoc.BCP47 (parseBCP47, Lang(..)) import Text.Pandoc.Lua (runLuaFilter, LuaException(..)) @@ -169,14 +170,13 @@ pdfWriterAndProg mWriter mEngine = do convertWithOpts :: Opt -> IO () convertWithOpts opts = do - let args = optInputFiles opts let outputFile = fromMaybe "-" (optOutputFile opts) let filters = optFilters opts let verbosity = optVerbosity opts when (optDumpArgs opts) $ do UTF8.hPutStrLn stdout outputFile - mapM_ (UTF8.hPutStrLn stdout) args + mapM_ (UTF8.hPutStrLn stdout) (optInputFiles opts) exitSuccess epubMetadata <- case optEpubMetadata opts of @@ -197,7 +197,7 @@ convertWithOpts opts = do let filters' = if needsCiteproc then "pandoc-citeproc" : filters else filters - let sources = case args of + let sources = case optInputFiles opts of [] -> ["-"] xs | optIgnoreArgs opts -> ["-"] | otherwise -> xs @@ -261,15 +261,6 @@ convertWithOpts opts = do _ -> e let standalone = optStandalone opts || not (isTextFormat format) || pdfOutput - let sourceURL = case sources of - [] -> Nothing - (x:_) -> case parseURI x of - Just u - | uriScheme u `elem` ["http:","https:"] -> - Just $ show u{ uriQuery = "", - uriFragment = "" } - _ -> Nothing - let addStringAsVariable varname s vars = return $ (varname, s) : vars highlightStyle <- lookupHighlightStyle $ optHighlightStyle opts @@ -347,6 +338,8 @@ convertWithOpts opts = do runIO' $ do setUserDataDir datadir + setInputFiles (optInputFiles opts) + setOutputFile (optOutputFile opts) variables <- withList (addStringAsVariable "sourcefile") @@ -449,7 +442,6 @@ convertWithOpts opts = do , writerColumns = optColumns opts , writerEmailObfuscation = optEmailObfuscation opts , writerIdentifierPrefix = optIdentifierPrefix opts - , writerSourceURL = sourceURL , writerHtmlQTags = optHtmlQTags opts , writerTopLevelDivision = optTopLevelDivision opts , writerListings = optListings opts @@ -509,7 +501,7 @@ convertWithOpts opts = do setResourcePath (optResourcePath opts) doc <- sourceToDoc sources >>= ( (if isJust (optExtractMedia opts) - then fillMediaBag (writerSourceURL writerOptions) + then fillMediaBag else return) >=> return . flip (foldr addMetadata) metadata >=> applyLuaFilters datadir (optLuaFilters opts) format @@ -545,8 +537,7 @@ convertWithOpts opts = do if optSelfContained opts && htmlFormat -- TODO not maximally efficient; change type -- of makeSelfContained so it works w/ Text - then T.pack <$> makeSelfContained writerOptions - (T.unpack output) + then T.pack <$> makeSelfContained (T.unpack output) else return output type Transform = Pandoc -> Pandoc diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index f60062d6c..cc24c1c30 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -66,7 +66,9 @@ module Text.Pandoc.Class ( PandocMonad(..) , getUserDataDir , fetchItem , getInputFiles + , setInputFiles , getOutputFile + , setOutputFile , setResourcePath , getResourcePath , PandocIO(..) @@ -251,12 +253,29 @@ insertMedia fp mime bs = do let mb' = MB.insertMedia fp mime bs mb setMediaBag mb' -getInputFiles :: PandocMonad m => m (Maybe [FilePath]) +getInputFiles :: PandocMonad m => m [FilePath] getInputFiles = getsCommonState stInputFiles +setInputFiles :: PandocMonad m => [FilePath] -> m () +setInputFiles fs = do + let sourceURL = case fs of + [] -> Nothing + (x:_) -> case parseURI x of + Just u + | uriScheme u `elem` ["http:","https:"] -> + Just $ show u{ uriQuery = "", + uriFragment = "" } + _ -> Nothing + + modifyCommonState $ \st -> st{ stInputFiles = fs + , stSourceURL = sourceURL } + getOutputFile :: PandocMonad m => m (Maybe FilePath) getOutputFile = getsCommonState stOutputFile +setOutputFile :: PandocMonad m => Maybe FilePath -> m () +setOutputFile mbf = modifyCommonState $ \st -> st{ stOutputFile = mbf } + setResourcePath :: PandocMonad m => [FilePath] -> m () setResourcePath ps = modifyCommonState $ \st -> st{stResourcePath = ps} @@ -289,12 +308,14 @@ data CommonState = CommonState { stLog :: [LogMessage] -- ^ A list of log messages in reverse order , stUserDataDir :: Maybe FilePath -- ^ Directory to search for data files + , stSourceURL :: Maybe String + -- ^ Absolute URL + dir of 1st source file , stMediaBag :: MediaBag -- ^ Media parsed from binary containers , stTranslations :: Maybe (Lang, Maybe Translations) -- ^ Translations for localization - , stInputFiles :: Maybe [FilePath] + , stInputFiles :: [FilePath] -- ^ List of input files from command line , stOutputFile :: Maybe FilePath -- ^ Output file from command line @@ -311,9 +332,10 @@ data CommonState = CommonState { stLog :: [LogMessage] instance Default CommonState where def = CommonState { stLog = [] , stUserDataDir = Nothing + , stSourceURL = Nothing , stMediaBag = mempty , stTranslations = Nothing - , stInputFiles = Nothing + , stInputFiles = [] , stOutputFile = Nothing , stResourcePath = ["."] , stVerbosity = WARNING @@ -473,20 +495,19 @@ getUserDataDir = getsCommonState stUserDataDir -- | 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 + => String -> m (B.ByteString, Maybe MimeType) -fetchItem sourceURL s = do +fetchItem s = do mediabag <- getMediaBag case lookupMedia s mediabag of Just (mime, bs) -> return (BL.toStrict bs, Just mime) - Nothing -> downloadOrRead sourceURL s + Nothing -> downloadOrRead s downloadOrRead :: PandocMonad m - => Maybe String - -> String + => String -> m (B.ByteString, Maybe MimeType) -downloadOrRead sourceURL s = +downloadOrRead s = do + sourceURL <- getsCommonState stSourceURL case (sourceURL >>= parseURIReference' . ensureEscaped, ensureEscaped s) of (Just u, s') -> -- try fetching from relative path at source @@ -637,10 +658,9 @@ withPaths (p:ps) action fp = -- | Fetch local or remote resource (like an image) and provide data suitable -- for adding it to the MediaBag. fetchMediaResource :: PandocMonad m - => Maybe String -> String - -> m (FilePath, Maybe MimeType, BL.ByteString) -fetchMediaResource sourceUrl src = do - (bs, mt) <- downloadOrRead sourceUrl src + => String -> m (FilePath, Maybe MimeType, BL.ByteString) +fetchMediaResource src = do + (bs, mt) <- downloadOrRead src let ext = fromMaybe (takeExtension src) (mt >>= extensionFromMimeType) let bs' = BL.fromChunks [bs] @@ -650,15 +670,15 @@ fetchMediaResource sourceUrl src = do -- | Traverse tree, filling media bag for any images that -- aren't already in the media bag. -fillMediaBag :: PandocMonad m => Maybe String -> Pandoc -> m Pandoc -fillMediaBag sourceURL d = walkM handleImage d +fillMediaBag :: PandocMonad m => Pandoc -> m Pandoc +fillMediaBag d = walkM handleImage d where handleImage :: PandocMonad m => Inline -> m Inline handleImage (Image attr lab (src, tit)) = catchError (do mediabag <- getMediaBag case lookupMedia src mediabag of Just (_, _) -> return $ Image attr lab (src, tit) Nothing -> do - (fname, mt, bs) <- fetchMediaResource sourceURL src + (fname, mt, bs) <- fetchMediaResource src insertMedia fname mt bs return $ Image attr lab (fname, tit)) (\e -> diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index ffd681d30..326de1886 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -136,11 +136,9 @@ mediaDirectoryFn mbRef = do insertResource :: IORef MB.MediaBag -> String - -> OrNil String -> Lua NumResults -insertResource mbRef src sourceUrlOrNil = do - (fp, mimeType, bs) <- liftIO . runIOorExplode $ - fetchMediaResource (toMaybe sourceUrlOrNil) src +insertResource mbRef src = do + (fp, mimeType, bs) <- liftIO . runIOorExplode $ fetchMediaResource src liftIO $ print (fp, mimeType) insertMediaFn mbRef fp (OrNil mimeType) bs diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index 345245855..f936658f4 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -207,7 +207,6 @@ data WriterOptions = WriterOptions , writerEmailObfuscation :: ObfuscationMethod -- ^ How to obfuscate emails , writerIdentifierPrefix :: String -- ^ Prefix for section & note ids in HTML -- and for footnote marks in markdown - , writerSourceURL :: Maybe String -- ^ Absolute URL + directory of 1st source file , writerCiteMethod :: CiteMethod -- ^ How to print cites , writerHtmlQTags :: Bool -- ^ Use @<q>@ tags for quotes in HTML , writerSlideLevel :: Maybe Int -- ^ Force header level of slides @@ -244,7 +243,6 @@ instance Default WriterOptions where , writerColumns = 72 , writerEmailObfuscation = NoObfuscation , writerIdentifierPrefix = "" - , writerSourceURL = Nothing , writerCiteMethod = Citeproc , writerHtmlQTags = False , writerSlideLevel = Nothing diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index b2b7da54f..26f831c6d 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -129,7 +129,7 @@ makePDF program writer opts verbosity mediabag doc = do else withTempDir resourcePath <- getResourcePath liftIO $ withTemp "tex2pdf." $ \tmpdir -> do - doc' <- handleImages verbosity opts resourcePath mediabag tmpdir doc + doc' <- handleImages verbosity resourcePath mediabag tmpdir doc source <- runIOorExplode $ do setVerbosity verbosity writer opts doc' @@ -141,18 +141,17 @@ makePDF program writer opts verbosity mediabag doc = do _ -> return $ Left $ UTF8.fromStringLazy $ "Unknown program " ++ program handleImages :: Verbosity - -> WriterOptions -> [FilePath] -> MediaBag -> FilePath -- ^ temp dir to store images -> Pandoc -- ^ document -> IO Pandoc -handleImages verbosity opts resourcePath mediabag tmpdir doc = do +handleImages verbosity resourcePath mediabag tmpdir doc = do doc' <- runIOorExplode $ do setVerbosity verbosity setResourcePath resourcePath setMediaBag mediabag - fillMediaBag (writerSourceURL opts) doc >>= + fillMediaBag doc >>= extractMedia tmpdir walkM (convertImages verbosity tmpdir) doc' diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index daaeff2f0..2d6bb979f 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -850,7 +850,7 @@ csvTableDirective top fields rawcsv = do rawcsv' <- case trim <$> lookup "file" fields `mplus` lookup "url" fields of Just u -> do - (bs, _) <- fetchItem Nothing u + (bs, _) <- fetchItem u return $ UTF8.toString bs Nothing -> return rawcsv let res = parseCSV opts (T.pack $ case explicitHeader of diff --git a/src/Text/Pandoc/Readers/Txt2Tags.hs b/src/Text/Pandoc/Readers/Txt2Tags.hs index f000646c2..2d3e541cf 100644 --- a/src/Text/Pandoc/Readers/Txt2Tags.hs +++ b/src/Text/Pandoc/Readers/Txt2Tags.hs @@ -70,14 +70,8 @@ instance Default T2TMeta where -- | Get the meta information required by Txt2Tags macros 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 -> "" + inps <- P.getInputFiles + outp <- fromMaybe "" <$> P.getOutputFile curDate <- formatTime defaultTimeLocale "%F" <$> P.getZonedTime let getModTime = fmap (formatTime defaultTimeLocale "%T") . P.getModificationTime diff --git a/src/Text/Pandoc/SelfContained.hs b/src/Text/Pandoc/SelfContained.hs index 55df147b6..787ea1954 100644 --- a/src/Text/Pandoc/SelfContained.hs +++ b/src/Text/Pandoc/SelfContained.hs @@ -42,10 +42,11 @@ import qualified Data.ByteString.Char8 as B import qualified Data.ByteString.Lazy as L import Data.Char (isAlphaNum, isAscii, toLower) import Data.List (isPrefixOf) -import Network.URI (URI (..), escapeURIString, parseURI) +import Network.URI (escapeURIString) import System.FilePath (takeDirectory, takeExtension, (</>)) import Text.HTML.TagSoup -import Text.Pandoc.Class (PandocMonad (..), fetchItem, report) +import Text.Pandoc.Class (PandocMonad (..), fetchItem, report, + getInputFiles, setInputFiles) import Text.Pandoc.Error import Text.Pandoc.Logging import Text.Pandoc.MIME (MimeType) @@ -68,29 +69,29 @@ makeDataURI (mime, raw) = then mime ++ ";charset=utf-8" else mime -- mime type already has charset -convertTags :: PandocMonad m => Maybe String -> [Tag String] -> m [Tag String] -convertTags _ [] = return [] -convertTags sourceURL (t@TagOpen{}:ts) - | fromAttrib "data-external" t == "1" = (t:) <$> convertTags sourceURL ts -convertTags sourceURL (t@(TagOpen tagname as):ts) +convertTags :: PandocMonad m => [Tag String] -> m [Tag String] +convertTags [] = return [] +convertTags (t@TagOpen{}:ts) + | fromAttrib "data-external" t == "1" = (t:) <$> convertTags ts +convertTags (t@(TagOpen tagname as):ts) | tagname `elem` ["img", "embed", "video", "input", "audio", "source", "track"] = do as' <- mapM processAttribute as - rest <- convertTags sourceURL ts + rest <- convertTags ts return $ TagOpen tagname as' : rest where processAttribute (x,y) = if x == "src" || x == "data-src" || x == "href" || x == "poster" then do - enc <- getDataURI sourceURL (fromAttrib "type" t) y + enc <- getDataURI (fromAttrib "type" t) y return (x, enc) else return (x,y) -convertTags sourceURL (t@(TagOpen "script" as):TagClose "script":ts) = +convertTags (t@(TagOpen "script" as):TagClose "script":ts) = case fromAttrib "src" t of - [] -> (t:) <$> convertTags sourceURL ts + [] -> (t:) <$> convertTags ts src -> do let typeAttr = fromAttrib "type" t - res <- getData sourceURL typeAttr src - rest <- convertTags sourceURL ts + res <- getData typeAttr src + rest <- convertTags ts case res of Left dataUri -> return $ TagOpen "script" (("src",dataUri) : [(x,y) | (x,y) <- as, x /= "src"]) : @@ -110,21 +111,21 @@ convertTags sourceURL (t@(TagOpen "script" as):TagClose "script":ts) = (("src",makeDataURI (mime, bs)) : [(x,y) | (x,y) <- as, x /= "src"]) : TagClose "script" : rest -convertTags sourceURL (t@(TagOpen "link" as):ts) = +convertTags (t@(TagOpen "link" as):ts) = case fromAttrib "href" t of - [] -> (t:) <$> convertTags sourceURL ts + [] -> (t:) <$> convertTags ts src -> do - res <- getData sourceURL (fromAttrib "type" t) src + res <- getData (fromAttrib "type" t) src case res of Left dataUri -> do - rest <- convertTags sourceURL ts + rest <- convertTags ts return $ TagOpen "link" (("href",dataUri) : [(x,y) | (x,y) <- as, x /= "href"]) : rest Right (mime, bs) | "text/css" `isPrefixOf` mime && not ("</" `B.isInfixOf` bs) -> do - rest <- convertTags sourceURL $ + rest <- convertTags $ dropWhile (==TagClose "link") ts return $ TagOpen "style" [("type", mime)] @@ -132,16 +133,16 @@ convertTags sourceURL (t@(TagOpen "link" as):ts) = : TagClose "style" : rest | otherwise -> do - rest <- convertTags sourceURL ts + rest <- convertTags ts return $ TagOpen "link" (("href",makeDataURI (mime, bs)) : [(x,y) | (x,y) <- as, x /= "href"]) : rest -convertTags sourceURL (t:ts) = (t:) <$> convertTags sourceURL ts +convertTags (t:ts) = (t:) <$> convertTags ts cssURLs :: PandocMonad m - => Maybe String -> FilePath -> ByteString -> m ByteString -cssURLs sourceURL d orig = do - res <- runParserT (parseCSSUrls sourceURL d) () "css" orig + => FilePath -> ByteString -> m ByteString +cssURLs d orig = do + res <- runParserT (parseCSSUrls d) () "css" orig case res of Left e -> do report $ CouldNotParseCSS (show e) @@ -149,17 +150,16 @@ cssURLs sourceURL d orig = do Right bs -> return bs parseCSSUrls :: PandocMonad m - => Maybe String -> FilePath -> ParsecT ByteString () m ByteString -parseCSSUrls sourceURL d = B.concat <$> P.many - (pCSSWhite <|> pCSSComment <|> pCSSImport sourceURL d <|> - pCSSUrl sourceURL d <|> pCSSOther) + => FilePath -> ParsecT ByteString () m ByteString +parseCSSUrls d = B.concat <$> P.many + (pCSSWhite <|> pCSSComment <|> pCSSImport d <|> pCSSUrl d <|> pCSSOther) -pCSSImport :: PandocMonad m => Maybe String -> FilePath - -> ParsecT ByteString () m ByteString -pCSSImport sourceURL d = P.try $ do +pCSSImport :: PandocMonad m + => FilePath -> ParsecT ByteString () m ByteString +pCSSImport d = P.try $ do P.string "@import" P.spaces - res <- (pQuoted <|> pUrl) >>= handleCSSUrl sourceURL d + res <- (pQuoted <|> pUrl) >>= handleCSSUrl d P.spaces P.char ';' P.spaces @@ -184,9 +184,9 @@ pCSSOther = do (B.singleton <$> P.char '/') pCSSUrl :: PandocMonad m - => Maybe String -> FilePath -> ParsecT ByteString () m ByteString -pCSSUrl sourceURL d = P.try $ do - res <- pUrl >>= handleCSSUrl sourceURL d + => FilePath -> ParsecT ByteString () m ByteString +pCSSUrl d = P.try $ do + res <- pUrl >>= handleCSSUrl d case res of Left b -> return b Right (mt,b) -> do @@ -215,41 +215,41 @@ pUrl = P.try $ do return (url, fallback) handleCSSUrl :: PandocMonad m - => Maybe String -> FilePath -> (String, ByteString) + => FilePath -> (String, ByteString) -> ParsecT ByteString () m (Either ByteString (MimeType, ByteString)) -handleCSSUrl sourceURL d (url, fallback) = do +handleCSSUrl d (url, fallback) = do -- pipes are used in URLs provided by Google Code fonts -- but parseURI doesn't like them, so we escape them: case escapeURIString (/='|') (trim url) of '#':_ -> return $ Left fallback 'd':'a':'t':'a':':':_ -> return $ Left fallback u -> do let url' = if isURI u then u else d </> u - res <- lift $ getData sourceURL "" url' + res <- lift $ getData "" url' case res of Left uri -> return $ Left (B.pack $ "url(" ++ uri ++ ")") Right (mt, raw) -> do -- note that the downloaded CSS may -- itself contain url(...). b <- if "text/css" `isPrefixOf` mt - then cssURLs sourceURL d raw + then cssURLs d raw else return raw return $ Right (mt, b) -getDataURI :: PandocMonad m => Maybe String -> MimeType -> String -> m String -getDataURI sourceURL mimetype src = do - res <- getData sourceURL mimetype src +getDataURI :: PandocMonad m => MimeType -> String -> m String +getDataURI mimetype src = do + res <- getData mimetype src case res of Left uri -> return uri Right x -> return $ makeDataURI x getData :: PandocMonad m - => Maybe String -> MimeType -> String + => MimeType -> String -> m (Either String (MimeType, ByteString)) -getData _ _ src@('d':'a':'t':'a':':':_) = return $ Left src-- already data: uri -getData sourceURL mimetype src = do +getData _ src@('d':'a':'t':'a':':':_) = return $ Left src-- already data: uri +getData mimetype src = do let ext = map toLower $ takeExtension src - (raw, respMime) <- fetchItem sourceURL src + (raw, respMime) <- fetchItem src let raw' = if ext == ".gz" then B.concat $ L.toChunks $ Gzip.decompress $ L.fromChunks $ [raw] @@ -259,15 +259,13 @@ getData sourceURL mimetype src = do $ "Could not determine mime type for `" ++ src ++ "'" (x, Nothing) -> return x (_, Just x ) -> return x - let cssSourceURL = case parseURI src of - Just u - | uriScheme u `elem` ["http:","https:"] -> - Just $ show u{ uriPath = "", - uriQuery = "", - uriFragment = "" } - _ -> Nothing result <- if "text/css" `isPrefixOf` mime - then cssURLs cssSourceURL (takeDirectory src) raw' + then do + oldInputs <- getInputFiles + setInputFiles [src] + res <- cssURLs (takeDirectory src) raw' + setInputFiles oldInputs + return res else return raw' return $ Right (mime, result) @@ -275,8 +273,8 @@ getData sourceURL mimetype src = do -- | Convert HTML into self-contained HTML, incorporating images, -- scripts, and CSS using data: URIs. -makeSelfContained :: PandocMonad m => WriterOptions -> String -> m String -makeSelfContained opts inp = do +makeSelfContained :: PandocMonad m => String -> m String +makeSelfContained inp = do let tags = parseTags inp - out' <- convertTags (writerSourceURL opts) tags + out' <- convertTags tags return $ renderTags' out' diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 3d6eb9fe5..6102d97ed 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -1295,7 +1295,7 @@ inlineToOpenXML' opts (Image attr alt (src, title)) = do Just (_,_,_,elt,_) -> return [elt] Nothing -> do catchError - (do (img, mt) <- P.fetchItem (writerSourceURL opts) src + (do (img, mt) <- P.fetchItem src ident <- ("rId"++) `fmap` ((lift . lift) getUniqueId) let (xpt,ypt) = desiredSizeInPoints opts attr (either (const def) id (imageSize opts img)) diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index 04126fbb7..6bae65b6b 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -918,7 +918,7 @@ modifyMediaRef opts oldsrc = do case lookup oldsrc media of Just (n,_) -> return n Nothing -> catchError - (do (img, mbMime) <- P.fetchItem (writerSourceURL opts) oldsrc + (do (img, mbMime) <- P.fetchItem oldsrc let new = "media/file" ++ show (length media) ++ fromMaybe (takeExtension (takeWhile (/='?') oldsrc)) (('.':) <$> (mbMime >>= extensionFromMimeType)) diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index 4c764d987..36c572b63 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -255,7 +255,7 @@ fetchImage href link = do else return Nothing (True, Just _) -> return Nothing -- not base64-encoded _ -> do - catchError (do (bs, mbmime) <- P.fetchItem Nothing link + catchError (do (bs, mbmime) <- P.fetchItem link case mbmime of Nothing -> do report $ CouldNotDetermineMimeType link diff --git a/src/Text/Pandoc/Writers/ICML.hs b/src/Text/Pandoc/Writers/ICML.hs index 37df58e65..650a1c012 100644 --- a/src/Text/Pandoc/Writers/ICML.hs +++ b/src/Text/Pandoc/Writers/ICML.hs @@ -543,7 +543,7 @@ styleToStrAttr style = imageICML :: PandocMonad m => WriterOptions -> Style -> Attr -> Target -> WS m Doc imageICML opts style attr (src, _) = do imgS <- catchError - (do (img, _) <- P.fetchItem (writerSourceURL opts) src + (do (img, _) <- P.fetchItem src case imageSize opts img of Right size -> return size Left msg -> do diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index 4c74ef469..90b7c3501 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -180,7 +180,7 @@ addLang lang = everywhere' (mkT updateLangAttr) -- | transform both Image and Math elements transformPicMath :: PandocMonad m => WriterOptions ->Inline -> O m Inline transformPicMath opts (Image attr@(id', cls, _) lab (src,t)) = catchError - (do (img, mbMimeType) <- P.fetchItem (writerSourceURL opts) src + (do (img, mbMimeType) <- P.fetchItem src (ptX, ptY) <- case imageSize opts img of Right s -> return $ sizeInPoints s Left msg -> do diff --git a/src/Text/Pandoc/Writers/RTF.hs b/src/Text/Pandoc/Writers/RTF.hs index 48d31c7bf..d4de3112c 100644 --- a/src/Text/Pandoc/Writers/RTF.hs +++ b/src/Text/Pandoc/Writers/RTF.hs @@ -56,7 +56,7 @@ import Text.Printf (printf) -- 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,_)) = catchError - (do result <- P.fetchItem (writerSourceURL opts) src + (do result <- P.fetchItem src case result of (imgdata, Just mime) | mime == "image/jpeg" || mime == "image/png" -> do -- cgit v1.2.3 From 9ec458f39f9d79e9523330e6536f5f92aa91ec43 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 30 Sep 2017 17:15:55 -0500 Subject: Text.Pandoc.Class - add getVerbosity. --- src/Text/Pandoc/Class.hs | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index cc24c1c30..2b1943140 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -59,6 +59,7 @@ module Text.Pandoc.Class ( PandocMonad(..) , setTrace , getLog , setVerbosity + , getVerbosity , getMediaBag , setMediaBag , insertMedia @@ -217,6 +218,10 @@ setVerbosity :: PandocMonad m => Verbosity -> m () setVerbosity verbosity = modifyCommonState $ \st -> st{ stVerbosity = verbosity } +-- | Get the verbosity level. +getVerbosity :: PandocMonad m => m Verbosity +getVerbosity = getsCommonState stVerbosity + -- Get the accomulated log messages (in temporal order). getLog :: PandocMonad m => m [LogMessage] getLog = reverse <$> getsCommonState stLog -- cgit v1.2.3 From dfe816163c3122cff03e2d7d7b286a60916587fb Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 30 Sep 2017 17:17:04 -0500 Subject: Removed Verbosity and MediaBag params from makePDF. They can be obtained from CommonState since we're in PandocIO. --- src/Text/Pandoc/App.hs | 5 ++--- src/Text/Pandoc/PDF.hs | 60 +++++++++++++++++++++++--------------------------- 2 files changed, 29 insertions(+), 36 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 503d7b0ac..57a91581b 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -76,7 +76,7 @@ import qualified System.IO as IO (Newline (..)) import Text.Pandoc import Text.Pandoc.Builder (setMeta) import Text.Pandoc.Class (PandocIO, extractMedia, fillMediaBag, getLog, - setResourcePath, getMediaBag, setTrace, report, + setResourcePath, setTrace, report, setUserDataDir, readFileStrict, readDataFile, readDefaultDataFile, setTranslations, setInputFiles, setOutputFile) @@ -509,13 +509,12 @@ convertWithOpts opts = do >=> applyTransforms transforms >=> applyFilters readerOpts datadir filters' [format] ) - media <- getMediaBag case writer of ByteStringWriter f -> f writerOptions doc >>= writeFnBinary outputFile TextWriter f -> case maybePdfProg of Just pdfProg -> do - res <- makePDF pdfProg f writerOptions verbosity media doc + res <- makePDF pdfProg f writerOptions doc case res of Right pdf -> writeFnBinary outputFile pdf Left err' -> liftIO $ diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index 26f831c6d..797b5c138 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -58,7 +58,6 @@ import System.IO.Error (isDoesNotExistError) #endif import Text.Pandoc.Definition import Text.Pandoc.Error (PandocError(PandocPDFProgramNotFoundError)) -import Text.Pandoc.MediaBag import Text.Pandoc.MIME (getMimeType) import Text.Pandoc.Options (HTMLMathMethod (..), WriterOptions (..)) import Text.Pandoc.Process (pipeProcess) @@ -70,8 +69,9 @@ import Text.Pandoc.Writers.Shared (getField, metaToJSON) import Data.List (intercalate) #endif import Text.Pandoc.Class (PandocIO, report, runIO, runIOorExplode, - setMediaBag, setVerbosity, getResourcePath, - setResourcePath, fillMediaBag, extractMedia) + setVerbosity, getVerbosity, + fillMediaBag, extractMedia, putCommonState, + getCommonState) import Text.Pandoc.Logging #ifdef _WINDOWS @@ -83,11 +83,9 @@ makePDF :: String -- ^ pdf creator (pdflatex, lualatex, xelatex, -- wkhtmltopdf, weasyprint, prince, context, pdfroff) -> (WriterOptions -> Pandoc -> PandocIO Text) -- ^ writer -> WriterOptions -- ^ options - -> Verbosity -- ^ verbosity level - -> MediaBag -- ^ media -> Pandoc -- ^ document -> PandocIO (Either ByteString ByteString) -makePDF "wkhtmltopdf" writer opts verbosity _ doc@(Pandoc meta _) = do +makePDF "wkhtmltopdf" writer opts doc@(Pandoc meta _) = 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' });", @@ -109,29 +107,34 @@ makePDF "wkhtmltopdf" writer opts verbosity _ doc@(Pandoc meta _) = do (getField "margin-left" meta')) ] source <- writer opts doc + verbosity <- getVerbosity liftIO $ html2pdf verbosity "wkhtmltopdf" args source -makePDF "weasyprint" writer opts verbosity _ doc = do +makePDF "weasyprint" writer opts doc = do let args = writerPdfArgs opts source <- writer opts doc + verbosity <- getVerbosity liftIO $ html2pdf verbosity "weasyprint" args source -makePDF "prince" writer opts verbosity _ doc = do +makePDF "prince" writer opts doc = do let args = writerPdfArgs opts source <- writer opts doc + verbosity <- getVerbosity liftIO $ html2pdf verbosity "prince" args source -makePDF "pdfroff" writer opts verbosity _mediabag doc = do +makePDF "pdfroff" writer opts doc = do source <- writer opts doc let args = ["-ms", "-mpdfmark", "-e", "-t", "-k", "-KUTF-8", "-i", "--no-toc-relocation"] ++ writerPdfArgs opts + verbosity <- getVerbosity liftIO $ ms2pdf verbosity args source -makePDF program writer opts verbosity mediabag doc = do +makePDF program writer opts doc = do let withTemp = if takeBaseName program == "context" then withTempDirectory "." else withTempDir - resourcePath <- getResourcePath + commonState <- getCommonState + verbosity <- getVerbosity liftIO $ withTemp "tex2pdf." $ \tmpdir -> do - doc' <- handleImages verbosity resourcePath mediabag tmpdir doc source <- runIOorExplode $ do - setVerbosity verbosity + putCommonState commonState + doc' <- handleImages tmpdir doc writer opts doc' let args = writerPdfArgs opts case takeBaseName program of @@ -140,34 +143,25 @@ makePDF program writer opts verbosity mediabag doc = do -> tex2pdf' verbosity args tmpdir program source _ -> return $ Left $ UTF8.fromStringLazy $ "Unknown program " ++ program -handleImages :: Verbosity - -> [FilePath] - -> MediaBag - -> FilePath -- ^ temp dir to store images +handleImages :: FilePath -- ^ temp dir to store images -> Pandoc -- ^ document - -> IO Pandoc -handleImages verbosity resourcePath mediabag tmpdir doc = do - doc' <- runIOorExplode $ do - setVerbosity verbosity - setResourcePath resourcePath - setMediaBag mediabag - fillMediaBag doc >>= - extractMedia tmpdir - walkM (convertImages verbosity tmpdir) doc' + -> PandocIO Pandoc +handleImages tmpdir doc = + fillMediaBag doc >>= + extractMedia tmpdir >>= + walkM (convertImages tmpdir) -convertImages :: Verbosity -> FilePath -> Inline -> IO Inline -convertImages verbosity tmpdir (Image attr ils (src, tit)) = do - img <- convertImage tmpdir src +convertImages :: FilePath -> Inline -> PandocIO Inline +convertImages tmpdir (Image attr ils (src, tit)) = do + img <- liftIO $ convertImage tmpdir src newPath <- case img of Left e -> do - runIO $ do - setVerbosity verbosity - report $ CouldNotConvertImage src e + report $ CouldNotConvertImage src e return src Right fp -> return fp return (Image attr ils (newPath, tit)) -convertImages _ _ x = return x +convertImages _ x = return x -- Convert formats which do not work well in pdf to png convertImage :: FilePath -> FilePath -> IO (Either String FilePath) -- cgit v1.2.3 From 9004da4587b41b0f24a1a646a03fa9a092e50864 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 30 Sep 2017 17:18:37 -0500 Subject: Removed unneeded import. --- src/Text/Pandoc/SelfContained.hs | 1 - 1 file changed, 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/SelfContained.hs b/src/Text/Pandoc/SelfContained.hs index 787ea1954..9ab7be6b9 100644 --- a/src/Text/Pandoc/SelfContained.hs +++ b/src/Text/Pandoc/SelfContained.hs @@ -50,7 +50,6 @@ import Text.Pandoc.Class (PandocMonad (..), fetchItem, report, import Text.Pandoc.Error import Text.Pandoc.Logging import Text.Pandoc.MIME (MimeType) -import Text.Pandoc.Options (WriterOptions (..)) import Text.Pandoc.Shared (isURI, renderTags', trim) import Text.Pandoc.UTF8 (toString) import Text.Parsec (ParsecT, runParserT) -- cgit v1.2.3 From 99aba1e7886d27ede519dede87652e0bda9a1d08 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 30 Sep 2017 17:19:07 -0500 Subject: EPUB writer: simplified some functions. --- src/Text/Pandoc/Writers/EPUB.hs | 42 ++++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 24 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index 6bae65b6b..c75845fa9 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -433,7 +433,7 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do -- handle pictures -- mediaRef <- P.newIORef [] Pandoc _ blocks <- walkM (transformInline opts') doc >>= - walkM (transformBlock opts') + walkM transformBlock picEntries <- (catMaybes . map (snd . snd)) <$> (gets stMediaPaths) -- handle fonts let matchingGlob f = do @@ -891,29 +891,26 @@ showDateTimeISO8601 :: UTCTime -> String showDateTimeISO8601 = formatTime defaultTimeLocale "%FT%TZ" transformTag :: PandocMonad m - => WriterOptions - -- -> IORef [(FilePath, (FilePath, Maybe Entry))] -- ^ (oldpath, newpath, entry) media - -> Tag String + => Tag String -> E m (Tag String) -transformTag opts tag@(TagOpen name attr) +transformTag 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 src - newposter <- modifyMediaRef opts poster + newsrc <- modifyMediaRef src + newposter <- modifyMediaRef 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 :: PandocMonad m - => WriterOptions - -> FilePath + => FilePath -> E m FilePath -modifyMediaRef _ "" = return "" -modifyMediaRef opts oldsrc = do +modifyMediaRef "" = return "" +modifyMediaRef oldsrc = do media <- gets stMediaPaths case lookup oldsrc media of Just (n,_) -> return n @@ -932,35 +929,32 @@ modifyMediaRef opts oldsrc = do return oldsrc) transformBlock :: PandocMonad m - => WriterOptions - -- -> IORef [(FilePath, (FilePath, Maybe Entry))] -- ^ (oldpath, newpath, entry) media - -> Block + => Block -> E m Block -transformBlock opts (RawBlock fmt raw) +transformBlock (RawBlock fmt raw) | fmt == Format "html" = do let tags = parseTags raw - tags' <- mapM (transformTag opts) tags + tags' <- mapM transformTag tags return $ RawBlock fmt (renderTags' tags') -transformBlock _ b = return b +transformBlock b = return b transformInline :: PandocMonad m => WriterOptions - -- -> IORef [(FilePath, (FilePath, Maybe Entry))] -- ^ (oldpath, newpath) media -> Inline -> E m Inline -transformInline opts (Image attr lab (src,tit)) = do - newsrc <- modifyMediaRef opts src +transformInline _opts (Image attr lab (src,tit)) = do + newsrc <- modifyMediaRef src return $ Image attr lab ("../" ++ newsrc, tit) transformInline opts (x@(Math t m)) | WebTeX url <- writerHTMLMathMethod opts = do - newsrc <- modifyMediaRef opts (url ++ urlEncode m) + newsrc <- modifyMediaRef (url ++ urlEncode m) let mathclass = if t == DisplayMath then "display" else "inline" return $ Span ("",["math",mathclass],[]) [Image nullAttr [x] ("../" ++ newsrc, "")] -transformInline opts (RawInline fmt raw) +transformInline _opts (RawInline fmt raw) | fmt == Format "html" = do let tags = parseTags raw - tags' <- mapM (transformTag opts) tags + tags' <- mapM transformTag tags return $ RawInline fmt (renderTags' tags') transformInline _ x = return x -- cgit v1.2.3 From 896c288625a8c48e290fe86e90b65109bd4fce9f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 30 Sep 2017 17:19:39 -0500 Subject: Lua filters: make sure whole CommonState is passed through... to insertResource (`fetch`). --- src/Text/Pandoc/Lua.hs | 13 ++++++++----- src/Text/Pandoc/Lua/PandocModule.hs | 23 ++++++++++++++--------- 2 files changed, 22 insertions(+), 14 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index f7e74d0a8..2e4204898 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -44,7 +44,8 @@ import Data.Map (Map) import Data.Maybe (isJust) import Foreign.Lua (Lua, FromLuaStack (peek), LuaException (..), StackIndex, Status (OK), ToLuaStack (push)) -import Text.Pandoc.Class (PandocIO, getMediaBag, setMediaBag) +import Text.Pandoc.Class (PandocIO, getMediaBag, setMediaBag, + getCommonState, CommonState) import Text.Pandoc.MediaBag (MediaBag) import Text.Pandoc.Definition import Text.Pandoc.Lua.PandocModule (pushPandocModule, pushMediaBagModule) @@ -56,17 +57,19 @@ import qualified Foreign.Lua as Lua runLuaFilter :: Maybe FilePath -> FilePath -> String -> Pandoc -> PandocIO (Either LuaException Pandoc) runLuaFilter datadir filterPath format pd = do + commonState <- getCommonState mediaBag <- getMediaBag mediaBagRef <- liftIO (newIORef mediaBag) res <- liftIO . Lua.runLuaEither $ - runLuaFilter' datadir filterPath format mediaBagRef pd + runLuaFilter' commonState datadir filterPath format mediaBagRef pd newMediaBag <- liftIO (readIORef mediaBagRef) setMediaBag newMediaBag return res -runLuaFilter' :: Maybe FilePath -> FilePath -> String -> IORef MediaBag +runLuaFilter' :: CommonState + -> Maybe FilePath -> FilePath -> String -> IORef MediaBag -> Pandoc -> Lua Pandoc -runLuaFilter' datadir filterPath format mbRef pd = do +runLuaFilter' commonState datadir filterPath format mbRef pd = do Lua.openlibs -- store module in global "pandoc" pushPandocModule datadir @@ -89,7 +92,7 @@ runLuaFilter' datadir filterPath format mbRef pd = do addMediaBagModule = do Lua.getglobal "pandoc" push "mediabag" - pushMediaBagModule mbRef + pushMediaBagModule commonState mbRef Lua.rawset (-3) registerFormat = do push format diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index 326de1886..bf45cab17 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -41,7 +41,8 @@ import Data.IORef import Data.Text (pack) import Foreign.Lua (Lua, FromLuaStack, ToLuaStack, NumResults, liftIO) import Text.Pandoc.Class (fetchMediaResource, readDataFile, runIO, - runIOorExplode, setUserDataDir) + runIOorExplode, setUserDataDir, CommonState(..), + putCommonState) import Text.Pandoc.Options (ReaderOptions(readerExtensions)) import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Readers (Reader (..), getReader) @@ -83,13 +84,13 @@ readDoc formatSpec content = do -- -- MediaBag submodule -- -pushMediaBagModule :: IORef MB.MediaBag -> Lua () -pushMediaBagModule mediaBagRef = do +pushMediaBagModule :: CommonState -> IORef MB.MediaBag -> Lua () +pushMediaBagModule commonState mediaBagRef = do Lua.newtable addFunction "insert" (insertMediaFn mediaBagRef) addFunction "lookup" (lookupMediaFn mediaBagRef) addFunction "list" (mediaDirectoryFn mediaBagRef) - addFunction "fetch" (insertResource mediaBagRef) + addFunction "fetch" (insertResource commonState mediaBagRef) return () where addFunction name fn = do @@ -103,7 +104,8 @@ insertMediaFn :: IORef MB.MediaBag -> BL.ByteString -> Lua NumResults insertMediaFn mbRef fp nilOrMime contents = do - liftIO . modifyIORef' mbRef $ MB.insertMedia fp (toMaybe nilOrMime) contents + liftIO . modifyIORef' mbRef $ + MB.insertMedia fp (toMaybe nilOrMime) contents return 0 lookupMediaFn :: IORef MB.MediaBag @@ -134,12 +136,15 @@ mediaDirectoryFn mbRef = do Lua.push "length" *> Lua.push contentLength *> Lua.rawset (-3) Lua.rawseti (-2) idx -insertResource :: IORef MB.MediaBag +insertResource :: CommonState + -> IORef MB.MediaBag -> String -> Lua NumResults -insertResource mbRef src = do - (fp, mimeType, bs) <- liftIO . runIOorExplode $ fetchMediaResource src - liftIO $ print (fp, mimeType) +insertResource commonState mbRef src = do + (fp, mimeType, bs) <- liftIO . runIOorExplode $ do + putCommonState commonState + fetchMediaResource src + liftIO $ print (fp, mimeType) -- TODO DEBUG insertMediaFn mbRef fp (OrNil mimeType) bs -- -- cgit v1.2.3 From 73c47a44d86f5075e3635e90574de12ac5f0b2eb Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 30 Sep 2017 21:58:35 -0700 Subject: Lua: make lua.mediabag.fetch return filename and mime type. This is necessary because you may need to insert the filename into an image or link element. --- src/Text/Pandoc/Lua/PandocModule.hs | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index bf45cab17..a110905e5 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -38,6 +38,7 @@ import Control.Monad (unless, zipWithM_) import Data.ByteString.Char8 (unpack) import Data.Default (Default (..)) import Data.IORef +import Data.Maybe (fromMaybe) import Data.Text (pack) import Foreign.Lua (Lua, FromLuaStack, ToLuaStack, NumResults, liftIO) import Text.Pandoc.Class (fetchMediaResource, readDataFile, runIO, @@ -146,6 +147,9 @@ insertResource commonState mbRef src = do fetchMediaResource src liftIO $ print (fp, mimeType) -- TODO DEBUG insertMediaFn mbRef fp (OrNil mimeType) bs + Lua.push fp + Lua.push $ fromMaybe "" mimeType + return 2 -- returns 2 values: name in mediabag, mimetype -- -- Helper types and orphan instances -- cgit v1.2.3 From 17583cd99d0ea2cb4a5dcb3eecf2735395ebc3d6 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 30 Sep 2017 22:54:12 -0700 Subject: Lua: simply mediabag module. Now 'fetch' simply fetches content and mime type. A new 'hashname' function is provided to get a filename based on the sha1 hash of the contents and the mime type. --- src/Text/Pandoc/Lua/PandocModule.hs | 46 +++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 15 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index a110905e5..f27d6f45e 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -41,13 +41,14 @@ import Data.IORef import Data.Maybe (fromMaybe) import Data.Text (pack) import Foreign.Lua (Lua, FromLuaStack, ToLuaStack, NumResults, liftIO) -import Text.Pandoc.Class (fetchMediaResource, readDataFile, runIO, +import Text.Pandoc.Class (readDataFile, runIO, runIOorExplode, setUserDataDir, CommonState(..), - putCommonState) + putCommonState, fetchItem, setMediaBag) import Text.Pandoc.Options (ReaderOptions(readerExtensions)) import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Readers (Reader (..), getReader) -import Text.Pandoc.MIME (MimeType) +import Text.Pandoc.MIME (MimeType, extensionFromMimeType) +import Data.Digest.Pure.SHA (sha1, showDigest) import qualified Foreign.Lua as Lua import qualified Data.ByteString.Lazy as BL @@ -91,7 +92,8 @@ pushMediaBagModule commonState mediaBagRef = do addFunction "insert" (insertMediaFn mediaBagRef) addFunction "lookup" (lookupMediaFn mediaBagRef) addFunction "list" (mediaDirectoryFn mediaBagRef) - addFunction "fetch" (insertResource commonState mediaBagRef) + addFunction "fetch" (fetch commonState mediaBagRef) + addFunction "hashname" hashnameFn return () where addFunction name fn = do @@ -99,6 +101,20 @@ pushMediaBagModule commonState mediaBagRef = do Lua.pushHaskellFunction fn Lua.rawset (-3) +hashnameFn :: OrNil MimeType + -> BL.ByteString + -> Lua NumResults +hashnameFn nilOrMime contents = do + Lua.push (getHashname (toMaybe nilOrMime) contents) + return 1 + +getHashname :: Maybe MimeType -> BL.ByteString -> String +getHashname mbMime bs = + let ext = fromMaybe "" + (('.':) <$> (mbMime >>= extensionFromMimeType)) + basename = showDigest $ sha1 bs + in basename ++ ext + insertMediaFn :: IORef MB.MediaBag -> FilePath -> OrNil MimeType @@ -137,19 +153,19 @@ mediaDirectoryFn mbRef = do Lua.push "length" *> Lua.push contentLength *> Lua.rawset (-3) Lua.rawseti (-2) idx -insertResource :: CommonState - -> IORef MB.MediaBag - -> String - -> Lua NumResults -insertResource commonState mbRef src = do - (fp, mimeType, bs) <- liftIO . runIOorExplode $ do +fetch :: CommonState + -> IORef MB.MediaBag + -> String + -> Lua NumResults +fetch commonState mbRef src = do + mediaBag <- liftIO $ readIORef mbRef + (bs, mimeType) <- liftIO . runIOorExplode $ do putCommonState commonState - fetchMediaResource src - liftIO $ print (fp, mimeType) -- TODO DEBUG - insertMediaFn mbRef fp (OrNil mimeType) bs - Lua.push fp + setMediaBag mediaBag + fetchItem src + Lua.push bs Lua.push $ fromMaybe "" mimeType - return 2 -- returns 2 values: name in mediabag, mimetype + return 2 -- returns 2 values: contents, mimetype -- -- Helper types and orphan instances -- cgit v1.2.3 From 9451d83058fa0b237514e618ebe6140a69fe13db Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 30 Sep 2017 23:00:14 -0700 Subject: Lua: make fetch return mime type first and then content. --- src/Text/Pandoc/Lua/PandocModule.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index f27d6f45e..a54e54c09 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -163,8 +163,8 @@ fetch commonState mbRef src = do putCommonState commonState setMediaBag mediaBag fetchItem src - Lua.push bs Lua.push $ fromMaybe "" mimeType + Lua.push bs return 2 -- returns 2 values: contents, mimetype -- -- cgit v1.2.3 From 8768f7e5b060b4024d88d12f4255d515dd4ca7fa Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 30 Sep 2017 23:15:43 -0700 Subject: Lua: use sha1 instead of hashname. Better to leave control over the extension to the user. --- src/Text/Pandoc/Lua/PandocModule.hs | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index a54e54c09..5d826883d 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -47,7 +47,7 @@ import Text.Pandoc.Class (readDataFile, runIO, import Text.Pandoc.Options (ReaderOptions(readerExtensions)) import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Readers (Reader (..), getReader) -import Text.Pandoc.MIME (MimeType, extensionFromMimeType) +import Text.Pandoc.MIME (MimeType) import Data.Digest.Pure.SHA (sha1, showDigest) import qualified Foreign.Lua as Lua @@ -93,7 +93,7 @@ pushMediaBagModule commonState mediaBagRef = do addFunction "lookup" (lookupMediaFn mediaBagRef) addFunction "list" (mediaDirectoryFn mediaBagRef) addFunction "fetch" (fetch commonState mediaBagRef) - addFunction "hashname" hashnameFn + addFunction "sha1" sha1HashFn return () where addFunction name fn = do @@ -101,20 +101,12 @@ pushMediaBagModule commonState mediaBagRef = do Lua.pushHaskellFunction fn Lua.rawset (-3) -hashnameFn :: OrNil MimeType - -> BL.ByteString +sha1HashFn :: BL.ByteString -> Lua NumResults -hashnameFn nilOrMime contents = do - Lua.push (getHashname (toMaybe nilOrMime) contents) +sha1HashFn contents = do + Lua.push $ showDigest (sha1 contents) return 1 -getHashname :: Maybe MimeType -> BL.ByteString -> String -getHashname mbMime bs = - let ext = fromMaybe "" - (('.':) <$> (mbMime >>= extensionFromMimeType)) - basename = showDigest $ sha1 bs - in basename ++ ext - insertMediaFn :: IORef MB.MediaBag -> FilePath -> OrNil MimeType -- cgit v1.2.3 From 4c3b3bf65a769eaec4b8382d8ada4f28a3b91e04 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 1 Oct 2017 00:33:56 -0700 Subject: Lua: move sha1 from pandoc.mediabag to pandoc. --- src/Text/Pandoc/Lua/PandocModule.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index 5d826883d..ecb1c530f 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -63,6 +63,9 @@ pushPandocModule datadir = do Lua.push "__read" Lua.pushHaskellFunction readDoc Lua.rawset (-3) + Lua.push "sha1" + Lua.pushHaskellFunction sha1HashFn + Lua.rawset (-3) -- | Get the string representation of the pandoc module pandocModuleScript :: Maybe FilePath -> IO String @@ -93,7 +96,6 @@ pushMediaBagModule commonState mediaBagRef = do addFunction "lookup" (lookupMediaFn mediaBagRef) addFunction "list" (mediaDirectoryFn mediaBagRef) addFunction "fetch" (fetch commonState mediaBagRef) - addFunction "sha1" sha1HashFn return () where addFunction name fn = do -- cgit v1.2.3 From 3e77ea4792979879a80e67f20712766e4af2fdf5 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 1 Oct 2017 15:23:20 -0700 Subject: Lua: added 'pipe', which encapsulates Text.Pandoc.Process.pipeProcess. This is hard to do in lua, so it's helpful to provide this. --- src/Text/Pandoc/Lua/PandocModule.hs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index ecb1c530f..6a84a4350 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -48,6 +48,8 @@ import Text.Pandoc.Options (ReaderOptions(readerExtensions)) import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Readers (Reader (..), getReader) import Text.Pandoc.MIME (MimeType) +import Text.Pandoc.Process (pipeProcess) +import System.Exit (ExitCode(..)) import Data.Digest.Pure.SHA (sha1, showDigest) import qualified Foreign.Lua as Lua @@ -66,6 +68,9 @@ pushPandocModule datadir = do Lua.push "sha1" Lua.pushHaskellFunction sha1HashFn Lua.rawset (-3) + Lua.push "pipe" + Lua.pushHaskellFunction pipeFn + Lua.rawset (-3) -- | Get the string representation of the pandoc module pandocModuleScript :: Maybe FilePath -> IO String @@ -109,6 +114,18 @@ sha1HashFn contents = do Lua.push $ showDigest (sha1 contents) return 1 +pipeFn :: String + -> [String] + -> BL.ByteString + -> Lua NumResults +pipeFn command args input = do + (ec, output) <- liftIO $ pipeProcess Nothing command args input + Lua.push $ case ec of + ExitSuccess -> 0 + ExitFailure n -> n + Lua.push output + return 2 + insertMediaFn :: IORef MB.MediaBag -> FilePath -> OrNil MimeType -- cgit v1.2.3 From 514662e544a828e6c3904d2fec0216dc19bbcb9f Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Mon, 2 Oct 2017 23:11:58 +0200 Subject: Org reader: support `\n` export option The `\n` export option turns all newlines in the text into hard linebreaks. Closes #3950 --- src/Text/Pandoc/Readers/Org/ExportSettings.hs | 2 +- src/Text/Pandoc/Readers/Org/Inlines.hs | 3 ++- src/Text/Pandoc/Readers/Org/ParserState.hs | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/ExportSettings.hs b/src/Text/Pandoc/Readers/Org/ExportSettings.hs index 11f0972d5..36258aeba 100644 --- a/src/Text/Pandoc/Readers/Org/ExportSettings.hs +++ b/src/Text/Pandoc/Readers/Org/ExportSettings.hs @@ -52,7 +52,7 @@ exportSetting = choice , booleanSetting "-" (\val es -> es { exportSpecialStrings = val }) , ignoredSetting ":" , ignoredSetting "<" - , ignoredSetting "\\n" + , booleanSetting "\\n" (\val es -> es { exportPreserveBreaks = val }) , archivedTreeSetting "arch" (\val es -> es { exportArchivedTrees = val }) , booleanSetting "author" (\val es -> es { exportWithAuthor = val }) , ignoredSetting "c" diff --git a/src/Text/Pandoc/Readers/Org/Inlines.hs b/src/Text/Pandoc/Readers/Org/Inlines.hs index c5b1ccc52..39f4dc926 100644 --- a/src/Text/Pandoc/Readers/Org/Inlines.hs +++ b/src/Text/Pandoc/Readers/Org/Inlines.hs @@ -158,7 +158,8 @@ endline = try $ do decEmphasisNewlinesCount guard =<< newlinesCountWithinLimits updateLastPreCharPos - returnF B.softbreak + useHardBreaks <- exportPreserveBreaks . orgStateExportSettings <$> getState + returnF (if useHardBreaks then B.linebreak else B.softbreak) -- diff --git a/src/Text/Pandoc/Readers/Org/ParserState.hs b/src/Text/Pandoc/Readers/Org/ParserState.hs index fc98213fb..0349f7617 100644 --- a/src/Text/Pandoc/Readers/Org/ParserState.hs +++ b/src/Text/Pandoc/Readers/Org/ParserState.hs @@ -242,6 +242,7 @@ data ExportSettings = ExportSettings , exportEmphasizedText :: Bool -- ^ Parse emphasized text , exportHeadlineLevels :: Int -- ^ Maximum depth of headlines, deeper headlines are convert to list + , exportPreserveBreaks :: Bool -- ^ Whether to preserve linebreaks , exportSmartQuotes :: Bool -- ^ Parse quotes smartly , exportSpecialStrings :: Bool -- ^ Parse ellipses and dashes smartly , exportSubSuperscripts :: Bool -- ^ TeX-like syntax for sub- and superscripts @@ -261,6 +262,7 @@ defaultExportSettings = ExportSettings , exportDrawers = Left ["LOGBOOK"] , exportEmphasizedText = True , exportHeadlineLevels = 3 + , exportPreserveBreaks = False , exportSmartQuotes = False , exportSpecialStrings = True , exportSubSuperscripts = True -- cgit v1.2.3 From 492f496842217b8377e657217bec0d8875424f72 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 2 Oct 2017 21:27:00 -0700 Subject: Markdown reader: Fixed bug with indented code following raw LaTeX. Closes #3947. --- src/Text/Pandoc/Readers/Markdown.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 1364f25cb..61c07ed12 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -1101,7 +1101,7 @@ rawTeXBlock = do <|> (B.rawBlock "latex" . concat <$> rawLaTeXBlock `sepEndBy1` blankline) - spaces + optional blanklines return $ return result rawHtmlBlocks :: PandocMonad m => MarkdownParser m (F Blocks) -- cgit v1.2.3 From 9b750f7d879b1da386e49ecfd51ef9d023dc5d66 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Tue, 3 Oct 2017 13:13:45 +0200 Subject: Lua.PandocModule: promote addFunction to top level This reduces some boilerplate. --- src/Text/Pandoc/Lua/PandocModule.hs | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index 6a84a4350..c689edc4e 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -41,6 +41,7 @@ import Data.IORef import Data.Maybe (fromMaybe) import Data.Text (pack) import Foreign.Lua (Lua, FromLuaStack, ToLuaStack, NumResults, liftIO) +import Foreign.Lua.FunctionCalling (ToHaskellFunction) import Text.Pandoc.Class (readDataFile, runIO, runIOorExplode, setUserDataDir, CommonState(..), putCommonState, fetchItem, setMediaBag) @@ -62,15 +63,9 @@ pushPandocModule datadir = do script <- liftIO (pandocModuleScript datadir) status <- Lua.loadstring script unless (status /= Lua.OK) $ Lua.call 0 1 - Lua.push "__read" - Lua.pushHaskellFunction readDoc - Lua.rawset (-3) - Lua.push "sha1" - Lua.pushHaskellFunction sha1HashFn - Lua.rawset (-3) - Lua.push "pipe" - Lua.pushHaskellFunction pipeFn - Lua.rawset (-3) + addFunction "pipe" pipeFn + addFunction "_read" readDoc + addFunction "sha1" sha1HashFn -- | Get the string representation of the pandoc module pandocModuleScript :: Maybe FilePath -> IO String @@ -102,11 +97,12 @@ pushMediaBagModule commonState mediaBagRef = do addFunction "list" (mediaDirectoryFn mediaBagRef) addFunction "fetch" (fetch commonState mediaBagRef) return () - where - addFunction name fn = do - Lua.push name - Lua.pushHaskellFunction fn - Lua.rawset (-3) + +addFunction :: ToHaskellFunction a => String -> a -> Lua () +addFunction name fn = do + Lua.push name + Lua.pushHaskellFunction fn + Lua.rawset (-3) sha1HashFn :: BL.ByteString -> Lua NumResults -- cgit v1.2.3 From 371f9b708478700992a74864985cfea0af2fd4c3 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Tue, 3 Oct 2017 20:45:11 +0200 Subject: pandoc.lua: use wrapper funciton for pipe command The pipe command is wrapped in a lua function, throwing a lua error if the command returns with an error. A wrapper is needed as Haskell functions exposed to lua may not throw lua errors due to limitations of hslua. The error handling is written such that a table can be returned as an error object in the future. This is potentially useful when finer control is required while catching the error in lua code. Current limitations of hslua require error objects to be strings. --- src/Text/Pandoc/Lua/PandocModule.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index c689edc4e..f9b072dff 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -63,7 +63,7 @@ pushPandocModule datadir = do script <- liftIO (pandocModuleScript datadir) status <- Lua.loadstring script unless (status /= Lua.OK) $ Lua.call 0 1 - addFunction "pipe" pipeFn + addFunction "_pipe" pipeFn addFunction "_read" readDoc addFunction "sha1" sha1HashFn -- cgit v1.2.3 From 2262f005ce5b39ccd3694f61f2507b9e61c3804c Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Thu, 5 Oct 2017 11:30:44 +0200 Subject: Use hslua v0.9.0 --- src/Text/Pandoc/Lua/PandocModule.hs | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index f9b072dff..3c27ecffb 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -16,9 +16,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {-# LANGUAGE CPP #-} -#if !MIN_VERSION_hslua(0,9,0) -{-# OPTIONS_GHC -fno-warn-orphans #-} -#endif {- | Module : Text.Pandoc.Lua.PandocModule Copyright : Copyright © 2017 Albert Krewinkel @@ -40,7 +37,7 @@ import Data.Default (Default (..)) import Data.IORef import Data.Maybe (fromMaybe) import Data.Text (pack) -import Foreign.Lua (Lua, FromLuaStack, ToLuaStack, NumResults, liftIO) +import Foreign.Lua (Lua, FromLuaStack, NumResults, liftIO) import Foreign.Lua.FunctionCalling (ToHaskellFunction) import Text.Pandoc.Class (readDataFile, runIO, runIOorExplode, setUserDataDir, CommonState(..), @@ -186,11 +183,3 @@ instance FromLuaStack a => FromLuaStack (OrNil a) where if noValue then return (OrNil Nothing) else OrNil . Just <$> Lua.peek idx - -#if !MIN_VERSION_hslua(0,9,0) -instance ToLuaStack BL.ByteString where - push = Lua.push . BL.toStrict - -instance FromLuaStack BL.ByteString where - peek = fmap BL.fromStrict . Lua.peek -#endif -- cgit v1.2.3 From 12f8efe0128ade1bd6497a59508f6bd836eb3788 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Thu, 5 Oct 2017 11:41:59 +0200 Subject: pandoc.lua: throw better error when pipe command fails A table containing the error code, command, and command output is thrown instead of just a string error message. --- src/Text/Pandoc/Lua.hs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 2e4204898..583d43a2e 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -196,9 +196,8 @@ runFilterFunction lf x = do push x z <- Lua.pcall 1 1 Nothing when (z /= OK) $ do - msg <- Lua.peek (-1) <* Lua.pop 1 - let prefix = "Error while running filter function: " - Lua.throwLuaError $ prefix ++ msg + let addPrefix = ("Error while running filter function: " ++) + Lua.throwTopMessageAsError' addPrefix elementOrList :: FromLuaStack a => a -> Lua [a] elementOrList x = do -- cgit v1.2.3 From 23eaf2a74f686f0af95544047020bff5fcb6ab4f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 5 Oct 2017 10:08:04 -0700 Subject: KaTeX fixes: * In Options.HTMLMathMethod, the KaTeX contsructor now takes only one string (for the KaTeX base URL), rather than two [API change]. * The default URL has been updated to the latest version. * The autoload script is now loaded by default. --- src/Text/Pandoc/App.hs | 26 ++++---------------------- src/Text/Pandoc/Options.hs | 2 +- src/Text/Pandoc/Writers/HTML.hs | 34 +++++++++++++++------------------- src/Text/Pandoc/Writers/Math.hs | 2 +- 4 files changed, 21 insertions(+), 43 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 57a91581b..a18cc2961 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -183,13 +183,6 @@ convertWithOpts opts = do Nothing -> return Nothing Just fp -> Just <$> UTF8.readFile fp - let mathMethod = - case (optKaTeXJS opts, optKaTeXStylesheet opts) of - (Nothing, _) -> optHTMLMathMethod opts - (Just js, ss) -> KaTeX js (fromMaybe - (defaultKaTeXURL ++ "katex.min.css") ss) - - -- --bibliography implies -F pandoc-citeproc for backwards compatibility: let needsCiteproc = isJust (lookup "bibliography" (optMetadata opts)) && optCiteMethod opts `notElem` [Natbib, Biblatex] && @@ -368,7 +361,7 @@ convertWithOpts opts = do maybe return (addStringAsVariable "epub-cover-image") (optEpubCoverImage opts) >>= - (\vars -> case mathMethod of + (\vars -> case optHTMLMathMethod opts of LaTeXMathML Nothing -> do s <- UTF8.toString <$> readDataFile "LaTeXMathML.js" return $ ("mathml-script", s) : vars @@ -428,7 +421,7 @@ convertWithOpts opts = do , writerVariables = variables , writerTabStop = optTabStop opts , writerTableOfContents = optTableOfContents opts - , writerHTMLMathMethod = mathMethod + , writerHTMLMathMethod = optHTMLMathMethod opts , writerIncremental = optIncremental opts , writerCiteMethod = optCiteMethod opts , writerNumberSections = optNumberSections opts @@ -642,8 +635,6 @@ data Opt = Opt , optExtractMedia :: Maybe FilePath -- ^ Path to extract embedded media , optTrackChanges :: TrackChanges -- ^ Accept or reject MS Word track-changes. , optFileScope :: Bool -- ^ Parse input files before combining - , optKaTeXStylesheet :: Maybe String -- ^ Path to stylesheet for KaTeX - , optKaTeXJS :: Maybe String -- ^ Path to js file for KaTeX , optTitlePrefix :: Maybe String -- ^ Prefix for title , optCss :: [FilePath] -- ^ CSS files to link to , optIncludeBeforeBody :: [FilePath] -- ^ Files to include before @@ -719,8 +710,6 @@ defaultOpts = Opt , optExtractMedia = Nothing , optTrackChanges = AcceptChanges , optFileScope = False - , optKaTeXStylesheet = Nothing - , optKaTeXJS = Nothing , optTitlePrefix = Nothing , optCss = [] , optIncludeBeforeBody = [] @@ -1455,18 +1444,11 @@ options = (OptArg (\arg opt -> return opt - { optKaTeXJS = - arg <|> Just (defaultKaTeXURL ++ "katex.min.js")}) + { optHTMLMathMethod = KaTeX $ + fromMaybe defaultKaTeXURL arg }) "URL") "" -- Use KaTeX for HTML Math - , Option "" ["katex-stylesheet"] - (ReqArg - (\arg opt -> - return opt { optKaTeXStylesheet = Just arg }) - "URL") - "" -- Set the KaTeX Stylesheet location - , Option "" ["gladtex"] (NoArg (\opt -> return opt { optHTMLMathMethod = GladTeX })) diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index f936658f4..99c7afba7 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -107,7 +107,7 @@ data HTMLMathMethod = PlainMath | WebTeX String -- url of TeX->image script. | MathML | MathJax String -- url of MathJax.js - | KaTeX String String -- url of stylesheet and katex.js + | KaTeX String -- url of KaTeX files deriving (Show, Read, Eq, Data, Typeable, Generic) instance ToJSON HTMLMathMethod where diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 1641b991c..41b50bf70 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -268,10 +268,17 @@ pandocToHtml opts (Pandoc meta blocks) = do H.script ! A.src (toValue url) ! A.type_ "text/javascript" $ mempty - KaTeX js css -> - (H.script ! A.src (toValue js) $ mempty) <> - (H.link ! A.rel "stylesheet" ! A.href (toValue css)) <> - (H.script ! A.type_ "text/javascript" $ toHtml renderKaTeX) + KaTeX url -> + (H.script ! + A.src (toValue $ url ++ "katex.min.js") $ mempty) <> + (H.script ! + A.src (toValue $ url ++ "contrib/auto-render.min.js") + $ mempty) <> + (H.script $ + "document.addEventListener(\"DOMContentLoaded\", function() {\n renderMathInElement(document.body);\n});") <> + (H.link ! A.rel "stylesheet" ! + A.href (toValue $ url ++ "katex.min.css")) + _ -> case lookup "mathml-script" (writerVariables opts) of Just s | not (stHtml5 st) -> H.script ! A.type_ "text/javascript" @@ -1009,10 +1016,10 @@ inlineToHtml opts inline = do case t of InlineMath -> "\\(" ++ str ++ "\\)" DisplayMath -> "\\[" ++ str ++ "\\]" - KaTeX _ _ -> return $ H.span ! A.class_ mathClass $ - toHtml (case t of - InlineMath -> str - DisplayMath -> "\\displaystyle " ++ str) + KaTeX _ -> return $ H.span ! A.class_ mathClass $ toHtml $ + case t of + InlineMath -> "\\(" ++ str ++ "\\)" + DisplayMath -> "\\[" ++ str ++ "\\]" PlainMath -> do x <- lift (texMathToInlines t str) >>= inlineListToHtml opts let m = H.span ! A.class_ mathClass $ x @@ -1133,17 +1140,6 @@ blockListToNote opts ref blocks = _ -> noteItem return $ nl opts >> noteItem' --- Javascript snippet to render all KaTeX elements -renderKaTeX :: String -renderKaTeX = unlines [ - "window.onload = function(){var mathElements = document.getElementsByClassName(\"math\");" - , "for (var i=0; i < mathElements.length; i++)" - , "{" - , " var texText = mathElements[i].firstChild" - , " katex.render(texText.data, mathElements[i])" - , "}}" - ] - isMathEnvironment :: String -> Bool isMathEnvironment s = "\\begin{" `isPrefixOf` s && envName `elem` mathmlenvs diff --git a/src/Text/Pandoc/Writers/Math.hs b/src/Text/Pandoc/Writers/Math.hs index 58252d60f..1677cb5b6 100644 --- a/src/Text/Pandoc/Writers/Math.hs +++ b/src/Text/Pandoc/Writers/Math.hs @@ -53,4 +53,4 @@ defaultMathJaxURL :: String defaultMathJaxURL = "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/" defaultKaTeXURL :: String -defaultKaTeXURL = "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/" +defaultKaTeXURL = "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.8.3/" -- cgit v1.2.3 From c359bdd9b127465d2374448dd51ba68d5ebce75a Mon Sep 17 00:00:00 2001 From: bucklereed <bucklereed@gmail.com> Date: Fri, 6 Oct 2017 12:17:50 +0100 Subject: LaTeX reader: read polyglossia/babel \text($LANG){...}. --- src/Text/Pandoc/Readers/LaTeX.hs | 242 +++++++++++++++++++++------------------ 1 file changed, 129 insertions(+), 113 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 4bdf02734..8555d2eea 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1214,7 +1214,7 @@ inlineEnvironments = M.fromList [ ] inlineCommands :: PandocMonad m => M.Map Text (LP m Inlines) -inlineCommands = M.fromList $ +inlineCommands = M.union inlineLanguageCommands $ M.fromList $ [ ("emph", extractSpaces emph <$> tok) , ("textit", extractSpaces emph <$> tok) , ("textsl", extractSpaces emph <$> tok) @@ -1224,7 +1224,6 @@ inlineCommands = M.fromList $ , ("textrm", extractSpaces (spanWith ("",["roman"],[])) <$> tok) , ("textup", extractSpaces (spanWith ("",["upright"],[])) <$> tok) , ("texttt", ttfamily) - , ("textenglish", spanWith ("",[],[("lang","en")]) <$> tok) , ("sout", extractSpaces strikeout <$> tok) , ("lq", return (str "‘")) , ("rq", return (str "’")) @@ -1244,7 +1243,6 @@ inlineCommands = M.fromList $ , ("dots", lit "…") , ("mdots", lit "…") , ("sim", lit "~") - , ("textgreek", tok) , ("sep", lit ",") , ("label", rawInlineOr "label" dolabel) , ("ref", rawInlineOr "ref" $ doref "ref") @@ -1465,6 +1463,19 @@ inlineCommands = M.fromList $ , ("Rn", romanNumeralLower) ] +inlineLanguageCommands :: PandocMonad m => M.Map Text (LP m Inlines) +inlineLanguageCommands = M.fromList $ mk <$> M.toList polyglossiaLangToBCP47 + where + mk (polyglossia, bcp47Func) = + ("text" <> T.pack polyglossia, inlineLanguage bcp47Func) + +inlineLanguage :: PandocMonad m => (String -> Lang) -> LP m Inlines +inlineLanguage bcp47Func = do + o <- option "" $ (T.unpack . T.filter (\c -> c /= '[' && c /= ']')) + <$> rawopt + let lang = renderLang $ bcp47Func o + extractSpaces (spanWith ("", [], [("lang", lang)])) <$> tok + hyperlink :: PandocMonad m => LP m Inlines hyperlink = try $ do src <- toksToString <$> braced @@ -2464,118 +2475,123 @@ setDefaultLanguage = do o <- option "" $ (T.unpack . T.filter (\c -> c /= '[' && c /= ']')) <$> rawopt polylang <- toksToString <$> braced - case polyglossiaLangToBCP47 polylang o of + case M.lookup polylang polyglossiaLangToBCP47 of Nothing -> return mempty -- TODO mzero? warning? - Just l -> do + Just langFunc -> do + let l = langFunc o setTranslations l updateState $ setMeta "lang" $ str (renderLang l) return mempty -polyglossiaLangToBCP47 :: String -> String -> Maybe Lang -polyglossiaLangToBCP47 s o = - case (s, filter (/=' ') o) of - ("arabic", "locale=algeria") -> Just $ Lang "ar" "" "DZ" [] - ("arabic", "locale=mashriq") -> Just $ Lang "ar" "" "SY" [] - ("arabic", "locale=libya") -> Just $ Lang "ar" "" "LY" [] - ("arabic", "locale=morocco") -> Just $ Lang "ar" "" "MA" [] - ("arabic", "locale=mauritania") -> Just $ Lang "ar" "" "MR" [] - ("arabic", "locale=tunisia") -> Just $ Lang "ar" "" "TN" [] - ("german", "spelling=old") -> Just $ Lang "de" "" "DE" ["1901"] - ("german", "variant=austrian,spelling=old") - -> Just $ Lang "de" "" "AT" ["1901"] - ("german", "variant=austrian") -> Just $ Lang "de" "" "AT" [] - ("german", "variant=swiss,spelling=old") - -> Just $ Lang "de" "" "CH" ["1901"] - ("german", "variant=swiss") -> Just $ Lang "de" "" "CH" [] - ("german", _) -> Just $ Lang "de" "" "" [] - ("lsorbian", _) -> Just $ Lang "dsb" "" "" [] - ("greek", "variant=poly") -> Just $ Lang "el" "" "polyton" [] - ("english", "variant=australian") -> Just $ Lang "en" "" "AU" [] - ("english", "variant=canadian") -> Just $ Lang "en" "" "CA" [] - ("english", "variant=british") -> Just $ Lang "en" "" "GB" [] - ("english", "variant=newzealand") -> Just $ Lang "en" "" "NZ" [] - ("english", "variant=american") -> Just $ Lang "en" "" "US" [] - ("greek", "variant=ancient") -> Just $ Lang "grc" "" "" [] - ("usorbian", _) -> Just $ Lang "hsb" "" "" [] - ("latin", "variant=classic") -> Just $ Lang "la" "" "" ["x-classic"] - ("slovenian", _) -> Just $ Lang "sl" "" "" [] - ("serbianc", _) -> Just $ Lang "sr" "cyrl" "" [] - ("pinyin", _) -> Just $ Lang "zh" "Latn" "" ["pinyin"] - ("afrikaans", _) -> Just $ Lang "af" "" "" [] - ("amharic", _) -> Just $ Lang "am" "" "" [] - ("arabic", _) -> Just $ Lang "ar" "" "" [] - ("assamese", _) -> Just $ Lang "as" "" "" [] - ("asturian", _) -> Just $ Lang "ast" "" "" [] - ("bulgarian", _) -> Just $ Lang "bg" "" "" [] - ("bengali", _) -> Just $ Lang "bn" "" "" [] - ("tibetan", _) -> Just $ Lang "bo" "" "" [] - ("breton", _) -> Just $ Lang "br" "" "" [] - ("catalan", _) -> Just $ Lang "ca" "" "" [] - ("welsh", _) -> Just $ Lang "cy" "" "" [] - ("czech", _) -> Just $ Lang "cs" "" "" [] - ("coptic", _) -> Just $ Lang "cop" "" "" [] - ("danish", _) -> Just $ Lang "da" "" "" [] - ("divehi", _) -> Just $ Lang "dv" "" "" [] - ("greek", _) -> Just $ Lang "el" "" "" [] - ("english", _) -> Just $ Lang "en" "" "" [] - ("esperanto", _) -> Just $ Lang "eo" "" "" [] - ("spanish", _) -> Just $ Lang "es" "" "" [] - ("estonian", _) -> Just $ Lang "et" "" "" [] - ("basque", _) -> Just $ Lang "eu" "" "" [] - ("farsi", _) -> Just $ Lang "fa" "" "" [] - ("finnish", _) -> Just $ Lang "fi" "" "" [] - ("french", _) -> Just $ Lang "fr" "" "" [] - ("friulan", _) -> Just $ Lang "fur" "" "" [] - ("irish", _) -> Just $ Lang "ga" "" "" [] - ("scottish", _) -> Just $ Lang "gd" "" "" [] - ("ethiopic", _) -> Just $ Lang "gez" "" "" [] - ("galician", _) -> Just $ Lang "gl" "" "" [] - ("hebrew", _) -> Just $ Lang "he" "" "" [] - ("hindi", _) -> Just $ Lang "hi" "" "" [] - ("croatian", _) -> Just $ Lang "hr" "" "" [] - ("magyar", _) -> Just $ Lang "hu" "" "" [] - ("armenian", _) -> Just $ Lang "hy" "" "" [] - ("interlingua", _) -> Just $ Lang "ia" "" "" [] - ("indonesian", _) -> Just $ Lang "id" "" "" [] - ("icelandic", _) -> Just $ Lang "is" "" "" [] - ("italian", _) -> Just $ Lang "it" "" "" [] - ("japanese", _) -> Just $ Lang "jp" "" "" [] - ("khmer", _) -> Just $ Lang "km" "" "" [] - ("kurmanji", _) -> Just $ Lang "kmr" "" "" [] - ("kannada", _) -> Just $ Lang "kn" "" "" [] - ("korean", _) -> Just $ Lang "ko" "" "" [] - ("latin", _) -> Just $ Lang "la" "" "" [] - ("lao", _) -> Just $ Lang "lo" "" "" [] - ("lithuanian", _) -> Just $ Lang "lt" "" "" [] - ("latvian", _) -> Just $ Lang "lv" "" "" [] - ("malayalam", _) -> Just $ Lang "ml" "" "" [] - ("mongolian", _) -> Just $ Lang "mn" "" "" [] - ("marathi", _) -> Just $ Lang "mr" "" "" [] - ("dutch", _) -> Just $ Lang "nl" "" "" [] - ("nynorsk", _) -> Just $ Lang "nn" "" "" [] - ("norsk", _) -> Just $ Lang "no" "" "" [] - ("nko", _) -> Just $ Lang "nqo" "" "" [] - ("occitan", _) -> Just $ Lang "oc" "" "" [] - ("panjabi", _) -> Just $ Lang "pa" "" "" [] - ("polish", _) -> Just $ Lang "pl" "" "" [] - ("piedmontese", _) -> Just $ Lang "pms" "" "" [] - ("portuguese", _) -> Just $ Lang "pt" "" "" [] - ("romansh", _) -> Just $ Lang "rm" "" "" [] - ("romanian", _) -> Just $ Lang "ro" "" "" [] - ("russian", _) -> Just $ Lang "ru" "" "" [] - ("sanskrit", _) -> Just $ Lang "sa" "" "" [] - ("samin", _) -> Just $ Lang "se" "" "" [] - ("slovak", _) -> Just $ Lang "sk" "" "" [] - ("albanian", _) -> Just $ Lang "sq" "" "" [] - ("serbian", _) -> Just $ Lang "sr" "" "" [] - ("swedish", _) -> Just $ Lang "sv" "" "" [] - ("syriac", _) -> Just $ Lang "syr" "" "" [] - ("tamil", _) -> Just $ Lang "ta" "" "" [] - ("telugu", _) -> Just $ Lang "te" "" "" [] - ("thai", _) -> Just $ Lang "th" "" "" [] - ("turkmen", _) -> Just $ Lang "tk" "" "" [] - ("turkish", _) -> Just $ Lang "tr" "" "" [] - ("ukrainian", _) -> Just $ Lang "uk" "" "" [] - ("urdu", _) -> Just $ Lang "ur" "" "" [] - ("vietnamese", _) -> Just $ Lang "vi" "" "" [] - _ -> Nothing +polyglossiaLangToBCP47 :: M.Map String (String -> Lang) +polyglossiaLangToBCP47 = M.fromList + [ ("arabic", \o -> case filter (/=' ') o of + "locale=algeria" -> Lang "ar" "" "DZ" [] + "locale=mashriq" -> Lang "ar" "" "SY" [] + "locale=libya" -> Lang "ar" "" "LY" [] + "locale=morocco" -> Lang "ar" "" "MA" [] + "locale=mauritania" -> Lang "ar" "" "MR" [] + "locale=tunisia" -> Lang "ar" "" "TN" [] + _ -> Lang "ar" "" "" []) + , ("german", \o -> case filter (/=' ') o of + "spelling=old" -> Lang "de" "" "DE" ["1901"] + "variant=austrian,spelling=old" + -> Lang "de" "" "AT" ["1901"] + "variant=austrian" -> Lang "de" "" "AT" [] + "variant=swiss,spelling=old" + -> Lang "de" "" "CH" ["1901"] + "variant=swiss" -> Lang "de" "" "CH" [] + _ -> Lang "de" "" "" []) + , ("lsorbian", \_ -> Lang "dsb" "" "" []) + , ("greek", \o -> case filter (/=' ') o of + "variant=poly" -> Lang "el" "" "polyton" [] + "variant=ancient" -> Lang "grc" "" "" [] + _ -> Lang "el" "" "" []) + , ("english", \o -> case filter (/=' ') o of + "variant=australian" -> Lang "en" "" "AU" [] + "variant=canadian" -> Lang "en" "" "CA" [] + "variant=british" -> Lang "en" "" "GB" [] + "variant=newzealand" -> Lang "en" "" "NZ" [] + "variant=american" -> Lang "en" "" "US" [] + _ -> Lang "en" "" "" []) + , ("usorbian", \_ -> Lang "hsb" "" "" []) + , ("latin", \o -> case filter (/=' ') o of + "variant=classic" -> Lang "la" "" "" ["x-classic"] + _ -> Lang "la" "" "" []) + , ("slovenian", \_ -> Lang "sl" "" "" []) + , ("serbianc", \_ -> Lang "sr" "cyrl" "" []) + , ("pinyin", \_ -> Lang "zh" "Latn" "" ["pinyin"]) + , ("afrikaans", \_ -> Lang "af" "" "" []) + , ("amharic", \_ -> Lang "am" "" "" []) + , ("assamese", \_ -> Lang "as" "" "" []) + , ("asturian", \_ -> Lang "ast" "" "" []) + , ("bulgarian", \_ -> Lang "bg" "" "" []) + , ("bengali", \_ -> Lang "bn" "" "" []) + , ("tibetan", \_ -> Lang "bo" "" "" []) + , ("breton", \_ -> Lang "br" "" "" []) + , ("catalan", \_ -> Lang "ca" "" "" []) + , ("welsh", \_ -> Lang "cy" "" "" []) + , ("czech", \_ -> Lang "cs" "" "" []) + , ("coptic", \_ -> Lang "cop" "" "" []) + , ("danish", \_ -> Lang "da" "" "" []) + , ("divehi", \_ -> Lang "dv" "" "" []) + , ("esperanto", \_ -> Lang "eo" "" "" []) + , ("spanish", \_ -> Lang "es" "" "" []) + , ("estonian", \_ -> Lang "et" "" "" []) + , ("basque", \_ -> Lang "eu" "" "" []) + , ("farsi", \_ -> Lang "fa" "" "" []) + , ("finnish", \_ -> Lang "fi" "" "" []) + , ("french", \_ -> Lang "fr" "" "" []) + , ("friulan", \_ -> Lang "fur" "" "" []) + , ("irish", \_ -> Lang "ga" "" "" []) + , ("scottish", \_ -> Lang "gd" "" "" []) + , ("ethiopic", \_ -> Lang "gez" "" "" []) + , ("galician", \_ -> Lang "gl" "" "" []) + , ("hebrew", \_ -> Lang "he" "" "" []) + , ("hindi", \_ -> Lang "hi" "" "" []) + , ("croatian", \_ -> Lang "hr" "" "" []) + , ("magyar", \_ -> Lang "hu" "" "" []) + , ("armenian", \_ -> Lang "hy" "" "" []) + , ("interlingua", \_ -> Lang "ia" "" "" []) + , ("indonesian", \_ -> Lang "id" "" "" []) + , ("icelandic", \_ -> Lang "is" "" "" []) + , ("italian", \_ -> Lang "it" "" "" []) + , ("japanese", \_ -> Lang "jp" "" "" []) + , ("khmer", \_ -> Lang "km" "" "" []) + , ("kurmanji", \_ -> Lang "kmr" "" "" []) + , ("kannada", \_ -> Lang "kn" "" "" []) + , ("korean", \_ -> Lang "ko" "" "" []) + , ("lao", \_ -> Lang "lo" "" "" []) + , ("lithuanian", \_ -> Lang "lt" "" "" []) + , ("latvian", \_ -> Lang "lv" "" "" []) + , ("malayalam", \_ -> Lang "ml" "" "" []) + , ("mongolian", \_ -> Lang "mn" "" "" []) + , ("marathi", \_ -> Lang "mr" "" "" []) + , ("dutch", \_ -> Lang "nl" "" "" []) + , ("nynorsk", \_ -> Lang "nn" "" "" []) + , ("norsk", \_ -> Lang "no" "" "" []) + , ("nko", \_ -> Lang "nqo" "" "" []) + , ("occitan", \_ -> Lang "oc" "" "" []) + , ("panjabi", \_ -> Lang "pa" "" "" []) + , ("polish", \_ -> Lang "pl" "" "" []) + , ("piedmontese", \_ -> Lang "pms" "" "" []) + , ("portuguese", \_ -> Lang "pt" "" "" []) + , ("romansh", \_ -> Lang "rm" "" "" []) + , ("romanian", \_ -> Lang "ro" "" "" []) + , ("russian", \_ -> Lang "ru" "" "" []) + , ("sanskrit", \_ -> Lang "sa" "" "" []) + , ("samin", \_ -> Lang "se" "" "" []) + , ("slovak", \_ -> Lang "sk" "" "" []) + , ("albanian", \_ -> Lang "sq" "" "" []) + , ("serbian", \_ -> Lang "sr" "" "" []) + , ("swedish", \_ -> Lang "sv" "" "" []) + , ("syriac", \_ -> Lang "syr" "" "" []) + , ("tamil", \_ -> Lang "ta" "" "" []) + , ("telugu", \_ -> Lang "te" "" "" []) + , ("thai", \_ -> Lang "th" "" "" []) + , ("turkmen", \_ -> Lang "tk" "" "" []) + , ("turkish", \_ -> Lang "tr" "" "" []) + , ("ukrainian", \_ -> Lang "uk" "" "" []) + , ("urdu", \_ -> Lang "ur" "" "" []) + , ("vietnamese", \_ -> Lang "vi" "" "" []) + ] -- cgit v1.2.3 From c0b3f7fc6059bf8c555bbb2e17c95ae6b8d24f97 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 6 Oct 2017 20:29:11 -0700 Subject: Removed redundant import --- src/Text/Pandoc/App.hs | 1 - 1 file changed, 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index a18cc2961..6b64a314e 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -38,7 +38,6 @@ module Text.Pandoc.App ( , parseOptions , options ) where -import Control.Applicative ((<|>)) import qualified Control.Exception as E import Control.Monad import Control.Monad.Except (throwError, catchError) -- cgit v1.2.3 From 89f136266037ae1f25277a50c1fab42c28a92c47 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 6 Oct 2017 21:12:54 -0700 Subject: Use mathjax 2.7.2 by default. --- src/Text/Pandoc/Writers/Math.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Math.hs b/src/Text/Pandoc/Writers/Math.hs index 1677cb5b6..61358378b 100644 --- a/src/Text/Pandoc/Writers/Math.hs +++ b/src/Text/Pandoc/Writers/Math.hs @@ -50,7 +50,7 @@ convertMath writer mt str = do InlineMath -> DisplayInline defaultMathJaxURL :: String -defaultMathJaxURL = "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/" +defaultMathJaxURL = "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/" defaultKaTeXURL :: String defaultKaTeXURL = "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.8.3/" -- cgit v1.2.3 From f176ad6f21aa02884f628082c05eecb76816d014 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sun, 8 Oct 2017 14:17:26 +0200 Subject: Org reader: end footnotes after two blank lines Footnotes can not only be terminated by the start of a new footnote or a header, but also by two consecutive blank lines. --- src/Text/Pandoc/Readers/Org/Blocks.hs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index 3e0ab0127..7f10195fe 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -724,13 +724,14 @@ latexEnd envName = try $ noteBlock :: PandocMonad m => OrgParser m (F Blocks) noteBlock = try $ do ref <- noteMarker <* skipSpaces <* updateLastPreCharPos - content <- mconcat <$> blocksTillHeaderOrNote + content <- mconcat <$> many1Till block endOfFootnote addToNotesTable (ref, content) return mempty where - blocksTillHeaderOrNote = - many1Till block (eof <|> () <$ lookAhead noteMarker - <|> () <$ lookAhead headerStart) + endOfFootnote = eof + <|> () <$ lookAhead noteMarker + <|> () <$ lookAhead headerStart + <|> () <$ lookAhead (try $ blankline *> blankline) -- Paragraphs or Plain text paraOrPlain :: PandocMonad m => OrgParser m (F Blocks) -- cgit v1.2.3 From fdaae5aec5bf31ae2f4fa1612e4263fb91ded4c0 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 8 Oct 2017 09:53:03 -0700 Subject: Small logic fix. --- src/Text/Pandoc/Readers/LaTeX.hs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 8555d2eea..9ad4c599f 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -2104,8 +2104,7 @@ rawEnv name = do then return $ rawBlock "latex" $ T.unpack $ beginCommand <> untokenize raw else do - unless parseRaw $ do - report $ SkippedContent (T.unpack beginCommand) pos1 + report $ SkippedContent (T.unpack beginCommand) pos1 pos2 <- getPosition report $ SkippedContent ("\\end{" ++ T.unpack name ++ "}") pos2 return bs -- cgit v1.2.3 From ad13189c8f3ce051a29817550f2619c912439edb Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 8 Oct 2017 21:11:58 -0700 Subject: LaTeX reader: don't treat "..." as Quoted. This caused quotes to be omitted in `\texttt` contexts. Closes #3958. However, a better fix would be to modify the --- src/Text/Pandoc/Readers/LaTeX.hs | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 9ad4c599f..57db33871 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -659,8 +659,6 @@ doubleQuote = do -- the following is used by babel for localized quotes: <|> quoted' doubleQuoted (try $ sequence [symbol '"', symbol '`']) (void $ try $ sequence [symbol '"', symbol '\'']) - <|> quoted' doubleQuoted ((:[]) <$> symbol '"') - (void $ symbol '"') singleQuote :: PandocMonad m => LP m Inlines singleQuote = do -- cgit v1.2.3 From 7d2ff7ed6d1cdd1b30d52e58decd830e1b8f819d Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 8 Oct 2017 21:55:57 -0700 Subject: Shared.stringify, removeFormatting: handle Quoted better. Previously we were losing the qutation marks in Quoted elements. See #3958. --- src/Text/Pandoc/Shared.hs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 9f88a0ad4..f0c2f172e 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -353,7 +353,7 @@ extractSpaces f is = -- | Extract inlines, removing formatting. removeFormatting :: Walkable Inline a => a -> [Inline] -removeFormatting = query go . walk deNote +removeFormatting = query go . walk (deNote . deQuote) where go :: Inline -> [Inline] go (Str xs) = [Str xs] go Space = [Space] @@ -367,11 +367,18 @@ deNote :: Inline -> Inline deNote (Note _) = Str "" deNote x = x +deQuote :: Inline -> Inline +deQuote (Quoted SingleQuote xs) = + Span ("",[],[]) (Str "\8216" : xs ++ [Str "\8217"]) +deQuote (Quoted DoubleQuote xs) = + Span ("",[],[]) (Str "\8220" : xs ++ [Str "\8221"]) +deQuote x = x + -- | Convert pandoc structure to a string with formatting removed. -- Footnotes are skipped (since we don't want their contents in link -- labels). stringify :: Walkable Inline a => a -> String -stringify = query go . walk deNote +stringify = query go . walk (deNote . deQuote) where go :: Inline -> [Char] go Space = " " go SoftBreak = " " -- cgit v1.2.3 From 81d28412d0d1136b5e2efc1d612ab757a46aeb12 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 9 Oct 2017 18:08:27 +0300 Subject: hlint FB2 writer --- src/Text/Pandoc/Writers/FB2.hs | 100 ++++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 50 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index 36c572b63..ae2d5a796 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -38,8 +38,8 @@ FictionBook is an XML-based e-book format. For more information see: module Text.Pandoc.Writers.FB2 (writeFB2) where import Control.Monad.Except (catchError) -import Control.Monad.State.Strict (StateT, evalStateT, get, lift, modify) -import Control.Monad.State.Strict (liftM) +import Control.Monad.State.Strict (StateT, evalStateT, get, lift, modify, liftM) +import Control.Monad (zipWithM) import Data.ByteString.Base64 (encode) import qualified Data.ByteString.Char8 as B8 import Data.Char (isAscii, isControl, isSpace, toLower) @@ -100,10 +100,10 @@ pandocToFB2 opts (Pandoc meta blocks) = do secs <- renderSections 1 blocks let body = el "body" $ fp ++ secs notes <- renderFootnotes - (imgs,missing) <- liftM imagesToFetch get >>= \s -> lift (fetchImages s) + (imgs,missing) <- fmap imagesToFetch get >>= \s -> lift (fetchImages s) let body' = replaceImagesWithAlt missing body let fb2_xml = el "FictionBook" (fb2_attrs, [desc, body'] ++ notes ++ imgs) - return $ pack $ xml_head ++ (showContent fb2_xml) ++ "\n" + return $ pack $ xml_head ++ showContent fb2_xml ++ "\n" where xml_head = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" fb2_attrs = @@ -115,7 +115,7 @@ pandocToFB2 opts (Pandoc meta blocks) = do frontpage :: PandocMonad m => Meta -> FBM m [Content] frontpage meta' = do t <- cMapM toXml . docTitle $ meta' - return $ + return [ el "title" (el "p" t) , el "annotation" (map (el "p" . cMap plain) (docAuthors meta' ++ [docDate meta'])) @@ -131,7 +131,7 @@ description meta' = do Just (MetaString s) -> [el "lang" $ iso639 s] _ -> [] where iso639 = takeWhile (/= '-') -- Convert BCP 47 to ISO 639 - return $ el "description" $ + return $ el "description" [ el "title-info" (bt ++ as ++ dd ++ lang) , el "document-info" [ el "program-used" "pandoc" ] -- FIXME: +version ] @@ -149,16 +149,16 @@ 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) + email = el "email" <$> take 1 (filter ('@' `elem`) ws) ws' = filter ('@' `notElem`) ws names = case ws' of - (nickname:[]) -> [ el "nickname" nickname ] - (fname:lname:[]) -> [ el "first-name" fname + [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 :: PandocMonad m => Meta -> FBM m [Content] @@ -181,7 +181,7 @@ renderSection level (ttl, body) = do title <- if null ttl then return [] else return . list . el "title" . formatTitle $ ttl - content <- if (hasSubsections body) + content <- if hasSubsections body then renderSections (level + 1) body else cMapM blockToXml body return $ el "section" (title ++ content) @@ -213,7 +213,7 @@ splitSections level blocks = reverse $ revSplit (reverse blocks) let (lastsec, before) = break sameLevel rblocks (header, prevblocks) = case before of - ((Header n _ title):prevblocks') -> + (Header n _ title:prevblocks') -> if n == level then (title, prevblocks') else ([], before) @@ -232,7 +232,7 @@ renderFootnotes = do el "body" ([uattr "name" "notes"], map renderFN (reverse fns)) where renderFN (n, idstr, cs) = - let fn_texts = (el "title" (el "p" (show n))) : cs + let fn_texts = el "title" (el "p" (show n)) : cs in el "section" ([uattr "id" idstr], fn_texts) -- | Fetch images and encode them for the FictionBook XML. @@ -240,7 +240,7 @@ renderFootnotes = do fetchImages :: PandocMonad m => [(String,String)] -> m ([Content],[String]) fetchImages links = do imgs <- mapM (uncurry fetchImage) links - return $ (rights imgs, lefts imgs) + return (rights imgs, lefts imgs) -- | Fetch image data from disk or from network and make a <binary> XML section. -- Return either (Left hrefOfMissingImage) or (Right xmlContent). @@ -254,7 +254,7 @@ fetchImage href link = do then return (Just (mime',base64)) else return Nothing (True, Just _) -> return Nothing -- not base64-encoded - _ -> do + _ -> catchError (do (bs, mbmime) <- P.fetchItem link case mbmime of Nothing -> do @@ -266,7 +266,7 @@ fetchImage href link = do do report $ CouldNotFetchResource link (show e) return Nothing) case mbimg of - Just (imgtype, imgdata) -> do + Just (imgtype, imgdata) -> return . Right $ el "binary" ( [uattr "id" href , uattr "content-type" imgtype] @@ -300,8 +300,8 @@ isMimeType :: String -> Bool isMimeType s = case split (=='/') s of [mtype,msubtype] -> - ((map toLower mtype) `elem` types - || "x-" `isPrefixOf` (map toLower mtype)) + (map toLower mtype `elem` types + || "x-" `isPrefixOf` map toLower mtype) && all valid mtype && all valid msubtype _ -> False @@ -311,10 +311,10 @@ isMimeType s = c `notElem` "()<>@,;:\\\"/[]?=" footnoteID :: Int -> String -footnoteID i = "n" ++ (show i) +footnoteID i = "n" ++ show i linkID :: Int -> String -linkID i = "l" ++ (show i) +linkID i = "l" ++ show i -- | Convert a block-level Pandoc's element to FictionBook XML representation. blockToXml :: PandocMonad m => Block -> FBM m [Content] @@ -323,14 +323,14 @@ blockToXml (Para [Math DisplayMath formula]) = insertMath NormalImage formula -- title beginning with fig: indicates that the image is a figure blockToXml (Para [Image atr alt (src,'f':'i':'g':':':tit)]) = insertImage NormalImage (Image atr alt (src,tit)) -blockToXml (Para ss) = liftM (list . el "p") $ cMapM toXml ss +blockToXml (Para ss) = (list . el "p") <$> cMapM toXml ss blockToXml (CodeBlock _ s) = return . spaceBeforeAfter . map (el "p" . el "code") . lines $ s blockToXml b@(RawBlock _ _) = do report $ BlockNotRendered b return [] blockToXml (Div _ bs) = cMapM blockToXml bs -blockToXml (BlockQuote bs) = liftM (list . el "cite") $ cMapM blockToXml bs +blockToXml (BlockQuote bs) = (list . el "cite") <$> cMapM blockToXml bs blockToXml (LineBlock lns) = blockToXml $ linesToPara lns blockToXml (OrderedList a bss) = do state <- get @@ -341,19 +341,19 @@ blockToXml (OrderedList a bss) = do itemtext <- cMapM blockToXml . paraToPlain $ bs modify (\s -> s { parentListMarker = pmrk }) -- old parent marker return . el "p" $ [ txt mrk, txt " " ] ++ itemtext - mapM (uncurry mkitem) (zip markers bss) + zipWithM mkitem markers bss blockToXml (BulletList bss) = do state <- get let level = parentBulletLevel state let pmrk = parentListMarker state let prefix = replicate (length pmrk) ' ' let bullets = ["\x2022", "\x25e6", "*", "\x2043", "\x2023"] - let mrk = prefix ++ bullets !! (level `mod` (length bullets)) + let mrk = prefix ++ bullets !! (level `mod` length bullets) let mkitem bs = do - modify (\s -> s { parentBulletLevel = (level+1) }) + modify (\s -> s { parentBulletLevel = level+1 }) itemtext <- cMapM blockToXml . paraToPlain $ bs modify (\s -> s { parentBulletLevel = level }) -- restore bullet level - return $ el "p" $ [ txt (mrk ++ " ") ] ++ itemtext + return $ el "p" $ txt (mrk ++ " ") : itemtext mapM mkitem bss blockToXml (DefinitionList defs) = cMapM mkdef defs @@ -370,7 +370,7 @@ blockToXml (DefinitionList defs) = needsBreak (Para _) = False needsBreak (Plain ins) = LineBreak `notElem` ins needsBreak _ = True -blockToXml h@(Header _ _ _) = do +blockToXml h@Header{} = do -- should not occur after hierarchicalize, except inside lists/blockquotes report $ BlockNotRendered h return [] @@ -381,12 +381,12 @@ blockToXml HorizontalRule = return blockToXml (Table caption aligns _ headers rows) = do hd <- mkrow "th" headers aligns bd <- mapM (\r -> mkrow "td" r aligns) rows - c <- return . el "emphasis" =<< cMapM toXml caption + c <- el "emphasis" <$> cMapM toXml caption return [el "table" (hd : bd), el "p" c] where mkrow :: PandocMonad m => String -> [TableCell] -> [Alignment] -> FBM m Content mkrow tag cells aligns' = - (el "tr") `liftM` (mapM (mkcell tag) (zip cells aligns')) + el "tr" <$> mapM (mkcell tag) (zip cells aligns') -- mkcell :: PandocMonad m => String -> (TableCell, Alignment) -> FBM m Content mkcell tag (cell, align) = do @@ -405,7 +405,7 @@ blockToXml Null = return [] paraToPlain :: [Block] -> [Block] paraToPlain [] = [] paraToPlain (Para inlines : rest) = - let p = (Plain (inlines ++ [LineBreak])) + let p = Plain (inlines ++ [LineBreak]) in p : paraToPlain rest paraToPlain (p:rest) = p : paraToPlain rest @@ -418,8 +418,8 @@ indent = indentBlock spacer :: String spacer = replicate 4 ' ' -- - indentBlock (Plain ins) = Plain ((Str spacer):ins) - indentBlock (Para ins) = Para ((Str spacer):ins) + indentBlock (Plain ins) = Plain (Str spacer:ins) + indentBlock (Para ins) = Para (Str spacer:ins) indentBlock (CodeBlock a s) = let s' = unlines . map (spacer++) . lines $ s in CodeBlock a s' @@ -429,7 +429,7 @@ indent = indentBlock -- indent every (explicit) line indentLines :: [Inline] -> [Inline] indentLines ins = let lns = split isLineBreak ins :: [[Inline]] - in intercalate [LineBreak] $ map ((Str spacer):) lns + in intercalate [LineBreak] $ map (Str spacer:) lns -- | Convert a Pandoc's Inline element to FictionBook XML representation. toXml :: PandocMonad m => Inline -> FBM m [Content] @@ -473,7 +473,7 @@ toXml (Link _ text (url,ttl)) = do ( [ attr ("l","href") ('#':ln_id) , uattr "type" "note" ] , ln_ref) ] -toXml img@(Image _ _ _) = insertImage InlineImage img +toXml img@Image{} = insertImage InlineImage img toXml (Note bs) = do fns <- footnotes `liftM` get let n = 1 + length fns @@ -487,7 +487,7 @@ toXml (Note bs) = do insertMath :: PandocMonad m => ImageMode -> String -> FBM m [Content] insertMath immode formula = do - htmlMath <- return . writerHTMLMathMethod . writerOptions =<< get + htmlMath <- fmap (writerHTMLMathMethod . writerOptions) get case htmlMath of WebTeX url -> do let alt = [Code nullAttr formula] @@ -531,17 +531,17 @@ replaceImagesWithAlt missingHrefs body = Nothing -> c' -- end of document -- isImage :: Content -> Bool - isImage (Elem e) = (elName e) == (uname "image") + isImage (Elem e) = elName e == uname "image" isImage _ = False -- - isMissing (Elem img@(Element _ _ _ _)) = + isMissing (Elem img@Element{}) = let imgAttrs = elAttribs img badAttrs = map (attr ("l","href")) missingHrefs in any (`elem` imgAttrs) badAttrs isMissing _ = False -- replaceNode :: Content -> Content - replaceNode n@(Elem img@(Element _ _ _ _)) = + replaceNode n@(Elem img@Element{}) = let attrs = elAttribs img alt = getAttrVal attrs (uname "alt") imtype = getAttrVal attrs (qname "l" "type") @@ -572,15 +572,15 @@ list = (:[]) -- | Convert an 'Inline' to plaintext. plain :: Inline -> String plain (Str s) = s -plain (Emph ss) = concat (map plain ss) -plain (Span _ ss) = concat (map plain ss) -plain (Strong ss) = concat (map plain ss) -plain (Strikeout ss) = concat (map plain ss) -plain (Superscript ss) = concat (map plain ss) -plain (Subscript ss) = concat (map plain ss) -plain (SmallCaps ss) = concat (map plain ss) -plain (Quoted _ ss) = concat (map plain ss) -plain (Cite _ ss) = concat (map plain ss) -- FIXME +plain (Emph ss) = concatMap plain ss +plain (Span _ ss) = concatMap plain ss +plain (Strong ss) = concatMap plain ss +plain (Strikeout ss) = concatMap plain ss +plain (Superscript ss) = concatMap plain ss +plain (Subscript ss) = concatMap plain ss +plain (SmallCaps ss) = concatMap plain ss +plain (Quoted _ ss) = concatMap plain ss +plain (Cite _ ss) = concatMap plain ss -- FIXME plain (Code _ s) = s plain Space = " " plain SoftBreak = " " @@ -588,7 +588,7 @@ plain LineBreak = "\n" plain (Math _ s) = s plain (RawInline _ _) = "" plain (Link _ text (url,_)) = concat (map plain text ++ [" <", url, ">"]) -plain (Image _ alt _) = concat (map plain alt) +plain (Image _ alt _) = concatMap plain alt plain (Note _) = "" -- FIXME -- | Create an XML element. @@ -610,11 +610,11 @@ txt s = Text $ CData CDataText s Nothing -- | Create an XML attribute with an unqualified name. uattr :: String -> String -> Text.XML.Light.Attr -uattr name val = Attr (uname name) val +uattr name = Attr (uname name) -- | Create an XML attribute with a qualified name from given namespace. attr :: (String, String) -> String -> Text.XML.Light.Attr -attr (ns, name) val = Attr (qname ns name) val +attr (ns, name) = Attr (qname ns name) -- | Unqualified name uname :: String -> QName -- cgit v1.2.3 From 4bfcbbfc3053524467fb6a59b012f8e121e7ac20 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 9 Oct 2017 19:04:10 +0300 Subject: FB2 writer: replace concatMap with cMap --- src/Text/Pandoc/Writers/FB2.hs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index ae2d5a796..736e95636 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -572,15 +572,15 @@ list = (:[]) -- | Convert an 'Inline' to plaintext. plain :: Inline -> String plain (Str s) = s -plain (Emph ss) = concatMap plain ss -plain (Span _ ss) = concatMap plain ss -plain (Strong ss) = concatMap plain ss -plain (Strikeout ss) = concatMap plain ss -plain (Superscript ss) = concatMap plain ss -plain (Subscript ss) = concatMap plain ss -plain (SmallCaps ss) = concatMap plain ss -plain (Quoted _ ss) = concatMap plain ss -plain (Cite _ ss) = concatMap plain ss -- FIXME +plain (Emph ss) = cMap plain ss +plain (Span _ ss) = cMap plain ss +plain (Strong ss) = cMap plain ss +plain (Strikeout ss) = cMap plain ss +plain (Superscript ss) = cMap plain ss +plain (Subscript ss) = cMap plain ss +plain (SmallCaps ss) = cMap plain ss +plain (Quoted _ ss) = cMap plain ss +plain (Cite _ ss) = cMap plain ss -- FIXME plain (Code _ s) = s plain Space = " " plain SoftBreak = " " @@ -588,7 +588,7 @@ plain LineBreak = "\n" plain (Math _ s) = s plain (RawInline _ _) = "" plain (Link _ text (url,_)) = concat (map plain text ++ [" <", url, ">"]) -plain (Image _ alt _) = concatMap plain alt +plain (Image _ alt _) = cMap plain alt plain (Note _) = "" -- FIXME -- | Create an XML element. -- cgit v1.2.3 From cdb9efa823db2fea051ebd25707facaee64847b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agust=C3=ADn=20Mart=C3=ADn=20Barbero?= <ambarbero@ree.es> Date: Tue, 10 Oct 2017 00:58:25 +0200 Subject: docx writer - activate evenAndOddHeaders from reference doc Fixes #3901 by checking for the evenAndOddHeaders mark in the reference doc, and copying it to the resulting docx if present. --- src/Text/Pandoc/Writers/Docx.hs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 6102d97ed..f19621744 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -549,6 +549,7 @@ writeDocx opts doc@(Pandoc meta _) = do , "w:consecutiveHyphenLimit" , "w:hyphenationZone" , "w:doNotHyphenateCap" + , "w:evenAndOddHeaders" ] settingsEntry <- copyChildren refArchive distArchive settingsPath epochtime settingsList -- cgit v1.2.3 From 00013c21eb10dc15b8edad9c0d5ec07622855ba0 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 9 Oct 2017 05:38:02 +0300 Subject: FB2 Writer: format LineBlock as poem Previously writer produced one paragraph with <empty-line/> elements, which are not allowed inside <p> according to FB2 schema. --- src/Text/Pandoc/Writers/FB2.hs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index 736e95636..9cb9098de 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -56,7 +56,7 @@ import qualified Text.Pandoc.Class as P import Text.Pandoc.Definition import Text.Pandoc.Logging import Text.Pandoc.Options (HTMLMathMethod (..), WriterOptions (..), def) -import Text.Pandoc.Shared (capitalize, isHeaderBlock, isURI, linesToPara, +import Text.Pandoc.Shared (capitalize, isHeaderBlock, isURI, orderedListMarkers) -- | Data to be written at the end of the document: @@ -331,7 +331,11 @@ blockToXml b@(RawBlock _ _) = do return [] blockToXml (Div _ bs) = cMapM blockToXml bs blockToXml (BlockQuote bs) = (list . el "cite") <$> cMapM blockToXml bs -blockToXml (LineBlock lns) = blockToXml $ linesToPara lns +blockToXml (LineBlock lns) = + (list . el "poem") <$> mapM stanza (split null lns) + where + v xs = el "v" <$> cMapM toXml xs + stanza xs = el "stanza" <$> mapM v xs blockToXml (OrderedList a bss) = do state <- get let pmrk = parentListMarker state -- cgit v1.2.3 From 75d8c99c73d2a725c6753170050fa77469c803d1 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 11 Oct 2017 20:21:55 -0700 Subject: ConTeXt writer: Use identifiers for chapters. Closes #3968. --- src/Text/Pandoc/Writers/ConTeXt.hs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index 6f2cb2b9e..0a399197d 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -449,19 +449,20 @@ sectionHeader (ident,classes,_) hdrLevel lst = do TopLevelChapter -> hdrLevel - 1 TopLevelSection -> hdrLevel TopLevelDefault -> hdrLevel - let ident' = toLabel ident + let ident' = if null ident + then empty + else brackets (text (toLabel ident)) let (section, chapter) = if "unnumbered" `elem` classes then (text "subject", text "title") else (text "section", text "chapter") return $ case level' of - -1 -> text "\\part" <> braces contents - 0 -> char '\\' <> chapter <> braces contents + -1 -> text "\\part" <> ident' <> braces contents + 0 -> char '\\' <> chapter <> ident' <> + braces contents n | n >= 1 && n <= 5 -> char '\\' <> text (concat (replicate (n - 1) "sub")) <> section - <> (if (not . null) ident' - then brackets (text ident') - else empty) + <> ident' <> braces contents <> blankline _ -> contents <> blankline -- cgit v1.2.3 From 8dd8f492c16aa87f074ff3229941007be77e46c2 Mon Sep 17 00:00:00 2001 From: d-dorazio <daniele.dorazio@adroll.com> Date: Thu, 12 Oct 2017 23:11:57 +0200 Subject: markdown writer: always write bracketed_spans' attributes --- src/Text/Pandoc/Writers/Markdown.hs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 0221ba6ef..a54f4eb85 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -952,8 +952,10 @@ inlineToMarkdown opts (Span attrs ils) = do True -> contents False | attrs == nullAttr -> contents | isEnabled Ext_bracketed_spans opts -> - "[" <> contents <> "]" <> - linkAttributes opts attrs + let attrs' = if attrs /= nullAttr + then attrsToMarkdown attrs + else empty + in "[" <> contents <> "]" <> attrs' | isEnabled Ext_raw_html opts || isEnabled Ext_native_spans opts -> tagWithAttrs "span" attrs <> contents <> text "</span>" -- cgit v1.2.3 From 6934b921b35950d0a86a966ed3aff7ba1b660ca6 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 13 Oct 2017 10:36:27 -0700 Subject: CommonMark writer: omit "fig:" prefix in image titles. This is used internally to indicate internal figures. See https://groups.google.com/d/msgid/pandoc-discuss/892c5a25-086a-4e19-b4c1-8 975cea8df0f@googlegroups.com?utm_medium=email&utm_source=footer --- src/Text/Pandoc/Writers/CommonMark.hs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/CommonMark.hs b/src/Text/Pandoc/Writers/CommonMark.hs index 446578f42..7ea76a373 100644 --- a/src/Text/Pandoc/Writers/CommonMark.hs +++ b/src/Text/Pandoc/Writers/CommonMark.hs @@ -257,6 +257,9 @@ inlineToNodes opts (SmallCaps xs) = [node (HTML_INLINE (T.pack "</span>")) []]) ++ ) inlineToNodes opts (Link _ ils (url,tit)) = (node (LINK (T.pack url) (T.pack tit)) (inlinesToNodes opts ils) :) +-- title beginning with fig: indicates implicit figure +inlineToNodes opts (Image alt ils (url,'f':'i':'g':':':tit)) = + inlineToNodes opts (Image alt ils (url,tit)) inlineToNodes opts (Image _ ils (url,tit)) = (node (IMAGE (T.pack url) (T.pack tit)) (inlinesToNodes opts ils) :) inlineToNodes _ (RawInline fmt xs) -- cgit v1.2.3 From 5e1c4223899cb24ecf5f4ad1fd26c3ac22841e77 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 15 Oct 2017 18:27:48 -0700 Subject: Handle unknown options in form `--latex-engine=foo`. Previously these were not triggering the helpful message about option name changes. --- src/Text/Pandoc/App.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 6b64a314e..46696c425 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -110,7 +110,8 @@ parseOptions options' defaults = do let (actions, args, unrecognizedOpts, errors) = getOpt' Permute options' rawArgs - let unknownOptionErrors = foldr handleUnrecognizedOption [] unrecognizedOpts + let unknownOptionErrors = foldr handleUnrecognizedOption [] $ + map (takeWhile (/= '=')) unrecognizedOpts unless (null errors && null unknownOptionErrors) $ E.throwIO $ PandocOptionError $ -- cgit v1.2.3 From a1f7a4263f56a4843b6c03ef4b986715f2bdb82d Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 15 Oct 2017 22:09:28 -0700 Subject: Class: add stRequestHeaders to CommonState, and setRequestHeader. --- src/Text/Pandoc/Class.hs | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 2b1943140..451d430ca 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -57,6 +57,7 @@ module Text.Pandoc.Class ( PandocMonad(..) , readFileFromDirs , report , setTrace + , setRequestHeader , getLog , setVerbosity , getVerbosity @@ -245,6 +246,15 @@ report msg = do setTrace :: PandocMonad m => Bool -> m () setTrace useTracing = modifyCommonState $ \st -> st{stTrace = useTracing} +-- | Set request header to use in HTTP requests. +setRequestHeader :: PandocMonad m + => String -- ^ Header name + -> String -- ^ Value + -> m () +setRequestHeader name val = modifyCommonState $ \st -> + st{ stRequestHeaders = + (name, val) : filter (\(n,_) -> n /= name) (stRequestHeaders st) } + -- | Initialize the media bag. setMediaBag :: PandocMonad m => MediaBag -> m () setMediaBag mb = modifyCommonState $ \st -> st{stMediaBag = mb} @@ -315,6 +325,8 @@ data CommonState = CommonState { stLog :: [LogMessage] -- ^ Directory to search for data files , stSourceURL :: Maybe String -- ^ Absolute URL + dir of 1st source file + , stRequestHeaders :: [(String, String)] + -- ^ Headers to add for HTTP requests , stMediaBag :: MediaBag -- ^ Media parsed from binary containers , stTranslations :: Maybe @@ -338,6 +350,7 @@ instance Default CommonState where def = CommonState { stLog = [] , stUserDataDir = Nothing , stSourceURL = Nothing + , stRequestHeaders = [] , stMediaBag = mempty , stTranslations = Nothing , stInputFiles = [] -- cgit v1.2.3 From 2f66d57616c72ad82c64cf632a10d3e842eab533 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 15 Oct 2017 22:10:13 -0700 Subject: Remove openURL from Shared (API change). Now all the guts of openURL have been put into openURL from Class. openURL is now sensitive to stRequestHeaders in CommonState and will add these custom headers when making a request. It no longer looks at the USER_AGENT environment variable, since you can now set the `User-Agent` header directly. --- src/Text/Pandoc/Class.hs | 51 +++++++++++++++++++++++++++++++++++++++------- src/Text/Pandoc/Shared.hs | 52 +---------------------------------------------- 2 files changed, 45 insertions(+), 58 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 451d430ca..65f8f33d0 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -1,10 +1,12 @@ {-# LANGUAGE CPP #-} +{-# LANGUAGE PatternGuards #-} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE ScopedTypeVariables #-} #if MIN_VERSION_base(4,8,0) #else {-# LANGUAGE OverlappingInstances #-} @@ -97,9 +99,10 @@ import Prelude hiding (readFile) import System.Random (StdGen, next, mkStdGen) import qualified System.Random as IO (newStdGen) import Codec.Archive.Zip +import qualified Data.CaseInsensitive as CI import Data.Unique (hashUnique) +import Data.List (stripPrefix) import qualified Data.Unique as IO (newUnique) -import qualified Text.Pandoc.Shared as Shared import qualified Text.Pandoc.UTF8 as UTF8 import qualified System.Directory as Directory import Text.Pandoc.Compat.Time (UTCTime) @@ -115,9 +118,21 @@ import Data.Time.Clock.POSIX ( utcTimeToPOSIXSeconds , posixSecondsToUTCTime , POSIXTime ) import Data.Time.LocalTime (TimeZone, ZonedTime, utcToZonedTime, utc) +import Data.ByteString.Base64 (decodeLenient) import Network.URI ( escapeURIString, nonStrictRelativeTo, unEscapeString, parseURIReference, isAllowedInURI, parseURI, URI(..) ) +import Network.HTTP.Client (httpLbs, responseBody, responseHeaders, + Request(port,host,requestHeaders)) +import Network.HTTP.Client (parseRequest) +import Network.HTTP.Client (newManager) +import Network.HTTP.Client.Internal (addProxy) +import Network.HTTP.Client.TLS (tlsManagerSettings) +import System.Environment (getEnv) +import Network.HTTP.Types.Header ( hContentType ) +import Network (withSocketsDo) +import Data.ByteString.Lazy (toChunks) +import qualified Control.Exception as E import qualified Data.Time.LocalTime as IO (getCurrentTimeZone) import Text.Pandoc.MediaBag (MediaBag, lookupMedia, mediaDirectory) import Text.Pandoc.Walk (walkM, walk) @@ -456,12 +471,34 @@ instance PandocMonad PandocIO where getCurrentTimeZone = liftIO IO.getCurrentTimeZone newStdGen = liftIO IO.newStdGen newUniqueHash = hashUnique <$> liftIO IO.newUnique - openURL u = do - report $ Fetching u - res <- liftIOError Shared.openURL u - case res of - Right r -> return r - Left e -> throwError $ PandocHttpError u e + + openURL u + | Just u'' <- stripPrefix "data:" u = do + let mime = takeWhile (/=',') u'' + let contents = UTF8.fromString $ + unEscapeString $ drop 1 $ dropWhile (/=',') u'' + return (decodeLenient contents, Just mime) + | otherwise = do + let toReqHeader (n, v) = (CI.mk (UTF8.fromString n), UTF8.fromString v) + customHeaders <- map toReqHeader <$> getsCommonState stRequestHeaders + report $ Fetching u + res <- liftIO $ E.try $ withSocketsDo $ do + let parseReq = parseRequest + proxy <- tryIOError (getEnv "http_proxy") + let addProxy' x = case proxy of + Left _ -> return x + Right pr -> parseReq pr >>= \r -> + return (addProxy (host r) (port r) x) + req <- parseReq u >>= addProxy' + let req' = req{requestHeaders = customHeaders ++ requestHeaders req} + resp <- newManager tlsManagerSettings >>= httpLbs req' + return (B.concat $ toChunks $ responseBody resp, + UTF8.toString `fmap` lookup hContentType (responseHeaders resp)) + + case res of + Right r -> return r + Left e -> throwError $ PandocHttpError u e + readFileLazy s = liftIOError BL.readFile s readFileStrict s = liftIOError B.readFile s diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index f0c2f172e..4c5f464d8 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -76,7 +76,6 @@ module Text.Pandoc.Shared ( renderTags', -- * File handling inDirectory, - openURL, collapseFilePath, filteredFilesFromArchive, -- * URI handling @@ -98,19 +97,17 @@ import Text.Pandoc.Definition import Text.Pandoc.Walk import Text.Pandoc.Builder (Inlines, Blocks, ToMetaValue(..)) import qualified Text.Pandoc.Builder as B -import qualified Text.Pandoc.UTF8 as UTF8 import Data.Char ( toLower, isLower, isUpper, isAlpha, isLetter, isDigit, isSpace ) import Data.List ( find, stripPrefix, intercalate ) import Data.Maybe (mapMaybe) import Data.Version ( showVersion ) import qualified Data.Map as M -import Network.URI ( URI(uriScheme), escapeURIString, unEscapeString, parseURI ) +import Network.URI ( URI(uriScheme), escapeURIString, parseURI ) 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) import Data.Generics (Typeable, Data) import qualified Control.Monad.State.Strict as S import qualified Control.Exception as E @@ -118,33 +115,16 @@ import Control.Monad (msum, unless, MonadPlus(..)) import Text.Pandoc.Pretty (charWidth) import Text.Pandoc.Generic (bottomUp) import Text.Pandoc.Compat.Time -import System.IO.Error import System.IO.Temp import Text.HTML.TagSoup (renderTagsOptions, RenderOptions(..), Tag(..), renderOptions) import Data.Monoid ((<>)) -import qualified Data.ByteString as BS -import qualified Data.ByteString.Char8 as B8 -import Data.ByteString.Base64 (decodeLenient) import Data.Sequence (ViewR(..), ViewL(..), viewl, viewr) import qualified Data.Text as T -import Data.ByteString.Lazy (toChunks) import qualified Data.ByteString.Lazy as BL import Paths_pandoc (version) - import Codec.Archive.Zip -import Network.HTTP.Client (httpLbs, responseBody, responseHeaders, - Request(port,host,requestHeaders), - HttpException) -import Network.HTTP.Client (parseRequest) -import Network.HTTP.Client (newManager) -import Network.HTTP.Client.Internal (addProxy) -import Network.HTTP.Client.TLS (tlsManagerSettings) -import System.Environment (getEnv) -import Network.HTTP.Types.Header ( hContentType, hUserAgent) -import Network (withSocketsDo) - -- | Version number of pandoc library. pandocVersion :: String pandocVersion = showVersion version @@ -606,36 +586,6 @@ inDirectory path action = E.bracket setCurrentDirectory (const $ setCurrentDirectory path >> action) --- | Read from a URL and return raw data and maybe mime type. -openURL :: String -> IO (Either HttpException (BS.ByteString, Maybe MimeType)) -openURL u - | Just u'' <- stripPrefix "data:" u = - let mime = takeWhile (/=',') u'' - contents = B8.pack $ unEscapeString $ drop 1 $ dropWhile (/=',') u'' - in return $ Right (decodeLenient contents, Just mime) - | otherwise = E.try $ withSocketsDo $ do - let parseReq = parseRequest - (proxy :: Either IOError String) <- - tryIOError $ getEnv "http_proxy" - (useragent :: Either IOError String) <- - tryIOError $ getEnv "USER_AGENT" - req <- parseReq u - req' <- case proxy of - Left _ -> return req - Right pr -> (parseReq pr >>= \r -> - return $ addProxy (host r) (port r) req) - `mplus` return req - req'' <- case useragent of - Left _ -> return req' - Right ua -> do - let headers = requestHeaders req' - let useragentheader = (hUserAgent, B8.pack ua) - let headers' = useragentheader:headers - return $ req' {requestHeaders = headers'} - resp <- newManager tlsManagerSettings >>= httpLbs req'' - return (BS.concat $ toChunks $ responseBody resp, - UTF8.toString `fmap` lookup hContentType (responseHeaders resp)) - -- -- Error reporting -- -- cgit v1.2.3 From d8804f4747b0214a3aca45ecdf6cb2f6a9d09646 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 15 Oct 2017 22:11:43 -0700 Subject: App: added --request-header option. --- src/Text/Pandoc/App.hs | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 46696c425..6bcc90357 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -75,9 +75,9 @@ import qualified System.IO as IO (Newline (..)) import Text.Pandoc import Text.Pandoc.Builder (setMeta) import Text.Pandoc.Class (PandocIO, extractMedia, fillMediaBag, getLog, - setResourcePath, setTrace, report, + setResourcePath, setTrace, report, setRequestHeader, setUserDataDir, readFileStrict, readDataFile, - readDefaultDataFile, setTranslations, + readDefaultDataFile, setTranslations, openURL, setInputFiles, setOutputFile) import Text.Pandoc.Highlighting (highlightingStyles) import Text.Pandoc.BCP47 (parseBCP47, Lang(..)) @@ -86,7 +86,7 @@ import Text.Pandoc.Writers.Math (defaultMathJaxURL, defaultKaTeXURL) import Text.Pandoc.PDF (makePDF) import Text.Pandoc.Process (pipeProcess) import Text.Pandoc.SelfContained (makeDataURI, makeSelfContained) -import Text.Pandoc.Shared (headerShift, isURI, openURL, ordNub, +import Text.Pandoc.Shared (headerShift, isURI, ordNub, safeRead, tabFilter, eastAsianLineBreakFilter) import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.XML (toEntities) @@ -491,7 +491,10 @@ convertWithOpts opts = do when (readerName == "markdown_github" || writerName == "markdown_github") $ report $ Deprecated "markdown_github" "Use gfm instead." + setResourcePath (optResourcePath opts) + mapM_ (\(n,v) -> setRequestHeader n v) (optRequestHeaders opts) + doc <- sourceToDoc sources >>= ( (if isJust (optExtractMedia opts) then fillMediaBag @@ -641,6 +644,7 @@ data Opt = Opt , optIncludeAfterBody :: [FilePath] -- ^ Files to include after body , optIncludeInHeader :: [FilePath] -- ^ Files to include in header , optResourcePath :: [FilePath] -- ^ Path to search for images etc + , optRequestHeaders :: [(String, String)] -- ^ Headers for HTTP requests , optEol :: LineEnding -- ^ Style of line-endings to use , optStripComments :: Bool -- ^ Skip HTML comments } deriving (Generic, Show) @@ -716,6 +720,7 @@ defaultOpts = Opt , optIncludeAfterBody = [] , optIncludeInHeader = [] , optResourcePath = ["."] + , optRequestHeaders = [] , optEol = Native , optStripComments = False } @@ -863,11 +868,7 @@ readSource src = case parseURI src of BS.readFile src readURI :: FilePath -> PandocIO Text -readURI src = do - res <- liftIO $ openURL src - case res of - Left e -> throwError $ PandocHttpError src e - Right (contents, _) -> return $ UTF8.toText contents +readURI src = UTF8.toText . fst <$> openURL src readFile' :: MonadIO m => FilePath -> m B.ByteString readFile' "-" = liftIO B.getContents @@ -1161,6 +1162,14 @@ options = "SEARCHPATH") "" -- "Paths to search for images and other resources" + , Option "" ["request-header"] + (ReqArg + (\arg opt -> do + let (key, val) = splitField arg + return opt{ optRequestHeaders = + (key, val) : optRequestHeaders opt }) + "NAME:VALUE") + "" , Option "" ["self-contained"] (NoArg -- cgit v1.2.3 From cba18c19a69d05ecd3e617bcbd74780482bffd7e Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 16 Oct 2017 20:36:37 -0700 Subject: RST writer: don't backslash-escape word-internal punctuation. Closes #3978. --- src/Text/Pandoc/Writers/RST.hs | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index 94c135715..cd277b51b 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -169,21 +169,24 @@ pictToRST (label, (attr, src, _, mbtarget)) = do -- | Escape special characters for RST. escapeString :: WriterOptions -> String -> String -escapeString _ [] = [] -escapeString opts (c:cs) = - case c of - _ | c `elem` ['\\','`','*','_','|'] -> '\\':c:escapeString opts cs - '\'' | isEnabled Ext_smart opts -> '\\':'\'':escapeString opts cs - '"' | isEnabled Ext_smart opts -> '\\':'"':escapeString opts cs - '-' | isEnabled Ext_smart opts -> - case cs of - '-':_ -> '\\':'-':escapeString opts cs - _ -> '-':escapeString opts cs - '.' | isEnabled Ext_smart opts -> - case cs of - '.':'.':rest -> '\\':'.':'.':'.':escapeString opts rest - _ -> '.':escapeString opts cs - _ -> c : escapeString opts cs +escapeString = escapeString' True + where + escapeString' _ _ [] = [] + escapeString' firstChar opts (c:cs) = + case c of + _ | c `elem` ['\\','`','*','_','|'] && + (firstChar || null cs) -> '\\':c:escapeString' False opts cs + '\'' | isEnabled Ext_smart opts -> '\\':'\'':escapeString' False opts cs + '"' | isEnabled Ext_smart opts -> '\\':'"':escapeString' False opts cs + '-' | isEnabled Ext_smart opts -> + case cs of + '-':_ -> '\\':'-':escapeString' False opts cs + _ -> '-':escapeString' False opts cs + '.' | isEnabled Ext_smart opts -> + case cs of + '.':'.':rest -> '\\':'.':'.':'.':escapeString' False opts rest + _ -> '.':escapeString' False opts cs + _ -> c : escapeString' False opts cs titleToRST :: PandocMonad m => [Inline] -> [Inline] -> RST m Doc titleToRST [] _ = return empty -- cgit v1.2.3 From 9cf9a64923c672fafd1458bda6f643ada83e2b1e Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 16 Oct 2017 20:54:43 -0700 Subject: RST writer: correctly handle inline code containing backticks. (Use a :literal: role.) Closes #3974. --- src/Text/Pandoc/Writers/RST.hs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index cd277b51b..8599680cf 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -483,10 +483,15 @@ inlineToRST (Quoted DoubleQuote lst) = do else return $ "“" <> contents <> "”" inlineToRST (Cite _ lst) = inlineListToRST lst -inlineToRST (Code _ str) = +inlineToRST (Code _ str) = do + opts <- gets stOptions -- we trim the string because the delimiters must adjoin a -- non-space character; see #3496 - return $ "``" <> text (trim str) <> "``" + -- we use :literal: when the code contains backticks, since + -- :literal: allows backslash-escapes; see #3974 + return $ if '`' `elem` str + then ":literal:`" <> text (escapeString opts (trim str)) <> "`" + else "``" <> text (trim str) <> "``" inlineToRST (Str str) = do opts <- gets stOptions return $ text $ -- cgit v1.2.3 From c40857b38905b9ea298a777354eb4cb0da2213c6 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 16 Oct 2017 22:03:57 -0700 Subject: Improved handling of include files in LaTeX reader. Previously `\include` wouldn't work if the included file contained, e.g., a begin without a matching end. We've changed the Tok type so that it stores a full SourcePos, rather than just a line and column. So tokens keeep track of the file they came from. This allows us to use a simpler method for includes, which doesn't require parsing the included document as a whole. Closes #3971. --- src/Text/Pandoc/Readers/LaTeX.hs | 134 +++++++++++++++++++-------------- src/Text/Pandoc/Readers/LaTeX/Types.hs | 8 +- 2 files changed, 81 insertions(+), 61 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 57db33871..8c6c7d0ff 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -71,7 +71,9 @@ import Text.Pandoc.Shared import Text.Pandoc.Readers.LaTeX.Types (Macro(..), ExpansionPoint(..), Tok(..), TokType(..)) import Text.Pandoc.Walk -import Text.Pandoc.Error (PandocError(PandocParsecError, PandocMacroLoop)) +import Text.Pandoc.Error + (PandocError(PandocParsecError, PandocParseError, PandocMacroLoop)) +import Text.Parsec.Pos -- for debugging: -- import Text.Pandoc.Extensions (getDefaultExtensions) @@ -85,7 +87,7 @@ readLaTeX :: PandocMonad m -> m Pandoc readLaTeX opts ltx = do parsed <- runParserT parseLaTeX def{ sOptions = opts } "source" - (tokenize (crFilter ltx)) + (tokenize "source" (crFilter ltx)) case parsed of Right result -> return result Left e -> throwError $ PandocParsecError (T.unpack ltx) e @@ -127,7 +129,7 @@ resolveRefs _ x = x -- res <- runIOorExplode (runParserT p defaultLaTeXState{ -- sOptions = def{ readerExtensions = -- enableExtension Ext_raw_tex $ --- getDefaultExtensions "latex" }} "source" (tokenize t)) +-- getDefaultExtensions "latex" }} "source" (tokenize "source" t)) -- case res of -- Left e -> error (show e) -- Right r -> return r @@ -238,7 +240,7 @@ rawLaTeXParser :: (PandocMonad m, HasMacros s, HasReaderOptions s) => LP m a -> ParserT String s m String rawLaTeXParser parser = do inp <- getInput - let toks = tokenize $ T.pack inp + let toks = tokenize "source" $ T.pack inp pstate <- getState let lstate = def{ sOptions = extractReaderOptions pstate } res <- lift $ runParserT ((,) <$> try (snd <$> withRaw parser) <*> getState) @@ -257,7 +259,7 @@ applyMacros s = (guardDisabled Ext_latex_macros >> return s) <|> pstate <- getState let lstate = def{ sOptions = extractReaderOptions pstate , sMacros = extractMacros pstate } - res <- runParserT retokenize lstate "math" (tokenize (T.pack s)) + res <- runParserT retokenize lstate "math" (tokenize "math" (T.pack s)) case res of Left e -> fail (show e) Right s' -> return s' @@ -278,7 +280,7 @@ inlineCommand :: PandocMonad m => ParserT String ParserState m Inlines inlineCommand = do lookAhead (try (char '\\' >> letter) <|> char '$') inp <- getInput - let toks = tokenize $ T.pack inp + let toks = tokenize "chunk" $ T.pack inp let rawinline = do (il, raw) <- try $ withRaw (inlineEnvironment <|> inlineCommand') st <- getState @@ -294,32 +296,33 @@ inlineCommand = do takeP (T.length (untokenize raw)) return il -tokenize :: Text -> [Tok] -tokenize = totoks (1, 1) +tokenize :: SourceName -> Text -> [Tok] +tokenize sourcename = totoks (initialPos sourcename) -totoks :: (Line, Column) -> Text -> [Tok] -totoks (lin,col) t = +totoks :: SourcePos -> Text -> [Tok] +totoks pos t = case T.uncons t of Nothing -> [] Just (c, rest) | c == '\n' -> - Tok (lin, col) Newline "\n" - : totoks (lin + 1,1) rest + Tok pos Newline "\n" + : totoks (setSourceColumn (incSourceLine pos 1) 1) rest | isSpaceOrTab c -> let (sps, rest') = T.span isSpaceOrTab t - in Tok (lin, col) Spaces sps - : totoks (lin, col + T.length sps) rest' + in Tok pos Spaces sps + : totoks (incSourceColumn pos (T.length sps)) + rest' | isAlphaNum c -> let (ws, rest') = T.span isAlphaNum t - in Tok (lin, col) Word ws - : totoks (lin, col + T.length ws) rest' + in Tok pos Word ws + : totoks (incSourceColumn pos (T.length ws)) rest' | c == '%' -> let (cs, rest') = T.break (== '\n') rest - in Tok (lin, col) Comment ("%" <> cs) - : totoks (lin, col + 1 + T.length cs) rest' + in Tok pos Comment ("%" <> cs) + : totoks (incSourceColumn pos (1 + T.length cs)) rest' | c == '\\' -> case T.uncons rest of - Nothing -> [Tok (lin, col) Symbol (T.singleton c)] + Nothing -> [Tok pos Symbol (T.singleton c)] Just (d, rest') | isLetterOrAt d -> -- \makeatletter is common in macro defs; @@ -328,24 +331,24 @@ totoks (lin,col) t = -- probably best for now let (ws, rest'') = T.span isLetterOrAt rest (ss, rest''') = T.span isSpaceOrTab rest'' - in Tok (lin, col) (CtrlSeq ws) ("\\" <> ws <> ss) - : totoks (lin, - col + 1 + T.length ws + T.length ss) rest''' + in Tok pos (CtrlSeq ws) ("\\" <> ws <> ss) + : totoks (incSourceColumn pos + (1 + T.length ws + T.length ss)) rest''' | d == '\t' || d == '\n' -> - Tok (lin, col) Symbol ("\\") - : totoks (lin, col + 1) rest + Tok pos Symbol ("\\") + : totoks (incSourceColumn pos 1) rest | otherwise -> - Tok (lin, col) (CtrlSeq (T.singleton d)) (T.pack [c,d]) - : totoks (lin, col + 2) rest' + Tok pos (CtrlSeq (T.singleton d)) (T.pack [c,d]) + : totoks (incSourceColumn pos 2) rest' | c == '#' -> let (t1, t2) = T.span (\d -> d >= '0' && d <= '9') rest in case safeRead (T.unpack t1) of Just i -> - Tok (lin, col) (Arg i) ("#" <> t1) - : totoks (lin, col + 1 + T.length t1) t2 + Tok pos (Arg i) ("#" <> t1) + : totoks (incSourceColumn pos (1 + T.length t1)) t2 Nothing -> - Tok (lin, col) Symbol ("#") - : totoks (lin, col + 1) t2 + Tok pos Symbol ("#") + : totoks (incSourceColumn pos 1) t2 | c == '^' -> case T.uncons rest of Just ('^', rest') -> @@ -354,20 +357,20 @@ totoks (lin,col) t = | isLowerHex d -> case T.uncons rest'' of Just (e, rest''') | isLowerHex e -> - Tok (lin, col) Esc2 (T.pack ['^','^',d,e]) - : totoks (lin, col + 4) rest''' + Tok pos Esc2 (T.pack ['^','^',d,e]) + : totoks (incSourceColumn pos 4) rest''' _ -> - Tok (lin, col) Esc1 (T.pack ['^','^',d]) - : totoks (lin, col + 3) rest'' + Tok pos Esc1 (T.pack ['^','^',d]) + : totoks (incSourceColumn pos 3) rest'' | d < '\128' -> - Tok (lin, col) Esc1 (T.pack ['^','^',d]) - : totoks (lin, col + 3) rest'' - _ -> [Tok (lin, col) Symbol ("^"), - Tok (lin, col + 1) Symbol ("^")] - _ -> Tok (lin, col) Symbol ("^") - : totoks (lin, col + 1) rest + Tok pos Esc1 (T.pack ['^','^',d]) + : totoks (incSourceColumn pos 3) rest'' + _ -> [Tok pos Symbol ("^"), + Tok (incSourceColumn pos 1) Symbol ("^")] + _ -> Tok pos Symbol ("^") + : totoks (incSourceColumn pos 1) rest | otherwise -> - Tok (lin, col) Symbol (T.singleton c) : totoks (lin, col + 1) rest + Tok pos Symbol (T.singleton c) : totoks (incSourceColumn pos 1) rest where isSpaceOrTab ' ' = True isSpaceOrTab '\t' = True @@ -393,8 +396,7 @@ satisfyTok f = where matcher t | f t = Just t | otherwise = Nothing updatePos :: SourcePos -> Tok -> [Tok] -> SourcePos - updatePos spos _ (Tok (lin,col) _ _ : _) = - setSourceColumn (setSourceLine spos lin) col + updatePos _spos _ (Tok pos _ _ : _) = pos updatePos spos _ [] = spos doMacros :: PandocMonad m => Int -> LP m () @@ -437,7 +439,7 @@ doMacros n = do else doMacros (n + 1) ExpandWhenDefined -> return () -setpos :: (Line, Column) -> Tok -> Tok +setpos :: SourcePos -> Tok -> Tok setpos spos (Tok _ tt txt) = Tok spos tt txt anyControlSeq :: PandocMonad m => LP m Tok @@ -728,15 +730,15 @@ doverb = do verbTok :: PandocMonad m => Char -> LP m Tok verbTok stopchar = do - t@(Tok (lin, col) toktype txt) <- satisfyTok (not . isNewlineTok) + t@(Tok pos toktype txt) <- satisfyTok (not . isNewlineTok) case T.findIndex (== stopchar) txt of Nothing -> return t Just i -> do let (t1, t2) = T.splitAt i txt inp <- getInput - setInput $ Tok (lin, col + i) Symbol (T.singleton stopchar) - : (totoks (lin, col + i + 1) (T.drop 1 t2)) ++ inp - return $ Tok (lin, col) toktype t1 + setInput $ Tok (incSourceColumn pos i) Symbol (T.singleton stopchar) + : (totoks (incSourceColumn pos (i + 1)) (T.drop 1 t2)) ++ inp + return $ Tok pos toktype t1 dolstinline :: PandocMonad m => LP m Inlines dolstinline = do @@ -1117,16 +1119,16 @@ tok = grouped inline <|> inlineCommand' <|> singleChar' singleChar :: PandocMonad m => LP m Tok singleChar = try $ do - Tok (lin,col) toktype t <- satisfyTok (tokTypeIn [Word, Symbol]) + Tok pos toktype t <- satisfyTok (tokTypeIn [Word, Symbol]) guard $ not $ toktype == Symbol && T.any (`Set.member` specialChars) t if T.length t > 1 then do let (t1, t2) = (T.take 1 t, T.drop 1 t) inp <- getInput - setInput $ (Tok (lin, col + 1) toktype t2) : inp - return $ Tok (lin,col) toktype t1 - else return $ Tok (lin,col) toktype t + setInput $ (Tok (incSourceColumn pos 1) toktype t2) : inp + return $ Tok pos toktype t1 + else return $ Tok pos toktype t opt :: PandocMonad m => LP m Inlines opt = bracketed inline @@ -1159,7 +1161,7 @@ withRaw :: PandocMonad m => LP m a -> LP m (a, [Tok]) withRaw parser = do inp <- getInput result <- parser - nxt <- option (Tok (0,0) Word "") (lookAhead anyTok) + nxt <- option (Tok (initialPos "source") Word "") (lookAhead anyTok) let raw = takeWhile (/= nxt) inp return (result, raw) @@ -1739,7 +1741,27 @@ include = do then map (maybeAddExtension ".sty") fs else map (maybeAddExtension ".tex") fs dirs <- (splitBy (==':') . fromMaybe ".") <$> lookupEnv "TEXINPUTS" - mconcat <$> mapM (insertIncludedFile blocks (tokenize . T.pack) dirs) fs' + mapM_ (insertIncluded dirs) fs' + return mempty + +insertIncluded :: PandocMonad m + => [FilePath] + -> FilePath + -> LP m () +insertIncluded dirs f = do + pos <- getPosition + containers <- getIncludeFiles <$> getState + when (f `elem` containers) $ do + throwError $ PandocParseError $ "Include file loop at " ++ show pos + updateState $ addIncludeFile f + mbcontents <- readFileFromDirs dirs f + contents <- case mbcontents of + Just s -> return s + Nothing -> do + report $ CouldNotLoadIncludeFile f pos + return "" + getInput >>= setInput . (tokenize f (T.pack contents) ++) + updateState dropLatestIncludeFile maybeAddExtension :: String -> FilePath -> FilePath maybeAddExtension ext fp = @@ -2394,9 +2416,7 @@ parseTableRow envname prefsufs = do >> anyTok) suffpos <- getPosition option [] (count 1 amp) - return $ map (setpos (sourceLine prefpos, sourceColumn prefpos)) pref - ++ contents ++ - map (setpos (sourceLine suffpos, sourceColumn suffpos)) suff + return $ map (setpos prefpos) pref ++ contents ++ map (setpos suffpos) suff rawcells <- sequence (map celltoks prefsufs) oldInput <- getInput cells <- sequence $ map (\ts -> setInput ts >> parseTableCell) rawcells diff --git a/src/Text/Pandoc/Readers/LaTeX/Types.hs b/src/Text/Pandoc/Readers/LaTeX/Types.hs index 2bef3cb1a..9e441714d 100644 --- a/src/Text/Pandoc/Readers/LaTeX/Types.hs +++ b/src/Text/Pandoc/Readers/LaTeX/Types.hs @@ -31,17 +31,17 @@ module Text.Pandoc.Readers.LaTeX.Types ( Tok(..) , TokType(..) , Macro(..) , ExpansionPoint(..) - , Line - , Column ) + , SourcePos + ) where import Data.Text (Text) -import Text.Parsec.Pos (Line, Column) +import Text.Parsec.Pos (SourcePos) data TokType = CtrlSeq Text | Spaces | Newline | Symbol | Word | Comment | Esc1 | Esc2 | Arg Int deriving (Eq, Ord, Show) -data Tok = Tok (Line, Column) TokType Text +data Tok = Tok SourcePos TokType Text deriving (Eq, Ord, Show) data ExpansionPoint = ExpandWhenDefined | ExpandWhenUsed -- cgit v1.2.3 From 514958f5eaac17b6429118f8a59f10baeeb124fd Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 16 Oct 2017 23:06:13 -0700 Subject: HTML writer: don't add data- prefix to unknown attributes beginning with data-. Or we'll get data-data-blah. --- src/Text/Pandoc/Writers/HTML.hs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 41b50bf70..f197bceb2 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -551,6 +551,7 @@ toAttrs kvs = do return $ map (\(x,y) -> customAttribute (fromString (if not html5 || x `Set.member` html5Attributes + || "data-" `isPrefixOf` x then x else "data-" ++ x)) (toValue y)) kvs -- cgit v1.2.3 From c2de9d749cbc21386e153a4f9efc3049a299cecc Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 16 Oct 2017 23:10:05 -0700 Subject: SelfContained: handle data-background attribute on section. This should help with #3979. In my test, I got a data uri for data-background. But it didn't actually work in the browser (the background image didn't show). Not sure whether this is a problem in reveal.js or a problem in pandoc... --- 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 9ab7be6b9..36be62f0a 100644 --- a/src/Text/Pandoc/SelfContained.hs +++ b/src/Text/Pandoc/SelfContained.hs @@ -74,12 +74,13 @@ convertTags (t@TagOpen{}:ts) | fromAttrib "data-external" t == "1" = (t:) <$> convertTags ts convertTags (t@(TagOpen tagname as):ts) | tagname `elem` - ["img", "embed", "video", "input", "audio", "source", "track"] = do + ["img", "embed", "video", "input", "audio", "source", "track", + "section"] = do as' <- mapM processAttribute as rest <- convertTags ts return $ TagOpen tagname as' : rest where processAttribute (x,y) = - if x == "src" || x == "data-src" || x == "href" || x == "poster" + if x `elem` ["src", "data-src", "href", "poster", "data-background"] then do enc <- getDataURI (fromAttrib "type" t) y return (x, enc) -- cgit v1.2.3 From 9046dbadb1147a4d9e2b114a2afc1a0292ef7762 Mon Sep 17 00:00:00 2001 From: Ben Firshman <ben@firshman.co.uk> Date: Fri, 13 Oct 2017 16:49:00 +0300 Subject: Latex reader: Skip spaces in image options --- src/Text/Pandoc/Readers/LaTeX.hs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 8c6c7d0ff..b3c637748 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -758,8 +758,10 @@ keyval = try $ do Tok _ Word key <- satisfyTok isWordTok let isSpecSym (Tok _ Symbol t) = t `elem` [".",":","-","|","\\"] isSpecSym _ = False + optional sp val <- option [] $ do symbol '=' + optional sp braced <|> (many1 (satisfyTok isWordTok <|> satisfyTok isSpecSym <|> anyControlSeq)) optional sp -- cgit v1.2.3 From e941ba05b911d01a51614b0b0060f705b2000688 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 19 Oct 2017 11:50:16 -0700 Subject: LaTeX reader: handle `\DeclareRobustCommand`. Currently it's just treated as a synonym for `\newcommand`. Handles the second case mentioned in #3983. --- src/Text/Pandoc/Readers/LaTeX.hs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index b3c637748..089d3d741 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1610,7 +1610,7 @@ isBlockCommand s = treatAsBlock :: Set.Set Text treatAsBlock = Set.fromList - [ "let", "def" + [ "let", "def", "DeclareRobustCommand" , "newcommand", "renewcommand" , "newenvironment", "renewenvironment" , "providecommand", "provideenvironment" @@ -1838,7 +1838,8 @@ newcommand = do pos <- getPosition Tok _ (CtrlSeq mtype) _ <- controlSeq "newcommand" <|> controlSeq "renewcommand" <|> - controlSeq "providecommand" + controlSeq "providecommand" <|> + controlSeq "DeclareRobustCommand" optional $ symbol '*' Tok _ (CtrlSeq name) txt <- withVerbatimMode $ anyControlSeq <|> (symbol '{' *> spaces *> anyControlSeq <* spaces <* symbol '}') -- cgit v1.2.3 From 28bb5d610dc8c96a014f610d53b937ea7b9d977e Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 19 Oct 2017 12:52:12 -0700 Subject: LaTeX reader: support `\expandafter`. Closes #3983. --- src/Text/Pandoc/Readers/LaTeX.hs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 089d3d741..37becf59f 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -372,11 +372,14 @@ totoks pos t = | otherwise -> Tok pos Symbol (T.singleton c) : totoks (incSourceColumn pos 1) rest - where isSpaceOrTab ' ' = True - isSpaceOrTab '\t' = True - isSpaceOrTab _ = False - isLetterOrAt '@' = True - isLetterOrAt c = isLetter c +isSpaceOrTab :: Char -> Bool +isSpaceOrTab ' ' = True +isSpaceOrTab '\t' = True +isSpaceOrTab _ = False + +isLetterOrAt :: Char -> Bool +isLetterOrAt '@' = True +isLetterOrAt c = isLetter c isLowerHex :: Char -> Bool isLowerHex x = x >= '0' && x <= '9' || x >= 'a' && x <= 'f' @@ -411,10 +414,19 @@ doMacros n = do Tok spos (CtrlSeq "end") _ : Tok _ Symbol "{" : Tok _ Word name : Tok _ Symbol "}" : ts -> handleMacros spos ("end" <> name) ts + Tok _ (CtrlSeq "expandafter") _ : t : ts + -> do setInput ts + doMacros n + getInput >>= setInput . combineTok t Tok spos (CtrlSeq name) _ : ts -> handleMacros spos name ts _ -> return () - where handleMacros spos name ts = do + where combineTok (Tok spos (CtrlSeq name) x) (Tok _ Word w : ts) + | T.all isLetterOrAt w = + Tok spos (CtrlSeq (name <> w)) (x1 <> w <> x2) : ts + where (x1, x2) = T.break isSpaceOrTab x + combineTok t ts = t:ts + handleMacros spos name ts = do macros <- sMacros <$> getState case M.lookup name macros of Nothing -> return () @@ -439,6 +451,7 @@ doMacros n = do else doMacros (n + 1) ExpandWhenDefined -> return () + setpos :: SourcePos -> Tok -> Tok setpos spos (Tok _ tt txt) = Tok spos tt txt -- cgit v1.2.3 From f615d7bb989bc06b17ae84b16b54c74c2fe83c19 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 19 Oct 2017 15:36:18 -0700 Subject: LaTeX reader: Strip off quotes in `\include` filenames. --- src/Text/Pandoc/Readers/LaTeX.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 37becf59f..54c19b622 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1751,7 +1751,8 @@ include = do controlSeq "include" <|> controlSeq "input" <|> controlSeq "subfile" <|> controlSeq "usepackage" skipMany $ bracketed inline -- skip options - fs <- (map trim . splitBy (==',') . T.unpack . untokenize) <$> braced + fs <- (map (T.unpack . removeDoubleQuotes . T.strip) . T.splitOn "," . + untokenize) <$> braced let fs' = if name == "usepackage" then map (maybeAddExtension ".sty") fs else map (maybeAddExtension ".tex") fs -- cgit v1.2.3 From 21328a87718818d78c658c87a2c42298453cc45f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 19 Oct 2017 15:58:12 -0700 Subject: LaTeX reader: be more forgiving in parsing command options. This was needed, for example, to make some minted options work. --- src/Text/Pandoc/Readers/LaTeX.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 54c19b622..5ac114b19 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -769,7 +769,7 @@ dolstinline = do keyval :: PandocMonad m => LP m (String, String) keyval = try $ do Tok _ Word key <- satisfyTok isWordTok - let isSpecSym (Tok _ Symbol t) = t `elem` [".",":","-","|","\\"] + let isSpecSym (Tok _ Symbol t) = t /= "]" && t /= "," isSpecSym _ = False optional sp val <- option [] $ do @@ -2095,7 +2095,7 @@ environments = M.fromList , ("BVerbatim", fancyverbEnv "BVerbatim") , ("lstlisting", do attr <- parseListingsOptions <$> option [] keyvals codeBlockWith attr <$> verbEnv "lstlisting") - , ("minted", minted) + , ("minted", minted) , ("obeylines", obeylines) , ("displaymath", mathEnvWith para Nothing "displaymath") , ("equation", mathEnvWith para Nothing "equation") -- cgit v1.2.3 From 5164ecd0ecf59f1a923ed1dcf2fd48f2fcd84dc8 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 20 Oct 2017 23:16:53 -0700 Subject: SelfContained: data-background-image instead of data-background. Really closes #3979. --- src/Text/Pandoc/SelfContained.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/SelfContained.hs b/src/Text/Pandoc/SelfContained.hs index 36be62f0a..ae44cd8cb 100644 --- a/src/Text/Pandoc/SelfContained.hs +++ b/src/Text/Pandoc/SelfContained.hs @@ -80,7 +80,7 @@ convertTags (t@(TagOpen tagname as):ts) rest <- convertTags ts return $ TagOpen tagname as' : rest where processAttribute (x,y) = - if x `elem` ["src", "data-src", "href", "poster", "data-background"] + if x `elem` ["src", "data-src", "href", "poster", "data-background-image"] then do enc <- getDataURI (fromAttrib "type" t) y return (x, enc) -- cgit v1.2.3 From 4aa84f40060884311324d3dd0a81ec22fdd9329f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 21 Oct 2017 21:51:16 -0700 Subject: In rendering PandocParsecError, only print input at error location... if the source name is `source` (i.e., the top level). Otherwise results will be misleading, since what readM does is to look up the source position of the error in the *original* input, which may not match the input used by parseFromString or in parsing from an included file. Closes #3865. Not a great fix, maybe there's something better that could be done, but this should at least avoid misleading messages. --- src/Text/Pandoc/Error.hs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs index 2bd8bef0a..a05cdfe43 100644 --- a/src/Text/Pandoc/Error.hs +++ b/src/Text/Pandoc/Error.hs @@ -90,7 +90,12 @@ handleError (Left e) = ,"\n", replicate (errColumn - 1) ' ' ,"^"] else "" - in err 65 $ "\nError at " ++ show err' ++ errorInFile + in err 65 $ "\nError at " ++ show err' ++ + -- if error comes from a chunk or included file, + -- then we won't get the right text this way: + if sourceName errPos == "source" + then errorInFile + else "" PandocMakePDFError s -> err 65 s PandocOptionError s -> err 2 s PandocSyntaxMapError s -> err 67 s -- cgit v1.2.3 From 05adbd6f19631b8e04c04784565b7bb7f62b2c8c Mon Sep 17 00:00:00 2001 From: mb21 <mb21@users.noreply.github.com> Date: Sun, 22 Oct 2017 12:03:05 +0200 Subject: LaTeX Reader: use opt function consistently --- src/Text/Pandoc/Readers/LaTeX.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 5ac114b19..cb7f3f50d 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1320,7 +1320,7 @@ inlineCommands = M.union inlineLanguageCommands $ M.fromList $ , ("i", lit "i") , ("\\", linebreak <$ (do inTableCell <- sInTableCell <$> getState guard $ not inTableCell - optional (bracketed inline) + optional opt spaces)) , (",", lit "\8198") , ("@", pure mempty) @@ -1750,7 +1750,7 @@ include = do (Tok _ (CtrlSeq name) _) <- controlSeq "include" <|> controlSeq "input" <|> controlSeq "subfile" <|> controlSeq "usepackage" - skipMany $ bracketed inline -- skip options + skipMany opt fs <- (map (T.unpack . removeDoubleQuotes . T.strip) . T.splitOn "," . untokenize) <$> braced let fs' = if name == "usepackage" @@ -2355,7 +2355,7 @@ hline = try $ do controlSeq "endhead" <|> controlSeq "endfirsthead" spaces - optional $ bracketed inline + optional opt return () lbreak :: PandocMonad m => LP m Tok -- cgit v1.2.3 From e2123a4033ea9fc5c285f521883dfb65b721f069 Mon Sep 17 00:00:00 2001 From: mb21 <mb21@users.noreply.github.com> Date: Sun, 22 Oct 2017 12:24:30 +0200 Subject: LaTeX Reader: support \lettrine --- 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 cb7f3f50d..a37c152d3 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1264,6 +1264,7 @@ inlineCommands = M.union inlineLanguageCommands $ M.fromList $ , ("cref", rawInlineOr "cref" $ doref "ref") -- from cleveref.sty , ("vref", rawInlineOr "vref" $ doref "ref+page") -- from varioref.sty , ("eqref", rawInlineOr "eqref" $ doref "eqref") -- from amsmath.sty + , ("lettrine", optional opt >> extractSpaces (spanWith ("",["lettrine"],[])) <$> tok) , ("(", mathInline . toksToString <$> manyTill anyTok (controlSeq ")")) , ("[", mathDisplay . toksToString <$> manyTill anyTok (controlSeq "]")) , ("ensuremath", mathInline . toksToString <$> braced) -- cgit v1.2.3 From 6d862ff9549445ee544d43edbccec439bed3fde6 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 23 Oct 2017 10:54:51 -0700 Subject: Downgraded SkippedContent and DocxParserWarning from WARNING to INFO --- src/Text/Pandoc/Logging.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs index ad3247ec9..f7fd503d3 100644 --- a/src/Text/Pandoc/Logging.hs +++ b/src/Text/Pandoc/Logging.hs @@ -306,7 +306,7 @@ showLogMessage msg = messageVerbosity:: LogMessage -> Verbosity messageVerbosity msg = case msg of - SkippedContent{} -> WARNING + SkippedContent{} -> INFO CouldNotParseYamlMetadata{} -> WARNING DuplicateLinkReference{} -> WARNING DuplicateNoteReference{} -> WARNING @@ -320,7 +320,7 @@ messageVerbosity msg = ParsingUnescaped{} -> INFO InlineNotRendered{} -> INFO BlockNotRendered{} -> INFO - DocxParserWarning{} -> WARNING + DocxParserWarning{} -> INFO CouldNotFetchResource{} -> WARNING CouldNotDetermineImageSize{} -> WARNING CouldNotConvertImage{} -> WARNING -- cgit v1.2.3 From 1a82ecbb6866a00689e3220d304a0fafd81358bb Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 23 Oct 2017 15:00:11 -0700 Subject: More pleasing presentation of warnings and info messages. !! warning -- info --- src/Text/Pandoc/Class.hs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 65f8f33d0..bee529bd8 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -111,7 +111,6 @@ import Text.Parsec (ParsecT, getPosition, sourceLine, sourceName) import qualified Text.Pandoc.Compat.Time as IO (getCurrentTime) import Text.Pandoc.MIME (MimeType, getMimeType, extensionFromMimeType) import Text.Pandoc.Definition -import Data.Char (toLower) import Data.Digest.Pure.SHA (sha1, showDigest) import Data.Maybe (fromMaybe) import Data.Time.Clock.POSIX ( utcTimeToPOSIXSeconds @@ -513,8 +512,10 @@ instance PandocMonad PandocIO where getCommonState = PandocIO $ lift get putCommonState x = PandocIO $ lift $ put x logOutput msg = liftIO $ do - UTF8.hPutStr stderr $ "[" ++ - map toLower (show (messageVerbosity msg)) ++ "] " + UTF8.hPutStr stderr $ + case messageVerbosity msg of + WARNING -> "!! " + _ -> ".. " alertIndent $ lines $ showLogMessage msg alertIndent :: [String] -> IO () @@ -522,7 +523,7 @@ alertIndent [] = return () alertIndent (l:ls) = do UTF8.hPutStrLn stderr l mapM_ go ls - where go l' = do UTF8.hPutStr stderr "! " + where go l' = do UTF8.hPutStr stderr " " UTF8.hPutStrLn stderr l' -- | Specialized version of parseURIReference that disallows -- cgit v1.2.3 From 896803b0d5d1f5d680d125eb75913025fa734190 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 23 Oct 2017 17:29:32 -0700 Subject: HTML reader: `htmlTag` improvements. We previously failed on cases where an attribute contained a `>` character. This patch fixes the bug. Closes #3989. --- src/Text/Pandoc/Readers/HTML.hs | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 4cbc03089..a545f3f3d 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -1125,9 +1125,15 @@ htmlTag :: (HasReaderOptions st, Monad m) htmlTag f = try $ do lookAhead (char '<') inp <- getInput - let (next : _) = canonicalizeTags $ parseTagsOptions - parseOptions{ optTagWarning = False } inp - guard $ f next + let ts = canonicalizeTags $ parseTagsOptions + parseOptions{ optTagWarning = False + , optTagPosition = True } + (inp ++ " ") -- add space to ensure that + -- we get a TagPosition after the tag + (next, ln, col) <- case ts of + (TagPosition{} : next : TagPosition ln col : _) + | f next -> return (next, ln, col) + _ -> mzero -- <www.boe.es/buscar/act.php?id=BOE-A-1996-8930#a66> -- should NOT be parsed as an HTML tag, see #2277, @@ -1138,6 +1144,11 @@ htmlTag f = try $ do [] -> False (c:cs) -> isLetter c && all isNameChar cs + let endAngle = try $ do char '>' + pos <- getPosition + guard $ (sourceLine pos == ln && + sourceColumn pos >= col) || + sourceLine pos > ln let handleTag tagname = do -- basic sanity check, since the parser is very forgiving -- and finds tags in stuff like x<y) @@ -1146,14 +1157,14 @@ htmlTag f = try $ do -- <https://example.org> should NOT be a tag either. -- tagsoup will parse it as TagOpen "https:" [("example.org","")] guard $ last tagname /= ':' - rendered <- manyTill anyChar (char '>') - return (next, rendered <> ">") + char '<' + rendered <- manyTill anyChar endAngle + return (next, "<" ++ rendered ++ ">") case next of TagComment s | "<!--" `isPrefixOf` inp -> do - count (length s + 4) anyChar - skipMany (satisfy (/='>')) - char '>' + char '<' + manyTill anyChar endAngle stripComments <- getOption readerStripComments if stripComments then return (next, "") -- cgit v1.2.3 From fda0c0119f415c6df95b20730650388c0471241d Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 23 Oct 2017 21:40:45 -0700 Subject: Implemented fenced Divs. + Added Ext_fenced_divs to Extensions (default for pandoc Markdown). + Document fenced_divs extension in manual. + Implemented fenced code divs in Markdown reader. + Added test. Closes #168. --- src/Text/Pandoc/Extensions.hs | 2 ++ src/Text/Pandoc/Parsing.hs | 2 ++ src/Text/Pandoc/Readers/Markdown.hs | 28 ++++++++++++++++++++++++++++ 3 files changed, 32 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 5d3a4cb29..8c8b405be 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -107,6 +107,7 @@ data Extension = | Ext_raw_attribute -- ^ Allow explicit raw blocks/inlines | Ext_markdown_in_html_blocks -- ^ Interpret as markdown inside HTML blocks | Ext_native_divs -- ^ Use Div blocks for contents of <div> tags + | Ext_fenced_divs -- ^ Allow fenced div syntax ::: | Ext_native_spans -- ^ Use Span inlines for contents of <span> | Ext_bracketed_spans -- ^ Bracketed spans with attributes | Ext_markdown_attribute -- ^ Interpret text inside HTML as markdown @@ -183,6 +184,7 @@ pandocExtensions = extensionsFromList , Ext_raw_attribute , Ext_markdown_in_html_blocks , Ext_native_divs + , Ext_fenced_divs , Ext_native_spans , Ext_bracketed_spans , Ext_escaped_line_breaks diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 2543f11f0..73498788d 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -1069,6 +1069,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 + stateFencedDivLevel :: Int, -- ^ Depth of fenced div stateContainers :: [String], -- ^ parent include files stateLogMessages :: [LogMessage], -- ^ log messages stateMarkdownAttribute :: Bool -- ^ True if in markdown=1 context @@ -1185,6 +1186,7 @@ defaultParserState = stateRstCustomRoles = M.empty, stateCaption = Nothing, stateInHtmlBlock = Nothing, + stateFencedDivLevel = 0, stateContainers = [], stateLogMessages = [], stateMarkdownAttribute = False diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 61c07ed12..221c834e8 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -499,6 +499,7 @@ block = do , header , lhsCodeBlock , divHtml + , divFenced , htmlBlock , table , codeBlockIndented @@ -1686,6 +1687,9 @@ endline = try $ do guardEnabled Ext_blank_before_header <|> (notFollowedBy . char =<< atxChar) -- atx header guardDisabled Ext_backtick_code_blocks <|> notFollowedBy (() <$ (lookAhead (char '`') >> codeBlockFenced)) + guardDisabled Ext_fenced_divs <|> + do divLevel <- stateFencedDivLevel <$> getState + guard (divLevel < 1) <|> notFollowedBy fenceEnd notFollowedByHtmlCloser (eof >> return mempty) <|> (guardEnabled Ext_hard_line_breaks >> return (return B.linebreak)) @@ -1930,6 +1934,30 @@ divHtml = try $ do else -- avoid backtracing return $ return (B.rawBlock "html" (rawtag <> bls)) <> contents +divFenced :: PandocMonad m => MarkdownParser m (F Blocks) +divFenced = try $ do + guardEnabled Ext_fenced_divs + nonindentSpaces + string ":::" + skipMany (char ':') + skipMany spaceChar + attribs <- attributes <|> ((\x -> ("",[x],[])) <$> many1 nonspaceChar) + skipMany spaceChar + skipMany (char ':') + blankline + updateState $ \st -> st{ stateFencedDivLevel = stateFencedDivLevel st + 1 } + bs <- mconcat <$> manyTill block fenceEnd + updateState $ \st -> st{ stateFencedDivLevel = stateFencedDivLevel st - 1 } + return $ B.divWith attribs <$> bs + +fenceEnd :: PandocMonad m => MarkdownParser m () +fenceEnd = try $ do + nonindentSpaces + string ":::" + skipMany (char ':') + blanklines + return () + rawHtmlInline :: PandocMonad m => MarkdownParser m (F Inlines) rawHtmlInline = do guardEnabled Ext_raw_html -- cgit v1.2.3 From ecb5475a2acc861d1c8201f29a024d57b44663cb Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 23 Oct 2017 23:01:37 -0700 Subject: Back to using [WARNING] and [INFO] to mark messages. --- src/Text/Pandoc/Class.hs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index bee529bd8..227505a23 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -513,9 +513,7 @@ instance PandocMonad PandocIO where putCommonState x = PandocIO $ lift $ put x logOutput msg = liftIO $ do UTF8.hPutStr stderr $ - case messageVerbosity msg of - WARNING -> "!! " - _ -> ".. " + "[" ++ show (messageVerbosity msg) ++ "] " alertIndent $ lines $ showLogMessage msg alertIndent :: [String] -> IO () @@ -523,7 +521,7 @@ alertIndent [] = return () alertIndent (l:ls) = do UTF8.hPutStrLn stderr l mapM_ go ls - where go l' = do UTF8.hPutStr stderr " " + where go l' = do UTF8.hPutStr stderr " " UTF8.hPutStrLn stderr l' -- | Specialized version of parseURIReference that disallows -- cgit v1.2.3 From 513b16a71b30a04cc91c056a22e2761f7ea554d2 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 24 Oct 2017 09:53:29 -0700 Subject: Fenced divs: ensure that paragraph at end doesn't become Plain. Added test case. --- src/Text/Pandoc/Readers/Markdown.hs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 221c834e8..a27e05fed 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -1027,6 +1027,11 @@ para = try $ do Just "div" -> () <$ lookAhead (htmlTag (~== TagClose "div")) _ -> mzero + <|> do guardEnabled Ext_fenced_divs + divLevel <- stateFencedDivLevel <$> getState + if divLevel > 0 + then lookAhead divFenceEnd + else mzero return $ do result' <- result case B.toList result' of @@ -1689,7 +1694,7 @@ endline = try $ do notFollowedBy (() <$ (lookAhead (char '`') >> codeBlockFenced)) guardDisabled Ext_fenced_divs <|> do divLevel <- stateFencedDivLevel <$> getState - guard (divLevel < 1) <|> notFollowedBy fenceEnd + guard (divLevel < 1) <|> notFollowedBy divFenceEnd notFollowedByHtmlCloser (eof >> return mempty) <|> (guardEnabled Ext_hard_line_breaks >> return (return B.linebreak)) @@ -1946,12 +1951,12 @@ divFenced = try $ do skipMany (char ':') blankline updateState $ \st -> st{ stateFencedDivLevel = stateFencedDivLevel st + 1 } - bs <- mconcat <$> manyTill block fenceEnd + bs <- mconcat <$> manyTill block divFenceEnd updateState $ \st -> st{ stateFencedDivLevel = stateFencedDivLevel st - 1 } return $ B.divWith attribs <$> bs -fenceEnd :: PandocMonad m => MarkdownParser m () -fenceEnd = try $ do +divFenceEnd :: PandocMonad m => MarkdownParser m () +divFenceEnd = try $ do nonindentSpaces string ":::" skipMany (char ':') -- cgit v1.2.3 From d2c4243f89a6368d4f9f8a511d9b026d0be19cd8 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 24 Oct 2017 14:27:49 -0700 Subject: HTML reader: td or th implicitly closes blocks within last td/th. --- src/Text/Pandoc/Readers/HTML.hs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index a545f3f3d..98ba6b8c9 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -1036,7 +1036,11 @@ _ `closes` "html" = False "body" `closes` "head" = True "a" `closes` "a" = True "li" `closes` "li" = True -"th" `closes` t | t `elem` ["th","td"] = True +t1 `closes` t2 + | t1 `elem` ["th","td"] && + t2 `Set.member` blockTags && + t2 /= "tr" && + t2 /= "table" = True "tr" `closes` t | t `elem` ["th","td","tr"] = True "dd" `closes` t | t `elem` ["dt", "dd"] = True "dt" `closes` t | t `elem` ["dt","dd"] = True -- cgit v1.2.3 From ad2df0655eadcd0119a4f0ee2fea3666bb8495de Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 24 Oct 2017 14:32:51 -0700 Subject: Revert "HTML reader: td or th implicitly closes blocks within last td/th." This reverts commit d2c4243f89a6368d4f9f8a511d9b026d0be19cd8. --- src/Text/Pandoc/Readers/HTML.hs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 98ba6b8c9..a545f3f3d 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -1036,11 +1036,7 @@ _ `closes` "html" = False "body" `closes` "head" = True "a" `closes` "a" = True "li" `closes` "li" = True -t1 `closes` t2 - | t1 `elem` ["th","td"] && - t2 `Set.member` blockTags && - t2 /= "tr" && - t2 /= "table" = True +"th" `closes` t | t `elem` ["th","td"] = True "tr" `closes` t | t `elem` ["th","td","tr"] = True "dd" `closes` t | t `elem` ["dt", "dd"] = True "dt" `closes` t | t `elem` ["dt","dd"] = True -- cgit v1.2.3 From 38b5f24a668418a27fcea11a56f8961b41515299 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 24 Oct 2017 14:42:33 -0700 Subject: HTML reader: td should close an open th or td. --- src/Text/Pandoc/Readers/HTML.hs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index a545f3f3d..6b3f06f4b 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -1037,6 +1037,7 @@ _ `closes` "html" = False "a" `closes` "a" = True "li" `closes` "li" = True "th" `closes` t | t `elem` ["th","td"] = True +"td" `closes` t | t `elem` ["th","td"] = True "tr" `closes` t | t `elem` ["th","td","tr"] = True "dd" `closes` t | t `elem` ["dt", "dd"] = True "dt" `closes` t | t `elem` ["dt","dd"] = True -- cgit v1.2.3 From ebc801cfc5ef99bf05d63aaac4c2ee471e40f372 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 24 Oct 2017 14:45:43 -0700 Subject: HTML reader: close td/th should close any open block tag... Closes #3991. --- src/Text/Pandoc/Readers/HTML.hs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 6b3f06f4b..c648c8628 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -847,6 +847,8 @@ pCloses tagtype = try $ do (TagClose "dl") | tagtype == "dd" -> return () (TagClose "table") | tagtype == "td" -> return () (TagClose "table") | tagtype == "tr" -> return () + (TagClose "td") | tagtype `Set.member` blockHtmlTags -> return () + (TagClose "th") | tagtype `Set.member` blockHtmlTags -> return () (TagClose t') | tagtype == "p" && t' `Set.member` blockHtmlTags -> return () -- see #3794 _ -> mzero -- cgit v1.2.3 From f82bcc2bf344a884851a7e7f475986055df6c27a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 24 Oct 2017 22:12:05 -0700 Subject: Added some haddock docs for Text.Pandoc.Class functions. --- src/Text/Pandoc/Class.hs | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 227505a23..51d5f5811 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -273,18 +273,22 @@ setRequestHeader name val = modifyCommonState $ \st -> setMediaBag :: PandocMonad m => MediaBag -> m () setMediaBag mb = modifyCommonState $ \st -> st{stMediaBag = mb} +-- Retrieve the media bag. getMediaBag :: PandocMonad m => m MediaBag getMediaBag = getsCommonState stMediaBag +-- Insert an item into the media bag. insertMedia :: PandocMonad m => FilePath -> Maybe MimeType -> BL.ByteString -> m () insertMedia fp mime bs = do mb <- getMediaBag let mb' = MB.insertMedia fp mime bs mb setMediaBag mb' +-- Retrieve the input filenames. getInputFiles :: PandocMonad m => m [FilePath] getInputFiles = getsCommonState stInputFiles +-- Set the input filenames. setInputFiles :: PandocMonad m => [FilePath] -> m () setInputFiles fs = do let sourceURL = case fs of @@ -299,21 +303,27 @@ setInputFiles fs = do modifyCommonState $ \st -> st{ stInputFiles = fs , stSourceURL = sourceURL } +-- Retrieve the output filename. getOutputFile :: PandocMonad m => m (Maybe FilePath) getOutputFile = getsCommonState stOutputFile +-- Set the output filename. setOutputFile :: PandocMonad m => Maybe FilePath -> m () setOutputFile mbf = modifyCommonState $ \st -> st{ stOutputFile = mbf } -setResourcePath :: PandocMonad m => [FilePath] -> m () -setResourcePath ps = modifyCommonState $ \st -> st{stResourcePath = ps} - +-- Retrieve the resource path searched by 'fetchItem'. getResourcePath :: PandocMonad m => m [FilePath] getResourcePath = getsCommonState stResourcePath +-- Set the resource path searched by 'fetchItem'. +setResourcePath :: PandocMonad m => [FilePath] -> m () +setResourcePath ps = modifyCommonState $ \st -> st{stResourcePath = ps} + +-- Get the POSIX time. getPOSIXTime :: PandocMonad m => m POSIXTime getPOSIXTime = utcTimeToPOSIXSeconds <$> getCurrentTime +-- Get the zoned time. getZonedTime :: PandocMonad m => m ZonedTime getZonedTime = do t <- getCurrentTime @@ -445,6 +455,8 @@ translateTerm term = do runIO :: PandocIO a -> IO (Either PandocError a) runIO ma = flip evalStateT def $ runExceptT $ unPandocIO ma +-- | Evaluate a 'PandocIO' operation, handling any errors +-- by exiting with an appropriate message and error status. runIOorExplode :: PandocIO a -> IO a runIOorExplode ma = runIO ma >>= handleError @@ -457,6 +469,7 @@ newtype PandocIO a = PandocIO { , MonadError PandocError ) +-- | Utility function to lift IO errors into 'PandocError's. liftIOError :: (String -> IO a) -> String -> PandocIO a liftIOError f u = do res <- liftIO $ tryIOError $ f u @@ -600,6 +613,7 @@ downloadOrRead s = do convertSlash '\\' = '/' convertSlash x = x +-- Retrieve default reference.docx. getDefaultReferenceDocx :: PandocMonad m => m Archive getDefaultReferenceDocx = do let paths = ["[Content_Types].xml", @@ -634,6 +648,7 @@ getDefaultReferenceDocx = do Nothing -> foldr addEntryToArchive emptyArchive <$> mapM pathToEntry paths +-- Retrieve default reference.odt. getDefaultReferenceODT :: PandocMonad m => m Archive getDefaultReferenceODT = do let paths = ["mimetype", @@ -760,6 +775,7 @@ extractMedia dir d = do mapM_ (writeMedia dir media) fps return $ walk (adjustImagePath dir fps) d +-- Write the contents of a media bag to a path. writeMedia :: FilePath -> MediaBag -> FilePath -> PandocIO () writeMedia dir mediabag subpath = do -- we join and split to convert a/b/c to a\b\c on Windows; @@ -778,6 +794,8 @@ adjustImagePath dir paths (Image attr lab (src, tit)) | src `elem` paths = Image attr lab (dir ++ "/" ++ src, tit) adjustImagePath _ _ x = x +-- | The 'PureState' contains ersatz representations +-- of things that would normally be obtained through IO. data PureState = PureState { stStdGen :: StdGen , stWord8Store :: [Word8] -- should be -- inifinite, @@ -863,6 +881,7 @@ newtype PandocPure a = PandocPure { , MonadError PandocError ) +-- Run a 'PandocPure' operation. runPure :: PandocPure a -> Either PandocError a runPure x = flip evalState def $ flip evalStateT def $ -- cgit v1.2.3 From 23fbf8a5338512ed713b7152beb36ac3eb7ab364 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 24 Oct 2017 22:46:06 -0700 Subject: Text.Pandoc.App: export applyFilters, applyLuaFilters. (API change) --- src/Text/Pandoc/App.hs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 6bcc90357..29df04d24 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -37,6 +37,8 @@ module Text.Pandoc.App ( , defaultOpts , parseOptions , options + , applyFilters + , applyLuaFilters ) where import qualified Control.Exception as E import Control.Monad -- cgit v1.2.3 From 424e94bd4509715cfc2dd62efadfd842e06fd472 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 26 Oct 2017 11:11:04 -0700 Subject: makePDF: add argument for pdf options, remove writerPdfArgs. - Removed writerPdfArgs from WriterOptions (API change). - Added parameter for pdf args to makePDF. --- src/Text/Pandoc/App.hs | 7 ++++--- src/Text/Pandoc/Options.hs | 2 -- src/Text/Pandoc/PDF.hs | 25 +++++++++++-------------- 3 files changed, 15 insertions(+), 19 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 29df04d24..8533fe48c 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -449,7 +449,6 @@ convertWithOpts opts = do , writerEpubChapterLevel = optEpubChapterLevel opts , writerTOCDepth = optTOCDepth opts , writerReferenceDoc = optReferenceDoc opts - , writerPdfArgs = optPdfEngineArgs opts , writerSyntaxMap = syntaxMap } @@ -512,14 +511,16 @@ convertWithOpts opts = do ByteStringWriter f -> f writerOptions doc >>= writeFnBinary outputFile TextWriter f -> case maybePdfProg of Just pdfProg -> do - res <- makePDF pdfProg f writerOptions doc + res <- makePDF pdfProg (optPdfEngineArgs opts) f + writerOptions doc case res of Right pdf -> writeFnBinary outputFile pdf Left err' -> liftIO $ E.throwIO $ PandocPDFError (UTF8.toStringLazy err') Nothing -> do let htmlFormat = format `elem` - ["html","html4","html5","s5","slidy","slideous","dzslides","revealjs"] + ["html","html4","html5","s5","slidy", + "slideous","dzslides","revealjs"] handleEntities = if (htmlFormat || format == "docbook4" || format == "docbook5" || diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index 99c7afba7..d004abca4 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -221,7 +221,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 - , writerPdfArgs :: [String] -- ^ Flags to pass to pdf-engine , writerReferenceLocation :: ReferenceLocation -- ^ Location of footnotes and references for writing markdown , writerSyntaxMap :: SyntaxMap } deriving (Show, Data, Typeable, Generic) @@ -256,7 +255,6 @@ instance Default WriterOptions where , writerEpubChapterLevel = 1 , writerTOCDepth = 3 , writerReferenceDoc = Nothing - , writerPdfArgs = [] , writerReferenceLocation = EndOfDocument , writerSyntaxMap = defaultSyntaxMap } diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index 797b5c138..f90a4454f 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -81,11 +81,12 @@ changePathSeparators = intercalate "/" . splitDirectories makePDF :: String -- ^ pdf creator (pdflatex, lualatex, xelatex, -- wkhtmltopdf, weasyprint, prince, context, pdfroff) + -> [String] -- ^ arguments to pass to pdf creator -> (WriterOptions -> Pandoc -> PandocIO Text) -- ^ writer -> WriterOptions -- ^ options -> Pandoc -- ^ document -> PandocIO (Either ByteString ByteString) -makePDF "wkhtmltopdf" writer opts doc@(Pandoc meta _) = do +makePDF "wkhtmltopdf" pdfargs writer opts doc@(Pandoc meta _) = 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' });", @@ -93,8 +94,7 @@ makePDF "wkhtmltopdf" writer opts doc@(Pandoc meta _) = do _ -> [] meta' <- metaToJSON opts (return . stringify) (return . stringify) meta let toArgs (f, mbd) = maybe [] (\d -> ['-':'-':f, d]) mbd - let args = writerPdfArgs opts ++ mathArgs ++ - concatMap toArgs + let args = pdfargs ++ mathArgs ++ concatMap toArgs [("page-size", getField "papersize" meta') ,("title", getField "title" meta') ,("margin-bottom", fromMaybe (Just "1.2in") @@ -109,23 +109,21 @@ makePDF "wkhtmltopdf" writer opts doc@(Pandoc meta _) = do source <- writer opts doc verbosity <- getVerbosity liftIO $ html2pdf verbosity "wkhtmltopdf" args source -makePDF "weasyprint" writer opts doc = do - let args = writerPdfArgs opts +makePDF "weasyprint" pdfargs writer opts doc = do source <- writer opts doc verbosity <- getVerbosity - liftIO $ html2pdf verbosity "weasyprint" args source -makePDF "prince" writer opts doc = do - let args = writerPdfArgs opts + liftIO $ html2pdf verbosity "weasyprint" pdfargs source +makePDF "prince" pdfargs writer opts doc = do source <- writer opts doc verbosity <- getVerbosity - liftIO $ html2pdf verbosity "prince" args source -makePDF "pdfroff" writer opts doc = do + liftIO $ html2pdf verbosity "prince" pdfargs source +makePDF "pdfroff" pdfargs writer opts doc = do source <- writer opts doc let args = ["-ms", "-mpdfmark", "-e", "-t", "-k", "-KUTF-8", "-i", - "--no-toc-relocation"] ++ writerPdfArgs opts + "--no-toc-relocation"] ++ pdfargs verbosity <- getVerbosity liftIO $ ms2pdf verbosity args source -makePDF program writer opts doc = do +makePDF program pdfargs writer opts doc = do let withTemp = if takeBaseName program == "context" then withTempDirectory "." else withTempDir @@ -136,11 +134,10 @@ makePDF program writer opts doc = do putCommonState commonState doc' <- handleImages tmpdir doc writer opts doc' - let args = writerPdfArgs opts case takeBaseName program of "context" -> context2pdf verbosity tmpdir source prog | prog `elem` ["pdflatex", "lualatex", "xelatex"] - -> tex2pdf' verbosity args tmpdir program source + -> tex2pdf' verbosity pdfargs tmpdir program source _ -> return $ Left $ UTF8.fromStringLazy $ "Unknown program " ++ program handleImages :: FilePath -- ^ temp dir to store images -- cgit v1.2.3 From 33eee0ceb85d8c956f3124430418e8d01d8e6106 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 26 Oct 2017 11:35:27 -0700 Subject: Comment reformat. --- 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 51d5f5811..c98a6411d 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -241,12 +241,10 @@ getVerbosity = getsCommonState stVerbosity getLog :: PandocMonad m => m [LogMessage] getLog = reverse <$> getsCommonState stLog --- | Log a message using 'logOutput'. Note that --- 'logOutput' is called only if the verbosity --- level exceeds the level of the message, but --- the message is added to the list of log messages --- that will be retrieved by 'getLog' regardless --- of its verbosity level. +-- | Log a message using 'logOutput'. Note that 'logOutput' is +-- called only if the verbosity level exceeds the level of the +-- message, but the message is added to the list of log messages +-- that will be retrieved by 'getLog' regardless of its verbosity level. report :: PandocMonad m => LogMessage -> m () report msg = do verbosity <- getsCommonState stVerbosity -- cgit v1.2.3 From 66fd3247eac9a564aca179ca9fe4fd007ee471e7 Mon Sep 17 00:00:00 2001 From: Sascha Wilde <wilde@sha-bang.de> Date: Fri, 27 Oct 2017 01:19:28 +0200 Subject: Creole reader (#3994) This is feature complete but not very thoroughly tested yet. --- src/Text/Pandoc/Readers.hs | 3 + src/Text/Pandoc/Readers/Creole.hs | 316 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 319 insertions(+) create mode 100644 src/Text/Pandoc/Readers/Creole.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers.hs b/src/Text/Pandoc/Readers.hs index 92a185e0d..9fc9c3d18 100644 --- a/src/Text/Pandoc/Readers.hs +++ b/src/Text/Pandoc/Readers.hs @@ -45,6 +45,7 @@ module Text.Pandoc.Readers , readOdt , readMarkdown , readCommonMark + , readCreole , readMediaWiki , readVimwiki , readRST @@ -76,6 +77,7 @@ import Text.Pandoc.Error import Text.Pandoc.Extensions import Text.Pandoc.Options import Text.Pandoc.Readers.CommonMark +import Text.Pandoc.Readers.Creole import Text.Pandoc.Readers.DocBook import Text.Pandoc.Readers.Docx import Text.Pandoc.Readers.EPUB @@ -117,6 +119,7 @@ readers = [ ("native" , TextReader readNative) ,("markdown_github" , TextReader readMarkdown) ,("markdown_mmd", TextReader readMarkdown) ,("commonmark" , TextReader readCommonMark) + ,("creole" , TextReader readCreole) ,("gfm" , TextReader readCommonMark) ,("rst" , TextReader readRST) ,("mediawiki" , TextReader readMediaWiki) diff --git a/src/Text/Pandoc/Readers/Creole.hs b/src/Text/Pandoc/Readers/Creole.hs new file mode 100644 index 000000000..ab90772ef --- /dev/null +++ b/src/Text/Pandoc/Readers/Creole.hs @@ -0,0 +1,316 @@ +{- + Copyright (C) 2017 Sascha Wilde <wilde@sha-bang.de> + + partly based on all the other readers, especialy the work by + John MacFarlane <jgm@berkeley.edu> and + Alexander Sulfrian <alexander.sulfrian@fu-berlin.de> + all bugs are solely created by me. + +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.Creole + Copyright : Copyright (C) 2017 Sascha Wilde + License : GNU GPL, version 2 or above + + Maintainer : Sascha Wilde <wilde@sha-bang.de> + Stability : WIP + Portability : portable + +Conversion of creole text to 'Pandoc' document. +-} +module Text.Pandoc.Readers.Creole ( readCreole + ) where + +import Control.Monad.Except (liftM2, throwError, guard) +import qualified Data.Foldable as F +import qualified Text.Pandoc.Builder as B +import Text.Pandoc.Class (PandocMonad(..)) +import Text.Pandoc.Definition +import Text.Pandoc.Options +import Text.Pandoc.Parsing hiding (enclosed) +import Text.Pandoc.Shared (crFilter) +import Data.Maybe (fromMaybe) +import Data.Monoid +import Data.Text (Text) +import qualified Data.Text as T + + +-- | Read creole from an input string and return a Pandoc document. +readCreole :: PandocMonad m + => ReaderOptions + -> Text + -> m Pandoc +readCreole opts s = do + res <- readWithM parseCreole def{ stateOptions = opts } + (T.unpack (crFilter s) ++ "\n\n") + case res of + Left e -> throwError e + Right d -> return d + +type CRLParser = ParserT [Char] ParserState + +-- +-- Utility funcitons +-- + +(<+>) :: (Monad m, Monoid a) => m a -> m a -> m a +(<+>) = liftM2 (<>) + +-- we have to redefine `enclosed' from Text.Pandoc.Parsing, because it +-- assumes, that there can't be a space after the start parser, but +-- with creole this is possible. +enclosed :: (Show end, PandocMonad m) => CRLParser m start -- ^ start parser + -> CRLParser m end -- ^ end parser + -> CRLParser m a -- ^ content parser (to be used repeatedly) + -> CRLParser m [a] +enclosed start end parser = try $ start >> many1Till parser end + +-- +-- main parser +-- + +specialChars :: [Char] +specialChars = "*/~{}\\|[]()<>\"'" + +parseCreole :: PandocMonad m => CRLParser m Pandoc +parseCreole = do + bs <- mconcat <$> many block + spaces + eof + return $ B.doc bs + + +-- +-- block parsers +-- + +block :: PandocMonad m => CRLParser m B.Blocks +block = do + res <- mempty <$ skipMany1 blankline + <|> nowiki + <|> header + <|> horizontalRule + <|> anyList 1 + <|> table + <|> para + skipMany blankline + return res + +nowiki :: PandocMonad m => CRLParser m B.Blocks +nowiki = try $ nowikiStart >> manyTill content nowikiEnd >>= return . B.codeBlock . mconcat + where + content = brackets <|> line + brackets = try $ option "" ((:[]) <$> newline) + <+> (char ' ' >> (many (char ' ') <+> string "}}}") <* eol) + line = option "" ((:[]) <$> newline) <+> manyTill anyChar eol + eol = lookAhead $ try $ nowikiEnd <|> newline + nowikiStart = optional newline >> string "{{{" >> skipMany spaceChar >> newline + nowikiEnd = try $ linebreak >> string "}}}" >> skipMany spaceChar >> newline + +header :: PandocMonad m => CRLParser m B.Blocks +header = try $ do + skipSpaces + level <- many1 (char '=') >>= return . length + guard $ level <= 6 + skipSpaces + content <- B.str <$> manyTill (noneOf "\n") headerEnd + return $ B.header level content + where + headerEnd = try $ skipSpaces >> many (char '=') >> skipSpaces >> newline + +unorderedList :: PandocMonad m => Int -> CRLParser m B.Blocks +unorderedList = list '*' B.bulletList + +orderedList :: PandocMonad m => Int -> CRLParser m B.Blocks +orderedList = list '#' B.orderedList + +anyList :: PandocMonad m => Int -> CRLParser m B.Blocks +anyList n = unorderedList n <|> orderedList n + +anyListItem :: PandocMonad m => Int -> CRLParser m B.Blocks +anyListItem n = listItem '*' n <|> listItem '#' n + +list :: PandocMonad m => Char -> ([B.Blocks] -> B.Blocks) -> Int -> CRLParser m B.Blocks +list c f n = many1 (itemPlusSublist <|> listItem c n) + >>= return . f + where itemPlusSublist = try $ listItem c n <+> anyList (n+1) + +listItem :: PandocMonad m => Char -> Int -> CRLParser m B.Blocks +listItem c n = (listStart >> many1Till inline itemEnd) + >>= return . B.plain . B.trimInlines .mconcat + where + listStart = try $ optional newline >> skipSpaces >> count n (char c) + >> (lookAhead $ noneOf [c]) >> skipSpaces + itemEnd = endOfParaElement <|> nextItem n + <|> if n < 3 then nextItem (n+1) + else nextItem (n+1) <|> nextItem (n-1) + nextItem x = lookAhead $ try $ blankline >> anyListItem x >> return mempty + +table :: PandocMonad m => CRLParser m B.Blocks +table = try $ do + headers <- optionMaybe headerRow + rows <- many1 row + return $ B.simpleTable (fromMaybe [mempty] headers) rows + where + headerRow = try $ skipSpaces >> many1Till headerCell rowEnd + headerCell = B.plain . B.trimInlines . mconcat + <$> (string "|=" >> many1Till inline cellEnd) + row = try $ skipSpaces >> many1Till cell rowEnd + cell = B.plain . B.trimInlines . mconcat + <$> (char '|' >> many1Till inline cellEnd) + rowEnd = try $ optional (char '|') >> skipSpaces >> newline + cellEnd = lookAhead $ try $ char '|' <|> rowEnd + +para :: PandocMonad m => CRLParser m B.Blocks +para = many1Till inline endOfParaElement >>= return . result . mconcat + where + result content = if F.all (==Space) content + then mempty + else B.para $ B.trimInlines content + +endOfParaElement :: PandocMonad m => CRLParser m () +endOfParaElement = lookAhead $ endOfInput <|> endOfPara + <|> startOfList <|> startOfTable + <|> startOfHeader <|> hr <|> startOfNowiki + where + endOfInput = try $ skipMany blankline >> skipSpaces >> eof + endOfPara = try $ blankline >> skipMany1 blankline + startOf :: PandocMonad m => CRLParser m a -> CRLParser m () + startOf p = try $ blankline >> p >> return mempty + startOfList = startOf $ anyList 1 + startOfTable = startOf $ table + startOfHeader = startOf header + startOfNowiki = startOf nowiki + hr = startOf horizontalRule + +horizontalRule :: PandocMonad m => CRLParser m B.Blocks +horizontalRule = try $ skipSpaces >> string "----" >> skipSpaces >> newline + >> return B.horizontalRule + +-- +-- inline parsers +-- + +inline :: PandocMonad m => CRLParser m B.Inlines +inline = choice [ whitespace + , escapedLink + , escapedChar + , link + , inlineNowiki + , placeholder + , image + , forcedLinebreak + , bold + , finalBold + , italics + , finalItalics + , str + , symbol + ] <?> "inline" + +escapedChar :: PandocMonad m => CRLParser m B.Inlines +escapedChar = (try $ char '~' >> noneOf "\t\n ") >>= return . B.str . (:[]) + +escapedLink :: PandocMonad m => CRLParser m B.Inlines +escapedLink = try $ do + char '~' + (orig, _) <- uri + return $ B.str orig + +image :: PandocMonad m => CRLParser m B.Inlines +image = try $ do + (orig, src) <- wikiImg + return $ B.image src "" (B.str $ orig) + where + linkSrc = many $ noneOf "|}\n\r\t" + linkDsc = char '|' >> many (noneOf "}\n\r\t") + wikiImg = try $ do + string "{{" + src <- linkSrc + dsc <- option "" linkDsc + string "}}" + return (dsc, src) + +link :: PandocMonad m => CRLParser m B.Inlines +link = try $ do + (orig, src) <- uriLink <|> wikiLink + return $ B.link src "" orig + where + linkSrc = many $ noneOf "|]\n\r\t" + linkDsc :: PandocMonad m => String -> CRLParser m B.Inlines + linkDsc otxt = B.str + <$> (try $ option otxt + (char '|' >> many (noneOf "]\n\r\t"))) + linkImg = try $ char '|' >> image + wikiLink = try $ do + string "[[" + src <- linkSrc + dsc <- linkImg <|> linkDsc src + string "]]" + return (dsc, src) + uriLink = try $ do + (orig, src) <- uri + return (B.str orig, src) + +inlineNowiki :: PandocMonad m => CRLParser m B.Inlines +inlineNowiki = B.code <$> (start >> manyTill (noneOf "\n\r") end) + where + start = try $ string "{{{" + end = try $ string "}}}" >> (lookAhead $ noneOf "}") + +placeholder :: PandocMonad m => CRLParser m B.Inlines +-- The semantics of the placeholder is basicallly implementation +-- dependent, so there is no way to DTRT for all cases. +-- So for now we just drop them. +placeholder = B.text <$> (try $ string "<<<" >> manyTill anyChar (string ">>>") + >> return "") + +whitespace :: PandocMonad m => CRLParser m B.Inlines +whitespace = (lb <|> regsp) >>= return + where lb = try $ skipMany spaceChar >> linebreak >> return B.space + regsp = try $ skipMany1 spaceChar >> return B.space + +linebreak :: PandocMonad m => CRLParser m B.Inlines +linebreak = newline >> notFollowedBy newline >> (lastNewline <|> innerNewline) + where lastNewline = eof >> return mempty + innerNewline = return B.space + +symbol :: PandocMonad m => CRLParser m B.Inlines +symbol = oneOf specialChars >>= return . B.str . (:[]) + +str :: PandocMonad m => CRLParser m B.Inlines +str = let strChar = noneOf ("\t\n " ++ specialChars) in + many1 strChar >>= return . B.str + +bold :: PandocMonad m => CRLParser m B.Inlines +bold = B.strong . mconcat <$> + enclosed (string "**") (try $ string "**") inline + +italics :: PandocMonad m => CRLParser m B.Inlines +italics = B.emph . mconcat <$> + enclosed (string "//") (try $ string "//") inline + +finalBold :: PandocMonad m => CRLParser m B.Inlines +finalBold = B.strong . mconcat <$> + try (string "**" >> many1Till inline endOfParaElement) + +finalItalics :: PandocMonad m => CRLParser m B.Inlines +finalItalics = B.emph . mconcat <$> + try (string "//" >> many1Till inline endOfParaElement) + +forcedLinebreak :: PandocMonad m => CRLParser m B.Inlines +forcedLinebreak = try $ string "\\\\" >> return B.linebreak -- cgit v1.2.3 From 0b09409385e7bb249e666d889ba8b13adc553607 Mon Sep 17 00:00:00 2001 From: Kolen Cheung <christian.kolen@gmail.com> Date: Thu, 26 Oct 2017 22:57:13 -0700 Subject: update years in copyright --- src/Text/Pandoc/Lua/Util.hs | 2 +- src/Text/Pandoc/Readers/CommonMark.hs | 4 ++-- src/Text/Pandoc/Writers/CommonMark.hs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/Util.hs b/src/Text/Pandoc/Lua/Util.hs index 1b6338e64..6b1d51159 100644 --- a/src/Text/Pandoc/Lua/Util.hs +++ b/src/Text/Pandoc/Lua/Util.hs @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {-# LANGUAGE FlexibleInstances #-} {- | Module : Text.Pandoc.Lua.Util - Copyright : © 2012–2016 John MacFarlane, + Copyright : © 2012–2017 John MacFarlane, © 2017 Albert Krewinkel License : GNU GPL, version 2 or above diff --git a/src/Text/Pandoc/Readers/CommonMark.hs b/src/Text/Pandoc/Readers/CommonMark.hs index 2dba18c9f..62a7f4119 100644 --- a/src/Text/Pandoc/Readers/CommonMark.hs +++ b/src/Text/Pandoc/Readers/CommonMark.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2015-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.CommonMark - Copyright : Copyright (C) 2015 John MacFarlane + Copyright : Copyright (C) 2015-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/CommonMark.hs b/src/Text/Pandoc/Writers/CommonMark.hs index 7ea76a373..9dc7158fe 100644 --- a/src/Text/Pandoc/Writers/CommonMark.hs +++ b/src/Text/Pandoc/Writers/CommonMark.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2015 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2015-2017 John MacFarlane <jgm@berkeley.edu> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.CommonMark - Copyright : Copyright (C) 2015 John MacFarlane + Copyright : Copyright (C) 2015-2017 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> -- cgit v1.2.3 From 7f8a3c6cb70e61666598873dbcea8ef45ab85b56 Mon Sep 17 00:00:00 2001 From: hftf <hftf@users.noreply.github.com> Date: Fri, 27 Oct 2017 18:45:00 -0400 Subject: Consistent underline for Readers (#2270) * Added underlineSpan builder function. This can be easily updated if needed. The purpose is for Readers to transform underlines consistently. * Docx Reader: Use underlineSpan and update test * Org Reader: Use underlineSpan and add test * Textile Reader: Use underlineSpan and add test case * Txt2Tags Reader: Use underlineSpan and update test * HTML Reader: Use underlineSpan and add test case --- src/Text/Pandoc/Readers/Docx.hs | 5 +++-- src/Text/Pandoc/Readers/HTML.hs | 6 +++++- src/Text/Pandoc/Readers/Org/Inlines.hs | 4 ++-- src/Text/Pandoc/Readers/Textile.hs | 4 ++-- src/Text/Pandoc/Readers/Txt2Tags.hs | 4 ++-- src/Text/Pandoc/Shared.hs | 8 ++++++++ 6 files changed, 22 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index 21aa358f2..2448d24e5 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -52,12 +52,13 @@ implemented, [-] means partially implemented): * Inlines - [X] Str - - [X] Emph (italics and underline both read as Emph) + - [X] Emph - [X] Strong - [X] Strikeout - [X] Superscript - [X] Subscript - [X] SmallCaps + - [-] Underline (was previously converted to Emph) - [ ] Quoted - [ ] Cite - [X] Code (styled with `VerbatimChar`) @@ -287,7 +288,7 @@ runStyleToTransform rPr | Just SubScrpt <- rVertAlign rPr = subscript . (runStyleToTransform rPr {rVertAlign = Nothing}) | Just "single" <- rUnderline rPr = - emph . (runStyleToTransform rPr {rUnderline = Nothing}) + underlineSpan . (runStyleToTransform rPr {rUnderline = Nothing}) | otherwise = id runToInlines :: PandocMonad m => Run -> DocxContext m Inlines diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index c648c8628..277405b09 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -45,7 +45,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, addMetaField - , escapeURI, safeRead, crFilter ) + , escapeURI, safeRead, crFilter, underlineSpan ) import Text.Pandoc.Options ( ReaderOptions(readerExtensions,readerStripComments), extensionEnabled, Extension (Ext_epub_html_exts, @@ -627,6 +627,7 @@ inline = choice , pSuperscript , pSubscript , pStrikeout + , pUnderline , pLineBreak , pLink , pImage @@ -696,6 +697,9 @@ pStrikeout = do contents <- mconcat <$> manyTill inline (pCloses "span") return $ B.strikeout contents) +pUnderline :: PandocMonad m => TagParser m Inlines +pUnderline = pInlinesInTags "u" underlineSpan <|> pInlinesInTags "ins" underlineSpan + pLineBreak :: PandocMonad m => TagParser m Inlines pLineBreak = do pSelfClosing (=="br") (const True) diff --git a/src/Text/Pandoc/Readers/Org/Inlines.hs b/src/Text/Pandoc/Readers/Org/Inlines.hs index 39f4dc926..f3649af66 100644 --- a/src/Text/Pandoc/Readers/Org/Inlines.hs +++ b/src/Text/Pandoc/Readers/Org/Inlines.hs @@ -45,6 +45,7 @@ import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Readers.LaTeX (inlineCommand, rawLaTeXInline) +import Text.Pandoc.Shared (underlineSpan) import Text.TeXMath (DisplayType (..), readTeX, writePandoc) import qualified Text.TeXMath.Readers.MathML.EntityMap as MathMLEntityMap @@ -572,9 +573,8 @@ strong = fmap B.strong <$> emphasisBetween '*' strikeout :: PandocMonad m => OrgParser m (F Inlines) strikeout = fmap B.strikeout <$> emphasisBetween '+' --- There is no underline, so we use strong instead. underline :: PandocMonad m => OrgParser m (F Inlines) -underline = fmap B.strong <$> emphasisBetween '_' +underline = fmap underlineSpan <$> emphasisBetween '_' verbatim :: PandocMonad m => OrgParser m (F Inlines) verbatim = return . B.code <$> verbatimBetween '=' diff --git a/src/Text/Pandoc/Readers/Textile.hs b/src/Text/Pandoc/Readers/Textile.hs index 9cd3d2c36..90567ef23 100644 --- a/src/Text/Pandoc/Readers/Textile.hs +++ b/src/Text/Pandoc/Readers/Textile.hs @@ -68,7 +68,7 @@ import Text.Pandoc.Options import Text.Pandoc.Parsing import Text.Pandoc.Readers.HTML (htmlTag, isBlockTag, isInlineTag) import Text.Pandoc.Readers.LaTeX (rawLaTeXBlock, rawLaTeXInline) -import Text.Pandoc.Shared (trim, crFilter) +import Text.Pandoc.Shared (trim, crFilter, underlineSpan) import Data.Text (Text) import qualified Data.Text as T @@ -468,7 +468,7 @@ inlineMarkup = choice [ simpleInline (string "??") (B.cite []) , simpleInline (string "__") B.emph , simpleInline (char '*') B.strong , simpleInline (char '_') B.emph - , simpleInline (char '+') B.emph -- approximates underline + , simpleInline (char '+') underlineSpan , simpleInline (char '-' <* notFollowedBy (char '-')) B.strikeout , simpleInline (char '^') B.superscript , simpleInline (char '~') B.subscript diff --git a/src/Text/Pandoc/Readers/Txt2Tags.hs b/src/Text/Pandoc/Readers/Txt2Tags.hs index 2d3e541cf..0e68cdfb7 100644 --- a/src/Text/Pandoc/Readers/Txt2Tags.hs +++ b/src/Text/Pandoc/Readers/Txt2Tags.hs @@ -41,7 +41,7 @@ import qualified Text.Pandoc.Builder as B import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (space, spaces, uri) -import Text.Pandoc.Shared (compactify, compactifyDL, escapeURI, crFilter) +import Text.Pandoc.Shared (compactify, compactifyDL, escapeURI, crFilter, underlineSpan) import Control.Monad (guard, void, when) import Control.Monad.Reader (Reader, asks, runReader) import Data.Default @@ -393,7 +393,7 @@ bold :: T2T Inlines bold = inlineMarkup inline B.strong '*' (B.str) underline :: T2T Inlines -underline = inlineMarkup inline B.emph '_' (B.str) +underline = inlineMarkup inline underlineSpan '_' (B.str) strike :: T2T Inlines strike = inlineMarkup inline B.strikeout '-' (B.str) diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 4c5f464d8..2307470a1 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -72,6 +72,7 @@ module Text.Pandoc.Shared ( addMetaField, makeMeta, eastAsianLineBreakFilter, + underlineSpan, -- * TagSoup HTML handling renderTags', -- * File handling @@ -563,6 +564,13 @@ eastAsianLineBreakFilter = bottomUp go _ -> x:SoftBreak:y:zs go xs = xs +-- | Builder for underline. +-- This probably belongs in Builder.hs in pandoc-types. +-- Will be replaced once Underline is an element. +underlineSpan :: Inlines -> Inlines +underlineSpan = B.spanWith ("", ["underline"], []) + + -- -- TagSoup HTML handling -- -- cgit v1.2.3 From ff16db1aa306113132cc6cfaa70791a0db75e0a5 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 27 Oct 2017 20:28:29 -0700 Subject: Automatic reformating by stylish-haskell. --- src/Text/Pandoc/App.hs | 34 +++---- src/Text/Pandoc/BCP47.hs | 12 +-- src/Text/Pandoc/CSV.hs | 6 +- src/Text/Pandoc/Data.hs | 2 +- src/Text/Pandoc/Error.hs | 8 +- src/Text/Pandoc/Extensions.hs | 4 +- src/Text/Pandoc/Lua.hs | 18 ++-- src/Text/Pandoc/Lua/PandocModule.hs | 18 ++-- src/Text/Pandoc/Lua/StackInstances.hs | 11 +- src/Text/Pandoc/Lua/Util.hs | 4 +- src/Text/Pandoc/Options.hs | 4 +- src/Text/Pandoc/PDF.hs | 15 ++- src/Text/Pandoc/Readers.hs | 10 +- src/Text/Pandoc/Readers/CommonMark.hs | 54 +++++----- src/Text/Pandoc/Readers/Creole.hs | 12 +-- src/Text/Pandoc/Readers/Docx.hs | 4 +- src/Text/Pandoc/Readers/EPUB.hs | 4 +- src/Text/Pandoc/Readers/Haddock.hs | 4 +- src/Text/Pandoc/Readers/LaTeX.hs | 116 +++++++++++---------- src/Text/Pandoc/Readers/Markdown.hs | 9 +- src/Text/Pandoc/Readers/MediaWiki.hs | 8 +- src/Text/Pandoc/Readers/Native.hs | 2 +- src/Text/Pandoc/Readers/OPML.hs | 4 +- src/Text/Pandoc/Readers/Odt.hs | 36 +++---- src/Text/Pandoc/Readers/Odt/Arrows/State.hs | 18 ++-- src/Text/Pandoc/Readers/Odt/Arrows/Utils.hs | 8 +- src/Text/Pandoc/Readers/Odt/Base.hs | 4 +- src/Text/Pandoc/Readers/Odt/ContentReader.hs | 46 ++++----- src/Text/Pandoc/Readers/Odt/Generic/Fallible.hs | 2 +- src/Text/Pandoc/Readers/Odt/Generic/Utils.hs | 14 +-- src/Text/Pandoc/Readers/Odt/Namespaces.hs | 8 +- src/Text/Pandoc/Readers/Odt/StyleReader.hs | 51 +++++----- src/Text/Pandoc/Readers/Org/ParserState.hs | 11 +- src/Text/Pandoc/Readers/RST.hs | 42 ++++---- src/Text/Pandoc/Readers/TWiki.hs | 8 +- src/Text/Pandoc/Readers/Textile.hs | 8 +- src/Text/Pandoc/Readers/TikiWiki.hs | 46 +++++---- src/Text/Pandoc/Readers/Txt2Tags.hs | 21 ++-- src/Text/Pandoc/Readers/Vimwiki.hs | 73 ++++++------- src/Text/Pandoc/SelfContained.hs | 6 +- src/Text/Pandoc/Shared.hs | 130 ++++++++++++------------ src/Text/Pandoc/Templates.hs | 2 +- src/Text/Pandoc/Translations.hs | 10 +- src/Text/Pandoc/Writers.hs | 8 +- src/Text/Pandoc/Writers/CommonMark.hs | 6 +- src/Text/Pandoc/Writers/ConTeXt.hs | 50 ++++----- src/Text/Pandoc/Writers/Custom.hs | 6 +- src/Text/Pandoc/Writers/Docbook.hs | 2 +- src/Text/Pandoc/Writers/Docx.hs | 10 +- src/Text/Pandoc/Writers/DokuWiki.hs | 4 +- src/Text/Pandoc/Writers/EPUB.hs | 16 +-- src/Text/Pandoc/Writers/FB2.hs | 9 +- src/Text/Pandoc/Writers/HTML.hs | 16 +-- src/Text/Pandoc/Writers/Haddock.hs | 2 +- src/Text/Pandoc/Writers/JATS.hs | 6 +- src/Text/Pandoc/Writers/LaTeX.hs | 4 +- src/Text/Pandoc/Writers/Man.hs | 2 +- src/Text/Pandoc/Writers/Markdown.hs | 6 +- src/Text/Pandoc/Writers/MediaWiki.hs | 2 +- src/Text/Pandoc/Writers/Ms.hs | 50 ++++----- src/Text/Pandoc/Writers/Native.hs | 2 +- src/Text/Pandoc/Writers/ODT.hs | 2 +- src/Text/Pandoc/Writers/OpenDocument.hs | 4 +- src/Text/Pandoc/Writers/Org.hs | 4 +- src/Text/Pandoc/Writers/RST.hs | 14 +-- src/Text/Pandoc/Writers/RTF.hs | 2 +- src/Text/Pandoc/Writers/TEI.hs | 4 +- src/Text/Pandoc/Writers/Textile.hs | 2 +- src/Text/Pandoc/Writers/ZimWiki.hs | 6 +- 69 files changed, 576 insertions(+), 570 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 8533fe48c..ed9992911 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -42,18 +42,18 @@ module Text.Pandoc.App ( ) where import qualified Control.Exception as E import Control.Monad -import Control.Monad.Except (throwError, catchError) +import Control.Monad.Except (catchError, throwError) import Control.Monad.Trans -import Data.Monoid import Data.Aeson (FromJSON (..), ToJSON (..), defaultOptions, eitherDecode', encode, genericToEncoding) import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as B import Data.Char (toLower, toUpper) import Data.Foldable (foldrM) -import Data.List (intercalate, isPrefixOf, isSuffixOf, sort, find) +import Data.List (find, intercalate, isPrefixOf, isSuffixOf, sort) import qualified Data.Map as M import Data.Maybe (fromMaybe, isJust, isNothing) +import Data.Monoid import qualified Data.Set as Set import Data.Text (Text) import qualified Data.Text as T @@ -72,25 +72,25 @@ import System.Environment (getArgs, getEnvironment, getProgName) import System.Exit (ExitCode (..), exitSuccess) import System.FilePath import System.IO (nativeNewline, stdout) -import System.IO.Error (isDoesNotExistError) import qualified System.IO as IO (Newline (..)) +import System.IO.Error (isDoesNotExistError) import Text.Pandoc +import Text.Pandoc.BCP47 (Lang (..), parseBCP47) import Text.Pandoc.Builder (setMeta) -import Text.Pandoc.Class (PandocIO, extractMedia, fillMediaBag, getLog, - setResourcePath, setTrace, report, setRequestHeader, - setUserDataDir, readFileStrict, readDataFile, - readDefaultDataFile, setTranslations, openURL, - setInputFiles, setOutputFile) +import Text.Pandoc.Class (PandocIO, extractMedia, fillMediaBag, getLog, openURL, + readDataFile, readDefaultDataFile, readFileStrict, + report, setInputFiles, setOutputFile, + setRequestHeader, setResourcePath, setTrace, + setTranslations, setUserDataDir) import Text.Pandoc.Highlighting (highlightingStyles) -import Text.Pandoc.BCP47 (parseBCP47, Lang(..)) -import Text.Pandoc.Lua (runLuaFilter, LuaException(..)) -import Text.Pandoc.Writers.Math (defaultMathJaxURL, defaultKaTeXURL) +import Text.Pandoc.Lua (LuaException (..), runLuaFilter) import Text.Pandoc.PDF (makePDF) import Text.Pandoc.Process (pipeProcess) import Text.Pandoc.SelfContained (makeDataURI, makeSelfContained) -import Text.Pandoc.Shared (headerShift, isURI, ordNub, - safeRead, tabFilter, eastAsianLineBreakFilter) +import Text.Pandoc.Shared (eastAsianLineBreakFilter, headerShift, isURI, ordNub, + safeRead, tabFilter) import qualified Text.Pandoc.UTF8 as UTF8 +import Text.Pandoc.Writers.Math (defaultKaTeXURL, defaultMathJaxURL) import Text.Pandoc.XML (toEntities) import Text.Printf #ifndef _WINDOWS @@ -414,7 +414,7 @@ convertWithOpts opts = do case lookup "lang" (optMetadata opts) of Just l -> case parseBCP47 l of - Left _ -> return () + Left _ -> return () Right l' -> setTranslations l' Nothing -> setTranslations $ Lang "en" "" "US" [] @@ -649,7 +649,7 @@ data Opt = Opt , optResourcePath :: [FilePath] -- ^ Path to search for images etc , optRequestHeaders :: [(String, String)] -- ^ Headers for HTTP requests , optEol :: LineEnding -- ^ Style of line-endings to use - , optStripComments :: Bool -- ^ Skip HTML comments + , optStripComments :: Bool -- ^ Skip HTML comments } deriving (Generic, Show) instance ToJSON Opt where @@ -844,7 +844,7 @@ applyLuaFilters mbDatadir filters format d = do let go f d' = do res <- runLuaFilter mbDatadir f format d' case res of - Right x -> return x + Right x -> return x Left (LuaException s) -> E.throw (PandocFilterError f s) foldrM ($) d $ map go expandedFilters diff --git a/src/Text/Pandoc/BCP47.hs b/src/Text/Pandoc/BCP47.hs index 1790ccfb7..d49a0c115 100644 --- a/src/Text/Pandoc/BCP47.hs +++ b/src/Text/Pandoc/BCP47.hs @@ -35,18 +35,18 @@ module Text.Pandoc.BCP47 ( ) where import Control.Monad (guard) -import Data.Char (isAscii, isLetter, isUpper, isLower, toUpper, toLower, - isAlphaNum) +import Data.Char (isAlphaNum, isAscii, isLetter, isLower, isUpper, toLower, + toUpper) import Data.List (intercalate) import Text.Pandoc.Definition import Text.Pandoc.Options import qualified Text.Parsec as P -- | Represents BCP 47 language/country code. -data Lang = Lang{ langLanguage :: String - , langScript :: String - , langRegion :: String - , langVariants :: [String] } +data Lang = Lang{ langLanguage :: String + , langScript :: String + , langRegion :: String + , langVariants :: [String] } deriving (Eq, Ord, Show) -- | Render a Lang as BCP 47. diff --git a/src/Text/Pandoc/CSV.hs b/src/Text/Pandoc/CSV.hs index 810c58f92..ee68d988b 100644 --- a/src/Text/Pandoc/CSV.hs +++ b/src/Text/Pandoc/CSV.hs @@ -34,11 +34,11 @@ module Text.Pandoc.CSV ( ParseError ) where -import Text.Parsec -import Text.Parsec.Text (Parser) +import Control.Monad (void) import Data.Text (Text) import qualified Data.Text as T -import Control.Monad (void) +import Text.Parsec +import Text.Parsec.Text (Parser) data CSVOptions = CSVOptions{ csvDelim :: Char diff --git a/src/Text/Pandoc/Data.hs b/src/Text/Pandoc/Data.hs index b8e189440..6bb6069ca 100644 --- a/src/Text/Pandoc/Data.hs +++ b/src/Text/Pandoc/Data.hs @@ -2,8 +2,8 @@ module Text.Pandoc.Data (dataFiles) where -import Data.FileEmbed import qualified Data.ByteString as B +import Data.FileEmbed import System.FilePath (splitDirectories) import qualified System.FilePath.Posix as Posix diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs index a05cdfe43..ade33e26d 100644 --- a/src/Text/Pandoc/Error.hs +++ b/src/Text/Pandoc/Error.hs @@ -37,12 +37,12 @@ module Text.Pandoc.Error ( import Control.Exception (Exception) import Data.Generics (Typeable) import GHC.Generics (Generic) +import Network.HTTP.Client (HttpException) +import System.Exit (ExitCode (..), exitWith) +import System.IO (stderr) +import qualified Text.Pandoc.UTF8 as UTF8 import Text.Parsec.Error import Text.Parsec.Pos hiding (Line) -import qualified Text.Pandoc.UTF8 as UTF8 -import System.Exit (exitWith, ExitCode(..)) -import System.IO (stderr) -import Network.HTTP.Client (HttpException) type Input = String diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 8c8b405be..b1b8336ef 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -45,14 +45,14 @@ module Text.Pandoc.Extensions ( Extension(..) , githubMarkdownExtensions , multimarkdownExtensions ) where +import Data.Aeson (FromJSON (..), ToJSON (..), defaultOptions, + genericToEncoding) import Data.Bits (clearBit, setBit, testBit, (.|.)) import Data.Data (Data) import Data.Typeable (Typeable) import GHC.Generics (Generic) import Text.Pandoc.Shared (safeRead) import Text.Parsec -import Data.Aeson (ToJSON(..), FromJSON(..), - genericToEncoding, defaultOptions) newtype Extensions = Extensions Integer deriving (Show, Read, Eq, Ord, Data, Typeable, Generic) diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 583d43a2e..091deab8c 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -36,19 +36,19 @@ module Text.Pandoc.Lua (LuaException (..), pushPandocModule, runLuaFilter) where import Control.Monad (mplus, unless, when, (>=>)) import Control.Monad.Identity (Identity) import Control.Monad.Trans (MonadIO (..)) -import Data.Data (DataType, Data, toConstr, showConstr, dataTypeOf, - dataTypeConstrs, dataTypeName, tyconUQname) +import Data.Data (Data, DataType, dataTypeConstrs, dataTypeName, dataTypeOf, + showConstr, toConstr, tyconUQname) import Data.Foldable (foldrM) import Data.IORef (IORef, newIORef, readIORef) import Data.Map (Map) import Data.Maybe (isJust) -import Foreign.Lua (Lua, FromLuaStack (peek), LuaException (..), StackIndex, +import Foreign.Lua (FromLuaStack (peek), Lua, LuaException (..), StackIndex, Status (OK), ToLuaStack (push)) -import Text.Pandoc.Class (PandocIO, getMediaBag, setMediaBag, - getCommonState, CommonState) -import Text.Pandoc.MediaBag (MediaBag) +import Text.Pandoc.Class (CommonState, PandocIO, getCommonState, getMediaBag, + setMediaBag) import Text.Pandoc.Definition -import Text.Pandoc.Lua.PandocModule (pushPandocModule, pushMediaBagModule) +import Text.Pandoc.Lua.PandocModule (pushMediaBagModule, pushPandocModule) +import Text.Pandoc.MediaBag (MediaBag) import Text.Pandoc.Walk (walkM) import qualified Data.Map as Map @@ -182,7 +182,7 @@ instance FromLuaStack LuaFilter where filterFn <- Lua.tryLua (peek (-1)) Lua.pop 1 return $ case filterFn of - Left _ -> acc + Left _ -> acc Right f -> (c, f) : acc in LuaFilter . Map.fromList <$> foldrM fn [] constrs @@ -209,7 +209,7 @@ elementOrList x = do mbres <- Lua.peekEither topOfStack case mbres of Right res -> [res] <$ Lua.pop 1 - Left _ -> Lua.toList topOfStack <* Lua.pop 1 + Left _ -> Lua.toList topOfStack <* Lua.pop 1 singleElement :: FromLuaStack a => a -> Lua a singleElement x = do diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index 3c27ecffb..c42e180c6 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -34,24 +34,24 @@ module Text.Pandoc.Lua.PandocModule import Control.Monad (unless, zipWithM_) import Data.ByteString.Char8 (unpack) import Data.Default (Default (..)) +import Data.Digest.Pure.SHA (sha1, showDigest) import Data.IORef import Data.Maybe (fromMaybe) import Data.Text (pack) -import Foreign.Lua (Lua, FromLuaStack, NumResults, liftIO) +import Foreign.Lua (FromLuaStack, Lua, NumResults, liftIO) import Foreign.Lua.FunctionCalling (ToHaskellFunction) -import Text.Pandoc.Class (readDataFile, runIO, - runIOorExplode, setUserDataDir, CommonState(..), - putCommonState, fetchItem, setMediaBag) -import Text.Pandoc.Options (ReaderOptions(readerExtensions)) +import System.Exit (ExitCode (..)) +import Text.Pandoc.Class (CommonState (..), fetchItem, putCommonState, + readDataFile, runIO, runIOorExplode, setMediaBag, + setUserDataDir) import Text.Pandoc.Lua.StackInstances () -import Text.Pandoc.Readers (Reader (..), getReader) import Text.Pandoc.MIME (MimeType) +import Text.Pandoc.Options (ReaderOptions (readerExtensions)) import Text.Pandoc.Process (pipeProcess) -import System.Exit (ExitCode(..)) -import Data.Digest.Pure.SHA (sha1, showDigest) +import Text.Pandoc.Readers (Reader (..), getReader) -import qualified Foreign.Lua as Lua import qualified Data.ByteString.Lazy as BL +import qualified Foreign.Lua as Lua import qualified Text.Pandoc.MediaBag as MB -- | Push the "pandoc" on the lua stack. diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index 73b04e50f..0a7b61578 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -33,10 +33,11 @@ StackValue instances for pandoc types. module Text.Pandoc.Lua.StackInstances () where import Control.Applicative ((<|>)) -import Foreign.Lua (Lua, LuaInteger, LuaNumber, Type (..), FromLuaStack (peek), - ToLuaStack (push), StackIndex, throwLuaError, tryLua) +import Foreign.Lua (FromLuaStack (peek), Lua, LuaInteger, LuaNumber, StackIndex, + ToLuaStack (push), Type (..), throwLuaError, tryLua) import Text.Pandoc.Definition -import Text.Pandoc.Lua.Util (addValue, adjustIndexBy, getTable, pushViaConstructor) +import Text.Pandoc.Lua.Util (addValue, adjustIndexBy, getTable, + pushViaConstructor) import Text.Pandoc.Shared (safeRead) import qualified Foreign.Lua as Lua @@ -139,7 +140,7 @@ instance FromLuaStack Int where safeRead' :: Read a => String -> Lua a safeRead' s = case safeRead s of Nothing -> throwLuaError ("Could not read: " ++ s) - Just x -> return x + Just x -> return x -- | Push an meta value element to the top of the lua stack. pushMetaValue :: MetaValue -> Lua () @@ -293,7 +294,7 @@ getTag idx = do Lua.settop top case r of Left (Lua.LuaException err) -> throwLuaError err - Right res -> return res + Right res -> return res withAttr :: (Attr -> a -> b) -> (LuaAttr, a) -> b withAttr f (attributes, x) = f (fromLuaAttr attributes) x diff --git a/src/Text/Pandoc/Lua/Util.hs b/src/Text/Pandoc/Lua/Util.hs index 6b1d51159..7960c0670 100644 --- a/src/Text/Pandoc/Lua/Util.hs +++ b/src/Text/Pandoc/Lua/Util.hs @@ -40,8 +40,8 @@ module Text.Pandoc.Lua.Util , pushViaConstructor ) where -import Foreign.Lua (Lua, FromLuaStack (..), ToLuaStack (..), NumArgs, - StackIndex, getglobal') +import Foreign.Lua (FromLuaStack (..), Lua, NumArgs, StackIndex, + ToLuaStack (..), getglobal') import Foreign.Lua.Api (call, pop, rawget, rawgeti, rawset, rawseti) -- | Adjust the stack index, assuming that @n@ new elements have been pushed on diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index d004abca4..35c17c2ac 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -45,8 +45,8 @@ module Text.Pandoc.Options ( module Text.Pandoc.Extensions , def , isEnabled ) where -import Data.Aeson (ToJSON(..), FromJSON(..), - genericToEncoding, defaultOptions) +import Data.Aeson (FromJSON (..), ToJSON (..), defaultOptions, + genericToEncoding) import Data.Data (Data) import Data.Default import qualified Data.Set as Set diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index f90a4454f..028d63dcb 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -36,15 +36,15 @@ import qualified Codec.Picture as JP import qualified Control.Exception as E import Control.Monad (unless, when) import Control.Monad.Trans (MonadIO (..)) -import qualified Data.Text as T -import qualified Data.Text.IO as TextIO -import Data.Text (Text) import qualified Data.ByteString as BS import Data.ByteString.Lazy (ByteString) import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Lazy.Char8 as BC import Data.Maybe (fromMaybe) import Data.Monoid ((<>)) +import Data.Text (Text) +import qualified Data.Text as T +import qualified Data.Text.IO as TextIO import System.Directory import System.Environment import System.Exit (ExitCode (..)) @@ -57,7 +57,7 @@ import System.IO.Error (IOError, isDoesNotExistError) import System.IO.Error (isDoesNotExistError) #endif import Text.Pandoc.Definition -import Text.Pandoc.Error (PandocError(PandocPDFProgramNotFoundError)) +import Text.Pandoc.Error (PandocError (PandocPDFProgramNotFoundError)) import Text.Pandoc.MIME (getMimeType) import Text.Pandoc.Options (HTMLMathMethod (..), WriterOptions (..)) import Text.Pandoc.Process (pipeProcess) @@ -68,10 +68,9 @@ import Text.Pandoc.Writers.Shared (getField, metaToJSON) #ifdef _WINDOWS import Data.List (intercalate) #endif -import Text.Pandoc.Class (PandocIO, report, runIO, runIOorExplode, - setVerbosity, getVerbosity, - fillMediaBag, extractMedia, putCommonState, - getCommonState) +import Text.Pandoc.Class (PandocIO, extractMedia, fillMediaBag, getCommonState, + getVerbosity, putCommonState, report, runIO, + runIOorExplode, setVerbosity) import Text.Pandoc.Logging #ifdef _WINDOWS diff --git a/src/Text/Pandoc/Readers.hs b/src/Text/Pandoc/Readers.hs index 9fc9c3d18..d954256c8 100644 --- a/src/Text/Pandoc/Readers.hs +++ b/src/Text/Pandoc/Readers.hs @@ -70,7 +70,9 @@ module Text.Pandoc.Readers import Control.Monad.Except (throwError) import Data.Aeson +import qualified Data.ByteString.Lazy as BL import Data.List (intercalate) +import Data.Text (Text) import Text.Pandoc.Class import Text.Pandoc.Definition import Text.Pandoc.Error @@ -86,7 +88,6 @@ import Text.Pandoc.Readers.HTML import Text.Pandoc.Readers.LaTeX import Text.Pandoc.Readers.Markdown import Text.Pandoc.Readers.MediaWiki -import Text.Pandoc.Readers.Vimwiki import Text.Pandoc.Readers.Muse import Text.Pandoc.Readers.Native import Text.Pandoc.Readers.Odt @@ -94,14 +95,13 @@ import Text.Pandoc.Readers.OPML import Text.Pandoc.Readers.Org import Text.Pandoc.Readers.RST import Text.Pandoc.Readers.Textile -import Text.Pandoc.Readers.TWiki import Text.Pandoc.Readers.TikiWiki +import Text.Pandoc.Readers.TWiki import Text.Pandoc.Readers.Txt2Tags +import Text.Pandoc.Readers.Vimwiki import Text.Pandoc.Shared (mapLeft) -import Text.Parsec.Error import qualified Text.Pandoc.UTF8 as UTF8 -import qualified Data.ByteString.Lazy as BL -import Data.Text (Text) +import Text.Parsec.Error data Reader m = TextReader (ReaderOptions -> Text -> m Pandoc) | ByteStringReader (ReaderOptions -> BL.ByteString -> m Pandoc) diff --git a/src/Text/Pandoc/Readers/CommonMark.hs b/src/Text/Pandoc/Readers/CommonMark.hs index 62a7f4119..8189e7760 100644 --- a/src/Text/Pandoc/Readers/CommonMark.hs +++ b/src/Text/Pandoc/Readers/CommonMark.hs @@ -34,10 +34,10 @@ where import CMarkGFM import Control.Monad.State -import Data.Char (isLetter, isAlphaNum, isSpace, toLower) +import Data.Char (isAlphaNum, isLetter, isSpace, toLower) import Data.List (groupBy) -import Data.Text (Text, unpack) import qualified Data.Map as Map +import Data.Text (Text, unpack) import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Emoji (emojis) @@ -153,9 +153,9 @@ addBlock opts (Node _ (TABLE alignments) nodes) = do (h:rs) -> (h, rs) [] -> ([], []) isRow (Node _ TABLE_ROW _) = True - isRow _ = False + isRow _ = False isCell (Node _ TABLE_CELL _) = True - isCell _ = False + isCell _ = False toRow (Node _ TABLE_ROW ns) = map toCell $ filter isCell ns toRow (Node _ t _) = error $ "toRow encountered non-row " ++ show t toCell (Node _ TABLE_CELL []) = [] @@ -170,30 +170,30 @@ addBlock _ _ = id isBlockNode :: Node -> Bool isBlockNode (Node _ nodetype _) = case nodetype of - DOCUMENT -> True - THEMATIC_BREAK -> True - PARAGRAPH -> True - BLOCK_QUOTE -> True - HTML_BLOCK _ -> True - CUSTOM_BLOCK _ _ -> True - CODE_BLOCK _ _ -> True - HEADING _ -> True - LIST _ -> True - ITEM -> True - TEXT _ -> False - SOFTBREAK -> False - LINEBREAK -> False - HTML_INLINE _ -> False + DOCUMENT -> True + THEMATIC_BREAK -> True + PARAGRAPH -> True + BLOCK_QUOTE -> True + HTML_BLOCK _ -> True + CUSTOM_BLOCK _ _ -> True + CODE_BLOCK _ _ -> True + HEADING _ -> True + LIST _ -> True + ITEM -> True + TEXT _ -> False + SOFTBREAK -> False + LINEBREAK -> False + HTML_INLINE _ -> False CUSTOM_INLINE _ _ -> False - CODE _ -> False - EMPH -> False - STRONG -> False - LINK _ _ -> False - IMAGE _ _ -> False - STRIKETHROUGH -> False - TABLE _ -> False - TABLE_ROW -> False - TABLE_CELL -> False + CODE _ -> False + EMPH -> False + STRONG -> False + LINK _ _ -> False + IMAGE _ _ -> False + STRIKETHROUGH -> False + TABLE _ -> False + TABLE_ROW -> False + TABLE_CELL -> False children :: Node -> [Node] children (Node _ _ ns) = ns diff --git a/src/Text/Pandoc/Readers/Creole.hs b/src/Text/Pandoc/Readers/Creole.hs index ab90772ef..9886394a7 100644 --- a/src/Text/Pandoc/Readers/Creole.hs +++ b/src/Text/Pandoc/Readers/Creole.hs @@ -35,18 +35,18 @@ Conversion of creole text to 'Pandoc' document. module Text.Pandoc.Readers.Creole ( readCreole ) where -import Control.Monad.Except (liftM2, throwError, guard) +import Control.Monad.Except (guard, liftM2, throwError) import qualified Data.Foldable as F +import Data.Maybe (fromMaybe) +import Data.Monoid +import Data.Text (Text) +import qualified Data.Text as T import qualified Text.Pandoc.Builder as B -import Text.Pandoc.Class (PandocMonad(..)) +import Text.Pandoc.Class (PandocMonad (..)) import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (enclosed) import Text.Pandoc.Shared (crFilter) -import Data.Maybe (fromMaybe) -import Data.Monoid -import Data.Text (Text) -import qualified Data.Text as T -- | Read creole from an input string and return a Pandoc document. diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index 2448d24e5..465c3abec 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -472,10 +472,10 @@ rowToBlocksList (Row cells) = do -- like trimInlines, but also take out linebreaks trimSps :: Inlines -> Inlines trimSps (Many ils) = Many $ Seq.dropWhileL isSp $ Seq.dropWhileR isSp $ ils - where isSp Space = True + where isSp Space = True isSp SoftBreak = True isSp LineBreak = True - isSp _ = False + isSp _ = False parStyleToTransform :: ParagraphStyle -> (Blocks -> Blocks) parStyleToTransform pPr diff --git a/src/Text/Pandoc/Readers/EPUB.hs b/src/Text/Pandoc/Readers/EPUB.hs index c0d8029dc..d38a40c8d 100644 --- a/src/Text/Pandoc/Readers/EPUB.hs +++ b/src/Text/Pandoc/Readers/EPUB.hs @@ -13,12 +13,12 @@ import Control.DeepSeq (NFData, deepseq) import Control.Monad (guard, liftM) import Control.Monad.Except (throwError) import qualified Data.ByteString.Lazy as BL (ByteString) -import qualified Data.Text.Lazy.Encoding as TL -import qualified Data.Text.Lazy as TL import Data.List (isInfixOf, isPrefixOf) import qualified Data.Map as M (Map, elems, fromList, lookup) import Data.Maybe (fromMaybe, mapMaybe) import Data.Monoid ((<>)) +import qualified Data.Text.Lazy as TL +import qualified Data.Text.Lazy.Encoding as TL import Network.URI (unEscapeString) import System.FilePath (dropFileName, dropFileName, normalise, splitFileName, takeFileName, (</>)) diff --git a/src/Text/Pandoc/Readers/Haddock.hs b/src/Text/Pandoc/Readers/Haddock.hs index a09ed8be9..9d0610e01 100644 --- a/src/Text/Pandoc/Readers/Haddock.hs +++ b/src/Text/Pandoc/Readers/Haddock.hs @@ -16,9 +16,9 @@ module Text.Pandoc.Readers.Haddock import Control.Monad.Except (throwError) import Data.List (intersperse, stripPrefix) -import Data.Text (Text, unpack) import Data.Maybe (fromMaybe) import Data.Monoid ((<>)) +import Data.Text (Text, unpack) import Documentation.Haddock.Parser import Documentation.Haddock.Types import Text.Pandoc.Builder (Blocks, Inlines) @@ -27,7 +27,7 @@ import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Error import Text.Pandoc.Options -import Text.Pandoc.Shared (splitBy, trim, crFilter) +import Text.Pandoc.Shared (crFilter, splitBy, trim) -- | Parse Haddock markup and return a 'Pandoc' document. diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index a37c152d3..665ed6548 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1,8 +1,8 @@ -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE PatternGuards #-} -{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE PatternGuards #-} +{-# LANGUAGE ScopedTypeVariables #-} {- Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> @@ -44,35 +44,33 @@ import Control.Applicative (many, optional, (<|>)) import Control.Monad import Control.Monad.Except (throwError) import Control.Monad.Trans (lift) -import Data.Char (chr, isAlphaNum, isLetter, ord, isDigit, toLower) +import Data.Char (chr, isAlphaNum, isDigit, isLetter, ord, toLower) import Data.Default -import Data.Text (Text) -import qualified Data.Text as T import Data.List (intercalate, isPrefixOf) import qualified Data.Map as M -import qualified Data.Set as Set import Data.Maybe (fromMaybe, maybeToList) +import qualified Data.Set as Set +import Data.Text (Text) +import qualified Data.Text as T import Safe (minimumDef) import System.FilePath (addExtension, replaceExtension, takeExtension) +import Text.Pandoc.BCP47 (Lang (..), renderLang) import Text.Pandoc.Builder -import Text.Pandoc.Class (PandocMonad, PandocPure, lookupEnv, +import Text.Pandoc.Class (PandocMonad, PandocPure, getResourcePath, lookupEnv, readFileFromDirs, report, setResourcePath, - getResourcePath, setTranslations, translateTerm) -import qualified Text.Pandoc.Translations as Translations -import Text.Pandoc.BCP47 (Lang(..), renderLang) + setTranslations, translateTerm) +import Text.Pandoc.Error (PandocError (PandocMacroLoop, PandocParseError, PandocParsecError)) import Text.Pandoc.Highlighting (fromListingsLanguage, languagesByExtension) import Text.Pandoc.ImageSize (numUnit, showFl) import Text.Pandoc.Logging import Text.Pandoc.Options -import Text.Pandoc.Parsing hiding (many, optional, withRaw, - mathInline, mathDisplay, - space, (<|>), spaces, blankline) +import Text.Pandoc.Parsing hiding (blankline, many, mathDisplay, mathInline, + optional, space, spaces, withRaw, (<|>)) +import Text.Pandoc.Readers.LaTeX.Types (ExpansionPoint (..), Macro (..), + Tok (..), TokType (..)) import Text.Pandoc.Shared -import Text.Pandoc.Readers.LaTeX.Types (Macro(..), ExpansionPoint(..), Tok(..), - TokType(..)) +import qualified Text.Pandoc.Translations as Translations import Text.Pandoc.Walk -import Text.Pandoc.Error - (PandocError(PandocParsecError, PandocParseError, PandocMacroLoop)) import Text.Parsec.Pos -- for debugging: @@ -100,10 +98,10 @@ parseLaTeX = do let meta = sMeta st let doc' = doc bs let headerLevel (Header n _ _) = [n] - headerLevel _ = [] + headerLevel _ = [] let bottomLevel = minimumDef 1 $ query headerLevel doc' let adjustHeaders m (Header n attr ils) = Header (n+m) attr ils - adjustHeaders _ x = x + adjustHeaders _ x = x let (Pandoc _ bs') = -- handle the case where you have \part or \chapter (if bottomLevel < 1 @@ -261,7 +259,7 @@ applyMacros s = (guardDisabled Ext_latex_macros >> return s) <|> , sMacros = extractMacros pstate } res <- runParserT retokenize lstate "math" (tokenize "math" (T.pack s)) case res of - Left e -> fail (show e) + Left e -> fail (show e) Right s' -> return s' rawLaTeXBlock :: (PandocMonad m, HasMacros s, HasReaderOptions s) @@ -378,8 +376,8 @@ isSpaceOrTab '\t' = True isSpaceOrTab _ = False isLetterOrAt :: Char -> Bool -isLetterOrAt '@' = True -isLetterOrAt c = isLetter c +isLetterOrAt '@' = True +isLetterOrAt c = isLetter c isLowerHex :: Char -> Bool isLowerHex x = x >= '0' && x <= '9' || x >= 'a' && x <= 'f' @@ -400,7 +398,7 @@ satisfyTok f = | otherwise = Nothing updatePos :: SourcePos -> Tok -> [Tok] -> SourcePos updatePos _spos _ (Tok pos _ _ : _) = pos - updatePos spos _ [] = spos + updatePos spos _ [] = spos doMacros :: PandocMonad m => Int -> LP m () doMacros n = do @@ -477,20 +475,20 @@ tokTypeIn toktypes (Tok _ tt _) = tt `elem` toktypes controlSeq :: PandocMonad m => Text -> LP m Tok controlSeq name = satisfyTok isNamed where isNamed (Tok _ (CtrlSeq n) _) = n == name - isNamed _ = False + isNamed _ = False symbol :: PandocMonad m => Char -> LP m Tok symbol c = satisfyTok isc where isc (Tok _ Symbol d) = case T.uncons d of Just (c',_) -> c == c' - _ -> False + _ -> False isc _ = False symbolIn :: PandocMonad m => [Char] -> LP m Tok symbolIn cs = satisfyTok isInCs where isInCs (Tok _ Symbol d) = case T.uncons d of Just (c,_) -> c `elem` cs - _ -> False + _ -> False isInCs _ = False sp :: PandocMonad m => LP m () @@ -499,19 +497,19 @@ sp = whitespace <|> endline whitespace :: PandocMonad m => LP m () whitespace = () <$ satisfyTok isSpaceTok where isSpaceTok (Tok _ Spaces _) = True - isSpaceTok _ = False + isSpaceTok _ = False newlineTok :: PandocMonad m => LP m () newlineTok = () <$ satisfyTok isNewlineTok isNewlineTok :: Tok -> Bool isNewlineTok (Tok _ Newline _) = True -isNewlineTok _ = False +isNewlineTok _ = False comment :: PandocMonad m => LP m () comment = () <$ satisfyTok isCommentTok where isCommentTok (Tok _ Comment _) = True - isCommentTok _ = False + isCommentTok _ = False anyTok :: PandocMonad m => LP m Tok anyTok = satisfyTok (const True) @@ -535,7 +533,7 @@ primEscape = do | otherwise -> return (chr (ord c + 64)) Nothing -> fail "Empty content of Esc1" Esc2 -> case safeRead ('0':'x':T.unpack (T.drop 2 t)) of - Just x -> return (chr x) + Just x -> return (chr x) Nothing -> fail $ "Could not read: " ++ T.unpack t _ -> fail "Expected an Esc1 or Esc2 token" -- should not happen @@ -594,7 +592,7 @@ word = (str . T.unpack . untoken) <$> satisfyTok isWordTok regularSymbol :: PandocMonad m => LP m Inlines regularSymbol = (str . T.unpack . untoken) <$> satisfyTok isRegularSymbol where isRegularSymbol (Tok _ Symbol t) = not $ T.any isSpecial t - isRegularSymbol _ = False + isRegularSymbol _ = False isSpecial c = c `Set.member` specialChars specialChars :: Set.Set Char @@ -602,7 +600,7 @@ specialChars = Set.fromList "#$%&~_^\\{}" isWordTok :: Tok -> Bool isWordTok (Tok _ Word _) = True -isWordTok _ = False +isWordTok _ = False inlineGroup :: PandocMonad m => LP m Inlines inlineGroup = do @@ -623,7 +621,7 @@ mkImage options src = do let replaceTextwidth (k,v) = case numUnit v of Just (num, "\\textwidth") -> (k, showFl (num * 100) ++ "%") - _ -> (k, v) + _ -> (k, v) let kvs = map replaceTextwidth $ filter (\(k,_) -> k `elem` ["width", "height"]) options let attr = ("",[], kvs) @@ -640,7 +638,7 @@ doxspace = do where startsWithLetter (Tok _ Word t) = case T.uncons t of Just (c, _) | isLetter c -> True - _ -> False + _ -> False startsWithLetter _ = False @@ -686,7 +684,7 @@ singleQuote = do where startsWithLetter (Tok _ Word t) = case T.uncons t of Just (c, _) | isLetter c -> True - _ -> False + _ -> False startsWithLetter _ = False quoted' :: PandocMonad m @@ -736,7 +734,7 @@ doverb = do Tok _ Symbol t <- anySymbol marker <- case T.uncons t of Just (c, ts) | T.null ts -> return c - _ -> mzero + _ -> mzero withVerbatimMode $ (code . T.unpack . untokenize) <$> manyTill (verbTok marker) (symbol marker) @@ -760,7 +758,7 @@ dolstinline = do Tok _ Symbol t <- anySymbol marker <- case T.uncons t of Just (c, ts) | T.null ts -> return c - _ -> mzero + _ -> mzero let stopchar = if marker == '{' then '}' else marker withVerbatimMode $ (codeWith ("",classes,[]) . T.unpack . untokenize) <$> @@ -770,7 +768,7 @@ keyval :: PandocMonad m => LP m (String, String) keyval = try $ do Tok _ Word key <- satisfyTok isWordTok let isSpecSym (Tok _ Symbol t) = t /= "]" && t /= "," - isSpecSym _ = False + isSpecSym _ = False optional sp val <- option [] $ do symbol '=' @@ -1504,7 +1502,7 @@ hypertargetBlock = try $ do bs <- grouped block case toList bs of [Header 1 (ident,_,_) _] | ident == ref -> return bs - _ -> return $ divWith (ref, [], []) bs + _ -> return $ divWith (ref, [], []) bs hypertargetInline :: PandocMonad m => LP m Inlines hypertargetInline = try $ do @@ -1846,7 +1844,7 @@ argSeq n = do isArgTok :: Tok -> Bool isArgTok (Tok _ (Arg _) _) = True -isArgTok _ = False +isArgTok _ = False newcommand :: PandocMonad m => LP m (Text, Macro) newcommand = do @@ -1869,7 +1867,7 @@ newcommand = do when (mtype == "newcommand") $ do macros <- sMacros <$> getState case M.lookup name macros of - Just _ -> report $ MacroAlreadyDefined (T.unpack txt) pos + Just _ -> report $ MacroAlreadyDefined (T.unpack txt) pos Nothing -> return () return (name, Macro ExpandWhenUsed numargs optarg contents) @@ -1893,7 +1891,7 @@ newenvironment = do when (mtype == "newenvironment") $ do macros <- sMacros <$> getState case M.lookup name macros of - Just _ -> report $ MacroAlreadyDefined (T.unpack name) pos + Just _ -> report $ MacroAlreadyDefined (T.unpack name) pos Nothing -> return () return (name, Macro ExpandWhenUsed numargs optarg startcontents, Macro ExpandWhenUsed 0 Nothing endcontents) @@ -2186,8 +2184,8 @@ obeylines = do softBreakToHard x = x removeLeadingTrailingBreaks = reverse . dropWhile isLineBreak . reverse . dropWhile isLineBreak - isLineBreak LineBreak = True - isLineBreak _ = False + isLineBreak LineBreak = True + isLineBreak _ = False minted :: PandocMonad m => LP m Blocks minted = do @@ -2521,13 +2519,13 @@ setDefaultLanguage = do polyglossiaLangToBCP47 :: M.Map String (String -> Lang) polyglossiaLangToBCP47 = M.fromList [ ("arabic", \o -> case filter (/=' ') o of - "locale=algeria" -> Lang "ar" "" "DZ" [] - "locale=mashriq" -> Lang "ar" "" "SY" [] - "locale=libya" -> Lang "ar" "" "LY" [] - "locale=morocco" -> Lang "ar" "" "MA" [] + "locale=algeria" -> Lang "ar" "" "DZ" [] + "locale=mashriq" -> Lang "ar" "" "SY" [] + "locale=libya" -> Lang "ar" "" "LY" [] + "locale=morocco" -> Lang "ar" "" "MA" [] "locale=mauritania" -> Lang "ar" "" "MR" [] - "locale=tunisia" -> Lang "ar" "" "TN" [] - _ -> Lang "ar" "" "" []) + "locale=tunisia" -> Lang "ar" "" "TN" [] + _ -> Lang "ar" "" "" []) , ("german", \o -> case filter (/=' ') o of "spelling=old" -> Lang "de" "" "DE" ["1901"] "variant=austrian,spelling=old" @@ -2539,20 +2537,20 @@ polyglossiaLangToBCP47 = M.fromList _ -> Lang "de" "" "" []) , ("lsorbian", \_ -> Lang "dsb" "" "" []) , ("greek", \o -> case filter (/=' ') o of - "variant=poly" -> Lang "el" "" "polyton" [] + "variant=poly" -> Lang "el" "" "polyton" [] "variant=ancient" -> Lang "grc" "" "" [] - _ -> Lang "el" "" "" []) + _ -> Lang "el" "" "" []) , ("english", \o -> case filter (/=' ') o of "variant=australian" -> Lang "en" "" "AU" [] - "variant=canadian" -> Lang "en" "" "CA" [] - "variant=british" -> Lang "en" "" "GB" [] + "variant=canadian" -> Lang "en" "" "CA" [] + "variant=british" -> Lang "en" "" "GB" [] "variant=newzealand" -> Lang "en" "" "NZ" [] - "variant=american" -> Lang "en" "" "US" [] - _ -> Lang "en" "" "" []) + "variant=american" -> Lang "en" "" "US" [] + _ -> Lang "en" "" "" []) , ("usorbian", \_ -> Lang "hsb" "" "" []) , ("latin", \o -> case filter (/=' ') o of "variant=classic" -> Lang "la" "" "" ["x-classic"] - _ -> Lang "la" "" "" []) + _ -> Lang "la" "" "" []) , ("slovenian", \_ -> Lang "sl" "" "" []) , ("serbianc", \_ -> Lang "sr" "cyrl" "" []) , ("pinyin", \_ -> Lang "zh" "Latn" "" ["pinyin"]) diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index a27e05fed..48719a678 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -52,7 +52,7 @@ import System.FilePath (addExtension, takeExtension) import Text.HTML.TagSoup import Text.Pandoc.Builder (Blocks, Inlines) import qualified Text.Pandoc.Builder as B -import Text.Pandoc.Class (PandocMonad(..), report) +import Text.Pandoc.Class (PandocMonad (..), report) import Text.Pandoc.Definition import Text.Pandoc.Emoji (emojis) import Text.Pandoc.Error @@ -61,8 +61,7 @@ import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (tableWith) import Text.Pandoc.Readers.HTML (htmlInBalanced, htmlTag, isBlockTag, isCommentTag, isInlineTag, isTextTag) -import Text.Pandoc.Readers.LaTeX (rawLaTeXBlock, - rawLaTeXInline, applyMacros) +import Text.Pandoc.Readers.LaTeX (applyMacros, rawLaTeXBlock, rawLaTeXInline) import Text.Pandoc.Shared import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.XML (fromEntities) @@ -1506,8 +1505,8 @@ escapedChar :: PandocMonad m => MarkdownParser m (F Inlines) escapedChar = do result <- escapedChar' case result of - ' ' -> return $ return $ B.str "\160" -- "\ " is a nonbreaking space - _ -> return $ return $ B.str [result] + ' ' -> return $ return $ B.str "\160" -- "\ " is a nonbreaking space + _ -> return $ return $ B.str [result] ltSign :: PandocMonad m => MarkdownParser m (F Inlines) ltSign = do diff --git a/src/Text/Pandoc/Readers/MediaWiki.hs b/src/Text/Pandoc/Readers/MediaWiki.hs index a7f073d50..01a6c74b6 100644 --- a/src/Text/Pandoc/Readers/MediaWiki.hs +++ b/src/Text/Pandoc/Readers/MediaWiki.hs @@ -41,7 +41,6 @@ module Text.Pandoc.Readers.MediaWiki ( readMediaWiki ) where import Control.Monad import Control.Monad.Except (throwError) import Data.Char (isDigit, isSpace) -import Data.Text (Text, unpack) import qualified Data.Foldable as F import Data.List (intercalate, intersperse, isPrefixOf) import qualified Data.Map as M @@ -49,17 +48,18 @@ import Data.Maybe (fromMaybe) import Data.Monoid ((<>)) import Data.Sequence (ViewL (..), viewl, (<|)) import qualified Data.Set as Set +import Data.Text (Text, unpack) import Text.HTML.TagSoup import Text.Pandoc.Builder (Blocks, Inlines, trimInlines) import qualified Text.Pandoc.Builder as B -import Text.Pandoc.Class (PandocMonad(..)) +import Text.Pandoc.Class (PandocMonad (..)) import Text.Pandoc.Definition import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (nested) import Text.Pandoc.Readers.HTML (htmlTag, isBlockTag, isCommentTag) -import Text.Pandoc.Shared (safeRead, stringify, stripTrailingNewlines, trim, - crFilter) +import Text.Pandoc.Shared (crFilter, safeRead, stringify, stripTrailingNewlines, + trim) import Text.Pandoc.Walk (walk) import Text.Pandoc.XML (fromEntities) diff --git a/src/Text/Pandoc/Readers/Native.hs b/src/Text/Pandoc/Readers/Native.hs index abc2ed38a..d065bff8d 100644 --- a/src/Text/Pandoc/Readers/Native.hs +++ b/src/Text/Pandoc/Readers/Native.hs @@ -35,9 +35,9 @@ import Text.Pandoc.Options (ReaderOptions) import Text.Pandoc.Shared (safeRead) import Control.Monad.Except (throwError) +import Data.Text (Text, unpack) import Text.Pandoc.Class import Text.Pandoc.Error -import Data.Text (Text, unpack) -- | Read native formatted text and return a Pandoc document. -- The input may be a full pandoc document, a block list, a block, diff --git a/src/Text/Pandoc/Readers/OPML.hs b/src/Text/Pandoc/Readers/OPML.hs index c25ace800..167ad6d4e 100644 --- a/src/Text/Pandoc/Readers/OPML.hs +++ b/src/Text/Pandoc/Readers/OPML.hs @@ -2,16 +2,16 @@ module Text.Pandoc.Readers.OPML ( readOPML ) where import Control.Monad.State.Strict import Data.Char (toUpper) -import Data.Text (Text, unpack, pack) import Data.Default import Data.Generics +import Data.Text (Text, pack, unpack) import Text.HTML.TagSoup.Entity (lookupEntity) import Text.Pandoc.Builder import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Options -import Text.Pandoc.Shared (crFilter) import Text.Pandoc.Readers.HTML (readHtml) import Text.Pandoc.Readers.Markdown (readMarkdown) +import Text.Pandoc.Shared (crFilter) import Text.XML.Light type OPML m = StateT OPMLState m diff --git a/src/Text/Pandoc/Readers/Odt.hs b/src/Text/Pandoc/Readers/Odt.hs index ac22f2c09..875c18a85 100644 --- a/src/Text/Pandoc/Readers/Odt.hs +++ b/src/Text/Pandoc/Readers/Odt.hs @@ -32,29 +32,29 @@ Entry point to the odt reader. module Text.Pandoc.Readers.Odt ( readOdt ) where -import Codec.Archive.Zip -import qualified Text.XML.Light as XML +import Codec.Archive.Zip +import qualified Text.XML.Light as XML -import qualified Data.ByteString.Lazy as B +import qualified Data.ByteString.Lazy as B -import System.FilePath +import System.FilePath -import Control.Monad.Except (throwError) +import Control.Monad.Except (throwError) -import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Class (PandocMonad) import qualified Text.Pandoc.Class as P -import Text.Pandoc.Definition -import Text.Pandoc.Error -import Text.Pandoc.Options -import Text.Pandoc.MediaBag -import qualified Text.Pandoc.UTF8 as UTF8 - -import Text.Pandoc.Readers.Odt.ContentReader -import Text.Pandoc.Readers.Odt.StyleReader - -import Text.Pandoc.Readers.Odt.Generic.XMLConverter -import Text.Pandoc.Readers.Odt.Generic.Fallible -import Text.Pandoc.Shared (filteredFilesFromArchive) +import Text.Pandoc.Definition +import Text.Pandoc.Error +import Text.Pandoc.MediaBag +import Text.Pandoc.Options +import qualified Text.Pandoc.UTF8 as UTF8 + +import Text.Pandoc.Readers.Odt.ContentReader +import Text.Pandoc.Readers.Odt.StyleReader + +import Text.Pandoc.Readers.Odt.Generic.Fallible +import Text.Pandoc.Readers.Odt.Generic.XMLConverter +import Text.Pandoc.Shared (filteredFilesFromArchive) readOdt :: PandocMonad m => ReaderOptions diff --git a/src/Text/Pandoc/Readers/Odt/Arrows/State.hs b/src/Text/Pandoc/Readers/Odt/Arrows/State.hs index 3d716ba19..0f7483431 100644 --- a/src/Text/Pandoc/Readers/Odt/Arrows/State.hs +++ b/src/Text/Pandoc/Readers/Odt/Arrows/State.hs @@ -1,6 +1,6 @@ {-# LANGUAGE Arrows #-} -{-# LANGUAGE TupleSections #-} {-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE TupleSections #-} {- Copyright (C) 2015 Martin Linnemann <theCodingMarlin@googlemail.com> @@ -38,17 +38,17 @@ faster and easier to implement this way. module Text.Pandoc.Readers.Odt.Arrows.State where -import Prelude hiding ( foldr, foldl ) +import Prelude hiding (foldl, foldr) -import qualified Control.Category as Cat -import Control.Arrow -import Control.Monad +import Control.Arrow +import qualified Control.Category as Cat +import Control.Monad -import Data.Foldable -import Data.Monoid +import Data.Foldable +import Data.Monoid -import Text.Pandoc.Readers.Odt.Arrows.Utils -import Text.Pandoc.Readers.Odt.Generic.Fallible +import Text.Pandoc.Readers.Odt.Arrows.Utils +import Text.Pandoc.Readers.Odt.Generic.Fallible newtype ArrowState state a b = ArrowState diff --git a/src/Text/Pandoc/Readers/Odt/Arrows/Utils.hs b/src/Text/Pandoc/Readers/Odt/Arrows/Utils.hs index ecef8b6e3..cdfa8f8df 100644 --- a/src/Text/Pandoc/Readers/Odt/Arrows/Utils.hs +++ b/src/Text/Pandoc/Readers/Odt/Arrows/Utils.hs @@ -39,11 +39,11 @@ with an equivalent return value. -- We export everything module Text.Pandoc.Readers.Odt.Arrows.Utils where -import Control.Arrow -import Control.Monad ( join ) +import Control.Arrow +import Control.Monad (join) -import Text.Pandoc.Readers.Odt.Generic.Fallible -import Text.Pandoc.Readers.Odt.Generic.Utils +import Text.Pandoc.Readers.Odt.Generic.Fallible +import Text.Pandoc.Readers.Odt.Generic.Utils and2 :: (Arrow a) => a b c -> a b c' -> a b (c,c') and2 = (&&&) diff --git a/src/Text/Pandoc/Readers/Odt/Base.hs b/src/Text/Pandoc/Readers/Odt/Base.hs index 1f095bade..f8a0b86e7 100644 --- a/src/Text/Pandoc/Readers/Odt/Base.hs +++ b/src/Text/Pandoc/Readers/Odt/Base.hs @@ -32,8 +32,8 @@ Core types of the odt reader. module Text.Pandoc.Readers.Odt.Base where -import Text.Pandoc.Readers.Odt.Generic.XMLConverter -import Text.Pandoc.Readers.Odt.Namespaces +import Text.Pandoc.Readers.Odt.Generic.XMLConverter +import Text.Pandoc.Readers.Odt.Namespaces type OdtConverterState s = XMLConverterState Namespace s diff --git a/src/Text/Pandoc/Readers/Odt/ContentReader.hs b/src/Text/Pandoc/Readers/Odt/ContentReader.hs index 777c10df5..69eececc8 100644 --- a/src/Text/Pandoc/Readers/Odt/ContentReader.hs +++ b/src/Text/Pandoc/Readers/Odt/ContentReader.hs @@ -1,8 +1,8 @@ {-# LANGUAGE Arrows #-} -{-# LANGUAGE TupleSections #-} {-# LANGUAGE PatternGuards #-} -{-# LANGUAGE ViewPatterns #-} {-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE TupleSections #-} +{-# LANGUAGE ViewPatterns #-} {- Copyright (C) 2015 Martin Linnemann <theCodingMarlin@googlemail.com> @@ -39,29 +39,29 @@ module Text.Pandoc.Readers.Odt.ContentReader , read_body ) where -import Control.Arrow -import Control.Applicative hiding ( liftA, liftA2, liftA3 ) +import Control.Applicative hiding (liftA, liftA2, liftA3) +import Control.Arrow -import qualified Data.ByteString.Lazy as B -import qualified Data.Map as M -import Data.List ( find, intercalate ) -import Data.Maybe +import qualified Data.ByteString.Lazy as B +import Data.List (find, intercalate) +import qualified Data.Map as M +import Data.Maybe -import qualified Text.XML.Light as XML +import qualified Text.XML.Light as XML -import Text.Pandoc.Definition -import Text.Pandoc.Builder -import Text.Pandoc.MediaBag (insertMedia, MediaBag) -import Text.Pandoc.Shared +import Text.Pandoc.Definition +import Text.Pandoc.Builder +import Text.Pandoc.MediaBag (MediaBag, insertMedia) +import Text.Pandoc.Shared -import Text.Pandoc.Readers.Odt.Base -import Text.Pandoc.Readers.Odt.Namespaces -import Text.Pandoc.Readers.Odt.StyleReader +import Text.Pandoc.Readers.Odt.Base +import Text.Pandoc.Readers.Odt.Namespaces +import Text.Pandoc.Readers.Odt.StyleReader -import Text.Pandoc.Readers.Odt.Arrows.Utils -import Text.Pandoc.Readers.Odt.Generic.XMLConverter -import Text.Pandoc.Readers.Odt.Generic.Fallible -import Text.Pandoc.Readers.Odt.Generic.Utils +import Text.Pandoc.Readers.Odt.Arrows.Utils +import Text.Pandoc.Readers.Odt.Generic.Fallible +import Text.Pandoc.Readers.Odt.Generic.Utils +import Text.Pandoc.Readers.Odt.Generic.XMLConverter import qualified Data.Set as Set @@ -342,9 +342,9 @@ modifierFromStyleDiff propertyTriple = Just oldVPos -> getVPosModifier' (oldVPos, verticalPosition textProps) getVPosModifier' (oldVPos , newVPos ) | oldVPos == newVPos = ignore - getVPosModifier' ( _ , VPosSub ) = subscript - getVPosModifier' ( _ , VPosSuper ) = superscript - getVPosModifier' ( _ , _ ) = ignore + getVPosModifier' ( _ , VPosSub ) = subscript + getVPosModifier' ( _ , VPosSuper ) = superscript + getVPosModifier' ( _ , _ ) = ignore hasEmphChanged :: PropertyTriple -> Bool hasEmphChanged = swing any [ hasChanged isEmphasised diff --git a/src/Text/Pandoc/Readers/Odt/Generic/Fallible.hs b/src/Text/Pandoc/Readers/Odt/Generic/Fallible.hs index 8c47cdaf5..72509e591 100644 --- a/src/Text/Pandoc/Readers/Odt/Generic/Fallible.hs +++ b/src/Text/Pandoc/Readers/Odt/Generic/Fallible.hs @@ -39,7 +39,7 @@ compatible instances of "ArrowChoice". -- We export everything module Text.Pandoc.Readers.Odt.Generic.Fallible where -import Data.Monoid ((<>)) +import Data.Monoid ((<>)) -- | Default for now. Will probably become a class at some point. type Failure = () diff --git a/src/Text/Pandoc/Readers/Odt/Generic/Utils.hs b/src/Text/Pandoc/Readers/Odt/Generic/Utils.hs index 4af4242b6..f492ec944 100644 --- a/src/Text/Pandoc/Readers/Odt/Generic/Utils.hs +++ b/src/Text/Pandoc/Readers/Odt/Generic/Utils.hs @@ -1,6 +1,6 @@ -{-# LANGUAGE TypeOperators #-} -{-# LANGUAGE TupleSections #-} {-# LANGUAGE PatternGuards #-} +{-# LANGUAGE TupleSections #-} +{-# LANGUAGE TypeOperators #-} {-# LANGUAGE ViewPatterns #-} {- @@ -51,12 +51,12 @@ module Text.Pandoc.Readers.Odt.Generic.Utils , composition ) where -import Control.Category ( Category, (>>>), (<<<) ) -import qualified Control.Category as Cat ( id ) -import Control.Monad ( msum ) +import Control.Category (Category, (<<<), (>>>)) +import qualified Control.Category as Cat (id) +import Control.Monad (msum) -import qualified Data.Foldable as F ( Foldable, foldr ) -import Data.Maybe +import qualified Data.Foldable as F (Foldable, foldr) +import Data.Maybe -- | Aequivalent to diff --git a/src/Text/Pandoc/Readers/Odt/Namespaces.hs b/src/Text/Pandoc/Readers/Odt/Namespaces.hs index deb009998..3c11aeb8e 100644 --- a/src/Text/Pandoc/Readers/Odt/Namespaces.hs +++ b/src/Text/Pandoc/Readers/Odt/Namespaces.hs @@ -31,11 +31,11 @@ Namespaces used in odt files. module Text.Pandoc.Readers.Odt.Namespaces ( Namespace (..) ) where -import Data.List ( isPrefixOf ) -import Data.Maybe ( fromMaybe, listToMaybe ) -import qualified Data.Map as M ( empty, insert ) +import Data.List (isPrefixOf) +import qualified Data.Map as M (empty, insert) +import Data.Maybe (fromMaybe, listToMaybe) -import Text.Pandoc.Readers.Odt.Generic.Namespaces +import Text.Pandoc.Readers.Odt.Generic.Namespaces instance NameSpaceID Namespace where diff --git a/src/Text/Pandoc/Readers/Odt/StyleReader.hs b/src/Text/Pandoc/Readers/Odt/StyleReader.hs index 87a6dc91c..abb131983 100644 --- a/src/Text/Pandoc/Readers/Odt/StyleReader.hs +++ b/src/Text/Pandoc/Readers/Odt/StyleReader.hs @@ -1,8 +1,7 @@ -{-# LANGUAGE TupleSections #-} +{-# LANGUAGE Arrows #-} {-# LANGUAGE PatternGuards #-} -{-# LANGUAGE ViewPatterns #-} {-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE Arrows #-} +{-# LANGUAGE TupleSections #-} {- Copyright (C) 2015 Martin Linnemann <theCodingMarlin@googlemail.com> @@ -58,28 +57,28 @@ module Text.Pandoc.Readers.Odt.StyleReader , readStylesAt ) where -import Control.Arrow -import Control.Applicative hiding ( liftA, liftA2, liftA3 ) +import Control.Applicative hiding (liftA, liftA2, liftA3) +import Control.Arrow -import qualified Data.Foldable as F -import qualified Data.Map as M -import qualified Data.Set as S -import Data.Char ( isDigit ) -import Data.Default -import Data.List ( unfoldr ) -import Data.Maybe +import Data.Char (isDigit) +import Data.Default +import qualified Data.Foldable as F +import Data.List (unfoldr) +import qualified Data.Map as M +import Data.Maybe +import qualified Data.Set as S -import qualified Text.XML.Light as XML +import qualified Text.XML.Light as XML -import Text.Pandoc.Readers.Odt.Arrows.Utils +import Text.Pandoc.Readers.Odt.Arrows.Utils -import Text.Pandoc.Readers.Odt.Generic.Utils -import qualified Text.Pandoc.Readers.Odt.Generic.SetMap as SM -import Text.Pandoc.Readers.Odt.Generic.Fallible -import Text.Pandoc.Readers.Odt.Generic.XMLConverter +import Text.Pandoc.Readers.Odt.Generic.Fallible +import qualified Text.Pandoc.Readers.Odt.Generic.SetMap as SM +import Text.Pandoc.Readers.Odt.Generic.Utils +import Text.Pandoc.Readers.Odt.Generic.XMLConverter -import Text.Pandoc.Readers.Odt.Namespaces -import Text.Pandoc.Readers.Odt.Base +import Text.Pandoc.Readers.Odt.Base +import Text.Pandoc.Readers.Odt.Namespaces readStylesAt :: XML.Element -> Fallible Styles @@ -217,15 +216,15 @@ instance Lookupable StyleFamily where ] -- | A named style -data Style = Style { styleFamily :: Maybe StyleFamily - , styleParentName :: Maybe StyleName - , listStyle :: Maybe StyleName - , styleProperties :: StyleProperties +data Style = Style { styleFamily :: Maybe StyleFamily + , styleParentName :: Maybe StyleName + , listStyle :: Maybe StyleName + , styleProperties :: StyleProperties } deriving ( Eq, Show ) -data StyleProperties = SProps { textProperties :: Maybe TextProperties - , paraProperties :: Maybe ParaProperties +data StyleProperties = SProps { textProperties :: Maybe TextProperties + , paraProperties :: Maybe ParaProperties -- , tableColProperties :: Maybe TColProperties -- , tableRowProperties :: Maybe TRowProperties -- , tableCellProperties :: Maybe TCellProperties diff --git a/src/Text/Pandoc/Readers/Org/ParserState.hs b/src/Text/Pandoc/Readers/Org/ParserState.hs index 0349f7617..e0045fcd5 100644 --- a/src/Text/Pandoc/Readers/Org/ParserState.hs +++ b/src/Text/Pandoc/Readers/Org/ParserState.hs @@ -64,14 +64,13 @@ import Text.Pandoc.Builder (Blocks, Inlines) import Text.Pandoc.Definition (Meta (..), nullMeta) import Text.Pandoc.Logging import Text.Pandoc.Options (ReaderOptions (..)) -import Text.Pandoc.Readers.LaTeX.Types (Macro) import Text.Pandoc.Parsing (Future, HasHeaderMap (..), HasIdentifierList (..), HasIncludeFiles (..), HasLastStrPosition (..), - HasLogMessages (..), HasQuoteContext (..), - HasMacros (..), - HasReaderOptions (..), ParserContext (..), - QuoteContext (..), SourcePos, askF, asksF, returnF, - runF, trimInlinesF) + HasLogMessages (..), HasMacros (..), + HasQuoteContext (..), HasReaderOptions (..), + ParserContext (..), QuoteContext (..), SourcePos, + askF, asksF, returnF, runF, trimInlinesF) +import Text.Pandoc.Readers.LaTeX.Types (Macro) -- | This is used to delay evaluation until all relevant information has been -- parsed and made available in the parser state. diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 2d6bb979f..dae9fe40a 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -31,21 +31,23 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Conversion from reStructuredText to 'Pandoc' document. -} module Text.Pandoc.Readers.RST ( readRST ) where -import Control.Monad (guard, liftM, mzero, when, forM_, mplus) -import Control.Monad.Identity (Identity(..)) +import Control.Monad (forM_, guard, liftM, mplus, mzero, when) import Control.Monad.Except (throwError) +import Control.Monad.Identity (Identity (..)) import Data.Char (isHexDigit, isSpace, toLower, toUpper) -import Data.List (deleteFirstsBy, findIndex, intercalate, isInfixOf, - isSuffixOf, nub, sort, transpose, union) +import Data.List (deleteFirstsBy, findIndex, intercalate, isInfixOf, isSuffixOf, + nub, sort, transpose, union) import qualified Data.Map as M import Data.Maybe (fromMaybe, isJust) import Data.Monoid ((<>)) import Data.Sequence (ViewR (..), viewr) +import Data.Text (Text) +import qualified Data.Text as T import Text.Pandoc.Builder (fromList, setMeta) import Text.Pandoc.Builder (Blocks, Inlines, trimInlines) import qualified Text.Pandoc.Builder as B -import Text.Pandoc.Class (PandocMonad, readFileFromDirs, fetchItem) -import Text.Pandoc.CSV (CSVOptions(..), defaultCSVOptions, parseCSV) +import Text.Pandoc.Class (PandocMonad, fetchItem, readFileFromDirs) +import Text.Pandoc.CSV (CSVOptions (..), defaultCSVOptions, parseCSV) import Text.Pandoc.Definition import Text.Pandoc.Error import Text.Pandoc.ImageSize (lengthToDim, scaleDimension) @@ -55,8 +57,6 @@ import Text.Pandoc.Parsing import Text.Pandoc.Shared import qualified Text.Pandoc.UTF8 as UTF8 import Text.Printf (printf) -import Data.Text (Text) -import qualified Data.Text as T -- TODO: -- [ ] .. parsed-literal @@ -149,10 +149,10 @@ metaFromDefList ds meta = adjustAuthors $ foldr f meta ds splitOnSemi . concatMap factorSemi normalizeSpaces = reverse . dropWhile isSp . reverse . dropWhile isSp - isSp Space = True - isSp SoftBreak = True - isSp LineBreak = True - isSp _ = False + isSp Space = True + isSp SoftBreak = True + isSp LineBreak = True + isSp _ = False splitOnSemi = splitBy (==Str ";") factorSemi (Str []) = [] factorSemi (Str s) = case break (==';') s of @@ -817,9 +817,9 @@ listTableDirective top fields body = do headerRow bodyRows where takeRows [BulletList rows] = map takeCells rows - takeRows _ = [] + takeRows _ = [] takeCells [BulletList cells] = map B.fromList cells - takeCells _ = [] + takeCells _ = [] normWidths ws = map (/ max 1 (sum ws)) ws csvTableDirective :: PandocMonad m @@ -829,19 +829,19 @@ csvTableDirective top fields rawcsv = do let explicitHeader = trim <$> lookup "header" fields let opts = defaultCSVOptions{ csvDelim = case trim <$> lookup "delim" fields of - Just "tab" -> '\t' + Just "tab" -> '\t' Just "space" -> ' ' - Just [c] -> c - _ -> ',' + Just [c] -> c + _ -> ',' , csvQuote = case trim <$> lookup "quote" fields of Just [c] -> c - _ -> '"' + _ -> '"' , csvEscape = case trim <$> lookup "escape" fields of Just [c] -> Just c - _ -> Nothing + _ -> Nothing , csvKeepSpace = case trim <$> lookup "keepspace" fields of Just "true" -> True - _ -> False + _ -> False } let headerRowsNum = fromMaybe (case explicitHeader of Just _ -> 1 :: Int @@ -854,7 +854,7 @@ csvTableDirective top fields rawcsv = do return $ UTF8.toString bs Nothing -> return rawcsv let res = parseCSV opts (T.pack $ case explicitHeader of - Just h -> h ++ "\n" ++ rawcsv' + Just h -> h ++ "\n" ++ rawcsv' Nothing -> rawcsv') case res of Left e -> do diff --git a/src/Text/Pandoc/Readers/TWiki.hs b/src/Text/Pandoc/Readers/TWiki.hs index d41152de5..75e3f89eb 100644 --- a/src/Text/Pandoc/Readers/TWiki.hs +++ b/src/Text/Pandoc/Readers/TWiki.hs @@ -40,17 +40,17 @@ import Control.Monad.Except (throwError) import Data.Char (isAlphaNum) import qualified Data.Foldable as F import Data.Maybe (fromMaybe) +import Data.Text (Text) +import qualified Data.Text as T import Text.HTML.TagSoup import qualified Text.Pandoc.Builder as B -import Text.Pandoc.Class (PandocMonad(..)) +import Text.Pandoc.Class (PandocMonad (..)) import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (enclosed, nested) import Text.Pandoc.Readers.HTML (htmlTag, isCommentTag) -import Text.Pandoc.XML (fromEntities) import Text.Pandoc.Shared (crFilter) -import Data.Text (Text) -import qualified Data.Text as T +import Text.Pandoc.XML (fromEntities) -- | Read twiki from an input string and return a Pandoc document. readTWiki :: PandocMonad m diff --git a/src/Text/Pandoc/Readers/Textile.hs b/src/Text/Pandoc/Readers/Textile.hs index 90567ef23..a57ab93d7 100644 --- a/src/Text/Pandoc/Readers/Textile.hs +++ b/src/Text/Pandoc/Readers/Textile.hs @@ -57,20 +57,20 @@ import Control.Monad.Except (throwError) import Data.Char (digitToInt, isUpper) import Data.List (intercalate, intersperse, transpose) import Data.Monoid ((<>)) +import Data.Text (Text) +import qualified Data.Text as T import Text.HTML.TagSoup (Tag (..), fromAttrib) import Text.HTML.TagSoup.Match import Text.Pandoc.Builder (Blocks, Inlines, trimInlines) import qualified Text.Pandoc.Builder as B -import Text.Pandoc.Class (PandocMonad(..)) +import Text.Pandoc.Class (PandocMonad (..)) import Text.Pandoc.CSS import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Parsing import Text.Pandoc.Readers.HTML (htmlTag, isBlockTag, isInlineTag) import Text.Pandoc.Readers.LaTeX (rawLaTeXBlock, rawLaTeXInline) -import Text.Pandoc.Shared (trim, crFilter, underlineSpan) -import Data.Text (Text) -import qualified Data.Text as T +import Text.Pandoc.Shared (crFilter, trim, underlineSpan) -- | Parse a Textile text and return a Pandoc document. readTextile :: PandocMonad m diff --git a/src/Text/Pandoc/Readers/TikiWiki.hs b/src/Text/Pandoc/Readers/TikiWiki.hs index 4acbaa30b..16d6e633b 100644 --- a/src/Text/Pandoc/Readers/TikiWiki.hs +++ b/src/Text/Pandoc/Readers/TikiWiki.hs @@ -1,6 +1,8 @@ -{-# LANGUAGE RelaxedPolyRec, FlexibleInstances, TypeSynonymInstances #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RelaxedPolyRec #-} +{-# LANGUAGE TypeSynonymInstances #-} {- | Module : Text.Pandoc.Readers.TikiWiki @@ -19,20 +21,20 @@ module Text.Pandoc.Readers.TikiWiki ( readTikiWiki import Control.Monad import Control.Monad.Except (throwError) -import Text.Pandoc.Definition +import qualified Data.Foldable as F +import Data.List (intercalate) +import Data.Maybe (fromMaybe) +import Data.Text (Text) +import qualified Data.Text as T import qualified Text.Pandoc.Builder as B +import Text.Pandoc.Class (CommonState (..), PandocMonad (..)) +import Text.Pandoc.Definition +import Text.Pandoc.Logging (Verbosity (..)) import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (enclosed, nested) -import Text.Printf (printf) -import Text.Pandoc.XML (fromEntities) -import Text.Pandoc.Class (PandocMonad(..), CommonState(..)) import Text.Pandoc.Shared (crFilter) -import Text.Pandoc.Logging (Verbosity(..)) -import Data.Maybe (fromMaybe) -import Data.List (intercalate) -import qualified Data.Foldable as F -import Data.Text (Text) -import qualified Data.Text as T +import Text.Pandoc.XML (fromEntities) +import Text.Printf (printf) -- | Read TikiWiki from an input string and return a Pandoc document. readTikiWiki :: PandocMonad m @@ -129,9 +131,9 @@ header = tryMsg "header" $ do tableRow :: PandocMonad m => TikiWikiParser m [B.Blocks] tableRow = try $ do --- row <- sepBy1 (many1Till inline $ oneOf "\n|") (try $ string "|" <* notFollowedBy (oneOf "|\n")) +-- row <- sepBy1 (many1Till inline $ oneOf "\n|") (try $ string "|" <* notFollowedBy (oneOf "|\n")) -- return $ map (B.plain . mconcat) row - row <- sepBy1 ((many1 $ noneOf "\n|") >>= parseColumn) (try $ string "|" <* notFollowedBy (oneOf "|\n")) + row <- sepBy1 ((many1 $ noneOf "\n|") >>= parseColumn) (try $ string "|" <* notFollowedBy (oneOf "|\n")) return $ map B.plain row where parseColumn x = do @@ -155,7 +157,7 @@ tableRow = try $ do -- || Orange | Apple | more -- Bread | Pie | more -- Butter | Ice cream | and more || --- +-- table :: PandocMonad m => TikiWikiParser m B.Blocks table = try $ do string "||" @@ -233,8 +235,8 @@ fixListNesting :: [B.Blocks] -> [B.Blocks] fixListNesting [] = [] fixListNesting (first:[]) = [recurseOnList first] -- fixListNesting nestall | trace ("\n\nfixListNesting: " ++ (show nestall)) False = undefined --- fixListNesting nestall@(first:second:rest) = -fixListNesting (first:second:rest) = +-- fixListNesting nestall@(first:second:rest) = +fixListNesting (first:second:rest) = let secondBlock = head $ B.toList second in case secondBlock of BulletList _ -> fixListNesting $ [(mappend (recurseOnList first) (recurseOnList second))] ++ rest @@ -300,8 +302,8 @@ listWrap upperLN curLN retTree = retTree else case lntype curLN of - None -> [] - Bullet -> [B.bulletList retTree] + None -> [] + Bullet -> [B.bulletList retTree] Numbered -> [B.orderedList retTree] listItem :: PandocMonad m => TikiWikiParser m (ListNesting, B.Blocks) @@ -418,7 +420,7 @@ whitespace = (lb <|> regsp) >>= return -- for this nbsp :: PandocMonad m => TikiWikiParser m B.Inlines nbsp = try $ do - string "~hs~" + string "~hs~" return $ B.str " NOT SUPPORTED BEGIN: ~hs~ (non-breaking space) :END " -- UNSUPPORTED, as the desired behaviour (that the data be @@ -426,7 +428,7 @@ nbsp = try $ do -- silently throwing data out seemed bad. htmlComment :: PandocMonad m => TikiWikiParser m B.Inlines htmlComment = try $ do - string "~hc~" + string "~hc~" inner <- many1 $ noneOf "~" string "~/hc~" return $ B.str $ " NOT SUPPORTED: ~hc~ (html comment opener) BEGIN: " ++ inner ++ " ~/hc~ :END " diff --git a/src/Text/Pandoc/Readers/Txt2Tags.hs b/src/Text/Pandoc/Readers/Txt2Tags.hs index 0e68cdfb7..fdf7a827a 100644 --- a/src/Text/Pandoc/Readers/Txt2Tags.hs +++ b/src/Text/Pandoc/Readers/Txt2Tags.hs @@ -32,26 +32,27 @@ module Text.Pandoc.Readers.Txt2Tags ( readTxt2Tags ) where +import Control.Monad (guard, void, when) +import Control.Monad.Except (catchError, throwError) +import Control.Monad.Reader (Reader, asks, runReader) import Data.Char (toLower) +import Data.Default import Data.List (intercalate, intersperse, transpose) import Data.Maybe (fromMaybe) import Data.Monoid ((<>)) -import Text.Pandoc.Builder (Blocks, Inlines, trimInlines) -import qualified Text.Pandoc.Builder as B -import Text.Pandoc.Definition -import Text.Pandoc.Options -import Text.Pandoc.Parsing hiding (space, spaces, uri) -import Text.Pandoc.Shared (compactify, compactifyDL, escapeURI, crFilter, underlineSpan) -import Control.Monad (guard, void, when) -import Control.Monad.Reader (Reader, asks, runReader) -import Data.Default import Data.Text (Text) import qualified Data.Text as T -import Control.Monad.Except (catchError, throwError) import Data.Time.Format (formatTime) +import Text.Pandoc.Builder (Blocks, Inlines, trimInlines) +import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad) import qualified Text.Pandoc.Class as P import Text.Pandoc.Compat.Time (defaultTimeLocale) +import Text.Pandoc.Definition +import Text.Pandoc.Options +import Text.Pandoc.Parsing hiding (space, spaces, uri) +import Text.Pandoc.Shared (compactify, compactifyDL, crFilter, escapeURI, + underlineSpan) type T2T = ParserT String ParserState (Reader T2TMeta) diff --git a/src/Text/Pandoc/Readers/Vimwiki.hs b/src/Text/Pandoc/Readers/Vimwiki.hs index 52bf37d35..37c8c32d0 100644 --- a/src/Text/Pandoc/Readers/Vimwiki.hs +++ b/src/Text/Pandoc/Readers/Vimwiki.hs @@ -63,36 +63,41 @@ Conversion of vimwiki text to 'Pandoc' document. module Text.Pandoc.Readers.Vimwiki ( readVimwiki ) where -import Control.Monad.Except (throwError) import Control.Monad (guard) +import Control.Monad.Except (throwError) import Data.Default +import Data.List (isInfixOf, isPrefixOf) import Data.Maybe import Data.Monoid ((<>)) -import Data.List (isInfixOf, isPrefixOf) import Data.Text (Text, unpack) -import Text.Pandoc.Builder (Blocks, Inlines, trimInlines, fromList, toList) -import qualified Text.Pandoc.Builder - as B (headerWith, str, space, strong, emph, strikeout, code, link, image, - spanWith, para, horizontalRule, blockQuote, bulletList, plain, - orderedList, simpleTable, softbreak, codeBlockWith, imageWith, divWith, - setMeta, definitionList, superscript, subscript, displayMath, - math) -import Text.Pandoc.Class (PandocMonad(..)) -import Text.Pandoc.Definition (Pandoc(..), Inline(Space), - Block(BulletList, OrderedList), Attr, nullMeta, Meta, ListNumberStyle(..), - ListNumberDelim(..)) +import Text.Pandoc.Builder (Blocks, Inlines, fromList, toList, trimInlines) +import qualified Text.Pandoc.Builder as B (blockQuote, bulletList, code, + codeBlockWith, definitionList, + displayMath, divWith, emph, + headerWith, horizontalRule, image, + imageWith, link, math, orderedList, + para, plain, setMeta, simpleTable, + softbreak, space, spanWith, str, + strikeout, strong, subscript, + superscript) +import Text.Pandoc.Class (PandocMonad (..)) +import Text.Pandoc.Definition (Attr, Block (BulletList, OrderedList), + Inline (Space), ListNumberDelim (..), + ListNumberStyle (..), Meta, Pandoc (..), + nullMeta) import Text.Pandoc.Options (ReaderOptions) -import Text.Pandoc.Parsing (readWithM, ParserT, stateOptions, ParserState, - stateMeta', blanklines, registerHeader, spaceChar, emailAddress, uri, F, runF, - orderedListMarker, many1Till) -import Text.Pandoc.Shared (splitBy, stripFirstAndLast, stringify, crFilter) -import Text.Parsec.Char (spaces, char, anyChar, newline, string, noneOf, - alphaNum) -import Text.Parsec.Combinator (eof, choice, many1, manyTill, count, skipMany1, - notFollowedBy, option) -import Text.Parsec.Prim (many, try, updateState, getState) +import Text.Pandoc.Parsing (F, ParserState, ParserT, blanklines, emailAddress, + many1Till, orderedListMarker, readWithM, + registerHeader, runF, spaceChar, stateMeta', + stateOptions, uri) +import Text.Pandoc.Shared (crFilter, splitBy, stringify, stripFirstAndLast) +import Text.Parsec.Char (alphaNum, anyChar, char, newline, noneOf, spaces, + string) import Text.Parsec.Char (oneOf, space) -import Text.Parsec.Combinator (lookAhead, between) +import Text.Parsec.Combinator (choice, count, eof, many1, manyTill, + notFollowedBy, option, skipMany1) +import Text.Parsec.Combinator (between, lookAhead) +import Text.Parsec.Prim (getState, many, try, updateState) import Text.Parsec.Prim ((<|>)) readVimwiki :: PandocMonad m => ReaderOptions -> Text -> m Pandoc @@ -100,7 +105,7 @@ readVimwiki opts s = do res <- readWithM parseVimwiki def{ stateOptions = opts } (unpack (crFilter s)) case res of - Left e -> throwError e + Left e -> throwError e Right result -> return result type VwParser = ParserT [Char] ParserState @@ -278,19 +283,19 @@ displayMath = try $ do mathTagLaTeX :: String -> String mathTagLaTeX s = case s of - "equation" -> "" + "equation" -> "" "equation*" -> "" - "gather" -> "gathered" - "gather*" -> "gathered" - "multline" -> "gathered" + "gather" -> "gathered" + "gather*" -> "gathered" + "multline" -> "gathered" "multline*" -> "gathered" - "eqnarray" -> "aligned" + "eqnarray" -> "aligned" "eqnarray*" -> "aligned" - "align" -> "aligned" - "align*" -> "aligned" - "alignat" -> "aligned" - "alignat*" -> "aligned" - _ -> s + "align" -> "aligned" + "align*" -> "aligned" + "alignat" -> "aligned" + "alignat*" -> "aligned" + _ -> s mixedList :: PandocMonad m => VwParser m Blocks diff --git a/src/Text/Pandoc/SelfContained.hs b/src/Text/Pandoc/SelfContained.hs index ae44cd8cb..b599eb62b 100644 --- a/src/Text/Pandoc/SelfContained.hs +++ b/src/Text/Pandoc/SelfContained.hs @@ -35,18 +35,18 @@ import Codec.Compression.GZip as Gzip import Control.Applicative ((<|>)) import Control.Monad.Except (throwError) import Control.Monad.Trans (lift) -import Data.Monoid ((<>)) import Data.ByteString (ByteString) import Data.ByteString.Base64 import qualified Data.ByteString.Char8 as B import qualified Data.ByteString.Lazy as L import Data.Char (isAlphaNum, isAscii, toLower) import Data.List (isPrefixOf) +import Data.Monoid ((<>)) import Network.URI (escapeURIString) import System.FilePath (takeDirectory, takeExtension, (</>)) import Text.HTML.TagSoup -import Text.Pandoc.Class (PandocMonad (..), fetchItem, report, - getInputFiles, setInputFiles) +import Text.Pandoc.Class (PandocMonad (..), fetchItem, getInputFiles, report, + setInputFiles) import Text.Pandoc.Error import Text.Pandoc.Logging import Text.Pandoc.MIME (MimeType) diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 2307470a1..49b41b534 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -1,6 +1,10 @@ -{-# LANGUAGE DeriveDataTypeable, CPP, MultiParamTypeClasses, - FlexibleContexts, ScopedTypeVariables, PatternGuards, - ViewPatterns #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE PatternGuards #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE ViewPatterns #-} {- Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> @@ -94,37 +98,37 @@ module Text.Pandoc.Shared ( pandocVersion ) where -import Text.Pandoc.Definition -import Text.Pandoc.Walk -import Text.Pandoc.Builder (Inlines, Blocks, ToMetaValue(..)) -import qualified Text.Pandoc.Builder as B -import Data.Char ( toLower, isLower, isUpper, isAlpha, - isLetter, isDigit, isSpace ) -import Data.List ( find, stripPrefix, intercalate ) -import Data.Maybe (mapMaybe) -import Data.Version ( showVersion ) +import Codec.Archive.Zip +import qualified Control.Exception as E +import Control.Monad (MonadPlus (..), msum, unless) +import qualified Control.Monad.State.Strict as S +import qualified Data.ByteString.Lazy as BL +import Data.Char (isAlpha, isDigit, isLetter, isLower, isSpace, isUpper, + toLower) +import Data.Generics (Data, Typeable) +import Data.List (find, intercalate, stripPrefix) import qualified Data.Map as M -import Network.URI ( URI(uriScheme), escapeURIString, parseURI ) +import Data.Maybe (mapMaybe) +import Data.Monoid ((<>)) +import Data.Sequence (ViewL (..), ViewR (..), viewl, viewr) import qualified Data.Set as Set +import qualified Data.Text as T +import Data.Version (showVersion) +import Network.URI (URI (uriScheme), escapeURIString, parseURI) +import Paths_pandoc (version) import System.Directory -import System.FilePath (splitDirectories, isPathSeparator) +import System.FilePath (isPathSeparator, splitDirectories) import qualified System.FilePath.Posix as Posix -import Data.Generics (Typeable, Data) -import qualified Control.Monad.State.Strict as S -import qualified Control.Exception as E -import Control.Monad (msum, unless, MonadPlus(..)) -import Text.Pandoc.Pretty (charWidth) -import Text.Pandoc.Generic (bottomUp) -import Text.Pandoc.Compat.Time import System.IO.Temp -import Text.HTML.TagSoup (renderTagsOptions, RenderOptions(..), Tag(..), - renderOptions) -import Data.Monoid ((<>)) -import Data.Sequence (ViewR(..), ViewL(..), viewl, viewr) -import qualified Data.Text as T -import qualified Data.ByteString.Lazy as BL -import Paths_pandoc (version) -import Codec.Archive.Zip +import Text.HTML.TagSoup (RenderOptions (..), Tag (..), renderOptions, + renderTagsOptions) +import Text.Pandoc.Builder (Blocks, Inlines, ToMetaValue (..)) +import qualified Text.Pandoc.Builder as B +import Text.Pandoc.Compat.Time +import Text.Pandoc.Definition +import Text.Pandoc.Generic (bottomUp) +import Text.Pandoc.Pretty (charWidth) +import Text.Pandoc.Walk -- | Version number of pandoc library. pandocVersion :: String @@ -192,8 +196,8 @@ escapeStringUsing :: [(Char, String)] -> String -> String escapeStringUsing _ [] = "" escapeStringUsing escapeTable (x:xs) = case (lookup x escapeTable) of - Just str -> str ++ rest - Nothing -> x:rest + Just str -> str ++ rest + Nothing -> x:rest where rest = escapeStringUsing escapeTable xs -- | Strip trailing newlines from string. @@ -279,7 +283,7 @@ normalizeDate s = fmap (formatTime defaultTimeLocale "%F") (msum $ map (\fs -> parsetimeWith fs s >>= rejectBadYear) formats :: Maybe Day) where rejectBadYear day = case toGregorian day of (y, _, _) | y >= 1601 && y <= 9999 -> Just day - _ -> Nothing + _ -> Nothing parsetimeWith = #if MIN_VERSION_time(1,5,0) parseTimeM True defaultTimeLocale @@ -336,13 +340,13 @@ extractSpaces f is = removeFormatting :: Walkable Inline a => a -> [Inline] removeFormatting = query go . walk (deNote . deQuote) where go :: Inline -> [Inline] - go (Str xs) = [Str xs] - go Space = [Space] - go SoftBreak = [SoftBreak] - go (Code _ x) = [Str x] - go (Math _ x) = [Str x] - go LineBreak = [Space] - go _ = [] + go (Str xs) = [Str xs] + go Space = [Space] + go SoftBreak = [SoftBreak] + go (Code _ x) = [Str x] + go (Math _ x) = [Str x] + go LineBreak = [Space] + go _ = [] deNote :: Inline -> Inline deNote (Note _) = Str "" @@ -361,14 +365,14 @@ deQuote x = x stringify :: Walkable Inline a => a -> String stringify = query go . walk (deNote . deQuote) where go :: Inline -> [Char] - go Space = " " - go SoftBreak = " " - go (Str x) = x - go (Code _ x) = x - go (Math _ x) = x + go Space = " " + go SoftBreak = " " + go (Str x) = x + go (Code _ x) = x + go (Math _ x) = x go (RawInline (Format "html") ('<':'b':'r':_)) = " " -- see #2105 - go LineBreak = " " - go _ = "" + go LineBreak = " " + go _ = "" -- | Bring all regular text in a pandoc structure to uppercase. -- @@ -440,7 +444,7 @@ instance Walkable Inline Element where ils' <- walkM f ils elts' <- walkM f elts return $ Sec lev nums attr ils' elts' - query f (Blk x) = query f x + query f (Blk x) = query f x query f (Sec _ _ _ ils elts) = query f ils <> query f elts instance Walkable Block Element where @@ -451,7 +455,7 @@ instance Walkable Block Element where ils' <- walkM f ils elts' <- walkM f elts return $ Sec lev nums attr ils' elts' - query f (Blk x) = query f x + query f (Blk x) = query f x query f (Sec _ _ _ ils elts) = query f ils <> query f elts @@ -464,8 +468,8 @@ inlineListToIdentifier = map (nbspToSp . toLower) . filter (\c -> isLetter c || isDigit c || c `elem` "_-. ") . stringify - where nbspToSp '\160' = ' ' - nbspToSp x = x + where nbspToSp '\160' = ' ' + nbspToSp x = x -- | Convert list of Pandoc blocks into (hierarchical) list of Elements hierarchicalize :: [Block] -> [Element] @@ -495,17 +499,17 @@ hierarchicalizeWithIds (x:rest) = do return $ (Blk x) : rest' headerLtEq :: Int -> Block -> Bool -headerLtEq level (Header l _ _) = l <= level -headerLtEq level (Div ("",["references"],[]) (Header l _ _ : _)) = l <= level -headerLtEq _ _ = False +headerLtEq level (Header l _ _) = l <= level +headerLtEq level (Div ("",["references"],[]) (Header l _ _ : _)) = l <= level +headerLtEq _ _ = False -- | Generate a unique identifier from a list of inlines. -- Second argument is a list of already used identifiers. uniqueIdent :: [Inline] -> Set.Set String -> String uniqueIdent title' usedIdents = let baseIdent = case inlineListToIdentifier title' of - "" -> "section" - x -> x + "" -> "section" + x -> x numIdent n = baseIdent ++ "-" ++ show n in if baseIdent `Set.member` usedIdents then case find (\x -> not $ numIdent x `Set.member` usedIdents) ([1..60000] :: [Int]) of @@ -516,7 +520,7 @@ uniqueIdent title' usedIdents -- | True if block is a Header block. isHeaderBlock :: Block -> Bool isHeaderBlock (Header _ _ _) = True -isHeaderBlock _ = False +isHeaderBlock _ = False -- | Shift header levels up or down. headerShift :: Int -> Pandoc -> Pandoc @@ -542,8 +546,8 @@ addMetaField key val (Meta meta) = Meta $ M.insertWith combine key (toMetaValue val) meta where combine newval (MetaList xs) = MetaList (xs ++ tolist newval) combine newval x = MetaList [x, newval] - tolist (MetaList ys) = ys - tolist y = [y] + tolist (MetaList ys) = ys + tolist y = [y] -- | Create 'Meta' from old-style title, authors, date. This is -- provided to ease the transition from the old API. @@ -599,7 +603,7 @@ inDirectory path action = E.bracket -- mapLeft :: (a -> b) -> Either a c -> Either b c -mapLeft f (Left x) = Left (f x) +mapLeft f (Left x) = Left (f x) mapLeft _ (Right x) = Right x -- | Remove intermediate "." and ".." directories from a path. @@ -616,14 +620,14 @@ collapseFilePath = Posix.joinPath . reverse . foldl go [] . splitDirectories where go rs "." = rs go r@(p:rs) ".." = case p of - ".." -> ("..":r) + ".." -> ("..":r) (checkPathSeperator -> Just True) -> ("..":r) - _ -> rs + _ -> rs go _ (checkPathSeperator -> Just True) = [[Posix.pathSeparator]] go rs x = x:rs - isSingleton [] = Nothing + isSingleton [] = Nothing isSingleton [x] = Just x - isSingleton _ = Nothing + isSingleton _ = Nothing checkPathSeperator = fmap isPathSeparator . isSingleton -- diff --git a/src/Text/Pandoc/Templates.hs b/src/Text/Pandoc/Templates.hs index 7914c35f8..9f3781259 100644 --- a/src/Text/Pandoc/Templates.hs +++ b/src/Text/Pandoc/Templates.hs @@ -44,7 +44,7 @@ import qualified Data.Text as T import System.FilePath ((<.>), (</>)) import Text.DocTemplates (Template, TemplateTarget, applyTemplate, compileTemplate, renderTemplate, varListToJSON) -import Text.Pandoc.Class (readDataFile, PandocMonad) +import Text.Pandoc.Class (PandocMonad, readDataFile) import Text.Pandoc.Error import qualified Text.Pandoc.UTF8 as UTF8 diff --git a/src/Text/Pandoc/Translations.hs b/src/Text/Pandoc/Translations.hs index d9e7e05e2..00529c1de 100644 --- a/src/Text/Pandoc/Translations.hs +++ b/src/Text/Pandoc/Translations.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {- Copyright (C) 2017 John MacFarlane <jgm@berkeley.edu> @@ -45,14 +45,14 @@ module Text.Pandoc.Translations ( , readTranslations ) where +import Data.Aeson.Types (typeMismatch) import qualified Data.HashMap.Strict as HM import qualified Data.Map as M -import GHC.Generics (Generic) -import qualified Text.Pandoc.UTF8 as UTF8 import Data.Text as T -import Text.Pandoc.Shared (safeRead) import Data.Yaml as Yaml -import Data.Aeson.Types (typeMismatch) +import GHC.Generics (Generic) +import Text.Pandoc.Shared (safeRead) +import qualified Text.Pandoc.UTF8 as UTF8 data Term = Preface diff --git a/src/Text/Pandoc/Writers.hs b/src/Text/Pandoc/Writers.hs index 3e8729eb9..046022b09 100644 --- a/src/Text/Pandoc/Writers.hs +++ b/src/Text/Pandoc/Writers.hs @@ -82,11 +82,13 @@ module Text.Pandoc.Writers ) where import Data.Aeson +import qualified Data.ByteString.Lazy as BL import Data.List (intercalate) import Data.Text (Text) import Text.Pandoc.Class import Text.Pandoc.Definition import Text.Pandoc.Options +import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Writers.AsciiDoc import Text.Pandoc.Writers.CommonMark import Text.Pandoc.Writers.ConTeXt @@ -96,8 +98,8 @@ import Text.Pandoc.Writers.Docx import Text.Pandoc.Writers.DokuWiki import Text.Pandoc.Writers.EPUB import Text.Pandoc.Writers.FB2 -import Text.Pandoc.Writers.HTML import Text.Pandoc.Writers.Haddock +import Text.Pandoc.Writers.HTML import Text.Pandoc.Writers.ICML import Text.Pandoc.Writers.JATS import Text.Pandoc.Writers.LaTeX @@ -108,8 +110,8 @@ import Text.Pandoc.Writers.Ms import Text.Pandoc.Writers.Muse import Text.Pandoc.Writers.Native import Text.Pandoc.Writers.ODT -import Text.Pandoc.Writers.OPML import Text.Pandoc.Writers.OpenDocument +import Text.Pandoc.Writers.OPML import Text.Pandoc.Writers.Org import Text.Pandoc.Writers.RST import Text.Pandoc.Writers.RTF @@ -118,8 +120,6 @@ import Text.Pandoc.Writers.Texinfo import Text.Pandoc.Writers.Textile import Text.Pandoc.Writers.ZimWiki import Text.Parsec.Error -import qualified Text.Pandoc.UTF8 as UTF8 -import qualified Data.ByteString.Lazy as BL data Writer m = TextWriter (WriterOptions -> Pandoc -> m Text) | ByteStringWriter (WriterOptions -> Pandoc -> m BL.ByteString) diff --git a/src/Text/Pandoc/Writers/CommonMark.hs b/src/Text/Pandoc/Writers/CommonMark.hs index 9dc7158fe..9bd9f25bc 100644 --- a/src/Text/Pandoc/Writers/CommonMark.hs +++ b/src/Text/Pandoc/Writers/CommonMark.hs @@ -35,8 +35,8 @@ module Text.Pandoc.Writers.CommonMark (writeCommonMark) where import CMarkGFM import Control.Monad.State.Strict (State, get, modify, runState) import Data.Foldable (foldrM) -import Data.Monoid (Any (..), (<>)) import Data.List (transpose) +import Data.Monoid (Any (..), (<>)) import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Class (PandocMonad) @@ -44,7 +44,7 @@ import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Shared (isTightList, linesToPara, substitute) import Text.Pandoc.Templates (renderTemplate') -import Text.Pandoc.Walk (walkM, walk, query) +import Text.Pandoc.Walk (query, walk, walkM) import Text.Pandoc.Writers.HTML (writeHtml5String) import Text.Pandoc.Writers.Shared @@ -67,7 +67,7 @@ writeCommonMark opts (Pandoc meta blocks) = do softBreakToSpace :: Inline -> Inline softBreakToSpace SoftBreak = Space -softBreakToSpace x = x +softBreakToSpace x = x processNotes :: Inline -> State [[Block]] Inline processNotes (Note bs) = do diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index 0a399197d..101be3fc0 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {- Copyright (C) 2007-2017 John MacFarlane <jgm@berkeley.edu> @@ -38,9 +38,9 @@ import Data.Text (Text) import Network.URI (unEscapeString) import Text.Pandoc.BCP47 import Text.Pandoc.Class (PandocMonad, report, toLang) -import Text.Pandoc.Logging import Text.Pandoc.Definition import Text.Pandoc.ImageSize +import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Pretty import Text.Pandoc.Shared @@ -475,26 +475,26 @@ fromBCP47 mbs = fromBCP47' <$> toLang mbs -- https://tools.ietf.org/html/bcp47#section-2.1 -- http://wiki.contextgarden.net/Language_Codes fromBCP47' :: Maybe Lang -> Maybe String -fromBCP47' (Just (Lang "ar" _ "SY" _) ) = Just "ar-sy" -fromBCP47' (Just (Lang "ar" _ "IQ" _) ) = Just "ar-iq" -fromBCP47' (Just (Lang "ar" _ "JO" _) ) = Just "ar-jo" -fromBCP47' (Just (Lang "ar" _ "LB" _) ) = Just "ar-lb" -fromBCP47' (Just (Lang "ar" _ "DZ" _) ) = Just "ar-dz" -fromBCP47' (Just (Lang "ar" _ "MA" _) ) = Just "ar-ma" -fromBCP47' (Just (Lang "de" _ _ ["1901"]) ) = Just "deo" -fromBCP47' (Just (Lang "de" _ "DE" _) ) = Just "de-de" -fromBCP47' (Just (Lang "de" _ "AT" _) ) = Just "de-at" -fromBCP47' (Just (Lang "de" _ "CH" _) ) = Just "de-ch" -fromBCP47' (Just (Lang "el" _ _ ["poly"]) ) = Just "agr" -fromBCP47' (Just (Lang "en" _ "US" _) ) = Just "en-us" -fromBCP47' (Just (Lang "en" _ "GB" _) ) = Just "en-gb" -fromBCP47' (Just (Lang "grc"_ _ _) ) = Just "agr" -fromBCP47' (Just (Lang "el" _ _ _) ) = Just "gr" -fromBCP47' (Just (Lang "eu" _ _ _) ) = Just "ba" -fromBCP47' (Just (Lang "he" _ _ _) ) = Just "il" -fromBCP47' (Just (Lang "jp" _ _ _) ) = Just "ja" -fromBCP47' (Just (Lang "uk" _ _ _) ) = Just "ua" -fromBCP47' (Just (Lang "vi" _ _ _) ) = Just "vn" -fromBCP47' (Just (Lang "zh" _ _ _) ) = Just "cn" -fromBCP47' (Just (Lang l _ _ _) ) = Just l -fromBCP47' Nothing = Nothing +fromBCP47' (Just (Lang "ar" _ "SY" _) ) = Just "ar-sy" +fromBCP47' (Just (Lang "ar" _ "IQ" _) ) = Just "ar-iq" +fromBCP47' (Just (Lang "ar" _ "JO" _) ) = Just "ar-jo" +fromBCP47' (Just (Lang "ar" _ "LB" _) ) = Just "ar-lb" +fromBCP47' (Just (Lang "ar" _ "DZ" _) ) = Just "ar-dz" +fromBCP47' (Just (Lang "ar" _ "MA" _) ) = Just "ar-ma" +fromBCP47' (Just (Lang "de" _ _ ["1901"]) ) = Just "deo" +fromBCP47' (Just (Lang "de" _ "DE" _) ) = Just "de-de" +fromBCP47' (Just (Lang "de" _ "AT" _) ) = Just "de-at" +fromBCP47' (Just (Lang "de" _ "CH" _) ) = Just "de-ch" +fromBCP47' (Just (Lang "el" _ _ ["poly"]) ) = Just "agr" +fromBCP47' (Just (Lang "en" _ "US" _) ) = Just "en-us" +fromBCP47' (Just (Lang "en" _ "GB" _) ) = Just "en-gb" +fromBCP47' (Just (Lang "grc"_ _ _) ) = Just "agr" +fromBCP47' (Just (Lang "el" _ _ _) ) = Just "gr" +fromBCP47' (Just (Lang "eu" _ _ _) ) = Just "ba" +fromBCP47' (Just (Lang "he" _ _ _) ) = Just "il" +fromBCP47' (Just (Lang "jp" _ _ _) ) = Just "ja" +fromBCP47' (Just (Lang "uk" _ _ _) ) = Just "ua" +fromBCP47' (Just (Lang "vi" _ _ _) ) = Just "vn" +fromBCP47' (Just (Lang "zh" _ _ _) ) = Just "cn" +fromBCP47' (Just (Lang l _ _ _) ) = Just l +fromBCP47' Nothing = Nothing diff --git a/src/Text/Pandoc/Writers/Custom.hs b/src/Text/Pandoc/Writers/Custom.hs index d7dff6d19..09cf3fac8 100644 --- a/src/Text/Pandoc/Writers/Custom.hs +++ b/src/Text/Pandoc/Writers/Custom.hs @@ -43,12 +43,12 @@ import Data.List (intersperse) import qualified Data.Map as M import Data.Text (Text, pack) import Data.Typeable -import GHC.IO.Encoding (getForeignEncoding, setForeignEncoding, utf8) import Foreign.Lua (Lua, ToLuaStack (..), callFunc, runLua) import Foreign.Lua.Api -import Text.Pandoc.Error -import Text.Pandoc.Lua.Util ( addValue ) +import GHC.IO.Encoding (getForeignEncoding, setForeignEncoding, utf8) import Text.Pandoc.Definition +import Text.Pandoc.Error +import Text.Pandoc.Lua.Util (addValue) import Text.Pandoc.Options import Text.Pandoc.Templates import qualified Text.Pandoc.UTF8 as UTF8 diff --git a/src/Text/Pandoc/Writers/Docbook.hs b/src/Text/Pandoc/Writers/Docbook.hs index 9db9a0102..d6b7f7cad 100644 --- a/src/Text/Pandoc/Writers/Docbook.hs +++ b/src/Text/Pandoc/Writers/Docbook.hs @@ -32,10 +32,10 @@ Conversion of 'Pandoc' documents to Docbook XML. module Text.Pandoc.Writers.Docbook ( writeDocbook4, writeDocbook5 ) where import Control.Monad.Reader import Data.Char (toLower) -import Data.Text (Text) import Data.Generics (everywhere, mkT) import Data.List (intercalate, isPrefixOf, isSuffixOf, stripPrefix) import Data.Monoid (Any (..)) +import Data.Text (Text) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Definition diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index f19621744..3ab4548a2 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -51,6 +51,7 @@ import qualified Data.Text as T import Data.Time.Clock.POSIX import Skylighting import System.Random (randomR) +import Text.Pandoc.BCP47 (getLang, renderLang) import Text.Pandoc.Class (PandocMonad, report, toLang) import qualified Text.Pandoc.Class as P import Text.Pandoc.Compat.Time @@ -68,7 +69,6 @@ import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Walk import Text.Pandoc.Writers.Math import Text.Pandoc.Writers.Shared (fixDisplayMath) -import Text.Pandoc.BCP47 (getLang, renderLang) import Text.Printf (printf) import Text.TeXMath import Text.XML.Light as XML @@ -276,9 +276,9 @@ writeDocx opts doc@(Pandoc meta _) = do setval _ x = x setvalattr :: String -> XML.Attr -> XML.Attr setvalattr l (XML.Attr qn@(QName "val" _ _) _) = XML.Attr qn l - setvalattr _ x = x + setvalattr _ x = x isLangElt (Elem e') = qName (elName e') == "lang" - isLangElt _ = False + isLangElt _ = False let stylepath = "word/styles.xml" styledoc <- addLang <$> parseXml refArchive distArchive stylepath @@ -508,8 +508,8 @@ writeDocx opts doc@(Pandoc meta _) = do , qName (elName e) == "num" ] } let keywords = case lookupMeta "keywords" meta of - Just (MetaList xs) -> map stringify xs - _ -> [] + Just (MetaList xs) -> map stringify xs + _ -> [] let docPropsPath = "docProps/core.xml" let docProps = mknode "cp:coreProperties" diff --git a/src/Text/Pandoc/Writers/DokuWiki.hs b/src/Text/Pandoc/Writers/DokuWiki.hs index 279475a21..43e2952de 100644 --- a/src/Text/Pandoc/Writers/DokuWiki.hs +++ b/src/Text/Pandoc/Writers/DokuWiki.hs @@ -46,9 +46,9 @@ import Data.Default (Default (..)) import Data.List (intercalate, intersect, isPrefixOf, transpose) import Data.Text (Text, pack) import Text.Pandoc.Class (PandocMonad, report) -import Text.Pandoc.Logging import Text.Pandoc.Definition import Text.Pandoc.ImageSize +import Text.Pandoc.Logging import Text.Pandoc.Options (WrapOption (..), WriterOptions (writerTableOfContents, writerTemplate, writerWrapText)) import Text.Pandoc.Shared (camelCaseToHyphenated, escapeURI, isURI, linesToPara, removeFormatting, substitute, trimr) @@ -291,7 +291,7 @@ listItemToDokuWiki opts items = do bs <- mapM (blockToDokuWiki opts) items let contents = case items of [_, CodeBlock _ _] -> concat bs - _ -> vcat bs + _ -> vcat bs indent <- stIndent <$> ask backSlash <- stBackSlashLB <$> ask let indent' = if backSlash then (drop 2 indent) else indent diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index c75845fa9..d28187bf0 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -34,19 +34,19 @@ Conversion of 'Pandoc' documents to EPUB. module Text.Pandoc.Writers.EPUB ( writeEPUB2, writeEPUB3 ) where import Codec.Archive.Zip (Entry, addEntryToArchive, eRelativePath, emptyArchive, fromArchive, fromEntry, toEntry) -import Control.Monad (mplus, when, unless, zipWithM) +import Control.Monad (mplus, unless, when, zipWithM) import Control.Monad.Except (catchError, throwError) -import Control.Monad.State.Strict (State, StateT, evalState, evalStateT, get, gets, - lift, modify, put) +import Control.Monad.State.Strict (State, StateT, evalState, evalStateT, get, + gets, lift, modify, put) import qualified Data.ByteString.Lazy as B import qualified Data.ByteString.Lazy.Char8 as B8 -import qualified Data.Text.Lazy as TL -import qualified Data.Text as TS -import Data.Char (isAlphaNum, isDigit, toLower, isAscii) +import Data.Char (isAlphaNum, isAscii, isDigit, toLower) import Data.List (intercalate, isInfixOf, isPrefixOf) import qualified Data.Map as M import Data.Maybe (catMaybes, fromMaybe) import qualified Data.Set as Set +import qualified Data.Text as TS +import qualified Data.Text.Lazy as TL import Network.HTTP (urlEncode) import System.FilePath (takeExtension, takeFileName) import Text.HTML.TagSoup (Tag (TagOpen), fromAttrib, parseTags) @@ -71,7 +71,7 @@ import Text.Pandoc.Writers.HTML (writeHtmlStringForEPUB) import Text.Printf (printf) import Text.XML.Light (Attr (..), Element (..), Node (..), QName (..), add_attrs, lookupAttr, node, onlyElems, parseXML, - ppElement, strContent, unode, unqual, showElement) + ppElement, showElement, strContent, unode, unqual) -- A Chapter includes a list of blocks and maybe a section -- number offset. Note, some chapters are unnumbered. The section @@ -709,7 +709,7 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do Right x -> x -- can't have a element inside a... delink (Link _ ils _) = Span ("", [], []) ils - delink x = x + delink x = x let navtag = if epub3 then "nav" else "div" tocBlocks <- lift $ evalStateT (mapM (navPointNode navXhtmlFormatter) secs) 1 diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index 9cb9098de..b7dc43685 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -37,15 +37,15 @@ FictionBook is an XML-based e-book format. For more information see: -} module Text.Pandoc.Writers.FB2 (writeFB2) where -import Control.Monad.Except (catchError) -import Control.Monad.State.Strict (StateT, evalStateT, get, lift, modify, liftM) import Control.Monad (zipWithM) +import Control.Monad.Except (catchError) +import Control.Monad.State.Strict (StateT, evalStateT, get, lift, liftM, modify) import Data.ByteString.Base64 (encode) import qualified Data.ByteString.Char8 as B8 import Data.Char (isAscii, isControl, isSpace, toLower) import Data.Either (lefts, rights) -import Data.Text (Text, pack) import Data.List (intercalate, intersperse, isPrefixOf, stripPrefix) +import Data.Text (Text, pack) import Network.HTTP (urlEncode) import Text.XML.Light import qualified Text.XML.Light as X @@ -56,8 +56,7 @@ import qualified Text.Pandoc.Class as P import Text.Pandoc.Definition import Text.Pandoc.Logging import Text.Pandoc.Options (HTMLMathMethod (..), WriterOptions (..), def) -import Text.Pandoc.Shared (capitalize, isHeaderBlock, isURI, - orderedListMarkers) +import Text.Pandoc.Shared (capitalize, isHeaderBlock, isURI, orderedListMarkers) -- | Data to be written at the end of the document: -- (foot)notes, URLs, references, images. diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index f197bceb2..9cb3aced8 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -45,13 +45,13 @@ module Text.Pandoc.Writers.HTML ( ) where import Control.Monad.State.Strict import Data.Char (ord, toLower) -import Data.Text (Text) -import qualified Data.Text.Lazy as TL -import Data.List (intersperse, isPrefixOf, partition, intercalate) +import Data.List (intercalate, intersperse, isPrefixOf, partition) import Data.Maybe (catMaybes, fromMaybe, isJust, isNothing) import Data.Monoid ((<>)) import qualified Data.Set as Set import Data.String (fromString) +import Data.Text (Text) +import qualified Data.Text.Lazy as TL import Network.HTTP (urlEncode) import Network.URI (URI (..), parseURIReference, unEscapeString) import Numeric (showHex) @@ -79,7 +79,7 @@ import qualified Text.Blaze.Html5 as H5 #endif import Control.Monad.Except (throwError) import Data.Aeson (Value) -import System.FilePath (takeExtension, takeBaseName) +import System.FilePath (takeBaseName, takeExtension) import Text.Blaze.Html.Renderer.Text (renderHtml) import qualified Text.Blaze.XHtml1.Transitional as H import qualified Text.Blaze.XHtml1.Transitional.Attributes as A @@ -582,14 +582,14 @@ dimensionsToAttrList attr = consolidateStyles $ go Width ++ go Height consolidateStyles :: [(String, String)] -> [(String, String)] consolidateStyles xs = case partition isStyle xs of - ([], _) -> xs + ([], _) -> xs (ss, rest) -> ("style", intercalate ";" $ map snd ss) : rest isStyle ("style", _) = True isStyle _ = False go dir = case (dimension dir attr) of - (Just (Pixel a)) -> [(show dir, show a)] - (Just x) -> [("style", show dir ++ ":" ++ show x)] - Nothing -> [] + (Just (Pixel a)) -> [(show dir, show a)] + (Just x) -> [("style", show dir ++ ":" ++ show x)] + Nothing -> [] imageExts :: [String] imageExts = [ "art", "bmp", "cdr", "cdt", "cpt", "cr2", "crw", "djvu", "erf", diff --git a/src/Text/Pandoc/Writers/Haddock.hs b/src/Text/Pandoc/Writers/Haddock.hs index d1146ca73..c964ddf74 100644 --- a/src/Text/Pandoc/Writers/Haddock.hs +++ b/src/Text/Pandoc/Writers/Haddock.hs @@ -35,8 +35,8 @@ Haddock: <http://www.haskell.org/haddock/doc/html/> module Text.Pandoc.Writers.Haddock (writeHaddock) where import Control.Monad.State.Strict import Data.Default -import Data.Text (Text) import Data.List (intersperse, transpose) +import Data.Text (Text) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.Logging diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs index 012ff8416..4efd00ee5 100644 --- a/src/Text/Pandoc/Writers/JATS.hs +++ b/src/Text/Pandoc/Writers/JATS.hs @@ -33,15 +33,16 @@ https://jats.nlm.nih.gov/publishing/tag-library/1.1d3/element/mml-math.html module Text.Pandoc.Writers.JATS ( writeJATS ) where import Control.Monad.Reader import Data.Char (toLower) -import Data.Text (Text) import Data.Generics (everywhere, mkT) import Data.List (intercalate, isSuffixOf, partition) import Data.Maybe (fromMaybe) +import Data.Text (Text) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.Highlighting (languages, languagesByExtension) import Text.Pandoc.Logging +import Text.Pandoc.MIME (getMimeType) import Text.Pandoc.Options import Text.Pandoc.Pretty import Text.Pandoc.Shared @@ -49,7 +50,6 @@ import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Writers.Math import Text.Pandoc.Writers.Shared import Text.Pandoc.XML -import Text.Pandoc.MIME (getMimeType) import Text.TeXMath import qualified Text.XML.Light as Xml @@ -90,7 +90,7 @@ writeJATS opts d = docToJATS :: PandocMonad m => WriterOptions -> Pandoc -> DB m Text docToJATS opts (Pandoc meta blocks) = do let isBackBlock (Div ("refs",_,_) _) = True - isBackBlock _ = False + isBackBlock _ = False let (backblocks, bodyblocks) = partition isBackBlock blocks let elements = hierarchicalize bodyblocks let backElements = hierarchicalize backblocks diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 282910ee5..1a36f987b 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -45,7 +45,7 @@ import Data.Maybe (catMaybes, fromMaybe, isJust) import Data.Text (Text) import qualified Data.Text as T import Network.URI (unEscapeString) -import Text.Pandoc.BCP47 (Lang(..), getLang, renderLang) +import Text.Pandoc.BCP47 (Lang (..), getLang, renderLang) import Text.Pandoc.Class (PandocMonad, report, toLang) import Text.Pandoc.Definition import Text.Pandoc.Highlighting (formatLaTeXBlock, formatLaTeXInline, highlight, @@ -1001,7 +1001,7 @@ inlineToLaTeX (Code (_,classes,_) str) = do inItem <- gets stInItem let listingsCode = do let listingsopt = case getListingsLanguage classes of - Just l -> "[language=" ++ mbBraced l ++ "]" + Just l -> "[language=" ++ mbBraced l ++ "]" Nothing -> "" inNote <- gets stInNote when inNote $ modify $ \s -> s{ stVerbInNote = True } diff --git a/src/Text/Pandoc/Writers/Man.hs b/src/Text/Pandoc/Writers/Man.hs index 8adb3e7eb..40c0dd815 100644 --- a/src/Text/Pandoc/Writers/Man.hs +++ b/src/Text/Pandoc/Writers/Man.hs @@ -31,7 +31,7 @@ Conversion of 'Pandoc' documents to groff man page format. -} module Text.Pandoc.Writers.Man ( writeMan) where import Control.Monad.State.Strict -import Data.List (intercalate, intersperse, stripPrefix, sort) +import Data.List (intercalate, intersperse, sort, stripPrefix) import qualified Data.Map as Map import Data.Maybe (fromMaybe) import Data.Text (Text) diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index a54f4eb85..5d812b169 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -39,8 +39,8 @@ import Control.Monad.State.Strict import Data.Char (chr, isPunctuation, isSpace, ord) import Data.Default import qualified Data.HashMap.Strict as H -import qualified Data.Map as M import Data.List (find, group, intersperse, sortBy, stripPrefix, transpose) +import qualified Data.Map as M import Data.Maybe (fromMaybe) import Data.Monoid (Any (..)) import Data.Ord (comparing) @@ -994,7 +994,7 @@ inlineToMarkdown opts (Superscript lst) = else let rendered = render Nothing contents in case mapM toSuperscript rendered of - Just r -> text r + Just r -> text r Nothing -> text $ "^(" ++ rendered ++ ")" inlineToMarkdown _ (Subscript []) = return empty inlineToMarkdown opts (Subscript lst) = @@ -1007,7 +1007,7 @@ inlineToMarkdown opts (Subscript lst) = else let rendered = render Nothing contents in case mapM toSubscript rendered of - Just r -> text r + Just r -> text r Nothing -> text $ "_(" ++ rendered ++ ")" inlineToMarkdown opts (SmallCaps lst) = do plain <- asks envPlain diff --git a/src/Text/Pandoc/Writers/MediaWiki.hs b/src/Text/Pandoc/Writers/MediaWiki.hs index 58d1b0707..839f16cea 100644 --- a/src/Text/Pandoc/Writers/MediaWiki.hs +++ b/src/Text/Pandoc/Writers/MediaWiki.hs @@ -36,9 +36,9 @@ import Data.List (intercalate) import qualified Data.Set as Set import Data.Text (Text, pack) import Text.Pandoc.Class (PandocMonad, report) -import Text.Pandoc.Logging import Text.Pandoc.Definition import Text.Pandoc.ImageSize +import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Pretty (render) import Text.Pandoc.Shared diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 493da1545..68c0d6096 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -36,29 +36,29 @@ TODO: -} module Text.Pandoc.Writers.Ms ( writeMs ) where -import Text.Pandoc.Definition -import Text.Pandoc.Templates -import Text.Pandoc.Shared -import Text.Pandoc.Writers.Shared -import Text.Pandoc.Options -import Text.Pandoc.Writers.Math -import Text.Printf ( printf ) -import qualified Data.Text as T -import Data.Text (Text) +import Control.Monad.State.Strict +import Data.Char (isLower, isUpper, toUpper) +import Data.List (intercalate, intersperse, sort) import qualified Data.Map as Map -import Data.Maybe ( catMaybes, fromMaybe ) -import Data.List ( intersperse, intercalate, sort ) -import Text.Pandoc.Pretty +import Data.Maybe (catMaybes, fromMaybe) +import Data.Text (Text) +import qualified Data.Text as T +import Network.URI (escapeURIString, isAllowedInURI) +import Skylighting +import System.FilePath (takeExtension) import Text.Pandoc.Class (PandocMonad, report) +import Text.Pandoc.Definition +import Text.Pandoc.Highlighting import Text.Pandoc.ImageSize import Text.Pandoc.Logging -import Control.Monad.State.Strict -import Data.Char ( isLower, isUpper, toUpper ) +import Text.Pandoc.Options +import Text.Pandoc.Pretty +import Text.Pandoc.Shared +import Text.Pandoc.Templates +import Text.Pandoc.Writers.Math +import Text.Pandoc.Writers.Shared +import Text.Printf (printf) import Text.TeXMath (writeEqn) -import System.FilePath (takeExtension) -import Skylighting -import Text.Pandoc.Highlighting -import Network.URI (escapeURIString, isAllowedInURI) data WriterState = WriterState { stHasInlineMath :: Bool , stFirstPara :: Bool @@ -147,7 +147,7 @@ msEscapes = Map.fromList $ escapeChar :: Char -> String escapeChar c = case Map.lookup c msEscapes of - Just s -> s + Just s -> s Nothing -> [c] -- | Escape | character, used to mark inline math, inside math. @@ -176,13 +176,13 @@ toSmallCaps (c:cs) -- | Escape a literal (code) section for Ms. escapeCode :: String -> String escapeCode = concat . intersperse "\n" . map escapeLine . lines - where escapeCodeChar ' ' = "\\ " + where escapeCodeChar ' ' = "\\ " escapeCodeChar '\t' = "\\\t" - escapeCodeChar c = escapeChar c + escapeCodeChar c = escapeChar c escapeLine codeline = case concatMap escapeCodeChar codeline of a@('.':_) -> "\\&" ++ a - b -> b + b -> b -- We split inline lists into sentences, and print one sentence per -- line. groff/troff treats the line-ending period differently. @@ -194,8 +194,8 @@ breakSentence [] = ([],[]) breakSentence xs = let isSentenceEndInline (Str ys@(_:_)) | last ys == '.' = True isSentenceEndInline (Str ys@(_:_)) | last ys == '?' = True - isSentenceEndInline (LineBreak) = True - isSentenceEndInline _ = False + isSentenceEndInline (LineBreak) = True + isSentenceEndInline _ = False (as, bs) = break isSentenceEndInline xs in case bs of [] -> (as, []) @@ -548,7 +548,7 @@ handleNote opts bs = do -- line after the note ref: let bs' = case bs of (Para ils : rest) -> Plain ils : rest - _ -> bs + _ -> bs contents <- blockListToMs opts bs' return $ cr <> text ".FS" $$ contents $$ text ".FE" <> cr diff --git a/src/Text/Pandoc/Writers/Native.hs b/src/Text/Pandoc/Writers/Native.hs index 3ef33f05c..c934fe4d9 100644 --- a/src/Text/Pandoc/Writers/Native.hs +++ b/src/Text/Pandoc/Writers/Native.hs @@ -30,8 +30,8 @@ Conversion of a 'Pandoc' document to a string representation. -} module Text.Pandoc.Writers.Native ( writeNative ) where -import Data.Text (Text) import Data.List (intersperse) +import Data.Text (Text) import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Options (WrapOption (..), WriterOptions (..)) diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index 90b7c3501..32fcb0292 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -38,6 +38,7 @@ import Data.List (isPrefixOf) import Data.Maybe (fromMaybe) import qualified Data.Text.Lazy as TL import System.FilePath (takeDirectory, takeExtension, (<.>)) +import Text.Pandoc.BCP47 (Lang (..), getLang, renderLang) import Text.Pandoc.Class (PandocMonad, report, toLang) import qualified Text.Pandoc.Class as P import Text.Pandoc.Definition @@ -51,7 +52,6 @@ import Text.Pandoc.UTF8 (fromStringLazy, fromTextLazy, toStringLazy) import Text.Pandoc.Walk import Text.Pandoc.Writers.OpenDocument (writeOpenDocument) import Text.Pandoc.Writers.Shared (fixDisplayMath) -import Text.Pandoc.BCP47 (getLang, Lang(..), renderLang) import Text.Pandoc.XML import Text.TeXMath import Text.XML.Light diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index ed3dabb87..04cae0b4b 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -36,10 +36,11 @@ import Control.Arrow ((***), (>>>)) import Control.Monad.State.Strict hiding (when) import Data.Char (chr) import Data.List (sortBy) -import Data.Text (Text) import qualified Data.Map as Map import Data.Ord (comparing) import qualified Data.Set as Set +import Data.Text (Text) +import Text.Pandoc.BCP47 (Lang (..), parseBCP47) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.Logging @@ -50,7 +51,6 @@ import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Writers.Math import Text.Pandoc.Writers.Shared import Text.Pandoc.XML -import Text.Pandoc.BCP47 (parseBCP47, Lang(..)) import Text.Printf (printf) -- | Auxiliary function to convert Plain block to Para. diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index 88f42acd4..f73822b86 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -37,14 +37,14 @@ Org-Mode: <http://orgmode.org> module Text.Pandoc.Writers.Org (writeOrg) where import Control.Monad.State.Strict import Data.Char (isAlphaNum, toLower) -import Data.Text (Text) import Data.List (intersect, intersperse, isPrefixOf, partition, transpose) +import Data.Text (Text) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Definition +import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Pretty import Text.Pandoc.Shared -import Text.Pandoc.Logging import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Writers.Shared diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index 8599680cf..cfbacdaed 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -38,9 +38,9 @@ import Data.Maybe (fromMaybe) import Data.Text (Text, stripEnd) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad, report) -import Text.Pandoc.Logging import Text.Pandoc.Definition import Text.Pandoc.ImageSize +import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Pretty import Text.Pandoc.Shared @@ -371,12 +371,12 @@ blockToRST' x = do modify (\s -> s{stLastNested = case x of Para [Image _ _ (_,'f':'i':'g':':':_)] -> True - Para{} -> False - Plain{} -> False - Header{} -> False - LineBlock{} -> False - HorizontalRule -> False - _ -> True + Para{} -> False + Plain{} -> False + Header{} -> False + LineBlock{} -> False + HorizontalRule -> False + _ -> True }) blockToRST x diff --git a/src/Text/Pandoc/Writers/RTF.hs b/src/Text/Pandoc/Writers/RTF.hs index d4de3112c..2b05f2f7e 100644 --- a/src/Text/Pandoc/Writers/RTF.hs +++ b/src/Text/Pandoc/Writers/RTF.hs @@ -34,9 +34,9 @@ import Control.Monad.Except (catchError, throwError) import qualified Data.ByteString as B import Data.Char (chr, isDigit, ord) import Data.List (intercalate, isSuffixOf) +import qualified Data.Map as M import Data.Text (Text) import qualified Data.Text as T -import qualified Data.Map as M import Text.Pandoc.Class (PandocMonad, report) import qualified Text.Pandoc.Class as P import Text.Pandoc.Definition diff --git a/src/Text/Pandoc/Writers/TEI.hs b/src/Text/Pandoc/Writers/TEI.hs index 26070966e..dfdb443a2 100644 --- a/src/Text/Pandoc/Writers/TEI.hs +++ b/src/Text/Pandoc/Writers/TEI.hs @@ -31,14 +31,14 @@ Conversion of 'Pandoc' documents to Docbook XML. -} module Text.Pandoc.Writers.TEI (writeTEI) where import Data.Char (toLower) -import Data.Text (Text) import Data.List (isPrefixOf, stripPrefix) +import Data.Text (Text) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad, report) -import Text.Pandoc.Logging import Text.Pandoc.Definition import Text.Pandoc.Highlighting (languages, languagesByExtension) import Text.Pandoc.ImageSize +import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Pretty import Text.Pandoc.Shared diff --git a/src/Text/Pandoc/Writers/Textile.hs b/src/Text/Pandoc/Writers/Textile.hs index acc9eaa0f..5ee9d3250 100644 --- a/src/Text/Pandoc/Writers/Textile.hs +++ b/src/Text/Pandoc/Writers/Textile.hs @@ -35,9 +35,9 @@ import Data.Char (isSpace) import Data.List (intercalate) import Data.Text (Text, pack) import Text.Pandoc.Class (PandocMonad, report) -import Text.Pandoc.Logging import Text.Pandoc.Definition import Text.Pandoc.ImageSize +import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Pretty (render) import Text.Pandoc.Shared diff --git a/src/Text/Pandoc/Writers/ZimWiki.hs b/src/Text/Pandoc/Writers/ZimWiki.hs index ced02d4be..67dcd72d1 100644 --- a/src/Text/Pandoc/Writers/ZimWiki.hs +++ b/src/Text/Pandoc/Writers/ZimWiki.hs @@ -37,13 +37,13 @@ import Control.Monad.State.Strict (StateT, evalStateT, gets, modify) import Data.Default (Default (..)) import Data.List (intercalate, isInfixOf, isPrefixOf, transpose) import qualified Data.Map as Map -import Data.Text (breakOnAll, pack, Text) +import Data.Text (Text, breakOnAll, pack) import Text.Pandoc.Class (PandocMonad, report) -import Text.Pandoc.Logging import Text.Pandoc.Definition import Text.Pandoc.ImageSize +import Text.Pandoc.Logging import Text.Pandoc.Options (WrapOption (..), WriterOptions (writerTableOfContents, writerTemplate, writerWrapText)) -import Text.Pandoc.Shared (isURI, escapeURI, linesToPara, removeFormatting, +import Text.Pandoc.Shared (escapeURI, isURI, linesToPara, removeFormatting, substitute, trimr) import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Writers.Shared (defField, metaToJSON) -- cgit v1.2.3 From e77d08e439e727111b05a832531fdf86d03d2436 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 27 Oct 2017 21:07:47 -0700 Subject: hlint suggestions. --- src/Text/Pandoc/BCP47.hs | 2 +- src/Text/Pandoc/CSV.hs | 2 +- src/Text/Pandoc/Class.hs | 9 ++++----- src/Text/Pandoc/Highlighting.hs | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/BCP47.hs b/src/Text/Pandoc/BCP47.hs index d49a0c115..0f1421555 100644 --- a/src/Text/Pandoc/BCP47.hs +++ b/src/Text/Pandoc/BCP47.hs @@ -120,6 +120,6 @@ parseBCP47 lang = P.char 'x' P.char '-' cs <- P.many1 $ P.satisfy (\c -> isAscii c && isAlphaNum c) - guard $ length cs >= 1 && length cs <= 8 + guard $ not (null cs) && length cs <= 8 let var = "x-" ++ cs return $ map toLower var diff --git a/src/Text/Pandoc/CSV.hs b/src/Text/Pandoc/CSV.hs index ee68d988b..924052dca 100644 --- a/src/Text/Pandoc/CSV.hs +++ b/src/Text/Pandoc/CSV.hs @@ -79,7 +79,7 @@ pCSVQuotedCell opts = do return $ T.pack res escaped :: CSVOptions -> Parser Char -escaped opts = do +escaped opts = case csvEscape opts of Nothing -> try $ char (csvQuote opts) >> char (csvQuote opts) Just c -> try $ char c >> noneOf "\r\n" diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index c98a6411d..592ec11e5 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -121,10 +121,9 @@ import Data.ByteString.Base64 (decodeLenient) import Network.URI ( escapeURIString, nonStrictRelativeTo, unEscapeString, parseURIReference, isAllowedInURI, parseURI, URI(..) ) -import Network.HTTP.Client (httpLbs, responseBody, responseHeaders, - Request(port,host,requestHeaders)) -import Network.HTTP.Client (parseRequest) -import Network.HTTP.Client (newManager) +import Network.HTTP.Client + (httpLbs, responseBody, responseHeaders, + Request(port, host, requestHeaders), parseRequest, newManager) import Network.HTTP.Client.Internal (addProxy) import Network.HTTP.Client.TLS (tlsManagerSettings) import System.Environment (getEnv) @@ -432,7 +431,7 @@ getTranslations = do report $ CouldNotLoadTranslations (renderLang lang) $ case e of PandocCouldNotFindDataFileError _ -> - ("data file " ++ fallbackFile ++ " not found") + "data file " ++ fallbackFile ++ " not found" _ -> "" -- make sure we don't try again... modifyCommonState $ \st -> st{ stTranslations = Nothing } diff --git a/src/Text/Pandoc/Highlighting.hs b/src/Text/Pandoc/Highlighting.hs index 0754aae4c..cc72967e4 100644 --- a/src/Text/Pandoc/Highlighting.hs +++ b/src/Text/Pandoc/Highlighting.hs @@ -91,7 +91,7 @@ highlight syntaxmap formatter (_, classes, keyvals) rawCode = , traceOutput = False } classes' = map T.pack classes rawCode' = T.pack rawCode - in case msum (map ((`lookupSyntax` syntaxmap)) classes') of + in case msum (map (`lookupSyntax` syntaxmap) classes') of Nothing | numberLines fmtOpts -> Right $ formatter fmtOpts{ codeClasses = [], -- cgit v1.2.3 From be7a29e9b95bb8ffa77c3ffeb49e7cf6ba164ed4 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 27 Oct 2017 21:11:26 -0700 Subject: hlint suggestions. --- src/Text/Pandoc/App.hs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index ed9992911..e965771b7 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -112,8 +112,9 @@ parseOptions options' defaults = do let (actions, args, unrecognizedOpts, errors) = getOpt' Permute options' rawArgs - let unknownOptionErrors = foldr handleUnrecognizedOption [] $ - map (takeWhile (/= '=')) unrecognizedOpts + let unknownOptionErrors = + foldr (handleUnrecognizedOption . (takeWhile (/= '='))) [] + unrecognizedOpts unless (null errors && null unknownOptionErrors) $ E.throwIO $ PandocOptionError $ @@ -205,12 +206,10 @@ convertWithOpts opts = do Just _ -> return $ optDataDir opts -- assign reader and writer based on options and filenames - let readerName = case optReader opts of - Nothing -> defaultReaderName - (if any isURI sources - then "html" - else "markdown") sources - Just x -> x + let readerName = fromMaybe ( defaultReaderName + (if any isURI sources + then "html" + else "markdown") sources) (optReader opts) let nonPdfWriterName Nothing = defaultWriterName outputFile nonPdfWriterName (Just x) = x @@ -286,7 +285,7 @@ convertWithOpts opts = do #else istty <- queryTerminal stdOutput #endif - when (not (isTextFormat format) && istty && optOutputFile opts == Nothing) $ + when (not (isTextFormat format) && istty && isNothing ( optOutputFile opts)) $ E.throwIO $ PandocAppError $ "Cannot write " ++ format ++ " output to terminal.\n" ++ "Specify an output file using the -o option, or " ++ -- cgit v1.2.3 From f3e901c29d9a1ca82a1b35ea13df4e673e753443 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 27 Oct 2017 21:26:16 -0700 Subject: hlint suggestions. --- src/Text/Pandoc/App.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index e965771b7..a80c6ac44 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -113,7 +113,7 @@ parseOptions options' defaults = do getOpt' Permute options' rawArgs let unknownOptionErrors = - foldr (handleUnrecognizedOption . (takeWhile (/= '='))) [] + foldr (handleUnrecognizedOption . takeWhile (/= '=')) [] unrecognizedOpts unless (null errors && null unknownOptionErrors) $ -- cgit v1.2.3 From b201a8aa582e1146243796fac26e57579af55f5f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 27 Oct 2017 21:29:22 -0700 Subject: hlint changes. --- src/Text/Pandoc/MediaBag.hs | 2 +- src/Text/Pandoc/PDF.hs | 8 ++-- src/Text/Pandoc/Pretty.hs | 36 ++++++++--------- src/Text/Pandoc/Readers/CommonMark.hs | 4 +- src/Text/Pandoc/Readers/Creole.hs | 36 ++++++++--------- src/Text/Pandoc/Readers/DocBook.hs | 40 ++++++++++--------- src/Text/Pandoc/Readers/Docx/Combine.hs | 24 ++++++------ src/Text/Pandoc/Readers/Docx/Lists.hs | 68 +++++++++++++++------------------ src/Text/Pandoc/Readers/Docx/Parse.hs | 26 ++++++------- 9 files changed, 118 insertions(+), 126 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/MediaBag.hs b/src/Text/Pandoc/MediaBag.hs index f89c60c9e..1c15d1cee 100644 --- a/src/Text/Pandoc/MediaBag.hs +++ b/src/Text/Pandoc/MediaBag.hs @@ -80,4 +80,4 @@ lookupMedia fp (MediaBag mediamap) = M.lookup (splitDirectories fp) mediamap mediaDirectory :: MediaBag -> [(String, MimeType, Int)] mediaDirectory (MediaBag mediamap) = M.foldrWithKey (\fp (mime,contents) -> - (((Posix.joinPath fp), mime, fromIntegral $ BL.length contents):)) [] mediamap + ((Posix.joinPath fp, mime, fromIntegral $ BL.length contents):)) [] mediamap diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index 028d63dcb..cafb4a226 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -327,7 +327,7 @@ ms2pdf verbosity args source = do putStrLn "[makePDF] Environment:" mapM_ print env' putStr "\n" - putStrLn $ "[makePDF] Contents:\n" + putStrLn "[makePDF] Contents:\n" putStr $ T.unpack source putStr "\n" (exit, out) <- E.catch @@ -351,9 +351,7 @@ html2pdf :: Verbosity -- ^ Verbosity level -> IO (Either ByteString ByteString) html2pdf verbosity program args source = do pdfFile <- withTempFile "." "html2pdf.pdf" $ \fp _ -> return fp - let pdfFileArgName = if program == "prince" - then ["-o"] - else [] + let pdfFileArgName = ["-o" | program == "prince"] let programArgs = args ++ ["-"] ++ pdfFileArgName ++ [pdfFile] env' <- getEnvironment when (verbosity >= INFO) $ do @@ -363,7 +361,7 @@ html2pdf verbosity program args source = do putStrLn "[makePDF] Environment:" mapM_ print env' putStr "\n" - putStrLn $ "[makePDF] Contents of intermediate HTML:" + putStrLn "[makePDF] Contents of intermediate HTML:" TextIO.putStr source putStr "\n" (exit, out) <- E.catch diff --git a/src/Text/Pandoc/Pretty.hs b/src/Text/Pandoc/Pretty.hs index b5600ad39..40a7d018c 100644 --- a/src/Text/Pandoc/Pretty.hs +++ b/src/Text/Pandoc/Pretty.hs @@ -78,6 +78,7 @@ module Text.Pandoc.Pretty ( where import Control.Monad.State.Strict +import Control.Monad (when) import Data.Char (isSpace) import Data.Foldable (toList) import Data.List (intersperse) @@ -144,11 +145,10 @@ hcat = mconcat -- between them. infixr 6 <+> (<+>) :: Doc -> Doc -> Doc -(<+>) x y = if isEmpty x - then y - else if isEmpty y - then x - else x <> space <> y +(<+>) x y + | isEmpty x = y + | isEmpty y = x + | otherwise = x <> space <> y -- | Same as 'cat', but putting breakable spaces between the -- 'Doc's. @@ -158,20 +158,18 @@ hsep = foldr (<+>) empty infixr 5 $$ -- | @a $$ b@ puts @a@ above @b@. ($$) :: Doc -> Doc -> Doc -($$) x y = if isEmpty x - then y - else if isEmpty y - then x - else x <> cr <> y +($$) x y + | isEmpty x = y + | isEmpty y = x + | otherwise = x <> cr <> y infixr 5 $+$ -- | @a $+$ b@ puts @a@ above @b@, with a blank line between. ($+$) :: Doc -> Doc -> Doc -($+$) x y = if isEmpty x - then y - else if isEmpty y - then x - else x <> blankline <> y +($+$) x y + | isEmpty x = y + | isEmpty y = x + | otherwise = x <> blankline <> y -- | List version of '$$'. vcat :: [Doc] -> Doc @@ -217,9 +215,9 @@ outp off s | off < 0 = do -- offset < 0 means newline characters outp off s = do -- offset >= 0 (0 might be combining char) st' <- get let pref = prefix st' - when (column st' == 0 && usePrefix st' && not (null pref)) $ do + when (column st' == 0 && usePrefix st' && not (null pref)) $ modify $ \st -> st{ output = fromString pref : output st - , column = column st + realLength pref } + , column = column st + realLength pref } modify $ \st -> st{ output = fromString s : output st , column = column st + off , newlines = 0 } @@ -328,9 +326,7 @@ renderList (BreakingSpace : xs) = do renderList (AfterBreak s : xs) = do st <- get - if newlines st > 0 - then outp (realLength s) s - else return () + when (newlines st > 0) $ outp (realLength s) s renderList xs renderList (Block i1 s1 : Block i2 s2 : xs) = diff --git a/src/Text/Pandoc/Readers/CommonMark.hs b/src/Text/Pandoc/Readers/CommonMark.hs index 8189e7760..6b864521f 100644 --- a/src/Text/Pandoc/Readers/CommonMark.hs +++ b/src/Text/Pandoc/Readers/CommonMark.hs @@ -132,12 +132,12 @@ addBlock opts (Node _ (LIST listAttrs) nodes) = setTightness = if listTight listAttrs then map paraToPlain else id - paraToPlain (Para xs) = Plain (xs) + paraToPlain (Para xs) = Plain xs paraToPlain x = x delim = case listDelim listAttrs of PERIOD_DELIM -> Period PAREN_DELIM -> OneParen -addBlock opts (Node _ (TABLE alignments) nodes) = do +addBlock opts (Node _ (TABLE alignments) nodes) = (Table [] aligns widths headers rows :) where aligns = map fromTableCellAlignment alignments fromTableCellAlignment NoAlignment = AlignDefault diff --git a/src/Text/Pandoc/Readers/Creole.hs b/src/Text/Pandoc/Readers/Creole.hs index 9886394a7..3b330e544 100644 --- a/src/Text/Pandoc/Readers/Creole.hs +++ b/src/Text/Pandoc/Readers/Creole.hs @@ -111,7 +111,7 @@ block = do return res nowiki :: PandocMonad m => CRLParser m B.Blocks -nowiki = try $ nowikiStart >> manyTill content nowikiEnd >>= return . B.codeBlock . mconcat +nowiki = try $ fmap (B.codeBlock . mconcat) (nowikiStart >> manyTill content nowikiEnd) where content = brackets <|> line brackets = try $ option "" ((:[]) <$> newline) @@ -124,7 +124,8 @@ nowiki = try $ nowikiStart >> manyTill content nowikiEnd >>= return . B.codeBloc header :: PandocMonad m => CRLParser m B.Blocks header = try $ do skipSpaces - level <- many1 (char '=') >>= return . length + level <- + fmap length (many1 (char '=')) guard $ level <= 6 skipSpaces content <- B.str <$> manyTill (noneOf "\n") headerEnd @@ -145,16 +146,16 @@ anyListItem :: PandocMonad m => Int -> CRLParser m B.Blocks anyListItem n = listItem '*' n <|> listItem '#' n list :: PandocMonad m => Char -> ([B.Blocks] -> B.Blocks) -> Int -> CRLParser m B.Blocks -list c f n = many1 (itemPlusSublist <|> listItem c n) - >>= return . f +list c f n = + fmap f (many1 (itemPlusSublist <|> listItem c n)) where itemPlusSublist = try $ listItem c n <+> anyList (n+1) listItem :: PandocMonad m => Char -> Int -> CRLParser m B.Blocks -listItem c n = (listStart >> many1Till inline itemEnd) - >>= return . B.plain . B.trimInlines .mconcat +listItem c n = + fmap (B.plain . B.trimInlines .mconcat) (listStart >> many1Till inline itemEnd) where listStart = try $ optional newline >> skipSpaces >> count n (char c) - >> (lookAhead $ noneOf [c]) >> skipSpaces + >> lookAhead (noneOf [c]) >> skipSpaces itemEnd = endOfParaElement <|> nextItem n <|> if n < 3 then nextItem (n+1) else nextItem (n+1) <|> nextItem (n-1) @@ -176,7 +177,7 @@ table = try $ do cellEnd = lookAhead $ try $ char '|' <|> rowEnd para :: PandocMonad m => CRLParser m B.Blocks -para = many1Till inline endOfParaElement >>= return . result . mconcat +para = fmap (result . mconcat) (many1Till inline endOfParaElement) where result content = if F.all (==Space) content then mempty @@ -192,7 +193,7 @@ endOfParaElement = lookAhead $ endOfInput <|> endOfPara startOf :: PandocMonad m => CRLParser m a -> CRLParser m () startOf p = try $ blankline >> p >> return mempty startOfList = startOf $ anyList 1 - startOfTable = startOf $ table + startOfTable =startOf table startOfHeader = startOf header startOfNowiki = startOf nowiki hr = startOf horizontalRule @@ -223,7 +224,8 @@ inline = choice [ whitespace ] <?> "inline" escapedChar :: PandocMonad m => CRLParser m B.Inlines -escapedChar = (try $ char '~' >> noneOf "\t\n ") >>= return . B.str . (:[]) +escapedChar = + fmap (B.str . (:[])) (try $ char '~' >> noneOf "\t\n ") escapedLink :: PandocMonad m => CRLParser m B.Inlines escapedLink = try $ do @@ -234,7 +236,7 @@ escapedLink = try $ do image :: PandocMonad m => CRLParser m B.Inlines image = try $ do (orig, src) <- wikiImg - return $ B.image src "" (B.str $ orig) + return $ B.image src "" (B.str orig) where linkSrc = many $ noneOf "|}\n\r\t" linkDsc = char '|' >> many (noneOf "}\n\r\t") @@ -253,7 +255,7 @@ link = try $ do linkSrc = many $ noneOf "|]\n\r\t" linkDsc :: PandocMonad m => String -> CRLParser m B.Inlines linkDsc otxt = B.str - <$> (try $ option otxt + <$> try (option otxt (char '|' >> many (noneOf "]\n\r\t"))) linkImg = try $ char '|' >> image wikiLink = try $ do @@ -270,17 +272,17 @@ inlineNowiki :: PandocMonad m => CRLParser m B.Inlines inlineNowiki = B.code <$> (start >> manyTill (noneOf "\n\r") end) where start = try $ string "{{{" - end = try $ string "}}}" >> (lookAhead $ noneOf "}") + end = try $ string "}}}" >> lookAhead (noneOf "}") placeholder :: PandocMonad m => CRLParser m B.Inlines -- The semantics of the placeholder is basicallly implementation -- dependent, so there is no way to DTRT for all cases. -- So for now we just drop them. -placeholder = B.text <$> (try $ string "<<<" >> manyTill anyChar (string ">>>") +placeholder = B.text <$> try (string "<<<" >> manyTill anyChar (string ">>>") >> return "") whitespace :: PandocMonad m => CRLParser m B.Inlines -whitespace = (lb <|> regsp) >>= return +whitespace = (lb <|> regsp) where lb = try $ skipMany spaceChar >> linebreak >> return B.space regsp = try $ skipMany1 spaceChar >> return B.space @@ -290,11 +292,11 @@ linebreak = newline >> notFollowedBy newline >> (lastNewline <|> innerNewline) innerNewline = return B.space symbol :: PandocMonad m => CRLParser m B.Inlines -symbol = oneOf specialChars >>= return . B.str . (:[]) +symbol = fmap (B.str . (:[])) (oneOf specialChars) str :: PandocMonad m => CRLParser m B.Inlines str = let strChar = noneOf ("\t\n " ++ specialChars) in - many1 strChar >>= return . B.str + fmap B.str (many1 strChar) bold :: PandocMonad m => CRLParser m B.Inlines bold = B.strong . mconcat <$> diff --git a/src/Text/Pandoc/Readers/DocBook.hs b/src/Text/Pandoc/Readers/DocBook.hs index f816a9c47..0f3f6f6e3 100644 --- a/src/Text/Pandoc/Readers/DocBook.hs +++ b/src/Text/Pandoc/Readers/DocBook.hs @@ -1,5 +1,5 @@ module Text.Pandoc.Readers.DocBook ( readDocBook ) where -import Data.Char (toUpper) +import Data.Char (toUpper, isSpace) import Text.Pandoc.Shared (safeRead, crFilter) import Text.Pandoc.Options import Text.Pandoc.Definition @@ -8,7 +8,6 @@ import Text.XML.Light import Text.HTML.TagSoup.Entity (lookupEntity) import Data.Either (rights) import Data.Generics -import Data.Char (isSpace) import Control.Monad.State.Strict import Data.List (intersperse) import Data.Maybe (fromMaybe) @@ -528,7 +527,7 @@ readDocBook :: PandocMonad m => ReaderOptions -> Text -> m Pandoc readDocBook _ inp = do let tree = normalizeTree . parseXML . handleInstructions $ T.unpack $ crFilter inp - (bs, st') <- flip runStateT (def{ dbContent = tree }) $ mapM parseBlock $ tree + (bs, st') <- flip runStateT (def{ dbContent = tree }) $ mapM parseBlock tree return $ Pandoc (dbMeta st') (toList . mconcat $ bs) -- We treat <?asciidoc-br?> specially (issue #1236), converting it @@ -567,14 +566,12 @@ normalizeTree = everywhere (mkT go) go xs = xs convertEntity :: String -> String -convertEntity e = maybe (map toUpper e) id (lookupEntity e) +convertEntity e = Data.Maybe.fromMaybe (map toUpper e) (lookupEntity e) -- convenience function to get an attribute value, defaulting to "" attrValue :: String -> Element -> String attrValue attr elt = - case lookupAttrBy (\x -> qName x == attr) (elAttribs elt) of - Just z -> z - Nothing -> "" + fromMaybe "" (lookupAttrBy (\x -> qName x == attr) (elAttribs elt)) -- convenience function named :: String -> Element -> Bool @@ -654,15 +651,17 @@ getMediaobject e = do || named "textobject" x || named "alt" x) el of Nothing -> return mempty - Just z -> mconcat <$> (mapM parseInline $ elContent z) + Just z -> mconcat <$> + mapM parseInline (elContent z) figTitle <- gets dbFigureTitle let (caption, title) = if isNull figTitle then (getCaption e, "") else (return figTitle, "fig:") - liftM (imageWith attr imageUrl title) caption + fmap (imageWith attr imageUrl title) caption getBlocks :: PandocMonad m => Element -> DB m Blocks -getBlocks e = mconcat <$> (mapM parseBlock $ elContent e) +getBlocks e = mconcat <$> + mapM parseBlock (elContent e) parseBlock :: PandocMonad m => Content -> DB m Blocks @@ -806,7 +805,8 @@ parseBlock (Elem e) = attrib <- case filterChild (named "attribution") e of Nothing -> return mempty Just z -> (para . (str "— " <>) . mconcat) - <$> (mapM parseInline $ elContent z) + <$> + mapM parseInline (elContent z) contents <- getBlocks e return $ blockQuote (contents <> attrib) listitems = mapM getBlocks $ filterChildren (named "listitem") e @@ -906,7 +906,8 @@ parseBlock (Elem e) = metaBlock = acceptingMetadata (getBlocks e) >> return mempty getInlines :: PandocMonad m => Element -> DB m Inlines -getInlines e' = (trimInlines . mconcat) <$> (mapM parseInline $ elContent e') +getInlines e' = (trimInlines . mconcat) <$> + mapM parseInline (elContent e') strContentRecursive :: Element -> String strContentRecursive = strContent . @@ -919,7 +920,7 @@ elementToStr x = x 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 + return $ maybe (text $ map toUpper ref) text $ lookupEntity ref parseInline (Elem e) = case qName (elName e) of "equation" -> equation displayMath @@ -960,8 +961,10 @@ parseInline (Elem e) = "userinput" -> codeWithLang "varargs" -> return $ code "(...)" "keycap" -> return (str $ strContent e) - "keycombo" -> keycombo <$> (mapM parseInline $ elContent e) - "menuchoice" -> menuchoice <$> (mapM parseInline $ + "keycombo" -> keycombo <$> + mapM parseInline (elContent e) + "menuchoice" -> menuchoice <$> + mapM parseInline ( filter isGuiMenu $ elContent e) "xref" -> do content <- dbContent <$> get @@ -980,7 +983,7 @@ parseInline (Elem e) = ils <- innerInlines let href = case findAttr (QName "href" (Just "http://www.w3.org/1999/xlink") Nothing) e of Just h -> h - _ -> ('#' : attrValue "linkend" e) + _ -> '#' : attrValue "linkend" e let ils' = if ils == mempty then str href else ils let attr = (attrValue "id" e, words $ attrValue "role" e, []) return $ linkWith attr href "" ils' @@ -990,7 +993,8 @@ parseInline (Elem e) = "strong" -> strong <$> innerInlines "strikethrough" -> strikeout <$> innerInlines _ -> emph <$> innerInlines - "footnote" -> (note . mconcat) <$> (mapM parseBlock $ elContent e) + "footnote" -> (note . mconcat) <$> + mapM parseBlock (elContent e) "title" -> return mempty "affiliation" -> return mempty -- Note: this isn't a real docbook tag; it's what we convert @@ -999,7 +1003,7 @@ parseInline (Elem e) = "br" -> return linebreak _ -> innerInlines where innerInlines = (trimInlines . mconcat) <$> - (mapM parseInline $ elContent e) + mapM parseInline (elContent e) equation constructor = return $ mconcat $ map (constructor . writeTeX) $ rights diff --git a/src/Text/Pandoc/Readers/Docx/Combine.hs b/src/Text/Pandoc/Readers/Docx/Combine.hs index f516d63d4..003265e6e 100644 --- a/src/Text/Pandoc/Readers/Docx/Combine.hs +++ b/src/Text/Pandoc/Readers/Docx/Combine.hs @@ -36,16 +36,16 @@ spaceOutInlines ils = right = case viewr contents of (_ :> Space) -> space _ -> mempty in - (left, (stackInlines fs $ trimInlines . Many $ contents), right) + (left, stackInlines fs $ trimInlines . Many $ contents, right) stackInlines :: [Modifier Inlines] -> Inlines -> Inlines stackInlines [] ms = ms stackInlines (NullModifier : fs) ms = stackInlines fs ms -stackInlines ((Modifier f) : fs) ms = +stackInlines (Modifier f : fs) ms = if isEmpty ms then stackInlines fs ms else f $ stackInlines fs ms -stackInlines ((AttrModifier f attr) : fs) ms = f attr $ stackInlines fs ms +stackInlines (AttrModifier f attr : fs) ms = f attr $ stackInlines fs ms unstackInlines :: Inlines -> ([Modifier Inlines], Inlines) unstackInlines ms = case ilModifier ms of @@ -97,7 +97,7 @@ combineInlines x y = let (xs', x') = inlinesR x (y', ys') = inlinesL y in - xs' <> (combineSingletonInlines x' y') <> ys' + xs' <> combineSingletonInlines x' y' <> ys' combineSingletonInlines :: Inlines -> Inlines -> Inlines combineSingletonInlines x y = @@ -114,10 +114,10 @@ combineSingletonInlines x y = stackInlines (x_rem_attr ++ y_rem_attr) mempty | isEmpty xs -> let (sp, y') = spaceOutInlinesL y in - (stackInlines x_rem_attr mempty) <> sp <> y' + stackInlines x_rem_attr mempty <> sp <> y' | isEmpty ys -> let (x', sp) = spaceOutInlinesR x in - x' <> sp <> (stackInlines y_rem_attr mempty) + x' <> sp <> stackInlines y_rem_attr mempty | otherwise -> let (x', xsp) = spaceOutInlinesR x (ysp, y') = spaceOutInlinesL y @@ -130,15 +130,15 @@ combineSingletonInlines x y = combineBlocks :: Blocks -> Blocks -> Blocks combineBlocks bs cs - | bs' :> (BlockQuote bs'') <- viewr (unMany bs) - , (BlockQuote cs'') :< cs' <- viewl (unMany cs) = - Many $ (bs' |> (BlockQuote (bs'' <> cs''))) >< cs' + | bs' :> BlockQuote bs'' <- viewr (unMany bs) + , BlockQuote cs'' :< cs' <- viewl (unMany cs) = + Many $ (bs' |> BlockQuote (bs'' <> cs'')) >< cs' combineBlocks bs cs = bs <> cs instance (Monoid a, Eq a) => Eq (Modifier a) where - (Modifier f) == (Modifier g) = (f mempty == g mempty) - (AttrModifier f attr) == (AttrModifier g attr') = (f attr mempty == g attr' mempty) - (NullModifier) == (NullModifier) = True + (Modifier f) == (Modifier g) = f mempty == g mempty + (AttrModifier f attr) == (AttrModifier g attr') = f attr mempty == g attr' mempty + NullModifier == NullModifier = True _ == _ = False isEmpty :: (Monoid a, Eq a) => a -> Bool diff --git a/src/Text/Pandoc/Readers/Docx/Lists.hs b/src/Text/Pandoc/Readers/Docx/Lists.hs index 8be2e1894..c7f4adc98 100644 --- a/src/Text/Pandoc/Readers/Docx/Lists.hs +++ b/src/Text/Pandoc/Readers/Docx/Lists.hs @@ -33,7 +33,6 @@ module Text.Pandoc.Readers.Docx.Lists ( blocksToBullets , listParagraphDivs ) where -import Control.Monad import Data.List import Data.Maybe import Text.Pandoc.Generic (bottomUp) @@ -45,22 +44,18 @@ isListItem (Div (_, classes, _) _) | "list-item" `elem` classes = True isListItem _ = False getLevel :: Block -> Maybe Integer -getLevel (Div (_, _, kvs) _) = liftM read $ lookup "level" kvs +getLevel (Div (_, _, kvs) _) = fmap read $ lookup "level" kvs getLevel _ = Nothing getLevelN :: Block -> Integer -getLevelN b = case getLevel b of - Just n -> n - Nothing -> -1 +getLevelN b = fromMaybe (-1) (getLevel b) getNumId :: Block -> Maybe Integer -getNumId (Div (_, _, kvs) _) = liftM read $ lookup "num-id" kvs +getNumId (Div (_, _, kvs) _) = fmap read $ lookup "num-id" kvs getNumId _ = Nothing getNumIdN :: Block -> Integer -getNumIdN b = case getNumId b of - Just n -> n - Nothing -> -1 +getNumIdN b = fromMaybe (-1) (getNumId b) getText :: Block -> Maybe String getText (Div (_, _, kvs) _) = lookup "text" kvs @@ -109,27 +104,27 @@ listParagraphDivs = ["ListParagraph"] handleListParagraphs :: [Block] -> [Block] handleListParagraphs [] = [] handleListParagraphs ( - (Div attr1@(_, classes1, _) blks1) : - (Div (ident2, classes2, kvs2) blks2) : + Div attr1@(_, classes1, _) blks1 : + Div (ident2, classes2, kvs2) blks2 : blks ) | "list-item" `elem` classes1 && - not ("list-item" `elem` classes2) && + notElem "list-item" classes2 && (not . null) (listParagraphDivs `intersect` classes2) = -- We don't want to keep this indent. let newDiv2 = - (Div (ident2, classes2, filter (\kv -> fst kv /= "indent") kvs2) blks2) + Div (ident2, classes2, filter (\kv -> fst kv /= "indent") kvs2) blks2 in - handleListParagraphs ((Div attr1 (blks1 ++ [newDiv2])) : blks) -handleListParagraphs (blk:blks) = blk : (handleListParagraphs blks) + handleListParagraphs (Div attr1 (blks1 ++ [newDiv2]) : blks) +handleListParagraphs (blk:blks) = blk : handleListParagraphs blks separateBlocks' :: Block -> [[Block]] -> [[Block]] -separateBlocks' blk ([] : []) = [[blk]] -separateBlocks' b@(BulletList _) acc = (init acc) ++ [(last acc) ++ [b]] -separateBlocks' b@(OrderedList _ _) acc = (init acc) ++ [(last acc) ++ [b]] +separateBlocks' blk [[]] = [[blk]] +separateBlocks' b@(BulletList _) acc = init acc ++ [last acc ++ [b]] +separateBlocks' b@(OrderedList _ _) acc = init acc ++ [last acc ++ [b]] -- The following is for the invisible bullet lists. This is how -- pandoc-generated ooxml does multiparagraph item lists. -separateBlocks' b acc | liftM trim (getText b) == Just "" = - (init acc) ++ [(last acc) ++ [b]] +separateBlocks' b acc | fmap trim (getText b) == Just "" = + init acc ++ [last acc ++ [b]] separateBlocks' b acc = acc ++ [[b]] separateBlocks :: [Block] -> [[Block]] @@ -138,38 +133,37 @@ separateBlocks blks = foldr separateBlocks' [[]] (reverse blks) flatToBullets' :: Integer -> [Block] -> [Block] flatToBullets' _ [] = [] flatToBullets' num xs@(b : elems) - | getLevelN b == num = b : (flatToBullets' num elems) + | getLevelN b == num = b : flatToBullets' num elems | otherwise = let bNumId = getNumIdN b bLevel = getLevelN b (children, remaining) = span (\b' -> - ((getLevelN b') > bLevel || - ((getLevelN b') == bLevel && (getNumIdN b') == bNumId))) + (getLevelN b') > bLevel || + ((getLevelN b') == bLevel && (getNumIdN b') == bNumId)) xs in case getListType b of Just (Enumerated attr) -> - (OrderedList attr (separateBlocks $ flatToBullets' bLevel children)) : - (flatToBullets' num remaining) + OrderedList attr (separateBlocks $ flatToBullets' bLevel children) : + flatToBullets' num remaining _ -> - (BulletList (separateBlocks $ flatToBullets' bLevel children)) : - (flatToBullets' num remaining) + BulletList (separateBlocks $ flatToBullets' bLevel children) : + flatToBullets' num remaining flatToBullets :: [Block] -> [Block] flatToBullets elems = flatToBullets' (-1) elems singleItemHeaderToHeader :: Block -> Block -singleItemHeaderToHeader (OrderedList _ [[h@(Header _ _ _)]]) = h +singleItemHeaderToHeader (OrderedList _ [[h@(Header{})]]) = h singleItemHeaderToHeader blk = blk blocksToBullets :: [Block] -> [Block] blocksToBullets blks = map singleItemHeaderToHeader $ - bottomUp removeListDivs $ - flatToBullets $ (handleListParagraphs blks) + bottomUp removeListDivs $flatToBullets (handleListParagraphs blks) plainParaInlines :: Block -> [Inline] plainParaInlines (Plain ils) = ils @@ -179,18 +173,16 @@ plainParaInlines _ = [] blocksToDefinitions' :: [([Inline], [[Block]])] -> [Block] -> [Block] -> [Block] blocksToDefinitions' [] acc [] = reverse acc blocksToDefinitions' defAcc acc [] = - reverse $ (DefinitionList (reverse defAcc)) : acc + reverse $ DefinitionList (reverse defAcc) : acc blocksToDefinitions' defAcc acc - ((Div (_, classes1, _) blks1) : (Div (ident2, classes2, kvs2) blks2) : blks) + (Div (_, classes1, _) blks1 : Div (ident2, classes2, kvs2) blks2 : blks) | "DefinitionTerm" `elem` classes1 && "Definition" `elem` classes2 = let remainingAttr2 = (ident2, delete "Definition" classes2, kvs2) - pair = case remainingAttr2 == ("", [], []) of - True -> (concatMap plainParaInlines blks1, [blks2]) - False -> (concatMap plainParaInlines blks1, [[Div remainingAttr2 blks2]]) + pair = if remainingAttr2 == ("", [], []) then (concatMap plainParaInlines blks1, [blks2]) else (concatMap plainParaInlines blks1, [[Div remainingAttr2 blks2]]) in blocksToDefinitions' (pair : defAcc) acc blks blocksToDefinitions' defAcc acc - ((Div (ident2, classes2, kvs2) blks2) : blks) + (Div (ident2, classes2, kvs2) blks2 : blks) | (not . null) defAcc && "Definition" `elem` classes2 = let remainingAttr2 = (ident2, delete "Definition" classes2, kvs2) defItems2 = case remainingAttr2 == ("", [], []) of @@ -205,14 +197,14 @@ blocksToDefinitions' defAcc acc blocksToDefinitions' [] acc (b:blks) = blocksToDefinitions' [] (b:acc) blks blocksToDefinitions' defAcc acc (b:blks) = - blocksToDefinitions' [] (b : (DefinitionList (reverse defAcc)) : acc) blks + blocksToDefinitions' [] (b : DefinitionList (reverse defAcc) : acc) blks removeListDivs' :: Block -> [Block] removeListDivs' (Div (ident, classes, kvs) blks) | "list-item" `elem` classes = case delete "list-item" classes of [] -> blks - classes' -> [Div (ident, classes', kvs) $ blks] + classes' -> [Div (ident, classes', kvs) blks] removeListDivs' (Div (ident, classes, kvs) blks) | not $ null $ listParagraphDivs `intersect` classes = case classes \\ listParagraphDivs of diff --git a/src/Text/Pandoc/Readers/Docx/Parse.hs b/src/Text/Pandoc/Readers/Docx/Parse.hs index 05ce691a6..1aa69f62e 100644 --- a/src/Text/Pandoc/Readers/Docx/Parse.hs +++ b/src/Text/Pandoc/Readers/Docx/Parse.hs @@ -106,7 +106,7 @@ eitherToD (Right b) = return b eitherToD (Left _) = throwError DocxError concatMapM :: (Monad m) => (a -> m [b]) -> [a] -> m [b] -concatMapM f xs = liftM concat (mapM f xs) +concatMapM f xs = fmap concat (mapM f xs) -- This is similar to `mapMaybe`: it maps a function returning the D @@ -304,7 +304,7 @@ archiveToDocument zf = do elemToBody :: NameSpaces -> Element -> D Body elemToBody ns element | isElem ns "w" "body" element = mapD (elemToBodyPart ns) (elChildren element) >>= - (\bps -> return $ Body bps) + (return . Body) elemToBody _ _ = throwError WrongElem archiveToStyles :: Archive -> (CharStyleMap, ParStyleMap) @@ -329,7 +329,7 @@ isBasedOnStyle ns element parentStyle , styleType == cStyleType parentStyle , Just basedOnVal <- findChildByName ns "w" "basedOn" element >>= findAttrByName ns "w" "val" - , Just ps <- parentStyle = (basedOnVal == getStyleId ps) + , Just ps <- parentStyle = basedOnVal == getStyleId ps | isElem ns "w" "style" element , Just styleType <- findAttrByName ns "w" "type" element , styleType == cStyleType parentStyle @@ -371,10 +371,10 @@ getStyleChildren ns element parentStyle buildBasedOnList :: (ElemToStyle a) => NameSpaces -> Element -> Maybe a -> [a] buildBasedOnList ns element rootStyle = - case (getStyleChildren ns element rootStyle) of + case getStyleChildren ns element rootStyle of [] -> [] stys -> stys ++ - (concatMap (\s -> buildBasedOnList ns element (Just s)) stys) + concatMap (\s -> buildBasedOnList ns element (Just s)) stys archiveToNotes :: Archive -> Notes archiveToNotes zf = @@ -389,8 +389,8 @@ archiveToNotes zf = Just e -> elemToNameSpaces e Nothing -> [] ns = unionBy (\x y -> fst x == fst y) fn_namespaces en_namespaces - fn = fnElem >>= (elemToNotes ns "footnote") - en = enElem >>= (elemToNotes ns "endnote") + fn = fnElem >>= elemToNotes ns "footnote" + en = enElem >>= elemToNotes ns "endnote" in Notes ns fn en @@ -401,7 +401,7 @@ archiveToComments zf = cmts_namespaces = case cmtsElem of Just e -> elemToNameSpaces e Nothing -> [] - cmts = (elemToComments cmts_namespaces) <$> cmtsElem + cmts = elemToComments cmts_namespaces <$> cmtsElem in case cmts of Just c -> Comments cmts_namespaces c @@ -442,8 +442,7 @@ lookupLevel :: String -> String -> Numbering -> Maybe Level lookupLevel numId ilvl (Numbering _ numbs absNumbs) = do absNumId <- lookup numId $ map (\(Numb nid absnumid) -> (nid, absnumid)) numbs lvls <- lookup absNumId $ map (\(AbstractNumb aid ls) -> (aid, ls)) absNumbs - lvl <- lookup ilvl $ map (\l@(i, _, _, _) -> (i, l)) lvls - return lvl + lookup ilvl $ map (\l@(i, _, _, _) -> (i, l)) lvls numElemToNum :: NameSpaces -> Element -> Maybe Numb @@ -479,7 +478,7 @@ levelElemToLevel ns element levelElemToLevel _ _ = Nothing archiveToNumbering' :: Archive -> Maybe Numbering -archiveToNumbering' zf = do +archiveToNumbering' zf = case findEntryByPath "word/numbering.xml" zf of Nothing -> Just $ Numbering [] [] [] Just entry -> do @@ -503,7 +502,8 @@ elemToNotes ns notetype element (\a -> Just (a, e))) (findChildrenByName ns "w" notetype element) in - Just $ M.fromList $ pairs + Just $ + M.fromList pairs elemToNotes _ _ _ = Nothing elemToComments :: NameSpaces -> Element -> M.Map String Element @@ -514,7 +514,7 @@ elemToComments ns element (\a -> Just (a, e))) (findChildrenByName ns "w" "comment" element) in - M.fromList $ pairs + M.fromList pairs elemToComments _ _ = M.empty -- cgit v1.2.3 From 84812983573232a1dc25f68268acfa9b28ac5a22 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 27 Oct 2017 21:44:22 -0700 Subject: Don't rely on syb when we don't need to. --- src/Text/Pandoc/Error.hs | 2 +- src/Text/Pandoc/Logging.hs | 2 +- src/Text/Pandoc/Shared.hs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs index ade33e26d..0c97ecbad 100644 --- a/src/Text/Pandoc/Error.hs +++ b/src/Text/Pandoc/Error.hs @@ -35,7 +35,7 @@ module Text.Pandoc.Error ( handleError) where import Control.Exception (Exception) -import Data.Generics (Typeable) +import Data.Typeable (Typeable) import GHC.Generics (Generic) import Network.HTTP.Client (HttpException) import System.Exit (ExitCode (..), exitWith) diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs index f7fd503d3..a156f017c 100644 --- a/src/Text/Pandoc/Logging.hs +++ b/src/Text/Pandoc/Logging.hs @@ -45,7 +45,7 @@ import Data.Aeson.Encode.Pretty (Config (..), defConfig, encodePretty', keyOrder) import qualified Data.ByteString.Lazy as BL import Data.Data (Data, toConstr) -import Data.Generics (Typeable) +import Data.Typeable (Typeable) import qualified Data.Text as Text import GHC.Generics (Generic) import Text.Pandoc.Definition diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 49b41b534..85f13c865 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -105,7 +105,7 @@ import qualified Control.Monad.State.Strict as S import qualified Data.ByteString.Lazy as BL import Data.Char (isAlpha, isDigit, isLetter, isLower, isSpace, isUpper, toLower) -import Data.Generics (Data, Typeable) +import Data.Data (Data, Typeable) import Data.List (find, intercalate, stripPrefix) import qualified Data.Map as M import Data.Maybe (mapMaybe) -- cgit v1.2.3 From cbcb9b36c088b3dd1e07f9d0318594b78e5d38f2 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 27 Oct 2017 23:13:55 -0700 Subject: hlint suggestions. --- src/Text/Pandoc/CSV.hs | 1 - src/Text/Pandoc/Class.hs | 1 - src/Text/Pandoc/Emoji.hs | 1 - src/Text/Pandoc/ImageSize.hs | 1 - src/Text/Pandoc/Lua/StackInstances.hs | 20 ++-- src/Text/Pandoc/MIME.hs | 1 - src/Text/Pandoc/PDF.hs | 1 - src/Text/Pandoc/Readers/Creole.hs | 2 +- src/Text/Pandoc/Readers/Docx.hs | 75 +++++++------- src/Text/Pandoc/Readers/Docx/Lists.hs | 2 +- src/Text/Pandoc/Readers/Docx/Parse.hs | 26 ++--- src/Text/Pandoc/Readers/Docx/Util.hs | 1 - src/Text/Pandoc/Readers/EPUB.hs | 22 ++-- src/Text/Pandoc/Readers/Haddock.hs | 2 +- src/Text/Pandoc/Readers/LaTeX.hs | 68 ++++++------- src/Text/Pandoc/Readers/LaTeX/Types.hs | 1 - src/Text/Pandoc/Readers/Markdown.hs | 70 ++++++------- src/Text/Pandoc/Readers/Muse.hs | 18 ++-- src/Text/Pandoc/Readers/Native.hs | 1 - src/Text/Pandoc/Readers/OPML.hs | 15 ++- src/Text/Pandoc/Readers/Odt/Arrows/State.hs | 6 +- src/Text/Pandoc/Readers/Odt/Base.hs | 3 +- src/Text/Pandoc/Readers/Odt/Generic/Fallible.hs | 4 +- src/Text/Pandoc/Readers/Odt/Generic/Utils.hs | 4 +- src/Text/Pandoc/Readers/Textile.hs | 2 +- src/Text/Pandoc/Readers/TikiWiki.hs | 104 +++++++++---------- src/Text/Pandoc/Readers/Vimwiki.hs | 129 ++++++++++++------------ src/Text/Pandoc/SelfContained.hs | 13 +-- src/Text/Pandoc/Shared.hs | 43 ++++---- src/Text/Pandoc/Slides.hs | 4 +- src/Text/Pandoc/Templates.hs | 4 +- src/Text/Pandoc/UUID.hs | 1 - 32 files changed, 307 insertions(+), 339 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/CSV.hs b/src/Text/Pandoc/CSV.hs index 924052dca..e25b684f8 100644 --- a/src/Text/Pandoc/CSV.hs +++ b/src/Text/Pandoc/CSV.hs @@ -100,4 +100,3 @@ endline :: Parser () endline = do optional (void $ char '\r') void $ char '\n' - diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 592ec11e5..7c518e84b 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -997,4 +997,3 @@ instance PandocMonad m => PandocMonad (ParsecT s st m) where else "") (return ()) logOutput = lift . logOutput - diff --git a/src/Text/Pandoc/Emoji.hs b/src/Text/Pandoc/Emoji.hs index c9f368abc..3766960ea 100644 --- a/src/Text/Pandoc/Emoji.hs +++ b/src/Text/Pandoc/Emoji.hs @@ -903,4 +903,3 @@ emojis = M.fromList ,("zero","0\65039\8419") ,("zzz","\128164") ] - diff --git a/src/Text/Pandoc/ImageSize.hs b/src/Text/Pandoc/ImageSize.hs index 61ff006cf..27d5c6a9c 100644 --- a/src/Text/Pandoc/ImageSize.hs +++ b/src/Text/Pandoc/ImageSize.hs @@ -602,4 +602,3 @@ tagTypeTable = M.fromList , (0xa300, FileSource) , (0xa301, SceneType) ] - diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index 0a7b61578..3eb14eba3 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -208,18 +208,18 @@ peekBlock idx = do case tag of "BlockQuote" -> BlockQuote <$> elementContent "BulletList" -> BulletList <$> elementContent - "CodeBlock" -> (withAttr CodeBlock) <$> elementContent + "CodeBlock" -> withAttr CodeBlock <$> elementContent "DefinitionList" -> DefinitionList <$> elementContent - "Div" -> (withAttr Div) <$> elementContent + "Div" -> withAttr Div <$> elementContent "Header" -> (\(lvl, LuaAttr attr, lst) -> Header lvl attr lst) <$> elementContent "HorizontalRule" -> return HorizontalRule "LineBlock" -> LineBlock <$> elementContent - "OrderedList" -> (uncurry OrderedList) <$> elementContent + "OrderedList" -> uncurry OrderedList <$> elementContent "Null" -> return Null "Para" -> Para <$> elementContent "Plain" -> Plain <$> elementContent - "RawBlock" -> (uncurry RawBlock) <$> elementContent + "RawBlock" -> uncurry RawBlock <$> elementContent "Table" -> (\(capt, aligns, widths, headers, body) -> Table capt aligns widths headers body) <$> elementContent @@ -257,8 +257,8 @@ peekInline :: StackIndex -> Lua Inline peekInline idx = do tag <- getTag idx case tag of - "Cite" -> (uncurry Cite) <$> elementContent - "Code" -> (withAttr Code) <$> elementContent + "Cite" -> uncurry Cite <$> elementContent + "Code" -> withAttr Code <$> elementContent "Emph" -> Emph <$> elementContent "Image" -> (\(LuaAttr attr, lst, tgt) -> Image attr lst tgt) <$> elementContent @@ -266,13 +266,13 @@ peekInline idx = do <$> elementContent "LineBreak" -> return LineBreak "Note" -> Note <$> elementContent - "Math" -> (uncurry Math) <$> elementContent - "Quoted" -> (uncurry Quoted) <$> elementContent - "RawInline" -> (uncurry RawInline) <$> elementContent + "Math" -> uncurry Math <$> elementContent + "Quoted" -> uncurry Quoted <$> elementContent + "RawInline" -> uncurry RawInline <$> elementContent "SmallCaps" -> SmallCaps <$> elementContent "SoftBreak" -> return SoftBreak "Space" -> return Space - "Span" -> (withAttr Span) <$> elementContent + "Span" -> withAttr Span <$> elementContent "Str" -> Str <$> elementContent "Strikeout" -> Strikeout <$> elementContent "Strong" -> Strong <$> elementContent diff --git a/src/Text/Pandoc/MIME.hs b/src/Text/Pandoc/MIME.hs index 162112634..fb85910bb 100644 --- a/src/Text/Pandoc/MIME.hs +++ b/src/Text/Pandoc/MIME.hs @@ -525,4 +525,3 @@ mimeTypesList = -- List borrowed from happstack-server. ,("zip","application/zip") ,("zmt","chemical/x-mopac-input") ] - diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index cafb4a226..beb3c569f 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -438,4 +438,3 @@ context2pdf verbosity tmpDir source = inDirectory tmpDir $ do return $ Left logmsg (ExitSuccess, Nothing) -> return $ Left "" (ExitSuccess, Just pdf) -> return $ Right pdf - diff --git a/src/Text/Pandoc/Readers/Creole.hs b/src/Text/Pandoc/Readers/Creole.hs index 3b330e544..4da259c0e 100644 --- a/src/Text/Pandoc/Readers/Creole.hs +++ b/src/Text/Pandoc/Readers/Creole.hs @@ -282,7 +282,7 @@ placeholder = B.text <$> try (string "<<<" >> manyTill anyChar (string ">>>") >> return "") whitespace :: PandocMonad m => CRLParser m B.Inlines -whitespace = (lb <|> regsp) +whitespace = lb <|> regsp where lb = try $ skipMany spaceChar >> linebreak >> return B.space regsp = try $ skipMany1 spaceChar >> return B.space diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index 465c3abec..1874a011a 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -139,7 +139,7 @@ instance Default DEnv where type DocxContext m = ReaderT DEnv (StateT DState m) evalDocxContext :: PandocMonad m => DocxContext m a -> DEnv -> DState -> m a -evalDocxContext ctx env st = flip evalStateT st $ flip runReaderT env $ ctx +evalDocxContext ctx env st = flip evalStateT st $flip runReaderT env ctx -- This is empty, but we put it in for future-proofing. spansToKeep :: [String] @@ -156,7 +156,7 @@ metaStyles = M.fromList [ ("Title", "title") , ("Abstract", "abstract")] sepBodyParts :: [BodyPart] -> ([BodyPart], [BodyPart]) -sepBodyParts = span (\bp -> (isMetaPar bp || isEmptyPar bp)) +sepBodyParts = span (\bp -> isMetaPar bp || isEmptyPar bp) isMetaPar :: BodyPart -> Bool isMetaPar (Paragraph pPr _) = @@ -183,7 +183,7 @@ bodyPartsToMeta' (bp : bps) remaining <- bodyPartsToMeta' bps let f (MetaInlines ils) (MetaInlines ils') = MetaBlocks [Para ils, Para ils'] - f (MetaInlines ils) (MetaBlocks blks) = MetaBlocks ((Para ils) : blks) + f (MetaInlines ils) (MetaBlocks blks) = MetaBlocks (Para ils : blks) f m (MetaList mv) = MetaList (m : mv) f m n = MetaList [m, n] return $ M.insertWith f metaField (MetaInlines (toList inlines)) remaining @@ -215,17 +215,17 @@ codeDivs = ["SourceCode"] runElemToInlines :: RunElem -> Inlines runElemToInlines (TextRun s) = text s -runElemToInlines (LnBrk) = linebreak -runElemToInlines (Tab) = space -runElemToInlines (SoftHyphen) = text "\xad" -runElemToInlines (NoBreakHyphen) = text "\x2011" +runElemToInlines LnBrk = linebreak +runElemToInlines Tab = space +runElemToInlines SoftHyphen = text "\xad" +runElemToInlines NoBreakHyphen = text "\x2011" runElemToString :: RunElem -> String runElemToString (TextRun s) = s -runElemToString (LnBrk) = ['\n'] -runElemToString (Tab) = ['\t'] -runElemToString (SoftHyphen) = ['\xad'] -runElemToString (NoBreakHyphen) = ['\x2011'] +runElemToString LnBrk = ['\n'] +runElemToString Tab = ['\t'] +runElemToString SoftHyphen = ['\xad'] +runElemToString NoBreakHyphen = ['\x2011'] runToString :: Run -> String runToString (Run _ runElems) = concatMap runElemToString runElems @@ -274,21 +274,21 @@ runStyleToTransform rPr , s `elem` spansToKeep = let rPr' = rPr{rStyle = Nothing} in - (spanWith ("", [s], [])) . (runStyleToTransform rPr') + spanWith ("", [s], []) . runStyleToTransform rPr' | Just True <- isItalic rPr = - emph . (runStyleToTransform rPr {isItalic = Nothing}) + emph . runStyleToTransform rPr {isItalic = Nothing} | Just True <- isBold rPr = - strong . (runStyleToTransform rPr {isBold = Nothing}) + strong . runStyleToTransform rPr {isBold = Nothing} | Just True <- isSmallCaps rPr = - smallcaps . (runStyleToTransform rPr {isSmallCaps = Nothing}) + smallcaps . runStyleToTransform rPr {isSmallCaps = Nothing} | Just True <- isStrike rPr = - strikeout . (runStyleToTransform rPr {isStrike = Nothing}) + strikeout . runStyleToTransform rPr {isStrike = Nothing} | Just SupScrpt <- rVertAlign rPr = - superscript . (runStyleToTransform rPr {rVertAlign = Nothing}) + superscript . runStyleToTransform rPr {rVertAlign = Nothing} | Just SubScrpt <- rVertAlign rPr = - subscript . (runStyleToTransform rPr {rVertAlign = Nothing}) + subscript . runStyleToTransform rPr {rVertAlign = Nothing} | Just "single" <- rUnderline rPr = - underlineSpan . (runStyleToTransform rPr {rUnderline = Nothing}) + underlineSpan . runStyleToTransform rPr {rUnderline = Nothing} | otherwise = id runToInlines :: PandocMonad m => Run -> DocxContext m Inlines @@ -306,10 +306,10 @@ runToInlines (Run rs runElems) let ils = smushInlines (map runElemToInlines runElems) return $ (runStyleToTransform $ resolveDependentRunStyle rs) ils runToInlines (Footnote bps) = do - blksList <- smushBlocks <$> (mapM bodyPartToBlocks bps) + blksList <- smushBlocks <$> mapM bodyPartToBlocks bps return $ note blksList runToInlines (Endnote bps) = do - blksList <- smushBlocks <$> (mapM bodyPartToBlocks bps) + blksList <- smushBlocks <$> mapM bodyPartToBlocks bps return $ note blksList runToInlines (InlineDrawing fp title alt bs ext) = do (lift . lift) $ P.insertMedia fp Nothing bs @@ -330,7 +330,7 @@ blocksToInlinesWarn cmtId blks = do notParaOrPlain (Para _) = False notParaOrPlain (Plain _) = False notParaOrPlain _ = True - when (not $ null $ filter notParaOrPlain blkList) $ + unless (null $ filter notParaOrPlain blkList) $ lift $ P.report $ DocxParserWarning $ "Docx comment " ++ cmtId ++ " will not retain formatting" return $ fromList $ blocksToInlines blkList @@ -390,7 +390,7 @@ parPartToInlines (BookMark _ anchor) = -- are not defined in pandoc, it seems like a necessary evil to -- avoid an extra pass. let newAnchor = - if not inHdrBool && anchor `elem` (M.elems anchorMap) + if not inHdrBool && anchor `elem` M.elems anchorMap then uniqueIdent [Str anchor] (Set.fromList $ M.elems anchorMap) else anchor unless inHdrBool @@ -399,7 +399,7 @@ parPartToInlines (BookMark _ anchor) = parPartToInlines (Drawing fp title alt bs ext) = do (lift . lift) $ P.insertMedia fp Nothing bs return $ imageWith (extentToAttr ext) fp title $ text alt -parPartToInlines Chart = do +parPartToInlines Chart = return $ spanWith ("", ["chart"], []) $ text "[CHART]" parPartToInlines (InternalHyperLink anchor runs) = do ils <- smushInlines <$> mapM runToInlines runs @@ -407,11 +407,10 @@ parPartToInlines (InternalHyperLink anchor runs) = do parPartToInlines (ExternalHyperLink target runs) = do ils <- smushInlines <$> mapM runToInlines runs return $ link target "" ils -parPartToInlines (PlainOMath exps) = do +parPartToInlines (PlainOMath exps) = return $ math $ writeTeX exps parPartToInlines (SmartTag runs) = do - ils <- smushInlines <$> mapM runToInlines runs - return ils + smushInlines <$> mapM runToInlines runs isAnchorSpan :: Inline -> Bool isAnchorSpan (Span (_, classes, kvs) _) = @@ -454,7 +453,7 @@ makeHeaderAnchor' blk = return blk -- Rewrite a standalone paragraph block as a plain singleParaToPlain :: Blocks -> Blocks singleParaToPlain blks - | (Para (ils) :< seeq) <- viewl $ unMany blks + | (Para ils :< seeq) <- viewl $ unMany blks , Seq.null seeq = singleton $ Plain ils singleParaToPlain blks = blks @@ -471,7 +470,7 @@ rowToBlocksList (Row cells) = do -- like trimInlines, but also take out linebreaks trimSps :: Inlines -> Inlines -trimSps (Many ils) = Many $ Seq.dropWhileL isSp $ Seq.dropWhileR isSp $ ils +trimSps (Many ils) = Many $ Seq.dropWhileL isSp $Seq.dropWhileR isSp ils where isSp Space = True isSp SoftBreak = True isSp LineBreak = True @@ -483,17 +482,17 @@ parStyleToTransform pPr , c `elem` divsToKeep = let pPr' = pPr { pStyle = cs } in - (divWith ("", [c], [])) . (parStyleToTransform pPr') + divWith ("", [c], []) . parStyleToTransform pPr' | (c:cs) <- pStyle pPr, c `elem` listParagraphDivs = let pPr' = pPr { pStyle = cs, indentation = Nothing} in - (divWith ("", [c], [])) . (parStyleToTransform pPr') + divWith ("", [c], []) . parStyleToTransform pPr' | (_:cs) <- pStyle pPr , Just True <- pBlockQuote pPr = let pPr' = pPr { pStyle = cs } in - blockQuote . (parStyleToTransform pPr') + blockQuote . parStyleToTransform pPr' | (_:cs) <- pStyle pPr = let pPr' = pPr { pStyle = cs} in @@ -523,7 +522,7 @@ bodyPartToBlocks (Paragraph pPr parparts) $ codeBlock $ concatMap parPartToString parparts | Just (style, n) <- pHeading pPr = do - ils <- local (\s-> s{docxInHeaderBlock=True}) $ + ils <-local (\s-> s{docxInHeaderBlock=True}) (smushInlines <$> mapM parPartToInlines parparts) makeHeaderAnchor $ headerWith ("", delete style (pStyle pPr), []) n ils @@ -545,7 +544,7 @@ bodyPartToBlocks (ListItem pPr numId lvl (Just levelInfo) parparts) = do , ("num-id", numId) , ("format", fmt) , ("text", txt) - , ("start", (show start)) + , ("start", show start) ] (_, fmt, txt, Nothing) -> [ ("level", lvl) @@ -556,7 +555,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) = - let pPr' = pPr {pStyle = "ListParagraph": (pStyle pPr)} + let pPr' = pPr {pStyle = "ListParagraph": pStyle pPr} in bodyPartToBlocks $ Paragraph pPr' parparts bodyPartToBlocks (Tbl _ _ _ []) = @@ -588,7 +587,7 @@ bodyPartToBlocks (Tbl cap _ look (r:rs)) = do widths = replicate width 0 :: [Double] return $ table caption (zip alignments widths) hdrCells cells -bodyPartToBlocks (OMathPara e) = do +bodyPartToBlocks (OMathPara e) = return $ para $ displayMath (writeTeX e) @@ -597,7 +596,7 @@ 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 - Just newTarget -> (Link attr ils ('#':newTarget, title)) + Just newTarget -> Link attr ils ('#':newTarget, title) Nothing -> l rewriteLink' il = return il @@ -610,7 +609,7 @@ bodyToOutput (Body bps) = do meta <- bodyPartsToMeta metabps blks <- smushBlocks <$> mapM bodyPartToBlocks blkbps blks' <- rewriteLinks $ blocksToDefinitions $ blocksToBullets $ toList blks - return $ (meta, blks') + return (meta, blks') docxToOutput :: PandocMonad m => ReaderOptions diff --git a/src/Text/Pandoc/Readers/Docx/Lists.hs b/src/Text/Pandoc/Readers/Docx/Lists.hs index c7f4adc98..53840c609 100644 --- a/src/Text/Pandoc/Readers/Docx/Lists.hs +++ b/src/Text/Pandoc/Readers/Docx/Lists.hs @@ -156,7 +156,7 @@ flatToBullets :: [Block] -> [Block] flatToBullets elems = flatToBullets' (-1) elems singleItemHeaderToHeader :: Block -> Block -singleItemHeaderToHeader (OrderedList _ [[h@(Header{})]]) = h +singleItemHeaderToHeader (OrderedList _ [[h@Header{}]]) = h singleItemHeaderToHeader blk = blk diff --git a/src/Text/Pandoc/Readers/Docx/Parse.hs b/src/Text/Pandoc/Readers/Docx/Parse.hs index 1aa69f62e..fea595027 100644 --- a/src/Text/Pandoc/Readers/Docx/Parse.hs +++ b/src/Text/Pandoc/Readers/Docx/Parse.hs @@ -303,8 +303,7 @@ archiveToDocument zf = do elemToBody :: NameSpaces -> Element -> D Body elemToBody ns element | isElem ns "w" "body" element = - mapD (elemToBodyPart ns) (elChildren element) >>= - (return . Body) + fmap Body (mapD (elemToBodyPart ns) (elChildren element)) elemToBody _ _ = throwError WrongElem archiveToStyles :: Archive -> (CharStyleMap, ParStyleMap) @@ -374,7 +373,7 @@ buildBasedOnList ns element rootStyle = case getStyleChildren ns element rootStyle of [] -> [] stys -> stys ++ - concatMap (\s -> buildBasedOnList ns element (Just s)) stys + concatMap (buildBasedOnList ns element . Just) stys archiveToNotes :: Archive -> Notes archiveToNotes zf = @@ -577,7 +576,7 @@ testBitMask :: String -> Int -> Bool testBitMask bitMaskS n = case (reads ("0x" ++ bitMaskS) :: [(Int, String)]) of [] -> False - ((n', _) : _) -> ((n' .|. n) /= 0) + ((n', _) : _) -> (n' .|. n) /= 0 stringToInteger :: String -> Maybe Integer stringToInteger s = listToMaybe $ map fst (reads s :: [(Integer, String)]) @@ -654,12 +653,8 @@ getTitleAndAlt :: NameSpaces -> Element -> (String, String) getTitleAndAlt ns element = let mbDocPr = findChildByName ns "wp" "inline" element >>= findChildByName ns "wp" "docPr" - title = case mbDocPr >>= findAttrByName ns "" "title" of - Just title' -> title' - Nothing -> "" - alt = case mbDocPr >>= findAttrByName ns "" "descr" of - Just alt' -> alt' - Nothing -> "" + title = fromMaybe "" (mbDocPr >>= findAttrByName ns "" "title") + alt = fromMaybe "" (mbDocPr >>= findAttrByName ns "" "descr") in (title, alt) elemToParPart :: NameSpaces -> Element -> D ParPart @@ -727,7 +722,7 @@ elemToParPart ns element runs <- mapD (elemToRun ns) (elChildren element) rels <- asks envRelationships case lookupRelationship location relId rels of - Just target -> do + Just target -> case findAttrByName ns "w" "anchor" element of Just anchor -> return $ ExternalHyperLink (target ++ '#':anchor) runs Nothing -> return $ ExternalHyperLink target runs @@ -750,7 +745,7 @@ elemToParPart ns element return $ CommentEnd cmtId elemToParPart ns element | isElem ns "m" "oMath" element = - (eitherToD $ readOMML $ showElement element) >>= (return . PlainOMath) + fmap PlainOMath (eitherToD $ readOMML $ showElement element) elemToParPart _ _ = throwError WrongElem elemToCommentStart :: NameSpaces -> Element -> D ParPart @@ -764,10 +759,10 @@ elemToCommentStart ns element elemToCommentStart _ _ = throwError WrongElem lookupFootnote :: String -> Notes -> Maybe Element -lookupFootnote s (Notes _ fns _) = fns >>= (M.lookup s) +lookupFootnote s (Notes _ fns _) = fns >>= M.lookup s lookupEndnote :: String -> Notes -> Maybe Element -lookupEndnote s (Notes _ _ ens) = ens >>= (M.lookup s) +lookupEndnote s (Notes _ _ ens) = ens >>= M.lookup s elemToExtent :: Element -> Extent elemToExtent drawingElem = @@ -1035,11 +1030,10 @@ elemToRunElems ns element let font = do fontElem <- findElement (qualName "rFonts") element stringToFont =<< - (foldr (<|>) Nothing $ + foldr (<|>) Nothing ( map (flip findAttr fontElem . qualName) ["ascii", "hAnsi"]) local (setFont font) (mapD (elemToRunElem ns) (elChildren element)) elemToRunElems _ _ = throwError WrongElem setFont :: Maybe Font -> ReaderEnv -> ReaderEnv setFont f s = s{envFont = f} - diff --git a/src/Text/Pandoc/Readers/Docx/Util.hs b/src/Text/Pandoc/Readers/Docx/Util.hs index 8415dbf68..d9d65bc07 100644 --- a/src/Text/Pandoc/Readers/Docx/Util.hs +++ b/src/Text/Pandoc/Readers/Docx/Util.hs @@ -44,4 +44,3 @@ findAttrByName :: NameSpaces -> String -> String -> Element -> Maybe String findAttrByName ns pref name el = let ns' = ns ++ elemToNameSpaces el in findAttr (elemName ns' pref name) el - diff --git a/src/Text/Pandoc/Readers/EPUB.hs b/src/Text/Pandoc/Readers/EPUB.hs index d38a40c8d..c1eb6ca59 100644 --- a/src/Text/Pandoc/Readers/EPUB.hs +++ b/src/Text/Pandoc/Readers/EPUB.hs @@ -1,5 +1,5 @@ {-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE StandaloneDeriving #-} + {-# LANGUAGE TupleSections #-} {-# LANGUAGE ViewPatterns #-} @@ -39,7 +39,7 @@ type Items = M.Map String (FilePath, MimeType) readEPUB :: PandocMonad m => ReaderOptions -> BL.ByteString -> m Pandoc readEPUB opts bytes = case toArchiveOrFail bytes of - Right archive -> archiveToEPUB opts $ archive + Right archive -> archiveToEPUB opts archive Left _ -> throwError $ PandocParseError "Couldn't extract ePub file" -- runEPUB :: Except PandocError a -> Either PandocError a @@ -61,7 +61,7 @@ archiveToEPUB os archive = do Pandoc _ bs <- foldM' (\a b -> ((a <>) . walk (prependHash escapedSpine)) `liftM` parseSpineElem root b) mempty spine - let ast = coverDoc <> (Pandoc meta bs) + let ast = coverDoc <> Pandoc meta bs fetchImages (M.elems items) root archive ast return ast where @@ -79,7 +79,7 @@ archiveToEPUB os archive = do return $ fixInternalReferences path html mimeToReader s _ (unEscapeString -> path) | s `elem` imageMimes = return $ imageToPandoc path - | otherwise = return $ mempty + | otherwise = return mempty -- paths should be absolute when this function is called -- renameImages should do this @@ -122,7 +122,7 @@ parseManifest content = do let items = findChildren (dfName "item") manifest r <- mapM parseItem items let cover = findAttr (emptyName "href") =<< filterChild findCover manifest - return (cover, (M.fromList r)) + return (cover, M.fromList r) where findCover e = maybe False (isInfixOf "cover-image") (findAttr (emptyName "properties") e) @@ -136,7 +136,7 @@ parseSpine :: PandocMonad m => Items -> Element -> m [(FilePath, MimeType)] parseSpine is e = do spine <- findElementE (dfName "spine") e let itemRefs = findChildren (dfName "itemref") spine - mapM (mkE "parseSpine" . (flip M.lookup is)) $ mapMaybe parseItemRef itemRefs + mapM (mkE "parseSpine" . flip M.lookup is) $ mapMaybe parseItemRef itemRefs where parseItemRef ref = do let linear = maybe True (== "yes") (findAttr (emptyName "linear") ref) @@ -167,21 +167,21 @@ getManifest archive = do docElem <- (parseXMLDocE . UTF8.toStringLazy . fromEntry) metaEntry let namespaces = mapMaybe attrToNSPair (elAttribs docElem) ns <- mkE "xmlns not in namespaces" (lookup "xmlns" namespaces) - as <- liftM ((map attrToPair) . elAttribs) + as <- fmap (map attrToPair . elAttribs) (findElementE (QName "rootfile" (Just ns) Nothing) docElem) manifestFile <- mkE "Root not found" (lookup "full-path" as) let rootdir = dropFileName manifestFile --mime <- lookup "media-type" as manifest <- findEntryByPathE manifestFile archive - liftM ((,) rootdir) (parseXMLDocE . UTF8.toStringLazy . fromEntry $ manifest) + fmap ((,) rootdir) (parseXMLDocE . UTF8.toStringLazy . fromEntry $ manifest) -- Fixup fixInternalReferences :: FilePath -> Pandoc -> Pandoc fixInternalReferences pathToFile = - (walk $ renameImages root) - . (walk $ fixBlockIRs filename) - . (walk $ fixInlineIRs filename) + walk (renameImages root) + . walk (fixBlockIRs filename) + . walk (fixInlineIRs filename) where (root, escapeURI -> filename) = splitFileName pathToFile diff --git a/src/Text/Pandoc/Readers/Haddock.hs b/src/Text/Pandoc/Readers/Haddock.hs index 9d0610e01..e98c79ed8 100644 --- a/src/Text/Pandoc/Readers/Haddock.hs +++ b/src/Text/Pandoc/Readers/Haddock.hs @@ -142,7 +142,7 @@ makeExample prompt expression result = <> B.space <> B.codeWith ([], ["haskell","expr"], []) (trim expression) <> B.linebreak - <> (mconcat $ intersperse B.linebreak $ map coder result') + <> mconcat (intersperse B.linebreak $ map coder result') where -- 1. drop trailing whitespace from the prompt, remember the prefix prefix = takeWhile (`elem` " \t") prompt diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 665ed6548..c91e8bd79 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -107,7 +107,7 @@ parseLaTeX = do (if bottomLevel < 1 then walk (adjustHeaders (1 - bottomLevel)) else id) $ - walk (resolveRefs (sLabels st)) $ doc' + walk (resolveRefs (sLabels st)) doc' return $ Pandoc meta bs' resolveRefs :: M.Map String [Inline] -> Inline -> Inline @@ -246,7 +246,7 @@ rawLaTeXParser parser = do case res of Left _ -> mzero Right (raw, st) -> do - updateState (updateMacros ((sMacros st) <>)) + updateState (updateMacros (sMacros st <>)) takeP (T.length (untokenize raw)) applyMacros :: (PandocMonad m, HasMacros s, HasReaderOptions s) @@ -333,7 +333,7 @@ totoks pos t = : totoks (incSourceColumn pos (1 + T.length ws + T.length ss)) rest''' | d == '\t' || d == '\n' -> - Tok pos Symbol ("\\") + Tok pos Symbol "\\" : totoks (incSourceColumn pos 1) rest | otherwise -> Tok pos (CtrlSeq (T.singleton d)) (T.pack [c,d]) @@ -403,7 +403,7 @@ satisfyTok f = doMacros :: PandocMonad m => Int -> LP m () doMacros n = do verbatimMode <- sVerbatimMode <$> getState - when (not verbatimMode) $ do + unless verbatimMode $ do inp <- getInput case inp of Tok spos (CtrlSeq "begin") _ : Tok _ Symbol "{" : @@ -543,7 +543,7 @@ bgroup = try $ do symbol '{' <|> controlSeq "bgroup" <|> controlSeq "begingroup" egroup :: PandocMonad m => LP m Tok -egroup = (symbol '}' <|> controlSeq "egroup" <|> controlSeq "endgroup") +egroup = symbol '}' <|> controlSeq "egroup" <|> controlSeq "endgroup" grouped :: (PandocMonad m, Monoid a) => LP m a -> LP m a grouped parser = try $ do @@ -577,7 +577,7 @@ dimenarg :: PandocMonad m => LP m Text dimenarg = try $ do ch <- option False $ True <$ symbol '=' Tok _ _ s <- satisfyTok isWordTok - guard $ (T.take 2 (T.reverse s)) `elem` + guard $ T.take 2 (T.reverse s) `elem` ["pt","pc","in","bp","cm","mm","dd","cc","sp"] let num = T.take (T.length s - 2) s guard $ T.length num > 0 @@ -633,7 +633,7 @@ mkImage options src = do _ -> return $ imageWith attr src "" alt doxspace :: PandocMonad m => LP m Inlines -doxspace = do +doxspace = (space <$ lookAhead (satisfyTok startsWithLetter)) <|> return mempty where startsWithLetter (Tok _ Word t) = case T.uncons t of @@ -662,22 +662,22 @@ lit = pure . str removeDoubleQuotes :: Text -> Text removeDoubleQuotes t = - maybe t id $ T.stripPrefix "\"" t >>= T.stripSuffix "\"" + Data.Maybe.fromMaybe t $ T.stripPrefix "\"" t >>= T.stripSuffix "\"" doubleQuote :: PandocMonad m => LP m Inlines -doubleQuote = do +doubleQuote = quoted' doubleQuoted (try $ count 2 $ symbol '`') - (void $ try $ count 2 $ symbol '\'') + (void $ try $ count 2 $ symbol '\'') <|> quoted' doubleQuoted ((:[]) <$> symbol '“') (void $ symbol '”') -- the following is used by babel for localized quotes: <|> quoted' doubleQuoted (try $ sequence [symbol '"', symbol '`']) (void $ try $ sequence [symbol '"', symbol '\'']) singleQuote :: PandocMonad m => LP m Inlines -singleQuote = do +singleQuote = quoted' singleQuoted ((:[]) <$> symbol '`') - (try $ symbol '\'' >> - notFollowedBy (satisfyTok startsWithLetter)) + (try $ symbol '\'' >> + notFollowedBy (satisfyTok startsWithLetter)) <|> quoted' singleQuoted ((:[]) <$> symbol '‘') (try $ symbol '’' >> notFollowedBy (satisfyTok startsWithLetter)) @@ -726,8 +726,8 @@ doAcronymPlural form = do acro <- braced plural <- lit "s" return . mconcat $ [spanWith ("",[],[("acronym-label", toksToString acro), - ("acronym-form", "plural+" ++ form)]) $ mconcat - $ [str $ toksToString acro, plural]] + ("acronym-form", "plural+" ++ form)]) $ + mconcat [str $ toksToString acro, plural]] doverb :: PandocMonad m => LP m Inlines doverb = do @@ -748,7 +748,7 @@ verbTok stopchar = do let (t1, t2) = T.splitAt i txt inp <- getInput setInput $ Tok (incSourceColumn pos i) Symbol (T.singleton stopchar) - : (totoks (incSourceColumn pos (i + 1)) (T.drop 1 t2)) ++ inp + : totoks (incSourceColumn pos (i + 1)) (T.drop 1 t2) ++ inp return $ Tok pos toktype t1 dolstinline :: PandocMonad m => LP m Inlines @@ -773,8 +773,8 @@ keyval = try $ do val <- option [] $ do symbol '=' optional sp - braced <|> (many1 (satisfyTok isWordTok <|> satisfyTok isSpecSym - <|> anyControlSeq)) + braced <|> many1 (satisfyTok isWordTok <|> satisfyTok isSpecSym + <|> anyControlSeq) optional sp optional (symbol ',') optional sp @@ -1020,10 +1020,10 @@ dollarsMath = do contents <- trim . toksToString <$> many (notFollowedBy (symbol '$') >> anyTok) if display - then do + then mathDisplay contents <$ try (symbol '$' >> symbol '$') - <|> (guard (null contents) >> return (mathInline "")) - else mathInline contents <$ (symbol '$') + <|> (guard (null contents) >> return (mathInline "")) + else mathInline contents <$ symbol '$' -- citations @@ -1041,7 +1041,7 @@ simpleCiteArgs :: PandocMonad m => LP m [Citation] simpleCiteArgs = try $ do first <- optionMaybe $ toList <$> opt second <- optionMaybe $ toList <$> opt - keys <- try $ bgroup *> (manyTill citationLabel egroup) + keys <- try $ bgroup *> manyTill citationLabel egroup let (pre, suf) = case (first , second ) of (Just s , Nothing) -> (mempty, s ) (Just s , Just t ) -> (s , t ) @@ -1080,7 +1080,7 @@ cites mode multi = try $ do 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 ++ (toksToString raw)) + return $ cite c (rawInline "latex" $ "\\" ++ name ++ toksToString raw) handleCitationPart :: Inlines -> [Citation] handleCitationPart ils = @@ -1139,7 +1139,7 @@ singleChar = try $ do then do let (t1, t2) = (T.take 1 t, T.drop 1 t) inp <- getInput - setInput $ (Tok (incSourceColumn pos 1) toktype t2) : inp + setInput $ Tok (incSourceColumn pos 1) toktype t2 : inp return $ Tok pos toktype t1 else return $ Tok pos toktype t @@ -1606,7 +1606,7 @@ getRawCommand name txt = do void braced skipopts void $ count 4 braced - "def" -> do + "def" -> void $ manyTill anyTok braced _ -> do skipangles @@ -1715,14 +1715,14 @@ inlines = mconcat <$> many inline -- block elements: begin_ :: PandocMonad m => Text -> LP m () -begin_ t = (try $ do +begin_ t = try (do controlSeq "begin" spaces txt <- untokenize <$> braced guard (t == txt)) <?> ("\\begin{" ++ T.unpack t ++ "}") end_ :: PandocMonad m => Text -> LP m () -end_ t = (try $ do +end_ t = try (do controlSeq "end" spaces txt <- untokenize <$> braced @@ -1766,7 +1766,7 @@ insertIncluded :: PandocMonad m insertIncluded dirs f = do pos <- getPosition containers <- getIncludeFiles <$> getState - when (f `elem` containers) $ do + when (f `elem` containers) $ throwError $ PandocParseError $ "Include file loop at " ++ show pos updateState $ addIncludeFile f mbcontents <- readFileFromDirs dirs f @@ -1800,7 +1800,7 @@ authors = try $ do addMeta "author" (map trimInlines auths) macroDef :: PandocMonad m => LP m Blocks -macroDef = do +macroDef = mempty <$ ((commandDef <|> environmentDef) <* doMacros 0) where commandDef = do (name, macro') <- newcommand <|> letmacro <|> defmacro @@ -2177,9 +2177,9 @@ fancyverbEnv name = do codeBlockWith attr <$> verbEnv name obeylines :: PandocMonad m => LP m Blocks -obeylines = do +obeylines = para . fromList . removeLeadingTrailingBreaks . - walk softBreakToHard . toList <$> env "obeylines" inlines + walk softBreakToHard . toList <$> env "obeylines" inlines where softBreakToHard SoftBreak = LineBreak softBreakToHard x = x removeLeadingTrailingBreaks = reverse . dropWhile isLineBreak . @@ -2368,7 +2368,7 @@ splitWordTok :: PandocMonad m => LP m () splitWordTok = do inp <- getInput case inp of - (Tok spos Word t : rest) -> do + (Tok spos Word t : rest) -> setInput $ map (Tok spos Symbol . T.singleton) (T.unpack t) ++ rest _ -> return () @@ -2433,9 +2433,9 @@ parseTableRow envname prefsufs = do suffpos <- getPosition option [] (count 1 amp) return $ map (setpos prefpos) pref ++ contents ++ map (setpos suffpos) suff - rawcells <- sequence (map celltoks prefsufs) + rawcells <- mapM celltoks prefsufs oldInput <- getInput - cells <- sequence $ map (\ts -> setInput ts >> parseTableCell) rawcells + cells <- mapM (\ts -> setInput ts >> parseTableCell) rawcells setInput oldInput spaces let numcells = length cells diff --git a/src/Text/Pandoc/Readers/LaTeX/Types.hs b/src/Text/Pandoc/Readers/LaTeX/Types.hs index 9e441714d..b24b2ad0a 100644 --- a/src/Text/Pandoc/Readers/LaTeX/Types.hs +++ b/src/Text/Pandoc/Readers/LaTeX/Types.hs @@ -49,4 +49,3 @@ data ExpansionPoint = ExpandWhenDefined | ExpandWhenUsed data Macro = Macro ExpansionPoint Int (Maybe [Tok]) [Tok] deriving Show - diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 48719a678..69e70f9f5 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -74,7 +74,7 @@ readMarkdown :: PandocMonad m -> Text -- ^ String to parse (assuming @'\n'@ line endings) -> m Pandoc readMarkdown opts s = do - parsed <- (readWithM parseMarkdown) def{ stateOptions = opts } + parsed <- readWithM parseMarkdown def{ stateOptions = opts } (T.unpack (crFilter s) ++ "\n\n") case parsed of Right result -> return result @@ -162,7 +162,7 @@ charsInBalancedBrackets openBrackets = (char '[' >> charsInBalancedBrackets (openBrackets + 1)) <|> (char ']' >> charsInBalancedBrackets (openBrackets - 1)) <|> (( (() <$ code) - <|> (() <$ (escapedChar')) + <|> (() <$ escapedChar') <|> (newline >> notFollowedBy blankline) <|> skipMany1 (noneOf "[]`\n\\") <|> (() <$ count 1 (oneOf "`\\")) @@ -241,7 +241,7 @@ yamlMetaBlock = try $ do case Yaml.decodeEither' $ UTF8.fromString rawYaml of Right (Yaml.Object hashmap) -> do let alist = H.toList hashmap - mapM_ (\(k, v) -> do + mapM_ (\(k, v) -> if ignorable k then return () else do @@ -320,7 +320,7 @@ yamlToMeta (Yaml.Array xs) = do return $ B.toMetaValue xs'' yamlToMeta (Yaml.Object o) = do let alist = H.toList o - foldM (\m (k,v) -> do + foldM (\m (k,v) -> if ignorable k then return m else do @@ -353,7 +353,7 @@ kvPair allowEmpty = try $ do (try $ newline >> lookAhead (blankline <|> nonspaceChar)) guard $ allowEmpty || not (null val) let key' = concat $ words $ map toLower key - let val' = MetaBlocks $ B.toList $ B.plain $ B.text $ val + let val' = MetaBlocks $ B.toList $ B.plain $B.text val return (key',val') parseMarkdown :: PandocMonad m => MarkdownParser m Pandoc @@ -364,8 +364,7 @@ parseMarkdown = do -- check for notes with no corresponding note references let notesUsed = stateNoteRefs st let notesDefined = M.keys (stateNotes' st) - mapM_ (\n -> unless (n `Set.member` notesUsed) $ do - -- lookup to get sourcepos + mapM_ (\n -> unless (n `Set.member` notesUsed) $ case M.lookup n (stateNotes' st) of Just (pos, _) -> report (NoteDefinedButNotUsed n pos) Nothing -> throwError $ @@ -384,7 +383,7 @@ referenceKey = try $ do (_,raw) <- reference char ':' skipSpaces >> optional newline >> skipSpaces >> notFollowedBy (char '[') - let sourceURL = liftM unwords $ many $ try $ do + let sourceURL = fmap unwords $ many $ try $ do skipMany spaceChar notFollowedBy' referenceTitle notFollowedBy' $ guardEnabled Ext_link_attributes >> attributes @@ -533,7 +532,7 @@ atxChar = do atxHeader :: PandocMonad m => MarkdownParser m (F Blocks) atxHeader = try $ do - level <- atxChar >>= many1 . char >>= return . length + level <- fmap length (atxChar >>= many1 . char) notFollowedBy $ guardEnabled Ext_fancy_lists >> (char '.' <|> char ')') -- this would be a list guardDisabled Ext_space_in_atx_header <|> notFollowedBy nonspaceChar @@ -588,7 +587,7 @@ setextHeader = try $ do underlineChar <- oneOf setextHChars many (char underlineChar) blanklines - let level = (fromMaybe 0 $ findIndex (== underlineChar) setextHChars) + 1 + let level = fromMaybe 0 (findIndex (== underlineChar) setextHChars) + 1 attr' <- registerHeader attr (runF text defaultParserState) guardDisabled Ext_implicit_header_references <|> registerImplicitHeader raw attr' @@ -629,8 +628,7 @@ blockDelimiter f len = try $ do c <- lookAhead (satisfy f) case len of Just l -> count l (char c) >> many (char c) >> return l - Nothing -> count 3 (char c) >> many (char c) >>= - return . (+ 3) . length + Nothing -> fmap ((+ 3) . length) (count 3 (char c) >> many (char c)) attributes :: PandocMonad m => MarkdownParser m Attr attributes = try $ do @@ -794,7 +792,7 @@ blockQuote :: PandocMonad m => MarkdownParser m (F Blocks) blockQuote = do raw <- emailBlockQuote -- parse the extracted block, which may contain various block elements: - contents <- parseFromString' parseBlocks $ (intercalate "\n" raw) ++ "\n\n" + contents <- parseFromString' parseBlocks $ intercalate "\n" raw ++ "\n\n" return $ B.blockQuote <$> contents -- @@ -840,7 +838,7 @@ orderedListStart mbstydelim = try $ do return (num, style, delim)) listStart :: PandocMonad m => MarkdownParser m () -listStart = bulletListStart <|> (orderedListStart Nothing >> return ()) +listStart = bulletListStart <|> (Control.Monad.void (orderedListStart Nothing)) listLine :: PandocMonad m => Int -> MarkdownParser m String listLine continuationIndent = try $ do @@ -854,7 +852,7 @@ listLine continuationIndent = try $ do listLineCommon :: PandocMonad m => MarkdownParser m String listLineCommon = concat <$> manyTill ( many1 (satisfy $ \c -> c /= '\n' && c /= '<') - <|> liftM snd (htmlTag isCommentTag) + <|> fmap snd (htmlTag isCommentTag) <|> count 1 anyChar ) newline @@ -973,7 +971,7 @@ defRawBlock compact = try $ do <|> notFollowedBy defListMarker anyLine ) rawlines <- many dline - cont <- liftM concat $ many $ try $ do + cont <- fmap concat $ many $ try $ do trailing <- option "" blanklines ln <- indentSpaces >> notFollowedBy blankline >> anyLine lns <- many dline @@ -984,7 +982,7 @@ defRawBlock compact = try $ do definitionList :: PandocMonad m => MarkdownParser m (F Blocks) definitionList = try $ do lookAhead (anyLine >> - optional (blankline >> notFollowedBy (table >> return ())) >> + optional (blankline >> notFollowedBy (Control.Monad.void table)) >> -- don't capture table caption as def list! defListMarker) compactDefinitionList <|> normalDefinitionList @@ -1052,7 +1050,7 @@ plain = fmap B.plain . trimInlinesF <$> inlines1 htmlElement :: PandocMonad m => MarkdownParser m String htmlElement = rawVerbatimBlock <|> strictHtmlBlock - <|> liftM snd (htmlTag isBlockTag) + <|> fmap snd (htmlTag isBlockTag) htmlBlock :: PandocMonad m => MarkdownParser m (F Blocks) htmlBlock = do @@ -1183,17 +1181,17 @@ simpleTableHeader headless = try $ do let (lengths, lines') = unzip dashes let indices = scanl (+) (length initSp) lines' -- If no header, calculate alignment on basis of first row of text - rawHeads <- liftM (tail . splitStringByIndices (init indices)) $ + rawHeads <- fmap (tail . splitStringByIndices (init indices)) $ if headless then lookAhead anyLine else return rawContent - let aligns = zipWith alignType (map (\a -> [a]) rawHeads) lengths + let aligns = zipWith alignType (map ((: [])) rawHeads) lengths let rawHeads' = if headless then replicate (length dashes) "" else rawHeads heads <- fmap sequence - $ mapM (parseFromString' (mconcat <$> many plain)) - $ map trim rawHeads' + $ + mapM ((parseFromString' (mconcat <$> many plain)).trim) rawHeads' return (heads, aligns, indices) -- Returns an alignment type for a table, based on a list of strings @@ -1295,7 +1293,7 @@ multilineTableHeader headless = try $ do let (lengths, lines') = unzip dashes let indices = scanl (+) (length initSp) lines' rawHeadsList <- if headless - then liftM (map (:[]) . tail . + then fmap (map (:[]) . tail . splitStringByIndices (init indices)) $ lookAhead anyLine else return $ transpose $ map (tail . splitStringByIndices (init indices)) @@ -1305,8 +1303,7 @@ multilineTableHeader headless = try $ do then replicate (length dashes) "" else map (unlines . map trim) rawHeadsList heads <- fmap sequence $ - mapM (parseFromString' (mconcat <$> many plain)) $ - map trim rawHeads + mapM ((parseFromString' (mconcat <$> many plain)).trim) rawHeads return (heads, aligns, indices) -- Parse a grid table: starts with row of '-' on top, then header @@ -1345,7 +1342,7 @@ pipeTable = try $ do fromIntegral (len + 1) / fromIntegral numColumns) seplengths else replicate (length aligns) 0.0 - return $ (aligns, widths, heads', sequence lines'') + return (aligns, widths, heads', sequence lines'') sepPipe :: PandocMonad m => MarkdownParser m () sepPipe = try $ do @@ -1363,7 +1360,7 @@ pipeTableRow = try $ do <|> void (noneOf "|\n\r") let cellContents = ((trim . snd) <$> withRaw (many chunk)) >>= parseFromString' pipeTableCell - cells <- cellContents `sepEndBy1` (char '|') + cells <- cellContents `sepEndBy1` char '|' -- surrounding pipes needed for a one-column table: guard $ not (length cells == 1 && not openPipe) blankline @@ -1383,7 +1380,7 @@ pipeTableHeaderPart = try $ do right <- optionMaybe (char ':') skipMany spaceChar let len = length pipe + maybe 0 (const 1) left + maybe 0 (const 1) right - return $ + return ((case (left,right) of (Nothing,Nothing) -> AlignDefault (Just _,Nothing) -> AlignLeft @@ -1412,10 +1409,10 @@ tableWith headerParser rowParser lineParser footerParser = try $ do lines' <- fmap sequence $ rowParser indices `sepEndBy1` lineParser footerParser numColumns <- getOption readerColumns - let widths = if (indices == []) + let widths = if indices == [] then replicate (length aligns) 0.0 else widthsFromIndices numColumns indices - return $ (aligns, widths, heads, lines') + return (aligns, widths, heads, lines') table :: PandocMonad m => MarkdownParser m (F Blocks) table = try $ do @@ -1573,7 +1570,7 @@ enclosure c = do <|> (guard =<< notAfterString) cs <- many1 (char c) (return (B.str cs) <>) <$> whitespace - <|> do + <|> case length cs of 3 -> three c 2 -> two c mempty @@ -1723,7 +1720,7 @@ source = do skipSpaces let urlChunk = try parenthesizedChars - <|> (notFollowedBy (oneOf " )") >> (count 1 litChar)) + <|> (notFollowedBy (oneOf " )") >> count 1 litChar) <|> try (many1 spaceChar <* notFollowedBy (oneOf "\"')")) let sourceURL = (unwords . words . concat) <$> many urlChunk let betweenAngles = try $ @@ -1892,8 +1889,8 @@ rawConTeXtEnvironment :: PandocMonad m => ParserT [Char] st m String rawConTeXtEnvironment = try $ do string "\\start" completion <- inBrackets (letter <|> digit <|> spaceChar) - <|> (many1 letter) - contents <- manyTill (rawConTeXtEnvironment <|> (count 1 anyChar)) + <|> many1 letter + contents <- manyTill (rawConTeXtEnvironment <|> count 1 anyChar) (try $ string "\\stop" >> string completion) return $ "\\start" ++ completion ++ concat contents ++ "\\stop" ++ completion @@ -1999,10 +1996,9 @@ emoji = try $ do cite :: PandocMonad m => MarkdownParser m (F Inlines) cite = do guardEnabled Ext_citations - citations <- textualCite + textualCite <|> do (cs, raw) <- withRaw normalCite return $ (flip B.cite (B.text raw)) <$> cs - return citations textualCite :: PandocMonad m => MarkdownParser m (F Inlines) textualCite = try $ do @@ -2076,7 +2072,7 @@ suffix = try $ do prefix :: PandocMonad m => MarkdownParser m (F Inlines) prefix = trimInlinesF . mconcat <$> - manyTill inline (char ']' <|> liftM (const ']') (lookAhead citeKey)) + manyTill inline (char ']' <|> fmap (const ']') (lookAhead citeKey)) citeList :: PandocMonad m => MarkdownParser m (F [Citation]) citeList = fmap sequence $ sepBy1 citation (try $ char ';' >> spnl) diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 6f9b9b3c2..6cc505e3b 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -137,7 +137,7 @@ parseHtmlContentWithAttrs tag parser = do endOfContent = try $ skipMany blankline >> skipSpaces >> eof parseHtmlContent :: PandocMonad m => String -> MuseParser m a -> MuseParser m [a] -parseHtmlContent tag p = liftM snd (parseHtmlContentWithAttrs tag p) +parseHtmlContent tag p = fmap snd (parseHtmlContentWithAttrs tag p) -- -- directive parsers @@ -213,7 +213,7 @@ header = try $ do st <- stateParserContext <$> getState q <- stateQuoteContext <$> getState getPosition >>= \pos -> guard (st == NullState && q == NoQuote && sourceColumn pos == 1) - level <- liftM length $ many1 $ char '*' + level <- fmap length $ many1 $ char '*' guard $ level <= 5 spaceChar content <- trimInlinesF . mconcat <$> manyTill inline eol @@ -240,7 +240,7 @@ exampleTag = do chop = lchop . rchop literal :: PandocMonad m => MuseParser m (F Blocks) -literal = liftM (return . rawBlock) $ htmlElement "literal" +literal = fmap (return . rawBlock) $ htmlElement "literal" where format (_, _, kvs) = fromMaybe "html" $ lookup "format" kvs rawBlock (attrs, content) = B.rawBlock (format attrs) content @@ -268,7 +268,7 @@ quoteTag = withQuoteContext InDoubleQuote $ blockTag B.blockQuote "quote" divTag :: PandocMonad m => MuseParser m (F Blocks) divTag = do (attrs, content) <- parseHtmlContentWithAttrs "div" block - return $ (B.divWith attrs) <$> mconcat content + return $ B.divWith attrs <$> mconcat content verseLine :: PandocMonad m => MuseParser m String verseLine = do @@ -296,7 +296,7 @@ para :: PandocMonad m => MuseParser m (F Blocks) para = do indent <- length <$> many spaceChar let f = if indent >= 2 && indent < 6 then B.blockQuote else id - liftM (f . B.para) . trimInlinesF . mconcat <$> many1Till inline endOfParaElement + fmap (f . B.para) . trimInlinesF . mconcat <$> many1Till inline endOfParaElement where endOfParaElement = lookAhead $ endOfInput <|> endOfPara <|> newBlockElement endOfInput = try $ skipMany blankline >> skipSpaces >> eof @@ -481,7 +481,7 @@ museAppendElement tbl element = return tbl{ museTableCaption = inlines' } tableCell :: PandocMonad m => MuseParser m (F Blocks) -tableCell = try $ liftM B.plain . trimInlinesF . mconcat <$> manyTill inline (lookAhead cellEnd) +tableCell = try $ fmap B.plain . trimInlinesF . mconcat <$> manyTill inline (lookAhead cellEnd) where cellEnd = try $ void (many1 spaceChar >> char '|') <|> eol tableElements :: PandocMonad m => MuseParser m [MuseTableElement] @@ -575,7 +575,7 @@ footnote = try $ do return $ B.note contents' whitespace :: PandocMonad m => MuseParser m (F Inlines) -whitespace = liftM return (lb <|> regsp) +whitespace = fmap return (lb <|> regsp) where lb = try $ skipMany spaceChar >> linebreak >> return B.space regsp = try $ skipMany1 spaceChar >> return B.space @@ -655,10 +655,10 @@ codeTag = do return $ return $ B.codeWith attrs $ fromEntities content str :: PandocMonad m => MuseParser m (F Inlines) -str = liftM (return . B.str) (many1 alphaNum <|> count 1 characterReference) +str = fmap (return . B.str) (many1 alphaNum <|> count 1 characterReference) symbol :: PandocMonad m => MuseParser m (F Inlines) -symbol = liftM (return . B.str) $ count 1 nonspaceChar +symbol = fmap (return . B.str) $ count 1 nonspaceChar link :: PandocMonad m => MuseParser m (F Inlines) link = try $ do diff --git a/src/Text/Pandoc/Readers/Native.hs b/src/Text/Pandoc/Readers/Native.hs index d065bff8d..ce33e080b 100644 --- a/src/Text/Pandoc/Readers/Native.hs +++ b/src/Text/Pandoc/Readers/Native.hs @@ -69,4 +69,3 @@ readInlines s = maybe ((:[]) <$> readInline s) Right (safeRead (unpack s)) readInline :: Text -> Either PandocError Inline readInline s = maybe (Left . PandocParseError $ "Could not read: " ++ unpack s) Right (safeRead (unpack s)) - diff --git a/src/Text/Pandoc/Readers/OPML.hs b/src/Text/Pandoc/Readers/OPML.hs index 167ad6d4e..e3ef67bc1 100644 --- a/src/Text/Pandoc/Readers/OPML.hs +++ b/src/Text/Pandoc/Readers/OPML.hs @@ -4,6 +4,7 @@ import Control.Monad.State.Strict import Data.Char (toUpper) import Data.Default import Data.Generics +import Data.Maybe (fromMaybe) import Data.Text (Text, pack, unpack) import Text.HTML.TagSoup.Entity (lookupEntity) import Text.Pandoc.Builder @@ -32,9 +33,9 @@ instance Default OPMLState where readOPML :: PandocMonad m => ReaderOptions -> Text -> m Pandoc readOPML _ inp = do - (bs, st') <- flip runStateT def + (bs, st') <- runStateT (mapM parseBlock $ normalizeTree $ - parseXML (unpack (crFilter inp))) + parseXML (unpack (crFilter inp))) def return $ setTitle (opmlDocTitle st') $ setAuthors (opmlDocAuthors st') $ @@ -62,9 +63,7 @@ convertEntity e = maybe (map toUpper e) id (lookupEntity e) -- convenience function to get an attribute value, defaulting to "" attrValue :: String -> Element -> String attrValue attr elt = - case lookupAttrBy (\x -> qName x == attr) (elAttribs elt) of - Just z -> z - Nothing -> "" + fromMaybe "" (lookupAttrBy (\x -> qName x == attr) (elAttribs elt)) -- exceptT :: PandocMonad m => Either PandocError a -> OPML m a -- exceptT = either throwError return @@ -73,13 +72,13 @@ asHtml :: PandocMonad m => String -> OPML m Inlines asHtml s = (\(Pandoc _ bs) -> case bs of [Plain ils] -> fromList ils - _ -> mempty) <$> (lift $ readHtml def (pack s)) + _ -> mempty) <$> lift (readHtml def (pack s)) asMarkdown :: PandocMonad m => String -> OPML m Blocks -asMarkdown s = (\(Pandoc _ bs) -> fromList bs) <$> (lift $ readMarkdown def (pack s)) +asMarkdown s = (\(Pandoc _ bs) -> fromList bs) <$> lift (readMarkdown def (pack s)) getBlocks :: PandocMonad m => Element -> OPML m Blocks -getBlocks e = mconcat <$> (mapM parseBlock $ elContent e) +getBlocks e = mconcat <$> mapM parseBlock (elContent e) parseBlock :: PandocMonad m => Content -> OPML m Blocks parseBlock (Elem e) = diff --git a/src/Text/Pandoc/Readers/Odt/Arrows/State.hs b/src/Text/Pandoc/Readers/Odt/Arrows/State.hs index 0f7483431..06b2dcaaa 100644 --- a/src/Text/Pandoc/Readers/Odt/Arrows/State.hs +++ b/src/Text/Pandoc/Readers/Odt/Arrows/State.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE Arrows #-} + {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE TupleSections #-} {- @@ -139,7 +139,7 @@ iterateS :: (Foldable f, MonadPlus m) => ArrowState s x y -> ArrowState s (f x) (m y) iterateS a = ArrowState $ \(s,f) -> foldr a' (s,mzero) f - where a' x (s',m) = second ((mplus m).return) $ runArrowState a (s',x) + where a' x (s',m) = second (mplus m.return) $ runArrowState a (s',x) -- | Fold a state arrow through something 'Foldable'. Collect the results in a -- 'MonadPlus'. @@ -147,7 +147,7 @@ iterateSL :: (Foldable f, MonadPlus m) => ArrowState s x y -> ArrowState s (f x) (m y) iterateSL a = ArrowState $ \(s,f) -> foldl a' (s,mzero) f - where a' (s',m) x = second ((mplus m).return) $ runArrowState a (s',x) + where a' (s',m) x = second (mplus m.return) $ runArrowState a (s',x) -- | Fold a fallible state arrow through something 'Foldable'. diff --git a/src/Text/Pandoc/Readers/Odt/Base.hs b/src/Text/Pandoc/Readers/Odt/Base.hs index f8a0b86e7..51c2da788 100644 --- a/src/Text/Pandoc/Readers/Odt/Base.hs +++ b/src/Text/Pandoc/Readers/Odt/Base.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE PatternGuards #-} + {- Copyright (C) 2015 Martin Linnemann <theCodingMarlin@googlemail.com> @@ -40,4 +40,3 @@ type OdtConverterState s = XMLConverterState Namespace s type XMLReader s a b = FallibleXMLConverter Namespace s a b type XMLReaderSafe s a b = XMLConverter Namespace s a b - diff --git a/src/Text/Pandoc/Readers/Odt/Generic/Fallible.hs b/src/Text/Pandoc/Readers/Odt/Generic/Fallible.hs index 72509e591..f8ea5c605 100644 --- a/src/Text/Pandoc/Readers/Odt/Generic/Fallible.hs +++ b/src/Text/Pandoc/Readers/Odt/Generic/Fallible.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE GeneralizedNewtypeDeriving #-} + {- Copyright (C) 2015 Martin Linnemann <theCodingMarlin@googlemail.com> @@ -121,6 +121,6 @@ newtype SuccessList a = SuccessList { collectNonFailing :: [a] } deriving ( Eq, Ord, Show ) instance ChoiceVector SuccessList where - spreadChoice = Right . SuccessList . (foldr unTagRight []) . collectNonFailing + spreadChoice = Right . SuccessList . foldr unTagRight [] . collectNonFailing where unTagRight (Right x) = (x:) unTagRight _ = id diff --git a/src/Text/Pandoc/Readers/Odt/Generic/Utils.hs b/src/Text/Pandoc/Readers/Odt/Generic/Utils.hs index f492ec944..556517259 100644 --- a/src/Text/Pandoc/Readers/Odt/Generic/Utils.hs +++ b/src/Text/Pandoc/Readers/Odt/Generic/Utils.hs @@ -1,5 +1,5 @@ -{-# LANGUAGE PatternGuards #-} -{-# LANGUAGE TupleSections #-} + + {-# LANGUAGE TypeOperators #-} {-# LANGUAGE ViewPatterns #-} diff --git a/src/Text/Pandoc/Readers/Textile.hs b/src/Text/Pandoc/Readers/Textile.hs index a57ab93d7..f8c2b8cb7 100644 --- a/src/Text/Pandoc/Readers/Textile.hs +++ b/src/Text/Pandoc/Readers/Textile.hs @@ -110,7 +110,7 @@ noteBlock = try $ do startPos <- getPosition ref <- noteMarker optional blankline - contents <- liftM unlines $ many1Till anyLine (blanklines <|> noteBlock) + contents <- fmap unlines $ many1Till anyLine (blanklines <|> noteBlock) endPos <- getPosition let newnote = (ref, contents ++ "\n") st <- getState diff --git a/src/Text/Pandoc/Readers/TikiWiki.hs b/src/Text/Pandoc/Readers/TikiWiki.hs index 16d6e633b..ad35a6935 100644 --- a/src/Text/Pandoc/Readers/TikiWiki.hs +++ b/src/Text/Pandoc/Readers/TikiWiki.hs @@ -22,7 +22,6 @@ module Text.Pandoc.Readers.TikiWiki ( readTikiWiki import Control.Monad import Control.Monad.Except (throwError) import qualified Data.Foldable as F -import Data.List (intercalate) import Data.Maybe (fromMaybe) import Data.Text (Text) import qualified Data.Text as T @@ -58,7 +57,7 @@ tryMsg :: PandocMonad m => String -> TikiWikiParser m a -> TikiWikiParser m a tryMsg msg p = try p <?> msg skip :: PandocMonad m => TikiWikiParser m a -> TikiWikiParser m () -skip parser = parser >> return () +skip parser = Control.Monad.void parser nested :: PandocMonad m => TikiWikiParser m a -> TikiWikiParser m a nested p = do @@ -88,7 +87,7 @@ block = do <|> blockElements <|> para skipMany blankline - when (verbosity >= INFO) $ do + when (verbosity >= INFO) $ trace (printf "line %d: %s" (sourceLine pos) (take 60 $ show $ B.toList res)) return res @@ -112,7 +111,7 @@ hr = try $ do string "----" many (char '-') newline - return $ B.horizontalRule + return B.horizontalRule -- ! header -- @@ -122,18 +121,18 @@ hr = try $ do -- header :: PandocMonad m => TikiWikiParser m B.Blocks header = tryMsg "header" $ do - level <- many1 (char '!') >>= return . length + level <- fmap length (many1 (char '!')) guard $ level <= 6 skipSpaces content <- B.trimInlines . mconcat <$> manyTill inline newline attr <- registerHeader nullAttr content - return $ B.headerWith attr level $ content + return $B.headerWith attr level content tableRow :: PandocMonad m => TikiWikiParser m [B.Blocks] tableRow = try $ do -- row <- sepBy1 (many1Till inline $ oneOf "\n|") (try $ string "|" <* notFollowedBy (oneOf "|\n")) -- return $ map (B.plain . mconcat) row - row <- sepBy1 ((many1 $ noneOf "\n|") >>= parseColumn) (try $ string "|" <* notFollowedBy (oneOf "|\n")) + row <- sepBy1 (many1 (noneOf "\n|") >>= parseColumn) (try $ string "|" <* notFollowedBy (oneOf "|\n")) return $ map B.plain row where parseColumn x = do @@ -165,14 +164,14 @@ table = try $ do string "||" newline -- return $ B.simpleTable (headers rows) $ trace ("rows: " ++ (show rows)) rows - return $ B.simpleTable (headers rows) $ rows + return $B.simpleTable (headers rows) rows where -- The headers are as many empty srings as the number of columns -- in the first row - headers rows = map (B.plain . B.str) $ take (length $ rows !! 0) $ repeat "" + headers rows = map (B.plain . B.str) $replicate (length $ rows !! 0) "" para :: PandocMonad m => TikiWikiParser m B.Blocks -para = many1Till inline endOfParaElement >>= return . result . mconcat +para = fmap (result . mconcat) ( many1Till inline endOfParaElement) where endOfParaElement = lookAhead $ endOfInput <|> endOfPara <|> newBlockElement endOfInput = try $ skipMany blankline >> skipSpaces >> eof @@ -189,7 +188,7 @@ para = many1Till inline endOfParaElement >>= return . result . mconcat -- definitionList :: PandocMonad m => TikiWikiParser m B.Blocks definitionList = tryMsg "definitionList" $ do - elements <- many1 $ parseDefinitionListItem + elements <-many1 parseDefinitionListItem return $ B.definitionList elements where parseDefinitionListItem :: PandocMonad m => TikiWikiParser m (B.Inlines, [B.Blocks]) @@ -197,7 +196,7 @@ definitionList = tryMsg "definitionList" $ do skipSpaces >> char ';' <* skipSpaces term <- many1Till inline $ char ':' <* skipSpaces line <- listItemLine 1 - return $ (mconcat term, [B.plain line]) + return (mconcat term, [B.plain line]) data ListType = None | Numbered | Bullet deriving (Ord, Eq, Show) @@ -233,15 +232,15 @@ mixedList = try $ do -- figre out a fold or something. fixListNesting :: [B.Blocks] -> [B.Blocks] fixListNesting [] = [] -fixListNesting (first:[]) = [recurseOnList first] +fixListNesting [first] = [recurseOnList first] -- fixListNesting nestall | trace ("\n\nfixListNesting: " ++ (show nestall)) False = undefined -- fixListNesting nestall@(first:second:rest) = fixListNesting (first:second:rest) = let secondBlock = head $ B.toList second in case secondBlock of - BulletList _ -> fixListNesting $ [(mappend (recurseOnList first) (recurseOnList second))] ++ rest - OrderedList _ _ -> fixListNesting $ [(mappend (recurseOnList first) (recurseOnList second))] ++ rest - _ -> [recurseOnList first] ++ fixListNesting (second:rest) + BulletList _ -> fixListNesting $ (mappend (recurseOnList first) (recurseOnList second)) : rest + OrderedList _ _ -> fixListNesting $ (mappend (recurseOnList first) (recurseOnList second)) : rest + _ -> recurseOnList first : fixListNesting (second:rest) -- This function walks the Block structure for fixListNesting, -- because it's a bit complicated, what with converting to and from @@ -249,7 +248,7 @@ fixListNesting (first:second:rest) = recurseOnList :: B.Blocks -> B.Blocks -- recurseOnList item | trace ("rOL: " ++ (show $ length $ B.toList item) ++ ", " ++ (show $ B.toList item)) False = undefined recurseOnList items - | (length $ B.toList items) == 1 = + | length (B.toList items) == 1 = let itemBlock = head $ B.toList items in case itemBlock of BulletList listItems -> B.bulletList $ fixListNesting $ map B.fromList listItems @@ -272,11 +271,11 @@ recurseOnList items -- sections. spanFoldUpList :: ListNesting -> [(ListNesting, B.Blocks)] -> [B.Blocks] spanFoldUpList _ [] = [] -spanFoldUpList ln (first:[]) = +spanFoldUpList ln [first] = listWrap ln (fst first) [snd first] spanFoldUpList ln (first:rest) = let (span1, span2) = span (splitListNesting (fst first)) rest - newTree1 = listWrap ln (fst first) $ [snd first] ++ spanFoldUpList (fst first) span1 + newTree1 = listWrap ln (fst first) $ snd first : spanFoldUpList (fst first) span1 newTree2 = spanFoldUpList ln span2 in newTree1 ++ newTree2 @@ -285,14 +284,13 @@ spanFoldUpList ln (first:rest) = -- item, which is true if the second item is at a deeper nesting -- level and of the same type. splitListNesting :: ListNesting -> (ListNesting, B.Blocks) -> Bool -splitListNesting ln1 (ln2, _) = - if (lnnest ln1) < (lnnest ln2) then - True - else - if ln1 == ln2 then - True - else - False +splitListNesting ln1 (ln2, _) + | (lnnest ln1) < (lnnest ln2) = + True + | ln1 == ln2 = + True + | otherwise = + False -- If we've moved to a deeper nesting level, wrap the new level in -- the appropriate type of list. @@ -323,7 +321,7 @@ bulletItem = try $ do prefix <- many1 $ char '*' many1 $ char ' ' content <- listItemLine (length prefix) - return $ (LN Bullet (length prefix), B.plain content) + return (LN Bullet (length prefix), B.plain content) -- # Start each line -- # with a number (1.). @@ -335,17 +333,17 @@ numberedItem = try $ do prefix <- many1 $ char '#' many1 $ char ' ' content <- listItemLine (length prefix) - return $ (LN Numbered (length prefix), B.plain content) + return (LN Numbered (length prefix), B.plain content) listItemLine :: PandocMonad m => Int -> TikiWikiParser m B.Inlines -listItemLine nest = lineContent >>= parseContent >>= return +listItemLine nest = lineContent >>= parseContent where lineContent = do content <- anyLine continuation <- optionMaybe listContinuation - return $ filterSpaces content ++ "\n" ++ (maybe "" id continuation) + return $ filterSpaces content ++ "\n" ++ maybe "" id continuation filterSpaces = reverse . dropWhile (== ' ') . reverse - listContinuation = string (take nest (repeat '+')) >> lineContent + listContinuation = string (replicate nest '+') >> lineContent parseContent x = do parsed <- parseFromString (many1 inline) x return $ mconcat parsed @@ -373,7 +371,7 @@ codeMacro = try $ do string ")}" body <- manyTill anyChar (try (string "{CODE}")) newline - if length rawAttrs > 0 + if not (null rawAttrs) then return $ B.codeBlockWith (mungeAttrs rawAttrs) body else @@ -412,7 +410,7 @@ inline = choice [ whitespace ] <?> "inline" whitespace :: PandocMonad m => TikiWikiParser m B.Inlines -whitespace = (lb <|> regsp) >>= return +whitespace = (lb <|> regsp) where lb = try $ skipMany spaceChar >> linebreak >> return B.space regsp = try $ skipMany1 spaceChar >> return B.space @@ -452,7 +450,7 @@ enclosed sep p = between sep (try $ sep <* endMarker) p nestedInlines :: (Show a, PandocMonad m) => TikiWikiParser m a -> TikiWikiParser m B.Inlines nestedInlines end = innerSpace <|> nestedInline where - innerSpace = try $ whitespace <* (notFollowedBy end) + innerSpace = try $ whitespace <* notFollowedBy end nestedInline = notFollowedBy whitespace >> nested inline -- {img attId="39" imalign="right" link="http://info.tikiwiki.org" alt="Panama Hat"} @@ -470,13 +468,13 @@ image = try $ do let title = fromMaybe src $ lookup "desc" rawAttrs let alt = fromMaybe title $ lookup "alt" rawAttrs let classes = map fst $ filter (\(_,b) -> b == "" || b == "y") rawAttrs - if length src > 0 + if not (null src) then return $ B.imageWith ("", classes, rawAttrs) src title (B.str alt) else - return $ B.str $ " NOT SUPPORTED: image without src attribute BEGIN: {img " ++ (printAttrs rawAttrs) ++ "} :END " + return $ B.str $ " NOT SUPPORTED: image without src attribute BEGIN: {img " ++ printAttrs rawAttrs ++ "} :END " where - printAttrs attrs = intercalate " " $ map (\(a, b) -> a ++ "=\"" ++ b ++ "\"") attrs + printAttrs attrs = unwords $ map (\(a, b) -> a ++ "=\"" ++ b ++ "\"") attrs imageAttr :: PandocMonad m => TikiWikiParser m (String, String) imageAttr = try $ do @@ -491,11 +489,11 @@ imageAttr = try $ do -- __strong__ strong :: PandocMonad m => TikiWikiParser m B.Inlines -strong = try $ enclosed (string "__") nestedInlines >>= return . B.strong +strong = try $ fmap B.strong (enclosed (string "__") nestedInlines) -- ''emph'' emph :: PandocMonad m => TikiWikiParser m B.Inlines -emph = try $ enclosed (string "''") nestedInlines >>= return . B.emph +emph = try $ fmap B.emph (enclosed (string "''") nestedInlines) -- ~246~ escapedChar :: PandocMonad m => TikiWikiParser m B.Inlines @@ -503,7 +501,7 @@ escapedChar = try $ do string "~" inner <- many1 $ oneOf "0123456789" string "~" - return $ B.str $ [(toEnum ((read inner) :: Int)) :: Char] + return $B.str [(toEnum ((read inner) :: Int)) :: Char] -- UNSUPPORTED, as there doesn't seem to be any facility in calibre -- for this @@ -543,10 +541,10 @@ boxed = try $ do -- --text-- strikeout :: PandocMonad m => TikiWikiParser m B.Inlines -strikeout = try $ enclosed (string "--") nestedInlines >>= return . B.strikeout +strikeout = try $ fmap B.strikeout (enclosed (string "--") nestedInlines) nestedString :: (Show a, PandocMonad m) => TikiWikiParser m a -> TikiWikiParser m String -nestedString end = innerSpace <|> (count 1 nonspaceChar) +nestedString end = innerSpace <|> count 1 nonspaceChar where innerSpace = try $ many1 spaceChar <* notFollowedBy end @@ -555,7 +553,7 @@ breakChars = try $ string "%%%" >> return B.linebreak -- superscript: foo{TAG(tag=>sup)}super{TAG}foo / bar{SUP()}super2{SUP}bar superTag :: PandocMonad m => TikiWikiParser m B.Inlines -superTag = try $ between (string "{TAG(tag=>sup)}") (string "{TAG}") nestedString >>= return . B.superscript . B.text . fromEntities +superTag = try $ fmap (B.superscript . B.text . fromEntities) ( between (string "{TAG(tag=>sup)}") (string "{TAG}") nestedString) superMacro :: PandocMonad m => TikiWikiParser m B.Inlines superMacro = try $ do @@ -566,7 +564,7 @@ superMacro = try $ do -- subscript: baz{TAG(tag=>sub)}sub{TAG}qux / qux{SUB()}sub2{SUB}qux subTag :: PandocMonad m => TikiWikiParser m B.Inlines -subTag = try $ between (string "{TAG(tag=>sub)}") (string "{TAG}") nestedString >>= return . B.subscript . B.text . fromEntities +subTag = try $ fmap (B.subscript . B.text . fromEntities) ( between (string "{TAG(tag=>sub)}") (string "{TAG}") nestedString) subMacro :: PandocMonad m => TikiWikiParser m B.Inlines subMacro = try $ do @@ -577,7 +575,7 @@ subMacro = try $ do -- -+text+- code :: PandocMonad m => TikiWikiParser m B.Inlines -code = try $ between (string "-+") (string "+-") nestedString >>= return . B.code . fromEntities +code = try $ fmap (B.code . fromEntities) ( between (string "-+") (string "+-") nestedString) macroAttr :: PandocMonad m => TikiWikiParser m (String, String) macroAttr = try $ do @@ -590,8 +588,7 @@ macroAttr = try $ do macroAttrs :: PandocMonad m => TikiWikiParser m [(String, String)] macroAttrs = try $ do - attrs <- sepEndBy macroAttr spaces - return attrs + sepEndBy macroAttr spaces -- ~np~ __not bold__ ~/np~ noparse :: PandocMonad m => TikiWikiParser m B.Inlines @@ -601,10 +598,10 @@ noparse = try $ do return $ B.str body str :: PandocMonad m => TikiWikiParser m B.Inlines -str = (many1 alphaNum <|> count 1 characterReference) >>= return . B.str +str = fmap B.str (many1 alphaNum <|> count 1 characterReference) symbol :: PandocMonad m => TikiWikiParser m B.Inlines -symbol = count 1 nonspaceChar >>= return . B.str +symbol = fmap B.str (count 1 nonspaceChar) -- [[not a link] notExternalLink :: PandocMonad m => TikiWikiParser m B.Inlines @@ -627,7 +624,7 @@ makeLink start middle end = try $ do (url, title, anchor) <- wikiLinkText start middle end parsedTitle <- parseFromString (many1 inline) title setState $ st{ stateAllowLinks = True } - return $ B.link (url++anchor) "" $ mconcat $ parsedTitle + return $ B.link (url++anchor) "" $mconcat parsedTitle wikiLinkText :: PandocMonad m => String -> String -> String -> TikiWikiParser m (String, String, String) wikiLinkText start middle end = do @@ -643,9 +640,9 @@ wikiLinkText start middle end = do return (url, seg1, "") where linkContent = do - (char '|') + char '|' mystr <- many (noneOf middle) - return $ mystr + return mystr externalLink :: PandocMonad m => TikiWikiParser m B.Inlines externalLink = makeLink "[" "]|" "]" @@ -657,4 +654,3 @@ externalLink = makeLink "[" "]|" "]" -- [see also this other post](My Other Page) is perfectly valid. wikiLink :: PandocMonad m => TikiWikiParser m B.Inlines wikiLink = makeLink "((" ")|" "))" - diff --git a/src/Text/Pandoc/Readers/Vimwiki.hs b/src/Text/Pandoc/Readers/Vimwiki.hs index 37c8c32d0..49da5a6c6 100644 --- a/src/Text/Pandoc/Readers/Vimwiki.hs +++ b/src/Text/Pandoc/Readers/Vimwiki.hs @@ -91,14 +91,13 @@ import Text.Pandoc.Parsing (F, ParserState, ParserT, blanklines, emailAddress, registerHeader, runF, spaceChar, stateMeta', stateOptions, uri) import Text.Pandoc.Shared (crFilter, splitBy, stringify, stripFirstAndLast) -import Text.Parsec.Char (alphaNum, anyChar, char, newline, noneOf, spaces, - string) -import Text.Parsec.Char (oneOf, space) -import Text.Parsec.Combinator (choice, count, eof, many1, manyTill, - notFollowedBy, option, skipMany1) -import Text.Parsec.Combinator (between, lookAhead) -import Text.Parsec.Prim (getState, many, try, updateState) -import Text.Parsec.Prim ((<|>)) +import Text.Parsec.Char + (alphaNum, anyChar, char, newline, noneOf, spaces, string, oneOf, + space) +import Text.Parsec.Combinator + (choice, count, eof, many1, manyTill, notFollowedBy, option, + skipMany1, between, lookAhead) +import Text.Parsec.Prim (getState, many, try, updateState, (<|>)) readVimwiki :: PandocMonad m => ReaderOptions -> Text -> m Pandoc readVimwiki opts s = do @@ -161,9 +160,9 @@ header = try $ do let lev = length eqs guard $ lev <= 6 contents <- trimInlines . mconcat <$> manyTill inline (try $ spaceChar - >> (string eqs) >> many spaceChar >> newline) + >> string eqs >> many spaceChar >> newline) attr <- registerHeader (makeId contents, - (if sp == "" then [] else ["justcenter"]), []) contents + if sp == "" then [] else ["justcenter"], []) contents return $ B.headerWith attr lev contents para :: PandocMonad m => VwParser m Blocks @@ -191,22 +190,22 @@ blockQuote = try $ do definitionList :: PandocMonad m => VwParser m Blocks definitionList = try $ - B.definitionList <$> (many1 (dlItemWithDT <|> dlItemWithoutDT)) + B.definitionList <$> many1 (dlItemWithDT <|> dlItemWithoutDT) dlItemWithDT :: PandocMonad m => VwParser m (Inlines, [Blocks]) dlItemWithDT = do dt <- definitionTerm dds <- many definitionDef - return $ (dt, dds) + return (dt, dds) dlItemWithoutDT :: PandocMonad m => VwParser m (Inlines, [Blocks]) dlItemWithoutDT = do dds <- many1 definitionDef - return $ (mempty, dds) + return (mempty, dds) definitionDef :: PandocMonad m => VwParser m Blocks definitionDef = try $ - (notFollowedBy definitionTerm) >> many spaceChar + notFollowedBy definitionTerm >> many spaceChar >> (definitionDef1 <|> definitionDef2) definitionDef1 :: PandocMonad m => VwParser m Blocks @@ -220,16 +219,16 @@ definitionDef2 = try $ B.plain <$> definitionTerm :: PandocMonad m => VwParser m Inlines definitionTerm = try $ do x <- definitionTerm1 <|> definitionTerm2 - guard $ (stringify x /= "") + guard (stringify x /= "") return x definitionTerm1 :: PandocMonad m => VwParser m Inlines definitionTerm1 = try $ - trimInlines . mconcat <$> manyTill inline' (try $ defMarkerE) + trimInlines . mconcat <$> manyTill inline' (try defMarkerE) definitionTerm2 :: PandocMonad m => VwParser m Inlines definitionTerm2 = try $ trimInlines . mconcat <$> manyTill inline' - (try $ lookAhead $ (defMarkerM >> notFollowedBy hasDefMarkerM)) + (try $lookAhead (defMarkerM >> notFollowedBy hasDefMarkerM)) defMarkerM :: PandocMonad m => VwParser m Char defMarkerM = string "::" >> spaceChar @@ -247,14 +246,14 @@ preformatted = try $ do lookAhead newline contents <- manyTill anyChar (try (char '\n' >> many spaceChar >> string "}}}" >> many spaceChar >> newline)) - if (not $ contents == "") && (head contents == '\n') + if not (contents == "") && (head contents == '\n') then return $ B.codeBlockWith (makeAttr attrText) (tail contents) else return $ B.codeBlockWith (makeAttr attrText) contents makeAttr :: String -> Attr makeAttr s = let xs = splitBy (`elem` " \t") s in - ("", [], catMaybes $ map nameValue xs) + ("", [], mapMaybe nameValue xs) nameValue :: String -> Maybe (String, String) nameValue s = @@ -262,7 +261,7 @@ nameValue s = if length t /= 2 then Nothing else let (a, b) = (head t, last t) in - if ((length b) < 2) || ((head b, last b) /= ('"', '"')) + if (length b < 2) || ((head b, last b) /= ('"', '"')) then Nothing else Just (a, stripFirstAndLast b) @@ -317,12 +316,12 @@ mixedList' prevInd = do if lowInd >= curInd then do (sameIndList, endInd) <- (mixedList' lowInd) - let curList = (combineList curLine subList) ++ sameIndList + let curList = combineList curLine subList ++ sameIndList if curInd > prevInd then return ([listBuilder curList], endInd) else return (curList, endInd) else do - let (curList, endInd) = ((combineList curLine subList), + let (curList, endInd) = (combineList curLine subList, lowInd) if curInd > prevInd then return ([listBuilder curList], endInd) @@ -335,7 +334,7 @@ plainInlineML' w = do return $ B.plain $ trimInlines $ mconcat $ w:xs plainInlineML :: PandocMonad m => VwParser m Blocks -plainInlineML = (notFollowedBy listStart) >> spaceChar >> plainInlineML' mempty +plainInlineML = notFollowedBy listStart >> spaceChar >> plainInlineML' mempty listItemContent :: PandocMonad m => VwParser m Blocks @@ -372,9 +371,9 @@ makeListMarkerSpan x = combineList :: Blocks -> [Blocks] -> [Blocks] combineList x [y] = case toList y of - [BulletList z] -> [fromList $ (toList x) + [BulletList z] -> [fromList $ toList x ++ [BulletList z]] - [OrderedList attr z] -> [fromList $ (toList x) + [OrderedList attr z] -> [fromList $ toList x ++ [OrderedList attr z]] _ -> x:[y] combineList x xs = x:xs @@ -391,7 +390,7 @@ bulletListMarkers = "ul" <$ (char '*' <|> char '-') orderedListMarkers :: PandocMonad m => VwParser m String orderedListMarkers = - ("ol" <$ (choice $ (orderedListMarker Decimal Period):(($OneParen) + ("ol" <$choice ((orderedListMarker Decimal Period):(($OneParen) <$> orderedListMarker <$> [Decimal, LowerRoman, UpperRoman, LowerAlpha, UpperAlpha]))) <|> ("ol" <$ char '#') @@ -418,11 +417,11 @@ table1 = try $ do table2 :: PandocMonad m => VwParser m ([Blocks], [[Blocks]]) table2 = try $ do trs <- many1 tableRow - return (take (length $ head trs) $ repeat mempty, trs) + return (replicate (length $ head trs) mempty, trs) tableHeaderSeparator :: PandocMonad m => VwParser m () tableHeaderSeparator = try $ do - many spaceChar >> char '|' >> many1 ((many1 $ char '-') >> char '|') + many spaceChar >> char '|' >> many1 (many1 (char '-') >> char '|') >> many spaceChar >> newline return () @@ -438,16 +437,16 @@ tableRow = try $ do tableCell :: PandocMonad m => VwParser m Blocks tableCell = try $ - B.plain <$> trimInlines . mconcat <$> (manyTill inline' (char '|')) + B.plain . trimInlines . mconcat <$> (manyTill inline' (char '|')) placeholder :: PandocMonad m => VwParser m () placeholder = try $ - (choice (ph <$> ["title", "date"])) <|> noHtmlPh <|> templatePh + choice (ph <$> ["title", "date"]) <|> noHtmlPh <|> templatePh ph :: PandocMonad m => String -> VwParser m () ph s = try $ do - many spaceChar >> (string $ '%':s) >> spaceChar - contents <- (trimInlines . mconcat <$> (manyTill inline (lookAhead newline))) + many spaceChar >>string ('%':s) >> spaceChar + contents <- trimInlines . mconcat <$> (manyTill inline (lookAhead newline)) --use lookAhead because of placeholder in the whitespace parser let meta' = return $ B.setMeta s contents nullMeta :: F Meta updateState $ \st -> st { stateMeta' = stateMeta' st <> meta' } @@ -455,17 +454,17 @@ ph s = try $ do noHtmlPh :: PandocMonad m => VwParser m () noHtmlPh = try $ () <$ (many spaceChar >> string "%nohtml" >> many spaceChar - >> (lookAhead newline)) + >> lookAhead newline) templatePh :: PandocMonad m => VwParser m () templatePh = try $ - () <$ (many spaceChar >> string "%template" >> (many $ noneOf "\n") - >> (lookAhead newline)) + () <$ (many spaceChar >> string "%template" >>many (noneOf "\n") + >> lookAhead newline) -- inline parser inline :: PandocMonad m => VwParser m Inlines -inline = choice $ (whitespace endlineP):inlineList +inline = choice $ whitespace endlineP:inlineList inlineList :: PandocMonad m => [VwParser m Inlines] inlineList = [ bareURL @@ -490,18 +489,18 @@ inline' = choice $ whitespace':inlineList -- inline parser for blockquotes inlineBQ :: PandocMonad m => VwParser m Inlines -inlineBQ = choice $ (whitespace endlineBQ):inlineList +inlineBQ = choice $ whitespace endlineBQ:inlineList -- inline parser for mixedlists inlineML :: PandocMonad m => VwParser m Inlines -inlineML = choice $ (whitespace endlineML):inlineList +inlineML = choice $ whitespace endlineML:inlineList str :: PandocMonad m => VwParser m Inlines -str = B.str <$> (many1 $ noneOf $ spaceChars ++ specialChars) +str = B.str <$>many1 (noneOf $ spaceChars ++ specialChars) whitespace :: PandocMonad m => VwParser m () -> VwParser m Inlines whitespace endline = B.space <$ (skipMany1 spaceChar <|> - (try (newline >> (comment <|> placeholder)))) + try (newline >> (comment <|> placeholder))) <|> B.softbreak <$ endline whitespace' :: PandocMonad m => VwParser m Inlines @@ -518,31 +517,31 @@ bareURL = try $ do strong :: PandocMonad m => VwParser m Inlines strong = try $ do s <- lookAhead $ between (char '*') (char '*') (many1 $ noneOf "*") - guard $ (not $ (head s) `elem` spaceChars) - && (not $ (last s) `elem` spaceChars) + guard $ not ((head s) `elem` spaceChars) + &¬ ((last s) `elem` spaceChars) char '*' - contents <- mconcat <$> (manyTill inline' $ char '*' + contents <- mconcat <$>manyTill inline' (char '*' >> notFollowedBy alphaNum) - return $ (B.spanWith ((makeId contents), [], []) mempty) - <> (B.strong contents) + return $ B.spanWith ((makeId contents), [], []) mempty + <> B.strong contents makeId :: Inlines -> String -makeId i = concat (stringify <$> (toList i)) +makeId i = concat (stringify <$> toList i) emph :: PandocMonad m => VwParser m Inlines emph = try $ do s <- lookAhead $ between (char '_') (char '_') (many1 $ noneOf "_") - guard $ (not $ (head s) `elem` spaceChars) - && (not $ (last s) `elem` spaceChars) + guard $ not ((head s) `elem` spaceChars) + &¬ ((last s) `elem` spaceChars) char '_' - contents <- mconcat <$> (manyTill inline' $ char '_' + contents <- mconcat <$>manyTill inline' (char '_' >> notFollowedBy alphaNum) return $ B.emph contents strikeout :: PandocMonad m => VwParser m Inlines strikeout = try $ do string "~~" - contents <- mconcat <$> (many1Till inline' $ string $ "~~") + contents <- mconcat <$>many1Till inline' (string $ "~~") return $ B.strikeout contents code :: PandocMonad m => VwParser m Inlines @@ -553,11 +552,11 @@ code = try $ do superscript :: PandocMonad m => VwParser m Inlines superscript = try $ - B.superscript <$> mconcat <$> (char '^' >> many1Till inline' (char '^')) + B.superscript . mconcat <$> (char '^' >> many1Till inline' (char '^')) subscript :: PandocMonad m => VwParser m Inlines subscript = try $ - B.subscript <$> mconcat <$> (string ",," + B.subscript . mconcat <$> (string ",," >> many1Till inline' (try $ string ",,")) link :: PandocMonad m => VwParser m Inlines @@ -587,29 +586,29 @@ images k return $ B.image (procImgurl imgurl) "" (B.str "") | k == 1 = do imgurl <- manyTill anyChar (char '|') - alt <- mconcat <$> (manyTill inline $ (try $ string "}}")) + alt <- mconcat <$> (manyTill inline (try $ string "}}")) return $ B.image (procImgurl imgurl) "" alt | k == 2 = do imgurl <- manyTill anyChar (char '|') - alt <- mconcat <$> (manyTill inline $ char '|') + alt <- mconcat <$>manyTill inline (char '|') attrText <- manyTill anyChar (try $ string "}}") return $ B.imageWith (makeAttr attrText) (procImgurl imgurl) "" alt | otherwise = do imgurl <- manyTill anyChar (char '|') - alt <- mconcat <$> (manyTill inline $ char '|') + alt <- mconcat <$>manyTill inline (char '|') attrText <- manyTill anyChar (char '|') manyTill anyChar (try $ string "}}") return $ B.imageWith (makeAttr attrText) (procImgurl imgurl) "" alt procLink' :: String -> String procLink' s - | ((take 6 s) == "local:") = "file" ++ (drop 5 s) - | ((take 6 s) == "diary:") = "diary/" ++ (drop 6 s) ++ ".html" + | (take 6 s) == "local:" = "file" ++ drop 5 s + | (take 6 s) == "diary:" = "diary/" ++ drop 6 s ++ ".html" | or ((`isPrefixOf` s) <$> [ "http:", "https:", "ftp:", "file:", "mailto:", "news:", "telnet:" ]) = s | s == "" = "" - | (last s) == '/' = s + | last s == '/' = s | otherwise = s ++ ".html" procLink :: String -> String @@ -617,7 +616,7 @@ procLink s = procLink' x ++ y where (x, y) = break (=='#') s procImgurl :: String -> String -procImgurl s = if ((take 6 s) == "local:") then "file" ++ (drop 5 s) else s +procImgurl s = if (take 6 s) == "local:" then "file" ++ drop 5 s else s inlineMath :: PandocMonad m => VwParser m Inlines inlineMath = try $ do @@ -628,10 +627,10 @@ inlineMath = try $ do tag :: PandocMonad m => VwParser m Inlines tag = try $ do char ':' - s <- manyTill (noneOf spaceChars) (try (char ':' >> (lookAhead space))) + s <- manyTill (noneOf spaceChars) (try (char ':' >> lookAhead space)) guard $ not $ "::" `isInfixOf` (":" ++ s ++ ":") let ss = splitBy (==':') s - return $ mconcat $ (makeTagSpan' $ head ss):(makeTagSpan <$> (tail ss)) + return $ mconcat $ makeTagSpan' (head ss):(makeTagSpan <$> tail ss) todoMark :: PandocMonad m => VwParser m Inlines todoMark = try $ do @@ -661,18 +660,18 @@ nFBTTBSB = notFollowedBy hasDefMarker hasDefMarker :: PandocMonad m => VwParser m () -hasDefMarker = () <$ (manyTill (noneOf "\n") (string "::" >> oneOf spaceChars)) +hasDefMarker = () <$ manyTill (noneOf "\n") (string "::" >> oneOf spaceChars) makeTagSpan' :: String -> Inlines makeTagSpan' s = B.spanWith ('-' : s, [], []) (B.str "") <> B.spanWith (s, ["tag"], []) (B.str s) makeTagSpan :: String -> Inlines -makeTagSpan s = (B.space) <> (makeTagSpan' s) +makeTagSpan s = B.space <> makeTagSpan' s mathTagParser :: PandocMonad m => VwParser m String mathTagParser = do - s <- try $ lookAhead (char '%' >> (manyTill (noneOf spaceChars) - (try $ char '%' >> many (noneOf $ '%':spaceChars) >> space))) + s <- try $ lookAhead (char '%' >> manyTill (noneOf spaceChars) + (try $ char '%' >> many (noneOf $ '%':spaceChars) >> space)) char '%' >> string s >> char '%' return $ mathTagLaTeX s diff --git a/src/Text/Pandoc/SelfContained.hs b/src/Text/Pandoc/SelfContained.hs index b599eb62b..d3b768109 100644 --- a/src/Text/Pandoc/SelfContained.hs +++ b/src/Text/Pandoc/SelfContained.hs @@ -178,10 +178,10 @@ pCSSComment = P.try $ do return B.empty pCSSOther :: PandocMonad m => ParsecT ByteString () m ByteString -pCSSOther = do +pCSSOther = (B.pack <$> P.many1 (P.noneOf "u/ \n\r\t")) <|> - (B.singleton <$> P.char 'u') <|> - (B.singleton <$> P.char '/') + (B.singleton <$> P.char 'u') <|> + (B.singleton <$> P.char '/') pCSSUrl :: PandocMonad m => FilePath -> ParsecT ByteString () m ByteString @@ -218,9 +218,7 @@ handleCSSUrl :: PandocMonad m => FilePath -> (String, ByteString) -> ParsecT ByteString () m (Either ByteString (MimeType, ByteString)) -handleCSSUrl d (url, fallback) = do - -- pipes are used in URLs provided by Google Code fonts - -- but parseURI doesn't like them, so we escape them: +handleCSSUrl d (url, fallback) = case escapeURIString (/='|') (trim url) of '#':_ -> return $ Left fallback 'd':'a':'t':'a':':':_ -> return $ Left fallback @@ -251,8 +249,7 @@ getData mimetype src = do let ext = map toLower $ takeExtension src (raw, respMime) <- fetchItem src let raw' = if ext == ".gz" - then B.concat $ L.toChunks $ Gzip.decompress $ L.fromChunks - $ [raw] + then B.concat $ L.toChunks $ Gzip.decompress $ L.fromChunks [raw] else raw mime <- case (mimetype, respMime) of ("",Nothing) -> throwError $ PandocSomeError diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 85f13c865..9d4877c24 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -2,7 +2,7 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE PatternGuards #-} + {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ViewPatterns #-} {- @@ -144,11 +144,11 @@ splitBy _ [] = [] splitBy isSep lst = let (first, rest) = break isSep lst rest' = dropWhile isSep rest - in first:(splitBy isSep rest') + in first:splitBy isSep rest' splitByIndices :: [Int] -> [a] -> [[a]] splitByIndices [] lst = [lst] -splitByIndices (x:xs) lst = first:(splitByIndices (map (\y -> y - x) xs) rest) +splitByIndices (x:xs) lst = first:splitByIndices (map (\y -> y - x) xs) rest where (first, rest) = splitAt x lst -- | Split string into chunks divided at specified indices. @@ -156,7 +156,7 @@ splitStringByIndices :: [Int] -> [Char] -> [[Char]] splitStringByIndices [] lst = [lst] splitStringByIndices (x:xs) lst = let (first, rest) = splitAt' x lst in - first : (splitStringByIndices (map (\y -> y - x) xs) rest) + first : splitStringByIndices (map (\y -> y - x) xs) rest splitAt' :: Int -> [Char] -> ([Char],[Char]) splitAt' _ [] = ([],[]) @@ -195,7 +195,7 @@ backslashEscapes = map (\ch -> (ch, ['\\',ch])) escapeStringUsing :: [(Char, String)] -> String -> String escapeStringUsing _ [] = "" escapeStringUsing escapeTable (x:xs) = - case (lookup x escapeTable) of + case lookup x escapeTable of Just str -> str ++ rest Nothing -> x:rest where rest = escapeStringUsing escapeTable xs @@ -219,14 +219,14 @@ trimr = reverse . triml . reverse -- | Strip leading and trailing characters from string stripFirstAndLast :: String -> String stripFirstAndLast str = - drop 1 $ take ((length str) - 1) str + drop 1 $ take (length str - 1) str -- | Change CamelCase word to hyphenated lowercase (e.g., camel-case). camelCaseToHyphenated :: String -> String camelCaseToHyphenated [] = "" camelCaseToHyphenated (a:b:rest) | isLower a && isUpper b = - a:'-':(toLower b):(camelCaseToHyphenated rest) -camelCaseToHyphenated (a:rest) = (toLower a):(camelCaseToHyphenated rest) + a:'-':toLower b:camelCaseToHyphenated rest +camelCaseToHyphenated (a:rest) = toLower a:camelCaseToHyphenated rest -- | Convert number < 4000 to uppercase roman numeral. toRomanNumeral :: Int -> String @@ -477,7 +477,7 @@ hierarchicalize blocks = S.evalState (hierarchicalizeWithIds blocks) [] hierarchicalizeWithIds :: [Block] -> S.State [Int] [Element] hierarchicalizeWithIds [] = return [] -hierarchicalizeWithIds ((Header level attr@(_,classes,_) title'):xs) = do +hierarchicalizeWithIds (Header level attr@(_,classes,_) title':xs) = do lastnum <- S.get let lastnum' = take level lastnum let newnum = case length lastnum' of @@ -490,13 +490,13 @@ hierarchicalizeWithIds ((Header level attr@(_,classes,_) title'):xs) = do sectionContents' <- hierarchicalizeWithIds sectionContents rest' <- hierarchicalizeWithIds rest return $ Sec level newnum attr title' sectionContents' : rest' -hierarchicalizeWithIds ((Div ("",["references"],[]) - (Header level (ident,classes,kvs) title' : xs)):ys) = - hierarchicalizeWithIds ((Header level (ident,("references":classes),kvs) - title') : (xs ++ ys)) +hierarchicalizeWithIds (Div ("",["references"],[]) + (Header level (ident,classes,kvs) title' : xs):ys) = + hierarchicalizeWithIds (Header level (ident,("references":classes),kvs) + title' : (xs ++ ys)) hierarchicalizeWithIds (x:rest) = do rest' <- hierarchicalizeWithIds rest - return $ (Blk x) : rest' + return $ Blk x : rest' headerLtEq :: Int -> Block -> Bool headerLtEq level (Header l _ _) = l <= level @@ -519,7 +519,7 @@ uniqueIdent title' usedIdents -- | True if block is a Header block. isHeaderBlock :: Block -> Bool -isHeaderBlock (Header _ _ _) = True +isHeaderBlock (Header{}) = True isHeaderBlock _ = False -- | Shift header levels up or down. @@ -555,15 +555,14 @@ makeMeta :: [Inline] -> [[Inline]] -> [Inline] -> Meta makeMeta title authors date = addMetaField "title" (B.fromList title) $ addMetaField "author" (map B.fromList authors) - $ addMetaField "date" (B.fromList date) - $ nullMeta + $ addMetaField "date" (B.fromList date) nullMeta -- | Remove soft breaks between East Asian characters. eastAsianLineBreakFilter :: Pandoc -> Pandoc eastAsianLineBreakFilter = bottomUp go where go (x:SoftBreak:y:zs) = case (stringify x, stringify y) of - (xs@(_:_), (c:_)) + (xs@(_:_), c:_) | charWidth (last xs) == 2 && charWidth c == 2 -> x:y:zs _ -> x:SoftBreak:y:zs go xs = xs @@ -620,8 +619,8 @@ collapseFilePath = Posix.joinPath . reverse . foldl go [] . splitDirectories where go rs "." = rs go r@(p:rs) ".." = case p of - ".." -> ("..":r) - (checkPathSeperator -> Just True) -> ("..":r) + ".." -> "..":r + (checkPathSeperator -> Just True) -> "..":r _ -> rs go _ (checkPathSeperator -> Just True) = [[Posix.pathSeparator]] go rs x = x:rs @@ -725,9 +724,9 @@ blockToInlines (DefinitionList pairslst) = where f (ils, blkslst) = ils ++ [Str ":", Space] ++ - (concatMap blocksToInlines blkslst) + concatMap blocksToInlines blkslst blockToInlines (Header _ _ ils) = ils -blockToInlines (HorizontalRule) = [] +blockToInlines HorizontalRule = [] blockToInlines (Table _ _ _ headers rows) = intercalate [LineBreak] $ map (concatMap blocksToInlines) tbl where diff --git a/src/Text/Pandoc/Slides.hs b/src/Text/Pandoc/Slides.hs index cd7695dbe..d83735029 100644 --- a/src/Text/Pandoc/Slides.hs +++ b/src/Text/Pandoc/Slides.hs @@ -40,8 +40,8 @@ getSlideLevel = go 6 | otherwise = go least (x:xs) go least (_ : xs) = go least xs go least [] = least - nonHOrHR (Header{}) = False - nonHOrHR (HorizontalRule) = False + nonHOrHR Header{} = False + nonHOrHR HorizontalRule = False nonHOrHR _ = True -- | Prepare a block list to be passed to hierarchicalize. diff --git a/src/Text/Pandoc/Templates.hs b/src/Text/Pandoc/Templates.hs index 9f3781259..89d524d96 100644 --- a/src/Text/Pandoc/Templates.hs +++ b/src/Text/Pandoc/Templates.hs @@ -1,5 +1,5 @@ {-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} + {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeSynonymInstances #-} {- @@ -77,7 +77,7 @@ getDefaultTemplate writer = do -- raises an error if compilation fails. renderTemplate' :: (PandocMonad m, ToJSON a, TemplateTarget b) => String -> a -> m b -renderTemplate' template context = do +renderTemplate' template context = case applyTemplate (T.pack template) context of Left e -> throwError (PandocTemplateError e) Right r -> return r diff --git a/src/Text/Pandoc/UUID.hs b/src/Text/Pandoc/UUID.hs index 989dd20c6..1527ce435 100644 --- a/src/Text/Pandoc/UUID.hs +++ b/src/Text/Pandoc/UUID.hs @@ -75,4 +75,3 @@ getUUID gen = getRandomUUID :: IO UUID getRandomUUID = getUUID <$> getStdGen - -- cgit v1.2.3 From cb42bb820cff10b38662d077574a94ad78888f94 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 28 Oct 2017 00:01:44 -0700 Subject: Change order of imports to satisfy older ghc. --- src/Text/Pandoc/Pretty.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Pretty.hs b/src/Text/Pandoc/Pretty.hs index 40a7d018c..a6e04e4d5 100644 --- a/src/Text/Pandoc/Pretty.hs +++ b/src/Text/Pandoc/Pretty.hs @@ -77,8 +77,8 @@ module Text.Pandoc.Pretty ( ) where -import Control.Monad.State.Strict import Control.Monad (when) +import Control.Monad.State.Strict import Data.Char (isSpace) import Data.Foldable (toList) import Data.List (intersperse) -- cgit v1.2.3 From b39f782c8d333eda8c7560eea9e8468e04676321 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 28 Oct 2017 00:09:23 -0700 Subject: Try to fix imports for older ghc. --- src/Text/Pandoc/Pretty.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Pretty.hs b/src/Text/Pandoc/Pretty.hs index a6e04e4d5..ed6dde149 100644 --- a/src/Text/Pandoc/Pretty.hs +++ b/src/Text/Pandoc/Pretty.hs @@ -77,7 +77,7 @@ module Text.Pandoc.Pretty ( ) where -import Control.Monad (when) +import Control.Monad import Control.Monad.State.Strict import Data.Char (isSpace) import Data.Foldable (toList) -- cgit v1.2.3 From 47e2719cc3e18a6216c886b8ab7166f48bfe457f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 28 Oct 2017 09:56:20 -0700 Subject: Fix warning for older GHC versions. --- src/Text/Pandoc/Readers/Docx/Parse.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx/Parse.hs b/src/Text/Pandoc/Readers/Docx/Parse.hs index fea595027..99e6f99e6 100644 --- a/src/Text/Pandoc/Readers/Docx/Parse.hs +++ b/src/Text/Pandoc/Readers/Docx/Parse.hs @@ -106,7 +106,7 @@ eitherToD (Right b) = return b eitherToD (Left _) = throwError DocxError concatMapM :: (Monad m) => (a -> m [b]) -> [a] -> m [b] -concatMapM f xs = fmap concat (mapM f xs) +concatMapM f xs = liftM concat (mapM f xs) -- This is similar to `mapMaybe`: it maps a function returning the D -- cgit v1.2.3 From 3263d0d7c4052307ce38f342800197dec5a3fba0 Mon Sep 17 00:00:00 2001 From: Alexander <ilabdsf@gmail.com> Date: Sun, 29 Oct 2017 21:46:44 +0300 Subject: Write FB2 lists without nesting blocks inside <p> (#4004) According to FB2 XML schema <empty-line /> cannot be placed inside <p>. Linux FBReader can't display such paragraphs, e.g. any "loose" lists produced by pandoc prior to this commit. Besides that, FB2 writer placed <p> inside <p> when writing nested lists, this commit fixes the bug. Also this commit removes leading non-breaking space from ordered lists for consistency with bullet lists. Definition lists are not affected at all. --- src/Text/Pandoc/Writers/FB2.hs | 48 ++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 16 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index b7dc43685..6d61ea62c 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -338,13 +338,13 @@ blockToXml (LineBlock lns) = blockToXml (OrderedList a bss) = do state <- get let pmrk = parentListMarker state - let markers = map ((pmrk ++ " ") ++) $ orderedListMarkers a + let markers = map (pmrk ++) $ orderedListMarkers a let mkitem mrk bs = do - modify (\s -> s { parentListMarker = mrk }) - itemtext <- cMapM blockToXml . paraToPlain $ bs + modify (\s -> s { parentListMarker = mrk ++ " "}) + item <- cMapM blockToXml $ plainToPara $ indentBlocks (mrk ++ " ") bs modify (\s -> s { parentListMarker = pmrk }) -- old parent marker - return . el "p" $ [ txt mrk, txt " " ] ++ itemtext - zipWithM mkitem markers bss + return item + concat <$> (zipWithM mkitem markers bss) blockToXml (BulletList bss) = do state <- get let level = parentBulletLevel state @@ -354,10 +354,10 @@ blockToXml (BulletList bss) = do let mrk = prefix ++ bullets !! (level `mod` length bullets) let mkitem bs = do modify (\s -> s { parentBulletLevel = level+1 }) - itemtext <- cMapM blockToXml . paraToPlain $ bs + item <- cMapM blockToXml $ plainToPara $ indentBlocks (mrk ++ " ") bs modify (\s -> s { parentBulletLevel = level }) -- restore bullet level - return $ el "p" $ txt (mrk ++ " ") : itemtext - mapM mkitem bss + return item + cMapM mkitem bss blockToXml (DefinitionList defs) = cMapM mkdef defs where @@ -408,19 +408,24 @@ blockToXml Null = return [] paraToPlain :: [Block] -> [Block] paraToPlain [] = [] paraToPlain (Para inlines : rest) = - let p = Plain (inlines ++ [LineBreak]) - in p : paraToPlain rest + Plain (inlines) : Plain ([LineBreak]) : paraToPlain rest paraToPlain (p:rest) = p : paraToPlain rest +-- Replace plain text with paragraphs and add line break after paragraphs. +-- It is used to convert plain text from tight list items to paragraphs. +plainToPara :: [Block] -> [Block] +plainToPara [] = [] +plainToPara (Plain inlines : rest) = + Para (inlines) : plainToPara rest +plainToPara (Para inlines : rest) = + Para (inlines) : Plain [LineBreak] : plainToPara rest +plainToPara (p:rest) = p : plainToPara rest + -- Simulate increased indentation level. Will not really work -- for multi-line paragraphs. -indent :: Block -> Block -indent = indentBlock +indentPrefix :: String -> Block -> Block +indentPrefix spacer = indentBlock where - -- indentation space - spacer :: String - spacer = replicate 4 ' ' - -- indentBlock (Plain ins) = Plain (Str spacer:ins) indentBlock (Para ins) = Para (Str spacer:ins) indentBlock (CodeBlock a s) = @@ -434,6 +439,17 @@ indent = indentBlock indentLines ins = let lns = split isLineBreak ins :: [[Inline]] in intercalate [LineBreak] $ map (Str spacer:) lns +indent :: Block -> Block +indent = indentPrefix spacer + where + -- indentation space + spacer :: String + spacer = replicate 4 ' ' + +indentBlocks :: String -> [Block] -> [Block] +indentBlocks _ [] = [] +indentBlocks prefix (x:xs) = indentPrefix prefix x : map (indentPrefix $ replicate (length prefix) ' ') xs + -- | Convert a Pandoc's Inline element to FictionBook XML representation. toXml :: PandocMonad m => Inline -> FBM m [Content] toXml (Str s) = return [txt s] -- cgit v1.2.3 From 8e5e8746d8f114d81b38f8a5d4ac5322937cb5a5 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 29 Oct 2017 12:45:37 -0700 Subject: More hlint fixes. --- src/Text/Pandoc/Readers/LaTeX.hs | 4 +- src/Text/Pandoc/Readers/Markdown.hs | 4 +- src/Text/Pandoc/Readers/Muse.hs | 4 +- src/Text/Pandoc/Readers/OPML.hs | 2 +- src/Text/Pandoc/Readers/Odt/StyleReader.hs | 2 +- src/Text/Pandoc/Readers/RST.hs | 96 ++++++++++++++---------------- 6 files changed, 54 insertions(+), 58 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index c91e8bd79..d6a3de2f1 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -665,7 +665,7 @@ removeDoubleQuotes t = Data.Maybe.fromMaybe t $ T.stripPrefix "\"" t >>= T.stripSuffix "\"" doubleQuote :: PandocMonad m => LP m Inlines -doubleQuote = +doubleQuote = quoted' doubleQuoted (try $ count 2 $ symbol '`') (void $ try $ count 2 $ symbol '\'') <|> quoted' doubleQuoted ((:[]) <$> symbol '“') (void $ symbol '”') @@ -674,7 +674,7 @@ doubleQuote = (void $ try $ sequence [symbol '"', symbol '\'']) singleQuote :: PandocMonad m => LP m Inlines -singleQuote = +singleQuote = quoted' singleQuoted ((:[]) <$> symbol '`') (try $ symbol '\'' >> notFollowedBy (satisfyTok startsWithLetter)) diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 69e70f9f5..2a88b39ec 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -241,7 +241,7 @@ yamlMetaBlock = try $ do case Yaml.decodeEither' $ UTF8.fromString rawYaml of Right (Yaml.Object hashmap) -> do let alist = H.toList hashmap - mapM_ (\(k, v) -> + mapM_ (\(k, v) -> if ignorable k then return () else do @@ -320,7 +320,7 @@ yamlToMeta (Yaml.Array xs) = do return $ B.toMetaValue xs'' yamlToMeta (Yaml.Object o) = do let alist = H.toList o - foldM (\m (k,v) -> + foldM (\m (k,v) -> if ignorable k then return m else do diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 6cc505e3b..3bb4b64e6 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -240,7 +240,7 @@ exampleTag = do chop = lchop . rchop literal :: PandocMonad m => MuseParser m (F Blocks) -literal = fmap (return . rawBlock) $ htmlElement "literal" +literal = (return . rawBlock) <$> htmlElement "literal" where format (_, _, kvs) = fromMaybe "html" $ lookup "format" kvs rawBlock (attrs, content) = B.rawBlock (format attrs) content @@ -658,7 +658,7 @@ str :: PandocMonad m => MuseParser m (F Inlines) str = fmap (return . B.str) (many1 alphaNum <|> count 1 characterReference) symbol :: PandocMonad m => MuseParser m (F Inlines) -symbol = fmap (return . B.str) $ count 1 nonspaceChar +symbol = (return . B.str) <$> count 1 nonspaceChar link :: PandocMonad m => MuseParser m (F Inlines) link = try $ do diff --git a/src/Text/Pandoc/Readers/OPML.hs b/src/Text/Pandoc/Readers/OPML.hs index e3ef67bc1..1a1375b16 100644 --- a/src/Text/Pandoc/Readers/OPML.hs +++ b/src/Text/Pandoc/Readers/OPML.hs @@ -58,7 +58,7 @@ normalizeTree = everywhere (mkT go) go xs = xs convertEntity :: String -> String -convertEntity e = maybe (map toUpper e) id (lookupEntity e) +convertEntity e = Data.Maybe.fromMaybe (map toUpper e) (lookupEntity e) -- convenience function to get an attribute value, defaulting to "" attrValue :: String -> Element -> String diff --git a/src/Text/Pandoc/Readers/Odt/StyleReader.hs b/src/Text/Pandoc/Readers/Odt/StyleReader.hs index abb131983..1384072d1 100644 --- a/src/Text/Pandoc/Readers/Odt/StyleReader.hs +++ b/src/Text/Pandoc/Readers/Odt/StyleReader.hs @@ -1,5 +1,5 @@ {-# LANGUAGE Arrows #-} -{-# LANGUAGE PatternGuards #-} + {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TupleSections #-} diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index dae9fe40a..070a05df1 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -31,20 +31,21 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Conversion from reStructuredText to 'Pandoc' document. -} module Text.Pandoc.Readers.RST ( readRST ) where +import Control.Arrow (second) import Control.Monad (forM_, guard, liftM, mplus, mzero, when) import Control.Monad.Except (throwError) import Control.Monad.Identity (Identity (..)) import Data.Char (isHexDigit, isSpace, toLower, toUpper) -import Data.List (deleteFirstsBy, findIndex, intercalate, isInfixOf, isSuffixOf, - nub, sort, transpose, union) +import Data.List (deleteFirstsBy, intercalate, isInfixOf, + elemIndex, isSuffixOf, nub, sort, transpose, union) import qualified Data.Map as M import Data.Maybe (fromMaybe, isJust) import Data.Monoid ((<>)) import Data.Sequence (ViewR (..), viewr) import Data.Text (Text) import qualified Data.Text as T -import Text.Pandoc.Builder (fromList, setMeta) -import Text.Pandoc.Builder (Blocks, Inlines, trimInlines) +import Text.Pandoc.Builder + (fromList, setMeta, Blocks, Inlines, trimInlines) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad, fetchItem, readFileFromDirs) import Text.Pandoc.CSV (CSVOptions (..), defaultCSVOptions, parseCSV) @@ -67,7 +68,7 @@ readRST :: PandocMonad m -> Text -- ^ String to parse (assuming @'\n'@ line endings) -> m Pandoc readRST opts s = do - parsed <- (readWithM parseRST) def{ stateOptions = opts } + parsed <- readWithM parseRST def{ stateOptions = opts } (T.unpack (crFilter s) ++ "\n\n") case parsed of Right result -> return result @@ -100,9 +101,9 @@ isHeader _ _ = False -- | Promote all headers in a list of blocks. (Part of -- title transformation for RST.) promoteHeaders :: Int -> [Block] -> [Block] -promoteHeaders num ((Header level attr text):rest) = - (Header (level - num) attr text):(promoteHeaders num rest) -promoteHeaders num (other:rest) = other:(promoteHeaders num rest) +promoteHeaders num (Header level attr text:rest) = + Header (level - num) attr text:promoteHeaders num rest +promoteHeaders num (other:rest) = other:promoteHeaders num rest promoteHeaders _ [] = [] -- | If list of blocks starts with a header (or a header and subheader) @@ -114,11 +115,11 @@ titleTransform :: ([Block], Meta) -- ^ list of blocks, metadata titleTransform (bs, meta) = let (bs', meta') = case bs of - ((Header 1 _ head1):(Header 2 _ head2):rest) + (Header 1 _ head1:Header 2 _ head2:rest) | not (any (isHeader 1) rest || any (isHeader 2) rest) -> -- tit/sub (promoteHeaders 2 rest, setMeta "title" (fromList head1) $ setMeta "subtitle" (fromList head2) meta) - ((Header 1 _ head1):rest) + (Header 1 _ head1:rest) | not (any (isHeader 1) rest) -> -- title only (promoteHeaders 1 rest, setMeta "title" (fromList head1) meta) @@ -137,8 +138,7 @@ metaFromDefList ds meta = adjustAuthors $ foldr f meta ds $ M.mapKeys (\k -> if k == "authors" then "author" - else k) - $ metamap + else k) metamap toPlain (MetaBlocks [Para xs]) = MetaInlines xs toPlain x = x splitAuthors (MetaBlocks [Para xs]) @@ -201,7 +201,7 @@ parseCitation :: PandocMonad m => (String, String) -> RSTParser m (Inlines, [Blocks]) parseCitation (ref, raw) = do contents <- parseFromString' parseBlocks raw - return $ (B.spanWith (ref, ["citation-label"], []) (B.str ref), + return (B.spanWith (ref, ["citation-label"], []) (B.str ref), [contents]) @@ -289,7 +289,7 @@ para = try $ do newline blanklines case viewr (B.unMany result) of - ys :> (Str xs) | "::" `isSuffixOf` xs -> do + ys :> Str xs | "::" `isSuffixOf` xs -> do raw <- option mempty codeBlockBody return $ B.para (B.Many ys <> B.str (take (length xs - 1) xs)) <> raw @@ -313,9 +313,9 @@ doubleHeader = do -- if so, get appropriate level. if not, add to list. state <- getState let headerTable = stateHeaderTable state - let (headerTable',level) = case findIndex (== DoubleHeader c) headerTable of + let (headerTable',level) = case elemIndex (DoubleHeader c) headerTable of Just ind -> (headerTable, ind + 1) - Nothing -> (headerTable ++ [DoubleHeader c], (length headerTable) + 1) + Nothing -> (headerTable ++ [DoubleHeader c], length headerTable + 1) setState (state { stateHeaderTable = headerTable' }) attr <- registerHeader nullAttr txt return $ B.headerWith attr level txt @@ -329,8 +329,8 @@ doubleHeader' = try $ do newline txt <- trimInlines . mconcat <$> many1 (notFollowedBy blankline >> inline) pos <- getPosition - let len = (sourceColumn pos) - 1 - if (len > lenTop) then fail "title longer than border" else return () + let len = sourceColumn pos - 1 + when (len > lenTop) $ fail "title longer than border" blankline -- spaces and newline count lenTop (char c) -- the bottom line blanklines @@ -342,9 +342,9 @@ singleHeader = do (txt, c) <- singleHeader' state <- getState let headerTable = stateHeaderTable state - let (headerTable',level) = case findIndex (== SingleHeader c) headerTable of + let (headerTable',level) = case elemIndex (SingleHeader c) headerTable of Just ind -> (headerTable, ind + 1) - Nothing -> (headerTable ++ [SingleHeader c], (length headerTable) + 1) + Nothing -> (headerTable ++ [SingleHeader c], length headerTable + 1) setState (state { stateHeaderTable = headerTable' }) attr <- registerHeader nullAttr txt return $ B.headerWith attr level txt @@ -355,7 +355,7 @@ singleHeader' = try $ do lookAhead $ anyLine >> oneOf underlineChars txt <- trimInlines . mconcat <$> many1 (do {notFollowedBy newline; inline}) pos <- getPosition - let len = (sourceColumn pos) - 1 + let len = sourceColumn pos - 1 blankline c <- oneOf underlineChars count (len - 1) (char c) @@ -491,8 +491,7 @@ includeDirective top fields body = do Just x | x >= 0 -> x | otherwise -> numLines + x -- negative from end let contentLines' = drop (startLine' - 1) - $ take (endLine' - 1) - $ contentLines + $ take (endLine' - 1) contentLines let contentLines'' = (case trim <$> lookup "end-before" fields of Just patt -> takeWhile (not . (patt `isInfixOf`)) Nothing -> id) . @@ -692,7 +691,7 @@ directive' = do "csv-table" -> csvTableDirective top fields body' "line-block" -> lineBlockDirective body' "raw" -> return $ B.rawBlock (trim top) (stripTrailingNewlines body) - "role" -> addNewRole top $ map (\(k,v) -> (k, trim v)) fields + "role" -> addNewRole top $ map (second trim) fields "container" -> parseFromString' parseBlocks body' "replace" -> B.para <$> -- consumed by substKey parseInlineFromString (trim top) @@ -733,7 +732,7 @@ directive' = do codeblock (words $ fromMaybe [] $ lookup "class" fields) (lookup "number-lines" fields) (trim top) body "aafig" -> do - let attribs = ("", ["aafig"], map (\(k,v) -> (k, trimr v)) fields) + let attribs = ("", ["aafig"], map (second trimr) fields) return $ B.codeBlockWith attribs $ stripTrailingNewlines body "math" -> return $ B.para $ mconcat $ map B.displayMath $ toChunks $ top ++ "\n\n" ++ body @@ -752,8 +751,8 @@ directive' = do $ B.imageWith attr src "" alt Nothing -> B.imageWith attr src "" alt "class" -> do - let attrs = ("", (splitBy isSpace $ trim top), - map (\(k,v) -> (k, trimr v)) fields) + let attrs = ("", splitBy isSpace $ trim top, + map (second trimr) fields) -- directive content or the first immediately following element children <- case body of "" -> block @@ -857,7 +856,7 @@ csvTableDirective top fields rawcsv = do Just h -> h ++ "\n" ++ rawcsv' Nothing -> rawcsv') case res of - Left e -> do + Left e -> throwError $ PandocParsecError "csv table" e Right rawrows -> do let parseCell = parseFromString' (plain <|> return mempty) . T.unpack @@ -909,13 +908,13 @@ addNewRole roleString fields = do in (ident, nub . (role :) . annotate $ classes, keyValues) -- warn about syntax we ignore - flip mapM_ fields $ \(key, _) -> case key of - "language" -> when (baseRole /= "code") $ logMessage $ - SkippedContent ":language: [because parent of role is not :code:]" - pos - "format" -> when (baseRole /= "raw") $ logMessage $ - SkippedContent ":format: [because parent of role is not :raw:]" pos - _ -> logMessage $ SkippedContent (":" ++ key ++ ":") pos + forM_ fields $ \(key, _) -> case key of + "language" -> when (baseRole /= "code") $ logMessage $ + SkippedContent ":language: [because parent of role is not :code:]" + pos + "format" -> when (baseRole /= "raw") $ logMessage $ + SkippedContent ":format: [because parent of role is not :raw:]" pos + _ -> logMessage $ SkippedContent (":" ++ key ++ ":") pos when (parentRole == "raw" && countKeys "format" > 1) $ logMessage $ SkippedContent ":format: [after first in definition of role]" @@ -983,7 +982,7 @@ codeblock classes numberLines lang body = return $ B.codeBlockWith attribs $ stripTrailingNewlines body where attribs = ("", classes', kvs) classes' = "sourceCode" : lang - : maybe [] (\_ -> ["numberLines"]) numberLines + : maybe [] (const ["numberLines"]) numberLines ++ classes kvs = case numberLines of Just "" -> [] @@ -1038,7 +1037,8 @@ noteMarker :: Monad m => RSTParser m [Char] noteMarker = do char '[' res <- many1 digit - <|> (try $ char '#' >> liftM ('#':) simpleReferenceName') + <|> + try (char '#' >> liftM ('#':) simpleReferenceName') <|> count 1 (oneOf "#*") char ']' return res @@ -1050,13 +1050,11 @@ noteMarker = do quotedReferenceName :: PandocMonad m => RSTParser m Inlines quotedReferenceName = try $ do char '`' >> notFollowedBy (char '`') -- `` means inline code! - label' <- trimInlines . mconcat <$> many1Till inline (char '`') - return label' + trimInlines . mconcat <$> many1Till inline (char '`') unquotedReferenceName :: PandocMonad m => RSTParser m Inlines -unquotedReferenceName = try $ do - label' <- trimInlines . mconcat <$> many1Till inline (lookAhead $ char ':') - return label' +unquotedReferenceName = try $ do -- `` means inline code! + trimInlines . mconcat <$> many1Till inline (lookAhead $ char ':') -- Simple reference names are single words consisting of alphanumerics -- plus isolated (no two adjacent) internal hyphens, underscores, @@ -1066,7 +1064,8 @@ simpleReferenceName' :: Monad m => ParserT [Char] st m String simpleReferenceName' = do x <- alphaNum xs <- many $ alphaNum - <|> (try $ oneOf "-_:+." <* lookAhead alphaNum) + <|> + try (oneOf "-_:+." <* lookAhead alphaNum) return (x:xs) simpleReferenceName :: Monad m => ParserT [Char] st m Inlines @@ -1074,7 +1073,7 @@ simpleReferenceName = B.str <$> simpleReferenceName' referenceName :: PandocMonad m => RSTParser m Inlines referenceName = quotedReferenceName <|> - (try $ simpleReferenceName <* lookAhead (char ':')) <|> + try (simpleReferenceName <* lookAhead (char ':')) <|> unquotedReferenceName referenceKey :: PandocMonad m => RSTParser m [Char] @@ -1093,7 +1092,7 @@ targetURI = do contents <- many1 (try (many spaceChar >> newline >> many1 spaceChar >> noneOf " \t\n") <|> noneOf "\n") blanklines - return $ escapeURI $ trim $ contents + return $ escapeURI $ trim contents substKey :: PandocMonad m => RSTParser m () substKey = try $ do @@ -1258,8 +1257,7 @@ simpleTableHeader headless = try $ do let rawHeads = if headless then replicate (length dashes) "" else simpleTableSplitLine indices rawContent - heads <- mapM (parseFromString' (mconcat <$> many plain)) $ - map trim rawHeads + heads <- mapM ( (parseFromString' (mconcat <$> many plain)) . trim) rawHeads return (heads, aligns, indices) -- Parse a simple table. @@ -1450,10 +1448,8 @@ endline = try $ do notFollowedBy blankline -- parse potential list-starts at beginning of line differently in a list: st <- getState - if (stateParserContext st) == ListItemState - then notFollowedBy (anyOrderedListMarker >> spaceChar) >> + when ((stateParserContext st) == ListItemState) $ notFollowedBy (anyOrderedListMarker >> spaceChar) >> notFollowedBy' bulletListStart - else return () return B.softbreak -- -- cgit v1.2.3 From b18dbfe7920570c8234d58b7c9833ee643b47c4f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 29 Oct 2017 12:58:41 -0700 Subject: Use uncurry. --- src/Text/Pandoc/App.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index a80c6ac44..f0e953d53 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -493,7 +493,7 @@ convertWithOpts opts = do report $ Deprecated "markdown_github" "Use gfm instead." setResourcePath (optResourcePath opts) - mapM_ (\(n,v) -> setRequestHeader n v) (optRequestHeaders opts) + mapM_ (uncurry setRequestHeader) (optRequestHeaders opts) doc <- sourceToDoc sources >>= ( (if isJust (optExtractMedia opts) -- cgit v1.2.3 From 9ef4ad2e208c1618563bc5118f5ee84f3a52ed4c Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 29 Oct 2017 13:01:04 -0700 Subject: Small reformat. --- src/Text/Pandoc/BCP47.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/BCP47.hs b/src/Text/Pandoc/BCP47.hs index 0f1421555..a9fb5c7a7 100644 --- a/src/Text/Pandoc/BCP47.hs +++ b/src/Text/Pandoc/BCP47.hs @@ -79,7 +79,7 @@ parseBCP47 lang = region <- P.option "" pRegion variants <- P.many (pVariant P.<|> pExtension P.<|> pPrivateUse) P.eof - return $ Lang{ langLanguage = language + return Lang{ langLanguage = language , langScript = script , langRegion = region , langVariants = variants } -- cgit v1.2.3 From 271e1fe2f1c41ff177807a86ead47e2d70d69c55 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 29 Oct 2017 13:19:15 -0700 Subject: More hlint. --- src/Text/Pandoc/Writers/AsciiDoc.hs | 25 ++++++++++++------------- src/Text/Pandoc/Writers/CommonMark.hs | 12 ++++++------ src/Text/Pandoc/Writers/ConTeXt.hs | 26 ++++++++++++-------------- 3 files changed, 30 insertions(+), 33 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/AsciiDoc.hs b/src/Text/Pandoc/Writers/AsciiDoc.hs index 112f8b657..82d422f93 100644 --- a/src/Text/Pandoc/Writers/AsciiDoc.hs +++ b/src/Text/Pandoc/Writers/AsciiDoc.hs @@ -42,7 +42,7 @@ import Data.Aeson (Result (..), Value (String), fromJSON, toJSON) import Data.Char (isPunctuation, isSpace) import Data.List (intercalate, intersperse, stripPrefix) import qualified Data.Map as M -import Data.Maybe (fromMaybe) +import Data.Maybe (fromMaybe, isJust) import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Class (PandocMonad, report) @@ -100,9 +100,8 @@ pandocToAsciiDoc opts (Pandoc meta blocks) = do let context = defField "body" main $ defField "toc" (writerTableOfContents opts && - writerTemplate opts /= Nothing) - $ defField "titleblock" titleblock - $ metadata' + Data.Maybe.isJust (writerTemplate opts)) + $defField "titleblock" titleblock metadata' case writerTemplate opts of Nothing -> return main Just tpl -> renderTemplate' tpl context @@ -137,7 +136,7 @@ blockToAsciiDoc _ Null = return empty blockToAsciiDoc opts (Plain inlines) = do contents <- inlineListToAsciiDoc opts inlines return $ contents <> blankline -blockToAsciiDoc opts (Para [Image attr alt (src,'f':'i':'g':':':tit)]) = do +blockToAsciiDoc opts (Para [Image attr alt (src,'f':'i':'g':':':tit)]) = blockToAsciiDoc opts (Para [Image attr alt (src,tit)]) blockToAsciiDoc opts (Para inlines) = do contents <- inlineListToAsciiDoc opts inlines @@ -165,9 +164,9 @@ blockToAsciiDoc opts (Header level (ident,_,_) inlines) = do let len = offset contents -- ident seem to be empty most of the time and asciidoc will generate them automatically -- so lets make them not show up when null - let identifier = if (null ident) then empty else ("[[" <> text ident <> "]]") + let identifier = if null ident then empty else "[[" <> text ident <> "]]" let setext = writerSetextHeaders opts - return $ + return (if setext then identifier $$ contents $$ @@ -179,7 +178,7 @@ blockToAsciiDoc opts (Header level (ident,_,_) inlines) = do _ -> empty) <> blankline else identifier $$ text (replicate level '=') <> space <> contents <> blankline) -blockToAsciiDoc _ (CodeBlock (_,classes,_) str) = return $ (flush $ +blockToAsciiDoc _ (CodeBlock (_,classes,_) str) = return $ flush ( if null classes then "...." $$ text str $$ "...." else attrs $$ "----" $$ text str $$ "----") @@ -204,7 +203,7 @@ blockToAsciiDoc opts (Table caption aligns widths headers rows) = do let isSimple = all (== 0) widths let relativePercentWidths = if isSimple then widths - else map (/ (sum widths)) widths + else map (/ sum widths) widths let widths'' :: [Integer] widths'' = map (floor . (* 100)) relativePercentWidths -- ensure that the widths sum to 100 @@ -266,14 +265,14 @@ blockToAsciiDoc opts (OrderedList (_start, sty, _delim) items) = do let markers' = map (\m -> if length m < 3 then m ++ replicate (3 - length m) ' ' else m) markers - contents <- mapM (\(item, num) -> orderedListItemToAsciiDoc opts item num) $ + contents <- mapM (uncurry (orderedListItemToAsciiDoc opts)) $ zip markers' items return $ cat contents <> blankline blockToAsciiDoc opts (DefinitionList items) = do contents <- mapM (definitionListItemToAsciiDoc opts) items return $ cat contents <> blankline blockToAsciiDoc opts (Div (ident,_,_) bs) = do - let identifier = if (null ident) then empty else ("[[" <> text ident <> "]]") + let identifier = if null ident then empty else ("[[" <> text ident <> "]]") contents <- blockListToAsciiDoc opts bs return $ identifier $$ contents @@ -460,7 +459,7 @@ inlineToAsciiDoc opts (Image attr alternate (src, tit)) = do let linktitle = if null tit then empty else ",title=\"" <> text tit <> "\"" - showDim dir = case (dimension dir attr) of + showDim dir = case dimension dir attr of Just (Percent a) -> ["scaledwidth=" <> text (show (Percent a))] Just dim -> @@ -480,6 +479,6 @@ inlineToAsciiDoc opts (Note [Plain inlines]) = do -- asciidoc can't handle blank lines in notes inlineToAsciiDoc _ (Note _) = return "[multiblock footnote omitted]" inlineToAsciiDoc opts (Span (ident,_,_) ils) = do - let identifier = if (null ident) then empty else ("[[" <> text ident <> "]]") + let identifier = if null ident then empty else ("[[" <> text ident <> "]]") contents <- inlineListToAsciiDoc opts ils return $ identifier <> contents diff --git a/src/Text/Pandoc/Writers/CommonMark.hs b/src/Text/Pandoc/Writers/CommonMark.hs index 9bd9f25bc..e6d297291 100644 --- a/src/Text/Pandoc/Writers/CommonMark.hs +++ b/src/Text/Pandoc/Writers/CommonMark.hs @@ -60,7 +60,7 @@ writeCommonMark opts (Pandoc meta blocks) = do (blocksToCommonMark opts) (inlinesToCommonMark opts) meta - let context = defField "body" main $ metadata + let context = defField "body" main metadata case writerTemplate opts of Nothing -> return main Just tpl -> renderTemplate' tpl context @@ -108,11 +108,11 @@ blockToNodes opts (Plain xs) ns = blockToNodes opts (Para xs) ns = return (node PARAGRAPH (inlinesToNodes opts xs) : ns) blockToNodes opts (LineBlock lns) ns = blockToNodes opts (linesToPara lns) ns -blockToNodes _ (CodeBlock (_,classes,_) xs) ns = return $ +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) + | otherwise = return (node (CUSTOM_BLOCK (T.pack xs) T.empty) [] : ns) blockToNodes opts (BlockQuote bs) ns = do nodes <- blocksToNodes opts bs return (node BLOCK_QUOTE nodes : ns) @@ -142,9 +142,9 @@ blockToNodes opts (Div _ bs) ns = do blockToNodes opts (DefinitionList items) ns = blockToNodes opts (BulletList items') ns where items' = map dlToBullet items - dlToBullet (term, ((Para xs : ys) : zs)) = + dlToBullet (term, (Para xs : ys) : zs) = Para (term ++ [LineBreak] ++ xs) : ys ++ concat zs - dlToBullet (term, ((Plain xs : ys) : zs)) = + dlToBullet (term, (Plain xs : ys) : zs) = Plain (term ++ [LineBreak] ++ xs) : ys ++ concat zs dlToBullet (term, xs) = Para term : concat xs @@ -264,7 +264,7 @@ inlineToNodes opts (Image _ ils (url,tit)) = (node (IMAGE (T.pack url) (T.pack tit)) (inlinesToNodes opts ils) :) inlineToNodes _ (RawInline fmt xs) | fmt == Format "html" = (node (HTML_INLINE (T.pack xs)) [] :) - | otherwise = (node (CUSTOM_INLINE (T.pack xs) (T.empty)) [] :) + | otherwise = (node (CUSTOM_INLINE (T.pack xs) T.empty) [] :) inlineToNodes opts (Quoted qt ils) = ((node (TEXT start) [] : inlinesToNodes opts ils ++ [node (TEXT end) []]) ++) diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index 101be3fc0..63113ac82 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -33,7 +33,7 @@ module Text.Pandoc.Writers.ConTeXt ( writeConTeXt ) where import Control.Monad.State.Strict import Data.Char (ord) import Data.List (intercalate, intersperse) -import Data.Maybe (catMaybes) +import Data.Maybe (mapMaybe) import Data.Text (Text) import Network.URI (unEscapeString) import Text.Pandoc.BCP47 @@ -82,8 +82,7 @@ pandocToConTeXt options (Pandoc meta blocks) = do meta body <- mapM (elementToConTeXt options) $ hierarchicalize blocks let main = (render' . vcat) body - let layoutFromMargins = intercalate [','] $ catMaybes $ - map (\(x,y) -> + let layoutFromMargins = intercalate [','] $ mapMaybe (\(x,y) -> ((x ++ "=") ++) <$> getField y metadata) [("leftmargin","margin-left") ,("rightmargin","margin-right") @@ -107,8 +106,7 @@ pandocToConTeXt options (Pandoc meta blocks) = do $ (case getField "papersize" metadata of Just ("a4" :: String) -> resetField "papersize" ("A4" :: String) - _ -> id) - $ metadata + _ -> id) metadata let context' = defField "context-dir" (toContextDir $ getField "dir" context) context case writerTemplate options of @@ -150,7 +148,7 @@ stringToConTeXt opts = concatMap (escapeCharForConTeXt opts) toLabel :: String -> String toLabel z = concatMap go z where go x - | elem x ("\\#[]\",{}%()|=" :: String) = "ux" ++ printf "%x" (ord x) + | x `elem` ("\\#[]\",{}%()|=" :: String) = "ux" ++ printf "%x" (ord x) | otherwise = [x] -- | Convert Elements to ConTeXt @@ -206,7 +204,7 @@ blockToConTeXt (Div (ident,_,kvs) bs) = do <> text lng <> "]" $$ txt $$ "\\stop" Nothing -> txt wrapBlank txt = blankline <> txt <> blankline - fmap (wrapBlank . wrapLang . wrapDir . wrapRef) $ blockListToConTeXt bs + (wrapBlank . wrapLang . wrapDir . wrapRef) <$> blockListToConTeXt bs blockToConTeXt (BulletList lst) = do contents <- mapM listItemToConTeXt lst return $ ("\\startitemize" <> if isTightList lst @@ -261,7 +259,7 @@ blockToConTeXt (Table caption aligns widths heads rows) = do if colWidth == 0 then "|" else ("p(" ++ printf "%.2f" colWidth ++ "\\textwidth)|") - let colDescriptors = "|" ++ (concat $ + let colDescriptors = "|" ++ concat ( zipWith colDescriptor widths aligns) headers <- if all null heads then return empty @@ -279,11 +277,11 @@ blockToConTeXt (Table caption aligns widths heads rows) = do tableRowToConTeXt :: PandocMonad m => [[Block]] -> WM m Doc tableRowToConTeXt cols = do cols' <- mapM blockListToConTeXt cols - return $ (vcat (map ("\\NC " <>) cols')) $$ "\\NC\\AR" + return $ vcat (map ("\\NC " <>) cols') $$ "\\NC\\AR" listItemToConTeXt :: PandocMonad m => [Block] -> WM m Doc listItemToConTeXt list = blockListToConTeXt list >>= - return . ("\\item" $$) . (nest 2) + return . ("\\item" $$) . nest 2 defListItemToConTeXt :: PandocMonad m => ([Inline], [[Block]]) -> WM m Doc defListItemToConTeXt (term, defs) = do @@ -358,7 +356,7 @@ inlineToConTeXt (RawInline "tex" str) = return $ text str inlineToConTeXt il@(RawInline _ _) = do report $ InlineNotRendered il return empty -inlineToConTeXt (LineBreak) = return $ text "\\crlf" <> cr +inlineToConTeXt LineBreak = return $ text "\\crlf" <> cr inlineToConTeXt SoftBreak = do wrapText <- gets (writerWrapText . stOptions) return $ case wrapText of @@ -367,7 +365,7 @@ inlineToConTeXt SoftBreak = do WrapPreserve -> cr inlineToConTeXt Space = return space -- Handle HTML-like internal document references to sections -inlineToConTeXt (Link _ txt (('#' : ref), _)) = do +inlineToConTeXt (Link _ txt ('#' : ref, _)) = do opts <- gets stOptions contents <- inlineListToConTeXt txt let ref' = toLabel $ stringToConTeXt opts ref @@ -393,7 +391,7 @@ inlineToConTeXt (Link _ txt (src, _)) = do inlineToConTeXt (Image attr@(_,cls,_) _ (src, _)) = do opts <- gets stOptions let showDim dir = let d = text (show dir) <> "=" - in case (dimension dir attr) of + in case dimension dir attr of Just (Pixel a) -> [d <> text (showInInch opts (Pixel a)) <> "in"] Just (Percent a) -> @@ -432,7 +430,7 @@ inlineToConTeXt (Span (_,_,kvs) ils) = do Just lng -> "\\start\\language[" <> text lng <> "]" <> txt <> "\\stop " Nothing -> txt - fmap (wrapLang . wrapDir) $ inlineListToConTeXt ils + (wrapLang . wrapDir) <$> inlineListToConTeXt ils -- | Craft the section header, inserting the section reference, if supplied. sectionHeader :: PandocMonad m -- cgit v1.2.3 From f270dd9b18de69e87198216f13943b2ceefea8f8 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 29 Oct 2017 14:18:06 -0700 Subject: hlint suggestions. --- src/Text/Pandoc/Writers/AsciiDoc.hs | 2 +- src/Text/Pandoc/Writers/Custom.hs | 2 +- src/Text/Pandoc/Writers/Docbook.hs | 42 +++--- src/Text/Pandoc/Writers/Docx.hs | 228 ++++++++++++++++---------------- src/Text/Pandoc/Writers/DokuWiki.hs | 24 ++-- src/Text/Pandoc/Writers/EPUB.hs | 49 +++---- src/Text/Pandoc/Writers/FB2.hs | 8 +- src/Text/Pandoc/Writers/HTML.hs | 101 +++++++------- src/Text/Pandoc/Writers/Haddock.hs | 30 ++--- src/Text/Pandoc/Writers/ICML.hs | 76 +++++------ src/Text/Pandoc/Writers/JATS.hs | 37 +++--- src/Text/Pandoc/Writers/LaTeX.hs | 102 +++++++------- src/Text/Pandoc/Writers/Man.hs | 34 ++--- src/Text/Pandoc/Writers/Math.hs | 4 +- src/Text/Pandoc/Writers/Ms.hs | 43 +++--- src/Text/Pandoc/Writers/Native.hs | 9 +- src/Text/Pandoc/Writers/ODT.hs | 20 ++- src/Text/Pandoc/Writers/OPML.hs | 10 +- src/Text/Pandoc/Writers/OpenDocument.hs | 29 ++-- src/Text/Pandoc/Writers/RST.hs | 36 ++--- src/Text/Pandoc/Writers/RTF.hs | 55 ++++---- src/Text/Pandoc/Writers/Shared.hs | 10 +- src/Text/Pandoc/Writers/TEI.hs | 33 ++--- src/Text/Pandoc/Writers/Texinfo.hs | 26 ++-- src/Text/Pandoc/Writers/Textile.hs | 6 +- src/Text/Pandoc/Writers/ZimWiki.hs | 53 ++++---- 26 files changed, 527 insertions(+), 542 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/AsciiDoc.hs b/src/Text/Pandoc/Writers/AsciiDoc.hs index 82d422f93..bf58a755f 100644 --- a/src/Text/Pandoc/Writers/AsciiDoc.hs +++ b/src/Text/Pandoc/Writers/AsciiDoc.hs @@ -100,7 +100,7 @@ pandocToAsciiDoc opts (Pandoc meta blocks) = do let context = defField "body" main $ defField "toc" (writerTableOfContents opts && - Data.Maybe.isJust (writerTemplate opts)) + isJust (writerTemplate opts)) $defField "titleblock" titleblock metadata' case writerTemplate opts of Nothing -> return main diff --git a/src/Text/Pandoc/Writers/Custom.hs b/src/Text/Pandoc/Writers/Custom.hs index 09cf3fac8..87b97dcee 100644 --- a/src/Text/Pandoc/Writers/Custom.hs +++ b/src/Text/Pandoc/Writers/Custom.hs @@ -235,7 +235,7 @@ inlineToCustom (Math InlineMath str) = inlineToCustom (RawInline format str) = callFunc "RawInline" format str -inlineToCustom (LineBreak) = callFunc "LineBreak" +inlineToCustom LineBreak = callFunc "LineBreak" inlineToCustom (Link attr txt (src,tit)) = callFunc "Link" txt src tit (attrToMap attr) diff --git a/src/Text/Pandoc/Writers/Docbook.hs b/src/Text/Pandoc/Writers/Docbook.hs index d6b7f7cad..24df7e2b4 100644 --- a/src/Text/Pandoc/Writers/Docbook.hs +++ b/src/Text/Pandoc/Writers/Docbook.hs @@ -33,7 +33,7 @@ module Text.Pandoc.Writers.Docbook ( writeDocbook4, writeDocbook5 ) where import Control.Monad.Reader import Data.Char (toLower) import Data.Generics (everywhere, mkT) -import Data.List (intercalate, isPrefixOf, isSuffixOf, stripPrefix) +import Data.List (isPrefixOf, isSuffixOf, stripPrefix) import Data.Monoid (Any (..)) import Data.Text (Text) import qualified Text.Pandoc.Builder as B @@ -78,7 +78,7 @@ authorToDocbook opts name' = do (firstname, lastname) = case lengthname of 0 -> ("","") 1 -> ("", name) - n -> (intercalate " " (take (n-1) namewords), last namewords) + n -> (unwords (take (n-1) namewords), last namewords) in inTagsSimple "firstname" (text $ escapeStringForXML firstname) $$ inTagsSimple "surname" (text $ escapeStringForXML lastname) @@ -99,9 +99,9 @@ writeDocbook opts (Pandoc meta blocks) = do else Nothing let render' :: Doc -> Text render' = render colwidth - let opts' = if (maybe False (("/book>" `isSuffixOf`) . trimr) + let opts' = if maybe False (("/book>" `isSuffixOf`) . trimr) (writerTemplate opts) && - TopLevelDefault == writerTopLevelDivision opts) + TopLevelDefault == writerTopLevelDivision opts then opts{ writerTopLevelDivision = TopLevelChapter } else opts -- The numbering here follows LaTeX's internal numbering @@ -114,16 +114,16 @@ writeDocbook opts (Pandoc meta blocks) = do let meta' = B.setMeta "author" auths' meta metadata <- metaToJSON opts (fmap (render' . vcat) . - (mapM (elementToDocbook opts' startLvl) . - hierarchicalize)) + mapM (elementToDocbook opts' startLvl) . + hierarchicalize) (fmap render' . 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 - _ -> False) - $ metadata + $ + defField "mathml" (case writerHTMLMathMethod opts of + MathML -> True + _ -> False) metadata case writerTemplate opts of Nothing -> return main Just tpl -> renderTemplate' tpl context @@ -170,7 +170,7 @@ plainToPara x = x deflistItemsToDocbook :: PandocMonad m => WriterOptions -> [([Inline],[[Block]])] -> DB m Doc deflistItemsToDocbook opts items = - vcat <$> mapM (\(term, defs) -> deflistItemToDocbook opts term defs) items + vcat <$> mapM (uncurry (deflistItemToDocbook opts)) items -- | Convert a term and a list of blocks into a Docbook varlistentry. deflistItemToDocbook :: PandocMonad m @@ -196,7 +196,7 @@ imageToDocbook _ attr src = selfClosingTag "imagedata" $ ("fileref", src) : idAndRole attr ++ dims where dims = go Width "width" ++ go Height "depth" - go dir dstr = case (dimension dir attr) of + go dir dstr = case dimension dir attr of Just a -> [(dstr, show a)] Nothing -> [] @@ -217,7 +217,7 @@ blockToDocbook opts (Div (ident,_,_) bs) = do (if null ident then mempty else selfClosingTag "anchor" [("id", ident)]) $$ contents -blockToDocbook _ h@(Header _ _ _) = do +blockToDocbook _ h@Header{} = do -- should not occur after hierarchicalize, except inside lists/blockquotes report $ BlockNotRendered h return empty @@ -230,9 +230,9 @@ blockToDocbook opts (Para [Image attr txt (src,'f':'i':'g':':':_)]) = do else inTagsSimple "title" alt return $ inTagsIndented "figure" $ capt $$ - (inTagsIndented "mediaobject" $ - (inTagsIndented "imageobject" - (imageToDocbook opts attr src)) $$ + inTagsIndented "mediaobject" ( + inTagsIndented "imageobject" + (imageToDocbook opts attr src) $$ inTagsSimple "textobject" (inTagsSimple "phrase" alt)) blockToDocbook opts (Para lst) | hasLineBreaks lst = (flush . nowrap . inTagsSimple "literallayout") @@ -275,7 +275,7 @@ blockToDocbook opts (OrderedList (start, numstyle, _) (first:rest)) = do first' <- blocksToDocbook opts (map plainToPara first) rest' <- listItemsToDocbook opts rest return $ - (inTags True "listitem" [("override",show start)] first') $$ + inTags True "listitem" [("override",show start)] first' $$ rest' return $ inTags True "orderedlist" attribs items blockToDocbook opts (DefinitionList lst) = do @@ -308,7 +308,7 @@ blockToDocbook opts (Table caption aligns widths headers rows) = do body' <- (inTagsIndented "tbody" . vcat) <$> mapM (tableRowToDocbook opts) rows return $ inTagsIndented tableType $ captionDoc $$ - (inTags True "tgroup" [("cols", show (length headers))] $ + inTags True "tgroup" [("cols", show (length headers))] ( coltags $$ head' $$ body') hasLineBreaks :: [Inline] -> Bool @@ -406,7 +406,7 @@ inlineToDocbook _ SoftBreak = return space inlineToDocbook opts (Link attr txt (src, _)) | Just email <- stripPrefix "mailto:" src = let emailLink = inTagsSimple "email" $ text $ - escapeStringForXML $ email + escapeStringForXML email in case txt of [Str s] | escapeURI s == email -> return emailLink _ -> do contents <- inlinesToDocbook opts txt @@ -414,7 +414,7 @@ inlineToDocbook opts (Link attr txt (src, _)) char '(' <> emailLink <> char ')' | otherwise = do version <- ask - (if isPrefixOf "#" src + (if "#" `isPrefixOf` src then inTags False "link" $ ("linkend", writerIdentifierPrefix opts ++ drop 1 src) : idAndRole attr else if version == DocBook5 then inTags False "link" $ ("xlink:href", src) : idAndRole attr diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 3ab4548a2..d146ebf84 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE DeriveFunctor #-} + {-# LANGUAGE PatternGuards #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} @@ -177,16 +177,16 @@ renumIdMap :: Int -> [Element] -> M.Map String String renumIdMap _ [] = M.empty renumIdMap n (e:es) | Just oldId <- findAttr (QName "Id" Nothing Nothing) e = - M.insert oldId ("rId" ++ (show n)) (renumIdMap (n+1) es) + M.insert oldId ("rId" ++ show n) (renumIdMap (n+1) es) | otherwise = renumIdMap n es replaceAttr :: (QName -> Bool) -> String -> [XML.Attr] -> [XML.Attr] replaceAttr _ _ [] = [] replaceAttr f val (a:as) | f (attrKey a) = - (XML.Attr (attrKey a) val) : (replaceAttr f val as) - | otherwise = a : (replaceAttr f val as) + XML.Attr (attrKey a) val : replaceAttr f val as + | otherwise = a : replaceAttr f val as -renumId :: (QName -> Bool) -> (M.Map String String) -> Element -> Element +renumId :: (QName -> Bool) -> M.Map String String -> Element -> Element renumId f renumMap e | Just oldId <- findAttrBy f e , Just newId <- M.lookup oldId renumMap = @@ -195,7 +195,7 @@ renumId f renumMap e e { elAttribs = attrs' } | otherwise = e -renumIds :: (QName -> Bool) -> (M.Map String String) -> [Element] -> [Element] +renumIds :: (QName -> Bool) -> M.Map String String -> [Element] -> [Element] renumIds f renumMap = map (renumId f renumMap) -- | Certain characters are invalid in XML even if escaped. @@ -228,7 +228,7 @@ writeDocx :: (PandocMonad m) -> Pandoc -- ^ Document to convert -> m BL.ByteString writeDocx opts doc@(Pandoc meta _) = do - let doc' = walk fixDisplayMath $ doc + let doc' = walk fixDisplayMath doc username <- P.lookupEnv "USERNAME" utctime <- P.getCurrentTime distArchive <- (toArchive . BL.fromStrict) <$> @@ -243,12 +243,12 @@ writeDocx opts doc@(Pandoc meta _) = do let mbsectpr = filterElementName (wname (=="sectPr")) parsedDoc -- Gets the template size - let mbpgsz = mbsectpr >>= (filterElementName (wname (=="pgSz"))) - let mbAttrSzWidth = (elAttribs <$> mbpgsz) >>= (lookupAttrBy ((=="w") . qName)) + let mbpgsz = mbsectpr >>= filterElementName (wname (=="pgSz")) + let mbAttrSzWidth = (elAttribs <$> mbpgsz) >>= lookupAttrBy ((=="w") . qName) - let mbpgmar = mbsectpr >>= (filterElementName (wname (=="pgMar"))) - let mbAttrMarLeft = (elAttribs <$> mbpgmar) >>= (lookupAttrBy ((=="left") . qName)) - let mbAttrMarRight = (elAttribs <$> mbpgmar) >>= (lookupAttrBy ((=="right") . qName)) + let mbpgmar = mbsectpr >>= filterElementName (wname (=="pgMar")) + let mbAttrMarLeft = (elAttribs <$> mbpgmar) >>= lookupAttrBy ((=="left") . qName) + let mbAttrMarRight = (elAttribs <$> mbpgmar) >>= lookupAttrBy ((=="right") . qName) -- Get the avaible area (converting the size and the margins to int and -- doing the difference @@ -303,7 +303,7 @@ writeDocx opts doc@(Pandoc meta _) = do envRTL = isRTLmeta , envChangesAuthor = fromMaybe "unknown" username , envChangesDate = formatTime defaultTimeLocale "%FT%XZ" utctime - , envPrintWidth = (maybe 420 (\x -> quot x 20) pgContentWidth) + , envPrintWidth = maybe 420 (\x -> quot x 20) pgContentWidth } @@ -446,7 +446,7 @@ writeDocx opts doc@(Pandoc meta _) = do (elChildren sectpr') in add_attrs (elAttribs sectpr') $ mknode "w:sectPr" [] cs - Nothing -> (mknode "w:sectPr" [] ()) + Nothing -> mknode "w:sectPr" [] () -- let sectpr = fromMaybe (mknode "w:sectPr" [] ()) mbsectpr' let contents' = contents ++ [sectpr] @@ -489,7 +489,7 @@ writeDocx opts doc@(Pandoc meta _) = do map newTextPropToOpenXml newDynamicTextProps ++ (case writerHighlightStyle opts of Nothing -> [] - Just sty -> (styleToOpenXml styleMaps sty)) + Just sty -> styleToOpenXml styleMaps sty) let styledoc' = styledoc{ elContent = elContent styledoc ++ map Elem newstyles } let styleEntry = toEntry stylepath epochtime $ renderXml styledoc' @@ -641,8 +641,8 @@ styleToOpenXml sm style = , mknode "w:link" [("w:val","VerbatimChar")] () , mknode "w:pPr" [] $ mknode "w:wordWrap" [("w:val","off")] () - : ( maybe [] (\col -> [mknode "w:shd" [("w:val","clear"),("w:fill",drop 1 $ fromColor col)] ()]) - $ backgroundColor style ) + : + maybe [] (\col -> [mknode "w:shd" [("w:val","clear"),("w:fill",drop 1 $ fromColor col)] ()]) (backgroundColor style) ] copyChildren :: (PandocMonad m) => Archive -> Archive -> String -> Integer -> [String] -> m Entry @@ -747,11 +747,11 @@ getNumId = (((baseListId - 1) +) . length) `fmap` gets stLists makeTOC :: (PandocMonad m) => WriterOptions -> WS m [Element] makeTOC opts | writerTableOfContents opts = do - let depth = "1-"++(show (writerTOCDepth opts)) + let depth = "1-"++show (writerTOCDepth opts) let tocCmd = "TOC \\o \""++depth++"\" \\h \\z \\u" tocTitle <- gets stTocTitle title <- withParaPropM (pStyleM "TOC Heading") (blocksToOpenXML opts [Para tocTitle]) - return $ + return [mknode "w:sdt" [] ([ mknode "w:sdtPr" [] ( mknode "w:docPartObj" [] ( @@ -803,7 +803,7 @@ writeOpenXML opts (Pandoc meta blocks) = do convertSpace (Str x : Str y : xs) = Str (x ++ y) : xs convertSpace xs = xs let blocks' = bottomUp convertSpace blocks - doc' <- (setFirstPara >> blocksToOpenXML opts blocks') + doc' <- setFirstPara >> blocksToOpenXML opts blocks' notes' <- reverse <$> gets stFootnotes comments <- reverse <$> gets stComments let toComment (kvs, ils) = do @@ -1106,7 +1106,7 @@ formattedString str = [w] -> formattedString' w ws -> do sh <- formattedRun [mknode "w:softHyphen" [] ()] - (intercalate sh) <$> mapM formattedString' ws + intercalate sh <$> mapM formattedString' ws formattedString' :: PandocMonad m => String -> WS m [Element] formattedString' str = do @@ -1134,13 +1134,13 @@ inlineToOpenXML' opts SoftBreak = inlineToOpenXML opts (Str " ") inlineToOpenXML' _ (Span (ident,["comment-start"],kvs) ils) = do modify $ \st -> st{ stComments = (("id",ident):kvs, ils) : stComments st } return [ mknode "w:commentRangeStart" [("w:id", ident)] () ] -inlineToOpenXML' _ (Span (ident,["comment-end"],_) _) = do +inlineToOpenXML' _ (Span (ident,["comment-end"],_) _) = return [ mknode "w:commentRangeEnd" [("w:id", ident)] () - , mknode "w:r" [] - [ mknode "w:rPr" [] - [ mknode "w:rStyle" [("w:val", "CommentReference")] () ] - , mknode "w:commentReference" [("w:id", ident)] () ] - ] + , mknode "w:r" [] + [ mknode "w:rPr" [] + [ mknode "w:rStyle" [("w:val", "CommentReference")] () ] + , mknode "w:commentReference" [("w:id", ident)] () ] + ] inlineToOpenXML' opts (Span (ident,classes,kvs) ils) = do stylemod <- case lookup dynamicStyleKey kvs of Just sty -> do @@ -1166,13 +1166,13 @@ inlineToOpenXML' opts (Span (ident,classes,kvs) ils) = do let author = fromMaybe defaultAuthor (lookup "author" kvs) date = fromMaybe defaultDate (lookup "date" kvs) insId <- gets stInsId - modify $ \s -> s{stInsId = (insId + 1)} + modify $ \s -> s{stInsId = insId + 1} return $ \f -> do x <- f - return $ [ mknode "w:ins" + return [ mknode "w:ins" [("w:id", (show insId)), ("w:author", author), - ("w:date", date)] x] + ("w:date", date)] x ] else return id delmod <- if "insertion" `elem` classes then do @@ -1181,11 +1181,11 @@ inlineToOpenXML' opts (Span (ident,classes,kvs) ils) = do let author = fromMaybe defaultAuthor (lookup "author" kvs) date = fromMaybe defaultDate (lookup "date" kvs) insId <- gets stInsId - modify $ \s -> s{stInsId = (insId + 1)} + modify $ \s -> s{stInsId = insId + 1} return $ \f -> do x <- f return [mknode "w:ins" - [("w:id", (show insId)), + [("w:id", show insId), ("w:author", author), ("w:date", date)] x] else return id @@ -1235,7 +1235,7 @@ inlineToOpenXML' opts (Math mathType str) = do inlineToOpenXML' opts (Cite _ lst) = inlinesToOpenXML opts lst inlineToOpenXML' opts (Code attrs str) = do let unhighlighted = intercalate [br] `fmap` - (mapM formattedString $ lines str) + mapM formattedString (lines str) formatOpenXML _fmtOpts = intercalate [br] . map (map toHlTok) toHlTok (toktype,tok) = mknode "w:r" [] [ mknode "w:rPr" [] @@ -1267,7 +1267,7 @@ inlineToOpenXML' opts (Note bs) = do , envTextProperties = [] }) (withParaPropM (pStyleM "Footnote Text") $ blocksToOpenXML opts $ insertNoteRef bs) - let newnote = mknode "w:footnote" [("w:id", notenum)] $ contents + let newnote = mknode "w:footnote" [("w:id", notenum)] contents modify $ \s -> s{ stFootnotes = newnote : notes } return [ mknode "w:r" [] [ mknode "w:rPr" [] footnoteStyle @@ -1283,7 +1283,7 @@ inlineToOpenXML' opts (Link _ txt (src,_)) = do id' <- case M.lookup src extlinks of Just i -> return i Nothing -> do - i <- ("rId"++) `fmap` ((lift . lift) getUniqueId) + i <- ("rId"++) `fmap` (lift . lift) getUniqueId modify $ \st -> st{ stExternalLinks = M.insert src i extlinks } return i @@ -1294,81 +1294,81 @@ inlineToOpenXML' opts (Image attr alt (src, title)) = do imgs <- gets stImages case M.lookup src imgs of Just (_,_,_,elt,_) -> return [elt] - Nothing -> do + Nothing -> catchError - (do (img, mt) <- P.fetchItem src - ident <- ("rId"++) `fmap` ((lift . lift) getUniqueId) - let (xpt,ypt) = desiredSizeInPoints opts attr - (either (const def) id (imageSize opts img)) - -- 12700 emu = 1 pt - let (xemu,yemu) = fitToPage (xpt * 12700, ypt * 12700) - (pageWidth * 12700) - let cNvPicPr = mknode "pic:cNvPicPr" [] $ - mknode "a:picLocks" [("noChangeArrowheads","1") - ,("noChangeAspect","1")] () - let nvPicPr = mknode "pic:nvPicPr" [] - [ mknode "pic:cNvPr" - [("descr",src),("id","0"),("name","Picture")] () - , cNvPicPr ] - let blipFill = mknode "pic:blipFill" [] - [ mknode "a:blip" [("r:embed",ident)] () - , mknode "a:stretch" [] $ - mknode "a:fillRect" [] () ] - let xfrm = mknode "a:xfrm" [] - [ mknode "a:off" [("x","0"),("y","0")] () - , mknode "a:ext" [("cx",show xemu) - ,("cy",show yemu)] () ] - let prstGeom = mknode "a:prstGeom" [("prst","rect")] $ - mknode "a:avLst" [] () - let ln = mknode "a:ln" [("w","9525")] - [ mknode "a:noFill" [] () - , mknode "a:headEnd" [] () - , mknode "a:tailEnd" [] () ] - let spPr = mknode "pic:spPr" [("bwMode","auto")] - [xfrm, prstGeom, mknode "a:noFill" [] (), ln] - let graphic = mknode "a:graphic" [] $ - mknode "a:graphicData" - [("uri","http://schemas.openxmlformats.org/drawingml/2006/picture")] - [ mknode "pic:pic" [] - [ nvPicPr - , blipFill - , spPr ] ] - let imgElt = mknode "w:r" [] $ - mknode "w:drawing" [] $ - mknode "wp:inline" [] - [ mknode "wp:extent" [("cx",show xemu),("cy",show yemu)] () - , mknode "wp:effectExtent" - [("b","0"),("l","0"),("r","0"),("t","0")] () - , mknode "wp:docPr" [("descr",stringify alt) - ,("title", title) - ,("id","1") - ,("name","Picture")] () - , graphic ] - let imgext = case mt >>= extensionFromMimeType of - Just x -> '.':x - Nothing -> case imageType img of - Just Png -> ".png" - Just Jpeg -> ".jpeg" - Just Gif -> ".gif" - Just Pdf -> ".pdf" - Just Eps -> ".eps" - Just Svg -> ".svg" - Nothing -> "" - if null imgext - then -- without an extension there is no rule for content type - inlinesToOpenXML opts alt -- return alt to avoid corrupted docx - else do - let imgpath = "media/" ++ ident ++ imgext - let mbMimeType = mt <|> getMimeType imgpath - -- insert mime type to use in constructing [Content_Types].xml - modify $ \st -> st{ stImages = - M.insert src (ident, imgpath, mbMimeType, imgElt, img) - $ stImages st } - return [imgElt]) - (\e -> do - report $ CouldNotFetchResource src (show e) - -- emit alt text - inlinesToOpenXML opts alt) + (do (img, mt) <- P.fetchItem src + ident <- ("rId"++) `fmap` ((lift . lift) getUniqueId) + let (xpt,ypt) = desiredSizeInPoints opts attr + (either (const def) id (imageSize opts img)) + -- 12700 emu = 1 pt + let (xemu,yemu) = fitToPage (xpt * 12700, ypt * 12700) + (pageWidth * 12700) + let cNvPicPr = mknode "pic:cNvPicPr" [] $ + mknode "a:picLocks" [("noChangeArrowheads","1") + ,("noChangeAspect","1")] () + let nvPicPr = mknode "pic:nvPicPr" [] + [ mknode "pic:cNvPr" + [("descr",src),("id","0"),("name","Picture")] () + , cNvPicPr ] + let blipFill = mknode "pic:blipFill" [] + [ mknode "a:blip" [("r:embed",ident)] () + , mknode "a:stretch" [] $ + mknode "a:fillRect" [] () ] + let xfrm = mknode "a:xfrm" [] + [ mknode "a:off" [("x","0"),("y","0")] () + , mknode "a:ext" [("cx",show xemu) + ,("cy",show yemu)] () ] + let prstGeom = mknode "a:prstGeom" [("prst","rect")] $ + mknode "a:avLst" [] () + let ln = mknode "a:ln" [("w","9525")] + [ mknode "a:noFill" [] () + , mknode "a:headEnd" [] () + , mknode "a:tailEnd" [] () ] + let spPr = mknode "pic:spPr" [("bwMode","auto")] + [xfrm, prstGeom, mknode "a:noFill" [] (), ln] + let graphic = mknode "a:graphic" [] $ + mknode "a:graphicData" + [("uri","http://schemas.openxmlformats.org/drawingml/2006/picture")] + [ mknode "pic:pic" [] + [ nvPicPr + , blipFill + , spPr ] ] + let imgElt = mknode "w:r" [] $ + mknode "w:drawing" [] $ + mknode "wp:inline" [] + [ mknode "wp:extent" [("cx",show xemu),("cy",show yemu)] () + , mknode "wp:effectExtent" + [("b","0"),("l","0"),("r","0"),("t","0")] () + , mknode "wp:docPr" [("descr",stringify alt) + ,("title", title) + ,("id","1") + ,("name","Picture")] () + , graphic ] + let imgext = case mt >>= extensionFromMimeType of + Just x -> '.':x + Nothing -> case imageType img of + Just Png -> ".png" + Just Jpeg -> ".jpeg" + Just Gif -> ".gif" + Just Pdf -> ".pdf" + Just Eps -> ".eps" + Just Svg -> ".svg" + Nothing -> "" + if null imgext + then -- without an extension there is no rule for content type + inlinesToOpenXML opts alt -- return alt to avoid corrupted docx + else do + let imgpath = "media/" ++ ident ++ imgext + let mbMimeType = mt <|> getMimeType imgpath + -- insert mime type to use in constructing [Content_Types].xml + modify $ \st -> st{ stImages = + M.insert src (ident, imgpath, mbMimeType, imgElt, img) + $ stImages st } + return [imgElt]) + (\e -> do + report $ CouldNotFetchResource src (show e) + -- emit alt text + inlinesToOpenXML opts alt) br :: Element br = breakElement "textWrapping" @@ -1382,12 +1382,12 @@ breakElement kind = mknode "w:r" [] [mknode "w:br" [("w:type",kind)] () ] -- problems. So we want to make sure we insert them into our document. defaultFootnotes :: [Element] defaultFootnotes = [ mknode "w:footnote" - [("w:type", "separator"), ("w:id", "-1")] $ + [("w:type", "separator"), ("w:id", "-1")] [ mknode "w:p" [] $ [mknode "w:r" [] $ [ mknode "w:separator" [] ()]]] , mknode "w:footnote" - [("w:type", "continuationSeparator"), ("w:id", "0")] $ + [("w:type", "continuationSeparator"), ("w:id", "0")] [ mknode "w:p" [] $ [ mknode "w:r" [] $ [ mknode "w:continuationSeparator" [] ()]]]] @@ -1407,7 +1407,7 @@ fitToPage :: (Double, Double) -> Integer -> (Integer, Integer) fitToPage (x, y) pageWidth -- Fixes width to the page width and scales the height | x > fromIntegral pageWidth = - (pageWidth, floor $ ((fromIntegral pageWidth) / x) * y) + (pageWidth, floor $ (fromIntegral pageWidth / x) * y) | otherwise = (floor x, floor y) withDirection :: PandocMonad m => WS m a -> WS m a @@ -1423,8 +1423,8 @@ withDirection x = do if isRTL -- if we are going right-to-left, we (re?)add the properties. then flip local x $ - \env -> env { envParaProperties = (mknode "w:bidi" [] ()) : paraProps' - , envTextProperties = (mknode "w:rtl" [] ()) : textProps' + \env -> env { envParaProperties = mknode "w:bidi" [] () : paraProps' + , envTextProperties = mknode "w:rtl" [] () : textProps' } else flip local x $ \env -> env { envParaProperties = paraProps' , envTextProperties = textProps' diff --git a/src/Text/Pandoc/Writers/DokuWiki.hs b/src/Text/Pandoc/Writers/DokuWiki.hs index 43e2952de..09dd846ba 100644 --- a/src/Text/Pandoc/Writers/DokuWiki.hs +++ b/src/Text/Pandoc/Writers/DokuWiki.hs @@ -93,14 +93,9 @@ pandocToDokuWiki opts (Pandoc meta blocks) = do meta body <- blockListToDokuWiki opts blocks notesExist <- gets stNotes - let notes = if notesExist - then "" -- TODO Was "\n<references />" Check whether I can really remove this: - -- if it is definitely to do with footnotes, can remove this whole bit - else "" - let main = pack $ body ++ notes + let main = pack body let context = defField "body" main - $ defField "toc" (writerTableOfContents opts) - $ metadata + $ defField "toc" (writerTableOfContents opts) metadata case writerTemplate opts of Nothing -> return main Just tpl -> renderTemplate' tpl context @@ -155,7 +150,8 @@ blockToDokuWiki _ b@(RawBlock f str) -- See https://www.dokuwiki.org/wiki:syntax -- use uppercase HTML tag for block-level content: | f == Format "html" = return $ "<HTML>\n" ++ str ++ "\n</HTML>" - | otherwise = "" <$ (report $ BlockNotRendered b) + | otherwise = "" <$ + report (BlockNotRendered b) blockToDokuWiki _ HorizontalRule = return "\n----\n" @@ -199,7 +195,7 @@ blockToDokuWiki opts (Table capt aligns _ headers rows) = do rows' <- mapM (zipWithM (tableItemToDokuWiki opts) aligns) rows let widths = map (maximum . map length) $ transpose (headers':rows') let padTo (width, al) s = - case (width - length s) of + case width - length s of x | x > 0 -> if al == AlignLeft || al == AlignDefault then s ++ replicate x ' ' @@ -294,7 +290,7 @@ listItemToDokuWiki opts items = do _ -> vcat bs indent <- stIndent <$> ask backSlash <- stBackSlashLB <$> ask - let indent' = if backSlash then (drop 2 indent) else indent + let indent' = if backSlash then drop 2 indent else indent return $ indent' ++ "* " ++ contents -- | Convert ordered list item (list of blocks) to DokuWiki. @@ -308,7 +304,7 @@ orderedListItemToDokuWiki opts items = do else do indent <- stIndent <$> ask backSlash <- stBackSlashLB <$> ask - let indent' = if backSlash then (drop 2 indent) else indent + let indent' = if backSlash then drop 2 indent else indent return $ indent' ++ "- " ++ contents -- | Convert definition list item (label, list of blocks) to DokuWiki. @@ -322,11 +318,11 @@ definitionListItemToDokuWiki opts (label, items) = do useTags <- stUseTags <$> ask if useTags then return $ "<HTML><dt></HTML>" ++ labelText ++ "<HTML></dt></HTML>\n" ++ - (intercalate "\n" $ map (\d -> "<HTML><dd></HTML>" ++ d ++ "<HTML></dd></HTML>") contents) + intercalate "\n" (map (\d -> "<HTML><dd></HTML>" ++ d ++ "<HTML></dd></HTML>") contents) else do indent <- stIndent <$> ask backSlash <- stBackSlashLB <$> ask - let indent' = if backSlash then (drop 2 indent) else indent + let indent' = if backSlash then drop 2 indent else indent return $ indent' ++ "* **" ++ labelText ++ "** " ++ concat contents -- | True if the list can be handled by simple wiki markup, False if HTML tags will be needed. @@ -419,7 +415,7 @@ consolidateRawBlocks (x:xs) = x : consolidateRawBlocks xs inlineListToDokuWiki :: PandocMonad m => WriterOptions -> [Inline] -> DokuWiki m String inlineListToDokuWiki opts lst = - concat <$> (mapM (inlineToDokuWiki opts) lst) + concat <$> mapM (inlineToDokuWiki opts) lst -- | Convert Pandoc inline element to DokuWiki. inlineToDokuWiki :: PandocMonad m diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index d28187bf0..6bfd78d3c 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -43,7 +43,7 @@ import qualified Data.ByteString.Lazy.Char8 as B8 import Data.Char (isAlphaNum, isAscii, isDigit, toLower) import Data.List (intercalate, isInfixOf, isPrefixOf) import qualified Data.Map as M -import Data.Maybe (catMaybes, fromMaybe) +import Data.Maybe (fromMaybe, mapMaybe, isNothing) import qualified Data.Set as Set import qualified Data.Text as TS import qualified Data.Text.Lazy as TL @@ -280,11 +280,10 @@ getCreator s meta = getList s meta handleMetaValue getDate :: String -> Meta -> [Date] getDate s meta = getList s meta handleMetaValue where handleMetaValue (MetaMap m) = - Date{ dateText = maybe "" id $ + Date{ dateText = fromMaybe "" $ M.lookup "text" m >>= normalizeDate' . metaValueToString , dateEvent = metaValueToString <$> M.lookup "event" m } - handleMetaValue mv = Date { dateText = maybe "" - id $ normalizeDate' $ metaValueToString mv + handleMetaValue mv = Date { dateText = fromMaybe "" $ normalizeDate' $ metaValueToString mv , dateEvent = Nothing } simpleList :: String -> Meta -> [String] @@ -334,7 +333,7 @@ metadataFromMeta opts meta = EPUBMetadata{ rights = metaValueToString <$> lookupMeta "rights" meta coverImage = lookup "epub-cover-image" (writerVariables opts) `mplus` (metaValueToString <$> lookupMeta "cover-image" meta) - stylesheets = maybe [] id + stylesheets = fromMaybe [] (metaValueToPaths <$> lookupMeta "stylesheet" meta) ++ [f | ("css",f) <- writerVariables opts] pageDirection = case map toLower . metaValueToString <$> @@ -434,7 +433,7 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do -- mediaRef <- P.newIORef [] Pandoc _ blocks <- walkM (transformInline opts') doc >>= walkM transformBlock - picEntries <- (catMaybes . map (snd . snd)) <$> (gets stMediaPaths) + picEntries <- (mapMaybe (snd . snd)) <$> gets stMediaPaths -- handle fonts let matchingGlob f = do xs <- lift $ P.glob f @@ -479,7 +478,7 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do mbnum <- if "unnumbered" `elem` classes then return Nothing else case splitAt (n - 1) nums of - (ks, (m:_)) -> do + (ks, m:_) -> do let nums' = ks ++ [m+1] put nums' return $ Just (ks ++ [m]) @@ -528,22 +527,23 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do let chapToEntry num (Chapter mbnum bs) = mkEntry ("text/" ++ showChapter num) <$> - (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) + 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 && - "<math" `isInfixOf` (B8.unpack $ fromEntry ent) + "<math" `isInfixOf` + B8.unpack (fromEntry ent) let containsSVG ent = epub3 && - "<svg" `isInfixOf` (B8.unpack $ fromEntry ent) + "<svg" `isInfixOf` + B8.unpack (fromEntry ent) let props ent = ["mathml" | containsMathML ent] ++ ["svg" | containsSVG ent] -- contents.opf @@ -577,7 +577,7 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do 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 + currentTime <- lift P.getCurrentTime let contentsData = UTF8.fromStringLazy $ ppTopElement $ unode "package" ! [("version", case version of EPUB2 -> "2.0" @@ -594,8 +594,8 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do ,("media-type","application/xhtml+xml")] ++ [("properties","nav") | epub3 ]) $ () ] ++ - [ (unode "item" ! [("id","style"), ("href",fp) - ,("media-type","text/css")] $ ()) | + [ unode "item" ! [("id","style"), ("href",fp) + ,("media-type","text/css")] $ () | fp <- map eRelativePath stylesheetEntries ] ++ map chapterNode (cpgEntry ++ (tpEntry : chapterEntries)) ++ (case cpicEntry of @@ -605,7 +605,8 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do (pictureNode x)]) ++ map pictureNode picEntries ++ map fontNode fontEntries - , unode "spine" ! ([("toc","ncx")] ++ progressionDirection) $ + , unode "spine" ! ( + ("toc","ncx") : progressionDirection) $ case epubCoverImage metadata of Nothing -> [] Just _ -> [ unode "itemref" ! @@ -684,7 +685,7 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do Nothing -> [] Just img -> [unode "meta" ! [("name","cover"), ("content", toId img)] $ ()] - , unode "docTitle" $ unode "text" $ plainTitle + , unode "docTitle" $ unode "text" plainTitle , unode "navMap" $ tpNode : navMap ] @@ -826,7 +827,7 @@ metadataElement version md currentTime = ("content",toId img)] $ ()]) $ epubCoverImage md modifiedNodes = [ unode "meta" ! [("property", "dcterms:modified")] $ - (showDateTimeISO8601 currentTime) | version == EPUB3 ] + showDateTimeISO8601 currentTime | version == EPUB3 ] dcTag n s = unode ("dc:" ++ n) s dcTag' n s = [dcTag n s] toIdentifierNode id' (Identifier txt scheme) @@ -895,7 +896,7 @@ transformTag :: PandocMonad m -> E m (Tag String) transformTag tag@(TagOpen name attr) | name `elem` ["video", "source", "img", "audio"] && - lookup "data-external" attr == Nothing = do + isNothing (lookup "data-external" attr) = do let src = fromAttrib "src" tag let poster = fromAttrib "poster" tag newsrc <- modifyMediaRef src diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index 6d61ea62c..cf96393ca 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -344,7 +344,7 @@ blockToXml (OrderedList a bss) = do item <- cMapM blockToXml $ plainToPara $ indentBlocks (mrk ++ " ") bs modify (\s -> s { parentListMarker = pmrk }) -- old parent marker return item - concat <$> (zipWithM mkitem markers bss) + concat <$> zipWithM mkitem markers bss blockToXml (BulletList bss) = do state <- get let level = parentBulletLevel state @@ -408,7 +408,7 @@ blockToXml Null = return [] paraToPlain :: [Block] -> [Block] paraToPlain [] = [] paraToPlain (Para inlines : rest) = - Plain (inlines) : Plain ([LineBreak]) : paraToPlain rest + Plain inlines : Plain [LineBreak] : paraToPlain rest paraToPlain (p:rest) = p : paraToPlain rest -- Replace plain text with paragraphs and add line break after paragraphs. @@ -416,9 +416,9 @@ paraToPlain (p:rest) = p : paraToPlain rest plainToPara :: [Block] -> [Block] plainToPara [] = [] plainToPara (Plain inlines : rest) = - Para (inlines) : plainToPara rest + Para inlines : plainToPara rest plainToPara (Para inlines : rest) = - Para (inlines) : Plain [LineBreak] : plainToPara rest + Para inlines : Plain [LineBreak] : plainToPara rest plainToPara (p:rest) = p : plainToPara rest -- Simulate increased indentation level. Will not really work diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 9cb3aced8..ddbd9e972 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -215,7 +215,7 @@ writeHtmlString' st opts d = do defField "body" (renderHtml' body) context' writeHtml' :: PandocMonad m => WriterState -> WriterOptions -> Pandoc -> m Html -writeHtml' st opts d = do +writeHtml' st opts d = case writerTemplate opts of Just _ -> preEscapedText <$> writeHtmlString' st opts d Nothing -> do @@ -274,7 +274,8 @@ pandocToHtml opts (Pandoc meta blocks) = do (H.script ! A.src (toValue $ url ++ "contrib/auto-render.min.js") $ mempty) <> - (H.script $ + ( + H.script "document.addEventListener(\"DOMContentLoaded\", function() {\n renderMathInElement(document.body);\n});") <> (H.link ! A.rel "stylesheet" ! A.href (toValue $ url ++ "katex.min.css")) @@ -315,7 +316,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" (stHtml5 st) $ + defField "html5" (stHtml5 st) metadata return (thebody, context) @@ -334,9 +335,9 @@ toList :: PandocMonad m toList listop opts items = do slideVariant <- gets stSlideVariant return $ - if (writerIncremental opts) - then if (slideVariant /= RevealJsSlides) - then (listop $ mconcat items) ! A.class_ "incremental" + 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 @@ -364,7 +365,7 @@ tableOfContents opts sects = do -- | Convert section number to string showSecNum :: [Int] -> String -showSecNum = concat . intersperse "." . map show +showSecNum = intercalate "." . map show -- | Converts an Element to a list item for a table of contents, -- retrieving the appropriate identifier from state. @@ -390,7 +391,7 @@ elementToListItem opts (Sec lev num (id',classes,_) headerText subsecs) let revealSlash = ['/' | slideVariant== RevealJsSlides] return $ Just $ if null id' - then (H.a $ toHtml txt) >> subList + then H.a (toHtml txt) >> subList else (H.a ! A.href (toValue $ "#" ++ revealSlash ++ writerIdentifierPrefix opts ++ id') $ toHtml txt) >> subList @@ -419,7 +420,7 @@ elementToHtml slideLevel opts (Sec level num (id',classes,keyvals) title' elemen modify (\st -> st{ stElement = False}) return res - let isSec (Sec _ _ _ _ _) = True + let isSec (Sec{}) = True isSec (Blk _) = False let isPause (Blk x) = x == Para [Str ".",Space,Str ".",Space,Str "."] isPause _ = False @@ -448,7 +449,8 @@ elementToHtml slideLevel opts (Sec level num (id',classes,keyvals) title' elemen let attr = (id',classes',keyvals) if titleSlide then do - t <- addAttrs opts attr $ secttag $ header' + t <- addAttrs opts attr $ + secttag header' return $ (if slideVariant == RevealJsSlides then H5.section @@ -468,21 +470,19 @@ 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 slideVariant /= NoSlides - then H.div ! A.class_ "footnotes slide" $ x - else H.div ! A.class_ "footnotes" $ x + let container x + | html5 = H5.section ! A.class_ "footnotes" $ x + | slideVariant /= NoSlides = H.div ! A.class_ "footnotes slide" $ x + | otherwise = H.div ! A.class_ "footnotes" $ x return $ if null notes then mempty - else nl opts >> (container - $ nl opts >> hrtag >> nl opts >> + 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) -parseMailto s = do +parseMailto s = case break (==':') s of (xs,':':addr) | map toLower xs == "mailto" -> do let (name', rest) = span (/='@') addr @@ -514,8 +514,8 @@ obfuscateLink opts attr (TL.unpack . renderHtml -> txt) s = ReferenceObfuscation -> -- need to use preEscapedString or &'s are escaped to & in URL return $ - preEscapedString $ "<a href=\"" ++ (obfuscateString s') - ++ "\" class=\"email\">" ++ (obfuscateString txt) ++ "</a>" + preEscapedString $ "<a href=\"" ++ obfuscateString s' + ++ "\" class=\"email\">" ++ obfuscateString txt ++ "</a>" JavascriptObfuscation -> return $ (H.script ! A.type_ "text/javascript" $ @@ -586,7 +586,7 @@ dimensionsToAttrList attr = consolidateStyles $ go Width ++ go Height (ss, rest) -> ("style", intercalate ";" $ map snd ss) : rest isStyle ("style", _) = True isStyle _ = False - go dir = case (dimension dir attr) of + go dir = case dimension dir attr of (Just (Pixel a)) -> [(show dir, show a)] (Just x) -> [("style", show dir ++ ":" ++ show x)] Nothing -> [] @@ -599,9 +599,7 @@ imageExts = [ "art", "bmp", "cdr", "cdt", "cpt", "cr2", "crw", "djvu", "erf", treatAsImage :: FilePath -> Bool treatAsImage fp = - let path = case uriPath `fmap` parseURIReference fp of - Nothing -> fp - Just up -> up + let path = fromMaybe fp (uriPath `fmap` parseURIReference fp) ext = map toLower $ drop 1 $ takeExtension path in null ext || ext `elem` imageExts @@ -674,13 +672,17 @@ blockToHtml opts (Div attr@(ident, classes, kvs') bs) = do slideVariant <- gets stSlideVariant if speakerNotes then case slideVariant of - RevealJsSlides -> addAttrs opts' attr $ H5.aside $ contents' + RevealJsSlides -> addAttrs opts' attr $ + H5.aside contents' DZSlides -> do - t <- addAttrs opts' attr $ H5.div $ contents' - return $ t ! (H5.customAttribute "role" "note") - NoSlides -> addAttrs opts' attr $ H.div $ contents' + t <- addAttrs opts' attr $ + H5.div contents' + return $ t ! H5.customAttribute "role" "note" + NoSlides -> addAttrs opts' attr $ + H.div contents' _ -> return mempty - else addAttrs opts (ident, classes', kvs) $ divtag $ contents' + else addAttrs opts (ident, classes', kvs) $ + divtag contents' blockToHtml opts (RawBlock f str) = do ishtml <- isRawHtml f if ishtml @@ -692,7 +694,7 @@ blockToHtml opts (RawBlock f str) = do else do report $ BlockNotRendered (RawBlock f str) return mempty -blockToHtml _ (HorizontalRule) = do +blockToHtml _ HorizontalRule = do html5 <- gets stHtml5 return $ if html5 then H5.hr else H.hr blockToHtml opts (CodeBlock (id',classes,keyvals) rawCode) = do @@ -768,12 +770,8 @@ blockToHtml opts (OrderedList (startnum, numstyle, _) lst) = do let numstyle' = case numstyle of Example -> "decimal" _ -> camelCaseToHyphenated $ show numstyle - let attribs = (if startnum /= 1 - then [A.start $ toValue startnum] - else []) ++ - (if numstyle == Example - then [A.class_ "example"] - else []) ++ + let attribs = ([A.start $ toValue startnum | startnum /= 1]) ++ + ([A.class_ "example" | numstyle == Example]) ++ (if numstyle /= DefaultStyle then if html5 then [A.type_ $ @@ -794,7 +792,7 @@ blockToHtml opts (DefinitionList lst) = do do term' <- if null term then return mempty else liftM H.dt $ inlineListToHtml opts term - defs' <- mapM ((liftM (\x -> H.dd $ (x >> nl opts))) . + defs' <- mapM (liftM (\x -> H.dd $ (x >> nl opts)) . blockListToHtml opts) defs return $ mconcat $ nl opts : term' : nl opts : intersperse (nl opts) defs') lst @@ -848,7 +846,7 @@ tableRowToHtml opts aligns rownum cols' = do 0 -> "header" x | x `rem` 2 == 1 -> "odd" _ -> "even" - cols'' <- sequence $ zipWith + cols'' <- zipWithM (\alignment item -> tableItemToHtml opts mkcell alignment item) aligns cols' return $ (H.tr ! A.class_ rowclass $ nl opts >> mconcat cols'') @@ -877,7 +875,8 @@ tableItemToHtml opts tag' align' item = do let tag'' = if null alignStr then tag' else tag' ! attribs - return $ (tag'' $ contents) >> nl opts + return $ ( + tag'' contents) >> nl opts toListItems :: WriterOptions -> [Html] -> [Html] toListItems opts items = map (toListItem opts) items ++ [nl opts] @@ -887,7 +886,7 @@ toListItem opts item = nl opts >> H.li item blockListToHtml :: PandocMonad m => WriterOptions -> [Block] -> StateT WriterState m Html blockListToHtml opts lst = - fmap (mconcat . intersperse (nl opts)) $ mapM (blockToHtml opts) lst + (mconcat . intersperse (nl opts)) <$> mapM (blockToHtml opts) lst -- | Convert list of Pandoc inline elements to HTML. inlineListToHtml :: PandocMonad m => WriterOptions -> [Inline] -> StateT WriterState m Html @@ -915,12 +914,12 @@ inlineToHtml opts inline = do html5 <- gets stHtml5 case inline of (Str str) -> return $ strToHtml str - (Space) -> return $ strToHtml " " - (SoftBreak) -> return $ case writerWrapText opts of - WrapNone -> preEscapedString " " - WrapAuto -> preEscapedString " " - WrapPreserve -> preEscapedString "\n" - (LineBreak) -> return $ (if html5 then H5.br else H.br) + Space -> return $ strToHtml " " + SoftBreak -> return $ case writerWrapText opts of + WrapNone -> preEscapedString " " + WrapAuto -> preEscapedString " " + WrapPreserve -> preEscapedString "\n" + LineBreak -> return $ (if html5 then H5.br else H.br) <> strToHtml "\n" (Span (id',classes,kvs) ils) -> inlineListToHtml opts ils >>= @@ -931,7 +930,7 @@ inlineToHtml opts inline = do "csl-no-smallcaps"]) classes kvs' = if null styles then kvs - else (("style", concat styles) : kvs) + else ("style", concat styles) : kvs styles = ["font-style:normal;" | "csl-no-emph" `elem` classes] ++ ["font-weight:normal;" @@ -1090,12 +1089,12 @@ inlineToHtml opts inline = do -- note: null title included, as in Markdown.pl (Note contents) -> do notes <- gets stNotes - let number = (length notes) + 1 + 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)} + modify $ \st -> st {stNotes = htmlContents:notes} slideVariant <- gets stSlideVariant let revealSlash = ['/' | slideVariant == RevealJsSlides] let link = H.a ! A.href (toValue $ "#" ++ @@ -1134,7 +1133,7 @@ blockListToNote opts ref blocks = _ -> otherBlocks ++ [lastBlock, Plain backlink] in do contents <- blockListToHtml opts blocks' - let noteItem = H.li ! (prefixedId opts ("fn" ++ ref)) $ contents + let noteItem = H.li ! prefixedId opts ("fn" ++ ref) $ contents epubVersion <- gets stEPUBVersion let noteItem' = case epubVersion of Just EPUB3 -> noteItem ! customAttribute "epub:type" "footnote" @@ -1175,7 +1174,7 @@ isMathEnvironment s = "\\begin{" `isPrefixOf` s && allowsMathEnvironments :: HTMLMathMethod -> Bool allowsMathEnvironments (MathJax _) = True -allowsMathEnvironments (MathML) = True +allowsMathEnvironments MathML = True allowsMathEnvironments (WebTeX _) = True allowsMathEnvironments _ = False diff --git a/src/Text/Pandoc/Writers/Haddock.hs b/src/Text/Pandoc/Writers/Haddock.hs index c964ddf74..caa4b9031 100644 --- a/src/Text/Pandoc/Writers/Haddock.hs +++ b/src/Text/Pandoc/Writers/Haddock.hs @@ -76,8 +76,7 @@ pandocToHaddock opts (Pandoc meta blocks) = do (fmap render' . blockListToHaddock opts) (fmap render' . inlineListToHaddock opts) meta - let context = defField "body" main - $ metadata + let context = defField "body" main metadata case writerTemplate opts of Nothing -> return main Just tpl -> renderTemplate' tpl context @@ -118,7 +117,7 @@ blockToHaddock opts (Para inlines) = blockToHaddock opts (LineBlock lns) = blockToHaddock opts $ linesToPara lns blockToHaddock _ b@(RawBlock f str) - | f == "haddock" = do + | f == "haddock" = return $ text str <> text "\n" | otherwise = do report $ BlockNotRendered b @@ -150,16 +149,16 @@ blockToHaddock opts (Table caption aligns widths headers rows) = do isPlainBlock _ = False let hasBlocks = not (all isPlainBlock $ concat . concat $ headers:rows) (nst,tbl) <- case True of - _ | isSimple -> fmap (nest 2,) $ + _ | isSimple -> (nest 2,) <$> pandocTable opts (all null headers) aligns widths rawHeaders rawRows - | not hasBlocks -> fmap (nest 2,) $ + | not hasBlocks -> (nest 2,) <$> pandocTable opts (all null headers) aligns widths rawHeaders rawRows - | otherwise -> fmap (id,) $ + | otherwise -> (id,) <$> gridTable opts blockListToHaddock (all null headers) aligns widths headers rows - return $ (prefixed "> " $ nst $ tbl $$ blankline $$ caption'') $$ blankline + return $ prefixed "> " (nst $ tbl $$ blankline $$ caption'') $$ blankline blockToHaddock opts (BulletList items) = do contents <- mapM (bulletListItemToHaddock opts) items return $ cat contents <> blankline @@ -169,7 +168,7 @@ blockToHaddock opts (OrderedList (start,_,delim) items) = do let markers' = map (\m -> if length m < 3 then m ++ replicate (3 - length m) ' ' else m) markers - contents <- mapM (\(item, num) -> orderedListItemToHaddock opts item num) $ + contents <- mapM (uncurry (orderedListItemToHaddock opts)) $ zip markers' items return $ cat contents <> blankline blockToHaddock opts (DefinitionList items) = do @@ -194,18 +193,17 @@ pandocTable opts headless aligns widths rawHeaders rawRows = do (floor . (fromIntegral (writerColumns opts) *)) widths let makeRow = hcat . intersperse (lblock 1 (text " ")) . - (zipWith3 alignHeader aligns widthsInChars) + zipWith3 alignHeader aligns widthsInChars let rows' = map makeRow rawRows let head' = makeRow rawHeaders let maxRowHeight = maximum $ map height (head':rows') let underline = cat $ intersperse (text " ") $ map (\width -> text (replicate width '-')) widthsInChars - let border = if maxRowHeight > 1 - then text (replicate (sum widthsInChars + - length widthsInChars - 1) '-') - else if headless - then underline - else empty + let border + | maxRowHeight > 1 = text (replicate (sum widthsInChars + + length widthsInChars - 1) '-') + | headless = underline + | otherwise = empty let head'' = if headless then empty else border <> cr <> head' @@ -304,7 +302,7 @@ inlineToHaddock opts (Quoted DoubleQuote lst) = do return $ "“" <> contents <> "”" inlineToHaddock _ (Code _ str) = return $ "@" <> text (escapeString str) <> "@" -inlineToHaddock _ (Str str) = do +inlineToHaddock _ (Str str) = return $ text $ escapeString str inlineToHaddock opts (Math mt str) = do let adjust x = case mt of diff --git a/src/Text/Pandoc/Writers/ICML.hs b/src/Text/Pandoc/Writers/ICML.hs index 650a1c012..4afa23cb9 100644 --- a/src/Text/Pandoc/Writers/ICML.hs +++ b/src/Text/Pandoc/Writers/ICML.hs @@ -19,6 +19,7 @@ module Text.Pandoc.Writers.ICML (writeICML) where import Control.Monad.Except (catchError) import Control.Monad.State.Strict import Data.List (intersperse, isInfixOf, isPrefixOf, stripPrefix) +import Data.Maybe (fromMaybe) import qualified Data.Set as Set import Data.Text as Text (breakOnAll, pack) import Data.Text (Text) @@ -145,8 +146,7 @@ writeICML opts (Pandoc meta blocks) = do context = defField "body" main $ defField "charStyles" (render' $ charStylesToDoc st) $ defField "parStyles" (render' $ parStylesToDoc st) - $ defField "hyperlinks" (render' $ hyperlinksToDoc $ links st) - $ metadata + $ defField "hyperlinks" (render' $ hyperlinksToDoc $ links st) metadata case writerTemplate opts of Nothing -> return main Just tpl -> renderTemplate' tpl context @@ -154,9 +154,7 @@ writeICML opts (Pandoc meta blocks) = do -- | Auxilary functions for parStylesToDoc and charStylesToDoc. contains :: String -> (String, (String, String)) -> [(String, String)] contains s rule = - if isInfixOf (fst rule) s - then [snd rule] - else [] + [snd rule | isInfixOf (fst rule) s] -- | The monospaced font to use as default. monospacedFont :: Doc @@ -180,7 +178,7 @@ parStylesToDoc st = vcat $ map makeStyle $ Set.toAscList $ blockStyles st where makeStyle s = let countSubStrs sub str = length $ Text.breakOnAll (Text.pack sub) (Text.pack str) - attrs = concat $ map (contains s) $ [ + attrs = concatMap (contains s) [ (defListTermName, ("BulletsAndNumberingListType", "BulletList")) , (defListTermName, ("FontStyle", "Bold")) , (tableHeaderName, ("FontStyle", "Bold")) @@ -206,9 +204,9 @@ parStylesToDoc st = vcat $ map makeStyle $ Set.toAscList $ blockStyles st where numbering | isOrderedList = [("NumberingExpression", "^#.^t"), ("NumberingLevel", show nOrds)] | otherwise = [] - listType | isOrderedList && (not $ isInfixOf subListParName s) + listType | isOrderedList && not (isInfixOf subListParName s) = [("BulletsAndNumberingListType", "NumberedList")] - | isBulletList && (not $ isInfixOf subListParName s) + | isBulletList && not (isInfixOf subListParName s) = [("BulletsAndNumberingListType", "BulletList")] | otherwise = [] indent = [("LeftIndent", show indt)] @@ -216,9 +214,9 @@ parStylesToDoc st = vcat $ map makeStyle $ Set.toAscList $ blockStyles st nBlockQuotes = countSubStrs blockQuoteName s nDefLists = countSubStrs defListDefName s indt = max 0 $ defaultListIndent*(nBuls + nOrds - 1) + defaultIndent*(nBlockQuotes + nDefLists) - props = inTags True "Properties" [] $ (basedOn $$ tabList $$ numbForm) + props = inTags True "Properties" [] (basedOn $$ tabList $$ numbForm) where - font = if isInfixOf codeBlockName s + font = if codeBlockName `isInfixOf` s then monospacedFont else empty basedOn = inTags False "BasedOn" [("type", "object")] (text "$ID/NormalParagraphStyle") $$ font @@ -245,7 +243,7 @@ charStylesToDoc :: WriterState -> Doc charStylesToDoc st = vcat $ map makeStyle $ Set.toAscList $ inlineStyles st where makeStyle s = - let attrs = concat $ map (contains s) [ + let attrs = concatMap (contains s) [ (strikeoutName, ("StrikeThru", "true")) , (superscriptName, ("Position", "Superscript")) , (subscriptName, ("Position", "Subscript")) @@ -259,7 +257,7 @@ charStylesToDoc st = vcat $ map makeStyle $ Set.toAscList $ inlineStyles st inTags False "BasedOn" [("type", "object")] (text "$ID/NormalCharacterStyle") $$ font where font = - if isInfixOf codeName s + if codeName `isInfixOf` s then monospacedFont else empty in inTags True "CharacterStyle" ([("Self", "CharacterStyle/"++s), ("Name", s)] ++ attrs') props @@ -279,13 +277,12 @@ hyperlinksToDoc (x:xs) = hyp x $$ hyperlinksToDoc xs hyp (ident, url) = hdest $$ hlink where hdest = selfClosingTag "HyperlinkURLDestination" - [("Self", "HyperlinkURLDestination/"++(escapeColons url)), ("Name","link"), ("DestinationURL",url), ("DestinationUniqueKey","1")] -- HyperlinkURLDestination with more than one colon crashes CS6 + [("Self", "HyperlinkURLDestination/"++escapeColons url), ("Name","link"), ("DestinationURL",url), ("DestinationUniqueKey","1")] -- HyperlinkURLDestination with more than one colon crashes CS6 hlink = inTags True "Hyperlink" [("Self","uf-"++show ident), ("Name",url), ("Source","htss-"++show ident), ("Visible","true"), ("DestinationUniqueKey","1")] $ inTags True "Properties" [] $ inTags False "BorderColor" [("type","enumeration")] (text "Black") - $$ (inTags False "Destination" [("type","object")] - $ text $ "HyperlinkURLDestination/"++(escapeColons (escapeStringForXML url))) -- HyperlinkURLDestination with more than one colon crashes CS6 + $$ inTags False "Destination" [("type","object")] (text $ "HyperlinkURLDestination/"++(escapeColons (escapeStringForXML url))) -- HyperlinkURLDestination with more than one colon crashes CS6 -- | Convert a list of Pandoc blocks to ICML. @@ -305,7 +302,7 @@ blockToICML opts style (Para img@[Image _ txt (_,'f':'i':'g':':':_)]) = do blockToICML opts style (Para lst) = parStyle opts (paragraphName:style) lst blockToICML opts style (LineBlock lns) = blockToICML opts style $ linesToPara lns -blockToICML opts style (CodeBlock _ str) = parStyle opts (codeBlockName:style) $ [Str str] +blockToICML opts style (CodeBlock _ str) = parStyle opts (codeBlockName:style) [Str str] blockToICML _ _ b@(RawBlock f str) | f == Format "icml" = return $ text str | otherwise = do @@ -351,11 +348,10 @@ blockToICML opts style (Table caption aligns widths headers rows) = then rows else headers:rows cells <- rowsToICML tabl (0::Int) - let colWidths w = if w > 0 - then [("SingleColumnWidth",show $ 500 * w)] - else [] - let tupToDoc tup = selfClosingTag "Column" $ [("Name",show $ fst tup)] ++ (colWidths $ snd tup) - let colDescs = vcat $ map tupToDoc $ zip [0..nrCols-1] widths + let colWidths w = + [("SingleColumnWidth",show $ 500 * w) | w > 0] + let tupToDoc tup = selfClosingTag "Column" $ ("Name",show $ fst tup) : (colWidths $ snd tup) + let colDescs = vcat $ zipWith (curry tupToDoc) [0..nrCols-1] widths let tableDoc = return $ inTags True "Table" [ ("AppliedTableStyle","TableStyle/Table") , ("HeaderRowCount", nrHeaders) @@ -391,9 +387,8 @@ listItemToICML opts style isFirst attribs item = doN LowerAlpha = [lowerAlphaName] doN UpperAlpha = [upperAlphaName] doN _ = [] - bw = if beginsWith > 1 - then [beginsWithName ++ show beginsWith] - else [] + bw = + [beginsWithName ++ show beginsWith | beginsWith > 1] in doN numbStl ++ bw makeNumbStart Nothing = [] stl = if isFirst @@ -402,7 +397,7 @@ listItemToICML opts style isFirst attribs item = stl' = makeNumbStart attribs ++ stl in if length item > 1 then do - let insertTab (Para lst) = blockToICML opts (subListParName:style) $ Para $ (Str "\t"):lst + let insertTab (Para lst) = blockToICML opts (subListParName:style) $ Para $ Str "\t":lst insertTab block = blockToICML opts style block f <- blockToICML opts stl' $ head item r <- mapM insertTab $ tail item @@ -413,7 +408,7 @@ definitionListItemToICML :: PandocMonad m => WriterOptions -> Style -> ([Inline] definitionListItemToICML opts style (term,defs) = do term' <- parStyle opts (defListTermName:style) term defs' <- mapM (blocksToICML opts (defListDefName:style)) defs - return $ intersperseBrs $ (term' : defs') + return $ intersperseBrs (term' : defs') -- | Convert a list of inline elements to ICML. @@ -453,8 +448,8 @@ inlineToICML opts style (Link _ lst (url, title)) = do state $ \st -> let ident = if null $ links st then 1::Int - else 1 + (fst $ head $ links st) - newst = st{ links = (ident, url):(links st) } + else 1 + fst (head $ links st) + newst = st{ links = (ident, url):links st } cont = inTags True "HyperlinkTextSource" [("Self","htss-"++show ident), ("Name",title), ("Hidden","false")] content in (cont, newst) @@ -465,7 +460,7 @@ inlineToICML opts style (Span _ lst) = inlinesToICML opts style lst -- | Convert a list of block elements to an ICML footnote. 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 + let insertTab (Para ls) = blockToICML opts (footnoteName:style) $ Para $ Str "\t":ls insertTab block = blockToICML opts (footnoteName:style) block in do contents <- mapM insertTab lst @@ -477,11 +472,11 @@ footnoteToICML opts style lst = -- | Auxiliary function to merge Space elements into the adjacent Strs. mergeSpaces :: [Inline] -> [Inline] -mergeSpaces ((Str s):(x:((Str s'):xs))) | isSp x = +mergeSpaces (Str s:(x:(Str s':xs))) | isSp x = mergeSpaces $ Str(s++" "++s') : xs -mergeSpaces (x:((Str s):xs)) | isSp x = mergeSpaces $ Str (" "++s) : xs -mergeSpaces ((Str s):(x:xs)) | isSp x = mergeSpaces $ Str (s++" ") : xs -mergeSpaces (x:xs) = x : (mergeSpaces xs) +mergeSpaces (x:(Str s:xs)) | isSp x = mergeSpaces $ Str (" "++s) : xs +mergeSpaces (Str s:(x:xs)) | isSp x = mergeSpaces $ Str (s++" ") : xs +mergeSpaces (x:xs) = x : mergeSpaces xs mergeSpaces [] = [] isSp :: Inline -> Bool @@ -509,7 +504,7 @@ parStyle opts style lst = begins = filter (isPrefixOf beginsWithName) style in if null begins then ats - else let i = maybe "" id $ stripPrefix beginsWithName $ head begins + else let i = fromMaybe "" $ stripPrefix beginsWithName $ head begins in ("NumberingStartAt", i) : ats else [attrs] in do @@ -522,12 +517,12 @@ 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 - in do + in state $ \st -> - let styles = if null stlStr - then st - else st{ inlineStyles = Set.insert stlStr $ inlineStyles st } - in (doc, styles) + let styles = if null stlStr + then st + else st{ inlineStyles = Set.insert stlStr $ inlineStyles st } + in (doc, styles) -- | Transform a Style to a tuple of String (eliminating duplicates and ordered) and corresponding attribute. styleToStrAttr :: Style -> (String, [(String, String)]) @@ -580,6 +575,5 @@ imageICML opts style attr (src, _) = do ] doc = inTags True "CharacterStyleRange" attrs $ inTags True "Rectangle" [("Self","uec"), ("StrokeWeight", "0"), - ("ItemTransform", scale++" "++hw++" -"++hh)] - $ (props $$ image) + ("ItemTransform", scale++" "++hw++" -"++hh)] (props $$ image) state $ \st -> (doc, st{ inlineStyles = Set.insert stlStr $ inlineStyles st } ) diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs index 4efd00ee5..a62286fa3 100644 --- a/src/Text/Pandoc/Writers/JATS.hs +++ b/src/Text/Pandoc/Writers/JATS.hs @@ -1,5 +1,5 @@ {-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE PatternGuards #-} + {- Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> @@ -34,7 +34,7 @@ module Text.Pandoc.Writers.JATS ( writeJATS ) where import Control.Monad.Reader import Data.Char (toLower) import Data.Generics (everywhere, mkT) -import Data.List (intercalate, isSuffixOf, partition) +import Data.List (isSuffixOf, partition) import Data.Maybe (fromMaybe) import Data.Text (Text) import qualified Text.Pandoc.Builder as B @@ -78,7 +78,7 @@ authorToJATS opts name' = do (firstname, lastname) = case lengthname of 0 -> ("","") 1 -> ("", name) - n -> (intercalate " " (take (n-1) namewords), last namewords) + n -> (unwords (take (n-1) namewords), last namewords) in inTagsSimple "firstname" (text $ escapeStringForXML firstname) $$ inTagsSimple "surname" (text $ escapeStringForXML lastname) @@ -99,9 +99,9 @@ docToJATS opts (Pandoc meta blocks) = do else Nothing let render' :: Doc -> Text render' = render colwidth - let opts' = if (maybe False (("/book>" `isSuffixOf`) . trimr) + let opts' = if maybe False (("/book>" `isSuffixOf`) . trimr) (writerTemplate opts) && - TopLevelDefault == writerTopLevelDivision opts) + TopLevelDefault == writerTopLevelDivision opts then opts{ writerTopLevelDivision = TopLevelChapter } else opts -- The numbering here follows LaTeX's internal numbering @@ -114,20 +114,19 @@ docToJATS opts (Pandoc meta blocks) = do let meta' = B.setMeta "author" auths' meta metadata <- metaToJSON opts (fmap (render' . vcat) . - (mapM (elementToJATS opts' startLvl) . - hierarchicalize)) + mapM (elementToJATS opts' startLvl) . + hierarchicalize) (fmap render' . inlinesToJATS opts') meta' main <- (render' . vcat) <$> - (mapM (elementToJATS opts' startLvl) elements) + mapM (elementToJATS opts' startLvl) elements back <- (render' . vcat) <$> - (mapM (elementToJATS opts' startLvl) backElements) + mapM (elementToJATS opts' startLvl) backElements let context = defField "body" main $ defField "back" back $ defField "mathml" (case writerHTMLMathMethod opts of MathML -> True - _ -> False) - $ metadata + _ -> False) metadata case writerTemplate opts of Nothing -> return main Just tpl -> renderTemplate' tpl context @@ -158,7 +157,7 @@ plainToPara x = x deflistItemsToJATS :: PandocMonad m => WriterOptions -> [([Inline],[[Block]])] -> DB m Doc deflistItemsToJATS opts items = - vcat <$> mapM (\(term, defs) -> deflistItemToJATS opts term defs) items + vcat <$> mapM (uncurry (deflistItemToJATS opts)) items -- | Convert a term and a list of blocks into a JATS varlistentry. deflistItemToJATS :: PandocMonad m @@ -172,7 +171,7 @@ deflistItemToJATS opts term defs = do -- | Convert a list of lists of blocks to a list of JATS list items. listItemsToJATS :: PandocMonad m - => WriterOptions -> (Maybe [String]) -> [[Block]] -> DB m Doc + => WriterOptions -> Maybe [String] -> [[Block]] -> DB m Doc listItemsToJATS opts markers items = case markers of Nothing -> vcat <$> mapM (listItemToJATS opts Nothing) items @@ -180,7 +179,7 @@ listItemsToJATS opts markers items = -- | Convert a list of blocks into a JATS list item. listItemToJATS :: PandocMonad m - => WriterOptions -> (Maybe String) -> [Block] -> DB m Doc + => WriterOptions -> Maybe String -> [Block] -> DB m Doc listItemToJATS opts mbmarker item = do contents <- blocksToJATS opts item return $ inTagsIndented "list-item" $ @@ -203,7 +202,7 @@ blockToJATS opts (Div (ident,_,kvs) bs) = do [(k,v) | (k,v) <- kvs, k `elem` ["specific-use", "content-type", "orientation", "position"]] return $ inTags True "boxed-text" attr contents -blockToJATS _ h@(Header _ _ _) = do +blockToJATS _ h@(Header{}) = do -- should not occur after hierarchicalize, except inside lists/blockquotes report $ BlockNotRendered h return empty @@ -256,9 +255,9 @@ blockToJATS _ (CodeBlock (ident,classes,kvs) str) = return $ else languagesByExtension . map toLower $ s langs = concatMap langsFrom classes blockToJATS _ (BulletList []) = return empty -blockToJATS opts (BulletList lst) = do +blockToJATS opts (BulletList lst) = inTags True "list" [("list-type", "bullet")] <$> - listItemsToJATS opts Nothing lst + listItemsToJATS opts Nothing lst blockToJATS _ (OrderedList _ []) = return empty blockToJATS opts (OrderedList (start, numstyle, delimstyle) items) = do let listType = case numstyle of @@ -277,7 +276,7 @@ blockToJATS opts (OrderedList (start, numstyle, delimstyle) items) = do orderedListMarkers (start, numstyle, delimstyle) inTags True "list" [("list-type", listType)] <$> listItemsToJATS opts markers items -blockToJATS opts (DefinitionList lst) = do +blockToJATS opts (DefinitionList lst) = inTags True "def-list" [] <$> deflistItemsToJATS opts lst blockToJATS _ b@(RawBlock f str) | f == "jats" = return $ text str -- raw XML block @@ -400,7 +399,7 @@ inlineToJATS _ (Math t str) = do case res of Right r -> inTagsSimple "alternatives" $ cr <> rawtex $$ - (text $ Xml.ppcElement conf $ fixNS r) + text (Xml.ppcElement conf $ fixNS r) Left _ -> rawtex inlineToJATS _ (Link _attr [Str t] ('m':'a':'i':'l':'t':'o':':':email, _)) | escapeURI t == email = diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 1a36f987b..e667984ef 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -41,7 +41,7 @@ import Data.Char (isAlphaNum, isAscii, isDigit, isLetter, isPunctuation, ord, toLower) import Data.List (foldl', intercalate, intersperse, isInfixOf, nubBy, stripPrefix, (\\)) -import Data.Maybe (catMaybes, fromMaybe, isJust) +import Data.Maybe (catMaybes, fromMaybe, isJust, mapMaybe) import Data.Text (Text) import qualified Data.Text as T import Network.URI (unEscapeString) @@ -102,10 +102,10 @@ startingState options = WriterState { , stUrl = False , stGraphics = False , stLHS = False - , stBook = (case writerTopLevelDivision options of - TopLevelPart -> True - TopLevelChapter -> True - _ -> False) + , stBook = case writerTopLevelDivision options of + TopLevelPart -> True + TopLevelChapter -> True + _ -> False , stCsquotes = False , stHighlighting = False , stIncremental = writerIncremental options @@ -134,14 +134,14 @@ pandocToLaTeX options (Pandoc meta blocks) = do let method = writerCiteMethod options let blocks' = if method == Biblatex || method == Natbib then case reverse blocks of - (Div (_,["references"],_) _):xs -> reverse xs + Div (_,["references"],_) _:xs -> reverse xs _ -> blocks else blocks -- see if there are internal links let isInternalLink (Link _ _ ('#':xs,_)) = [xs] isInternalLink _ = [] modify $ \s -> s{ stInternalLinks = query isInternalLink blocks' } - let template = maybe "" id $ writerTemplate options + let template = fromMaybe "" $ writerTemplate options -- set stBook depending on documentclass let colwidth = if writerWrapText options == WrapAuto then Just $ writerColumns options @@ -190,8 +190,7 @@ pandocToLaTeX options (Pandoc meta blocks) = do docLangs <- catMaybes <$> mapM (toLang . Just) (ordNub (query (extract "lang") blocks)) let hasStringValue x = isJust (getField x metadata :: Maybe String) - let geometryFromMargins = intercalate [','] $ catMaybes $ - map (\(x,y) -> + let geometryFromMargins = intercalate [','] $ mapMaybe (\(x,y) -> ((x ++ "=") ++) <$> getField y metadata) [("lmargin","margin-left") ,("rmargin","margin-right") @@ -256,7 +255,7 @@ pandocToLaTeX options (Pandoc meta blocks) = do (case getField "papersize" metadata of Just ("A4" :: String) -> resetField "papersize" ("a4" :: String) - _ -> id) $ + _ -> id) metadata let context' = -- note: lang is used in some conditionals in the template, @@ -287,9 +286,9 @@ pandocToLaTeX options (Pandoc meta blocks) = do ) $ maybe id (defField "polyglossia-lang" . toPolyObj) mblang $ defField "polyglossia-otherlangs" (map toPolyObj docLangs) - $ defField "latex-dir-rtl" - (getField "dir" context == Just ("rtl" :: String)) - $ context + $ + defField "latex-dir-rtl" + (getField "dir" context == Just ("rtl" :: String)) context case writerTemplate options of Nothing -> return main Just tpl -> renderTemplate' tpl context' @@ -357,7 +356,7 @@ toLabel z = go `fmap` stringToLaTeX URLString z where go [] = "" go (x:xs) | (isLetter x || isDigit x) && isAscii x = x:go xs - | elem x ("_-+=:;." :: String) = x:go xs + | x `elem` ("_-+=:;." :: String) = x:go xs | otherwise = "ux" ++ printf "%x" (ord x) ++ go xs -- | Puts contents into LaTeX command. @@ -369,7 +368,7 @@ toSlides bs = do opts <- gets stOptions let slideLevel = fromMaybe (getSlideLevel bs) $ writerSlideLevel opts let bs' = prepSlides slideLevel bs - concat `fmap` (mapM (elementToBeamer slideLevel) $ hierarchicalize bs') + concat `fmap` mapM (elementToBeamer slideLevel) (hierarchicalize bs') elementToBeamer :: PandocMonad m => Int -> Element -> LW m [Block] elementToBeamer _slideLevel (Blk b) = return [b] @@ -381,7 +380,7 @@ elementToBeamer slideLevel (Sec lvl _num (ident,classes,kvs) tit elts) : bs ++ [RawBlock "latex" "\\end{block}"] | lvl < slideLevel = do bs <- concat `fmap` mapM (elementToBeamer slideLevel) elts - return $ (Header lvl (ident,classes,kvs) tit) : bs + return $ Header lvl (ident,classes,kvs) tit : bs | otherwise = do -- lvl == slideLevel -- note: [fragile] is required or verbatim breaks let hasCodeBlock (CodeBlock _ _) = [True] @@ -480,8 +479,8 @@ blockToLaTeX (Div (identifier,classes,kvs) bs) = do wrapNotes txt = if beamer && "notes" `elem` classes then "\\note" <> braces txt -- speaker notes else linkAnchor $$ txt - fmap (wrapColumns . wrapColumn . wrapDir . wrapLang . wrapNotes) - $ blockListToLaTeX bs + (wrapColumns . wrapColumn . wrapDir . wrapLang . wrapNotes) + <$> blockListToLaTeX bs blockToLaTeX (Plain lst) = inlineListToLaTeX $ dropWhile isLineBreakOrSpace lst -- title beginning with fig: indicates that the image is a figure @@ -517,7 +516,7 @@ blockToLaTeX (Para [Str ".",Space,Str ".",Space,Str "."]) = do else inlineListToLaTeX [Str ".",Space,Str ".",Space,Str "."] blockToLaTeX (Para lst) = inlineListToLaTeX $ dropWhile isLineBreakOrSpace lst -blockToLaTeX (LineBlock lns) = do +blockToLaTeX (LineBlock lns) = blockToLaTeX $ linesToPara lns blockToLaTeX (BlockQuote lst) = do beamer <- gets stBeamer @@ -645,12 +644,11 @@ blockToLaTeX (OrderedList (start, numstyle, numdelim) lst) = do Example -> "1" DefaultStyle -> "1" let enum = text $ "enum" ++ map toLower (toRomanNumeral oldlevel) - let stylecommand = if numstyle == DefaultStyle && numdelim == DefaultDelim - then empty - else if beamer - then brackets (todelim exemplar) - else "\\def" <> "\\label" <> enum <> - braces (todelim $ tostyle enum) + let stylecommand + | numstyle == DefaultStyle && numdelim == DefaultDelim = empty + | beamer = brackets (todelim exemplar) + | otherwise = "\\def" <> "\\label" <> enum <> + braces (todelim $ tostyle enum) let resetcounter = if start == 1 || oldlevel > 4 then empty else "\\setcounter" <> braces enum <> @@ -674,7 +672,8 @@ blockToLaTeX (DefinitionList lst) = do else empty return $ text ("\\begin{description}" ++ inc) $$ spacing $$ vcat items $$ "\\end{description}" -blockToLaTeX HorizontalRule = return $ +blockToLaTeX HorizontalRule = + return "\\begin{center}\\rule{0.5\\linewidth}{\\linethickness}\\end{center}" blockToLaTeX (Header level (id',classes,_) lst) = do modify $ \s -> s{stInHeading = True} @@ -682,7 +681,7 @@ blockToLaTeX (Header level (id',classes,_) lst) = do modify $ \s -> s{stInHeading = False} return hdr blockToLaTeX (Table caption aligns widths heads rows) = do - let toHeaders hs = do contents <- (tableRowToLaTeX True aligns widths) hs + let toHeaders hs = do contents <- tableRowToLaTeX True aligns widths hs return ("\\toprule" $$ contents $$ "\\midrule") let removeNote (Note _) = Span ("", [], []) [] removeNote x = x @@ -702,7 +701,7 @@ blockToLaTeX (Table caption aligns widths heads rows) = do else text "\\caption" <> braces captionText <> "\\tabularnewline" rows' <- mapM (tableRowToLaTeX False aligns widths) rows - let colDescriptors = text $ concat $ map toColDescriptor aligns + let colDescriptors = text $ concatMap toColDescriptor aligns modify $ \s -> s{ stTable = True } return $ "\\begin{longtable}[]" <> braces ("@{}" <> colDescriptors <> "@{}") @@ -812,10 +811,10 @@ listItemToLaTeX lst -- we need to put some text before a header if it's the first -- element in an item. This will look ugly in LaTeX regardless, but -- this will keep the typesetter from throwing an error. - | ((Header _ _ _) :_) <- lst = - blockListToLaTeX lst >>= return . (text "\\item ~" $$) . (nest 2) + | (Header _ _ _ :_) <- lst = + blockListToLaTeX lst >>= return . (text "\\item ~" $$) . nest 2 | otherwise = blockListToLaTeX lst >>= return . (text "\\item" $$) . - (nest 2) + nest 2 defListItemToLaTeX :: PandocMonad m => ([Inline], [[Block]]) -> LW m Doc defListItemToLaTeX (term, defs) = do @@ -832,7 +831,7 @@ defListItemToLaTeX (term, defs) = do else term' def' <- liftM vsep $ mapM blockListToLaTeX defs return $ case defs of - (((Header _ _ _) : _) : _) -> + ((Header _ _ _ : _) : _) -> "\\item" <> brackets term'' <> " ~ " $$ def' _ -> "\\item" <> brackets term'' $$ def' @@ -849,16 +848,16 @@ sectionHeader unnumbered ident level lst = do plain <- stringToLaTeX TextString $ concatMap stringify lst let removeInvalidInline (Note _) = [] removeInvalidInline (Span (id', _, _) _) | not (null id') = [] - removeInvalidInline (Image _ _ _) = [] + removeInvalidInline (Image{}) = [] removeInvalidInline x = [x] let lstNoNotes = foldr (mappend . (\x -> walkM removeInvalidInline x)) mempty lst txtNoNotes <- inlineListToLaTeX lstNoNotes -- footnotes in sections don't work (except for starred variants) -- unless you specify an optional argument: -- \section[mysec]{mysec\footnote{blah}} - optional <- if unnumbered || lstNoNotes == lst || lstNoNotes == [] + optional <- if unnumbered || lstNoNotes == lst || null lstNoNotes then return empty - else do + else return $ brackets txtNoNotes let contents = if render Nothing txt == plain then braces txt @@ -983,7 +982,7 @@ inlineToLaTeX (Strikeout lst) = do return $ inCmd "sout" contents inlineToLaTeX (Superscript lst) = inlineListToLaTeX lst >>= return . inCmd "textsuperscript" -inlineToLaTeX (Subscript lst) = do +inlineToLaTeX (Subscript lst) = inlineListToLaTeX lst >>= return . inCmd "textsubscript" inlineToLaTeX (SmallCaps lst) = inlineListToLaTeX lst >>= return . inCmd "textsc" @@ -1018,7 +1017,7 @@ inlineToLaTeX (Code (_,classes,_) str) = do $ stringToLaTeX CodeString str where escapeSpaces = concatMap (\c -> if c == ' ' then "\\ " else [c]) - let highlightCode = do + let highlightCode = case highlight (writerSyntaxMap opts) formatLaTeXInline ("",classes,[]) str of Left msg -> do @@ -1038,10 +1037,10 @@ inlineToLaTeX (Quoted qt lst) = do if csquotes then return $ "\\enquote" <> braces contents else do - let s1 = if (not (null lst)) && (isQuoted (head lst)) + let s1 = if not (null lst) && isQuoted (head lst) then "\\," else empty - let s2 = if (not (null lst)) && (isQuoted (last lst)) + let s2 = if not (null lst) && isQuoted (last lst) then "\\," else empty let inner = s1 <> contents <> s2 @@ -1071,7 +1070,7 @@ inlineToLaTeX il@(RawInline f str) | otherwise = do report $ InlineNotRendered il return empty -inlineToLaTeX (LineBreak) = do +inlineToLaTeX LineBreak = do emptyLine <- gets stEmptyLine setEmptyLine True return $ (if emptyLine then "~" else "") <> "\\\\" <> cr @@ -1111,7 +1110,7 @@ inlineToLaTeX (Image attr _ (source, _)) = do modify $ \s -> s{ stGraphics = True } opts <- gets stOptions let showDim dir = let d = text (show dir) <> "=" - in case (dimension dir attr) of + in case dimension dir attr of Just (Pixel a) -> [d <> text (showInInch opts (Pixel a)) <> "in"] Just (Percent a) -> @@ -1165,7 +1164,8 @@ setEmptyLine :: PandocMonad m => Bool -> LW m () setEmptyLine b = modify $ \st -> st{ stEmptyLine = b } citationsToNatbib :: PandocMonad m => [Citation] -> LW m Doc -citationsToNatbib (one:[]) +citationsToNatbib + [one] = citeCommand c p s k where Citation { citationId = k @@ -1185,9 +1185,11 @@ citationsToNatbib cits where noPrefix = all (null . citationPrefix) noSuffix = all (null . citationSuffix) - ismode m = all (((==) m) . citationMode) - p = citationPrefix $ head $ cits - s = citationSuffix $ last $ cits + ismode m = all ((==) m . citationMode) + p = citationPrefix $ + head cits + s = citationSuffix $ + last cits ks = intercalate ", " $ map citationId cits citationsToNatbib (c:cs) | citationMode c == AuthorInText = do @@ -1221,7 +1223,8 @@ citeArguments :: PandocMonad m => [Inline] -> [Inline] -> String -> LW m Doc citeArguments p s k = do let s' = case s of - (Str (x:[]) : r) | isPunctuation x -> dropWhile (== Space) r + (Str + [x] : r) | isPunctuation x -> dropWhile (== Space) r (Str (x:xs) : r) | isPunctuation x -> Str xs : r _ -> s pdoc <- inlineListToLaTeX p @@ -1233,7 +1236,8 @@ citeArguments p s k = do return $ optargs <> braces (text k) citationsToBiblatex :: PandocMonad m => [Citation] -> LW m Doc -citationsToBiblatex (one:[]) +citationsToBiblatex + [one] = citeCommand cmd p s k where Citation { citationId = k @@ -1264,8 +1268,8 @@ citationsToBiblatex _ = return empty -- Determine listings language from list of class attributes. getListingsLanguage :: [String] -> Maybe String -getListingsLanguage [] = Nothing -getListingsLanguage (x:xs) = toListingsLanguage x <|> getListingsLanguage xs +getListingsLanguage xs + = foldr ((<|>) . toListingsLanguage) Nothing xs mbBraced :: String -> String mbBraced x = if not (all isAlphaNum x) diff --git a/src/Text/Pandoc/Writers/Man.hs b/src/Text/Pandoc/Writers/Man.hs index 40c0dd815..cd7a98d43 100644 --- a/src/Text/Pandoc/Writers/Man.hs +++ b/src/Text/Pandoc/Writers/Man.hs @@ -104,8 +104,7 @@ pandocToMan opts (Pandoc meta blocks) = do $ setFieldsFromTitle $ defField "has-tables" hasTables $ defField "hyphenate" True - $ defField "pandoc-version" pandocVersion - $ metadata + $ defField "pandoc-version" pandocVersion metadata case writerTemplate opts of Nothing -> return main Just tpl -> renderTemplate' tpl context @@ -115,7 +114,7 @@ notesToMan :: PandocMonad m => WriterOptions -> [[Block]] -> StateT WriterState notesToMan opts notes = if null notes then return empty - else mapM (\(num, note) -> noteToMan opts num note) (zip [1..] notes) >>= + else mapM (uncurry (noteToMan opts)) (zip [1..] notes) >>= return . (text ".SH NOTES" $$) . vcat -- | Return man representation of a note. @@ -141,7 +140,7 @@ escapeString = escapeStringUsing manEscapes -- | Escape a literal (code) section for Man. escapeCode :: String -> String -escapeCode = concat . intersperse "\n" . map escapeLine . lines where +escapeCode = intercalate "\n" . map escapeLine . lines where escapeLine codeline = case escapeStringUsing (manEscapes ++ backslashEscapes "\t ") codeline of a@('.':_) -> "\\&" ++ a @@ -157,7 +156,7 @@ breakSentence [] = ([],[]) breakSentence xs = let isSentenceEndInline (Str ys@(_:_)) | last ys == '.' = True isSentenceEndInline (Str ys@(_:_)) | last ys == '?' = True - isSentenceEndInline (LineBreak) = True + isSentenceEndInline LineBreak = True isSentenceEndInline _ = False (as, bs) = break isSentenceEndInline xs in case bs of @@ -226,12 +225,12 @@ blockToMan opts (Table caption alignments widths headers rows) = then repeat "" else map (printf "w(%0.1fn)" . (70 *)) widths -- 78n default width - 8n indent = 70n - let coldescriptions = text $ intercalate " " + let coldescriptions = text $ unwords (zipWith (\align width -> aligncode align ++ width) alignments iwidths) ++ "." colheadings <- mapM (blockListToMan opts) headers let makeRow cols = text "T{" $$ - (vcat $ intersperse (text "T}@T{") cols) $$ + vcat (intersperse (text "T}@T{") cols) $$ text "T}" let colheadings' = if all null headers then empty @@ -248,7 +247,8 @@ blockToMan opts (BulletList items) = do return (vcat contents) blockToMan opts (OrderedList attribs items) = do let markers = take (length items) $ orderedListMarkers attribs - let indent = 1 + (maximum $ map length markers) + let indent = 1 + + maximum (map length markers) contents <- mapM (\(num, item) -> orderedListItemToMan opts num indent item) $ zip markers items return (vcat contents) @@ -259,9 +259,9 @@ blockToMan opts (DefinitionList items) = do -- | Convert bullet list item (list of blocks) to man. bulletListItemToMan :: PandocMonad m => WriterOptions -> [Block] -> StateT WriterState m Doc bulletListItemToMan _ [] = return empty -bulletListItemToMan opts ((Para first):rest) = - bulletListItemToMan opts ((Plain first):rest) -bulletListItemToMan opts ((Plain first):rest) = do +bulletListItemToMan opts (Para first:rest) = + bulletListItemToMan opts (Plain first:rest) +bulletListItemToMan opts (Plain first:rest) = do first' <- blockToMan opts (Plain first) rest' <- blockListToMan opts rest let first'' = text ".IP \\[bu] 2" $$ first' @@ -282,8 +282,8 @@ orderedListItemToMan :: PandocMonad m -> [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) +orderedListItemToMan opts num indent (Para first:rest) = + orderedListItemToMan opts num indent (Plain first:rest) orderedListItemToMan opts num indent (first:rest) = do first' <- blockToMan opts first rest' <- blockListToMan opts rest @@ -332,9 +332,9 @@ inlineListToMan opts lst = mapM (inlineToMan opts) lst >>= (return . hcat) -- | Convert Pandoc inline element to man. inlineToMan :: PandocMonad m => WriterOptions -> Inline -> StateT WriterState m Doc inlineToMan opts (Span _ ils) = inlineListToMan opts ils -inlineToMan opts (Emph lst) = do +inlineToMan opts (Emph lst) = withFontFeature 'I' (inlineListToMan opts lst) -inlineToMan opts (Strong lst) = do +inlineToMan opts (Strong lst) = withFontFeature 'B' (inlineListToMan opts lst) inlineToMan opts (Strikeout lst) = do contents <- inlineListToMan opts lst @@ -382,7 +382,7 @@ inlineToMan opts (Link _ txt (src, _)) = do char '<' <> text srcSuffix <> char '>' _ -> linktext <> text " (" <> text src <> char ')' inlineToMan opts (Image attr alternate (source, tit)) = do - let txt = if (null alternate) || (alternate == [Str ""]) || + let txt = if null alternate || (alternate == [Str ""]) || (alternate == [Str source]) -- to prevent autolinks then [Str "image"] else alternate @@ -392,7 +392,7 @@ inlineToMan _ (Note contents) = do -- add to notes in state modify $ \st -> st{ stNotes = contents : stNotes st } notes <- gets stNotes - let ref = show $ (length notes) + let ref = show (length notes) return $ char '[' <> text ref <> char ']' fontChange :: PandocMonad m => StateT WriterState m Doc diff --git a/src/Text/Pandoc/Writers/Math.hs b/src/Text/Pandoc/Writers/Math.hs index 61358378b..477f5a0b1 100644 --- a/src/Text/Pandoc/Writers/Math.hs +++ b/src/Text/Pandoc/Writers/Math.hs @@ -22,7 +22,7 @@ texMathToInlines mt inp = do res <- convertMath writePandoc mt inp case res of Right (Just ils) -> return ils - Right (Nothing) -> do + Right Nothing -> do report $ CouldNotConvertTeXMath inp "" return [mkFallback mt inp] Left il -> return [il] @@ -39,7 +39,7 @@ mkFallback mt str = Str (delim ++ str ++ delim) convertMath :: PandocMonad m => (DisplayType -> [Exp] -> a) -> MathType -> String -> m (Either Inline a) -convertMath writer mt str = do +convertMath writer mt str = case writer dt <$> readTeX str of Right r -> return (Right r) Left e -> do diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 68c0d6096..9e3036753 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -121,15 +121,14 @@ pandocToMs opts (Pandoc meta blocks) = do $ defField "toc" (writerTableOfContents opts) $ defField "title-meta" titleMeta $ defField "author-meta" (intercalate "; " authorsMeta) - $ defField "highlighting-macros" highlightingMacros - $ metadata + $ defField "highlighting-macros" highlightingMacros metadata case writerTemplate opts of Nothing -> return main Just tpl -> renderTemplate' tpl context -- | Association list of characters to escape. msEscapes :: Map.Map Char String -msEscapes = Map.fromList $ +msEscapes = Map.fromList [ ('\160', "\\~") , ('\'', "\\[aq]") , ('`', "\\`") @@ -146,9 +145,7 @@ msEscapes = Map.fromList $ ] escapeChar :: Char -> String -escapeChar c = case Map.lookup c msEscapes of - Just s -> s - Nothing -> [c] +escapeChar c = fromMaybe [c] (Map.lookup c msEscapes) -- | Escape | character, used to mark inline math, inside math. escapeBar :: String -> String @@ -175,7 +172,7 @@ toSmallCaps (c:cs) -- | Escape a literal (code) section for Ms. escapeCode :: String -> String -escapeCode = concat . intersperse "\n" . map escapeLine . lines +escapeCode = intercalate "\n" . map escapeLine . lines where escapeCodeChar ' ' = "\\ " escapeCodeChar '\t' = "\\\t" escapeCodeChar c = escapeChar c @@ -194,7 +191,7 @@ breakSentence [] = ([],[]) breakSentence xs = let isSentenceEndInline (Str ys@(_:_)) | last ys == '.' = True isSentenceEndInline (Str ys@(_:_)) | last ys == '?' = True - isSentenceEndInline (LineBreak) = True + isSentenceEndInline LineBreak = True isSentenceEndInline _ = False (as, bs) = break isSentenceEndInline xs in case bs of @@ -283,11 +280,11 @@ blockToMs opts (Header level (ident,classes,_) inlines) = do level <= writerTOCDepth opts then text ".XS" $$ backlink <> doubleQuotes ( - nowrap ((text (replicate level '\t') <> + nowrap (text (replicate level '\t') <> (if null secnum then empty else text secnum <> text "\\~\\~") - <> contents))) + <> contents)) $$ text ".XE" else empty modify $ \st -> st{ stFirstPara = True } @@ -325,12 +322,12 @@ blockToMs opts (Table caption alignments widths headers rows) = then repeat "" else map (printf "w(%0.1fn)" . (70 *)) widths -- 78n default width - 8n indent = 70n - let coldescriptions = text $ intercalate " " + let coldescriptions = text $ unwords (zipWith (\align width -> aligncode align ++ width) alignments iwidths) ++ "." colheadings <- mapM (blockListToMs opts) headers let makeRow cols = text "T{" $$ - (vcat $ intersperse (text "T}\tT{") cols) $$ + vcat (intersperse (text "T}\tT{") cols) $$ text "T}" let colheadings' = if all null headers then empty @@ -349,7 +346,8 @@ blockToMs opts (BulletList items) = do return (vcat contents) blockToMs opts (OrderedList attribs items) = do let markers = take (length items) $ orderedListMarkers attribs - let indent = 2 + (maximum $ map length markers) + let indent = 2 + + maximum (map length markers) contents <- mapM (\(num, item) -> orderedListItemToMs opts num indent item) $ zip markers items setFirstPara @@ -362,9 +360,9 @@ blockToMs opts (DefinitionList items) = do -- | Convert bullet list item (list of blocks) to ms. bulletListItemToMs :: PandocMonad m => WriterOptions -> [Block] -> MS m Doc bulletListItemToMs _ [] = return empty -bulletListItemToMs opts ((Para first):rest) = - bulletListItemToMs opts ((Plain first):rest) -bulletListItemToMs opts ((Plain first):rest) = do +bulletListItemToMs opts (Para first:rest) = + bulletListItemToMs opts (Plain first:rest) +bulletListItemToMs opts (Plain first:rest) = do first' <- blockToMs opts (Plain first) rest' <- blockListToMs opts rest let first'' = text ".IP \\[bu] 3" $$ first' @@ -385,8 +383,8 @@ orderedListItemToMs :: PandocMonad m -> [Block] -- ^ list item (list of blocks) -> MS m Doc orderedListItemToMs _ _ _ [] = return empty -orderedListItemToMs opts num indent ((Para first):rest) = - orderedListItemToMs opts num indent ((Plain first):rest) +orderedListItemToMs opts num indent (Para first:rest) = + orderedListItemToMs opts num indent (Plain first:rest) orderedListItemToMs opts num indent (first:rest) = do first' <- blockToMs opts first rest' <- blockListToMs opts rest @@ -409,7 +407,7 @@ definitionListItemToMs opts (label, defs) = do then return empty else liftM vcat $ forM defs $ \blocks -> do let (first, rest) = case blocks of - ((Para x):y) -> (Plain x,y) + (Para x:y) -> (Plain x,y) (x:y) -> (x,y) [] -> (Plain [], []) -- should not happen @@ -503,7 +501,7 @@ inlineToMs _ il@(RawInline f str) | otherwise = do report $ InlineNotRendered il return empty -inlineToMs _ (LineBreak) = return $ cr <> text ".br" <> cr +inlineToMs _ LineBreak = return $ cr <> text ".br" <> cr inlineToMs opts SoftBreak = handleNotes opts $ case writerWrapText opts of @@ -539,8 +537,7 @@ handleNotes opts fallback = do then return fallback else do modify $ \st -> st{ stNotes = [] } - res <- vcat <$> mapM (handleNote opts) notes - return res + vcat <$> mapM (handleNote opts) notes handleNote :: PandocMonad m => WriterOptions -> Note -> MS m Doc handleNote opts bs = do @@ -589,7 +586,7 @@ styleToMs sty = vcat $ colordefs ++ map (toMacro sty) alltoktypes allcolors = catMaybes $ ordNub $ [defaultColor sty, backgroundColor sty, lineNumberColor sty, lineNumberBackgroundColor sty] ++ - concatMap colorsForToken (map snd (tokenStyles sty)) + concatMap (colorsForToken. snd) (tokenStyles sty) colorsForToken ts = [tokenColor ts, tokenBackground ts] hexColor :: Color -> String diff --git a/src/Text/Pandoc/Writers/Native.hs b/src/Text/Pandoc/Writers/Native.hs index c934fe4d9..1fb685985 100644 --- a/src/Text/Pandoc/Writers/Native.hs +++ b/src/Text/Pandoc/Writers/Native.hs @@ -39,7 +39,8 @@ import Text.Pandoc.Pretty prettyList :: [Doc] -> Doc prettyList ds = - "[" <> (cat $ intersperse (cr <> ",") $ map (nest 1) ds) <> "]" + "[" <> + cat (intersperse (cr <> ",") $ map (nest 1) ds) <> "]" -- | Prettyprint Pandoc block element. prettyBlock :: Block -> Doc @@ -49,12 +50,12 @@ prettyBlock (BlockQuote blocks) = "BlockQuote" $$ prettyList (map prettyBlock blocks) prettyBlock (OrderedList attribs blockLists) = "OrderedList" <> space <> text (show attribs) $$ - (prettyList $ map (prettyList . map prettyBlock) blockLists) + prettyList (map (prettyList . map prettyBlock) blockLists) prettyBlock (BulletList blockLists) = "BulletList" $$ - (prettyList $ map (prettyList . map prettyBlock) blockLists) + prettyList (map (prettyList . map prettyBlock) blockLists) prettyBlock (DefinitionList items) = "DefinitionList" $$ - (prettyList $ map deflistitem items) + prettyList (map deflistitem items) where deflistitem (term, defs) = "(" <> text (show term) <> "," <> cr <> nest 1 (prettyList $ map (prettyList . map prettyBlock) defs) <> ")" prettyBlock (Table caption aligns widths header rows) = diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index 32fcb0292..fcd551227 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -89,7 +89,7 @@ pandocToODT opts doc@(Pandoc meta _) = do -- picEntriesRef <- P.newIORef ([] :: [Entry]) doc' <- walkM (transformPicMath opts) $ walk fixDisplayMath doc newContents <- lift $ writeOpenDocument opts{writerWrapText = WrapNone} doc' - epochtime <- floor `fmap` (lift P.getPOSIXTime) + epochtime <- floor `fmap` lift P.getPOSIXTime let contentEntry = toEntry "content.xml" epochtime $ fromTextLazy $ TL.fromStrict newContents picEntries <- gets stEntries @@ -111,10 +111,9 @@ pandocToODT opts doc@(Pandoc meta _) = do $ fromStringLazy $ render Nothing $ text "<?xml version=\"1.0\" encoding=\"utf-8\"?>" $$ - ( inTags True "manifest:manifest" + (inTags True "manifest:manifest" [("xmlns:manifest","urn:oasis:names:tc:opendocument:xmlns:manifest:1.0") - ,("manifest:version","1.2")] - $ ( selfClosingTag "manifest:file-entry" + ,("manifest:version","1.2")] ( selfClosingTag "manifest:file-entry" [("manifest:media-type","application/vnd.oasis.opendocument.text") ,("manifest:full-path","/")] $$ vcat ( map toFileEntry $ files ) @@ -126,15 +125,14 @@ pandocToODT opts doc@(Pandoc meta _) = do $ fromStringLazy $ render Nothing $ text "<?xml version=\"1.0\" encoding=\"utf-8\"?>" $$ - ( inTags True "office:document-meta" + (inTags True "office:document-meta" [("xmlns:office","urn:oasis:names:tc:opendocument:xmlns:office:1.0") ,("xmlns:xlink","http://www.w3.org/1999/xlink") ,("xmlns:dc","http://purl.org/dc/elements/1.1/") ,("xmlns:meta","urn:oasis:names:tc:opendocument:xmlns:meta:1.0") ,("xmlns:ooo","http://openoffice.org/2004/office") ,("xmlns:grddl","http://www.w3.org/2003/g/data-view#") - ,("office:version","1.2")] - $ ( inTagsSimple "office:meta" $ + ,("office:version","1.2")] ( inTagsSimple "office:meta" $ ( inTagsSimple "dc:title" (text $ escapeStringForXML (stringify title)) $$ @@ -156,7 +154,7 @@ pandocToODT opts doc@(Pandoc meta _) = do updateStyleWithLang :: PandocMonad m => Maybe Lang -> Archive -> O m Archive updateStyleWithLang Nothing arch = return arch updateStyleWithLang (Just lang) arch = do - epochtime <- floor `fmap` (lift P.getPOSIXTime) + epochtime <- floor `fmap` lift P.getPOSIXTime return arch{ zEntries = [if eRelativePath e == "styles.xml" then case parseXMLDoc (toStringLazy (fromEntry e)) of @@ -196,7 +194,7 @@ transformPicMath opts (Image attr@(id', cls, _) lab (src,t)) = catchError _ -> [("width", show ptX ++ "pt"), ("height", show ptY ++ "pt")] where ratio = ptX / ptY - getDim dir = case (dimension dir attr) of + getDim dir = case dimension dir attr of Just (Percent i) -> Just $ Percent i Just dim -> Just $ Inch $ inInch opts dim Nothing -> Nothing @@ -206,7 +204,7 @@ transformPicMath opts (Image attr@(id', cls, _) lab (src,t)) = catchError (mbMimeType >>= extensionFromMimeType) let newsrc = "Pictures/" ++ show (length entries) <.> extension let toLazy = B.fromChunks . (:[]) - epochtime <- floor `fmap` (lift P.getPOSIXTime) + epochtime <- floor `fmap` lift P.getPOSIXTime let entry = toEntry newsrc epochtime $ toLazy img modify $ \st -> st{ stEntries = entry : entries } return $ Image newattr lab (newsrc, t)) @@ -222,7 +220,7 @@ transformPicMath _ (Math t math) = do Right r -> do let conf = useShortEmptyTags (const False) defaultConfigPP let mathml = ppcTopElement conf r - epochtime <- floor `fmap` (lift $ 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) diff --git a/src/Text/Pandoc/Writers/OPML.hs b/src/Text/Pandoc/Writers/OPML.hs index 52577ac17..3a2467c65 100644 --- a/src/Text/Pandoc/Writers/OPML.hs +++ b/src/Text/Pandoc/Writers/OPML.hs @@ -56,9 +56,9 @@ writeOPML opts (Pandoc meta blocks) = do meta' = B.setMeta "date" (B.str $ convertDate $ docDate meta) meta metadata <- metaToJSON opts (writeMarkdown def . Pandoc nullMeta) - (\ils -> T.stripEnd <$> (writeMarkdown def $ Pandoc nullMeta [Plain ils])) + (\ils -> T.stripEnd <$> writeMarkdown def (Pandoc nullMeta [Plain ils])) meta' - main <- (render colwidth . vcat) <$> (mapM (elementToOPML opts) elements) + main <- (render colwidth . vcat) <$> mapM (elementToOPML opts) elements let context = defField "body" main metadata case writerTemplate opts of Nothing -> return main @@ -67,7 +67,7 @@ writeOPML opts (Pandoc meta blocks) = do writeHtmlInlines :: PandocMonad m => [Inline] -> m Text writeHtmlInlines ils = - T.strip <$> (writeHtml5String def $ Pandoc nullMeta [Plain ils]) + T.strip <$> writeHtml5String def (Pandoc nullMeta [Plain ils]) -- date format: RFC 822: Thu, 14 Jul 2005 23:41:05 GMT showDateTimeRFC822 :: UTCTime -> String @@ -80,7 +80,7 @@ convertDate ils = maybe "" showDateTimeRFC822 $ #else parseTime #endif - defaultTimeLocale "%F" =<< (normalizeDate $ stringify ils) + defaultTimeLocale "%F" =<< normalizeDate (stringify ils) -- | Convert an Element to OPML. elementToOPML :: PandocMonad m => WriterOptions -> Element -> m Doc @@ -100,7 +100,7 @@ elementToOPML opts (Sec _ _num _ title elements) = do then return mempty else do blks <- mapM fromBlk blocks writeMarkdown def $ Pandoc nullMeta blks - let attrs = [("text", unpack htmlIls)] ++ + let attrs = ("text", unpack htmlIls) : [("_note", unpack md) | not (null blocks)] 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 04cae0b4b..ac4a85670 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -117,7 +117,7 @@ increaseIndent :: PandocMonad m => OD m () increaseIndent = modify $ \s -> s { stIndentPara = 1 + stIndentPara s } resetIndent :: PandocMonad m => OD m () -resetIndent = modify $ \s -> s { stIndentPara = (stIndentPara s) - 1 } +resetIndent = modify $ \s -> s { stIndentPara = stIndentPara s - 1 } inTightList :: PandocMonad m => OD m a -> OD m a inTightList f = modify (\s -> s { stTight = True }) >> f >>= \r -> @@ -135,7 +135,7 @@ inParagraphTags d = do b <- gets stFirstPara a <- if b then do modify $ \st -> st { stFirstPara = False } - return $ [("text:style-name", "First_20_paragraph")] + return [("text:style-name", "First_20_paragraph")] else return [("text:style-name", "Text_20_body")] return $ inTags False "text:p" a d @@ -213,16 +213,15 @@ writeOpenDocument opts (Pandoc meta blocks) = do b <- render' `fmap` blocksToOpenDocument opts blocks return (b, m) let styles = stTableStyles s ++ stParaStyles s ++ - map snd (reverse $ sortBy (comparing fst) $ - Map.elems (stTextStyles s)) + map snd (sortBy (flip (comparing fst)) ( + Map.elems (stTextStyles s))) listStyle (n,l) = inTags True "text:list-style" [("style:name", "L" ++ show n)] (vcat l) let listStyles = map listStyle (stListStyles s) let automaticStyles = vcat $ reverse $ styles ++ listStyles let context = defField "body" body $ defField "toc" (writerTableOfContents opts) - $ defField "automatic-styles" (render' automaticStyles) - $ metadata + $defField "automatic-styles" (render' automaticStyles) metadata case writerTemplate opts of Nothing -> return body Just tpl -> renderTemplate' tpl context @@ -297,7 +296,7 @@ deflistItemToOpenDocument o (t,d) = do ds = if isTightList d then "Definition_20_Definition_20_Tight" else "Definition_20_Definition" t' <- withParagraphStyle o ts [Para t] - d' <- liftM vcat $ mapM (withParagraphStyle o ds . (map plainToPara)) d + d' <- liftM vcat $ mapM (withParagraphStyle o ds . map plainToPara) d return $ t' $$ d' inBlockQuote :: PandocMonad m @@ -307,8 +306,8 @@ inBlockQuote o i (b:bs) ni <- paraStyle [("style:parent-style-name","Quotations")] go =<< inBlockQuote o ni (map plainToPara l) - | Para l <- b = do go =<< inParagraphTagsWithStyle ("P" ++ show i) <$> inlinesToOpenDocument o l - | otherwise = do go =<< blockToOpenDocument o b + | Para l <- b = go =<< inParagraphTagsWithStyle ("P" ++ show i) <$> inlinesToOpenDocument o l + | otherwise = go =<< blockToOpenDocument o b where go block = ($$) block <$> inBlockQuote o i bs inBlockQuote _ _ [] = resetIndent >> return empty @@ -446,7 +445,7 @@ inlineToOpenDocument o ils SoftBreak | writerWrapText o == WrapPreserve -> return $ preformatted "\n" - | otherwise -> return $ space + | otherwise ->return space Span attr xs -> withLangFromAttr attr (inlinesToOpenDocument o xs) LineBreak -> return $ selfClosingTag "text:line-break" [] Str s -> return $ handleSpaces $ escapeStringForXML s @@ -556,7 +555,7 @@ tableStyle num wcs = [ ("style:name" , tableId ++ "." ++ [c]) , ("style:family", "table-column" )] $ selfClosingTag "style:table-column-properties" - [("style:rel-column-width", printf "%d*" $ (floor $ w * 65535 :: Integer))] + [("style:rel-column-width", printf "%d*" (floor $ w * 65535 :: Integer))] cellStyle = inTags True "style:style" [ ("style:name" , tableId ++ ".A1") , ("style:family", "table-cell" )] $ @@ -584,8 +583,10 @@ paraStyle attrs = do , ("style:auto-text-indent" , "false" )] else [] attributes = indent ++ tight - paraProps = when (not $ null attributes) $ - selfClosingTag "style:paragraph-properties" attributes + paraProps = if null attributes + then mempty + else selfClosingTag + "style:paragraph-properties" attributes addParaStyle $ inTags True "style:style" (styleAttr ++ attrs) paraProps return pn @@ -643,7 +644,7 @@ withLangFromAttr :: PandocMonad m => Attr -> OD m a -> OD m a withLangFromAttr (_,_,kvs) action = case lookup "lang" kvs of Nothing -> action - Just l -> do + Just l -> case parseBCP47 l of Right lang -> withTextStyle (Language lang) action Left _ -> do diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index cfbacdaed..6c6010880 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -97,15 +97,14 @@ pandocToRST (Pandoc meta blocks) = do pics <- gets (reverse . stImages) >>= pictRefsToRST hasMath <- gets stHasMath rawTeX <- gets stHasRawTeX - let main = render' $ foldl ($+$) empty $ [body, notes, refs, pics] + let main = render' $ foldl ($+$) empty [body, notes, refs, pics] let context = defField "body" main $ defField "toc" (writerTableOfContents opts) $ defField "toc-depth" (show $ writerTOCDepth opts) $ defField "math" hasMath $ defField "title" (render Nothing title :: String) $ defField "math" hasMath - $ defField "rawtex" rawTeX - $ metadata + $ defField "rawtex" rawTeX metadata case writerTemplate opts of Nothing -> return main Just tpl -> renderTemplate' tpl context @@ -126,7 +125,7 @@ refsToRST refs = mapM keyToRST refs >>= return . vcat keyToRST :: PandocMonad m => ([Inline], (String, String)) -> RST m Doc keyToRST (label, (src, _)) = do label' <- inlineListToRST label - let label'' = if ':' `elem` ((render Nothing label') :: String) + let label'' = if ':' `elem` (render Nothing label' :: String) then char '`' <> label' <> char '`' else label' return $ nowrap $ ".. _" <> label'' <> ": " <> text src @@ -134,7 +133,7 @@ keyToRST (label, (src, _)) = do -- | Return RST representation of notes. notesToRST :: PandocMonad m => [[Block]] -> RST m Doc notesToRST notes = - mapM (\(num, note) -> noteToRST num note) (zip [1..] notes) >>= + mapM (uncurry noteToRST) (zip [1..] notes) >>= return . vsep -- | Return RST representation of a note. @@ -226,7 +225,7 @@ blockToRST (Para [Image attr txt (src,'f':'i':'g':':':tit)]) = do else ":figclass: " <> text (unwords cls) return $ hang 3 ".. " (fig $$ alt $$ classes $$ dims $+$ capt) $$ blankline blockToRST (Para inlines) - | LineBreak `elem` inlines = do -- use line block if LineBreaks + | LineBreak `elem` inlines = linesToLineBlock $ splitBy (==LineBreak) inlines | otherwise = do contents <- inlineListToRST inlines @@ -237,7 +236,7 @@ blockToRST (RawBlock f@(Format f') str) | f == "rst" = return $ text str | otherwise = return $ blankline <> ".. raw:: " <> text (map toLower f') $+$ - (nest 3 $ text str) $$ blankline + nest 3 (text str) $$ blankline blockToRST HorizontalRule = return $ blankline $$ "--------------" $$ blankline blockToRST (Header level (name,classes,_) inlines) = do @@ -279,7 +278,7 @@ blockToRST (CodeBlock (_,classes,kvs) str) = do blockToRST (BlockQuote blocks) = do tabstop <- gets $ writerTabStop . stOptions contents <- blockListToRST blocks - return $ (nest tabstop contents) <> blankline + return $ nest tabstop contents <> blankline blockToRST (Table caption aligns widths headers rows) = do caption' <- inlineListToRST caption let blocksToDoc opts bs = do @@ -302,13 +301,13 @@ blockToRST (BulletList items) = do return $ blankline $$ chomp (vcat contents) $$ blankline blockToRST (OrderedList (start, style', delim) items) = do let markers = if start == 1 && style' == DefaultStyle && delim == DefaultDelim - then take (length items) $ repeat "#." + then replicate (length items) "#." else take (length items) $ orderedListMarkers (start, style', delim) let maxMarkerLength = maximum $ map length markers let markers' = map (\m -> let s = maxMarkerLength - length m in m ++ replicate s ' ') markers - contents <- mapM (\(item, num) -> orderedListItemToRST item num) $ + contents <- mapM (uncurry orderedListItemToRST) $ zip markers' items -- ensure that sublists have preceding blank line return $ blankline $$ chomp (vcat contents) $$ blankline @@ -345,7 +344,8 @@ definitionListItemToRST (label, defs) = do linesToLineBlock :: PandocMonad m => [[Inline]] -> RST m Doc linesToLineBlock inlineLines = do lns <- mapM inlineListToRST inlineLines - return $ (vcat $ map (hang 2 (text "| ")) lns) <> blankline + return $ + vcat (map (hang 2 (text "| ")) lns) <> blankline -- | Convert list of Pandoc block elements to RST. blockListToRST' :: PandocMonad m @@ -397,7 +397,7 @@ inlineListToRST lst = removeSpaceAfterDisplayMath [] = [] insertBS :: [Inline] -> [Inline] -- insert '\ ' where needed insertBS (x:y:z:zs) - | isComplex y && (surroundComplex x z) = + | isComplex y && surroundComplex x z = x : y : insertBS (z : zs) insertBS (x:y:zs) | isComplex x && not (okAfterComplex y) = @@ -437,8 +437,8 @@ inlineListToRST lst = isComplex (Strikeout _) = True isComplex (Superscript _) = True isComplex (Subscript _) = True - isComplex (Link _ _ _) = True - isComplex (Image _ _ _) = True + isComplex (Link{}) = True + isComplex (Image{}) = True isComplex (Code _ _) = True isComplex (Math _ _) = True isComplex (Cite _ (x:_)) = isComplex x @@ -512,7 +512,7 @@ inlineToRST il@(RawInline f x) modify $ \st -> st{ stHasRawTeX = True } return $ ":raw-latex:`" <> text x <> "`" | otherwise = empty <$ report (InlineNotRendered il) -inlineToRST (LineBreak) = return cr -- there's no line break in RST (see Para) +inlineToRST LineBreak = return cr -- there's no line break in RST (see Para) inlineToRST Space = return space inlineToRST SoftBreak = do wrapText <- gets $ writerWrapText . stOptions @@ -540,7 +540,7 @@ inlineToRST (Link _ txt (src, tit)) = do Just (src',tit') -> if src == src' && tit == tit' then return $ "`" <> linktext <> "`_" - else do -- duplicate label, use non-reference link + else return $ "`" <> linktext <> " <" <> text src <> ">`__" Nothing -> do modify $ \st -> st { stLinks = (txt,(src,tit)):refs } @@ -553,7 +553,7 @@ inlineToRST (Note contents) = do -- add to notes in state notes <- gets stNotes modify $ \st -> st { stNotes = contents:notes } - let ref = show $ (length notes) + 1 + let ref = show $ length notes + 1 return $ " [" <> text ref <> "]_" registerImage :: PandocMonad m => Attr -> [Inline] -> Target -> Maybe String -> RST m Doc @@ -578,7 +578,7 @@ imageDimsToRST attr = do then empty else ":name: " <> text ident showDim dir = let cols d = ":" <> text (show dir) <> ": " <> text (show d) - in case (dimension dir attr) of + in case dimension dir attr of Just (Percent a) -> case dir of Height -> empty diff --git a/src/Text/Pandoc/Writers/RTF.hs b/src/Text/Pandoc/Writers/RTF.hs index 2b05f2f7e..917fef3eb 100644 --- a/src/Text/Pandoc/Writers/RTF.hs +++ b/src/Text/Pandoc/Writers/RTF.hs @@ -113,7 +113,7 @@ writeRTF options doc = do $ metamap metadata <- metaToJSON options (fmap concat . mapM (blockToRTF 0 AlignDefault)) - (inlinesToRTF) + inlinesToRTF meta' body <- blocksToRTF 0 AlignDefault blocks let isTOCHeader (Header lev _ _) = lev <= writerTOCDepth options @@ -121,14 +121,13 @@ writeRTF options doc = do toc <- tableOfContents $ filter isTOCHeader blocks let context = defField "body" body $ defField "spacer" spacer - $ (if writerTableOfContents options - then defField "table-of-contents" toc - -- for backwards compatibility, - -- we populate toc with the contents - -- of the toc rather than a boolean: - . defField "toc" toc - else id) - $ metadata + $(if writerTableOfContents options + then defField "table-of-contents" toc + -- for backwards compatibility, + -- we populate toc with the contents + -- of the toc rather than a boolean: + . defField "toc" toc + else id) metadata T.pack <$> case writerTemplate options of Just tpl -> renderTemplate' tpl context @@ -141,12 +140,12 @@ writeRTF options doc = do tableOfContents :: PandocMonad m => [Block] -> m String tableOfContents headers = do let contents = map elementToListItem $ hierarchicalize headers - blocksToRTF 0 AlignDefault $ + blocksToRTF 0 AlignDefault [Header 1 nullAttr [Str "Contents"], BulletList contents] elementToListItem :: Element -> [Block] elementToListItem (Blk _) = [] -elementToListItem (Sec _ _ _ sectext subsecs) = [Plain sectext] ++ +elementToListItem (Sec _ _ _ sectext subsecs) = Plain sectext : if null subsecs then [] else [BulletList (map elementToListItem subsecs)] @@ -163,11 +162,11 @@ handleUnicode (c:cs) = lower = r + 0xDC00 in enc (chr upper) ++ enc (chr lower) ++ handleUnicode cs else enc c ++ handleUnicode cs - else c:(handleUnicode cs) + else c:handleUnicode cs where surrogate x = not ( (0x0000 <= ord x && ord x <= 0xd7ff) || (0xe000 <= ord x && ord x <= 0xffff) ) - enc x = '\\':'u':(show (ord x)) ++ "?" + enc x = '\\':'u':show (ord x) ++ "?" -- | Escape special characters. escapeSpecial :: String -> String @@ -203,8 +202,8 @@ rtfParSpaced spaceAfter indent firstLineIndent alignment content = AlignCenter -> "\\qc " AlignDefault -> "\\ql " in "{\\pard " ++ alignString ++ - "\\f0 \\sa" ++ (show spaceAfter) ++ " \\li" ++ (show indent) ++ - " \\fi" ++ (show firstLineIndent) ++ " " ++ content ++ "\\par}\n" + "\\f0 \\sa" ++ show spaceAfter ++ " \\li" ++ show indent ++ + " \\fi" ++ show firstLineIndent ++ " " ++ content ++ "\\par}\n" -- | Default paragraph. rtfPar :: Int -- ^ block indent (in twips) @@ -269,7 +268,7 @@ blockToRTF indent alignment (LineBlock lns) = blockToRTF indent alignment (BlockQuote lst) = blocksToRTF (indent + indentIncrement) alignment lst blockToRTF indent _ (CodeBlock _ str) = - return $ rtfPar indent 0 AlignLeft ("\\f1 " ++ (codeStringToRTF str)) + return $ rtfPar indent 0 AlignLeft ("\\f1 " ++ codeStringToRTF str) blockToRTF _ _ b@(RawBlock f str) | f == Format "rtf" = return str | otherwise = do @@ -279,7 +278,7 @@ 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) + mapM (uncurry (listItemToRTF alignment indent)) (zip (orderedMarkers indent attribs) lst) blockToRTF indent alignment (DefinitionList lst) = (spaceAtEnd . concat) <$> mapM (definitionListItemToRTF alignment indent) lst @@ -288,7 +287,7 @@ blockToRTF indent _ HorizontalRule = return $ blockToRTF indent alignment (Header level _ lst) = do contents <- inlinesToRTF lst return $ rtfPar indent 0 alignment $ - "\\b \\fs" ++ (show (40 - (level * 4))) ++ " " ++ contents + "\\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 @@ -302,9 +301,9 @@ tableRowToRTF :: PandocMonad m tableRowToRTF header indent aligns sizes' cols = do let totalTwips = 6 * 1440 -- 6 inches let sizes = if all (== 0) sizes' - then take (length cols) $ repeat (1.0 / fromIntegral (length cols)) + then replicate (length cols) (1.0 / fromIntegral (length cols)) else sizes' - columns <- concat <$> mapM (\(x,y) -> tableItemToRTF indent x y) + columns <- concat <$> mapM (uncurry (tableItemToRTF indent)) (zip aligns cols) let rightEdges = tail $ scanl (\sofar new -> sofar + floor (new * totalTwips)) (0 :: Integer) sizes @@ -326,8 +325,8 @@ tableItemToRTF indent alignment item = do -- lists as after regular lists. spaceAtEnd :: String -> String spaceAtEnd str = - if isSuffixOf "\\par}\n" str - then (take ((length str) - 6) str) ++ "\\sa180\\par}\n" + if "\\par}\n" `isSuffixOf` str + then take ((length str) - 6) str ++ "\\sa180\\par}\n" else str -- | Convert list item (list of blocks) to RTF. @@ -338,11 +337,11 @@ listItemToRTF :: PandocMonad m -> [Block] -- ^ list item (list of blocks) -> m String listItemToRTF alignment indent marker [] = return $ - rtfCompact (indent + listIncrement) (0 - listIncrement) alignment - (marker ++ "\\tx" ++ (show listIncrement) ++ "\\tab ") + rtfCompact (indent + listIncrement) (negate listIncrement) alignment + (marker ++ "\\tx" ++ show listIncrement ++ "\\tab ") listItemToRTF alignment indent marker list = do (first:rest) <- mapM (blockToRTF (indent + listIncrement) alignment) list - let listMarker = "\\fi" ++ show (0 - listIncrement) ++ " " ++ marker ++ + let listMarker = "\\fi" ++ show (negate listIncrement) ++ " " ++ marker ++ "\\tx" ++ show listIncrement ++ "\\tab" let insertListMarker ('\\':'f':'i':'-':d:xs) | isDigit d = listMarker ++ dropWhile isDigit xs @@ -400,7 +399,7 @@ inlineToRTF (Quoted SingleQuote lst) = do inlineToRTF (Quoted DoubleQuote lst) = do contents <- inlinesToRTF lst return $ "\\u8220\"" ++ contents ++ "\\u8221\"" -inlineToRTF (Code _ str) = return $ "{\\f1 " ++ (codeStringToRTF str) ++ "}" +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 @@ -409,12 +408,12 @@ inlineToRTF il@(RawInline f str) | otherwise = do return $ InlineNotRendered il return "" -inlineToRTF (LineBreak) = return "\\line " +inlineToRTF LineBreak = return "\\line " inlineToRTF SoftBreak = return " " inlineToRTF Space = return " " inlineToRTF (Link _ text (src, _)) = do contents <- inlinesToRTF text - return $ "{\\field{\\*\\fldinst{HYPERLINK \"" ++ (codeStringToRTF src) ++ + return $ "{\\field{\\*\\fldinst{HYPERLINK \"" ++ codeStringToRTF src ++ "\"}}{\\fldrslt{\\ul\n" ++ contents ++ "\n}}}\n" inlineToRTF (Image _ _ (source, _)) = return $ "{\\cf1 [image: " ++ source ++ "]\\cf0}" diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index 8f1a06688..0b951b0c9 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -268,19 +268,19 @@ gridTable opts blocksToDoc headless aligns widths headers rows = do else handleGivenWidths widths let hpipeBlocks blocks = hcat [beg, middle, end] where h = maximum (1 : map height blocks) - sep' = lblock 3 $ vcat (map text $ replicate h " | ") - beg = lblock 2 $ vcat (map text $ replicate h "| ") - end = lblock 2 $ vcat (map text $ replicate h " |") + sep' = lblock 3 $ vcat (replicate h (text " | ")) + beg = lblock 2 $ vcat (replicate h (text "| ")) + end = lblock 2 $ vcat (replicate h (text " |")) middle = chomp $ hcat $ intersperse sep' blocks let makeRow = hpipeBlocks . zipWith lblock widthsInChars let head' = makeRow rawHeaders let rows' = map (makeRow . map chomp) rawRows let borderpart ch align widthInChars = - (if (align == AlignLeft || align == AlignCenter) + (if align == AlignLeft || align == AlignCenter then char ':' else char ch) <> text (replicate widthInChars ch) <> - (if (align == AlignRight || align == AlignCenter) + (if align == AlignRight || align == AlignCenter then char ':' else char ch) let border ch aligns' widthsInChars' = diff --git a/src/Text/Pandoc/Writers/TEI.hs b/src/Text/Pandoc/Writers/TEI.hs index dfdb443a2..aa87c55e1 100644 --- a/src/Text/Pandoc/Writers/TEI.hs +++ b/src/Text/Pandoc/Writers/TEI.hs @@ -79,10 +79,10 @@ writeTEI opts (Pandoc meta blocks) = do meta' main <- (render' . vcat) <$> mapM (elementToTEI opts startLvl) elements let context = defField "body" main - $ defField "mathml" (case writerHTMLMathMethod opts of - MathML -> True - _ -> False) - $ metadata + $ + defField "mathml" (case writerHTMLMathMethod opts of + MathML -> True + _ -> False) metadata case writerTemplate opts of Nothing -> return main Just tpl -> renderTemplate' tpl context @@ -121,7 +121,7 @@ plainToPara x = x deflistItemsToTEI :: PandocMonad m => WriterOptions -> [([Inline],[[Block]])] -> m Doc deflistItemsToTEI opts items = - vcat <$> mapM (\(term, defs) -> deflistItemToTEI opts term defs) items + vcat <$> mapM (uncurry (deflistItemToTEI opts)) items -- | Convert a term and a list of blocks into a TEI varlistentry. deflistItemToTEI :: PandocMonad m @@ -146,7 +146,7 @@ imageToTEI _ attr src = return $ selfClosingTag "graphic" $ ("url", src) : idAndRole attr ++ dims where dims = go Width "width" ++ go Height "depth" - go dir dstr = case (dimension dir attr) of + go dir dstr = case dimension dir attr of Just a -> [(dstr, show a)] Nothing -> [] @@ -159,7 +159,7 @@ blockToTEI opts (Div (ident,_,_) [Para lst]) = do let attribs = [("id", ident) | not (null ident)] inTags False "p" attribs <$> inlinesToTEI opts lst blockToTEI opts (Div _ bs) = blocksToTEI opts $ map plainToPara bs -blockToTEI _ h@(Header _ _ _) = do +blockToTEI _ h@(Header{}) = do -- should not occur after hierarchicalize, except inside lists/blockquotes report $ BlockNotRendered h return empty @@ -214,7 +214,7 @@ blockToTEI opts (OrderedList (start, numstyle, _) (first:rest)) = do else do fi <- blocksToTEI opts $ map plainToPara first re <- listItemsToTEI opts rest - return $ (inTags True "item" [("n",show start)] fi) $$ re + return $ inTags True "item" [("n",show start)] fi $$ re return $ inTags True "list" attribs items blockToTEI opts (DefinitionList lst) = do let attribs = [("type", "definition")] @@ -295,28 +295,31 @@ inlineToTEI _ (Code _ str) = return $ inlineToTEI _ (Math t str) = return $ case t of InlineMath -> inTags False "formula" [("notation","TeX")] $ - text (str) + text str DisplayMath -> inTags True "figure" [("type","math")] $ - inTags False "formula" [("notation","TeX")] $ text (str) + inTags False "formula" [("notation","TeX")] $ text str inlineToTEI _ il@(RawInline f x) | f == "tei" = return $ text x | otherwise = empty <$ report (InlineNotRendered il) inlineToTEI _ LineBreak = return $ selfClosingTag "lb" [] -inlineToTEI _ Space = return $ space +inlineToTEI _ Space = + return space -- because we use \n for LineBreak, we can't do soft breaks: -inlineToTEI _ SoftBreak = return $ space +inlineToTEI _ SoftBreak = + return space inlineToTEI opts (Link attr txt (src, _)) | Just email <- stripPrefix "mailto:" src = do let emailLink = text $ - escapeStringForXML $ email + escapeStringForXML email case txt of - [Str s] | escapeURI s == email -> return $ emailLink + [Str s] | escapeURI s == email -> + return emailLink _ -> do linktext <- inlinesToTEI opts txt return $ linktext <+> char '(' <> emailLink <> char ')' | otherwise = - (if isPrefixOf "#" src + (if "#" `isPrefixOf` src then inTags False "ref" $ ("target", drop 1 src) : idAndRole attr else inTags False "ref" $ ("target", src) : idAndRole attr ) <$> inlinesToTEI opts txt diff --git a/src/Text/Pandoc/Writers/Texinfo.hs b/src/Text/Pandoc/Writers/Texinfo.hs index 549d4f3d9..2d0c7a86d 100644 --- a/src/Text/Pandoc/Writers/Texinfo.hs +++ b/src/Text/Pandoc/Writers/Texinfo.hs @@ -71,7 +71,7 @@ type TI m = StateT WriterState m -- | Convert Pandoc to Texinfo. writeTexinfo :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeTexinfo options document = - evalStateT (pandocToTexinfo options $ wrapTop document) $ + evalStateT (pandocToTexinfo options $ wrapTop document) WriterState { stStrikeout = False, stSuperscript = False, stEscapeComma = False, stSubscript = False, stIdentifiers = Set.empty, stOptions = options} @@ -102,8 +102,8 @@ pandocToTexinfo options (Pandoc meta blocks) = do $ defField "titlepage" titlePage $ defField "subscript" (stSubscript st) $ defField "superscript" (stSuperscript st) - $ defField "strikeout" (stStrikeout st) - $ metadata + $ + defField "strikeout" (stStrikeout st) metadata case writerTemplate options of Nothing -> return body Just tpl -> renderTemplate' tpl context @@ -166,11 +166,11 @@ blockToTexinfo (BlockQuote lst) = do contents $$ text "@end quotation" -blockToTexinfo (CodeBlock _ str) = do +blockToTexinfo (CodeBlock _ str) = return $ blankline $$ - text "@verbatim" $$ - flush (text str) $$ - text "@end verbatim" <> blankline + text "@verbatim" $$ + flush (text str) $$ + text "@end verbatim" <> blankline blockToTexinfo b@(RawBlock f str) | f == "texinfo" = return $ text str @@ -218,7 +218,7 @@ blockToTexinfo HorizontalRule = text "@bigskip@hrule@bigskip" $$ text "@end iftex" $$ text "@ifnottex" $$ - text (take 72 $ repeat '-') $$ + text (replicate 72 '-') $$ text "@end ifnottex" blockToTexinfo (Header 0 _ lst) = do @@ -339,7 +339,7 @@ blockListToTexinfo (x:xs) = do Para _ -> do xs' <- blockListToTexinfo xs case xs of - ((CodeBlock _ _):_) -> return $ x' $$ xs' + (CodeBlock _ _:_) -> return $ x' $$ xs' _ -> return $ x' $+$ xs' _ -> do xs' <- blockListToTexinfo xs @@ -437,7 +437,7 @@ inlineToTexinfo (Subscript lst) = do inlineToTexinfo (SmallCaps lst) = inlineListToTexinfo lst >>= return . inCmd "sc" -inlineToTexinfo (Code _ str) = do +inlineToTexinfo (Code _ str) = return $ text $ "@code{" ++ stringToTexinfo str ++ "}" inlineToTexinfo (Quoted SingleQuote lst) = do @@ -459,7 +459,7 @@ inlineToTexinfo il@(RawInline f str) | otherwise = do report $ InlineNotRendered il return empty -inlineToTexinfo (LineBreak) = return $ text "@*" <> cr +inlineToTexinfo LineBreak = return $ text "@*" <> cr inlineToTexinfo SoftBreak = do wrapText <- gets (writerWrapText . stOptions) case wrapText of @@ -472,7 +472,7 @@ inlineToTexinfo (Link _ txt (src@('#':_), _)) = do contents <- escapeCommas $ inlineListToTexinfo txt return $ text "@ref" <> braces (text (stringToTexinfo src) <> text "," <> contents) -inlineToTexinfo (Link _ txt (src, _)) = do +inlineToTexinfo (Link _ txt (src, _)) = case txt of [Str x] | escapeURI x == src -> -- autolink do return $ text $ "@url{" ++ x ++ "}" @@ -484,7 +484,7 @@ inlineToTexinfo (Link _ txt (src, _)) = do inlineToTexinfo (Image attr alternate (source, _)) = do content <- escapeCommas $ inlineListToTexinfo alternate opts <- gets stOptions - let showDim dim = case (dimension dim attr) of + let showDim dim = case dimension dim attr of (Just (Pixel a)) -> showInInch opts (Pixel a) ++ "in" (Just (Percent _)) -> "" (Just d) -> show d diff --git a/src/Text/Pandoc/Writers/Textile.hs b/src/Text/Pandoc/Writers/Textile.hs index 5ee9d3250..11fb2ae12 100644 --- a/src/Text/Pandoc/Writers/Textile.hs +++ b/src/Text/Pandoc/Writers/Textile.hs @@ -297,7 +297,7 @@ definitionListItemToTextile opts (label, items) = do labelText <- inlineListToTextile opts label contents <- mapM (blockListToTextile opts) items return $ "<dt>" ++ labelText ++ "</dt>\n" ++ - (intercalate "\n" $ map (\d -> "<dd>" ++ d ++ "</dd>") contents) + intercalate "\n" (map (\d -> "<dd>" ++ d ++ "</dd>") contents) -- | True if the list can be handled by simple wiki markup, False if HTML tags will be needed. isSimpleList :: Block -> Bool @@ -350,7 +350,7 @@ tableRowToTextile opts alignStrings rownum cols' = do 0 -> "header" x | x `rem` 2 == 1 -> "odd" _ -> "even" - cols'' <- sequence $ zipWith + cols'' <- zipWithM (\alignment item -> tableItemToTextile opts celltype alignment item) alignStrings cols' return $ "<tr class=\"" ++ rowclass ++ "\">\n" ++ unlines cols'' ++ "</tr>" @@ -483,7 +483,7 @@ inlineToTextile opts (Image attr@(_, cls, _) alt (source, tit)) = do then "" else "(" ++ unwords cls ++ ")" showDim dir = let toCss str = Just $ show dir ++ ":" ++ str ++ ";" - in case (dimension dir attr) of + in case dimension dir attr of Just (Percent a) -> toCss $ show (Percent a) Just dim -> toCss $ showInPixel opts dim ++ "px" Nothing -> Nothing diff --git a/src/Text/Pandoc/Writers/ZimWiki.hs b/src/Text/Pandoc/Writers/ZimWiki.hs index 67dcd72d1..60029c0d4 100644 --- a/src/Text/Pandoc/Writers/ZimWiki.hs +++ b/src/Text/Pandoc/Writers/ZimWiki.hs @@ -36,6 +36,7 @@ import Control.Monad (zipWithM) import Control.Monad.State.Strict (StateT, evalStateT, gets, modify) import Data.Default (Default (..)) import Data.List (intercalate, isInfixOf, isPrefixOf, transpose) +import Data.Maybe (fromMaybe) import qualified Data.Map as Map import Data.Text (Text, breakOnAll, pack) import Text.Pandoc.Class (PandocMonad, report) @@ -75,8 +76,7 @@ pandocToZimWiki opts (Pandoc meta blocks) = do --let header = "Content-Type: text/x-zim-wiki\nWiki-Format: zim 0.4\n" let main = body let context = defField "body" main - $ defField "toc" (writerTableOfContents opts) - $ metadata + $ defField "toc" (writerTableOfContents opts) metadata case writerTemplate opts of Just tpl -> renderTemplate' tpl context Nothing -> return main @@ -118,12 +118,12 @@ blockToZimWiki opts (Para inlines) = do contents <- inlineListToZimWiki opts inlines return $ contents ++ if null indent then "\n" else "" -blockToZimWiki opts (LineBlock lns) = do +blockToZimWiki opts (LineBlock lns) = blockToZimWiki opts $ linesToPara lns blockToZimWiki opts b@(RawBlock f str) | f == Format "zimwiki" = return str - | f == Format "html" = do cont <- indentFromHTML opts str; return cont + | f == Format "html" = indentFromHTML opts str | otherwise = do report $ BlockNotRendered b return "" @@ -142,9 +142,7 @@ blockToZimWiki _ (CodeBlock (_,classes,_) str) = do return $ case classes of [] -> "'''\n" ++ cleanupCode str ++ "\n'''\n" -- turn no lang block into a quote block (x:_) -> "{{{code: lang=\"" ++ - (case Map.lookup x langmap of - Nothing -> x - Just y -> y) ++ "\" linenumbers=\"True\"\n" ++ str ++ "\n}}}\n" -- for zim's code plugin, go verbatim on the lang spec + (fromMaybe x (Map.lookup x langmap)) ++ "\" linenumbers=\"True\"\n" ++ str ++ "\n}}}\n" -- for zim's code plugin, go verbatim on the lang spec blockToZimWiki opts (BlockQuote blocks) = do contents <- blockListToZimWiki opts blocks @@ -157,12 +155,12 @@ blockToZimWiki opts (Table capt aligns _ headers rows) = do c <- inlineListToZimWiki opts capt return $ "" ++ c ++ "\n" headers' <- if all null headers - then zipWithM (tableItemToZimWiki opts) aligns (rows !! 0) - else mapM (inlineListToZimWiki opts) (map removeFormatting headers) -- emphasis, links etc. are not allowed in table headers + then zipWithM (tableItemToZimWiki opts) aligns (head rows) + else mapM ((inlineListToZimWiki opts) . removeFormatting)headers -- emphasis, links etc. are not allowed in table headers rows' <- mapM (zipWithM (tableItemToZimWiki opts) aligns) rows let widths = map (maximum . map length) $ transpose (headers':rows') let padTo (width, al) s = - case (width - length s) of + case width - length s of x | x > 0 -> if al == AlignLeft || al == AlignDefault then s ++ replicate x ' ' @@ -171,14 +169,11 @@ blockToZimWiki opts (Table capt aligns _ headers rows) = do else replicate (x `div` 2) ' ' ++ s ++ replicate (x - x `div` 2) ' ' | otherwise -> s - let borderCell (width, al) _ = - if al == AlignLeft - then ":"++ replicate (width-1) '-' - else if al == AlignDefault - then replicate width '-' - else if al == AlignRight - then replicate (width-1) '-' ++ ":" - else ":" ++ replicate (width-2) '-' ++ ":" + let borderCell (width, al) _ + | al == AlignLeft = ":"++ replicate (width-1) '-' + | al == AlignDefault = replicate width '-' + | al == AlignRight = replicate (width-1) '-' ++ ":" + | otherwise = ":" ++ replicate (width-2) '-' ++ ":" let underheader = "|" ++ intercalate "|" (zipWith borderCell (zip widths aligns) headers') ++ "|" let renderRow cells = "|" ++ intercalate "|" (zipWith padTo (zip widths aligns) cells) ++ "|" return $ captionDoc ++ @@ -188,19 +183,19 @@ blockToZimWiki opts (Table capt aligns _ headers rows) = do blockToZimWiki opts (BulletList items) = do indent <- gets stIndent modify $ \s -> s { stIndent = stIndent s ++ "\t" } - contents <- (mapM (listItemToZimWiki opts) items) + contents <- mapM (listItemToZimWiki opts) items modify $ \s -> s{ stIndent = indent } -- drop 1 (stIndent s) } return $ vcat contents ++ if null indent then "\n" else "" blockToZimWiki opts (OrderedList _ items) = do indent <- gets stIndent modify $ \s -> s { stIndent = stIndent s ++ "\t", stItemNum = 1 } - contents <- (mapM (orderedListItemToZimWiki opts) items) + contents <- mapM (orderedListItemToZimWiki opts) items modify $ \s -> s{ stIndent = indent } -- drop 1 (stIndent s) } return $ vcat contents ++ if null indent then "\n" else "" blockToZimWiki opts (DefinitionList items) = do - contents <- (mapM (definitionListItemToZimWiki opts) items) + contents <- mapM (definitionListItemToZimWiki opts) items return $ vcat contents definitionListItemToZimWiki :: PandocMonad m @@ -218,19 +213,19 @@ indentFromHTML :: PandocMonad m => WriterOptions -> String -> ZW m String indentFromHTML _ str = do indent <- gets stIndent itemnum <- gets stItemNum - if isInfixOf "<li>" str then return $ indent ++ show itemnum ++ "." - else if isInfixOf "</li>" str then return "\n" - else if isInfixOf "<li value=" str then do + if "<li>" `isInfixOf` str then return $ indent ++ show itemnum ++ "." + else if "</li>" `isInfixOf` str then return "\n" + else if "<li value=" `isInfixOf` str then do -- poor man's cut let val = drop 10 $ reverse $ drop 1 $ reverse str --let val = take ((length valls) - 2) valls modify $ \s -> s { stItemNum = read val } return "" - else if isInfixOf "<ol>" str then do + else if "<ol>" `isInfixOf` str then do let olcount=countSubStrs "<ol>" str modify $ \s -> s { stIndent = stIndent s ++ replicate olcount '\t', stItemNum = 1 } return "" - else if isInfixOf "</ol>" str then do + else if "</ol>" `isInfixOf` str then do let olcount=countSubStrs "/<ol>" str modify $ \s -> s{ stIndent = drop olcount (stIndent s) } return "" @@ -286,7 +281,7 @@ blockListToZimWiki opts blocks = vcat <$> mapM (blockToZimWiki opts) blocks -- | Convert list of Pandoc inline elements to ZimWiki. inlineListToZimWiki :: PandocMonad m => WriterOptions -> [Inline] -> ZW m String -inlineListToZimWiki opts lst = concat <$> (mapM (inlineToZimWiki opts) lst) +inlineListToZimWiki opts lst = concat <$> mapM (inlineToZimWiki opts) lst -- | Convert Pandoc inline element to ZimWiki. inlineToZimWiki :: PandocMonad m @@ -335,7 +330,7 @@ inlineToZimWiki _ (Str str) = do then return $ substitute "|" "\\|" . escapeString $ str else if inLink - then return $ str + then return str else return $ escapeString str inlineToZimWiki _ (Math mathType str) = return $ delim ++ str ++ delim -- note: str should NOT be escaped @@ -346,7 +341,7 @@ inlineToZimWiki _ (Math mathType str) = return $ delim ++ str ++ delim -- note -- | f == Format "html" = return $ "<html>" ++ str ++ "</html>" inlineToZimWiki opts il@(RawInline f str) | f == Format "zimwiki" = return str - | f == Format "html" = do cont <- indentFromHTML opts str; return cont + | f == Format "html" = indentFromHTML opts str | otherwise = do report $ InlineNotRendered il return "" -- cgit v1.2.3 From 52ee19a825fad5255e15fccfdf6de8d4203b5fec Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 29 Oct 2017 14:29:32 -0700 Subject: Source code reformatting. --- src/Text/Pandoc/Class.hs | 1 + src/Text/Pandoc/Logging.hs | 2 +- src/Text/Pandoc/Parsing.hs | 129 +++++++++++++-------------- src/Text/Pandoc/Readers/DocBook.hs | 48 +++++----- src/Text/Pandoc/Readers/Docx.hs | 4 +- src/Text/Pandoc/Readers/Docx/Lists.hs | 2 +- src/Text/Pandoc/Readers/EPUB.hs | 6 +- src/Text/Pandoc/Readers/HTML.hs | 124 ++++++++++++------------- src/Text/Pandoc/Readers/Muse.hs | 18 ++-- src/Text/Pandoc/Readers/Odt/ContentReader.hs | 1 - src/Text/Pandoc/Readers/RST.hs | 11 ++- src/Text/Pandoc/Readers/Vimwiki.hs | 10 +-- src/Text/Pandoc/Shared.hs | 2 +- src/Text/Pandoc/Slides.hs | 2 +- src/Text/Pandoc/Templates.hs | 6 +- src/Text/Pandoc/Writers/Docbook.hs | 6 +- src/Text/Pandoc/Writers/EPUB.hs | 2 +- src/Text/Pandoc/Writers/HTML.hs | 4 +- src/Text/Pandoc/Writers/JATS.hs | 6 +- src/Text/Pandoc/Writers/LaTeX.hs | 4 +- src/Text/Pandoc/Writers/Man.hs | 2 +- src/Text/Pandoc/Writers/Ms.hs | 6 +- src/Text/Pandoc/Writers/RST.hs | 6 +- src/Text/Pandoc/Writers/Texinfo.hs | 2 +- src/Text/Pandoc/Writers/ZimWiki.hs | 2 +- 25 files changed, 202 insertions(+), 204 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 7c518e84b..2b8b1c090 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -2,6 +2,7 @@ {-# LANGUAGE PatternGuards #-} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE TypeSynonymInstances #-} +{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE FlexibleContexts #-} diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs index a156f017c..4723c1119 100644 --- a/src/Text/Pandoc/Logging.hs +++ b/src/Text/Pandoc/Logging.hs @@ -45,8 +45,8 @@ import Data.Aeson.Encode.Pretty (Config (..), defConfig, encodePretty', keyOrder) import qualified Data.ByteString.Lazy as BL import Data.Data (Data, toConstr) -import Data.Typeable (Typeable) import qualified Data.Text as Text +import Data.Typeable (Typeable) import GHC.Generics (Generic) import Text.Pandoc.Definition import Text.Parsec.Pos diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 73498788d..a02034de4 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -1,11 +1,10 @@ -{-# LANGUAGE - FlexibleContexts -, GeneralizedNewtypeDeriving -, TypeSynonymInstances -, MultiParamTypeClasses -, FlexibleInstances -, IncoherentInstances #-} - +{-# LANGUAGE ExplicitForAll #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE IncoherentInstances #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE TypeSynonymInstances #-} {- Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> @@ -187,34 +186,34 @@ module Text.Pandoc.Parsing ( takeWhileP, ) where +import Control.Monad.Identity +import Control.Monad.Reader +import Data.Char (chr, isAlphaNum, isAscii, isHexDigit, isPunctuation, isSpace, + ord, toLower, toUpper) +import Data.Default +import Data.List (intercalate, isSuffixOf, transpose) +import qualified Data.Map as M +import Data.Maybe (catMaybes) +import Data.Monoid ((<>)) +import qualified Data.Set as Set import Data.Text (Text) +import Text.HTML.TagSoup.Entity (lookupEntity) +import Text.Pandoc.Asciify (toAsciiChar) +import Text.Pandoc.Builder (Blocks, HasMeta (..), Inlines, trimInlines) +import qualified Text.Pandoc.Builder as B +import Text.Pandoc.Class (PandocMonad, readFileFromDirs, report) import Text.Pandoc.Definition +import Text.Pandoc.Logging import Text.Pandoc.Options -import Text.Pandoc.Builder (Blocks, Inlines, HasMeta(..), trimInlines) -import qualified Text.Pandoc.Builder as B -import Text.Pandoc.XML (fromEntities) +import Text.Pandoc.Readers.LaTeX.Types (Macro) +import Text.Pandoc.Shared import qualified Text.Pandoc.UTF8 as UTF8 (putStrLn) +import Text.Pandoc.XML (fromEntities) import Text.Parsec hiding (token) -import Text.Parsec.Pos (newPos, initialPos, updatePosString) -import Data.Char ( toLower, toUpper, ord, chr, isAscii, isAlphaNum, - isHexDigit, isSpace, isPunctuation ) -import Data.List ( intercalate, transpose, isSuffixOf ) -import Text.Pandoc.Shared -import qualified Data.Map as M -import Text.Pandoc.Readers.LaTeX.Types (Macro) -import Text.HTML.TagSoup.Entity ( lookupEntity ) -import Text.Pandoc.Asciify (toAsciiChar) -import Data.Monoid ((<>)) -import Text.Pandoc.Class (PandocMonad, readFileFromDirs, report) -import Text.Pandoc.Logging -import Data.Default -import qualified Data.Set as Set -import Control.Monad.Reader -import Control.Monad.Identity -import Data.Maybe (catMaybes) +import Text.Parsec.Pos (initialPos, newPos, updatePosString) -import Text.Pandoc.Error import Control.Monad.Except +import Text.Pandoc.Error type Parser t s = Parsec t s @@ -670,9 +669,9 @@ withRaw parser = do let (l2,c2) = (sourceLine pos2, sourceColumn pos2) let inplines = take ((l2 - l1) + 1) $ lines inp let raw = case inplines of - [] -> "" - [l] -> take (c2 - c1) l - ls -> unlines (init ls) ++ take (c2 - 1) (last ls) + [] -> "" + [l] -> take (c2 - c1) l + ls -> unlines (init ls) ++ take (c2 - 1) (last ls) return (result, raw) -- | Parses backslash, then applies character parser. @@ -688,11 +687,11 @@ characterReference = try $ do ent <- many1Till nonspaceChar (char ';') let ent' = case ent of '#':'X':xs -> '#':'x':xs -- workaround tagsoup bug - '#':_ -> ent - _ -> ent ++ ";" + '#':_ -> ent + _ -> ent ++ ";" case lookupEntity ent' of - Just (c : _) -> return c - _ -> fail "entity not found" + Just (c : _) -> return c + _ -> fail "entity not found" -- | Parses an uppercase roman numeral and returns (UpperRoman, number). upperRoman :: Stream s m Char => ParserT s st m (ListNumberStyle, Int) @@ -1006,7 +1005,7 @@ removeOneLeadingSpace xs = if all startsWithSpace xs then map (drop 1) xs else xs - where startsWithSpace "" = True + where startsWithSpace "" = True startsWithSpace (y:_) = y == ' ' -- | Parse footer for a grid table. @@ -1042,36 +1041,36 @@ testStringWith parser str = UTF8.putStrLn $ show $ -- | Parsing options. data ParserState = ParserState - { stateOptions :: ReaderOptions, -- ^ User options - stateParserContext :: ParserContext, -- ^ Inside list? - stateQuoteContext :: QuoteContext, -- ^ Inside quoted environment? - stateAllowLinks :: Bool, -- ^ Allow parsing of links - stateMaxNestingLevel :: Int, -- ^ Max # of nested Strong/Emph - stateLastStrPos :: Maybe SourcePos, -- ^ Position after last str parsed - stateKeys :: KeyTable, -- ^ List of reference keys - stateHeaderKeys :: KeyTable, -- ^ List of implicit header ref keys - stateSubstitutions :: SubstTable, -- ^ List of substitution references - stateNotes :: NoteTable, -- ^ List of notes (raw bodies) - stateNotes' :: NoteTable', -- ^ List of notes (parsed bodies) - stateNoteRefs :: Set.Set String, -- ^ List of note references used - stateMeta :: Meta, -- ^ Document metadata - stateMeta' :: F Meta, -- ^ Document metadata - stateCitations :: M.Map String String, -- ^ RST-style citations - stateHeaderTable :: [HeaderType], -- ^ Ordered list of header types used - stateHeaders :: M.Map Inlines String, -- ^ List of headers and ids (used for implicit ref links) - stateIdentifiers :: Set.Set String, -- ^ Header identifiers used - stateNextExample :: Int, -- ^ Number of next example - stateExamples :: M.Map String Int, -- ^ Map from example labels to numbers - stateMacros :: M.Map Text Macro, -- ^ Table of macros defined so far - stateRstDefaultRole :: String, -- ^ Current rST default interpreted text role - stateRstCustomRoles :: M.Map String (String, Maybe String, Attr), -- ^ Current rST custom text roles + { stateOptions :: ReaderOptions, -- ^ User options + stateParserContext :: ParserContext, -- ^ Inside list? + stateQuoteContext :: QuoteContext, -- ^ Inside quoted environment? + stateAllowLinks :: Bool, -- ^ Allow parsing of links + stateMaxNestingLevel :: Int, -- ^ Max # of nested Strong/Emph + stateLastStrPos :: Maybe SourcePos, -- ^ Position after last str parsed + stateKeys :: KeyTable, -- ^ List of reference keys + stateHeaderKeys :: KeyTable, -- ^ List of implicit header ref keys + stateSubstitutions :: SubstTable, -- ^ List of substitution references + stateNotes :: NoteTable, -- ^ List of notes (raw bodies) + stateNotes' :: NoteTable', -- ^ List of notes (parsed bodies) + stateNoteRefs :: Set.Set String, -- ^ List of note references used + stateMeta :: Meta, -- ^ Document metadata + stateMeta' :: F Meta, -- ^ Document metadata + stateCitations :: M.Map String String, -- ^ RST-style citations + stateHeaderTable :: [HeaderType], -- ^ Ordered list of header types used + stateHeaders :: M.Map Inlines String, -- ^ List of headers and ids (used for implicit ref links) + stateIdentifiers :: Set.Set String, -- ^ Header identifiers used + stateNextExample :: Int, -- ^ Number of next example + stateExamples :: M.Map String Int, -- ^ Map from example labels to numbers + stateMacros :: M.Map Text Macro, -- ^ Table of macros defined so far + stateRstDefaultRole :: String, -- ^ Current rST default interpreted text role + stateRstCustomRoles :: M.Map String (String, Maybe String, Attr), -- ^ Current rST custom text roles -- Triple represents: 1) Base role, 2) Optional format (only for :raw: -- 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 - stateFencedDivLevel :: Int, -- ^ Depth of fenced div - stateContainers :: [String], -- ^ parent include files - stateLogMessages :: [LogMessage], -- ^ log messages + stateCaption :: Maybe Inlines, -- ^ Caption in current environment + stateInHtmlBlock :: Maybe String, -- ^ Tag type of HTML block being parsed + stateFencedDivLevel :: Int, -- ^ Depth of fenced div + stateContainers :: [String], -- ^ parent include files + stateLogMessages :: [LogMessage], -- ^ log messages stateMarkdownAttribute :: Bool -- ^ True if in markdown=1 context } diff --git a/src/Text/Pandoc/Readers/DocBook.hs b/src/Text/Pandoc/Readers/DocBook.hs index 0f3f6f6e3..728f77a05 100644 --- a/src/Text/Pandoc/Readers/DocBook.hs +++ b/src/Text/Pandoc/Readers/DocBook.hs @@ -1,22 +1,22 @@ +{-# LANGUAGE ExplicitForAll #-} module Text.Pandoc.Readers.DocBook ( readDocBook ) where -import Data.Char (toUpper, isSpace) -import Text.Pandoc.Shared (safeRead, crFilter) -import Text.Pandoc.Options -import Text.Pandoc.Definition -import Text.Pandoc.Builder -import Text.XML.Light -import Text.HTML.TagSoup.Entity (lookupEntity) +import Control.Monad.State.Strict +import Data.Char (isSpace, toUpper) +import Data.Default import Data.Either (rights) +import Data.Foldable (asum) import Data.Generics -import Control.Monad.State.Strict import Data.List (intersperse) import Data.Maybe (fromMaybe) -import Text.TeXMath (readMathML, writeTeX) -import Data.Default -import Data.Foldable (asum) -import Text.Pandoc.Class (PandocMonad) import Data.Text (Text) import qualified Data.Text as T +import Text.HTML.TagSoup.Entity (lookupEntity) +import Text.Pandoc.Builder +import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Options +import Text.Pandoc.Shared (crFilter, safeRead) +import Text.TeXMath (readMathML, writeTeX) +import Text.XML.Light {- @@ -538,12 +538,12 @@ handleInstructions ('<':'?':'a':'s':'c':'i':'i':'d':'o':'c':'-':'b':'r':'?':'>': handleInstructions xs = case break (=='<') xs of (ys, []) -> ys ([], '<':zs) -> '<' : handleInstructions zs - (ys, zs) -> ys ++ handleInstructions zs + (ys, zs) -> ys ++ handleInstructions zs getFigure :: PandocMonad m => Element -> DB m Blocks getFigure e = do tit <- case filterChild (named "title") e of - Just t -> getInlines t + Just t -> getInlines t Nothing -> return mempty modify $ \st -> st{ dbFigureTitle = tit } res <- getBlocks e @@ -797,8 +797,8 @@ parseBlock (Elem e) = return $ p <> b <> x codeBlockWithLang = do let classes' = case attrValue "language" e of - "" -> [] - x -> [x] + "" -> [] + x -> [x] return $ codeBlockWith (attrValue "id" e, classes', []) $ trimNl $ strContentRecursive e parseBlockquote = do @@ -871,11 +871,11 @@ parseBlock (Elem e) = || x == '.') w Nothing -> 0 :: Double let numrows = case bodyrows of - [] -> 0 - xs -> maximum $ map length xs + [] -> 0 + xs -> maximum $ map length xs let aligns = case colspecs of - [] -> replicate numrows AlignDefault - cs -> map toAlignment cs + [] -> replicate numrows AlignDefault + cs -> map toAlignment cs let widths = case colspecs of [] -> replicate numrows 0 cs -> let ws = map toWidth cs @@ -895,7 +895,7 @@ parseBlock (Elem e) = headerText <- case filterChild (named "title") e `mplus` (filterChild (named "info") e >>= filterChild (named "title")) of - Just t -> getInlines t + Just t -> getInlines t Nothing -> return mempty modify $ \st -> st{ dbSectionLevel = n } b <- getBlocks e @@ -989,10 +989,10 @@ parseInline (Elem e) = return $ linkWith attr href "" ils' "foreignphrase" -> emph <$> innerInlines "emphasis" -> case attrValue "role" e of - "bold" -> strong <$> innerInlines - "strong" -> strong <$> innerInlines + "bold" -> strong <$> innerInlines + "strong" -> strong <$> innerInlines "strikethrough" -> strikeout <$> innerInlines - _ -> emph <$> innerInlines + _ -> emph <$> innerInlines "footnote" -> (note . mconcat) <$> mapM parseBlock (elContent e) "title" -> return mempty diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index 1874a011a..295b79195 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -214,14 +214,14 @@ codeDivs :: [String] codeDivs = ["SourceCode"] runElemToInlines :: RunElem -> Inlines -runElemToInlines (TextRun s) = text s +runElemToInlines (TextRun s) = text s runElemToInlines LnBrk = linebreak runElemToInlines Tab = space runElemToInlines SoftHyphen = text "\xad" runElemToInlines NoBreakHyphen = text "\x2011" runElemToString :: RunElem -> String -runElemToString (TextRun s) = s +runElemToString (TextRun s) = s runElemToString LnBrk = ['\n'] runElemToString Tab = ['\t'] runElemToString SoftHyphen = ['\xad'] diff --git a/src/Text/Pandoc/Readers/Docx/Lists.hs b/src/Text/Pandoc/Readers/Docx/Lists.hs index 53840c609..70eccd7d6 100644 --- a/src/Text/Pandoc/Readers/Docx/Lists.hs +++ b/src/Text/Pandoc/Readers/Docx/Lists.hs @@ -157,7 +157,7 @@ flatToBullets elems = flatToBullets' (-1) elems singleItemHeaderToHeader :: Block -> Block singleItemHeaderToHeader (OrderedList _ [[h@Header{}]]) = h -singleItemHeaderToHeader blk = blk +singleItemHeaderToHeader blk = blk blocksToBullets :: [Block] -> [Block] diff --git a/src/Text/Pandoc/Readers/EPUB.hs b/src/Text/Pandoc/Readers/EPUB.hs index c1eb6ca59..3b13bbe13 100644 --- a/src/Text/Pandoc/Readers/EPUB.hs +++ b/src/Text/Pandoc/Readers/EPUB.hs @@ -1,7 +1,7 @@ -{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE TupleSections #-} -{-# LANGUAGE ViewPatterns #-} +{-# LANGUAGE TupleSections #-} +{-# LANGUAGE ViewPatterns #-} module Text.Pandoc.Readers.EPUB (readEPUB) diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 277405b09..8d37deb26 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -1,5 +1,8 @@ -{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, -ViewPatterns, OverloadedStrings #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ViewPatterns #-} {- Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> @@ -39,43 +42,42 @@ module Text.Pandoc.Readers.HTML ( readHtml , isCommentTag ) where +import Control.Applicative ((<|>)) +import Control.Arrow ((***)) +import Control.Monad (guard, mplus, msum, mzero, unless, void) +import Control.Monad.Except (throwError) +import Control.Monad.Reader (ReaderT, ask, asks, lift, local, runReaderT) +import Data.Char (isAlphaNum, isDigit, isLetter) +import Data.Default (Default (..), def) +import Data.Foldable (for_) +import Data.List (intercalate, isPrefixOf) +import Data.List.Split (wordsBy) +import qualified Data.Map as M +import Data.Maybe (fromMaybe, isJust, isNothing) +import Data.Monoid (First (..)) +import Data.Monoid ((<>)) +import qualified Data.Set as Set +import Data.Text (Text) +import qualified Data.Text as T +import Network.URI (URI, nonStrictRelativeTo, parseURIReference) import Text.HTML.TagSoup import Text.HTML.TagSoup.Match -import Text.Pandoc.Definition +import Text.Pandoc.Builder (Blocks, HasMeta (..), Inlines, trimInlines) import qualified Text.Pandoc.Builder as B -import Text.Pandoc.Builder (Blocks, Inlines, trimInlines, HasMeta(..)) -import Text.Pandoc.Shared ( extractSpaces, addMetaField - , escapeURI, safeRead, crFilter, underlineSpan ) -import Text.Pandoc.Options ( - ReaderOptions(readerExtensions,readerStripComments), extensionEnabled, - Extension (Ext_epub_html_exts, - Ext_raw_html, Ext_native_divs, Ext_native_spans)) +import Text.Pandoc.Class (PandocMonad (..)) +import Text.Pandoc.CSS (foldOrElse, pickStyleAttrProps) +import Text.Pandoc.Definition +import Text.Pandoc.Error import Text.Pandoc.Logging +import Text.Pandoc.Options (Extension (Ext_epub_html_exts, Ext_native_divs, Ext_native_spans, Ext_raw_html), + ReaderOptions (readerExtensions, readerStripComments), + extensionEnabled) import Text.Pandoc.Parsing hiding ((<|>)) +import Text.Pandoc.Shared (addMetaField, crFilter, escapeURI, extractSpaces, + safeRead, underlineSpan) import Text.Pandoc.Walk -import qualified Data.Map as M -import Data.Foldable ( for_ ) -import Data.Maybe ( fromMaybe, isJust, isNothing ) -import Data.List.Split ( wordsBy ) -import Data.List ( intercalate, isPrefixOf ) -import Data.Char ( isDigit, isLetter, isAlphaNum ) -import Control.Monad ( guard, mzero, void, unless, mplus, msum ) -import Control.Arrow ((***)) -import Control.Applicative ( (<|>) ) -import Data.Monoid (First (..)) -import Data.Text (Text) -import qualified Data.Text as T -import Text.TeXMath (readMathML, writeTeX) -import Data.Default (Default (..), def) -import Control.Monad.Reader (ask, asks, local, ReaderT, runReaderT, lift) -import Network.URI (URI, parseURIReference, nonStrictRelativeTo) -import Text.Pandoc.CSS (foldOrElse, pickStyleAttrProps) -import Data.Monoid ((<>)) import Text.Parsec.Error -import qualified Data.Set as Set -import Text.Pandoc.Error -import Text.Pandoc.Class (PandocMonad(..)) -import Control.Monad.Except (throwError) +import Text.TeXMath (readMathML, writeTeX) -- | Convert HTML-formatted string to 'Pandoc' document. readHtml :: PandocMonad m @@ -123,8 +125,8 @@ data HTMLState = } data HTMLLocal = HTMLLocal { quoteContext :: QuoteContext - , inChapter :: Bool -- ^ Set if in chapter section - , inPlain :: Bool -- ^ Set if in pPlain + , inChapter :: Bool -- ^ Set if in chapter section + , inPlain :: Bool -- ^ Set if in pPlain } setInChapter :: PandocMonad m => HTMLParser m s a -> HTMLParser m s a @@ -354,16 +356,16 @@ fixPlains :: Bool -> Blocks -> Blocks fixPlains inList bs = if any isParaish bs' then B.fromList $ map plainToPara bs' else bs - where isParaish (Para _) = True - isParaish (CodeBlock _ _) = True - isParaish (Header _ _ _) = True - isParaish (BlockQuote _) = True - isParaish (BulletList _) = not inList - isParaish (OrderedList _ _) = not inList + where isParaish (Para _) = True + isParaish (CodeBlock _ _) = True + isParaish (Header _ _ _) = True + isParaish (BlockQuote _) = True + isParaish (BulletList _) = not inList + isParaish (OrderedList _ _) = not inList isParaish (DefinitionList _) = not inList - isParaish _ = False + isParaish _ = False plainToPara (Plain xs) = Para xs - plainToPara x = x + plainToPara x = x bs' = B.toList bs pRawTag :: PandocMonad m => TagParser m Text @@ -377,10 +379,10 @@ pRawTag = do pDiv :: PandocMonad m => TagParser m Blocks pDiv = try $ do guardEnabled Ext_native_divs - let isDivLike "div" = True + let isDivLike "div" = True isDivLike "section" = True - isDivLike "main" = True - isDivLike _ = False + isDivLike "main" = True + isDivLike _ = False TagOpen tag attr' <- lookAhead $ pSatisfy $ tagOpen isDivLike (const True) let attr = toStringAttr attr' contents <- pInTags tag block @@ -545,9 +547,9 @@ pCell celltype = try $ do skipMany pBlank tag <- lookAhead $ pSatisfy (\t -> t ~== TagOpen celltype [] && noColOrRowSpans t) - let extractAlign' [] = "" + let extractAlign' [] = "" extractAlign' ("text-align":x:_) = x - extractAlign' (_:xs) = extractAlign' xs + extractAlign' (_:xs) = extractAlign' xs let extractAlign = extractAlign' . wordsBy (`elem` [' ','\t',';',':']) let align = case maybeFromAttrib "align" tag `mplus` (extractAlign <$> maybeFromAttrib "style" tag) of @@ -603,18 +605,18 @@ pCodeBlock = try $ do let rawText = concatMap tagToString contents -- drop leading newline if any let result' = case rawText of - '\n':xs -> xs - _ -> rawText + '\n':xs -> xs + _ -> rawText -- drop trailing newline if any let result = case reverse result' of - '\n':_ -> init result' - _ -> result' + '\n':_ -> init result' + _ -> result' return $ B.codeBlockWith (mkAttr attr) result tagToString :: Tag Text -> String -tagToString (TagText s) = T.unpack s +tagToString (TagText s) = T.unpack s tagToString (TagOpen "br" _) = "\n" -tagToString _ = "" +tagToString _ = "" inline :: PandocMonad m => TagParser m Inlines inline = choice @@ -893,16 +895,16 @@ pStr = do return $ B.str result isSpecial :: Char -> Bool -isSpecial '"' = True -isSpecial '\'' = True -isSpecial '.' = True -isSpecial '-' = True -isSpecial '$' = True +isSpecial '"' = True +isSpecial '\'' = True +isSpecial '.' = True +isSpecial '-' = True +isSpecial '$' = True isSpecial '\8216' = True isSpecial '\8217' = True isSpecial '\8220' = True isSpecial '\8221' = True -isSpecial _ = False +isSpecial _ = False pSymbol :: PandocMonad m => InlinesParser m Inlines pSymbol = satisfy isSpecial >>= return . B.str . (:[]) @@ -1123,7 +1125,7 @@ htmlInBalanced' tagname ts = fromMaybe [] $ go 0 ts hasTagWarning :: [Tag a] -> Bool hasTagWarning (TagWarning _:_) = True -hasTagWarning _ = False +hasTagWarning _ = False -- | Matches a tag meeting a certain condition. htmlTag :: (HasReaderOptions st, Monad m) @@ -1148,7 +1150,7 @@ htmlTag f = try $ do -- in XML elemnet names let isNameChar c = isAlphaNum c || c == ':' || c == '-' || c == '_' let isName s = case s of - [] -> False + [] -> False (c:cs) -> isLetter c && all isNameChar cs let endAngle = try $ do char '>' diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 3bb4b64e6..6f4244ac3 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleContexts #-} {- Copyright (C) 2017 Alexander Krotov <ilabdsf@gmail.com> @@ -42,23 +42,23 @@ module Text.Pandoc.Readers.Muse (readMuse) where import Control.Monad import Control.Monad.Except (throwError) -import qualified Data.Map as M import Data.Char (isLetter) -import Data.Text (Text, unpack) import Data.List (stripPrefix) +import qualified Data.Map as M import Data.Maybe (fromMaybe) +import Data.Text (Text, unpack) +import System.FilePath (takeExtension) import Text.HTML.TagSoup import Text.Pandoc.Builder (Blocks, Inlines) import qualified Text.Pandoc.Builder as B -import Text.Pandoc.Class (PandocMonad(..)) +import Text.Pandoc.Class (PandocMonad (..)) import Text.Pandoc.Definition import Text.Pandoc.Logging import Text.Pandoc.Options -import Text.Pandoc.Shared (crFilter) import Text.Pandoc.Parsing hiding (nested) import Text.Pandoc.Readers.HTML (htmlTag) +import Text.Pandoc.Shared (crFilter) import Text.Pandoc.XML (fromEntities) -import System.FilePath (takeExtension) -- | Read Muse from an input string and return a Pandoc document. readMuse :: PandocMonad m @@ -233,7 +233,7 @@ exampleTag = do return $ return $ B.codeBlockWith attr $ chop contents where lchop s = case s of '\n':ss -> ss - _ -> s + _ -> s rchop = reverse . lchop . reverse -- Trim up to one newline from the beginning and the end, -- in case opening and/or closing tags are on separate lines. @@ -315,7 +315,7 @@ noteBlock = try $ do content <- mconcat <$> blocksTillNote oldnotes <- stateNotes' <$> getState case M.lookup ref oldnotes of - Just _ -> logMessage $ DuplicateNoteReference ref pos + Just _ -> logMessage $ DuplicateNoteReference ref pos Nothing -> return () updateState $ \s -> s{ stateNotes' = M.insert ref (pos, content) oldnotes } return mempty @@ -445,7 +445,7 @@ definitionList = do data MuseTable = MuseTable { museTableCaption :: Inlines , museTableHeaders :: [[Blocks]] - , museTableRows :: [[Blocks]] + , museTableRows :: [[Blocks]] , museTableFooters :: [[Blocks]] } diff --git a/src/Text/Pandoc/Readers/Odt/ContentReader.hs b/src/Text/Pandoc/Readers/Odt/ContentReader.hs index 69eececc8..44bd89278 100644 --- a/src/Text/Pandoc/Readers/Odt/ContentReader.hs +++ b/src/Text/Pandoc/Readers/Odt/ContentReader.hs @@ -49,7 +49,6 @@ import Data.Maybe import qualified Text.XML.Light as XML -import Text.Pandoc.Definition import Text.Pandoc.Builder import Text.Pandoc.MediaBag (MediaBag, insertMedia) import Text.Pandoc.Shared diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 070a05df1..de488adfe 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -36,16 +36,15 @@ import Control.Monad (forM_, guard, liftM, mplus, mzero, when) import Control.Monad.Except (throwError) import Control.Monad.Identity (Identity (..)) import Data.Char (isHexDigit, isSpace, toLower, toUpper) -import Data.List (deleteFirstsBy, intercalate, isInfixOf, - elemIndex, isSuffixOf, nub, sort, transpose, union) +import Data.List (deleteFirstsBy, elemIndex, intercalate, isInfixOf, isSuffixOf, + nub, sort, transpose, union) import qualified Data.Map as M import Data.Maybe (fromMaybe, isJust) import Data.Monoid ((<>)) import Data.Sequence (ViewR (..), viewr) import Data.Text (Text) import qualified Data.Text as T -import Text.Pandoc.Builder - (fromList, setMeta, Blocks, Inlines, trimInlines) +import Text.Pandoc.Builder (Blocks, Inlines, fromList, setMeta, trimInlines) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad, fetchItem, readFileFromDirs) import Text.Pandoc.CSV (CSVOptions (..), defaultCSVOptions, parseCSV) @@ -315,7 +314,7 @@ doubleHeader = do let headerTable = stateHeaderTable state let (headerTable',level) = case elemIndex (DoubleHeader c) headerTable of Just ind -> (headerTable, ind + 1) - Nothing -> (headerTable ++ [DoubleHeader c], length headerTable + 1) + Nothing -> (headerTable ++ [DoubleHeader c], length headerTable + 1) setState (state { stateHeaderTable = headerTable' }) attr <- registerHeader nullAttr txt return $ B.headerWith attr level txt @@ -344,7 +343,7 @@ singleHeader = do let headerTable = stateHeaderTable state let (headerTable',level) = case elemIndex (SingleHeader c) headerTable of Just ind -> (headerTable, ind + 1) - Nothing -> (headerTable ++ [SingleHeader c], length headerTable + 1) + Nothing -> (headerTable ++ [SingleHeader c], length headerTable + 1) setState (state { stateHeaderTable = headerTable' }) attr <- registerHeader nullAttr txt return $ B.headerWith attr level txt diff --git a/src/Text/Pandoc/Readers/Vimwiki.hs b/src/Text/Pandoc/Readers/Vimwiki.hs index 49da5a6c6..fecbb2fb4 100644 --- a/src/Text/Pandoc/Readers/Vimwiki.hs +++ b/src/Text/Pandoc/Readers/Vimwiki.hs @@ -91,12 +91,10 @@ import Text.Pandoc.Parsing (F, ParserState, ParserT, blanklines, emailAddress, registerHeader, runF, spaceChar, stateMeta', stateOptions, uri) import Text.Pandoc.Shared (crFilter, splitBy, stringify, stripFirstAndLast) -import Text.Parsec.Char - (alphaNum, anyChar, char, newline, noneOf, spaces, string, oneOf, - space) -import Text.Parsec.Combinator - (choice, count, eof, many1, manyTill, notFollowedBy, option, - skipMany1, between, lookAhead) +import Text.Parsec.Char (alphaNum, anyChar, char, newline, noneOf, oneOf, space, + spaces, string) +import Text.Parsec.Combinator (between, choice, count, eof, lookAhead, many1, + manyTill, notFollowedBy, option, skipMany1) import Text.Parsec.Prim (getState, many, try, updateState, (<|>)) readVimwiki :: PandocMonad m => ReaderOptions -> Text -> m Pandoc diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 9d4877c24..60c8e1a0c 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -520,7 +520,7 @@ uniqueIdent title' usedIdents -- | True if block is a Header block. isHeaderBlock :: Block -> Bool isHeaderBlock (Header{}) = True -isHeaderBlock _ = False +isHeaderBlock _ = False -- | Shift header levels up or down. headerShift :: Int -> Pandoc -> Pandoc diff --git a/src/Text/Pandoc/Slides.hs b/src/Text/Pandoc/Slides.hs index d83735029..27e7d3d76 100644 --- a/src/Text/Pandoc/Slides.hs +++ b/src/Text/Pandoc/Slides.hs @@ -42,7 +42,7 @@ getSlideLevel = go 6 go least [] = least nonHOrHR Header{} = False nonHOrHR HorizontalRule = False - nonHOrHR _ = True + nonHOrHR _ = True -- | Prepare a block list to be passed to hierarchicalize. prepSlides :: Int -> [Block] -> [Block] diff --git a/src/Text/Pandoc/Templates.hs b/src/Text/Pandoc/Templates.hs index 89d524d96..1ba8d5a05 100644 --- a/src/Text/Pandoc/Templates.hs +++ b/src/Text/Pandoc/Templates.hs @@ -1,7 +1,7 @@ -{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TypeSynonymInstances #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeSynonymInstances #-} {- Copyright (C) 2009-2017 John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/Docbook.hs b/src/Text/Pandoc/Writers/Docbook.hs index 24df7e2b4..74a1249a4 100644 --- a/src/Text/Pandoc/Writers/Docbook.hs +++ b/src/Text/Pandoc/Writers/Docbook.hs @@ -76,9 +76,9 @@ authorToDocbook opts name' = do let namewords = words name lengthname = length namewords (firstname, lastname) = case lengthname of - 0 -> ("","") - 1 -> ("", name) - n -> (unwords (take (n-1) namewords), last namewords) + 0 -> ("","") + 1 -> ("", name) + n -> (unwords (take (n-1) namewords), last namewords) in inTagsSimple "firstname" (text $ escapeStringForXML firstname) $$ inTagsSimple "surname" (text $ escapeStringForXML lastname) diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index 6bfd78d3c..94eea3a45 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -43,7 +43,7 @@ import qualified Data.ByteString.Lazy.Char8 as B8 import Data.Char (isAlphaNum, isAscii, isDigit, toLower) import Data.List (intercalate, isInfixOf, isPrefixOf) import qualified Data.Map as M -import Data.Maybe (fromMaybe, mapMaybe, isNothing) +import Data.Maybe (fromMaybe, isNothing, mapMaybe) import qualified Data.Set as Set import qualified Data.Text as TS import qualified Data.Text.Lazy as TL diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index ddbd9e972..ffcde3ce7 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -421,7 +421,7 @@ elementToHtml slideLevel opts (Sec level num (id',classes,keyvals) title' elemen return res let isSec (Sec{}) = True - isSec (Blk _) = False + isSec (Blk _) = False let isPause (Blk x) = x == Para [Str ".",Space,Str ".",Space,Str "."] isPause _ = False let fragmentClass = case slideVariant of @@ -1174,7 +1174,7 @@ isMathEnvironment s = "\\begin{" `isPrefixOf` s && allowsMathEnvironments :: HTMLMathMethod -> Bool allowsMathEnvironments (MathJax _) = True -allowsMathEnvironments MathML = True +allowsMathEnvironments MathML = True allowsMathEnvironments (WebTeX _) = True allowsMathEnvironments _ = False diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs index a62286fa3..2aac777c6 100644 --- a/src/Text/Pandoc/Writers/JATS.hs +++ b/src/Text/Pandoc/Writers/JATS.hs @@ -76,9 +76,9 @@ authorToJATS opts name' = do let namewords = words name lengthname = length namewords (firstname, lastname) = case lengthname of - 0 -> ("","") - 1 -> ("", name) - n -> (unwords (take (n-1) namewords), last namewords) + 0 -> ("","") + 1 -> ("", name) + n -> (unwords (take (n-1) namewords), last namewords) in inTagsSimple "firstname" (text $ escapeStringForXML firstname) $$ inTagsSimple "surname" (text $ escapeStringForXML lastname) diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index e667984ef..ab1e90b3b 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -135,7 +135,7 @@ pandocToLaTeX options (Pandoc meta blocks) = do let blocks' = if method == Biblatex || method == Natbib then case reverse blocks of Div (_,["references"],_) _:xs -> reverse xs - _ -> blocks + _ -> blocks else blocks -- see if there are internal links let isInternalLink (Link _ _ ('#':xs,_)) = [xs] @@ -848,7 +848,7 @@ sectionHeader unnumbered ident level lst = do plain <- stringToLaTeX TextString $ concatMap stringify lst let removeInvalidInline (Note _) = [] removeInvalidInline (Span (id', _, _) _) | not (null id') = [] - removeInvalidInline (Image{}) = [] + removeInvalidInline (Image{}) = [] removeInvalidInline x = [x] let lstNoNotes = foldr (mappend . (\x -> walkM removeInvalidInline x)) mempty lst txtNoNotes <- inlineListToLaTeX lstNoNotes diff --git a/src/Text/Pandoc/Writers/Man.hs b/src/Text/Pandoc/Writers/Man.hs index cd7a98d43..ad3de41eb 100644 --- a/src/Text/Pandoc/Writers/Man.hs +++ b/src/Text/Pandoc/Writers/Man.hs @@ -156,7 +156,7 @@ breakSentence [] = ([],[]) breakSentence xs = let isSentenceEndInline (Str ys@(_:_)) | last ys == '.' = True isSentenceEndInline (Str ys@(_:_)) | last ys == '?' = True - isSentenceEndInline LineBreak = True + isSentenceEndInline LineBreak = True isSentenceEndInline _ = False (as, bs) = break isSentenceEndInline xs in case bs of diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 9e3036753..223d1bcc1 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -191,7 +191,7 @@ breakSentence [] = ([],[]) breakSentence xs = let isSentenceEndInline (Str ys@(_:_)) | last ys == '.' = True isSentenceEndInline (Str ys@(_:_)) | last ys == '?' = True - isSentenceEndInline LineBreak = True + isSentenceEndInline LineBreak = True isSentenceEndInline _ = False (as, bs) = break isSentenceEndInline xs in case bs of @@ -408,8 +408,8 @@ definitionListItemToMs opts (label, defs) = do else liftM vcat $ forM defs $ \blocks -> do let (first, rest) = case blocks of (Para x:y) -> (Plain x,y) - (x:y) -> (x,y) - [] -> (Plain [], []) + (x:y) -> (x,y) + [] -> (Plain [], []) -- should not happen rest' <- liftM vcat $ mapM (\item -> blockToMs opts item) rest diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index 6c6010880..aab8a3bf0 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -344,7 +344,7 @@ definitionListItemToRST (label, defs) = do linesToLineBlock :: PandocMonad m => [[Inline]] -> RST m Doc linesToLineBlock inlineLines = do lns <- mapM inlineListToRST inlineLines - return $ + return $ vcat (map (hang 2 (text "| ")) lns) <> blankline -- | Convert list of Pandoc block elements to RST. @@ -437,8 +437,8 @@ inlineListToRST lst = isComplex (Strikeout _) = True isComplex (Superscript _) = True isComplex (Subscript _) = True - isComplex (Link{}) = True - isComplex (Image{}) = True + isComplex (Link{}) = True + isComplex (Image{}) = True isComplex (Code _ _) = True isComplex (Math _ _) = True isComplex (Cite _ (x:_)) = isComplex x diff --git a/src/Text/Pandoc/Writers/Texinfo.hs b/src/Text/Pandoc/Writers/Texinfo.hs index 2d0c7a86d..15dd2e3d9 100644 --- a/src/Text/Pandoc/Writers/Texinfo.hs +++ b/src/Text/Pandoc/Writers/Texinfo.hs @@ -340,7 +340,7 @@ blockListToTexinfo (x:xs) = do xs' <- blockListToTexinfo xs case xs of (CodeBlock _ _:_) -> return $ x' $$ xs' - _ -> return $ x' $+$ xs' + _ -> return $ x' $+$ xs' _ -> do xs' <- blockListToTexinfo xs return $ x' $$ xs' diff --git a/src/Text/Pandoc/Writers/ZimWiki.hs b/src/Text/Pandoc/Writers/ZimWiki.hs index 60029c0d4..29849aa51 100644 --- a/src/Text/Pandoc/Writers/ZimWiki.hs +++ b/src/Text/Pandoc/Writers/ZimWiki.hs @@ -36,8 +36,8 @@ import Control.Monad (zipWithM) import Control.Monad.State.Strict (StateT, evalStateT, gets, modify) import Data.Default (Default (..)) import Data.List (intercalate, isInfixOf, isPrefixOf, transpose) -import Data.Maybe (fromMaybe) import qualified Data.Map as Map +import Data.Maybe (fromMaybe) import Data.Text (Text, breakOnAll, pack) import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Definition -- cgit v1.2.3 From 95ccbdaac20703e4eb447a88fc4397298ff005e3 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 29 Oct 2017 14:54:29 -0700 Subject: Removed useless notes state in DokuWiki writer. --- src/Text/Pandoc/Writers/DokuWiki.hs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/DokuWiki.hs b/src/Text/Pandoc/Writers/DokuWiki.hs index 09dd846ba..e52cc75ad 100644 --- a/src/Text/Pandoc/Writers/DokuWiki.hs +++ b/src/Text/Pandoc/Writers/DokuWiki.hs @@ -41,7 +41,7 @@ DokuWiki: <https://www.dokuwiki.org/dokuwiki> module Text.Pandoc.Writers.DokuWiki ( writeDokuWiki ) where import Control.Monad (zipWithM) import Control.Monad.Reader (ReaderT, ask, local, runReaderT) -import Control.Monad.State.Strict (StateT, evalStateT, gets, modify) +import Control.Monad.State.Strict (StateT, evalStateT) import Data.Default (Default (..)) import Data.List (intercalate, intersect, isPrefixOf, transpose) import Data.Text (Text, pack) @@ -56,7 +56,6 @@ import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Writers.Shared (defField, metaToJSON) data WriterState = WriterState { - stNotes :: Bool -- True if there are notes } data WriterEnvironment = WriterEnvironment { @@ -66,7 +65,7 @@ data WriterEnvironment = WriterEnvironment { } instance Default WriterState where - def = WriterState { stNotes = False } + def = WriterState {} instance Default WriterEnvironment where def = WriterEnvironment { stIndent = "" @@ -92,7 +91,6 @@ pandocToDokuWiki opts (Pandoc meta blocks) = do (inlineListToDokuWiki opts) meta body <- blockListToDokuWiki opts blocks - notesExist <- gets stNotes let main = pack body let context = defField "body" main $ defField "toc" (writerTableOfContents opts) metadata @@ -514,7 +512,6 @@ inlineToDokuWiki opts (Image attr alt (source, tit)) = do inlineToDokuWiki opts (Note contents) = do contents' <- blockListToDokuWiki opts contents - modify (\s -> s { stNotes = True }) return $ "((" ++ contents' ++ "))" -- note - may not work for notes with multiple blocks -- cgit v1.2.3 From 6a1476e7e26b603bb69112a2666c47ac883ee36f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 29 Oct 2017 15:00:49 -0700 Subject: Export all of Text.Pandoc.Class from Text.Pandoc. --- src/Text/Pandoc.hs | 6 +----- src/Text/Pandoc/Class.hs | 1 - 2 files changed, 1 insertion(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index 9fa5f098d..0da2a925c 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -65,11 +65,7 @@ module Text.Pandoc -- * Logging , module Text.Pandoc.Logging -- * Typeclass - , PandocMonad - , runIO - , runPure - , runIOorExplode - , setVerbosity + , module Text.Pandoc.Class -- * Error handling , module Text.Pandoc.Error -- * Readers: converting /to/ Pandoc format diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 2b8b1c090..19897e53f 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -93,7 +93,6 @@ module Text.Pandoc.Class ( PandocMonad(..) , setTranslations , translateTerm , Translations - , Term(..) ) where import Prelude hiding (readFile) -- cgit v1.2.3 From 1e1a7a9b83f13cfcbef5c4239e43f3724260cef4 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 29 Oct 2017 15:19:49 -0700 Subject: Fixed warnings. --- src/Text/Pandoc/App.hs | 5 ----- 1 file changed, 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index f0e953d53..3cf6f0788 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -77,11 +77,6 @@ import System.IO.Error (isDoesNotExistError) import Text.Pandoc import Text.Pandoc.BCP47 (Lang (..), parseBCP47) import Text.Pandoc.Builder (setMeta) -import Text.Pandoc.Class (PandocIO, extractMedia, fillMediaBag, getLog, openURL, - readDataFile, readDefaultDataFile, readFileStrict, - report, setInputFiles, setOutputFile, - setRequestHeader, setResourcePath, setTrace, - setTranslations, setUserDataDir) import Text.Pandoc.Highlighting (highlightingStyles) import Text.Pandoc.Lua (LuaException (..), runLuaFilter) import Text.Pandoc.PDF (makePDF) -- cgit v1.2.3 From 601a28fd3610f74a9353450bf3031eba4d94e73f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 30 Oct 2017 10:59:52 -0700 Subject: Allow body of macro definition to be unbraced. e.g. \newcommand\arrow\to See #4007. --- src/Text/Pandoc/Readers/LaTeX.hs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index d6a3de2f1..0664a94aa 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1824,7 +1824,7 @@ letmacro = do Tok _ (CtrlSeq name) _ <- anyControlSeq optional $ symbol '=' spaces - contents <- braced <|> ((:[]) <$> (anyControlSeq <|> singleChar)) + contents <- macroContents return (name, Macro ExpandWhenDefined 0 Nothing contents) defmacro :: PandocMonad m => LP m (Text, Macro) @@ -1832,7 +1832,9 @@ defmacro = try $ do controlSeq "def" Tok _ (CtrlSeq name) _ <- anyControlSeq numargs <- option 0 $ argSeq 1 - contents <- withVerbatimMode braced + -- we use withVerbatimMode, because macros are to be expanded + -- at point of use, not point of definition + contents <- withVerbatimMode macroContents return (name, Macro ExpandWhenUsed numargs Nothing contents) -- Note: we don't yet support fancy things like #1.#2 @@ -1846,6 +1848,9 @@ isArgTok :: Tok -> Bool isArgTok (Tok _ (Arg _) _) = True isArgTok _ = False +macroContents :: PandocMonad m => LP m [Tok] +macroContents = braced <|> ((:[]) <$> (anyControlSeq <|> singleChar)) + newcommand :: PandocMonad m => LP m (Text, Macro) newcommand = do pos <- getPosition @@ -1861,9 +1866,7 @@ newcommand = do spaces optarg <- option Nothing $ Just <$> try bracketedToks spaces - contents <- withVerbatimMode braced - -- we use withVerbatimMode, because macros are to be expanded - -- at point of use, not point of definition + contents <- withVerbatimMode macroContents when (mtype == "newcommand") $ do macros <- sMacros <$> getState case M.lookup name macros of @@ -1885,9 +1888,9 @@ newenvironment = do spaces optarg <- option Nothing $ Just <$> try bracketedToks spaces - startcontents <- withVerbatimMode braced + startcontents <- withVerbatimMode macroContents spaces - endcontents <- withVerbatimMode braced + endcontents <- withVerbatimMode macroContents when (mtype == "newenvironment") $ do macros <- sMacros <$> getState case M.lookup name macros of -- cgit v1.2.3 From 272b833ad55c6705e44703d1d38b07548f017ecf Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 30 Oct 2017 11:35:40 -0700 Subject: Allow unbraced arguments for macros. See #4007. --- src/Text/Pandoc/Readers/LaTeX.hs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 0664a94aa..407952a54 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -430,7 +430,7 @@ doMacros n = do Nothing -> return () Just (Macro expansionPoint numargs optarg newtoks) -> do setInput ts - let getarg = spaces >> braced + let getarg = try $ spaces >> bracedOrToken args <- case optarg of Nothing -> count numargs getarg Just o -> @@ -1824,7 +1824,7 @@ letmacro = do Tok _ (CtrlSeq name) _ <- anyControlSeq optional $ symbol '=' spaces - contents <- macroContents + contents <- bracedOrToken return (name, Macro ExpandWhenDefined 0 Nothing contents) defmacro :: PandocMonad m => LP m (Text, Macro) @@ -1834,7 +1834,7 @@ defmacro = try $ do numargs <- option 0 $ argSeq 1 -- we use withVerbatimMode, because macros are to be expanded -- at point of use, not point of definition - contents <- withVerbatimMode macroContents + contents <- withVerbatimMode bracedOrToken return (name, Macro ExpandWhenUsed numargs Nothing contents) -- Note: we don't yet support fancy things like #1.#2 @@ -1848,8 +1848,8 @@ isArgTok :: Tok -> Bool isArgTok (Tok _ (Arg _) _) = True isArgTok _ = False -macroContents :: PandocMonad m => LP m [Tok] -macroContents = braced <|> ((:[]) <$> (anyControlSeq <|> singleChar)) +bracedOrToken :: PandocMonad m => LP m [Tok] +bracedOrToken = braced <|> ((:[]) <$> (anyControlSeq <|> singleChar)) newcommand :: PandocMonad m => LP m (Text, Macro) newcommand = do @@ -1866,7 +1866,7 @@ newcommand = do spaces optarg <- option Nothing $ Just <$> try bracketedToks spaces - contents <- withVerbatimMode macroContents + contents <- withVerbatimMode bracedOrToken when (mtype == "newcommand") $ do macros <- sMacros <$> getState case M.lookup name macros of @@ -1888,9 +1888,9 @@ newenvironment = do spaces optarg <- option Nothing $ Just <$> try bracketedToks spaces - startcontents <- withVerbatimMode macroContents + startcontents <- withVerbatimMode bracedOrToken spaces - endcontents <- withVerbatimMode macroContents + endcontents <- withVerbatimMode bracedOrToken when (mtype == "newenvironment") $ do macros <- sMacros <$> getState case M.lookup name macros of -- cgit v1.2.3 From 90597fe292a84959d289fe17eae226c7e8bf23c8 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 30 Oct 2017 11:51:49 -0700 Subject: LaTeX reader: insert space when needed in macro expansion. Sometimes we need to insert a space after a control sequence to prevent it merging with a following letter. Closes #4007. --- src/Text/Pandoc/Readers/LaTeX.hs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 407952a54..a982029af 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -438,7 +438,14 @@ doMacros n = do <*> count (numargs - 1) getarg let addTok (Tok _ (Arg i) _) acc | i > 0 , i <= numargs = - map (setpos spos) (args !! (i - 1)) ++ acc + foldr addTok acc (args !! (i - 1)) + -- add space if needed after control sequence + -- see #4007 + addTok (Tok _ (CtrlSeq x) txt) + acc@(Tok _ Word _ : _) + | not (T.null txt) && + (isLetter (T.last txt)) = + Tok spos (CtrlSeq x) (txt <> " ") : acc addTok t acc = setpos spos t : acc ts' <- getInput setInput $ foldr addTok ts' newtoks -- cgit v1.2.3 From 599d4aa03239f6094ee56fedd2a466983c68f434 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 30 Oct 2017 17:24:27 -0700 Subject: EPUB writer fixes: - Ensure that epub2 is recognized as a non-text format, so that a template is used. - Don't include "prefix" attribute for ibooks for epub2. It doesn't validate. - Fix stylesheet paths; previously we had an incorrect stylesheet path for the cover page and nav page. --- src/Text/Pandoc/App.hs | 2 +- src/Text/Pandoc/Writers/EPUB.hs | 55 ++++++++++++++++++++++++----------------- 2 files changed, 33 insertions(+), 24 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 3cf6f0788..e82ccf3f0 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -535,7 +535,7 @@ convertWithOpts opts = do type Transform = Pandoc -> Pandoc isTextFormat :: String -> Bool -isTextFormat s = s `notElem` ["odt","docx","epub","epub3"] +isTextFormat s = s `notElem` ["odt","docx","epub2","epub3","epub"] externalFilter :: MonadIO m => ReaderOptions -> FilePath -> [String] -> Pandoc -> m Pandoc diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index 94eea3a45..1ba0016a2 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -43,7 +43,7 @@ import qualified Data.ByteString.Lazy.Char8 as B8 import Data.Char (isAlphaNum, isAscii, isDigit, toLower) import Data.List (intercalate, isInfixOf, isPrefixOf) import qualified Data.Map as M -import Data.Maybe (fromMaybe, isNothing, mapMaybe) +import Data.Maybe (fromMaybe, isNothing, mapMaybe, isJust) import qualified Data.Set as Set import qualified Data.Text as TS import qualified Data.Text.Lazy as TL @@ -399,8 +399,11 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do stylesheets [(1 :: Int)..] let vars = ("epub3", if epub3 then "true" else "false") - : map (\e -> ("css", "../" ++ eRelativePath e)) stylesheetEntries - ++ [(x,y) | (x,y) <- writerVariables opts, x /= "css"] + : [(x,y) | (x,y) <- writerVariables opts, x /= "css"] + + let cssvars pref = map (\e -> ("css", pref ++ eRelativePath e)) + stylesheetEntries + let opts' = opts{ writerEmailObfuscation = NoObfuscation , writerSectionDivs = True , writerVariables = vars @@ -417,7 +420,9 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do Just img -> do let coverImage = "media/" ++ takeFileName img cpContent <- lift $ writeHtml - opts'{ writerVariables = ("coverpage","true"):vars } + opts'{ writerVariables = + ("coverpage","true"): + cssvars "" ++ vars } (Pandoc meta [RawBlock (Format "html") $ "<div id=\"cover-image\">\n<img src=\"" ++ coverImage ++ "\" alt=\"cover image\" />\n</div>"]) imgContent <- lift $ P.readFileLazy img return ( [mkEntry "cover.xhtml" cpContent] @@ -425,7 +430,8 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do -- title page tpContent <- lift $ writeHtml opts'{ - writerVariables = ("titlepage","true"):vars } + writerVariables = ("titlepage","true"): + cssvars "../" ++ vars } (Pandoc meta []) let tpEntry = mkEntry "text/title_page.xhtml" tpContent @@ -527,13 +533,14 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do let chapToEntry num (Chapter mbnum bs) = mkEntry ("text/" ++ showChapter num) <$> - 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) + writeHtml opts'{ writerNumberOffset = fromMaybe [] mbnum + , writerVariables = cssvars "../" ++ vars } + (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 @@ -579,12 +586,14 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do [] -> throwError $ PandocShouldNeverHappenError "epubIdentifier is null" -- shouldn't happen currentTime <- lift P.getCurrentTime let contentsData = UTF8.fromStringLazy $ ppTopElement $ - unode "package" ! [("version", case version of - EPUB2 -> "2.0" - EPUB3 -> "3.0") - ,("xmlns","http://www.idpf.org/2007/opf") - ,("unique-identifier","epub-id-1") - ,("prefix","ibooks: http://vocabulary.itunes.apple.com/rdf/ibooks/vocabulary-extensions-1.0/")] $ + unode "package" ! + ([("version", case version of + EPUB2 -> "2.0" + EPUB3 -> "3.0") + ,("xmlns","http://www.idpf.org/2007/opf") + ,("unique-identifier","epub-id-1") + ] ++ + [("prefix","ibooks: http://vocabulary.itunes.apple.com/rdf/ibooks/vocabulary-extensions-1.0/") | version == EPUB3]) $ [ metadataElement version metadata currentTime , unode "manifest" $ [ unode "item" ! [("id","ncx"), ("href","toc.ncx") @@ -625,7 +634,10 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do ("href","nav.xhtml")] $ () ] ++ [ unode "reference" ! - [("type","cover"),("title","Cover"),("href","cover.xhtml")] $ () | epubCoverImage metadata /= Nothing + [("type","cover") + ,("title","Cover") + ,("href","cover.xhtml")] $ () + | isJust (epubCoverImage metadata) ] ] let contentsEntry = mkEntry "content.opf" contentsData @@ -741,10 +753,7 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do ] else [] navData <- lift $ writeHtml opts'{ writerVariables = ("navpage","true"): - -- remove the leading ../ from stylesheet paths: - map (\(k,v) -> if k == "css" - then (k, drop 3 v) - else (k, v)) vars } + cssvars "" ++ vars } (Pandoc (setMeta "title" (walk removeNote $ fromList $ docTitle' meta) nullMeta) (navBlocks ++ landmarks)) -- cgit v1.2.3 From c0e51c571032bc0475f6c8b25641515cf37b2ff3 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 30 Oct 2017 17:43:08 -0700 Subject: EPUB writer: fixed filepaths for nonstandard epub-subdirectory values. --- src/Text/Pandoc/Writers/EPUB.hs | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index 1ba0016a2..1129ac3f4 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -382,6 +382,10 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do -- sanity check on epubSubdir unless (all (\c -> isAscii c && isAlphaNum c) epubSubdir) $ throwError $ PandocEpubSubdirectoryError epubSubdir + let inSubdir f = if null epubSubdir + then f + else epubSubdir ++ "/" ++ f + let epub3 = version == EPUB3 let writeHtml o = fmap (UTF8.fromTextLazy . TL.fromStrict) . writeHtmlStringForEPUB version o @@ -401,8 +405,12 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do let vars = ("epub3", if epub3 then "true" else "false") : [(x,y) | (x,y) <- writerVariables opts, x /= "css"] - let cssvars pref = map (\e -> ("css", pref ++ eRelativePath e)) - stylesheetEntries + let cssvars useprefix = map (\e -> ("css", + (if useprefix && not (null epubSubdir) + then "../" + else "") + ++ eRelativePath e)) + stylesheetEntries let opts' = opts{ writerEmailObfuscation = NoObfuscation , writerSectionDivs = True @@ -422,7 +430,7 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do cpContent <- lift $ writeHtml opts'{ writerVariables = ("coverpage","true"): - cssvars "" ++ vars } + cssvars False ++ vars } (Pandoc meta [RawBlock (Format "html") $ "<div id=\"cover-image\">\n<img src=\"" ++ coverImage ++ "\" alt=\"cover image\" />\n</div>"]) imgContent <- lift $ P.readFileLazy img return ( [mkEntry "cover.xhtml" cpContent] @@ -431,9 +439,9 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do -- title page tpContent <- lift $ writeHtml opts'{ writerVariables = ("titlepage","true"): - cssvars "../" ++ vars } + cssvars True ++ vars } (Pandoc meta []) - let tpEntry = mkEntry "text/title_page.xhtml" tpContent + let tpEntry = mkEntry (inSubdir "title_page.xhtml") tpContent -- handle pictures -- mediaRef <- P.newIORef [] @@ -532,9 +540,9 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do chapters' let chapToEntry num (Chapter mbnum bs) = - mkEntry ("text/" ++ showChapter num) <$> + mkEntry (inSubdir (showChapter num)) <$> writeHtml opts'{ writerNumberOffset = fromMaybe [] mbnum - , writerVariables = cssvars "../" ++ vars } + , writerVariables = cssvars True ++ vars } (case bs of (Header _ _ xs : _) -> -- remove notes or we get doubled footnotes @@ -673,12 +681,13 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do navMapFormatter n tit src subs = unode "navPoint" ! [("id", "navPoint-" ++ show n)] $ [ unode "navLabel" $ unode "text" $ stringify tit - , unode "content" ! [("src", "text/" ++ src)] $ () + , unode "content" ! [("src", inSubdir src)] $ () ] ++ subs let tpNode = unode "navPoint" ! [("id", "navPoint-0")] $ [ unode "navLabel" $ unode "text" (stringify $ docTitle' meta) - , unode "content" ! [("src","text/title_page.xhtml")] $ () ] + , unode "content" ! [("src", inSubdir "title_page.xhtml")] + $ () ] navMap <- lift $ evalStateT (mapM (navPointNode navMapFormatter) secs) 1 let tocData = UTF8.fromStringLazy $ ppTopElement $ @@ -706,8 +715,8 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do let navXhtmlFormatter :: Int -> [Inline] -> String -> [Element] -> Element navXhtmlFormatter n tit src subs = unode "li" ! [("id", "toc-li-" ++ show n)] $ - (unode "a" ! [("href", "text/" ++ - src)] + (unode "a" ! + [("href", inSubdir src)] $ titElements) : case subs of [] -> [] @@ -753,7 +762,7 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do ] else [] navData <- lift $ writeHtml opts'{ writerVariables = ("navpage","true"): - cssvars "" ++ vars } + cssvars False ++ vars } (Pandoc (setMeta "title" (walk removeNote $ fromList $ docTitle' meta) nullMeta) (navBlocks ++ landmarks)) @@ -767,8 +776,7 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do unode "container" ! [("version","1.0") ,("xmlns","urn:oasis:names:tc:opendocument:xmlns:container")] $ unode "rootfiles" $ - unode "rootfile" ! [("full-path", - epubSubdir ++ ['/' | not (null epubSubdir)] ++ "content.opf") + unode "rootfile" ! [("full-path", inSubdir "content.opf") ,("media-type","application/oebps-package+xml")] $ () let containerEntry = mkEntry "META-INF/container.xml" containerData @@ -780,8 +788,7 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do let appleEntry = mkEntry "META-INF/com.apple.ibooks.display-options.xml" apple let addEpubSubdir :: Entry -> Entry - addEpubSubdir e = e{ eRelativePath = - epubSubdir ++ ['/' | not (null epubSubdir)] ++ eRelativePath e } + addEpubSubdir e = e{ eRelativePath = inSubdir (eRelativePath e) } -- construct archive let archive = foldr addEntryToArchive emptyArchive $ [mimetypeEntry, containerEntry, appleEntry] ++ -- cgit v1.2.3 From 94d02a6efa29ce24ddf8ebbccac29d5acabbd84f Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Tue, 31 Oct 2017 11:33:55 +0300 Subject: FB2 writer: make bullet lists consistent with ordered lists Previously bullet lists interacted in odd way with ordered lists. For example, bullet lists nested in ordered list had incorrect indentation. Besides that, indentation with spaces is not rendered by FBReader and fbless. To avoid this problem, bullet lists are indented by appending bullets to marker just the same way it is done for ordered lists. --- src/Text/Pandoc/Writers/FB2.hs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index cf96393ca..46913e605 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -64,7 +64,6 @@ data FbRenderState = FbRenderState { footnotes :: [ (Int, String, [Content]) ] -- ^ #, ID, text , imagesToFetch :: [ (String, String) ] -- ^ filename, URL or path , parentListMarker :: String -- ^ list marker of the parent ordered list - , parentBulletLevel :: Int -- ^ nesting level of the unordered list , writerOptions :: WriterOptions } deriving (Show) @@ -73,7 +72,7 @@ type FBM m = StateT FbRenderState m newFB :: FbRenderState newFB = FbRenderState { footnotes = [], imagesToFetch = [] - , parentListMarker = "", parentBulletLevel = 0 + , parentListMarker = "" , writerOptions = def } data ImageMode = NormalImage | InlineImage deriving (Eq) @@ -347,15 +346,12 @@ blockToXml (OrderedList a bss) = do concat <$> zipWithM mkitem markers bss blockToXml (BulletList bss) = do state <- get - let level = parentBulletLevel state let pmrk = parentListMarker state - let prefix = replicate (length pmrk) ' ' - let bullets = ["\x2022", "\x25e6", "*", "\x2043", "\x2023"] - let mrk = prefix ++ bullets !! (level `mod` length bullets) + let mrk = pmrk ++ "•" let mkitem bs = do - modify (\s -> s { parentBulletLevel = level+1 }) + modify (\s -> s { parentListMarker = mrk ++ " "}) item <- cMapM blockToXml $ plainToPara $ indentBlocks (mrk ++ " ") bs - modify (\s -> s { parentBulletLevel = level }) -- restore bullet level + modify (\s -> s { parentListMarker = pmrk }) -- old parent marker return item cMapM mkitem bss blockToXml (DefinitionList defs) = -- cgit v1.2.3 From 8d7ce0fdf0a71e8c34fb7e3bff8909884f3af3a2 Mon Sep 17 00:00:00 2001 From: mb21 <mb21@users.noreply.github.com> Date: Tue, 31 Oct 2017 10:32:17 +0100 Subject: HTML Writer: consistently use dashed class-names see #3556 --- src/Text/Pandoc/Writers/HTML.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index ffcde3ce7..9c5dfccf8 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -438,7 +438,7 @@ elementToHtml slideLevel opts (Sec level num (id',classes,keyvals) title' elemen [] -> [] (x:xs) -> x ++ concatMap inDiv xs let inNl x = mconcat $ nl opts : intersperse (nl opts) x ++ [nl opts] - let classes' = ["titleslide" | titleSlide] ++ ["slide" | slide] ++ + let classes' = ["title-slide" | titleSlide] ++ ["slide" | slide] ++ ["section" | (slide || writerSectionDivs opts) && not html5 ] ++ ["level" ++ show level | slide || writerSectionDivs opts ] @@ -1100,7 +1100,7 @@ inlineToHtml opts inline = do let link = H.a ! A.href (toValue $ "#" ++ revealSlash ++ writerIdentifierPrefix opts ++ "fn" ++ ref) - ! A.class_ "footnoteRef" + ! A.class_ "footnote-ref" ! prefixedId opts ("fnref" ++ ref) $ (if isJust epubVersion then id @@ -1120,7 +1120,7 @@ blockListToNote :: PandocMonad m => WriterOptions -> String -> [Block] -> StateT 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. - let backlink = [Link ("",["footnoteBack"],[]) [Str "↩"] ("#" ++ writerIdentifierPrefix opts ++ "fnref" ++ ref,[])] + let backlink = [Link ("",["footnote-back"],[]) [Str "↩"] ("#" ++ writerIdentifierPrefix opts ++ "fnref" ++ ref,[])] blocks' = if null blocks then [] else let lastBlock = last blocks -- cgit v1.2.3 From a496979c6d0eb3e6efd57264cb89d4aad1f7afdb Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Tue, 31 Oct 2017 20:16:22 +0300 Subject: FB2 writer: write blocks outside of <p> in definitions --- src/Text/Pandoc/Writers/FB2.hs | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index cf96393ca..0a8ae17bb 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -362,17 +362,9 @@ blockToXml (DefinitionList defs) = cMapM mkdef defs where mkdef (term, bss) = do - def' <- cMapM (cMapM blockToXml . sep . paraToPlain . map indent) bss + items <- cMapM (cMapM blockToXml . plainToPara . indentBlocks (replicate 4 ' ')) bss t <- wrap "strong" term - return [ el "p" t, el "p" def' ] - sep blocks = - if all needsBreak blocks then - blocks ++ [Plain [LineBreak]] - else - blocks - needsBreak (Para _) = False - needsBreak (Plain ins) = LineBreak `notElem` ins - needsBreak _ = True + return (el "p" t : items) blockToXml h@Header{} = do -- should not occur after hierarchicalize, except inside lists/blockquotes report $ BlockNotRendered h @@ -403,14 +395,6 @@ blockToXml (Table caption aligns _ headers rows) = do align_str AlignDefault = "left" blockToXml Null = return [] --- Replace paragraphs with plain text and line break. --- Necessary to simulate multi-paragraph lists in FB2. -paraToPlain :: [Block] -> [Block] -paraToPlain [] = [] -paraToPlain (Para inlines : rest) = - Plain inlines : Plain [LineBreak] : paraToPlain rest -paraToPlain (p:rest) = p : paraToPlain rest - -- Replace plain text with paragraphs and add line break after paragraphs. -- It is used to convert plain text from tight list items to paragraphs. plainToPara :: [Block] -> [Block] -- cgit v1.2.3 From fa67d6e86ff1464874480cde84d329f02f132474 Mon Sep 17 00:00:00 2001 From: Sascha Wilde <wilde@sha-bang.de> Date: Tue, 31 Oct 2017 18:55:27 +0100 Subject: Creole reader: fixed lists with trailing white space. --- src/Text/Pandoc/Readers/Creole.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Creole.hs b/src/Text/Pandoc/Readers/Creole.hs index 4da259c0e..53154f5e0 100644 --- a/src/Text/Pandoc/Readers/Creole.hs +++ b/src/Text/Pandoc/Readers/Creole.hs @@ -154,7 +154,8 @@ listItem :: PandocMonad m => Char -> Int -> CRLParser m B.Blocks listItem c n = fmap (B.plain . B.trimInlines .mconcat) (listStart >> many1Till inline itemEnd) where - listStart = try $ optional newline >> skipSpaces >> count n (char c) + listStart = try $ skipSpaces >> optional newline >> skipSpaces + >> count n (char c) >> lookAhead (noneOf [c]) >> skipSpaces itemEnd = endOfParaElement <|> nextItem n <|> if n < 3 then nextItem (n+1) -- cgit v1.2.3 From 81610144f94a99dc0156abeffea66ffd7aa808b7 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 31 Oct 2017 10:38:48 -0700 Subject: Make `fenced_divs` affect the Markdown writer. If `fenced_divs` is enabled, fenced divs will be used. --- src/Text/Pandoc/Writers/Markdown.hs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 5d812b169..c109385d8 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -397,11 +397,19 @@ blockToMarkdown' :: PandocMonad m blockToMarkdown' _ Null = return empty blockToMarkdown' opts (Div attrs ils) = do contents <- blockListToMarkdown opts ils - return $ if isEnabled Ext_raw_html opts && - isEnabled Ext_markdown_in_html_blocks opts - then tagWithAttrs "div" attrs <> blankline <> - contents <> blankline <> "</div>" <> blankline - else contents <> blankline + return $ + case () of + _ | isEnabled Ext_fenced_divs opts && + attrs /= nullAttr -> + nowrap (text ":::" <+> attrsToMarkdown attrs) $$ + contents $$ + text ":::" <> blankline + | isEnabled Ext_native_divs opts || + (isEnabled Ext_raw_html opts && + isEnabled Ext_markdown_in_html_blocks opts) -> + tagWithAttrs "div" attrs <> blankline <> + contents <> blankline <> "</div>" <> blankline + | otherwise -> contents <> blankline blockToMarkdown' opts (Plain inlines) = do contents <- inlineListToMarkdown opts inlines -- escape if para starts with ordered list marker -- cgit v1.2.3 From 556c6c2c6ded6da4e8fd28e5f57fb55df8625373 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 31 Oct 2017 10:48:13 -0700 Subject: Markdown reader: make sure fenced div closers work in lists. Previously the following failed: ::: {.class} 1. one 2. two ::: and you needed a blank line before the closing `:::`. --- src/Text/Pandoc/Readers/Markdown.hs | 14 +++++++++++--- src/Text/Pandoc/Writers/Markdown.hs | 2 +- 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 2a88b39ec..98552e65d 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -846,6 +846,7 @@ listLine continuationIndent = try $ do skipMany spaceChar listStart) notFollowedByHtmlCloser + notFollowedByDivCloser optional (() <$ gobbleSpaces continuationIndent) listLineCommon @@ -883,16 +884,24 @@ listContinuation continuationIndent = try $ do x <- try $ do notFollowedBy blankline notFollowedByHtmlCloser + notFollowedByDivCloser gobbleSpaces continuationIndent anyLineNewline xs <- many $ try $ do notFollowedBy blankline notFollowedByHtmlCloser + notFollowedByDivCloser gobbleSpaces continuationIndent <|> notFollowedBy' listStart anyLineNewline blanks <- many blankline return $ concat (x:xs) ++ blanks +notFollowedByDivCloser :: PandocMonad m => MarkdownParser m () +notFollowedByDivCloser = do + guardDisabled Ext_fenced_divs <|> + do divLevel <- stateFencedDivLevel <$> getState + guard (divLevel < 1) <|> notFollowedBy divFenceEnd + notFollowedByHtmlCloser :: PandocMonad m => MarkdownParser m () notFollowedByHtmlCloser = do inHtmlBlock <- stateInHtmlBlock <$> getState @@ -965,6 +974,7 @@ defRawBlock compact = try $ do let dline = try ( do notFollowedBy blankline notFollowedByHtmlCloser + notFollowedByDivCloser if compact -- laziness not compatible with compact then () <$ indentSpaces else (() <$ indentSpaces) @@ -1688,10 +1698,8 @@ endline = try $ do guardEnabled Ext_blank_before_header <|> (notFollowedBy . char =<< atxChar) -- atx header guardDisabled Ext_backtick_code_blocks <|> notFollowedBy (() <$ (lookAhead (char '`') >> codeBlockFenced)) - guardDisabled Ext_fenced_divs <|> - do divLevel <- stateFencedDivLevel <$> getState - guard (divLevel < 1) <|> notFollowedBy divFenceEnd notFollowedByHtmlCloser + notFollowedByDivCloser (eof >> return mempty) <|> (guardEnabled Ext_hard_line_breaks >> return (return B.linebreak)) <|> (guardEnabled Ext_ignore_line_breaks >> return mempty) diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index c109385d8..a1f30cb0e 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -402,7 +402,7 @@ blockToMarkdown' opts (Div attrs ils) = do _ | isEnabled Ext_fenced_divs opts && attrs /= nullAttr -> nowrap (text ":::" <+> attrsToMarkdown attrs) $$ - contents $$ + chomp contents $$ text ":::" <> blankline | isEnabled Ext_native_divs opts || (isEnabled Ext_raw_html opts && -- cgit v1.2.3 From 2a81ff324523e992e73b68db24b080182465431c Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 31 Oct 2017 11:07:39 -0700 Subject: LaTeX/Beamer writer: support "blocks" inside columns and other Divs. Example: ``` <div class="columns"> <div class="column" width="40%"> - Item </div> <div class="column" width="60%"> - Item </div> </div> ``` Closes #4016. --- src/Text/Pandoc/Writers/LaTeX.hs | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index ab1e90b3b..976450dcd 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -371,6 +371,10 @@ toSlides bs = do concat `fmap` mapM (elementToBeamer slideLevel) (hierarchicalize bs') elementToBeamer :: PandocMonad m => Int -> Element -> LW m [Block] +elementToBeamer _slideLevel (Blk (Div attr bs)) = do + -- make sure we support "blocks" inside divs + bs' <- concat `fmap` mapM (elementToBeamer 0) (hierarchicalize bs) + return [Div attr bs'] elementToBeamer _slideLevel (Blk b) = return [b] elementToBeamer slideLevel (Sec lvl _num (ident,classes,kvs) tit elts) | lvl > slideLevel = do -- cgit v1.2.3 From 5f9f458df394686405b525757d1b5ef3b4cbad17 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 31 Oct 2017 11:30:56 -0700 Subject: LaTeX reader: handle `%` comment right after command. For example \emph% {hi} --- 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 a982029af..9bac3d3a7 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1132,7 +1132,7 @@ inlineCommand' = try $ do lookupListDefault raw names inlineCommands tok :: PandocMonad m => LP m Inlines -tok = grouped inline <|> inlineCommand' <|> singleChar' +tok = try $ spaces >> grouped inline <|> inlineCommand' <|> singleChar' where singleChar' = do Tok _ _ t <- singleChar return (str (T.unpack t)) -- cgit v1.2.3 From 0e57b8b85dd22b78e2f60226700260b4eb8d36ea Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 31 Oct 2017 11:58:43 -0700 Subject: Add Millimeter constructor to Dimension in ImageSize. Minor API change. Now sizes given in 'mm' are no longer converted to 'cm'. Closes #4012. --- src/Text/Pandoc/ImageSize.hs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/ImageSize.hs b/src/Text/Pandoc/ImageSize.hs index 27d5c6a9c..5f491e08b 100644 --- a/src/Text/Pandoc/ImageSize.hs +++ b/src/Text/Pandoc/ImageSize.hs @@ -79,6 +79,7 @@ instance Show Direction where data Dimension = Pixel Integer | Centimeter Double + | Millimeter Double | Inch Double | Percent Double | Em Double @@ -86,6 +87,7 @@ data Dimension = Pixel Integer instance Show Dimension where show (Pixel a) = show a ++ "px" show (Centimeter a) = showFl a ++ "cm" + show (Millimeter a) = showFl a ++ "mm" show (Inch a) = showFl a ++ "in" show (Percent a) = show a ++ "%" show (Em a) = showFl a ++ "em" @@ -184,6 +186,7 @@ inInch opts dim = case dim of (Pixel a) -> fromIntegral a / fromIntegral (writerDpi opts) (Centimeter a) -> a * 0.3937007874 + (Millimeter a) -> a * 0.03937007874 (Inch a) -> a (Percent _) -> 0 (Em a) -> a * (11/64) @@ -193,6 +196,7 @@ inPixel opts dim = case dim of (Pixel a) -> a (Centimeter a) -> floor $ dpi * a * 0.3937007874 :: Integer + (Millimeter a) -> floor $ dpi * a * 0.03937007874 :: Integer (Inch a) -> floor $ dpi * a :: Integer (Percent _) -> 0 (Em a) -> floor $ dpi * a * (11/64) :: Integer @@ -225,6 +229,7 @@ scaleDimension factor dim = case dim of Pixel x -> Pixel (round $ factor * fromIntegral x) Centimeter x -> Centimeter (factor * x) + Millimeter x -> Millimeter (factor * x) Inch x -> Inch (factor * x) Percent x -> Percent (factor * x) Em x -> Em (factor * x) @@ -243,7 +248,7 @@ lengthToDim :: String -> Maybe Dimension lengthToDim s = numUnit s >>= uncurry toDim where toDim a "cm" = Just $ Centimeter a - toDim a "mm" = Just $ Centimeter (a / 10) + toDim a "mm" = Just $ Millimeter a toDim a "in" = Just $ Inch a toDim a "inch" = Just $ Inch a toDim a "%" = Just $ Percent a -- cgit v1.2.3 From 534e625ace6c2d94af5544591fab0a425445dfa3 Mon Sep 17 00:00:00 2001 From: Sascha Wilde <wilde@sha-bang.de> Date: Tue, 31 Oct 2017 22:33:58 +0100 Subject: Creole reader: fixed some minor typos and formatting. --- src/Text/Pandoc/Readers/Creole.hs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Creole.hs b/src/Text/Pandoc/Readers/Creole.hs index 53154f5e0..b4eb6eaef 100644 --- a/src/Text/Pandoc/Readers/Creole.hs +++ b/src/Text/Pandoc/Readers/Creole.hs @@ -27,7 +27,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA License : GNU GPL, version 2 or above Maintainer : Sascha Wilde <wilde@sha-bang.de> - Stability : WIP + Stability : alpha Portability : portable Conversion of creole text to 'Pandoc' document. @@ -64,7 +64,7 @@ readCreole opts s = do type CRLParser = ParserT [Char] ParserState -- --- Utility funcitons +-- Utility functions -- (<+>) :: (Monad m, Monoid a) => m a -> m a -> m a @@ -111,7 +111,8 @@ block = do return res nowiki :: PandocMonad m => CRLParser m B.Blocks -nowiki = try $ fmap (B.codeBlock . mconcat) (nowikiStart >> manyTill content nowikiEnd) +nowiki = try $ fmap (B.codeBlock . mconcat) (nowikiStart + >> manyTill content nowikiEnd) where content = brackets <|> line brackets = try $ option "" ((:[]) <$> newline) @@ -194,7 +195,7 @@ endOfParaElement = lookAhead $ endOfInput <|> endOfPara startOf :: PandocMonad m => CRLParser m a -> CRLParser m () startOf p = try $ blankline >> p >> return mempty startOfList = startOf $ anyList 1 - startOfTable =startOf table + startOfTable = startOf table startOfHeader = startOf header startOfNowiki = startOf nowiki hr = startOf horizontalRule -- cgit v1.2.3 From fb6e5812bce4c71d6ac9a8946f0d69d4c08820de Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 31 Oct 2017 21:13:15 -0700 Subject: Fixed regression in parsing of HTML comments in markdown... and other non-HTML formats (`Text.Pandoc.Readers.HTML.htmlTag`). The parser stopped at the first `>` character, even if it wasn't the end of the comment. Closes #4019. --- src/Text/Pandoc/Readers/HTML.hs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 8d37deb26..915fa852f 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -1172,8 +1172,9 @@ htmlTag f = try $ do case next of TagComment s | "<!--" `isPrefixOf` inp -> do - char '<' - manyTill anyChar endAngle + string "<!--" + count (length s) anyChar + string "-->" stripComments <- getOption readerStripComments if stripComments then return (next, "") -- cgit v1.2.3 From 32f6938605b2251535df7ab2005cb88964f4744e Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 31 Oct 2017 21:27:08 -0700 Subject: Properly pass through author metadata in JATS writer. Closes #4020. --- src/Text/Pandoc/Writers/JATS.hs | 55 +++++++++++------------------------------ 1 file changed, 14 insertions(+), 41 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs index 2aac777c6..0ac37efba 100644 --- a/src/Text/Pandoc/Writers/JATS.hs +++ b/src/Text/Pandoc/Writers/JATS.hs @@ -37,7 +37,6 @@ import Data.Generics (everywhere, mkT) import Data.List (isSuffixOf, partition) import Data.Maybe (fromMaybe) import Data.Text (Text) -import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Definition import Text.Pandoc.Highlighting (languages, languagesByExtension) @@ -56,38 +55,14 @@ import qualified Text.XML.Light as Xml data JATSVersion = JATS1_1 deriving (Eq, Show) -type DB = ReaderT JATSVersion - --- | Convert list of authors to a docbook <author> section -authorToJATS :: PandocMonad m => WriterOptions -> [Inline] -> DB m B.Inlines -authorToJATS opts name' = do - name <- render Nothing <$> inlinesToJATS opts name' - let colwidth = if writerWrapText opts == WrapAuto - then Just $ writerColumns opts - else Nothing - return $ B.rawInline "docbook" $ render colwidth $ - if ',' `elem` name - then -- last name first - let (lastname, rest) = break (==',') name - firstname = triml rest in - inTagsSimple "firstname" (text $ escapeStringForXML firstname) <> - inTagsSimple "surname" (text $ escapeStringForXML lastname) - else -- last name last - let namewords = words name - lengthname = length namewords - (firstname, lastname) = case lengthname of - 0 -> ("","") - 1 -> ("", name) - n -> (unwords (take (n-1) namewords), last namewords) - in inTagsSimple "firstname" (text $ escapeStringForXML firstname) $$ - inTagsSimple "surname" (text $ escapeStringForXML lastname) +type JATS = ReaderT JATSVersion writeJATS :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeJATS opts d = runReaderT (docToJATS opts d) JATS1_1 -- | Convert Pandoc document to string in JATS format. -docToJATS :: PandocMonad m => WriterOptions -> Pandoc -> DB m Text +docToJATS :: PandocMonad m => WriterOptions -> Pandoc -> JATS m Text docToJATS opts (Pandoc meta blocks) = do let isBackBlock (Div ("refs",_,_) _) = True isBackBlock _ = False @@ -110,14 +85,12 @@ docToJATS opts (Pandoc meta blocks) = do TopLevelChapter -> 0 TopLevelSection -> 1 TopLevelDefault -> 1 - auths' <- mapM (authorToJATS opts) $ docAuthors meta - let meta' = B.setMeta "author" auths' meta metadata <- metaToJSON opts (fmap (render' . vcat) . mapM (elementToJATS opts' startLvl) . hierarchicalize) (fmap render' . inlinesToJATS opts') - meta' + meta main <- (render' . vcat) <$> mapM (elementToJATS opts' startLvl) elements back <- (render' . vcat) <$> @@ -132,7 +105,7 @@ docToJATS opts (Pandoc meta blocks) = do Just tpl -> renderTemplate' tpl context -- | Convert an Element to JATS. -elementToJATS :: PandocMonad m => WriterOptions -> Int -> Element -> DB m Doc +elementToJATS :: PandocMonad m => WriterOptions -> Int -> Element -> JATS m Doc elementToJATS opts _ (Blk block) = blockToJATS opts block elementToJATS opts lvl (Sec _ _num (id',_,kvs) title elements) = do let idAttr = [("id", writerIdentifierPrefix opts ++ id') | not (null id')] @@ -144,7 +117,7 @@ elementToJATS opts lvl (Sec _ _num (id',_,kvs) title elements) = do inTagsSimple "title" title' $$ vcat contents -- | Convert a list of Pandoc blocks to JATS. -blocksToJATS :: PandocMonad m => WriterOptions -> [Block] -> DB m Doc +blocksToJATS :: PandocMonad m => WriterOptions -> [Block] -> JATS m Doc blocksToJATS opts = fmap vcat . mapM (blockToJATS opts) -- | Auxiliary function to convert Plain block to Para. @@ -155,13 +128,13 @@ plainToPara x = x -- | Convert a list of pairs of terms and definitions into a list of -- JATS varlistentrys. deflistItemsToJATS :: PandocMonad m - => WriterOptions -> [([Inline],[[Block]])] -> DB m Doc + => WriterOptions -> [([Inline],[[Block]])] -> JATS m Doc deflistItemsToJATS opts items = vcat <$> mapM (uncurry (deflistItemToJATS opts)) items -- | Convert a term and a list of blocks into a JATS varlistentry. deflistItemToJATS :: PandocMonad m - => WriterOptions -> [Inline] -> [[Block]] -> DB m Doc + => WriterOptions -> [Inline] -> [[Block]] -> JATS m Doc deflistItemToJATS opts term defs = do term' <- inlinesToJATS opts term def' <- blocksToJATS opts $ concatMap (map plainToPara) defs @@ -171,7 +144,7 @@ deflistItemToJATS opts term defs = do -- | Convert a list of lists of blocks to a list of JATS list items. listItemsToJATS :: PandocMonad m - => WriterOptions -> Maybe [String] -> [[Block]] -> DB m Doc + => WriterOptions -> Maybe [String] -> [[Block]] -> JATS m Doc listItemsToJATS opts markers items = case markers of Nothing -> vcat <$> mapM (listItemToJATS opts Nothing) items @@ -179,7 +152,7 @@ listItemsToJATS opts markers items = -- | Convert a list of blocks into a JATS list item. listItemToJATS :: PandocMonad m - => WriterOptions -> Maybe String -> [Block] -> DB m Doc + => WriterOptions -> Maybe String -> [Block] -> JATS m Doc listItemToJATS opts mbmarker item = do contents <- blocksToJATS opts item return $ inTagsIndented "list-item" $ @@ -187,7 +160,7 @@ listItemToJATS opts mbmarker item = do $$ contents -- | Convert a Pandoc block element to JATS. -blockToJATS :: PandocMonad m => WriterOptions -> Block -> DB m Doc +blockToJATS :: PandocMonad m => WriterOptions -> Block -> JATS m Doc blockToJATS _ Null = return empty -- Bibliography reference: blockToJATS opts (Div ('r':'e':'f':'-':_,_,_) [Para lst]) = @@ -311,7 +284,7 @@ tableRowToJATS :: PandocMonad m => WriterOptions -> Bool -> [[Block]] - -> DB m Doc + -> JATS m Doc tableRowToJATS opts isHeader cols = (inTagsIndented "tr" . vcat) <$> mapM (tableItemToJATS opts isHeader) cols @@ -319,17 +292,17 @@ tableItemToJATS :: PandocMonad m => WriterOptions -> Bool -> [Block] - -> DB m Doc + -> JATS m Doc tableItemToJATS opts isHeader item = (inTags True (if isHeader then "th" else "td") [] . vcat) <$> mapM (blockToJATS opts) item -- | Convert a list of inline elements to JATS. -inlinesToJATS :: PandocMonad m => WriterOptions -> [Inline] -> DB m Doc +inlinesToJATS :: PandocMonad m => WriterOptions -> [Inline] -> JATS m Doc inlinesToJATS opts lst = hcat <$> mapM (inlineToJATS opts) lst -- | Convert an inline element to JATS. -inlineToJATS :: PandocMonad m => WriterOptions -> Inline -> DB m Doc +inlineToJATS :: PandocMonad m => WriterOptions -> Inline -> JATS m Doc inlineToJATS _ (Str str) = return $ text $ escapeStringForXML str inlineToJATS opts (Emph lst) = inTagsSimple "italic" <$> inlinesToJATS opts lst -- cgit v1.2.3 From 8a5541dca88190e6fdf6ed9ff5c1c5c69c3c9710 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Wed, 1 Nov 2017 13:08:15 +0300 Subject: FB2 writer: remove <annotation> from <body> <annotation> is not allowed inside <body> according to FictionBook2 XML schema. Besides that, the same information is already placed inside <description>. Related bug: #2424 --- src/Text/Pandoc/Writers/FB2.hs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index 666b67e52..8986c1191 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -94,9 +94,9 @@ pandocToFB2 :: PandocMonad m pandocToFB2 opts (Pandoc meta blocks) = do modify (\s -> s { writerOptions = opts }) desc <- description meta - fp <- frontpage meta + title <- cMapM toXml . docTitle $ meta secs <- renderSections 1 blocks - let body = el "body" $ fp ++ secs + let body = el "body" $ el "title" (el "p" title) : secs notes <- renderFootnotes (imgs,missing) <- fmap imagesToFetch get >>= \s -> lift (fetchImages s) let body' = replaceImagesWithAlt missing body @@ -110,15 +110,6 @@ pandocToFB2 opts (Pandoc meta blocks) = do in [ uattr "xmlns" xmlns , attr ("xmlns", "l") xlink ] -frontpage :: PandocMonad m => Meta -> FBM m [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 :: PandocMonad m => Meta -> FBM m Content description meta' = do bt <- booktitle meta' -- cgit v1.2.3 From 3cee9c89768de064910deedbce3d8d28c1ffef84 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Wed, 1 Nov 2017 13:31:16 +0300 Subject: FB2 writer: Add "unrecognised" genre to <title-info> XML schema requires at least one genre. --- src/Text/Pandoc/Writers/FB2.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index 8986c1191..2d7516daf 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -112,6 +112,7 @@ pandocToFB2 opts (Pandoc meta blocks) = do description :: PandocMonad m => Meta -> FBM m Content description meta' = do + let genre = el "genre" "unrecognised" bt <- booktitle meta' let as = authors meta' dd <- docdate meta' @@ -121,7 +122,7 @@ description meta' = do _ -> [] where iso639 = takeWhile (/= '-') -- Convert BCP 47 to ISO 639 return $ el "description" - [ el "title-info" (bt ++ as ++ dd ++ lang) + [ el "title-info" (genre : (bt ++ as ++ dd ++ lang)) , el "document-info" [ el "program-used" "pandoc" ] -- FIXME: +version ] -- cgit v1.2.3 From 00b64f337da635e3cb2fb4bd473d606f48653eb5 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Wed, 1 Nov 2017 14:20:03 +0300 Subject: hlint --- src/Text/Pandoc/CSS.hs | 2 +- src/Text/Pandoc/ImageSize.hs | 4 ++-- src/Text/Pandoc/Logging.hs | 14 ++++++------- src/Text/Pandoc/Options.hs | 2 +- src/Text/Pandoc/Parsing.hs | 36 ++++++++++++++++----------------- src/Text/Pandoc/Pretty.hs | 2 +- src/Text/Pandoc/Readers/CommonMark.hs | 2 +- src/Text/Pandoc/Readers/HTML.hs | 10 ++++----- src/Text/Pandoc/Shared.hs | 6 +++--- src/Text/Pandoc/Writers/FB2.hs | 2 +- src/Text/Pandoc/Writers/ICML.hs | 6 +++--- src/Text/Pandoc/Writers/LaTeX.hs | 2 +- src/Text/Pandoc/Writers/ODT.hs | 4 ++-- src/Text/Pandoc/Writers/OpenDocument.hs | 2 +- src/Text/Pandoc/Writers/RST.hs | 4 ++-- src/Text/Pandoc/Writers/RTF.hs | 2 +- src/Text/Pandoc/Writers/TEI.hs | 2 +- src/Text/Pandoc/Writers/ZimWiki.hs | 4 ++-- 18 files changed, 53 insertions(+), 53 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/CSS.hs b/src/Text/Pandoc/CSS.hs index 41be1ea13..d44b5e1e2 100644 --- a/src/Text/Pandoc/CSS.hs +++ b/src/Text/Pandoc/CSS.hs @@ -40,4 +40,4 @@ pickStylesToKVs props styleAttr = pickStyleAttrProps :: [String] -> String -> Maybe String pickStyleAttrProps lookupProps styleAttr = do styles <- eitherToMaybe $ parse styleAttrParser "" styleAttr - foldOrElse Nothing $ map (flip lookup styles) lookupProps + foldOrElse Nothing $ map (`lookup` styles) lookupProps diff --git a/src/Text/Pandoc/ImageSize.hs b/src/Text/Pandoc/ImageSize.hs index 5f491e08b..b4206b84b 100644 --- a/src/Text/Pandoc/ImageSize.hs +++ b/src/Text/Pandoc/ImageSize.hs @@ -301,8 +301,8 @@ findpHYs x factor = if u == 1 -- dots per meter then \z -> z * 254 `div` 10000 else const 72 - in ( factor $ (shift x1 24) + (shift x2 16) + (shift x3 8) + x4, - factor $ (shift y1 24) + (shift y2 16) + (shift y3 8) + y4 ) + in ( factor $ shift x1 24 + shift x2 16 + shift x3 8 + x4, + factor $ shift y1 24 + shift y2 16 + shift y3 8 + y4 ) | otherwise = findpHYs $ B.drop 1 x -- read another byte gifSize :: ByteString -> Maybe ImageSize diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs index 4723c1119..7f4ae2ada 100644 --- a/src/Text/Pandoc/Logging.hs +++ b/src/Text/Pandoc/Logging.hs @@ -230,7 +230,7 @@ showLogMessage msg = "Skipped '" ++ s ++ "' at " ++ showPos pos CouldNotParseYamlMetadata s pos -> "Could not parse YAML metadata at " ++ showPos pos ++ - if null s then "" else (": " ++ s) + if null s then "" else ": " ++ s DuplicateLinkReference s pos -> "Duplicate link reference '" ++ s ++ "' at " ++ showPos pos DuplicateNoteReference s pos -> @@ -260,20 +260,20 @@ showLogMessage msg = "Docx parser warning: " ++ s CouldNotFetchResource fp s -> "Could not fetch resource '" ++ fp ++ "'" ++ - if null s then "" else (": " ++ s) + if null s then "" else ": " ++ s CouldNotDetermineImageSize fp s -> "Could not determine image size for '" ++ fp ++ "'" ++ - if null s then "" else (": " ++ s) + if null s then "" else ": " ++ s CouldNotConvertImage fp s -> "Could not convert image '" ++ fp ++ "'" ++ - if null s then "" else (": " ++ s) + if null s then "" else ": " ++ s CouldNotDetermineMimeType fp -> "Could not determine mime type for '" ++ fp ++ "'" CouldNotConvertTeXMath s m -> "Could not convert TeX math '" ++ s ++ "', rendering as TeX" ++ - if null m then "" else (':':'\n':m) + if null m then "" else ':' : '\n' : m CouldNotParseCSS m -> - "Could not parse CSS" ++ if null m then "" else (':':'\n':m) + "Could not parse CSS" ++ if null m then "" else ':' : '\n' : m Fetching fp -> "Fetching " ++ fp ++ "..." Extracting fp -> @@ -301,7 +301,7 @@ showLogMessage msg = "The term " ++ t ++ " has no translation defined." CouldNotLoadTranslations lang m -> "Could not load translations for " ++ lang ++ - if null m then "" else ('\n':m) + if null m then "" else '\n' : m messageVerbosity:: LogMessage -> Verbosity messageVerbosity msg = diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index 35c17c2ac..581f4c82a 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -261,4 +261,4 @@ instance Default WriterOptions where -- | Returns True if the given extension is enabled. isEnabled :: Extension -> WriterOptions -> Bool -isEnabled ext opts = ext `extensionEnabled` (writerExtensions opts) +isEnabled ext opts = ext `extensionEnabled` writerExtensions opts diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index a02034de4..61d3caf3d 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -188,12 +188,12 @@ where import Control.Monad.Identity import Control.Monad.Reader -import Data.Char (chr, isAlphaNum, isAscii, isHexDigit, isPunctuation, isSpace, +import Data.Char (chr, isAlphaNum, isAscii, isAsciiUpper, isHexDigit, isPunctuation, isSpace, ord, toLower, toUpper) import Data.Default import Data.List (intercalate, isSuffixOf, transpose) import qualified Data.Map as M -import Data.Maybe (catMaybes) +import Data.Maybe (mapMaybe) import Data.Monoid ((<>)) import qualified Data.Set as Set import Data.Text (Text) @@ -354,7 +354,7 @@ oneOfStringsCI = oneOfStrings' ciMatch -- this optimizes toLower by checking common ASCII case -- first, before calling the expensive unicode-aware -- function: - toLower' c | c >= 'A' && c <= 'Z' = chr (ord c + 32) + toLower' c | isAsciiUpper c = chr (ord c + 32) | isAscii c = c | otherwise = toLower c @@ -497,19 +497,19 @@ romanNumeral upperCase = do lookAhead $ oneOf romanDigits let [one, five, ten, fifty, hundred, fivehundred, thousand] = map char romanDigits - thousands <- many thousand >>= (return . (1000 *) . length) + thousands <- ((1000 *) . length) <$> many thousand ninehundreds <- option 0 $ try $ hundred >> thousand >> return 900 - fivehundreds <- many fivehundred >>= (return . (500 *) . length) + fivehundreds <- ((500 *) . length) <$> many fivehundred fourhundreds <- option 0 $ try $ hundred >> fivehundred >> return 400 - hundreds <- many hundred >>= (return . (100 *) . length) + hundreds <- ((100 *) . length) <$> many hundred nineties <- option 0 $ try $ ten >> hundred >> return 90 - fifties <- many fifty >>= (return . (50 *) . length) + fifties <- ((50 *) . length) <$> many fifty forties <- option 0 $ try $ ten >> fifty >> return 40 - tens <- many ten >>= (return . (10 *) . length) + tens <- ((10 *) . length) <$> many ten nines <- option 0 $ try $ one >> ten >> return 9 - fives <- many five >>= (return . (5 *) . length) + fives <- ((5 *) . length) <$> many five fours <- option 0 $ try $ one >> five >> return 4 - ones <- many one >>= (return . length) + ones <- length <$> many one let total = thousands + ninehundreds + fivehundreds + fourhundreds + hundreds + nineties + fifties + forties + tens + nines + fives + fours + ones @@ -545,7 +545,7 @@ emailAddress = try $ toResult <$> mailbox <*> (char '@' *> domain) isEmailPunct c = c `elem` "!\"#$%&'*+-/=?^_{|}~;" -- note: sepBy1 from parsec consumes input when sep -- succeeds and p fails, so we use this variant here. - sepby1 p sep = (:) <$> p <*> (many (try $ sep >> p)) + sepby1 p sep = (:) <$> p <*> many (try $ sep >> p) uriScheme :: Stream s m Char => ParserT s st m String @@ -568,7 +568,7 @@ uri = try $ do let percentEscaped = try $ char '%' >> skipMany1 (satisfy isHexDigit) let entity = () <$ characterReference let punct = skipMany1 (char ',') - <|> () <$ (satisfy (\c -> not (isSpace c) && c /= '<' && c /= '>')) + <|> () <$ satisfy (\c -> not (isSpace c) && c /= '<' && c /= '>') let uriChunk = skipMany1 wordChar <|> percentEscaped <|> entity @@ -837,7 +837,7 @@ blankLineBlockLine = try (char '|' >> blankline) lineBlockLines :: Monad m => ParserT [Char] st m [String] lineBlockLines = try $ do lines' <- many1 (lineBlockLine <|> ((:[]) <$> blankLineBlockLine)) - skipMany $ blankline + skipMany blankline return lines' -- | Parse a table using 'headerParser', 'rowParser', @@ -868,10 +868,10 @@ tableWith' headerParser rowParser lineParser footerParser = try $ do lines' <- sequence <$> rowParser indices `sepEndBy1` lineParser footerParser numColumns <- getOption readerColumns - let widths = if (indices == []) + let widths = if null indices then replicate (length aligns) 0.0 else widthsFromIndices numColumns indices - return $ (aligns, widths, heads, lines') + return (aligns, widths, heads, lines') -- Calculate relative widths of table columns, based on indices widthsFromIndices :: Int -- Number of columns on terminal @@ -1271,7 +1271,7 @@ registerHeader (ident,classes,kvs) header' = do then do let id' = uniqueIdent (B.toList header') ids let id'' = if Ext_ascii_identifiers `extensionEnabled` exts - then catMaybes $ map toAsciiChar id' + then mapMaybe toAsciiChar id' else id' updateState $ updateIdentifierList $ Set.insert id' updateState $ updateIdentifierList $ Set.insert id'' @@ -1417,10 +1417,10 @@ a <+?> b = a >>= flip fmap (try b <|> return mempty) . (<>) extractIdClass :: Attr -> Attr extractIdClass (ident, cls, kvs) = (ident', cls', kvs') where - ident' = case (lookup "id" kvs) of + ident' = case lookup "id" kvs of Just v -> v Nothing -> ident - cls' = case (lookup "class" kvs) of + cls' = case lookup "class" kvs of Just cl -> words cl Nothing -> cls kvs' = filter (\(k,_) -> k /= "id" || k /= "class") kvs diff --git a/src/Text/Pandoc/Pretty.hs b/src/Text/Pandoc/Pretty.hs index ed6dde149..f95bfa8e0 100644 --- a/src/Text/Pandoc/Pretty.hs +++ b/src/Text/Pandoc/Pretty.hs @@ -357,7 +357,7 @@ mergeBlocks addSpace (IsBlock w1 lns1) (IsBlock w2 lns2) = | otherwise -> (lns1, lns2) pad n s = s ++ replicate (n - realLength s) ' ' sp "" = "" - sp xs = if addSpace then (' ' : xs) else xs + sp xs = if addSpace then ' ' : xs else xs offsetOf :: D -> Int offsetOf (Text o _) = o diff --git a/src/Text/Pandoc/Readers/CommonMark.hs b/src/Text/Pandoc/Readers/CommonMark.hs index 6b864521f..47f4c4088 100644 --- a/src/Text/Pandoc/Readers/CommonMark.hs +++ b/src/Text/Pandoc/Readers/CommonMark.hs @@ -59,7 +59,7 @@ readCommonMark opts s = return $ -- | Returns True if the given extension is enabled. enabled :: Extension -> ReaderOptions -> Bool -enabled ext opts = ext `extensionEnabled` (readerExtensions opts) +enabled ext opts = ext `extensionEnabled` readerExtensions opts convertEmojis :: String -> String convertEmojis (':':xs) = diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 915fa852f..e2be1c5bd 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -141,7 +141,7 @@ type TagParser m = HTMLParser m [Tag Text] pHtml :: PandocMonad m => TagParser m Blocks pHtml = try $ do - (TagOpen "html" attr) <- lookAhead $ pAnyTag + (TagOpen "html" attr) <- lookAhead pAnyTag for_ (lookup "lang" attr) $ updateState . B.setMeta "lang" . B.text . T.unpack pInTags "html" block @@ -152,7 +152,7 @@ pBody = pInTags "body" block 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) + setTitle t = mempty <$ updateState (B.setMeta "title" t) pMetaTag = do mt <- pSatisfy (matchTagOpen "meta" []) let name = T.unpack $ fromAttrib "name" mt @@ -233,7 +233,7 @@ eFootnote :: PandocMonad m => TagParser m () eFootnote = try $ do let notes = ["footnote", "rearnote"] guardEnabled Ext_epub_html_exts - (TagOpen tag attr') <- lookAhead $ pAnyTag + (TagOpen tag attr') <- lookAhead pAnyTag let attr = toStringAttr attr' guard (maybe False (flip elem notes) (lookup "type" attr)) let ident = fromMaybe "" (lookup "id" attr) @@ -478,7 +478,7 @@ pTable = try $ do let pTh = option [] $ pInTags "tr" (pCell "th") pTr = try $ skipMany pBlank >> pInTags "tr" (pCell "td" <|> pCell "th") - pTBody = do pOptInTag "tbody" $ many1 pTr + pTBody = pOptInTag "tbody" $ many1 pTr head'' <- pOptInTag "thead" pTh head' <- map snd <$> (pOptInTag "tbody" $ @@ -1256,7 +1256,7 @@ renderTags' = renderTagsOptions renderOptions{ optMinimize = matchTags ["hr", "br", "img", "meta", "link"] , optRawTag = matchTags ["script", "style"] } - where matchTags = \tags -> flip elem tags . T.toLower + where matchTags tags = flip elem tags . T.toLower -- EPUB Specific diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 60c8e1a0c..e0ea8b5e7 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -519,8 +519,8 @@ uniqueIdent title' usedIdents -- | True if block is a Header block. isHeaderBlock :: Block -> Bool -isHeaderBlock (Header{}) = True -isHeaderBlock _ = False +isHeaderBlock Header{} = True +isHeaderBlock _ = False -- | Shift header levels up or down. headerShift :: Int -> Pandoc -> Pandoc @@ -584,7 +584,7 @@ renderTags' = renderTagsOptions renderOptions{ optMinimize = matchTags ["hr", "br", "img", "meta", "link"] , optRawTag = matchTags ["script", "style"] } - where matchTags = \tags -> flip elem tags . map toLower + where matchTags tags = flip elem tags . map toLower -- -- File handling diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index 2d7516daf..633f42442 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -329,7 +329,7 @@ blockToXml (LineBlock lns) = blockToXml (OrderedList a bss) = do state <- get let pmrk = parentListMarker state - let markers = map (pmrk ++) $ orderedListMarkers a + let markers = (pmrk ++) <$> orderedListMarkers a let mkitem mrk bs = do modify (\s -> s { parentListMarker = mrk ++ " "}) item <- cMapM blockToXml $ plainToPara $ indentBlocks (mrk ++ " ") bs diff --git a/src/Text/Pandoc/Writers/ICML.hs b/src/Text/Pandoc/Writers/ICML.hs index 4afa23cb9..ba274fb59 100644 --- a/src/Text/Pandoc/Writers/ICML.hs +++ b/src/Text/Pandoc/Writers/ICML.hs @@ -204,9 +204,9 @@ parStylesToDoc st = vcat $ map makeStyle $ Set.toAscList $ blockStyles st where numbering | isOrderedList = [("NumberingExpression", "^#.^t"), ("NumberingLevel", show nOrds)] | otherwise = [] - listType | isOrderedList && not (isInfixOf subListParName s) + listType | isOrderedList && not (subListParName `isInfixOf` s) = [("BulletsAndNumberingListType", "NumberedList")] - | isBulletList && not (isInfixOf subListParName s) + | isBulletList && not (subListParName `isInfixOf` s) = [("BulletsAndNumberingListType", "BulletList")] | otherwise = [] indent = [("LeftIndent", show indt)] @@ -350,7 +350,7 @@ blockToICML opts style (Table caption aligns widths headers rows) = cells <- rowsToICML tabl (0::Int) let colWidths w = [("SingleColumnWidth",show $ 500 * w) | w > 0] - let tupToDoc tup = selfClosingTag "Column" $ ("Name",show $ fst tup) : (colWidths $ snd tup) + let tupToDoc tup = selfClosingTag "Column" $ ("Name",show $ fst tup) : colWidths (snd tup) let colDescs = vcat $ zipWith (curry tupToDoc) [0..nrCols-1] widths let tableDoc = return $ inTags True "Table" [ ("AppliedTableStyle","TableStyle/Table") diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 976450dcd..156af4bb2 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -835,7 +835,7 @@ defListItemToLaTeX (term, defs) = do else term' def' <- liftM vsep $ mapM blockListToLaTeX defs return $ case defs of - ((Header _ _ _ : _) : _) -> + ((Header{} : _) : _) -> "\\item" <> brackets term'' <> " ~ " $$ def' _ -> "\\item" <> brackets term'' $$ def' diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index fcd551227..390d7c3ba 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -116,8 +116,8 @@ pandocToODT opts doc@(Pandoc meta _) = do ,("manifest:version","1.2")] ( selfClosingTag "manifest:file-entry" [("manifest:media-type","application/vnd.oasis.opendocument.text") ,("manifest:full-path","/")] - $$ vcat ( map toFileEntry $ files ) - $$ vcat ( map toFileEntry $ formulas ) + $$ vcat ( map toFileEntry files ) + $$ vcat ( map toFileEntry formulas ) ) ) let archive' = addEntryToArchive manifestEntry archive diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index ac4a85670..702349636 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -572,7 +572,7 @@ paraStyle attrs = do t <- gets stTight let styleAttr = [ ("style:name" , "P" ++ show pn) , ("style:family" , "paragraph" )] - indentVal = flip (++) "in" . show $ if b then (max 0.5 i) else i + indentVal = flip (++) "in" . show $ if b then max 0.5 i else i tight = if t then [ ("fo:margin-top" , "0in" ) , ("fo:margin-bottom" , "0in" )] else [] diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index aab8a3bf0..42d4d0040 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -437,8 +437,8 @@ inlineListToRST lst = isComplex (Strikeout _) = True isComplex (Superscript _) = True isComplex (Subscript _) = True - isComplex (Link{}) = True - isComplex (Image{}) = True + isComplex Link{} = True + isComplex Image{} = True isComplex (Code _ _) = True isComplex (Math _ _) = True isComplex (Cite _ (x:_)) = isComplex x diff --git a/src/Text/Pandoc/Writers/RTF.hs b/src/Text/Pandoc/Writers/RTF.hs index 917fef3eb..955b3f7f1 100644 --- a/src/Text/Pandoc/Writers/RTF.hs +++ b/src/Text/Pandoc/Writers/RTF.hs @@ -326,7 +326,7 @@ tableItemToRTF indent alignment item = do spaceAtEnd :: String -> String spaceAtEnd str = if "\\par}\n" `isSuffixOf` str - then take ((length str) - 6) str ++ "\\sa180\\par}\n" + then take (length str - 6) str ++ "\\sa180\\par}\n" else str -- | Convert list item (list of blocks) to RTF. diff --git a/src/Text/Pandoc/Writers/TEI.hs b/src/Text/Pandoc/Writers/TEI.hs index aa87c55e1..8e9d155fa 100644 --- a/src/Text/Pandoc/Writers/TEI.hs +++ b/src/Text/Pandoc/Writers/TEI.hs @@ -159,7 +159,7 @@ blockToTEI opts (Div (ident,_,_) [Para lst]) = do let attribs = [("id", ident) | not (null ident)] inTags False "p" attribs <$> inlinesToTEI opts lst blockToTEI opts (Div _ bs) = blocksToTEI opts $ map plainToPara bs -blockToTEI _ h@(Header{}) = do +blockToTEI _ h@Header{} = do -- should not occur after hierarchicalize, except inside lists/blockquotes report $ BlockNotRendered h return empty diff --git a/src/Text/Pandoc/Writers/ZimWiki.hs b/src/Text/Pandoc/Writers/ZimWiki.hs index 29849aa51..30317db73 100644 --- a/src/Text/Pandoc/Writers/ZimWiki.hs +++ b/src/Text/Pandoc/Writers/ZimWiki.hs @@ -142,7 +142,7 @@ blockToZimWiki _ (CodeBlock (_,classes,_) str) = do return $ case classes of [] -> "'''\n" ++ cleanupCode str ++ "\n'''\n" -- turn no lang block into a quote block (x:_) -> "{{{code: lang=\"" ++ - (fromMaybe x (Map.lookup x langmap)) ++ "\" linenumbers=\"True\"\n" ++ str ++ "\n}}}\n" -- for zim's code plugin, go verbatim on the lang spec + fromMaybe x (Map.lookup x langmap) ++ "\" linenumbers=\"True\"\n" ++ str ++ "\n}}}\n" -- for zim's code plugin, go verbatim on the lang spec blockToZimWiki opts (BlockQuote blocks) = do contents <- blockListToZimWiki opts blocks @@ -156,7 +156,7 @@ blockToZimWiki opts (Table capt aligns _ headers rows) = do return $ "" ++ c ++ "\n" headers' <- if all null headers then zipWithM (tableItemToZimWiki opts) aligns (head rows) - else mapM ((inlineListToZimWiki opts) . removeFormatting)headers -- emphasis, links etc. are not allowed in table headers + else mapM (inlineListToZimWiki opts . removeFormatting)headers -- emphasis, links etc. are not allowed in table headers rows' <- mapM (zipWithM (tableItemToZimWiki opts) aligns) rows let widths = map (maximum . map length) $ transpose (headers':rows') let padTo (width, al) s = -- cgit v1.2.3 From ed3d46638425825de30aaa3d1152b9343292c315 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 1 Nov 2017 09:27:51 -0700 Subject: Really fix #3989. The previous fix only worked in certain cases. Other cases with `>` in an HTML attribute broke. --- src/Text/Pandoc/Readers/HTML.hs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index e2be1c5bd..2b667c63c 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -1133,6 +1133,7 @@ htmlTag :: (HasReaderOptions st, Monad m) -> ParserT [Char] st m (Tag String, String) htmlTag f = try $ do lookAhead (char '<') + startpos <- getPosition inp <- getInput let ts = canonicalizeTags $ parseTagsOptions parseOptions{ optTagWarning = False @@ -1153,11 +1154,17 @@ htmlTag f = try $ do [] -> False (c:cs) -> isLetter c && all isNameChar cs - let endAngle = try $ do char '>' - pos <- getPosition - guard $ (sourceLine pos == ln && - sourceColumn pos >= col) || - sourceLine pos > ln + let endpos = if ln == 1 + then setSourceColumn startpos + (sourceColumn startpos + (col - 1)) + else setSourceColumn (setSourceLine startpos + (sourceLine startpos + (ln - 1))) + col + let endAngle = try $ + do char '>' + pos <- getPosition + guard $ pos >= endpos + let handleTag tagname = do -- basic sanity check, since the parser is very forgiving -- and finds tags in stuff like x<y) -- cgit v1.2.3 From 52e372b71d8ac5168025ad7ec2e9623c74c2dd93 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 1 Nov 2017 17:42:33 -0700 Subject: SelfContained: use base64 for css links with media attribute. This fixes `--self-contained` with s5. Closes #4026. --- src/Text/Pandoc/SelfContained.hs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/SelfContained.hs b/src/Text/Pandoc/SelfContained.hs index d3b768109..7cdd6f6e1 100644 --- a/src/Text/Pandoc/SelfContained.hs +++ b/src/Text/Pandoc/SelfContained.hs @@ -124,6 +124,7 @@ convertTags (t@(TagOpen "link" as):ts) = rest Right (mime, bs) | "text/css" `isPrefixOf` mime + && null (fromAttrib "media" t) && not ("</" `B.isInfixOf` bs) -> do rest <- convertTags $ dropWhile (==TagClose "link") ts -- cgit v1.2.3 From a553baf3a46e29d5137a8051838153c4fc83c6d5 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Thu, 2 Nov 2017 15:28:53 +0300 Subject: hlint --- src/Text/Pandoc/Readers/Markdown.hs | 22 +++++++++++----------- src/Text/Pandoc/Readers/Odt/Arrows/State.hs | 2 +- src/Text/Pandoc/Readers/Odt/ContentReader.hs | 8 ++++---- src/Text/Pandoc/Readers/Textile.hs | 9 ++++----- src/Text/Pandoc/Readers/TikiWiki.hs | 8 +++----- src/Text/Pandoc/Readers/Txt2Tags.hs | 2 +- src/Text/Pandoc/Writers/AsciiDoc.hs | 2 +- 7 files changed, 25 insertions(+), 28 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 98552e65d..8fc92f7e8 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -1419,7 +1419,7 @@ tableWith headerParser rowParser lineParser footerParser = try $ do lines' <- fmap sequence $ rowParser indices `sepEndBy1` lineParser footerParser numColumns <- getOption readerColumns - let widths = if indices == [] + let widths = if null indices then replicate (length aligns) 0.0 else widthsFromIndices numColumns indices return (aligns, widths, heads, lines') @@ -2006,7 +2006,7 @@ cite = do guardEnabled Ext_citations textualCite <|> do (cs, raw) <- withRaw normalCite - return $ (flip B.cite (B.text raw)) <$> cs + return $ flip B.cite (B.text raw) <$> cs textualCite :: PandocMonad m => MarkdownParser m (F Inlines) textualCite = try $ do @@ -2093,15 +2093,15 @@ citation = try $ do return $ do x <- pref y <- suff - return $ Citation{ citationId = key - , citationPrefix = B.toList x - , citationSuffix = B.toList y - , citationMode = if suppress_author - then SuppressAuthor - else NormalCitation - , citationNoteNum = 0 - , citationHash = 0 - } + return Citation{ citationId = key + , citationPrefix = B.toList x + , citationSuffix = B.toList y + , citationMode = if suppress_author + then SuppressAuthor + else NormalCitation + , citationNoteNum = 0 + , citationHash = 0 + } smart :: PandocMonad m => MarkdownParser m (F Inlines) smart = do diff --git a/src/Text/Pandoc/Readers/Odt/Arrows/State.hs b/src/Text/Pandoc/Readers/Odt/Arrows/State.hs index 06b2dcaaa..73bed545e 100644 --- a/src/Text/Pandoc/Readers/Odt/Arrows/State.hs +++ b/src/Text/Pandoc/Readers/Odt/Arrows/State.hs @@ -82,7 +82,7 @@ tryModifyState f = ArrowState $ \(state,a) instance Cat.Category (ArrowState s) where id = ArrowState id - arrow2 . arrow1 = ArrowState $ (runArrowState arrow2).(runArrowState arrow1) + arrow2 . arrow1 = ArrowState $ runArrowState arrow2 . runArrowState arrow1 instance Arrow (ArrowState state) where arr = ignoringState diff --git a/src/Text/Pandoc/Readers/Odt/ContentReader.hs b/src/Text/Pandoc/Readers/Odt/ContentReader.hs index 44bd89278..4189d5aaa 100644 --- a/src/Text/Pandoc/Readers/Odt/ContentReader.hs +++ b/src/Text/Pandoc/Readers/Odt/ContentReader.hs @@ -293,7 +293,7 @@ withNewStyle a = proc x -> do modifier <- arr modifierFromStyleDiff -< triple fShouldTrace <- isStyleToTrace -< style case fShouldTrace of - Right shouldTrace -> do + Right shouldTrace -> if shouldTrace then do pushStyle -< style @@ -357,7 +357,7 @@ modifierFromStyleDiff propertyTriple = hasChangedM property triple@(_, textProps,_) = fromMaybe False $ (/=) <$> property textProps <*> lookupPreviousValueM property triple - lookupPreviousValue f = lookupPreviousStyleValue ((fmap f).textProperties) + lookupPreviousValue f = lookupPreviousStyleValue (fmap f . textProperties) lookupPreviousValueM f = lookupPreviousStyleValue ((f =<<).textProperties) @@ -803,9 +803,9 @@ read_frame_text_box = matchingElement NsDraw "text-box" arr read_img_with_caption -< toList paragraphs read_img_with_caption :: [Block] -> Inlines -read_img_with_caption ((Para ((Image attr alt (src,title)) : [])) : _) = +read_img_with_caption ((Para [Image attr alt (src,title)]) : _) = singleton (Image attr alt (src, 'f':'i':'g':':':title)) -- no text, default caption -read_img_with_caption ((Para ((Image attr _ (src,title)) : txt)) : _) = +read_img_with_caption (Para (Image attr _ (src,title) : txt) : _) = singleton (Image attr txt (src, 'f':'i':'g':':':title) ) -- override caption with the text that follows read_img_with_caption ( (Para (_ : xs)) : ys) = read_img_with_caption ((Para xs) : ys) diff --git a/src/Text/Pandoc/Readers/Textile.hs b/src/Text/Pandoc/Readers/Textile.hs index f8c2b8cb7..a3b4f2ff1 100644 --- a/src/Text/Pandoc/Readers/Textile.hs +++ b/src/Text/Pandoc/Readers/Textile.hs @@ -387,7 +387,7 @@ table = try $ do char '.' rawcapt <- trim <$> anyLine parseFromString' (mconcat <$> many inline) rawcapt - rawrows <- many1 $ (skipMany ignorableRow) >> tableRow + rawrows <- many1 $ skipMany ignorableRow >> tableRow skipMany ignorableRow blanklines let (headers, rows) = case rawrows of @@ -438,8 +438,7 @@ maybeExplicitBlock name blk = try $ do -- | Any inline element inline :: PandocMonad m => ParserT [Char] ParserState m Inlines -inline = do - choice inlineParsers <?> "inline" +inline = choice inlineParsers <?> "inline" -- | Inline parsers tried in order inlineParsers :: PandocMonad m => [ParserT [Char] ParserState m Inlines] @@ -610,7 +609,7 @@ escapedInline = escapedEqs <|> escapedTag escapedEqs :: PandocMonad m => ParserT [Char] ParserState m Inlines escapedEqs = B.str <$> - (try $ string "==" *> manyTill anyChar' (try $ string "==")) + try (string "==" *> manyTill anyChar' (try $ string "==")) -- | literal text escaped btw <notextile> tags escapedTag :: PandocMonad m => ParserT [Char] ParserState m Inlines @@ -643,7 +642,7 @@ code2 = do -- | Html / CSS attributes attributes :: PandocMonad m => ParserT [Char] ParserState m Attr -attributes = (foldl (flip ($)) ("",[],[])) <$> +attributes = foldl (flip ($)) ("",[],[]) <$> try (do special <- option id specialAttribute attrs <- many attribute return (special : attrs)) diff --git a/src/Text/Pandoc/Readers/TikiWiki.hs b/src/Text/Pandoc/Readers/TikiWiki.hs index ad35a6935..4a66cc13d 100644 --- a/src/Text/Pandoc/Readers/TikiWiki.hs +++ b/src/Text/Pandoc/Readers/TikiWiki.hs @@ -501,7 +501,7 @@ escapedChar = try $ do string "~" inner <- many1 $ oneOf "0123456789" string "~" - return $B.str [(toEnum ((read inner) :: Int)) :: Char] + return $B.str [(toEnum (read inner :: Int)) :: Char] -- UNSUPPORTED, as there doesn't seem to be any facility in calibre -- for this @@ -587,8 +587,7 @@ macroAttr = try $ do return (key, value) macroAttrs :: PandocMonad m => TikiWikiParser m [(String, String)] -macroAttrs = try $ do - sepEndBy macroAttr spaces +macroAttrs = try $ sepEndBy macroAttr spaces -- ~np~ __not bold__ ~/np~ noparse :: PandocMonad m => TikiWikiParser m B.Inlines @@ -641,8 +640,7 @@ wikiLinkText start middle end = do where linkContent = do char '|' - mystr <- many (noneOf middle) - return mystr + many (noneOf middle) externalLink :: PandocMonad m => TikiWikiParser m B.Inlines externalLink = makeLink "[" "]|" "]" diff --git a/src/Text/Pandoc/Readers/Txt2Tags.hs b/src/Text/Pandoc/Readers/Txt2Tags.hs index fdf7a827a..3fc54aaab 100644 --- a/src/Text/Pandoc/Readers/Txt2Tags.hs +++ b/src/Text/Pandoc/Readers/Txt2Tags.hs @@ -560,7 +560,7 @@ endline = try $ do notFollowedBy quote notFollowedBy list notFollowedBy table - return $ B.softbreak + return B.softbreak str :: T2T Inlines str = try $ do diff --git a/src/Text/Pandoc/Writers/AsciiDoc.hs b/src/Text/Pandoc/Writers/AsciiDoc.hs index bf58a755f..3231e1e30 100644 --- a/src/Text/Pandoc/Writers/AsciiDoc.hs +++ b/src/Text/Pandoc/Writers/AsciiDoc.hs @@ -219,7 +219,7 @@ blockToAsciiDoc opts (Table caption aligns widths headers rows) = do AlignCenter -> "^" AlignRight -> ">" AlignDefault -> "") ++ - if wi == 0 then "" else (show wi ++ "%") + if wi == 0 then "" else show wi ++ "%" let headerspec = if all null headers then empty else text "options=\"header\"," -- cgit v1.2.3 From 6d00e6e8c3d483cc36afe2c065cdfdd4ef811f85 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 2 Nov 2017 10:23:04 -0700 Subject: Fixed revealjs slide column width issues. * Remove "width" attribute which is not allowed on div. * Remove space between `<div class="column">` elements, since this prevents columns whose widths sum to 100% (the space takes up space). Closes #4028. --- src/Text/Pandoc/Writers/HTML.hs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 9c5dfccf8..89cec38a2 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -655,7 +655,7 @@ blockToHtml opts (LineBlock lns) = return $ H.div ! A.class_ "line-block" $ htmlLines blockToHtml opts (Div attr@(ident, classes, kvs') bs) = do html5 <- gets stHtml5 - let kvs = kvs' ++ + let kvs = [(k,v) | (k,v) <- kvs', k /= "width"] ++ if "column" `elem` classes then let w = fromMaybe "48%" (lookup "width" kvs') in [("style", "width:" ++ w ++ ";min-width:" ++ w ++ @@ -664,7 +664,12 @@ blockToHtml opts (Div attr@(ident, classes, kvs') bs) = do 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 + contents <- if "columns" `elem` classes + then -- we don't use blockListToHtml because it inserts + -- a newline between the column divs, which throws + -- off widths! see #4028 + mconcat <$> mapM (blockToHtml opts) bs + else blockListToHtml opts' bs let contents' = nl opts >> contents >> nl opts let (divtag, classes') = if html5 && "section" `elem` classes then (H5.section, filter (/= "section") classes) -- cgit v1.2.3 From 856587ff63b1e89c71b73a367f5ba1730050e6dd Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 2 Nov 2017 16:02:04 -0700 Subject: Use latest skylighting; ensure no duplicate ids on code lines. The line identifiers are built using the code block's identifier as a prefix. If the code block has null identifier, we use "cb1", "cb2", etc. Closes #4031. --- src/Text/Pandoc/Highlighting.hs | 7 +++++-- src/Text/Pandoc/Writers/HTML.hs | 14 +++++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Highlighting.hs b/src/Text/Pandoc/Highlighting.hs index cc72967e4..cd8c5fd4b 100644 --- a/src/Text/Pandoc/Highlighting.hs +++ b/src/Text/Pandoc/Highlighting.hs @@ -81,12 +81,15 @@ highlight :: SyntaxMap -> Attr -- ^ Attributes of the CodeBlock -> String -- ^ Raw contents of the CodeBlock -> Either String a -highlight syntaxmap formatter (_, classes, keyvals) rawCode = +highlight syntaxmap formatter (ident, classes, keyvals) rawCode = let firstNum = fromMaybe 1 (safeRead (fromMaybe "1" $ lookup "startFrom" keyvals)) fmtOpts = defaultFormatOpts{ startNumber = firstNum, numberLines = any (`elem` - ["number","numberLines", "number-lines"]) classes } + ["number","numberLines", "number-lines"]) classes, + lineIdPrefix = if null ident + then mempty + else T.pack (ident ++ "-") } tokenizeOpts = TokenizerConfig{ syntaxMap = syntaxmap , traceOutput = False } classes' = map T.pack classes diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 89cec38a2..1999bdbcf 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -101,6 +101,7 @@ data WriterState = WriterState , stHtml5 :: Bool -- ^ Use HTML5 , stEPUBVersion :: Maybe EPUBVersion -- ^ EPUB version if for epub , stSlideVariant :: HTMLSlideVariant + , stCodeBlockNum :: Int -- ^ Number of code block } defaultWriterState :: WriterState @@ -108,7 +109,8 @@ defaultWriterState = WriterState {stNotes= [], stMath = False, stQuotes = False, stHighlighting = False, stSecNum = [], stElement = False, stHtml5 = False, stEPUBVersion = Nothing, - stSlideVariant = NoSlides} + stSlideVariant = NoSlides, + stCodeBlockNum = 0} -- Helpers to render HTML with the appropriate function. @@ -703,6 +705,12 @@ blockToHtml _ HorizontalRule = do html5 <- gets stHtml5 return $ if html5 then H5.hr else H.hr blockToHtml opts (CodeBlock (id',classes,keyvals) rawCode) = do + id'' <- if null id' + then do + modify $ \st -> st{ stCodeBlockNum = stCodeBlockNum st + 1 } + codeblocknum <- gets stCodeBlockNum + return ("cb" ++ show codeblocknum) + else return id' let tolhs = isEnabled Ext_literate_haskell opts && any (\c -> map toLower c == "haskell") classes && any (\c -> map toLower c == "literate") classes @@ -716,7 +724,7 @@ blockToHtml opts (CodeBlock (id',classes,keyvals) rawCode) = do else rawCode hlCode = if isJust (writerHighlightStyle opts) then highlight (writerSyntaxMap opts) formatHtmlBlock - (id',classes',keyvals) adjCode + (id'',classes',keyvals) adjCode else Left "" case hlCode of Left msg -> do @@ -725,7 +733,7 @@ blockToHtml opts (CodeBlock (id',classes,keyvals) rawCode) = do addAttrs opts (id',classes,keyvals) $ H.pre $ H.code $ toHtml adjCode Right h -> modify (\st -> st{ stHighlighting = True }) >> - addAttrs opts (id',[],keyvals) h + addAttrs opts (id'',[],keyvals) h blockToHtml opts (BlockQuote blocks) = do -- in S5, treat list in blockquote specially -- if default is incremental, make it nonincremental; -- cgit v1.2.3 From 5dfe131ee07065dc7b68912d9a068508e29e18a4 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 2 Nov 2017 16:38:06 -0700 Subject: Support `lineAnchors` (or `line-anchors`) in code blocks, for HTML. --- src/Text/Pandoc/Highlighting.hs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Highlighting.hs b/src/Text/Pandoc/Highlighting.hs index cd8c5fd4b..9c90b229e 100644 --- a/src/Text/Pandoc/Highlighting.hs +++ b/src/Text/Pandoc/Highlighting.hs @@ -85,6 +85,8 @@ highlight syntaxmap formatter (ident, classes, keyvals) rawCode = let firstNum = fromMaybe 1 (safeRead (fromMaybe "1" $ lookup "startFrom" keyvals)) fmtOpts = defaultFormatOpts{ startNumber = firstNum, + lineAnchors = any (`elem` + ["line-anchors", "lineAnchors"]) classes, numberLines = any (`elem` ["number","numberLines", "number-lines"]) classes, lineIdPrefix = if null ident -- cgit v1.2.3 From 642d603666521157d386e2a87bfdcca11d479b87 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 2 Nov 2017 20:55:29 -0700 Subject: Improved support for columns in HTML. * Move as much as possible to the CSS in the template. * Ensure that all the HTML-based templates (including epub) contain the CSS for columns. * Columns default to 50% width unless they are given a width attribute. Closes #4028. --- src/Text/Pandoc/Writers/HTML.hs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 1999bdbcf..2dc8b7a61 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -658,11 +658,8 @@ blockToHtml opts (LineBlock lns) = blockToHtml opts (Div attr@(ident, classes, kvs') bs) = do html5 <- gets stHtml5 let kvs = [(k,v) | (k,v) <- kvs', k /= "width"] ++ - if "column" `elem` classes - then let w = fromMaybe "48%" (lookup "width" kvs') - in [("style", "width:" ++ w ++ ";min-width:" ++ w ++ - ";vertical-align:top;")] - else [] + [("style", "width:" ++ w ++ ";") + | ("width",w) <- kvs', "column" `elem` classes] 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 -- cgit v1.2.3 From 8e53489cbca3f230eed94294af3810d2447db2af Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 4 Nov 2017 10:35:52 -0700 Subject: Fix strikethrough in gfm writer. Previously we got a crash, because we were trying to print a native cmark STRIKETHROUGH node, and the commonmark writer in cmark-github doesn't support this. Work around this by using a raw node to add the strikethrough delimiters. Closes #4038. --- src/Text/Pandoc/Writers/CommonMark.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/CommonMark.hs b/src/Text/Pandoc/Writers/CommonMark.hs index e6d297291..8677dd840 100644 --- a/src/Text/Pandoc/Writers/CommonMark.hs +++ b/src/Text/Pandoc/Writers/CommonMark.hs @@ -242,7 +242,7 @@ inlineToNodes opts (Emph xs) = (node EMPH (inlinesToNodes opts xs) :) inlineToNodes opts (Strong xs) = (node STRONG (inlinesToNodes opts xs) :) inlineToNodes opts (Strikeout xs) = if isEnabled Ext_strikeout opts - then (node STRIKETHROUGH (inlinesToNodes opts xs) :) + then (node (CUSTOM_INLINE "~~" "~~") (inlinesToNodes opts xs) :) else ((node (HTML_INLINE (T.pack "<s>")) [] : inlinesToNodes opts xs ++ [node (HTML_INLINE (T.pack "</s>")) []]) ++ ) inlineToNodes opts (Superscript xs) = -- cgit v1.2.3 From 1a81751cef330d875cc34f11cde4a0d478969db7 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 4 Nov 2017 11:25:38 -0700 Subject: Better indentation under headers in org mode output. See #4036. Close examination by org experts needed, to ensure that nothing breaks. --- src/Text/Pandoc/Writers/Org.hs | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index f73822b86..47f63f591 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -77,7 +77,7 @@ pandocToOrg (Pandoc meta blocks) = do (fmap render' . blockListToOrg) (fmap render' . inlineListToOrg) meta - body <- blockListToOrg blocks + body <- vcat <$> mapM (elementToOrg 0) (hierarchicalize blocks) notes <- gets (reverse . stNotes) >>= notesToOrg hasMath <- gets stHasMath let main = render colwidth . foldl ($+$) empty $ [body, notes] @@ -96,9 +96,9 @@ notesToOrg notes = -- | Return Org representation of a note. noteToOrg :: PandocMonad m => Int -> [Block] -> Org m Doc noteToOrg num note = do - contents <- blockListToOrg note + contents <- vcat <$> mapM (elementToOrg 0) (hierarchicalize note) let marker = "[fn:" ++ show num ++ "] " - return $ hang (length marker) (text marker) contents + return $ hang (length marker) (text marker) $ contents -- | Escape special characters for Org. escapeString :: String -> String @@ -113,6 +113,18 @@ isRawFormat :: Format -> Bool isRawFormat f = f == Format "latex" || f == Format "tex" || f == Format "org" +elementToOrg :: PandocMonad m + => Int -> Element -> Org m Doc +elementToOrg nestlevel (Blk block) = do + contents <- blockToOrg block + if isEmpty contents + then return empty + else return $ nest nestlevel contents $$ blankline +elementToOrg _nestlevel (Sec level _num attr title' elements) = do + hdr <- blockToOrg (Header level attr title') + body <- vcat <$> mapM (elementToOrg (level + 1)) elements + return $ hdr $$ body + -- | Convert Pandoc block element to Org. blockToOrg :: PandocMonad m => Block -- ^ Block element @@ -140,14 +152,14 @@ blockToOrg (Div (ident, classes, kv) bs) = do (blockType:classes'') -> blankline $$ attrHtml (ident, classes'' <> classes', kv) $$ "#+BEGIN_" <> text blockType $$ contents $$ - "#+END_" <> text blockType $$ blankline + "#+END_" <> text blockType _ -> -- fallback with id: add id as an anchor if present, discard classes and -- key-value pairs, unwrap the content. let contents' = if not (null ident) then "<<" <> text ident <> ">>" $$ contents else contents - in blankline $$ contents' $$ blankline + in blankline $$ contents' blockToOrg (Plain inlines) = inlineListToOrg inlines -- title beginning with fig: indicates that the image is a figure blockToOrg (Para [Image attr txt (src,'f':'i':'g':':':tit)]) = do @@ -155,7 +167,7 @@ blockToOrg (Para [Image attr txt (src,'f':'i':'g':':':tit)]) = do then return empty else ("#+CAPTION: " <>) `fmap` inlineListToOrg txt img <- inlineToOrg (Image attr txt (src,tit)) - return $ capt $$ img $$ blankline + return $ capt $$ img blockToOrg (Para inlines) = do contents <- inlineListToOrg inlines return $ contents <> blankline @@ -172,13 +184,13 @@ blockToOrg (LineBlock lns) = do nest 2 contents $$ "#+END_VERSE" <> blankline blockToOrg (RawBlock "html" str) = return $ blankline $$ "#+BEGIN_HTML" $$ - nest 2 (text str) $$ "#+END_HTML" $$ blankline + nest 2 (text str) $$ "#+END_HTML" blockToOrg b@(RawBlock f str) | isRawFormat f = return $ text str | otherwise = do report $ BlockNotRendered b return empty -blockToOrg HorizontalRule = return $ blankline $$ "--------------" $$ blankline +blockToOrg HorizontalRule = return $ blankline $$ "--------------" blockToOrg (Header level attr inlines) = do contents <- inlineListToOrg inlines let headerStr = text $ if level > 999 then " " else replicate level '*' @@ -193,11 +205,11 @@ blockToOrg (CodeBlock (_,classes,_) str) = do let (beg, end) = case at of [] -> ("#+BEGIN_EXAMPLE", "#+END_EXAMPLE") (x:_) -> ("#+BEGIN_SRC " ++ x, "#+END_SRC") - return $ text beg $$ nest tabstop (text str) $$ text end $$ blankline + return $ text beg $$ nest tabstop (text str) $$ text end blockToOrg (BlockQuote blocks) = do contents <- blockListToOrg blocks return $ blankline $$ "#+BEGIN_QUOTE" $$ - nest 2 contents $$ "#+END_QUOTE" $$ blankline + nest 2 contents $$ "#+END_QUOTE" blockToOrg (Table caption' _ _ headers rows) = do caption'' <- inlineListToOrg caption' let caption = if null caption' @@ -228,11 +240,11 @@ blockToOrg (Table caption' _ _ headers rows) = do let head'' = if all null headers then empty else head' $$ border '-' - return $ head'' $$ body $$ caption $$ blankline + return $ head'' $$ body $$ caption blockToOrg (BulletList items) = do contents <- mapM bulletListItemToOrg items -- ensure that sublists have preceding blank line - return $ blankline $+$ vcat contents $$ blankline + return $ blankline $+$ vcat contents blockToOrg (OrderedList (start, _, delim) items) = do let delim' = case delim of TwoParens -> OneParen @@ -244,10 +256,10 @@ blockToOrg (OrderedList (start, _, delim) items) = do in m ++ replicate s ' ') markers contents <- zipWithM orderedListItemToOrg markers' items -- ensure that sublists have preceding blank line - return $ blankline $$ vcat contents $$ blankline + return $ blankline $$ vcat contents blockToOrg (DefinitionList items) = do contents <- mapM definitionListItemToOrg items - return $ vcat contents $$ blankline + return $ vcat contents -- | Convert bullet list item (list of blocks) to Org. bulletListItemToOrg :: PandocMonad m => [Block] -> Org m Doc -- cgit v1.2.3 From fe42c175ebf105b32f342a1609417ce632f317e1 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 4 Nov 2017 11:32:47 -0700 Subject: Revert "Better indentation under headers in org mode output." This reverts commit 1a81751cef330d875cc34f11cde4a0d478969db7. --- src/Text/Pandoc/Writers/Org.hs | 40 ++++++++++++++-------------------------- 1 file changed, 14 insertions(+), 26 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index 47f63f591..f73822b86 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -77,7 +77,7 @@ pandocToOrg (Pandoc meta blocks) = do (fmap render' . blockListToOrg) (fmap render' . inlineListToOrg) meta - body <- vcat <$> mapM (elementToOrg 0) (hierarchicalize blocks) + body <- blockListToOrg blocks notes <- gets (reverse . stNotes) >>= notesToOrg hasMath <- gets stHasMath let main = render colwidth . foldl ($+$) empty $ [body, notes] @@ -96,9 +96,9 @@ notesToOrg notes = -- | Return Org representation of a note. noteToOrg :: PandocMonad m => Int -> [Block] -> Org m Doc noteToOrg num note = do - contents <- vcat <$> mapM (elementToOrg 0) (hierarchicalize note) + contents <- blockListToOrg note let marker = "[fn:" ++ show num ++ "] " - return $ hang (length marker) (text marker) $ contents + return $ hang (length marker) (text marker) contents -- | Escape special characters for Org. escapeString :: String -> String @@ -113,18 +113,6 @@ isRawFormat :: Format -> Bool isRawFormat f = f == Format "latex" || f == Format "tex" || f == Format "org" -elementToOrg :: PandocMonad m - => Int -> Element -> Org m Doc -elementToOrg nestlevel (Blk block) = do - contents <- blockToOrg block - if isEmpty contents - then return empty - else return $ nest nestlevel contents $$ blankline -elementToOrg _nestlevel (Sec level _num attr title' elements) = do - hdr <- blockToOrg (Header level attr title') - body <- vcat <$> mapM (elementToOrg (level + 1)) elements - return $ hdr $$ body - -- | Convert Pandoc block element to Org. blockToOrg :: PandocMonad m => Block -- ^ Block element @@ -152,14 +140,14 @@ blockToOrg (Div (ident, classes, kv) bs) = do (blockType:classes'') -> blankline $$ attrHtml (ident, classes'' <> classes', kv) $$ "#+BEGIN_" <> text blockType $$ contents $$ - "#+END_" <> text blockType + "#+END_" <> text blockType $$ blankline _ -> -- fallback with id: add id as an anchor if present, discard classes and -- key-value pairs, unwrap the content. let contents' = if not (null ident) then "<<" <> text ident <> ">>" $$ contents else contents - in blankline $$ contents' + in blankline $$ contents' $$ blankline blockToOrg (Plain inlines) = inlineListToOrg inlines -- title beginning with fig: indicates that the image is a figure blockToOrg (Para [Image attr txt (src,'f':'i':'g':':':tit)]) = do @@ -167,7 +155,7 @@ blockToOrg (Para [Image attr txt (src,'f':'i':'g':':':tit)]) = do then return empty else ("#+CAPTION: " <>) `fmap` inlineListToOrg txt img <- inlineToOrg (Image attr txt (src,tit)) - return $ capt $$ img + return $ capt $$ img $$ blankline blockToOrg (Para inlines) = do contents <- inlineListToOrg inlines return $ contents <> blankline @@ -184,13 +172,13 @@ blockToOrg (LineBlock lns) = do nest 2 contents $$ "#+END_VERSE" <> blankline blockToOrg (RawBlock "html" str) = return $ blankline $$ "#+BEGIN_HTML" $$ - nest 2 (text str) $$ "#+END_HTML" + nest 2 (text str) $$ "#+END_HTML" $$ blankline blockToOrg b@(RawBlock f str) | isRawFormat f = return $ text str | otherwise = do report $ BlockNotRendered b return empty -blockToOrg HorizontalRule = return $ blankline $$ "--------------" +blockToOrg HorizontalRule = return $ blankline $$ "--------------" $$ blankline blockToOrg (Header level attr inlines) = do contents <- inlineListToOrg inlines let headerStr = text $ if level > 999 then " " else replicate level '*' @@ -205,11 +193,11 @@ blockToOrg (CodeBlock (_,classes,_) str) = do let (beg, end) = case at of [] -> ("#+BEGIN_EXAMPLE", "#+END_EXAMPLE") (x:_) -> ("#+BEGIN_SRC " ++ x, "#+END_SRC") - return $ text beg $$ nest tabstop (text str) $$ text end + return $ text beg $$ nest tabstop (text str) $$ text end $$ blankline blockToOrg (BlockQuote blocks) = do contents <- blockListToOrg blocks return $ blankline $$ "#+BEGIN_QUOTE" $$ - nest 2 contents $$ "#+END_QUOTE" + nest 2 contents $$ "#+END_QUOTE" $$ blankline blockToOrg (Table caption' _ _ headers rows) = do caption'' <- inlineListToOrg caption' let caption = if null caption' @@ -240,11 +228,11 @@ blockToOrg (Table caption' _ _ headers rows) = do let head'' = if all null headers then empty else head' $$ border '-' - return $ head'' $$ body $$ caption + return $ head'' $$ body $$ caption $$ blankline blockToOrg (BulletList items) = do contents <- mapM bulletListItemToOrg items -- ensure that sublists have preceding blank line - return $ blankline $+$ vcat contents + return $ blankline $+$ vcat contents $$ blankline blockToOrg (OrderedList (start, _, delim) items) = do let delim' = case delim of TwoParens -> OneParen @@ -256,10 +244,10 @@ blockToOrg (OrderedList (start, _, delim) items) = do in m ++ replicate s ' ') markers contents <- zipWithM orderedListItemToOrg markers' items -- ensure that sublists have preceding blank line - return $ blankline $$ vcat contents + return $ blankline $$ vcat contents $$ blankline blockToOrg (DefinitionList items) = do contents <- mapM definitionListItemToOrg items - return $ vcat contents + return $ vcat contents $$ blankline -- | Convert bullet list item (list of blocks) to Org. bulletListItemToOrg :: PandocMonad m => [Block] -> Org m Doc -- cgit v1.2.3 From 8e8d7802eef373b458653fc87857672c2c376b05 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 4 Nov 2017 17:07:55 -0700 Subject: Logging: issue INFO, not WARNING, if LaTeX .sty file can't be read. Normally this is not a situation requiring a fix from the user, so a warning is inappropriate. --- src/Text/Pandoc/Logging.hs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs index 7f4ae2ada..016e64f6c 100644 --- a/src/Text/Pandoc/Logging.hs +++ b/src/Text/Pandoc/Logging.hs @@ -45,6 +45,7 @@ import Data.Aeson.Encode.Pretty (Config (..), defConfig, encodePretty', keyOrder) import qualified Data.ByteString.Lazy as BL import Data.Data (Data, toConstr) +import Data.List (isSuffixOf) import qualified Data.Text as Text import Data.Typeable (Typeable) import GHC.Generics (Generic) @@ -315,7 +316,9 @@ messageVerbosity msg = ReferenceNotFound{} -> WARNING CircularReference{} -> WARNING UndefinedToggle{} -> WARNING - CouldNotLoadIncludeFile{} -> WARNING + CouldNotLoadIncludeFile f _ + | ".sty" `isSuffixOf` f -> INFO + | otherwise -> WARNING MacroAlreadyDefined{} -> WARNING ParsingUnescaped{} -> INFO InlineNotRendered{} -> INFO -- cgit v1.2.3 From 38ad0b32041aed9e5459b79b6e97af5d6ba74985 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 6 Nov 2017 06:56:21 +0300 Subject: Spellcheck comments --- src/Text/Pandoc/Readers/Odt/ContentReader.hs | 4 ++-- src/Text/Pandoc/Readers/Odt/StyleReader.hs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Odt/ContentReader.hs b/src/Text/Pandoc/Readers/Odt/ContentReader.hs index 4189d5aaa..cc9b798b3 100644 --- a/src/Text/Pandoc/Readers/Odt/ContentReader.hs +++ b/src/Text/Pandoc/Readers/Odt/ContentReader.hs @@ -247,7 +247,7 @@ getPrettyAnchor = proc (baseIdent, uglyAnchor) -> do modifyExtraState (putPrettyAnchor uglyAnchor newPretty) -<< newPretty -- | Input: basis for a new header anchor --- Ouput: saved new anchor +-- Output: saved new anchor getHeaderAnchor :: OdtReaderSafe Inlines Anchor getHeaderAnchor = proc title -> do state <- getExtraState -< () @@ -566,7 +566,7 @@ read_text_seq = matchingElement NsText "sequence" -- specifically. I honor that, although the current implementation of '(<>)' --- for 'Inlines' in "Text.Pandoc.Builder" will collaps them agein. +-- for 'Inlines' in "Text.Pandoc.Builder" will collapse them again. -- The rational is to be prepared for future modifications. read_spaces :: InlineMatcher read_spaces = matchingElement NsText "s" ( diff --git a/src/Text/Pandoc/Readers/Odt/StyleReader.hs b/src/Text/Pandoc/Readers/Odt/StyleReader.hs index 1384072d1..6129c1664 100644 --- a/src/Text/Pandoc/Readers/Odt/StyleReader.hs +++ b/src/Text/Pandoc/Readers/Odt/StyleReader.hs @@ -104,7 +104,7 @@ instance Lookupable FontPitch where instance Default FontPitch where def = PitchVariable --- The font pitch can be specifed in a style directly. Normally, however, +-- The font pitch can be specified in a style directly. Normally, however, -- it is defined in the font. That is also the specs' recommendation. -- -- Thus, we want @@ -340,8 +340,8 @@ instance Read XslUnit where readsPrec _ "em" = [(XslUnitEM , "")] readsPrec _ _ = [] --- | Rough conversion of measures into millimeters. --- Pixels and em's are actually implemetation dependant/relative measures, +-- | Rough conversion of measures into millimetres. +-- Pixels and em's are actually implementation dependant/relative measures, -- so I could not really easily calculate anything exact here even if I wanted. -- But I do not care about exactness right now, as I only use measures -- to determine if a paragraph is "indented" or not. @@ -425,7 +425,7 @@ instance Read ListItemNumberFormat where -------------------------------------------------------------------------------- -- Readers -- --- ...it seems like a whole lot of this should be automatically deriveable +-- ...it seems like a whole lot of this should be automatically derivable -- or at least moveable into a class. Most of this is data concealed in -- code. -------------------------------------------------------------------------------- -- cgit v1.2.3 From 36449d3ea4708009e474507de27077c1b936adad Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 7 Nov 2017 08:30:52 -0800 Subject: EPUB writer: fix image paths with empty `--epub-subdirectory`. --- src/Text/Pandoc/Writers/EPUB.hs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index 1129ac3f4..0dcef1d63 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -959,15 +959,21 @@ transformInline :: PandocMonad m => WriterOptions -> Inline -> E m Inline -transformInline _opts (Image attr lab (src,tit)) = do +transformInline opts (Image attr lab (src,tit)) = do newsrc <- modifyMediaRef src - return $ Image attr lab ("../" ++ newsrc, tit) + let pref = if null (writerEpubSubdirectory opts) + then "" + else "../" + return $ Image attr lab (pref ++ newsrc, tit) transformInline opts (x@(Math t m)) | WebTeX url <- writerHTMLMathMethod opts = do newsrc <- modifyMediaRef (url ++ urlEncode m) let mathclass = if t == DisplayMath then "display" else "inline" + let pref = if null (writerEpubSubdirectory opts) + then "" + else "../" return $ Span ("",["math",mathclass],[]) - [Image nullAttr [x] ("../" ++ newsrc, "")] + [Image nullAttr [x] (pref ++ newsrc, "")] transformInline _opts (RawInline fmt raw) | fmt == Format "html" = do let tags = parseTags raw -- cgit v1.2.3 From b0b90aba6260998579a1ee28a657614901865ba1 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 7 Nov 2017 12:24:37 -0800 Subject: EPUB writer: fixed EPUB OCF structure. The structure of the EPUBs was messed up, and #3720 was improperly implemented. This commit fixes things. --- src/Text/Pandoc/Writers/EPUB.hs | 147 ++++++++++++++++++++++------------------ 1 file changed, 82 insertions(+), 65 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index 0dcef1d63..23df046d0 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -48,7 +48,7 @@ import qualified Data.Set as Set import qualified Data.Text as TS import qualified Data.Text.Lazy as TL import Network.HTTP (urlEncode) -import System.FilePath (takeExtension, takeFileName) +import System.FilePath (takeExtension, takeFileName, makeRelative) import Text.HTML.TagSoup (Tag (TagOpen), fromAttrib, parseTags) import Text.Pandoc.Builder (fromList, setMeta) import Text.Pandoc.Class (PandocMonad, report) @@ -81,6 +81,7 @@ data Chapter = Chapter (Maybe [Int]) [Block] data EPUBState = EPUBState { stMediaPaths :: [(FilePath, (FilePath, Maybe Entry))] + , stEpubSubdir :: String } type E m = StateT EPUBState m @@ -149,6 +150,20 @@ removeNote :: Inline -> Inline removeNote (Note _) = Str "" removeNote x = x +mkEntry :: PandocMonad m => FilePath -> B.ByteString -> E m Entry +mkEntry path content = do + epubSubdir <- gets stEpubSubdir + let addEpubSubdir :: Entry -> Entry + addEpubSubdir e = e{ eRelativePath = + (if null epubSubdir + then "" + else epubSubdir ++ "/") ++ eRelativePath e } + epochtime <- floor <$> lift P.getPOSIXTime + return $ + (if path == "mimetype" || "META-INF" `isPrefixOf` path + then id + else addEpubSubdir) $ toEntry path epochtime content + getEPUBMetadata :: PandocMonad m => WriterOptions -> Meta -> E m EPUBMetadata getEPUBMetadata opts meta = do let md = metadataFromMeta opts meta @@ -366,11 +381,13 @@ writeEPUB :: PandocMonad m -> WriterOptions -- ^ Writer options -> Pandoc -- ^ Document to convert -> m B.ByteString -writeEPUB epubVersion opts doc = - let initState = EPUBState { stMediaPaths = [] } - in - evalStateT (pandocToEPUB epubVersion opts doc) - initState +writeEPUB epubVersion opts doc = do + let epubSubdir = writerEpubSubdirectory opts + -- sanity check on epubSubdir + unless (all (\c -> isAscii c && isAlphaNum c) epubSubdir) $ + throwError $ PandocEpubSubdirectoryError epubSubdir + let initState = EPUBState { stMediaPaths = [], stEpubSubdir = epubSubdir } + evalStateT (pandocToEPUB epubVersion opts doc) initState pandocToEPUB :: PandocMonad m => EPUBVersion @@ -378,27 +395,18 @@ pandocToEPUB :: PandocMonad m -> Pandoc -> E m B.ByteString pandocToEPUB version opts doc@(Pandoc meta _) = do - let epubSubdir = writerEpubSubdirectory opts - -- sanity check on epubSubdir - unless (all (\c -> isAscii c && isAlphaNum c) epubSubdir) $ - throwError $ PandocEpubSubdirectoryError epubSubdir - let inSubdir f = if null epubSubdir - then f - else epubSubdir ++ "/" ++ f - + epubSubdir <- gets stEpubSubdir let epub3 = version == EPUB3 let writeHtml o = fmap (UTF8.fromTextLazy . TL.fromStrict) . writeHtmlStringForEPUB version o - epochtime <- floor <$> lift P.getPOSIXTime metadata <- getEPUBMetadata opts meta - let mkEntry path content = toEntry path epochtime content -- stylesheet stylesheets <- case epubStylesheets metadata of [] -> (\x -> [B.fromChunks [x]]) <$> P.readDataFile "epub.css" fs -> mapM P.readFileLazy fs - let stylesheetEntries = zipWith + stylesheetEntries <- zipWithM (\bs n -> mkEntry ("styles/stylesheet" ++ show n ++ ".css") bs) stylesheets [(1 :: Int)..] @@ -406,10 +414,10 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do : [(x,y) | (x,y) <- writerVariables opts, x /= "css"] let cssvars useprefix = map (\e -> ("css", - (if useprefix && not (null epubSubdir) + (if useprefix then "../" else "") - ++ eRelativePath e)) + ++ makeRelative epubSubdir (eRelativePath e))) stylesheetEntries let opts' = opts{ writerEmailObfuscation = NoObfuscation @@ -430,18 +438,21 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do cpContent <- lift $ writeHtml opts'{ writerVariables = ("coverpage","true"): - cssvars False ++ vars } - (Pandoc meta [RawBlock (Format "html") $ "<div id=\"cover-image\">\n<img src=\"" ++ coverImage ++ "\" alt=\"cover image\" />\n</div>"]) + cssvars True ++ vars } + (Pandoc meta [RawBlock (Format "html") $ "<div id=\"cover-image\">\n<img src=\"../media/" ++ coverImage ++ "\" alt=\"cover image\" />\n</div>"]) imgContent <- lift $ P.readFileLazy img - return ( [mkEntry "cover.xhtml" cpContent] - , [mkEntry coverImage imgContent] ) + coverEntry <- mkEntry "text/cover.xhtml" cpContent + coverImageEntry <- mkEntry ("media/" ++ coverImage) + imgContent + return ( [ coverEntry ] + , [ coverImageEntry ] ) -- title page tpContent <- lift $ writeHtml opts'{ writerVariables = ("titlepage","true"): cssvars True ++ vars } (Pandoc meta []) - let tpEntry = mkEntry (inSubdir "title_page.xhtml") tpContent + tpEntry <- mkEntry "text/title_page.xhtml" tpContent -- handle pictures -- mediaRef <- P.newIORef [] @@ -454,7 +465,7 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do when (null xs) $ report $ CouldNotFetchResource f "glob did not match any font files" return xs - let mkFontEntry f = mkEntry ("fonts/" ++ takeFileName f) <$> + let mkFontEntry f = mkEntry ("fonts/" ++ takeFileName f) =<< lift (P.readFileLazy f) fontFiles <- concat <$> mapM matchingGlob (writerEpubFonts opts') fontEntries <- mapM mkFontEntry fontFiles @@ -540,7 +551,7 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do chapters' let chapToEntry num (Chapter mbnum bs) = - mkEntry (inSubdir (showChapter num)) <$> + mkEntry ("text/" ++ showChapter num) =<< writeHtml opts'{ writerNumberOffset = fromMaybe [] mbnum , writerVariables = cssvars True ++ vars } (case bs of @@ -550,7 +561,7 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do nullMeta) bs _ -> Pandoc nullMeta bs) - chapterEntries <- lift $ zipWithM chapToEntry [1..] chapters + chapterEntries <- zipWithM chapToEntry [1..] chapters -- incredibly inefficient (TODO): let containsMathML ent = epub3 && @@ -563,24 +574,34 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do -- contents.opf let chapterNode ent = unode "item" ! - ([("id", toId $ eRelativePath ent), - ("href", eRelativePath ent), + ([("id", toId $ makeRelative epubSubdir + $ eRelativePath ent), + ("href", makeRelative epubSubdir + $ eRelativePath ent), ("media-type", "application/xhtml+xml")] ++ case props ent of [] -> [] xs -> [("properties", unwords xs)]) $ () + let chapterRefNode ent = unode "itemref" ! - [("idref", toId $ eRelativePath ent)] $ () + [("idref", toId $ makeRelative epubSubdir + $ eRelativePath ent)] $ () let pictureNode ent = unode "item" ! - [("id", toId $ eRelativePath ent), - ("href", eRelativePath ent), - ("media-type", fromMaybe "application/octet-stream" + [("id", toId $ makeRelative epubSubdir + $ eRelativePath ent), + ("href", makeRelative epubSubdir + $ eRelativePath ent), + ("media-type", + fromMaybe "application/octet-stream" $ mediaTypeOf $ eRelativePath ent)] $ () let fontNode ent = unode "item" ! - [("id", toId $ eRelativePath ent), - ("href", eRelativePath ent), - ("media-type", fromMaybe "" $ getMimeType $ eRelativePath ent)] $ () + [("id", toId $ makeRelative epubSubdir + $ eRelativePath ent), + ("href", makeRelative epubSubdir + $ eRelativePath ent), + ("media-type", fromMaybe "" $ + getMimeType $ eRelativePath ent)] $ () let plainTitle = case docTitle' meta of [] -> case epubTitle metadata of [] -> "UNTITLED" @@ -613,7 +634,9 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do ] ++ [ unode "item" ! [("id","style"), ("href",fp) ,("media-type","text/css")] $ () | - fp <- map eRelativePath stylesheetEntries ] ++ + fp <- map + (makeRelative epubSubdir . eRelativePath) + stylesheetEntries ] ++ map chapterNode (cpgEntry ++ (tpEntry : chapterEntries)) ++ (case cpicEntry of [] -> [] @@ -648,7 +671,7 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do | isJust (epubCoverImage metadata) ] ] - let contentsEntry = mkEntry "content.opf" contentsData + contentsEntry <- mkEntry "content.opf" contentsData -- toc.ncx let secs = hierarchicalize blocks' @@ -681,12 +704,12 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do navMapFormatter n tit src subs = unode "navPoint" ! [("id", "navPoint-" ++ show n)] $ [ unode "navLabel" $ unode "text" $ stringify tit - , unode "content" ! [("src", inSubdir src)] $ () + , unode "content" ! [("src", "text/" ++ src)] $ () ] ++ subs let tpNode = unode "navPoint" ! [("id", "navPoint-0")] $ [ unode "navLabel" $ unode "text" (stringify $ docTitle' meta) - , unode "content" ! [("src", inSubdir "title_page.xhtml")] + , unode "content" ! [("src", "text/title_page.xhtml")] $ () ] navMap <- lift $ evalStateT (mapM (navPointNode navMapFormatter) secs) 1 @@ -710,13 +733,13 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do , unode "navMap" $ tpNode : navMap ] - let tocEntry = mkEntry "toc.ncx" tocData + tocEntry <- mkEntry "toc.ncx" tocData let navXhtmlFormatter :: Int -> [Inline] -> String -> [Element] -> Element navXhtmlFormatter n tit src subs = unode "li" ! [("id", "toc-li-" ++ show n)] $ (unode "a" ! - [("href", inSubdir src)] + [("href", "text/" ++ src)] $ titElements) : case subs of [] -> [] @@ -766,36 +789,37 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do (Pandoc (setMeta "title" (walk removeNote $ fromList $ docTitle' meta) nullMeta) (navBlocks ++ landmarks)) - let navEntry = mkEntry "nav.xhtml" navData + navEntry <- mkEntry "nav.xhtml" navData -- mimetype - let mimetypeEntry = mkEntry "mimetype" $ UTF8.fromStringLazy "application/epub+zip" + mimetypeEntry <- mkEntry "mimetype" $ + UTF8.fromStringLazy "application/epub+zip" -- container.xml let containerData = UTF8.fromStringLazy $ ppTopElement $ unode "container" ! [("version","1.0") ,("xmlns","urn:oasis:names:tc:opendocument:xmlns:container")] $ unode "rootfiles" $ - unode "rootfile" ! [("full-path", inSubdir "content.opf") + unode "rootfile" ! [("full-path", + (if null epubSubdir + then "" + else epubSubdir ++ "/") ++ "content.opf") ,("media-type","application/oebps-package+xml")] $ () - let containerEntry = mkEntry "META-INF/container.xml" containerData + containerEntry <- mkEntry "META-INF/container.xml" containerData -- com.apple.ibooks.display-options.xml let apple = UTF8.fromStringLazy $ ppTopElement $ unode "display_options" $ unode "platform" ! [("name","*")] $ unode "option" ! [("name","specified-fonts")] $ "true" - let appleEntry = mkEntry "META-INF/com.apple.ibooks.display-options.xml" apple + appleEntry <- mkEntry "META-INF/com.apple.ibooks.display-options.xml" apple - let addEpubSubdir :: Entry -> Entry - addEpubSubdir e = e{ eRelativePath = inSubdir (eRelativePath e) } -- construct archive let archive = foldr addEntryToArchive emptyArchive $ - [mimetypeEntry, containerEntry, appleEntry] ++ - map addEpubSubdir - (tpEntry : contentsEntry : tocEntry : navEntry : - (stylesheetEntries ++ picEntries ++ cpicEntry ++ - cpgEntry ++ chapterEntries ++ fontEntries)) + [mimetypeEntry, containerEntry, appleEntry, + contentsEntry, tocEntry, navEntry, tpEntry] ++ + stylesheetEntries ++ picEntries ++ cpicEntry ++ + cpgEntry ++ chapterEntries ++ fontEntries return $ fromArchive archive metadataElement :: EPUBVersion -> EPUBMetadata -> UTCTime -> Element @@ -936,8 +960,7 @@ modifyMediaRef oldsrc = 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) + entry <- mkEntry new (B.fromChunks . (:[]) $ img) modify $ \st -> st{ stMediaPaths = (oldsrc, (new, Just entry)):media} return new) @@ -959,21 +982,15 @@ transformInline :: PandocMonad m => WriterOptions -> Inline -> E m Inline -transformInline opts (Image attr lab (src,tit)) = do +transformInline _opts (Image attr lab (src,tit)) = do newsrc <- modifyMediaRef src - let pref = if null (writerEpubSubdirectory opts) - then "" - else "../" - return $ Image attr lab (pref ++ newsrc, tit) + return $ Image attr lab ("../" ++ newsrc, tit) transformInline opts (x@(Math t m)) | WebTeX url <- writerHTMLMathMethod opts = do newsrc <- modifyMediaRef (url ++ urlEncode m) let mathclass = if t == DisplayMath then "display" else "inline" - let pref = if null (writerEpubSubdirectory opts) - then "" - else "../" return $ Span ("",["math",mathclass],[]) - [Image nullAttr [x] (pref ++ newsrc, "")] + [Image nullAttr [x] ("../" ++ newsrc, "")] transformInline _opts (RawInline fmt raw) | fmt == Format "html" = do let tags = parseTags raw -- cgit v1.2.3 From 5a4c0d6a8cf7e15ec1d871fee641ffcc063ef89a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 7 Nov 2017 13:05:06 -0800 Subject: Deprecated ancient HTML math methods. `--latexmathml`, `--gladtex`, `--mimetex`, `--jsmath` --- src/Text/Pandoc/App.hs | 62 +++++++++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 24 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index e82ccf3f0..d9924d3a1 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -1401,29 +1401,12 @@ options = (\opt -> return opt { optCiteMethod = Biblatex })) "" -- "Use biblatex cite commands in LaTeX output" - , Option "m" ["latexmathml", "asciimathml"] - (OptArg - (\arg opt -> - return opt { optHTMLMathMethod = LaTeXMathML arg }) - "URL") - "" -- "Use LaTeXMathML script in html output" - , Option "" ["mathml"] (NoArg (\opt -> return opt { optHTMLMathMethod = MathML })) "" -- "Use mathml for HTML math" - , Option "" ["mimetex"] - (OptArg - (\arg opt -> do - let url' = case arg of - Just u -> u ++ "?" - Nothing -> "/cgi-bin/mimetex.cgi?" - return opt { optHTMLMathMethod = WebTeX url' }) - "URL") - "" -- "Use mimetex for HTML math" - , Option "" ["webtex"] (OptArg (\arg opt -> do @@ -1432,12 +1415,6 @@ options = "URL") "" -- "Use web service for HTML math" - , Option "" ["jsmath"] - (OptArg - (\arg opt -> return opt { optHTMLMathMethod = JsMath arg}) - "URL") - "" -- "Use jsMath for HTML math" - , Option "" ["mathjax"] (OptArg (\arg opt -> do @@ -1446,6 +1423,7 @@ options = return opt { optHTMLMathMethod = MathJax url'}) "URL") "" -- "Use MathJax for HTML math" + , Option "" ["katex"] (OptArg (\arg opt -> @@ -1455,9 +1433,38 @@ options = "URL") "" -- Use KaTeX for HTML Math + , Option "m" ["latexmathml", "asciimathml"] + (OptArg + (\arg opt -> do + deprecatedOption "--latexmathml" + return opt { optHTMLMathMethod = LaTeXMathML arg }) + "URL") + "" -- "Use LaTeXMathML script in html output" + + , Option "" ["mimetex"] + (OptArg + (\arg opt -> do + deprecatedOption "--mimetex" + let url' = case arg of + Just u -> u ++ "?" + Nothing -> "/cgi-bin/mimetex.cgi?" + return opt { optHTMLMathMethod = WebTeX url' }) + "URL") + "" -- "Use mimetex for HTML math" + + , Option "" ["jsmath"] + (OptArg + (\arg opt -> do + deprecatedOption "--jsmath" + return opt { optHTMLMathMethod = JsMath arg}) + "URL") + "" -- "Use jsMath for HTML math" + , Option "" ["gladtex"] (NoArg - (\opt -> return opt { optHTMLMathMethod = GladTeX })) + (\opt -> do + deprecatedOption "--gladtex" + return opt { optHTMLMathMethod = GladTeX })) "" -- "Use gladtex for HTML math" , Option "" ["abbreviations"] @@ -1655,3 +1662,10 @@ splitField s = baseWriterName :: String -> String baseWriterName = takeWhile (\c -> c /= '+' && c /= '-') + +deprecatedOption :: String -> IO () +deprecatedOption o = + runIO (report $ Deprecated o "") >>= + \r -> case r of + Right () -> return () + Left e -> E.throwIO e -- cgit v1.2.3 From c6338fa88340c76271934e90d96fa9ff606ae78a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 8 Nov 2017 17:06:26 -0800 Subject: EPUB writer: fixed modified paths for raw HTML tags (src, poster, etc.) This had not been updated for the new EPUB container layout, with a separate text/ subdirectory. Closes #4050. Closes #4055. --- src/Text/Pandoc/Writers/EPUB.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index 23df046d0..e943ef17f 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -942,8 +942,8 @@ transformTag tag@(TagOpen name attr) newsrc <- modifyMediaRef src newposter <- modifyMediaRef poster let attr' = filter (\(x,_) -> x /= "src" && x /= "poster") attr ++ - [("src", newsrc) | not (null newsrc)] ++ - [("poster", newposter) | not (null newposter)] + [("src", "../" ++ newsrc) | not (null newsrc)] ++ + [("poster", "../" ++ newposter) | not (null newposter)] return $ TagOpen name attr' transformTag tag = return tag -- cgit v1.2.3 From fef5770591e7d3a185dc8d7dc2b70594732b0367 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 8 Nov 2017 21:54:23 -0800 Subject: Fix regression with --metadata. It should replace a metadata value set in the document itself, rather than creating a list including a new value. Closes #4054. --- src/Text/Pandoc/App.hs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index d9924d3a1..b2394e142 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -76,7 +76,7 @@ import qualified System.IO as IO (Newline (..)) import System.IO.Error (isDoesNotExistError) import Text.Pandoc import Text.Pandoc.BCP47 (Lang (..), parseBCP47) -import Text.Pandoc.Builder (setMeta) +import Text.Pandoc.Builder (setMeta, deleteMeta) import Text.Pandoc.Highlighting (highlightingStyles) import Text.Pandoc.Lua (LuaException (..), runLuaFilter) import Text.Pandoc.PDF (makePDF) @@ -494,7 +494,7 @@ convertWithOpts opts = do ( (if isJust (optExtractMedia opts) then fillMediaBag else return) - >=> return . flip (foldr addMetadata) metadata + >=> return . addMetadata metadata >=> applyLuaFilters datadir (optLuaFilters opts) format >=> maybe return extractMedia (optExtractMedia opts) >=> applyTransforms transforms @@ -722,8 +722,11 @@ defaultOpts = Opt , optStripComments = False } -addMetadata :: (String, String) -> Pandoc -> Pandoc -addMetadata (k, v) (Pandoc meta bs) = Pandoc meta' bs +addMetadata :: [(String, String)] -> Pandoc -> Pandoc +addMetadata kvs pdc = foldr addMeta (removeMetaKeys kvs pdc) kvs + +addMeta :: (String, String) -> Pandoc -> Pandoc +addMeta (k, v) (Pandoc meta bs) = Pandoc meta' bs where meta' = case lookupMeta k meta of Nothing -> setMeta k v' meta Just (MetaList xs) -> @@ -731,6 +734,9 @@ addMetadata (k, v) (Pandoc meta bs) = Pandoc meta' bs Just x -> setMeta k (MetaList [x, v']) meta v' = readMetaValue v +removeMetaKeys :: [(String,String)] -> Pandoc -> Pandoc +removeMetaKeys kvs pdc = foldr (deleteMeta . fst) pdc kvs + readMetaValue :: String -> MetaValue readMetaValue s = case decode (UTF8.fromString s) of Just (Yaml.String t) -> MetaString $ T.unpack t -- cgit v1.2.3 From f72d7636557b01812dcd13826b7697c3ceb20a76 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 10 Nov 2017 02:23:58 +0300 Subject: HTML reader: hlint --- src/Text/Pandoc/Readers/HTML.hs | 61 ++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 31 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 2b667c63c..7d514e042 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -54,8 +54,7 @@ import Data.List (intercalate, isPrefixOf) import Data.List.Split (wordsBy) import qualified Data.Map as M import Data.Maybe (fromMaybe, isJust, isNothing) -import Data.Monoid (First (..)) -import Data.Monoid ((<>)) +import Data.Monoid (First (..), (<>)) import qualified Data.Set as Set import Data.Text (Text) import qualified Data.Text as T @@ -89,7 +88,7 @@ readHtml opts inp = do parseTagsOptions parseOptions{ optTagPosition = True } (crFilter inp) parseDoc = do - blocks <- (fixPlains False) . mconcat <$> manyTill block eof + blocks <- fixPlains False . mconcat <$> manyTill block eof meta <- stateMeta . parserState <$> getState bs' <- replaceNotes (B.toList blocks) reportLogMessages @@ -223,10 +222,10 @@ eSwitch constructor parser = try $ do eCase :: PandocMonad m => TagParser m (Maybe Inlines) eCase = do skipMany pBlank - TagOpen _ attr' <- lookAhead $ pSatisfy $ (matchTagOpen "case" []) + TagOpen _ attr' <- lookAhead $ pSatisfy (matchTagOpen "case" []) let attr = toStringAttr attr' - case (flip lookup namespaces) =<< lookup "required-namespace" attr of - Just p -> Just <$> (pInTags "case" (skipMany pBlank *> p <* skipMany pBlank)) + case flip lookup namespaces =<< lookup "required-namespace" attr of + Just p -> Just <$> pInTags "case" (skipMany pBlank *> p <* skipMany pBlank) Nothing -> Nothing <$ manyTill pAnyTag (pSatisfy (matchTagClose "case")) eFootnote :: PandocMonad m => TagParser m () @@ -235,20 +234,20 @@ eFootnote = try $ do guardEnabled Ext_epub_html_exts (TagOpen tag attr') <- lookAhead pAnyTag let attr = toStringAttr attr' - guard (maybe False (flip elem notes) (lookup "type" attr)) + guard $ maybe False (`elem` notes) (lookup "type" attr) let ident = fromMaybe "" (lookup "id" attr) content <- pInTags tag block addNote ident content addNote :: PandocMonad m => String -> Blocks -> TagParser m () -addNote uid cont = updateState (\s -> s {noteTable = (uid, cont) : (noteTable s)}) +addNote uid cont = updateState (\s -> s {noteTable = (uid, cont) : noteTable s}) eNoteref :: PandocMonad m => TagParser m Inlines eNoteref = try $ do guardEnabled Ext_epub_html_exts - TagOpen tag attr' <- lookAhead $ pAnyTag + TagOpen tag attr' <- lookAhead pAnyTag let attr = toStringAttr attr' - guard (maybe False (== "noteref") (lookup "type" attr)) + guard $ lookup "type" attr == Just "noteref" let ident = maybe "" (dropWhile (== '#')) (lookup "href" attr) guard (not (null ident)) pInTags tag block @@ -258,8 +257,8 @@ eNoteref = try $ do 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)) + (TagOpen tag attr) <- lookAhead pAnyTag + guard $ lookup "type" attr == Just "toc" void (pInTags tag block) pList :: PandocMonad m => TagParser m Blocks @@ -285,7 +284,7 @@ pListItem nonItem = do (Plain ils:xs) -> B.fromList (Plain [Span (ident, [], []) ils] : xs) _ -> B.divWith (ident, [], []) bs - (maybe id addId (lookup "id" attr)) <$> + maybe id addId (lookup "id" attr) <$> pInTags "li" block <* skipMany nonItem parseListStyleType :: String -> ListNumberStyle @@ -356,14 +355,14 @@ fixPlains :: Bool -> Blocks -> Blocks fixPlains inList bs = if any isParaish bs' then B.fromList $ map plainToPara bs' else bs - where isParaish (Para _) = True - isParaish (CodeBlock _ _) = True - isParaish (Header _ _ _) = True - isParaish (BlockQuote _) = True - isParaish (BulletList _) = not inList - isParaish (OrderedList _ _) = not inList - isParaish (DefinitionList _) = not inList - isParaish _ = False + where isParaish Para{} = True + isParaish CodeBlock{} = True + isParaish Header{} = True + isParaish BlockQuote{} = True + isParaish BulletList{} = not inList + isParaish OrderedList{} = not inList + isParaish DefinitionList{} = not inList + isParaish _ = False plainToPara (Plain xs) = Para xs plainToPara x = x bs' = B.toList bs @@ -427,10 +426,10 @@ eSection = try $ do setInChapter (pInTags tag block) headerLevel :: PandocMonad m => Text -> TagParser m Int -headerLevel tagtype = do +headerLevel tagtype = case safeRead (T.unpack (T.drop 1 tagtype)) of Just level -> - (try $ do + try (do guardEnabled Ext_epub_html_exts asks inChapter >>= guard return (level - 1)) @@ -481,12 +480,12 @@ pTable = try $ do pTBody = pOptInTag "tbody" $ many1 pTr head'' <- pOptInTag "thead" pTh head' <- map snd <$> - (pOptInTag "tbody" $ - if null head'' then pTh else return head'') + pOptInTag "tbody" + (if null head'' then pTh else return head'') rowsLs <- many pTBody rows' <- pOptInTag "tfoot" $ many pTr TagClose _ <- pSatisfy (matchTagClose "table") - let rows'' = (concat rowsLs) <> rows' + let rows'' = concat rowsLs <> rows' let rows''' = map (map snd) rows'' -- let rows''' = map (map snd) rows'' -- fail on empty table @@ -691,7 +690,7 @@ pSubscript :: PandocMonad m => TagParser m Inlines pSubscript = pInlinesInTags "sub" B.subscript pStrikeout :: PandocMonad m => TagParser m Inlines -pStrikeout = do +pStrikeout = pInlinesInTags "s" B.strikeout <|> pInlinesInTags "strike" B.strikeout <|> pInlinesInTags "del" B.strikeout <|> @@ -719,7 +718,7 @@ pLink = try $ do tag <- pSatisfy $ tagOpenLit "a" (const True) let title = T.unpack $ fromAttrib "title" tag -- take id from id attribute if present, otherwise name - let uid = maybe (T.unpack $ fromAttrib "name" tag) id $ + let uid = fromMaybe (T.unpack $ fromAttrib "name" tag) $ maybeFromAttrib "id" tag let cls = words $ T.unpack $ fromAttrib "class" tag lab <- trimInlines . mconcat <$> manyTill inline (pCloses "a") @@ -750,7 +749,7 @@ pImage = do let getAtt k = case fromAttrib k tag of "" -> [] v -> [(T.unpack k, T.unpack v)] - let kvs = concat $ map getAtt ["width", "height", "sizes", "srcset"] + let kvs = concatMap getAtt ["width", "height", "sizes", "srcset"] return $ B.imageWith (uid, cls, kvs) (escapeURI url) title (B.text alt) pCode :: PandocMonad m => TagParser m Inlines @@ -846,7 +845,7 @@ pCloses :: PandocMonad m => Text -> TagParser m () pCloses tagtype = try $ do t <- lookAhead $ pSatisfy $ \tag -> isTagClose tag || isTagOpen tag case t of - (TagClose t') | t' == tagtype -> pAnyTag >> return () + (TagClose t') | t' == tagtype -> void pAnyTag (TagOpen t' _) | t' `closes` tagtype -> return () (TagClose "ul") | tagtype == "li" -> return () (TagClose "ol") | tagtype == "li" -> return () @@ -1197,7 +1196,7 @@ htmlTag f = try $ do mkAttr :: [(String, String)] -> Attr mkAttr attr = (attribsId, attribsClasses, attribsKV) where attribsId = fromMaybe "" $ lookup "id" attr - attribsClasses = (words $ fromMaybe "" $ lookup "class" attr) <> epubTypes + attribsClasses = words (fromMaybe "" $ lookup "class" attr) <> epubTypes attribsKV = filter (\(k,_) -> k /= "class" && k /= "id") attr epubTypes = words $ fromMaybe "" $ lookup "epub:type" attr -- cgit v1.2.3 From 1592d3882142bbb938608e04d179f148453d93bb Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 9 Nov 2017 11:34:50 -0800 Subject: Allow fenced code blocks to be indented 1-3 spaces. This brings our handling of them into alignment with CommonMark's. Closes #??. --- src/Text/Pandoc/Readers/Markdown.hs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 8fc92f7e8..8977517c1 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -623,8 +623,9 @@ indentedLine = indentSpaces >> anyLineNewline blockDelimiter :: PandocMonad m => (Char -> Bool) -> Maybe Int - -> ParserT [Char] st m Int + -> ParserT [Char] ParserState m Int blockDelimiter f len = try $ do + skipNonindentSpaces c <- lookAhead (satisfy f) case len of Just l -> count l (char c) >> many (char c) >> return l @@ -689,6 +690,8 @@ rawAttribute = do codeBlockFenced :: PandocMonad m => MarkdownParser m (F Blocks) codeBlockFenced = try $ do + indentchars <- nonindentSpaces + let indentLevel = length indentchars c <- try (guardEnabled Ext_fenced_code_blocks >> lookAhead (char '~')) <|> (guardEnabled Ext_backtick_code_blocks >> lookAhead (char '`')) size <- blockDelimiter (== c) Nothing @@ -701,7 +704,8 @@ codeBlockFenced = try $ do <|> ((\x -> ("",[toLanguageId x],[])) <$> many1 nonspaceChar))) blankline contents <- intercalate "\n" <$> - manyTill anyLine (blockDelimiter (== c) (Just size)) + manyTill (gobbleAtMostSpaces indentLevel >> anyLine) + (blockDelimiter (== c) (Just size)) blanklines return $ return $ case rawattr of -- cgit v1.2.3 From 207b3edcb9721c9026ebbbb9a1c98d290171a1ce Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 10 Nov 2017 13:12:31 +0300 Subject: Vimwiki reader: hlint --- src/Text/Pandoc/Readers/Vimwiki.hs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Vimwiki.hs b/src/Text/Pandoc/Readers/Vimwiki.hs index fecbb2fb4..5575b3687 100644 --- a/src/Text/Pandoc/Readers/Vimwiki.hs +++ b/src/Text/Pandoc/Readers/Vimwiki.hs @@ -244,7 +244,7 @@ preformatted = try $ do lookAhead newline contents <- manyTill anyChar (try (char '\n' >> many spaceChar >> string "}}}" >> many spaceChar >> newline)) - if not (contents == "") && (head contents == '\n') + if (contents /= "") && (head contents == '\n') then return $ B.codeBlockWith (makeAttr attrText) (tail contents) else return $ B.codeBlockWith (makeAttr attrText) contents @@ -310,10 +310,10 @@ mixedList' prevInd = do curLine <- listItemContent let listBuilder = if builder == "ul" then B.bulletList else B.orderedList - (subList, lowInd) <- (mixedList' curInd) + (subList, lowInd) <- mixedList' curInd if lowInd >= curInd then do - (sameIndList, endInd) <- (mixedList' lowInd) + (sameIndList, endInd) <- mixedList' lowInd let curList = combineList curLine subList ++ sameIndList if curInd > prevInd then return ([listBuilder curList], endInd) @@ -388,7 +388,7 @@ bulletListMarkers = "ul" <$ (char '*' <|> char '-') orderedListMarkers :: PandocMonad m => VwParser m String orderedListMarkers = - ("ol" <$choice ((orderedListMarker Decimal Period):(($OneParen) + ("ol" <$choice (orderedListMarker Decimal Period:(($OneParen) <$> orderedListMarker <$> [Decimal, LowerRoman, UpperRoman, LowerAlpha, UpperAlpha]))) <|> ("ol" <$ char '#') @@ -435,7 +435,7 @@ tableRow = try $ do tableCell :: PandocMonad m => VwParser m Blocks tableCell = try $ - B.plain . trimInlines . mconcat <$> (manyTill inline' (char '|')) + B.plain . trimInlines . mconcat <$> manyTill inline' (char '|') placeholder :: PandocMonad m => VwParser m () placeholder = try $ @@ -444,7 +444,7 @@ placeholder = try $ ph :: PandocMonad m => String -> VwParser m () ph s = try $ do many spaceChar >>string ('%':s) >> spaceChar - contents <- trimInlines . mconcat <$> (manyTill inline (lookAhead newline)) + contents <- trimInlines . mconcat <$> manyTill inline (lookAhead newline) --use lookAhead because of placeholder in the whitespace parser let meta' = return $ B.setMeta s contents nullMeta :: F Meta updateState $ \st -> st { stateMeta' = stateMeta' st <> meta' } @@ -515,12 +515,12 @@ bareURL = try $ do strong :: PandocMonad m => VwParser m Inlines strong = try $ do s <- lookAhead $ between (char '*') (char '*') (many1 $ noneOf "*") - guard $ not ((head s) `elem` spaceChars) - &¬ ((last s) `elem` spaceChars) + guard $ (head s `notElem` spaceChars) + && (last s `notElem` spaceChars) char '*' contents <- mconcat <$>manyTill inline' (char '*' >> notFollowedBy alphaNum) - return $ B.spanWith ((makeId contents), [], []) mempty + return $ B.spanWith (makeId contents, [], []) mempty <> B.strong contents makeId :: Inlines -> String @@ -529,8 +529,8 @@ makeId i = concat (stringify <$> toList i) emph :: PandocMonad m => VwParser m Inlines emph = try $ do s <- lookAhead $ between (char '_') (char '_') (many1 $ noneOf "_") - guard $ not ((head s) `elem` spaceChars) - &¬ ((last s) `elem` spaceChars) + guard $ (head s `notElem` spaceChars) + && (last s `notElem` spaceChars) char '_' contents <- mconcat <$>manyTill inline' (char '_' >> notFollowedBy alphaNum) @@ -539,7 +539,7 @@ emph = try $ do strikeout :: PandocMonad m => VwParser m Inlines strikeout = try $ do string "~~" - contents <- mconcat <$>many1Till inline' (string $ "~~") + contents <- mconcat <$>many1Till inline' (string "~~") return $ B.strikeout contents code :: PandocMonad m => VwParser m Inlines @@ -568,7 +568,7 @@ link = try $ do return $ B.link (procLink contents) "" (B.str contents) True -> do url <- manyTill anyChar $ char '|' - lab <- mconcat <$> (manyTill inline $ string "]]") + lab <- mconcat <$> manyTill inline (string "]]") return $ B.link (procLink url) "" lab image :: PandocMonad m => VwParser m Inlines @@ -584,7 +584,7 @@ images k return $ B.image (procImgurl imgurl) "" (B.str "") | k == 1 = do imgurl <- manyTill anyChar (char '|') - alt <- mconcat <$> (manyTill inline (try $ string "}}")) + alt <- mconcat <$> manyTill inline (try $ string "}}") return $ B.image (procImgurl imgurl) "" alt | k == 2 = do imgurl <- manyTill anyChar (char '|') @@ -600,8 +600,8 @@ images k procLink' :: String -> String procLink' s - | (take 6 s) == "local:" = "file" ++ drop 5 s - | (take 6 s) == "diary:" = "diary/" ++ drop 6 s ++ ".html" + | take 6 s == "local:" = "file" ++ drop 5 s + | take 6 s == "diary:" = "diary/" ++ drop 6 s ++ ".html" | or ((`isPrefixOf` s) <$> [ "http:", "https:", "ftp:", "file:", "mailto:", "news:", "telnet:" ]) = s @@ -614,7 +614,7 @@ procLink s = procLink' x ++ y where (x, y) = break (=='#') s procImgurl :: String -> String -procImgurl s = if (take 6 s) == "local:" then "file" ++ drop 5 s else s +procImgurl s = if take 6 s == "local:" then "file" ++ drop 5 s else s inlineMath :: PandocMonad m => VwParser m Inlines inlineMath = try $ do -- cgit v1.2.3 From 6e832a571b4357dbaaf57c0cdaf44cb2ea9c4144 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 10 Nov 2017 14:48:11 +0300 Subject: Txt2Tags reader: hlint --- src/Text/Pandoc/Readers/Txt2Tags.hs | 52 ++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 27 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Txt2Tags.hs b/src/Text/Pandoc/Readers/Txt2Tags.hs index 3fc54aaab..68399afc9 100644 --- a/src/Text/Pandoc/Readers/Txt2Tags.hs +++ b/src/Text/Pandoc/Readers/Txt2Tags.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE ViewPatterns #-} {- Copyright (C) 2014 Matthew Pickering <matthewtpickering@gmail.com> @@ -94,7 +93,7 @@ readTxt2Tags opts s = do readWithM parseT2T (def {stateOptions = opts}) $ T.unpack (crFilter s) ++ "\n\n" case parsed of - Right result -> return $ result + Right result -> return result Left e -> throwError e -- | Read Txt2Tags (ignoring all macros) from an input string returning @@ -149,7 +148,7 @@ setting = do string "%!" keyword <- ignoreSpacesCap (many1 alphaNum) char ':' - value <- ignoreSpacesCap (manyTill anyChar (newline)) + value <- ignoreSpacesCap (manyTill anyChar newline) return (keyword, value) -- Blocks @@ -158,7 +157,7 @@ parseBlocks :: T2T Blocks parseBlocks = mconcat <$> manyTill block eof block :: T2T Blocks -block = do +block = choice [ mempty <$ blanklines , quote @@ -196,7 +195,7 @@ para = try $ do listStart = try bulletListStart <|> orderedListStart commentBlock :: T2T Blocks -commentBlock = try (blockMarkupArea (anyLine) (const mempty) "%%%") <|> comment +commentBlock = try (blockMarkupArea anyLine (const mempty) "%%%") <|> comment -- Seperator and Strong line treated the same hrule :: T2T Blocks @@ -230,7 +229,7 @@ orderedList = B.orderedList . compactify <$> many1 (listItem orderedListStart parseBlocks) definitionList :: T2T Blocks -definitionList = try $ do +definitionList = try $ B.definitionList . compactifyDL <$> many1 (listItem definitionListStart definitionListEnd) @@ -282,17 +281,17 @@ table = try $ do rows <- many1 (many commentLine *> tableRow) let columns = transpose rows let ncolumns = length columns - let aligns = map (foldr1 findAlign) (map (map fst) columns) + let aligns = map (foldr1 findAlign . map fst) columns let rows' = map (map snd) rows let size = maximum (map length rows') let rowsPadded = map (pad size) rows' - let headerPadded = if (not (null tableHeader)) then pad size tableHeader else mempty + let headerPadded = if null tableHeader then mempty else pad size tableHeader return $ B.table mempty (zip aligns (replicate ncolumns 0.0)) headerPadded rowsPadded pad :: (Monoid a) => Int -> [a] -> [a] -pad n xs = xs ++ (replicate (n - length xs) mempty) +pad n xs = xs ++ replicate (n - length xs) mempty findAlign :: Alignment -> Alignment -> Alignment @@ -315,7 +314,7 @@ genericRow start = try $ do tableCell :: T2T (Alignment, Blocks) tableCell = try $ do leftSpaces <- length <$> lookAhead (many1 space) -- Case of empty cell means we must lookAhead - content <- (manyTill inline (try $ lookAhead (cellEnd))) + content <- manyTill inline (try $ lookAhead cellEnd) rightSpaces <- length <$> many space let align = case compare leftSpaces rightSpaces of @@ -323,9 +322,9 @@ tableCell = try $ do EQ -> AlignCenter GT -> AlignRight endOfCell - return $ (align, B.plain (B.trimInlines $ mconcat content)) + return (align, B.plain (B.trimInlines $ mconcat content)) where - cellEnd = (void newline <|> (many1 space *> endOfCell)) + cellEnd = void newline <|> (many1 space *> endOfCell) endOfCell :: T2T () endOfCell = try (skipMany1 $ char '|') <|> ( () <$ lookAhead newline) @@ -348,10 +347,10 @@ taggedBlock = do genericBlock :: Monoid a => T2T a -> (a -> Blocks) -> String -> T2T Blocks genericBlock p f s = blockMarkupArea p f s <|> blockMarkupLine p f s -blockMarkupArea :: Monoid a => (T2T a) -> (a -> Blocks) -> String -> T2T Blocks -blockMarkupArea p f s = try $ (do +blockMarkupArea :: Monoid a => T2T a -> (a -> Blocks) -> String -> T2T Blocks +blockMarkupArea p f s = try (do string s *> blankline - f . mconcat <$> (manyTill p (eof <|> void (string s *> blankline)))) + f . mconcat <$> manyTill p (eof <|> void (string s *> blankline))) blockMarkupLine :: T2T a -> (a -> Blocks) -> String -> T2T Blocks blockMarkupLine p f s = try (f <$> (string s *> space *> p)) @@ -369,7 +368,7 @@ parseInlines :: T2T Inlines parseInlines = trimInlines . mconcat <$> many1 inline inline :: T2T Inlines -inline = do +inline = choice [ endline , macro @@ -391,16 +390,16 @@ inline = do ] bold :: T2T Inlines -bold = inlineMarkup inline B.strong '*' (B.str) +bold = inlineMarkup inline B.strong '*' B.str underline :: T2T Inlines -underline = inlineMarkup inline underlineSpan '_' (B.str) +underline = inlineMarkup inline underlineSpan '_' B.str strike :: T2T Inlines -strike = inlineMarkup inline B.strikeout '-' (B.str) +strike = inlineMarkup inline B.strikeout '-' B.str italic :: T2T Inlines -italic = inlineMarkup inline B.emph '/' (B.str) +italic = inlineMarkup inline B.emph '/' B.str code :: T2T Inlines code = inlineMarkup ((:[]) <$> anyChar) B.code '`' id @@ -419,7 +418,7 @@ tagged = do -- Glued meaning that markup must be tight to content -- Markup can't pass newlines inlineMarkup :: Monoid a - => (T2T a) -- Content parser + => T2T a -- Content parser -> (a -> Inlines) -- Constructor -> Char -- Fence -> (String -> a) -- Special Case to handle ****** @@ -431,7 +430,7 @@ inlineMarkup p f c special = try $ do when (l == 2) (void $ notFollowedBy space) -- We must make sure that there is no space before the start of the -- closing tags - body <- optionMaybe (try $ manyTill (noneOf "\n\r") $ + body <- optionMaybe (try $ manyTill (noneOf "\n\r") (try $ lookAhead (noneOf " " >> string [c,c] ))) case body of Just middle -> do @@ -448,7 +447,7 @@ inlineMarkup p f c special = try $ do return $ f (start' <> body' <> end') Nothing -> do -- Either bad or case such as ***** guard (l >= 5) - let body' = (replicate (l - 4) c) + let body' = replicate (l - 4) c return $ f (special body') link :: T2T Inlines @@ -463,7 +462,7 @@ titleLink = try $ do guard (length tokens >= 2) char ']' let link' = last tokens - guard (length link' > 0) + guard $ not $ null link' let tit = concat (intersperse " " (init tokens)) return $ B.link link' "" (B.text tit) @@ -489,7 +488,7 @@ macro = try $ do -- raw URLs in text are automatically linked url :: T2T Inlines url = try $ do - (rawUrl, escapedUrl) <- (try uri <|> emailAddress) + (rawUrl, escapedUrl) <- try uri <|> emailAddress return $ B.link rawUrl "" (B.str escapedUrl) uri :: T2T (String, String) @@ -563,8 +562,7 @@ endline = try $ do return B.softbreak str :: T2T Inlines -str = try $ do - B.str <$> many1 (noneOf $ specialChars ++ "\n\r ") +str = try $ B.str <$> many1 (noneOf $ specialChars ++ "\n\r ") whitespace :: T2T Inlines whitespace = try $ B.space <$ spaceChar -- cgit v1.2.3 From f501ad031d4afb653d00c2a88856c903ed87d9b5 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sat, 11 Nov 2017 03:10:33 +0300 Subject: MediaWiki reader: hlint --- src/Text/Pandoc/Readers/MediaWiki.hs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/MediaWiki.hs b/src/Text/Pandoc/Readers/MediaWiki.hs index 01a6c74b6..a2b3346df 100644 --- a/src/Text/Pandoc/Readers/MediaWiki.hs +++ b/src/Text/Pandoc/Readers/MediaWiki.hs @@ -44,7 +44,7 @@ import Data.Char (isDigit, isSpace) import qualified Data.Foldable as F import Data.List (intercalate, intersperse, isPrefixOf) import qualified Data.Map as M -import Data.Maybe (fromMaybe) +import Data.Maybe (fromMaybe, maybeToList) import Data.Monoid ((<>)) import Data.Sequence (ViewL (..), viewl, (<|)) import qualified Data.Set as Set @@ -239,7 +239,7 @@ table = do Nothing -> 1.0 caption <- option mempty tableCaption optional rowsep - hasheader <- option False $ True <$ (lookAhead (skipSpaces *> char '!')) + hasheader <- option False $ True <$ lookAhead (skipSpaces *> char '!') (cellspecs',hdr) <- unzip <$> tableRow let widths = map ((tableWidth *) . snd) cellspecs' let restwidth = tableWidth - sum widths @@ -358,7 +358,7 @@ syntaxhighlight tag attrs = try $ do let mblang = lookup "lang" attrs let mbstart = lookup "start" attrs let mbline = lookup "line" attrs - let classes = maybe [] (:[]) mblang ++ maybe [] (const ["numberLines"]) mbline + let classes = maybeToList mblang ++ maybe [] (const ["numberLines"]) mbline let kvs = maybe [] (\x -> [("startFrom",x)]) mbstart contents <- charsInTags tag return $ B.codeBlockWith ("",classes,kvs) $ trimCode contents @@ -373,7 +373,7 @@ preformatted :: PandocMonad m => MWParser m Blocks preformatted = try $ do guardColumnOne char ' ' - let endline' = B.linebreak <$ (try $ newline <* char ' ') + let endline' = B.linebreak <$ try (newline <* char ' ') let whitespace' = B.str <$> many1 ('\160' <$ spaceChar) let spToNbsp ' ' = '\160' spToNbsp x = x @@ -382,7 +382,7 @@ preformatted = try $ do (htmlTag (~== TagOpen "nowiki" []) *> manyTill anyChar (htmlTag (~== TagClose "nowiki"))) let inline' = whitespace' <|> endline' <|> nowiki' - <|> (try $ notFollowedBy newline *> inline) + <|> try (notFollowedBy newline *> inline) contents <- mconcat <$> many1 inline' let spacesStr (Str xs) = all isSpace xs spacesStr _ = False @@ -397,7 +397,7 @@ encode = B.fromList . normalizeCode . B.toList . walk strToCode strToCode x = x normalizeCode [] = [] normalizeCode (Code a1 x : Code a2 y : zs) | a1 == a2 = - normalizeCode $ (Code a1 (x ++ y)) : zs + normalizeCode $ Code a1 (x ++ y) : zs normalizeCode (x:xs) = x : normalizeCode xs header :: PandocMonad m => MWParser m Blocks @@ -510,8 +510,8 @@ listItem' c = try $ do firstParaToPlain :: Blocks -> Blocks firstParaToPlain contents = case viewl (B.unMany contents) of - (Para xs) :< ys -> B.Many $ (Plain xs) <| ys - _ -> contents + Para xs :< ys -> B.Many $ Plain xs <| ys + _ -> contents -- -- inline parsers @@ -612,13 +612,13 @@ image = try $ do choice imageIdentifiers fname <- addUnderscores <$> many1 (noneOf "|]") _ <- many imageOption - dims <- try (char '|' *> (sepBy (many digit) (char 'x')) <* string "px") + dims <- try (char '|' *> sepBy (many digit) (char 'x') <* string "px") <|> return [] _ <- many imageOption let kvs = case dims of - w:[] -> [("width", w)] - w:(h:[]) -> [("width", w), ("height", h)] - _ -> [] + [w] -> [("width", w)] + [w, h] -> [("width", w), ("height", h)] + _ -> [] let attr = ("", [], kvs) caption <- (B.str fname <$ sym "]]") <|> try (char '|' *> (mconcat <$> manyTill inline (sym "]]"))) @@ -651,7 +651,7 @@ internalLink = try $ do ( (mconcat <$> many1 (notFollowedBy (char ']') *> inline)) -- the "pipe trick" -- [[Help:Contents|] -> "Contents" - <|> (return $ B.text $ drop 1 $ dropWhile (/=':') pagename) ) + <|> return (B.text $ drop 1 $ dropWhile (/=':') pagename) ) sym "]]" linktrail <- B.text <$> many letter let link = B.link (addUnderscores pagename) "wikilink" (label <> linktrail) -- cgit v1.2.3 From fb5ba1bb00a0d328db568952925f543b2bd8b584 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 11 Nov 2017 10:17:53 -0500 Subject: Fixed YAML metadata with "chomp" (`|-`). Previously if a YAML block under `|-` contained a blank line, pandoc would not parse it as metadata. --- src/Text/Pandoc/Readers/Markdown.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 8977517c1..a94c85c32 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -290,8 +290,8 @@ toMetaValue :: PandocMonad m toMetaValue x = parseFromString' parser' (T.unpack x) where parser' = (asInlines <$> ((trimInlinesF . mconcat) - <$> (guard (not endsWithNewline) - *> manyTill inline eof))) + <$> try (guard (not endsWithNewline) + *> manyTill inline eof))) <|> (asBlocks <$> parseBlocks) asBlocks p = do p' <- p -- cgit v1.2.3 From 6174b5bea5e8c4c35c191bd62f1f42e4d7fce69e Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 11 Nov 2017 11:01:38 -0500 Subject: Add lua filter functions to walk inline and block elements. Refactored some code from Text.Pandoc.Lua.PandocModule into new internal module Text.Pandoc.Lua.Filter. Add `walk_inline` and `walk_block` in pandoc lua module. --- src/Text/Pandoc/Lua.hs | 150 +------------------------------- src/Text/Pandoc/Lua/Filter.hs | 168 ++++++++++++++++++++++++++++++++++++ src/Text/Pandoc/Lua/PandocModule.hs | 22 ++++- 3 files changed, 192 insertions(+), 148 deletions(-) create mode 100644 src/Text/Pandoc/Lua/Filter.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 091deab8c..355a5baf1 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -33,25 +33,18 @@ Pandoc lua utils. -} module Text.Pandoc.Lua (LuaException (..), pushPandocModule, runLuaFilter) where -import Control.Monad (mplus, unless, when, (>=>)) +import Control.Monad (when, (>=>)) import Control.Monad.Identity (Identity) import Control.Monad.Trans (MonadIO (..)) -import Data.Data (Data, DataType, dataTypeConstrs, dataTypeName, dataTypeOf, - showConstr, toConstr, tyconUQname) -import Data.Foldable (foldrM) import Data.IORef (IORef, newIORef, readIORef) -import Data.Map (Map) -import Data.Maybe (isJust) -import Foreign.Lua (FromLuaStack (peek), Lua, LuaException (..), StackIndex, +import Foreign.Lua (FromLuaStack (peek), Lua, LuaException (..), Status (OK), ToLuaStack (push)) import Text.Pandoc.Class (CommonState, PandocIO, getCommonState, getMediaBag, setMediaBag) import Text.Pandoc.Definition import Text.Pandoc.Lua.PandocModule (pushMediaBagModule, pushPandocModule) +import Text.Pandoc.Lua.Filter (LuaFilter, walkMWithLuaFilter) import Text.Pandoc.MediaBag (MediaBag) -import Text.Pandoc.Walk (walkM) - -import qualified Data.Map as Map import qualified Foreign.Lua as Lua runLuaFilter :: Maybe FilePath -> FilePath -> String @@ -109,142 +102,5 @@ pushGlobalFilter = do runAll :: [LuaFilter] -> Pandoc -> Lua Pandoc runAll = foldr ((>=>) . walkMWithLuaFilter) return -walkMWithLuaFilter :: LuaFilter -> Pandoc -> Lua Pandoc -walkMWithLuaFilter (LuaFilter fnMap) = - walkInlines >=> walkBlocks >=> walkMeta >=> walkPandoc - where - walkInlines :: Pandoc -> Lua Pandoc - walkInlines = - if hasOneOf inlineFilterNames - then walkM (mconcatMapM (tryFilter fnMap :: Inline -> Lua [Inline])) - else return - - walkBlocks :: Pandoc -> Lua Pandoc - walkBlocks = - if hasOneOf blockFilterNames - then walkM (mconcatMapM (tryFilter fnMap :: Block -> Lua [Block])) - else return - - walkMeta :: Pandoc -> Lua Pandoc - walkMeta = - case Map.lookup "Meta" fnMap of - Just fn -> walkM (\(Pandoc meta blocks) -> do - meta' <- runFilterFunction fn meta *> singleElement meta - return $ Pandoc meta' blocks) - Nothing -> return - - walkPandoc :: Pandoc -> Lua Pandoc - walkPandoc = - case foldl mplus Nothing (map (`Map.lookup` fnMap) pandocFilterNames) of - Just fn -> \x -> runFilterFunction fn x *> singleElement x - Nothing -> return - - mconcatMapM f = fmap mconcat . mapM f - hasOneOf = any (\k -> isJust (Map.lookup k fnMap)) - -constructorsFor :: DataType -> [String] -constructorsFor x = map show (dataTypeConstrs x) - -inlineFilterNames :: [String] -inlineFilterNames = "Inline" : constructorsFor (dataTypeOf (Str [])) - -blockFilterNames :: [String] -blockFilterNames = "Block" : constructorsFor (dataTypeOf (Para [])) - -metaFilterName :: String -metaFilterName = "Meta" - -pandocFilterNames :: [String] -pandocFilterNames = ["Pandoc", "Doc"] - -type FunctionMap = Map String LuaFilterFunction -newtype LuaFilter = LuaFilter FunctionMap -newtype LuaFilterFunction = LuaFilterFunction { functionIndex :: Int } - --- | Try running a filter for the given element -tryFilter :: (Data a, FromLuaStack a, ToLuaStack a) - => FunctionMap -> a -> Lua [a] -tryFilter fnMap x = - let filterFnName = showConstr (toConstr x) - catchAllName = tyconUQname $ dataTypeName (dataTypeOf x) - in - case Map.lookup filterFnName fnMap `mplus` Map.lookup catchAllName fnMap of - Just fn -> runFilterFunction fn x *> elementOrList x - Nothing -> return [x] - -instance FromLuaStack LuaFilter where - peek idx = - let constrs = metaFilterName : pandocFilterNames - ++ blockFilterNames - ++ inlineFilterNames - fn c acc = do - Lua.getfield idx c - filterFn <- Lua.tryLua (peek (-1)) - Lua.pop 1 - return $ case filterFn of - Left _ -> acc - Right f -> (c, f) : acc - in LuaFilter . Map.fromList <$> foldrM fn [] constrs - --- | Push a value to the stack via a lua filter function. The filter function is --- called with given element as argument and is expected to return an element. --- Alternatively, the function can return nothing or nil, in which case the --- element is left unchanged. -runFilterFunction :: ToLuaStack a => LuaFilterFunction -> a -> Lua () -runFilterFunction lf x = do - pushFilterFunction lf - push x - z <- Lua.pcall 1 1 Nothing - when (z /= OK) $ do - let addPrefix = ("Error while running filter function: " ++) - Lua.throwTopMessageAsError' addPrefix - -elementOrList :: FromLuaStack a => a -> Lua [a] -elementOrList x = do - let topOfStack = Lua.StackIndex (-1) - elementUnchanged <- Lua.isnil topOfStack - if elementUnchanged - then [x] <$ Lua.pop 1 - else do - mbres <- Lua.peekEither topOfStack - case mbres of - Right res -> [res] <$ Lua.pop 1 - Left _ -> Lua.toList topOfStack <* Lua.pop 1 - -singleElement :: FromLuaStack a => a -> Lua a -singleElement x = do - elementUnchanged <- Lua.isnil (-1) - if elementUnchanged - then x <$ Lua.pop 1 - else do - mbres <- Lua.peekEither (-1) - case mbres of - Right res -> res <$ Lua.pop 1 - Left err -> do - Lua.pop 1 - Lua.throwLuaError $ - "Error while trying to get a filter's return " ++ - "value from lua stack.\n" ++ err - --- | Push the filter function to the top of the stack. -pushFilterFunction :: LuaFilterFunction -> Lua () -pushFilterFunction lf = - -- The function is stored in a lua registry table, retrieve it from there. - Lua.rawgeti Lua.registryindex (functionIndex lf) - -registerFilterFunction :: StackIndex -> Lua LuaFilterFunction -registerFilterFunction idx = do - isFn <- Lua.isfunction idx - unless isFn . Lua.throwLuaError $ "Not a function at index " ++ show idx - Lua.pushvalue idx - refIdx <- Lua.ref Lua.registryindex - return $ LuaFilterFunction refIdx - instance (FromLuaStack a) => FromLuaStack (Identity a) where peek = fmap return . peek - -instance ToLuaStack LuaFilterFunction where - push = pushFilterFunction - -instance FromLuaStack LuaFilterFunction where - peek = registerFilterFunction diff --git a/src/Text/Pandoc/Lua/Filter.hs b/src/Text/Pandoc/Lua/Filter.hs new file mode 100644 index 000000000..8db31e7fa --- /dev/null +++ b/src/Text/Pandoc/Lua/Filter.hs @@ -0,0 +1,168 @@ +{-# LANGUAGE FlexibleContexts #-} + +module Text.Pandoc.Lua.Filter ( LuaFilterFunction + , LuaFilter + , tryFilter + , runFilterFunction + , walkMWithLuaFilter + , walkInlines + , walkBlocks + , blockElementNames + , inlineElementNames + ) where +import Control.Monad (mplus, unless, when, (>=>)) +import Text.Pandoc.Definition +import Data.Foldable (foldrM) +import Data.Map (Map) +import qualified Data.Map as Map +import qualified Foreign.Lua as Lua +import Foreign.Lua (FromLuaStack (peek), Lua, StackIndex, + Status (OK), ToLuaStack (push)) +import Text.Pandoc.Walk (walkM, Walkable) +import Data.Data (Data, DataType, dataTypeConstrs, dataTypeName, dataTypeOf, + showConstr, toConstr, tyconUQname) +import Text.Pandoc.Lua.StackInstances() + +type FunctionMap = Map String LuaFilterFunction + +newtype LuaFilterFunction = LuaFilterFunction { functionIndex :: Int } + +instance ToLuaStack LuaFilterFunction where + push = pushFilterFunction + +instance FromLuaStack LuaFilterFunction where + peek = registerFilterFunction + +newtype LuaFilter = LuaFilter FunctionMap + +instance FromLuaStack LuaFilter where + peek idx = + let constrs = metaFilterName : pandocFilterNames + ++ blockElementNames + ++ inlineElementNames + fn c acc = do + Lua.getfield idx c + filterFn <- Lua.tryLua (peek (-1)) + Lua.pop 1 + return $ case filterFn of + Left _ -> acc + Right f -> (c, f) : acc + in LuaFilter . Map.fromList <$> foldrM fn [] constrs + +-- | Push the filter function to the top of the stack. +pushFilterFunction :: LuaFilterFunction -> Lua () +pushFilterFunction lf = + -- The function is stored in a lua registry table, retrieve it from there. + Lua.rawgeti Lua.registryindex (functionIndex lf) + +registerFilterFunction :: StackIndex -> Lua LuaFilterFunction +registerFilterFunction idx = do + isFn <- Lua.isfunction idx + unless isFn . Lua.throwLuaError $ "Not a function at index " ++ show idx + Lua.pushvalue idx + refIdx <- Lua.ref Lua.registryindex + return $ LuaFilterFunction refIdx + +elementOrList :: FromLuaStack a => a -> Lua [a] +elementOrList x = do + let topOfStack = Lua.StackIndex (-1) + elementUnchanged <- Lua.isnil topOfStack + if elementUnchanged + then [x] <$ Lua.pop 1 + else do + mbres <- Lua.peekEither topOfStack + case mbres of + Right res -> [res] <$ Lua.pop 1 + Left _ -> Lua.toList topOfStack <* Lua.pop 1 + +-- | Try running a filter for the given element +tryFilter :: (Data a, FromLuaStack a, ToLuaStack a) + => LuaFilter -> a -> Lua [a] +tryFilter (LuaFilter fnMap) x = + let filterFnName = showConstr (toConstr x) + catchAllName = tyconUQname $ dataTypeName (dataTypeOf x) + in + case Map.lookup filterFnName fnMap `mplus` Map.lookup catchAllName fnMap of + Just fn -> runFilterFunction fn x *> elementOrList x + Nothing -> return [x] + +-- | Push a value to the stack via a lua filter function. The filter function is +-- called with given element as argument and is expected to return an element. +-- Alternatively, the function can return nothing or nil, in which case the +-- element is left unchanged. +runFilterFunction :: ToLuaStack a => LuaFilterFunction -> a -> Lua () +runFilterFunction lf x = do + pushFilterFunction lf + push x + z <- Lua.pcall 1 1 Nothing + when (z /= OK) $ do + let addPrefix = ("Error while running filter function: " ++) + Lua.throwTopMessageAsError' addPrefix + +walkMWithLuaFilter :: LuaFilter -> Pandoc -> Lua Pandoc +walkMWithLuaFilter f = + walkInlines f >=> walkBlocks f >=> walkMeta f >=> walkPandoc f + +mconcatMapM :: Monad m => (a -> m [a]) -> [a] -> m [a] +mconcatMapM f = fmap mconcat . mapM f + +hasOneOf :: LuaFilter -> [String] -> Bool +hasOneOf (LuaFilter fnMap) = any (\k -> Map.member k fnMap) + +walkInlines :: Walkable [Inline] a => LuaFilter -> a -> Lua a +walkInlines f = + if f `hasOneOf` inlineElementNames + then walkM (mconcatMapM (tryFilter f :: Inline -> Lua [Inline])) + else return + +walkBlocks :: Walkable [Block] a => LuaFilter -> a -> Lua a +walkBlocks f = + if f `hasOneOf` blockElementNames + then walkM (mconcatMapM (tryFilter f :: Block -> Lua [Block])) + else return + +walkMeta :: LuaFilter -> Pandoc -> Lua Pandoc +walkMeta (LuaFilter fnMap) = + case Map.lookup "Meta" fnMap of + Just fn -> walkM (\(Pandoc meta blocks) -> do + meta' <- runFilterFunction fn meta *> singleElement meta + return $ Pandoc meta' blocks) + Nothing -> return + +walkPandoc :: LuaFilter -> Pandoc -> Lua Pandoc +walkPandoc (LuaFilter fnMap) = + case foldl mplus Nothing (map (`Map.lookup` fnMap) pandocFilterNames) of + Just fn -> \x -> runFilterFunction fn x *> singleElement x + Nothing -> return + +constructorsFor :: DataType -> [String] +constructorsFor x = map show (dataTypeConstrs x) + +inlineElementNames :: [String] +inlineElementNames = "Inline" : constructorsFor (dataTypeOf (Str [])) + +blockElementNames :: [String] +blockElementNames = "Block" : constructorsFor (dataTypeOf (Para [])) + +metaFilterName :: String +metaFilterName = "Meta" + +pandocFilterNames :: [String] +pandocFilterNames = ["Pandoc", "Doc"] + +singleElement :: FromLuaStack a => a -> Lua a +singleElement x = do + elementUnchanged <- Lua.isnil (-1) + if elementUnchanged + then x <$ Lua.pop 1 + else do + mbres <- Lua.peekEither (-1) + case mbres of + Right res -> res <$ Lua.pop 1 + Left err -> do + Lua.pop 1 + Lua.throwLuaError $ + "Error while trying to get a filter's return " ++ + "value from lua stack.\n" ++ err + + diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index c42e180c6..ac7839d0f 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -1,3 +1,5 @@ +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE FlexibleContexts #-} {- Copyright © 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> @@ -38,13 +40,15 @@ import Data.Digest.Pure.SHA (sha1, showDigest) import Data.IORef import Data.Maybe (fromMaybe) import Data.Text (pack) -import Foreign.Lua (FromLuaStack, Lua, NumResults, liftIO) +import Foreign.Lua (ToLuaStack, FromLuaStack, Lua, NumResults, liftIO) import Foreign.Lua.FunctionCalling (ToHaskellFunction) import System.Exit (ExitCode (..)) import Text.Pandoc.Class (CommonState (..), fetchItem, putCommonState, readDataFile, runIO, runIOorExplode, setMediaBag, setUserDataDir) import Text.Pandoc.Lua.StackInstances () +import Text.Pandoc.Definition (Block, Inline) +import Text.Pandoc.Walk (Walkable) import Text.Pandoc.MIME (MimeType) import Text.Pandoc.Options (ReaderOptions (readerExtensions)) import Text.Pandoc.Process (pipeProcess) @@ -53,6 +57,7 @@ import Text.Pandoc.Readers (Reader (..), getReader) import qualified Data.ByteString.Lazy as BL import qualified Foreign.Lua as Lua import qualified Text.Pandoc.MediaBag as MB +import Text.Pandoc.Lua.Filter (walkInlines, walkBlocks, LuaFilter) -- | Push the "pandoc" on the lua stack. pushPandocModule :: Maybe FilePath -> Lua () @@ -63,12 +68,27 @@ pushPandocModule datadir = do addFunction "_pipe" pipeFn addFunction "_read" readDoc addFunction "sha1" sha1HashFn + addFunction "walk_block" walkBlock + addFunction "walk_inline" walkInline -- | Get the string representation of the pandoc module pandocModuleScript :: Maybe FilePath -> IO String pandocModuleScript datadir = unpack <$> runIOorExplode (setUserDataDir datadir >> readDataFile "pandoc.lua") +walkElement :: (ToLuaStack a, Walkable [Inline] a, Walkable [Block] a) + => a -> LuaFilter -> Lua NumResults +walkElement x f = do + x' <- walkInlines f x >>= walkBlocks f + Lua.push x' + return 1 + +walkInline :: Inline -> LuaFilter -> Lua NumResults +walkInline = walkElement + +walkBlock :: Block -> LuaFilter -> Lua NumResults +walkBlock = walkElement + readDoc :: String -> String -> Lua NumResults readDoc formatSpec content = do case getReader formatSpec of -- cgit v1.2.3 From 6094c84b7ae0e00d51c16e7e283ad2e589091bee Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 11 Nov 2017 21:13:43 -0800 Subject: Functor instance to fix ghc 7.8 warning. --- src/Text/Pandoc/Lua/Filter.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/Filter.hs b/src/Text/Pandoc/Lua/Filter.hs index 8db31e7fa..687ab2be5 100644 --- a/src/Text/Pandoc/Lua/Filter.hs +++ b/src/Text/Pandoc/Lua/Filter.hs @@ -103,7 +103,7 @@ walkMWithLuaFilter :: LuaFilter -> Pandoc -> Lua Pandoc walkMWithLuaFilter f = walkInlines f >=> walkBlocks f >=> walkMeta f >=> walkPandoc f -mconcatMapM :: Monad m => (a -> m [a]) -> [a] -> m [a] +mconcatMapM :: (Monad m, Functor m) => (a -> m [a]) -> [a] -> m [a] mconcatMapM f = fmap mconcat . mapM f hasOneOf :: LuaFilter -> [String] -> Bool -- cgit v1.2.3 From f13f142945a1a05ef7b54b720b1670d16a051225 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sun, 12 Nov 2017 16:02:25 +0300 Subject: Add emacs extension --- src/Text/Pandoc/Extensions.hs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index b1b8336ef..ef05a597e 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -153,6 +153,7 @@ data Extension = | Ext_smart -- ^ "Smart" quotes, apostrophes, ellipses, dashes | Ext_old_dashes -- ^ -- = em, - before number = en | Ext_spaced_reference_links -- ^ Allow space between two parts of ref link + | Ext_emacs -- ^ Try to emulate Emacs Muse instead of Amusewiki deriving (Show, Read, Enum, Eq, Ord, Bounded, Data, Typeable, Generic) instance ToJSON Extension where -- cgit v1.2.3 From df4cb20f297b4a33e0520e62ce3d5ac08bf2a8ee Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sun, 12 Nov 2017 18:08:11 +0300 Subject: Muse reader: accept Emacs Muse definition lists Emacs Muse does not require indentation. --- src/Text/Pandoc/Readers/Muse.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 6f4244ac3..6739bc41f 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -423,7 +423,8 @@ definitionListItem = try $ do pure $ do lineContent' <- lineContent pure (B.text term, [lineContent']) where - termParser = many1 spaceChar >> -- Initial space as required by Amusewiki, but not Emacs Muse + termParser = (guardEnabled Ext_emacs <|> void spaceChar) >> -- Initial space is required by Amusewiki, but not Emacs Muse + many spaceChar >> many1Till anyChar (lookAhead (void (try (spaceChar >> string "::")) <|> void newline)) endOfInput = try $ skipMany blankline >> skipSpaces >> eof twoBlankLines = try $ blankline >> skipMany1 blankline -- cgit v1.2.3 From 7ba0ae8b4d9a6d3e7d4484a5f257e1e53f35667d Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 12 Nov 2017 14:19:58 -0800 Subject: LaTeX reader: allow optional args for parbox. See #4056. --- 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 9bac3d3a7..708980f1d 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -2003,7 +2003,7 @@ closing = do blockCommands :: PandocMonad m => M.Map Text (LP m Blocks) blockCommands = M.fromList $ [ ("par", mempty <$ skipopts) - , ("parbox", braced >> grouped blocks) + , ("parbox", skipopts >> braced >> grouped blocks) , ("title", mempty <$ (skipopts *> (grouped inline >>= addMeta "title") <|> (grouped block >>= addMeta "title"))) -- cgit v1.2.3 From eeaa3b048c325859d049f1b7aa7f60553c897aa6 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 12 Nov 2017 14:46:29 -0800 Subject: LaTeX reader: support column specs like `*{2}{r}`. This is equivalent to `rr`. We now expand it like a macro. Closes #4056. --- src/Text/Pandoc/Readers/LaTeX.hs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 708980f1d..28c8fd736 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -2406,8 +2406,7 @@ parseAligns = try $ do case safeRead ds of Just w -> return w Nothing -> return 0.0 - let alignSpec = try $ do - spaces + let alignSpec = do pref <- option [] alignPrefix spaces al <- alignChar @@ -2418,10 +2417,21 @@ parseAligns = try $ do spaces suff <- option [] alignSuffix return (al, width, (pref, suff)) + let starAlign = do -- *{2}{r} == rr, we just expand like a macro + symbol '*' + spaces + ds <- trim . toksToString <$> braced + spaces + spec <- braced + case safeRead ds of + Just n -> do + getInput >>= setInput . (mconcat (replicate n spec) ++) + Nothing -> fail $ "Could not parse " ++ ds ++ " as number" bgroup spaces maybeBar - aligns' <- many (alignSpec <* maybeBar) + aligns' <- many $ try $ spaces >> optional starAlign >> + (alignSpec <* maybeBar) spaces egroup spaces -- cgit v1.2.3 From b85921259dd2fb40e442d5d38ac6a0c72bca7566 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 12 Nov 2017 16:51:49 -0800 Subject: Fix comment that confuses haddock. --- 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 28c8fd736..f1fe6ff17 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -2417,7 +2417,7 @@ parseAligns = try $ do spaces suff <- option [] alignSuffix return (al, width, (pref, suff)) - let starAlign = do -- *{2}{r} == rr, we just expand like a macro + let starAlign = do -- '*{2}{r}' == 'rr', we just expand like a macro symbol '*' spaces ds <- trim . toksToString <$> braced -- cgit v1.2.3 From 3a83b3843db6434e9806558ea1e912055d5baf04 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 13 Nov 2017 18:41:30 +0300 Subject: Replace "emacs" extension with "amuse" extension It makes clear that extension is related to Muse markup. --- src/Text/Pandoc/Extensions.hs | 5 ++++- src/Text/Pandoc/Readers/Muse.hs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index ef05a597e..b7227860a 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -153,7 +153,7 @@ data Extension = | Ext_smart -- ^ "Smart" quotes, apostrophes, ellipses, dashes | Ext_old_dashes -- ^ -- = em, - before number = en | Ext_spaced_reference_links -- ^ Allow space between two parts of ref link - | Ext_emacs -- ^ Try to emulate Emacs Muse instead of Amusewiki + | Ext_amuse -- ^ Enable Text::Amuse extensions to Emacs Muse markup deriving (Show, Read, Enum, Eq, Ord, Bounded, Data, Typeable, Generic) instance ToJSON Extension where @@ -315,6 +315,9 @@ getDefaultExtensions "markdown_phpextra" = phpMarkdownExtraExtensions getDefaultExtensions "markdown_mmd" = multimarkdownExtensions getDefaultExtensions "markdown_github" = githubMarkdownExtensions getDefaultExtensions "markdown" = pandocExtensions +getDefaultExtensions "muse" = extensionsFromList + [Ext_amuse, + Ext_auto_identifiers] getDefaultExtensions "plain" = plainExtensions getDefaultExtensions "gfm" = githubMarkdownExtensions getDefaultExtensions "org" = extensionsFromList diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 6739bc41f..13b517d09 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -423,7 +423,7 @@ definitionListItem = try $ do pure $ do lineContent' <- lineContent pure (B.text term, [lineContent']) where - termParser = (guardEnabled Ext_emacs <|> void spaceChar) >> -- Initial space is required by Amusewiki, but not Emacs Muse + termParser = (guardDisabled Ext_amuse <|> void spaceChar) >> -- Initial space is required by Amusewiki, but not Emacs Muse many spaceChar >> many1Till anyChar (lookAhead (void (try (spaceChar >> string "::")) <|> void newline)) endOfInput = try $ skipMany blankline >> skipSpaces >> eof -- cgit v1.2.3 From 8d6e0e516a0b9d36992b2802e23dbcc6162c7346 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 13 Nov 2017 21:12:04 -0800 Subject: Markdown writer: fix bug with doubled footnotes in grid tables. Closes #4061. --- src/Text/Pandoc/Writers/Markdown.hs | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index a1f30cb0e..a8452f468 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -575,8 +575,6 @@ blockToMarkdown' opts t@(Table caption aligns widths headers rows) = do let padRow r = case numcols - length r of x | x > 0 -> r ++ replicate x empty | otherwise -> r - rawHeaders <- padRow <$> mapM (blockListToMarkdown opts) headers - rawRows <- mapM (fmap padRow . mapM (blockListToMarkdown opts)) rows let aligns' = case numcols - length aligns of x | x > 0 -> aligns ++ replicate x AlignDefault | otherwise -> aligns @@ -586,16 +584,25 @@ blockToMarkdown' opts t@(Table caption aligns widths headers rows) = do (nst,tbl) <- case True of _ | isSimple && - isEnabled Ext_simple_tables opts -> fmap (nest 2,) $ - pandocTable opts False (all null headers) aligns' widths' - rawHeaders rawRows + isEnabled Ext_simple_tables opts -> do + rawHeaders <- padRow <$> mapM (blockListToMarkdown opts) headers + rawRows <- mapM (fmap padRow . mapM (blockListToMarkdown opts)) + rows + (nest 2,) <$> pandocTable opts False (all null headers) + aligns' widths' rawHeaders rawRows | isSimple && - isEnabled Ext_pipe_tables opts -> fmap (id,) $ - pipeTable (all null headers) aligns' rawHeaders rawRows + isEnabled Ext_pipe_tables opts -> do + rawHeaders <- padRow <$> mapM (blockListToMarkdown opts) headers + rawRows <- mapM (fmap padRow . mapM (blockListToMarkdown opts)) + rows + (id,) <$> pipeTable (all null headers) aligns' rawHeaders rawRows | not hasBlocks && - isEnabled Ext_multiline_tables opts -> fmap (nest 2,) $ - pandocTable opts True (all null headers) aligns' widths' - rawHeaders rawRows + isEnabled Ext_multiline_tables opts -> do + rawHeaders <- padRow <$> mapM (blockListToMarkdown opts) headers + rawRows <- mapM (fmap padRow . mapM (blockListToMarkdown opts)) + rows + (nest 2,) <$> pandocTable opts True (all null headers) + aligns' widths' rawHeaders rawRows | isEnabled Ext_grid_tables opts && writerColumns opts >= 8 * numcols -> (id,) <$> gridTable opts blockListToMarkdown @@ -604,8 +611,11 @@ blockToMarkdown' opts t@(Table caption aligns widths headers rows) = do (text . T.unpack) <$> (writeHtml5String def $ Pandoc nullMeta [t]) | hasSimpleCells && - isEnabled Ext_pipe_tables opts -> fmap (id,) $ - pipeTable (all null headers) aligns' rawHeaders rawRows + isEnabled Ext_pipe_tables opts -> do + rawHeaders <- padRow <$> mapM (blockListToMarkdown opts) headers + rawRows <- mapM (fmap padRow . mapM (blockListToMarkdown opts)) + rows + (id,) <$> pipeTable (all null headers) aligns' rawHeaders rawRows | otherwise -> return $ (id, text "[TABLE]") return $ nst $ tbl $$ caption'' $$ blankline blockToMarkdown' opts (BulletList items) = do -- cgit v1.2.3 From 51897937cd07a066df656451068ef56d13b4edc4 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 13 Nov 2017 21:19:38 -0800 Subject: LaTeX reader: allow optional arguments on `\footnote`. Closes #4062. --- src/Text/Pandoc/Readers/LaTeX.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index f1fe6ff17..3bc59f262 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1337,8 +1337,8 @@ inlineCommands = M.union inlineLanguageCommands $ M.fromList $ , ("bar", lit "|") , ("textless", lit "<") , ("textgreater", lit ">") - , ("thanks", note <$> grouped block) - , ("footnote", note <$> grouped block) + , ("thanks", skipopts >> note <$> grouped block) + , ("footnote", skipopts >> note <$> grouped block) , ("verb", doverb) , ("lstinline", dolstinline) , ("Verb", doverb) -- cgit v1.2.3 From d29ef39f6f59cfa4cf8f9cc61c64b75d20b054b4 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 14 Nov 2017 20:57:41 -0800 Subject: EPUB writer: fix paths for cover image. Closes #4069. --- src/Text/Pandoc/Writers/EPUB.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index e943ef17f..7062fd925 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -667,7 +667,7 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do [ unode "reference" ! [("type","cover") ,("title","Cover") - ,("href","cover.xhtml")] $ () + ,("href","text/cover.xhtml")] $ () | isJust (epubCoverImage metadata) ] ] @@ -770,7 +770,7 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do ,("hidden","hidden")] $ [ unode "ol" $ [ unode "li" - [ unode "a" ! [("href", "cover.xhtml") + [ unode "a" ! [("href", "text/cover.xhtml") ,("epub:type", "cover")] $ "Cover"] | epubCoverImage metadata /= Nothing -- cgit v1.2.3 From c2a68ad7632cfa0ee00cd7bac9c7ee58f9947abd Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 14 Nov 2017 21:06:24 -0800 Subject: EPUB writer: Fixed path for cover image. It was previously `media/media/imagename`, and should have been `media/imagename`. --- src/Text/Pandoc/Writers/EPUB.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index 7062fd925..2ed397d36 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -434,7 +434,7 @@ pandocToEPUB version opts doc@(Pandoc meta _) = do case epubCoverImage metadata of Nothing -> return ([],[]) Just img -> do - let coverImage = "media/" ++ takeFileName img + let coverImage = takeFileName img cpContent <- lift $ writeHtml opts'{ writerVariables = ("coverpage","true"): -- cgit v1.2.3 From 22d69c8916f5cf53c4329c7757d063093cf39928 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 14 Nov 2017 21:20:30 -0800 Subject: RST reader: better support for 'container' directive. Create a div, incorporate name attribute and classes. Closes #4066. --- src/Text/Pandoc/Readers/RST.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index de488adfe..41b3c8b82 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -658,6 +658,7 @@ directive' = do body <- option "" $ try $ blanklines >> indentedBlock optional blanklines let body' = body ++ "\n\n" + name = trim $ fromMaybe "" (lookup "name" fields) imgAttr cl = ("", classes, widthAttr ++ heightAttr) where classes = words $ maybe "" trim (lookup cl fields) ++ @@ -691,7 +692,8 @@ directive' = do "line-block" -> lineBlockDirective body' "raw" -> return $ B.rawBlock (trim top) (stripTrailingNewlines body) "role" -> addNewRole top $ map (second trim) fields - "container" -> parseFromString' parseBlocks body' + "container" -> B.divWith (name, "container" : words top, []) <$> + parseFromString' parseBlocks body' "replace" -> B.para <$> -- consumed by substKey parseInlineFromString (trim top) "unicode" -> B.para <$> -- consumed by substKey -- cgit v1.2.3 From 508aab0bd555b4ca7632a78957ebb91c758f0a7e Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 14 Nov 2017 22:05:47 -0800 Subject: Text.Pandoc.Parsing.uri: allow `&` and `=` as word characters. This fixes a bug where pandoc would stop parsing a URI with an empty attribute: for example, `&a=&b=` wolud stop at `a`. (The uri parser tries to guess which punctuation characters are part of the URI and which might be punctuation after it.) Closes #4068. --- src/Text/Pandoc/Parsing.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 61d3caf3d..d8418ed11 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -563,7 +563,7 @@ uri = try $ do -- http://en.wikipedia.org/wiki/State_of_emergency_(disambiguation) -- as a URL, while NOT picking up the closing paren in -- (http://wikipedia.org). So we include balanced parens in the URL. - let isWordChar c = isAlphaNum c || c `elem` "#$%*+/@\\_-" + let isWordChar c = isAlphaNum c || c `elem` "#$%*+/@\\_-&=" let wordChar = satisfy isWordChar let percentEscaped = try $ char '%' >> skipMany1 (satisfy isHexDigit) let entity = () <$ characterReference -- cgit v1.2.3 From 4e02ed5f5cd5cf4d07c7252f2aac9a83b9ca7463 Mon Sep 17 00:00:00 2001 From: Sascha Wilde <wilde@sha-bang.de> Date: Thu, 16 Nov 2017 02:38:11 +0100 Subject: Creole reader: Fix performance issue for longer lists. (#4075) Fixes #4067. --- src/Text/Pandoc/Readers/Creole.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Creole.hs b/src/Text/Pandoc/Readers/Creole.hs index b4eb6eaef..505d1686d 100644 --- a/src/Text/Pandoc/Readers/Creole.hs +++ b/src/Text/Pandoc/Readers/Creole.hs @@ -194,7 +194,7 @@ endOfParaElement = lookAhead $ endOfInput <|> endOfPara endOfPara = try $ blankline >> skipMany1 blankline startOf :: PandocMonad m => CRLParser m a -> CRLParser m () startOf p = try $ blankline >> p >> return mempty - startOfList = startOf $ anyList 1 + startOfList = startOf $ anyListItem 1 startOfTable = startOf table startOfHeader = startOf header startOfNowiki = startOf nowiki -- cgit v1.2.3 From 26e59b331fc6ce2509a3b53f4454c0fadfbc58ee Mon Sep 17 00:00:00 2001 From: Alexander <ilabdsf@gmail.com> Date: Thu, 16 Nov 2017 20:24:02 +0300 Subject: Introduce `HasSyntaxExtensions` typeclass (#4074) + Added new `HasSyntaxExtensions` typeclass for `ReaderOptions` and `WriterOptions`. + Reimplemented `isEnabled` function from `Options.hs` to accept both `ReaderOptions` and `WriterOptions`. + Replaced `enabled` from `CommonMark.hs` with new `isEnabled`. --- src/Text/Pandoc/Options.hs | 13 +++++++++++-- src/Text/Pandoc/Readers/CommonMark.hs | 22 +++++++++------------- 2 files changed, 20 insertions(+), 15 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index 581f4c82a..03960b6b9 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -56,6 +56,9 @@ import Skylighting (SyntaxMap, defaultSyntaxMap) import Text.Pandoc.Extensions import Text.Pandoc.Highlighting (Style, pygments) +class HasSyntaxExtensions a where + getExtensions :: a -> Extensions + data ReaderOptions = ReaderOptions{ readerExtensions :: Extensions -- ^ Syntax extensions , readerStandalone :: Bool -- ^ Standalone document with header @@ -69,6 +72,9 @@ data ReaderOptions = ReaderOptions{ , readerStripComments :: Bool -- ^ Strip HTML comments instead of parsing as raw HTML } deriving (Show, Read, Data, Typeable, Generic) +instance HasSyntaxExtensions ReaderOptions where + getExtensions opts = readerExtensions opts + instance ToJSON ReaderOptions where toEncoding = genericToEncoding defaultOptions instance FromJSON ReaderOptions @@ -259,6 +265,9 @@ instance Default WriterOptions where , writerSyntaxMap = defaultSyntaxMap } +instance HasSyntaxExtensions WriterOptions where + getExtensions opts = writerExtensions opts + -- | Returns True if the given extension is enabled. -isEnabled :: Extension -> WriterOptions -> Bool -isEnabled ext opts = ext `extensionEnabled` writerExtensions opts +isEnabled :: HasSyntaxExtensions a => Extension -> a -> Bool +isEnabled ext opts = ext `extensionEnabled` getExtensions opts diff --git a/src/Text/Pandoc/Readers/CommonMark.hs b/src/Text/Pandoc/Readers/CommonMark.hs index 47f4c4088..ea9747342 100644 --- a/src/Text/Pandoc/Readers/CommonMark.hs +++ b/src/Text/Pandoc/Readers/CommonMark.hs @@ -48,18 +48,14 @@ import Text.Pandoc.Walk (walkM) -- | Parse a CommonMark formatted string into a 'Pandoc' structure. readCommonMark :: PandocMonad m => ReaderOptions -> Text -> m Pandoc readCommonMark opts s = return $ - (if enabled Ext_gfm_auto_identifiers opts + (if isEnabled Ext_gfm_auto_identifiers opts then addHeaderIdentifiers else id) $ nodeToPandoc opts $ commonmarkToNode opts' exts s - where opts' = [ optSmart | enabled Ext_smart opts ] - exts = [ extStrikethrough | enabled Ext_strikeout opts ] ++ - [ extTable | enabled Ext_pipe_tables opts ] ++ - [ extAutolink | enabled Ext_autolink_bare_uris opts ] - --- | Returns True if the given extension is enabled. -enabled :: Extension -> ReaderOptions -> Bool -enabled ext opts = ext `extensionEnabled` readerExtensions opts + where opts' = [ optSmart | isEnabled Ext_smart opts ] + exts = [ extStrikethrough | isEnabled Ext_strikeout opts ] ++ + [ extTable | isEnabled Ext_pipe_tables opts ] ++ + [ extAutolink | isEnabled Ext_autolink_bare_uris opts ] convertEmojis :: String -> String convertEmojis (':':xs) = @@ -112,7 +108,7 @@ addBlock _ (Node _ THEMATIC_BREAK _) = addBlock opts (Node _ BLOCK_QUOTE nodes) = (BlockQuote (addBlocks opts nodes) :) addBlock opts (Node _ (HTML_BLOCK t) _) - | enabled Ext_raw_html opts = (RawBlock (Format "html") (unpack t) :) + | isEnabled Ext_raw_html opts = (RawBlock (Format "html") (unpack t) :) | otherwise = id -- Note: the cmark parser will never generate CUSTOM_BLOCK, -- so we don't need to handle it: @@ -210,15 +206,15 @@ addInline opts (Node _ (TEXT t) _) = (map toinl clumps ++) samekind _ ' ' = False samekind _ _ = True toinl (' ':_) = Space - toinl xs = Str $ if enabled Ext_emoji opts + toinl xs = Str $ if isEnabled Ext_emoji opts then convertEmojis xs else xs addInline _ (Node _ LINEBREAK _) = (LineBreak :) addInline opts (Node _ SOFTBREAK _) - | enabled Ext_hard_line_breaks opts = (LineBreak :) + | isEnabled Ext_hard_line_breaks opts = (LineBreak :) | otherwise = (SoftBreak :) addInline opts (Node _ (HTML_INLINE t) _) - | enabled Ext_raw_html opts = (RawInline (Format "html") (unpack t) :) + | isEnabled Ext_raw_html opts = (RawInline (Format "html") (unpack t) :) | otherwise = id -- Note: the cmark parser will never generate CUSTOM_BLOCK, -- so we don't need to handle it: -- cgit v1.2.3 From 6018a2324d4eddc3844aa4c00b17294e85003750 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Tue, 14 Nov 2017 22:34:47 +0300 Subject: Muse reader: Add Text::Amuse footnote extensions Footnote end is indicated by indentation, so footnotes can be placed anywhere in the text, not just at the end of it. --- src/Text/Pandoc/Readers/Muse.hs | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 13b517d09..8c785e002 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -189,7 +189,8 @@ blockElements = choice [ comment , definitionList , table , commentTag - , noteBlock + , amuseNoteBlock + , emacsNoteBlock ] comment :: PandocMonad m => MuseParser m (F Blocks) @@ -308,8 +309,26 @@ noteMarker = try $ do char '[' many1Till digit $ char ']' -noteBlock :: PandocMonad m => MuseParser m (F Blocks) -noteBlock = try $ do +-- Amusewiki version of note +-- Parsing is similar to list item, except that note marker is used instead of list marker +amuseNoteBlock :: PandocMonad m => MuseParser m (F Blocks) +amuseNoteBlock = try $ do + guardEnabled Ext_amuse + pos <- getPosition + ref <- noteMarker <* skipSpaces + content <- listItemContents $ 2 + length ref + oldnotes <- stateNotes' <$> getState + case M.lookup ref oldnotes of + Just _ -> logMessage $ DuplicateNoteReference ref pos + Nothing -> return () + updateState $ \s -> s{ stateNotes' = M.insert ref (pos, content) oldnotes } + return mempty + +-- Emacs version of note +-- Notes are allowed only at the end of text, no indentation is required. +emacsNoteBlock :: PandocMonad m => MuseParser m (F Blocks) +emacsNoteBlock = try $ do + guardDisabled Ext_amuse pos <- getPosition ref <- noteMarker <* skipSpaces content <- mconcat <$> blocksTillNote @@ -376,9 +395,8 @@ listStart marker = try $ do postWhitespace <- length <$> many1 spaceChar return $ preWhitespace + markerLength + postWhitespace -listItem :: PandocMonad m => MuseParser m Int -> MuseParser m (F Blocks) -listItem start = try $ do - markerLength <- start +listItemContents :: PandocMonad m => Int -> MuseParser m (F Blocks) +listItemContents markerLength = do firstLine <- anyLineNewline restLines <- many $ listLine markerLength blank <- option "" ("\n" <$ blankline) @@ -386,6 +404,11 @@ listItem start = try $ do rest <- many $ listContinuation markerLength parseFromString (withListContext parseBlocks) $ concat (first:rest) ++ "\n" +listItem :: PandocMonad m => MuseParser m Int -> MuseParser m (F Blocks) +listItem start = try $ do + markerLength <- start + listItemContents markerLength + bulletListItems :: PandocMonad m => MuseParser m (F [Blocks]) bulletListItems = sequence <$> many1 (listItem bulletListStart) -- cgit v1.2.3 From 53aafd66434d97f5e0e9209650581177e2c79a91 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert+github@zeitkraut.de> Date: Sat, 18 Nov 2017 22:24:06 +0100 Subject: Lua filters: preload text module (#4077) The `text` module is preloaded in lua. The module contains some UTF-8 aware string functions, implemented in Haskell. The module is loaded on request only, e.g.: text = require 'text' function Str (s) s.text = text.upper(s.text) return s end --- src/Text/Pandoc/Lua.hs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 355a5baf1..148e7a23d 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -46,6 +46,7 @@ import Text.Pandoc.Lua.PandocModule (pushMediaBagModule, pushPandocModule) import Text.Pandoc.Lua.Filter (LuaFilter, walkMWithLuaFilter) import Text.Pandoc.MediaBag (MediaBag) import qualified Foreign.Lua as Lua +import qualified Foreign.Lua.Module.Text as Lua runLuaFilter :: Maybe FilePath -> FilePath -> String -> Pandoc -> PandocIO (Either LuaException Pandoc) @@ -64,6 +65,7 @@ runLuaFilter' :: CommonState -> Pandoc -> Lua Pandoc runLuaFilter' commonState datadir filterPath format mbRef pd = do Lua.openlibs + Lua.preloadTextModule "text" -- store module in global "pandoc" pushPandocModule datadir Lua.setglobal "pandoc" -- cgit v1.2.3 From b9cdef03f071b5ced19d094601a2e8cbd16748d6 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 18 Nov 2017 14:08:45 -0800 Subject: HTML reader: ensure we don't produce level 0 headers, even for chapter sections in epubs. This causes problems because writers aren't set up to expect these. This fixes the most immediate problem in #4076. It would be good to think more about how to propagate the information that top-level headers are chapters from the reader to the writer. --- src/Text/Pandoc/Readers/HTML.hs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 7d514e042..2f3b53a90 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -429,11 +429,11 @@ headerLevel :: PandocMonad m => Text -> TagParser m Int headerLevel tagtype = case safeRead (T.unpack (T.drop 1 tagtype)) of Just level -> - try (do - guardEnabled Ext_epub_html_exts - asks inChapter >>= guard - return (level - 1)) - <|> +-- try (do +-- guardEnabled Ext_epub_html_exts +-- asks inChapter >>= guard +-- return (level - 1)) +-- <|> return level Nothing -> fail "Could not retrieve header level" -- cgit v1.2.3 From 163af3fdee3c09234436ea7aab84a8b29ec1cece Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sun, 19 Nov 2017 01:23:46 +0300 Subject: Muse reader: produce SoftBreaks on newlines Now wrapping can be preserved with --wrap=preserve --- src/Text/Pandoc/Readers/Muse.hs | 46 +++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 18 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 8c785e002..4510e08ce 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -556,25 +556,35 @@ tableParseCaption = try $ do -- inline parsers -- +inlineList :: PandocMonad m => [MuseParser m (F Inlines)] +inlineList = [ endline + , br + , anchor + , footnote + , strong + , strongTag + , emph + , emphTag + , superscriptTag + , subscriptTag + , strikeoutTag + , verbatimTag + , link + , code + , codeTag + , whitespace + , str + , symbol + ] + inline :: PandocMonad m => MuseParser m (F Inlines) -inline = choice [ br - , anchor - , footnote - , strong - , strongTag - , emph - , emphTag - , superscriptTag - , subscriptTag - , strikeoutTag - , verbatimTag - , link - , code - , codeTag - , whitespace - , str - , symbol - ] <?> "inline" +inline = (choice inlineList) <?> "inline" + +endline :: PandocMonad m => MuseParser m (F Inlines) +endline = try $ do + newline + notFollowedBy blankline + returnF B.softbreak anchor :: PandocMonad m => MuseParser m (F Inlines) anchor = try $ do -- cgit v1.2.3 From 82bcda80c6e8b9e6e4e6c707569df4f9022b042f Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sun, 19 Nov 2017 04:26:45 +0300 Subject: Muse reader: count only one space as part of list item marker --- src/Text/Pandoc/Readers/Muse.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 4510e08ce..d54019777 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -392,8 +392,8 @@ listStart marker = try $ do st <- stateParserContext <$> getState getPosition >>= \pos -> guard (st == ListItemState || sourceColumn pos /= 1) markerLength <- marker - postWhitespace <- length <$> many1 spaceChar - return $ preWhitespace + markerLength + postWhitespace + many1 spaceChar + return $ preWhitespace + markerLength + 1 listItemContents :: PandocMonad m => Int -> MuseParser m (F Blocks) listItemContents markerLength = do -- cgit v1.2.3 From 97efed8c23414bd85801538de7df42b9f38d1fe7 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 19 Nov 2017 13:06:03 -0800 Subject: Allow spaces after `\(` and before `\)` with `tex_math_single_backslash`. Previously `\( \frac{1}{a} < \frac{1}{b} \)` was not parsed as math in `markdown` or `html` `+tex_math_single_backslash`. --- src/Text/Pandoc/Parsing.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index d8418ed11..c86f6718a 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -586,7 +586,7 @@ uri = try $ do mathInlineWith :: Stream s m Char => String -> String -> ParserT s st m String mathInlineWith op cl = try $ do string op - notFollowedBy space + when (op == "$") $ notFollowedBy space words' <- many1Till (count 1 (noneOf " \t\n\\") <|> (char '\\' >> -- This next clause is needed because \text{..} can @@ -600,7 +600,7 @@ mathInlineWith op cl = try $ do return " " ) (try $ string cl) notFollowedBy digit -- to prevent capture of $5 - return $ concat words' + return $ trim $ concat words' where inBalancedBraces :: Stream s m Char => Int -> String -> ParserT s st m String inBalancedBraces 0 "" = do -- cgit v1.2.3 From 1b970cca13ec6f5ace72dedd1df81fdaf1b9ad8b Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Tue, 21 Nov 2017 14:08:14 +0300 Subject: Recognize ".muse" file extension --- src/Text/Pandoc/App.hs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index b2394e142..d9f92335c 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -753,6 +753,7 @@ defaultReaderName fallback (x:xs) = ".htm" -> "html" ".md" -> "markdown" ".markdown" -> "markdown" + ".muse" -> "muse" ".tex" -> "latex" ".latex" -> "latex" ".ltx" -> "latex" @@ -793,6 +794,7 @@ defaultWriterName x = ".txt" -> "markdown" ".text" -> "markdown" ".md" -> "markdown" + ".muse" -> "muse" ".markdown" -> "markdown" ".textile" -> "textile" ".lhs" -> "markdown+lhs" -- cgit v1.2.3 From 91d67334263058fa884793cb71d5ba9e7fcc4eb3 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Tue, 21 Nov 2017 18:29:29 +0300 Subject: Muse reader: <literal> has "style" attribute, not "format" --- src/Text/Pandoc/Readers/Muse.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index d54019777..89b23f5a3 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -243,7 +243,8 @@ exampleTag = do literal :: PandocMonad m => MuseParser m (F Blocks) literal = (return . rawBlock) <$> htmlElement "literal" where - format (_, _, kvs) = fromMaybe "html" $ lookup "format" kvs + -- FIXME: Emacs Muse inserts <literal> without style into all output formats, but we assume HTML + format (_, _, kvs) = fromMaybe "html" $ lookup "style" kvs rawBlock (attrs, content) = B.rawBlock (format attrs) content blockTag :: PandocMonad m -- cgit v1.2.3 From 046f5bcc8129334cd2a3945417abdc3a956318f2 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Tue, 21 Nov 2017 19:01:19 +0300 Subject: Muse reader: chop newlines after <literal> and before </literal> --- src/Text/Pandoc/Readers/Muse.hs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 89b23f5a3..9f1ba1e6c 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -228,24 +228,28 @@ example = try $ do contents <- manyTill anyChar $ try (optionMaybe blankline >> string "}}}") return $ return $ B.codeBlock contents -exampleTag :: PandocMonad m => MuseParser m (F Blocks) -exampleTag = do - (attr, contents) <- htmlElement "example" - return $ return $ B.codeBlockWith attr $ chop contents +-- Trim up to one newline from the beginning and the end, +-- in case opening and/or closing tags are on separate lines. +chop :: String -> String +chop = lchop . rchop where lchop s = case s of '\n':ss -> ss _ -> s rchop = reverse . lchop . reverse - -- Trim up to one newline from the beginning and the end, - -- in case opening and/or closing tags are on separate lines. - chop = lchop . rchop + +exampleTag :: PandocMonad m => MuseParser m (F Blocks) +exampleTag = do + (attr, contents) <- htmlElement "example" + return $ return $ B.codeBlockWith attr $ chop contents literal :: PandocMonad m => MuseParser m (F Blocks) -literal = (return . rawBlock) <$> htmlElement "literal" +literal = do + guardDisabled Ext_amuse -- Text::Amuse does not support <literal> + (return . rawBlock) <$> htmlElement "literal" where -- FIXME: Emacs Muse inserts <literal> without style into all output formats, but we assume HTML format (_, _, kvs) = fromMaybe "html" $ lookup "style" kvs - rawBlock (attrs, content) = B.rawBlock (format attrs) content + rawBlock (attrs, content) = B.rawBlock (format attrs) $ chop content blockTag :: PandocMonad m => (Blocks -> Blocks) -- cgit v1.2.3 From 6c17117ef2e0ef6082ab55cf67256e59ea93ddf8 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Tue, 21 Nov 2017 19:19:49 +0300 Subject: Muse reader: add inline <literal> support --- src/Text/Pandoc/Readers/Muse.hs | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 9f1ba1e6c..0a0e86df8 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -577,6 +577,7 @@ inlineList = [ endline , link , code , codeTag + , inlineLiteralTag , whitespace , str , symbol @@ -693,6 +694,16 @@ codeTag = do (attrs, content) <- parseHtmlContentWithAttrs "code" anyChar return $ return $ B.codeWith attrs $ fromEntities content +inlineLiteralTag :: PandocMonad m => MuseParser m (F Inlines) +inlineLiteralTag = do + guardDisabled Ext_amuse -- Text::Amuse does not support <literal> + (attrs, content) <- parseHtmlContentWithAttrs "literal" anyChar + return $ return $ rawInline (attrs, content) + where + -- FIXME: Emacs Muse inserts <literal> without style into all output formats, but we assume HTML + format (_, _, kvs) = fromMaybe "html" $ lookup "style" kvs + rawInline (attrs, content) = B.rawInline (format attrs) $ fromEntities content + str :: PandocMonad m => MuseParser m (F Inlines) str = fmap (return . B.str) (many1 alphaNum <|> count 1 characterReference) -- cgit v1.2.3 From 30c1e53c42b4829c6eac9ab55d66fac6411c2c71 Mon Sep 17 00:00:00 2001 From: Jasper Van der Jeugt <jaspervdj@gmail.com> Date: Tue, 21 Nov 2017 18:20:05 +0100 Subject: Change JSON instances for Opt to TemplateHaskell (#4083) The `Generic` JSON instances for `Text.Pandoc.App.Opt` seem to tickle a particulary bad quadratic complexity case (Generics complexity is worse than quadratic with respect to the number of fields in the datatype). This is with GHC-8.2.1, I didn't test it using 8.0 but I assume it is similar. Using `Generic`, compilation of the `Text.Pandoc.App` module takes minutes and often gets killed due to out of memory on slower machines with "only" 8GB of accessible memory. This is particularly annoying to me since it means I cannot build pandoc on Travis. TemplateHaskell is a little uglier, but the module seems to compile within a few seconds, and compilation doesn't take more than 1GB of memory. Should I also change the other JSON instances throughout the codebase for consistency? --- src/Text/Pandoc/App.hs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index d9f92335c..4c4525dce 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -1,6 +1,7 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TupleSections #-} {- Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> @@ -44,8 +45,8 @@ import qualified Control.Exception as E import Control.Monad import Control.Monad.Except (catchError, throwError) import Control.Monad.Trans -import Data.Aeson (FromJSON (..), ToJSON (..), defaultOptions, eitherDecode', - encode, genericToEncoding) +import Data.Aeson (defaultOptions, eitherDecode', encode) +import Data.Aeson.TH (deriveJSON) import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as B import Data.Char (toLower, toUpper) @@ -95,10 +96,6 @@ import System.Posix.Terminal (queryTerminal) data LineEnding = LF | CRLF | Native deriving (Show, Generic) -instance ToJSON LineEnding where - toEncoding = genericToEncoding defaultOptions -instance FromJSON LineEnding - parseOptions :: [OptDescr (Opt -> IO Opt)] -> Opt -> IO Opt parseOptions options' defaults = do rawArgs <- map UTF8.decodeArg <$> getArgs @@ -646,10 +643,6 @@ data Opt = Opt , optStripComments :: Bool -- ^ Skip HTML comments } deriving (Generic, Show) -instance ToJSON Opt where - toEncoding = genericToEncoding defaultOptions -instance FromJSON Opt - -- | Defaults for command-line options. defaultOpts :: Opt defaultOpts = Opt @@ -1677,3 +1670,6 @@ deprecatedOption o = \r -> case r of Right () -> return () Left e -> E.throwIO e + +$(deriveJSON defaultOptions ''LineEnding) +$(deriveJSON defaultOptions ''Opt) -- cgit v1.2.3 From fda4426883a2e80899a9dea613910a803ccc4be0 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 21 Nov 2017 09:22:06 -0800 Subject: Add comment explaining why TH is used in Text.Pandoc.App. --- src/Text/Pandoc/App.hs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 4c4525dce..41b6a310b 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -1671,5 +1671,7 @@ deprecatedOption o = Right () -> return () Left e -> E.throwIO e +-- see https://github.com/jgm/pandoc/pull/4083 +-- using generic deriving caused long compilation times $(deriveJSON defaultOptions ''LineEnding) $(deriveJSON defaultOptions ''Opt) -- cgit v1.2.3 From cf87ffe9ee87b5e908725616998724fca0d6a8fd Mon Sep 17 00:00:00 2001 From: Jasper Van der Jeugt <jaspervdj@gmail.com> Date: Tue, 21 Nov 2017 21:27:40 +0100 Subject: Change Generic JSON instances to TemplateHaskell (#4085) --- src/Text/Pandoc/Extensions.hs | 22 ++++++++----------- src/Text/Pandoc/Options.hs | 51 +++++++++++-------------------------------- 2 files changed, 22 insertions(+), 51 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index b7227860a..67ad2ad04 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -15,8 +15,10 @@ 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 -} -{-# LANGUAGE DeriveDataTypeable #-} -{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE TemplateHaskell #-} {- | Module : Text.Pandoc.Extensions @@ -45,8 +47,8 @@ module Text.Pandoc.Extensions ( Extension(..) , githubMarkdownExtensions , multimarkdownExtensions ) where -import Data.Aeson (FromJSON (..), ToJSON (..), defaultOptions, - genericToEncoding) +import Data.Aeson (FromJSON (..), ToJSON (..), defaultOptions) +import Data.Aeson.TH (deriveJSON) import Data.Bits (clearBit, setBit, testBit, (.|.)) import Data.Data (Data) import Data.Typeable (Typeable) @@ -55,11 +57,7 @@ import Text.Pandoc.Shared (safeRead) import Text.Parsec newtype Extensions = Extensions Integer - deriving (Show, Read, Eq, Ord, Data, Typeable, Generic) - -instance ToJSON Extensions where - toEncoding = genericToEncoding defaultOptions -instance FromJSON Extensions + deriving (Show, Read, Eq, Ord, Data, Typeable, Generic, ToJSON, FromJSON) instance Monoid Extensions where mempty = Extensions 0 @@ -156,10 +154,6 @@ data Extension = | Ext_amuse -- ^ Enable Text::Amuse extensions to Emacs Muse markup deriving (Show, Read, Enum, Eq, Ord, Bounded, Data, Typeable, Generic) -instance ToJSON Extension where - toEncoding = genericToEncoding defaultOptions -instance FromJSON Extension - -- | Extensions to be used with pandoc-flavored markdown. pandocExtensions :: Extensions pandocExtensions = extensionsFromList @@ -373,3 +367,5 @@ parseFormatSpec = parse formatSpec "" return $ case polarity of '-' -> disableExtension ext _ -> enableExtension ext + +$(deriveJSON defaultOptions ''Extension) diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index 03960b6b9..1fb838321 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -1,5 +1,6 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE TemplateHaskell #-} {- Copyright (C) 2012-2017 John MacFarlane <jgm@berkeley.edu> @@ -45,8 +46,8 @@ module Text.Pandoc.Options ( module Text.Pandoc.Extensions , def , isEnabled ) where -import Data.Aeson (FromJSON (..), ToJSON (..), defaultOptions, - genericToEncoding) +import Data.Aeson (defaultOptions) +import Data.Aeson.TH (deriveJSON) import Data.Data (Data) import Data.Default import qualified Data.Set as Set @@ -75,10 +76,6 @@ data ReaderOptions = ReaderOptions{ instance HasSyntaxExtensions ReaderOptions where getExtensions opts = readerExtensions opts -instance ToJSON ReaderOptions where - toEncoding = genericToEncoding defaultOptions -instance FromJSON ReaderOptions - instance Default ReaderOptions where def = ReaderOptions{ readerExtensions = emptyExtensions @@ -116,29 +113,17 @@ data HTMLMathMethod = PlainMath | KaTeX String -- url of KaTeX files deriving (Show, Read, Eq, Data, Typeable, Generic) -instance ToJSON HTMLMathMethod where - toEncoding = genericToEncoding defaultOptions -instance FromJSON HTMLMathMethod - data CiteMethod = Citeproc -- use citeproc to render them | Natbib -- output natbib cite commands | Biblatex -- output biblatex cite commands deriving (Show, Read, Eq, Data, Typeable, Generic) -instance ToJSON CiteMethod where - toEncoding = genericToEncoding defaultOptions -instance FromJSON CiteMethod - -- | Methods for obfuscating email addresses in HTML. data ObfuscationMethod = NoObfuscation | ReferenceObfuscation | JavascriptObfuscation deriving (Show, Read, Eq, Data, Typeable, Generic) -instance ToJSON ObfuscationMethod where - toEncoding = genericToEncoding defaultOptions -instance FromJSON ObfuscationMethod - -- | Varieties of HTML slide shows. data HTMLSlideVariant = S5Slides | SlidySlides @@ -148,30 +133,18 @@ data HTMLSlideVariant = S5Slides | NoSlides deriving (Show, Read, Eq, Data, Typeable, Generic) -instance ToJSON HTMLSlideVariant where - toEncoding = genericToEncoding defaultOptions -instance FromJSON HTMLSlideVariant - -- | Options for accepting or rejecting MS Word track-changes. data TrackChanges = AcceptChanges | RejectChanges | AllChanges deriving (Show, Read, Eq, Data, Typeable, Generic) -instance ToJSON TrackChanges where - toEncoding = genericToEncoding defaultOptions -instance FromJSON TrackChanges - -- | Options for wrapping text in the output. data WrapOption = WrapAuto -- ^ Automatically wrap to width | WrapNone -- ^ No non-semantic newlines | WrapPreserve -- ^ Preserve wrapping of input source deriving (Show, Read, Eq, Data, Typeable, Generic) -instance ToJSON WrapOption where - toEncoding = genericToEncoding defaultOptions -instance FromJSON WrapOption - -- | Options defining the type of top-level headers. data TopLevelDivision = TopLevelPart -- ^ Top-level headers become parts | TopLevelChapter -- ^ Top-level headers become chapters @@ -180,20 +153,12 @@ data TopLevelDivision = TopLevelPart -- ^ Top-level headers become parts -- heuristics deriving (Show, Read, Eq, Data, Typeable, Generic) -instance ToJSON TopLevelDivision where - toEncoding = genericToEncoding defaultOptions -instance FromJSON TopLevelDivision - -- | Locations for footnotes and references in markdown output data ReferenceLocation = EndOfBlock -- ^ End of block | EndOfSection -- ^ prior to next section header (or end of document) | EndOfDocument -- ^ at end of document deriving (Show, Read, Eq, Data, Typeable, Generic) -instance ToJSON ReferenceLocation where - toEncoding = genericToEncoding defaultOptions -instance FromJSON ReferenceLocation - -- | Options for writers data WriterOptions = WriterOptions { writerTemplate :: Maybe String -- ^ Template to use @@ -271,3 +236,13 @@ instance HasSyntaxExtensions WriterOptions where -- | Returns True if the given extension is enabled. isEnabled :: HasSyntaxExtensions a => Extension -> a -> Bool isEnabled ext opts = ext `extensionEnabled` getExtensions opts + +$(deriveJSON defaultOptions ''ReaderOptions) +$(deriveJSON defaultOptions ''HTMLMathMethod) +$(deriveJSON defaultOptions ''CiteMethod) +$(deriveJSON defaultOptions ''ObfuscationMethod) +$(deriveJSON defaultOptions ''HTMLSlideVariant) +$(deriveJSON defaultOptions ''TrackChanges) +$(deriveJSON defaultOptions ''WrapOption) +$(deriveJSON defaultOptions ''TopLevelDivision) +$(deriveJSON defaultOptions ''ReferenceLocation) -- cgit v1.2.3 From df3a80cc97e99a8f4fdb8bf80b5ca85a216111b2 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Tue, 21 Nov 2017 17:41:29 +0300 Subject: Muse writer: escape only </code> inside code tag Additional <verbatim> is not needed as <code> is verbatim already. --- src/Text/Pandoc/Writers/Muse.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index 3a5eefc18..c834e5883 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -328,7 +328,7 @@ inlineToMuse (Quoted DoubleQuote lst) = do -- so just fallback to expanding inlines. inlineToMuse (Cite _ lst) = inlineListToMuse lst inlineToMuse (Code _ str) = return $ - "<code>" <> text (conditionalEscapeString str) <> "</code>" + "<code>" <> text (substitute "</code>" "<</code><code>/code>" str) <> "</code>" inlineToMuse (Math InlineMath str) = lift (texMathToInlines InlineMath str) >>= inlineListToMuse inlineToMuse (Math DisplayMath str) = do -- cgit v1.2.3 From 351765d4ad4e7bfa674fa48cb36dee824efc98ea Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Tue, 21 Nov 2017 23:46:05 +0300 Subject: Muse reader: concatenate inlines of the same type --- src/Text/Pandoc/Readers/Muse.hs | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 0a0e86df8..760308d5d 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -139,6 +139,20 @@ parseHtmlContentWithAttrs tag parser = do parseHtmlContent :: PandocMonad m => String -> MuseParser m a -> MuseParser m [a] parseHtmlContent tag p = fmap snd (parseHtmlContentWithAttrs tag p) +normalizeInlineList :: [Inline] -> [Inline] +normalizeInlineList (Code a1 x1 : Code a2 x2 : ils) | a1 == a2 + = normalizeInlineList $ Code a1 (x1 ++ x2) : ils +normalizeInlineList (RawInline f1 x1 : RawInline f2 x2 : ils) | f1 == f2 + = normalizeInlineList $ RawInline f1 (x1 ++ x2) : ils +normalizeInlineList (x:xs) = x : normalizeInlineList xs +normalizeInlineList [] = [] + +normalizeInlines :: Inlines -> Inlines +normalizeInlines = B.fromList . normalizeInlineList . B.toList . B.trimInlines + +normalizeInlinesF :: Future s Inlines -> Future s Inlines +normalizeInlinesF = liftM normalizeInlines + -- -- directive parsers -- @@ -150,7 +164,7 @@ parseDirective = do space spaces raw <- manyTill anyChar eol - value <- parseFromString (trimInlinesF . mconcat <$> many inline) raw + value <- parseFromString (normalizeInlinesF . mconcat <$> many inline) raw return (key, value) directive :: PandocMonad m => MuseParser m () @@ -217,7 +231,7 @@ header = try $ do level <- fmap length $ many1 $ char '*' guard $ level <= 5 spaceChar - content <- trimInlinesF . mconcat <$> manyTill inline eol + content <- normalizeInlinesF . mconcat <$> manyTill inline eol attr <- registerHeader ("", [], []) (runF content defaultParserState) return $ B.headerWith attr level <$> content @@ -286,7 +300,7 @@ verseLines :: PandocMonad m => MuseParser m (F Blocks) verseLines = do optionMaybe blankline -- Skip blankline after opening tag on separate line lns <- many verseLine - lns' <- mapM (parseFromString' (trimInlinesF . mconcat <$> many inline)) lns + lns' <- mapM (parseFromString' (normalizeInlinesF . mconcat <$> many inline)) lns return $ B.lineBlock <$> sequence lns' verseTag :: PandocMonad m => MuseParser m (F Blocks) @@ -302,7 +316,7 @@ para :: PandocMonad m => MuseParser m (F Blocks) para = do indent <- length <$> many spaceChar let f = if indent >= 2 && indent < 6 then B.blockQuote else id - fmap (f . B.para) . trimInlinesF . mconcat <$> many1Till inline endOfParaElement + fmap (f . B.para) . normalizeInlinesF . mconcat <$> many1Till inline endOfParaElement where endOfParaElement = lookAhead $ endOfInput <|> endOfPara <|> newBlockElement endOfInput = try $ skipMany blankline >> skipSpaces >> eof -- cgit v1.2.3 From 7e42857ed8357bc5b5e9528d9fd09408f27ab4e1 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Wed, 22 Nov 2017 00:17:15 +0300 Subject: Muse writer: escape "----" to avoid accidental horizontal rules --- src/Text/Pandoc/Writers/Muse.hs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index c834e5883..8963c7ce4 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -284,10 +284,12 @@ escapeString s = -- | Escape special characters for Muse if needed. conditionalEscapeString :: String -> String -conditionalEscapeString s - | any (`elem` ("*<=>[]|" :: String)) s || - "::" `isInfixOf` s = escapeString s - | otherwise = s +conditionalEscapeString s = + if any (`elem` ("*<=>[]|" :: String)) s || + "::" `isInfixOf` s || + "----" `isInfixOf` s + then escapeString s + else s -- | Convert list of Pandoc inline elements to Muse. inlineListToMuse :: PandocMonad m -- cgit v1.2.3 From e32657a6713dc113d511f409f0eb1a7570915d28 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Wed, 22 Nov 2017 14:53:19 +0300 Subject: Muse reader: fix reading of multiline definitions --- src/Text/Pandoc/Readers/Muse.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 760308d5d..2e4b58de7 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -467,8 +467,8 @@ definitionListItem = try $ do where termParser = (guardDisabled Ext_amuse <|> void spaceChar) >> -- Initial space is required by Amusewiki, but not Emacs Muse many spaceChar >> - many1Till anyChar (lookAhead (void (try (spaceChar >> string "::")) <|> void newline)) - endOfInput = try $ skipMany blankline >> skipSpaces >> eof + many1Till (noneOf "\n") (lookAhead (void (try (spaceChar >> string "::")))) + endOfInput = lookAhead $ try $ skipMany blankline >> skipSpaces >> eof twoBlankLines = try $ blankline >> skipMany1 blankline newDefinitionListItem = try $ void termParser endOfListItemElement = lookAhead $ endOfInput <|> newDefinitionListItem <|> twoBlankLines -- cgit v1.2.3 From 87e10ac28be163a6319ad482c5a207275b23a2a0 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Wed, 22 Nov 2017 15:22:39 +0300 Subject: Muse reader: don't allow blockquotes within lists --- src/Text/Pandoc/Readers/Muse.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 2e4b58de7..c0f8bfa01 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -315,7 +315,8 @@ commentTag = parseHtmlContent "comment" anyChar >> return mempty para :: PandocMonad m => MuseParser m (F Blocks) para = do indent <- length <$> many spaceChar - let f = if indent >= 2 && indent < 6 then B.blockQuote else id + st <- stateParserContext <$> getState + let f = if st /= ListItemState && indent >= 2 && indent < 6 then B.blockQuote else id fmap (f . B.para) . normalizeInlinesF . mconcat <$> many1Till inline endOfParaElement where endOfParaElement = lookAhead $ endOfInput <|> endOfPara <|> newBlockElement -- cgit v1.2.3 From 454062eccdcc0047e6134bdd4d86c2debb0b3ce7 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Wed, 22 Nov 2017 16:01:57 +0300 Subject: Muse writer: escape hash symbol --- src/Text/Pandoc/Writers/Muse.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index 8963c7ce4..a6ef28ba7 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -285,7 +285,7 @@ escapeString s = -- | Escape special characters for Muse if needed. conditionalEscapeString :: String -> String conditionalEscapeString s = - if any (`elem` ("*<=>[]|" :: String)) s || + if any (`elem` ("#*<=>[]|" :: String)) s || "::" `isInfixOf` s || "----" `isInfixOf` s then escapeString s -- cgit v1.2.3 From 75e2a1104cda760a939ca12258c0355844f70a31 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Wed, 22 Nov 2017 18:49:07 +0300 Subject: Muse reader: allow list items to be empty --- src/Text/Pandoc/Readers/Muse.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index c0f8bfa01..11fea5fde 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -412,7 +412,7 @@ listStart marker = try $ do st <- stateParserContext <$> getState getPosition >>= \pos -> guard (st == ListItemState || sourceColumn pos /= 1) markerLength <- marker - many1 spaceChar + void (many1 spaceChar) <|> eol return $ preWhitespace + markerLength + 1 listItemContents :: PandocMonad m => Int -> MuseParser m (F Blocks) @@ -448,7 +448,7 @@ orderedListStart style delim = listStart (snd <$> withHorizDisplacement (ordered orderedList :: PandocMonad m => MuseParser m (F Blocks) orderedList = try $ do - p@(_, style, delim) <- lookAhead (many spaceChar *> anyOrderedListMarker <* spaceChar) + p@(_, style, delim) <- lookAhead (many spaceChar *> anyOrderedListMarker <* (eol <|> void spaceChar)) guard $ style `elem` [Decimal, LowerAlpha, UpperAlpha, LowerRoman, UpperRoman] guard $ delim == Period items <- sequence <$> many1 (listItem $ orderedListStart style delim) -- cgit v1.2.3 From cd85c73ded2b100d33d3c1d36eac182bdd593b2f Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Wed, 22 Nov 2017 22:17:45 +0100 Subject: Org reader: allow empty list items Fixes: #4090 --- src/Text/Pandoc/Readers/Org/BlockStarts.hs | 26 +++++++------ src/Text/Pandoc/Readers/Org/Blocks.hs | 61 +++++++++++++----------------- 2 files changed, 42 insertions(+), 45 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/BlockStarts.hs b/src/Text/Pandoc/Readers/Org/BlockStarts.hs index 9c6614c99..7937c0ef7 100644 --- a/src/Text/Pandoc/Readers/Org/BlockStarts.hs +++ b/src/Text/Pandoc/Readers/Org/BlockStarts.hs @@ -75,21 +75,25 @@ latexEnvStart = try $ latexEnvName :: Monad m => OrgParser m String latexEnvName = try $ mappend <$> many1 alphaNum <*> option "" (string "*") - --- | Parses bullet list marker. -bulletListStart :: Monad m => OrgParser m () -bulletListStart = try $ - choice - [ () <$ skipSpaces <* oneOf "+-" <* skipSpaces1 - , () <$ skipSpaces1 <* char '*' <* skipSpaces1 - ] +bulletListStart :: Monad m => OrgParser m Int +bulletListStart = try $ do + ind <- length <$> many spaceChar + -- Unindented lists cannot use '*' bullets. + oneOf (if ind == 0 then "+-" else "*+-") + skipSpaces1 <|> lookAhead eol + return (ind + 1) genericListStart :: Monad m => OrgParser m String -> OrgParser m Int -genericListStart listMarker = try $ - (+) <$> (length <$> many spaceChar) - <*> (length <$> listMarker <* many1 spaceChar) +genericListStart listMarker = try $ do + ind <- length <$> many spaceChar + void listMarker + skipSpaces1 <|> lookAhead eol + return (ind + 1) + +eol :: Monad m => OrgParser m () +eol = void (char '\n') orderedListStart :: Monad m => OrgParser m Int orderedListStart = genericListStart orderedListMarker diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index 7f10195fe..04a0efc15 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -744,7 +744,7 @@ paraOrPlain = try $ do -- is directly followed by a list item, in which case the block is read as -- plain text. try (guard nl - *> notFollowedBy (inList *> (() <$ orderedListStart <|> bulletListStart)) + *> notFollowedBy (inList *> (orderedListStart <|> bulletListStart)) *> return (B.para <$> ils)) <|> return (B.plain <$> ils) @@ -757,40 +757,34 @@ list :: PandocMonad m => OrgParser m (F Blocks) list = choice [ definitionList, bulletList, orderedList ] <?> "list" definitionList :: PandocMonad m => OrgParser m (F Blocks) -definitionList = try $ do n <- lookAhead (bulletListStart' Nothing) - fmap (B.definitionList . compactifyDL) . sequence - <$> many1 (definitionListItem $ bulletListStart' (Just n)) +definitionList = try $ do + indent <- lookAhead bulletListStart + fmap (B.definitionList . compactifyDL) . sequence + <$> many1 (definitionListItem (bulletListStart `indented` indent)) bulletList :: PandocMonad m => OrgParser m (F Blocks) -bulletList = try $ do n <- lookAhead (bulletListStart' Nothing) - fmap (B.bulletList . compactify) . sequence - <$> many1 (listItem (bulletListStart' $ Just n)) +bulletList = try $ do + indent <- lookAhead bulletListStart + fmap (B.bulletList . compactify) . sequence + <$> many1 (listItem (bulletListStart `indented` indent)) + +indented :: Monad m => OrgParser m Int -> Int -> OrgParser m Int +indented indentedMarker minIndent = try $ do + n <- indentedMarker + guard (minIndent <= n) + return n orderedList :: PandocMonad m => OrgParser m (F Blocks) -orderedList = fmap (B.orderedList . compactify) . sequence - <$> many1 (listItem orderedListStart) - -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) - skipSpaces1 - return (ind + 1) -bulletListStart' (Just n) = do count (n-1) spaceChar - oneOf (bullets $ n == 1) - many1 spaceChar - return n - --- Unindented lists are legal, but they can't use '*' bullets. --- We return n to maintain compatibility with the generic listItem. -bullets :: Bool -> String -bullets unindented = if unindented then "+-" else "*+-" +orderedList = try $ do + indent <- lookAhead orderedListStart + fmap (B.orderedList . compactify) . sequence + <$> many1 (listItem (orderedListStart `indented` indent)) definitionListItem :: PandocMonad m => OrgParser m Int -> OrgParser m (F (Inlines, [Blocks])) -definitionListItem parseMarkerGetLength = try $ do - markerLength <- parseMarkerGetLength +definitionListItem parseIndentedMarker = try $ do + markerLength <- parseIndentedMarker term <- manyTill (noneOf "\n\r") (try definitionMarker) line1 <- anyLineNewline blank <- option "" ("\n" <$ blankline) @@ -802,13 +796,12 @@ definitionListItem parseMarkerGetLength = try $ do definitionMarker = spaceChar *> string "::" <* (spaceChar <|> lookAhead newline) - --- parse raw text for one list item, excluding start marker and continuations +-- | parse raw text for one list item listItem :: PandocMonad m => OrgParser m Int -> OrgParser m (F Blocks) -listItem start = try . withContext ListItemState $ do - markerLength <- try start +listItem parseIndentedMarker = try . withContext ListItemState $ do + markerLength <- try parseIndentedMarker firstLine <- anyLineNewline blank <- option "" ("\n" <$ blankline) rest <- concat <$> many (listContinuation markerLength) @@ -818,9 +811,9 @@ listItem start = try . withContext ListItemState $ do -- Note: nested lists are parsed as continuations. listContinuation :: Monad m => Int -> OrgParser m String -listContinuation markerLength = try $ +listContinuation markerLength = try $ do notFollowedBy' blankline - *> (mappend <$> (concat <$> many1 listLine) - <*> many blankline) + mappend <$> (concat <$> many1 listLine) + <*> many blankline where listLine = try $ indentWith markerLength *> anyLineNewline -- cgit v1.2.3 From 0cfd764d27bc03b59871e477d6bfd7341f4916b0 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 24 Nov 2017 12:17:20 +0300 Subject: Muse: move inline list normalization to writer --- src/Text/Pandoc/Readers/Muse.hs | 22 ++++------------------ src/Text/Pandoc/Writers/Muse.hs | 10 +++++++++- 2 files changed, 13 insertions(+), 19 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 11fea5fde..a3cb40e58 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -139,20 +139,6 @@ parseHtmlContentWithAttrs tag parser = do parseHtmlContent :: PandocMonad m => String -> MuseParser m a -> MuseParser m [a] parseHtmlContent tag p = fmap snd (parseHtmlContentWithAttrs tag p) -normalizeInlineList :: [Inline] -> [Inline] -normalizeInlineList (Code a1 x1 : Code a2 x2 : ils) | a1 == a2 - = normalizeInlineList $ Code a1 (x1 ++ x2) : ils -normalizeInlineList (RawInline f1 x1 : RawInline f2 x2 : ils) | f1 == f2 - = normalizeInlineList $ RawInline f1 (x1 ++ x2) : ils -normalizeInlineList (x:xs) = x : normalizeInlineList xs -normalizeInlineList [] = [] - -normalizeInlines :: Inlines -> Inlines -normalizeInlines = B.fromList . normalizeInlineList . B.toList . B.trimInlines - -normalizeInlinesF :: Future s Inlines -> Future s Inlines -normalizeInlinesF = liftM normalizeInlines - -- -- directive parsers -- @@ -164,7 +150,7 @@ parseDirective = do space spaces raw <- manyTill anyChar eol - value <- parseFromString (normalizeInlinesF . mconcat <$> many inline) raw + value <- parseFromString (trimInlinesF . mconcat <$> many inline) raw return (key, value) directive :: PandocMonad m => MuseParser m () @@ -231,7 +217,7 @@ header = try $ do level <- fmap length $ many1 $ char '*' guard $ level <= 5 spaceChar - content <- normalizeInlinesF . mconcat <$> manyTill inline eol + content <- trimInlinesF . mconcat <$> manyTill inline eol attr <- registerHeader ("", [], []) (runF content defaultParserState) return $ B.headerWith attr level <$> content @@ -300,7 +286,7 @@ verseLines :: PandocMonad m => MuseParser m (F Blocks) verseLines = do optionMaybe blankline -- Skip blankline after opening tag on separate line lns <- many verseLine - lns' <- mapM (parseFromString' (normalizeInlinesF . mconcat <$> many inline)) lns + lns' <- mapM (parseFromString' (trimInlinesF . mconcat <$> many inline)) lns return $ B.lineBlock <$> sequence lns' verseTag :: PandocMonad m => MuseParser m (F Blocks) @@ -317,7 +303,7 @@ para = do indent <- length <$> many spaceChar st <- stateParserContext <$> getState let f = if st /= ListItemState && indent >= 2 && indent < 6 then B.blockQuote else id - fmap (f . B.para) . normalizeInlinesF . mconcat <$> many1Till inline endOfParaElement + fmap (f . B.para) . trimInlinesF . mconcat <$> many1Till inline endOfParaElement where endOfParaElement = lookAhead $ endOfInput <|> endOfPara <|> newBlockElement endOfInput = try $ skipMany blankline >> skipSpaces >> eof diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index a6ef28ba7..ed5ad5793 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -291,11 +291,19 @@ conditionalEscapeString s = then escapeString s else s +normalizeInlineList :: [Inline] -> [Inline] +normalizeInlineList (Code a1 x1 : Code a2 x2 : ils) | a1 == a2 + = normalizeInlineList $ Code a1 (x1 ++ x2) : ils +normalizeInlineList (RawInline f1 x1 : RawInline f2 x2 : ils) | f1 == f2 + = normalizeInlineList $ RawInline f1 (x1 ++ x2) : ils +normalizeInlineList (x:xs) = x : normalizeInlineList xs +normalizeInlineList [] = [] + -- | Convert list of Pandoc inline elements to Muse. inlineListToMuse :: PandocMonad m => [Inline] -> StateT WriterState m Doc -inlineListToMuse lst = liftM hcat (mapM inlineToMuse lst) +inlineListToMuse lst = liftM hcat (mapM inlineToMuse (normalizeInlineList lst)) -- | Convert Pandoc inline element to Muse. inlineToMuse :: PandocMonad m -- cgit v1.2.3 From bd3feb864fb482a074f0c9930d479e01621f3132 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 24 Nov 2017 12:28:09 +0300 Subject: Muse writer: improve inline normalization --- src/Text/Pandoc/Writers/Muse.hs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index ed5ad5793..ccda8edf1 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -292,10 +292,24 @@ conditionalEscapeString s = else s normalizeInlineList :: [Inline] -> [Inline] +normalizeInlineList (Emph x1 : Emph x2 : ils) + = normalizeInlineList $ Emph (x1 ++ x2) : ils +normalizeInlineList (Strong x1 : Strong x2 : ils) + = normalizeInlineList $ Strong (x1 ++ x2) : ils +normalizeInlineList (Strikeout x1 : Strikeout x2 : ils) + = normalizeInlineList $ Strikeout (x1 ++ x2) : ils +normalizeInlineList (Superscript x1 : Superscript x2 : ils) + = normalizeInlineList $ Superscript (x1 ++ x2) : ils +normalizeInlineList (Subscript x1 : Subscript x2 : ils) + = normalizeInlineList $ Subscript (x1 ++ x2) : ils +normalizeInlineList (SmallCaps x1 : SmallCaps x2 : ils) + = normalizeInlineList $ SmallCaps (x1 ++ x2) : ils normalizeInlineList (Code a1 x1 : Code a2 x2 : ils) | a1 == a2 = normalizeInlineList $ Code a1 (x1 ++ x2) : ils normalizeInlineList (RawInline f1 x1 : RawInline f2 x2 : ils) | f1 == f2 = normalizeInlineList $ RawInline f1 (x1 ++ x2) : ils +normalizeInlineList (Span a1 x1 : Span a2 x2 : ils) | a1 == a2 + = normalizeInlineList $ Span a1 (x1 ++ x2) : ils normalizeInlineList (x:xs) = x : normalizeInlineList xs normalizeInlineList [] = [] -- cgit v1.2.3 From 137c7c2a657492c4652d1ebcaceea44ff69f262b Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 24 Nov 2017 13:16:09 +0300 Subject: Muse reader: allow definition to end with EOF --- src/Text/Pandoc/Readers/Muse.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index a3cb40e58..6599ba059 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -445,7 +445,7 @@ definitionListItem = try $ do term <- termParser many1 spaceChar string "::" - firstLine <- anyLineNewline + firstLine <- many $ noneOf "\n" restLines <- manyTill anyLineNewline endOfListItemElement let lns = firstLine : restLines lineContent <- parseFromString (withListContext parseBlocks) $ concat lns ++ "\n" -- cgit v1.2.3 From 77af25b4c3297cdd011a4c3c2755ffcac9807b7d Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 24 Nov 2017 14:02:43 +0300 Subject: Muse reader: parse markup in definition list terms --- src/Text/Pandoc/Readers/Muse.hs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 6599ba059..c6a66a1ed 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -442,7 +442,8 @@ orderedList = try $ do definitionListItem :: PandocMonad m => MuseParser m (F (Inlines, [Blocks])) definitionListItem = try $ do - term <- termParser + rawTerm <- termParser + term <- parseFromString (trimInlinesF . mconcat <$> many inline) rawTerm many1 spaceChar string "::" firstLine <- many $ noneOf "\n" @@ -450,7 +451,8 @@ definitionListItem = try $ do let lns = firstLine : restLines lineContent <- parseFromString (withListContext parseBlocks) $ concat lns ++ "\n" pure $ do lineContent' <- lineContent - pure (B.text term, [lineContent']) + term' <- term + pure (term', [lineContent']) where termParser = (guardDisabled Ext_amuse <|> void spaceChar) >> -- Initial space is required by Amusewiki, but not Emacs Muse many spaceChar >> -- cgit v1.2.3 From 887977c421e50f8dc84bd738a401bb3f833f2129 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sat, 25 Nov 2017 18:59:03 +0300 Subject: Muse reader: remove `nested` --- src/Text/Pandoc/Readers/Muse.hs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index c6a66a1ed..56ca9b640 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -55,7 +55,7 @@ import Text.Pandoc.Class (PandocMonad (..)) import Text.Pandoc.Definition import Text.Pandoc.Logging import Text.Pandoc.Options -import Text.Pandoc.Parsing hiding (nested) +import Text.Pandoc.Parsing import Text.Pandoc.Readers.HTML (htmlTag) import Text.Pandoc.Shared (crFilter) import Text.Pandoc.XML (fromEntities) @@ -102,15 +102,6 @@ parseBlocks = do eol :: Stream s m Char => ParserT s st m () eol = void newline <|> eof -nested :: PandocMonad m => MuseParser m a -> MuseParser m a -nested p = do - nestlevel <- stateMaxNestingLevel <$> getState - guard $ nestlevel > 0 - updateState $ \st -> st{ stateMaxNestingLevel = stateMaxNestingLevel st - 1 } - res <- p - updateState $ \st -> st{ stateMaxNestingLevel = nestlevel } - return res - htmlElement :: PandocMonad m => String -> MuseParser m (Attr, String) htmlElement tag = try $ do (TagOpen _ attr, _) <- htmlTag (~== TagOpen tag []) @@ -133,7 +124,7 @@ parseHtmlContentWithAttrs tag parser = do parsedContent <- parseContent (content ++ "\n") return (attr, parsedContent) where - parseContent = parseFromString $ nested $ manyTill parser endOfContent + parseContent = parseFromString $ manyTill parser endOfContent endOfContent = try $ skipMany blankline >> skipSpaces >> eof parseHtmlContent :: PandocMonad m => String -> MuseParser m a -> MuseParser m [a] -- cgit v1.2.3 From ea2ea455b3f7ba5cb4fb9da9b5fd0ef624ab9a3b Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sat, 25 Nov 2017 22:46:25 +0300 Subject: Muse reader: don't interpret XML entities --- src/Text/Pandoc/Readers/Muse.hs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 56ca9b640..2d701fb91 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -58,7 +58,6 @@ import Text.Pandoc.Options import Text.Pandoc.Parsing import Text.Pandoc.Readers.HTML (htmlTag) import Text.Pandoc.Shared (crFilter) -import Text.Pandoc.XML (fromEntities) -- | Read Muse from an input string and return a Pandoc document. readMuse :: PandocMonad m @@ -667,7 +666,7 @@ strikeoutTag = inlineTag B.strikeout "del" verbatimTag :: PandocMonad m => MuseParser m (F Inlines) verbatimTag = do content <- parseHtmlContent "verbatim" anyChar - return $ return $ B.text $ fromEntities content + return $ return $ B.text content code :: PandocMonad m => MuseParser m (F Inlines) code = try $ do @@ -686,7 +685,7 @@ code = try $ do codeTag :: PandocMonad m => MuseParser m (F Inlines) codeTag = do (attrs, content) <- parseHtmlContentWithAttrs "code" anyChar - return $ return $ B.codeWith attrs $ fromEntities content + return $ return $ B.codeWith attrs content inlineLiteralTag :: PandocMonad m => MuseParser m (F Inlines) inlineLiteralTag = do @@ -696,13 +695,13 @@ inlineLiteralTag = do where -- FIXME: Emacs Muse inserts <literal> without style into all output formats, but we assume HTML format (_, _, kvs) = fromMaybe "html" $ lookup "style" kvs - rawInline (attrs, content) = B.rawInline (format attrs) $ fromEntities content + rawInline (attrs, content) = B.rawInline (format attrs) content str :: PandocMonad m => MuseParser m (F Inlines) -str = fmap (return . B.str) (many1 alphaNum <|> count 1 characterReference) +str = return . B.str <$> many1 alphaNum symbol :: PandocMonad m => MuseParser m (F Inlines) -symbol = (return . B.str) <$> count 1 nonspaceChar +symbol = return . B.str <$> count 1 nonspaceChar link :: PandocMonad m => MuseParser m (F Inlines) link = try $ do -- cgit v1.2.3 From 5ba890a973cd1b87bb9f9b51a0be8a70a04cc1fa Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sat, 25 Nov 2017 22:47:29 +0300 Subject: Fix comment typo: s/elemnet/element/ --- src/Text/Pandoc/Readers/HTML.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 2f3b53a90..24935fcd7 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -1147,7 +1147,7 @@ htmlTag f = try $ do -- <www.boe.es/buscar/act.php?id=BOE-A-1996-8930#a66> -- should NOT be parsed as an HTML tag, see #2277, -- so we exclude . even though it's a valid character - -- in XML elemnet names + -- in XML element names let isNameChar c = isAlphaNum c || c == ':' || c == '-' || c == '_' let isName s = case s of [] -> False -- cgit v1.2.3 From bdad8c1d690f791ca5ef36aee07c9874fcf50e53 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sun, 26 Nov 2017 07:32:59 +0300 Subject: Muse reader: drop common space prefix from list items --- src/Text/Pandoc/Readers/Muse.hs | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 2d701fb91..d24f0ba2b 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -376,11 +376,11 @@ withListContext p = do updateState (\st -> st {stateParserContext = oldContext}) return parsed -listContinuation :: PandocMonad m => Int -> MuseParser m String +listContinuation :: PandocMonad m => Int -> MuseParser m [String] listContinuation markerLength = try $ do result <- many1 $ listLine markerLength - blank <- option "" ("\n" <$ blankline) - return $ concat result ++ blank + blank <- option id ((++ ["\n"]) <$ blankline) + return $ blank result listStart :: PandocMonad m => MuseParser m Int -> MuseParser m Int listStart marker = try $ do @@ -388,17 +388,23 @@ listStart marker = try $ do st <- stateParserContext <$> getState getPosition >>= \pos -> guard (st == ListItemState || sourceColumn pos /= 1) markerLength <- marker - void (many1 spaceChar) <|> eol + void spaceChar <|> eol return $ preWhitespace + markerLength + 1 +dropSpacePrefix :: [String] -> [String] +dropSpacePrefix lns = + map (drop maxIndent) lns + where maxIndent = minimum $ map (length . takeWhile (== ' ')) lns + listItemContents :: PandocMonad m => Int -> MuseParser m (F Blocks) listItemContents markerLength = do firstLine <- anyLineNewline restLines <- many $ listLine markerLength - blank <- option "" ("\n" <$ blankline) - let first = firstLine ++ concat restLines ++ blank + blank <- option id ((++ ["\n"]) <$ blankline) + let first = firstLine : blank restLines rest <- many $ listContinuation markerLength - parseFromString (withListContext parseBlocks) $ concat (first:rest) ++ "\n" + let allLines = concat (first : rest) + parseFromString (withListContext parseBlocks) $ concat (dropSpacePrefix allLines) ++ "\n" listItem :: PandocMonad m => MuseParser m Int -> MuseParser m (F Blocks) listItem start = try $ do @@ -436,10 +442,10 @@ definitionListItem = try $ do term <- parseFromString (trimInlinesF . mconcat <$> many inline) rawTerm many1 spaceChar string "::" - firstLine <- many $ noneOf "\n" - restLines <- manyTill anyLineNewline endOfListItemElement - let lns = firstLine : restLines - lineContent <- parseFromString (withListContext parseBlocks) $ concat lns ++ "\n" + firstLine <- manyTill anyChar eol + restLines <- manyTill anyLine endOfListItemElement + let lns = (dropWhile (== ' ') firstLine) : dropSpacePrefix restLines + lineContent <- parseFromString (withListContext parseBlocks) $ unlines lns ++ "\n" pure $ do lineContent' <- lineContent term' <- term pure (term', [lineContent']) -- cgit v1.2.3 From 00004f042c7c49197d57968cae23785ffcba5c63 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 27 Nov 2017 04:51:25 +0300 Subject: Muse reader: make code blocks round trip --- src/Text/Pandoc/Readers/Muse.hs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index d24f0ba2b..4f9e9697d 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -129,6 +129,13 @@ parseHtmlContentWithAttrs tag parser = do parseHtmlContent :: PandocMonad m => String -> MuseParser m a -> MuseParser m [a] parseHtmlContent tag p = fmap snd (parseHtmlContentWithAttrs tag p) +commonPrefix :: String -> String -> String +commonPrefix _ [] = [] +commonPrefix [] _ = [] +commonPrefix (x:xs) (y:ys) + | x == y = x : commonPrefix xs ys + | otherwise = [] + -- -- directive parsers -- @@ -365,7 +372,7 @@ lineBlock = try $ do listLine :: PandocMonad m => Int -> MuseParser m String listLine markerLength = try $ do indentWith markerLength - anyLineNewline + manyTill anyChar eol withListContext :: PandocMonad m => MuseParser m a -> MuseParser m a withListContext p = do @@ -379,7 +386,7 @@ withListContext p = do listContinuation :: PandocMonad m => Int -> MuseParser m [String] listContinuation markerLength = try $ do result <- many1 $ listLine markerLength - blank <- option id ((++ ["\n"]) <$ blankline) + blank <- option id ((++ [""]) <$ blankline) return $ blank result listStart :: PandocMonad m => MuseParser m Int -> MuseParser m Int @@ -394,17 +401,18 @@ listStart marker = try $ do dropSpacePrefix :: [String] -> [String] dropSpacePrefix lns = map (drop maxIndent) lns - where maxIndent = minimum $ map (length . takeWhile (== ' ')) lns + where flns = filter (\s -> not $ all (== ' ') s) lns + maxIndent = if null flns then 0 else length $ takeWhile (== ' ') $ foldl1 commonPrefix flns listItemContents :: PandocMonad m => Int -> MuseParser m (F Blocks) listItemContents markerLength = do - firstLine <- anyLineNewline + firstLine <- manyTill anyChar eol restLines <- many $ listLine markerLength - blank <- option id ((++ ["\n"]) <$ blankline) + blank <- option id ((++ [""]) <$ blankline) let first = firstLine : blank restLines rest <- many $ listContinuation markerLength let allLines = concat (first : rest) - parseFromString (withListContext parseBlocks) $ concat (dropSpacePrefix allLines) ++ "\n" + parseFromString (withListContext parseBlocks) $ unlines (dropSpacePrefix allLines) listItem :: PandocMonad m => MuseParser m Int -> MuseParser m (F Blocks) listItem start = try $ do @@ -444,8 +452,8 @@ definitionListItem = try $ do string "::" firstLine <- manyTill anyChar eol restLines <- manyTill anyLine endOfListItemElement - let lns = (dropWhile (== ' ') firstLine) : dropSpacePrefix restLines - lineContent <- parseFromString (withListContext parseBlocks) $ unlines lns ++ "\n" + let lns = dropWhile (== ' ') firstLine : dropSpacePrefix restLines + lineContent <- parseFromString (withListContext parseBlocks) $ unlines lns pure $ do lineContent' <- lineContent term' <- term pure (term', [lineContent']) -- cgit v1.2.3 From a1378ed76bbefdd85e36674158103e991be6c578 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 26 Nov 2017 22:31:32 -0800 Subject: Clearer deprecation warning for --latexmathml, --asciimathml, -m. Previously we only mentioned `--latexmathml`, even if `-m` was used. --- src/Text/Pandoc/App.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 41b6a310b..34410191f 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -1437,7 +1437,7 @@ options = , Option "m" ["latexmathml", "asciimathml"] (OptArg (\arg opt -> do - deprecatedOption "--latexmathml" + deprecatedOption "--latexmathml, --asciimathml, -m" return opt { optHTMLMathMethod = LaTeXMathML arg }) "URL") "" -- "Use LaTeXMathML script in html output" -- cgit v1.2.3 From c2993a6fc6882ed4b5fc4162238cfda8f84dbc81 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 27 Nov 2017 12:24:55 +0300 Subject: Muse reader: parse "~~" as non-breaking space in Emacs mode --- src/Text/Pandoc/Readers/Muse.hs | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 4f9e9697d..794ca7385 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -581,6 +581,7 @@ inlineList = [ endline , subscriptTag , strikeoutTag , verbatimTag + , nbsp , link , code , codeTag @@ -682,6 +683,12 @@ verbatimTag = do content <- parseHtmlContent "verbatim" anyChar return $ return $ B.text content +nbsp :: PandocMonad m => MuseParser m (F Inlines) +nbsp = do + guardDisabled Ext_amuse -- Supported only by Emacs Muse + string "~~" + return $ return $ B.str "\160" + code :: PandocMonad m => MuseParser m (F Inlines) code = try $ do pos <- getPosition -- cgit v1.2.3 From de75d4eaf93aede14f500368168c0aa62a2427ea Mon Sep 17 00:00:00 2001 From: Mauro Bieg <mb21@users.noreply.github.com> Date: Tue, 28 Nov 2017 02:30:53 +0100 Subject: Fix --help output for --highlight-style to include FILE (#4098) Closes #4095. --- src/Text/Pandoc/App.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 34410191f..3c52e4247 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -1119,7 +1119,7 @@ options = , Option "" ["highlight-style"] (ReqArg (\arg opt -> return opt{ optHighlightStyle = Just arg }) - "STYLE") + "STYLE|FILE") "" -- "Style for highlighted code" , Option "" ["syntax-definition"] -- cgit v1.2.3 From 393ce6f1e3f9ac2b5ab5cee44560332ce9902ae1 Mon Sep 17 00:00:00 2001 From: Mauro Bieg <mb21@users.noreply.github.com> Date: Tue, 28 Nov 2017 19:15:35 +0100 Subject: make normalizeDate more forgiving (#4101) also parse two-digit days, e.g. "April 20, 2017" --- src/Text/Pandoc/Shared.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index e0ea8b5e7..5c13e0acb 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -291,7 +291,7 @@ normalizeDate s = fmap (formatTime defaultTimeLocale "%F") parseTime defaultTimeLocale #endif formats = ["%x","%m/%d/%Y", "%D","%F", "%d %b %Y", - "%d %B %Y", "%b. %d, %Y", "%B %d, %Y", + "%e %B %Y", "%b. %e, %Y", "%B %e, %Y", "%Y%m%d", "%Y%m", "%Y"] -- -- cgit v1.2.3 From 00561b1bb956a5b4de291e80001cf752c38c4549 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 28 Nov 2017 10:56:32 -0800 Subject: Support `--webtex` for `gfm` output. --- src/Text/Pandoc/Writers/CommonMark.hs | 22 ++++++++++++++++------ src/Text/Pandoc/Writers/Markdown.hs | 5 ++--- 2 files changed, 18 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/CommonMark.hs b/src/Text/Pandoc/Writers/CommonMark.hs index 8677dd840..f4d376458 100644 --- a/src/Text/Pandoc/Writers/CommonMark.hs +++ b/src/Text/Pandoc/Writers/CommonMark.hs @@ -39,6 +39,7 @@ import Data.List (transpose) import Data.Monoid (Any (..), (<>)) import Data.Text (Text) import qualified Data.Text as T +import Network.HTTP (urlEncode) import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Options @@ -276,12 +277,21 @@ inlineToNodes opts (Quoted qt ils) = | isEnabled Ext_smart opts -> ("\"", "\"") | otherwise -> ("“", "”") inlineToNodes _ (Code _ str) = (node (CODE (T.pack str)) [] :) -inlineToNodes _ (Math mt str) = - case mt of - InlineMath -> - (node (HTML_INLINE (T.pack ("\\(" ++ str ++ "\\)"))) [] :) - DisplayMath -> - (node (HTML_INLINE (T.pack ("\\[" ++ str ++ "\\]"))) [] :) +inlineToNodes opts (Math mt str) = + case writerHTMLMathMethod opts of + WebTeX url -> + let core = inlineToNodes opts + (Image nullAttr [Str str] (url ++ urlEncode str, str)) + sep = if mt == DisplayMath + then (node LINEBREAK [] :) + else id + in (sep . core . sep) + _ -> + case mt of + InlineMath -> + (node (HTML_INLINE (T.pack ("\\(" ++ str ++ "\\)"))) [] :) + DisplayMath -> + (node (HTML_INLINE (T.pack ("\\[" ++ str ++ "\\]"))) [] :) inlineToNodes opts (Span _ ils) = (inlinesToNodes opts ils ++) inlineToNodes opts (Cite _ ils) = (inlinesToNodes opts ils ++) inlineToNodes _ (Note _) = id -- should not occur diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index a8452f468..7a3d204f2 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -1068,9 +1068,8 @@ inlineToMarkdown opts (Str str) = do return $ text str' inlineToMarkdown opts (Math InlineMath str) = case writerHTMLMathMethod opts of - WebTeX url -> - inlineToMarkdown opts (Image nullAttr [Str str] - (url ++ urlEncode str, str)) + WebTeX url -> inlineToMarkdown opts + (Image nullAttr [Str str] (url ++ urlEncode str, str)) _ | isEnabled Ext_tex_math_dollars opts -> return $ "$" <> text str <> "$" | isEnabled Ext_tex_math_single_backslash opts -> -- cgit v1.2.3 From 845b6c8670a6d1a11c1a31ec15ec2f0831883cbe Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 28 Nov 2017 13:35:49 -0800 Subject: Added --print-highlight-style option. This generates a JSON version of a highlighting style, which can be saved as a .theme file, modified, and used with `--highlight-style`. Closes #4106. Closes #4096. --- src/Text/Pandoc/App.hs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 3c52e4247..f1c21c69a 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -63,7 +63,10 @@ import qualified Data.Yaml as Yaml import GHC.Generics import Network.URI (URI (..), parseURI) import Paths_pandoc (getDataDir) -import Skylighting (Style, Syntax (..), defaultSyntaxMap, parseTheme) +import Data.Aeson.Encode.Pretty (encodePretty', Config(..), keyOrder, + defConfig, Indent(..), NumberFormat(..)) +import Skylighting (Style, Syntax (..), defaultSyntaxMap, parseTheme, + pygments) import Skylighting.Parser (addSyntaxDefinition, missingIncludes, parseSyntaxDefinition) import System.Console.GetOpt @@ -1047,6 +1050,28 @@ options = "FILE") "" -- "Print default data file" + , Option "" ["print-highlight-style"] + (ReqArg + (\arg _ -> do + sty <- fromMaybe pygments <$> + lookupHighlightStyle (Just arg) + B.putStr $ encodePretty' + defConfig{confIndent = Spaces 4 + ,confCompare = keyOrder + (map T.pack + ["text-color" + ,"background-color" + ,"line-numbers" + ,"bold" + ,"italic" + ,"underline" + ,"text-styles"]) + ,confNumFormat = Generic + ,confTrailingNewline = True} sty + exitSuccess) + "STYLE|FILE") + "" -- "Print default template for FORMAT" + , Option "" ["dpi"] (ReqArg (\arg opt -> -- cgit v1.2.3 From 0105a3c2930675971ea83da3b987c6b62f97ce26 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert+github@zeitkraut.de> Date: Wed, 29 Nov 2017 01:20:01 +0100 Subject: Add basic lua List module (#4099) The List module is automatically loaded, but not assigned to a global variable. It can be included in filters by calling `List = require 'List'`. Lists of blocks, lists of inlines, and lists of classes are now given `List` as a metatable, making working with them more convenient. E.g., it is now possible to concatenate lists of inlines using Lua's concatenation operator `..` (requires at least one of the operants to have `List` as a metatable): function Emph (emph) local s = {pandoc.Space(), pandoc.Str 'emphasized'} return pandoc.Span(emph.content .. s) end Closes: #4081 --- src/Text/Pandoc/Lua/PandocModule.hs | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index ac7839d0f..ba3193211 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -59,10 +59,12 @@ import qualified Foreign.Lua as Lua import qualified Text.Pandoc.MediaBag as MB import Text.Pandoc.Lua.Filter (walkInlines, walkBlocks, LuaFilter) --- | Push the "pandoc" on the lua stack. +-- | Push the "pandoc" on the lua stack. Requires the `list` module to be +-- loaded. pushPandocModule :: Maybe FilePath -> Lua () pushPandocModule datadir = do - script <- liftIO (pandocModuleScript datadir) + loadListModule datadir + script <- liftIO (moduleScript datadir "pandoc.lua") status <- Lua.loadstring script unless (status /= Lua.OK) $ Lua.call 0 1 addFunction "_pipe" pipeFn @@ -72,9 +74,25 @@ pushPandocModule datadir = do addFunction "walk_inline" walkInline -- | Get the string representation of the pandoc module -pandocModuleScript :: Maybe FilePath -> IO String -pandocModuleScript datadir = unpack <$> - runIOorExplode (setUserDataDir datadir >> readDataFile "pandoc.lua") +moduleScript :: Maybe FilePath -> FilePath -> IO String +moduleScript datadir moduleFile = unpack <$> + runIOorExplode (setUserDataDir datadir >> readDataFile moduleFile) + +-- Loads pandoc's list module without assigning it to a variable. +pushListModule :: Maybe FilePath -> Lua () +pushListModule datadir = do + script <- liftIO (moduleScript datadir "List.lua") + status <- Lua.loadstring script + if status == Lua.OK + then Lua.call 0 1 + else Lua.throwTopMessageAsError' ("Error while loading module `list`\n" ++) + +loadListModule :: Maybe FilePath -> Lua () +loadListModule datadir = do + Lua.getglobal' "package.loaded" + pushListModule datadir + Lua.setfield (-2) "List" + Lua.pop 1 walkElement :: (ToLuaStack a, Walkable [Inline] a, Walkable [Block] a) => a -> LuaFilter -> Lua NumResults -- cgit v1.2.3 From 7751391fce9a51066e02ecbe3677e69224c2161d Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Wed, 29 Nov 2017 05:09:10 +0300 Subject: Muse reader: correctly remove indentation from notes Exactly one space is required and considered to be part of the marker. --- src/Text/Pandoc/Readers/Muse.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 794ca7385..f10a2172f 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -318,8 +318,8 @@ amuseNoteBlock :: PandocMonad m => MuseParser m (F Blocks) amuseNoteBlock = try $ do guardEnabled Ext_amuse pos <- getPosition - ref <- noteMarker <* skipSpaces - content <- listItemContents $ 2 + length ref + ref <- noteMarker <* spaceChar + content <- listItemContents $ 3 + length ref oldnotes <- stateNotes' <$> getState case M.lookup ref oldnotes of Just _ -> logMessage $ DuplicateNoteReference ref pos -- cgit v1.2.3 From a276bb0a8a07650368f3007820b96909c2734ff0 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 29 Nov 2017 09:45:38 -0800 Subject: Let papersizes a0, a1, a2, ... be case-insensitive in LaTeX and ConTeXt. --- src/Text/Pandoc/Writers/ConTeXt.hs | 7 ++++--- src/Text/Pandoc/Writers/LaTeX.hs | 6 ++++-- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index 63113ac82..f0f4cd00e 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -31,7 +31,7 @@ Conversion of 'Pandoc' format into ConTeXt. -} module Text.Pandoc.Writers.ConTeXt ( writeConTeXt ) where import Control.Monad.State.Strict -import Data.Char (ord) +import Data.Char (ord, isDigit) import Data.List (intercalate, intersperse) import Data.Maybe (mapMaybe) import Data.Text (Text) @@ -104,8 +104,9 @@ pandocToConTeXt options (Pandoc meta blocks) = do $ defField "number-sections" (writerNumberSections options) $ maybe id (defField "context-lang") mblang $ (case getField "papersize" metadata of - Just ("a4" :: String) -> resetField "papersize" - ("A4" :: String) + Just (('a':d:ds) :: String) + | all isDigit (d:ds) -> resetField "papersize" + (('A':d:ds) :: String) _ -> id) metadata let context' = defField "context-dir" (toContextDir $ getField "dir" context) context diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 156af4bb2..8620f989b 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -253,8 +253,10 @@ pandocToLaTeX options (Pandoc meta blocks) = do defField "section-titles" True $ defField "geometry" geometryFromMargins $ (case getField "papersize" metadata of - Just ("A4" :: String) -> resetField "papersize" - ("a4" :: String) + -- uppercase a4, a5, etc. + Just (('A':d:ds) :: String) + | all isDigit (d:ds) -> resetField "papersize" + (('a':d:ds) :: String) _ -> id) metadata let context' = -- cgit v1.2.3 From 03ddac451edcd6eb8dfa1a77b174fd146aa5e722 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 29 Nov 2017 21:30:13 -0800 Subject: Support beamer `\alert` in LaTeX reader. Closes #4091. --- 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 3bc59f262..d1d9682c3 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1245,6 +1245,7 @@ inlineCommands = M.union inlineLanguageCommands $ M.fromList $ , ("textup", extractSpaces (spanWith ("",["upright"],[])) <$> tok) , ("texttt", ttfamily) , ("sout", extractSpaces strikeout <$> tok) + , ("alert", skipangles >> spanWith ("",["alert"],[]) <$> tok) -- beamer , ("lq", return (str "‘")) , ("rq", return (str "’")) , ("textquoteleft", return (str "‘")) -- cgit v1.2.3 From 171187a4527497701b3c77bd56cea2d770d4e3b0 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 30 Nov 2017 16:02:59 -0800 Subject: LaTeX writer: Add keepaspectratio to includegraphics... ...if only one of height/width is given. --- src/Text/Pandoc/Writers/LaTeX.hs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 8620f989b..1972269ff 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -1125,7 +1125,12 @@ inlineToLaTeX (Image attr _ (source, _)) = do [d <> text (show dim)] Nothing -> [] - dimList = showDim Width ++ showDim Height + -- if we just have a width or a height, we add keepaspectratio: + keepaspectratio = case (dimension Height attr, dimension Width attr) of + (Nothing, Just _) -> ["keepaspectratio"] + (Just _, Nothing) -> ["keepaspectratio"] + _ -> [] + dimList = showDim Width ++ showDim Height ++ keepaspectratio dims = if null dimList then empty else brackets $ cat (intersperse "," dimList) -- cgit v1.2.3 From 8473a151c5685f8ceb515abf6000ab4fb7a3911c Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Fri, 1 Dec 2017 17:12:56 +0100 Subject: List.lua: add missing fixes as discussed in #4099 The changes were missing due to an error while using git. --- src/Text/Pandoc/Lua/PandocModule.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index ba3193211..4df01f019 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -91,7 +91,7 @@ loadListModule :: Maybe FilePath -> Lua () loadListModule datadir = do Lua.getglobal' "package.loaded" pushListModule datadir - Lua.setfield (-2) "List" + Lua.setfield (-2) "pandoc.List" Lua.pop 1 walkElement :: (ToLuaStack a, Walkable [Inline] a, Walkable [Block] a) -- cgit v1.2.3 From 6640506ddc0ab848824d818a363c2e685b8b31a5 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Fri, 1 Dec 2017 17:58:12 +0100 Subject: Lua/StackInstances: push Pandoc and Meta via constructor Pandoc and Meta elements are now pushed by calling the respective constructor functions of the pandoc Lua module. This makes serialization consistent with the way blocks and inlines are pushed to lua and allows to use List methods with the `blocks` value. --- src/Text/Pandoc/Lua/StackInstances.hs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index 3eb14eba3..ce6dbdb98 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -36,17 +36,14 @@ import Control.Applicative ((<|>)) import Foreign.Lua (FromLuaStack (peek), Lua, LuaInteger, LuaNumber, StackIndex, ToLuaStack (push), Type (..), throwLuaError, tryLua) import Text.Pandoc.Definition -import Text.Pandoc.Lua.Util (addValue, adjustIndexBy, getTable, - pushViaConstructor) +import Text.Pandoc.Lua.Util (adjustIndexBy, getTable, pushViaConstructor) import Text.Pandoc.Shared (safeRead) import qualified Foreign.Lua as Lua instance ToLuaStack Pandoc where - push (Pandoc meta blocks) = do - Lua.newtable - addValue "blocks" blocks - addValue "meta" meta + push (Pandoc meta blocks) = + pushViaConstructor "Pandoc" blocks meta instance FromLuaStack Pandoc where peek idx = do @@ -55,7 +52,8 @@ instance FromLuaStack Pandoc where return $ Pandoc meta blocks instance ToLuaStack Meta where - push (Meta mmap) = push mmap + push (Meta mmap) = + pushViaConstructor "Meta" mmap instance FromLuaStack Meta where peek idx = Meta <$> peek idx -- cgit v1.2.3 From b2a190546d9f8bbc853b5e65539093275252e0ef Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 1 Dec 2017 13:51:00 -0800 Subject: Revert "LaTeX writer: Add keepaspectratio to includegraphics..." This reverts commit 171187a4527497701b3c77bd56cea2d770d4e3b0. --- src/Text/Pandoc/Writers/LaTeX.hs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 1972269ff..8620f989b 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -1125,12 +1125,7 @@ inlineToLaTeX (Image attr _ (source, _)) = do [d <> text (show dim)] Nothing -> [] - -- if we just have a width or a height, we add keepaspectratio: - keepaspectratio = case (dimension Height attr, dimension Width attr) of - (Nothing, Just _) -> ["keepaspectratio"] - (Just _, Nothing) -> ["keepaspectratio"] - _ -> [] - dimList = showDim Width ++ showDim Height ++ keepaspectratio + dimList = showDim Width ++ showDim Height dims = if null dimList then empty else brackets $ cat (intersperse "," dimList) -- cgit v1.2.3 From 29ec13184ddb869038ca712e260e8a135992c8e9 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 1 Dec 2017 17:17:26 -0800 Subject: LaTeX writer: escape ~ in code with --listings. Closes #4111. --- src/Text/Pandoc/Writers/LaTeX.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 8620f989b..70744bde3 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -1013,7 +1013,7 @@ inlineToLaTeX (Code (_,classes,_) str) = do let chr = case "!\"&'()*,-./:;?@_" \\ str of (c:_) -> c [] -> '!' - let str' = escapeStringUsing (backslashEscapes "\\{}%") str + let str' = escapeStringUsing (backslashEscapes "\\{}%~") str -- we always put lstinline in a dummy 'passthrough' command -- (defined in the default template) so that we don't have -- to change the way we escape characters depending on whether -- cgit v1.2.3 From 1193c1a505aac616d41a7b1c61c0cde07d2560d2 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 1 Dec 2017 21:18:29 -0800 Subject: LaTeX writer: allow specifying just width or height for image size. Previously both needed to be specified (unless the image was being resized to be smaller than its original size). If height but not width is specified, we now set width to textwidth (and similarly if width but not height is specified). Since we have keepaspectratio, this yields the desired result. --- src/Text/Pandoc/Writers/LaTeX.hs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 70744bde3..18138bf4c 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -1124,7 +1124,12 @@ inlineToLaTeX (Image attr _ (source, _)) = do Just dim -> [d <> text (show dim)] Nothing -> - [] + case dir of + Width | isJust (dimension Height attr) -> + [d <> "\\textwidth"] + Height | isJust (dimension Width attr) -> + [d <> "\\textheight"] + _ -> [] dimList = showDim Width ++ showDim Height dims = if null dimList then empty -- cgit v1.2.3 From 22eb15ec3521fefdeaa93cb6fa02931afb602fe0 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 2 Dec 2017 10:31:58 -0800 Subject: LaTeX writer: escape `_` in code with --listings. --- src/Text/Pandoc/Writers/LaTeX.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 18138bf4c..3b2cd214e 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -1013,7 +1013,7 @@ inlineToLaTeX (Code (_,classes,_) str) = do let chr = case "!\"&'()*,-./:;?@_" \\ str of (c:_) -> c [] -> '!' - let str' = escapeStringUsing (backslashEscapes "\\{}%~") str + let str' = escapeStringUsing (backslashEscapes "\\{}%~_") str -- we always put lstinline in a dummy 'passthrough' command -- (defined in the default template) so that we don't have -- to change the way we escape characters depending on whether -- cgit v1.2.3 From 7b8c2b6691e3816ba52ee07ee7f63573d4ae7253 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 2 Dec 2017 15:21:59 -0800 Subject: Add --strip-empty-paragraphs option. This works for any input format. --- src/Text/Pandoc/App.hs | 20 +++++++++++++++----- src/Text/Pandoc/Shared.hs | 9 +++++++++ 2 files changed, 24 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index f1c21c69a..3fdbf1949 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -86,8 +86,8 @@ import Text.Pandoc.Lua (LuaException (..), runLuaFilter) import Text.Pandoc.PDF (makePDF) import Text.Pandoc.Process (pipeProcess) import Text.Pandoc.SelfContained (makeDataURI, makeSelfContained) -import Text.Pandoc.Shared (eastAsianLineBreakFilter, headerShift, isURI, ordNub, - safeRead, tabFilter) +import Text.Pandoc.Shared (eastAsianLineBreakFilter, stripEmptyParagraphs, + headerShift, isURI, ordNub, safeRead, tabFilter) import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Writers.Math (defaultKaTeXURL, defaultMathJaxURL) import Text.Pandoc.XML (toEntities) @@ -461,14 +461,17 @@ convertWithOpts opts = do let transforms = (case optBaseHeaderLevel opts of x | x > 1 -> (headerShift (x - 1) :) - | otherwise -> id) $ + | otherwise -> id) . + (if optStripEmptyParagraphs opts + then (stripEmptyParagraphs :) + else id) . (if extensionEnabled Ext_east_asian_line_breaks readerExts && not (extensionEnabled Ext_east_asian_line_breaks writerExts && writerWrapText writerOptions == WrapPreserve) then (eastAsianLineBreakFilter :) - else id) + else id) $ [] let sourceToDoc :: [FilePath] -> PandocIO Pandoc @@ -622,6 +625,7 @@ data Opt = Opt , optLuaFilters :: [FilePath] -- ^ Lua filters to apply , optEmailObfuscation :: ObfuscationMethod , optIdentifierPrefix :: String + , optStripEmptyParagraphs :: Bool -- ^ Strip empty paragraphs , optIndentedCodeClasses :: [String] -- ^ Default classes for indented code blocks , optDataDir :: Maybe FilePath , optCiteMethod :: CiteMethod -- ^ Method to output cites @@ -694,6 +698,7 @@ defaultOpts = Opt , optLuaFilters = [] , optEmailObfuscation = NoObfuscation , optIdentifierPrefix = "" + , optStripEmptyParagraphs = False , optIndentedCodeClasses = [] , optDataDir = Nothing , optCiteMethod = Citeproc @@ -940,7 +945,12 @@ options = "NUMBER") "" -- "Headers base level" - , Option "" ["indented-code-classes"] + , Option "" ["strip-empty-paragraphs"] + (NoArg + (\opt -> return opt{ optStripEmptyParagraphs = True })) + "" -- "Strip empty paragraphs" + + , Option "" ["indented-code-classes"] (ReqArg (\arg opt -> return opt { optIndentedCodeClasses = words $ map (\c -> if c == ',' then ' ' else c) arg }) diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 5c13e0acb..1c3a25cc7 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -72,6 +72,7 @@ module Text.Pandoc.Shared ( inlineListToIdentifier, isHeaderBlock, headerShift, + stripEmptyParagraphs, isTightList, addMetaField, makeMeta, @@ -529,6 +530,14 @@ headerShift n = walk shift shift (Header level attr inner) = Header (level + n) attr inner shift x = x +-- | Remove empty paragraphs. +stripEmptyParagraphs :: Pandoc -> Pandoc +stripEmptyParagraphs = walk go + where go :: [Block] -> [Block] + go = filter (not . isEmptyParagraph) + isEmptyParagraph (Para []) = True + isEmptyParagraph _ = False + -- | Detect if a list is tight. isTightList :: [[Block]] -> Bool isTightList = all firstIsPlain -- cgit v1.2.3 From f4b86a1bc2c77d50057399f9d93163c2bbc053bd Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 2 Dec 2017 16:26:26 -0800 Subject: Shared.blocksToInlines: rewrote using builder. This gives us automatic normalization, so we don't get for example two consecutive Spaces. --- src/Text/Pandoc/Shared.hs | 51 +++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 24 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 1c3a25cc7..975847de4 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -107,7 +107,7 @@ import qualified Data.ByteString.Lazy as BL import Data.Char (isAlpha, isDigit, isLetter, isLower, isSpace, isUpper, toLower) import Data.Data (Data, Typeable) -import Data.List (find, intercalate, stripPrefix) +import Data.List (find, intercalate, intersperse, stripPrefix) import qualified Data.Map as M import Data.Maybe (mapMaybe) import Data.Monoid ((<>)) @@ -717,37 +717,40 @@ isURI = maybe False hasKnownScheme . parseURI --- Squash blocks into inlines --- -blockToInlines :: Block -> [Inline] -blockToInlines (Plain ils) = ils -blockToInlines (Para ils) = ils -blockToInlines (LineBlock lns) = combineLines lns -blockToInlines (CodeBlock attr str) = [Code attr str] -blockToInlines (RawBlock fmt str) = [RawInline fmt str] -blockToInlines (BlockQuote blks) = blocksToInlines blks +blockToInlines :: Block -> Inlines +blockToInlines (Plain ils) = B.fromList ils +blockToInlines (Para ils) = B.fromList ils +blockToInlines (LineBlock lns) = B.fromList $ combineLines lns +blockToInlines (CodeBlock attr str) = B.codeWith attr str +blockToInlines (RawBlock (Format fmt) str) = B.rawInline fmt str +blockToInlines (BlockQuote blks) = blocksToInlines' blks blockToInlines (OrderedList _ blkslst) = - concatMap blocksToInlines blkslst + mconcat $ map blocksToInlines' blkslst blockToInlines (BulletList blkslst) = - concatMap blocksToInlines blkslst + mconcat $ map blocksToInlines' blkslst blockToInlines (DefinitionList pairslst) = - concatMap f pairslst + mconcat $ map f pairslst where - f (ils, blkslst) = ils ++ - [Str ":", Space] ++ - concatMap blocksToInlines blkslst -blockToInlines (Header _ _ ils) = ils -blockToInlines HorizontalRule = [] + f (ils, blkslst) = B.fromList ils <> B.str ":" <> B.space <> + mconcat (map blocksToInlines' blkslst) +blockToInlines (Header _ _ ils) = B.fromList ils +blockToInlines HorizontalRule = mempty blockToInlines (Table _ _ _ headers rows) = - intercalate [LineBreak] $ map (concatMap blocksToInlines) tbl - where - tbl = headers : rows -blockToInlines (Div _ blks) = blocksToInlines blks -blockToInlines Null = [] + mconcat $ intersperse B.linebreak $ + map (mconcat . map blocksToInlines') (headers:rows) +blockToInlines (Div _ blks) = blocksToInlines' blks +blockToInlines Null = mempty + +blocksToInlinesWithSep :: Inlines -> [Block] -> Inlines +blocksToInlinesWithSep sep = + mconcat . intersperse sep . map blockToInlines -blocksToInlinesWithSep :: [Inline] -> [Block] -> [Inline] -blocksToInlinesWithSep sep blks = intercalate sep $ map blockToInlines blks +blocksToInlines' :: [Block] -> Inlines +blocksToInlines' = blocksToInlinesWithSep parSep + where parSep = B.space <> B.str "¶" <> B.space blocksToInlines :: [Block] -> [Inline] -blocksToInlines = blocksToInlinesWithSep [Space, Str "¶", Space] +blocksToInlines = B.toList . blocksToInlines' -- -- cgit v1.2.3 From d6c58eb836f033a48955796de4d9ffb3b30e297b Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 2 Dec 2017 15:31:53 -0800 Subject: Docx reader: don't strip out empty paragraphs. We now have the `--strip-empty-paragraphs` option for that, if you want it. Closes #2252. Updated docx reader tests. We use stripEmptyParagraphs to avoid changing too many tests. We should add new tests for empty paragraphs. --- src/Text/Pandoc/Readers/Docx.hs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index 295b79195..1fac98b14 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -534,9 +534,7 @@ bodyPartToBlocks (Paragraph pPr parparts) then do modify $ \s -> s { docxDropCap = ils' } return mempty else do modify $ \s -> s { docxDropCap = mempty } - return $ case isNull ils' of - True -> mempty - _ -> parStyleToTransform pPr $ para ils' + return $ parStyleToTransform pPr $ para ils' bodyPartToBlocks (ListItem pPr numId lvl (Just levelInfo) parparts) = do let kvs = case levelInfo of -- cgit v1.2.3 From 92c527713b814144c91296537afeb14dc4faab76 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 2 Dec 2017 17:17:23 -0800 Subject: Docx writer: allow empty paragraphs. See #2252. This also changes fixDisplayMath from Text.Pandoc.Writers.Shared so that it no longer produces empty Para as an artifact. (That was the original reason the writer omitted them.) --- src/Text/Pandoc/Writers/Docx.hs | 2 -- src/Text/Pandoc/Writers/Shared.hs | 10 ++++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index d146ebf84..f80c2b59a 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -922,8 +922,6 @@ blockToOpenXML' opts (Para [Image attr alt (src,'f':'i':'g':':':tit)]) = do captionNode <- withParaProp (pCustomStyle "ImageCaption") $ blockToOpenXML opts (Para alt) return $ mknode "w:p" [] (paraProps ++ contents) : captionNode --- fixDisplayMath sometimes produces a Para [] as artifact -blockToOpenXML' _ (Para []) = return [] blockToOpenXML' opts (Para lst) = do isFirstPara <- gets stFirstPara paraProps <- getParaProps $ case lst of diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index 0b951b0c9..83280fa5c 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -196,13 +196,19 @@ fixDisplayMath :: Block -> Block fixDisplayMath (Plain lst) | any isDisplayMath lst && not (all isDisplayMath lst) = -- chop into several paragraphs so each displaymath is its own - Div ("",["math"],[]) $ map (Plain . stripLeadingTrailingSpace) $ + Div ("",["math"],[]) $ + map Plain $ + filter (not . null) $ + map stripLeadingTrailingSpace $ groupBy (\x y -> (isDisplayMath x && isDisplayMath y) || not (isDisplayMath x || isDisplayMath y)) lst fixDisplayMath (Para lst) | any isDisplayMath lst && not (all isDisplayMath lst) = -- chop into several paragraphs so each displaymath is its own - Div ("",["math"],[]) $ map (Para . stripLeadingTrailingSpace) $ + Div ("",["math"],[]) $ + map Para $ + filter (not . null) $ + map stripLeadingTrailingSpace $ groupBy (\x y -> (isDisplayMath x && isDisplayMath y) || not (isDisplayMath x || isDisplayMath y)) lst fixDisplayMath x = x -- cgit v1.2.3 From 0a091f1463135f95828f0f11f0b9747f81bec389 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 3 Dec 2017 11:45:22 -0800 Subject: commonmark/gfm writer: implement `raw_html` and `raw_tex` extensions. Note that `raw_html` is enabled by default for `gfm`, while `raw_tex` is disabled by default. --- src/Text/Pandoc/Writers/CommonMark.hs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/CommonMark.hs b/src/Text/Pandoc/Writers/CommonMark.hs index f4d376458..48e3923dd 100644 --- a/src/Text/Pandoc/Writers/CommonMark.hs +++ b/src/Text/Pandoc/Writers/CommonMark.hs @@ -111,9 +111,12 @@ blockToNodes opts (Para xs) ns = blockToNodes opts (LineBlock lns) ns = blockToNodes opts (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 opts (RawBlock fmt xs) ns + | fmt == Format "html" && isEnabled Ext_raw_html opts + = return (node (HTML_BLOCK (T.pack xs)) [] : ns) + | fmt == Format "latex" || fmt == Format "tex" && isEnabled Ext_raw_tex opts + = return (node (CUSTOM_BLOCK (T.pack xs) T.empty) [] : ns) + | otherwise = return ns blockToNodes opts (BlockQuote bs) ns = do nodes <- blocksToNodes opts bs return (node BLOCK_QUOTE nodes : ns) @@ -263,9 +266,12 @@ inlineToNodes opts (Image alt ils (url,'f':'i':'g':':':tit)) = inlineToNodes opts (Image alt ils (url,tit)) inlineToNodes opts (Image _ ils (url,tit)) = (node (IMAGE (T.pack url) (T.pack tit)) (inlinesToNodes opts ils) :) -inlineToNodes _ (RawInline fmt xs) - | fmt == Format "html" = (node (HTML_INLINE (T.pack xs)) [] :) - | otherwise = (node (CUSTOM_INLINE (T.pack xs) T.empty) [] :) +inlineToNodes opts (RawInline fmt xs) + | fmt == Format "html" && isEnabled Ext_raw_html opts + = (node (HTML_INLINE (T.pack xs)) [] :) + | (fmt == Format "latex" || fmt == Format "tex") && isEnabled Ext_raw_tex opts + = (node (CUSTOM_INLINE (T.pack xs) T.empty) [] :) + | otherwise = id inlineToNodes opts (Quoted qt ils) = ((node (TEXT start) [] : inlinesToNodes opts ils ++ [node (TEXT end) []]) ++) -- cgit v1.2.3 From 5d0863d19838cc5fab15664bceec103d7b563d35 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 3 Dec 2017 12:09:40 -0800 Subject: HTML writer: export tagWithAttributes. This is a helper allowing other writers to create single HTML tags. --- src/Text/Pandoc/Writers/HTML.hs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 2dc8b7a61..7fdfa567e 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -41,7 +41,8 @@ module Text.Pandoc.Writers.HTML ( writeSlidy, writeSlideous, writeDZSlides, - writeRevealJs + writeRevealJs, + tagWithAttributes ) where import Control.Monad.State.Strict import Data.Char (ord, toLower) @@ -55,6 +56,7 @@ import qualified Data.Text.Lazy as TL import Network.HTTP (urlEncode) import Network.URI (URI (..), parseURIReference, unEscapeString) import Numeric (showHex) +import Text.Blaze.Internal (customLeaf, textTag) import Text.Blaze.Html hiding (contents) import Text.Pandoc.Definition import Text.Pandoc.Highlighting (formatHtmlBlock, formatHtmlInline, highlight, @@ -83,7 +85,7 @@ import System.FilePath (takeBaseName, takeExtension) import Text.Blaze.Html.Renderer.Text (renderHtml) import qualified Text.Blaze.XHtml1.Transitional as H import qualified Text.Blaze.XHtml1.Transitional.Attributes as A -import Text.Pandoc.Class (PandocMonad, report) +import Text.Pandoc.Class (PandocMonad, report, runPure) import Text.Pandoc.Error import Text.Pandoc.Logging import Text.TeXMath @@ -542,6 +544,21 @@ obfuscateChar char = obfuscateString :: String -> String obfuscateString = concatMap obfuscateChar . fromEntities +-- | Create HTML tag with attributes. +tagWithAttributes :: WriterOptions + -> Bool -- ^ True for HTML5 + -> Bool -- ^ True if self-closing tag + -> Text -- ^ Tag text + -> Attr -- ^ Pandoc style tag attributes + -> Text +tagWithAttributes opts html5 selfClosing tagname attr = + let mktag = (TL.toStrict . renderHtml <$> evalStateT + (addAttrs opts attr (customLeaf (textTag tagname) selfClosing)) + defaultWriterState{ stHtml5 = html5 }) + in case runPure mktag of + Left _ -> mempty + Right t -> t + addAttrs :: PandocMonad m => WriterOptions -> Attr -> Html -> StateT WriterState m Html addAttrs opts attr h = foldl (!) h <$> attrsToHtml opts attr -- cgit v1.2.3 From d25017fc7089a07af4c28dd76dc1709952fac5cc Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 3 Dec 2017 12:29:57 -0800 Subject: commonmark/gfm writer: use raw html for native divs/spans. This allows a pandoc markdown native div or span to be rendered in gfm using raw html tags. --- src/Text/Pandoc/Writers/CommonMark.hs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/CommonMark.hs b/src/Text/Pandoc/Writers/CommonMark.hs index 48e3923dd..8d1eb04d1 100644 --- a/src/Text/Pandoc/Writers/CommonMark.hs +++ b/src/Text/Pandoc/Writers/CommonMark.hs @@ -46,7 +46,7 @@ import Text.Pandoc.Options import Text.Pandoc.Shared (isTightList, linesToPara, substitute) import Text.Pandoc.Templates (renderTemplate') import Text.Pandoc.Walk (query, walk, walkM) -import Text.Pandoc.Writers.HTML (writeHtml5String) +import Text.Pandoc.Writers.HTML (writeHtml5String, tagWithAttributes) import Text.Pandoc.Writers.Shared -- | Convert Pandoc to CommonMark. @@ -140,9 +140,13 @@ blockToNodes opts (OrderedList (start, _sty, delim) items) ns = do blockToNodes _ HorizontalRule ns = return (node THEMATIC_BREAK [] : ns) blockToNodes opts (Header lev _ ils) ns = return (node (HEADING lev) (inlinesToNodes opts ils) : ns) -blockToNodes opts (Div _ bs) ns = do +blockToNodes opts (Div attr bs) ns = do nodes <- blocksToNodes opts bs - return (nodes ++ ns) + let op = tagWithAttributes opts True False "div" attr + if isEnabled Ext_raw_html opts + then return (node (HTML_BLOCK op) [] : nodes ++ + [node (HTML_BLOCK (T.pack "</div>")) []] ++ ns) + else return (nodes ++ ns) blockToNodes opts (DefinitionList items) ns = blockToNodes opts (BulletList items') ns where items' = map dlToBullet items @@ -298,7 +302,13 @@ inlineToNodes opts (Math mt str) = (node (HTML_INLINE (T.pack ("\\(" ++ str ++ "\\)"))) [] :) DisplayMath -> (node (HTML_INLINE (T.pack ("\\[" ++ str ++ "\\]"))) [] :) -inlineToNodes opts (Span _ ils) = (inlinesToNodes opts ils ++) +inlineToNodes opts (Span attr ils) = + let nodes = inlinesToNodes opts ils + op = tagWithAttributes opts True False "span" attr + in if isEnabled Ext_raw_html opts + then ((node (HTML_INLINE op) [] : nodes ++ + [node (HTML_INLINE (T.pack "</span>")) []]) ++) + else (nodes ++) inlineToNodes opts (Cite _ ils) = (inlinesToNodes opts ils ++) inlineToNodes _ (Note _) = id -- should not occur -- we remove Note elements in preprocessing -- cgit v1.2.3 From 45a46bf900bc7153f981088b65bd2cc90d3537d2 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 3 Dec 2017 20:25:04 -0800 Subject: Removed unnecessary import. --- src/Text/Pandoc/Writers/HTML.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 7fdfa567e..756bc3fd8 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -56,7 +56,7 @@ import qualified Data.Text.Lazy as TL import Network.HTTP (urlEncode) import Network.URI (URI (..), parseURIReference, unEscapeString) import Numeric (showHex) -import Text.Blaze.Internal (customLeaf, textTag) +import Text.Blaze.Internal (customLeaf) import Text.Blaze.Html hiding (contents) import Text.Pandoc.Definition import Text.Pandoc.Highlighting (formatHtmlBlock, formatHtmlInline, highlight, -- cgit v1.2.3 From ed261e58320038601ec37a78cb5fe94f2013ca66 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 4 Dec 2017 15:59:26 +0300 Subject: Muse reader: add underline support in Emacs Muse mode --- src/Text/Pandoc/Readers/Muse.hs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index f10a2172f..0688b479c 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -57,7 +57,7 @@ import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Parsing import Text.Pandoc.Readers.HTML (htmlTag) -import Text.Pandoc.Shared (crFilter) +import Text.Pandoc.Shared (crFilter, underlineSpan) -- | Read Muse from an input string and return a Pandoc document. readMuse :: PandocMonad m @@ -577,6 +577,7 @@ inlineList = [ endline , strongTag , emph , emphTag + , underlined , superscriptTag , subscriptTag , strikeoutTag @@ -666,6 +667,11 @@ strong = fmap B.strong <$> emphasisBetween (string "**") emph :: PandocMonad m => MuseParser m (F Inlines) emph = fmap B.emph <$> emphasisBetween (char '*') +underlined :: PandocMonad m => MuseParser m (F Inlines) +underlined = do + guardDisabled Ext_amuse -- Supported only by Emacs Muse + fmap underlineSpan <$> emphasisBetween (char '_') + emphTag :: PandocMonad m => MuseParser m (F Inlines) emphTag = inlineTag B.emph "em" -- cgit v1.2.3 From ae60e0196c5c12d358002cf3251dfebf07c66da6 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 4 Dec 2017 10:31:06 -0800 Subject: Add `empty_paragraphs` extension. * Deprecate `--strip-empty-paragraphs` option. Instead we now use an `empty_paragraphs` extension that can be enabled on the reader or writer. By default, disabled. * Add `Ext_empty_paragraphs` constructor to `Extension`. * Revert "Docx reader: don't strip out empty paragraphs." This reverts commit d6c58eb836f033a48955796de4d9ffb3b30e297b. * Implement `empty_paragraphs` extension in docx reader and writer, opendocument writer, html reader and writer. * Add tests for `empty_paragraphs` extension. --- src/Text/Pandoc/App.hs | 19 +++++++++++-------- src/Text/Pandoc/Extensions.hs | 1 + src/Text/Pandoc/Readers/Docx.hs | 5 ++++- src/Text/Pandoc/Readers/HTML.hs | 13 +++++++++---- src/Text/Pandoc/Writers/Docx.hs | 29 ++++++++++++++++------------- src/Text/Pandoc/Writers/HTML.hs | 14 +++++++++----- src/Text/Pandoc/Writers/OpenDocument.hs | 4 ++-- 7 files changed, 52 insertions(+), 33 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 3fdbf1949..7d7d630ea 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -947,7 +947,10 @@ options = , Option "" ["strip-empty-paragraphs"] (NoArg - (\opt -> return opt{ optStripEmptyParagraphs = True })) + (\opt -> do + deprecatedOption "--stripEmptyParagraphs" + "Use +empty_paragraphs extension." + return opt{ optStripEmptyParagraphs = True })) "" -- "Strip empty paragraphs" , Option "" ["indented-code-classes"] @@ -1472,7 +1475,7 @@ options = , Option "m" ["latexmathml", "asciimathml"] (OptArg (\arg opt -> do - deprecatedOption "--latexmathml, --asciimathml, -m" + deprecatedOption "--latexmathml, --asciimathml, -m" "" return opt { optHTMLMathMethod = LaTeXMathML arg }) "URL") "" -- "Use LaTeXMathML script in html output" @@ -1480,7 +1483,7 @@ options = , Option "" ["mimetex"] (OptArg (\arg opt -> do - deprecatedOption "--mimetex" + deprecatedOption "--mimetex" "" let url' = case arg of Just u -> u ++ "?" Nothing -> "/cgi-bin/mimetex.cgi?" @@ -1491,7 +1494,7 @@ options = , Option "" ["jsmath"] (OptArg (\arg opt -> do - deprecatedOption "--jsmath" + deprecatedOption "--jsmath" "" return opt { optHTMLMathMethod = JsMath arg}) "URL") "" -- "Use jsMath for HTML math" @@ -1499,7 +1502,7 @@ options = , Option "" ["gladtex"] (NoArg (\opt -> do - deprecatedOption "--gladtex" + deprecatedOption "--gladtex" "" return opt { optHTMLMathMethod = GladTeX })) "" -- "Use gladtex for HTML math" @@ -1699,9 +1702,9 @@ splitField s = baseWriterName :: String -> String baseWriterName = takeWhile (\c -> c /= '+' && c /= '-') -deprecatedOption :: String -> IO () -deprecatedOption o = - runIO (report $ Deprecated o "") >>= +deprecatedOption :: String -> String -> IO () +deprecatedOption o msg = + runIO (report $ Deprecated o msg) >>= \r -> case r of Right () -> return () Left e -> E.throwIO e diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 67ad2ad04..771898d70 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -152,6 +152,7 @@ data Extension = | Ext_old_dashes -- ^ -- = em, - before number = en | Ext_spaced_reference_links -- ^ Allow space between two parts of ref link | Ext_amuse -- ^ Enable Text::Amuse extensions to Emacs Muse markup + | Ext_empty_paragraphs -- ^ Allow empty paragraphs deriving (Show, Read, Enum, Eq, Ord, Bounded, Data, Typeable, Generic) -- | Extensions to be used with pandoc-flavored markdown. diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index 1fac98b14..651d46753 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -534,7 +534,10 @@ bodyPartToBlocks (Paragraph pPr parparts) then do modify $ \s -> s { docxDropCap = ils' } return mempty else do modify $ \s -> s { docxDropCap = mempty } - return $ parStyleToTransform pPr $ para ils' + opts <- asks docxOptions + if isNull ils' && not (isEnabled Ext_empty_paragraphs opts) + then return mempty + else return $ parStyleToTransform pPr $ para ils' bodyPartToBlocks (ListItem pPr numId lvl (Just levelInfo) parparts) = do let kvs = case levelInfo of diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 24935fcd7..b0f5d38f9 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -68,9 +68,11 @@ import Text.Pandoc.CSS (foldOrElse, pickStyleAttrProps) import Text.Pandoc.Definition import Text.Pandoc.Error import Text.Pandoc.Logging -import Text.Pandoc.Options (Extension (Ext_epub_html_exts, Ext_native_divs, Ext_native_spans, Ext_raw_html), - ReaderOptions (readerExtensions, readerStripComments), - extensionEnabled) +import Text.Pandoc.Options ( + Extension (Ext_epub_html_exts, Ext_empty_paragraphs, Ext_native_divs, + Ext_native_spans, Ext_raw_html), + ReaderOptions (readerExtensions, readerStripComments), + extensionEnabled) import Text.Pandoc.Parsing hiding ((<|>)) import Text.Pandoc.Shared (addMetaField, crFilter, escapeURI, extractSpaces, safeRead, underlineSpan) @@ -575,7 +577,10 @@ pPlain = do pPara :: PandocMonad m => TagParser m Blocks pPara = do contents <- trimInlines <$> pInTags "p" inline - return $ B.para contents + (do guardDisabled Ext_empty_paragraphs + guard (B.isNull contents) + return mempty) + <|> return (B.para contents) pFigure :: PandocMonad m => TagParser m Blocks pFigure = try $ do diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index f80c2b59a..c9eaaf838 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -922,19 +922,22 @@ blockToOpenXML' opts (Para [Image attr alt (src,'f':'i':'g':':':tit)]) = do captionNode <- withParaProp (pCustomStyle "ImageCaption") $ blockToOpenXML opts (Para alt) return $ mknode "w:p" [] (paraProps ++ contents) : captionNode -blockToOpenXML' opts (Para lst) = do - isFirstPara <- gets stFirstPara - paraProps <- getParaProps $ case lst of - [Math DisplayMath _] -> True - _ -> False - bodyTextStyle <- pStyleM "Body Text" - let paraProps' = case paraProps of - [] | isFirstPara -> [mknode "w:pPr" [] [pCustomStyle "FirstParagraph"]] - [] -> [mknode "w:pPr" [] [bodyTextStyle]] - ps -> ps - modify $ \s -> s { stFirstPara = False } - contents <- inlinesToOpenXML opts lst - return [mknode "w:p" [] (paraProps' ++ contents)] +blockToOpenXML' opts (Para lst) + | null lst && not (isEnabled Ext_empty_paragraphs opts) = return [] + | otherwise = do + isFirstPara <- gets stFirstPara + paraProps <- getParaProps $ case lst of + [Math DisplayMath _] -> True + _ -> False + bodyTextStyle <- pStyleM "Body Text" + let paraProps' = case paraProps of + [] | isFirstPara -> [mknode "w:pPr" [] + [pCustomStyle "FirstParagraph"]] + [] -> [mknode "w:pPr" [] [bodyTextStyle]] + ps -> ps + modify $ \s -> s { stFirstPara = False } + contents <- inlinesToOpenXML opts lst + return [mknode "w:p" [] (paraProps' ++ contents)] blockToOpenXML' opts (LineBlock lns) = blockToOpenXML opts $ linesToPara lns blockToOpenXML' _ b@(RawBlock format str) | format == Format "openxml" = return [ x | Elem x <- parseXML str ] diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 756bc3fd8..f25bbadfb 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -56,7 +56,7 @@ import qualified Data.Text.Lazy as TL import Network.HTTP (urlEncode) import Network.URI (URI (..), parseURIReference, unEscapeString) import Numeric (showHex) -import Text.Blaze.Internal (customLeaf) +import Text.Blaze.Internal (customLeaf, MarkupM(Empty)) import Text.Blaze.Html hiding (contents) import Text.Pandoc.Definition import Text.Pandoc.Highlighting (formatHtmlBlock, formatHtmlInline, highlight, @@ -658,6 +658,7 @@ blockToHtml opts (Para [Image attr txt (s,'f':'i':'g':':':tit)]) = figure opts attr txt (s,tit) blockToHtml opts (Para lst) | isEmptyRaw lst = return mempty + | null lst && not (isEnabled Ext_empty_paragraphs opts) = return mempty | otherwise = do contents <- inlineListToHtml opts lst return $ H.p contents @@ -902,8 +903,7 @@ tableItemToHtml opts tag' align' item = do let tag'' = if null alignStr then tag' else tag' ! attribs - return $ ( - tag'' contents) >> nl opts + return $ tag'' contents >> nl opts toListItems :: WriterOptions -> [Html] -> [Html] toListItems opts items = map (toListItem opts) items ++ [nl opts] @@ -911,9 +911,13 @@ toListItems opts items = map (toListItem opts) items ++ [nl opts] toListItem :: WriterOptions -> Html -> Html toListItem opts item = nl opts >> H.li item -blockListToHtml :: PandocMonad m => WriterOptions -> [Block] -> StateT WriterState m Html +blockListToHtml :: PandocMonad m + => WriterOptions -> [Block] -> StateT WriterState m Html blockListToHtml opts lst = - (mconcat . intersperse (nl opts)) <$> mapM (blockToHtml opts) lst + (mconcat . intersperse (nl opts) . filter nonempty) + <$> mapM (blockToHtml opts) lst + where nonempty (Empty _) = False + nonempty _ = True -- | Convert list of Pandoc inline elements to HTML. inlineListToHtml :: PandocMonad m => WriterOptions -> [Inline] -> StateT WriterState m Html diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index 702349636..8aa19dbb5 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -130,7 +130,6 @@ setFirstPara :: PandocMonad m => OD m () setFirstPara = modify $ \s -> s { stFirstPara = True } inParagraphTags :: PandocMonad m => Doc -> OD m Doc -inParagraphTags d | isEmpty d = return empty inParagraphTags d = do b <- gets stFirstPara a <- if b @@ -323,7 +322,8 @@ 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 + | Para b <- bs = if null b && + not (isEnabled Ext_empty_paragraphs o) then return empty else inParagraphTags =<< inlinesToOpenDocument o b | LineBlock b <- bs = blockToOpenDocument o $ linesToPara b -- cgit v1.2.3 From fac3953abf26d5b55fac9bdd6c74c0074660ab7a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 4 Dec 2017 15:04:47 -0800 Subject: Markdown reader: Don't parse native div as table caption. Closes #4119. --- src/Text/Pandoc/Readers/Markdown.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index a94c85c32..2d7c12e99 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -1267,7 +1267,7 @@ tableCaption :: PandocMonad m => MarkdownParser m (F Inlines) tableCaption = try $ do guardEnabled Ext_table_captions skipNonindentSpaces - string ":" <|> string "Table:" + (string ":" <* notFollowedBy (string "::")) <|> string "Table:" trimInlinesF <$> inlines1 <* blanklines -- Parse a simple table with '---' header and one line per row. -- cgit v1.2.3 From 12789fd42a15126751018f7a392d08a05c68c210 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Tue, 5 Dec 2017 12:59:28 +0300 Subject: Muse reader: support multiline directives in Amusewiki mode --- src/Text/Pandoc/Readers/Muse.hs | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 0688b479c..39aa67f82 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -140,19 +140,39 @@ commonPrefix (x:xs) (y:ys) -- directive parsers -- -parseDirective :: PandocMonad m => MuseParser m (String, F Inlines) -parseDirective = do +-- While not documented, Emacs Muse allows "-" in directive name +parseDirectiveKey :: PandocMonad m => MuseParser m (String) +parseDirectiveKey = do char '#' - key <- many letter + many (letter <|> char '-') + +parseEmacsDirective :: PandocMonad m => MuseParser m (String, F Inlines) +parseEmacsDirective = do + key <- parseDirectiveKey space spaces raw <- manyTill anyChar eol value <- parseFromString (trimInlinesF . mconcat <$> many inline) raw return (key, value) +parseAmuseDirective :: PandocMonad m => MuseParser m (String, F Inlines) +parseAmuseDirective = do + key <- parseDirectiveKey + space + spaces + first <- manyTill anyChar eol + rest <- manyTill anyLine endOfDirective + many blankline + value <- parseFromString (trimInlinesF . mconcat <$> many inline) $ unlines (first : rest) + return (key, value) + where + endOfDirective = lookAhead $ endOfInput <|> (try $ void blankline) <|> (try $ void parseDirectiveKey) + endOfInput = try $ skipMany blankline >> skipSpaces >> eof + directive :: PandocMonad m => MuseParser m () directive = do - (key, value) <- parseDirective + ext <- getOption readerExtensions + (key, value) <- if extensionEnabled Ext_amuse ext then parseAmuseDirective else parseEmacsDirective updateState $ \st -> st { stateMeta' = B.setMeta key <$> value <*> stateMeta' st } -- -- cgit v1.2.3 From 3ae359721d174205582cd5bd3b13525493e10619 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Wed, 6 Dec 2017 19:04:35 +0300 Subject: Muse reader: don't allow emphasis to be preceded by letter --- src/Text/Pandoc/Readers/Muse.hs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 39aa67f82..72344bfe0 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -136,6 +136,13 @@ commonPrefix (x:xs) (y:ys) | x == y = x : commonPrefix xs ys | otherwise = [] +atStart :: PandocMonad m => MuseParser m a -> MuseParser m a +atStart p = do + pos <- getPosition + st <- getState + guard $ stateLastStrPos st /= Just pos + p + -- -- directive parsers -- @@ -668,7 +675,7 @@ enclosedInlines :: (PandocMonad m, Show a, Show b) -> MuseParser m b -> MuseParser m (F Inlines) enclosedInlines start end = try $ - trimInlinesF . mconcat <$> (enclosed start end inline <* notFollowedBy (satisfy isLetter)) + trimInlinesF . mconcat <$> (enclosed (atStart start) end inline <* notFollowedBy (satisfy isLetter)) inlineTag :: PandocMonad m => (Inlines -> Inlines) @@ -745,7 +752,10 @@ inlineLiteralTag = do rawInline (attrs, content) = B.rawInline (format attrs) content str :: PandocMonad m => MuseParser m (F Inlines) -str = return . B.str <$> many1 alphaNum +str = do + result <- many1 alphaNum + updateLastStrPos + return $ return $ B.str result symbol :: PandocMonad m => MuseParser m (F Inlines) symbol = return . B.str <$> count 1 nonspaceChar -- cgit v1.2.3 From e536c4d9c9c4ff73213dc0273654ea3e1916f22e Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Wed, 6 Dec 2017 19:38:25 +0300 Subject: hlint Muse reader and tests --- src/Text/Pandoc/Readers/Muse.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 72344bfe0..04cec149b 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -148,7 +148,7 @@ atStart p = do -- -- While not documented, Emacs Muse allows "-" in directive name -parseDirectiveKey :: PandocMonad m => MuseParser m (String) +parseDirectiveKey :: PandocMonad m => MuseParser m String parseDirectiveKey = do char '#' many (letter <|> char '-') @@ -173,7 +173,7 @@ parseAmuseDirective = do value <- parseFromString (trimInlinesF . mconcat <$> many inline) $ unlines (first : rest) return (key, value) where - endOfDirective = lookAhead $ endOfInput <|> (try $ void blankline) <|> (try $ void parseDirectiveKey) + endOfDirective = lookAhead $ endOfInput <|> try (void blankline) <|> try (void parseDirectiveKey) endOfInput = try $ skipMany blankline >> skipSpaces >> eof directive :: PandocMonad m => MuseParser m () @@ -428,7 +428,7 @@ listStart marker = try $ do dropSpacePrefix :: [String] -> [String] dropSpacePrefix lns = map (drop maxIndent) lns - where flns = filter (\s -> not $ all (== ' ') s) lns + where flns = filter (not . all (== ' ')) lns maxIndent = if null flns then 0 else length $ takeWhile (== ' ') $ foldl1 commonPrefix flns listItemContents :: PandocMonad m => Int -> MuseParser m (F Blocks) @@ -620,7 +620,7 @@ inlineList = [ endline ] inline :: PandocMonad m => MuseParser m (F Inlines) -inline = (choice inlineList) <?> "inline" +inline = choice inlineList <?> "inline" endline :: PandocMonad m => MuseParser m (F Inlines) endline = try $ do -- cgit v1.2.3 From d5b1c7b767a24bda592ea35902b8e1dc971d6d80 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sat, 2 Dec 2017 23:07:29 +0100 Subject: Lua filters: refactor lua module handling The integration with Lua's package/module system is improved: A pandoc-specific package searcher is prepended to the searchers in `package.searchers`. The modules `pandoc` and `pandoc.mediabag` can now be loaded via `require`. --- src/Text/Pandoc/Lua.hs | 65 +++++++++++++-------- src/Text/Pandoc/Lua/Packages.hs | 109 ++++++++++++++++++++++++++++++++++++ src/Text/Pandoc/Lua/PandocModule.hs | 39 +++---------- src/Text/Pandoc/Lua/Util.hs | 33 ++++++++++- 4 files changed, 190 insertions(+), 56 deletions(-) create mode 100644 src/Text/Pandoc/Lua/Packages.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 148e7a23d..1ca67dced 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -31,45 +31,46 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Pandoc lua utils. -} -module Text.Pandoc.Lua (LuaException (..), pushPandocModule, runLuaFilter) where +module Text.Pandoc.Lua + ( LuaException (..) + , LuaPackageParams (..) + , pushPandocModule + , runLuaFilter + , initLuaState + , luaPackageParams + ) where import Control.Monad (when, (>=>)) import Control.Monad.Identity (Identity) import Control.Monad.Trans (MonadIO (..)) -import Data.IORef (IORef, newIORef, readIORef) +import Data.IORef (newIORef, readIORef) import Foreign.Lua (FromLuaStack (peek), Lua, LuaException (..), Status (OK), ToLuaStack (push)) -import Text.Pandoc.Class (CommonState, PandocIO, getCommonState, getMediaBag, - setMediaBag) +import Text.Pandoc.Class (PandocIO, getCommonState, getMediaBag, setMediaBag) import Text.Pandoc.Definition +import Text.Pandoc.Lua.Packages (LuaPackageParams (..), + installPandocPackageSearcher) import Text.Pandoc.Lua.PandocModule (pushMediaBagModule, pushPandocModule) import Text.Pandoc.Lua.Filter (LuaFilter, walkMWithLuaFilter) -import Text.Pandoc.MediaBag (MediaBag) import qualified Foreign.Lua as Lua import qualified Foreign.Lua.Module.Text as Lua runLuaFilter :: Maybe FilePath -> FilePath -> String -> Pandoc -> PandocIO (Either LuaException Pandoc) runLuaFilter datadir filterPath format pd = do - commonState <- getCommonState - mediaBag <- getMediaBag - mediaBagRef <- liftIO (newIORef mediaBag) + luaPkgParams <- luaPackageParams datadir res <- liftIO . Lua.runLuaEither $ - runLuaFilter' commonState datadir filterPath format mediaBagRef pd - newMediaBag <- liftIO (readIORef mediaBagRef) + runLuaFilter' luaPkgParams filterPath format pd + newMediaBag <- liftIO (readIORef (luaPkgMediaBag luaPkgParams)) setMediaBag newMediaBag return res -runLuaFilter' :: CommonState - -> Maybe FilePath -> FilePath -> String -> IORef MediaBag +runLuaFilter' :: LuaPackageParams + -> FilePath -> String -> Pandoc -> Lua Pandoc -runLuaFilter' commonState datadir filterPath format mbRef pd = do - Lua.openlibs - Lua.preloadTextModule "text" +runLuaFilter' luaPkgOpts filterPath format pd = do + initLuaState luaPkgOpts -- store module in global "pandoc" - pushPandocModule datadir - Lua.setglobal "pandoc" - addMediaBagModule registerFormat top <- Lua.gettop stat <- Lua.dofile filterPath @@ -84,15 +85,33 @@ runLuaFilter' commonState datadir filterPath format mbRef pd = do luaFilters <- peek (-1) runAll luaFilters pd where - addMediaBagModule = do - Lua.getglobal "pandoc" - push "mediabag" - pushMediaBagModule commonState mbRef - Lua.rawset (-3) registerFormat = do push format Lua.setglobal "FORMAT" +luaPackageParams :: Maybe FilePath -> PandocIO LuaPackageParams +luaPackageParams datadir = do + commonState <- getCommonState + mbRef <- liftIO . newIORef =<< getMediaBag + return LuaPackageParams + { luaPkgCommonState = commonState + , luaPkgDataDir = datadir + , luaPkgMediaBag = mbRef + } + +-- Initialize the lua state with all required values +initLuaState :: LuaPackageParams -> Lua () +initLuaState luaPkgParams@(LuaPackageParams commonState datadir mbRef) = do + Lua.openlibs + Lua.preloadTextModule "text" + installPandocPackageSearcher luaPkgParams + pushPandocModule datadir + -- add MediaBag module + push "mediabag" + pushMediaBagModule commonState mbRef + Lua.rawset (-3) + Lua.setglobal "pandoc" + return () pushGlobalFilter :: Lua () pushGlobalFilter = do diff --git a/src/Text/Pandoc/Lua/Packages.hs b/src/Text/Pandoc/Lua/Packages.hs new file mode 100644 index 000000000..ede7beccd --- /dev/null +++ b/src/Text/Pandoc/Lua/Packages.hs @@ -0,0 +1,109 @@ +{- +Copyright © 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> + +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 +-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE FlexibleContexts #-} +{- | + Module : Text.Pandoc.Lua.Packages + Copyright : Copyright © 2017 Albert Krewinkel + License : GNU GPL, version 2 or above + + Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> + Stability : alpha + +Pandoc module for lua. +-} +module Text.Pandoc.Lua.Packages + ( LuaPackageParams (..) + , installPandocPackageSearcher + ) where + +import Control.Monad (forM_) +import Data.ByteString.Char8 (unpack) +import Data.IORef (IORef) +import Foreign.Lua (Lua, NumResults, liftIO) +import Text.Pandoc.Class (CommonState, readDataFile, runIO, setUserDataDir) +import Text.Pandoc.MediaBag (MediaBag) +import Text.Pandoc.Lua.PandocModule (pushPandocModule, pushMediaBagModule) +import Text.Pandoc.Lua.Util (dostring') + +import qualified Foreign.Lua as Lua + +-- | Parameters used to create lua packages/modules. +data LuaPackageParams = LuaPackageParams + { luaPkgCommonState :: CommonState + , luaPkgDataDir :: Maybe FilePath + , luaPkgMediaBag :: IORef MediaBag + } + +-- | Insert pandoc's package loader as the first loader, making it the default. +installPandocPackageSearcher :: LuaPackageParams -> Lua () +installPandocPackageSearcher luaPkgParams = do + Lua.getglobal' "package.searchers" + shiftArray + Lua.pushHaskellFunction (pandocPackageSearcher luaPkgParams) + Lua.wrapHaskellFunction + Lua.rawseti (-2) 1 + Lua.pop 1 -- remove 'package.searchers' from stack + where + shiftArray = forM_ [4, 3, 2, 1] $ \i -> do + Lua.rawgeti (-1) i + Lua.rawseti (-2) (i + 1) + +-- | Load a pandoc module. +pandocPackageSearcher :: LuaPackageParams -> String -> Lua NumResults +pandocPackageSearcher luaPkgParams pkgName = + case pkgName of + "pandoc" -> let datadir = luaPkgDataDir luaPkgParams + in pushWrappedHsFun (pushPandocModule datadir) + "pandoc.mediabag" -> let st = luaPkgCommonState luaPkgParams + mbRef = luaPkgMediaBag luaPkgParams + in pushWrappedHsFun (pushMediaBagModule st mbRef) + _ -> searchPureLuaLoader + where + pushWrappedHsFun f = do + Lua.pushHaskellFunction f + Lua.wrapHaskellFunction + return 1 + searchPureLuaLoader = do + let filename = pkgName ++ ".lua" + modScript <- liftIO (dataDirScript (luaPkgDataDir luaPkgParams) filename) + case modScript of + Just script -> pushWrappedHsFun (loadStringAsPackage pkgName script) + Nothing -> do + Lua.push ("no file '" ++ filename ++ "' in pandoc's datadir") + return 1 + +loadStringAsPackage :: String -> String -> Lua NumResults +loadStringAsPackage pkgName script = do + status <- dostring' script + if status == Lua.OK + then return (1 :: NumResults) + else do + msg <- Lua.peek (-1) <* Lua.pop 1 + Lua.push ("Error while loading ``" ++ pkgName ++ "`.\n" ++ msg) + Lua.lerror + return (2 :: NumResults) + +-- | Get the string representation of the pandoc module +dataDirScript :: Maybe FilePath -> FilePath -> IO (Maybe String) +dataDirScript datadir moduleFile = do + res <- runIO $ setUserDataDir datadir >> readDataFile moduleFile + return $ case res of + Left _ -> Nothing + Right s -> Just (unpack s) + diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index 4df01f019..744edfe82 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -33,21 +33,21 @@ module Text.Pandoc.Lua.PandocModule , pushMediaBagModule ) where -import Control.Monad (unless, zipWithM_) -import Data.ByteString.Char8 (unpack) +import Control.Monad (zipWithM_) import Data.Default (Default (..)) import Data.Digest.Pure.SHA (sha1, showDigest) -import Data.IORef +import Data.IORef (IORef, modifyIORef', readIORef) import Data.Maybe (fromMaybe) import Data.Text (pack) import Foreign.Lua (ToLuaStack, FromLuaStack, Lua, NumResults, liftIO) import Foreign.Lua.FunctionCalling (ToHaskellFunction) import System.Exit (ExitCode (..)) import Text.Pandoc.Class (CommonState (..), fetchItem, putCommonState, - readDataFile, runIO, runIOorExplode, setMediaBag, - setUserDataDir) -import Text.Pandoc.Lua.StackInstances () + runIO, runIOorExplode, setMediaBag) import Text.Pandoc.Definition (Block, Inline) +import Text.Pandoc.Lua.Filter (walkInlines, walkBlocks, LuaFilter) +import Text.Pandoc.Lua.StackInstances () +import Text.Pandoc.Lua.Util (loadScriptFromDataDir) import Text.Pandoc.Walk (Walkable) import Text.Pandoc.MIME (MimeType) import Text.Pandoc.Options (ReaderOptions (readerExtensions)) @@ -57,43 +57,18 @@ import Text.Pandoc.Readers (Reader (..), getReader) import qualified Data.ByteString.Lazy as BL import qualified Foreign.Lua as Lua import qualified Text.Pandoc.MediaBag as MB -import Text.Pandoc.Lua.Filter (walkInlines, walkBlocks, LuaFilter) -- | Push the "pandoc" on the lua stack. Requires the `list` module to be -- loaded. pushPandocModule :: Maybe FilePath -> Lua () pushPandocModule datadir = do - loadListModule datadir - script <- liftIO (moduleScript datadir "pandoc.lua") - status <- Lua.loadstring script - unless (status /= Lua.OK) $ Lua.call 0 1 + loadScriptFromDataDir datadir "pandoc.lua" addFunction "_pipe" pipeFn addFunction "_read" readDoc addFunction "sha1" sha1HashFn addFunction "walk_block" walkBlock addFunction "walk_inline" walkInline --- | Get the string representation of the pandoc module -moduleScript :: Maybe FilePath -> FilePath -> IO String -moduleScript datadir moduleFile = unpack <$> - runIOorExplode (setUserDataDir datadir >> readDataFile moduleFile) - --- Loads pandoc's list module without assigning it to a variable. -pushListModule :: Maybe FilePath -> Lua () -pushListModule datadir = do - script <- liftIO (moduleScript datadir "List.lua") - status <- Lua.loadstring script - if status == Lua.OK - then Lua.call 0 1 - else Lua.throwTopMessageAsError' ("Error while loading module `list`\n" ++) - -loadListModule :: Maybe FilePath -> Lua () -loadListModule datadir = do - Lua.getglobal' "package.loaded" - pushListModule datadir - Lua.setfield (-2) "pandoc.List" - Lua.pop 1 - walkElement :: (ToLuaStack a, Walkable [Inline] a, Walkable [Block] a) => a -> LuaFilter -> Lua NumResults walkElement x f = do diff --git a/src/Text/Pandoc/Lua/Util.hs b/src/Text/Pandoc/Lua/Util.hs index 7960c0670..5803e62dc 100644 --- a/src/Text/Pandoc/Lua/Util.hs +++ b/src/Text/Pandoc/Lua/Util.hs @@ -38,11 +38,18 @@ module Text.Pandoc.Lua.Util , PushViaCall , pushViaCall , pushViaConstructor + , loadScriptFromDataDir + , dostring' ) where +import Control.Monad (when) +import Data.ByteString.Char8 (unpack) import Foreign.Lua (FromLuaStack (..), Lua, NumArgs, StackIndex, ToLuaStack (..), getglobal') -import Foreign.Lua.Api (call, pop, rawget, rawgeti, rawset, rawseti) +import Foreign.Lua.Api (Status, call, pop, rawget, rawgeti, rawset, rawseti) +import Text.Pandoc.Class (readDataFile, runIOorExplode, setUserDataDir) + +import qualified Foreign.Lua as Lua -- | Adjust the stack index, assuming that @n@ new elements have been pushed on -- the stack. @@ -107,3 +114,27 @@ pushViaCall fn = pushViaCall' fn (return ()) 0 -- | Call a pandoc element constructor within lua, passing all given arguments. pushViaConstructor :: PushViaCall a => String -> a pushViaConstructor pandocFn = pushViaCall ("pandoc." ++ pandocFn) + +-- | Load a file from pandoc's data directory. +loadScriptFromDataDir :: Maybe FilePath -> FilePath -> Lua () +loadScriptFromDataDir datadir scriptFile = do + script <- fmap unpack . Lua.liftIO . runIOorExplode $ + setUserDataDir datadir >> readDataFile scriptFile + status <- dostring' script + when (status /= Lua.OK) . + Lua.throwTopMessageAsError' $ \msg -> + "Couldn't load '" ++ scriptFile ++ "'.\n" ++ msg + +-- | Load a string and immediately perform a full garbage collection. This is +-- important to keep the program from hanging: If the program contained a call +-- to @require@, the a new loader function was created which then become +-- garbage. If that function is collected at an inopportune times, i.e. when the +-- Lua API is called via a function that doesn't allow calling back into Haskell +-- (getraw, setraw, …). The function's finalizer, and the full program, hangs +-- when that happens. +dostring' :: String -> Lua Status +dostring' script = do + loadRes <- Lua.loadstring script + if loadRes == Lua.OK + then Lua.pcall 0 1 Nothing <* Lua.gc Lua.GCCOLLECT 0 + else return loadRes -- cgit v1.2.3 From 4066a385ace1cee53336bf4c10734239044a92ae Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Wed, 6 Dec 2017 20:45:38 +0100 Subject: Lua filters: use script to initialize the interpreter The file `init.lua` is used to initialize the Lua interpreter which is used in Lua filters. This gives users the option to require libraries which they want to use in all of their filters, and to extend default modules. --- src/Text/Pandoc/Lua.hs | 15 +++++---------- src/Text/Pandoc/Lua/PandocModule.hs | 7 ++++--- 2 files changed, 9 insertions(+), 13 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 1ca67dced..7132ad718 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -48,10 +48,11 @@ import Foreign.Lua (FromLuaStack (peek), Lua, LuaException (..), Status (OK), ToLuaStack (push)) import Text.Pandoc.Class (PandocIO, getCommonState, getMediaBag, setMediaBag) import Text.Pandoc.Definition +import Text.Pandoc.Lua.Filter (LuaFilter, walkMWithLuaFilter) import Text.Pandoc.Lua.Packages (LuaPackageParams (..), installPandocPackageSearcher) -import Text.Pandoc.Lua.PandocModule (pushMediaBagModule, pushPandocModule) -import Text.Pandoc.Lua.Filter (LuaFilter, walkMWithLuaFilter) +import Text.Pandoc.Lua.PandocModule (pushPandocModule) -- TODO: remove +import Text.Pandoc.Lua.Util (loadScriptFromDataDir) import qualified Foreign.Lua as Lua import qualified Foreign.Lua.Module.Text as Lua @@ -101,17 +102,11 @@ luaPackageParams datadir = do -- Initialize the lua state with all required values initLuaState :: LuaPackageParams -> Lua () -initLuaState luaPkgParams@(LuaPackageParams commonState datadir mbRef) = do +initLuaState luaPkgParams = do Lua.openlibs Lua.preloadTextModule "text" installPandocPackageSearcher luaPkgParams - pushPandocModule datadir - -- add MediaBag module - push "mediabag" - pushMediaBagModule commonState mbRef - Lua.rawset (-3) - Lua.setglobal "pandoc" - return () + loadScriptFromDataDir (luaPkgDataDir luaPkgParams) "init.lua" pushGlobalFilter :: Lua () pushGlobalFilter = do diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index 744edfe82..75f884c46 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -60,7 +60,7 @@ import qualified Text.Pandoc.MediaBag as MB -- | Push the "pandoc" on the lua stack. Requires the `list` module to be -- loaded. -pushPandocModule :: Maybe FilePath -> Lua () +pushPandocModule :: Maybe FilePath -> Lua NumResults pushPandocModule datadir = do loadScriptFromDataDir datadir "pandoc.lua" addFunction "_pipe" pipeFn @@ -68,6 +68,7 @@ pushPandocModule datadir = do addFunction "sha1" sha1HashFn addFunction "walk_block" walkBlock addFunction "walk_inline" walkInline + return 1 walkElement :: (ToLuaStack a, Walkable [Inline] a, Walkable [Block] a) => a -> LuaFilter -> Lua NumResults @@ -99,14 +100,14 @@ readDoc formatSpec content = do -- -- MediaBag submodule -- -pushMediaBagModule :: CommonState -> IORef MB.MediaBag -> Lua () +pushMediaBagModule :: CommonState -> IORef MB.MediaBag -> Lua NumResults pushMediaBagModule commonState mediaBagRef = do Lua.newtable addFunction "insert" (insertMediaFn mediaBagRef) addFunction "lookup" (lookupMediaFn mediaBagRef) addFunction "list" (mediaDirectoryFn mediaBagRef) addFunction "fetch" (fetch commonState mediaBagRef) - return () + return 1 addFunction :: ToHaskellFunction a => String -> a -> Lua () addFunction name fn = do -- cgit v1.2.3 From f6007e7146460643a8fe4a4d3434001c6ef136bb Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 6 Dec 2017 16:05:27 -0800 Subject: Markdown reader: accept processing instructions as raw HTML. Closes #4125. --- src/Text/Pandoc/Readers/HTML.hs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index b0f5d38f9..f5f296712 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -1155,8 +1155,9 @@ htmlTag f = try $ do -- in XML element names let isNameChar c = isAlphaNum c || c == ':' || c == '-' || c == '_' let isName s = case s of - [] -> False - (c:cs) -> isLetter c && all isNameChar cs + [] -> False + ('?':_) -> True -- processing instruction + (c:cs) -> isLetter c && all isNameChar cs let endpos = if ln == 1 then setSourceColumn startpos -- cgit v1.2.3 From 67b6abc8065a290517ff1486d09b4e57fce19733 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 8 Dec 2017 16:33:29 -0800 Subject: LaTeX reader: fix \ before newline. This should be a nonbreaking space, as long as it's not followed by a blank line. This has been fixed at the tokenizer level. Closes #4134. --- src/Text/Pandoc/Readers/LaTeX.hs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index d1d9682c3..90d0fe5d1 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -332,9 +332,20 @@ totoks pos t = in Tok pos (CtrlSeq ws) ("\\" <> ws <> ss) : totoks (incSourceColumn pos (1 + T.length ws + T.length ss)) rest''' - | d == '\t' || d == '\n' -> - Tok pos Symbol "\\" - : totoks (incSourceColumn pos 1) rest + | isSpaceOrTab d || d == '\n' -> + let (w1, r1) = T.span isSpaceOrTab rest + (w2, (w3, r3)) = case T.uncons r1 of + Just ('\n', r2) + -> (T.pack "\n", + T.span isSpaceOrTab r2) + _ -> (mempty, (w1, r1)) + in case T.uncons r3 of + Just ('\n', _) -> + Tok pos (CtrlSeq " ") ("\\" <> w1) + : totoks (incSourceColumn pos 1) r1 + _ -> + Tok pos (CtrlSeq " ") ("\\" <> w1 <> w2 <> w3) + : totoks (incSourceColumn pos 1) r3 | otherwise -> Tok pos (CtrlSeq (T.singleton d)) (T.pack [c,d]) : totoks (incSourceColumn pos 2) rest' -- cgit v1.2.3 From 544494d0e234e899a45f02b185cf5a448787f786 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 9 Dec 2017 14:09:00 -0800 Subject: Man writer: omit internal links. That is, just print the link text without the url. Closes #4136. --- src/Text/Pandoc/Writers/Man.hs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Man.hs b/src/Text/Pandoc/Writers/Man.hs index ad3de41eb..34b5c0ece 100644 --- a/src/Text/Pandoc/Writers/Man.hs +++ b/src/Text/Pandoc/Writers/Man.hs @@ -373,6 +373,8 @@ inlineToMan _ LineBreak = return $ cr <> text ".PD 0" $$ text ".P" $$ text ".PD" <> cr inlineToMan _ SoftBreak = return space inlineToMan _ Space = return space +inlineToMan opts (Link _ txt ('#':_, _)) = + inlineListToMan opts txt -- skip internal links inlineToMan opts (Link _ txt (src, _)) = do linktext <- inlineListToMan opts txt let srcSuffix = fromMaybe src (stripPrefix "mailto:" src) -- cgit v1.2.3 From 1cd785fe33231a15423dea7d26cb9a7d770a7ace Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sun, 10 Dec 2017 21:43:57 +0100 Subject: Lua filters: fix package loading for Lua 5.1 The list of package searchers is named `package.loaders` in Lua 5.1 and LuaJIT, and `package.searchers` in Lua 5.2 and later. --- src/Text/Pandoc/Lua/Packages.hs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/Packages.hs b/src/Text/Pandoc/Lua/Packages.hs index ede7beccd..b2dbff496 100644 --- a/src/Text/Pandoc/Lua/Packages.hs +++ b/src/Text/Pandoc/Lua/Packages.hs @@ -53,7 +53,10 @@ data LuaPackageParams = LuaPackageParams -- | Insert pandoc's package loader as the first loader, making it the default. installPandocPackageSearcher :: LuaPackageParams -> Lua () installPandocPackageSearcher luaPkgParams = do - Lua.getglobal' "package.searchers" + luaVersion <- Lua.getglobal "_VERSION" *> Lua.peek (-1) + if luaVersion == "Lua 5.1" + then Lua.getglobal' "package.loaders" + else Lua.getglobal' "package.searchers" shiftArray Lua.pushHaskellFunction (pandocPackageSearcher luaPkgParams) Lua.wrapHaskellFunction -- cgit v1.2.3 From 5e039d913f0f8db2e7fc8ea37dee30b174c4594f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 8 Dec 2017 12:16:32 -0800 Subject: Changes for skylighting-0.5. This fixes a bug in 2.0.4, whereby pandoc could not read the theme files generated with `--print-highlight-style`. It also fixes some CSS issues involving line numbers. Highlighted code blocks are now enclosed in a div with class sourceCode. Highlighting CSS no longer sets a generic color for pre and code; we only set these for class `sourceCode`. This will close #4133 and #4128. --- src/Text/Pandoc/App.hs | 3 ++- src/Text/Pandoc/Writers/Docx.hs | 6 +++--- src/Text/Pandoc/Writers/Ms.hs | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 7d7d630ea..ae62db4f9 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -1074,7 +1074,8 @@ options = (map T.pack ["text-color" ,"background-color" - ,"line-numbers" + ,"line-number-color" + ,"line-number-background-color" ,"bold" ,"italic" ,"underline" diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index c9eaaf838..d76990284 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -625,12 +625,12 @@ styleToOpenXml sm style = [ mknode "w:u" [] () | tokFeature tokenUnderline toktype ] ] tokStyles = tokenStyles style - tokFeature f toktype = maybe False f $ lookup toktype tokStyles + tokFeature f toktype = maybe False f $ M.lookup toktype tokStyles tokCol toktype = maybe "auto" (drop 1 . fromColor) - $ (tokenColor =<< lookup toktype tokStyles) + $ (tokenColor =<< M.lookup toktype tokStyles) `mplus` defaultColor style tokBg toktype = maybe "auto" (drop 1 . fromColor) - $ (tokenBackground =<< lookup toktype tokStyles) + $ (tokenBackground =<< M.lookup toktype tokStyles) `mplus` backgroundColor style parStyle | hasStyleName "Source Code" (sParaStyleMap sm) = Nothing | otherwise = Just $ diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 223d1bcc1..30633cec6 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -586,7 +586,7 @@ styleToMs sty = vcat $ colordefs ++ map (toMacro sty) alltoktypes allcolors = catMaybes $ ordNub $ [defaultColor sty, backgroundColor sty, lineNumberColor sty, lineNumberBackgroundColor sty] ++ - concatMap (colorsForToken. snd) (tokenStyles sty) + concatMap (colorsForToken. snd) (Map.toList (tokenStyles sty)) colorsForToken ts = [tokenColor ts, tokenBackground ts] hexColor :: Color -> String @@ -611,7 +611,7 @@ toMacro sty toktype = resetfont = if tokBold || tokItalic then text "\\\\f[C]" else empty - tokSty = lookup toktype (tokenStyles sty) + tokSty = Map.lookup toktype (tokenStyles sty) tokCol = (tokSty >>= tokenColor) `mplus` defaultColor sty -- tokBg = (tokSty >>= tokenBackground) `mplus` backgroundColor sty tokBold = fromMaybe False (tokenBold <$> tokSty) -- cgit v1.2.3 From 6cc673dbab15bc1aeb96564b7e23b8067a9ae924 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sun, 10 Dec 2017 10:56:17 -0500 Subject: Create shared OOXML writer file. This is for functions used by both Powerpoint and Docx writers. --- src/Text/Pandoc/Writers/Docx.hs | 34 +----------- src/Text/Pandoc/Writers/OOXML.hs | 109 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 33 deletions(-) create mode 100644 src/Text/Pandoc/Writers/OOXML.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index d76990284..538efa3a6 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -73,6 +73,7 @@ import Text.Printf (printf) import Text.TeXMath import Text.XML.Light as XML import Text.XML.Light.Cursor as XMLC +import Text.Pandoc.Writers.OOXML data ListMarker = NoMarker | BulletMarker @@ -156,22 +157,6 @@ defaultWriterState = WriterState{ type WS m = ReaderT WriterEnv (StateT WriterState m) -mknode :: Node t => String -> [(String,String)] -> t -> Element -mknode s attrs = - add_attrs (map (\(k,v) -> Attr (nodename k) v) attrs) . node (nodename s) - -nodename :: String -> QName -nodename s = QName{ qName = name, qURI = Nothing, qPrefix = prefix } - where (name, prefix) = case break (==':') s of - (xs,[]) -> (xs, Nothing) - (ys, _:zs) -> (zs, Just ys) - -toLazy :: B.ByteString -> BL.ByteString -toLazy = BL.fromChunks . (:[]) - -renderXml :: Element -> BL.ByteString -renderXml elt = BL8.pack "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" <> - UTF8.fromStringLazy (showElement elt) renumIdMap :: Int -> [Element] -> M.Map String String renumIdMap _ [] = M.empty @@ -1393,23 +1378,6 @@ defaultFootnotes = [ mknode "w:footnote" [ mknode "w:r" [] $ [ mknode "w:continuationSeparator" [] ()]]]] -parseXml :: (PandocMonad m) => Archive -> Archive -> String -> m 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" - Just d -> return d - --- | Scales the image to fit the page --- sizes are passed in emu -fitToPage :: (Double, Double) -> Integer -> (Integer, Integer) -fitToPage (x, y) pageWidth - -- Fixes width to the page width and scales the height - | x > fromIntegral pageWidth = - (pageWidth, floor $ (fromIntegral pageWidth / x) * y) - | otherwise = (floor x, floor y) withDirection :: PandocMonad m => WS m a -> WS m a withDirection x = do diff --git a/src/Text/Pandoc/Writers/OOXML.hs b/src/Text/Pandoc/Writers/OOXML.hs new file mode 100644 index 000000000..f48d27bd6 --- /dev/null +++ b/src/Text/Pandoc/Writers/OOXML.hs @@ -0,0 +1,109 @@ +module Text.Pandoc.Writers.OOXML ( mknode + , nodename + , toLazy + , renderXml + , parseXml + , elemToNameSpaces + , elemName + , isElem + , NameSpaces + , fitToPage + ) where +import Codec.Archive.Zip +--import Control.Applicative ((<|>)) +-- import Control.Monad.Except (catchError) +import Control.Monad.Reader +-- import Control.Monad.State +import qualified Data.ByteString as B +import qualified Data.ByteString.Lazy as BL +import qualified Data.ByteString.Lazy.Char8 as BL8 +-- import Data.Char (isSpace, ord, toLower) +-- import Data.List (intercalate, isPrefixOf, isSuffixOf) +-- import qualified Data.Map as M +import Data.Maybe (mapMaybe) +import Data.Monoid ((<>)) +-- import qualified Data.Set as Set +-- import qualified Data.Text as T +-- import Data.Time.Clock.POSIX +-- import Skylighting +-- import System.Random (randomR) +import Text.Pandoc.Class (PandocMonad) +-- import qualified Text.Pandoc.Class as P +-- import Text.Pandoc.Compat.Time +-- import Text.Pandoc.Definition +-- import Text.Pandoc.Generic +-- import Text.Pandoc.Highlighting (highlight) +-- import Text.Pandoc.ImageSize +-- import Text.Pandoc.Logging +-- import Text.Pandoc.MIME (MimeType, extensionFromMimeType, getMimeType, +-- getMimeTypeDef) +-- import Text.Pandoc.Options +-- import Text.Pandoc.Readers.Docx.StyleMap +-- import Text.Pandoc.Shared hiding (Element) +import qualified Text.Pandoc.UTF8 as UTF8 +-- import Text.Pandoc.Walk +-- import Text.Pandoc.Writers.Math +-- import Text.Pandoc.Writers.Shared (fixDisplayMath) +-- import Text.Printf (printf) +-- import Text.TeXMath +import Text.XML.Light as XML +-- import Text.XML.Light.Cursor as XMLC + + +mknode :: Node t => String -> [(String,String)] -> t -> Element +mknode s attrs = + add_attrs (map (\(k,v) -> Attr (nodename k) v) attrs) . node (nodename s) + +nodename :: String -> QName +nodename s = QName{ qName = name, qURI = Nothing, qPrefix = prefix } + where (name, prefix) = case break (==':') s of + (xs,[]) -> (xs, Nothing) + (ys, _:zs) -> (zs, Just ys) + +toLazy :: B.ByteString -> BL.ByteString +toLazy = BL.fromChunks . (:[]) + +renderXml :: Element -> BL.ByteString +renderXml elt = BL8.pack "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" <> + UTF8.fromStringLazy (showElement elt) + +parseXml :: (PandocMonad m) => Archive -> Archive -> String -> m Element +parseXml refArchive distArchive relpath = + case findEntryByPath relpath refArchive `mplus` + findEntryByPath relpath distArchive of + Nothing -> fail $ relpath ++ " missing in reference file" + Just e -> case parseXMLDoc . UTF8.toStringLazy . fromEntry $ e of + Nothing -> fail $ relpath ++ " corrupt in reference file" + Just d -> return d + +-- Copied from Util + +attrToNSPair :: XML.Attr -> Maybe (String, String) +attrToNSPair (XML.Attr (QName s _ (Just "xmlns")) val) = Just (s, val) +attrToNSPair _ = Nothing + + +elemToNameSpaces :: Element -> NameSpaces +elemToNameSpaces = mapMaybe attrToNSPair . elAttribs + +elemName :: NameSpaces -> String -> String -> QName +elemName ns prefix name = + QName name (lookup prefix ns) (if null prefix then Nothing else Just prefix) + +isElem :: NameSpaces -> String -> String -> Element -> Bool +isElem ns prefix name element = + let ns' = ns ++ elemToNameSpaces element + in qName (elName element) == name && + qURI (elName element) == lookup prefix ns' + +type NameSpaces = [(String, String)] + +-- | Scales the image to fit the page +-- sizes are passed in emu +fitToPage :: (Double, Double) -> Integer -> (Integer, Integer) +fitToPage (x, y) pageWidth + -- Fixes width to the page width and scales the height + | x > fromIntegral pageWidth = + (pageWidth, floor $ ((fromIntegral pageWidth) / x) * y) + | otherwise = (floor x, floor y) + -- cgit v1.2.3 From 8cd0ebe303f096f63097742f5d38bbc7fd10fd3a Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sun, 10 Dec 2017 10:58:50 -0500 Subject: Add necessary powerpoint functions to Class. --- src/Text/Pandoc/Class.hs | 64 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 19897e53f..f48b19c12 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -674,6 +674,66 @@ getDefaultReferenceODT = do Nothing -> foldr addEntryToArchive emptyArchive <$> mapM pathToEntry paths +getDefaultReferencePptx :: PandocMonad m => m Archive +getDefaultReferencePptx = do + -- We're going to narrow this down substantially once we get it + -- working. + let paths = [ "[Content_Types].xml" + , "_rels/.rels" + , "docProps/app.xml" + , "docProps/core.xml" + , "ppt/_rels/presentation.xml.rels" + , "ppt/presProps.xml" + , "ppt/presentation.xml" + , "ppt/slideLayouts/_rels/slideLayout1.xml.rels" + , "ppt/slideLayouts/_rels/slideLayout10.xml.rels" + , "ppt/slideLayouts/_rels/slideLayout11.xml.rels" + , "ppt/slideLayouts/_rels/slideLayout2.xml.rels" + , "ppt/slideLayouts/_rels/slideLayout3.xml.rels" + , "ppt/slideLayouts/_rels/slideLayout4.xml.rels" + , "ppt/slideLayouts/_rels/slideLayout5.xml.rels" + , "ppt/slideLayouts/_rels/slideLayout6.xml.rels" + , "ppt/slideLayouts/_rels/slideLayout7.xml.rels" + , "ppt/slideLayouts/_rels/slideLayout8.xml.rels" + , "ppt/slideLayouts/_rels/slideLayout9.xml.rels" + , "ppt/slideLayouts/slideLayout1.xml" + , "ppt/slideLayouts/slideLayout10.xml" + , "ppt/slideLayouts/slideLayout11.xml" + , "ppt/slideLayouts/slideLayout2.xml" + , "ppt/slideLayouts/slideLayout3.xml" + , "ppt/slideLayouts/slideLayout4.xml" + , "ppt/slideLayouts/slideLayout5.xml" + , "ppt/slideLayouts/slideLayout6.xml" + , "ppt/slideLayouts/slideLayout7.xml" + , "ppt/slideLayouts/slideLayout8.xml" + , "ppt/slideLayouts/slideLayout9.xml" + , "ppt/slideMasters/_rels/slideMaster1.xml.rels" + , "ppt/slideMasters/slideMaster1.xml" + , "ppt/slides/_rels/slide1.xml.rels" + , "ppt/slides/slide1.xml" + , "ppt/tableStyles.xml" + , "ppt/theme/theme1.xml" + , "ppt/viewProps.xml" + ] + let toLazy = BL.fromChunks . (:[]) + let pathToEntry path = do + epochtime <- (floor . utcTimeToPOSIXSeconds) <$> getCurrentTime + contents <- toLazy <$> readDataFile ("pptx/" ++ path) + return $ toEntry path epochtime contents + datadir <- getUserDataDir + mbArchive <- case datadir of + Nothing -> return Nothing + Just d -> do + exists <- fileExists (d </> "reference.pptx") + if exists + then return (Just (d </> "reference.pptx")) + else return Nothing + case mbArchive of + Just arch -> toArchive <$> readFileLazy arch + Nothing -> foldr addEntryToArchive emptyArchive <$> + mapM pathToEntry paths + + -- | Read file from user data directory or, -- if not found there, from Cabal data directory. readDataFile :: PandocMonad m => FilePath -> m B.ByteString @@ -691,6 +751,8 @@ readDataFile fname = do readDefaultDataFile :: PandocMonad m => FilePath -> m B.ByteString readDefaultDataFile "reference.docx" = (B.concat . BL.toChunks . fromArchive) <$> getDefaultReferenceDocx +readDefaultDataFile "reference.pptx" = + (B.concat . BL.toChunks . fromArchive) <$> getDefaultReferencePptx readDefaultDataFile "reference.odt" = (B.concat . BL.toChunks . fromArchive) <$> getDefaultReferenceODT readDefaultDataFile fname = @@ -806,6 +868,7 @@ data PureState = PureState { stStdGen :: StdGen , stTime :: UTCTime , stTimeZone :: TimeZone , stReferenceDocx :: Archive + , stReferencePptx :: Archive , stReferenceODT :: Archive , stFiles :: FileTree , stUserDataFiles :: FileTree @@ -820,6 +883,7 @@ instance Default PureState where , stTime = posixSecondsToUTCTime 0 , stTimeZone = utc , stReferenceDocx = emptyArchive + , stReferencePptx = emptyArchive , stReferenceODT = emptyArchive , stFiles = mempty , stUserDataFiles = mempty -- cgit v1.2.3 From da4703236dbef86dd7fa036fb28fa43039e45146 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sun, 10 Dec 2017 10:59:25 -0500 Subject: Add Powerpoint writer. This imports the essential Powerpoint writer. It works following the standard Pandoc conventions for making other sorts of slides. At the moment, there are still these TODOs: 1. Syntax highlighting is not yet implemented. (This is difficult because there are no character classes in Powerpoint.) 2. Footnotes and Definition lists are not yet implemented. (Notes will usually take the form of a final slide. 3. Image placement and auto-resizing has a few glitches. 4. Reference powerpoint files don't work dependably from the command line. This will be implemented, but at the moment users are advised to change themes from within Powerpoint. --- src/Text/Pandoc/Writers/Powerpoint.hs | 1665 +++++++++++++++++++++++++++++++++ 1 file changed, 1665 insertions(+) create mode 100644 src/Text/Pandoc/Writers/Powerpoint.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs new file mode 100644 index 000000000..f7d5bbc5f --- /dev/null +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -0,0 +1,1665 @@ +{-# LANGUAGE PatternGuards, MultiWayIf, OverloadedStrings #-} + +{- +Copyright (C) 2017 Jesse Rosenthal <jrosenthal@jhu.edu> + +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.Writers.Powerpoint + Copyright : Copyright (C) 2017 Jesse Rosenthal + License : GNU GPL, version 2 or above + + Maintainer : Jesse Rosenthal <jrosenthal@jhu.edu> + Stability : alpha + Portability : portable + +Conversion of 'Pandoc' documents to powerpoint (pptx). +-} + +module Text.Pandoc.Writers.Powerpoint (writePowerpoint) where + +import Control.Monad.Except (throwError) +import Control.Monad.Reader +import Control.Monad.State +import Codec.Archive.Zip +import Data.List (intercalate, stripPrefix, isPrefixOf, nub) +-- import Control.Monad (mplus) +import Data.Default +import Data.Time.Clock (UTCTime) +import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds, posixSecondsToUTCTime) +import System.FilePath.Posix (splitDirectories, splitExtension) +import Text.XML.Light +import Text.Pandoc.Definition +import qualified Text.Pandoc.UTF8 as UTF8 +import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Error (PandocError(..)) +import qualified Text.Pandoc.Class as P +import Text.Pandoc.Options +import Text.Pandoc.MIME +import Text.Pandoc.Logging +import qualified Data.ByteString.Lazy as BL +-- import qualified Data.ByteString.Lazy.Char8 as BL8 +-- import qualified Text.Pandoc.UTF8 as UTF8 +import Text.Pandoc.Walk +import Text.Pandoc.Writers.Shared (fixDisplayMath) +import Text.Pandoc.Writers.OOXML +import qualified Data.Map as M +import Data.Maybe (mapMaybe, listToMaybe) +import Text.Pandoc.ImageSize +import Control.Applicative ((<|>)) + +import Text.TeXMath +import Text.Pandoc.Writers.Math (convertMath) + + +writePowerpoint :: (PandocMonad m) + => WriterOptions -- ^ Writer options + -> Pandoc -- ^ Document to convert + -> m BL.ByteString +writePowerpoint opts (Pandoc meta blks) = do + let blks' = walk fixDisplayMath blks + distArchive <- (toArchive . BL.fromStrict) <$> + P.readDefaultDataFile "reference.pptx" + refArchive <- case writerReferenceDoc opts of + Just f -> toArchive <$> P.readFileLazy f + Nothing -> (toArchive . BL.fromStrict) <$> + P.readDataFile "reference.pptx" + + utctime <- P.getCurrentTime + + let env = def { envMetadata = meta + , envRefArchive = refArchive + , envDistArchive = distArchive + , envUTCTime = utctime + , envOpts = opts + } + runP env def $ do pres <- blocksToPresentation blks' + archv <- presentationToArchive pres + return $ fromArchive archv + +concatMapM :: (Monad m) => (a -> m [b]) -> [a] -> m [b] +concatMapM f xs = liftM concat (mapM f xs) + +data WriterEnv = WriterEnv { envMetadata :: Meta + , envRunProps :: RunProps + , envParaProps :: ParaProps + , envSlideLevel :: Int + , envRefArchive :: Archive + , envDistArchive :: Archive + , envUTCTime :: UTCTime + , envOpts :: WriterOptions + , envPresentationSize :: PresentationSize + , envSlideHasHeader :: Bool + , envInList :: Bool + } + deriving (Show) + +instance Default WriterEnv where + def = WriterEnv { envMetadata = mempty + , envRunProps = def + , envParaProps = def + , envSlideLevel = 2 + , envRefArchive = emptyArchive + , envDistArchive = emptyArchive + , envUTCTime = posixSecondsToUTCTime 0 + , envOpts = def + , envPresentationSize = def + , envSlideHasHeader = False + , envInList = False + } + +data MediaInfo = MediaInfo { mInfoFilePath :: FilePath + , mInfoLocalId :: Int + , mInfoGlobalId :: Int + , mInfoMimeType :: Maybe MimeType + , mInfoExt :: Maybe String + , mInfoCaption :: Bool + } deriving (Show, Eq) + +data WriterState = WriterState { stCurSlideId :: Int + -- the difference between the number at + -- the end of the slide file name and + -- the rId number + , stSlideIdOffset :: Int + , stLinkIds :: M.Map Int (M.Map Int (URL, String)) + -- (FP, Local ID, Global ID, Maybe Mime) + , stMediaIds :: M.Map Int [MediaInfo] + , stMediaGlobalIds :: M.Map FilePath Int + } deriving (Show, Eq) + +instance Default WriterState where + def = WriterState { stCurSlideId = 0 + , stSlideIdOffset = 1 + , stLinkIds = mempty + , stMediaIds = mempty + , stMediaGlobalIds = mempty + } + +type P m = ReaderT WriterEnv (StateT WriterState m) + +runP :: Monad m => WriterEnv -> WriterState -> P m a -> m a +runP env st p = evalStateT (runReaderT p env) st + +type Pixels = Integer + +data Presentation = Presentation PresentationSize [Slide] + deriving (Show) + +data PresentationSize = PresentationSize { presSizeWidth :: Pixels + , presSizeRatio :: PresentationRatio + } + deriving (Show, Eq) + +data PresentationRatio = Ratio4x3 + | Ratio16x9 + | Ratio16x10 + deriving (Show, Eq) + +-- Note that right now we're only using Ratio4x3. +getPageHeight :: PresentationSize -> Pixels +getPageHeight sz = case presSizeRatio sz of + Ratio4x3 -> floor (((fromInteger (presSizeWidth sz)) / 4) * 3 :: Double) + Ratio16x9 -> floor (((fromInteger (presSizeWidth sz)) / 16) * 9 :: Double) + Ratio16x10 -> floor (((fromInteger (presSizeWidth sz)) / 16) * 10 :: Double) + +instance Default PresentationSize where + def = PresentationSize 720 Ratio4x3 + +data Slide = MetadataSlide { metadataSlideTitle :: [ParaElem] + , metadataSlideSubtitle :: [ParaElem] + , metadataSlideAuthors :: [[ParaElem]] + , metadataSlideDate :: [ParaElem] + } + | TitleSlide { titleSlideHeader :: [ParaElem]} + | ContentSlide { contentSlideHeader :: [ParaElem] + , contentSlideContent :: [Shape] + } + deriving (Show, Eq) + +data SlideElement = SlideElement Pixels Pixels Pixels Pixels Shape + deriving (Show, Eq) + +data Shape = Pic FilePath Text.Pandoc.Definition.Attr [ParaElem] + | GraphicFrame [Graphic] [ParaElem] + | TextBox [Paragraph] + deriving (Show, Eq) + +type Cell = [Paragraph] + +data TableProps = TableProps { tblPrFirstRow :: Bool + , tblPrBandRow :: Bool + } deriving (Show, Eq) + +type ColWidth = Integer + +data Graphic = Tbl TableProps [ColWidth] [Cell] [[Cell]] + deriving (Show, Eq) + + +data Paragraph = Paragraph { paraProps :: ParaProps + , paraElems :: [ParaElem] + } deriving (Show, Eq) + +data HeaderType = TitleHeader | SlideHeader | InternalHeader Int + deriving (Show, Eq) + +-- type StartingAt = Int + +-- data AutoNumType = ArabicNum +-- | AlphaUpperNum +-- | AlphaLowerNum +-- | RomanUpperNum +-- | RomanLowerNum +-- deriving (Show, Eq) + +-- data AutoNumDelim = PeriodDelim +-- | OneParenDelim +-- | TwoParensDelim +-- deriving (Show, Eq) + +autoNumberingToType :: ListAttributes -> String +autoNumberingToType (_, numStyle, numDelim) = + typeString ++ delimString + where + typeString = case numStyle of + Decimal -> "arabic" + UpperAlpha -> "alphaUc" + LowerAlpha -> "alphaLc" + UpperRoman -> "romanUc" + LowerRoman -> "romanLc" + _ -> "arabic" + delimString = case numDelim of + Period -> "Period" + OneParen -> "ParenR" + TwoParens -> "ParenBoth" + _ -> "Period" + +data BulletType = Bullet + | AutoNumbering ListAttributes + deriving (Show, Eq) + +data Algnment = AlgnLeft | AlgnRight | AlgnCenter + deriving (Show, Eq) + +data ParaProps = ParaProps { pPropHeaderType :: Maybe HeaderType + , pPropMarginLeft :: Maybe Pixels + , pPropMarginRight :: Maybe Pixels + , pPropLevel :: Int + , pPropBullet :: Maybe BulletType + , pPropAlign :: Maybe Algnment + } deriving (Show, Eq) + +instance Default ParaProps where + def = ParaProps { pPropHeaderType = Nothing + , pPropMarginLeft = Just 0 + , pPropMarginRight = Just 0 + , pPropLevel = 0 + , pPropBullet = Nothing + , pPropAlign = Nothing + } + +newtype TeXString = TeXString {unTeXString :: String} + deriving (Eq, Show) + +data ParaElem = Break + | Run RunProps String + -- It would be more elegant to have native TeXMath + -- Expressions here, but this allows us to use + -- `convertmath` from T.P.Writers.Math. Will perhaps + -- revisit in the future. + | MathElem MathType TeXString + deriving (Show, Eq) + +data Strikethrough = NoStrike | SingleStrike | DoubleStrike + deriving (Show, Eq) + +data Capitals = NoCapitals | SmallCapitals | AllCapitals + deriving (Show, Eq) + +type URL = String + +data RunProps = RunProps { rPropBold :: Bool + , rPropItalics :: Bool + , rStrikethrough :: Maybe Strikethrough + , rBaseline :: Maybe Int + , rCap :: Maybe Capitals + , rLink :: Maybe (URL, String) + , rPropCode :: Bool + , rPropBlockQuote :: Bool + } deriving (Show, Eq) + +instance Default RunProps where + def = RunProps { rPropBold = False + , rPropItalics = False + , rStrikethrough = Nothing + , rBaseline = Nothing + , rCap = Nothing + , rLink = Nothing + , rPropCode = False + , rPropBlockQuote = False + } + +-------------------------------------------------- + +inlinesToParElems :: Monad m => [Inline] -> P m [ParaElem] +inlinesToParElems ils = concatMapM inlineToParElems ils + +inlineToParElems :: Monad m => Inline -> P m [ParaElem] +inlineToParElems (Str s) = do + pr <- asks envRunProps + return [Run pr s] +inlineToParElems (Emph ils) = + local (\r -> r{envRunProps = (envRunProps r){rPropItalics=True}}) $ + inlinesToParElems ils +inlineToParElems (Strong ils) = + local (\r -> r{envRunProps = (envRunProps r){rPropBold=True}}) $ + inlinesToParElems ils +inlineToParElems (Strikeout ils) = + local (\r -> r{envRunProps = (envRunProps r){rStrikethrough=Just SingleStrike}}) $ + inlinesToParElems ils +inlineToParElems (Superscript ils) = + local (\r -> r{envRunProps = (envRunProps r){rBaseline=Just 30000}}) $ + inlinesToParElems ils +inlineToParElems (Subscript ils) = + local (\r -> r{envRunProps = (envRunProps r){rBaseline=Just (-25000)}}) $ + inlinesToParElems ils +inlineToParElems (SmallCaps ils) = + local (\r -> r{envRunProps = (envRunProps r){rCap = Just SmallCapitals}}) $ + inlinesToParElems ils +inlineToParElems Space = inlineToParElems (Str " ") +inlineToParElems SoftBreak = inlineToParElems (Str " ") +inlineToParElems LineBreak = return [Break] +inlineToParElems (Link _ ils (url, title)) = do + local (\r ->r{envRunProps = (envRunProps r){rLink = Just (url, title)}}) $ + inlinesToParElems ils +inlineToParElems (Code _ str) = do + local (\r ->r{envRunProps = def{rPropCode = True}}) $ + inlineToParElems $ Str str +inlineToParElems (Math mathtype str) = + return [MathElem mathtype (TeXString str)] +inlineToParElems (Span _ ils) = concatMapM inlineToParElems ils +inlineToParElems (RawInline _ _) = return [] +inlineToParElems _ = return [] + +blockToParagraphs :: PandocMonad m => Block -> P m [Paragraph] +blockToParagraphs (Plain ils) = do + parElems <- inlinesToParElems ils + pProps <- asks envParaProps + return [Paragraph pProps parElems] +blockToParagraphs (Para ils) = do + parElems <- inlinesToParElems ils + pProps <- asks envParaProps + return [Paragraph pProps parElems] +blockToParagraphs (LineBlock ilsList) = do + parElems <- inlinesToParElems $ intercalate [LineBreak] ilsList + pProps <- asks envParaProps + return [Paragraph pProps parElems] +-- TODO: work out the attributes +blockToParagraphs (CodeBlock attr str) = + local (\r -> r{envParaProps = def{pPropMarginLeft = Just 100}}) $ + blockToParagraphs $ Para [Code attr str] +-- TODO: work out the format +blockToParagraphs (BlockQuote blks) = + local (\r -> r{ envParaProps = (envParaProps r){pPropMarginLeft = Just 100} + , envRunProps = (envRunProps r){rPropBlockQuote = True}})$ + concatMapM blockToParagraphs blks +-- TODO: work out the format +blockToParagraphs (RawBlock _ _) = return [] + -- parElems <- inlinesToParElems [Str str] + -- paraProps <- asks envParaProps + -- return [Paragraph paraProps parElems] +-- TODO: work out the format +blockToParagraphs (Header n _ ils) = do + slideLevel <- asks envSlideLevel + parElems <- inlinesToParElems ils + -- For the time being we're not doing headers inside of bullets, but + -- we might change that. + let headerType = case n `compare` slideLevel of + LT -> TitleHeader + EQ -> SlideHeader + GT -> InternalHeader (n - slideLevel) + return [Paragraph def{pPropHeaderType = Just headerType} parElems] +blockToParagraphs (BulletList blksLst) = do + pProps <- asks envParaProps + let lvl = pPropLevel pProps + local (\env -> env{ envInList = True + , envParaProps = pProps{ pPropLevel = lvl + 1 + , pPropBullet = Just Bullet + , pPropMarginLeft = Nothing + }}) $ + concatMapM multiParBullet blksLst +blockToParagraphs (OrderedList listAttr blksLst) = do + pProps <- asks envParaProps + let lvl = pPropLevel pProps + local (\env -> env{ envInList = True + , envParaProps = pProps{ pPropLevel = lvl + 1 + , pPropBullet = Just (AutoNumbering listAttr) + , pPropMarginLeft = Nothing + }}) $ + concatMapM multiParBullet blksLst +blockToParagraphs (Div _ blks) = concatMapM blockToParagraphs blks +-- TODO +blockToParagraphs blk = do + P.report $ BlockNotRendered blk + return [] + +-- Make sure the bullet env gets turned off after the first para. +multiParBullet :: PandocMonad m => [Block] -> P m [Paragraph] +multiParBullet [] = return [] +multiParBullet (b:bs) = do + pProps <- asks envParaProps + p <- blockToParagraphs b + ps <- local (\env -> env{envParaProps = pProps{pPropBullet = Nothing}}) $ + concatMapM blockToParagraphs bs + return $ p ++ ps + +cellToParagraphs :: PandocMonad m => Alignment -> TableCell -> P m [Paragraph] +cellToParagraphs algn tblCell = do + paras <- mapM (blockToParagraphs) tblCell + let alignment = case algn of + AlignLeft -> Just AlgnLeft + AlignRight -> Just AlgnRight + AlignCenter -> Just AlgnCenter + AlignDefault -> Nothing + paras' = map (map (\p -> p{paraProps = (paraProps p){pPropAlign = alignment}})) paras + return $ concat paras' + +rowToParagraphs :: PandocMonad m => [Alignment] -> [TableCell] -> P m [[Paragraph]] +rowToParagraphs algns tblCells = do + -- We have to make sure we have the right number of alignments + let pairs = zip (algns ++ repeat AlignDefault) tblCells + mapM (\(a, tc) -> cellToParagraphs a tc) pairs + +blockToShape :: PandocMonad m => Block -> P m Shape +blockToShape (Plain (il:_)) | Image attr ils (url, _) <- il = + Pic url attr <$> (inlinesToParElems ils) +blockToShape (Para (il:_)) | Image attr ils (url, _) <- il = + Pic url attr <$> (inlinesToParElems ils) +blockToShape (Table caption algn _ hdrCells rows) = do + caption' <- inlinesToParElems caption + pageWidth <- presSizeWidth <$> asks envPresentationSize + hdrCells' <- rowToParagraphs algn hdrCells + rows' <- mapM (rowToParagraphs algn) rows + let tblPr = if null hdrCells + then TableProps { tblPrFirstRow = False + , tblPrBandRow = True + } + else TableProps { tblPrFirstRow = True + , tblPrBandRow = True + } + colWidths = if null hdrCells + then case rows of + r : _ | not (null r) -> replicate (length r) $ + (pageWidth - (2 * hardcodedTableMargin))`div` (toInteger $ length r) + -- satisfy the compiler. This is the same as + -- saying that rows is empty, but the compiler + -- won't understand that `[]` exhausts the + -- alternatives. + _ -> [] + else replicate (length hdrCells) $ + (pageWidth - (2 * hardcodedTableMargin)) `div` (toInteger $ length hdrCells) + + return $ GraphicFrame [Tbl tblPr colWidths hdrCells' rows'] caption' +blockToShape blk = TextBox <$> blockToParagraphs blk + +blocksToShapes :: PandocMonad m => [Block] -> P m [Shape] +blocksToShapes blks = combineShapes <$> mapM blockToShape blks + +splitBlocks' :: Monad m => [Block] -> [[Block]] -> [Block] -> P m [[Block]] +splitBlocks' cur acc [] = return $ acc ++ (if null cur then [] else [cur]) +splitBlocks' cur acc (HorizontalRule : blks) = + splitBlocks' [] (acc ++ (if null cur then [] else [cur])) blks +splitBlocks' cur acc (h@(Header n _ _) : blks) = do + slideLevel <- asks envSlideLevel + case compare n slideLevel of + LT -> splitBlocks' [] (acc ++ (if null cur then [] else [cur]) ++ [[h]]) blks + EQ -> splitBlocks' [h] (acc ++ (if null cur then [] else [cur])) blks + GT -> splitBlocks' (cur ++ [h]) acc blks +splitBlocks' cur acc ((Para (img@(Image _ _ _):ils)) : blks) = do + slideLevel <- asks envSlideLevel + case cur of + (Header n _ _) : [] | n == slideLevel -> + splitBlocks' [] + (acc ++ [cur ++ [Para [img]]]) + (if null ils then blks else (Para ils) : blks) + _ -> splitBlocks' [] + (acc ++ (if null cur then [] else [cur]) ++ [[Para [img]]]) + (if null ils then blks else (Para ils) : blks) +splitBlocks' cur acc ((Plain (img@(Image _ _ _):ils)) : blks) = do + slideLevel <- asks envSlideLevel + case cur of + (Header n _ _) : [] | n == slideLevel -> + splitBlocks' [] + (acc ++ [cur ++ [Para [img]]]) + (if null ils then blks else (Plain ils) : blks) + _ -> splitBlocks' [] + (acc ++ (if null cur then [] else [cur]) ++ [[Para [img]]]) + (if null ils then blks else (Plain ils) : blks) +splitBlocks' cur acc (tbl@(Table _ _ _ _ _) : blks) = do + slideLevel <- asks envSlideLevel + case cur of + (Header n _ _) : [] | n == slideLevel -> + splitBlocks' [] (acc ++ [cur ++ [tbl]]) blks + _ -> splitBlocks' [] (acc ++ (if null cur then [] else [cur]) ++ [[tbl]]) blks +splitBlocks' cur acc (blk : blks) = splitBlocks' (cur ++ [blk]) acc blks + +splitBlocks :: Monad m => [Block] -> P m [[Block]] +splitBlocks = splitBlocks' [] [] + +blocksToSlide' :: PandocMonad m => Int -> [Block] -> P m Slide +blocksToSlide' lvl ((Header n _ ils) : blks) + | n < lvl = do + hdr <- inlinesToParElems ils + return $ TitleSlide {titleSlideHeader = hdr} + | n == lvl = do + hdr <- inlinesToParElems ils + shapes <- blocksToShapes blks + return $ ContentSlide { contentSlideHeader = hdr + , contentSlideContent = shapes + } +blocksToSlide' _ (blk : blks) = do + shapes <- blocksToShapes (blk : blks) + return $ ContentSlide { contentSlideHeader = [] + , contentSlideContent = shapes + } +blocksToSlide' _ [] = return $ ContentSlide { contentSlideHeader = [] + , contentSlideContent = [] + } + +blocksToSlide :: PandocMonad m => [Block] -> P m Slide +blocksToSlide blks = do + slideLevel <- asks envSlideLevel + blocksToSlide' slideLevel blks + +getMetaSlide :: PandocMonad m => P m (Maybe Slide) +getMetaSlide = do + meta <- asks envMetadata + title <- inlinesToParElems $ docTitle meta + subtitle <- inlinesToParElems $ + case lookupMeta "subtitle" meta of + Just (MetaString s) -> [Str s] + Just (MetaInlines ils) -> ils + Just (MetaBlocks [Plain ils]) -> ils + Just (MetaBlocks [Para ils]) -> ils + _ -> [] + authors <- mapM inlinesToParElems $ docAuthors meta + date <- inlinesToParElems $ docDate meta + if null title && null subtitle && null authors && null date + then return Nothing + else return $ Just $ MetadataSlide { metadataSlideTitle = title + , metadataSlideSubtitle = subtitle + , metadataSlideAuthors = authors + , metadataSlideDate = date + } + +blocksToPresentation :: PandocMonad m => [Block] -> P m Presentation +blocksToPresentation blks = do + blksLst <- splitBlocks blks + slides <- mapM blocksToSlide blksLst + metadataslide <- getMetaSlide + presSize <- asks envPresentationSize + return $ case metadataslide of + Just metadataslide' -> Presentation presSize $ metadataslide' : slides + Nothing -> Presentation presSize slides + +-------------------------------------------------------------------- + +copyFileToArchive :: PandocMonad m => Archive -> FilePath -> P m Archive +copyFileToArchive arch fp = do + refArchive <- asks envRefArchive + distArchive <- asks envDistArchive + case findEntryByPath fp refArchive `mplus` findEntryByPath fp distArchive of + Nothing -> fail $ fp ++ " missing in reference file" + Just e -> return $ addEntryToArchive e arch + +getMediaFiles :: PandocMonad m => P m [FilePath] +getMediaFiles = do + refArchive <- asks envRefArchive + distArchive <- asks envDistArchive + let allEntries = nub $ filesInArchive refArchive ++ filesInArchive distArchive + return $ filter (isPrefixOf "ppt/media") allEntries + + +copyFileToArchiveIfExists :: PandocMonad m => Archive -> FilePath -> P m Archive +copyFileToArchiveIfExists arch fp = do + refArchive <- asks envRefArchive + distArchive <- asks envDistArchive + case findEntryByPath fp refArchive `mplus` findEntryByPath fp distArchive of + Nothing -> return $ arch + Just e -> return $ addEntryToArchive e arch + +inheritedFiles :: [FilePath] +inheritedFiles = [ "_rels/.rels" + , "docProps/app.xml" + , "docProps/core.xml" + , "ppt/slideLayouts/slideLayout4.xml" + , "ppt/slideLayouts/_rels/slideLayout9.xml.rels" + , "ppt/slideLayouts/_rels/slideLayout2.xml.rels" + , "ppt/slideLayouts/_rels/slideLayout10.xml.rels" + , "ppt/slideLayouts/_rels/slideLayout1.xml.rels" + , "ppt/slideLayouts/_rels/slideLayout3.xml.rels" + , "ppt/slideLayouts/_rels/slideLayout5.xml.rels" + , "ppt/slideLayouts/_rels/slideLayout7.xml.rels" + , "ppt/slideLayouts/_rels/slideLayout8.xml.rels" + , "ppt/slideLayouts/_rels/slideLayout11.xml.rels" + , "ppt/slideLayouts/_rels/slideLayout4.xml.rels" + , "ppt/slideLayouts/_rels/slideLayout6.xml.rels" + , "ppt/slideLayouts/slideLayout2.xml" + , "ppt/slideLayouts/slideLayout8.xml" + , "ppt/slideLayouts/slideLayout11.xml" + , "ppt/slideLayouts/slideLayout3.xml" + , "ppt/slideLayouts/slideLayout6.xml" + , "ppt/slideLayouts/slideLayout9.xml" + , "ppt/slideLayouts/slideLayout5.xml" + , "ppt/slideLayouts/slideLayout7.xml" + , "ppt/slideLayouts/slideLayout1.xml" + , "ppt/slideLayouts/slideLayout10.xml" + -- , "ppt/_rels/presentation.xml.rels" + , "ppt/theme/theme1.xml" + , "ppt/presProps.xml" + -- , "ppt/slides/_rels/slide1.xml.rels" + -- , "ppt/slides/_rels/slide2.xml.rels" + -- This is the one we're + -- going to build + -- , "ppt/slides/slide2.xml" + -- , "ppt/slides/slide1.xml" + , "ppt/viewProps.xml" + , "ppt/tableStyles.xml" + , "ppt/slideMasters/_rels/slideMaster1.xml.rels" + , "ppt/slideMasters/slideMaster1.xml" + -- , "ppt/presentation.xml" + -- , "[Content_Types].xml" + ] + +-- Here are some that might not be there. We won't fail if they're not +possibleInheritedFiles :: [FilePath] +possibleInheritedFiles = [ "ppt/theme/_rels/theme1.xml.rels" ] + +presentationToArchive :: PandocMonad m => Presentation -> P m Archive +presentationToArchive p@(Presentation _ slides) = do + newArch <- foldM copyFileToArchive emptyArchive inheritedFiles + mediaDir <- getMediaFiles + newArch' <- foldM copyFileToArchiveIfExists newArch $ + possibleInheritedFiles ++ mediaDir + -- presentation entry and rels. We have to do the rels first to make + -- sure we know the correct offset for the rIds. + presEntry <- presentationToPresEntry p + presRelsEntry <- presentationToRelsEntry p + slideEntries <- mapM (\(s, n) -> slideToEntry s n) $ zip slides [1..] + slideRelEntries <- mapM (\(s,n) -> slideToSlideRelEntry s n) $ zip slides [1..] + -- These have to come after everything, because they need the info + -- built up in the state. + mediaEntries <- makeMediaEntries + contentTypesEntry <- presentationToContentTypes p >>= contentTypesToEntry + -- fold everything into our inherited archive and return it. + return $ foldr addEntryToArchive newArch' $ + slideEntries ++ + slideRelEntries ++ + mediaEntries ++ + [contentTypesEntry, presEntry, presRelsEntry] + +-------------------------------------------------- + +combineShapes :: [Shape] -> [Shape] +combineShapes [] = [] +combineShapes (s : []) = [s] +combineShapes (pic@(Pic _ _ _) : ss) = pic : combineShapes ss +combineShapes ((TextBox []) : ss) = combineShapes ss +combineShapes (s : TextBox [] : ss) = combineShapes (s : ss) +combineShapes (s@(TextBox (p:ps)) : s'@(TextBox (p':ps')) : ss) + | pPropHeaderType (paraProps p) == Just TitleHeader || + pPropHeaderType (paraProps p) == Just SlideHeader = + TextBox [p] : (combineShapes $ TextBox ps : s' : ss) + | pPropHeaderType (paraProps p') == Just TitleHeader || + pPropHeaderType (paraProps p') == Just SlideHeader = + s : TextBox [p'] : (combineShapes $ TextBox ps' : ss) + | otherwise = combineShapes $ TextBox ((p:ps) ++ (p':ps')) : ss +combineShapes (s:ss) = s : combineShapes ss + +-------------------------------------------------- + +getLayout :: PandocMonad m => Slide -> P m Element +getLayout slide = do + let layoutpath = case slide of + (MetadataSlide _ _ _ _) -> "ppt/slideLayouts/slideLayout1.xml" + (TitleSlide _) -> "ppt/slideLayouts/slideLayout3.xml" + (ContentSlide _ _) -> "ppt/slideLayouts/slideLayout2.xml" + distArchive <- asks envDistArchive + root <- case findEntryByPath layoutpath distArchive of + Just e -> case parseXMLDoc $ UTF8.toStringLazy $ fromEntry e of + Just element -> return $ element + Nothing -> throwError $ + PandocSomeError $ + layoutpath ++ " corrupt in reference file" + Nothing -> throwError $ + PandocSomeError $ + layoutpath ++ " missing in reference file" + return root + -- let ns = elemToNameSpaces root + -- case findChild (elemName ns "p" "cSld") root of + -- Just element' -> return element' + -- Nothing -> throwError $ + -- PandocSomeError $ + -- layoutpath ++ " not correctly formed layout file" + +shapeHasName :: NameSpaces -> String -> Element -> Bool +shapeHasName ns name element + | Just nvSpPr <- findChild (elemName ns "p" "nvSpPr") element + , Just cNvPr <- findChild (elemName ns "p" "cNvPr") nvSpPr + , Just nm <- findAttr (QName "name" Nothing Nothing) cNvPr = + nm == name + | otherwise = False + +-- getContentTitleShape :: NameSpaces -> Element -> Maybe Element +-- getContentTitleShape ns spTreeElem +-- | isElem ns "p" "spTree" spTreeElem = +-- filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasName ns "Title 1" e)) spTreeElem +-- | otherwise = Nothing + +-- getSubtitleShape :: NameSpaces -> Element -> Maybe Element +-- getSubtitleShape ns spTreeElem +-- | isElem ns "p" "spTree" spTreeElem = +-- filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasName ns "Subtitle 2" e)) spTreeElem +-- | otherwise = Nothing + +-- getDateShape :: NameSpaces -> Element -> Maybe Element +-- getDateShape ns spTreeElem +-- | isElem ns "p" "spTree" spTreeElem = +-- filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasName ns "Date Placeholder 3" e)) spTreeElem +-- | otherwise = Nothing + +getContentShape :: NameSpaces -> Element -> Maybe Element +getContentShape ns spTreeElem + | isElem ns "p" "spTree" spTreeElem = + filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasName ns "Content Placeholder 2" e)) spTreeElem + | otherwise = Nothing + + +-- cursorHasName :: QName -> XMLC.Cursor -> Bool +-- cursorHasName nm cur = case XMLC.current cur of +-- Elem element -> case XMLC.tagName $ XMLC.getTag element of +-- nm -> True +-- _ -> False +-- _ -> False + +-- fillInTxBody :: NameSpaces -> [Paragraph] -> Element -> Element +-- fillInTxBody ns paras txBodyElem +-- | isElem ns "p" "txBody" txBodyElem = +-- replaceNamedChildren ns "a" "p" (map paragraphToElement paras) txBodyElem +-- | otherwise = txBodyElem + +-- fillInShape :: NameSpaces -> Shape -> Element -> Element +-- fillInShape ns shape spElem +-- | TextBox paras <- shape +-- , isElemn ns "p" "sp" spElem = +-- replaceNamedChildren ns "p" "txBody" (fillInTxBody ns paras sp + + +-- fillInShape :: NameSpaces -> Element -> Shape -> Element +-- fillInShape ns spElem (TextBox paras) = fillInParagraphs ns spElem paras +-- fillInShape _ spElem pic = spElem + +contentIsElem :: NameSpaces -> String -> String -> Content -> Bool +contentIsElem ns prefix name (Elem element) = isElem ns prefix name element +contentIsElem _ _ _ _ = False + +replaceNamedChildren :: NameSpaces -> String -> String -> [Element] -> Element -> Element +replaceNamedChildren ns prefix name newKids element = + let content = elContent element + content' = filter (\c -> not (contentIsElem ns prefix name c)) content + in + element{elContent = content' ++ map Elem newKids} + + +---------------------------------------------------------------- + +registerLink :: PandocMonad m => (URL, String) -> P m Int +registerLink link = do + curSlideId <- gets stCurSlideId + linkReg <- gets stLinkIds + mediaReg <- gets stMediaIds + let maxLinkId = case M.lookup curSlideId linkReg of + Just mp -> case M.keys mp of + [] -> 1 + ks -> maximum ks + Nothing -> 1 + maxMediaId = case M.lookup curSlideId mediaReg of + Just [] -> 1 + Just mInfos -> maximum $ map mInfoLocalId mInfos + Nothing -> 1 + maxId = max maxLinkId maxMediaId + slideLinks = case M.lookup curSlideId linkReg of + Just mp -> M.insert (maxId + 1) link mp + Nothing -> M.singleton (maxId + 1) link + modify $ \st -> st{ stLinkIds = M.insert curSlideId slideLinks linkReg} + return $ maxId + 1 + +registerMedia :: PandocMonad m => FilePath -> [ParaElem] -> P m MediaInfo +registerMedia fp caption = do + curSlideId <- gets stCurSlideId + linkReg <- gets stLinkIds + mediaReg <- gets stMediaIds + globalIds <- gets stMediaGlobalIds + let maxLinkId = case M.lookup curSlideId linkReg of + Just mp -> case M.keys mp of + [] -> 1 + ks -> maximum ks + Nothing -> 1 + maxMediaId = case M.lookup curSlideId mediaReg of + Just [] -> 1 + Just mInfos -> maximum $ map mInfoLocalId mInfos + Nothing -> 1 + maxLocalId = max maxLinkId maxMediaId + + maxGlobalId = case M.elems globalIds of + [] -> 0 + ids -> maximum ids + + (imgBytes, mbMt) <- P.fetchItem fp + let imgExt = (mbMt >>= extensionFromMimeType >>= (\x -> return $ '.':x)) + <|> + case imageType imgBytes of + Just Png -> Just ".png" + Just Jpeg -> Just ".jpeg" + Just Gif -> Just ".gif" + Just Pdf -> Just ".pdf" + Just Eps -> Just ".eps" + Just Svg -> Just ".svg" + Nothing -> Nothing + + let newGlobalId = case M.lookup fp globalIds of + Just ident -> ident + Nothing -> maxGlobalId + 1 + + let newGlobalIds = M.insert fp newGlobalId globalIds + + let mediaInfo = MediaInfo { mInfoFilePath = fp + , mInfoLocalId = maxLocalId + 1 + , mInfoGlobalId = newGlobalId + , mInfoMimeType = mbMt + , mInfoExt = imgExt + , mInfoCaption = (not . null) caption + } + + let slideMediaInfos = case M.lookup curSlideId mediaReg of + Just minfos -> mediaInfo : minfos + Nothing -> [mediaInfo] + + + modify $ \st -> st{ stMediaIds = M.insert curSlideId slideMediaInfos mediaReg + , stMediaGlobalIds = newGlobalIds + } + return mediaInfo + +makeMediaEntry :: PandocMonad m => MediaInfo -> P m Entry +makeMediaEntry mInfo = do + epochtime <- (floor . utcTimeToPOSIXSeconds) <$> asks envUTCTime + (imgBytes, _) <- P.fetchItem (mInfoFilePath mInfo) + let ext = case mInfoExt mInfo of + Just e -> e + Nothing -> "" + let fp = "ppt/media/image" ++ (show $ mInfoGlobalId mInfo) ++ ext + return $ toEntry fp epochtime $ BL.fromStrict imgBytes + +makeMediaEntries :: PandocMonad m => P m [Entry] +makeMediaEntries = do + mediaInfos <- gets stMediaIds + let allInfos = mconcat $ M.elems mediaInfos + mapM makeMediaEntry allInfos + +-- | Scales the image to fit the page +-- sizes are passed in emu +fitToPage' :: (Double, Double) -- image size in emu + -> Integer -- pageWidth + -> Integer -- pageHeight + -> (Integer, Integer) -- imagesize +fitToPage' (x, y) pageWidth pageHeight + -- Fixes width to the page width and scales the height + | x <= fromIntegral pageWidth && y <= fromIntegral pageHeight = + (floor x, floor y) + | x / fromIntegral pageWidth > y / fromIntegral pageWidth = + (pageWidth, floor $ ((fromIntegral pageWidth) / x) * y) + | otherwise = + (floor $ ((fromIntegral pageHeight) / y) * x, pageHeight) + +positionImage :: (Double, Double) -> Integer -> Integer -> (Integer, Integer) +positionImage (x, y) pageWidth pageHeight = + let (x', y') = fitToPage' (x, y) pageWidth pageHeight + in + ((pageWidth - x') `div` 2, (pageHeight - y') `div` 2) + +getMaster :: PandocMonad m => P m Element +getMaster = do + refArchive <- asks envRefArchive + distArchive <- asks envDistArchive + parseXml refArchive distArchive "ppt/slideMasters/slideMaster1.xml" + +-- We want to get the header dimensions, so we can make sure that the +-- image goes underneath it. We only use this in a content slide if it +-- has a header. + +getHeaderSize :: PandocMonad m => P m ((Integer, Integer), (Integer, Integer)) +getHeaderSize = do + master <- getMaster + let ns = elemToNameSpaces master + sps = [master] >>= + findChildren (elemName ns "p" "cSld") >>= + findChildren (elemName ns "p" "spTree") >>= + findChildren (elemName ns "p" "sp") + mbXfrm = + listToMaybe (filter (shapeHasName ns "Title Placeholder 1") sps) >>= + findChild (elemName ns "p" "spPr") >>= + findChild (elemName ns "a" "xfrm") + xoff = mbXfrm >>= + findChild (elemName ns "a" "off") >>= + findAttr (QName "x" Nothing Nothing) >>= + (listToMaybe . (\s -> reads s :: [(Integer, String)])) + yoff = mbXfrm >>= + findChild (elemName ns "a" "off") >>= + findAttr (QName "y" Nothing Nothing) >>= + (listToMaybe . (\s -> reads s :: [(Integer, String)])) + xext = mbXfrm >>= + findChild (elemName ns "a" "ext") >>= + findAttr (QName "cx" Nothing Nothing) >>= + (listToMaybe . (\s -> reads s :: [(Integer, String)])) + yext = mbXfrm >>= + findChild (elemName ns "a" "ext") >>= + findAttr (QName "cy" Nothing Nothing) >>= + (listToMaybe . (\s -> reads s :: [(Integer, String)])) + off = case xoff of + Just (xoff', _) | Just (yoff',_) <- yoff -> (xoff', yoff') + _ -> (1043490, 1027664) + ext = case xext of + Just (xext', _) | Just (yext',_) <- yext -> (xext', yext') + _ -> (7024744, 1143000) + return $ (off, ext) + + +-- Hard-coded for now +captionPosition :: ((Integer, Integer), (Integer, Integer)) +captionPosition = ((457200, 6061972), (8229600, 527087)) + +createCaption :: PandocMonad m => [ParaElem] -> P m Element +createCaption paraElements = do + let para = Paragraph def{pPropAlign = Just AlgnCenter} paraElements + elements <- mapM paragraphToElement [para] + let ((x, y), (cx, cy)) = captionPosition + let txBody = mknode "p:txBody" [] $ + [mknode "a:bodyPr" [] (), mknode "a:lstStyle" [] ()] ++ elements + return $ + mknode "p:sp" [] [ mknode "p:nvSpPr" [] + [ mknode "p:cNvPr" [("id","1"), ("name","TextBox 3")] () + , mknode "p:cNvSpPr" [("txBox", "1")] () + , mknode "p:nvPr" [] () + ] + , mknode "p:spPr" [] + [ mknode "a:xfrm" [] + [ mknode "a:off" [("x", show x), ("y", show y)] () + , mknode "a:ext" [("cx", show cx), ("cy", show cy)] () + ] + , mknode "a:prstGeom" [("prst", "rect")] + [ mknode "a:avLst" [] () + ] + , mknode "a:noFill" [] () + ] + , txBody + ] + +-- Largely lifted from inlineToOpenXML' in T.P.W.Docx. Can't be easily +-- abstracted because of some different namespaces and monads. TODO. +makePicElement :: PandocMonad m + => MediaInfo + -> Text.Pandoc.Definition.Attr + -> P m Element +makePicElement mInfo attr = do + opts <- asks envOpts + pageWidth <- presSizeWidth <$> asks envPresentationSize + pageHeight <- getPageHeight <$> asks envPresentationSize + hasHeader <- asks envSlideHasHeader + let hasCaption = mInfoCaption mInfo + (imgBytes, _) <- P.fetchItem (mInfoFilePath mInfo) + -- We're not using x exts + ((hXoff, hYoff), (_, hYext)) <- if hasHeader + then getHeaderSize + else return ((0, 0), (0, 0)) + + let ((capX, capY), (_, _)) = if hasCaption + then captionPosition + else ((0,0), (0,0)) + let (xpt,ypt) = desiredSizeInPoints opts attr + (either (const def) id (imageSize opts imgBytes)) + -- 12700 emu = 1 pt + let (xemu,yemu) = fitToPage' (xpt * 12700, ypt * 12700) + ((pageWidth * 12700) - (2 * hXoff) - (2 * capX)) + ((if hasCaption then capY else (pageHeight * 12700)) - (hYoff + hYext)) + (xoff, yoff) = positionImage (xpt * 12700, ypt * 12700) (pageWidth * 12700) (pageHeight * 12700) + xoff' = if hasHeader then xoff + hXoff else xoff + xoff'' = if hasCaption then xoff' + capX else xoff' + yoff' = if hasHeader then hYoff + hYext else yoff + -- let (xemu,yemu)=((floor $ xpt * 12700), (floor $ ypt * 12700)) + let cNvPicPr = mknode "p:cNvPicPr" [] $ + mknode "a:picLocks" [("noGrp","1") + ,("noChangeAspect","1")] () + let nvPicPr = mknode "p:nvPicPr" [] + [ mknode "p:cNvPr" + [("descr", mInfoFilePath mInfo),("id","0"),("name","Picture 1")] () + , cNvPicPr + , mknode "p:nvPr" [] ()] + let blipFill = mknode "p:blipFill" [] + [ mknode "a:blip" [("r:embed", "rId" ++ (show $ mInfoLocalId mInfo))] () + , mknode "a:stretch" [] $ + mknode "a:fillRect" [] () ] + let xfrm = mknode "a:xfrm" [] + [ mknode "a:off" [("x",show xoff''), ("y",show yoff')] () + , mknode "a:ext" [("cx",show xemu) + ,("cy",show yemu)] () ] + let prstGeom = mknode "a:prstGeom" [("prst","rect")] $ + mknode "a:avLst" [] () + let ln = mknode "a:ln" [("w","9525")] + [ mknode "a:noFill" [] () + , mknode "a:headEnd" [] () + , mknode "a:tailEnd" [] () ] + let spPr = mknode "p:spPr" [("bwMode","auto")] + [xfrm, prstGeom, mknode "a:noFill" [] (), ln] + return $ + mknode "p:pic" [] + [ nvPicPr + , blipFill + , spPr ] + +-- Currently hardcoded, until I figure out how to make it dynamic. +blockQuoteSize :: Pixels +blockQuoteSize = 20 + +paraElemToElement :: PandocMonad m => ParaElem -> P m Element +paraElemToElement Break = return $ mknode "a:br" [] () +paraElemToElement (Run rpr s) = do + let attrs = + if rPropCode rpr + then [] + else (if rPropBlockQuote rpr then [("sz", (show $ blockQuoteSize * 100))] else []) ++ + (if rPropBold rpr then [("b", "1")] else []) ++ + (if rPropItalics rpr then [("i", "1")] else []) ++ + (case rStrikethrough rpr of + Just NoStrike -> [("strike", "noStrike")] + Just SingleStrike -> [("strike", "sngStrike")] + Just DoubleStrike -> [("strike", "dblStrike")] + Nothing -> []) ++ + (case rBaseline rpr of + Just n -> [("baseline", show n)] + Nothing -> []) ++ + (case rCap rpr of + Just NoCapitals -> [("cap", "none")] + Just SmallCapitals -> [("cap", "small")] + Just AllCapitals -> [("cap", "all")] + Nothing -> []) ++ + [] + linkProps <- case rLink rpr of + Just link -> do idNum <- registerLink link + return [mknode "a:hlinkClick" + [("r:id", "rId" ++ show idNum)] + () + ] + Nothing -> return [] + let propContents = if rPropCode rpr + then [mknode "a:latin" [("typeface", "Courier")] ()] + else linkProps + return $ mknode "a:r" [] [ mknode "a:rPr" attrs propContents + , mknode "a:t" [] s + ] +paraElemToElement (MathElem mathType texStr) = do + res <- convertMath writeOMML mathType (unTeXString texStr) + case res of + Right r -> return $ mknode "a14:m" [] $ addMathInfo r + Left (Str s) -> paraElemToElement (Run def s) + Left _ -> throwError $ PandocShouldNeverHappenError "non-string math fallback" + +-- This is a bit of a kludge -- really requires adding an option to +-- TeXMath, but since that's a different package, we'll do this one +-- step at a time. +addMathInfo :: Element -> Element +addMathInfo element = + let mathspace = Attr { attrKey = (QName "m" Nothing (Just "xmlns")) + , attrVal = "http://schemas.openxmlformats.org/officeDocument/2006/math" + } + in add_attr mathspace element + +-- We look through the element to see if it contains an a14:m +-- element. If so, we surround it. This is a bit ugly, but it seems +-- more dependable than looking through shapes for math. Plus this is +-- an xml implementation detail, so it seems to make sense to do it at +-- the xml level. +surroundWithMathAlternate :: Element -> Element +surroundWithMathAlternate element = + case findElement (QName "m" Nothing (Just "a14")) element of + Just _ -> + mknode "mc:AlternateContent" + [("xmlns:mc", "http://schemas.openxmlformats.org/markup-compatibility/2006") + ] [ mknode "mc:Choice" + [ ("xmlns:a14", "http://schemas.microsoft.com/office/drawing/2010/main") + , ("Requires", "a14")] [ element ] + ] + Nothing -> element + +paragraphToElement :: PandocMonad m => Paragraph -> P m Element +paragraphToElement par = do + let + attrs = [("lvl", show $ pPropLevel $ paraProps par)] ++ + (case pPropMarginLeft (paraProps par) of + Just px -> [("marL", show $ 12700 * px), ("indent", "0")] + Nothing -> [] + ) ++ + (case pPropAlign (paraProps par) of + Just AlgnLeft -> [("algn", "l")] + Just AlgnRight -> [("algn", "r")] + Just AlgnCenter -> [("algn", "ctr")] + Nothing -> [] + ) + props = [] ++ + (case pPropBullet $ paraProps par of + Just Bullet -> [] + Just (AutoNumbering attrs') -> + [mknode "a:buAutoNum" [("type", autoNumberingToType attrs')] ()] + Nothing -> [mknode "a:buNone" [] ()] + ) + paras <- mapM paraElemToElement (paraElems par) + return $ mknode "a:p" [] $ [mknode "a:pPr" attrs props] ++ paras + +shapeToElement :: PandocMonad m => Element -> Shape -> P m Element +shapeToElement layout (TextBox paras) + | ns <- elemToNameSpaces layout + , Just cSld <- findChild (elemName ns "p" "cSld") layout + , Just spTree <- findChild (elemName ns "p" "spTree") cSld + , Just sp <- getContentShape ns spTree = do + elements <- mapM paragraphToElement paras + let txBody = mknode "p:txBody" [] $ + [mknode "a:bodyPr" [] (), mknode "a:lstStyle" [] ()] ++ elements + emptySpPr = mknode "p:spPr" [] () + return $ + surroundWithMathAlternate $ + replaceNamedChildren ns "p" "txBody" [txBody] $ + replaceNamedChildren ns "p" "spPr" [emptySpPr] $ + sp + -- XXX: TODO + | otherwise = return $ mknode "p:sp" [] () +-- XXX: TODO +shapeToElement layout (Pic fp attr alt) = do + mInfo <- registerMedia fp alt + case mInfoExt mInfo of + Just _ -> makePicElement mInfo attr + Nothing -> shapeToElement layout $ TextBox [Paragraph def alt] +shapeToElement _ (GraphicFrame tbls _) = do + elements <- mapM graphicToElement tbls + return $ mknode "p:graphicFrame" [] $ + [ mknode "p:nvGraphicFramePr" [] $ + [ mknode "p:cNvPr" [("id", "6"), ("name", "Content Placeholder 5")] () + , mknode "p:cNvGraphicFramePr" [] $ + [mknode "a:graphicFrameLocks" [("noGrp", "1")] ()] + , mknode "p:nvPr" [] $ + [mknode "p:ph" [("idx", "1")] ()] + ] + , mknode "p:xfrm" [] $ + [ mknode "a:off" [("x", "457200"), ("y", "1600200")] () + , mknode "a:ext" [("cx", "8029388"), ("cy", "3644152")] () + ] + ] ++ elements + +shapeToElements :: PandocMonad m => Element -> Shape -> P m [Element] +shapeToElements layout shp = do + case shp of + (Pic _ _ alt) | (not . null) alt -> do + element <- shapeToElement layout shp + caption <- createCaption alt + return [element, caption] + (GraphicFrame _ cptn) | (not . null) cptn -> do + element <- shapeToElement layout shp + caption <- createCaption cptn + return [element, caption] + _ -> do + element <- shapeToElement layout shp + return [element] + +shapesToElements :: PandocMonad m => Element -> [Shape] -> P m [Element] +shapesToElements layout shps = do + concat <$> mapM (shapeToElements layout) shps + +hardcodedTableMargin :: Integer +hardcodedTableMargin = 36 + + +graphicToElement :: PandocMonad m => Graphic -> P m Element +graphicToElement (Tbl tblPr colWidths hdrCells rows) = do + let cellToOpenXML paras = do elements <- mapM paragraphToElement paras + return $ + [mknode "a:txBody" [] $ + ([ mknode "a:bodyPr" [] () + , mknode "a:lstStyle" [] ()] + ++ elements)] + headers' <- mapM cellToOpenXML hdrCells + rows' <- mapM (mapM cellToOpenXML) rows + let borderProps = mknode "a:tcPr" [] () + let emptyCell = [mknode "a:p" [] [mknode "a:pPr" [] ()]] + let mkcell border contents = mknode "a:tc" [] + $ (if null contents + then emptyCell + else contents) ++ [ borderProps | border ] + let mkrow border cells = mknode "a:tr" [("h", "0")] $ map (mkcell border) cells + -- let textwidth = 14400 -- 5.5 in in twips, 1/20 pt + -- let fullrow = 14400 -- 100% specified in pct + -- let rowwidth = fullrow * sum colWidths + + let mkgridcol w = mknode "a:gridCol" + [("w", show ((12700 * w) :: Integer))] () + let hasHeader = not (all null hdrCells) + return $ mknode "a:graphic" [] $ + [mknode "a:graphicData" [("uri", "http://schemas.openxmlformats.org/drawingml/2006/table")] $ + [mknode "a:tbl" [] $ + [ mknode "a:tblPr" [ ("firstRow", if tblPrFirstRow tblPr then "1" else "0") + , ("bandRow", if tblPrBandRow tblPr then "1" else "0") + ] () + , mknode "a:tblGrid" [] (if all (==0) colWidths + then [] + else map mkgridcol colWidths) + ] + ++ [ mkrow True headers' | hasHeader ] ++ map (mkrow False) rows' + ] + ] + +getShapeByName :: NameSpaces -> Element -> String -> Maybe Element +getShapeByName ns spTreeElem name + | isElem ns "p" "spTree" spTreeElem = + filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasName ns name e)) spTreeElem + | otherwise = Nothing + +nonBodyTextToElement :: PandocMonad m => Element -> String -> [ParaElem] -> P m Element +nonBodyTextToElement layout shapeName paraElements + | ns <- elemToNameSpaces layout + , Just cSld <- findChild (elemName ns "p" "cSld") layout + , Just spTree <- findChild (elemName ns "p" "spTree") cSld + , Just sp <- getShapeByName ns spTree shapeName = do + let hdrPara = Paragraph def paraElements + element <- paragraphToElement hdrPara + let txBody = mknode "p:txBody" [] $ + [mknode "a:bodyPr" [] (), mknode "a:lstStyle" [] ()] ++ + [element] + return $ replaceNamedChildren ns "p" "txBody" [txBody] sp + -- XXX: TODO + | otherwise = return $ mknode "p:sp" [] () + + +-- hdrToElement :: Element -> [ParaElem] -> Element +-- hdrToElement layout paraElems +-- | ns <- elemToNameSpaces layout +-- , Just cSld <- findChild (elemName ns "p" "cSld") layout +-- , Just spTree <- findChild (elemName ns "p" "spTree") cSld +-- , Just sp <- getContentTitleShape ns spTree = +-- let hdrPara = Paragraph def paraElems +-- txBody = mknode "p:txBody" [] $ +-- [mknode "a:bodyPr" [] (), mknode "a:lstStyle" [] ()] ++ +-- [paragraphToElement hdrPara] +-- in +-- replaceNamedChildren ns "p" "txBody" [txBody] sp +-- -- XXX: TODO +-- | otherwise = mknode "p:sp" [] () +-- -- XXX: TODO +-- hdrToElement _ _ = mknode "p:sp" [] () + +contentToElement :: PandocMonad m => Element -> [ParaElem] -> [Shape] -> P m Element +contentToElement layout hdrShape shapes + | ns <- elemToNameSpaces layout + , Just cSld <- findChild (elemName ns "p" "cSld") layout + , Just spTree <- findChild (elemName ns "p" "spTree") cSld = do + element <- nonBodyTextToElement layout "Title 1" hdrShape + let hdrShapeElements = if null hdrShape + then [] + else [element] + contentElements <- shapesToElements layout shapes + return $ + replaceNamedChildren ns "p" "sp" + (hdrShapeElements ++ contentElements) + spTree +contentToElement _ _ _ = return $ mknode "p:sp" [] () + +titleToElement :: PandocMonad m => Element -> [ParaElem] -> P m Element +titleToElement layout titleElems + | ns <- elemToNameSpaces layout + , Just cSld <- findChild (elemName ns "p" "cSld") layout + , Just spTree <- findChild (elemName ns "p" "spTree") cSld = do + element <- nonBodyTextToElement layout "Title 1" titleElems + let titleShapeElements = if null titleElems + then [] + else [element] + return $ replaceNamedChildren ns "p" "sp" titleShapeElements spTree +titleToElement _ _ = return $ mknode "p:sp" [] () + +metadataToElement :: PandocMonad m => Element -> [ParaElem] -> [ParaElem] -> [[ParaElem]] -> [ParaElem] -> P m Element +metadataToElement layout titleElems subtitleElems authorsElems dateElems + | ns <- elemToNameSpaces layout + , Just cSld <- findChild (elemName ns "p" "cSld") layout + , Just spTree <- findChild (elemName ns "p" "spTree") cSld = do + titleShapeElements <- if null titleElems + then return [] + else sequence [nonBodyTextToElement layout "Title 1" titleElems] + let combinedAuthorElems = intercalate [Break] authorsElems + subtitleAndAuthorElems = intercalate [Break, Break] [subtitleElems, combinedAuthorElems] + subtitleShapeElements <- if null subtitleAndAuthorElems + then return [] + else sequence [nonBodyTextToElement layout "Subtitle 2" subtitleAndAuthorElems] + dateShapeElements <- if null dateElems + then return [] + else sequence [nonBodyTextToElement layout "Date Placeholder 3" dateElems] + return $ replaceNamedChildren ns "p" "sp" + (titleShapeElements ++ subtitleShapeElements ++ dateShapeElements) + spTree +metadataToElement _ _ _ _ _ = return $ mknode "p:sp" [] () + +slideToElement :: PandocMonad m => Slide -> P m Element +slideToElement s@(ContentSlide hdrElems shapes) = do + layout <- getLayout s + spTree <- local (\env -> if null hdrElems + then env + else env{envSlideHasHeader=True}) $ + contentToElement layout hdrElems shapes + return $ mknode "p:sld" + [ ("xmlns:a", "http://schemas.openxmlformats.org/drawingml/2006/main"), + ("xmlns:r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"), + ("xmlns:p", "http://schemas.openxmlformats.org/presentationml/2006/main") + ] [mknode "p:cSld" [] [spTree]] +slideToElement s@(TitleSlide hdrElems) = do + layout <- getLayout s + spTree <- titleToElement layout hdrElems + return $ mknode "p:sld" + [ ("xmlns:a", "http://schemas.openxmlformats.org/drawingml/2006/main"), + ("xmlns:r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"), + ("xmlns:p", "http://schemas.openxmlformats.org/presentationml/2006/main") + ] [mknode "p:cSld" [] [spTree]] +slideToElement s@(MetadataSlide titleElems subtitleElems authorElems dateElems) = do + layout <- getLayout s + spTree <- metadataToElement layout titleElems subtitleElems authorElems dateElems + return $ mknode "p:sld" + [ ("xmlns:a", "http://schemas.openxmlformats.org/drawingml/2006/main"), + ("xmlns:r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"), + ("xmlns:p", "http://schemas.openxmlformats.org/presentationml/2006/main") + ] [mknode "p:cSld" [] [spTree]] + +----------------------------------------------------------------------- + +slideToFilePath :: Slide -> Int -> FilePath +slideToFilePath _ idNum = "slide" ++ (show $ idNum) ++ ".xml" + +slideToSlideId :: Monad m => Slide -> Int -> P m String +slideToSlideId _ idNum = do + n <- gets stSlideIdOffset + return $ "rId" ++ (show $ idNum + n) + + +data Relationship = Relationship { relId :: Int + , relType :: MimeType + , relTarget :: FilePath + } deriving (Show, Eq) + +elementToRel :: Element -> Maybe Relationship +elementToRel element + | elName element == QName "Relationship" (Just "http://schemas.openxmlformats.org/package/2006/relationships") Nothing = + do rId <- findAttr (QName "Id" Nothing Nothing) element + numStr <- stripPrefix "rId" rId + num <- case reads numStr :: [(Int, String)] of + (n, _) : _ -> Just n + [] -> Nothing + type' <- findAttr (QName "Type" Nothing Nothing) element + target <- findAttr (QName "Target" Nothing Nothing) element + return $ Relationship num type' target + | otherwise = Nothing + +slideToPresRel :: Monad m => Slide -> Int -> P m Relationship +slideToPresRel slide idNum = do + n <- gets stSlideIdOffset + let rId = idNum + n + fp = "slides/" ++ slideToFilePath slide idNum + return $ Relationship { relId = rId + , relType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" + , relTarget = fp + } + +getRels :: PandocMonad m => P m [Relationship] +getRels = do + refArchive <- asks envRefArchive + distArchive <- asks envDistArchive + relsElem <- parseXml refArchive distArchive "ppt/_rels/presentation.xml.rels" + let globalNS = "http://schemas.openxmlformats.org/package/2006/relationships" + let relElems = findChildren (QName "Relationship" (Just globalNS) Nothing) relsElem + return $ mapMaybe elementToRel relElems + +presentationToRels :: PandocMonad m => Presentation -> P m [Relationship] +presentationToRels (Presentation _ slides) = do + mySlideRels <- mapM (\(s, n) -> slideToPresRel s n) $ zip slides [1..] + rels <- getRels + let relsWithoutSlides = filter (\r -> relType r /= "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide") rels + -- We want to make room for the slides in the id space. The slides + -- will start at Id2 (since Id1 is for the slide master). There are + -- two slides in the data file, but that might change in the future, + -- so we will do this: + -- + -- 1. We look to see what the minimum relWithoutSlide id (greater than 1) is. + -- 2. We add the difference between this and the number of slides to + -- all relWithoutSlide rels (unless they're 1) + + let minRelNotOne = case filter (1<) $ map relId relsWithoutSlides of + [] -> 0 -- doesn't matter in this case, since + -- there will be nothing to map the + -- function over + l -> minimum l + + modifyRelNum :: Int -> Int + modifyRelNum 1 = 1 + modifyRelNum n = n - minRelNotOne + 2 + length slides + + relsWithoutSlides' = map (\r -> r{relId = modifyRelNum $ relId r}) relsWithoutSlides + + return $ mySlideRels ++ relsWithoutSlides' + +relToElement :: Relationship -> Element +relToElement rel = mknode "Relationship" [ ("Id", "rId" ++ (show $ relId rel)) + , ("Type", relType rel) + , ("Target", relTarget rel) ] () + +relsToElement :: [Relationship] -> Element +relsToElement rels = mknode "Relationships" + [("xmlns", "http://schemas.openxmlformats.org/package/2006/relationships")] + (map relToElement rels) + +presentationToRelsEntry :: PandocMonad m => Presentation -> P m Entry +presentationToRelsEntry pres = do + rels <- presentationToRels pres + elemToEntry "ppt/_rels/presentation.xml.rels" $ relsToElement rels + +elemToEntry :: PandocMonad m => FilePath -> Element -> P m Entry +elemToEntry fp element = do + epochtime <- (floor . utcTimeToPOSIXSeconds) <$> asks envUTCTime + return $ toEntry fp epochtime $ renderXml element + +slideToEntry :: PandocMonad m => Slide -> Int -> P m Entry +slideToEntry slide idNum = do + modify $ \st -> st{stCurSlideId = idNum} + element <- slideToElement slide + elemToEntry ("ppt/slides/" ++ slideToFilePath slide idNum) element + +slideToSlideRelEntry :: PandocMonad m => Slide -> Int -> P m Entry +slideToSlideRelEntry slide idNum = do + element <- slideToSlideRelElement slide idNum + elemToEntry ("ppt/slides/_rels/" ++ slideToFilePath slide idNum ++ ".rels") element + +linkRelElement :: Int -> (URL, String) -> Element +linkRelElement idNum (url, _) = + mknode "Relationship" [ ("Id", "rId" ++ show idNum) + , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink") + , ("Target", url) + , ("TargetMode", "External") + ] () + +linkRelElements :: M.Map Int (URL, String) -> [Element] +linkRelElements mp = map (\(n, lnk) -> linkRelElement n lnk) (M.toList mp) + +mediaRelElement :: MediaInfo -> Element +mediaRelElement mInfo = + let ext = case mInfoExt mInfo of + Just e -> e + Nothing -> "" + in + mknode "Relationship" [ ("Id", "rId" ++ (show $ mInfoLocalId mInfo)) + , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image") + , ("Target", "../media/image" ++ (show $ mInfoGlobalId mInfo) ++ ext) + ] () + +slideToSlideRelElement :: PandocMonad m => Slide -> Int -> P m Element +slideToSlideRelElement slide idNum = do + let target = case slide of + (MetadataSlide _ _ _ _) -> "../slideLayouts/slideLayout1.xml" + (TitleSlide _) -> "../slideLayouts/slideLayout3.xml" + (ContentSlide _ _) -> "../slideLayouts/slideLayout2.xml" + + linkIds <- gets stLinkIds + mediaIds <- gets stMediaIds + + let linkRels = case M.lookup idNum linkIds of + Just mp -> linkRelElements mp + Nothing -> [] + mediaRels = case M.lookup idNum mediaIds of + Just mInfos -> map mediaRelElement mInfos + Nothing -> [] + + return $ + mknode "Relationships" + [("xmlns", "http://schemas.openxmlformats.org/package/2006/relationships")] + ([mknode "Relationship" [ ("Id", "rId1") + , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout") + , ("Target", target)] () + ] ++ linkRels ++ mediaRels) + +-- slideToSlideRelEntry :: PandocMonad m => Slide -> Int -> P m Entry +-- slideToSlideRelEntry slide idNum = do +-- let fp = "ppt/slides/_rels/slide" ++ (show idNum) ++ ".xml.rels" +-- elemToEntry fp $ slideToSlideRelElement slide + +slideToSldIdElement :: PandocMonad m => Slide -> Int -> P m Element +slideToSldIdElement slide idNum = do + let id' = show $ idNum + 255 + rId <- slideToSlideId slide idNum + return $ mknode "p:sldId" [("id", id'), ("r:id", rId)] () + +presentationToSldIdLst :: PandocMonad m => Presentation -> P m Element +presentationToSldIdLst (Presentation _ slides) = do + ids <- mapM (\(s,n) -> slideToSldIdElement s n) (zip slides [1..]) + return $ mknode "p:sldIdLst" [] ids + +presentationToPresentationElement :: PandocMonad m => Presentation -> P m Element +presentationToPresentationElement pres = do + refArchive <- asks envRefArchive + distArchive <- asks envDistArchive + element <- parseXml refArchive distArchive "ppt/presentation.xml" + sldIdLst <- presentationToSldIdLst pres + + let modifySldIdLst :: Content -> Content + modifySldIdLst (Elem e) = case elName e of + (QName "sldIdLst" _ _) -> Elem sldIdLst + _ -> Elem e + modifySldIdLst ct = ct + + newContent = map modifySldIdLst $ elContent element + + return $ element{elContent = newContent} + +presentationToPresEntry :: PandocMonad m => Presentation -> P m Entry +presentationToPresEntry pres = presentationToPresentationElement pres >>= + elemToEntry "ppt/presentation.xml" + + + + +defaultContentTypeToElem :: DefaultContentType -> Element +defaultContentTypeToElem dct = + mknode "Default" + [("Extension", defContentTypesExt dct), + ("ContentType", defContentTypesType dct)] + () + +overrideContentTypeToElem :: OverrideContentType -> Element +overrideContentTypeToElem oct = + mknode "Override" + [("PartName", overrideContentTypesPart oct), + ("ContentType", overrideContentTypesType oct)] + () + +contentTypesToElement :: ContentTypes -> Element +contentTypesToElement ct = + let ns = "http://schemas.openxmlformats.org/package/2006/content-types" + in + mknode "Types" [("xmlns", ns)] $ + (map defaultContentTypeToElem $ contentTypesDefaults ct) ++ + (map overrideContentTypeToElem $ contentTypesOverrides ct) + +data DefaultContentType = DefaultContentType + { defContentTypesExt :: String + , defContentTypesType:: MimeType + } + deriving (Show, Eq) + +data OverrideContentType = OverrideContentType + { overrideContentTypesPart :: FilePath + , overrideContentTypesType :: MimeType + } + deriving (Show, Eq) + +data ContentTypes = ContentTypes { contentTypesDefaults :: [DefaultContentType] + , contentTypesOverrides :: [OverrideContentType] + } + deriving (Show, Eq) + +contentTypesToEntry :: PandocMonad m => ContentTypes -> P m Entry +contentTypesToEntry ct = elemToEntry "[Content_Types].xml" $ contentTypesToElement ct + +pathToOverride :: FilePath -> Maybe OverrideContentType +pathToOverride fp = OverrideContentType ("/" ++ fp) <$> (getContentType fp) + +mediaContentType :: MediaInfo -> Maybe DefaultContentType +mediaContentType mInfo + | Just ('.' : ext) <- mInfoExt mInfo = + Just $ DefaultContentType { defContentTypesExt = ext + , defContentTypesType = + case mInfoMimeType mInfo of + Just mt -> mt + Nothing -> "application/octet-stream" + } + | otherwise = Nothing + +presentationToContentTypes :: PandocMonad m => Presentation -> P m ContentTypes +presentationToContentTypes (Presentation _ slides) = do + mediaInfos <- (mconcat . M.elems) <$> gets stMediaIds + let defaults = [ DefaultContentType "xml" "application/xml" + , DefaultContentType "rels" "application/vnd.openxmlformats-package.relationships+xml" + ] + mediaDefaults = nub $ mapMaybe mediaContentType mediaInfos + inheritedOverrides = mapMaybe pathToOverride inheritedFiles + presOverride = mapMaybe pathToOverride ["ppt/presentation.xml"] + slideOverrides = + mapMaybe + (\(s, n) -> + pathToOverride $ "ppt/slides/" ++ slideToFilePath s n) + (zip slides [1..]) + -- propOverride = mapMaybe pathToOverride ["docProps/core.xml"] + return $ ContentTypes + (defaults ++ mediaDefaults) + (inheritedOverrides ++ presOverride ++ slideOverrides) + +-- slideToElement :: Element -> Slide -> Element +-- slideToElement layout (ContentSlide _ shapes) = +-- let sps = map (shapeToElement layout) shapes + +presML :: String +presML = "application/vnd.openxmlformats-officedocument.presentationml" + +noPresML :: String +noPresML = "application/vnd.openxmlformats-officedocument" + +getContentType :: FilePath -> Maybe MimeType +getContentType fp + | fp == "ppt/presentation.xml" = Just $ presML ++ ".presentation.main+xml" + | fp == "ppt/presProps.xml" = Just $ presML ++ ".presProps+xml" + | fp == "ppt/viewProps.xml" = Just $ presML ++ ".viewProps+xml" + | fp == "ppt/tableStyles.xml" = Just $ presML ++ ".tableStyles+xml" + | fp == "docProps/core.xml" = Just $ "application/vnd.openxmlformats-package.core-properties+xml" + | fp == "docProps/app.xml" = Just $ noPresML ++ ".extended-properties+xml" + | "ppt" : "slideMasters" : f : [] <- splitDirectories fp + , (_, ".xml") <- splitExtension f = + Just $ presML ++ ".slideMaster+xml" + | "ppt" : "slides" : f : [] <- splitDirectories fp + , (_, ".xml") <- splitExtension f = + Just $ presML ++ ".slide+xml" + | "ppt" : "notesMasters" : f : [] <- splitDirectories fp + , (_, ".xml") <- splitExtension f = + Just $ presML ++ ".notesMaster+xml" + | "ppt" : "notesSlides" : f : [] <- splitDirectories fp + , (_, ".xml") <- splitExtension f = + Just $ presML ++ ".notesSlide+xml" + | "ppt" : "theme" : f : [] <- splitDirectories fp + , (_, ".xml") <- splitExtension f = + Just $ noPresML ++ ".theme+xml" + -- | "ppt" : "slideLayouts" : f : [] <- splitDirectories fp + -- , (_, ".xml") <- splitExtension f = + | "ppt" : "slideLayouts" : _ : [] <- splitDirectories fp= + Just $ presML ++ ".slideLayout+xml" + | otherwise = Nothing -- cgit v1.2.3 From b212a51062c1ea2ab6aa6c5fb9f777ef2e1f29aa Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sun, 10 Dec 2017 11:05:21 -0500 Subject: Integrate Powerpoint writer into pandoc. --- src/Text/Pandoc/App.hs | 1 + src/Text/Pandoc/Data.hs | 1 + src/Text/Pandoc/Writers.hs | 3 +++ 3 files changed, 5 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index ae62db4f9..3dd4f214c 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -815,6 +815,7 @@ defaultWriterName x = ".tei" -> "tei" ".ms" -> "ms" ".roff" -> "ms" + ".pptx" -> "pptx" ['.',y] | y `elem` ['1'..'9'] -> "man" _ -> "html" diff --git a/src/Text/Pandoc/Data.hs b/src/Text/Pandoc/Data.hs index 6bb6069ca..332882c22 100644 --- a/src/Text/Pandoc/Data.hs +++ b/src/Text/Pandoc/Data.hs @@ -18,4 +18,5 @@ dataFiles' = ("MANUAL.txt", $(embedFile "MANUAL.txt")) : -- handle the hidden file separately, since embedDir doesn't -- include it: ("docx/_rels/.rels", $(embedFile "data/docx/_rels/.rels")) : + ("pptx/_rels/.rels", $(embedFile "data/pptx/_rels/.rels")) : $(embedDir "data") diff --git a/src/Text/Pandoc/Writers.hs b/src/Text/Pandoc/Writers.hs index 046022b09..b336c1f1a 100644 --- a/src/Text/Pandoc/Writers.hs +++ b/src/Text/Pandoc/Writers.hs @@ -68,6 +68,7 @@ module Text.Pandoc.Writers , writeOpenDocument , writeOrg , writePlain + , writePowerpoint , writeRST , writeRTF , writeRevealJs @@ -113,6 +114,7 @@ import Text.Pandoc.Writers.ODT import Text.Pandoc.Writers.OpenDocument import Text.Pandoc.Writers.OPML import Text.Pandoc.Writers.Org +import Text.Pandoc.Writers.Powerpoint import Text.Pandoc.Writers.RST import Text.Pandoc.Writers.RTF import Text.Pandoc.Writers.TEI @@ -131,6 +133,7 @@ writers = [ ,("json" , TextWriter $ \o d -> return $ writeJSON o d) ,("docx" , ByteStringWriter writeDocx) ,("odt" , ByteStringWriter writeODT) + ,("pptx" , ByteStringWriter writePowerpoint) ,("epub" , ByteStringWriter writeEPUB3) ,("epub2" , ByteStringWriter writeEPUB2) ,("epub3" , ByteStringWriter writeEPUB3) -- cgit v1.2.3 From 8fd51e12efba25d279e447bced9517984462059c Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sun, 10 Dec 2017 11:12:38 -0500 Subject: Remove redundant imports from Docx writer. These were a result of moving functions to the OOXML module. --- src/Text/Pandoc/Writers/Docx.hs | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 538efa3a6..94529dad4 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -40,12 +40,10 @@ import Control.Monad.Reader import Control.Monad.State.Strict import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL -import qualified Data.ByteString.Lazy.Char8 as BL8 import Data.Char (isSpace, ord, toLower) import Data.List (intercalate, isPrefixOf, isSuffixOf) import qualified Data.Map as M import Data.Maybe (fromMaybe, isNothing, mapMaybe, maybeToList) -import Data.Monoid ((<>)) import qualified Data.Set as Set import qualified Data.Text as T import Data.Time.Clock.POSIX @@ -65,7 +63,6 @@ import Text.Pandoc.MIME (MimeType, extensionFromMimeType, getMimeType, import Text.Pandoc.Options import Text.Pandoc.Readers.Docx.StyleMap import Text.Pandoc.Shared hiding (Element) -import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Walk import Text.Pandoc.Writers.Math import Text.Pandoc.Writers.Shared (fixDisplayMath) -- cgit v1.2.3 From be6b43b14cbda3140ed05fcbaff0d3839cf18e48 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sun, 10 Dec 2017 15:48:02 -0500 Subject: Fix comment that confused compiler. --- src/Text/Pandoc/Writers/Powerpoint.hs | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index f7d5bbc5f..d78833c81 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -1658,8 +1658,6 @@ getContentType fp | "ppt" : "theme" : f : [] <- splitDirectories fp , (_, ".xml") <- splitExtension f = Just $ noPresML ++ ".theme+xml" - -- | "ppt" : "slideLayouts" : f : [] <- splitDirectories fp - -- , (_, ".xml") <- splitExtension f = | "ppt" : "slideLayouts" : _ : [] <- splitDirectories fp= Just $ presML ++ ".slideLayout+xml" | otherwise = Nothing -- cgit v1.2.3 From 51c0ae5a66b9cf58632cd9d8eb324802f60bd6e2 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 11 Dec 2017 07:54:33 -0800 Subject: `--pdf-engine-opt`: fix bug where option order was reversed. Closes #4137. --- src/Text/Pandoc/App.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 3dd4f214c..f7d6450cc 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -1405,7 +1405,7 @@ options = (ReqArg (\arg opt -> do let oldArgs = optPdfEngineArgs opt - return opt { optPdfEngineArgs = arg : oldArgs }) + return opt { optPdfEngineArgs = oldArgs ++ [arg]}) "STRING") "" -- "Flags to pass to the PDF-engine, all instances of this option are accumulated and used" -- cgit v1.2.3 From ec1693505c65ef5dfca1df09d415f852a2787c15 Mon Sep 17 00:00:00 2001 From: Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com> Date: Wed, 13 Dec 2017 12:06:22 +1300 Subject: fig, table-wrap & caption Divs for JATS writer Support writing <fig> and <table-wrap> elements with <title> and <caption> inside them by using Divs with class set to on of fig, table-wrap or cation. The title is included as a Heading so the constraint on where Heading can occur is also relaxed. Also leaves out empty alt attributes on links. --- src/Text/Pandoc/Writers/JATS.hs | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs index 0ac37efba..fe5a36d13 100644 --- a/src/Text/Pandoc/Writers/JATS.hs +++ b/src/Text/Pandoc/Writers/JATS.hs @@ -168,6 +168,13 @@ blockToJATS opts (Div ('r':'e':'f':'-':_,_,_) [Para lst]) = blockToJATS opts (Div ("refs",_,_) xs) = do contents <- blocksToJATS opts xs return $ inTagsIndented "ref-list" contents +blockToJATS opts (Div (ident,[cls],kvs) bs) | cls `elem` ["fig", "caption", "table-wrap"] = do + contents <- blocksToJATS opts bs + let attr = [("id", ident) | not (null ident)] ++ + [("xml:lang",l) | ("lang",l) <- kvs] ++ + [(k,v) | (k,v) <- kvs, k `elem` ["specific-use", + "content-type", "orientation", "position"]] + return $ inTags True cls attr contents blockToJATS opts (Div (ident,_,kvs) bs) = do contents <- blocksToJATS opts bs let attr = [("id", ident) | not (null ident)] ++ @@ -175,10 +182,9 @@ blockToJATS opts (Div (ident,_,kvs) bs) = do [(k,v) | (k,v) <- kvs, k `elem` ["specific-use", "content-type", "orientation", "position"]] return $ inTags True "boxed-text" attr contents -blockToJATS _ h@(Header{}) = do - -- should not occur after hierarchicalize, except inside lists/blockquotes - report $ BlockNotRendered h - return empty +blockToJATS opts (Header _ _ title) = do + title' <- inlinesToJATS opts title + return $ inTagsSimple "title" title' -- No Plain, everything needs to be in a block-level tag blockToJATS opts (Plain lst) = blockToJATS opts (Para lst) -- title beginning with fig: indicates that the image is a figure @@ -204,6 +210,24 @@ blockToJATS opts (Para [Image (ident,_,kvs) txt ("xlink:title",tit)] return $ inTags True "fig" attr $ capt $$ selfClosingTag "graphic" graphicattr +blockToJATS _ (Para [Image (ident,_,kvs) _ (src, tit)]) = do + let mbMT = getMimeType src + let maintype = fromMaybe "image" $ + lookup "mimetype" kvs `mplus` + (takeWhile (/='/') <$> mbMT) + let subtype = fromMaybe "" $ + lookup "mime-subtype" kvs `mplus` + ((drop 1 . dropWhile (/='/')) <$> mbMT) + let attr = [("id", ident) | not (null ident)] ++ + [("mimetype", maintype), + ("mime-subtype", subtype), + ("xlink:href", src)] ++ + [("xlink:title", tit) | not (null tit)] ++ + [(k,v) | (k,v) <- kvs, k `elem` ["baseline-shift", + "content-type", "specific-use", "xlink:actuate", + "xlink:href", "xlink:role", "xlink:show", + "xlink:type"]] + return $ selfClosingTag "graphic" attr blockToJATS opts (Para lst) = inTagsIndented "p" <$> inlinesToJATS opts lst blockToJATS opts (LineBlock lns) = @@ -379,8 +403,8 @@ inlineToJATS _ (Link _attr [Str t] ('m':'a':'i':'l':'t':'o':':':email, _)) return $ inTagsSimple "email" $ text (escapeStringForXML email) inlineToJATS opts (Link (ident,_,kvs) txt ('#':src, _)) = do let attr = [("id", ident) | not (null ident)] ++ - [("alt", stringify txt), - ("rid", src)] ++ + [("alt", stringify txt) | not (null txt)] ++ + [("rid", src)] ++ [(k,v) | (k,v) <- kvs, k `elem` ["ref-type", "specific-use"]] contents <- inlinesToJATS opts txt return $ inTags False "xref" attr contents -- cgit v1.2.3 From e86c337356fc723e366e5f0d6209205bcffe88f3 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 12 Dec 2017 15:16:16 -0800 Subject: Pipe tables: use full text width for tables with wrapping cells. Previously we computed the column sizes based on the ratio between the header lines and the text width (as set by `--columns`). This meant that tables with very short header lines would be very narrow. With this change, pipe tables with wrapping cells will always take up the whole text width. The relative column widths will still be determined by the ratio of header lines, but they will be normalized to add up to 1.0. --- src/Text/Pandoc/Readers/Markdown.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 2d7c12e99..b97a724a4 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -1353,8 +1353,8 @@ pipeTable = try $ do numColumns <- getOption readerColumns let widths = if maxlength > numColumns then map (\len -> - fromIntegral (len + 1) / fromIntegral numColumns) - seplengths + fromIntegral (len + 1) / fromIntegral (sum seplengths)) + seplengths else replicate (length aligns) 0.0 return (aligns, widths, heads', sequence lines'') -- cgit v1.2.3 From 7093a3b44cbd0b962c53a6e00db51d5de9a840f9 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 12 Dec 2017 15:36:29 -0800 Subject: Markdown: Improved computation of relative cell widths in pipe tables. --- src/Text/Pandoc/Readers/Markdown.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index b97a724a4..68f810abe 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -1353,7 +1353,7 @@ pipeTable = try $ do numColumns <- getOption readerColumns let widths = if maxlength > numColumns then map (\len -> - fromIntegral (len + 1) / fromIntegral (sum seplengths)) + fromIntegral len / fromIntegral (sum seplengths)) seplengths else replicate (length aligns) 0.0 return (aligns, widths, heads', sequence lines'') -- cgit v1.2.3 From d9cdce4281254dce803e2fe21393eb3d40e2f875 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 13 Dec 2017 10:20:57 -0800 Subject: Markdown reader: always use four space rule for example lists. It would be awkward to indent example list contents to the first non-space character after the label, since example list labels are often long. Thanks to Bernhard Fisseni for the suggestion. --- src/Text/Pandoc/Readers/Markdown.hs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 68f810abe..3b6dcbcb9 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -863,14 +863,16 @@ listLineCommon = concat <$> manyTill -- parse raw text for one list item, excluding start marker and continuations rawListItem :: PandocMonad m - => MarkdownParser m a + => Bool -- four space rule + -> MarkdownParser m a -> MarkdownParser m (String, Int) -rawListItem start = try $ do +rawListItem fourSpaceRule start = try $ do pos1 <- getPosition start pos2 <- getPosition - continuationIndent <- (4 <$ guardEnabled Ext_four_space_rule) - <|> return (sourceColumn pos2 - sourceColumn pos1) + let continuationIndent = if fourSpaceRule + then 4 + else (sourceColumn pos2 - sourceColumn pos1) first <- listLineCommon rest <- many (do notFollowedBy listStart notFollowedBy (() <$ codeBlockFenced) @@ -914,10 +916,11 @@ notFollowedByHtmlCloser = do Nothing -> return () listItem :: PandocMonad m - => MarkdownParser m a + => Bool -- four-space rule + -> MarkdownParser m a -> MarkdownParser m (F Blocks) -listItem start = try $ do - (first, continuationIndent) <- rawListItem start +listItem fourSpaceRule start = try $ do + (first, continuationIndent) <- rawListItem fourSpaceRule start continuations <- many (listContinuation continuationIndent) -- parsing with ListItemState forces markers at beginning of lines to -- count as list item markers, even if not separated by blank space. @@ -938,14 +941,18 @@ orderedList = try $ do delim `elem` [DefaultDelim, Period]) $ guardEnabled Ext_fancy_lists when (style == Example) $ guardEnabled Ext_example_lists - items <- fmap sequence $ many1 $ listItem + fourSpaceRule <- (True <$ guardEnabled Ext_four_space_rule) + <|> return (style == Example) + items <- fmap sequence $ many1 $ listItem fourSpaceRule (orderedListStart (Just (style, delim))) start' <- (start <$ guardEnabled Ext_startnum) <|> return 1 return $ B.orderedListWith (start', style, delim) <$> fmap compactify items bulletList :: PandocMonad m => MarkdownParser m (F Blocks) bulletList = do - items <- fmap sequence $ many1 $ listItem bulletListStart + fourSpaceRule <- (True <$ guardEnabled Ext_four_space_rule) + <|> return False + items <- fmap sequence $ many1 $ listItem fourSpaceRule bulletListStart return $ B.bulletList <$> fmap compactify items -- definition lists -- cgit v1.2.3 From 0abb9bdc546d8a675bdfae95f0c402b79db19df5 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Tue, 12 Dec 2017 07:35:41 +0100 Subject: Custom writer: define instances for newtype wrapper The custom writer used its own `ToLuaStack` instance definitions, which made it difficult to share code with Lua filters, as this could result in conflicting instances. A `Stringify` wrapper is introduced to avoid this problem. --- src/Text/Pandoc/Writers/Custom.hs | 126 +++++++++++++++++--------------------- 1 file changed, 57 insertions(+), 69 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Custom.hs b/src/Text/Pandoc/Writers/Custom.hs index 87b97dcee..ffe637966 100644 --- a/src/Text/Pandoc/Writers/Custom.hs +++ b/src/Text/Pandoc/Writers/Custom.hs @@ -1,11 +1,5 @@ -{-# OPTIONS_GHC -fno-warn-orphans #-} -{-# LANGUAGE CPP #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE FlexibleInstances #-} -#if MIN_VERSION_base(4,8,0) -#else -{-# LANGUAGE OverlappingInstances #-} -#endif {- Copyright (C) 2012-2017 John MacFarlane <jgm@berkeley.edu> This program is free software; you can redistribute it and/or modify @@ -36,6 +30,7 @@ Conversion of 'Pandoc' documents to custom markup using a lua writer. -} module Text.Pandoc.Writers.Custom ( writeCustom ) where +import Control.Arrow ((***)) import Control.Exception import Control.Monad (when) import Data.Char (toLower) @@ -48,6 +43,7 @@ import Foreign.Lua.Api import GHC.IO.Encoding (getForeignEncoding, setForeignEncoding, utf8) import Text.Pandoc.Definition import Text.Pandoc.Error +import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Lua.Util (addValue) import Text.Pandoc.Options import Text.Pandoc.Templates @@ -60,43 +56,31 @@ attrToMap (id',classes,keyvals) = M.fromList : ("class", unwords classes) : keyvals -instance ToLuaStack Double where - push = push . (realToFrac :: Double -> LuaNumber) - -instance ToLuaStack Int where - push = push . (fromIntegral :: Int -> LuaInteger) - -instance ToLuaStack Format where - push (Format f) = push (map toLower f) - -#if MIN_VERSION_base(4,8,0) -instance {-# OVERLAPS #-} ToLuaStack [Inline] where -#else -instance ToLuaStack [Inline] where -#endif - push ils = push =<< inlineListToCustom ils - -#if MIN_VERSION_base(4,8,0) -instance {-# OVERLAPS #-} ToLuaStack [Block] where -#else -instance ToLuaStack [Block] where -#endif - push ils = push =<< blockListToCustom ils - -instance ToLuaStack MetaValue where - push (MetaMap m) = push m - push (MetaList xs) = push xs - push (MetaBool x) = push x - push (MetaString s) = push s - push (MetaInlines ils) = push ils - push (MetaBlocks bs) = push bs - -instance ToLuaStack Citation where - push cit = do +newtype Stringify a = Stringify a + +instance ToLuaStack (Stringify Format) where + push (Stringify (Format f)) = push (map toLower f) + +instance ToLuaStack (Stringify [Inline]) where + push (Stringify ils) = push =<< inlineListToCustom ils + +instance ToLuaStack (Stringify [Block]) where + push (Stringify blks) = push =<< blockListToCustom blks + +instance ToLuaStack (Stringify MetaValue) where + push (Stringify (MetaMap m)) = push (fmap Stringify m) + push (Stringify (MetaList xs)) = push (map Stringify xs) + push (Stringify (MetaBool x)) = push x + push (Stringify (MetaString s)) = push s + push (Stringify (MetaInlines ils)) = push (Stringify ils) + push (Stringify (MetaBlocks bs)) = push (Stringify bs) + +instance ToLuaStack (Stringify Citation) where + push (Stringify cit) = do createtable 6 0 addValue "citationId" $ citationId cit - addValue "citationPrefix" $ citationPrefix cit - addValue "citationSuffix" $ citationSuffix cit + addValue "citationPrefix" . Stringify $ citationPrefix cit + addValue "citationSuffix" . Stringify $ citationSuffix cit addValue "citationMode" $ show (citationMode cit) addValue "citationNoteNum" $ citationNoteNum cit addValue "citationHash" $ citationHash cit @@ -138,7 +122,7 @@ writeCustom luaFile opts doc@(Pandoc meta _) = do docToCustom :: WriterOptions -> Pandoc -> Lua String docToCustom opts (Pandoc (Meta metamap) blocks) = do body <- blockListToCustom blocks - callFunc "Doc" body metamap (writerVariables opts) + callFunc "Doc" body (fmap Stringify metamap) (writerVariables opts) -- | Convert Pandoc block element to Custom. blockToCustom :: Block -- ^ Block element @@ -146,41 +130,45 @@ blockToCustom :: Block -- ^ Block element blockToCustom Null = return "" -blockToCustom (Plain inlines) = callFunc "Plain" inlines +blockToCustom (Plain inlines) = callFunc "Plain" (Stringify inlines) blockToCustom (Para [Image attr txt (src,tit)]) = - callFunc "CaptionedImage" src tit txt (attrToMap attr) + callFunc "CaptionedImage" src tit (Stringify txt) (attrToMap attr) -blockToCustom (Para inlines) = callFunc "Para" inlines +blockToCustom (Para inlines) = callFunc "Para" (Stringify inlines) -blockToCustom (LineBlock linesList) = callFunc "LineBlock" linesList +blockToCustom (LineBlock linesList) = callFunc "LineBlock" (map Stringify linesList) blockToCustom (RawBlock format str) = - callFunc "RawBlock" format str + callFunc "RawBlock" (Stringify format) str blockToCustom HorizontalRule = callFunc "HorizontalRule" blockToCustom (Header level attr inlines) = - callFunc "Header" level inlines (attrToMap attr) + callFunc "Header" level (Stringify inlines) (attrToMap attr) blockToCustom (CodeBlock attr str) = callFunc "CodeBlock" str (attrToMap attr) -blockToCustom (BlockQuote blocks) = callFunc "BlockQuote" blocks +blockToCustom (BlockQuote blocks) = callFunc "BlockQuote" (Stringify blocks) -blockToCustom (Table capt aligns widths headers rows') = - callFunc "Table" capt (map show aligns) widths headers rows' +blockToCustom (Table capt aligns widths headers rows) = + let aligns' = map show aligns + capt' = Stringify capt + headers' = map Stringify headers + rows' = map (map Stringify) rows + in callFunc "Table" capt' aligns' widths headers' rows' -blockToCustom (BulletList items) = callFunc "BulletList" items +blockToCustom (BulletList items) = callFunc "BulletList" (map Stringify items) blockToCustom (OrderedList (num,sty,delim) items) = - callFunc "OrderedList" items num (show sty) (show delim) + callFunc "OrderedList" (map Stringify items) num (show sty) (show delim) blockToCustom (DefinitionList items) = - callFunc "DefinitionList" items + callFunc "DefinitionList" (map (Stringify *** map Stringify) items) blockToCustom (Div attr items) = - callFunc "Div" items (attrToMap attr) + callFunc "Div" (Stringify items) (attrToMap attr) -- | Convert list of Pandoc block elements to Custom. blockListToCustom :: [Block] -- ^ List of block elements @@ -205,23 +193,23 @@ inlineToCustom Space = callFunc "Space" inlineToCustom SoftBreak = callFunc "SoftBreak" -inlineToCustom (Emph lst) = callFunc "Emph" lst +inlineToCustom (Emph lst) = callFunc "Emph" (Stringify lst) -inlineToCustom (Strong lst) = callFunc "Strong" lst +inlineToCustom (Strong lst) = callFunc "Strong" (Stringify lst) -inlineToCustom (Strikeout lst) = callFunc "Strikeout" lst +inlineToCustom (Strikeout lst) = callFunc "Strikeout" (Stringify lst) -inlineToCustom (Superscript lst) = callFunc "Superscript" lst +inlineToCustom (Superscript lst) = callFunc "Superscript" (Stringify lst) -inlineToCustom (Subscript lst) = callFunc "Subscript" lst +inlineToCustom (Subscript lst) = callFunc "Subscript" (Stringify lst) -inlineToCustom (SmallCaps lst) = callFunc "SmallCaps" lst +inlineToCustom (SmallCaps lst) = callFunc "SmallCaps" (Stringify lst) -inlineToCustom (Quoted SingleQuote lst) = callFunc "SingleQuoted" lst +inlineToCustom (Quoted SingleQuote lst) = callFunc "SingleQuoted" (Stringify lst) -inlineToCustom (Quoted DoubleQuote lst) = callFunc "DoubleQuoted" lst +inlineToCustom (Quoted DoubleQuote lst) = callFunc "DoubleQuoted" (Stringify lst) -inlineToCustom (Cite cs lst) = callFunc "Cite" lst cs +inlineToCustom (Cite cs lst) = callFunc "Cite" (Stringify lst) (map Stringify cs) inlineToCustom (Code attr str) = callFunc "Code" str (attrToMap attr) @@ -233,17 +221,17 @@ inlineToCustom (Math InlineMath str) = callFunc "InlineMath" str inlineToCustom (RawInline format str) = - callFunc "RawInline" format str + callFunc "RawInline" (Stringify format) str inlineToCustom LineBreak = callFunc "LineBreak" inlineToCustom (Link attr txt (src,tit)) = - callFunc "Link" txt src tit (attrToMap attr) + callFunc "Link" (Stringify txt) src tit (attrToMap attr) inlineToCustom (Image attr alt (src,tit)) = - callFunc "Image" alt src tit (attrToMap attr) + callFunc "Image" (Stringify alt) src tit (attrToMap attr) -inlineToCustom (Note contents) = callFunc "Note" contents +inlineToCustom (Note contents) = callFunc "Note" (Stringify contents) inlineToCustom (Span attr items) = - callFunc "Span" items (attrToMap attr) + callFunc "Span" (Stringify items) (attrToMap attr) -- cgit v1.2.3 From f9d0e1c89cf8deca97a005d8cd6d2d601e422e24 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Tue, 12 Dec 2017 08:58:47 +0100 Subject: Lua filters: drop unused code, language extensions --- src/Text/Pandoc/Lua.hs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 7132ad718..696f4de44 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -1,9 +1,3 @@ -{-# LANGUAGE CPP #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# OPTIONS_GHC -fno-warn-orphans #-} {- Copyright © 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> @@ -29,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> Stability : alpha -Pandoc lua utils. +Running pandoc Lua filters. -} module Text.Pandoc.Lua ( LuaException (..) @@ -41,7 +35,6 @@ module Text.Pandoc.Lua ) where import Control.Monad (when, (>=>)) -import Control.Monad.Identity (Identity) import Control.Monad.Trans (MonadIO (..)) import Data.IORef (newIORef, readIORef) import Foreign.Lua (FromLuaStack (peek), Lua, LuaException (..), @@ -117,6 +110,3 @@ pushGlobalFilter = do runAll :: [LuaFilter] -> Pandoc -> Lua Pandoc runAll = foldr ((>=>) . walkMWithLuaFilter) return - -instance (FromLuaStack a) => FromLuaStack (Identity a) where - peek = fmap return . peek -- cgit v1.2.3 From 4c64af4407776e6ceb2fcc8a803b83568b4c1964 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Wed, 13 Dec 2017 21:15:41 +0100 Subject: Custom writer: use init file to setup Lua interpreter The same init file (`data/init`) that is used to setup the Lua interpreter for Lua filters is also used to setup the interpreter of custom writers.lua. --- src/Text/Pandoc/App.hs | 4 +- src/Text/Pandoc/Lua.hs | 55 +++++++-------------------- src/Text/Pandoc/Lua/Init.hs | 79 +++++++++++++++++++++++++++++++++++++++ src/Text/Pandoc/Writers/Custom.hs | 23 ++++++------ 4 files changed, 106 insertions(+), 55 deletions(-) create mode 100644 src/Text/Pandoc/Lua/Init.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index f7d6450cc..e70b606a9 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -223,7 +223,7 @@ convertWithOpts opts = do if ".lua" `isSuffixOf` format -- note: use non-lowercased version writerName then return (TextWriter - (\o d -> liftIO $ writeCustom writerName o d) + (\o d -> writeCustom writerName o d) :: Writer PandocIO, mempty) else case getWriter writerName of Left e -> E.throwIO $ PandocAppError $ @@ -846,7 +846,7 @@ applyLuaFilters :: Maybe FilePath -> [FilePath] -> String -> Pandoc applyLuaFilters mbDatadir filters format d = do expandedFilters <- mapM (expandFilterPath mbDatadir) filters let go f d' = do - res <- runLuaFilter mbDatadir f format d' + res <- runLuaFilter f format d' case res of Right x -> return x Left (LuaException s) -> E.throw (PandocFilterError f s) diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 696f4de44..a56e89511 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -27,43 +27,32 @@ Running pandoc Lua filters. -} module Text.Pandoc.Lua ( LuaException (..) - , LuaPackageParams (..) - , pushPandocModule , runLuaFilter - , initLuaState - , luaPackageParams + , runPandocLua + , pushPandocModule ) where import Control.Monad (when, (>=>)) -import Control.Monad.Trans (MonadIO (..)) -import Data.IORef (newIORef, readIORef) import Foreign.Lua (FromLuaStack (peek), Lua, LuaException (..), Status (OK), ToLuaStack (push)) -import Text.Pandoc.Class (PandocIO, getCommonState, getMediaBag, setMediaBag) -import Text.Pandoc.Definition +import Text.Pandoc.Class (PandocIO) +import Text.Pandoc.Definition (Pandoc) import Text.Pandoc.Lua.Filter (LuaFilter, walkMWithLuaFilter) -import Text.Pandoc.Lua.Packages (LuaPackageParams (..), - installPandocPackageSearcher) +import Text.Pandoc.Lua.Init (runPandocLua) import Text.Pandoc.Lua.PandocModule (pushPandocModule) -- TODO: remove -import Text.Pandoc.Lua.Util (loadScriptFromDataDir) import qualified Foreign.Lua as Lua -import qualified Foreign.Lua.Module.Text as Lua -runLuaFilter :: Maybe FilePath -> FilePath -> String +-- | Run the Lua filter in @filterPath@ for a transformation to target +-- format @format@. Pandoc uses Lua init files to setup the Lua +-- interpreter. +runLuaFilter :: FilePath -> String -> Pandoc -> PandocIO (Either LuaException Pandoc) -runLuaFilter datadir filterPath format pd = do - luaPkgParams <- luaPackageParams datadir - res <- liftIO . Lua.runLuaEither $ - runLuaFilter' luaPkgParams filterPath format pd - newMediaBag <- liftIO (readIORef (luaPkgMediaBag luaPkgParams)) - setMediaBag newMediaBag - return res +runLuaFilter filterPath format doc = + runPandocLua (runLuaFilter' filterPath format doc) -runLuaFilter' :: LuaPackageParams - -> FilePath -> String +runLuaFilter' :: FilePath -> String -> Pandoc -> Lua Pandoc -runLuaFilter' luaPkgOpts filterPath format pd = do - initLuaState luaPkgOpts +runLuaFilter' filterPath format pd = do -- store module in global "pandoc" registerFormat top <- Lua.gettop @@ -83,24 +72,6 @@ runLuaFilter' luaPkgOpts filterPath format pd = do push format Lua.setglobal "FORMAT" -luaPackageParams :: Maybe FilePath -> PandocIO LuaPackageParams -luaPackageParams datadir = do - commonState <- getCommonState - mbRef <- liftIO . newIORef =<< getMediaBag - return LuaPackageParams - { luaPkgCommonState = commonState - , luaPkgDataDir = datadir - , luaPkgMediaBag = mbRef - } - --- Initialize the lua state with all required values -initLuaState :: LuaPackageParams -> Lua () -initLuaState luaPkgParams = do - Lua.openlibs - Lua.preloadTextModule "text" - installPandocPackageSearcher luaPkgParams - loadScriptFromDataDir (luaPkgDataDir luaPkgParams) "init.lua" - pushGlobalFilter :: Lua () pushGlobalFilter = do Lua.newtable diff --git a/src/Text/Pandoc/Lua/Init.hs b/src/Text/Pandoc/Lua/Init.hs new file mode 100644 index 000000000..a2bfa3801 --- /dev/null +++ b/src/Text/Pandoc/Lua/Init.hs @@ -0,0 +1,79 @@ +{- +Copyright © 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> + +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.Lua + Copyright : Copyright © 2017 Albert Krewinkel + License : GNU GPL, version 2 or above + + Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> + Stability : alpha + +Functions to initialize the Lua interpreter. +-} +module Text.Pandoc.Lua.Init + ( LuaException (..) + , LuaPackageParams (..) + , runPandocLua + , initLuaState + , luaPackageParams + ) where + +import Control.Monad.Trans (MonadIO (..)) +import Data.IORef (newIORef, readIORef) +import Foreign.Lua (Lua, LuaException (..)) +import GHC.IO.Encoding (getForeignEncoding, setForeignEncoding, utf8) +import Text.Pandoc.Class (PandocIO, getCommonState, getUserDataDir, getMediaBag, + setMediaBag) +import Text.Pandoc.Lua.Packages (LuaPackageParams (..), + installPandocPackageSearcher) +import Text.Pandoc.Lua.Util (loadScriptFromDataDir) + +import qualified Foreign.Lua as Lua +import qualified Foreign.Lua.Module.Text as Lua + +-- | Run the lua interpreter, using pandoc's default way of environment +-- initalization. +runPandocLua :: Lua a -> PandocIO (Either LuaException a) +runPandocLua luaOp = do + datadir <- getUserDataDir + luaPkgParams <- luaPackageParams datadir + enc <- liftIO $ getForeignEncoding <* setForeignEncoding utf8 + res <- liftIO $ Lua.runLuaEither (initLuaState luaPkgParams *> luaOp) + liftIO $ setForeignEncoding enc + newMediaBag <- liftIO (readIORef (luaPkgMediaBag luaPkgParams)) + setMediaBag newMediaBag + return res + +-- | Generate parameters required to setup pandoc's lua environment. +luaPackageParams :: Maybe FilePath -> PandocIO LuaPackageParams +luaPackageParams datadir = do + commonState <- getCommonState + mbRef <- liftIO . newIORef =<< getMediaBag + return LuaPackageParams + { luaPkgCommonState = commonState + , luaPkgDataDir = datadir + , luaPkgMediaBag = mbRef + } + +-- Initialize the lua state with all required values +initLuaState :: LuaPackageParams -> Lua () +initLuaState luaPkgParams = do + Lua.openlibs + Lua.preloadTextModule "text" + installPandocPackageSearcher luaPkgParams + loadScriptFromDataDir (luaPkgDataDir luaPkgParams) "init.lua" diff --git a/src/Text/Pandoc/Writers/Custom.hs b/src/Text/Pandoc/Writers/Custom.hs index ffe637966..72f443ed0 100644 --- a/src/Text/Pandoc/Writers/Custom.hs +++ b/src/Text/Pandoc/Writers/Custom.hs @@ -33,18 +33,20 @@ module Text.Pandoc.Writers.Custom ( writeCustom ) where import Control.Arrow ((***)) import Control.Exception import Control.Monad (when) +import Control.Monad.Trans (MonadIO (liftIO)) import Data.Char (toLower) import Data.List (intersperse) import qualified Data.Map as M import Data.Text (Text, pack) import Data.Typeable -import Foreign.Lua (Lua, ToLuaStack (..), callFunc, runLua) +import Foreign.Lua (Lua, ToLuaStack (..), callFunc) import Foreign.Lua.Api -import GHC.IO.Encoding (getForeignEncoding, setForeignEncoding, utf8) +import Text.Pandoc.Class (PandocIO) import Text.Pandoc.Definition import Text.Pandoc.Error +import Text.Pandoc.Lua.Init (runPandocLua) import Text.Pandoc.Lua.StackInstances () -import Text.Pandoc.Lua.Util (addValue) +import Text.Pandoc.Lua.Util (addValue, dostring') import Text.Pandoc.Options import Text.Pandoc.Templates import qualified Text.Pandoc.UTF8 as UTF8 @@ -91,14 +93,11 @@ data PandocLuaException = PandocLuaException String instance Exception PandocLuaException -- | Convert Pandoc to custom markup. -writeCustom :: FilePath -> WriterOptions -> Pandoc -> IO Text +writeCustom :: FilePath -> WriterOptions -> Pandoc -> PandocIO Text writeCustom luaFile opts doc@(Pandoc meta _) = do - luaScript <- UTF8.readFile luaFile - enc <- getForeignEncoding - setForeignEncoding utf8 - (body, context) <- runLua $ do - openlibs - stat <- loadstring luaScript + luaScript <- liftIO $ UTF8.readFile luaFile + res <- runPandocLua $ do + stat <- dostring' luaScript -- check for error in lua script (later we'll change the return type -- to handle this more gracefully): when (stat /= OK) $ @@ -111,7 +110,9 @@ writeCustom luaFile opts doc@(Pandoc meta _) = do inlineListToCustom meta return (rendered, context) - setForeignEncoding enc + let (body, context) = case res of + Left e -> throw (PandocLuaException (show e)) + Right x -> x case writerTemplate opts of Nothing -> return $ pack body Just tpl -> -- cgit v1.2.3 From 3c7a3d378c6fdb026297aee10e4818267a8b86a8 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Wed, 13 Dec 2017 15:08:37 -0500 Subject: Docx writer: Continue lists after interruption. Docx expects that lists will continue where they left off after an interruption and introduces a new id if a list is starting again. So we keep track of the state of lists and use them to define a "start" attribute, if necessary. Closes #4025 --- src/Text/Pandoc/Readers/Docx.hs | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index 651d46753..7c7845c71 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -121,6 +121,9 @@ data DState = DState { docxAnchorMap :: M.Map String String , docxMediaBag :: MediaBag , docxDropCap :: Inlines , docxWarnings :: [String] + -- keep track of (numId, lvl) values for + -- restarting + , docxListState :: M.Map (String, String) Integer } instance Default DState where @@ -128,6 +131,7 @@ instance Default DState where , docxMediaBag = mempty , docxDropCap = mempty , docxWarnings = [] + , docxListState = M.empty } data DEnv = DEnv { docxOptions :: ReaderOptions @@ -539,22 +543,25 @@ bodyPartToBlocks (Paragraph pPr parparts) then return mempty else return $ parStyleToTransform pPr $ para ils' bodyPartToBlocks (ListItem pPr numId lvl (Just levelInfo) parparts) = do - let - kvs = case levelInfo of - (_, fmt, txt, Just start) -> [ ("level", lvl) - , ("num-id", numId) - , ("format", fmt) - , ("text", txt) - , ("start", show start) - ] - - (_, fmt, txt, Nothing) -> [ ("level", lvl) - , ("num-id", numId) - , ("format", fmt) - , ("text", txt) - ] + -- We check whether this current numId has previously been used, + -- since Docx expects us to pick up where we left off. + listState <- gets docxListState + let startFromState = M.lookup (numId, lvl) listState + (_, fmt,txt, startFromLevelInfo) = levelInfo + start = case startFromState of + Just n -> n + 1 + Nothing -> case startFromLevelInfo of + Just n' -> n' + Nothing -> 1 + kvs = [ ("level", lvl) + , ("num-id", numId) + , ("format", fmt) + , ("text", txt) + , ("start", show start) + ] + modify $ \st -> st{ docxListState = M.insert (numId, lvl) start listState} blks <- bodyPartToBlocks (Paragraph pPr parparts) - return $ divWith ("", ["list-item"], kvs) blks + return $ divWith ("", ["list-item"], kvs) blks bodyPartToBlocks (ListItem pPr _ _ _ parparts) = let pPr' = pPr {pStyle = "ListParagraph": pStyle pPr} in -- cgit v1.2.3 From 935b16b38a710a26b06f0ae2ced5967429e010cc Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 13 Dec 2017 20:48:24 -0800 Subject: Removed whitespace at ends of line. --- src/Text/Pandoc/Class.hs | 6 +-- src/Text/Pandoc/Data.hs | 2 +- src/Text/Pandoc/Writers/OOXML.hs | 4 +- src/Text/Pandoc/Writers/Powerpoint.hs | 72 +++++++++++++++++------------------ 4 files changed, 42 insertions(+), 42 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index f48b19c12..c63781adf 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -453,7 +453,7 @@ runIO :: PandocIO a -> IO (Either PandocError a) runIO ma = flip evalStateT def $ runExceptT $ unPandocIO ma -- | Evaluate a 'PandocIO' operation, handling any errors --- by exiting with an appropriate message and error status. +-- by exiting with an appropriate message and error status. runIOorExplode :: PandocIO a -> IO a runIOorExplode ma = runIO ma >>= handleError @@ -720,7 +720,7 @@ getDefaultReferencePptx = do epochtime <- (floor . utcTimeToPOSIXSeconds) <$> getCurrentTime contents <- toLazy <$> readDataFile ("pptx/" ++ path) return $ toEntry path epochtime contents - datadir <- getUserDataDir + datadir <- getUserDataDir mbArchive <- case datadir of Nothing -> return Nothing Just d -> do @@ -732,7 +732,7 @@ getDefaultReferencePptx = do Just arch -> toArchive <$> readFileLazy arch Nothing -> foldr addEntryToArchive emptyArchive <$> mapM pathToEntry paths - + -- | Read file from user data directory or, -- if not found there, from Cabal data directory. diff --git a/src/Text/Pandoc/Data.hs b/src/Text/Pandoc/Data.hs index 332882c22..af0e4504f 100644 --- a/src/Text/Pandoc/Data.hs +++ b/src/Text/Pandoc/Data.hs @@ -18,5 +18,5 @@ dataFiles' = ("MANUAL.txt", $(embedFile "MANUAL.txt")) : -- handle the hidden file separately, since embedDir doesn't -- include it: ("docx/_rels/.rels", $(embedFile "data/docx/_rels/.rels")) : - ("pptx/_rels/.rels", $(embedFile "data/pptx/_rels/.rels")) : + ("pptx/_rels/.rels", $(embedFile "data/pptx/_rels/.rels")) : $(embedDir "data") diff --git a/src/Text/Pandoc/Writers/OOXML.hs b/src/Text/Pandoc/Writers/OOXML.hs index f48d27bd6..aa4979653 100644 --- a/src/Text/Pandoc/Writers/OOXML.hs +++ b/src/Text/Pandoc/Writers/OOXML.hs @@ -76,12 +76,12 @@ parseXml refArchive distArchive relpath = Nothing -> fail $ relpath ++ " corrupt in reference file" Just d -> return d --- Copied from Util +-- Copied from Util attrToNSPair :: XML.Attr -> Maybe (String, String) attrToNSPair (XML.Attr (QName s _ (Just "xmlns")) val) = Just (s, val) attrToNSPair _ = Nothing - + elemToNameSpaces :: Element -> NameSpaces elemToNameSpaces = mapMaybe attrToNSPair . elAttribs diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index d78833c81..b5f06c581 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -174,7 +174,7 @@ getPageHeight :: PresentationSize -> Pixels getPageHeight sz = case presSizeRatio sz of Ratio4x3 -> floor (((fromInteger (presSizeWidth sz)) / 4) * 3 :: Double) Ratio16x9 -> floor (((fromInteger (presSizeWidth sz)) / 16) * 9 :: Double) - Ratio16x10 -> floor (((fromInteger (presSizeWidth sz)) / 16) * 10 :: Double) + Ratio16x10 -> floor (((fromInteger (presSizeWidth sz)) / 16) * 10 :: Double) instance Default PresentationSize where def = PresentationSize 720 Ratio4x3 @@ -183,7 +183,7 @@ data Slide = MetadataSlide { metadataSlideTitle :: [ParaElem] , metadataSlideSubtitle :: [ParaElem] , metadataSlideAuthors :: [[ParaElem]] , metadataSlideDate :: [ParaElem] - } + } | TitleSlide { titleSlideHeader :: [ParaElem]} | ContentSlide { contentSlideHeader :: [ParaElem] , contentSlideContent :: [Shape] @@ -206,7 +206,7 @@ data TableProps = TableProps { tblPrFirstRow :: Bool type ColWidth = Integer -data Graphic = Tbl TableProps [ColWidth] [Cell] [[Cell]] +data Graphic = Tbl TableProps [ColWidth] [Cell] [[Cell]] deriving (Show, Eq) @@ -217,7 +217,7 @@ data Paragraph = Paragraph { paraProps :: ParaProps data HeaderType = TitleHeader | SlideHeader | InternalHeader Int deriving (Show, Eq) --- type StartingAt = Int +-- type StartingAt = Int -- data AutoNumType = ArabicNum -- | AlphaUpperNum @@ -362,7 +362,7 @@ blockToParagraphs (Plain ils) = do return [Paragraph pProps parElems] blockToParagraphs (Para ils) = do parElems <- inlinesToParElems ils - pProps <- asks envParaProps + pProps <- asks envParaProps return [Paragraph pProps parElems] blockToParagraphs (LineBlock ilsList) = do parElems <- inlinesToParElems $ intercalate [LineBreak] ilsList @@ -380,9 +380,9 @@ blockToParagraphs (BlockQuote blks) = -- TODO: work out the format blockToParagraphs (RawBlock _ _) = return [] -- parElems <- inlinesToParElems [Str str] - -- paraProps <- asks envParaProps + -- paraProps <- asks envParaProps -- return [Paragraph paraProps parElems] --- TODO: work out the format +-- TODO: work out the format blockToParagraphs (Header n _ ils) = do slideLevel <- asks envSlideLevel parElems <- inlinesToParElems ils @@ -490,7 +490,7 @@ splitBlocks' cur acc (h@(Header n _ _) : blks) = do EQ -> splitBlocks' [h] (acc ++ (if null cur then [] else [cur])) blks GT -> splitBlocks' (cur ++ [h]) acc blks splitBlocks' cur acc ((Para (img@(Image _ _ _):ils)) : blks) = do - slideLevel <- asks envSlideLevel + slideLevel <- asks envSlideLevel case cur of (Header n _ _) : [] | n == slideLevel -> splitBlocks' [] @@ -500,7 +500,7 @@ splitBlocks' cur acc ((Para (img@(Image _ _ _):ils)) : blks) = do (acc ++ (if null cur then [] else [cur]) ++ [[Para [img]]]) (if null ils then blks else (Para ils) : blks) splitBlocks' cur acc ((Plain (img@(Image _ _ _):ils)) : blks) = do - slideLevel <- asks envSlideLevel + slideLevel <- asks envSlideLevel case cur of (Header n _ _) : [] | n == slideLevel -> splitBlocks' [] @@ -510,7 +510,7 @@ splitBlocks' cur acc ((Plain (img@(Image _ _ _):ils)) : blks) = do (acc ++ (if null cur then [] else [cur]) ++ [[Para [img]]]) (if null ils then blks else (Plain ils) : blks) splitBlocks' cur acc (tbl@(Table _ _ _ _ _) : blks) = do - slideLevel <- asks envSlideLevel + slideLevel <- asks envSlideLevel case cur of (Header n _ _) : [] | n == slideLevel -> splitBlocks' [] (acc ++ [cur ++ [tbl]]) blks @@ -592,7 +592,7 @@ getMediaFiles = do distArchive <- asks envDistArchive let allEntries = nub $ filesInArchive refArchive ++ filesInArchive distArchive return $ filter (isPrefixOf "ppt/media") allEntries - + copyFileToArchiveIfExists :: PandocMonad m => Archive -> FilePath -> P m Archive copyFileToArchiveIfExists arch fp = do @@ -635,7 +635,7 @@ inheritedFiles = [ "_rels/.rels" -- , "ppt/slides/_rels/slide2.xml.rels" -- This is the one we're -- going to build - -- , "ppt/slides/slide2.xml" + -- , "ppt/slides/slide2.xml" -- , "ppt/slides/slide1.xml" , "ppt/viewProps.xml" , "ppt/tableStyles.xml" @@ -670,7 +670,7 @@ presentationToArchive p@(Presentation _ slides) = do slideEntries ++ slideRelEntries ++ mediaEntries ++ - [contentTypesEntry, presEntry, presRelsEntry] + [contentTypesEntry, presEntry, presRelsEntry] -------------------------------------------------- @@ -726,25 +726,25 @@ shapeHasName ns name element -- getContentTitleShape :: NameSpaces -> Element -> Maybe Element -- getContentTitleShape ns spTreeElem --- | isElem ns "p" "spTree" spTreeElem = +-- | isElem ns "p" "spTree" spTreeElem = -- filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasName ns "Title 1" e)) spTreeElem -- | otherwise = Nothing -- getSubtitleShape :: NameSpaces -> Element -> Maybe Element -- getSubtitleShape ns spTreeElem --- | isElem ns "p" "spTree" spTreeElem = +-- | isElem ns "p" "spTree" spTreeElem = -- filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasName ns "Subtitle 2" e)) spTreeElem -- | otherwise = Nothing -- getDateShape :: NameSpaces -> Element -> Maybe Element -- getDateShape ns spTreeElem --- | isElem ns "p" "spTree" spTreeElem = +-- | isElem ns "p" "spTree" spTreeElem = -- filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasName ns "Date Placeholder 3" e)) spTreeElem -- | otherwise = Nothing - + getContentShape :: NameSpaces -> Element -> Maybe Element getContentShape ns spTreeElem - | isElem ns "p" "spTree" spTreeElem = + | isElem ns "p" "spTree" spTreeElem = filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasName ns "Content Placeholder 2" e)) spTreeElem | otherwise = Nothing @@ -831,7 +831,7 @@ registerMedia fp caption = do (imgBytes, mbMt) <- P.fetchItem fp let imgExt = (mbMt >>= extensionFromMimeType >>= (\x -> return $ '.':x)) - <|> + <|> case imageType imgBytes of Just Png -> Just ".png" Just Jpeg -> Just ".jpeg" @@ -840,7 +840,7 @@ registerMedia fp caption = do Just Eps -> Just ".eps" Just Svg -> Just ".svg" Nothing -> Nothing - + let newGlobalId = case M.lookup fp globalIds of Just ident -> ident Nothing -> maxGlobalId + 1 @@ -893,7 +893,7 @@ fitToPage' (x, y) pageWidth pageHeight (floor x, floor y) | x / fromIntegral pageWidth > y / fromIntegral pageWidth = (pageWidth, floor $ ((fromIntegral pageWidth) / x) * y) - | otherwise = + | otherwise = (floor $ ((fromIntegral pageHeight) / y) * x, pageHeight) positionImage :: (Double, Double) -> Integer -> Integer -> (Integer, Integer) @@ -957,7 +957,7 @@ createCaption :: PandocMonad m => [ParaElem] -> P m Element createCaption paraElements = do let para = Paragraph def{pPropAlign = Just AlgnCenter} paraElements elements <- mapM paragraphToElement [para] - let ((x, y), (cx, cy)) = captionPosition + let ((x, y), (cx, cy)) = captionPosition let txBody = mknode "p:txBody" [] $ [mknode "a:bodyPr" [] (), mknode "a:lstStyle" [] ()] ++ elements return $ @@ -1041,7 +1041,7 @@ makePicElement mInfo attr = do , blipFill , spPr ] --- Currently hardcoded, until I figure out how to make it dynamic. +-- Currently hardcoded, until I figure out how to make it dynamic. blockQuoteSize :: Pixels blockQuoteSize = 20 @@ -1150,7 +1150,7 @@ shapeToElement layout (TextBox paras) [mknode "a:bodyPr" [] (), mknode "a:lstStyle" [] ()] ++ elements emptySpPr = mknode "p:spPr" [] () return $ - surroundWithMathAlternate $ + surroundWithMathAlternate $ replaceNamedChildren ns "p" "txBody" [txBody] $ replaceNamedChildren ns "p" "spPr" [emptySpPr] $ sp @@ -1199,7 +1199,7 @@ shapesToElements layout shps = do hardcodedTableMargin :: Integer hardcodedTableMargin = 36 - + graphicToElement :: PandocMonad m => Graphic -> P m Element graphicToElement (Tbl tblPr colWidths hdrCells rows) = do @@ -1241,7 +1241,7 @@ graphicToElement (Tbl tblPr colWidths hdrCells rows) = do getShapeByName :: NameSpaces -> Element -> String -> Maybe Element getShapeByName ns spTreeElem name - | isElem ns "p" "spTree" spTreeElem = + | isElem ns "p" "spTree" spTreeElem = filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasName ns name e)) spTreeElem | otherwise = Nothing @@ -1266,7 +1266,7 @@ nonBodyTextToElement layout shapeName paraElements -- | ns <- elemToNameSpaces layout -- , Just cSld <- findChild (elemName ns "p" "cSld") layout -- , Just spTree <- findChild (elemName ns "p" "spTree") cSld --- , Just sp <- getContentTitleShape ns spTree = +-- , Just sp <- getContentTitleShape ns spTree = -- let hdrPara = Paragraph def paraElems -- txBody = mknode "p:txBody" [] $ -- [mknode "a:bodyPr" [] (), mknode "a:lstStyle" [] ()] ++ @@ -1387,7 +1387,7 @@ elementToRel element slideToPresRel :: Monad m => Slide -> Int -> P m Relationship slideToPresRel slide idNum = do - n <- gets stSlideIdOffset + n <- gets stSlideIdOffset let rId = idNum + n fp = "slides/" ++ slideToFilePath slide idNum return $ Relationship { relId = rId @@ -1429,7 +1429,7 @@ presentationToRels (Presentation _ slides) = do modifyRelNum n = n - minRelNotOne + 2 + length slides relsWithoutSlides' = map (\r -> r{relId = modifyRelNum $ relId r}) relsWithoutSlides - + return $ mySlideRels ++ relsWithoutSlides' relToElement :: Relationship -> Element @@ -1479,7 +1479,7 @@ mediaRelElement mInfo = let ext = case mInfoExt mInfo of Just e -> e Nothing -> "" - in + in mknode "Relationship" [ ("Id", "rId" ++ (show $ mInfoLocalId mInfo)) , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image") , ("Target", "../media/image" ++ (show $ mInfoGlobalId mInfo) ++ ext) @@ -1503,7 +1503,7 @@ slideToSlideRelElement slide idNum = do Nothing -> [] return $ - mknode "Relationships" + mknode "Relationships" [("xmlns", "http://schemas.openxmlformats.org/package/2006/relationships")] ([mknode "Relationship" [ ("Id", "rId1") , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout") @@ -1546,9 +1546,9 @@ presentationToPresentationElement pres = do presentationToPresEntry :: PandocMonad m => Presentation -> P m Entry presentationToPresEntry pres = presentationToPresentationElement pres >>= elemToEntry "ppt/presentation.xml" - - + + defaultContentTypeToElem :: DefaultContentType -> Element defaultContentTypeToElem dct = @@ -1558,7 +1558,7 @@ defaultContentTypeToElem dct = () overrideContentTypeToElem :: OverrideContentType -> Element -overrideContentTypeToElem oct = +overrideContentTypeToElem oct = mknode "Override" [("PartName", overrideContentTypesPart oct), ("ContentType", overrideContentTypesType oct)] @@ -1571,7 +1571,7 @@ contentTypesToElement ct = mknode "Types" [("xmlns", ns)] $ (map defaultContentTypeToElem $ contentTypesDefaults ct) ++ (map overrideContentTypeToElem $ contentTypesOverrides ct) - + data DefaultContentType = DefaultContentType { defContentTypesExt :: String , defContentTypesType:: MimeType @@ -1634,7 +1634,7 @@ presML = "application/vnd.openxmlformats-officedocument.presentationml" noPresML :: String noPresML = "application/vnd.openxmlformats-officedocument" - + getContentType :: FilePath -> Maybe MimeType getContentType fp | fp == "ppt/presentation.xml" = Just $ presML ++ ".presentation.main+xml" -- cgit v1.2.3 From fa0241592c0341c85246e94b5a0342ef3a301755 Mon Sep 17 00:00:00 2001 From: Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com> Date: Thu, 14 Dec 2017 18:38:19 +1300 Subject: Deduplicate JATS writer image mime type code --- src/Text/Pandoc/Writers/JATS.hs | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs index fe5a36d13..8824eeb24 100644 --- a/src/Text/Pandoc/Writers/JATS.hs +++ b/src/Text/Pandoc/Writers/JATS.hs @@ -159,6 +159,17 @@ listItemToJATS opts mbmarker item = do maybe empty (\lbl -> inTagsIndented "label" (text lbl)) mbmarker $$ contents +imageMimeType :: String -> [(String, String)] -> (String, String) +imageMimeType src kvs = + let mbMT = getMimeType src + maintype = fromMaybe "image" $ + lookup "mimetype" kvs `mplus` + (takeWhile (/='/') <$> mbMT) + subtype = fromMaybe "" $ + lookup "mime-subtype" kvs `mplus` + ((drop 1 . dropWhile (/='/')) <$> mbMT) + in (maintype, subtype) + -- | Convert a Pandoc block element to JATS. blockToJATS :: PandocMonad m => WriterOptions -> Block -> JATS m Doc blockToJATS _ Null = return empty @@ -191,33 +202,21 @@ blockToJATS opts (Plain lst) = blockToJATS opts (Para lst) blockToJATS opts (Para [Image (ident,_,kvs) txt (src,'f':'i':'g':':':tit)]) = do alt <- inlinesToJATS opts txt + let (maintype, subtype) = imageMimeType src kvs let capt = if null txt then empty else inTagsSimple "caption" alt let attr = [("id", ident) | not (null ident)] ++ [(k,v) | (k,v) <- kvs, k `elem` ["fig-type", "orientation", "position", "specific-use"]] - let mbMT = getMimeType src - let maintype = fromMaybe "image" $ - lookup "mimetype" kvs `mplus` - (takeWhile (/='/') <$> mbMT) - let subtype = fromMaybe "" $ - lookup "mime-subtype" kvs `mplus` - ((drop 1 . dropWhile (/='/')) <$> mbMT) let graphicattr = [("mimetype",maintype), - ("mime-subtype",drop 1 subtype), + ("mime-subtype",subtype), ("xlink:href",src), -- do we need to URL escape this? ("xlink:title",tit)] return $ inTags True "fig" attr $ capt $$ selfClosingTag "graphic" graphicattr blockToJATS _ (Para [Image (ident,_,kvs) _ (src, tit)]) = do - let mbMT = getMimeType src - let maintype = fromMaybe "image" $ - lookup "mimetype" kvs `mplus` - (takeWhile (/='/') <$> mbMT) - let subtype = fromMaybe "" $ - lookup "mime-subtype" kvs `mplus` - ((drop 1 . dropWhile (/='/')) <$> mbMT) + let (maintype, subtype) = imageMimeType src kvs let attr = [("id", ident) | not (null ident)] ++ [("mimetype", maintype), ("mime-subtype", subtype), -- cgit v1.2.3 From 7888f49342d205973004c8d3e642b0d5d2f92e1a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 14 Dec 2017 12:03:14 -0800 Subject: Markdown reader: be pickier about table captions. A caption starts with a `:` which can't be followed by punctuation. Otherwise we can falsely interpret the start of a fenced div, or even a table header line like `:--:|:--:`, as a caption. --- src/Text/Pandoc/Readers/Markdown.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 3b6dcbcb9..9ffdbf00d 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -1274,7 +1274,7 @@ tableCaption :: PandocMonad m => MarkdownParser m (F Inlines) tableCaption = try $ do guardEnabled Ext_table_captions skipNonindentSpaces - (string ":" <* notFollowedBy (string "::")) <|> string "Table:" + (string ":" <* notFollowedBy (satisfy isPunctuation)) <|> string "Table:" trimInlinesF <$> inlines1 <* blanklines -- Parse a simple table with '---' header and one line per row. -- cgit v1.2.3 From b94f1e2045d8113f57adabf6c4e475c744a8ce80 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 14 Dec 2017 12:47:15 -0800 Subject: RST reader: more accurate parsing of references. Previously we erroneously included the enclosing backticks in a reference ID (closes #4156). This change also disables interpretation of syntax inside references, as in docutils. So, there is no emphasis in `my *link*`_ --- src/Text/Pandoc/Readers/RST.hs | 60 +++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 36 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 41b3c8b82..6b5d0a331 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -1030,7 +1030,7 @@ noteBlock' marker = try $ do citationMarker :: Monad m => RSTParser m [Char] citationMarker = do char '[' - res <- simpleReferenceName' + res <- simpleReferenceName char ']' return res @@ -1039,7 +1039,7 @@ noteMarker = do char '[' res <- many1 digit <|> - try (char '#' >> liftM ('#':) simpleReferenceName') + try (char '#' >> liftM ('#':) simpleReferenceName) <|> count 1 (oneOf "#*") char ']' return res @@ -1048,34 +1048,24 @@ noteMarker = do -- reference key -- -quotedReferenceName :: PandocMonad m => RSTParser m Inlines +quotedReferenceName :: PandocMonad m => RSTParser m String quotedReferenceName = try $ do char '`' >> notFollowedBy (char '`') -- `` means inline code! - trimInlines . mconcat <$> many1Till inline (char '`') - -unquotedReferenceName :: PandocMonad m => RSTParser m Inlines -unquotedReferenceName = try $ do -- `` means inline code! - trimInlines . mconcat <$> many1Till inline (lookAhead $ char ':') + manyTill anyChar (char '`') -- Simple reference names are single words consisting of alphanumerics -- plus isolated (no two adjacent) internal hyphens, underscores, -- periods, colons and plus signs; no whitespace or other characters -- are allowed. -simpleReferenceName' :: Monad m => ParserT [Char] st m String -simpleReferenceName' = do +simpleReferenceName :: Monad m => ParserT [Char] st m String +simpleReferenceName = do x <- alphaNum xs <- many $ alphaNum - <|> - try (oneOf "-_:+." <* lookAhead alphaNum) + <|> try (oneOf "-_:+." <* lookAhead alphaNum) return (x:xs) -simpleReferenceName :: Monad m => ParserT [Char] st m Inlines -simpleReferenceName = B.str <$> simpleReferenceName' - -referenceName :: PandocMonad m => RSTParser m Inlines -referenceName = quotedReferenceName <|> - try (simpleReferenceName <* lookAhead (char ':')) <|> - unquotedReferenceName +referenceName :: PandocMonad m => RSTParser m String +referenceName = quotedReferenceName <|> simpleReferenceName referenceKey :: PandocMonad m => RSTParser m [Char] referenceKey = do @@ -1123,16 +1113,17 @@ anonymousKey = try $ do updateState $ \s -> s { stateKeys = M.insert key ((src,""), nullAttr) $ stateKeys s } -stripTicks :: String -> String -stripTicks = reverse . stripTick . reverse . stripTick - where stripTick ('`':xs) = xs - stripTick xs = xs - referenceNames :: PandocMonad m => RSTParser m [String] referenceNames = do let rn = try $ do string ".. _" - (_, ref) <- withRaw referenceName + ref <- quotedReferenceName + <|> many ( noneOf ":\n" + <|> try (char '\n' <* + string " " <* + notFollowedBy blankline) + <|> try (char ':' <* lookAhead alphaNum) + ) char ':' return ref first <- rn @@ -1147,16 +1138,15 @@ regularKey = try $ do refs <- referenceNames src <- targetURI guard $ not (null src) - let keys = map (toKey . stripTicks) refs + let keys = map toKey refs forM_ keys $ \key -> updateState $ \s -> s { stateKeys = M.insert key ((src,""), nullAttr) $ stateKeys s } anchorDef :: PandocMonad m => RSTParser m [Char] anchorDef = try $ do - (refs, raw) <- withRaw (try (referenceNames <* blanklines)) - let keys = map stripTicks refs - forM_ keys $ \rawkey -> + (refs, raw) <- withRaw $ try (referenceNames <* blanklines) + forM_ refs $ \rawkey -> updateState $ \s -> s { stateKeys = M.insert (toKey rawkey) (('#':rawkey,""), nullAttr) $ stateKeys s } -- keep this for 2nd round of parsing, where we'll add the divs (anchor) @@ -1479,22 +1469,20 @@ explicitLink = try $ do _ -> return ((src, ""), nullAttr) return $ B.linkWith attr (escapeURI src') tit label'' -citationName :: PandocMonad m => RSTParser m Inlines +citationName :: PandocMonad m => RSTParser m String citationName = do raw <- citationMarker - return $ B.str $ "[" ++ raw ++ "]" + return $ "[" ++ raw ++ "]" referenceLink :: PandocMonad m => RSTParser m Inlines referenceLink = try $ do - (label',ref) <- withRaw (quotedReferenceName - <|> simpleReferenceName - <|> citationName) <* - char '_' + ref <- (referenceName <|> citationName) <* char '_' + let label' = B.text ref let isAnonKey (Key ('_':_)) = True isAnonKey _ = False state <- getState let keyTable = stateKeys state - key <- option (toKey $ stripTicks ref) $ + key <- option (toKey ref) $ do char '_' let anonKeys = sort $ filter isAnonKey $ M.keys keyTable case anonKeys of -- cgit v1.2.3 From 044d58bb24cf41493222a14612e2de4f8d05ea6a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 15 Dec 2017 09:45:29 -0800 Subject: Fixed regression in LateX tokenization. This mainly affects the Markdown reader when parsing raw LaTeX with escaped spaces. Closes #4159. --- src/Text/Pandoc/Readers/LaTeX.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 90d0fe5d1..fab97347b 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -320,7 +320,7 @@ totoks pos t = : totoks (incSourceColumn pos (1 + T.length cs)) rest' | c == '\\' -> case T.uncons rest of - Nothing -> [Tok pos Symbol (T.singleton c)] + Nothing -> [Tok pos (CtrlSeq " ") "\\"] Just (d, rest') | isLetterOrAt d -> -- \makeatletter is common in macro defs; @@ -338,7 +338,7 @@ totoks pos t = Just ('\n', r2) -> (T.pack "\n", T.span isSpaceOrTab r2) - _ -> (mempty, (w1, r1)) + _ -> (mempty, (mempty, r1)) in case T.uncons r3 of Just ('\n', _) -> Tok pos (CtrlSeq " ") ("\\" <> w1) -- cgit v1.2.3 From 3a3d661408db64ff26040f17bfb3b5153020024b Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 15 Dec 2017 10:13:16 -0800 Subject: LaTeX reader: export tokenize, untokenize. Mainly so they can be tested. --- src/Text/Pandoc/Readers/LaTeX.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index fab97347b..c82697704 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -37,7 +37,9 @@ module Text.Pandoc.Readers.LaTeX ( readLaTeX, applyMacros, rawLaTeXInline, rawLaTeXBlock, - inlineCommand + inlineCommand, + tokenize, + untokenize ) where import Control.Applicative (many, optional, (<|>)) -- cgit v1.2.3 From bba76463404fff64f60e097886fa196b973ce539 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 15 Dec 2017 12:16:40 -0800 Subject: LaTeX writer: use \renewcommand for \textlatin with babel. This avoids a clash with a deprecated \textlatin command defined in Babel. Closes #4161. --- src/Text/Pandoc/Writers/LaTeX.hs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 3b2cd214e..666aea07c 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -276,10 +276,13 @@ pandocToLaTeX options (Pandoc meta blocks) = do "\\AddBabelHook{" ++ poly ++ "}{afterextras}" ++ "{\\renewcommand{\\text" ++ poly ++ "}[2][]{\\foreignlanguage{" ++ poly ++ "}{##2}}}\n" - else "\\newcommand{\\text" ++ poly ++ "}[2][]{\\foreignlanguage{" - ++ babel ++ "}{#2}}\n" ++ - "\\newenvironment{" ++ poly ++ "}[2][]{\\begin{otherlanguage}{" - ++ babel ++ "}}{\\end{otherlanguage}}\n" + else (if poly == "latin" -- see #4161 + then "\\providecommand{\\textlatin}{}\n\\renewcommand" + else "\\newcommand") ++ "{\\text" ++ poly ++ + "}[2][]{\\foreignlanguage{" ++ babel ++ "}{#2}}\n" ++ + "\\newenvironment{" ++ poly ++ + "}[2][]{\\begin{otherlanguage}{" ++ + babel ++ "}}{\\end{otherlanguage}}\n" ) -- eliminate duplicates that have same polyglossia name $ nubBy (\a b -> fst a == fst b) -- cgit v1.2.3 From 808f6d3fa1816401d9eb1d6a5d1821fb783cc1d5 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 17 Dec 2017 09:47:24 -0800 Subject: OPML reader: enable raw HTML and other extensions by default for notes. This fixes a regression in 2.0. Note that extensions can now be individually disabled, e.g. `-f opml-smart-raw_html`. Closes #4164. --- src/Text/Pandoc/Extensions.hs | 1 + src/Text/Pandoc/Readers/OPML.hs | 23 ++++++++++++++--------- 2 files changed, 15 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 771898d70..bea293891 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -343,6 +343,7 @@ getDefaultExtensions "textile" = extensionsFromList Ext_smart, Ext_raw_html, Ext_auto_identifiers] +getDefaultExtensions "opml" = pandocExtensions -- affects notes getDefaultExtensions _ = extensionsFromList [Ext_auto_identifiers] diff --git a/src/Text/Pandoc/Readers/OPML.hs b/src/Text/Pandoc/Readers/OPML.hs index 1a1375b16..68f3252a9 100644 --- a/src/Text/Pandoc/Readers/OPML.hs +++ b/src/Text/Pandoc/Readers/OPML.hs @@ -12,7 +12,7 @@ import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Options import Text.Pandoc.Readers.HTML (readHtml) import Text.Pandoc.Readers.Markdown (readMarkdown) -import Text.Pandoc.Shared (crFilter) +import Text.Pandoc.Shared (crFilter, blocksToInlines) import Text.XML.Light type OPML m = StateT OPMLState m @@ -22,6 +22,7 @@ data OPMLState = OPMLState{ , opmlDocTitle :: Inlines , opmlDocAuthors :: [Inlines] , opmlDocDate :: Inlines + , opmlOptions :: ReaderOptions } deriving Show instance Default OPMLState where @@ -29,13 +30,14 @@ instance Default OPMLState where , opmlDocTitle = mempty , opmlDocAuthors = [] , opmlDocDate = mempty - } + , opmlOptions = def + } readOPML :: PandocMonad m => ReaderOptions -> Text -> m Pandoc -readOPML _ inp = do +readOPML opts inp = do (bs, st') <- runStateT (mapM parseBlock $ normalizeTree $ - parseXML (unpack (crFilter inp))) def + parseXML (unpack (crFilter inp))) def{ opmlOptions = opts } return $ setTitle (opmlDocTitle st') $ setAuthors (opmlDocAuthors st') $ @@ -69,13 +71,16 @@ attrValue attr elt = -- exceptT = either throwError return asHtml :: PandocMonad m => String -> OPML m Inlines -asHtml s = - (\(Pandoc _ bs) -> case bs of - [Plain ils] -> fromList ils - _ -> mempty) <$> lift (readHtml def (pack s)) +asHtml s = do + opts <- gets opmlOptions + Pandoc _ bs <- readHtml def{ readerExtensions = readerExtensions opts } (pack s) + return $ fromList $ blocksToInlines bs asMarkdown :: PandocMonad m => String -> OPML m Blocks -asMarkdown s = (\(Pandoc _ bs) -> fromList bs) <$> lift (readMarkdown def (pack s)) +asMarkdown s = do + opts <- gets opmlOptions + Pandoc _ bs <- readMarkdown def{ readerExtensions = readerExtensions opts } (pack s) + return $ fromList bs getBlocks :: PandocMonad m => Element -> OPML m Blocks getBlocks e = mconcat <$> mapM parseBlock (elContent e) -- cgit v1.2.3 From 70dc5834da16680caedef4727b1d7f335f0b5427 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Mon, 18 Dec 2017 14:24:16 +0100 Subject: Lua filters: perform minor code clean-up Change: minor --- src/Text/Pandoc/Lua/Init.hs | 8 ++++---- src/Text/Pandoc/Lua/PandocModule.hs | 11 ++++------- 2 files changed, 8 insertions(+), 11 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/Init.hs b/src/Text/Pandoc/Lua/Init.hs index a2bfa3801..9b107e945 100644 --- a/src/Text/Pandoc/Lua/Init.hs +++ b/src/Text/Pandoc/Lua/Init.hs @@ -50,8 +50,7 @@ import qualified Foreign.Lua.Module.Text as Lua -- initalization. runPandocLua :: Lua a -> PandocIO (Either LuaException a) runPandocLua luaOp = do - datadir <- getUserDataDir - luaPkgParams <- luaPackageParams datadir + luaPkgParams <- luaPackageParams enc <- liftIO $ getForeignEncoding <* setForeignEncoding utf8 res <- liftIO $ Lua.runLuaEither (initLuaState luaPkgParams *> luaOp) liftIO $ setForeignEncoding enc @@ -60,9 +59,10 @@ runPandocLua luaOp = do return res -- | Generate parameters required to setup pandoc's lua environment. -luaPackageParams :: Maybe FilePath -> PandocIO LuaPackageParams -luaPackageParams datadir = do +luaPackageParams :: PandocIO LuaPackageParams +luaPackageParams = do commonState <- getCommonState + datadir <- getUserDataDir mbRef <- liftIO . newIORef =<< getMediaBag return LuaPackageParams { luaPkgCommonState = commonState diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index 75f884c46..6bc2618fd 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -71,16 +71,13 @@ pushPandocModule datadir = do return 1 walkElement :: (ToLuaStack a, Walkable [Inline] a, Walkable [Block] a) - => a -> LuaFilter -> Lua NumResults -walkElement x f = do - x' <- walkInlines f x >>= walkBlocks f - Lua.push x' - return 1 + => a -> LuaFilter -> Lua a +walkElement x f = walkInlines f x >>= walkBlocks f -walkInline :: Inline -> LuaFilter -> Lua NumResults +walkInline :: Inline -> LuaFilter -> Lua Inline walkInline = walkElement -walkBlock :: Block -> LuaFilter -> Lua NumResults +walkBlock :: Block -> LuaFilter -> Lua Block walkBlock = walkElement readDoc :: String -> String -> Lua NumResults -- cgit v1.2.3 From c0cc9270cbfad719d4cd3b8c57060cb06d41fe78 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 18 Dec 2017 16:31:32 -0800 Subject: Org writer: don't allow fn refs to wrap to beginning of line. Otherwise they can be interpreted as footnote definitions. Closes #4171. --- src/Text/Pandoc/Writers/Org.hs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index f73822b86..b2f9bbc53 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -308,7 +308,11 @@ blockListToOrg blocks = vcat <$> mapM blockToOrg blocks inlineListToOrg :: PandocMonad m => [Inline] -> Org m Doc -inlineListToOrg lst = hcat <$> mapM inlineToOrg lst +inlineListToOrg lst = hcat <$> mapM inlineToOrg (fixNotes lst) + where fixNotes [] = [] -- prevent note ref from wrapping, see #4171 + fixNotes (Space : n@Note{} : rest) = + Str " " : n : fixNotes rest + fixNotes (x : rest) = x : fixNotes rest -- | Convert Pandoc inline element to Org. inlineToOrg :: PandocMonad m => Inline -> Org m Doc -- cgit v1.2.3 From f6abf15832e4b41ed26b285b4a7c8f515ddb139f Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Tue, 19 Dec 2017 04:16:57 +0300 Subject: Muse reader: parse empty comments correctly --- src/Text/Pandoc/Readers/Muse.hs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 04cec149b..46dcf38d9 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -220,8 +220,7 @@ blockElements = choice [ comment comment :: PandocMonad m => MuseParser m (F Blocks) comment = try $ do char ';' - space - many $ noneOf "\n" + optionMaybe (spaceChar >> (many $ noneOf "\n")) eol return mempty -- cgit v1.2.3 From ef8430e70269d0332f802986c9ef570faad8faa0 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Tue, 19 Dec 2017 13:16:08 +0300 Subject: Fix for #4171 fix: don't wrap note references after SoftBreak --- src/Text/Pandoc/Writers/Org.hs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index b2f9bbc53..e10fcd5ce 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -312,6 +312,8 @@ inlineListToOrg lst = hcat <$> mapM inlineToOrg (fixNotes lst) where fixNotes [] = [] -- prevent note ref from wrapping, see #4171 fixNotes (Space : n@Note{} : rest) = Str " " : n : fixNotes rest + fixNotes (SoftBreak : n@Note{} : rest) = + Str " " : n : fixNotes rest fixNotes (x : rest) = x : fixNotes rest -- | Convert Pandoc inline element to Org. -- cgit v1.2.3 From 1e21cfb251506d42cbdcf3e24661f08633817572 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Tue, 19 Dec 2017 13:22:15 +0300 Subject: Muse writer: don't wrap note references to the next line Closes #4172. --- src/Text/Pandoc/Writers/Muse.hs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index ccda8edf1..545891d97 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -313,11 +313,17 @@ normalizeInlineList (Span a1 x1 : Span a2 x2 : ils) | a1 == a2 normalizeInlineList (x:xs) = x : normalizeInlineList xs normalizeInlineList [] = [] +fixNotes :: [Inline] -> [Inline] +fixNotes [] = [] +fixNotes (Space : n@Note{} : rest) = Str " " : n : fixNotes rest +fixNotes (SoftBreak : n@Note{} : rest) = Str " " : n : fixNotes rest +fixNotes (x:xs) = x : fixNotes xs + -- | Convert list of Pandoc inline elements to Muse. inlineListToMuse :: PandocMonad m => [Inline] -> StateT WriterState m Doc -inlineListToMuse lst = liftM hcat (mapM inlineToMuse (normalizeInlineList lst)) +inlineListToMuse lst = hcat <$> mapM inlineToMuse (fixNotes $ normalizeInlineList lst) -- | Convert Pandoc inline element to Muse. inlineToMuse :: PandocMonad m -- cgit v1.2.3 From 5d3c9e56460165be452b672f12fc476e7a5ed3a9 Mon Sep 17 00:00:00 2001 From: Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com> Date: Wed, 20 Dec 2017 13:54:02 +1300 Subject: Add Basic JATS reader based on DocBook reader --- src/Text/Pandoc/Readers.hs | 5 +- src/Text/Pandoc/Readers/JATS.hs | 387 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 391 insertions(+), 1 deletion(-) create mode 100644 src/Text/Pandoc/Readers/JATS.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers.hs b/src/Text/Pandoc/Readers.hs index d954256c8..a8448952e 100644 --- a/src/Text/Pandoc/Readers.hs +++ b/src/Text/Pandoc/Readers.hs @@ -52,6 +52,7 @@ module Text.Pandoc.Readers , readOrg , readLaTeX , readHtml + , readJATS , readTextile , readDocBook , readOPML @@ -84,7 +85,8 @@ import Text.Pandoc.Readers.DocBook import Text.Pandoc.Readers.Docx import Text.Pandoc.Readers.EPUB import Text.Pandoc.Readers.Haddock -import Text.Pandoc.Readers.HTML +import Text.Pandoc.Readers.HTML (readHtml) +import Text.Pandoc.Readers.JATS (readJATS) import Text.Pandoc.Readers.LaTeX import Text.Pandoc.Readers.Markdown import Text.Pandoc.Readers.MediaWiki @@ -129,6 +131,7 @@ readers = [ ("native" , TextReader readNative) ,("org" , TextReader readOrg) ,("textile" , TextReader readTextile) -- TODO : textile+lhs ,("html" , TextReader readHtml) + ,("jats" , TextReader readJATS) ,("latex" , TextReader readLaTeX) ,("haddock" , TextReader readHaddock) ,("twiki" , TextReader readTWiki) diff --git a/src/Text/Pandoc/Readers/JATS.hs b/src/Text/Pandoc/Readers/JATS.hs new file mode 100644 index 000000000..fc71e9a51 --- /dev/null +++ b/src/Text/Pandoc/Readers/JATS.hs @@ -0,0 +1,387 @@ +{-# LANGUAGE ExplicitForAll, TupleSections #-} +module Text.Pandoc.Readers.JATS ( readJATS ) where +import Control.Monad.State.Strict +import Data.Char (isDigit, isSpace, toUpper) +import Data.Default +import Data.Generics +import Data.List (intersperse) +import Data.Maybe (maybeToList, fromMaybe) +import Data.Text (Text) +import qualified Data.Text as T +import Text.HTML.TagSoup.Entity (lookupEntity) +import Text.Pandoc.Builder +import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Options +import Text.Pandoc.Shared (underlineSpan, crFilter, safeRead) +import Text.TeXMath (readMathML, writeTeX) +import Text.XML.Light + +type JATS m = StateT JATSState m + +data JATSState = JATSState{ jatsSectionLevel :: Int + , jatsQuoteType :: QuoteType + , jatsMeta :: Meta + , jatsAcceptsMeta :: Bool + , jatsBook :: Bool + , jatsFigureTitle :: Inlines + , jatsContent :: [Content] + } deriving Show + +instance Default JATSState where + def = JATSState{ jatsSectionLevel = 0 + , jatsQuoteType = DoubleQuote + , jatsMeta = mempty + , jatsAcceptsMeta = False + , jatsBook = False + , jatsFigureTitle = mempty + , jatsContent = [] } + + +readJATS :: PandocMonad m => ReaderOptions -> Text -> m Pandoc +readJATS _ inp = do + let tree = normalizeTree . parseXML + $ T.unpack $ crFilter inp + (bs, st') <- flip runStateT (def{ jatsContent = tree }) $ mapM parseBlock tree + return $ Pandoc (jatsMeta st') (toList . mconcat $ bs) + +-- normalize input, consolidating adjacent Text and CRef elements +normalizeTree :: [Content] -> [Content] +normalizeTree = everywhere (mkT go) + where go :: [Content] -> [Content] + go (Text (CData CDataRaw _ _):xs) = xs + go (Text (CData CDataText s1 z):Text (CData CDataText s2 _):xs) = + Text (CData CDataText (s1 ++ s2) z):xs + go (Text (CData CDataText s1 z):CRef r:xs) = + Text (CData CDataText (s1 ++ convertEntity r) z):xs + go (CRef r:Text (CData CDataText s1 z):xs) = + Text (CData CDataText (convertEntity r ++ s1) z):xs + go (CRef r1:CRef r2:xs) = + Text (CData CDataText (convertEntity r1 ++ convertEntity r2) Nothing):xs + go xs = xs + +convertEntity :: String -> String +convertEntity e = Data.Maybe.fromMaybe (map toUpper e) (lookupEntity e) + +-- convenience function to get an attribute value, defaulting to "" +attrValue :: String -> Element -> String +attrValue attr = + fromMaybe "" . maybeAttrValue attr + +maybeAttrValue :: String -> Element -> Maybe String +maybeAttrValue attr elt = + lookupAttrBy (\x -> qName x == attr) (elAttribs elt) + +-- convenience function +named :: String -> Element -> Bool +named s e = qName (elName e) == s + +-- + +acceptingMetadata :: PandocMonad m => JATS m a -> JATS m a +acceptingMetadata p = do + modify (\s -> s { jatsAcceptsMeta = True } ) + res <- p + modify (\s -> s { jatsAcceptsMeta = False }) + return res + +checkInMeta :: (PandocMonad m, Monoid a) => JATS m () -> JATS m a +checkInMeta p = do + accepts <- jatsAcceptsMeta <$> get + when accepts p + return mempty + +addMeta :: PandocMonad m => ToMetaValue a => String -> a -> JATS m () +addMeta field val = modify (setMeta field val) + +instance HasMeta JATSState where + setMeta field v s = s {jatsMeta = setMeta field v (jatsMeta s)} + deleteMeta field s = s {jatsMeta = deleteMeta field (jatsMeta s)} + +isBlockElement :: Content -> Bool +isBlockElement (Elem e) = qName (elName e) `elem` blocktags + where blocktags = paragraphLevel ++ lists ++ mathML ++ other + paragraphLevel = ["address", "array", "boxed-text", "chem-struct-wrap", + "code", "fig", "fig-group", "graphic", "media", "preformat", + "supplementary-material", "table-wrap", "table-wrap-group", + "alternatives", "disp-formula", "disp-formula-group"] + lists = ["def-list", "list"] + mathML = ["tex-math", "mml:math"] + other = ["p", "related-article", "related-object", "ack", "disp-quote", + "speech", "statement", "verse-group", "x"] +isBlockElement _ = False + +-- Trim leading and trailing newline characters +trimNl :: String -> String +trimNl = reverse . go . reverse . go + where go ('\n':xs) = xs + go xs = xs + +-- function that is used by both graphic (in parseBlock) +-- and inline-graphic (in parseInline) +getGraphic :: PandocMonad m => Element -> JATS m Inlines +getGraphic e = do + let atVal a = attrValue a e + attr = (atVal "id", words $ atVal "role", []) + imageUrl = atVal "href" + captionOrLabel = case filterChild (\x -> named "caption" x + || named "label" x) e of + Nothing -> return mempty + Just z -> mconcat <$> + mapM parseInline (elContent z) + figTitle <- gets jatsFigureTitle + let (caption, title) = if isNull figTitle + then (captionOrLabel, atVal "title") + else (return figTitle, "fig:") + fmap (imageWith attr imageUrl title) caption + +getBlocks :: PandocMonad m => Element -> JATS m Blocks +getBlocks e = mconcat <$> + mapM parseBlock (elContent e) + + +parseBlock :: PandocMonad m => Content -> JATS m Blocks +parseBlock (Text (CData CDataRaw _ _)) = return mempty -- DOCTYPE +parseBlock (Text (CData _ s _)) = if all isSpace s + then return mempty + else return $ plain $ trimInlines $ text s +parseBlock (CRef x) = return $ plain $ str $ map toUpper x +parseBlock (Elem e) = + case qName (elName e) of + "p" -> parseMixed para (elContent e) + "code" -> codeBlockWithLang + "preformat" -> codeBlockWithLang + "disp-quote" -> parseBlockquote + "list" -> case attrValue "list-type" e of + "bullet" -> bulletList <$> listitems + listType -> do + let start = fromMaybe 1 $ + (strContent <$> (filterElement (named "list-item") e + >>= filterElement (named "lable"))) + >>= safeRead + orderedListWith (start, parseListStyleType listType, DefaultDelim) + <$> listitems + "def-list" -> definitionList <$> deflistitems + "sec" -> gets jatsSectionLevel >>= sect . (+1) + "title" -> return mempty + "title-group" -> checkInMeta getTitle + "graphic" -> para <$> getGraphic e + "journal-meta" -> metaBlock + "article-meta" -> metaBlock + "custom-meta" -> metaBlock + "table" -> parseTable + "fig" -> divWith (attrValue "id" e, ["fig"], []) <$> getBlocks e + "table-wrap" -> divWith (attrValue "id" e, ["table-wrap"], []) <$> getBlocks e + "caption" -> divWith (attrValue "id" e, ["caption"], []) <$> sect 6 + "ref-list" -> divWith ("refs", [], []) <$> getBlocks e + "ref" -> divWith ("ref-" <> attrValue "id" e, [], []) <$> getBlocks e + "?xml" -> return mempty + _ -> getBlocks e + where parseMixed container conts = do + let (ils,rest) = break isBlockElement conts + ils' <- (trimInlines . mconcat) <$> mapM parseInline ils + let p = if ils' == mempty then mempty else container ils' + case rest of + [] -> return p + (r:rs) -> do + b <- parseBlock r + x <- parseMixed container rs + return $ p <> b <> x + codeBlockWithLang = do + let classes' = case attrValue "language" e of + "" -> [] + x -> [x] + return $ codeBlockWith (attrValue "id" e, classes', []) + $ trimNl $ strContentRecursive e + parseBlockquote = do + attrib <- case filterChild (named "attribution") e of + Nothing -> return mempty + Just z -> (para . (str "— " <>) . mconcat) + <$> + mapM parseInline (elContent z) + contents <- getBlocks e + return $ blockQuote (contents <> attrib) + parseListStyleType "roman-lower" = LowerRoman + parseListStyleType "roman-upper" = UpperRoman + parseListStyleType "alpha-lower" = LowerAlpha + parseListStyleType "alpha-upper" = UpperAlpha + parseListStyleType _ = DefaultStyle + listitems = mapM getBlocks $ filterChildren (named "list-item") e + deflistitems = mapM parseVarListEntry $ filterChildren + (named "def-item") e + parseVarListEntry e' = do + let terms = filterChildren (named "term") e' + let items = filterChildren (named "def") e' + terms' <- mapM getInlines terms + items' <- mapM getBlocks items + return (mconcat $ intersperse (str "; ") terms', items') + getTitle = do + tit <- case filterChild (named "article-title") e of + Just s -> getInlines s + Nothing -> return mempty + subtit <- case filterChild (named "subtitle") e of + Just s -> (text ": " <>) <$> + getInlines s + Nothing -> return mempty + addMeta "title" (tit <> subtit) + + parseTable = do + let isCaption x = named "title" x || named "caption" x + caption <- case filterChild isCaption e of + Just t -> getInlines t + Nothing -> return mempty + let e' = fromMaybe e $ filterChild (named "tgroup") e + let isColspec x = named "colspec" x || named "col" x + let colspecs = case filterChild (named "colgroup") e' of + Just c -> filterChildren isColspec c + _ -> filterChildren isColspec e' + let isRow x = named "row" x || named "tr" x + headrows <- case filterChild (named "thead") e' of + Just h -> case filterChild isRow h of + Just x -> parseRow x + Nothing -> return [] + Nothing -> return [] + bodyrows <- case filterChild (named "tbody") e' of + Just b -> mapM parseRow + $ filterChildren isRow b + Nothing -> mapM parseRow + $ filterChildren isRow e' + let toAlignment c = case findAttr (unqual "align") c of + Just "left" -> AlignLeft + Just "right" -> AlignRight + Just "center" -> AlignCenter + _ -> AlignDefault + let toWidth c = case findAttr (unqual "colwidth") c of + Just w -> fromMaybe 0 + $ safeRead $ '0': filter (\x -> + isDigit x || x == '.') w + Nothing -> 0 :: Double + let numrows = case bodyrows of + [] -> 0 + xs -> maximum $ map length xs + let aligns = case colspecs of + [] -> replicate numrows AlignDefault + cs -> map toAlignment cs + let widths = case colspecs of + [] -> replicate numrows 0 + cs -> let ws = map toWidth cs + tot = sum ws + in if all (> 0) ws + then map (/ tot) ws + else replicate numrows 0 + let headrows' = if null headrows + then replicate numrows mempty + else headrows + return $ table caption (zip aligns widths) + headrows' bodyrows + isEntry x = named "entry" x || named "td" x || named "th" x + parseRow = mapM (parseMixed plain . elContent) . filterChildren isEntry + sect n = do isbook <- gets jatsBook + let n' = if isbook || n == 0 then n + 1 else n + headerText <- case filterChild (named "title") e `mplus` + (filterChild (named "info") e >>= + filterChild (named "title")) of + Just t -> getInlines t + Nothing -> return mempty + oldN <- gets jatsSectionLevel + modify $ \st -> st{ jatsSectionLevel = n } + b <- getBlocks e + let ident = attrValue "id" e + modify $ \st -> st{ jatsSectionLevel = oldN } + return $ headerWith (ident,[],[]) n' headerText <> b +-- lineItems = mapM getInlines $ filterChildren (named "line") e + metaBlock = acceptingMetadata (getBlocks e) >> return mempty + +getInlines :: PandocMonad m => Element -> JATS m Inlines +getInlines e' = (trimInlines . mconcat) <$> + mapM parseInline (elContent e') + +strContentRecursive :: Element -> String +strContentRecursive = strContent . + (\e' -> e'{ elContent = map elementToStr $ elContent e' }) + +elementToStr :: Content -> Content +elementToStr (Elem e') = Text $ CData CDataText (strContentRecursive e') Nothing +elementToStr x = x + +parseInline :: PandocMonad m => Content -> JATS m Inlines +parseInline (Text (CData _ s _)) = return $ text s +parseInline (CRef ref) = + return $ maybe (text $ map toUpper ref) text $ lookupEntity ref +parseInline (Elem e) = + case qName (elName e) of + "italic" -> emph <$> innerInlines + "bold" -> strong <$> innerInlines + "strike" -> strikeout <$> innerInlines + "sub" -> subscript <$> innerInlines + "sup" -> superscript <$> innerInlines + "underline" -> underlineSpan <$> innerInlines + "break" -> return linebreak + "sc" -> smallcaps <$> innerInlines + + "code" -> codeWithLang + "monospace" -> codeWithLang + + "inline-graphic" -> getGraphic e + "disp-quote" -> do + qt <- gets jatsQuoteType + let qt' = if qt == SingleQuote then DoubleQuote else SingleQuote + modify $ \st -> st{ jatsQuoteType = qt' } + contents <- innerInlines + modify $ \st -> st{ jatsQuoteType = qt } + return $ if qt == SingleQuote + then singleQuoted contents + else doubleQuoted contents + + "xref" -> do + ils <- innerInlines + let rid = attrValue "rid" e + let refType = ("ref-type",) <$> maybeAttrValue "ref-type" e + let attr = (attrValue "id" e, [], maybeToList refType) + return $ linkWith attr ('#' : rid) "" ils + "ext-link" -> do + ils <- innerInlines + let title = fromMaybe "" $ findAttr (QName "title" (Just "http://www.w3.org/1999/xlink") Nothing) e + let href = case findAttr (QName "href" (Just "http://www.w3.org/1999/xlink") Nothing) e of + Just h -> h + _ -> '#' : attrValue "rid" e + let ils' = if ils == mempty then str href else ils + let attr = (attrValue "id" e, [], []) + return $ linkWith attr href title ils' + + "disp-formula" -> formula displayMath + "inline-formula" -> formula math + "math" | qPrefix (elName e) == Just "mml" -> return . math $ mathML e + "tex-math" -> return . math $ strContent e + + "email" -> return $ link ("mailto:" ++ strContent e) "" + $ str $ strContent e + "uri" -> return $ link (strContent e) "" $ str $ strContent e + "fn" -> (note . mconcat) <$> + mapM parseBlock (elContent e) + -- Note: this isn't a real docbook tag; it's what we convert + -- <?asciidor-br?> to in handleInstructions, above. A kludge to + -- work around xml-light's inability to parse an instruction. + _ -> innerInlines + where innerInlines = (trimInlines . mconcat) <$> + mapM parseInline (elContent e) + mathML x = + case readMathML . showElement $ everywhere (mkT removePrefix) x of + Left _ -> mempty + Right m -> writeTeX m + formula constructor = do + let whereToLook = fromMaybe e $ filterElement (named "alternatives") e + texMaths = map strContent $ + filterChildren (named "tex-math") whereToLook + mathMLs = map mathML $ + filterChildren isMathML whereToLook + return . mconcat . take 1 . map constructor $ texMaths ++ mathMLs + + isMathML x = qName (elName x) == "math" && + qPrefix (elName x) == Just "mml" + removePrefix elname = elname { qPrefix = Nothing } + codeWithLang = do + let classes' = case attrValue "language" e of + "" -> [] + l -> [l] + return $ codeWith (attrValue "id" e,classes',[]) $ strContentRecursive e + -- cgit v1.2.3 From b5e62a5c0963cf10338170a94f6a5a2c5b39ad1d Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Wed, 20 Dec 2017 14:00:30 +0300 Subject: Muse reader: require that note references does not start with 0 --- src/Text/Pandoc/Readers/Muse.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 46dcf38d9..1ea78676b 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -336,7 +336,9 @@ para = do noteMarker :: PandocMonad m => MuseParser m String noteMarker = try $ do char '[' - many1Till digit $ char ']' + first <- oneOf "123456789" + rest <- manyTill digit (char ']') + return $ first:rest -- Amusewiki version of note -- Parsing is similar to list item, except that note marker is used instead of list marker -- cgit v1.2.3 From e45f87a3be12086f7f642fdbb407ce5f3bfe59d4 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert+github@zeitkraut.de> Date: Wed, 20 Dec 2017 18:04:50 +0100 Subject: Org reader: fix asterisks-related parsing error A parsing error was fixed which caused the org reader to fail when parsing a paragraph starting with two or more asterisks. Fixes: #4180 --- src/Text/Pandoc/Readers/Org/Blocks.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index 04a0efc15..cc6abbfa5 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -737,7 +737,7 @@ noteBlock = try $ do paraOrPlain :: PandocMonad m => OrgParser m (F Blocks) paraOrPlain = try $ do -- Make sure we are not looking at a headline - notFollowedBy' (char '*' *> oneOf " *") + notFollowedBy' headerStart ils <- inlines nl <- option False (newline *> return True) -- Read block as paragraph, except if we are in a list context and the block -- cgit v1.2.3 From 5d3573e780d5056c87bb64858ea0890a27bc1686 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Wed, 20 Dec 2017 21:59:11 +0100 Subject: Lua modules: turn pipe, read into full Haskell functions The `pipe` and `read` utility functions are converted from hybrid lua/haskell functions into full Haskell functions. This avoids the need for intermediate `_pipe`/`_read` helper functions, which have dropped. --- src/Text/Pandoc/Lua/PandocModule.hs | 93 ++++++++++++++++++++++++++----------- src/Text/Pandoc/Lua/Util.hs | 15 ++++-- 2 files changed, 77 insertions(+), 31 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs index 6bc2618fd..4a3e4d354 100644 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ b/src/Text/Pandoc/Lua/PandocModule.hs @@ -1,5 +1,3 @@ -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE FlexibleContexts #-} {- Copyright © 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> @@ -17,7 +15,7 @@ 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 -} -{-# LANGUAGE CPP #-} +{-# LANGUAGE FlexibleContexts #-} {- | Module : Text.Pandoc.Lua.PandocModule Copyright : Copyright © 2017 Albert Krewinkel @@ -33,21 +31,20 @@ module Text.Pandoc.Lua.PandocModule , pushMediaBagModule ) where -import Control.Monad (zipWithM_) +import Control.Monad (when, zipWithM_) import Data.Default (Default (..)) import Data.Digest.Pure.SHA (sha1, showDigest) import Data.IORef (IORef, modifyIORef', readIORef) import Data.Maybe (fromMaybe) import Data.Text (pack) import Foreign.Lua (ToLuaStack, FromLuaStack, Lua, NumResults, liftIO) -import Foreign.Lua.FunctionCalling (ToHaskellFunction) import System.Exit (ExitCode (..)) import Text.Pandoc.Class (CommonState (..), fetchItem, putCommonState, runIO, runIOorExplode, setMediaBag) import Text.Pandoc.Definition (Block, Inline) import Text.Pandoc.Lua.Filter (walkInlines, walkBlocks, LuaFilter) import Text.Pandoc.Lua.StackInstances () -import Text.Pandoc.Lua.Util (loadScriptFromDataDir) +import Text.Pandoc.Lua.Util (addFunction, addValue, loadScriptFromDataDir) import Text.Pandoc.Walk (Walkable) import Text.Pandoc.MIME (MimeType) import Text.Pandoc.Options (ReaderOptions (readerExtensions)) @@ -55,6 +52,7 @@ import Text.Pandoc.Process (pipeProcess) import Text.Pandoc.Readers (Reader (..), getReader) import qualified Data.ByteString.Lazy as BL +import qualified Data.ByteString.Lazy.Char8 as BSL import qualified Foreign.Lua as Lua import qualified Text.Pandoc.MediaBag as MB @@ -63,8 +61,8 @@ import qualified Text.Pandoc.MediaBag as MB pushPandocModule :: Maybe FilePath -> Lua NumResults pushPandocModule datadir = do loadScriptFromDataDir datadir "pandoc.lua" - addFunction "_pipe" pipeFn - addFunction "_read" readDoc + addFunction "read" readDoc + addFunction "pipe" pipeFn addFunction "sha1" sha1HashFn addFunction "walk_block" walkBlock addFunction "walk_inline" walkInline @@ -80,19 +78,23 @@ walkInline = walkElement walkBlock :: Block -> LuaFilter -> Lua Block walkBlock = walkElement -readDoc :: String -> String -> Lua NumResults -readDoc formatSpec content = do +readDoc :: String -> OrNil String -> Lua NumResults +readDoc content formatSpecOrNil = do + let formatSpec = fromMaybe "markdown" (toMaybe formatSpecOrNil) case getReader formatSpec of - Left s -> Lua.push s -- Unknown reader + Left s -> raiseError s -- Unknown reader Right (reader, es) -> case reader of TextReader r -> do res <- liftIO $ runIO $ r def{ readerExtensions = es } (pack content) case res of - Left s -> Lua.push $ show s -- error while reading - Right pd -> Lua.push pd -- success, push Pandoc - _ -> Lua.push "Only string formats are supported at the moment." - return 1 + Right pd -> (1 :: NumResults) <$ Lua.push pd -- success, push Pandoc + Left s -> raiseError (show s) -- error while reading + _ -> raiseError "Only string formats are supported at the moment." + where + raiseError s = do + Lua.push s + fromIntegral <$> Lua.lerror -- -- MediaBag submodule @@ -106,29 +108,64 @@ pushMediaBagModule commonState mediaBagRef = do addFunction "fetch" (fetch commonState mediaBagRef) return 1 -addFunction :: ToHaskellFunction a => String -> a -> Lua () -addFunction name fn = do - Lua.push name - Lua.pushHaskellFunction fn - Lua.rawset (-3) - sha1HashFn :: BL.ByteString -> Lua NumResults sha1HashFn contents = do Lua.push $ showDigest (sha1 contents) return 1 +-- | Pipes input through a command. pipeFn :: String -> [String] -> BL.ByteString -> Lua NumResults pipeFn command args input = do (ec, output) <- liftIO $ pipeProcess Nothing command args input - Lua.push $ case ec of - ExitSuccess -> 0 - ExitFailure n -> n - Lua.push output - return 2 + case ec of + ExitSuccess -> do + Lua.push output + return 1 + ExitFailure n -> do + Lua.push (PipeError command n output) + fromIntegral <$> Lua.lerror + +data PipeError = PipeError + { pipeErrorCommand :: String + , pipeErrorCode :: Int + , pipeErrorOutput :: BL.ByteString + } + +instance FromLuaStack PipeError where + peek idx = + PipeError + <$> (Lua.getfield idx "command" *> Lua.peek (-1) <* Lua.pop 1) + <*> (Lua.getfield idx "error_code" *> Lua.peek (-1) <* Lua.pop 1) + <*> (Lua.getfield idx "output" *> Lua.peek (-1) <* Lua.pop 1) + +instance ToLuaStack PipeError where + push pipeErr = do + Lua.newtable + addValue "command" (pipeErrorCommand pipeErr) + addValue "error_code" (pipeErrorCode pipeErr) + addValue "output" (pipeErrorOutput pipeErr) + pushPipeErrorMetaTable + Lua.setmetatable (-2) + where + pushPipeErrorMetaTable :: Lua () + pushPipeErrorMetaTable = do + v <- Lua.newmetatable "pandoc pipe error" + when v $ addFunction "__tostring" pipeErrorMessage + + pipeErrorMessage :: PipeError -> Lua BL.ByteString + pipeErrorMessage (PipeError cmd errorCode output) = return $ mconcat + [ BSL.pack "Error running " + , BSL.pack cmd + , BSL.pack " (error code " + , BSL.pack $ show errorCode + , BSL.pack "): " + , if output == mempty then BSL.pack "<no output>" else output + ] +-- end: pipe insertMediaFn :: IORef MB.MediaBag -> FilePath @@ -183,14 +220,14 @@ fetch commonState mbRef src = do return 2 -- returns 2 values: contents, mimetype -- --- Helper types and orphan instances +-- Helper types -- newtype OrNil a = OrNil { toMaybe :: Maybe a } instance FromLuaStack a => FromLuaStack (OrNil a) where peek idx = do - noValue <- Lua.isnil idx + noValue <- Lua.isnoneornil idx if noValue then return (OrNil Nothing) else OrNil . Just <$> Lua.peek idx diff --git a/src/Text/Pandoc/Lua/Util.hs b/src/Text/Pandoc/Lua/Util.hs index 5803e62dc..f72ccd7f9 100644 --- a/src/Text/Pandoc/Lua/Util.hs +++ b/src/Text/Pandoc/Lua/Util.hs @@ -32,6 +32,7 @@ module Text.Pandoc.Lua.Util ( adjustIndexBy , getTable , addValue + , addFunction , getRawInt , setRawInt , addRawInt @@ -44,8 +45,8 @@ module Text.Pandoc.Lua.Util import Control.Monad (when) import Data.ByteString.Char8 (unpack) -import Foreign.Lua (FromLuaStack (..), Lua, NumArgs, StackIndex, - ToLuaStack (..), getglobal') +import Foreign.Lua (FromLuaStack (..), ToHaskellFunction, Lua, NumArgs, + StackIndex, ToLuaStack (..), getglobal') import Foreign.Lua.Api (Status, call, pop, rawget, rawgeti, rawset, rawseti) import Text.Pandoc.Class (readDataFile, runIOorExplode, setUserDataDir) @@ -66,13 +67,21 @@ getTable idx key = do rawget (idx `adjustIndexBy` 1) peek (-1) <* pop 1 --- | Add a key-value pair to the table at the top of the stack +-- | Add a key-value pair to the table at the top of the stack. addValue :: (ToLuaStack a, ToLuaStack b) => a -> b -> Lua () addValue key value = do push key push value rawset (-3) +-- | Add a function to the table at the top of the stack, using the given name. +addFunction :: ToHaskellFunction a => String -> a -> Lua () +addFunction name fn = do + Lua.push name + Lua.pushHaskellFunction fn + Lua.wrapHaskellFunction + Lua.rawset (-3) + -- | Get value behind key from table at given index. getRawInt :: FromLuaStack a => StackIndex -> Int -> Lua a getRawInt idx key = -- cgit v1.2.3 From 0405c5b461ee8d9a57eacc5ff2b44fafa5c0637f Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Thu, 21 Dec 2017 15:33:54 +0300 Subject: Muse reader: parse anchors immediately after headings as IDs --- src/Text/Pandoc/Readers/Muse.hs | 14 +++++++++----- src/Text/Pandoc/Writers/Muse.hs | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 1ea78676b..7142c249f 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -31,7 +31,6 @@ Conversion of Muse text to 'Pandoc' document. {- TODO: - Page breaks (five "*") -- Headings with anchors (make it round trip with Muse writer) - Org tables - table.el tables - Images with attributes (floating and width) @@ -241,7 +240,8 @@ header = try $ do guard $ level <= 5 spaceChar content <- trimInlinesF . mconcat <$> manyTill inline eol - attr <- registerHeader ("", [], []) (runF content defaultParserState) + anchorId <- option "" parseAnchor + attr <- registerHeader (anchorId, [], []) (runF content defaultParserState) return $ B.headerWith attr level <$> content example :: PandocMonad m => MuseParser m (F Blocks) @@ -629,14 +629,18 @@ endline = try $ do notFollowedBy blankline returnF B.softbreak -anchor :: PandocMonad m => MuseParser m (F Inlines) -anchor = try $ do +parseAnchor :: PandocMonad m => MuseParser m String +parseAnchor = try $ do getPosition >>= \pos -> guard (sourceColumn pos == 1) char '#' first <- letter rest <- many (letter <|> digit) skipMany spaceChar <|> void newline - let anchorId = first:rest + return $ first:rest + +anchor :: PandocMonad m => MuseParser m (F Inlines) +anchor = try $ do + anchorId <- parseAnchor return $ return $ B.spanWith (anchorId, [], []) mempty footnote :: PandocMonad m => MuseParser m (F Inlines) diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index 545891d97..34936504e 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -229,7 +229,7 @@ blockToMuse (Header level (ident,_,_) inlines) = do else "#" <> text ident <> cr let header' = text $ replicate level '*' return $ blankline <> nowrap (header' <> space <> contents) - <> blankline <> attr' + $$ attr' <> blankline -- https://www.gnu.org/software/emacs-muse/manual/muse.html#Horizontal-Rules-and-Anchors blockToMuse HorizontalRule = return $ blankline $$ "----" $$ blankline blockToMuse (Table caption _ _ headers rows) = do -- cgit v1.2.3 From d035689a0646261d7a4731e39bce7dbf85187773 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Thu, 21 Dec 2017 16:36:29 +0300 Subject: Org writer: do not wrap "-" to avoid accidental bullet lists Also add TODO for ordered lists. --- src/Text/Pandoc/Writers/Org.hs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index e10fcd5ce..43b5b59ee 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -308,13 +308,18 @@ blockListToOrg blocks = vcat <$> mapM blockToOrg blocks inlineListToOrg :: PandocMonad m => [Inline] -> Org m Doc -inlineListToOrg lst = hcat <$> mapM inlineToOrg (fixNotes lst) - where fixNotes [] = [] -- prevent note ref from wrapping, see #4171 - fixNotes (Space : n@Note{} : rest) = - Str " " : n : fixNotes rest - fixNotes (SoftBreak : n@Note{} : rest) = - Str " " : n : fixNotes rest - fixNotes (x : rest) = x : fixNotes rest +inlineListToOrg lst = hcat <$> mapM inlineToOrg (fixMarkers lst) + where fixMarkers [] = [] -- prevent note refs and list markers from wrapping, see #4171 + fixMarkers (Space : x : rest) | shouldFix x = + Str " " : x : fixMarkers rest + fixMarkers (SoftBreak : x : rest) | shouldFix x = + Str " " : x : fixMarkers rest + fixMarkers (x : rest) = x : fixMarkers rest + + shouldFix Note{} = True -- Prevent footnotes + shouldFix (Str "-") = True -- Prevent bullet list items + -- TODO: prevent ordered list items + shouldFix _ = False -- | Convert Pandoc inline element to Org. inlineToOrg :: PandocMonad m => Inline -> Org m Doc -- cgit v1.2.3 From 3c8f0269f9be633453ecdde8771c7d0bee87691a Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Thu, 21 Dec 2017 10:22:58 -0500 Subject: Add pptx to isTextFormat list This is used to check standalone and not writing to the terminal. --- src/Text/Pandoc/App.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index e70b606a9..df4bdc151 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -538,7 +538,7 @@ convertWithOpts opts = do type Transform = Pandoc -> Pandoc isTextFormat :: String -> Bool -isTextFormat s = s `notElem` ["odt","docx","epub2","epub3","epub"] +isTextFormat s = s `notElem` ["odt","docx","epub2","epub3","epub","pptx"] externalFilter :: MonadIO m => ReaderOptions -> FilePath -> [String] -> Pandoc -> m Pandoc -- cgit v1.2.3 From 4e53c7bf55354189a20423c2156963b779f0aa04 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Thu, 21 Dec 2017 10:34:54 -0500 Subject: Don't look for default template file for Powerpoint. When using readerStandalone, this keeps us from looking for a non-existent template pptx file. Closes #4181 --- src/Text/Pandoc/Templates.hs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Templates.hs b/src/Text/Pandoc/Templates.hs index 1ba8d5a05..d4524c333 100644 --- a/src/Text/Pandoc/Templates.hs +++ b/src/Text/Pandoc/Templates.hs @@ -59,6 +59,7 @@ getDefaultTemplate writer = do "json" -> return "" "docx" -> return "" "fb2" -> return "" + "pptx" -> return "" "odt" -> getDefaultTemplate "opendocument" "html" -> getDefaultTemplate "html5" "docbook" -> getDefaultTemplate "docbook5" -- cgit v1.2.3 From 4d0cb0b2fca905dd4d5f0655af1a68a8a7074588 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Thu, 21 Dec 2017 11:09:05 -0500 Subject: Implement basic definition list functionality to PowerPoint writer. These are currently implemented in terms of a Bold para for the terms, and then blockquotes for the definitions. THis can be refined a bit in the future. --- src/Text/Pandoc/Writers/Powerpoint.hs | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index b5f06c581..eb695b2be 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -411,6 +411,15 @@ blockToParagraphs (OrderedList listAttr blksLst) = do , pPropMarginLeft = Nothing }}) $ concatMapM multiParBullet blksLst +blockToParagraphs (DefinitionList entries) = do + let go :: PandocMonad m => ([Inline], [[Block]]) -> P m [Paragraph] + go (ils, blksLst) = do + term <-blockToParagraphs $ Para [Strong ils] + -- For now, we'll treat each definition term as a + -- blockquote. We can extend this further later. + definition <- concatMapM (blockToParagraphs . BlockQuote) blksLst + return $ term ++ definition + concatMapM go entries blockToParagraphs (Div _ blks) = concatMapM blockToParagraphs blks -- TODO blockToParagraphs blk = do -- cgit v1.2.3 From d6c9e4f243438de767a2e3e2a08c5a0eb546023c Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Thu, 21 Dec 2017 11:35:00 -0500 Subject: Add Note state to PowerPoint writer. First step toward implementing notes in pptx writer. --- src/Text/Pandoc/Writers/Powerpoint.hs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index eb695b2be..6f83b5976 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -139,6 +139,7 @@ data WriterState = WriterState { stCurSlideId :: Int -- (FP, Local ID, Global ID, Maybe Mime) , stMediaIds :: M.Map Int [MediaInfo] , stMediaGlobalIds :: M.Map FilePath Int + , stNoteIds :: M.Map Int [Block] } deriving (Show, Eq) instance Default WriterState where @@ -147,6 +148,7 @@ instance Default WriterState where , stLinkIds = mempty , stMediaIds = mempty , stMediaGlobalIds = mempty + , stNoteIds = mempty } type P m = ReaderT WriterEnv (StateT WriterState m) -- cgit v1.2.3 From f76b4fc497706c4939c51138384ac1414b6f8b08 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Thu, 21 Dec 2017 11:45:08 -0500 Subject: PowerPoint writer: Register notes to state. When we encounter a note, we write it to the state directory of notes, and input a superscript. --- src/Text/Pandoc/Writers/Powerpoint.hs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index 6f83b5976..4a6f6a341 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -353,6 +353,14 @@ inlineToParElems (Code _ str) = do inlineToParElems $ Str str inlineToParElems (Math mathtype str) = return [MathElem mathtype (TeXString str)] +inlineToParElems (Note blks) = do + notes <- gets stNoteIds + let maxNoteId = case M.keys notes of + [] -> 0 + lst -> maximum lst + curNoteId = maxNoteId + 1 + modify $ \st -> st { stNoteIds = M.insert curNoteId blks notes } + inlineToParElems $ Superscript [Str $ show curNoteId] inlineToParElems (Span _ ils) = concatMapM inlineToParElems ils inlineToParElems (RawInline _ _) = return [] inlineToParElems _ = return [] -- cgit v1.2.3 From c4f58684ee3a53367efe381db908e6d6664d90ce Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Thu, 21 Dec 2017 12:11:58 -0500 Subject: PowerPoint writer: Implement notes This currently prints all notes on a final slide. Note that at the moment, there is a danger of text overflowing the note slide, since there is no logic for adding further slides. A future commit will shrink the font size on these notes, but that won't take care of the problem altogether. (We might have to implement some sort of clumsy page-breaking logic here based on font size and text-box dimensions, though that seems like a can of worms.) --- src/Text/Pandoc/Writers/Powerpoint.hs | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index 4a6f6a341..c13b32d49 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -564,6 +564,32 @@ blocksToSlide blks = do slideLevel <- asks envSlideLevel blocksToSlide' slideLevel blks +makeNoteEntry :: Int -> [Block] -> [Block] +makeNoteEntry n blks = + let enum = Str (show n ++ ".") + in + case blks of + (Para ils : blks') -> (Para $ enum : Space : ils) : blks' + _ -> (Para [enum]) : blks + +-- Right now, there's no logic for making more than one slide, but I +-- want to leave the option open to make multiple slides if we figure +-- out how to guess at how much space the text of the notes will take +-- up (or if we allow a way for it to be manually controlled). Plus a +-- list will make it easier to put together in the final +-- `blocksToPresentation` function (since we can just add an empty +-- list without checking the state). +makeNotesSlides :: PandocMonad m => P m [Slide] +makeNotesSlides = do + noteIds <- gets stNoteIds + if M.null noteIds + then return [] + else do let hdr = Header 2 nullAttr [Str "Notes"] + blks = concatMap (\(n, bs) -> makeNoteEntry n bs) $ + M.toList noteIds + sld <- blocksToSlide $ hdr : blks + return [sld] + getMetaSlide :: PandocMonad m => P m (Maybe Slide) getMetaSlide = do meta <- asks envMetadata @@ -589,11 +615,13 @@ blocksToPresentation :: PandocMonad m => [Block] -> P m Presentation blocksToPresentation blks = do blksLst <- splitBlocks blks slides <- mapM blocksToSlide blksLst + noteSlides <- makeNotesSlides + let slides' = slides ++ noteSlides metadataslide <- getMetaSlide presSize <- asks envPresentationSize return $ case metadataslide of - Just metadataslide' -> Presentation presSize $ metadataslide' : slides - Nothing -> Presentation presSize slides + Just metadataslide' -> Presentation presSize $ metadataslide' : slides' + Nothing -> Presentation presSize slides' -------------------------------------------------------------------- -- cgit v1.2.3 From 685e90cd4f25e75d80b07b8fa9d3cd63f5999555 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 21 Dec 2017 11:50:55 -0800 Subject: LaTeX reader: Fixed subtle bug in tokenizer. Material following `^^` was dropped if it wasn't a character escape. This only affected invalid LaTeX, so we didn't see it in the wild, but it appeared in a QuickCheck test failure https://travis-ci.org/jgm/pandoc/jobs/319812224 --- src/Text/Pandoc/Readers/LaTeX.hs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index c82697704..e6ae4c11b 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -376,8 +376,9 @@ totoks pos t = | d < '\128' -> Tok pos Esc1 (T.pack ['^','^',d]) : totoks (incSourceColumn pos 3) rest'' - _ -> [Tok pos Symbol ("^"), - Tok (incSourceColumn pos 1) Symbol ("^")] + _ -> Tok pos Symbol ("^") : + Tok (incSourceColumn pos 1) Symbol ("^") : + totoks (incSourceColumn pos 2) rest' _ -> Tok pos Symbol ("^") : totoks (incSourceColumn pos 1) rest | otherwise -> -- cgit v1.2.3 From ab3c5065847f60f0c3f3b097331a28d27716dc8d Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Thu, 21 Dec 2017 21:37:40 +0100 Subject: Lua modules: move to dedicated submodule The Haskell module defining the Lua `pandoc` module is moved to Text.Pandoc.Lua.Module.Pandoc. Change: minor --- src/Text/Pandoc/Lua.hs | 6 +- src/Text/Pandoc/Lua/Module/Pandoc.hs | 213 ++++++++++++++++++++++++++++++++ src/Text/Pandoc/Lua/Packages.hs | 5 +- src/Text/Pandoc/Lua/PandocModule.hs | 233 ----------------------------------- src/Text/Pandoc/Lua/Util.hs | 22 +++- 5 files changed, 241 insertions(+), 238 deletions(-) create mode 100644 src/Text/Pandoc/Lua/Module/Pandoc.hs delete mode 100644 src/Text/Pandoc/Lua/PandocModule.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index a56e89511..ee259e3fd 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -39,7 +39,7 @@ import Text.Pandoc.Class (PandocIO) import Text.Pandoc.Definition (Pandoc) import Text.Pandoc.Lua.Filter (LuaFilter, walkMWithLuaFilter) import Text.Pandoc.Lua.Init (runPandocLua) -import Text.Pandoc.Lua.PandocModule (pushPandocModule) -- TODO: remove +import Text.Pandoc.Lua.Module.Pandoc (pushModule) -- TODO: remove import qualified Foreign.Lua as Lua -- | Run the Lua filter in @filterPath@ for a transformation to target @@ -81,3 +81,7 @@ pushGlobalFilter = do runAll :: [LuaFilter] -> Pandoc -> Lua Pandoc runAll = foldr ((>=>) . walkMWithLuaFilter) return + +-- | DEPRECATED: Push the pandoc module to the Lua Stack. +pushPandocModule :: Maybe FilePath -> Lua Lua.NumResults +pushPandocModule = pushModule diff --git a/src/Text/Pandoc/Lua/Module/Pandoc.hs b/src/Text/Pandoc/Lua/Module/Pandoc.hs new file mode 100644 index 000000000..5e00a9252 --- /dev/null +++ b/src/Text/Pandoc/Lua/Module/Pandoc.hs @@ -0,0 +1,213 @@ +{- +Copyright © 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> + +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 +-} +{-# LANGUAGE FlexibleContexts #-} +{- | + Module : Text.Pandoc.Lua.Module.Pandoc + Copyright : Copyright © 2017 Albert Krewinkel + License : GNU GPL, version 2 or above + + Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> + Stability : alpha + +Pandoc module for lua. +-} +module Text.Pandoc.Lua.Module.Pandoc + ( pushModule + , pushMediaBagModule + ) where + +import Control.Monad (when, zipWithM_) +import Data.Default (Default (..)) +import Data.Digest.Pure.SHA (sha1, showDigest) +import Data.IORef (IORef, modifyIORef', readIORef) +import Data.Maybe (fromMaybe) +import Data.Text (pack) +import Foreign.Lua (ToLuaStack, FromLuaStack, Lua, NumResults, liftIO) +import System.Exit (ExitCode (..)) +import Text.Pandoc.Class (CommonState (..), fetchItem, putCommonState, + runIO, runIOorExplode, setMediaBag) +import Text.Pandoc.Definition (Block, Inline) +import Text.Pandoc.Lua.Filter (walkInlines, walkBlocks, LuaFilter) +import Text.Pandoc.Lua.StackInstances () +import Text.Pandoc.Lua.Util (OrNil (toMaybe), addFunction, addValue, + loadScriptFromDataDir, raiseError) +import Text.Pandoc.Walk (Walkable) +import Text.Pandoc.MIME (MimeType) +import Text.Pandoc.Options (ReaderOptions (readerExtensions)) +import Text.Pandoc.Process (pipeProcess) +import Text.Pandoc.Readers (Reader (..), getReader) + +import qualified Data.ByteString.Lazy as BL +import qualified Data.ByteString.Lazy.Char8 as BSL +import qualified Foreign.Lua as Lua +import qualified Text.Pandoc.MediaBag as MB + +-- | Push the "pandoc" on the lua stack. Requires the `list` module to be +-- loaded. +pushModule :: Maybe FilePath -> Lua NumResults +pushModule datadir = do + loadScriptFromDataDir datadir "pandoc.lua" + addFunction "read" readDoc + addFunction "pipe" pipeFn + addFunction "sha1" sha1HashFn + addFunction "walk_block" walkBlock + addFunction "walk_inline" walkInline + return 1 + +walkElement :: (ToLuaStack a, Walkable [Inline] a, Walkable [Block] a) + => a -> LuaFilter -> Lua a +walkElement x f = walkInlines f x >>= walkBlocks f + +walkInline :: Inline -> LuaFilter -> Lua Inline +walkInline = walkElement + +walkBlock :: Block -> LuaFilter -> Lua Block +walkBlock = walkElement + +readDoc :: String -> OrNil String -> Lua NumResults +readDoc content formatSpecOrNil = do + let formatSpec = fromMaybe "markdown" (toMaybe formatSpecOrNil) + case getReader formatSpec of + Left s -> raiseError s -- Unknown reader + Right (reader, es) -> + case reader of + TextReader r -> do + res <- liftIO $ runIO $ r def{ readerExtensions = es } (pack content) + case res of + Right pd -> (1 :: NumResults) <$ Lua.push pd -- success, push Pandoc + Left s -> raiseError (show s) -- error while reading + _ -> raiseError "Only string formats are supported at the moment." + +-- +-- MediaBag submodule +-- +pushMediaBagModule :: CommonState -> IORef MB.MediaBag -> Lua NumResults +pushMediaBagModule commonState mediaBagRef = do + Lua.newtable + addFunction "insert" (insertMediaFn mediaBagRef) + addFunction "lookup" (lookupMediaFn mediaBagRef) + addFunction "list" (mediaDirectoryFn mediaBagRef) + addFunction "fetch" (fetch commonState mediaBagRef) + return 1 + +sha1HashFn :: BL.ByteString + -> Lua NumResults +sha1HashFn contents = do + Lua.push $ showDigest (sha1 contents) + return 1 + +-- | Pipes input through a command. +pipeFn :: String + -> [String] + -> BL.ByteString + -> Lua NumResults +pipeFn command args input = do + (ec, output) <- liftIO $ pipeProcess Nothing command args input + case ec of + ExitSuccess -> 1 <$ Lua.push output + ExitFailure n -> raiseError (PipeError command n output) + +data PipeError = PipeError + { pipeErrorCommand :: String + , pipeErrorCode :: Int + , pipeErrorOutput :: BL.ByteString + } + +instance FromLuaStack PipeError where + peek idx = + PipeError + <$> (Lua.getfield idx "command" *> Lua.peek (-1) <* Lua.pop 1) + <*> (Lua.getfield idx "error_code" *> Lua.peek (-1) <* Lua.pop 1) + <*> (Lua.getfield idx "output" *> Lua.peek (-1) <* Lua.pop 1) + +instance ToLuaStack PipeError where + push pipeErr = do + Lua.newtable + addValue "command" (pipeErrorCommand pipeErr) + addValue "error_code" (pipeErrorCode pipeErr) + addValue "output" (pipeErrorOutput pipeErr) + pushPipeErrorMetaTable + Lua.setmetatable (-2) + where + pushPipeErrorMetaTable :: Lua () + pushPipeErrorMetaTable = do + v <- Lua.newmetatable "pandoc pipe error" + when v $ addFunction "__tostring" pipeErrorMessage + + pipeErrorMessage :: PipeError -> Lua BL.ByteString + pipeErrorMessage (PipeError cmd errorCode output) = return $ mconcat + [ BSL.pack "Error running " + , BSL.pack cmd + , BSL.pack " (error code " + , BSL.pack $ show errorCode + , BSL.pack "): " + , if output == mempty then BSL.pack "<no output>" else output + ] +-- end: pipe + +insertMediaFn :: IORef MB.MediaBag + -> FilePath + -> OrNil MimeType + -> BL.ByteString + -> Lua NumResults +insertMediaFn mbRef fp nilOrMime contents = do + liftIO . modifyIORef' mbRef $ + MB.insertMedia fp (toMaybe nilOrMime) contents + return 0 + +lookupMediaFn :: IORef MB.MediaBag + -> FilePath + -> Lua NumResults +lookupMediaFn mbRef fp = do + res <- MB.lookupMedia fp <$> liftIO (readIORef mbRef) + case res of + Nothing -> Lua.pushnil *> return 1 + Just (mimeType, contents) -> do + Lua.push mimeType + Lua.push contents + return 2 + +mediaDirectoryFn :: IORef MB.MediaBag + -> Lua NumResults +mediaDirectoryFn mbRef = do + dirContents <- MB.mediaDirectory <$> liftIO (readIORef mbRef) + Lua.newtable + zipWithM_ addEntry [1..] dirContents + return 1 + where + addEntry :: Int -> (FilePath, MimeType, Int) -> Lua () + addEntry idx (fp, mimeType, contentLength) = do + Lua.newtable + Lua.push "path" *> Lua.push fp *> Lua.rawset (-3) + Lua.push "type" *> Lua.push mimeType *> Lua.rawset (-3) + Lua.push "length" *> Lua.push contentLength *> Lua.rawset (-3) + Lua.rawseti (-2) idx + +fetch :: CommonState + -> IORef MB.MediaBag + -> String + -> Lua NumResults +fetch commonState mbRef src = do + mediaBag <- liftIO $ readIORef mbRef + (bs, mimeType) <- liftIO . runIOorExplode $ do + putCommonState commonState + setMediaBag mediaBag + fetchItem src + Lua.push $ fromMaybe "" mimeType + Lua.push bs + return 2 -- returns 2 values: contents, mimetype diff --git a/src/Text/Pandoc/Lua/Packages.hs b/src/Text/Pandoc/Lua/Packages.hs index b2dbff496..e9173739f 100644 --- a/src/Text/Pandoc/Lua/Packages.hs +++ b/src/Text/Pandoc/Lua/Packages.hs @@ -38,10 +38,11 @@ import Data.IORef (IORef) import Foreign.Lua (Lua, NumResults, liftIO) import Text.Pandoc.Class (CommonState, readDataFile, runIO, setUserDataDir) import Text.Pandoc.MediaBag (MediaBag) -import Text.Pandoc.Lua.PandocModule (pushPandocModule, pushMediaBagModule) +import Text.Pandoc.Lua.Module.Pandoc (pushMediaBagModule) import Text.Pandoc.Lua.Util (dostring') import qualified Foreign.Lua as Lua +import Text.Pandoc.Lua.Module.Pandoc as Pandoc -- | Parameters used to create lua packages/modules. data LuaPackageParams = LuaPackageParams @@ -72,7 +73,7 @@ pandocPackageSearcher :: LuaPackageParams -> String -> Lua NumResults pandocPackageSearcher luaPkgParams pkgName = case pkgName of "pandoc" -> let datadir = luaPkgDataDir luaPkgParams - in pushWrappedHsFun (pushPandocModule datadir) + in pushWrappedHsFun (Pandoc.pushModule datadir) "pandoc.mediabag" -> let st = luaPkgCommonState luaPkgParams mbRef = luaPkgMediaBag luaPkgParams in pushWrappedHsFun (pushMediaBagModule st mbRef) diff --git a/src/Text/Pandoc/Lua/PandocModule.hs b/src/Text/Pandoc/Lua/PandocModule.hs deleted file mode 100644 index 4a3e4d354..000000000 --- a/src/Text/Pandoc/Lua/PandocModule.hs +++ /dev/null @@ -1,233 +0,0 @@ -{- -Copyright © 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> - -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 --} -{-# LANGUAGE FlexibleContexts #-} -{- | - Module : Text.Pandoc.Lua.PandocModule - Copyright : Copyright © 2017 Albert Krewinkel - License : GNU GPL, version 2 or above - - Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> - Stability : alpha - -Pandoc module for lua. --} -module Text.Pandoc.Lua.PandocModule - ( pushPandocModule - , pushMediaBagModule - ) where - -import Control.Monad (when, zipWithM_) -import Data.Default (Default (..)) -import Data.Digest.Pure.SHA (sha1, showDigest) -import Data.IORef (IORef, modifyIORef', readIORef) -import Data.Maybe (fromMaybe) -import Data.Text (pack) -import Foreign.Lua (ToLuaStack, FromLuaStack, Lua, NumResults, liftIO) -import System.Exit (ExitCode (..)) -import Text.Pandoc.Class (CommonState (..), fetchItem, putCommonState, - runIO, runIOorExplode, setMediaBag) -import Text.Pandoc.Definition (Block, Inline) -import Text.Pandoc.Lua.Filter (walkInlines, walkBlocks, LuaFilter) -import Text.Pandoc.Lua.StackInstances () -import Text.Pandoc.Lua.Util (addFunction, addValue, loadScriptFromDataDir) -import Text.Pandoc.Walk (Walkable) -import Text.Pandoc.MIME (MimeType) -import Text.Pandoc.Options (ReaderOptions (readerExtensions)) -import Text.Pandoc.Process (pipeProcess) -import Text.Pandoc.Readers (Reader (..), getReader) - -import qualified Data.ByteString.Lazy as BL -import qualified Data.ByteString.Lazy.Char8 as BSL -import qualified Foreign.Lua as Lua -import qualified Text.Pandoc.MediaBag as MB - --- | Push the "pandoc" on the lua stack. Requires the `list` module to be --- loaded. -pushPandocModule :: Maybe FilePath -> Lua NumResults -pushPandocModule datadir = do - loadScriptFromDataDir datadir "pandoc.lua" - addFunction "read" readDoc - addFunction "pipe" pipeFn - addFunction "sha1" sha1HashFn - addFunction "walk_block" walkBlock - addFunction "walk_inline" walkInline - return 1 - -walkElement :: (ToLuaStack a, Walkable [Inline] a, Walkable [Block] a) - => a -> LuaFilter -> Lua a -walkElement x f = walkInlines f x >>= walkBlocks f - -walkInline :: Inline -> LuaFilter -> Lua Inline -walkInline = walkElement - -walkBlock :: Block -> LuaFilter -> Lua Block -walkBlock = walkElement - -readDoc :: String -> OrNil String -> Lua NumResults -readDoc content formatSpecOrNil = do - let formatSpec = fromMaybe "markdown" (toMaybe formatSpecOrNil) - case getReader formatSpec of - Left s -> raiseError s -- Unknown reader - Right (reader, es) -> - case reader of - TextReader r -> do - res <- liftIO $ runIO $ r def{ readerExtensions = es } (pack content) - case res of - Right pd -> (1 :: NumResults) <$ Lua.push pd -- success, push Pandoc - Left s -> raiseError (show s) -- error while reading - _ -> raiseError "Only string formats are supported at the moment." - where - raiseError s = do - Lua.push s - fromIntegral <$> Lua.lerror - --- --- MediaBag submodule --- -pushMediaBagModule :: CommonState -> IORef MB.MediaBag -> Lua NumResults -pushMediaBagModule commonState mediaBagRef = do - Lua.newtable - addFunction "insert" (insertMediaFn mediaBagRef) - addFunction "lookup" (lookupMediaFn mediaBagRef) - addFunction "list" (mediaDirectoryFn mediaBagRef) - addFunction "fetch" (fetch commonState mediaBagRef) - return 1 - -sha1HashFn :: BL.ByteString - -> Lua NumResults -sha1HashFn contents = do - Lua.push $ showDigest (sha1 contents) - return 1 - --- | Pipes input through a command. -pipeFn :: String - -> [String] - -> BL.ByteString - -> Lua NumResults -pipeFn command args input = do - (ec, output) <- liftIO $ pipeProcess Nothing command args input - case ec of - ExitSuccess -> do - Lua.push output - return 1 - ExitFailure n -> do - Lua.push (PipeError command n output) - fromIntegral <$> Lua.lerror - -data PipeError = PipeError - { pipeErrorCommand :: String - , pipeErrorCode :: Int - , pipeErrorOutput :: BL.ByteString - } - -instance FromLuaStack PipeError where - peek idx = - PipeError - <$> (Lua.getfield idx "command" *> Lua.peek (-1) <* Lua.pop 1) - <*> (Lua.getfield idx "error_code" *> Lua.peek (-1) <* Lua.pop 1) - <*> (Lua.getfield idx "output" *> Lua.peek (-1) <* Lua.pop 1) - -instance ToLuaStack PipeError where - push pipeErr = do - Lua.newtable - addValue "command" (pipeErrorCommand pipeErr) - addValue "error_code" (pipeErrorCode pipeErr) - addValue "output" (pipeErrorOutput pipeErr) - pushPipeErrorMetaTable - Lua.setmetatable (-2) - where - pushPipeErrorMetaTable :: Lua () - pushPipeErrorMetaTable = do - v <- Lua.newmetatable "pandoc pipe error" - when v $ addFunction "__tostring" pipeErrorMessage - - pipeErrorMessage :: PipeError -> Lua BL.ByteString - pipeErrorMessage (PipeError cmd errorCode output) = return $ mconcat - [ BSL.pack "Error running " - , BSL.pack cmd - , BSL.pack " (error code " - , BSL.pack $ show errorCode - , BSL.pack "): " - , if output == mempty then BSL.pack "<no output>" else output - ] --- end: pipe - -insertMediaFn :: IORef MB.MediaBag - -> FilePath - -> OrNil MimeType - -> BL.ByteString - -> Lua NumResults -insertMediaFn mbRef fp nilOrMime contents = do - liftIO . modifyIORef' mbRef $ - MB.insertMedia fp (toMaybe nilOrMime) contents - return 0 - -lookupMediaFn :: IORef MB.MediaBag - -> FilePath - -> Lua NumResults -lookupMediaFn mbRef fp = do - res <- MB.lookupMedia fp <$> liftIO (readIORef mbRef) - case res of - Nothing -> Lua.pushnil *> return 1 - Just (mimeType, contents) -> do - Lua.push mimeType - Lua.push contents - return 2 - -mediaDirectoryFn :: IORef MB.MediaBag - -> Lua NumResults -mediaDirectoryFn mbRef = do - dirContents <- MB.mediaDirectory <$> liftIO (readIORef mbRef) - Lua.newtable - zipWithM_ addEntry [1..] dirContents - return 1 - where - addEntry :: Int -> (FilePath, MimeType, Int) -> Lua () - addEntry idx (fp, mimeType, contentLength) = do - Lua.newtable - Lua.push "path" *> Lua.push fp *> Lua.rawset (-3) - Lua.push "type" *> Lua.push mimeType *> Lua.rawset (-3) - Lua.push "length" *> Lua.push contentLength *> Lua.rawset (-3) - Lua.rawseti (-2) idx - -fetch :: CommonState - -> IORef MB.MediaBag - -> String - -> Lua NumResults -fetch commonState mbRef src = do - mediaBag <- liftIO $ readIORef mbRef - (bs, mimeType) <- liftIO . runIOorExplode $ do - putCommonState commonState - setMediaBag mediaBag - fetchItem src - Lua.push $ fromMaybe "" mimeType - Lua.push bs - return 2 -- returns 2 values: contents, mimetype - --- --- Helper types --- - -newtype OrNil a = OrNil { toMaybe :: Maybe a } - -instance FromLuaStack a => FromLuaStack (OrNil a) where - peek idx = do - noValue <- Lua.isnoneornil idx - if noValue - then return (OrNil Nothing) - else OrNil . Just <$> Lua.peek idx diff --git a/src/Text/Pandoc/Lua/Util.hs b/src/Text/Pandoc/Lua/Util.hs index f72ccd7f9..e688ad255 100644 --- a/src/Text/Pandoc/Lua/Util.hs +++ b/src/Text/Pandoc/Lua/Util.hs @@ -36,6 +36,8 @@ module Text.Pandoc.Lua.Util , getRawInt , setRawInt , addRawInt + , raiseError + , OrNil (..) , PushViaCall , pushViaCall , pushViaConstructor @@ -45,8 +47,8 @@ module Text.Pandoc.Lua.Util import Control.Monad (when) import Data.ByteString.Char8 (unpack) -import Foreign.Lua (FromLuaStack (..), ToHaskellFunction, Lua, NumArgs, - StackIndex, ToLuaStack (..), getglobal') +import Foreign.Lua (FromLuaStack (..), NumResults, Lua, NumArgs, StackIndex, + ToLuaStack (..), ToHaskellFunction, getglobal') import Foreign.Lua.Api (Status, call, pop, rawget, rawgeti, rawset, rawseti) import Text.Pandoc.Class (readDataFile, runIOorExplode, setUserDataDir) @@ -99,6 +101,22 @@ setRawInt idx key value = do addRawInt :: ToLuaStack a => Int -> a -> Lua () addRawInt = setRawInt (-1) +raiseError :: ToLuaStack a => a -> Lua NumResults +raiseError e = do + Lua.push e + fromIntegral <$> Lua.lerror + +-- | Newtype wrapper intended to be used for optional Lua values. Nesting this +-- type is strongly discouraged and will likely lead to a wrong result. +newtype OrNil a = OrNil { toMaybe :: Maybe a } + +instance FromLuaStack a => FromLuaStack (OrNil a) where + peek idx = do + noValue <- Lua.isnoneornil idx + if noValue + then return (OrNil Nothing) + else OrNil . Just <$> Lua.peek idx + -- | Helper class for pushing a single value to the stack via a lua function. -- See @pushViaCall@. class PushViaCall a where -- cgit v1.2.3 From 5ad719c1fb6dbf06cbf4f48e57ae4a6d187e0a5e Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Thu, 21 Dec 2017 22:17:37 +0100 Subject: Lua modules: make a Haskell module for each Lua module Definitions for the `pandoc.mediabag` modules are moved to a separate Haskell module. Change: minor --- src/Text/Pandoc/Lua/Module/MediaBag.hs | 108 +++++++++++++++++++++++++++++++++ src/Text/Pandoc/Lua/Module/Pandoc.hs | 74 +--------------------- src/Text/Pandoc/Lua/Packages.hs | 4 +- 3 files changed, 112 insertions(+), 74 deletions(-) create mode 100644 src/Text/Pandoc/Lua/Module/MediaBag.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/Module/MediaBag.hs b/src/Text/Pandoc/Lua/Module/MediaBag.hs new file mode 100644 index 000000000..33c441c99 --- /dev/null +++ b/src/Text/Pandoc/Lua/Module/MediaBag.hs @@ -0,0 +1,108 @@ +{- +Copyright © 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> + +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.Lua.Module.MediaBag + Copyright : Copyright © 2017 Albert Krewinkel + License : GNU GPL, version 2 or above + + Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> + Stability : alpha + +The lua module @pandoc.mediabag@. +-} +module Text.Pandoc.Lua.Module.MediaBag + ( pushModule + ) where + +import Control.Monad (zipWithM_) +import Data.IORef (IORef, modifyIORef', readIORef) +import Data.Maybe (fromMaybe) +import Foreign.Lua (Lua, NumResults, liftIO) +import Text.Pandoc.Class (CommonState (..), fetchItem, putCommonState, + runIOorExplode, setMediaBag) +import Text.Pandoc.Lua.StackInstances () +import Text.Pandoc.Lua.Util (OrNil (toMaybe), addFunction) +import Text.Pandoc.MIME (MimeType) + +import qualified Data.ByteString.Lazy as BL +import qualified Foreign.Lua as Lua +import qualified Text.Pandoc.MediaBag as MB + +-- +-- MediaBag submodule +-- +pushModule :: CommonState -> IORef MB.MediaBag -> Lua NumResults +pushModule commonState mediaBagRef = do + Lua.newtable + addFunction "insert" (insertMediaFn mediaBagRef) + addFunction "lookup" (lookupMediaFn mediaBagRef) + addFunction "list" (mediaDirectoryFn mediaBagRef) + addFunction "fetch" (fetch commonState mediaBagRef) + return 1 + +insertMediaFn :: IORef MB.MediaBag + -> FilePath + -> OrNil MimeType + -> BL.ByteString + -> Lua NumResults +insertMediaFn mbRef fp nilOrMime contents = do + liftIO . modifyIORef' mbRef $ + MB.insertMedia fp (toMaybe nilOrMime) contents + return 0 + +lookupMediaFn :: IORef MB.MediaBag + -> FilePath + -> Lua NumResults +lookupMediaFn mbRef fp = do + res <- MB.lookupMedia fp <$> liftIO (readIORef mbRef) + case res of + Nothing -> Lua.pushnil *> return 1 + Just (mimeType, contents) -> do + Lua.push mimeType + Lua.push contents + return 2 + +mediaDirectoryFn :: IORef MB.MediaBag + -> Lua NumResults +mediaDirectoryFn mbRef = do + dirContents <- MB.mediaDirectory <$> liftIO (readIORef mbRef) + Lua.newtable + zipWithM_ addEntry [1..] dirContents + return 1 + where + addEntry :: Int -> (FilePath, MimeType, Int) -> Lua () + addEntry idx (fp, mimeType, contentLength) = do + Lua.newtable + Lua.push "path" *> Lua.push fp *> Lua.rawset (-3) + Lua.push "type" *> Lua.push mimeType *> Lua.rawset (-3) + Lua.push "length" *> Lua.push contentLength *> Lua.rawset (-3) + Lua.rawseti (-2) idx + +fetch :: CommonState + -> IORef MB.MediaBag + -> String + -> Lua NumResults +fetch commonState mbRef src = do + mediaBag <- liftIO $ readIORef mbRef + (bs, mimeType) <- liftIO . runIOorExplode $ do + putCommonState commonState + setMediaBag mediaBag + fetchItem src + Lua.push $ fromMaybe "" mimeType + Lua.push bs + return 2 -- returns 2 values: contents, mimetype diff --git a/src/Text/Pandoc/Lua/Module/Pandoc.hs b/src/Text/Pandoc/Lua/Module/Pandoc.hs index 5e00a9252..ed80b9715 100644 --- a/src/Text/Pandoc/Lua/Module/Pandoc.hs +++ b/src/Text/Pandoc/Lua/Module/Pandoc.hs @@ -28,26 +28,22 @@ Pandoc module for lua. -} module Text.Pandoc.Lua.Module.Pandoc ( pushModule - , pushMediaBagModule ) where -import Control.Monad (when, zipWithM_) +import Control.Monad (when) import Data.Default (Default (..)) import Data.Digest.Pure.SHA (sha1, showDigest) -import Data.IORef (IORef, modifyIORef', readIORef) import Data.Maybe (fromMaybe) import Data.Text (pack) import Foreign.Lua (ToLuaStack, FromLuaStack, Lua, NumResults, liftIO) import System.Exit (ExitCode (..)) -import Text.Pandoc.Class (CommonState (..), fetchItem, putCommonState, - runIO, runIOorExplode, setMediaBag) +import Text.Pandoc.Class (runIO) import Text.Pandoc.Definition (Block, Inline) import Text.Pandoc.Lua.Filter (walkInlines, walkBlocks, LuaFilter) import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Lua.Util (OrNil (toMaybe), addFunction, addValue, loadScriptFromDataDir, raiseError) import Text.Pandoc.Walk (Walkable) -import Text.Pandoc.MIME (MimeType) import Text.Pandoc.Options (ReaderOptions (readerExtensions)) import Text.Pandoc.Process (pipeProcess) import Text.Pandoc.Readers (Reader (..), getReader) @@ -55,7 +51,6 @@ import Text.Pandoc.Readers (Reader (..), getReader) import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Lazy.Char8 as BSL import qualified Foreign.Lua as Lua -import qualified Text.Pandoc.MediaBag as MB -- | Push the "pandoc" on the lua stack. Requires the `list` module to be -- loaded. @@ -93,18 +88,6 @@ readDoc content formatSpecOrNil = do Left s -> raiseError (show s) -- error while reading _ -> raiseError "Only string formats are supported at the moment." --- --- MediaBag submodule --- -pushMediaBagModule :: CommonState -> IORef MB.MediaBag -> Lua NumResults -pushMediaBagModule commonState mediaBagRef = do - Lua.newtable - addFunction "insert" (insertMediaFn mediaBagRef) - addFunction "lookup" (lookupMediaFn mediaBagRef) - addFunction "list" (mediaDirectoryFn mediaBagRef) - addFunction "fetch" (fetch commonState mediaBagRef) - return 1 - sha1HashFn :: BL.ByteString -> Lua NumResults sha1HashFn contents = do @@ -158,56 +141,3 @@ instance ToLuaStack PipeError where , BSL.pack "): " , if output == mempty then BSL.pack "<no output>" else output ] --- end: pipe - -insertMediaFn :: IORef MB.MediaBag - -> FilePath - -> OrNil MimeType - -> BL.ByteString - -> Lua NumResults -insertMediaFn mbRef fp nilOrMime contents = do - liftIO . modifyIORef' mbRef $ - MB.insertMedia fp (toMaybe nilOrMime) contents - return 0 - -lookupMediaFn :: IORef MB.MediaBag - -> FilePath - -> Lua NumResults -lookupMediaFn mbRef fp = do - res <- MB.lookupMedia fp <$> liftIO (readIORef mbRef) - case res of - Nothing -> Lua.pushnil *> return 1 - Just (mimeType, contents) -> do - Lua.push mimeType - Lua.push contents - return 2 - -mediaDirectoryFn :: IORef MB.MediaBag - -> Lua NumResults -mediaDirectoryFn mbRef = do - dirContents <- MB.mediaDirectory <$> liftIO (readIORef mbRef) - Lua.newtable - zipWithM_ addEntry [1..] dirContents - return 1 - where - addEntry :: Int -> (FilePath, MimeType, Int) -> Lua () - addEntry idx (fp, mimeType, contentLength) = do - Lua.newtable - Lua.push "path" *> Lua.push fp *> Lua.rawset (-3) - Lua.push "type" *> Lua.push mimeType *> Lua.rawset (-3) - Lua.push "length" *> Lua.push contentLength *> Lua.rawset (-3) - Lua.rawseti (-2) idx - -fetch :: CommonState - -> IORef MB.MediaBag - -> String - -> Lua NumResults -fetch commonState mbRef src = do - mediaBag <- liftIO $ readIORef mbRef - (bs, mimeType) <- liftIO . runIOorExplode $ do - putCommonState commonState - setMediaBag mediaBag - fetchItem src - Lua.push $ fromMaybe "" mimeType - Lua.push bs - return 2 -- returns 2 values: contents, mimetype diff --git a/src/Text/Pandoc/Lua/Packages.hs b/src/Text/Pandoc/Lua/Packages.hs index e9173739f..03847c979 100644 --- a/src/Text/Pandoc/Lua/Packages.hs +++ b/src/Text/Pandoc/Lua/Packages.hs @@ -38,11 +38,11 @@ import Data.IORef (IORef) import Foreign.Lua (Lua, NumResults, liftIO) import Text.Pandoc.Class (CommonState, readDataFile, runIO, setUserDataDir) import Text.Pandoc.MediaBag (MediaBag) -import Text.Pandoc.Lua.Module.Pandoc (pushMediaBagModule) import Text.Pandoc.Lua.Util (dostring') import qualified Foreign.Lua as Lua import Text.Pandoc.Lua.Module.Pandoc as Pandoc +import Text.Pandoc.Lua.Module.MediaBag as MediaBag -- | Parameters used to create lua packages/modules. data LuaPackageParams = LuaPackageParams @@ -76,7 +76,7 @@ pandocPackageSearcher luaPkgParams pkgName = in pushWrappedHsFun (Pandoc.pushModule datadir) "pandoc.mediabag" -> let st = luaPkgCommonState luaPkgParams mbRef = luaPkgMediaBag luaPkgParams - in pushWrappedHsFun (pushMediaBagModule st mbRef) + in pushWrappedHsFun (MediaBag.pushModule st mbRef) _ -> searchPureLuaLoader where pushWrappedHsFun f = do -- cgit v1.2.3 From bd3ea723717b54e3853487bee7a48947fb73b68a Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Thu, 21 Dec 2017 22:30:59 +0100 Subject: Lua modules: added pandoc.utils module A new module `pandoc.utils` has been created. It holds utility functions like `sha1`, which was moved from the main `pandoc` module. --- src/Text/Pandoc/Lua/Module/Pandoc.hs | 8 ------ src/Text/Pandoc/Lua/Module/Utils.hs | 50 ++++++++++++++++++++++++++++++++++++ src/Text/Pandoc/Lua/Packages.hs | 2 ++ 3 files changed, 52 insertions(+), 8 deletions(-) create mode 100644 src/Text/Pandoc/Lua/Module/Utils.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/Module/Pandoc.hs b/src/Text/Pandoc/Lua/Module/Pandoc.hs index ed80b9715..5b8714e07 100644 --- a/src/Text/Pandoc/Lua/Module/Pandoc.hs +++ b/src/Text/Pandoc/Lua/Module/Pandoc.hs @@ -32,7 +32,6 @@ module Text.Pandoc.Lua.Module.Pandoc import Control.Monad (when) import Data.Default (Default (..)) -import Data.Digest.Pure.SHA (sha1, showDigest) import Data.Maybe (fromMaybe) import Data.Text (pack) import Foreign.Lua (ToLuaStack, FromLuaStack, Lua, NumResults, liftIO) @@ -59,7 +58,6 @@ pushModule datadir = do loadScriptFromDataDir datadir "pandoc.lua" addFunction "read" readDoc addFunction "pipe" pipeFn - addFunction "sha1" sha1HashFn addFunction "walk_block" walkBlock addFunction "walk_inline" walkInline return 1 @@ -88,12 +86,6 @@ readDoc content formatSpecOrNil = do Left s -> raiseError (show s) -- error while reading _ -> raiseError "Only string formats are supported at the moment." -sha1HashFn :: BL.ByteString - -> Lua NumResults -sha1HashFn contents = do - Lua.push $ showDigest (sha1 contents) - return 1 - -- | Pipes input through a command. pipeFn :: String -> [String] diff --git a/src/Text/Pandoc/Lua/Module/Utils.hs b/src/Text/Pandoc/Lua/Module/Utils.hs new file mode 100644 index 000000000..496fdbc0a --- /dev/null +++ b/src/Text/Pandoc/Lua/Module/Utils.hs @@ -0,0 +1,50 @@ +{- +Copyright © 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> + +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.Lua.Module.Utils + Copyright : Copyright © 2017 Albert Krewinkel + License : GNU GPL, version 2 or above + + Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> + Stability : alpha + +Utility module for lua, exposing internal helper functions. +-} +module Text.Pandoc.Lua.Module.Utils + ( pushModule + ) where + +import Data.Digest.Pure.SHA (sha1, showDigest) +import Foreign.Lua (Lua, NumResults) +import Text.Pandoc.Lua.StackInstances () +import Text.Pandoc.Lua.Util (addFunction) + +import qualified Data.ByteString.Lazy as BSL +import qualified Foreign.Lua as Lua + +-- | Push the "pandoc.utils" module to the lua stack. +pushModule :: Lua NumResults +pushModule = do + Lua.newtable + addFunction "sha1" sha1HashFn + return 1 + +-- | Calculate the hash of the given contents. +sha1HashFn :: BSL.ByteString + -> Lua String +sha1HashFn = return . showDigest . sha1 diff --git a/src/Text/Pandoc/Lua/Packages.hs b/src/Text/Pandoc/Lua/Packages.hs index 03847c979..f26c17084 100644 --- a/src/Text/Pandoc/Lua/Packages.hs +++ b/src/Text/Pandoc/Lua/Packages.hs @@ -43,6 +43,7 @@ import Text.Pandoc.Lua.Util (dostring') import qualified Foreign.Lua as Lua import Text.Pandoc.Lua.Module.Pandoc as Pandoc import Text.Pandoc.Lua.Module.MediaBag as MediaBag +import Text.Pandoc.Lua.Module.Utils as Utils -- | Parameters used to create lua packages/modules. data LuaPackageParams = LuaPackageParams @@ -77,6 +78,7 @@ pandocPackageSearcher luaPkgParams pkgName = "pandoc.mediabag" -> let st = luaPkgCommonState luaPkgParams mbRef = luaPkgMediaBag luaPkgParams in pushWrappedHsFun (MediaBag.pushModule st mbRef) + "pandoc.utils" -> pushWrappedHsFun Utils.pushModule _ -> searchPureLuaLoader where pushWrappedHsFun f = do -- cgit v1.2.3 From 5b2c38a07db87318382efbcb94da2c349cb0a589 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Thu, 21 Dec 2017 12:46:38 -0500 Subject: PowerPoint writer: Add ability to force size. This replaces the more specific blockQuote runProp, which only affected the size of blockquotes. We can use this for notes, etc. --- src/Text/Pandoc/Writers/Powerpoint.hs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index c13b32d49..9743b8bc3 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -302,6 +302,7 @@ data RunProps = RunProps { rPropBold :: Bool , rLink :: Maybe (URL, String) , rPropCode :: Bool , rPropBlockQuote :: Bool + , rPropForceSize :: Maybe Pixels } deriving (Show, Eq) instance Default RunProps where @@ -313,6 +314,7 @@ instance Default RunProps where , rLink = Nothing , rPropCode = False , rPropBlockQuote = False + , rPropForceSize = Nothing } -------------------------------------------------- @@ -385,7 +387,7 @@ blockToParagraphs (CodeBlock attr str) = -- TODO: work out the format blockToParagraphs (BlockQuote blks) = local (\r -> r{ envParaProps = (envParaProps r){pPropMarginLeft = Just 100} - , envRunProps = (envRunProps r){rPropBlockQuote = True}})$ + , envRunProps = (envRunProps r){rPropForceSize = Just blockQuoteSize}})$ concatMapM blockToParagraphs blks -- TODO: work out the format blockToParagraphs (RawBlock _ _) = return [] @@ -1098,7 +1100,9 @@ paraElemToElement (Run rpr s) = do let attrs = if rPropCode rpr then [] - else (if rPropBlockQuote rpr then [("sz", (show $ blockQuoteSize * 100))] else []) ++ + else (case rPropForceSize rpr of + Just n -> [("sz", (show $ n * 100))] + Nothing -> []) ++ (if rPropBold rpr then [("b", "1")] else []) ++ (if rPropItalics rpr then [("i", "1")] else []) ++ (case rStrikethrough rpr of -- cgit v1.2.3 From 3c10951023bc9767a0281b861b53e2014e7b350c Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Thu, 21 Dec 2017 16:54:28 -0500 Subject: Change notes to a smaller size. This will allow more to fit on a single slide, and will probably look better. --- src/Text/Pandoc/Writers/Powerpoint.hs | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index 9743b8bc3..7a453ef1f 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -105,6 +105,7 @@ data WriterEnv = WriterEnv { envMetadata :: Meta , envPresentationSize :: PresentationSize , envSlideHasHeader :: Bool , envInList :: Bool + , envInNoteSlide :: Bool } deriving (Show) @@ -120,6 +121,7 @@ instance Default WriterEnv where , envPresentationSize = def , envSlideHasHeader = False , envInList = False + , envInNoteSlide = False } data MediaInfo = MediaInfo { mInfoFilePath :: FilePath @@ -548,12 +550,18 @@ blocksToSlide' lvl ((Header n _ ils) : blks) return $ TitleSlide {titleSlideHeader = hdr} | n == lvl = do hdr <- inlinesToParElems ils - shapes <- blocksToShapes blks + inNoteSlide <- asks envInNoteSlide + shapes <- if inNoteSlide + then forceFontSize noteSize $ blocksToShapes blks + else blocksToShapes blks return $ ContentSlide { contentSlideHeader = hdr , contentSlideContent = shapes } blocksToSlide' _ (blk : blks) = do - shapes <- blocksToShapes (blk : blks) + inNoteSlide <- asks envInNoteSlide + shapes <- if inNoteSlide + then forceFontSize noteSize $ blocksToShapes (blk : blks) + else blocksToShapes (blk : blks) return $ ContentSlide { contentSlideHeader = [] , contentSlideContent = shapes } @@ -574,6 +582,11 @@ makeNoteEntry n blks = (Para ils : blks') -> (Para $ enum : Space : ils) : blks' _ -> (Para [enum]) : blks +forceFontSize :: PandocMonad m => Pixels -> P m a -> P m a +forceFontSize px x = do + rpr <- asks envRunProps + local (\r -> r {envRunProps = rpr{rPropForceSize = Just px}}) x + -- Right now, there's no logic for making more than one slide, but I -- want to leave the option open to make multiple slides if we figure -- out how to guess at how much space the text of the notes will take @@ -582,13 +595,14 @@ makeNoteEntry n blks = -- `blocksToPresentation` function (since we can just add an empty -- list without checking the state). makeNotesSlides :: PandocMonad m => P m [Slide] -makeNotesSlides = do +makeNotesSlides = local (\env -> env{envInNoteSlide=True}) $ do noteIds <- gets stNoteIds if M.null noteIds then return [] else do let hdr = Header 2 nullAttr [Str "Notes"] - blks = concatMap (\(n, bs) -> makeNoteEntry n bs) $ - M.toList noteIds + blks <- return $ + concatMap (\(n, bs) -> makeNoteEntry n bs) $ + M.toList noteIds sld <- blocksToSlide $ hdr : blks return [sld] @@ -1094,6 +1108,9 @@ makePicElement mInfo attr = do blockQuoteSize :: Pixels blockQuoteSize = 20 +noteSize :: Pixels +noteSize = 18 + paraElemToElement :: PandocMonad m => ParaElem -> P m Element paraElemToElement Break = return $ mknode "a:br" [] () paraElemToElement (Run rpr s) = do -- cgit v1.2.3 From d85357139748ea657f030ab314c39e70f56764f4 Mon Sep 17 00:00:00 2001 From: Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com> Date: Wed, 20 Dec 2017 23:55:48 +1300 Subject: Improve support for code language in JATS --- src/Text/Pandoc/Readers/JATS.hs | 21 ++++++++++++++++-- src/Text/Pandoc/Writers/JATS.hs | 47 +++++++++++++++++++++++++---------------- 2 files changed, 48 insertions(+), 20 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/JATS.hs b/src/Text/Pandoc/Readers/JATS.hs index fc71e9a51..851fbec35 100644 --- a/src/Text/Pandoc/Readers/JATS.hs +++ b/src/Text/Pandoc/Readers/JATS.hs @@ -15,6 +15,8 @@ import Text.Pandoc.Options import Text.Pandoc.Shared (underlineSpan, crFilter, safeRead) import Text.TeXMath (readMathML, writeTeX) import Text.XML.Light +import qualified Data.Set as S (fromList, member) +import Data.Set ((\\)) type JATS m = StateT JATSState m @@ -98,8 +100,8 @@ instance HasMeta JATSState where deleteMeta field s = s {jatsMeta = deleteMeta field (jatsMeta s)} isBlockElement :: Content -> Bool -isBlockElement (Elem e) = qName (elName e) `elem` blocktags - where blocktags = paragraphLevel ++ lists ++ mathML ++ other +isBlockElement (Elem e) = qName (elName e) `S.member` blocktags + where blocktags = S.fromList (paragraphLevel ++ lists ++ mathML ++ other) \\ S.fromList inlinetags paragraphLevel = ["address", "array", "boxed-text", "chem-struct-wrap", "code", "fig", "fig-group", "graphic", "media", "preformat", "supplementary-material", "table-wrap", "table-wrap-group", @@ -108,6 +110,21 @@ isBlockElement (Elem e) = qName (elName e) `elem` blocktags mathML = ["tex-math", "mml:math"] other = ["p", "related-article", "related-object", "ack", "disp-quote", "speech", "statement", "verse-group", "x"] + inlinetags = ["email", "ext-link", "uri", "inline-supplementary-material", + "related-article", "related-object", "hr", "bold", "fixed-case", + "italic", "monospace", "overline", "overline-start", "overline-end", + "roman", "sans-serif", "sc", "strike", "underline", "underline-start", + "underline-end", "ruby", "alternatives", "inline-graphic", "private-char", + "chem-struct", "inline-formula", "tex-math", "mml:math", "abbrev", + "milestone-end", "milestone-start", "named-content", "styled-content", + "fn", "target", "xref", "sub", "sup", "x", "address", "array", + "boxed-text", "chem-struct-wrap", "code", "fig", "fig-group", "graphic", + "media", "preformat", "supplementary-material", "table-wrap", + "table-wrap-group", "disp-formula", "disp-formula-group", + "citation-alternatives", "element-citation", "mixed-citation", + "nlm-citation", "award-id", "funding-source", "open-access", + "def-list", "list", "ack", "disp-quote", "speech", "statement", + "verse-group"] isBlockElement _ = False -- Trim leading and trailing newline characters diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs index 8824eeb24..8dda969d9 100644 --- a/src/Text/Pandoc/Writers/JATS.hs +++ b/src/Text/Pandoc/Writers/JATS.hs @@ -170,6 +170,28 @@ imageMimeType src kvs = ((drop 1 . dropWhile (/='/')) <$> mbMT) in (maintype, subtype) +languageFor :: [String] -> String +languageFor classes = + case langs of + (l:_) -> escapeStringForXML l + [] -> "" + where isLang l = map toLower l `elem` map (map toLower) languages + langsFrom s = if isLang s + then [s] + else languagesByExtension . map toLower $ s + langs = concatMap langsFrom classes + +codeAttr :: Attr -> (String, [(String, String)]) +codeAttr (ident,classes,kvs) = (lang, attr) + where + attr = [("id",ident) | not (null ident)] ++ + [("language",lang) | not (null lang)] ++ + [(k,v) | (k,v) <- kvs, k `elem` ["code-type", + "code-version", "executable", + "language-version", "orientation", + "platforms", "position", "specific-use"]] + lang = languageFor classes + -- | Convert a Pandoc block element to JATS. blockToJATS :: PandocMonad m => WriterOptions -> Block -> JATS m Doc blockToJATS _ Null = return empty @@ -233,23 +255,10 @@ blockToJATS opts (LineBlock lns) = blockToJATS opts $ linesToPara lns blockToJATS opts (BlockQuote blocks) = inTagsIndented "disp-quote" <$> blocksToJATS opts blocks -blockToJATS _ (CodeBlock (ident,classes,kvs) str) = return $ +blockToJATS _ (CodeBlock a str) = return $ inTags False tag attr (flush (text (escapeStringForXML str))) - where attr = [("id",ident) | not (null ident)] ++ - [("language",lang) | not (null lang)] ++ - [(k,v) | (k,v) <- kvs, k `elem` ["code-type", - "code-version", "executable", - "language-version", "orientation", - "platforms", "position", "specific-use"]] - tag = if null lang then "preformat" else "code" - lang = case langs of - (l:_) -> escapeStringForXML l - [] -> "" - isLang l = map toLower l `elem` map (map toLower) languages - langsFrom s = if isLang s - then [s] - else languagesByExtension . map toLower $ s - langs = concatMap langsFrom classes + where (lang, attr) = codeAttr a + tag = if null lang then "preformat" else "code" blockToJATS _ (BulletList []) = return empty blockToJATS opts (BulletList lst) = inTags True "list" [("list-type", "bullet")] <$> @@ -346,8 +355,10 @@ inlineToJATS opts (Quoted SingleQuote lst) = do inlineToJATS opts (Quoted DoubleQuote lst) = do contents <- inlinesToJATS opts lst return $ char '“' <> contents <> char '”' -inlineToJATS _ (Code _ str) = - return $ inTagsSimple "monospace" $ text (escapeStringForXML str) +inlineToJATS _ (Code a str) = + return $ inTags False tag attr $ text (escapeStringForXML str) + where (lang, attr) = codeAttr a + tag = if null lang then "monospace" else "code" inlineToJATS _ il@(RawInline f x) | f == "jats" = return $ text x | otherwise = do -- cgit v1.2.3 From de8c47eae807ae8ecc96eb24cd1fed6cf8e6b932 Mon Sep 17 00:00:00 2001 From: Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com> Date: Fri, 22 Dec 2017 18:29:03 +1300 Subject: jats writer: Self closing tags for empty xref (#4187) --- src/Text/Pandoc/Writers/JATS.hs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs index 8824eeb24..ee3d61015 100644 --- a/src/Text/Pandoc/Writers/JATS.hs +++ b/src/Text/Pandoc/Writers/JATS.hs @@ -405,8 +405,11 @@ inlineToJATS opts (Link (ident,_,kvs) txt ('#':src, _)) = do [("alt", stringify txt) | not (null txt)] ++ [("rid", src)] ++ [(k,v) | (k,v) <- kvs, k `elem` ["ref-type", "specific-use"]] - contents <- inlinesToJATS opts txt - return $ inTags False "xref" attr contents + if null txt + then return $ selfClosingTag "xref" attr + else do + contents <- inlinesToJATS opts txt + return $ inTags False "xref" attr contents inlineToJATS opts (Link (ident,_,kvs) txt (src, tit)) = do let attr = [("id", ident) | not (null ident)] ++ [("ext-link-type", "uri"), -- cgit v1.2.3 From a5d7be075d6aa3afd817bfb754b5c84cac63614b Mon Sep 17 00:00:00 2001 From: Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com> Date: Fri, 22 Dec 2017 18:30:22 +1300 Subject: JATS writer: Make <p> optional in <td> and <th> (#4178) If the contents are single `Plain` block then do not wrap them with a <p> element. --- src/Text/Pandoc/Writers/JATS.hs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs index ee3d61015..901bcb646 100644 --- a/src/Text/Pandoc/Writers/JATS.hs +++ b/src/Text/Pandoc/Writers/JATS.hs @@ -316,6 +316,9 @@ tableItemToJATS :: PandocMonad m -> Bool -> [Block] -> JATS m Doc +tableItemToJATS opts isHeader [Plain item] = + inTags True (if isHeader then "th" else "td") [] <$> + inlinesToJATS opts item tableItemToJATS opts isHeader item = (inTags True (if isHeader then "th" else "td") [] . vcat) <$> mapM (blockToJATS opts) item -- cgit v1.2.3 From 239cfb7f00773e524a659f1e266c0ff210969d1a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 21 Dec 2017 21:44:13 -0800 Subject: Docx writer: ensure that `distArchive` is the one that comes with pandoc. Previously a `reference.docx` in `~/.pandoc` (or the user data dir) would be used instead, and this could cause problems because a user-modified docx sometimes lacks vital sections that we count on the `distArchive` to supply. Closes #4182. --- src/Text/Pandoc/Writers/Docx.hs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 94529dad4..e4240ca4f 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -213,8 +213,12 @@ writeDocx opts doc@(Pandoc meta _) = do let doc' = walk fixDisplayMath doc username <- P.lookupEnv "USERNAME" utctime <- P.getCurrentTime - distArchive <- (toArchive . BL.fromStrict) <$> - P.readDefaultDataFile "reference.docx" + distArchive <- (toArchive . BL.fromStrict) <$> do + oldUserDataDir <- P.getUserDataDir + P.setUserDataDir Nothing + res <- P.readDefaultDataFile "reference.docx" + P.setUserDataDir oldUserDataDir + return res refArchive <- case writerReferenceDoc opts of Just f -> toArchive <$> P.readFileLazy f Nothing -> (toArchive . BL.fromStrict) <$> -- cgit v1.2.3 From 279c254007601de5cb6a44e0d51522748880d732 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Fri, 22 Dec 2017 05:31:04 -0500 Subject: PowerPoint writer: Treat lists inside BlockQuotes as lists We don't yet produce incremental lists in PowerPoint, but we should at least treat lists inside BlockQuotes as lists, for compatibility with other slide formats. --- src/Text/Pandoc/Writers/Powerpoint.hs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index 7a453ef1f..d5627f51c 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -369,6 +369,12 @@ inlineToParElems (Span _ ils) = concatMapM inlineToParElems ils inlineToParElems (RawInline _ _) = return [] inlineToParElems _ = return [] +isListType :: Block -> Bool +isListType (OrderedList _ _) = True +isListType (BulletList _) = True +isListType (DefinitionList _) = True +isListType _ = False + blockToParagraphs :: PandocMonad m => Block -> P m [Paragraph] blockToParagraphs (Plain ils) = do parElems <- inlinesToParElems ils @@ -386,7 +392,13 @@ blockToParagraphs (LineBlock ilsList) = do blockToParagraphs (CodeBlock attr str) = local (\r -> r{envParaProps = def{pPropMarginLeft = Just 100}}) $ blockToParagraphs $ Para [Code attr str] --- TODO: work out the format +-- We can't yet do incremental lists, but we should render a +-- (BlockQuote List) as a list to maintain compatibility with other +-- formats. +blockToParagraphs (BlockQuote (blk : blks)) | isListType blk = do + ps <- blockToParagraphs blk + ps' <- blockToParagraphs $ BlockQuote blks + return $ ps ++ ps' blockToParagraphs (BlockQuote blks) = local (\r -> r{ envParaProps = (envParaProps r){pPropMarginLeft = Just 100} , envRunProps = (envRunProps r){rPropForceSize = Just blockQuoteSize}})$ -- cgit v1.2.3 From 9758720a24d7cc9782579bb237d747d32cf72835 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 22 Dec 2017 10:35:53 -0800 Subject: RST writer: fix anchors for headers. We were missing an `_`. See #4188. --- src/Text/Pandoc/Writers/RST.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index 42d4d0040..515276985 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -250,7 +250,7 @@ blockToRST (Header level (name,classes,_) inlines) = do let headerChar = if level > 5 then ' ' else "=-~^'" !! (level - 1) let border = text $ replicate (offset contents) headerChar let anchor | null name || name == autoId = empty - | otherwise = ".. " <> text name <> ":" $$ blankline + | otherwise = ".. _" <> text name <> ":" $$ blankline return $ nowrap $ anchor $$ contents $$ border $$ blankline else do let rub = "rubric:: " <> contents -- cgit v1.2.3 From 9ddf84072b27553e9ffd578ae40003108f51015a Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Fri, 22 Dec 2017 20:08:45 +0100 Subject: Lua.Util: avoid altering the stack if peeking fails The stack now remains unaltered if `getRawInt` or `getTable` fail. This is important when those functions are used in an operation that is part of an Alternative. Change: minor --- src/Text/Pandoc/Lua/Util.hs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/Util.hs b/src/Text/Pandoc/Lua/Util.hs index e688ad255..28d09d339 100644 --- a/src/Text/Pandoc/Lua/Util.hs +++ b/src/Text/Pandoc/Lua/Util.hs @@ -67,7 +67,7 @@ getTable :: (ToLuaStack a, FromLuaStack b) => StackIndex -> a -> Lua b getTable idx key = do push key rawget (idx `adjustIndexBy` 1) - peek (-1) <* pop 1 + popValue -- | Add a key-value pair to the table at the top of the stack. addValue :: (ToLuaStack a, ToLuaStack b) => a -> b -> Lua () @@ -86,10 +86,9 @@ addFunction name fn = do -- | Get value behind key from table at given index. getRawInt :: FromLuaStack a => StackIndex -> Int -> Lua a -getRawInt idx key = +getRawInt idx key = do rawgeti idx key - *> peek (-1) - <* pop 1 + popValue -- | Set numeric key/value in table at the given index setRawInt :: ToLuaStack a => StackIndex -> Int -> a -> Lua () @@ -106,6 +105,15 @@ raiseError e = do Lua.push e fromIntegral <$> Lua.lerror +-- | Get, then pop the value at the top of the stack. +popValue :: FromLuaStack a => Lua a +popValue = do + resOrError <- Lua.peekEither (-1) + pop 1 + case resOrError of + Left err -> Lua.throwLuaError err + Right x -> return x + -- | Newtype wrapper intended to be used for optional Lua values. Nesting this -- type is strongly discouraged and will likely lead to a wrong result. newtype OrNil a = OrNil { toMaybe :: Maybe a } -- cgit v1.2.3 From 23edb958dbf0210ff82fd0284563c8280ab79bf1 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Fri, 22 Dec 2017 20:08:51 +0100 Subject: Lua modules: add stringify function to pandoc.utils The new function `pandoc.utils.stringify` converts any AST element to a string with formatting removed. --- src/Text/Pandoc/Lua/Module/Utils.hs | 41 +++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/Module/Utils.hs b/src/Text/Pandoc/Lua/Module/Utils.hs index 496fdbc0a..3a3727355 100644 --- a/src/Text/Pandoc/Lua/Module/Utils.hs +++ b/src/Text/Pandoc/Lua/Module/Utils.hs @@ -29,22 +29,51 @@ module Text.Pandoc.Lua.Module.Utils ( pushModule ) where -import Data.Digest.Pure.SHA (sha1, showDigest) -import Foreign.Lua (Lua, NumResults) +import Control.Applicative ((<|>)) +import Foreign.Lua (FromLuaStack, Lua, NumResults) +import Text.Pandoc.Definition (Pandoc, Meta, Block, Inline) import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Lua.Util (addFunction) +import qualified Data.Digest.Pure.SHA as SHA import qualified Data.ByteString.Lazy as BSL import qualified Foreign.Lua as Lua +import qualified Text.Pandoc.Shared as Shared -- | Push the "pandoc.utils" module to the lua stack. pushModule :: Lua NumResults pushModule = do Lua.newtable - addFunction "sha1" sha1HashFn + addFunction "sha1" sha1 + addFunction "stringify" stringify return 1 -- | Calculate the hash of the given contents. -sha1HashFn :: BSL.ByteString - -> Lua String -sha1HashFn = return . showDigest . sha1 +sha1 :: BSL.ByteString + -> Lua String +sha1 = return . SHA.showDigest . SHA.sha1 + +stringify :: AstElement -> Lua String +stringify el = return $ case el of + PandocElement pd -> Shared.stringify pd + InlineElement i -> Shared.stringify i + BlockElement b -> Shared.stringify b + MetaElement m -> Shared.stringify m + +data AstElement + = PandocElement Pandoc + | MetaElement Meta + | BlockElement Block + | InlineElement Inline + deriving (Show) + +instance FromLuaStack AstElement where + peek idx = do + res <- Lua.tryLua $ (PandocElement <$> Lua.peek idx) + <|> (InlineElement <$> Lua.peek idx) + <|> (BlockElement <$> Lua.peek idx) + <|> (MetaElement <$> Lua.peek idx) + case res of + Right x -> return x + Left _ -> Lua.throwLuaError + "Expected an AST element, but could not parse value as such." -- cgit v1.2.3 From add3cf73a965fb7fa5b45f75442e386294c6fb47 Mon Sep 17 00:00:00 2001 From: mb21 <mb21@users.noreply.github.com> Date: Fri, 22 Dec 2017 12:26:06 +0100 Subject: API change: export blocksToInlines' from Text.Pandoc.Shared --- src/Text/Pandoc/Readers/Docx.hs | 2 +- src/Text/Pandoc/Readers/OPML.hs | 4 ++-- src/Text/Pandoc/Shared.hs | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index 7c7845c71..d73da3085 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -337,7 +337,7 @@ blocksToInlinesWarn cmtId blks = do unless (null $ filter notParaOrPlain blkList) $ lift $ P.report $ DocxParserWarning $ "Docx comment " ++ cmtId ++ " will not retain formatting" - return $ fromList $ blocksToInlines blkList + return $ blocksToInlines' blkList parPartToInlines :: PandocMonad m => ParPart -> DocxContext m Inlines parPartToInlines (PlainRun r) = runToInlines r diff --git a/src/Text/Pandoc/Readers/OPML.hs b/src/Text/Pandoc/Readers/OPML.hs index 68f3252a9..82266748f 100644 --- a/src/Text/Pandoc/Readers/OPML.hs +++ b/src/Text/Pandoc/Readers/OPML.hs @@ -12,7 +12,7 @@ import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Options import Text.Pandoc.Readers.HTML (readHtml) import Text.Pandoc.Readers.Markdown (readMarkdown) -import Text.Pandoc.Shared (crFilter, blocksToInlines) +import Text.Pandoc.Shared (crFilter, blocksToInlines') import Text.XML.Light type OPML m = StateT OPMLState m @@ -74,7 +74,7 @@ asHtml :: PandocMonad m => String -> OPML m Inlines asHtml s = do opts <- gets opmlOptions Pandoc _ bs <- readHtml def{ readerExtensions = readerExtensions opts } (pack s) - return $ fromList $ blocksToInlines bs + return $ blocksToInlines' bs asMarkdown :: PandocMonad m => String -> OPML m Blocks asMarkdown s = do diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 975847de4..005603191 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -91,6 +91,7 @@ module Text.Pandoc.Shared ( mapLeft, -- * for squashing blocks blocksToInlines, + blocksToInlines', -- * Safe read safeRead, -- * Temp directory -- cgit v1.2.3 From 9daf22fa615ad49b675e4e3aae722c982eb4cce3 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 22 Dec 2017 16:45:13 -0800 Subject: LaTeX reader: Refactored inlineCommand. --- src/Text/Pandoc/Readers/LaTeX.hs | 35 +++++++++++------------------------ 1 file changed, 11 insertions(+), 24 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index e6ae4c11b..a43f3d572 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -237,19 +237,21 @@ withVerbatimMode parser = do return result rawLaTeXParser :: (PandocMonad m, HasMacros s, HasReaderOptions s) - => LP m a -> ParserT String s m String + => LP m a -> ParserT String s m (a, String) rawLaTeXParser parser = do inp <- getInput let toks = tokenize "source" $ T.pack inp pstate <- getState - let lstate = def{ sOptions = extractReaderOptions pstate } - res <- lift $ runParserT ((,) <$> try (snd <$> withRaw parser) <*> getState) - lstate "source" toks + let lstate = def{ sOptions = extractReaderOptions pstate + , sMacros = extractMacros pstate } + let rawparser = (,) <$> withRaw parser <*> getState + res <- lift $ runParserT rawparser lstate "chunk" toks case res of Left _ -> mzero - Right (raw, st) -> do + Right ((val, raw), st) -> do updateState (updateMacros (sMacros st <>)) - takeP (T.length (untokenize raw)) + rawstring <- takeP (T.length (untokenize raw)) + return (val, rawstring) applyMacros :: (PandocMonad m, HasMacros s, HasReaderOptions s) => String -> ParserT String s m String @@ -268,33 +270,18 @@ rawLaTeXBlock :: (PandocMonad m, HasMacros s, HasReaderOptions s) => ParserT String s m String rawLaTeXBlock = do lookAhead (try (char '\\' >> letter)) - rawLaTeXParser (environment <|> macroDef <|> blockCommand) + snd <$> rawLaTeXParser (environment <|> macroDef <|> blockCommand) rawLaTeXInline :: (PandocMonad m, HasMacros s, HasReaderOptions s) => ParserT String s m String rawLaTeXInline = do lookAhead (try (char '\\' >> letter) <|> char '$') - rawLaTeXParser (inlineEnvironment <|> inlineCommand') + snd <$> rawLaTeXParser (inlineEnvironment <|> inlineCommand') inlineCommand :: PandocMonad m => ParserT String ParserState m Inlines inlineCommand = do lookAhead (try (char '\\' >> letter) <|> char '$') - inp <- getInput - let toks = tokenize "chunk" $ T.pack inp - let rawinline = do - (il, raw) <- try $ withRaw (inlineEnvironment <|> inlineCommand') - st <- getState - return (il, raw, st) - pstate <- getState - let lstate = def{ sOptions = extractReaderOptions pstate - , sMacros = extractMacros pstate } - res <- runParserT rawinline lstate "source" toks - case res of - Left _ -> mzero - Right (il, raw, s) -> do - updateState $ updateMacros (const $ sMacros s) - takeP (T.length (untokenize raw)) - return il + fst <$> rawLaTeXParser (inlineEnvironment <|> inlineCommand') tokenize :: SourceName -> Text -> [Tok] tokenize sourcename = totoks (initialPos sourcename) -- cgit v1.2.3 From 3679d8d0bd0969d08142a2f6eb40b3a4bd0522d4 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 22 Dec 2017 16:57:52 -0800 Subject: LaTeX reader: use applyMacros in rawLaTeXBlock, rawLaTeXInline. --- src/Text/Pandoc/Readers/LaTeX.hs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index a43f3d572..5299b964f 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -270,13 +270,16 @@ rawLaTeXBlock :: (PandocMonad m, HasMacros s, HasReaderOptions s) => ParserT String s m String rawLaTeXBlock = do lookAhead (try (char '\\' >> letter)) - snd <$> rawLaTeXParser (environment <|> macroDef <|> blockCommand) + -- we don't want to apply newly defined latex macros to their own + -- definitions: + (snd <$> rawLaTeXParser macroDef) <|> + ((snd <$> rawLaTeXParser (environment <|> blockCommand)) >>= applyMacros) rawLaTeXInline :: (PandocMonad m, HasMacros s, HasReaderOptions s) => ParserT String s m String rawLaTeXInline = do lookAhead (try (char '\\' >> letter) <|> char '$') - snd <$> rawLaTeXParser (inlineEnvironment <|> inlineCommand') + rawLaTeXParser (inlineEnvironment <|> inlineCommand') >>= applyMacros . snd inlineCommand :: PandocMonad m => ParserT String ParserState m Inlines inlineCommand = do -- cgit v1.2.3 From 4a07977715021da241b1bf5ab3e1ee62fef89fa1 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 22 Dec 2017 17:33:02 -0800 Subject: Markdown reader: improved raw tex parsing. + Preserve original whitespace between blocks. + Recognize `\placeformula` as context. --- src/Text/Pandoc/Readers/Markdown.hs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 9ffdbf00d..af020261b 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -1120,14 +1120,17 @@ rawVerbatimBlock = htmlInBalanced isVerbTag rawTeXBlock :: PandocMonad m => MarkdownParser m (F Blocks) rawTeXBlock = do guardEnabled Ext_raw_tex - result <- (B.rawBlock "context" . concat <$> - rawConTeXtEnvironment `sepEndBy1` blankline) - <|> (B.rawBlock "latex" . concat <$> - rawLaTeXBlock `sepEndBy1` blankline) - - optional blanklines + result <- (B.rawBlock "context" . trimr . concat <$> + many1 ((++) <$> (rawConTeXtEnvironment <|> conTeXtCommand) + <*> (blanklines <|> many spaceChar))) + <|> (B.rawBlock "latex" . trimr . concat <$> + many1 ((++) <$> rawLaTeXBlock + <*> (blanklines <|> many spaceChar))) return $ return result +conTeXtCommand :: PandocMonad m => MarkdownParser m String +conTeXtCommand = oneOfStrings ["\\placeformula"] + rawHtmlBlocks :: PandocMonad m => MarkdownParser m (F Blocks) rawHtmlBlocks = do (TagOpen tagtype _, raw) <- htmlTag isBlockTag -- cgit v1.2.3 From 28b736bf957da0df79ffb211fc5e7ec4ff713c4b Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 22 Dec 2017 17:59:47 -0800 Subject: `latex_macros` extension changes. Don't pass through macro definitions themselves when `latex_macros` is set. The macros have already been applied. If `latex_macros` is enabled, then `rawLaTeXBlock` in Text.Pandoc.Readers.LaTeX will succeed in parsing a macro definition, and will update pandoc's internal macro map accordingly, but the empty string will be returned. Together with earlier changes, this closes #4179. --- src/Text/Pandoc/Readers/LaTeX.hs | 6 ++++-- src/Text/Pandoc/Readers/Markdown.hs | 10 +++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 5299b964f..f7e45e01a 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -272,8 +272,10 @@ rawLaTeXBlock = do lookAhead (try (char '\\' >> letter)) -- we don't want to apply newly defined latex macros to their own -- definitions: - (snd <$> rawLaTeXParser macroDef) <|> - ((snd <$> rawLaTeXParser (environment <|> blockCommand)) >>= applyMacros) + (do (_, raw) <- rawLaTeXParser macroDef + (guardDisabled Ext_latex_macros >> return raw) <|> return "") + <|> (do (_, raw) <- rawLaTeXParser (environment <|> blockCommand) + applyMacros raw) rawLaTeXInline :: (PandocMonad m, HasMacros s, HasReaderOptions s) => ParserT String s m String diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index af020261b..e7ad9d8ba 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -1120,13 +1120,17 @@ rawVerbatimBlock = htmlInBalanced isVerbTag rawTeXBlock :: PandocMonad m => MarkdownParser m (F Blocks) rawTeXBlock = do guardEnabled Ext_raw_tex - result <- (B.rawBlock "context" . trimr . concat <$> + result <- (B.rawBlock "context" . trim . concat <$> many1 ((++) <$> (rawConTeXtEnvironment <|> conTeXtCommand) <*> (blanklines <|> many spaceChar))) - <|> (B.rawBlock "latex" . trimr . concat <$> + <|> (B.rawBlock "latex" . trim . concat <$> many1 ((++) <$> rawLaTeXBlock <*> (blanklines <|> many spaceChar))) - return $ return result + return $ case B.toList result of + [RawBlock _ cs] + | all (`elem` [' ','\t','\n']) cs -> return mempty + -- don't create a raw block for suppressed macro defs + _ -> return result conTeXtCommand :: PandocMonad m => MarkdownParser m String conTeXtCommand = oneOfStrings ["\\placeformula"] -- cgit v1.2.3 From 9b54b9461221f1bb34b8d3e6ffa0f43d5a9e6352 Mon Sep 17 00:00:00 2001 From: mb21 <mb21@users.noreply.github.com> Date: Thu, 21 Dec 2017 09:56:14 +0100 Subject: HTML Reader: be more forgiving about figcaption fixes #4183 --- src/Text/Pandoc/Readers/HTML.hs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index f5f296712..3e59c4bf7 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -74,8 +74,8 @@ import Text.Pandoc.Options ( ReaderOptions (readerExtensions, readerStripComments), extensionEnabled) import Text.Pandoc.Parsing hiding ((<|>)) -import Text.Pandoc.Shared (addMetaField, crFilter, escapeURI, extractSpaces, - safeRead, underlineSpan) +import Text.Pandoc.Shared (addMetaField, blocksToInlines', crFilter, escapeURI, + extractSpaces, safeRead, underlineSpan) import Text.Pandoc.Walk import Text.Parsec.Error import Text.TeXMath (readMathML, writeTeX) @@ -588,8 +588,11 @@ pFigure = try $ do skipMany pBlank let pImg = (\x -> (Just x, Nothing)) <$> (pOptInTag "p" pImage <* skipMany pBlank) - pCapt = (\x -> (Nothing, Just x)) <$> - (pInTags "figcaption" inline <* skipMany pBlank) + pCapt = (\x -> (Nothing, Just x)) <$> do + skipMany pBlank + bs <- pInTags "figcaption" block + skipMany pBlank + return $ blocksToInlines' $ B.toList bs pSkip = (Nothing, Nothing) <$ pSatisfy (not . matchTagClose "figure") res <- many (pImg <|> pCapt <|> pSkip) let mbimg = msum $ map fst res -- cgit v1.2.3 From dd399594531ad3496c5d471af886573a4c099d9f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 22 Dec 2017 19:01:21 -0800 Subject: JATS reader: better citation handling. We now convert a ref-list element into a list of citations in metadata, suitable for use with pandoc-citeproc. We also convert references to pandoc citation elements. Thus a JATS article with embedded bibliographic information can be processed with pandoc and pandoc-citeproc to produce a formatted bibliography. --- src/Text/Pandoc/Readers/JATS.hs | 82 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/JATS.hs b/src/Text/Pandoc/Readers/JATS.hs index 851fbec35..8c0cb2db5 100644 --- a/src/Text/Pandoc/Readers/JATS.hs +++ b/src/Text/Pandoc/Readers/JATS.hs @@ -5,6 +5,7 @@ import Data.Char (isDigit, isSpace, toUpper) import Data.Default import Data.Generics import Data.List (intersperse) +import qualified Data.Map as Map import Data.Maybe (maybeToList, fromMaybe) import Data.Text (Text) import qualified Data.Text as T @@ -189,8 +190,7 @@ parseBlock (Elem e) = "fig" -> divWith (attrValue "id" e, ["fig"], []) <$> getBlocks e "table-wrap" -> divWith (attrValue "id" e, ["table-wrap"], []) <$> getBlocks e "caption" -> divWith (attrValue "id" e, ["caption"], []) <$> sect 6 - "ref-list" -> divWith ("refs", [], []) <$> getBlocks e - "ref" -> divWith ("ref-" <> attrValue "id" e, [], []) <$> getBlocks e + "ref-list" -> parseRefList e "?xml" -> return mempty _ -> getBlocks e where parseMixed container conts = do @@ -312,6 +312,74 @@ getInlines :: PandocMonad m => Element -> JATS m Inlines getInlines e' = (trimInlines . mconcat) <$> mapM parseInline (elContent e') +parseRefList :: PandocMonad m => Element -> JATS m Blocks +parseRefList e = do + refs <- mapM parseRef $ filterChildren (named "ref") e + addMeta "references" refs + return mempty + +parseRef :: PandocMonad m + => Element -> JATS m (Map.Map String MetaValue) +parseRef e = do + let refId = text $ attrValue "id" e + let getInlineText n = maybe (return mempty) getInlines . filterChild (named n) + case filterChild (named "element-citation") e of + Just c -> do + let refType = text $ + case attrValue "publication-type" c of + "journal" -> "article-journal" + x -> x + (refTitle, refContainerTitle) <- do + t <- getInlineText "article-title" c + ct <- getInlineText "source" c + if t == mempty + then return (ct, mempty) + else return (t, ct) + refLabel <- getInlineText "label" c + refYear <- getInlineText "year" c + refVolume <- getInlineText "volume" c + refFirstPage <- getInlineText "fpage" c + refLastPage <- getInlineText "lpage" c + refPublisher <- getInlineText "publisher-name" c + refPublisherPlace <- getInlineText "publisher-loc" c + let refPages = refFirstPage <> (if refLastPage == mempty + then mempty + else text "\x2013" <> refLastPage) + let personGroups' = filterElements (named "person-group") c + let getName nm = do + given <- maybe (return mempty) getInlines + $ filterElement (named "given-names") nm + family <- maybe (return mempty) getInlines + $ filterElement (named "surname") nm + return $ toMetaValue $ Map.fromList [ + ("given", given) + , ("family", family) + ] + personGroups <- mapM (\pg -> + do names <- mapM getName + (filterElements (named "name") pg) + return (attrValue "person-group-type" pg, + toMetaValue names)) + personGroups' + return $ Map.fromList $ + [ ("id", toMetaValue refId) + , ("type", toMetaValue refType) + , ("title", toMetaValue refTitle) + , ("container-title", toMetaValue refContainerTitle) + , ("publisher", toMetaValue refPublisher) + , ("publisher-place", toMetaValue refPublisherPlace) + , ("title", toMetaValue refTitle) + , ("issued", toMetaValue + $ Map.fromList [ + ("year", refYear) + ]) + , ("volume", toMetaValue refVolume) + , ("page", toMetaValue refPages) + , ("citation-label", toMetaValue refLabel) + ] ++ personGroups + Nothing -> return $ Map.insert "id" (toMetaValue refId) mempty + -- TODO handle mixed-citation + strContentRecursive :: Element -> String strContentRecursive = strContent . (\e' -> e'{ elContent = map elementToStr $ elContent e' }) @@ -354,7 +422,15 @@ parseInline (Elem e) = let rid = attrValue "rid" e let refType = ("ref-type",) <$> maybeAttrValue "ref-type" e let attr = (attrValue "id" e, [], maybeToList refType) - return $ linkWith attr ('#' : rid) "" ils + return $ if refType == Just ("ref-type","bibr") + then cite [Citation{ + citationId = rid + , citationPrefix = [] + , citationSuffix = [] + , citationMode = NormalCitation + , citationNoteNum = 0 + , citationHash = 0}] ils + else linkWith attr ('#' : rid) "" ils "ext-link" -> do ils <- innerInlines let title = fromMaybe "" $ findAttr (QName "title" (Just "http://www.w3.org/1999/xlink") Nothing) e -- cgit v1.2.3 From 35f0567a8fe840ca65f8474d0293942c76a1220f Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sat, 23 Dec 2017 11:53:26 +0100 Subject: Lua modules: add function pandoc.utils.to_roman_numeral The function allows conversion of numbers below 4000 into roman numerals. --- src/Text/Pandoc/Lua/Module/Utils.hs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/Module/Utils.hs b/src/Text/Pandoc/Lua/Module/Utils.hs index 3a3727355..3c830a4bd 100644 --- a/src/Text/Pandoc/Lua/Module/Utils.hs +++ b/src/Text/Pandoc/Lua/Module/Utils.hs @@ -30,7 +30,7 @@ module Text.Pandoc.Lua.Module.Utils ) where import Control.Applicative ((<|>)) -import Foreign.Lua (FromLuaStack, Lua, NumResults) +import Foreign.Lua (FromLuaStack, Lua, LuaInteger, NumResults) import Text.Pandoc.Definition (Pandoc, Meta, Block, Inline) import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Lua.Util (addFunction) @@ -44,6 +44,7 @@ import qualified Text.Pandoc.Shared as Shared pushModule :: Lua NumResults pushModule = do Lua.newtable + addFunction "to_roman_numeral" toRomanNumeral addFunction "sha1" sha1 addFunction "stringify" stringify return 1 @@ -53,6 +54,9 @@ sha1 :: BSL.ByteString -> Lua String sha1 = return . SHA.showDigest . SHA.sha1 +-- | Convert pandoc structure to a string with formatting removed. +-- Footnotes are skipped (since we don't want their contents in link +-- labels). stringify :: AstElement -> Lua String stringify el = return $ case el of PandocElement pd -> Shared.stringify pd @@ -77,3 +81,7 @@ instance FromLuaStack AstElement where Right x -> return x Left _ -> Lua.throwLuaError "Expected an AST element, but could not parse value as such." + +-- | Convert a number < 4000 to uppercase roman numeral. +toRomanNumeral :: LuaInteger -> Lua String +toRomanNumeral = return . Shared.toRomanNumeral . fromIntegral -- cgit v1.2.3 From 2c66a42ab81d40e771eda0f054c62f22ad45f3d0 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sat, 23 Dec 2017 13:35:27 +0100 Subject: Lua modules: add function pandoc.utils.normalize_date The function parses a date and converts it (if possible) to "YYYY-MM-DD" format. --- src/Text/Pandoc/Lua/Module/Utils.hs | 12 ++++++++++-- src/Text/Pandoc/Lua/Util.hs | 4 ++++ 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/Module/Utils.hs b/src/Text/Pandoc/Lua/Module/Utils.hs index 3c830a4bd..458716a03 100644 --- a/src/Text/Pandoc/Lua/Module/Utils.hs +++ b/src/Text/Pandoc/Lua/Module/Utils.hs @@ -33,7 +33,7 @@ import Control.Applicative ((<|>)) import Foreign.Lua (FromLuaStack, Lua, LuaInteger, NumResults) import Text.Pandoc.Definition (Pandoc, Meta, Block, Inline) import Text.Pandoc.Lua.StackInstances () -import Text.Pandoc.Lua.Util (addFunction) +import Text.Pandoc.Lua.Util (OrNil (OrNil), addFunction) import qualified Data.Digest.Pure.SHA as SHA import qualified Data.ByteString.Lazy as BSL @@ -44,9 +44,10 @@ import qualified Text.Pandoc.Shared as Shared pushModule :: Lua NumResults pushModule = do Lua.newtable - addFunction "to_roman_numeral" toRomanNumeral + addFunction "normalize_date" normalizeDate addFunction "sha1" sha1 addFunction "stringify" stringify + addFunction "to_roman_numeral" toRomanNumeral return 1 -- | Calculate the hash of the given contents. @@ -85,3 +86,10 @@ instance FromLuaStack AstElement where -- | Convert a number < 4000 to uppercase roman numeral. toRomanNumeral :: LuaInteger -> Lua String toRomanNumeral = return . Shared.toRomanNumeral . fromIntegral + +-- | Parse a date and convert (if possible) to "YYYY-MM-DD" format. We +-- limit years to the range 1601-9999 (ISO 8601 accepts greater than +-- or equal to 1583, but MS Word only accepts dates starting 1601). +-- Returns nil instead of a string if the conversion failed. +normalizeDate :: String -> Lua (OrNil String) +normalizeDate = return . OrNil . Shared.normalizeDate diff --git a/src/Text/Pandoc/Lua/Util.hs b/src/Text/Pandoc/Lua/Util.hs index 28d09d339..1f7664fc0 100644 --- a/src/Text/Pandoc/Lua/Util.hs +++ b/src/Text/Pandoc/Lua/Util.hs @@ -125,6 +125,10 @@ instance FromLuaStack a => FromLuaStack (OrNil a) where then return (OrNil Nothing) else OrNil . Just <$> Lua.peek idx +instance ToLuaStack a => ToLuaStack (OrNil a) where + push (OrNil Nothing) = Lua.pushnil + push (OrNil (Just x)) = Lua.push x + -- | Helper class for pushing a single value to the stack via a lua function. -- See @pushViaCall@. class PushViaCall a where -- cgit v1.2.3 From 6b63b63f30c8d03575d99f300cca6e54b78d42eb Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 23 Dec 2017 10:03:13 -0800 Subject: JATS reader: process author metadata. --- src/Text/Pandoc/Readers/JATS.hs | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/JATS.hs b/src/Text/Pandoc/Readers/JATS.hs index 8c0cb2db5..29c443d86 100644 --- a/src/Text/Pandoc/Readers/JATS.hs +++ b/src/Text/Pandoc/Readers/JATS.hs @@ -182,6 +182,7 @@ parseBlock (Elem e) = "sec" -> gets jatsSectionLevel >>= sect . (+1) "title" -> return mempty "title-group" -> checkInMeta getTitle + "contrib-group" -> checkInMeta getAuthors "graphic" -> para <$> getGraphic e "journal-meta" -> metaBlock "article-meta" -> metaBlock @@ -239,7 +240,28 @@ parseBlock (Elem e) = Just s -> (text ": " <>) <$> getInlines s Nothing -> return mempty - addMeta "title" (tit <> subtit) + when (tit /= mempty) $ addMeta "title" tit + when (subtit /= mempty) $ addMeta "subtitle" subtit + + getAuthors :: PandocMonad m => JATS m () + getAuthors = do + authors <- mapM getContrib $ filterChildren + (\x -> named "contrib" x && + attrValue "contrib-type" x == "author") e + unless (null authors) $ addMeta "author" authors + + getContrib :: PandocMonad m => Element -> JATS m Inlines + getContrib x = do + given <- maybe (return mempty) getInlines + $ filterElement (named "given-names") x + family <- maybe (return mempty) getInlines + $ filterElement (named "surname") x + if given == mempty && family == mempty + then return mempty + else if given == mempty || family == mempty + then return $ given <> family + else return $ given <> space <> family + -- TODO institute, etc. parseTable = do let isCaption x = named "title" x || named "caption" x @@ -345,19 +367,19 @@ parseRef e = do let refPages = refFirstPage <> (if refLastPage == mempty then mempty else text "\x2013" <> refLastPage) - let personGroups' = filterElements (named "person-group") c + let personGroups' = filterChildren (named "person-group") c let getName nm = do given <- maybe (return mempty) getInlines - $ filterElement (named "given-names") nm + $ filterChild (named "given-names") nm family <- maybe (return mempty) getInlines - $ filterElement (named "surname") nm + $ filterChild (named "surname") nm return $ toMetaValue $ Map.fromList [ ("given", given) , ("family", family) ] personGroups <- mapM (\pg -> do names <- mapM getName - (filterElements (named "name") pg) + (filterChildren (named "name") pg) return (attrValue "person-group-type" pg, toMetaValue names)) personGroups' -- cgit v1.2.3 From 790dc2546b965853dbcaa8db1a36dd05b147ef85 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 23 Dec 2017 10:20:13 -0800 Subject: JATS reader: include institute metadata. --- src/Text/Pandoc/Readers/JATS.hs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/JATS.hs b/src/Text/Pandoc/Readers/JATS.hs index 29c443d86..dcc412a63 100644 --- a/src/Text/Pandoc/Readers/JATS.hs +++ b/src/Text/Pandoc/Readers/JATS.hs @@ -248,7 +248,13 @@ parseBlock (Elem e) = authors <- mapM getContrib $ filterChildren (\x -> named "contrib" x && attrValue "contrib-type" x == "author") e - unless (null authors) $ addMeta "author" authors + unless (null authors) $ + addMeta "author" authors + + getAffiliations :: PandocMonad m => Element -> JATS m () + getAffiliations x = do + affs <- mapM getInlines $ filterChildren (named "aff") x + unless (null affs) $ addMeta "institute" affs getContrib :: PandocMonad m => Element -> JATS m Inlines getContrib x = do @@ -328,7 +334,10 @@ parseBlock (Elem e) = modify $ \st -> st{ jatsSectionLevel = oldN } return $ headerWith (ident,[],[]) n' headerText <> b -- lineItems = mapM getInlines $ filterChildren (named "line") e - metaBlock = acceptingMetadata (getBlocks e) >> return mempty + metaBlock = do + acceptingMetadata (getBlocks e) + getAffiliations e + return mempty getInlines :: PandocMonad m => Element -> JATS m Inlines getInlines e' = (trimInlines . mconcat) <$> -- cgit v1.2.3 From 59a47454574d9eab424ef3d6d9ef6b238515f479 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sat, 23 Dec 2017 22:39:05 +0100 Subject: Lua modules: add function pandoc.utils.hierarchicalize Convert list of Pandoc blocks into (hierarchical) list of Elements. --- src/Text/Pandoc/Lua/Module/Utils.hs | 19 ++++++++++++------- src/Text/Pandoc/Lua/StackInstances.hs | 28 +++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/Module/Utils.hs b/src/Text/Pandoc/Lua/Module/Utils.hs index 458716a03..35495dae1 100644 --- a/src/Text/Pandoc/Lua/Module/Utils.hs +++ b/src/Text/Pandoc/Lua/Module/Utils.hs @@ -44,12 +44,24 @@ import qualified Text.Pandoc.Shared as Shared pushModule :: Lua NumResults pushModule = do Lua.newtable + addFunction "hierarchicalize" hierarchicalize addFunction "normalize_date" normalizeDate addFunction "sha1" sha1 addFunction "stringify" stringify addFunction "to_roman_numeral" toRomanNumeral return 1 +-- | Convert list of Pandoc blocks into (hierarchical) list of Elements. +hierarchicalize :: [Block] -> Lua [Shared.Element] +hierarchicalize = return . Shared.hierarchicalize + +-- | Parse a date and convert (if possible) to "YYYY-MM-DD" format. We +-- limit years to the range 1601-9999 (ISO 8601 accepts greater than +-- or equal to 1583, but MS Word only accepts dates starting 1601). +-- Returns nil instead of a string if the conversion failed. +normalizeDate :: String -> Lua (OrNil String) +normalizeDate = return . OrNil . Shared.normalizeDate + -- | Calculate the hash of the given contents. sha1 :: BSL.ByteString -> Lua String @@ -86,10 +98,3 @@ instance FromLuaStack AstElement where -- | Convert a number < 4000 to uppercase roman numeral. toRomanNumeral :: LuaInteger -> Lua String toRomanNumeral = return . Shared.toRomanNumeral . fromIntegral - --- | Parse a date and convert (if possible) to "YYYY-MM-DD" format. We --- limit years to the range 1601-9999 (ISO 8601 accepts greater than --- or equal to 1583, but MS Word only accepts dates starting 1601). --- Returns nil instead of a string if the conversion failed. -normalizeDate :: String -> Lua (OrNil String) -normalizeDate = return . OrNil . Shared.normalizeDate diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index ce6dbdb98..119946b78 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -33,13 +33,15 @@ StackValue instances for pandoc types. module Text.Pandoc.Lua.StackInstances () where import Control.Applicative ((<|>)) +import Control.Monad (when) import Foreign.Lua (FromLuaStack (peek), Lua, LuaInteger, LuaNumber, StackIndex, ToLuaStack (push), Type (..), throwLuaError, tryLua) import Text.Pandoc.Definition import Text.Pandoc.Lua.Util (adjustIndexBy, getTable, pushViaConstructor) -import Text.Pandoc.Shared (safeRead) +import Text.Pandoc.Shared (Element (Blk, Sec), safeRead) import qualified Foreign.Lua as Lua +import qualified Text.Pandoc.Lua.Util as LuaUtil instance ToLuaStack Pandoc where push (Pandoc meta blocks) = @@ -306,3 +308,27 @@ instance ToLuaStack LuaAttr where instance FromLuaStack LuaAttr where peek idx = LuaAttr <$> peek idx + +-- +-- Hierarchical elements +-- +instance ToLuaStack Element where + push (Blk blk) = push blk + push (Sec lvl num attr label contents) = do + Lua.newtable + LuaUtil.addValue "level" lvl + LuaUtil.addValue "numbering" num + LuaUtil.addValue "attr" (LuaAttr attr) + LuaUtil.addValue "label" label + LuaUtil.addValue "contents" contents + pushSecMetaTable + Lua.setmetatable (-2) + where + pushSecMetaTable :: Lua () + pushSecMetaTable = do + inexistant <- Lua.newmetatable "PandocElementSec" + when inexistant $ do + LuaUtil.addValue "t" "Sec" + Lua.push "__index" + Lua.pushvalue (-2) + Lua.rawset (-3) -- cgit v1.2.3 From dd3ec34a3452ebc159ab201c643ad6d4738733ec Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 23 Dec 2017 21:07:52 -0800 Subject: Fixed bug: when target is PDF, writer extensions were being ignored. E.g. `-t latex-smart -o file.pdf` would produce a different latex intermediate than `-t latex-smart -o file.tex`. Thanks to Bernhard Fisseni for pointing this out. This is a regression since pandoc 2.0 (introduced in commit c7e3c1ec). --- src/Text/Pandoc/App.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index df4bdc151..e597c56d6 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -143,7 +143,7 @@ pdfWriterAndProg :: Maybe String -- ^ user-specified writer name -> IO (String, Maybe String) -- ^ IO (writerName, maybePdfEngineProg) pdfWriterAndProg mWriter mEngine = do let panErr msg = liftIO $ E.throwIO $ PandocAppError msg - case go (baseWriterName <$> mWriter) mEngine of + case go mWriter mEngine of Right (writ, prog) -> return (writ, Just prog) Left err -> panErr err where @@ -151,7 +151,7 @@ pdfWriterAndProg mWriter mEngine = do go (Just writer) Nothing = (writer,) <$> engineForWriter writer go Nothing (Just engine) = (,engine) <$> writerForEngine engine go (Just writer) (Just engine) = - case find (== (writer, engine)) engines of + case find (== (baseWriterName writer, engine)) engines of Just _ -> Right (writer, engine) Nothing -> Left $ "pdf-engine " ++ engine ++ " is not compatible with output format " ++ writer @@ -161,7 +161,7 @@ pdfWriterAndProg mWriter mEngine = do [] -> Left $ "pdf-engine " ++ eng ++ " not known" - engineForWriter w = case [e | (f,e) <- engines, f == w] of + engineForWriter w = case [e | (f,e) <- engines, f == baseWriterName w] of eng : _ -> Right eng [] -> Left $ "cannot produce pdf output from " ++ w -- cgit v1.2.3 From 0d1546328e38636be38ba669badba77f424484a3 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 23 Dec 2017 21:30:10 -0800 Subject: Plain writer: don't linkify table of contents. --- src/Text/Pandoc/Writers/Markdown.hs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 7a3d204f2..13572c466 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -305,22 +305,24 @@ escapeString opts (c:cs) = _ -> c : escapeString opts cs -- | Construct table of contents from list of header blocks. -tableOfContents :: PandocMonad m => WriterOptions -> [Block] -> m Doc -tableOfContents opts headers = - let contents = BulletList $ map (elementToListItem opts) $ hierarchicalize headers - in evalMD (blockToMarkdown opts contents) def def +tableOfContents :: PandocMonad m => WriterOptions -> [Block] -> MD m Doc +tableOfContents opts headers = do + contents <- BulletList <$> mapM (elementToListItem opts) (hierarchicalize headers) + blockToMarkdown opts contents -- | Converts an Element to a list item for a table of contents, -elementToListItem :: WriterOptions -> Element -> [Block] +elementToListItem :: PandocMonad m => WriterOptions -> Element -> MD m [Block] elementToListItem opts (Sec lev _nums (ident,_,_) headerText subsecs) - = Plain headerLink : - [ BulletList (map (elementToListItem opts) subsecs) | - not (null subsecs) && lev < writerTOCDepth opts ] - where headerLink = if null ident + = do isPlain <- asks envPlain + let headerLink = if null ident || isPlain then walk deNote headerText else [Link nullAttr (walk deNote headerText) ('#':ident, "")] -elementToListItem _ (Blk _) = [] + listContents <- if null subsecs || lev >= writerTOCDepth opts + then return [] + else mapM (elementToListItem opts) subsecs + return [Plain headerLink, BulletList listContents] +elementToListItem _ (Blk _) = return [] attrsToMarkdown :: Attr -> Doc attrsToMarkdown attribs = braces $ hsep [attribId, attribClasses, attribKeys] -- cgit v1.2.3 From 4612a9a8c11313104ef733442e403f607bbcfb7e Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 23 Dec 2017 21:54:06 -0800 Subject: JATS reader: code refactoring. --- src/Text/Pandoc/Readers/JATS.hs | 111 +++++++++++++++++----------------------- 1 file changed, 48 insertions(+), 63 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/JATS.hs b/src/Text/Pandoc/Readers/JATS.hs index dcc412a63..77cd08cb3 100644 --- a/src/Text/Pandoc/Readers/JATS.hs +++ b/src/Text/Pandoc/Readers/JATS.hs @@ -24,7 +24,6 @@ type JATS m = StateT JATSState m data JATSState = JATSState{ jatsSectionLevel :: Int , jatsQuoteType :: QuoteType , jatsMeta :: Meta - , jatsAcceptsMeta :: Bool , jatsBook :: Bool , jatsFigureTitle :: Inlines , jatsContent :: [Content] @@ -34,7 +33,6 @@ instance Default JATSState where def = JATSState{ jatsSectionLevel = 0 , jatsQuoteType = DoubleQuote , jatsMeta = mempty - , jatsAcceptsMeta = False , jatsBook = False , jatsFigureTitle = mempty , jatsContent = [] } @@ -80,19 +78,6 @@ named s e = qName (elName e) == s -- -acceptingMetadata :: PandocMonad m => JATS m a -> JATS m a -acceptingMetadata p = do - modify (\s -> s { jatsAcceptsMeta = True } ) - res <- p - modify (\s -> s { jatsAcceptsMeta = False }) - return res - -checkInMeta :: (PandocMonad m, Monoid a) => JATS m () -> JATS m a -checkInMeta p = do - accepts <- jatsAcceptsMeta <$> get - when accepts p - return mempty - addMeta :: PandocMonad m => ToMetaValue a => String -> a -> JATS m () addMeta field val = modify (setMeta field val) @@ -180,13 +165,11 @@ parseBlock (Elem e) = <$> listitems "def-list" -> definitionList <$> deflistitems "sec" -> gets jatsSectionLevel >>= sect . (+1) - "title" -> return mempty - "title-group" -> checkInMeta getTitle - "contrib-group" -> checkInMeta getAuthors "graphic" -> para <$> getGraphic e - "journal-meta" -> metaBlock - "article-meta" -> metaBlock - "custom-meta" -> metaBlock + "journal-meta" -> parseMetadata e + "article-meta" -> parseMetadata e + "custom-meta" -> parseMetadata e + "title" -> return mempty -- processed by header "table" -> parseTable "fig" -> divWith (attrValue "id" e, ["fig"], []) <$> getBlocks e "table-wrap" -> divWith (attrValue "id" e, ["table-wrap"], []) <$> getBlocks e @@ -232,43 +215,6 @@ parseBlock (Elem e) = terms' <- mapM getInlines terms items' <- mapM getBlocks items return (mconcat $ intersperse (str "; ") terms', items') - getTitle = do - tit <- case filterChild (named "article-title") e of - Just s -> getInlines s - Nothing -> return mempty - subtit <- case filterChild (named "subtitle") e of - Just s -> (text ": " <>) <$> - getInlines s - Nothing -> return mempty - when (tit /= mempty) $ addMeta "title" tit - when (subtit /= mempty) $ addMeta "subtitle" subtit - - getAuthors :: PandocMonad m => JATS m () - getAuthors = do - authors <- mapM getContrib $ filterChildren - (\x -> named "contrib" x && - attrValue "contrib-type" x == "author") e - unless (null authors) $ - addMeta "author" authors - - getAffiliations :: PandocMonad m => Element -> JATS m () - getAffiliations x = do - affs <- mapM getInlines $ filterChildren (named "aff") x - unless (null affs) $ addMeta "institute" affs - - getContrib :: PandocMonad m => Element -> JATS m Inlines - getContrib x = do - given <- maybe (return mempty) getInlines - $ filterElement (named "given-names") x - family <- maybe (return mempty) getInlines - $ filterElement (named "surname") x - if given == mempty && family == mempty - then return mempty - else if given == mempty || family == mempty - then return $ given <> family - else return $ given <> space <> family - -- TODO institute, etc. - parseTable = do let isCaption x = named "title" x || named "caption" x caption <- case filterChild isCaption e of @@ -333,16 +279,55 @@ parseBlock (Elem e) = let ident = attrValue "id" e modify $ \st -> st{ jatsSectionLevel = oldN } return $ headerWith (ident,[],[]) n' headerText <> b --- lineItems = mapM getInlines $ filterChildren (named "line") e - metaBlock = do - acceptingMetadata (getBlocks e) - getAffiliations e - return mempty getInlines :: PandocMonad m => Element -> JATS m Inlines getInlines e' = (trimInlines . mconcat) <$> mapM parseInline (elContent e') +parseMetadata :: PandocMonad m => Element -> JATS m Blocks +parseMetadata e = do + getTitle e + getAuthors e + getAffiliations e + return mempty + +getTitle :: PandocMonad m => Element -> JATS m () +getTitle e = do + tit <- case filterElement (named "article-title") e of + Just s -> getInlines s + Nothing -> return mempty + subtit <- case filterElement (named "subtitle") e of + Just s -> (text ": " <>) <$> + getInlines s + Nothing -> return mempty + when (tit /= mempty) $ addMeta "title" tit + when (subtit /= mempty) $ addMeta "subtitle" subtit + +getAuthors :: PandocMonad m => Element -> JATS m () +getAuthors e = do + authors <- mapM getContrib $ filterElements + (\x -> named "contrib" x && + attrValue "contrib-type" x == "author") e + unless (null authors) $ + addMeta "author" authors + +getAffiliations :: PandocMonad m => Element -> JATS m () +getAffiliations x = do + affs <- mapM getInlines $ filterChildren (named "aff") x + unless (null affs) $ addMeta "institute" affs + +getContrib :: PandocMonad m => Element -> JATS m Inlines +getContrib x = do + given <- maybe (return mempty) getInlines + $ filterElement (named "given-names") x + family <- maybe (return mempty) getInlines + $ filterElement (named "surname") x + if given == mempty && family == mempty + then return mempty + else if given == mempty || family == mempty + then return $ given <> family + else return $ given <> space <> family + parseRefList :: PandocMonad m => Element -> JATS m Blocks parseRefList e = do refs <- mapM parseRef $ filterChildren (named "ref") e -- cgit v1.2.3 From c7e5543c293f08059a953aed2ca2039a3b1dd092 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 23 Dec 2017 22:28:43 -0800 Subject: JATS reader: handle author-notes. --- src/Text/Pandoc/Readers/JATS.hs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/JATS.hs b/src/Text/Pandoc/Readers/JATS.hs index 77cd08cb3..9223db68c 100644 --- a/src/Text/Pandoc/Readers/JATS.hs +++ b/src/Text/Pandoc/Readers/JATS.hs @@ -308,8 +308,12 @@ getAuthors e = do authors <- mapM getContrib $ filterElements (\x -> named "contrib" x && attrValue "contrib-type" x == "author") e - unless (null authors) $ - addMeta "author" authors + authorNotes <- mapM getInlines $ filterElements (named "author-notes") e + let authors' = case (reverse authors, authorNotes) of + ([], _) -> [] + (_, []) -> authors + (a:as, ns) -> reverse as ++ [a <> mconcat ns] + unless (null authors) $ addMeta "author" authors' getAffiliations :: PandocMonad m => Element -> JATS m () getAffiliations x = do @@ -467,9 +471,6 @@ parseInline (Elem e) = "uri" -> return $ link (strContent e) "" $ str $ strContent e "fn" -> (note . mconcat) <$> mapM parseBlock (elContent e) - -- Note: this isn't a real docbook tag; it's what we convert - -- <?asciidor-br?> to in handleInstructions, above. A kludge to - -- work around xml-light's inability to parse an instruction. _ -> innerInlines where innerInlines = (trimInlines . mconcat) <$> mapM parseInline (elContent e) -- cgit v1.2.3 From ee5fe9bf2c0617ddbad0e517f78e7fffe4e737df Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 24 Dec 2017 13:02:18 -0800 Subject: RST reader: allow empty list items (as docutils does). Closes #4193. --- src/Text/Pandoc/Readers/RST.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 6b5d0a331..9f259d958 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -547,7 +547,7 @@ bulletListStart :: Monad m => ParserT [Char] st m Int bulletListStart = try $ do notFollowedBy' hrule -- because hrules start out just like lists marker <- oneOf bulletListMarkers - white <- many1 spaceChar + white <- many1 spaceChar <|> "" <$ lookAhead (char '\n') return $ length (marker:white) -- parses ordered list start and returns its length (inc following whitespace) @@ -556,7 +556,7 @@ orderedListStart :: Monad m => ListNumberStyle -> RSTParser m Int orderedListStart style delim = try $ do (_, markerLen) <- withHorizDisplacement (orderedListMarker style delim) - white <- many1 spaceChar + white <- many1 spaceChar <|> "" <$ lookAhead (char '\n') return $ markerLen + length white -- parse a line of a list item -- cgit v1.2.3 From 718b2c5837f4a72a6ba58bc5c307431316e7d4dc Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 26 Dec 2017 08:38:19 -0800 Subject: HTML writer: Use br elements in line blocks... instead of relying on CSS. Closes #4162. HTML-based templates have had the custom CSS for div.line-block removed. Those maintaining custom templates will want to remove this too. We still enclose line blocks in a div with class line-block. --- src/Text/Pandoc/Writers/HTML.hs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index f25bbadfb..7ff7284cc 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -670,8 +670,7 @@ blockToHtml opts (LineBlock lns) = if writerWrapText opts == WrapNone then blockToHtml opts $ linesToPara lns else do - let lf = preEscapedString "\n" - htmlLines <- mconcat . intersperse lf <$> mapM (inlineListToHtml opts) lns + htmlLines <- inlineListToHtml opts $ intercalate [LineBreak] lns return $ H.div ! A.class_ "line-block" $ htmlLines blockToHtml opts (Div attr@(ident, classes, kvs') bs) = do html5 <- gets stHtml5 -- cgit v1.2.3 From 9e1d86638ce7ee9e22e0623f77ffe8609f552b15 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 26 Dec 2017 10:57:57 -0800 Subject: LaTeX reader: support `\foreignlanguage` from babel. --- src/Text/Pandoc/Readers/LaTeX.hs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index f7e45e01a..6c5567ffd 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1489,8 +1489,17 @@ inlineCommands = M.union inlineLanguageCommands $ M.fromList $ -- biblatex misc , ("RN", romanNumeralUpper) , ("Rn", romanNumeralLower) + -- babel + , ("foreignlanguage", foreignlanguage) ] +foreignlanguage :: PandocMonad m => LP m Inlines +foreignlanguage = do + babelLang <- T.unpack . untokenize <$> braced + case babelLangToBCP47 babelLang of + Just lang -> spanWith ("", [], [("lang", renderLang $ lang)]) <$> tok + _ -> tok + inlineLanguageCommands :: PandocMonad m => M.Map Text (LP m Inlines) inlineLanguageCommands = M.fromList $ mk <$> M.toList polyglossiaLangToBCP47 where @@ -2655,3 +2664,24 @@ polyglossiaLangToBCP47 = M.fromList , ("urdu", \_ -> Lang "ur" "" "" []) , ("vietnamese", \_ -> Lang "vi" "" "" []) ] + +babelLangToBCP47 :: String -> Maybe Lang +babelLangToBCP47 s = + case s of + "austrian" -> Just $ Lang "de" "" "AT" ["1901"] + "naustrian" -> Just $ Lang "de" "" "AT" [] + "swissgerman" -> Just $ Lang "de" "" "CH" ["1901"] + "nswissgerman" -> Just $ Lang "de" "" "CH" [] + "german" -> Just $ Lang "de" "" "DE" ["1901"] + "ngerman" -> Just $ Lang "de" "" "DE" [] + "lowersorbian" -> Just $ Lang "dsb" "" "" [] + "uppersorbian" -> Just $ Lang "hsb" "" "" [] + "polutonikogreek" -> Just $ Lang "el" "" "" ["polyton"] + "slovene" -> Just $ Lang "sl" "" "" [] + "australian" -> Just $ Lang "en" "" "AU" [] + "canadian" -> Just $ Lang "en" "" "CA" [] + "british" -> Just $ Lang "en" "" "GB" [] + "newzealand" -> Just $ Lang "en" "" "NZ" [] + "american" -> Just $ Lang "en" "" "US" [] + "classiclatin" -> Just $ Lang "la" "" "" ["x-classic"] + _ -> fmap ($ "") $ M.lookup s polyglossiaLangToBCP47 -- cgit v1.2.3 From b5ae5373213634a92cb9cca6ae17e8df995e246c Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 26 Dec 2017 11:03:03 -0800 Subject: Add opus to MIME type table as audio/ogg. See #4198. --- src/Text/Pandoc/MIME.hs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/MIME.hs b/src/Text/Pandoc/MIME.hs index fb85910bb..eba8d512f 100644 --- a/src/Text/Pandoc/MIME.hs +++ b/src/Text/Pandoc/MIME.hs @@ -325,6 +325,7 @@ mimeTypesList = -- List borrowed from happstack-server. ,("ogv","video/ogg") ,("ogx","application/ogg") ,("old","application/x-trash") + ,("opus","audio/ogg") ,("otg","application/vnd.oasis.opendocument.graphics-template") ,("oth","application/vnd.oasis.opendocument.text-web") ,("otp","application/vnd.oasis.opendocument.presentation-template") -- cgit v1.2.3 From b52cbb71c2cbd3a380c36877c3066e17660f1ade Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 26 Dec 2017 11:17:32 -0800 Subject: LaTeX writer: Allow fragile=singleslide attribute in beamer slides. Closes #4169. --- src/Text/Pandoc/Writers/LaTeX.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 666aea07c..d6ccc1512 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -398,10 +398,10 @@ elementToBeamer slideLevel (Sec lvl _num (ident,classes,kvs) tit elts) hasCode _ = [] let fragile = "fragile" `elem` classes || not (null $ query hasCodeBlock elts ++ query hasCode elts) - let frameoptions = ["allowdisplaybreaks", "allowframebreaks", + let frameoptions = ["allowdisplaybreaks", "allowframebreaks", "fragile", "b", "c", "t", "environment", "label", "plain", "shrink", "standout"] - let optionslist = ["fragile" | fragile] ++ + let optionslist = ["fragile" | fragile && lookup "fragile" kvs == Nothing] ++ [k | k <- classes, k `elem` frameoptions] ++ [k ++ "=" ++ v | (k,v) <- kvs, k `elem` frameoptions] let options = if null optionslist -- cgit v1.2.3 From 7e8cfc099017b554dc4105a8c56544867784a9f8 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Wed, 27 Dec 2017 09:11:03 +0100 Subject: Fix custom writer regression An additional `Lua.call` was left in during refactoring, which caused an exception "attempt to call a nil value". Fixes: #4202 --- src/Text/Pandoc/Writers/Custom.hs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Custom.hs b/src/Text/Pandoc/Writers/Custom.hs index 72f443ed0..6a6fabf1d 100644 --- a/src/Text/Pandoc/Writers/Custom.hs +++ b/src/Text/Pandoc/Writers/Custom.hs @@ -102,8 +102,7 @@ writeCustom luaFile opts doc@(Pandoc meta _) = do -- to handle this more gracefully): when (stat /= OK) $ tostring 1 >>= throw . PandocLuaException . UTF8.toString - call 0 0 - -- TODO - call hierarchicalize, so we have that info + -- TODO - call hierarchicalize, so we have that info rendered <- docToCustom opts doc context <- metaToJSON opts blockListToCustom -- cgit v1.2.3 From dc3ee500a0447dc258ae5b49cf5907cba0d407aa Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Wed, 27 Dec 2017 09:49:28 -0500 Subject: Docx Reader: preprocess Document body to unwrap "w:sdt" elements We walk through the document (using the zipper in Text.XML.Light.Cursor) to unwrap the sdt tags before doing the rest of the parsing of the document. Note that the function is generically named `walkDocument` in case we need to do any further preprocessing in the future. Closes #4190 --- src/Text/Pandoc/Readers/Docx/Parse.hs | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx/Parse.hs b/src/Text/Pandoc/Readers/Docx/Parse.hs index 99e6f99e6..48a512be2 100644 --- a/src/Text/Pandoc/Readers/Docx/Parse.hs +++ b/src/Text/Pandoc/Readers/Docx/Parse.hs @@ -73,6 +73,7 @@ import Text.TeXMath (Exp) import Text.TeXMath.Readers.OMML (readOMML) import Text.TeXMath.Unicode.Fonts (Font (..), getUnicode, stringToFont) import Text.XML.Light +import qualified Text.XML.Light.Cursor as XMLC data ReaderEnv = ReaderEnv { envNotes :: Notes , envComments :: Comments @@ -117,6 +118,32 @@ mapD f xs = in concatMapM handler xs +unwrapSDT :: NameSpaces -> Content -> Content +unwrapSDT ns (Elem element) + | isElem ns "w" "sdt" element + , Just sdtContent <- findChildByName ns "w" "sdtContent" element + , child : _ <- elChildren sdtContent + = Elem child +unwrapSDT _ content = content + +walkDocument' :: NameSpaces -> XMLC.Cursor -> XMLC.Cursor +walkDocument' ns cur = + let modifiedCur = XMLC.modifyContent (unwrapSDT ns) cur + in + case XMLC.nextDF modifiedCur of + Just cur' -> walkDocument' ns cur' + Nothing -> XMLC.root modifiedCur + +walkDocument :: NameSpaces -> Element -> Maybe Element +walkDocument ns element = + let cur = XMLC.fromContent (Elem element) + cur' = walkDocument' ns cur + in + case XMLC.toTree cur' of + Elem element' -> Just element' + _ -> Nothing + + data Docx = Docx Document deriving Show @@ -298,7 +325,10 @@ archiveToDocument zf = do docElem <- maybeToD $ (parseXMLDoc . UTF8.toStringLazy . fromEntry) entry let namespaces = elemToNameSpaces docElem bodyElem <- maybeToD $ findChildByName namespaces "w" "body" docElem - body <- elemToBody namespaces bodyElem + let bodyElem' = case walkDocument namespaces bodyElem of + Just e -> e + Nothing -> bodyElem + body <- elemToBody namespaces bodyElem' return $ Document namespaces body elemToBody :: NameSpaces -> Element -> D Body -- cgit v1.2.3 From 2953983e9ec72c096fe3b2055640e76ab1949fa0 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Wed, 27 Dec 2017 18:42:19 +0100 Subject: Fix regression of DefinitionLists in custom writer Pairs where serialized as two-element lists instead, and are now pushed again as a table with a single key/value pair. Fixes: #4202 --- src/Text/Pandoc/Writers/Custom.hs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Custom.hs b/src/Text/Pandoc/Writers/Custom.hs index 6a6fabf1d..a33196cbe 100644 --- a/src/Text/Pandoc/Writers/Custom.hs +++ b/src/Text/Pandoc/Writers/Custom.hs @@ -87,6 +87,15 @@ instance ToLuaStack (Stringify Citation) where addValue "citationNoteNum" $ citationNoteNum cit addValue "citationHash" $ citationHash cit +-- | Key-value pair, pushed as a table with @a@ as the only key and @v@ as the +-- associated value. +newtype KeyValue a b = KeyValue (a, b) + +instance (ToLuaStack a, ToLuaStack b) => ToLuaStack (KeyValue a b) where + push (KeyValue (k, v)) = do + newtable + addValue k v + data PandocLuaException = PandocLuaException String deriving (Show, Typeable) @@ -165,7 +174,8 @@ blockToCustom (OrderedList (num,sty,delim) items) = callFunc "OrderedList" (map Stringify items) num (show sty) (show delim) blockToCustom (DefinitionList items) = - callFunc "DefinitionList" (map (Stringify *** map Stringify) items) + callFunc "DefinitionList" + (map (KeyValue . (Stringify *** map Stringify)) items) blockToCustom (Div attr items) = callFunc "Div" (Stringify items) (attrToMap attr) -- cgit v1.2.3 From 8a35b44aaf82f0b6cf8384aa78d92f963ad7ee92 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 27 Dec 2017 11:36:08 -0800 Subject: Allow lenient decoding of *latex error logs. These sometimes aren't properly UTF8 encoded, and it's confusing if we get an encoding error due to the error log. Closes #4200. --- src/Text/Pandoc/App.hs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index e597c56d6..641d3782b 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -58,6 +58,9 @@ import Data.Monoid import qualified Data.Set as Set import Data.Text (Text) import qualified Data.Text as T +import qualified Data.Text.Lazy as TL +import qualified Data.Text.Lazy.Encoding as TE +import qualified Data.Text.Encoding.Error as TE import Data.Yaml (decode) import qualified Data.Yaml as Yaml import GHC.Generics @@ -513,7 +516,9 @@ convertWithOpts opts = do case res of Right pdf -> writeFnBinary outputFile pdf Left err' -> liftIO $ - E.throwIO $ PandocPDFError (UTF8.toStringLazy err') + E.throwIO $ PandocPDFError $ + TL.unpack (TE.decodeUtf8With TE.lenientDecode err') + Nothing -> do let htmlFormat = format `elem` ["html","html4","html5","s5","slidy", -- cgit v1.2.3 From cc9e3a91724dff0b962d23a58c1188d60dee07dd Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 27 Dec 2017 12:25:48 -0800 Subject: Allow `--list-extensions` to take an optional FORMAT argument. This lists the extensions set by default for the selected FORMAT. --- src/Text/Pandoc/App.hs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 641d3782b..50464830b 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -1589,15 +1589,17 @@ options = "" , Option "" ["list-extensions"] - (NoArg - (\_ -> do + (OptArg + (\arg _ -> do + let exts = getDefaultExtensions (fromMaybe "markdown" arg) let showExt x = drop 4 (show x) ++ - if extensionEnabled x pandocExtensions + if extensionEnabled x exts then " +" else " -" mapM_ (UTF8.hPutStrLn stdout . showExt) ([minBound..maxBound] :: [Extension]) - exitSuccess )) + exitSuccess ) + "FORMAT") "" , Option "" ["list-highlight-languages"] -- cgit v1.2.3 From a888083ee1b381e8f8abab58d7af83d88c5343d0 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 27 Dec 2017 12:26:15 -0800 Subject: HTML reader: parse div with class `line-block` as LineBlock. See #4162. --- src/Text/Pandoc/Extensions.hs | 1 + src/Text/Pandoc/Readers/HTML.hs | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index bea293891..7fa75cdd9 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -321,6 +321,7 @@ getDefaultExtensions "org" = extensionsFromList getDefaultExtensions "html" = extensionsFromList [Ext_auto_identifiers, Ext_native_divs, + Ext_line_blocks, Ext_native_spans] getDefaultExtensions "html4" = getDefaultExtensions "html" getDefaultExtensions "html5" = getDefaultExtensions "html" diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index f5f296712..1b758a668 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -51,7 +51,7 @@ import Data.Char (isAlphaNum, isDigit, isLetter) import Data.Default (Default (..), def) import Data.Foldable (for_) import Data.List (intercalate, isPrefixOf) -import Data.List.Split (wordsBy) +import Data.List.Split (wordsBy, splitWhen) import qualified Data.Map as M import Data.Maybe (fromMaybe, isJust, isNothing) import Data.Monoid (First (..), (<>)) @@ -66,6 +66,7 @@ import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad (..)) import Text.Pandoc.CSS (foldOrElse, pickStyleAttrProps) import Text.Pandoc.Definition +import Text.Pandoc.Extensions (Extension(..)) import Text.Pandoc.Error import Text.Pandoc.Logging import Text.Pandoc.Options ( @@ -191,6 +192,7 @@ block = do , pHtml , pHead , pBody + , pLineBlock , pDiv , pPlain , pFigure @@ -377,6 +379,16 @@ pRawTag = do then return mempty else return $ renderTags' [tag] +pLineBlock :: PandocMonad m => TagParser m Blocks +pLineBlock = try $ do + guardEnabled Ext_line_blocks + _ <- pSatisfy $ tagOpen (=="div") (== [("class","line-block")]) + ils <- trimInlines . mconcat <$> manyTill inline (pSatisfy (tagClose (=="div"))) + let lns = map B.fromList $ + splitWhen (== LineBreak) $ filter (/= SoftBreak) $ + B.toList ils + return $ B.lineBlock lns + pDiv :: PandocMonad m => TagParser m Blocks pDiv = try $ do guardEnabled Ext_native_divs -- cgit v1.2.3 From f688086979307bb5c81ffe61ed09493bc72785a5 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 27 Dec 2017 12:38:04 -0800 Subject: Small improvement to figcaption parsing. #4184. --- src/Text/Pandoc/Readers/HTML.hs | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 05a80335a..393917a9c 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -601,9 +601,7 @@ pFigure = try $ do let pImg = (\x -> (Just x, Nothing)) <$> (pOptInTag "p" pImage <* skipMany pBlank) pCapt = (\x -> (Nothing, Just x)) <$> do - skipMany pBlank bs <- pInTags "figcaption" block - skipMany pBlank return $ blocksToInlines' $ B.toList bs pSkip = (Nothing, Nothing) <$ pSatisfy (not . matchTagClose "figure") res <- many (pImg <|> pCapt <|> pSkip) -- cgit v1.2.3 From ba6c19345728cb6f1fcc18198346613dc2ca40c5 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 27 Dec 2017 13:38:44 -0800 Subject: Fix warning. --- src/Text/Pandoc/Readers/HTML.hs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 393917a9c..65171d37a 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -66,12 +66,11 @@ import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad (..)) import Text.Pandoc.CSS (foldOrElse, pickStyleAttrProps) import Text.Pandoc.Definition -import Text.Pandoc.Extensions (Extension(..)) import Text.Pandoc.Error import Text.Pandoc.Logging import Text.Pandoc.Options ( Extension (Ext_epub_html_exts, Ext_empty_paragraphs, Ext_native_divs, - Ext_native_spans, Ext_raw_html), + Ext_native_spans, Ext_raw_html, Ext_line_blocks), ReaderOptions (readerExtensions, readerStripComments), extensionEnabled) import Text.Pandoc.Parsing hiding ((<|>)) -- cgit v1.2.3 From 52a4cf669970881424b426c654dc5a2f544786e3 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Thu, 28 Dec 2017 07:47:07 -0500 Subject: PowerPoint writer: Obey slide level option --- src/Text/Pandoc/Writers/Powerpoint.hs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index d5627f51c..ab3b2eabf 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -86,6 +86,9 @@ writePowerpoint opts (Pandoc meta blks) = do , envDistArchive = distArchive , envUTCTime = utctime , envOpts = opts + , envSlideLevel = case writerSlideLevel opts of + Just n -> n + Nothing -> 2 } runP env def $ do pres <- blocksToPresentation blks' archv <- presentationToArchive pres -- cgit v1.2.3 From 0fd7ed0cab165821346b71055bd3ca7f94a825e4 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Thu, 28 Dec 2017 16:02:42 +0300 Subject: FB2 writer: add cover image specified by "cover-image" meta Fixes #4195 --- src/Text/Pandoc/Writers/FB2.hs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index 633f42442..0a4130ca4 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -121,9 +121,18 @@ description meta' = do Just (MetaString s) -> [el "lang" $ iso639 s] _ -> [] where iso639 = takeWhile (/= '-') -- Convert BCP 47 to ISO 639 + let coverimage url = do + let img = Image nullAttr mempty (url, "") + im <- insertImage InlineImage img + return [el "coverpage" im] + coverpage <- case lookupMeta "cover-image" meta' of + Just (MetaInlines [Str s]) -> coverimage s + Just (MetaString s) -> coverimage s + _ -> return [] return $ el "description" [ el "title-info" (genre : (bt ++ as ++ dd ++ lang)) - , el "document-info" [ el "program-used" "pandoc" ] -- FIXME: +version + , el "document-info" ([ el "program-used" "pandoc" ] -- FIXME: +version + ++ coverpage) ] booktitle :: PandocMonad m => Meta -> FBM m [Content] -- cgit v1.2.3 From e5c8b650041a270b58e2f72e18eb28a32f153954 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Mon, 25 Dec 2017 23:31:05 +0100 Subject: Org reader: support minlevel option for includes The level of headers in included files can be shifted to a higher level by specifying a minimum header level via the `:minlevel` parameter. E.g. `#+include: "tour.org" :minlevel 1` will shift the headers in tour.org such that the topmost headers become level 1 headers. Fixes: #4154 --- src/Text/Pandoc/Readers/Org/Blocks.hs | 51 +++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 14 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index cc6abbfa5..a930652af 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -41,7 +41,6 @@ import Text.Pandoc.Readers.Org.Shared (cleanLinkString, isImageFilename, originalLang, translateLang) import Text.Pandoc.Builder (Blocks, Inlines) -import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Options @@ -54,6 +53,9 @@ import Data.List (foldl', isPrefixOf) import Data.Maybe (fromMaybe, isJust, isNothing) import Data.Monoid ((<>)) +import qualified Text.Pandoc.Builder as B +import qualified Text.Pandoc.Walk as Walk + -- -- parsing blocks -- @@ -509,19 +511,18 @@ include :: PandocMonad m => OrgParser m (F Blocks) include = try $ do metaLineStart <* stringAnyCase "include:" <* skipSpaces filename <- includeTarget - blockType <- optionMaybe $ skipSpaces *> many1 alphaNum - blocksParser <- case blockType of - Just "example" -> - return $ pure . B.codeBlock <$> parseRaw - Just "export" -> do - format <- skipSpaces *> many (noneOf "\n\r\t ") - return $ pure . B.rawBlock format <$> parseRaw - Just "src" -> do - language <- skipSpaces *> many (noneOf "\n\r\t ") - let attr = (mempty, [language], mempty) - return $ pure . B.codeBlockWith attr <$> parseRaw - _ -> return $ pure . B.fromList <$> blockList - anyLine + includeArgs <- many (try $ skipSpaces *> many1 alphaNum) + params <- keyValues + blocksParser <- case includeArgs of + ("example" : _) -> return $ pure . B.codeBlock <$> parseRaw + ["export"] -> return . returnF $ B.fromList [] + ("export" : format : []) -> return $ pure . B.rawBlock format <$> parseRaw + ("src" : rest) -> do + let attr = case rest of + [lang] -> (mempty, [lang], mempty) + _ -> nullAttr + return $ pure . B.codeBlockWith attr <$> parseRaw + _ -> return $ return . B.fromList . blockFilter params <$> blockList insertIncludedFileF blocksParser ["."] filename where includeTarget :: PandocMonad m => OrgParser m FilePath @@ -532,6 +533,28 @@ include = try $ do parseRaw :: PandocMonad m => OrgParser m String parseRaw = many anyChar + blockFilter :: [(String, String)] -> [Block] -> [Block] + blockFilter params blks = + let minlvl = lookup "minlevel" params + in case (minlvl >>= safeRead :: Maybe Int) of + Nothing -> blks + Just lvl -> let levels = Walk.query headerLevel blks + -- CAVE: partial function in else + curMin = if null levels then 0 else minimum levels + in Walk.walk (shiftHeader (curMin - lvl)) blks + + headerLevel :: Block -> [Int] + headerLevel (Header lvl _attr _content) = [lvl] + headerLevel _ = [] + + shiftHeader :: Int -> Block -> Block + shiftHeader shift blk = + if shift <= 0 + then blk + else case blk of + (Header lvl attr content) -> Header (lvl - shift) attr content + _ -> blk + rawExportLine :: PandocMonad m => OrgParser m Blocks rawExportLine = try $ do metaLineStart -- cgit v1.2.3 From 3c93ac5cf0995cc2dd8bb5775029da550af61e0d Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 28 Dec 2017 08:38:42 -0800 Subject: LaTeX reader: be more tolerant of `&` character. This allows us to parse unknown tabular environments as raw LaTeX. Closes #4208. --- 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 6c5567ffd..e0972bb6c 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1726,7 +1726,7 @@ inline = (mempty <$ comment) <|> (guardEnabled Ext_literate_haskell *> symbol '|' *> doLHSverb) <|> (str . (:[]) <$> primEscape) <|> regularSymbol - <|> (do res <- symbolIn "#^'`\"[]" + <|> (do res <- symbolIn "#^'`\"[]&" pos <- getPosition let s = T.unpack (untoken res) report $ ParsingUnescaped s pos -- cgit v1.2.3 From 90296d7e96de6c8a36192c6eb12b4cc42b673d1e Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 28 Dec 2017 08:53:54 -0800 Subject: LaTeX writer: Use \endhead after \toprule in headerless tables. Closes #4207. --- src/Text/Pandoc/Writers/LaTeX.hs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index d6ccc1512..87ce65586 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -699,10 +699,9 @@ blockToLaTeX (Table caption aligns widths heads rows) = do then return empty else ($$ text "\\endfirsthead") <$> toHeaders heads head' <- if all null heads - then return empty + then return "\\toprule" -- avoid duplicate notes in head and firsthead: - else ($$ text "\\endhead") <$> - toHeaders (if isEmpty firsthead + else toHeaders (if isEmpty firsthead then heads else walk removeNote heads) let capt = if isEmpty captionText @@ -717,8 +716,8 @@ blockToLaTeX (Table caption aligns widths heads rows) = do -- the @{} removes extra space at beginning and end $$ capt $$ firsthead - $$ (if all null heads then "\\toprule" else empty) $$ head' + $$ "\\endhead" $$ vcat rows' $$ "\\bottomrule" $$ "\\end{longtable}" -- cgit v1.2.3 From ddd6a892470181b321ac2f80ffb76809f15419c8 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 28 Dec 2017 10:01:09 -0800 Subject: Text.Pandoc.Class: add insertInFileTree (API change). This gives a pure way to insert an ersatz file into a FileTree. In addition, we normalize paths both on insertion and on lookup, so that "foo" and "./foo" will be judged equivalent. --- src/Text/Pandoc/Class.hs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index c63781adf..487f66793 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -81,6 +81,7 @@ module Text.Pandoc.Class ( PandocMonad(..) , FileTree(..) , FileInfo(..) , addToFileTree + , insertInFileTree , runIO , runIOorExplode , runPure @@ -141,7 +142,7 @@ import qualified System.Environment as IO (lookupEnv) import System.FilePath.Glob (match, compile) import System.Directory (createDirectoryIfMissing, getDirectoryContents, doesDirectoryExist) -import System.FilePath ((</>), (<.>), takeDirectory, +import System.FilePath ((</>), (<.>), takeDirectory, makeRelative, takeExtension, dropExtension, isRelative, normalise) import qualified System.FilePath.Glob as IO (glob) import qualified System.Directory as IO (getModificationTime) @@ -912,12 +913,13 @@ newtype FileTree = FileTree {unFileTree :: M.Map FilePath FileInfo} deriving (Monoid) getFileInfo :: FilePath -> FileTree -> Maybe FileInfo -getFileInfo fp tree = M.lookup fp $ unFileTree tree +getFileInfo fp tree = + M.lookup (makeRelative "." fp) (unFileTree tree) -- | Add the specified file to the FileTree. If file -- is a directory, add its contents recursively. addToFileTree :: FileTree -> FilePath -> IO FileTree -addToFileTree (FileTree treemap) fp = do +addToFileTree tree fp = do isdir <- doesDirectoryExist fp if isdir then do -- recursively add contents of directories @@ -925,13 +927,17 @@ addToFileTree (FileTree treemap) fp = do isSpecial "." = True isSpecial _ = False fs <- (map (fp </>) . filter (not . isSpecial)) <$> getDirectoryContents fp - foldM addToFileTree (FileTree treemap) fs + foldM addToFileTree tree fs else do contents <- B.readFile fp mtime <- IO.getModificationTime fp - return $ FileTree $ - M.insert fp FileInfo{ infoFileMTime = mtime - , infoFileContents = contents } treemap + return $ insertInFileTree fp FileInfo{ infoFileMTime = mtime + , infoFileContents = contents } tree + +-- | Insert an ersatz file into the 'FileTree'. +insertInFileTree :: FilePath -> FileInfo -> FileTree -> FileTree +insertInFileTree fp info (FileTree treemap) = + FileTree $ M.insert (makeRelative "." fp) info treemap newtype PandocPure a = PandocPure { unPandocPure :: ExceptT PandocError -- cgit v1.2.3 From 60ace79b03e133521f1bdea8bf69f5db22dc5e88 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 28 Dec 2017 10:47:09 -0800 Subject: Class: use makeCanonical for normalization in FileTree and data files. --- src/Text/Pandoc/Class.hs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 487f66793..5ea736539 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -142,7 +142,7 @@ import qualified System.Environment as IO (lookupEnv) import System.FilePath.Glob (match, compile) import System.Directory (createDirectoryIfMissing, getDirectoryContents, doesDirectoryExist) -import System.FilePath ((</>), (<.>), takeDirectory, makeRelative, +import System.FilePath ((</>), (<.>), takeDirectory, takeExtension, dropExtension, isRelative, normalise) import qualified System.FilePath.Glob as IO (glob) import qualified System.Directory as IO (getModificationTime) @@ -621,6 +621,7 @@ getDefaultReferenceDocx = do "word/document.xml", "word/fontTable.xml", "word/footnotes.xml", + "word/comments.xml", "word/numbering.xml", "word/settings.xml", "word/webSettings.xml", @@ -761,15 +762,17 @@ readDefaultDataFile fname = case lookup (makeCanonical fname) dataFiles of Nothing -> throwError $ PandocCouldNotFindDataFileError fname Just contents -> return contents - where makeCanonical = Posix.joinPath . transformPathParts . splitDirectories - transformPathParts = reverse . foldl go [] - go as "." = as - go (_:as) ".." = as - go as x = x : as #else getDataFileName fname' >>= checkExistence >>= readFileStrict where fname' = if fname == "MANUAL.txt" then fname else "data" </> fname +makeCanonical :: FilePath -> FilePath +makeCanonical = Posix.joinPath . transformPathParts . splitDirectories + where transformPathParts = reverse . foldl go [] + go as "." = as + go (_:as) ".." = as + go as x = x : as + checkExistence :: PandocMonad m => FilePath -> m FilePath checkExistence fn = do exists <- fileExists fn @@ -914,7 +917,7 @@ newtype FileTree = FileTree {unFileTree :: M.Map FilePath FileInfo} getFileInfo :: FilePath -> FileTree -> Maybe FileInfo getFileInfo fp tree = - M.lookup (makeRelative "." fp) (unFileTree tree) + M.lookup (makeCanonical fp) (unFileTree tree) -- | Add the specified file to the FileTree. If file -- is a directory, add its contents recursively. @@ -937,7 +940,7 @@ addToFileTree tree fp = do -- | Insert an ersatz file into the 'FileTree'. insertInFileTree :: FilePath -> FileInfo -> FileTree -> FileTree insertInFileTree fp info (FileTree treemap) = - FileTree $ M.insert (makeRelative "." fp) info treemap + FileTree $ M.insert (makeCanonical fp) info treemap newtype PandocPure a = PandocPure { unPandocPure :: ExceptT PandocError -- cgit v1.2.3 From d5770b74969ac60836e40ca6a95a38ac0bf3c3bf Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 28 Dec 2017 11:03:34 -0800 Subject: Moved makeCanoncial definition out of ifdef! Also added slide2 to the default pptx, and reordered the data files in pandoc.cabal. --- src/Text/Pandoc/Class.hs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index 5ea736539..d0e33268f 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -688,8 +688,6 @@ getDefaultReferencePptx = do , "ppt/presProps.xml" , "ppt/presentation.xml" , "ppt/slideLayouts/_rels/slideLayout1.xml.rels" - , "ppt/slideLayouts/_rels/slideLayout10.xml.rels" - , "ppt/slideLayouts/_rels/slideLayout11.xml.rels" , "ppt/slideLayouts/_rels/slideLayout2.xml.rels" , "ppt/slideLayouts/_rels/slideLayout3.xml.rels" , "ppt/slideLayouts/_rels/slideLayout4.xml.rels" @@ -698,6 +696,8 @@ getDefaultReferencePptx = do , "ppt/slideLayouts/_rels/slideLayout7.xml.rels" , "ppt/slideLayouts/_rels/slideLayout8.xml.rels" , "ppt/slideLayouts/_rels/slideLayout9.xml.rels" + , "ppt/slideLayouts/_rels/slideLayout10.xml.rels" + , "ppt/slideLayouts/_rels/slideLayout11.xml.rels" , "ppt/slideLayouts/slideLayout1.xml" , "ppt/slideLayouts/slideLayout10.xml" , "ppt/slideLayouts/slideLayout11.xml" @@ -713,6 +713,8 @@ getDefaultReferencePptx = do , "ppt/slideMasters/slideMaster1.xml" , "ppt/slides/_rels/slide1.xml.rels" , "ppt/slides/slide1.xml" + , "ppt/slides/_rels/slide2.xml.rels" + , "ppt/slides/slide2.xml" , "ppt/tableStyles.xml" , "ppt/theme/theme1.xml" , "ppt/viewProps.xml" @@ -766,13 +768,6 @@ readDefaultDataFile fname = getDataFileName fname' >>= checkExistence >>= readFileStrict where fname' = if fname == "MANUAL.txt" then fname else "data" </> fname -makeCanonical :: FilePath -> FilePath -makeCanonical = Posix.joinPath . transformPathParts . splitDirectories - where transformPathParts = reverse . foldl go [] - go as "." = as - go (_:as) ".." = as - go as x = x : as - checkExistence :: PandocMonad m => FilePath -> m FilePath checkExistence fn = do exists <- fileExists fn @@ -781,6 +776,13 @@ checkExistence fn = do else throwError $ PandocCouldNotFindDataFileError fn #endif +makeCanonical :: FilePath -> FilePath +makeCanonical = Posix.joinPath . transformPathParts . splitDirectories + where transformPathParts = reverse . foldl go [] + go as "." = as + go (_:as) ".." = as + go as x = x : as + withPaths :: PandocMonad m => [FilePath] -> (FilePath -> m a) -> FilePath -> m a withPaths [] _ fp = throwError $ PandocResourceNotFound fp withPaths (p:ps) action fp = -- cgit v1.2.3 From 3494b6efd32bebb36e3bef204be6d51650ac6085 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 28 Dec 2017 12:06:38 -0800 Subject: Powerpoint writer tests: use IO. Otherwise we can't find the data files when compiled with -embed_data_files. --- src/Text/Pandoc/Class.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index d0e33268f..f93ddc7d1 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -145,6 +145,8 @@ import System.Directory (createDirectoryIfMissing, getDirectoryContents, import System.FilePath ((</>), (<.>), takeDirectory, takeExtension, dropExtension, isRelative, normalise) import qualified System.FilePath.Glob as IO (glob) +import qualified System.FilePath.Posix as Posix +import System.FilePath (splitDirectories) import qualified System.Directory as IO (getModificationTime) import Control.Monad as M (fail) import Control.Monad.State.Strict @@ -161,8 +163,6 @@ import Text.Pandoc.Translations (Term(..), Translations, lookupTerm, import qualified Debug.Trace #ifdef EMBED_DATA_FILES import Text.Pandoc.Data (dataFiles) -import qualified System.FilePath.Posix as Posix -import System.FilePath (splitDirectories) #else import qualified Paths_pandoc as Paths #endif -- cgit v1.2.3 From e10864d9d59ac292fbf69031263931a8f6c9209a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 28 Dec 2017 12:12:22 -0800 Subject: Changed format of --list-extensions. Now the + or - occurs first. --- src/Text/Pandoc/App.hs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 50464830b..e46d03025 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -1592,10 +1592,9 @@ options = (OptArg (\arg _ -> do let exts = getDefaultExtensions (fromMaybe "markdown" arg) - let showExt x = drop 4 (show x) ++ - if extensionEnabled x exts - then " +" - else " -" + let showExt x = (if extensionEnabled x exts + then '+' + else '-') : drop 4 (show x) mapM_ (UTF8.hPutStrLn stdout . showExt) ([minBound..maxBound] :: [Extension]) exitSuccess ) -- cgit v1.2.3 From 98fd9a978ab82a8738f03583c19fafc54097d035 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 28 Dec 2017 12:23:46 -0800 Subject: Alphabetical order Extension constructors. This makes them appear in order in `--list-extensions`. --- src/Text/Pandoc/Extensions.hs | 122 +++++++++++++++++++++--------------------- 1 file changed, 61 insertions(+), 61 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 7fa75cdd9..31fddb148 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -80,79 +80,79 @@ disableExtension x (Extensions exts) = Extensions (clearBit exts (fromEnum x)) -- | Individually selectable syntax extensions. data Extension = - Ext_footnotes -- ^ Pandoc/PHP/MMD style footnotes - | Ext_inline_notes -- ^ Pandoc-style inline notes - | Ext_pandoc_title_block -- ^ Pandoc title block - | Ext_yaml_metadata_block -- ^ YAML metadata block - | Ext_mmd_title_block -- ^ Multimarkdown metadata block - | Ext_table_captions -- ^ Pandoc-style table captions - | Ext_implicit_figures -- ^ A paragraph with just an image is a figure - | Ext_simple_tables -- ^ Pandoc-style simple tables - | Ext_multiline_tables -- ^ Pandoc-style multiline tables - | Ext_grid_tables -- ^ Grid tables (pandoc, reST) - | Ext_pipe_tables -- ^ Pipe tables (as in PHP markdown extra) - | Ext_citations -- ^ Pandoc/citeproc citations - | Ext_raw_tex -- ^ Allow raw TeX (other than math) - | Ext_raw_html -- ^ Allow raw HTML - | Ext_tex_math_dollars -- ^ TeX math between $..$ or $$..$$ - | Ext_tex_math_single_backslash -- ^ TeX math btw \(..\) \[..\] - | Ext_tex_math_double_backslash -- ^ TeX math btw \\(..\\) \\[..\\] - | Ext_latex_macros -- ^ Parse LaTeX macro definitions (for math only) - | Ext_fenced_code_blocks -- ^ Parse fenced code blocks - | Ext_fenced_code_attributes -- ^ Allow attributes on fenced code blocks - | Ext_backtick_code_blocks -- ^ GitHub style ``` code blocks - | Ext_inline_code_attributes -- ^ Allow attributes on inline code - | Ext_raw_attribute -- ^ Allow explicit raw blocks/inlines - | Ext_markdown_in_html_blocks -- ^ Interpret as markdown inside HTML blocks - | Ext_native_divs -- ^ Use Div blocks for contents of <div> tags - | Ext_fenced_divs -- ^ Allow fenced div syntax ::: - | Ext_native_spans -- ^ Use Span inlines for contents of <span> - | Ext_bracketed_spans -- ^ Bracketed spans with attributes - | Ext_markdown_attribute -- ^ Interpret text inside HTML as markdown - -- iff container has attribute 'markdown' - | Ext_escaped_line_breaks -- ^ Treat a backslash at EOL as linebreak - | Ext_link_attributes -- ^ link and image attributes - | Ext_mmd_link_attributes -- ^ MMD style reference link attributes - | Ext_autolink_bare_uris -- ^ Make all absolute URIs into links - | Ext_fancy_lists -- ^ Enable fancy list numbers and delimiters - | Ext_lists_without_preceding_blankline -- ^ Allow lists without preceding blank - | Ext_four_space_rule -- ^ Require 4-space indent for list contents - | Ext_startnum -- ^ Make start number of ordered list significant - | Ext_definition_lists -- ^ Definition lists as in pandoc, mmd, php - | Ext_compact_definition_lists -- ^ Definition lists without - -- space between items, and disallow laziness - | Ext_example_lists -- ^ Markdown-style numbered examples + Ext_abbreviations -- ^ PHP markdown extra abbreviation definitions | Ext_all_symbols_escapable -- ^ Make all non-alphanumerics escapable + | Ext_amuse -- ^ Enable Text::Amuse extensions to Emacs Muse markup | Ext_angle_brackets_escapable -- ^ Make < and > escapable - | Ext_intraword_underscores -- ^ Treat underscore inside word as literal + | Ext_ascii_identifiers -- ^ ascii-only identifiers for headers + | Ext_auto_identifiers -- ^ Automatic identifiers for headers + | Ext_autolink_bare_uris -- ^ Make all absolute URIs into links + | Ext_backtick_code_blocks -- ^ GitHub style ``` code blocks | Ext_blank_before_blockquote -- ^ Require blank line before a blockquote | Ext_blank_before_header -- ^ Require blank line before a header - | Ext_space_in_atx_header -- ^ Require space between # and header text - | Ext_strikeout -- ^ Strikeout using ~~this~~ syntax - | Ext_superscript -- ^ Superscript using ^this^ syntax - | Ext_subscript -- ^ Subscript using ~this~ syntax - | Ext_hard_line_breaks -- ^ All newlines become hard line breaks - | Ext_ignore_line_breaks -- ^ Newlines in paragraphs are ignored + | Ext_bracketed_spans -- ^ Bracketed spans with attributes + | Ext_citations -- ^ Pandoc/citeproc citations + | Ext_compact_definition_lists -- ^ Definition lists without space between items, + -- and disallow laziness + | Ext_definition_lists -- ^ Definition lists as in pandoc, mmd, php | Ext_east_asian_line_breaks -- ^ Newlines in paragraphs are ignored between - -- East Asian wide characters - | Ext_literate_haskell -- ^ Enable literate Haskell conventions - | Ext_abbreviations -- ^ PHP markdown extra abbreviation definitions + -- East Asian wide characters | Ext_emoji -- ^ Support emoji like :smile: - | Ext_auto_identifiers -- ^ Automatic identifiers for headers - | Ext_gfm_auto_identifiers -- ^ Automatic identifiers for headers, - -- using GitHub's method for generating identifiers - | Ext_ascii_identifiers -- ^ ascii-only identifiers for headers + | Ext_empty_paragraphs -- ^ Allow empty paragraphs + | Ext_epub_html_exts -- ^ Recognise the EPUB extended version of HTML + | Ext_escaped_line_breaks -- ^ Treat a backslash at EOL as linebreak + | Ext_example_lists -- ^ Markdown-style numbered examples + | Ext_fancy_lists -- ^ Enable fancy list numbers and delimiters + | Ext_fenced_code_attributes -- ^ Allow attributes on fenced code blocks + | Ext_fenced_code_blocks -- ^ Parse fenced code blocks + | Ext_fenced_divs -- ^ Allow fenced div syntax ::: + | Ext_footnotes -- ^ Pandoc/PHP/MMD style footnotes + | Ext_four_space_rule -- ^ Require 4-space indent for list contents + | Ext_gfm_auto_identifiers -- ^ Automatic identifiers for headers, using + -- GitHub's method for generating identifiers + | Ext_grid_tables -- ^ Grid tables (pandoc, reST) + | Ext_hard_line_breaks -- ^ All newlines become hard line breaks | Ext_header_attributes -- ^ Explicit header attributes {#id .class k=v} - | Ext_mmd_header_identifiers -- ^ Multimarkdown style header identifiers [myid] + | Ext_ignore_line_breaks -- ^ Newlines in paragraphs are ignored + | Ext_implicit_figures -- ^ A paragraph with just an image is a figure | Ext_implicit_header_references -- ^ Implicit reference links for headers + | Ext_inline_code_attributes -- ^ Allow attributes on inline code + | Ext_inline_notes -- ^ Pandoc-style inline notes + | Ext_intraword_underscores -- ^ Treat underscore inside word as literal + | Ext_latex_macros -- ^ Parse LaTeX macro definitions (for math only) | Ext_line_blocks -- ^ RST style line blocks - | Ext_epub_html_exts -- ^ Recognise the EPUB extended version of HTML + | Ext_link_attributes -- ^ link and image attributes + | Ext_lists_without_preceding_blankline -- ^ Allow lists without preceding blank + | Ext_literate_haskell -- ^ Enable literate Haskell conventions + | Ext_markdown_attribute -- ^ Interpret text inside HTML as markdown iff + -- container has attribute 'markdown' + | Ext_markdown_in_html_blocks -- ^ Interpret as markdown inside HTML blocks + | Ext_mmd_header_identifiers -- ^ Multimarkdown style header identifiers [myid] + | Ext_mmd_link_attributes -- ^ MMD style reference link attributes + | Ext_mmd_title_block -- ^ Multimarkdown metadata block + | Ext_multiline_tables -- ^ Pandoc-style multiline tables + | Ext_native_divs -- ^ Use Div blocks for contents of <div> tags + | Ext_native_spans -- ^ Use Span inlines for contents of <span> + | Ext_old_dashes -- ^ -- = em, - before number = en + | Ext_pandoc_title_block -- ^ Pandoc title block + | Ext_pipe_tables -- ^ Pipe tables (as in PHP markdown extra) + | Ext_raw_attribute -- ^ Allow explicit raw blocks/inlines + | Ext_raw_html -- ^ Allow raw HTML + | Ext_raw_tex -- ^ Allow raw TeX (other than math) | Ext_shortcut_reference_links -- ^ Shortcut reference links + | Ext_simple_tables -- ^ Pandoc-style simple tables | Ext_smart -- ^ "Smart" quotes, apostrophes, ellipses, dashes - | Ext_old_dashes -- ^ -- = em, - before number = en + | Ext_space_in_atx_header -- ^ Require space between # and header text | Ext_spaced_reference_links -- ^ Allow space between two parts of ref link - | Ext_amuse -- ^ Enable Text::Amuse extensions to Emacs Muse markup - | Ext_empty_paragraphs -- ^ Allow empty paragraphs + | Ext_startnum -- ^ Make start number of ordered list significant + | Ext_strikeout -- ^ Strikeout using ~~this~~ syntax + | Ext_subscript -- ^ Subscript using ~this~ syntax + | Ext_superscript -- ^ Superscript using ^this^ syntax + | Ext_table_captions -- ^ Pandoc-style table captions + | Ext_tex_math_dollars -- ^ TeX math between $..$ or $$..$$ + | Ext_tex_math_double_backslash -- ^ TeX math btw \\(..\\) \\[..\\] + | Ext_tex_math_single_backslash -- ^ TeX math btw \(..\) \[..\] + | Ext_yaml_metadata_block -- ^ YAML metadata block deriving (Show, Read, Enum, Eq, Ord, Bounded, Data, Typeable, Generic) -- | Extensions to be used with pandoc-flavored markdown. -- cgit v1.2.3 From 19bab48dccd670e272a6f949321de5413eb96136 Mon Sep 17 00:00:00 2001 From: oltolm <oleg.tolmatcev@gmail.com> Date: Sat, 30 Aug 2014 22:57:59 +0200 Subject: improve formatting of formulas in OpenDocument --- src/Text/Pandoc/Writers/ODT.hs | 38 ++++++++++++++++++++++++++------- src/Text/Pandoc/Writers/OpenDocument.hs | 20 ++++++++++++++++- 2 files changed, 49 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index 390d7c3ba..08b4206e3 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -56,7 +56,7 @@ import Text.Pandoc.XML import Text.TeXMath import Text.XML.Light -data ODTState = ODTState { stEntries :: [Entry] +newtype ODTState = ODTState { stEntries :: [Entry] } type O m = StateT ODTState m @@ -224,17 +224,39 @@ transformPicMath _ (Math t math) = do let dirname = "Formula-" ++ show (length entries) ++ "/" let fname = dirname ++ "content.xml" let entry = toEntry fname epochtime (fromStringLazy mathml) - modify $ \st -> st{ stEntries = entry : entries } + let fname' = dirname ++ "settings.xml" + let entry' = toEntry fname' epochtime $ documentSettings (t == InlineMath) + modify $ \st -> st{ stEntries = entry' : (entry : entries) } return $ RawInline (Format "opendocument") $ render Nothing $ - inTags False "draw:frame" [("text:anchor-type", - if t == DisplayMath - then "paragraph" - else "as-char") - ,("style:vertical-pos", "middle") - ,("style:vertical-rel", "text")] $ + inTags False "draw:frame" (if t == DisplayMath + then [("draw:style-name","fr2") + -- `draw:frame` does not support either + -- `style:vertical-pos` or `style:vertical-rel`, + -- therefore those attributes must go into the + -- `style:style` element + ,("text:anchor-type","paragraph")] + else [("draw:style-name","fr1") + ,("text:anchor-type","as-char")]) $ selfClosingTag "draw:object" [("xlink:href", dirname) , ("xlink:type", "simple") , ("xlink:show", "embed") , ("xlink:actuate", "onLoad")] transformPicMath _ x = return x + +documentSettings :: Bool -> B.ByteString +documentSettings isTextMode = fromStringLazy $ render Nothing + $ text "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + $$ + (inTags True "office:document-settings" + [("xmlns:office","urn:oasis:names:tc:opendocument:xmlns:office:1.0") + ,("xmlns:xlink","http://www.w3.org/1999/xlink") + ,("xmlns:config","urn:oasis:names:tc:opendocument:xmlns:config:1.0") + ,("xmlns:ooo","http://openoffice.org/2004/office") + ,("office:version","1.2")] $ + inTagsSimple "office:settings" $ + inTags False "config:config-item-set" + [("config:name", "ooo:configuration-settings")] $ + inTags False "config:config-item" [("config:name", "IsTextMode") + ,("config:type", "boolean")] $ + text $ if isTextMode then "true" else "false") diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index 8aa19dbb5..dc7d14d05 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -173,6 +173,24 @@ inTextStyle d = do return $ inTags False "text:span" [("text:style-name",styleName)] d +formulaStyles :: [Doc] +formulaStyles = [formulaStyle InlineMath, formulaStyle DisplayMath] + +formulaStyle :: MathType -> Doc +formulaStyle mt = inTags False "style:style" + [("style:name", if mt == InlineMath then "fr1" else "fr2") + ,("style:family", "graphic") + ,("style:parent-style-name", "Formula")] + $ selfClosingTag "style:graphic-properties" $ if mt == InlineMath then + [("style:vertical-pos", "middle") + ,("style:vertical-rel", "text")] + else + [("style:vertical-pos", "middle") + ,("style:vertical-rel", "paragraph-content") + ,("style:horizontal-pos", "center") + ,("style:horizontal-rel", "paragraph-content") + ,("style:wrap", "none")] + inHeaderTags :: PandocMonad m => Int -> Doc -> OD m Doc inHeaderTags i d = return $ inTags False "text:h" [ ("text:style-name", "Heading_20_" ++ show i) @@ -211,7 +229,7 @@ writeOpenDocument opts (Pandoc meta blocks) = do meta b <- render' `fmap` blocksToOpenDocument opts blocks return (b, m) - let styles = stTableStyles s ++ stParaStyles s ++ + let styles = stTableStyles s ++ stParaStyles s ++ formulaStyles ++ map snd (sortBy (flip (comparing fst)) ( Map.elems (stTextStyles s))) listStyle (n,l) = inTags True "text:list-style" -- cgit v1.2.3 From 8b575dbf845f82c6750a71f7372bae7067502554 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 26 Dec 2017 10:11:19 -0800 Subject: Filter changes. * Previously we ran all lua filters before JSON filters. * Now we run filters in the order they are presented on the command line, whether lua or JSON. * The type of `applyFilters` has changed (incompatible API change). * `applyLuaFilters` has been removed (incompatible API change). * Bump version to 2.1. See #4196. --- src/Text/Pandoc/App.hs | 80 +++++++++++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 34 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index e46d03025..7c463d743 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -35,11 +35,12 @@ Does a pandoc conversion based on command-line options. module Text.Pandoc.App ( convertWithOpts , Opt(..) + , LineEnding(..) + , Filter(..) , defaultOpts , parseOptions , options , applyFilters - , applyLuaFilters ) where import qualified Control.Exception as E import Control.Monad @@ -184,11 +185,13 @@ convertWithOpts opts = do Nothing -> return Nothing Just fp -> Just <$> UTF8.readFile fp + let isPandocCiteproc (JSONFilter f) = takeBaseName f == "pandoc-citeproc" + isPandocCiteproc _ = False -- --bibliography implies -F pandoc-citeproc for backwards compatibility: let needsCiteproc = isJust (lookup "bibliography" (optMetadata opts)) && optCiteMethod opts `notElem` [Natbib, Biblatex] && - "pandoc-citeproc" `notElem` map takeBaseName filters - let filters' = if needsCiteproc then "pandoc-citeproc" : filters + all (not . isPandocCiteproc) filters + let filters' = if needsCiteproc then JSONFilter "pandoc-citeproc" : filters else filters let sources = case optInputFiles opts of @@ -501,10 +504,9 @@ convertWithOpts opts = do then fillMediaBag else return) >=> return . addMetadata metadata - >=> applyLuaFilters datadir (optLuaFilters opts) format - >=> maybe return extractMedia (optExtractMedia opts) >=> applyTransforms transforms - >=> applyFilters readerOpts datadir filters' [format] + >=> applyFilters readerOpts filters' [format] + >=> maybe return extractMedia (optExtractMedia opts) ) case writer of @@ -583,6 +585,10 @@ externalFilter ropts f args' d = liftIO $ do where filterException :: E.SomeException -> IO a filterException e = E.throwIO $ PandocFilterError f (show e) +data Filter = LuaFilter FilePath + | JSONFilter FilePath + deriving (Show) + -- | Data structure for command line options. data Opt = Opt { optTabStop :: Int -- ^ Number of spaces per tab @@ -626,8 +632,7 @@ data Opt = Opt , optDpi :: Int -- ^ Dpi , optWrapText :: WrapOption -- ^ Options for wrapping text , optColumns :: Int -- ^ Line length in characters - , optFilters :: [FilePath] -- ^ Filters to apply - , optLuaFilters :: [FilePath] -- ^ Lua filters to apply + , optFilters :: [Filter] -- ^ Filters to apply , optEmailObfuscation :: ObfuscationMethod , optIdentifierPrefix :: String , optStripEmptyParagraphs :: Bool -- ^ Strip empty paragraphs @@ -700,7 +705,6 @@ defaultOpts = Opt , optWrapText = WrapAuto , optColumns = 72 , optFilters = [] - , optLuaFilters = [] , optEmailObfuscation = NoObfuscation , optIdentifierPrefix = "" , optStripEmptyParagraphs = False @@ -832,41 +836,46 @@ applyTransforms transforms d = return $ foldr ($) d transforms -- First we check to see if a filter is found. If not, and if it's -- not an absolute path, we check to see whether it's in `userdir/filters`. -- If not, we leave it unchanged. -expandFilterPath :: MonadIO m => Maybe FilePath -> FilePath -> m FilePath -expandFilterPath mbDatadir fp = liftIO $ do - fpExists <- doesFileExist fp +expandFilterPath :: PandocMonad m => FilePath -> m FilePath +expandFilterPath fp = do + mbDatadir <- getUserDataDir + fpExists <- fileExists fp if fpExists then return fp else case mbDatadir of Just datadir | isRelative fp -> do let filterPath = datadir </> "filters" </> fp - filterPathExists <- doesFileExist filterPath + filterPathExists <- fileExists filterPath if filterPathExists then return filterPath else return fp _ -> return fp -applyLuaFilters :: Maybe FilePath -> [FilePath] -> String -> Pandoc - -> PandocIO Pandoc -applyLuaFilters mbDatadir filters format d = do - expandedFilters <- mapM (expandFilterPath mbDatadir) filters - let go f d' = do - res <- runLuaFilter f format d' - case res of - Right x -> return x - Left (LuaException s) -> E.throw (PandocFilterError f s) - foldrM ($) d $ map go expandedFilters - -applyFilters :: MonadIO m - => ReaderOptions - -> Maybe FilePath - -> [FilePath] +applyFilters :: ReaderOptions + -> [Filter] -> [String] -> Pandoc - -> m Pandoc -applyFilters ropts mbDatadir filters args d = do - expandedFilters <- mapM (expandFilterPath mbDatadir) filters - foldrM ($) d $ map (flip (externalFilter ropts) args) expandedFilters + -> PandocIO Pandoc +applyFilters ropts filters args d = do + foldrM ($) d $ map (applyFilter ropts args) filters + +applyFilter :: ReaderOptions + -> [String] + -> Filter + -> Pandoc + -> PandocIO Pandoc +applyFilter _ropts args (LuaFilter f) d = do + f' <- expandFilterPath f + let format = case args of + (x:_) -> x + _ -> error "Format not supplied for lua filter" + res <- runLuaFilter f' format d + case res of + Right x -> return x + Left (LuaException s) -> E.throw (PandocFilterError f s) +applyFilter ropts args (JSONFilter f) d = do + f' <- expandFilterPath f + liftIO $ externalFilter ropts f' args d readSource :: FilePath -> PandocIO Text readSource "-" = liftIO (UTF8.toText <$> BS.getContents) @@ -968,13 +977,15 @@ options = , Option "F" ["filter"] (ReqArg - (\arg opt -> return opt { optFilters = arg : optFilters opt }) + (\arg opt -> return opt { optFilters = + JSONFilter arg : optFilters opt }) "PROGRAM") "" -- "External JSON filter" , Option "" ["lua-filter"] (ReqArg - (\arg opt -> return opt { optLuaFilters = arg : optLuaFilters opt }) + (\arg opt -> return opt { optFilters = + LuaFilter arg : optFilters opt }) "SCRIPTPATH") "" -- "Lua filter" @@ -1720,4 +1731,5 @@ deprecatedOption o msg = -- see https://github.com/jgm/pandoc/pull/4083 -- using generic deriving caused long compilation times $(deriveJSON defaultOptions ''LineEnding) +$(deriveJSON defaultOptions ''Filter) $(deriveJSON defaultOptions ''Opt) -- cgit v1.2.3 From da64e5baa4de1f2caa9b0e338af685d70bc32f29 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 28 Dec 2017 10:30:29 -0800 Subject: Class: make FileTree opaque. This forces uses to interact with it using `insertInFileTree` and `getFileInfo`, which normalize file names. --- 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 f93ddc7d1..f8d6b6737 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -78,7 +78,7 @@ module Text.Pandoc.Class ( PandocMonad(..) , getResourcePath , PandocIO(..) , PandocPure(..) - , FileTree(..) + , FileTree , FileInfo(..) , addToFileTree , insertInFileTree -- cgit v1.2.3 From 9be2c7624cb0cf3ef63516e5df959672958058bc Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Fri, 29 Dec 2017 09:40:22 +0100 Subject: data/pandoc.lua: drop function pandoc.global_filter The function `global_filter` was used internally to get the implicitly defined global filter. It was of little value to end-users, but caused unnecessary code duplication in pandoc. The function has hence been dropped. Internally, the global filter is now received by interpreting the global table as lua filter. This is a Lua API change. --- src/Text/Pandoc/Lua.hs | 18 +++++++----------- src/Text/Pandoc/Lua/Filter.hs | 2 -- src/Text/Pandoc/Lua/Util.hs | 1 + 3 files changed, 8 insertions(+), 13 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index ee259e3fd..02e1b0424 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -32,7 +32,7 @@ module Text.Pandoc.Lua , pushPandocModule ) where -import Control.Monad (when, (>=>)) +import Control.Monad ((>=>)) import Foreign.Lua (FromLuaStack (peek), Lua, LuaException (..), Status (OK), ToLuaStack (push)) import Text.Pandoc.Class (PandocIO) @@ -40,6 +40,7 @@ import Text.Pandoc.Definition (Pandoc) import Text.Pandoc.Lua.Filter (LuaFilter, walkMWithLuaFilter) import Text.Pandoc.Lua.Init (runPandocLua) import Text.Pandoc.Lua.Module.Pandoc (pushModule) -- TODO: remove +import Text.Pandoc.Lua.Util (popValue) import qualified Foreign.Lua as Lua -- | Run the Lua filter in @filterPath@ for a transformation to target @@ -63,22 +64,17 @@ runLuaFilter' filterPath format pd = do Lua.throwLuaError luaErrMsg else do newtop <- Lua.gettop - -- Use the implicitly defined global filter if nothing was returned - when (newtop - top < 1) pushGlobalFilter - luaFilters <- peek (-1) + -- Use the returned filters, or the implicitly defined global filter if + -- nothing was returned. + luaFilters <- if (newtop - top >= 1) + then peek (-1) + else Lua.getglobal "_G" *> fmap (:[]) popValue runAll luaFilters pd where registerFormat = do push format Lua.setglobal "FORMAT" -pushGlobalFilter :: Lua () -pushGlobalFilter = do - Lua.newtable - Lua.getglobal' "pandoc.global_filter" - Lua.call 0 1 - Lua.rawseti (-2) 1 - runAll :: [LuaFilter] -> Pandoc -> Lua Pandoc runAll = foldr ((>=>) . walkMWithLuaFilter) return diff --git a/src/Text/Pandoc/Lua/Filter.hs b/src/Text/Pandoc/Lua/Filter.hs index 687ab2be5..9e109bb52 100644 --- a/src/Text/Pandoc/Lua/Filter.hs +++ b/src/Text/Pandoc/Lua/Filter.hs @@ -164,5 +164,3 @@ singleElement x = do Lua.throwLuaError $ "Error while trying to get a filter's return " ++ "value from lua stack.\n" ++ err - - diff --git a/src/Text/Pandoc/Lua/Util.hs b/src/Text/Pandoc/Lua/Util.hs index 1f7664fc0..2958bd734 100644 --- a/src/Text/Pandoc/Lua/Util.hs +++ b/src/Text/Pandoc/Lua/Util.hs @@ -37,6 +37,7 @@ module Text.Pandoc.Lua.Util , setRawInt , addRawInt , raiseError + , popValue , OrNil (..) , PushViaCall , pushViaCall -- cgit v1.2.3 From f42839ee2c14cf707c1059c0b3f5e4b31c642efb Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Fri, 29 Dec 2017 10:06:38 +0100 Subject: Lua filters: stop exporting pushPandocModule The function `pushPandocModule` was exported by Text.Pandoc.Lua to enable simpler testing. The introduction of `runPandocLua` renders direct use of this function obsolete. (API change) --- src/Text/Pandoc/Lua.hs | 6 ------ 1 file changed, 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 02e1b0424..d02963418 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -29,7 +29,6 @@ module Text.Pandoc.Lua ( LuaException (..) , runLuaFilter , runPandocLua - , pushPandocModule ) where import Control.Monad ((>=>)) @@ -39,7 +38,6 @@ import Text.Pandoc.Class (PandocIO) import Text.Pandoc.Definition (Pandoc) import Text.Pandoc.Lua.Filter (LuaFilter, walkMWithLuaFilter) import Text.Pandoc.Lua.Init (runPandocLua) -import Text.Pandoc.Lua.Module.Pandoc (pushModule) -- TODO: remove import Text.Pandoc.Lua.Util (popValue) import qualified Foreign.Lua as Lua @@ -77,7 +75,3 @@ runLuaFilter' filterPath format pd = do runAll :: [LuaFilter] -> Pandoc -> Lua Pandoc runAll = foldr ((>=>) . walkMWithLuaFilter) return - --- | DEPRECATED: Push the pandoc module to the Lua Stack. -pushPandocModule :: Maybe FilePath -> Lua Lua.NumResults -pushPandocModule = pushModule -- cgit v1.2.3 From 4fc3f511863c578be6a3237d02133da25db0ce05 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sat, 30 Dec 2017 08:21:42 -0500 Subject: Docx reader: Read multiple children of w:sdtContents` Previously we had only read the first child of an sdtContents tag. Now we replace sdt with all children of the sdtContents tag. This changes the expected test result of our nested_anchors test, since now we read docx's generated TOCs. --- src/Text/Pandoc/Readers/Docx/Parse.hs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx/Parse.hs b/src/Text/Pandoc/Readers/Docx/Parse.hs index 48a512be2..071f901b6 100644 --- a/src/Text/Pandoc/Readers/Docx/Parse.hs +++ b/src/Text/Pandoc/Readers/Docx/Parse.hs @@ -118,17 +118,21 @@ mapD f xs = in concatMapM handler xs -unwrapSDT :: NameSpaces -> Content -> Content +unwrapSDT :: NameSpaces -> Content -> [Content] unwrapSDT ns (Elem element) | isElem ns "w" "sdt" element , Just sdtContent <- findChildByName ns "w" "sdtContent" element - , child : _ <- elChildren sdtContent - = Elem child -unwrapSDT _ content = content + = map Elem $ elChildren sdtContent +unwrapSDT _ content = [content] + +unwrapSDTchild :: NameSpaces -> Content -> Content +unwrapSDTchild ns (Elem element) = + Elem $ element { elContent = concatMap (unwrapSDT ns) (elContent element) } +unwrapSDTchild _ content = content walkDocument' :: NameSpaces -> XMLC.Cursor -> XMLC.Cursor walkDocument' ns cur = - let modifiedCur = XMLC.modifyContent (unwrapSDT ns) cur + let modifiedCur = XMLC.modifyContent (unwrapSDTchild ns) cur in case XMLC.nextDF modifiedCur of Just cur' -> walkDocument' ns cur' -- cgit v1.2.3 From bbfb6f0c3cda85588ae6a309a3402a48275e3b21 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sat, 30 Dec 2017 17:37:06 +0300 Subject: Muse writer: don't escape URIs from AST --- src/Text/Pandoc/Writers/Muse.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index 34936504e..ff393e574 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -377,7 +377,7 @@ inlineToMuse (Link _ txt (src, _)) = return $ "[[" <> text (escapeLink x) <> "]]" _ -> do contents <- inlineListToMuse txt return $ "[[" <> text (escapeLink src) <> "][" <> contents <> "]]" - where escapeLink lnk = escapeURI (if isImageUrl lnk then "URL:" ++ lnk else lnk) + where escapeLink lnk = if isImageUrl lnk then "URL:" ++ lnk else lnk -- Taken from muse-image-regexp defined in Emacs Muse file lisp/muse-regexps.el imageExtensions = [".eps", ".gif", ".jpg", ".jpeg", ".pbm", ".png", ".tiff", ".xbm", ".xpm"] isImageUrl = (`elem` imageExtensions) . takeExtension -- cgit v1.2.3 From debc6d13aa4aba79d957757ba8802cef65187afc Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sun, 31 Dec 2017 00:26:05 +0300 Subject: Muse reader: automatically translate #cover into #cover-image Amusewiki uses #cover directive to specify cover image. --- src/Text/Pandoc/Readers/Muse.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 7142c249f..d86b47e83 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -179,7 +179,9 @@ directive :: PandocMonad m => MuseParser m () directive = do ext <- getOption readerExtensions (key, value) <- if extensionEnabled Ext_amuse ext then parseAmuseDirective else parseEmacsDirective - updateState $ \st -> st { stateMeta' = B.setMeta key <$> value <*> stateMeta' st } + updateState $ \st -> st { stateMeta' = B.setMeta (translateKey key) <$> value <*> stateMeta' st } + where translateKey "cover" = "cover-image" + translateKey x = x -- -- block parsers -- cgit v1.2.3 From e90c714c73be58ef09b08272c676f96e2a21c767 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sat, 30 Dec 2017 22:17:06 -0500 Subject: Docx reader: Remove unused anchors. Docx produces a lot of anchors with nothing pointing to them -- we now remove these to produce cleaner output. Note that this has to occur at the end of the process because it has to follow link/anchor rewriting. Closes #3679. --- src/Text/Pandoc/Readers/Docx.hs | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index d73da3085..248cb0b84 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -118,6 +118,7 @@ readDocx _ _ = throwError $ PandocSomeError "couldn't parse docx file" data DState = DState { docxAnchorMap :: M.Map String String + , docxAnchorSet :: Set.Set String , docxMediaBag :: MediaBag , docxDropCap :: Inlines , docxWarnings :: [String] @@ -128,6 +129,7 @@ data DState = DState { docxAnchorMap :: M.Map String String instance Default DState where def = DState { docxAnchorMap = M.empty + , docxAnchorSet = mempty , docxMediaBag = mempty , docxDropCap = mempty , docxWarnings = [] @@ -561,7 +563,7 @@ bodyPartToBlocks (ListItem pPr numId lvl (Just levelInfo) parparts) = do ] modify $ \st -> st{ docxListState = M.insert (numId, lvl) start listState} blks <- bodyPartToBlocks (Paragraph pPr parparts) - return $ divWith ("", ["list-item"], kvs) blks + return $ divWith ("", ["list-item"], kvs) blks bodyPartToBlocks (ListItem pPr _ _ _ parparts) = let pPr' = pPr {pStyle = "ListParagraph": pStyle pPr} in @@ -603,21 +605,41 @@ bodyPartToBlocks (OMathPara e) = 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 - Just newTarget -> Link attr ils ('#':newTarget, title) - Nothing -> l + case M.lookup target anchorMap of + Just newTarget -> do + modify $ \s -> s{docxAnchorSet = Set.insert newTarget (docxAnchorSet s)} + return $ Link attr ils ('#':newTarget, title) + Nothing -> do + modify $ \s -> s{docxAnchorSet = Set.insert target (docxAnchorSet s)} + return l rewriteLink' il = return il rewriteLinks :: PandocMonad m => [Block] -> DocxContext m [Block] rewriteLinks = mapM (walkM rewriteLink') +removeOrphanAnchors'' :: PandocMonad m => Inline -> DocxContext m [Inline] +removeOrphanAnchors'' s@(Span (ident, classes, _) ils) + | "anchor" `elem` classes = do + anchorSet <- gets docxAnchorSet + return $ if ident `Set.member` anchorSet + then [s] + else ils +removeOrphanAnchors'' il = return [il] + +removeOrphanAnchors' :: PandocMonad m => [Inline] -> DocxContext m [Inline] +removeOrphanAnchors' ils = liftM concat $ mapM removeOrphanAnchors'' ils + +removeOrphanAnchors :: PandocMonad m => [Block] -> DocxContext m [Block] +removeOrphanAnchors = mapM (walkM removeOrphanAnchors') + 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 - return (meta, blks') + blks'' <- removeOrphanAnchors blks' + return (meta, blks'') docxToOutput :: PandocMonad m => ReaderOptions -- cgit v1.2.3 From d6d4388f6f738f99c4b337645a1de715e699114d Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 30 Dec 2017 22:43:46 -0800 Subject: LaTeX reader: Simplified a check for raw tex command. --- src/Text/Pandoc/Readers/LaTeX.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index e0972bb6c..a0447962c 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -280,12 +280,12 @@ rawLaTeXBlock = do rawLaTeXInline :: (PandocMonad m, HasMacros s, HasReaderOptions s) => ParserT String s m String rawLaTeXInline = do - lookAhead (try (char '\\' >> letter) <|> char '$') + lookAhead (try (char '\\' >> letter)) rawLaTeXParser (inlineEnvironment <|> inlineCommand') >>= applyMacros . snd inlineCommand :: PandocMonad m => ParserT String ParserState m Inlines inlineCommand = do - lookAhead (try (char '\\' >> letter) <|> char '$') + lookAhead (try (char '\\' >> letter)) fst <$> rawLaTeXParser (inlineEnvironment <|> inlineCommand') tokenize :: SourceName -> Text -> [Tok] -- cgit v1.2.3 From a274e15f0d236140fec7e4554117fcb55a219566 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 30 Dec 2017 23:33:03 -0800 Subject: Markdown reader: Avoid parsing raw tex unless \ + letter seen. This seems to help with the performance problem, #4216. --- src/Text/Pandoc/Readers/Markdown.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index e7ad9d8ba..f5e6512f8 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -1120,6 +1120,7 @@ rawVerbatimBlock = htmlInBalanced isVerbTag rawTeXBlock :: PandocMonad m => MarkdownParser m (F Blocks) rawTeXBlock = do guardEnabled Ext_raw_tex + lookAhead $ try $ char '\\' >> letter result <- (B.rawBlock "context" . trim . concat <$> many1 ((++) <$> (rawConTeXtEnvironment <|> conTeXtCommand) <*> (blanklines <|> many spaceChar))) @@ -1906,7 +1907,7 @@ inlineNote = try $ do rawLaTeXInline' :: PandocMonad m => MarkdownParser m (F Inlines) rawLaTeXInline' = try $ do guardEnabled Ext_raw_tex - lookAhead (char '\\') + lookAhead $ try $ char '\\' >> letter notFollowedBy' rawConTeXtEnvironment s <- rawLaTeXInline return $ return $ B.rawInline "tex" s -- "tex" because it might be context -- cgit v1.2.3 From 836153de43933dca3205e4459f55979d467f927e Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sun, 31 Dec 2017 09:09:22 -0500 Subject: Docx Reader: Combine adjacent anchors. There isn't any reason to have numberous anchors in the same place, since we can't maintain docx's non-nesting overlapping. So we reduce to a single anchor, and have all links pointing to one of the overlapping anchors point to that one. This changes the behavior from commit e90c714c7 slightly (use the first anchor instead of the last) so we change the expected test result. Note that because this produces a state that has to be set after every invocation of `parPartToInlines`, we make the main function into a primed subfunction `parPartToInlines'`, and make `parPartToInlines` a wrapper around that. --- src/Text/Pandoc/Readers/Docx.hs | 67 +++++++++++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 20 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index 248cb0b84..6ca1590a4 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -82,6 +82,7 @@ import qualified Data.ByteString.Lazy as B import Data.Default (Default) import Data.List (delete, intersect) import qualified Data.Map as M +import Data.Maybe (isJust) import Data.Sequence (ViewL (..), viewl) import qualified Data.Sequence as Seq import qualified Data.Set as Set @@ -119,6 +120,7 @@ readDocx _ _ = data DState = DState { docxAnchorMap :: M.Map String String , docxAnchorSet :: Set.Set String + , docxImmedPrevAnchor :: Maybe String , docxMediaBag :: MediaBag , docxDropCap :: Inlines , docxWarnings :: [String] @@ -130,6 +132,7 @@ data DState = DState { docxAnchorMap :: M.Map String String instance Default DState where def = DState { docxAnchorMap = M.empty , docxAnchorSet = mempty + , docxImmedPrevAnchor = Nothing , docxMediaBag = mempty , docxDropCap = mempty , docxWarnings = [] @@ -341,9 +344,26 @@ blocksToInlinesWarn cmtId blks = do "Docx comment " ++ cmtId ++ " will not retain formatting" return $ blocksToInlines' blkList +-- The majority of work in this function is done in the primted +-- subfunction `partPartToInlines'`. We make this wrapper so that we +-- don't have to modify `docxImmedPrevAnchor` state after every function. parPartToInlines :: PandocMonad m => ParPart -> DocxContext m Inlines -parPartToInlines (PlainRun r) = runToInlines r -parPartToInlines (Insertion _ author date runs) = do +parPartToInlines parPart = + case parPart of + (BookMark _ anchor) | not $ anchor `elem` dummyAnchors -> do + inHdrBool <- asks docxInHeaderBlock + ils <- parPartToInlines' parPart + immedPrevAnchor <- gets docxImmedPrevAnchor + unless (isJust immedPrevAnchor || inHdrBool) + (modify $ \s -> s{ docxImmedPrevAnchor = Just anchor}) + return ils + _ -> do + ils <- parPartToInlines' parPart + modify $ \s -> s{ docxImmedPrevAnchor = Nothing} + return ils +parPartToInlines' :: PandocMonad m => ParPart -> DocxContext m Inlines +parPartToInlines' (PlainRun r) = runToInlines r +parPartToInlines' (Insertion _ author date runs) = do opts <- asks docxOptions case readerTrackChanges opts of AcceptChanges -> smushInlines <$> mapM runToInlines runs @@ -352,7 +372,7 @@ parPartToInlines (Insertion _ author date runs) = do ils <- smushInlines <$> mapM runToInlines runs let attr = ("", ["insertion"], [("author", author), ("date", date)]) return $ spanWith attr ils -parPartToInlines (Deletion _ author date runs) = do +parPartToInlines' (Deletion _ author date runs) = do opts <- asks docxOptions case readerTrackChanges opts of AcceptChanges -> return mempty @@ -361,7 +381,7 @@ parPartToInlines (Deletion _ author date runs) = do ils <- smushInlines <$> mapM runToInlines runs let attr = ("", ["deletion"], [("author", author), ("date", date)]) return $ spanWith attr ils -parPartToInlines (CommentStart cmtId author date bodyParts) = do +parPartToInlines' (CommentStart cmtId author date bodyParts) = do opts <- asks docxOptions case readerTrackChanges opts of AllChanges -> do @@ -370,16 +390,16 @@ parPartToInlines (CommentStart cmtId author date bodyParts) = do let attr = ("", ["comment-start"], [("id", cmtId), ("author", author), ("date", date)]) return $ spanWith attr ils _ -> return mempty -parPartToInlines (CommentEnd cmtId) = do +parPartToInlines' (CommentEnd cmtId) = do opts <- asks docxOptions case readerTrackChanges opts of AllChanges -> do let attr = ("", ["comment-end"], [("id", cmtId)]) return $ spanWith attr mempty _ -> return mempty -parPartToInlines (BookMark _ anchor) | anchor `elem` dummyAnchors = +parPartToInlines' (BookMark _ anchor) | anchor `elem` dummyAnchors = return mempty -parPartToInlines (BookMark _ anchor) = +parPartToInlines' (BookMark _ anchor) = -- We record these, so we can make sure not to overwrite -- user-defined anchor links with header auto ids. do @@ -395,27 +415,34 @@ parPartToInlines (BookMark _ anchor) = -- of rewriting user-defined anchor links. However, since these -- are not defined in pandoc, it seems like a necessary evil to -- avoid an extra pass. - let newAnchor = - if not inHdrBool && anchor `elem` M.elems anchorMap - then uniqueIdent [Str anchor] (Set.fromList $ M.elems anchorMap) - else anchor - unless inHdrBool - (modify $ \s -> s { docxAnchorMap = M.insert anchor newAnchor anchorMap}) - return $ spanWith (newAnchor, ["anchor"], []) mempty -parPartToInlines (Drawing fp title alt bs ext) = do + immedPrevAnchor <- gets docxImmedPrevAnchor + case immedPrevAnchor of + Just prevAnchor -> do + unless inHdrBool + (modify $ \s -> s { docxAnchorMap = M.insert anchor prevAnchor anchorMap}) + return mempty + Nothing -> do + let newAnchor = + if not inHdrBool && anchor `elem` M.elems anchorMap + then uniqueIdent [Str anchor] (Set.fromList $ M.elems anchorMap) + else anchor + unless inHdrBool + (modify $ \s -> s { docxAnchorMap = M.insert anchor newAnchor anchorMap}) + return $ spanWith (newAnchor, ["anchor"], []) mempty +parPartToInlines' (Drawing fp title alt bs ext) = do (lift . lift) $ P.insertMedia fp Nothing bs return $ imageWith (extentToAttr ext) fp title $ text alt -parPartToInlines Chart = +parPartToInlines' Chart = return $ spanWith ("", ["chart"], []) $ text "[CHART]" -parPartToInlines (InternalHyperLink anchor runs) = do +parPartToInlines' (InternalHyperLink anchor runs) = do ils <- smushInlines <$> mapM runToInlines runs return $ link ('#' : anchor) "" ils -parPartToInlines (ExternalHyperLink target runs) = do +parPartToInlines' (ExternalHyperLink target runs) = do ils <- smushInlines <$> mapM runToInlines runs return $ link target "" ils -parPartToInlines (PlainOMath exps) = +parPartToInlines' (PlainOMath exps) = return $ math $ writeTeX exps -parPartToInlines (SmartTag runs) = do +parPartToInlines' (SmartTag runs) = do smushInlines <$> mapM runToInlines runs isAnchorSpan :: Inline -> Bool -- cgit v1.2.3 From e6b04fa0cfbb9453dbb203a71740ef6c3404d589 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sun, 31 Dec 2017 10:18:03 -0500 Subject: Docx reader: minor cleanup. --- src/Text/Pandoc/Readers/Docx.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index 6ca1590a4..a2f22c1ea 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -344,7 +344,7 @@ blocksToInlinesWarn cmtId blks = do "Docx comment " ++ cmtId ++ " will not retain formatting" return $ blocksToInlines' blkList --- The majority of work in this function is done in the primted +-- The majority of work in this function is done in the primed -- subfunction `partPartToInlines'`. We make this wrapper so that we -- don't have to modify `docxImmedPrevAnchor` state after every function. parPartToInlines :: PandocMonad m => ParPart -> DocxContext m Inlines @@ -361,6 +361,7 @@ parPartToInlines parPart = ils <- parPartToInlines' parPart modify $ \s -> s{ docxImmedPrevAnchor = Nothing} return ils + parPartToInlines' :: PandocMonad m => ParPart -> DocxContext m Inlines parPartToInlines' (PlainRun r) = runToInlines r parPartToInlines' (Insertion _ author date runs) = do -- cgit v1.2.3 From ecc46e229fde934f163d1f646383d24bfe2039e1 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 1 Jan 2018 14:11:17 -0800 Subject: Lua.Module.Utils: make stringify work on MetaValues. I'm sure this was intended in the first place, but currently only Meta is supported. --- src/Text/Pandoc/Lua/Module/Utils.hs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/Module/Utils.hs b/src/Text/Pandoc/Lua/Module/Utils.hs index 35495dae1..c0d7397ce 100644 --- a/src/Text/Pandoc/Lua/Module/Utils.hs +++ b/src/Text/Pandoc/Lua/Module/Utils.hs @@ -31,7 +31,7 @@ module Text.Pandoc.Lua.Module.Utils import Control.Applicative ((<|>)) import Foreign.Lua (FromLuaStack, Lua, LuaInteger, NumResults) -import Text.Pandoc.Definition (Pandoc, Meta, Block, Inline) +import Text.Pandoc.Definition (Pandoc, Meta, MetaValue, Block, Inline) import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Lua.Util (OrNil (OrNil), addFunction) @@ -76,12 +76,14 @@ stringify el = return $ case el of InlineElement i -> Shared.stringify i BlockElement b -> Shared.stringify b MetaElement m -> Shared.stringify m + MetaValueElement m -> Shared.stringify m data AstElement = PandocElement Pandoc | MetaElement Meta | BlockElement Block | InlineElement Inline + | MetaValueElement MetaValue deriving (Show) instance FromLuaStack AstElement where @@ -90,6 +92,7 @@ instance FromLuaStack AstElement where <|> (InlineElement <$> Lua.peek idx) <|> (BlockElement <$> Lua.peek idx) <|> (MetaElement <$> Lua.peek idx) + <|> (MetaValueElement <$> Lua.peek idx) case res of Right x -> return x Left _ -> Lua.throwLuaError -- cgit v1.2.3 From 4f43a1d250287e8d89ee5cd3c8b020ea6fe83758 Mon Sep 17 00:00:00 2001 From: stalmon <35015406+stalmon@users.noreply.github.com> Date: Tue, 2 Jan 2018 01:14:36 +0100 Subject: Removed redundant subtitle in title subtitle is allready used to create a subtitle for the document appending the subtitle to the main title leads to double subtitle in the document --- src/Text/Pandoc/Writers/Docx.hs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index e4240ca4f..88caccdf7 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -763,9 +763,7 @@ makeTOC _ = return [] -- OpenXML elements (the main document and footnotes). writeOpenXML :: (PandocMonad m) => WriterOptions -> Pandoc -> WS m ([Element], [Element],[Element]) writeOpenXML opts (Pandoc meta blocks) = do - let tit = docTitle meta ++ case lookupMeta "subtitle" meta of - Just (MetaBlocks [Plain xs]) -> LineBreak : xs - _ -> [] + let tit = docTitle meta let auths = docAuthors meta let dat = docDate meta let abstract' = case lookupMeta "abstract" meta of -- cgit v1.2.3 From 5733183b3de4ab93a1341410eef4249f09648f3f Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 1 Jan 2018 16:42:19 -0800 Subject: Markdown reader: rewrite inlinesInBalancedBrackets. The rewrite is much more direct, avoiding parseFromString. And it performs significantly better; unfortunately, parsing time still increases exponentially. See #1735. --- src/Text/Pandoc/Readers/Markdown.hs | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index f5e6512f8..4a09c2aad 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -148,25 +148,19 @@ litChar = escapedChar' -- | Parse a sequence of inline elements between square brackets, -- including inlines between balanced pairs of square brackets. inlinesInBalancedBrackets :: PandocMonad m => MarkdownParser m (F Inlines) -inlinesInBalancedBrackets = do - char '[' - pos <- getPosition - (_, raw) <- withRaw $ charsInBalancedBrackets 1 - guard $ not $ null raw - parseFromString' (setPosition pos >> - trimInlinesF <$> inlines) (init raw) - -charsInBalancedBrackets :: PandocMonad m => Int -> MarkdownParser m () -charsInBalancedBrackets 0 = return () -charsInBalancedBrackets openBrackets = - (char '[' >> charsInBalancedBrackets (openBrackets + 1)) - <|> (char ']' >> charsInBalancedBrackets (openBrackets - 1)) - <|> (( (() <$ code) - <|> (() <$ escapedChar') - <|> (newline >> notFollowedBy blankline) - <|> skipMany1 (noneOf "[]`\n\\") - <|> (() <$ count 1 (oneOf "`\\")) - ) >> charsInBalancedBrackets openBrackets) +inlinesInBalancedBrackets = try $ char '[' >> go 1 + where go :: PandocMonad m => Int -> MarkdownParser m (F Inlines) + go 0 = return mempty + go openBrackets = + (mappend <$> (bracketedSpan <|> link <|> image) <*> + go openBrackets) + <|> ((if openBrackets > 1 + then (return (B.str "]") <>) + else id) <$> + (char ']' >> go (openBrackets - 1))) + <|> ((return (B.str "[") <>) <$> + (char '[' >> go (openBrackets + 1))) + <|> (mappend <$> inline <*> go openBrackets) -- -- document structure -- cgit v1.2.3 From b9d73428c7d420cf7f07e4e20263d5d705c73f85 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 1 Jan 2018 21:44:06 -0800 Subject: Docx writer: Fix custom styles with spaces in the name. Custom styles with spaces worked for divs but not for spans. This commit fixes the problem. Closes #3290. --- src/Text/Pandoc/Writers/Docx.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index e4240ca4f..3959585c6 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -1134,7 +1134,7 @@ inlineToOpenXML' opts (Span (ident,classes,kvs) ils) = do modify $ \s -> s{stDynamicTextProps = Set.insert sty (stDynamicTextProps s)} - return $ withTextProp (rCustomStyle sty) + return $ withTextPropM (rStyleM sty) _ -> return id let dirmod = case lookup "dir" kvs of Just "rtl" -> local (\env -> env { envRTL = True }) -- cgit v1.2.3 From 053a1dcd102c5198267734ce140fff3aa9c08bac Mon Sep 17 00:00:00 2001 From: Sebastian Talmon <35015406+stalmon@users.noreply.github.com> Date: Tue, 2 Jan 2018 09:43:04 +0100 Subject: firstRow table definition compatibility for Word 2016 Word 2016 seems to use a default value of "1" for table headers, if there is no firstRow definition (although a default value of 0 is documented), so all tables get the first Row formatted as header. Setting the parameter to 0 if the table has no header row fixes this for Word 2016 --- src/Text/Pandoc/Writers/Docx.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 88caccdf7..ac8bef003 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -981,7 +981,7 @@ blockToOpenXML' opts (Table caption aligns widths headers rows) = do ( mknode "w:tblPr" [] ( mknode "w:tblStyle" [("w:val","Table")] () : mknode "w:tblW" [("w:type", "pct"), ("w:w", show rowwidth)] () : - mknode "w:tblLook" [("w:firstRow","1") | hasHeader ] () : + mknode "w:tblLook" [("w:firstRow",if hasHeader then "1" else "0") ] () : [ mknode "w:tblCaption" [("w:val", captionStr)] () | not (null caption) ] ) : mknode "w:tblGrid" [] -- cgit v1.2.3 From f6fde0ae5ea21d91d610c21c0488029f9c26ca3f Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Tue, 2 Jan 2018 07:50:08 -0500 Subject: Docx reader: Extract tracked changes type from parpart. We're going to want to use it elsewhere as well, in upcoming tracking of paragraph insertion/deletion. --- src/Text/Pandoc/Readers/Docx.hs | 4 ++-- src/Text/Pandoc/Readers/Docx/Parse.hs | 21 +++++++++++++++++---- 2 files changed, 19 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index a2f22c1ea..3ac3b7563 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -364,7 +364,7 @@ parPartToInlines parPart = parPartToInlines' :: PandocMonad m => ParPart -> DocxContext m Inlines parPartToInlines' (PlainRun r) = runToInlines r -parPartToInlines' (Insertion _ author date runs) = do +parPartToInlines' (ChangedRuns (TrackedChange Insertion (ChangeInfo _ author date)) runs) = do opts <- asks docxOptions case readerTrackChanges opts of AcceptChanges -> smushInlines <$> mapM runToInlines runs @@ -373,7 +373,7 @@ parPartToInlines' (Insertion _ author date runs) = do ils <- smushInlines <$> mapM runToInlines runs let attr = ("", ["insertion"], [("author", author), ("date", date)]) return $ spanWith attr ils -parPartToInlines' (Deletion _ author date runs) = do +parPartToInlines' (ChangedRuns (TrackedChange Deletion (ChangeInfo _ author date)) runs) = do opts <- asks docxOptions case readerTrackChanges opts of AcceptChanges -> return mempty diff --git a/src/Text/Pandoc/Readers/Docx/Parse.hs b/src/Text/Pandoc/Readers/Docx/Parse.hs index 071f901b6..71ab5085b 100644 --- a/src/Text/Pandoc/Readers/Docx/Parse.hs +++ b/src/Text/Pandoc/Readers/Docx/Parse.hs @@ -51,6 +51,9 @@ module Text.Pandoc.Readers.Docx.Parse ( Docx(..) , ParagraphStyle(..) , Row(..) , Cell(..) + , TrackedChange(..) + , ChangeType(..) + , ChangeInfo(..) , archiveToDocx , archiveToDocxWithWarnings ) where @@ -198,6 +201,15 @@ data ParIndentation = ParIndentation { leftParIndent :: Maybe Integer , hangingParIndent :: Maybe Integer} deriving Show +data ChangeType = Insertion | Deletion + deriving Show + +data ChangeInfo = ChangeInfo ChangeId Author ChangeDate + deriving Show + +data TrackedChange = TrackedChange ChangeType ChangeInfo + deriving Show + data ParagraphStyle = ParagraphStyle { pStyle :: [String] , indentation :: Maybe ParIndentation , dropCap :: Bool @@ -241,8 +253,7 @@ data Cell = Cell [BodyPart] type Extent = Maybe (Double, Double) data ParPart = PlainRun Run - | Insertion ChangeId Author ChangeDate [Run] - | Deletion ChangeId Author ChangeDate [Run] + | ChangedRuns TrackedChange [Run] | CommentStart CommentId Author CommentDate [BodyPart] | CommentEnd CommentId | BookMark BookMarkId Anchor @@ -732,14 +743,16 @@ elemToParPart ns element , Just cAuthor <- findAttrByName ns "w" "author" element , Just cDate <- findAttrByName ns "w" "date" element = do runs <- mapD (elemToRun ns) (elChildren element) - return $ Insertion cId cAuthor cDate runs + return $ ChangedRuns + (TrackedChange Insertion (ChangeInfo cId cAuthor cDate)) runs elemToParPart ns element | isElem ns "w" "del" element || isElem ns "w" "moveFrom" element , Just cId <- findAttrByName ns "w" "id" element , Just cAuthor <- findAttrByName ns "w" "author" element , Just cDate <- findAttrByName ns "w" "date" element = do runs <- mapD (elemToRun ns) (elChildren element) - return $ Deletion cId cAuthor cDate runs + return $ ChangedRuns + (TrackedChange Deletion (ChangeInfo cId cAuthor cDate)) runs elemToParPart ns element | isElem ns "w" "smartTag" element = do runs <- mapD (elemToRun ns) (elChildren element) -- cgit v1.2.3 From 2746f73093dcf126977ae3cfa89dda1297d0dcec Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Tue, 2 Jan 2018 08:09:33 -0500 Subject: Docx reader: Parse track changes info into paragraph props. This will tell us whether a paragraph break was inserted or deleted. We add a generalized track-changes parsing function, and use it in `elemToParPart` as well. --- src/Text/Pandoc/Readers/Docx/Parse.hs | 42 ++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 15 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx/Parse.hs b/src/Text/Pandoc/Readers/Docx/Parse.hs index 71ab5085b..1fcbdf386 100644 --- a/src/Text/Pandoc/Readers/Docx/Parse.hs +++ b/src/Text/Pandoc/Readers/Docx/Parse.hs @@ -216,6 +216,7 @@ data ParagraphStyle = ParagraphStyle { pStyle :: [String] , pHeading :: Maybe (String, Int) , pNumInfo :: Maybe (String, String) , pBlockQuote :: Maybe Bool + , pChange :: Maybe TrackedChange } deriving Show @@ -226,6 +227,7 @@ defaultParagraphStyle = ParagraphStyle { pStyle = [] , pHeading = Nothing , pNumInfo = Nothing , pBlockQuote = Nothing + , pChange = Nothing } @@ -738,21 +740,9 @@ elemToParPart ns element | isElem ns "w" "r" element = elemToRun ns element >>= (\r -> return $ PlainRun r) elemToParPart ns element - | isElem ns "w" "ins" element || isElem ns "w" "moveTo" element - , Just cId <- findAttrByName ns "w" "id" element - , Just cAuthor <- findAttrByName ns "w" "author" element - , Just cDate <- findAttrByName ns "w" "date" element = do - runs <- mapD (elemToRun ns) (elChildren element) - return $ ChangedRuns - (TrackedChange Insertion (ChangeInfo cId cAuthor cDate)) runs -elemToParPart ns element - | isElem ns "w" "del" element || isElem ns "w" "moveFrom" element - , Just cId <- findAttrByName ns "w" "id" element - , Just cAuthor <- findAttrByName ns "w" "author" element - , Just cDate <- findAttrByName ns "w" "date" element = do - runs <- mapD (elemToRun ns) (elChildren element) - return $ ChangedRuns - (TrackedChange Deletion (ChangeInfo cId cAuthor cDate)) runs + | Just change <- getTrackedChange ns element = do + runs <- mapD (elemToRun ns) (elChildren element) + return $ ChangedRuns change runs elemToParPart ns element | isElem ns "w" "smartTag" element = do runs <- mapD (elemToRun ns) (elChildren element) @@ -903,6 +893,21 @@ getParStyleField field stylemap styles = Just y getParStyleField _ _ _ = Nothing +getTrackedChange :: NameSpaces -> Element -> Maybe TrackedChange +getTrackedChange ns element + | isElem ns "w" "ins" element || isElem ns "w" "moveTo" element + , Just cId <- findAttrByName ns "w" "id" element + , Just cAuthor <- findAttrByName ns "w" "author" element + , Just cDate <- findAttrByName ns "w" "date" element = + Just $ TrackedChange Insertion (ChangeInfo cId cAuthor cDate) +getTrackedChange ns element + | isElem ns "w" "del" element || isElem ns "w" "moveFrom" element + , Just cId <- findAttrByName ns "w" "id" element + , Just cAuthor <- findAttrByName ns "w" "author" element + , Just cDate <- findAttrByName ns "w" "date" element = + Just $ TrackedChange Deletion (ChangeInfo cId cAuthor cDate) +getTrackedChange _ _ = Nothing + elemToParagraphStyle :: NameSpaces -> Element -> ParStyleMap -> ParagraphStyle elemToParagraphStyle ns element sty | Just pPr <- findChildByName ns "w" "pPr" element = @@ -926,6 +931,13 @@ elemToParagraphStyle ns element sty , pHeading = getParStyleField headingLev sty style , pNumInfo = getParStyleField numInfo sty style , pBlockQuote = getParStyleField isBlockQuote sty style + , pChange = findChildByName ns "w" "rPr" pPr >>= + filterChild (\e -> isElem ns "w" "ins" e || + isElem ns "w" "moveTo" e || + isElem ns "w" "del" e || + isElem ns "w" "moveFrom" e + ) >>= + getTrackedChange ns } elemToParagraphStyle _ _ _ = defaultParagraphStyle -- cgit v1.2.3 From 2e90e2932cb0195be2e5955ce7c52616a2b7414a Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Tue, 2 Jan 2018 08:34:21 -0500 Subject: Docx reader: Allow for insertion/deletion of paragraphs. If the paragraph has a deleted or inserted paragraph break (depending on the track-changes setting) we hold onto it until the next paragraph. This takes care of accept and reject. For this we introduce a new state which holds the ils from the previous para if necessary. For `--track-changes=all`, we add an empty span with class `paragraph-insertion`/`paragraph-deletion` at the end of the paragraph prior to the break to be inserted or deleted. Closes #3927. --- src/Text/Pandoc/Readers/Docx.hs | 48 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index 3ac3b7563..e79c4a4fc 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -1,7 +1,7 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternGuards #-} - +{-# LANGUAGE MultiWayIf #-} {- Copyright (C) 2014-2017 Jesse Rosenthal <jrosenthal@jhu.edu> @@ -127,6 +127,7 @@ data DState = DState { docxAnchorMap :: M.Map String String -- keep track of (numId, lvl) values for -- restarting , docxListState :: M.Map (String, String) Integer + , docxPrevPara :: Inlines } instance Default DState where @@ -137,6 +138,7 @@ instance Default DState where , docxDropCap = mempty , docxWarnings = [] , docxListState = M.empty + , docxPrevPara = mempty } data DEnv = DEnv { docxOptions :: ReaderOptions @@ -562,16 +564,54 @@ bodyPartToBlocks (Paragraph pPr parparts) headerWith ("", delete style (pStyle pPr), []) n ils | otherwise = do ils <- (trimSps . smushInlines) <$> mapM parPartToInlines parparts + prevParaIls <- gets docxPrevPara dropIls <- gets docxDropCap let ils' = dropIls <> ils if dropCap pPr then do modify $ \s -> s { docxDropCap = ils' } return mempty else do modify $ \s -> s { docxDropCap = mempty } + let ils'' = prevParaIls <> + (if isNull prevParaIls then mempty else space) <> + ils' opts <- asks docxOptions - if isNull ils' && not (isEnabled Ext_empty_paragraphs opts) - then return mempty - else return $ parStyleToTransform pPr $ para ils' + if | isNull ils'' && not (isEnabled Ext_empty_paragraphs opts) -> + return mempty + | Just (TrackedChange Insertion _) <- pChange pPr + , AcceptChanges <- readerTrackChanges opts -> do + modify $ \s -> s {docxPrevPara = mempty} + return $ parStyleToTransform pPr $ para ils'' + | Just (TrackedChange Insertion _) <- pChange pPr + , RejectChanges <- readerTrackChanges opts -> do + modify $ \s -> s {docxPrevPara = ils''} + return mempty + | Just (TrackedChange Insertion cInfo) <- pChange pPr + , AllChanges <- readerTrackChanges opts + , ChangeInfo _ cAuthor cDate <- cInfo -> do + let attr = ("", ["paragraph-insertion"], [("author", cAuthor), ("date", cDate)]) + insertMark = spanWith attr mempty + return $ + parStyleToTransform pPr $ + para $ ils'' <> insertMark + | Just (TrackedChange Deletion _) <- pChange pPr + , AcceptChanges <- readerTrackChanges opts -> do + modify $ \s -> s {docxPrevPara = ils''} + return mempty + | Just (TrackedChange Deletion _) <- pChange pPr + , RejectChanges <- readerTrackChanges opts -> do + modify $ \s -> s {docxPrevPara = mempty} + return $ parStyleToTransform pPr $ para ils'' + | Just (TrackedChange Deletion cInfo) <- pChange pPr + , AllChanges <- readerTrackChanges opts + , ChangeInfo _ cAuthor cDate <- cInfo -> do + let attr = ("", ["paragraph-deletion"], [("author", cAuthor), ("date", cDate)]) + insertMark = spanWith attr mempty + return $ + parStyleToTransform pPr $ + para $ ils'' <> insertMark + | otherwise -> do + modify $ \s -> s {docxPrevPara = mempty} + return $ parStyleToTransform pPr $ para ils'' bodyPartToBlocks (ListItem pPr numId lvl (Just levelInfo) parparts) = do -- We check whether this current numId has previously been used, -- since Docx expects us to pick up where we left off. -- cgit v1.2.3 From a55a1e3a573d554677a85f2398e975ab71846997 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Tue, 2 Jan 2018 11:45:22 -0500 Subject: Docx reader: remove MultiWayIf Different formatting rules across 7.X and 8.X. Use empty case expression instead. --- src/Text/Pandoc/Readers/Docx.hs | 77 +++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 38 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index e79c4a4fc..f01a94550 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -1,7 +1,6 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternGuards #-} -{-# LANGUAGE MultiWayIf #-} {- Copyright (C) 2014-2017 Jesse Rosenthal <jrosenthal@jhu.edu> @@ -575,43 +574,45 @@ bodyPartToBlocks (Paragraph pPr parparts) (if isNull prevParaIls then mempty else space) <> ils' opts <- asks docxOptions - if | isNull ils'' && not (isEnabled Ext_empty_paragraphs opts) -> - return mempty - | Just (TrackedChange Insertion _) <- pChange pPr - , AcceptChanges <- readerTrackChanges opts -> do - modify $ \s -> s {docxPrevPara = mempty} - return $ parStyleToTransform pPr $ para ils'' - | Just (TrackedChange Insertion _) <- pChange pPr - , RejectChanges <- readerTrackChanges opts -> do - modify $ \s -> s {docxPrevPara = ils''} - return mempty - | Just (TrackedChange Insertion cInfo) <- pChange pPr - , AllChanges <- readerTrackChanges opts - , ChangeInfo _ cAuthor cDate <- cInfo -> do - let attr = ("", ["paragraph-insertion"], [("author", cAuthor), ("date", cDate)]) - insertMark = spanWith attr mempty - return $ - parStyleToTransform pPr $ - para $ ils'' <> insertMark - | Just (TrackedChange Deletion _) <- pChange pPr - , AcceptChanges <- readerTrackChanges opts -> do - modify $ \s -> s {docxPrevPara = ils''} - return mempty - | Just (TrackedChange Deletion _) <- pChange pPr - , RejectChanges <- readerTrackChanges opts -> do - modify $ \s -> s {docxPrevPara = mempty} - return $ parStyleToTransform pPr $ para ils'' - | Just (TrackedChange Deletion cInfo) <- pChange pPr - , AllChanges <- readerTrackChanges opts - , ChangeInfo _ cAuthor cDate <- cInfo -> do - let attr = ("", ["paragraph-deletion"], [("author", cAuthor), ("date", cDate)]) - insertMark = spanWith attr mempty - return $ - parStyleToTransform pPr $ - para $ ils'' <> insertMark - | otherwise -> do - modify $ \s -> s {docxPrevPara = mempty} - return $ parStyleToTransform pPr $ para ils'' + case () of + + _ | isNull ils'' && not (isEnabled Ext_empty_paragraphs opts) -> + return mempty + _ | Just (TrackedChange Insertion _) <- pChange pPr + , AcceptChanges <- readerTrackChanges opts -> do + modify $ \s -> s {docxPrevPara = mempty} + return $ parStyleToTransform pPr $ para ils'' + _ | Just (TrackedChange Insertion _) <- pChange pPr + , RejectChanges <- readerTrackChanges opts -> do + modify $ \s -> s {docxPrevPara = ils''} + return mempty + _ | Just (TrackedChange Insertion cInfo) <- pChange pPr + , AllChanges <- readerTrackChanges opts + , ChangeInfo _ cAuthor cDate <- cInfo -> do + let attr = ("", ["paragraph-insertion"], [("author", cAuthor), ("date", cDate)]) + insertMark = spanWith attr mempty + return $ + parStyleToTransform pPr $ + para $ ils'' <> insertMark + _ | Just (TrackedChange Deletion _) <- pChange pPr + , AcceptChanges <- readerTrackChanges opts -> do + modify $ \s -> s {docxPrevPara = ils''} + return mempty + _ | Just (TrackedChange Deletion _) <- pChange pPr + , RejectChanges <- readerTrackChanges opts -> do + modify $ \s -> s {docxPrevPara = mempty} + return $ parStyleToTransform pPr $ para ils'' + _ | Just (TrackedChange Deletion cInfo) <- pChange pPr + , AllChanges <- readerTrackChanges opts + , ChangeInfo _ cAuthor cDate <- cInfo -> do + let attr = ("", ["paragraph-deletion"], [("author", cAuthor), ("date", cDate)]) + insertMark = spanWith attr mempty + return $ + parStyleToTransform pPr $ + para $ ils'' <> insertMark + _ | otherwise -> do + modify $ \s -> s {docxPrevPara = mempty} + return $ parStyleToTransform pPr $ para ils'' bodyPartToBlocks (ListItem pPr numId lvl (Just levelInfo) parparts) = do -- We check whether this current numId has previously been used, -- since Docx expects us to pick up where we left off. -- cgit v1.2.3 From dcc6e6b777ff6f35331118346ee2ebe845740e7e Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Wed, 3 Jan 2018 10:18:43 -0500 Subject: Powerpoint writer: Implement two-column slides. This uses the columns/column div format described in the pandoc manual. At the moment, only two columns (half the screen each) are allowed. Custom widths are not supported. --- src/Text/Pandoc/Writers/Powerpoint.hs | 107 ++++++++++++++++++++++++++++++---- 1 file changed, 95 insertions(+), 12 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index ab3b2eabf..7aed2e43f 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -43,6 +43,7 @@ import Data.Time.Clock (UTCTime) import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds, posixSecondsToUTCTime) import System.FilePath.Posix (splitDirectories, splitExtension) import Text.XML.Light +import qualified Text.XML.Light.Cursor as XMLC import Text.Pandoc.Definition import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Class (PandocMonad) @@ -191,10 +192,14 @@ data Slide = MetadataSlide { metadataSlideTitle :: [ParaElem] , metadataSlideAuthors :: [[ParaElem]] , metadataSlideDate :: [ParaElem] } - | TitleSlide { titleSlideHeader :: [ParaElem]} - | ContentSlide { contentSlideHeader :: [ParaElem] + | TitleSlide { slideHeader :: [ParaElem]} + | ContentSlide { slideHeader :: [ParaElem] , contentSlideContent :: [Shape] } + | TwoColumnSlide { slideHeader :: [ParaElem] + , twoColumnSlideLeft :: [Shape] + , twoColumnSlideRight :: [Shape] + } deriving (Show, Eq) data SlideElement = SlideElement Pixels Pixels Pixels Pixels Shape @@ -553,6 +558,12 @@ splitBlocks' cur acc (tbl@(Table _ _ _ _ _) : blks) = do (Header n _ _) : [] | n == slideLevel -> splitBlocks' [] (acc ++ [cur ++ [tbl]]) blks _ -> splitBlocks' [] (acc ++ (if null cur then [] else [cur]) ++ [[tbl]]) blks +splitBlocks' cur acc (d@(Div (_, classes, _) _): blks) | "columns" `elem` classes = do + slideLevel <- asks envSlideLevel + case cur of + (Header n _ _) : [] | n == slideLevel -> + splitBlocks' [] (acc ++ [cur ++ [d]]) blks + _ -> splitBlocks' [] (acc ++ (if null cur then [] else [cur]) ++ [[d]]) blks splitBlocks' cur acc (blk : blks) = splitBlocks' (cur ++ [blk]) acc blks splitBlocks :: Monad m => [Block] -> P m [[Block]] @@ -562,25 +573,37 @@ blocksToSlide' :: PandocMonad m => Int -> [Block] -> P m Slide blocksToSlide' lvl ((Header n _ ils) : blks) | n < lvl = do hdr <- inlinesToParElems ils - return $ TitleSlide {titleSlideHeader = hdr} + return $ TitleSlide {slideHeader = hdr} | n == lvl = do hdr <- inlinesToParElems ils - inNoteSlide <- asks envInNoteSlide - shapes <- if inNoteSlide - then forceFontSize noteSize $ blocksToShapes blks - else blocksToShapes blks - return $ ContentSlide { contentSlideHeader = hdr - , contentSlideContent = shapes - } + -- Now get the slide without the header, and then add the header + -- in. + slide <- blocksToSlide' lvl blks + return $ slide {slideHeader = hdr} +blocksToSlide' _ (blk : blks) + | Div (_, classes, _) divBlks <- blk + , "columns" `elem` classes + , (Div (_, clsL, _) blksL) : (Div (_, clsR, _) blksR) : remaining <- divBlks + , "column" `elem` clsL, "column" `elem` clsR = do + unless (null blks) + (mapM (P.report . BlockNotRendered) blks >> return ()) + unless (null remaining) + (mapM (P.report . BlockNotRendered) remaining >> return ()) + shapesL <- blocksToShapes blksL + shapesR <- blocksToShapes blksR + return $ TwoColumnSlide { slideHeader = [] + , twoColumnSlideLeft = shapesL + , twoColumnSlideRight = shapesR + } blocksToSlide' _ (blk : blks) = do inNoteSlide <- asks envInNoteSlide shapes <- if inNoteSlide then forceFontSize noteSize $ blocksToShapes (blk : blks) else blocksToShapes (blk : blks) - return $ ContentSlide { contentSlideHeader = [] + return $ ContentSlide { slideHeader = [] , contentSlideContent = shapes } -blocksToSlide' _ [] = return $ ContentSlide { contentSlideHeader = [] +blocksToSlide' _ [] = return $ ContentSlide { slideHeader = [] , contentSlideContent = [] } @@ -776,6 +799,7 @@ getLayout slide = do (MetadataSlide _ _ _ _) -> "ppt/slideLayouts/slideLayout1.xml" (TitleSlide _) -> "ppt/slideLayouts/slideLayout3.xml" (ContentSlide _ _) -> "ppt/slideLayouts/slideLayout2.xml" + (TwoColumnSlide _ _ _) -> "ppt/slideLayouts/slideLayout4.xml" distArchive <- asks envDistArchive root <- case findEntryByPath layoutpath distArchive of Just e -> case parseXMLDoc $ UTF8.toStringLazy $ fromEntry e of @@ -1377,6 +1401,53 @@ contentToElement layout hdrShape shapes spTree contentToElement _ _ _ = return $ mknode "p:sp" [] () +setIdx'' :: NameSpaces -> String -> Content -> Content +setIdx'' _ idx (Elem element) = + let tag = XMLC.getTag element + attrs = XMLC.tagAttribs tag + idxKey = (QName "idx" Nothing Nothing) + attrs' = Attr idxKey idx : (filter (\a -> attrKey a /= idxKey) attrs) + tag' = tag {XMLC.tagAttribs = attrs'} + in Elem $ XMLC.setTag tag' element +setIdx'' _ _ c = c + +setIdx' :: NameSpaces -> String -> XMLC.Cursor -> XMLC.Cursor +setIdx' ns idx cur = + let modifiedCur = XMLC.modifyContent (setIdx'' ns idx) cur + in + case XMLC.nextDF modifiedCur of + Just cur' -> setIdx' ns idx cur' + Nothing -> XMLC.root modifiedCur + +setIdx :: NameSpaces -> String -> Element -> Element +setIdx ns idx element = + let cur = XMLC.fromContent (Elem element) + cur' = setIdx' ns idx cur + in + case XMLC.toTree cur' of + Elem element' -> element' + _ -> element + +twoColumnToElement :: PandocMonad m => Element -> [ParaElem] -> [Shape] -> [Shape] -> P m Element +twoColumnToElement layout hdrShape shapesL shapesR + | ns <- elemToNameSpaces layout + , Just cSld <- findChild (elemName ns "p" "cSld") layout + , Just spTree <- findChild (elemName ns "p" "spTree") cSld = do + element <- nonBodyTextToElement layout "Title 1" hdrShape + let hdrShapeElements = if null hdrShape + then [] + else [element] + contentElementsL <- shapesToElements layout shapesL + contentElementsR <- shapesToElements layout shapesR + let contentElementsL' = map (setIdx ns "1") contentElementsL + contentElementsR' = map (setIdx ns "2") contentElementsR + return $ + replaceNamedChildren ns "p" "sp" + (hdrShapeElements ++ contentElementsL' ++ contentElementsR') + spTree +twoColumnToElement _ _ _ _= return $ mknode "p:sp" [] () + + titleToElement :: PandocMonad m => Element -> [ParaElem] -> P m Element titleToElement layout titleElems | ns <- elemToNameSpaces layout @@ -1422,6 +1493,17 @@ slideToElement s@(ContentSlide hdrElems shapes) = do ("xmlns:r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"), ("xmlns:p", "http://schemas.openxmlformats.org/presentationml/2006/main") ] [mknode "p:cSld" [] [spTree]] +slideToElement s@(TwoColumnSlide hdrElems shapesL shapesR) = do + layout <- getLayout s + spTree <- local (\env -> if null hdrElems + then env + else env{envSlideHasHeader=True}) $ + twoColumnToElement layout hdrElems shapesL shapesR + return $ mknode "p:sld" + [ ("xmlns:a", "http://schemas.openxmlformats.org/drawingml/2006/main"), + ("xmlns:r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"), + ("xmlns:p", "http://schemas.openxmlformats.org/presentationml/2006/main") + ] [mknode "p:cSld" [] [spTree]] slideToElement s@(TitleSlide hdrElems) = do layout <- getLayout s spTree <- titleToElement layout hdrElems @@ -1574,6 +1656,7 @@ slideToSlideRelElement slide idNum = do (MetadataSlide _ _ _ _) -> "../slideLayouts/slideLayout1.xml" (TitleSlide _) -> "../slideLayouts/slideLayout3.xml" (ContentSlide _ _) -> "../slideLayouts/slideLayout2.xml" + (TwoColumnSlide _ _ _) -> "../slideLayouts/slideLayout4.xml" linkIds <- gets stLinkIds mediaIds <- gets stMediaIds -- cgit v1.2.3 From 576ed3f41625c0b245ecbfdc47bf6d1878785365 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Wed, 3 Jan 2018 11:00:49 -0500 Subject: Powerpoint writer: Appease compiler --- src/Text/Pandoc/Writers/Powerpoint.hs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index 7aed2e43f..c43910741 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -192,11 +192,11 @@ data Slide = MetadataSlide { metadataSlideTitle :: [ParaElem] , metadataSlideAuthors :: [[ParaElem]] , metadataSlideDate :: [ParaElem] } - | TitleSlide { slideHeader :: [ParaElem]} - | ContentSlide { slideHeader :: [ParaElem] + | TitleSlide { titleSlideHeader :: [ParaElem]} + | ContentSlide { contentSlideHeader :: [ParaElem] , contentSlideContent :: [Shape] } - | TwoColumnSlide { slideHeader :: [ParaElem] + | TwoColumnSlide { twoColumnSlideHeader :: [ParaElem] , twoColumnSlideLeft :: [Shape] , twoColumnSlideRight :: [Shape] } @@ -573,13 +573,16 @@ blocksToSlide' :: PandocMonad m => Int -> [Block] -> P m Slide blocksToSlide' lvl ((Header n _ ils) : blks) | n < lvl = do hdr <- inlinesToParElems ils - return $ TitleSlide {slideHeader = hdr} + return $ TitleSlide {titleSlideHeader = hdr} | n == lvl = do hdr <- inlinesToParElems ils -- Now get the slide without the header, and then add the header -- in. slide <- blocksToSlide' lvl blks - return $ slide {slideHeader = hdr} + return $ case slide of + ContentSlide _ _ -> slide {contentSlideHeader = hdr} + TwoColumnSlide _ _ _ -> slide {twoColumnSlideHeader = hdr} + _ -> slide blocksToSlide' _ (blk : blks) | Div (_, classes, _) divBlks <- blk , "columns" `elem` classes @@ -591,7 +594,7 @@ blocksToSlide' _ (blk : blks) (mapM (P.report . BlockNotRendered) remaining >> return ()) shapesL <- blocksToShapes blksL shapesR <- blocksToShapes blksR - return $ TwoColumnSlide { slideHeader = [] + return $ TwoColumnSlide { twoColumnSlideHeader = [] , twoColumnSlideLeft = shapesL , twoColumnSlideRight = shapesR } @@ -600,10 +603,10 @@ blocksToSlide' _ (blk : blks) = do shapes <- if inNoteSlide then forceFontSize noteSize $ blocksToShapes (blk : blks) else blocksToShapes (blk : blks) - return $ ContentSlide { slideHeader = [] + return $ ContentSlide { contentSlideHeader = [] , contentSlideContent = shapes } -blocksToSlide' _ [] = return $ ContentSlide { slideHeader = [] +blocksToSlide' _ [] = return $ ContentSlide { contentSlideHeader = [] , contentSlideContent = [] } -- cgit v1.2.3 From 2f5cca85fa1842a3265aed949502f5811ead51a6 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Wed, 3 Jan 2018 11:16:41 -0500 Subject: Powerpoint writer: Fix compiler error (again) The record syntax in a pattern match seems to be confusing the 8.X compilers. Stop using it. --- src/Text/Pandoc/Writers/Powerpoint.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index c43910741..12967a196 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -580,9 +580,9 @@ blocksToSlide' lvl ((Header n _ ils) : blks) -- in. slide <- blocksToSlide' lvl blks return $ case slide of - ContentSlide _ _ -> slide {contentSlideHeader = hdr} - TwoColumnSlide _ _ _ -> slide {twoColumnSlideHeader = hdr} - _ -> slide + ContentSlide _ cont -> ContentSlide hdr cont + TwoColumnSlide _ contL contR -> TwoColumnSlide hdr contL contR + slide' -> slide' blocksToSlide' _ (blk : blks) | Div (_, classes, _) divBlks <- blk , "columns" `elem` classes -- cgit v1.2.3 From cd00225219f2d71d74a61d99df4906dfb92e3797 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Wed, 3 Jan 2018 10:55:23 -0500 Subject: PowerPoint writer: Fix error with empty table cell. We require an empty "<a:p>" tag, even if the cell contains no paragraphs -- otherwise PowerPoint complains of corruption. --- src/Text/Pandoc/Writers/Powerpoint.hs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index 12967a196..d21e6b494 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -1313,12 +1313,16 @@ hardcodedTableMargin = 36 graphicToElement :: PandocMonad m => Graphic -> P m Element graphicToElement (Tbl tblPr colWidths hdrCells rows) = do - let cellToOpenXML paras = do elements <- mapM paragraphToElement paras - return $ - [mknode "a:txBody" [] $ - ([ mknode "a:bodyPr" [] () - , mknode "a:lstStyle" [] ()] - ++ elements)] + let cellToOpenXML paras = + do elements <- mapM paragraphToElement paras + let elements' = if null elements + then [mknode "a:p" [] [mknode "a:endParaRPr" [] ()]] + else elements + return $ + [mknode "a:txBody" [] $ + ([ mknode "a:bodyPr" [] () + , mknode "a:lstStyle" [] ()] + ++ elements')] headers' <- mapM cellToOpenXML hdrCells rows' <- mapM (mapM cellToOpenXML) rows let borderProps = mknode "a:tcPr" [] () -- cgit v1.2.3 From 143ec05bd9c34e5e018e9068b8277e2fc1970a57 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Wed, 3 Jan 2018 12:58:38 -0500 Subject: Powerpoint writer: Allow linked images. The following markdown: [![Image Title](image.jpg)](http://www.example.com) will now produce a linked image in the resulting PowerPoint file. --- src/Text/Pandoc/Writers/Powerpoint.hs | 43 ++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 11 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index d21e6b494..23313fbea 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -205,7 +205,7 @@ data Slide = MetadataSlide { metadataSlideTitle :: [ParaElem] data SlideElement = SlideElement Pixels Pixels Pixels Pixels Shape deriving (Show, Eq) -data Shape = Pic FilePath Text.Pandoc.Definition.Attr [ParaElem] +data Shape = Pic PicProps FilePath Text.Pandoc.Definition.Attr [ParaElem] | GraphicFrame [Graphic] [ParaElem] | TextBox [Paragraph] deriving (Show, Eq) @@ -327,6 +327,13 @@ instance Default RunProps where , rPropForceSize = Nothing } +data PicProps = PicProps { picPropLink :: Maybe (URL, String) + } deriving (Show, Eq) + +instance Default PicProps where + def = PicProps { picPropLink = Nothing + } + -------------------------------------------------- inlinesToParElems :: Monad m => [Inline] -> P m [ParaElem] @@ -489,9 +496,15 @@ rowToParagraphs algns tblCells = do blockToShape :: PandocMonad m => Block -> P m Shape blockToShape (Plain (il:_)) | Image attr ils (url, _) <- il = - Pic url attr <$> (inlinesToParElems ils) + Pic def url attr <$> (inlinesToParElems ils) blockToShape (Para (il:_)) | Image attr ils (url, _) <- il = - Pic url attr <$> (inlinesToParElems ils) + Pic def url attr <$> (inlinesToParElems ils) +blockToShape (Plain (il:_)) | Link _ (il':_) target <- il + , Image attr ils (url, _) <- il' = + Pic def{picPropLink = Just target} url attr <$> (inlinesToParElems ils) +blockToShape (Para (il:_)) | Link _ (il':_) target <- il + , Image attr ils (url, _) <- il' = + Pic def{picPropLink = Just target} url attr <$> (inlinesToParElems ils) blockToShape (Table caption algn _ hdrCells rows) = do caption' <- inlinesToParElems caption pageWidth <- presSizeWidth <$> asks envPresentationSize @@ -781,7 +794,7 @@ presentationToArchive p@(Presentation _ slides) = do combineShapes :: [Shape] -> [Shape] combineShapes [] = [] combineShapes (s : []) = [s] -combineShapes (pic@(Pic _ _ _) : ss) = pic : combineShapes ss +combineShapes (pic@(Pic _ _ _ _) : ss) = pic : combineShapes ss combineShapes ((TextBox []) : ss) = combineShapes ss combineShapes (s : TextBox [] : ss) = combineShapes (s : ss) combineShapes (s@(TextBox (p:ps)) : s'@(TextBox (p':ps')) : ss) @@ -1087,10 +1100,11 @@ createCaption paraElements = do -- Largely lifted from inlineToOpenXML' in T.P.W.Docx. Can't be easily -- abstracted because of some different namespaces and monads. TODO. makePicElement :: PandocMonad m - => MediaInfo + => PicProps + -> MediaInfo -> Text.Pandoc.Definition.Attr -> P m Element -makePicElement mInfo attr = do +makePicElement picProps mInfo attr = do opts <- asks envOpts pageWidth <- presSizeWidth <$> asks envPresentationSize pageHeight <- getPageHeight <$> asks envPresentationSize @@ -1119,9 +1133,16 @@ makePicElement mInfo attr = do let cNvPicPr = mknode "p:cNvPicPr" [] $ mknode "a:picLocks" [("noGrp","1") ,("noChangeAspect","1")] () + -- cNvPr will contain the link information so we do that separately, + -- and register the link if necessary. + let cNvPrAttr = [("descr", mInfoFilePath mInfo), ("id","0"),("name","Picture 1")] + cNvPr <- case picPropLink picProps of + Just link -> do idNum <- registerLink link + return $ mknode "p:cNvPr" cNvPrAttr $ + mknode "a:hlinkClick" [("r:id", "rId" ++ show idNum)] () + Nothing -> return $ mknode "p:cNvPr" cNvPrAttr () let nvPicPr = mknode "p:nvPicPr" [] - [ mknode "p:cNvPr" - [("descr", mInfoFilePath mInfo),("id","0"),("name","Picture 1")] () + [ cNvPr , cNvPicPr , mknode "p:nvPr" [] ()] let blipFill = mknode "p:blipFill" [] @@ -1267,10 +1288,10 @@ shapeToElement layout (TextBox paras) -- XXX: TODO | otherwise = return $ mknode "p:sp" [] () -- XXX: TODO -shapeToElement layout (Pic fp attr alt) = do +shapeToElement layout (Pic picProps fp attr alt) = do mInfo <- registerMedia fp alt case mInfoExt mInfo of - Just _ -> makePicElement mInfo attr + Just _ -> makePicElement picProps mInfo attr Nothing -> shapeToElement layout $ TextBox [Paragraph def alt] shapeToElement _ (GraphicFrame tbls _) = do elements <- mapM graphicToElement tbls @@ -1291,7 +1312,7 @@ shapeToElement _ (GraphicFrame tbls _) = do shapeToElements :: PandocMonad m => Element -> Shape -> P m [Element] shapeToElements layout shp = do case shp of - (Pic _ _ alt) | (not . null) alt -> do + (Pic _ _ _ alt) | (not . null) alt -> do element <- shapeToElement layout shp caption <- createCaption alt return [element, caption] -- cgit v1.2.3 From 1ce736c2dfdb6c3ecbadc5b7d28a091e86d172ec Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Wed, 3 Jan 2018 13:36:12 -0500 Subject: Powerpoint writer: code cleanup Remove commented-out functions and imports. --- src/Text/Pandoc/Writers/Powerpoint.hs | 103 ---------------------------------- 1 file changed, 103 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index 23313fbea..bf80b8afb 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -37,7 +37,6 @@ import Control.Monad.Reader import Control.Monad.State import Codec.Archive.Zip import Data.List (intercalate, stripPrefix, isPrefixOf, nub) --- import Control.Monad (mplus) import Data.Default import Data.Time.Clock (UTCTime) import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds, posixSecondsToUTCTime) @@ -53,8 +52,6 @@ import Text.Pandoc.Options import Text.Pandoc.MIME import Text.Pandoc.Logging import qualified Data.ByteString.Lazy as BL --- import qualified Data.ByteString.Lazy.Char8 as BL8 --- import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Walk import Text.Pandoc.Writers.Shared (fixDisplayMath) import Text.Pandoc.Writers.OOXML @@ -229,20 +226,6 @@ data Paragraph = Paragraph { paraProps :: ParaProps data HeaderType = TitleHeader | SlideHeader | InternalHeader Int deriving (Show, Eq) --- type StartingAt = Int - --- data AutoNumType = ArabicNum --- | AlphaUpperNum --- | AlphaLowerNum --- | RomanUpperNum --- | RomanLowerNum --- deriving (Show, Eq) - --- data AutoNumDelim = PeriodDelim --- | OneParenDelim --- | TwoParensDelim --- deriving (Show, Eq) - autoNumberingToType :: ListAttributes -> String autoNumberingToType (_, numStyle, numDelim) = typeString ++ delimString @@ -420,10 +403,6 @@ blockToParagraphs (BlockQuote blks) = concatMapM blockToParagraphs blks -- TODO: work out the format blockToParagraphs (RawBlock _ _) = return [] - -- parElems <- inlinesToParElems [Str str] - -- paraProps <- asks envParaProps - -- return [Paragraph paraProps parElems] --- TODO: work out the format blockToParagraphs (Header n _ ils) = do slideLevel <- asks envSlideLevel parElems <- inlinesToParElems ils @@ -462,7 +441,6 @@ blockToParagraphs (DefinitionList entries) = do return $ term ++ definition concatMapM go entries blockToParagraphs (Div _ blks) = concatMapM blockToParagraphs blks --- TODO blockToParagraphs blk = do P.report $ BlockNotRendered blk return [] @@ -827,12 +805,6 @@ getLayout slide = do PandocSomeError $ layoutpath ++ " missing in reference file" return root - -- let ns = elemToNameSpaces root - -- case findChild (elemName ns "p" "cSld") root of - -- Just element' -> return element' - -- Nothing -> throwError $ - -- PandocSomeError $ - -- layoutpath ++ " not correctly formed layout file" shapeHasName :: NameSpaces -> String -> Element -> Bool shapeHasName ns name element @@ -842,55 +814,12 @@ shapeHasName ns name element nm == name | otherwise = False --- getContentTitleShape :: NameSpaces -> Element -> Maybe Element --- getContentTitleShape ns spTreeElem --- | isElem ns "p" "spTree" spTreeElem = --- filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasName ns "Title 1" e)) spTreeElem --- | otherwise = Nothing - --- getSubtitleShape :: NameSpaces -> Element -> Maybe Element --- getSubtitleShape ns spTreeElem --- | isElem ns "p" "spTree" spTreeElem = --- filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasName ns "Subtitle 2" e)) spTreeElem --- | otherwise = Nothing - --- getDateShape :: NameSpaces -> Element -> Maybe Element --- getDateShape ns spTreeElem --- | isElem ns "p" "spTree" spTreeElem = --- filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasName ns "Date Placeholder 3" e)) spTreeElem --- | otherwise = Nothing - getContentShape :: NameSpaces -> Element -> Maybe Element getContentShape ns spTreeElem | isElem ns "p" "spTree" spTreeElem = filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasName ns "Content Placeholder 2" e)) spTreeElem | otherwise = Nothing - --- cursorHasName :: QName -> XMLC.Cursor -> Bool --- cursorHasName nm cur = case XMLC.current cur of --- Elem element -> case XMLC.tagName $ XMLC.getTag element of --- nm -> True --- _ -> False --- _ -> False - --- fillInTxBody :: NameSpaces -> [Paragraph] -> Element -> Element --- fillInTxBody ns paras txBodyElem --- | isElem ns "p" "txBody" txBodyElem = --- replaceNamedChildren ns "a" "p" (map paragraphToElement paras) txBodyElem --- | otherwise = txBodyElem - --- fillInShape :: NameSpaces -> Shape -> Element -> Element --- fillInShape ns shape spElem --- | TextBox paras <- shape --- , isElemn ns "p" "sp" spElem = --- replaceNamedChildren ns "p" "txBody" (fillInTxBody ns paras sp - - --- fillInShape :: NameSpaces -> Element -> Shape -> Element --- fillInShape ns spElem (TextBox paras) = fillInParagraphs ns spElem paras --- fillInShape _ spElem pic = spElem - contentIsElem :: NameSpaces -> String -> String -> Content -> Bool contentIsElem ns prefix name (Elem element) = isElem ns prefix name element contentIsElem _ _ _ _ = False @@ -902,7 +831,6 @@ replaceNamedChildren ns prefix name newKids element = in element{elContent = content' ++ map Elem newKids} - ---------------------------------------------------------------- registerLink :: PandocMonad m => (URL, String) -> P m Int @@ -1129,7 +1057,6 @@ makePicElement picProps mInfo attr = do xoff' = if hasHeader then xoff + hXoff else xoff xoff'' = if hasCaption then xoff' + capX else xoff' yoff' = if hasHeader then hYoff + hYext else yoff - -- let (xemu,yemu)=((floor $ xpt * 12700), (floor $ ypt * 12700)) let cNvPicPr = mknode "p:cNvPicPr" [] $ mknode "a:picLocks" [("noGrp","1") ,("noChangeAspect","1")] () @@ -1353,9 +1280,6 @@ graphicToElement (Tbl tblPr colWidths hdrCells rows) = do then emptyCell else contents) ++ [ borderProps | border ] let mkrow border cells = mknode "a:tr" [("h", "0")] $ map (mkcell border) cells - -- let textwidth = 14400 -- 5.5 in in twips, 1/20 pt - -- let fullrow = 14400 -- 100% specified in pct - -- let rowwidth = fullrow * sum colWidths let mkgridcol w = mknode "a:gridCol" [("w", show ((12700 * w) :: Integer))] () @@ -1395,24 +1319,6 @@ nonBodyTextToElement layout shapeName paraElements -- XXX: TODO | otherwise = return $ mknode "p:sp" [] () - --- hdrToElement :: Element -> [ParaElem] -> Element --- hdrToElement layout paraElems --- | ns <- elemToNameSpaces layout --- , Just cSld <- findChild (elemName ns "p" "cSld") layout --- , Just spTree <- findChild (elemName ns "p" "spTree") cSld --- , Just sp <- getContentTitleShape ns spTree = --- let hdrPara = Paragraph def paraElems --- txBody = mknode "p:txBody" [] $ --- [mknode "a:bodyPr" [] (), mknode "a:lstStyle" [] ()] ++ --- [paragraphToElement hdrPara] --- in --- replaceNamedChildren ns "p" "txBody" [txBody] sp --- -- XXX: TODO --- | otherwise = mknode "p:sp" [] () --- -- XXX: TODO --- hdrToElement _ _ = mknode "p:sp" [] () - contentToElement :: PandocMonad m => Element -> [ParaElem] -> [Shape] -> P m Element contentToElement layout hdrShape shapes | ns <- elemToNameSpaces layout @@ -1704,11 +1610,6 @@ slideToSlideRelElement slide idNum = do , ("Target", target)] () ] ++ linkRels ++ mediaRels) --- slideToSlideRelEntry :: PandocMonad m => Slide -> Int -> P m Entry --- slideToSlideRelEntry slide idNum = do --- let fp = "ppt/slides/_rels/slide" ++ (show idNum) ++ ".xml.rels" --- elemToEntry fp $ slideToSlideRelElement slide - slideToSldIdElement :: PandocMonad m => Slide -> Int -> P m Element slideToSldIdElement slide idNum = do let id' = show $ idNum + 255 @@ -1819,10 +1720,6 @@ presentationToContentTypes (Presentation _ slides) = do (defaults ++ mediaDefaults) (inheritedOverrides ++ presOverride ++ slideOverrides) --- slideToElement :: Element -> Slide -> Element --- slideToElement layout (ContentSlide _ shapes) = --- let sps = map (shapeToElement layout) shapes - presML :: String presML = "application/vnd.openxmlformats-officedocument.presentationml" -- cgit v1.2.3 From 13990c05017baa473f77f1c827d902b40f42b6a4 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Wed, 3 Jan 2018 14:29:09 -0500 Subject: Powerpoint writer: simplify replaceNamedChildren function A lot of work in the powerpoint writer is replacing XML from within slidelayouts from templates. This function does a good deal of that work, and this makes it preserve element order, as well as making it a bit easier to understand. --- src/Text/Pandoc/Writers/Powerpoint.hs | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index bf80b8afb..50b48fd87 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -820,16 +820,24 @@ getContentShape ns spTreeElem filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasName ns "Content Placeholder 2" e)) spTreeElem | otherwise = Nothing -contentIsElem :: NameSpaces -> String -> String -> Content -> Bool -contentIsElem ns prefix name (Elem element) = isElem ns prefix name element -contentIsElem _ _ _ _ = False - -replaceNamedChildren :: NameSpaces -> String -> String -> [Element] -> Element -> Element +replaceChildren :: (Element -> [Element]) -> Element -> Element +replaceChildren fun element = + element{elContent = concatMap fun' $ elContent element} + where fun' :: Content -> [Content] + fun' (Elem e) = map Elem $ fun e + fun' content = [content] + +replaceNamedChildren :: NameSpaces + -> String + -> String + -> [Element] + -> Element + -> Element replaceNamedChildren ns prefix name newKids element = - let content = elContent element - content' = filter (\c -> not (contentIsElem ns prefix name c)) content - in - element{elContent = content' ++ map Elem newKids} + let fun :: Element -> [Element] + fun e | isElem ns prefix name e = newKids + | otherwise = [e] + in replaceChildren fun element ---------------------------------------------------------------- -- cgit v1.2.3 From 6aae43998082280a356e2dda3df77c021abed58f Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Wed, 3 Jan 2018 15:33:18 -0500 Subject: PowerPoint writer: make inline code inherit code size. Previously (a) the code size wasn't set when we force size, and (b) the properties was set from the default, instead of inheriting. Both of those problems were fixed. --- src/Text/Pandoc/Writers/Powerpoint.hs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index 50b48fd87..32b7e2ec6 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -351,7 +351,7 @@ inlineToParElems (Link _ ils (url, title)) = do local (\r ->r{envRunProps = (envRunProps r){rLink = Just (url, title)}}) $ inlinesToParElems ils inlineToParElems (Code _ str) = do - local (\r ->r{envRunProps = def{rPropCode = True}}) $ + local (\r ->r{envRunProps = (envRunProps r){rPropCode = True}}) $ inlineToParElems $ Str str inlineToParElems (Math mathtype str) = return [MathElem mathtype (TeXString str)] @@ -1112,13 +1112,13 @@ noteSize = 18 paraElemToElement :: PandocMonad m => ParaElem -> P m Element paraElemToElement Break = return $ mknode "a:br" [] () paraElemToElement (Run rpr s) = do - let attrs = + let sizeAttrs = case rPropForceSize rpr of + Just n -> [("sz", (show $ n * 100))] + Nothing -> [] + attrs = sizeAttrs ++ if rPropCode rpr then [] - else (case rPropForceSize rpr of - Just n -> [("sz", (show $ n * 100))] - Nothing -> []) ++ - (if rPropBold rpr then [("b", "1")] else []) ++ + else (if rPropBold rpr then [("b", "1")] else []) ++ (if rPropItalics rpr then [("i", "1")] else []) ++ (case rStrikethrough rpr of Just NoStrike -> [("strike", "noStrike")] -- cgit v1.2.3 From 02d85469abcb06f10b22ef461e2152c84f12394b Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Wed, 3 Jan 2018 16:55:33 -0500 Subject: Powerpoint writer: Fix new replaceNamedChildren Previous version replaced *each* element from the template with the new elements -- leading to multiple overlapping frames. This only replaces the first instance, and throws out the rest. --- src/Text/Pandoc/Writers/Powerpoint.hs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index 32b7e2ec6..13542e78f 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -820,13 +820,6 @@ getContentShape ns spTreeElem filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasName ns "Content Placeholder 2" e)) spTreeElem | otherwise = Nothing -replaceChildren :: (Element -> [Element]) -> Element -> Element -replaceChildren fun element = - element{elContent = concatMap fun' $ elContent element} - where fun' :: Content -> [Content] - fun' (Elem e) = map Elem $ fun e - fun' content = [content] - replaceNamedChildren :: NameSpaces -> String -> String @@ -834,10 +827,15 @@ replaceNamedChildren :: NameSpaces -> Element -> Element replaceNamedChildren ns prefix name newKids element = - let fun :: Element -> [Element] - fun e | isElem ns prefix name e = newKids - | otherwise = [e] - in replaceChildren fun element + element { elContent = concat $ fun True $ elContent element } + where + fun :: Bool -> [Content] -> [[Content]] + fun _ [] = [] + fun switch ((Elem e) : conts) | isElem ns prefix name e = + if switch + then (map Elem $ newKids) : fun False conts + else fun False conts + fun switch (cont : conts) = [cont] : fun switch conts ---------------------------------------------------------------- -- cgit v1.2.3 From 101aece6cc03eb8dc434f2ff23bd4d66198fb592 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Wed, 3 Jan 2018 17:51:29 -0500 Subject: Powerpoint writer: combine adjacent runs. This will make the xml easier to read for debugging purposes. It should also make links behave more consistently across numerous words. --- src/Text/Pandoc/Writers/Powerpoint.hs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index 13542e78f..cac3a0af9 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -56,7 +56,7 @@ import Text.Pandoc.Walk import Text.Pandoc.Writers.Shared (fixDisplayMath) import Text.Pandoc.Writers.OOXML import qualified Data.Map as M -import Data.Maybe (mapMaybe, listToMaybe) +import Data.Maybe (mapMaybe, listToMaybe, maybeToList) import Text.Pandoc.ImageSize import Control.Applicative ((<|>)) @@ -1200,7 +1200,7 @@ paragraphToElement par = do [mknode "a:buAutoNum" [("type", autoNumberingToType attrs')] ()] Nothing -> [mknode "a:buNone" [] ()] ) - paras <- mapM paraElemToElement (paraElems par) + paras <- mapM paraElemToElement (combineParaElems $ paraElems par) return $ mknode "a:p" [] $ [mknode "a:pPr" attrs props] ++ paras shapeToElement :: PandocMonad m => Element -> Shape -> P m Element @@ -1758,3 +1758,20 @@ getContentType fp | "ppt" : "slideLayouts" : _ : [] <- splitDirectories fp= Just $ presML ++ ".slideLayout+xml" | otherwise = Nothing + +------------------------------------------------------- + +combineParaElems' :: Maybe ParaElem -> [ParaElem] -> [ParaElem] +combineParaElems' mbPElem [] = maybeToList mbPElem +combineParaElems' Nothing (pElem : pElems) = + combineParaElems' (Just pElem) pElems +combineParaElems' (Just pElem') (pElem : pElems) + | Run rPr' s' <- pElem' + , Run rPr s <- pElem + , rPr == rPr' = + combineParaElems' (Just $ Run rPr' $ s' ++ s) pElems + | otherwise = + pElem' : combineParaElems' (Just pElem) pElems + +combineParaElems :: [ParaElem] -> [ParaElem] +combineParaElems = combineParaElems' Nothing -- cgit v1.2.3 From 41dc65b37f19fefb5036d40d631a9d29561dc422 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Wed, 3 Jan 2018 20:52:48 -0500 Subject: Powerpoint writer: Split blocks correctly for linked images We treat links with an image as the first inline as an image with a link picProp -- so we have to split on it the same as if it were an image. --- src/Text/Pandoc/Writers/Powerpoint.hs | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index cac3a0af9..073cd72a1 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -513,6 +513,11 @@ blockToShape blk = TextBox <$> blockToParagraphs blk blocksToShapes :: PandocMonad m => [Block] -> P m [Shape] blocksToShapes blks = combineShapes <$> mapM blockToShape blks +isImage :: Inline -> Bool +isImage (Image _ _ _) = True +isImage (Link _ ((Image _ _ _) : _) _) = True +isImage _ = False + splitBlocks' :: Monad m => [Block] -> [[Block]] -> [Block] -> P m [[Block]] splitBlocks' cur acc [] = return $ acc ++ (if null cur then [] else [cur]) splitBlocks' cur acc (HorizontalRule : blks) = @@ -523,26 +528,26 @@ splitBlocks' cur acc (h@(Header n _ _) : blks) = do LT -> splitBlocks' [] (acc ++ (if null cur then [] else [cur]) ++ [[h]]) blks EQ -> splitBlocks' [h] (acc ++ (if null cur then [] else [cur])) blks GT -> splitBlocks' (cur ++ [h]) acc blks -splitBlocks' cur acc ((Para (img@(Image _ _ _):ils)) : blks) = do +splitBlocks' cur acc ((Para (il:ils)) : blks) | isImage il = do slideLevel <- asks envSlideLevel case cur of (Header n _ _) : [] | n == slideLevel -> splitBlocks' [] - (acc ++ [cur ++ [Para [img]]]) + (acc ++ [cur ++ [Para [il]]]) (if null ils then blks else (Para ils) : blks) - _ -> splitBlocks' [] - (acc ++ (if null cur then [] else [cur]) ++ [[Para [img]]]) - (if null ils then blks else (Para ils) : blks) -splitBlocks' cur acc ((Plain (img@(Image _ _ _):ils)) : blks) = do + _ -> splitBlocks' [] + (acc ++ (if null cur then [] else [cur]) ++ [[Para [il]]]) + (if null ils then blks else (Para ils) : blks) +splitBlocks' cur acc ((Plain (il:ils)) : blks) | isImage il = do slideLevel <- asks envSlideLevel case cur of (Header n _ _) : [] | n == slideLevel -> splitBlocks' [] - (acc ++ [cur ++ [Para [img]]]) + (acc ++ [cur ++ [Plain [il]]]) (if null ils then blks else (Plain ils) : blks) - _ -> splitBlocks' [] - (acc ++ (if null cur then [] else [cur]) ++ [[Para [img]]]) - (if null ils then blks else (Plain ils) : blks) + _ -> splitBlocks' [] + (acc ++ (if null cur then [] else [cur]) ++ [[Plain [il]]]) + (if null ils then blks else (Plain ils) : blks) splitBlocks' cur acc (tbl@(Table _ _ _ _ _) : blks) = do slideLevel <- asks envSlideLevel case cur of -- cgit v1.2.3 From bf15258d3b18bd2a7daae803b52a9b851eb204eb Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Wed, 3 Jan 2018 21:58:39 -0500 Subject: Powerpoint writer: Set default slidelevel correctly. We had previously defaulted to slideLevel 2. Now we use the correct behavior of defaulting to the highest level header followed by content. We change an expected test result to match this behavior. --- src/Text/Pandoc/Writers/Powerpoint.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index 073cd72a1..e5c29b75a 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -47,6 +47,7 @@ import Text.Pandoc.Definition import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Error (PandocError(..)) +import Text.Pandoc.Slides (getSlideLevel) import qualified Text.Pandoc.Class as P import Text.Pandoc.Options import Text.Pandoc.MIME @@ -86,7 +87,7 @@ writePowerpoint opts (Pandoc meta blks) = do , envOpts = opts , envSlideLevel = case writerSlideLevel opts of Just n -> n - Nothing -> 2 + Nothing -> getSlideLevel blks' } runP env def $ do pres <- blocksToPresentation blks' archv <- presentationToArchive pres -- cgit v1.2.3 From 85f8d9285063767e52266243d2b10af1cbe5f856 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Wed, 3 Jan 2018 22:07:07 -0500 Subject: Powerpoint writer: Ignore Notes div For now, ignore notes div for parity with other slide outputs. --- src/Text/Pandoc/Writers/Powerpoint.hs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index e5c29b75a..c53f2f66c 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -441,6 +441,7 @@ blockToParagraphs (DefinitionList entries) = do definition <- concatMapM (blockToParagraphs . BlockQuote) blksLst return $ term ++ definition concatMapM go entries +blockToParagraphs (Div (_, ("notes" : []), _) _) = return [] blockToParagraphs (Div _ blks) = concatMapM blockToParagraphs blks blockToParagraphs blk = do P.report $ BlockNotRendered blk -- cgit v1.2.3 From 7c8a6feaf2d494b9623ef5af9022580a7e4c3c16 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Thu, 4 Jan 2018 09:46:34 -0500 Subject: Powerpoint writer: remove some code duplication. --- src/Text/Pandoc/Writers/Powerpoint.hs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index c53f2f66c..9466b1570 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -530,6 +530,9 @@ splitBlocks' cur acc (h@(Header n _ _) : blks) = do LT -> splitBlocks' [] (acc ++ (if null cur then [] else [cur]) ++ [[h]]) blks EQ -> splitBlocks' [h] (acc ++ (if null cur then [] else [cur])) blks GT -> splitBlocks' (cur ++ [h]) acc blks +-- `blockToParagraphs` treats Plain and Para the same, so we can save +-- some code duplication by treating them the same here. +splitBlocks' cur acc ((Plain ils) : blks) = splitBlocks' cur acc ((Para ils) : blks) splitBlocks' cur acc ((Para (il:ils)) : blks) | isImage il = do slideLevel <- asks envSlideLevel case cur of @@ -540,16 +543,6 @@ splitBlocks' cur acc ((Para (il:ils)) : blks) | isImage il = do _ -> splitBlocks' [] (acc ++ (if null cur then [] else [cur]) ++ [[Para [il]]]) (if null ils then blks else (Para ils) : blks) -splitBlocks' cur acc ((Plain (il:ils)) : blks) | isImage il = do - slideLevel <- asks envSlideLevel - case cur of - (Header n _ _) : [] | n == slideLevel -> - splitBlocks' [] - (acc ++ [cur ++ [Plain [il]]]) - (if null ils then blks else (Plain ils) : blks) - _ -> splitBlocks' [] - (acc ++ (if null cur then [] else [cur]) ++ [[Plain [il]]]) - (if null ils then blks else (Plain ils) : blks) splitBlocks' cur acc (tbl@(Table _ _ _ _ _) : blks) = do slideLevel <- asks envSlideLevel case cur of -- cgit v1.2.3 From 856bc54526fc01b48a2d770406fcb9aaa2fa5da3 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Thu, 4 Jan 2018 19:05:14 +0100 Subject: Use hslua utils where possible Some helper functions and types have been moved to hslua. Change: minor --- src/Text/Pandoc/Lua/Module/MediaBag.hs | 10 +++++----- src/Text/Pandoc/Lua/Module/Pandoc.hs | 17 ++++++++--------- src/Text/Pandoc/Lua/Module/Utils.hs | 6 +++--- src/Text/Pandoc/Lua/Util.hs | 16 ---------------- 4 files changed, 16 insertions(+), 33 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/Module/MediaBag.hs b/src/Text/Pandoc/Lua/Module/MediaBag.hs index 33c441c99..9dd0a046d 100644 --- a/src/Text/Pandoc/Lua/Module/MediaBag.hs +++ b/src/Text/Pandoc/Lua/Module/MediaBag.hs @@ -32,11 +32,11 @@ module Text.Pandoc.Lua.Module.MediaBag import Control.Monad (zipWithM_) import Data.IORef (IORef, modifyIORef', readIORef) import Data.Maybe (fromMaybe) -import Foreign.Lua (Lua, NumResults, liftIO) +import Foreign.Lua (Lua, NumResults, Optional, liftIO) import Text.Pandoc.Class (CommonState (..), fetchItem, putCommonState, runIOorExplode, setMediaBag) import Text.Pandoc.Lua.StackInstances () -import Text.Pandoc.Lua.Util (OrNil (toMaybe), addFunction) +import Text.Pandoc.Lua.Util (addFunction) import Text.Pandoc.MIME (MimeType) import qualified Data.ByteString.Lazy as BL @@ -57,12 +57,12 @@ pushModule commonState mediaBagRef = do insertMediaFn :: IORef MB.MediaBag -> FilePath - -> OrNil MimeType + -> Optional MimeType -> BL.ByteString -> Lua NumResults -insertMediaFn mbRef fp nilOrMime contents = do +insertMediaFn mbRef fp optionalMime contents = do liftIO . modifyIORef' mbRef $ - MB.insertMedia fp (toMaybe nilOrMime) contents + MB.insertMedia fp (Lua.fromOptional optionalMime) contents return 0 lookupMediaFn :: IORef MB.MediaBag diff --git a/src/Text/Pandoc/Lua/Module/Pandoc.hs b/src/Text/Pandoc/Lua/Module/Pandoc.hs index 5b8714e07..a10bd3217 100644 --- a/src/Text/Pandoc/Lua/Module/Pandoc.hs +++ b/src/Text/Pandoc/Lua/Module/Pandoc.hs @@ -34,14 +34,13 @@ import Control.Monad (when) import Data.Default (Default (..)) import Data.Maybe (fromMaybe) import Data.Text (pack) -import Foreign.Lua (ToLuaStack, FromLuaStack, Lua, NumResults, liftIO) +import Foreign.Lua (ToLuaStack, FromLuaStack, Lua, NumResults, Optional, liftIO) import System.Exit (ExitCode (..)) import Text.Pandoc.Class (runIO) import Text.Pandoc.Definition (Block, Inline) import Text.Pandoc.Lua.Filter (walkInlines, walkBlocks, LuaFilter) import Text.Pandoc.Lua.StackInstances () -import Text.Pandoc.Lua.Util (OrNil (toMaybe), addFunction, addValue, - loadScriptFromDataDir, raiseError) +import Text.Pandoc.Lua.Util (addFunction, addValue, loadScriptFromDataDir) import Text.Pandoc.Walk (Walkable) import Text.Pandoc.Options (ReaderOptions (readerExtensions)) import Text.Pandoc.Process (pipeProcess) @@ -72,19 +71,19 @@ walkInline = walkElement walkBlock :: Block -> LuaFilter -> Lua Block walkBlock = walkElement -readDoc :: String -> OrNil String -> Lua NumResults +readDoc :: String -> Optional String -> Lua NumResults readDoc content formatSpecOrNil = do - let formatSpec = fromMaybe "markdown" (toMaybe formatSpecOrNil) + let formatSpec = fromMaybe "markdown" (Lua.fromOptional formatSpecOrNil) case getReader formatSpec of - Left s -> raiseError s -- Unknown reader + Left s -> Lua.raiseError s -- Unknown reader Right (reader, es) -> case reader of TextReader r -> do res <- liftIO $ runIO $ r def{ readerExtensions = es } (pack content) case res of Right pd -> (1 :: NumResults) <$ Lua.push pd -- success, push Pandoc - Left s -> raiseError (show s) -- error while reading - _ -> raiseError "Only string formats are supported at the moment." + Left s -> Lua.raiseError (show s) -- error while reading + _ -> Lua.raiseError "Only string formats are supported at the moment." -- | Pipes input through a command. pipeFn :: String @@ -95,7 +94,7 @@ pipeFn command args input = do (ec, output) <- liftIO $ pipeProcess Nothing command args input case ec of ExitSuccess -> 1 <$ Lua.push output - ExitFailure n -> raiseError (PipeError command n output) + ExitFailure n -> Lua.raiseError (PipeError command n output) data PipeError = PipeError { pipeErrorCommand :: String diff --git a/src/Text/Pandoc/Lua/Module/Utils.hs b/src/Text/Pandoc/Lua/Module/Utils.hs index c0d7397ce..e4ed409b3 100644 --- a/src/Text/Pandoc/Lua/Module/Utils.hs +++ b/src/Text/Pandoc/Lua/Module/Utils.hs @@ -33,7 +33,7 @@ import Control.Applicative ((<|>)) import Foreign.Lua (FromLuaStack, Lua, LuaInteger, NumResults) import Text.Pandoc.Definition (Pandoc, Meta, MetaValue, Block, Inline) import Text.Pandoc.Lua.StackInstances () -import Text.Pandoc.Lua.Util (OrNil (OrNil), addFunction) +import Text.Pandoc.Lua.Util (addFunction) import qualified Data.Digest.Pure.SHA as SHA import qualified Data.ByteString.Lazy as BSL @@ -59,8 +59,8 @@ hierarchicalize = return . Shared.hierarchicalize -- limit years to the range 1601-9999 (ISO 8601 accepts greater than -- or equal to 1583, but MS Word only accepts dates starting 1601). -- Returns nil instead of a string if the conversion failed. -normalizeDate :: String -> Lua (OrNil String) -normalizeDate = return . OrNil . Shared.normalizeDate +normalizeDate :: String -> Lua (Lua.Optional String) +normalizeDate = return . Lua.Optional . Shared.normalizeDate -- | Calculate the hash of the given contents. sha1 :: BSL.ByteString diff --git a/src/Text/Pandoc/Lua/Util.hs b/src/Text/Pandoc/Lua/Util.hs index 2958bd734..6b46cfc62 100644 --- a/src/Text/Pandoc/Lua/Util.hs +++ b/src/Text/Pandoc/Lua/Util.hs @@ -38,7 +38,6 @@ module Text.Pandoc.Lua.Util , addRawInt , raiseError , popValue - , OrNil (..) , PushViaCall , pushViaCall , pushViaConstructor @@ -115,21 +114,6 @@ popValue = do Left err -> Lua.throwLuaError err Right x -> return x --- | Newtype wrapper intended to be used for optional Lua values. Nesting this --- type is strongly discouraged and will likely lead to a wrong result. -newtype OrNil a = OrNil { toMaybe :: Maybe a } - -instance FromLuaStack a => FromLuaStack (OrNil a) where - peek idx = do - noValue <- Lua.isnoneornil idx - if noValue - then return (OrNil Nothing) - else OrNil . Just <$> Lua.peek idx - -instance ToLuaStack a => ToLuaStack (OrNil a) where - push (OrNil Nothing) = Lua.pushnil - push (OrNil (Just x)) = Lua.push x - -- | Helper class for pushing a single value to the stack via a lua function. -- See @pushViaCall@. class PushViaCall a where -- cgit v1.2.3 From 4f564b92030168a5416f8ca07530d189f3a6f277 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Fri, 5 Jan 2018 08:15:43 +0100 Subject: data/pandoc.lua: fix attribute names of Citation The fields were named like the Haskell fields, not like the documented, shorter version. The names are changed to match the documentation and Citations are given a shared metatable to enable simple extensibility. Fixes: #4222 --- src/Text/Pandoc/Lua/StackInstances.hs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index 119946b78..c669f2865 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -84,12 +84,12 @@ instance ToLuaStack Citation where instance FromLuaStack Citation where peek idx = do - id' <- getTable idx "citationId" - prefix <- getTable idx "citationPrefix" - suffix <- getTable idx "citationSuffix" - mode <- getTable idx "citationMode" - num <- getTable idx "citationNoteNum" - hash <- getTable idx "citationHash" + id' <- getTable idx "id" + prefix <- getTable idx "prefix" + suffix <- getTable idx "suffix" + mode <- getTable idx "mode" + num <- getTable idx "note_num" + hash <- getTable idx "hash" return $ Citation id' prefix suffix mode num hash instance ToLuaStack Alignment where -- cgit v1.2.3 From 0d935bd081bb4013168dc114461ab7c47fec2f44 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Fri, 5 Jan 2018 20:19:47 +0100 Subject: Update copyright notices to include 2018 --- src/Text/Pandoc.hs | 4 ++-- src/Text/Pandoc/App.hs | 6 +++--- src/Text/Pandoc/Asciify.hs | 4 ++-- src/Text/Pandoc/BCP47.hs | 4 ++-- src/Text/Pandoc/CSV.hs | 4 ++-- src/Text/Pandoc/Error.hs | 4 ++-- src/Text/Pandoc/Extensions.hs | 4 ++-- src/Text/Pandoc/Highlighting.hs | 4 ++-- src/Text/Pandoc/ImageSize.hs | 4 ++-- src/Text/Pandoc/Logging.hs | 2 +- src/Text/Pandoc/Lua.hs | 4 ++-- src/Text/Pandoc/Lua/Init.hs | 4 ++-- src/Text/Pandoc/Lua/Module/MediaBag.hs | 4 ++-- src/Text/Pandoc/Lua/Module/Pandoc.hs | 4 ++-- src/Text/Pandoc/Lua/Module/Utils.hs | 4 ++-- src/Text/Pandoc/Lua/Packages.hs | 4 ++-- src/Text/Pandoc/Lua/StackInstances.hs | 8 ++++---- src/Text/Pandoc/Lua/Util.hs | 8 ++++---- src/Text/Pandoc/MIME.hs | 4 ++-- src/Text/Pandoc/MediaBag.hs | 4 ++-- src/Text/Pandoc/Options.hs | 4 ++-- src/Text/Pandoc/PDF.hs | 4 ++-- src/Text/Pandoc/Parsing.hs | 4 ++-- src/Text/Pandoc/Pretty.hs | 4 ++-- src/Text/Pandoc/Process.hs | 4 ++-- src/Text/Pandoc/Readers.hs | 4 ++-- src/Text/Pandoc/Readers/CommonMark.hs | 4 ++-- src/Text/Pandoc/Readers/Docx.hs | 4 ++-- src/Text/Pandoc/Readers/Docx/Lists.hs | 4 ++-- src/Text/Pandoc/Readers/Docx/Parse.hs | 4 ++-- src/Text/Pandoc/Readers/HTML.hs | 4 ++-- src/Text/Pandoc/Readers/LaTeX.hs | 4 ++-- src/Text/Pandoc/Readers/LaTeX/Types.hs | 4 ++-- src/Text/Pandoc/Readers/Markdown.hs | 4 ++-- src/Text/Pandoc/Readers/MediaWiki.hs | 4 ++-- src/Text/Pandoc/Readers/Muse.hs | 4 ++-- src/Text/Pandoc/Readers/Native.hs | 4 ++-- src/Text/Pandoc/Readers/Org.hs | 4 ++-- src/Text/Pandoc/Readers/Org/BlockStarts.hs | 4 ++-- src/Text/Pandoc/Readers/Org/Blocks.hs | 4 ++-- src/Text/Pandoc/Readers/Org/DocumentTree.hs | 4 ++-- src/Text/Pandoc/Readers/Org/ExportSettings.hs | 4 ++-- src/Text/Pandoc/Readers/Org/Inlines.hs | 4 ++-- src/Text/Pandoc/Readers/Org/Meta.hs | 4 ++-- src/Text/Pandoc/Readers/Org/ParserState.hs | 4 ++-- src/Text/Pandoc/Readers/Org/Parsing.hs | 4 ++-- src/Text/Pandoc/Readers/Org/Shared.hs | 4 ++-- src/Text/Pandoc/Readers/RST.hs | 4 ++-- src/Text/Pandoc/Readers/Textile.hs | 4 ++-- src/Text/Pandoc/Readers/Vimwiki.hs | 4 ++-- src/Text/Pandoc/SelfContained.hs | 4 ++-- src/Text/Pandoc/Shared.hs | 4 ++-- src/Text/Pandoc/Slides.hs | 4 ++-- src/Text/Pandoc/Templates.hs | 4 ++-- src/Text/Pandoc/Translations.hs | 4 ++-- src/Text/Pandoc/UTF8.hs | 4 ++-- src/Text/Pandoc/UUID.hs | 4 ++-- src/Text/Pandoc/Writers.hs | 4 ++-- src/Text/Pandoc/Writers/AsciiDoc.hs | 4 ++-- src/Text/Pandoc/Writers/CommonMark.hs | 4 ++-- src/Text/Pandoc/Writers/ConTeXt.hs | 4 ++-- src/Text/Pandoc/Writers/Custom.hs | 4 ++-- src/Text/Pandoc/Writers/Docbook.hs | 4 ++-- src/Text/Pandoc/Writers/Docx.hs | 4 ++-- src/Text/Pandoc/Writers/DokuWiki.hs | 4 ++-- src/Text/Pandoc/Writers/EPUB.hs | 4 ++-- src/Text/Pandoc/Writers/FB2.hs | 4 ++-- src/Text/Pandoc/Writers/HTML.hs | 4 ++-- src/Text/Pandoc/Writers/Haddock.hs | 2 +- src/Text/Pandoc/Writers/ICML.hs | 2 +- src/Text/Pandoc/Writers/JATS.hs | 4 ++-- src/Text/Pandoc/Writers/LaTeX.hs | 4 ++-- src/Text/Pandoc/Writers/Man.hs | 4 ++-- src/Text/Pandoc/Writers/Markdown.hs | 4 ++-- src/Text/Pandoc/Writers/MediaWiki.hs | 4 ++-- src/Text/Pandoc/Writers/Ms.hs | 4 ++-- src/Text/Pandoc/Writers/Muse.hs | 4 ++-- src/Text/Pandoc/Writers/Native.hs | 4 ++-- src/Text/Pandoc/Writers/ODT.hs | 4 ++-- src/Text/Pandoc/Writers/OPML.hs | 4 ++-- src/Text/Pandoc/Writers/OpenDocument.hs | 4 ++-- src/Text/Pandoc/Writers/Org.hs | 8 ++++---- src/Text/Pandoc/Writers/Powerpoint.hs | 4 ++-- src/Text/Pandoc/Writers/RST.hs | 4 ++-- src/Text/Pandoc/Writers/RTF.hs | 4 ++-- src/Text/Pandoc/Writers/Shared.hs | 4 ++-- src/Text/Pandoc/Writers/TEI.hs | 4 ++-- src/Text/Pandoc/Writers/Texinfo.hs | 4 ++-- src/Text/Pandoc/Writers/Textile.hs | 4 ++-- src/Text/Pandoc/Writers/ZimWiki.hs | 6 +++--- src/Text/Pandoc/XML.hs | 4 ++-- 91 files changed, 187 insertions(+), 187 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc.hs b/src/Text/Pandoc.hs index 0da2a925c..dd2856674 100644 --- a/src/Text/Pandoc.hs +++ b/src/Text/Pandoc.hs @@ -2,7 +2,7 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE ScopedTypeVariables #-} {- -Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc - Copyright : Copyright (C) 2006-2017 John MacFarlane + Copyright : Copyright (C) 2006-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 7c463d743..c759df46c 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -4,7 +4,7 @@ {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TupleSections #-} {- -Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.App - Copyright : Copyright (C) 2006-2017 John MacFarlane + Copyright : Copyright (C) 2006-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley@edu> @@ -1662,7 +1662,7 @@ usageMessage programName = usageInfo (programName ++ " [OPTIONS] [FILES]") copyrightMessage :: String copyrightMessage = intercalate "\n" [ "", - "Copyright (C) 2006-2017 John MacFarlane", + "Copyright (C) 2006-2018 John MacFarlane", "Web: http://pandoc.org", "This is free software; see the source for copying conditions.", "There is no warranty, not even for merchantability or fitness", diff --git a/src/Text/Pandoc/Asciify.hs b/src/Text/Pandoc/Asciify.hs index 7125e5bcd..11d3eddac 100644 --- a/src/Text/Pandoc/Asciify.hs +++ b/src/Text/Pandoc/Asciify.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2013-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2013-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Asciify - Copyright : Copyright (C) 2013-2017 John MacFarlane + Copyright : Copyright (C) 2013-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/BCP47.hs b/src/Text/Pandoc/BCP47.hs index a9fb5c7a7..2dd825142 100644 --- a/src/Text/Pandoc/BCP47.hs +++ b/src/Text/Pandoc/BCP47.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2017–2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.BCP47 - Copyright : Copyright (C) 2017 John MacFarlane + Copyright : Copyright (C) 2017–2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/CSV.hs b/src/Text/Pandoc/CSV.hs index e25b684f8..3415ae88f 100644 --- a/src/Text/Pandoc/CSV.hs +++ b/src/Text/Pandoc/CSV.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2017–2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.CSV - Copyright : Copyright (C) 2017 John MacFarlane <jgm@berkeley.edu> + Copyright : Copyright (C) 2017–2018 John MacFarlane <jgm@berkeley.edu> License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> Stability : alpha diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs index 0c97ecbad..f78a31481 100644 --- a/src/Text/Pandoc/Error.hs +++ b/src/Text/Pandoc/Error.hs @@ -1,7 +1,7 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-} {- -Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | Module : Text.Pandoc.Error - Copyright : Copyright (C) 2006-2017 John MacFarlane + Copyright : Copyright (C) 2006-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 31fddb148..cb3490cf7 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2012-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2012-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Extensions - Copyright : Copyright (C) 2012-2017 John MacFarlane + Copyright : Copyright (C) 2012-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Highlighting.hs b/src/Text/Pandoc/Highlighting.hs index 9c90b229e..113727750 100644 --- a/src/Text/Pandoc/Highlighting.hs +++ b/src/Text/Pandoc/Highlighting.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2008-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2008-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Highlighting - Copyright : Copyright (C) 2008-2017 John MacFarlane + Copyright : Copyright (C) 2008-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/ImageSize.hs b/src/Text/Pandoc/ImageSize.hs index b4206b84b..4ac1d535f 100644 --- a/src/Text/Pandoc/ImageSize.hs +++ b/src/Text/Pandoc/ImageSize.hs @@ -1,7 +1,7 @@ {-# LANGUAGE OverloadedStrings, ScopedTypeVariables, CPP #-} {-# OPTIONS_GHC -fno-warn-type-defaults #-} {- - Copyright (C) 2011-2017 John MacFarlane <jgm@berkeley.edu> + Copyright (C) 2011-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -20,7 +20,7 @@ {- | Module : Text.Pandoc.ImageSize -Copyright : Copyright (C) 2011-2017 John MacFarlane +Copyright : Copyright (C) 2011-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs index 016e64f6c..b22c08467 100644 --- a/src/Text/Pandoc/Logging.hs +++ b/src/Text/Pandoc/Logging.hs @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | Module : Text.Pandoc.Logging - Copyright : Copyright (C) 2006-2017 John MacFarlane + Copyright : Copyright (C) 2006-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index d02963418..48518aa54 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -1,5 +1,5 @@ {- -Copyright © 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright © 2017–2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -17,7 +17,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | Module : Text.Pandoc.Lua - Copyright : Copyright © 2017 Albert Krewinkel + Copyright : Copyright © 2017–2018 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> diff --git a/src/Text/Pandoc/Lua/Init.hs b/src/Text/Pandoc/Lua/Init.hs index 9b107e945..25869bf91 100644 --- a/src/Text/Pandoc/Lua/Init.hs +++ b/src/Text/Pandoc/Lua/Init.hs @@ -1,5 +1,5 @@ {- -Copyright © 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright © 2017-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -17,7 +17,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | Module : Text.Pandoc.Lua - Copyright : Copyright © 2017 Albert Krewinkel + Copyright : Copyright © 2017-2018 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> diff --git a/src/Text/Pandoc/Lua/Module/MediaBag.hs b/src/Text/Pandoc/Lua/Module/MediaBag.hs index 9dd0a046d..7d942a452 100644 --- a/src/Text/Pandoc/Lua/Module/MediaBag.hs +++ b/src/Text/Pandoc/Lua/Module/MediaBag.hs @@ -1,5 +1,5 @@ {- -Copyright © 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright © 2017-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -17,7 +17,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | Module : Text.Pandoc.Lua.Module.MediaBag - Copyright : Copyright © 2017 Albert Krewinkel + Copyright : Copyright © 2017-2018 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> diff --git a/src/Text/Pandoc/Lua/Module/Pandoc.hs b/src/Text/Pandoc/Lua/Module/Pandoc.hs index a10bd3217..f458d4773 100644 --- a/src/Text/Pandoc/Lua/Module/Pandoc.hs +++ b/src/Text/Pandoc/Lua/Module/Pandoc.hs @@ -1,5 +1,5 @@ {- -Copyright © 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright © 2017-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {-# LANGUAGE FlexibleContexts #-} {- | Module : Text.Pandoc.Lua.Module.Pandoc - Copyright : Copyright © 2017 Albert Krewinkel + Copyright : Copyright © 2017-2018 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> diff --git a/src/Text/Pandoc/Lua/Module/Utils.hs b/src/Text/Pandoc/Lua/Module/Utils.hs index e4ed409b3..b453b38d7 100644 --- a/src/Text/Pandoc/Lua/Module/Utils.hs +++ b/src/Text/Pandoc/Lua/Module/Utils.hs @@ -1,5 +1,5 @@ {- -Copyright © 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright © 2017-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -17,7 +17,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | Module : Text.Pandoc.Lua.Module.Utils - Copyright : Copyright © 2017 Albert Krewinkel + Copyright : Copyright © 2017-2018 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> diff --git a/src/Text/Pandoc/Lua/Packages.hs b/src/Text/Pandoc/Lua/Packages.hs index f26c17084..dda2dd2fe 100644 --- a/src/Text/Pandoc/Lua/Packages.hs +++ b/src/Text/Pandoc/Lua/Packages.hs @@ -1,5 +1,5 @@ {- -Copyright © 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright © 2017-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {-# LANGUAGE FlexibleContexts #-} {- | Module : Text.Pandoc.Lua.Packages - Copyright : Copyright © 2017 Albert Krewinkel + Copyright : Copyright © 2017-2018 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index c669f2865..531261099 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -1,6 +1,6 @@ {- -Copyright © 2012-2017 John MacFarlane <jgm@berkeley.edu> - 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright © 2012-2018 John MacFarlane <jgm@berkeley.edu> + 2017-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -21,8 +21,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {-# OPTIONS_GHC -fno-warn-orphans #-} {- | Module : Text.Pandoc.Lua.StackInstances - Copyright : © 2012-2017 John MacFarlane - © 2017 Albert Krewinkel + Copyright : © 2012-2018 John MacFarlane + © 2017-2018 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> diff --git a/src/Text/Pandoc/Lua/Util.hs b/src/Text/Pandoc/Lua/Util.hs index 6b46cfc62..799b45b72 100644 --- a/src/Text/Pandoc/Lua/Util.hs +++ b/src/Text/Pandoc/Lua/Util.hs @@ -1,6 +1,6 @@ {- -Copyright © 2012-2017 John MacFarlane <jgm@berkeley.edu> - 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright © 2012-2018 John MacFarlane <jgm@berkeley.edu> + 2017-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -19,8 +19,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {-# LANGUAGE FlexibleInstances #-} {- | Module : Text.Pandoc.Lua.Util - Copyright : © 2012–2017 John MacFarlane, - © 2017 Albert Krewinkel + Copyright : © 2012–2018 John MacFarlane, + © 2017-2018 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> diff --git a/src/Text/Pandoc/MIME.hs b/src/Text/Pandoc/MIME.hs index eba8d512f..43abe9b2f 100644 --- a/src/Text/Pandoc/MIME.hs +++ b/src/Text/Pandoc/MIME.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2011-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2011-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.MIME - Copyright : Copyright (C) 2011-2017 John MacFarlane + Copyright : Copyright (C) 2011-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/MediaBag.hs b/src/Text/Pandoc/MediaBag.hs index 1c15d1cee..0d060fe1a 100644 --- a/src/Text/Pandoc/MediaBag.hs +++ b/src/Text/Pandoc/MediaBag.hs @@ -1,7 +1,7 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {- -Copyright (C) 2014-2015, 2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2014-2015, 2017–2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.MediaBag - Copyright : Copyright (C) 2014-2015, 2017 John MacFarlane + Copyright : Copyright (C) 2014-2015, 2017–2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Options.hs b/src/Text/Pandoc/Options.hs index 1fb838321..bd4ab252b 100644 --- a/src/Text/Pandoc/Options.hs +++ b/src/Text/Pandoc/Options.hs @@ -2,7 +2,7 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE TemplateHaskell #-} {- -Copyright (C) 2012-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2012-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Options - Copyright : Copyright (C) 2012-2017 John MacFarlane + Copyright : Copyright (C) 2012-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index beb3c569f..974934763 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -2,7 +2,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {- -Copyright (C) 2012-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2012-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.PDF - Copyright : Copyright (C) 2012-2017 John MacFarlane + Copyright : Copyright (C) 2012-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index c86f6718a..9573d7875 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -6,7 +6,7 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeSynonymInstances #-} {- -Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -25,7 +25,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Parsing - Copyright : Copyright (C) 2006-2017 John MacFarlane + Copyright : Copyright (C) 2006-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Pretty.hs b/src/Text/Pandoc/Pretty.hs index f95bfa8e0..25c2373a6 100644 --- a/src/Text/Pandoc/Pretty.hs +++ b/src/Text/Pandoc/Pretty.hs @@ -1,7 +1,7 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {- -Copyright (C) 2010-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2010-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111(-1)307 USA {- | Module : Text.Pandoc.Pretty - Copyright : Copyright (C) 2010-2017 John MacFarlane + Copyright : Copyright (C) 2010-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Process.hs b/src/Text/Pandoc/Process.hs index b2a0c17f1..27807a8c8 100644 --- a/src/Text/Pandoc/Process.hs +++ b/src/Text/Pandoc/Process.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2013-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2013-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Process - Copyright : Copyright (C) 2013-2017 John MacFarlane + Copyright : Copyright (C) 2013-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Readers.hs b/src/Text/Pandoc/Readers.hs index a8448952e..b9374ba06 100644 --- a/src/Text/Pandoc/Readers.hs +++ b/src/Text/Pandoc/Readers.hs @@ -2,7 +2,7 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE ScopedTypeVariables #-} {- -Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers - Copyright : Copyright (C) 2006-2017 John MacFarlane + Copyright : Copyright (C) 2006-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Readers/CommonMark.hs b/src/Text/Pandoc/Readers/CommonMark.hs index ea9747342..6fbc09c17 100644 --- a/src/Text/Pandoc/Readers/CommonMark.hs +++ b/src/Text/Pandoc/Readers/CommonMark.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2015-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2015-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.CommonMark - Copyright : Copyright (C) 2015-2017 John MacFarlane + Copyright : Copyright (C) 2015-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index f01a94550..e0f32b908 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -2,7 +2,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternGuards #-} {- -Copyright (C) 2014-2017 Jesse Rosenthal <jrosenthal@jhu.edu> +Copyright (C) 2014-2018 Jesse Rosenthal <jrosenthal@jhu.edu> 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 @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Docx - Copyright : Copyright (C) 2014-2017 Jesse Rosenthal + Copyright : Copyright (C) 2014-2018 Jesse Rosenthal License : GNU GPL, version 2 or above Maintainer : Jesse Rosenthal <jrosenthal@jhu.edu> diff --git a/src/Text/Pandoc/Readers/Docx/Lists.hs b/src/Text/Pandoc/Readers/Docx/Lists.hs index 70eccd7d6..fa4870fff 100644 --- a/src/Text/Pandoc/Readers/Docx/Lists.hs +++ b/src/Text/Pandoc/Readers/Docx/Lists.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2014-2017 Jesse Rosenthal <jrosenthal@jhu.edu> +Copyright (C) 2014-2018 Jesse Rosenthal <jrosenthal@jhu.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Docx.Lists - Copyright : Copyright (C) 2014-2017 Jesse Rosenthal + Copyright : Copyright (C) 2014-2018 Jesse Rosenthal License : GNU GPL, version 2 or above Maintainer : Jesse Rosenthal <jrosenthal@jhu.edu> diff --git a/src/Text/Pandoc/Readers/Docx/Parse.hs b/src/Text/Pandoc/Readers/Docx/Parse.hs index 1fcbdf386..b79b39369 100644 --- a/src/Text/Pandoc/Readers/Docx/Parse.hs +++ b/src/Text/Pandoc/Readers/Docx/Parse.hs @@ -3,7 +3,7 @@ {-# LANGUAGE ViewPatterns #-} {- -Copyright (C) 2014-2017 Jesse Rosenthal <jrosenthal@jhu.edu> +Copyright (C) 2014-2018 Jesse Rosenthal <jrosenthal@jhu.edu> 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 @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Docx.Parse - Copyright : Copyright (C) 2014-2017 Jesse Rosenthal + Copyright : Copyright (C) 2014-2018 Jesse Rosenthal License : GNU GPL, version 2 or above Maintainer : Jesse Rosenthal <jrosenthal@jhu.edu> diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index 65171d37a..e8dd9ec11 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -4,7 +4,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ViewPatterns #-} {- -Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.HTML - Copyright : Copyright (C) 2006-2017 John MacFarlane + Copyright : Copyright (C) 2006-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index a0447962c..62d240688 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -4,7 +4,7 @@ {-# LANGUAGE PatternGuards #-} {-# LANGUAGE ScopedTypeVariables #-} {- -Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.LaTeX - Copyright : Copyright (C) 2006-2017 John MacFarlane + Copyright : Copyright (C) 2006-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Readers/LaTeX/Types.hs b/src/Text/Pandoc/Readers/LaTeX/Types.hs index b24b2ad0a..c9cbaa9b9 100644 --- a/src/Text/Pandoc/Readers/LaTeX/Types.hs +++ b/src/Text/Pandoc/Readers/LaTeX/Types.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2017-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.LaTeX.Types - Copyright : Copyright (C) 2017 John MacFarlane + Copyright : Copyright (C) 2017-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 4a09c2aad..aaefa5ba1 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -2,7 +2,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {- -Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Markdown - Copyright : Copyright (C) 2006-2017 John MacFarlane + Copyright : Copyright (C) 2006-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Readers/MediaWiki.hs b/src/Text/Pandoc/Readers/MediaWiki.hs index a2b3346df..c19ef2f46 100644 --- a/src/Text/Pandoc/Readers/MediaWiki.hs +++ b/src/Text/Pandoc/Readers/MediaWiki.hs @@ -3,7 +3,7 @@ {-# LANGUAGE TypeSynonymInstances #-} -- RelaxedPolyRec needed for inlinesBetween on GHC < 7 {- - Copyright (C) 2012-2017 John MacFarlane <jgm@berkeley.edu> + Copyright (C) 2012-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.MediaWiki - Copyright : Copyright (C) 2012-2017 John MacFarlane + Copyright : Copyright (C) 2012-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index d86b47e83..78c567759 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -1,6 +1,6 @@ {-# LANGUAGE FlexibleContexts #-} {- - Copyright (C) 2017 Alexander Krotov <ilabdsf@gmail.com> + Copyright (C) 2017-2018 Alexander Krotov <ilabdsf@gmail.com> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Muse - Copyright : Copyright (C) 2017 Alexander Krotov + Copyright : Copyright (C) 2017-2018 Alexander Krotov License : GNU GPL, version 2 or above Maintainer : Alexander Krotov <ilabdsf@gmail.com> diff --git a/src/Text/Pandoc/Readers/Native.hs b/src/Text/Pandoc/Readers/Native.hs index ce33e080b..88f6bfe8f 100644 --- a/src/Text/Pandoc/Readers/Native.hs +++ b/src/Text/Pandoc/Readers/Native.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2011-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2011-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Native - Copyright : Copyright (C) 2011-2017 John MacFarlane + Copyright : Copyright (C) 2011-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Readers/Org.hs b/src/Text/Pandoc/Readers/Org.hs index eaccc251c..292830bd2 100644 --- a/src/Text/Pandoc/Readers/Org.hs +++ b/src/Text/Pandoc/Readers/Org.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2014-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright (C) 2014-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Org - Copyright : Copyright (C) 2014-2017 Albert Krewinkel + Copyright : Copyright (C) 2014-2018 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> diff --git a/src/Text/Pandoc/Readers/Org/BlockStarts.hs b/src/Text/Pandoc/Readers/Org/BlockStarts.hs index 7937c0ef7..424102cb0 100644 --- a/src/Text/Pandoc/Readers/Org/BlockStarts.hs +++ b/src/Text/Pandoc/Readers/Org/BlockStarts.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2014-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright (C) 2014-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Org.BlockStarts - Copyright : Copyright (C) 2014-2017 Albert Krewinkel + Copyright : Copyright (C) 2014-2018 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index a930652af..c5a7d8e10 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2014-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright (C) 2014-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {-# LANGUAGE RecordWildCards #-} {- | Module : Text.Pandoc.Readers.Org.Blocks - Copyright : Copyright (C) 2014-2017 Albert Krewinkel + Copyright : Copyright (C) 2014-2018 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> diff --git a/src/Text/Pandoc/Readers/Org/DocumentTree.hs b/src/Text/Pandoc/Readers/Org/DocumentTree.hs index 3b90c9336..f77778ec9 100644 --- a/src/Text/Pandoc/Readers/Org/DocumentTree.hs +++ b/src/Text/Pandoc/Readers/Org/DocumentTree.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2014-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright (C) 2014-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {-# LANGUAGE ViewPatterns #-} {- | Module : Text.Pandoc.Readers.Org.DocumentTree - Copyright : Copyright (C) 2014-2017 Albert Krewinkel + Copyright : Copyright (C) 2014-2018 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> diff --git a/src/Text/Pandoc/Readers/Org/ExportSettings.hs b/src/Text/Pandoc/Readers/Org/ExportSettings.hs index 36258aeba..6a70c50b9 100644 --- a/src/Text/Pandoc/Readers/Org/ExportSettings.hs +++ b/src/Text/Pandoc/Readers/Org/ExportSettings.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2014-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright (C) 2016-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Org.ExportSettings - Copyright : © 2016–2017 Albert Krewinkel + Copyright : © 2016–2018 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> diff --git a/src/Text/Pandoc/Readers/Org/Inlines.hs b/src/Text/Pandoc/Readers/Org/Inlines.hs index f3649af66..670f8ace0 100644 --- a/src/Text/Pandoc/Readers/Org/Inlines.hs +++ b/src/Text/Pandoc/Readers/Org/Inlines.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2014-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright (C) 2014-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Org.Inlines - Copyright : Copyright (C) 2014-2017 Albert Krewinkel + Copyright : Copyright (C) 2014-2018 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> diff --git a/src/Text/Pandoc/Readers/Org/Meta.hs b/src/Text/Pandoc/Readers/Org/Meta.hs index d22902eae..0a690028d 100644 --- a/src/Text/Pandoc/Readers/Org/Meta.hs +++ b/src/Text/Pandoc/Readers/Org/Meta.hs @@ -1,7 +1,7 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TupleSections #-} {- -Copyright (C) 2014-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright (C) 2014-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Org.Meta - Copyright : Copyright (C) 2014-2017 Albert Krewinkel + Copyright : Copyright (C) 2014-2018 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> diff --git a/src/Text/Pandoc/Readers/Org/ParserState.hs b/src/Text/Pandoc/Readers/Org/ParserState.hs index e0045fcd5..e2acce5bf 100644 --- a/src/Text/Pandoc/Readers/Org/ParserState.hs +++ b/src/Text/Pandoc/Readers/Org/ParserState.hs @@ -1,7 +1,7 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {- -Copyright (C) 2014-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright (C) 2014-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Org.ParserState - Copyright : Copyright (C) 2014-2017 Albert Krewinkel + Copyright : Copyright (C) 2014-2018 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> diff --git a/src/Text/Pandoc/Readers/Org/Parsing.hs b/src/Text/Pandoc/Readers/Org/Parsing.hs index 3273c92e4..36420478b 100644 --- a/src/Text/Pandoc/Readers/Org/Parsing.hs +++ b/src/Text/Pandoc/Readers/Org/Parsing.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2014-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright (C) 2014-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Org.Parsing - Copyright : Copyright (C) 2014-2017 Albert Krewinkel + Copyright : Copyright (C) 2014-2018 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> diff --git a/src/Text/Pandoc/Readers/Org/Shared.hs b/src/Text/Pandoc/Readers/Org/Shared.hs index 580e9194f..cba72cc07 100644 --- a/src/Text/Pandoc/Readers/Org/Shared.hs +++ b/src/Text/Pandoc/Readers/Org/Shared.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2014-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> +Copyright (C) 2014-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Org.Shared - Copyright : Copyright (C) 2014-2017 Albert Krewinkel + Copyright : Copyright (C) 2014-2018 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 9f259d958..27ce5fa2d 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -2,7 +2,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {- -Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.RST - Copyright : Copyright (C) 2006-2017 John MacFarlane + Copyright : Copyright (C) 2006-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Readers/Textile.hs b/src/Text/Pandoc/Readers/Textile.hs index a3b4f2ff1..46d6301e4 100644 --- a/src/Text/Pandoc/Readers/Textile.hs +++ b/src/Text/Pandoc/Readers/Textile.hs @@ -1,6 +1,6 @@ {- Copyright (C) 2010-2012 Paul Rivier <paul*rivier#demotera*com> | tr '*#' '.@' - 2010-2017 John MacFarlane + 2010-2018 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 @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Textile Copyright : Copyright (C) 2010-2012 Paul Rivier - 2010-2017 John MacFarlane + 2010-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : Paul Rivier <paul*rivier#demotera*com> diff --git a/src/Text/Pandoc/Readers/Vimwiki.hs b/src/Text/Pandoc/Readers/Vimwiki.hs index 5575b3687..162fb371e 100644 --- a/src/Text/Pandoc/Readers/Vimwiki.hs +++ b/src/Text/Pandoc/Readers/Vimwiki.hs @@ -1,5 +1,5 @@ {- - Copyright (C) 2017 Yuchen Pei <me@ypei.me> + Copyright (C) 2017-2018 Yuchen Pei <me@ypei.me> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Readers.Vimwiki - Copyright : Copyright (C) 2017 Yuchen Pei + Copyright : Copyright (C) 2017-2018 Yuchen Pei License : GNU GPL, version 2 or above Maintainer : Yuchen Pei <me@ypei.me> diff --git a/src/Text/Pandoc/SelfContained.hs b/src/Text/Pandoc/SelfContained.hs index 7cdd6f6e1..a1c5c919e 100644 --- a/src/Text/Pandoc/SelfContained.hs +++ b/src/Text/Pandoc/SelfContained.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2011-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2011-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.SelfContained - Copyright : Copyright (C) 2011-2017 John MacFarlane + Copyright : Copyright (C) 2011-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 005603191..583c7a63f 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -6,7 +6,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ViewPatterns #-} {- -Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -25,7 +25,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Shared - Copyright : Copyright (C) 2006-2017 John MacFarlane + Copyright : Copyright (C) 2006-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Slides.hs b/src/Text/Pandoc/Slides.hs index 27e7d3d76..9d63555c2 100644 --- a/src/Text/Pandoc/Slides.hs +++ b/src/Text/Pandoc/Slides.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2012-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2012-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Slides - Copyright : Copyright (C) 2012-2017 John MacFarlane + Copyright : Copyright (C) 2012-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Templates.hs b/src/Text/Pandoc/Templates.hs index d4524c333..4be0d081c 100644 --- a/src/Text/Pandoc/Templates.hs +++ b/src/Text/Pandoc/Templates.hs @@ -3,7 +3,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeSynonymInstances #-} {- -Copyright (C) 2009-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2009-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Templates - Copyright : Copyright (C) 2009-2017 John MacFarlane + Copyright : Copyright (C) 2009-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Translations.hs b/src/Text/Pandoc/Translations.hs index 00529c1de..949618178 100644 --- a/src/Text/Pandoc/Translations.hs +++ b/src/Text/Pandoc/Translations.hs @@ -1,7 +1,7 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {- -Copyright (C) 2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2017-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Translations - Copyright : Copyright (C) 2017 John MacFarlane + Copyright : Copyright (C) 2017-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/UTF8.hs b/src/Text/Pandoc/UTF8.hs index 663f30d92..3f759958f 100644 --- a/src/Text/Pandoc/UTF8.hs +++ b/src/Text/Pandoc/UTF8.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2010-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2010-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.UTF8 - Copyright : Copyright (C) 2010-2017 John MacFarlane + Copyright : Copyright (C) 2010-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/UUID.hs b/src/Text/Pandoc/UUID.hs index 1527ce435..4d99324db 100644 --- a/src/Text/Pandoc/UUID.hs +++ b/src/Text/Pandoc/UUID.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2010-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2010-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.UUID - Copyright : Copyright (C) 2010-2017 John MacFarlane + Copyright : Copyright (C) 2010-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers.hs b/src/Text/Pandoc/Writers.hs index b336c1f1a..596a8680e 100644 --- a/src/Text/Pandoc/Writers.hs +++ b/src/Text/Pandoc/Writers.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc - Copyright : Copyright (C) 2006-2017 John MacFarlane + Copyright : Copyright (C) 2006-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/AsciiDoc.hs b/src/Text/Pandoc/Writers/AsciiDoc.hs index 3231e1e30..a6906eb68 100644 --- a/src/Text/Pandoc/Writers/AsciiDoc.hs +++ b/src/Text/Pandoc/Writers/AsciiDoc.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.AsciiDoc - Copyright : Copyright (C) 2006-2017 John MacFarlane + Copyright : Copyright (C) 2006-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/CommonMark.hs b/src/Text/Pandoc/Writers/CommonMark.hs index 8d1eb04d1..7a6eb2948 100644 --- a/src/Text/Pandoc/Writers/CommonMark.hs +++ b/src/Text/Pandoc/Writers/CommonMark.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2015-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2015-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.CommonMark - Copyright : Copyright (C) 2015-2017 John MacFarlane + Copyright : Copyright (C) 2015-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index f0f4cd00e..072c2ca8d 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -1,7 +1,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {- -Copyright (C) 2007-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2007-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.ConTeXt - Copyright : Copyright (C) 2007-2017 John MacFarlane + Copyright : Copyright (C) 2007-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/Custom.hs b/src/Text/Pandoc/Writers/Custom.hs index a33196cbe..37b44b646 100644 --- a/src/Text/Pandoc/Writers/Custom.hs +++ b/src/Text/Pandoc/Writers/Custom.hs @@ -1,6 +1,6 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE FlexibleInstances #-} -{- Copyright (C) 2012-2017 John MacFarlane <jgm@berkeley.edu> +{- Copyright (C) 2012-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Custom - Copyright : Copyright (C) 2012-2017 John MacFarlane + Copyright : Copyright (C) 2012-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/Docbook.hs b/src/Text/Pandoc/Writers/Docbook.hs index 74a1249a4..3034fade5 100644 --- a/src/Text/Pandoc/Writers/Docbook.hs +++ b/src/Text/Pandoc/Writers/Docbook.hs @@ -1,7 +1,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternGuards #-} {- -Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Docbook - Copyright : Copyright (C) 2006-2017 John MacFarlane + Copyright : Copyright (C) 2006-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 6343b314e..c077d54ba 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -4,7 +4,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ViewPatterns #-} {- -Copyright (C) 2012-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2012-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Docx - Copyright : Copyright (C) 2012-2017 John MacFarlane + Copyright : Copyright (C) 2012-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/DokuWiki.hs b/src/Text/Pandoc/Writers/DokuWiki.hs index e52cc75ad..dda21d23d 100644 --- a/src/Text/Pandoc/Writers/DokuWiki.hs +++ b/src/Text/Pandoc/Writers/DokuWiki.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2008-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2008-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.DokuWiki - Copyright : Copyright (C) 2008-2017 John MacFarlane + Copyright : Copyright (C) 2008-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : Clare Macrae <clare.macrae@googlemail.com> diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs index 2ed397d36..7b4853a24 100644 --- a/src/Text/Pandoc/Writers/EPUB.hs +++ b/src/Text/Pandoc/Writers/EPUB.hs @@ -3,7 +3,7 @@ {-# LANGUAGE PatternGuards #-} {-# LANGUAGE ScopedTypeVariables #-} {- -Copyright (C) 2010-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2010-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.EPUB - Copyright : Copyright (C) 2010-2017 John MacFarlane + Copyright : Copyright (C) 2010-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index 0a4130ca4..b1e8c8575 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -2,7 +2,7 @@ {- Copyright (c) 2011-2012 Sergey Astanin - 2012-2017 John MacFarlane + 2012-2018 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 @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.FB2 Copyright : Copyright (C) 2011-2012 Sergey Astanin - 2012-2017 John MacFarlane + 2012-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 7ff7284cc..e81338550 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -3,7 +3,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ViewPatterns #-} {- -Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.HTML - Copyright : Copyright (C) 2006-2017 John MacFarlane + Copyright : Copyright (C) 2006-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/Haddock.hs b/src/Text/Pandoc/Writers/Haddock.hs index caa4b9031..9ed3be6cf 100644 --- a/src/Text/Pandoc/Writers/Haddock.hs +++ b/src/Text/Pandoc/Writers/Haddock.hs @@ -2,7 +2,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} {- -Copyright (C) 2014-2015, 2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2014-2015, 2017-2018 John MacFarlane <jgm@berkeley.edu> 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 diff --git a/src/Text/Pandoc/Writers/ICML.hs b/src/Text/Pandoc/Writers/ICML.hs index ba274fb59..80d2fcbef 100644 --- a/src/Text/Pandoc/Writers/ICML.hs +++ b/src/Text/Pandoc/Writers/ICML.hs @@ -4,7 +4,7 @@ {- | Module : Text.Pandoc.Writers.ICML - Copyright : Copyright (C) 2013-2017 github.com/mb21 + Copyright : Copyright (C) 2013-2018 github.com/mb21 License : GNU GPL, version 2 or above Stability : alpha diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs index e9e380a6c..639961acd 100644 --- a/src/Text/Pandoc/Writers/JATS.hs +++ b/src/Text/Pandoc/Writers/JATS.hs @@ -1,7 +1,7 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | Module : Text.Pandoc.Writers.JATS - Copyright : Copyright (C) 2017 John MacFarlane + Copyright : Copyright (C) 2017-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 87ce65586..296b30ee1 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -2,7 +2,7 @@ {-# LANGUAGE PatternGuards #-} {-# LANGUAGE ScopedTypeVariables #-} {- -Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.LaTeX - Copyright : Copyright (C) 2006-2017 John MacFarlane + Copyright : Copyright (C) 2006-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/Man.hs b/src/Text/Pandoc/Writers/Man.hs index 34b5c0ece..c1427b15c 100644 --- a/src/Text/Pandoc/Writers/Man.hs +++ b/src/Text/Pandoc/Writers/Man.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2007-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2007-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Man - Copyright : Copyright (C) 2007-2017 John MacFarlane + Copyright : Copyright (C) 2007-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index 13572c466..08dff2c4e 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -3,7 +3,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} {- -Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Markdown - Copyright : Copyright (C) 2006-2017 John MacFarlane + Copyright : Copyright (C) 2006-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/MediaWiki.hs b/src/Text/Pandoc/Writers/MediaWiki.hs index 839f16cea..2470d9200 100644 --- a/src/Text/Pandoc/Writers/MediaWiki.hs +++ b/src/Text/Pandoc/Writers/MediaWiki.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2008-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2008-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.MediaWiki - Copyright : Copyright (C) 2008-2017 John MacFarlane + Copyright : Copyright (C) 2008-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/Ms.hs b/src/Text/Pandoc/Writers/Ms.hs index 30633cec6..83d80cd4a 100644 --- a/src/Text/Pandoc/Writers/Ms.hs +++ b/src/Text/Pandoc/Writers/Ms.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2007-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2007-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Ms - Copyright : Copyright (C) 2007-2017 John MacFarlane + Copyright : Copyright (C) 2007-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index ff393e574..163cb2dda 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2017 Alexander Krotov <ilabdsf@gmail.com> +Copyright (C) 2017-2018 Alexander Krotov <ilabdsf@gmail.com> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Muse - Copyright : Copyright (C) 2017 Alexander Krotov + Copyright : Copyright (C) 2017-2018 Alexander Krotov License : GNU GPL, version 2 or above Maintainer : Alexander Krotov <ilabdsf@gmail.com> diff --git a/src/Text/Pandoc/Writers/Native.hs b/src/Text/Pandoc/Writers/Native.hs index 1fb685985..f852bad96 100644 --- a/src/Text/Pandoc/Writers/Native.hs +++ b/src/Text/Pandoc/Writers/Native.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Native - Copyright : Copyright (C) 2006-2017 John MacFarlane + Copyright : Copyright (C) 2006-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/ODT.hs b/src/Text/Pandoc/Writers/ODT.hs index 08b4206e3..63a3f915a 100644 --- a/src/Text/Pandoc/Writers/ODT.hs +++ b/src/Text/Pandoc/Writers/ODT.hs @@ -1,6 +1,6 @@ {-# LANGUAGE ScopedTypeVariables #-} {- -Copyright (C) 2008-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2008-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.ODT - Copyright : Copyright (C) 2008-2017 John MacFarlane + Copyright : Copyright (C) 2008-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/OPML.hs b/src/Text/Pandoc/Writers/OPML.hs index 3a2467c65..29e1bc80c 100644 --- a/src/Text/Pandoc/Writers/OPML.hs +++ b/src/Text/Pandoc/Writers/OPML.hs @@ -1,6 +1,6 @@ {-# LANGUAGE CPP #-} {- -Copyright (C) 2013-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2013-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.OPML - Copyright : Copyright (C) 2013-2017 John MacFarlane + Copyright : Copyright (C) 2013-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index dc7d14d05..e0097f507 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -2,7 +2,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternGuards #-} {- -Copyright (C) 2008-2017 Andrea Rossato <andrea.rossato@ing.unitn.it> +Copyright (C) 2008-2018 Andrea Rossato <andrea.rossato@ing.unitn.it> and John MacFarlane. This program is free software; you can redistribute it and/or modify @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.OpenDocument - Copyright : Copyright (C) 2008-2017 Andrea Rossato and John MacFarlane + Copyright : Copyright (C) 2008-2018 Andrea Rossato and John MacFarlane License : GNU GPL, version 2 or above Maintainer : Andrea Rossato <andrea.rossato@ing.unitn.it> diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index 43b5b59ee..72def8e48 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -1,8 +1,8 @@ {-# LANGUAGE OverloadedStrings #-} {- Copyright (C) 2010-2015 Puneeth Chaganti <punchagan@gmail.com> - 2010-2017 John MacFarlane <jgm@berkeley.edu> - 2016-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> + 2010-2018 John MacFarlane <jgm@berkeley.edu> + 2016-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 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 @@ -22,8 +22,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Org Copyright : © 2010-2015 Puneeth Chaganti <punchagan@gmail.com> - 2010-2017 John MacFarlane <jgm@berkeley.edu> - 2016-2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> + 2010-2018 John MacFarlane <jgm@berkeley.edu> + 2016-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index 9466b1570..1de4dcb18 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -1,7 +1,7 @@ {-# LANGUAGE PatternGuards, MultiWayIf, OverloadedStrings #-} {- -Copyright (C) 2017 Jesse Rosenthal <jrosenthal@jhu.edu> +Copyright (C) 2017-2018 Jesse Rosenthal <jrosenthal@jhu.edu> 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 @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Powerpoint - Copyright : Copyright (C) 2017 Jesse Rosenthal + Copyright : Copyright (C) 2017-2018 Jesse Rosenthal License : GNU GPL, version 2 or above Maintainer : Jesse Rosenthal <jrosenthal@jhu.edu> diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index 515276985..2b28dccf0 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.RST - Copyright : Copyright (C) 2006-2017 John MacFarlane + Copyright : Copyright (C) 2006-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/RTF.hs b/src/Text/Pandoc/Writers/RTF.hs index 955b3f7f1..790bebc01 100644 --- a/src/Text/Pandoc/Writers/RTF.hs +++ b/src/Text/Pandoc/Writers/RTF.hs @@ -1,6 +1,6 @@ {-# LANGUAGE ScopedTypeVariables #-} {- -Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.RTF - Copyright : Copyright (C) 2006-2017 John MacFarlane + Copyright : Copyright (C) 2006-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index 83280fa5c..713e4289e 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2013-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2013-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Shared - Copyright : Copyright (C) 2013-2017 John MacFarlane + Copyright : Copyright (C) 2013-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/TEI.hs b/src/Text/Pandoc/Writers/TEI.hs index 8e9d155fa..907e2af24 100644 --- a/src/Text/Pandoc/Writers/TEI.hs +++ b/src/Text/Pandoc/Writers/TEI.hs @@ -1,7 +1,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternGuards #-} {- -Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Docbook - Copyright : Copyright (C) 2006-2017 John MacFarlane + Copyright : Copyright (C) 2006-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/Texinfo.hs b/src/Text/Pandoc/Writers/Texinfo.hs index 15dd2e3d9..b5d72aa56 100644 --- a/src/Text/Pandoc/Writers/Texinfo.hs +++ b/src/Text/Pandoc/Writers/Texinfo.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {- -Copyright (C) 2008-2017 John MacFarlane +Copyright (C) 2008-2018 John MacFarlane 2012 Peter Wang This program is free software; you can redistribute it and/or modify @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Texinfo - Copyright : Copyright (C) 2008-2017 John MacFarlane + Copyright : Copyright (C) 2008-2018 John MacFarlane 2012 Peter Wang License : GNU GPL, version 2 or above diff --git a/src/Text/Pandoc/Writers/Textile.hs b/src/Text/Pandoc/Writers/Textile.hs index 11fb2ae12..f46eb43bc 100644 --- a/src/Text/Pandoc/Writers/Textile.hs +++ b/src/Text/Pandoc/Writers/Textile.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2010-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2010-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.Textile - Copyright : Copyright (C) 2010-2017 John MacFarlane + Copyright : Copyright (C) 2010-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> diff --git a/src/Text/Pandoc/Writers/ZimWiki.hs b/src/Text/Pandoc/Writers/ZimWiki.hs index 30317db73..dec1f9d4a 100644 --- a/src/Text/Pandoc/Writers/ZimWiki.hs +++ b/src/Text/Pandoc/Writers/ZimWiki.hs @@ -1,6 +1,6 @@ {- -Copyright (C) 2008-2017 John MacFarlane <jgm@berkeley.edu> - 2017 Alex Ivkin +Copyright (C) 2008-2018 John MacFarlane <jgm@berkeley.edu> + 2017-2018 Alex Ivkin 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 @@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.Writers.ZimWiki - Copyright : Copyright (C) 2008-2017 John MacFarlane, 2017 Alex Ivkin + Copyright : Copyright (C) 2008-2018 John MacFarlane, 2017-2018 Alex Ivkin License : GNU GPL, version 2 or above Maintainer : Alex Ivkin <alex@ivkin.net> diff --git a/src/Text/Pandoc/XML.hs b/src/Text/Pandoc/XML.hs index 67608fb43..62874f0b9 100644 --- a/src/Text/Pandoc/XML.hs +++ b/src/Text/Pandoc/XML.hs @@ -1,5 +1,5 @@ {- -Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> +Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu> 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 @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {- | Module : Text.Pandoc.XML - Copyright : Copyright (C) 2006-2017 John MacFarlane + Copyright : Copyright (C) 2006-2018 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane <jgm@berkeley.edu> -- cgit v1.2.3 From 3a22907306992f2dd1b6bcb548633734c0c9a1b1 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 6 Jan 2018 00:03:59 -0800 Subject: Don't use `missingIncludes` unless custom syntax defs have been given. This avoids a huge performance sink that comes from evaluating all the elements of the default syntax map. Better just to have run-time errors for missing includes? See #4226. --- src/Text/Pandoc/App.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index c759df46c..fc0a911ea 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -268,7 +268,8 @@ convertWithOpts opts = do syntaxMap <- foldM addSyntaxMap defaultSyntaxMap (optSyntaxDefinitions opts) - case missingIncludes (M.elems syntaxMap) of + unless (null (optSyntaxDefinitions opts)) $ + case missingIncludes (M.elems syntaxMap) of [] -> return () xs -> E.throwIO $ PandocSyntaxMapError $ "Missing syntax definitions:\n" ++ -- cgit v1.2.3 From 043740d32baa9bf5c58409c2a8ace5a196283fa8 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sun, 7 Jan 2018 13:43:03 +0100 Subject: Lua: make pandoc version available as PANDOC_VERSION The current pandoc version is made available to Lua programs in the global PANDOC_VERSION. It contains the version as a list of numbers. --- src/Text/Pandoc/Lua/Init.hs | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/Init.hs b/src/Text/Pandoc/Lua/Init.hs index 25869bf91..5e879114f 100644 --- a/src/Text/Pandoc/Lua/Init.hs +++ b/src/Text/Pandoc/Lua/Init.hs @@ -35,8 +35,10 @@ module Text.Pandoc.Lua.Init import Control.Monad.Trans (MonadIO (..)) import Data.IORef (newIORef, readIORef) +import Data.Version (Version (versionBranch)) import Foreign.Lua (Lua, LuaException (..)) import GHC.IO.Encoding (getForeignEncoding, setForeignEncoding, utf8) +import Paths_pandoc (version) import Text.Pandoc.Class (PandocIO, getCommonState, getUserDataDir, getMediaBag, setMediaBag) import Text.Pandoc.Lua.Packages (LuaPackageParams (..), @@ -75,5 +77,7 @@ initLuaState :: LuaPackageParams -> Lua () initLuaState luaPkgParams = do Lua.openlibs Lua.preloadTextModule "text" + Lua.push (versionBranch version) + Lua.setglobal "PANDOC_VERSION" installPandocPackageSearcher luaPkgParams loadScriptFromDataDir (luaPkgDataDir luaPkgParams) "init.lua" -- cgit v1.2.3 From f5dec4bdc12e5ba895458ba117e0966a78b3c790 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sun, 7 Jan 2018 14:06:34 +0100 Subject: Lua: make pandoc-types version available as PANDOC_API_VERSION The current pandoc-types version is made available to Lua programs in the global PANDOC_API_VERSION. It contains the version as a list of numbers. --- src/Text/Pandoc/Lua/Init.hs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/Init.hs b/src/Text/Pandoc/Lua/Init.hs index 5e879114f..f3ee2caf1 100644 --- a/src/Text/Pandoc/Lua/Init.hs +++ b/src/Text/Pandoc/Lua/Init.hs @@ -41,6 +41,7 @@ import GHC.IO.Encoding (getForeignEncoding, setForeignEncoding, utf8) import Paths_pandoc (version) import Text.Pandoc.Class (PandocIO, getCommonState, getUserDataDir, getMediaBag, setMediaBag) +import Text.Pandoc.Definition (pandocTypesVersion) import Text.Pandoc.Lua.Packages (LuaPackageParams (..), installPandocPackageSearcher) import Text.Pandoc.Lua.Util (loadScriptFromDataDir) @@ -79,5 +80,7 @@ initLuaState luaPkgParams = do Lua.preloadTextModule "text" Lua.push (versionBranch version) Lua.setglobal "PANDOC_VERSION" + Lua.push (versionBranch pandocTypesVersion) + Lua.setglobal "PANDOC_API_VERSION" installPandocPackageSearcher luaPkgParams loadScriptFromDataDir (luaPkgDataDir luaPkgParams) "init.lua" -- cgit v1.2.3 From ae6ba1533bbe79bc82d2b3fc47dc3cde55bf7370 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 7 Jan 2018 18:41:01 -0800 Subject: Use latest skylighting and omit the 'missingincludes' check. If you use a custom syntax definition that refers to a syntax you haven't loaded, pandoc will now complain when it is highlighting the text, rather than at the start. This saves a huge performance hit from the `missingIncludes` check. Closes #4226. --- src/Text/Pandoc/App.hs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index fc0a911ea..ed16b07a5 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -71,8 +71,7 @@ import Data.Aeson.Encode.Pretty (encodePretty', Config(..), keyOrder, defConfig, Indent(..), NumberFormat(..)) import Skylighting (Style, Syntax (..), defaultSyntaxMap, parseTheme, pygments) -import Skylighting.Parser (addSyntaxDefinition, missingIncludes, - parseSyntaxDefinition) +import Skylighting.Parser (addSyntaxDefinition, parseSyntaxDefinition) import System.Console.GetOpt import System.Directory (Permissions (..), doesFileExist, findExecutable, getAppUserDataDirectory, getPermissions) @@ -268,15 +267,6 @@ convertWithOpts opts = do syntaxMap <- foldM addSyntaxMap defaultSyntaxMap (optSyntaxDefinitions opts) - unless (null (optSyntaxDefinitions opts)) $ - case missingIncludes (M.elems syntaxMap) of - [] -> return () - xs -> E.throwIO $ PandocSyntaxMapError $ - "Missing syntax definitions:\n" ++ - unlines (map - (\(syn,dep) -> (T.unpack syn ++ " requires " ++ - T.unpack dep ++ " through IncludeRules.")) xs) - -- We don't want to send output to the terminal if the user -- does 'pandoc -t docx input.txt'; though we allow them to -- force this with '-o -'. On posix systems, we detect -- cgit v1.2.3 From e3f01235e9966d662bb956dbdc66c0f64c759d0a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 9 Jan 2018 15:29:27 -0800 Subject: HTML writer: Fixed footnote backlinks with --id-prefix. Closes #4235. --- src/Text/Pandoc/Writers/HTML.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index e81338550..5d5c88dd9 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -1150,7 +1150,7 @@ blockListToNote :: PandocMonad m => WriterOptions -> String -> [Block] -> StateT 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. - let backlink = [Link ("",["footnote-back"],[]) [Str "↩"] ("#" ++ writerIdentifierPrefix opts ++ "fnref" ++ ref,[])] + let backlink = [Link ("",["footnote-back"],[]) [Str "↩"] ("#" ++ "fnref" ++ ref,[])] blocks' = if null blocks then [] else let lastBlock = last blocks -- cgit v1.2.3 From 6b40b8c27c71231607e64221a071d3a2d366dce3 Mon Sep 17 00:00:00 2001 From: newmana <andrewfnewman@gmail.com> Date: Wed, 10 Jan 2018 13:58:35 +1000 Subject: Add header and footer parameters --- src/Text/Pandoc/PDF.hs | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs index 974934763..5f41d6c55 100644 --- a/src/Text/Pandoc/PDF.hs +++ b/src/Text/Pandoc/PDF.hs @@ -104,6 +104,10 @@ makePDF "wkhtmltopdf" pdfargs writer opts doc@(Pandoc meta _) = do (getField "margin-right" meta')) ,("margin-left", fromMaybe (Just "1.25in") (getField "margin-left" meta')) + ,("footer-html", fromMaybe Nothing + (getField "footer-html" meta')) + ,("header-html", fromMaybe Nothing + (getField "header-html" meta')) ] source <- writer opts doc verbosity <- getVerbosity -- cgit v1.2.3 From 49007ded7b7a64cf9c875f23e9c59a966e1284c8 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 10 Jan 2018 12:07:33 -0800 Subject: RST reader: better handling for headers with an anchor. Instead of creating a div containing the header, we put the id directly on the header. This way header promotion will work properly. Closes #4240. --- src/Text/Pandoc/Readers/RST.hs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 27ce5fa2d..ba5a24f8f 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -1157,9 +1157,19 @@ anchor = try $ do refs <- referenceNames blanklines b <- block - -- put identifier on next block: let addDiv ref = B.divWith (ref, [], []) - return $ foldr addDiv b refs + let emptySpanWithId id' = Span (id',[],[]) [] + -- put identifier on next block: + case B.toList b of + [Header lev (_,classes,kvs) txt] -> + case reverse refs of + [] -> return b + (r:rs) -> return $ B.singleton $ + Header lev (r,classes,kvs) + (txt ++ map emptySpanWithId rs) + -- we avoid generating divs for headers, + -- because it hides them from promoteHeader, see #4240 + _ -> return $ foldr addDiv b refs headerBlock :: PandocMonad m => RSTParser m [Char] headerBlock = do -- cgit v1.2.3 From c5ba3b8ee32704f1fdf241480ba64950f8ceedd8 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 10 Jan 2018 12:28:42 -0800 Subject: LaTeX reader: fix inconsistent column widths. This fixes a bug whereby column widths for the body were different from widths for the header in some tables. Closes #4238. --- src/Text/Pandoc/Writers/LaTeX.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 296b30ee1..49b39f014 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -749,9 +749,9 @@ tableRowToLaTeX header aligns widths cols = do isSimple [] = True isSimple _ = False -- simple tables have to have simple cells: - let widths' = if not (all isSimple cols) + let widths' = if all (== 0) widths && not (all isSimple cols) then replicate (length aligns) - (0.97 / fromIntegral (length aligns)) + (scaleFactor / fromIntegral (length aligns)) else map (scaleFactor *) widths cells <- mapM (tableCellToLaTeX header) $ zip3 widths' aligns cols return $ hsep (intersperse "&" cells) <> "\\tabularnewline" -- cgit v1.2.3 From 206545c6756f7d4dec4c4761d8e256c2c0dfa33a Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Thu, 11 Jan 2018 16:55:13 -0500 Subject: Powerpoint writer: move curSlideId to environment. It really isn't a moving state, and that can be misleading. --- src/Text/Pandoc/Writers/Powerpoint.hs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index 1de4dcb18..0ff80bdc9 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -108,6 +108,11 @@ data WriterEnv = WriterEnv { envMetadata :: Meta , envSlideHasHeader :: Bool , envInList :: Bool , envInNoteSlide :: Bool + , envCurSlideId :: Int + -- the difference between the number at + -- the end of the slide file name and + -- the rId number + , envSlideIdOffset :: Int } deriving (Show) @@ -124,6 +129,8 @@ instance Default WriterEnv where , envSlideHasHeader = False , envInList = False , envInNoteSlide = False + , envCurSlideId = 1 + , envSlideIdOffset = 1 } data MediaInfo = MediaInfo { mInfoFilePath :: FilePath @@ -134,12 +141,7 @@ data MediaInfo = MediaInfo { mInfoFilePath :: FilePath , mInfoCaption :: Bool } deriving (Show, Eq) -data WriterState = WriterState { stCurSlideId :: Int - -- the difference between the number at - -- the end of the slide file name and - -- the rId number - , stSlideIdOffset :: Int - , stLinkIds :: M.Map Int (M.Map Int (URL, String)) +data WriterState = WriterState { stLinkIds :: M.Map Int (M.Map Int (URL, String)) -- (FP, Local ID, Global ID, Maybe Mime) , stMediaIds :: M.Map Int [MediaInfo] , stMediaGlobalIds :: M.Map FilePath Int @@ -147,9 +149,7 @@ data WriterState = WriterState { stCurSlideId :: Int } deriving (Show, Eq) instance Default WriterState where - def = WriterState { stCurSlideId = 0 - , stSlideIdOffset = 1 - , stLinkIds = mempty + def = WriterState { stLinkIds = mempty , stMediaIds = mempty , stMediaGlobalIds = mempty , stNoteIds = mempty @@ -841,7 +841,7 @@ replaceNamedChildren ns prefix name newKids element = registerLink :: PandocMonad m => (URL, String) -> P m Int registerLink link = do - curSlideId <- gets stCurSlideId + curSlideId <- asks envCurSlideId linkReg <- gets stLinkIds mediaReg <- gets stMediaIds let maxLinkId = case M.lookup curSlideId linkReg of @@ -862,7 +862,7 @@ registerLink link = do registerMedia :: PandocMonad m => FilePath -> [ParaElem] -> P m MediaInfo registerMedia fp caption = do - curSlideId <- gets stCurSlideId + curSlideId <- asks envCurSlideId linkReg <- gets stLinkIds mediaReg <- gets stMediaIds globalIds <- gets stMediaGlobalIds @@ -1468,7 +1468,7 @@ slideToFilePath _ idNum = "slide" ++ (show $ idNum) ++ ".xml" slideToSlideId :: Monad m => Slide -> Int -> P m String slideToSlideId _ idNum = do - n <- gets stSlideIdOffset + n <- asks envSlideIdOffset return $ "rId" ++ (show $ idNum + n) @@ -1492,7 +1492,7 @@ elementToRel element slideToPresRel :: Monad m => Slide -> Int -> P m Relationship slideToPresRel slide idNum = do - n <- gets stSlideIdOffset + n <- asks envSlideIdOffset let rId = idNum + n fp = "slides/" ++ slideToFilePath slide idNum return $ Relationship { relId = rId @@ -1559,9 +1559,9 @@ elemToEntry fp element = do slideToEntry :: PandocMonad m => Slide -> Int -> P m Entry slideToEntry slide idNum = do - modify $ \st -> st{stCurSlideId = idNum} - element <- slideToElement slide - elemToEntry ("ppt/slides/" ++ slideToFilePath slide idNum) element + local (\env -> env{envCurSlideId = idNum}) $ do + element <- slideToElement slide + elemToEntry ("ppt/slides/" ++ slideToFilePath slide idNum) element slideToSlideRelEntry :: PandocMonad m => Slide -> Int -> P m Entry slideToSlideRelEntry slide idNum = do -- cgit v1.2.3 From da72d0f412559fa0ee719329e3de61d387a31ceb Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Thu, 11 Jan 2018 17:05:04 -0500 Subject: Powerpoint writer: Make the slide number available to the blocks. For anchors, block-processing functions need to know what slide number they're in. We make the envCurSlideId available to blocks. --- src/Text/Pandoc/Writers/Powerpoint.hs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index 0ff80bdc9..1509f967f 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -146,6 +146,8 @@ data WriterState = WriterState { stLinkIds :: M.Map Int (M.Map Int (URL, String) , stMediaIds :: M.Map Int [MediaInfo] , stMediaGlobalIds :: M.Map FilePath Int , stNoteIds :: M.Map Int [Block] + -- anchors in the current slide + , stCurSlideAnchors :: M.Map String Int } deriving (Show, Eq) instance Default WriterState where @@ -153,6 +155,7 @@ instance Default WriterState where , stMediaIds = mempty , stMediaGlobalIds = mempty , stNoteIds = mempty + , stCurSlideAnchors = mempty } type P m = ReaderT WriterEnv (StateT WriterState m) @@ -606,6 +609,8 @@ blocksToSlide blks = do slideLevel <- asks envSlideLevel blocksToSlide' slideLevel blks + + makeNoteEntry :: Int -> [Block] -> [Block] makeNoteEntry n blks = let enum = Str (show n ++ ".") @@ -662,8 +667,11 @@ getMetaSlide = do blocksToPresentation :: PandocMonad m => [Block] -> P m Presentation blocksToPresentation blks = do blksLst <- splitBlocks blks - slides <- mapM blocksToSlide blksLst - noteSlides <- makeNotesSlides + slides <- mapM + (\(bs, n) -> local (\st -> st{envCurSlideId = n}) (blocksToSlide bs)) + (zip blksLst [1..]) + let noteSlidesNum = length blksLst + 1 + noteSlides <- local (\st -> st {envCurSlideId = noteSlidesNum}) makeNotesSlides let slides' = slides ++ noteSlides metadataslide <- getMetaSlide presSize <- asks envPresentationSize -- cgit v1.2.3 From 2afca42f777c1e15843f8895c0d3b959f9320f11 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Fri, 12 Jan 2018 06:31:53 -0500 Subject: Powerpoint writer: Add anchor links For anchor-type links (`[foo](#bar)`) we produce an anchor link. In powerpoint these are links to slides, so we keep track of a map relating anchors to the slides they occur on. --- src/Text/Pandoc/Writers/Powerpoint.hs | 67 ++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 20 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index 1509f967f..990d90433 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -146,8 +146,8 @@ data WriterState = WriterState { stLinkIds :: M.Map Int (M.Map Int (URL, String) , stMediaIds :: M.Map Int [MediaInfo] , stMediaGlobalIds :: M.Map FilePath Int , stNoteIds :: M.Map Int [Block] - -- anchors in the current slide - , stCurSlideAnchors :: M.Map String Int + -- associate anchors with slide id + , stAnchorMap :: M.Map String Int } deriving (Show, Eq) instance Default WriterState where @@ -155,7 +155,7 @@ instance Default WriterState where , stMediaIds = mempty , stMediaGlobalIds = mempty , stNoteIds = mempty - , stCurSlideAnchors = mempty + , stAnchorMap= mempty } type P m = ReaderT WriterEnv (StateT WriterState m) @@ -377,6 +377,13 @@ isListType (BulletList _) = True isListType (DefinitionList _) = True isListType _ = False +registerAnchorId :: PandocMonad m => String -> P m () +registerAnchorId anchor = do + anchorMap <- gets stAnchorMap + slideId <- asks envCurSlideId + unless (null anchor) $ + modify $ \st -> st {stAnchorMap = M.insert anchor slideId anchorMap} + blockToParagraphs :: PandocMonad m => Block -> P m [Paragraph] blockToParagraphs (Plain ils) = do parElems <- inlinesToParElems ils @@ -407,7 +414,11 @@ blockToParagraphs (BlockQuote blks) = concatMapM blockToParagraphs blks -- TODO: work out the format blockToParagraphs (RawBlock _ _) = return [] -blockToParagraphs (Header n _ ils) = do +blockToParagraphs (Header n (ident, _, _) ils) = do + -- Note that this function will only touch headers that are not at + -- the beginning of slides -- all the rest will be taken care of by + -- `blocksToSlide'`. We have the register anchors in both of them. + registerAnchorId ident slideLevel <- asks envSlideLevel parElems <- inlinesToParElems ils -- For the time being we're not doing headers inside of bullets, but @@ -564,11 +575,13 @@ splitBlocks :: Monad m => [Block] -> P m [[Block]] splitBlocks = splitBlocks' [] [] blocksToSlide' :: PandocMonad m => Int -> [Block] -> P m Slide -blocksToSlide' lvl ((Header n _ ils) : blks) +blocksToSlide' lvl ((Header n (ident, _, _) ils) : blks) | n < lvl = do + registerAnchorId ident hdr <- inlinesToParElems ils return $ TitleSlide {titleSlideHeader = hdr} | n == lvl = do + registerAnchorId ident hdr <- inlinesToParElems ils -- Now get the slide without the header, and then add the header -- in. @@ -1141,11 +1154,15 @@ paraElemToElement (Run rpr s) = do Nothing -> []) ++ [] linkProps <- case rLink rpr of - Just link -> do idNum <- registerLink link - return [mknode "a:hlinkClick" - [("r:id", "rId" ++ show idNum)] - () - ] + Just link -> do + idNum <- registerLink link + let (url, _) = link + linkAttrs = [("r:id", "rId" ++ show idNum)] + -- we have to add an extra action if it's an anchor. + linkAttrs' = linkAttrs ++ case url of + '#' : _ -> [("action", "ppaction://hlinksldjump")] + _ -> [] + return [mknode "a:hlinkClick" linkAttrs' ()] Nothing -> return [] let propContents = if rPropCode rpr then [mknode "a:latin" [("typeface", "Courier")] ()] @@ -1576,16 +1593,26 @@ slideToSlideRelEntry slide idNum = do element <- slideToSlideRelElement slide idNum elemToEntry ("ppt/slides/_rels/" ++ slideToFilePath slide idNum ++ ".rels") element -linkRelElement :: Int -> (URL, String) -> Element -linkRelElement idNum (url, _) = - mknode "Relationship" [ ("Id", "rId" ++ show idNum) - , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink") +linkRelElement :: PandocMonad m => Int -> (URL, String) -> P m Element +linkRelElement idNum (url, _) = do + anchorMap <- gets stAnchorMap + case url of + '#' : anchor | Just num <- M.lookup anchor anchorMap -> + return $ + mknode "Relationship" [ ("Id", "rId" ++ show idNum) + , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide") + , ("Target", "slide" ++ show num ++ ".xml") + ] () + _ -> + return $ + mknode "Relationship" [ ("Id", "rId" ++ show idNum) + , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink") , ("Target", url) , ("TargetMode", "External") ] () -linkRelElements :: M.Map Int (URL, String) -> [Element] -linkRelElements mp = map (\(n, lnk) -> linkRelElement n lnk) (M.toList mp) +linkRelElements :: PandocMonad m => M.Map Int (URL, String) -> P m [Element] +linkRelElements mp = mapM (\(n, lnk) -> linkRelElement n lnk) (M.toList mp) mediaRelElement :: MediaInfo -> Element mediaRelElement mInfo = @@ -1609,10 +1636,10 @@ slideToSlideRelElement slide idNum = do linkIds <- gets stLinkIds mediaIds <- gets stMediaIds - let linkRels = case M.lookup idNum linkIds of - Just mp -> linkRelElements mp - Nothing -> [] - mediaRels = case M.lookup idNum mediaIds of + linkRels <- case M.lookup idNum linkIds of + Just mp -> linkRelElements mp + Nothing -> return [] + let mediaRels = case M.lookup idNum mediaIds of Just mInfos -> map mediaRelElement mInfos Nothing -> [] -- cgit v1.2.3 From 0b66b5652393673fe0b49581e7afdd822020071c Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Fri, 12 Jan 2018 09:24:15 -0500 Subject: Powerpoint writer: Clean up adding metadata slide We want to count the slide numbers correctly if it's in there. --- src/Text/Pandoc/Writers/Powerpoint.hs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index 990d90433..5a1d089a9 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -679,18 +679,20 @@ getMetaSlide = do blocksToPresentation :: PandocMonad m => [Block] -> P m Presentation blocksToPresentation blks = do + metadataslide <- getMetaSlide + let bodyStartNum = case metadataslide of + Just _ -> 2 + Nothing -> 1 blksLst <- splitBlocks blks slides <- mapM (\(bs, n) -> local (\st -> st{envCurSlideId = n}) (blocksToSlide bs)) - (zip blksLst [1..]) + (zip blksLst [bodyStartNum..]) let noteSlidesNum = length blksLst + 1 noteSlides <- local (\st -> st {envCurSlideId = noteSlidesNum}) makeNotesSlides - let slides' = slides ++ noteSlides - metadataslide <- getMetaSlide presSize <- asks envPresentationSize - return $ case metadataslide of - Just metadataslide' -> Presentation presSize $ metadataslide' : slides' - Nothing -> Presentation presSize slides' + return $ + Presentation presSize $ + (maybeToList metadataslide) ++ slides ++ noteSlides -------------------------------------------------------------------- -- cgit v1.2.3 From 53c48dd2c9417916bfaa15be15fad992c9659af9 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Fri, 12 Jan 2018 09:45:01 -0500 Subject: Powerpoint writer: Ignore internal links without targets. If the user entered an internal link without a corresponding anchor, it would produce a corrupted file. Now we check the anchor map, and make sure the target is in the file. If it isn't, we ignore it. --- src/Text/Pandoc/Writers/Powerpoint.hs | 43 +++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 12 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index 5a1d089a9..7b73d0ecb 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -57,7 +57,7 @@ import Text.Pandoc.Walk import Text.Pandoc.Writers.Shared (fixDisplayMath) import Text.Pandoc.Writers.OOXML import qualified Data.Map as M -import Data.Maybe (mapMaybe, listToMaybe, maybeToList) +import Data.Maybe (mapMaybe, listToMaybe, maybeToList, catMaybes) import Text.Pandoc.ImageSize import Control.Applicative ((<|>)) @@ -1158,13 +1158,27 @@ paraElemToElement (Run rpr s) = do linkProps <- case rLink rpr of Just link -> do idNum <- registerLink link - let (url, _) = link - linkAttrs = [("r:id", "rId" ++ show idNum)] - -- we have to add an extra action if it's an anchor. - linkAttrs' = linkAttrs ++ case url of - '#' : _ -> [("action", "ppaction://hlinksldjump")] - _ -> [] - return [mknode "a:hlinkClick" linkAttrs' ()] + -- first we have to make sure that if it's an + -- anchor, it's in the anchor map. If not, there's + -- no link. + anchorMap <- gets stAnchorMap + return $ case link of + -- anchor with nothing in the map + ('#':target, _) | Nothing <- M.lookup target anchorMap -> + [] + -- anchor that is in the map + ('#':_, _) -> + let linkAttrs = + [ ("r:id", "rId" ++ show idNum) + , ("action", "ppaction://hlinksldjump") + ] + in [mknode "a:hlinkClick" linkAttrs ()] + -- external + _ -> + let linkAttrs = + [ ("r:id", "rId" ++ show idNum) + ] + in [mknode "a:hlinkClick" linkAttrs ()] Nothing -> return [] let propContents = if rPropCode rpr then [mknode "a:latin" [("typeface", "Courier")] ()] @@ -1595,18 +1609,23 @@ slideToSlideRelEntry slide idNum = do element <- slideToSlideRelElement slide idNum elemToEntry ("ppt/slides/_rels/" ++ slideToFilePath slide idNum ++ ".rels") element -linkRelElement :: PandocMonad m => Int -> (URL, String) -> P m Element +linkRelElement :: PandocMonad m => Int -> (URL, String) -> P m (Maybe Element) linkRelElement idNum (url, _) = do anchorMap <- gets stAnchorMap case url of + -- if it's an anchor in the map, we use the slide number for an + -- internal link. '#' : anchor | Just num <- M.lookup anchor anchorMap -> - return $ + return $ Just $ mknode "Relationship" [ ("Id", "rId" ++ show idNum) , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide") , ("Target", "slide" ++ show num ++ ".xml") ] () + -- if it's an anchor not in the map, we return nothing. + '#' : _ -> return Nothing + -- Anything else we treat as an external link _ -> - return $ + return $ Just $ mknode "Relationship" [ ("Id", "rId" ++ show idNum) , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink") , ("Target", url) @@ -1614,7 +1633,7 @@ linkRelElement idNum (url, _) = do ] () linkRelElements :: PandocMonad m => M.Map Int (URL, String) -> P m [Element] -linkRelElements mp = mapM (\(n, lnk) -> linkRelElement n lnk) (M.toList mp) +linkRelElements mp = catMaybes <$> mapM (\(n, lnk) -> linkRelElement n lnk) (M.toList mp) mediaRelElement :: MediaInfo -> Element mediaRelElement mInfo = -- cgit v1.2.3 From 4ce07c20d7f06da3519fa601b9d3df94a16d507e Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Fri, 12 Jan 2018 10:00:59 -0500 Subject: Powerpoint writer: Set notes slide number correctly Previously, this hadn't been aware of a metadata slide. We also clarify the logic for setting the startnumber of different slide sections correctly. --- src/Text/Pandoc/Writers/Powerpoint.hs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index 7b73d0ecb..ef9bfedff 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -679,20 +679,18 @@ getMetaSlide = do blocksToPresentation :: PandocMonad m => [Block] -> P m Presentation blocksToPresentation blks = do - metadataslide <- getMetaSlide - let bodyStartNum = case metadataslide of - Just _ -> 2 - Nothing -> 1 + metadataslides <- maybeToList <$> getMetaSlide + let bodyStartNum = length metadataslides + 1 blksLst <- splitBlocks blks - slides <- mapM - (\(bs, n) -> local (\st -> st{envCurSlideId = n}) (blocksToSlide bs)) - (zip blksLst [bodyStartNum..]) - let noteSlidesNum = length blksLst + 1 - noteSlides <- local (\st -> st {envCurSlideId = noteSlidesNum}) makeNotesSlides + bodyslides <- mapM + (\(bs, n) -> local (\st -> st{envCurSlideId = n}) (blocksToSlide bs)) + (zip blksLst [bodyStartNum..]) + let noteStartNum = bodyStartNum + length bodyslides + noteSlides <- local (\st -> st {envCurSlideId = noteStartNum}) makeNotesSlides presSize <- asks envPresentationSize return $ Presentation presSize $ - (maybeToList metadataslide) ++ slides ++ noteSlides + metadataslides ++ bodyslides ++ noteSlides -------------------------------------------------------------------- -- cgit v1.2.3 From 021e5ac89d4423e844a741801d6dc59a3edafa51 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Fri, 12 Jan 2018 10:43:02 -0500 Subject: Powerpoint writer: Add table of contents This is triggered by the `--toc` flag. Note that in a long slide deck this risks overrunning the text box. The user can address this by setting `--toc-depth=1`. --- src/Text/Pandoc/Writers/Powerpoint.hs | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index ef9bfedff..c3f743c5f 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -54,6 +54,7 @@ import Text.Pandoc.MIME import Text.Pandoc.Logging import qualified Data.ByteString.Lazy as BL import Text.Pandoc.Walk +import qualified Text.Pandoc.Shared as Shared -- so we don't overlap "Element" import Text.Pandoc.Writers.Shared (fixDisplayMath) import Text.Pandoc.Writers.OOXML import qualified Data.Map as M @@ -677,10 +678,40 @@ getMetaSlide = do , metadataSlideDate = date } +-- adapted from the markdown writer +elementToListItem :: PandocMonad m => Shared.Element -> P m [Block] +elementToListItem (Shared.Sec lev _nums (ident,_,_) headerText subsecs) = do + opts <- asks envOpts + let headerLink = if null ident + then walk Shared.deNote headerText + else [Link nullAttr (walk Shared.deNote headerText) + ('#':ident, "")] + listContents <- if null subsecs || lev >= writerTOCDepth opts + then return [] + else mapM elementToListItem subsecs + return [Plain headerLink, BulletList listContents] +elementToListItem (Shared.Blk _) = return [] + +makeTOCSlide :: PandocMonad m => [Block] -> P m Slide +makeTOCSlide blks = do + contents <- BulletList <$> mapM elementToListItem (Shared.hierarchicalize blks) + slideLevel <- asks envSlideLevel + let tocTitle = [Str "Table of Contents"] + hdr = Header slideLevel nullAttr tocTitle + sld <- blocksToSlide [hdr, contents] + return sld + blocksToPresentation :: PandocMonad m => [Block] -> P m Presentation blocksToPresentation blks = do + opts <- asks envOpts + let metadataStartNum = 1 metadataslides <- maybeToList <$> getMetaSlide - let bodyStartNum = length metadataslides + 1 + let tocStartNum = metadataStartNum + length metadataslides + tocSlides <- if writerTableOfContents opts + then do toc <- makeTOCSlide blks + return [toc] + else return [] + let bodyStartNum = tocStartNum + length tocSlides blksLst <- splitBlocks blks bodyslides <- mapM (\(bs, n) -> local (\st -> st{envCurSlideId = n}) (blocksToSlide bs)) @@ -690,7 +721,7 @@ blocksToPresentation blks = do presSize <- asks envPresentationSize return $ Presentation presSize $ - metadataslides ++ bodyslides ++ noteSlides + metadataslides ++ tocSlides ++ bodyslides ++ noteSlides -------------------------------------------------------------------- -- cgit v1.2.3 From 1d9c2770e3025b1b08463577eac499eb475e5bcf Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Fri, 12 Jan 2018 10:48:28 -0500 Subject: Powerpoint writer: Set notes slide header with slide-level It used to be hardcoded to 2. This will set it to the appropriate slide-level. --- src/Text/Pandoc/Writers/Powerpoint.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index c3f743c5f..e56663ae0 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -648,9 +648,10 @@ forceFontSize px x = do makeNotesSlides :: PandocMonad m => P m [Slide] makeNotesSlides = local (\env -> env{envInNoteSlide=True}) $ do noteIds <- gets stNoteIds + slideLevel <- asks envSlideLevel if M.null noteIds then return [] - else do let hdr = Header 2 nullAttr [Str "Notes"] + else do let hdr = Header slideLevel nullAttr [Str "Notes"] blks <- return $ concatMap (\(n, bs) -> makeNoteEntry n bs) $ M.toList noteIds -- cgit v1.2.3 From 6d74b357511a3d84eb27d18dff51383cfa869cb5 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Fri, 12 Jan 2018 11:06:03 -0500 Subject: Move `metaValueToInlines` to T.P.W.Shared This will allow the Powerpoint writer to use it as well. --- src/Text/Pandoc/Writers/Docx.hs | 12 +----------- src/Text/Pandoc/Writers/Shared.hs | 9 +++++++++ 2 files changed, 10 insertions(+), 11 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index c077d54ba..adf5f232a 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -1,4 +1,3 @@ - {-# LANGUAGE PatternGuards #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} @@ -65,7 +64,7 @@ import Text.Pandoc.Readers.Docx.StyleMap import Text.Pandoc.Shared hiding (Element) import Text.Pandoc.Walk import Text.Pandoc.Writers.Math -import Text.Pandoc.Writers.Shared (fixDisplayMath) +import Text.Pandoc.Writers.Shared (fixDisplayMath, metaValueToInlines) import Text.Printf (printf) import Text.TeXMath import Text.XML.Light as XML @@ -196,15 +195,6 @@ isValidChar (ord -> c) | 0x10000 <= c && c <= 0x10FFFF = True | otherwise = False -metaValueToInlines :: MetaValue -> [Inline] -metaValueToInlines (MetaString s) = [Str s] -metaValueToInlines (MetaInlines ils) = ils -metaValueToInlines (MetaBlocks bs) = query return bs -metaValueToInlines (MetaBool b) = [Str $ show b] -metaValueToInlines _ = [] - - - writeDocx :: (PandocMonad m) => WriterOptions -- ^ Writer options -> Pandoc -- ^ Document to convert diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs index 713e4289e..ae4cc5cc5 100644 --- a/src/Text/Pandoc/Writers/Shared.hs +++ b/src/Text/Pandoc/Writers/Shared.hs @@ -40,6 +40,7 @@ module Text.Pandoc.Writers.Shared ( , fixDisplayMath , unsmartify , gridTable + , metaValueToInlines ) where import Control.Monad (zipWithM) @@ -55,6 +56,7 @@ import qualified Text.Pandoc.Builder as Builder import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Pretty +import Text.Pandoc.Walk (query) import Text.Pandoc.UTF8 (toStringLazy) import Text.Pandoc.XML (escapeStringForXML) @@ -308,3 +310,10 @@ gridTable opts blocksToDoc headless aligns widths headers rows = do head'' $$ body $$ border '-' (repeat AlignDefault) widthsInChars + +metaValueToInlines :: MetaValue -> [Inline] +metaValueToInlines (MetaString s) = [Str s] +metaValueToInlines (MetaInlines ils) = ils +metaValueToInlines (MetaBlocks bs) = query return bs +metaValueToInlines (MetaBool b) = [Str $ show b] +metaValueToInlines _ = [] -- cgit v1.2.3 From 624abeec5c3b9f5c27cffe6d157617aa97367e92 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Fri, 12 Jan 2018 11:08:46 -0500 Subject: Powerpoint writer: allow setting toc-title in metadata. Accompanying change in MANUAL.txt --- src/Text/Pandoc/Writers/Powerpoint.hs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index e56663ae0..90eb0ffc3 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -55,7 +55,7 @@ import Text.Pandoc.Logging import qualified Data.ByteString.Lazy as BL import Text.Pandoc.Walk import qualified Text.Pandoc.Shared as Shared -- so we don't overlap "Element" -import Text.Pandoc.Writers.Shared (fixDisplayMath) +import Text.Pandoc.Writers.Shared (fixDisplayMath, metaValueToInlines) import Text.Pandoc.Writers.OOXML import qualified Data.Map as M import Data.Maybe (mapMaybe, listToMaybe, maybeToList, catMaybes) @@ -696,8 +696,11 @@ elementToListItem (Shared.Blk _) = return [] makeTOCSlide :: PandocMonad m => [Block] -> P m Slide makeTOCSlide blks = do contents <- BulletList <$> mapM elementToListItem (Shared.hierarchicalize blks) + meta <- asks envMetadata slideLevel <- asks envSlideLevel - let tocTitle = [Str "Table of Contents"] + let tocTitle = case lookupMeta "toc-title" meta of + Just val -> metaValueToInlines val + Nothing -> [Str "Table of Contents"] hdr = Header slideLevel nullAttr tocTitle sld <- blocksToSlide [hdr, contents] return sld -- cgit v1.2.3 From 5b852f8d2ad6e2d9713e894bbb80489c1d383847 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Fri, 12 Jan 2018 08:56:33 +0100 Subject: Lua filters: make PANDOC_READER_OPTIONS available The options which were used to read the document are made available to Lua filters via the `PANDOC_READER_OPTIONS` global. --- src/Text/Pandoc/App.hs | 4 +-- src/Text/Pandoc/Lua.hs | 17 +++++++----- src/Text/Pandoc/Lua/StackInstances.hs | 50 +++++++++++++++++++++++++++++++++-- 3 files changed, 61 insertions(+), 10 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index ed16b07a5..976311e77 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -855,12 +855,12 @@ applyFilter :: ReaderOptions -> Filter -> Pandoc -> PandocIO Pandoc -applyFilter _ropts args (LuaFilter f) d = do +applyFilter ropts args (LuaFilter f) d = do f' <- expandFilterPath f let format = case args of (x:_) -> x _ -> error "Format not supplied for lua filter" - res <- runLuaFilter f' format d + res <- runLuaFilter ropts f' format d case res of Right x -> return x Left (LuaException s) -> E.throw (PandocFilterError f s) diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 48518aa54..edf803b45 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -39,21 +39,22 @@ import Text.Pandoc.Definition (Pandoc) import Text.Pandoc.Lua.Filter (LuaFilter, walkMWithLuaFilter) import Text.Pandoc.Lua.Init (runPandocLua) import Text.Pandoc.Lua.Util (popValue) +import Text.Pandoc.Options (ReaderOptions) import qualified Foreign.Lua as Lua -- | Run the Lua filter in @filterPath@ for a transformation to target -- format @format@. Pandoc uses Lua init files to setup the Lua -- interpreter. -runLuaFilter :: FilePath -> String +runLuaFilter :: ReaderOptions -> FilePath -> String -> Pandoc -> PandocIO (Either LuaException Pandoc) -runLuaFilter filterPath format doc = - runPandocLua (runLuaFilter' filterPath format doc) +runLuaFilter ropts filterPath format doc = + runPandocLua (runLuaFilter' ropts filterPath format doc) -runLuaFilter' :: FilePath -> String +runLuaFilter' :: ReaderOptions -> FilePath -> String -> Pandoc -> Lua Pandoc -runLuaFilter' filterPath format pd = do - -- store module in global "pandoc" +runLuaFilter' ropts filterPath format pd = do registerFormat + registerReaderOptions top <- Lua.gettop stat <- Lua.dofile filterPath if stat /= OK @@ -73,5 +74,9 @@ runLuaFilter' filterPath format pd = do push format Lua.setglobal "FORMAT" + registerReaderOptions = do + push ropts + Lua.setglobal "PANDOC_READER_OPTIONS" + runAll :: [LuaFilter] -> Pandoc -> Lua Pandoc runAll = foldr ((>=>) . walkMWithLuaFilter) return diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index 531261099..d0289d1ef 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -16,8 +16,9 @@ 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 -} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_GHC -fno-warn-orphans #-} {- | Module : Text.Pandoc.Lua.StackInstances @@ -34,13 +35,18 @@ module Text.Pandoc.Lua.StackInstances () where import Control.Applicative ((<|>)) import Control.Monad (when) +import Data.Data (showConstr, toConstr) +import Data.Foldable (forM_) import Foreign.Lua (FromLuaStack (peek), Lua, LuaInteger, LuaNumber, StackIndex, ToLuaStack (push), Type (..), throwLuaError, tryLua) import Text.Pandoc.Definition +import Text.Pandoc.Extensions (Extensions) import Text.Pandoc.Lua.Util (adjustIndexBy, getTable, pushViaConstructor) +import Text.Pandoc.Options (ReaderOptions (..), TrackChanges) import Text.Pandoc.Shared (Element (Blk, Sec), safeRead) import qualified Foreign.Lua as Lua +import qualified Data.Set as Set import qualified Text.Pandoc.Lua.Util as LuaUtil instance ToLuaStack Pandoc where @@ -332,3 +338,43 @@ instance ToLuaStack Element where Lua.push "__index" Lua.pushvalue (-2) Lua.rawset (-3) + + +-- +-- Reader Options +-- +instance ToLuaStack Extensions where + push exts = push (show exts) + +instance ToLuaStack TrackChanges where + push = push . showConstr . toConstr + +instance ToLuaStack a => ToLuaStack (Set.Set a) where + push set = do + Lua.newtable + forM_ set (`LuaUtil.addValue` True) + +instance ToLuaStack ReaderOptions where + push ro = do + let ReaderOptions + (extensions :: Extensions) + (standalone :: Bool) + (columns :: Int) + (tabStop :: Int) + (indentedCodeClasses :: [String]) + (abbreviations :: Set.Set String) + (defaultImageExtension :: String) + (trackChanges :: TrackChanges) + (stripComments :: Bool) + = ro + Lua.newtable + LuaUtil.addValue "extensions" extensions + LuaUtil.addValue "standalone" standalone + LuaUtil.addValue "columns" columns + LuaUtil.addValue "tabStop" tabStop + LuaUtil.addValue "indentedCodeClasses" indentedCodeClasses + LuaUtil.addValue "abbreviations" abbreviations + LuaUtil.addValue "defaultImageExtension" defaultImageExtension + LuaUtil.addValue "trackChanges" trackChanges + LuaUtil.addValue "stripComments" stripComments + -- cgit v1.2.3 From f130109b90d4f369a6d8d03c7a520e95db2e0d1f Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Fri, 12 Jan 2018 14:27:53 -0500 Subject: Powerpoint writer: Include Notes slide in TOC --- src/Text/Pandoc/Writers/Powerpoint.hs | 61 ++++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 19 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index 90eb0ffc3..af19ec93b 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -638,25 +638,26 @@ forceFontSize px x = do rpr <- asks envRunProps local (\r -> r {envRunProps = rpr{rPropForceSize = Just px}}) x --- Right now, there's no logic for making more than one slide, but I --- want to leave the option open to make multiple slides if we figure --- out how to guess at how much space the text of the notes will take --- up (or if we allow a way for it to be manually controlled). Plus a --- list will make it easier to put together in the final --- `blocksToPresentation` function (since we can just add an empty --- list without checking the state). -makeNotesSlides :: PandocMonad m => P m [Slide] -makeNotesSlides = local (\env -> env{envInNoteSlide=True}) $ do +-- We leave these as blocks because we will want to include them in +-- the TOC. +makeNotesSlideBlocks :: PandocMonad m => P m [Block] +makeNotesSlideBlocks = do noteIds <- gets stNoteIds slideLevel <- asks envSlideLevel + meta <- asks envMetadata + -- Get identifiers so we can give the notes section a unique ident. + anchorSet <- M.keysSet <$> gets stAnchorMap if M.null noteIds then return [] - else do let hdr = Header slideLevel nullAttr [Str "Notes"] + else do let title = case lookupMeta "notes-title" meta of + Just val -> metaValueToInlines val + Nothing -> [Str "Notes"] + ident = Shared.uniqueIdent title anchorSet + hdr = Header slideLevel (ident, [], []) title blks <- return $ concatMap (\(n, bs) -> makeNoteEntry n bs) $ M.toList noteIds - sld <- blocksToSlide $ hdr : blks - return [sld] + return $ hdr : blks getMetaSlide :: PandocMonad m => P m (Maybe Slide) getMetaSlide = do @@ -711,21 +712,43 @@ blocksToPresentation blks = do let metadataStartNum = 1 metadataslides <- maybeToList <$> getMetaSlide let tocStartNum = metadataStartNum + length metadataslides - tocSlides <- if writerTableOfContents opts - then do toc <- makeTOCSlide blks - return [toc] - else return [] - let bodyStartNum = tocStartNum + length tocSlides + -- As far as I can tell, if we want to have a variable-length toc in + -- the future, we'll have to make it twice. Once to get the length, + -- and a second time to include the notes slide. We can't make the + -- notes slide before the body slides because we need to know if + -- there are notes, and we can't make either before the toc slide, + -- because we need to know its length to get slide numbers right. + -- + -- For now, though, since the TOC slide is only length 1, if it + -- exists, we'll just get the length, and then come back to make the + -- slide later + let tocSlidesLength = if writerTableOfContents opts then 1 else 0 + let bodyStartNum = tocStartNum + tocSlidesLength blksLst <- splitBlocks blks bodyslides <- mapM (\(bs, n) -> local (\st -> st{envCurSlideId = n}) (blocksToSlide bs)) (zip blksLst [bodyStartNum..]) let noteStartNum = bodyStartNum + length bodyslides - noteSlides <- local (\st -> st {envCurSlideId = noteStartNum}) makeNotesSlides + notesSlideBlocks <- makeNotesSlideBlocks + -- now we come back and make the real toc... + tocSlides <- if writerTableOfContents opts + then do toc <- makeTOCSlide $ blks ++ notesSlideBlocks + return [toc] + else return [] + -- ... and the notes slide. We test to see if the blocks are empty, + -- because we don't want to make an empty slide. + notesSlides <- if null notesSlideBlocks + then return [] + else do notesSlide <- local + (\env -> env { envCurSlideId = noteStartNum + , envInNoteSlide = True + }) + (blocksToSlide $ notesSlideBlocks) + return [notesSlide] presSize <- asks envPresentationSize return $ Presentation presSize $ - metadataslides ++ tocSlides ++ bodyslides ++ noteSlides + metadataslides ++ tocSlides ++ bodyslides ++ notesSlides -------------------------------------------------------------------- -- cgit v1.2.3 From 6528082401100cd8ef26c8dc3e953b960a997827 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Fri, 12 Jan 2018 21:26:34 +0100 Subject: Lua filters: improve error messages Provide more context about the task which caused an error. --- src/Text/Pandoc/Lua/Filter.hs | 8 ++++++-- src/Text/Pandoc/Lua/StackInstances.hs | 32 +++++++++++++++++++------------- src/Text/Pandoc/Lua/Util.hs | 9 +++++++++ 3 files changed, 34 insertions(+), 15 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/Filter.hs b/src/Text/Pandoc/Lua/Filter.hs index 9e109bb52..cc2b9d47e 100644 --- a/src/Text/Pandoc/Lua/Filter.hs +++ b/src/Text/Pandoc/Lua/Filter.hs @@ -11,6 +11,7 @@ module Text.Pandoc.Lua.Filter ( LuaFilterFunction , inlineElementNames ) where import Control.Monad (mplus, unless, when, (>=>)) +import Control.Monad.Catch (finally) import Text.Pandoc.Definition import Data.Foldable (foldrM) import Data.Map (Map) @@ -22,6 +23,7 @@ import Text.Pandoc.Walk (walkM, Walkable) import Data.Data (Data, DataType, dataTypeConstrs, dataTypeName, dataTypeOf, showConstr, toConstr, tyconUQname) import Text.Pandoc.Lua.StackInstances() +import Text.Pandoc.Lua.Util (typeCheck) type FunctionMap = Map String LuaFilterFunction @@ -65,7 +67,7 @@ registerFilterFunction idx = do elementOrList :: FromLuaStack a => a -> Lua [a] elementOrList x = do - let topOfStack = Lua.StackIndex (-1) + let topOfStack = Lua.stackTop elementUnchanged <- Lua.isnil topOfStack if elementUnchanged then [x] <$ Lua.pop 1 @@ -73,7 +75,9 @@ elementOrList x = do mbres <- Lua.peekEither topOfStack case mbres of Right res -> [res] <$ Lua.pop 1 - Left _ -> Lua.toList topOfStack <* Lua.pop 1 + Left _ -> do + typeCheck Lua.stackTop Lua.TypeTable + Lua.toList topOfStack `finally` Lua.pop 1 -- | Try running a filter for the given element tryFilter :: (Data a, FromLuaStack a, ToLuaStack a) diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index d0289d1ef..38404157c 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -35,13 +35,15 @@ module Text.Pandoc.Lua.StackInstances () where import Control.Applicative ((<|>)) import Control.Monad (when) +import Control.Monad.Catch (finally) import Data.Data (showConstr, toConstr) import Data.Foldable (forM_) import Foreign.Lua (FromLuaStack (peek), Lua, LuaInteger, LuaNumber, StackIndex, ToLuaStack (push), Type (..), throwLuaError, tryLua) import Text.Pandoc.Definition import Text.Pandoc.Extensions (Extensions) -import Text.Pandoc.Lua.Util (adjustIndexBy, getTable, pushViaConstructor) +import Text.Pandoc.Lua.Util (adjustIndexBy, getTable, pushViaConstructor, + typeCheck) import Text.Pandoc.Options (ReaderOptions (..), TrackChanges) import Text.Pandoc.Shared (Element (Blk, Sec), safeRead) @@ -49,21 +51,27 @@ import qualified Foreign.Lua as Lua import qualified Data.Set as Set import qualified Text.Pandoc.Lua.Util as LuaUtil +defineHowTo :: String -> Lua a -> Lua a +defineHowTo ctx op = op `Lua.modifyLuaError` (("Could not " ++ ctx ++ ": ") ++) + instance ToLuaStack Pandoc where push (Pandoc meta blocks) = pushViaConstructor "Pandoc" blocks meta instance FromLuaStack Pandoc where - peek idx = do + peek idx = defineHowTo "get Pandoc value" $ do + typeCheck idx Lua.TypeTable blocks <- getTable idx "blocks" - meta <- getTable idx "meta" + meta <- Lua.getfield idx "meta" *> (Lua.peek Lua.stackTop `finally` Lua.pop 1) return $ Pandoc meta blocks instance ToLuaStack Meta where push (Meta mmap) = pushViaConstructor "Meta" mmap instance FromLuaStack Meta where - peek idx = Meta <$> peek idx + peek idx = defineHowTo "get Meta value" $ do + typeCheck idx Lua.TypeTable + Meta <$> peek idx instance ToLuaStack MetaValue where push = pushMetaValue @@ -160,7 +168,7 @@ pushMetaValue = \case -- | Interpret the value at the given stack index as meta value. peekMetaValue :: StackIndex -> Lua MetaValue -peekMetaValue idx = do +peekMetaValue idx = defineHowTo "get MetaValue" $ do -- Get the contents of an AST element. let elementContent :: FromLuaStack a => Lua a elementContent = peek idx @@ -209,7 +217,8 @@ pushBlock = \case -- | Return the value at the given index as block if possible. peekBlock :: StackIndex -> Lua Block -peekBlock idx = do +peekBlock idx = defineHowTo "get Block value" $ do + typeCheck idx Lua.TypeTable tag <- getTag idx case tag of "BlockQuote" -> BlockQuote <$> elementContent @@ -260,7 +269,8 @@ pushInline = \case -- | Return the value at the given index as inline if possible. peekInline :: StackIndex -> Lua Inline -peekInline idx = do +peekInline idx = defineHowTo "get Inline value" $ do + typeCheck idx Lua.TypeTable tag <- getTag idx case tag of "Cite" -> uncurry Cite <$> elementContent @@ -296,11 +306,7 @@ getTag idx = do hasMT <- Lua.getmetatable idx push "tag" if hasMT then Lua.rawget (-2) else Lua.rawget (idx `adjustIndexBy` 1) - r <- tryLua (peek (-1)) - Lua.settop top - case r of - Left (Lua.LuaException err) -> throwLuaError err - Right res -> return res + peek Lua.stackTop `finally` Lua.settop top withAttr :: (Attr -> a -> b) -> (LuaAttr, a) -> b withAttr f (attributes, x) = f (fromLuaAttr attributes) x @@ -313,7 +319,7 @@ instance ToLuaStack LuaAttr where pushViaConstructor "Attr" id' classes kv instance FromLuaStack LuaAttr where - peek idx = LuaAttr <$> peek idx + peek idx = defineHowTo "get Attr value" (LuaAttr <$> peek idx) -- -- Hierarchical elements diff --git a/src/Text/Pandoc/Lua/Util.hs b/src/Text/Pandoc/Lua/Util.hs index 799b45b72..a3af155c9 100644 --- a/src/Text/Pandoc/Lua/Util.hs +++ b/src/Text/Pandoc/Lua/Util.hs @@ -36,6 +36,7 @@ module Text.Pandoc.Lua.Util , getRawInt , setRawInt , addRawInt + , typeCheck , raiseError , popValue , PushViaCall @@ -100,6 +101,14 @@ setRawInt idx key value = do addRawInt :: ToLuaStack a => Int -> a -> Lua () addRawInt = setRawInt (-1) +typeCheck :: StackIndex -> Lua.Type -> Lua () +typeCheck idx expected = do + actual <- Lua.ltype idx + when (actual /= expected) $ do + expName <- Lua.typename expected + actName <- Lua.typename actual + Lua.throwLuaError $ "expected " ++ expName ++ " but got " ++ actName ++ "." + raiseError :: ToLuaStack a => a -> Lua NumResults raiseError e = do Lua.push e -- cgit v1.2.3 From 5d49cbd35e815dd041e54da511bdd0eeafd400c0 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Wed, 10 Jan 2018 22:26:12 +0100 Subject: Move filter functions to separate module --- src/Text/Pandoc/App.hs | 100 +++-------------------------------------- src/Text/Pandoc/Filter.hs | 60 +++++++++++++++++++++++++ src/Text/Pandoc/Filter/Json.hs | 97 +++++++++++++++++++++++++++++++++++++++ src/Text/Pandoc/Filter/Lua.hs | 53 ++++++++++++++++++++++ src/Text/Pandoc/Filter/Path.hs | 53 ++++++++++++++++++++++ 5 files changed, 268 insertions(+), 95 deletions(-) create mode 100644 src/Text/Pandoc/Filter.hs create mode 100644 src/Text/Pandoc/Filter/Json.hs create mode 100644 src/Text/Pandoc/Filter/Lua.hs create mode 100644 src/Text/Pandoc/Filter/Path.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 976311e77..26c754cd6 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -46,12 +46,11 @@ import qualified Control.Exception as E import Control.Monad import Control.Monad.Except (catchError, throwError) import Control.Monad.Trans -import Data.Aeson (defaultOptions, eitherDecode', encode) +import Data.Aeson (defaultOptions) import Data.Aeson.TH (deriveJSON) import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as B import Data.Char (toLower, toUpper) -import Data.Foldable (foldrM) import Data.List (find, intercalate, isPrefixOf, isSuffixOf, sort) import qualified Data.Map as M import Data.Maybe (fromMaybe, isJust, isNothing) @@ -73,10 +72,9 @@ import Skylighting (Style, Syntax (..), defaultSyntaxMap, parseTheme, pygments) import Skylighting.Parser (addSyntaxDefinition, parseSyntaxDefinition) import System.Console.GetOpt -import System.Directory (Permissions (..), doesFileExist, findExecutable, - getAppUserDataDirectory, getPermissions) -import System.Environment (getArgs, getEnvironment, getProgName) -import System.Exit (ExitCode (..), exitSuccess) +import System.Directory (getAppUserDataDirectory) +import System.Environment (getArgs, getProgName) +import System.Exit (exitSuccess) import System.FilePath import System.IO (nativeNewline, stdout) import qualified System.IO as IO (Newline (..)) @@ -84,10 +82,9 @@ import System.IO.Error (isDoesNotExistError) import Text.Pandoc import Text.Pandoc.BCP47 (Lang (..), parseBCP47) import Text.Pandoc.Builder (setMeta, deleteMeta) +import Text.Pandoc.Filter (Filter (JSONFilter, LuaFilter), applyFilters) import Text.Pandoc.Highlighting (highlightingStyles) -import Text.Pandoc.Lua (LuaException (..), runLuaFilter) import Text.Pandoc.PDF (makePDF) -import Text.Pandoc.Process (pipeProcess) import Text.Pandoc.SelfContained (makeDataURI, makeSelfContained) import Text.Pandoc.Shared (eastAsianLineBreakFilter, stripEmptyParagraphs, headerShift, isURI, ordNub, safeRead, tabFilter) @@ -538,48 +535,6 @@ type Transform = Pandoc -> Pandoc isTextFormat :: String -> Bool isTextFormat s = s `notElem` ["odt","docx","epub2","epub3","epub","pptx"] -externalFilter :: MonadIO m - => ReaderOptions -> FilePath -> [String] -> Pandoc -> m Pandoc -externalFilter ropts f args' d = liftIO $ do - exists <- doesFileExist f - isExecutable <- if exists - then executable <$> getPermissions f - else return True - let (f', args'') = if exists - then case map toLower (takeExtension f) of - _ | isExecutable -> ("." </> f, args') - ".py" -> ("python", f:args') - ".hs" -> ("runhaskell", f:args') - ".pl" -> ("perl", f:args') - ".rb" -> ("ruby", f:args') - ".php" -> ("php", f:args') - ".js" -> ("node", f:args') - ".r" -> ("Rscript", f:args') - _ -> (f, args') - else (f, args') - unless (exists && isExecutable) $ do - mbExe <- findExecutable f' - when (isNothing mbExe) $ - E.throwIO $ PandocFilterError f ("Could not find executable " ++ f') - env <- getEnvironment - let env' = Just - ( ("PANDOC_VERSION", pandocVersion) - : ("PANDOC_READER_OPTIONS", UTF8.toStringLazy (encode ropts)) - : env ) - (exitcode, outbs) <- E.handle filterException $ - pipeProcess env' f' args'' $ encode d - case exitcode of - ExitSuccess -> either (E.throwIO . PandocFilterError f) - return $ eitherDecode' outbs - ExitFailure ec -> E.throwIO $ PandocFilterError f - ("Filter returned error status " ++ show ec) - where filterException :: E.SomeException -> IO a - filterException e = E.throwIO $ PandocFilterError f (show e) - -data Filter = LuaFilter FilePath - | JSONFilter FilePath - deriving (Show) - -- | Data structure for command line options. data Opt = Opt { optTabStop :: Int -- ^ Number of spaces per tab @@ -824,50 +779,6 @@ defaultWriterName x = applyTransforms :: Monad m => [Transform] -> Pandoc -> m Pandoc applyTransforms transforms d = return $ foldr ($) d transforms - -- First we check to see if a filter is found. If not, and if it's - -- not an absolute path, we check to see whether it's in `userdir/filters`. - -- If not, we leave it unchanged. -expandFilterPath :: PandocMonad m => FilePath -> m FilePath -expandFilterPath fp = do - mbDatadir <- getUserDataDir - fpExists <- fileExists fp - if fpExists - then return fp - else case mbDatadir of - Just datadir | isRelative fp -> do - let filterPath = datadir </> "filters" </> fp - filterPathExists <- fileExists filterPath - if filterPathExists - then return filterPath - else return fp - _ -> return fp - -applyFilters :: ReaderOptions - -> [Filter] - -> [String] - -> Pandoc - -> PandocIO Pandoc -applyFilters ropts filters args d = do - foldrM ($) d $ map (applyFilter ropts args) filters - -applyFilter :: ReaderOptions - -> [String] - -> Filter - -> Pandoc - -> PandocIO Pandoc -applyFilter ropts args (LuaFilter f) d = do - f' <- expandFilterPath f - let format = case args of - (x:_) -> x - _ -> error "Format not supplied for lua filter" - res <- runLuaFilter ropts f' format d - case res of - Right x -> return x - Left (LuaException s) -> E.throw (PandocFilterError f s) -applyFilter ropts args (JSONFilter f) d = do - f' <- expandFilterPath f - liftIO $ externalFilter ropts f' args d - readSource :: FilePath -> PandocIO Text readSource "-" = liftIO (UTF8.toText <$> BS.getContents) readSource src = case parseURI src of @@ -1722,5 +1633,4 @@ deprecatedOption o msg = -- see https://github.com/jgm/pandoc/pull/4083 -- using generic deriving caused long compilation times $(deriveJSON defaultOptions ''LineEnding) -$(deriveJSON defaultOptions ''Filter) $(deriveJSON defaultOptions ''Opt) diff --git a/src/Text/Pandoc/Filter.hs b/src/Text/Pandoc/Filter.hs new file mode 100644 index 000000000..30c99cc28 --- /dev/null +++ b/src/Text/Pandoc/Filter.hs @@ -0,0 +1,60 @@ +{- +Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu> + +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 +-} +{-# LANGUAGE TemplateHaskell #-} + +{- | + Module : Text.Pandoc.Filter + Copyright : Copyright (C) 2006-2017 John MacFarlane + License : GNU GPL, version 2 or above + + Maintainer : John MacFarlane <jgm@berkeley@edu> + Stability : alpha + Portability : portable + +Programmatically modifications of pandoc documents. +-} +module Text.Pandoc.Filter + ( Filter (..) + , applyFilters + ) where + +import Data.Aeson (defaultOptions) +import Data.Aeson.TH (deriveJSON) +import Data.Foldable (foldrM) +import Text.Pandoc.Class (PandocIO) +import Text.Pandoc.Definition (Pandoc) +import Text.Pandoc.Options (ReaderOptions) +import qualified Text.Pandoc.Filter.Json as JsonFilter +import qualified Text.Pandoc.Filter.Lua as LuaFilter + +data Filter = LuaFilter FilePath + | JSONFilter FilePath + deriving (Show) + +applyFilters :: ReaderOptions + -> [Filter] + -> [String] + -> Pandoc + -> PandocIO Pandoc +applyFilters ropts filters args d = do + foldrM ($) d $ map applyFilter filters + where + applyFilter (JSONFilter f) = JsonFilter.apply ropts args f + applyFilter (LuaFilter f) = LuaFilter.apply ropts args f + +$(deriveJSON defaultOptions ''Filter) diff --git a/src/Text/Pandoc/Filter/Json.hs b/src/Text/Pandoc/Filter/Json.hs new file mode 100644 index 000000000..681c52720 --- /dev/null +++ b/src/Text/Pandoc/Filter/Json.hs @@ -0,0 +1,97 @@ +{- +Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu> + +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.Filter + Copyright : Copyright (C) 2006-2018 John MacFarlane + License : GNU GPL, version 2 or above + + Maintainer : John MacFarlane <jgm@berkeley@edu> + Stability : alpha + Portability : portable + +Programmatically modifications of pandoc documents via JSON filters. +-} +module Text.Pandoc.Filter.Json (apply) where + +import Control.Monad (unless, when) +import Control.Monad.Trans (MonadIO (liftIO)) +import Data.Aeson (eitherDecode', encode) +import Data.Char (toLower) +import Data.Maybe (isNothing) +import System.Directory (executable, doesFileExist, findExecutable, + getPermissions) +import System.Environment (getEnvironment) +import System.Exit (ExitCode (..)) +import System.FilePath ((</>), takeExtension) +import Text.Pandoc.Class (PandocIO) +import Text.Pandoc.Error (PandocError (PandocFilterError)) +import Text.Pandoc.Definition (Pandoc) +import Text.Pandoc.Filter.Path (expandFilterPath) +import Text.Pandoc.Options (ReaderOptions) +import Text.Pandoc.Process (pipeProcess) +import Text.Pandoc.Shared (pandocVersion) +import qualified Control.Exception as E +import qualified Text.Pandoc.UTF8 as UTF8 + +apply :: ReaderOptions + -> [String] + -> FilePath + -> Pandoc + -> PandocIO Pandoc +apply ropts args f d = do + f' <- expandFilterPath f + liftIO $ externalFilter ropts f' args d + +externalFilter :: MonadIO m + => ReaderOptions -> FilePath -> [String] -> Pandoc -> m Pandoc +externalFilter ropts f args' d = liftIO $ do + exists <- doesFileExist f + isExecutable <- if exists + then executable <$> getPermissions f + else return True + let (f', args'') = if exists + then case map toLower (takeExtension f) of + _ | isExecutable -> ("." </> f, args') + ".py" -> ("python", f:args') + ".hs" -> ("runhaskell", f:args') + ".pl" -> ("perl", f:args') + ".rb" -> ("ruby", f:args') + ".php" -> ("php", f:args') + ".js" -> ("node", f:args') + ".r" -> ("Rscript", f:args') + _ -> (f, args') + else (f, args') + unless (exists && isExecutable) $ do + mbExe <- findExecutable f' + when (isNothing mbExe) $ + E.throwIO $ PandocFilterError f ("Could not find executable " ++ f') + env <- getEnvironment + let env' = Just + ( ("PANDOC_VERSION", pandocVersion) + : ("PANDOC_READER_OPTIONS", UTF8.toStringLazy (encode ropts)) + : env ) + (exitcode, outbs) <- E.handle filterException $ + pipeProcess env' f' args'' $ encode d + case exitcode of + ExitSuccess -> either (E.throwIO . PandocFilterError f) + return $ eitherDecode' outbs + ExitFailure ec -> E.throwIO $ PandocFilterError f + ("Filter returned error status " ++ show ec) + where filterException :: E.SomeException -> IO a + filterException e = E.throwIO $ PandocFilterError f (show e) diff --git a/src/Text/Pandoc/Filter/Lua.hs b/src/Text/Pandoc/Filter/Lua.hs new file mode 100644 index 000000000..597a31cbc --- /dev/null +++ b/src/Text/Pandoc/Filter/Lua.hs @@ -0,0 +1,53 @@ +{- +Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu> + +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.Filter.Lua + Copyright : Copyright (C) 2006-2018 John MacFarlane + License : GNU GPL, version 2 or above + + Maintainer : John MacFarlane <jgm@berkeley@edu> + Stability : alpha + Portability : portable + +Apply Lua filters to modify a pandoc documents programmatically. +-} +module Text.Pandoc.Filter.Lua (apply) where + +import Control.Exception (throw) +import Text.Pandoc.Class (PandocIO) +import Text.Pandoc.Definition (Pandoc) +import Text.Pandoc.Error (PandocError (PandocFilterError)) +import Text.Pandoc.Filter.Path (expandFilterPath) +import Text.Pandoc.Lua (LuaException (..), runLuaFilter) +import Text.Pandoc.Options (ReaderOptions) + +apply :: ReaderOptions + -> [String] + -> FilePath + -> Pandoc + -> PandocIO Pandoc +apply ropts args f d = do + f' <- expandFilterPath f + let format = case args of + (x:_) -> x + _ -> error "Format not supplied for lua filter" + res <- runLuaFilter ropts f' format d + case res of + Right x -> return x + Left (LuaException s) -> throw (PandocFilterError f s) diff --git a/src/Text/Pandoc/Filter/Path.hs b/src/Text/Pandoc/Filter/Path.hs new file mode 100644 index 000000000..8074bcbb7 --- /dev/null +++ b/src/Text/Pandoc/Filter/Path.hs @@ -0,0 +1,53 @@ +{- +Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu> + +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.Filter.Path + Copyright : Copyright (C) 2006-2018 John MacFarlane + License : GNU GPL, version 2 or above + + Maintainer : John MacFarlane <jgm@berkeley@edu> + Stability : alpha + Portability : portable + +Expand paths of filters, searching the data directory. +-} +module Text.Pandoc.Filter.Path + ( expandFilterPath + ) where + +import Text.Pandoc.Class (PandocMonad, fileExists, getUserDataDir) +import System.FilePath ((</>), isRelative) + + -- First we check to see if a filter is found. If not, and if it's + -- not an absolute path, we check to see whether it's in `userdir/filters`. + -- If not, we leave it unchanged. +expandFilterPath :: PandocMonad m => FilePath -> m FilePath +expandFilterPath fp = do + mbDatadir <- getUserDataDir + fpExists <- fileExists fp + if fpExists + then return fp + else case mbDatadir of + Just datadir | isRelative fp -> do + let filterPath = datadir </> "filters" </> fp + filterPathExists <- fileExists filterPath + if filterPathExists + then return filterPath + else return fp + _ -> return fp -- cgit v1.2.3 From 8d5422f36b28bab67b4d13e4a3d2154d0c5024f8 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Wed, 10 Jan 2018 22:26:12 +0100 Subject: Lua modules: add function pandoc.utils.run_json_filter Runs a JSON filter on a Pandoc document. --- src/Text/Pandoc/Lua/Module/Utils.hs | 29 ++++++++++++++++++++++++++--- src/Text/Pandoc/Lua/Packages.hs | 3 ++- 2 files changed, 28 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/Module/Utils.hs b/src/Text/Pandoc/Lua/Module/Utils.hs index b453b38d7..ab29cc0c7 100644 --- a/src/Text/Pandoc/Lua/Module/Utils.hs +++ b/src/Text/Pandoc/Lua/Module/Utils.hs @@ -30,22 +30,26 @@ module Text.Pandoc.Lua.Module.Utils ) where import Control.Applicative ((<|>)) +import Data.Default (def) import Foreign.Lua (FromLuaStack, Lua, LuaInteger, NumResults) +import Text.Pandoc.Class (runIO, setUserDataDir) import Text.Pandoc.Definition (Pandoc, Meta, MetaValue, Block, Inline) import Text.Pandoc.Lua.StackInstances () -import Text.Pandoc.Lua.Util (addFunction) +import Text.Pandoc.Lua.Util (addFunction, popValue) import qualified Data.Digest.Pure.SHA as SHA import qualified Data.ByteString.Lazy as BSL import qualified Foreign.Lua as Lua +import qualified Text.Pandoc.Filter.Json as JsonFilter import qualified Text.Pandoc.Shared as Shared -- | Push the "pandoc.utils" module to the lua stack. -pushModule :: Lua NumResults -pushModule = do +pushModule :: Maybe FilePath -> Lua NumResults +pushModule mbDatadir = do Lua.newtable addFunction "hierarchicalize" hierarchicalize addFunction "normalize_date" normalizeDate + addFunction "run_json_filter" (runJsonFilter mbDatadir) addFunction "sha1" sha1 addFunction "stringify" stringify addFunction "to_roman_numeral" toRomanNumeral @@ -62,6 +66,25 @@ hierarchicalize = return . Shared.hierarchicalize normalizeDate :: String -> Lua (Lua.Optional String) normalizeDate = return . Lua.Optional . Shared.normalizeDate +-- | Run a JSON filter on the given document. +runJsonFilter :: Maybe FilePath + -> Pandoc + -> FilePath + -> Lua.Optional [String] + -> Lua NumResults +runJsonFilter mbDatadir doc filterFile optArgs = do + args <- case Lua.fromOptional optArgs of + Just x -> return x + Nothing -> do + Lua.getglobal "FORMAT" + (:[]) <$> popValue + filterRes <- Lua.liftIO . runIO $ do + setUserDataDir mbDatadir + JsonFilter.apply def args filterFile doc + case filterRes of + Left err -> Lua.raiseError (show err) + Right d -> (1 :: NumResults) <$ Lua.push d + -- | Calculate the hash of the given contents. sha1 :: BSL.ByteString -> Lua String diff --git a/src/Text/Pandoc/Lua/Packages.hs b/src/Text/Pandoc/Lua/Packages.hs index dda2dd2fe..0169d0045 100644 --- a/src/Text/Pandoc/Lua/Packages.hs +++ b/src/Text/Pandoc/Lua/Packages.hs @@ -78,7 +78,8 @@ pandocPackageSearcher luaPkgParams pkgName = "pandoc.mediabag" -> let st = luaPkgCommonState luaPkgParams mbRef = luaPkgMediaBag luaPkgParams in pushWrappedHsFun (MediaBag.pushModule st mbRef) - "pandoc.utils" -> pushWrappedHsFun Utils.pushModule + "pandoc.utils" -> let datadirMb = luaPkgDataDir luaPkgParams + in pushWrappedHsFun (Utils.pushModule datadirMb) _ -> searchPureLuaLoader where pushWrappedHsFun f = do -- cgit v1.2.3 From a2870a1aeb534b5cb237f2cff9448ca714574b35 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Fri, 12 Jan 2018 17:28:58 -0500 Subject: Powerpoint writer: Improve templating using `--reference-doc` Templating should work much more reliably now. There is still some problem with image placement when we change sizes. A further commit will address this. --- src/Text/Pandoc/Writers/Powerpoint.hs | 214 +++++++++++++++++++++++----------- 1 file changed, 145 insertions(+), 69 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index af19ec93b..7fa327668 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -36,11 +36,11 @@ import Control.Monad.Except (throwError) import Control.Monad.Reader import Control.Monad.State import Codec.Archive.Zip -import Data.List (intercalate, stripPrefix, isPrefixOf, nub) +import Data.List (intercalate, stripPrefix, nub, union) import Data.Default import Data.Time.Clock (UTCTime) import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds, posixSecondsToUTCTime) -import System.FilePath.Posix (splitDirectories, splitExtension) +import System.FilePath.Posix (splitDirectories, splitExtension, takeExtension) import Text.XML.Light import qualified Text.XML.Light.Cursor as XMLC import Text.Pandoc.Definition @@ -61,6 +61,7 @@ import qualified Data.Map as M import Data.Maybe (mapMaybe, listToMaybe, maybeToList, catMaybes) import Text.Pandoc.ImageSize import Control.Applicative ((<|>)) +import System.FilePath.Glob import Text.TeXMath import Text.Pandoc.Writers.Math (convertMath) @@ -90,9 +91,13 @@ writePowerpoint opts (Pandoc meta blks) = do Just n -> n Nothing -> getSlideLevel blks' } - runP env def $ do pres <- blocksToPresentation blks' - archv <- presentationToArchive pres - return $ fromArchive archv + + let st = def { stMediaGlobalIds = initialGlobalIds refArchive distArchive + } + + runP env st $ do pres <- blocksToPresentation blks' + archv <- presentationToArchive pres + return $ fromArchive archv concatMapM :: (Monad m) => (a -> m [b]) -> [a] -> m [b] concatMapM f xs = liftM concat (mapM f xs) @@ -149,6 +154,8 @@ data WriterState = WriterState { stLinkIds :: M.Map Int (M.Map Int (URL, String) , stNoteIds :: M.Map Int [Block] -- associate anchors with slide id , stAnchorMap :: M.Map String Int + -- media inherited from the template. + , stTemplateMedia :: [FilePath] } deriving (Show, Eq) instance Default WriterState where @@ -157,8 +164,25 @@ instance Default WriterState where , stMediaGlobalIds = mempty , stNoteIds = mempty , stAnchorMap= mempty + , stTemplateMedia = [] } +-- This populates the global ids map with images already in the +-- template, so the ids won't be used by images introduced by the +-- user. +initialGlobalIds :: Archive -> Archive -> M.Map FilePath Int +initialGlobalIds refArchive distArchive = + let archiveFiles = filesInArchive refArchive `union` filesInArchive distArchive + mediaPaths = filter (match (compile "ppt/media/image")) archiveFiles + + go :: FilePath -> Maybe (FilePath, Int) + go fp = do + s <- stripPrefix "ppt/media/image" $ fst $ splitExtension fp + (n, _) <- listToMaybe $ reads s + return (fp, n) + in + M.fromList $ mapMaybe go mediaPaths + type P m = ReaderT WriterEnv (StateT WriterState m) runP :: Monad m => WriterEnv -> WriterState -> P m a -> m a @@ -760,75 +784,111 @@ copyFileToArchive arch fp = do Nothing -> fail $ fp ++ " missing in reference file" Just e -> return $ addEntryToArchive e arch -getMediaFiles :: PandocMonad m => P m [FilePath] -getMediaFiles = do +-- getMediaFiles :: PandocMonad m => P m [FilePath] +-- getMediaFiles = do +-- refArchive <- asks envRefArchive +-- distArchive <- asks envDistArchive +-- let allEntries = nub $ filesInArchive refArchive ++ filesInArchive distArchive +-- return $ filter (isPrefixOf "ppt/media") allEntries + + +-- copyFileToArchiveIfExists :: PandocMonad m => Archive -> FilePath -> P m Archive +-- copyFileToArchiveIfExists arch fp = do +-- refArchive <- asks envRefArchive +-- distArchive <- asks envDistArchive +-- case findEntryByPath fp refArchive `mplus` findEntryByPath fp distArchive of +-- Nothing -> return $ arch +-- Just e -> return $ addEntryToArchive e arch + +inheritedPatterns :: [Pattern] +inheritedPatterns = map compile [ "_rels/.rels" + , "docProps/app.xml" + , "docProps/core.xml" + , "ppt/slideLayouts/slideLayout*.xml" + , "ppt/slideLayouts/_rels/slideLayout*.xml.rels" + , "ppt/slideMasters/slideMaster1.xml" + , "ppt/slideMasters/_rels/slideMaster1.xml.rels" + , "ppt/theme/theme1.xml" + , "ppt/theme/_rels/theme1.xml.rels" + , "ppt/presProps.xml" + , "ppt/viewProps.xml" + , "ppt/tableStyles.xml" + , "ppt/media/image*" + ] + +patternToFilePaths :: PandocMonad m => Pattern -> P m [FilePath] +patternToFilePaths pat = do refArchive <- asks envRefArchive distArchive <- asks envDistArchive - let allEntries = nub $ filesInArchive refArchive ++ filesInArchive distArchive - return $ filter (isPrefixOf "ppt/media") allEntries + let archiveFiles = filesInArchive refArchive `union` filesInArchive distArchive + return $ filter (match pat) archiveFiles + +patternsToFilePaths :: PandocMonad m => [Pattern] -> P m [FilePath] +patternsToFilePaths pats = concat <$> mapM patternToFilePaths pats + +-- requiredFiles :: [FilePath] +-- requiredFiles = inheritedFiles + +-- inheritedFiles :: [FilePath] +-- inheritedFiles = [ "_rels/.rels" +-- , "docProps/app.xml" +-- , "docProps/core.xml" +-- , "ppt/slideLayouts/slideLayout4.xml" +-- , "ppt/slideLayouts/_rels/slideLayout9.xml.rels" +-- , "ppt/slideLayouts/_rels/slideLayout2.xml.rels" +-- , "ppt/slideLayouts/_rels/slideLayout10.xml.rels" +-- , "ppt/slideLayouts/_rels/slideLayout1.xml.rels" +-- , "ppt/slideLayouts/_rels/slideLayout3.xml.rels" +-- , "ppt/slideLayouts/_rels/slideLayout5.xml.rels" +-- , "ppt/slideLayouts/_rels/slideLayout7.xml.rels" +-- , "ppt/slideLayouts/_rels/slideLayout8.xml.rels" +-- , "ppt/slideLayouts/_rels/slideLayout11.xml.rels" +-- , "ppt/slideLayouts/_rels/slideLayout4.xml.rels" +-- , "ppt/slideLayouts/_rels/slideLayout6.xml.rels" +-- , "ppt/slideLayouts/slideLayout2.xml" +-- , "ppt/slideLayouts/slideLayout8.xml" +-- , "ppt/slideLayouts/slideLayout11.xml" +-- , "ppt/slideLayouts/slideLayout3.xml" +-- , "ppt/slideLayouts/slideLayout6.xml" +-- , "ppt/slideLayouts/slideLayout9.xml" +-- , "ppt/slideLayouts/slideLayout5.xml" +-- , "ppt/slideLayouts/slideLayout7.xml" +-- , "ppt/slideLayouts/slideLayout1.xml" +-- , "ppt/slideLayouts/slideLayout10.xml" +-- -- , "ppt/_rels/presentation.xml.rels" +-- , "ppt/theme/theme1.xml" +-- , "ppt/presProps.xml" +-- -- , "ppt/slides/_rels/slide1.xml.rels" +-- -- , "ppt/slides/_rels/slide2.xml.rels" +-- -- This is the one we're +-- -- going to build +-- -- , "ppt/slides/slide2.xml" +-- -- , "ppt/slides/slide1.xml" +-- , "ppt/viewProps.xml" +-- , "ppt/tableStyles.xml" +-- , "ppt/slideMasters/_rels/slideMaster1.xml.rels" +-- , "ppt/slideMasters/slideMaster1.xml" +-- -- , "ppt/presentation.xml" +-- -- , "[Content_Types].xml" +-- ] + +-- -- Here are some that might not be there. We won't fail if they're not +-- possibleInheritedFiles :: [FilePath] +-- possibleInheritedFiles = [ "ppt/theme/_rels/theme1.xml.rels" ] -copyFileToArchiveIfExists :: PandocMonad m => Archive -> FilePath -> P m Archive -copyFileToArchiveIfExists arch fp = do - refArchive <- asks envRefArchive - distArchive <- asks envDistArchive - case findEntryByPath fp refArchive `mplus` findEntryByPath fp distArchive of - Nothing -> return $ arch - Just e -> return $ addEntryToArchive e arch - -inheritedFiles :: [FilePath] -inheritedFiles = [ "_rels/.rels" - , "docProps/app.xml" - , "docProps/core.xml" - , "ppt/slideLayouts/slideLayout4.xml" - , "ppt/slideLayouts/_rels/slideLayout9.xml.rels" - , "ppt/slideLayouts/_rels/slideLayout2.xml.rels" - , "ppt/slideLayouts/_rels/slideLayout10.xml.rels" - , "ppt/slideLayouts/_rels/slideLayout1.xml.rels" - , "ppt/slideLayouts/_rels/slideLayout3.xml.rels" - , "ppt/slideLayouts/_rels/slideLayout5.xml.rels" - , "ppt/slideLayouts/_rels/slideLayout7.xml.rels" - , "ppt/slideLayouts/_rels/slideLayout8.xml.rels" - , "ppt/slideLayouts/_rels/slideLayout11.xml.rels" - , "ppt/slideLayouts/_rels/slideLayout4.xml.rels" - , "ppt/slideLayouts/_rels/slideLayout6.xml.rels" - , "ppt/slideLayouts/slideLayout2.xml" - , "ppt/slideLayouts/slideLayout8.xml" - , "ppt/slideLayouts/slideLayout11.xml" - , "ppt/slideLayouts/slideLayout3.xml" - , "ppt/slideLayouts/slideLayout6.xml" - , "ppt/slideLayouts/slideLayout9.xml" - , "ppt/slideLayouts/slideLayout5.xml" - , "ppt/slideLayouts/slideLayout7.xml" - , "ppt/slideLayouts/slideLayout1.xml" - , "ppt/slideLayouts/slideLayout10.xml" - -- , "ppt/_rels/presentation.xml.rels" - , "ppt/theme/theme1.xml" - , "ppt/presProps.xml" - -- , "ppt/slides/_rels/slide1.xml.rels" - -- , "ppt/slides/_rels/slide2.xml.rels" - -- This is the one we're - -- going to build - -- , "ppt/slides/slide2.xml" - -- , "ppt/slides/slide1.xml" - , "ppt/viewProps.xml" - , "ppt/tableStyles.xml" - , "ppt/slideMasters/_rels/slideMaster1.xml.rels" - , "ppt/slideMasters/slideMaster1.xml" - -- , "ppt/presentation.xml" - -- , "[Content_Types].xml" - ] - --- Here are some that might not be there. We won't fail if they're not -possibleInheritedFiles :: [FilePath] -possibleInheritedFiles = [ "ppt/theme/_rels/theme1.xml.rels" ] presentationToArchive :: PandocMonad m => Presentation -> P m Archive presentationToArchive p@(Presentation _ slides) = do - newArch <- foldM copyFileToArchive emptyArchive inheritedFiles - mediaDir <- getMediaFiles - newArch' <- foldM copyFileToArchiveIfExists newArch $ - possibleInheritedFiles ++ mediaDir + filePaths <- patternsToFilePaths inheritedPatterns + newArch' <- foldM copyFileToArchive emptyArchive filePaths + + -- set the template media to the relevant fps: + + -- we register any media fp in the filepaths + -- mediaDir <- getMediaFiles + -- newArch' <- foldM copyFileToArchiveIfExists newArch $ + -- possibleInheritedFiles ++ mediaDir -- presentation entry and rels. We have to do the rels first to make -- sure we know the correct offset for the rIds. presEntry <- presentationToPresEntry p @@ -1808,6 +1868,17 @@ contentTypesToEntry ct = elemToEntry "[Content_Types].xml" $ contentTypesToEleme pathToOverride :: FilePath -> Maybe OverrideContentType pathToOverride fp = OverrideContentType ("/" ++ fp) <$> (getContentType fp) +mediaFileContentType :: FilePath -> Maybe DefaultContentType +mediaFileContentType fp = case takeExtension fp of + '.' : ext -> Just $ + DefaultContentType { defContentTypesExt = ext + , defContentTypesType = + case getMimeType fp of + Just mt -> mt + Nothing -> "application/octet-stream" + } + _ -> Nothing + mediaContentType :: MediaInfo -> Maybe DefaultContentType mediaContentType mInfo | Just ('.' : ext) <- mInfoExt mInfo = @@ -1822,11 +1893,16 @@ mediaContentType mInfo presentationToContentTypes :: PandocMonad m => Presentation -> P m ContentTypes presentationToContentTypes (Presentation _ slides) = do mediaInfos <- (mconcat . M.elems) <$> gets stMediaIds + filePaths <- patternsToFilePaths inheritedPatterns + let mediaFps = filter (match (compile "ppt/media/image*")) filePaths let defaults = [ DefaultContentType "xml" "application/xml" , DefaultContentType "rels" "application/vnd.openxmlformats-package.relationships+xml" ] - mediaDefaults = nub $ mapMaybe mediaContentType mediaInfos - inheritedOverrides = mapMaybe pathToOverride inheritedFiles + mediaDefaults = nub $ + (mapMaybe mediaContentType $ mediaInfos) ++ + (mapMaybe mediaFileContentType $ mediaFps) + + inheritedOverrides = mapMaybe pathToOverride filePaths presOverride = mapMaybe pathToOverride ["ppt/presentation.xml"] slideOverrides = mapMaybe -- cgit v1.2.3 From 194f08d17a0ba695187f99e2494977fb9bca53ef Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sat, 13 Jan 2018 07:37:02 -0500 Subject: Powerpoint writer: Check for required files Since we now import from reference/dist file by glob, we need to make sure that we're getting the files we need to make a non-corrupt Powerpoint. This performs that check. (In the process, this change also cleaned up a lot of commented-out code left from the switch to the new reference-doc method.) --- src/Text/Pandoc/Writers/Powerpoint.hs | 101 ++++++++++------------------------ 1 file changed, 30 insertions(+), 71 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index 7fa327668..2a46e40fe 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -784,22 +784,6 @@ copyFileToArchive arch fp = do Nothing -> fail $ fp ++ " missing in reference file" Just e -> return $ addEntryToArchive e arch --- getMediaFiles :: PandocMonad m => P m [FilePath] --- getMediaFiles = do --- refArchive <- asks envRefArchive --- distArchive <- asks envDistArchive --- let allEntries = nub $ filesInArchive refArchive ++ filesInArchive distArchive --- return $ filter (isPrefixOf "ppt/media") allEntries - - --- copyFileToArchiveIfExists :: PandocMonad m => Archive -> FilePath -> P m Archive --- copyFileToArchiveIfExists arch fp = do --- refArchive <- asks envRefArchive --- distArchive <- asks envDistArchive --- case findEntryByPath fp refArchive `mplus` findEntryByPath fp distArchive of --- Nothing -> return $ arch --- Just e -> return $ addEntryToArchive e arch - inheritedPatterns :: [Pattern] inheritedPatterns = map compile [ "_rels/.rels" , "docProps/app.xml" @@ -827,68 +811,43 @@ patternToFilePaths pat = do patternsToFilePaths :: PandocMonad m => [Pattern] -> P m [FilePath] patternsToFilePaths pats = concat <$> mapM patternToFilePaths pats --- requiredFiles :: [FilePath] --- requiredFiles = inheritedFiles - --- inheritedFiles :: [FilePath] --- inheritedFiles = [ "_rels/.rels" --- , "docProps/app.xml" --- , "docProps/core.xml" --- , "ppt/slideLayouts/slideLayout4.xml" --- , "ppt/slideLayouts/_rels/slideLayout9.xml.rels" --- , "ppt/slideLayouts/_rels/slideLayout2.xml.rels" --- , "ppt/slideLayouts/_rels/slideLayout10.xml.rels" --- , "ppt/slideLayouts/_rels/slideLayout1.xml.rels" --- , "ppt/slideLayouts/_rels/slideLayout3.xml.rels" --- , "ppt/slideLayouts/_rels/slideLayout5.xml.rels" --- , "ppt/slideLayouts/_rels/slideLayout7.xml.rels" --- , "ppt/slideLayouts/_rels/slideLayout8.xml.rels" --- , "ppt/slideLayouts/_rels/slideLayout11.xml.rels" --- , "ppt/slideLayouts/_rels/slideLayout4.xml.rels" --- , "ppt/slideLayouts/_rels/slideLayout6.xml.rels" --- , "ppt/slideLayouts/slideLayout2.xml" --- , "ppt/slideLayouts/slideLayout8.xml" --- , "ppt/slideLayouts/slideLayout11.xml" --- , "ppt/slideLayouts/slideLayout3.xml" --- , "ppt/slideLayouts/slideLayout6.xml" --- , "ppt/slideLayouts/slideLayout9.xml" --- , "ppt/slideLayouts/slideLayout5.xml" --- , "ppt/slideLayouts/slideLayout7.xml" --- , "ppt/slideLayouts/slideLayout1.xml" --- , "ppt/slideLayouts/slideLayout10.xml" --- -- , "ppt/_rels/presentation.xml.rels" --- , "ppt/theme/theme1.xml" --- , "ppt/presProps.xml" --- -- , "ppt/slides/_rels/slide1.xml.rels" --- -- , "ppt/slides/_rels/slide2.xml.rels" --- -- This is the one we're --- -- going to build --- -- , "ppt/slides/slide2.xml" --- -- , "ppt/slides/slide1.xml" --- , "ppt/viewProps.xml" --- , "ppt/tableStyles.xml" --- , "ppt/slideMasters/_rels/slideMaster1.xml.rels" --- , "ppt/slideMasters/slideMaster1.xml" --- -- , "ppt/presentation.xml" --- -- , "[Content_Types].xml" --- ] - --- -- Here are some that might not be there. We won't fail if they're not --- possibleInheritedFiles :: [FilePath] --- possibleInheritedFiles = [ "ppt/theme/_rels/theme1.xml.rels" ] +-- Here are the files we'll require to make a Powerpoint document. If +-- any of these are missing, we should error out of our build. +requiredFiles :: [FilePath] +requiredFiles = [ "_rels/.rels" + , "docProps/app.xml" + , "docProps/core.xml" + , "ppt/presProps.xml" + , "ppt/slideLayouts/slideLayout1.xml" + , "ppt/slideLayouts/_rels/slideLayout1.xml.rels" + , "ppt/slideLayouts/slideLayout2.xml" + , "ppt/slideLayouts/_rels/slideLayout2.xml.rels" + , "ppt/slideLayouts/slideLayout3.xml" + , "ppt/slideLayouts/_rels/slideLayout3.xml.rels" + , "ppt/slideLayouts/slideLayout4.xml" + , "ppt/slideLayouts/_rels/slideLayout4.xml.rels" + , "ppt/slideMasters/slideMaster1.xml" + , "ppt/slideMasters/_rels/slideMaster1.xml.rels" + , "ppt/theme/theme1.xml" + , "ppt/viewProps.xml" + , "ppt/tableStyles.xml" + ] presentationToArchive :: PandocMonad m => Presentation -> P m Archive presentationToArchive p@(Presentation _ slides) = do filePaths <- patternsToFilePaths inheritedPatterns - newArch' <- foldM copyFileToArchive emptyArchive filePaths - -- set the template media to the relevant fps: + -- make sure all required files are available: + let missingFiles = filter (\fp -> not (fp `elem` filePaths)) requiredFiles + unless (null missingFiles) + (throwError $ + PandocSomeError $ + "The following required files are missing:\n" ++ + (unlines $ map (" " ++) missingFiles) + ) - -- we register any media fp in the filepaths - -- mediaDir <- getMediaFiles - -- newArch' <- foldM copyFileToArchiveIfExists newArch $ - -- possibleInheritedFiles ++ mediaDir + newArch' <- foldM copyFileToArchive emptyArchive filePaths -- presentation entry and rels. We have to do the rels first to make -- sure we know the correct offset for the rIds. presEntry <- presentationToPresEntry p -- cgit v1.2.3 From a842d3ae7d511267dddb7ec1fef04e58495d08ce Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sat, 13 Jan 2018 09:08:28 -0500 Subject: Powerpoint writer: Handle (sub)headers above slidelevel correctly. Above the slidelevel, subheaders will be printed in bold and given a bit of extra space before them. Note that at the moment, no distinction is made between levels of headers above the slide header, though that can be changed. (It has to be changed in pandoc, since PowerPoint has no concept of paragraph or character classes.) This allows us to clean up the code as well: the code in `blockToParagraphs` since it will only touch content blocks, and therefore will not deal with headers at or below the slidelevel. --- src/Text/Pandoc/Writers/Powerpoint.hs | 56 ++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 27 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index 2a46e40fe..b6fdf0883 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -252,9 +252,6 @@ data Paragraph = Paragraph { paraProps :: ParaProps , paraElems :: [ParaElem] } deriving (Show, Eq) -data HeaderType = TitleHeader | SlideHeader | InternalHeader Int - deriving (Show, Eq) - autoNumberingToType :: ListAttributes -> String autoNumberingToType (_, numStyle, numDelim) = typeString ++ delimString @@ -279,21 +276,21 @@ data BulletType = Bullet data Algnment = AlgnLeft | AlgnRight | AlgnCenter deriving (Show, Eq) -data ParaProps = ParaProps { pPropHeaderType :: Maybe HeaderType - , pPropMarginLeft :: Maybe Pixels +data ParaProps = ParaProps { pPropMarginLeft :: Maybe Pixels , pPropMarginRight :: Maybe Pixels , pPropLevel :: Int , pPropBullet :: Maybe BulletType , pPropAlign :: Maybe Algnment + , pPropSpaceBefore :: Maybe Pixels } deriving (Show, Eq) instance Default ParaProps where - def = ParaProps { pPropHeaderType = Nothing - , pPropMarginLeft = Just 0 + def = ParaProps { pPropMarginLeft = Just 0 , pPropMarginRight = Just 0 , pPropLevel = 0 , pPropBullet = Nothing , pPropAlign = Nothing + , pPropSpaceBefore = Nothing } newtype TeXString = TeXString {unTeXString :: String} @@ -439,20 +436,17 @@ blockToParagraphs (BlockQuote blks) = concatMapM blockToParagraphs blks -- TODO: work out the format blockToParagraphs (RawBlock _ _) = return [] -blockToParagraphs (Header n (ident, _, _) ils) = do - -- Note that this function will only touch headers that are not at - -- the beginning of slides -- all the rest will be taken care of by +blockToParagraphs (Header _ (ident, _, _) ils) = do + -- Note that this function only deals with content blocks, so it + -- will only touch headers that are above the current slide level -- + -- slides at or below the slidelevel will be taken care of by -- `blocksToSlide'`. We have the register anchors in both of them. registerAnchorId ident - slideLevel <- asks envSlideLevel - parElems <- inlinesToParElems ils - -- For the time being we're not doing headers inside of bullets, but - -- we might change that. - let headerType = case n `compare` slideLevel of - LT -> TitleHeader - EQ -> SlideHeader - GT -> InternalHeader (n - slideLevel) - return [Paragraph def{pPropHeaderType = Just headerType} parElems] + -- we set the subeader to bold + parElems <- local (\e->e{envRunProps = (envRunProps e){rPropBold=True}}) $ + inlinesToParElems ils + -- and give it a bit of space before it. + return [Paragraph def{pPropSpaceBefore = Just 30} parElems] blockToParagraphs (BulletList blksLst) = do pProps <- asks envParaProps let lvl = pPropLevel pProps @@ -873,14 +867,15 @@ combineShapes (s : []) = [s] combineShapes (pic@(Pic _ _ _ _) : ss) = pic : combineShapes ss combineShapes ((TextBox []) : ss) = combineShapes ss combineShapes (s : TextBox [] : ss) = combineShapes (s : ss) -combineShapes (s@(TextBox (p:ps)) : s'@(TextBox (p':ps')) : ss) - | pPropHeaderType (paraProps p) == Just TitleHeader || - pPropHeaderType (paraProps p) == Just SlideHeader = - TextBox [p] : (combineShapes $ TextBox ps : s' : ss) - | pPropHeaderType (paraProps p') == Just TitleHeader || - pPropHeaderType (paraProps p') == Just SlideHeader = - s : TextBox [p'] : (combineShapes $ TextBox ps' : ss) - | otherwise = combineShapes $ TextBox ((p:ps) ++ (p':ps')) : ss +combineShapes ((TextBox (p:ps)) : (TextBox (p':ps')) : ss) = + -- | pPropHeaderType (paraProps p) == Just TitleHeader || + -- pPropHeaderType (paraProps p) == Just SlideHeader = + -- TextBox [p] : (combineShapes $ TextBox ps : s' : ss) + -- | pPropHeaderType (paraProps p') == Just TitleHeader || + -- pPropHeaderType (paraProps p') == Just SlideHeader = + -- s : TextBox [p'] : (combineShapes $ TextBox ps' : ss) + -- | otherwise = combineShapes $ TextBox ((p:ps) ++ (p':ps')) : ss + combineShapes $ TextBox ((p:ps) ++ (p':ps')) : ss combineShapes (s:ss) = s : combineShapes ss -------------------------------------------------- @@ -1310,6 +1305,13 @@ paragraphToElement par = do Nothing -> [] ) props = [] ++ + (case pPropSpaceBefore $ paraProps par of + Just px -> [mknode "a:spcBef" [] [ + mknode "a:spcPts" [("val", show $ 100 * px)] () + ] + ] + Nothing -> [] + ) ++ (case pPropBullet $ paraProps par of Just Bullet -> [] Just (AutoNumbering attrs') -> -- cgit v1.2.3 From e08776b9d51a90e9f948f8f5dd2c3c2588d50942 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sat, 13 Jan 2018 09:34:03 -0500 Subject: Powerpoint writer: code cleanup Last commit accidentally left commented-out code in. --- src/Text/Pandoc/Writers/Powerpoint.hs | 7 ------- 1 file changed, 7 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index b6fdf0883..86f07d9c6 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -868,13 +868,6 @@ combineShapes (pic@(Pic _ _ _ _) : ss) = pic : combineShapes ss combineShapes ((TextBox []) : ss) = combineShapes ss combineShapes (s : TextBox [] : ss) = combineShapes (s : ss) combineShapes ((TextBox (p:ps)) : (TextBox (p':ps')) : ss) = - -- | pPropHeaderType (paraProps p) == Just TitleHeader || - -- pPropHeaderType (paraProps p) == Just SlideHeader = - -- TextBox [p] : (combineShapes $ TextBox ps : s' : ss) - -- | pPropHeaderType (paraProps p') == Just TitleHeader || - -- pPropHeaderType (paraProps p') == Just SlideHeader = - -- s : TextBox [p'] : (combineShapes $ TextBox ps' : ss) - -- | otherwise = combineShapes $ TextBox ((p:ps) ++ (p':ps')) : ss combineShapes $ TextBox ((p:ps) ++ (p':ps')) : ss combineShapes (s:ss) = s : combineShapes ss -- cgit v1.2.3 From 944ed5e0987e5069bfe70504e948f45a84f57324 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sat, 13 Jan 2018 14:53:56 -0500 Subject: Powerpoint writer: read presentation size from reference file. Our presentation size is now dependent on the reference/template file we use. This will make it easier to set different output sizes by supplying different reference files. The alternative (allowing a user to explicitly set output size regardless of the template) will lead to too many thorny issues, as explicitly set sizes at the various level of powerpoint layout would have to be reset. --- src/Text/Pandoc/Writers/Powerpoint.hs | 74 +++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 29 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index 86f07d9c6..17ffe611c 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -82,6 +82,12 @@ writePowerpoint opts (Pandoc meta blks) = do utctime <- P.getCurrentTime + presSize <- case getPresentationSize refArchive distArchive of + Just sz -> return sz + Nothing -> throwError $ + PandocSomeError $ + "Could not determine presentation size" + let env = def { envMetadata = meta , envRefArchive = refArchive , envDistArchive = distArchive @@ -90,6 +96,7 @@ writePowerpoint opts (Pandoc meta blks) = do , envSlideLevel = case writerSlideLevel opts of Just n -> n Nothing -> getSlideLevel blks' + , envPresentationSize = presSize } let st = def { stMediaGlobalIds = initialGlobalIds refArchive distArchive @@ -110,7 +117,7 @@ data WriterEnv = WriterEnv { envMetadata :: Meta , envDistArchive :: Archive , envUTCTime :: UTCTime , envOpts :: WriterOptions - , envPresentationSize :: PresentationSize + , envPresentationSize :: (Integer, Integer) , envSlideHasHeader :: Bool , envInList :: Bool , envInNoteSlide :: Bool @@ -131,7 +138,7 @@ instance Default WriterEnv where , envDistArchive = emptyArchive , envUTCTime = posixSecondsToUTCTime 0 , envOpts = def - , envPresentationSize = def + , envPresentationSize = (720, 540) , envSlideHasHeader = False , envInList = False , envInNoteSlide = False @@ -183,6 +190,19 @@ initialGlobalIds refArchive distArchive = in M.fromList $ mapMaybe go mediaPaths +getPresentationSize :: Archive -> Archive -> Maybe (Integer, Integer) +getPresentationSize refArchive distArchive = do + entry <- findEntryByPath "ppt/presentation.xml" refArchive `mplus` + findEntryByPath "ppt/presentation.xml" distArchive + presElement <- parseXMLDoc $ UTF8.toStringLazy $ fromEntry entry + let ns = elemToNameSpaces presElement + sldSize <- findChild (elemName ns "p" "sldSz") presElement + cxS <- findAttr (QName "cx" Nothing Nothing) sldSize + cyS <- findAttr (QName "cy" Nothing Nothing) sldSize + (cx, _) <- listToMaybe $ reads cxS :: Maybe (Integer, String) + (cy, _) <- listToMaybe $ reads cyS :: Maybe (Integer, String) + return (cx `div` 12700, cy `div` 12700) + type P m = ReaderT WriterEnv (StateT WriterState m) runP :: Monad m => WriterEnv -> WriterState -> P m a -> m a @@ -190,28 +210,28 @@ runP env st p = evalStateT (runReaderT p env) st type Pixels = Integer -data Presentation = Presentation PresentationSize [Slide] +data Presentation = Presentation [Slide] deriving (Show) -data PresentationSize = PresentationSize { presSizeWidth :: Pixels - , presSizeRatio :: PresentationRatio - } - deriving (Show, Eq) +-- data PresentationSize = PresentationSize { presSizeWidth :: Pixels +-- , presSizeRatio :: PresentationRatio +-- } +-- deriving (Show, Eq) -data PresentationRatio = Ratio4x3 - | Ratio16x9 - | Ratio16x10 - deriving (Show, Eq) +-- data PresentationRatio = Ratio4x3 +-- | Ratio16x9 +-- | Ratio16x10 +-- deriving (Show, Eq) -- Note that right now we're only using Ratio4x3. -getPageHeight :: PresentationSize -> Pixels -getPageHeight sz = case presSizeRatio sz of - Ratio4x3 -> floor (((fromInteger (presSizeWidth sz)) / 4) * 3 :: Double) - Ratio16x9 -> floor (((fromInteger (presSizeWidth sz)) / 16) * 9 :: Double) - Ratio16x10 -> floor (((fromInteger (presSizeWidth sz)) / 16) * 10 :: Double) +-- getPageHeight :: PresentationSize -> Pixels +-- getPageHeight sz = case presSizeRatio sz of +-- Ratio4x3 -> floor (((fromInteger (presSizeWidth sz)) / 4) * 3 :: Double) +-- Ratio16x9 -> floor (((fromInteger (presSizeWidth sz)) / 16) * 9 :: Double) +-- Ratio16x10 -> floor (((fromInteger (presSizeWidth sz)) / 16) * 10 :: Double) -instance Default PresentationSize where - def = PresentationSize 720 Ratio4x3 +-- instance Default PresentationSize where +-- def = PresentationSize 720 Ratio4x3 data Slide = MetadataSlide { metadataSlideTitle :: [ParaElem] , metadataSlideSubtitle :: [ParaElem] @@ -520,7 +540,7 @@ blockToShape (Para (il:_)) | Link _ (il':_) target <- il Pic def{picPropLink = Just target} url attr <$> (inlinesToParElems ils) blockToShape (Table caption algn _ hdrCells rows) = do caption' <- inlinesToParElems caption - pageWidth <- presSizeWidth <$> asks envPresentationSize + (pageWidth, _) <- asks envPresentationSize hdrCells' <- rowToParagraphs algn hdrCells rows' <- mapM (rowToParagraphs algn) rows let tblPr = if null hdrCells @@ -641,8 +661,6 @@ blocksToSlide blks = do slideLevel <- asks envSlideLevel blocksToSlide' slideLevel blks - - makeNoteEntry :: Int -> [Block] -> [Block] makeNoteEntry n blks = let enum = Str (show n ++ ".") @@ -763,9 +781,8 @@ blocksToPresentation blks = do }) (blocksToSlide $ notesSlideBlocks) return [notesSlide] - presSize <- asks envPresentationSize return $ - Presentation presSize $ + Presentation $ metadataslides ++ tocSlides ++ bodyslides ++ notesSlides -------------------------------------------------------------------- @@ -829,7 +846,7 @@ requiredFiles = [ "_rels/.rels" presentationToArchive :: PandocMonad m => Presentation -> P m Archive -presentationToArchive p@(Presentation _ slides) = do +presentationToArchive p@(Presentation slides) = do filePaths <- patternsToFilePaths inheritedPatterns -- make sure all required files are available: @@ -1126,8 +1143,7 @@ makePicElement :: PandocMonad m -> P m Element makePicElement picProps mInfo attr = do opts <- asks envOpts - pageWidth <- presSizeWidth <$> asks envPresentationSize - pageHeight <- getPageHeight <$> asks envPresentationSize + (pageWidth, pageHeight) <- asks envPresentationSize hasHeader <- asks envSlideHasHeader let hasCaption = mInfoCaption mInfo (imgBytes, _) <- P.fetchItem (mInfoFilePath mInfo) @@ -1621,7 +1637,7 @@ getRels = do return $ mapMaybe elementToRel relElems presentationToRels :: PandocMonad m => Presentation -> P m [Relationship] -presentationToRels (Presentation _ slides) = do +presentationToRels (Presentation slides) = do mySlideRels <- mapM (\(s, n) -> slideToPresRel s n) $ zip slides [1..] rels <- getRels let relsWithoutSlides = filter (\r -> relType r /= "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide") rels @@ -1749,7 +1765,7 @@ slideToSldIdElement slide idNum = do return $ mknode "p:sldId" [("id", id'), ("r:id", rId)] () presentationToSldIdLst :: PandocMonad m => Presentation -> P m Element -presentationToSldIdLst (Presentation _ slides) = do +presentationToSldIdLst (Presentation slides) = do ids <- mapM (\(s,n) -> slideToSldIdElement s n) (zip slides [1..]) return $ mknode "p:sldIdLst" [] ids @@ -1845,7 +1861,7 @@ mediaContentType mInfo | otherwise = Nothing presentationToContentTypes :: PandocMonad m => Presentation -> P m ContentTypes -presentationToContentTypes (Presentation _ slides) = do +presentationToContentTypes (Presentation slides) = do mediaInfos <- (mconcat . M.elems) <$> gets stMediaIds filePaths <- patternsToFilePaths inheritedPatterns let mediaFps = filter (match (compile "ppt/media/image*")) filePaths -- cgit v1.2.3 From 44222e0373f47c986833a609271d495d55ff48de Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 13 Jan 2018 12:10:52 -0800 Subject: LaTeX reader: allow macro definitions inside macros. Previously we went into an infinite loop with ``` \newcommand{\noop}[1]{#1} \noop{\newcommand{\foo}[1]{#1}} \foo{hi} ``` See #4253. --- src/Text/Pandoc/Readers/LaTeX.hs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 62d240688..d9b188606 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -442,19 +442,22 @@ doMacros n = do Just o -> (:) <$> option o bracketedToks <*> count (numargs - 1) getarg - let addTok (Tok _ (Arg i) _) acc | i > 0 - , i <= numargs = - foldr addTok acc (args !! (i - 1)) + -- first boolean param is true if we're tokenizing + -- an argument (in which case we don't want to + -- expand #1 etc.) + let addTok False (Tok _ (Arg i) _) acc | i > 0 + , i <= numargs = + foldr (addTok True) acc (args !! (i - 1)) -- add space if needed after control sequence -- see #4007 - addTok (Tok _ (CtrlSeq x) txt) + addTok _ (Tok _ (CtrlSeq x) txt) acc@(Tok _ Word _ : _) | not (T.null txt) && (isLetter (T.last txt)) = Tok spos (CtrlSeq x) (txt <> " ") : acc - addTok t acc = setpos spos t : acc + addTok _ t acc = setpos spos t : acc ts' <- getInput - setInput $ foldr addTok ts' newtoks + setInput $ foldr (addTok False) ts' newtoks case expansionPoint of ExpandWhenUsed -> if n > 20 -- detect macro expansion loops -- cgit v1.2.3 From 50b64bcf1c61e21402611d50fb23609b62b5c9c2 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sat, 13 Jan 2018 15:56:30 -0500 Subject: Powerpoint writer: Improve image handling. We now determine image and caption placement by getting the dimensions of the content box in a given layout. This allows for images to be correctly sized and positioned in a different template. Note that iamges without captions and headers are no longer full-screened. We can't do this dependably in different layouts, because we don't know where the header is (it could be to the side of the content, for example). --- src/Text/Pandoc/Writers/Powerpoint.hs | 377 ++++++++++++++++++++-------------- 1 file changed, 224 insertions(+), 153 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index 17ffe611c..ebac15db4 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -213,26 +213,6 @@ type Pixels = Integer data Presentation = Presentation [Slide] deriving (Show) --- data PresentationSize = PresentationSize { presSizeWidth :: Pixels --- , presSizeRatio :: PresentationRatio --- } --- deriving (Show, Eq) - --- data PresentationRatio = Ratio4x3 --- | Ratio16x9 --- | Ratio16x10 --- deriving (Show, Eq) - --- Note that right now we're only using Ratio4x3. --- getPageHeight :: PresentationSize -> Pixels --- getPageHeight sz = case presSizeRatio sz of --- Ratio4x3 -> floor (((fromInteger (presSizeWidth sz)) / 4) * 3 :: Double) --- Ratio16x9 -> floor (((fromInteger (presSizeWidth sz)) / 16) * 9 :: Double) --- Ratio16x10 -> floor (((fromInteger (presSizeWidth sz)) / 16) * 10 :: Double) - --- instance Default PresentationSize where --- def = PresentationSize 720 Ratio4x3 - data Slide = MetadataSlide { metadataSlideTitle :: [ParaElem] , metadataSlideSubtitle :: [ParaElem] , metadataSlideAuthors :: [[ParaElem]] @@ -917,12 +897,71 @@ shapeHasName ns name element nm == name | otherwise = False +shapeHasId :: NameSpaces -> String -> Element -> Bool +shapeHasId ns ident element + | Just nvSpPr <- findChild (elemName ns "p" "nvSpPr") element + , Just cNvPr <- findChild (elemName ns "p" "cNvPr") nvSpPr + , Just nm <- findAttr (QName "id" Nothing Nothing) cNvPr = + nm == ident + | otherwise = False + getContentShape :: NameSpaces -> Element -> Maybe Element getContentShape ns spTreeElem | isElem ns "p" "spTree" spTreeElem = filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasName ns "Content Placeholder 2" e)) spTreeElem | otherwise = Nothing +getShapeDimensions :: NameSpaces + -> Element + -> Maybe ((Integer, Integer), (Integer, Integer)) +getShapeDimensions ns element + | isElem ns "p" "sp" element = do + spPr <- findChild (elemName ns "p" "spPr") element + xfrm <- findChild (elemName ns "a" "xfrm") spPr + off <- findChild (elemName ns "a" "off") xfrm + xS <- findAttr (QName "x" Nothing Nothing) off + yS <- findAttr (QName "y" Nothing Nothing) off + ext <- findChild (elemName ns "a" "ext") xfrm + cxS <- findAttr (QName "cx" Nothing Nothing) ext + cyS <- findAttr (QName "cy" Nothing Nothing) ext + (x, _) <- listToMaybe $ reads xS + (y, _) <- listToMaybe $ reads yS + (cx, _) <- listToMaybe $ reads cxS + (cy, _) <- listToMaybe $ reads cyS + return $ ((x `div` 12700, y `div` 12700), (cx `div` 12700, cy `div` 12700)) + | otherwise = Nothing + + +getMasterShapeDimensionsById :: String + -> Element + -> Maybe ((Integer, Integer), (Integer, Integer)) +getMasterShapeDimensionsById ident master = do + let ns = elemToNameSpaces master + cSld <- findChild (elemName ns "p" "cSld") master + spTree <- findChild (elemName ns "p" "spTree") cSld + sp <- filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasId ns ident e)) spTree + getShapeDimensions ns sp + +getContentShapeSize :: NameSpaces + -> Element + -> Element + -> Maybe ((Integer, Integer), (Integer, Integer)) +getContentShapeSize ns layout master + | isElem ns "p" "sldLayout" layout + , Just cSld <- findChild (elemName ns "p" "cSld") layout + , Just spTree <- findChild (elemName ns "p" "spTree") cSld + , Just sp <- getContentShape ns spTree + , Just sz <- getShapeDimensions ns sp = Just sz + | isElem ns "p" "sldLayout" layout + , Just cSld <- findChild (elemName ns "p" "cSld") layout + , Just spTree <- findChild (elemName ns "p" "spTree") cSld + , Just sp <- getContentShape ns spTree + , Just ident <- findChild (elemName ns "p" "nvSpPr") sp >>= + findChild (elemName ns "p" "cNvPr") >>= + findAttr (QName "id" Nothing Nothing) + , Just sz <- getMasterShapeDimensionsById ident master = Just sz + | otherwise = Nothing + replaceNamedChildren :: NameSpaces -> String -> String @@ -1036,26 +1075,26 @@ makeMediaEntries = do let allInfos = mconcat $ M.elems mediaInfos mapM makeMediaEntry allInfos --- | Scales the image to fit the page --- sizes are passed in emu -fitToPage' :: (Double, Double) -- image size in emu - -> Integer -- pageWidth - -> Integer -- pageHeight - -> (Integer, Integer) -- imagesize -fitToPage' (x, y) pageWidth pageHeight - -- Fixes width to the page width and scales the height - | x <= fromIntegral pageWidth && y <= fromIntegral pageHeight = - (floor x, floor y) - | x / fromIntegral pageWidth > y / fromIntegral pageWidth = - (pageWidth, floor $ ((fromIntegral pageWidth) / x) * y) - | otherwise = - (floor $ ((fromIntegral pageHeight) / y) * x, pageHeight) - -positionImage :: (Double, Double) -> Integer -> Integer -> (Integer, Integer) -positionImage (x, y) pageWidth pageHeight = - let (x', y') = fitToPage' (x, y) pageWidth pageHeight - in - ((pageWidth - x') `div` 2, (pageHeight - y') `div` 2) +-- -- | Scales the image to fit the page +-- -- sizes are passed in emu +-- fitToPage' :: (Double, Double) -- image size in emu +-- -> Integer -- pageWidth +-- -> Integer -- pageHeight +-- -> (Integer, Integer) -- imagesize +-- fitToPage' (x, y) pageWidth pageHeight +-- -- Fixes width to the page width and scales the height +-- | x <= fromIntegral pageWidth && y <= fromIntegral pageHeight = +-- (floor x, floor y) +-- | x / fromIntegral pageWidth > y / fromIntegral pageWidth = +-- (pageWidth, floor $ ((fromIntegral pageWidth) / x) * y) +-- | otherwise = +-- (floor $ ((fromIntegral pageHeight) / y) * x, pageHeight) + +-- positionImage :: (Double, Double) -> Integer -> Integer -> (Integer, Integer) +-- positionImage (x, y) pageWidth pageHeight = +-- let (x', y') = fitToPage' (x, y) pageWidth pageHeight +-- in +-- ((pageWidth - x') `div` 2, (pageHeight - y') `div` 2) getMaster :: PandocMonad m => P m Element getMaster = do @@ -1067,52 +1106,57 @@ getMaster = do -- image goes underneath it. We only use this in a content slide if it -- has a header. -getHeaderSize :: PandocMonad m => P m ((Integer, Integer), (Integer, Integer)) -getHeaderSize = do - master <- getMaster - let ns = elemToNameSpaces master - sps = [master] >>= - findChildren (elemName ns "p" "cSld") >>= - findChildren (elemName ns "p" "spTree") >>= - findChildren (elemName ns "p" "sp") - mbXfrm = - listToMaybe (filter (shapeHasName ns "Title Placeholder 1") sps) >>= - findChild (elemName ns "p" "spPr") >>= - findChild (elemName ns "a" "xfrm") - xoff = mbXfrm >>= - findChild (elemName ns "a" "off") >>= - findAttr (QName "x" Nothing Nothing) >>= - (listToMaybe . (\s -> reads s :: [(Integer, String)])) - yoff = mbXfrm >>= - findChild (elemName ns "a" "off") >>= - findAttr (QName "y" Nothing Nothing) >>= - (listToMaybe . (\s -> reads s :: [(Integer, String)])) - xext = mbXfrm >>= - findChild (elemName ns "a" "ext") >>= - findAttr (QName "cx" Nothing Nothing) >>= - (listToMaybe . (\s -> reads s :: [(Integer, String)])) - yext = mbXfrm >>= - findChild (elemName ns "a" "ext") >>= - findAttr (QName "cy" Nothing Nothing) >>= - (listToMaybe . (\s -> reads s :: [(Integer, String)])) - off = case xoff of - Just (xoff', _) | Just (yoff',_) <- yoff -> (xoff', yoff') - _ -> (1043490, 1027664) - ext = case xext of - Just (xext', _) | Just (yext',_) <- yext -> (xext', yext') - _ -> (7024744, 1143000) - return $ (off, ext) - +-- getHeaderSize :: PandocMonad m => P m ((Integer, Integer), (Integer, Integer)) +-- getHeaderSize = do +-- master <- getMaster +-- let ns = elemToNameSpaces master +-- sps = [master] >>= +-- findChildren (elemName ns "p" "cSld") >>= +-- findChildren (elemName ns "p" "spTree") >>= +-- findChildren (elemName ns "p" "sp") +-- mbXfrm = +-- listToMaybe (filter (shapeHasName ns "Title Placeholder 1") sps) >>= +-- findChild (elemName ns "p" "spPr") >>= +-- findChild (elemName ns "a" "xfrm") +-- xoff = mbXfrm >>= +-- findChild (elemName ns "a" "off") >>= +-- findAttr (QName "x" Nothing Nothing) >>= +-- (listToMaybe . (\s -> reads s :: [(Integer, String)])) +-- yoff = mbXfrm >>= +-- findChild (elemName ns "a" "off") >>= +-- findAttr (QName "y" Nothing Nothing) >>= +-- (listToMaybe . (\s -> reads s :: [(Integer, String)])) +-- xext = mbXfrm >>= +-- findChild (elemName ns "a" "ext") >>= +-- findAttr (QName "cx" Nothing Nothing) >>= +-- (listToMaybe . (\s -> reads s :: [(Integer, String)])) +-- yext = mbXfrm >>= +-- findChild (elemName ns "a" "ext") >>= +-- findAttr (QName "cy" Nothing Nothing) >>= +-- (listToMaybe . (\s -> reads s :: [(Integer, String)])) +-- off = case xoff of +-- Just (xoff', _) | Just (yoff',_) <- yoff -> (xoff', yoff') +-- _ -> (1043490, 1027664) +-- ext = case xext of +-- Just (xext', _) | Just (yext',_) <- yext -> (xext', yext') +-- _ -> (7024744, 1143000) +-- return $ (off, ext) -- Hard-coded for now -captionPosition :: ((Integer, Integer), (Integer, Integer)) -captionPosition = ((457200, 6061972), (8229600, 527087)) +-- captionPosition :: ((Integer, Integer), (Integer, Integer)) +-- captionPosition = ((457200, 6061972), (8229600, 527087)) + +captionHeight :: Integer +captionHeight = 40 -createCaption :: PandocMonad m => [ParaElem] -> P m Element -createCaption paraElements = do +createCaption :: PandocMonad m + => ((Integer, Integer), (Integer, Integer)) + -> [ParaElem] + -> P m Element +createCaption contentShapeDimensions paraElements = do let para = Paragraph def{pPropAlign = Just AlgnCenter} paraElements elements <- mapM paragraphToElement [para] - let ((x, y), (cx, cy)) = captionPosition + let ((x, y), (cx, cy)) = contentShapeDimensions let txBody = mknode "p:txBody" [] $ [mknode "a:bodyPr" [] (), mknode "a:lstStyle" [] ()] ++ elements return $ @@ -1123,8 +1167,10 @@ createCaption paraElements = do ] , mknode "p:spPr" [] [ mknode "a:xfrm" [] - [ mknode "a:off" [("x", show x), ("y", show y)] () - , mknode "a:ext" [("cx", show cx), ("cy", show cy)] () + [ mknode "a:off" [("x", show $ 12700 * x), + ("y", show $ 12700 * (y + cy - captionHeight))] () + , mknode "a:ext" [("cx", show $ 12700 * cx), + ("cy", show $ 12700 * captionHeight)] () ] , mknode "a:prstGeom" [("prst", "rect")] [ mknode "a:avLst" [] () @@ -1134,37 +1180,41 @@ createCaption paraElements = do , txBody ] --- Largely lifted from inlineToOpenXML' in T.P.W.Docx. Can't be easily --- abstracted because of some different namespaces and monads. TODO. -makePicElement :: PandocMonad m - => PicProps - -> MediaInfo - -> Text.Pandoc.Definition.Attr - -> P m Element -makePicElement picProps mInfo attr = do +makePicElements :: PandocMonad m + => Element + -> PicProps + -> MediaInfo + -> Text.Pandoc.Definition.Attr + -> [ParaElem] + -> P m [Element] +makePicElements layout picProps mInfo _ alt = do opts <- asks envOpts (pageWidth, pageHeight) <- asks envPresentationSize - hasHeader <- asks envSlideHasHeader + -- hasHeader <- asks envSlideHasHeader let hasCaption = mInfoCaption mInfo (imgBytes, _) <- P.fetchItem (mInfoFilePath mInfo) - -- We're not using x exts - ((hXoff, hYoff), (_, hYext)) <- if hasHeader - then getHeaderSize - else return ((0, 0), (0, 0)) - - let ((capX, capY), (_, _)) = if hasCaption - then captionPosition - else ((0,0), (0,0)) - let (xpt,ypt) = desiredSizeInPoints opts attr - (either (const def) id (imageSize opts imgBytes)) - -- 12700 emu = 1 pt - let (xemu,yemu) = fitToPage' (xpt * 12700, ypt * 12700) - ((pageWidth * 12700) - (2 * hXoff) - (2 * capX)) - ((if hasCaption then capY else (pageHeight * 12700)) - (hYoff + hYext)) - (xoff, yoff) = positionImage (xpt * 12700, ypt * 12700) (pageWidth * 12700) (pageHeight * 12700) - xoff' = if hasHeader then xoff + hXoff else xoff - xoff'' = if hasCaption then xoff' + capX else xoff' - yoff' = if hasHeader then hYoff + hYext else yoff + let (pxX, pxY) = case imageSize opts imgBytes of + Right sz -> sizeInPixels $ sz + Left _ -> sizeInPixels $ def + master <- getMaster + let ns = elemToNameSpaces layout + let ((x, y), (cx, cytmp)) = case getContentShapeSize ns layout master of + Just dims -> dims + Nothing -> ((0, 0), (pageWidth, pageHeight)) + + cy = if hasCaption then cytmp - captionHeight else cytmp + + let imgRatio = fromIntegral pxX / fromIntegral pxY :: Double + boxRatio = fromIntegral cx / fromIntegral cy :: Double + (dimX, dimY) = if imgRatio > boxRatio + then (fromIntegral cx, fromIntegral cx / imgRatio) + else (fromIntegral cy * imgRatio, fromIntegral cy) + + (dimX', dimY') = (round dimX * 12700, round dimY * 12700) :: (Integer, Integer) + (xoff, yoff) = (fromIntegral x + (fromIntegral cx - dimX) / 2, + fromIntegral y + (fromIntegral cy - dimY) / 2) + (xoff', yoff') = (round xoff * 12700, round yoff * 12700) :: (Integer, Integer) + let cNvPicPr = mknode "p:cNvPicPr" [] $ mknode "a:picLocks" [("noGrp","1") ,("noChangeAspect","1")] () @@ -1185,9 +1235,9 @@ makePicElement picProps mInfo attr = do , mknode "a:stretch" [] $ mknode "a:fillRect" [] () ] let xfrm = mknode "a:xfrm" [] - [ mknode "a:off" [("x",show xoff''), ("y",show yoff')] () - , mknode "a:ext" [("cx",show xemu) - ,("cy",show yemu)] () ] + [ mknode "a:off" [("x",show xoff'), ("y",show yoff')] () + , mknode "a:ext" [("cx",show dimX') + ,("cy",show dimY')] () ] let prstGeom = mknode "a:prstGeom" [("prst","rect")] $ mknode "a:avLst" [] () let ln = mknode "a:ln" [("w","9525")] @@ -1196,11 +1246,17 @@ makePicElement picProps mInfo attr = do , mknode "a:tailEnd" [] () ] let spPr = mknode "p:spPr" [("bwMode","auto")] [xfrm, prstGeom, mknode "a:noFill" [] (), ln] - return $ - mknode "p:pic" [] - [ nvPicPr - , blipFill - , spPr ] + + let picShape = mknode "p:pic" [] + [ nvPicPr + , blipFill + , spPr ] + + -- And now, maybe create the caption: + if hasCaption + then do cap <- createCaption ((x, y), (cx, cytmp)) alt + return [picShape, cap] + else return [picShape] -- Currently hardcoded, until I figure out how to make it dynamic. blockQuoteSize :: Pixels @@ -1345,44 +1401,21 @@ shapeToElement layout (TextBox paras) replaceNamedChildren ns "p" "txBody" [txBody] $ replaceNamedChildren ns "p" "spPr" [emptySpPr] $ sp - -- XXX: TODO - | otherwise = return $ mknode "p:sp" [] () --- XXX: TODO -shapeToElement layout (Pic picProps fp attr alt) = do - mInfo <- registerMedia fp alt - case mInfoExt mInfo of - Just _ -> makePicElement picProps mInfo attr - Nothing -> shapeToElement layout $ TextBox [Paragraph def alt] -shapeToElement _ (GraphicFrame tbls _) = do - elements <- mapM graphicToElement tbls - return $ mknode "p:graphicFrame" [] $ - [ mknode "p:nvGraphicFramePr" [] $ - [ mknode "p:cNvPr" [("id", "6"), ("name", "Content Placeholder 5")] () - , mknode "p:cNvGraphicFramePr" [] $ - [mknode "a:graphicFrameLocks" [("noGrp", "1")] ()] - , mknode "p:nvPr" [] $ - [mknode "p:ph" [("idx", "1")] ()] - ] - , mknode "p:xfrm" [] $ - [ mknode "a:off" [("x", "457200"), ("y", "1600200")] () - , mknode "a:ext" [("cx", "8029388"), ("cy", "3644152")] () - ] - ] ++ elements +-- GraphicFrame and Pic should never reach this. +shapeToElement _ _ = return $ mknode "p:sp" [] () shapeToElements :: PandocMonad m => Element -> Shape -> P m [Element] +shapeToElements layout (Pic picProps fp attr alt) = do + mInfo <- registerMedia fp alt + case mInfoExt mInfo of + Just _ -> do + makePicElements layout picProps mInfo attr alt + Nothing -> shapeToElements layout $ TextBox [Paragraph def alt] +shapeToElements layout (GraphicFrame tbls cptn) = + graphicFrameToElements layout tbls cptn shapeToElements layout shp = do - case shp of - (Pic _ _ _ alt) | (not . null) alt -> do - element <- shapeToElement layout shp - caption <- createCaption alt - return [element, caption] - (GraphicFrame _ cptn) | (not . null) cptn -> do - element <- shapeToElement layout shp - caption <- createCaption cptn - return [element, caption] - _ -> do - element <- shapeToElement layout shp - return [element] + element <- shapeToElement layout shp + return [element] shapesToElements :: PandocMonad m => Element -> [Shape] -> P m [Element] shapesToElements layout shps = do @@ -1391,6 +1424,38 @@ shapesToElements layout shps = do hardcodedTableMargin :: Integer hardcodedTableMargin = 36 +graphicFrameToElements :: PandocMonad m => Element -> [Graphic] -> [ParaElem] -> P m [Element] +graphicFrameToElements layout tbls caption = do + -- get the sizing + master <- getMaster + (pageWidth, pageHeight) <- asks envPresentationSize + let ns = elemToNameSpaces layout + let ((x, y), (cx, cytmp)) = case getContentShapeSize ns layout master of + Just dims -> dims + Nothing -> ((0, 0), (pageWidth, pageHeight)) + + cy = if (not $ null caption) then cytmp - captionHeight else cytmp + + elements <- mapM graphicToElement tbls + let graphicFrameElts = + mknode "p:graphicFrame" [] $ + [ mknode "p:nvGraphicFramePr" [] $ + [ mknode "p:cNvPr" [("id", "6"), ("name", "Content Placeholder 5")] () + , mknode "p:cNvGraphicFramePr" [] $ + [mknode "a:graphicFrameLocks" [("noGrp", "1")] ()] + , mknode "p:nvPr" [] $ + [mknode "p:ph" [("idx", "1")] ()] + ] + , mknode "p:xfrm" [] $ + [ mknode "a:off" [("x", show $ 12700 * x), ("y", show $ 12700 * y)] () + , mknode "a:ext" [("cx", show $ 12700 * cx), ("cy", show $ 12700 * cy)] () + ] + ] ++ elements + + if (not $ null caption) + then do capElt <- createCaption ((x, y), (cx, cytmp)) caption + return [graphicFrameElts, capElt] + else return [graphicFrameElts] graphicToElement :: PandocMonad m => Graphic -> P m Element graphicToElement (Tbl tblPr colWidths hdrCells rows) = do @@ -1437,6 +1502,12 @@ getShapeByName ns spTreeElem name filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasName ns name e)) spTreeElem | otherwise = Nothing +-- getShapeById :: NameSpaces -> Element -> String -> Maybe Element +-- getShapeById ns spTreeElem ident +-- | isElem ns "p" "spTree" spTreeElem = +-- filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasId ns ident e)) spTreeElem +-- | otherwise = Nothing + nonBodyTextToElement :: PandocMonad m => Element -> String -> [ParaElem] -> P m Element nonBodyTextToElement layout shapeName paraElements | ns <- elemToNameSpaces layout -- cgit v1.2.3 From 485535464df27f5c2e2dd144650af318bde239c1 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 13 Jan 2018 22:04:26 -0800 Subject: LaTeX reader: fixed pos calculation in tokenizing escaped space. --- src/Text/Pandoc/Readers/LaTeX.hs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index d9b188606..d3f730259 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -333,13 +333,16 @@ totoks pos t = -> (T.pack "\n", T.span isSpaceOrTab r2) _ -> (mempty, (mempty, r1)) + ws = "\\" <> w1 <> w2 <> w3 in case T.uncons r3 of Just ('\n', _) -> Tok pos (CtrlSeq " ") ("\\" <> w1) - : totoks (incSourceColumn pos 1) r1 + : totoks (incSourceColumn pos (T.length ws)) + r1 _ -> - Tok pos (CtrlSeq " ") ("\\" <> w1 <> w2 <> w3) - : totoks (incSourceColumn pos 1) r3 + Tok pos (CtrlSeq " ") ws + : totoks (incSourceColumn pos (T.length ws)) + r3 | otherwise -> Tok pos (CtrlSeq (T.singleton d)) (T.pack [c,d]) : totoks (incSourceColumn pos 2) rest' -- cgit v1.2.3 From e7d95cadf537909bcb1e7d17d4545932d6bb34bc Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 13 Jan 2018 22:12:32 -0800 Subject: LaTeX reader: pass through macro defs in rawLaTeXBlock... even if the `latex_macros` extension is set. This reverts to earlier behavior and is probably safer on the whole, since some macros only modify things in included packages, which pandoc's macro expansion can't modify. Closes #4246. --- src/Text/Pandoc/Readers/LaTeX.hs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index d3f730259..0a78fbe53 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -272,10 +272,8 @@ rawLaTeXBlock = do lookAhead (try (char '\\' >> letter)) -- we don't want to apply newly defined latex macros to their own -- definitions: - (do (_, raw) <- rawLaTeXParser macroDef - (guardDisabled Ext_latex_macros >> return raw) <|> return "") - <|> (do (_, raw) <- rawLaTeXParser (environment <|> blockCommand) - applyMacros raw) + (snd <$> rawLaTeXParser macroDef) + <|> ((snd <$> rawLaTeXParser (environment <|> blockCommand)) >>= applyMacros) rawLaTeXInline :: (PandocMonad m, HasMacros s, HasReaderOptions s) => ParserT String s m String -- cgit v1.2.3 From 15772896720f082cbaa44e5e556e6db1c9229756 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sun, 14 Jan 2018 01:10:23 -0500 Subject: Powerpoint writer: Make content shape retrieval environment-aware We put `getContentShape` and `getContentShapeSize` inside the P monad, so that we can (in the future) make use of knowledge of what slide environment we're in to get the correct shape. This will allow us, for example, to get individual columns for a two-column layout, and place images in them appropriately. --- src/Text/Pandoc/Writers/Powerpoint.hs | 76 ++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 28 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index ebac15db4..62f355d76 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -32,7 +32,7 @@ Conversion of 'Pandoc' documents to powerpoint (pptx). module Text.Pandoc.Writers.Powerpoint (writePowerpoint) where -import Control.Monad.Except (throwError) +import Control.Monad.Except (throwError, catchError) import Control.Monad.Reader import Control.Monad.State import Codec.Archive.Zip @@ -126,6 +126,7 @@ data WriterEnv = WriterEnv { envMetadata :: Meta -- the end of the slide file name and -- the rId number , envSlideIdOffset :: Int + , envColumnNumber :: Maybe Int } deriving (Show) @@ -144,6 +145,7 @@ instance Default WriterEnv where , envInNoteSlide = False , envCurSlideId = 1 , envSlideIdOffset = 1 + , envColumnNumber = Nothing } data MediaInfo = MediaInfo { mInfoFilePath :: FilePath @@ -905,11 +907,23 @@ shapeHasId ns ident element nm == ident | otherwise = False -getContentShape :: NameSpaces -> Element -> Maybe Element +-- The content shape in slideLayout2 (Title/Content) has id=3 In +-- slideLayout4 (two column) the left column is id=3, and the right +-- column is id=4. +getContentShape :: PandocMonad m => NameSpaces -> Element -> P m Element getContentShape ns spTreeElem | isElem ns "p" "spTree" spTreeElem = - filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasName ns "Content Placeholder 2" e)) spTreeElem - | otherwise = Nothing + case filterChild + (\e -> (isElem ns "p" "sp" e) && (shapeHasId ns "3" e)) + spTreeElem + of + Just e -> return e + Nothing -> throwError $ + PandocSomeError $ + "Could not find shape for Powerpoint content" +getContentShape _ _ = throwError $ + PandocSomeError $ + "Attempted to find content on non shapeTree" getShapeDimensions :: NameSpaces -> Element @@ -942,25 +956,31 @@ getMasterShapeDimensionsById ident master = do sp <- filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasId ns ident e)) spTree getShapeDimensions ns sp -getContentShapeSize :: NameSpaces +getContentShapeSize :: PandocMonad m + => NameSpaces -> Element -> Element - -> Maybe ((Integer, Integer), (Integer, Integer)) + -> P m ((Integer, Integer), (Integer, Integer)) getContentShapeSize ns layout master | isElem ns "p" "sldLayout" layout , Just cSld <- findChild (elemName ns "p" "cSld") layout - , Just spTree <- findChild (elemName ns "p" "spTree") cSld - , Just sp <- getContentShape ns spTree - , Just sz <- getShapeDimensions ns sp = Just sz - | isElem ns "p" "sldLayout" layout - , Just cSld <- findChild (elemName ns "p" "cSld") layout - , Just spTree <- findChild (elemName ns "p" "spTree") cSld - , Just sp <- getContentShape ns spTree - , Just ident <- findChild (elemName ns "p" "nvSpPr") sp >>= - findChild (elemName ns "p" "cNvPr") >>= - findAttr (QName "id" Nothing Nothing) - , Just sz <- getMasterShapeDimensionsById ident master = Just sz - | otherwise = Nothing + , Just spTree <- findChild (elemName ns "p" "spTree") cSld = do + sp <- getContentShape ns spTree + case getShapeDimensions ns sp of + Just sz -> return sz + Nothing -> do let mbSz = + findChild (elemName ns "p" "nvSpPr") sp >>= + findChild (elemName ns "p" "cNvPr") >>= + findAttr (QName "id" Nothing Nothing) >>= + flip getMasterShapeDimensionsById master + case mbSz of + Just sz' -> return sz' + Nothing -> throwError $ + PandocSomeError $ + "Couldn't find necessary content shape size" +getContentShapeSize _ _ _ = throwError $ + PandocSomeError $ + "Attempted to find content shape size in non-layout" replaceNamedChildren :: NameSpaces -> String @@ -1198,11 +1218,11 @@ makePicElements layout picProps mInfo _ alt = do Left _ -> sizeInPixels $ def master <- getMaster let ns = elemToNameSpaces layout - let ((x, y), (cx, cytmp)) = case getContentShapeSize ns layout master of - Just dims -> dims - Nothing -> ((0, 0), (pageWidth, pageHeight)) + ((x, y), (cx, cytmp)) <- getContentShapeSize ns layout master + `catchError` + (\_ -> return ((0, 0), (pageWidth, pageHeight))) - cy = if hasCaption then cytmp - captionHeight else cytmp + let cy = if hasCaption then cytmp - captionHeight else cytmp let imgRatio = fromIntegral pxX / fromIntegral pxY :: Double boxRatio = fromIntegral cx / fromIntegral cy :: Double @@ -1390,8 +1410,8 @@ shapeToElement :: PandocMonad m => Element -> Shape -> P m Element shapeToElement layout (TextBox paras) | ns <- elemToNameSpaces layout , Just cSld <- findChild (elemName ns "p" "cSld") layout - , Just spTree <- findChild (elemName ns "p" "spTree") cSld - , Just sp <- getContentShape ns spTree = do + , Just spTree <- findChild (elemName ns "p" "spTree") cSld = do + sp <- getContentShape ns spTree elements <- mapM paragraphToElement paras let txBody = mknode "p:txBody" [] $ [mknode "a:bodyPr" [] (), mknode "a:lstStyle" [] ()] ++ elements @@ -1430,11 +1450,11 @@ graphicFrameToElements layout tbls caption = do master <- getMaster (pageWidth, pageHeight) <- asks envPresentationSize let ns = elemToNameSpaces layout - let ((x, y), (cx, cytmp)) = case getContentShapeSize ns layout master of - Just dims -> dims - Nothing -> ((0, 0), (pageWidth, pageHeight)) + ((x, y), (cx, cytmp)) <- getContentShapeSize ns layout master + `catchError` + (\_ -> return ((0, 0), (pageWidth, pageHeight))) - cy = if (not $ null caption) then cytmp - captionHeight else cytmp + let cy = if (not $ null caption) then cytmp - captionHeight else cytmp elements <- mapM graphicToElement tbls let graphicFrameElts = -- cgit v1.2.3 From 64c4451ef3b55a6c545de232af62780e0f5766d7 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sun, 14 Jan 2018 01:37:51 -0500 Subject: Powerpoint writer: Position images correctly in two-column layout. You can have two images side-by-side, or text alongside an image. The image will be fit correctly within the column. --- src/Text/Pandoc/Writers/Powerpoint.hs | 78 +++++++++++++++-------------------- 1 file changed, 33 insertions(+), 45 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index 62f355d76..4b6ea0853 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -42,7 +42,6 @@ import Data.Time.Clock (UTCTime) import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds, posixSecondsToUTCTime) import System.FilePath.Posix (splitDirectories, splitExtension, takeExtension) import Text.XML.Light -import qualified Text.XML.Light.Cursor as XMLC import Text.Pandoc.Definition import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Class (PandocMonad) @@ -126,7 +125,7 @@ data WriterEnv = WriterEnv { envMetadata :: Meta -- the end of the slide file name and -- the rId number , envSlideIdOffset :: Int - , envColumnNumber :: Maybe Int + , envContentType :: ContentType } deriving (Show) @@ -145,9 +144,14 @@ instance Default WriterEnv where , envInNoteSlide = False , envCurSlideId = 1 , envSlideIdOffset = 1 - , envColumnNumber = Nothing + , envContentType = NormalContent } +data ContentType = NormalContent + | TwoColumnLeftContent + | TwoColumnRightContent + deriving (Show, Eq) + data MediaInfo = MediaInfo { mInfoFilePath :: FilePath , mInfoLocalId :: Int , mInfoGlobalId :: Int @@ -912,15 +916,20 @@ shapeHasId ns ident element -- column is id=4. getContentShape :: PandocMonad m => NameSpaces -> Element -> P m Element getContentShape ns spTreeElem - | isElem ns "p" "spTree" spTreeElem = - case filterChild - (\e -> (isElem ns "p" "sp" e) && (shapeHasId ns "3" e)) - spTreeElem - of - Just e -> return e - Nothing -> throwError $ - PandocSomeError $ - "Could not find shape for Powerpoint content" + | isElem ns "p" "spTree" spTreeElem = do + contentType <- asks envContentType + let ident = case contentType of + NormalContent -> "3" + TwoColumnLeftContent -> "3" + TwoColumnRightContent -> "4" + case filterChild + (\e -> (isElem ns "p" "sp" e) && (shapeHasId ns ident e)) + spTreeElem + of + Just e -> return e + Nothing -> throwError $ + PandocSomeError $ + "Could not find shape for Powerpoint content" getContentShape _ _ = throwError $ PandocSomeError $ "Attempted to find content on non shapeTree" @@ -1552,40 +1561,15 @@ contentToElement layout hdrShape shapes let hdrShapeElements = if null hdrShape then [] else [element] - contentElements <- shapesToElements layout shapes + contentElements <- local + (\env -> env {envContentType = NormalContent}) + (shapesToElements layout shapes) return $ replaceNamedChildren ns "p" "sp" (hdrShapeElements ++ contentElements) spTree contentToElement _ _ _ = return $ mknode "p:sp" [] () -setIdx'' :: NameSpaces -> String -> Content -> Content -setIdx'' _ idx (Elem element) = - let tag = XMLC.getTag element - attrs = XMLC.tagAttribs tag - idxKey = (QName "idx" Nothing Nothing) - attrs' = Attr idxKey idx : (filter (\a -> attrKey a /= idxKey) attrs) - tag' = tag {XMLC.tagAttribs = attrs'} - in Elem $ XMLC.setTag tag' element -setIdx'' _ _ c = c - -setIdx' :: NameSpaces -> String -> XMLC.Cursor -> XMLC.Cursor -setIdx' ns idx cur = - let modifiedCur = XMLC.modifyContent (setIdx'' ns idx) cur - in - case XMLC.nextDF modifiedCur of - Just cur' -> setIdx' ns idx cur' - Nothing -> XMLC.root modifiedCur - -setIdx :: NameSpaces -> String -> Element -> Element -setIdx ns idx element = - let cur = XMLC.fromContent (Elem element) - cur' = setIdx' ns idx cur - in - case XMLC.toTree cur' of - Elem element' -> element' - _ -> element - twoColumnToElement :: PandocMonad m => Element -> [ParaElem] -> [Shape] -> [Shape] -> P m Element twoColumnToElement layout hdrShape shapesL shapesR | ns <- elemToNameSpaces layout @@ -1595,13 +1579,17 @@ twoColumnToElement layout hdrShape shapesL shapesR let hdrShapeElements = if null hdrShape then [] else [element] - contentElementsL <- shapesToElements layout shapesL - contentElementsR <- shapesToElements layout shapesR - let contentElementsL' = map (setIdx ns "1") contentElementsL - contentElementsR' = map (setIdx ns "2") contentElementsR + contentElementsL <- local + (\env -> env {envContentType =TwoColumnLeftContent}) + (shapesToElements layout shapesL) + contentElementsR <- local + (\env -> env {envContentType =TwoColumnRightContent}) + (shapesToElements layout shapesR) + -- let contentElementsL' = map (setIdx ns "1") contentElementsL + -- contentElementsR' = map (setIdx ns "2") contentElementsR return $ replaceNamedChildren ns "p" "sp" - (hdrShapeElements ++ contentElementsL' ++ contentElementsR') + (hdrShapeElements ++ contentElementsL ++ contentElementsR) spTree twoColumnToElement _ _ _ _= return $ mknode "p:sp" [] () -- cgit v1.2.3 From 90dcd0bc8795796583a6c895d15827b1c99cfb75 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sun, 14 Jan 2018 01:47:38 -0500 Subject: Powerpoint writer: Avoid overlapping blocks in column output. Just as a slide can't have an image and text on the same slide because of overlapping, we can't have both in a single column. We run splitBlocks on the text in the column and discard the rest. --- src/Text/Pandoc/Writers/Powerpoint.hs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index 4b6ea0853..647c37a0b 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -624,8 +624,16 @@ blocksToSlide' _ (blk : blks) (mapM (P.report . BlockNotRendered) blks >> return ()) unless (null remaining) (mapM (P.report . BlockNotRendered) remaining >> return ()) - shapesL <- blocksToShapes blksL - shapesR <- blocksToShapes blksR + mbSplitBlksL <- splitBlocks blksL + mbSplitBlksR <- splitBlocks blksR + let blksL' = case mbSplitBlksL of + bs : _ -> bs + [] -> [] + let blksR' = case mbSplitBlksR of + bs : _ -> bs + [] -> [] + shapesL <- blocksToShapes blksL' + shapesR <- blocksToShapes blksR' return $ TwoColumnSlide { twoColumnSlideHeader = [] , twoColumnSlideLeft = shapesL , twoColumnSlideRight = shapesR -- cgit v1.2.3 From 431f6166fa7dc6670fb5cff4a9bd5499c67e0bed Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sun, 14 Jan 2018 08:59:10 -0500 Subject: Powerpoint writer: Refactor into separate modules. There are two steps in the conversion: a conversion from pandoc to a Presentation datatype modeling pptx, and a conversion from Presentation to a pptx archive. The two steps were sharing the same state and environment, and the code was getting a bit spaghetti-ish. This separates the conversion into separate modules (T.P.W.Powerpoint.Presentation, which defineds the Presentation datatype and goes Pandoc->Presentation) and (T.P.W.Pandoc.Output, which goes Presentation->Archive). Text.Pandoc.Writers.Powerpoint a thin wrapper around the two modules. --- src/Text/Pandoc/Writers/Powerpoint.hs | 1998 +------------------- src/Text/Pandoc/Writers/Powerpoint/Output.hs | 1431 ++++++++++++++ src/Text/Pandoc/Writers/Powerpoint/Presentation.hs | 701 +++++++ 3 files changed, 2151 insertions(+), 1979 deletions(-) create mode 100644 src/Text/Pandoc/Writers/Powerpoint/Output.hs create mode 100644 src/Text/Pandoc/Writers/Powerpoint/Presentation.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index 647c37a0b..3d6b736f2 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE PatternGuards, MultiWayIf, OverloadedStrings #-} +{-# LANGUAGE PatternGuards #-} {- Copyright (C) 2017-2018 Jesse Rosenthal <jrosenthal@jhu.edu> @@ -27,44 +27,29 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Stability : alpha Portability : portable -Conversion of 'Pandoc' documents to powerpoint (pptx). +Conversion of 'Pandoc' documents to powerpoint (pptx). -} + +{- +This is a wrapper around two modules: + + - Text.Pandoc.Writers.Powerpoint.Presentation (which converts a + pandoc document into a Presentation datatype), and + + - Text.Pandoc.Writers.Powerpoint.Output (which converts a + Presentation into a zip archive, which can be output). -} module Text.Pandoc.Writers.Powerpoint (writePowerpoint) where -import Control.Monad.Except (throwError, catchError) -import Control.Monad.Reader -import Control.Monad.State import Codec.Archive.Zip -import Data.List (intercalate, stripPrefix, nub, union) -import Data.Default -import Data.Time.Clock (UTCTime) -import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds, posixSecondsToUTCTime) -import System.FilePath.Posix (splitDirectories, splitExtension, takeExtension) -import Text.XML.Light import Text.Pandoc.Definition -import qualified Text.Pandoc.UTF8 as UTF8 +import Text.Pandoc.Walk import Text.Pandoc.Class (PandocMonad) -import Text.Pandoc.Error (PandocError(..)) -import Text.Pandoc.Slides (getSlideLevel) -import qualified Text.Pandoc.Class as P -import Text.Pandoc.Options -import Text.Pandoc.MIME -import Text.Pandoc.Logging +import Text.Pandoc.Options (WriterOptions) +import Text.Pandoc.Writers.Shared (fixDisplayMath) +import Text.Pandoc.Writers.Powerpoint.Presentation (documentToPresentation) +import Text.Pandoc.Writers.Powerpoint.Output (presentationToArchive) import qualified Data.ByteString.Lazy as BL -import Text.Pandoc.Walk -import qualified Text.Pandoc.Shared as Shared -- so we don't overlap "Element" -import Text.Pandoc.Writers.Shared (fixDisplayMath, metaValueToInlines) -import Text.Pandoc.Writers.OOXML -import qualified Data.Map as M -import Data.Maybe (mapMaybe, listToMaybe, maybeToList, catMaybes) -import Text.Pandoc.ImageSize -import Control.Applicative ((<|>)) -import System.FilePath.Glob - -import Text.TeXMath -import Text.Pandoc.Writers.Math (convertMath) - writePowerpoint :: (PandocMonad m) => WriterOptions -- ^ Writer options @@ -72,1951 +57,6 @@ writePowerpoint :: (PandocMonad m) -> m BL.ByteString writePowerpoint opts (Pandoc meta blks) = do let blks' = walk fixDisplayMath blks - distArchive <- (toArchive . BL.fromStrict) <$> - P.readDefaultDataFile "reference.pptx" - refArchive <- case writerReferenceDoc opts of - Just f -> toArchive <$> P.readFileLazy f - Nothing -> (toArchive . BL.fromStrict) <$> - P.readDataFile "reference.pptx" - - utctime <- P.getCurrentTime - - presSize <- case getPresentationSize refArchive distArchive of - Just sz -> return sz - Nothing -> throwError $ - PandocSomeError $ - "Could not determine presentation size" - - let env = def { envMetadata = meta - , envRefArchive = refArchive - , envDistArchive = distArchive - , envUTCTime = utctime - , envOpts = opts - , envSlideLevel = case writerSlideLevel opts of - Just n -> n - Nothing -> getSlideLevel blks' - , envPresentationSize = presSize - } - - let st = def { stMediaGlobalIds = initialGlobalIds refArchive distArchive - } - - runP env st $ do pres <- blocksToPresentation blks' - archv <- presentationToArchive pres - return $ fromArchive archv - -concatMapM :: (Monad m) => (a -> m [b]) -> [a] -> m [b] -concatMapM f xs = liftM concat (mapM f xs) - -data WriterEnv = WriterEnv { envMetadata :: Meta - , envRunProps :: RunProps - , envParaProps :: ParaProps - , envSlideLevel :: Int - , envRefArchive :: Archive - , envDistArchive :: Archive - , envUTCTime :: UTCTime - , envOpts :: WriterOptions - , envPresentationSize :: (Integer, Integer) - , envSlideHasHeader :: Bool - , envInList :: Bool - , envInNoteSlide :: Bool - , envCurSlideId :: Int - -- the difference between the number at - -- the end of the slide file name and - -- the rId number - , envSlideIdOffset :: Int - , envContentType :: ContentType - } - deriving (Show) - -instance Default WriterEnv where - def = WriterEnv { envMetadata = mempty - , envRunProps = def - , envParaProps = def - , envSlideLevel = 2 - , envRefArchive = emptyArchive - , envDistArchive = emptyArchive - , envUTCTime = posixSecondsToUTCTime 0 - , envOpts = def - , envPresentationSize = (720, 540) - , envSlideHasHeader = False - , envInList = False - , envInNoteSlide = False - , envCurSlideId = 1 - , envSlideIdOffset = 1 - , envContentType = NormalContent - } - -data ContentType = NormalContent - | TwoColumnLeftContent - | TwoColumnRightContent - deriving (Show, Eq) - -data MediaInfo = MediaInfo { mInfoFilePath :: FilePath - , mInfoLocalId :: Int - , mInfoGlobalId :: Int - , mInfoMimeType :: Maybe MimeType - , mInfoExt :: Maybe String - , mInfoCaption :: Bool - } deriving (Show, Eq) - -data WriterState = WriterState { stLinkIds :: M.Map Int (M.Map Int (URL, String)) - -- (FP, Local ID, Global ID, Maybe Mime) - , stMediaIds :: M.Map Int [MediaInfo] - , stMediaGlobalIds :: M.Map FilePath Int - , stNoteIds :: M.Map Int [Block] - -- associate anchors with slide id - , stAnchorMap :: M.Map String Int - -- media inherited from the template. - , stTemplateMedia :: [FilePath] - } deriving (Show, Eq) - -instance Default WriterState where - def = WriterState { stLinkIds = mempty - , stMediaIds = mempty - , stMediaGlobalIds = mempty - , stNoteIds = mempty - , stAnchorMap= mempty - , stTemplateMedia = [] - } - --- This populates the global ids map with images already in the --- template, so the ids won't be used by images introduced by the --- user. -initialGlobalIds :: Archive -> Archive -> M.Map FilePath Int -initialGlobalIds refArchive distArchive = - let archiveFiles = filesInArchive refArchive `union` filesInArchive distArchive - mediaPaths = filter (match (compile "ppt/media/image")) archiveFiles - - go :: FilePath -> Maybe (FilePath, Int) - go fp = do - s <- stripPrefix "ppt/media/image" $ fst $ splitExtension fp - (n, _) <- listToMaybe $ reads s - return (fp, n) - in - M.fromList $ mapMaybe go mediaPaths - -getPresentationSize :: Archive -> Archive -> Maybe (Integer, Integer) -getPresentationSize refArchive distArchive = do - entry <- findEntryByPath "ppt/presentation.xml" refArchive `mplus` - findEntryByPath "ppt/presentation.xml" distArchive - presElement <- parseXMLDoc $ UTF8.toStringLazy $ fromEntry entry - let ns = elemToNameSpaces presElement - sldSize <- findChild (elemName ns "p" "sldSz") presElement - cxS <- findAttr (QName "cx" Nothing Nothing) sldSize - cyS <- findAttr (QName "cy" Nothing Nothing) sldSize - (cx, _) <- listToMaybe $ reads cxS :: Maybe (Integer, String) - (cy, _) <- listToMaybe $ reads cyS :: Maybe (Integer, String) - return (cx `div` 12700, cy `div` 12700) - -type P m = ReaderT WriterEnv (StateT WriterState m) - -runP :: Monad m => WriterEnv -> WriterState -> P m a -> m a -runP env st p = evalStateT (runReaderT p env) st - -type Pixels = Integer - -data Presentation = Presentation [Slide] - deriving (Show) - -data Slide = MetadataSlide { metadataSlideTitle :: [ParaElem] - , metadataSlideSubtitle :: [ParaElem] - , metadataSlideAuthors :: [[ParaElem]] - , metadataSlideDate :: [ParaElem] - } - | TitleSlide { titleSlideHeader :: [ParaElem]} - | ContentSlide { contentSlideHeader :: [ParaElem] - , contentSlideContent :: [Shape] - } - | TwoColumnSlide { twoColumnSlideHeader :: [ParaElem] - , twoColumnSlideLeft :: [Shape] - , twoColumnSlideRight :: [Shape] - } - deriving (Show, Eq) - -data SlideElement = SlideElement Pixels Pixels Pixels Pixels Shape - deriving (Show, Eq) - -data Shape = Pic PicProps FilePath Text.Pandoc.Definition.Attr [ParaElem] - | GraphicFrame [Graphic] [ParaElem] - | TextBox [Paragraph] - deriving (Show, Eq) - -type Cell = [Paragraph] - -data TableProps = TableProps { tblPrFirstRow :: Bool - , tblPrBandRow :: Bool - } deriving (Show, Eq) - -type ColWidth = Integer - -data Graphic = Tbl TableProps [ColWidth] [Cell] [[Cell]] - deriving (Show, Eq) - - -data Paragraph = Paragraph { paraProps :: ParaProps - , paraElems :: [ParaElem] - } deriving (Show, Eq) - -autoNumberingToType :: ListAttributes -> String -autoNumberingToType (_, numStyle, numDelim) = - typeString ++ delimString - where - typeString = case numStyle of - Decimal -> "arabic" - UpperAlpha -> "alphaUc" - LowerAlpha -> "alphaLc" - UpperRoman -> "romanUc" - LowerRoman -> "romanLc" - _ -> "arabic" - delimString = case numDelim of - Period -> "Period" - OneParen -> "ParenR" - TwoParens -> "ParenBoth" - _ -> "Period" - -data BulletType = Bullet - | AutoNumbering ListAttributes - deriving (Show, Eq) - -data Algnment = AlgnLeft | AlgnRight | AlgnCenter - deriving (Show, Eq) - -data ParaProps = ParaProps { pPropMarginLeft :: Maybe Pixels - , pPropMarginRight :: Maybe Pixels - , pPropLevel :: Int - , pPropBullet :: Maybe BulletType - , pPropAlign :: Maybe Algnment - , pPropSpaceBefore :: Maybe Pixels - } deriving (Show, Eq) - -instance Default ParaProps where - def = ParaProps { pPropMarginLeft = Just 0 - , pPropMarginRight = Just 0 - , pPropLevel = 0 - , pPropBullet = Nothing - , pPropAlign = Nothing - , pPropSpaceBefore = Nothing - } - -newtype TeXString = TeXString {unTeXString :: String} - deriving (Eq, Show) - -data ParaElem = Break - | Run RunProps String - -- It would be more elegant to have native TeXMath - -- Expressions here, but this allows us to use - -- `convertmath` from T.P.Writers.Math. Will perhaps - -- revisit in the future. - | MathElem MathType TeXString - deriving (Show, Eq) - -data Strikethrough = NoStrike | SingleStrike | DoubleStrike - deriving (Show, Eq) - -data Capitals = NoCapitals | SmallCapitals | AllCapitals - deriving (Show, Eq) - -type URL = String - -data RunProps = RunProps { rPropBold :: Bool - , rPropItalics :: Bool - , rStrikethrough :: Maybe Strikethrough - , rBaseline :: Maybe Int - , rCap :: Maybe Capitals - , rLink :: Maybe (URL, String) - , rPropCode :: Bool - , rPropBlockQuote :: Bool - , rPropForceSize :: Maybe Pixels - } deriving (Show, Eq) - -instance Default RunProps where - def = RunProps { rPropBold = False - , rPropItalics = False - , rStrikethrough = Nothing - , rBaseline = Nothing - , rCap = Nothing - , rLink = Nothing - , rPropCode = False - , rPropBlockQuote = False - , rPropForceSize = Nothing - } - -data PicProps = PicProps { picPropLink :: Maybe (URL, String) - } deriving (Show, Eq) - -instance Default PicProps where - def = PicProps { picPropLink = Nothing - } - --------------------------------------------------- - -inlinesToParElems :: Monad m => [Inline] -> P m [ParaElem] -inlinesToParElems ils = concatMapM inlineToParElems ils - -inlineToParElems :: Monad m => Inline -> P m [ParaElem] -inlineToParElems (Str s) = do - pr <- asks envRunProps - return [Run pr s] -inlineToParElems (Emph ils) = - local (\r -> r{envRunProps = (envRunProps r){rPropItalics=True}}) $ - inlinesToParElems ils -inlineToParElems (Strong ils) = - local (\r -> r{envRunProps = (envRunProps r){rPropBold=True}}) $ - inlinesToParElems ils -inlineToParElems (Strikeout ils) = - local (\r -> r{envRunProps = (envRunProps r){rStrikethrough=Just SingleStrike}}) $ - inlinesToParElems ils -inlineToParElems (Superscript ils) = - local (\r -> r{envRunProps = (envRunProps r){rBaseline=Just 30000}}) $ - inlinesToParElems ils -inlineToParElems (Subscript ils) = - local (\r -> r{envRunProps = (envRunProps r){rBaseline=Just (-25000)}}) $ - inlinesToParElems ils -inlineToParElems (SmallCaps ils) = - local (\r -> r{envRunProps = (envRunProps r){rCap = Just SmallCapitals}}) $ - inlinesToParElems ils -inlineToParElems Space = inlineToParElems (Str " ") -inlineToParElems SoftBreak = inlineToParElems (Str " ") -inlineToParElems LineBreak = return [Break] -inlineToParElems (Link _ ils (url, title)) = do - local (\r ->r{envRunProps = (envRunProps r){rLink = Just (url, title)}}) $ - inlinesToParElems ils -inlineToParElems (Code _ str) = do - local (\r ->r{envRunProps = (envRunProps r){rPropCode = True}}) $ - inlineToParElems $ Str str -inlineToParElems (Math mathtype str) = - return [MathElem mathtype (TeXString str)] -inlineToParElems (Note blks) = do - notes <- gets stNoteIds - let maxNoteId = case M.keys notes of - [] -> 0 - lst -> maximum lst - curNoteId = maxNoteId + 1 - modify $ \st -> st { stNoteIds = M.insert curNoteId blks notes } - inlineToParElems $ Superscript [Str $ show curNoteId] -inlineToParElems (Span _ ils) = concatMapM inlineToParElems ils -inlineToParElems (RawInline _ _) = return [] -inlineToParElems _ = return [] - -isListType :: Block -> Bool -isListType (OrderedList _ _) = True -isListType (BulletList _) = True -isListType (DefinitionList _) = True -isListType _ = False - -registerAnchorId :: PandocMonad m => String -> P m () -registerAnchorId anchor = do - anchorMap <- gets stAnchorMap - slideId <- asks envCurSlideId - unless (null anchor) $ - modify $ \st -> st {stAnchorMap = M.insert anchor slideId anchorMap} - -blockToParagraphs :: PandocMonad m => Block -> P m [Paragraph] -blockToParagraphs (Plain ils) = do - parElems <- inlinesToParElems ils - pProps <- asks envParaProps - return [Paragraph pProps parElems] -blockToParagraphs (Para ils) = do - parElems <- inlinesToParElems ils - pProps <- asks envParaProps - return [Paragraph pProps parElems] -blockToParagraphs (LineBlock ilsList) = do - parElems <- inlinesToParElems $ intercalate [LineBreak] ilsList - pProps <- asks envParaProps - return [Paragraph pProps parElems] --- TODO: work out the attributes -blockToParagraphs (CodeBlock attr str) = - local (\r -> r{envParaProps = def{pPropMarginLeft = Just 100}}) $ - blockToParagraphs $ Para [Code attr str] --- We can't yet do incremental lists, but we should render a --- (BlockQuote List) as a list to maintain compatibility with other --- formats. -blockToParagraphs (BlockQuote (blk : blks)) | isListType blk = do - ps <- blockToParagraphs blk - ps' <- blockToParagraphs $ BlockQuote blks - return $ ps ++ ps' -blockToParagraphs (BlockQuote blks) = - local (\r -> r{ envParaProps = (envParaProps r){pPropMarginLeft = Just 100} - , envRunProps = (envRunProps r){rPropForceSize = Just blockQuoteSize}})$ - concatMapM blockToParagraphs blks --- TODO: work out the format -blockToParagraphs (RawBlock _ _) = return [] -blockToParagraphs (Header _ (ident, _, _) ils) = do - -- Note that this function only deals with content blocks, so it - -- will only touch headers that are above the current slide level -- - -- slides at or below the slidelevel will be taken care of by - -- `blocksToSlide'`. We have the register anchors in both of them. - registerAnchorId ident - -- we set the subeader to bold - parElems <- local (\e->e{envRunProps = (envRunProps e){rPropBold=True}}) $ - inlinesToParElems ils - -- and give it a bit of space before it. - return [Paragraph def{pPropSpaceBefore = Just 30} parElems] -blockToParagraphs (BulletList blksLst) = do - pProps <- asks envParaProps - let lvl = pPropLevel pProps - local (\env -> env{ envInList = True - , envParaProps = pProps{ pPropLevel = lvl + 1 - , pPropBullet = Just Bullet - , pPropMarginLeft = Nothing - }}) $ - concatMapM multiParBullet blksLst -blockToParagraphs (OrderedList listAttr blksLst) = do - pProps <- asks envParaProps - let lvl = pPropLevel pProps - local (\env -> env{ envInList = True - , envParaProps = pProps{ pPropLevel = lvl + 1 - , pPropBullet = Just (AutoNumbering listAttr) - , pPropMarginLeft = Nothing - }}) $ - concatMapM multiParBullet blksLst -blockToParagraphs (DefinitionList entries) = do - let go :: PandocMonad m => ([Inline], [[Block]]) -> P m [Paragraph] - go (ils, blksLst) = do - term <-blockToParagraphs $ Para [Strong ils] - -- For now, we'll treat each definition term as a - -- blockquote. We can extend this further later. - definition <- concatMapM (blockToParagraphs . BlockQuote) blksLst - return $ term ++ definition - concatMapM go entries -blockToParagraphs (Div (_, ("notes" : []), _) _) = return [] -blockToParagraphs (Div _ blks) = concatMapM blockToParagraphs blks -blockToParagraphs blk = do - P.report $ BlockNotRendered blk - return [] - --- Make sure the bullet env gets turned off after the first para. -multiParBullet :: PandocMonad m => [Block] -> P m [Paragraph] -multiParBullet [] = return [] -multiParBullet (b:bs) = do - pProps <- asks envParaProps - p <- blockToParagraphs b - ps <- local (\env -> env{envParaProps = pProps{pPropBullet = Nothing}}) $ - concatMapM blockToParagraphs bs - return $ p ++ ps - -cellToParagraphs :: PandocMonad m => Alignment -> TableCell -> P m [Paragraph] -cellToParagraphs algn tblCell = do - paras <- mapM (blockToParagraphs) tblCell - let alignment = case algn of - AlignLeft -> Just AlgnLeft - AlignRight -> Just AlgnRight - AlignCenter -> Just AlgnCenter - AlignDefault -> Nothing - paras' = map (map (\p -> p{paraProps = (paraProps p){pPropAlign = alignment}})) paras - return $ concat paras' - -rowToParagraphs :: PandocMonad m => [Alignment] -> [TableCell] -> P m [[Paragraph]] -rowToParagraphs algns tblCells = do - -- We have to make sure we have the right number of alignments - let pairs = zip (algns ++ repeat AlignDefault) tblCells - mapM (\(a, tc) -> cellToParagraphs a tc) pairs - -blockToShape :: PandocMonad m => Block -> P m Shape -blockToShape (Plain (il:_)) | Image attr ils (url, _) <- il = - Pic def url attr <$> (inlinesToParElems ils) -blockToShape (Para (il:_)) | Image attr ils (url, _) <- il = - Pic def url attr <$> (inlinesToParElems ils) -blockToShape (Plain (il:_)) | Link _ (il':_) target <- il - , Image attr ils (url, _) <- il' = - Pic def{picPropLink = Just target} url attr <$> (inlinesToParElems ils) -blockToShape (Para (il:_)) | Link _ (il':_) target <- il - , Image attr ils (url, _) <- il' = - Pic def{picPropLink = Just target} url attr <$> (inlinesToParElems ils) -blockToShape (Table caption algn _ hdrCells rows) = do - caption' <- inlinesToParElems caption - (pageWidth, _) <- asks envPresentationSize - hdrCells' <- rowToParagraphs algn hdrCells - rows' <- mapM (rowToParagraphs algn) rows - let tblPr = if null hdrCells - then TableProps { tblPrFirstRow = False - , tblPrBandRow = True - } - else TableProps { tblPrFirstRow = True - , tblPrBandRow = True - } - colWidths = if null hdrCells - then case rows of - r : _ | not (null r) -> replicate (length r) $ - (pageWidth - (2 * hardcodedTableMargin))`div` (toInteger $ length r) - -- satisfy the compiler. This is the same as - -- saying that rows is empty, but the compiler - -- won't understand that `[]` exhausts the - -- alternatives. - _ -> [] - else replicate (length hdrCells) $ - (pageWidth - (2 * hardcodedTableMargin)) `div` (toInteger $ length hdrCells) - - return $ GraphicFrame [Tbl tblPr colWidths hdrCells' rows'] caption' -blockToShape blk = TextBox <$> blockToParagraphs blk - -blocksToShapes :: PandocMonad m => [Block] -> P m [Shape] -blocksToShapes blks = combineShapes <$> mapM blockToShape blks - -isImage :: Inline -> Bool -isImage (Image _ _ _) = True -isImage (Link _ ((Image _ _ _) : _) _) = True -isImage _ = False - -splitBlocks' :: Monad m => [Block] -> [[Block]] -> [Block] -> P m [[Block]] -splitBlocks' cur acc [] = return $ acc ++ (if null cur then [] else [cur]) -splitBlocks' cur acc (HorizontalRule : blks) = - splitBlocks' [] (acc ++ (if null cur then [] else [cur])) blks -splitBlocks' cur acc (h@(Header n _ _) : blks) = do - slideLevel <- asks envSlideLevel - case compare n slideLevel of - LT -> splitBlocks' [] (acc ++ (if null cur then [] else [cur]) ++ [[h]]) blks - EQ -> splitBlocks' [h] (acc ++ (if null cur then [] else [cur])) blks - GT -> splitBlocks' (cur ++ [h]) acc blks --- `blockToParagraphs` treats Plain and Para the same, so we can save --- some code duplication by treating them the same here. -splitBlocks' cur acc ((Plain ils) : blks) = splitBlocks' cur acc ((Para ils) : blks) -splitBlocks' cur acc ((Para (il:ils)) : blks) | isImage il = do - slideLevel <- asks envSlideLevel - case cur of - (Header n _ _) : [] | n == slideLevel -> - splitBlocks' [] - (acc ++ [cur ++ [Para [il]]]) - (if null ils then blks else (Para ils) : blks) - _ -> splitBlocks' [] - (acc ++ (if null cur then [] else [cur]) ++ [[Para [il]]]) - (if null ils then blks else (Para ils) : blks) -splitBlocks' cur acc (tbl@(Table _ _ _ _ _) : blks) = do - slideLevel <- asks envSlideLevel - case cur of - (Header n _ _) : [] | n == slideLevel -> - splitBlocks' [] (acc ++ [cur ++ [tbl]]) blks - _ -> splitBlocks' [] (acc ++ (if null cur then [] else [cur]) ++ [[tbl]]) blks -splitBlocks' cur acc (d@(Div (_, classes, _) _): blks) | "columns" `elem` classes = do - slideLevel <- asks envSlideLevel - case cur of - (Header n _ _) : [] | n == slideLevel -> - splitBlocks' [] (acc ++ [cur ++ [d]]) blks - _ -> splitBlocks' [] (acc ++ (if null cur then [] else [cur]) ++ [[d]]) blks -splitBlocks' cur acc (blk : blks) = splitBlocks' (cur ++ [blk]) acc blks - -splitBlocks :: Monad m => [Block] -> P m [[Block]] -splitBlocks = splitBlocks' [] [] - -blocksToSlide' :: PandocMonad m => Int -> [Block] -> P m Slide -blocksToSlide' lvl ((Header n (ident, _, _) ils) : blks) - | n < lvl = do - registerAnchorId ident - hdr <- inlinesToParElems ils - return $ TitleSlide {titleSlideHeader = hdr} - | n == lvl = do - registerAnchorId ident - hdr <- inlinesToParElems ils - -- Now get the slide without the header, and then add the header - -- in. - slide <- blocksToSlide' lvl blks - return $ case slide of - ContentSlide _ cont -> ContentSlide hdr cont - TwoColumnSlide _ contL contR -> TwoColumnSlide hdr contL contR - slide' -> slide' -blocksToSlide' _ (blk : blks) - | Div (_, classes, _) divBlks <- blk - , "columns" `elem` classes - , (Div (_, clsL, _) blksL) : (Div (_, clsR, _) blksR) : remaining <- divBlks - , "column" `elem` clsL, "column" `elem` clsR = do - unless (null blks) - (mapM (P.report . BlockNotRendered) blks >> return ()) - unless (null remaining) - (mapM (P.report . BlockNotRendered) remaining >> return ()) - mbSplitBlksL <- splitBlocks blksL - mbSplitBlksR <- splitBlocks blksR - let blksL' = case mbSplitBlksL of - bs : _ -> bs - [] -> [] - let blksR' = case mbSplitBlksR of - bs : _ -> bs - [] -> [] - shapesL <- blocksToShapes blksL' - shapesR <- blocksToShapes blksR' - return $ TwoColumnSlide { twoColumnSlideHeader = [] - , twoColumnSlideLeft = shapesL - , twoColumnSlideRight = shapesR - } -blocksToSlide' _ (blk : blks) = do - inNoteSlide <- asks envInNoteSlide - shapes <- if inNoteSlide - then forceFontSize noteSize $ blocksToShapes (blk : blks) - else blocksToShapes (blk : blks) - return $ ContentSlide { contentSlideHeader = [] - , contentSlideContent = shapes - } -blocksToSlide' _ [] = return $ ContentSlide { contentSlideHeader = [] - , contentSlideContent = [] - } - -blocksToSlide :: PandocMonad m => [Block] -> P m Slide -blocksToSlide blks = do - slideLevel <- asks envSlideLevel - blocksToSlide' slideLevel blks - -makeNoteEntry :: Int -> [Block] -> [Block] -makeNoteEntry n blks = - let enum = Str (show n ++ ".") - in - case blks of - (Para ils : blks') -> (Para $ enum : Space : ils) : blks' - _ -> (Para [enum]) : blks - -forceFontSize :: PandocMonad m => Pixels -> P m a -> P m a -forceFontSize px x = do - rpr <- asks envRunProps - local (\r -> r {envRunProps = rpr{rPropForceSize = Just px}}) x - --- We leave these as blocks because we will want to include them in --- the TOC. -makeNotesSlideBlocks :: PandocMonad m => P m [Block] -makeNotesSlideBlocks = do - noteIds <- gets stNoteIds - slideLevel <- asks envSlideLevel - meta <- asks envMetadata - -- Get identifiers so we can give the notes section a unique ident. - anchorSet <- M.keysSet <$> gets stAnchorMap - if M.null noteIds - then return [] - else do let title = case lookupMeta "notes-title" meta of - Just val -> metaValueToInlines val - Nothing -> [Str "Notes"] - ident = Shared.uniqueIdent title anchorSet - hdr = Header slideLevel (ident, [], []) title - blks <- return $ - concatMap (\(n, bs) -> makeNoteEntry n bs) $ - M.toList noteIds - return $ hdr : blks - -getMetaSlide :: PandocMonad m => P m (Maybe Slide) -getMetaSlide = do - meta <- asks envMetadata - title <- inlinesToParElems $ docTitle meta - subtitle <- inlinesToParElems $ - case lookupMeta "subtitle" meta of - Just (MetaString s) -> [Str s] - Just (MetaInlines ils) -> ils - Just (MetaBlocks [Plain ils]) -> ils - Just (MetaBlocks [Para ils]) -> ils - _ -> [] - authors <- mapM inlinesToParElems $ docAuthors meta - date <- inlinesToParElems $ docDate meta - if null title && null subtitle && null authors && null date - then return Nothing - else return $ Just $ MetadataSlide { metadataSlideTitle = title - , metadataSlideSubtitle = subtitle - , metadataSlideAuthors = authors - , metadataSlideDate = date - } - --- adapted from the markdown writer -elementToListItem :: PandocMonad m => Shared.Element -> P m [Block] -elementToListItem (Shared.Sec lev _nums (ident,_,_) headerText subsecs) = do - opts <- asks envOpts - let headerLink = if null ident - then walk Shared.deNote headerText - else [Link nullAttr (walk Shared.deNote headerText) - ('#':ident, "")] - listContents <- if null subsecs || lev >= writerTOCDepth opts - then return [] - else mapM elementToListItem subsecs - return [Plain headerLink, BulletList listContents] -elementToListItem (Shared.Blk _) = return [] - -makeTOCSlide :: PandocMonad m => [Block] -> P m Slide -makeTOCSlide blks = do - contents <- BulletList <$> mapM elementToListItem (Shared.hierarchicalize blks) - meta <- asks envMetadata - slideLevel <- asks envSlideLevel - let tocTitle = case lookupMeta "toc-title" meta of - Just val -> metaValueToInlines val - Nothing -> [Str "Table of Contents"] - hdr = Header slideLevel nullAttr tocTitle - sld <- blocksToSlide [hdr, contents] - return sld - -blocksToPresentation :: PandocMonad m => [Block] -> P m Presentation -blocksToPresentation blks = do - opts <- asks envOpts - let metadataStartNum = 1 - metadataslides <- maybeToList <$> getMetaSlide - let tocStartNum = metadataStartNum + length metadataslides - -- As far as I can tell, if we want to have a variable-length toc in - -- the future, we'll have to make it twice. Once to get the length, - -- and a second time to include the notes slide. We can't make the - -- notes slide before the body slides because we need to know if - -- there are notes, and we can't make either before the toc slide, - -- because we need to know its length to get slide numbers right. - -- - -- For now, though, since the TOC slide is only length 1, if it - -- exists, we'll just get the length, and then come back to make the - -- slide later - let tocSlidesLength = if writerTableOfContents opts then 1 else 0 - let bodyStartNum = tocStartNum + tocSlidesLength - blksLst <- splitBlocks blks - bodyslides <- mapM - (\(bs, n) -> local (\st -> st{envCurSlideId = n}) (blocksToSlide bs)) - (zip blksLst [bodyStartNum..]) - let noteStartNum = bodyStartNum + length bodyslides - notesSlideBlocks <- makeNotesSlideBlocks - -- now we come back and make the real toc... - tocSlides <- if writerTableOfContents opts - then do toc <- makeTOCSlide $ blks ++ notesSlideBlocks - return [toc] - else return [] - -- ... and the notes slide. We test to see if the blocks are empty, - -- because we don't want to make an empty slide. - notesSlides <- if null notesSlideBlocks - then return [] - else do notesSlide <- local - (\env -> env { envCurSlideId = noteStartNum - , envInNoteSlide = True - }) - (blocksToSlide $ notesSlideBlocks) - return [notesSlide] - return $ - Presentation $ - metadataslides ++ tocSlides ++ bodyslides ++ notesSlides - --------------------------------------------------------------------- - -copyFileToArchive :: PandocMonad m => Archive -> FilePath -> P m Archive -copyFileToArchive arch fp = do - refArchive <- asks envRefArchive - distArchive <- asks envDistArchive - case findEntryByPath fp refArchive `mplus` findEntryByPath fp distArchive of - Nothing -> fail $ fp ++ " missing in reference file" - Just e -> return $ addEntryToArchive e arch - -inheritedPatterns :: [Pattern] -inheritedPatterns = map compile [ "_rels/.rels" - , "docProps/app.xml" - , "docProps/core.xml" - , "ppt/slideLayouts/slideLayout*.xml" - , "ppt/slideLayouts/_rels/slideLayout*.xml.rels" - , "ppt/slideMasters/slideMaster1.xml" - , "ppt/slideMasters/_rels/slideMaster1.xml.rels" - , "ppt/theme/theme1.xml" - , "ppt/theme/_rels/theme1.xml.rels" - , "ppt/presProps.xml" - , "ppt/viewProps.xml" - , "ppt/tableStyles.xml" - , "ppt/media/image*" - ] - -patternToFilePaths :: PandocMonad m => Pattern -> P m [FilePath] -patternToFilePaths pat = do - refArchive <- asks envRefArchive - distArchive <- asks envDistArchive - - let archiveFiles = filesInArchive refArchive `union` filesInArchive distArchive - return $ filter (match pat) archiveFiles - -patternsToFilePaths :: PandocMonad m => [Pattern] -> P m [FilePath] -patternsToFilePaths pats = concat <$> mapM patternToFilePaths pats - --- Here are the files we'll require to make a Powerpoint document. If --- any of these are missing, we should error out of our build. -requiredFiles :: [FilePath] -requiredFiles = [ "_rels/.rels" - , "docProps/app.xml" - , "docProps/core.xml" - , "ppt/presProps.xml" - , "ppt/slideLayouts/slideLayout1.xml" - , "ppt/slideLayouts/_rels/slideLayout1.xml.rels" - , "ppt/slideLayouts/slideLayout2.xml" - , "ppt/slideLayouts/_rels/slideLayout2.xml.rels" - , "ppt/slideLayouts/slideLayout3.xml" - , "ppt/slideLayouts/_rels/slideLayout3.xml.rels" - , "ppt/slideLayouts/slideLayout4.xml" - , "ppt/slideLayouts/_rels/slideLayout4.xml.rels" - , "ppt/slideMasters/slideMaster1.xml" - , "ppt/slideMasters/_rels/slideMaster1.xml.rels" - , "ppt/theme/theme1.xml" - , "ppt/viewProps.xml" - , "ppt/tableStyles.xml" - ] - - -presentationToArchive :: PandocMonad m => Presentation -> P m Archive -presentationToArchive p@(Presentation slides) = do - filePaths <- patternsToFilePaths inheritedPatterns - - -- make sure all required files are available: - let missingFiles = filter (\fp -> not (fp `elem` filePaths)) requiredFiles - unless (null missingFiles) - (throwError $ - PandocSomeError $ - "The following required files are missing:\n" ++ - (unlines $ map (" " ++) missingFiles) - ) - - newArch' <- foldM copyFileToArchive emptyArchive filePaths - -- presentation entry and rels. We have to do the rels first to make - -- sure we know the correct offset for the rIds. - presEntry <- presentationToPresEntry p - presRelsEntry <- presentationToRelsEntry p - slideEntries <- mapM (\(s, n) -> slideToEntry s n) $ zip slides [1..] - slideRelEntries <- mapM (\(s,n) -> slideToSlideRelEntry s n) $ zip slides [1..] - -- These have to come after everything, because they need the info - -- built up in the state. - mediaEntries <- makeMediaEntries - contentTypesEntry <- presentationToContentTypes p >>= contentTypesToEntry - -- fold everything into our inherited archive and return it. - return $ foldr addEntryToArchive newArch' $ - slideEntries ++ - slideRelEntries ++ - mediaEntries ++ - [contentTypesEntry, presEntry, presRelsEntry] - --------------------------------------------------- - -combineShapes :: [Shape] -> [Shape] -combineShapes [] = [] -combineShapes (s : []) = [s] -combineShapes (pic@(Pic _ _ _ _) : ss) = pic : combineShapes ss -combineShapes ((TextBox []) : ss) = combineShapes ss -combineShapes (s : TextBox [] : ss) = combineShapes (s : ss) -combineShapes ((TextBox (p:ps)) : (TextBox (p':ps')) : ss) = - combineShapes $ TextBox ((p:ps) ++ (p':ps')) : ss -combineShapes (s:ss) = s : combineShapes ss - --------------------------------------------------- - -getLayout :: PandocMonad m => Slide -> P m Element -getLayout slide = do - let layoutpath = case slide of - (MetadataSlide _ _ _ _) -> "ppt/slideLayouts/slideLayout1.xml" - (TitleSlide _) -> "ppt/slideLayouts/slideLayout3.xml" - (ContentSlide _ _) -> "ppt/slideLayouts/slideLayout2.xml" - (TwoColumnSlide _ _ _) -> "ppt/slideLayouts/slideLayout4.xml" - distArchive <- asks envDistArchive - root <- case findEntryByPath layoutpath distArchive of - Just e -> case parseXMLDoc $ UTF8.toStringLazy $ fromEntry e of - Just element -> return $ element - Nothing -> throwError $ - PandocSomeError $ - layoutpath ++ " corrupt in reference file" - Nothing -> throwError $ - PandocSomeError $ - layoutpath ++ " missing in reference file" - return root - -shapeHasName :: NameSpaces -> String -> Element -> Bool -shapeHasName ns name element - | Just nvSpPr <- findChild (elemName ns "p" "nvSpPr") element - , Just cNvPr <- findChild (elemName ns "p" "cNvPr") nvSpPr - , Just nm <- findAttr (QName "name" Nothing Nothing) cNvPr = - nm == name - | otherwise = False - -shapeHasId :: NameSpaces -> String -> Element -> Bool -shapeHasId ns ident element - | Just nvSpPr <- findChild (elemName ns "p" "nvSpPr") element - , Just cNvPr <- findChild (elemName ns "p" "cNvPr") nvSpPr - , Just nm <- findAttr (QName "id" Nothing Nothing) cNvPr = - nm == ident - | otherwise = False - --- The content shape in slideLayout2 (Title/Content) has id=3 In --- slideLayout4 (two column) the left column is id=3, and the right --- column is id=4. -getContentShape :: PandocMonad m => NameSpaces -> Element -> P m Element -getContentShape ns spTreeElem - | isElem ns "p" "spTree" spTreeElem = do - contentType <- asks envContentType - let ident = case contentType of - NormalContent -> "3" - TwoColumnLeftContent -> "3" - TwoColumnRightContent -> "4" - case filterChild - (\e -> (isElem ns "p" "sp" e) && (shapeHasId ns ident e)) - spTreeElem - of - Just e -> return e - Nothing -> throwError $ - PandocSomeError $ - "Could not find shape for Powerpoint content" -getContentShape _ _ = throwError $ - PandocSomeError $ - "Attempted to find content on non shapeTree" - -getShapeDimensions :: NameSpaces - -> Element - -> Maybe ((Integer, Integer), (Integer, Integer)) -getShapeDimensions ns element - | isElem ns "p" "sp" element = do - spPr <- findChild (elemName ns "p" "spPr") element - xfrm <- findChild (elemName ns "a" "xfrm") spPr - off <- findChild (elemName ns "a" "off") xfrm - xS <- findAttr (QName "x" Nothing Nothing) off - yS <- findAttr (QName "y" Nothing Nothing) off - ext <- findChild (elemName ns "a" "ext") xfrm - cxS <- findAttr (QName "cx" Nothing Nothing) ext - cyS <- findAttr (QName "cy" Nothing Nothing) ext - (x, _) <- listToMaybe $ reads xS - (y, _) <- listToMaybe $ reads yS - (cx, _) <- listToMaybe $ reads cxS - (cy, _) <- listToMaybe $ reads cyS - return $ ((x `div` 12700, y `div` 12700), (cx `div` 12700, cy `div` 12700)) - | otherwise = Nothing - - -getMasterShapeDimensionsById :: String - -> Element - -> Maybe ((Integer, Integer), (Integer, Integer)) -getMasterShapeDimensionsById ident master = do - let ns = elemToNameSpaces master - cSld <- findChild (elemName ns "p" "cSld") master - spTree <- findChild (elemName ns "p" "spTree") cSld - sp <- filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasId ns ident e)) spTree - getShapeDimensions ns sp - -getContentShapeSize :: PandocMonad m - => NameSpaces - -> Element - -> Element - -> P m ((Integer, Integer), (Integer, Integer)) -getContentShapeSize ns layout master - | isElem ns "p" "sldLayout" layout - , Just cSld <- findChild (elemName ns "p" "cSld") layout - , Just spTree <- findChild (elemName ns "p" "spTree") cSld = do - sp <- getContentShape ns spTree - case getShapeDimensions ns sp of - Just sz -> return sz - Nothing -> do let mbSz = - findChild (elemName ns "p" "nvSpPr") sp >>= - findChild (elemName ns "p" "cNvPr") >>= - findAttr (QName "id" Nothing Nothing) >>= - flip getMasterShapeDimensionsById master - case mbSz of - Just sz' -> return sz' - Nothing -> throwError $ - PandocSomeError $ - "Couldn't find necessary content shape size" -getContentShapeSize _ _ _ = throwError $ - PandocSomeError $ - "Attempted to find content shape size in non-layout" - -replaceNamedChildren :: NameSpaces - -> String - -> String - -> [Element] - -> Element - -> Element -replaceNamedChildren ns prefix name newKids element = - element { elContent = concat $ fun True $ elContent element } - where - fun :: Bool -> [Content] -> [[Content]] - fun _ [] = [] - fun switch ((Elem e) : conts) | isElem ns prefix name e = - if switch - then (map Elem $ newKids) : fun False conts - else fun False conts - fun switch (cont : conts) = [cont] : fun switch conts - ----------------------------------------------------------------- - -registerLink :: PandocMonad m => (URL, String) -> P m Int -registerLink link = do - curSlideId <- asks envCurSlideId - linkReg <- gets stLinkIds - mediaReg <- gets stMediaIds - let maxLinkId = case M.lookup curSlideId linkReg of - Just mp -> case M.keys mp of - [] -> 1 - ks -> maximum ks - Nothing -> 1 - maxMediaId = case M.lookup curSlideId mediaReg of - Just [] -> 1 - Just mInfos -> maximum $ map mInfoLocalId mInfos - Nothing -> 1 - maxId = max maxLinkId maxMediaId - slideLinks = case M.lookup curSlideId linkReg of - Just mp -> M.insert (maxId + 1) link mp - Nothing -> M.singleton (maxId + 1) link - modify $ \st -> st{ stLinkIds = M.insert curSlideId slideLinks linkReg} - return $ maxId + 1 - -registerMedia :: PandocMonad m => FilePath -> [ParaElem] -> P m MediaInfo -registerMedia fp caption = do - curSlideId <- asks envCurSlideId - linkReg <- gets stLinkIds - mediaReg <- gets stMediaIds - globalIds <- gets stMediaGlobalIds - let maxLinkId = case M.lookup curSlideId linkReg of - Just mp -> case M.keys mp of - [] -> 1 - ks -> maximum ks - Nothing -> 1 - maxMediaId = case M.lookup curSlideId mediaReg of - Just [] -> 1 - Just mInfos -> maximum $ map mInfoLocalId mInfos - Nothing -> 1 - maxLocalId = max maxLinkId maxMediaId - - maxGlobalId = case M.elems globalIds of - [] -> 0 - ids -> maximum ids - - (imgBytes, mbMt) <- P.fetchItem fp - let imgExt = (mbMt >>= extensionFromMimeType >>= (\x -> return $ '.':x)) - <|> - case imageType imgBytes of - Just Png -> Just ".png" - Just Jpeg -> Just ".jpeg" - Just Gif -> Just ".gif" - Just Pdf -> Just ".pdf" - Just Eps -> Just ".eps" - Just Svg -> Just ".svg" - Nothing -> Nothing - - let newGlobalId = case M.lookup fp globalIds of - Just ident -> ident - Nothing -> maxGlobalId + 1 - - let newGlobalIds = M.insert fp newGlobalId globalIds - - let mediaInfo = MediaInfo { mInfoFilePath = fp - , mInfoLocalId = maxLocalId + 1 - , mInfoGlobalId = newGlobalId - , mInfoMimeType = mbMt - , mInfoExt = imgExt - , mInfoCaption = (not . null) caption - } - - let slideMediaInfos = case M.lookup curSlideId mediaReg of - Just minfos -> mediaInfo : minfos - Nothing -> [mediaInfo] - - - modify $ \st -> st{ stMediaIds = M.insert curSlideId slideMediaInfos mediaReg - , stMediaGlobalIds = newGlobalIds - } - return mediaInfo - -makeMediaEntry :: PandocMonad m => MediaInfo -> P m Entry -makeMediaEntry mInfo = do - epochtime <- (floor . utcTimeToPOSIXSeconds) <$> asks envUTCTime - (imgBytes, _) <- P.fetchItem (mInfoFilePath mInfo) - let ext = case mInfoExt mInfo of - Just e -> e - Nothing -> "" - let fp = "ppt/media/image" ++ (show $ mInfoGlobalId mInfo) ++ ext - return $ toEntry fp epochtime $ BL.fromStrict imgBytes - -makeMediaEntries :: PandocMonad m => P m [Entry] -makeMediaEntries = do - mediaInfos <- gets stMediaIds - let allInfos = mconcat $ M.elems mediaInfos - mapM makeMediaEntry allInfos - --- -- | Scales the image to fit the page --- -- sizes are passed in emu --- fitToPage' :: (Double, Double) -- image size in emu --- -> Integer -- pageWidth --- -> Integer -- pageHeight --- -> (Integer, Integer) -- imagesize --- fitToPage' (x, y) pageWidth pageHeight --- -- Fixes width to the page width and scales the height --- | x <= fromIntegral pageWidth && y <= fromIntegral pageHeight = --- (floor x, floor y) --- | x / fromIntegral pageWidth > y / fromIntegral pageWidth = --- (pageWidth, floor $ ((fromIntegral pageWidth) / x) * y) --- | otherwise = --- (floor $ ((fromIntegral pageHeight) / y) * x, pageHeight) - --- positionImage :: (Double, Double) -> Integer -> Integer -> (Integer, Integer) --- positionImage (x, y) pageWidth pageHeight = --- let (x', y') = fitToPage' (x, y) pageWidth pageHeight --- in --- ((pageWidth - x') `div` 2, (pageHeight - y') `div` 2) - -getMaster :: PandocMonad m => P m Element -getMaster = do - refArchive <- asks envRefArchive - distArchive <- asks envDistArchive - parseXml refArchive distArchive "ppt/slideMasters/slideMaster1.xml" - --- We want to get the header dimensions, so we can make sure that the --- image goes underneath it. We only use this in a content slide if it --- has a header. - --- getHeaderSize :: PandocMonad m => P m ((Integer, Integer), (Integer, Integer)) --- getHeaderSize = do --- master <- getMaster --- let ns = elemToNameSpaces master --- sps = [master] >>= --- findChildren (elemName ns "p" "cSld") >>= --- findChildren (elemName ns "p" "spTree") >>= --- findChildren (elemName ns "p" "sp") --- mbXfrm = --- listToMaybe (filter (shapeHasName ns "Title Placeholder 1") sps) >>= --- findChild (elemName ns "p" "spPr") >>= --- findChild (elemName ns "a" "xfrm") --- xoff = mbXfrm >>= --- findChild (elemName ns "a" "off") >>= --- findAttr (QName "x" Nothing Nothing) >>= --- (listToMaybe . (\s -> reads s :: [(Integer, String)])) --- yoff = mbXfrm >>= --- findChild (elemName ns "a" "off") >>= --- findAttr (QName "y" Nothing Nothing) >>= --- (listToMaybe . (\s -> reads s :: [(Integer, String)])) --- xext = mbXfrm >>= --- findChild (elemName ns "a" "ext") >>= --- findAttr (QName "cx" Nothing Nothing) >>= --- (listToMaybe . (\s -> reads s :: [(Integer, String)])) --- yext = mbXfrm >>= --- findChild (elemName ns "a" "ext") >>= --- findAttr (QName "cy" Nothing Nothing) >>= --- (listToMaybe . (\s -> reads s :: [(Integer, String)])) --- off = case xoff of --- Just (xoff', _) | Just (yoff',_) <- yoff -> (xoff', yoff') --- _ -> (1043490, 1027664) --- ext = case xext of --- Just (xext', _) | Just (yext',_) <- yext -> (xext', yext') --- _ -> (7024744, 1143000) --- return $ (off, ext) - --- Hard-coded for now --- captionPosition :: ((Integer, Integer), (Integer, Integer)) --- captionPosition = ((457200, 6061972), (8229600, 527087)) - -captionHeight :: Integer -captionHeight = 40 - -createCaption :: PandocMonad m - => ((Integer, Integer), (Integer, Integer)) - -> [ParaElem] - -> P m Element -createCaption contentShapeDimensions paraElements = do - let para = Paragraph def{pPropAlign = Just AlgnCenter} paraElements - elements <- mapM paragraphToElement [para] - let ((x, y), (cx, cy)) = contentShapeDimensions - let txBody = mknode "p:txBody" [] $ - [mknode "a:bodyPr" [] (), mknode "a:lstStyle" [] ()] ++ elements - return $ - mknode "p:sp" [] [ mknode "p:nvSpPr" [] - [ mknode "p:cNvPr" [("id","1"), ("name","TextBox 3")] () - , mknode "p:cNvSpPr" [("txBox", "1")] () - , mknode "p:nvPr" [] () - ] - , mknode "p:spPr" [] - [ mknode "a:xfrm" [] - [ mknode "a:off" [("x", show $ 12700 * x), - ("y", show $ 12700 * (y + cy - captionHeight))] () - , mknode "a:ext" [("cx", show $ 12700 * cx), - ("cy", show $ 12700 * captionHeight)] () - ] - , mknode "a:prstGeom" [("prst", "rect")] - [ mknode "a:avLst" [] () - ] - , mknode "a:noFill" [] () - ] - , txBody - ] - -makePicElements :: PandocMonad m - => Element - -> PicProps - -> MediaInfo - -> Text.Pandoc.Definition.Attr - -> [ParaElem] - -> P m [Element] -makePicElements layout picProps mInfo _ alt = do - opts <- asks envOpts - (pageWidth, pageHeight) <- asks envPresentationSize - -- hasHeader <- asks envSlideHasHeader - let hasCaption = mInfoCaption mInfo - (imgBytes, _) <- P.fetchItem (mInfoFilePath mInfo) - let (pxX, pxY) = case imageSize opts imgBytes of - Right sz -> sizeInPixels $ sz - Left _ -> sizeInPixels $ def - master <- getMaster - let ns = elemToNameSpaces layout - ((x, y), (cx, cytmp)) <- getContentShapeSize ns layout master - `catchError` - (\_ -> return ((0, 0), (pageWidth, pageHeight))) - - let cy = if hasCaption then cytmp - captionHeight else cytmp - - let imgRatio = fromIntegral pxX / fromIntegral pxY :: Double - boxRatio = fromIntegral cx / fromIntegral cy :: Double - (dimX, dimY) = if imgRatio > boxRatio - then (fromIntegral cx, fromIntegral cx / imgRatio) - else (fromIntegral cy * imgRatio, fromIntegral cy) - - (dimX', dimY') = (round dimX * 12700, round dimY * 12700) :: (Integer, Integer) - (xoff, yoff) = (fromIntegral x + (fromIntegral cx - dimX) / 2, - fromIntegral y + (fromIntegral cy - dimY) / 2) - (xoff', yoff') = (round xoff * 12700, round yoff * 12700) :: (Integer, Integer) - - let cNvPicPr = mknode "p:cNvPicPr" [] $ - mknode "a:picLocks" [("noGrp","1") - ,("noChangeAspect","1")] () - -- cNvPr will contain the link information so we do that separately, - -- and register the link if necessary. - let cNvPrAttr = [("descr", mInfoFilePath mInfo), ("id","0"),("name","Picture 1")] - cNvPr <- case picPropLink picProps of - Just link -> do idNum <- registerLink link - return $ mknode "p:cNvPr" cNvPrAttr $ - mknode "a:hlinkClick" [("r:id", "rId" ++ show idNum)] () - Nothing -> return $ mknode "p:cNvPr" cNvPrAttr () - let nvPicPr = mknode "p:nvPicPr" [] - [ cNvPr - , cNvPicPr - , mknode "p:nvPr" [] ()] - let blipFill = mknode "p:blipFill" [] - [ mknode "a:blip" [("r:embed", "rId" ++ (show $ mInfoLocalId mInfo))] () - , mknode "a:stretch" [] $ - mknode "a:fillRect" [] () ] - let xfrm = mknode "a:xfrm" [] - [ mknode "a:off" [("x",show xoff'), ("y",show yoff')] () - , mknode "a:ext" [("cx",show dimX') - ,("cy",show dimY')] () ] - let prstGeom = mknode "a:prstGeom" [("prst","rect")] $ - mknode "a:avLst" [] () - let ln = mknode "a:ln" [("w","9525")] - [ mknode "a:noFill" [] () - , mknode "a:headEnd" [] () - , mknode "a:tailEnd" [] () ] - let spPr = mknode "p:spPr" [("bwMode","auto")] - [xfrm, prstGeom, mknode "a:noFill" [] (), ln] - - let picShape = mknode "p:pic" [] - [ nvPicPr - , blipFill - , spPr ] - - -- And now, maybe create the caption: - if hasCaption - then do cap <- createCaption ((x, y), (cx, cytmp)) alt - return [picShape, cap] - else return [picShape] - --- Currently hardcoded, until I figure out how to make it dynamic. -blockQuoteSize :: Pixels -blockQuoteSize = 20 - -noteSize :: Pixels -noteSize = 18 - -paraElemToElement :: PandocMonad m => ParaElem -> P m Element -paraElemToElement Break = return $ mknode "a:br" [] () -paraElemToElement (Run rpr s) = do - let sizeAttrs = case rPropForceSize rpr of - Just n -> [("sz", (show $ n * 100))] - Nothing -> [] - attrs = sizeAttrs ++ - if rPropCode rpr - then [] - else (if rPropBold rpr then [("b", "1")] else []) ++ - (if rPropItalics rpr then [("i", "1")] else []) ++ - (case rStrikethrough rpr of - Just NoStrike -> [("strike", "noStrike")] - Just SingleStrike -> [("strike", "sngStrike")] - Just DoubleStrike -> [("strike", "dblStrike")] - Nothing -> []) ++ - (case rBaseline rpr of - Just n -> [("baseline", show n)] - Nothing -> []) ++ - (case rCap rpr of - Just NoCapitals -> [("cap", "none")] - Just SmallCapitals -> [("cap", "small")] - Just AllCapitals -> [("cap", "all")] - Nothing -> []) ++ - [] - linkProps <- case rLink rpr of - Just link -> do - idNum <- registerLink link - -- first we have to make sure that if it's an - -- anchor, it's in the anchor map. If not, there's - -- no link. - anchorMap <- gets stAnchorMap - return $ case link of - -- anchor with nothing in the map - ('#':target, _) | Nothing <- M.lookup target anchorMap -> - [] - -- anchor that is in the map - ('#':_, _) -> - let linkAttrs = - [ ("r:id", "rId" ++ show idNum) - , ("action", "ppaction://hlinksldjump") - ] - in [mknode "a:hlinkClick" linkAttrs ()] - -- external - _ -> - let linkAttrs = - [ ("r:id", "rId" ++ show idNum) - ] - in [mknode "a:hlinkClick" linkAttrs ()] - Nothing -> return [] - let propContents = if rPropCode rpr - then [mknode "a:latin" [("typeface", "Courier")] ()] - else linkProps - return $ mknode "a:r" [] [ mknode "a:rPr" attrs propContents - , mknode "a:t" [] s - ] -paraElemToElement (MathElem mathType texStr) = do - res <- convertMath writeOMML mathType (unTeXString texStr) - case res of - Right r -> return $ mknode "a14:m" [] $ addMathInfo r - Left (Str s) -> paraElemToElement (Run def s) - Left _ -> throwError $ PandocShouldNeverHappenError "non-string math fallback" - --- This is a bit of a kludge -- really requires adding an option to --- TeXMath, but since that's a different package, we'll do this one --- step at a time. -addMathInfo :: Element -> Element -addMathInfo element = - let mathspace = Attr { attrKey = (QName "m" Nothing (Just "xmlns")) - , attrVal = "http://schemas.openxmlformats.org/officeDocument/2006/math" - } - in add_attr mathspace element - --- We look through the element to see if it contains an a14:m --- element. If so, we surround it. This is a bit ugly, but it seems --- more dependable than looking through shapes for math. Plus this is --- an xml implementation detail, so it seems to make sense to do it at --- the xml level. -surroundWithMathAlternate :: Element -> Element -surroundWithMathAlternate element = - case findElement (QName "m" Nothing (Just "a14")) element of - Just _ -> - mknode "mc:AlternateContent" - [("xmlns:mc", "http://schemas.openxmlformats.org/markup-compatibility/2006") - ] [ mknode "mc:Choice" - [ ("xmlns:a14", "http://schemas.microsoft.com/office/drawing/2010/main") - , ("Requires", "a14")] [ element ] - ] - Nothing -> element - -paragraphToElement :: PandocMonad m => Paragraph -> P m Element -paragraphToElement par = do - let - attrs = [("lvl", show $ pPropLevel $ paraProps par)] ++ - (case pPropMarginLeft (paraProps par) of - Just px -> [("marL", show $ 12700 * px), ("indent", "0")] - Nothing -> [] - ) ++ - (case pPropAlign (paraProps par) of - Just AlgnLeft -> [("algn", "l")] - Just AlgnRight -> [("algn", "r")] - Just AlgnCenter -> [("algn", "ctr")] - Nothing -> [] - ) - props = [] ++ - (case pPropSpaceBefore $ paraProps par of - Just px -> [mknode "a:spcBef" [] [ - mknode "a:spcPts" [("val", show $ 100 * px)] () - ] - ] - Nothing -> [] - ) ++ - (case pPropBullet $ paraProps par of - Just Bullet -> [] - Just (AutoNumbering attrs') -> - [mknode "a:buAutoNum" [("type", autoNumberingToType attrs')] ()] - Nothing -> [mknode "a:buNone" [] ()] - ) - paras <- mapM paraElemToElement (combineParaElems $ paraElems par) - return $ mknode "a:p" [] $ [mknode "a:pPr" attrs props] ++ paras - -shapeToElement :: PandocMonad m => Element -> Shape -> P m Element -shapeToElement layout (TextBox paras) - | ns <- elemToNameSpaces layout - , Just cSld <- findChild (elemName ns "p" "cSld") layout - , Just spTree <- findChild (elemName ns "p" "spTree") cSld = do - sp <- getContentShape ns spTree - elements <- mapM paragraphToElement paras - let txBody = mknode "p:txBody" [] $ - [mknode "a:bodyPr" [] (), mknode "a:lstStyle" [] ()] ++ elements - emptySpPr = mknode "p:spPr" [] () - return $ - surroundWithMathAlternate $ - replaceNamedChildren ns "p" "txBody" [txBody] $ - replaceNamedChildren ns "p" "spPr" [emptySpPr] $ - sp --- GraphicFrame and Pic should never reach this. -shapeToElement _ _ = return $ mknode "p:sp" [] () - -shapeToElements :: PandocMonad m => Element -> Shape -> P m [Element] -shapeToElements layout (Pic picProps fp attr alt) = do - mInfo <- registerMedia fp alt - case mInfoExt mInfo of - Just _ -> do - makePicElements layout picProps mInfo attr alt - Nothing -> shapeToElements layout $ TextBox [Paragraph def alt] -shapeToElements layout (GraphicFrame tbls cptn) = - graphicFrameToElements layout tbls cptn -shapeToElements layout shp = do - element <- shapeToElement layout shp - return [element] - -shapesToElements :: PandocMonad m => Element -> [Shape] -> P m [Element] -shapesToElements layout shps = do - concat <$> mapM (shapeToElements layout) shps - -hardcodedTableMargin :: Integer -hardcodedTableMargin = 36 - -graphicFrameToElements :: PandocMonad m => Element -> [Graphic] -> [ParaElem] -> P m [Element] -graphicFrameToElements layout tbls caption = do - -- get the sizing - master <- getMaster - (pageWidth, pageHeight) <- asks envPresentationSize - let ns = elemToNameSpaces layout - ((x, y), (cx, cytmp)) <- getContentShapeSize ns layout master - `catchError` - (\_ -> return ((0, 0), (pageWidth, pageHeight))) - - let cy = if (not $ null caption) then cytmp - captionHeight else cytmp - - elements <- mapM graphicToElement tbls - let graphicFrameElts = - mknode "p:graphicFrame" [] $ - [ mknode "p:nvGraphicFramePr" [] $ - [ mknode "p:cNvPr" [("id", "6"), ("name", "Content Placeholder 5")] () - , mknode "p:cNvGraphicFramePr" [] $ - [mknode "a:graphicFrameLocks" [("noGrp", "1")] ()] - , mknode "p:nvPr" [] $ - [mknode "p:ph" [("idx", "1")] ()] - ] - , mknode "p:xfrm" [] $ - [ mknode "a:off" [("x", show $ 12700 * x), ("y", show $ 12700 * y)] () - , mknode "a:ext" [("cx", show $ 12700 * cx), ("cy", show $ 12700 * cy)] () - ] - ] ++ elements - - if (not $ null caption) - then do capElt <- createCaption ((x, y), (cx, cytmp)) caption - return [graphicFrameElts, capElt] - else return [graphicFrameElts] - -graphicToElement :: PandocMonad m => Graphic -> P m Element -graphicToElement (Tbl tblPr colWidths hdrCells rows) = do - let cellToOpenXML paras = - do elements <- mapM paragraphToElement paras - let elements' = if null elements - then [mknode "a:p" [] [mknode "a:endParaRPr" [] ()]] - else elements - return $ - [mknode "a:txBody" [] $ - ([ mknode "a:bodyPr" [] () - , mknode "a:lstStyle" [] ()] - ++ elements')] - headers' <- mapM cellToOpenXML hdrCells - rows' <- mapM (mapM cellToOpenXML) rows - let borderProps = mknode "a:tcPr" [] () - let emptyCell = [mknode "a:p" [] [mknode "a:pPr" [] ()]] - let mkcell border contents = mknode "a:tc" [] - $ (if null contents - then emptyCell - else contents) ++ [ borderProps | border ] - let mkrow border cells = mknode "a:tr" [("h", "0")] $ map (mkcell border) cells - - let mkgridcol w = mknode "a:gridCol" - [("w", show ((12700 * w) :: Integer))] () - let hasHeader = not (all null hdrCells) - return $ mknode "a:graphic" [] $ - [mknode "a:graphicData" [("uri", "http://schemas.openxmlformats.org/drawingml/2006/table")] $ - [mknode "a:tbl" [] $ - [ mknode "a:tblPr" [ ("firstRow", if tblPrFirstRow tblPr then "1" else "0") - , ("bandRow", if tblPrBandRow tblPr then "1" else "0") - ] () - , mknode "a:tblGrid" [] (if all (==0) colWidths - then [] - else map mkgridcol colWidths) - ] - ++ [ mkrow True headers' | hasHeader ] ++ map (mkrow False) rows' - ] - ] - -getShapeByName :: NameSpaces -> Element -> String -> Maybe Element -getShapeByName ns spTreeElem name - | isElem ns "p" "spTree" spTreeElem = - filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasName ns name e)) spTreeElem - | otherwise = Nothing - --- getShapeById :: NameSpaces -> Element -> String -> Maybe Element --- getShapeById ns spTreeElem ident --- | isElem ns "p" "spTree" spTreeElem = --- filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasId ns ident e)) spTreeElem --- | otherwise = Nothing - -nonBodyTextToElement :: PandocMonad m => Element -> String -> [ParaElem] -> P m Element -nonBodyTextToElement layout shapeName paraElements - | ns <- elemToNameSpaces layout - , Just cSld <- findChild (elemName ns "p" "cSld") layout - , Just spTree <- findChild (elemName ns "p" "spTree") cSld - , Just sp <- getShapeByName ns spTree shapeName = do - let hdrPara = Paragraph def paraElements - element <- paragraphToElement hdrPara - let txBody = mknode "p:txBody" [] $ - [mknode "a:bodyPr" [] (), mknode "a:lstStyle" [] ()] ++ - [element] - return $ replaceNamedChildren ns "p" "txBody" [txBody] sp - -- XXX: TODO - | otherwise = return $ mknode "p:sp" [] () - -contentToElement :: PandocMonad m => Element -> [ParaElem] -> [Shape] -> P m Element -contentToElement layout hdrShape shapes - | ns <- elemToNameSpaces layout - , Just cSld <- findChild (elemName ns "p" "cSld") layout - , Just spTree <- findChild (elemName ns "p" "spTree") cSld = do - element <- nonBodyTextToElement layout "Title 1" hdrShape - let hdrShapeElements = if null hdrShape - then [] - else [element] - contentElements <- local - (\env -> env {envContentType = NormalContent}) - (shapesToElements layout shapes) - return $ - replaceNamedChildren ns "p" "sp" - (hdrShapeElements ++ contentElements) - spTree -contentToElement _ _ _ = return $ mknode "p:sp" [] () - -twoColumnToElement :: PandocMonad m => Element -> [ParaElem] -> [Shape] -> [Shape] -> P m Element -twoColumnToElement layout hdrShape shapesL shapesR - | ns <- elemToNameSpaces layout - , Just cSld <- findChild (elemName ns "p" "cSld") layout - , Just spTree <- findChild (elemName ns "p" "spTree") cSld = do - element <- nonBodyTextToElement layout "Title 1" hdrShape - let hdrShapeElements = if null hdrShape - then [] - else [element] - contentElementsL <- local - (\env -> env {envContentType =TwoColumnLeftContent}) - (shapesToElements layout shapesL) - contentElementsR <- local - (\env -> env {envContentType =TwoColumnRightContent}) - (shapesToElements layout shapesR) - -- let contentElementsL' = map (setIdx ns "1") contentElementsL - -- contentElementsR' = map (setIdx ns "2") contentElementsR - return $ - replaceNamedChildren ns "p" "sp" - (hdrShapeElements ++ contentElementsL ++ contentElementsR) - spTree -twoColumnToElement _ _ _ _= return $ mknode "p:sp" [] () - - -titleToElement :: PandocMonad m => Element -> [ParaElem] -> P m Element -titleToElement layout titleElems - | ns <- elemToNameSpaces layout - , Just cSld <- findChild (elemName ns "p" "cSld") layout - , Just spTree <- findChild (elemName ns "p" "spTree") cSld = do - element <- nonBodyTextToElement layout "Title 1" titleElems - let titleShapeElements = if null titleElems - then [] - else [element] - return $ replaceNamedChildren ns "p" "sp" titleShapeElements spTree -titleToElement _ _ = return $ mknode "p:sp" [] () - -metadataToElement :: PandocMonad m => Element -> [ParaElem] -> [ParaElem] -> [[ParaElem]] -> [ParaElem] -> P m Element -metadataToElement layout titleElems subtitleElems authorsElems dateElems - | ns <- elemToNameSpaces layout - , Just cSld <- findChild (elemName ns "p" "cSld") layout - , Just spTree <- findChild (elemName ns "p" "spTree") cSld = do - titleShapeElements <- if null titleElems - then return [] - else sequence [nonBodyTextToElement layout "Title 1" titleElems] - let combinedAuthorElems = intercalate [Break] authorsElems - subtitleAndAuthorElems = intercalate [Break, Break] [subtitleElems, combinedAuthorElems] - subtitleShapeElements <- if null subtitleAndAuthorElems - then return [] - else sequence [nonBodyTextToElement layout "Subtitle 2" subtitleAndAuthorElems] - dateShapeElements <- if null dateElems - then return [] - else sequence [nonBodyTextToElement layout "Date Placeholder 3" dateElems] - return $ replaceNamedChildren ns "p" "sp" - (titleShapeElements ++ subtitleShapeElements ++ dateShapeElements) - spTree -metadataToElement _ _ _ _ _ = return $ mknode "p:sp" [] () - -slideToElement :: PandocMonad m => Slide -> P m Element -slideToElement s@(ContentSlide hdrElems shapes) = do - layout <- getLayout s - spTree <- local (\env -> if null hdrElems - then env - else env{envSlideHasHeader=True}) $ - contentToElement layout hdrElems shapes - return $ mknode "p:sld" - [ ("xmlns:a", "http://schemas.openxmlformats.org/drawingml/2006/main"), - ("xmlns:r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"), - ("xmlns:p", "http://schemas.openxmlformats.org/presentationml/2006/main") - ] [mknode "p:cSld" [] [spTree]] -slideToElement s@(TwoColumnSlide hdrElems shapesL shapesR) = do - layout <- getLayout s - spTree <- local (\env -> if null hdrElems - then env - else env{envSlideHasHeader=True}) $ - twoColumnToElement layout hdrElems shapesL shapesR - return $ mknode "p:sld" - [ ("xmlns:a", "http://schemas.openxmlformats.org/drawingml/2006/main"), - ("xmlns:r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"), - ("xmlns:p", "http://schemas.openxmlformats.org/presentationml/2006/main") - ] [mknode "p:cSld" [] [spTree]] -slideToElement s@(TitleSlide hdrElems) = do - layout <- getLayout s - spTree <- titleToElement layout hdrElems - return $ mknode "p:sld" - [ ("xmlns:a", "http://schemas.openxmlformats.org/drawingml/2006/main"), - ("xmlns:r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"), - ("xmlns:p", "http://schemas.openxmlformats.org/presentationml/2006/main") - ] [mknode "p:cSld" [] [spTree]] -slideToElement s@(MetadataSlide titleElems subtitleElems authorElems dateElems) = do - layout <- getLayout s - spTree <- metadataToElement layout titleElems subtitleElems authorElems dateElems - return $ mknode "p:sld" - [ ("xmlns:a", "http://schemas.openxmlformats.org/drawingml/2006/main"), - ("xmlns:r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"), - ("xmlns:p", "http://schemas.openxmlformats.org/presentationml/2006/main") - ] [mknode "p:cSld" [] [spTree]] - ------------------------------------------------------------------------ - -slideToFilePath :: Slide -> Int -> FilePath -slideToFilePath _ idNum = "slide" ++ (show $ idNum) ++ ".xml" - -slideToSlideId :: Monad m => Slide -> Int -> P m String -slideToSlideId _ idNum = do - n <- asks envSlideIdOffset - return $ "rId" ++ (show $ idNum + n) - - -data Relationship = Relationship { relId :: Int - , relType :: MimeType - , relTarget :: FilePath - } deriving (Show, Eq) - -elementToRel :: Element -> Maybe Relationship -elementToRel element - | elName element == QName "Relationship" (Just "http://schemas.openxmlformats.org/package/2006/relationships") Nothing = - do rId <- findAttr (QName "Id" Nothing Nothing) element - numStr <- stripPrefix "rId" rId - num <- case reads numStr :: [(Int, String)] of - (n, _) : _ -> Just n - [] -> Nothing - type' <- findAttr (QName "Type" Nothing Nothing) element - target <- findAttr (QName "Target" Nothing Nothing) element - return $ Relationship num type' target - | otherwise = Nothing - -slideToPresRel :: Monad m => Slide -> Int -> P m Relationship -slideToPresRel slide idNum = do - n <- asks envSlideIdOffset - let rId = idNum + n - fp = "slides/" ++ slideToFilePath slide idNum - return $ Relationship { relId = rId - , relType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" - , relTarget = fp - } - -getRels :: PandocMonad m => P m [Relationship] -getRels = do - refArchive <- asks envRefArchive - distArchive <- asks envDistArchive - relsElem <- parseXml refArchive distArchive "ppt/_rels/presentation.xml.rels" - let globalNS = "http://schemas.openxmlformats.org/package/2006/relationships" - let relElems = findChildren (QName "Relationship" (Just globalNS) Nothing) relsElem - return $ mapMaybe elementToRel relElems - -presentationToRels :: PandocMonad m => Presentation -> P m [Relationship] -presentationToRels (Presentation slides) = do - mySlideRels <- mapM (\(s, n) -> slideToPresRel s n) $ zip slides [1..] - rels <- getRels - let relsWithoutSlides = filter (\r -> relType r /= "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide") rels - -- We want to make room for the slides in the id space. The slides - -- will start at Id2 (since Id1 is for the slide master). There are - -- two slides in the data file, but that might change in the future, - -- so we will do this: - -- - -- 1. We look to see what the minimum relWithoutSlide id (greater than 1) is. - -- 2. We add the difference between this and the number of slides to - -- all relWithoutSlide rels (unless they're 1) - - let minRelNotOne = case filter (1<) $ map relId relsWithoutSlides of - [] -> 0 -- doesn't matter in this case, since - -- there will be nothing to map the - -- function over - l -> minimum l - - modifyRelNum :: Int -> Int - modifyRelNum 1 = 1 - modifyRelNum n = n - minRelNotOne + 2 + length slides - - relsWithoutSlides' = map (\r -> r{relId = modifyRelNum $ relId r}) relsWithoutSlides - - return $ mySlideRels ++ relsWithoutSlides' - -relToElement :: Relationship -> Element -relToElement rel = mknode "Relationship" [ ("Id", "rId" ++ (show $ relId rel)) - , ("Type", relType rel) - , ("Target", relTarget rel) ] () - -relsToElement :: [Relationship] -> Element -relsToElement rels = mknode "Relationships" - [("xmlns", "http://schemas.openxmlformats.org/package/2006/relationships")] - (map relToElement rels) - -presentationToRelsEntry :: PandocMonad m => Presentation -> P m Entry -presentationToRelsEntry pres = do - rels <- presentationToRels pres - elemToEntry "ppt/_rels/presentation.xml.rels" $ relsToElement rels - -elemToEntry :: PandocMonad m => FilePath -> Element -> P m Entry -elemToEntry fp element = do - epochtime <- (floor . utcTimeToPOSIXSeconds) <$> asks envUTCTime - return $ toEntry fp epochtime $ renderXml element - -slideToEntry :: PandocMonad m => Slide -> Int -> P m Entry -slideToEntry slide idNum = do - local (\env -> env{envCurSlideId = idNum}) $ do - element <- slideToElement slide - elemToEntry ("ppt/slides/" ++ slideToFilePath slide idNum) element - -slideToSlideRelEntry :: PandocMonad m => Slide -> Int -> P m Entry -slideToSlideRelEntry slide idNum = do - element <- slideToSlideRelElement slide idNum - elemToEntry ("ppt/slides/_rels/" ++ slideToFilePath slide idNum ++ ".rels") element - -linkRelElement :: PandocMonad m => Int -> (URL, String) -> P m (Maybe Element) -linkRelElement idNum (url, _) = do - anchorMap <- gets stAnchorMap - case url of - -- if it's an anchor in the map, we use the slide number for an - -- internal link. - '#' : anchor | Just num <- M.lookup anchor anchorMap -> - return $ Just $ - mknode "Relationship" [ ("Id", "rId" ++ show idNum) - , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide") - , ("Target", "slide" ++ show num ++ ".xml") - ] () - -- if it's an anchor not in the map, we return nothing. - '#' : _ -> return Nothing - -- Anything else we treat as an external link - _ -> - return $ Just $ - mknode "Relationship" [ ("Id", "rId" ++ show idNum) - , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink") - , ("Target", url) - , ("TargetMode", "External") - ] () - -linkRelElements :: PandocMonad m => M.Map Int (URL, String) -> P m [Element] -linkRelElements mp = catMaybes <$> mapM (\(n, lnk) -> linkRelElement n lnk) (M.toList mp) - -mediaRelElement :: MediaInfo -> Element -mediaRelElement mInfo = - let ext = case mInfoExt mInfo of - Just e -> e - Nothing -> "" - in - mknode "Relationship" [ ("Id", "rId" ++ (show $ mInfoLocalId mInfo)) - , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image") - , ("Target", "../media/image" ++ (show $ mInfoGlobalId mInfo) ++ ext) - ] () - -slideToSlideRelElement :: PandocMonad m => Slide -> Int -> P m Element -slideToSlideRelElement slide idNum = do - let target = case slide of - (MetadataSlide _ _ _ _) -> "../slideLayouts/slideLayout1.xml" - (TitleSlide _) -> "../slideLayouts/slideLayout3.xml" - (ContentSlide _ _) -> "../slideLayouts/slideLayout2.xml" - (TwoColumnSlide _ _ _) -> "../slideLayouts/slideLayout4.xml" - - linkIds <- gets stLinkIds - mediaIds <- gets stMediaIds - - linkRels <- case M.lookup idNum linkIds of - Just mp -> linkRelElements mp - Nothing -> return [] - let mediaRels = case M.lookup idNum mediaIds of - Just mInfos -> map mediaRelElement mInfos - Nothing -> [] - - return $ - mknode "Relationships" - [("xmlns", "http://schemas.openxmlformats.org/package/2006/relationships")] - ([mknode "Relationship" [ ("Id", "rId1") - , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout") - , ("Target", target)] () - ] ++ linkRels ++ mediaRels) - -slideToSldIdElement :: PandocMonad m => Slide -> Int -> P m Element -slideToSldIdElement slide idNum = do - let id' = show $ idNum + 255 - rId <- slideToSlideId slide idNum - return $ mknode "p:sldId" [("id", id'), ("r:id", rId)] () - -presentationToSldIdLst :: PandocMonad m => Presentation -> P m Element -presentationToSldIdLst (Presentation slides) = do - ids <- mapM (\(s,n) -> slideToSldIdElement s n) (zip slides [1..]) - return $ mknode "p:sldIdLst" [] ids - -presentationToPresentationElement :: PandocMonad m => Presentation -> P m Element -presentationToPresentationElement pres = do - refArchive <- asks envRefArchive - distArchive <- asks envDistArchive - element <- parseXml refArchive distArchive "ppt/presentation.xml" - sldIdLst <- presentationToSldIdLst pres - - let modifySldIdLst :: Content -> Content - modifySldIdLst (Elem e) = case elName e of - (QName "sldIdLst" _ _) -> Elem sldIdLst - _ -> Elem e - modifySldIdLst ct = ct - - newContent = map modifySldIdLst $ elContent element - - return $ element{elContent = newContent} - -presentationToPresEntry :: PandocMonad m => Presentation -> P m Entry -presentationToPresEntry pres = presentationToPresentationElement pres >>= - elemToEntry "ppt/presentation.xml" - - - - -defaultContentTypeToElem :: DefaultContentType -> Element -defaultContentTypeToElem dct = - mknode "Default" - [("Extension", defContentTypesExt dct), - ("ContentType", defContentTypesType dct)] - () - -overrideContentTypeToElem :: OverrideContentType -> Element -overrideContentTypeToElem oct = - mknode "Override" - [("PartName", overrideContentTypesPart oct), - ("ContentType", overrideContentTypesType oct)] - () - -contentTypesToElement :: ContentTypes -> Element -contentTypesToElement ct = - let ns = "http://schemas.openxmlformats.org/package/2006/content-types" - in - mknode "Types" [("xmlns", ns)] $ - (map defaultContentTypeToElem $ contentTypesDefaults ct) ++ - (map overrideContentTypeToElem $ contentTypesOverrides ct) - -data DefaultContentType = DefaultContentType - { defContentTypesExt :: String - , defContentTypesType:: MimeType - } - deriving (Show, Eq) - -data OverrideContentType = OverrideContentType - { overrideContentTypesPart :: FilePath - , overrideContentTypesType :: MimeType - } - deriving (Show, Eq) - -data ContentTypes = ContentTypes { contentTypesDefaults :: [DefaultContentType] - , contentTypesOverrides :: [OverrideContentType] - } - deriving (Show, Eq) - -contentTypesToEntry :: PandocMonad m => ContentTypes -> P m Entry -contentTypesToEntry ct = elemToEntry "[Content_Types].xml" $ contentTypesToElement ct - -pathToOverride :: FilePath -> Maybe OverrideContentType -pathToOverride fp = OverrideContentType ("/" ++ fp) <$> (getContentType fp) - -mediaFileContentType :: FilePath -> Maybe DefaultContentType -mediaFileContentType fp = case takeExtension fp of - '.' : ext -> Just $ - DefaultContentType { defContentTypesExt = ext - , defContentTypesType = - case getMimeType fp of - Just mt -> mt - Nothing -> "application/octet-stream" - } - _ -> Nothing - -mediaContentType :: MediaInfo -> Maybe DefaultContentType -mediaContentType mInfo - | Just ('.' : ext) <- mInfoExt mInfo = - Just $ DefaultContentType { defContentTypesExt = ext - , defContentTypesType = - case mInfoMimeType mInfo of - Just mt -> mt - Nothing -> "application/octet-stream" - } - | otherwise = Nothing - -presentationToContentTypes :: PandocMonad m => Presentation -> P m ContentTypes -presentationToContentTypes (Presentation slides) = do - mediaInfos <- (mconcat . M.elems) <$> gets stMediaIds - filePaths <- patternsToFilePaths inheritedPatterns - let mediaFps = filter (match (compile "ppt/media/image*")) filePaths - let defaults = [ DefaultContentType "xml" "application/xml" - , DefaultContentType "rels" "application/vnd.openxmlformats-package.relationships+xml" - ] - mediaDefaults = nub $ - (mapMaybe mediaContentType $ mediaInfos) ++ - (mapMaybe mediaFileContentType $ mediaFps) - - inheritedOverrides = mapMaybe pathToOverride filePaths - presOverride = mapMaybe pathToOverride ["ppt/presentation.xml"] - slideOverrides = - mapMaybe - (\(s, n) -> - pathToOverride $ "ppt/slides/" ++ slideToFilePath s n) - (zip slides [1..]) - -- propOverride = mapMaybe pathToOverride ["docProps/core.xml"] - return $ ContentTypes - (defaults ++ mediaDefaults) - (inheritedOverrides ++ presOverride ++ slideOverrides) - -presML :: String -presML = "application/vnd.openxmlformats-officedocument.presentationml" - -noPresML :: String -noPresML = "application/vnd.openxmlformats-officedocument" - -getContentType :: FilePath -> Maybe MimeType -getContentType fp - | fp == "ppt/presentation.xml" = Just $ presML ++ ".presentation.main+xml" - | fp == "ppt/presProps.xml" = Just $ presML ++ ".presProps+xml" - | fp == "ppt/viewProps.xml" = Just $ presML ++ ".viewProps+xml" - | fp == "ppt/tableStyles.xml" = Just $ presML ++ ".tableStyles+xml" - | fp == "docProps/core.xml" = Just $ "application/vnd.openxmlformats-package.core-properties+xml" - | fp == "docProps/app.xml" = Just $ noPresML ++ ".extended-properties+xml" - | "ppt" : "slideMasters" : f : [] <- splitDirectories fp - , (_, ".xml") <- splitExtension f = - Just $ presML ++ ".slideMaster+xml" - | "ppt" : "slides" : f : [] <- splitDirectories fp - , (_, ".xml") <- splitExtension f = - Just $ presML ++ ".slide+xml" - | "ppt" : "notesMasters" : f : [] <- splitDirectories fp - , (_, ".xml") <- splitExtension f = - Just $ presML ++ ".notesMaster+xml" - | "ppt" : "notesSlides" : f : [] <- splitDirectories fp - , (_, ".xml") <- splitExtension f = - Just $ presML ++ ".notesSlide+xml" - | "ppt" : "theme" : f : [] <- splitDirectories fp - , (_, ".xml") <- splitExtension f = - Just $ noPresML ++ ".theme+xml" - | "ppt" : "slideLayouts" : _ : [] <- splitDirectories fp= - Just $ presML ++ ".slideLayout+xml" - | otherwise = Nothing - -------------------------------------------------------- - -combineParaElems' :: Maybe ParaElem -> [ParaElem] -> [ParaElem] -combineParaElems' mbPElem [] = maybeToList mbPElem -combineParaElems' Nothing (pElem : pElems) = - combineParaElems' (Just pElem) pElems -combineParaElems' (Just pElem') (pElem : pElems) - | Run rPr' s' <- pElem' - , Run rPr s <- pElem - , rPr == rPr' = - combineParaElems' (Just $ Run rPr' $ s' ++ s) pElems - | otherwise = - pElem' : combineParaElems' (Just pElem) pElems - -combineParaElems :: [ParaElem] -> [ParaElem] -combineParaElems = combineParaElems' Nothing + pres <- documentToPresentation opts (Pandoc meta blks') + archv <- presentationToArchive opts pres + return $ fromArchive archv diff --git a/src/Text/Pandoc/Writers/Powerpoint/Output.hs b/src/Text/Pandoc/Writers/Powerpoint/Output.hs new file mode 100644 index 000000000..f3df62690 --- /dev/null +++ b/src/Text/Pandoc/Writers/Powerpoint/Output.hs @@ -0,0 +1,1431 @@ +{-# LANGUAGE PatternGuards #-} + +{- +Copyright (C) 2017-2018 Jesse Rosenthal <jrosenthal@jhu.edu> + +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.Writers.Powerpoint.Output + Copyright : Copyright (C) 2017-2018 Jesse Rosenthal + License : GNU GPL, version 2 or above + + Maintainer : Jesse Rosenthal <jrosenthal@jhu.edu> + Stability : alpha + Portability : portable + +Conversion of Presentation datatype (defined in +Text.Pandoc.Writers.Powerpoint.Presentation) to a zip archive. +-} + +module Text.Pandoc.Writers.Powerpoint.Output ( presentationToArchive + ) where + +import Control.Monad.Except (throwError, catchError) +import Control.Monad.Reader +import Control.Monad.State +import Codec.Archive.Zip +import Data.List (intercalate, stripPrefix, nub, union) +import Data.Default +import Data.Time.Clock (UTCTime) +import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds, posixSecondsToUTCTime) +import System.FilePath.Posix (splitDirectories, splitExtension, takeExtension) +import Text.XML.Light +import Text.Pandoc.Definition +import qualified Text.Pandoc.UTF8 as UTF8 +import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Error (PandocError(..)) +import qualified Text.Pandoc.Class as P +import Text.Pandoc.Options +import Text.Pandoc.MIME +import qualified Data.ByteString.Lazy as BL +import Text.Pandoc.Writers.OOXML +import qualified Data.Map as M +import Data.Maybe (mapMaybe, listToMaybe, catMaybes) +import Text.Pandoc.ImageSize +import Control.Applicative ((<|>)) +import System.FilePath.Glob +import Text.TeXMath +import Text.Pandoc.Writers.Math (convertMath) +import Text.Pandoc.Writers.Powerpoint.Presentation + +-- This populates the global ids map with images already in the +-- template, so the ids won't be used by images introduced by the +-- user. +initialGlobalIds :: Archive -> Archive -> M.Map FilePath Int +initialGlobalIds refArchive distArchive = + let archiveFiles = filesInArchive refArchive `union` filesInArchive distArchive + mediaPaths = filter (match (compile "ppt/media/image")) archiveFiles + + go :: FilePath -> Maybe (FilePath, Int) + go fp = do + s <- stripPrefix "ppt/media/image" $ fst $ splitExtension fp + (n, _) <- listToMaybe $ reads s + return (fp, n) + in + M.fromList $ mapMaybe go mediaPaths + +getPresentationSize :: Archive -> Archive -> Maybe (Integer, Integer) +getPresentationSize refArchive distArchive = do + entry <- findEntryByPath "ppt/presentation.xml" refArchive `mplus` + findEntryByPath "ppt/presentation.xml" distArchive + presElement <- parseXMLDoc $ UTF8.toStringLazy $ fromEntry entry + let ns = elemToNameSpaces presElement + sldSize <- findChild (elemName ns "p" "sldSz") presElement + cxS <- findAttr (QName "cx" Nothing Nothing) sldSize + cyS <- findAttr (QName "cy" Nothing Nothing) sldSize + (cx, _) <- listToMaybe $ reads cxS :: Maybe (Integer, String) + (cy, _) <- listToMaybe $ reads cyS :: Maybe (Integer, String) + return (cx `div` 12700, cy `div` 12700) + +data WriterEnv = WriterEnv { envRefArchive :: Archive + , envDistArchive :: Archive + , envUTCTime :: UTCTime + , envOpts :: WriterOptions + , envPresentationSize :: (Integer, Integer) + , envSlideHasHeader :: Bool + , envInList :: Bool + , envInNoteSlide :: Bool + , envCurSlideId :: Int + -- the difference between the number at + -- the end of the slide file name and + -- the rId number + , envSlideIdOffset :: Int + , envContentType :: ContentType + } + deriving (Show) + +instance Default WriterEnv where + def = WriterEnv { envRefArchive = emptyArchive + , envDistArchive = emptyArchive + , envUTCTime = posixSecondsToUTCTime 0 + , envOpts = def + , envPresentationSize = (720, 540) + , envSlideHasHeader = False + , envInList = False + , envInNoteSlide = False + , envCurSlideId = 1 + , envSlideIdOffset = 1 + , envContentType = NormalContent + } + +data ContentType = NormalContent + | TwoColumnLeftContent + | TwoColumnRightContent + deriving (Show, Eq) + +data MediaInfo = MediaInfo { mInfoFilePath :: FilePath + , mInfoLocalId :: Int + , mInfoGlobalId :: Int + , mInfoMimeType :: Maybe MimeType + , mInfoExt :: Maybe String + , mInfoCaption :: Bool + } deriving (Show, Eq) + +data WriterState = WriterState { stLinkIds :: M.Map Int (M.Map Int (URL, String)) + -- (FP, Local ID, Global ID, Maybe Mime) + , stMediaIds :: M.Map Int [MediaInfo] + , stMediaGlobalIds :: M.Map FilePath Int + , stNoteIds :: M.Map Int [Block] + -- associate anchors with slide id + , stAnchorMap :: M.Map String Int + -- media inherited from the template. + , stTemplateMedia :: [FilePath] + } deriving (Show, Eq) + +instance Default WriterState where + def = WriterState { stLinkIds = mempty + , stMediaIds = mempty + , stMediaGlobalIds = mempty + , stNoteIds = mempty + , stAnchorMap= mempty + , stTemplateMedia = [] + } + +type P m = ReaderT WriterEnv (StateT WriterState m) + +runP :: Monad m => WriterEnv -> WriterState -> P m a -> m a +runP env st p = evalStateT (runReaderT p env) st + +-------------------------------------------------------------------- + +copyFileToArchive :: PandocMonad m => Archive -> FilePath -> P m Archive +copyFileToArchive arch fp = do + refArchive <- asks envRefArchive + distArchive <- asks envDistArchive + case findEntryByPath fp refArchive `mplus` findEntryByPath fp distArchive of + Nothing -> fail $ fp ++ " missing in reference file" + Just e -> return $ addEntryToArchive e arch + +inheritedPatterns :: [Pattern] +inheritedPatterns = map compile [ "_rels/.rels" + , "docProps/app.xml" + , "docProps/core.xml" + , "ppt/slideLayouts/slideLayout*.xml" + , "ppt/slideLayouts/_rels/slideLayout*.xml.rels" + , "ppt/slideMasters/slideMaster1.xml" + , "ppt/slideMasters/_rels/slideMaster1.xml.rels" + , "ppt/theme/theme1.xml" + , "ppt/theme/_rels/theme1.xml.rels" + , "ppt/presProps.xml" + , "ppt/viewProps.xml" + , "ppt/tableStyles.xml" + , "ppt/media/image*" + ] + +patternToFilePaths :: PandocMonad m => Pattern -> P m [FilePath] +patternToFilePaths pat = do + refArchive <- asks envRefArchive + distArchive <- asks envDistArchive + + let archiveFiles = filesInArchive refArchive `union` filesInArchive distArchive + return $ filter (match pat) archiveFiles + +patternsToFilePaths :: PandocMonad m => [Pattern] -> P m [FilePath] +patternsToFilePaths pats = concat <$> mapM patternToFilePaths pats + +-- Here are the files we'll require to make a Powerpoint document. If +-- any of these are missing, we should error out of our build. +requiredFiles :: [FilePath] +requiredFiles = [ "_rels/.rels" + , "docProps/app.xml" + , "docProps/core.xml" + , "ppt/presProps.xml" + , "ppt/slideLayouts/slideLayout1.xml" + , "ppt/slideLayouts/_rels/slideLayout1.xml.rels" + , "ppt/slideLayouts/slideLayout2.xml" + , "ppt/slideLayouts/_rels/slideLayout2.xml.rels" + , "ppt/slideLayouts/slideLayout3.xml" + , "ppt/slideLayouts/_rels/slideLayout3.xml.rels" + , "ppt/slideLayouts/slideLayout4.xml" + , "ppt/slideLayouts/_rels/slideLayout4.xml.rels" + , "ppt/slideMasters/slideMaster1.xml" + , "ppt/slideMasters/_rels/slideMaster1.xml.rels" + , "ppt/theme/theme1.xml" + , "ppt/viewProps.xml" + , "ppt/tableStyles.xml" + ] + + +presentationToArchiveP :: PandocMonad m => Presentation -> P m Archive +presentationToArchiveP p@(Presentation slides) = do + filePaths <- patternsToFilePaths inheritedPatterns + + -- make sure all required files are available: + let missingFiles = filter (\fp -> not (fp `elem` filePaths)) requiredFiles + unless (null missingFiles) + (throwError $ + PandocSomeError $ + "The following required files are missing:\n" ++ + (unlines $ map (" " ++) missingFiles) + ) + + newArch' <- foldM copyFileToArchive emptyArchive filePaths + -- presentation entry and rels. We have to do the rels first to make + -- sure we know the correct offset for the rIds. + presEntry <- presentationToPresEntry p + presRelsEntry <- presentationToRelsEntry p + slideEntries <- mapM (\(s, n) -> slideToEntry s n) $ zip slides [1..] + slideRelEntries <- mapM (\(s,n) -> slideToSlideRelEntry s n) $ zip slides [1..] + -- These have to come after everything, because they need the info + -- built up in the state. + mediaEntries <- makeMediaEntries + contentTypesEntry <- presentationToContentTypes p >>= contentTypesToEntry + -- fold everything into our inherited archive and return it. + return $ foldr addEntryToArchive newArch' $ + slideEntries ++ + slideRelEntries ++ + mediaEntries ++ + [contentTypesEntry, presEntry, presRelsEntry] + +presentationToArchive :: PandocMonad m => WriterOptions -> Presentation -> m Archive +presentationToArchive opts pres = do + distArchive <- (toArchive . BL.fromStrict) <$> + P.readDefaultDataFile "reference.pptx" + refArchive <- case writerReferenceDoc opts of + Just f -> toArchive <$> P.readFileLazy f + Nothing -> (toArchive . BL.fromStrict) <$> + P.readDataFile "reference.pptx" + + utctime <- P.getCurrentTime + + presSize <- case getPresentationSize refArchive distArchive of + Just sz -> return sz + Nothing -> throwError $ + PandocSomeError $ + "Could not determine presentation size" + + let env = def { envRefArchive = refArchive + , envDistArchive = distArchive + , envUTCTime = utctime + , envOpts = opts + , envPresentationSize = presSize + } + + let st = def { stMediaGlobalIds = initialGlobalIds refArchive distArchive + } + + runP env st $ presentationToArchiveP pres + + + +-------------------------------------------------- + +-------------------------------------------------- + +getLayout :: PandocMonad m => Slide -> P m Element +getLayout slide = do + let layoutpath = case slide of + (MetadataSlide _ _ _ _) -> "ppt/slideLayouts/slideLayout1.xml" + (TitleSlide _) -> "ppt/slideLayouts/slideLayout3.xml" + (ContentSlide _ _) -> "ppt/slideLayouts/slideLayout2.xml" + (TwoColumnSlide _ _ _) -> "ppt/slideLayouts/slideLayout4.xml" + distArchive <- asks envDistArchive + root <- case findEntryByPath layoutpath distArchive of + Just e -> case parseXMLDoc $ UTF8.toStringLazy $ fromEntry e of + Just element -> return $ element + Nothing -> throwError $ + PandocSomeError $ + layoutpath ++ " corrupt in reference file" + Nothing -> throwError $ + PandocSomeError $ + layoutpath ++ " missing in reference file" + return root + +shapeHasName :: NameSpaces -> String -> Element -> Bool +shapeHasName ns name element + | Just nvSpPr <- findChild (elemName ns "p" "nvSpPr") element + , Just cNvPr <- findChild (elemName ns "p" "cNvPr") nvSpPr + , Just nm <- findAttr (QName "name" Nothing Nothing) cNvPr = + nm == name + | otherwise = False + +shapeHasId :: NameSpaces -> String -> Element -> Bool +shapeHasId ns ident element + | Just nvSpPr <- findChild (elemName ns "p" "nvSpPr") element + , Just cNvPr <- findChild (elemName ns "p" "cNvPr") nvSpPr + , Just nm <- findAttr (QName "id" Nothing Nothing) cNvPr = + nm == ident + | otherwise = False + +-- The content shape in slideLayout2 (Title/Content) has id=3 In +-- slideLayout4 (two column) the left column is id=3, and the right +-- column is id=4. +getContentShape :: PandocMonad m => NameSpaces -> Element -> P m Element +getContentShape ns spTreeElem + | isElem ns "p" "spTree" spTreeElem = do + contentType <- asks envContentType + let ident = case contentType of + NormalContent -> "3" + TwoColumnLeftContent -> "3" + TwoColumnRightContent -> "4" + case filterChild + (\e -> (isElem ns "p" "sp" e) && (shapeHasId ns ident e)) + spTreeElem + of + Just e -> return e + Nothing -> throwError $ + PandocSomeError $ + "Could not find shape for Powerpoint content" +getContentShape _ _ = throwError $ + PandocSomeError $ + "Attempted to find content on non shapeTree" + +getShapeDimensions :: NameSpaces + -> Element + -> Maybe ((Integer, Integer), (Integer, Integer)) +getShapeDimensions ns element + | isElem ns "p" "sp" element = do + spPr <- findChild (elemName ns "p" "spPr") element + xfrm <- findChild (elemName ns "a" "xfrm") spPr + off <- findChild (elemName ns "a" "off") xfrm + xS <- findAttr (QName "x" Nothing Nothing) off + yS <- findAttr (QName "y" Nothing Nothing) off + ext <- findChild (elemName ns "a" "ext") xfrm + cxS <- findAttr (QName "cx" Nothing Nothing) ext + cyS <- findAttr (QName "cy" Nothing Nothing) ext + (x, _) <- listToMaybe $ reads xS + (y, _) <- listToMaybe $ reads yS + (cx, _) <- listToMaybe $ reads cxS + (cy, _) <- listToMaybe $ reads cyS + return $ ((x `div` 12700, y `div` 12700), (cx `div` 12700, cy `div` 12700)) + | otherwise = Nothing + + +getMasterShapeDimensionsById :: String + -> Element + -> Maybe ((Integer, Integer), (Integer, Integer)) +getMasterShapeDimensionsById ident master = do + let ns = elemToNameSpaces master + cSld <- findChild (elemName ns "p" "cSld") master + spTree <- findChild (elemName ns "p" "spTree") cSld + sp <- filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasId ns ident e)) spTree + getShapeDimensions ns sp + +getContentShapeSize :: PandocMonad m + => NameSpaces + -> Element + -> Element + -> P m ((Integer, Integer), (Integer, Integer)) +getContentShapeSize ns layout master + | isElem ns "p" "sldLayout" layout + , Just cSld <- findChild (elemName ns "p" "cSld") layout + , Just spTree <- findChild (elemName ns "p" "spTree") cSld = do + sp <- getContentShape ns spTree + case getShapeDimensions ns sp of + Just sz -> return sz + Nothing -> do let mbSz = + findChild (elemName ns "p" "nvSpPr") sp >>= + findChild (elemName ns "p" "cNvPr") >>= + findAttr (QName "id" Nothing Nothing) >>= + flip getMasterShapeDimensionsById master + case mbSz of + Just sz' -> return sz' + Nothing -> throwError $ + PandocSomeError $ + "Couldn't find necessary content shape size" +getContentShapeSize _ _ _ = throwError $ + PandocSomeError $ + "Attempted to find content shape size in non-layout" + +replaceNamedChildren :: NameSpaces + -> String + -> String + -> [Element] + -> Element + -> Element +replaceNamedChildren ns prefix name newKids element = + element { elContent = concat $ fun True $ elContent element } + where + fun :: Bool -> [Content] -> [[Content]] + fun _ [] = [] + fun switch ((Elem e) : conts) | isElem ns prefix name e = + if switch + then (map Elem $ newKids) : fun False conts + else fun False conts + fun switch (cont : conts) = [cont] : fun switch conts + +---------------------------------------------------------------- + +registerLink :: PandocMonad m => (URL, String) -> P m Int +registerLink link = do + curSlideId <- asks envCurSlideId + linkReg <- gets stLinkIds + mediaReg <- gets stMediaIds + let maxLinkId = case M.lookup curSlideId linkReg of + Just mp -> case M.keys mp of + [] -> 1 + ks -> maximum ks + Nothing -> 1 + maxMediaId = case M.lookup curSlideId mediaReg of + Just [] -> 1 + Just mInfos -> maximum $ map mInfoLocalId mInfos + Nothing -> 1 + maxId = max maxLinkId maxMediaId + slideLinks = case M.lookup curSlideId linkReg of + Just mp -> M.insert (maxId + 1) link mp + Nothing -> M.singleton (maxId + 1) link + modify $ \st -> st{ stLinkIds = M.insert curSlideId slideLinks linkReg} + return $ maxId + 1 + +registerMedia :: PandocMonad m => FilePath -> [ParaElem] -> P m MediaInfo +registerMedia fp caption = do + curSlideId <- asks envCurSlideId + linkReg <- gets stLinkIds + mediaReg <- gets stMediaIds + globalIds <- gets stMediaGlobalIds + let maxLinkId = case M.lookup curSlideId linkReg of + Just mp -> case M.keys mp of + [] -> 1 + ks -> maximum ks + Nothing -> 1 + maxMediaId = case M.lookup curSlideId mediaReg of + Just [] -> 1 + Just mInfos -> maximum $ map mInfoLocalId mInfos + Nothing -> 1 + maxLocalId = max maxLinkId maxMediaId + + maxGlobalId = case M.elems globalIds of + [] -> 0 + ids -> maximum ids + + (imgBytes, mbMt) <- P.fetchItem fp + let imgExt = (mbMt >>= extensionFromMimeType >>= (\x -> return $ '.':x)) + <|> + case imageType imgBytes of + Just Png -> Just ".png" + Just Jpeg -> Just ".jpeg" + Just Gif -> Just ".gif" + Just Pdf -> Just ".pdf" + Just Eps -> Just ".eps" + Just Svg -> Just ".svg" + Nothing -> Nothing + + let newGlobalId = case M.lookup fp globalIds of + Just ident -> ident + Nothing -> maxGlobalId + 1 + + let newGlobalIds = M.insert fp newGlobalId globalIds + + let mediaInfo = MediaInfo { mInfoFilePath = fp + , mInfoLocalId = maxLocalId + 1 + , mInfoGlobalId = newGlobalId + , mInfoMimeType = mbMt + , mInfoExt = imgExt + , mInfoCaption = (not . null) caption + } + + let slideMediaInfos = case M.lookup curSlideId mediaReg of + Just minfos -> mediaInfo : minfos + Nothing -> [mediaInfo] + + + modify $ \st -> st{ stMediaIds = M.insert curSlideId slideMediaInfos mediaReg + , stMediaGlobalIds = newGlobalIds + } + return mediaInfo + +makeMediaEntry :: PandocMonad m => MediaInfo -> P m Entry +makeMediaEntry mInfo = do + epochtime <- (floor . utcTimeToPOSIXSeconds) <$> asks envUTCTime + (imgBytes, _) <- P.fetchItem (mInfoFilePath mInfo) + let ext = case mInfoExt mInfo of + Just e -> e + Nothing -> "" + let fp = "ppt/media/image" ++ (show $ mInfoGlobalId mInfo) ++ ext + return $ toEntry fp epochtime $ BL.fromStrict imgBytes + +makeMediaEntries :: PandocMonad m => P m [Entry] +makeMediaEntries = do + mediaInfos <- gets stMediaIds + let allInfos = mconcat $ M.elems mediaInfos + mapM makeMediaEntry allInfos + +-- -- | Scales the image to fit the page +-- -- sizes are passed in emu +-- fitToPage' :: (Double, Double) -- image size in emu +-- -> Integer -- pageWidth +-- -> Integer -- pageHeight +-- -> (Integer, Integer) -- imagesize +-- fitToPage' (x, y) pageWidth pageHeight +-- -- Fixes width to the page width and scales the height +-- | x <= fromIntegral pageWidth && y <= fromIntegral pageHeight = +-- (floor x, floor y) +-- | x / fromIntegral pageWidth > y / fromIntegral pageWidth = +-- (pageWidth, floor $ ((fromIntegral pageWidth) / x) * y) +-- | otherwise = +-- (floor $ ((fromIntegral pageHeight) / y) * x, pageHeight) + +-- positionImage :: (Double, Double) -> Integer -> Integer -> (Integer, Integer) +-- positionImage (x, y) pageWidth pageHeight = +-- let (x', y') = fitToPage' (x, y) pageWidth pageHeight +-- in +-- ((pageWidth - x') `div` 2, (pageHeight - y') `div` 2) + +getMaster :: PandocMonad m => P m Element +getMaster = do + refArchive <- asks envRefArchive + distArchive <- asks envDistArchive + parseXml refArchive distArchive "ppt/slideMasters/slideMaster1.xml" + +-- We want to get the header dimensions, so we can make sure that the +-- image goes underneath it. We only use this in a content slide if it +-- has a header. + +-- getHeaderSize :: PandocMonad m => P m ((Integer, Integer), (Integer, Integer)) +-- getHeaderSize = do +-- master <- getMaster +-- let ns = elemToNameSpaces master +-- sps = [master] >>= +-- findChildren (elemName ns "p" "cSld") >>= +-- findChildren (elemName ns "p" "spTree") >>= +-- findChildren (elemName ns "p" "sp") +-- mbXfrm = +-- listToMaybe (filter (shapeHasName ns "Title Placeholder 1") sps) >>= +-- findChild (elemName ns "p" "spPr") >>= +-- findChild (elemName ns "a" "xfrm") +-- xoff = mbXfrm >>= +-- findChild (elemName ns "a" "off") >>= +-- findAttr (QName "x" Nothing Nothing) >>= +-- (listToMaybe . (\s -> reads s :: [(Integer, String)])) +-- yoff = mbXfrm >>= +-- findChild (elemName ns "a" "off") >>= +-- findAttr (QName "y" Nothing Nothing) >>= +-- (listToMaybe . (\s -> reads s :: [(Integer, String)])) +-- xext = mbXfrm >>= +-- findChild (elemName ns "a" "ext") >>= +-- findAttr (QName "cx" Nothing Nothing) >>= +-- (listToMaybe . (\s -> reads s :: [(Integer, String)])) +-- yext = mbXfrm >>= +-- findChild (elemName ns "a" "ext") >>= +-- findAttr (QName "cy" Nothing Nothing) >>= +-- (listToMaybe . (\s -> reads s :: [(Integer, String)])) +-- off = case xoff of +-- Just (xoff', _) | Just (yoff',_) <- yoff -> (xoff', yoff') +-- _ -> (1043490, 1027664) +-- ext = case xext of +-- Just (xext', _) | Just (yext',_) <- yext -> (xext', yext') +-- _ -> (7024744, 1143000) +-- return $ (off, ext) + +-- Hard-coded for now +-- captionPosition :: ((Integer, Integer), (Integer, Integer)) +-- captionPosition = ((457200, 6061972), (8229600, 527087)) + +captionHeight :: Integer +captionHeight = 40 + +createCaption :: PandocMonad m + => ((Integer, Integer), (Integer, Integer)) + -> [ParaElem] + -> P m Element +createCaption contentShapeDimensions paraElements = do + let para = Paragraph def{pPropAlign = Just AlgnCenter} paraElements + elements <- mapM paragraphToElement [para] + let ((x, y), (cx, cy)) = contentShapeDimensions + let txBody = mknode "p:txBody" [] $ + [mknode "a:bodyPr" [] (), mknode "a:lstStyle" [] ()] ++ elements + return $ + mknode "p:sp" [] [ mknode "p:nvSpPr" [] + [ mknode "p:cNvPr" [("id","1"), ("name","TextBox 3")] () + , mknode "p:cNvSpPr" [("txBox", "1")] () + , mknode "p:nvPr" [] () + ] + , mknode "p:spPr" [] + [ mknode "a:xfrm" [] + [ mknode "a:off" [("x", show $ 12700 * x), + ("y", show $ 12700 * (y + cy - captionHeight))] () + , mknode "a:ext" [("cx", show $ 12700 * cx), + ("cy", show $ 12700 * captionHeight)] () + ] + , mknode "a:prstGeom" [("prst", "rect")] + [ mknode "a:avLst" [] () + ] + , mknode "a:noFill" [] () + ] + , txBody + ] + +makePicElements :: PandocMonad m + => Element + -> PicProps + -> MediaInfo + -> Text.Pandoc.Definition.Attr + -> [ParaElem] + -> P m [Element] +makePicElements layout picProps mInfo _ alt = do + opts <- asks envOpts + (pageWidth, pageHeight) <- asks envPresentationSize + -- hasHeader <- asks envSlideHasHeader + let hasCaption = mInfoCaption mInfo + (imgBytes, _) <- P.fetchItem (mInfoFilePath mInfo) + let (pxX, pxY) = case imageSize opts imgBytes of + Right sz -> sizeInPixels $ sz + Left _ -> sizeInPixels $ def + master <- getMaster + let ns = elemToNameSpaces layout + ((x, y), (cx, cytmp)) <- getContentShapeSize ns layout master + `catchError` + (\_ -> return ((0, 0), (pageWidth, pageHeight))) + + let cy = if hasCaption then cytmp - captionHeight else cytmp + + let imgRatio = fromIntegral pxX / fromIntegral pxY :: Double + boxRatio = fromIntegral cx / fromIntegral cy :: Double + (dimX, dimY) = if imgRatio > boxRatio + then (fromIntegral cx, fromIntegral cx / imgRatio) + else (fromIntegral cy * imgRatio, fromIntegral cy) + + (dimX', dimY') = (round dimX * 12700, round dimY * 12700) :: (Integer, Integer) + (xoff, yoff) = (fromIntegral x + (fromIntegral cx - dimX) / 2, + fromIntegral y + (fromIntegral cy - dimY) / 2) + (xoff', yoff') = (round xoff * 12700, round yoff * 12700) :: (Integer, Integer) + + let cNvPicPr = mknode "p:cNvPicPr" [] $ + mknode "a:picLocks" [("noGrp","1") + ,("noChangeAspect","1")] () + -- cNvPr will contain the link information so we do that separately, + -- and register the link if necessary. + let cNvPrAttr = [("descr", mInfoFilePath mInfo), ("id","0"),("name","Picture 1")] + cNvPr <- case picPropLink picProps of + Just link -> do idNum <- registerLink link + return $ mknode "p:cNvPr" cNvPrAttr $ + mknode "a:hlinkClick" [("r:id", "rId" ++ show idNum)] () + Nothing -> return $ mknode "p:cNvPr" cNvPrAttr () + let nvPicPr = mknode "p:nvPicPr" [] + [ cNvPr + , cNvPicPr + , mknode "p:nvPr" [] ()] + let blipFill = mknode "p:blipFill" [] + [ mknode "a:blip" [("r:embed", "rId" ++ (show $ mInfoLocalId mInfo))] () + , mknode "a:stretch" [] $ + mknode "a:fillRect" [] () ] + let xfrm = mknode "a:xfrm" [] + [ mknode "a:off" [("x",show xoff'), ("y",show yoff')] () + , mknode "a:ext" [("cx",show dimX') + ,("cy",show dimY')] () ] + let prstGeom = mknode "a:prstGeom" [("prst","rect")] $ + mknode "a:avLst" [] () + let ln = mknode "a:ln" [("w","9525")] + [ mknode "a:noFill" [] () + , mknode "a:headEnd" [] () + , mknode "a:tailEnd" [] () ] + let spPr = mknode "p:spPr" [("bwMode","auto")] + [xfrm, prstGeom, mknode "a:noFill" [] (), ln] + + let picShape = mknode "p:pic" [] + [ nvPicPr + , blipFill + , spPr ] + + -- And now, maybe create the caption: + if hasCaption + then do cap <- createCaption ((x, y), (cx, cytmp)) alt + return [picShape, cap] + else return [picShape] + + +paraElemToElement :: PandocMonad m => ParaElem -> P m Element +paraElemToElement Break = return $ mknode "a:br" [] () +paraElemToElement (Run rpr s) = do + let sizeAttrs = case rPropForceSize rpr of + Just n -> [("sz", (show $ n * 100))] + Nothing -> [] + attrs = sizeAttrs ++ + if rPropCode rpr + then [] + else (if rPropBold rpr then [("b", "1")] else []) ++ + (if rPropItalics rpr then [("i", "1")] else []) ++ + (case rStrikethrough rpr of + Just NoStrike -> [("strike", "noStrike")] + Just SingleStrike -> [("strike", "sngStrike")] + Just DoubleStrike -> [("strike", "dblStrike")] + Nothing -> []) ++ + (case rBaseline rpr of + Just n -> [("baseline", show n)] + Nothing -> []) ++ + (case rCap rpr of + Just NoCapitals -> [("cap", "none")] + Just SmallCapitals -> [("cap", "small")] + Just AllCapitals -> [("cap", "all")] + Nothing -> []) ++ + [] + linkProps <- case rLink rpr of + Just link -> do + idNum <- registerLink link + -- first we have to make sure that if it's an + -- anchor, it's in the anchor map. If not, there's + -- no link. + anchorMap <- gets stAnchorMap + return $ case link of + -- anchor with nothing in the map + ('#':target, _) | Nothing <- M.lookup target anchorMap -> + [] + -- anchor that is in the map + ('#':_, _) -> + let linkAttrs = + [ ("r:id", "rId" ++ show idNum) + , ("action", "ppaction://hlinksldjump") + ] + in [mknode "a:hlinkClick" linkAttrs ()] + -- external + _ -> + let linkAttrs = + [ ("r:id", "rId" ++ show idNum) + ] + in [mknode "a:hlinkClick" linkAttrs ()] + Nothing -> return [] + let propContents = if rPropCode rpr + then [mknode "a:latin" [("typeface", "Courier")] ()] + else linkProps + return $ mknode "a:r" [] [ mknode "a:rPr" attrs propContents + , mknode "a:t" [] s + ] +paraElemToElement (MathElem mathType texStr) = do + res <- convertMath writeOMML mathType (unTeXString texStr) + case res of + Right r -> return $ mknode "a14:m" [] $ addMathInfo r + Left (Str s) -> paraElemToElement (Run def s) + Left _ -> throwError $ PandocShouldNeverHappenError "non-string math fallback" + +-- This is a bit of a kludge -- really requires adding an option to +-- TeXMath, but since that's a different package, we'll do this one +-- step at a time. +addMathInfo :: Element -> Element +addMathInfo element = + let mathspace = Attr { attrKey = (QName "m" Nothing (Just "xmlns")) + , attrVal = "http://schemas.openxmlformats.org/officeDocument/2006/math" + } + in add_attr mathspace element + +-- We look through the element to see if it contains an a14:m +-- element. If so, we surround it. This is a bit ugly, but it seems +-- more dependable than looking through shapes for math. Plus this is +-- an xml implementation detail, so it seems to make sense to do it at +-- the xml level. +surroundWithMathAlternate :: Element -> Element +surroundWithMathAlternate element = + case findElement (QName "m" Nothing (Just "a14")) element of + Just _ -> + mknode "mc:AlternateContent" + [("xmlns:mc", "http://schemas.openxmlformats.org/markup-compatibility/2006") + ] [ mknode "mc:Choice" + [ ("xmlns:a14", "http://schemas.microsoft.com/office/drawing/2010/main") + , ("Requires", "a14")] [ element ] + ] + Nothing -> element + +paragraphToElement :: PandocMonad m => Paragraph -> P m Element +paragraphToElement par = do + let + attrs = [("lvl", show $ pPropLevel $ paraProps par)] ++ + (case pPropMarginLeft (paraProps par) of + Just px -> [("marL", show $ 12700 * px), ("indent", "0")] + Nothing -> [] + ) ++ + (case pPropAlign (paraProps par) of + Just AlgnLeft -> [("algn", "l")] + Just AlgnRight -> [("algn", "r")] + Just AlgnCenter -> [("algn", "ctr")] + Nothing -> [] + ) + props = [] ++ + (case pPropSpaceBefore $ paraProps par of + Just px -> [mknode "a:spcBef" [] [ + mknode "a:spcPts" [("val", show $ 100 * px)] () + ] + ] + Nothing -> [] + ) ++ + (case pPropBullet $ paraProps par of + Just Bullet -> [] + Just (AutoNumbering attrs') -> + [mknode "a:buAutoNum" [("type", autoNumberingToType attrs')] ()] + Nothing -> [mknode "a:buNone" [] ()] + ) + paras <- mapM paraElemToElement (paraElems par) + return $ mknode "a:p" [] $ [mknode "a:pPr" attrs props] ++ paras + +shapeToElement :: PandocMonad m => Element -> Shape -> P m Element +shapeToElement layout (TextBox paras) + | ns <- elemToNameSpaces layout + , Just cSld <- findChild (elemName ns "p" "cSld") layout + , Just spTree <- findChild (elemName ns "p" "spTree") cSld = do + sp <- getContentShape ns spTree + elements <- mapM paragraphToElement paras + let txBody = mknode "p:txBody" [] $ + [mknode "a:bodyPr" [] (), mknode "a:lstStyle" [] ()] ++ elements + emptySpPr = mknode "p:spPr" [] () + return $ + surroundWithMathAlternate $ + replaceNamedChildren ns "p" "txBody" [txBody] $ + replaceNamedChildren ns "p" "spPr" [emptySpPr] $ + sp +-- GraphicFrame and Pic should never reach this. +shapeToElement _ _ = return $ mknode "p:sp" [] () + +shapeToElements :: PandocMonad m => Element -> Shape -> P m [Element] +shapeToElements layout (Pic picProps fp attr alt) = do + mInfo <- registerMedia fp alt + case mInfoExt mInfo of + Just _ -> do + makePicElements layout picProps mInfo attr alt + Nothing -> shapeToElements layout $ TextBox [Paragraph def alt] +shapeToElements layout (GraphicFrame tbls cptn) = + graphicFrameToElements layout tbls cptn +shapeToElements layout shp = do + element <- shapeToElement layout shp + return [element] + +shapesToElements :: PandocMonad m => Element -> [Shape] -> P m [Element] +shapesToElements layout shps = do + concat <$> mapM (shapeToElements layout) shps + +hardcodedTableMargin :: Integer +hardcodedTableMargin = 36 + +graphicFrameToElements :: PandocMonad m => Element -> [Graphic] -> [ParaElem] -> P m [Element] +graphicFrameToElements layout tbls caption = do + -- get the sizing + master <- getMaster + (pageWidth, pageHeight) <- asks envPresentationSize + let ns = elemToNameSpaces layout + ((x, y), (cx, cytmp)) <- getContentShapeSize ns layout master + `catchError` + (\_ -> return ((0, 0), (pageWidth, pageHeight))) + + let cy = if (not $ null caption) then cytmp - captionHeight else cytmp + + elements <- mapM graphicToElement tbls + let graphicFrameElts = + mknode "p:graphicFrame" [] $ + [ mknode "p:nvGraphicFramePr" [] $ + [ mknode "p:cNvPr" [("id", "6"), ("name", "Content Placeholder 5")] () + , mknode "p:cNvGraphicFramePr" [] $ + [mknode "a:graphicFrameLocks" [("noGrp", "1")] ()] + , mknode "p:nvPr" [] $ + [mknode "p:ph" [("idx", "1")] ()] + ] + , mknode "p:xfrm" [] $ + [ mknode "a:off" [("x", show $ 12700 * x), ("y", show $ 12700 * y)] () + , mknode "a:ext" [("cx", show $ 12700 * cx), ("cy", show $ 12700 * cy)] () + ] + ] ++ elements + + if (not $ null caption) + then do capElt <- createCaption ((x, y), (cx, cytmp)) caption + return [graphicFrameElts, capElt] + else return [graphicFrameElts] + +graphicToElement :: PandocMonad m => Graphic -> P m Element +graphicToElement (Tbl tblPr hdrCells rows) = do + (pageWidth, _) <- asks envPresentationSize + let colWidths = if null hdrCells + then case rows of + r : _ | not (null r) -> replicate (length r) $ + (pageWidth - (2 * hardcodedTableMargin))`div` (toInteger $ length r) + -- satisfy the compiler. This is the same as + -- saying that rows is empty, but the compiler + -- won't understand that `[]` exhausts the + -- alternatives. + _ -> [] + else replicate (length hdrCells) $ + (pageWidth - (2 * hardcodedTableMargin)) `div` (toInteger $ length hdrCells) + + let cellToOpenXML paras = + do elements <- mapM paragraphToElement paras + let elements' = if null elements + then [mknode "a:p" [] [mknode "a:endParaRPr" [] ()]] + else elements + return $ + [mknode "a:txBody" [] $ + ([ mknode "a:bodyPr" [] () + , mknode "a:lstStyle" [] ()] + ++ elements')] + headers' <- mapM cellToOpenXML hdrCells + rows' <- mapM (mapM cellToOpenXML) rows + let borderProps = mknode "a:tcPr" [] () + let emptyCell = [mknode "a:p" [] [mknode "a:pPr" [] ()]] + let mkcell border contents = mknode "a:tc" [] + $ (if null contents + then emptyCell + else contents) ++ [ borderProps | border ] + let mkrow border cells = mknode "a:tr" [("h", "0")] $ map (mkcell border) cells + + let mkgridcol w = mknode "a:gridCol" + [("w", show ((12700 * w) :: Integer))] () + let hasHeader = not (all null hdrCells) + return $ mknode "a:graphic" [] $ + [mknode "a:graphicData" [("uri", "http://schemas.openxmlformats.org/drawingml/2006/table")] $ + [mknode "a:tbl" [] $ + [ mknode "a:tblPr" [ ("firstRow", if tblPrFirstRow tblPr then "1" else "0") + , ("bandRow", if tblPrBandRow tblPr then "1" else "0") + ] () + , mknode "a:tblGrid" [] (if all (==0) colWidths + then [] + else map mkgridcol colWidths) + ] + ++ [ mkrow True headers' | hasHeader ] ++ map (mkrow False) rows' + ] + ] + +getShapeByName :: NameSpaces -> Element -> String -> Maybe Element +getShapeByName ns spTreeElem name + | isElem ns "p" "spTree" spTreeElem = + filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasName ns name e)) spTreeElem + | otherwise = Nothing + +-- getShapeById :: NameSpaces -> Element -> String -> Maybe Element +-- getShapeById ns spTreeElem ident +-- | isElem ns "p" "spTree" spTreeElem = +-- filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasId ns ident e)) spTreeElem +-- | otherwise = Nothing + +nonBodyTextToElement :: PandocMonad m => Element -> String -> [ParaElem] -> P m Element +nonBodyTextToElement layout shapeName paraElements + | ns <- elemToNameSpaces layout + , Just cSld <- findChild (elemName ns "p" "cSld") layout + , Just spTree <- findChild (elemName ns "p" "spTree") cSld + , Just sp <- getShapeByName ns spTree shapeName = do + let hdrPara = Paragraph def paraElements + element <- paragraphToElement hdrPara + let txBody = mknode "p:txBody" [] $ + [mknode "a:bodyPr" [] (), mknode "a:lstStyle" [] ()] ++ + [element] + return $ replaceNamedChildren ns "p" "txBody" [txBody] sp + -- XXX: TODO + | otherwise = return $ mknode "p:sp" [] () + +contentToElement :: PandocMonad m => Element -> [ParaElem] -> [Shape] -> P m Element +contentToElement layout hdrShape shapes + | ns <- elemToNameSpaces layout + , Just cSld <- findChild (elemName ns "p" "cSld") layout + , Just spTree <- findChild (elemName ns "p" "spTree") cSld = do + element <- nonBodyTextToElement layout "Title 1" hdrShape + let hdrShapeElements = if null hdrShape + then [] + else [element] + contentElements <- local + (\env -> env {envContentType = NormalContent}) + (shapesToElements layout shapes) + return $ + replaceNamedChildren ns "p" "sp" + (hdrShapeElements ++ contentElements) + spTree +contentToElement _ _ _ = return $ mknode "p:sp" [] () + +twoColumnToElement :: PandocMonad m => Element -> [ParaElem] -> [Shape] -> [Shape] -> P m Element +twoColumnToElement layout hdrShape shapesL shapesR + | ns <- elemToNameSpaces layout + , Just cSld <- findChild (elemName ns "p" "cSld") layout + , Just spTree <- findChild (elemName ns "p" "spTree") cSld = do + element <- nonBodyTextToElement layout "Title 1" hdrShape + let hdrShapeElements = if null hdrShape + then [] + else [element] + contentElementsL <- local + (\env -> env {envContentType =TwoColumnLeftContent}) + (shapesToElements layout shapesL) + contentElementsR <- local + (\env -> env {envContentType =TwoColumnRightContent}) + (shapesToElements layout shapesR) + -- let contentElementsL' = map (setIdx ns "1") contentElementsL + -- contentElementsR' = map (setIdx ns "2") contentElementsR + return $ + replaceNamedChildren ns "p" "sp" + (hdrShapeElements ++ contentElementsL ++ contentElementsR) + spTree +twoColumnToElement _ _ _ _= return $ mknode "p:sp" [] () + + +titleToElement :: PandocMonad m => Element -> [ParaElem] -> P m Element +titleToElement layout titleElems + | ns <- elemToNameSpaces layout + , Just cSld <- findChild (elemName ns "p" "cSld") layout + , Just spTree <- findChild (elemName ns "p" "spTree") cSld = do + element <- nonBodyTextToElement layout "Title 1" titleElems + let titleShapeElements = if null titleElems + then [] + else [element] + return $ replaceNamedChildren ns "p" "sp" titleShapeElements spTree +titleToElement _ _ = return $ mknode "p:sp" [] () + +metadataToElement :: PandocMonad m => Element -> [ParaElem] -> [ParaElem] -> [[ParaElem]] -> [ParaElem] -> P m Element +metadataToElement layout titleElems subtitleElems authorsElems dateElems + | ns <- elemToNameSpaces layout + , Just cSld <- findChild (elemName ns "p" "cSld") layout + , Just spTree <- findChild (elemName ns "p" "spTree") cSld = do + titleShapeElements <- if null titleElems + then return [] + else sequence [nonBodyTextToElement layout "Title 1" titleElems] + let combinedAuthorElems = intercalate [Break] authorsElems + subtitleAndAuthorElems = intercalate [Break, Break] [subtitleElems, combinedAuthorElems] + subtitleShapeElements <- if null subtitleAndAuthorElems + then return [] + else sequence [nonBodyTextToElement layout "Subtitle 2" subtitleAndAuthorElems] + dateShapeElements <- if null dateElems + then return [] + else sequence [nonBodyTextToElement layout "Date Placeholder 3" dateElems] + return $ replaceNamedChildren ns "p" "sp" + (titleShapeElements ++ subtitleShapeElements ++ dateShapeElements) + spTree +metadataToElement _ _ _ _ _ = return $ mknode "p:sp" [] () + +slideToElement :: PandocMonad m => Slide -> P m Element +slideToElement s@(ContentSlide hdrElems shapes) = do + layout <- getLayout s + spTree <- local (\env -> if null hdrElems + then env + else env{envSlideHasHeader=True}) $ + contentToElement layout hdrElems shapes + return $ mknode "p:sld" + [ ("xmlns:a", "http://schemas.openxmlformats.org/drawingml/2006/main"), + ("xmlns:r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"), + ("xmlns:p", "http://schemas.openxmlformats.org/presentationml/2006/main") + ] [mknode "p:cSld" [] [spTree]] +slideToElement s@(TwoColumnSlide hdrElems shapesL shapesR) = do + layout <- getLayout s + spTree <- local (\env -> if null hdrElems + then env + else env{envSlideHasHeader=True}) $ + twoColumnToElement layout hdrElems shapesL shapesR + return $ mknode "p:sld" + [ ("xmlns:a", "http://schemas.openxmlformats.org/drawingml/2006/main"), + ("xmlns:r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"), + ("xmlns:p", "http://schemas.openxmlformats.org/presentationml/2006/main") + ] [mknode "p:cSld" [] [spTree]] +slideToElement s@(TitleSlide hdrElems) = do + layout <- getLayout s + spTree <- titleToElement layout hdrElems + return $ mknode "p:sld" + [ ("xmlns:a", "http://schemas.openxmlformats.org/drawingml/2006/main"), + ("xmlns:r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"), + ("xmlns:p", "http://schemas.openxmlformats.org/presentationml/2006/main") + ] [mknode "p:cSld" [] [spTree]] +slideToElement s@(MetadataSlide titleElems subtitleElems authorElems dateElems) = do + layout <- getLayout s + spTree <- metadataToElement layout titleElems subtitleElems authorElems dateElems + return $ mknode "p:sld" + [ ("xmlns:a", "http://schemas.openxmlformats.org/drawingml/2006/main"), + ("xmlns:r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"), + ("xmlns:p", "http://schemas.openxmlformats.org/presentationml/2006/main") + ] [mknode "p:cSld" [] [spTree]] + +----------------------------------------------------------------------- + +slideToFilePath :: Slide -> Int -> FilePath +slideToFilePath _ idNum = "slide" ++ (show $ idNum) ++ ".xml" + +slideToSlideId :: Monad m => Slide -> Int -> P m String +slideToSlideId _ idNum = do + n <- asks envSlideIdOffset + return $ "rId" ++ (show $ idNum + n) + + +data Relationship = Relationship { relId :: Int + , relType :: MimeType + , relTarget :: FilePath + } deriving (Show, Eq) + +elementToRel :: Element -> Maybe Relationship +elementToRel element + | elName element == QName "Relationship" (Just "http://schemas.openxmlformats.org/package/2006/relationships") Nothing = + do rId <- findAttr (QName "Id" Nothing Nothing) element + numStr <- stripPrefix "rId" rId + num <- case reads numStr :: [(Int, String)] of + (n, _) : _ -> Just n + [] -> Nothing + type' <- findAttr (QName "Type" Nothing Nothing) element + target <- findAttr (QName "Target" Nothing Nothing) element + return $ Relationship num type' target + | otherwise = Nothing + +slideToPresRel :: Monad m => Slide -> Int -> P m Relationship +slideToPresRel slide idNum = do + n <- asks envSlideIdOffset + let rId = idNum + n + fp = "slides/" ++ slideToFilePath slide idNum + return $ Relationship { relId = rId + , relType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" + , relTarget = fp + } + +getRels :: PandocMonad m => P m [Relationship] +getRels = do + refArchive <- asks envRefArchive + distArchive <- asks envDistArchive + relsElem <- parseXml refArchive distArchive "ppt/_rels/presentation.xml.rels" + let globalNS = "http://schemas.openxmlformats.org/package/2006/relationships" + let relElems = findChildren (QName "Relationship" (Just globalNS) Nothing) relsElem + return $ mapMaybe elementToRel relElems + +presentationToRels :: PandocMonad m => Presentation -> P m [Relationship] +presentationToRels (Presentation slides) = do + mySlideRels <- mapM (\(s, n) -> slideToPresRel s n) $ zip slides [1..] + rels <- getRels + let relsWithoutSlides = filter (\r -> relType r /= "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide") rels + -- We want to make room for the slides in the id space. The slides + -- will start at Id2 (since Id1 is for the slide master). There are + -- two slides in the data file, but that might change in the future, + -- so we will do this: + -- + -- 1. We look to see what the minimum relWithoutSlide id (greater than 1) is. + -- 2. We add the difference between this and the number of slides to + -- all relWithoutSlide rels (unless they're 1) + + let minRelNotOne = case filter (1<) $ map relId relsWithoutSlides of + [] -> 0 -- doesn't matter in this case, since + -- there will be nothing to map the + -- function over + l -> minimum l + + modifyRelNum :: Int -> Int + modifyRelNum 1 = 1 + modifyRelNum n = n - minRelNotOne + 2 + length slides + + relsWithoutSlides' = map (\r -> r{relId = modifyRelNum $ relId r}) relsWithoutSlides + + return $ mySlideRels ++ relsWithoutSlides' + +relToElement :: Relationship -> Element +relToElement rel = mknode "Relationship" [ ("Id", "rId" ++ (show $ relId rel)) + , ("Type", relType rel) + , ("Target", relTarget rel) ] () + +relsToElement :: [Relationship] -> Element +relsToElement rels = mknode "Relationships" + [("xmlns", "http://schemas.openxmlformats.org/package/2006/relationships")] + (map relToElement rels) + +presentationToRelsEntry :: PandocMonad m => Presentation -> P m Entry +presentationToRelsEntry pres = do + rels <- presentationToRels pres + elemToEntry "ppt/_rels/presentation.xml.rels" $ relsToElement rels + +elemToEntry :: PandocMonad m => FilePath -> Element -> P m Entry +elemToEntry fp element = do + epochtime <- (floor . utcTimeToPOSIXSeconds) <$> asks envUTCTime + return $ toEntry fp epochtime $ renderXml element + +slideToEntry :: PandocMonad m => Slide -> Int -> P m Entry +slideToEntry slide idNum = do + local (\env -> env{envCurSlideId = idNum}) $ do + element <- slideToElement slide + elemToEntry ("ppt/slides/" ++ slideToFilePath slide idNum) element + +slideToSlideRelEntry :: PandocMonad m => Slide -> Int -> P m Entry +slideToSlideRelEntry slide idNum = do + element <- slideToSlideRelElement slide idNum + elemToEntry ("ppt/slides/_rels/" ++ slideToFilePath slide idNum ++ ".rels") element + +linkRelElement :: PandocMonad m => Int -> (URL, String) -> P m (Maybe Element) +linkRelElement idNum (url, _) = do + anchorMap <- gets stAnchorMap + case url of + -- if it's an anchor in the map, we use the slide number for an + -- internal link. + '#' : anchor | Just num <- M.lookup anchor anchorMap -> + return $ Just $ + mknode "Relationship" [ ("Id", "rId" ++ show idNum) + , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide") + , ("Target", "slide" ++ show num ++ ".xml") + ] () + -- if it's an anchor not in the map, we return nothing. + '#' : _ -> return Nothing + -- Anything else we treat as an external link + _ -> + return $ Just $ + mknode "Relationship" [ ("Id", "rId" ++ show idNum) + , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink") + , ("Target", url) + , ("TargetMode", "External") + ] () + +linkRelElements :: PandocMonad m => M.Map Int (URL, String) -> P m [Element] +linkRelElements mp = catMaybes <$> mapM (\(n, lnk) -> linkRelElement n lnk) (M.toList mp) + +mediaRelElement :: MediaInfo -> Element +mediaRelElement mInfo = + let ext = case mInfoExt mInfo of + Just e -> e + Nothing -> "" + in + mknode "Relationship" [ ("Id", "rId" ++ (show $ mInfoLocalId mInfo)) + , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image") + , ("Target", "../media/image" ++ (show $ mInfoGlobalId mInfo) ++ ext) + ] () + +slideToSlideRelElement :: PandocMonad m => Slide -> Int -> P m Element +slideToSlideRelElement slide idNum = do + let target = case slide of + (MetadataSlide _ _ _ _) -> "../slideLayouts/slideLayout1.xml" + (TitleSlide _) -> "../slideLayouts/slideLayout3.xml" + (ContentSlide _ _) -> "../slideLayouts/slideLayout2.xml" + (TwoColumnSlide _ _ _) -> "../slideLayouts/slideLayout4.xml" + + linkIds <- gets stLinkIds + mediaIds <- gets stMediaIds + + linkRels <- case M.lookup idNum linkIds of + Just mp -> linkRelElements mp + Nothing -> return [] + let mediaRels = case M.lookup idNum mediaIds of + Just mInfos -> map mediaRelElement mInfos + Nothing -> [] + + return $ + mknode "Relationships" + [("xmlns", "http://schemas.openxmlformats.org/package/2006/relationships")] + ([mknode "Relationship" [ ("Id", "rId1") + , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout") + , ("Target", target)] () + ] ++ linkRels ++ mediaRels) + +slideToSldIdElement :: PandocMonad m => Slide -> Int -> P m Element +slideToSldIdElement slide idNum = do + let id' = show $ idNum + 255 + rId <- slideToSlideId slide idNum + return $ mknode "p:sldId" [("id", id'), ("r:id", rId)] () + +presentationToSldIdLst :: PandocMonad m => Presentation -> P m Element +presentationToSldIdLst (Presentation slides) = do + ids <- mapM (\(s,n) -> slideToSldIdElement s n) (zip slides [1..]) + return $ mknode "p:sldIdLst" [] ids + +presentationToPresentationElement :: PandocMonad m => Presentation -> P m Element +presentationToPresentationElement pres = do + refArchive <- asks envRefArchive + distArchive <- asks envDistArchive + element <- parseXml refArchive distArchive "ppt/presentation.xml" + sldIdLst <- presentationToSldIdLst pres + + let modifySldIdLst :: Content -> Content + modifySldIdLst (Elem e) = case elName e of + (QName "sldIdLst" _ _) -> Elem sldIdLst + _ -> Elem e + modifySldIdLst ct = ct + + newContent = map modifySldIdLst $ elContent element + + return $ element{elContent = newContent} + +presentationToPresEntry :: PandocMonad m => Presentation -> P m Entry +presentationToPresEntry pres = presentationToPresentationElement pres >>= + elemToEntry "ppt/presentation.xml" + + + + +defaultContentTypeToElem :: DefaultContentType -> Element +defaultContentTypeToElem dct = + mknode "Default" + [("Extension", defContentTypesExt dct), + ("ContentType", defContentTypesType dct)] + () + +overrideContentTypeToElem :: OverrideContentType -> Element +overrideContentTypeToElem oct = + mknode "Override" + [("PartName", overrideContentTypesPart oct), + ("ContentType", overrideContentTypesType oct)] + () + +contentTypesToElement :: ContentTypes -> Element +contentTypesToElement ct = + let ns = "http://schemas.openxmlformats.org/package/2006/content-types" + in + mknode "Types" [("xmlns", ns)] $ + (map defaultContentTypeToElem $ contentTypesDefaults ct) ++ + (map overrideContentTypeToElem $ contentTypesOverrides ct) + +data DefaultContentType = DefaultContentType + { defContentTypesExt :: String + , defContentTypesType:: MimeType + } + deriving (Show, Eq) + +data OverrideContentType = OverrideContentType + { overrideContentTypesPart :: FilePath + , overrideContentTypesType :: MimeType + } + deriving (Show, Eq) + +data ContentTypes = ContentTypes { contentTypesDefaults :: [DefaultContentType] + , contentTypesOverrides :: [OverrideContentType] + } + deriving (Show, Eq) + +contentTypesToEntry :: PandocMonad m => ContentTypes -> P m Entry +contentTypesToEntry ct = elemToEntry "[Content_Types].xml" $ contentTypesToElement ct + +pathToOverride :: FilePath -> Maybe OverrideContentType +pathToOverride fp = OverrideContentType ("/" ++ fp) <$> (getContentType fp) + +mediaFileContentType :: FilePath -> Maybe DefaultContentType +mediaFileContentType fp = case takeExtension fp of + '.' : ext -> Just $ + DefaultContentType { defContentTypesExt = ext + , defContentTypesType = + case getMimeType fp of + Just mt -> mt + Nothing -> "application/octet-stream" + } + _ -> Nothing + +mediaContentType :: MediaInfo -> Maybe DefaultContentType +mediaContentType mInfo + | Just ('.' : ext) <- mInfoExt mInfo = + Just $ DefaultContentType { defContentTypesExt = ext + , defContentTypesType = + case mInfoMimeType mInfo of + Just mt -> mt + Nothing -> "application/octet-stream" + } + | otherwise = Nothing + +presentationToContentTypes :: PandocMonad m => Presentation -> P m ContentTypes +presentationToContentTypes (Presentation slides) = do + mediaInfos <- (mconcat . M.elems) <$> gets stMediaIds + filePaths <- patternsToFilePaths inheritedPatterns + let mediaFps = filter (match (compile "ppt/media/image*")) filePaths + let defaults = [ DefaultContentType "xml" "application/xml" + , DefaultContentType "rels" "application/vnd.openxmlformats-package.relationships+xml" + ] + mediaDefaults = nub $ + (mapMaybe mediaContentType $ mediaInfos) ++ + (mapMaybe mediaFileContentType $ mediaFps) + + inheritedOverrides = mapMaybe pathToOverride filePaths + presOverride = mapMaybe pathToOverride ["ppt/presentation.xml"] + slideOverrides = + mapMaybe + (\(s, n) -> + pathToOverride $ "ppt/slides/" ++ slideToFilePath s n) + (zip slides [1..]) + -- propOverride = mapMaybe pathToOverride ["docProps/core.xml"] + return $ ContentTypes + (defaults ++ mediaDefaults) + (inheritedOverrides ++ presOverride ++ slideOverrides) + +presML :: String +presML = "application/vnd.openxmlformats-officedocument.presentationml" + +noPresML :: String +noPresML = "application/vnd.openxmlformats-officedocument" + +getContentType :: FilePath -> Maybe MimeType +getContentType fp + | fp == "ppt/presentation.xml" = Just $ presML ++ ".presentation.main+xml" + | fp == "ppt/presProps.xml" = Just $ presML ++ ".presProps+xml" + | fp == "ppt/viewProps.xml" = Just $ presML ++ ".viewProps+xml" + | fp == "ppt/tableStyles.xml" = Just $ presML ++ ".tableStyles+xml" + | fp == "docProps/core.xml" = Just $ "application/vnd.openxmlformats-package.core-properties+xml" + | fp == "docProps/app.xml" = Just $ noPresML ++ ".extended-properties+xml" + | "ppt" : "slideMasters" : f : [] <- splitDirectories fp + , (_, ".xml") <- splitExtension f = + Just $ presML ++ ".slideMaster+xml" + | "ppt" : "slides" : f : [] <- splitDirectories fp + , (_, ".xml") <- splitExtension f = + Just $ presML ++ ".slide+xml" + | "ppt" : "notesMasters" : f : [] <- splitDirectories fp + , (_, ".xml") <- splitExtension f = + Just $ presML ++ ".notesMaster+xml" + | "ppt" : "notesSlides" : f : [] <- splitDirectories fp + , (_, ".xml") <- splitExtension f = + Just $ presML ++ ".notesSlide+xml" + | "ppt" : "theme" : f : [] <- splitDirectories fp + , (_, ".xml") <- splitExtension f = + Just $ noPresML ++ ".theme+xml" + | "ppt" : "slideLayouts" : _ : [] <- splitDirectories fp= + Just $ presML ++ ".slideLayout+xml" + | otherwise = Nothing + +autoNumberingToType :: ListAttributes -> String +autoNumberingToType (_, numStyle, numDelim) = + typeString ++ delimString + where + typeString = case numStyle of + Decimal -> "arabic" + UpperAlpha -> "alphaUc" + LowerAlpha -> "alphaLc" + UpperRoman -> "romanUc" + LowerRoman -> "romanLc" + _ -> "arabic" + delimString = case numDelim of + Period -> "Period" + OneParen -> "ParenR" + TwoParens -> "ParenBoth" + _ -> "Period" diff --git a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs new file mode 100644 index 000000000..68b2aeeb2 --- /dev/null +++ b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs @@ -0,0 +1,701 @@ +{-# LANGUAGE PatternGuards #-} + +{- +Copyright (C) 2017-2018 Jesse Rosenthal <jrosenthal@jhu.edu> + +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.Writers.Powerpoint.Presentation + Copyright : Copyright (C) 2017-2018 Jesse Rosenthal + License : GNU GPL, version 2 or above + + Maintainer : Jesse Rosenthal <jrosenthal@jhu.edu> + Stability : alpha + Portability : portable + +Definition of Presentation datatype, modeling a MS Powerpoint (pptx) +document, and functions for converting a Pandoc document to +Presentation. +-} + +module Text.Pandoc.Writers.Powerpoint.Presentation ( documentToPresentation + , Presentation(..) + , Slide(..) + , SlideElement(..) + , Shape(..) + , Graphic(..) + , BulletType(..) + , Algnment(..) + , Paragraph(..) + , ParaElem(..) + , ParaProps(..) + , RunProps(..) + , TableProps(..) + , Strikethrough(..) + , Capitals(..) + , PicProps(..) + , URL + , TeXString(..) + ) where + + +import Control.Monad.Reader +import Control.Monad.State +import Data.List (intercalate) +import Data.Default +import Text.Pandoc.Definition +import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Slides (getSlideLevel) +import qualified Text.Pandoc.Class as P +import Text.Pandoc.Options +import Text.Pandoc.Logging +import Text.Pandoc.Walk +import qualified Text.Pandoc.Shared as Shared -- so we don't overlap "Element" +import Text.Pandoc.Writers.Shared (metaValueToInlines) +import qualified Data.Map as M +import Data.Maybe (maybeToList) + +data WriterEnv = WriterEnv { envMetadata :: Meta + , envRunProps :: RunProps + , envParaProps :: ParaProps + , envSlideLevel :: Int + , envOpts :: WriterOptions + , envSlideHasHeader :: Bool + , envInList :: Bool + , envInNoteSlide :: Bool + , envCurSlideId :: Int + -- the difference between the number at + -- the end of the slide file name and + -- the rId number + , envSlideIdOffset :: Int + } + deriving (Show) + +instance Default WriterEnv where + def = WriterEnv { envMetadata = mempty + , envRunProps = def + , envParaProps = def + , envSlideLevel = 2 + , envOpts = def + , envSlideHasHeader = False + , envInList = False + , envInNoteSlide = False + , envCurSlideId = 1 + , envSlideIdOffset = 1 + } + + +data WriterState = WriterState { stNoteIds :: M.Map Int [Block] + -- associate anchors with slide id + , stAnchorMap :: M.Map String Int + } deriving (Show, Eq) + +instance Default WriterState where + def = WriterState { stNoteIds = mempty + , stAnchorMap= mempty + } + +type Pres m = ReaderT WriterEnv (StateT WriterState m) + +runPres :: Monad m => WriterEnv -> WriterState -> Pres m a -> m a +runPres env st p = evalStateT (runReaderT p env) st + +-- GHC 7.8 will still complain about concat <$> mapM unless we specify +-- Functor. We can get rid of this when we stop supporting GHC 7.8. +concatMapM :: (Monad m) => (a -> m [b]) -> [a] -> m [b] +concatMapM f xs = liftM concat (mapM f xs) + +type Pixels = Integer + +data Presentation = Presentation [Slide] + deriving (Show) + +data Slide = MetadataSlide { metadataSlideTitle :: [ParaElem] + , metadataSlideSubtitle :: [ParaElem] + , metadataSlideAuthors :: [[ParaElem]] + , metadataSlideDate :: [ParaElem] + } + | TitleSlide { titleSlideHeader :: [ParaElem]} + | ContentSlide { contentSlideHeader :: [ParaElem] + , contentSlideContent :: [Shape] + } + | TwoColumnSlide { twoColumnSlideHeader :: [ParaElem] + , twoColumnSlideLeft :: [Shape] + , twoColumnSlideRight :: [Shape] + } + deriving (Show, Eq) + +data SlideElement = SlideElement Pixels Pixels Pixels Pixels Shape + deriving (Show, Eq) + +data Shape = Pic PicProps FilePath Text.Pandoc.Definition.Attr [ParaElem] + | GraphicFrame [Graphic] [ParaElem] + | TextBox [Paragraph] + deriving (Show, Eq) + +type Cell = [Paragraph] + +data TableProps = TableProps { tblPrFirstRow :: Bool + , tblPrBandRow :: Bool + } deriving (Show, Eq) + +data Graphic = Tbl TableProps [Cell] [[Cell]] + deriving (Show, Eq) + + +data Paragraph = Paragraph { paraProps :: ParaProps + , paraElems :: [ParaElem] + } deriving (Show, Eq) + + +data BulletType = Bullet + | AutoNumbering ListAttributes + deriving (Show, Eq) + +data Algnment = AlgnLeft | AlgnRight | AlgnCenter + deriving (Show, Eq) + +data ParaProps = ParaProps { pPropMarginLeft :: Maybe Pixels + , pPropMarginRight :: Maybe Pixels + , pPropLevel :: Int + , pPropBullet :: Maybe BulletType + , pPropAlign :: Maybe Algnment + , pPropSpaceBefore :: Maybe Pixels + } deriving (Show, Eq) + +instance Default ParaProps where + def = ParaProps { pPropMarginLeft = Just 0 + , pPropMarginRight = Just 0 + , pPropLevel = 0 + , pPropBullet = Nothing + , pPropAlign = Nothing + , pPropSpaceBefore = Nothing + } + +newtype TeXString = TeXString {unTeXString :: String} + deriving (Eq, Show) + +data ParaElem = Break + | Run RunProps String + -- It would be more elegant to have native TeXMath + -- Expressions here, but this allows us to use + -- `convertmath` from T.P.Writers.Math. Will perhaps + -- revisit in the future. + | MathElem MathType TeXString + deriving (Show, Eq) + +data Strikethrough = NoStrike | SingleStrike | DoubleStrike + deriving (Show, Eq) + +data Capitals = NoCapitals | SmallCapitals | AllCapitals + deriving (Show, Eq) + +type URL = String + +data RunProps = RunProps { rPropBold :: Bool + , rPropItalics :: Bool + , rStrikethrough :: Maybe Strikethrough + , rBaseline :: Maybe Int + , rCap :: Maybe Capitals + , rLink :: Maybe (URL, String) + , rPropCode :: Bool + , rPropBlockQuote :: Bool + , rPropForceSize :: Maybe Pixels + } deriving (Show, Eq) + +instance Default RunProps where + def = RunProps { rPropBold = False + , rPropItalics = False + , rStrikethrough = Nothing + , rBaseline = Nothing + , rCap = Nothing + , rLink = Nothing + , rPropCode = False + , rPropBlockQuote = False + , rPropForceSize = Nothing + } + +data PicProps = PicProps { picPropLink :: Maybe (URL, String) + } deriving (Show, Eq) + +instance Default PicProps where + def = PicProps { picPropLink = Nothing + } + +-------------------------------------------------- + +inlinesToParElems :: Monad m => [Inline] -> Pres m [ParaElem] +inlinesToParElems ils = concatMapM inlineToParElems ils + +inlineToParElems :: Monad m => Inline -> Pres m [ParaElem] +inlineToParElems (Str s) = do + pr <- asks envRunProps + return [Run pr s] +inlineToParElems (Emph ils) = + local (\r -> r{envRunProps = (envRunProps r){rPropItalics=True}}) $ + inlinesToParElems ils +inlineToParElems (Strong ils) = + local (\r -> r{envRunProps = (envRunProps r){rPropBold=True}}) $ + inlinesToParElems ils +inlineToParElems (Strikeout ils) = + local (\r -> r{envRunProps = (envRunProps r){rStrikethrough=Just SingleStrike}}) $ + inlinesToParElems ils +inlineToParElems (Superscript ils) = + local (\r -> r{envRunProps = (envRunProps r){rBaseline=Just 30000}}) $ + inlinesToParElems ils +inlineToParElems (Subscript ils) = + local (\r -> r{envRunProps = (envRunProps r){rBaseline=Just (-25000)}}) $ + inlinesToParElems ils +inlineToParElems (SmallCaps ils) = + local (\r -> r{envRunProps = (envRunProps r){rCap = Just SmallCapitals}}) $ + inlinesToParElems ils +inlineToParElems Space = inlineToParElems (Str " ") +inlineToParElems SoftBreak = inlineToParElems (Str " ") +inlineToParElems LineBreak = return [Break] +inlineToParElems (Link _ ils (url, title)) = do + local (\r ->r{envRunProps = (envRunProps r){rLink = Just (url, title)}}) $ + inlinesToParElems ils +inlineToParElems (Code _ str) = do + local (\r ->r{envRunProps = (envRunProps r){rPropCode = True}}) $ + inlineToParElems $ Str str +inlineToParElems (Math mathtype str) = + return [MathElem mathtype (TeXString str)] +inlineToParElems (Note blks) = do + notes <- gets stNoteIds + let maxNoteId = case M.keys notes of + [] -> 0 + lst -> maximum lst + curNoteId = maxNoteId + 1 + modify $ \st -> st { stNoteIds = M.insert curNoteId blks notes } + inlineToParElems $ Superscript [Str $ show curNoteId] +inlineToParElems (Span _ ils) = concatMapM inlineToParElems ils +inlineToParElems (RawInline _ _) = return [] +inlineToParElems _ = return [] + +isListType :: Block -> Bool +isListType (OrderedList _ _) = True +isListType (BulletList _) = True +isListType (DefinitionList _) = True +isListType _ = False + +registerAnchorId :: PandocMonad m => String -> Pres m () +registerAnchorId anchor = do + anchorMap <- gets stAnchorMap + slideId <- asks envCurSlideId + unless (null anchor) $ + modify $ \st -> st {stAnchorMap = M.insert anchor slideId anchorMap} + +-- Currently hardcoded, until I figure out how to make it dynamic. +blockQuoteSize :: Pixels +blockQuoteSize = 20 + +noteSize :: Pixels +noteSize = 18 + +blockToParagraphs :: PandocMonad m => Block -> Pres m [Paragraph] +blockToParagraphs (Plain ils) = do + parElems <- inlinesToParElems ils + pProps <- asks envParaProps + return [Paragraph pProps parElems] +blockToParagraphs (Para ils) = do + parElems <- inlinesToParElems ils + pProps <- asks envParaProps + return [Paragraph pProps parElems] +blockToParagraphs (LineBlock ilsList) = do + parElems <- inlinesToParElems $ intercalate [LineBreak] ilsList + pProps <- asks envParaProps + return [Paragraph pProps parElems] +-- TODO: work out the attributes +blockToParagraphs (CodeBlock attr str) = + local (\r -> r{envParaProps = def{pPropMarginLeft = Just 100}}) $ + blockToParagraphs $ Para [Code attr str] +-- We can't yet do incremental lists, but we should render a +-- (BlockQuote List) as a list to maintain compatibility with other +-- formats. +blockToParagraphs (BlockQuote (blk : blks)) | isListType blk = do + ps <- blockToParagraphs blk + ps' <- blockToParagraphs $ BlockQuote blks + return $ ps ++ ps' +blockToParagraphs (BlockQuote blks) = + local (\r -> r{ envParaProps = (envParaProps r){pPropMarginLeft = Just 100} + , envRunProps = (envRunProps r){rPropForceSize = Just blockQuoteSize}})$ + concatMapM blockToParagraphs blks +-- TODO: work out the format +blockToParagraphs (RawBlock _ _) = return [] +blockToParagraphs (Header _ (ident, _, _) ils) = do + -- Note that this function only deals with content blocks, so it + -- will only touch headers that are above the current slide level -- + -- slides at or below the slidelevel will be taken care of by + -- `blocksToSlide'`. We have the register anchors in both of them. + registerAnchorId ident + -- we set the subeader to bold + parElems <- local (\e->e{envRunProps = (envRunProps e){rPropBold=True}}) $ + inlinesToParElems ils + -- and give it a bit of space before it. + return [Paragraph def{pPropSpaceBefore = Just 30} parElems] +blockToParagraphs (BulletList blksLst) = do + pProps <- asks envParaProps + let lvl = pPropLevel pProps + local (\env -> env{ envInList = True + , envParaProps = pProps{ pPropLevel = lvl + 1 + , pPropBullet = Just Bullet + , pPropMarginLeft = Nothing + }}) $ + concatMapM multiParBullet blksLst +blockToParagraphs (OrderedList listAttr blksLst) = do + pProps <- asks envParaProps + let lvl = pPropLevel pProps + local (\env -> env{ envInList = True + , envParaProps = pProps{ pPropLevel = lvl + 1 + , pPropBullet = Just (AutoNumbering listAttr) + , pPropMarginLeft = Nothing + }}) $ + concatMapM multiParBullet blksLst +blockToParagraphs (DefinitionList entries) = do + let go :: PandocMonad m => ([Inline], [[Block]]) -> Pres m [Paragraph] + go (ils, blksLst) = do + term <-blockToParagraphs $ Para [Strong ils] + -- For now, we'll treat each definition term as a + -- blockquote. We can extend this further later. + definition <- concatMapM (blockToParagraphs . BlockQuote) blksLst + return $ term ++ definition + concatMapM go entries +blockToParagraphs (Div (_, ("notes" : []), _) _) = return [] +blockToParagraphs (Div _ blks) = concatMapM blockToParagraphs blks +blockToParagraphs blk = do + P.report $ BlockNotRendered blk + return [] + +-- Make sure the bullet env gets turned off after the first para. +multiParBullet :: PandocMonad m => [Block] -> Pres m [Paragraph] +multiParBullet [] = return [] +multiParBullet (b:bs) = do + pProps <- asks envParaProps + p <- blockToParagraphs b + ps <- local (\env -> env{envParaProps = pProps{pPropBullet = Nothing}}) $ + concatMapM blockToParagraphs bs + return $ p ++ ps + +cellToParagraphs :: PandocMonad m => Alignment -> TableCell -> Pres m [Paragraph] +cellToParagraphs algn tblCell = do + paras <- mapM (blockToParagraphs) tblCell + let alignment = case algn of + AlignLeft -> Just AlgnLeft + AlignRight -> Just AlgnRight + AlignCenter -> Just AlgnCenter + AlignDefault -> Nothing + paras' = map (map (\p -> p{paraProps = (paraProps p){pPropAlign = alignment}})) paras + return $ concat paras' + +rowToParagraphs :: PandocMonad m => [Alignment] -> [TableCell] -> Pres m [[Paragraph]] +rowToParagraphs algns tblCells = do + -- We have to make sure we have the right number of alignments + let pairs = zip (algns ++ repeat AlignDefault) tblCells + mapM (\(a, tc) -> cellToParagraphs a tc) pairs + +blockToShape :: PandocMonad m => Block -> Pres m Shape +blockToShape (Plain (il:_)) | Image attr ils (url, _) <- il = + Pic def url attr <$> (inlinesToParElems ils) +blockToShape (Para (il:_)) | Image attr ils (url, _) <- il = + Pic def url attr <$> (inlinesToParElems ils) +blockToShape (Plain (il:_)) | Link _ (il':_) target <- il + , Image attr ils (url, _) <- il' = + Pic def{picPropLink = Just target} url attr <$> (inlinesToParElems ils) +blockToShape (Para (il:_)) | Link _ (il':_) target <- il + , Image attr ils (url, _) <- il' = + Pic def{picPropLink = Just target} url attr <$> (inlinesToParElems ils) +blockToShape (Table caption algn _ hdrCells rows) = do + caption' <- inlinesToParElems caption + hdrCells' <- rowToParagraphs algn hdrCells + rows' <- mapM (rowToParagraphs algn) rows + let tblPr = if null hdrCells + then TableProps { tblPrFirstRow = False + , tblPrBandRow = True + } + else TableProps { tblPrFirstRow = True + , tblPrBandRow = True + } + + return $ GraphicFrame [Tbl tblPr hdrCells' rows'] caption' +blockToShape blk = do paras <- blockToParagraphs blk + let paras' = map (\par -> par{paraElems = combineParaElems $ paraElems par}) paras + return $ TextBox paras' + +combineShapes :: [Shape] -> [Shape] +combineShapes [] = [] +combineShapes (s : []) = [s] +combineShapes (pic@(Pic _ _ _ _) : ss) = pic : combineShapes ss +combineShapes ((TextBox []) : ss) = combineShapes ss +combineShapes (s : TextBox [] : ss) = combineShapes (s : ss) +combineShapes ((TextBox (p:ps)) : (TextBox (p':ps')) : ss) = + combineShapes $ TextBox ((p:ps) ++ (p':ps')) : ss +combineShapes (s:ss) = s : combineShapes ss + +blocksToShapes :: PandocMonad m => [Block] -> Pres m [Shape] +blocksToShapes blks = combineShapes <$> mapM blockToShape blks + +isImage :: Inline -> Bool +isImage (Image _ _ _) = True +isImage (Link _ ((Image _ _ _) : _) _) = True +isImage _ = False + +splitBlocks' :: Monad m => [Block] -> [[Block]] -> [Block] -> Pres m [[Block]] +splitBlocks' cur acc [] = return $ acc ++ (if null cur then [] else [cur]) +splitBlocks' cur acc (HorizontalRule : blks) = + splitBlocks' [] (acc ++ (if null cur then [] else [cur])) blks +splitBlocks' cur acc (h@(Header n _ _) : blks) = do + slideLevel <- asks envSlideLevel + case compare n slideLevel of + LT -> splitBlocks' [] (acc ++ (if null cur then [] else [cur]) ++ [[h]]) blks + EQ -> splitBlocks' [h] (acc ++ (if null cur then [] else [cur])) blks + GT -> splitBlocks' (cur ++ [h]) acc blks +-- `blockToParagraphs` treats Plain and Para the same, so we can save +-- some code duplication by treating them the same here. +splitBlocks' cur acc ((Plain ils) : blks) = splitBlocks' cur acc ((Para ils) : blks) +splitBlocks' cur acc ((Para (il:ils)) : blks) | isImage il = do + slideLevel <- asks envSlideLevel + case cur of + (Header n _ _) : [] | n == slideLevel -> + splitBlocks' [] + (acc ++ [cur ++ [Para [il]]]) + (if null ils then blks else (Para ils) : blks) + _ -> splitBlocks' [] + (acc ++ (if null cur then [] else [cur]) ++ [[Para [il]]]) + (if null ils then blks else (Para ils) : blks) +splitBlocks' cur acc (tbl@(Table _ _ _ _ _) : blks) = do + slideLevel <- asks envSlideLevel + case cur of + (Header n _ _) : [] | n == slideLevel -> + splitBlocks' [] (acc ++ [cur ++ [tbl]]) blks + _ -> splitBlocks' [] (acc ++ (if null cur then [] else [cur]) ++ [[tbl]]) blks +splitBlocks' cur acc (d@(Div (_, classes, _) _): blks) | "columns" `elem` classes = do + slideLevel <- asks envSlideLevel + case cur of + (Header n _ _) : [] | n == slideLevel -> + splitBlocks' [] (acc ++ [cur ++ [d]]) blks + _ -> splitBlocks' [] (acc ++ (if null cur then [] else [cur]) ++ [[d]]) blks +splitBlocks' cur acc (blk : blks) = splitBlocks' (cur ++ [blk]) acc blks + +splitBlocks :: Monad m => [Block] -> Pres m [[Block]] +splitBlocks = splitBlocks' [] [] + +blocksToSlide' :: PandocMonad m => Int -> [Block] -> Pres m Slide +blocksToSlide' lvl ((Header n (ident, _, _) ils) : blks) + | n < lvl = do + registerAnchorId ident + hdr <- inlinesToParElems ils + return $ TitleSlide {titleSlideHeader = hdr} + | n == lvl = do + registerAnchorId ident + hdr <- inlinesToParElems ils + -- Now get the slide without the header, and then add the header + -- in. + slide <- blocksToSlide' lvl blks + return $ case slide of + ContentSlide _ cont -> ContentSlide hdr cont + TwoColumnSlide _ contL contR -> TwoColumnSlide hdr contL contR + slide' -> slide' +blocksToSlide' _ (blk : blks) + | Div (_, classes, _) divBlks <- blk + , "columns" `elem` classes + , (Div (_, clsL, _) blksL) : (Div (_, clsR, _) blksR) : remaining <- divBlks + , "column" `elem` clsL, "column" `elem` clsR = do + unless (null blks) + (mapM (P.report . BlockNotRendered) blks >> return ()) + unless (null remaining) + (mapM (P.report . BlockNotRendered) remaining >> return ()) + mbSplitBlksL <- splitBlocks blksL + mbSplitBlksR <- splitBlocks blksR + let blksL' = case mbSplitBlksL of + bs : _ -> bs + [] -> [] + let blksR' = case mbSplitBlksR of + bs : _ -> bs + [] -> [] + shapesL <- blocksToShapes blksL' + shapesR <- blocksToShapes blksR' + return $ TwoColumnSlide { twoColumnSlideHeader = [] + , twoColumnSlideLeft = shapesL + , twoColumnSlideRight = shapesR + } +blocksToSlide' _ (blk : blks) = do + inNoteSlide <- asks envInNoteSlide + shapes <- if inNoteSlide + then forceFontSize noteSize $ blocksToShapes (blk : blks) + else blocksToShapes (blk : blks) + return $ ContentSlide { contentSlideHeader = [] + , contentSlideContent = shapes + } +blocksToSlide' _ [] = return $ ContentSlide { contentSlideHeader = [] + , contentSlideContent = [] + } + +blocksToSlide :: PandocMonad m => [Block] -> Pres m Slide +blocksToSlide blks = do + slideLevel <- asks envSlideLevel + blocksToSlide' slideLevel blks + +makeNoteEntry :: Int -> [Block] -> [Block] +makeNoteEntry n blks = + let enum = Str (show n ++ ".") + in + case blks of + (Para ils : blks') -> (Para $ enum : Space : ils) : blks' + _ -> (Para [enum]) : blks + +forceFontSize :: PandocMonad m => Pixels -> Pres m a -> Pres m a +forceFontSize px x = do + rpr <- asks envRunProps + local (\r -> r {envRunProps = rpr{rPropForceSize = Just px}}) x + +-- We leave these as blocks because we will want to include them in +-- the TOC. +makeNotesSlideBlocks :: PandocMonad m => Pres m [Block] +makeNotesSlideBlocks = do + noteIds <- gets stNoteIds + slideLevel <- asks envSlideLevel + meta <- asks envMetadata + -- Get identifiers so we can give the notes section a unique ident. + anchorSet <- M.keysSet <$> gets stAnchorMap + if M.null noteIds + then return [] + else do let title = case lookupMeta "notes-title" meta of + Just val -> metaValueToInlines val + Nothing -> [Str "Notes"] + ident = Shared.uniqueIdent title anchorSet + hdr = Header slideLevel (ident, [], []) title + blks <- return $ + concatMap (\(n, bs) -> makeNoteEntry n bs) $ + M.toList noteIds + return $ hdr : blks + +getMetaSlide :: PandocMonad m => Pres m (Maybe Slide) +getMetaSlide = do + meta <- asks envMetadata + title <- inlinesToParElems $ docTitle meta + subtitle <- inlinesToParElems $ + case lookupMeta "subtitle" meta of + Just (MetaString s) -> [Str s] + Just (MetaInlines ils) -> ils + Just (MetaBlocks [Plain ils]) -> ils + Just (MetaBlocks [Para ils]) -> ils + _ -> [] + authors <- mapM inlinesToParElems $ docAuthors meta + date <- inlinesToParElems $ docDate meta + if null title && null subtitle && null authors && null date + then return Nothing + else return $ Just $ MetadataSlide { metadataSlideTitle = title + , metadataSlideSubtitle = subtitle + , metadataSlideAuthors = authors + , metadataSlideDate = date + } +-- adapted from the markdown writer +elementToListItem :: PandocMonad m => Shared.Element -> Pres m [Block] +elementToListItem (Shared.Sec lev _nums (ident,_,_) headerText subsecs) = do + opts <- asks envOpts + let headerLink = if null ident + then walk Shared.deNote headerText + else [Link nullAttr (walk Shared.deNote headerText) + ('#':ident, "")] + listContents <- if null subsecs || lev >= writerTOCDepth opts + then return [] + else mapM elementToListItem subsecs + return [Plain headerLink, BulletList listContents] +elementToListItem (Shared.Blk _) = return [] + +makeTOCSlide :: PandocMonad m => [Block] -> Pres m Slide +makeTOCSlide blks = do + contents <- BulletList <$> mapM elementToListItem (Shared.hierarchicalize blks) + meta <- asks envMetadata + slideLevel <- asks envSlideLevel + let tocTitle = case lookupMeta "toc-title" meta of + Just val -> metaValueToInlines val + Nothing -> [Str "Table of Contents"] + hdr = Header slideLevel nullAttr tocTitle + sld <- blocksToSlide [hdr, contents] + return sld + +combineParaElems' :: Maybe ParaElem -> [ParaElem] -> [ParaElem] +combineParaElems' mbPElem [] = maybeToList mbPElem +combineParaElems' Nothing (pElem : pElems) = + combineParaElems' (Just pElem) pElems +combineParaElems' (Just pElem') (pElem : pElems) + | Run rPr' s' <- pElem' + , Run rPr s <- pElem + , rPr == rPr' = + combineParaElems' (Just $ Run rPr' $ s' ++ s) pElems + | otherwise = + pElem' : combineParaElems' (Just pElem) pElems + +combineParaElems :: [ParaElem] -> [ParaElem] +combineParaElems = combineParaElems' Nothing + +blocksToPresentation :: PandocMonad m => [Block] -> Pres m Presentation +blocksToPresentation blks = do + opts <- asks envOpts + let metadataStartNum = 1 + metadataslides <- maybeToList <$> getMetaSlide + let tocStartNum = metadataStartNum + length metadataslides + -- As far as I can tell, if we want to have a variable-length toc in + -- the future, we'll have to make it twice. Once to get the length, + -- and a second time to include the notes slide. We can't make the + -- notes slide before the body slides because we need to know if + -- there are notes, and we can't make either before the toc slide, + -- because we need to know its length to get slide numbers right. + -- + -- For now, though, since the TOC slide is only length 1, if it + -- exists, we'll just get the length, and then come back to make the + -- slide later + let tocSlidesLength = if writerTableOfContents opts then 1 else 0 + let bodyStartNum = tocStartNum + tocSlidesLength + blksLst <- splitBlocks blks + bodyslides <- mapM + (\(bs, n) -> local (\st -> st{envCurSlideId = n}) (blocksToSlide bs)) + (zip blksLst [bodyStartNum..]) + let noteStartNum = bodyStartNum + length bodyslides + notesSlideBlocks <- makeNotesSlideBlocks + -- now we come back and make the real toc... + tocSlides <- if writerTableOfContents opts + then do toc <- makeTOCSlide $ blks ++ notesSlideBlocks + return [toc] + else return [] + -- ... and the notes slide. We test to see if the blocks are empty, + -- because we don't want to make an empty slide. + notesSlides <- if null notesSlideBlocks + then return [] + else do notesSlide <- local + (\env -> env { envCurSlideId = noteStartNum + , envInNoteSlide = True + }) + (blocksToSlide $ notesSlideBlocks) + return [notesSlide] + return $ + Presentation $ + metadataslides ++ tocSlides ++ bodyslides ++ notesSlides + +documentToPresentation :: PandocMonad m + => WriterOptions + -> Pandoc + -> m Presentation +documentToPresentation opts (Pandoc meta blks) = do + let env = def { envOpts = opts + , envMetadata = meta + , envSlideLevel = case writerSlideLevel opts of + Just lvl -> lvl + Nothing -> getSlideLevel blks + } + runPres env def $ blocksToPresentation blks -- cgit v1.2.3 From 4debcf7bdc38fb44d9fb53d37251e7aac2c5c82e Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 14 Jan 2018 10:28:55 -0800 Subject: LaTeX reader: Advance source position at end of stream. --- 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 0a78fbe53..6298e0b2f 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -405,7 +405,7 @@ satisfyTok f = | otherwise = Nothing updatePos :: SourcePos -> Tok -> [Tok] -> SourcePos updatePos _spos _ (Tok pos _ _ : _) = pos - updatePos spos _ [] = spos + updatePos spos _ [] = incSourceColumn spos 1 doMacros :: PandocMonad m => Int -> LP m () doMacros n = do -- cgit v1.2.3 From d9584d73f94501787026c57b77d217e51f21505d Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 14 Jan 2018 12:23:16 -0800 Subject: Markdown reader: Improved inlinesInBalancedBrackets. The change both improves performance and fixes a regression whereby normal citations inside inline notes were not parsed correctly. Closes jgm/pandoc-citeproc#315. --- src/Text/Pandoc/Parsing.hs | 1 + src/Text/Pandoc/Readers/Markdown.hs | 34 +++++++++++++++++++++------------- 2 files changed, 22 insertions(+), 13 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 9573d7875..f1b823965 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -180,6 +180,7 @@ module Text.Pandoc.Parsing ( takeWhileP, sourceLine, setSourceColumn, setSourceLine, + incSourceColumn, newPos, Line, Column diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index aaefa5ba1..b76ff498b 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -148,19 +148,27 @@ litChar = escapedChar' -- | Parse a sequence of inline elements between square brackets, -- including inlines between balanced pairs of square brackets. inlinesInBalancedBrackets :: PandocMonad m => MarkdownParser m (F Inlines) -inlinesInBalancedBrackets = try $ char '[' >> go 1 - where go :: PandocMonad m => Int -> MarkdownParser m (F Inlines) - go 0 = return mempty - go openBrackets = - (mappend <$> (bracketedSpan <|> link <|> image) <*> - go openBrackets) - <|> ((if openBrackets > 1 - then (return (B.str "]") <>) - else id) <$> - (char ']' >> go (openBrackets - 1))) - <|> ((return (B.str "[") <>) <$> - (char '[' >> go (openBrackets + 1))) - <|> (mappend <$> inline <*> go openBrackets) +inlinesInBalancedBrackets = + try $ char '[' >> withRaw (go 1) >>= + parseFromString inlines . stripBracket . snd + where stripBracket [] = [] + stripBracket xs = if last xs == ']' then init xs else xs + go :: PandocMonad m => Int -> MarkdownParser m () + go 0 = return () + go openBrackets = do + (() <$ (escapedChar <|> + code <|> + rawHtmlInline <|> + rawLaTeXInline') >> go openBrackets) + <|> + (do char ']' + if openBrackets > 1 + then go (openBrackets - 1) + else return ()) + <|> + (char '[' >> go (openBrackets + 1)) + <|> + (anyChar >> go openBrackets) -- -- document structure -- cgit v1.2.3 From 615a99c2c20782daae5de38854025e2d40d85f29 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sun, 14 Jan 2018 15:10:13 -0800 Subject: RST reader: add aligned environment when needed in math. rst2latex.py uses an align* environment for math in `.. math::` blocks, so this math may contain line breaks. If it does, we put the math in an `aligned` environment to simulate rst2latex.py's behavior. Closes #4254. --- src/Text/Pandoc/Readers/RST.hs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index ba5a24f8f..49cc3018c 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -972,11 +972,16 @@ extractCaption = do legend <- optional blanklines >> (mconcat <$> many block) return (capt,legend) --- divide string by blanklines +-- divide string by blanklines, and surround with +-- \begin{aligned}...\end{aligned} if needed. toChunks :: String -> [String] toChunks = dropWhile null - . map (trim . unlines) + . map (addAligned . trim . unlines) . splitBy (all (`elem` (" \t" :: String))) . lines + -- we put this in an aligned environment if it contains \\, see #4254 + where addAligned s = if "\\\\" `isInfixOf` s + then "\\begin{aligned}\n" ++ s ++ "\n\\end{aligned}" + else s codeblock :: [String] -> Maybe String -> String -> String -> RSTParser m Blocks codeblock classes numberLines lang body = -- cgit v1.2.3 From 3156722ac413ddbc200dfcfdabcf08e7c2e1875d Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sun, 14 Jan 2018 21:56:00 -0500 Subject: Powerpoint writer: Fix anchor links. They were broken when I refactored (the Output module wanted to use state left over from the construction of the Presentation type). This change introduces a new type `LinkTarget = InternalTarget | ExternalTarget`. Internal target points to a slide number, and these will all be resolved before the Presentation is passed along to the Output module. --- src/Text/Pandoc/Writers/Powerpoint/Output.hs | 65 +++++++----------- src/Text/Pandoc/Writers/Powerpoint/Presentation.hs | 76 +++++++++++++++++----- 2 files changed, 81 insertions(+), 60 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint/Output.hs b/src/Text/Pandoc/Writers/Powerpoint/Output.hs index f3df62690..c45479579 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Output.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Output.hs @@ -54,7 +54,7 @@ import Text.Pandoc.MIME import qualified Data.ByteString.Lazy as BL import Text.Pandoc.Writers.OOXML import qualified Data.Map as M -import Data.Maybe (mapMaybe, listToMaybe, catMaybes) +import Data.Maybe (mapMaybe, listToMaybe) import Text.Pandoc.ImageSize import Control.Applicative ((<|>)) import System.FilePath.Glob @@ -135,24 +135,16 @@ data MediaInfo = MediaInfo { mInfoFilePath :: FilePath , mInfoCaption :: Bool } deriving (Show, Eq) -data WriterState = WriterState { stLinkIds :: M.Map Int (M.Map Int (URL, String)) +data WriterState = WriterState { stLinkIds :: M.Map Int (M.Map Int LinkTarget) -- (FP, Local ID, Global ID, Maybe Mime) , stMediaIds :: M.Map Int [MediaInfo] , stMediaGlobalIds :: M.Map FilePath Int - , stNoteIds :: M.Map Int [Block] - -- associate anchors with slide id - , stAnchorMap :: M.Map String Int - -- media inherited from the template. - , stTemplateMedia :: [FilePath] } deriving (Show, Eq) instance Default WriterState where def = WriterState { stLinkIds = mempty , stMediaIds = mempty , stMediaGlobalIds = mempty - , stNoteIds = mempty - , stAnchorMap= mempty - , stTemplateMedia = [] } type P m = ReaderT WriterEnv (StateT WriterState m) @@ -420,7 +412,7 @@ replaceNamedChildren ns prefix name newKids element = ---------------------------------------------------------------- -registerLink :: PandocMonad m => (URL, String) -> P m Int +registerLink :: PandocMonad m => LinkTarget -> P m Int registerLink link = do curSlideId <- asks envCurSlideId linkReg <- gets stLinkIds @@ -729,20 +721,15 @@ paraElemToElement (Run rpr s) = do -- first we have to make sure that if it's an -- anchor, it's in the anchor map. If not, there's -- no link. - anchorMap <- gets stAnchorMap return $ case link of - -- anchor with nothing in the map - ('#':target, _) | Nothing <- M.lookup target anchorMap -> - [] - -- anchor that is in the map - ('#':_, _) -> + InternalTarget _ -> let linkAttrs = [ ("r:id", "rId" ++ show idNum) , ("action", "ppaction://hlinksldjump") ] in [mknode "a:hlinkClick" linkAttrs ()] -- external - _ -> + ExternalTarget _ -> let linkAttrs = [ ("r:id", "rId" ++ show idNum) ] @@ -1191,31 +1178,23 @@ slideToSlideRelEntry slide idNum = do element <- slideToSlideRelElement slide idNum elemToEntry ("ppt/slides/_rels/" ++ slideToFilePath slide idNum ++ ".rels") element -linkRelElement :: PandocMonad m => Int -> (URL, String) -> P m (Maybe Element) -linkRelElement idNum (url, _) = do - anchorMap <- gets stAnchorMap - case url of - -- if it's an anchor in the map, we use the slide number for an - -- internal link. - '#' : anchor | Just num <- M.lookup anchor anchorMap -> - return $ Just $ - mknode "Relationship" [ ("Id", "rId" ++ show idNum) - , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide") - , ("Target", "slide" ++ show num ++ ".xml") - ] () - -- if it's an anchor not in the map, we return nothing. - '#' : _ -> return Nothing - -- Anything else we treat as an external link - _ -> - return $ Just $ - mknode "Relationship" [ ("Id", "rId" ++ show idNum) - , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink") - , ("Target", url) - , ("TargetMode", "External") - ] () - -linkRelElements :: PandocMonad m => M.Map Int (URL, String) -> P m [Element] -linkRelElements mp = catMaybes <$> mapM (\(n, lnk) -> linkRelElement n lnk) (M.toList mp) +linkRelElement :: PandocMonad m => Int -> LinkTarget -> P m Element +linkRelElement idNum (InternalTarget num) = do + return $ + mknode "Relationship" [ ("Id", "rId" ++ show idNum) + , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide") + , ("Target", "slide" ++ show num ++ ".xml") + ] () +linkRelElement idNum (ExternalTarget (url, _)) = do + return $ + mknode "Relationship" [ ("Id", "rId" ++ show idNum) + , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink") + , ("Target", url) + , ("TargetMode", "External") + ] () + +linkRelElements :: PandocMonad m => M.Map Int LinkTarget -> P m [Element] +linkRelElements mp = mapM (\(n, lnk) -> linkRelElement n lnk) (M.toList mp) mediaRelElement :: MediaInfo -> Element mediaRelElement mInfo = diff --git a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs index 68b2aeeb2..e68f5eb57 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs @@ -35,7 +35,6 @@ Presentation. module Text.Pandoc.Writers.Powerpoint.Presentation ( documentToPresentation , Presentation(..) , Slide(..) - , SlideElement(..) , Shape(..) , Graphic(..) , BulletType(..) @@ -50,6 +49,7 @@ module Text.Pandoc.Writers.Powerpoint.Presentation ( documentToPresentation , PicProps(..) , URL , TeXString(..) + , LinkTarget(..) ) where @@ -78,10 +78,6 @@ data WriterEnv = WriterEnv { envMetadata :: Meta , envInList :: Bool , envInNoteSlide :: Bool , envCurSlideId :: Int - -- the difference between the number at - -- the end of the slide file name and - -- the rId number - , envSlideIdOffset :: Int } deriving (Show) @@ -95,7 +91,6 @@ instance Default WriterEnv where , envInList = False , envInNoteSlide = False , envCurSlideId = 1 - , envSlideIdOffset = 1 } @@ -139,9 +134,6 @@ data Slide = MetadataSlide { metadataSlideTitle :: [ParaElem] } deriving (Show, Eq) -data SlideElement = SlideElement Pixels Pixels Pixels Pixels Shape - deriving (Show, Eq) - data Shape = Pic PicProps FilePath Text.Pandoc.Definition.Attr [ParaElem] | GraphicFrame [Graphic] [ParaElem] | TextBox [Paragraph] @@ -206,12 +198,16 @@ data Capitals = NoCapitals | SmallCapitals | AllCapitals type URL = String +data LinkTarget = ExternalTarget (URL, String) + | InternalTarget Int -- slideId + deriving (Show, Eq) + data RunProps = RunProps { rPropBold :: Bool , rPropItalics :: Bool , rStrikethrough :: Maybe Strikethrough , rBaseline :: Maybe Int , rCap :: Maybe Capitals - , rLink :: Maybe (URL, String) + , rLink :: Maybe LinkTarget , rPropCode :: Bool , rPropBlockQuote :: Bool , rPropForceSize :: Maybe Pixels @@ -229,7 +225,7 @@ instance Default RunProps where , rPropForceSize = Nothing } -data PicProps = PicProps { picPropLink :: Maybe (URL, String) +data PicProps = PicProps { picPropLink :: Maybe LinkTarget } deriving (Show, Eq) instance Default PicProps where @@ -267,7 +263,7 @@ inlineToParElems Space = inlineToParElems (Str " ") inlineToParElems SoftBreak = inlineToParElems (Str " ") inlineToParElems LineBreak = return [Break] inlineToParElems (Link _ ils (url, title)) = do - local (\r ->r{envRunProps = (envRunProps r){rLink = Just (url, title)}}) $ + local (\r ->r{envRunProps = (envRunProps r){rLink = Just $ ExternalTarget (url, title)}}) $ inlinesToParElems ils inlineToParElems (Code _ str) = do local (\r ->r{envRunProps = (envRunProps r){rPropCode = True}}) $ @@ -414,10 +410,10 @@ blockToShape (Para (il:_)) | Image attr ils (url, _) <- il = Pic def url attr <$> (inlinesToParElems ils) blockToShape (Plain (il:_)) | Link _ (il':_) target <- il , Image attr ils (url, _) <- il' = - Pic def{picPropLink = Just target} url attr <$> (inlinesToParElems ils) + Pic def{picPropLink = Just $ ExternalTarget target} url attr <$> (inlinesToParElems ils) blockToShape (Para (il:_)) | Link _ (il':_) target <- il , Image attr ils (url, _) <- il' = - Pic def{picPropLink = Just target} url attr <$> (inlinesToParElems ils) + Pic def{picPropLink = Just $ ExternalTarget target} url attr <$> (inlinesToParElems ils) blockToShape (Table caption algn _ hdrCells rows) = do caption' <- inlinesToParElems caption hdrCells' <- rowToParagraphs algn hdrCells @@ -644,6 +640,51 @@ combineParaElems' (Just pElem') (pElem : pElems) combineParaElems :: [ParaElem] -> [ParaElem] combineParaElems = combineParaElems' Nothing +applyToParagraph :: Monad m => (ParaElem -> m ParaElem) -> Paragraph -> m Paragraph +applyToParagraph f para = do + paraElems' <- mapM f $ paraElems para + return $ para {paraElems = paraElems'} + +applyToShape :: Monad m => (ParaElem -> m ParaElem) -> Shape -> m Shape +applyToShape f (Pic pPr fp attr pes) = do + pes' <- mapM f pes + return $ Pic pPr fp attr pes' +applyToShape f (GraphicFrame gfx pes) = do + pes' <- mapM f pes + return $ GraphicFrame gfx pes' +applyToShape f (TextBox paras) = do + paras' <- mapM (applyToParagraph f) paras + return $ TextBox paras' + +applyToSlide :: Monad m => (ParaElem -> m ParaElem) -> Slide -> m Slide +applyToSlide f (MetadataSlide title subtitle authors date) = do + title' <- mapM f title + subtitle' <- mapM f subtitle + authors' <- mapM (mapM f) authors + date' <- mapM f date + return $ MetadataSlide title' subtitle' authors' date' +applyToSlide f (TitleSlide title) = do + title' <- mapM f title + return $ TitleSlide title' +applyToSlide f (ContentSlide hdr content) = do + hdr' <- mapM f hdr + content' <- mapM (applyToShape f) content + return $ ContentSlide hdr' content' +applyToSlide f (TwoColumnSlide hdr contentL contentR) = do + hdr' <- mapM f hdr + contentL' <- mapM (applyToShape f) contentL + contentR' <- mapM (applyToShape f) contentR + return $ TwoColumnSlide hdr' contentL' contentR' + +replaceAnchor :: PandocMonad m => ParaElem -> Pres m ParaElem +replaceAnchor (Run rProps s) + | Just (ExternalTarget ('#':anchor, _)) <- rLink rProps = do + anchorMap <- gets stAnchorMap + return $ case M.lookup anchor anchorMap of + Just n -> Run (rProps{rLink = Just $ InternalTarget n}) s + Nothing -> Run rProps s +replaceAnchor pe = return pe + blocksToPresentation :: PandocMonad m => [Block] -> Pres m Presentation blocksToPresentation blks = do opts <- asks envOpts @@ -683,9 +724,10 @@ blocksToPresentation blks = do }) (blocksToSlide $ notesSlideBlocks) return [notesSlide] - return $ - Presentation $ - metadataslides ++ tocSlides ++ bodyslides ++ notesSlides + + let slides = metadataslides ++ tocSlides ++ bodyslides ++ notesSlides + slides' <- mapM (applyToSlide replaceAnchor) slides + return $ Presentation slides' documentToPresentation :: PandocMonad m => WriterOptions -- cgit v1.2.3 From a7d131cf442f6d93f1e3183d26b855ca5f7112af Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Mon, 15 Jan 2018 08:54:42 -0500 Subject: Powerpoint writer: Ignore anchor links to nowehere. We don't convert a '#target' ExternalTarget to an InternalTarget if `target` is not in the AnchorMap. We just remove the link. This prevents broken links in the Powerpoint output. --- src/Text/Pandoc/Writers/Powerpoint/Presentation.hs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs index e68f5eb57..5ced4e8a8 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs @@ -680,9 +680,12 @@ replaceAnchor :: PandocMonad m => ParaElem -> Pres m ParaElem replaceAnchor (Run rProps s) | Just (ExternalTarget ('#':anchor, _)) <- rLink rProps = do anchorMap <- gets stAnchorMap - return $ case M.lookup anchor anchorMap of - Just n -> Run (rProps{rLink = Just $ InternalTarget n}) s - Nothing -> Run rProps s + -- If the anchor is not in the anchormap, we just remove the + -- link. + let rProps' = case M.lookup anchor anchorMap of + Just n -> rProps{rLink = Just $ InternalTarget n} + Nothing -> rProps{rLink = Nothing} + return $ Run rProps' s replaceAnchor pe = return pe blocksToPresentation :: PandocMonad m => [Block] -> Pres m Presentation -- cgit v1.2.3 From b010113f3f63f5ca936942ba48a4ea823470ba8b Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Mon, 15 Jan 2018 10:01:59 -0500 Subject: Powerpoint writer: Move Presentation.hs out of PandocMonad We don't need it for anything but the log messages, and we can just keep track of that in state and pass it along to the `writePowerpoint` function. This will simplify the code. --- src/Text/Pandoc/Writers/Powerpoint.hs | 5 +- src/Text/Pandoc/Writers/Powerpoint/Presentation.hs | 69 +++++++++++----------- 2 files changed, 39 insertions(+), 35 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index 3d6b736f2..acb33f582 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -44,7 +44,7 @@ module Text.Pandoc.Writers.Powerpoint (writePowerpoint) where import Codec.Archive.Zip import Text.Pandoc.Definition import Text.Pandoc.Walk -import Text.Pandoc.Class (PandocMonad) +import Text.Pandoc.Class (PandocMonad, report) import Text.Pandoc.Options (WriterOptions) import Text.Pandoc.Writers.Shared (fixDisplayMath) import Text.Pandoc.Writers.Powerpoint.Presentation (documentToPresentation) @@ -57,6 +57,7 @@ writePowerpoint :: (PandocMonad m) -> m BL.ByteString writePowerpoint opts (Pandoc meta blks) = do let blks' = walk fixDisplayMath blks - pres <- documentToPresentation opts (Pandoc meta blks') + let (pres, logMsgs) = documentToPresentation opts (Pandoc meta blks') + mapM_ report logMsgs archv <- presentationToArchive opts pres return $ fromArchive archv diff --git a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs index 5ced4e8a8..3c5dd617d 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs @@ -58,9 +58,7 @@ import Control.Monad.State import Data.List (intercalate) import Data.Default import Text.Pandoc.Definition -import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Slides (getSlideLevel) -import qualified Text.Pandoc.Class as P import Text.Pandoc.Options import Text.Pandoc.Logging import Text.Pandoc.Walk @@ -97,17 +95,23 @@ instance Default WriterEnv where data WriterState = WriterState { stNoteIds :: M.Map Int [Block] -- associate anchors with slide id , stAnchorMap :: M.Map String Int + , stLog :: [LogMessage] } deriving (Show, Eq) instance Default WriterState where def = WriterState { stNoteIds = mempty - , stAnchorMap= mempty + , stAnchorMap = mempty + , stLog = [] } -type Pres m = ReaderT WriterEnv (StateT WriterState m) +addLogMessage :: LogMessage -> Pres () +addLogMessage msg = modify $ \st -> st{stLog = msg : (stLog st)} -runPres :: Monad m => WriterEnv -> WriterState -> Pres m a -> m a -runPres env st p = evalStateT (runReaderT p env) st +type Pres = ReaderT WriterEnv (State WriterState) + +runPres :: WriterEnv -> WriterState -> Pres a -> (a, [LogMessage]) +runPres env st p = (pres, reverse $ stLog finalSt) + where (pres, finalSt) = runState (runReaderT p env) st -- GHC 7.8 will still complain about concat <$> mapM unless we specify -- Functor. We can get rid of this when we stop supporting GHC 7.8. @@ -234,10 +238,10 @@ instance Default PicProps where -------------------------------------------------- -inlinesToParElems :: Monad m => [Inline] -> Pres m [ParaElem] +inlinesToParElems :: [Inline] -> Pres [ParaElem] inlinesToParElems ils = concatMapM inlineToParElems ils -inlineToParElems :: Monad m => Inline -> Pres m [ParaElem] +inlineToParElems :: Inline -> Pres [ParaElem] inlineToParElems (Str s) = do pr <- asks envRunProps return [Run pr s] @@ -288,7 +292,7 @@ isListType (BulletList _) = True isListType (DefinitionList _) = True isListType _ = False -registerAnchorId :: PandocMonad m => String -> Pres m () +registerAnchorId :: String -> Pres () registerAnchorId anchor = do anchorMap <- gets stAnchorMap slideId <- asks envCurSlideId @@ -302,7 +306,7 @@ blockQuoteSize = 20 noteSize :: Pixels noteSize = 18 -blockToParagraphs :: PandocMonad m => Block -> Pres m [Paragraph] +blockToParagraphs :: Block -> Pres [Paragraph] blockToParagraphs (Plain ils) = do parElems <- inlinesToParElems ils pProps <- asks envParaProps @@ -362,7 +366,7 @@ blockToParagraphs (OrderedList listAttr blksLst) = do }}) $ concatMapM multiParBullet blksLst blockToParagraphs (DefinitionList entries) = do - let go :: PandocMonad m => ([Inline], [[Block]]) -> Pres m [Paragraph] + let go :: ([Inline], [[Block]]) -> Pres [Paragraph] go (ils, blksLst) = do term <-blockToParagraphs $ Para [Strong ils] -- For now, we'll treat each definition term as a @@ -373,11 +377,11 @@ blockToParagraphs (DefinitionList entries) = do blockToParagraphs (Div (_, ("notes" : []), _) _) = return [] blockToParagraphs (Div _ blks) = concatMapM blockToParagraphs blks blockToParagraphs blk = do - P.report $ BlockNotRendered blk + addLogMessage $ BlockNotRendered blk return [] -- Make sure the bullet env gets turned off after the first para. -multiParBullet :: PandocMonad m => [Block] -> Pres m [Paragraph] +multiParBullet :: [Block] -> Pres [Paragraph] multiParBullet [] = return [] multiParBullet (b:bs) = do pProps <- asks envParaProps @@ -386,7 +390,7 @@ multiParBullet (b:bs) = do concatMapM blockToParagraphs bs return $ p ++ ps -cellToParagraphs :: PandocMonad m => Alignment -> TableCell -> Pres m [Paragraph] +cellToParagraphs :: Alignment -> TableCell -> Pres [Paragraph] cellToParagraphs algn tblCell = do paras <- mapM (blockToParagraphs) tblCell let alignment = case algn of @@ -397,13 +401,13 @@ cellToParagraphs algn tblCell = do paras' = map (map (\p -> p{paraProps = (paraProps p){pPropAlign = alignment}})) paras return $ concat paras' -rowToParagraphs :: PandocMonad m => [Alignment] -> [TableCell] -> Pres m [[Paragraph]] +rowToParagraphs :: [Alignment] -> [TableCell] -> Pres [[Paragraph]] rowToParagraphs algns tblCells = do -- We have to make sure we have the right number of alignments let pairs = zip (algns ++ repeat AlignDefault) tblCells mapM (\(a, tc) -> cellToParagraphs a tc) pairs -blockToShape :: PandocMonad m => Block -> Pres m Shape +blockToShape :: Block -> Pres Shape blockToShape (Plain (il:_)) | Image attr ils (url, _) <- il = Pic def url attr <$> (inlinesToParElems ils) blockToShape (Para (il:_)) | Image attr ils (url, _) <- il = @@ -441,7 +445,7 @@ combineShapes ((TextBox (p:ps)) : (TextBox (p':ps')) : ss) = combineShapes $ TextBox ((p:ps) ++ (p':ps')) : ss combineShapes (s:ss) = s : combineShapes ss -blocksToShapes :: PandocMonad m => [Block] -> Pres m [Shape] +blocksToShapes :: [Block] -> Pres [Shape] blocksToShapes blks = combineShapes <$> mapM blockToShape blks isImage :: Inline -> Bool @@ -449,7 +453,7 @@ isImage (Image _ _ _) = True isImage (Link _ ((Image _ _ _) : _) _) = True isImage _ = False -splitBlocks' :: Monad m => [Block] -> [[Block]] -> [Block] -> Pres m [[Block]] +splitBlocks' :: [Block] -> [[Block]] -> [Block] -> Pres [[Block]] splitBlocks' cur acc [] = return $ acc ++ (if null cur then [] else [cur]) splitBlocks' cur acc (HorizontalRule : blks) = splitBlocks' [] (acc ++ (if null cur then [] else [cur])) blks @@ -486,10 +490,10 @@ splitBlocks' cur acc (d@(Div (_, classes, _) _): blks) | "columns" `elem` classe _ -> splitBlocks' [] (acc ++ (if null cur then [] else [cur]) ++ [[d]]) blks splitBlocks' cur acc (blk : blks) = splitBlocks' (cur ++ [blk]) acc blks -splitBlocks :: Monad m => [Block] -> Pres m [[Block]] +splitBlocks :: [Block] -> Pres [[Block]] splitBlocks = splitBlocks' [] [] -blocksToSlide' :: PandocMonad m => Int -> [Block] -> Pres m Slide +blocksToSlide' :: Int -> [Block] -> Pres Slide blocksToSlide' lvl ((Header n (ident, _, _) ils) : blks) | n < lvl = do registerAnchorId ident @@ -511,9 +515,9 @@ blocksToSlide' _ (blk : blks) , (Div (_, clsL, _) blksL) : (Div (_, clsR, _) blksR) : remaining <- divBlks , "column" `elem` clsL, "column" `elem` clsR = do unless (null blks) - (mapM (P.report . BlockNotRendered) blks >> return ()) + (mapM (addLogMessage . BlockNotRendered) blks >> return ()) unless (null remaining) - (mapM (P.report . BlockNotRendered) remaining >> return ()) + (mapM (addLogMessage . BlockNotRendered) remaining >> return ()) mbSplitBlksL <- splitBlocks blksL mbSplitBlksR <- splitBlocks blksR let blksL' = case mbSplitBlksL of @@ -540,7 +544,7 @@ blocksToSlide' _ [] = return $ ContentSlide { contentSlideHeader = [] , contentSlideContent = [] } -blocksToSlide :: PandocMonad m => [Block] -> Pres m Slide +blocksToSlide :: [Block] -> Pres Slide blocksToSlide blks = do slideLevel <- asks envSlideLevel blocksToSlide' slideLevel blks @@ -553,14 +557,14 @@ makeNoteEntry n blks = (Para ils : blks') -> (Para $ enum : Space : ils) : blks' _ -> (Para [enum]) : blks -forceFontSize :: PandocMonad m => Pixels -> Pres m a -> Pres m a +forceFontSize :: Pixels -> Pres a -> Pres a forceFontSize px x = do rpr <- asks envRunProps local (\r -> r {envRunProps = rpr{rPropForceSize = Just px}}) x -- We leave these as blocks because we will want to include them in -- the TOC. -makeNotesSlideBlocks :: PandocMonad m => Pres m [Block] +makeNotesSlideBlocks :: Pres [Block] makeNotesSlideBlocks = do noteIds <- gets stNoteIds slideLevel <- asks envSlideLevel @@ -579,7 +583,7 @@ makeNotesSlideBlocks = do M.toList noteIds return $ hdr : blks -getMetaSlide :: PandocMonad m => Pres m (Maybe Slide) +getMetaSlide :: Pres (Maybe Slide) getMetaSlide = do meta <- asks envMetadata title <- inlinesToParElems $ docTitle meta @@ -600,7 +604,7 @@ getMetaSlide = do , metadataSlideDate = date } -- adapted from the markdown writer -elementToListItem :: PandocMonad m => Shared.Element -> Pres m [Block] +elementToListItem :: Shared.Element -> Pres [Block] elementToListItem (Shared.Sec lev _nums (ident,_,_) headerText subsecs) = do opts <- asks envOpts let headerLink = if null ident @@ -613,7 +617,7 @@ elementToListItem (Shared.Sec lev _nums (ident,_,_) headerText subsecs) = do return [Plain headerLink, BulletList listContents] elementToListItem (Shared.Blk _) = return [] -makeTOCSlide :: PandocMonad m => [Block] -> Pres m Slide +makeTOCSlide :: [Block] -> Pres Slide makeTOCSlide blks = do contents <- BulletList <$> mapM elementToListItem (Shared.hierarchicalize blks) meta <- asks envMetadata @@ -676,7 +680,7 @@ applyToSlide f (TwoColumnSlide hdr contentL contentR) = do contentR' <- mapM (applyToShape f) contentR return $ TwoColumnSlide hdr' contentL' contentR' -replaceAnchor :: PandocMonad m => ParaElem -> Pres m ParaElem +replaceAnchor :: ParaElem -> Pres ParaElem replaceAnchor (Run rProps s) | Just (ExternalTarget ('#':anchor, _)) <- rLink rProps = do anchorMap <- gets stAnchorMap @@ -688,7 +692,7 @@ replaceAnchor (Run rProps s) return $ Run rProps' s replaceAnchor pe = return pe -blocksToPresentation :: PandocMonad m => [Block] -> Pres m Presentation +blocksToPresentation :: [Block] -> Pres Presentation blocksToPresentation blks = do opts <- asks envOpts let metadataStartNum = 1 @@ -732,10 +736,9 @@ blocksToPresentation blks = do slides' <- mapM (applyToSlide replaceAnchor) slides return $ Presentation slides' -documentToPresentation :: PandocMonad m - => WriterOptions +documentToPresentation :: WriterOptions -> Pandoc - -> m Presentation + -> (Presentation, [LogMessage]) documentToPresentation opts (Pandoc meta blks) = do let env = def { envOpts = opts , envMetadata = meta -- cgit v1.2.3 From 4b7bc40e8ba5a0981bd6429f48fa6acdb21d5d69 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 15 Jan 2018 10:46:40 -0800 Subject: Renaming: Json -> JSON in modules and functions. --- src/Text/Pandoc/Filter.hs | 4 +- src/Text/Pandoc/Filter/JSON.hs | 97 +++++++++++++++++++++++++++++++++++++ src/Text/Pandoc/Filter/Json.hs | 97 ------------------------------------- src/Text/Pandoc/Lua/Module/Utils.hs | 10 ++-- 4 files changed, 104 insertions(+), 104 deletions(-) create mode 100644 src/Text/Pandoc/Filter/JSON.hs delete mode 100644 src/Text/Pandoc/Filter/Json.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/Filter.hs b/src/Text/Pandoc/Filter.hs index 30c99cc28..67b3a5f2c 100644 --- a/src/Text/Pandoc/Filter.hs +++ b/src/Text/Pandoc/Filter.hs @@ -39,7 +39,7 @@ import Data.Foldable (foldrM) import Text.Pandoc.Class (PandocIO) import Text.Pandoc.Definition (Pandoc) import Text.Pandoc.Options (ReaderOptions) -import qualified Text.Pandoc.Filter.Json as JsonFilter +import qualified Text.Pandoc.Filter.JSON as JSONFilter import qualified Text.Pandoc.Filter.Lua as LuaFilter data Filter = LuaFilter FilePath @@ -54,7 +54,7 @@ applyFilters :: ReaderOptions applyFilters ropts filters args d = do foldrM ($) d $ map applyFilter filters where - applyFilter (JSONFilter f) = JsonFilter.apply ropts args f + applyFilter (JSONFilter f) = JSONFilter.apply ropts args f applyFilter (LuaFilter f) = LuaFilter.apply ropts args f $(deriveJSON defaultOptions ''Filter) diff --git a/src/Text/Pandoc/Filter/JSON.hs b/src/Text/Pandoc/Filter/JSON.hs new file mode 100644 index 000000000..5772c2c41 --- /dev/null +++ b/src/Text/Pandoc/Filter/JSON.hs @@ -0,0 +1,97 @@ +{- +Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu> + +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.Filter + Copyright : Copyright (C) 2006-2018 John MacFarlane + License : GNU GPL, version 2 or above + + Maintainer : John MacFarlane <jgm@berkeley@edu> + Stability : alpha + Portability : portable + +Programmatically modifications of pandoc documents via JSON filters. +-} +module Text.Pandoc.Filter.JSON (apply) where + +import Control.Monad (unless, when) +import Control.Monad.Trans (MonadIO (liftIO)) +import Data.Aeson (eitherDecode', encode) +import Data.Char (toLower) +import Data.Maybe (isNothing) +import System.Directory (executable, doesFileExist, findExecutable, + getPermissions) +import System.Environment (getEnvironment) +import System.Exit (ExitCode (..)) +import System.FilePath ((</>), takeExtension) +import Text.Pandoc.Class (PandocIO) +import Text.Pandoc.Error (PandocError (PandocFilterError)) +import Text.Pandoc.Definition (Pandoc) +import Text.Pandoc.Filter.Path (expandFilterPath) +import Text.Pandoc.Options (ReaderOptions) +import Text.Pandoc.Process (pipeProcess) +import Text.Pandoc.Shared (pandocVersion) +import qualified Control.Exception as E +import qualified Text.Pandoc.UTF8 as UTF8 + +apply :: ReaderOptions + -> [String] + -> FilePath + -> Pandoc + -> PandocIO Pandoc +apply ropts args f d = do + f' <- expandFilterPath f + liftIO $ externalFilter ropts f' args d + +externalFilter :: MonadIO m + => ReaderOptions -> FilePath -> [String] -> Pandoc -> m Pandoc +externalFilter ropts f args' d = liftIO $ do + exists <- doesFileExist f + isExecutable <- if exists + then executable <$> getPermissions f + else return True + let (f', args'') = if exists + then case map toLower (takeExtension f) of + _ | isExecutable -> ("." </> f, args') + ".py" -> ("python", f:args') + ".hs" -> ("runhaskell", f:args') + ".pl" -> ("perl", f:args') + ".rb" -> ("ruby", f:args') + ".php" -> ("php", f:args') + ".js" -> ("node", f:args') + ".r" -> ("Rscript", f:args') + _ -> (f, args') + else (f, args') + unless (exists && isExecutable) $ do + mbExe <- findExecutable f' + when (isNothing mbExe) $ + E.throwIO $ PandocFilterError f ("Could not find executable " ++ f') + env <- getEnvironment + let env' = Just + ( ("PANDOC_VERSION", pandocVersion) + : ("PANDOC_READER_OPTIONS", UTF8.toStringLazy (encode ropts)) + : env ) + (exitcode, outbs) <- E.handle filterException $ + pipeProcess env' f' args'' $ encode d + case exitcode of + ExitSuccess -> either (E.throwIO . PandocFilterError f) + return $ eitherDecode' outbs + ExitFailure ec -> E.throwIO $ PandocFilterError f + ("Filter returned error status " ++ show ec) + where filterException :: E.SomeException -> IO a + filterException e = E.throwIO $ PandocFilterError f (show e) diff --git a/src/Text/Pandoc/Filter/Json.hs b/src/Text/Pandoc/Filter/Json.hs deleted file mode 100644 index 681c52720..000000000 --- a/src/Text/Pandoc/Filter/Json.hs +++ /dev/null @@ -1,97 +0,0 @@ -{- -Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu> - -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.Filter - Copyright : Copyright (C) 2006-2018 John MacFarlane - License : GNU GPL, version 2 or above - - Maintainer : John MacFarlane <jgm@berkeley@edu> - Stability : alpha - Portability : portable - -Programmatically modifications of pandoc documents via JSON filters. --} -module Text.Pandoc.Filter.Json (apply) where - -import Control.Monad (unless, when) -import Control.Monad.Trans (MonadIO (liftIO)) -import Data.Aeson (eitherDecode', encode) -import Data.Char (toLower) -import Data.Maybe (isNothing) -import System.Directory (executable, doesFileExist, findExecutable, - getPermissions) -import System.Environment (getEnvironment) -import System.Exit (ExitCode (..)) -import System.FilePath ((</>), takeExtension) -import Text.Pandoc.Class (PandocIO) -import Text.Pandoc.Error (PandocError (PandocFilterError)) -import Text.Pandoc.Definition (Pandoc) -import Text.Pandoc.Filter.Path (expandFilterPath) -import Text.Pandoc.Options (ReaderOptions) -import Text.Pandoc.Process (pipeProcess) -import Text.Pandoc.Shared (pandocVersion) -import qualified Control.Exception as E -import qualified Text.Pandoc.UTF8 as UTF8 - -apply :: ReaderOptions - -> [String] - -> FilePath - -> Pandoc - -> PandocIO Pandoc -apply ropts args f d = do - f' <- expandFilterPath f - liftIO $ externalFilter ropts f' args d - -externalFilter :: MonadIO m - => ReaderOptions -> FilePath -> [String] -> Pandoc -> m Pandoc -externalFilter ropts f args' d = liftIO $ do - exists <- doesFileExist f - isExecutable <- if exists - then executable <$> getPermissions f - else return True - let (f', args'') = if exists - then case map toLower (takeExtension f) of - _ | isExecutable -> ("." </> f, args') - ".py" -> ("python", f:args') - ".hs" -> ("runhaskell", f:args') - ".pl" -> ("perl", f:args') - ".rb" -> ("ruby", f:args') - ".php" -> ("php", f:args') - ".js" -> ("node", f:args') - ".r" -> ("Rscript", f:args') - _ -> (f, args') - else (f, args') - unless (exists && isExecutable) $ do - mbExe <- findExecutable f' - when (isNothing mbExe) $ - E.throwIO $ PandocFilterError f ("Could not find executable " ++ f') - env <- getEnvironment - let env' = Just - ( ("PANDOC_VERSION", pandocVersion) - : ("PANDOC_READER_OPTIONS", UTF8.toStringLazy (encode ropts)) - : env ) - (exitcode, outbs) <- E.handle filterException $ - pipeProcess env' f' args'' $ encode d - case exitcode of - ExitSuccess -> either (E.throwIO . PandocFilterError f) - return $ eitherDecode' outbs - ExitFailure ec -> E.throwIO $ PandocFilterError f - ("Filter returned error status " ++ show ec) - where filterException :: E.SomeException -> IO a - filterException e = E.throwIO $ PandocFilterError f (show e) diff --git a/src/Text/Pandoc/Lua/Module/Utils.hs b/src/Text/Pandoc/Lua/Module/Utils.hs index ab29cc0c7..f8eb96dc7 100644 --- a/src/Text/Pandoc/Lua/Module/Utils.hs +++ b/src/Text/Pandoc/Lua/Module/Utils.hs @@ -40,7 +40,7 @@ import Text.Pandoc.Lua.Util (addFunction, popValue) import qualified Data.Digest.Pure.SHA as SHA import qualified Data.ByteString.Lazy as BSL import qualified Foreign.Lua as Lua -import qualified Text.Pandoc.Filter.Json as JsonFilter +import qualified Text.Pandoc.Filter.JSON as JSONFilter import qualified Text.Pandoc.Shared as Shared -- | Push the "pandoc.utils" module to the lua stack. @@ -49,7 +49,7 @@ pushModule mbDatadir = do Lua.newtable addFunction "hierarchicalize" hierarchicalize addFunction "normalize_date" normalizeDate - addFunction "run_json_filter" (runJsonFilter mbDatadir) + addFunction "run_json_filter" (runJSONFilter mbDatadir) addFunction "sha1" sha1 addFunction "stringify" stringify addFunction "to_roman_numeral" toRomanNumeral @@ -67,12 +67,12 @@ normalizeDate :: String -> Lua (Lua.Optional String) normalizeDate = return . Lua.Optional . Shared.normalizeDate -- | Run a JSON filter on the given document. -runJsonFilter :: Maybe FilePath +runJSONFilter :: Maybe FilePath -> Pandoc -> FilePath -> Lua.Optional [String] -> Lua NumResults -runJsonFilter mbDatadir doc filterFile optArgs = do +runJSONFilter mbDatadir doc filterFile optArgs = do args <- case Lua.fromOptional optArgs of Just x -> return x Nothing -> do @@ -80,7 +80,7 @@ runJsonFilter mbDatadir doc filterFile optArgs = do (:[]) <$> popValue filterRes <- Lua.liftIO . runIO $ do setUserDataDir mbDatadir - JsonFilter.apply def args filterFile doc + JSONFilter.apply def args filterFile doc case filterRes of Left err -> Lua.raiseError (show err) Right d -> (1 :: NumResults) <$ Lua.push d -- cgit v1.2.3 From f79a6f11631f9e26b7a4113f846b22b34593f190 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Mon, 15 Jan 2018 14:48:35 -0500 Subject: Powerpoint writer: Import reference-doc images properly. There was a glob error that was leading to images from the reference-doc pptx not being imported. We don't need a glob here -- just replace it with `isPrefixOf`. --- src/Text/Pandoc/Writers/Powerpoint/Output.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint/Output.hs b/src/Text/Pandoc/Writers/Powerpoint/Output.hs index c45479579..540df6afa 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Output.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Output.hs @@ -38,7 +38,7 @@ import Control.Monad.Except (throwError, catchError) import Control.Monad.Reader import Control.Monad.State import Codec.Archive.Zip -import Data.List (intercalate, stripPrefix, nub, union) +import Data.List (intercalate, stripPrefix, nub, union, isPrefixOf) import Data.Default import Data.Time.Clock (UTCTime) import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds, posixSecondsToUTCTime) @@ -68,7 +68,7 @@ import Text.Pandoc.Writers.Powerpoint.Presentation initialGlobalIds :: Archive -> Archive -> M.Map FilePath Int initialGlobalIds refArchive distArchive = let archiveFiles = filesInArchive refArchive `union` filesInArchive distArchive - mediaPaths = filter (match (compile "ppt/media/image")) archiveFiles + mediaPaths = filter (isPrefixOf "ppt/media/image") archiveFiles go :: FilePath -> Maybe (FilePath, Int) go fp = do -- cgit v1.2.3 From e408ae6278482b67c8995ebbc7239c30bdf480d1 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Mon, 15 Jan 2018 15:35:06 -0500 Subject: Powerpoint writer: Make our own _rels/.rels file. The toplevel .rels file could have a thumbnail image if taken from the template. Rather than removing it from the inherited file, it's easier to just make our own. --- src/Text/Pandoc/Writers/Powerpoint/Output.hs | 64 +++++++++++++++++++--------- 1 file changed, 43 insertions(+), 21 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint/Output.hs b/src/Text/Pandoc/Writers/Powerpoint/Output.hs index 540df6afa..68f3991ea 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Output.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Output.hs @@ -163,8 +163,7 @@ copyFileToArchive arch fp = do Just e -> return $ addEntryToArchive e arch inheritedPatterns :: [Pattern] -inheritedPatterns = map compile [ "_rels/.rels" - , "docProps/app.xml" +inheritedPatterns = map compile [ "docProps/app.xml" , "docProps/core.xml" , "ppt/slideLayouts/slideLayout*.xml" , "ppt/slideLayouts/_rels/slideLayout*.xml.rels" @@ -192,24 +191,23 @@ patternsToFilePaths pats = concat <$> mapM patternToFilePaths pats -- Here are the files we'll require to make a Powerpoint document. If -- any of these are missing, we should error out of our build. requiredFiles :: [FilePath] -requiredFiles = [ "_rels/.rels" - , "docProps/app.xml" - , "docProps/core.xml" - , "ppt/presProps.xml" - , "ppt/slideLayouts/slideLayout1.xml" - , "ppt/slideLayouts/_rels/slideLayout1.xml.rels" - , "ppt/slideLayouts/slideLayout2.xml" - , "ppt/slideLayouts/_rels/slideLayout2.xml.rels" - , "ppt/slideLayouts/slideLayout3.xml" - , "ppt/slideLayouts/_rels/slideLayout3.xml.rels" - , "ppt/slideLayouts/slideLayout4.xml" - , "ppt/slideLayouts/_rels/slideLayout4.xml.rels" - , "ppt/slideMasters/slideMaster1.xml" - , "ppt/slideMasters/_rels/slideMaster1.xml.rels" - , "ppt/theme/theme1.xml" - , "ppt/viewProps.xml" - , "ppt/tableStyles.xml" - ] +requiredFiles = [ "docProps/app.xml" + , "docProps/core.xml" + , "ppt/presProps.xml" + , "ppt/slideLayouts/slideLayout1.xml" + , "ppt/slideLayouts/_rels/slideLayout1.xml.rels" + , "ppt/slideLayouts/slideLayout2.xml" + , "ppt/slideLayouts/_rels/slideLayout2.xml.rels" + , "ppt/slideLayouts/slideLayout3.xml" + , "ppt/slideLayouts/_rels/slideLayout3.xml.rels" + , "ppt/slideLayouts/slideLayout4.xml" + , "ppt/slideLayouts/_rels/slideLayout4.xml.rels" + , "ppt/slideMasters/slideMaster1.xml" + , "ppt/slideMasters/_rels/slideMaster1.xml.rels" + , "ppt/theme/theme1.xml" + , "ppt/viewProps.xml" + , "ppt/tableStyles.xml" + ] presentationToArchiveP :: PandocMonad m => Presentation -> P m Archive @@ -226,6 +224,9 @@ presentationToArchiveP p@(Presentation slides) = do ) newArch' <- foldM copyFileToArchive emptyArchive filePaths + -- we make this ourself in case there's something unexpected in the + -- one in the reference doc. + relsEntry <- topLevelRelsEntry -- presentation entry and rels. We have to do the rels first to make -- sure we know the correct offset for the rIds. presEntry <- presentationToPresEntry p @@ -241,7 +242,7 @@ presentationToArchiveP p@(Presentation slides) = do slideEntries ++ slideRelEntries ++ mediaEntries ++ - [contentTypesEntry, presEntry, presRelsEntry] + [contentTypesEntry, relsEntry, presEntry, presRelsEntry] presentationToArchive :: PandocMonad m => WriterOptions -> Presentation -> m Archive presentationToArchive opts pres = do @@ -1147,6 +1148,27 @@ presentationToRels (Presentation slides) = do return $ mySlideRels ++ relsWithoutSlides' +-- We make this ourselves, in case there's a thumbnail in the one from +-- the template. +topLevelRels :: [Relationship] +topLevelRels = + [ Relationship { relId = 1 + , relType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" + , relTarget = "ppt/presentation.xml" + } + , Relationship { relId = 2 + , relType = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" + , relTarget = "docProps/core.xml" + } + , Relationship { relId = 3 + , relType = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/extended-properties" + , relTarget = "docProps/app.xml" + } + ] + +topLevelRelsEntry :: PandocMonad m => P m Entry +topLevelRelsEntry = elemToEntry "_rels/.rels" $ relsToElement topLevelRels + relToElement :: Relationship -> Element relToElement rel = mknode "Relationship" [ ("Id", "rId" ++ (show $ relId rel)) , ("Type", relType rel) -- cgit v1.2.3 From c6a55f8e9f189d2cda279ff2ee5f17e516490942 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Mon, 15 Jan 2018 15:51:58 -0500 Subject: Powerpoint writer: Improve table placement. --- src/Text/Pandoc/Writers/Powerpoint/Output.hs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint/Output.hs b/src/Text/Pandoc/Writers/Powerpoint/Output.hs index 68f3991ea..95dccb655 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Output.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Output.hs @@ -842,9 +842,6 @@ shapesToElements :: PandocMonad m => Element -> [Shape] -> P m [Element] shapesToElements layout shps = do concat <$> mapM (shapeToElements layout) shps -hardcodedTableMargin :: Integer -hardcodedTableMargin = 36 - graphicFrameToElements :: PandocMonad m => Element -> [Graphic] -> [ParaElem] -> P m [Element] graphicFrameToElements layout tbls caption = do -- get the sizing @@ -857,7 +854,7 @@ graphicFrameToElements layout tbls caption = do let cy = if (not $ null caption) then cytmp - captionHeight else cytmp - elements <- mapM graphicToElement tbls + elements <- mapM (graphicToElement cx) tbls let graphicFrameElts = mknode "p:graphicFrame" [] $ [ mknode "p:nvGraphicFramePr" [] $ @@ -878,20 +875,19 @@ graphicFrameToElements layout tbls caption = do return [graphicFrameElts, capElt] else return [graphicFrameElts] -graphicToElement :: PandocMonad m => Graphic -> P m Element -graphicToElement (Tbl tblPr hdrCells rows) = do - (pageWidth, _) <- asks envPresentationSize +graphicToElement :: PandocMonad m => Integer -> Graphic -> P m Element +graphicToElement tableWidth (Tbl tblPr hdrCells rows) = do let colWidths = if null hdrCells then case rows of r : _ | not (null r) -> replicate (length r) $ - (pageWidth - (2 * hardcodedTableMargin))`div` (toInteger $ length r) + (tableWidth `div` (toInteger $ length r)) -- satisfy the compiler. This is the same as -- saying that rows is empty, but the compiler -- won't understand that `[]` exhausts the -- alternatives. _ -> [] else replicate (length hdrCells) $ - (pageWidth - (2 * hardcodedTableMargin)) `div` (toInteger $ length hdrCells) + (tableWidth `div` (toInteger $ length hdrCells)) let cellToOpenXML paras = do elements <- mapM paragraphToElement paras -- cgit v1.2.3 From 6910267abfa7d5a1743589d301e7b9ecf2a54e4f Mon Sep 17 00:00:00 2001 From: Henri Menke <henri@icp.uni-stuttgart.de> Date: Tue, 16 Jan 2018 14:38:33 +1300 Subject: ConTeXt writer: Use xtables instead of Tables (#4223) - Default to xtables for context output. - Added `ntb` extension (affecting context writer only) to use Natural Tables instead. - Added `Ext_ntb` constructor to `Extension` (API change). --- src/Text/Pandoc/Extensions.hs | 1 + src/Text/Pandoc/Writers/ConTeXt.hs | 98 ++++++++++++++++++++++++++++---------- 2 files changed, 73 insertions(+), 26 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index cb3490cf7..8f6d49ade 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -133,6 +133,7 @@ data Extension = | Ext_multiline_tables -- ^ Pandoc-style multiline tables | Ext_native_divs -- ^ Use Div blocks for contents of <div> tags | Ext_native_spans -- ^ Use Span inlines for contents of <span> + | Ext_ntb -- ^ ConTeXt Natural Tables | Ext_old_dashes -- ^ -- = em, - before number = en | Ext_pandoc_title_block -- ^ Pandoc title block | Ext_pipe_tables -- ^ Pipe tables (as in PHP markdown extra) diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index 072c2ca8d..64b7d2c53 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -55,6 +55,8 @@ data WriterState = , stOptions :: WriterOptions -- writer options } +data Tabl = Xtb | Ntb deriving (Show, Eq) + orderedListStyles :: [Char] orderedListStyles = cycle "narg" @@ -252,33 +254,77 @@ blockToConTeXt HorizontalRule = return $ "\\thinrule" <> blankline -- If this is ever executed, provide a default for the reference identifier. blockToConTeXt (Header level attr lst) = sectionHeader attr level lst blockToConTeXt (Table caption aligns widths heads rows) = do - let colDescriptor colWidth alignment = (case alignment of - AlignLeft -> 'l' - AlignRight -> 'r' - AlignCenter -> 'c' - AlignDefault -> 'l'): - if colWidth == 0 - then "|" - else ("p(" ++ printf "%.2f" colWidth ++ "\\textwidth)|") - let colDescriptors = "|" ++ concat ( - zipWith colDescriptor widths aligns) - headers <- if all null heads - then return empty - else liftM ($$ "\\HL") $ tableRowToConTeXt heads + opts <- gets stOptions + let tabl = if isEnabled Ext_ntb opts + then Ntb + else Xtb captionText <- inlineListToConTeXt caption - rows' <- mapM tableRowToConTeXt rows - return $ "\\placetable" <> (if null caption - then brackets "none" - else empty) - <> braces captionText $$ - "\\starttable" <> brackets (text colDescriptors) $$ - "\\HL" $$ headers $$ - vcat rows' $$ "\\HL" $$ "\\stoptable" <> blankline - -tableRowToConTeXt :: PandocMonad m => [[Block]] -> WM m Doc -tableRowToConTeXt cols = do - cols' <- mapM blockListToConTeXt cols - return $ vcat (map ("\\NC " <>) cols') $$ "\\NC\\AR" + headers <- if all null heads + then return empty + else tableRowToConTeXt tabl aligns widths heads + rows' <- mapM (tableRowToConTeXt tabl aligns widths) rows + body <- tableToConTeXt tabl headers rows' + return $ "\\startplacetable" <> brackets ( + if null caption + then "location=none" + else "caption=" <> braces captionText + ) $$ body $$ "\\stopplacetable" <> blankline + +tableToConTeXt :: PandocMonad m => Tabl -> Doc -> [Doc] -> WM m Doc +tableToConTeXt Xtb heads rows = + return $ "\\startxtable" $$ + (if isEmpty heads + then empty + else "\\startxtablehead[head]" $$ heads $$ "\\stopxtablehead") $$ + (if null rows + then empty + else "\\startxtablebody[body]" $$ vcat (init rows) $$ "\\stopxtablebody" $$ + "\\startxtablefoot[foot]" $$ last rows $$ "\\stopxtablefoot") $$ + "\\stopxtable" +tableToConTeXt Ntb heads rows = + return $ "\\startTABLE" $$ + (if isEmpty heads + then empty + else "\\startTABLEhead" $$ heads $$ "\\stopTABLEhead") $$ + (if null rows + then empty + else "\\startTABLEbody" $$ vcat (init rows) $$ "\\stopTABLEbody" $$ + "\\startTABLEfoot" $$ last rows $$ "\\stopTABLEfoot") $$ + "\\stopTABLE" + +tableRowToConTeXt :: PandocMonad m => Tabl -> [Alignment] -> [Double] -> [[Block]] -> WM m Doc +tableRowToConTeXt Xtb aligns widths cols = do + cells <- mapM (tableColToConTeXt Xtb) $ zip3 aligns widths cols + return $ "\\startxrow" $$ vcat cells $$ "\\stopxrow" +tableRowToConTeXt Ntb aligns widths cols = do + cells <- mapM (tableColToConTeXt Ntb) $ zip3 aligns widths cols + return $ vcat cells $$ "\\NC\\NR" + +tableColToConTeXt :: PandocMonad m => Tabl -> (Alignment, Double, [Block]) -> WM m Doc +tableColToConTeXt tabl (align, width, blocks) = do + cellContents <- blockListToConTeXt blocks + let colwidth = if width == 0 + then empty + else "width=" <> braces (text (printf "%.2f\\textwidth" width)) + let halign = alignToConTeXt align + let options = (if keys == empty + then empty + else brackets keys) <> space + where keys = hcat $ intersperse "," $ filter (empty /=) [halign, colwidth] + tableCellToConTeXt tabl options cellContents + +tableCellToConTeXt :: PandocMonad m => Tabl -> Doc -> Doc -> WM m Doc +tableCellToConTeXt Xtb options cellContents = + return $ "\\startxcell" <> options <> cellContents <> " \\stopxcell" +tableCellToConTeXt Ntb options cellContents = + return $ "\\NC" <> options <> cellContents + +alignToConTeXt :: Alignment -> Doc +alignToConTeXt align = case align of + AlignLeft -> "align=right" + AlignRight -> "align=left" + AlignCenter -> "align=middle" + AlignDefault -> empty listItemToConTeXt :: PandocMonad m => [Block] -> WM m Doc listItemToConTeXt list = blockListToConTeXt list >>= -- cgit v1.2.3 From f5f0b76636dcf568d6afa53783fb8dc2d36c74b3 Mon Sep 17 00:00:00 2001 From: n3fariox <n3fariox@gmail.com> Date: Mon, 15 Jan 2018 23:46:12 -0500 Subject: HTML reader: Fix col width parsing for percentages < 10% (#4262) Rather than take user input, and place a "0." in front, actually calculate the percentage to catch cases where small column sizes (e.g. `2%`) are needed. --- src/Text/Pandoc/Readers/HTML.hs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index e8dd9ec11..f15bf1c96 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -531,15 +531,18 @@ pCol = try $ do skipMany pBlank optional $ pSatisfy (matchTagClose "col") skipMany pBlank - return $ case lookup "width" attribs of + let width = case lookup "width" attribs of Nothing -> case lookup "style" attribs of Just ('w':'i':'d':'t':'h':':':xs) | '%' `elem` xs -> - fromMaybe 0.0 $ safeRead ('0':'.':filter + fromMaybe 0.0 $ safeRead (filter (`notElem` (" \t\r\n%'\";" :: [Char])) xs) _ -> 0.0 Just x | not (null x) && last x == '%' -> - fromMaybe 0.0 $ safeRead ('0':'.':init x) + fromMaybe 0.0 $ safeRead (init x) _ -> 0.0 + if width > 0.0 + then return $ width / 100.0 + else return 0.0 pColgroup :: PandocMonad m => TagParser m [Double] pColgroup = try $ do -- cgit v1.2.3 From b2268b1fc743904903cec5e1a78bc1fe10ed62b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=BCtz?= <robert.schuetz@stud.uni-heidelberg.de> Date: Tue, 16 Jan 2018 16:34:39 +0100 Subject: LaTeX writer: escape & in lstinline --- src/Text/Pandoc/Writers/LaTeX.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index 49b39f014..de2cc3480 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -1015,7 +1015,7 @@ inlineToLaTeX (Code (_,classes,_) str) = do let chr = case "!\"&'()*,-./:;?@_" \\ str of (c:_) -> c [] -> '!' - let str' = escapeStringUsing (backslashEscapes "\\{}%~_") str + let str' = escapeStringUsing (backslashEscapes "\\{}%~_&") str -- we always put lstinline in a dummy 'passthrough' command -- (defined in the default template) so that we don't have -- to change the way we escape characters depending on whether -- cgit v1.2.3 From 404706d29a8f45b43ef2ef13e93d1786dde863a0 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Tue, 16 Jan 2018 10:45:45 -0500 Subject: Docx reader: Parse fldChar tags This will allow us to parse instrTxt inside fldChar tags. --- src/Text/Pandoc/Readers/Docx.hs | 3 ++ src/Text/Pandoc/Readers/Docx/Parse.hs | 86 +++++++++++++++++++++++++++++++++-- 2 files changed, 84 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index e0f32b908..998179d2f 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -446,6 +446,9 @@ parPartToInlines' (PlainOMath exps) = return $ math $ writeTeX exps parPartToInlines' (SmartTag runs) = do smushInlines <$> mapM runToInlines runs +parPartToInlines' (Field _ runs) = do + smushInlines <$> mapM runToInlines runs +parPartToInlines' NullParPart = return mempty isAnchorSpan :: Inline -> Bool isAnchorSpan (Span (_, classes, kvs) _) = diff --git a/src/Text/Pandoc/Readers/Docx/Parse.hs b/src/Text/Pandoc/Readers/Docx/Parse.hs index b79b39369..b3a0fee8e 100644 --- a/src/Text/Pandoc/Readers/Docx/Parse.hs +++ b/src/Text/Pandoc/Readers/Docx/Parse.hs @@ -90,10 +90,19 @@ data ReaderEnv = ReaderEnv { envNotes :: Notes } deriving Show -data ReaderState = ReaderState { stateWarnings :: [String] } +data ReaderState = ReaderState { stateWarnings :: [String] + , stateFldCharState :: FldCharState + } deriving Show -data DocxError = DocxError | WrongElem +data FldCharState = FldCharOpen + | FldCharFieldInfo FieldInfo + | FldCharContent FieldInfo [Run] + | FldCharClosed + deriving (Show) + +data DocxError = DocxError + | WrongElem deriving Show type D = ExceptT DocxError (ReaderT ReaderEnv (State ReaderState)) @@ -265,6 +274,9 @@ data ParPart = PlainRun Run | Chart -- placeholder for now | PlainOMath [Exp] | SmartTag [Run] + | Field FieldInfo [Run] + | NullParPart -- when we need to return nothing, but + -- not because of an error. deriving Show data Run = Run RunStyle [RunElem] @@ -274,6 +286,10 @@ data Run = Run RunStyle [RunElem] | InlineChart -- placeholder deriving Show +data FieldInfo = HyperlinkField URL + | UnknownField + deriving (Show) + data RunElem = TextRun String | LnBrk | Tab | SoftHyphen | NoBreakHyphen deriving Show @@ -328,7 +344,9 @@ archiveToDocxWithWarnings archive = do (styles, parstyles) = archiveToStyles archive rEnv = ReaderEnv notes comments numbering rels media Nothing styles parstyles InDocument - rState = ReaderState { stateWarnings = [] } + rState = ReaderState { stateWarnings = [] + , stateFldCharState = FldCharClosed + } (eitherDoc, st) = runD (archiveToDocument archive) rEnv rState case eitherDoc of Right doc -> Right (Docx doc, stateWarnings st) @@ -736,9 +754,67 @@ elemToParPart ns element , c_ns <- "http://schemas.openxmlformats.org/drawingml/2006/chart" , Just _ <- findElement (QName "chart" (Just c_ns) (Just "c")) drawingElem = return Chart +{- +The next one is a bit complicated. fldChar fields work by first +having a <w:fldChar fldCharType="begin"> in a run, then a run with +<w:instrText>, then a <w:fldChar fldCharType="separate"> run, then the +content runs, and finally a <w:fldChar fldCharType="end"> run. For +example (omissions and my comments in brackets): + + <w:r> + [...] + <w:fldChar w:fldCharType="begin"/> + </w:r> + <w:r> + [...] + <w:instrText xml:space="preserve"> HYPERLINK [hyperlink url] </w:instrText> + </w:r> + <w:r> + [...] + <w:fldChar w:fldCharType="separate"/> + </w:r> + <w:r w:rsidRPr=[...]> + [...] + <w:t>Foundations of Analysis, 2nd Edition</w:t> + </w:r> + <w:r> + [...] + <w:fldChar w:fldCharType="end"/> + </w:r> + +So we do this in a number of steps. If we encounter the fldchar begin +tag, we start open a fldchar state variable (see state above). We add +the instrtext to it as FieldInfo. Then we close that and start adding +the runs when we get to separate. Then when we get to end, we produce +the Field type with approriate FieldInfo and Runs. +-} +elemToParPart ns element + | isElem ns "w" "r" element + , Just fldChar <- findChildByName ns "w" "fldChar" element + , Just fldCharType <- findAttrByName ns "w" "fldCharType" fldChar = do + fldCharState <- gets stateFldCharState + case fldCharState of + FldCharClosed | fldCharType == "begin" -> do + modify $ \st -> st {stateFldCharState = FldCharOpen} + return NullParPart + FldCharFieldInfo info | fldCharType == "separate" -> do + modify $ \st -> st {stateFldCharState = FldCharContent info []} + return NullParPart + FldCharContent info runs | fldCharType == "end" -> do + modify $ \st -> st {stateFldCharState = FldCharClosed} + return $ Field info runs + _ -> throwError WrongElem elemToParPart ns element - | isElem ns "w" "r" element = - elemToRun ns element >>= (\r -> return $ PlainRun r) + | isElem ns "w" "r" element = do + run <- elemToRun ns element + -- we check to see if we have an open FldChar in state that we're + -- recording. + fldCharState <- gets stateFldCharState + case fldCharState of + FldCharContent info runs -> do + modify $ \st -> st{stateFldCharState = FldCharContent info (run : runs)} + return NullParPart + _ -> return $ PlainRun run elemToParPart ns element | Just change <- getTrackedChange ns element = do runs <- mapD (elemToRun ns) (elChildren element) -- cgit v1.2.3 From ae8c0cdba82c9cfb847025c3f5ca410407b0fb96 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Tue, 16 Jan 2018 12:50:37 -0500 Subject: Docx reader: Parse instrText info in fldChar tags. We introduce a new module, Text.Pandoc.Readers.Docx.Fields which contains a simple parsec parser. At the moment, only simple hyperlink fields are accepted, but that can be extended in the future. --- src/Text/Pandoc/Readers/Docx/Fields.hs | 89 ++++++++++++++++++++++++++++++++++ src/Text/Pandoc/Readers/Docx/Parse.hs | 18 +++++-- 2 files changed, 102 insertions(+), 5 deletions(-) create mode 100644 src/Text/Pandoc/Readers/Docx/Fields.hs (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx/Fields.hs b/src/Text/Pandoc/Readers/Docx/Fields.hs new file mode 100644 index 000000000..69758b431 --- /dev/null +++ b/src/Text/Pandoc/Readers/Docx/Fields.hs @@ -0,0 +1,89 @@ +{- +Copyright (C) 2014-2018 Jesse Rosenthal <jrosenthal@jhu.edu> + +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.Docx.Fields + Copyright : Copyright (C) 2014-2018 Jesse Rosenthal + License : GNU GPL, version 2 or above + + Maintainer : Jesse Rosenthal <jrosenthal@jhu.edu> + Stability : alpha + Portability : portable + +For parsing Field definitions in instText tags, as described in +ECMA-376-1:2016, §17.16.5 -} + +module Text.Pandoc.Readers.Docx.Fields ( FieldInfo(..) + , parseFieldInfo + ) where + +import Text.Parsec +import Text.Parsec.String (Parser) + +type URL = String + +data FieldInfo = HyperlinkField URL + | UnknownField + deriving (Show) + +parseFieldInfo :: String -> Either ParseError FieldInfo +parseFieldInfo = parse fieldInfo "" + +fieldInfo :: Parser FieldInfo +fieldInfo = + (try $ HyperlinkField <$> hyperlink) + <|> + return UnknownField + +escapedQuote :: Parser String +escapedQuote = string "\\\"" + +inQuotes :: Parser String +inQuotes = do + (try escapedQuote) <|> (anyChar >>= (\c -> return [c])) + +quotedString :: Parser String +quotedString = do + char '"' + concat <$> manyTill inQuotes (try (char '"')) + +unquotedString :: Parser String +unquotedString = manyTill anyChar (try (space)) + +fieldArgument :: Parser String +fieldArgument = quotedString <|> unquotedString + +-- there are other switches, but this is the only one I've seen in the wild so far, so it's the first one I'll implement. See §17.16.5.25 +hyperlinkSwitch :: Parser (String, String) +hyperlinkSwitch = do + sw <- string "\\l" + spaces + farg <- fieldArgument + return (sw, farg) + +hyperlink :: Parser URL +hyperlink = do + many space + string "HYPERLINK" + spaces + farg <- fieldArgument + switches <- (spaces *> many hyperlinkSwitch) + let url = case switches of + ("\\l", s) : _ -> farg ++ ('#': s) + _ -> farg + return url diff --git a/src/Text/Pandoc/Readers/Docx/Parse.hs b/src/Text/Pandoc/Readers/Docx/Parse.hs index b3a0fee8e..5f648666f 100644 --- a/src/Text/Pandoc/Readers/Docx/Parse.hs +++ b/src/Text/Pandoc/Readers/Docx/Parse.hs @@ -54,6 +54,7 @@ module Text.Pandoc.Readers.Docx.Parse ( Docx(..) , TrackedChange(..) , ChangeType(..) , ChangeInfo(..) + , FieldInfo(..) , archiveToDocx , archiveToDocxWithWarnings ) where @@ -70,6 +71,7 @@ import qualified Data.Map as M import Data.Maybe import System.FilePath import Text.Pandoc.Readers.Docx.Util +import Text.Pandoc.Readers.Docx.Fields import Text.Pandoc.Shared (filteredFilesFromArchive, safeRead) import qualified Text.Pandoc.UTF8 as UTF8 import Text.TeXMath (Exp) @@ -286,10 +288,6 @@ data Run = Run RunStyle [RunElem] | InlineChart -- placeholder deriving Show -data FieldInfo = HyperlinkField URL - | UnknownField - deriving (Show) - data RunElem = TextRun String | LnBrk | Tab | SoftHyphen | NoBreakHyphen deriving Show @@ -802,8 +800,18 @@ elemToParPart ns element return NullParPart FldCharContent info runs | fldCharType == "end" -> do modify $ \st -> st {stateFldCharState = FldCharClosed} - return $ Field info runs + return $ Field info $ reverse runs _ -> throwError WrongElem +elemToParPart ns element + | isElem ns "w" "r" element + , Just instrText <- findChildByName ns "w" "instrText" element = do + fldCharState <- gets stateFldCharState + case fldCharState of + FldCharOpen -> do + info <- eitherToD $ parseFieldInfo $ strContent instrText + modify $ \st -> st{stateFldCharState = FldCharFieldInfo info} + return NullParPart + _ -> return NullParPart elemToParPart ns element | isElem ns "w" "r" element = do run <- elemToRun ns element -- cgit v1.2.3 From 95d602d3b7df8946f236aaad310bedf8fdc2e09f Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Tue, 16 Jan 2018 13:15:00 -0500 Subject: Docx reader: Parse hyperlinks in instrText tags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was a form of hyperlink found in older versions of word. The changes introduced for this, though, create a framework for parsing further fields in MS Word (see the spec, ECMA-376-1:2016, §17.16.5, for more on these fields). Closes #3389 and #4266. --- src/Text/Pandoc/Readers/Docx.hs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index 998179d2f..21120824f 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -446,8 +446,10 @@ parPartToInlines' (PlainOMath exps) = return $ math $ writeTeX exps parPartToInlines' (SmartTag runs) = do smushInlines <$> mapM runToInlines runs -parPartToInlines' (Field _ runs) = do - smushInlines <$> mapM runToInlines runs +parPartToInlines' (Field info runs) = do + case info of + HyperlinkField url -> parPartToInlines' $ ExternalHyperLink url runs + UnknownField -> smushInlines <$> mapM runToInlines runs parPartToInlines' NullParPart = return mempty isAnchorSpan :: Inline -> Bool -- cgit v1.2.3 From d987a9d1142c092798694c650856ed0c371641ac Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Tue, 16 Jan 2018 23:13:33 -0800 Subject: Markdown writer: fix cell width calculation. Previously we could get ever-lengthening cell widths when a table was run repeatedly through `pandoc -f markdown -t markdown`. This patch stabilizes the relative cell widths. Closes #4265. --- 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 08dff2c4e..c8b3a1526 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -701,7 +701,7 @@ pandocTable opts multiline headless aligns widths rawHeaders rawRows = do let columns = transpose (rawHeaders : rawRows) -- minimal column width without wrapping a single word let relWidth w col = - max (floor $ fromIntegral (writerColumns opts) * w) + max (floor $ fromIntegral (writerColumns opts - 1) * w) (if writerWrapText opts == WrapAuto then minNumChars col else numChars col) -- cgit v1.2.3 From c1014167b5c3c2446be3c4e8d10f946979e0737b Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Mon, 15 Jan 2018 12:39:22 -0500 Subject: ImageSize: add derived Eq instance to Dimension --- src/Text/Pandoc/ImageSize.hs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/ImageSize.hs b/src/Text/Pandoc/ImageSize.hs index 4ac1d535f..65559e1ce 100644 --- a/src/Text/Pandoc/ImageSize.hs +++ b/src/Text/Pandoc/ImageSize.hs @@ -83,6 +83,7 @@ data Dimension = Pixel Integer | Inch Double | Percent Double | Em Double + deriving Eq instance Show Dimension where show (Pixel a) = show a ++ "px" -- cgit v1.2.3 From 0482edadbd87f7d981c965f8b3ec04c4b9d102d0 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Mon, 15 Jan 2018 12:36:27 -0500 Subject: Powerpoint writer: Move image sizing into picProps. Rather than passing around attributes, we can have image sizing in the picProps and then pass it along to write to XML. --- src/Text/Pandoc/Writers/Powerpoint/Output.hs | 7 ++--- src/Text/Pandoc/Writers/Powerpoint/Presentation.hs | 32 ++++++++++++++++------ 2 files changed, 27 insertions(+), 12 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint/Output.hs b/src/Text/Pandoc/Writers/Powerpoint/Output.hs index 95dccb655..1ea940497 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Output.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Output.hs @@ -616,10 +616,9 @@ makePicElements :: PandocMonad m => Element -> PicProps -> MediaInfo - -> Text.Pandoc.Definition.Attr -> [ParaElem] -> P m [Element] -makePicElements layout picProps mInfo _ alt = do +makePicElements layout picProps mInfo alt = do opts <- asks envOpts (pageWidth, pageHeight) <- asks envPresentationSize -- hasHeader <- asks envSlideHasHeader @@ -826,11 +825,11 @@ shapeToElement layout (TextBox paras) shapeToElement _ _ = return $ mknode "p:sp" [] () shapeToElements :: PandocMonad m => Element -> Shape -> P m [Element] -shapeToElements layout (Pic picProps fp attr alt) = do +shapeToElements layout (Pic picProps fp alt) = do mInfo <- registerMedia fp alt case mInfoExt mInfo of Just _ -> do - makePicElements layout picProps mInfo attr alt + makePicElements layout picProps mInfo alt Nothing -> shapeToElements layout $ TextBox [Paragraph def alt] shapeToElements layout (GraphicFrame tbls cptn) = graphicFrameToElements layout tbls cptn diff --git a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs index 3c5dd617d..fce85968a 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs @@ -58,6 +58,7 @@ import Control.Monad.State import Data.List (intercalate) import Data.Default import Text.Pandoc.Definition +import Text.Pandoc.ImageSize import Text.Pandoc.Slides (getSlideLevel) import Text.Pandoc.Options import Text.Pandoc.Logging @@ -138,7 +139,7 @@ data Slide = MetadataSlide { metadataSlideTitle :: [ParaElem] } deriving (Show, Eq) -data Shape = Pic PicProps FilePath Text.Pandoc.Definition.Attr [ParaElem] +data Shape = Pic PicProps FilePath [ParaElem] | GraphicFrame [Graphic] [ParaElem] | TextBox [Paragraph] deriving (Show, Eq) @@ -230,10 +231,14 @@ instance Default RunProps where } data PicProps = PicProps { picPropLink :: Maybe LinkTarget + , picWidth :: Maybe Dimension + , picHeight :: Maybe Dimension } deriving (Show, Eq) instance Default PicProps where def = PicProps { picPropLink = Nothing + , picWidth = Nothing + , picHeight = Nothing } -------------------------------------------------- @@ -407,17 +412,28 @@ rowToParagraphs algns tblCells = do let pairs = zip (algns ++ repeat AlignDefault) tblCells mapM (\(a, tc) -> cellToParagraphs a tc) pairs +withAttr :: Attr -> Shape -> Shape +withAttr attr (Pic picPr url caption) = + let picPr' = picPr { picWidth = dimension Width attr + , picHeight = dimension Height attr + } + in + Pic picPr' url caption +withAttr _ sp = sp + blockToShape :: Block -> Pres Shape blockToShape (Plain (il:_)) | Image attr ils (url, _) <- il = - Pic def url attr <$> (inlinesToParElems ils) + (withAttr attr . Pic def url) <$> (inlinesToParElems ils) blockToShape (Para (il:_)) | Image attr ils (url, _) <- il = - Pic def url attr <$> (inlinesToParElems ils) + (withAttr attr . Pic def url) <$> (inlinesToParElems ils) blockToShape (Plain (il:_)) | Link _ (il':_) target <- il , Image attr ils (url, _) <- il' = - Pic def{picPropLink = Just $ ExternalTarget target} url attr <$> (inlinesToParElems ils) + (withAttr attr . Pic def {picPropLink = Just $ ExternalTarget target} url) <$> + (inlinesToParElems ils) blockToShape (Para (il:_)) | Link _ (il':_) target <- il , Image attr ils (url, _) <- il' = - Pic def{picPropLink = Just $ ExternalTarget target} url attr <$> (inlinesToParElems ils) + (withAttr attr . Pic def{picPropLink = Just $ ExternalTarget target} url) <$> + (inlinesToParElems ils) blockToShape (Table caption algn _ hdrCells rows) = do caption' <- inlinesToParElems caption hdrCells' <- rowToParagraphs algn hdrCells @@ -438,7 +454,7 @@ blockToShape blk = do paras <- blockToParagraphs blk combineShapes :: [Shape] -> [Shape] combineShapes [] = [] combineShapes (s : []) = [s] -combineShapes (pic@(Pic _ _ _ _) : ss) = pic : combineShapes ss +combineShapes (pic@(Pic _ _ _) : ss) = pic : combineShapes ss combineShapes ((TextBox []) : ss) = combineShapes ss combineShapes (s : TextBox [] : ss) = combineShapes (s : ss) combineShapes ((TextBox (p:ps)) : (TextBox (p':ps')) : ss) = @@ -650,9 +666,9 @@ applyToParagraph f para = do return $ para {paraElems = paraElems'} applyToShape :: Monad m => (ParaElem -> m ParaElem) -> Shape -> m Shape -applyToShape f (Pic pPr fp attr pes) = do +applyToShape f (Pic pPr fp pes) = do pes' <- mapM f pes - return $ Pic pPr fp attr pes' + return $ Pic pPr fp pes' applyToShape f (GraphicFrame gfx pes) = do pes' <- mapM f pes return $ GraphicFrame gfx pes' -- cgit v1.2.3 From fcbec16e573e6f6bcab2775f48a3ad0a2e1a540f Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Wed, 17 Jan 2018 08:22:13 -0500 Subject: Powerpoint writer: Change reference to notesSlide to endNotesSlide This will prevent confusion when speakers notes are implemented. --- src/Text/Pandoc/Writers/Powerpoint/Presentation.hs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs index fce85968a..5046922ce 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs @@ -580,8 +580,8 @@ forceFontSize px x = do -- We leave these as blocks because we will want to include them in -- the TOC. -makeNotesSlideBlocks :: Pres [Block] -makeNotesSlideBlocks = do +makeEndNotesSlideBlocks :: Pres [Block] +makeEndNotesSlideBlocks = do noteIds <- gets stNoteIds slideLevel <- asks envSlideLevel meta <- asks envMetadata @@ -730,25 +730,25 @@ blocksToPresentation blks = do bodyslides <- mapM (\(bs, n) -> local (\st -> st{envCurSlideId = n}) (blocksToSlide bs)) (zip blksLst [bodyStartNum..]) - let noteStartNum = bodyStartNum + length bodyslides - notesSlideBlocks <- makeNotesSlideBlocks + let endNoteStartNum = bodyStartNum + length bodyslides + endNotesSlideBlocks <- makeEndNotesSlideBlocks -- now we come back and make the real toc... tocSlides <- if writerTableOfContents opts - then do toc <- makeTOCSlide $ blks ++ notesSlideBlocks + then do toc <- makeTOCSlide $ blks ++ endNotesSlideBlocks return [toc] else return [] -- ... and the notes slide. We test to see if the blocks are empty, -- because we don't want to make an empty slide. - notesSlides <- if null notesSlideBlocks + endNotesSlides <- if null endNotesSlideBlocks then return [] - else do notesSlide <- local - (\env -> env { envCurSlideId = noteStartNum + else do endNotesSlide <- local + (\env -> env { envCurSlideId = endNoteStartNum , envInNoteSlide = True }) - (blocksToSlide $ notesSlideBlocks) - return [notesSlide] + (blocksToSlide $ endNotesSlideBlocks) + return [endNotesSlide] - let slides = metadataslides ++ tocSlides ++ bodyslides ++ notesSlides + let slides = metadataslides ++ tocSlides ++ bodyslides ++ endNotesSlides slides' <- mapM (applyToSlide replaceAnchor) slides return $ Presentation slides' -- cgit v1.2.3 From ca8cd38bdca72d1db4ba740481c48e3705b6ee63 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 17 Jan 2018 09:22:35 -0800 Subject: Markdown reader: don't coalesce adjacent raw LaTeX blocks... if they are separated by a blank line. See lierdakil/pandoc-crossref#160 for motivation. --- src/Text/Pandoc/Readers/Markdown.hs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index b76ff498b..94f04eee7 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -122,6 +122,13 @@ spnl = try $ do skipSpaces notFollowedBy (char '\n') +spnl' :: PandocMonad m => ParserT [Char] st m String +spnl' = try $ do + xs <- many spaceChar + ys <- option "" $ try $ (:) <$> newline + <*> (many spaceChar <* notFollowedBy (char '\n')) + return (xs ++ ys) + indentSpaces :: PandocMonad m => MarkdownParser m String indentSpaces = try $ do tabStop <- getOption readerTabStop @@ -1125,10 +1132,9 @@ rawTeXBlock = do lookAhead $ try $ char '\\' >> letter result <- (B.rawBlock "context" . trim . concat <$> many1 ((++) <$> (rawConTeXtEnvironment <|> conTeXtCommand) - <*> (blanklines <|> many spaceChar))) + <*> spnl')) <|> (B.rawBlock "latex" . trim . concat <$> - many1 ((++) <$> rawLaTeXBlock - <*> (blanklines <|> many spaceChar))) + many1 ((++) <$> rawLaTeXBlock <*> spnl')) return $ case B.toList result of [RawBlock _ cs] | all (`elem` [' ','\t','\n']) cs -> return mempty -- cgit v1.2.3 From 63a2507d0e35036d784eb84badb75811f70a5fb9 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Wed, 17 Jan 2018 12:18:40 -0500 Subject: Powerpoint writer: Revamp slide type This is an internal change to the Presentation type. The algebraic datatype that used to be called `Slide` is now `Layout`, and Slide is defined as `Slide SlideId Layout (Maybe Notes)`. Though there should be no user-visible changes in this commit, it offers two benefits moving forward: 1. Slides now carry their Id with them, instead of being assigned it in deck order. This makes it easier to set up a link to, say, an endnotes slide ahead of time. 2. This makes room for Notes slides, when we implement them. --- src/Text/Pandoc/Writers/Powerpoint/Output.hs | 30 +++--- src/Text/Pandoc/Writers/Powerpoint/Presentation.hs | 115 +++++++++++++++------ 2 files changed, 97 insertions(+), 48 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint/Output.hs b/src/Text/Pandoc/Writers/Powerpoint/Output.hs index 1ea940497..752a57047 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Output.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Output.hs @@ -279,9 +279,9 @@ presentationToArchive opts pres = do -------------------------------------------------- -getLayout :: PandocMonad m => Slide -> P m Element -getLayout slide = do - let layoutpath = case slide of +getLayout :: PandocMonad m => Layout -> P m Element +getLayout layout = do + let layoutpath = case layout of (MetadataSlide _ _ _ _) -> "ppt/slideLayouts/slideLayout1.xml" (TitleSlide _) -> "ppt/slideLayouts/slideLayout3.xml" (ContentSlide _ _) -> "ppt/slideLayouts/slideLayout2.xml" @@ -1028,8 +1028,8 @@ metadataToElement layout titleElems subtitleElems authorsElems dateElems metadataToElement _ _ _ _ _ = return $ mknode "p:sp" [] () slideToElement :: PandocMonad m => Slide -> P m Element -slideToElement s@(ContentSlide hdrElems shapes) = do - layout <- getLayout s +slideToElement (Slide _ l@(ContentSlide hdrElems shapes) _ )= do + layout <- getLayout l spTree <- local (\env -> if null hdrElems then env else env{envSlideHasHeader=True}) $ @@ -1039,8 +1039,8 @@ slideToElement s@(ContentSlide hdrElems shapes) = do ("xmlns:r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"), ("xmlns:p", "http://schemas.openxmlformats.org/presentationml/2006/main") ] [mknode "p:cSld" [] [spTree]] -slideToElement s@(TwoColumnSlide hdrElems shapesL shapesR) = do - layout <- getLayout s +slideToElement (Slide _ l@(TwoColumnSlide hdrElems shapesL shapesR) _) = do + layout <- getLayout l spTree <- local (\env -> if null hdrElems then env else env{envSlideHasHeader=True}) $ @@ -1050,16 +1050,16 @@ slideToElement s@(TwoColumnSlide hdrElems shapesL shapesR) = do ("xmlns:r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"), ("xmlns:p", "http://schemas.openxmlformats.org/presentationml/2006/main") ] [mknode "p:cSld" [] [spTree]] -slideToElement s@(TitleSlide hdrElems) = do - layout <- getLayout s +slideToElement (Slide _ l@(TitleSlide hdrElems) _) = do + layout <- getLayout l spTree <- titleToElement layout hdrElems return $ mknode "p:sld" [ ("xmlns:a", "http://schemas.openxmlformats.org/drawingml/2006/main"), ("xmlns:r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"), ("xmlns:p", "http://schemas.openxmlformats.org/presentationml/2006/main") ] [mknode "p:cSld" [] [spTree]] -slideToElement s@(MetadataSlide titleElems subtitleElems authorElems dateElems) = do - layout <- getLayout s +slideToElement (Slide _ l@(MetadataSlide titleElems subtitleElems authorElems dateElems) _) = do + layout <- getLayout l spTree <- metadataToElement layout titleElems subtitleElems authorElems dateElems return $ mknode "p:sld" [ ("xmlns:a", "http://schemas.openxmlformats.org/drawingml/2006/main"), @@ -1227,10 +1227,10 @@ mediaRelElement mInfo = slideToSlideRelElement :: PandocMonad m => Slide -> Int -> P m Element slideToSlideRelElement slide idNum = do let target = case slide of - (MetadataSlide _ _ _ _) -> "../slideLayouts/slideLayout1.xml" - (TitleSlide _) -> "../slideLayouts/slideLayout3.xml" - (ContentSlide _ _) -> "../slideLayouts/slideLayout2.xml" - (TwoColumnSlide _ _ _) -> "../slideLayouts/slideLayout4.xml" + (Slide _ (MetadataSlide _ _ _ _) _) -> "../slideLayouts/slideLayout1.xml" + (Slide _ (TitleSlide _) _) -> "../slideLayouts/slideLayout3.xml" + (Slide _ (ContentSlide _ _) _) -> "../slideLayouts/slideLayout2.xml" + (Slide _ (TwoColumnSlide _ _ _) _) -> "../slideLayouts/slideLayout4.xml" linkIds <- gets stLinkIds mediaIds <- gets stMediaIds diff --git a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs index 5046922ce..1825a048e 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs @@ -35,6 +35,9 @@ Presentation. module Text.Pandoc.Writers.Powerpoint.Presentation ( documentToPresentation , Presentation(..) , Slide(..) + , Layout(..) + , Notes(..) + , SlideId(..) , Shape(..) , Graphic(..) , BulletType(..) @@ -76,7 +79,7 @@ data WriterEnv = WriterEnv { envMetadata :: Meta , envSlideHasHeader :: Bool , envInList :: Bool , envInNoteSlide :: Bool - , envCurSlideId :: Int + , envCurSlideId :: SlideId } deriving (Show) @@ -89,13 +92,13 @@ instance Default WriterEnv where , envSlideHasHeader = False , envInList = False , envInNoteSlide = False - , envCurSlideId = 1 + , envCurSlideId = SlideId "1" } data WriterState = WriterState { stNoteIds :: M.Map Int [Block] -- associate anchors with slide id - , stAnchorMap :: M.Map String Int + , stAnchorMap :: M.Map String SlideId , stLog :: [LogMessage] } deriving (Show, Eq) @@ -124,7 +127,20 @@ type Pixels = Integer data Presentation = Presentation [Slide] deriving (Show) -data Slide = MetadataSlide { metadataSlideTitle :: [ParaElem] + +data Slide = Slide SlideId Layout (Maybe Notes) + deriving (Show, Eq) + +newtype SlideId = SlideId String + deriving (Show, Eq) + +-- In theory you could have anything on a notes slide but it seems +-- designed mainly for one textbox, so we'll just put in the contents +-- of that textbox, to avoid other shapes that won't work as well. +newtype Notes = Notes [Paragraph] + deriving (Show, Eq) + +data Layout = MetadataSlide { metadataSlideTitle :: [ParaElem] , metadataSlideSubtitle :: [ParaElem] , metadataSlideAuthors :: [[ParaElem]] , metadataSlideDate :: [ParaElem] @@ -204,7 +220,7 @@ data Capitals = NoCapitals | SmallCapitals | AllCapitals type URL = String data LinkTarget = ExternalTarget (URL, String) - | InternalTarget Int -- slideId + | InternalTarget SlideId deriving (Show, Eq) data RunProps = RunProps { rPropBold :: Bool @@ -513,18 +529,20 @@ blocksToSlide' :: Int -> [Block] -> Pres Slide blocksToSlide' lvl ((Header n (ident, _, _) ils) : blks) | n < lvl = do registerAnchorId ident + slideId <- asks envCurSlideId hdr <- inlinesToParElems ils - return $ TitleSlide {titleSlideHeader = hdr} + return $ Slide slideId (TitleSlide {titleSlideHeader = hdr}) Nothing | n == lvl = do registerAnchorId ident hdr <- inlinesToParElems ils -- Now get the slide without the header, and then add the header -- in. - slide <- blocksToSlide' lvl blks - return $ case slide of - ContentSlide _ cont -> ContentSlide hdr cont - TwoColumnSlide _ contL contR -> TwoColumnSlide hdr contL contR - slide' -> slide' + (Slide slideId layout mbNotes) <- blocksToSlide' lvl blks + let layout' = case layout of + ContentSlide _ cont -> ContentSlide hdr cont + TwoColumnSlide _ contL contR -> TwoColumnSlide hdr contL contR + layout'' -> layout'' + return $ Slide slideId layout' mbNotes blocksToSlide' _ (blk : blks) | Div (_, classes, _) divBlks <- blk , "columns" `elem` classes @@ -544,21 +562,36 @@ blocksToSlide' _ (blk : blks) [] -> [] shapesL <- blocksToShapes blksL' shapesR <- blocksToShapes blksR' - return $ TwoColumnSlide { twoColumnSlideHeader = [] - , twoColumnSlideLeft = shapesL - , twoColumnSlideRight = shapesR - } + slideId <- asks envCurSlideId + return $ Slide + slideId + TwoColumnSlide { twoColumnSlideHeader = [] + , twoColumnSlideLeft = shapesL + , twoColumnSlideRight = shapesR + } + Nothing blocksToSlide' _ (blk : blks) = do inNoteSlide <- asks envInNoteSlide shapes <- if inNoteSlide then forceFontSize noteSize $ blocksToShapes (blk : blks) else blocksToShapes (blk : blks) - return $ ContentSlide { contentSlideHeader = [] - , contentSlideContent = shapes - } -blocksToSlide' _ [] = return $ ContentSlide { contentSlideHeader = [] - , contentSlideContent = [] - } + slideId <- asks envCurSlideId + return $ + Slide + slideId + ContentSlide { contentSlideHeader = [] + , contentSlideContent = shapes + } + Nothing +blocksToSlide' _ [] = do + slideId <- asks envCurSlideId + return $ + Slide + slideId + ContentSlide { contentSlideHeader = [] + , contentSlideContent = [] + } + Nothing blocksToSlide :: [Block] -> Pres Slide blocksToSlide blks = do @@ -612,13 +645,20 @@ getMetaSlide = do _ -> [] authors <- mapM inlinesToParElems $ docAuthors meta date <- inlinesToParElems $ docDate meta + slideId <- asks envCurSlideId if null title && null subtitle && null authors && null date then return Nothing - else return $ Just $ MetadataSlide { metadataSlideTitle = title - , metadataSlideSubtitle = subtitle - , metadataSlideAuthors = authors - , metadataSlideDate = date - } + else return $ + Just $ + Slide + slideId + MetadataSlide { metadataSlideTitle = title + , metadataSlideSubtitle = subtitle + , metadataSlideAuthors = authors + , metadataSlideDate = date + } + Nothing + -- adapted from the markdown writer elementToListItem :: Shared.Element -> Pres [Block] elementToListItem (Shared.Sec lev _nums (ident,_,_) headerText subsecs) = do @@ -676,26 +716,35 @@ applyToShape f (TextBox paras) = do paras' <- mapM (applyToParagraph f) paras return $ TextBox paras' -applyToSlide :: Monad m => (ParaElem -> m ParaElem) -> Slide -> m Slide -applyToSlide f (MetadataSlide title subtitle authors date) = do +applyToLayout :: Monad m => (ParaElem -> m ParaElem) -> Layout -> m Layout +applyToLayout f (MetadataSlide title subtitle authors date) = do title' <- mapM f title subtitle' <- mapM f subtitle authors' <- mapM (mapM f) authors date' <- mapM f date return $ MetadataSlide title' subtitle' authors' date' -applyToSlide f (TitleSlide title) = do +applyToLayout f (TitleSlide title) = do title' <- mapM f title return $ TitleSlide title' -applyToSlide f (ContentSlide hdr content) = do +applyToLayout f (ContentSlide hdr content) = do hdr' <- mapM f hdr content' <- mapM (applyToShape f) content return $ ContentSlide hdr' content' -applyToSlide f (TwoColumnSlide hdr contentL contentR) = do +applyToLayout f (TwoColumnSlide hdr contentL contentR) = do hdr' <- mapM f hdr contentL' <- mapM (applyToShape f) contentL contentR' <- mapM (applyToShape f) contentR return $ TwoColumnSlide hdr' contentL' contentR' +applyToSlide :: Monad m => (ParaElem -> m ParaElem) -> Slide -> m Slide +applyToSlide f (Slide slideId layout mbNotes) = do + layout' <- applyToLayout f layout + mbNotes' <- case mbNotes of + Just (Notes notes) -> (Just . Notes) <$> + mapM (applyToParagraph f) notes + Nothing -> return Nothing + return $ Slide slideId layout' mbNotes' + replaceAnchor :: ParaElem -> Pres ParaElem replaceAnchor (Run rProps s) | Just (ExternalTarget ('#':anchor, _)) <- rLink rProps = do @@ -728,7 +777,7 @@ blocksToPresentation blks = do let bodyStartNum = tocStartNum + tocSlidesLength blksLst <- splitBlocks blks bodyslides <- mapM - (\(bs, n) -> local (\st -> st{envCurSlideId = n}) (blocksToSlide bs)) + (\(bs, n) -> local (\st -> st{envCurSlideId = SlideId $ show n}) (blocksToSlide bs)) (zip blksLst [bodyStartNum..]) let endNoteStartNum = bodyStartNum + length bodyslides endNotesSlideBlocks <- makeEndNotesSlideBlocks @@ -742,7 +791,7 @@ blocksToPresentation blks = do endNotesSlides <- if null endNotesSlideBlocks then return [] else do endNotesSlide <- local - (\env -> env { envCurSlideId = endNoteStartNum + (\env -> env { envCurSlideId = SlideId $ show endNoteStartNum , envInNoteSlide = True }) (blocksToSlide $ endNotesSlideBlocks) -- cgit v1.2.3 From 0d53efeddb34ecafefecb731d5b90b98571cefa2 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Wed, 17 Jan 2018 13:31:39 -0500 Subject: Powerpoint writer: Use slideids to simplify code. --- src/Text/Pandoc/Writers/Powerpoint/Output.hs | 103 +++++++++++++-------- src/Text/Pandoc/Writers/Powerpoint/Presentation.hs | 48 +++++----- 2 files changed, 91 insertions(+), 60 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint/Output.hs b/src/Text/Pandoc/Writers/Powerpoint/Output.hs index 752a57047..8ef5665fa 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Output.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Output.hs @@ -105,6 +105,7 @@ data WriterEnv = WriterEnv { envRefArchive :: Archive -- the rId number , envSlideIdOffset :: Int , envContentType :: ContentType + , envSlideIdMap :: M.Map SlideId Int } deriving (Show) @@ -120,6 +121,7 @@ instance Default WriterEnv where , envCurSlideId = 1 , envSlideIdOffset = 1 , envContentType = NormalContent + , envSlideIdMap = mempty } data ContentType = NormalContent @@ -231,8 +233,8 @@ presentationToArchiveP p@(Presentation slides) = do -- sure we know the correct offset for the rIds. presEntry <- presentationToPresEntry p presRelsEntry <- presentationToRelsEntry p - slideEntries <- mapM (\(s, n) -> slideToEntry s n) $ zip slides [1..] - slideRelEntries <- mapM (\(s,n) -> slideToSlideRelEntry s n) $ zip slides [1..] + slideEntries <- mapM slideToEntry slides + slideRelEntries <- mapM slideToSlideRelEntry slides -- These have to come after everything, because they need the info -- built up in the state. mediaEntries <- makeMediaEntries @@ -244,6 +246,10 @@ presentationToArchiveP p@(Presentation slides) = do mediaEntries ++ [contentTypesEntry, relsEntry, presEntry, presRelsEntry] +makeSlideIdMap :: Presentation -> M.Map SlideId Int +makeSlideIdMap (Presentation slides) = + M.fromList $ (map slideId slides) `zip` [1..] + presentationToArchive :: PandocMonad m => WriterOptions -> Presentation -> m Archive presentationToArchive opts pres = do distArchive <- (toArchive . BL.fromStrict) <$> @@ -266,6 +272,7 @@ presentationToArchive opts pres = do , envUTCTime = utctime , envOpts = opts , envPresentationSize = presSize + , envSlideIdMap = makeSlideIdMap pres } let st = def { stMediaGlobalIds = initialGlobalIds refArchive distArchive @@ -1069,13 +1076,31 @@ slideToElement (Slide _ l@(MetadataSlide titleElems subtitleElems authorElems da ----------------------------------------------------------------------- -slideToFilePath :: Slide -> Int -> FilePath -slideToFilePath _ idNum = "slide" ++ (show $ idNum) ++ ".xml" +getSlideIdNum :: PandocMonad m => SlideId -> P m Int +getSlideIdNum sldId = do + slideIdMap <- asks envSlideIdMap + case M.lookup sldId slideIdMap of + Just n -> return n + Nothing -> throwError $ + PandocShouldNeverHappenError $ + "Slide Id " ++ (show sldId) ++ " not found." -slideToSlideId :: Monad m => Slide -> Int -> P m String -slideToSlideId _ idNum = do - n <- asks envSlideIdOffset - return $ "rId" ++ (show $ idNum + n) +slideNum :: PandocMonad m => Slide -> P m Int +slideNum slide = getSlideIdNum $ slideId slide + +idNumToFilePath :: Int -> FilePath +idNumToFilePath idNum = "slide" ++ (show $ idNum) ++ ".xml" + +slideToFilePath :: PandocMonad m => Slide -> P m FilePath +slideToFilePath slide = do + idNum <- slideNum slide + return $ "slide" ++ (show $ idNum) ++ ".xml" + +slideToRelId :: PandocMonad m => Slide -> P m String +slideToRelId slide = do + n <- slideNum slide + offset <- asks envSlideIdOffset + return $ "rId" ++ (show $ n + offset) data Relationship = Relationship { relId :: Int @@ -1096,11 +1121,12 @@ elementToRel element return $ Relationship num type' target | otherwise = Nothing -slideToPresRel :: Monad m => Slide -> Int -> P m Relationship -slideToPresRel slide idNum = do +slideToPresRel :: PandocMonad m => Slide -> P m Relationship +slideToPresRel slide = do + idNum <- slideNum slide n <- asks envSlideIdOffset let rId = idNum + n - fp = "slides/" ++ slideToFilePath slide idNum + fp = "slides/" ++ idNumToFilePath idNum return $ Relationship { relId = rId , relType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" , relTarget = fp @@ -1117,7 +1143,7 @@ getRels = do presentationToRels :: PandocMonad m => Presentation -> P m [Relationship] presentationToRels (Presentation slides) = do - mySlideRels <- mapM (\(s, n) -> slideToPresRel s n) $ zip slides [1..] + mySlideRels <- mapM slideToPresRel slides rels <- getRels let relsWithoutSlides = filter (\r -> relType r /= "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide") rels -- We want to make room for the slides in the id space. The slides @@ -1184,27 +1210,30 @@ elemToEntry fp element = do epochtime <- (floor . utcTimeToPOSIXSeconds) <$> asks envUTCTime return $ toEntry fp epochtime $ renderXml element -slideToEntry :: PandocMonad m => Slide -> Int -> P m Entry -slideToEntry slide idNum = do +slideToEntry :: PandocMonad m => Slide -> P m Entry +slideToEntry slide = do + idNum <- slideNum slide local (\env -> env{envCurSlideId = idNum}) $ do element <- slideToElement slide - elemToEntry ("ppt/slides/" ++ slideToFilePath slide idNum) element + elemToEntry ("ppt/slides/" ++ idNumToFilePath idNum) element -slideToSlideRelEntry :: PandocMonad m => Slide -> Int -> P m Entry -slideToSlideRelEntry slide idNum = do - element <- slideToSlideRelElement slide idNum - elemToEntry ("ppt/slides/_rels/" ++ slideToFilePath slide idNum ++ ".rels") element +slideToSlideRelEntry :: PandocMonad m => Slide -> P m Entry +slideToSlideRelEntry slide = do + idNum <- slideNum slide + element <- slideToSlideRelElement slide + elemToEntry ("ppt/slides/_rels/" ++ idNumToFilePath idNum ++ ".rels") element linkRelElement :: PandocMonad m => Int -> LinkTarget -> P m Element -linkRelElement idNum (InternalTarget num) = do +linkRelElement rIdNum (InternalTarget targetId) = do + targetIdNum <- getSlideIdNum targetId return $ - mknode "Relationship" [ ("Id", "rId" ++ show idNum) + mknode "Relationship" [ ("Id", "rId" ++ show rIdNum) , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide") - , ("Target", "slide" ++ show num ++ ".xml") + , ("Target", "slide" ++ show targetIdNum ++ ".xml") ] () -linkRelElement idNum (ExternalTarget (url, _)) = do +linkRelElement rIdNum (ExternalTarget (url, _)) = do return $ - mknode "Relationship" [ ("Id", "rId" ++ show idNum) + mknode "Relationship" [ ("Id", "rId" ++ show rIdNum) , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink") , ("Target", url) , ("TargetMode", "External") @@ -1224,8 +1253,9 @@ mediaRelElement mInfo = , ("Target", "../media/image" ++ (show $ mInfoGlobalId mInfo) ++ ext) ] () -slideToSlideRelElement :: PandocMonad m => Slide -> Int -> P m Element -slideToSlideRelElement slide idNum = do +slideToSlideRelElement :: PandocMonad m => Slide -> P m Element +slideToSlideRelElement slide = do + idNum <- slideNum slide let target = case slide of (Slide _ (MetadataSlide _ _ _ _) _) -> "../slideLayouts/slideLayout1.xml" (Slide _ (TitleSlide _) _) -> "../slideLayouts/slideLayout3.xml" @@ -1250,15 +1280,16 @@ slideToSlideRelElement slide idNum = do , ("Target", target)] () ] ++ linkRels ++ mediaRels) -slideToSldIdElement :: PandocMonad m => Slide -> Int -> P m Element -slideToSldIdElement slide idNum = do - let id' = show $ idNum + 255 - rId <- slideToSlideId slide idNum +slideToSldIdElement :: PandocMonad m => Slide -> P m Element +slideToSldIdElement slide = do + n <- slideNum slide + let id' = show $ n + 255 + rId <- slideToRelId slide return $ mknode "p:sldId" [("id", id'), ("r:id", rId)] () presentationToSldIdLst :: PandocMonad m => Presentation -> P m Element presentationToSldIdLst (Presentation slides) = do - ids <- mapM (\(s,n) -> slideToSldIdElement s n) (zip slides [1..]) + ids <- mapM slideToSldIdElement slides return $ mknode "p:sldIdLst" [] ids presentationToPresentationElement :: PandocMonad m => Presentation -> P m Element @@ -1366,12 +1397,10 @@ presentationToContentTypes (Presentation slides) = do inheritedOverrides = mapMaybe pathToOverride filePaths presOverride = mapMaybe pathToOverride ["ppt/presentation.xml"] - slideOverrides = - mapMaybe - (\(s, n) -> - pathToOverride $ "ppt/slides/" ++ slideToFilePath s n) - (zip slides [1..]) - -- propOverride = mapMaybe pathToOverride ["docProps/core.xml"] + relativePaths <- mapM slideToFilePath slides + let slideOverrides = mapMaybe + (\fp -> pathToOverride $ "ppt/slides/" ++ fp) + relativePaths return $ ContentTypes (defaults ++ mediaDefaults) (inheritedOverrides ++ presOverride ++ slideOverrides) diff --git a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs index 1825a048e..3f98dceea 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs @@ -128,11 +128,13 @@ data Presentation = Presentation [Slide] deriving (Show) -data Slide = Slide SlideId Layout (Maybe Notes) - deriving (Show, Eq) +data Slide = Slide { slideId :: SlideId + , slideLayout :: Layout + , slideNotes :: (Maybe Notes) + } deriving (Show, Eq) newtype SlideId = SlideId String - deriving (Show, Eq) + deriving (Show, Eq, Ord) -- In theory you could have anything on a notes slide but it seems -- designed mainly for one textbox, so we'll just put in the contents @@ -316,9 +318,9 @@ isListType _ = False registerAnchorId :: String -> Pres () registerAnchorId anchor = do anchorMap <- gets stAnchorMap - slideId <- asks envCurSlideId + sldId <- asks envCurSlideId unless (null anchor) $ - modify $ \st -> st {stAnchorMap = M.insert anchor slideId anchorMap} + modify $ \st -> st {stAnchorMap = M.insert anchor sldId anchorMap} -- Currently hardcoded, until I figure out how to make it dynamic. blockQuoteSize :: Pixels @@ -529,20 +531,20 @@ blocksToSlide' :: Int -> [Block] -> Pres Slide blocksToSlide' lvl ((Header n (ident, _, _) ils) : blks) | n < lvl = do registerAnchorId ident - slideId <- asks envCurSlideId + sldId <- asks envCurSlideId hdr <- inlinesToParElems ils - return $ Slide slideId (TitleSlide {titleSlideHeader = hdr}) Nothing + return $ Slide sldId (TitleSlide {titleSlideHeader = hdr}) Nothing | n == lvl = do registerAnchorId ident hdr <- inlinesToParElems ils -- Now get the slide without the header, and then add the header -- in. - (Slide slideId layout mbNotes) <- blocksToSlide' lvl blks - let layout' = case layout of + slide <- blocksToSlide' lvl blks + let layout = case slideLayout slide of ContentSlide _ cont -> ContentSlide hdr cont TwoColumnSlide _ contL contR -> TwoColumnSlide hdr contL contR - layout'' -> layout'' - return $ Slide slideId layout' mbNotes + layout' -> layout' + return $ slide{slideLayout = layout} blocksToSlide' _ (blk : blks) | Div (_, classes, _) divBlks <- blk , "columns" `elem` classes @@ -562,9 +564,9 @@ blocksToSlide' _ (blk : blks) [] -> [] shapesL <- blocksToShapes blksL' shapesR <- blocksToShapes blksR' - slideId <- asks envCurSlideId + sldId <- asks envCurSlideId return $ Slide - slideId + sldId TwoColumnSlide { twoColumnSlideHeader = [] , twoColumnSlideLeft = shapesL , twoColumnSlideRight = shapesR @@ -575,19 +577,19 @@ blocksToSlide' _ (blk : blks) = do shapes <- if inNoteSlide then forceFontSize noteSize $ blocksToShapes (blk : blks) else blocksToShapes (blk : blks) - slideId <- asks envCurSlideId + sldId <- asks envCurSlideId return $ Slide - slideId + sldId ContentSlide { contentSlideHeader = [] , contentSlideContent = shapes } Nothing blocksToSlide' _ [] = do - slideId <- asks envCurSlideId + sldId <- asks envCurSlideId return $ Slide - slideId + sldId ContentSlide { contentSlideHeader = [] , contentSlideContent = [] } @@ -645,13 +647,13 @@ getMetaSlide = do _ -> [] authors <- mapM inlinesToParElems $ docAuthors meta date <- inlinesToParElems $ docDate meta - slideId <- asks envCurSlideId + sldId <- asks envCurSlideId if null title && null subtitle && null authors && null date then return Nothing else return $ Just $ Slide - slideId + sldId MetadataSlide { metadataSlideTitle = title , metadataSlideSubtitle = subtitle , metadataSlideAuthors = authors @@ -737,13 +739,13 @@ applyToLayout f (TwoColumnSlide hdr contentL contentR) = do return $ TwoColumnSlide hdr' contentL' contentR' applyToSlide :: Monad m => (ParaElem -> m ParaElem) -> Slide -> m Slide -applyToSlide f (Slide slideId layout mbNotes) = do - layout' <- applyToLayout f layout - mbNotes' <- case mbNotes of +applyToSlide f slide = do + layout' <- applyToLayout f $ slideLayout slide + mbNotes' <- case slideNotes slide of Just (Notes notes) -> (Just . Notes) <$> mapM (applyToParagraph f) notes Nothing -> return Nothing - return $ Slide slideId layout' mbNotes' + return slide{slideLayout = layout', slideNotes = mbNotes'} replaceAnchor :: ParaElem -> Pres ParaElem replaceAnchor (Run rProps s) -- cgit v1.2.3 From 42e690d1b2da5f7111c6d7918e9311f5cdc7a335 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Wed, 17 Jan 2018 13:58:19 -0500 Subject: Powerpoint writer: Use more specific slide id names. --- src/Text/Pandoc/Writers/Powerpoint/Presentation.hs | 36 ++++++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs index 3f98dceea..d130d8247 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs @@ -69,6 +69,7 @@ import Text.Pandoc.Walk import qualified Text.Pandoc.Shared as Shared -- so we don't overlap "Element" import Text.Pandoc.Writers.Shared (metaValueToInlines) import qualified Data.Map as M +import qualified Data.Set as S import Data.Maybe (maybeToList) data WriterEnv = WriterEnv { envMetadata :: Meta @@ -92,22 +93,46 @@ instance Default WriterEnv where , envSlideHasHeader = False , envInList = False , envInNoteSlide = False - , envCurSlideId = SlideId "1" + , envCurSlideId = SlideId "Default" } data WriterState = WriterState { stNoteIds :: M.Map Int [Block] -- associate anchors with slide id , stAnchorMap :: M.Map String SlideId + , stSlideIdSet :: S.Set SlideId , stLog :: [LogMessage] + } deriving (Show, Eq) instance Default WriterState where def = WriterState { stNoteIds = mempty , stAnchorMap = mempty + -- we reserve this s + , stSlideIdSet = reservedSlideIds , stLog = [] } +reservedSlideIds :: S.Set SlideId +reservedSlideIds = S.fromList [SlideId "EndNotes"] + +uniqueSlideId' :: Integer -> S.Set SlideId -> String -> SlideId +uniqueSlideId' n idSet s = + let s' = if n == 0 then s else (s ++ "-" ++ show n) + in if SlideId s' `S.member` idSet + then uniqueSlideId' (n+1) idSet s + else SlideId s' + +uniqueSlideId :: S.Set SlideId -> String -> SlideId +uniqueSlideId = uniqueSlideId' 0 + +runUniqueSlideId :: String -> Pres SlideId +runUniqueSlideId s = do + idSet <- gets stSlideIdSet + let sldId = uniqueSlideId idSet s + modify $ \st -> st{stSlideIdSet = S.insert sldId idSet} + return sldId + addLogMessage :: LogMessage -> Pres () addLogMessage msg = modify $ \st -> st{stLog = msg : (stLog st)} @@ -778,9 +803,14 @@ blocksToPresentation blks = do let tocSlidesLength = if writerTableOfContents opts then 1 else 0 let bodyStartNum = tocStartNum + tocSlidesLength blksLst <- splitBlocks blks + + bodySlideIds <- mapM + (\n -> runUniqueSlideId $ "BodySlide" ++ show n) + ([1..] :: [Integer]) bodyslides <- mapM - (\(bs, n) -> local (\st -> st{envCurSlideId = SlideId $ show n}) (blocksToSlide bs)) - (zip blksLst [bodyStartNum..]) + (\(bs, ident) -> + local (\st -> st{envCurSlideId = ident}) (blocksToSlide bs)) + (zip blksLst bodySlideIds) let endNoteStartNum = bodyStartNum + length bodyslides endNotesSlideBlocks <- makeEndNotesSlideBlocks -- now we come back and make the real toc... -- cgit v1.2.3 From 588af3cc7876e0e0d91112f396d999a1d4d9aa73 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Wed, 17 Jan 2018 14:12:51 -0500 Subject: Powerpoint writer: Link notes to endnotes slide. --- src/Text/Pandoc/Writers/Powerpoint/Presentation.hs | 44 ++++++++++++---------- 1 file changed, 25 insertions(+), 19 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs index d130d8247..495675aad 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs @@ -113,8 +113,20 @@ instance Default WriterState where , stLog = [] } +metadataSlideId :: SlideId +metadataSlideId = SlideId "Metadata" + +tocSlideId :: SlideId +tocSlideId = SlideId "TOC" + +endNotesSlideId :: SlideId +endNotesSlideId = SlideId "EndNotes" + reservedSlideIds :: S.Set SlideId -reservedSlideIds = S.fromList [SlideId "EndNotes"] +reservedSlideIds = S.fromList [ metadataSlideId + , tocSlideId + , endNotesSlideId + ] uniqueSlideId' :: Integer -> S.Set SlideId -> String -> SlideId uniqueSlideId' n idSet s = @@ -329,7 +341,8 @@ inlineToParElems (Note blks) = do lst -> maximum lst curNoteId = maxNoteId + 1 modify $ \st -> st { stNoteIds = M.insert curNoteId blks notes } - inlineToParElems $ Superscript [Str $ show curNoteId] + local (\env -> env{envRunProps = (envRunProps env){rLink = Just $ InternalTarget endNotesSlideId}}) $ + inlineToParElems $ Superscript [Str $ show curNoteId] inlineToParElems (Span _ ils) = concatMapM inlineToParElems ils inlineToParElems (RawInline _ _) = return [] inlineToParElems _ = return [] @@ -672,13 +685,12 @@ getMetaSlide = do _ -> [] authors <- mapM inlinesToParElems $ docAuthors meta date <- inlinesToParElems $ docDate meta - sldId <- asks envCurSlideId if null title && null subtitle && null authors && null date then return Nothing else return $ Just $ Slide - sldId + metadataSlideId MetadataSlide { metadataSlideTitle = title , metadataSlideSubtitle = subtitle , metadataSlideAuthors = authors @@ -701,7 +713,7 @@ elementToListItem (Shared.Sec lev _nums (ident,_,_) headerText subsecs) = do elementToListItem (Shared.Blk _) = return [] makeTOCSlide :: [Block] -> Pres Slide -makeTOCSlide blks = do +makeTOCSlide blks = local (\env -> env{envCurSlideId = tocSlideId}) $ do contents <- BulletList <$> mapM elementToListItem (Shared.hierarchicalize blks) meta <- asks envMetadata slideLevel <- asks envSlideLevel @@ -787,9 +799,7 @@ replaceAnchor pe = return pe blocksToPresentation :: [Block] -> Pres Presentation blocksToPresentation blks = do opts <- asks envOpts - let metadataStartNum = 1 metadataslides <- maybeToList <$> getMetaSlide - let tocStartNum = metadataStartNum + length metadataslides -- As far as I can tell, if we want to have a variable-length toc in -- the future, we'll have to make it twice. Once to get the length, -- and a second time to include the notes slide. We can't make the @@ -800,18 +810,14 @@ blocksToPresentation blks = do -- For now, though, since the TOC slide is only length 1, if it -- exists, we'll just get the length, and then come back to make the -- slide later - let tocSlidesLength = if writerTableOfContents opts then 1 else 0 - let bodyStartNum = tocStartNum + tocSlidesLength blksLst <- splitBlocks blks - bodySlideIds <- mapM (\n -> runUniqueSlideId $ "BodySlide" ++ show n) - ([1..] :: [Integer]) + (take (length blksLst) [1..] :: [Integer]) bodyslides <- mapM (\(bs, ident) -> local (\st -> st{envCurSlideId = ident}) (blocksToSlide bs)) (zip blksLst bodySlideIds) - let endNoteStartNum = bodyStartNum + length bodyslides endNotesSlideBlocks <- makeEndNotesSlideBlocks -- now we come back and make the real toc... tocSlides <- if writerTableOfContents opts @@ -821,13 +827,13 @@ blocksToPresentation blks = do -- ... and the notes slide. We test to see if the blocks are empty, -- because we don't want to make an empty slide. endNotesSlides <- if null endNotesSlideBlocks - then return [] - else do endNotesSlide <- local - (\env -> env { envCurSlideId = SlideId $ show endNoteStartNum - , envInNoteSlide = True - }) - (blocksToSlide $ endNotesSlideBlocks) - return [endNotesSlide] + then return [] + else do endNotesSlide <- local + (\env -> env { envCurSlideId = endNotesSlideId + , envInNoteSlide = True + }) + (blocksToSlide $ endNotesSlideBlocks) + return [endNotesSlide] let slides = metadataslides ++ tocSlides ++ bodyslides ++ endNotesSlides slides' <- mapM (applyToSlide replaceAnchor) slides -- cgit v1.2.3 From d7f0ecfdd884871e3c49a9ebec738fb874685cd3 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Tue, 16 Jan 2018 19:14:05 +0300 Subject: Muse reader: code cleanup --- src/Text/Pandoc/Readers/Muse.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 78c567759..de7d629bd 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -221,7 +221,7 @@ blockElements = choice [ comment comment :: PandocMonad m => MuseParser m (F Blocks) comment = try $ do char ';' - optionMaybe (spaceChar >> (many $ noneOf "\n")) + optionMaybe (spaceChar >> many (noneOf "\n")) eol return mempty @@ -658,7 +658,7 @@ footnote = try $ do return $ B.note contents' whitespace :: PandocMonad m => MuseParser m (F Inlines) -whitespace = fmap return (lb <|> regsp) +whitespace = return <$> (lb <|> regsp) where lb = try $ skipMany spaceChar >> linebreak >> return B.space regsp = try $ skipMany1 spaceChar >> return B.space -- cgit v1.2.3 From ab85143e8aa94de8927208c7eefa1dbfa97666de Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Tue, 16 Jan 2018 13:53:17 +0300 Subject: Muse reader: refactor list parsing Now list item contents is parsed as blocks, without resorting to parseFromString. Only the first line of paragraph has to be indented now, just like in Emacs Muse and Text::Amuse. Definition lists are not refactored yet. See also: issue #3865. --- src/Text/Pandoc/Readers/Muse.hs | 140 +++++++++++++++++++--------------------- 1 file changed, 65 insertions(+), 75 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index de7d629bd..abc194769 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -42,7 +42,8 @@ module Text.Pandoc.Readers.Muse (readMuse) where import Control.Monad import Control.Monad.Except (throwError) import Data.Char (isLetter) -import Data.List (stripPrefix) +import Data.List (stripPrefix, intercalate) +import Data.List.Split (splitOn) import qualified Data.Map as M import Data.Maybe (fromMaybe) import Data.Text (Text, unpack) @@ -187,17 +188,19 @@ directive = do -- block parsers -- -block :: PandocMonad m => MuseParser m (F Blocks) -block = do - res <- mempty <$ skipMany1 blankline - <|> blockElements - <|> para - skipMany blankline +parseBlock :: PandocMonad m => MuseParser m (F Blocks) +parseBlock = do + res <- blockElements <|> para + optionMaybe blankline trace (take 60 $ show $ B.toList $ runF res defaultParserState) return res +block :: PandocMonad m => MuseParser m (F Blocks) +block = parseBlock <* skipMany blankline + blockElements :: PandocMonad m => MuseParser m (F Blocks) -blockElements = choice [ comment +blockElements = choice [ mempty <$ blankline + , comment , separator , header , example @@ -257,15 +260,26 @@ example = try $ do -- in case opening and/or closing tags are on separate lines. chop :: String -> String chop = lchop . rchop - where lchop s = case s of + +lchop :: String -> String +lchop s = case s of '\n':ss -> ss _ -> s - rchop = reverse . lchop . reverse + +rchop :: String -> String +rchop = reverse . lchop . reverse + +dropSpacePrefix :: [String] -> [String] +dropSpacePrefix lns = + map (drop maxIndent) lns + where flns = filter (not . all (== ' ')) lns + maxIndent = if null flns then maximum (map length lns) else length $ takeWhile (== ' ') $ foldl1 commonPrefix flns exampleTag :: PandocMonad m => MuseParser m (F Blocks) -exampleTag = do +exampleTag = try $ do + many spaceChar (attr, contents) <- htmlElement "example" - return $ return $ B.codeBlockWith attr $ chop contents + return $ return $ B.codeBlockWith attr $ rchop $ intercalate "\n" $ dropSpacePrefix $ splitOn "\n" $ lchop contents literal :: PandocMonad m => MuseParser m (F Blocks) literal = do @@ -309,7 +323,7 @@ verseLine = do verseLines :: PandocMonad m => MuseParser m (F Blocks) verseLines = do - optionMaybe blankline -- Skip blankline after opening tag on separate line + --optionMaybe blankline -- Skip blankline after opening tag on separate line lns <- many verseLine lns' <- mapM (parseFromString' (trimInlinesF . mconcat <$> many inline)) lns return $ B.lineBlock <$> sequence lns' @@ -317,7 +331,7 @@ verseLines = do verseTag :: PandocMonad m => MuseParser m (F Blocks) verseTag = do (_, content) <- htmlElement "verse" - parseFromString verseLines content + parseFromString verseLines (intercalate "\n" $ dropSpacePrefix $ splitOn "\n" $ lchop content) commentTag :: PandocMonad m => MuseParser m (F Blocks) commentTag = parseHtmlContent "comment" anyChar >> return mempty @@ -349,7 +363,7 @@ amuseNoteBlock = try $ do guardEnabled Ext_amuse pos <- getPosition ref <- noteMarker <* spaceChar - content <- listItemContents $ 3 + length ref + content <- listItemContents oldnotes <- stateNotes' <$> getState case M.lookup ref oldnotes of Just _ -> logMessage $ DuplicateNoteReference ref pos @@ -399,11 +413,6 @@ lineBlock = try $ do -- lists -- -listLine :: PandocMonad m => Int -> MuseParser m String -listLine markerLength = try $ do - indentWith markerLength - manyTill anyChar eol - withListContext :: PandocMonad m => MuseParser m a -> MuseParser m a withListContext p = do state <- getState @@ -413,66 +422,47 @@ withListContext p = do updateState (\st -> st {stateParserContext = oldContext}) return parsed -listContinuation :: PandocMonad m => Int -> MuseParser m [String] -listContinuation markerLength = try $ do - result <- many1 $ listLine markerLength - blank <- option id ((++ [""]) <$ blankline) - return $ blank result - -listStart :: PandocMonad m => MuseParser m Int -> MuseParser m Int -listStart marker = try $ do - preWhitespace <- length <$> many spaceChar - st <- stateParserContext <$> getState - getPosition >>= \pos -> guard (st == ListItemState || sourceColumn pos /= 1) - markerLength <- marker - void spaceChar <|> eol - return $ preWhitespace + markerLength + 1 - -dropSpacePrefix :: [String] -> [String] -dropSpacePrefix lns = - map (drop maxIndent) lns - where flns = filter (not . all (== ' ')) lns - maxIndent = if null flns then 0 else length $ takeWhile (== ' ') $ foldl1 commonPrefix flns - -listItemContents :: PandocMonad m => Int -> MuseParser m (F Blocks) -listItemContents markerLength = do - firstLine <- manyTill anyChar eol - restLines <- many $ listLine markerLength - blank <- option id ((++ [""]) <$ blankline) - let first = firstLine : blank restLines - rest <- many $ listContinuation markerLength - let allLines = concat (first : rest) - parseFromString (withListContext parseBlocks) $ unlines (dropSpacePrefix allLines) - -listItem :: PandocMonad m => MuseParser m Int -> MuseParser m (F Blocks) -listItem start = try $ do - markerLength <- start - listItemContents markerLength - -bulletListItems :: PandocMonad m => MuseParser m (F [Blocks]) -bulletListItems = sequence <$> many1 (listItem bulletListStart) +listItemContents :: PandocMonad m => MuseParser m (F Blocks) +listItemContents = do + pos <- getPosition + let col = sourceColumn pos - 1 + first <- try $ withListContext parseBlock + rest <- many $ try (skipMany blankline >> indentWith col >> withListContext parseBlock) + return $ mconcat (first : rest) -bulletListStart :: PandocMonad m => MuseParser m Int -bulletListStart = listStart (char '-' >> return 1) +listItem :: PandocMonad m => Int -> MuseParser m () -> MuseParser m (F Blocks) +listItem n p = try $ do + optionMaybe blankline + count n spaceChar + p + void spaceChar <|> lookAhead eol + listItemContents bulletList :: PandocMonad m => MuseParser m (F Blocks) -bulletList = do - listItems <- bulletListItems - return $ B.bulletList <$> listItems - -orderedListStart :: PandocMonad m - => ListNumberStyle - -> ListNumberDelim - -> MuseParser m Int -orderedListStart style delim = listStart (snd <$> withHorizDisplacement (orderedListMarker style delim)) +bulletList = try $ do + many spaceChar + pos <- getPosition + let col = sourceColumn pos + guard $ col /= 1 + char '-' + void spaceChar <|> lookAhead eol + first <- listItemContents + rest <- many $ listItem (col - 1) (void (char '-')) + return $ B.bulletList <$> sequence (first : rest) orderedList :: PandocMonad m => MuseParser m (F Blocks) orderedList = try $ do - p@(_, style, delim) <- lookAhead (many spaceChar *> anyOrderedListMarker <* (eol <|> void spaceChar)) + many spaceChar + pos <- getPosition + let col = sourceColumn pos + guard $ col /= 1 + p@(_, style, delim) <- anyOrderedListMarker guard $ style `elem` [Decimal, LowerAlpha, UpperAlpha, LowerRoman, UpperRoman] guard $ delim == Period - items <- sequence <$> many1 (listItem $ orderedListStart style delim) - return $ B.orderedListWith p <$> items + void spaceChar <|> lookAhead eol + first <- listItemContents + rest <- many $ listItem (col - 1) (void (orderedListMarker style delim)) + return $ B.orderedListWith p <$> sequence (first : rest) definitionListItem :: PandocMonad m => MuseParser m (F (Inlines, [Blocks])) definitionListItem = try $ do @@ -482,7 +472,7 @@ definitionListItem = try $ do string "::" firstLine <- manyTill anyChar eol restLines <- manyTill anyLine endOfListItemElement - let lns = dropWhile (== ' ') firstLine : dropSpacePrefix restLines + let lns = dropWhile (== ' ') firstLine : restLines lineContent <- parseFromString (withListContext parseBlocks) $ unlines lns pure $ do lineContent' <- lineContent term' <- term @@ -501,8 +491,8 @@ definitionListItems = sequence <$> many1 definitionListItem definitionList :: PandocMonad m => MuseParser m (F Blocks) definitionList = do - listItems <- definitionListItems - return $ B.definitionList <$> listItems + items <- definitionListItems + return $ B.definitionList <$> items -- -- tables -- cgit v1.2.3 From 9986ccb3330847963532311e00f137dfb4a004e1 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Thu, 18 Jan 2018 02:46:02 +0300 Subject: Muse reader: parse "~~" as non-breaking space in Text::Amuse mode Latest Text::Amuse supports "~~" --- src/Text/Pandoc/Readers/Muse.hs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index abc194769..5d032608c 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -714,8 +714,7 @@ verbatimTag = do return $ return $ B.text content nbsp :: PandocMonad m => MuseParser m (F Inlines) -nbsp = do - guardDisabled Ext_amuse -- Supported only by Emacs Muse +nbsp = try $ do string "~~" return $ return $ B.str "\160" -- cgit v1.2.3 From 5f57094a47c18d1849f7cd5e9306bf05d6187881 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Thu, 18 Jan 2018 14:50:28 +0300 Subject: Muse reader: refactor definition list parsing Test with wrong indentation is removed, because now it is parsed as nested lists. Emacs Muse and Text::Amuse don't have the same behavior anyway. --- src/Text/Pandoc/Readers/Muse.hs | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 5d032608c..b06b6e550 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -422,13 +422,17 @@ withListContext p = do updateState (\st -> st {stateParserContext = oldContext}) return parsed +listItemContents' :: PandocMonad m => Int -> MuseParser m (F Blocks) +listItemContents' col = do + first <- try $ withListContext parseBlock + rest <- many $ try (skipMany blankline >> indentWith col >> withListContext parseBlock) + return $ mconcat (first : rest) + listItemContents :: PandocMonad m => MuseParser m (F Blocks) listItemContents = do pos <- getPosition let col = sourceColumn pos - 1 - first <- try $ withListContext parseBlock - rest <- many $ try (skipMany blankline >> indentWith col >> withListContext parseBlock) - return $ mconcat (first : rest) + listItemContents' col listItem :: PandocMonad m => Int -> MuseParser m () -> MuseParser m (F Blocks) listItem n p = try $ do @@ -466,25 +470,19 @@ orderedList = try $ do definitionListItem :: PandocMonad m => MuseParser m (F (Inlines, [Blocks])) definitionListItem = try $ do - rawTerm <- termParser + guardDisabled Ext_amuse <|> void spaceChar -- Initial space is required by Amusewiki, but not Emacs Muse + many spaceChar + pos <- getPosition + rawTerm <- many1Till (noneOf "\n") (lookAhead (void (try (spaceChar >> string "::")))) term <- parseFromString (trimInlinesF . mconcat <$> many inline) rawTerm many1 spaceChar string "::" - firstLine <- manyTill anyChar eol - restLines <- manyTill anyLine endOfListItemElement - let lns = dropWhile (== ' ') firstLine : restLines - lineContent <- parseFromString (withListContext parseBlocks) $ unlines lns - pure $ do lineContent' <- lineContent + void spaceChar <|> lookAhead eol + contents <- listItemContents' $ sourceColumn pos + optionMaybe blankline + pure $ do lineContent' <- contents term' <- term pure (term', [lineContent']) - where - termParser = (guardDisabled Ext_amuse <|> void spaceChar) >> -- Initial space is required by Amusewiki, but not Emacs Muse - many spaceChar >> - many1Till (noneOf "\n") (lookAhead (void (try (spaceChar >> string "::")))) - endOfInput = lookAhead $ try $ skipMany blankline >> skipSpaces >> eof - twoBlankLines = try $ blankline >> skipMany1 blankline - newDefinitionListItem = try $ void termParser - endOfListItemElement = lookAhead $ endOfInput <|> newDefinitionListItem <|> twoBlankLines definitionListItems :: PandocMonad m => MuseParser m (F [(Inlines, [Blocks])]) definitionListItems = sequence <$> many1 definitionListItem -- cgit v1.2.3 From a516198d47bcc31e72e56e04bde976d9178142aa Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Thu, 18 Jan 2018 15:35:43 +0300 Subject: Muse reader: fix parsing of code at the beginning of paragraph --- src/Text/Pandoc/Readers/Muse.hs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index b06b6e550..d378fe676 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -718,17 +718,13 @@ nbsp = try $ do code :: PandocMonad m => MuseParser m (F Inlines) code = try $ do - pos <- getPosition - sp <- if sourceColumn pos == 1 - then pure mempty - else skipMany1 spaceChar >> pure B.space - char '=' + atStart $ char '=' contents <- many1Till (noneOf "\n\r" <|> (newline <* notFollowedBy newline)) $ char '=' guard $ not $ null contents guard $ head contents `notElem` " \t\n" guard $ last contents `notElem` " \t\n" notFollowedBy $ satisfy isLetter - return $ return (sp B.<> B.code contents) + return $ return $ B.code contents codeTag :: PandocMonad m => MuseParser m (F Inlines) codeTag = do -- cgit v1.2.3 From bfef2cbbf33ac1ebc2a1b90a78a9598b3bc76169 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Thu, 18 Jan 2018 08:17:09 -0500 Subject: Powerpoint writer: Add docProps to Presentation datatype. This picks up the necessary information from meta and carries it over to the XML output, so Output.hs doesn't need access to the original pandoc information. --- src/Text/Pandoc/Writers/Powerpoint/Output.hs | 10 +++--- src/Text/Pandoc/Writers/Powerpoint/Presentation.hs | 41 ++++++++++++++++++---- 2 files changed, 39 insertions(+), 12 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint/Output.hs b/src/Text/Pandoc/Writers/Powerpoint/Output.hs index 8ef5665fa..45ae86352 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Output.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Output.hs @@ -213,7 +213,7 @@ requiredFiles = [ "docProps/app.xml" presentationToArchiveP :: PandocMonad m => Presentation -> P m Archive -presentationToArchiveP p@(Presentation slides) = do +presentationToArchiveP p@(Presentation _ slides) = do filePaths <- patternsToFilePaths inheritedPatterns -- make sure all required files are available: @@ -247,7 +247,7 @@ presentationToArchiveP p@(Presentation slides) = do [contentTypesEntry, relsEntry, presEntry, presRelsEntry] makeSlideIdMap :: Presentation -> M.Map SlideId Int -makeSlideIdMap (Presentation slides) = +makeSlideIdMap (Presentation _ slides) = M.fromList $ (map slideId slides) `zip` [1..] presentationToArchive :: PandocMonad m => WriterOptions -> Presentation -> m Archive @@ -1142,7 +1142,7 @@ getRels = do return $ mapMaybe elementToRel relElems presentationToRels :: PandocMonad m => Presentation -> P m [Relationship] -presentationToRels (Presentation slides) = do +presentationToRels (Presentation _ slides) = do mySlideRels <- mapM slideToPresRel slides rels <- getRels let relsWithoutSlides = filter (\r -> relType r /= "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide") rels @@ -1288,7 +1288,7 @@ slideToSldIdElement slide = do return $ mknode "p:sldId" [("id", id'), ("r:id", rId)] () presentationToSldIdLst :: PandocMonad m => Presentation -> P m Element -presentationToSldIdLst (Presentation slides) = do +presentationToSldIdLst (Presentation _ slides) = do ids <- mapM slideToSldIdElement slides return $ mknode "p:sldIdLst" [] ids @@ -1384,7 +1384,7 @@ mediaContentType mInfo | otherwise = Nothing presentationToContentTypes :: PandocMonad m => Presentation -> P m ContentTypes -presentationToContentTypes (Presentation slides) = do +presentationToContentTypes (Presentation _ slides) = do mediaInfos <- (mconcat . M.elems) <$> gets stMediaIds filePaths <- patternsToFilePaths inheritedPatterns let mediaFps = filter (match (compile "ppt/media/image*")) filePaths diff --git a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs index 495675aad..1300bbe39 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs @@ -66,6 +66,7 @@ import Text.Pandoc.Slides (getSlideLevel) import Text.Pandoc.Options import Text.Pandoc.Logging import Text.Pandoc.Walk +import Text.Pandoc.Compat.Time (UTCTime) import qualified Text.Pandoc.Shared as Shared -- so we don't overlap "Element" import Text.Pandoc.Writers.Shared (metaValueToInlines) import qualified Data.Map as M @@ -161,9 +162,16 @@ concatMapM f xs = liftM concat (mapM f xs) type Pixels = Integer -data Presentation = Presentation [Slide] +data Presentation = Presentation DocProps [Slide] deriving (Show) +data DocProps = DocProps { dcTitle :: Maybe String + , dcSubject :: Maybe String + , dcCreator :: Maybe String + , dcKeywords :: Maybe [String] + , dcCreated :: Maybe UTCTime + } deriving (Show, Eq) + data Slide = Slide { slideId :: SlideId , slideLayout :: Layout @@ -796,8 +804,8 @@ replaceAnchor (Run rProps s) return $ Run rProps' s replaceAnchor pe = return pe -blocksToPresentation :: [Block] -> Pres Presentation -blocksToPresentation blks = do +blocksToPresentationSlides :: [Block] -> Pres [Slide] +blocksToPresentationSlides blks = do opts <- asks envOpts metadataslides <- maybeToList <$> getMetaSlide -- As far as I can tell, if we want to have a variable-length toc in @@ -836,17 +844,36 @@ blocksToPresentation blks = do return [endNotesSlide] let slides = metadataslides ++ tocSlides ++ bodyslides ++ endNotesSlides - slides' <- mapM (applyToSlide replaceAnchor) slides - return $ Presentation slides' + mapM (applyToSlide replaceAnchor) slides + +metaToDocProps :: Meta -> DocProps +metaToDocProps meta = + let keywords = case lookupMeta "keywords" meta of + Just (MetaList xs) -> Just $ map Shared.stringify xs + _ -> Nothing + + authors = case lookupMeta "author" meta of + Just (MetaList xs) -> Just $ map Shared.stringify xs + _ -> Nothing + in + DocProps{ dcTitle = Shared.stringify <$> lookupMeta "title" meta + , dcSubject = Shared.stringify <$> lookupMeta "subject" meta + , dcCreator = (intercalate "; ") <$> authors + , dcKeywords = keywords + , dcCreated = Nothing + } documentToPresentation :: WriterOptions -> Pandoc -> (Presentation, [LogMessage]) -documentToPresentation opts (Pandoc meta blks) = do +documentToPresentation opts (Pandoc meta blks) = let env = def { envOpts = opts , envMetadata = meta , envSlideLevel = case writerSlideLevel opts of Just lvl -> lvl Nothing -> getSlideLevel blks } - runPres env def $ blocksToPresentation blks + (presSlides, msgs) = runPres env def $ blocksToPresentationSlides blks + docProps = metaToDocProps meta + in + (Presentation docProps presSlides, msgs) -- cgit v1.2.3 From eae790485325ba6993b29d7b3ad638fefb1d21ee Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Thu, 18 Jan 2018 09:41:16 -0500 Subject: Powerpoint writer: Make our own docProps/core.xml file. This allows us to set document metadata properties from pandoc metadata. --- src/Text/Pandoc/Writers/Powerpoint/Output.hs | 39 ++++++++++++++++++---- src/Text/Pandoc/Writers/Powerpoint/Presentation.hs | 9 ++--- 2 files changed, 37 insertions(+), 11 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint/Output.hs b/src/Text/Pandoc/Writers/Powerpoint/Output.hs index 45ae86352..f0485adcc 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Output.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Output.hs @@ -40,6 +40,7 @@ import Control.Monad.State import Codec.Archive.Zip import Data.List (intercalate, stripPrefix, nub, union, isPrefixOf) import Data.Default +import Text.Pandoc.Compat.Time (formatTime, defaultTimeLocale) import Data.Time.Clock (UTCTime) import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds, posixSecondsToUTCTime) import System.FilePath.Posix (splitDirectories, splitExtension, takeExtension) @@ -54,7 +55,7 @@ import Text.Pandoc.MIME import qualified Data.ByteString.Lazy as BL import Text.Pandoc.Writers.OOXML import qualified Data.Map as M -import Data.Maybe (mapMaybe, listToMaybe) +import Data.Maybe (mapMaybe, listToMaybe, fromMaybe) import Text.Pandoc.ImageSize import Control.Applicative ((<|>)) import System.FilePath.Glob @@ -166,7 +167,6 @@ copyFileToArchive arch fp = do inheritedPatterns :: [Pattern] inheritedPatterns = map compile [ "docProps/app.xml" - , "docProps/core.xml" , "ppt/slideLayouts/slideLayout*.xml" , "ppt/slideLayouts/_rels/slideLayout*.xml.rels" , "ppt/slideMasters/slideMaster1.xml" @@ -194,7 +194,6 @@ patternsToFilePaths pats = concat <$> mapM patternToFilePaths pats -- any of these are missing, we should error out of our build. requiredFiles :: [FilePath] requiredFiles = [ "docProps/app.xml" - , "docProps/core.xml" , "ppt/presProps.xml" , "ppt/slideLayouts/slideLayout1.xml" , "ppt/slideLayouts/_rels/slideLayout1.xml.rels" @@ -213,7 +212,7 @@ requiredFiles = [ "docProps/app.xml" presentationToArchiveP :: PandocMonad m => Presentation -> P m Archive -presentationToArchiveP p@(Presentation _ slides) = do +presentationToArchiveP p@(Presentation docProps slides) = do filePaths <- patternsToFilePaths inheritedPatterns -- make sure all required files are available: @@ -226,6 +225,8 @@ presentationToArchiveP p@(Presentation _ slides) = do ) newArch' <- foldM copyFileToArchive emptyArchive filePaths + -- we make a docProps/core.xml entry out of the presentation docprops + docPropsEntry <- docPropsToEntry docProps -- we make this ourself in case there's something unexpected in the -- one in the reference doc. relsEntry <- topLevelRelsEntry @@ -244,7 +245,7 @@ presentationToArchiveP p@(Presentation _ slides) = do slideEntries ++ slideRelEntries ++ mediaEntries ++ - [contentTypesEntry, relsEntry, presEntry, presRelsEntry] + [contentTypesEntry, docPropsEntry, relsEntry, presEntry, presRelsEntry] makeSlideIdMap :: Presentation -> M.Map SlideId Int makeSlideIdMap (Presentation _ slides) = @@ -1313,7 +1314,30 @@ presentationToPresEntry :: PandocMonad m => Presentation -> P m Entry presentationToPresEntry pres = presentationToPresentationElement pres >>= elemToEntry "ppt/presentation.xml" - +-- adapted from the Docx writer +docPropsElement :: PandocMonad m => DocProps -> P m Element +docPropsElement docProps = do + utctime <- asks envUTCTime + let keywords = case dcKeywords docProps of + Just xs -> intercalate "," xs + Nothing -> "" + return $ + mknode "cp:coreProperties" + [("xmlns:cp","http://schemas.openxmlformats.org/package/2006/metadata/core-properties") + ,("xmlns:dc","http://purl.org/dc/elements/1.1/") + ,("xmlns:dcterms","http://purl.org/dc/terms/") + ,("xmlns:dcmitype","http://purl.org/dc/dcmitype/") + ,("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance")] + $ (mknode "dc:title" [] $ fromMaybe "" $ dcTitle docProps) + : (mknode "dc:creator" [] $ fromMaybe "" $ dcCreator docProps) + : (mknode "cp:keywords" [] keywords) + : (\x -> [ mknode "dcterms:created" [("xsi:type","dcterms:W3CDTF")] x + , mknode "dcterms:modified" [("xsi:type","dcterms:W3CDTF")] x + ]) (formatTime defaultTimeLocale "%FT%XZ" utctime) + +docPropsToEntry :: PandocMonad m => DocProps -> P m Entry +docPropsToEntry docProps = docPropsElement docProps >>= + elemToEntry "docProps/core.xml" defaultContentTypeToElem :: DefaultContentType -> Element @@ -1396,6 +1420,7 @@ presentationToContentTypes (Presentation _ slides) = do (mapMaybe mediaFileContentType $ mediaFps) inheritedOverrides = mapMaybe pathToOverride filePaths + docPropsOverride = mapMaybe pathToOverride ["docProps/core.xml"] presOverride = mapMaybe pathToOverride ["ppt/presentation.xml"] relativePaths <- mapM slideToFilePath slides let slideOverrides = mapMaybe @@ -1403,7 +1428,7 @@ presentationToContentTypes (Presentation _ slides) = do relativePaths return $ ContentTypes (defaults ++ mediaDefaults) - (inheritedOverrides ++ presOverride ++ slideOverrides) + (inheritedOverrides ++ docPropsOverride ++ presOverride ++ slideOverrides) presML :: String presML = "application/vnd.openxmlformats-officedocument.presentationml" diff --git a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs index 1300bbe39..e1192745f 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs @@ -34,6 +34,7 @@ Presentation. module Text.Pandoc.Writers.Powerpoint.Presentation ( documentToPresentation , Presentation(..) + , DocProps(..) , Slide(..) , Layout(..) , Notes(..) @@ -852,13 +853,13 @@ metaToDocProps meta = Just (MetaList xs) -> Just $ map Shared.stringify xs _ -> Nothing - authors = case lookupMeta "author" meta of - Just (MetaList xs) -> Just $ map Shared.stringify xs - _ -> Nothing + authors = case map Shared.stringify $ docAuthors meta of + [] -> Nothing + ss -> Just $ intercalate ";" ss in DocProps{ dcTitle = Shared.stringify <$> lookupMeta "title" meta , dcSubject = Shared.stringify <$> lookupMeta "subject" meta - , dcCreator = (intercalate "; ") <$> authors + , dcCreator = authors , dcKeywords = keywords , dcCreated = Nothing } -- cgit v1.2.3 From 63ae9076297de804b74c5dc945b2e853739a1c78 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Thu, 18 Jan 2018 11:49:18 -0500 Subject: Clean up T.P.W.OOXML file, and add copyright. --- src/Text/Pandoc/Writers/OOXML.hs | 81 ++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 41 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/OOXML.hs b/src/Text/Pandoc/Writers/OOXML.hs index aa4979653..2a9b9bc84 100644 --- a/src/Text/Pandoc/Writers/OOXML.hs +++ b/src/Text/Pandoc/Writers/OOXML.hs @@ -1,54 +1,54 @@ +{- +Copyright (C) 2012-2018 John MacFarlane <jgm@berkeley.edu> + +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.Writers.OOXML + Copyright : Copyright (C) 2012-2018 John MacFarlane + License : GNU GPL, version 2 or above + + Maintainer : John MacFarlane <jgm@berkeley.edu> + Stability : alpha + Portability : portable + +Functions common to OOXML writers (Docx and Powerpoint) +-} module Text.Pandoc.Writers.OOXML ( mknode - , nodename - , toLazy - , renderXml - , parseXml - , elemToNameSpaces - , elemName - , isElem - , NameSpaces - , fitToPage - ) where + , nodename + , toLazy + , renderXml + , parseXml + , elemToNameSpaces + , elemName + , isElem + , NameSpaces + , fitToPage + ) where + import Codec.Archive.Zip ---import Control.Applicative ((<|>)) --- import Control.Monad.Except (catchError) import Control.Monad.Reader --- import Control.Monad.State import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Lazy.Char8 as BL8 --- import Data.Char (isSpace, ord, toLower) --- import Data.List (intercalate, isPrefixOf, isSuffixOf) --- import qualified Data.Map as M import Data.Maybe (mapMaybe) import Data.Monoid ((<>)) --- import qualified Data.Set as Set --- import qualified Data.Text as T --- import Data.Time.Clock.POSIX --- import Skylighting --- import System.Random (randomR) import Text.Pandoc.Class (PandocMonad) --- import qualified Text.Pandoc.Class as P --- import Text.Pandoc.Compat.Time --- import Text.Pandoc.Definition --- import Text.Pandoc.Generic --- import Text.Pandoc.Highlighting (highlight) --- import Text.Pandoc.ImageSize --- import Text.Pandoc.Logging --- import Text.Pandoc.MIME (MimeType, extensionFromMimeType, getMimeType, --- getMimeTypeDef) --- import Text.Pandoc.Options --- import Text.Pandoc.Readers.Docx.StyleMap --- import Text.Pandoc.Shared hiding (Element) import qualified Text.Pandoc.UTF8 as UTF8 --- import Text.Pandoc.Walk --- import Text.Pandoc.Writers.Math --- import Text.Pandoc.Writers.Shared (fixDisplayMath) --- import Text.Printf (printf) --- import Text.TeXMath import Text.XML.Light as XML --- import Text.XML.Light.Cursor as XMLC - mknode :: Node t => String -> [(String,String)] -> t -> Element mknode s attrs = @@ -106,4 +106,3 @@ fitToPage (x, y) pageWidth | x > fromIntegral pageWidth = (pageWidth, floor $ ((fromIntegral pageWidth) / x) * y) | otherwise = (floor x, floor y) - -- cgit v1.2.3 From d0a895acee371b13a9c31873c10dd124e04564d1 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Thu, 18 Jan 2018 12:34:19 -0500 Subject: Powerpoint writer: Implement syntax highlighting This also necessitated implementing colors and underlining, though there is currently no way to produce these from markdown. Note that background colors can't be implemented in PowerPoint, so highlighting styles that require these will be incomplete. --- src/Text/Pandoc/Writers/Powerpoint/Output.hs | 57 +++++++++++++--------- src/Text/Pandoc/Writers/Powerpoint/Presentation.hs | 49 ++++++++++++++++++- 2 files changed, 82 insertions(+), 24 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint/Output.hs b/src/Text/Pandoc/Writers/Powerpoint/Output.hs index f0485adcc..d30819d47 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Output.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Output.hs @@ -38,6 +38,7 @@ import Control.Monad.Except (throwError, catchError) import Control.Monad.Reader import Control.Monad.State import Codec.Archive.Zip +import Data.Char (toUpper) import Data.List (intercalate, stripPrefix, nub, union, isPrefixOf) import Data.Default import Text.Pandoc.Compat.Time (formatTime, defaultTimeLocale) @@ -62,6 +63,7 @@ import System.FilePath.Glob import Text.TeXMath import Text.Pandoc.Writers.Math (convertMath) import Text.Pandoc.Writers.Powerpoint.Presentation +import Skylighting (fromColor) -- This populates the global ids map with images already in the -- template, so the ids won't be used by images introduced by the @@ -703,26 +705,28 @@ paraElemToElement Break = return $ mknode "a:br" [] () paraElemToElement (Run rpr s) = do let sizeAttrs = case rPropForceSize rpr of Just n -> [("sz", (show $ n * 100))] - Nothing -> [] + Nothing -> if rPropCode rpr + -- hardcoded size for code for now + then [("sz", "1800")] + else [] attrs = sizeAttrs ++ - if rPropCode rpr - then [] - else (if rPropBold rpr then [("b", "1")] else []) ++ - (if rPropItalics rpr then [("i", "1")] else []) ++ - (case rStrikethrough rpr of - Just NoStrike -> [("strike", "noStrike")] - Just SingleStrike -> [("strike", "sngStrike")] - Just DoubleStrike -> [("strike", "dblStrike")] - Nothing -> []) ++ - (case rBaseline rpr of - Just n -> [("baseline", show n)] - Nothing -> []) ++ - (case rCap rpr of - Just NoCapitals -> [("cap", "none")] - Just SmallCapitals -> [("cap", "small")] - Just AllCapitals -> [("cap", "all")] - Nothing -> []) ++ - [] + (if rPropBold rpr then [("b", "1")] else []) ++ + (if rPropItalics rpr then [("i", "1")] else []) ++ + (if rPropUnderline rpr then [("u", "sng")] else []) ++ + (case rStrikethrough rpr of + Just NoStrike -> [("strike", "noStrike")] + Just SingleStrike -> [("strike", "sngStrike")] + Just DoubleStrike -> [("strike", "dblStrike")] + Nothing -> []) ++ + (case rBaseline rpr of + Just n -> [("baseline", show n)] + Nothing -> []) ++ + (case rCap rpr of + Just NoCapitals -> [("cap", "none")] + Just SmallCapitals -> [("cap", "small")] + Just AllCapitals -> [("cap", "all")] + Nothing -> []) ++ + [] linkProps <- case rLink rpr of Just link -> do idNum <- registerLink link @@ -743,10 +747,19 @@ paraElemToElement (Run rpr s) = do ] in [mknode "a:hlinkClick" linkAttrs ()] Nothing -> return [] - let propContents = if rPropCode rpr + let colorContents = case rSolidFill rpr of + Just color -> + case fromColor color of + '#':hx -> [mknode "a:solidFill" [] + [mknode "a:srgbClr" [("val", map toUpper hx)] ()] + ] + _ -> [] + Nothing -> [] + let codeContents = if rPropCode rpr then [mknode "a:latin" [("typeface", "Courier")] ()] - else linkProps - return $ mknode "a:r" [] [ mknode "a:rPr" attrs propContents + else [] + let propContents = linkProps ++ colorContents ++ codeContents + return $ mknode "a:r" [] [ mknode "a:rPr" attrs $ propContents , mknode "a:t" [] s ] paraElemToElement (MathElem mathType texStr) = do diff --git a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs index e1192745f..f5f7d850f 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs @@ -73,6 +73,10 @@ import Text.Pandoc.Writers.Shared (metaValueToInlines) import qualified Data.Map as M import qualified Data.Set as S import Data.Maybe (maybeToList) +import Text.Pandoc.Highlighting +import qualified Data.Text as T +import Control.Applicative ((<|>)) +import Skylighting data WriterEnv = WriterEnv { envMetadata :: Meta , envRunProps :: RunProps @@ -280,6 +284,10 @@ data RunProps = RunProps { rPropBold :: Bool , rPropCode :: Bool , rPropBlockQuote :: Bool , rPropForceSize :: Maybe Pixels + , rSolidFill :: Maybe Color + -- TODO: Make a full underline data type with + -- the different options. + , rPropUnderline :: Bool } deriving (Show, Eq) instance Default RunProps where @@ -292,6 +300,8 @@ instance Default RunProps where , rPropCode = False , rPropBlockQuote = False , rPropForceSize = Nothing + , rSolidFill = Nothing + , rPropUnderline = False } data PicProps = PicProps { picPropLink :: Maybe LinkTarget @@ -391,8 +401,17 @@ blockToParagraphs (LineBlock ilsList) = do return [Paragraph pProps parElems] -- TODO: work out the attributes blockToParagraphs (CodeBlock attr str) = - local (\r -> r{envParaProps = def{pPropMarginLeft = Just 100}}) $ - blockToParagraphs $ Para [Code attr str] + local (\r -> r{ envParaProps = def{pPropMarginLeft = Just 100} + , envRunProps = (envRunProps r){rPropCode = True}}) $ do + mbSty <- writerHighlightStyle <$> asks envOpts + synMap <- writerSyntaxMap <$> asks envOpts + case mbSty of + Just sty -> + case highlight synMap (formatSourceLines sty) attr str of + Right pElems -> do pProps <- asks envParaProps + return $ [Paragraph pProps pElems] + Left _ -> blockToParagraphs $ Para [Str str] + Nothing -> blockToParagraphs $ Para [Str str] -- We can't yet do incremental lists, but we should render a -- (BlockQuote List) as a list to maintain compatibility with other -- formats. @@ -878,3 +897,29 @@ documentToPresentation opts (Pandoc meta blks) = docProps = metaToDocProps meta in (Presentation docProps presSlides, msgs) + +-- -------------------------------------------------------------- + +applyTokStyToRunProps :: TokenStyle -> RunProps -> RunProps +applyTokStyToRunProps tokSty rProps = + rProps{ rSolidFill = tokenColor tokSty <|> rSolidFill rProps + , rPropBold = tokenBold tokSty || rPropBold rProps + , rPropItalics = tokenItalic tokSty || rPropItalics rProps + , rPropUnderline = tokenUnderline tokSty || rPropUnderline rProps + } + +formatToken :: Style -> Token -> ParaElem +formatToken sty (tokType, txt) = + let rProps = def{rPropCode = True, rSolidFill = defaultColor sty} + rProps' = case M.lookup tokType (tokenStyles sty) of + Just tokSty -> applyTokStyToRunProps tokSty rProps + Nothing -> rProps + in + Run rProps' $ T.unpack txt + +formatSourceLine :: Style -> FormatOptions -> SourceLine -> [ParaElem] +formatSourceLine sty _ srcLn = map (formatToken sty) srcLn + +formatSourceLines :: Style -> FormatOptions -> [SourceLine] -> [ParaElem] +formatSourceLines sty opts srcLns = intercalate [Break] $ + map (formatSourceLine sty opts) srcLns -- cgit v1.2.3 From 7e2c75c8653eb9ec64a19ac7d6f4677936fbb13b Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Wed, 17 Jan 2018 12:30:15 +0300 Subject: Muse reader: do not remove trailing whitespace from <code> --- src/Text/Pandoc/Readers/Muse.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index d378fe676..f0ac81f01 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -728,7 +728,7 @@ code = try $ do codeTag :: PandocMonad m => MuseParser m (F Inlines) codeTag = do - (attrs, content) <- parseHtmlContentWithAttrs "code" anyChar + (attrs, content) <- htmlElement "code" return $ return $ B.codeWith attrs content inlineLiteralTag :: PandocMonad m => MuseParser m (F Inlines) -- cgit v1.2.3 From 19d257622356e603d56f3d4350b586420ed63dcb Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 19 Jan 2018 01:41:16 +0300 Subject: Muse reader: parse definition list terms without parseFromString --- src/Text/Pandoc/Readers/Muse.hs | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index f0ac81f01..7067d8abc 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -473,10 +473,7 @@ definitionListItem = try $ do guardDisabled Ext_amuse <|> void spaceChar -- Initial space is required by Amusewiki, but not Emacs Muse many spaceChar pos <- getPosition - rawTerm <- many1Till (noneOf "\n") (lookAhead (void (try (spaceChar >> string "::")))) - term <- parseFromString (trimInlinesF . mconcat <$> many inline) rawTerm - many1 spaceChar - string "::" + term <- trimInlinesF . mconcat <$> manyTill (choice inlineList) (string "::") void spaceChar <|> lookAhead eol contents <- listItemContents' $ sourceColumn pos optionMaybe blankline @@ -587,7 +584,7 @@ tableParseCaption = try $ do -- inlineList :: PandocMonad m => [MuseParser m (F Inlines)] -inlineList = [ endline +inlineList = [ whitespace , br , anchor , footnote @@ -605,13 +602,12 @@ inlineList = [ endline , code , codeTag , inlineLiteralTag - , whitespace , str , symbol ] inline :: PandocMonad m => MuseParser m (F Inlines) -inline = choice inlineList <?> "inline" +inline = choice [endline, linebreak] <|> choice inlineList <?> "inline" endline :: PandocMonad m => MuseParser m (F Inlines) endline = try $ do @@ -645,23 +641,23 @@ footnote = try $ do let contents' = runF contents st { stateNotes' = M.empty } return $ B.note contents' +linebreak :: PandocMonad m => MuseParser m (F Inlines) +linebreak = try $ do + skipMany spaceChar + newline + notFollowedBy newline + return $ return B.space + whitespace :: PandocMonad m => MuseParser m (F Inlines) -whitespace = return <$> (lb <|> regsp) - where lb = try $ skipMany spaceChar >> linebreak >> return B.space - regsp = try $ skipMany1 spaceChar >> return B.space +whitespace = try $ do + skipMany1 spaceChar + return $ return B.space br :: PandocMonad m => MuseParser m (F Inlines) br = try $ do string "<br>" return $ return B.linebreak -linebreak :: PandocMonad m => MuseParser m (F Inlines) -linebreak = newline >> notFollowedBy newline >> (lastNewline <|> innerNewline) - where lastNewline = do - eof - return $ return mempty - innerNewline = return $ return B.space - emphasisBetween :: (PandocMonad m, Show a) => MuseParser m a -> MuseParser m (F Inlines) emphasisBetween c = try $ enclosedInlines c c -- cgit v1.2.3 From c36c02e58d3262a117c00681d4a3525c96eaf383 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 19 Jan 2018 11:12:19 +0300 Subject: Muse reader: simplify tableParseCaption --- src/Text/Pandoc/Readers/Muse.hs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 7067d8abc..8455f983c 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -575,9 +575,7 @@ tableParseCaption :: PandocMonad m => MuseParser m MuseTableElement tableParseCaption = try $ do many spaceChar string "|+" - contents <- trimInlinesF . mconcat <$> many1Till inline (lookAhead $ string "+|") - string "+|" - return $ MuseCaption contents + MuseCaption <$> (trimInlinesF . mconcat <$> many1Till inline (string "+|")) -- -- inline parsers -- cgit v1.2.3 From fc5d79b6e9753a620b4e6754b8a5e1ea1163f119 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 19 Jan 2018 11:14:15 +0300 Subject: Muse reader: parse link text without parseFromString --- src/Text/Pandoc/Readers/Muse.hs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 8455f983c..2d424ec81 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -763,8 +763,7 @@ link = try $ do linkContent :: PandocMonad m => MuseParser m (F Inlines) linkContent = do char '[' - res <- many1Till anyChar $ char ']' - parseFromString (mconcat <$> many1 inline) res + trimInlinesF . mconcat <$> many1Till inline (string "]") linkText :: PandocMonad m => MuseParser m (String, String, Maybe (F Inlines)) linkText = do -- cgit v1.2.3 From 9e21ae15e1d84f1a2c07bdd1961d9465557ecd63 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 19 Jan 2018 11:29:11 +0300 Subject: Muse reader: parse Emacs Muse directives without parseFromString Also require space (not newline) after directive name. --- src/Text/Pandoc/Readers/Muse.hs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 2d424ec81..65d97814d 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -156,10 +156,8 @@ parseDirectiveKey = do parseEmacsDirective :: PandocMonad m => MuseParser m (String, F Inlines) parseEmacsDirective = do key <- parseDirectiveKey - space - spaces - raw <- manyTill anyChar eol - value <- parseFromString (trimInlinesF . mconcat <$> many inline) raw + spaceChar + value <- trimInlinesF . mconcat <$> manyTill (choice inlineList) eol return (key, value) parseAmuseDirective :: PandocMonad m => MuseParser m (String, F Inlines) -- cgit v1.2.3 From 4d3f787d6365006021e637d301904a7a68646861 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 19 Jan 2018 11:36:57 +0300 Subject: Muse reader: remove commented out code --- src/Text/Pandoc/Readers/Muse.hs | 1 - 1 file changed, 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 65d97814d..4d5f9ba90 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -321,7 +321,6 @@ verseLine = do verseLines :: PandocMonad m => MuseParser m (F Blocks) verseLines = do - --optionMaybe blankline -- Skip blankline after opening tag on separate line lns <- many verseLine lns' <- mapM (parseFromString' (trimInlinesF . mconcat <$> many inline)) lns return $ B.lineBlock <$> sequence lns' -- cgit v1.2.3 From 4a5801b823e1fbd3d5b208b97acce78859c45b0d Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 19 Jan 2018 12:12:06 +0300 Subject: Muse reader: parse verse without parseFromString --- src/Text/Pandoc/Readers/Muse.hs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 4d5f9ba90..a23ac239c 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -390,21 +390,23 @@ emacsNoteBlock = try $ do -- Verse markup -- -lineVerseLine :: PandocMonad m => MuseParser m String +lineVerseLine :: PandocMonad m => MuseParser m (F Inlines) lineVerseLine = try $ do - char '>' - white <- many1 (char ' ' >> pure '\160') - rest <- anyLine - return $ tail white ++ rest + string "> " + indent <- B.str <$> many (char ' ' >> pure '\160') + rest <- manyTill (choice inlineList) eol + return $ trimInlinesF $ mconcat (pure indent : rest) -blanklineVerseLine :: PandocMonad m => MuseParser m Char -blanklineVerseLine = try $ char '>' >> blankline +blanklineVerseLine :: PandocMonad m => MuseParser m (F Inlines) +blanklineVerseLine = try $ do + char '>' + blankline + pure mempty lineBlock :: PandocMonad m => MuseParser m (F Blocks) lineBlock = try $ do - lns <- many1 (pure <$> blanklineVerseLine <|> lineVerseLine) - lns' <- mapM (parseFromString' (trimInlinesF . mconcat <$> many inline)) lns - return $ B.lineBlock <$> sequence lns' + lns <- many1 (blanklineVerseLine <|> lineVerseLine) + return $ B.lineBlock <$> sequence lns -- -- lists -- cgit v1.2.3 From 7680e9b9642c1bade4152e4c833003688c439ca1 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 19 Jan 2018 14:16:08 +0300 Subject: Muse reader: require only one space for nested definition list indentation --- src/Text/Pandoc/Readers/Muse.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index a23ac239c..8bef5b539 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -469,8 +469,9 @@ orderedList = try $ do definitionListItem :: PandocMonad m => MuseParser m (F (Inlines, [Blocks])) definitionListItem = try $ do - guardDisabled Ext_amuse <|> void spaceChar -- Initial space is required by Amusewiki, but not Emacs Muse many spaceChar + startPos <- getPosition + (guardDisabled Ext_amuse) <|> (guard (sourceColumn startPos /= 1)) -- Initial space is required by Amusewiki, but not Emacs Muse pos <- getPosition term <- trimInlinesF . mconcat <$> manyTill (choice inlineList) (string "::") void spaceChar <|> lookAhead eol -- cgit v1.2.3 From 01499b766b16f4ab1c7ce4e3e82780a099c6dd37 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 19 Jan 2018 14:58:15 +0300 Subject: Muse writer: support definitions with multiple descriptions Muse reader does not support this syntax yet, but Emacs Muse parses it correctly. --- src/Text/Pandoc/Writers/Muse.hs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index 163cb2dda..7c4865da8 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -212,10 +212,13 @@ blockToMuse (DefinitionList items) = do -> StateT WriterState m Doc definitionListItemToMuse (label, defs) = do label' <- inlineListToMuse label - contents <- liftM vcat $ mapM blockListToMuse defs - let label'' = label' <> " :: " - let ind = offset label'' - return $ hang ind label'' contents + contents <- liftM vcat $ mapM descriptionToMuse defs + let ind = offset label' + return $ hang ind label' contents + descriptionToMuse :: PandocMonad m + => [Block] + -> StateT WriterState m Doc + descriptionToMuse desc = (hang 4 " :: ") <$> blockListToMuse desc blockToMuse (Header level (ident,_,_) inlines) = do opts <- gets stOptions contents <- inlineListToMuse inlines -- cgit v1.2.3 From 2165efef7e3608b5c8e7f144b4f3884635410bc5 Mon Sep 17 00:00:00 2001 From: danse <f.occhipinti@gmail.com> Date: Mon, 15 Jan 2018 12:24:20 +0100 Subject: in RST writer insert comment between lists and quotes, closes #4248 --- src/Text/Pandoc/Writers/RST.hs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index 2b28dccf0..694d623a6 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -353,9 +353,20 @@ blockListToRST' :: PandocMonad m -> [Block] -- ^ List of block elements -> RST m Doc blockListToRST' topLevel blocks = do + -- insert comment between list and quoted blocks, see #4248 + let fixBlocks (b1:b2@(BlockQuote _):bs) + | isList b1 = b1 : commentSep : b2 : fixBlocks bs + where + isList (BulletList _) = True + isList (OrderedList _ _) = True + isList (DefinitionList _) = True + isList _ = False + commentSep = RawBlock "rst" "" + fixBlocks (b:bs) = b : fixBlocks bs + fixBlocks [] = [] tl <- gets stTopLevel modify (\s->s{stTopLevel=topLevel, stLastNested=False}) - res <- vcat `fmap` mapM blockToRST' blocks + res <- vcat `fmap` mapM blockToRST' (fixBlocks blocks) modify (\s->s{stTopLevel=tl}) return res -- cgit v1.2.3 From a0ee8420967c1973e1aef0b94ceebc2ce10cb0d8 Mon Sep 17 00:00:00 2001 From: danse <f.occhipinti@gmail.com> Date: Fri, 19 Jan 2018 16:32:08 +0100 Subject: remove `blockToRST'` moving its logic into `fixBlocks` --- src/Text/Pandoc/Writers/RST.hs | 45 ++++++++++++------------------------------ 1 file changed, 13 insertions(+), 32 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index 694d623a6..a57527aa8 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -57,7 +57,6 @@ data WriterState = , stHasRawTeX :: Bool , stOptions :: WriterOptions , stTopLevel :: Bool - , stLastNested :: Bool } type RST = StateT WriterState @@ -68,7 +67,7 @@ writeRST opts document = do let st = WriterState { stNotes = [], stLinks = [], stImages = [], stHasMath = False, stHasRawTeX = False, stOptions = opts, - stTopLevel = True, stLastNested = False} + stTopLevel = True } evalStateT (pandocToRST document) st -- | Return RST representation of document. @@ -353,44 +352,26 @@ blockListToRST' :: PandocMonad m -> [Block] -- ^ List of block elements -> RST m Doc blockListToRST' topLevel blocks = do - -- insert comment between list and quoted blocks, see #4248 + -- insert comment between list and quoted blocks, see #4248 and #3675 let fixBlocks (b1:b2@(BlockQuote _):bs) - | isList b1 = b1 : commentSep : b2 : fixBlocks bs + | toClose b1 = b1 : commentSep : b2 : fixBlocks bs where - isList (BulletList _) = True - isList (OrderedList _ _) = True - isList (DefinitionList _) = True - isList _ = False - commentSep = RawBlock "rst" "" + toClose (Plain{}) = False + toClose (Header{}) = False + toClose (LineBlock{}) = False + toClose (HorizontalRule) = False + toClose (Para [Image _ _ (_,'f':'i':'g':':':_)]) = True + toClose (Para{}) = False + toClose _ = True + commentSep = RawBlock "rst" "..\n\n" fixBlocks (b:bs) = b : fixBlocks bs fixBlocks [] = [] tl <- gets stTopLevel - modify (\s->s{stTopLevel=topLevel, stLastNested=False}) - res <- vcat `fmap` mapM blockToRST' (fixBlocks blocks) + modify (\s->s{stTopLevel=topLevel}) + res <- vcat `fmap` mapM blockToRST (fixBlocks blocks) modify (\s->s{stTopLevel=tl}) return res -blockToRST' :: PandocMonad m => Block -> RST m Doc -blockToRST' (x@BlockQuote{}) = do - lastNested <- gets stLastNested - res <- blockToRST x - modify (\s -> s{stLastNested = True}) - return $ if lastNested - then ".." $+$ res - else res -blockToRST' x = do - modify (\s -> s{stLastNested = - case x of - Para [Image _ _ (_,'f':'i':'g':':':_)] -> True - Para{} -> False - Plain{} -> False - Header{} -> False - LineBlock{} -> False - HorizontalRule -> False - _ -> True - }) - blockToRST x - blockListToRST :: PandocMonad m => [Block] -- ^ List of block elements -> RST m Doc -- cgit v1.2.3 From f019d3cc45e595710eff9a1c380dbcee9f02c93a Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 19 Jan 2018 19:33:04 +0300 Subject: Muse reader: remove duplicate variable in definitionListItem --- src/Text/Pandoc/Readers/Muse.hs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 8bef5b539..074f1e65a 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -470,9 +470,8 @@ orderedList = try $ do definitionListItem :: PandocMonad m => MuseParser m (F (Inlines, [Blocks])) definitionListItem = try $ do many spaceChar - startPos <- getPosition - (guardDisabled Ext_amuse) <|> (guard (sourceColumn startPos /= 1)) -- Initial space is required by Amusewiki, but not Emacs Muse pos <- getPosition + (guardDisabled Ext_amuse) <|> (guard (sourceColumn pos /= 1)) -- Initial space is required by Amusewiki, but not Emacs Muse term <- trimInlinesF . mconcat <$> manyTill (choice inlineList) (string "::") void spaceChar <|> lookAhead eol contents <- listItemContents' $ sourceColumn pos -- cgit v1.2.3 From 62c395dafa467ea760cdc938993ef61379e07ace Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 19 Jan 2018 11:39:27 -0800 Subject: LaTeX reader: fixed parsing of tabular* environment. This was just a typo in the source. Closes #4279. --- 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 6298e0b2f..3408201eb 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -2105,7 +2105,7 @@ environments = M.fromList resetCaption *> simpTable "longtable" False >>= addTableCaption) , ("table", env "table" $ resetCaption *> skipopts *> blocks >>= addTableCaption) - , ("tabular*", env "tabular" $ simpTable "tabular*" True) + , ("tabular*", env "tabular*" $ simpTable "tabular*" True) , ("tabularx", env "tabularx" $ simpTable "tabularx" True) , ("tabular", env "tabular" $ simpTable "tabular" False) , ("quote", blockQuote <$> env "quote" blocks) -- cgit v1.2.3 From 22b69b557ecd9a86caa8628b9a395bdd86b55035 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 19 Jan 2018 16:05:14 +0300 Subject: Muse reader: fix parsing of nested definition lists --- src/Text/Pandoc/Readers/Muse.hs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 074f1e65a..4c6d1278e 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -467,26 +467,25 @@ orderedList = try $ do rest <- many $ listItem (col - 1) (void (orderedListMarker style delim)) return $ B.orderedListWith p <$> sequence (first : rest) -definitionListItem :: PandocMonad m => MuseParser m (F (Inlines, [Blocks])) -definitionListItem = try $ do - many spaceChar +definitionListItem :: PandocMonad m => Int -> MuseParser m (F (Inlines, [Blocks])) +definitionListItem n = try $ do + count n spaceChar pos <- getPosition - (guardDisabled Ext_amuse) <|> (guard (sourceColumn pos /= 1)) -- Initial space is required by Amusewiki, but not Emacs Muse term <- trimInlinesF . mconcat <$> manyTill (choice inlineList) (string "::") void spaceChar <|> lookAhead eol contents <- listItemContents' $ sourceColumn pos - optionMaybe blankline pure $ do lineContent' <- contents term' <- term pure (term', [lineContent']) -definitionListItems :: PandocMonad m => MuseParser m (F [(Inlines, [Blocks])]) -definitionListItems = sequence <$> many1 definitionListItem - definitionList :: PandocMonad m => MuseParser m (F Blocks) -definitionList = do - items <- definitionListItems - return $ B.definitionList <$> items +definitionList = try $ do + many spaceChar + pos <- getPosition + (guardDisabled Ext_amuse) <|> (guard (sourceColumn pos /= 1)) -- Initial space is required by Amusewiki, but not Emacs Muse + first <- definitionListItem 0 + rest <- many $ try (optionMaybe blankline >> definitionListItem (sourceColumn pos - 1)) + return $ B.definitionList <$> sequence (first : rest) -- -- tables -- cgit v1.2.3 From b8ffd834cff717fe424f22e506351f2ecec4655a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 19 Jan 2018 21:25:24 -0800 Subject: hlint code improvements. --- src/Text/Pandoc/Class.hs | 6 +- src/Text/Pandoc/Filter.hs | 2 +- src/Text/Pandoc/Lua.hs | 2 +- src/Text/Pandoc/Lua/Packages.hs | 1 - src/Text/Pandoc/Lua/StackInstances.hs | 1 - src/Text/Pandoc/Parsing.hs | 24 +++---- src/Text/Pandoc/Readers/Docx.hs | 16 ++--- src/Text/Pandoc/Readers/Docx/Fields.hs | 8 +-- src/Text/Pandoc/Readers/Docx/Lists.hs | 8 +-- src/Text/Pandoc/Readers/Docx/Parse.hs | 11 ++-- src/Text/Pandoc/Readers/HTML.hs | 8 +-- src/Text/Pandoc/Readers/JATS.hs | 1 - src/Text/Pandoc/Readers/LaTeX.hs | 20 +++--- src/Text/Pandoc/Readers/Markdown.hs | 58 ++++++++--------- src/Text/Pandoc/Readers/Muse.hs | 2 +- src/Text/Pandoc/Readers/Odt/Arrows/Utils.hs | 4 +- src/Text/Pandoc/Readers/Odt/ContentReader.hs | 19 +++--- src/Text/Pandoc/Readers/Odt/Namespaces.hs | 2 +- src/Text/Pandoc/Readers/Odt/StyleReader.hs | 43 ++++++------- src/Text/Pandoc/Readers/Org/Blocks.hs | 2 +- src/Text/Pandoc/Readers/RST.hs | 8 +-- src/Text/Pandoc/Readers/Textile.hs | 13 ++-- src/Text/Pandoc/Readers/TikiWiki.hs | 14 ++-- src/Text/Pandoc/Readers/Txt2Tags.hs | 4 +- src/Text/Pandoc/Readers/Vimwiki.hs | 4 +- src/Text/Pandoc/Shared.hs | 2 +- src/Text/Pandoc/Writers/AsciiDoc.hs | 5 +- src/Text/Pandoc/Writers/Docx.hs | 13 ++-- src/Text/Pandoc/Writers/FB2.hs | 3 +- src/Text/Pandoc/Writers/HTML.hs | 13 ++-- src/Text/Pandoc/Writers/Haddock.hs | 3 +- src/Text/Pandoc/Writers/ICML.hs | 4 +- src/Text/Pandoc/Writers/LaTeX.hs | 8 +-- src/Text/Pandoc/Writers/Man.hs | 2 +- src/Text/Pandoc/Writers/Muse.hs | 2 +- src/Text/Pandoc/Writers/OOXML.hs | 2 +- src/Text/Pandoc/Writers/OpenDocument.hs | 2 +- src/Text/Pandoc/Writers/Powerpoint.hs | 2 +- src/Text/Pandoc/Writers/Powerpoint/Presentation.hs | 74 +++++++++++----------- src/Text/Pandoc/Writers/RST.hs | 15 ++--- src/Text/Pandoc/Writers/RTF.hs | 8 +-- src/Text/Pandoc/Writers/Texinfo.hs | 2 +- 42 files changed, 207 insertions(+), 234 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index f8d6b6737..ae538046a 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -142,11 +142,11 @@ import qualified System.Environment as IO (lookupEnv) import System.FilePath.Glob (match, compile) import System.Directory (createDirectoryIfMissing, getDirectoryContents, doesDirectoryExist) -import System.FilePath ((</>), (<.>), takeDirectory, - takeExtension, dropExtension, isRelative, normalise) +import System.FilePath + ((</>), (<.>), takeDirectory, takeExtension, dropExtension, + isRelative, normalise, splitDirectories) import qualified System.FilePath.Glob as IO (glob) import qualified System.FilePath.Posix as Posix -import System.FilePath (splitDirectories) import qualified System.Directory as IO (getModificationTime) import Control.Monad as M (fail) import Control.Monad.State.Strict diff --git a/src/Text/Pandoc/Filter.hs b/src/Text/Pandoc/Filter.hs index 67b3a5f2c..e2a3c3e16 100644 --- a/src/Text/Pandoc/Filter.hs +++ b/src/Text/Pandoc/Filter.hs @@ -51,7 +51,7 @@ applyFilters :: ReaderOptions -> [String] -> Pandoc -> PandocIO Pandoc -applyFilters ropts filters args d = do +applyFilters ropts filters args d = foldrM ($) d $ map applyFilter filters where applyFilter (JSONFilter f) = JSONFilter.apply ropts args f diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index edf803b45..790be47d5 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -65,7 +65,7 @@ runLuaFilter' ropts filterPath format pd = do newtop <- Lua.gettop -- Use the returned filters, or the implicitly defined global filter if -- nothing was returned. - luaFilters <- if (newtop - top >= 1) + luaFilters <- if newtop - top >= 1 then peek (-1) else Lua.getglobal "_G" *> fmap (:[]) popValue runAll luaFilters pd diff --git a/src/Text/Pandoc/Lua/Packages.hs b/src/Text/Pandoc/Lua/Packages.hs index 0169d0045..1e6ff22fe 100644 --- a/src/Text/Pandoc/Lua/Packages.hs +++ b/src/Text/Pandoc/Lua/Packages.hs @@ -113,4 +113,3 @@ dataDirScript datadir moduleFile = do return $ case res of Left _ -> Nothing Right s -> Just (unpack s) - diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index 38404157c..a504e5626 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -383,4 +383,3 @@ instance ToLuaStack ReaderOptions where LuaUtil.addValue "defaultImageExtension" defaultImageExtension LuaUtil.addValue "trackChanges" trackChanges LuaUtil.addValue "stripComments" stripComments - diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index f1b823965..e87ea71da 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -189,12 +189,12 @@ where import Control.Monad.Identity import Control.Monad.Reader -import Data.Char (chr, isAlphaNum, isAscii, isAsciiUpper, isHexDigit, isPunctuation, isSpace, - ord, toLower, toUpper) +import Data.Char (chr, isAlphaNum, isAscii, isAsciiUpper, isHexDigit, + isPunctuation, isSpace, ord, toLower, toUpper) import Data.Default import Data.List (intercalate, isSuffixOf, transpose) import qualified Data.Map as M -import Data.Maybe (mapMaybe) +import Data.Maybe (mapMaybe, fromMaybe) import Data.Monoid ((<>)) import qualified Data.Set as Set import Data.Text (Text) @@ -304,7 +304,7 @@ indentWith :: Stream s m Char => Int -> ParserT s st m [Char] indentWith num = do tabStop <- getOption readerTabStop - if (num < tabStop) + if num < tabStop then count num (char ' ') else choice [ try (count num (char ' ')) , try (char '\t' >> indentWith (num - tabStop)) ] @@ -573,7 +573,7 @@ uri = try $ do let uriChunk = skipMany1 wordChar <|> percentEscaped <|> entity - <|> (try $ punct >> + <|> try (punct >> lookAhead (void (satisfy isWordChar) <|> percentEscaped)) str <- snd <$> withRaw (skipMany1 ( () <$ (enclosed (char '(') (char ')') uriChunk @@ -755,7 +755,7 @@ romanOne = (char 'i' >> return (LowerRoman, 1)) <|> -- | Parses an ordered list marker and returns list attributes. anyOrderedListMarker :: Stream s m Char => ParserT s ParserState m ListAttributes -anyOrderedListMarker = choice $ +anyOrderedListMarker = choice [delimParser numParser | delimParser <- [inPeriod, inOneParen, inTwoParens], numParser <- [decimal, exampleNum, defaultNum, romanOne, lowerAlpha, lowerRoman, upperAlpha, upperRoman]] @@ -896,7 +896,7 @@ widthsFromIndices numColumns' indices = quotient = if totLength > numColumns then fromIntegral totLength else fromIntegral numColumns - fracs = map (\l -> (fromIntegral l) / quotient) lengths in + fracs = map (\l -> fromIntegral l / quotient) lengths in tail fracs --- @@ -977,7 +977,7 @@ gridTableHeader headless blocks = try $ do then replicate (length underDashes) "" else map (unlines . map trim) $ transpose $ map (gridTableSplitLine indices) rawContent - heads <- fmap sequence $ mapM (parseFromString blocks . trim) rawHeads + heads <- sequence <$> mapM (parseFromString blocks . trim) rawHeads return (heads, aligns, indices) gridTableRawLine :: Stream s m Char => [Int] -> ParserT s st m [String] @@ -1323,9 +1323,7 @@ failIfInQuoteContext :: (HasQuoteContext st m, Stream s m t) -> ParserT s st m () failIfInQuoteContext context = do context' <- getQuoteContext - if context' == context - then fail "already inside quotes" - else return () + when (context' == context) $ fail "already inside quotes" charOrRef :: Stream s m Char => String -> ParserT s st m Char charOrRef cs = @@ -1418,9 +1416,7 @@ a <+?> b = a >>= flip fmap (try b <|> return mempty) . (<>) extractIdClass :: Attr -> Attr extractIdClass (ident, cls, kvs) = (ident', cls', kvs') where - ident' = case lookup "id" kvs of - Just v -> v - Nothing -> ident + ident' = fromMaybe ident (lookup "id" kvs) cls' = case lookup "class" kvs of Just cl -> words cl Nothing -> cls diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index 21120824f..c24c43901 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -81,7 +81,7 @@ import qualified Data.ByteString.Lazy as B import Data.Default (Default) import Data.List (delete, intersect) import qualified Data.Map as M -import Data.Maybe (isJust) +import Data.Maybe (isJust, fromMaybe) import Data.Sequence (ViewL (..), viewl) import qualified Data.Sequence as Seq import qualified Data.Set as Set @@ -187,7 +187,7 @@ bodyPartsToMeta' :: PandocMonad m => [BodyPart] -> DocxContext m (M.Map String M bodyPartsToMeta' [] = return M.empty bodyPartsToMeta' (bp : bps) | (Paragraph pPr parParts) <- bp - , (c : _)<- intersect (pStyle pPr) (M.keys metaStyles) + , (c : _)<- (pStyle pPr) `intersect` (M.keys metaStyles) , (Just metaField) <- M.lookup c metaStyles = do inlines <- smushInlines <$> mapM parPartToInlines parParts remaining <- bodyPartsToMeta' bps @@ -340,7 +340,7 @@ blocksToInlinesWarn cmtId blks = do notParaOrPlain (Para _) = False notParaOrPlain (Plain _) = False notParaOrPlain _ = True - unless (null $ filter notParaOrPlain blkList) $ + unless ( not (any notParaOrPlain blkList)) $ lift $ P.report $ DocxParserWarning $ "Docx comment " ++ cmtId ++ " will not retain formatting" return $ blocksToInlines' blkList @@ -351,7 +351,7 @@ blocksToInlinesWarn cmtId blks = do parPartToInlines :: PandocMonad m => ParPart -> DocxContext m Inlines parPartToInlines parPart = case parPart of - (BookMark _ anchor) | not $ anchor `elem` dummyAnchors -> do + (BookMark _ anchor) | notElem anchor dummyAnchors -> do inHdrBool <- asks docxInHeaderBlock ils <- parPartToInlines' parPart immedPrevAnchor <- gets docxImmedPrevAnchor @@ -444,9 +444,9 @@ parPartToInlines' (ExternalHyperLink target runs) = do return $ link target "" ils parPartToInlines' (PlainOMath exps) = return $ math $ writeTeX exps -parPartToInlines' (SmartTag runs) = do +parPartToInlines' (SmartTag runs) = smushInlines <$> mapM runToInlines runs -parPartToInlines' (Field info runs) = do +parPartToInlines' (Field info runs) = case info of HyperlinkField url -> parPartToInlines' $ ExternalHyperLink url runs UnknownField -> smushInlines <$> mapM runToInlines runs @@ -626,9 +626,7 @@ bodyPartToBlocks (ListItem pPr numId lvl (Just levelInfo) parparts) = do (_, fmt,txt, startFromLevelInfo) = levelInfo start = case startFromState of Just n -> n + 1 - Nothing -> case startFromLevelInfo of - Just n' -> n' - Nothing -> 1 + Nothing -> fromMaybe 1 startFromLevelInfo kvs = [ ("level", lvl) , ("num-id", numId) , ("format", fmt) diff --git a/src/Text/Pandoc/Readers/Docx/Fields.hs b/src/Text/Pandoc/Readers/Docx/Fields.hs index 69758b431..f0821a751 100644 --- a/src/Text/Pandoc/Readers/Docx/Fields.hs +++ b/src/Text/Pandoc/Readers/Docx/Fields.hs @@ -46,7 +46,7 @@ parseFieldInfo = parse fieldInfo "" fieldInfo :: Parser FieldInfo fieldInfo = - (try $ HyperlinkField <$> hyperlink) + try (HyperlinkField <$> hyperlink) <|> return UnknownField @@ -54,7 +54,7 @@ escapedQuote :: Parser String escapedQuote = string "\\\"" inQuotes :: Parser String -inQuotes = do +inQuotes = (try escapedQuote) <|> (anyChar >>= (\c -> return [c])) quotedString :: Parser String @@ -63,7 +63,7 @@ quotedString = do concat <$> manyTill inQuotes (try (char '"')) unquotedString :: Parser String -unquotedString = manyTill anyChar (try (space)) +unquotedString = manyTill anyChar (try space) fieldArgument :: Parser String fieldArgument = quotedString <|> unquotedString @@ -82,7 +82,7 @@ hyperlink = do string "HYPERLINK" spaces farg <- fieldArgument - switches <- (spaces *> many hyperlinkSwitch) + switches <- spaces *> many hyperlinkSwitch let url = case switches of ("\\l", s) : _ -> farg ++ ('#': s) _ -> farg diff --git a/src/Text/Pandoc/Readers/Docx/Lists.hs b/src/Text/Pandoc/Readers/Docx/Lists.hs index fa4870fff..c0f05094a 100644 --- a/src/Text/Pandoc/Readers/Docx/Lists.hs +++ b/src/Text/Pandoc/Readers/Docx/Lists.hs @@ -44,14 +44,14 @@ isListItem (Div (_, classes, _) _) | "list-item" `elem` classes = True isListItem _ = False getLevel :: Block -> Maybe Integer -getLevel (Div (_, _, kvs) _) = fmap read $ lookup "level" kvs +getLevel (Div (_, _, kvs) _) = read <$> lookup "level" kvs getLevel _ = Nothing getLevelN :: Block -> Integer getLevelN b = fromMaybe (-1) (getLevel b) getNumId :: Block -> Maybe Integer -getNumId (Div (_, _, kvs) _) = fmap read $ lookup "num-id" kvs +getNumId (Div (_, _, kvs) _) = read <$> lookup "num-id" kvs getNumId _ = Nothing getNumIdN :: Block -> Integer @@ -140,8 +140,8 @@ flatToBullets' num xs@(b : elems) (children, remaining) = span (\b' -> - (getLevelN b') > bLevel || - ((getLevelN b') == bLevel && (getNumIdN b') == bNumId)) + getLevelN b' > bLevel || + (getLevelN b' == bLevel && getNumIdN b' == bNumId)) xs in case getListType b of diff --git a/src/Text/Pandoc/Readers/Docx/Parse.hs b/src/Text/Pandoc/Readers/Docx/Parse.hs index 5f648666f..c123a0018 100644 --- a/src/Text/Pandoc/Readers/Docx/Parse.hs +++ b/src/Text/Pandoc/Readers/Docx/Parse.hs @@ -358,9 +358,7 @@ archiveToDocument zf = do docElem <- maybeToD $ (parseXMLDoc . UTF8.toStringLazy . fromEntry) entry let namespaces = elemToNameSpaces docElem bodyElem <- maybeToD $ findChildByName namespaces "w" "body" docElem - let bodyElem' = case walkDocument namespaces bodyElem of - Just e -> e - Nothing -> bodyElem + let bodyElem' = fromMaybe bodyElem (walkDocument namespaces bodyElem) body <- elemToBody namespaces bodyElem' return $ Document namespaces body @@ -603,7 +601,7 @@ elemToTblLook ns element | isElem ns "w" "tblLook" element = Just bitMask -> testBitMask bitMask 0x020 Nothing -> False in - return $ TblLook{firstRowFormatting = firstRowFmt} + return TblLook{firstRowFormatting = firstRowFmt} elemToTblLook _ _ = throwError WrongElem elemToRow :: NameSpaces -> Element -> D Row @@ -623,7 +621,7 @@ elemToCell _ _ = throwError WrongElem elemToParIndentation :: NameSpaces -> Element -> Maybe ParIndentation elemToParIndentation ns element | isElem ns "w" "ind" element = - Just $ ParIndentation { + Just ParIndentation { leftParIndent = findAttrByName ns "w" "left" element >>= stringToInteger @@ -1173,8 +1171,7 @@ elemToRunElems ns element let font = do fontElem <- findElement (qualName "rFonts") element stringToFont =<< - foldr (<|>) Nothing ( - map (flip findAttr fontElem . qualName) ["ascii", "hAnsi"]) + foldr ((<|>) . (flip findAttr fontElem . qualName)) Nothing ["ascii", "hAnsi"] local (setFont font) (mapD (elemToRunElem ns) (elChildren element)) elemToRunElems _ _ = throwError WrongElem diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs index f15bf1c96..0e79f9ec3 100644 --- a/src/Text/Pandoc/Readers/HTML.hs +++ b/src/Text/Pandoc/Readers/HTML.hs @@ -43,14 +43,14 @@ module Text.Pandoc.Readers.HTML ( readHtml ) where import Control.Applicative ((<|>)) -import Control.Arrow ((***)) +import Control.Arrow (first) import Control.Monad (guard, mplus, msum, mzero, unless, void) import Control.Monad.Except (throwError) import Control.Monad.Reader (ReaderT, ask, asks, lift, local, runReaderT) import Data.Char (isAlphaNum, isDigit, isLetter) import Data.Default (Default (..), def) import Data.Foldable (for_) -import Data.List (intercalate, isPrefixOf) +import Data.List (isPrefixOf) import Data.List.Split (wordsBy, splitWhen) import qualified Data.Map as M import Data.Maybe (fromMaybe, isJust, isNothing) @@ -777,7 +777,7 @@ pCode = try $ do (TagOpen open attr') <- pSatisfy $ tagOpen (`elem` ["code","tt"]) (const True) let attr = toStringAttr attr' result <- manyTill pAnyTag (pCloses open) - return $ B.codeWith (mkAttr attr) $ intercalate " " $ lines $ T.unpack $ + return $ B.codeWith (mkAttr attr) $ unwords $ lines $ T.unpack $ innerText result pSpan :: PandocMonad m => TagParser m Inlines @@ -1227,7 +1227,7 @@ stripPrefixes = map stripPrefix stripPrefix :: Tag Text -> Tag Text stripPrefix (TagOpen s as) = - TagOpen (stripPrefix' s) (map (stripPrefix' *** id) as) + TagOpen (stripPrefix' s) (map (first stripPrefix') as) stripPrefix (TagClose s) = TagClose (stripPrefix' s) stripPrefix x = x diff --git a/src/Text/Pandoc/Readers/JATS.hs b/src/Text/Pandoc/Readers/JATS.hs index 9223db68c..8158a4511 100644 --- a/src/Text/Pandoc/Readers/JATS.hs +++ b/src/Text/Pandoc/Readers/JATS.hs @@ -494,4 +494,3 @@ parseInline (Elem e) = "" -> [] l -> [l] return $ codeWith (attrValue "id" e,classes',[]) $ strContentRecursive e - diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 3408201eb..1ce3d18e5 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -272,7 +272,7 @@ rawLaTeXBlock = do lookAhead (try (char '\\' >> letter)) -- we don't want to apply newly defined latex macros to their own -- definitions: - (snd <$> rawLaTeXParser macroDef) + snd <$> rawLaTeXParser macroDef <|> ((snd <$> rawLaTeXParser (environment <|> blockCommand)) >>= applyMacros) rawLaTeXInline :: (PandocMonad m, HasMacros s, HasReaderOptions s) @@ -351,7 +351,7 @@ totoks pos t = Tok pos (Arg i) ("#" <> t1) : totoks (incSourceColumn pos (1 + T.length t1)) t2 Nothing -> - Tok pos Symbol ("#") + Tok pos Symbol "#" : totoks (incSourceColumn pos 1) t2 | c == '^' -> case T.uncons rest of @@ -369,10 +369,10 @@ totoks pos t = | d < '\128' -> Tok pos Esc1 (T.pack ['^','^',d]) : totoks (incSourceColumn pos 3) rest'' - _ -> Tok pos Symbol ("^") : - Tok (incSourceColumn pos 1) Symbol ("^") : + _ -> Tok pos Symbol "^" : + Tok (incSourceColumn pos 1) Symbol "^" : totoks (incSourceColumn pos 2) rest' - _ -> Tok pos Symbol ("^") + _ -> Tok pos Symbol "^" : totoks (incSourceColumn pos 1) rest | otherwise -> Tok pos Symbol (T.singleton c) : totoks (incSourceColumn pos 1) rest @@ -454,7 +454,7 @@ doMacros n = do addTok _ (Tok _ (CtrlSeq x) txt) acc@(Tok _ Word _ : _) | not (T.null txt) && - (isLetter (T.last txt)) = + isLetter (T.last txt) = Tok spos (CtrlSeq x) (txt <> " ") : acc addTok _ t acc = setpos spos t : acc ts' <- getInput @@ -1244,7 +1244,7 @@ inlineEnvironments = M.fromList [ ] inlineCommands :: PandocMonad m => M.Map Text (LP m Inlines) -inlineCommands = M.union inlineLanguageCommands $ M.fromList $ +inlineCommands = M.union inlineLanguageCommands $ M.fromList [ ("emph", extractSpaces emph <$> tok) , ("textit", extractSpaces emph <$> tok) , ("textsl", extractSpaces emph <$> tok) @@ -1501,7 +1501,7 @@ foreignlanguage :: PandocMonad m => LP m Inlines foreignlanguage = do babelLang <- T.unpack . untokenize <$> braced case babelLangToBCP47 babelLang of - Just lang -> spanWith ("", [], [("lang", renderLang $ lang)]) <$> tok + Just lang -> spanWith ("", [], [("lang", renderLang lang)]) <$> tok _ -> tok inlineLanguageCommands :: PandocMonad m => M.Map Text (LP m Inlines) @@ -2021,7 +2021,7 @@ closing = do return $ para (trimInlines contents) <> sigs blockCommands :: PandocMonad m => M.Map Text (LP m Blocks) -blockCommands = M.fromList $ +blockCommands = M.fromList [ ("par", mempty <$ skipopts) , ("parbox", skipopts >> braced >> grouped blocks) , ("title", mempty <$ (skipopts *> @@ -2444,7 +2444,7 @@ parseAligns = try $ do spaces spec <- braced case safeRead ds of - Just n -> do + Just n -> getInput >>= setInput . (mconcat (replicate n spec) ++) Nothing -> fail $ "Could not parse " ++ ds ++ " as number" bgroup diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 94f04eee7..92e9098bd 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -36,7 +36,7 @@ import Control.Monad import Control.Monad.Except (throwError) import Data.Char (isAlphaNum, isPunctuation, isSpace, toLower) import qualified Data.HashMap.Strict as H -import Data.List (findIndex, intercalate, sortBy, transpose) +import Data.List (intercalate, sortBy, transpose, elemIndex) import qualified Data.Map as M import Data.Maybe import Data.Monoid ((<>)) @@ -162,16 +162,14 @@ inlinesInBalancedBrackets = stripBracket xs = if last xs == ']' then init xs else xs go :: PandocMonad m => Int -> MarkdownParser m () go 0 = return () - go openBrackets = do + go openBrackets = (() <$ (escapedChar <|> - code <|> - rawHtmlInline <|> - rawLaTeXInline') >> go openBrackets) + code <|> + rawHtmlInline <|> + rawLaTeXInline') >> go openBrackets) <|> (do char ']' - if openBrackets > 1 - then go (openBrackets - 1) - else return ()) + Control.Monad.when (openBrackets > 1) $ go (openBrackets - 1)) <|> (char '[' >> go (openBrackets + 1)) <|> @@ -257,13 +255,13 @@ yamlMetaBlock = try $ do v' <- yamlToMeta v let k' = T.unpack k updateState $ \st -> st{ stateMeta' = - (do m <- stateMeta' st - -- if there's already a value, leave it unchanged - case lookupMeta k' m of - Just _ -> return m - Nothing -> do - v'' <- v' - return $ B.setMeta (T.unpack k) v'' m)} + do m <- stateMeta' st + -- if there's already a value, leave it unchanged + case lookupMeta k' m of + Just _ -> return m + Nothing -> do + v'' <- v' + return $ B.setMeta (T.unpack k) v'' m} ) alist Right Yaml.Null -> return () Right _ -> do @@ -596,7 +594,7 @@ setextHeader = try $ do underlineChar <- oneOf setextHChars many (char underlineChar) blanklines - let level = fromMaybe 0 (findIndex (== underlineChar) setextHChars) + 1 + let level = fromMaybe 0 (elemIndex underlineChar setextHChars) + 1 attr' <- registerHeader attr (runF text defaultParserState) guardDisabled Ext_implicit_header_references <|> registerImplicitHeader raw attr' @@ -851,7 +849,7 @@ orderedListStart mbstydelim = try $ do return (num, style, delim)) listStart :: PandocMonad m => MarkdownParser m () -listStart = bulletListStart <|> (Control.Monad.void (orderedListStart Nothing)) +listStart = bulletListStart <|> Control.Monad.void (orderedListStart Nothing) listLine :: PandocMonad m => Int -> MarkdownParser m String listLine continuationIndent = try $ do @@ -881,7 +879,7 @@ rawListItem fourSpaceRule start = try $ do pos2 <- getPosition let continuationIndent = if fourSpaceRule then 4 - else (sourceColumn pos2 - sourceColumn pos1) + else sourceColumn pos2 - sourceColumn pos1 first <- listLineCommon rest <- many (do notFollowedBy listStart notFollowedBy (() <$ codeBlockFenced) @@ -912,10 +910,10 @@ listContinuation continuationIndent = try $ do return $ concat (x:xs) ++ blanks notFollowedByDivCloser :: PandocMonad m => MarkdownParser m () -notFollowedByDivCloser = do +notFollowedByDivCloser = guardDisabled Ext_fenced_divs <|> - do divLevel <- stateFencedDivLevel <$> getState - guard (divLevel < 1) <|> notFollowedBy divFenceEnd + do divLevel <- stateFencedDivLevel <$> getState + guard (divLevel < 1) <|> notFollowedBy divFenceEnd notFollowedByHtmlCloser :: PandocMonad m => MarkdownParser m () notFollowedByHtmlCloser = do @@ -1222,7 +1220,7 @@ simpleTableHeader headless = try $ do if headless then lookAhead anyLine else return rawContent - let aligns = zipWith alignType (map ((: [])) rawHeads) lengths + let aligns = zipWith alignType (map (: []) rawHeads) lengths let rawHeads' = if headless then replicate (length dashes) "" else rawHeads @@ -1418,11 +1416,11 @@ pipeTableHeaderPart = try $ do skipMany spaceChar let len = length pipe + maybe 0 (const 1) left + maybe 0 (const 1) right return - ((case (left,right) of - (Nothing,Nothing) -> AlignDefault - (Just _,Nothing) -> AlignLeft - (Nothing,Just _) -> AlignRight - (Just _,Just _) -> AlignCenter), len) + (case (left,right) of + (Nothing,Nothing) -> AlignDefault + (Just _,Nothing) -> AlignLeft + (Nothing,Just _) -> AlignRight + (Just _,Just _) -> AlignCenter, len) -- Succeed only if current line contains a pipe. scanForPipe :: PandocMonad m => ParserT [Char] st m () @@ -1929,7 +1927,7 @@ rawConTeXtEnvironment = try $ do (try $ string "\\stop" >> string completion) return $ "\\start" ++ completion ++ concat contents ++ "\\stop" ++ completion -inBrackets :: PandocMonad m => (ParserT [Char] st m Char) -> ParserT [Char] st m String +inBrackets :: PandocMonad m => ParserT [Char] st m Char -> ParserT [Char] st m String inBrackets parser = do char '[' contents <- many parser @@ -2150,6 +2148,6 @@ doubleQuoted :: PandocMonad m => MarkdownParser m (F Inlines) doubleQuoted = try $ do doubleQuoteStart contents <- mconcat <$> many (try $ notFollowedBy doubleQuoteEnd >> inline) - (withQuoteContext InDoubleQuote $ doubleQuoteEnd >> return + withQuoteContext InDoubleQuote (doubleQuoteEnd >> return (fmap B.doubleQuoted . trimInlinesF $ contents)) - <|> (return $ return (B.str "\8220") <> contents) + <|> return (return (B.str "\8220") <> contents) diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 4c6d1278e..973dfa15c 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -482,7 +482,7 @@ definitionList :: PandocMonad m => MuseParser m (F Blocks) definitionList = try $ do many spaceChar pos <- getPosition - (guardDisabled Ext_amuse) <|> (guard (sourceColumn pos /= 1)) -- Initial space is required by Amusewiki, but not Emacs Muse + guardDisabled Ext_amuse <|> guard (sourceColumn pos /= 1) -- Initial space is required by Amusewiki, but not Emacs Muse first <- definitionListItem 0 rest <- many $ try (optionMaybe blankline >> definitionListItem (sourceColumn pos - 1)) return $ B.definitionList <$> sequence (first : rest) diff --git a/src/Text/Pandoc/Readers/Odt/Arrows/Utils.hs b/src/Text/Pandoc/Readers/Odt/Arrows/Utils.hs index cdfa8f8df..ef8b2d18a 100644 --- a/src/Text/Pandoc/Readers/Odt/Arrows/Utils.hs +++ b/src/Text/Pandoc/Readers/Odt/Arrows/Utils.hs @@ -211,9 +211,9 @@ a ^>>?% f = arr a >>?^ (uncurry f) --- (>>?%?) :: (ArrowChoice a) => FallibleArrow a x f (b,b') - -> (b -> b' -> (Either f c)) + -> (b -> b' -> Either f c) -> FallibleArrow a x f c -a >>?%? f = a >>?^? (uncurry f) +a >>?%? f = a >>?^? uncurry f infixr 1 >>?, >>?^, >>?^? infixr 1 ^>>?, >>?! diff --git a/src/Text/Pandoc/Readers/Odt/ContentReader.hs b/src/Text/Pandoc/Readers/Odt/ContentReader.hs index cc9b798b3..380f16c66 100644 --- a/src/Text/Pandoc/Readers/Odt/ContentReader.hs +++ b/src/Text/Pandoc/Readers/Odt/ContentReader.hs @@ -322,7 +322,7 @@ type InlineModifier = Inlines -> Inlines modifierFromStyleDiff :: PropertyTriple -> InlineModifier modifierFromStyleDiff propertyTriple = composition $ - (getVPosModifier propertyTriple) + getVPosModifier propertyTriple : map (first ($ propertyTriple) >>> ifThen_else ignore) [ (hasEmphChanged , emph ) , (hasChanged isStrong , strong ) @@ -352,7 +352,7 @@ modifierFromStyleDiff propertyTriple = ] hasChanged property triple@(_, property -> newProperty, _) = - maybe True (/=newProperty) (lookupPreviousValue property triple) + (/= Just newProperty) (lookupPreviousValue property triple) hasChangedM property triple@(_, textProps,_) = fromMaybe False $ (/=) <$> property textProps <*> lookupPreviousValueM property triple @@ -362,7 +362,7 @@ modifierFromStyleDiff propertyTriple = lookupPreviousValueM f = lookupPreviousStyleValue ((f =<<).textProperties) lookupPreviousStyleValue f (ReaderState{..},_,mFamily) - = ( findBy f $ extendedStylePropertyChain styleTrace styleSet ) + = findBy f (extendedStylePropertyChain styleTrace styleSet) <|> ( f =<< fmap (lookupDefaultStyle' styleSet) mFamily ) @@ -793,8 +793,7 @@ read_image_src = matchingElement NsDraw "image" Left _ -> returnV "" -< () read_frame_title :: InlineMatcher -read_frame_title = matchingElement NsSVG "title" - $ (matchChildContent [] read_plain_text) +read_frame_title = matchingElement NsSVG "title" (matchChildContent [] read_plain_text) read_frame_text_box :: InlineMatcher read_frame_text_box = matchingElement NsDraw "text-box" @@ -803,12 +802,12 @@ read_frame_text_box = matchingElement NsDraw "text-box" arr read_img_with_caption -< toList paragraphs read_img_with_caption :: [Block] -> Inlines -read_img_with_caption ((Para [Image attr alt (src,title)]) : _) = +read_img_with_caption (Para [Image attr alt (src,title)] : _) = singleton (Image attr alt (src, 'f':'i':'g':':':title)) -- no text, default caption read_img_with_caption (Para (Image attr _ (src,title) : txt) : _) = singleton (Image attr txt (src, 'f':'i':'g':':':title) ) -- override caption with the text that follows -read_img_with_caption ( (Para (_ : xs)) : ys) = - read_img_with_caption ((Para xs) : ys) +read_img_with_caption ( Para (_ : xs) : ys) = + read_img_with_caption (Para xs : ys) read_img_with_caption _ = mempty @@ -909,8 +908,8 @@ post_process (Pandoc m blocks) = Pandoc m (post_process' blocks) post_process' :: [Block] -> [Block] -post_process' ((Table _ a w h r) : (Div ("", ["caption"], _) [Para inlines] ) : xs) = - (Table inlines a w h r) : ( post_process' xs ) +post_process' (Table _ a w h r : Div ("", ["caption"], _) [Para inlines] : xs) = + Table inlines a w h r : post_process' xs post_process' bs = bs read_body :: OdtReader _x (Pandoc, MediaBag) diff --git a/src/Text/Pandoc/Readers/Odt/Namespaces.hs b/src/Text/Pandoc/Readers/Odt/Namespaces.hs index 3c11aeb8e..92e12931d 100644 --- a/src/Text/Pandoc/Readers/Odt/Namespaces.hs +++ b/src/Text/Pandoc/Readers/Odt/Namespaces.hs @@ -48,7 +48,7 @@ instance NameSpaceID Namespace where findID :: NameSpaceIRI -> Maybe Namespace -findID iri = listToMaybe [nsID | (iri',~nsID) <- nsIDs, iri' `isPrefixOf` iri] +findID iri = listToMaybe [nsID | (iri',nsID) <- nsIDs, iri' `isPrefixOf` iri] nsIDmap :: NameSpaceIRIs Namespace nsIDmap = foldr (uncurry $ flip M.insert) M.empty nsIDs diff --git a/src/Text/Pandoc/Readers/Odt/StyleReader.hs b/src/Text/Pandoc/Readers/Odt/StyleReader.hs index 6129c1664..58be8e4a3 100644 --- a/src/Text/Pandoc/Readers/Odt/StyleReader.hs +++ b/src/Text/Pandoc/Readers/Odt/StyleReader.hs @@ -131,13 +131,12 @@ type StyleReaderSafe a b = XMLReaderSafe FontPitches a b -- | A reader for font pitches fontPitchReader :: XMLReader _s _x FontPitches fontPitchReader = executeIn NsOffice "font-face-decls" ( - ( withEveryL NsStyle "font-face" $ liftAsSuccess ( + withEveryL NsStyle "font-face" (liftAsSuccess ( findAttr' NsStyle "name" &&& lookupDefaultingAttr NsStyle "font-pitch" - ) - ) - >>?^ ( M.fromList . (foldl accumLegalPitches []) ) + )) + >>?^ ( M.fromList . foldl accumLegalPitches [] ) ) where accumLegalPitches ls (Nothing,_) = ls accumLegalPitches ls (Just n,p) = (n,p):ls @@ -383,11 +382,11 @@ data ListLevelStyle = ListLevelStyle { listLevelType :: ListLevelType instance Show ListLevelStyle where show ListLevelStyle{..} = "<LLS|" - ++ (show listLevelType) + ++ show listLevelType ++ "|" - ++ (maybeToString listItemPrefix) - ++ (show listItemFormat) - ++ (maybeToString listItemSuffix) + ++ maybeToString listItemPrefix + ++ show listItemFormat + ++ maybeToString listItemSuffix ++ ">" where maybeToString = fromMaybe "" @@ -483,14 +482,14 @@ readTextProperties = ( liftA6 PropT ( searchAttr NsXSL_FO "font-style" False isFontEmphasised ) ( searchAttr NsXSL_FO "font-weight" False isFontBold ) - ( findPitch ) + findPitch ( getAttr NsStyle "text-position" ) - ( readUnderlineMode ) - ( readStrikeThroughMode ) + readUnderlineMode + readStrikeThroughMode ) where isFontEmphasised = [("normal",False),("italic",True),("oblique",True)] isFontBold = ("normal",False):("bold",True) - :(map ((,True).show) ([100,200..900]::[Int])) + :map ((,True).show) ([100,200..900]::[Int]) readUnderlineMode :: StyleReaderSafe _x (Maybe UnderlineMode) readUnderlineMode = readLineMode "text-underline-mode" @@ -510,7 +509,7 @@ readLineMode modeAttr styleAttr = proc x -> do Nothing -> returnA -< Just UnderlineModeNormal else returnA -< Nothing where - isLinePresent = [("none",False)] ++ map (,True) + isLinePresent = ("none",False) : map (,True) [ "dash" , "dot-dash" , "dot-dot-dash" , "dotted" , "long-dash" , "solid" , "wave" ] @@ -547,20 +546,18 @@ readListStyle = findAttr NsStyle "name" >>?! keepingTheValue ( liftA ListStyle - $ ( liftA3 SM.union3 + $ liftA3 SM.union3 ( readListLevelStyles NsText "list-level-style-number" LltNumbered ) ( readListLevelStyles NsText "list-level-style-bullet" LltBullet ) - ( readListLevelStyles NsText "list-level-style-image" LltImage ) - ) >>^ M.mapMaybe chooseMostSpecificListLevelStyle + ( readListLevelStyles NsText "list-level-style-image" LltImage ) >>^ M.mapMaybe chooseMostSpecificListLevelStyle ) -- readListLevelStyles :: Namespace -> ElementName -> ListLevelType -> StyleReaderSafe _x (SM.SetMap Int ListLevelStyle) readListLevelStyles namespace elementName levelType = - ( tryAll namespace elementName (readListLevelStyle levelType) + tryAll namespace elementName (readListLevelStyle levelType) >>^ SM.fromList - ) -- readListLevelStyle :: ListLevelType -> StyleReader _x (Int, ListLevelStyle) @@ -632,7 +629,7 @@ parents style styles = unfoldr findNextParent style -- Ha! getStyleFamily :: Style -> Styles -> Maybe StyleFamily getStyleFamily style@Style{..} styles = styleFamily - <|> (F.asum $ map (`getStyleFamily` styles) $ parents style styles) + <|> F.asum (map (`getStyleFamily` styles) $ parents style styles) -- | Each 'Style' has certain 'StyleProperties'. But sometimes not all property -- values are specified. Instead, a value might be inherited from a @@ -654,7 +651,7 @@ stylePropertyChain style styles -- extendedStylePropertyChain :: [Style] -> Styles -> [StyleProperties] extendedStylePropertyChain [] _ = [] -extendedStylePropertyChain [style] styles = (stylePropertyChain style styles) - ++ (maybeToList (fmap (lookupDefaultStyle' styles) (getStyleFamily style styles))) -extendedStylePropertyChain (style:trace) styles = (stylePropertyChain style styles) - ++ (extendedStylePropertyChain trace styles) +extendedStylePropertyChain [style] styles = stylePropertyChain style styles + ++ maybeToList (fmap (lookupDefaultStyle' styles) (getStyleFamily style styles)) +extendedStylePropertyChain (style:trace) styles = stylePropertyChain style styles + ++ extendedStylePropertyChain trace styles diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs index c5a7d8e10..fa016283c 100644 --- a/src/Text/Pandoc/Readers/Org/Blocks.hs +++ b/src/Text/Pandoc/Readers/Org/Blocks.hs @@ -516,7 +516,7 @@ include = try $ do blocksParser <- case includeArgs of ("example" : _) -> return $ pure . B.codeBlock <$> parseRaw ["export"] -> return . returnF $ B.fromList [] - ("export" : format : []) -> return $ pure . B.rawBlock format <$> parseRaw + ["export", format] -> return $ pure . B.rawBlock format <$> parseRaw ("src" : rest) -> do let attr = case rest of [lang] -> (mempty, [lang], mempty) diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 49cc3018c..0e90fe945 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -1263,7 +1263,7 @@ simpleTableHeader headless = try $ do let rawHeads = if headless then replicate (length dashes) "" else simpleTableSplitLine indices rawContent - heads <- mapM ( (parseFromString' (mconcat <$> many plain)) . trim) rawHeads + heads <- mapM ( parseFromString' (mconcat <$> many plain) . trim) rawHeads return (heads, aligns, indices) -- Parse a simple table. @@ -1414,7 +1414,7 @@ renderRole contents fmt role attr = case role of pepUrl = "http://www.python.org/dev/peps/pep-" ++ padNo ++ "/" addClass :: String -> Attr -> Attr -addClass c (ident, classes, keyValues) = (ident, union classes [c], keyValues) +addClass c (ident, classes, keyValues) = (ident, classes `union` [c], keyValues) roleName :: PandocMonad m => RSTParser m String roleName = many1 (letter <|> char '-') @@ -1454,7 +1454,7 @@ endline = try $ do notFollowedBy blankline -- parse potential list-starts at beginning of line differently in a list: st <- getState - when ((stateParserContext st) == ListItemState) $ notFollowedBy (anyOrderedListMarker >> spaceChar) >> + when (stateParserContext st == ListItemState) $ notFollowedBy (anyOrderedListMarker >> spaceChar) >> notFollowedBy' bulletListStart return B.softbreak @@ -1577,7 +1577,7 @@ note = try $ do -- not yet in this implementation. updateState $ \st -> st{ stateNotes = [] } contents <- parseFromString' parseBlocks raw - let newnotes = if (ref == "*" || ref == "#") -- auto-numbered + let newnotes = if ref == "*" || ref == "#" -- auto-numbered -- delete the note so the next auto-numbered note -- doesn't get the same contents: then deleteFirstsBy (==) notes [(ref,raw)] diff --git a/src/Text/Pandoc/Readers/Textile.hs b/src/Text/Pandoc/Readers/Textile.hs index 46d6301e4..30bb6a715 100644 --- a/src/Text/Pandoc/Readers/Textile.hs +++ b/src/Text/Pandoc/Readers/Textile.hs @@ -110,7 +110,7 @@ noteBlock = try $ do startPos <- getPosition ref <- noteMarker optional blankline - contents <- fmap unlines $ many1Till anyLine (blanklines <|> noteBlock) + contents <- unlines <$> many1Till anyLine (blanklines <|> noteBlock) endPos <- getPosition let newnote = (ref, contents ++ "\n") st <- getState @@ -360,7 +360,7 @@ cellAttributes = try $ do tableCell :: PandocMonad m => ParserT [Char] ParserState m ((Bool, Alignment), Blocks) tableCell = try $ do char '|' - (isHeader, alignment) <- option (False, AlignDefault) $ cellAttributes + (isHeader, alignment) <- option (False, AlignDefault) cellAttributes notFollowedBy blankline raw <- trim <$> many (noneOf "|\n" <|> try (char '\n' <* notFollowedBy blankline)) @@ -499,7 +499,7 @@ copy = do note :: PandocMonad m => ParserT [Char] ParserState m Inlines note = try $ do - ref <- (char '[' *> many1 digit <* char ']') + ref <- char '[' *> many1 digit <* char ']' notes <- stateNotes <$> getState case lookup ref notes of Nothing -> fail "note not found" @@ -530,7 +530,7 @@ hyphenedWords = do wordChunk :: PandocMonad m => ParserT [Char] ParserState m String wordChunk = try $ do hd <- noneOf wordBoundaries - tl <- many ( (noneOf wordBoundaries) <|> + tl <- many ( noneOf wordBoundaries <|> try (notFollowedBy' note *> oneOf markupChars <* lookAhead (noneOf wordBoundaries) ) ) return $ hd:tl @@ -614,7 +614,7 @@ escapedEqs = B.str <$> -- | literal text escaped btw <notextile> tags escapedTag :: PandocMonad m => ParserT [Char] ParserState m Inlines escapedTag = B.str <$> - (try $ string "<notextile>" *> + try (string "<notextile>" *> manyTill anyChar' (try $ string "</notextile>")) -- | Any special symbol defined in wordBoundaries @@ -630,7 +630,8 @@ code = code1 <|> code2 -- any character except a newline before a blank line anyChar' :: PandocMonad m => ParserT [Char] ParserState m Char anyChar' = - satisfy (/='\n') <|> (try $ char '\n' <* notFollowedBy blankline) + satisfy (/='\n') <|> + try (char '\n' <* notFollowedBy blankline) code1 :: PandocMonad m => ParserT [Char] ParserState m Inlines code1 = B.code <$> surrounded (char '@') anyChar' diff --git a/src/Text/Pandoc/Readers/TikiWiki.hs b/src/Text/Pandoc/Readers/TikiWiki.hs index 4a66cc13d..a92f7bed2 100644 --- a/src/Text/Pandoc/Readers/TikiWiki.hs +++ b/src/Text/Pandoc/Readers/TikiWiki.hs @@ -168,7 +168,7 @@ table = try $ do where -- The headers are as many empty srings as the number of columns -- in the first row - headers rows = map (B.plain . B.str) $replicate (length $ rows !! 0) "" + headers rows = map (B.plain . B.str) $replicate (length $ head rows) "" para :: PandocMonad m => TikiWikiParser m B.Blocks para = fmap (result . mconcat) ( many1Till inline endOfParaElement) @@ -238,8 +238,8 @@ fixListNesting [first] = [recurseOnList first] fixListNesting (first:second:rest) = let secondBlock = head $ B.toList second in case secondBlock of - BulletList _ -> fixListNesting $ (mappend (recurseOnList first) (recurseOnList second)) : rest - OrderedList _ _ -> fixListNesting $ (mappend (recurseOnList first) (recurseOnList second)) : rest + BulletList _ -> fixListNesting $ mappend (recurseOnList first) (recurseOnList second) : rest + OrderedList _ _ -> fixListNesting $ mappend (recurseOnList first) (recurseOnList second) : rest _ -> recurseOnList first : fixListNesting (second:rest) -- This function walks the Block structure for fixListNesting, @@ -285,7 +285,7 @@ spanFoldUpList ln (first:rest) = -- level and of the same type. splitListNesting :: ListNesting -> (ListNesting, B.Blocks) -> Bool splitListNesting ln1 (ln2, _) - | (lnnest ln1) < (lnnest ln2) = + | lnnest ln1 < lnnest ln2 = True | ln1 == ln2 = True @@ -341,7 +341,7 @@ listItemLine nest = lineContent >>= parseContent lineContent = do content <- anyLine continuation <- optionMaybe listContinuation - return $ filterSpaces content ++ "\n" ++ maybe "" id continuation + return $ filterSpaces content ++ "\n" ++ Data.Maybe.fromMaybe "" continuation filterSpaces = reverse . dropWhile (== ' ') . reverse listContinuation = string (replicate nest '+') >> lineContent parseContent x = do @@ -410,7 +410,7 @@ inline = choice [ whitespace ] <?> "inline" whitespace :: PandocMonad m => TikiWikiParser m B.Inlines -whitespace = (lb <|> regsp) +whitespace = lb <|> regsp where lb = try $ skipMany spaceChar >> linebreak >> return B.space regsp = try $ skipMany1 spaceChar >> return B.space @@ -501,7 +501,7 @@ escapedChar = try $ do string "~" inner <- many1 $ oneOf "0123456789" string "~" - return $B.str [(toEnum (read inner :: Int)) :: Char] + return $B.str [toEnum (read inner :: Int) :: Char] -- UNSUPPORTED, as there doesn't seem to be any facility in calibre -- for this diff --git a/src/Text/Pandoc/Readers/Txt2Tags.hs b/src/Text/Pandoc/Readers/Txt2Tags.hs index 68399afc9..b4f4bc564 100644 --- a/src/Text/Pandoc/Readers/Txt2Tags.hs +++ b/src/Text/Pandoc/Readers/Txt2Tags.hs @@ -36,7 +36,7 @@ import Control.Monad.Except (catchError, throwError) import Control.Monad.Reader (Reader, asks, runReader) import Data.Char (toLower) import Data.Default -import Data.List (intercalate, intersperse, transpose) +import Data.List (intercalate, transpose) import Data.Maybe (fromMaybe) import Data.Monoid ((<>)) import Data.Text (Text) @@ -463,7 +463,7 @@ titleLink = try $ do char ']' let link' = last tokens guard $ not $ null link' - let tit = concat (intersperse " " (init tokens)) + let tit = unwords (init tokens) return $ B.link link' "" (B.text tit) -- Link with image diff --git a/src/Text/Pandoc/Readers/Vimwiki.hs b/src/Text/Pandoc/Readers/Vimwiki.hs index 162fb371e..d717a1ba8 100644 --- a/src/Text/Pandoc/Readers/Vimwiki.hs +++ b/src/Text/Pandoc/Readers/Vimwiki.hs @@ -388,9 +388,7 @@ bulletListMarkers = "ul" <$ (char '*' <|> char '-') orderedListMarkers :: PandocMonad m => VwParser m String orderedListMarkers = - ("ol" <$choice (orderedListMarker Decimal Period:(($OneParen) - <$> orderedListMarker - <$> [Decimal, LowerRoman, UpperRoman, LowerAlpha, UpperAlpha]))) + ("ol" <$choice (orderedListMarker Decimal Period:(($OneParen) . orderedListMarker <$> [Decimal, LowerRoman, UpperRoman, LowerAlpha, UpperAlpha]))) <|> ("ol" <$ char '#') --many need trimInlines diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 583c7a63f..52e1447db 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -494,7 +494,7 @@ hierarchicalizeWithIds (Header level attr@(_,classes,_) title':xs) = do return $ Sec level newnum attr title' sectionContents' : rest' hierarchicalizeWithIds (Div ("",["references"],[]) (Header level (ident,classes,kvs) title' : xs):ys) = - hierarchicalizeWithIds (Header level (ident,("references":classes),kvs) + hierarchicalizeWithIds (Header level (ident,"references":classes,kvs) title' : (xs ++ ys)) hierarchicalizeWithIds (x:rest) = do rest' <- hierarchicalizeWithIds rest diff --git a/src/Text/Pandoc/Writers/AsciiDoc.hs b/src/Text/Pandoc/Writers/AsciiDoc.hs index a6906eb68..b8f647b66 100644 --- a/src/Text/Pandoc/Writers/AsciiDoc.hs +++ b/src/Text/Pandoc/Writers/AsciiDoc.hs @@ -265,8 +265,7 @@ blockToAsciiDoc opts (OrderedList (_start, sty, _delim) items) = do let markers' = map (\m -> if length m < 3 then m ++ replicate (3 - length m) ' ' else m) markers - contents <- mapM (uncurry (orderedListItemToAsciiDoc opts)) $ - zip markers' items + contents <- zipWithM (orderedListItemToAsciiDoc opts) markers' items return $ cat contents <> blankline blockToAsciiDoc opts (DefinitionList items) = do contents <- mapM (definitionListItemToAsciiDoc opts) items @@ -452,7 +451,7 @@ inlineToAsciiDoc opts (Link _ txt (src, _tit)) = do else prefix <> text src <> "[" <> linktext <> "]" inlineToAsciiDoc opts (Image attr alternate (src, tit)) = do -- image:images/logo.png[Company logo, title="blah"] - let txt = if (null alternate) || (alternate == [Str ""]) + let txt = if null alternate || (alternate == [Str ""]) then [Str "image"] else alternate linktext <- inlineListToAsciiDoc opts txt diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index adf5f232a..928eaa712 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -1057,12 +1057,9 @@ getParaProps displayMathPara = do props <- asks envParaProperties listLevel <- asks envListLevel numid <- asks envListNumId - let listPr = if listLevel >= 0 && not displayMathPara - then [ mknode "w:numPr" [] - [ mknode "w:numId" [("w:val",show numid)] () - , mknode "w:ilvl" [("w:val",show listLevel)] () ] - ] - else [] + let listPr = [mknode "w:numPr" [] + [ mknode "w:numId" [("w:val",show numid)] () + , mknode "w:ilvl" [("w:val",show listLevel)] () ] | listLevel >= 0 && not displayMathPara] return $ case props ++ listPr of [] -> [] ps -> [mknode "w:pPr" [] ps] @@ -1145,7 +1142,7 @@ inlineToOpenXML' opts (Span (ident,classes,kvs) ils) = do return $ \f -> do x <- f return [ mknode "w:ins" - [("w:id", (show insId)), + [("w:id", show insId), ("w:author", author), ("w:date", date)] x ] else return id @@ -1272,7 +1269,7 @@ inlineToOpenXML' opts (Image attr alt (src, title)) = do Nothing -> catchError (do (img, mt) <- P.fetchItem src - ident <- ("rId"++) `fmap` ((lift . lift) getUniqueId) + ident <- ("rId"++) `fmap` (lift . lift) getUniqueId let (xpt,ypt) = desiredSizeInPoints opts attr (either (const def) id (imageSize opts img)) -- 12700 emu = 1 pt diff --git a/src/Text/Pandoc/Writers/FB2.hs b/src/Text/Pandoc/Writers/FB2.hs index b1e8c8575..e322c7d98 100644 --- a/src/Text/Pandoc/Writers/FB2.hs +++ b/src/Text/Pandoc/Writers/FB2.hs @@ -131,8 +131,7 @@ description meta' = do _ -> return [] return $ el "description" [ el "title-info" (genre : (bt ++ as ++ dd ++ lang)) - , el "document-info" ([ el "program-used" "pandoc" ] -- FIXME: +version - ++ coverpage) + , el "document-info" (el "program-used" "pandoc" : coverpage) ] booktitle :: PandocMonad m => Meta -> FBM m [Content] diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 5d5c88dd9..9e2347798 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -56,7 +56,8 @@ import qualified Data.Text.Lazy as TL import Network.HTTP (urlEncode) import Network.URI (URI (..), parseURIReference, unEscapeString) import Numeric (showHex) -import Text.Blaze.Internal (customLeaf, MarkupM(Empty)) +import Text.Blaze.Internal + (customLeaf, MarkupM(Empty), preEscapedString, preEscapedText) import Text.Blaze.Html hiding (contents) import Text.Pandoc.Definition import Text.Pandoc.Highlighting (formatHtmlBlock, formatHtmlInline, highlight, @@ -424,7 +425,7 @@ elementToHtml slideLevel opts (Sec level num (id',classes,keyvals) title' elemen modify (\st -> st{ stElement = False}) return res - let isSec (Sec{}) = True + let isSec Sec{} = True isSec (Blk _) = False let isPause (Blk x) = x == Para [Str ".",Space,Str ".",Space,Str "."] isPause _ = False @@ -618,7 +619,7 @@ imageExts = [ "art", "bmp", "cdr", "cdt", "cpt", "cr2", "crw", "djvu", "erf", treatAsImage :: FilePath -> Bool treatAsImage fp = - let path = fromMaybe fp (uriPath `fmap` parseURIReference fp) + let path = maybe fp uriPath (parseURIReference fp) ext = map toLower $ drop 1 $ takeExtension path in null ext || ext `elem` imageExts @@ -797,8 +798,8 @@ blockToHtml opts (OrderedList (startnum, numstyle, _) lst) = do let numstyle' = case numstyle of Example -> "decimal" _ -> camelCaseToHyphenated $ show numstyle - let attribs = ([A.start $ toValue startnum | startnum /= 1]) ++ - ([A.class_ "example" | numstyle == Example]) ++ + let attribs = [A.start $ toValue startnum | startnum /= 1] ++ + [A.class_ "example" | numstyle == Example] ++ (if numstyle /= DefaultStyle then if html5 then [A.type_ $ @@ -819,7 +820,7 @@ blockToHtml opts (DefinitionList lst) = do do term' <- if null term then return mempty else liftM H.dt $ inlineListToHtml opts term - defs' <- mapM (liftM (\x -> H.dd $ (x >> nl opts)) . + defs' <- mapM (liftM (\x -> H.dd (x >> nl opts)) . blockListToHtml opts) defs return $ mconcat $ nl opts : term' : nl opts : intersperse (nl opts) defs') lst diff --git a/src/Text/Pandoc/Writers/Haddock.hs b/src/Text/Pandoc/Writers/Haddock.hs index 9ed3be6cf..688c1f390 100644 --- a/src/Text/Pandoc/Writers/Haddock.hs +++ b/src/Text/Pandoc/Writers/Haddock.hs @@ -168,8 +168,7 @@ blockToHaddock opts (OrderedList (start,_,delim) items) = do let markers' = map (\m -> if length m < 3 then m ++ replicate (3 - length m) ' ' else m) markers - contents <- mapM (uncurry (orderedListItemToHaddock opts)) $ - zip markers' items + contents <- zipWithM (orderedListItemToHaddock opts) markers' items return $ cat contents <> blankline blockToHaddock opts (DefinitionList items) = do contents <- mapM (definitionListItemToHaddock opts) items diff --git a/src/Text/Pandoc/Writers/ICML.hs b/src/Text/Pandoc/Writers/ICML.hs index 80d2fcbef..a5d851e40 100644 --- a/src/Text/Pandoc/Writers/ICML.hs +++ b/src/Text/Pandoc/Writers/ICML.hs @@ -154,7 +154,7 @@ writeICML opts (Pandoc meta blocks) = do -- | Auxilary functions for parStylesToDoc and charStylesToDoc. contains :: String -> (String, (String, String)) -> [(String, String)] contains s rule = - [snd rule | isInfixOf (fst rule) s] + [snd rule | (fst rule) `isInfixOf` s] -- | The monospaced font to use as default. monospacedFont :: Doc @@ -282,7 +282,7 @@ hyperlinksToDoc (x:xs) = hyp x $$ hyperlinksToDoc xs ("Source","htss-"++show ident), ("Visible","true"), ("DestinationUniqueKey","1")] $ inTags True "Properties" [] $ inTags False "BorderColor" [("type","enumeration")] (text "Black") - $$ inTags False "Destination" [("type","object")] (text $ "HyperlinkURLDestination/"++(escapeColons (escapeStringForXML url))) -- HyperlinkURLDestination with more than one colon crashes CS6 + $$ inTags False "Destination" [("type","object")] (text $ "HyperlinkURLDestination/"++escapeColons (escapeStringForXML url)) -- HyperlinkURLDestination with more than one colon crashes CS6 -- | Convert a list of Pandoc blocks to ICML. diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index de2cc3480..fa72f0f1a 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -41,7 +41,7 @@ import Data.Char (isAlphaNum, isAscii, isDigit, isLetter, isPunctuation, ord, toLower) import Data.List (foldl', intercalate, intersperse, isInfixOf, nubBy, stripPrefix, (\\)) -import Data.Maybe (catMaybes, fromMaybe, isJust, mapMaybe) +import Data.Maybe (catMaybes, fromMaybe, isJust, mapMaybe, isNothing) import Data.Text (Text) import qualified Data.Text as T import Network.URI (unEscapeString) @@ -401,7 +401,7 @@ elementToBeamer slideLevel (Sec lvl _num (ident,classes,kvs) tit elts) let frameoptions = ["allowdisplaybreaks", "allowframebreaks", "fragile", "b", "c", "t", "environment", "label", "plain", "shrink", "standout"] - let optionslist = ["fragile" | fragile && lookup "fragile" kvs == Nothing] ++ + let optionslist = ["fragile" | fragile && isNothing (lookup "fragile" kvs)] ++ [k | k <- classes, k `elem` frameoptions] ++ [k ++ "=" ++ v | (k,v) <- kvs, k `elem` frameoptions] let options = if null optionslist @@ -819,7 +819,7 @@ listItemToLaTeX lst -- we need to put some text before a header if it's the first -- element in an item. This will look ugly in LaTeX regardless, but -- this will keep the typesetter from throwing an error. - | (Header _ _ _ :_) <- lst = + | (Header{} :_) <- lst = blockListToLaTeX lst >>= return . (text "\\item ~" $$) . nest 2 | otherwise = blockListToLaTeX lst >>= return . (text "\\item" $$) . nest 2 @@ -856,7 +856,7 @@ sectionHeader unnumbered ident level lst = do plain <- stringToLaTeX TextString $ concatMap stringify lst let removeInvalidInline (Note _) = [] removeInvalidInline (Span (id', _, _) _) | not (null id') = [] - removeInvalidInline (Image{}) = [] + removeInvalidInline Image{} = [] removeInvalidInline x = [x] let lstNoNotes = foldr (mappend . (\x -> walkM removeInvalidInline x)) mempty lst txtNoNotes <- inlineListToLaTeX lstNoNotes diff --git a/src/Text/Pandoc/Writers/Man.hs b/src/Text/Pandoc/Writers/Man.hs index c1427b15c..1be955fe3 100644 --- a/src/Text/Pandoc/Writers/Man.hs +++ b/src/Text/Pandoc/Writers/Man.hs @@ -114,7 +114,7 @@ notesToMan :: PandocMonad m => WriterOptions -> [[Block]] -> StateT WriterState notesToMan opts notes = if null notes then return empty - else mapM (uncurry (noteToMan opts)) (zip [1..] notes) >>= + else zipWithM (noteToMan opts) [1..] notes >>= return . (text ".SH NOTES" $$) . vcat -- | Return man representation of a note. diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index 7c4865da8..fbebe5c20 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -218,7 +218,7 @@ blockToMuse (DefinitionList items) = do descriptionToMuse :: PandocMonad m => [Block] -> StateT WriterState m Doc - descriptionToMuse desc = (hang 4 " :: ") <$> blockListToMuse desc + descriptionToMuse desc = hang 4 " :: " <$> blockListToMuse desc blockToMuse (Header level (ident,_,_) inlines) = do opts <- gets stOptions contents <- inlineListToMuse inlines diff --git a/src/Text/Pandoc/Writers/OOXML.hs b/src/Text/Pandoc/Writers/OOXML.hs index 2a9b9bc84..30d8d72dd 100644 --- a/src/Text/Pandoc/Writers/OOXML.hs +++ b/src/Text/Pandoc/Writers/OOXML.hs @@ -104,5 +104,5 @@ fitToPage :: (Double, Double) -> Integer -> (Integer, Integer) fitToPage (x, y) pageWidth -- Fixes width to the page width and scales the height | x > fromIntegral pageWidth = - (pageWidth, floor $ ((fromIntegral pageWidth) / x) * y) + (pageWidth, floor $ (fromIntegral pageWidth / x) * y) | otherwise = (floor x, floor y) diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs index e0097f507..17edc0cbd 100644 --- a/src/Text/Pandoc/Writers/OpenDocument.hs +++ b/src/Text/Pandoc/Writers/OpenDocument.hs @@ -594,7 +594,7 @@ paraStyle attrs = do tight = if t then [ ("fo:margin-top" , "0in" ) , ("fo:margin-bottom" , "0in" )] else [] - indent = if (i /= 0 || b) + indent = if i /= 0 || b then [ ("fo:margin-left" , indentVal) , ("fo:margin-right" , "0in" ) , ("fo:text-indent" , "0in" ) diff --git a/src/Text/Pandoc/Writers/Powerpoint.hs b/src/Text/Pandoc/Writers/Powerpoint.hs index acb33f582..645a4cb86 100644 --- a/src/Text/Pandoc/Writers/Powerpoint.hs +++ b/src/Text/Pandoc/Writers/Powerpoint.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE PatternGuards #-} + {- Copyright (C) 2017-2018 Jesse Rosenthal <jrosenthal@jhu.edu> diff --git a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs index f5f7d850f..0cf01ee01 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs @@ -72,7 +72,7 @@ import qualified Text.Pandoc.Shared as Shared -- so we don't overlap "Element" import Text.Pandoc.Writers.Shared (metaValueToInlines) import qualified Data.Map as M import qualified Data.Set as S -import Data.Maybe (maybeToList) +import Data.Maybe (maybeToList, fromMaybe) import Text.Pandoc.Highlighting import qualified Data.Text as T import Control.Applicative ((<|>)) @@ -136,7 +136,7 @@ reservedSlideIds = S.fromList [ metadataSlideId uniqueSlideId' :: Integer -> S.Set SlideId -> String -> SlideId uniqueSlideId' n idSet s = - let s' = if n == 0 then s else (s ++ "-" ++ show n) + let s' = if n == 0 then s else s ++ "-" ++ show n in if SlideId s' `S.member` idSet then uniqueSlideId' (n+1) idSet s else SlideId s' @@ -152,7 +152,7 @@ runUniqueSlideId s = do return sldId addLogMessage :: LogMessage -> Pres () -addLogMessage msg = modify $ \st -> st{stLog = msg : (stLog st)} +addLogMessage msg = modify $ \st -> st{stLog = msg : stLog st} type Pres = ReaderT WriterEnv (State WriterState) @@ -180,7 +180,7 @@ data DocProps = DocProps { dcTitle :: Maybe String data Slide = Slide { slideId :: SlideId , slideLayout :: Layout - , slideNotes :: (Maybe Notes) + , slideNotes :: Maybe Notes } deriving (Show, Eq) newtype SlideId = SlideId String @@ -345,12 +345,12 @@ inlineToParElems (SmallCaps ils) = inlineToParElems Space = inlineToParElems (Str " ") inlineToParElems SoftBreak = inlineToParElems (Str " ") inlineToParElems LineBreak = return [Break] -inlineToParElems (Link _ ils (url, title)) = do +inlineToParElems (Link _ ils (url, title)) = local (\r ->r{envRunProps = (envRunProps r){rLink = Just $ ExternalTarget (url, title)}}) $ - inlinesToParElems ils -inlineToParElems (Code _ str) = do + inlinesToParElems ils +inlineToParElems (Code _ str) = local (\r ->r{envRunProps = (envRunProps r){rPropCode = True}}) $ - inlineToParElems $ Str str + inlineToParElems $ Str str inlineToParElems (Math mathtype str) = return [MathElem mathtype (TeXString str)] inlineToParElems (Note blks) = do @@ -409,7 +409,7 @@ blockToParagraphs (CodeBlock attr str) = Just sty -> case highlight synMap (formatSourceLines sty) attr str of Right pElems -> do pProps <- asks envParaProps - return $ [Paragraph pProps pElems] + return [Paragraph pProps pElems] Left _ -> blockToParagraphs $ Para [Str str] Nothing -> blockToParagraphs $ Para [Str str] -- We can't yet do incremental lists, but we should render a @@ -463,7 +463,7 @@ blockToParagraphs (DefinitionList entries) = do definition <- concatMapM (blockToParagraphs . BlockQuote) blksLst return $ term ++ definition concatMapM go entries -blockToParagraphs (Div (_, ("notes" : []), _) _) = return [] +blockToParagraphs (Div (_, "notes" : [], _) _) = return [] blockToParagraphs (Div _ blks) = concatMapM blockToParagraphs blks blockToParagraphs blk = do addLogMessage $ BlockNotRendered blk @@ -481,7 +481,7 @@ multiParBullet (b:bs) = do cellToParagraphs :: Alignment -> TableCell -> Pres [Paragraph] cellToParagraphs algn tblCell = do - paras <- mapM (blockToParagraphs) tblCell + paras <- mapM blockToParagraphs tblCell let alignment = case algn of AlignLeft -> Just AlgnLeft AlignRight -> Just AlgnRight @@ -494,7 +494,7 @@ rowToParagraphs :: [Alignment] -> [TableCell] -> Pres [[Paragraph]] rowToParagraphs algns tblCells = do -- We have to make sure we have the right number of alignments let pairs = zip (algns ++ repeat AlignDefault) tblCells - mapM (\(a, tc) -> cellToParagraphs a tc) pairs + mapM (uncurry cellToParagraphs) pairs withAttr :: Attr -> Shape -> Shape withAttr attr (Pic picPr url caption) = @@ -507,17 +507,17 @@ withAttr _ sp = sp blockToShape :: Block -> Pres Shape blockToShape (Plain (il:_)) | Image attr ils (url, _) <- il = - (withAttr attr . Pic def url) <$> (inlinesToParElems ils) + (withAttr attr . Pic def url) <$> inlinesToParElems ils blockToShape (Para (il:_)) | Image attr ils (url, _) <- il = - (withAttr attr . Pic def url) <$> (inlinesToParElems ils) + (withAttr attr . Pic def url) <$> inlinesToParElems ils blockToShape (Plain (il:_)) | Link _ (il':_) target <- il , Image attr ils (url, _) <- il' = (withAttr attr . Pic def {picPropLink = Just $ ExternalTarget target} url) <$> - (inlinesToParElems ils) + inlinesToParElems ils blockToShape (Para (il:_)) | Link _ (il':_) target <- il , Image attr ils (url, _) <- il' = (withAttr attr . Pic def{picPropLink = Just $ ExternalTarget target} url) <$> - (inlinesToParElems ils) + inlinesToParElems ils blockToShape (Table caption algn _ hdrCells rows) = do caption' <- inlinesToParElems caption hdrCells' <- rowToParagraphs algn hdrCells @@ -537,11 +537,11 @@ blockToShape blk = do paras <- blockToParagraphs blk combineShapes :: [Shape] -> [Shape] combineShapes [] = [] -combineShapes (s : []) = [s] -combineShapes (pic@(Pic _ _ _) : ss) = pic : combineShapes ss -combineShapes ((TextBox []) : ss) = combineShapes ss +combineShapes[s] = [s] +combineShapes (pic@(Pic{}) : ss) = pic : combineShapes ss +combineShapes (TextBox [] : ss) = combineShapes ss combineShapes (s : TextBox [] : ss) = combineShapes (s : ss) -combineShapes ((TextBox (p:ps)) : (TextBox (p':ps')) : ss) = +combineShapes (TextBox (p:ps) : TextBox (p':ps') : ss) = combineShapes $ TextBox ((p:ps) ++ (p':ps')) : ss combineShapes (s:ss) = s : combineShapes ss @@ -549,8 +549,8 @@ blocksToShapes :: [Block] -> Pres [Shape] blocksToShapes blks = combineShapes <$> mapM blockToShape blks isImage :: Inline -> Bool -isImage (Image _ _ _) = True -isImage (Link _ ((Image _ _ _) : _) _) = True +isImage (Image{}) = True +isImage (Link _ (Image _ _ _ : _) _) = True isImage _ = False splitBlocks' :: [Block] -> [[Block]] -> [Block] -> Pres [[Block]] @@ -565,27 +565,27 @@ splitBlocks' cur acc (h@(Header n _ _) : blks) = do GT -> splitBlocks' (cur ++ [h]) acc blks -- `blockToParagraphs` treats Plain and Para the same, so we can save -- some code duplication by treating them the same here. -splitBlocks' cur acc ((Plain ils) : blks) = splitBlocks' cur acc ((Para ils) : blks) -splitBlocks' cur acc ((Para (il:ils)) : blks) | isImage il = do +splitBlocks' cur acc (Plain ils : blks) = splitBlocks' cur acc (Para ils : blks) +splitBlocks' cur acc (Para (il:ils) : blks) | isImage il = do slideLevel <- asks envSlideLevel case cur of - (Header n _ _) : [] | n == slideLevel -> + [(Header n _ _)] | n == slideLevel -> splitBlocks' [] (acc ++ [cur ++ [Para [il]]]) - (if null ils then blks else (Para ils) : blks) + (if null ils then blks else Para ils : blks) _ -> splitBlocks' [] (acc ++ (if null cur then [] else [cur]) ++ [[Para [il]]]) - (if null ils then blks else (Para ils) : blks) -splitBlocks' cur acc (tbl@(Table _ _ _ _ _) : blks) = do + (if null ils then blks else Para ils : blks) +splitBlocks' cur acc (tbl@(Table{}) : blks) = do slideLevel <- asks envSlideLevel case cur of - (Header n _ _) : [] | n == slideLevel -> + [(Header n _ _)] | n == slideLevel -> splitBlocks' [] (acc ++ [cur ++ [tbl]]) blks _ -> splitBlocks' [] (acc ++ (if null cur then [] else [cur]) ++ [[tbl]]) blks splitBlocks' cur acc (d@(Div (_, classes, _) _): blks) | "columns" `elem` classes = do slideLevel <- asks envSlideLevel case cur of - (Header n _ _) : [] | n == slideLevel -> + [(Header n _ _)] | n == slideLevel -> splitBlocks' [] (acc ++ [cur ++ [d]]) blks _ -> splitBlocks' [] (acc ++ (if null cur then [] else [cur]) ++ [[d]]) blks splitBlocks' cur acc (blk : blks) = splitBlocks' (cur ++ [blk]) acc blks @@ -594,12 +594,12 @@ splitBlocks :: [Block] -> Pres [[Block]] splitBlocks = splitBlocks' [] [] blocksToSlide' :: Int -> [Block] -> Pres Slide -blocksToSlide' lvl ((Header n (ident, _, _) ils) : blks) +blocksToSlide' lvl (Header n (ident, _, _) ils : blks) | n < lvl = do registerAnchorId ident sldId <- asks envCurSlideId hdr <- inlinesToParElems ils - return $ Slide sldId (TitleSlide {titleSlideHeader = hdr}) Nothing + return $ Slide sldId TitleSlide {titleSlideHeader = hdr} Nothing | n == lvl = do registerAnchorId ident hdr <- inlinesToParElems ils @@ -614,7 +614,7 @@ blocksToSlide' lvl ((Header n (ident, _, _) ils) : blks) blocksToSlide' _ (blk : blks) | Div (_, classes, _) divBlks <- blk , "columns" `elem` classes - , (Div (_, clsL, _) blksL) : (Div (_, clsR, _) blksR) : remaining <- divBlks + , Div (_, clsL, _) blksL : Div (_, clsR, _) blksR : remaining <- divBlks , "column" `elem` clsL, "column" `elem` clsR = do unless (null blks) (mapM (addLogMessage . BlockNotRendered) blks >> return ()) @@ -672,7 +672,7 @@ makeNoteEntry n blks = in case blks of (Para ils : blks') -> (Para $ enum : Space : ils) : blks' - _ -> (Para [enum]) : blks + _ -> Para [enum] : blks forceFontSize :: Pixels -> Pres a -> Pres a forceFontSize px x = do @@ -860,7 +860,7 @@ blocksToPresentationSlides blks = do (\env -> env { envCurSlideId = endNotesSlideId , envInNoteSlide = True }) - (blocksToSlide $ endNotesSlideBlocks) + (blocksToSlide endNotesSlideBlocks) return [endNotesSlide] let slides = metadataslides ++ tocSlides ++ bodyslides ++ endNotesSlides @@ -889,9 +889,7 @@ documentToPresentation :: WriterOptions documentToPresentation opts (Pandoc meta blks) = let env = def { envOpts = opts , envMetadata = meta - , envSlideLevel = case writerSlideLevel opts of - Just lvl -> lvl - Nothing -> getSlideLevel blks + , envSlideLevel = fromMaybe (getSlideLevel blks) (writerSlideLevel opts) } (presSlides, msgs) = runPres env def $ blocksToPresentationSlides blks docProps = metaToDocProps meta diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs index a57527aa8..95cb46643 100644 --- a/src/Text/Pandoc/Writers/RST.hs +++ b/src/Text/Pandoc/Writers/RST.hs @@ -132,7 +132,7 @@ keyToRST (label, (src, _)) = do -- | Return RST representation of notes. notesToRST :: PandocMonad m => [[Block]] -> RST m Doc notesToRST notes = - mapM (uncurry noteToRST) (zip [1..] notes) >>= + zipWithM noteToRST [1..] notes >>= return . vsep -- | Return RST representation of a note. @@ -306,8 +306,7 @@ blockToRST (OrderedList (start, style', delim) items) = do let maxMarkerLength = maximum $ map length markers let markers' = map (\m -> let s = maxMarkerLength - length m in m ++ replicate s ' ') markers - contents <- mapM (uncurry orderedListItemToRST) $ - zip markers' items + contents <- zipWithM orderedListItemToRST markers' items -- ensure that sublists have preceding blank line return $ blankline $$ chomp (vcat contents) $$ blankline blockToRST (DefinitionList items) = do @@ -356,12 +355,12 @@ blockListToRST' topLevel blocks = do let fixBlocks (b1:b2@(BlockQuote _):bs) | toClose b1 = b1 : commentSep : b2 : fixBlocks bs where - toClose (Plain{}) = False - toClose (Header{}) = False - toClose (LineBlock{}) = False - toClose (HorizontalRule) = False + toClose Plain{} = False + toClose Header{} = False + toClose LineBlock{} = False + toClose HorizontalRule = False toClose (Para [Image _ _ (_,'f':'i':'g':':':_)]) = True - toClose (Para{}) = False + toClose Para{} = False toClose _ = True commentSep = RawBlock "rst" "..\n\n" fixBlocks (b:bs) = b : fixBlocks bs diff --git a/src/Text/Pandoc/Writers/RTF.hs b/src/Text/Pandoc/Writers/RTF.hs index 790bebc01..7006b58d1 100644 --- a/src/Text/Pandoc/Writers/RTF.hs +++ b/src/Text/Pandoc/Writers/RTF.hs @@ -31,6 +31,7 @@ Conversion of 'Pandoc' documents to RTF (rich text format). module Text.Pandoc.Writers.RTF ( writeRTF ) where import Control.Monad.Except (catchError, throwError) +import Control.Monad import qualified Data.ByteString as B import Data.Char (chr, isDigit, ord) import Data.List (intercalate, isSuffixOf) @@ -278,8 +279,7 @@ blockToRTF indent alignment (BulletList lst) = (spaceAtEnd . concat) <$> mapM (listItemToRTF alignment indent (bulletMarker indent)) lst blockToRTF indent alignment (OrderedList attribs lst) = (spaceAtEnd . concat) <$> - mapM (uncurry (listItemToRTF alignment indent)) - (zip (orderedMarkers indent attribs) lst) + zipWithM (listItemToRTF alignment indent) (orderedMarkers indent attribs) lst blockToRTF indent alignment (DefinitionList lst) = (spaceAtEnd . concat) <$> mapM (definitionListItemToRTF alignment indent) lst blockToRTF indent _ HorizontalRule = return $ @@ -303,8 +303,8 @@ tableRowToRTF header indent aligns sizes' cols = do let sizes = if all (== 0) sizes' then replicate (length cols) (1.0 / fromIntegral (length cols)) else sizes' - columns <- concat <$> mapM (uncurry (tableItemToRTF indent)) - (zip aligns cols) + columns <- concat <$> + zipWithM (tableItemToRTF indent) aligns cols let rightEdges = tail $ scanl (\sofar new -> sofar + floor (new * totalTwips)) (0 :: Integer) sizes let cellDefs = map (\edge -> (if header diff --git a/src/Text/Pandoc/Writers/Texinfo.hs b/src/Text/Pandoc/Writers/Texinfo.hs index b5d72aa56..bf434642e 100644 --- a/src/Text/Pandoc/Writers/Texinfo.hs +++ b/src/Text/Pandoc/Writers/Texinfo.hs @@ -475,7 +475,7 @@ inlineToTexinfo (Link _ txt (src@('#':_), _)) = do inlineToTexinfo (Link _ txt (src, _)) = case txt of [Str x] | escapeURI x == src -> -- autolink - do return $ text $ "@url{" ++ x ++ "}" + return $ text $ "@url{" ++ x ++ "}" _ -> do contents <- escapeCommas $ inlineListToTexinfo txt let src1 = stringToTexinfo src return $ text ("@uref{" ++ src1 ++ ",") <> contents <> -- cgit v1.2.3 From 8ffb4e5b334a302ba7b07c4213e7102c23ef7721 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 19 Jan 2018 22:46:29 -0800 Subject: Conditional import to avoid warning. --- src/Text/Pandoc/Writers/HTML.hs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 9e2347798..cbceae2ce 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -56,8 +56,11 @@ import qualified Data.Text.Lazy as TL import Network.HTTP (urlEncode) import Network.URI (URI (..), parseURIReference, unEscapeString) import Numeric (showHex) -import Text.Blaze.Internal - (customLeaf, MarkupM(Empty), preEscapedString, preEscapedText) +import Text.Blaze.Internal (customLeaf, MarkupM(Empty)) +#if MIN_VERSION_blaze_markup(0,6,3) +#else +import Text.Blaze.Internal (preEscapedString, preEscapedText) +#endif import Text.Blaze.Html hiding (contents) import Text.Pandoc.Definition import Text.Pandoc.Highlighting (formatHtmlBlock, formatHtmlInline, highlight, -- cgit v1.2.3 From 736c2c554f3c93a1c862fea7fd2c90680de3500d Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sat, 20 Jan 2018 09:38:57 -0500 Subject: Docx reader: small change to Fields hyperlink parser Previously, unquoted string required a space at the end of the line (and consumed it). Now we either take a space (and don't consume it), or end of input. --- src/Text/Pandoc/Readers/Docx/Fields.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx/Fields.hs b/src/Text/Pandoc/Readers/Docx/Fields.hs index f0821a751..aef10ffcf 100644 --- a/src/Text/Pandoc/Readers/Docx/Fields.hs +++ b/src/Text/Pandoc/Readers/Docx/Fields.hs @@ -63,7 +63,7 @@ quotedString = do concat <$> manyTill inQuotes (try (char '"')) unquotedString :: Parser String -unquotedString = manyTill anyChar (try space) +unquotedString = manyTill anyChar (try $ lookAhead space $> () <|> eof) fieldArgument :: Parser String fieldArgument = quotedString <|> unquotedString -- cgit v1.2.3 From fa912bb15e3ceac863fb8d46ee6f137da34eb634 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sat, 20 Jan 2018 09:48:47 -0500 Subject: Docx reader: Use already imported operator This fixes an import error in the last commit. --- src/Text/Pandoc/Readers/Docx/Fields.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx/Fields.hs b/src/Text/Pandoc/Readers/Docx/Fields.hs index aef10ffcf..6eeb55d2f 100644 --- a/src/Text/Pandoc/Readers/Docx/Fields.hs +++ b/src/Text/Pandoc/Readers/Docx/Fields.hs @@ -63,7 +63,7 @@ quotedString = do concat <$> manyTill inQuotes (try (char '"')) unquotedString :: Parser String -unquotedString = manyTill anyChar (try $ lookAhead space $> () <|> eof) +unquotedString = manyTill anyChar (try $ lookAhead space *> return () <|> eof) fieldArgument :: Parser String fieldArgument = quotedString <|> unquotedString -- cgit v1.2.3 From 957c0e110dd54cf7adaca6c9d7eb0b01c8b0210c Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 20 Jan 2018 11:08:08 -0800 Subject: RST reader: fix parsing of headers with trailing space. This was a regression in pandoc 2.0. Closes #4280. --- src/Text/Pandoc/Readers/RST.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index 0e90fe945..e88d997f0 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -352,7 +352,7 @@ singleHeader' :: PandocMonad m => RSTParser m (Inlines, Char) singleHeader' = try $ do notFollowedBy' whitespace lookAhead $ anyLine >> oneOf underlineChars - txt <- trimInlines . mconcat <$> many1 (do {notFollowedBy newline; inline}) + txt <- trimInlines . mconcat <$> many1 (notFollowedBy blankline >> inline) pos <- getPosition let len = sourceColumn pos - 1 blankline -- cgit v1.2.3 From 1dd5018a807a80866c35d73b9c0131cc9bc16cb5 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sun, 21 Jan 2018 00:47:30 +0300 Subject: Muse reader: simplify paragraph parsing Blanklines are already consumed during block parsing, there is no need to check for them specifically. --- src/Text/Pandoc/Readers/Muse.hs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 973dfa15c..752461e6a 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -341,9 +341,8 @@ para = do let f = if st /= ListItemState && indent >= 2 && indent < 6 then B.blockQuote else id fmap (f . B.para) . trimInlinesF . mconcat <$> many1Till inline endOfParaElement where - endOfParaElement = lookAhead $ endOfInput <|> endOfPara <|> newBlockElement + endOfParaElement = lookAhead $ endOfInput <|> newBlockElement endOfInput = try $ skipMany blankline >> skipSpaces >> eof - endOfPara = try $ blankline >> skipMany1 blankline newBlockElement = try $ blankline >> void blockElements noteMarker :: PandocMonad m => MuseParser m String -- cgit v1.2.3 From ac08a887cf83ce3d08b56447a1dbfc8aec4cd1a3 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 20 Jan 2018 14:43:33 -0800 Subject: Markdown reader: Fix parsing bug with nested fenced divs. Closes #4281. Previously we allowed "nonindent spaces" before the opening and closing `:::`, but this interfered with list parsing, so now we require the fences to be flush with the margin of the containing block. --- src/Text/Pandoc/Readers/Markdown.hs | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs index 92e9098bd..14cf73de4 100644 --- a/src/Text/Pandoc/Readers/Markdown.hs +++ b/src/Text/Pandoc/Readers/Markdown.hs @@ -1971,7 +1971,6 @@ divHtml = try $ do divFenced :: PandocMonad m => MarkdownParser m (F Blocks) divFenced = try $ do guardEnabled Ext_fenced_divs - nonindentSpaces string ":::" skipMany (char ':') skipMany spaceChar @@ -1986,7 +1985,6 @@ divFenced = try $ do divFenceEnd :: PandocMonad m => MarkdownParser m () divFenceEnd = try $ do - nonindentSpaces string ":::" skipMany (char ':') blanklines -- cgit v1.2.3 From eaebc5fd19ac7f035805ddfec90ebeca17cd6561 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sun, 21 Jan 2018 02:32:26 +0300 Subject: Muse reader: make listItem generic --- src/Text/Pandoc/Readers/Muse.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 752461e6a..daebe81aa 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -432,7 +432,7 @@ listItemContents = do let col = sourceColumn pos - 1 listItemContents' col -listItem :: PandocMonad m => Int -> MuseParser m () -> MuseParser m (F Blocks) +listItem :: PandocMonad m => Int -> MuseParser m a -> MuseParser m (F Blocks) listItem n p = try $ do optionMaybe blankline count n spaceChar @@ -449,7 +449,7 @@ bulletList = try $ do char '-' void spaceChar <|> lookAhead eol first <- listItemContents - rest <- many $ listItem (col - 1) (void (char '-')) + rest <- many $ listItem (col - 1) (char '-') return $ B.bulletList <$> sequence (first : rest) orderedList :: PandocMonad m => MuseParser m (F Blocks) @@ -463,7 +463,7 @@ orderedList = try $ do guard $ delim == Period void spaceChar <|> lookAhead eol first <- listItemContents - rest <- many $ listItem (col - 1) (void (orderedListMarker style delim)) + rest <- many $ listItem (col - 1) (orderedListMarker style delim) return $ B.orderedListWith p <$> sequence (first : rest) definitionListItem :: PandocMonad m => Int -> MuseParser m (F (Inlines, [Blocks])) -- cgit v1.2.3 From 91bca732666d8825702c4986d0dfd5a6d3fee3bb Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sun, 21 Jan 2018 03:06:58 +0300 Subject: Muse reader: embed parseBlocks into parseMuse --- src/Text/Pandoc/Readers/Muse.hs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index daebe81aa..8f36db9d1 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -79,7 +79,8 @@ type MuseParser = ParserT String ParserState parseMuse :: PandocMonad m => MuseParser m Pandoc parseMuse = do many directive - blocks <- parseBlocks + blocks <- mconcat <$> many block + eof st <- getState let doc = runF (do Pandoc _ bs <- B.doc <$> blocks meta <- stateMeta' st @@ -87,13 +88,6 @@ parseMuse = do reportLogMessages return doc -parseBlocks :: PandocMonad m => MuseParser m (F Blocks) -parseBlocks = do - res <- mconcat <$> many block - spaces - eof - return res - -- -- utility functions -- -- cgit v1.2.3 From 288065cfd44eb02f9ef4e6c9b901e14776246dbb Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 22 Jan 2018 16:24:44 +0300 Subject: Muse writer: indent lists inside Div --- src/Text/Pandoc/Writers/Muse.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index fbebe5c20..23aa98866 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -259,7 +259,7 @@ blockToMuse (Table caption _ _ headers rows) = do $$ body $$ (if null caption then empty else " |+ " <> caption' <> " +|") $$ blankline -blockToMuse (Div _ bs) = blockListToMuse bs +blockToMuse (Div _ bs) = flatBlockListToMuse bs blockToMuse Null = return empty -- | Return Muse representation of notes. -- cgit v1.2.3 From e9ed4832edb1a9f9c3cd7b6c670c39f513444192 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 22 Jan 2018 16:27:14 +0300 Subject: Muse writer: join code with different attributes during normalization --- src/Text/Pandoc/Writers/Muse.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index 23aa98866..164a46411 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -307,8 +307,8 @@ normalizeInlineList (Subscript x1 : Subscript x2 : ils) = normalizeInlineList $ Subscript (x1 ++ x2) : ils normalizeInlineList (SmallCaps x1 : SmallCaps x2 : ils) = normalizeInlineList $ SmallCaps (x1 ++ x2) : ils -normalizeInlineList (Code a1 x1 : Code a2 x2 : ils) | a1 == a2 - = normalizeInlineList $ Code a1 (x1 ++ x2) : ils +normalizeInlineList (Code _ x1 : Code _ x2 : ils) + = normalizeInlineList $ Code nullAttr (x1 ++ x2) : ils normalizeInlineList (RawInline f1 x1 : RawInline f2 x2 : ils) | f1 == f2 = normalizeInlineList $ RawInline f1 (x1 ++ x2) : ils normalizeInlineList (Span a1 x1 : Span a2 x2 : ils) | a1 == a2 -- cgit v1.2.3 From 71bbadb7936e68673a46f7391caf446e35c81205 Mon Sep 17 00:00:00 2001 From: Henri Menke <henri@icp.uni-stuttgart.de> Date: Tue, 23 Jan 2018 09:15:59 +1300 Subject: ConTeXt writer: xtables: correct wrong usage of caption --- src/Text/Pandoc/Writers/ConTeXt.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index 64b7d2c53..bca00b55a 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -267,7 +267,7 @@ blockToConTeXt (Table caption aligns widths heads rows) = do return $ "\\startplacetable" <> brackets ( if null caption then "location=none" - else "caption=" <> braces captionText + else "title=" <> braces captionText ) $$ body $$ "\\stopplacetable" <> blankline tableToConTeXt :: PandocMonad m => Tabl -> Doc -> [Doc] -> WM m Doc -- cgit v1.2.3 From 2e0bb773345f489798666ac3c2d96d3873fa82a3 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Tue, 23 Jan 2018 21:29:52 +0100 Subject: Lua: move getTag from StackInstances to Util Change: minor --- src/Text/Pandoc/Lua/StackInstances.hs | 11 +---------- src/Text/Pandoc/Lua/Util.hs | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 13 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/StackInstances.hs b/src/Text/Pandoc/Lua/StackInstances.hs index a504e5626..7e0dc20c4 100644 --- a/src/Text/Pandoc/Lua/StackInstances.hs +++ b/src/Text/Pandoc/Lua/StackInstances.hs @@ -42,8 +42,7 @@ import Foreign.Lua (FromLuaStack (peek), Lua, LuaInteger, LuaNumber, StackIndex, ToLuaStack (push), Type (..), throwLuaError, tryLua) import Text.Pandoc.Definition import Text.Pandoc.Extensions (Extensions) -import Text.Pandoc.Lua.Util (adjustIndexBy, getTable, pushViaConstructor, - typeCheck) +import Text.Pandoc.Lua.Util (getTable, getTag, pushViaConstructor, typeCheck) import Text.Pandoc.Options (ReaderOptions (..), TrackChanges) import Text.Pandoc.Shared (Element (Blk, Sec), safeRead) @@ -300,14 +299,6 @@ peekInline idx = defineHowTo "get Inline value" $ do elementContent :: FromLuaStack a => Lua a elementContent = getTable idx "c" -getTag :: StackIndex -> Lua String -getTag idx = do - top <- Lua.gettop - hasMT <- Lua.getmetatable idx - push "tag" - if hasMT then Lua.rawget (-2) else Lua.rawget (idx `adjustIndexBy` 1) - peek Lua.stackTop `finally` Lua.settop top - withAttr :: (Attr -> a -> b) -> (LuaAttr, a) -> b withAttr f (attributes, x) = f (fromLuaAttr attributes) x diff --git a/src/Text/Pandoc/Lua/Util.hs b/src/Text/Pandoc/Lua/Util.hs index a3af155c9..f82ec4753 100644 --- a/src/Text/Pandoc/Lua/Util.hs +++ b/src/Text/Pandoc/Lua/Util.hs @@ -29,7 +29,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Lua utility functions. -} module Text.Pandoc.Lua.Util - ( adjustIndexBy + ( getTag , getTable , addValue , addFunction @@ -47,6 +47,7 @@ module Text.Pandoc.Lua.Util ) where import Control.Monad (when) +import Control.Monad.Catch (finally) import Data.ByteString.Char8 (unpack) import Foreign.Lua (FromLuaStack (..), NumResults, Lua, NumArgs, StackIndex, ToLuaStack (..), ToHaskellFunction, getglobal') @@ -163,11 +164,23 @@ loadScriptFromDataDir datadir scriptFile = do -- to @require@, the a new loader function was created which then become -- garbage. If that function is collected at an inopportune times, i.e. when the -- Lua API is called via a function that doesn't allow calling back into Haskell --- (getraw, setraw, …). The function's finalizer, and the full program, hangs --- when that happens. +-- (getraw, setraw, …), then the function's finalizer, and the full program, +-- will hang. dostring' :: String -> Lua Status dostring' script = do loadRes <- Lua.loadstring script if loadRes == Lua.OK then Lua.pcall 0 1 Nothing <* Lua.gc Lua.GCCOLLECT 0 else return loadRes + +-- | Get the tag of a value. This is an optimized and specialized version of +-- @Lua.getfield idx "tag"@. It only checks for the field on the table at index +-- @idx@ and on its metatable, also ignoring any @__index@ value on the +-- metatable. +getTag :: StackIndex -> Lua String +getTag idx = do + top <- Lua.gettop + hasMT <- Lua.getmetatable idx + push "tag" + if hasMT then Lua.rawget (-2) else Lua.rawget (idx `adjustIndexBy` 1) + peek Lua.stackTop `finally` Lua.settop top -- cgit v1.2.3 From 517f65a7cc3e94b9c4ad574369a32c48e0a95be6 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Tue, 23 Jan 2018 21:29:58 +0100 Subject: Lua filters: store constructors in registry Lua functions used to construct AST element values are stored in the Lua registry for quicker access. Getting a value from the registry is much faster than getting a global value (partly to idiosyncrasies of hslua); this change results in a considerable performance boost. --- src/Text/Pandoc/Lua/Init.hs | 25 +++++++++++++++++++++++++ src/Text/Pandoc/Lua/Module/Pandoc.hs | 2 +- src/Text/Pandoc/Lua/Util.hs | 5 +++-- 3 files changed, 29 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua/Init.hs b/src/Text/Pandoc/Lua/Init.hs index f3ee2caf1..d1a26ebad 100644 --- a/src/Text/Pandoc/Lua/Init.hs +++ b/src/Text/Pandoc/Lua/Init.hs @@ -34,6 +34,7 @@ module Text.Pandoc.Lua.Init ) where import Control.Monad.Trans (MonadIO (..)) +import Data.Data (Data, dataTypeConstrs, dataTypeOf, showConstr) import Data.IORef (newIORef, readIORef) import Data.Version (Version (versionBranch)) import Foreign.Lua (Lua, LuaException (..)) @@ -48,6 +49,7 @@ import Text.Pandoc.Lua.Util (loadScriptFromDataDir) import qualified Foreign.Lua as Lua import qualified Foreign.Lua.Module.Text as Lua +import qualified Text.Pandoc.Definition as Pandoc -- | Run the lua interpreter, using pandoc's default way of environment -- initalization. @@ -84,3 +86,26 @@ initLuaState luaPkgParams = do Lua.setglobal "PANDOC_API_VERSION" installPandocPackageSearcher luaPkgParams loadScriptFromDataDir (luaPkgDataDir luaPkgParams) "init.lua" + putConstructorsInRegistry + +putConstructorsInRegistry :: Lua () +putConstructorsInRegistry = do + Lua.getglobal "pandoc" + constrsToReg $ Pandoc.Pandoc mempty mempty + constrsToReg $ Pandoc.Str mempty + constrsToReg $ Pandoc.Para mempty + constrsToReg $ Pandoc.Meta mempty + constrsToReg $ Pandoc.MetaList mempty + constrsToReg $ Pandoc.Citation mempty mempty mempty Pandoc.AuthorInText 0 0 + putInReg "Attr" -- used for Attr type alias + Lua.pop 1 + where + constrsToReg :: Data a => a -> Lua () + constrsToReg = mapM_ putInReg . map showConstr . dataTypeConstrs . dataTypeOf + + putInReg :: String -> Lua () + putInReg name = do + Lua.push ("pandoc." ++ name) -- name in registry + Lua.push name -- in pandoc module + Lua.rawget (Lua.nthFromTop 3) + Lua.rawset Lua.registryindex diff --git a/src/Text/Pandoc/Lua/Module/Pandoc.hs b/src/Text/Pandoc/Lua/Module/Pandoc.hs index f458d4773..b9410a353 100644 --- a/src/Text/Pandoc/Lua/Module/Pandoc.hs +++ b/src/Text/Pandoc/Lua/Module/Pandoc.hs @@ -82,7 +82,7 @@ readDoc content formatSpecOrNil = do res <- liftIO $ runIO $ r def{ readerExtensions = es } (pack content) case res of Right pd -> (1 :: NumResults) <$ Lua.push pd -- success, push Pandoc - Left s -> Lua.raiseError (show s) -- error while reading + Left s -> Lua.raiseError (show s) -- error while reading _ -> Lua.raiseError "Only string formats are supported at the moment." -- | Pipes input through a command. diff --git a/src/Text/Pandoc/Lua/Util.hs b/src/Text/Pandoc/Lua/Util.hs index f82ec4753..b7149af39 100644 --- a/src/Text/Pandoc/Lua/Util.hs +++ b/src/Text/Pandoc/Lua/Util.hs @@ -50,7 +50,7 @@ import Control.Monad (when) import Control.Monad.Catch (finally) import Data.ByteString.Char8 (unpack) import Foreign.Lua (FromLuaStack (..), NumResults, Lua, NumArgs, StackIndex, - ToLuaStack (..), ToHaskellFunction, getglobal') + ToLuaStack (..), ToHaskellFunction) import Foreign.Lua.Api (Status, call, pop, rawget, rawgeti, rawset, rawseti) import Text.Pandoc.Class (readDataFile, runIOorExplode, setUserDataDir) @@ -131,7 +131,8 @@ class PushViaCall a where instance PushViaCall (Lua ()) where pushViaCall' fn pushArgs num = do - getglobal' fn + Lua.push fn + Lua.rawget (Lua.registryindex) pushArgs call num 1 -- cgit v1.2.3 From 763126dae07e29435dc8b3cc1f7f20cfff823fe0 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Wed, 24 Jan 2018 13:58:43 +0300 Subject: Muse reader: remove `block` function --- src/Text/Pandoc/Readers/Muse.hs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 8f36db9d1..a9c0162d2 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -79,7 +79,7 @@ type MuseParser = ParserT String ParserState parseMuse :: PandocMonad m => MuseParser m Pandoc parseMuse = do many directive - blocks <- mconcat <$> many block + blocks <- mconcat <$> many parseBlock eof st <- getState let doc = runF (do Pandoc _ bs <- B.doc <$> blocks @@ -187,9 +187,6 @@ parseBlock = do trace (take 60 $ show $ B.toList $ runF res defaultParserState) return res -block :: PandocMonad m => MuseParser m (F Blocks) -block = parseBlock <* skipMany blankline - blockElements :: PandocMonad m => MuseParser m (F Blocks) blockElements = choice [ mempty <$ blankline , comment @@ -287,7 +284,7 @@ blockTag :: PandocMonad m -> String -> MuseParser m (F Blocks) blockTag f s = do - res <- parseHtmlContent s block + res <- parseHtmlContent s parseBlock return $ f <$> mconcat res -- <center> tag is ignored @@ -304,7 +301,7 @@ quoteTag = withQuoteContext InDoubleQuote $ blockTag B.blockQuote "quote" -- <div> tag is supported by Emacs Muse, but not Amusewiki 2.025 divTag :: PandocMonad m => MuseParser m (F Blocks) divTag = do - (attrs, content) <- parseHtmlContentWithAttrs "div" block + (attrs, content) <- parseHtmlContentWithAttrs "div" parseBlock return $ B.divWith attrs <$> mconcat content verseLine :: PandocMonad m => MuseParser m String @@ -377,7 +374,7 @@ emacsNoteBlock = try $ do return mempty where blocksTillNote = - many1Till block (eof <|> () <$ lookAhead noteMarker) + many1Till parseBlock (eof <|> () <$ lookAhead noteMarker) -- -- Verse markup -- cgit v1.2.3 From 6337539e32cb1728e4cd9e6b1cce7313aaf04f03 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Wed, 24 Jan 2018 14:16:56 +0300 Subject: Muse reader: fix matching of closing inline tags --- src/Text/Pandoc/Readers/Muse.hs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index a9c0162d2..b64d877b3 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -658,8 +658,9 @@ inlineTag :: PandocMonad m => (Inlines -> Inlines) -> String -> MuseParser m (F Inlines) -inlineTag f s = try $ do - res <- parseHtmlContent s inline +inlineTag f tag = try $ do + htmlTag (~== TagOpen tag []) + res <- manyTill inline (void $ htmlTag (~== TagClose tag)) return $ f <$> mconcat res strongTag :: PandocMonad m => MuseParser m (F Inlines) -- cgit v1.2.3 From 0d7aedca58742f9c353010d9877ef666ee4c7af0 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Thu, 25 Jan 2018 10:31:40 -0500 Subject: Dock writer: Fix deletion track changes This had been mistakenly written as a second insertion function. Closes: #4303 --- src/Text/Pandoc/Writers/Docx.hs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 928eaa712..7cbc946cc 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -1146,18 +1146,18 @@ inlineToOpenXML' opts (Span (ident,classes,kvs) ils) = do ("w:author", author), ("w:date", date)] x ] else return id - delmod <- if "insertion" `elem` classes + delmod <- if "deletion" `elem` classes then do defaultAuthor <- asks envChangesAuthor defaultDate <- asks envChangesDate let author = fromMaybe defaultAuthor (lookup "author" kvs) date = fromMaybe defaultDate (lookup "date" kvs) - insId <- gets stInsId - modify $ \s -> s{stInsId = insId + 1} - return $ \f -> do + delId <- gets stDelId + modify $ \s -> s{stDelId = delId + 1} + return $ \f -> local (\env->env{envInDel=True}) $ do x <- f - return [mknode "w:ins" - [("w:id", show insId), + return [mknode "w:del" + [("w:id", show delId), ("w:author", author), ("w:date", date)] x] else return id -- cgit v1.2.3 From 751b5ad010794ec51699bfb89de91b38c85d3297 Mon Sep 17 00:00:00 2001 From: Henri Menke <henri@icp.uni-stuttgart.de> Date: Fri, 26 Jan 2018 08:56:28 +1300 Subject: ConTeXt writer: new section syntax and --section-divs (#4295) Fixes #2609. This PR introduces the new-style section headings: `\section[my-header]{My Header}` -> `\section[title={My Header},reference={my-header}]`. On top of this, the ConTeXt writer now supports the `--section-divs` option to write sections in the fenced style, with `\startsection` and `\stopsection`. --- src/Text/Pandoc/Writers/ConTeXt.hs | 54 ++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 17 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/ConTeXt.hs b/src/Text/Pandoc/Writers/ConTeXt.hs index bca00b55a..f94c12d89 100644 --- a/src/Text/Pandoc/Writers/ConTeXt.hs +++ b/src/Text/Pandoc/Writers/ConTeXt.hs @@ -159,8 +159,9 @@ elementToConTeXt :: PandocMonad m => WriterOptions -> Element -> WM m Doc elementToConTeXt _ (Blk block) = blockToConTeXt block elementToConTeXt opts (Sec level _ attr title' elements) = do header' <- sectionHeader attr level title' + footer' <- sectionFooter attr level innerContents <- mapM (elementToConTeXt opts) elements - return $ vcat (header' : innerContents) + return $ header' $$ vcat innerContents $$ footer' -- | Convert Pandoc block element to ConTeXt. blockToConTeXt :: PandocMonad m => Block -> WM m Doc @@ -485,32 +486,51 @@ sectionHeader :: PandocMonad m -> Int -> [Inline] -> WM m Doc -sectionHeader (ident,classes,_) hdrLevel lst = do +sectionHeader (ident,classes,kvs) hdrLevel lst = do + opts <- gets stOptions contents <- inlineListToConTeXt lst - st <- get - let opts = stOptions st + levelText <- sectionLevelToText opts (ident,classes,kvs) hdrLevel + let ident' = if null ident + then empty + else "reference=" <> braces (text (toLabel ident)) + let contents' = if contents == empty + then empty + else "title=" <> braces contents + let options = if keys == empty || levelText == empty + then empty + else brackets keys + where keys = hcat $ intersperse "," $ filter (empty /=) [contents', ident'] + let starter = if writerSectionDivs opts + then "\\start" + else "\\" + return $ starter <> levelText <> options <> blankline + +-- | Craft the section footer +sectionFooter :: PandocMonad m => Attr -> Int -> WM m Doc +sectionFooter attr hdrLevel = do + opts <- gets stOptions + levelText <- sectionLevelToText opts attr hdrLevel + return $ if writerSectionDivs opts + then "\\stop" <> levelText <> blankline + else empty + +-- | Generate a textual representation of the section level +sectionLevelToText :: PandocMonad m => WriterOptions -> Attr -> Int -> WM m Doc +sectionLevelToText opts (_,classes,_) hdrLevel = do let level' = case writerTopLevelDivision opts of TopLevelPart -> hdrLevel - 2 TopLevelChapter -> hdrLevel - 1 TopLevelSection -> hdrLevel TopLevelDefault -> hdrLevel - let ident' = if null ident - then empty - else brackets (text (toLabel ident)) let (section, chapter) = if "unnumbered" `elem` classes then (text "subject", text "title") else (text "section", text "chapter") return $ case level' of - -1 -> text "\\part" <> ident' <> braces contents - 0 -> char '\\' <> chapter <> ident' <> - braces contents - n | n >= 1 && n <= 5 -> char '\\' - <> text (concat (replicate (n - 1) "sub")) - <> section - <> ident' - <> braces contents - <> blankline - _ -> contents <> blankline + -1 -> text "part" + 0 -> chapter + n | n >= 1 -> text (concat (replicate (n - 1) "sub")) + <> section + _ -> empty -- cannot happen fromBCP47 :: PandocMonad m => Maybe String -> WM m (Maybe String) fromBCP47 mbs = fromBCP47' <$> toLang mbs -- cgit v1.2.3 From ae2157fe8bca342ba23881e0f5dfba8d9fb07d84 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sat, 27 Jan 2018 07:21:25 -0500 Subject: Docx writer: Fix ids in comment writing Comments from `--track-changes=all` were producing corrupt docx, because the writer was trying to get id from the `(ID,_,_)` field of the attributes, and ignoring the "id" entry in the key-value pairs. We now check both. There is a larger conversation to be had about the right way to treat "id" and "class" entries in kvs, but this fix will correctly interpret the output of the docx reader work. --- src/Text/Pandoc/Writers/Docx.hs | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 7cbc946cc..ffecb7c7f 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -1104,15 +1104,23 @@ inlineToOpenXML' _ (Str str) = inlineToOpenXML' opts Space = inlineToOpenXML opts (Str " ") inlineToOpenXML' opts SoftBreak = inlineToOpenXML opts (Str " ") inlineToOpenXML' _ (Span (ident,["comment-start"],kvs) ils) = do - modify $ \st -> st{ stComments = (("id",ident):kvs, ils) : stComments st } - return [ mknode "w:commentRangeStart" [("w:id", ident)] () ] -inlineToOpenXML' _ (Span (ident,["comment-end"],_) _) = - return [ mknode "w:commentRangeEnd" [("w:id", ident)] () - , mknode "w:r" [] - [ mknode "w:rPr" [] - [ mknode "w:rStyle" [("w:val", "CommentReference")] () ] - , mknode "w:commentReference" [("w:id", ident)] () ] - ] + -- prefer the "id" in kvs, since that is the one produced by the docx + -- reader. + let ident' = fromMaybe ident (lookup "id" kvs) + kvs' = filter (("id" /=) . fst) kvs + modify $ \st -> st{ stComments = (("id",ident'):kvs', ils) : stComments st } + return [ mknode "w:commentRangeStart" [("w:id", ident')] () ] +inlineToOpenXML' _ (Span (ident,["comment-end"],kvs) _) = + -- prefer the "id" in kvs, since that is the one produced by the docx + -- reader. + let ident' = fromMaybe ident (lookup "id" kvs) + in + return [ mknode "w:commentRangeEnd" [("w:id", ident')] () + , mknode "w:r" [] + [ mknode "w:rPr" [] + [ mknode "w:rStyle" [("w:val", "CommentReference")] () ] + , mknode "w:commentReference" [("w:id", ident')] () ] + ] inlineToOpenXML' opts (Span (ident,classes,kvs) ils) = do stylemod <- case lookup dynamicStyleKey kvs of Just sty -> do -- cgit v1.2.3 From 9cf9f1f89d31e8a4a65cbdd419a50b6e4e62c9ab Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Thu, 25 Jan 2018 16:59:04 -0500 Subject: Docx writer: make more deterministic to facilitate testing This will allow us to compare files directly in a golden test. Times are still based on IO, but we will be able to safely skip those. Changes: - `getUniqueId` now calls to the state to get an incremented digit, instead of calling to P.uniqueHash. - we always start the PRNG in mkNumbering/mkAbstractNum with the same seed (1848), so our randoms should be the same each time. --- src/Text/Pandoc/Writers/Docx.hs | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index ffecb7c7f..55588ba22 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -47,7 +47,7 @@ import qualified Data.Set as Set import qualified Data.Text as T import Data.Time.Clock.POSIX import Skylighting -import System.Random (randomR) +import System.Random (randomR, StdGen, mkStdGen) import Text.Pandoc.BCP47 (getLang, renderLang) import Text.Pandoc.Class (PandocMonad, report, toLang) import qualified Text.Pandoc.Class as P @@ -132,6 +132,7 @@ data WriterState = WriterState{ , stTocTitle :: [Inline] , stDynamicParaProps :: Set.Set String , stDynamicTextProps :: Set.Set String + , stCurId :: Int } defaultWriterState :: WriterState @@ -149,6 +150,7 @@ defaultWriterState = WriterState{ , stTocTitle = [Str "Table of Contents"] , stDynamicParaProps = Set.empty , stDynamicTextProps = Set.empty + , stCurId = 20 } type WS m = ReaderT WriterEnv (StateT WriterState m) @@ -642,7 +644,7 @@ baseListId = 1000 mkNumbering :: (PandocMonad m) => [ListMarker] -> m [Element] mkNumbering lists = do - elts <- mapM mkAbstractNum (ordNub lists) + elts <- evalStateT (mapM mkAbstractNum (ordNub lists)) (mkStdGen 1848) return $ elts ++ zipWith mkNum lists [baseListId..(baseListId + length lists - 1)] maxListLevel :: Int @@ -660,10 +662,11 @@ mkNum marker numid = $ mknode "w:startOverride" [("w:val",show start)] ()) [0..maxListLevel] -mkAbstractNum :: (PandocMonad m) => ListMarker -> m Element +mkAbstractNum :: (PandocMonad m) => ListMarker -> StateT StdGen m Element mkAbstractNum marker = do - gen <- P.newStdGen - let (nsid, _) = randomR (0x10000000 :: Integer, 0xFFFFFFFF :: Integer) gen + gen <- get + let (nsid, gen') = randomR (0x10000000 :: Integer, 0xFFFFFFFF :: Integer) gen + put gen' return $ mknode "w:abstractNum" [("w:abstractNumId",listMarkerToId marker)] $ mknode "w:nsid" [("w:val", printf "%8x" nsid)] () : mknode "w:multiLevelType" [("w:val","multilevel")] () @@ -822,10 +825,13 @@ rStyleM styleName = do let sty' = getStyleId styleName $ sCharStyleMap styleMaps return $ mknode "w:rStyle" [("w:val",sty')] () -getUniqueId :: (PandocMonad m) => m String +getUniqueId :: (PandocMonad m) => WS 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 +getUniqueId = do + n <- gets stCurId + modify $ \st -> st{stCurId = n + 1} + return $ show n -- | Key for specifying user-defined docx styles. dynamicStyleKey :: String @@ -1232,7 +1238,7 @@ inlineToOpenXML' opts (Code attrs str) = do unhighlighted inlineToOpenXML' opts (Note bs) = do notes <- gets stFootnotes - notenum <- (lift . lift) getUniqueId + notenum <- getUniqueId footnoteStyle <- rStyleM "Footnote Reference" let notemarker = mknode "w:r" [] [ mknode "w:rPr" [] footnoteStyle @@ -1263,7 +1269,7 @@ inlineToOpenXML' opts (Link _ txt (src,_)) = do id' <- case M.lookup src extlinks of Just i -> return i Nothing -> do - i <- ("rId"++) `fmap` (lift . lift) getUniqueId + i <- ("rId"++) `fmap` getUniqueId modify $ \st -> st{ stExternalLinks = M.insert src i extlinks } return i @@ -1277,7 +1283,7 @@ inlineToOpenXML' opts (Image attr alt (src, title)) = do Nothing -> catchError (do (img, mt) <- P.fetchItem src - ident <- ("rId"++) `fmap` (lift . lift) getUniqueId + ident <- ("rId"++) `fmap` getUniqueId let (xpt,ypt) = desiredSizeInPoints opts attr (either (const def) id (imageSize opts img)) -- 12700 emu = 1 pt -- cgit v1.2.3 From 75762ee0dc30e10befd1072a7ad182d92e4f5680 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sun, 28 Jan 2018 01:04:32 +0300 Subject: Muse reader: simplify paragraph parsing Blank lines are already handled by blockElements. --- src/Text/Pandoc/Readers/Muse.hs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index b64d877b3..7b6da1174 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -332,9 +332,8 @@ para = do let f = if st /= ListItemState && indent >= 2 && indent < 6 then B.blockQuote else id fmap (f . B.para) . trimInlinesF . mconcat <$> many1Till inline endOfParaElement where - endOfParaElement = lookAhead $ endOfInput <|> newBlockElement - endOfInput = try $ skipMany blankline >> skipSpaces >> eof - newBlockElement = try $ blankline >> void blockElements + endOfParaElement = lookAhead $ try (eof <|> newBlockElement) + newBlockElement = blankline >> void blockElements noteMarker :: PandocMonad m => MuseParser m String noteMarker = try $ do -- cgit v1.2.3 From 248f6076bc1df7f3dd55d7402707233ae08d3cdb Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sun, 28 Jan 2018 02:59:06 +0300 Subject: Muse reader: fix parsing of trailing whitespace Newline after whitespace now results in softbreak instead of space. --- src/Text/Pandoc/Readers/Muse.hs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 7b6da1174..f4aca92d0 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -592,7 +592,7 @@ inlineList = [ whitespace ] inline :: PandocMonad m => MuseParser m (F Inlines) -inline = choice [endline, linebreak] <|> choice inlineList <?> "inline" +inline = endline <|> choice inlineList <?> "inline" endline :: PandocMonad m => MuseParser m (F Inlines) endline = try $ do @@ -626,13 +626,6 @@ footnote = try $ do let contents' = runF contents st { stateNotes' = M.empty } return $ B.note contents' -linebreak :: PandocMonad m => MuseParser m (F Inlines) -linebreak = try $ do - skipMany spaceChar - newline - notFollowedBy newline - return $ return B.space - whitespace :: PandocMonad m => MuseParser m (F Inlines) whitespace = try $ do skipMany1 spaceChar -- cgit v1.2.3 From 4bfab8f04c105f111d8d4e1c3ed7f7b5c75dbd19 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sun, 28 Jan 2018 06:31:33 +0300 Subject: Muse reader: parse directives without parseFromString --- src/Text/Pandoc/Readers/Muse.hs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index f4aca92d0..995a3ae9e 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -157,16 +157,14 @@ parseEmacsDirective = do parseAmuseDirective :: PandocMonad m => MuseParser m (String, F Inlines) parseAmuseDirective = do key <- parseDirectiveKey - space - spaces - first <- manyTill anyChar eol - rest <- manyTill anyLine endOfDirective + many1 spaceChar + value <- trimInlinesF . mconcat <$> many1Till inline endOfDirective many blankline - value <- parseFromString (trimInlinesF . mconcat <$> many inline) $ unlines (first : rest) return (key, value) where - endOfDirective = lookAhead $ endOfInput <|> try (void blankline) <|> try (void parseDirectiveKey) - endOfInput = try $ skipMany blankline >> skipSpaces >> eof + endOfDirective = lookAhead $ try (eof <|> + void (newline >> blankline) <|> + void (newline >> parseDirectiveKey)) directive :: PandocMonad m => MuseParser m () directive = do -- cgit v1.2.3 From 05275be2cb576561b36a111d04fb42a197ddbab6 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sun, 28 Jan 2018 08:35:00 +0300 Subject: Muse reader: make verseLine return Inlines, not String --- src/Text/Pandoc/Readers/Muse.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 995a3ae9e..27d5922a0 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -302,17 +302,17 @@ divTag = do (attrs, content) <- parseHtmlContentWithAttrs "div" parseBlock return $ B.divWith attrs <$> mconcat content -verseLine :: PandocMonad m => MuseParser m String +verseLine :: PandocMonad m => MuseParser m (F Inlines) verseLine = do line <- anyLine <|> many1Till anyChar eof let (white, rest) = span (== ' ') line - return $ replicate (length white) '\160' ++ rest + let s = replicate (length white) '\160' ++ rest + parseFromString' (trimInlinesF . mconcat <$> many inline) s verseLines :: PandocMonad m => MuseParser m (F Blocks) verseLines = do lns <- many verseLine - lns' <- mapM (parseFromString' (trimInlinesF . mconcat <$> many inline)) lns - return $ B.lineBlock <$> sequence lns' + return $ B.lineBlock <$> sequence lns verseTag :: PandocMonad m => MuseParser m (F Blocks) verseTag = do -- cgit v1.2.3 From 264a25e5e98c252dba87b63dceda6539e93461aa Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sun, 28 Jan 2018 22:09:47 +0300 Subject: Muse reader: parse verse tag contents without parseFromString --- src/Text/Pandoc/Readers/Muse.hs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 27d5922a0..97f8a963f 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -304,10 +304,9 @@ divTag = do verseLine :: PandocMonad m => MuseParser m (F Inlines) verseLine = do - line <- anyLine <|> many1Till anyChar eof - let (white, rest) = span (== ' ') line - let s = replicate (length white) '\160' ++ rest - parseFromString' (trimInlinesF . mconcat <$> many inline) s + indent <- (B.str <$> many1 (char ' ' >> pure '\160')) <|> (pure mempty) + rest <- manyTill (choice inlineList) newline + return $ trimInlinesF $ mconcat (pure indent : rest) verseLines :: PandocMonad m => MuseParser m (F Blocks) verseLines = do -- cgit v1.2.3 From 60b2863e90eec6a1f8dd053b363eb93cc068a79b Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sun, 28 Jan 2018 23:22:11 +0300 Subject: Muse reader: parse <comment> and <verbatim> without parseFromString --- src/Text/Pandoc/Readers/Muse.hs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 97f8a963f..f15ea085f 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -319,7 +319,7 @@ verseTag = do parseFromString verseLines (intercalate "\n" $ dropSpacePrefix $ splitOn "\n" $ lchop content) commentTag :: PandocMonad m => MuseParser m (F Blocks) -commentTag = parseHtmlContent "comment" anyChar >> return mempty +commentTag = htmlElement "comment" >> return mempty -- Indented paragraph is either center, right or quote para :: PandocMonad m => MuseParser m (F Blocks) @@ -679,9 +679,7 @@ strikeoutTag :: PandocMonad m => MuseParser m (F Inlines) strikeoutTag = inlineTag B.strikeout "del" verbatimTag :: PandocMonad m => MuseParser m (F Inlines) -verbatimTag = do - content <- parseHtmlContent "verbatim" anyChar - return $ return $ B.text content +verbatimTag = return . B.text . snd <$> htmlElement "verbatim" nbsp :: PandocMonad m => MuseParser m (F Inlines) nbsp = try $ do -- cgit v1.2.3 From ff31602267d2d8aebda319cd8f55ba5e1399bb1f Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sun, 28 Jan 2018 23:39:31 +0300 Subject: Muse reader: parse inline <literal> without parseFromString --- src/Text/Pandoc/Readers/Muse.hs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index f15ea085f..6d9794f9e 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -704,8 +704,7 @@ codeTag = do inlineLiteralTag :: PandocMonad m => MuseParser m (F Inlines) inlineLiteralTag = do guardDisabled Ext_amuse -- Text::Amuse does not support <literal> - (attrs, content) <- parseHtmlContentWithAttrs "literal" anyChar - return $ return $ rawInline (attrs, content) + (return . rawInline) <$> htmlElement "literal" where -- FIXME: Emacs Muse inserts <literal> without style into all output formats, but we assume HTML format (_, _, kvs) = fromMaybe "html" $ lookup "style" kvs -- cgit v1.2.3 From 37271fabeee917c085c9ea7f04b8c847b524db4a Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 29 Jan 2018 12:05:00 +0300 Subject: Muse reader: simplify block tag parsing code --- src/Text/Pandoc/Readers/Muse.hs | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 6d9794f9e..c4175c4b2 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -110,19 +110,16 @@ htmlAttrToPandoc attrs = (ident, classes, keyvals) classes = maybe [] words $ lookup "class" attrs keyvals = [(k,v) | (k,v) <- attrs, k /= "id" && k /= "class"] -parseHtmlContentWithAttrs :: PandocMonad m - => String -> MuseParser m a -> MuseParser m (Attr, [a]) -parseHtmlContentWithAttrs tag parser = do +parseHtmlContent :: PandocMonad m + => String -> MuseParser m (Attr, F Blocks) +parseHtmlContent tag = do (attr, content) <- htmlElement tag parsedContent <- parseContent (content ++ "\n") - return (attr, parsedContent) + return (attr, mconcat parsedContent) where - parseContent = parseFromString $ manyTill parser endOfContent + parseContent = parseFromString $ manyTill parseBlock endOfContent endOfContent = try $ skipMany blankline >> skipSpaces >> eof -parseHtmlContent :: PandocMonad m => String -> MuseParser m a -> MuseParser m [a] -parseHtmlContent tag p = fmap snd (parseHtmlContentWithAttrs tag p) - commonPrefix :: String -> String -> String commonPrefix _ [] = [] commonPrefix [] _ = [] @@ -277,30 +274,24 @@ literal = do format (_, _, kvs) = fromMaybe "html" $ lookup "style" kvs rawBlock (attrs, content) = B.rawBlock (format attrs) $ chop content -blockTag :: PandocMonad m - => (Blocks -> Blocks) - -> String - -> MuseParser m (F Blocks) -blockTag f s = do - res <- parseHtmlContent s parseBlock - return $ f <$> mconcat res - -- <center> tag is ignored centerTag :: PandocMonad m => MuseParser m (F Blocks) -centerTag = blockTag id "center" +centerTag = snd <$> parseHtmlContent "center" -- <right> tag is ignored rightTag :: PandocMonad m => MuseParser m (F Blocks) -rightTag = blockTag id "right" +rightTag = snd <$> parseHtmlContent "right" quoteTag :: PandocMonad m => MuseParser m (F Blocks) -quoteTag = withQuoteContext InDoubleQuote $ blockTag B.blockQuote "quote" +quoteTag = do + res <- snd <$> withQuoteContext InDoubleQuote (parseHtmlContent "quote") + return $ B.blockQuote <$> res -- <div> tag is supported by Emacs Muse, but not Amusewiki 2.025 divTag :: PandocMonad m => MuseParser m (F Blocks) divTag = do - (attrs, content) <- parseHtmlContentWithAttrs "div" parseBlock - return $ B.divWith attrs <$> mconcat content + (attrs, content) <- parseHtmlContent "div" + return $ B.divWith attrs <$> content verseLine :: PandocMonad m => MuseParser m (F Inlines) verseLine = do -- cgit v1.2.3 From b7d8930dc097defcae754120a4fa0d6727c9b265 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 29 Jan 2018 12:54:43 +0300 Subject: Muse writer: escape nonbreaking space ("~~") --- src/Text/Pandoc/Writers/Muse.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index 164a46411..73d79a9a2 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -290,7 +290,8 @@ conditionalEscapeString :: String -> String conditionalEscapeString s = if any (`elem` ("#*<=>[]|" :: String)) s || "::" `isInfixOf` s || - "----" `isInfixOf` s + "----" `isInfixOf` s || + "~~" `isInfixOf` s then escapeString s else s -- cgit v1.2.3 From 9ff8bc64f9c873e2bcae6a1a46f71af9287d6753 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 29 Jan 2018 23:27:40 +0300 Subject: Muse writer: don't wrap displayMath into <verse> <verse> is a block tag and displayMath is an inline element. Writing <verse> around displayMath could result in nested <verse> tags. --- src/Text/Pandoc/Writers/Muse.hs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index 73d79a9a2..c3c1c3120 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -363,11 +363,8 @@ inlineToMuse (Quoted DoubleQuote lst) = do inlineToMuse (Cite _ lst) = inlineListToMuse lst inlineToMuse (Code _ str) = return $ "<code>" <> text (substitute "</code>" "<</code><code>/code>" str) <> "</code>" -inlineToMuse (Math InlineMath str) = - lift (texMathToInlines InlineMath str) >>= inlineListToMuse -inlineToMuse (Math DisplayMath str) = do - contents <- lift (texMathToInlines DisplayMath str) >>= inlineListToMuse - return $ "<verse>" <> contents <> "</verse>" <> blankline +inlineToMuse (Math t str) = + lift (texMathToInlines t str) >>= inlineListToMuse inlineToMuse (RawInline (Format f) str) = return $ "<literal style=\"" <> text f <> "\">" <> text str <> "</literal>" inlineToMuse LineBreak = return $ "<br>" <> cr -- cgit v1.2.3 From 309595aff33994d8325af518424eb6831d779de8 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Wed, 31 Jan 2018 01:32:44 +0300 Subject: Export list marker parsers from Text.Pandoc.Parsing --- src/Text/Pandoc/Parsing.hs | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index e87ea71da..562e3d577 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -65,6 +65,11 @@ module Text.Pandoc.Parsing ( takeWhileP, withRaw, escaped, characterReference, + upperRoman, + lowerRoman, + decimal, + lowerAlpha, + upperAlpha, anyOrderedListMarker, orderedListMarker, charRef, -- cgit v1.2.3 From 00b7ab8d0064c71bf33ce558ffa1e4c07e00f9b6 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Tue, 30 Jan 2018 19:09:07 +0300 Subject: Muse reader: replace ParserState with MuseState --- src/Text/Pandoc/Readers/Muse.hs | 133 +++++++++++++++++++++++++++++++--------- 1 file changed, 104 insertions(+), 29 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index c4175c4b2..4e1bb95ec 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -42,9 +42,11 @@ module Text.Pandoc.Readers.Muse (readMuse) where import Control.Monad import Control.Monad.Except (throwError) import Data.Char (isLetter) +import Data.Default import Data.List (stripPrefix, intercalate) import Data.List.Split (splitOn) import qualified Data.Map as M +import qualified Data.Set as Set import Data.Maybe (fromMaybe) import Data.Text (Text, unpack) import System.FilePath (takeExtension) @@ -55,7 +57,7 @@ import Text.Pandoc.Class (PandocMonad (..)) import Text.Pandoc.Definition import Text.Pandoc.Logging import Text.Pandoc.Options -import Text.Pandoc.Parsing +import Text.Pandoc.Parsing hiding (F) import Text.Pandoc.Readers.HTML (htmlTag) import Text.Pandoc.Shared (crFilter, underlineSpan) @@ -65,12 +67,61 @@ readMuse :: PandocMonad m -> Text -> m Pandoc readMuse opts s = do - res <- readWithM parseMuse def{ stateOptions = opts } (unpack (crFilter s)) + res <- readWithM parseMuse def{ museOptions = opts } (unpack (crFilter s)) case res of Left e -> throwError e Right d -> return d -type MuseParser = ParserT String ParserState +type F = Future MuseState + +data MuseState = MuseState { museMeta :: F Meta -- ^ Document metadata + , museOptions :: ReaderOptions + , museHeaders :: M.Map Inlines String -- ^ List of headers and ids (used for implicit ref links) + , museIdentifierList :: Set.Set String + , museLastStrPos :: Maybe SourcePos -- ^ Position after last str parsed + , museLogMessages :: [LogMessage] + , museNotes :: M.Map String (SourcePos, F Blocks) + , museInQuote :: Bool + , museInList :: Bool + , museInLink :: Bool + } + +instance Default MuseState where + def = defaultMuseState + +defaultMuseState :: MuseState +defaultMuseState = MuseState { museMeta = return nullMeta + , museOptions = def + , museHeaders = M.empty + , museIdentifierList = Set.empty + , museLastStrPos = Nothing + , museLogMessages = [] + , museNotes = M.empty + , museInQuote = False + , museInList = False + , museInLink = False + } + +type MuseParser = ParserT String MuseState + +instance HasReaderOptions MuseState where + extractReaderOptions = museOptions + +instance HasHeaderMap MuseState where + extractHeaderMap = museHeaders + updateHeaderMap f st = st{ museHeaders = f $ museHeaders st } + +instance HasIdentifierList MuseState where + extractIdentifierList = museIdentifierList + updateIdentifierList f st = st{ museIdentifierList = f $ museIdentifierList st } + +instance HasLastStrPosition MuseState where + setLastStrPos pos st = st{ museLastStrPos = Just pos } + getLastStrPos st = museLastStrPos st + +instance HasLogMessages MuseState where + addLogMessage m s = s{ museLogMessages = m : museLogMessages s } + getLogMessages = reverse . museLogMessages -- -- main parser @@ -83,7 +134,7 @@ parseMuse = do eof st <- getState let doc = runF (do Pandoc _ bs <- B.doc <$> blocks - meta <- stateMeta' st + meta <- museMeta st return $ Pandoc meta bs) st reportLogMessages return doc @@ -131,7 +182,7 @@ atStart :: PandocMonad m => MuseParser m a -> MuseParser m a atStart p = do pos <- getPosition st <- getState - guard $ stateLastStrPos st /= Just pos + guard $ museLastStrPos st /= Just pos p -- @@ -167,7 +218,7 @@ directive :: PandocMonad m => MuseParser m () directive = do ext <- getOption readerExtensions (key, value) <- if extensionEnabled Ext_amuse ext then parseAmuseDirective else parseEmacsDirective - updateState $ \st -> st { stateMeta' = B.setMeta (translateKey key) <$> value <*> stateMeta' st } + updateState $ \st -> st { museMeta = B.setMeta (translateKey key) <$> value <*> museMeta st } where translateKey "cover" = "cover-image" translateKey x = x @@ -179,7 +230,7 @@ parseBlock :: PandocMonad m => MuseParser m (F Blocks) parseBlock = do res <- blockElements <|> para optionMaybe blankline - trace (take 60 $ show $ B.toList $ runF res defaultParserState) + trace (take 60 $ show $ B.toList $ runF res def) return res blockElements :: PandocMonad m => MuseParser m (F Blocks) @@ -222,15 +273,15 @@ separator = try $ do header :: PandocMonad m => MuseParser m (F Blocks) header = try $ do - st <- stateParserContext <$> getState - q <- stateQuoteContext <$> getState - getPosition >>= \pos -> guard (st == NullState && q == NoQuote && sourceColumn pos == 1) + st <- museInList <$> getState + q <- museInQuote <$> getState + getPosition >>= \pos -> guard (not st && not q && sourceColumn pos == 1) level <- fmap length $ many1 $ char '*' guard $ level <= 5 spaceChar content <- trimInlinesF . mconcat <$> manyTill inline eol anchorId <- option "" parseAnchor - attr <- registerHeader (anchorId, [], []) (runF content defaultParserState) + attr <- registerHeader (anchorId, [], []) (runF content def) return $ B.headerWith attr level <$> content example :: PandocMonad m => MuseParser m (F Blocks) @@ -284,7 +335,11 @@ rightTag = snd <$> parseHtmlContent "right" quoteTag :: PandocMonad m => MuseParser m (F Blocks) quoteTag = do - res <- snd <$> withQuoteContext InDoubleQuote (parseHtmlContent "quote") + st <- getState + let oldInQuote = museInQuote st + setState $ st{ museInQuote = True } + res <- snd <$> (parseHtmlContent "quote") + setState $ st{ museInQuote = oldInQuote } return $ B.blockQuote <$> res -- <div> tag is supported by Emacs Muse, but not Amusewiki 2.025 @@ -316,8 +371,8 @@ commentTag = htmlElement "comment" >> return mempty para :: PandocMonad m => MuseParser m (F Blocks) para = do indent <- length <$> many spaceChar - st <- stateParserContext <$> getState - let f = if st /= ListItemState && indent >= 2 && indent < 6 then B.blockQuote else id + st <- museInList <$> getState + let f = if not st && indent >= 2 && indent < 6 then B.blockQuote else id fmap (f . B.para) . trimInlinesF . mconcat <$> many1Till inline endOfParaElement where endOfParaElement = lookAhead $ try (eof <|> newBlockElement) @@ -338,11 +393,11 @@ amuseNoteBlock = try $ do pos <- getPosition ref <- noteMarker <* spaceChar content <- listItemContents - oldnotes <- stateNotes' <$> getState + oldnotes <- museNotes <$> getState case M.lookup ref oldnotes of Just _ -> logMessage $ DuplicateNoteReference ref pos Nothing -> return () - updateState $ \s -> s{ stateNotes' = M.insert ref (pos, content) oldnotes } + updateState $ \s -> s{ museNotes = M.insert ref (pos, content) oldnotes } return mempty -- Emacs version of note @@ -353,11 +408,11 @@ emacsNoteBlock = try $ do pos <- getPosition ref <- noteMarker <* skipSpaces content <- mconcat <$> blocksTillNote - oldnotes <- stateNotes' <$> getState + oldnotes <- museNotes <$> getState case M.lookup ref oldnotes of Just _ -> logMessage $ DuplicateNoteReference ref pos Nothing -> return () - updateState $ \s -> s{ stateNotes' = M.insert ref (pos, content) oldnotes } + updateState $ \s -> s{ museNotes = M.insert ref (pos, content) oldnotes } return mempty where blocksTillNote = @@ -392,10 +447,10 @@ lineBlock = try $ do withListContext :: PandocMonad m => MuseParser m a -> MuseParser m a withListContext p = do state <- getState - let oldContext = stateParserContext state - setState $ state { stateParserContext = ListItemState } + let oldInList = museInList state + setState $ state { museInList = True } parsed <- p - updateState (\st -> st {stateParserContext = oldContext}) + updateState (\st -> st { museInList = oldInList }) return parsed listItemContents' :: PandocMonad m => Int -> MuseParser m (F Blocks) @@ -430,18 +485,38 @@ bulletList = try $ do rest <- many $ listItem (col - 1) (char '-') return $ B.bulletList <$> sequence (first : rest) +-- | Parses an ordered list marker and returns list attributes. +anyMuseOrderedListMarker :: PandocMonad m => MuseParser m ListAttributes +anyMuseOrderedListMarker = do + (style, start) <- decimal <|> lowerAlpha <|> lowerRoman <|> upperAlpha <|> upperRoman + char '.' + return (start, style, Period) + +museOrderedListMarker :: PandocMonad m + => ListNumberStyle + -> MuseParser m Int +museOrderedListMarker style = do + (_, start) <- case style of + Decimal -> decimal + UpperRoman -> upperRoman + LowerRoman -> lowerRoman + UpperAlpha -> upperAlpha + LowerAlpha -> lowerAlpha + _ -> fail "Unhandled case" + char '.' + return start + orderedList :: PandocMonad m => MuseParser m (F Blocks) orderedList = try $ do many spaceChar pos <- getPosition let col = sourceColumn pos guard $ col /= 1 - p@(_, style, delim) <- anyOrderedListMarker + p@(_, style, _) <- anyMuseOrderedListMarker guard $ style `elem` [Decimal, LowerAlpha, UpperAlpha, LowerRoman, UpperRoman] - guard $ delim == Period void spaceChar <|> lookAhead eol first <- listItemContents - rest <- many $ listItem (col - 1) (orderedListMarker style delim) + rest <- many $ listItem (col - 1) (museOrderedListMarker style) return $ B.orderedListWith p <$> sequence (first : rest) definitionListItem :: PandocMonad m => Int -> MuseParser m (F (Inlines, [Blocks])) @@ -606,12 +681,12 @@ footnote :: PandocMonad m => MuseParser m (F Inlines) footnote = try $ do ref <- noteMarker return $ do - notes <- asksF stateNotes' + notes <- asksF museNotes case M.lookup ref notes of Nothing -> return $ B.str $ "[" ++ ref ++ "]" Just (_pos, contents) -> do st <- askF - let contents' = runF contents st { stateNotes' = M.empty } + let contents' = runF contents st { museNotes = M.empty } return $ B.note contents' whitespace :: PandocMonad m => MuseParser m (F Inlines) @@ -713,10 +788,10 @@ symbol = return . B.str <$> count 1 nonspaceChar link :: PandocMonad m => MuseParser m (F Inlines) link = try $ do st <- getState - guard $ stateAllowLinks st - setState $ st{ stateAllowLinks = False } + guard $ not $ museInLink st + setState $ st{ museInLink = True } (url, title, content) <- linkText - setState $ st{ stateAllowLinks = True } + setState $ st{ museInLink = False } return $ case stripPrefix "URL:" url of Nothing -> if isImageUrl url then B.image url title <$> fromMaybe (return mempty) content -- cgit v1.2.3 From 8bdbdc24deba5c32950d781b533d132100a77b26 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Thu, 1 Feb 2018 11:38:33 -0800 Subject: TEI writer: Use height instead of depth for images. Closes #4331. --- src/Text/Pandoc/Writers/TEI.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/TEI.hs b/src/Text/Pandoc/Writers/TEI.hs index 907e2af24..be1c594aa 100644 --- a/src/Text/Pandoc/Writers/TEI.hs +++ b/src/Text/Pandoc/Writers/TEI.hs @@ -145,7 +145,7 @@ imageToTEI :: PandocMonad m => WriterOptions -> Attr -> String -> m Doc imageToTEI _ attr src = return $ selfClosingTag "graphic" $ ("url", src) : idAndRole attr ++ dims where - dims = go Width "width" ++ go Height "depth" + dims = go Width "width" ++ go Height "height" go dir dstr = case dimension dir attr of Just a -> [(dstr, show a)] Nothing -> [] -- cgit v1.2.3 From eeafb3fa773e992174dd460d093653cd77255ce5 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 2 Feb 2018 10:00:14 -0800 Subject: Determine image size for PDFs. Closes #4322. --- src/Text/Pandoc/ImageSize.hs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/ImageSize.hs b/src/Text/Pandoc/ImageSize.hs index 65559e1ce..0a811d545 100644 --- a/src/Text/Pandoc/ImageSize.hs +++ b/src/Text/Pandoc/ImageSize.hs @@ -138,7 +138,7 @@ imageSize opts img = Just Jpeg -> jpegSize img Just Svg -> mbToEither "could not determine SVG size" $ svgSize opts img Just Eps -> mbToEither "could not determine EPS size" $ epsSize img - Just Pdf -> Left "could not determine PDF size" -- TODO + Just Pdf -> mbToEither "Could not determine PDF size" $ pdfSize img Nothing -> Left "could not determine image type" where mbToEither msg Nothing = Left msg mbToEither _ (Just x) = Right x @@ -277,6 +277,27 @@ epsSize img = do , dpiY = 72 } _ -> mzero +pdfSize :: ByteString -> Maybe ImageSize +pdfSize img = + case dropWhile (\l -> not (l == "stream" || + "/MediaBox" `B.isPrefixOf` l)) (B.lines img) of + (x:_) + | "/MediaBox" `B.isPrefixOf` x + -> case B.words $ B.filter (\c -> c /= '[' && c /= ']') + $ B.drop 10 x of + [x1, y1, x2, y2] -> do + x1' <- safeRead $ B.unpack x1 + x2' <- safeRead $ B.unpack x2 + y1' <- safeRead $ B.unpack y1 + y2' <- safeRead $ B.unpack y2 + return ImageSize{ + pxX = x2' - x1' + , pxY = y2' - y1' + , dpiX = 72 + , dpiY = 72 } + _ -> mzero + _ -> mzero + pngSize :: ByteString -> Maybe ImageSize pngSize img = do let (h, rest) = B.splitAt 8 img -- cgit v1.2.3 From d777fe8bbecc3320cf95754dbe7f9eee21876770 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sat, 3 Feb 2018 18:36:38 +0300 Subject: Muse writer: write image width specified in percent in Text::Amuse mode --- src/Text/Pandoc/Writers/Muse.hs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index c3c1c3120..a1414abc5 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -48,6 +48,7 @@ import Data.List (intersperse, transpose, isInfixOf) import System.FilePath (takeExtension) import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Definition +import Text.Pandoc.ImageSize import Text.Pandoc.Options import Text.Pandoc.Pretty import Text.Pandoc.Shared @@ -384,14 +385,18 @@ inlineToMuse (Link _ txt (src, _)) = isImageUrl = (`elem` imageExtensions) . takeExtension inlineToMuse (Image attr alt (source,'f':'i':'g':':':title)) = inlineToMuse (Image attr alt (source,title)) -inlineToMuse (Image _ inlines (source, title)) = do +inlineToMuse (Image attr inlines (source, title)) = do + opts <- gets stOptions alt <- inlineListToMuse inlines let title' = if null title then if null inlines then "" else "[" <> alt <> "]" else "[" <> text title <> "]" - return $ "[[" <> text source <> "]" <> title' <> "]" + let width = case dimension Width attr of + Just (Percent x) | isEnabled Ext_amuse opts -> " " ++ show (round x :: Integer) + _ -> "" + return $ "[[" <> text (source ++ width) <> "]" <> title' <> "]" inlineToMuse (Note contents) = do -- add to notes in state notes <- gets stNotes -- cgit v1.2.3 From 5439e29dd9d897c0c65bc7f9f126038ca36475cb Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 3 Feb 2018 11:22:25 -0800 Subject: C -> c. --- src/Text/Pandoc/ImageSize.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/ImageSize.hs b/src/Text/Pandoc/ImageSize.hs index 0a811d545..f21284dac 100644 --- a/src/Text/Pandoc/ImageSize.hs +++ b/src/Text/Pandoc/ImageSize.hs @@ -138,7 +138,7 @@ imageSize opts img = Just Jpeg -> jpegSize img Just Svg -> mbToEither "could not determine SVG size" $ svgSize opts img Just Eps -> mbToEither "could not determine EPS size" $ epsSize img - Just Pdf -> mbToEither "Could not determine PDF size" $ pdfSize img + Just Pdf -> mbToEither "could not determine PDF size" $ pdfSize img Nothing -> Left "could not determine image type" where mbToEither msg Nothing = Left msg mbToEither _ (Just x) = Right x -- cgit v1.2.3 From ca4a61a348efcdcb3418935f45f152c291ec75a0 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sun, 4 Feb 2018 19:19:55 +0300 Subject: Muse reader: simplify listItemContents' with sepBy1 --- src/Text/Pandoc/Readers/Muse.hs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 4e1bb95ec..1d4b9cc89 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -454,10 +454,8 @@ withListContext p = do return parsed listItemContents' :: PandocMonad m => Int -> MuseParser m (F Blocks) -listItemContents' col = do - first <- try $ withListContext parseBlock - rest <- many $ try (skipMany blankline >> indentWith col >> withListContext parseBlock) - return $ mconcat (first : rest) +listItemContents' col = + mconcat <$> withListContext (parseBlock `sepBy1` try (skipMany blankline >> indentWith col)) listItemContents :: PandocMonad m => MuseParser m (F Blocks) listItemContents = do -- cgit v1.2.3 From 1a06f0ecfb5a4898daf2769f4c072fa12bae6d39 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 5 Feb 2018 01:44:31 +0300 Subject: Muse reader: make block parsers responsible for parsing newline Block parsers must always stop after newline or at the end of file. --- src/Text/Pandoc/Readers/Muse.hs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 1d4b9cc89..63dcac122 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -166,6 +166,7 @@ parseHtmlContent :: PandocMonad m parseHtmlContent tag = do (attr, content) <- htmlElement tag parsedContent <- parseContent (content ++ "\n") + manyTill spaceChar eol -- closing tag must be followed by optional whitespace and newline return (attr, mconcat parsedContent) where parseContent = parseFromString $ manyTill parseBlock endOfContent @@ -229,7 +230,6 @@ directive = do parseBlock :: PandocMonad m => MuseParser m (F Blocks) parseBlock = do res <- blockElements <|> para - optionMaybe blankline trace (take 60 $ show $ B.toList $ runF res def) return res @@ -338,7 +338,7 @@ quoteTag = do st <- getState let oldInQuote = museInQuote st setState $ st{ museInQuote = True } - res <- snd <$> (parseHtmlContent "quote") + res <- snd <$> parseHtmlContent "quote" setState $ st{ museInQuote = oldInQuote } return $ B.blockQuote <$> res @@ -373,7 +373,9 @@ para = do indent <- length <$> many spaceChar st <- museInList <$> getState let f = if not st && indent >= 2 && indent < 6 then B.blockQuote else id - fmap (f . B.para) . trimInlinesF . mconcat <$> many1Till inline endOfParaElement + res <- fmap (f . B.para) . trimInlinesF . mconcat <$> many1Till inline endOfParaElement + manyTill spaceChar eol + return res where endOfParaElement = lookAhead $ try (eof <|> newBlockElement) newBlockElement = blankline >> void blockElements -- cgit v1.2.3 From c74d2064a7cf40b4c03b1b047a03d24a5fd31645 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 5 Feb 2018 02:57:03 +0300 Subject: Muse reader: avoid parsing newline after paragraph twice Removed lookAhead from blankline, so it is consumed. --- src/Text/Pandoc/Readers/Muse.hs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 63dcac122..4f4300a63 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -373,12 +373,10 @@ para = do indent <- length <$> many spaceChar st <- museInList <$> getState let f = if not st && indent >= 2 && indent < 6 then B.blockQuote else id - res <- fmap (f . B.para) . trimInlinesF . mconcat <$> many1Till inline endOfParaElement - manyTill spaceChar eol - return res + fmap (f . B.para) . trimInlinesF . mconcat <$> many1Till inline endOfParaElement where - endOfParaElement = lookAhead $ try (eof <|> newBlockElement) - newBlockElement = blankline >> void blockElements + endOfParaElement = try (eof <|> newBlockElement) + newBlockElement = blankline >> void (lookAhead blockElements) noteMarker :: PandocMonad m => MuseParser m String noteMarker = try $ do -- cgit v1.2.3 From e645510d54faf4a700b60b0671ade38b037bad90 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 5 Feb 2018 04:17:11 +0300 Subject: Muse reader: implement paraUntil paraUntil does not discard the result of the following block parsing. This change is a part of Muse reader refactoring to avoid reparsing. --- src/Text/Pandoc/Readers/Muse.hs | 44 +++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 4f4300a63..d985f5cf8 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -186,6 +186,29 @@ atStart p = do guard $ museLastStrPos st /= Just pos p +-- Like manyTill, but also returns result of end parser +manyUntil :: (Stream s m t) + => ParserT s u m a + -> ParserT s u m b + -> ParserT s u m ([a], b) +manyUntil p end = scan + where scan = + (do e <- end + return ([], e) + ) <|> + (do x <- p + (xs, e) <- scan + return (x:xs, e)) + +someUntil :: (Stream s m t) + => ParserT s u m a + -> ParserT s u m b + -> ParserT s u m ([a], b) +someUntil p end = do + first <- p + (rest, e) <- manyUntil p end + return (first:rest, e) + -- -- directive parsers -- @@ -368,15 +391,20 @@ commentTag :: PandocMonad m => MuseParser m (F Blocks) commentTag = htmlElement "comment" >> return mempty -- Indented paragraph is either center, right or quote +paraUntil :: PandocMonad m + => MuseParser m a + -> MuseParser m (F Blocks, a) +paraUntil end = do + indent <- length <$> many spaceChar + st <- museInList <$> getState + let f = if not st && indent >= 2 && indent < 6 then B.blockQuote else id + (l, e) <- someUntil inline $ try end + let p = fmap (f . B.para) $ trimInlinesF $ mconcat l + return (p, e) + para :: PandocMonad m => MuseParser m (F Blocks) -para = do - indent <- length <$> many spaceChar - st <- museInList <$> getState - let f = if not st && indent >= 2 && indent < 6 then B.blockQuote else id - fmap (f . B.para) . trimInlinesF . mconcat <$> many1Till inline endOfParaElement - where - endOfParaElement = try (eof <|> newBlockElement) - newBlockElement = blankline >> void (lookAhead blockElements) +para = + fst <$> paraUntil (try (eof <|> (blankline >> void (lookAhead blockElements)))) noteMarker :: PandocMonad m => MuseParser m String noteMarker = try $ do -- cgit v1.2.3 From fe5fd12812e657c720cc2c2ddff314ad7c1547fd Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 5 Feb 2018 15:41:51 +0300 Subject: Muse reader: avoid reparsing at the top level Blocks following paragraphs are parsed only once at the top level. Lists still take exponential time to parse, but this time is not doubled anymore when this list terminates paragraph. --- src/Text/Pandoc/Readers/Muse.hs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index d985f5cf8..f24ad9274 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -130,8 +130,7 @@ instance HasLogMessages MuseState where parseMuse :: PandocMonad m => MuseParser m Pandoc parseMuse = do many directive - blocks <- mconcat <$> many parseBlock - eof + blocks <- parseBlocks st <- getState let doc = runF (do Pandoc _ bs <- B.doc <$> blocks meta <- museMeta st @@ -250,6 +249,19 @@ directive = do -- block parsers -- +parseBlocks :: PandocMonad m + => MuseParser m (F Blocks) +parseBlocks = + try (mempty <$ eof) <|> + try blockStart <|> + try paraStart + where + blockStart = do first <- blockElements + rest <- parseBlocks + return $ first B.<> rest + paraStart = do (first, rest) <- paraUntil ((mempty <$ eof) <|> (blankline >> blockStart)) + return $ first B.<> rest + parseBlock :: PandocMonad m => MuseParser m (F Blocks) parseBlock = do res <- blockElements <|> para -- cgit v1.2.3 From 5b3247d0b28b5cdd11bc82f2381bbf688d557a07 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Tue, 6 Feb 2018 01:35:11 +0300 Subject: Muse reader: rename "literal" to "literalTag" --- src/Text/Pandoc/Readers/Muse.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index f24ad9274..2ea4b1dd5 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -275,7 +275,7 @@ blockElements = choice [ mempty <$ blankline , header , example , exampleTag - , literal + , literalTag , centerTag , rightTag , quoteTag @@ -351,8 +351,8 @@ exampleTag = try $ do (attr, contents) <- htmlElement "example" return $ return $ B.codeBlockWith attr $ rchop $ intercalate "\n" $ dropSpacePrefix $ splitOn "\n" $ lchop contents -literal :: PandocMonad m => MuseParser m (F Blocks) -literal = do +literalTag :: PandocMonad m => MuseParser m (F Blocks) +literalTag = do guardDisabled Ext_amuse -- Text::Amuse does not support <literal> (return . rawBlock) <$> htmlElement "literal" where -- cgit v1.2.3 From 02cb6eb47794d6af650b2d504400c61eccc8d18c Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sun, 11 Feb 2018 19:30:01 +0300 Subject: Muse reader: move end-of-line parsing to paraUntil --- src/Text/Pandoc/Readers/Muse.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 2ea4b1dd5..00123120d 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -259,7 +259,7 @@ parseBlocks = blockStart = do first <- blockElements rest <- parseBlocks return $ first B.<> rest - paraStart = do (first, rest) <- paraUntil ((mempty <$ eof) <|> (blankline >> blockStart)) + paraStart = do (first, rest) <- paraUntil ((mempty <$ eof) <|> blockStart) return $ first B.<> rest parseBlock :: PandocMonad m => MuseParser m (F Blocks) @@ -410,13 +410,13 @@ paraUntil end = do indent <- length <$> many spaceChar st <- museInList <$> getState let f = if not st && indent >= 2 && indent < 6 then B.blockQuote else id - (l, e) <- someUntil inline $ try end + (l, e) <- someUntil inline $ try (manyTill spaceChar eol >> end) let p = fmap (f . B.para) $ trimInlinesF $ mconcat l return (p, e) para :: PandocMonad m => MuseParser m (F Blocks) para = - fst <$> paraUntil (try (eof <|> (blankline >> void (lookAhead blockElements)))) + fst <$> paraUntil (try (eof <|> void (lookAhead blockElements))) noteMarker :: PandocMonad m => MuseParser m String noteMarker = try $ do -- cgit v1.2.3 From cadcf62ff39d4bec569622592b1a970714b66d0f Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 5 Feb 2018 16:15:52 +0300 Subject: Muse reader: move para definition into blockElements --- src/Text/Pandoc/Readers/Muse.hs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 00123120d..147b0731b 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -267,6 +267,7 @@ parseBlock = do res <- blockElements <|> para trace (take 60 $ show $ B.toList $ runF res def) return res + where para = fst <$> paraUntil (try (eof <|> void (lookAhead blockElements))) blockElements :: PandocMonad m => MuseParser m (F Blocks) blockElements = choice [ mempty <$ blankline @@ -414,10 +415,6 @@ paraUntil end = do let p = fmap (f . B.para) $ trimInlinesF $ mconcat l return (p, e) -para :: PandocMonad m => MuseParser m (F Blocks) -para = - fst <$> paraUntil (try (eof <|> void (lookAhead blockElements))) - noteMarker :: PandocMonad m => MuseParser m String noteMarker = try $ do char '[' -- cgit v1.2.3 From 30cd636c21d70ce07ffa03d6c0b452fa569724cb Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 12 Feb 2018 01:40:55 +0300 Subject: Muse reader: replace optionMaybe with optional --- src/Text/Pandoc/Readers/Muse.hs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 147b0731b..5b4781ec0 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -295,7 +295,7 @@ blockElements = choice [ mempty <$ blankline comment :: PandocMonad m => MuseParser m (F Blocks) comment = try $ do char ';' - optionMaybe (spaceChar >> many (noneOf "\n")) + optional (spaceChar >> many (noneOf "\n")) eol return mempty @@ -323,8 +323,8 @@ header = try $ do example :: PandocMonad m => MuseParser m (F Blocks) example = try $ do string "{{{" - optionMaybe blankline - contents <- manyTill anyChar $ try (optionMaybe blankline >> string "}}}") + optional blankline + contents <- manyTill anyChar $ try (optional blankline >> string "}}}") return $ return $ B.codeBlock contents -- Trim up to one newline from the beginning and the end, @@ -502,7 +502,7 @@ listItemContents = do listItem :: PandocMonad m => Int -> MuseParser m a -> MuseParser m (F Blocks) listItem n p = try $ do - optionMaybe blankline + optional blankline count n spaceChar p void spaceChar <|> lookAhead eol @@ -571,7 +571,7 @@ definitionList = try $ do pos <- getPosition guardDisabled Ext_amuse <|> guard (sourceColumn pos /= 1) -- Initial space is required by Amusewiki, but not Emacs Muse first <- definitionListItem 0 - rest <- many $ try (optionMaybe blankline >> definitionListItem (sourceColumn pos - 1)) + rest <- many $ try (optional blankline >> definitionListItem (sourceColumn pos - 1)) return $ B.definitionList <$> sequence (first : rest) -- -- cgit v1.2.3 From 3480a8acc24c650bc208b2e6cc8f1d5ac2e04aa5 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 12 Feb 2018 04:25:13 +0300 Subject: Muse reader: paragraph indentation does not indicate nested quote Muse allows indentation to indicate quotation or alignment, but only on the top level, not within a <quote> or list. This patch also simplifies the code by removing museInQuote and museInList fields from the state structure. Headers and indented paragraphs are attempted to be parsed only at the topmost level, instead of aborting parsing with guards. --- src/Text/Pandoc/Readers/Muse.hs | 45 +++++++++-------------------------------- 1 file changed, 10 insertions(+), 35 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 5b4781ec0..32be9018f 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -81,8 +81,6 @@ data MuseState = MuseState { museMeta :: F Meta -- ^ Document metadata , museLastStrPos :: Maybe SourcePos -- ^ Position after last str parsed , museLogMessages :: [LogMessage] , museNotes :: M.Map String (SourcePos, F Blocks) - , museInQuote :: Bool - , museInList :: Bool , museInLink :: Bool } @@ -97,8 +95,6 @@ defaultMuseState = MuseState { museMeta = return nullMeta , museLastStrPos = Nothing , museLogMessages = [] , museNotes = M.empty - , museInQuote = False - , museInList = False , museInLink = False } @@ -256,11 +252,14 @@ parseBlocks = try blockStart <|> try paraStart where - blockStart = do first <- blockElements + blockStart = do first <- header <|> blockElements <|> amuseNoteBlock <|> emacsNoteBlock rest <- parseBlocks return $ first B.<> rest - paraStart = do (first, rest) <- paraUntil ((mempty <$ eof) <|> blockStart) - return $ first B.<> rest + paraStart = do + indent <- length <$> many spaceChar + (first, rest) <- paraUntil ((mempty <$ eof) <|> blockStart) + let first' = if indent >= 2 && indent < 6 then B.blockQuote <$> first else first + return $ first' B.<> rest parseBlock :: PandocMonad m => MuseParser m (F Blocks) parseBlock = do @@ -273,7 +272,6 @@ blockElements :: PandocMonad m => MuseParser m (F Blocks) blockElements = choice [ mempty <$ blankline , comment , separator - , header , example , exampleTag , literalTag @@ -288,8 +286,6 @@ blockElements = choice [ mempty <$ blankline , definitionList , table , commentTag - , amuseNoteBlock - , emacsNoteBlock ] comment :: PandocMonad m => MuseParser m (F Blocks) @@ -309,9 +305,7 @@ separator = try $ do header :: PandocMonad m => MuseParser m (F Blocks) header = try $ do - st <- museInList <$> getState - q <- museInQuote <$> getState - getPosition >>= \pos -> guard (not st && not q && sourceColumn pos == 1) + getPosition >>= \pos -> guard (sourceColumn pos == 1) level <- fmap length $ many1 $ char '*' guard $ level <= 5 spaceChar @@ -370,13 +364,7 @@ rightTag :: PandocMonad m => MuseParser m (F Blocks) rightTag = snd <$> parseHtmlContent "right" quoteTag :: PandocMonad m => MuseParser m (F Blocks) -quoteTag = do - st <- getState - let oldInQuote = museInQuote st - setState $ st{ museInQuote = True } - res <- snd <$> parseHtmlContent "quote" - setState $ st{ museInQuote = oldInQuote } - return $ B.blockQuote <$> res +quoteTag = (fmap B.blockQuote) <$> snd <$> parseHtmlContent "quote" -- <div> tag is supported by Emacs Muse, but not Amusewiki 2.025 divTag :: PandocMonad m => MuseParser m (F Blocks) @@ -408,12 +396,8 @@ paraUntil :: PandocMonad m => MuseParser m a -> MuseParser m (F Blocks, a) paraUntil end = do - indent <- length <$> many spaceChar - st <- museInList <$> getState - let f = if not st && indent >= 2 && indent < 6 then B.blockQuote else id (l, e) <- someUntil inline $ try (manyTill spaceChar eol >> end) - let p = fmap (f . B.para) $ trimInlinesF $ mconcat l - return (p, e) + return (fmap (B.para) $ trimInlinesF $ mconcat l, e) noteMarker :: PandocMonad m => MuseParser m String noteMarker = try $ do @@ -481,18 +465,9 @@ lineBlock = try $ do -- lists -- -withListContext :: PandocMonad m => MuseParser m a -> MuseParser m a -withListContext p = do - state <- getState - let oldInList = museInList state - setState $ state { museInList = True } - parsed <- p - updateState (\st -> st { museInList = oldInList }) - return parsed - listItemContents' :: PandocMonad m => Int -> MuseParser m (F Blocks) listItemContents' col = - mconcat <$> withListContext (parseBlock `sepBy1` try (skipMany blankline >> indentWith col)) + mconcat <$> parseBlock `sepBy1` try (skipMany blankline >> indentWith col) listItemContents :: PandocMonad m => MuseParser m (F Blocks) listItemContents = do -- cgit v1.2.3 From 10c8b9f4bbd78de75ebd134547445e9f1df13248 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 12 Feb 2018 14:56:39 +0300 Subject: Muse reader: move indentation parsing from definitionListItem to definitionList --- src/Text/Pandoc/Readers/Muse.hs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 32be9018f..1385533b3 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -529,9 +529,8 @@ orderedList = try $ do rest <- many $ listItem (col - 1) (museOrderedListMarker style) return $ B.orderedListWith p <$> sequence (first : rest) -definitionListItem :: PandocMonad m => Int -> MuseParser m (F (Inlines, [Blocks])) -definitionListItem n = try $ do - count n spaceChar +definitionListItem :: PandocMonad m => MuseParser m (F (Inlines, [Blocks])) +definitionListItem = try $ do pos <- getPosition term <- trimInlinesF . mconcat <$> manyTill (choice inlineList) (string "::") void spaceChar <|> lookAhead eol @@ -544,9 +543,10 @@ definitionList :: PandocMonad m => MuseParser m (F Blocks) definitionList = try $ do many spaceChar pos <- getPosition - guardDisabled Ext_amuse <|> guard (sourceColumn pos /= 1) -- Initial space is required by Amusewiki, but not Emacs Muse - first <- definitionListItem 0 - rest <- many $ try (optional blankline >> definitionListItem (sourceColumn pos - 1)) + let indent = sourceColumn pos - 1 + guardDisabled Ext_amuse <|> guard (indent /= 0) -- Initial space is required by Amusewiki, but not Emacs Muse + first <- definitionListItem + rest <- many $ try (optional blankline >> count indent spaceChar >> definitionListItem) return $ B.definitionList <$> sequence (first : rest) -- -- cgit v1.2.3 From 8aed3652c2cb1811aa5685bbeb7c97b097b2eed4 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Tue, 6 Feb 2018 03:17:31 +0300 Subject: Muse reader: refactor to avoid reparsing Lists are parsed in linear instead of exponential time now. Contents of block tags, such as <quote>, is parsed directly, without storing it in a string and parsing with parseFromString. Fixed a bug: headers did not terminate lists. --- src/Text/Pandoc/Readers/Muse.hs | 273 ++++++++++++++++++++++++++++------------ 1 file changed, 195 insertions(+), 78 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 1385533b3..c8ebe1883 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -47,7 +47,7 @@ import Data.List (stripPrefix, intercalate) import Data.List.Split (splitOn) import qualified Data.Map as M import qualified Data.Set as Set -import Data.Maybe (fromMaybe) +import Data.Maybe (fromMaybe, isNothing) import Data.Text (Text, unpack) import System.FilePath (takeExtension) import Text.HTML.TagSoup @@ -82,6 +82,7 @@ data MuseState = MuseState { museMeta :: F Meta -- ^ Document metadata , museLogMessages :: [LogMessage] , museNotes :: M.Map String (SourcePos, F Blocks) , museInLink :: Bool + , museInPara :: Bool } instance Default MuseState where @@ -96,6 +97,7 @@ defaultMuseState = MuseState { museMeta = return nullMeta , museLogMessages = [] , museNotes = M.empty , museInLink = False + , museInPara = False } type MuseParser = ParserT String MuseState @@ -149,6 +151,12 @@ htmlElement tag = try $ do where endtag = void $ htmlTag (~== TagClose tag) +htmlBlock :: PandocMonad m => String -> MuseParser m (Attr, String) +htmlBlock tag = try $ do + res <- htmlElement tag + manyTill spaceChar eol + return res + htmlAttrToPandoc :: [Attribute String] -> Attr htmlAttrToPandoc attrs = (ident, classes, keyvals) where @@ -159,13 +167,13 @@ htmlAttrToPandoc attrs = (ident, classes, keyvals) parseHtmlContent :: PandocMonad m => String -> MuseParser m (Attr, F Blocks) parseHtmlContent tag = do - (attr, content) <- htmlElement tag - parsedContent <- parseContent (content ++ "\n") + (TagOpen _ attr, _) <- htmlTag (~== TagOpen tag []) + manyTill spaceChar eol + content <- parseBlocksTill (manyTill spaceChar endtag) manyTill spaceChar eol -- closing tag must be followed by optional whitespace and newline - return (attr, mconcat parsedContent) + return (htmlAttrToPandoc attr, content) where - parseContent = parseFromString $ manyTill parseBlock endOfContent - endOfContent = try $ skipMany blankline >> skipSpaces >> eof + endtag = void $ htmlTag (~== TagClose tag) commonPrefix :: String -> String -> String commonPrefix _ [] = [] @@ -248,19 +256,85 @@ directive = do parseBlocks :: PandocMonad m => MuseParser m (F Blocks) parseBlocks = - try (mempty <$ eof) <|> + try parseEnd <|> try blockStart <|> + try listStart <|> try paraStart where + parseEnd = mempty <$ eof blockStart = do first <- header <|> blockElements <|> amuseNoteBlock <|> emacsNoteBlock rest <- parseBlocks return $ first B.<> rest + listStart = do + st <- getState + setState $ st{ museInPara = False } + (first, rest) <- anyListUntil parseBlocks + return $ first B.<> rest paraStart = do indent <- length <$> many spaceChar - (first, rest) <- paraUntil ((mempty <$ eof) <|> blockStart) + (first, rest) <- paraUntil parseBlocks let first' = if indent >= 2 && indent < 6 then B.blockQuote <$> first else first return $ first' B.<> rest +parseBlocksTill :: PandocMonad m + => MuseParser m a + -> MuseParser m (F Blocks) +parseBlocksTill end = + try parseEnd <|> + try blockStart <|> + try listStart <|> + try paraStart + where + parseEnd = mempty <$ end + blockStart = do first <- blockElements + rest <- continuation + return $ first B.<> rest + listStart = do + st <- getState + setState $ st{ museInPara = False } + (first, e) <- anyListUntil ((Left <$> end) <|> (Right <$> continuation)) + case e of + Left _ -> return first + Right rest -> return $ first B.<> rest + paraStart = do (first, e) <- paraUntil ((Left <$> end) <|> (Right <$> continuation)) + case e of + Left _ -> return $ first + Right rest -> return $ first B.<> rest + continuation = parseBlocksTill end + +listItemContentsUntil :: PandocMonad m + => Int + -> MuseParser m a + -> MuseParser m (F Blocks, a) +listItemContentsUntil col end = + try blockStart <|> + try listStart <|> + try paraStart + where + parseEnd = do e <- end + return (mempty, e) + paraStart = do + (first, e) <- paraUntil ((Right <$> continuation) <|> (Left <$> end)) + case e of + Left ee -> return (first, ee) + Right (rest, ee) -> return (first B.<> rest, ee) + blockStart = do first <- blockElements + (rest, e) <- continuation <|> parseEnd + return (first B.<> rest, e) + listStart = do + st <- getState + setState $ st{ museInPara = False } + (first, e) <- anyListUntil ((Right <$> continuation) <|> (Left <$> end)) + case e of + Left ee -> return (first, ee) + Right (rest, ee) -> return $ (first B.<> rest, ee) + continuation = try $ do blank <- optionMaybe blankline + skipMany blankline + indentWith col + st <- getState + setState $ st{ museInPara = museInPara st && isNothing blank } + listItemContentsUntil col end + parseBlock :: PandocMonad m => MuseParser m (F Blocks) parseBlock = do res <- blockElements <|> para @@ -269,24 +343,24 @@ parseBlock = do where para = fst <$> paraUntil (try (eof <|> void (lookAhead blockElements))) blockElements :: PandocMonad m => MuseParser m (F Blocks) -blockElements = choice [ mempty <$ blankline - , comment - , separator - , example - , exampleTag - , literalTag - , centerTag - , rightTag - , quoteTag - , divTag - , verseTag - , lineBlock - , bulletList - , orderedList - , definitionList - , table - , commentTag - ] +blockElements = do + st <- getState + setState $ st{ museInPara = False } + choice [ mempty <$ blankline + , comment + , separator + , example + , exampleTag + , literalTag + , centerTag + , rightTag + , quoteTag + , divTag + , verseTag + , lineBlock + , table + , commentTag + ] comment :: PandocMonad m => MuseParser m (F Blocks) comment = try $ do @@ -343,13 +417,13 @@ dropSpacePrefix lns = exampleTag :: PandocMonad m => MuseParser m (F Blocks) exampleTag = try $ do many spaceChar - (attr, contents) <- htmlElement "example" + (attr, contents) <- htmlBlock "example" return $ return $ B.codeBlockWith attr $ rchop $ intercalate "\n" $ dropSpacePrefix $ splitOn "\n" $ lchop contents literalTag :: PandocMonad m => MuseParser m (F Blocks) literalTag = do guardDisabled Ext_amuse -- Text::Amuse does not support <literal> - (return . rawBlock) <$> htmlElement "literal" + (return . rawBlock) <$> htmlBlock "literal" where -- FIXME: Emacs Muse inserts <literal> without style into all output formats, but we assume HTML format (_, _, kvs) = fromMaybe "html" $ lookup "style" kvs @@ -385,18 +459,22 @@ verseLines = do verseTag :: PandocMonad m => MuseParser m (F Blocks) verseTag = do - (_, content) <- htmlElement "verse" + (_, content) <- htmlBlock "verse" parseFromString verseLines (intercalate "\n" $ dropSpacePrefix $ splitOn "\n" $ lchop content) commentTag :: PandocMonad m => MuseParser m (F Blocks) -commentTag = htmlElement "comment" >> return mempty +commentTag = htmlBlock "comment" >> return mempty -- Indented paragraph is either center, right or quote paraUntil :: PandocMonad m => MuseParser m a -> MuseParser m (F Blocks, a) paraUntil end = do + state <- getState + guard $ not $ museInPara state + setState $ state{ museInPara = True } (l, e) <- someUntil inline $ try (manyTill spaceChar eol >> end) + updateState (\st -> st { museInPara = False }) return (fmap (B.para) $ trimInlinesF $ mconcat l, e) noteMarker :: PandocMonad m => MuseParser m String @@ -413,6 +491,8 @@ amuseNoteBlock = try $ do guardEnabled Ext_amuse pos <- getPosition ref <- noteMarker <* spaceChar + st <- getState + setState $ st{ museInPara = False } content <- listItemContents oldnotes <- museNotes <$> getState case M.lookup ref oldnotes of @@ -465,35 +545,36 @@ lineBlock = try $ do -- lists -- -listItemContents' :: PandocMonad m => Int -> MuseParser m (F Blocks) -listItemContents' col = - mconcat <$> parseBlock `sepBy1` try (skipMany blankline >> indentWith col) +bulletListItemsUntil :: PandocMonad m + => Int + -> MuseParser m a + -> MuseParser m ([F Blocks], a) +bulletListItemsUntil indent end = try $ do + char '-' + void spaceChar <|> lookAhead eol + st <- getState + setState $ st{ museInPara = False } + (x, e) <- listItemContentsUntil (indent + 2) ((Right <$> try (optional blankline >> indentWith indent >> bulletListItemsUntil indent end)) <|> (Left <$> end)) + case e of + Left ee -> return ([x], ee) + Right (xs, ee) -> return (x:xs, ee) + +bulletListUntil :: PandocMonad m + => MuseParser m a + -> MuseParser m (F Blocks, a) +bulletListUntil end = try $ do + many spaceChar + pos <- getPosition + let indent = sourceColumn pos - 1 + guard $ indent /= 0 + (items, e) <- bulletListItemsUntil indent end + return $ (B.bulletList <$> sequence items, e) listItemContents :: PandocMonad m => MuseParser m (F Blocks) listItemContents = do pos <- getPosition let col = sourceColumn pos - 1 - listItemContents' col - -listItem :: PandocMonad m => Int -> MuseParser m a -> MuseParser m (F Blocks) -listItem n p = try $ do - optional blankline - count n spaceChar - p - void spaceChar <|> lookAhead eol - listItemContents - -bulletList :: PandocMonad m => MuseParser m (F Blocks) -bulletList = try $ do - many spaceChar - pos <- getPosition - let col = sourceColumn pos - guard $ col /= 1 - char '-' - void spaceChar <|> lookAhead eol - first <- listItemContents - rest <- many $ listItem (col - 1) (char '-') - return $ B.bulletList <$> sequence (first : rest) + mconcat <$> parseBlock `sepBy1` try (skipMany blankline >> indentWith col) -- | Parses an ordered list marker and returns list attributes. anyMuseOrderedListMarker :: PandocMonad m => MuseParser m ListAttributes @@ -516,38 +597,74 @@ museOrderedListMarker style = do char '.' return start -orderedList :: PandocMonad m => MuseParser m (F Blocks) -orderedList = try $ do +orderedListItemsUntil :: PandocMonad m + => Int + -> ListNumberStyle + -> MuseParser m a + -> MuseParser m ([F Blocks], a) +orderedListItemsUntil indent style end = + continuation + where + continuation = try $ do + pos <- getPosition + void spaceChar <|> lookAhead eol + st <- getState + setState $ st{ museInPara = False } + (x, e) <- listItemContentsUntil (sourceColumn pos) ((Right <$> try (optionMaybe blankline >> indentWith indent >> museOrderedListMarker style >> continuation)) <|> (Left <$> end)) + case e of + Left ee -> return ([x], ee) + Right (xs, ee) -> return (x:xs, ee) + +orderedListUntil :: PandocMonad m + => MuseParser m a + -> MuseParser m (F Blocks, a) +orderedListUntil end = try $ do many spaceChar pos <- getPosition - let col = sourceColumn pos - guard $ col /= 1 + let indent = sourceColumn pos - 1 + guard $ indent /= 0 p@(_, style, _) <- anyMuseOrderedListMarker guard $ style `elem` [Decimal, LowerAlpha, UpperAlpha, LowerRoman, UpperRoman] - void spaceChar <|> lookAhead eol - first <- listItemContents - rest <- many $ listItem (col - 1) (museOrderedListMarker style) - return $ B.orderedListWith p <$> sequence (first : rest) - -definitionListItem :: PandocMonad m => MuseParser m (F (Inlines, [Blocks])) -definitionListItem = try $ do - pos <- getPosition - term <- trimInlinesF . mconcat <$> manyTill (choice inlineList) (string "::") - void spaceChar <|> lookAhead eol - contents <- listItemContents' $ sourceColumn pos - pure $ do lineContent' <- contents - term' <- term - pure (term', [lineContent']) - -definitionList :: PandocMonad m => MuseParser m (F Blocks) -definitionList = try $ do + (items, e) <- orderedListItemsUntil indent style end + return $ (B.orderedListWith p <$> sequence items, e) + +definitionListItemsUntil :: PandocMonad m + => Int + -> MuseParser m a + -> MuseParser m ([F (Inlines, [Blocks])], a) +definitionListItemsUntil indent end = + continuation + where continuation = try $ do + pos <- getPosition + term <- trimInlinesF . mconcat <$> manyTill (choice inlineList) (string "::") + void spaceChar <|> lookAhead eol + st <- getState + setState $ st{ museInPara = False } + (x, e) <- listItemContentsUntil (sourceColumn pos) ((Right <$> try (optional blankline >> count indent spaceChar >> continuation)) <|> (Left <$> end)) + let xx = do + term' <- term + x' <- x + (return (term', [x']))::(F (Inlines, [Blocks])) + case e of + Left ee -> return $ ([xx], ee) + Right (xs, ee) -> return $ (xx : xs, ee) + +definitionListUntil :: PandocMonad m + => MuseParser m a + -> MuseParser m (F Blocks, a) +definitionListUntil end = try $ do many spaceChar pos <- getPosition let indent = sourceColumn pos - 1 guardDisabled Ext_amuse <|> guard (indent /= 0) -- Initial space is required by Amusewiki, but not Emacs Muse - first <- definitionListItem - rest <- many $ try (optional blankline >> count indent spaceChar >> definitionListItem) - return $ B.definitionList <$> sequence (first : rest) + (items, e) <- definitionListItemsUntil indent end + return (B.definitionList <$> sequence items, e) + +anyListUntil :: PandocMonad m + => MuseParser m a + -> MuseParser m (F Blocks, a) +anyListUntil end = + bulletListUntil end <|> orderedListUntil end <|> definitionListUntil end -- -- tables -- cgit v1.2.3 From 5a304360d0c871e95cbc4c61a5d5127ebbe99651 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Tue, 13 Feb 2018 14:13:00 +0300 Subject: Muse reader: parse next list item before parsing more item contents --- src/Text/Pandoc/Readers/Muse.hs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index c8ebe1883..18d4104ff 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -305,26 +305,29 @@ parseBlocksTill end = listItemContentsUntil :: PandocMonad m => Int -> MuseParser m a + -> MuseParser m a -> MuseParser m (F Blocks, a) -listItemContentsUntil col end = +listItemContentsUntil col pre end = try blockStart <|> try listStart <|> try paraStart where + parsePre = do e <- pre + return (mempty, e) parseEnd = do e <- end return (mempty, e) paraStart = do - (first, e) <- paraUntil ((Right <$> continuation) <|> (Left <$> end)) + (first, e) <- paraUntil ((Left <$> pre) <|> (Right <$> continuation) <|> (Left <$> end)) case e of Left ee -> return (first, ee) Right (rest, ee) -> return (first B.<> rest, ee) blockStart = do first <- blockElements - (rest, e) <- continuation <|> parseEnd + (rest, e) <- parsePre <|> continuation <|> parseEnd return (first B.<> rest, e) listStart = do st <- getState setState $ st{ museInPara = False } - (first, e) <- anyListUntil ((Right <$> continuation) <|> (Left <$> end)) + (first, e) <- anyListUntil ((Left <$> pre) <|> (Right <$> continuation) <|> (Left <$> end)) case e of Left ee -> return (first, ee) Right (rest, ee) -> return $ (first B.<> rest, ee) @@ -333,7 +336,7 @@ listItemContentsUntil col end = indentWith col st <- getState setState $ st{ museInPara = museInPara st && isNothing blank } - listItemContentsUntil col end + listItemContentsUntil col pre end parseBlock :: PandocMonad m => MuseParser m (F Blocks) parseBlock = do @@ -554,7 +557,7 @@ bulletListItemsUntil indent end = try $ do void spaceChar <|> lookAhead eol st <- getState setState $ st{ museInPara = False } - (x, e) <- listItemContentsUntil (indent + 2) ((Right <$> try (optional blankline >> indentWith indent >> bulletListItemsUntil indent end)) <|> (Left <$> end)) + (x, e) <- listItemContentsUntil (indent + 2) (Right <$> try (optional blankline >> indentWith indent >> bulletListItemsUntil indent end)) (Left <$> end) case e of Left ee -> return ([x], ee) Right (xs, ee) -> return (x:xs, ee) @@ -610,7 +613,7 @@ orderedListItemsUntil indent style end = void spaceChar <|> lookAhead eol st <- getState setState $ st{ museInPara = False } - (x, e) <- listItemContentsUntil (sourceColumn pos) ((Right <$> try (optionMaybe blankline >> indentWith indent >> museOrderedListMarker style >> continuation)) <|> (Left <$> end)) + (x, e) <- listItemContentsUntil (sourceColumn pos) (Right <$> try (optionMaybe blankline >> indentWith indent >> museOrderedListMarker style >> continuation)) (Left <$> end) case e of Left ee -> return ([x], ee) Right (xs, ee) -> return (x:xs, ee) -- cgit v1.2.3 From 42e39fbd2678fb8480b6253232ffe0258d2bae00 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Tue, 13 Feb 2018 08:55:30 +0300 Subject: Muse reader: parse definition lists with multiple descriptions --- src/Text/Pandoc/Readers/Muse.hs | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 18d4104ff..7ac33fe69 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -631,26 +631,37 @@ orderedListUntil end = try $ do (items, e) <- orderedListItemsUntil indent style end return $ (B.orderedListWith p <$> sequence items, e) +descriptionsUntil :: PandocMonad m + => Int + -> MuseParser m a + -> MuseParser m ([F Blocks], a) +descriptionsUntil indent end = do + void spaceChar <|> lookAhead eol + st <- getState + setState $ st{ museInPara = False } + (x, e) <- listItemContentsUntil indent (Right <$> try (optional blankline >> indentWith indent >> manyTill spaceChar (string "::") >> descriptionsUntil indent end)) (Left <$> end) + case e of + Right (xs, ee) -> return (x:xs, ee) + Left ee -> return ([x], ee) + definitionListItemsUntil :: PandocMonad m => Int -> MuseParser m a -> MuseParser m ([F (Inlines, [Blocks])], a) definitionListItemsUntil indent end = continuation - where continuation = try $ do - pos <- getPosition - term <- trimInlinesF . mconcat <$> manyTill (choice inlineList) (string "::") - void spaceChar <|> lookAhead eol - st <- getState - setState $ st{ museInPara = False } - (x, e) <- listItemContentsUntil (sourceColumn pos) ((Right <$> try (optional blankline >> count indent spaceChar >> continuation)) <|> (Left <$> end)) - let xx = do - term' <- term - x' <- x - (return (term', [x']))::(F (Inlines, [Blocks])) - case e of - Left ee -> return $ ([xx], ee) - Right (xs, ee) -> return $ (xx : xs, ee) + where + continuation = try $ do + pos <- getPosition + term <- trimInlinesF . mconcat <$> manyTill (choice inlineList) (string "::") + (x, e) <- descriptionsUntil (sourceColumn pos) ((Right <$> try (optional blankline >> indentWith indent >> continuation)) <|> (Left <$> end)) + let xx = do + term' <- term + x' <- sequence x + return (term', x') + case e of + Left ee -> return ([xx], ee) + Right (xs, ee) -> return (xx:xs, ee) definitionListUntil :: PandocMonad m => MuseParser m a -- cgit v1.2.3 From e02b7d2b384ac5ae55e13cb60dcf7f89dffcd806 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Tue, 13 Feb 2018 14:34:27 +0300 Subject: Muse reader: hlint --- src/Text/Pandoc/Readers/Muse.hs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 7ac33fe69..770199fd7 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -298,7 +298,7 @@ parseBlocksTill end = Right rest -> return $ first B.<> rest paraStart = do (first, e) <- paraUntil ((Left <$> end) <|> (Right <$> continuation)) case e of - Left _ -> return $ first + Left _ -> return first Right rest -> return $ first B.<> rest continuation = parseBlocksTill end @@ -330,7 +330,7 @@ listItemContentsUntil col pre end = (first, e) <- anyListUntil ((Left <$> pre) <|> (Right <$> continuation) <|> (Left <$> end)) case e of Left ee -> return (first, ee) - Right (rest, ee) -> return $ (first B.<> rest, ee) + Right (rest, ee) -> return (first B.<> rest, ee) continuation = try $ do blank <- optionMaybe blankline skipMany blankline indentWith col @@ -441,7 +441,7 @@ rightTag :: PandocMonad m => MuseParser m (F Blocks) rightTag = snd <$> parseHtmlContent "right" quoteTag :: PandocMonad m => MuseParser m (F Blocks) -quoteTag = (fmap B.blockQuote) <$> snd <$> parseHtmlContent "quote" +quoteTag = fmap B.blockQuote . snd <$> parseHtmlContent "quote" -- <div> tag is supported by Emacs Muse, but not Amusewiki 2.025 divTag :: PandocMonad m => MuseParser m (F Blocks) @@ -451,7 +451,7 @@ divTag = do verseLine :: PandocMonad m => MuseParser m (F Inlines) verseLine = do - indent <- (B.str <$> many1 (char ' ' >> pure '\160')) <|> (pure mempty) + indent <- (B.str <$> many1 (char ' ' >> pure '\160')) <|> pure mempty rest <- manyTill (choice inlineList) newline return $ trimInlinesF $ mconcat (pure indent : rest) @@ -478,7 +478,7 @@ paraUntil end = do setState $ state{ museInPara = True } (l, e) <- someUntil inline $ try (manyTill spaceChar eol >> end) updateState (\st -> st { museInPara = False }) - return (fmap (B.para) $ trimInlinesF $ mconcat l, e) + return (fmap B.para $ trimInlinesF $ mconcat l, e) noteMarker :: PandocMonad m => MuseParser m String noteMarker = try $ do @@ -571,7 +571,7 @@ bulletListUntil end = try $ do let indent = sourceColumn pos - 1 guard $ indent /= 0 (items, e) <- bulletListItemsUntil indent end - return $ (B.bulletList <$> sequence items, e) + return (B.bulletList <$> sequence items, e) listItemContents :: PandocMonad m => MuseParser m (F Blocks) listItemContents = do @@ -629,7 +629,7 @@ orderedListUntil end = try $ do p@(_, style, _) <- anyMuseOrderedListMarker guard $ style `elem` [Decimal, LowerAlpha, UpperAlpha, LowerRoman, UpperRoman] (items, e) <- orderedListItemsUntil indent style end - return $ (B.orderedListWith p <$> sequence items, e) + return (B.orderedListWith p <$> sequence items, e) descriptionsUntil :: PandocMonad m => Int -- cgit v1.2.3 From 650b30737f15ea75e1e78d5523e2a429df2e8a2c Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Tue, 13 Feb 2018 15:58:22 +0300 Subject: Muse reader: remove listItemContents function --- src/Text/Pandoc/Readers/Muse.hs | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 770199fd7..a89bc629d 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -262,13 +262,13 @@ parseBlocks = try paraStart where parseEnd = mempty <$ eof - blockStart = do first <- header <|> blockElements <|> amuseNoteBlock <|> emacsNoteBlock + blockStart = do first <- header <|> blockElements <|> emacsNoteBlock rest <- parseBlocks return $ first B.<> rest listStart = do st <- getState setState $ st{ museInPara = False } - (first, rest) <- anyListUntil parseBlocks + (first, rest) <- anyListUntil parseBlocks <|> amuseNoteBlockUntil parseBlocks return $ first B.<> rest paraStart = do indent <- length <$> many spaceChar @@ -489,20 +489,22 @@ noteMarker = try $ do -- Amusewiki version of note -- Parsing is similar to list item, except that note marker is used instead of list marker -amuseNoteBlock :: PandocMonad m => MuseParser m (F Blocks) -amuseNoteBlock = try $ do +amuseNoteBlockUntil :: PandocMonad m + => MuseParser m a + -> MuseParser m (F Blocks, a) +amuseNoteBlockUntil end = try $ do guardEnabled Ext_amuse pos <- getPosition ref <- noteMarker <* spaceChar st <- getState setState $ st{ museInPara = False } - content <- listItemContents + (content, e) <- listItemContentsUntil (sourceColumn pos) (fail "x") end oldnotes <- museNotes <$> getState case M.lookup ref oldnotes of Just _ -> logMessage $ DuplicateNoteReference ref pos Nothing -> return () updateState $ \s -> s{ museNotes = M.insert ref (pos, content) oldnotes } - return mempty + return (mempty, e) -- Emacs version of note -- Notes are allowed only at the end of text, no indentation is required. @@ -573,12 +575,6 @@ bulletListUntil end = try $ do (items, e) <- bulletListItemsUntil indent end return (B.bulletList <$> sequence items, e) -listItemContents :: PandocMonad m => MuseParser m (F Blocks) -listItemContents = do - pos <- getPosition - let col = sourceColumn pos - 1 - mconcat <$> parseBlock `sepBy1` try (skipMany blankline >> indentWith col) - -- | Parses an ordered list marker and returns list attributes. anyMuseOrderedListMarker :: PandocMonad m => MuseParser m ListAttributes anyMuseOrderedListMarker = do -- cgit v1.2.3 From 6dcb9744237be713f4ef94017a6b68fc0cfddb73 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Tue, 13 Feb 2018 23:12:28 +0300 Subject: AsciiDoc writer: do not output implicit heading IDs Convert to asciidoc-auto_identifiers for old behaviour. Fixes #4363 --- src/Text/Pandoc/Writers/AsciiDoc.hs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/AsciiDoc.hs b/src/Text/Pandoc/Writers/AsciiDoc.hs index b8f647b66..f91fa8fa0 100644 --- a/src/Text/Pandoc/Writers/AsciiDoc.hs +++ b/src/Text/Pandoc/Writers/AsciiDoc.hs @@ -43,6 +43,7 @@ import Data.Char (isPunctuation, isSpace) import Data.List (intercalate, intersperse, stripPrefix) import qualified Data.Map as M import Data.Maybe (fromMaybe, isJust) +import qualified Data.Set as Set import Data.Text (Text) import qualified Data.Text as T import Text.Pandoc.Class (PandocMonad, report) @@ -60,6 +61,7 @@ data WriterState = WriterState { defListMarker :: String , orderedListLevel :: Int , bulletListLevel :: Int , intraword :: Bool + , autoIds :: Set.Set String } -- | Convert Pandoc to AsciiDoc. @@ -70,6 +72,7 @@ writeAsciiDoc opts document = , orderedListLevel = 1 , bulletListLevel = 1 , intraword = False + , autoIds = Set.empty } type ADW = StateT WriterState @@ -164,7 +167,11 @@ blockToAsciiDoc opts (Header level (ident,_,_) inlines) = do let len = offset contents -- ident seem to be empty most of the time and asciidoc will generate them automatically -- so lets make them not show up when null - let identifier = if null ident then empty else "[[" <> text ident <> "]]" + ids <- gets autoIds + let autoId = uniqueIdent inlines ids + modify $ \st -> st{ autoIds = Set.insert autoId ids } + let identifier = if null ident || (isEnabled Ext_auto_identifiers opts && ident == autoId) + then empty else "[[" <> text ident <> "]]" let setext = writerSetextHeaders opts return (if setext -- cgit v1.2.3 From 9131d62c9b9a70829e0d938d0d201a09b2cec179 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Wed, 14 Feb 2018 13:49:56 +0300 Subject: Muse writer: use unicode quotes for quoted text --- src/Text/Pandoc/Writers/Muse.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index a1414abc5..7f53e202d 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -353,10 +353,10 @@ inlineToMuse (Subscript lst) = do inlineToMuse (SmallCaps lst) = inlineListToMuse lst inlineToMuse (Quoted SingleQuote lst) = do contents <- inlineListToMuse lst - return $ "'" <> contents <> "'" + return $ "‘" <> contents <> "’" inlineToMuse (Quoted DoubleQuote lst) = do contents <- inlineListToMuse lst - return $ "\"" <> contents <> "\"" + return $ "“" <> contents <> "”" -- Amusewiki does not support <cite> tag, -- and Emacs Muse citation support is limited -- (https://www.gnu.org/software/emacs-muse/manual/html_node/Citations.html#Citation) -- cgit v1.2.3 From 82a0ceaf18e589e8916fbd70e0b13e5945bcc99a Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Thu, 15 Feb 2018 16:32:47 +0300 Subject: Muse reader: fix directive parsing This fixes bugs introduced in commit 4bfab8f04c105f111d8d4e1c3ed7f7b5c75dbd19. --- src/Text/Pandoc/Readers/Muse.hs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index a89bc629d..7504a33ca 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -237,9 +237,7 @@ parseAmuseDirective = do many blankline return (key, value) where - endOfDirective = lookAhead $ try (eof <|> - void (newline >> blankline) <|> - void (newline >> parseDirectiveKey)) + endOfDirective = lookAhead $ eof <|> try (newline >> (void blankline <|> void parseDirectiveKey)) directive :: PandocMonad m => MuseParser m () directive = do -- cgit v1.2.3 From e6ff7f79861d3088f8cba2b22d86d9f75db73f6a Mon Sep 17 00:00:00 2001 From: danse <f.occhipinti@gmail.com> Date: Mon, 12 Feb 2018 17:10:29 +0100 Subject: Docx reader: Pick table width from the longest row or header This change is intended to preserve as much of the table content as possible Closes #4360 --- src/Text/Pandoc/Readers/Docx.hs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index c24c43901..098759a61 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -642,7 +642,7 @@ bodyPartToBlocks (ListItem pPr _ _ _ parparts) = bodyPartToBlocks $ Paragraph pPr' parparts bodyPartToBlocks (Tbl _ _ _ []) = return $ para mempty -bodyPartToBlocks (Tbl cap _ look (r:rs)) = do +bodyPartToBlocks (Tbl cap _ look parts@(r:rs)) = do let caption = text cap (hdr, rows) = case firstRowFormatting look of True | null rs -> (Nothing, [r]) @@ -651,10 +651,14 @@ bodyPartToBlocks (Tbl cap _ look (r:rs)) = do cells <- mapM rowToBlocksList rows - let width = case cells of - r':_ -> length r' - -- shouldn't happen - [] -> 0 + let width = maybe 0 maximum $ nonEmpty $ map rowLength parts + -- Data.List.NonEmpty is not available with ghc 7.10 so we roll out + -- our own, see + -- https://github.com/jgm/pandoc/pull/4361#issuecomment-365416155 + nonEmpty [] = Nothing + nonEmpty l = Just l + rowLength :: Row -> Int + rowLength (Row c) = length c hdrCells <- case hdr of Just r' -> rowToBlocksList r' -- cgit v1.2.3 From 0e4b8ae36296030569627baa537b9d71af621d20 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 16 Feb 2018 12:53:41 +0300 Subject: Muse reader: prioritize lists with roman numerals over alphabetical lists This is to make sure "i." starts a roman numbered list, instead of a list with letter "i" (followed by "j", "k", ..."). --- src/Text/Pandoc/Readers/Muse.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 7504a33ca..a842925a2 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -576,7 +576,7 @@ bulletListUntil end = try $ do -- | Parses an ordered list marker and returns list attributes. anyMuseOrderedListMarker :: PandocMonad m => MuseParser m ListAttributes anyMuseOrderedListMarker = do - (style, start) <- decimal <|> lowerAlpha <|> lowerRoman <|> upperAlpha <|> upperRoman + (style, start) <- decimal <|> lowerRoman <|> upperRoman <|> lowerAlpha <|> upperAlpha char '.' return (start, style, Period) -- cgit v1.2.3 From ded2e211ca181603ab1a0da3f938508efdb7cdf0 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 16 Feb 2018 10:41:22 -0800 Subject: TEI writer: Use 'xml:id', not 'id' attribute. Closes #4371. --- src/Text/Pandoc/Writers/TEI.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/TEI.hs b/src/Text/Pandoc/Writers/TEI.hs index be1c594aa..d49a58818 100644 --- a/src/Text/Pandoc/Writers/TEI.hs +++ b/src/Text/Pandoc/Writers/TEI.hs @@ -104,7 +104,7 @@ elementToTEI opts lvl (Sec _ _num (id',_,_) title elements) = do contents <- vcat <$> mapM (elementToTEI opts (lvl + 1)) elements' titleContents <- inlinesToTEI opts title return $ inTags True "div" (("type", divType) : - [("id", writerIdentifierPrefix opts ++ id') | not (null id')]) $ + [("xml:id", writerIdentifierPrefix opts ++ id') | not (null id')]) $ inTagsSimple "head" titleContents $$ contents -- | Convert a list of Pandoc blocks to TEI. @@ -156,7 +156,7 @@ blockToTEI _ Null = return empty -- Add ids to paragraphs in divs with ids - this is needed for -- pandoc-citeproc to get link anchors in bibliographies: blockToTEI opts (Div (ident,_,_) [Para lst]) = do - let attribs = [("id", ident) | not (null ident)] + let attribs = [("xml:id", ident) | not (null ident)] inTags False "p" attribs <$> inlinesToTEI opts lst blockToTEI opts (Div _ bs) = blocksToTEI opts $ map plainToPara bs blockToTEI _ h@Header{} = do @@ -342,7 +342,7 @@ idAndRole (id',cls,_) = ident ++ role where ident = if null id' then [] - else [("id", id')] + else [("xml:id", id')] role = if null cls then [] else [("role", unwords cls)] -- cgit v1.2.3 From 036767ea80368a1f6a382e6372c222bfd768adc7 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 16 Feb 2018 10:47:46 -0800 Subject: TEI writer: more attribute fixes. - Ensure that id prefix is always used. - Don't emit `role` attribute; that was a leftover from the Docbook writer. --- src/Text/Pandoc/Writers/TEI.hs | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/TEI.hs b/src/Text/Pandoc/Writers/TEI.hs index d49a58818..4936c743e 100644 --- a/src/Text/Pandoc/Writers/TEI.hs +++ b/src/Text/Pandoc/Writers/TEI.hs @@ -90,7 +90,7 @@ writeTEI opts (Pandoc meta blocks) = do -- | Convert an Element to TEI. elementToTEI :: PandocMonad m => WriterOptions -> Int -> Element -> m Doc elementToTEI opts _ (Blk block) = blockToTEI opts block -elementToTEI opts lvl (Sec _ _num (id',_,_) title elements) = do +elementToTEI opts lvl (Sec _ _num attr title elements) = do -- TEI doesn't allow sections with no content, so insert some if needed let elements' = if null elements then [Blk (Para [])] @@ -103,8 +103,7 @@ elementToTEI opts lvl (Sec _ _num (id',_,_) title elements) = do | otherwise -> "section" contents <- vcat <$> mapM (elementToTEI opts (lvl + 1)) elements' titleContents <- inlinesToTEI opts title - return $ inTags True "div" (("type", divType) : - [("xml:id", writerIdentifierPrefix opts ++ id') | not (null id')]) $ + return $ inTags True "div" (("type", divType) : idFromAttr opts attr) $ inTagsSimple "head" titleContents $$ contents -- | Convert a list of Pandoc blocks to TEI. @@ -142,8 +141,8 @@ listItemToTEI opts item = inTagsIndented "item" <$> blocksToTEI opts (map plainToPara item) imageToTEI :: PandocMonad m => WriterOptions -> Attr -> String -> m Doc -imageToTEI _ attr src = return $ selfClosingTag "graphic" $ - ("url", src) : idAndRole attr ++ dims +imageToTEI opts attr src = return $ selfClosingTag "graphic" $ + ("url", src) : idFromAttr opts attr ++ dims where dims = go Width "width" ++ go Height "height" go dir dstr = case dimension dir attr of @@ -155,8 +154,8 @@ blockToTEI :: PandocMonad m => WriterOptions -> Block -> m Doc blockToTEI _ Null = return empty -- Add ids to paragraphs in divs with ids - this is needed for -- pandoc-citeproc to get link anchors in bibliographies: -blockToTEI opts (Div (ident,_,_) [Para lst]) = do - let attribs = [("xml:id", ident) | not (null ident)] +blockToTEI opts (Div attr [Para lst]) = do + let attribs = idFromAttr opts attr inTags False "p" attribs <$> inlinesToTEI opts lst blockToTEI opts (Div _ bs) = blocksToTEI opts $ map plainToPara bs blockToTEI _ h@Header{} = do @@ -320,8 +319,10 @@ inlineToTEI opts (Link attr txt (src, _)) return $ linktext <+> char '(' <> emailLink <> char ')' | otherwise = (if "#" `isPrefixOf` src - then inTags False "ref" $ ("target", drop 1 src) : idAndRole attr - else inTags False "ref" $ ("target", src) : idAndRole attr ) <$> + then inTags False "ref" $ ("target", drop 1 src) + : idFromAttr opts attr + else inTags False "ref" $ ("target", src) + : idFromAttr opts attr ) <$> inlinesToTEI opts txt inlineToTEI opts (Image attr description (src, tit)) = do let titleDoc = if null tit @@ -337,12 +338,8 @@ inlineToTEI opts (Image attr description (src, tit)) = do inlineToTEI opts (Note contents) = inTagsIndented "note" <$> blocksToTEI opts contents -idAndRole :: Attr -> [(String, String)] -idAndRole (id',cls,_) = ident ++ role - where - ident = if null id' - then [] - else [("xml:id", id')] - role = if null cls - then [] - else [("role", unwords cls)] +idFromAttr :: WriterOptions -> Attr -> [(String, String)] +idFromAttr opts (id',_,_) = + if null id' + then [] + else [("xml:id", writerIdentifierPrefix opts ++ id')] -- cgit v1.2.3 From c75740e22ce35165108e020be174ebe9f35ea667 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 16 Feb 2018 13:45:15 -0800 Subject: Make image size detection for PDFs more robust. See #4322. --- src/Text/Pandoc/ImageSize.hs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/ImageSize.hs b/src/Text/Pandoc/ImageSize.hs index f21284dac..1d6db8dfa 100644 --- a/src/Text/Pandoc/ImageSize.hs +++ b/src/Text/Pandoc/ImageSize.hs @@ -283,8 +283,10 @@ pdfSize img = "/MediaBox" `B.isPrefixOf` l)) (B.lines img) of (x:_) | "/MediaBox" `B.isPrefixOf` x - -> case B.words $ B.filter (\c -> c /= '[' && c /= ']') - $ B.drop 10 x of + -> case B.words . B.takeWhile (/=']') + . B.drop 1 + . B.dropWhile (/='[') + $ x of [x1, y1, x2, y2] -> do x1' <- safeRead $ B.unpack x1 x2' <- safeRead $ B.unpack x2 -- cgit v1.2.3 From 3535af019722fc5086e968cc485ab8b94b76f1fd Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 16 Feb 2018 15:59:03 -0800 Subject: Markdown writer: properly escape @ to avoid capture as citation. Closes #4366. --- src/Text/Pandoc/Writers/Markdown.hs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs index c8b3a1526..cdd8f3b66 100644 --- a/src/Text/Pandoc/Writers/Markdown.hs +++ b/src/Text/Pandoc/Writers/Markdown.hs @@ -36,7 +36,7 @@ Markdown: <http://daringfireball.net/projects/markdown/> module Text.Pandoc.Writers.Markdown (writeMarkdown, writePlain) where import Control.Monad.Reader import Control.Monad.State.Strict -import Data.Char (chr, isPunctuation, isSpace, ord) +import Data.Char (chr, isPunctuation, isSpace, ord, isAlphaNum) import Data.Default import qualified Data.HashMap.Strict as H import Data.List (find, group, intersperse, sortBy, stripPrefix, transpose) @@ -286,6 +286,12 @@ escapeString opts (c:cs) = '>' | isEnabled Ext_all_symbols_escapable opts -> '\\' : '>' : escapeString opts cs | otherwise -> ">" ++ escapeString opts cs + '@' | isEnabled Ext_citations opts -> + case cs of + (d:_) + | isAlphaNum d || d == '_' + -> '\\':'@':escapeString opts cs + _ -> '@':escapeString opts cs _ | c `elem` ['\\','`','*','_','[',']','#'] -> '\\':c:escapeString opts cs '|' | isEnabled Ext_pipe_tables opts -> '\\':'|':escapeString opts cs -- cgit v1.2.3 From 377640402f32e8189fc88e54515c397ce48cc916 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Sat, 17 Feb 2018 23:06:54 -0800 Subject: LaTeX reader: Fixed comments inside citations. Closes #4374. --- src/Text/Pandoc/Readers/LaTeX.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index 1ce3d18e5..cb70b6403 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1074,12 +1074,12 @@ simpleCiteArgs = try $ do citationLabel :: PandocMonad m => LP m String citationLabel = do - optional sp + optional spaces toksToString <$> (many1 (satisfyTok isWordTok <|> symbolIn bibtexKeyChar) - <* optional sp + <* optional spaces <* optional (symbol ',') - <* optional sp) + <* optional spaces) where bibtexKeyChar = ".:;?!`'()/*@_+=-[]" :: [Char] cites :: PandocMonad m => CitationMode -> Bool -> LP m [Citation] -- cgit v1.2.3 From bb7681a85af76b2b85b263d593f560caf5428def Mon Sep 17 00:00:00 2001 From: Andrew Pritchard <andrewjpritchard@gmail.com> Date: Mon, 19 Feb 2018 03:12:58 +0800 Subject: EMF Image size support (#4375) --- src/Text/Pandoc/ImageSize.hs | 38 +++++++++++++++++++++++++++- src/Text/Pandoc/Writers/Docx.hs | 1 + src/Text/Pandoc/Writers/Powerpoint/Output.hs | 1 + 3 files changed, 39 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/ImageSize.hs b/src/Text/Pandoc/ImageSize.hs index 1d6db8dfa..4c76aac13 100644 --- a/src/Text/Pandoc/ImageSize.hs +++ b/src/Text/Pandoc/ImageSize.hs @@ -71,7 +71,7 @@ import Data.Maybe (fromMaybe) -- quick and dirty functions to get image sizes -- algorithms borrowed from wwwis.pl -data ImageType = Png | Gif | Jpeg | Svg | Pdf | Eps deriving Show +data ImageType = Png | Gif | Jpeg | Svg | Pdf | Eps | Emf deriving Show data Direction = Width | Height instance Show Direction where show Width = "width" @@ -125,6 +125,9 @@ imageType img = case B.take 4 img of "%!PS" | B.take 4 (B.drop 1 $ B.dropWhile (/=' ') img) == "EPSF" -> return Eps + "\x01\x00\x00\x00" + | B.take 4 (B.drop 40 img) == " EMF" + -> return Emf _ -> mzero findSvgTag :: ByteString -> Bool @@ -139,6 +142,7 @@ imageSize opts img = Just Svg -> mbToEither "could not determine SVG size" $ svgSize opts img Just Eps -> mbToEither "could not determine EPS size" $ epsSize img Just Pdf -> mbToEither "could not determine PDF size" $ pdfSize img + Just Emf -> mbToEither "could not determine EMF size" $ emfSize img Nothing -> Left "could not determine image type" where mbToEither msg Nothing = Left msg mbToEither _ (Just x) = Right x @@ -357,6 +361,38 @@ svgSize opts img = do , dpiX = dpi , dpiY = dpi } + +emfSize :: ByteString -> Maybe ImageSize +emfSize img = + let + parseheader = runGetOrFail $ do + skip 0x18 -- 0x00 + frameL <- getWord32le -- 0x18 measured in 1/100 of a millimetre + frameT <- getWord32le -- 0x1C + frameR <- getWord32le -- 0x20 + frameB <- getWord32le -- 0x24 + skip 0x20 -- 0x28 + deviceX <- getWord32le -- 0x48 pixels of reference device + deviceY <- getWord32le -- 0x4C + mmX <- getWord32le -- 0x50 real mm of reference device (always 320*240?) + mmY <- getWord32le -- 0x58 + -- end of header + let + w = (deviceX * (frameR - frameL)) `quot` (mmX * 100) + h = (deviceY * (frameB - frameT)) `quot` (mmY * 100) + dpiW = (deviceX * 254) `quot` (mmX * 10) + dpiH = (deviceY * 254) `quot` (mmY * 10) + return $ ImageSize + { pxX = fromIntegral w + , pxY = fromIntegral h + , dpiX = fromIntegral dpiW + , dpiY = fromIntegral dpiH + } + in + case parseheader . BL.fromStrict $ img of + Left _ -> Nothing + Right (_, _, size) -> Just size + jpegSize :: ByteString -> Either String ImageSize jpegSize img = diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 55588ba22..5ad6bf82b 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -1339,6 +1339,7 @@ inlineToOpenXML' opts (Image attr alt (src, title)) = do Just Pdf -> ".pdf" Just Eps -> ".eps" Just Svg -> ".svg" + Just Emf -> ".emf" Nothing -> "" if null imgext then -- without an extension there is no rule for content type diff --git a/src/Text/Pandoc/Writers/Powerpoint/Output.hs b/src/Text/Pandoc/Writers/Powerpoint/Output.hs index d30819d47..b41696043 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Output.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Output.hs @@ -475,6 +475,7 @@ registerMedia fp caption = do Just Pdf -> Just ".pdf" Just Eps -> Just ".eps" Just Svg -> Just ".svg" + Just Emf -> Just ".emf" Nothing -> Nothing let newGlobalId = case M.lookup fp globalIds of -- cgit v1.2.3 From 6fa6b6a5f26bc4b23501ff0f0cd6597524d48dd3 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sat, 17 Feb 2018 08:12:50 -0500 Subject: Powerpoint writer: Change references to Notes to SpeakerNotes This is to avoid confusion with {foot,end}notes. --- src/Text/Pandoc/Writers/Powerpoint/Presentation.hs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs index 0cf01ee01..e42a38dbe 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs @@ -37,7 +37,7 @@ module Text.Pandoc.Writers.Powerpoint.Presentation ( documentToPresentation , DocProps(..) , Slide(..) , Layout(..) - , Notes(..) + , SpeakerNotes(..) , SlideId(..) , Shape(..) , Graphic(..) @@ -180,7 +180,7 @@ data DocProps = DocProps { dcTitle :: Maybe String data Slide = Slide { slideId :: SlideId , slideLayout :: Layout - , slideNotes :: Maybe Notes + , slideSpeakerNotes :: Maybe SpeakerNotes } deriving (Show, Eq) newtype SlideId = SlideId String @@ -189,7 +189,7 @@ newtype SlideId = SlideId String -- In theory you could have anything on a notes slide but it seems -- designed mainly for one textbox, so we'll just put in the contents -- of that textbox, to avoid other shapes that won't work as well. -newtype Notes = Notes [Paragraph] +newtype SpeakerNotes = SpeakerNotes {fromSpeakerNotes :: [Paragraph]} deriving (Show, Eq) data Layout = MetadataSlide { metadataSlideTitle :: [ParaElem] @@ -806,11 +806,11 @@ applyToLayout f (TwoColumnSlide hdr contentL contentR) = do applyToSlide :: Monad m => (ParaElem -> m ParaElem) -> Slide -> m Slide applyToSlide f slide = do layout' <- applyToLayout f $ slideLayout slide - mbNotes' <- case slideNotes slide of - Just (Notes notes) -> (Just . Notes) <$> - mapM (applyToParagraph f) notes + mbNotes' <- case slideSpeakerNotes slide of + Just (SpeakerNotes notes) -> (Just . SpeakerNotes) <$> + mapM (applyToParagraph f) notes Nothing -> return Nothing - return slide{slideLayout = layout', slideNotes = mbNotes'} + return slide{slideLayout = layout', slideSpeakerNotes = mbNotes'} replaceAnchor :: ParaElem -> Pres ParaElem replaceAnchor (Run rProps s) -- cgit v1.2.3 From d9e179d6fd68b3da16cd0ccf22860ffe54184a2f Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sat, 17 Feb 2018 08:51:43 -0500 Subject: Powerpoint writer: Read notes into powerpoint Presentatation type. We record notes in a map in state while processing. --- src/Text/Pandoc/Writers/Powerpoint/Presentation.hs | 23 +++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs index e42a38dbe..2ba74f4ec 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs @@ -108,7 +108,7 @@ data WriterState = WriterState { stNoteIds :: M.Map Int [Block] , stAnchorMap :: M.Map String SlideId , stSlideIdSet :: S.Set SlideId , stLog :: [LogMessage] - + , stSpeakerNotesMap :: M.Map SlideId [[Paragraph]] } deriving (Show, Eq) instance Default WriterState where @@ -117,6 +117,7 @@ instance Default WriterState where -- we reserve this s , stSlideIdSet = reservedSlideIds , stLog = [] + , stSpeakerNotesMap = mempty } metadataSlideId :: SlideId @@ -463,7 +464,15 @@ blockToParagraphs (DefinitionList entries) = do definition <- concatMapM (blockToParagraphs . BlockQuote) blksLst return $ term ++ definition concatMapM go entries -blockToParagraphs (Div (_, "notes" : [], _) _) = return [] +blockToParagraphs (Div (_, "notes" : [], _) blks) = do + sldId <- asks envCurSlideId + spkNotesMap <- gets stSpeakerNotesMap + paras <- concatMapM blockToParagraphs blks + let spkNotesMap' = case M.lookup sldId spkNotesMap of + Just lst -> M.insert sldId (paras : lst) spkNotesMap + Nothing -> M.insert sldId [paras] spkNotesMap + modify $ \st -> st{stSpeakerNotesMap = spkNotesMap'} + return [] blockToParagraphs (Div _ blks) = concatMapM blockToParagraphs blks blockToParagraphs blk = do addLogMessage $ BlockNotRendered blk @@ -593,6 +602,12 @@ splitBlocks' cur acc (blk : blks) = splitBlocks' (cur ++ [blk]) acc blks splitBlocks :: [Block] -> Pres [[Block]] splitBlocks = splitBlocks' [] [] +getSpeakerNotes :: Pres (Maybe SpeakerNotes) +getSpeakerNotes = do + sldId <- asks envCurSlideId + spkNtsMap <- gets stSpeakerNotesMap + return $ (SpeakerNotes . concat . reverse) <$> (M.lookup sldId spkNtsMap) + blocksToSlide' :: Int -> [Block] -> Pres Slide blocksToSlide' lvl (Header n (ident, _, _) ils : blks) | n < lvl = do @@ -664,7 +679,9 @@ blocksToSlide' _ [] = do blocksToSlide :: [Block] -> Pres Slide blocksToSlide blks = do slideLevel <- asks envSlideLevel - blocksToSlide' slideLevel blks + sld <- blocksToSlide' slideLevel blks + spkNotes <- getSpeakerNotes + return $ sld{slideSpeakerNotes = spkNotes} makeNoteEntry :: Int -> [Block] -> [Block] makeNoteEntry n blks = -- cgit v1.2.3 From 6c6ac9f22e746f4883412013f6369bf985831e91 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sat, 17 Feb 2018 09:40:39 -0500 Subject: Powerpoint writer: Move notes slides into data tree. --- src/Text/Pandoc/Class.hs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Class.hs b/src/Text/Pandoc/Class.hs index ae538046a..aa0379942 100644 --- a/src/Text/Pandoc/Class.hs +++ b/src/Text/Pandoc/Class.hs @@ -718,6 +718,14 @@ getDefaultReferencePptx = do , "ppt/tableStyles.xml" , "ppt/theme/theme1.xml" , "ppt/viewProps.xml" + -- These relate to notes slides. + , "ppt/notesMasters/notesMaster1.xml" + , "ppt/notesMasters/_rels/notesMaster1.xml.rels" + , "ppt/notesSlides/notesSlide1.xml" + , "ppt/notesSlides/_rels/notesSlide1.xml.rels" + , "ppt/notesSlides/notesSlide2.xml" + , "ppt/notesSlides/_rels/notesSlide2.xml.rels" + , "ppt/theme/theme2.xml" ] let toLazy = BL.fromChunks . (:[]) let pathToEntry path = do -- cgit v1.2.3 From eace2357dde61bebbd7b93d6f4073620353a384a Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sat, 17 Feb 2018 10:01:12 -0500 Subject: Powerpoint writer: Read speaker note templates conditionally If there are speaker notes in the presentation, we read in the notesMasters templates from the reference pptx file. --- src/Text/Pandoc/Writers/Powerpoint/Output.hs | 56 +++++++++++++++++++--------- 1 file changed, 38 insertions(+), 18 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint/Output.hs b/src/Text/Pandoc/Writers/Powerpoint/Output.hs index b41696043..12b338b1c 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Output.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Output.hs @@ -56,7 +56,7 @@ import Text.Pandoc.MIME import qualified Data.ByteString.Lazy as BL import Text.Pandoc.Writers.OOXML import qualified Data.Map as M -import Data.Maybe (mapMaybe, listToMaybe, fromMaybe) +import Data.Maybe (mapMaybe, listToMaybe, fromMaybe, isJust) import Text.Pandoc.ImageSize import Control.Applicative ((<|>)) import System.FilePath.Glob @@ -167,19 +167,35 @@ copyFileToArchive arch fp = do Nothing -> fail $ fp ++ " missing in reference file" Just e -> return $ addEntryToArchive e arch -inheritedPatterns :: [Pattern] -inheritedPatterns = map compile [ "docProps/app.xml" - , "ppt/slideLayouts/slideLayout*.xml" - , "ppt/slideLayouts/_rels/slideLayout*.xml.rels" - , "ppt/slideMasters/slideMaster1.xml" - , "ppt/slideMasters/_rels/slideMaster1.xml.rels" - , "ppt/theme/theme1.xml" - , "ppt/theme/_rels/theme1.xml.rels" - , "ppt/presProps.xml" - , "ppt/viewProps.xml" - , "ppt/tableStyles.xml" - , "ppt/media/image*" - ] +alwaysInheritedPatterns :: [Pattern] +alwaysInheritedPatterns = + map compile [ "docProps/app.xml" + , "ppt/slideLayouts/slideLayout*.xml" + , "ppt/slideLayouts/_rels/slideLayout*.xml.rels" + , "ppt/slideMasters/slideMaster1.xml" + , "ppt/slideMasters/_rels/slideMaster1.xml.rels" + , "ppt/theme/theme1.xml" + , "ppt/theme/_rels/theme1.xml.rels" + , "ppt/presProps.xml" + , "ppt/viewProps.xml" + , "ppt/tableStyles.xml" + , "ppt/media/image*" + ] + +-- We only look for these under special conditions +contingentInheritedPatterns :: Presentation -> [Pattern] +contingentInheritedPatterns pres = [] ++ + if hasSpeakerNotes pres + then map compile [ "ppt/notesMasters/notesMaster*.xml" + , "ppt/notesMasters/_rels/notesMaster*.xml.rels" + , "ppt/theme/theme2.xml" + , "ppt/theme/_rels/theme2.xml.rels" + ] + else [] + +inheritedPatterns :: Presentation -> [Pattern] +inheritedPatterns pres = + alwaysInheritedPatterns ++ contingentInheritedPatterns pres patternToFilePaths :: PandocMonad m => Pattern -> P m [FilePath] patternToFilePaths pat = do @@ -212,10 +228,9 @@ requiredFiles = [ "docProps/app.xml" , "ppt/tableStyles.xml" ] - presentationToArchiveP :: PandocMonad m => Presentation -> P m Archive presentationToArchiveP p@(Presentation docProps slides) = do - filePaths <- patternsToFilePaths inheritedPatterns + filePaths <- patternsToFilePaths $ inheritedPatterns p -- make sure all required files are available: let missingFiles = filter (\fp -> not (fp `elem` filePaths)) requiredFiles @@ -287,6 +302,11 @@ presentationToArchive opts pres = do -------------------------------------------------- +-- Check to see if the presentation has speaker notes. This will +-- influence whether we import the notesMaster template. +hasSpeakerNotes :: Presentation -> Bool +hasSpeakerNotes (Presentation _ slides) = any isJust $ map slideSpeakerNotes slides + -------------------------------------------------- getLayout :: PandocMonad m => Layout -> P m Element @@ -1422,9 +1442,9 @@ mediaContentType mInfo | otherwise = Nothing presentationToContentTypes :: PandocMonad m => Presentation -> P m ContentTypes -presentationToContentTypes (Presentation _ slides) = do +presentationToContentTypes p@(Presentation _ slides) = do mediaInfos <- (mconcat . M.elems) <$> gets stMediaIds - filePaths <- patternsToFilePaths inheritedPatterns + filePaths <- patternsToFilePaths $ inheritedPatterns p let mediaFps = filter (match (compile "ppt/media/image*")) filePaths let defaults = [ DefaultContentType "xml" "application/xml" , DefaultContentType "rels" "application/vnd.openxmlformats-package.relationships+xml" -- cgit v1.2.3 From 47a399303dbef997450f4a07b4f7c8b20bf6fb66 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sat, 17 Feb 2018 14:46:42 -0500 Subject: Powerpoint writer: modify speaker notes in presentation.xml We remove the `notesMasterIdLst` entry in `presentation.xml` if there no speaker notes in the presentation. --- src/Text/Pandoc/Writers/Powerpoint/Output.hs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint/Output.hs b/src/Text/Pandoc/Writers/Powerpoint/Output.hs index 12b338b1c..1ed021086 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Output.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Output.hs @@ -1340,7 +1340,18 @@ presentationToPresentationElement pres = do _ -> Elem e modifySldIdLst ct = ct - newContent = map modifySldIdLst $ elContent element + removeSpeakerNotes' :: Content -> [Content] + removeSpeakerNotes' (Elem e) = case elName e of + (QName "notesMasterIdLst" _ _) -> [] + _ -> [Elem e] + removeSpeakerNotes' ct = [ct] + + removeSpeakerNotes :: [Content] -> [Content] + removeSpeakerNotes = if not (hasSpeakerNotes pres) + then concatMap removeSpeakerNotes' + else id + + newContent = removeSpeakerNotes $ map modifySldIdLst $ elContent element return $ element{elContent = newContent} -- cgit v1.2.3 From 575a360c6c1c49f6ce04b6dbde0ed167d40b9f48 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sat, 17 Feb 2018 15:57:40 -0500 Subject: Powerpoint writer: Output speaker notes. There are a number of interlocking parts here. The main thing to note is that, to match the MSPowerPoint-generated pptx files, we only include the notesMaster and notesSlide files if there are notes. This means we have to be careful with the rIds, and build a number of files conditionally. --- src/Text/Pandoc/Writers/Powerpoint/Output.hs | 312 ++++++++++++++++++++++++--- 1 file changed, 287 insertions(+), 25 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint/Output.hs b/src/Text/Pandoc/Writers/Powerpoint/Output.hs index 1ed021086..801e0485e 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Output.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Output.hs @@ -56,7 +56,7 @@ import Text.Pandoc.MIME import qualified Data.ByteString.Lazy as BL import Text.Pandoc.Writers.OOXML import qualified Data.Map as M -import Data.Maybe (mapMaybe, listToMaybe, fromMaybe, isJust) +import Data.Maybe (mapMaybe, listToMaybe, fromMaybe, isJust, maybeToList, catMaybes) import Text.Pandoc.ImageSize import Control.Applicative ((<|>)) import System.FilePath.Glob @@ -109,6 +109,11 @@ data WriterEnv = WriterEnv { envRefArchive :: Archive , envSlideIdOffset :: Int , envContentType :: ContentType , envSlideIdMap :: M.Map SlideId Int + -- maps the slide number to the + -- corresponding notes id number. If there + -- are no notes for a slide, there will be + -- no entry in the map for it. + , envSpeakerNotesIdMap :: M.Map Int Int } deriving (Show) @@ -125,6 +130,7 @@ instance Default WriterEnv where , envSlideIdOffset = 1 , envContentType = NormalContent , envSlideIdMap = mempty + , envSpeakerNotesIdMap = mempty } data ContentType = NormalContent @@ -185,7 +191,7 @@ alwaysInheritedPatterns = -- We only look for these under special conditions contingentInheritedPatterns :: Presentation -> [Pattern] contingentInheritedPatterns pres = [] ++ - if hasSpeakerNotes pres + if presHasSpeakerNotes pres then map compile [ "ppt/notesMasters/notesMaster*.xml" , "ppt/notesMasters/_rels/notesMaster*.xml.rels" , "ppt/theme/theme2.xml" @@ -253,6 +259,8 @@ presentationToArchiveP p@(Presentation docProps slides) = do presRelsEntry <- presentationToRelsEntry p slideEntries <- mapM slideToEntry slides slideRelEntries <- mapM slideToSlideRelEntry slides + spkNotesEntries <- catMaybes <$> mapM slideToSpeakerNotesEntry slides + spkNotesRelEntries <- catMaybes <$> mapM slideToSpeakerNotesRelEntry slides -- These have to come after everything, because they need the info -- built up in the state. mediaEntries <- makeMediaEntries @@ -261,6 +269,8 @@ presentationToArchiveP p@(Presentation docProps slides) = do return $ foldr addEntryToArchive newArch' $ slideEntries ++ slideRelEntries ++ + spkNotesEntries ++ + spkNotesRelEntries ++ mediaEntries ++ [contentTypesEntry, docPropsEntry, relsEntry, presEntry, presRelsEntry] @@ -268,6 +278,12 @@ makeSlideIdMap :: Presentation -> M.Map SlideId Int makeSlideIdMap (Presentation _ slides) = M.fromList $ (map slideId slides) `zip` [1..] +makeSpeakerNotesMap :: Presentation -> M.Map Int Int +makeSpeakerNotesMap (Presentation _ slides) = + M.fromList $ (mapMaybe f $ slides `zip` [1..]) `zip` [1..] + where f (Slide _ _ Nothing, _) = Nothing + f (Slide _ _ (Just _), n) = Just n + presentationToArchive :: PandocMonad m => WriterOptions -> Presentation -> m Archive presentationToArchive opts pres = do distArchive <- (toArchive . BL.fromStrict) <$> @@ -291,6 +307,7 @@ presentationToArchive opts pres = do , envOpts = opts , envPresentationSize = presSize , envSlideIdMap = makeSlideIdMap pres + , envSpeakerNotesIdMap = makeSpeakerNotesMap pres } let st = def { stMediaGlobalIds = initialGlobalIds refArchive distArchive @@ -304,8 +321,14 @@ presentationToArchive opts pres = do -- Check to see if the presentation has speaker notes. This will -- influence whether we import the notesMaster template. -hasSpeakerNotes :: Presentation -> Bool -hasSpeakerNotes (Presentation _ slides) = any isJust $ map slideSpeakerNotes slides +presHasSpeakerNotes :: Presentation -> Bool +presHasSpeakerNotes (Presentation _ slides) = any isJust $ map slideSpeakerNotes slides + +curSlideHasSpeakerNotes :: PandocMonad m => P m Bool +curSlideHasSpeakerNotes = do + sldId <- asks envCurSlideId + notesIdMap <- asks envSpeakerNotesIdMap + return $ isJust $ M.lookup sldId notesIdMap -------------------------------------------------- @@ -448,15 +471,16 @@ registerLink link = do curSlideId <- asks envCurSlideId linkReg <- gets stLinkIds mediaReg <- gets stMediaIds + hasSpeakerNotes <- curSlideHasSpeakerNotes let maxLinkId = case M.lookup curSlideId linkReg of Just mp -> case M.keys mp of - [] -> 1 + [] -> if hasSpeakerNotes then 2 else 1 ks -> maximum ks - Nothing -> 1 + Nothing -> if hasSpeakerNotes then 2 else 1 maxMediaId = case M.lookup curSlideId mediaReg of - Just [] -> 1 + Just [] -> if hasSpeakerNotes then 2 else 1 Just mInfos -> maximum $ map mInfoLocalId mInfos - Nothing -> 1 + Nothing -> if hasSpeakerNotes then 2 else 1 maxId = max maxLinkId maxMediaId slideLinks = case M.lookup curSlideId linkReg of Just mp -> M.insert (maxId + 1) link mp @@ -470,15 +494,16 @@ registerMedia fp caption = do linkReg <- gets stLinkIds mediaReg <- gets stMediaIds globalIds <- gets stMediaGlobalIds + hasSpeakerNotes <- curSlideHasSpeakerNotes let maxLinkId = case M.lookup curSlideId linkReg of Just mp -> case M.keys mp of - [] -> 1 + [] -> if hasSpeakerNotes then 2 else 1 ks -> maximum ks - Nothing -> 1 + Nothing -> if hasSpeakerNotes then 2 else 1 maxMediaId = case M.lookup curSlideId mediaReg of - Just [] -> 1 + Just [] -> if hasSpeakerNotes then 2 else 1 Just mInfos -> maximum $ map mInfoLocalId mInfos - Nothing -> 1 + Nothing -> if hasSpeakerNotes then 2 else 1 maxLocalId = max maxLinkId maxMediaId maxGlobalId = case M.elems globalIds of @@ -973,6 +998,21 @@ getShapeByName ns spTreeElem name filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasName ns name e)) spTreeElem | otherwise = Nothing + + +getShapeByPlaceHolderType :: NameSpaces -> Element -> String -> Maybe Element +getShapeByPlaceHolderType ns spTreeElem phType + | isElem ns "p" "spTree" spTreeElem = + let findPhType element = isElem ns "p" "sp" element && + Just phType == (Just element >>= + findChild (elemName ns "p" "nvSpPr") >>= + findChild (elemName ns "p" "nvPr") >>= + findChild (elemName ns "p" "ph") >>= + findAttr (QName "type" Nothing Nothing)) + in + filterChild findPhType spTreeElem + | otherwise = Nothing + -- getShapeById :: NameSpaces -> Element -> String -> Maybe Element -- getShapeById ns spTreeElem ident -- | isElem ns "p" "spTree" spTreeElem = @@ -1109,6 +1149,148 @@ slideToElement (Slide _ l@(MetadataSlide titleElems subtitleElems authorElems da ("xmlns:p", "http://schemas.openxmlformats.org/presentationml/2006/main") ] [mknode "p:cSld" [] [spTree]] + +-------------------------------------------------------------------- +-- Notes: + +getNotesMaster :: PandocMonad m => P m Element +getNotesMaster = do + let notesMasterPath = "ppt/notesMasters/notesMaster1.xml" + distArchive <- asks envDistArchive + root <- case findEntryByPath notesMasterPath distArchive of + Just e -> case parseXMLDoc $ UTF8.toStringLazy $ fromEntry e of + Just element -> return $ element + Nothing -> throwError $ + PandocSomeError $ + notesMasterPath ++ " corrupt in reference file" + Nothing -> throwError $ + PandocSomeError $ + notesMasterPath ++ " missing in reference file" + return root + +getSlideNumberFieldId :: PandocMonad m => Element -> P m String +getSlideNumberFieldId notesMaster + | ns <- elemToNameSpaces notesMaster + , Just cSld <- findChild (elemName ns "p" "cSld") notesMaster + , Just spTree <- findChild (elemName ns "p" "spTree") cSld + , Just sp <- getShapeByPlaceHolderType ns spTree "sldNum" + , Just txBody <- findChild (elemName ns "p" "txBody") sp + , Just p <- findChild (elemName ns "a" "p") txBody + , Just fld <- findChild (elemName ns "a" "fld") p + , Just fldId <- findAttr (QName "id" Nothing Nothing) fld = + return fldId + | otherwise = throwError $ + PandocSomeError $ + "No field id for slide numbers in notesMaster.xml" + +speakerNotesSlideImage :: Element +speakerNotesSlideImage = + mknode "p:sp" [] $ + [ mknode "p:nvSpPr" [] $ + [ mknode "p:cNvPr" [ ("id", "2") + , ("name", "Slide Image Placeholder 1") + ] () + , mknode "p:cNvSpPr" [] $ + [ mknode "a:spLocks" [ ("noGrp", "1") + , ("noRot", "1") + , ("noChangeAspect", "1") + ] () + ] + , mknode "p:nvPr" [] $ + [ mknode "p:ph" [("type", "sldImg")] ()] + ] + , mknode "p:spPr" [] () + ] + +speakerNotesBody :: PandocMonad m => [Paragraph] -> P m Element +speakerNotesBody paras = do + elements <- mapM paragraphToElement paras + let txBody = mknode "p:txBody" [] $ + [mknode "a:bodyPr" [] (), mknode "a:lstStyle" [] ()] ++ elements + return $ + mknode "p:sp" [] $ + [ mknode "p:nvSpPr" [] $ + [ mknode "p:cNvPr" [ ("id", "3") + , ("name", "Notes Placeholder 2") + ] () + , mknode "p:cNvSpPr" [] $ + [ mknode "a:spLocks" [("noGrp", "1")] ()] + , mknode "p:nvPr" [] $ + [ mknode "p:ph" [("type", "body"), ("idx", "1")] ()] + ] + , mknode "p:spPr" [] () + , txBody + ] + +speakerNotesSlideNumber :: Int -> String -> Element +speakerNotesSlideNumber pgNum fieldId = + mknode "p:sp" [] $ + [ mknode "p:nvSpPr" [] $ + [ mknode "p:cNvPr" [ ("id", "4") + , ("name", "Slide Number Placeholder 3") + ] () + , mknode "p:cNvSpPr" [] $ + [ mknode "a:spLocks" [("noGrp", "1")] ()] + , mknode "p:nvPr" [] $ + [ mknode "p:ph" [ ("type", "sldNum") + , ("sz", "quarter") + , ("idx", "10") + ] () + ] + ] + , mknode "p:spPr" [] () + , mknode "p:txBody" [] $ + [ mknode "a:bodyPr" [] () + , mknode "a:lstStyle" [] () + , mknode "a:p" [] $ + [ mknode "a:fld" [ ("id", fieldId) + , ("type", "slidenum") + ] + [ mknode "a:rPr" [("lang", "en-US")] () + , mknode "a:t" [] (show pgNum) + ] + , mknode "a:endParaRPr" [("lang", "en-US")] () + ] + ] + ] + +slideToSpeakerNotesElement :: PandocMonad m => Slide -> P m (Maybe Element) +slideToSpeakerNotesElement sld@(Slide _ _ mbNotes) + | Nothing <- mbNotes = return Nothing + | Just (SpeakerNotes paras) <- mbNotes = do + master <- getNotesMaster + fieldId <- getSlideNumberFieldId master + num <- slideNum sld + let imgShape = speakerNotesSlideImage + sldNumShape = speakerNotesSlideNumber num fieldId + bodyShape <- speakerNotesBody paras + return $ Just $ + mknode "p:notes" + [ ("xmlns:a", "http://schemas.openxmlformats.org/drawingml/2006/main") + , ("xmlns:r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships") + , ("xmlns:p", "http://schemas.openxmlformats.org/presentationml/2006/main") + ] [ mknode "p:cSld" [] + [ mknode "p:spTree" [] + [ mknode "p:nvGrpSpPr" [] + [ mknode "p:cNvPr" [("id", "1"), ("name", "")] () + , mknode "p:cNvGrpSpPr" [] () + , mknode "p:nvPr" [] () + ] + , mknode "p:grpSpPr" [] + [ mknode "a:xfrm" [] + [ mknode "a:off" [("x", "0"), ("y", "0")] () + , mknode "a:ext" [("cx", "0"), ("cy", "0")] () + , mknode "a:chOff" [("x", "0"), ("y", "0")] () + , mknode "a:chExt" [("cx", "0"), ("cy", "0")] () + ] + ] + , imgShape + , bodyShape + , sldNumShape + ] + ] + ] + ----------------------------------------------------------------------- getSlideIdNum :: PandocMonad m => SlideId -> P m Int @@ -1252,6 +1434,53 @@ slideToEntry slide = do element <- slideToElement slide elemToEntry ("ppt/slides/" ++ idNumToFilePath idNum) element +slideToSpeakerNotesEntry :: PandocMonad m => Slide -> P m (Maybe Entry) +slideToSpeakerNotesEntry slide = do + idNum <- slideNum slide + local (\env -> env{envCurSlideId = idNum}) $ do + mbElement <- slideToSpeakerNotesElement slide + mbNotesIdNum <- do mp <- asks envSpeakerNotesIdMap + return $ M.lookup idNum mp + case mbElement of + Just element | Just notesIdNum <- mbNotesIdNum -> + Just <$> + elemToEntry + ("ppt/notesSlides/notesSlide" ++ show notesIdNum ++ ".xml") + element + _ -> return Nothing + +slideToSpeakerNotesRelElement :: PandocMonad m => Slide -> P m (Maybe Element) +slideToSpeakerNotesRelElement slide@(Slide _ _ mbNotes) + | Nothing <- mbNotes = return Nothing + | Just _ <- mbNotes = do + idNum <- slideNum slide + return $ Just $ + mknode "Relationships" + [("xmlns", "http://schemas.openxmlformats.org/package/2006/relationships")] + [ mknode "Relationship" [ ("Id", "rId2") + , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide") + , ("Target", "../slides/slide" ++ show idNum ++ ".xml") + ] () + , mknode "Relationship" [ ("Id", "rId1") + , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesMaster") + , ("Target", "../notesMasters/notesMaster1.xml") + ] () + ] + +slideToSpeakerNotesRelEntry :: PandocMonad m => Slide -> P m (Maybe Entry) +slideToSpeakerNotesRelEntry slide = do + idNum <- slideNum slide + mbElement <- slideToSpeakerNotesRelElement slide + mp <- asks envSpeakerNotesIdMap + let mbNotesIdNum = M.lookup idNum mp + case mbElement of + Just element | Just notesIdNum <- mbNotesIdNum -> + Just <$> + elemToEntry + ("ppt/notesSlides/_rels/notesSlide" ++ show notesIdNum ++ ".xml.rels") + element + _ -> return Nothing + slideToSlideRelEntry :: PandocMonad m => Slide -> P m Entry slideToSlideRelEntry slide = do idNum <- slideNum slide @@ -1288,6 +1517,20 @@ mediaRelElement mInfo = , ("Target", "../media/image" ++ (show $ mInfoGlobalId mInfo) ++ ext) ] () +speakerNotesSlideRelElement :: PandocMonad m => Slide -> P m (Maybe Element) +speakerNotesSlideRelElement slide = do + idNum <- slideNum slide + mp <- asks envSpeakerNotesIdMap + return $ case M.lookup idNum mp of + Nothing -> Nothing + Just n -> + let target = "../notesSlides/notesSlide" ++ show n ++ ".xml" + in Just $ + mknode "Relationship" [ ("Id", "rId2") + , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide") + , ("Target", target) + ] () + slideToSlideRelElement :: PandocMonad m => Slide -> P m Element slideToSlideRelElement slide = do idNum <- slideNum slide @@ -1297,6 +1540,8 @@ slideToSlideRelElement slide = do (Slide _ (ContentSlide _ _) _) -> "../slideLayouts/slideLayout2.xml" (Slide _ (TwoColumnSlide _ _ _) _) -> "../slideLayouts/slideLayout4.xml" + speakerNotesRels <- maybeToList <$> speakerNotesSlideRelElement slide + linkIds <- gets stLinkIds mediaIds <- gets stMediaIds @@ -1313,7 +1558,7 @@ slideToSlideRelElement slide = do ([mknode "Relationship" [ ("Id", "rId1") , ("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout") , ("Target", target)] () - ] ++ linkRels ++ mediaRels) + ] ++ speakerNotesRels ++ linkRels ++ mediaRels) slideToSldIdElement :: PandocMonad m => Slide -> P m Element slideToSldIdElement slide = do @@ -1328,7 +1573,7 @@ presentationToSldIdLst (Presentation _ slides) = do return $ mknode "p:sldIdLst" [] ids presentationToPresentationElement :: PandocMonad m => Presentation -> P m Element -presentationToPresentationElement pres = do +presentationToPresentationElement pres@(Presentation _ slds) = do refArchive <- asks envRefArchive distArchive <- asks envDistArchive element <- parseXml refArchive distArchive "ppt/presentation.xml" @@ -1340,18 +1585,28 @@ presentationToPresentationElement pres = do _ -> Elem e modifySldIdLst ct = ct - removeSpeakerNotes' :: Content -> [Content] - removeSpeakerNotes' (Elem e) = case elName e of - (QName "notesMasterIdLst" _ _) -> [] - _ -> [Elem e] - removeSpeakerNotes' ct = [ct] + notesMasterRId = length slds + 2 + + modifySpeakerNotes' :: Content -> [Content] + modifySpeakerNotes' (Elem e) = case elName e of + (QName "notesMasterIdLst" _ _) -> + if presHasSpeakerNotes pres + then [Elem $ + mknode "p:notesMasterIdLst" [] + [ mknode + "p:NotesMasterId" + [("r:id", "rId" ++ show notesMasterRId)] + () + ] + ] + else [] + _ -> [Elem e] + modifySpeakerNotes' ct = [ct] - removeSpeakerNotes :: [Content] -> [Content] - removeSpeakerNotes = if not (hasSpeakerNotes pres) - then concatMap removeSpeakerNotes' - else id + modifySpeakerNotes :: [Content] -> [Content] + modifySpeakerNotes = concatMap modifySpeakerNotes' - newContent = removeSpeakerNotes $ map modifySldIdLst $ elContent element + newContent = modifySpeakerNotes $ map modifySldIdLst $ elContent element return $ element{elContent = newContent} @@ -1452,6 +1707,12 @@ mediaContentType mInfo } | otherwise = Nothing +getSpeakerNotesFilePaths :: PandocMonad m => P m [FilePath] +getSpeakerNotesFilePaths = do + mp <- asks envSpeakerNotesIdMap + let notesIdNums = M.elems mp + return $ map (\n -> "ppt/notesSlides/notesSlide" ++ show n ++ ".xml") notesIdNums + presentationToContentTypes :: PandocMonad m => Presentation -> P m ContentTypes presentationToContentTypes p@(Presentation _ slides) = do mediaInfos <- (mconcat . M.elems) <$> gets stMediaIds @@ -1471,9 +1732,10 @@ presentationToContentTypes p@(Presentation _ slides) = do let slideOverrides = mapMaybe (\fp -> pathToOverride $ "ppt/slides/" ++ fp) relativePaths + speakerNotesOverrides <- (mapMaybe pathToOverride) <$> getSpeakerNotesFilePaths return $ ContentTypes (defaults ++ mediaDefaults) - (inheritedOverrides ++ docPropsOverride ++ presOverride ++ slideOverrides) + (inheritedOverrides ++ docPropsOverride ++ presOverride ++ slideOverrides ++ speakerNotesOverrides) presML :: String presML = "application/vnd.openxmlformats-officedocument.presentationml" -- cgit v1.2.3 From 07f3aa178be1dda44cd5477089d26c26b9460751 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sun, 18 Feb 2018 15:58:06 -0500 Subject: Powerpoint writer: Ignore links and (end)notes in speaker notes. MS PowerPoint does not offer a way to insert links into speaker notes text, so we match that behavior, and make our lives easier. As for (end)notes, there is no clear solution to the question of wat that would *mean*. The default behavior would be to add it to the endnote slide, but that would put speaker note content into the public presentation. The best solution would be to put the content at the bottom of the notes page, but that would take some doing, and can be added to the speaker notes feature later. --- src/Text/Pandoc/Writers/Powerpoint/Output.hs | 10 +++++++- src/Text/Pandoc/Writers/Powerpoint/Presentation.hs | 30 +++++++++++++++------- 2 files changed, 30 insertions(+), 10 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint/Output.hs b/src/Text/Pandoc/Writers/Powerpoint/Output.hs index 801e0485e..93d511dce 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Output.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Output.hs @@ -1202,9 +1202,17 @@ speakerNotesSlideImage = , mknode "p:spPr" [] () ] +-- we want to wipe links from the speaker notes in the +-- paragraphs. Powerpoint doesn't allow you to input them, and it +-- would provide extra complications. +removeLinks :: Paragraph -> Paragraph +removeLinks paragraph = paragraph{paraElems = map f (paraElems paragraph)} + where f (Run rProps s) = Run rProps{rLink=Nothing} s + f pe = pe + speakerNotesBody :: PandocMonad m => [Paragraph] -> P m Element speakerNotesBody paras = do - elements <- mapM paragraphToElement paras + elements <- mapM paragraphToElement $ map removeLinks paras let txBody = mknode "p:txBody" [] $ [mknode "a:bodyPr" [] (), mknode "a:lstStyle" [] ()] ++ elements return $ diff --git a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs index 2ba74f4ec..ac7c86945 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs @@ -87,6 +87,7 @@ data WriterEnv = WriterEnv { envMetadata :: Meta , envInList :: Bool , envInNoteSlide :: Bool , envCurSlideId :: SlideId + , envInSpeakerNotes :: Bool } deriving (Show) @@ -100,6 +101,7 @@ instance Default WriterEnv where , envInList = False , envInNoteSlide = False , envCurSlideId = SlideId "Default" + , envInSpeakerNotes = False } @@ -354,15 +356,24 @@ inlineToParElems (Code _ str) = inlineToParElems $ Str str inlineToParElems (Math mathtype str) = return [MathElem mathtype (TeXString str)] +-- We ignore notes if we're in a speaker notes div. Otherwise this +-- would add an entry to the endnotes slide, which would put speaker +-- notes in the public presentation. In the future, we can entertain a +-- way of adding a speakernotes-specific note that would just add +-- paragraphs to the bottom of the notes page. inlineToParElems (Note blks) = do - notes <- gets stNoteIds - let maxNoteId = case M.keys notes of - [] -> 0 - lst -> maximum lst - curNoteId = maxNoteId + 1 - modify $ \st -> st { stNoteIds = M.insert curNoteId blks notes } - local (\env -> env{envRunProps = (envRunProps env){rLink = Just $ InternalTarget endNotesSlideId}}) $ - inlineToParElems $ Superscript [Str $ show curNoteId] + inSpNotes <- asks envInSpeakerNotes + if inSpNotes + then return [] + else do + notes <- gets stNoteIds + let maxNoteId = case M.keys notes of + [] -> 0 + lst -> maximum lst + curNoteId = maxNoteId + 1 + modify $ \st -> st { stNoteIds = M.insert curNoteId blks notes } + local (\env -> env{envRunProps = (envRunProps env){rLink = Just $ InternalTarget endNotesSlideId}}) $ + inlineToParElems $ Superscript [Str $ show curNoteId] inlineToParElems (Span _ ils) = concatMapM inlineToParElems ils inlineToParElems (RawInline _ _) = return [] inlineToParElems _ = return [] @@ -464,7 +475,8 @@ blockToParagraphs (DefinitionList entries) = do definition <- concatMapM (blockToParagraphs . BlockQuote) blksLst return $ term ++ definition concatMapM go entries -blockToParagraphs (Div (_, "notes" : [], _) blks) = do +blockToParagraphs (Div (_, "notes" : [], _) blks) = + local (\env -> env{envInSpeakerNotes=True}) $ do sldId <- asks envCurSlideId spkNotesMap <- gets stSpeakerNotesMap paras <- concatMapM blockToParagraphs blks -- cgit v1.2.3 From 6562863ef1eae9796b89b9b995e63ad4e75359fb Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sun, 18 Feb 2018 16:06:58 -0500 Subject: Powerpoint writer: separate SpeakerNotes paragraphs with empy par This will add a space between notes paragraphs, which seems more like what most users would do by hand (press "enter" twice). --- src/Text/Pandoc/Writers/Powerpoint/Output.hs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint/Output.hs b/src/Text/Pandoc/Writers/Powerpoint/Output.hs index 93d511dce..bc9c3193a 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Output.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Output.hs @@ -39,7 +39,7 @@ import Control.Monad.Reader import Control.Monad.State import Codec.Archive.Zip import Data.Char (toUpper) -import Data.List (intercalate, stripPrefix, nub, union, isPrefixOf) +import Data.List (intercalate, stripPrefix, nub, union, isPrefixOf, intersperse) import Data.Default import Text.Pandoc.Compat.Time (formatTime, defaultTimeLocale) import Data.Time.Clock (UTCTime) @@ -1205,14 +1205,18 @@ speakerNotesSlideImage = -- we want to wipe links from the speaker notes in the -- paragraphs. Powerpoint doesn't allow you to input them, and it -- would provide extra complications. -removeLinks :: Paragraph -> Paragraph -removeLinks paragraph = paragraph{paraElems = map f (paraElems paragraph)} +removeParaLinks :: Paragraph -> Paragraph +removeParaLinks paragraph = paragraph{paraElems = map f (paraElems paragraph)} where f (Run rProps s) = Run rProps{rLink=Nothing} s f pe = pe +-- put an empty paragraph between paragraphs for more expected spacing. +spaceParas :: [Paragraph] -> [Paragraph] +spaceParas = intersperse (Paragraph def []) + speakerNotesBody :: PandocMonad m => [Paragraph] -> P m Element speakerNotesBody paras = do - elements <- mapM paragraphToElement $ map removeLinks paras + elements <- mapM paragraphToElement $ spaceParas $ map removeParaLinks paras let txBody = mknode "p:txBody" [] $ [mknode "a:bodyPr" [] (), mknode "a:lstStyle" [] ()] ++ elements return $ -- cgit v1.2.3 From b4f43f879908592ebf28adc772127c46b0c1734d Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sun, 18 Feb 2018 17:44:09 -0500 Subject: Powerpoint writer: fix compiler complaints. --- src/Text/Pandoc/Writers/Powerpoint/Output.hs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint/Output.hs b/src/Text/Pandoc/Writers/Powerpoint/Output.hs index bc9c3193a..752c81857 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Output.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Output.hs @@ -1267,12 +1267,14 @@ speakerNotesSlideNumber pgNum fieldId = ] slideToSpeakerNotesElement :: PandocMonad m => Slide -> P m (Maybe Element) -slideToSpeakerNotesElement sld@(Slide _ _ mbNotes) - | Nothing <- mbNotes = return Nothing - | Just (SpeakerNotes paras) <- mbNotes = do +slideToSpeakerNotesElement slide + | Slide _ _ mbNotes <- slide + , Nothing <- mbNotes = return Nothing + | Slide _ _ mbNotes <- slide + , Just (SpeakerNotes paras) <- mbNotes = do master <- getNotesMaster fieldId <- getSlideNumberFieldId master - num <- slideNum sld + num <- slideNum slide let imgShape = speakerNotesSlideImage sldNumShape = speakerNotesSlideNumber num fieldId bodyShape <- speakerNotesBody paras @@ -1462,9 +1464,11 @@ slideToSpeakerNotesEntry slide = do _ -> return Nothing slideToSpeakerNotesRelElement :: PandocMonad m => Slide -> P m (Maybe Element) -slideToSpeakerNotesRelElement slide@(Slide _ _ mbNotes) - | Nothing <- mbNotes = return Nothing - | Just _ <- mbNotes = do +slideToSpeakerNotesRelElement slide + | Slide _ _ mbNotes <- slide + , Nothing <- mbNotes = return Nothing + | Slide _ _ mbNotes <- slide + , Just _ <- mbNotes = do idNum <- slideNum slide return $ Just $ mknode "Relationships" -- cgit v1.2.3 From 574104861f13997effb36f8e3483dfd7c7d01cd7 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Sun, 18 Feb 2018 18:42:32 -0500 Subject: Powerpoint writer: Another attempt at avoiding compiler warnings. --- src/Text/Pandoc/Writers/Powerpoint/Output.hs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint/Output.hs b/src/Text/Pandoc/Writers/Powerpoint/Output.hs index 752c81857..9a9ede864 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Output.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Output.hs @@ -1268,8 +1268,6 @@ speakerNotesSlideNumber pgNum fieldId = slideToSpeakerNotesElement :: PandocMonad m => Slide -> P m (Maybe Element) slideToSpeakerNotesElement slide - | Slide _ _ mbNotes <- slide - , Nothing <- mbNotes = return Nothing | Slide _ _ mbNotes <- slide , Just (SpeakerNotes paras) <- mbNotes = do master <- getNotesMaster @@ -1304,6 +1302,7 @@ slideToSpeakerNotesElement slide ] ] ] +slideToSpeakerNotesElement _ = return Nothing ----------------------------------------------------------------------- @@ -1465,8 +1464,6 @@ slideToSpeakerNotesEntry slide = do slideToSpeakerNotesRelElement :: PandocMonad m => Slide -> P m (Maybe Element) slideToSpeakerNotesRelElement slide - | Slide _ _ mbNotes <- slide - , Nothing <- mbNotes = return Nothing | Slide _ _ mbNotes <- slide , Just _ <- mbNotes = do idNum <- slideNum slide @@ -1482,6 +1479,7 @@ slideToSpeakerNotesRelElement slide , ("Target", "../notesMasters/notesMaster1.xml") ] () ] +slideToSpeakerNotesRelElement _ = return Nothing slideToSpeakerNotesRelEntry :: PandocMonad m => Slide -> P m (Maybe Entry) slideToSpeakerNotesRelEntry slide = do -- cgit v1.2.3 From 5a9d7d20ddeebc164acdbb3b318df298417ad1ab Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Mon, 19 Feb 2018 19:23:30 +0300 Subject: Move manyUntil to Text.Pandoc.Parsing and use it in Txt2Tags reader --- src/Text/Pandoc/Parsing.hs | 15 +++++++++++++++ src/Text/Pandoc/Readers/Muse.hs | 14 -------------- src/Text/Pandoc/Readers/Txt2Tags.hs | 3 +-- 3 files changed, 16 insertions(+), 16 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 562e3d577..1b66aa430 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -40,6 +40,7 @@ module Text.Pandoc.Parsing ( takeWhileP, anyLineNewline, indentWith, many1Till, + manyUntil, notFollowedBy', oneOfStrings, oneOfStringsCI, @@ -325,6 +326,20 @@ many1Till p end = do rest <- manyTill p end return (first:rest) +-- | Like @manyTill@, but also returns the result of end parser. +manyUntil :: (Stream s m t) + => ParserT s u m a + -> ParserT s u m b + -> ParserT s u m ([a], b) +manyUntil p end = scan + where scan = + (do e <- end + return ([], e) + ) <|> + (do x <- p + (xs, e) <- scan + return (x:xs, e)) + -- | A more general form of @notFollowedBy@. This one allows any -- type of parser to be specified, and succeeds only if that parser fails. -- It does not consume any input. diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index a842925a2..2f20de1c9 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -189,20 +189,6 @@ atStart p = do guard $ museLastStrPos st /= Just pos p --- Like manyTill, but also returns result of end parser -manyUntil :: (Stream s m t) - => ParserT s u m a - -> ParserT s u m b - -> ParserT s u m ([a], b) -manyUntil p end = scan - where scan = - (do e <- end - return ([], e) - ) <|> - (do x <- p - (xs, e) <- scan - return (x:xs, e)) - someUntil :: (Stream s m t) => ParserT s u m a -> ParserT s u m b diff --git a/src/Text/Pandoc/Readers/Txt2Tags.hs b/src/Text/Pandoc/Readers/Txt2Tags.hs index b4f4bc564..f4dda7a11 100644 --- a/src/Text/Pandoc/Readers/Txt2Tags.hs +++ b/src/Text/Pandoc/Readers/Txt2Tags.hs @@ -529,8 +529,7 @@ image = try $ do -- List taken from txt2tags source let extensions = [".jpg", ".jpeg", ".gif", ".png", ".eps", ".bmp"] char '[' - path <- manyTill (noneOf "\n\t\r ") (try $ lookAhead (oneOfStrings extensions)) - ext <- oneOfStrings extensions + (path, ext) <- manyUntil (noneOf "\n\t\r ") (oneOfStrings extensions) char ']' return $ B.image (path ++ ext) "" mempty -- cgit v1.2.3 From f1146cd7eef588e2c993167aa0293fda066c0aa9 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Mon, 19 Feb 2018 15:03:51 -0500 Subject: Powerpoint writer: Add notesMaster to presentation.xml if necessary In previous version, we only modified the notesMaster entry in the presentation.xml file, and removed it if necessary. But if using a template, it might not be available. So we always delete it, and then add it back in if necessary. We also have to make sure that we add it appropriately the .rels file associated with presentation.xml. --- src/Text/Pandoc/Writers/Powerpoint/Output.hs | 79 +++++++++++++++++++--------- 1 file changed, 54 insertions(+), 25 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint/Output.hs b/src/Text/Pandoc/Writers/Powerpoint/Output.hs index 9a9ede864..83695af3a 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Output.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Output.hs @@ -1372,10 +1372,23 @@ getRels = do return $ mapMaybe elementToRel relElems presentationToRels :: PandocMonad m => Presentation -> P m [Relationship] -presentationToRels (Presentation _ slides) = do +presentationToRels pres@(Presentation _ slides) = do mySlideRels <- mapM slideToPresRel slides + let notesMasterRels = + if presHasSpeakerNotes pres + then [Relationship { relId = length mySlideRels + 2 + , relType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesMaster" + , relTarget = "notesMasters/notesMaster1.xml" + }] + else [] + insertedRels = mySlideRels ++ notesMasterRels rels <- getRels - let relsWithoutSlides = filter (\r -> relType r /= "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide") rels + -- we remove the slide rels and the notesmaster (if it's + -- there). We'll put these back in ourselves, if necessary. + let relsWeKeep = filter + (\r -> relType r /= "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" && + relType r /= "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesMaster") + rels -- We want to make room for the slides in the id space. The slides -- will start at Id2 (since Id1 is for the slide master). There are -- two slides in the data file, but that might change in the future, @@ -1384,8 +1397,9 @@ presentationToRels (Presentation _ slides) = do -- 1. We look to see what the minimum relWithoutSlide id (greater than 1) is. -- 2. We add the difference between this and the number of slides to -- all relWithoutSlide rels (unless they're 1) + -- 3. If we have a notesmaster slide, we make space for that as well. - let minRelNotOne = case filter (1<) $ map relId relsWithoutSlides of + let minRelNotOne = case filter (1<) $ map relId relsWeKeep of [] -> 0 -- doesn't matter in this case, since -- there will be nothing to map the -- function over @@ -1393,11 +1407,11 @@ presentationToRels (Presentation _ slides) = do modifyRelNum :: Int -> Int modifyRelNum 1 = 1 - modifyRelNum n = n - minRelNotOne + 2 + length slides + modifyRelNum n = n - minRelNotOne + 2 + length insertedRels - relsWithoutSlides' = map (\r -> r{relId = modifyRelNum $ relId r}) relsWithoutSlides + relsWeKeep' = map (\r -> r{relId = modifyRelNum $ relId r}) relsWeKeep - return $ mySlideRels ++ relsWithoutSlides' + return $ insertedRels ++ relsWeKeep' -- We make this ourselves, in case there's a thumbnail in the one from -- the template. @@ -1601,26 +1615,41 @@ presentationToPresentationElement pres@(Presentation _ slds) = do notesMasterRId = length slds + 2 - modifySpeakerNotes' :: Content -> [Content] - modifySpeakerNotes' (Elem e) = case elName e of - (QName "notesMasterIdLst" _ _) -> - if presHasSpeakerNotes pres - then [Elem $ - mknode "p:notesMasterIdLst" [] - [ mknode - "p:NotesMasterId" - [("r:id", "rId" ++ show notesMasterRId)] - () - ] - ] - else [] - _ -> [Elem e] - modifySpeakerNotes' ct = [ct] - - modifySpeakerNotes :: [Content] -> [Content] - modifySpeakerNotes = concatMap modifySpeakerNotes' + notesMasterElem = mknode "p:notesMasterIdLst" [] + [ mknode + "p:NotesMasterId" + [("r:id", "rId" ++ show notesMasterRId)] + () + ] - newContent = modifySpeakerNotes $ map modifySldIdLst $ elContent element + -- if there's a notesMasterIdLst in the presentation.xml file, + -- we want to remove it. We then want to put our own, if + -- necessary, after the slideMasterIdLst element. + + removeNotesMaster' :: Content -> [Content] + removeNotesMaster' (Elem e) = case elName e of + (QName "notesMasterIdLst" _ _) -> [] + _ -> [Elem e] + removeNotesMaster' ct = [ct] + + removeNotesMaster :: [Content] -> [Content] + removeNotesMaster = concatMap removeNotesMaster' + + insertNotesMaster' :: Content -> [Content] + insertNotesMaster' (Elem e) = case elName e of + (QName "sldMasterIdLst" _ _) -> [Elem e, Elem notesMasterElem] + _ -> [Elem e] + insertNotesMaster' ct = [ct] + + insertNotesMaster :: [Content] -> [Content] + insertNotesMaster = if presHasSpeakerNotes pres + then concatMap insertNotesMaster' + else id + + newContent = insertNotesMaster $ + removeNotesMaster $ + map modifySldIdLst $ + elContent element return $ element{elContent = newContent} -- cgit v1.2.3 From a16382b06b860e2d4a7284527b6fda0167cffa61 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Tue, 20 Feb 2018 14:37:02 +0300 Subject: Muse reader: use updateState instead of setState to restore museInLink --- src/Text/Pandoc/Readers/Muse.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 2f20de1c9..b9f233a1f 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -912,7 +912,7 @@ link = try $ do guard $ not $ museInLink st setState $ st{ museInLink = True } (url, title, content) <- linkText - setState $ st{ museInLink = False } + updateState (\st -> st { museInLink = False }) return $ case stripPrefix "URL:" url of Nothing -> if isImageUrl url then B.image url title <$> fromMaybe (return mempty) content -- cgit v1.2.3 From b9b66d3b291cda768304fa28cba578f6e4dd8b37 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Mon, 19 Feb 2018 14:52:32 -0500 Subject: Powerpoint writer: Use ph name and idx for getting layout shapes Internal change: when we take shapes from the layout for title, content, etc, we should use the attributes of the "ph" (placeholder) tag -- idx and name. This is what powerpoint uses internally, and therefore seems more dependable across reference-docs than using the shape names, as we had previously done. There should be no output changes as a result of this commit. --- src/Text/Pandoc/Writers/Powerpoint/Output.hs | 63 ++++++++++++---------------- 1 file changed, 26 insertions(+), 37 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint/Output.hs b/src/Text/Pandoc/Writers/Powerpoint/Output.hs index 83695af3a..b5138b514 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Output.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Output.hs @@ -351,14 +351,6 @@ getLayout layout = do layoutpath ++ " missing in reference file" return root -shapeHasName :: NameSpaces -> String -> Element -> Bool -shapeHasName ns name element - | Just nvSpPr <- findChild (elemName ns "p" "nvSpPr") element - , Just cNvPr <- findChild (elemName ns "p" "cNvPr") nvSpPr - , Just nm <- findAttr (QName "name" Nothing Nothing) cNvPr = - nm == name - | otherwise = False - shapeHasId :: NameSpaces -> String -> Element -> Bool shapeHasId ns ident element | Just nvSpPr <- findChild (elemName ns "p" "nvSpPr") element @@ -374,14 +366,11 @@ getContentShape :: PandocMonad m => NameSpaces -> Element -> P m Element getContentShape ns spTreeElem | isElem ns "p" "spTree" spTreeElem = do contentType <- asks envContentType - let ident = case contentType of - NormalContent -> "3" - TwoColumnLeftContent -> "3" - TwoColumnRightContent -> "4" - case filterChild - (\e -> (isElem ns "p" "sp" e) && (shapeHasId ns ident e)) - spTreeElem - of + let idx = case contentType of + NormalContent -> "1" + TwoColumnLeftContent -> "1" + TwoColumnRightContent -> "2" + case getShapeByPlaceHolderIndex ns spTreeElem idx of Just e -> return e Nothing -> throwError $ PandocSomeError $ @@ -992,14 +981,6 @@ graphicToElement tableWidth (Tbl tblPr hdrCells rows) = do ] ] -getShapeByName :: NameSpaces -> Element -> String -> Maybe Element -getShapeByName ns spTreeElem name - | isElem ns "p" "spTree" spTreeElem = - filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasName ns name e)) spTreeElem - | otherwise = Nothing - - - getShapeByPlaceHolderType :: NameSpaces -> Element -> String -> Maybe Element getShapeByPlaceHolderType ns spTreeElem phType | isElem ns "p" "spTree" spTreeElem = @@ -1013,18 +994,26 @@ getShapeByPlaceHolderType ns spTreeElem phType filterChild findPhType spTreeElem | otherwise = Nothing --- getShapeById :: NameSpaces -> Element -> String -> Maybe Element --- getShapeById ns spTreeElem ident --- | isElem ns "p" "spTree" spTreeElem = --- filterChild (\e -> (isElem ns "p" "sp" e) && (shapeHasId ns ident e)) spTreeElem --- | otherwise = Nothing +getShapeByPlaceHolderIndex :: NameSpaces -> Element -> String -> Maybe Element +getShapeByPlaceHolderIndex ns spTreeElem phIdx + | isElem ns "p" "spTree" spTreeElem = + let findPhType element = isElem ns "p" "sp" element && + Just phIdx == (Just element >>= + findChild (elemName ns "p" "nvSpPr") >>= + findChild (elemName ns "p" "nvPr") >>= + findChild (elemName ns "p" "ph") >>= + findAttr (QName "idx" Nothing Nothing)) + in + filterChild findPhType spTreeElem + | otherwise = Nothing + nonBodyTextToElement :: PandocMonad m => Element -> String -> [ParaElem] -> P m Element -nonBodyTextToElement layout shapeName paraElements +nonBodyTextToElement layout phType paraElements | ns <- elemToNameSpaces layout , Just cSld <- findChild (elemName ns "p" "cSld") layout , Just spTree <- findChild (elemName ns "p" "spTree") cSld - , Just sp <- getShapeByName ns spTree shapeName = do + , Just sp <- getShapeByPlaceHolderType ns spTree phType = do let hdrPara = Paragraph def paraElements element <- paragraphToElement hdrPara let txBody = mknode "p:txBody" [] $ @@ -1039,7 +1028,7 @@ contentToElement layout hdrShape shapes | ns <- elemToNameSpaces layout , Just cSld <- findChild (elemName ns "p" "cSld") layout , Just spTree <- findChild (elemName ns "p" "spTree") cSld = do - element <- nonBodyTextToElement layout "Title 1" hdrShape + element <- nonBodyTextToElement layout "title" hdrShape let hdrShapeElements = if null hdrShape then [] else [element] @@ -1057,7 +1046,7 @@ twoColumnToElement layout hdrShape shapesL shapesR | ns <- elemToNameSpaces layout , Just cSld <- findChild (elemName ns "p" "cSld") layout , Just spTree <- findChild (elemName ns "p" "spTree") cSld = do - element <- nonBodyTextToElement layout "Title 1" hdrShape + element <- nonBodyTextToElement layout "title" hdrShape let hdrShapeElements = if null hdrShape then [] else [element] @@ -1081,7 +1070,7 @@ titleToElement layout titleElems | ns <- elemToNameSpaces layout , Just cSld <- findChild (elemName ns "p" "cSld") layout , Just spTree <- findChild (elemName ns "p" "spTree") cSld = do - element <- nonBodyTextToElement layout "Title 1" titleElems + element <- nonBodyTextToElement layout "title" titleElems let titleShapeElements = if null titleElems then [] else [element] @@ -1095,15 +1084,15 @@ metadataToElement layout titleElems subtitleElems authorsElems dateElems , Just spTree <- findChild (elemName ns "p" "spTree") cSld = do titleShapeElements <- if null titleElems then return [] - else sequence [nonBodyTextToElement layout "Title 1" titleElems] + else sequence [nonBodyTextToElement layout "ctrTitle" titleElems] let combinedAuthorElems = intercalate [Break] authorsElems subtitleAndAuthorElems = intercalate [Break, Break] [subtitleElems, combinedAuthorElems] subtitleShapeElements <- if null subtitleAndAuthorElems then return [] - else sequence [nonBodyTextToElement layout "Subtitle 2" subtitleAndAuthorElems] + else sequence [nonBodyTextToElement layout "subTitle" subtitleAndAuthorElems] dateShapeElements <- if null dateElems then return [] - else sequence [nonBodyTextToElement layout "Date Placeholder 3" dateElems] + else sequence [nonBodyTextToElement layout "dt" dateElems] return $ replaceNamedChildren ns "p" "sp" (titleShapeElements ++ subtitleShapeElements ++ dateShapeElements) spTree -- cgit v1.2.3 From 705145956d8fbb77aa0a63b87d00a10660ca3279 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Tue, 20 Feb 2018 09:17:52 -0500 Subject: Muse reader: fix compiler warning lambda variable `st` shadowed an existing variable. --- src/Text/Pandoc/Readers/Muse.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index b9f233a1f..c19b42503 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -912,7 +912,7 @@ link = try $ do guard $ not $ museInLink st setState $ st{ museInLink = True } (url, title, content) <- linkText - updateState (\st -> st { museInLink = False }) + updateState (\state -> state { museInLink = False }) return $ case stripPrefix "URL:" url of Nothing -> if isImageUrl url then B.image url title <$> fromMaybe (return mempty) content -- cgit v1.2.3 From 84db7e492a7a7091ca366f24c21dd5d44163f0da Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Wed, 21 Feb 2018 12:40:37 +0300 Subject: Muse reader: replace setState with updateState where possible --- src/Text/Pandoc/Readers/Muse.hs | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index c19b42503..e89a89b8f 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -250,8 +250,7 @@ parseBlocks = rest <- parseBlocks return $ first B.<> rest listStart = do - st <- getState - setState $ st{ museInPara = False } + updateState (\st -> st { museInPara = False }) (first, rest) <- anyListUntil parseBlocks <|> amuseNoteBlockUntil parseBlocks return $ first B.<> rest paraStart = do @@ -274,8 +273,7 @@ parseBlocksTill end = rest <- continuation return $ first B.<> rest listStart = do - st <- getState - setState $ st{ museInPara = False } + updateState (\st -> st { museInPara = False }) (first, e) <- anyListUntil ((Left <$> end) <|> (Right <$> continuation)) case e of Left _ -> return first @@ -309,8 +307,7 @@ listItemContentsUntil col pre end = (rest, e) <- parsePre <|> continuation <|> parseEnd return (first B.<> rest, e) listStart = do - st <- getState - setState $ st{ museInPara = False } + updateState (\st -> st { museInPara = False }) (first, e) <- anyListUntil ((Left <$> pre) <|> (Right <$> continuation) <|> (Left <$> end)) case e of Left ee -> return (first, ee) @@ -318,8 +315,7 @@ listItemContentsUntil col pre end = continuation = try $ do blank <- optionMaybe blankline skipMany blankline indentWith col - st <- getState - setState $ st{ museInPara = museInPara st && isNothing blank } + updateState (\st -> st { museInPara = museInPara st && isNothing blank }) listItemContentsUntil col pre end parseBlock :: PandocMonad m => MuseParser m (F Blocks) @@ -331,8 +327,7 @@ parseBlock = do blockElements :: PandocMonad m => MuseParser m (F Blocks) blockElements = do - st <- getState - setState $ st{ museInPara = False } + updateState (\st -> st { museInPara = False }) choice [ mempty <$ blankline , comment , separator @@ -480,8 +475,7 @@ amuseNoteBlockUntil end = try $ do guardEnabled Ext_amuse pos <- getPosition ref <- noteMarker <* spaceChar - st <- getState - setState $ st{ museInPara = False } + updateState (\st -> st { museInPara = False }) (content, e) <- listItemContentsUntil (sourceColumn pos) (fail "x") end oldnotes <- museNotes <$> getState case M.lookup ref oldnotes of @@ -541,8 +535,7 @@ bulletListItemsUntil :: PandocMonad m bulletListItemsUntil indent end = try $ do char '-' void spaceChar <|> lookAhead eol - st <- getState - setState $ st{ museInPara = False } + updateState (\st -> st { museInPara = False }) (x, e) <- listItemContentsUntil (indent + 2) (Right <$> try (optional blankline >> indentWith indent >> bulletListItemsUntil indent end)) (Left <$> end) case e of Left ee -> return ([x], ee) @@ -591,8 +584,7 @@ orderedListItemsUntil indent style end = continuation = try $ do pos <- getPosition void spaceChar <|> lookAhead eol - st <- getState - setState $ st{ museInPara = False } + updateState (\st -> st { museInPara = False }) (x, e) <- listItemContentsUntil (sourceColumn pos) (Right <$> try (optionMaybe blankline >> indentWith indent >> museOrderedListMarker style >> continuation)) (Left <$> end) case e of Left ee -> return ([x], ee) @@ -617,8 +609,7 @@ descriptionsUntil :: PandocMonad m -> MuseParser m ([F Blocks], a) descriptionsUntil indent end = do void spaceChar <|> lookAhead eol - st <- getState - setState $ st{ museInPara = False } + updateState (\st -> st { museInPara = False }) (x, e) <- listItemContentsUntil indent (Right <$> try (optional blankline >> indentWith indent >> manyTill spaceChar (string "::") >> descriptionsUntil indent end)) (Left <$> end) case e of Right (xs, ee) -> return (x:xs, ee) -- cgit v1.2.3 From 00d20ccd09a8542fda631ab16c7f569098f2918d Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Wed, 21 Feb 2018 08:53:29 +0100 Subject: Org reader: allow changing emphasis syntax The characters allowed before and after emphasis can be configured via `#+pandoc-emphasis-pre` and `#+pandoc-emphasis-post`, respectively. This allows to change which strings are recognized as emphasized text on a per-document or even per-paragraph basis. The allowed characters must be given as (Haskell) string. #+pandoc-emphasis-pre: "-\t ('\"{" #+pandoc-emphasis-post: "-\t\n .,:!?;'\")}[" If the argument cannot be read as a string, the default value is restored. Closes: #4378 --- src/Text/Pandoc/Readers/Org/Inlines.hs | 16 ++++++---------- src/Text/Pandoc/Readers/Org/Meta.hs | 22 ++++++++++++++++++++++ src/Text/Pandoc/Readers/Org/ParserState.hs | 12 +++++++++++- 3 files changed, 39 insertions(+), 11 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Org/Inlines.hs b/src/Text/Pandoc/Readers/Org/Inlines.hs index 670f8ace0..3a12f38d0 100644 --- a/src/Text/Pandoc/Readers/Org/Inlines.hs +++ b/src/Text/Pandoc/Readers/Org/Inlines.hs @@ -603,6 +603,8 @@ updatePositions :: PandocMonad m => Char -> OrgParser m Char updatePositions c = do + st <- getState + let emphasisPreChars = orgStateEmphasisPreChars st when (c `elem` emphasisPreChars) updateLastPreCharPos when (c `elem` emphasisForbiddenBorderChars) updateLastForbiddenCharPos return c @@ -681,8 +683,10 @@ emphasisEnd c = try $ do updateLastStrPos popInlineCharStack return c - where acceptablePostChars = - surroundingEmphasisChar >>= \x -> oneOf (x ++ emphasisPostChars) + where + acceptablePostChars = do + emphasisPostChars <- orgStateEmphasisPostChars <$> getState + surroundingEmphasisChar >>= \x -> oneOf (x ++ emphasisPostChars) mathStart :: PandocMonad m => Char -> OrgParser m Char mathStart c = try $ @@ -734,14 +738,6 @@ many1TillNOrLessNewlines n p end = try $ -- here (see, e.g., the Emacs Lisp variable `org-emphasis-regexp-components` -- for details). --- | Chars allowed to occur before emphasis (spaces and newlines are ok, too) -emphasisPreChars :: [Char] -emphasisPreChars = "-\t ('\"{" - --- | Chars allowed at after emphasis -emphasisPostChars :: [Char] -emphasisPostChars = "-\t\n .,:!?;'\")}[" - -- | Chars not allowed at the (inner) border of emphasis emphasisForbiddenBorderChars :: [Char] emphasisForbiddenBorderChars = "\t\n\r " diff --git a/src/Text/Pandoc/Readers/Org/Meta.hs b/src/Text/Pandoc/Readers/Org/Meta.hs index 0a690028d..6ad403fd8 100644 --- a/src/Text/Pandoc/Readers/Org/Meta.hs +++ b/src/Text/Pandoc/Readers/Org/Meta.hs @@ -43,6 +43,7 @@ import Text.Pandoc.Builder (Blocks, Inlines) import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class (PandocMonad) import Text.Pandoc.Definition +import Text.Pandoc.Shared (safeRead) import Control.Monad (mzero, void, when) import Data.Char (toLower) @@ -154,6 +155,8 @@ optionLine = try $ do "seq_todo" -> todoSequence >>= updateState . registerTodoSequence "typ_todo" -> todoSequence >>= updateState . registerTodoSequence "macro" -> macroDefinition >>= updateState . registerMacro + "pandoc-emphasis-pre" -> emphChars >>= updateState . setEmphasisPreChar + "pandoc-emphasis-post" -> emphChars >>= updateState . setEmphasisPostChar _ -> mzero addLinkFormat :: Monad m => String @@ -184,6 +187,25 @@ parseFormat = try $ replacePlain <|> replaceUrl <|> justAppend rest = manyTill anyChar (eof <|> () <$ oneOf "\n\r") tillSpecifier c = manyTill (noneOf "\n\r") (try $ string ('%':c:"")) +setEmphasisPreChar :: Maybe [Char] -> OrgParserState -> OrgParserState +setEmphasisPreChar csMb st = + let preChars = case csMb of + Nothing -> orgStateEmphasisPreChars defaultOrgParserState + Just cs -> cs + in st { orgStateEmphasisPreChars = preChars } + +setEmphasisPostChar :: Maybe [Char] -> OrgParserState -> OrgParserState +setEmphasisPostChar csMb st = + let postChars = case csMb of + Nothing -> orgStateEmphasisPostChars defaultOrgParserState + Just cs -> cs + in st { orgStateEmphasisPostChars = postChars } + +emphChars :: Monad m => OrgParser m (Maybe [Char]) +emphChars = do + skipSpaces + safeRead <$> anyLine + inlinesTillNewline :: PandocMonad m => OrgParser m (F Inlines) inlinesTillNewline = do updateLastPreCharPos diff --git a/src/Text/Pandoc/Readers/Org/ParserState.hs b/src/Text/Pandoc/Readers/Org/ParserState.hs index e2acce5bf..6316766fa 100644 --- a/src/Text/Pandoc/Readers/Org/ParserState.hs +++ b/src/Text/Pandoc/Readers/Org/ParserState.hs @@ -29,6 +29,7 @@ Define the Org-mode parser state. -} module Text.Pandoc.Readers.Org.ParserState ( OrgParserState (..) + , defaultOrgParserState , OrgParserLocal (..) , OrgNoteRecord , HasReaderOptions (..) @@ -104,6 +105,11 @@ type TodoSequence = [TodoMarker] data OrgParserState = OrgParserState { orgStateAnchorIds :: [String] , orgStateEmphasisCharStack :: [Char] + , orgStateEmphasisPreChars :: [Char] -- ^ Chars allowed to occur before + -- emphasis; spaces and newlines are + -- always ok in addition to what is + -- specified here. + , orgStateEmphasisPostChars :: [Char] -- ^ Chars allowed at after emphasis , orgStateEmphasisNewlines :: Maybe Int , orgStateExportSettings :: ExportSettings , orgStateHeaderMap :: M.Map Inlines String @@ -124,7 +130,9 @@ data OrgParserState = OrgParserState , orgMacros :: M.Map Text Macro } -data OrgParserLocal = OrgParserLocal { orgLocalQuoteContext :: QuoteContext } +data OrgParserLocal = OrgParserLocal + { orgLocalQuoteContext :: QuoteContext + } instance Default OrgParserLocal where def = OrgParserLocal NoQuote @@ -168,6 +176,8 @@ instance Default OrgParserState where defaultOrgParserState :: OrgParserState defaultOrgParserState = OrgParserState { orgStateAnchorIds = [] + , orgStateEmphasisPreChars = "-\t ('\"{" + , orgStateEmphasisPostChars = "-\t\n .,:!?;'\")}[" , orgStateEmphasisCharStack = [] , orgStateEmphasisNewlines = Nothing , orgStateExportSettings = def -- cgit v1.2.3 From 0690df507b5482acb37e67101162189ab0188f21 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 21 Feb 2018 17:17:11 -0800 Subject: LaTeX writer: Fix image height with percentage. This previously caused the image to be resized to a percentage of textwidth, rather than textheight. Closes #4389. --- src/Text/Pandoc/Writers/LaTeX.hs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index fa72f0f1a..ca59e451b 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -1122,7 +1122,11 @@ inlineToLaTeX (Image attr _ (source, _)) = do Just (Pixel a) -> [d <> text (showInInch opts (Pixel a)) <> "in"] Just (Percent a) -> - [d <> text (showFl (a / 100)) <> "\\textwidth"] + [d <> text (showFl (a / 100)) <> + case dir of + Width -> "\\textwidth" + Height -> "\\textheight" + ] Just dim -> [d <> text (show dim)] Nothing -> -- cgit v1.2.3 From b8f0d15a72bcd7423f8a929855766d86bf7ffd90 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Wed, 21 Feb 2018 17:29:34 -0800 Subject: LaTeX writer: Put hypertarget inside figure environment. Previously it surrounded the figure. This works around a problem with the endfloat package and makes pandoc's output compatible with it. Closes #4388. --- src/Text/Pandoc/Writers/LaTeX.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index ca59e451b..c94d256f5 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -509,14 +509,14 @@ blockToLaTeX (Para [Image attr@(ident, _, _) txt (src,'f':'i':'g':':':tit)]) = d let footnotes = notesToLaTeX notes lab <- labelFor ident let caption = "\\caption" <> captForLof <> braces capt <> lab - let figure = cr <> "\\begin{figure}" $$ "\\centering" $$ img $$ - caption $$ "\\end{figure}" <> cr - figure' <- hypertarget True ident figure + innards <- hypertarget True ident $ + "\\centering" $$ img $$ caption <> cr + let figure = cr <> "\\begin{figure}" $$ innards $$ "\\end{figure}" return $ if inNote || inMinipage -- can't have figures in notes or minipage (here, table cell) -- http://www.tex.ac.uk/FAQ-ouparmd.html then "\\begin{center}" $$ img $+$ capt $$ "\\end{center}" - else figure' $$ footnotes + else figure $$ footnotes -- . . . indicates pause in beamer slides blockToLaTeX (Para [Str ".",Space,Str ".",Space,Str "."]) = do beamer <- gets stBeamer -- cgit v1.2.3 From 88d17c6c66954be47bfa0f625323aca2ba48a9e9 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Thu, 22 Feb 2018 12:54:57 -0500 Subject: Extensions: Add Ext_styles This will be used in the docx reader (defaulting to off) to read pargraph and character styles not understood by pandoc (as divs and spans, respectively). --- src/Text/Pandoc/Extensions.hs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 8f6d49ade..fe690713c 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -149,6 +149,7 @@ data Extension = | Ext_strikeout -- ^ Strikeout using ~~this~~ syntax | Ext_subscript -- ^ Subscript using ~this~ syntax | Ext_superscript -- ^ Superscript using ^this^ syntax + | Ext_styles -- ^ Read styles that pandoc doesn't know | Ext_table_captions -- ^ Pandoc-style table captions | Ext_tex_math_dollars -- ^ TeX math between $..$ or $$..$$ | Ext_tex_math_double_backslash -- ^ TeX math btw \\(..\\) \\[..\\] -- cgit v1.2.3 From 5262c0853acbef6dd25d9d6cbda26120859b45ff Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Thu, 22 Feb 2018 12:56:19 -0500 Subject: Docx reader: read custom styles This will read all paragraph and character classes as divs and spans, respectively. Dependent styles will still be resolved, but will be wrapped with appropriate style tags. It is controlled by the `+styles` extension (`-f docx+styles`). This can be used in conjunction with the `custom-style` feature in the docx writer for a pandoc-docx editing workflow. Users can convert from an input docx, reading the custom-styles, and then use that same input docx file as a reference-doc for producing an output docx file. Styles will be maintained across the conversion, even if pandoc doesn't understand them. Without the extension: $ pandoc test/docx/custom-style-reference.docx -f docx -t markdown This is some text. This is text with an *emphasized* text style. And this is text with a **strengthened** text style. > Here is a styled paragraph that inherits from Block Text. With the extension: $ pandoc test/docx/custom-style-reference.docx -f docx+styles -t markdown ::: {custom-style="FirstParagraph"} This is some text. ::: ::: {custom-style="BodyText"} This is text with an *[[emphasized]{custom-style="Emphatic"}]{custom-style="Emphatic"}* text style. And this is text with a **[[strengthened]{custom-style="Strengthened"}]{custom-style="Strengthened"}** text style. ::: ::: {custom-style="MyBlockStyle"} Closes: #1843 --- src/Text/Pandoc/Readers/Docx.hs | 156 ++++++++++++++++++++++++---------------- 1 file changed, 96 insertions(+), 60 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index 098759a61..491eea753 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -278,28 +278,51 @@ resolveDependentRunStyle rPr , rStyle = rStyle rPr } | otherwise = rPr -runStyleToTransform :: RunStyle -> (Inlines -> Inlines) +extraRunStyleInfo :: PandocMonad m => RunStyle -> DocxContext m (Inlines -> Inlines) +extraRunStyleInfo rPr + | Just (s, _) <- rStyle rPr = do + opts <- asks docxOptions + return $ if isEnabled Ext_styles opts + then spanWith ("", [], [("custom-style", s)]) + else id + | otherwise = return id + +runStyleToTransform :: PandocMonad m => RunStyle -> DocxContext m (Inlines -> Inlines) runStyleToTransform rPr | Just (s, _) <- rStyle rPr - , s `elem` spansToKeep = - let rPr' = rPr{rStyle = Nothing} - in - spanWith ("", [s], []) . runStyleToTransform rPr' - | Just True <- isItalic rPr = - emph . runStyleToTransform rPr {isItalic = Nothing} - | Just True <- isBold rPr = - strong . runStyleToTransform rPr {isBold = Nothing} - | Just True <- isSmallCaps rPr = - smallcaps . runStyleToTransform rPr {isSmallCaps = Nothing} - | Just True <- isStrike rPr = - strikeout . runStyleToTransform rPr {isStrike = Nothing} - | Just SupScrpt <- rVertAlign rPr = - superscript . runStyleToTransform rPr {rVertAlign = Nothing} - | Just SubScrpt <- rVertAlign rPr = - subscript . runStyleToTransform rPr {rVertAlign = Nothing} - | Just "single" <- rUnderline rPr = - underlineSpan . runStyleToTransform rPr {rUnderline = Nothing} - | otherwise = id + , s `elem` spansToKeep = do + let rPr' = rPr{rStyle = Nothing} + transform <- runStyleToTransform rPr' + return $ spanWith ("", [s], []) . transform + | Just True <- isItalic rPr = do + transform <- runStyleToTransform rPr {isItalic = Nothing} + extraInfo <- extraRunStyleInfo rPr + return $ emph . extraInfo . transform + | Just True <- isBold rPr = do + transform <- runStyleToTransform rPr {isBold = Nothing} + extraInfo <- extraRunStyleInfo rPr + return $ strong . extraInfo . transform + | Just True <- isSmallCaps rPr = do + transform <- runStyleToTransform rPr {isSmallCaps = Nothing} + extraInfo <- extraRunStyleInfo rPr + return $ smallcaps . extraInfo .transform + | Just True <- isStrike rPr = do + transform <- runStyleToTransform rPr {isStrike = Nothing} + extraInfo <- extraRunStyleInfo rPr + return $ strikeout . extraInfo . transform + | Just SupScrpt <- rVertAlign rPr = do + transform <- runStyleToTransform rPr {rVertAlign = Nothing} + extraInfo <- extraRunStyleInfo rPr + return $ superscript . extraInfo . transform + | Just SubScrpt <- rVertAlign rPr = do + transform <- runStyleToTransform rPr {rVertAlign = Nothing} + extraInfo <- extraRunStyleInfo rPr + return $ subscript . extraInfo . transform + | Just "single" <- rUnderline rPr = do + transform <- runStyleToTransform rPr {rUnderline = Nothing} + extraInfo <- extraRunStyleInfo rPr + return $ underlineSpan . extraInfo . transform + | otherwise = extraRunStyleInfo rPr runToInlines :: PandocMonad m => Run -> DocxContext m Inlines runToInlines (Run rs runElems) @@ -314,7 +337,8 @@ runToInlines (Run rs runElems) _ -> codeString | otherwise = do let ils = smushInlines (map runElemToInlines runElems) - return $ (runStyleToTransform $ resolveDependentRunStyle rs) ils + transform <- runStyleToTransform $ resolveDependentRunStyle rs + return $ transform ils runToInlines (Footnote bps) = do blksList <- smushBlocks <$> mapM bodyPartToBlocks bps return $ note blksList @@ -516,51 +540,60 @@ trimSps (Many ils) = Many $ Seq.dropWhileL isSp $Seq.dropWhileR isSp ils isSp LineBreak = True isSp _ = False -parStyleToTransform :: ParagraphStyle -> (Blocks -> Blocks) +parStyleToTransform :: PandocMonad m => ParagraphStyle -> DocxContext m (Blocks -> Blocks) parStyleToTransform pPr | (c:cs) <- pStyle pPr - , c `elem` divsToKeep = - let pPr' = pPr { pStyle = cs } - in - divWith ("", [c], []) . parStyleToTransform pPr' + , c `elem` divsToKeep = do + let pPr' = pPr { pStyle = cs } + transform <- parStyleToTransform pPr' + return $ divWith ("", [c], []) . transform | (c:cs) <- pStyle pPr, - c `elem` listParagraphDivs = + c `elem` listParagraphDivs = do let pPr' = pPr { pStyle = cs, indentation = Nothing} - in - divWith ("", [c], []) . parStyleToTransform pPr' - | (_:cs) <- pStyle pPr - , Just True <- pBlockQuote pPr = - let pPr' = pPr { pStyle = cs } - in - blockQuote . parStyleToTransform pPr' - | (_:cs) <- pStyle pPr = + transform <- parStyleToTransform pPr' + return $ divWith ("", [c], []) . transform + | (c:cs) <- pStyle pPr + , Just True <- pBlockQuote pPr = do + opts <- asks docxOptions + let pPr' = pPr { pStyle = cs } + transform <- parStyleToTransform pPr' + let extraInfo = if isEnabled Ext_styles opts + then divWith ("", [], [("custom-style", c)]) + else id + return $ extraInfo . blockQuote . transform + | (c:cs) <- pStyle pPr = do + opts <- asks docxOptions let pPr' = pPr { pStyle = cs} - in - parStyleToTransform pPr' + transform <- parStyleToTransform pPr' + let extraInfo = if isEnabled Ext_styles opts + then divWith ("", [], [("custom-style", c)]) + else id + return $ extraInfo . transform | null (pStyle pPr) , Just left <- indentation pPr >>= leftParIndent - , Just hang <- indentation pPr >>= hangingParIndent = + , Just hang <- indentation pPr >>= hangingParIndent = do let pPr' = pPr { indentation = Nothing } - in - case (left - hang) > 0 of - True -> blockQuote . (parStyleToTransform pPr') - False -> parStyleToTransform pPr' + transform <- parStyleToTransform pPr' + return $ case (left - hang) > 0 of + True -> blockQuote . transform + False -> transform | null (pStyle pPr), - Just left <- indentation pPr >>= leftParIndent = + Just left <- indentation pPr >>= leftParIndent = do let pPr' = pPr { indentation = Nothing } - in - case left > 0 of - True -> blockQuote . (parStyleToTransform pPr') - False -> parStyleToTransform pPr' -parStyleToTransform _ = id + transform <- parStyleToTransform pPr' + return $ case left > 0 of + True -> blockQuote . transform + False -> transform +parStyleToTransform _ = return id bodyPartToBlocks :: PandocMonad m => BodyPart -> DocxContext m Blocks bodyPartToBlocks (Paragraph pPr parparts) - | not $ null $ codeDivs `intersect` (pStyle pPr) = - return - $ parStyleToTransform pPr - $ codeBlock - $ concatMap parPartToString parparts + | not $ null $ codeDivs `intersect` (pStyle pPr) = do + transform <- parStyleToTransform pPr + return $ + transform $ + codeBlock $ + concatMap parPartToString parparts | Just (style, n) <- pHeading pPr = do ils <-local (\s-> s{docxInHeaderBlock=True}) (smushInlines <$> mapM parPartToInlines parparts) @@ -586,7 +619,8 @@ bodyPartToBlocks (Paragraph pPr parparts) _ | Just (TrackedChange Insertion _) <- pChange pPr , AcceptChanges <- readerTrackChanges opts -> do modify $ \s -> s {docxPrevPara = mempty} - return $ parStyleToTransform pPr $ para ils'' + transform <- parStyleToTransform pPr + return $ transform $ para ils'' _ | Just (TrackedChange Insertion _) <- pChange pPr , RejectChanges <- readerTrackChanges opts -> do modify $ \s -> s {docxPrevPara = ils''} @@ -596,8 +630,8 @@ bodyPartToBlocks (Paragraph pPr parparts) , ChangeInfo _ cAuthor cDate <- cInfo -> do let attr = ("", ["paragraph-insertion"], [("author", cAuthor), ("date", cDate)]) insertMark = spanWith attr mempty - return $ - parStyleToTransform pPr $ + transform <- parStyleToTransform pPr + return $ transform $ para $ ils'' <> insertMark _ | Just (TrackedChange Deletion _) <- pChange pPr , AcceptChanges <- readerTrackChanges opts -> do @@ -606,18 +640,20 @@ bodyPartToBlocks (Paragraph pPr parparts) _ | Just (TrackedChange Deletion _) <- pChange pPr , RejectChanges <- readerTrackChanges opts -> do modify $ \s -> s {docxPrevPara = mempty} - return $ parStyleToTransform pPr $ para ils'' + transform <- parStyleToTransform pPr + return $ transform $ para ils'' _ | Just (TrackedChange Deletion cInfo) <- pChange pPr , AllChanges <- readerTrackChanges opts , ChangeInfo _ cAuthor cDate <- cInfo -> do let attr = ("", ["paragraph-deletion"], [("author", cAuthor), ("date", cDate)]) insertMark = spanWith attr mempty - return $ - parStyleToTransform pPr $ + transform <- parStyleToTransform pPr + return $ transform $ para $ ils'' <> insertMark _ | otherwise -> do modify $ \s -> s {docxPrevPara = mempty} - return $ parStyleToTransform pPr $ para ils'' + transform <- parStyleToTransform pPr + return $ transform $ para ils'' bodyPartToBlocks (ListItem pPr numId lvl (Just levelInfo) parparts) = do -- We check whether this current numId has previously been used, -- since Docx expects us to pick up where we left off. -- cgit v1.2.3 From 87e0728b87846cdacb8866e19b9a8e127490b4bf Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Thu, 22 Feb 2018 13:27:34 -0500 Subject: Docx reader: Avoid repeated spans in custom styles. The previous commit had a bug where custom-style spans would be read with every recurrsion. This fixes that, and changes the example given in the manual. --- src/Text/Pandoc/Readers/Docx.hs | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index 491eea753..775fa1cdd 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -141,10 +141,12 @@ instance Default DState where } data DEnv = DEnv { docxOptions :: ReaderOptions - , docxInHeaderBlock :: Bool } + , docxInHeaderBlock :: Bool + , docxCustomStyleAlready :: Bool + } instance Default DEnv where - def = DEnv def False + def = DEnv def False False type DocxContext m = ReaderT DEnv (StateT DState m) @@ -281,8 +283,9 @@ resolveDependentRunStyle rPr extraRunStyleInfo :: PandocMonad m => RunStyle -> DocxContext m (Inlines -> Inlines) extraRunStyleInfo rPr | Just (s, _) <- rStyle rPr = do + already <- asks docxCustomStyleAlready opts <- asks docxOptions - return $ if isEnabled Ext_styles opts + return $ if isEnabled Ext_styles opts && not already then spanWith ("", [], [("custom-style", s)]) else id | otherwise = return id @@ -295,32 +298,39 @@ runStyleToTransform rPr transform <- runStyleToTransform rPr' return $ spanWith ("", [s], []) . transform | Just True <- isItalic rPr = do - transform <- runStyleToTransform rPr {isItalic = Nothing} extraInfo <- extraRunStyleInfo rPr + transform <- local (\e -> e{docxCustomStyleAlready = True}) $ + runStyleToTransform rPr {isItalic = Nothing} return $ emph . extraInfo . transform | Just True <- isBold rPr = do - transform <- runStyleToTransform rPr {isBold = Nothing} extraInfo <- extraRunStyleInfo rPr + transform <- local (\e -> e{docxCustomStyleAlready = True}) $ + runStyleToTransform rPr {isBold = Nothing} return $ strong . extraInfo . transform | Just True <- isSmallCaps rPr = do - transform <- runStyleToTransform rPr {isSmallCaps = Nothing} extraInfo <- extraRunStyleInfo rPr + transform <- local (\e -> e{docxCustomStyleAlready = True}) $ + runStyleToTransform rPr {isSmallCaps = Nothing} return $ smallcaps . extraInfo .transform | Just True <- isStrike rPr = do - transform <- runStyleToTransform rPr {isStrike = Nothing} extraInfo <- extraRunStyleInfo rPr + transform <- local (\e -> e{docxCustomStyleAlready = True}) $ + runStyleToTransform rPr {isStrike = Nothing} return $ strikeout . extraInfo . transform | Just SupScrpt <- rVertAlign rPr = do - transform <- runStyleToTransform rPr {rVertAlign = Nothing} extraInfo <- extraRunStyleInfo rPr + transform <- local (\e -> e{docxCustomStyleAlready = True}) $ + runStyleToTransform rPr {rVertAlign = Nothing} return $ superscript . extraInfo . transform | Just SubScrpt <- rVertAlign rPr = do - transform <- runStyleToTransform rPr {rVertAlign = Nothing} extraInfo <- extraRunStyleInfo rPr + transform <- local (\e -> e{docxCustomStyleAlready = True}) $ + runStyleToTransform rPr {rVertAlign = Nothing} return $ subscript . extraInfo . transform | Just "single" <- rUnderline rPr = do - transform <- runStyleToTransform rPr {rUnderline = Nothing} extraInfo <- extraRunStyleInfo rPr + transform <- local (\e -> e{docxCustomStyleAlready = True}) $ + runStyleToTransform rPr {rUnderline = Nothing} return $ underlineSpan . extraInfo . transform | otherwise = extraRunStyleInfo rPr -- cgit v1.2.3 From 8b7df2d915fb4cedb99e60188b0899de9b8b7024 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Thu, 22 Feb 2018 13:39:19 -0500 Subject: Docx reader: Move pandoc inline styling inside custom-style span Previously Emph, Strong, etc were outside the custom-style span. This moves them inside in order to make it easier to write filters that act on the formatting in these contents. Tests and MANUAL example are changed to match. --- src/Text/Pandoc/Readers/Docx.hs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index 775fa1cdd..5f2ca0fff 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -301,37 +301,37 @@ runStyleToTransform rPr extraInfo <- extraRunStyleInfo rPr transform <- local (\e -> e{docxCustomStyleAlready = True}) $ runStyleToTransform rPr {isItalic = Nothing} - return $ emph . extraInfo . transform + return $ extraInfo . emph . transform | Just True <- isBold rPr = do extraInfo <- extraRunStyleInfo rPr transform <- local (\e -> e{docxCustomStyleAlready = True}) $ runStyleToTransform rPr {isBold = Nothing} - return $ strong . extraInfo . transform + return $ extraInfo . strong . transform | Just True <- isSmallCaps rPr = do extraInfo <- extraRunStyleInfo rPr transform <- local (\e -> e{docxCustomStyleAlready = True}) $ runStyleToTransform rPr {isSmallCaps = Nothing} - return $ smallcaps . extraInfo .transform + return $ extraInfo . smallcaps . transform | Just True <- isStrike rPr = do extraInfo <- extraRunStyleInfo rPr transform <- local (\e -> e{docxCustomStyleAlready = True}) $ runStyleToTransform rPr {isStrike = Nothing} - return $ strikeout . extraInfo . transform + return $ extraInfo . strikeout . transform | Just SupScrpt <- rVertAlign rPr = do extraInfo <- extraRunStyleInfo rPr transform <- local (\e -> e{docxCustomStyleAlready = True}) $ runStyleToTransform rPr {rVertAlign = Nothing} - return $ superscript . extraInfo . transform + return $ extraInfo . superscript . transform | Just SubScrpt <- rVertAlign rPr = do extraInfo <- extraRunStyleInfo rPr transform <- local (\e -> e{docxCustomStyleAlready = True}) $ runStyleToTransform rPr {rVertAlign = Nothing} - return $ subscript . extraInfo . transform + return $ extraInfo . subscript . transform | Just "single" <- rUnderline rPr = do extraInfo <- extraRunStyleInfo rPr transform <- local (\e -> e{docxCustomStyleAlready = True}) $ runStyleToTransform rPr {rUnderline = Nothing} - return $ underlineSpan . extraInfo . transform + return $ extraInfo . underlineSpan . transform | otherwise = extraRunStyleInfo rPr runToInlines :: PandocMonad m => Run -> DocxContext m Inlines -- cgit v1.2.3 From 69a72099305ff4efe10f8bc5dbab103f1302b2ad Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Tue, 20 Feb 2018 16:22:32 -0500 Subject: Presentations: Use divs to set incremental/non-incremental Currently, html and beamer presentations use a list-inside-blockquote convention for setting incremental and all-at-once presentation of lists (or reversing the command-line default). This allows the user to set this on a per-case basis with divs, named `incremental` and `nonincremental` respectively, as in: ::: incremental - snap - crackle - pop ::: Note that the former list-inside-blockquote convention still works so as not to break any existing presentations. Closes: #4381 --- src/Text/Pandoc/Writers/HTML.hs | 23 +++++--- src/Text/Pandoc/Writers/LaTeX.hs | 113 +++++++++++++++++++++++---------------- 2 files changed, 83 insertions(+), 53 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index cbceae2ce..1647df7ea 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -2,6 +2,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ViewPatterns #-} +{-# LANGUAGE MultiWayIf #-} {- Copyright (C) 2006-2018 John MacFarlane <jgm@berkeley.edu> @@ -678,23 +679,31 @@ blockToHtml opts (LineBlock lns) = return $ H.div ! A.class_ "line-block" $ htmlLines blockToHtml opts (Div attr@(ident, classes, kvs') bs) = do html5 <- gets stHtml5 + slideVariant <- gets stSlideVariant let kvs = [(k,v) | (k,v) <- kvs', k /= "width"] ++ [("style", "width:" ++ w ++ ";") | ("width",w) <- kvs', "column" `elem` classes] 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 <- if "columns" `elem` classes + let opts' = if | speakerNotes -> opts{ writerIncremental = False } + | "incremental" `elem` classes -> opts{ writerIncremental = True } + | "nonincremental" `elem` classes -> opts{ writerIncremental = False } + | otherwise -> opts + -- we remove "incremental" and "nonincremental" if we're in a + -- slide presentaiton format. + classes' = case slideVariant of + NoSlides -> classes + _ -> filter (\k -> k /= "incremental" && k /= "nonincremental") classes + contents <- if "columns" `elem` classes' then -- we don't use blockListToHtml because it inserts -- a newline between the column divs, which throws -- off widths! see #4028 mconcat <$> mapM (blockToHtml opts) bs else blockListToHtml opts' bs let contents' = nl opts >> contents >> nl opts - let (divtag, classes') = if html5 && "section" `elem` classes - then (H5.section, filter (/= "section") classes) - else (H.div, classes) - slideVariant <- gets stSlideVariant + let (divtag, classes'') = if html5 && "section" `elem` classes' + then (H5.section, filter (/= "section") classes') + else (H.div, classes') if speakerNotes then case slideVariant of RevealJsSlides -> addAttrs opts' attr $ @@ -706,7 +715,7 @@ blockToHtml opts (Div attr@(ident, classes, kvs') bs) = do NoSlides -> addAttrs opts' attr $ H.div contents' _ -> return mempty - else addAttrs opts (ident, classes', kvs) $ + else addAttrs opts (ident, classes'', kvs) $ divtag contents' blockToHtml opts (RawBlock f str) = do ishtml <- isRawHtml f diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index c94d256f5..f61c878e5 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -442,54 +442,75 @@ blockToLaTeX :: PandocMonad m => Block -- ^ Block to convert -> LW m Doc blockToLaTeX Null = return empty -blockToLaTeX (Div (identifier,classes,kvs) bs) = do - beamer <- gets stBeamer - linkAnchor' <- hypertarget True identifier empty - -- see #2704 for the motivation for adding \leavevmode: - let linkAnchor = - case bs of - Para _ : _ - | not (isEmpty linkAnchor') - -> "\\leavevmode" <> linkAnchor' <> "%" - _ -> linkAnchor' - let align dir txt = inCmd "begin" dir $$ txt $$ inCmd "end" dir - lang <- toLang $ lookup "lang" kvs - let wrapColumns = if "columns" `elem` classes - then \contents -> - inCmd "begin" "columns" <> brackets "T" - $$ contents - $$ inCmd "end" "columns" - else id - wrapColumn = if "column" `elem` classes - then \contents -> - let fromPct xs = - case reverse xs of - '%':ds -> '0':'.': reverse ds - _ -> xs - w = maybe "0.48" fromPct (lookup "width" kvs) - in inCmd "begin" "column" <> - braces (text w <> "\\textwidth") - $$ contents - $$ inCmd "end" "column" - else id - wrapDir = case lookup "dir" kvs of - Just "rtl" -> align "RTL" - Just "ltr" -> align "LTR" - _ -> id - wrapLang txt = case lang of - Just lng -> let (l, o) = toPolyglossiaEnv lng - ops = if null o - then "" - else brackets $ text o - in inCmd "begin" (text l) <> ops - $$ blankline <> txt <> blankline - $$ inCmd "end" (text l) - Nothing -> txt - wrapNotes txt = if beamer && "notes" `elem` classes +blockToLaTeX (Div (identifier,classes,kvs) bs) + | "incremental" `elem` classes = do + let classes' = filter ("incremental"/=) classes + beamer <- gets stBeamer + if beamer + then do oldIncremental <- gets stIncremental + modify $ \s -> s{ stIncremental = True } + result <- blockToLaTeX $ Div (identifier,classes',kvs) bs + modify $ \s -> s{ stIncremental = oldIncremental } + return result + else blockToLaTeX $ Div (identifier,classes',kvs) bs + | "nonincremental" `elem` classes = do + let classes' = filter ("nonincremental"/=) classes + beamer <- gets stBeamer + if beamer + then do oldIncremental <- gets stIncremental + modify $ \s -> s{ stIncremental = False } + result <- blockToLaTeX $ Div (identifier,classes',kvs) bs + modify $ \s -> s{ stIncremental = oldIncremental } + return result + else blockToLaTeX $ Div (identifier,classes',kvs) bs + | otherwise = do + beamer <- gets stBeamer + linkAnchor' <- hypertarget True identifier empty + -- see #2704 for the motivation for adding \leavevmode: + let linkAnchor = + case bs of + Para _ : _ + | not (isEmpty linkAnchor') + -> "\\leavevmode" <> linkAnchor' <> "%" + _ -> linkAnchor' + let align dir txt = inCmd "begin" dir $$ txt $$ inCmd "end" dir + lang <- toLang $ lookup "lang" kvs + let wrapColumns = if "columns" `elem` classes + then \contents -> + inCmd "begin" "columns" <> brackets "T" + $$ contents + $$ inCmd "end" "columns" + else id + wrapColumn = if "column" `elem` classes + then \contents -> + let fromPct xs = + case reverse xs of + '%':ds -> '0':'.': reverse ds + _ -> xs + w = maybe "0.48" fromPct (lookup "width" kvs) + in inCmd "begin" "column" <> + braces (text w <> "\\textwidth") + $$ contents + $$ inCmd "end" "column" + else id + wrapDir = case lookup "dir" kvs of + Just "rtl" -> align "RTL" + Just "ltr" -> align "LTR" + _ -> id + wrapLang txt = case lang of + Just lng -> let (l, o) = toPolyglossiaEnv lng + ops = if null o + then "" + else brackets $ text o + in inCmd "begin" (text l) <> ops + $$ blankline <> txt <> blankline + $$ inCmd "end" (text l) + Nothing -> txt + wrapNotes txt = if beamer && "notes" `elem` classes then "\\note" <> braces txt -- speaker notes else linkAnchor $$ txt - (wrapColumns . wrapColumn . wrapDir . wrapLang . wrapNotes) - <$> blockListToLaTeX bs + (wrapColumns . wrapColumn . wrapDir . wrapLang . wrapNotes) + <$> blockListToLaTeX bs blockToLaTeX (Plain lst) = inlineListToLaTeX $ dropWhile isLineBreakOrSpace lst -- title beginning with fig: indicates that the image is a figure -- cgit v1.2.3 From e810a5cc009aba006ea10a00ed9ac0e308f08ca5 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 23 Feb 2018 17:56:55 +0300 Subject: Export improved sepBy1 from Text.Pandoc.Parsing --- src/Text/Pandoc/Parsing.hs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Parsing.hs b/src/Text/Pandoc/Parsing.hs index 1b66aa430..82abcb440 100644 --- a/src/Text/Pandoc/Parsing.hs +++ b/src/Text/Pandoc/Parsing.hs @@ -41,6 +41,7 @@ module Text.Pandoc.Parsing ( takeWhileP, indentWith, many1Till, manyUntil, + sepBy1', notFollowedBy', oneOfStrings, oneOfStringsCI, @@ -340,6 +341,14 @@ manyUntil p end = scan (xs, e) <- scan return (x:xs, e)) +-- | Like @sepBy1@ from Parsec, +-- but does not fail if it @sep@ succeeds and @p@ fails. +sepBy1' :: (Stream s m t) + => ParsecT s u m a + -> ParsecT s u m sep + -> ParsecT s u m [a] +sepBy1' p sep = (:) <$> p <*> many (try $ sep >> p) + -- | A more general form of @notFollowedBy@. This one allows any -- type of parser to be specified, and succeeds only if that parser fails. -- It does not consume any input. @@ -546,8 +555,8 @@ emailAddress :: Stream s m Char => ParserT s st m (String, String) emailAddress = try $ toResult <$> mailbox <*> (char '@' *> domain) where toResult mbox dom = let full = fromEntities $ mbox ++ '@':dom in (full, escapeURI $ "mailto:" ++ full) - mailbox = intercalate "." <$> (emailWord `sepby1` dot) - domain = intercalate "." <$> (subdomain `sepby1` dot) + mailbox = intercalate "." <$> (emailWord `sepBy1'` dot) + domain = intercalate "." <$> (subdomain `sepBy1'` dot) dot = char '.' subdomain = many1 $ alphaNum <|> innerPunct -- this excludes some valid email addresses, since an @@ -564,9 +573,6 @@ emailAddress = try $ toResult <$> mailbox <*> (char '@' *> domain) return (x:xs) isEmailChar c = isAlphaNum c || isEmailPunct c isEmailPunct c = c `elem` "!\"#$%&'*+-/=?^_{|}~;" - -- note: sepBy1 from parsec consumes input when sep - -- succeeds and p fails, so we use this variant here. - sepby1 p sep = (:) <$> p <*> many (try $ sep >> p) uriScheme :: Stream s m Char => ParserT s st m String -- cgit v1.2.3 From 2eab8f465410db57a7df27631a83058f8f480d89 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Wed, 14 Feb 2018 13:41:05 +0300 Subject: Muse reader: improve verse parsing Now verse marked up with ">" (in contrast to <verse> tag) can be placed inside lists. --- src/Text/Pandoc/Readers/Muse.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index e89a89b8f..1fb37aa16 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -521,7 +521,8 @@ blanklineVerseLine = try $ do lineBlock :: PandocMonad m => MuseParser m (F Blocks) lineBlock = try $ do - lns <- many1 (blanklineVerseLine <|> lineVerseLine) + col <- sourceColumn <$> getPosition + lns <- (blanklineVerseLine <|> lineVerseLine) `sepBy1'` try (indentWith (col - 1)) return $ B.lineBlock <$> sequence lns -- -- cgit v1.2.3 From 03d86969919e811f0803e1a394ea291fe1471380 Mon Sep 17 00:00:00 2001 From: "laptop1\\Andrew" <andrewjpritchard@gmail.com> Date: Fri, 23 Feb 2018 23:15:20 +0800 Subject: Docx writer: fix #3930 Fixes an issuue regarding image sizing if the same image is included more than once. Previously, a record was kept, indexed by image source, which would include the XML for that image. For every image element in the document, this record was checked, and if the image was the same as previous examples, the same XML would be included twice. The information in this XML incudes the image description, title text, and size on the page, thus all images from the same source would always be sized the same, and have the same description. This commit fixes this by generating unique XML every time, but keeping the image ID and path if it is the same image. --- src/Text/Pandoc/Writers/Docx.hs | 184 ++++++++++++++++++++++------------------ 1 file changed, 100 insertions(+), 84 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index 5ad6bf82b..dd0df6828 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -123,7 +123,7 @@ data WriterState = WriterState{ , stComments :: [([(String,String)], [Inline])] , stSectionIds :: Set.Set String , stExternalLinks :: M.Map String String - , stImages :: M.Map FilePath (String, String, Maybe MimeType, Element, B.ByteString) + , stImages :: M.Map FilePath (String, String, Maybe MimeType, B.ByteString) , stLists :: [ListMarker] , stInsId :: Int , stDelId :: Int @@ -294,7 +294,7 @@ writeDocx opts doc@(Pandoc meta _) = do let imgs = M.elems $ stImages st -- create entries for images in word/media/... - let toImageEntry (_,path,_,_,img) = toEntry ("word/" ++ path) epochtime $ toLazy img + let toImageEntry (_,path,_,img) = toEntry ("word/" ++ path) epochtime $ toLazy img let imageEntries = map toImageEntry imgs let stdAttributes = @@ -326,7 +326,7 @@ writeDocx opts doc@(Pandoc meta _) = do -- [Content_Types].xml let mkOverrideNode (part', contentType') = mknode "Override" [("PartName",part'),("ContentType",contentType')] () - let mkImageOverride (_, imgpath, mbMimeType, _, _) = + let mkImageOverride (_, imgpath, mbMimeType, _) = mkOverrideNode ("/word/" ++ imgpath, fromMaybe "application/octet-stream" mbMimeType) let mkMediaOverride imgpath = @@ -407,7 +407,7 @@ writeDocx opts doc@(Pandoc meta _) = do let renumHeaders = renumIds (\q -> qName q == "Id") idMap headers let renumFooters = renumIds (\q -> qName q == "Id") idMap footers let baserels = baserels' ++ renumHeaders ++ renumFooters - let toImgRel (ident,path,_,_,_) = mknode "Relationship" [("Type","http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"),("Id",ident),("Target",path)] () + let toImgRel (ident,path,_,_) = mknode "Relationship" [("Type","http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"),("Id",ident),("Target",path)] () let imgrels = map toImgRel imgs let toLinkRel (src,ident) = mknode "Relationship" [("Type","http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"),("Id",ident),("Target",src),("TargetMode","External") ] () let linkrels = map toLinkRel $ M.toList $ stExternalLinks st @@ -1275,87 +1275,103 @@ inlineToOpenXML' opts (Link _ txt (src,_)) = do return i return [ mknode "w:hyperlink" [("r:id",id')] contents ] inlineToOpenXML' opts (Image attr alt (src, title)) = do - -- first, check to see if we've already done this image pageWidth <- asks envPrintWidth - imgs <- gets stImages - case M.lookup src imgs of - Just (_,_,_,elt,_) -> return [elt] - Nothing -> - catchError - (do (img, mt) <- P.fetchItem src - ident <- ("rId"++) `fmap` getUniqueId - let (xpt,ypt) = desiredSizeInPoints opts attr - (either (const def) id (imageSize opts img)) - -- 12700 emu = 1 pt - let (xemu,yemu) = fitToPage (xpt * 12700, ypt * 12700) - (pageWidth * 12700) - let cNvPicPr = mknode "pic:cNvPicPr" [] $ - mknode "a:picLocks" [("noChangeArrowheads","1") - ,("noChangeAspect","1")] () - let nvPicPr = mknode "pic:nvPicPr" [] - [ mknode "pic:cNvPr" - [("descr",src),("id","0"),("name","Picture")] () - , cNvPicPr ] - let blipFill = mknode "pic:blipFill" [] - [ mknode "a:blip" [("r:embed",ident)] () - , mknode "a:stretch" [] $ - mknode "a:fillRect" [] () ] - let xfrm = mknode "a:xfrm" [] - [ mknode "a:off" [("x","0"),("y","0")] () - , mknode "a:ext" [("cx",show xemu) - ,("cy",show yemu)] () ] - let prstGeom = mknode "a:prstGeom" [("prst","rect")] $ - mknode "a:avLst" [] () - let ln = mknode "a:ln" [("w","9525")] - [ mknode "a:noFill" [] () - , mknode "a:headEnd" [] () - , mknode "a:tailEnd" [] () ] - let spPr = mknode "pic:spPr" [("bwMode","auto")] - [xfrm, prstGeom, mknode "a:noFill" [] (), ln] - let graphic = mknode "a:graphic" [] $ - mknode "a:graphicData" - [("uri","http://schemas.openxmlformats.org/drawingml/2006/picture")] - [ mknode "pic:pic" [] - [ nvPicPr - , blipFill - , spPr ] ] - let imgElt = mknode "w:r" [] $ - mknode "w:drawing" [] $ - mknode "wp:inline" [] - [ mknode "wp:extent" [("cx",show xemu),("cy",show yemu)] () - , mknode "wp:effectExtent" - [("b","0"),("l","0"),("r","0"),("t","0")] () - , mknode "wp:docPr" [("descr",stringify alt) - ,("title", title) - ,("id","1") - ,("name","Picture")] () - , graphic ] - let imgext = case mt >>= extensionFromMimeType of - Just x -> '.':x - Nothing -> case imageType img of - Just Png -> ".png" - Just Jpeg -> ".jpeg" - Just Gif -> ".gif" - Just Pdf -> ".pdf" - Just Eps -> ".eps" - Just Svg -> ".svg" - Just Emf -> ".emf" - Nothing -> "" - if null imgext - then -- without an extension there is no rule for content type - inlinesToOpenXML opts alt -- return alt to avoid corrupted docx - else do - let imgpath = "media/" ++ ident ++ imgext - let mbMimeType = mt <|> getMimeType imgpath - -- insert mime type to use in constructing [Content_Types].xml - modify $ \st -> st{ stImages = - M.insert src (ident, imgpath, mbMimeType, imgElt, img) - $ stImages st } - return [imgElt]) - (\e -> do - report $ CouldNotFetchResource src (show e) - -- emit alt text - inlinesToOpenXML opts alt) + imgs <- gets stImages + let + stImage = M.lookup src imgs + generateImgElt (ident, _, _, img) = + let + (xpt,ypt) = desiredSizeInPoints opts attr + (either (const def) id (imageSize opts img)) + -- 12700 emu = 1 pt + (xemu,yemu) = fitToPage (xpt * 12700, ypt * 12700) + (pageWidth * 12700) + cNvPicPr = mknode "pic:cNvPicPr" [] $ + mknode "a:picLocks" [("noChangeArrowheads","1") + ,("noChangeAspect","1")] () + nvPicPr = mknode "pic:nvPicPr" [] + [ mknode "pic:cNvPr" + [("descr",src),("id","0"),("name","Picture")] () + , cNvPicPr ] + blipFill = mknode "pic:blipFill" [] + [ mknode "a:blip" [("r:embed",ident)] () + , mknode "a:stretch" [] $ + mknode "a:fillRect" [] () + ] + xfrm = mknode "a:xfrm" [] + [ mknode "a:off" [("x","0"),("y","0")] () + , mknode "a:ext" [("cx",show xemu) + ,("cy",show yemu)] () ] + prstGeom = mknode "a:prstGeom" [("prst","rect")] $ + mknode "a:avLst" [] () + ln = mknode "a:ln" [("w","9525")] + [ mknode "a:noFill" [] () + , mknode "a:headEnd" [] () + , mknode "a:tailEnd" [] () ] + spPr = mknode "pic:spPr" [("bwMode","auto")] + [xfrm, prstGeom, mknode "a:noFill" [] (), ln] + graphic = mknode "a:graphic" [] $ + mknode "a:graphicData" + [("uri","http://schemas.openxmlformats.org/drawingml/2006/picture")] + [ mknode "pic:pic" [] + [ nvPicPr + , blipFill + , spPr + ] + ] + imgElt = mknode "w:r" [] $ + mknode "w:drawing" [] $ + mknode "wp:inline" [] + [ mknode "wp:extent" [("cx",show xemu),("cy",show yemu)] () + , mknode "wp:effectExtent" + [("b","0"),("l","0"),("r","0"),("t","0")] () + , mknode "wp:docPr" + [ ("descr", stringify alt) + , ("title", title) + , ("id","1") + , ("name","Picture") + ] () + , graphic + ] + in + imgElt + + case stImage of + Just imgData -> return $ [generateImgElt imgData] + Nothing -> ( do --try + (img, mt) <- P.fetchItem src + ident <- ("rId"++) `fmap` getUniqueId + + let + imgext = case mt >>= extensionFromMimeType of + Just x -> '.':x + Nothing -> case imageType img of + Just Png -> ".png" + Just Jpeg -> ".jpeg" + Just Gif -> ".gif" + Just Pdf -> ".pdf" + Just Eps -> ".eps" + Just Svg -> ".svg" + Just Emf -> ".emf" + Nothing -> "" + imgpath = "media/" ++ ident ++ imgext + mbMimeType = mt <|> getMimeType imgpath + + imgData = (ident, imgpath, mbMimeType, img) + + if null imgext + then -- without an extension there is no rule for content type + inlinesToOpenXML opts alt -- return alt to avoid corrupted docx + else do + -- insert mime type to use in constructing [Content_Types].xml + modify $ \st -> st { stImages = M.insert src imgData $ stImages st } + return [generateImgElt imgData] + ) + `catchError` ( \e -> do + report $ CouldNotFetchResource src (show e) + -- emit alt text + inlinesToOpenXML opts alt + ) br :: Element br = breakElement "textWrapping" -- cgit v1.2.3 From 788cb6e9a19d575666436197e77e8e17679753ed Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Fri, 23 Feb 2018 11:54:00 -0500 Subject: Docx writer: trim trailing whitespace. --- src/Text/Pandoc/Writers/Docx.hs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs index dd0df6828..4542389a2 100644 --- a/src/Text/Pandoc/Writers/Docx.hs +++ b/src/Text/Pandoc/Writers/Docx.hs @@ -1276,8 +1276,8 @@ inlineToOpenXML' opts (Link _ txt (src,_)) = do return [ mknode "w:hyperlink" [("r:id",id')] contents ] inlineToOpenXML' opts (Image attr alt (src, title)) = do pageWidth <- asks envPrintWidth - imgs <- gets stImages - let + imgs <- gets stImages + let stImage = M.lookup src imgs generateImgElt (ident, _, _, img) = let @@ -1296,7 +1296,7 @@ inlineToOpenXML' opts (Image attr alt (src, title)) = do blipFill = mknode "pic:blipFill" [] [ mknode "a:blip" [("r:embed",ident)] () , mknode "a:stretch" [] $ - mknode "a:fillRect" [] () + mknode "a:fillRect" [] () ] xfrm = mknode "a:xfrm" [] [ mknode "a:off" [("x","0"),("y","0")] () @@ -1316,7 +1316,7 @@ inlineToOpenXML' opts (Image attr alt (src, title)) = do [ mknode "pic:pic" [] [ nvPicPr , blipFill - , spPr + , spPr ] ] imgElt = mknode "w:r" [] $ @@ -1325,24 +1325,24 @@ inlineToOpenXML' opts (Image attr alt (src, title)) = do [ mknode "wp:extent" [("cx",show xemu),("cy",show yemu)] () , mknode "wp:effectExtent" [("b","0"),("l","0"),("r","0"),("t","0")] () - , mknode "wp:docPr" + , mknode "wp:docPr" [ ("descr", stringify alt) , ("title", title) , ("id","1") , ("name","Picture") ] () , graphic - ] + ] in - imgElt - + imgElt + case stImage of Just imgData -> return $ [generateImgElt imgData] - Nothing -> ( do --try + Nothing -> ( do --try (img, mt) <- P.fetchItem src ident <- ("rId"++) `fmap` getUniqueId - - let + + let imgext = case mt >>= extensionFromMimeType of Just x -> '.':x Nothing -> case imageType img of @@ -1353,12 +1353,12 @@ inlineToOpenXML' opts (Image attr alt (src, title)) = do Just Eps -> ".eps" Just Svg -> ".svg" Just Emf -> ".emf" - Nothing -> "" + Nothing -> "" imgpath = "media/" ++ ident ++ imgext mbMimeType = mt <|> getMimeType imgpath - + imgData = (ident, imgpath, mbMimeType, img) - + if null imgext then -- without an extension there is no rule for content type inlinesToOpenXML opts alt -- return alt to avoid corrupted docx -- cgit v1.2.3 From 8f8f0f8a603a75ea56068f65ef6e13c2c66a8402 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 23 Feb 2018 22:07:30 +0300 Subject: Muse writer: don't indent nested definition lists --- src/Text/Pandoc/Writers/Muse.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index 7f53e202d..b4eb19ef6 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -207,7 +207,9 @@ blockToMuse (BulletList items) = do return $ hang 2 "- " contents blockToMuse (DefinitionList items) = do contents <- mapM definitionListItemToMuse items - return $ cr $$ nest 1 (vcat contents) $$ blankline + -- ensure that sublists have preceding blank line + topLevel <- gets stTopLevel + return $ cr $$ (if topLevel then nest 1 else id) (vcat contents) $$ blankline where definitionListItemToMuse :: PandocMonad m => ([Inline], [[Block]]) -> StateT WriterState m Doc -- cgit v1.2.3 From 5ada5cceaceb05316dbb7241bca7d3effb4d9767 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Fri, 23 Feb 2018 14:23:14 -0500 Subject: Docx reader: Don't look up dependant run styles if +styles is enabled. It makes more sense not to interpret -- otherwise using the original document as the reference-doc would produce two of everything: the interpreted version and the uninterpreted style version. --- src/Text/Pandoc/Readers/Docx.hs | 75 +++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 36 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index 5f2ca0fff..f1683a394 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -252,33 +252,36 @@ parPartToString _ = "" blacklistedCharStyles :: [String] blacklistedCharStyles = ["Hyperlink"] -resolveDependentRunStyle :: RunStyle -> RunStyle +resolveDependentRunStyle :: PandocMonad m => RunStyle -> DocxContext m RunStyle resolveDependentRunStyle rPr | Just (s, _) <- rStyle rPr, s `elem` blacklistedCharStyles = - rPr - | Just (_, cs) <- rStyle rPr = - let rPr' = resolveDependentRunStyle cs - in - RunStyle { isBold = case isBold rPr of - Just bool -> Just bool - Nothing -> isBold rPr' - , isItalic = case isItalic rPr of - Just bool -> Just bool - Nothing -> isItalic rPr' - , isSmallCaps = case isSmallCaps rPr of - Just bool -> Just bool - Nothing -> isSmallCaps rPr' - , isStrike = case isStrike rPr of - Just bool -> Just bool - Nothing -> isStrike rPr' - , rVertAlign = case rVertAlign rPr of - Just valign -> Just valign - Nothing -> rVertAlign rPr' - , rUnderline = case rUnderline rPr of - Just ulstyle -> Just ulstyle - Nothing -> rUnderline rPr' - , rStyle = rStyle rPr } - | otherwise = rPr + return rPr + | Just (_, cs) <- rStyle rPr = do + opts <- asks docxOptions + if isEnabled Ext_styles opts + then return rPr + else do rPr' <- resolveDependentRunStyle cs + return $ + RunStyle { isBold = case isBold rPr of + Just bool -> Just bool + Nothing -> isBold rPr' + , isItalic = case isItalic rPr of + Just bool -> Just bool + Nothing -> isItalic rPr' + , isSmallCaps = case isSmallCaps rPr of + Just bool -> Just bool + Nothing -> isSmallCaps rPr' + , isStrike = case isStrike rPr of + Just bool -> Just bool + Nothing -> isStrike rPr' + , rVertAlign = case rVertAlign rPr of + Just valign -> Just valign + Nothing -> rVertAlign rPr' + , rUnderline = case rUnderline rPr of + Just ulstyle -> Just ulstyle + Nothing -> rUnderline rPr' + , rStyle = rStyle rPr } + | otherwise = return rPr extraRunStyleInfo :: PandocMonad m => RunStyle -> DocxContext m (Inlines -> Inlines) extraRunStyleInfo rPr @@ -337,18 +340,18 @@ runStyleToTransform rPr runToInlines :: PandocMonad m => Run -> DocxContext m Inlines runToInlines (Run rs runElems) | Just (s, _) <- rStyle rs - , s `elem` codeStyles = - let rPr = resolveDependentRunStyle rs - codeString = code $ concatMap runElemToString runElems - in - return $ case rVertAlign rPr of - Just SupScrpt -> superscript codeString - Just SubScrpt -> subscript codeString - _ -> codeString + , s `elem` codeStyles = do + rPr <- resolveDependentRunStyle rs + let codeString = code $ concatMap runElemToString runElems + return $ case rVertAlign rPr of + Just SupScrpt -> superscript codeString + Just SubScrpt -> subscript codeString + _ -> codeString | otherwise = do - let ils = smushInlines (map runElemToInlines runElems) - transform <- runStyleToTransform $ resolveDependentRunStyle rs - return $ transform ils + rPr <- resolveDependentRunStyle rs + let ils = smushInlines (map runElemToInlines runElems) + transform <- runStyleToTransform rPr + return $ transform ils runToInlines (Footnote bps) = do blksList <- smushBlocks <$> mapM bodyPartToBlocks bps return $ note blksList -- cgit v1.2.3 From 3e10caad63b8886a8eff9d0372c7c68eb57f4610 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Fri, 23 Feb 2018 14:47:52 -0500 Subject: Docx reader: simplify custom-style reading code. --- src/Text/Pandoc/Readers/Docx.hs | 64 +++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 41 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index f1683a394..724450c2c 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -142,11 +142,10 @@ instance Default DState where data DEnv = DEnv { docxOptions :: ReaderOptions , docxInHeaderBlock :: Bool - , docxCustomStyleAlready :: Bool } instance Default DEnv where - def = DEnv def False False + def = DEnv def False type DocxContext m = ReaderT DEnv (StateT DState m) @@ -283,16 +282,6 @@ resolveDependentRunStyle rPr , rStyle = rStyle rPr } | otherwise = return rPr -extraRunStyleInfo :: PandocMonad m => RunStyle -> DocxContext m (Inlines -> Inlines) -extraRunStyleInfo rPr - | Just (s, _) <- rStyle rPr = do - already <- asks docxCustomStyleAlready - opts <- asks docxOptions - return $ if isEnabled Ext_styles opts && not already - then spanWith ("", [], [("custom-style", s)]) - else id - | otherwise = return id - runStyleToTransform :: PandocMonad m => RunStyle -> DocxContext m (Inlines -> Inlines) runStyleToTransform rPr | Just (s, _) <- rStyle rPr @@ -300,42 +289,35 @@ runStyleToTransform rPr let rPr' = rPr{rStyle = Nothing} transform <- runStyleToTransform rPr' return $ spanWith ("", [s], []) . transform + | Just (s, _) <- rStyle rPr = do + opts <- asks docxOptions + let extraInfo = if isEnabled Ext_styles opts + then spanWith ("", [], [("custom-style", s)]) + else id + transform <- runStyleToTransform rPr {rStyle = Nothing} + return $ extraInfo . transform | Just True <- isItalic rPr = do - extraInfo <- extraRunStyleInfo rPr - transform <- local (\e -> e{docxCustomStyleAlready = True}) $ - runStyleToTransform rPr {isItalic = Nothing} - return $ extraInfo . emph . transform + transform <- runStyleToTransform rPr {isItalic = Nothing} + return $ emph . transform | Just True <- isBold rPr = do - extraInfo <- extraRunStyleInfo rPr - transform <- local (\e -> e{docxCustomStyleAlready = True}) $ - runStyleToTransform rPr {isBold = Nothing} - return $ extraInfo . strong . transform + transform <- runStyleToTransform rPr {isBold = Nothing} + return $ strong . transform | Just True <- isSmallCaps rPr = do - extraInfo <- extraRunStyleInfo rPr - transform <- local (\e -> e{docxCustomStyleAlready = True}) $ - runStyleToTransform rPr {isSmallCaps = Nothing} - return $ extraInfo . smallcaps . transform + transform <- runStyleToTransform rPr {isSmallCaps = Nothing} + return $ smallcaps . transform | Just True <- isStrike rPr = do - extraInfo <- extraRunStyleInfo rPr - transform <- local (\e -> e{docxCustomStyleAlready = True}) $ - runStyleToTransform rPr {isStrike = Nothing} - return $ extraInfo . strikeout . transform + transform <- runStyleToTransform rPr {isStrike = Nothing} + return $ strikeout . transform | Just SupScrpt <- rVertAlign rPr = do - extraInfo <- extraRunStyleInfo rPr - transform <- local (\e -> e{docxCustomStyleAlready = True}) $ - runStyleToTransform rPr {rVertAlign = Nothing} - return $ extraInfo . superscript . transform + transform <- runStyleToTransform rPr {rVertAlign = Nothing} + return $ superscript . transform | Just SubScrpt <- rVertAlign rPr = do - extraInfo <- extraRunStyleInfo rPr - transform <- local (\e -> e{docxCustomStyleAlready = True}) $ - runStyleToTransform rPr {rVertAlign = Nothing} - return $ extraInfo . subscript . transform + transform <- runStyleToTransform rPr {rVertAlign = Nothing} + return $ subscript . transform | Just "single" <- rUnderline rPr = do - extraInfo <- extraRunStyleInfo rPr - transform <- local (\e -> e{docxCustomStyleAlready = True}) $ - runStyleToTransform rPr {rUnderline = Nothing} - return $ extraInfo . underlineSpan . transform - | otherwise = extraRunStyleInfo rPr + transform <- runStyleToTransform rPr {rUnderline = Nothing} + return $ underlineSpan . transform + | otherwise = return id runToInlines :: PandocMonad m => Run -> DocxContext m Inlines runToInlines (Run rs runElems) -- cgit v1.2.3 From 6de2c0710f2586e4925690583df07d50ce167725 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Fri, 23 Feb 2018 14:51:36 -0500 Subject: Docx reader: code cleanup. Make the code in `runStyleToTransform` a bit more consistent. --- src/Text/Pandoc/Readers/Docx.hs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs index 724450c2c..e58b0a905 100644 --- a/src/Text/Pandoc/Readers/Docx.hs +++ b/src/Text/Pandoc/Readers/Docx.hs @@ -286,36 +286,35 @@ runStyleToTransform :: PandocMonad m => RunStyle -> DocxContext m (Inlines -> In runStyleToTransform rPr | Just (s, _) <- rStyle rPr , s `elem` spansToKeep = do - let rPr' = rPr{rStyle = Nothing} - transform <- runStyleToTransform rPr' + transform <- runStyleToTransform rPr{rStyle = Nothing} return $ spanWith ("", [s], []) . transform | Just (s, _) <- rStyle rPr = do opts <- asks docxOptions let extraInfo = if isEnabled Ext_styles opts then spanWith ("", [], [("custom-style", s)]) else id - transform <- runStyleToTransform rPr {rStyle = Nothing} + transform <- runStyleToTransform rPr{rStyle = Nothing} return $ extraInfo . transform | Just True <- isItalic rPr = do - transform <- runStyleToTransform rPr {isItalic = Nothing} + transform <- runStyleToTransform rPr{isItalic = Nothing} return $ emph . transform | Just True <- isBold rPr = do - transform <- runStyleToTransform rPr {isBold = Nothing} + transform <- runStyleToTransform rPr{isBold = Nothing} return $ strong . transform | Just True <- isSmallCaps rPr = do - transform <- runStyleToTransform rPr {isSmallCaps = Nothing} + transform <- runStyleToTransform rPr{isSmallCaps = Nothing} return $ smallcaps . transform | Just True <- isStrike rPr = do - transform <- runStyleToTransform rPr {isStrike = Nothing} + transform <- runStyleToTransform rPr{isStrike = Nothing} return $ strikeout . transform | Just SupScrpt <- rVertAlign rPr = do - transform <- runStyleToTransform rPr {rVertAlign = Nothing} + transform <- runStyleToTransform rPr{rVertAlign = Nothing} return $ superscript . transform | Just SubScrpt <- rVertAlign rPr = do - transform <- runStyleToTransform rPr {rVertAlign = Nothing} + transform <- runStyleToTransform rPr{rVertAlign = Nothing} return $ subscript . transform | Just "single" <- rUnderline rPr = do - transform <- runStyleToTransform rPr {rUnderline = Nothing} + transform <- runStyleToTransform rPr{rUnderline = Nothing} return $ underlineSpan . transform | otherwise = return id -- cgit v1.2.3 From 39dd7c794bc881acd2030c07ddfb7b34842f19a3 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sat, 24 Feb 2018 02:37:35 +0300 Subject: Muse reader: allow single colon in definition list term --- src/Text/Pandoc/Readers/Muse.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 1fb37aa16..26da57883 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -625,7 +625,7 @@ definitionListItemsUntil indent end = where continuation = try $ do pos <- getPosition - term <- trimInlinesF . mconcat <$> manyTill (choice inlineList) (string "::") + term <- trimInlinesF . mconcat <$> manyTill (choice inlineList) (try $ string "::") (x, e) <- descriptionsUntil (sourceColumn pos) ((Right <$> try (optional blankline >> indentWith indent >> continuation)) <|> (Left <$> end)) let xx = do term' <- term -- cgit v1.2.3 From b5bd8a9461dc317ff61abec68feba4a86d39e9f2 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel <albert@zeitkraut.de> Date: Sat, 24 Feb 2018 21:59:50 +0100 Subject: Lua: register script name in global variable The name of the Lua script which is executed is made available in the global Lua variable `PANDOC_SCRIPT_FILE`, both for Lua filters and custom writers. Closes: #4393 --- src/Text/Pandoc/Lua.hs | 5 +++-- src/Text/Pandoc/Lua/Init.hs | 8 +++++++- src/Text/Pandoc/Writers/Custom.hs | 3 ++- 3 files changed, 12 insertions(+), 4 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 790be47d5..79955509d 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -37,7 +37,7 @@ import Foreign.Lua (FromLuaStack (peek), Lua, LuaException (..), import Text.Pandoc.Class (PandocIO) import Text.Pandoc.Definition (Pandoc) import Text.Pandoc.Lua.Filter (LuaFilter, walkMWithLuaFilter) -import Text.Pandoc.Lua.Init (runPandocLua) +import Text.Pandoc.Lua.Init (runPandocLua, registerScriptPath) import Text.Pandoc.Lua.Util (popValue) import Text.Pandoc.Options (ReaderOptions) import qualified Foreign.Lua as Lua @@ -55,11 +55,12 @@ runLuaFilter' :: ReaderOptions -> FilePath -> String runLuaFilter' ropts filterPath format pd = do registerFormat registerReaderOptions + registerScriptPath filterPath top <- Lua.gettop stat <- Lua.dofile filterPath if stat /= OK then do - luaErrMsg <- peek (-1) <* Lua.pop 1 + luaErrMsg <- popValue Lua.throwLuaError luaErrMsg else do newtop <- Lua.gettop diff --git a/src/Text/Pandoc/Lua/Init.hs b/src/Text/Pandoc/Lua/Init.hs index d1a26ebad..8fa228837 100644 --- a/src/Text/Pandoc/Lua/Init.hs +++ b/src/Text/Pandoc/Lua/Init.hs @@ -31,6 +31,7 @@ module Text.Pandoc.Lua.Init , runPandocLua , initLuaState , luaPackageParams + , registerScriptPath ) where import Control.Monad.Trans (MonadIO (..)) @@ -88,6 +89,11 @@ initLuaState luaPkgParams = do loadScriptFromDataDir (luaPkgDataDir luaPkgParams) "init.lua" putConstructorsInRegistry +registerScriptPath :: FilePath -> Lua () +registerScriptPath fp = do + Lua.push fp + Lua.setglobal "PANDOC_SCRIPT_FILE" + putConstructorsInRegistry :: Lua () putConstructorsInRegistry = do Lua.getglobal "pandoc" @@ -101,7 +107,7 @@ putConstructorsInRegistry = do Lua.pop 1 where constrsToReg :: Data a => a -> Lua () - constrsToReg = mapM_ putInReg . map showConstr . dataTypeConstrs . dataTypeOf + constrsToReg = mapM_ (putInReg . showConstr) . dataTypeConstrs . dataTypeOf putInReg :: String -> Lua () putInReg name = do diff --git a/src/Text/Pandoc/Writers/Custom.hs b/src/Text/Pandoc/Writers/Custom.hs index 37b44b646..3daa8d0cf 100644 --- a/src/Text/Pandoc/Writers/Custom.hs +++ b/src/Text/Pandoc/Writers/Custom.hs @@ -44,7 +44,7 @@ import Foreign.Lua.Api import Text.Pandoc.Class (PandocIO) import Text.Pandoc.Definition import Text.Pandoc.Error -import Text.Pandoc.Lua.Init (runPandocLua) +import Text.Pandoc.Lua.Init (runPandocLua, registerScriptPath) import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Lua.Util (addValue, dostring') import Text.Pandoc.Options @@ -106,6 +106,7 @@ writeCustom :: FilePath -> WriterOptions -> Pandoc -> PandocIO Text writeCustom luaFile opts doc@(Pandoc meta _) = do luaScript <- liftIO $ UTF8.readFile luaFile res <- runPandocLua $ do + registerScriptPath luaFile stat <- dostring' luaScript -- check for error in lua script (later we'll change the return type -- to handle this more gracefully): -- cgit v1.2.3 From 5ea43630f2d19bd20fd13c38dc23cc3b84c02e3c Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Mon, 26 Feb 2018 15:25:28 -0800 Subject: LaTeX reader: make --trace work. --- src/Text/Pandoc/Readers/LaTeX.hs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/LaTeX.hs b/src/Text/Pandoc/Readers/LaTeX.hs index cb70b6403..7ca1cb5a5 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -60,7 +60,7 @@ import Text.Pandoc.BCP47 (Lang (..), renderLang) import Text.Pandoc.Builder import Text.Pandoc.Class (PandocMonad, PandocPure, getResourcePath, lookupEnv, readFileFromDirs, report, setResourcePath, - setTranslations, translateTerm) + setTranslations, translateTerm, trace) import Text.Pandoc.Error (PandocError (PandocMacroLoop, PandocParseError, PandocParsecError)) import Text.Pandoc.Highlighting (fromListingsLanguage, languagesByExtension) import Text.Pandoc.ImageSize (numUnit, showFl) @@ -74,6 +74,7 @@ import Text.Pandoc.Shared import qualified Text.Pandoc.Translations as Translations import Text.Pandoc.Walk import Text.Parsec.Pos +import qualified Text.Pandoc.Builder as B -- for debugging: -- import Text.Pandoc.Extensions (getDefaultExtensions) @@ -2532,13 +2533,16 @@ addTableCaption = walkM go block :: PandocMonad m => LP m Blocks -block = (mempty <$ spaces1) +block = do + res <- (mempty <$ spaces1) <|> environment <|> include <|> macroDef <|> blockCommand <|> paragraph <|> grouped block + trace (take 60 $ show $ B.toList res) + return res blocks :: PandocMonad m => LP m Blocks blocks = mconcat <$> many block -- cgit v1.2.3 From cdbe45e8ee1c5b87516ad020584576a22fdb28f4 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Tue, 27 Feb 2018 09:09:45 -0500 Subject: Powerpoint writer: Remove empty slides Make sure there are no empty slides in the pptx output. Because of the way that slides were split, these could be accidentally produced by comments after images. When animations are added, there will be a way to add an empty slide with either incremental lists or pauses. Test outputs checked with MS PowerPoint (Office 2013, Windows 10, VBox). Both files have expected output and are not corrupted. --- src/Text/Pandoc/Writers/Powerpoint/Presentation.hs | 40 ++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs index ac7c86945..c818df124 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs @@ -76,6 +76,7 @@ import Data.Maybe (maybeToList, fromMaybe) import Text.Pandoc.Highlighting import qualified Data.Text as T import Control.Applicative ((<|>)) +import Data.Char (isSpace) import Skylighting data WriterEnv = WriterEnv { envMetadata :: Meta @@ -229,7 +230,6 @@ data Paragraph = Paragraph { paraProps :: ParaProps , paraElems :: [ParaElem] } deriving (Show, Eq) - data BulletType = Bullet | AutoNumbering ListAttributes deriving (Show, Eq) @@ -853,6 +853,41 @@ replaceAnchor (Run rProps s) return $ Run rProps' s replaceAnchor pe = return pe +emptyParaElem :: ParaElem -> Bool +emptyParaElem (Run _ s) = + null $ dropWhile isSpace $ reverse $ dropWhile isSpace $ reverse s +emptyParaElem (MathElem _ ts) = + null $ dropWhile isSpace $ reverse $ dropWhile isSpace $ reverse $ unTeXString ts +emptyParaElem _ = False + +emptyParagraph :: Paragraph -> Bool +emptyParagraph para = all emptyParaElem $ paraElems para + + +emptyShape :: Shape -> Bool +emptyShape (TextBox paras) = all emptyParagraph $ paras +emptyShape _ = False + +emptyLayout :: Layout -> Bool +emptyLayout layout = case layout of + MetadataSlide title subtitle authors date -> + all emptyParaElem title && + all emptyParaElem subtitle && + all (all emptyParaElem) authors && + all emptyParaElem date + TitleSlide hdr -> all emptyParaElem hdr + ContentSlide hdr shapes -> + all emptyParaElem hdr && + all emptyShape shapes + TwoColumnSlide hdr shapes1 shapes2 -> + all emptyParaElem hdr && + all emptyShape shapes1 && + all emptyShape shapes2 + +emptySlide :: Slide -> Bool +emptySlide (Slide _ layout Nothing) = emptyLayout layout +emptySlide _ = False + blocksToPresentationSlides :: [Block] -> Pres [Slide] blocksToPresentationSlides blks = do opts <- asks envOpts @@ -893,7 +928,8 @@ blocksToPresentationSlides blks = do return [endNotesSlide] let slides = metadataslides ++ tocSlides ++ bodyslides ++ endNotesSlides - mapM (applyToSlide replaceAnchor) slides + slides' = filter (not . emptySlide) slides + mapM (applyToSlide replaceAnchor) slides' metaToDocProps :: Meta -> DocProps metaToDocProps meta = -- cgit v1.2.3 From 9abcb4f2010348ae7d25a2199d8e7fcb91a6315d Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Tue, 27 Feb 2018 10:19:35 -0500 Subject: Powerpoint writer: Use table styles This will use the default table style in the reference-doc file. As a result they will be easier when using in a template, and match the color scheme. --- src/Text/Pandoc/Writers/Powerpoint/Output.hs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint/Output.hs b/src/Text/Pandoc/Writers/Powerpoint/Output.hs index b5138b514..410b6c20c 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Output.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Output.hs @@ -930,6 +930,13 @@ graphicFrameToElements layout tbls caption = do return [graphicFrameElts, capElt] else return [graphicFrameElts] +getDefaultTableStyle :: PandocMonad m => P m (Maybe String) +getDefaultTableStyle = do + refArchive <- asks envRefArchive + distArchive <- asks envDistArchive + tblStyleLst <- parseXml refArchive distArchive "ppt/tableStyles.xml" + return $ findAttr (QName "def" Nothing Nothing) tblStyleLst + graphicToElement :: PandocMonad m => Integer -> Graphic -> P m Element graphicToElement tableWidth (Tbl tblPr hdrCells rows) = do let colWidths = if null hdrCells @@ -967,12 +974,19 @@ graphicToElement tableWidth (Tbl tblPr hdrCells rows) = do let mkgridcol w = mknode "a:gridCol" [("w", show ((12700 * w) :: Integer))] () let hasHeader = not (all null hdrCells) + + mbDefTblStyle <- getDefaultTableStyle + let tblPrElt = mknode "a:tblPr" + [ ("firstRow", if tblPrFirstRow tblPr then "1" else "0") + , ("bandRow", if tblPrBandRow tblPr then "1" else "0") + ] (case mbDefTblStyle of + Nothing -> [] + Just sty -> [mknode "a:tableStyleId" [] sty]) + return $ mknode "a:graphic" [] $ [mknode "a:graphicData" [("uri", "http://schemas.openxmlformats.org/drawingml/2006/table")] $ [mknode "a:tbl" [] $ - [ mknode "a:tblPr" [ ("firstRow", if tblPrFirstRow tblPr then "1" else "0") - , ("bandRow", if tblPrBandRow tblPr then "1" else "0") - ] () + [ tblPrElt , mknode "a:tblGrid" [] (if all (==0) colWidths then [] else map mkgridcol colWidths) -- cgit v1.2.3 From 0287530a67dca95197ac59f215de84d6518170b6 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Tue, 27 Feb 2018 11:28:15 -0500 Subject: Powerpoint writer: use `trim` from Shared Instead of writing my own. --- src/Text/Pandoc/Writers/Powerpoint/Presentation.hs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs index c818df124..396469edd 100644 --- a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs +++ b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs @@ -76,7 +76,6 @@ import Data.Maybe (maybeToList, fromMaybe) import Text.Pandoc.Highlighting import qualified Data.Text as T import Control.Applicative ((<|>)) -import Data.Char (isSpace) import Skylighting data WriterEnv = WriterEnv { envMetadata :: Meta @@ -855,9 +854,9 @@ replaceAnchor pe = return pe emptyParaElem :: ParaElem -> Bool emptyParaElem (Run _ s) = - null $ dropWhile isSpace $ reverse $ dropWhile isSpace $ reverse s + null $ Shared.trim s emptyParaElem (MathElem _ ts) = - null $ dropWhile isSpace $ reverse $ dropWhile isSpace $ reverse $ unTeXString ts + null $ Shared.trim $ unTeXString ts emptyParaElem _ = False emptyParagraph :: Paragraph -> Bool -- cgit v1.2.3 From a7ac590b083f98bc6c98530077f0ed78e232bc0d Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Wed, 28 Feb 2018 12:11:56 +0300 Subject: Muse reader: allow <quote> and other tags to be indented --- src/Text/Pandoc/Readers/Muse.hs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 26da57883..d86c46aca 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -153,6 +153,7 @@ htmlElement tag = try $ do htmlBlock :: PandocMonad m => String -> MuseParser m (Attr, String) htmlBlock tag = try $ do + many spaceChar res <- htmlElement tag manyTill spaceChar eol return res @@ -166,7 +167,8 @@ htmlAttrToPandoc attrs = (ident, classes, keyvals) parseHtmlContent :: PandocMonad m => String -> MuseParser m (Attr, F Blocks) -parseHtmlContent tag = do +parseHtmlContent tag = try $ do + many spaceChar (TagOpen _ attr, _) <- htmlTag (~== TagOpen tag []) manyTill spaceChar eol content <- parseBlocksTill (manyTill spaceChar endtag) @@ -398,7 +400,6 @@ dropSpacePrefix lns = exampleTag :: PandocMonad m => MuseParser m (F Blocks) exampleTag = try $ do - many spaceChar (attr, contents) <- htmlBlock "example" return $ return $ B.codeBlockWith attr $ rchop $ intercalate "\n" $ dropSpacePrefix $ splitOn "\n" $ lchop contents -- cgit v1.2.3 From 1d57f7a641e9c66d56cab20905a602202336fbad Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Wed, 28 Feb 2018 14:26:11 +0300 Subject: Muse writer: remove empty strings during inline normalization --- src/Text/Pandoc/Writers/Muse.hs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index b4eb19ef6..4086bdd9c 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -299,6 +299,8 @@ conditionalEscapeString s = else s normalizeInlineList :: [Inline] -> [Inline] +normalizeInlineList (x : Str "" : xs) + = normalizeInlineList (x:xs) normalizeInlineList (Emph x1 : Emph x2 : ils) = normalizeInlineList $ Emph (x1 ++ x2) : ils normalizeInlineList (Strong x1 : Strong x2 : ils) -- cgit v1.2.3 From 8b1630aae029a0c4ce6e6dc881d3e11d0ca7e9ce Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Wed, 28 Feb 2018 14:42:43 +0300 Subject: Muse writer: change verse markup Use "> " instead of <verse> tag --- src/Text/Pandoc/Writers/Muse.hs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index 4086bdd9c..314e7a5c1 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -156,15 +156,8 @@ blockToMuse (Para inlines) = do contents <- inlineListToMuse inlines return $ contents <> blankline blockToMuse (LineBlock lns) = do - let splitStanza [] = [] - splitStanza xs = case break (== mempty) xs of - (l, []) -> [l] - (l, _:r) -> l : splitStanza r - let joinWithLinefeeds = nowrap . mconcat . intersperse cr - let joinWithBlankLines = mconcat . intersperse blankline - let prettyfyStanza ls = joinWithLinefeeds <$> mapM inlineListToMuse ls - contents <- joinWithBlankLines <$> mapM prettyfyStanza (splitStanza lns) - return $ blankline $$ "<verse>" $$ contents $$ "</verse>" <> blankline + lns' <- mapM inlineListToMuse lns + return $ nowrap $ vcat (map ((text "> ") <>) lns') <> blankline blockToMuse (CodeBlock (_,_,_) str) = return $ "<example>" $$ text str $$ "</example>" $$ blankline blockToMuse (RawBlock (Format format) str) = -- cgit v1.2.3 From 7d3e7a5a6d9e7b139fd15e10a52b85f87aba42b1 Mon Sep 17 00:00:00 2001 From: Jesse Rosenthal <jrosenthal@jhu.edu> Date: Wed, 28 Feb 2018 16:27:18 -0500 Subject: Docx reader: Handle nested sdt tags. Previously we had only unwrapped one level of sdt tags. Now we recurse if we find them. Closes: #4415 --- src/Text/Pandoc/Readers/Docx/Parse.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Docx/Parse.hs b/src/Text/Pandoc/Readers/Docx/Parse.hs index c123a0018..1f7f07e36 100644 --- a/src/Text/Pandoc/Readers/Docx/Parse.hs +++ b/src/Text/Pandoc/Readers/Docx/Parse.hs @@ -136,7 +136,7 @@ unwrapSDT :: NameSpaces -> Content -> [Content] unwrapSDT ns (Elem element) | isElem ns "w" "sdt" element , Just sdtContent <- findChildByName ns "w" "sdtContent" element - = map Elem $ elChildren sdtContent + = concatMap (unwrapSDT ns) $ map Elem $ elChildren sdtContent unwrapSDT _ content = [content] unwrapSDTchild :: NameSpaces -> Content -> Content -- cgit v1.2.3 From e881214bf9a3cc990110ae3a23aec1d6b4076f66 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Thu, 1 Mar 2018 14:33:18 +0300 Subject: Org writer: fix a typo s/prettyfy/prettify/ --- src/Text/Pandoc/Writers/Org.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Org.hs b/src/Text/Pandoc/Writers/Org.hs index 72def8e48..2307204a5 100644 --- a/src/Text/Pandoc/Writers/Org.hs +++ b/src/Text/Pandoc/Writers/Org.hs @@ -166,8 +166,8 @@ blockToOrg (LineBlock lns) = do (l, _:r) -> l : splitStanza r let joinWithLinefeeds = nowrap . mconcat . intersperse cr let joinWithBlankLines = mconcat . intersperse blankline - let prettyfyStanza ls = joinWithLinefeeds <$> mapM inlineListToOrg ls - contents <- joinWithBlankLines <$> mapM prettyfyStanza (splitStanza lns) + let prettifyStanza ls = joinWithLinefeeds <$> mapM inlineListToOrg ls + contents <- joinWithBlankLines <$> mapM prettifyStanza (splitStanza lns) return $ blankline $$ "#+BEGIN_VERSE" $$ nest 2 contents $$ "#+END_VERSE" <> blankline blockToOrg (RawBlock "html" str) = -- cgit v1.2.3 From 177c5120a5d13c7019d99a3b79d3cc0981a00214 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 2 Mar 2018 00:56:52 +0300 Subject: Muse reader: do not consume whitespace while looking for closing end tag Fix for a bug caught by round-trip test. --- src/Text/Pandoc/Readers/Muse.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index d86c46aca..6183f91cd 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -171,7 +171,7 @@ parseHtmlContent tag = try $ do many spaceChar (TagOpen _ attr, _) <- htmlTag (~== TagOpen tag []) manyTill spaceChar eol - content <- parseBlocksTill (manyTill spaceChar endtag) + content <- parseBlocksTill (try $ manyTill spaceChar endtag) manyTill spaceChar eol -- closing tag must be followed by optional whitespace and newline return (htmlAttrToPandoc attr, content) where -- cgit v1.2.3 From 9dbd59a7c120eee9a0bbe292a05dd144987fa0a0 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 2 Mar 2018 01:39:16 +0300 Subject: Muse writer: join strings during inline normalization --- src/Text/Pandoc/Writers/Muse.hs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index 314e7a5c1..bf1f267fd 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -294,6 +294,8 @@ conditionalEscapeString s = normalizeInlineList :: [Inline] -> [Inline] normalizeInlineList (x : Str "" : xs) = normalizeInlineList (x:xs) +normalizeInlineList (Str x1 : Str x2 : xs) + = normalizeInlineList $ Str (x1 ++ x2) : xs normalizeInlineList (Emph x1 : Emph x2 : ils) = normalizeInlineList $ Emph (x1 ++ x2) : ils normalizeInlineList (Strong x1 : Strong x2 : ils) -- cgit v1.2.3 From 64f8c5d99cb7040479d6baf8950ef991fa23acae Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 2 Mar 2018 12:50:36 +0300 Subject: Muse reader: remove space prefix from <literal> tag contents --- src/Text/Pandoc/Readers/Muse.hs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 6183f91cd..aaa54e65b 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -379,16 +379,13 @@ example = try $ do contents <- manyTill anyChar $ try (optional blankline >> string "}}}") return $ return $ B.codeBlock contents --- Trim up to one newline from the beginning and the end, --- in case opening and/or closing tags are on separate lines. -chop :: String -> String -chop = lchop . rchop - +-- Trim up to one newline from the beginning of the string. lchop :: String -> String lchop s = case s of '\n':ss -> ss _ -> s +-- Trim up to one newline from the end of the string. rchop :: String -> String rchop = reverse . lchop . reverse @@ -410,7 +407,7 @@ literalTag = do where -- FIXME: Emacs Muse inserts <literal> without style into all output formats, but we assume HTML format (_, _, kvs) = fromMaybe "html" $ lookup "style" kvs - rawBlock (attrs, content) = B.rawBlock (format attrs) $ chop content + rawBlock (attrs, content) = B.rawBlock (format attrs) $ rchop $ intercalate "\n" $ dropSpacePrefix $ splitOn "\n" $ lchop content -- <center> tag is ignored centerTag :: PandocMonad m => MuseParser m (F Blocks) -- cgit v1.2.3 From a01573692af91f54b5fe6670e4a07fb6a3935181 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 2 Mar 2018 12:52:39 +0300 Subject: Muse reader: enable <literal> tags even if amuse extension is enabled Amusewiki disables <literal> tags for security reasons. If user wants similar behavior in pandoc, RawBlocks and RawInlines can be removed or replaced with filters. --- src/Text/Pandoc/Readers/Muse.hs | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index aaa54e65b..c083933ff 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -402,7 +402,6 @@ exampleTag = try $ do literalTag :: PandocMonad m => MuseParser m (F Blocks) literalTag = do - guardDisabled Ext_amuse -- Text::Amuse does not support <literal> (return . rawBlock) <$> htmlBlock "literal" where -- FIXME: Emacs Muse inserts <literal> without style into all output formats, but we assume HTML @@ -880,7 +879,6 @@ codeTag = do inlineLiteralTag :: PandocMonad m => MuseParser m (F Inlines) inlineLiteralTag = do - guardDisabled Ext_amuse -- Text::Amuse does not support <literal> (return . rawInline) <$> htmlElement "literal" where -- FIXME: Emacs Muse inserts <literal> without style into all output formats, but we assume HTML -- cgit v1.2.3 From 991b57733c508634d6093e2aeb2ffb3feec13c42 Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Fri, 2 Mar 2018 19:51:54 +0300 Subject: hlint Muse reader and writer --- src/Text/Pandoc/Readers/Muse.hs | 4 ++-- src/Text/Pandoc/Writers/Muse.hs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index c083933ff..8bb087629 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -401,7 +401,7 @@ exampleTag = try $ do return $ return $ B.codeBlockWith attr $ rchop $ intercalate "\n" $ dropSpacePrefix $ splitOn "\n" $ lchop contents literalTag :: PandocMonad m => MuseParser m (F Blocks) -literalTag = do +literalTag = (return . rawBlock) <$> htmlBlock "literal" where -- FIXME: Emacs Muse inserts <literal> without style into all output formats, but we assume HTML @@ -878,7 +878,7 @@ codeTag = do return $ return $ B.codeWith attrs content inlineLiteralTag :: PandocMonad m => MuseParser m (F Inlines) -inlineLiteralTag = do +inlineLiteralTag = (return . rawInline) <$> htmlElement "literal" where -- FIXME: Emacs Muse inserts <literal> without style into all output formats, but we assume HTML diff --git a/src/Text/Pandoc/Writers/Muse.hs b/src/Text/Pandoc/Writers/Muse.hs index bf1f267fd..ad67e489d 100644 --- a/src/Text/Pandoc/Writers/Muse.hs +++ b/src/Text/Pandoc/Writers/Muse.hs @@ -157,7 +157,7 @@ blockToMuse (Para inlines) = do return $ contents <> blankline blockToMuse (LineBlock lns) = do lns' <- mapM inlineListToMuse lns - return $ nowrap $ vcat (map ((text "> ") <>) lns') <> blankline + return $ nowrap $ vcat (map (text "> " <>) lns') <> blankline blockToMuse (CodeBlock (_,_,_) str) = return $ "<example>" $$ text str $$ "</example>" $$ blankline blockToMuse (RawBlock (Format format) str) = -- cgit v1.2.3 From adefd86cd4c2273e9bf5fefe136c1aff1b13151a Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 2 Mar 2018 09:33:18 -0800 Subject: LaTeX reader: Fix regression in package options including underscore. Closes #4424. --- 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 7ca1cb5a5..57d2803ba 100644 --- a/src/Text/Pandoc/Readers/LaTeX.hs +++ b/src/Text/Pandoc/Readers/LaTeX.hs @@ -1162,7 +1162,7 @@ singleChar = try $ do else return $ Tok pos toktype t opt :: PandocMonad m => LP m Inlines -opt = bracketed inline +opt = bracketed inline <|> (str . T.unpack <$> rawopt) rawopt :: PandocMonad m => LP m Text rawopt = do -- cgit v1.2.3 From 7507117f150dd6d36835cf86ae0872b8af302af1 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 2 Mar 2018 16:29:04 -0800 Subject: Make `Ext_raw_html` default for commonmark format. --- src/Text/Pandoc/Extensions.hs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index fe690713c..968476930 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -317,6 +317,8 @@ getDefaultExtensions "muse" = extensionsFromList Ext_auto_identifiers] getDefaultExtensions "plain" = plainExtensions getDefaultExtensions "gfm" = githubMarkdownExtensions +getDefaultExtensions "commonmark" = extensionsFromList + [Ext_raw_html] getDefaultExtensions "org" = extensionsFromList [Ext_citations, Ext_auto_identifiers] -- cgit v1.2.3 From a71a1fec69104f35b3722e423a741dc68076f3db Mon Sep 17 00:00:00 2001 From: Alexander Krotov <ilabdsf@gmail.com> Date: Sat, 3 Mar 2018 03:32:37 +0300 Subject: Muse reader: fix indentation requirements for footnote continuations --- src/Text/Pandoc/Readers/Muse.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/Muse.hs b/src/Text/Pandoc/Readers/Muse.hs index 8bb087629..4a9523e84 100644 --- a/src/Text/Pandoc/Readers/Muse.hs +++ b/src/Text/Pandoc/Readers/Muse.hs @@ -470,10 +470,10 @@ amuseNoteBlockUntil :: PandocMonad m -> MuseParser m (F Blocks, a) amuseNoteBlockUntil end = try $ do guardEnabled Ext_amuse - pos <- getPosition ref <- noteMarker <* spaceChar + pos <- getPosition updateState (\st -> st { museInPara = False }) - (content, e) <- listItemContentsUntil (sourceColumn pos) (fail "x") end + (content, e) <- listItemContentsUntil (sourceColumn pos - 1) (fail "x") end oldnotes <- museNotes <$> getState case M.lookup ref oldnotes of Just _ -> logMessage $ DuplicateNoteReference ref pos -- cgit v1.2.3 From 6dd21250288b51f10056b15a83130f76c788d904 Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 2 Mar 2018 19:30:42 -0800 Subject: Commonmark reader: parse HTML as plain text if `-raw_html`. --- src/Text/Pandoc/Readers/CommonMark.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/CommonMark.hs b/src/Text/Pandoc/Readers/CommonMark.hs index 6fbc09c17..e5b8775c1 100644 --- a/src/Text/Pandoc/Readers/CommonMark.hs +++ b/src/Text/Pandoc/Readers/CommonMark.hs @@ -109,7 +109,7 @@ addBlock opts (Node _ BLOCK_QUOTE nodes) = (BlockQuote (addBlocks opts nodes) :) addBlock opts (Node _ (HTML_BLOCK t) _) | isEnabled Ext_raw_html opts = (RawBlock (Format "html") (unpack t) :) - | otherwise = id + | otherwise = (Para [Str (unpack t)] :) -- Note: the cmark parser will never generate CUSTOM_BLOCK, -- so we don't need to handle it: addBlock _ (Node _ (CUSTOM_BLOCK _onEnter _onExit) _nodes) = @@ -215,7 +215,7 @@ addInline opts (Node _ SOFTBREAK _) | otherwise = (SoftBreak :) addInline opts (Node _ (HTML_INLINE t) _) | isEnabled Ext_raw_html opts = (RawInline (Format "html") (unpack t) :) - | otherwise = id + | otherwise = (Str (unpack t) :) -- Note: the cmark parser will never generate CUSTOM_BLOCK, -- so we don't need to handle it: addInline _ (Node _ (CUSTOM_INLINE _onEnter _onExit) _nodes) = -- cgit v1.2.3 From 987140eadc0af868af55d6ac52414382eb1f855c Mon Sep 17 00:00:00 2001 From: John MacFarlane <jgm@berkeley.edu> Date: Fri, 2 Mar 2018 19:35:22 -0800 Subject: Revert "Commonmark reader: parse HTML as plain text if `-raw_html`." This reverts commit 6dd21250288b51f10056b15a83130f76c788d904. --- src/Text/Pandoc/Readers/CommonMark.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text') diff --git a/src/Text/Pandoc/Readers/CommonMark.hs b/src/Text/Pandoc/Readers/CommonMark.hs index e5b8775c1..6fbc09c17 100644 --- a/src/Text/Pandoc/Readers/CommonMark.hs +++ b/src/Text/Pandoc/Readers/CommonMark.hs @@ -109,7 +109,7 @@ addBlock opts (Node _ BLOCK_QUOTE nodes) = (BlockQuote (addBlocks opts nodes) :) addBlock opts (Node _ (HTML_BLOCK t) _) | isEnabled Ext_raw_html opts = (RawBlock (Format "html") (unpack t) :) - | otherwise = (Para [Str (unpack t)] :) + | otherwise = id -- Note: the cmark parser will never generate CUSTOM_BLOCK, -- so we don't need to handle it: addBlock _ (Node _ (CUSTOM_BLOCK _onEnter _onExit) _nodes) = @@ -215,7 +215,7 @@ addInline opts (Node _ SOFTBREAK _) | otherwise = (SoftBreak :) addInline opts (Node _ (HTML_INLINE t) _) | isEnabled Ext_raw_html opts = (RawInline (Format "html") (unpack t) :) - | otherwise = (Str (unpack t) :) + | otherwise = id -- Note: the cmark parser will never generate CUSTOM_BLOCK, -- so we don't need to handle it: addInline _ (Node _ (CUSTOM_INLINE _onEnter _onExit) _nodes) = -- cgit v1.2.3