Skip to content

Commit 61a2b72

Browse files
committed
Extract citationSuffix, citationPrefix.
In transforming pandoc Cite to citeproc Citation, extract a `citationSuffix` and `citationPrefix` from the last item's suffix and first item's prefix, respectively, if they contain a `|` character which separates the item's suffix or prefix from the whole Citation's. for example: [for example, see |@c1; @A3; @b4|, and others] Here "for example, see" acts as a prefix for the whole group and will remain at the beginning even if the citation items are reordered by citeproc. Similarly, ", and others" will be a suffix for the whole group. Closes #10894. Notes: 1. The org reader now adds global prefixes and suffixes the same way as the Markdown reader: as affixes to the first item's prefix or the last item's suffix, separated by a pipe (`|`). 2. The org writer, however, has not been modified to convert the `|` to a `;`, as required by org-cite syntax. 3. This change doesn't currently do what one would expect, because of changes that were made to citeproc to prevent citation items with prefixes and suffixes from being sorted. Hence in `test/command/10894.md`, we have test output ``` (Doe, 2020; Smith, 2021) ``` without affixes, but ``` (see Smith, 2021; Doe, 2020, and others) ``` with affixes. To make this work well, we'd need to remove the citeproc code that prevented bad results before we had proper global prefixes and suffixes. However, removing this code would mean that existing documents would render differently, unless the new pipe syntax for citation affixes were used. That may be something we want to avoid. 4. The use of pipes to separate out global affixes from item-level affixes is a kludge that could be avoided if we added additional fields to Cite in the pandoc AST. However, AST changes are disruptive, so perhaps it's not worth doing that.
1 parent 1b50a46 commit 61a2b72

6 files changed

Lines changed: 101 additions & 23 deletions

File tree

MANUAL.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6102,6 +6102,19 @@ the suffix as locator by prepending curly braces:
61026102
[@smith, {pp. iv, vi-xi, (xv)-(xvii)} with suffix here]
61036103
[@smith{}, 99 years later]
61046104

6105+
A prefix or suffix can be marked as going with the whole citation
6106+
rather than an individual item. A horizontal bar (`|`) is used
6107+
to separate the global citation prefix or suffix from the individual
6108+
items:
6109+
6110+
[for example, see |@C1; @A3; @B4, in part|, and others]
6111+
6112+
The global prefix/suffix will always be at the beginning/end
6113+
of the rendered citation, even if the style causes the
6114+
items to be sorted in a different order. By contrast, the
6115+
prefixes and suffixes on each item will move with the items when
6116+
they are sorted.
6117+
61056118
A minus sign (`-`) before the `@` will suppress mention of
61066119
the author in the citation. This can be useful when the
61076120
author is already mentioned in the text:

src/Text/Pandoc/Citeproc.hs

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -311,17 +311,49 @@ getCitations locale otherIdsMap (Pandoc meta blocks) =
311311
getCitation (Cite cs _fallback) = Seq.singleton $
312312
Citeproc.Citation { Citeproc.citationId = Nothing
313313
, Citeproc.citationResetPosition = False
314-
, Citeproc.citationPrefix = Nothing
315-
, Citeproc.citationSuffix = Nothing
314+
, Citeproc.citationPrefix = pref
315+
, Citeproc.citationSuffix = suff
316316
, Citeproc.citationNoteNumber =
317317
case cs of
318318
[] -> Nothing
319319
(Pandoc.Citation{ Pandoc.citationNoteNum = n }:
320320
_) | n > 0 -> Just n
321321
| otherwise -> Nothing
322-
, Citeproc.citationItems =
323-
fromPandocCitations locale otherIdsMap cs
322+
, Citeproc.citationItems = items
324323
}
324+
where
325+
(pref, suff, items) =
326+
case fromPandocCitations locale otherIdsMap cs of
327+
[] -> (Nothing, Nothing, [])
328+
(i:is) ->
329+
let (pref', i') = case citationItemPrefix i of
330+
Nothing -> (Nothing, i)
331+
Just p ->
332+
case splitInlinesOnPipe (B.toList p) of
333+
(_,[]) -> (Nothing, i)
334+
(as,bs) -> (Just (B.fromList as),
335+
i{ citationItemPrefix = Just (B.fromList bs) })
336+
(suff', is') = case reverse is of
337+
[] -> (Nothing, [])
338+
(i'':is'') ->
339+
case Citeproc.citationItemSuffix i'' of
340+
Nothing -> (Nothing, is)
341+
Just s ->
342+
case splitInlinesOnPipe (B.toList s) of
343+
(_,[]) -> (Nothing, is)
344+
(as,bs) -> (Just (B.fromList bs), reverse
345+
(i''{ citationItemSuffix = Just (B.fromList as) }:is''))
346+
in (pref', suff', i':is')
347+
splitInlinesOnPipe ils =
348+
case break isStrWithPipe ils of
349+
(xs,Str s : ys) ->
350+
let (as,bs) = T.break (=='|') s
351+
bs' = T.drop 1 bs
352+
in (xs ++ [Str as | not (T.null as)],
353+
[Str bs' | not (T.null bs')] ++ ys)
354+
_ -> (ils,[])
355+
isStrWithPipe (Str s) = T.any (=='|') s
356+
isStrWithPipe _ = False
325357
getCitation _ = mempty
326358

327359
fromPandocCitations :: Locale

src/Text/Pandoc/Citeproc/Locator.hs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import Control.Monad (mzero)
1919
import qualified Data.Map as M
2020
import Data.Char (isSpace, isPunctuation, isDigit)
2121

22-
2322
data LocatorInfo =
2423
LocatorInfo{ locatorRaw :: Text
2524
, locatorLabel :: Text
@@ -57,9 +56,11 @@ pLocatorWords locMap = do
5756
maybeAddComma :: [Inline] -> [Inline]
5857
maybeAddComma [] = []
5958
maybeAddComma ils@(Space : _) = ils
59+
maybeAddComma ils@(SoftBreak : _) = ils
60+
maybeAddComma ils@(LineBreak : _) = ils
6061
maybeAddComma ils@(Str t : _)
6162
| Just (c, _) <- T.uncons t
62-
, isPunctuation c = ils
63+
, isPunctuation c || c == '|' = ils
6364
maybeAddComma ils = Str "," : Space : ils
6465

6566
pLocatorDelimited :: LocatorMap -> LocatorParser LocatorInfo

src/Text/Pandoc/Readers/Markdown.hs

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ import Text.Pandoc.Shared
5656
import Text.Pandoc.URI (escapeURI, isURI, pBase64DataURI)
5757
import Text.Pandoc.XML (fromEntities)
5858
import Text.Pandoc.Readers.Metadata (yamlBsToMeta, yamlBsToRefs, yamlMetaBlock)
59-
-- import Debug.Trace (traceShowId)
6059

6160
type MarkdownParser m = ParsecT Sources ParserState m
6261

@@ -2313,14 +2312,8 @@ normalCite = try $ do
23132312
return citations
23142313

23152314
suffix :: PandocMonad m => MarkdownParser m (F Inlines)
2316-
suffix = try $ do
2317-
hasSpace <- option False (notFollowedBy nonspaceChar >> return True)
2318-
spnl
2319-
ils <- many (notFollowedBy (oneOf ";]") >> inline)
2320-
let rest = trimInlinesF (mconcat ils)
2321-
return $ if hasSpace && not (null ils)
2322-
then (B.space <>) <$> rest
2323-
else rest
2315+
suffix = try $
2316+
mconcat <$> many (notFollowedBy (oneOf ";]") >> inline)
23242317

23252318
prefix :: PandocMonad m => MarkdownParser m (F Inlines)
23262319
prefix = trimInlinesF . mconcat <$>
@@ -2340,11 +2333,11 @@ citation = try $ do
23402333
suff <- suffix
23412334
noteNum <- stateNoteNumber <$> getState
23422335
return $ do
2343-
x <- pref
2344-
y <- suff
2336+
pref' <- B.toList <$> pref
2337+
suff' <- B.toList <$> suff
23452338
return Citation{ citationId = key
2346-
, citationPrefix = B.toList x
2347-
, citationSuffix = B.toList y
2339+
, citationPrefix = pref'
2340+
, citationSuffix = suff'
23482341
, citationMode = if suppress_author
23492342
then SuppressAuthor
23502343
else NormalCitation

src/Text/Pandoc/Readers/Org/Inlines.hs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,21 +183,26 @@ adjustCiteStyle sty cs = do
183183
addPrefixToFirstItem :: (F Inlines) -> (F [Citation]) -> (F [Citation])
184184
addPrefixToFirstItem aff cs = do
185185
cs' <- cs
186-
aff' <- aff
186+
aff' <- B.toList <$> aff
187187
case cs' of
188188
[] -> return []
189189
(d:ds) -> return (d{ citationPrefix =
190-
B.toList aff' <> citationPrefix d }:ds)
190+
if null aff'
191+
then citationPrefix d
192+
else aff' ++ (Str "|" : citationPrefix d) }:ds)
191193

192194
addSuffixToLastItem :: (F Inlines) -> (F [Citation]) -> (F [Citation])
193195
addSuffixToLastItem aff cs = do
194196
cs' <- cs
195-
aff' <- aff
197+
aff' <- B.toList <$> aff
196198
case lastMay cs' of
197199
Nothing -> return cs'
198200
Just d ->
199201
return (init cs' ++ [d{ citationSuffix =
200-
citationSuffix d <> B.toList aff' }])
202+
citationSuffix d <>
203+
if null aff'
204+
then []
205+
else Str "|" : aff' }])
201206

202207
citeItems :: PandocMonad m => OrgParser m (F [Citation])
203208
citeItems = sequence <$> sepBy1' citeItem (char ';' <* void (many spaceChar))

test/command/10894.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
```
2+
% pandoc --citeproc -t plain --csl command/apa.csl
3+
---
4+
references:
5+
- author:
6+
- family: Doe
7+
given: John
8+
container-title: Journal of Examples
9+
id: doe2020
10+
issue: 1
11+
issued: 2020
12+
page: 1-10
13+
title: An example article
14+
type: article-journal
15+
volume: 1
16+
- author:
17+
- family: Smith
18+
given: Jane
19+
id: smith2021
20+
issued: 2021
21+
publisher: Example Press
22+
title: A sample book
23+
type: book
24+
suppress-bibliography: true
25+
---
26+
[@smith2021; @doe2020]
27+
28+
[see |@smith2021; @doe2020|, and others]
29+
^D
30+
(Doe, 2020; Smith, 2021)
31+
32+
(see Smith, 2021; Doe, 2020, and others)
33+
```
34+

0 commit comments

Comments
 (0)