@@ -105,6 +105,10 @@ instance HasMeta OrgParserState where
105105 deleteMeta field st =
106106 st{ orgStateMeta = deleteMeta field $ orgStateMeta st }
107107
108+instance HasLastStrPosition OrgParserState where
109+ getLastStrPos = orgStateLastStrPos
110+ setLastStrPos pos st = st{ orgStateLastStrPos = Just pos }
111+
108112instance Default OrgParserState where
109113 def = defaultOrgParserState
110114
@@ -865,6 +869,7 @@ inline :: OrgParser (F Inlines)
865869inline =
866870 choice [ whitespace
867871 , linebreak
872+ , cite
868873 , footnote
869874 , linkOrImage
870875 , anchor
@@ -929,6 +934,51 @@ endline = try $ do
929934 updateLastPreCharPos
930935 return . return $ B.space
931936
937+cite :: OrgParser (F Inlines)
938+cite = try $ do
939+ guardEnabled Ext_citations
940+ (cs, raw) <- withRaw normalCite
941+ return $ (flip B.cite (B.text raw)) <$> cs
942+
943+normalCite :: OrgParser (F [Citation])
944+normalCite = try $ char '['
945+ *> skipSpaces
946+ *> citeList
947+ <* skipSpaces
948+ <* char ']'
949+
950+citeList :: OrgParser (F [Citation])
951+citeList = sequence <$> sepBy1 citation (try $ char ';' *> skipSpaces)
952+
953+citation :: OrgParser (F Citation)
954+citation = try $ do
955+ pref <- prefix
956+ (suppress_author, key) <- citeKey
957+ suff <- suffix
958+ return $ do
959+ x <- pref
960+ y <- suff
961+ return $ Citation{ citationId = key
962+ , citationPrefix = B.toList x
963+ , citationSuffix = B.toList y
964+ , citationMode = if suppress_author
965+ then SuppressAuthor
966+ else NormalCitation
967+ , citationNoteNum = 0
968+ , citationHash = 0
969+ }
970+ where
971+ prefix = trimInlinesF . mconcat <$>
972+ manyTill inline (char ']' <|> (']' <$ lookAhead citeKey))
973+ suffix = try $ do
974+ hasSpace <- option False (notFollowedBy nonspaceChar >> return True)
975+ skipSpaces
976+ rest <- trimInlinesF . mconcat <$>
977+ many (notFollowedBy (oneOf ";]") *> inline)
978+ return $ if hasSpace
979+ then (B.space <>) <$> rest
980+ else rest
981+
932982footnote :: OrgParser (F Inlines)
933983footnote = try $ inlineNote <|> referencedNote
934984
@@ -1003,7 +1053,7 @@ selfTarget :: OrgParser String
10031053selfTarget = try $ char '[' *> linkTarget <* char ']'
10041054
10051055linkTarget :: OrgParser String
1006−linkTarget = enclosed (char '[') (char ']') (noneOf "\n\r[]")
1056+linkTarget = enclosedByPair '[' ']' (noneOf "\n\r[]")
10071057
10081058applyCustomLinkFormat :: String -> OrgParser (F String)
10091059applyCustomLinkFormat link = do
@@ -1079,7 +1129,12 @@ inlineCodeBlock = try $ do
10791129 let attrClasses = [translateLang lang, rundocBlockClass]
10801130 let attrKeyVal = map toRundocAttrib (("language", lang) : opts)
10811131 returnF $ B.codeWith ("", attrClasses, attrKeyVal) inlineCode
1082− where enclosedByPair s e p = char s *> many1Till p (char e)
1132+
1133+enclosedByPair :: Char -- ^ opening char
1134+ -> Char -- ^ closing char
1135+ -> OrgParser a -- ^ parser
1136+ -> OrgParser [a]
1137+enclosedByPair s e p = char s *> many1Till p (char e)
10831138
10841139emph :: OrgParser (F Inlines)
10851140emph = fmap B.emph <$> emphasisBetween '/'
@@ -1274,13 +1329,6 @@ afterEmphasisPreChar = do
12741329 lastPrePos <- orgStateLastPreCharPos <$> getState
12751330 return . fromMaybe True $ (== pos) <$> lastPrePos
12761331
1277−-- | Whether we are right after the end of a string
1278−notAfterString :: OrgParser Bool
1279−notAfterString = do
1280− pos <- getPosition
1281− lastStrPos <- orgStateLastStrPos <$> getState
1282− return $ lastStrPos /= Just pos
1283−
12841332-- | Whether the parser is right after a forbidden border char
12851333notAfterForbiddenBorderChar :: OrgParser Bool
12861334notAfterForbiddenBorderChar = do