@@ -284,7 +284,7 @@ block = choice [ mempty <$ blanklines
284 , orgBlock
284 , orgBlock
286 , example
286 , example
287− , drawer
287+ , genericDrawer
288 , specialLine
288 , specialLine
290 , return <$> hline
290 , return <$> hline
@@ -582,26 +582,55 @@ exampleCode = B.codeBlockWith ("", ["example"], [])
582exampleLine :: OrgParser String
582exampleLine :: OrgParser String
583exampleLine = try $ skipSpaces *> string ": " *> anyLine
583exampleLine = try $ skipSpaces *> string ": " *> anyLine
585−-- Drawers for properties or a logbook
585+
590+-- | A generic drawer which has no special meaning for org-mode.
586−drawer :: OrgParser (F Blocks)
591+genericDrawer :: OrgParser (F Blocks)
587−drawer = try $ do
592+genericDrawer = try $ do
588 drawerStart
593 drawerStart
589 manyTill drawerLine (try drawerEnd)
594 manyTill drawerLine (try drawerEnd)
590 return mempty
595 return mempty
592drawerStart :: OrgParser String
597drawerStart :: OrgParser String
593drawerStart = try $
598drawerStart = try $
594− skipSpaces *> drawerName <* skipSpaces <* P.newline
599+ skipSpaces *> drawerName <* skipSpaces <* newline
595− where drawerName = try $ char ':' *> validDrawerName <* char ':'
600+ where drawerName = char ':' *> manyTill nonspaceChar (char ':')
596− validDrawerName = stringAnyCase "PROPERTIES"
597− <|> stringAnyCase "LOGBOOK"
599drawerLine :: OrgParser String
602drawerLine :: OrgParser String
600−drawerLine = try anyLine
603+drawerLine = anyLine
602drawerEnd :: OrgParser String
605drawerEnd :: OrgParser String
603drawerEnd = try $
606drawerEnd = try $
604− skipSpaces *> stringAnyCase ":END:" <* skipSpaces <* P.newline
607+ skipSpaces *> stringAnyCase ":END:" <* skipSpaces <* newline
609+-- | Read a :PROPERTIES: drawer and return the key/value pairs contained
611+propertiesDrawer :: OrgParser [(String, String)]
612+propertiesDrawer = try $ do
613+ drawerType <- drawerStart
614+ guard $ map toUpper drawerType == "PROPERTIES"
615+ manyTill property (try drawerEnd)
617+ property :: OrgParser (String, String)
618+ property = try $ (,) <$> key <*> value
620+ key :: OrgParser String
621+ key = try $ skipSpaces *> char ':' *> many1Till nonspaceChar (char ':')
623+ value :: OrgParser String
624+ value = try $ skipSpaces *> manyTill anyChar (try $ skipSpaces *> P.newline)
626+keyValuesToAttr :: [(String, String)] -> Attr
627+keyValuesToAttr kvs =
629+ id' = fromMaybe mempty . lookup "id" $ kvs
630+ cls = fromMaybe mempty . lookup "class" $ kvs
631+ kvs' = filter (flip notElem ["id", "class"] . fst) kvs
633+ (id', words cls, kvs')
@@ -700,29 +729,28 @@ parseFormat = try $ do
700-- | Headers
729-- | Headers
701header :: OrgParser (F Blocks)
730header :: OrgParser (F Blocks)
702header = try $ do
731header = try $ do
703− level <- headerStart
732+ level <- headerStart
704− title <- manyTill inline (lookAhead headerEnd)
733+ title <- manyTill inline (lookAhead $ optional headerTags <* P.newline)
705− tags <- headerEnd
734+ tags <- option [] headerTags
706− let inlns = trimInlinesF . mconcat $ title <> map tagToInlineF tags
735+ newline
707− st <- getState
736+ propAttr <- option nullAttr (keyValuesToAttr <$> propertiesDrawer)
708− let inlines = runF inlns st
737+ inlines <- runF (tagTitle title tags) <$> getState
709− attr <- registerHeader nullAttr inlines
738+ attr <- registerHeader propAttr inlines
710 return $ pure (B.headerWith attr level inlines)
739 return $ pure (B.headerWith attr level inlines)
741+ tagTitle :: [F Inlines] -> [String] -> F Inlines
742+ tagTitle title tags = trimInlinesF . mconcat $ title <> map tagToInlineF tags
712 tagToInlineF :: String -> F Inlines
744 tagToInlineF :: String -> F Inlines
713 tagToInlineF t = return $ B.spanWith ("", ["tag"], [("data-tag-name", t)]) mempty
745 tagToInlineF t = return $ B.spanWith ("", ["tag"], [("data-tag-name", t)]) mempty
715−headerEnd :: OrgParser [String]
716−headerEnd = option [] headerTags <* newline
718−headerTags :: OrgParser [String]
747+ headerTags :: OrgParser [String]
719−headerTags = try $
748+ headerTags = try $
724− where tag = many1 (alphaNum <|> oneOf "@%#_")
749+ let tag = many1 (alphaNum <|> oneOf "@%#_") <* char ':'
725− <* char ':'
750+ in skipSpaces
727headerStart :: OrgParser Int
755headerStart :: OrgParser Int
728headerStart = try $
756headerStart = try $