git.iko.soy/typeable/pandoc
on master help sign in
Merge pull request #1224 from tarleb/org-math

Org reader: Read inline math, recognize definition lists

commitbfd598e1e93d9a15c8b69c167c5d0e5dd1503017 authorJohn MacFarlane <jgm@berkeley.edu> dateMon 7 Apr 2014, 07:24 (13 years ago) parente352ec5c47bd84
2 files changed+80−40
src/Text/Pandoc/Readers/Org.hs+53−37 tests/Tests/Readers/Org.hs+27−3
src/Text/Pandoc/Readers/Org.hs+53−37
@@ -268,8 +268,12 @@ data OrgTable = OrgTable
268268table :: OrgParser Blocks
269269table = try $ do
270270 lookAhead tableStart
271− OrgTable _ aligns heads lns <- normalizeTable . rowsToTable <$> tableRows
272− return $ B.table "" (zip aligns $ repeat 0) heads lns
271+ orgToPandocTable . normalizeTable . rowsToTable <$> tableRows
272+
273+orgToPandocTable :: OrgTable
274+ -> Blocks
275+orgToPandocTable (OrgTable _ aligns heads lns) =
276+ B.table "" (zip aligns $ repeat 0) heads lns
273277
274278tableStart :: OrgParser Char
275279tableStart = try $ skipSpaces *> char '|'
@@ -379,7 +383,10 @@ restOfLine = mconcat <$> manyTill inline newline
379383--
380384
381385list :: OrgParser Blocks
382−list = choice [ bulletList, orderedList ] <?> "list"
386+list = choice [ definitionList, bulletList, orderedList ] <?> "list"
387+
388+definitionList :: OrgParser Blocks
389+definitionList = B.definitionList <$> many1 (definitionListItem bulletListStart)
383390
384391bulletList :: OrgParser Blocks
385392bulletList = B.bulletList . compactify' <$> many1 (listItem bulletListStart)
@@ -403,20 +410,26 @@ orderedListStart = genericListStart orderedListMarker
403410 -- Ordered list markers allowed in org-mode
404411 where orderedListMarker = mappend <$> many1 digit <*> (pure <$> oneOf ".)")
405412
413+definitionListItem :: OrgParser Int
414+ -> OrgParser (Inlines, [Blocks])
415+definitionListItem parseMarkerGetLength = try $ do
416+ markerLength <- parseMarkerGetLength
417+ term <- manyTill (noneOf "\n\r") (try $ string "::")
418+ first <- anyLineNewline
419+ cont <- concat <$> many (listContinuation markerLength)
420+ term' <- parseFromString inline term
421+ contents' <- parseFromString parseBlocks $ first ++ cont
422+ return (term', [contents'])
423+
424+
425+-- parse raw text for one list item, excluding start marker and continuations
406426listItem :: OrgParser Int
407427 -> OrgParser Blocks
408428listItem start = try $ do
409− (markerLength, first) <- try (start >>= rawListItem)
410− rest <- many (listContinuation markerLength)
411− parseFromString parseBlocks $ concat (first:rest)
412−
413−-- parse raw text for one list item, excluding start marker and continuations
414−rawListItem :: Int
415− -> OrgParser (Int, String)
416−rawListItem markerLength = try $ do
417− firstLine <- anyLine
418− restLines <- many (listLine markerLength)
419− return (markerLength, (firstLine ++ "\n" ++ (concat restLines)))
429+ markerLength <- try start
430+ firstLine <- anyLineNewline
431+ rest <- concat <$> many (listContinuation markerLength)
432+ parseFromString parseBlocks $ firstLine ++ rest
420433
421434-- continuation of a list item - indented and separated by blankline or endline.
422435-- Note: nested lists are parsed as continuations.
@@ -424,14 +437,11 @@ listContinuation :: Int
424437 -> OrgParser String
425438listContinuation markerLength = try $
426439 mappend <$> many blankline
427− <*> (concat <$> many1 (listLine markerLength))
440+ <*> (concat <$> many1 listLine)
441+ where listLine = try $ indentWith markerLength *> anyLineNewline
428442
429−-- parse a line of a list item
430−listLine :: Int
431− -> OrgParser String
432−listLine markerLength = try $
433− indentWith markerLength *> anyLine
434− <**> pure (++ "\n")
443+anyLineNewline :: OrgParser String
444+anyLineNewline = (++ "\n") <$> anyLine
435445
436446
437447--
@@ -449,6 +459,7 @@ inline = choice inlineParsers <?> "inline"
449459 , strikeout
450460 , underline
451461 , code
462+ , math
452463 , verbatim
453464 , subscript
454465 , superscript
@@ -491,12 +502,11 @@ explicitOrImageLink = try $ do
491502 char '['
492503 src <- enclosedRaw (char '[') (char ']')
493504 title <- enclosedRaw (char '[') (char ']')
494− title' <- parseFromString (mconcat . butLast <$> many inline) (title++"\n")
505+ title' <- parseFromString (mconcat <$> many inline) title
495506 char ']'
496507 return $ if (isImage src) && (isImage title)
497508 then B.link src "" (B.image title "" "")
498509 else B.link src "" title'
499− where butLast = reverse . tail . reverse
500510
501511selflinkOrImage :: OrgParser Inlines
502512selflinkOrImage = try $ do
@@ -521,10 +531,13 @@ underline = B.strong <$> inlinesEnclosedBy '_'
521531code :: OrgParser Inlines
522532code = B.code <$> rawEnclosedBy '='
523533
524−verbatim :: OrgParser Inlines
534+math :: OrgParser Inlines
535+math = B.math <$> rawEnclosedBy '$'
536+
537+verbatim :: OrgParser Inlines
525538verbatim = B.rawInline "" <$> rawEnclosedBy '~'
526539
527−subscript :: OrgParser Inlines
540+subscript :: OrgParser Inlines
528541subscript = B.subscript <$> (try $ char '_' *> maybeGroupedByBraces)
529542
530543superscript :: OrgParser Inlines
@@ -552,11 +565,8 @@ inlinesEnclosedBy c = try $ do
552565 updateState $ \st -> st { orgInlineCharStack = c:(orgInlineCharStack st) }
553566 res <- enclosedInlines (atStart (char c) <* endsOnThisOrNextLine c)
554567 (atEnd $ char c)
555− updateState $ \st -> st { orgInlineCharStack = shift . orgInlineCharStack $ st }
568+ updateState $ \st -> st { orgInlineCharStack = drop 1 . orgInlineCharStack $ st }
556569 return res
557− where shift xs
558− | null xs = []
559− | otherwise = tail xs
560570
561571enclosedRaw :: OrgParser a
562572 -> OrgParser b
@@ -574,23 +584,28 @@ rawEnclosedBy c = enclosedRaw (atStart $ char c) (atEnd $ char c)
574584-- succeeds only if we're not right after a str (ie. in middle of word)
575585atStart :: OrgParser a -> OrgParser a
576586atStart p = do
577− pos <- getPosition
578− st <- getState
579− guard $ orgLastStrPos st /= Just pos
587+ guard =<< not <$> isRightAfterString
580588 p
581589
582590-- | succeeds only if we're at the end of a word
583591atEnd :: OrgParser a -> OrgParser a
584592atEnd p = try $ do
585− p <* lookingAtEndOfWord
586− where lookingAtEndOfWord = lookAhead . oneOf =<< postWordChars
593+ p <* lookingAtEndOfWord
594+ where lookingAtEndOfWord =
595+ eof <|> const (return ()) =<< lookAhead . oneOf =<< postWordChars
596+
597+isRightAfterString :: OrgParser Bool
598+isRightAfterString = do
599+ pos <- getPosition
600+ st <- getState
601+ -- the position `Nothing` isn't after a String, either, hence the double
602+ -- negation
603+ return $ not $ orgLastStrPos st /= Just pos
587604
588605postWordChars :: OrgParser [Char]
589606postWordChars = do
590607 st <- getState
591− return $ "\t\n\r !\"'),-.:?}" ++ (safeSecond . orgInlineCharStack $ st)
592− where safeSecond (_:x2:_) = [x2]
593− safeSecond _ = []
608+ return $ "\t\n\r !\"'),-.:?}" ++ (take 1 . drop 1 . orgInlineCharStack $ st)
594609
595610-- FIXME: These functions are hacks and should be replaced
596611endsOnThisOrNextLine :: Char
@@ -608,6 +623,7 @@ endsOnThisLine input c doOnOtherLines = do
608623 postWordChars' <- postWordChars
609624 case break (`elem` c:"\n") input of
610625 (_,'\n':rest) -> doOnOtherLines rest
626+ (_,_:[]) -> return ()
611627 (_,_:rest@(n:_)) -> if n `elem` postWordChars'
612628 then return ()
613629 else endsOnThisLine rest c doOnOtherLines
tests/Tests/Readers/Org.hs+27−3
@@ -43,8 +43,8 @@ tests =
4343 para (strong "Cider")
4444
4545 , "Strong Emphasis" =:
46− "/*strength*/" =?>
47− para (emph . strong $ "strength")
46+ "/*strength*/" =?>
47+ para (emph . strong $ "strength")
4848
4949 , "Strikeout" =:
5050 "+Kill Bill+" =?>
@@ -54,6 +54,10 @@ tests =
5454 "=Robot.rock()=" =?>
5555 para (code "Robot.rock()")
5656
57+ , "Math" =:
58+ "$E=mc^2$" =?>
59+ para (math "E=mc^2")
60+
5761 , "Verbatim" =:
5862 "~word for word~" =?>
5963 para (rawInline "" "word for word")
@@ -428,7 +432,27 @@ tests =
428432 , "Bullet List in Ordered List" =:
429433 ("1. GNU\n" ++
430434 " - Freedom\n") =?>
431− orderedList [ (para "GNU") <> bulletList [ (plain "Freedom") ] ]
435+ orderedList [ (para "GNU") <> bulletList [ (plain "Freedom") ] ]
436+
437+ , "Definition List" =:
438+ unlines [ "- PLL :: phase-locked loop"
439+ , "- TTL ::"
440+ , " transistor-transistor logic"
441+ , "- PSK::phase-shift keying"
442+ , ""
443+ , " a digital modulation scheme"
444+ ] =?>
445+ definitionList [ ("PLL", [ plain $ "phase-locked" <> space <> "loop" ])
446+ , ("TTL", [ plain $ "transistor-transistor" <> space <>
447+ "logic" ])
448+ , ("PSK", [ mconcat
449+ [ para $ "phase-shift" <> space <> "keying"
450+ , plain $ spcSep [ "a", "digital"
451+ , "modulation", "scheme" ]
452+ ]
453+ ]
454+ )
455+ ]
432456 ]
433457
434458 , testGroup "Tables"