git.iko.soy/typeable/pandoc
on master help sign in
Org reader: add support for "Berkeley-style" cites
8a9f591Albert Krewinkel
src/Text/Pandoc/Readers/Org/Inlines.hs+126−7
@@ -49,11 +49,13 @@ import Text.Pandoc.Readers.LaTeX ( inlineCommand, rawLaTeXInline )
49import Text.TeXMath ( readTeX, writePandoc, DisplayType(..) )
49import Text.TeXMath ( readTeX, writePandoc, DisplayType(..) )
50import qualified Text.TeXMath.Readers.MathML.EntityMap as MathMLEntityMap
50import qualified Text.TeXMath.Readers.MathML.EntityMap as MathMLEntityMap
51
51
52+import Prelude hiding (sequence)
52−import Control.Monad ( guard, mplus, mzero, when )
53+import Control.Monad ( guard, mplus, mzero, when, void )
53import Data.Char ( isAlphaNum, isSpace )
54import Data.Char ( isAlphaNum, isSpace )
54−import Data.List ( isPrefixOf )
55+import Data.List ( intersperse, isPrefixOf )
55import Data.Maybe ( fromMaybe )
56import Data.Maybe ( fromMaybe )
56import qualified Data.Map as M
57import qualified Data.Map as M
58+import Data.Traversable (sequence)
57
59
58--
60--
59-- Functions acting on the parser state
61-- Functions acting on the parser state
@@ -166,19 +168,42 @@ endline = try $ do
166 updateLastPreCharPos
168 updateLastPreCharPos
167 return . return $ B.softbreak
169 return . return $ B.softbreak
168
170
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+
169cite :: OrgParser (F Inlines)
187cite :: OrgParser (F Inlines)
170−cite = try $ do
188+cite = try $ berkeleyCite <|> do
171 guardEnabled Ext_citations
189 guardEnabled Ext_citations
190+ (cs, raw) <- withRaw $ choice
172− (cs, raw) <- withRaw (pandocOrgCite <|> orgRefCite)
191+ [ pandocOrgCite
192+ , orgRefCite
193+ , berkeleyTextualCite
194+ ]
173 return $ (flip B.cite (B.text raw)) <$> cs
195 return $ (flip B.cite (B.text raw)) <$> cs
174
196
175−-- | A citation in Pandoc Org-mode style (@[\@citekey]@).
197+-- | A citation in Pandoc Org-mode style (@[prefix \@citekey suffix]@).
176pandocOrgCite :: OrgParser (F [Citation])
198pandocOrgCite :: OrgParser (F [Citation])
177pandocOrgCite = try $
199pandocOrgCite = try $
178 char '[' *> skipSpaces *> citeList <* skipSpaces <* char ']'
200 char '[' *> skipSpaces *> citeList <* skipSpaces <* char ']'
179
201
180orgRefCite :: OrgParser (F [Citation])
202orgRefCite :: OrgParser (F [Citation])
203+orgRefCite = try $ choice
204+ [ normalOrgRefCite
181−orgRefCite = try $ normalOrgRefCite <|> (fmap (:[]) <$> linkLikeOrgRefCite)
205+ , fmap (:[]) <$> linkLikeOrgRefCite
206+ ]
182
207
183normalOrgRefCite :: OrgParser (F [Citation])
208normalOrgRefCite :: OrgParser (F [Citation])
184normalOrgRefCite = try $ do
209normalOrgRefCite = try $ do
@@ -199,6 +224,100 @@ normalOrgRefCite = try $ do
199 , citationHash = 0
224 , citationHash = 0
200 }
225 }
201
226
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+
202-- | Read a link-like org-ref style citation. The citation includes pre and
321-- | Read a link-like org-ref style citation. The citation includes pre and
203-- post text. However, multiple citations are not possible due to limitations
322-- post text. However, multiple citations are not possible due to limitations
204-- in the syntax.
323-- in the syntax.
@@ -243,7 +362,7 @@ orgRefCiteMode =
243 ]
362 ]
244
363
245citeList :: OrgParser (F [Citation])
364citeList :: OrgParser (F [Citation])
246−citeList = sequence <$> sepBy1 citation (try $ char ';' *> skipSpaces)
365+citeList = sequence <$> sepEndBy1 citation (try $ char ';' *> skipSpaces)
247
366
248citation :: OrgParser (F Citation)
367citation :: OrgParser (F Citation)
249citation = try $ do
368citation = try $ do
src/Text/Pandoc/Readers/Org/Parsing.hs+1
@@ -97,6 +97,7 @@ module Text.Pandoc.Readers.Org.Parsing
97 , try
97 , try
98 , sepBy
98 , sepBy
99 , sepBy1
99 , sepBy1
100+ , sepEndBy1
100 , option
101 , option
101 , optional
102 , optional
102 , optionMaybe
103 , optionMaybe
tests/Tests/Readers/Org.hs+49
@@ -336,6 +336,55 @@ tests =
336 }
336 }
337 in (para $ cite [citation] "[[citep:Dominik201408][See page 20::, for example]]")
337 in (para $ cite [citation] "[[citep:Dominik201408][See page 20::, for example]]")
338
338
339+ , testGroup "Berkeley-style citations" $
340+ let pandocCite = Citation
341+ { citationId = "Pandoc"
342+ , citationPrefix = mempty
343+ , citationSuffix = mempty
344+ , citationMode = NormalCitation
345+ , citationNoteNum = 0
346+ , citationHash = 0
347+ }
348+ pandocInText = pandocCite { citationMode = AuthorInText }
349+ dominikCite = Citation
350+ { citationId = "Dominik201408"
351+ , citationPrefix = mempty
352+ , citationSuffix = mempty
353+ , citationMode = NormalCitation
354+ , citationNoteNum = 0
355+ , citationHash = 0
356+ }
357+ dominikInText = dominikCite { citationMode = AuthorInText }
358+ in [
359+ "Berkeley-style in-text citation" =:
360+ "See @Dominik201408." =?>
361+ (para $ "See "
362+ <> cite [dominikInText] "@Dominik201408"
363+ <> ".")
364+
365+ , "Berkeley-style parenthetical citation list" =:
366+ "[(cite): see; @Dominik201408;also @Pandoc; and others]" =?>
367+ let pandocCite' = pandocCite {
368+ citationPrefix = toList "also"
369+ , citationSuffix = toList "and others"
370+ }
371+ dominikCite' = dominikCite {
372+ citationPrefix = toList "see"
373+ }
374+ in (para $ cite [dominikCite', pandocCite'] "")
375+
376+ , "Berkeley-style plain citation list" =:
377+ "[cite: See; @Dominik201408; and @Pandoc; and others]" =?>
378+ let pandocCite' = pandocInText {
379+ citationPrefix = toList "and"
380+ }
381+ in (para $ "See "
382+ <> cite [dominikInText] ""
383+ <> "," <> space
384+ <> cite [pandocCite'] ""
385+ <> "," <> space <> "and others")
386+ ]
387+
339 , "Inline LaTeX symbol" =:
388 , "Inline LaTeX symbol" =:
340 "\\dots" =?>
389 "\\dots" =?>
341 para "…"
390 para "…"