@@ -19,7 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1919{- |
2020 Module : Text.Pandoc.Readers.Markdown
2121 Copyright : Copyright (C) 2006-7 John MacFarlane
22− License : GNU GPL, version 2 or above
22+ License : GNU GPL, version 2 or above
2323
2424 Maintainer : John MacFarlane <jgm@berkeley.edu>
2525 Stability : alpha
@@ -27,8 +27,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2727
2828Conversion of markdown-formatted plain text to 'Pandoc' document.
2929-}
30−module Text.Pandoc.Readers.Markdown (
31− readMarkdown
30+module Text.Pandoc.Readers.Markdown (
31+ readMarkdown
3232 ) where
3333
3434import Data.List ( transpose, isPrefixOf, isSuffixOf, lookup, sortBy )
@@ -36,9 +36,9 @@ import Data.Ord ( comparing )
3636import Data.Char ( isAlphaNum )
3737import Network.URI ( isURI )
3838import Text.Pandoc.Definition
39−import Text.Pandoc.Shared
39+import Text.Pandoc.Shared
4040import Text.Pandoc.Readers.LaTeX ( rawLaTeXInline, rawLaTeXEnvironment )
41−import Text.Pandoc.Readers.HTML ( rawHtmlBlock, anyHtmlBlockTag,
41+import Text.Pandoc.Readers.HTML ( rawHtmlBlock, anyHtmlBlockTag,
4242 anyHtmlInlineTag, anyHtmlTag,
4343 anyHtmlEndTag, htmlEndTag, extractTagType,
4444 htmlBlockElement )
@@ -68,14 +68,14 @@ specialChars = "\\[]*_~`<>$!^-.&'\"\8216\8217\8220\8221"
6868indentSpaces = try $ do
6969 state <- getState
7070 let tabStop = stateTabStop state
71− try (count tabStop (char ' ')) <|>
71+ try (count tabStop (char ' ')) <|>
7272 (many (char ' ') >> string "\t") <?> "indentation"
7373
7474nonindentSpaces = do
7575 state <- getState
7676 let tabStop = stateTabStop state
7777 sps <- many (char ' ')
78− if length sps < tabStop
78+ if length sps < tabStop
7979 then return sps
8080 else unexpected "indented line"
8181
@@ -90,8 +90,8 @@ failUnlessSmart = do
9090 if stateSmart state then return () else fail "Smart typography feature"
9191
9292-- | Parse an inline Str element with a given content.
93−inlineString str = try $ do
94− (Str res) <- inline
93+inlineString str = try $ do
94+ (Str res) <- inline
9595 if res == str then return res else fail $ "unexpected Str content"
9696
9797-- | Parse a sequence of inline elements between a string
@@ -102,9 +102,9 @@ inlinesInBalanced opener closer = try $ do
102102 string opener
103103 result <- manyTill ( (do lookAhead (inlineString opener)
104104 -- because it might be a link...
105− bal <- inlinesInBalanced opener closer
105+ bal <- inlinesInBalanced opener closer
106106 return $ [Str opener] ++ bal ++ [Str closer])
107− <|> (count 1 inline))
107+ <|> (count 1 inline))
108108 (try (string closer))
109109 return $ concat result
110110
@@ -114,7 +114,7 @@ inlinesInBalanced opener closer = try $ do
114114
115115titleLine = try $ char '%' >> skipSpaces >> manyTill inline newline
116116
117−authorsLine = try $ do
117+authorsLine = try $ do
118118 char '%'
119119 skipSpaces
120120 authors <- sepEndBy (many1 (noneOf ",;\n")) (oneOf ",;")
@@ -142,15 +142,24 @@ parseMarkdown = do
142142 startPos <- getPosition
143143 -- go through once just to get list of reference keys
144144 -- docMinusKeys is the raw document with blanks where the keys were...
145− docMinusKeys <- manyTill (referenceKey <|> lineClump) eof >>=
146− return . concat
145+ docMinusKeys <- manyTill (referenceKey <|> headerReference <|>
146+ lineClump) eof >>= return . concat
147147 setInput docMinusKeys
148148 setPosition startPos
149149 st <- getState
150+ -- get headers and construct implicit references unless strict
151+ if stateStrict st
152+ then return ()
153+ else do let oldkeys = stateKeys st
154+ let headers = reverse $ stateHeaders st
155+ let idents = uniqueIdentifiers headers
156+ let implicitRefs = zipWith (\hd ident -> (hd, ("#" ++ ident, "")))
157+ headers idents
158+ updateState $ \st -> st { stateKeys = oldkeys ++ implicitRefs }
150159 -- go through again for notes unless strict...
151160 if stateStrict st
152161 then return ()
153− else do docMinusNotes <- manyTill (noteBlock <|> lineClump) eof >>=
162+ else do docMinusNotes <- manyTill (noteBlock <|> lineClump) eof >>=
154163 return . concat
155164 st <- getState
156165 let reversedNotes = stateNotes st
@@ -159,10 +168,10 @@ parseMarkdown = do
159168 setPosition startPos
160169 -- now parse it for real...
161170 (title, author, date) <- option ([],[],"") titleBlock
162− blocks <- parseBlocks
171+ blocks <- parseBlocks
163172 return $ Pandoc (Meta title author date) $ filter (/= Null) blocks
164173
165−--
174+--
166175-- initial pass for references and notes
167176--
168177
@@ -185,7 +194,17 @@ referenceKey = try $ do
185194 -- return blanks so line count isn't affected
186195 return $ replicate (sourceLine endPos - sourceLine startPos) '\n'
187196
188−referenceTitle = try $ do
197+headerReference = try $ do
198+ failIfStrict
199+ startPos <- getPosition
200+ (Header level text) <- lookAhead $ atxHeader <|> setextHeader
201+ st <- getState
202+ let headers = stateHeaders st
203+ updateState $ \st -> st { stateHeaders = text:headers }
204+ endPos <- getPosition
205+ lineClump -- return the raw header, because we need to parse it later
206+
207+referenceTitle = try $ do
189208 (many1 spaceChar >> option '\n' newline) <|> newline
190209 skipSpaces
191210 tit <- (charsInBalanced '(' ')' >>= return . unwords . words)
@@ -200,7 +219,7 @@ rawLine = do
200219 notFollowedBy blankline
201220 notFollowedBy' noteMarker
202221 contents <- many1 nonEndline
203− end <- option "" (newline >> optional indentSpaces >> return "\n")
222+ end <- option "" (newline >> optional indentSpaces >> return "\n")
204223 return $ contents ++ end
205224
206225rawLines = many1 rawLine >>= return . concat
@@ -229,7 +248,7 @@ noteBlock = try $ do
229248
230249parseBlocks = manyTill block eof
231250
232−block = choice [ header
251+block = choice [ header
233252 , table
234253 , codeBlock
235254 , hrule
@@ -260,7 +279,7 @@ setextHeader = try $ do
260279 -- first, see if this block has any chance of being a setextHeader:
261280 lookAhead (anyLine >> oneOf setextHChars)
262281 text <- many1Till inline newline >>= return . normalizeSpaces
263− level <- choice $ zipWith
282+ level <- choice $ zipWith
264283 (\ch lev -> try (many1 $ char ch) >> blanklines >> return lev)
265284 setextHChars [1..(length setextHChars)]
266285 return $ Header level text
@@ -285,7 +304,7 @@ hrule = try $ do
285304indentedLine = indentSpaces >> manyTill anyChar newline >>= return . (++ "\n")
286305
287306codeBlock = do
288− contents <- many1 (indentedLine <|>
307+ contents <- many1 (indentedLine <|>
289308 try (do b <- blanklines
290309 l <- indentedLine
291310 return $ b ++ l))
@@ -300,7 +319,7 @@ emacsBoxQuote = try $ do
300319 failIfStrict
301320 string ",----"
302321 manyTill anyChar newline
303− raw <- manyTill
322+ raw <- manyTill
304323 (try (char '|' >> optional (char ' ') >> manyTill anyChar newline))
305324 (try (string "`----"))
306325 blanklines
@@ -310,7 +329,7 @@ emailBlockQuoteStart = try $ nonindentSpaces >> char '>' >>~ optional (char ' ')
310329
311330emailBlockQuote = try $ do
312331 emailBlockQuoteStart
313− raw <- sepBy (many (nonEndline <|>
332+ raw <- sepBy (many (nonEndline <|>
314333 (try (endline >> notFollowedBy emailBlockQuoteStart >>
315334 return '\n'))))
316335 (try (newline >> emailBlockQuoteStart))
@@ -318,12 +337,12 @@ emailBlockQuote = try $ do
318337 optional blanklines
319338 return raw
320339
321−blockQuote = do
340+blockQuote = do
322341 raw <- emailBlockQuote <|> emacsBoxQuote
323342 -- parse the extracted block, which may contain various block elements:
324343 contents <- parseFromString parseBlocks $ (joinWithSep "\n" raw) ++ "\n\n"
325344 return $ BlockQuote contents
326−
345+
327346--
328347-- list blocks
329348--
@@ -358,7 +377,7 @@ orderedListStart style delim = try $ do
358377 then do many1 digit
359378 char '.'
360379 return 1
361− else orderedListMarker style delim
380+ else orderedListMarker style delim
362381 if delim == Period && (style == UpperAlpha || (style == UpperRoman &&
363382 num `elem` [1, 5, 10, 50, 100, 500, 1000]))
364383 then char '\t' <|> (spaceChar >> spaceChar)
@@ -382,7 +401,7 @@ rawListItem start = try $ do
382401 blanks <- many blankline
383402 return $ concat result ++ blanks
384403
385−-- continuation of a list item - indented and separated by blankline
404+-- continuation of a list item - indented and separated by blankline
386405-- or (in compact lists) endline.
387406-- note: nested lists are parsed as continuations
388407listContinuation start = try $ do
@@ -398,7 +417,7 @@ listContinuationLine start = try $ do
398417 result <- manyTill anyChar newline
399418 return $ result ++ "\n"
400419
401−listItem start = try $ do
420+listItem start = try $ do
402421 first <- rawListItem start
403422 continuations <- many (listContinuation start)
404423 -- parsing with ListItemState forces markers at beginning of lines to
@@ -418,7 +437,7 @@ orderedList = try $ do
418437 items <- many1 (listItem (orderedListStart style delim))
419438 return $ OrderedList (start, style, delim) $ compactify items
420439
421−bulletList = many1 (listItem bulletListStart) >>=
440+bulletList = many1 (listItem bulletListStart) >>=
422441 return . BulletList . compactify
423442
424443-- definition lists
@@ -459,7 +478,7 @@ definitionList = do
459478-- paragraph block
460479--
461480
462−para = try $ do
481+para = try $ do
463482 result <- many1 inline
464483 newline
465484 blanklines <|> do st <- getState
@@ -468,9 +487,9 @@ para = try $ do
468487 else lookAhead emacsBoxQuote >> return ""
469488 return $ Para $ normalizeSpaces result
470489
471−plain = many1 inline >>= return . Plain . normalizeSpaces
490+plain = many1 inline >>= return . Plain . normalizeSpaces
472491
473−--
492+--
474493-- raw html
475494--
476495
@@ -487,25 +506,25 @@ htmlBlock = do
487506 else rawHtmlBlocks
488507
489508-- True if tag is self-closing
490−isSelfClosing tag =
509+isSelfClosing tag =
491510 isSuffixOf "/>" $ filter (not . (`elem` " \n\t")) tag
492511
493512strictHtmlBlock = try $ do
494− tag <- anyHtmlBlockTag
513+ tag <- anyHtmlBlockTag
495514 let tag' = extractTagType tag
496− if isSelfClosing tag || tag' == "hr"
515+ if isSelfClosing tag || tag' == "hr"
497516 then return tag
498− else do contents <- many (notFollowedBy' (htmlEndTag tag') >>
517+ else do contents <- many (notFollowedBy' (htmlEndTag tag') >>
499518 (htmlElement <|> (count 1 anyChar)))
500519 end <- htmlEndTag tag'
501520 return $ tag ++ concat contents ++ end
502521
503522rawHtmlBlocks = do
504− htmlBlocks <- many1 rawHtmlBlock
523+ htmlBlocks <- many1 rawHtmlBlock
505524 let combined = concatMap (\(RawHtml str) -> str) htmlBlocks
506525 let combined' = if not (null combined) && last combined == '\n'
507− then init combined -- strip extra newline
508− else combined
526+ then init combined -- strip extra newline
527+ else combined
509528 return $ RawHtml combined'
510529
511530--
@@ -516,7 +535,7 @@ rawLaTeXEnvironment' = failIfStrict >> rawLaTeXEnvironment
516535
517536--
518537-- Tables
519−--
538+--
520539
521540-- Parse a dashed line with optional trailing spaces; return its length
522541-- and the length including trailing space.
@@ -525,7 +544,7 @@ dashedLine ch = do
525544 sp <- many spaceChar
526545 return $ (length dashes, length $ dashes ++ sp)
527546
528−-- Parse a table header with dashed lines of '-' preceded by
547+-- Parse a table header with dashed lines of '-' preceded by
529548-- one line of text.
530549simpleTableHeader = try $ do
531550 rawContent <- anyLine
@@ -548,7 +567,7 @@ tableSep = try $ nonindentSpaces >> many1 (dashedLine '-') >> string "\n"
548567rawTableLine indices = do
549568 notFollowedBy' (blanklines <|> tableFooter)
550569 line <- many1Till anyChar newline
551− return $ map removeLeadingTrailingSpace $ tail $
570+ return $ map removeLeadingTrailingSpace $ tail $
552571 splitByIndices (init indices) line
553572
554573-- Parse a table line and return a list of lists of blocks (columns).
@@ -565,8 +584,8 @@ multilineRow indices = do
565584widthsFromIndices :: Int -- Number of columns on terminal
566585 -> [Int] -- Indices
567586 -> [Float] -- Fractional relative sizes of columns
568−widthsFromIndices _ [] = []
569−widthsFromIndices numColumns indices =
587+widthsFromIndices _ [] = []
588+widthsFromIndices numColumns indices =
570589 let lengths = zipWith (-) indices (0:indices)
571590 totLength = sum lengths
572591 quotient = if totLength > numColumns
@@ -605,14 +624,14 @@ simpleTable = tableWith simpleTableHeader tableLine blanklines
605624multilineTable = tableWith multilineTableHeader multilineRow tableFooter
606625
607626multilineTableHeader = try $ do
608− tableSep
627+ tableSep
609628 rawContent <- many1 (notFollowedBy' tableSep >> many1Till anyChar newline)
610629 initSp <- nonindentSpaces
611630 dashes <- many1 (dashedLine '-')
612631 newline
613632 let (lengths, lines) = unzip dashes
614633 let indices = scanl (+) (length initSp) lines
615− let rawHeadsList = transpose $ map
634+ let rawHeadsList = transpose $ map
616635 (\ln -> tail $ splitByIndices (init indices) ln)
617636 rawContent
618637 let rawHeads = map (joinWithSep " ") rawHeadsList
@@ -625,7 +644,7 @@ multilineTableHeader = try $ do
625644alignType :: [String] -> Int -> Alignment
626645alignType [] len = AlignDefault
627646alignType strLst len =
628− let str = head $ sortBy (comparing length) $
647+ let str = head $ sortBy (comparing length) $
629648 map removeTrailingSpace strLst
630649 leftSpace = if null str then False else (str !! 0) `elem` " \t"
631650 rightSpace = length str < len || (str !! (len - 1)) `elem` " \t"
@@ -637,7 +656,7 @@ alignType strLst len =
637656
638657table = failIfStrict >> (simpleTable <|> multilineTable) <?> "table"
639658
640−--
659+--
641660-- inline
642661--
643662
@@ -667,7 +686,7 @@ inline = choice [ str
667686escapedChar = do
668687 char '\\'
669688 state <- getState
670− result <- option '\\' $ if stateStrict state
689+ result <- option '\\' $ if stateStrict state
671690 then oneOf "\\`*_{}[]()>#+-.!~"
672691 else satisfy (not . isAlphaNum)
673692 return $ Str [result]
@@ -681,17 +700,17 @@ ltSign = do
681700
682701specialCharsMinusLt = filter (/= '<') specialChars
683702
684−symbol = do
703+symbol = do
685704 result <- oneOf specialCharsMinusLt
686705 return $ Str [result]
687706
688707-- parses inline code, between n `s and n `s
689−code = try $ do
708+code = try $ do
690709 starts <- many1 (char '`')
691710 skipSpaces
692711 result <- many1Till (many1 (noneOf "`\n") <|> many1 (char '`') <|>
693− (char '\n' >> return " "))
694− (try (skipSpaces >> count (length starts) (char '`') >>
712+ (char '\n' >> return " "))
713+ (try (skipSpaces >> count (length starts) (char '`') >>
695714 notFollowedBy (char '`')))
696715 return $ Code $ removeLeadingTrailingSpace $ concat result
697716
@@ -707,30 +726,30 @@ math = try $ do
707726 return $ TeX ("$" ++ (joinWithSep " " words) ++ "$")
708727
709728emph = ((enclosed (char '*') (char '*') inline) <|>
710− (enclosed (char '_') (char '_' >> notFollowedBy alphaNum) inline)) >>=
729+ (enclosed (char '_') (char '_' >> notFollowedBy alphaNum) inline)) >>=
711730 return . Emph . normalizeSpaces
712731
713−strong = ((enclosed (string "**") (try $ string "**") inline) <|>
732+strong = ((enclosed (string "**") (try $ string "**") inline) <|>
714733 (enclosed (string "__") (try $ string "__") inline)) >>=
715734 return . Strong . normalizeSpaces
716735
717736strikeout = failIfStrict >> enclosed (string "~~") (try $ string "~~") inline >>=
718737 return . Strikeout . normalizeSpaces
719738
720−superscript = failIfStrict >> enclosed (char '^') (char '^')
739+superscript = failIfStrict >> enclosed (char '^') (char '^')
721740 (notFollowedBy' whitespace >> inline) >>= -- may not contain Space
722741 return . Superscript
723742
724743subscript = failIfStrict >> enclosed (char '~') (char '~')
725744 (notFollowedBy' whitespace >> inline) >>= -- may not contain Space
726− return . Subscript
745+ return . Subscript
727746
728−smartPunctuation = failUnlessSmart >>
747+smartPunctuation = failUnlessSmart >>
729748 choice [ quoted, apostrophe, dash, ellipses ]
730749
731750apostrophe = (char '\'' <|> char '\8217') >> return Apostrophe
732751
733−quoted = doubleQuoted <|> singleQuoted
752+quoted = doubleQuoted <|> singleQuoted
734753
735754withQuoteContext context parser = do
736755 oldState <- getState
@@ -746,7 +765,7 @@ singleQuoted = try $ do
746765 withQuoteContext InSingleQuote $ many1Till inline singleQuoteEnd >>=
747766 return . Quoted SingleQuote . normalizeSpaces
748767
749−doubleQuoted = try $ do
768+doubleQuoted = try $ do
750769 doubleQuoteStart
751770 withQuoteContext InDoubleQuote $ many1Till inline doubleQuoteEnd >>=
752771 return . Quoted DoubleQuote . normalizeSpaces
@@ -757,13 +776,13 @@ failIfInQuoteContext context = do
757776 then fail "already inside quotes"
758777 else return ()
759778
760−singleQuoteStart = do
779+singleQuoteStart = do
761780 failIfInQuoteContext InSingleQuote
762− char '\8216' <|>
763− (try $ do char '\''
781+ char '\8216' <|>
782+ (try $ do char '\''
764783 notFollowedBy (oneOf ")!],.;:-? \t\n")
765784 notFollowedBy (try (oneOfStrings ["s","t","m","ve","ll","re"] >>
766− satisfy (not . isAlphaNum)))
785+ satisfy (not . isAlphaNum)))
767786 -- possess/contraction
768787 return '\'')
769788
@@ -807,13 +826,13 @@ endline = try $ do
807826 newline
808827 notFollowedBy blankline
809828 st <- getState
810− if stateStrict st
829+ if stateStrict st
811830 then do notFollowedBy emailBlockQuoteStart
812831 notFollowedBy (char '#') -- atx header
813− else return ()
832+ else return ()
814833 -- parse potential list-starts differently if in a list:
815834 if stateParserContext st == ListItemState
816− then notFollowedBy' (bulletListStart <|>
835+ then notFollowedBy' (bulletListStart <|>
817836 (anyOrderedListStart >> return ()))
818837 else return ()
819838 return Space
@@ -827,7 +846,7 @@ reference = notFollowedBy' (string "[^") >> -- footnote reference
827846 inlinesInBalanced "[" "]" >>= (return . normalizeSpaces)
828847
829848-- source for a link, with optional title
830−source = try $ do
849+source = try $ do
831850 char '('
832851 optional (char '<')
833852 src <- many (noneOf ")> \t\n")
@@ -837,7 +856,7 @@ source = try $ do
837856 char ')'
838857 return (removeTrailingSpace src, tit)
839858
840−linkTitle = try $ do
859+linkTitle = try $ do
841860 (many1 spaceChar >> option '\n' newline) <|> newline
842861 skipSpaces
843862 delim <- char '\'' <|> char '"'
@@ -852,13 +871,13 @@ link = try $ do
852871
853872-- a link like [this][ref] or [this][] or [this]
854873referenceLink label = do
855− ref <- option [] (try (optional (char ' ') >>
874+ ref <- option [] (try (optional (char ' ') >>
856875 optional (newline >> skipSpaces) >> reference))
857876 let ref' = if null ref then label else ref
858877 state <- getState
859878 case lookupKeySrc (stateKeys state) ref' of
860− Nothing -> fail "no corresponding key"
861− Just target -> return target
879+ Nothing -> fail "no corresponding key"
880+ Just target -> return target
862881
863882emailAddress = try $ do
864883 name <- many1 (alphaNum <|> char '+')
@@ -879,7 +898,7 @@ autoLink = try $ do
879898 char '>'
880899 let src' = if "mailto:" `isPrefixOf` src
881900 then drop 7 src
882− else src
901+ else src
883902 st <- getState
884903 return $ if stateStrict st
885904 then Link [Str src'] (src, "")
@@ -910,7 +929,7 @@ rawLaTeXInline' = failIfStrict >> rawLaTeXInline
910929rawHtmlInline' = do
911930 st <- getState
912931 result <- choice $ if stateStrict st
913− then [htmlBlockElement, anyHtmlTag, anyHtmlEndTag]
932+ then [htmlBlockElement, anyHtmlTag, anyHtmlEndTag]
914933 else [htmlBlockElement, anyHtmlInlineTag]
915934 return $ HtmlInline result
916935