@@ -49,11 +49,13 @@ import Text.Pandoc.Readers.LaTeX ( inlineCommand, rawLaTeXInline )
4949import Text.TeXMath ( readTeX, writePandoc, DisplayType(..) )
5050import qualified Text.TeXMath.Readers.MathML.EntityMap as MathMLEntityMap
5151
52−import Control.Monad ( guard, mplus, mzero, when )
52+import Prelude hiding (sequence)
53+import Control.Monad ( guard, mplus, mzero, when, void )
5354import Data.Char ( isAlphaNum, isSpace )
54−import Data.List ( isPrefixOf )
55+import Data.List ( intersperse, isPrefixOf )
5556import Data.Maybe ( fromMaybe )
5657import qualified Data.Map as M
58+import Data.Traversable (sequence)
5759
5860--
5961-- Functions acting on the parser state
@@ -127,7 +129,7 @@ inlines = trimInlinesF . mconcat <$> many1 inline
127129
128130-- treat these as potentially non-text when parsing inline:
129131specialChars :: [Char]
130−specialChars = "\"$'()*+-,./:<=>[\\]^_{|}~"
132+specialChars = "\"$'()*+-,./:;<=>[\\]^_{|}~"
131133
132134
133135whitespace :: OrgParser (F Inlines)
@@ -166,19 +168,42 @@ endline = try $ do
166168 updateLastPreCharPos
167169 return . return $ B.softbreak
168170
171+
172+--
173+-- Citations
174+--
175+
176+-- The state of citations is a bit confusing due to the lack of an official
177+-- syntax and multiple syntaxes coexisting. The pandocOrgCite syntax was the
178+-- first to be implemented here and is almost identical to Markdown's citation
179+-- syntax. The org-ref package is in wide use to handle citations, but the
180+-- syntax is a bit limiting and not quite as simple to write. The
181+-- semi-offical Org-mode citation syntax is based on John MacFarlane's Pandoc
182+-- sytax and Org-oriented enhancements contributed by Richard Lawrence and
183+-- others. It's dubbed Berkeley syntax due the place of activity of its main
184+-- contributors. All this should be consolidated once an official Org-mode
185+-- citation syntax has emerged.
186+
169187cite :: OrgParser (F Inlines)
170−cite = try $ do
188+cite = try $ berkeleyCite <|> do
171189 guardEnabled Ext_citations
172− (cs, raw) <- withRaw (pandocOrgCite <|> orgRefCite)
190+ (cs, raw) <- withRaw $ choice
191+ [ pandocOrgCite
192+ , orgRefCite
193+ , berkeleyTextualCite
194+ ]
173195 return $ (flip B.cite (B.text raw)) <$> cs
174196
175−-- | A citation in Pandoc Org-mode style (@[\@citekey]@).
197+-- | A citation in Pandoc Org-mode style (@[prefix \@citekey suffix]@).
176198pandocOrgCite :: OrgParser (F [Citation])
177199pandocOrgCite = try $
178200 char '[' *> skipSpaces *> citeList <* skipSpaces <* char ']'
179201
180202orgRefCite :: OrgParser (F [Citation])
181−orgRefCite = try $ normalOrgRefCite <|> (fmap (:[]) <$> linkLikeOrgRefCite)
203+orgRefCite = try $ choice
204+ [ normalOrgRefCite
205+ , fmap (:[]) <$> linkLikeOrgRefCite
206+ ]
182207
183208normalOrgRefCite :: OrgParser (F [Citation])
184209normalOrgRefCite = try $ do
@@ -199,6 +224,100 @@ normalOrgRefCite = try $ do
199224 , citationHash = 0
200225 }
201226
227+-- | Read an Berkeley-style Org-mode citation. Berkeley citation style was
228+-- develop and adjusted to Org-mode style by John MacFarlane and Richard
229+-- Lawrence, respectively, both philosophers at UC Berkeley.
230+berkeleyCite :: OrgParser (F Inlines)
231+berkeleyCite = try $ do
232+ bcl <- berkeleyCitationList
233+ return $ do
234+ parens <- berkeleyCiteParens <$> bcl
235+ prefix <- berkeleyCiteCommonPrefix <$> bcl
236+ suffix <- berkeleyCiteCommonSuffix <$> bcl
237+ citationList <- berkeleyCiteCitations <$> bcl
238+ if parens
239+ then return . toCite . addToFirstAndLast prefix suffix $ citationList
240+ else return $ maybe mempty (<> " ") prefix
241+ <> (toListOfCites $ map toInTextMode citationList)
242+ <> maybe mempty (", " <>) suffix
243+ where
244+ toCite :: [Citation] -> Inlines
245+ toCite cs = B.cite cs mempty
246+
247+ toListOfCites :: [Citation] -> Inlines
248+ toListOfCites = mconcat . intersperse ", " . map (\c -> B.cite [c] mempty)
249+
250+ toInTextMode :: Citation -> Citation
251+ toInTextMode c = c { citationMode = AuthorInText }
252+
253+ addToFirstAndLast :: Maybe Inlines -> Maybe Inlines -> [Citation] -> [Citation]
254+ addToFirstAndLast pre suf (c:cs) =
255+ let firstCite = maybe c
256+ (\p -> c { citationPrefix = B.toList p <> citationPrefix c })
257+ pre
258+ cites = firstCite:cs
259+ lc = last cites
260+ lastCite = maybe lc
261+ (\s -> lc { citationSuffix = B.toList s <> citationSuffix lc })
262+ suf
263+ in init cites ++ [lastCite]
264+ addToFirstAndLast _ _ _ = []
265+
266+data BerkeleyCitationList = BerkeleyCitationList
267+ { berkeleyCiteParens :: Bool
268+ , berkeleyCiteCommonPrefix :: Maybe Inlines
269+ , berkeleyCiteCommonSuffix :: Maybe Inlines
270+ , berkeleyCiteCitations :: [Citation]
271+ }
272+berkeleyCitationList :: OrgParser (F BerkeleyCitationList)
273+berkeleyCitationList = try $ do
274+ char '['
275+ parens <- choice [ False <$ berkeleyBareTag, True <$ berkeleyParensTag ]
276+ char ':'
277+ skipSpaces
278+ commonPrefix <- optionMaybe (try $ citationListPart <* char ';')
279+ citations <- citeList
280+ commonSuffix <- optionMaybe (try $ citationListPart)
281+ char ']'
282+ return (BerkeleyCitationList parens
283+ <$> sequence commonPrefix
284+ <*> sequence commonSuffix
285+ <*> citations)
286+ where
287+ citationListPart :: OrgParser (F Inlines)
288+ citationListPart = fmap (trimInlinesF . mconcat) . try . many1 $ do
289+ notFollowedBy' citeKey
290+ notFollowedBy (oneOf ";]")
291+ inline
292+
293+berkeleyBareTag :: OrgParser ()
294+berkeleyBareTag = try $ void berkeleyBareTag'
295+
296+berkeleyParensTag :: OrgParser ()
297+berkeleyParensTag = try . void $ enclosedByPair '(' ')' berkeleyBareTag'
298+
299+berkeleyBareTag' :: OrgParser ()
300+berkeleyBareTag' = try $ void (string "cite")
301+
302+berkeleyTextualCite :: OrgParser (F [Citation])
303+berkeleyTextualCite = try $ do
304+ (suppressAuthor, key) <- citeKey
305+ returnF . return $ Citation
306+ { citationId = key
307+ , citationPrefix = mempty
308+ , citationSuffix = mempty
309+ , citationMode = if suppressAuthor then SuppressAuthor else AuthorInText
310+ , citationNoteNum = 0
311+ , citationHash = 0
312+ }
313+
314+-- The following is what a Berkeley-style bracketed textual citation parser
315+-- would look like. However, as these citations are a subset of Pandoc's Org
316+-- citation style, this isn't used.
317+-- berkeleyBracketedTextualCite :: OrgParser (F [Citation])
318+-- berkeleyBracketedTextualCite = try . (fmap head) $
319+-- enclosedByPair '[' ']' berkeleyTextualCite
320+
202321-- | Read a link-like org-ref style citation. The citation includes pre and
203322-- post text. However, multiple citations are not possible due to limitations
204323-- in the syntax.
@@ -243,7 +362,7 @@ orgRefCiteMode =
243362 ]
244363
245364citeList :: OrgParser (F [Citation])
246−citeList = sequence <$> sepBy1 citation (try $ char ';' *> skipSpaces)
365+citeList = sequence <$> sepEndBy1 citation (try $ char ';' *> skipSpaces)
247366
248367citation :: OrgParser (F Citation)
249368citation = try $ do