git.iko.soy/typeable/pandoc
on master help sign in
RST reader: Improved field lists.
703c421John MacFarlane
src/Text/Pandoc/Readers/RST.hs+56−59
@@ -34,10 +34,11 @@ import Text.Pandoc.Definition
34import Text.Pandoc.Shared
34import Text.Pandoc.Shared
35import Text.Pandoc.Parsing
35import Text.Pandoc.Parsing
36import Text.ParserCombinators.Parsec
36import Text.ParserCombinators.Parsec
37−import Control.Monad ( when, unless )
37+import Control.Monad ( when )
38import Data.List ( findIndex, intercalate, transpose, sort, deleteFirstsBy )
38import Data.List ( findIndex, intercalate, transpose, sort, deleteFirstsBy )
39import qualified Data.Map as M
39import qualified Data.Map as M
40import Text.Printf ( printf )
40import Text.Printf ( printf )
41+import Data.Maybe ( catMaybes )
41
42
42-- | Parse reStructuredText string and return Pandoc document.
43-- | Parse reStructuredText string and return Pandoc document.
43readRST :: ParserState -- ^ Parser state, including options for parser
44readRST :: ParserState -- ^ Parser state, including options for parser
@@ -121,10 +122,9 @@ parseBlocks = manyTill block eof
121
122
122block :: GenParser Char ParserState Block
123block :: GenParser Char ParserState Block
123block = choice [ codeBlock
124block = choice [ codeBlock
124− , rawHtmlBlock
125+ , rawBlock
125− , rawLaTeXBlock
126− , fieldList
127 , blockQuote
126 , blockQuote
127+ , fieldList
128 , imageBlock
128 , imageBlock
129 , customCodeBlock
129 , customCodeBlock
130 , unknownDirective
130 , unknownDirective
@@ -142,48 +142,54 @@ block = choice [ codeBlock
142-- field list
142-- field list
143--
143--
144
144
145−fieldListItem :: String -> GenParser Char st ([Char], [Char])
145+rawFieldListItem :: String -> GenParser Char ParserState (String, String)
146−fieldListItem indent = try $ do
146+rawFieldListItem indent = try $ do
147 string indent
147 string indent
148 char ':'
148 char ':'
149 name <- many1 $ alphaNum <|> spaceChar
149 name <- many1 $ alphaNum <|> spaceChar
150 string ": "
150 string ": "
151 skipSpaces
151 skipSpaces
152 first <- manyTill anyChar newline
152 first <- manyTill anyChar newline
153− rest <- option "" $ try $ lookAhead (string indent >> spaceChar) >>
153+ rest <- option "" $ try $ do lookAhead (string indent >> spaceChar)
154− indentedBlock
154+ indentedBlock
155− return (name, first ++ if null rest
155+ let raw = first ++ "\n" ++ rest ++ "\n"
156− then ""
156+ return (name, raw)
157− else ("\n" ++ rest))
157+
158+fieldListItem :: String
159+ -> GenParser Char ParserState (Maybe ([Inline], [[Block]]))
160+fieldListItem indent = try $ do
161+ (name, raw) <- rawFieldListItem indent
162+ let term = [Str name]
163+ contents <- parseFromString (many block) raw
164+ case (name, contents) of
165+ ("Author", x) -> do
166+ updateState $ \st ->
167+ st{ stateAuthors = stateAuthors st ++ [extractContents x] }
168+ return Nothing
169+ ("Authors", [BulletList auths]) -> do
170+ updateState $ \st -> st{ stateAuthors = map extractContents auths }
171+ return Nothing
172+ ("Date", x) -> do
173+ updateState $ \st -> st{ stateDate = extractContents x }
174+ return Nothing
175+ ("Title", x) -> do
176+ updateState $ \st -> st{ stateTitle = extractContents x }
177+ return Nothing
178+ _ -> return $ Just (term, [contents])
179+
180+extractContents :: [Block] -> [Inline]
181+extractContents [Plain auth] = auth
182+extractContents [Para auth] = auth
183+extractContents _ = []
158
184
159fieldList :: GenParser Char ParserState Block
185fieldList :: GenParser Char ParserState Block
160fieldList = try $ do
186fieldList = try $ do
161 indent <- lookAhead $ many spaceChar
187 indent <- lookAhead $ many spaceChar
162 items <- many1 $ fieldListItem indent
188 items <- many1 $ fieldListItem indent
163 blanklines
189 blanklines
164− let authors = case lookup "Authors" items of
165− Just auth -> [auth]
166− Nothing -> map snd (filter (\(x,_) -> x == "Author") items)
167− unless (null authors) $ do
168− authors' <- mapM (parseFromString (many inline)) authors
169− updateState $ \st -> st {stateAuthors = map normalizeSpaces authors'}
170− case (lookup "Date" items) of
171− Just dat -> do
172− dat' <- parseFromString (many inline) dat
173− updateState $ \st -> st{ stateDate = normalizeSpaces dat' }
174− Nothing -> return ()
175− case (lookup "Title" items) of
176− Just tit -> parseFromString (many inline) tit >>=
177− \t -> updateState $ \st -> st {stateTitle = t}
178− Nothing -> return ()
179− let remaining = filter (\(x,_) -> (x /= "Authors") && (x /= "Author") &&
180− (x /= "Date") && (x /= "Title")) items
181− if null remaining
190+ if null items
182− then return Null
191+ then return Null
183− else do terms <- mapM (return . (:[]) . Str . fst) remaining
184− defs <- mapM (parseFromString (many block) . snd)
185− remaining
186− return $ DefinitionList $ zip terms $ map (:[]) defs
192+ else return $ DefinitionList $ catMaybes items
187
193
188--
194--
189-- line block
195-- line block
@@ -237,15 +243,16 @@ plain = many1 inline >>= return . Plain . normalizeSpaces
237-- image block
243-- image block
238--
244--
239
245
240−imageBlock :: GenParser Char st Block
246+imageBlock :: GenParser Char ParserState Block
241imageBlock = try $ do
247imageBlock = try $ do
242 string ".. image:: "
248 string ".. image:: "
243 src <- manyTill anyChar newline
249 src <- manyTill anyChar newline
244− fields <- option [] $ do indent <- lookAhead $ many (oneOf " /t")
250+ fields <- try $ do indent <- lookAhead $ many (oneOf " /t")
245− many1 $ fieldListItem indent
251+ many $ rawFieldListItem indent
246 optional blanklines
252 optional blanklines
247 case lookup "alt" fields of
253 case lookup "alt" fields of
248− Just alt -> return $ Plain [Image [Str alt] (src, alt)]
254+ Just alt -> return $ Plain [Image [Str $ removeTrailingSpace alt]
255+ (src, "")]
249 Nothing -> return $ Plain [Image [Str "image"] (src, "")]
256 Nothing -> return $ Plain [Image [Str "image"] (src, "")]
250--
257--
251-- header blocks
258-- header blocks
@@ -320,20 +327,19 @@ hrule = try $ do
320indentedLine :: String -> GenParser Char st [Char]
327indentedLine :: String -> GenParser Char st [Char]
321indentedLine indents = try $ do
328indentedLine indents = try $ do
322 string indents
329 string indents
323− result <- manyTill anyChar newline
330+ manyTill anyChar newline
324− return $ result ++ "\n"
325
331
326-- two or more indented lines, possibly separated by blank lines.
332-- two or more indented lines, possibly separated by blank lines.
327-- any amount of indentation will work.
333-- any amount of indentation will work.
328indentedBlock :: GenParser Char st [Char]
334indentedBlock :: GenParser Char st [Char]
329−indentedBlock = do
335+indentedBlock = try $ do
330 indents <- lookAhead $ many1 spaceChar
336 indents <- lookAhead $ many1 spaceChar
331 lns <- many $ choice $ [ indentedLine indents,
337 lns <- many $ choice $ [ indentedLine indents,
332 try $ do b <- blanklines
338 try $ do b <- blanklines
333 l <- indentedLine indents
339 l <- indentedLine indents
334 return (b ++ l) ]
340 return (b ++ l) ]
335− optional blanklines
341+ optional blanklines
336− return $ concat lns
342+ return $ unlines lns
337
343
338codeBlock :: GenParser Char st Block
344codeBlock :: GenParser Char st Block
339codeBlock = try $ do
345codeBlock = try $ do
@@ -371,25 +377,16 @@ birdTrackLine = do
371 manyTill anyChar newline
377 manyTill anyChar newline
372
378
373--
379--
374−-- raw html
380+-- raw html/latex/etc
375--
381--
376
382
377−rawHtmlBlock :: GenParser Char st Block
383+rawBlock :: GenParser Char st Block
378−rawHtmlBlock = try $ do
384+rawBlock = try $ do
379− string ".. raw:: html"
385+ string ".. raw:: "
380− blanklines
386+ lang <- many1 (letter <|> digit)
381− indentedBlock >>= return . RawBlock "html"
382−
383−--
384−-- raw latex
385−--
386−
387−rawLaTeXBlock :: GenParser Char st Block
388−rawLaTeXBlock = try $ do
389− string ".. raw:: latex"
390 blanklines
387 blanklines
391 result <- indentedBlock
388 result <- indentedBlock
392− return $ RawBlock "latex" result
389+ return $ RawBlock lang result
393
390
394--
391--
395-- block quotes
392-- block quotes
@@ -416,7 +413,7 @@ definitionListItem = try $ do
416 term <- many1Till inline endline
413 term <- many1Till inline endline
417 raw <- indentedBlock
414 raw <- indentedBlock
418 -- parse the extracted block, which may contain various block elements:
415 -- parse the extracted block, which may contain various block elements:
419− contents <- parseFromString parseBlocks $ raw ++ "\n\n"
416+ contents <- parseFromString parseBlocks $ raw ++ "\n"
420 return (normalizeSpaces term, [contents])
417 return (normalizeSpaces term, [contents])
421
418
422definitionList :: GenParser Char ParserState Block
419definitionList :: GenParser Char ParserState Block
tests/Tests/Readers/RST.hs+11−11
@@ -27,20 +27,20 @@ tests = [ "field list" =:
27:Authors: - Me
27:Authors: - Me
28 - Myself
28 - Myself
29 - I
29 - I
30−:Indentation: Since the field marker may be quite long, the second
30+:Indentation: Since the field marker may be quite long, the second
31− and subsequent lines of the field body do not have to line up
31+ and subsequent lines of the field body do not have to line up
32− with the first line, but they must be indented relative to the
32+ with the first line, but they must be indented relative to the
33− field name marker, and they must line up with each other.
33+ field name marker, and they must line up with each other.
34−:Parameter i: integer
34+:Parameter i: integer
35|] =?> ( setAuthors ["Me","Myself","I"]
35|] =?> ( setAuthors ["Me","Myself","I"]
36 $ setDate "2001-08-16"
36 $ setDate "2001-08-16"
37 $ doc
37 $ doc
38− $ definitionList [ (str "Hostname", [plain "media08"])
38+ $ definitionList [ (str "Hostname", [para "media08"])
39− , (str "IP address", [plain "10.0.0.19"])
39+ , (str "IP address", [para "10.0.0.19"])
40− , (str "Size", [plain "3ru"])
40+ , (str "Size", [para "3ru"])
41− , (str "Version", [plain "1"])
41+ , (str "Version", [para "1"])
42− , (str "Indentation", [para "Since the field marker may be quite long, the second. and subsequent lines of the field body do not have to line up. with the first line, but they must be indented relative to the. field name marker, and they must line up with each other."])
42+ , (str "Indentation", [para "Since the field marker may be quite long, the second and subsequent lines of the field body do not have to line up with the first line, but they must be indented relative to the field name marker, and they must line up with each other."])
43− , (str "Parameter i", [plain "integer"])
43+ , (str "Parameter i", [para "integer"])
44 ])
44 ])
45 ]
45 ]
46
46
tests/rst-reader.native+13−12
@@ -1,7 +1,7 @@
1Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite",Str ":",Space,Str "Subtitle"], docAuthors = [[Str "John",Space,Str "MacFarlane"],[Str "Anonymous"]], docDate = [Str "July",Space,Str "17,",Space,Str "2006"]})
1Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite",Str ":",Space,Str "Subtitle"], docAuthors = [[Str "John",Space,Str "MacFarlane"],[Str "Anonymous"]], docDate = [Str "July",Space,Str "17,",Space,Str "2006"]})
2[DefinitionList
2[DefinitionList
3 [([Str "Revision"],
3 [([Str "Revision"],
4− [[Plain [Str "3"]]])]
4+ [[Para [Str "3"]]])]
5,Header 1 [Str "Level",Space,Str "one",Space,Str "header"]
5,Header 1 [Str "Level",Space,Str "one",Space,Str "header"]
6,Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "set",Space,Str "of",Space,Str "tests",Space,Str "for",Space,Str "pandoc",Str ".",Space,Str "Most",Space,Str "of",Space,Str "them",Space,Str "are",Space,Str "adapted",Space,Str "from",Space,Str "John",Space,Str "Gruber",Apostrophe,Str "s",Space,Str "markdown",Space,Str "test",Space,Str "suite",Str "."]
6,Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "set",Space,Str "of",Space,Str "tests",Space,Str "for",Space,Str "pandoc",Str ".",Space,Str "Most",Space,Str "of",Space,Str "them",Space,Str "are",Space,Str "adapted",Space,Str "from",Space,Str "John",Space,Str "Gruber",Apostrophe,Str "s",Space,Str "markdown",Space,Str "test",Space,Str "suite",Str "."]
7,Header 2 [Str "Level",Space,Str "two",Space,Str "header"]
7,Header 2 [Str "Level",Space,Str "two",Space,Str "header"]
@@ -158,20 +158,21 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite",Str ":
158 ,([Str "term",Space,Str "with",Space,Emph [Str "emphasis"]],
158 ,([Str "term",Space,Str "with",Space,Emph [Str "emphasis"]],
159 [[Para [Str "Definition",Space,Str "3",Str "."]]])]
159 [[Para [Str "Definition",Space,Str "3",Str "."]]])]
160,Header 1 [Str "Field",Space,Str "Lists"]
160,Header 1 [Str "Field",Space,Str "Lists"]
161+,BlockQuote
162+ [DefinitionList
163+ [([Str "address"],
164+ [[Para [Str "61",Space,Str "Main",Space,Str "St",Str "."]]])
165+ ,([Str "city"],
166+ [[Para [Emph [Str "Nowhere"],Str ",",Space,Str "MA,",Space,Str "USA"]]])
167+ ,([Str "phone"],
168+ [[Para [Str "123",EnDash,Str "4567"]]])]]
161,DefinitionList
169,DefinitionList
162 [([Str "address"],
170 [([Str "address"],
163− [[Plain [Str "61",Space,Str "Main",Space,Str "St",Str "."]]])
164− ,([Str "city"],
165− [[Plain [Emph [Str "Nowhere"],Str ",",Space,Str "MA,",Space,Str "USA"]]])
166− ,([Str "phone"],
167− [[Plain [Str "123",EnDash,Str "4567"]]])]
168−,DefinitionList
169− [([Str "address"],
170− [[Plain [Str "61",Space,Str "Main",Space,Str "St",Str "."]]])
171+ [[Para [Str "61",Space,Str "Main",Space,Str "St",Str "."]]])
171 ,([Str "city"],
172 ,([Str "city"],
172− [[Plain [Emph [Str "Nowhere"],Str ",",Space,Str "MA,",Space,Str "USA"]]])
173+ [[Para [Emph [Str "Nowhere"],Str ",",Space,Str "MA,",Space,Str "USA"]]])
173 ,([Str "phone"],
174 ,([Str "phone"],
174− [[Plain [Str "123",EnDash,Str "4567"]]])]
175+ [[Para [Str "123",EnDash,Str "4567"]]])]
175,Header 1 [Str "HTML",Space,Str "Blocks"]
176,Header 1 [Str "HTML",Space,Str "Blocks"]
176,Para [Str "Simple",Space,Str "block",Space,Str "on",Space,Str "one",Space,Str "line",Str ":"]
177,Para [Str "Simple",Space,Str "block",Space,Str "on",Space,Str "one",Space,Str "line",Str ":"]
177,RawBlock "html" "<div>foo</div>\n"
178,RawBlock "html" "<div>foo</div>\n"
@@ -223,7 +224,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite",Str ":
223,Header 1 [Str "Images"]
224,Header 1 [Str "Images"]
224,Para [Str "From",Space,Quoted DoubleQuote [Str "Voyage",Space,Str "dans",Space,Str "la",Space,Str "Lune"],Space,Str "by",Space,Str "Georges",Space,Str "Melies",Space,Str "(1902)",Str ":"]
225,Para [Str "From",Space,Quoted DoubleQuote [Str "Voyage",Space,Str "dans",Space,Str "la",Space,Str "Lune"],Space,Str "by",Space,Str "Georges",Space,Str "Melies",Space,Str "(1902)",Str ":"]
225,Plain [Image [Str "image"] ("lalune.jpg","")]
226,Plain [Image [Str "image"] ("lalune.jpg","")]
226−,Plain [Image [Str "Voyage dans la Lune"] ("lalune.jpg","Voyage dans la Lune")]
227+,Plain [Image [Str "Voyage dans la Lune"] ("lalune.jpg","")]
227,Para [Str "Here",Space,Str "is",Space,Str "a",Space,Str "movie",Space,Image [Str "movie"] ("movie.jpg",""),Space,Str "icon",Str "."]
228,Para [Str "Here",Space,Str "is",Space,Str "a",Space,Str "movie",Space,Image [Str "movie"] ("movie.jpg",""),Space,Str "icon",Str "."]
228,Header 1 [Str "Comments"]
229,Header 1 [Str "Comments"]
229,Para [Str "First",Space,Str "paragraph"]
230,Para [Str "First",Space,Str "paragraph"]