@@ -31,20 +31,21 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31Conversion from reStructuredText to 'Pandoc' document.
31Conversion from reStructuredText to 'Pandoc' document.
33module Text.Pandoc.Readers.RST ( readRST ) where
33module Text.Pandoc.Readers.RST ( readRST ) where
34+import Control.Arrow (second)
34import Control.Monad (forM_, guard, liftM, mplus, mzero, when)
35import Control.Monad (forM_, guard, liftM, mplus, mzero, when)
35import Control.Monad.Except (throwError)
36import Control.Monad.Except (throwError)
36import Control.Monad.Identity (Identity (..))
37import Control.Monad.Identity (Identity (..))
37import Data.Char (isHexDigit, isSpace, toLower, toUpper)
38import Data.Char (isHexDigit, isSpace, toLower, toUpper)
38−import Data.List (deleteFirstsBy, findIndex, intercalate, isInfixOf, isSuffixOf,
39+import Data.List (deleteFirstsBy, intercalate, isInfixOf,
39− nub, sort, transpose, union)
40+ elemIndex, isSuffixOf, nub, sort, transpose, union)
40import qualified Data.Map as M
41import qualified Data.Map as M
41import Data.Maybe (fromMaybe, isJust)
42import Data.Maybe (fromMaybe, isJust)
42import Data.Monoid ((<>))
43import Data.Monoid ((<>))
43import Data.Sequence (ViewR (..), viewr)
44import Data.Sequence (ViewR (..), viewr)
44import Data.Text (Text)
45import Data.Text (Text)
45import qualified Data.Text as T
46import qualified Data.Text as T
46−import Text.Pandoc.Builder (fromList, setMeta)
47+import Text.Pandoc.Builder
47−import Text.Pandoc.Builder (Blocks, Inlines, trimInlines)
48+ (fromList, setMeta, Blocks, Inlines, trimInlines)
48import qualified Text.Pandoc.Builder as B
49import qualified Text.Pandoc.Builder as B
49import Text.Pandoc.Class (PandocMonad, fetchItem, readFileFromDirs)
50import Text.Pandoc.Class (PandocMonad, fetchItem, readFileFromDirs)
50import Text.Pandoc.CSV (CSVOptions (..), defaultCSVOptions, parseCSV)
51import Text.Pandoc.CSV (CSVOptions (..), defaultCSVOptions, parseCSV)
@@ -67,7 +68,7 @@ readRST :: PandocMonad m
67 -> Text -- ^ String to parse (assuming @'\n'@ line endings)
68 -> Text -- ^ String to parse (assuming @'\n'@ line endings)
68 -> m Pandoc
69 -> m Pandoc
69readRST opts s = do
70readRST opts s = do
70− parsed <- (readWithM parseRST) def{ stateOptions = opts }
71+ parsed <- readWithM parseRST def{ stateOptions = opts }
71 (T.unpack (crFilter s) ++ "\n\n")
72 (T.unpack (crFilter s) ++ "\n\n")
72 case parsed of
73 case parsed of
73 Right result -> return result
74 Right result -> return result
@@ -100,9 +101,9 @@ isHeader _ _ = False
100-- | Promote all headers in a list of blocks. (Part of
101-- | Promote all headers in a list of blocks. (Part of
101-- title transformation for RST.)
102-- title transformation for RST.)
102promoteHeaders :: Int -> [Block] -> [Block]
103promoteHeaders :: Int -> [Block] -> [Block]
103−promoteHeaders num ((Header level attr text):rest) =
104+promoteHeaders num (Header level attr text:rest) =
104− (Header (level - num) attr text):(promoteHeaders num rest)
105+ Header (level - num) attr text:promoteHeaders num rest
105−promoteHeaders num (other:rest) = other:(promoteHeaders num rest)
106+promoteHeaders num (other:rest) = other:promoteHeaders num rest
106promoteHeaders _ [] = []
107promoteHeaders _ [] = []
108-- | If list of blocks starts with a header (or a header and subheader)
109-- | If list of blocks starts with a header (or a header and subheader)
@@ -114,11 +115,11 @@ titleTransform :: ([Block], Meta) -- ^ list of blocks, metadata
114titleTransform (bs, meta) =
115titleTransform (bs, meta) =
115 let (bs', meta') =
116 let (bs', meta') =
116 case bs of
117 case bs of
117− ((Header 1 _ head1):(Header 2 _ head2):rest)
118+ (Header 1 _ head1:Header 2 _ head2:rest)
118 | not (any (isHeader 1) rest || any (isHeader 2) rest) -> -- tit/sub
119 | not (any (isHeader 1) rest || any (isHeader 2) rest) -> -- tit/sub
119 (promoteHeaders 2 rest, setMeta "title" (fromList head1) $
120 (promoteHeaders 2 rest, setMeta "title" (fromList head1) $
120 setMeta "subtitle" (fromList head2) meta)
121 setMeta "subtitle" (fromList head2) meta)
121− ((Header 1 _ head1):rest)
122+ (Header 1 _ head1:rest)
122 | not (any (isHeader 1) rest) -> -- title only
123 | not (any (isHeader 1) rest) -> -- title only
123 (promoteHeaders 1 rest,
124 (promoteHeaders 1 rest,
124 setMeta "title" (fromList head1) meta)
125 setMeta "title" (fromList head1) meta)
@@ -137,8 +138,7 @@ metaFromDefList ds meta = adjustAuthors $ foldr f meta ds
137 $ M.mapKeys (\k ->
138 $ M.mapKeys (\k ->
138 if k == "authors"
139 if k == "authors"
139 then "author"
140 then "author"
141− $ metamap
141+ else k) metamap
142 toPlain (MetaBlocks [Para xs]) = MetaInlines xs
142 toPlain (MetaBlocks [Para xs]) = MetaInlines xs
143 toPlain x = x
143 toPlain x = x
144 splitAuthors (MetaBlocks [Para xs])
144 splitAuthors (MetaBlocks [Para xs])
@@ -201,7 +201,7 @@ parseCitation :: PandocMonad m
201 => (String, String) -> RSTParser m (Inlines, [Blocks])
201 => (String, String) -> RSTParser m (Inlines, [Blocks])
202parseCitation (ref, raw) = do
202parseCitation (ref, raw) = do
203 contents <- parseFromString' parseBlocks raw
203 contents <- parseFromString' parseBlocks raw
204− return $ (B.spanWith (ref, ["citation-label"], []) (B.str ref),
204+ return (B.spanWith (ref, ["citation-label"], []) (B.str ref),
205 [contents])
205 [contents])
@@ -289,7 +289,7 @@ para = try $ do
290 blanklines
290 blanklines
291 case viewr (B.unMany result) of
291 case viewr (B.unMany result) of
292− ys :> (Str xs) | "::" `isSuffixOf` xs -> do
292+ ys :> Str xs | "::" `isSuffixOf` xs -> do
293 raw <- option mempty codeBlockBody
293 raw <- option mempty codeBlockBody
294 return $ B.para (B.Many ys <> B.str (take (length xs - 1) xs))
294 return $ B.para (B.Many ys <> B.str (take (length xs - 1) xs))
@@ -313,9 +313,9 @@ doubleHeader = do
313 -- if so, get appropriate level. if not, add to list.
313 -- if so, get appropriate level. if not, add to list.
314 state <- getState
314 state <- getState
315 let headerTable = stateHeaderTable state
315 let headerTable = stateHeaderTable state
316− let (headerTable',level) = case findIndex (== DoubleHeader c) headerTable of
316+ let (headerTable',level) = case elemIndex (DoubleHeader c) headerTable of
317 Just ind -> (headerTable, ind + 1)
317 Just ind -> (headerTable, ind + 1)
318− Nothing -> (headerTable ++ [DoubleHeader c], (length headerTable) + 1)
318+ Nothing -> (headerTable ++ [DoubleHeader c], length headerTable + 1)
319 setState (state { stateHeaderTable = headerTable' })
319 setState (state { stateHeaderTable = headerTable' })
320 attr <- registerHeader nullAttr txt
320 attr <- registerHeader nullAttr txt
321 return $ B.headerWith attr level txt
321 return $ B.headerWith attr level txt
@@ -329,8 +329,8 @@ doubleHeader' = try $ do
330 txt <- trimInlines . mconcat <$> many1 (notFollowedBy blankline >> inline)
330 txt <- trimInlines . mconcat <$> many1 (notFollowedBy blankline >> inline)
331 pos <- getPosition
331 pos <- getPosition
332− let len = (sourceColumn pos) - 1
332+ let len = sourceColumn pos - 1
333− if (len > lenTop) then fail "title longer than border" else return ()
333+ when (len > lenTop) $ fail "title longer than border"
334 blankline -- spaces and newline
334 blankline -- spaces and newline
335 count lenTop (char c) -- the bottom line
335 count lenTop (char c) -- the bottom line
336 blanklines
336 blanklines
@@ -342,9 +342,9 @@ singleHeader = do
342 (txt, c) <- singleHeader'
342 (txt, c) <- singleHeader'
343 state <- getState
343 state <- getState
344 let headerTable = stateHeaderTable state
344 let headerTable = stateHeaderTable state
345− let (headerTable',level) = case findIndex (== SingleHeader c) headerTable of
345+ let (headerTable',level) = case elemIndex (SingleHeader c) headerTable of
346 Just ind -> (headerTable, ind + 1)
346 Just ind -> (headerTable, ind + 1)
347− Nothing -> (headerTable ++ [SingleHeader c], (length headerTable) + 1)
347+ Nothing -> (headerTable ++ [SingleHeader c], length headerTable + 1)
348 setState (state { stateHeaderTable = headerTable' })
348 setState (state { stateHeaderTable = headerTable' })
349 attr <- registerHeader nullAttr txt
349 attr <- registerHeader nullAttr txt
350 return $ B.headerWith attr level txt
350 return $ B.headerWith attr level txt
@@ -355,7 +355,7 @@ singleHeader' = try $ do
355 lookAhead $ anyLine >> oneOf underlineChars
355 lookAhead $ anyLine >> oneOf underlineChars
356 txt <- trimInlines . mconcat <$> many1 (do {notFollowedBy newline; inline})
356 txt <- trimInlines . mconcat <$> many1 (do {notFollowedBy newline; inline})
357 pos <- getPosition
357 pos <- getPosition
358− let len = (sourceColumn pos) - 1
358+ let len = sourceColumn pos - 1
359 blankline
359 blankline
360 c <- oneOf underlineChars
360 c <- oneOf underlineChars
361 count (len - 1) (char c)
361 count (len - 1) (char c)
@@ -491,8 +491,7 @@ includeDirective top fields body = do
491 Just x | x >= 0 -> x
491 Just x | x >= 0 -> x
492 | otherwise -> numLines + x -- negative from end
492 | otherwise -> numLines + x -- negative from end
493 let contentLines' = drop (startLine' - 1)
493 let contentLines' = drop (startLine' - 1)
494− $ take (endLine' - 1)
494+ $ take (endLine' - 1) contentLines
496 let contentLines'' = (case trim <$> lookup "end-before" fields of
495 let contentLines'' = (case trim <$> lookup "end-before" fields of
497 Just patt -> takeWhile (not . (patt `isInfixOf`))
496 Just patt -> takeWhile (not . (patt `isInfixOf`))
498 Nothing -> id) .
497 Nothing -> id) .
@@ -692,7 +691,7 @@ directive' = do
692 "csv-table" -> csvTableDirective top fields body'
691 "csv-table" -> csvTableDirective top fields body'
693 "line-block" -> lineBlockDirective body'
692 "line-block" -> lineBlockDirective body'
694 "raw" -> return $ B.rawBlock (trim top) (stripTrailingNewlines body)
693 "raw" -> return $ B.rawBlock (trim top) (stripTrailingNewlines body)
695− "role" -> addNewRole top $ map (\(k,v) -> (k, trim v)) fields
694+ "role" -> addNewRole top $ map (second trim) fields
696 "container" -> parseFromString' parseBlocks body'
695 "container" -> parseFromString' parseBlocks body'
697 "replace" -> B.para <$> -- consumed by substKey
696 "replace" -> B.para <$> -- consumed by substKey
698 parseInlineFromString (trim top)
697 parseInlineFromString (trim top)
@@ -733,7 +732,7 @@ directive' = do
733 codeblock (words $ fromMaybe [] $ lookup "class" fields)
732 codeblock (words $ fromMaybe [] $ lookup "class" fields)
734 (lookup "number-lines" fields) (trim top) body
733 (lookup "number-lines" fields) (trim top) body
735 "aafig" -> do
734 "aafig" -> do
736− let attribs = ("", ["aafig"], map (\(k,v) -> (k, trimr v)) fields)
735+ let attribs = ("", ["aafig"], map (second trimr) fields)
737 return $ B.codeBlockWith attribs $ stripTrailingNewlines body
736 return $ B.codeBlockWith attribs $ stripTrailingNewlines body
738 "math" -> return $ B.para $ mconcat $ map B.displayMath
737 "math" -> return $ B.para $ mconcat $ map B.displayMath
739 $ toChunks $ top ++ "\n\n" ++ body
738 $ toChunks $ top ++ "\n\n" ++ body
@@ -752,8 +751,8 @@ directive' = do
752 $ B.imageWith attr src "" alt
751 $ B.imageWith attr src "" alt
753 Nothing -> B.imageWith attr src "" alt
752 Nothing -> B.imageWith attr src "" alt
754 "class" -> do
753 "class" -> do
755− let attrs = ("", (splitBy isSpace $ trim top),
754+ let attrs = ("", splitBy isSpace $ trim top,
756− map (\(k,v) -> (k, trimr v)) fields)
755+ map (second trimr) fields)
757 -- directive content or the first immediately following element
756 -- directive content or the first immediately following element
758 children <- case body of
757 children <- case body of
759 "" -> block
758 "" -> block
@@ -857,7 +856,7 @@ csvTableDirective top fields rawcsv = do
857 Just h -> h ++ "\n" ++ rawcsv'
856 Just h -> h ++ "\n" ++ rawcsv'
858 Nothing -> rawcsv')
857 Nothing -> rawcsv')
859 case res of
858 case res of
860− Left e -> do
859+ Left e ->
861 throwError $ PandocParsecError "csv table" e
860 throwError $ PandocParsecError "csv table" e
862 Right rawrows -> do
861 Right rawrows -> do
863 let parseCell = parseFromString' (plain <|> return mempty) . T.unpack
862 let parseCell = parseFromString' (plain <|> return mempty) . T.unpack
@@ -909,13 +908,13 @@ addNewRole roleString fields = do
909 in (ident, nub . (role :) . annotate $ classes, keyValues)
908 in (ident, nub . (role :) . annotate $ classes, keyValues)
911 -- warn about syntax we ignore
910 -- warn about syntax we ignore
912− flip mapM_ fields $ \(key, _) -> case key of
911+ forM_ fields $ \(key, _) -> case key of
913− "language" -> when (baseRole /= "code") $ logMessage $
912+ "language" -> when (baseRole /= "code") $ logMessage $
914− SkippedContent ":language: [because parent of role is not :code:]"
913+ SkippedContent ":language: [because parent of role is not :code:]"
916− "format" -> when (baseRole /= "raw") $ logMessage $
915+ "format" -> when (baseRole /= "raw") $ logMessage $
917− SkippedContent ":format: [because parent of role is not :raw:]" pos
916+ SkippedContent ":format: [because parent of role is not :raw:]" pos
918− _ -> logMessage $ SkippedContent (":" ++ key ++ ":") pos
917+ _ -> logMessage $ SkippedContent (":" ++ key ++ ":") pos
919 when (parentRole == "raw" && countKeys "format" > 1) $
918 when (parentRole == "raw" && countKeys "format" > 1) $
920 logMessage $ SkippedContent
919 logMessage $ SkippedContent
921 ":format: [after first in definition of role]"
920 ":format: [after first in definition of role]"
@@ -983,7 +982,7 @@ codeblock classes numberLines lang body =
983 return $ B.codeBlockWith attribs $ stripTrailingNewlines body
982 return $ B.codeBlockWith attribs $ stripTrailingNewlines body
984 where attribs = ("", classes', kvs)
983 where attribs = ("", classes', kvs)
985 classes' = "sourceCode" : lang
984 classes' = "sourceCode" : lang
986− : maybe [] (\_ -> ["numberLines"]) numberLines
985+ : maybe [] (const ["numberLines"]) numberLines
987 ++ classes
986 ++ classes
988 kvs = case numberLines of
987 kvs = case numberLines of
989 Just "" -> []
988 Just "" -> []
@@ -1038,7 +1037,8 @@ noteMarker :: Monad m => RSTParser m [Char]
1038noteMarker = do
1037noteMarker = do
1039 char '['
1038 char '['
1040 res <- many1 digit
1039 res <- many1 digit
1041− <|> (try $ char '#' >> liftM ('#':) simpleReferenceName')
1041+ try (char '#' >> liftM ('#':) simpleReferenceName')
1042 <|> count 1 (oneOf "#*")
1042 <|> count 1 (oneOf "#*")
1043 char ']'
1043 char ']'
1044 return res
1044 return res
@@ -1050,13 +1050,11 @@ noteMarker = do
1050quotedReferenceName :: PandocMonad m => RSTParser m Inlines
1050quotedReferenceName :: PandocMonad m => RSTParser m Inlines
1051quotedReferenceName = try $ do
1051quotedReferenceName = try $ do
1052 char '`' >> notFollowedBy (char '`') -- `` means inline code!
1052 char '`' >> notFollowedBy (char '`') -- `` means inline code!
1053− label' <- trimInlines . mconcat <$> many1Till inline (char '`')
1053+ trimInlines . mconcat <$> many1Till inline (char '`')
1056unquotedReferenceName :: PandocMonad m => RSTParser m Inlines
1055unquotedReferenceName :: PandocMonad m => RSTParser m Inlines
1057−unquotedReferenceName = try $ do
1056+unquotedReferenceName = try $ do -- `` means inline code!
1058− label' <- trimInlines . mconcat <$> many1Till inline (lookAhead $ char ':')
1057+ trimInlines . mconcat <$> many1Till inline (lookAhead $ char ':')
1061-- Simple reference names are single words consisting of alphanumerics
1059-- Simple reference names are single words consisting of alphanumerics
1062-- plus isolated (no two adjacent) internal hyphens, underscores,
1060-- plus isolated (no two adjacent) internal hyphens, underscores,
@@ -1066,7 +1064,8 @@ simpleReferenceName' :: Monad m => ParserT [Char] st m String
1066simpleReferenceName' = do
1064simpleReferenceName' = do
1067 x <- alphaNum
1065 x <- alphaNum
1068 xs <- many $ alphaNum
1066 xs <- many $ alphaNum
1069− <|> (try $ oneOf "-_:+." <* lookAhead alphaNum)
1068+ try (oneOf "-_:+." <* lookAhead alphaNum)
1070 return (x:xs)
1069 return (x:xs)
1072simpleReferenceName :: Monad m => ParserT [Char] st m Inlines
1071simpleReferenceName :: Monad m => ParserT [Char] st m Inlines
@@ -1074,7 +1073,7 @@ simpleReferenceName = B.str <$> simpleReferenceName'
1075referenceName :: PandocMonad m => RSTParser m Inlines
1074referenceName :: PandocMonad m => RSTParser m Inlines
1076referenceName = quotedReferenceName <|>
1075referenceName = quotedReferenceName <|>
1077− (try $ simpleReferenceName <* lookAhead (char ':')) <|>
1076+ try (simpleReferenceName <* lookAhead (char ':')) <|>
1078 unquotedReferenceName
1077 unquotedReferenceName
1080referenceKey :: PandocMonad m => RSTParser m [Char]
1079referenceKey :: PandocMonad m => RSTParser m [Char]
@@ -1093,7 +1092,7 @@ targetURI = do
1093 contents <- many1 (try (many spaceChar >> newline >>
1092 contents <- many1 (try (many spaceChar >> newline >>
1094 many1 spaceChar >> noneOf " \t\n") <|> noneOf "\n")
1093 many1 spaceChar >> noneOf " \t\n") <|> noneOf "\n")
1095 blanklines
1094 blanklines
1096− return $ escapeURI $ trim $ contents
1095+ return $ escapeURI $ trim contents
1098substKey :: PandocMonad m => RSTParser m ()
1097substKey :: PandocMonad m => RSTParser m ()
1099substKey = try $ do
1098substKey = try $ do
@@ -1258,8 +1257,7 @@ simpleTableHeader headless = try $ do
1258 let rawHeads = if headless
1257 let rawHeads = if headless
1259 then replicate (length dashes) ""
1258 then replicate (length dashes) ""
1260 else simpleTableSplitLine indices rawContent
1259 else simpleTableSplitLine indices rawContent
1261− heads <- mapM (parseFromString' (mconcat <$> many plain)) $
1260+ heads <- mapM ( (parseFromString' (mconcat <$> many plain)) . trim) rawHeads
1263 return (heads, aligns, indices)
1261 return (heads, aligns, indices)
1265-- Parse a simple table.
1263-- Parse a simple table.
@@ -1450,10 +1448,8 @@ endline = try $ do
1450 notFollowedBy blankline
1448 notFollowedBy blankline
1451 -- parse potential list-starts at beginning of line differently in a list:
1449 -- parse potential list-starts at beginning of line differently in a list:
1452 st <- getState
1450 st <- getState
1453− if (stateParserContext st) == ListItemState
1454− then notFollowedBy (anyOrderedListMarker >> spaceChar) >>
1451+ when ((stateParserContext st) == ListItemState) $ notFollowedBy (anyOrderedListMarker >> spaceChar) >>
1455 notFollowedBy' bulletListStart
1452 notFollowedBy' bulletListStart
1457 return B.softbreak
1453 return B.softbreak