aboutsummaryrefslogtreecommitdiff
path: root/src/Text/Pandoc/Writers/RST.hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Text/Pandoc/Writers/RST.hs')
-rw-r--r--src/Text/Pandoc/Writers/RST.hs197
1 files changed, 101 insertions, 96 deletions
diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs
index efe86e73b..5f035ee1f 100644
--- a/src/Text/Pandoc/Writers/RST.hs
+++ b/src/Text/Pandoc/Writers/RST.hs
@@ -1,5 +1,6 @@
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ViewPatterns #-}
{- |
Module : Text.Pandoc.Writers.RST
Copyright : Copyright (C) 2006-2019 John MacFarlane
@@ -16,8 +17,8 @@ reStructuredText: <http://docutils.sourceforge.net/rst.html>
module Text.Pandoc.Writers.RST ( writeRST, flatten ) where
import Prelude
import Control.Monad.State.Strict
-import Data.Char (isSpace, toLower)
-import Data.List (isPrefixOf, stripPrefix, transpose, intersperse)
+import Data.Char (isSpace)
+import Data.List (transpose, intersperse)
import Data.Maybe (fromMaybe)
import qualified Data.Text as T
import Data.Text (Text)
@@ -38,7 +39,7 @@ type Refs = [([Inline], Target)]
data WriterState =
WriterState { stNotes :: [[Block]]
, stLinks :: Refs
- , stImages :: [([Inline], (Attr, String, String, Maybe String))]
+ , stImages :: [([Inline], (Attr, Text, Text, Maybe Text))]
, stHasMath :: Bool
, stHasRawTeX :: Bool
, stOptions :: WriterOptions
@@ -81,7 +82,7 @@ pandocToRST (Pandoc meta blocks) = do
let main = vsep [body, notes, refs, pics]
let context = defField "body" main
$ defField "toc" (writerTableOfContents opts)
- $ defField "toc-depth" (T.pack $ show $ writerTOCDepth opts)
+ $ defField "toc-depth" (tshow $ writerTOCDepth opts)
$ defField "number-sections" (writerNumberSections opts)
$ defField "math" hasMath
$ defField "titleblock" (render Nothing title :: Text)
@@ -105,13 +106,13 @@ refsToRST :: PandocMonad m => Refs -> RST m (Doc Text)
refsToRST refs = mapM keyToRST refs >>= return . vcat
-- | Return RST representation of a reference key.
-keyToRST :: PandocMonad m => ([Inline], (String, String)) -> RST m (Doc Text)
+keyToRST :: PandocMonad m => ([Inline], (Text, Text)) -> RST m (Doc Text)
keyToRST (label, (src, _)) = do
label' <- inlineListToRST label
let label'' = if (==':') `T.any` (render Nothing label' :: Text)
then char '`' <> label' <> char '`'
else label'
- return $ nowrap $ ".. _" <> label'' <> ": " <> text src
+ return $ nowrap $ ".. _" <> label'' <> ": " <> literal src
-- | Return RST representation of notes.
notesToRST :: PandocMonad m => [[Block]] -> RST m (Doc Text)
@@ -128,13 +129,13 @@ noteToRST num note = do
-- | Return RST representation of picture reference table.
pictRefsToRST :: PandocMonad m
- => [([Inline], (Attr, String, String, Maybe String))]
+ => [([Inline], (Attr, Text, Text, Maybe Text))]
-> RST m (Doc Text)
pictRefsToRST refs = mapM pictToRST refs >>= return . vcat
-- | Return RST representation of a picture substitution reference.
pictToRST :: PandocMonad m
- => ([Inline], (Attr, String, String, Maybe String))
+ => ([Inline], (Attr, Text, Text, Maybe Text))
-> RST m (Doc Text)
pictToRST (label, (attr, src, _, mbtarget)) = do
label' <- inlineListToRST label
@@ -145,32 +146,32 @@ pictToRST (label, (attr, src, _, mbtarget)) = do
["align-right"] -> ":align: right"
["align-left"] -> ":align: left"
["align-center"] -> ":align: center"
- _ -> ":class: " <> text (unwords cls)
+ _ -> ":class: " <> literal (T.unwords cls)
return $ nowrap
- $ ".. |" <> label' <> "| image:: " <> text src $$ hang 3 empty (classes $$ dims)
+ $ ".. |" <> label' <> "| image:: " <> literal src $$ hang 3 empty (classes $$ dims)
$$ case mbtarget of
Nothing -> empty
- Just t -> " :target: " <> text t
+ Just t -> " :target: " <> literal t
-- | Escape special characters for RST.
-escapeString :: WriterOptions -> String -> String
-escapeString = escapeString' True
+escapeText :: WriterOptions -> Text -> Text
+escapeText o = T.pack . escapeString' True o . T.unpack -- This ought to be parser
where
escapeString' _ _ [] = []
escapeString' firstChar opts (c:cs) =
case c of
- _ | c `elem` ['\\','`','*','_','|'] &&
- (firstChar || null cs) -> '\\':c:escapeString' False opts cs
+ _ | c `elemText` "\\`*_|" &&
+ (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
+ '"' | 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 Text)
@@ -186,7 +187,7 @@ bordered contents c =
then border $$ contents $$ border
else empty
where len = offset contents
- border = text (replicate len c)
+ border = literal (T.replicate len $ T.singleton c)
-- | Convert Pandoc block element to RST.
blockToRST :: PandocMonad m
@@ -203,30 +204,30 @@ blockToRST (Div (ident,classes,_kvs) bs) = do
let admonition = case classes of
(cl:_)
| cl `elem` admonitions
- -> ".. " <> text cl <> "::"
+ -> ".. " <> literal cl <> "::"
cls -> ".. container::" <> space <>
- text (unwords (filter (/= "container") cls))
+ literal (T.unwords (filter (/= "container") cls))
return $ blankline $$
admonition $$
- (if null ident
+ (if T.null ident
then blankline
- else " :name: " <> text ident $$ blankline) $$
+ else " :name: " <> literal ident $$ blankline) $$
nest 3 contents $$
blankline
blockToRST (Plain inlines) = inlineListToRST inlines
-- title beginning with fig: indicates that the image is a figure
-blockToRST (Para [Image attr txt (src,'f':'i':'g':':':tit)]) = do
+blockToRST (Para [Image attr txt (src,T.stripPrefix "fig:" -> Just tit)]) = do
capt <- inlineListToRST txt
dims <- imageDimsToRST attr
- let fig = "figure:: " <> text src
- alt = ":alt: " <> if null tit then capt else text tit
+ let fig = "figure:: " <> literal src
+ alt = ":alt: " <> if T.null tit then capt else literal tit
(_,cls,_) = attr
classes = case cls of
[] -> empty
["align-right"] -> ":align: right"
["align-left"] -> ":align: left"
["align-center"] -> ":align: center"
- _ -> ":figclass: " <> text (unwords cls)
+ _ -> ":figclass: " <> literal (T.unwords cls)
return $ hang 3 ".. " (fig $$ alt $$ classes $$ dims $+$ capt) $$ blankline
blockToRST (Para inlines)
| LineBreak `elem` inlines =
@@ -237,11 +238,11 @@ blockToRST (Para inlines)
blockToRST (LineBlock lns) =
linesToLineBlock lns
blockToRST (RawBlock f@(Format f') str)
- | f == "rst" = return $ text str
+ | f == "rst" = return $ literal str
| f == "tex" = blockToRST (RawBlock (Format "latex") str)
| otherwise = return $ blankline <> ".. raw:: " <>
- text (map toLower f') $+$
- nest 3 (text str) $$ blankline
+ literal (T.toLower f') $+$
+ nest 3 (literal str) $$ blankline
blockToRST HorizontalRule =
return $ blankline $$ "--------------" $$ blankline
blockToRST (Header level (name,classes,_) inlines) = do
@@ -254,33 +255,33 @@ blockToRST (Header level (name,classes,_) inlines) = do
if isTopLevel
then 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
+ let border = literal $ T.replicate (offset contents) $ T.singleton headerChar
+ let anchor | T.null name || name == autoId = empty
+ | otherwise = ".. _" <> literal name <> ":" $$ blankline
return $ nowrap $ anchor $$ contents $$ border $$ blankline
else do
let rub = "rubric:: " <> contents
- let name' | null name = empty
- | otherwise = ":name: " <> text name
- let cls | null classes = empty
- | otherwise = ":class: " <> text (unwords classes)
+ let name' | T.null name = empty
+ | otherwise = ":name: " <> literal name
+ let cls | null classes = empty
+ | otherwise = ":class: " <> literal (T.unwords classes)
return $ nowrap $ hang 3 ".. " (rub $$ name' $$ cls) $$ blankline
blockToRST (CodeBlock (_,classes,kvs) str) = do
opts <- gets stOptions
- let startnum = maybe "" (\x -> " " <> text x) $ lookup "startFrom" kvs
+ let startnum = maybe "" (\x -> " " <> literal x) $ lookup "startFrom" kvs
let numberlines = if "numberLines" `elem` classes
then " :number-lines:" <> startnum
else empty
if "haskell" `elem` classes && "literate" `elem` classes &&
isEnabled Ext_literate_haskell opts
- then return $ prefixed "> " (text str) $$ blankline
+ then return $ prefixed "> " (literal str) $$ blankline
else return $
(case [c | c <- classes,
c `notElem` ["sourceCode","literate","numberLines",
"number-lines","example"]] of
[] -> "::"
- (lang:_) -> (".. code:: " <> text lang) $$ numberlines)
- $+$ nest 3 (text str) $$ blankline
+ (lang:_) -> (".. code:: " <> literal lang) $$ numberlines)
+ $+$ nest 3 (literal str) $$ blankline
blockToRST (BlockQuote blocks) = do
contents <- blockListToRST blocks
return $ nest 3 contents <> blankline
@@ -314,9 +315,9 @@ blockToRST (OrderedList (start, style', delim) items) = do
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
+ let maxMarkerLength = maximum $ map T.length markers
+ let markers' = map (\m -> let s = maxMarkerLength - T.length m
+ in m <> T.replicate s " ") markers
contents <- zipWithM orderedListItemToRST markers' items
-- ensure that sublists have preceding blank line
return $ blankline $$
@@ -338,13 +339,13 @@ bulletListItemToRST items = do
-- | Convert ordered list item (a list of blocks) to RST.
orderedListItemToRST :: PandocMonad m
- => String -- ^ marker for list item
+ => Text -- ^ marker for list item
-> [Block] -- ^ list item (list of blocks)
-> RST m (Doc Text)
orderedListItemToRST marker items = do
contents <- blockListToRST items
- let marker' = marker ++ " "
- return $ hang (length marker') (text marker') contents $$
+ let marker' = marker <> " "
+ return $ hang (T.length marker') (literal marker') contents $$
if endsWithPlain items
then cr
else blankline
@@ -364,7 +365,7 @@ linesToLineBlock :: PandocMonad m => [[Inline]] -> RST m (Doc Text)
linesToLineBlock inlineLines = do
lns <- mapM inlineListToRST inlineLines
return $
- vcat (map (hang 2 (text "| ")) lns) <> blankline
+ vcat (map (hang 2 (literal "| ")) lns) <> blankline
-- | Convert list of Pandoc block elements to RST.
blockListToRST' :: PandocMonad m
@@ -376,13 +377,13 @@ 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 (Para [Image _ _ (_,'f':'i':'g':':':_)]) = True
- toClose Para{} = False
- toClose _ = True
+ toClose Plain{} = False
+ toClose Header{} = False
+ toClose LineBlock{} = False
+ toClose HorizontalRule = False
+ toClose (Para [Image _ _ (_,t)]) = "fig:" `T.isPrefixOf` t
+ toClose Para{} = False
+ toClose _ = True
commentSep = RawBlock "rst" "..\n\n"
fixBlocks (b:bs) = b : fixBlocks bs
fixBlocks [] = []
@@ -438,26 +439,30 @@ transformInlines = insertBS .
transformNested :: [Inline] -> [Inline]
transformNested = map (mapNested stripLeadingTrailingSpace)
surroundComplex :: Inline -> Inline -> Bool
- surroundComplex (Str s@(_:_)) (Str s'@(_:_)) =
- case (last s, head s') of
- ('\'','\'') -> True
- ('"','"') -> True
- ('<','>') -> True
- ('[',']') -> True
- ('{','}') -> True
- _ -> False
+ surroundComplex (Str s) (Str s')
+ | Just (_, c) <- T.unsnoc s
+ , Just (c', _) <- T.uncons s'
+ = case (c, c') of
+ ('\'','\'') -> True
+ ('"','"') -> True
+ ('<','>') -> True
+ ('[',']') -> True
+ ('{','}') -> True
+ _ -> False
surroundComplex _ _ = False
okAfterComplex :: Inline -> Bool
okAfterComplex Space = True
okAfterComplex SoftBreak = True
okAfterComplex LineBreak = True
- okAfterComplex (Str (c:_)) = isSpace c || c `elem` ("-.,:;!?\\/'\")]}>–—" :: String)
+ okAfterComplex (Str (T.uncons -> Just (c,_)))
+ = isSpace c || c `elemText` "-.,:;!?\\/'\")]}>–—"
okAfterComplex _ = False
okBeforeComplex :: Inline -> Bool
okBeforeComplex Space = True
okBeforeComplex SoftBreak = True
okBeforeComplex LineBreak = True
- okBeforeComplex (Str (c:_)) = isSpace c || c `elem` ("-:/'\"<([{–—" :: String)
+ okBeforeComplex (Str (T.uncons -> Just (c,_)))
+ = isSpace c || c `elemText` "-:/'\"<([{–—"
okBeforeComplex _ = False
isComplex :: Inline -> Bool
isComplex (Emph _) = True
@@ -563,7 +568,7 @@ inlineToRST (Span (_,_,kvs) ils) = do
contents <- writeInlines ils
return $
case lookup "role" kvs of
- Just role -> ":" <> text role <> ":`" <> contents <> "`"
+ Just role -> ":" <> literal role <> ":`" <> contents <> "`"
Nothing -> contents
inlineToRST (Emph lst) = do
contents <- writeInlines lst
@@ -596,7 +601,7 @@ inlineToRST (Quoted DoubleQuote lst) = do
inlineToRST (Cite _ lst) =
writeInlines lst
inlineToRST (Code (_,["interpreted-text"],[("role",role)]) str) = do
- return $ ":" <> text role <> ":`" <> text str <> "`"
+ return $ ":" <> literal role <> ":`" <> literal str <> "`"
inlineToRST (Code _ str) = do
opts <- gets stOptions
-- we trim the string because the delimiters must adjoin a
@@ -604,28 +609,28 @@ inlineToRST (Code _ str) = do
-- 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) <> "``"
+ if '`' `elemText` str
+ then ":literal:`" <> literal (escapeText opts (trim str)) <> "`"
+ else "``" <> literal (trim str) <> "``"
inlineToRST (Str str) = do
opts <- gets stOptions
- return $ text $
+ return $ literal $
(if isEnabled Ext_smart opts
then unsmartify opts
- else id) $ escapeString opts str
+ else id) $ escapeText opts str
inlineToRST (Math t str) = do
modify $ \st -> st{ stHasMath = True }
return $ if t == InlineMath
- then ":math:`" <> text str <> "`"
- else if '\n' `elem` str
+ then ":math:`" <> literal str <> "`"
+ else if '\n' `elemText` str
then blankline $$ ".. math::" $$
- blankline $$ nest 3 (text str) $$ blankline
- else blankline $$ (".. math:: " <> text str) $$ blankline
+ blankline $$ nest 3 (literal str) $$ blankline
+ else blankline $$ (".. math:: " <> literal str) $$ blankline
inlineToRST il@(RawInline f x)
- | f == "rst" = return $ text x
+ | f == "rst" = return $ literal x
| f == "latex" || f == "tex" = do
modify $ \st -> st{ stHasRawTeX = True }
- return $ ":raw-latex:`" <> text x <> "`"
+ return $ ":raw-latex:`" <> literal x <> "`"
| otherwise = empty <$ report (InlineNotRendered il)
inlineToRST LineBreak = return cr -- there's no line break in RST (see Para)
inlineToRST Space = return space
@@ -638,11 +643,11 @@ inlineToRST SoftBreak = do
-- autolink
inlineToRST (Link _ [Str str] (src, _))
| isURI src &&
- if "mailto:" `isPrefixOf` src
- then src == escapeURI ("mailto:" ++ str)
+ if "mailto:" `T.isPrefixOf` src
+ then src == escapeURI ("mailto:" <> str)
else src == escapeURI str = do
- let srcSuffix = fromMaybe src (stripPrefix "mailto:" src)
- return $ text srcSuffix
+ let srcSuffix = fromMaybe src (T.stripPrefix "mailto:" src)
+ return $ literal srcSuffix
inlineToRST (Link _ [Image attr alt (imgsrc,imgtit)] (src, _tit)) = do
label <- registerImage attr alt (imgsrc,imgtit) (Just src)
return $ "|" <> label <> "|"
@@ -656,11 +661,11 @@ inlineToRST (Link _ txt (src, tit)) = do
if src == src' && tit == tit'
then return $ "`" <> linktext <> "`_"
else
- return $ "`" <> linktext <> " <" <> text src <> ">`__"
+ return $ "`" <> linktext <> " <" <> literal src <> ">`__"
Nothing -> do
modify $ \st -> st { stLinks = (txt,(src,tit)):refs }
return $ "`" <> linktext <> "`_"
- else return $ "`" <> linktext <> " <" <> text src <> ">`__"
+ else return $ "`" <> linktext <> " <" <> literal src <> ">`__"
inlineToRST (Image attr alternate (source, tit)) = do
label <- registerImage attr alternate (source,tit) Nothing
return $ "|" <> label <> "|"
@@ -671,7 +676,7 @@ inlineToRST (Note contents) = do
let ref = show $ length notes + 1
return $ " [" <> text ref <> "]_"
-registerImage :: PandocMonad m => Attr -> [Inline] -> Target -> Maybe String -> RST m (Doc Text)
+registerImage :: PandocMonad m => Attr -> [Inline] -> Target -> Maybe Text -> RST m (Doc Text)
registerImage attr alt (src,tit) mbtarget = do
pics <- gets stImages
txt <- case lookup alt pics of
@@ -679,7 +684,7 @@ registerImage attr alt (src,tit) mbtarget = do
-> return alt
_ -> do
let alt' = if null alt || alt == [Str ""]
- then [Str $ "image" ++ show (length pics)]
+ then [Str $ "image" <> tshow (length pics)]
else alt
modify $ \st -> st { stImages =
(alt', (attr,src,tit, mbtarget)):stImages st }
@@ -689,9 +694,9 @@ registerImage attr alt (src,tit) mbtarget = do
imageDimsToRST :: PandocMonad m => Attr -> RST m (Doc Text)
imageDimsToRST attr = do
let (ident, _, _) = attr
- name = if null ident
+ name = if T.null ident
then empty
- else ":name: " <> text ident
+ else ":name: " <> literal ident
showDim dir = let cols d = ":" <> text (show dir) <> ": " <> text (show d)
in case dimension dir attr of
Just (Percent a) ->
@@ -711,7 +716,7 @@ simpleTable :: PandocMonad m
simpleTable opts blocksToDoc headers rows = do
-- can't have empty cells in first column:
let fixEmpties (d:ds) = if isEmpty d
- then text "\\ " : ds
+ then literal "\\ " : ds
else d : ds
fixEmpties [] = []
headerDocs <- if all null headers
@@ -722,7 +727,7 @@ simpleTable opts blocksToDoc headers rows = do
numChars xs = maximum . map offset $ xs
let colWidths = map numChars $ transpose (headerDocs : rowDocs)
let toRow = mconcat . intersperse (lblock 1 " ") . zipWith lblock colWidths
- let hline = nowrap $ hsep (map (\n -> text (replicate n '=')) colWidths)
+ let hline = nowrap $ hsep (map (\n -> literal (T.replicate n "=")) colWidths)
let hdr = if all null headers
then mempty
else hline $$ toRow headerDocs